juxscript 1.1.55 → 1.1.57

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":"AAaA,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":"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"}
@@ -8,7 +8,9 @@ const KEYWORDS = new Set([
8
8
  'let', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try',
9
9
  'typeof', 'var', 'void', 'while', 'with', 'yield', 'from', 'of',
10
10
  'static', 'get', 'set', 'as', 'interface', 'type', 'enum', 'namespace',
11
- 'jux', 'state', 'registry', 'render', 'bind', 'sync'
11
+ 'jux', 'state', 'registry', 'render', 'bind', 'sync', ';', '=', '==', '===',
12
+ '!=', '!==', '+', '-', '*', '/', '%', '++', '--', '&&', '||', '!', '<', '>',
13
+ '<=', '>=', '=>', '...', '.', ',', '(', ')', '{', '}', '[', ']', ':', '?'
12
14
  ]);
13
15
  /**
14
16
  * Escape HTML entities
@@ -22,47 +24,90 @@ function escapeHtml(text) {
22
24
  .replace(/'/g, '&#39;');
23
25
  }
24
26
  /**
25
- * Parse code into lines with simple syntax highlighting
27
+ * Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
26
28
  */
27
29
  export function parseCode(code, language = 'javascript') {
28
30
  const lines = code.split('\n');
29
31
  return lines.map((line, index) => ({
30
32
  lineNumber: index + 1,
31
- html: highlightLine(line),
33
+ html: tokenizeLine(line),
32
34
  raw: line
33
35
  }));
34
36
  }
35
37
  /**
36
- * Highlight a single line - token-by-token approach to avoid nested spans
38
+ * Tokenize a single line character-by-character
37
39
  */
38
- function highlightLine(line) {
40
+ function tokenizeLine(line) {
39
41
  if (!line.trim())
40
42
  return ' ';
41
- // ✅ Split line into tokens (words, operators, whitespace)
42
- const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
43
43
  let result = '';
44
- tokens.forEach(token => {
45
- // Skip empty tokens
46
- if (!token)
47
- return;
48
- // Whitespace - preserve as-is
49
- if (/^\s+$/.test(token)) {
50
- result += token;
51
- return;
44
+ let i = 0;
45
+ const len = line.length;
46
+ while (i < len) {
47
+ const char = line[i];
48
+ // Whitespace - preserve
49
+ if (/\s/.test(char)) {
50
+ result += char;
51
+ i++;
52
+ continue;
52
53
  }
53
- // Escape the token
54
- const escaped = escapeHtml(token);
55
- // Keywords
56
- if (KEYWORDS.has(token)) {
57
- result += `<span class="token-keyword">${escaped}</span>`;
54
+ // Word (identifier or keyword)
55
+ if (/[a-zA-Z_$]/.test(char)) {
56
+ const word = consumeWord(line, i);
57
+ const escaped = escapeHtml(word);
58
+ if (KEYWORDS.has(word)) {
59
+ result += `<span class="token-keyword">${escaped}</span>`;
60
+ }
61
+ else {
62
+ result += escaped;
63
+ }
64
+ i += word.length;
65
+ continue;
58
66
  }
59
- // Everything else - plain text
60
- else {
61
- result += escaped;
67
+ // Number
68
+ if (/\d/.test(char)) {
69
+ const num = consumeNumber(line, i);
70
+ result += `<span class="token-number">${escapeHtml(num)}</span>`;
71
+ i += num.length;
72
+ continue;
62
73
  }
63
- });
74
+ // Everything else - just escape and append
75
+ result += escapeHtml(char);
76
+ i++;
77
+ }
64
78
  return result || ' ';
65
79
  }
80
+ /**
81
+ * Consume a complete word (identifier)
82
+ */
83
+ function consumeWord(line, start) {
84
+ let i = start;
85
+ while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
86
+ i++;
87
+ }
88
+ return line.substring(start, i);
89
+ }
90
+ /**
91
+ * Consume a complete number
92
+ */
93
+ function consumeNumber(line, start) {
94
+ let i = start;
95
+ let hasDot = false;
96
+ while (i < line.length) {
97
+ const char = line[i];
98
+ if (/\d/.test(char)) {
99
+ i++;
100
+ }
101
+ else if (char === '.' && !hasDot) {
102
+ hasDot = true;
103
+ i++;
104
+ }
105
+ else {
106
+ break;
107
+ }
108
+ }
109
+ return line.substring(start, i);
110
+ }
66
111
  /**
67
112
  * Render a parsed line
68
113
  */
@@ -74,7 +119,7 @@ export function renderLineWithTokens(parsedLine) {
74
119
  */
75
120
  export function getSyntaxHighlightCSS() {
76
121
  return `
77
- /* Simple Syntax Highlighting */
122
+ /* Simple Code Highlighting */
78
123
  .jux-code {
79
124
  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
80
125
  font-size: 14px;
@@ -128,10 +173,13 @@ export function getSyntaxHighlightCSS() {
128
173
  display: none;
129
174
  }
130
175
 
131
- /* Token colors - JUST KEYWORDS */
176
+ /* Token colors */
132
177
  .token-keyword {
133
178
  color: #c678dd;
134
179
  font-weight: 600;
180
+ }
181
+ .token-number {
182
+ color: #d19a66;
135
183
  }
136
184
  `;
137
185
  }
@@ -8,7 +8,9 @@ const KEYWORDS = new Set([
8
8
  'let', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try',
9
9
  'typeof', 'var', 'void', 'while', 'with', 'yield', 'from', 'of',
10
10
  'static', 'get', 'set', 'as', 'interface', 'type', 'enum', 'namespace',
11
- 'jux', 'state', 'registry', 'render', 'bind', 'sync'
11
+ 'jux', 'state', 'registry', 'render', 'bind', 'sync', ';', '=', '==', '===',
12
+ '!=', '!==', '+', '-', '*', '/', '%', '++', '--', '&&', '||', '!', '<', '>',
13
+ '<=', '>=', '=>', '...', '.', ',', '(', ')', '{', '}', '[', ']', ':', '?'
12
14
  ]);
13
15
 
14
16
  export interface ParsedLine {
@@ -30,55 +32,103 @@ function escapeHtml(text: string): string {
30
32
  }
31
33
 
32
34
  /**
33
- * Parse code into lines with simple syntax highlighting
35
+ * Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
34
36
  */
35
37
  export function parseCode(code: string, language: string = 'javascript'): ParsedLine[] {
36
38
  const lines = code.split('\n');
37
39
 
38
40
  return lines.map((line, index) => ({
39
41
  lineNumber: index + 1,
40
- html: highlightLine(line),
42
+ html: tokenizeLine(line),
41
43
  raw: line
42
44
  }));
43
45
  }
44
46
 
45
47
  /**
46
- * Highlight a single line - token-by-token approach to avoid nested spans
48
+ * Tokenize a single line character-by-character
47
49
  */
48
- function highlightLine(line: string): string {
50
+ function tokenizeLine(line: string): string {
49
51
  if (!line.trim()) return ' ';
50
52
 
51
- // ✅ Split line into tokens (words, operators, whitespace)
52
- const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
53
-
54
53
  let result = '';
54
+ let i = 0;
55
+ const len = line.length;
55
56
 
56
- tokens.forEach(token => {
57
- // Skip empty tokens
58
- if (!token) return;
57
+ while (i < len) {
58
+ const char = line[i];
59
59
 
60
- // Whitespace - preserve as-is
61
- if (/^\s+$/.test(token)) {
62
- result += token;
63
- return;
60
+ // Whitespace - preserve
61
+ if (/\s/.test(char)) {
62
+ result += char;
63
+ i++;
64
+ continue;
64
65
  }
65
66
 
66
- // Escape the token
67
- const escaped = escapeHtml(token);
67
+ // Word (identifier or keyword)
68
+ if (/[a-zA-Z_$]/.test(char)) {
69
+ const word = consumeWord(line, i);
70
+ const escaped = escapeHtml(word);
71
+
72
+ if (KEYWORDS.has(word)) {
73
+ result += `<span class="token-keyword">${escaped}</span>`;
74
+ } else {
75
+ result += escaped;
76
+ }
68
77
 
69
- // Keywords
70
- if (KEYWORDS.has(token)) {
71
- result += `<span class="token-keyword">${escaped}</span>`;
78
+ i += word.length;
79
+ continue;
72
80
  }
73
- // Everything else - plain text
74
- else {
75
- result += escaped;
81
+
82
+ // Number
83
+ if (/\d/.test(char)) {
84
+ const num = consumeNumber(line, i);
85
+ result += `<span class="token-number">${escapeHtml(num)}</span>`;
86
+ i += num.length;
87
+ continue;
76
88
  }
77
- });
89
+
90
+ // Everything else - just escape and append
91
+ result += escapeHtml(char);
92
+ i++;
93
+ }
78
94
 
79
95
  return result || ' ';
80
96
  }
81
97
 
98
+ /**
99
+ * Consume a complete word (identifier)
100
+ */
101
+ function consumeWord(line: string, start: number): string {
102
+ let i = start;
103
+ while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
104
+ i++;
105
+ }
106
+ return line.substring(start, i);
107
+ }
108
+
109
+ /**
110
+ * Consume a complete number
111
+ */
112
+ function consumeNumber(line: string, start: number): string {
113
+ let i = start;
114
+ let hasDot = false;
115
+
116
+ while (i < line.length) {
117
+ const char = line[i];
118
+
119
+ if (/\d/.test(char)) {
120
+ i++;
121
+ } else if (char === '.' && !hasDot) {
122
+ hasDot = true;
123
+ i++;
124
+ } else {
125
+ break;
126
+ }
127
+ }
128
+
129
+ return line.substring(start, i);
130
+ }
131
+
82
132
  /**
83
133
  * Render a parsed line
84
134
  */
@@ -91,7 +141,7 @@ export function renderLineWithTokens(parsedLine: ParsedLine): string {
91
141
  */
92
142
  export function getSyntaxHighlightCSS(): string {
93
143
  return `
94
- /* Simple Syntax Highlighting */
144
+ /* Simple Code Highlighting */
95
145
  .jux-code {
96
146
  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
97
147
  font-size: 14px;
@@ -145,10 +195,13 @@ export function getSyntaxHighlightCSS(): string {
145
195
  display: none;
146
196
  }
147
197
 
148
- /* Token colors - JUST KEYWORDS */
198
+ /* Token colors */
149
199
  .token-keyword {
150
200
  color: #c678dd;
151
201
  font-weight: 600;
202
+ }
203
+ .token-number {
204
+ color: #d19a66;
152
205
  }
153
206
  `;
154
207
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.55",
3
+ "version": "1.1.57",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",