@delightstack/components 0.1.0 → 0.2.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.
package/SKILL.md CHANGED
@@ -92,7 +92,7 @@ have the exact types. Do not guess prop names.
92
92
  - `AvatarGroup` — Stacked overlapping avatars with overflow indicator (docs: /components/display/avatar-group.md)
93
93
  - `Calendar` — Date display/selection with single, range, and multiple modes (docs: /components/display/calendar.md)
94
94
  - `Chart` — Data visualization: line, area, bar, horizontal bar, pie, donut (docs: /components/display/chart.md)
95
- - `Code` — Syntax-highlighted code block with line numbers, copy, diff display (docs: /components/display/code.md)
95
+ - `Code` — Syntax-highlighted code block with line numbers, copy, diff display; its line tokenizer is exported as `tokenizeLine`/`CodeToken` from `@delightstack/components/display` for external highlighters (docs: /components/display/code.md)
96
96
  - `Comparison` — Before/after image slider using CSS clip-path reveal (docs: /components/display/comparison.md)
97
97
  - `Counter` — Animated count-up/down number with scroll-into-view trigger (docs: /components/display/counter.md)
98
98
  - `Expand` — Animated expand/collapse container (docs: /components/display/expand.md)
@@ -1,10 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { scrollbar } from '../actions/scrollbar';
3
-
4
- interface Token {
5
- type: string;
6
- content: string;
7
- }
3
+ import { tokenizeLine } from './code-tokens.js';
8
4
 
9
5
  const propId = $props.id();
10
6
  let {
@@ -85,246 +81,6 @@
85
81
  };
86
82
  });
87
83
 
88
- /* ------------------------------------------------------------------ */
89
- /* Built-in tokenizers */
90
- /* ------------------------------------------------------------------ */
91
-
92
- function tokenizePlain(line: string): Token[] {
93
- return line ? [{ type: 'plain', content: line }] : [{ type: 'plain', content: '' }];
94
- }
95
-
96
- function tokenizeByPatterns(line: string, patterns: [RegExp, string][]): Token[] {
97
- const tokens: Token[] = [];
98
- let remaining = line;
99
-
100
- while (remaining.length > 0) {
101
- let earliest_match: {
102
- index: number;
103
- length: number;
104
- type: string;
105
- text: string;
106
- } | null = null;
107
-
108
- for (const [regex, type] of patterns) {
109
- regex.lastIndex = 0;
110
- const m = regex.exec(remaining);
111
- if (m && (earliest_match === null || m.index < earliest_match.index)) {
112
- earliest_match = { index: m.index, length: m[0].length, type, text: m[0] };
113
- }
114
- }
115
-
116
- if (earliest_match === null) {
117
- tokens.push({ type: 'plain', content: remaining });
118
- break;
119
- }
120
-
121
- if (earliest_match.index > 0) {
122
- tokens.push({ type: 'plain', content: remaining.slice(0, earliest_match.index) });
123
- }
124
-
125
- tokens.push({ type: earliest_match.type, content: earliest_match.text });
126
- remaining = remaining.slice(earliest_match.index + earliest_match.length);
127
- }
128
-
129
- return tokens.length ? tokens : [{ type: 'plain', content: '' }];
130
- }
131
-
132
- const js_keywords =
133
- /\b(abstract|arguments|async|await|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|final|finally|float|for|from|function|goto|if|implements|import|in|instanceof|int|interface|let|long|native|new|null|of|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|try|typeof|undefined|var|void|volatile|while|with|yield|true|false)\b/g;
134
-
135
- const ts_keywords =
136
- /\b(abstract|arguments|as|async|await|boolean|break|byte|case|catch|char|class|const|continue|debugger|declare|default|delete|do|double|else|enum|export|extends|final|finally|float|for|from|function|goto|if|implements|import|in|infer|instanceof|int|interface|is|keyof|let|long|module|namespace|native|never|new|null|of|package|private|protected|public|readonly|return|short|static|string|number|super|switch|synchronized|this|throw|throws|transient|try|type|typeof|undefined|unknown|var|void|volatile|while|with|yield|true|false)\b/g;
137
-
138
- function tokenizeJS(line: string): Token[] {
139
- return tokenizeByPatterns(line, [
140
- [/\/\/.*$/g, 'comment'],
141
- [/\/\*.*?\*\//g, 'comment'],
142
- [/`(?:[^`\\]|\\.)*`/g, 'string'],
143
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
144
- [/'(?:[^'\\]|\\.)*'/g, 'string'],
145
- [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/gi, 'number'],
146
- [/\b[A-Za-z_$][\w$]*(?=\s*\()/g, 'function'],
147
- [js_keywords, 'keyword'],
148
- [/[+\-*/%=!<>&|^~?:]+/g, 'operator'],
149
- ]);
150
- }
151
-
152
- function tokenizeTS(line: string): Token[] {
153
- return tokenizeByPatterns(line, [
154
- [/\/\/.*$/g, 'comment'],
155
- [/\/\*.*?\*\//g, 'comment'],
156
- [/`(?:[^`\\]|\\.)*`/g, 'string'],
157
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
158
- [/'(?:[^'\\]|\\.)*'/g, 'string'],
159
- [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/gi, 'number'],
160
- [/\b[A-Za-z_$][\w$]*(?=\s*\()/g, 'function'],
161
- [ts_keywords, 'keyword'],
162
- [/[+\-*/%=!<>&|^~?:]+/g, 'operator'],
163
- ]);
164
- }
165
-
166
- function tokenizeHTML(line: string): Token[] {
167
- return tokenizeByPatterns(line, [
168
- [/<!--.*?-->/g, 'comment'],
169
- [/"[^"]*"/g, 'string'],
170
- [/'[^']*'/g, 'string'],
171
- [/<\/?[\w-]+/g, 'tag'],
172
- [/\/?>/g, 'tag'],
173
- [/\b[\w-]+(?==)/g, 'attribute'],
174
- ]);
175
- }
176
-
177
- function tokenizeCSS(line: string): Token[] {
178
- return tokenizeByPatterns(line, [
179
- [/\/\*.*?\*\//g, 'comment'],
180
- [/\/\/.*$/g, 'comment'],
181
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
182
- [/'(?:[^'\\]|\\.)*'/g, 'string'],
183
- [/\b\d+\.?\d*(?:px|em|rem|%|vh|vw|vmin|vmax|deg|s|ms|fr|ch|ex)?\b/g, 'number'],
184
- [/@[\w-]+/g, 'keyword'],
185
- [/[.#][\w-]+/g, 'variable'],
186
- [/[\w-]+(?=\s*:)/g, 'property'],
187
- [/:\s*[^;{}]+/g, 'value'],
188
- ]);
189
- }
190
-
191
- function tokenizeJSON(line: string): Token[] {
192
- return tokenizeByPatterns(line, [
193
- [/"(?:[^"\\]|\\.)*"(?=\s*:)/g, 'property'],
194
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
195
- [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/gi, 'number'],
196
- [/\b(true|false)\b/g, 'keyword'],
197
- [/\bnull\b/g, 'keyword'],
198
- ]);
199
- }
200
-
201
- const python_keywords =
202
- /\b(False|None|True|and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b/g;
203
-
204
- function tokenizePython(line: string): Token[] {
205
- return tokenizeByPatterns(line, [
206
- [/#.*$/g, 'comment'],
207
- [/""".*?"""/g, 'string'],
208
- [/'''.*?'''/g, 'string'],
209
- [/f"(?:[^"\\]|\\.)*"/g, 'string'],
210
- [/f'(?:[^'\\]|\\.)*'/g, 'string'],
211
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
212
- [/'(?:[^'\\]|\\.)*'/g, 'string'],
213
- [/@[\w.]+/g, 'decorator'],
214
- [/\b\d+\.?\d*(?:e[+-]?\d+)?j?\b/gi, 'number'],
215
- [/\b[A-Za-z_]\w*(?=\s*\()/g, 'function'],
216
- [python_keywords, 'keyword'],
217
- [/[+\-*/%=!<>&|^~@:]+/g, 'operator'],
218
- ]);
219
- }
220
-
221
- function tokenizeBash(line: string): Token[] {
222
- return tokenizeByPatterns(line, [
223
- [/#.*$/g, 'comment'],
224
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
225
- [/'[^']*'/g, 'string'],
226
- [/\$\{[^}]*\}/g, 'variable'],
227
- [/\$[\w]+/g, 'variable'],
228
- [/\b\d+\.?\d*\b/g, 'number'],
229
- [
230
- /\b(alias|bg|bind|break|builtin|caller|case|cd|command|compgen|complete|continue|declare|dirs|disown|do|done|echo|elif|else|enable|esac|eval|exec|exit|export|false|fc|fg|fi|for|function|getopts|hash|help|history|if|in|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|select|set|shift|shopt|source|suspend|test|then|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|until|wait|while)\b/g,
231
- 'keyword',
232
- ],
233
- [/\b[\w./-]+(?=\s|$)/g, 'function'],
234
- [/[|&;<>]+/g, 'operator'],
235
- ]);
236
- }
237
-
238
- const sql_keywords =
239
- /\b(ADD|ALL|ALTER|AND|ANY|AS|ASC|BACKUP|BETWEEN|BY|CASE|CHECK|COLUMN|CONSTRAINT|CREATE|DATABASE|DEFAULT|DELETE|DESC|DISTINCT|DROP|EACH|ELSE|END|EXEC|EXISTS|FOREIGN|FROM|FULL|GROUP|HAVING|IF|IN|INDEX|INNER|INSERT|INTO|IS|JOIN|KEY|LEFT|LIKE|LIMIT|NOT|NULL|OFFSET|ON|OR|ORDER|OUTER|PRIMARY|PROCEDURE|REFERENCES|REPLACE|RIGHT|ROWNUM|SELECT|SET|TABLE|THEN|TOP|TRUNCATE|UNION|UNIQUE|UPDATE|VALUES|VIEW|WHEN|WHERE|WITH)\b/gi;
240
-
241
- function tokenizeSQL(line: string): Token[] {
242
- return tokenizeByPatterns(line, [
243
- [/--.*$/g, 'comment'],
244
- [/\/\*.*?\*\//g, 'comment'],
245
- [/'(?:[^'\\]|\\.)*'/g, 'string'],
246
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
247
- [/\b\d+\.?\d*\b/g, 'number'],
248
- [/\b[A-Za-z_]\w*(?=\s*\()/g, 'function'],
249
- [sql_keywords, 'keyword'],
250
- [/[+\-*/%=!<>&|^~]+/g, 'operator'],
251
- ]);
252
- }
253
-
254
- function tokenizeSvelte(line: string): Token[] {
255
- return tokenizeByPatterns(line, [
256
- [/<!--.*?-->/g, 'comment'],
257
- [/\/\/.*$/g, 'comment'],
258
- [/\{#[\w]+/g, 'keyword'],
259
- [/\{\/[\w]+\}/g, 'keyword'],
260
- [/\{:[\w]+/g, 'keyword'],
261
- [/`(?:[^`\\]|\\.)*`/g, 'string'],
262
- [/"(?:[^"\\]|\\.)*"/g, 'string'],
263
- [/'(?:[^'\\]|\\.)*'/g, 'string'],
264
- [/\b\d+\.?\d*\b/g, 'number'],
265
- [/<\/?[\w-]+/g, 'tag'],
266
- [/\/?>/g, 'tag'],
267
- [/\b[\w-]+(?==)/g, 'attribute'],
268
- [/\b[A-Za-z_$][\w$]*(?=\s*\()/g, 'function'],
269
- [js_keywords, 'keyword'],
270
- [/[+\-*/%=!<>&|^~?:]+/g, 'operator'],
271
- ]);
272
- }
273
-
274
- function tokenizeMarkdown(line: string): Token[] {
275
- return tokenizeByPatterns(line, [
276
- [/^#{1,6}\s+.*/g, 'heading'],
277
- [/`[^`]+`/g, 'code'],
278
- [/\*\*[^*]+\*\*/g, 'bold'],
279
- [/\*[^*]+\*/g, 'italic'],
280
- [/__[^_]+__/g, 'bold'],
281
- [/_[^_]+_/g, 'italic'],
282
- [/\[([^\]]+)\]\([^)]+\)/g, 'link'],
283
- [/!\[([^\]]*)\]\([^)]+\)/g, 'link'],
284
- ]);
285
- }
286
-
287
- function tokenizeLine(line: string, lang: string): Token[] {
288
- switch (lang) {
289
- case 'javascript':
290
- case 'js':
291
- case 'jsx':
292
- return tokenizeJS(line);
293
- case 'typescript':
294
- case 'ts':
295
- case 'tsx':
296
- return tokenizeTS(line);
297
- case 'html':
298
- case 'xml':
299
- return tokenizeHTML(line);
300
- case 'css':
301
- case 'scss':
302
- case 'sass':
303
- case 'less':
304
- return tokenizeCSS(line);
305
- case 'json':
306
- case 'jsonc':
307
- return tokenizeJSON(line);
308
- case 'python':
309
- case 'py':
310
- return tokenizePython(line);
311
- case 'bash':
312
- case 'sh':
313
- case 'shell':
314
- case 'zsh':
315
- return tokenizeBash(line);
316
- case 'sql':
317
- return tokenizeSQL(line);
318
- case 'svelte':
319
- return tokenizeSvelte(line);
320
- case 'markdown':
321
- case 'md':
322
- return tokenizeMarkdown(line);
323
- default:
324
- return tokenizePlain(line);
325
- }
326
- }
327
-
328
84
  /* ------------------------------------------------------------------ */
329
85
  /* Derived state */
330
86
  /* ------------------------------------------------------------------ */
@@ -1 +1 @@
1
- {"version":3,"file":"Code.svelte.d.ts","sourceRoot":"","sources":["../../src/display/Code.svelte.ts"],"names":[],"mappings":"AAUC,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAmYH,QAAA,MAAM,IAAI,sDAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Code.svelte.d.ts","sourceRoot":"","sources":["../../src/display/Code.svelte.ts"],"names":[],"mappings":"AAMC,KAAK,gBAAgB,GAAI;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAmJH,QAAA,MAAM,IAAI,sDAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared line tokenizer for syntax highlighting: used by the `Code`
3
+ * display component and by `@delightstack/editor`'s editable code-block
4
+ * decorations, so both render identical tokens (`token-<type>` classes).
5
+ */
6
+ export interface Token {
7
+ type: string;
8
+ content: string;
9
+ }
10
+ export declare function tokenizeLine(line: string, lang: string): Token[];
11
+ //# sourceMappingURL=code-tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-tokens.d.ts","sourceRoot":"","sources":["../../src/display/code-tokens.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAyMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,CAuChE"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Shared line tokenizer for syntax highlighting: used by the `Code`
3
+ * display component and by `@delightstack/editor`'s editable code-block
4
+ * decorations, so both render identical tokens (`token-<type>` classes).
5
+ */
6
+ /* ------------------------------------------------------------------ */
7
+ /* Built-in tokenizers */
8
+ /* ------------------------------------------------------------------ */
9
+ function tokenizePlain(line) {
10
+ return line ? [{ type: 'plain', content: line }] : [{ type: 'plain', content: '' }];
11
+ }
12
+ function tokenizeByPatterns(line, patterns) {
13
+ const tokens = [];
14
+ let remaining = line;
15
+ while (remaining.length > 0) {
16
+ let earliest_match = null;
17
+ for (const [regex, type] of patterns) {
18
+ regex.lastIndex = 0;
19
+ const m = regex.exec(remaining);
20
+ if (m && (earliest_match === null || m.index < earliest_match.index)) {
21
+ earliest_match = { index: m.index, length: m[0].length, type, text: m[0] };
22
+ }
23
+ }
24
+ if (earliest_match === null) {
25
+ tokens.push({ type: 'plain', content: remaining });
26
+ break;
27
+ }
28
+ if (earliest_match.index > 0) {
29
+ tokens.push({ type: 'plain', content: remaining.slice(0, earliest_match.index) });
30
+ }
31
+ tokens.push({ type: earliest_match.type, content: earliest_match.text });
32
+ remaining = remaining.slice(earliest_match.index + earliest_match.length);
33
+ }
34
+ return tokens.length ? tokens : [{ type: 'plain', content: '' }];
35
+ }
36
+ const js_keywords = /\b(abstract|arguments|async|await|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|final|finally|float|for|from|function|goto|if|implements|import|in|instanceof|int|interface|let|long|native|new|null|of|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|try|typeof|undefined|var|void|volatile|while|with|yield|true|false)\b/g;
37
+ const ts_keywords = /\b(abstract|arguments|as|async|await|boolean|break|byte|case|catch|char|class|const|continue|debugger|declare|default|delete|do|double|else|enum|export|extends|final|finally|float|for|from|function|goto|if|implements|import|in|infer|instanceof|int|interface|is|keyof|let|long|module|namespace|native|never|new|null|of|package|private|protected|public|readonly|return|short|static|string|number|super|switch|synchronized|this|throw|throws|transient|try|type|typeof|undefined|unknown|var|void|volatile|while|with|yield|true|false)\b/g;
38
+ function tokenizeJS(line) {
39
+ return tokenizeByPatterns(line, [
40
+ [/\/\/.*$/g, 'comment'],
41
+ [/\/\*.*?\*\//g, 'comment'],
42
+ [/`(?:[^`\\]|\\.)*`/g, 'string'],
43
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
44
+ [/'(?:[^'\\]|\\.)*'/g, 'string'],
45
+ [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/gi, 'number'],
46
+ [/\b[A-Za-z_$][\w$]*(?=\s*\()/g, 'function'],
47
+ [js_keywords, 'keyword'],
48
+ [/[+\-*/%=!<>&|^~?:]+/g, 'operator'],
49
+ ]);
50
+ }
51
+ function tokenizeTS(line) {
52
+ return tokenizeByPatterns(line, [
53
+ [/\/\/.*$/g, 'comment'],
54
+ [/\/\*.*?\*\//g, 'comment'],
55
+ [/`(?:[^`\\]|\\.)*`/g, 'string'],
56
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
57
+ [/'(?:[^'\\]|\\.)*'/g, 'string'],
58
+ [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/gi, 'number'],
59
+ [/\b[A-Za-z_$][\w$]*(?=\s*\()/g, 'function'],
60
+ [ts_keywords, 'keyword'],
61
+ [/[+\-*/%=!<>&|^~?:]+/g, 'operator'],
62
+ ]);
63
+ }
64
+ function tokenizeHTML(line) {
65
+ return tokenizeByPatterns(line, [
66
+ [/<!--.*?-->/g, 'comment'],
67
+ [/"[^"]*"/g, 'string'],
68
+ [/'[^']*'/g, 'string'],
69
+ [/<\/?[\w-]+/g, 'tag'],
70
+ [/\/?>/g, 'tag'],
71
+ [/\b[\w-]+(?==)/g, 'attribute'],
72
+ ]);
73
+ }
74
+ function tokenizeCSS(line) {
75
+ return tokenizeByPatterns(line, [
76
+ [/\/\*.*?\*\//g, 'comment'],
77
+ [/\/\/.*$/g, 'comment'],
78
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
79
+ [/'(?:[^'\\]|\\.)*'/g, 'string'],
80
+ [/\b\d+\.?\d*(?:px|em|rem|%|vh|vw|vmin|vmax|deg|s|ms|fr|ch|ex)?\b/g, 'number'],
81
+ [/@[\w-]+/g, 'keyword'],
82
+ [/[.#][\w-]+/g, 'variable'],
83
+ [/[\w-]+(?=\s*:)/g, 'property'],
84
+ [/:\s*[^;{}]+/g, 'value'],
85
+ ]);
86
+ }
87
+ function tokenizeJSON(line) {
88
+ return tokenizeByPatterns(line, [
89
+ [/"(?:[^"\\]|\\.)*"(?=\s*:)/g, 'property'],
90
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
91
+ [/\b\d+\.?\d*(?:e[+-]?\d+)?\b/gi, 'number'],
92
+ [/\b(true|false)\b/g, 'keyword'],
93
+ [/\bnull\b/g, 'keyword'],
94
+ ]);
95
+ }
96
+ const python_keywords = /\b(False|None|True|and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b/g;
97
+ function tokenizePython(line) {
98
+ return tokenizeByPatterns(line, [
99
+ [/#.*$/g, 'comment'],
100
+ [/""".*?"""/g, 'string'],
101
+ [/'''.*?'''/g, 'string'],
102
+ [/f"(?:[^"\\]|\\.)*"/g, 'string'],
103
+ [/f'(?:[^'\\]|\\.)*'/g, 'string'],
104
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
105
+ [/'(?:[^'\\]|\\.)*'/g, 'string'],
106
+ [/@[\w.]+/g, 'decorator'],
107
+ [/\b\d+\.?\d*(?:e[+-]?\d+)?j?\b/gi, 'number'],
108
+ [/\b[A-Za-z_]\w*(?=\s*\()/g, 'function'],
109
+ [python_keywords, 'keyword'],
110
+ [/[+\-*/%=!<>&|^~@:]+/g, 'operator'],
111
+ ]);
112
+ }
113
+ function tokenizeBash(line) {
114
+ return tokenizeByPatterns(line, [
115
+ [/#.*$/g, 'comment'],
116
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
117
+ [/'[^']*'/g, 'string'],
118
+ [/\$\{[^}]*\}/g, 'variable'],
119
+ [/\$[\w]+/g, 'variable'],
120
+ [/\b\d+\.?\d*\b/g, 'number'],
121
+ [
122
+ /\b(alias|bg|bind|break|builtin|caller|case|cd|command|compgen|complete|continue|declare|dirs|disown|do|done|echo|elif|else|enable|esac|eval|exec|exit|export|false|fc|fg|fi|for|function|getopts|hash|help|history|if|in|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|select|set|shift|shopt|source|suspend|test|then|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|until|wait|while)\b/g,
123
+ 'keyword',
124
+ ],
125
+ [/\b[\w./-]+(?=\s|$)/g, 'function'],
126
+ [/[|&;<>]+/g, 'operator'],
127
+ ]);
128
+ }
129
+ const sql_keywords = /\b(ADD|ALL|ALTER|AND|ANY|AS|ASC|BACKUP|BETWEEN|BY|CASE|CHECK|COLUMN|CONSTRAINT|CREATE|DATABASE|DEFAULT|DELETE|DESC|DISTINCT|DROP|EACH|ELSE|END|EXEC|EXISTS|FOREIGN|FROM|FULL|GROUP|HAVING|IF|IN|INDEX|INNER|INSERT|INTO|IS|JOIN|KEY|LEFT|LIKE|LIMIT|NOT|NULL|OFFSET|ON|OR|ORDER|OUTER|PRIMARY|PROCEDURE|REFERENCES|REPLACE|RIGHT|ROWNUM|SELECT|SET|TABLE|THEN|TOP|TRUNCATE|UNION|UNIQUE|UPDATE|VALUES|VIEW|WHEN|WHERE|WITH)\b/gi;
130
+ function tokenizeSQL(line) {
131
+ return tokenizeByPatterns(line, [
132
+ [/--.*$/g, 'comment'],
133
+ [/\/\*.*?\*\//g, 'comment'],
134
+ [/'(?:[^'\\]|\\.)*'/g, 'string'],
135
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
136
+ [/\b\d+\.?\d*\b/g, 'number'],
137
+ [/\b[A-Za-z_]\w*(?=\s*\()/g, 'function'],
138
+ [sql_keywords, 'keyword'],
139
+ [/[+\-*/%=!<>&|^~]+/g, 'operator'],
140
+ ]);
141
+ }
142
+ function tokenizeSvelte(line) {
143
+ return tokenizeByPatterns(line, [
144
+ [/<!--.*?-->/g, 'comment'],
145
+ [/\/\/.*$/g, 'comment'],
146
+ [/\{#[\w]+/g, 'keyword'],
147
+ [/\{\/[\w]+\}/g, 'keyword'],
148
+ [/\{:[\w]+/g, 'keyword'],
149
+ [/`(?:[^`\\]|\\.)*`/g, 'string'],
150
+ [/"(?:[^"\\]|\\.)*"/g, 'string'],
151
+ [/'(?:[^'\\]|\\.)*'/g, 'string'],
152
+ [/\b\d+\.?\d*\b/g, 'number'],
153
+ [/<\/?[\w-]+/g, 'tag'],
154
+ [/\/?>/g, 'tag'],
155
+ [/\b[\w-]+(?==)/g, 'attribute'],
156
+ [/\b[A-Za-z_$][\w$]*(?=\s*\()/g, 'function'],
157
+ [js_keywords, 'keyword'],
158
+ [/[+\-*/%=!<>&|^~?:]+/g, 'operator'],
159
+ ]);
160
+ }
161
+ function tokenizeMarkdown(line) {
162
+ return tokenizeByPatterns(line, [
163
+ [/^#{1,6}\s+.*/g, 'heading'],
164
+ [/`[^`]+`/g, 'code'],
165
+ [/\*\*[^*]+\*\*/g, 'bold'],
166
+ [/\*[^*]+\*/g, 'italic'],
167
+ [/__[^_]+__/g, 'bold'],
168
+ [/_[^_]+_/g, 'italic'],
169
+ [/\[([^\]]+)\]\([^)]+\)/g, 'link'],
170
+ [/!\[([^\]]*)\]\([^)]+\)/g, 'link'],
171
+ ]);
172
+ }
173
+ export function tokenizeLine(line, lang) {
174
+ switch (lang) {
175
+ case 'javascript':
176
+ case 'js':
177
+ case 'jsx':
178
+ return tokenizeJS(line);
179
+ case 'typescript':
180
+ case 'ts':
181
+ case 'tsx':
182
+ return tokenizeTS(line);
183
+ case 'html':
184
+ case 'xml':
185
+ return tokenizeHTML(line);
186
+ case 'css':
187
+ case 'scss':
188
+ case 'sass':
189
+ case 'less':
190
+ return tokenizeCSS(line);
191
+ case 'json':
192
+ case 'jsonc':
193
+ return tokenizeJSON(line);
194
+ case 'python':
195
+ case 'py':
196
+ return tokenizePython(line);
197
+ case 'bash':
198
+ case 'sh':
199
+ case 'shell':
200
+ case 'zsh':
201
+ return tokenizeBash(line);
202
+ case 'sql':
203
+ return tokenizeSQL(line);
204
+ case 'svelte':
205
+ return tokenizeSvelte(line);
206
+ case 'markdown':
207
+ case 'md':
208
+ return tokenizeMarkdown(line);
209
+ default:
210
+ return tokenizePlain(line);
211
+ }
212
+ }
@@ -7,6 +7,7 @@ export { default as Chart } from './Chart.svelte';
7
7
  export type { ChartData, Dataset as ChartDataset } from './Chart.svelte';
8
8
  export type { CalendarEvent, MarkedDate } from './Calendar.svelte';
9
9
  export { default as Code } from './Code.svelte';
10
+ export { tokenizeLine, type Token as CodeToken } from './code-tokens.js';
10
11
  export { default as Comparison } from './Comparison.svelte';
11
12
  export { default as Counter } from './Counter.svelte';
12
13
  export { default as Expand } from './Expand.svelte';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EACX,MAAM,IAAI,WAAW,EACrB,gBAAgB,IAAI,qBAAqB,EACzC,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,UAAU,IAAI,eAAe,EAC7B,eAAe,IAAI,oBAAoB,EACvC,iBAAiB,IAAI,sBAAsB,EAC3C,uBAAuB,IAAI,4BAA4B,EACvD,cAAc,IAAI,mBAAmB,GACrC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EACX,MAAM,IAAI,WAAW,EACrB,gBAAgB,IAAI,qBAAqB,EACzC,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,UAAU,IAAI,eAAe,EAC7B,eAAe,IAAI,oBAAoB,EACvC,iBAAiB,IAAI,sBAAsB,EAC3C,uBAAuB,IAAI,4BAA4B,EACvD,cAAc,IAAI,mBAAmB,GACrC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
@@ -4,6 +4,7 @@ export { default as AvatarGroup } from './AvatarGroup.svelte';
4
4
  export { default as Calendar } from './Calendar.svelte';
5
5
  export { default as Chart } from './Chart.svelte';
6
6
  export { default as Code } from './Code.svelte';
7
+ export { tokenizeLine } from './code-tokens.js';
7
8
  export { default as Comparison } from './Comparison.svelte';
8
9
  export { default as Counter } from './Counter.svelte';
9
10
  export { default as Expand } from './Expand.svelte';
@@ -2362,15 +2362,26 @@
2362
2362
  /* NUMBER STEPPERS */
2363
2363
  /* ================================================================== */
2364
2364
 
2365
- /* The stepper pair sits after the suffix. */
2365
+ /* The stepper pair sits after the suffix. The buttons keep their full-size
2366
+ (4em, ~field-height) touch targets but pull together and into the field's
2367
+ end padding with negative margins, so the pair's visual footprint stays
2368
+ compact. */
2366
2369
  .steppers {
2367
2370
  display: flex;
2368
2371
  flex-direction: row;
2369
2372
  align-items: center;
2370
- gap: 0.1em;
2373
+ gap: 0;
2371
2374
  flex-shrink: 0;
2372
2375
  order: 2;
2373
- margin-right: -0.35em;
2376
+ margin-right: -0.45em;
2377
+ }
2378
+ .steppers :global(.button.input-icon-btn) {
2379
+ margin-inline: -0.5em;
2380
+ }
2381
+ /* The leading edge backs off only slightly — a strong pull there would
2382
+ drag the hit target over the field's text/clear button. */
2383
+ .steppers :global(.button.input-icon-btn:first-child) {
2384
+ margin-inline-start: -0.2em;
2374
2385
  }
2375
2386
 
2376
2387
  /* A thin divider sets the steppers off from the suffix — only needed when
@@ -2380,7 +2391,9 @@
2380
2391
  align-self: center;
2381
2392
  width: 1px;
2382
2393
  height: 1.5em;
2383
- margin-right: 0.35em;
2394
+ /* Clears the first stepper's negative leading margin (-0.2em at half
2395
+ font = 0.1em here) plus the original 0.35em breathing room */
2396
+ margin-right: 0.45em;
2384
2397
  background: var(--_border);
2385
2398
  }
2386
2399
 
@@ -794,8 +794,15 @@
794
794
  </span>
795
795
  </span>
796
796
  {/each}
797
- {:else if !multiple && selectedOptions && !Array.isArray(selectedOptions)}
797
+ {:else if !multiple && hasValue && selectedOptions && !Array.isArray(selectedOptions)}
798
798
  <span class="single-value">{selectedOptions.label}</span>
799
+ {:else if !multiple && !hasValue && selectedOptions && !Array.isArray(selectedOptions) && labelFloated}
800
+ <!-- An "empty" option (value '', null, undefined) is selected: the
801
+ field reads as empty at rest (the resting label covers it), but
802
+ while the label is floated (focus/open/distinct placeholder) the
803
+ chosen option's own label shows placeholder-styled — so picking
804
+ "None" gives visible feedback instead of looking like a no-op. -->
805
+ <span class="placeholder">{selectedOptions.label}</span>
799
806
  {:else if showPlaceholder}
800
807
  <span class="placeholder">{placeholder}</span>
801
808
  {/if}
@@ -1 +1 @@
1
- {"version":3,"file":"Select.svelte.d.ts","sourceRoot":"","sources":["../../src/form/Select.svelte.ts"],"names":[],"mappings":"AAGC,MAAM,WAAW,YAAY;IAC5B,qDAAqD;IACrD,KAAK,EAAE,OAAO,CAAC;IACf,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAIF,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AA45BlD,QAAA,MAAM,MAAM;YA94BqE,OAAO;cAAY,YAAY,EAAE;eAAa,OAAO;iBAAe,OAAO;gBAAc,OAAO;gBAAc,OAAO;cAAY,OAAO;eAAa,OAAO;kBAAgB,MAAM,GAAG,SAAS;YAAU,MAAM,GAAG,SAAS;YAAU,MAAM,GAAG,SAAS;YAAU,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,SAAS;kBAAgB,MAAM,GAAG,SAAS;eAAa,OAAO;WAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;eAAa,OAAO;cAAY,MAAM,GAAG,SAAS;YAAU,OAAO;kBAAgB,OAAO;aAAW,OAAO;;WAA6B,MAAM,GAAG,SAAS;YAAU,MAAM;eAAa,CAAC,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,SAAS;eAAa,CAAC,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,SAAS;eAAe,CAAC,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,GAAG,IAAI,GAAG,YAAY,CAAC,GAC9zB,SAAS;aAAW,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;cAAY,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;mBAAiB,OAAO,CAAC,CAAC,YAAY,GAAG,YAAY,EAAE,CAAC,CAAC,GAAG,SAAS;aAAW,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG,SAAS;eA64BpJ,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Select.svelte.d.ts","sourceRoot":"","sources":["../../src/form/Select.svelte.ts"],"names":[],"mappings":"AAGC,MAAM,WAAW,YAAY;IAC5B,qDAAqD;IACrD,KAAK,EAAE,OAAO,CAAC;IACf,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAIF,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AA+5BlD,QAAA,MAAM,MAAM;YAj5BqE,OAAO;cAAY,YAAY,EAAE;eAAa,OAAO;iBAAe,OAAO;gBAAc,OAAO;gBAAc,OAAO;cAAY,OAAO;eAAa,OAAO;kBAAgB,MAAM,GAAG,SAAS;YAAU,MAAM,GAAG,SAAS;YAAU,MAAM,GAAG,SAAS;YAAU,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,SAAS;kBAAgB,MAAM,GAAG,SAAS;eAAa,OAAO;WAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;eAAa,OAAO;cAAY,MAAM,GAAG,SAAS;YAAU,OAAO;kBAAgB,OAAO;aAAW,OAAO;;WAA6B,MAAM,GAAG,SAAS;YAAU,MAAM;eAAa,CAAC,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,SAAS;eAAa,CAAC,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,SAAS;eAAe,CAAC,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,GAAG,IAAI,GAAG,YAAY,CAAC,GAC9zB,SAAS;aAAW,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;cAAY,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS;mBAAiB,OAAO,CAAC,CAAC,YAAY,GAAG,YAAY,EAAE,CAAC,CAAC,GAAG,SAAS;aAAW,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG,SAAS;eAg5BpJ,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delightstack/components",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Delightstack Svelte 5 component library — accessible actions, forms, media, navigation, feedback, and display components.",