@marianmeres/stuic 3.190.0 → 3.192.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/AGENTS.md +2 -2
  2. package/API.md +29 -0
  3. package/dist/components/CodeBlock/CodeBlock.fixture.svelte +57 -0
  4. package/dist/components/CodeBlock/CodeBlock.fixture.svelte.d.ts +10 -0
  5. package/dist/components/CodeBlock/CodeBlock.svelte +441 -0
  6. package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +113 -0
  7. package/dist/components/CodeBlock/README.md +336 -0
  8. package/dist/components/CodeBlock/_internal/lines.d.ts +13 -0
  9. package/dist/components/CodeBlock/_internal/lines.js +37 -0
  10. package/dist/components/CodeBlock/_internal/normalize-code.d.ts +9 -0
  11. package/dist/components/CodeBlock/_internal/normalize-code.js +39 -0
  12. package/dist/components/CodeBlock/_internal/paint.d.ts +20 -0
  13. package/dist/components/CodeBlock/_internal/paint.js +81 -0
  14. package/dist/components/CodeBlock/highlight/http.d.ts +6 -0
  15. package/dist/components/CodeBlock/highlight/http.js +62 -0
  16. package/dist/components/CodeBlock/highlight/index.d.ts +14 -0
  17. package/dist/components/CodeBlock/highlight/index.js +29 -0
  18. package/dist/components/CodeBlock/highlight/json.d.ts +9 -0
  19. package/dist/components/CodeBlock/highlight/json.js +74 -0
  20. package/dist/components/CodeBlock/highlight/shell.d.ts +8 -0
  21. package/dist/components/CodeBlock/highlight/shell.js +230 -0
  22. package/dist/components/CodeBlock/highlight/types.d.ts +17 -0
  23. package/dist/components/CodeBlock/highlight/types.js +1 -0
  24. package/dist/components/CodeBlock/i18n-sk.d.ts +17 -0
  25. package/dist/components/CodeBlock/i18n-sk.js +22 -0
  26. package/dist/components/CodeBlock/i18n.d.ts +37 -0
  27. package/dist/components/CodeBlock/i18n.js +34 -0
  28. package/dist/components/CodeBlock/index.css +470 -0
  29. package/dist/components/CodeBlock/index.d.ts +4 -0
  30. package/dist/components/CodeBlock/index.js +4 -0
  31. package/dist/components/RangeSlider/README.md +22 -22
  32. package/dist/components/RangeSlider/RangeSlider.svelte +4 -4
  33. package/dist/components/RangeSlider/RangeSlider.svelte.d.ts +2 -2
  34. package/dist/components/RangeSlider/index.css +9 -6
  35. package/dist/components/Slider/README.md +23 -24
  36. package/dist/components/Slider/Slider.svelte +4 -4
  37. package/dist/components/Slider/Slider.svelte.d.ts +2 -2
  38. package/dist/components/Slider/index.css +6 -3
  39. package/dist/index.css +1 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +1 -0
  42. package/docs/domains/components.md +59 -1
  43. package/package.json +1 -1
@@ -0,0 +1,29 @@
1
+ import { highlightJson } from "./json.js";
2
+ import { highlightHttp } from "./http.js";
3
+ import { highlightShell } from "./shell.js";
4
+ export { highlightJson, highlightHttp, highlightShell };
5
+ const BY_LANG = {
6
+ json: highlightJson,
7
+ jsonc: highlightJson,
8
+ json5: highlightJson,
9
+ http: highlightHttp,
10
+ bash: highlightShell,
11
+ sh: highlightShell,
12
+ shell: highlightShell,
13
+ zsh: highlightShell,
14
+ console: highlightShell,
15
+ terminal: highlightShell,
16
+ "shell-session": highlightShell,
17
+ shellsession: highlightShell,
18
+ };
19
+ /** The `lang`s `highlightCode` knows (lower case). */
20
+ export const HIGHLIGHT_CODE_LANGS = Object.keys(BY_LANG);
21
+ /**
22
+ * `CodeBlock`'s built-in highlighter: JSON, HTTP and shell, picked by `lang` (case
23
+ * insensitive — see `HIGHLIGHT_CODE_LANGS`). Any other `lang` gets no tokens. Compose it
24
+ * to add a language: `(code, lang) => lang === "ts" ? myTs(code) : highlightCode(code, lang)`.
25
+ */
26
+ export const highlightCode = (code, lang) => {
27
+ const fn = lang ? BY_LANG[lang.trim().toLowerCase()] : undefined;
28
+ return fn ? fn(code) : [];
29
+ };
@@ -0,0 +1,9 @@
1
+ import type { CodeBlockToken } from "./types.js";
2
+ /**
3
+ * Tokenizes JSON into `out`, offsetting every token by `base` (so an HTTP body can be
4
+ * tokenized in place). Tolerant: `//` and `/* *\/` comments, unterminated strings and
5
+ * non-JSON placeholders (`...`) never throw — unknown characters are simply skipped.
6
+ */
7
+ export declare function tokenizeJsonInto(code: string, base: number, out: CodeBlockToken[]): void;
8
+ /** Highlights JSON (and JSON with comments): keys, strings, numbers, literals, punctuation. */
9
+ export declare function highlightJson(code: string): CodeBlockToken[];
@@ -0,0 +1,74 @@
1
+ const NUMBER = /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/y;
2
+ const LITERAL = /(?:true|false|null)(?![\w$])/y;
3
+ const WORD_CHAR = /[\w$.]/;
4
+ /**
5
+ * Tokenizes JSON into `out`, offsetting every token by `base` (so an HTTP body can be
6
+ * tokenized in place). Tolerant: `//` and `/* *\/` comments, unterminated strings and
7
+ * non-JSON placeholders (`...`) never throw — unknown characters are simply skipped.
8
+ */
9
+ export function tokenizeJsonInto(code, base, out) {
10
+ const n = code.length;
11
+ let i = 0;
12
+ while (i < n) {
13
+ const c = code[i];
14
+ if (c === '"') {
15
+ let j = i + 1;
16
+ while (j < n && code[j] !== '"' && code[j] !== "\n")
17
+ j += code[j] === "\\" ? 2 : 1;
18
+ const end = Math.min(code[j] === '"' ? j + 1 : j, n);
19
+ // a key is a string followed by a colon
20
+ let k = end;
21
+ while (code[k] === " " || code[k] === "\t")
22
+ k++;
23
+ out.push([base + i, base + end, code[k] === ":" ? "property" : "string"]);
24
+ i = end;
25
+ continue;
26
+ }
27
+ if (c === "/" && code[i + 1] === "/") {
28
+ const nl = code.indexOf("\n", i);
29
+ const end = nl < 0 ? n : nl;
30
+ out.push([base + i, base + end, "comment"]);
31
+ i = end;
32
+ continue;
33
+ }
34
+ if (c === "/" && code[i + 1] === "*") {
35
+ const close = code.indexOf("*/", i + 2);
36
+ const end = close < 0 ? n : close + 2;
37
+ out.push([base + i, base + end, "comment"]);
38
+ i = end;
39
+ continue;
40
+ }
41
+ if ("{}[],:".includes(c)) {
42
+ out.push([base + i, base + i + 1, "punctuation"]);
43
+ i++;
44
+ continue;
45
+ }
46
+ if (!WORD_CHAR.test(code[i - 1] ?? "")) {
47
+ if (c === "-" || (c >= "0" && c <= "9")) {
48
+ NUMBER.lastIndex = i;
49
+ const m = NUMBER.exec(code);
50
+ if (m && m[0] !== "-") {
51
+ out.push([base + i, base + i + m[0].length, "number"]);
52
+ i += m[0].length;
53
+ continue;
54
+ }
55
+ }
56
+ if (c === "t" || c === "f" || c === "n") {
57
+ LITERAL.lastIndex = i;
58
+ const m = LITERAL.exec(code);
59
+ if (m) {
60
+ out.push([base + i, base + i + m[0].length, "literal"]);
61
+ i += m[0].length;
62
+ continue;
63
+ }
64
+ }
65
+ }
66
+ i++;
67
+ }
68
+ }
69
+ /** Highlights JSON (and JSON with comments): keys, strings, numbers, literals, punctuation. */
70
+ export function highlightJson(code) {
71
+ const out = [];
72
+ tokenizeJsonInto(code, 0, out);
73
+ return out;
74
+ }
@@ -0,0 +1,8 @@
1
+ import type { CodeBlockToken } from "./types.js";
2
+ /**
3
+ * Highlights a shell command or session (bash, sh, zsh): `$ ` prompts, comments, quoted
4
+ * strings with the variables inside them, `$VARS`/`${…}`/`$(…)`, the command name, flags,
5
+ * assignments, operators and `\` line continuations. A reading aid, not a parser —
6
+ * heredocs and backticks are left plain.
7
+ */
8
+ export declare function highlightShell(code: string): CodeBlockToken[];
@@ -0,0 +1,230 @@
1
+ const KEYWORDS = new Set([
2
+ "if",
3
+ "then",
4
+ "else",
5
+ "elif",
6
+ "fi",
7
+ "for",
8
+ "while",
9
+ "until",
10
+ "do",
11
+ "done",
12
+ "case",
13
+ "esac",
14
+ "in",
15
+ "function",
16
+ "select",
17
+ "return",
18
+ "export",
19
+ "local",
20
+ "readonly",
21
+ "declare",
22
+ "unset",
23
+ ]);
24
+ /** Keywords followed by a name or a word list, not by a command */
25
+ const NOT_FOLLOWED_BY_COMMAND = new Set(["for", "select", "case", "in", "function"]);
26
+ /** Keywords followed by assignments */
27
+ const DECLARING = new Set(["export", "local", "readonly", "declare"]);
28
+ const ASSIGNMENT = /^[A-Za-z_]\w*=/;
29
+ /** Characters that end an unquoted word */
30
+ const WORD_END = /[\s|&;<>()'"$`]/;
31
+ /**
32
+ * Highlights a shell command or session (bash, sh, zsh): `$ ` prompts, comments, quoted
33
+ * strings with the variables inside them, `$VARS`/`${…}`/`$(…)`, the command name, flags,
34
+ * assignments, operators and `\` line continuations. A reading aid, not a parser —
35
+ * heredocs and backticks are left plain.
36
+ */
37
+ export function highlightShell(code) {
38
+ const out = [];
39
+ const n = code.length;
40
+ const push = (start, end, type) => {
41
+ if (end > start)
42
+ out.push([start, end, type]);
43
+ };
44
+ /** End of the `$…` expansion starting at `i` (a `$` followed by an expandable char) */
45
+ const expansionEnd = (i) => {
46
+ const next = code[i + 1];
47
+ if (next === "{" || next === "(") {
48
+ const close = next === "{" ? "}" : ")";
49
+ let depth = 0;
50
+ for (let j = i + 1; j < n; j++) {
51
+ if (code[j] === next)
52
+ depth++;
53
+ else if (code[j] === close && --depth === 0)
54
+ return j + 1;
55
+ }
56
+ return n;
57
+ }
58
+ if (/[A-Za-z_]/.test(next)) {
59
+ let j = i + 2;
60
+ while (j < n && /\w/.test(code[j]))
61
+ j++;
62
+ return j;
63
+ }
64
+ return i + 2; // $1 $? $@ $# $* $! $$ $-
65
+ };
66
+ const isExpansion = (i) => /[A-Za-z_{(0-9@*#?!$-]/.test(code[i + 1] ?? "");
67
+ let i = 0;
68
+ let command = true; // the next word is a command name
69
+ let declaring = false; // after `export` & co. — words are assignments
70
+ let awaitingIn = 0; // `for NAME in`, `case WORD in`: words until the `in`
71
+ let lineStart = true;
72
+ while (i < n) {
73
+ const c = code[i];
74
+ if (c === "\n") {
75
+ i++;
76
+ command = true;
77
+ declaring = false;
78
+ awaitingIn = 0;
79
+ lineStart = true;
80
+ continue;
81
+ }
82
+ if (c === " " || c === "\t") {
83
+ i++;
84
+ continue;
85
+ }
86
+ // a `$ ` prompt (a `$` followed by a space can't be an expansion)
87
+ if (lineStart) {
88
+ lineStart = false;
89
+ if (c === "$" && (i + 1 === n || code[i + 1] === " " || code[i + 1] === "\n")) {
90
+ push(i, i + 1, "meta");
91
+ i++;
92
+ continue;
93
+ }
94
+ }
95
+ // `\` + newline: the command continues on the next line
96
+ if (c === "\\" && code[i + 1] === "\n") {
97
+ push(i, i + 1, "punctuation");
98
+ i += 2;
99
+ continue;
100
+ }
101
+ // only at the start of a word — mid-word `#` (a URL fragment) is part of the word
102
+ if (c === "#") {
103
+ const nl = code.indexOf("\n", i);
104
+ const end = nl < 0 ? n : nl;
105
+ push(i, end, "comment");
106
+ i = end;
107
+ continue;
108
+ }
109
+ if (c === "'") {
110
+ const close = code.indexOf("'", i + 1);
111
+ const end = close < 0 ? n : close + 1;
112
+ push(i, end, "string");
113
+ i = end;
114
+ command = false;
115
+ continue;
116
+ }
117
+ if (c === '"') {
118
+ let from = i;
119
+ let j = i + 1;
120
+ while (j < n && code[j] !== '"') {
121
+ if (code[j] === "\\") {
122
+ j += 2;
123
+ }
124
+ else if (code[j] === "$" && isExpansion(j)) {
125
+ push(from, j, "string");
126
+ const e = expansionEnd(j);
127
+ push(j, e, "variable");
128
+ from = j = e;
129
+ }
130
+ else {
131
+ j++;
132
+ }
133
+ }
134
+ const end = Math.min(j + 1, n);
135
+ push(from, end, "string");
136
+ i = end;
137
+ command = false;
138
+ continue;
139
+ }
140
+ if (c === "$" && isExpansion(i)) {
141
+ if (code[i + 1] === "(") {
142
+ // command substitution: a nested command line
143
+ push(i, i + 2, "punctuation");
144
+ i += 2;
145
+ command = true;
146
+ continue;
147
+ }
148
+ const e = expansionEnd(i);
149
+ push(i, e, "variable");
150
+ i = e;
151
+ command = false;
152
+ continue;
153
+ }
154
+ if (c === "|" || c === "&" || c === ";") {
155
+ const two = code.slice(i, i + 2);
156
+ const len = two === "||" || two === "&&" || two === ";;" ? 2 : 1;
157
+ push(i, i + len, "operator");
158
+ i += len;
159
+ command = true;
160
+ declaring = false;
161
+ continue;
162
+ }
163
+ if (c === ">" || c === "<") {
164
+ const len = code[i + 1] === c || code[i + 1] === "&" ? 2 : 1;
165
+ push(i, i + len, "operator");
166
+ i += len;
167
+ continue;
168
+ }
169
+ if (c === "(" || c === ")" || c === "{" || c === "}") {
170
+ push(i, i + 1, "punctuation");
171
+ i++;
172
+ if (c === "(" || c === "{")
173
+ command = true;
174
+ continue;
175
+ }
176
+ // an unquoted word
177
+ let j = i;
178
+ while (j < n && !WORD_END.test(code[j])) {
179
+ if (code[j] === "\\") {
180
+ if (code[j + 1] === "\n")
181
+ break; // a continuation, tokenized on the next round
182
+ j += 2;
183
+ }
184
+ else {
185
+ j++;
186
+ }
187
+ }
188
+ if (j === i) {
189
+ // a lone character the rules above don't claim (a stray backslash, a backtick)
190
+ i++;
191
+ continue;
192
+ }
193
+ const word = code.slice(i, Math.min(j, n));
194
+ const inKeyword = awaitingIn === 1 && word === "in";
195
+ if (awaitingIn)
196
+ awaitingIn--;
197
+ const assignment = (command || declaring) && ASSIGNMENT.exec(word);
198
+ if (inKeyword) {
199
+ push(i, j, "keyword");
200
+ }
201
+ else if (assignment) {
202
+ const eq = i + assignment[0].length - 1;
203
+ push(i, eq, "variable");
204
+ push(eq, eq + 1, "operator");
205
+ // `FOO=1 cmd`: the command still follows
206
+ }
207
+ else if (command) {
208
+ if (KEYWORDS.has(word)) {
209
+ push(i, j, "keyword");
210
+ command = !NOT_FOLLOWED_BY_COMMAND.has(word) && !DECLARING.has(word);
211
+ declaring = DECLARING.has(word);
212
+ if (word === "for" || word === "select" || word === "case")
213
+ awaitingIn = 2;
214
+ }
215
+ else {
216
+ push(i, j, "function");
217
+ command = false;
218
+ }
219
+ }
220
+ else if (word.length > 1 && word[0] === "-" && !/^-\d/.test(word)) {
221
+ const eq = word.indexOf("=");
222
+ push(i, eq < 0 ? j : i + eq, "parameter");
223
+ }
224
+ else if (/^\d+$/.test(word)) {
225
+ push(i, j, "number");
226
+ }
227
+ i = j;
228
+ }
229
+ return out;
230
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The token types the built-in highlighters emit, and the ones `CodeBlock`'s stylesheet
3
+ * colors (all but `punctuation`, which is emitted but left in the text color).
4
+ */
5
+ export type CodeBlockTokenType = "comment" | "string" | "number" | "literal" | "keyword" | "property" | "variable" | "function" | "parameter" | "operator" | "punctuation" | "meta";
6
+ /**
7
+ * One highlighted span: `[start, end)` offsets into the code as displayed (after
8
+ * normalization), and its type. A custom type is allowed — it is painted by
9
+ * `::highlight(stuic-code-block-<type>)`, which the consumer styles.
10
+ */
11
+ export type CodeBlockToken = [
12
+ start: number,
13
+ end: number,
14
+ type: CodeBlockTokenType | (string & {})
15
+ ];
16
+ /** Turns a sample into tokens. `lang` is whatever the block was given. */
17
+ export type CodeBlockHighlighter = (code: string, lang?: string) => CodeBlockToken[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { CodeBlockMessages } from "./i18n.js";
2
+ /**
3
+ * Slovak message catalog for `CodeBlock` (its copy button included). Opt-in — English
4
+ * stays the built-in default, and this module is only pulled into a bundle when it is
5
+ * actually imported.
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <script>
10
+ * import { CodeBlock, createCodeBlockT, CODE_BLOCK_MESSAGES_SK } from "@marianmeres/stuic";
11
+ * const t = createCodeBlockT(CODE_BLOCK_MESSAGES_SK);
12
+ * </script>
13
+ *
14
+ * <CodeBlock lang="bash" code={sample} {t} />
15
+ * ```
16
+ */
17
+ export declare const CODE_BLOCK_MESSAGES_SK: CodeBlockMessages;
@@ -0,0 +1,22 @@
1
+ import { COPY_BUTTON_MESSAGES_SK } from "../CopyButton/i18n-sk.js";
2
+ /**
3
+ * Slovak message catalog for `CodeBlock` (its copy button included). Opt-in — English
4
+ * stays the built-in default, and this module is only pulled into a bundle when it is
5
+ * actually imported.
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <script>
10
+ * import { CodeBlock, createCodeBlockT, CODE_BLOCK_MESSAGES_SK } from "@marianmeres/stuic";
11
+ * const t = createCodeBlockT(CODE_BLOCK_MESSAGES_SK);
12
+ * </script>
13
+ *
14
+ * <CodeBlock lang="bash" code={sample} {t} />
15
+ * ```
16
+ */
17
+ export const CODE_BLOCK_MESSAGES_SK = {
18
+ ...COPY_BUTTON_MESSAGES_SK,
19
+ // "všetky riadky" reads right for any count; the number stays out of the plural
20
+ show_all_lines: "Zobraziť všetky riadky ({count})",
21
+ show_less: "Zobraziť menej",
22
+ };
@@ -0,0 +1,37 @@
1
+ import type { TranslateFn } from "../../types.js";
2
+ /**
3
+ * The built-in (English) message catalog of `CodeBlock`. It includes the `CopyButton`
4
+ * keys, so the one `t` a block is given also localizes its copy button. Also the fallback
5
+ * of every other bundled locale, so a locale missing a key still renders text.
6
+ */
7
+ export declare const CODE_BLOCK_MESSAGES_EN: {
8
+ /** The collapse toggle while collapsed; `{count}` is the sample's line count */
9
+ show_all_lines: string;
10
+ /** The collapse toggle while expanded */
11
+ show_less: string;
12
+ copy: string;
13
+ copied: string;
14
+ copy_failed: string;
15
+ };
16
+ /** Every message key `CodeBlock` may look up. */
17
+ export type CodeBlockMessageKey = keyof typeof CODE_BLOCK_MESSAGES_EN;
18
+ /** A (possibly partial) catalog for one locale. */
19
+ export type CodeBlockMessages = Record<CodeBlockMessageKey, string>;
20
+ /**
21
+ * Builds the `t` prop of `CodeBlock` from a message catalog. Unknown or untranslated keys
22
+ * fall back to `fallbackMessages` (English by default); `{name}` placeholders are
23
+ * replaced from the values.
24
+ *
25
+ * @example
26
+ * ```svelte
27
+ * <script>
28
+ * import { CodeBlock, createCodeBlockT, CODE_BLOCK_MESSAGES_SK } from "@marianmeres/stuic";
29
+ * const t = createCodeBlockT(CODE_BLOCK_MESSAGES_SK);
30
+ * </script>
31
+ *
32
+ * <CodeBlock lang="bash" code={sample} {t} />
33
+ * ```
34
+ */
35
+ export declare function createCodeBlockT(messages: Partial<CodeBlockMessages> | Record<string, string>, fallbackMessages?: Partial<CodeBlockMessages> | Record<string, string>): TranslateFn;
36
+ /** The component's built-in English `t`. */
37
+ export declare const t_default: TranslateFn;
@@ -0,0 +1,34 @@
1
+ import { COPY_BUTTON_MESSAGES_EN, createCopyButtonT } from "../CopyButton/i18n.js";
2
+ /**
3
+ * The built-in (English) message catalog of `CodeBlock`. It includes the `CopyButton`
4
+ * keys, so the one `t` a block is given also localizes its copy button. Also the fallback
5
+ * of every other bundled locale, so a locale missing a key still renders text.
6
+ */
7
+ export const CODE_BLOCK_MESSAGES_EN = {
8
+ ...COPY_BUTTON_MESSAGES_EN,
9
+ /** The collapse toggle while collapsed; `{count}` is the sample's line count */
10
+ show_all_lines: "Show all {count} lines",
11
+ /** The collapse toggle while expanded */
12
+ show_less: "Show less",
13
+ };
14
+ /**
15
+ * Builds the `t` prop of `CodeBlock` from a message catalog. Unknown or untranslated keys
16
+ * fall back to `fallbackMessages` (English by default); `{name}` placeholders are
17
+ * replaced from the values.
18
+ *
19
+ * @example
20
+ * ```svelte
21
+ * <script>
22
+ * import { CodeBlock, createCodeBlockT, CODE_BLOCK_MESSAGES_SK } from "@marianmeres/stuic";
23
+ * const t = createCodeBlockT(CODE_BLOCK_MESSAGES_SK);
24
+ * </script>
25
+ *
26
+ * <CodeBlock lang="bash" code={sample} {t} />
27
+ * ```
28
+ */
29
+ export function createCodeBlockT(messages, fallbackMessages = CODE_BLOCK_MESSAGES_EN) {
30
+ // same lookup + `{placeholder}` semantics as CopyButton's
31
+ return createCopyButtonT(messages, fallbackMessages);
32
+ }
33
+ /** The component's built-in English `t`. */
34
+ export const t_default = createCodeBlockT(CODE_BLOCK_MESSAGES_EN);