Files
inbuxa-admin/sieve-playground/eml-language.js
T
jcoffey-dev 98413e684c
ci / build (pull_request) Skipped
ci / publish (pull_request) Skipped
ci / notices (pull_request) Successful in 49s
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 1m3s
Debug Sieve scripts in a playground that ships with the console
The Debug button on a Sieve script sent a copy to an outside site, whose
interpreter knows only upstream's extension names: a script that requires
vnd.inbuxa.expressions or vnd.inbuxa.while failed to compile there, and
the button took admins to another project's site.

The playground now ships inside the console and runs the server's own
interpreter. sieve-playground/ is built by inbuxa-server's
tools/sieve-playground (SOURCE.md names the commit); sieve-playground.ts
copies it, with Monaco 0.52.2 from node_modules, into
sieve-playground/<content hash>/ (the server caches every file but the
console's index.html for good) and gives the console its path. The
script stays on the console's origin, so the warning dialog before
opening it is gone.

Strings (en.json): sievepad.failed reworded, sievepad.debugHint added,
the three warning-dialog strings removed.

webui.zip grows from 1.3 MB to 3.4 MB: 0.9 MB of compressed WebAssembly,
the rest Monaco's editor core.
2026-10-09 01:14:30 -07:00

114 lines
4.2 KiB
JavaScript

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 Coffey Labs LLC
*
* SPDX-License-Identifier: AGPL-3.0-only
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
export const EML_ID = "eml";
class EmlState {
constructor(inHeaders, boundaries) {
this.inHeaders = inHeaders;
this.boundaries = boundaries;
}
clone() {
return new EmlState(this.inHeaders, this.boundaries.slice());
}
equals(other) {
return (
other instanceof EmlState &&
other.inHeaders === this.inHeaders &&
other.boundaries.length === this.boundaries.length &&
other.boundaries.every((b, i) => b === this.boundaries[i])
);
}
}
const HEADER = /^([A-Za-z0-9!#$%&'*+.^_`|~-]+)(\s*:)/;
const BOUNDARY_PARAM = /boundary\s*=\s*"?([^";\s]+)"?/i;
export function registerEml(monaco) {
monaco.languages.register({ id: EML_ID, extensions: [".eml"], aliases: ["Email"] });
monaco.languages.setTokensProvider(EML_ID, {
getInitialState: () => new EmlState(true, []),
tokenize(line, state) {
const next = state.clone();
const tokens = [];
if (next.boundaries.length) {
const trimmed = line.trimEnd();
const hit = next.boundaries.find((b) => trimmed === `--${b}` || trimmed === `--${b}--`);
if (hit) {
tokens.push({ startIndex: 0, scopes: "keyword.boundary" });
if (trimmed.endsWith("--") && trimmed === `--${hit}--`) {
next.boundaries = next.boundaries.filter((b) => b !== hit);
next.inHeaders = false;
} else {
next.inHeaders = true;
}
return { tokens, endState: next };
}
}
if (next.inHeaders) {
if (line.trim() === "") {
next.inHeaders = false;
return { tokens: [{ startIndex: 0, scopes: "" }], endState: next };
}
const boundary = BOUNDARY_PARAM.exec(line);
if (boundary && !next.boundaries.includes(boundary[1])) next.boundaries.push(boundary[1]);
const header = HEADER.exec(line);
if (header) {
tokens.push({ startIndex: 0, scopes: "attribute.name.header" });
tokens.push({ startIndex: header[1].length, scopes: "delimiter" });
tokens.push({ startIndex: header[0].length, scopes: "string.header" });
} else if (/^\s/.test(line)) {
tokens.push({ startIndex: 0, scopes: "string.header" });
} else {
tokens.push({ startIndex: 0, scopes: "invalid" });
}
return { tokens, endState: next };
}
tokens.push({ startIndex: 0, scopes: /^>/.test(line) ? "comment.quote" : "" });
return { tokens, endState: next };
},
});
}
export function defineThemes(monaco) {
const rules = (dark) => [
{ token: "keyword", foreground: dark ? "c792ea" : "7a3eb1", fontStyle: "bold" },
{ token: "type", foreground: dark ? "82aaff" : "1f5fbf" },
{ token: "attribute.name", foreground: dark ? "f78c6c" : "b35300" },
{ token: "variable", foreground: dark ? "ffcb6b" : "a0680a", fontStyle: "bold" },
{ token: "string", foreground: dark ? "c3e88d" : "2f7d32" },
{ token: "string.heredoc", foreground: dark ? "89ddff" : "0a7f94", fontStyle: "bold" },
{ token: "string.escape", foreground: dark ? "89ddff" : "0a7f94" },
{ token: "number", foreground: dark ? "f78c6c" : "b35300" },
{ token: "comment", foreground: dark ? "6a7390" : "7d8595", fontStyle: "italic" },
{ token: "attribute.name.header", foreground: dark ? "82aaff" : "1f5fbf", fontStyle: "bold" },
{ token: "string.header", foreground: dark ? "d7dae0" : "3a3f4b" },
{ token: "keyword.boundary", foreground: dark ? "c792ea" : "7a3eb1" },
{ token: "comment.quote", foreground: dark ? "6a7390" : "7d8595" },
{ token: "invalid", foreground: dark ? "ff7b82" : "c6313a" },
];
monaco.editor.defineTheme("playground-light", {
base: "vs",
inherit: true,
rules: rules(false),
colors: { "editor.background": "#ffffff", "editorGutter.background": "#ffffff" },
});
monaco.editor.defineTheme("playground-dark", {
base: "vs-dark",
inherit: true,
rules: rules(true),
colors: { "editor.background": "#1b1d24", "editorGutter.background": "#1b1d24" },
});
}