This commit is contained in:
parent
03501280ec
commit
eb6dd6f2ee
5
editor/package-lock.json
generated
5
editor/package-lock.json
generated
|
@ -4220,6 +4220,11 @@
|
|||
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
|
||||
"dev": true
|
||||
},
|
||||
"mustache": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.1.tgz",
|
||||
"integrity": "sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA=="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"axios": "^0.19.0",
|
||||
"bulma": "^0.7.5",
|
||||
"css-loader": "^3.2.0",
|
||||
"lunr": "^2.3.8",
|
||||
"mustache": "^4.0.1",
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"style-loader": "^1.0.0",
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import listEditor from 'wiki-list-editor';
|
||||
import axios from 'axios';
|
||||
import qs from 'querystring'
|
||||
import $ from 'jquery';
|
||||
import search from './search';
|
||||
import Mustache from 'mustache';
|
||||
|
||||
import './styles.scss';
|
||||
|
||||
|
@ -28,7 +31,7 @@ function Indicator(element, timeout) {
|
|||
done() {
|
||||
timeoutId = setTimeout(() => {
|
||||
element.classList.add('hidden')
|
||||
}, timeout*1000);
|
||||
}, timeout * 1000);
|
||||
},
|
||||
|
||||
setText(text) {
|
||||
|
@ -53,19 +56,49 @@ function addIndicator(editor, indicator) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
let holder = document.getElementById('editor');
|
||||
let editor = listEditor(holder, JSON.parse(holder.dataset.input));
|
||||
if (holder) {
|
||||
let editor = listEditor(holder, JSON.parse(holder.dataset.input));
|
||||
|
||||
editor.on('change', function () {
|
||||
let element = document.getElementById('editor');
|
||||
let indicator = Indicator(document.getElementById('save-indicator'), 2);
|
||||
let saveUrl = element.dataset.saveurl;
|
||||
let page = element.dataset.page;
|
||||
editor.on('change', function () {
|
||||
let element = document.getElementById('editor');
|
||||
let indicator = Indicator(document.getElementById('save-indicator'), 2);
|
||||
let saveUrl = element.dataset.saveurl;
|
||||
let page = element.dataset.page;
|
||||
|
||||
indicator.setText('has changes...');
|
||||
addIndicator(
|
||||
addSaver(editor, saveUrl, page, () => indicator.setText('saving...')),
|
||||
indicator
|
||||
).save()
|
||||
indicator.setText('has changes...');
|
||||
addIndicator(
|
||||
addSaver(editor, saveUrl, page, () => indicator.setText('saving...')),
|
||||
indicator
|
||||
).save()
|
||||
})
|
||||
}
|
||||
|
||||
let timeout = null;
|
||||
let searchInput = document.getElementById('search-input');
|
||||
search(searchInput).then(searcher => {
|
||||
searchInput.addEventListener('keyup', function (e) {
|
||||
clearTimeout(timeout);
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
$(searchInput).val('');
|
||||
$('#autocomplete').hide();
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = setTimeout(function () {
|
||||
let query = $(searchInput).val()
|
||||
if (query === '') {
|
||||
let autocomplete = document.getElementById('autocomplete');
|
||||
$(autocomplete).hide()
|
||||
return
|
||||
}
|
||||
$('#autocomplete').show()
|
||||
let result = searcher.idx.search(query)
|
||||
var template = document.getElementById('result-template').innerHTML;
|
||||
var rendered = Mustache.render(template, {results: result}, {}, ['[[', ']]']);
|
||||
let autocomplete = document.getElementById('autocomplete');
|
||||
autocomplete.innerHTML = rendered;
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
|
|
26
editor/src/search.js
Normal file
26
editor/src/search.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import lunr from 'lunr';
|
||||
import $ from 'jquery';
|
||||
|
||||
function search(element) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
$.get('/documents.json', function (documents) {
|
||||
let idx = lunr(function () {
|
||||
this.ref('url')
|
||||
this.field('title')
|
||||
this.field('body')
|
||||
|
||||
let lunridx = this
|
||||
$.each(documents, function (index, doc) {
|
||||
lunridx.add(doc)
|
||||
})
|
||||
})
|
||||
|
||||
resolve({
|
||||
element: element,
|
||||
idx: idx
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default search;
|
4
file.go
4
file.go
|
@ -28,6 +28,7 @@ func NewFilePages(dirname string) PagesRepository {
|
|||
|
||||
func (fp *FilePages) Get(title string) Page {
|
||||
name := strings.Replace(title, " ", "_", -1)
|
||||
title = strings.Replace(title, "_", " ", -1)
|
||||
refs, err := loadBackrefs(fp, name)
|
||||
if err != nil {
|
||||
refs = nil
|
||||
|
@ -279,6 +280,9 @@ func (fp *FilePages) AllPages() ([]Page, error) {
|
|||
|
||||
var pages []Page
|
||||
for _, file := range files {
|
||||
if file.Name()[0] == '.' {
|
||||
continue
|
||||
}
|
||||
if file.Name() == "backrefs.json" {
|
||||
continue
|
||||
}
|
||||
|
|
45
main.go
45
main.go
|
@ -590,6 +590,51 @@ func main() {
|
|||
mp = NewFilePages("data")
|
||||
|
||||
http.Handle("/auth/", &authHandler{})
|
||||
http.HandleFunc("/documents.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
type Document struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
var results []Document
|
||||
pages, err := mp.(*FilePages).AllPages()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
for _, page := range pages {
|
||||
content := page.Content
|
||||
|
||||
var listItems []struct {
|
||||
Id int
|
||||
Indented int
|
||||
Text string
|
||||
}
|
||||
|
||||
err = json.NewDecoder(strings.NewReader(content)).Decode(&listItems)
|
||||
if err == nil {
|
||||
pageText := ""
|
||||
for _, item := range listItems {
|
||||
pageText += strings.Repeat(" ", item.Indented) + "* " + item.Text + "\n"
|
||||
}
|
||||
|
||||
content = pageText
|
||||
}
|
||||
|
||||
results = append(results, Document{
|
||||
Title: page.Title,
|
||||
Body: content,
|
||||
URL: page.Name,
|
||||
})
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(&results)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
})
|
||||
http.HandleFunc("/fetchLink", func(w http.ResponseWriter, r *http.Request) {
|
||||
link := r.URL.Query().Get("url")
|
||||
u, err := url.Parse(link)
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
<div id="editor" data-input="{{ .Data }}" data-saveurl="/save/" data-page="{{ .Page }}" save-type="{{ .ContentType }}"></div>
|
||||
<script src="/public/index.bundle.js"></script>
|
||||
|
|
|
@ -5,44 +5,81 @@
|
|||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" />
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css"/>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
||||
<title>{{ .Title }} - Wiki</title>
|
||||
{{ block "content_head" . }} {{ end }}
|
||||
<style>
|
||||
#autocomplete {
|
||||
z-index: 1;
|
||||
width: 217px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 300px;
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
font-family: "Fira Code Retina", monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.lighter {
|
||||
color:#ccc;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
del {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ins {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.checklist {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.checklist--item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.checklist--item-text {
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
@import url('https://rsms.me/inter/inter.css');
|
||||
html { font-family: 'Inter', sans-serif; }
|
||||
body { font-family: 'Inter', sans-serif; }
|
||||
input.input-line { font-family: 'Inter', sans-serif; }
|
||||
@supports (font-variation-settings: normal) {
|
||||
html { font-family: 'Inter var', sans-serif; }
|
||||
body { font-family: 'Inter var', sans-serif; }
|
||||
input.input-line { font-family: 'Inter var', sans-serif; }
|
||||
|
||||
html {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
input.input-line {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
@supports (font-variation-settings: normal) {
|
||||
html {
|
||||
font-family: 'Inter var', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter var', sans-serif;
|
||||
}
|
||||
|
||||
input.input-line {
|
||||
font-family: 'Inter var', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.list-item {
|
||||
padding: 3px;
|
||||
display: flex;
|
||||
|
@ -77,6 +114,7 @@
|
|||
.selected {
|
||||
background: lightblue;
|
||||
}
|
||||
|
||||
.editor.selected {
|
||||
background: none;
|
||||
}
|
||||
|
@ -135,7 +173,8 @@
|
|||
Wiki
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
|
||||
data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
|
@ -149,13 +188,35 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="level">
|
||||
<div class="level-left"></div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<div class="field">
|
||||
<p class="control">
|
||||
<input class="search input" id="search-input" type="text" placeholder="Find a page">
|
||||
</p>
|
||||
<div id="autocomplete" class="hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
{{ template "content" . }}
|
||||
</section>
|
||||
|
||||
<div id="save-indicator" class="hidden"></div>
|
||||
</div>
|
||||
{{ block "footer_scripts" . }}{{ end }}
|
||||
|
||||
{{ block "footer_scripts" . }}
|
||||
{{ end }}
|
||||
<script src="/public/index.bundle.js"></script>
|
||||
<div id="result-template" class="hidden">
|
||||
<ul>
|
||||
[[#results]]
|
||||
<li><a href="/[[ref]]">[[ref]]</a></li>
|
||||
[[/results]]
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue
Block a user