juxscript 1.1.57 → 1.1.59

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.
@@ -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;AAuFD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAiE9C;;;;;;;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;AAwOD;;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
  */
@@ -35,7 +72,85 @@ export function parseCode(code, language = 'javascript') {
35
72
  }));
36
73
  }
37
74
  /**
38
- * Tokenize a single line character-by-character
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)
92
+ */
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
+ * Check if character starts a multi-char operator
110
+ */
111
+ function consumeOperator(line, i) {
112
+ const char = line[i];
113
+ const next = line[i + 1];
114
+ const next2 = line[i + 2];
115
+ // Three-char operators
116
+ if (char === '=' && next === '=' && next2 === '=')
117
+ return '===';
118
+ if (char === '!' && next === '=' && next2 === '=')
119
+ return '!==';
120
+ if (char === '>' && next === '>' && next2 === '>')
121
+ return '>>>';
122
+ // Two-char operators
123
+ if (char === '=' && next === '=')
124
+ return '==';
125
+ if (char === '!' && next === '=')
126
+ return '!=';
127
+ if (char === '<' && next === '=')
128
+ return '<=';
129
+ if (char === '>' && next === '=')
130
+ return '>=';
131
+ if (char === '&' && next === '&')
132
+ return '&&';
133
+ if (char === '|' && next === '|')
134
+ return '||';
135
+ if (char === '+' && next === '+')
136
+ return '++';
137
+ if (char === '-' && next === '-')
138
+ return '--';
139
+ if (char === '=' && next === '>')
140
+ return '=>';
141
+ if (char === '>' && next === '>')
142
+ return '>>';
143
+ if (char === '<' && next === '<')
144
+ return '<<';
145
+ if (char === '*' && next === '*')
146
+ return '**';
147
+ if (char === '.' && next === '.' && next2 === '.')
148
+ return '...';
149
+ // Single-char operator
150
+ return char;
151
+ }
152
+ /**
153
+ * Tokenize a single line character-by-character with semantic categories
39
154
  */
40
155
  function tokenizeLine(line) {
41
156
  if (!line.trim())
@@ -43,6 +158,10 @@ function tokenizeLine(line) {
43
158
  let result = '';
44
159
  let i = 0;
45
160
  const len = line.length;
161
+ // Check for comment at start
162
+ if (line.trim().startsWith('//')) {
163
+ return `<span class="token-comment">${escapeHtml(line)}</span>`;
164
+ }
46
165
  while (i < len) {
47
166
  const char = line[i];
48
167
  // Whitespace - preserve
@@ -51,12 +170,59 @@ function tokenizeLine(line) {
51
170
  i++;
52
171
  continue;
53
172
  }
173
+ // Comment (// or /* */)
174
+ if (char === '/' && i + 1 < len) {
175
+ if (line[i + 1] === '/') {
176
+ // Line comment - consume rest of line
177
+ const comment = line.slice(i);
178
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
179
+ break;
180
+ }
181
+ else if (line[i + 1] === '*') {
182
+ // Block comment start
183
+ const commentEnd = line.indexOf('*/', i + 2);
184
+ if (commentEnd !== -1) {
185
+ const comment = line.slice(i, commentEnd + 2);
186
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
187
+ i = commentEnd + 2;
188
+ continue;
189
+ }
190
+ }
191
+ }
192
+ // String literals (single, double, backtick)
193
+ if (char === '"' || char === "'" || char === '`') {
194
+ let j = i + 1;
195
+ let escaped = false;
196
+ // Find matching quote
197
+ while (j < len) {
198
+ if (escaped) {
199
+ escaped = false;
200
+ j++;
201
+ continue;
202
+ }
203
+ if (line[j] === '\\') {
204
+ escaped = true;
205
+ j++;
206
+ continue;
207
+ }
208
+ if (line[j] === char) {
209
+ j++;
210
+ break;
211
+ }
212
+ j++;
213
+ }
214
+ const str = line.slice(i, j);
215
+ result += `<span class="token-string">${escapeHtml(str)}</span>`;
216
+ i = j;
217
+ continue;
218
+ }
54
219
  // Word (identifier or keyword)
55
220
  if (/[a-zA-Z_$]/.test(char)) {
56
221
  const word = consumeWord(line, i);
57
222
  const escaped = escapeHtml(word);
58
- if (KEYWORDS.has(word)) {
59
- result += `<span class="token-keyword">${escaped}</span>`;
223
+ if (ALL_KEYWORDS.has(word)) {
224
+ const tokenClass = getKeywordClass(word);
225
+ result += `<span class="${tokenClass}">${escaped}</span>`;
60
226
  }
61
227
  else {
62
228
  result += escaped;
@@ -71,6 +237,25 @@ function tokenizeLine(line) {
71
237
  i += num.length;
72
238
  continue;
73
239
  }
240
+ // Operators (multi-char)
241
+ if (isOperator(char)) {
242
+ const op = consumeOperator(line, i);
243
+ result += `<span class="token-operator">${escapeHtml(op)}</span>`;
244
+ i += op.length;
245
+ continue;
246
+ }
247
+ // Structural punctuation (braces, brackets, parens)
248
+ if (isStructural(char)) {
249
+ result += `<span class="token-structural">${escapeHtml(char)}</span>`;
250
+ i++;
251
+ continue;
252
+ }
253
+ // Delimiters (semicolon, comma, colon)
254
+ if (isDelimiter(char)) {
255
+ result += `<span class="token-delimiter">${escapeHtml(char)}</span>`;
256
+ i++;
257
+ continue;
258
+ }
74
259
  // Everything else - just escape and append
75
260
  result += escapeHtml(char);
76
261
  i++;
@@ -119,7 +304,7 @@ export function renderLineWithTokens(parsedLine) {
119
304
  */
120
305
  export function getSyntaxHighlightCSS() {
121
306
  return `
122
- /* Simple Code Highlighting */
307
+ /* Semantic Code Highlighting */
123
308
  .jux-code {
124
309
  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
125
310
  font-size: 14px;
@@ -173,13 +358,46 @@ export function getSyntaxHighlightCSS() {
173
358
  display: none;
174
359
  }
175
360
 
176
- /* Token colors */
177
- .token-keyword {
178
- color: #c678dd;
361
+ /* Token colors - Semantic categories */
362
+ .token-control {
363
+ color: #c678dd; /* Purple - control flow (if, for, return) */
179
364
  font-weight: 600;
180
365
  }
366
+ .token-declaration {
367
+ color: #61afef; /* Blue - declarations (const, let, function) */
368
+ font-weight: 600;
369
+ }
370
+ .token-operator-keyword {
371
+ color: #e06c75; /* Red - operator keywords (typeof, instanceof) */
372
+ font-weight: 600;
373
+ }
374
+ .token-special {
375
+ color: #e5c07b; /* Yellow - special keywords (this, super, async) */
376
+ font-weight: 600;
377
+ }
378
+ .token-jux {
379
+ color: #56b6c2; /* Cyan - JUX-specific (jux, state, render) */
380
+ font-weight: 700;
381
+ }
181
382
  .token-number {
182
- color: #d19a66;
383
+ color: #d19a66; /* Orange - numbers */
384
+ }
385
+ .token-string {
386
+ color: #98c379; /* Green - strings */
387
+ }
388
+ .token-comment {
389
+ color: #5c6370; /* Gray - comments */
390
+ font-style: italic;
391
+ }
392
+ .token-structural {
393
+ color: #abb2bf; /* Light gray - braces, brackets, parens */
394
+ font-weight: 600;
395
+ }
396
+ .token-delimiter {
397
+ color: #777; /* Dark gray - semicolons, commas, colons */
398
+ }
399
+ .token-operator {
400
+ color: #56b6c2; /* Cyan - operators (+, -, *, etc.) */
183
401
  }
184
402
  `;
185
403
  }
@@ -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;
@@ -45,7 +89,72 @@ export function parseCode(code: string, language: string = 'javascript'): Parsed
45
89
  }
46
90
 
47
91
  /**
48
- * Tokenize a single line character-by-character
92
+ * Get the appropriate CSS class for a keyword
93
+ */
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
+ }
123
+
124
+ /**
125
+ * Check if character starts a multi-char operator
126
+ */
127
+ function consumeOperator(line: string, i: number): string {
128
+ const char = line[i];
129
+ const next = line[i + 1];
130
+ const next2 = line[i + 2];
131
+
132
+ // Three-char operators
133
+ if (char === '=' && next === '=' && next2 === '=') return '===';
134
+ if (char === '!' && next === '=' && next2 === '=') return '!==';
135
+ if (char === '>' && next === '>' && next2 === '>') return '>>>';
136
+
137
+ // Two-char operators
138
+ if (char === '=' && next === '=') return '==';
139
+ if (char === '!' && next === '=') return '!=';
140
+ if (char === '<' && next === '=') return '<=';
141
+ if (char === '>' && next === '=') return '>=';
142
+ if (char === '&' && next === '&') return '&&';
143
+ if (char === '|' && next === '|') return '||';
144
+ if (char === '+' && next === '+') return '++';
145
+ if (char === '-' && next === '-') return '--';
146
+ if (char === '=' && next === '>') return '=>';
147
+ if (char === '>' && next === '>') return '>>';
148
+ if (char === '<' && next === '<') return '<<';
149
+ if (char === '*' && next === '*') return '**';
150
+ if (char === '.' && next === '.' && next2 === '.') return '...';
151
+
152
+ // Single-char operator
153
+ return char;
154
+ }
155
+
156
+ /**
157
+ * Tokenize a single line character-by-character with semantic categories
49
158
  */
50
159
  function tokenizeLine(line: string): string {
51
160
  if (!line.trim()) return ' ';
@@ -54,6 +163,11 @@ function tokenizeLine(line: string): string {
54
163
  let i = 0;
55
164
  const len = line.length;
56
165
 
166
+ // Check for comment at start
167
+ if (line.trim().startsWith('//')) {
168
+ return `<span class="token-comment">${escapeHtml(line)}</span>`;
169
+ }
170
+
57
171
  while (i < len) {
58
172
  const char = line[i];
59
173
 
@@ -64,13 +178,66 @@ function tokenizeLine(line: string): string {
64
178
  continue;
65
179
  }
66
180
 
181
+ // Comment (// or /* */)
182
+ if (char === '/' && i + 1 < len) {
183
+ if (line[i + 1] === '/') {
184
+ // Line comment - consume rest of line
185
+ const comment = line.slice(i);
186
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
187
+ break;
188
+ } else if (line[i + 1] === '*') {
189
+ // Block comment start
190
+ const commentEnd = line.indexOf('*/', i + 2);
191
+ if (commentEnd !== -1) {
192
+ const comment = line.slice(i, commentEnd + 2);
193
+ result += `<span class="token-comment">${escapeHtml(comment)}</span>`;
194
+ i = commentEnd + 2;
195
+ continue;
196
+ }
197
+ }
198
+ }
199
+
200
+ // String literals (single, double, backtick)
201
+ if (char === '"' || char === "'" || char === '`') {
202
+ let j = i + 1;
203
+ let escaped = false;
204
+
205
+ // Find matching quote
206
+ while (j < len) {
207
+ if (escaped) {
208
+ escaped = false;
209
+ j++;
210
+ continue;
211
+ }
212
+
213
+ if (line[j] === '\\') {
214
+ escaped = true;
215
+ j++;
216
+ continue;
217
+ }
218
+
219
+ if (line[j] === char) {
220
+ j++;
221
+ break;
222
+ }
223
+
224
+ j++;
225
+ }
226
+
227
+ const str = line.slice(i, j);
228
+ result += `<span class="token-string">${escapeHtml(str)}</span>`;
229
+ i = j;
230
+ continue;
231
+ }
232
+
67
233
  // Word (identifier or keyword)
68
234
  if (/[a-zA-Z_$]/.test(char)) {
69
235
  const word = consumeWord(line, i);
70
236
  const escaped = escapeHtml(word);
71
237
 
72
- if (KEYWORDS.has(word)) {
73
- result += `<span class="token-keyword">${escaped}</span>`;
238
+ if (ALL_KEYWORDS.has(word)) {
239
+ const tokenClass = getKeywordClass(word);
240
+ result += `<span class="${tokenClass}">${escaped}</span>`;
74
241
  } else {
75
242
  result += escaped;
76
243
  }
@@ -87,6 +254,28 @@ function tokenizeLine(line: string): string {
87
254
  continue;
88
255
  }
89
256
 
257
+ // Operators (multi-char)
258
+ if (isOperator(char)) {
259
+ const op = consumeOperator(line, i);
260
+ result += `<span class="token-operator">${escapeHtml(op)}</span>`;
261
+ i += op.length;
262
+ continue;
263
+ }
264
+
265
+ // Structural punctuation (braces, brackets, parens)
266
+ if (isStructural(char)) {
267
+ result += `<span class="token-structural">${escapeHtml(char)}</span>`;
268
+ i++;
269
+ continue;
270
+ }
271
+
272
+ // Delimiters (semicolon, comma, colon)
273
+ if (isDelimiter(char)) {
274
+ result += `<span class="token-delimiter">${escapeHtml(char)}</span>`;
275
+ i++;
276
+ continue;
277
+ }
278
+
90
279
  // Everything else - just escape and append
91
280
  result += escapeHtml(char);
92
281
  i++;
@@ -141,7 +330,7 @@ export function renderLineWithTokens(parsedLine: ParsedLine): string {
141
330
  */
142
331
  export function getSyntaxHighlightCSS(): string {
143
332
  return `
144
- /* Simple Code Highlighting */
333
+ /* Semantic Code Highlighting */
145
334
  .jux-code {
146
335
  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
147
336
  font-size: 14px;
@@ -195,13 +384,46 @@ export function getSyntaxHighlightCSS(): string {
195
384
  display: none;
196
385
  }
197
386
 
198
- /* Token colors */
199
- .token-keyword {
200
- color: #c678dd;
387
+ /* Token colors - Semantic categories */
388
+ .token-control {
389
+ color: #c678dd; /* Purple - control flow (if, for, return) */
390
+ font-weight: 600;
391
+ }
392
+ .token-declaration {
393
+ color: #61afef; /* Blue - declarations (const, let, function) */
394
+ font-weight: 600;
395
+ }
396
+ .token-operator-keyword {
397
+ color: #e06c75; /* Red - operator keywords (typeof, instanceof) */
398
+ font-weight: 600;
399
+ }
400
+ .token-special {
401
+ color: #e5c07b; /* Yellow - special keywords (this, super, async) */
201
402
  font-weight: 600;
202
403
  }
404
+ .token-jux {
405
+ color: #56b6c2; /* Cyan - JUX-specific (jux, state, render) */
406
+ font-weight: 700;
407
+ }
203
408
  .token-number {
204
- color: #d19a66;
409
+ color: #d19a66; /* Orange - numbers */
410
+ }
411
+ .token-string {
412
+ color: #98c379; /* Green - strings */
413
+ }
414
+ .token-comment {
415
+ color: #5c6370; /* Gray - comments */
416
+ font-style: italic;
417
+ }
418
+ .token-structural {
419
+ color: #abb2bf; /* Light gray - braces, brackets, parens */
420
+ font-weight: 600;
421
+ }
422
+ .token-delimiter {
423
+ color: #777; /* Dark gray - semicolons, commas, colons */
424
+ }
425
+ .token-operator {
426
+ color: #56b6c2; /* Cyan - operators (+, -, *, etc.) */
205
427
  }
206
428
  `;
207
429
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.57",
3
+ "version": "1.1.59",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",