Skip to content

Feature/add theme#827

Closed
Whitedragon115 wants to merge 4 commits intoCodeMyst:mainfrom
Whitedragon115:feature/add-theme
Closed

Feature/add theme#827
Whitedragon115 wants to merge 4 commits intoCodeMyst:mainfrom
Whitedragon115:feature/add-theme

Conversation

@Whitedragon115
Copy link
Copy Markdown
Contributor

No description provided.

1. Change the styling of the create paste button.
2. Add more font face
3. Add theme option
4. Mark where to change the main theme
1. Add guesslang detection to create paste logic.
2. add volumes to docker compose
3. add log level
Copilot AI review requested due to automatic review settings March 7, 2026 11:22
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR bundles two loosely related changes: (1) adding two new color themes ("fury" and "bit-fury") to pastemyst, including their CSS theme files, CodeMirror syntax-highlighting stylesheets, and a new "fusion-pixel" font; and (2) integrating a guesslang HTTP API for language autodetection as a supplement/replacement to the existing CLI-based autodetect tool, along with minor CSS layout adjustments and a log-level configuration change.

Changes:

  • Adds two new themes (fury and bit-fury) with associated CSS, CodeMirror styles, font definitions, and footer options.
  • Integrates a configurable external guesslang HTTP API into the language autodetection flow.
  • Miscellaneous tweaks: CSS margin/layout adjustments, docker-compose port/volume changes, and global log level set to info.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
views/footer.dt Adds fury and bit-fury theme options; removes copyright line
source/pastemyst/paste/create.d Adds guesslang HTTP API call for language autodetection; removes remove(filename) cleanup
source/pastemyst/data/config.d Adds languageDetectionUrl config field with two fallback config key names
source/app.d Sets global vibe.d log level to info at startup
public/style/theme-fury.css New fury theme CSS variables
public/style/theme-bit-fury.css New bit-fury theme CSS variables (identical to fury except font stack)
public/style/pages/home.css Minor margin/layout adjustments to paste options bar
public/style/main.css Imports new theme and CodeMirror CSS files
public/style/libs/codemirror-fury.css CodeMirror syntax highlighting for fury theme
public/style/libs/codemirror-bit-fury.css CodeMirror syntax highlighting for bit-fury theme
public/style/fonts.css Adds fusion-pixel font-face declarations (3 of 4 font files missing)
public/style/components/footer.css Reduces footer top margin
public/style/components/addEditorButton.css Increases add-editor button top margin
public/scripts/main.js Adds clarifying comment on default theme
docker-compose.yml Removes host port mapping; adds unused named volume
config-example.yml Documents new guesslang URL config (uses legacy key name)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +215 to +261
// 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";
}
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remove(filename) call has been removed from autodetectLanguage. In the original code, the temporary file written to tmp/ was cleaned up after use. Without this call, the temporary file tmp/{pasteId}-{pastyId}-autodetect is written but never deleted, causing the tmp/ directory to grow unboundedly over time as pastes are created. The remove function is still imported from std.file (line 177) but is no longer used. The file should be deleted after the detection logic completes, in a scope(exit) block or explicitly after the try/catch to ensure cleanup even on exceptions.

Copilot uses AI. Check for mistakes.
Comment on lines +215 to +260
// 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";
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guesslang API is always called whenever a paste is created with language autodetection, even when the pastemyst-autodetect command-line tool already successfully detected a language. The original code flow was: use CLI tool → fall back on failure. With this change, the API call is always made inside the try block regardless of whether the CLI succeeded, overriding CLI results and introducing an unconditional external HTTP call for every paste. If the intent is to use the API as a fallback (or primary), the condition should be clearly structured. Additionally, calling an external API unconditionally on every paste creation is a significant performance and reliability concern.

Suggested change
// 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";
// Only call external API if CLI detection did not identify a language
if (lang == "Plain Text")
{
// 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 detection fails, keep any previously detected language and log the error
logError("Language detection failed: %s", e.msg);

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +25
# Guesslang service URL
guesslangUrl: http://ip:port/guess
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The config example uses the key guesslangUrl (camelCase) for the language detection URL, but the config parsing code in config.d prefers the snake_case key language_detection_url and only falls back to guesslangUrl. This inconsistency means the example config is documenting the fallback (legacy) key rather than the preferred new key. The example should be updated to use language_detection_url to match the new preferred config key, or at a minimum show both options with a note about which is preferred.

Suggested change
# Guesslang service URL
guesslangUrl: http://ip:port/guess
# Language detection / Guesslang service URL
# Preferred key: language_detection_url (legacy fallback: guesslangUrl)
language_detection_url: http://ip:port/guess

Copilot uses AI. Check for mistakes.
- db:db.docker.local
ports:
- 5000:5000
- 5000
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docker-compose.yml previously exposed port 5000 on the host (5000:5000), making the app accessible from outside the container. Changing this to just 5000 exposes only the container port without host mapping, meaning the app will no longer be reachable from the host machine. This is a breaking change for anyone using docker-compose for local development. If the intention is to not expose the port externally (e.g., behind a reverse proxy), this should be documented, since the example config and setup docs may assume direct port access.

Suggested change
- 5000
- "5000:5000"

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +63
@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
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three of the four fusion-pixel font files referenced in fonts.css do not exist in the public/assets/fonts/ directory. Only fusion-pixel-10px-monospaced-zh_hant.woff2 is present; the bold (-bold.woff2), italic (-italic.woff2), and bold-italic (-bold-italic.woff2) variants are missing. This will cause font loading failures for bold and italic text when using the bit-fury theme.

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +28
volumes:
db-data:
driver: local
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The db-data volume is now declared as a named volume in docker-compose.yml, but the db service still mounts ./db-data:/data/db as a bind mount (a host directory). The named volume declaration and the bind mount are incompatible — the named volume db-data is never used by any service. If the intent was to switch from a bind mount to a managed named Docker volume, the db service's volumes entry should be changed from ./db-data:/data/db to db-data:/data/db. As-is, the volume declaration is unused.

Copilot uses AI. Check for mistakes.
*/

.cm-s-fury span.cm-meta { color: #da7b69; }
.cm-s-fury span.cm-number { color: #aaeac1e6; }
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The color value #aaeac1e6 is an 8-digit hex color code (with alpha channel) used in --color-nano context. While 8-digit hex colors are supported in modern browsers, they are not universally supported across all target environments and are inconsistent with the rest of the theme files in this codebase (which all use 6-digit hex colors). This applies to both codemirror-fury.css and codemirror-bit-fury.css line 8.

Suggested change
.cm-s-fury span.cm-number { color: #aaeac1e6; }
.cm-s-fury span.cm-number { color: #AAEAC1; }

Copilot uses AI. Check for mistakes.
*/

.cm-s-bit-fury span.cm-meta { color: #da7b69; }
.cm-s-bit-fury span.cm-number { color: #aaeac1e6; }
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The color value #aaeac1e6 is an 8-digit hex color code (with alpha channel), which is inconsistent with the rest of the theme files in this codebase (all using 6-digit hex colors). Same issue exists in codemirror-fury.css.

Suggested change
.cm-s-bit-fury span.cm-number { color: #aaeac1e6; }
.cm-s-bit-fury span.cm-number { color: #AAEAC1; }

Copilot uses AI. Check for mistakes.
Comment on lines +224 to +254
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;
}
}
}
}
);
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded fallback URL "https://guesslang.waterwater.moe/guess" is a third-party service URL embedded in the source code. If this service goes down or changes its API, language detection will silently fail for all users who haven't configured their own languageDetectionUrl. This default should either be removed (requiring explicit configuration) or clearly documented as a community-hosted fallback.

Suggested change
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;
}
}
}
}
);
if (config.languageDetectionUrl.length == 0)
{
// Language detection endpoint not configured; skip remote detection
logWarn("Language detection URL is not configured; using Plain Text as fallback.");
lang = "Plain Text";
}
else
{
string endpoint = config.languageDetectionUrl;
// 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;
}
}
}
}
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +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
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The theme-fury.css and theme-bit-fury.css files are identical in all CSS variable values — only the CSS selector differs (html.fury vs html.bit-fury) and the font stack ('fusion-pixel' vs 'UbuntuMono'). If these themes are intended to be distinct, the color values should differ; if they are the same theme with a different font, consider consolidating the shared values to avoid maintenance duplication.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants