- Remove the Monaco Editor because it was to heavy and hard to integrate - Use Ace instead - Use the lowercase identifier for languages (ex: "Text" -> "text") - Select automatically the default language in the drop down to create a note (like the expiration) - Add `ace_directory` to serve assets from a local folder instead of a CDN - "hcl" syntax highlighting has been removed - "go" syntax highlighting has been renamed to "golang" - Add option to disable the editor Fixes #32. Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
parent
9e0254c0b5
commit
b5701b5a4d
9 changed files with 113 additions and 73 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -5,6 +5,7 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ace-builds": "^1.43.3",
|
||||||
"bootstrap": "^5.3.8"
|
"bootstrap": "^5.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -18,6 +19,11 @@
|
||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ace-builds": {
|
||||||
|
"version": "1.43.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.3.tgz",
|
||||||
|
"integrity": "sha512-MCl9rALmXwIty/4Qboijo/yNysx1r6hBTzG+6n/TiOm5LFhZpEvEIcIITPFiEOEFDfgBOEmxu+a4f54LEFM6Sg=="
|
||||||
|
},
|
||||||
"node_modules/bootstrap": {
|
"node_modules/bootstrap": {
|
||||||
"version": "5.3.8",
|
"version": "5.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ace-builds": "^1.43.3",
|
||||||
"bootstrap": "^5.3.8"
|
"bootstrap": "^5.3.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,13 @@ The file format is **JSON**:
|
||||||
* **prometheus_route** (string): Route to expose Prometheus metrics (default "/metrics")
|
* **prometheus_route** (string): Route to expose Prometheus metrics (default "/metrics")
|
||||||
* **prometheus_notes_metric** (string): Name of the notes count metric (default "collerd_notes")
|
* **prometheus_notes_metric** (string): Name of the notes count metric (default "collerd_notes")
|
||||||
* **observation_internal** (int): Number of seconds to wait between two observations (default 60)
|
* **observation_internal** (int): Number of seconds to wait between two observations (default 60)
|
||||||
* **languages** ([]string): List of supported [languages](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages)
|
* **languages** ([]string): List of supported [languages](https://github.com/ajaxorg/ace/tree/master/src/mode)
|
||||||
* **language** (string): Default language (default "text")
|
* **language** (string): Default language (default "text")
|
||||||
* **tls_cert_file** (string): Path to TLS certificate file to enable HTTPS
|
* **tls_cert_file** (string): Path to TLS certificate file to enable HTTPS
|
||||||
* **tls_key_file** (string): Path to TLS key file to enable HTTPS
|
* **tls_key_file** (string): Path to TLS key file to enable HTTPS
|
||||||
|
* **ace_directory** (string): Serve [Ace](hhttps://ace.c9.io/) assets from this local directory (ex: "./node_modules/ace-builds"). See **Dependencies** for details.
|
||||||
* **bootstrap_directory** (string): Serve [Bootstrap](https://getbootstrap.com/) assets from this local directory (ex: "./node_modules/bootstrap/dist"). See **Dependencies** for details.
|
* **bootstrap_directory** (string): Serve [Bootstrap](https://getbootstrap.com/) assets from this local directory (ex: "./node_modules/bootstrap/dist"). See **Dependencies** for details.
|
||||||
|
* **disable_editor** (bool): Disable Ace editor.
|
||||||
|
|
||||||
The configuration file is not required but the service might not be exposed to the public.
|
The configuration file is not required but the service might not be exposed to the public.
|
||||||
|
|
||||||
|
@ -107,12 +109,12 @@ Errors return **500 Server Internal Error** with the **JSON** payload:
|
||||||
|
|
||||||
The web interface depends on:
|
The web interface depends on:
|
||||||
|
|
||||||
|
- [Ace](https://ace.c9.io/)
|
||||||
- [Bootstrap](https://getbootstrap.com/)
|
- [Bootstrap](https://getbootstrap.com/)
|
||||||
- [Monaco Editor](https://github.com/microsoft/monaco-editor/)
|
|
||||||
|
|
||||||
By default, those dependencies are fetched from **remote CDN** services by the client.
|
By default, those dependencies are fetched from **remote CDN** services by the client.
|
||||||
|
|
||||||
If you would like to download them to serve them locally:
|
If you would like to download and serve them locally:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
|
@ -128,8 +130,7 @@ Then configure the local directories:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"ace_directory": "./node_modules/ace-builds",
|
||||||
"bootstrap_directory": "./node_modules/bootstrap/dist"
|
"bootstrap_directory": "./node_modules/bootstrap/dist"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Downloading Monaco Editor is not supported yet.
|
|
||||||
|
|
|
@ -31,7 +31,9 @@ type Config struct {
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
TLSCertFile string `json:"tls_cert_file"`
|
TLSCertFile string `json:"tls_cert_file"`
|
||||||
TLSKeyFile string `json:"tls_key_file"`
|
TLSKeyFile string `json:"tls_key_file"`
|
||||||
|
AceDirectory string `json:"ace_directory"`
|
||||||
BootstrapDirectory string `json:"bootstrap_directory"`
|
BootstrapDirectory string `json:"bootstrap_directory"`
|
||||||
|
DisableEditor bool `json:"disable_editor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() *Config {
|
func NewConfig() *Config {
|
||||||
|
@ -59,22 +61,21 @@ func NewConfig() *Config {
|
||||||
PrometheusNotesMetric: "collerd_notes",
|
PrometheusNotesMetric: "collerd_notes",
|
||||||
ObservationInterval: 60,
|
ObservationInterval: 60,
|
||||||
Languages: []string{
|
Languages: []string{
|
||||||
"Text",
|
"css",
|
||||||
"CSS",
|
"dockerfile",
|
||||||
"Dockerfile",
|
"golang",
|
||||||
"Go",
|
"html",
|
||||||
"HCL",
|
"javascript",
|
||||||
"HTML",
|
"json",
|
||||||
"Javascript",
|
"markdown",
|
||||||
"JSON",
|
"perl",
|
||||||
"Markdown",
|
"python",
|
||||||
"Perl",
|
"ruby",
|
||||||
"Python",
|
"rust",
|
||||||
"Ruby",
|
"sh",
|
||||||
"Rust",
|
"sql",
|
||||||
"Shell",
|
"text",
|
||||||
"SQL",
|
"yaml",
|
||||||
"YAML",
|
|
||||||
},
|
},
|
||||||
Language: "text",
|
Language: "text",
|
||||||
EnableUploadFileButton: true,
|
EnableUploadFileButton: true,
|
||||||
|
|
|
@ -23,6 +23,7 @@ type PageData struct {
|
||||||
Expirations []int
|
Expirations []int
|
||||||
Expiration int
|
Expiration int
|
||||||
Languages []string
|
Languages []string
|
||||||
|
Language string
|
||||||
Err error
|
Err error
|
||||||
URL string
|
URL string
|
||||||
Note *Note
|
Note *Note
|
||||||
|
@ -30,7 +31,9 @@ type PageData struct {
|
||||||
EnableUploadFileButton bool
|
EnableUploadFileButton bool
|
||||||
AllowClientEncryptionKey bool
|
AllowClientEncryptionKey bool
|
||||||
AllowNoEncryption bool
|
AllowNoEncryption bool
|
||||||
|
AceDirectory string
|
||||||
BootstrapDirectory string
|
BootstrapDirectory string
|
||||||
|
DisableEditor bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type HomeHandler struct {
|
type HomeHandler struct {
|
||||||
|
|
|
@ -120,6 +120,9 @@ func (s *Server) Start() error {
|
||||||
Expirations: s.config.Expirations,
|
Expirations: s.config.Expirations,
|
||||||
Expiration: s.config.Expiration,
|
Expiration: s.config.Expiration,
|
||||||
Languages: s.config.Languages,
|
Languages: s.config.Languages,
|
||||||
|
Language: s.config.Language,
|
||||||
|
AceDirectory: s.config.AceDirectory,
|
||||||
|
DisableEditor: s.config.DisableEditor,
|
||||||
BootstrapDirectory: s.config.BootstrapDirectory,
|
BootstrapDirectory: s.config.BootstrapDirectory,
|
||||||
EnableUploadFileButton: s.config.EnableUploadFileButton,
|
EnableUploadFileButton: s.config.EnableUploadFileButton,
|
||||||
EnablePasswordProtection: s.config.EnablePasswordProtection,
|
EnablePasswordProtection: s.config.EnablePasswordProtection,
|
||||||
|
@ -225,6 +228,10 @@ func (s *Server) Start() error {
|
||||||
}
|
}
|
||||||
r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(protectedWebNoteHandler).Methods("POST")
|
r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(protectedWebNoteHandler).Methods("POST")
|
||||||
|
|
||||||
|
if s.config.AceDirectory != "" {
|
||||||
|
r.PathPrefix("/static/ace-builds/").Handler(http.StripPrefix("/static/ace-builds/", http.FileServer(http.Dir(s.config.AceDirectory))))
|
||||||
|
}
|
||||||
|
|
||||||
if s.config.BootstrapDirectory != "" {
|
if s.config.BootstrapDirectory != "" {
|
||||||
r.PathPrefix("/static/bootstrap/").Handler(http.StripPrefix("/static/bootstrap/", http.FileServer(http.Dir(s.config.BootstrapDirectory))))
|
r.PathPrefix("/static/bootstrap/").Handler(http.StripPrefix("/static/bootstrap/", http.FileServer(http.Dir(s.config.BootstrapDirectory))))
|
||||||
}
|
}
|
||||||
|
@ -234,10 +241,10 @@ func (s *Server) Start() error {
|
||||||
addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort)
|
addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort)
|
||||||
|
|
||||||
if s.config.HasTLS() {
|
if s.config.HasTLS() {
|
||||||
s.logger.Info(fmt.Sprintf("listening to %s:%d (https)", s.config.ListenAddress, s.config.ListenPort))
|
s.logger.Info(fmt.Sprintf("listening to %s:%d", s.config.ListenAddress, s.config.ListenPort), slog.Any("scheme", "http"))
|
||||||
return http.ListenAndServeTLS(addr, s.config.TLSCertFile, s.config.TLSKeyFile, r)
|
return http.ListenAndServeTLS(addr, s.config.TLSCertFile, s.config.TLSKeyFile, r)
|
||||||
} else {
|
} else {
|
||||||
s.logger.Info(fmt.Sprintf("listening to %s:%d (http)", s.config.ListenAddress, s.config.ListenPort))
|
s.logger.Info(fmt.Sprintf("listening to %s:%d", s.config.ListenAddress, s.config.ListenPort), slog.Any("scheme", "https"))
|
||||||
return http.ListenAndServe(addr, r)
|
return http.ListenAndServe(addr, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
<title>{{.Title}}</title>
|
<title>{{.Title}}</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link href="{{if ne .BootstrapDirectory ``}}/static/bootstrap/css/bootstrap.min.css{{else}}https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css{{end}}" rel="stylesheet">
|
<link
|
||||||
|
href="{{if .BootstrapDirectory}}/static/bootstrap/css/bootstrap.min.css{{else}}https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css{{end}}"
|
||||||
|
rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
{{end}}
|
{{end}}
|
|
@ -59,9 +59,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<select class="form-select" aria-label="Language" id="language" name="language">
|
<select class="form-select" aria-label="Language" id="language" name="language">
|
||||||
<option selected="selected" value="" disabled>Language</option>
|
<option disabled>Language</option>
|
||||||
{{range .Languages}}
|
{{range $language := .Languages}}
|
||||||
<option value="{{lower .}}">{{.}}</option>
|
<option {{ if eq $language $.Language }}selected="selected" {{end}}value="{{lower .}}">
|
||||||
|
{{$language}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,11 +70,15 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container mb-4">
|
<div class="container mb-4">
|
||||||
|
{{if .DisableEditor}}
|
||||||
|
<textarea class="form-control" id="content" name="content" cols="40" rows="12"></textarea>
|
||||||
|
{{else}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="editor" name="editor" class="form-control"
|
<div id="editor" name="editor" class="form-control"
|
||||||
style="height: 300px; resize: vertical; overflow: auto;"></div>
|
style="min-height: 300px; resize: vertical; overflow: auto;"></div>
|
||||||
<input type="hidden" id="content" />
|
<input type="hidden" id="content" />
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="container mb-4">
|
<div class="container mb-4">
|
||||||
<div class="row text-center justify-content-center">
|
<div class="row text-center justify-content-center">
|
||||||
|
@ -82,38 +87,40 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs/loader.min.js"></script>
|
{{if eq false .DisableEditor}}
|
||||||
|
<script
|
||||||
|
src="{{if .AceDirectory}}/static/ace-builds{{else}}https://cdn.jsdelivr.net/npm/ace-builds@1.43.3{{end}}/src-noconflict/ace.js"
|
||||||
|
type="text/javascript" charset="utf-8"></script>
|
||||||
<script>
|
<script>
|
||||||
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs' } });
|
var editor = ace.edit("editor");
|
||||||
|
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||||
require(['vs/editor/editor.main'], function () {
|
editor.setTheme("ace/theme/github_light_default");
|
||||||
var editor = monaco.editor.create(document.getElementById('editor'), {
|
} else {
|
||||||
theme: document.documentElement.getAttribute('data-bs-theme') == 'light' ? "vs" : "vs-dark",
|
editor.setTheme("ace/theme/github_dark");
|
||||||
language: document.getElementById("language").value,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Syntax highlighting
|
// Syntax highlighting
|
||||||
document.getElementById("language").addEventListener("change", (e) => {
|
document.getElementById("language").addEventListener("change", (e) => {
|
||||||
if (e.target.value != "") {
|
if (e.target.value != "") {
|
||||||
monaco.editor.setModelLanguage(editor.getModel(), e.target.value);
|
editor.getSession().setMode("ace/mode/" + e.target.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dark mode
|
// Dark mode
|
||||||
document.getElementById("lightSwitch").addEventListener("click", () => {
|
document.getElementById("lightSwitch").addEventListener("click", () => {
|
||||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||||
monaco.editor.setTheme("vs")
|
editor.setTheme("ace/theme/github_light_default")
|
||||||
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
||||||
monaco.editor.setTheme("vs-dark")
|
editor.setTheme("ace/theme/github_dark")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Copy content on submit
|
// Copy content on submit
|
||||||
document.getElementById("form").addEventListener("formdata", (e) => {
|
document.getElementById("form").addEventListener("formdata", (e) => {
|
||||||
e.formData.append('content', editor.getModel().getValue());
|
e.formData.append('content', editor.getValue());
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{{end}}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{block "footer" .}}{{end}}
|
{{block "footer" .}}{{end}}
|
||||||
|
|
|
@ -20,32 +20,44 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{{if .DisableEditor}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="editor" name="editor" class="form-control" style="height: 300px; resize: vertical; overflow: auto;">
|
<pre class="border px-3 pt-3" style="min-height: 300px; resize: vertical; overflow: auto;">
|
||||||
|
{{string .Note.Content}}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="row">
|
||||||
|
<div id="editor" name="editor" class="form-control" style="min-height: 300px; resize: vertical; overflow: auto;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs/loader.min.js"></script>
|
<script
|
||||||
|
src="{{if .AceDirectory}}/static/ace-builds{{else}}https://cdn.jsdelivr.net/npm/ace-builds@1.43.3{{end}}/src-noconflict/ace.js"
|
||||||
|
type="text/javascript" charset="utf-8"></script>
|
||||||
<script>
|
<script>
|
||||||
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs' } });
|
var editor = ace.edit("editor");
|
||||||
|
editor.setValue("{{string .Note.Content}}");
|
||||||
|
editor.setReadOnly(true);
|
||||||
|
editor.getSession().setMode("ace/mode/{{.Note.Language}}");
|
||||||
|
editor.getSession().selection.clearSelection();
|
||||||
|
editor.setOptions({ maxLines: Infinity });
|
||||||
|
|
||||||
require(['vs/editor/editor.main'], function () {
|
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||||
var editor = monaco.editor.create(document.getElementById('editor'), {
|
editor.setTheme("ace/theme/github_light_default");
|
||||||
theme: document.documentElement.getAttribute('data-bs-theme') == 'light' ? "vs" : "vs-dark",
|
} else {
|
||||||
language: "{{.Note.Language}}",
|
editor.setTheme("ace/theme/github_dark");
|
||||||
readOnly: true,
|
}
|
||||||
value: "{{string .Note.Content}}"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Dark mode
|
// Dark mode
|
||||||
document.getElementById("lightSwitch").addEventListener("click", () => {
|
document.getElementById("lightSwitch").addEventListener("click", () => {
|
||||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||||
monaco.editor.setTheme("vs")
|
editor.setTheme("ace/theme/github_light_default")
|
||||||
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
||||||
monaco.editor.setTheme("vs-dark")
|
editor.setTheme("ace/theme/github_dark")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
Loading…
Add table
Add a link
Reference in a new issue