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
|
@@ -23,14 +23,17 @@ import {
|
|
|
23
23
|
// Available modules for autocomplete introspection
|
|
24
24
|
// These are the actual runtime values that can be introspected
|
|
25
25
|
import { codeMirror, CodeMirror } from '../../editors/codemirror/component'
|
|
26
|
-
import {
|
|
27
|
-
tjs,
|
|
28
|
-
compileWasmBlocksForIframe,
|
|
29
|
-
generateWasmInstantiationCode,
|
|
30
|
-
type TJSTranspileOptions,
|
|
31
|
-
} from '../../src/lang'
|
|
26
|
+
import { tjs, type TJSTranspileOptions } from '../../src/lang'
|
|
32
27
|
import { generateDocsMarkdown } from './docs-utils'
|
|
33
28
|
import { extractImports, generateImportMap, resolveImports } from './imports'
|
|
29
|
+
import {
|
|
30
|
+
buildIframeDoc,
|
|
31
|
+
createIframeMessageHandler,
|
|
32
|
+
renderConsoleMessages,
|
|
33
|
+
renderTestResults,
|
|
34
|
+
formatExecTime,
|
|
35
|
+
sharedPlaygroundStyles,
|
|
36
|
+
} from './playground-shared'
|
|
34
37
|
import { ModuleStore, type ValidationResult } from './module-store'
|
|
35
38
|
import {
|
|
36
39
|
buildAutocompleteContext,
|
|
@@ -443,42 +446,11 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
443
446
|
}
|
|
444
447
|
|
|
445
448
|
private renderConsole() {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const html = this.consoleMessages
|
|
452
|
-
.map((msg) => {
|
|
453
|
-
// Escape HTML
|
|
454
|
-
const escaped = msg
|
|
455
|
-
.replace(/&/g, '&')
|
|
456
|
-
.replace(/</g, '<')
|
|
457
|
-
.replace(/>/g, '>')
|
|
458
|
-
|
|
459
|
-
// Replace line references with clickable spans
|
|
460
|
-
return escaped.replace(linePattern, (match, l1, c1, l2, c2) => {
|
|
461
|
-
const line = l1 || l2
|
|
462
|
-
const col = c1 || c2 || '1'
|
|
463
|
-
return `<span class="clickable-line" data-line="${line}" data-col="${col}">${match}</span>`
|
|
464
|
-
})
|
|
465
|
-
})
|
|
466
|
-
.join('\n')
|
|
467
|
-
|
|
468
|
-
this.parts.console.innerHTML = html
|
|
469
|
-
this.parts.console.scrollTop = this.parts.console.scrollHeight
|
|
470
|
-
|
|
471
|
-
// Add click handlers
|
|
472
|
-
this.parts.console.querySelectorAll('.clickable-line').forEach((el) => {
|
|
473
|
-
el.addEventListener('click', (e) => {
|
|
474
|
-
const target = e.currentTarget as HTMLElement
|
|
475
|
-
const line = parseInt(target.dataset.line || '0', 10)
|
|
476
|
-
const col = parseInt(target.dataset.col || '1', 10)
|
|
477
|
-
if (line > 0) {
|
|
478
|
-
this.goToSourceLine(line, col)
|
|
479
|
-
}
|
|
480
|
-
})
|
|
481
|
-
})
|
|
449
|
+
renderConsoleMessages(
|
|
450
|
+
this.consoleMessages,
|
|
451
|
+
this.parts.console,
|
|
452
|
+
(line, col) => this.goToSourceLine(line, col)
|
|
453
|
+
)
|
|
482
454
|
}
|
|
483
455
|
|
|
484
456
|
// Build flag toggle handlers
|
|
@@ -553,10 +525,7 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
553
525
|
// Update test results and status bar with timing
|
|
554
526
|
const tests = result.testResults || []
|
|
555
527
|
const failed = tests.filter((t: any) => !t.passed).length
|
|
556
|
-
const timeStr =
|
|
557
|
-
this.lastTranspileTime < 1
|
|
558
|
-
? `${(this.lastTranspileTime * 1000).toFixed(0)}μs`
|
|
559
|
-
: `${this.lastTranspileTime.toFixed(2)}ms`
|
|
528
|
+
const timeStr = formatExecTime(this.lastTranspileTime)
|
|
560
529
|
if (failed > 0) {
|
|
561
530
|
this.parts.statusBar.textContent = `Transpiled in ${timeStr} with ${failed} test failure${
|
|
562
531
|
failed > 1 ? 's' : ''
|
|
@@ -624,75 +593,13 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
624
593
|
|
|
625
594
|
private updateTestResults(result: any) {
|
|
626
595
|
const tests = result.testResults
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
this.
|
|
630
|
-
this.parts.tjsEditor
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const passed = tests.filter((t: any) => t.passed).length
|
|
635
|
-
const failed = tests.filter((t: any) => !t.passed).length
|
|
636
|
-
|
|
637
|
-
// Update tab label with indicator
|
|
596
|
+
const { passed, failed } = renderTestResults(
|
|
597
|
+
tests,
|
|
598
|
+
this.parts.testsOutput,
|
|
599
|
+
this.parts.tjsEditor,
|
|
600
|
+
(line) => this.goToSourceLine(line)
|
|
601
|
+
)
|
|
638
602
|
this.updateTestsTabLabel(passed, failed)
|
|
639
|
-
|
|
640
|
-
// Set gutter markers for failed tests
|
|
641
|
-
const failedTests = tests.filter((t: any) => !t.passed && t.line)
|
|
642
|
-
if (failedTests.length > 0) {
|
|
643
|
-
this.parts.tjsEditor.setMarkers(
|
|
644
|
-
failedTests.map((t: any) => ({
|
|
645
|
-
line: t.line,
|
|
646
|
-
message: t.error || t.description,
|
|
647
|
-
severity: 'error' as const,
|
|
648
|
-
}))
|
|
649
|
-
)
|
|
650
|
-
} else {
|
|
651
|
-
this.parts.tjsEditor.clearMarkers()
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
let html = `<div class="test-summary">`
|
|
655
|
-
html += `<strong>${passed} passed</strong>`
|
|
656
|
-
if (failed > 0) {
|
|
657
|
-
html += `, <strong class="test-failed">${failed} failed</strong>`
|
|
658
|
-
}
|
|
659
|
-
html += `</div><ul class="test-list">`
|
|
660
|
-
|
|
661
|
-
for (const test of tests) {
|
|
662
|
-
const icon = test.passed ? '✓' : '✗'
|
|
663
|
-
const cls = test.passed ? 'test-pass' : 'test-fail'
|
|
664
|
-
const sigBadge = test.isSignatureTest
|
|
665
|
-
? ' <span class="sig-badge">signature</span>'
|
|
666
|
-
: ''
|
|
667
|
-
const clickable =
|
|
668
|
-
!test.passed && test.line ? ' class="clickable-error"' : ''
|
|
669
|
-
const dataLine = test.line ? ` data-line="${test.line}"` : ''
|
|
670
|
-
html += `<li class="${cls}"${dataLine}>${icon} ${test.description}${sigBadge}`
|
|
671
|
-
if (!test.passed && test.error) {
|
|
672
|
-
html += `<div${clickable}${dataLine} class="test-error${
|
|
673
|
-
test.line ? ' clickable-error' : ''
|
|
674
|
-
}">${test.error}</div>`
|
|
675
|
-
}
|
|
676
|
-
html += `</li>`
|
|
677
|
-
}
|
|
678
|
-
html += `</ul>`
|
|
679
|
-
|
|
680
|
-
this.parts.testsOutput.innerHTML = html
|
|
681
|
-
|
|
682
|
-
// Add click handlers for clickable errors
|
|
683
|
-
this.parts.testsOutput
|
|
684
|
-
.querySelectorAll('.clickable-error')
|
|
685
|
-
.forEach((el) => {
|
|
686
|
-
el.addEventListener('click', (e) => {
|
|
687
|
-
const line = parseInt(
|
|
688
|
-
(e.currentTarget as HTMLElement).dataset.line || '0',
|
|
689
|
-
10
|
|
690
|
-
)
|
|
691
|
-
if (line > 0) {
|
|
692
|
-
this.goToSourceLine(line)
|
|
693
|
-
}
|
|
694
|
-
})
|
|
695
|
-
})
|
|
696
603
|
}
|
|
697
604
|
|
|
698
605
|
private updateTestsTabLabel(passed: number, failed: number) {
|
|
@@ -1057,29 +964,25 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1057
964
|
return
|
|
1058
965
|
}
|
|
1059
966
|
|
|
967
|
+
// Show JS output immediately after successful transpilation
|
|
968
|
+
this.parts.outputTabs.value = 0 // JS is first tab (index 0)
|
|
969
|
+
|
|
1060
970
|
this.parts.statusBar.textContent = 'Running...'
|
|
1061
971
|
|
|
1062
|
-
// Compile WASM blocks if present and WASM is enabled
|
|
1063
|
-
let wasmInstantiationCode = ''
|
|
1064
972
|
try {
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
this.log(`WASM: ${wasmResult.failed} failed (using JS fallback)`)
|
|
973
|
+
// Log WASM compilation results (WASM is now compiled at transpile time)
|
|
974
|
+
const wasmCompiled = this.lastTranspileResult.wasmCompiled
|
|
975
|
+
if (wasmCompiled && wasmCompiled.length > 0) {
|
|
976
|
+
const success = wasmCompiled.filter((w) => w.success).length
|
|
977
|
+
const failed = wasmCompiled.filter((w) => !w.success).length
|
|
978
|
+
if (success > 0) {
|
|
979
|
+
this.log(`WASM: ${success} block(s) compiled at transpile time`)
|
|
980
|
+
}
|
|
981
|
+
if (failed > 0) {
|
|
982
|
+
this.log(`WASM: ${failed} failed (using JS fallback)`)
|
|
983
|
+
for (const w of wasmCompiled.filter((w) => !w.success)) {
|
|
984
|
+
this.log(` ${w.id}: ${w.error}`)
|
|
1078
985
|
}
|
|
1079
|
-
} else {
|
|
1080
|
-
this.log(
|
|
1081
|
-
`WASM disabled - using JS fallback for ${wasmBlocks.length} block(s)`
|
|
1082
|
-
)
|
|
1083
986
|
}
|
|
1084
987
|
}
|
|
1085
988
|
|
|
@@ -1123,134 +1026,33 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1123
1026
|
)
|
|
1124
1027
|
|
|
1125
1028
|
// Create a complete HTML document for the iframe
|
|
1126
|
-
const iframeDoc =
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
<script>
|
|
1136
|
-
// Expose parent's run/runAgent/getIdToken in iframe for playground convenience
|
|
1137
|
-
if (parent.run) window.run = parent.run.bind(parent);
|
|
1138
|
-
if (parent.runAgent) window.runAgent = parent.runAgent.bind(parent);
|
|
1139
|
-
if (parent.getIdToken) window.getIdToken = parent.getIdToken.bind(parent);
|
|
1140
|
-
|
|
1141
|
-
globalThis.__tjs = {
|
|
1142
|
-
version: '0.0.0',
|
|
1143
|
-
pushStack: () => {},
|
|
1144
|
-
popStack: () => {},
|
|
1145
|
-
getStack: () => [],
|
|
1146
|
-
typeError: (path, expected, value) => {
|
|
1147
|
-
const actual = value === null ? 'null' : typeof value;
|
|
1148
|
-
const err = new Error(\`Expected \${expected} for '\${path}', got \${actual}\`);
|
|
1149
|
-
err.name = 'MonadicError';
|
|
1150
|
-
err.path = path;
|
|
1151
|
-
err.expected = expected;
|
|
1152
|
-
err.actual = actual;
|
|
1153
|
-
return err;
|
|
1154
|
-
},
|
|
1155
|
-
createRuntime: function() { return this; },
|
|
1156
|
-
Is: (a, b) => {
|
|
1157
|
-
if (a === b) return true;
|
|
1158
|
-
if (a === null || b === null) return a === b;
|
|
1159
|
-
if (typeof a !== typeof b) return false;
|
|
1160
|
-
if (typeof a !== 'object') return false;
|
|
1161
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1162
|
-
if (a.length !== b.length) return false;
|
|
1163
|
-
return a.every((v, i) => globalThis.__tjs.Is(v, b[i]));
|
|
1164
|
-
}
|
|
1165
|
-
const keysA = Object.keys(a);
|
|
1166
|
-
const keysB = Object.keys(b);
|
|
1167
|
-
if (keysA.length !== keysB.length) return false;
|
|
1168
|
-
return keysA.every(k => globalThis.__tjs.Is(a[k], b[k]));
|
|
1169
|
-
},
|
|
1170
|
-
IsNot: (a, b) => !globalThis.__tjs.Is(a, b),
|
|
1171
|
-
};
|
|
1172
|
-
</script>
|
|
1173
|
-
<script type="module">
|
|
1174
|
-
// Import statements must be at the top of the module
|
|
1175
|
-
${importStatements.join('\n ')}
|
|
1176
|
-
|
|
1177
|
-
// Capture console.log
|
|
1178
|
-
const _log = console.log;
|
|
1179
|
-
console.log = (...args) => {
|
|
1180
|
-
_log(...args);
|
|
1181
|
-
parent.postMessage({ type: 'console', message: args.map(a => {
|
|
1182
|
-
if (typeof a !== 'object' || a === null) return String(a);
|
|
1183
|
-
try {
|
|
1184
|
-
return JSON.stringify(a, null, 2);
|
|
1185
|
-
} catch {
|
|
1186
|
-
return String(a);
|
|
1187
|
-
}
|
|
1188
|
-
}).join(' ') }, '*');
|
|
1189
|
-
};
|
|
1190
|
-
|
|
1191
|
-
try {
|
|
1192
|
-
// Instantiate WASM blocks (compiled in parent, instantiated here)
|
|
1193
|
-
${wasmInstantiationCode}
|
|
1194
|
-
|
|
1195
|
-
const __execStart = performance.now();
|
|
1196
|
-
${codeWithoutImports}
|
|
1197
|
-
|
|
1198
|
-
// Try to call the function if it exists and show result
|
|
1199
|
-
const funcName = Object.keys(window).find(k => {
|
|
1200
|
-
try { return typeof window[k] === 'function' && window[k].__tjs; }
|
|
1201
|
-
catch { return false; }
|
|
1202
|
-
});
|
|
1203
|
-
if (funcName) {
|
|
1204
|
-
const __callStart = performance.now();
|
|
1205
|
-
const result = window[funcName]();
|
|
1206
|
-
const __execTime = performance.now() - __callStart;
|
|
1207
|
-
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
|
|
1208
|
-
if (result !== undefined) {
|
|
1209
|
-
// If result is a DOM node, append it; otherwise log it
|
|
1210
|
-
if (result instanceof Node) {
|
|
1211
|
-
document.body.append(result);
|
|
1212
|
-
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
1213
|
-
} else {
|
|
1214
|
-
console.log('Result:', result);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
} else {
|
|
1218
|
-
// No TJS function found, report total parse/exec time
|
|
1219
|
-
const __execTime = performance.now() - __execStart;
|
|
1220
|
-
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
|
|
1221
|
-
}
|
|
1222
|
-
// Check if body has content after execution
|
|
1223
|
-
if (document.body.children.length > 0) {
|
|
1224
|
-
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
1225
|
-
}
|
|
1226
|
-
} catch (e) {
|
|
1227
|
-
parent.postMessage({ type: 'error', message: e.message }, '*');
|
|
1228
|
-
}
|
|
1229
|
-
</script>
|
|
1230
|
-
</body>
|
|
1231
|
-
</html>`
|
|
1029
|
+
const iframeDoc = buildIframeDoc({
|
|
1030
|
+
cssContent,
|
|
1031
|
+
htmlContent,
|
|
1032
|
+
importMapScript,
|
|
1033
|
+
jsCode: codeWithoutImports,
|
|
1034
|
+
importStatements,
|
|
1035
|
+
parentBindings: true,
|
|
1036
|
+
autoCallTjsFunction: true,
|
|
1037
|
+
})
|
|
1232
1038
|
|
|
1233
1039
|
// Listen for messages from iframe
|
|
1234
|
-
const messageHandler = (
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
? `${(execTime * 1000).toFixed(0)}μs`
|
|
1243
|
-
: `${execTime.toFixed(2)}ms`
|
|
1244
|
-
this.parts.consoleHeader.textContent = `Console — executed in ${execStr}`
|
|
1245
|
-
} else if (event.data?.type === 'hasPreviewContent') {
|
|
1246
|
-
// Switch to Preview tab when content is added
|
|
1040
|
+
const messageHandler = createIframeMessageHandler({
|
|
1041
|
+
onConsole: (message) => this.log(message),
|
|
1042
|
+
onTiming: (execTime) => {
|
|
1043
|
+
this.parts.consoleHeader.textContent = `Console — executed in ${formatExecTime(
|
|
1044
|
+
execTime
|
|
1045
|
+
)}`
|
|
1046
|
+
},
|
|
1047
|
+
onPreviewContent: () => {
|
|
1247
1048
|
this.parts.outputTabs.value = 1 // Preview is second tab (index 1)
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1049
|
+
},
|
|
1050
|
+
onError: (message) => {
|
|
1051
|
+
this.log(`Error: ${message}`)
|
|
1250
1052
|
this.parts.statusBar.textContent = 'Runtime error'
|
|
1251
1053
|
this.parts.statusBar.classList.add('error')
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1054
|
+
},
|
|
1055
|
+
})
|
|
1254
1056
|
window.addEventListener('message', messageHandler)
|
|
1255
1057
|
|
|
1256
1058
|
// Set iframe content using blob URL instead of srcdoc
|
|
@@ -1348,16 +1150,9 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1348
1150
|
export const tjsPlayground = TJSPlayground.elementCreator({
|
|
1349
1151
|
tag: 'tjs-playground',
|
|
1350
1152
|
styleSpec: {
|
|
1351
|
-
|
|
1352
|
-
display: 'flex',
|
|
1353
|
-
flexDirection: 'column',
|
|
1354
|
-
height: '100%',
|
|
1355
|
-
flex: '1 1 auto',
|
|
1356
|
-
background: 'var(--background, #fff)',
|
|
1357
|
-
color: 'var(--text-color, #1f2937)',
|
|
1358
|
-
fontFamily: 'system-ui, sans-serif',
|
|
1359
|
-
},
|
|
1153
|
+
...sharedPlaygroundStyles,
|
|
1360
1154
|
|
|
1155
|
+
// TJS-specific: toolbar
|
|
1361
1156
|
':host .tjs-toolbar': {
|
|
1362
1157
|
display: 'flex',
|
|
1363
1158
|
alignItems: 'center',
|
|
@@ -1367,61 +1162,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1367
1162
|
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
1368
1163
|
},
|
|
1369
1164
|
|
|
1370
|
-
|
|
1371
|
-
display: 'flex',
|
|
1372
|
-
alignItems: 'center',
|
|
1373
|
-
gap: '4px',
|
|
1374
|
-
padding: '6px 12px',
|
|
1375
|
-
background: 'var(--brand-color, #3d4a6b)',
|
|
1376
|
-
color: 'var(--brand-text-color, white)',
|
|
1377
|
-
border: 'none',
|
|
1378
|
-
borderRadius: '6px',
|
|
1379
|
-
cursor: 'pointer',
|
|
1380
|
-
fontWeight: '500',
|
|
1381
|
-
fontSize: '14px',
|
|
1382
|
-
},
|
|
1383
|
-
|
|
1384
|
-
':host .run-btn:hover:not(:disabled)': {
|
|
1385
|
-
filter: 'brightness(1.1)',
|
|
1386
|
-
},
|
|
1387
|
-
|
|
1388
|
-
':host .run-btn:disabled': {
|
|
1389
|
-
opacity: '0.6',
|
|
1390
|
-
cursor: 'not-allowed',
|
|
1391
|
-
},
|
|
1392
|
-
|
|
1393
|
-
':host .toolbar-separator': {
|
|
1394
|
-
width: '1px',
|
|
1395
|
-
height: '20px',
|
|
1396
|
-
background: 'var(--code-border, #d1d5db)',
|
|
1397
|
-
},
|
|
1398
|
-
|
|
1399
|
-
':host .build-flags': {
|
|
1400
|
-
display: 'flex',
|
|
1401
|
-
alignItems: 'center',
|
|
1402
|
-
gap: '12px',
|
|
1403
|
-
},
|
|
1404
|
-
|
|
1405
|
-
':host .flag-label': {
|
|
1406
|
-
display: 'flex',
|
|
1407
|
-
alignItems: 'center',
|
|
1408
|
-
gap: '4px',
|
|
1409
|
-
fontSize: '13px',
|
|
1410
|
-
color: 'var(--text-color, #6b7280)',
|
|
1411
|
-
cursor: 'pointer',
|
|
1412
|
-
userSelect: 'none',
|
|
1413
|
-
},
|
|
1414
|
-
|
|
1415
|
-
':host .flag-label:hover': {
|
|
1416
|
-
color: 'var(--text-color, #374151)',
|
|
1417
|
-
},
|
|
1418
|
-
|
|
1419
|
-
':host .flag-label input[type="checkbox"]': {
|
|
1420
|
-
margin: '0',
|
|
1421
|
-
cursor: 'pointer',
|
|
1422
|
-
accentColor: 'var(--brand-color, #3d4a6b)',
|
|
1423
|
-
},
|
|
1424
|
-
|
|
1165
|
+
// TJS-specific: module name input
|
|
1425
1166
|
':host .module-name-input': {
|
|
1426
1167
|
padding: '6px 10px',
|
|
1427
1168
|
border: '1px solid var(--code-border, #d1d5db)',
|
|
@@ -1444,6 +1185,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1444
1185
|
opacity: '0.6',
|
|
1445
1186
|
},
|
|
1446
1187
|
|
|
1188
|
+
// TJS-specific: save button
|
|
1447
1189
|
':host .save-btn': {
|
|
1448
1190
|
display: 'flex',
|
|
1449
1191
|
alignItems: 'center',
|
|
@@ -1464,46 +1206,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1464
1206
|
borderColor: 'var(--brand-color, #3d4a6b)',
|
|
1465
1207
|
},
|
|
1466
1208
|
|
|
1467
|
-
|
|
1468
|
-
display: 'flex',
|
|
1469
|
-
alignItems: 'center',
|
|
1470
|
-
gap: '4px',
|
|
1471
|
-
padding: '6px 12px',
|
|
1472
|
-
background: 'var(--code-background, #e5e7eb)',
|
|
1473
|
-
color: 'var(--text-color, #374151)',
|
|
1474
|
-
border: '1px solid var(--code-border, #d1d5db)',
|
|
1475
|
-
borderRadius: '6px',
|
|
1476
|
-
cursor: 'pointer',
|
|
1477
|
-
fontWeight: '500',
|
|
1478
|
-
fontSize: '14px',
|
|
1479
|
-
transition: 'opacity 0.2s',
|
|
1480
|
-
},
|
|
1481
|
-
|
|
1482
|
-
':host .revert-btn:hover:not(:disabled)': {
|
|
1483
|
-
background: '#fef3c7',
|
|
1484
|
-
borderColor: '#f59e0b',
|
|
1485
|
-
color: '#92400e',
|
|
1486
|
-
},
|
|
1487
|
-
|
|
1488
|
-
':host .revert-btn:disabled': {
|
|
1489
|
-
cursor: 'default',
|
|
1490
|
-
},
|
|
1491
|
-
|
|
1492
|
-
':host .elastic': {
|
|
1493
|
-
flex: '1',
|
|
1494
|
-
},
|
|
1495
|
-
|
|
1496
|
-
':host .status-bar': {
|
|
1497
|
-
fontSize: '13px',
|
|
1498
|
-
color: 'var(--text-color, #6b7280)',
|
|
1499
|
-
opacity: '0.7',
|
|
1500
|
-
},
|
|
1501
|
-
|
|
1502
|
-
':host .status-bar.error': {
|
|
1503
|
-
color: '#dc2626',
|
|
1504
|
-
opacity: '1',
|
|
1505
|
-
},
|
|
1506
|
-
|
|
1209
|
+
// TJS-specific: layout
|
|
1507
1210
|
':host .tjs-main': {
|
|
1508
1211
|
display: 'flex',
|
|
1509
1212
|
flex: '1 1 auto',
|
|
@@ -1521,31 +1224,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1521
1224
|
overflow: 'hidden',
|
|
1522
1225
|
},
|
|
1523
1226
|
|
|
1524
|
-
//
|
|
1525
|
-
':host tosi-tabs > [name]': {
|
|
1526
|
-
background: 'var(--background, #fff)',
|
|
1527
|
-
color: 'var(--text-color, #1f2937)',
|
|
1528
|
-
},
|
|
1529
|
-
|
|
1530
|
-
// Editor wrapper - contains the shadow DOM code-mirror component
|
|
1531
|
-
':host .editor-wrapper': {
|
|
1532
|
-
flex: '1 1 auto',
|
|
1533
|
-
height: '100%',
|
|
1534
|
-
minHeight: '300px',
|
|
1535
|
-
position: 'relative',
|
|
1536
|
-
overflow: 'hidden',
|
|
1537
|
-
},
|
|
1538
|
-
|
|
1539
|
-
// code-mirror is shadow DOM, so we just size it - internal styles are handled by the component
|
|
1540
|
-
':host .editor-wrapper code-mirror': {
|
|
1541
|
-
display: 'block',
|
|
1542
|
-
position: 'absolute',
|
|
1543
|
-
top: '0',
|
|
1544
|
-
left: '0',
|
|
1545
|
-
right: '0',
|
|
1546
|
-
bottom: '0',
|
|
1547
|
-
},
|
|
1548
|
-
|
|
1227
|
+
// TJS-specific: JS output panel
|
|
1549
1228
|
':host .js-output': {
|
|
1550
1229
|
margin: '0',
|
|
1551
1230
|
padding: '12px',
|
|
@@ -1558,176 +1237,12 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1558
1237
|
whiteSpace: 'pre-wrap',
|
|
1559
1238
|
},
|
|
1560
1239
|
|
|
1561
|
-
|
|
1562
|
-
width: '100%',
|
|
1563
|
-
height: '100%',
|
|
1564
|
-
border: 'none',
|
|
1565
|
-
background: 'var(--background, #fff)',
|
|
1566
|
-
},
|
|
1567
|
-
|
|
1568
|
-
':host .docs-output': {
|
|
1569
|
-
display: 'block',
|
|
1570
|
-
padding: '12px 16px',
|
|
1571
|
-
fontSize: '14px',
|
|
1572
|
-
fontFamily: 'system-ui, sans-serif',
|
|
1573
|
-
color: 'var(--text-color, inherit)',
|
|
1574
|
-
background: 'var(--background, #fff)',
|
|
1575
|
-
height: '100%',
|
|
1576
|
-
overflow: 'auto',
|
|
1577
|
-
},
|
|
1578
|
-
|
|
1579
|
-
':host .docs-output h2': {
|
|
1580
|
-
fontSize: '1.25em',
|
|
1581
|
-
marginTop: '0',
|
|
1582
|
-
marginBottom: '0.5em',
|
|
1583
|
-
color: 'var(--text-color, #1f2937)',
|
|
1584
|
-
},
|
|
1585
|
-
|
|
1586
|
-
':host .docs-output pre': {
|
|
1587
|
-
background: 'var(--code-background, #f3f4f6)',
|
|
1588
|
-
padding: '8px 12px',
|
|
1589
|
-
borderRadius: '6px',
|
|
1590
|
-
overflow: 'auto',
|
|
1591
|
-
fontSize: '13px',
|
|
1592
|
-
},
|
|
1593
|
-
|
|
1594
|
-
':host .docs-output code': {
|
|
1595
|
-
fontFamily: 'ui-monospace, monospace',
|
|
1596
|
-
fontSize: '0.9em',
|
|
1597
|
-
},
|
|
1598
|
-
|
|
1599
|
-
':host .docs-output p': {
|
|
1600
|
-
margin: '0.75em 0',
|
|
1601
|
-
lineHeight: '1.5',
|
|
1602
|
-
},
|
|
1603
|
-
|
|
1604
|
-
':host .docs-output h3': {
|
|
1605
|
-
fontSize: '1em',
|
|
1606
|
-
marginTop: '1em',
|
|
1607
|
-
marginBottom: '0.5em',
|
|
1608
|
-
},
|
|
1609
|
-
|
|
1610
|
-
':host .docs-output ul': {
|
|
1611
|
-
paddingLeft: '1.5em',
|
|
1612
|
-
margin: '0.5em 0',
|
|
1613
|
-
},
|
|
1614
|
-
|
|
1615
|
-
':host .docs-output li': {
|
|
1616
|
-
marginBottom: '0.25em',
|
|
1617
|
-
},
|
|
1618
|
-
|
|
1619
|
-
':host .docs-output hr': {
|
|
1620
|
-
border: 'none',
|
|
1621
|
-
borderTop: '1px solid var(--code-border, #e5e7eb)',
|
|
1622
|
-
margin: '1.5em 0',
|
|
1623
|
-
},
|
|
1624
|
-
|
|
1625
|
-
':host .tests-output': {
|
|
1626
|
-
padding: '12px',
|
|
1627
|
-
fontSize: '14px',
|
|
1628
|
-
fontFamily: 'system-ui, sans-serif',
|
|
1629
|
-
color: 'var(--text-color, inherit)',
|
|
1630
|
-
background: 'var(--background, #fff)',
|
|
1631
|
-
height: '100%',
|
|
1632
|
-
overflow: 'auto',
|
|
1633
|
-
},
|
|
1634
|
-
|
|
1635
|
-
':host .test-summary': {
|
|
1636
|
-
marginBottom: '12px',
|
|
1637
|
-
paddingBottom: '8px',
|
|
1638
|
-
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
1639
|
-
},
|
|
1640
|
-
|
|
1641
|
-
':host .test-failed': {
|
|
1642
|
-
color: '#dc2626',
|
|
1643
|
-
},
|
|
1644
|
-
|
|
1645
|
-
':host .test-list': {
|
|
1646
|
-
listStyle: 'none',
|
|
1647
|
-
padding: 0,
|
|
1648
|
-
margin: 0,
|
|
1649
|
-
},
|
|
1650
|
-
|
|
1651
|
-
':host .test-list li': {
|
|
1652
|
-
padding: '4px 0',
|
|
1653
|
-
},
|
|
1654
|
-
|
|
1655
|
-
':host .test-pass': {
|
|
1656
|
-
color: '#16a34a',
|
|
1657
|
-
},
|
|
1658
|
-
|
|
1659
|
-
':host .test-fail': {
|
|
1660
|
-
color: '#dc2626',
|
|
1661
|
-
},
|
|
1662
|
-
|
|
1663
|
-
':host .test-error': {
|
|
1664
|
-
marginLeft: '20px',
|
|
1665
|
-
marginTop: '4px',
|
|
1666
|
-
padding: '8px',
|
|
1667
|
-
background: 'rgba(220, 38, 38, 0.1)',
|
|
1668
|
-
borderRadius: '4px',
|
|
1669
|
-
fontSize: '13px',
|
|
1670
|
-
fontFamily: 'var(--font-mono, monospace)',
|
|
1671
|
-
},
|
|
1672
|
-
|
|
1673
|
-
':host .clickable-error': {
|
|
1674
|
-
cursor: 'pointer',
|
|
1675
|
-
textDecoration: 'underline',
|
|
1676
|
-
textDecorationStyle: 'dotted',
|
|
1677
|
-
},
|
|
1678
|
-
|
|
1679
|
-
':host .clickable-error:hover': {
|
|
1680
|
-
background: 'rgba(220, 38, 38, 0.2)',
|
|
1681
|
-
},
|
|
1682
|
-
|
|
1683
|
-
':host .sig-badge': {
|
|
1684
|
-
fontSize: '11px',
|
|
1685
|
-
padding: '2px 6px',
|
|
1686
|
-
marginLeft: '8px',
|
|
1687
|
-
background: 'rgba(99, 102, 241, 0.1)',
|
|
1688
|
-
color: '#6366f1',
|
|
1689
|
-
borderRadius: '4px',
|
|
1690
|
-
},
|
|
1691
|
-
|
|
1240
|
+
// TJS-specific: console container class name
|
|
1692
1241
|
':host .tjs-console': {
|
|
1693
1242
|
height: '120px',
|
|
1694
1243
|
borderTop: '1px solid var(--code-border, #e5e7eb)',
|
|
1695
1244
|
display: 'flex',
|
|
1696
1245
|
flexDirection: 'column',
|
|
1697
1246
|
},
|
|
1698
|
-
|
|
1699
|
-
':host .console-header': {
|
|
1700
|
-
padding: '4px 12px',
|
|
1701
|
-
background: 'var(--code-background, #f3f4f6)',
|
|
1702
|
-
fontSize: '12px',
|
|
1703
|
-
fontWeight: '500',
|
|
1704
|
-
color: 'var(--text-color, #6b7280)',
|
|
1705
|
-
opacity: '0.7',
|
|
1706
|
-
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
1707
|
-
},
|
|
1708
|
-
|
|
1709
|
-
':host .console-output': {
|
|
1710
|
-
flex: '1',
|
|
1711
|
-
margin: '0',
|
|
1712
|
-
padding: '8px 12px',
|
|
1713
|
-
background: 'var(--code-background, #f3f4f6)',
|
|
1714
|
-
color: 'var(--text-color, #1f2937)',
|
|
1715
|
-
fontSize: '12px',
|
|
1716
|
-
fontFamily: 'ui-monospace, monospace',
|
|
1717
|
-
overflow: 'auto',
|
|
1718
|
-
whiteSpace: 'pre-wrap',
|
|
1719
|
-
},
|
|
1720
|
-
|
|
1721
|
-
':host .clickable-line': {
|
|
1722
|
-
cursor: 'pointer',
|
|
1723
|
-
color: '#2563eb',
|
|
1724
|
-
textDecoration: 'underline',
|
|
1725
|
-
textDecorationStyle: 'dotted',
|
|
1726
|
-
},
|
|
1727
|
-
|
|
1728
|
-
':host .clickable-line:hover': {
|
|
1729
|
-
color: '#1d4ed8',
|
|
1730
|
-
background: 'rgba(37, 99, 235, 0.1)',
|
|
1731
|
-
},
|
|
1732
1247
|
},
|
|
1733
1248
|
}) as ElementCreator<TJSPlayground>
|