diff --git a/config-example.yml b/config-example.yml index bd657cd1..2f2f2357 100644 --- a/config-example.yml +++ b/config-example.yml @@ -21,5 +21,8 @@ development_instance: true host_ip: 0.0.0.0 host_port: 5000 +# Guesslang service URL +guesslangUrl: http://ip:port/guess + # mongo connection string mongo_connection: root:root@db.docker.local diff --git a/docker-compose.yml b/docker-compose.yml index e8f1000e..f08d1230 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,3 +22,8 @@ services: - 5000:5000 volumes: - .:/app + +volumes: + db-data: + driver: local + diff --git a/public/assets/fonts/fusion-pixel-10px-monospaced-zh_hant.woff2 b/public/assets/fonts/fusion-pixel-10px-monospaced-zh_hant.woff2 new file mode 100644 index 00000000..157b60ad Binary files /dev/null and b/public/assets/fonts/fusion-pixel-10px-monospaced-zh_hant.woff2 differ diff --git a/public/scripts/main.js b/public/scripts/main.js index 57c22e12..b933304d 100644 --- a/public/scripts/main.js +++ b/public/scripts/main.js @@ -1,7 +1,8 @@ import { getWordwrap, getFullwidth } from "./helpers/options.js"; if (localStorage.getItem("theme") === null) - { + { + // Here to change default localStorage.setItem("theme", "myst"); } else diff --git a/public/style/components/addEditorButton.css b/public/style/components/addEditorButton.css index 504b43f7..1f012343 100644 --- a/public/style/components/addEditorButton.css +++ b/public/style/components/addEditorButton.css @@ -3,7 +3,7 @@ background-color: var(--color-mystge); display: flex; flex-direction: row; - margin-top: 20px; + margin-top: 25px; border-radius: var(--border-radius); padding: 0.35rem 1rem; padding-top: 0.5rem; diff --git a/public/style/components/footer.css b/public/style/components/footer.css index bc772c18..9d30a955 100644 --- a/public/style/components/footer.css +++ b/public/style/components/footer.css @@ -2,7 +2,7 @@ footer { background-color: var(--color-nanolight); border-radius: var(--border-radius); - margin-top: 20px; + margin-top: 10px; margin-bottom: 20px; padding: 0.5rem 1rem; display: flex; diff --git a/public/style/fonts.css b/public/style/fonts.css index abbf075a..002b0ca3 100644 --- a/public/style/fonts.css +++ b/public/style/fonts.css @@ -31,3 +31,33 @@ font-style: italic; font-display: swap; } + +@font-face { + font-family: fusion-pixel; + src: url('../assets/fonts/fusion-pixel-10px-monospaced-zh_hant.woff2'); + font-weight: normal; + font-display: swap; +} + +@font-face { + font-family: fusion-pixel; + src: url('../assets/fonts/fusion-pixel-10px-monospaced-zh_hant-bold.woff2'); + font-weight: bold; + font-display: swap; +} + +@font-face { + font-family: fusion-pixel; + src: url('../assets/fonts/fusion-pixel-10px-monospaced-zh_hant-italic.woff2'); + font-weight: normal; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: fusion-pixel; + src: url('../assets/fonts/fusion-pixel-10px-monospaced-zh_hant-bold-italic.woff2'); + font-weight: bold; + font-style: italic; + font-display: swap; +} \ No newline at end of file diff --git a/public/style/libs/codemirror-bit-fury.css b/public/style/libs/codemirror-bit-fury.css new file mode 100644 index 00000000..beb2b9ed --- /dev/null +++ b/public/style/libs/codemirror-bit-fury.css @@ -0,0 +1,50 @@ + +/** + Name: bit-fury + Deep gray background with blue accents + */ + + .cm-s-bit-fury span.cm-meta { color: #da7b69; } + .cm-s-bit-fury span.cm-number { color: #aaeac1e6; } + .cm-s-bit-fury span.cm-keyword { color: #348CD6; line-height: 1em; font-weight: normal; } + .cm-s-bit-fury span.cm-def { color: #77d08b; font-style: italic; } + .cm-s-bit-fury span.cm-variable { color: #cec990; } + .cm-s-bit-fury span.cm-variable-2 { color: #A9B7C6; } + .cm-s-bit-fury span.cm-variable-3 { color: #9876AA; } + .cm-s-bit-fury span.cm-type { color: #348CD6; font-weight: normal; } + .cm-s-bit-fury span.cm-property { color: #9CD0CB; } + .cm-s-bit-fury span.cm-operator { color: #cad6d6; } + .cm-s-bit-fury span.cm-string { color: #CE9178; } + .cm-s-bit-fury span.cm-string-2 { color: #CE9178; } + .cm-s-bit-fury span.cm-comment { color: #41922d; font-style: italic; } + .cm-s-bit-fury span.cm-link { color: #CE9178; } + .cm-s-bit-fury span.cm-atom { color: #CE9178; } + .cm-s-bit-fury span.cm-error { color: #BC3F3C; } + .cm-s-bit-fury span.cm-tag { color: #348CD6; font-weight: normal; font-style: normal; } + .cm-s-bit-fury span.cm-attribute { color: #6897bb; } + .cm-s-bit-fury span.cm-qualifier { color: #D7BA7D; } + .cm-s-bit-fury span.cm-bracket { color: #798080; } + .cm-s-bit-fury span.cm-builtin { color: #FF9E59; } + .cm-s-bit-fury span.cm-special { color: #FF9E59; } + .cm-s-bit-fury span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;} + .cm-s-bit-fury span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;} + .cm-s-bit-fury .CodeMirror-cursor { border-left: 1px solid #d3dae2; } + .cm-s-bit-fury .CodeMirror-activeline-background { background: #323232; } + .cm-s-bit-fury .CodeMirror-gutters { border-right: none; } + .cm-s-bit-fury .CodeMirror-guttermarker { color: #FFEE80; } + .cm-s-bit-fury .CodeMirror-guttermarker-subtle { color: #D0D0D0; } + .cm-s-bit-fury .CodeMirrir-linenumber { color: #606366; } + .cm-s-bit-fury .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } + + .cm-s-bit-fury div.CodeMirror-selected { background: #2f4c67; } + + .CodeMirror-hints.myst { + font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; + color: #9C9E9E; + background-color: #3B3E3F !important; + } + + .CodeMirror-hints.myst .CodeMirror-hint-active { + background-color: #494D4E !important; + color: #9C9E9E !important; + } diff --git a/public/style/libs/codemirror-fury.css b/public/style/libs/codemirror-fury.css new file mode 100644 index 00000000..b3b75eef --- /dev/null +++ b/public/style/libs/codemirror-fury.css @@ -0,0 +1,50 @@ + +/** + Name: fury + Deep gray background with blue accents + */ + + .cm-s-fury span.cm-meta { color: #da7b69; } + .cm-s-fury span.cm-number { color: #aaeac1e6; } + .cm-s-fury span.cm-keyword { color: #348CD6; line-height: 1em; font-weight: normal; } + .cm-s-fury span.cm-def { color: #77d08b; font-style: italic; } + .cm-s-fury span.cm-variable { color: #cec990; } + .cm-s-fury span.cm-variable-2 { color: #A9B7C6; } + .cm-s-fury span.cm-variable-3 { color: #9876AA; } + .cm-s-fury span.cm-type { color: #348CD6; font-weight: normal; } + .cm-s-fury span.cm-property { color: #9CD0CB; } + .cm-s-fury span.cm-operator { color: #cad6d6; } + .cm-s-fury span.cm-string { color: #CE9178; } + .cm-s-fury span.cm-string-2 { color: #CE9178; } + .cm-s-fury span.cm-comment { color: #41922d; font-style: italic; } + .cm-s-fury span.cm-link { color: #CE9178; } + .cm-s-fury span.cm-atom { color: #CE9178; } + .cm-s-fury span.cm-error { color: #BC3F3C; } + .cm-s-fury span.cm-tag { color: #348CD6; font-weight: normal; font-style: normal; } + .cm-s-fury span.cm-attribute { color: #6897bb; } + .cm-s-fury span.cm-qualifier { color: #D7BA7D; } + .cm-s-fury span.cm-bracket { color: #798080; } + .cm-s-fury span.cm-builtin { color: #FF9E59; } + .cm-s-fury span.cm-special { color: #FF9E59; } + .cm-s-fury span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;} + .cm-s-fury span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;} + .cm-s-fury .CodeMirror-cursor { border-left: 1px solid #d3dae2; } + .cm-s-fury .CodeMirror-activeline-background { background: #323232; } + .cm-s-fury .CodeMirror-gutters { border-right: none; } + .cm-s-fury .CodeMirror-guttermarker { color: #FFEE80; } + .cm-s-fury .CodeMirror-guttermarker-subtle { color: #D0D0D0; } + .cm-s-fury .CodeMirrir-linenumber { color: #606366; } + .cm-s-fury .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } + + .cm-s-fury div.CodeMirror-selected { background: #2f4c67; } + + .CodeMirror-hints.myst { + font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; + color: #9C9E9E; + background-color: #3B3E3F !important; + } + + .CodeMirror-hints.myst .CodeMirror-hint-active { + background-color: #494D4E !important; + color: #9C9E9E !important; + } diff --git a/public/style/main.css b/public/style/main.css index c40f1fe9..20e44faa 100644 --- a/public/style/main.css +++ b/public/style/main.css @@ -1,5 +1,7 @@ @import url(fonts.css); +@import url(theme-fury.css); +@import url(theme-bit-fury.css); @import url(theme-myst.css); @import url(theme-catppuccin.css); @import url(theme-darkplus.css); @@ -15,6 +17,8 @@ @import url(components/footer.css); @import url(libs/codemirror.css); +@import url(libs/codemirror-fury.css); +@import url(libs/codemirror-bit-fury.css); @import url(libs/codemirror-myst.css); @import url(libs/codemirror-catppuccin.css); @import url(libs/codemirror-darkplus.css); diff --git a/public/style/pages/home.css b/public/style/pages/home.css index 91725113..d870dfc8 100644 --- a/public/style/pages/home.css +++ b/public/style/pages/home.css @@ -9,7 +9,8 @@ padding: 1rem; z-index: 100; justify-content: space-between; - margin-top: 20px; + align-items: center; + margin-top: 25px; -webkit-transition: all 250ms ease-in-out; -moz-transition: all 250ms ease-in-out; -ms-transition: all 250ms ease-in-out; @@ -30,7 +31,7 @@ color: var(--color-nano); border-radius: var(--border-radius); padding: 0.5rem 1rem; - margin-bottom: 20px; + /* margin-bottom: 20px; */ width: 30%; text-align: center; } @@ -51,14 +52,13 @@ color: var(--color-nanolightlight); } -#home .paste-options-bottom-sticky -{ +#home .paste-options-bottom-sticky { -webkit-box-shadow: 0px -4px 13px 1px rgba(0,0,0,0.75); -moz-box-shadow: 0px -4px 13px 1px rgba(0,0,0,0.75); - box-shadow: 0px -4px 13px 1px rgba(0,0,0,0.75); - border-bottom: none; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; + box-shadow: 0px 0px 20px 5px rgb(93 93 93 / 50%); + bottom: 20px; + border: #91b9e0 3px solid; + border-radius: 15px; } #home .paste-options-bottom-1px @@ -97,6 +97,7 @@ border-radius: var(--border-radius); font-size: var(--font-size-normal); width: 30%; + margin-top: 20px; } #home #drop-area diff --git a/public/style/theme-bit-fury.css b/public/style/theme-bit-fury.css new file mode 100644 index 00000000..08eeddab --- /dev/null +++ b/public/style/theme-bit-fury.css @@ -0,0 +1,20 @@ +html.bit-fury +{ + --color-nano: #18283d; + --color-nanonotsolight: #2d3439; + --color-nanolight: #222e37; + --color-nanolightlight: #afafaf; + --color-white: #6d7f91; + --color-mystge: #527fab; + --color-mystlue: #2d688d; + --color-mysted: #4b97ed; + --color-mysteen: #2ec933; + --font-size-code: 1rem; + --font-size-small: 1rem; + --font-size-normal: 1.2rem; + --font-size-medium: 1.5rem; + --font-size-big: 3rem; + --border-radius: 0.3rem; + --break-small: 640px; + --font-stack: 'fusion-pixel', monospace; +} \ No newline at end of file diff --git a/public/style/theme-fury.css b/public/style/theme-fury.css new file mode 100644 index 00000000..33aca9fa --- /dev/null +++ b/public/style/theme-fury.css @@ -0,0 +1,20 @@ +html.fury +{ + --color-nano: #18283d; + --color-nanonotsolight: #2d3439; + --color-nanolight: #222e37; + --color-nanolightlight: #afafaf; + --color-white: #6d7f91; + --color-mystge: #527fab; + --color-mystlue: #2d688d; + --color-mysted: #4b97ed; + --color-mysteen: #2ec933; + --font-size-code: 1rem; + --font-size-small: 1rem; + --font-size-normal: 1.2rem; + --font-size-medium: 1.5rem; + --font-size-big: 3rem; + --border-radius: 0.3rem; + --break-small: 640px; + --font-stack: 'UbuntuMono', monospace; +} \ No newline at end of file diff --git a/source/app.d b/source/app.d index 93b88e7d..a69b4cc2 100644 --- a/source/app.d +++ b/source/app.d @@ -39,6 +39,9 @@ public void main() import pastemyst.paste : deleteExpiredPastes; import pastemyst.auth : deleteExpiredSessions; import pastemyst.data : config; + import vibe.core.log : setLogLevel, LogLevel; + + setLogLevel(LogLevel.info); URLRouter router = new URLRouter(); diff --git a/source/pastemyst/data/config.d b/source/pastemyst/data/config.d index 0d018cc2..fdd12ad6 100644 --- a/source/pastemyst/data/config.d +++ b/source/pastemyst/data/config.d @@ -46,6 +46,11 @@ public struct Config + html code that will get added to every page in the head tag +/ public string headHTML = ""; + + /++ + + optional guess-lang detection API URL + +/ + public string languageDetectionUrl = ""; } /++ @@ -166,5 +171,14 @@ static this() { _config.headHTML = cfg["head_html"].as!string(); } + + if (cfg.containsKey("language_detection_url")) + { + _config.languageDetectionUrl = cfg["language_detection_url"].as!string(); + } + else if (cfg.containsKey("guesslangUrl")) + { + _config.languageDetectionUrl = cfg["guesslangUrl"].as!string(); + } } } diff --git a/source/pastemyst/paste/create.d b/source/pastemyst/paste/create.d index 07610202..11bf9c56 100644 --- a/source/pastemyst/paste/create.d +++ b/source/pastemyst/paste/create.d @@ -179,6 +179,10 @@ private string autodetectLanguage(string pasteId, Pasty pasty) @safe import std.process : execute; import std.string : strip; import pastemyst.data : languages; + import vibe.http.client : requestHTTP, HTTPMethod; + import vibe.data.json : Json, parseJsonString; + import std.conv : to; + import vibe.core.log : logInfo, logError; // check if the language can be gotten from the extension auto ext = extension(pasty.title); @@ -207,10 +211,57 @@ private string autodetectLanguage(string pasteId, Pasty pasty) @safe { lang = res.output.strip(); } - } - catch(Exception) {} - remove(filename); + // Create API request + Json requestData = Json.emptyObject; + requestData["text"] = pasty.code; + requestData["verbose"] = false; + requestData["fineTune"] = false; + requestData["expectedRelativeConfidence"] = 0.2; + + // Set the guesslang API link + import pastemyst.data : config; + string endpoint = config.languageDetectionUrl.length ? config.languageDetectionUrl : "https://guesslang.waterwater.moe/guess"; + + // call guesslang API + requestHTTP(endpoint, + (scope req) { + req.method = HTTPMethod.POST; + req.headers["Content-Type"] = "application/json"; + req.writeJsonBody(requestData); + }, + (scope res) { + if (res.statusCode == 200) + { + auto responseJson = res.readJson(); + if ("languageId" in responseJson) + { + string langId = responseJson["languageId"].get!string; + + // try to map languageId to full name + auto fullLangName = getLanguageName(langId); + if (fullLangName !is null) + { + lang = fullLangName; + } + else + { + lang = langId; + } + } + } + } + ); + } + catch(Exception e) + { + // if API call fails, set language as Plain Text + logError("Language detection API failed: %s", e.msg); + lang = "Plain Text"; + } + // Logging to console + logInfo("Autodetected language for paste %s: %s", pasteId, lang); + return lang; } diff --git a/views/footer.dt b/views/footer.dt index 90be65b4..1fc0a157 100644 --- a/views/footer.dt +++ b/views/footer.dt @@ -5,8 +5,7 @@ footer - import pastemyst.data : Paste; - string year = Clock.currTime().year.to!string(); .copyright - p copyright © #[a(href="https://github.com/codemyst", target="_blank") codemyst] #{year} - p contact: code at myst.rs + p code at #[a(href="https://github.com/codemyst", target="_blank") myst.rs] .theme label(for="theme") theme: select(name="theme", id="theme-picker") @@ -17,4 +16,8 @@ footer option(value="dracula") dracula option(value="monokai") monokai option(value="solarized") solarized + //- Adding the theme I like + option(value="bit-fury") bit-fury + option(value="fury") fury + //- Adding the theme I like .paste-amount #{getCollectionCount!Paste()} currently active pastes