tjs-lang 0.2.7 → 0.3.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/demo/docs.json +32 -26
- package/demo/src/examples.ts +23 -83
- package/demo/src/playground-shared.ts +666 -0
- package/demo/src/tjs-playground.ts +65 -550
- package/demo/src/ts-examples.ts +5 -4
- package/demo/src/ts-playground.ts +50 -414
- package/dist/index.js +143 -160
- package/dist/index.js.map +12 -12
- package/dist/src/lang/emitters/js.d.ts +34 -2
- package/dist/src/lang/index.d.ts +1 -1
- package/dist/src/lang/types.d.ts +1 -1
- package/dist/src/types/Type.d.ts +3 -1
- package/dist/tjs-full.js +143 -160
- package/dist/tjs-full.js.map +12 -12
- package/dist/tjs-transpiler.js +122 -55
- package/dist/tjs-transpiler.js.map +9 -8
- package/dist/tjs-vm.js +14 -14
- package/dist/tjs-vm.js.map +5 -5
- package/docs/docs.json +792 -0
- package/docs/index.js +2652 -2835
- package/docs/index.js.map +11 -10
- package/editors/codemirror/ajs-language.ts +27 -1
- package/editors/codemirror/autocomplete.test.ts +3 -3
- package/package.json +1 -1
- package/src/lang/codegen.test.ts +11 -11
- package/src/lang/emitters/from-ts.ts +1 -1
- package/src/lang/emitters/js.ts +228 -4
- package/src/lang/index.ts +0 -3
- package/src/lang/inference.ts +40 -8
- package/src/lang/lang.test.ts +192 -35
- package/src/lang/roundtrip.test.ts +155 -0
- package/src/lang/runtime.ts +7 -0
- package/src/lang/types.ts +2 -0
- package/src/lang/typescript-syntax.test.ts +6 -4
- package/src/lang/wasm.test.ts +20 -0
- package/src/lang/wasm.ts +143 -0
- package/src/types/Type.test.ts +64 -0
- package/src/types/Type.ts +22 -1
- package/src/use-cases/transpiler-integration.test.ts +10 -10
- package/src/vm/atoms/batteries.ts +2 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for TJS and TS playgrounds
|
|
3
|
+
*
|
|
4
|
+
* Extracted from duplicated code in tjs-playground.ts and ts-playground.ts.
|
|
5
|
+
* Uses composition (exported functions), not inheritance.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CodeMirror } from '../../editors/codemirror/component'
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// TJS Runtime Stub (injected into iframe <script>)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/** The globalThis.__tjs runtime stub for iframe execution. Must stay in sync with src/lang/runtime.ts */
|
|
15
|
+
export const TJS_RUNTIME_STUB = `
|
|
16
|
+
globalThis.__tjs = {
|
|
17
|
+
version: '0.0.0',
|
|
18
|
+
pushStack: () => {},
|
|
19
|
+
popStack: () => {},
|
|
20
|
+
getStack: () => [],
|
|
21
|
+
typeError: (path, expected, value) => {
|
|
22
|
+
const actual = value === null ? 'null' : typeof value;
|
|
23
|
+
const err = new Error(\\\`Expected \\\${expected} for '\\\${path}', got \\\${actual}\\\`);
|
|
24
|
+
err.name = 'MonadicError';
|
|
25
|
+
err.path = path;
|
|
26
|
+
err.expected = expected;
|
|
27
|
+
err.actual = actual;
|
|
28
|
+
return err;
|
|
29
|
+
},
|
|
30
|
+
createRuntime: function() { return this; },
|
|
31
|
+
Is: (a, b) => {
|
|
32
|
+
if (a === b) return true;
|
|
33
|
+
if (a === null || b === null) return a === b;
|
|
34
|
+
if (typeof a !== typeof b) return false;
|
|
35
|
+
if (typeof a !== 'object') return false;
|
|
36
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
37
|
+
if (a.length !== b.length) return false;
|
|
38
|
+
return a.every((v, i) => globalThis.__tjs.Is(v, b[i]));
|
|
39
|
+
}
|
|
40
|
+
const keysA = Object.keys(a);
|
|
41
|
+
const keysB = Object.keys(b);
|
|
42
|
+
if (keysA.length !== keysB.length) return false;
|
|
43
|
+
return keysA.every(k => globalThis.__tjs.Is(a[k], b[k]));
|
|
44
|
+
},
|
|
45
|
+
IsNot: (a, b) => !globalThis.__tjs.Is(a, b),
|
|
46
|
+
};`
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Console capture script (injected into iframe <script>)
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/** Console.log capture that posts messages to parent. Handles objects with try/catch. */
|
|
53
|
+
export const CONSOLE_CAPTURE_SCRIPT = `
|
|
54
|
+
const _log = console.log;
|
|
55
|
+
console.log = (...args) => {
|
|
56
|
+
_log(...args);
|
|
57
|
+
parent.postMessage({ type: 'console', message: args.map(a => {
|
|
58
|
+
if (typeof a !== 'object' || a === null) return String(a);
|
|
59
|
+
try {
|
|
60
|
+
return JSON.stringify(a, null, 2);
|
|
61
|
+
} catch {
|
|
62
|
+
return String(a);
|
|
63
|
+
}
|
|
64
|
+
}).join(' ') }, '*');
|
|
65
|
+
};`
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Iframe document builder
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
export interface IframeDocOptions {
|
|
72
|
+
cssContent: string
|
|
73
|
+
htmlContent: string
|
|
74
|
+
importMapScript: string
|
|
75
|
+
jsCode: string
|
|
76
|
+
/** Import statements extracted from code (TJS separates these) */
|
|
77
|
+
importStatements?: string[]
|
|
78
|
+
/** Expose parent.run/runAgent/getIdToken in iframe */
|
|
79
|
+
parentBindings?: boolean
|
|
80
|
+
/** Auto-find and call TJS-annotated functions, append DOM results */
|
|
81
|
+
autoCallTjsFunction?: boolean
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Build the iframe HTML document for code execution.
|
|
86
|
+
* Includes TJS runtime stub, console capture, DOM content detection,
|
|
87
|
+
* execution timing, and error boundary.
|
|
88
|
+
*/
|
|
89
|
+
export function buildIframeDoc(options: IframeDocOptions): string {
|
|
90
|
+
const {
|
|
91
|
+
cssContent,
|
|
92
|
+
htmlContent,
|
|
93
|
+
importMapScript,
|
|
94
|
+
jsCode,
|
|
95
|
+
importStatements = [],
|
|
96
|
+
parentBindings = false,
|
|
97
|
+
autoCallTjsFunction = false,
|
|
98
|
+
} = options
|
|
99
|
+
|
|
100
|
+
const parentBindingsScript = parentBindings
|
|
101
|
+
? `
|
|
102
|
+
if (parent.run) window.run = parent.run.bind(parent);
|
|
103
|
+
if (parent.runAgent) window.runAgent = parent.runAgent.bind(parent);
|
|
104
|
+
if (parent.getIdToken) window.getIdToken = parent.getIdToken.bind(parent);`
|
|
105
|
+
: ''
|
|
106
|
+
|
|
107
|
+
// When imports are separated, use two script blocks:
|
|
108
|
+
// 1. Regular script for runtime stub (must execute before module imports)
|
|
109
|
+
// 2. Module script for imports + code
|
|
110
|
+
const useSeparateScripts = importStatements.length > 0
|
|
111
|
+
|
|
112
|
+
const executionCode = autoCallTjsFunction
|
|
113
|
+
? `
|
|
114
|
+
const __execStart = performance.now();
|
|
115
|
+
${jsCode}
|
|
116
|
+
|
|
117
|
+
// Try to call the function if it exists and show result
|
|
118
|
+
const funcName = Object.keys(window).find(k => {
|
|
119
|
+
try { return typeof window[k] === 'function' && window[k].__tjs; }
|
|
120
|
+
catch { return false; }
|
|
121
|
+
});
|
|
122
|
+
if (funcName) {
|
|
123
|
+
const __callStart = performance.now();
|
|
124
|
+
const result = window[funcName]();
|
|
125
|
+
const __execTime = performance.now() - __callStart;
|
|
126
|
+
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
|
|
127
|
+
if (result !== undefined) {
|
|
128
|
+
if (result instanceof Node) {
|
|
129
|
+
document.body.append(result);
|
|
130
|
+
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
131
|
+
} else {
|
|
132
|
+
console.log('Result:', result);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const __execTime = performance.now() - __execStart;
|
|
137
|
+
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
|
|
138
|
+
}`
|
|
139
|
+
: `
|
|
140
|
+
const __execStart = performance.now();
|
|
141
|
+
${jsCode}
|
|
142
|
+
const __execTime = performance.now() - __execStart;
|
|
143
|
+
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');`
|
|
144
|
+
|
|
145
|
+
if (useSeparateScripts) {
|
|
146
|
+
return `<!DOCTYPE html>
|
|
147
|
+
<html>
|
|
148
|
+
<head>
|
|
149
|
+
<style>${cssContent}</style>
|
|
150
|
+
${importMapScript}
|
|
151
|
+
</head>
|
|
152
|
+
<body>
|
|
153
|
+
${htmlContent}
|
|
154
|
+
<script>${parentBindingsScript}
|
|
155
|
+
${TJS_RUNTIME_STUB}
|
|
156
|
+
</script>
|
|
157
|
+
<script type="module">
|
|
158
|
+
${importStatements.join('\n ')}
|
|
159
|
+
${CONSOLE_CAPTURE_SCRIPT}
|
|
160
|
+
|
|
161
|
+
const __childrenBefore = document.body.children.length;
|
|
162
|
+
try {${executionCode}
|
|
163
|
+
if (document.body.children.length > __childrenBefore) {
|
|
164
|
+
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
165
|
+
}
|
|
166
|
+
} catch (e) {
|
|
167
|
+
parent.postMessage({ type: 'error', message: e.message }, '*');
|
|
168
|
+
}
|
|
169
|
+
</script>
|
|
170
|
+
</body>
|
|
171
|
+
</html>`
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return `<!DOCTYPE html>
|
|
175
|
+
<html>
|
|
176
|
+
<head>
|
|
177
|
+
<style>${cssContent}</style>
|
|
178
|
+
${importMapScript}
|
|
179
|
+
</head>
|
|
180
|
+
<body>
|
|
181
|
+
${htmlContent}
|
|
182
|
+
<script type="module">${parentBindingsScript}
|
|
183
|
+
${TJS_RUNTIME_STUB}
|
|
184
|
+
${CONSOLE_CAPTURE_SCRIPT}
|
|
185
|
+
|
|
186
|
+
const __childrenBefore = document.body.children.length;
|
|
187
|
+
try {${executionCode}
|
|
188
|
+
if (document.body.children.length > __childrenBefore) {
|
|
189
|
+
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
190
|
+
}
|
|
191
|
+
} catch (e) {
|
|
192
|
+
parent.postMessage({ type: 'error', message: e.message }, '*');
|
|
193
|
+
}
|
|
194
|
+
</script>
|
|
195
|
+
</body>
|
|
196
|
+
</html>`
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Iframe message handler
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
export interface IframeMessageCallbacks {
|
|
204
|
+
onConsole: (message: string) => void
|
|
205
|
+
onTiming: (execTime: number) => void
|
|
206
|
+
onPreviewContent: () => void
|
|
207
|
+
onError: (message: string) => void
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create a message event handler for iframe postMessage communication.
|
|
212
|
+
* Returns the handler function (caller is responsible for addEventListener/removeEventListener).
|
|
213
|
+
*/
|
|
214
|
+
export function createIframeMessageHandler(
|
|
215
|
+
callbacks: IframeMessageCallbacks
|
|
216
|
+
): (event: MessageEvent) => void {
|
|
217
|
+
return (event: MessageEvent) => {
|
|
218
|
+
if (event.data?.type === 'console') {
|
|
219
|
+
callbacks.onConsole(event.data.message)
|
|
220
|
+
} else if (event.data?.type === 'timing') {
|
|
221
|
+
callbacks.onTiming(event.data.execTime)
|
|
222
|
+
} else if (event.data?.type === 'hasPreviewContent') {
|
|
223
|
+
callbacks.onPreviewContent()
|
|
224
|
+
} else if (event.data?.type === 'error') {
|
|
225
|
+
callbacks.onError(event.data.message)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Console rendering
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Render console messages with clickable line references.
|
|
236
|
+
* Parses patterns like "at line X", "line X:", "Line X", ":X:Y" (line:col).
|
|
237
|
+
*/
|
|
238
|
+
export function renderConsoleMessages(
|
|
239
|
+
messages: string[],
|
|
240
|
+
consoleEl: HTMLElement,
|
|
241
|
+
goToLine: (line: number, col: number) => void
|
|
242
|
+
): void {
|
|
243
|
+
const linePattern =
|
|
244
|
+
/(?:at line |line |Line )(\d+)(?:[:,]?\s*(?:column |col )?(\d+))?|:(\d+):(\d+)/g
|
|
245
|
+
|
|
246
|
+
const html = messages
|
|
247
|
+
.map((msg) => {
|
|
248
|
+
const escaped = msg
|
|
249
|
+
.replace(/&/g, '&')
|
|
250
|
+
.replace(/</g, '<')
|
|
251
|
+
.replace(/>/g, '>')
|
|
252
|
+
|
|
253
|
+
return escaped.replace(linePattern, (match, l1, c1, l2, c2) => {
|
|
254
|
+
const line = l1 || l2
|
|
255
|
+
const col = c1 || c2 || '1'
|
|
256
|
+
return `<span class="clickable-line" data-line="${line}" data-col="${col}">${match}</span>`
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
.join('\n')
|
|
260
|
+
|
|
261
|
+
consoleEl.innerHTML = html
|
|
262
|
+
consoleEl.scrollTop = consoleEl.scrollHeight
|
|
263
|
+
|
|
264
|
+
consoleEl.querySelectorAll('.clickable-line').forEach((el) => {
|
|
265
|
+
el.addEventListener('click', (e) => {
|
|
266
|
+
const target = e.currentTarget as HTMLElement
|
|
267
|
+
const line = parseInt(target.dataset.line || '0', 10)
|
|
268
|
+
const col = parseInt(target.dataset.col || '1', 10)
|
|
269
|
+
if (line > 0) {
|
|
270
|
+
goToLine(line, col)
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Test results rendering
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Render test results HTML with clickable error links and editor gutter markers.
|
|
282
|
+
* Returns pass/fail counts so callers can update tab indicators.
|
|
283
|
+
*/
|
|
284
|
+
export function renderTestResults(
|
|
285
|
+
tests: any[],
|
|
286
|
+
outputEl: HTMLElement,
|
|
287
|
+
editor: CodeMirror,
|
|
288
|
+
goToLine: (line: number) => void
|
|
289
|
+
): { passed: number; failed: number } {
|
|
290
|
+
if (!tests || tests.length === 0) {
|
|
291
|
+
outputEl.textContent = 'No tests defined'
|
|
292
|
+
editor.clearMarkers()
|
|
293
|
+
return { passed: 0, failed: 0 }
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const passed = tests.filter((t: any) => t.passed).length
|
|
297
|
+
const failed = tests.filter((t: any) => !t.passed).length
|
|
298
|
+
|
|
299
|
+
// Set gutter markers for failed tests
|
|
300
|
+
const failedTests = tests.filter((t: any) => !t.passed && t.line)
|
|
301
|
+
if (failedTests.length > 0) {
|
|
302
|
+
editor.setMarkers(
|
|
303
|
+
failedTests.map((t: any) => ({
|
|
304
|
+
line: t.line,
|
|
305
|
+
message: t.error || t.description,
|
|
306
|
+
severity: 'error' as const,
|
|
307
|
+
}))
|
|
308
|
+
)
|
|
309
|
+
} else {
|
|
310
|
+
editor.clearMarkers()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let html = `<div class="test-summary">`
|
|
314
|
+
html += `<strong>${passed} passed</strong>`
|
|
315
|
+
if (failed > 0) {
|
|
316
|
+
html += `, <strong class="test-failed">${failed} failed</strong>`
|
|
317
|
+
}
|
|
318
|
+
html += `</div><ul class="test-list">`
|
|
319
|
+
|
|
320
|
+
for (const test of tests) {
|
|
321
|
+
const icon = test.passed ? '✓' : '✗'
|
|
322
|
+
const cls = test.passed ? 'test-pass' : 'test-fail'
|
|
323
|
+
const sigBadge = test.isSignatureTest
|
|
324
|
+
? ' <span class="sig-badge">signature</span>'
|
|
325
|
+
: ''
|
|
326
|
+
const dataLine = test.line ? ` data-line="${test.line}"` : ''
|
|
327
|
+
html += `<li class="${cls}"${dataLine}>${icon} ${test.description}${sigBadge}`
|
|
328
|
+
if (!test.passed && test.error) {
|
|
329
|
+
html += `<div class="test-error${
|
|
330
|
+
test.line ? ' clickable-error' : ''
|
|
331
|
+
}"${dataLine}>${test.error}</div>`
|
|
332
|
+
}
|
|
333
|
+
html += `</li>`
|
|
334
|
+
}
|
|
335
|
+
html += `</ul>`
|
|
336
|
+
|
|
337
|
+
outputEl.innerHTML = html
|
|
338
|
+
|
|
339
|
+
// Add click handlers for clickable errors
|
|
340
|
+
outputEl.querySelectorAll('.clickable-error').forEach((el) => {
|
|
341
|
+
el.addEventListener('click', (e) => {
|
|
342
|
+
const line = parseInt(
|
|
343
|
+
(e.currentTarget as HTMLElement).dataset.line || '0',
|
|
344
|
+
10
|
|
345
|
+
)
|
|
346
|
+
if (line > 0) {
|
|
347
|
+
goToLine(line)
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
return { passed, failed }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
// Formatting utilities
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
|
|
359
|
+
/** Format execution time as human-readable string (μs for <1ms, ms otherwise) */
|
|
360
|
+
export function formatExecTime(ms: number): string {
|
|
361
|
+
return ms < 1 ? `${(ms * 1000).toFixed(0)}μs` : `${ms.toFixed(2)}ms`
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// Shared CSS styles
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Shared playground CSS styles. Spread into each playground's styleSpec.
|
|
370
|
+
*
|
|
371
|
+
* Class names that differ between playgrounds (e.g. .tjs-toolbar vs .ts-toolbar,
|
|
372
|
+
* .tjs-main vs .ts-main) are NOT included here - those stay in each playground.
|
|
373
|
+
*/
|
|
374
|
+
export const sharedPlaygroundStyles: Record<string, Record<string, string>> = {
|
|
375
|
+
':host': {
|
|
376
|
+
display: 'flex',
|
|
377
|
+
flexDirection: 'column',
|
|
378
|
+
height: '100%',
|
|
379
|
+
flex: '1 1 auto',
|
|
380
|
+
background: 'var(--background, #fff)',
|
|
381
|
+
color: 'var(--text-color, #1f2937)',
|
|
382
|
+
fontFamily: 'system-ui, sans-serif',
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
':host .run-btn': {
|
|
386
|
+
display: 'flex',
|
|
387
|
+
alignItems: 'center',
|
|
388
|
+
gap: '4px',
|
|
389
|
+
padding: '6px 12px',
|
|
390
|
+
background: 'var(--brand-color, #3d4a6b)',
|
|
391
|
+
color: 'var(--brand-text-color, white)',
|
|
392
|
+
border: 'none',
|
|
393
|
+
borderRadius: '6px',
|
|
394
|
+
cursor: 'pointer',
|
|
395
|
+
fontWeight: '500',
|
|
396
|
+
fontSize: '14px',
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
':host .run-btn:hover:not(:disabled)': {
|
|
400
|
+
filter: 'brightness(1.1)',
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
':host .run-btn:disabled': {
|
|
404
|
+
opacity: '0.6',
|
|
405
|
+
cursor: 'not-allowed',
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
':host .toolbar-separator': {
|
|
409
|
+
width: '1px',
|
|
410
|
+
height: '20px',
|
|
411
|
+
background: 'var(--code-border, #d1d5db)',
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
':host .build-flags': {
|
|
415
|
+
display: 'flex',
|
|
416
|
+
alignItems: 'center',
|
|
417
|
+
gap: '12px',
|
|
418
|
+
},
|
|
419
|
+
|
|
420
|
+
':host .flag-label': {
|
|
421
|
+
display: 'flex',
|
|
422
|
+
alignItems: 'center',
|
|
423
|
+
gap: '4px',
|
|
424
|
+
fontSize: '13px',
|
|
425
|
+
color: 'var(--text-color, #6b7280)',
|
|
426
|
+
cursor: 'pointer',
|
|
427
|
+
userSelect: 'none',
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
':host .flag-label:hover': {
|
|
431
|
+
color: 'var(--text-color, #374151)',
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
':host .flag-label input[type="checkbox"]': {
|
|
435
|
+
margin: '0',
|
|
436
|
+
cursor: 'pointer',
|
|
437
|
+
accentColor: 'var(--brand-color, #3d4a6b)',
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
':host .revert-btn': {
|
|
441
|
+
display: 'flex',
|
|
442
|
+
alignItems: 'center',
|
|
443
|
+
gap: '4px',
|
|
444
|
+
padding: '6px 12px',
|
|
445
|
+
background: 'var(--code-background, #e5e7eb)',
|
|
446
|
+
color: 'var(--text-color, #374151)',
|
|
447
|
+
border: '1px solid var(--code-border, #d1d5db)',
|
|
448
|
+
borderRadius: '6px',
|
|
449
|
+
cursor: 'pointer',
|
|
450
|
+
fontWeight: '500',
|
|
451
|
+
fontSize: '14px',
|
|
452
|
+
transition: 'opacity 0.2s',
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
':host .revert-btn:hover:not(:disabled)': {
|
|
456
|
+
background: '#fef3c7',
|
|
457
|
+
borderColor: '#f59e0b',
|
|
458
|
+
color: '#92400e',
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
':host .revert-btn:disabled': {
|
|
462
|
+
cursor: 'default',
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
':host .elastic': {
|
|
466
|
+
flex: '1',
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
':host .status-bar': {
|
|
470
|
+
fontSize: '13px',
|
|
471
|
+
color: 'var(--text-color, #6b7280)',
|
|
472
|
+
opacity: '0.7',
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
':host .status-bar.error': {
|
|
476
|
+
color: '#dc2626',
|
|
477
|
+
opacity: '1',
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
':host tosi-tabs > [name]': {
|
|
481
|
+
background: 'var(--background, #fff)',
|
|
482
|
+
color: 'var(--text-color, #1f2937)',
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
':host .editor-wrapper': {
|
|
486
|
+
flex: '1 1 auto',
|
|
487
|
+
height: '100%',
|
|
488
|
+
minHeight: '300px',
|
|
489
|
+
position: 'relative',
|
|
490
|
+
overflow: 'hidden',
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
':host .editor-wrapper code-mirror': {
|
|
494
|
+
display: 'block',
|
|
495
|
+
position: 'absolute',
|
|
496
|
+
top: '0',
|
|
497
|
+
left: '0',
|
|
498
|
+
right: '0',
|
|
499
|
+
bottom: '0',
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
':host .preview-frame': {
|
|
503
|
+
width: '100%',
|
|
504
|
+
height: '100%',
|
|
505
|
+
border: 'none',
|
|
506
|
+
background: 'var(--background, #fff)',
|
|
507
|
+
},
|
|
508
|
+
|
|
509
|
+
':host .docs-output': {
|
|
510
|
+
display: 'block',
|
|
511
|
+
padding: '12px 16px',
|
|
512
|
+
fontSize: '14px',
|
|
513
|
+
fontFamily: 'system-ui, sans-serif',
|
|
514
|
+
color: 'var(--text-color, inherit)',
|
|
515
|
+
background: 'var(--background, #fff)',
|
|
516
|
+
height: '100%',
|
|
517
|
+
overflow: 'auto',
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
':host .docs-output h2': {
|
|
521
|
+
fontSize: '1.25em',
|
|
522
|
+
marginTop: '0',
|
|
523
|
+
marginBottom: '0.5em',
|
|
524
|
+
color: 'var(--text-color, #1f2937)',
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
':host .docs-output pre': {
|
|
528
|
+
background: 'var(--code-background, #f3f4f6)',
|
|
529
|
+
padding: '8px 12px',
|
|
530
|
+
borderRadius: '6px',
|
|
531
|
+
overflow: 'auto',
|
|
532
|
+
fontSize: '13px',
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
':host .docs-output code': {
|
|
536
|
+
fontFamily: 'ui-monospace, monospace',
|
|
537
|
+
fontSize: '0.9em',
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
':host .docs-output p': {
|
|
541
|
+
margin: '0.75em 0',
|
|
542
|
+
lineHeight: '1.5',
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
':host .docs-output h3': {
|
|
546
|
+
fontSize: '1em',
|
|
547
|
+
marginTop: '1em',
|
|
548
|
+
marginBottom: '0.5em',
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
':host .docs-output ul': {
|
|
552
|
+
paddingLeft: '1.5em',
|
|
553
|
+
margin: '0.5em 0',
|
|
554
|
+
},
|
|
555
|
+
|
|
556
|
+
':host .docs-output li': {
|
|
557
|
+
marginBottom: '0.25em',
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
':host .docs-output hr': {
|
|
561
|
+
border: 'none',
|
|
562
|
+
borderTop: '1px solid var(--code-border, #e5e7eb)',
|
|
563
|
+
margin: '1.5em 0',
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
':host .tests-output': {
|
|
567
|
+
padding: '12px',
|
|
568
|
+
fontSize: '14px',
|
|
569
|
+
fontFamily: 'system-ui, sans-serif',
|
|
570
|
+
color: 'var(--text-color, inherit)',
|
|
571
|
+
background: 'var(--background, #fff)',
|
|
572
|
+
height: '100%',
|
|
573
|
+
overflow: 'auto',
|
|
574
|
+
},
|
|
575
|
+
|
|
576
|
+
':host .test-summary': {
|
|
577
|
+
marginBottom: '12px',
|
|
578
|
+
paddingBottom: '8px',
|
|
579
|
+
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
':host .test-failed': {
|
|
583
|
+
color: '#dc2626',
|
|
584
|
+
},
|
|
585
|
+
|
|
586
|
+
':host .test-list': {
|
|
587
|
+
listStyle: 'none',
|
|
588
|
+
padding: '0',
|
|
589
|
+
margin: '0',
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
':host .test-list li': {
|
|
593
|
+
padding: '4px 0',
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
':host .test-pass': {
|
|
597
|
+
color: '#16a34a',
|
|
598
|
+
},
|
|
599
|
+
|
|
600
|
+
':host .test-fail': {
|
|
601
|
+
color: '#dc2626',
|
|
602
|
+
},
|
|
603
|
+
|
|
604
|
+
':host .test-error': {
|
|
605
|
+
marginLeft: '20px',
|
|
606
|
+
marginTop: '4px',
|
|
607
|
+
padding: '8px',
|
|
608
|
+
background: 'rgba(220, 38, 38, 0.1)',
|
|
609
|
+
borderRadius: '4px',
|
|
610
|
+
fontSize: '13px',
|
|
611
|
+
fontFamily: 'var(--font-mono, monospace)',
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
':host .clickable-error': {
|
|
615
|
+
cursor: 'pointer',
|
|
616
|
+
textDecoration: 'underline',
|
|
617
|
+
textDecorationStyle: 'dotted',
|
|
618
|
+
},
|
|
619
|
+
|
|
620
|
+
':host .clickable-error:hover': {
|
|
621
|
+
background: 'rgba(220, 38, 38, 0.2)',
|
|
622
|
+
},
|
|
623
|
+
|
|
624
|
+
':host .sig-badge': {
|
|
625
|
+
fontSize: '11px',
|
|
626
|
+
padding: '2px 6px',
|
|
627
|
+
marginLeft: '8px',
|
|
628
|
+
background: 'rgba(99, 102, 241, 0.1)',
|
|
629
|
+
color: '#6366f1',
|
|
630
|
+
borderRadius: '4px',
|
|
631
|
+
},
|
|
632
|
+
|
|
633
|
+
':host .console-header': {
|
|
634
|
+
padding: '4px 12px',
|
|
635
|
+
background: 'var(--code-background, #f3f4f6)',
|
|
636
|
+
fontSize: '12px',
|
|
637
|
+
fontWeight: '500',
|
|
638
|
+
color: 'var(--text-color, #6b7280)',
|
|
639
|
+
opacity: '0.7',
|
|
640
|
+
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
641
|
+
},
|
|
642
|
+
|
|
643
|
+
':host .console-output': {
|
|
644
|
+
flex: '1',
|
|
645
|
+
margin: '0',
|
|
646
|
+
padding: '8px 12px',
|
|
647
|
+
background: 'var(--code-background, #f3f4f6)',
|
|
648
|
+
color: 'var(--text-color, #1f2937)',
|
|
649
|
+
fontSize: '12px',
|
|
650
|
+
fontFamily: 'ui-monospace, monospace',
|
|
651
|
+
overflow: 'auto',
|
|
652
|
+
whiteSpace: 'pre-wrap',
|
|
653
|
+
},
|
|
654
|
+
|
|
655
|
+
':host .clickable-line': {
|
|
656
|
+
cursor: 'pointer',
|
|
657
|
+
color: '#2563eb',
|
|
658
|
+
textDecoration: 'underline',
|
|
659
|
+
textDecorationStyle: 'dotted',
|
|
660
|
+
},
|
|
661
|
+
|
|
662
|
+
':host .clickable-line:hover': {
|
|
663
|
+
color: '#1d4ed8',
|
|
664
|
+
background: 'rgba(37, 99, 235, 0.1)',
|
|
665
|
+
},
|
|
666
|
+
}
|