tjs-lang 0.2.8 → 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 +20 -14
- package/demo/src/examples.ts +23 -83
- package/demo/src/playground-shared.ts +666 -0
- package/demo/src/tjs-playground.ts +52 -528
- package/demo/src/ts-examples.ts +5 -4
- package/demo/src/ts-playground.ts +50 -414
- package/dist/index.js +58 -23
- package/dist/index.js.map +9 -9
- package/dist/src/lang/types.d.ts +1 -1
- package/dist/src/types/Type.d.ts +3 -1
- package/dist/tjs-full.js +58 -23
- package/dist/tjs-full.js.map +9 -9
- package/dist/tjs-transpiler.js +55 -20
- package/dist/tjs-transpiler.js.map +7 -7
- package/dist/tjs-vm.js +14 -14
- package/dist/tjs-vm.js.map +5 -5
- package/docs/index.js +740 -1010
- package/docs/index.js.map +9 -8
- 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 +74 -0
- package/src/lang/inference.ts +40 -8
- package/src/lang/lang.test.ts +154 -16
- 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/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
|
@@ -26,6 +26,14 @@ import { codeMirror, CodeMirror } from '../../editors/codemirror/component'
|
|
|
26
26
|
import { tjs, type TJSTranspileOptions } from '../../src/lang'
|
|
27
27
|
import { generateDocsMarkdown } from './docs-utils'
|
|
28
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'
|
|
29
37
|
import { ModuleStore, type ValidationResult } from './module-store'
|
|
30
38
|
import {
|
|
31
39
|
buildAutocompleteContext,
|
|
@@ -438,42 +446,11 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
438
446
|
}
|
|
439
447
|
|
|
440
448
|
private renderConsole() {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const html = this.consoleMessages
|
|
447
|
-
.map((msg) => {
|
|
448
|
-
// Escape HTML
|
|
449
|
-
const escaped = msg
|
|
450
|
-
.replace(/&/g, '&')
|
|
451
|
-
.replace(/</g, '<')
|
|
452
|
-
.replace(/>/g, '>')
|
|
453
|
-
|
|
454
|
-
// Replace line references with clickable spans
|
|
455
|
-
return escaped.replace(linePattern, (match, l1, c1, l2, c2) => {
|
|
456
|
-
const line = l1 || l2
|
|
457
|
-
const col = c1 || c2 || '1'
|
|
458
|
-
return `<span class="clickable-line" data-line="${line}" data-col="${col}">${match}</span>`
|
|
459
|
-
})
|
|
460
|
-
})
|
|
461
|
-
.join('\n')
|
|
462
|
-
|
|
463
|
-
this.parts.console.innerHTML = html
|
|
464
|
-
this.parts.console.scrollTop = this.parts.console.scrollHeight
|
|
465
|
-
|
|
466
|
-
// Add click handlers
|
|
467
|
-
this.parts.console.querySelectorAll('.clickable-line').forEach((el) => {
|
|
468
|
-
el.addEventListener('click', (e) => {
|
|
469
|
-
const target = e.currentTarget as HTMLElement
|
|
470
|
-
const line = parseInt(target.dataset.line || '0', 10)
|
|
471
|
-
const col = parseInt(target.dataset.col || '1', 10)
|
|
472
|
-
if (line > 0) {
|
|
473
|
-
this.goToSourceLine(line, col)
|
|
474
|
-
}
|
|
475
|
-
})
|
|
476
|
-
})
|
|
449
|
+
renderConsoleMessages(
|
|
450
|
+
this.consoleMessages,
|
|
451
|
+
this.parts.console,
|
|
452
|
+
(line, col) => this.goToSourceLine(line, col)
|
|
453
|
+
)
|
|
477
454
|
}
|
|
478
455
|
|
|
479
456
|
// Build flag toggle handlers
|
|
@@ -548,10 +525,7 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
548
525
|
// Update test results and status bar with timing
|
|
549
526
|
const tests = result.testResults || []
|
|
550
527
|
const failed = tests.filter((t: any) => !t.passed).length
|
|
551
|
-
const timeStr =
|
|
552
|
-
this.lastTranspileTime < 1
|
|
553
|
-
? `${(this.lastTranspileTime * 1000).toFixed(0)}μs`
|
|
554
|
-
: `${this.lastTranspileTime.toFixed(2)}ms`
|
|
528
|
+
const timeStr = formatExecTime(this.lastTranspileTime)
|
|
555
529
|
if (failed > 0) {
|
|
556
530
|
this.parts.statusBar.textContent = `Transpiled in ${timeStr} with ${failed} test failure${
|
|
557
531
|
failed > 1 ? 's' : ''
|
|
@@ -619,75 +593,13 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
619
593
|
|
|
620
594
|
private updateTestResults(result: any) {
|
|
621
595
|
const tests = result.testResults
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
this.
|
|
625
|
-
this.parts.tjsEditor
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const passed = tests.filter((t: any) => t.passed).length
|
|
630
|
-
const failed = tests.filter((t: any) => !t.passed).length
|
|
631
|
-
|
|
632
|
-
// 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
|
+
)
|
|
633
602
|
this.updateTestsTabLabel(passed, failed)
|
|
634
|
-
|
|
635
|
-
// Set gutter markers for failed tests
|
|
636
|
-
const failedTests = tests.filter((t: any) => !t.passed && t.line)
|
|
637
|
-
if (failedTests.length > 0) {
|
|
638
|
-
this.parts.tjsEditor.setMarkers(
|
|
639
|
-
failedTests.map((t: any) => ({
|
|
640
|
-
line: t.line,
|
|
641
|
-
message: t.error || t.description,
|
|
642
|
-
severity: 'error' as const,
|
|
643
|
-
}))
|
|
644
|
-
)
|
|
645
|
-
} else {
|
|
646
|
-
this.parts.tjsEditor.clearMarkers()
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
let html = `<div class="test-summary">`
|
|
650
|
-
html += `<strong>${passed} passed</strong>`
|
|
651
|
-
if (failed > 0) {
|
|
652
|
-
html += `, <strong class="test-failed">${failed} failed</strong>`
|
|
653
|
-
}
|
|
654
|
-
html += `</div><ul class="test-list">`
|
|
655
|
-
|
|
656
|
-
for (const test of tests) {
|
|
657
|
-
const icon = test.passed ? '✓' : '✗'
|
|
658
|
-
const cls = test.passed ? 'test-pass' : 'test-fail'
|
|
659
|
-
const sigBadge = test.isSignatureTest
|
|
660
|
-
? ' <span class="sig-badge">signature</span>'
|
|
661
|
-
: ''
|
|
662
|
-
const clickable =
|
|
663
|
-
!test.passed && test.line ? ' class="clickable-error"' : ''
|
|
664
|
-
const dataLine = test.line ? ` data-line="${test.line}"` : ''
|
|
665
|
-
html += `<li class="${cls}"${dataLine}>${icon} ${test.description}${sigBadge}`
|
|
666
|
-
if (!test.passed && test.error) {
|
|
667
|
-
html += `<div${clickable}${dataLine} class="test-error${
|
|
668
|
-
test.line ? ' clickable-error' : ''
|
|
669
|
-
}">${test.error}</div>`
|
|
670
|
-
}
|
|
671
|
-
html += `</li>`
|
|
672
|
-
}
|
|
673
|
-
html += `</ul>`
|
|
674
|
-
|
|
675
|
-
this.parts.testsOutput.innerHTML = html
|
|
676
|
-
|
|
677
|
-
// Add click handlers for clickable errors
|
|
678
|
-
this.parts.testsOutput
|
|
679
|
-
.querySelectorAll('.clickable-error')
|
|
680
|
-
.forEach((el) => {
|
|
681
|
-
el.addEventListener('click', (e) => {
|
|
682
|
-
const line = parseInt(
|
|
683
|
-
(e.currentTarget as HTMLElement).dataset.line || '0',
|
|
684
|
-
10
|
|
685
|
-
)
|
|
686
|
-
if (line > 0) {
|
|
687
|
-
this.goToSourceLine(line)
|
|
688
|
-
}
|
|
689
|
-
})
|
|
690
|
-
})
|
|
691
603
|
}
|
|
692
604
|
|
|
693
605
|
private updateTestsTabLabel(passed: number, failed: number) {
|
|
@@ -1052,6 +964,9 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1052
964
|
return
|
|
1053
965
|
}
|
|
1054
966
|
|
|
967
|
+
// Show JS output immediately after successful transpilation
|
|
968
|
+
this.parts.outputTabs.value = 0 // JS is first tab (index 0)
|
|
969
|
+
|
|
1055
970
|
this.parts.statusBar.textContent = 'Running...'
|
|
1056
971
|
|
|
1057
972
|
try {
|
|
@@ -1111,137 +1026,33 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1111
1026
|
)
|
|
1112
1027
|
|
|
1113
1028
|
// Create a complete HTML document for the iframe
|
|
1114
|
-
const iframeDoc =
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
<script>
|
|
1124
|
-
// Expose parent's run/runAgent/getIdToken in iframe for playground convenience
|
|
1125
|
-
if (parent.run) window.run = parent.run.bind(parent);
|
|
1126
|
-
if (parent.runAgent) window.runAgent = parent.runAgent.bind(parent);
|
|
1127
|
-
if (parent.getIdToken) window.getIdToken = parent.getIdToken.bind(parent);
|
|
1128
|
-
|
|
1129
|
-
// TJS runtime stub - must stay in sync with src/lang/runtime.ts
|
|
1130
|
-
// TODO: Eliminate this once transpiler emits self-contained code
|
|
1131
|
-
// See: src/lang/emitters/js.ts for the plan to inline runtime functions
|
|
1132
|
-
globalThis.__tjs = {
|
|
1133
|
-
version: '0.0.0',
|
|
1134
|
-
pushStack: () => {},
|
|
1135
|
-
popStack: () => {},
|
|
1136
|
-
getStack: () => [],
|
|
1137
|
-
typeError: (path, expected, value) => {
|
|
1138
|
-
const actual = value === null ? 'null' : typeof value;
|
|
1139
|
-
const err = new Error(\`Expected \${expected} for '\${path}', got \${actual}\`);
|
|
1140
|
-
err.name = 'MonadicError';
|
|
1141
|
-
err.path = path;
|
|
1142
|
-
err.expected = expected;
|
|
1143
|
-
err.actual = actual;
|
|
1144
|
-
return err;
|
|
1145
|
-
},
|
|
1146
|
-
createRuntime: function() { return this; },
|
|
1147
|
-
Is: (a, b) => {
|
|
1148
|
-
if (a === b) return true;
|
|
1149
|
-
if (a === null || b === null) return a === b;
|
|
1150
|
-
if (typeof a !== typeof b) return false;
|
|
1151
|
-
if (typeof a !== 'object') return false;
|
|
1152
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1153
|
-
if (a.length !== b.length) return false;
|
|
1154
|
-
return a.every((v, i) => globalThis.__tjs.Is(v, b[i]));
|
|
1155
|
-
}
|
|
1156
|
-
const keysA = Object.keys(a);
|
|
1157
|
-
const keysB = Object.keys(b);
|
|
1158
|
-
if (keysA.length !== keysB.length) return false;
|
|
1159
|
-
return keysA.every(k => globalThis.__tjs.Is(a[k], b[k]));
|
|
1160
|
-
},
|
|
1161
|
-
IsNot: (a, b) => !globalThis.__tjs.Is(a, b),
|
|
1162
|
-
};
|
|
1163
|
-
</script>
|
|
1164
|
-
<script type="module">
|
|
1165
|
-
// Import statements must be at the top of the module
|
|
1166
|
-
${importStatements.join('\n ')}
|
|
1167
|
-
|
|
1168
|
-
// Capture console.log
|
|
1169
|
-
const _log = console.log;
|
|
1170
|
-
console.log = (...args) => {
|
|
1171
|
-
_log(...args);
|
|
1172
|
-
parent.postMessage({ type: 'console', message: args.map(a => {
|
|
1173
|
-
if (typeof a !== 'object' || a === null) return String(a);
|
|
1174
|
-
try {
|
|
1175
|
-
return JSON.stringify(a, null, 2);
|
|
1176
|
-
} catch {
|
|
1177
|
-
return String(a);
|
|
1178
|
-
}
|
|
1179
|
-
}).join(' ') }, '*');
|
|
1180
|
-
};
|
|
1181
|
-
|
|
1182
|
-
try {
|
|
1183
|
-
// WASM blocks are pre-compiled and embedded in the transpiled code
|
|
1184
|
-
// They auto-instantiate via the async IIFE at the top of the code
|
|
1185
|
-
|
|
1186
|
-
const __execStart = performance.now();
|
|
1187
|
-
${codeWithoutImports}
|
|
1188
|
-
|
|
1189
|
-
// Try to call the function if it exists and show result
|
|
1190
|
-
const funcName = Object.keys(window).find(k => {
|
|
1191
|
-
try { return typeof window[k] === 'function' && window[k].__tjs; }
|
|
1192
|
-
catch { return false; }
|
|
1193
|
-
});
|
|
1194
|
-
if (funcName) {
|
|
1195
|
-
const __callStart = performance.now();
|
|
1196
|
-
const result = window[funcName]();
|
|
1197
|
-
const __execTime = performance.now() - __callStart;
|
|
1198
|
-
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
|
|
1199
|
-
if (result !== undefined) {
|
|
1200
|
-
// If result is a DOM node, append it; otherwise log it
|
|
1201
|
-
if (result instanceof Node) {
|
|
1202
|
-
document.body.append(result);
|
|
1203
|
-
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
1204
|
-
} else {
|
|
1205
|
-
console.log('Result:', result);
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
} else {
|
|
1209
|
-
// No TJS function found, report total parse/exec time
|
|
1210
|
-
const __execTime = performance.now() - __execStart;
|
|
1211
|
-
parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
|
|
1212
|
-
}
|
|
1213
|
-
// Check if body has content after execution
|
|
1214
|
-
if (document.body.children.length > 0) {
|
|
1215
|
-
parent.postMessage({ type: 'hasPreviewContent' }, '*');
|
|
1216
|
-
}
|
|
1217
|
-
} catch (e) {
|
|
1218
|
-
parent.postMessage({ type: 'error', message: e.message }, '*');
|
|
1219
|
-
}
|
|
1220
|
-
</script>
|
|
1221
|
-
</body>
|
|
1222
|
-
</html>`
|
|
1029
|
+
const iframeDoc = buildIframeDoc({
|
|
1030
|
+
cssContent,
|
|
1031
|
+
htmlContent,
|
|
1032
|
+
importMapScript,
|
|
1033
|
+
jsCode: codeWithoutImports,
|
|
1034
|
+
importStatements,
|
|
1035
|
+
parentBindings: true,
|
|
1036
|
+
autoCallTjsFunction: true,
|
|
1037
|
+
})
|
|
1223
1038
|
|
|
1224
1039
|
// Listen for messages from iframe
|
|
1225
|
-
const messageHandler = (
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
? `${(execTime * 1000).toFixed(0)}μs`
|
|
1234
|
-
: `${execTime.toFixed(2)}ms`
|
|
1235
|
-
this.parts.consoleHeader.textContent = `Console — executed in ${execStr}`
|
|
1236
|
-
} else if (event.data?.type === 'hasPreviewContent') {
|
|
1237
|
-
// 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: () => {
|
|
1238
1048
|
this.parts.outputTabs.value = 1 // Preview is second tab (index 1)
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1049
|
+
},
|
|
1050
|
+
onError: (message) => {
|
|
1051
|
+
this.log(`Error: ${message}`)
|
|
1241
1052
|
this.parts.statusBar.textContent = 'Runtime error'
|
|
1242
1053
|
this.parts.statusBar.classList.add('error')
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1054
|
+
},
|
|
1055
|
+
})
|
|
1245
1056
|
window.addEventListener('message', messageHandler)
|
|
1246
1057
|
|
|
1247
1058
|
// Set iframe content using blob URL instead of srcdoc
|
|
@@ -1339,16 +1150,9 @@ export class TJSPlayground extends Component<TJSPlaygroundParts> {
|
|
|
1339
1150
|
export const tjsPlayground = TJSPlayground.elementCreator({
|
|
1340
1151
|
tag: 'tjs-playground',
|
|
1341
1152
|
styleSpec: {
|
|
1342
|
-
|
|
1343
|
-
display: 'flex',
|
|
1344
|
-
flexDirection: 'column',
|
|
1345
|
-
height: '100%',
|
|
1346
|
-
flex: '1 1 auto',
|
|
1347
|
-
background: 'var(--background, #fff)',
|
|
1348
|
-
color: 'var(--text-color, #1f2937)',
|
|
1349
|
-
fontFamily: 'system-ui, sans-serif',
|
|
1350
|
-
},
|
|
1153
|
+
...sharedPlaygroundStyles,
|
|
1351
1154
|
|
|
1155
|
+
// TJS-specific: toolbar
|
|
1352
1156
|
':host .tjs-toolbar': {
|
|
1353
1157
|
display: 'flex',
|
|
1354
1158
|
alignItems: 'center',
|
|
@@ -1358,61 +1162,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1358
1162
|
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
1359
1163
|
},
|
|
1360
1164
|
|
|
1361
|
-
|
|
1362
|
-
display: 'flex',
|
|
1363
|
-
alignItems: 'center',
|
|
1364
|
-
gap: '4px',
|
|
1365
|
-
padding: '6px 12px',
|
|
1366
|
-
background: 'var(--brand-color, #3d4a6b)',
|
|
1367
|
-
color: 'var(--brand-text-color, white)',
|
|
1368
|
-
border: 'none',
|
|
1369
|
-
borderRadius: '6px',
|
|
1370
|
-
cursor: 'pointer',
|
|
1371
|
-
fontWeight: '500',
|
|
1372
|
-
fontSize: '14px',
|
|
1373
|
-
},
|
|
1374
|
-
|
|
1375
|
-
':host .run-btn:hover:not(:disabled)': {
|
|
1376
|
-
filter: 'brightness(1.1)',
|
|
1377
|
-
},
|
|
1378
|
-
|
|
1379
|
-
':host .run-btn:disabled': {
|
|
1380
|
-
opacity: '0.6',
|
|
1381
|
-
cursor: 'not-allowed',
|
|
1382
|
-
},
|
|
1383
|
-
|
|
1384
|
-
':host .toolbar-separator': {
|
|
1385
|
-
width: '1px',
|
|
1386
|
-
height: '20px',
|
|
1387
|
-
background: 'var(--code-border, #d1d5db)',
|
|
1388
|
-
},
|
|
1389
|
-
|
|
1390
|
-
':host .build-flags': {
|
|
1391
|
-
display: 'flex',
|
|
1392
|
-
alignItems: 'center',
|
|
1393
|
-
gap: '12px',
|
|
1394
|
-
},
|
|
1395
|
-
|
|
1396
|
-
':host .flag-label': {
|
|
1397
|
-
display: 'flex',
|
|
1398
|
-
alignItems: 'center',
|
|
1399
|
-
gap: '4px',
|
|
1400
|
-
fontSize: '13px',
|
|
1401
|
-
color: 'var(--text-color, #6b7280)',
|
|
1402
|
-
cursor: 'pointer',
|
|
1403
|
-
userSelect: 'none',
|
|
1404
|
-
},
|
|
1405
|
-
|
|
1406
|
-
':host .flag-label:hover': {
|
|
1407
|
-
color: 'var(--text-color, #374151)',
|
|
1408
|
-
},
|
|
1409
|
-
|
|
1410
|
-
':host .flag-label input[type="checkbox"]': {
|
|
1411
|
-
margin: '0',
|
|
1412
|
-
cursor: 'pointer',
|
|
1413
|
-
accentColor: 'var(--brand-color, #3d4a6b)',
|
|
1414
|
-
},
|
|
1415
|
-
|
|
1165
|
+
// TJS-specific: module name input
|
|
1416
1166
|
':host .module-name-input': {
|
|
1417
1167
|
padding: '6px 10px',
|
|
1418
1168
|
border: '1px solid var(--code-border, #d1d5db)',
|
|
@@ -1435,6 +1185,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1435
1185
|
opacity: '0.6',
|
|
1436
1186
|
},
|
|
1437
1187
|
|
|
1188
|
+
// TJS-specific: save button
|
|
1438
1189
|
':host .save-btn': {
|
|
1439
1190
|
display: 'flex',
|
|
1440
1191
|
alignItems: 'center',
|
|
@@ -1455,46 +1206,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1455
1206
|
borderColor: 'var(--brand-color, #3d4a6b)',
|
|
1456
1207
|
},
|
|
1457
1208
|
|
|
1458
|
-
|
|
1459
|
-
display: 'flex',
|
|
1460
|
-
alignItems: 'center',
|
|
1461
|
-
gap: '4px',
|
|
1462
|
-
padding: '6px 12px',
|
|
1463
|
-
background: 'var(--code-background, #e5e7eb)',
|
|
1464
|
-
color: 'var(--text-color, #374151)',
|
|
1465
|
-
border: '1px solid var(--code-border, #d1d5db)',
|
|
1466
|
-
borderRadius: '6px',
|
|
1467
|
-
cursor: 'pointer',
|
|
1468
|
-
fontWeight: '500',
|
|
1469
|
-
fontSize: '14px',
|
|
1470
|
-
transition: 'opacity 0.2s',
|
|
1471
|
-
},
|
|
1472
|
-
|
|
1473
|
-
':host .revert-btn:hover:not(:disabled)': {
|
|
1474
|
-
background: '#fef3c7',
|
|
1475
|
-
borderColor: '#f59e0b',
|
|
1476
|
-
color: '#92400e',
|
|
1477
|
-
},
|
|
1478
|
-
|
|
1479
|
-
':host .revert-btn:disabled': {
|
|
1480
|
-
cursor: 'default',
|
|
1481
|
-
},
|
|
1482
|
-
|
|
1483
|
-
':host .elastic': {
|
|
1484
|
-
flex: '1',
|
|
1485
|
-
},
|
|
1486
|
-
|
|
1487
|
-
':host .status-bar': {
|
|
1488
|
-
fontSize: '13px',
|
|
1489
|
-
color: 'var(--text-color, #6b7280)',
|
|
1490
|
-
opacity: '0.7',
|
|
1491
|
-
},
|
|
1492
|
-
|
|
1493
|
-
':host .status-bar.error': {
|
|
1494
|
-
color: '#dc2626',
|
|
1495
|
-
opacity: '1',
|
|
1496
|
-
},
|
|
1497
|
-
|
|
1209
|
+
// TJS-specific: layout
|
|
1498
1210
|
':host .tjs-main': {
|
|
1499
1211
|
display: 'flex',
|
|
1500
1212
|
flex: '1 1 auto',
|
|
@@ -1512,31 +1224,7 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1512
1224
|
overflow: 'hidden',
|
|
1513
1225
|
},
|
|
1514
1226
|
|
|
1515
|
-
//
|
|
1516
|
-
':host tosi-tabs > [name]': {
|
|
1517
|
-
background: 'var(--background, #fff)',
|
|
1518
|
-
color: 'var(--text-color, #1f2937)',
|
|
1519
|
-
},
|
|
1520
|
-
|
|
1521
|
-
// Editor wrapper - contains the shadow DOM code-mirror component
|
|
1522
|
-
':host .editor-wrapper': {
|
|
1523
|
-
flex: '1 1 auto',
|
|
1524
|
-
height: '100%',
|
|
1525
|
-
minHeight: '300px',
|
|
1526
|
-
position: 'relative',
|
|
1527
|
-
overflow: 'hidden',
|
|
1528
|
-
},
|
|
1529
|
-
|
|
1530
|
-
// code-mirror is shadow DOM, so we just size it - internal styles are handled by the component
|
|
1531
|
-
':host .editor-wrapper code-mirror': {
|
|
1532
|
-
display: 'block',
|
|
1533
|
-
position: 'absolute',
|
|
1534
|
-
top: '0',
|
|
1535
|
-
left: '0',
|
|
1536
|
-
right: '0',
|
|
1537
|
-
bottom: '0',
|
|
1538
|
-
},
|
|
1539
|
-
|
|
1227
|
+
// TJS-specific: JS output panel
|
|
1540
1228
|
':host .js-output': {
|
|
1541
1229
|
margin: '0',
|
|
1542
1230
|
padding: '12px',
|
|
@@ -1549,176 +1237,12 @@ export const tjsPlayground = TJSPlayground.elementCreator({
|
|
|
1549
1237
|
whiteSpace: 'pre-wrap',
|
|
1550
1238
|
},
|
|
1551
1239
|
|
|
1552
|
-
|
|
1553
|
-
width: '100%',
|
|
1554
|
-
height: '100%',
|
|
1555
|
-
border: 'none',
|
|
1556
|
-
background: 'var(--background, #fff)',
|
|
1557
|
-
},
|
|
1558
|
-
|
|
1559
|
-
':host .docs-output': {
|
|
1560
|
-
display: 'block',
|
|
1561
|
-
padding: '12px 16px',
|
|
1562
|
-
fontSize: '14px',
|
|
1563
|
-
fontFamily: 'system-ui, sans-serif',
|
|
1564
|
-
color: 'var(--text-color, inherit)',
|
|
1565
|
-
background: 'var(--background, #fff)',
|
|
1566
|
-
height: '100%',
|
|
1567
|
-
overflow: 'auto',
|
|
1568
|
-
},
|
|
1569
|
-
|
|
1570
|
-
':host .docs-output h2': {
|
|
1571
|
-
fontSize: '1.25em',
|
|
1572
|
-
marginTop: '0',
|
|
1573
|
-
marginBottom: '0.5em',
|
|
1574
|
-
color: 'var(--text-color, #1f2937)',
|
|
1575
|
-
},
|
|
1576
|
-
|
|
1577
|
-
':host .docs-output pre': {
|
|
1578
|
-
background: 'var(--code-background, #f3f4f6)',
|
|
1579
|
-
padding: '8px 12px',
|
|
1580
|
-
borderRadius: '6px',
|
|
1581
|
-
overflow: 'auto',
|
|
1582
|
-
fontSize: '13px',
|
|
1583
|
-
},
|
|
1584
|
-
|
|
1585
|
-
':host .docs-output code': {
|
|
1586
|
-
fontFamily: 'ui-monospace, monospace',
|
|
1587
|
-
fontSize: '0.9em',
|
|
1588
|
-
},
|
|
1589
|
-
|
|
1590
|
-
':host .docs-output p': {
|
|
1591
|
-
margin: '0.75em 0',
|
|
1592
|
-
lineHeight: '1.5',
|
|
1593
|
-
},
|
|
1594
|
-
|
|
1595
|
-
':host .docs-output h3': {
|
|
1596
|
-
fontSize: '1em',
|
|
1597
|
-
marginTop: '1em',
|
|
1598
|
-
marginBottom: '0.5em',
|
|
1599
|
-
},
|
|
1600
|
-
|
|
1601
|
-
':host .docs-output ul': {
|
|
1602
|
-
paddingLeft: '1.5em',
|
|
1603
|
-
margin: '0.5em 0',
|
|
1604
|
-
},
|
|
1605
|
-
|
|
1606
|
-
':host .docs-output li': {
|
|
1607
|
-
marginBottom: '0.25em',
|
|
1608
|
-
},
|
|
1609
|
-
|
|
1610
|
-
':host .docs-output hr': {
|
|
1611
|
-
border: 'none',
|
|
1612
|
-
borderTop: '1px solid var(--code-border, #e5e7eb)',
|
|
1613
|
-
margin: '1.5em 0',
|
|
1614
|
-
},
|
|
1615
|
-
|
|
1616
|
-
':host .tests-output': {
|
|
1617
|
-
padding: '12px',
|
|
1618
|
-
fontSize: '14px',
|
|
1619
|
-
fontFamily: 'system-ui, sans-serif',
|
|
1620
|
-
color: 'var(--text-color, inherit)',
|
|
1621
|
-
background: 'var(--background, #fff)',
|
|
1622
|
-
height: '100%',
|
|
1623
|
-
overflow: 'auto',
|
|
1624
|
-
},
|
|
1625
|
-
|
|
1626
|
-
':host .test-summary': {
|
|
1627
|
-
marginBottom: '12px',
|
|
1628
|
-
paddingBottom: '8px',
|
|
1629
|
-
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
1630
|
-
},
|
|
1631
|
-
|
|
1632
|
-
':host .test-failed': {
|
|
1633
|
-
color: '#dc2626',
|
|
1634
|
-
},
|
|
1635
|
-
|
|
1636
|
-
':host .test-list': {
|
|
1637
|
-
listStyle: 'none',
|
|
1638
|
-
padding: 0,
|
|
1639
|
-
margin: 0,
|
|
1640
|
-
},
|
|
1641
|
-
|
|
1642
|
-
':host .test-list li': {
|
|
1643
|
-
padding: '4px 0',
|
|
1644
|
-
},
|
|
1645
|
-
|
|
1646
|
-
':host .test-pass': {
|
|
1647
|
-
color: '#16a34a',
|
|
1648
|
-
},
|
|
1649
|
-
|
|
1650
|
-
':host .test-fail': {
|
|
1651
|
-
color: '#dc2626',
|
|
1652
|
-
},
|
|
1653
|
-
|
|
1654
|
-
':host .test-error': {
|
|
1655
|
-
marginLeft: '20px',
|
|
1656
|
-
marginTop: '4px',
|
|
1657
|
-
padding: '8px',
|
|
1658
|
-
background: 'rgba(220, 38, 38, 0.1)',
|
|
1659
|
-
borderRadius: '4px',
|
|
1660
|
-
fontSize: '13px',
|
|
1661
|
-
fontFamily: 'var(--font-mono, monospace)',
|
|
1662
|
-
},
|
|
1663
|
-
|
|
1664
|
-
':host .clickable-error': {
|
|
1665
|
-
cursor: 'pointer',
|
|
1666
|
-
textDecoration: 'underline',
|
|
1667
|
-
textDecorationStyle: 'dotted',
|
|
1668
|
-
},
|
|
1669
|
-
|
|
1670
|
-
':host .clickable-error:hover': {
|
|
1671
|
-
background: 'rgba(220, 38, 38, 0.2)',
|
|
1672
|
-
},
|
|
1673
|
-
|
|
1674
|
-
':host .sig-badge': {
|
|
1675
|
-
fontSize: '11px',
|
|
1676
|
-
padding: '2px 6px',
|
|
1677
|
-
marginLeft: '8px',
|
|
1678
|
-
background: 'rgba(99, 102, 241, 0.1)',
|
|
1679
|
-
color: '#6366f1',
|
|
1680
|
-
borderRadius: '4px',
|
|
1681
|
-
},
|
|
1682
|
-
|
|
1240
|
+
// TJS-specific: console container class name
|
|
1683
1241
|
':host .tjs-console': {
|
|
1684
1242
|
height: '120px',
|
|
1685
1243
|
borderTop: '1px solid var(--code-border, #e5e7eb)',
|
|
1686
1244
|
display: 'flex',
|
|
1687
1245
|
flexDirection: 'column',
|
|
1688
1246
|
},
|
|
1689
|
-
|
|
1690
|
-
':host .console-header': {
|
|
1691
|
-
padding: '4px 12px',
|
|
1692
|
-
background: 'var(--code-background, #f3f4f6)',
|
|
1693
|
-
fontSize: '12px',
|
|
1694
|
-
fontWeight: '500',
|
|
1695
|
-
color: 'var(--text-color, #6b7280)',
|
|
1696
|
-
opacity: '0.7',
|
|
1697
|
-
borderBottom: '1px solid var(--code-border, #e5e7eb)',
|
|
1698
|
-
},
|
|
1699
|
-
|
|
1700
|
-
':host .console-output': {
|
|
1701
|
-
flex: '1',
|
|
1702
|
-
margin: '0',
|
|
1703
|
-
padding: '8px 12px',
|
|
1704
|
-
background: 'var(--code-background, #f3f4f6)',
|
|
1705
|
-
color: 'var(--text-color, #1f2937)',
|
|
1706
|
-
fontSize: '12px',
|
|
1707
|
-
fontFamily: 'ui-monospace, monospace',
|
|
1708
|
-
overflow: 'auto',
|
|
1709
|
-
whiteSpace: 'pre-wrap',
|
|
1710
|
-
},
|
|
1711
|
-
|
|
1712
|
-
':host .clickable-line': {
|
|
1713
|
-
cursor: 'pointer',
|
|
1714
|
-
color: '#2563eb',
|
|
1715
|
-
textDecoration: 'underline',
|
|
1716
|
-
textDecorationStyle: 'dotted',
|
|
1717
|
-
},
|
|
1718
|
-
|
|
1719
|
-
':host .clickable-line:hover': {
|
|
1720
|
-
color: '#1d4ed8',
|
|
1721
|
-
background: 'rgba(37, 99, 235, 0.1)',
|
|
1722
|
-
},
|
|
1723
1247
|
},
|
|
1724
1248
|
}) as ElementCreator<TJSPlayground>
|
package/demo/src/ts-examples.ts
CHANGED
|
@@ -382,25 +382,26 @@ main()
|
|
|
382
382
|
description: 'Classes are supported with metadata',
|
|
383
383
|
group: 'advanced',
|
|
384
384
|
code: `// Classes with typed methods
|
|
385
|
+
// Note: return types omitted on methods — TJS infers them
|
|
385
386
|
|
|
386
387
|
class Calculator {
|
|
387
388
|
private value: number = 0
|
|
388
389
|
|
|
389
|
-
add(n: number)
|
|
390
|
+
add(n: number) {
|
|
390
391
|
this.value += n
|
|
391
392
|
return this
|
|
392
393
|
}
|
|
393
394
|
|
|
394
|
-
multiply(n: number)
|
|
395
|
+
multiply(n: number) {
|
|
395
396
|
this.value *= n
|
|
396
397
|
return this
|
|
397
398
|
}
|
|
398
399
|
|
|
399
|
-
getResult()
|
|
400
|
+
getResult() {
|
|
400
401
|
return this.value
|
|
401
402
|
}
|
|
402
403
|
|
|
403
|
-
reset()
|
|
404
|
+
reset() {
|
|
404
405
|
this.value = 0
|
|
405
406
|
}
|
|
406
407
|
}
|