tjs-lang 0.2.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.
Files changed (91) hide show
  1. package/CONTEXT.md +594 -0
  2. package/LICENSE +190 -0
  3. package/README.md +220 -0
  4. package/bin/benchmarks.ts +351 -0
  5. package/bin/dev.ts +205 -0
  6. package/bin/docs.js +170 -0
  7. package/bin/install-cursor.sh +71 -0
  8. package/bin/install-vscode.sh +71 -0
  9. package/bin/select-local-models.d.ts +1 -0
  10. package/bin/select-local-models.js +28 -0
  11. package/bin/select-local-models.ts +31 -0
  12. package/demo/autocomplete.test.ts +232 -0
  13. package/demo/docs.json +186 -0
  14. package/demo/examples.test.ts +598 -0
  15. package/demo/index.html +91 -0
  16. package/demo/src/autocomplete.ts +482 -0
  17. package/demo/src/capabilities.ts +859 -0
  18. package/demo/src/demo-nav.ts +2097 -0
  19. package/demo/src/examples.test.ts +161 -0
  20. package/demo/src/examples.ts +476 -0
  21. package/demo/src/imports.test.ts +196 -0
  22. package/demo/src/imports.ts +421 -0
  23. package/demo/src/index.ts +639 -0
  24. package/demo/src/module-store.ts +635 -0
  25. package/demo/src/module-sw.ts +132 -0
  26. package/demo/src/playground.ts +949 -0
  27. package/demo/src/service-host.ts +389 -0
  28. package/demo/src/settings.ts +440 -0
  29. package/demo/src/style.ts +280 -0
  30. package/demo/src/tjs-playground.ts +1605 -0
  31. package/demo/src/ts-examples.ts +478 -0
  32. package/demo/src/ts-playground.ts +1092 -0
  33. package/demo/static/favicon.svg +30 -0
  34. package/demo/static/photo-1.jpg +0 -0
  35. package/demo/static/photo-2.jpg +0 -0
  36. package/demo/static/texts/ai-history.txt +9 -0
  37. package/demo/static/texts/coffee-origins.txt +9 -0
  38. package/demo/static/texts/renewable-energy.txt +9 -0
  39. package/dist/index.js +256 -0
  40. package/dist/index.js.map +37 -0
  41. package/dist/tjs-batteries.js +4 -0
  42. package/dist/tjs-batteries.js.map +15 -0
  43. package/dist/tjs-full.js +256 -0
  44. package/dist/tjs-full.js.map +37 -0
  45. package/dist/tjs-transpiler.js +220 -0
  46. package/dist/tjs-transpiler.js.map +21 -0
  47. package/dist/tjs-vm.js +4 -0
  48. package/dist/tjs-vm.js.map +14 -0
  49. package/docs/CNAME +1 -0
  50. package/docs/favicon.svg +30 -0
  51. package/docs/index.html +91 -0
  52. package/docs/index.js +10468 -0
  53. package/docs/index.js.map +92 -0
  54. package/docs/photo-1.jpg +0 -0
  55. package/docs/photo-1.webp +0 -0
  56. package/docs/photo-2.jpg +0 -0
  57. package/docs/photo-2.webp +0 -0
  58. package/docs/texts/ai-history.txt +9 -0
  59. package/docs/texts/coffee-origins.txt +9 -0
  60. package/docs/texts/renewable-energy.txt +9 -0
  61. package/docs/tjs-lang.svg +31 -0
  62. package/docs/tosijs-agent.svg +31 -0
  63. package/editors/README.md +325 -0
  64. package/editors/ace/ajs-mode.js +328 -0
  65. package/editors/ace/ajs-mode.ts +269 -0
  66. package/editors/ajs-syntax.ts +212 -0
  67. package/editors/build-grammars.ts +510 -0
  68. package/editors/codemirror/ajs-language.js +287 -0
  69. package/editors/codemirror/ajs-language.ts +1447 -0
  70. package/editors/codemirror/autocomplete.test.ts +531 -0
  71. package/editors/codemirror/component.ts +404 -0
  72. package/editors/monaco/ajs-monarch.js +243 -0
  73. package/editors/monaco/ajs-monarch.ts +225 -0
  74. package/editors/tjs-syntax.ts +115 -0
  75. package/editors/vscode/language-configuration.json +37 -0
  76. package/editors/vscode/package.json +65 -0
  77. package/editors/vscode/syntaxes/ajs-injection.tmLanguage.json +107 -0
  78. package/editors/vscode/syntaxes/ajs.tmLanguage.json +252 -0
  79. package/editors/vscode/syntaxes/tjs.tmLanguage.json +333 -0
  80. package/package.json +83 -0
  81. package/src/cli/commands/check.ts +41 -0
  82. package/src/cli/commands/convert.ts +133 -0
  83. package/src/cli/commands/emit.ts +260 -0
  84. package/src/cli/commands/run.ts +68 -0
  85. package/src/cli/commands/test.ts +194 -0
  86. package/src/cli/commands/types.ts +20 -0
  87. package/src/cli/create-app.ts +236 -0
  88. package/src/cli/playground.ts +250 -0
  89. package/src/cli/tjs.ts +166 -0
  90. package/src/cli/tjsx.ts +160 -0
  91. package/tjs-lang.svg +31 -0
@@ -0,0 +1,639 @@
1
+ /*
2
+ * index.ts - Main entry point for agent-99 demo site
3
+ *
4
+ * Following the tosijs-ui documentation pattern:
5
+ * - Header with branding and settings
6
+ * - Sidebar navigation with search
7
+ * - Markdown content with live examples
8
+ */
9
+
10
+ import { elements, tosi, bindings, StyleSheet, bind } from 'tosijs'
11
+
12
+ import {
13
+ icons,
14
+ sideNav,
15
+ SideNav,
16
+ sizeBreak,
17
+ popMenu,
18
+ markdownViewer,
19
+ } from 'tosijs-ui'
20
+
21
+ import { styleSpec } from './style'
22
+ StyleSheet('demo-style', styleSpec)
23
+
24
+ // Import playground components
25
+ import { playground, Playground } from './playground'
26
+ import { tjsPlayground, TJSPlayground } from './tjs-playground'
27
+ import { tsPlayground, TSPlayground } from './ts-playground'
28
+
29
+ // Import new demo navigation
30
+ import { demoNav, DemoNav, tjsExamples } from './demo-nav'
31
+ import { tsExamples } from './ts-examples'
32
+
33
+ // Import examples
34
+ import { examples as ajsExamples } from './examples'
35
+
36
+ // Import settings dialog
37
+ import { showSettingsDialog } from './settings'
38
+
39
+ // Import tjs-lang for live examples
40
+ import * as agent from '../../src'
41
+ import * as tosijs from 'tosijs'
42
+ import * as tosijsui from 'tosijs-ui'
43
+
44
+ // Import capabilities builder for live examples
45
+ import { buildCapabilities, getSettings } from './capabilities'
46
+
47
+ // Create a demo runtime that uses settings-based capabilities
48
+ const demoRuntime = {
49
+ // Wraps vm.run with capabilities from settings
50
+ async run(ast: any, args: any, options: any = {}) {
51
+ const vm = new agent.AgentVM()
52
+ const caps = buildCapabilities(getSettings())
53
+ return vm.run(ast, args, {
54
+ ...options,
55
+ capabilities: {
56
+ ...options.capabilities,
57
+ llm: caps.llm,
58
+ },
59
+ })
60
+ },
61
+ }
62
+
63
+ // Make available globally for debugging
64
+ Object.assign(window, { agent, tosijs, tosijsui, demoRuntime })
65
+
66
+ // Load documentation
67
+ import docs from '../docs.json'
68
+
69
+ // Add playgrounds as special pages
70
+ const ajsPlaygroundDoc = {
71
+ title: '▶ AJS Playground',
72
+ filename: 'playground',
73
+ text: '',
74
+ isPlayground: 'ajs',
75
+ pin: 'top',
76
+ }
77
+
78
+ const tjsPlaygroundDoc = {
79
+ title: '▶ TJS Playground',
80
+ filename: 'tjs-playground',
81
+ text: '',
82
+ isPlayground: 'tjs',
83
+ pin: 'top',
84
+ }
85
+
86
+ // Insert playgrounds at top
87
+ const allDocs = [ajsPlaygroundDoc, tjsPlaygroundDoc, ...docs]
88
+
89
+ const PROJECT = 'tjs-lang'
90
+ const VERSION = '0.1.0' // TODO: import from package.json
91
+
92
+ // Determine initial doc from URL
93
+ const docName =
94
+ document.location.search !== ''
95
+ ? document.location.search.substring(1).split('&')[0]
96
+ : 'README.md'
97
+ const currentDoc =
98
+ allDocs.find((doc: any) => doc.filename === docName) ||
99
+ allDocs.find((d: any) => d.filename === 'README.md') ||
100
+ allDocs[0]
101
+
102
+ // Initialize reactive state
103
+ const { app, prefs } = tosi({
104
+ app: {
105
+ title: PROJECT,
106
+ version: VERSION,
107
+ githubUrl: `https://github.com/tonioloewald/${PROJECT}#readme`,
108
+ npmUrl: `https://www.npmjs.com/package/tjs-lang`,
109
+ bundleBadgeUrl: `https://deno.bundlejs.com/?q=tjs-lang&badge=`,
110
+ bundleUrl: `https://bundlejs.com/?q=tjs-lang`,
111
+ docs: allDocs,
112
+ currentDoc,
113
+ compact: false,
114
+ currentView: 'home' as 'home' | 'ajs' | 'tjs' | 'ts',
115
+ currentExample: null as any,
116
+ },
117
+ prefs: {
118
+ // UI settings (stored in localStorage)
119
+ theme: localStorage.getItem('theme') || 'system',
120
+ highContrast: localStorage.getItem('highContrast') === 'true',
121
+ // LLM settings (stored in localStorage)
122
+ preferredProvider: localStorage.getItem('preferredProvider') || 'auto',
123
+ openaiKey: localStorage.getItem('openaiKey') || '',
124
+ anthropicKey: localStorage.getItem('anthropicKey') || '',
125
+ deepseekKey: localStorage.getItem('deepseekKey') || '',
126
+ customLlmUrl: localStorage.getItem('customLlmUrl') || '',
127
+ },
128
+ })
129
+
130
+ // Persist preferences
131
+ const savePrefs = () => {
132
+ // UI settings
133
+ localStorage.setItem('theme', prefs.theme.valueOf())
134
+ localStorage.setItem('highContrast', String(prefs.highContrast.valueOf()))
135
+ // LLM settings
136
+ localStorage.setItem('preferredProvider', prefs.preferredProvider.valueOf())
137
+ localStorage.setItem('openaiKey', prefs.openaiKey.valueOf())
138
+ localStorage.setItem('anthropicKey', prefs.anthropicKey.valueOf())
139
+ localStorage.setItem('deepseekKey', prefs.deepseekKey.valueOf())
140
+ localStorage.setItem('customLlmUrl', prefs.customLlmUrl.valueOf())
141
+ }
142
+
143
+ // Custom bindings
144
+ bindings.docLink = {
145
+ toDOM(elt: HTMLElement, filename: string) {
146
+ elt.setAttribute('href', `?${filename}`)
147
+ },
148
+ }
149
+
150
+ bindings.current = {
151
+ toDOM(elt: HTMLElement, currentFile: string) {
152
+ const boundFile = elt.getAttribute('href') || ''
153
+ elt.classList.toggle('current', currentFile === boundFile.substring(1))
154
+ },
155
+ }
156
+
157
+ // Elements
158
+ const { h1, h2, div, span, a, img, header, button, template, input } = elements
159
+
160
+ // Theme binding
161
+ bind(document.body, 'prefs.theme', {
162
+ toDOM(element: HTMLElement, theme: string) {
163
+ if (theme === 'system') {
164
+ theme = window.matchMedia('(prefers-color-scheme: dark)').matches
165
+ ? 'dark'
166
+ : 'light'
167
+ }
168
+ element.classList.toggle('darkmode', theme === 'dark')
169
+ },
170
+ })
171
+
172
+ bind(document.body, 'prefs.highContrast', {
173
+ toDOM(element: HTMLElement, highContrast: any) {
174
+ element.classList.toggle('high-contrast', highContrast.valueOf())
175
+ },
176
+ })
177
+
178
+ // Handle browser navigation
179
+ window.addEventListener('popstate', () => {
180
+ const filename = window.location.search.substring(1)
181
+ app.currentDoc =
182
+ app.docs.find((doc: any) => doc.filename === filename) || app.docs[0]
183
+ })
184
+
185
+ // URL state management for view and example
186
+ function loadViewStateFromURL() {
187
+ const hash = window.location.hash.slice(1)
188
+ if (!hash) {
189
+ app.currentView = 'home'
190
+ return
191
+ }
192
+
193
+ const params = new URLSearchParams(hash)
194
+ const view = params.get('view')
195
+ const example = params.get('example')
196
+
197
+ if (view === 'home') {
198
+ app.currentView = 'home'
199
+ app.currentExample = null
200
+ } else if (view === 'ajs' || view === 'tjs' || view === 'ts') {
201
+ app.currentView = view
202
+ }
203
+
204
+ if (example) {
205
+ // Find example by name in appropriate list
206
+ if (view === 'ts') {
207
+ const found = tsExamples.find((e: any) => e.name === example)
208
+ if (found) app.currentExample = found
209
+ } else if (view === 'tjs') {
210
+ const found = tjsExamples.find((e: any) => e.name === example)
211
+ if (found) app.currentExample = found
212
+ } else {
213
+ const found = ajsExamples.find((e: any) => e.name === example)
214
+ if (found) app.currentExample = found
215
+ }
216
+ }
217
+ }
218
+
219
+ function saveViewStateToURL(view: string, exampleName?: string) {
220
+ const params = new URLSearchParams(window.location.hash.slice(1))
221
+ params.set('view', view)
222
+ if (exampleName) {
223
+ params.set('example', exampleName)
224
+ } else {
225
+ params.delete('example')
226
+ }
227
+ const newHash = params.toString()
228
+ window.history.replaceState(null, '', `#${newHash}`)
229
+ }
230
+
231
+ // Load initial state from URL
232
+ loadViewStateFromURL()
233
+
234
+ // Listen for hash changes
235
+ window.addEventListener('hashchange', loadViewStateFromURL)
236
+
237
+ // Main app
238
+ const main = document.querySelector('main') as HTMLElement
239
+
240
+ if (main) {
241
+ // Clear loading state
242
+ main.innerHTML = ''
243
+
244
+ main.append(
245
+ // Header
246
+ header(
247
+ // Menu toggle (compact mode)
248
+ button(
249
+ {
250
+ class: 'iconic',
251
+ title: 'Toggle navigation',
252
+ bind: {
253
+ value: app.compact,
254
+ binding: {
255
+ toDOM(element: HTMLElement, compact: boolean) {
256
+ element.style.display = compact ? '' : 'none'
257
+ },
258
+ },
259
+ },
260
+ onClick() {
261
+ const nav = document.querySelector(SideNav.tagName!) as SideNav
262
+ nav.contentVisible = !nav.contentVisible
263
+ },
264
+ },
265
+ icons.menu()
266
+ ),
267
+
268
+ // Logo and title
269
+ a(
270
+ {
271
+ href: '/',
272
+ style: {
273
+ display: 'flex',
274
+ alignItems: 'center',
275
+ gap: '8px',
276
+ },
277
+ },
278
+ // Agent icon
279
+ img({ src: '/favicon.svg', style: { width: '32px', height: '32px' } }),
280
+ h1('tjs-lang')
281
+ ),
282
+
283
+ // Elastic spacer
284
+ span({ class: 'elastic' }),
285
+
286
+ // Bundle size badge (responsive)
287
+ sizeBreak(
288
+ { minWidth: 600 },
289
+ span(
290
+ { class: 'badge' },
291
+ a(
292
+ { href: app.bundleUrl, target: '_blank' },
293
+ img({ alt: 'bundle size', src: app.bundleBadgeUrl })
294
+ )
295
+ ),
296
+ span({ slot: 'small' })
297
+ ),
298
+
299
+ // GitHub link
300
+ a(
301
+ {
302
+ class: 'iconic',
303
+ title: 'GitHub',
304
+ target: '_blank',
305
+ href: app.githubUrl,
306
+ },
307
+ icons.github()
308
+ ),
309
+
310
+ // NPM link
311
+ a(
312
+ {
313
+ class: 'iconic',
314
+ title: 'npm',
315
+ target: '_blank',
316
+ href: app.npmUrl,
317
+ },
318
+ icons.npm()
319
+ ),
320
+
321
+ // Settings menu
322
+ button(
323
+ {
324
+ class: 'iconic',
325
+ title: 'Settings',
326
+ onClick(event: MouseEvent) {
327
+ popMenu({
328
+ target: event.target as HTMLButtonElement,
329
+ menuItems: [
330
+ {
331
+ caption: 'API Keys',
332
+ icon: 'key',
333
+ action: () => {
334
+ showSettingsDialog(
335
+ {
336
+ preferredProvider:
337
+ prefs.preferredProvider.valueOf() as any,
338
+ openaiKey: prefs.openaiKey.valueOf(),
339
+ anthropicKey: prefs.anthropicKey.valueOf(),
340
+ deepseekKey: prefs.deepseekKey.valueOf(),
341
+ customLlmUrl: prefs.customLlmUrl.valueOf(),
342
+ },
343
+ (settings) => {
344
+ prefs.preferredProvider = settings.preferredProvider
345
+ prefs.openaiKey = settings.openaiKey
346
+ prefs.anthropicKey = settings.anthropicKey
347
+ prefs.deepseekKey = settings.deepseekKey
348
+ prefs.customLlmUrl = settings.customLlmUrl
349
+ savePrefs()
350
+ }
351
+ )
352
+ },
353
+ },
354
+ {
355
+ caption: 'Color Theme',
356
+ icon: 'rgb',
357
+ menuItems: [
358
+ {
359
+ caption: 'System',
360
+ checked: () => prefs.theme.valueOf() === 'system',
361
+ action: () => {
362
+ prefs.theme = 'system'
363
+ savePrefs()
364
+ },
365
+ },
366
+ {
367
+ caption: 'Dark',
368
+ checked: () => prefs.theme.valueOf() === 'dark',
369
+ action: () => {
370
+ prefs.theme = 'dark'
371
+ savePrefs()
372
+ },
373
+ },
374
+ {
375
+ caption: 'Light',
376
+ checked: () => prefs.theme.valueOf() === 'light',
377
+ action: () => {
378
+ prefs.theme = 'light'
379
+ savePrefs()
380
+ },
381
+ },
382
+ null,
383
+ {
384
+ caption: 'High Contrast',
385
+ checked: () => prefs.highContrast.valueOf(),
386
+ action: () => {
387
+ prefs.highContrast = !prefs.highContrast.valueOf()
388
+ savePrefs()
389
+ },
390
+ },
391
+ ],
392
+ },
393
+ null,
394
+ {
395
+ caption: 'GitHub',
396
+ icon: 'github',
397
+ action: () => window.open(app.githubUrl.valueOf(), '_blank'),
398
+ },
399
+ {
400
+ caption: 'npm',
401
+ icon: 'npm',
402
+ action: () => window.open(app.npmUrl.valueOf(), '_blank'),
403
+ },
404
+ ],
405
+ })
406
+ },
407
+ },
408
+ icons.moreVertical()
409
+ )
410
+ ),
411
+
412
+ // Side navigation + content
413
+ sideNav(
414
+ {
415
+ name: 'Examples',
416
+ navSize: 220,
417
+ minSize: 600,
418
+ style: {
419
+ flex: '1 1 auto',
420
+ overflow: 'hidden',
421
+ },
422
+ onChange() {
423
+ const nav = document.querySelector(SideNav.tagName!) as SideNav
424
+ app.compact = nav.compact
425
+ },
426
+ },
427
+
428
+ // Demo navigation with 4 accordion sections
429
+ (() => {
430
+ const nav = demoNav({
431
+ slot: 'nav',
432
+ style: {
433
+ width: '100%',
434
+ height: '100%',
435
+ },
436
+ }) as DemoNav
437
+
438
+ // Pass docs to the nav component
439
+ nav.docs = docs
440
+
441
+ // Handle AJS example selection - load into AJS playground
442
+ nav.addEventListener('select-ajs-example', ((event: CustomEvent) => {
443
+ const { example } = event.detail
444
+ app.currentView = 'ajs'
445
+ app.currentExample = example
446
+ saveViewStateToURL('ajs', example.name)
447
+ }) as EventListener)
448
+
449
+ // Handle TJS example selection - load into TJS playground
450
+ nav.addEventListener('select-tjs-example', ((event: CustomEvent) => {
451
+ const { example } = event.detail
452
+ app.currentView = 'tjs'
453
+ app.currentExample = example
454
+ saveViewStateToURL('tjs', example.name)
455
+ }) as EventListener)
456
+
457
+ // Handle TS example selection - load into TS playground
458
+ nav.addEventListener('select-ts-example', ((event: CustomEvent) => {
459
+ const { example } = event.detail
460
+ app.currentView = 'ts'
461
+ app.currentExample = example
462
+ saveViewStateToURL('ts', example.name)
463
+ }) as EventListener)
464
+
465
+ // Handle Home selection - show README
466
+ nav.addEventListener('select-home', (() => {
467
+ app.currentView = 'home'
468
+ app.currentExample = null
469
+ saveViewStateToURL('home')
470
+ }) as EventListener)
471
+
472
+ // Nav syncs its own state from URL - no need to overwrite on initial load
473
+
474
+ return nav
475
+ })(),
476
+
477
+ // Content area - shows the active playground
478
+ div(
479
+ {
480
+ style: {
481
+ position: 'relative',
482
+ overflow: 'hidden',
483
+ height: '100%',
484
+ display: 'flex',
485
+ flexDirection: 'column',
486
+ },
487
+ },
488
+ // Home/README view
489
+ (() => {
490
+ const readmeDoc = docs.find((d: any) => d.filename === 'README.md')
491
+ const viewer = markdownViewer({
492
+ value: readmeDoc?.text || '# Welcome to tjs-lang',
493
+ style: {
494
+ display: 'none',
495
+ flex: '1 1 auto',
496
+ height: '100%',
497
+ padding: '20px 40px',
498
+ overflow: 'auto',
499
+ maxWidth: '900px',
500
+ },
501
+ })
502
+
503
+ // Show/hide based on currentView
504
+ bind(viewer, 'app.currentView', {
505
+ toDOM(element: HTMLElement, view: string) {
506
+ element.style.display = view === 'home' ? 'block' : 'none'
507
+ },
508
+ })
509
+
510
+ return viewer
511
+ })(),
512
+
513
+ // AJS Playground
514
+ (() => {
515
+ const pg = playground({
516
+ style: {
517
+ display: 'none',
518
+ flex: '1 1 auto',
519
+ height: '100%',
520
+ padding: '10px',
521
+ },
522
+ }) as Playground
523
+
524
+ // Show/hide based on currentView
525
+ bind(pg, 'app.currentView', {
526
+ toDOM(element: HTMLElement, view: string) {
527
+ element.style.display = view === 'ajs' ? 'block' : 'none'
528
+ },
529
+ })
530
+
531
+ // Load example when selected
532
+ bind(pg, 'app.currentExample', {
533
+ toDOM(element: Playground, example: any) {
534
+ if (
535
+ example &&
536
+ app.currentView.valueOf() === 'ajs' &&
537
+ element.editor
538
+ ) {
539
+ element.editor.dispatch({
540
+ changes: {
541
+ from: 0,
542
+ to: element.editor.state.doc.length,
543
+ insert: example.code,
544
+ },
545
+ })
546
+ }
547
+ },
548
+ })
549
+
550
+ return pg
551
+ })(),
552
+
553
+ // TJS Playground
554
+ (() => {
555
+ const pg = tjsPlayground({
556
+ style: {
557
+ display: 'none',
558
+ flex: '1 1 auto',
559
+ height: '100%',
560
+ padding: '10px',
561
+ },
562
+ }) as TJSPlayground
563
+
564
+ // Show/hide based on currentView
565
+ bind(pg, 'app.currentView', {
566
+ toDOM(element: HTMLElement, view: string) {
567
+ // Use 'flex' not 'block' - component has flex layout internally
568
+ element.style.display = view === 'tjs' ? 'flex' : 'none'
569
+ },
570
+ })
571
+
572
+ // Load example when selected (auto-runs via setSource)
573
+ bind(pg, 'app.currentExample', {
574
+ toDOM(element: TJSPlayground, example: any) {
575
+ if (example && app.currentView.valueOf() === 'tjs') {
576
+ setTimeout(() => {
577
+ if (element.setSource) {
578
+ const code =
579
+ typeof example.code === 'string'
580
+ ? example.code
581
+ : String(example.code)
582
+ element.setSource(code, example.name)
583
+ }
584
+ }, 0)
585
+ }
586
+ },
587
+ })
588
+
589
+ return pg
590
+ })(),
591
+
592
+ // TS Playground (TypeScript -> TJS -> JS pipeline)
593
+ (() => {
594
+ const pg = tsPlayground({
595
+ style: {
596
+ display: 'none',
597
+ flex: '1 1 auto',
598
+ height: '100%',
599
+ padding: '10px',
600
+ },
601
+ }) as TSPlayground
602
+
603
+ // Show/hide based on currentView
604
+ bind(pg, 'app.currentView', {
605
+ toDOM(element: HTMLElement, view: string) {
606
+ element.style.display = view === 'ts' ? 'flex' : 'none'
607
+ },
608
+ })
609
+
610
+ // Load example when selected
611
+ bind(pg, 'app.currentExample', {
612
+ toDOM(element: TSPlayground, example: any) {
613
+ if (example && app.currentView.valueOf() === 'ts') {
614
+ setTimeout(() => {
615
+ if (element.setSource) {
616
+ const code =
617
+ typeof example.code === 'string'
618
+ ? example.code
619
+ : String(example.code)
620
+ element.setSource(code, example.name)
621
+ }
622
+ }, 0)
623
+ }
624
+ },
625
+ })
626
+
627
+ return pg
628
+ })()
629
+ )
630
+ )
631
+ )
632
+ }
633
+
634
+ // Log welcome message
635
+ console.log(
636
+ `%c tjs-lang %c v${VERSION} `,
637
+ 'background: #6366f1; color: white; padding: 2px 6px; border-radius: 3px 0 0 3px;',
638
+ 'background: #374151; color: white; padding: 2px 6px; border-radius: 0 3px 3px 0;'
639
+ )