juxscript 1.1.56 → 1.1.58

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.
@@ -8,7 +8,7 @@ export interface ParsedLine {
8
8
  */
9
9
  declare function escapeHtml(text: string): string;
10
10
  /**
11
- * Parse code into lines with simple syntax highlighting
11
+ * Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
12
12
  */
13
13
  export declare function parseCode(code: string, language?: string): ParsedLine[];
14
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,UAAU,EAAE,CAQrF;AAuCD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA8D9C;;;;;;;AAED,wBAKE"}
1
+ {"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"AA2DA,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,UAAU,EAAE,CAQrF;AAiLD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAkG9C;;;;;;;AAED,wBAKE"}
@@ -12,6 +12,43 @@ const KEYWORDS = new Set([
12
12
  '!=', '!==', '+', '-', '*', '/', '%', '++', '--', '&&', '||', '!', '<', '>',
13
13
  '<=', '>=', '=>', '...', '.', ',', '(', ')', '{', '}', '[', ']', ':', '?'
14
14
  ]);
15
+ /**
16
+ * Categorized token sets for semantic highlighting
17
+ */
18
+ // Control flow keywords (loops, conditionals, etc.)
19
+ const CONTROL_FLOW = new Set([
20
+ 'if', 'else', 'switch', 'case', 'default',
21
+ 'for', 'while', 'do', 'break', 'continue',
22
+ 'return', 'throw', 'try', 'catch', 'finally'
23
+ ]);
24
+ // Declaration keywords
25
+ const DECLARATIONS = new Set([
26
+ 'const', 'let', 'var', 'function', 'class',
27
+ 'import', 'export', 'from', 'as',
28
+ 'interface', 'type', 'enum', 'namespace',
29
+ 'static', 'get', 'set', 'extends'
30
+ ]);
31
+ // Operator keywords
32
+ const OPERATORS = new Set([
33
+ 'new', 'delete', 'typeof', 'instanceof',
34
+ 'void', 'await', 'yield', 'in', 'of'
35
+ ]);
36
+ // Special keywords
37
+ const SPECIAL = new Set([
38
+ 'this', 'super', 'async', 'debugger', 'with'
39
+ ]);
40
+ // JUX-specific keywords
41
+ const JUX_KEYWORDS = new Set([
42
+ 'jux', 'state', 'registry', 'render', 'bind', 'sync'
43
+ ]);
44
+ // Combine all keywords for quick lookup
45
+ const ALL_KEYWORDS = new Set([
46
+ ...CONTROL_FLOW,
47
+ ...DECLARATIONS,
48
+ ...OPERATORS,
49
+ ...SPECIAL,
50
+ ...JUX_KEYWORDS
51
+ ]);
15
52
  /**
16
53
  * Escape HTML entities
17
54
  */
@@ -24,47 +61,176 @@ function escapeHtml(text) {
24
61
  .replace(/'/g, '&#39;');
25
62
  }
26
63
  /**
27
- * Parse code into lines with simple syntax highlighting
64
+ * Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
28
65
  */
29
66
  export function parseCode(code, language = 'javascript') {
30
67
  const lines = code.split('\n');
31
68
  return lines.map((line, index) => ({
32
69
  lineNumber: index + 1,
33
- html: highlightLine(line),
70
+ html: tokenizeLine(line),
34
71
  raw: line
35
72
  }));
36
73
  }
37
74
  /**
38
- * Highlight a single line - token-by-token approach to avoid nested spans
75
+ * Get the appropriate CSS class for a keyword
76
+ */
77
+ function getKeywordClass(word) {
78
+ if (CONTROL_FLOW.has(word))
79
+ return 'token-control';
80
+ if (DECLARATIONS.has(word))
81
+ return 'token-declaration';
82
+ if (OPERATORS.has(word))
83
+ return 'token-operator-keyword';
84
+ if (SPECIAL.has(word))
85
+ return 'token-special';
86
+ if (JUX_KEYWORDS.has(word))
87
+ return 'token-jux';
88
+ return 'token-keyword'; // fallback
89
+ }
90
+ /**
91
+ * Check if character is a structural punctuation (braces, brackets, parens)
39
92
  */
40
- function highlightLine(line) {
93
+ function isStructural(char) {
94
+ return '(){}[]'.includes(char);
95
+ }
96
+ /**
97
+ * Check if character is a delimiter (semicolon, comma, colon)
98
+ */
99
+ function isDelimiter(char) {
100
+ return ';,:'.includes(char);
101
+ }
102
+ /**
103
+ * Check if character is an operator
104
+ */
105
+ function isOperator(char) {
106
+ return '+-*/%=<>!&|^~?'.includes(char);
107
+ }
108
+ /**
109
+ * Tokenize a single line character-by-character with semantic categories
110
+ */
111
+ function tokenizeLine(line) {
41
112
  if (!line.trim())
42
113
  return ' ';
43
- // ✅ Split line into tokens (words, operators, whitespace)
44
- const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
45
114
  let result = '';
46
- tokens.forEach(token => {
47
- // Skip empty tokens
48
- if (!token)
49
- return;
50
- // Whitespace - preserve as-is
51
- if (/^\s+$/.test(token)) {
52
- result += token;
53
- return;
115
+ let i = 0;
116
+ const len = line.length;
117
+ // Check for comment at start
118
+ if (line.trim().startsWith('//')) {
119
+ return `<span class="token-comment">${escapeHtml(line)}</span>`;
120
+ }
121
+ while (i < len) {
122
+ const char = line[i];
123
+ // Whitespace - preserve
124
+ if (/\s/.test(char)) {
125
+ result += char;
126
+ i++;
127
+ continue;
54
128
  }
55
- // Escape the token
56
- const escaped = escapeHtml(token);
57
- // Keywords
58
- if (KEYWORDS.has(token)) {
59
- result += `<span class="token-keyword">${escaped}</span>`;
129
+ // Comment (// or /* */)
130
+ if (char === '/' && i + 1 < len) {
131
+ if (line[i + 1] === '/') {
132
+ // Line comment - consume rest of line
133
+ const comment = line.slice(i);
134
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
135
+ break;
136
+ }
137
+ else if (line[i + 1] === '*') {
138
+ // Block comment start
139
+ const commentEnd = line.indexOf('*/', i + 2);
140
+ if (commentEnd !== -1) {
141
+ const comment = line.slice(i, commentEnd + 2);
142
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
143
+ i = commentEnd + 2;
144
+ continue;
145
+ }
146
+ }
60
147
  }
61
- // Everything else - plain text
62
- else {
63
- result += escaped;
148
+ // String literals (single, double, backtick)
149
+ if (char === '"' || char === "'" || char === '`') {
150
+ const stringEnd = line.indexOf(char, i + 1);
151
+ if (stringEnd !== -1) {
152
+ const str = line.slice(i, stringEnd + 1);
153
+ result += `<span class="token-string">${escapeHtml(str)}</span>`;
154
+ i = stringEnd + 1;
155
+ continue;
156
+ }
157
+ }
158
+ // Word (identifier or keyword)
159
+ if (/[a-zA-Z_$]/.test(char)) {
160
+ const word = consumeWord(line, i);
161
+ const escaped = escapeHtml(word);
162
+ if (ALL_KEYWORDS.has(word)) {
163
+ const tokenClass = getKeywordClass(word);
164
+ result += `<span class="${tokenClass}">${escaped}</span>`;
165
+ }
166
+ else {
167
+ result += escaped;
168
+ }
169
+ i += word.length;
170
+ continue;
171
+ }
172
+ // Number
173
+ if (/\d/.test(char)) {
174
+ const num = consumeNumber(line, i);
175
+ result += `<span class="token-number">${escapeHtml(num)}</span>`;
176
+ i += num.length;
177
+ continue;
178
+ }
179
+ // Structural punctuation (braces, brackets, parens)
180
+ if (isStructural(char)) {
181
+ result += `<span class="token-structural">${escapeHtml(char)}</span>`;
182
+ i++;
183
+ continue;
184
+ }
185
+ // Delimiters (semicolon, comma, colon)
186
+ if (isDelimiter(char)) {
187
+ result += `<span class="token-delimiter">${escapeHtml(char)}</span>`;
188
+ i++;
189
+ continue;
64
190
  }
65
- });
191
+ // Operators
192
+ if (isOperator(char)) {
193
+ result += `<span class="token-operator">${escapeHtml(char)}</span>`;
194
+ i++;
195
+ continue;
196
+ }
197
+ // Everything else - just escape and append
198
+ result += escapeHtml(char);
199
+ i++;
200
+ }
66
201
  return result || ' ';
67
202
  }
203
+ /**
204
+ * Consume a complete word (identifier)
205
+ */
206
+ function consumeWord(line, start) {
207
+ let i = start;
208
+ while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
209
+ i++;
210
+ }
211
+ return line.substring(start, i);
212
+ }
213
+ /**
214
+ * Consume a complete number
215
+ */
216
+ function consumeNumber(line, start) {
217
+ let i = start;
218
+ let hasDot = false;
219
+ while (i < line.length) {
220
+ const char = line[i];
221
+ if (/\d/.test(char)) {
222
+ i++;
223
+ }
224
+ else if (char === '.' && !hasDot) {
225
+ hasDot = true;
226
+ i++;
227
+ }
228
+ else {
229
+ break;
230
+ }
231
+ }
232
+ return line.substring(start, i);
233
+ }
68
234
  /**
69
235
  * Render a parsed line
70
236
  */
@@ -76,7 +242,7 @@ export function renderLineWithTokens(parsedLine) {
76
242
  */
77
243
  export function getSyntaxHighlightCSS() {
78
244
  return `
79
- /* Simple Syntax Highlighting */
245
+ /* Semantic Code Highlighting */
80
246
  .jux-code {
81
247
  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
82
248
  font-size: 14px;
@@ -130,10 +296,46 @@ export function getSyntaxHighlightCSS() {
130
296
  display: none;
131
297
  }
132
298
 
133
- /* Token colors - JUST KEYWORDS */
134
- .token-keyword {
135
- color: #c678dd;
299
+ /* Token colors - Semantic categories */
300
+ .token-control {
301
+ color: #c678dd; /* Purple - control flow (if, for, return) */
302
+ font-weight: 600;
303
+ }
304
+ .token-declaration {
305
+ color: #61afef; /* Blue - declarations (const, let, function) */
306
+ font-weight: 600;
307
+ }
308
+ .token-operator-keyword {
309
+ color: #e06c75; /* Red - operator keywords (typeof, instanceof) */
136
310
  font-weight: 600;
311
+ }
312
+ .token-special {
313
+ color: #e5c07b; /* Yellow - special keywords (this, super, async) */
314
+ font-weight: 600;
315
+ }
316
+ .token-jux {
317
+ color: #56b6c2; /* Cyan - JUX-specific (jux, state, render) */
318
+ font-weight: 700;
319
+ }
320
+ .token-number {
321
+ color: #d19a66; /* Orange - numbers */
322
+ }
323
+ .token-string {
324
+ color: #98c379; /* Green - strings */
325
+ }
326
+ .token-comment {
327
+ color: #5c6370; /* Gray - comments */
328
+ font-style: italic;
329
+ }
330
+ .token-structural {
331
+ color: #abb2bf; /* Light gray - braces, brackets, parens */
332
+ font-weight: 600;
333
+ }
334
+ .token-delimiter {
335
+ color: #777; /* Dark gray - semicolons, commas, colons */
336
+ }
337
+ .token-operator {
338
+ color: #56b6c2; /* Cyan - operators (+, -, *, etc.) */
137
339
  }
138
340
  `;
139
341
  }
@@ -13,6 +13,50 @@ const KEYWORDS = new Set([
13
13
  '<=', '>=', '=>', '...', '.', ',', '(', ')', '{', '}', '[', ']', ':', '?'
14
14
  ]);
15
15
 
16
+ /**
17
+ * Categorized token sets for semantic highlighting
18
+ */
19
+
20
+ // Control flow keywords (loops, conditionals, etc.)
21
+ const CONTROL_FLOW = new Set([
22
+ 'if', 'else', 'switch', 'case', 'default',
23
+ 'for', 'while', 'do', 'break', 'continue',
24
+ 'return', 'throw', 'try', 'catch', 'finally'
25
+ ]);
26
+
27
+ // Declaration keywords
28
+ const DECLARATIONS = new Set([
29
+ 'const', 'let', 'var', 'function', 'class',
30
+ 'import', 'export', 'from', 'as',
31
+ 'interface', 'type', 'enum', 'namespace',
32
+ 'static', 'get', 'set', 'extends'
33
+ ]);
34
+
35
+ // Operator keywords
36
+ const OPERATORS = new Set([
37
+ 'new', 'delete', 'typeof', 'instanceof',
38
+ 'void', 'await', 'yield', 'in', 'of'
39
+ ]);
40
+
41
+ // Special keywords
42
+ const SPECIAL = new Set([
43
+ 'this', 'super', 'async', 'debugger', 'with'
44
+ ]);
45
+
46
+ // JUX-specific keywords
47
+ const JUX_KEYWORDS = new Set([
48
+ 'jux', 'state', 'registry', 'render', 'bind', 'sync'
49
+ ]);
50
+
51
+ // Combine all keywords for quick lookup
52
+ const ALL_KEYWORDS = new Set([
53
+ ...CONTROL_FLOW,
54
+ ...DECLARATIONS,
55
+ ...OPERATORS,
56
+ ...SPECIAL,
57
+ ...JUX_KEYWORDS
58
+ ]);
59
+
16
60
  export interface ParsedLine {
17
61
  lineNumber: number;
18
62
  html: string;
@@ -32,55 +76,193 @@ function escapeHtml(text: string): string {
32
76
  }
33
77
 
34
78
  /**
35
- * Parse code into lines with simple syntax highlighting
79
+ * Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
36
80
  */
37
81
  export function parseCode(code: string, language: string = 'javascript'): ParsedLine[] {
38
82
  const lines = code.split('\n');
39
83
 
40
84
  return lines.map((line, index) => ({
41
85
  lineNumber: index + 1,
42
- html: highlightLine(line),
86
+ html: tokenizeLine(line),
43
87
  raw: line
44
88
  }));
45
89
  }
46
90
 
47
91
  /**
48
- * Highlight a single line - token-by-token approach to avoid nested spans
92
+ * Get the appropriate CSS class for a keyword
49
93
  */
50
- function highlightLine(line: string): string {
51
- if (!line.trim()) return ' ';
94
+ function getKeywordClass(word: string): string {
95
+ if (CONTROL_FLOW.has(word)) return 'token-control';
96
+ if (DECLARATIONS.has(word)) return 'token-declaration';
97
+ if (OPERATORS.has(word)) return 'token-operator-keyword';
98
+ if (SPECIAL.has(word)) return 'token-special';
99
+ if (JUX_KEYWORDS.has(word)) return 'token-jux';
100
+ return 'token-keyword'; // fallback
101
+ }
102
+
103
+ /**
104
+ * Check if character is a structural punctuation (braces, brackets, parens)
105
+ */
106
+ function isStructural(char: string): boolean {
107
+ return '(){}[]'.includes(char);
108
+ }
109
+
110
+ /**
111
+ * Check if character is a delimiter (semicolon, comma, colon)
112
+ */
113
+ function isDelimiter(char: string): boolean {
114
+ return ';,:'.includes(char);
115
+ }
116
+
117
+ /**
118
+ * Check if character is an operator
119
+ */
120
+ function isOperator(char: string): boolean {
121
+ return '+-*/%=<>!&|^~?'.includes(char);
122
+ }
52
123
 
53
- // ✅ Split line into tokens (words, operators, whitespace)
54
- const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
124
+ /**
125
+ * Tokenize a single line character-by-character with semantic categories
126
+ */
127
+ function tokenizeLine(line: string): string {
128
+ if (!line.trim()) return ' ';
55
129
 
56
130
  let result = '';
131
+ let i = 0;
132
+ const len = line.length;
57
133
 
58
- tokens.forEach(token => {
59
- // Skip empty tokens
60
- if (!token) return;
134
+ // Check for comment at start
135
+ if (line.trim().startsWith('//')) {
136
+ return `<span class="token-comment">${escapeHtml(line)}</span>`;
137
+ }
61
138
 
62
- // Whitespace - preserve as-is
63
- if (/^\s+$/.test(token)) {
64
- result += token;
65
- return;
139
+ while (i < len) {
140
+ const char = line[i];
141
+
142
+ // Whitespace - preserve
143
+ if (/\s/.test(char)) {
144
+ result += char;
145
+ i++;
146
+ continue;
66
147
  }
67
148
 
68
- // Escape the token
69
- const escaped = escapeHtml(token);
149
+ // Comment (// or /* */)
150
+ if (char === '/' && i + 1 < len) {
151
+ if (line[i + 1] === '/') {
152
+ // Line comment - consume rest of line
153
+ const comment = line.slice(i);
154
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
155
+ break;
156
+ } else if (line[i + 1] === '*') {
157
+ // Block comment start
158
+ const commentEnd = line.indexOf('*/', i + 2);
159
+ if (commentEnd !== -1) {
160
+ const comment = line.slice(i, commentEnd + 2);
161
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
162
+ i = commentEnd + 2;
163
+ continue;
164
+ }
165
+ }
166
+ }
70
167
 
71
- // Keywords
72
- if (KEYWORDS.has(token)) {
73
- result += `<span class="token-keyword">${escaped}</span>`;
168
+ // String literals (single, double, backtick)
169
+ if (char === '"' || char === "'" || char === '`') {
170
+ const stringEnd = line.indexOf(char, i + 1);
171
+ if (stringEnd !== -1) {
172
+ const str = line.slice(i, stringEnd + 1);
173
+ result += `<span class="token-string">${escapeHtml(str)}</span>`;
174
+ i = stringEnd + 1;
175
+ continue;
176
+ }
74
177
  }
75
- // Everything else - plain text
76
- else {
77
- result += escaped;
178
+
179
+ // Word (identifier or keyword)
180
+ if (/[a-zA-Z_$]/.test(char)) {
181
+ const word = consumeWord(line, i);
182
+ const escaped = escapeHtml(word);
183
+
184
+ if (ALL_KEYWORDS.has(word)) {
185
+ const tokenClass = getKeywordClass(word);
186
+ result += `<span class="${tokenClass}">${escaped}</span>`;
187
+ } else {
188
+ result += escaped;
189
+ }
190
+
191
+ i += word.length;
192
+ continue;
193
+ }
194
+
195
+ // Number
196
+ if (/\d/.test(char)) {
197
+ const num = consumeNumber(line, i);
198
+ result += `<span class="token-number">${escapeHtml(num)}</span>`;
199
+ i += num.length;
200
+ continue;
201
+ }
202
+
203
+ // Structural punctuation (braces, brackets, parens)
204
+ if (isStructural(char)) {
205
+ result += `<span class="token-structural">${escapeHtml(char)}</span>`;
206
+ i++;
207
+ continue;
208
+ }
209
+
210
+ // Delimiters (semicolon, comma, colon)
211
+ if (isDelimiter(char)) {
212
+ result += `<span class="token-delimiter">${escapeHtml(char)}</span>`;
213
+ i++;
214
+ continue;
215
+ }
216
+
217
+ // Operators
218
+ if (isOperator(char)) {
219
+ result += `<span class="token-operator">${escapeHtml(char)}</span>`;
220
+ i++;
221
+ continue;
78
222
  }
79
- });
223
+
224
+ // Everything else - just escape and append
225
+ result += escapeHtml(char);
226
+ i++;
227
+ }
80
228
 
81
229
  return result || ' ';
82
230
  }
83
231
 
232
+ /**
233
+ * Consume a complete word (identifier)
234
+ */
235
+ function consumeWord(line: string, start: number): string {
236
+ let i = start;
237
+ while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
238
+ i++;
239
+ }
240
+ return line.substring(start, i);
241
+ }
242
+
243
+ /**
244
+ * Consume a complete number
245
+ */
246
+ function consumeNumber(line: string, start: number): string {
247
+ let i = start;
248
+ let hasDot = false;
249
+
250
+ while (i < line.length) {
251
+ const char = line[i];
252
+
253
+ if (/\d/.test(char)) {
254
+ i++;
255
+ } else if (char === '.' && !hasDot) {
256
+ hasDot = true;
257
+ i++;
258
+ } else {
259
+ break;
260
+ }
261
+ }
262
+
263
+ return line.substring(start, i);
264
+ }
265
+
84
266
  /**
85
267
  * Render a parsed line
86
268
  */
@@ -93,7 +275,7 @@ export function renderLineWithTokens(parsedLine: ParsedLine): string {
93
275
  */
94
276
  export function getSyntaxHighlightCSS(): string {
95
277
  return `
96
- /* Simple Syntax Highlighting */
278
+ /* Semantic Code Highlighting */
97
279
  .jux-code {
98
280
  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
99
281
  font-size: 14px;
@@ -147,10 +329,46 @@ export function getSyntaxHighlightCSS(): string {
147
329
  display: none;
148
330
  }
149
331
 
150
- /* Token colors - JUST KEYWORDS */
151
- .token-keyword {
152
- color: #c678dd;
332
+ /* Token colors - Semantic categories */
333
+ .token-control {
334
+ color: #c678dd; /* Purple - control flow (if, for, return) */
153
335
  font-weight: 600;
336
+ }
337
+ .token-declaration {
338
+ color: #61afef; /* Blue - declarations (const, let, function) */
339
+ font-weight: 600;
340
+ }
341
+ .token-operator-keyword {
342
+ color: #e06c75; /* Red - operator keywords (typeof, instanceof) */
343
+ font-weight: 600;
344
+ }
345
+ .token-special {
346
+ color: #e5c07b; /* Yellow - special keywords (this, super, async) */
347
+ font-weight: 600;
348
+ }
349
+ .token-jux {
350
+ color: #56b6c2; /* Cyan - JUX-specific (jux, state, render) */
351
+ font-weight: 700;
352
+ }
353
+ .token-number {
354
+ color: #d19a66; /* Orange - numbers */
355
+ }
356
+ .token-string {
357
+ color: #98c379; /* Green - strings */
358
+ }
359
+ .token-comment {
360
+ color: #5c6370; /* Gray - comments */
361
+ font-style: italic;
362
+ }
363
+ .token-structural {
364
+ color: #abb2bf; /* Light gray - braces, brackets, parens */
365
+ font-weight: 600;
366
+ }
367
+ .token-delimiter {
368
+ color: #777; /* Dark gray - semicolons, commas, colons */
369
+ }
370
+ .token-operator {
371
+ color: #56b6c2; /* Cyan - operators (+, -, *, etc.) */
154
372
  }
155
373
  `;
156
374
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.56",
3
+ "version": "1.1.58",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",