feat: replace Monaco by Ace #32
9 changed files with 114 additions and 73 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -5,6 +5,7 @@
|
|||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.43.3",
|
||||
"bootstrap": "^5.3.8"
|
||||
}
|
||||
},
|
||||
|
@ -18,6 +19,11 @@
|
|||
"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": {
|
||||
"version": "5.3.8",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.43.3",
|
||||
"bootstrap": "^5.3.8"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,12 +31,14 @@ The file format is **JSON**:
|
|||
* **prometheus_route** (string): Route to expose Prometheus metrics (default "/metrics")
|
||||
* **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)
|
||||
* **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")
|
||||
* **enable_upload_file_button** (bool): Display the upload file button in the UI (default true)
|
||||
* **tls_cert_file** (string): Path to TLS certificate 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.
|
||||
* **disable_editor** (bool): Disable Ace editor.
|
||||
|
||||
The configuration file is not required but the service might not be exposed to the public.
|
||||
|
||||
|
@ -85,12 +87,12 @@ Errors return **500 Server Internal Error** with the **JSON** payload:
|
|||
|
||||
The web interface depends on:
|
||||
|
||||
- [Ace](https://ace.c9.io/)
|
||||
- [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.
|
||||
|
||||
If you would like to download them to serve them locally:
|
||||
If you would like to download and serve them locally:
|
||||
|
||||
```
|
||||
npm install
|
||||
|
@ -106,8 +108,7 @@ Then configure the local directories:
|
|||
|
||||
```json
|
||||
{
|
||||
"ace_directory": "./node_modules/ace-builds",
|
||||
"bootstrap_directory": "./node_modules/bootstrap/dist"
|
||||
}
|
||||
```
|
||||
|
||||
Downloading Monaco Editor is not supported yet.
|
||||
```
|
|
@ -28,7 +28,9 @@ type Config struct {
|
|||
EnableUploadFileButton bool `json:"enable_upload_file_button"`
|
||||
TLSCertFile string `json:"tls_cert_file"`
|
||||
TLSKeyFile string `json:"tls_key_file"`
|
||||
AceDirectory string `json:"ace_directory"`
|
||||
BootstrapDirectory string `json:"bootstrap_directory"`
|
||||
DisableEditor bool `json:"disable_editor"`
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
|
@ -56,22 +58,21 @@ func NewConfig() *Config {
|
|||
PrometheusNotesMetric: "collerd_notes",
|
||||
ObservationInterval: 60,
|
||||
Languages: []string{
|
||||
"Text",
|
||||
"CSS",
|
||||
"Dockerfile",
|
||||
"Go",
|
||||
"HCL",
|
||||
"HTML",
|
||||
"Javascript",
|
||||
"JSON",
|
||||
"Markdown",
|
||||
"Perl",
|
||||
"Python",
|
||||
"Ruby",
|
||||
"Rust",
|
||||
"Shell",
|
||||
"SQL",
|
||||
"YAML",
|
||||
"css",
|
||||
"dockerfile",
|
||||
"golang",
|
||||
"html",
|
||||
"javascript",
|
||||
"json",
|
||||
"markdown",
|
||||
"perl",
|
||||
"python",
|
||||
"ruby",
|
||||
"rust",
|
||||
"sh",
|
||||
"sql",
|
||||
"text",
|
||||
"yaml",
|
||||
},
|
||||
Language: "text",
|
||||
EnableUploadFileButton: true,
|
||||
|
|
|
@ -21,12 +21,15 @@ type PageData struct {
|
|||
Version string
|
||||
Expirations []int
|
||||
Expiration int
|
||||
Language string
|
||||
Languages []string
|
||||
Err error
|
||||
URL string
|
||||
Note *Note
|
||||
EnableUploadFileButton bool
|
||||
AceDirectory string
|
||||
BootstrapDirectory string
|
||||
DisableEditor bool
|
||||
}
|
||||
|
||||
type HomeHandler struct {
|
||||
|
|
|
@ -114,8 +114,11 @@ func (s *Server) Start() error {
|
|||
Title: s.config.Title,
|
||||
Expirations: s.config.Expirations,
|
||||
Expiration: s.config.Expiration,
|
||||
Language: s.config.Language,
|
||||
Languages: s.config.Languages,
|
||||
AceDirectory: s.config.AceDirectory,
|
||||
BootstrapDirectory: s.config.BootstrapDirectory,
|
||||
DisableEditor: s.config.DisableEditor,
|
||||
}
|
||||
|
||||
if s.config.ShowVersion {
|
||||
|
@ -160,6 +163,10 @@ func (s *Server) Start() error {
|
|||
}
|
||||
r.Path("/{id:[a-zA-Z0-9]+}.html").Handler(webNoteHandler).Methods("GET")
|
||||
|
||||
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 != "" {
|
||||
r.PathPrefix("/static/bootstrap/").Handler(http.StripPrefix("/static/bootstrap/", http.FileServer(http.Dir(s.config.BootstrapDirectory))))
|
||||
}
|
||||
|
@ -169,10 +176,10 @@ func (s *Server) Start() error {
|
|||
addr := fmt.Sprintf("%s:%d", s.config.ListenAddress, s.config.ListenPort)
|
||||
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
<title>{{.Title}}</title>
|
||||
<meta charset="utf-8">
|
||||
<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>
|
||||
{{end}}
|
|
@ -40,15 +40,17 @@
|
|||
<select class="form-select" aria-label="Expiration" id="expiration" name="expiration">
|
||||
<option disabled>Expiration</option>
|
||||
{{range $exp := .Expirations}}
|
||||
<option {{ if eq $exp $.Expiration }}selected="selected"{{end}} value="{{$exp}}">{{HumanDuration $exp}}</option>
|
||||
<option {{ if eq $exp $.Expiration }}selected="selected" {{end}} value="{{$exp}}">
|
||||
{{HumanDuration $exp}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<select class="form-select" aria-label="Language" id="language" name="language">
|
||||
<option selected="selected" value="" disabled>Language</option>
|
||||
{{range .Languages}}
|
||||
<option value="{{lower .}}">{{.}}</option>
|
||||
<option disabled>Language</option>
|
||||
{{range $language := .Languages}}
|
||||
<option {{ if eq $language $.Language }}selected="selected" {{end}}value="{{lower .}}">
|
||||
{{$language}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -56,11 +58,15 @@
|
|||
</div>
|
||||
|
||||
<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 id="editor" name="editor" class="form-control"
|
||||
style="height: 300px; resize: vertical; overflow: auto;"></div>
|
||||
<input type="hidden" id="content" />
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="container mb-4">
|
||||
<div class="row text-center justify-content-center">
|
||||
|
@ -69,38 +75,40 @@
|
|||
</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>
|
||||
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') {
|
||||
editor.setTheme("ace/theme/github_light_default");
|
||||
} else {
|
||||
editor.setTheme("ace/theme/github_dark");
|
||||
}
|
||||
|
||||
require(['vs/editor/editor.main'], function () {
|
||||
var editor = monaco.editor.create(document.getElementById('editor'), {
|
||||
theme: document.documentElement.getAttribute('data-bs-theme') == 'light' ? "vs" : "vs-dark",
|
||||
language: document.getElementById("language").value,
|
||||
});
|
||||
// Syntax highlighting
|
||||
document.getElementById("language").addEventListener("change", (e) => {
|
||||
if (e.target.value != "") {
|
||||
editor.getSession().setMode("ace/mode/" + e.target.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Syntax highlighting
|
||||
document.getElementById("language").addEventListener("change", (e) => {
|
||||
if (e.target.value != "") {
|
||||
monaco.editor.setModelLanguage(editor.getModel(), e.target.value);
|
||||
}
|
||||
});
|
||||
// Dark mode
|
||||
document.getElementById("lightSwitch").addEventListener("click", () => {
|
||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||
editor.setTheme("ace/theme/github_light_default")
|
||||
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
||||
editor.setTheme("ace/theme/github_dark")
|
||||
}
|
||||
});
|
||||
|
||||
// Dark mode
|
||||
document.getElementById("lightSwitch").addEventListener("click", () => {
|
||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||
monaco.editor.setTheme("vs")
|
||||
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
||||
monaco.editor.setTheme("vs-dark")
|
||||
}
|
||||
});
|
||||
|
||||
// Copy content on submit
|
||||
document.getElementById("form").addEventListener("formdata", (e) => {
|
||||
e.formData.append('content', editor.getModel().getValue());
|
||||
});
|
||||
// Copy content on submit
|
||||
document.getElementById("form").addEventListener("formdata", (e) => {
|
||||
e.formData.append('content', editor.getValue());
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
</form>
|
||||
|
||||
{{block "footer" .}}{{end}}
|
||||
|
|
|
@ -33,32 +33,44 @@
|
|||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{if .DisableEditor}}
|
||||
<div class="row">
|
||||
<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="height: 300px; resize: vertical; overflow: auto;"></div>
|
||||
style="min-height: 300px; resize: vertical; overflow: auto;"></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>
|
||||
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 () {
|
||||
var editor = monaco.editor.create(document.getElementById('editor'), {
|
||||
theme: document.documentElement.getAttribute('data-bs-theme') == 'light' ? "vs" : "vs-dark",
|
||||
language: "{{.Note.Language}}",
|
||||
readOnly: true,
|
||||
value: "{{string .Note.Content}}"
|
||||
});
|
||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||
editor.setTheme("ace/theme/github_light_default");
|
||||
} else {
|
||||
editor.setTheme("ace/theme/github_dark");
|
||||
}
|
||||
|
||||
// Dark mode
|
||||
document.getElementById("lightSwitch").addEventListener("click", () => {
|
||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||
monaco.editor.setTheme("vs")
|
||||
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
||||
monaco.editor.setTheme("vs-dark")
|
||||
}
|
||||
});
|
||||
// Dark mode
|
||||
document.getElementById("lightSwitch").addEventListener("click", () => {
|
||||
if (document.documentElement.getAttribute('data-bs-theme') == 'light') {
|
||||
editor.setTheme("ace/theme/github_light_default")
|
||||
} else if (document.documentElement.getAttribute('data-bs-theme') == 'dark') {
|
||||
editor.setTheme("ace/theme/github_dark")
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue