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/docs/index.js CHANGED
@@ -18702,6 +18702,41 @@ function runAllTests(tests, mocks, sigTestInfos, transpiledCode, resolvedImports
18702
18702
  if (!__deepEqual(actual, expected)) {
18703
18703
  throw new Error('Expected ' + __format(expected) + ' but got ' + __format(actual))
18704
18704
  }
18705
+ },
18706
+ toContain(item) {
18707
+ if (!Array.isArray(actual) || !actual.some(function(v) { return __deepEqual(v, item) })) {
18708
+ throw new Error('Expected ' + __format(actual) + ' to contain ' + __format(item))
18709
+ }
18710
+ },
18711
+ toBeTruthy() {
18712
+ if (!actual) {
18713
+ throw new Error('Expected ' + __format(actual) + ' to be truthy')
18714
+ }
18715
+ },
18716
+ toBeFalsy() {
18717
+ if (actual) {
18718
+ throw new Error('Expected ' + __format(actual) + ' to be falsy')
18719
+ }
18720
+ },
18721
+ toBeNull() {
18722
+ if (actual !== null) {
18723
+ throw new Error('Expected null but got ' + __format(actual))
18724
+ }
18725
+ },
18726
+ toBeUndefined() {
18727
+ if (actual !== undefined) {
18728
+ throw new Error('Expected undefined but got ' + __format(actual))
18729
+ }
18730
+ },
18731
+ toBeGreaterThan(n) {
18732
+ if (!(actual > n)) {
18733
+ throw new Error('Expected ' + __format(actual) + ' to be greater than ' + n)
18734
+ }
18735
+ },
18736
+ toBeLessThan(n) {
18737
+ if (!(actual < n)) {
18738
+ throw new Error('Expected ' + __format(actual) + ' to be less than ' + n)
18739
+ }
18705
18740
  }
18706
18741
  }
18707
18742
  }
@@ -236035,7 +236070,7 @@ var examples = [
236035
236070
  description: "Fetch weather data (no API key needed)",
236036
236071
  group: "api",
236037
236072
  code: `function getWeather({ lat = 37.7749, lon = -122.4194 }) {
236038
- let url = \`https://api.open-meteo.com/v1/forecast?latitude=\${lat}&longitude=\${lon}&current_weather=true\`
236073
+ let url = 'https://api.open-meteo.com/v1/forecast?latitude=' + lat + '&longitude=' + lon + '&current_weather=true'
236039
236074
  let response = httpFetch({ url, cache: 1800 })
236040
236075
  let weather = response.current_weather
236041
236076
  return { weather }
@@ -236046,7 +236081,7 @@ var examples = [
236046
236081
  description: "Search Apple iTunes catalog",
236047
236082
  group: "api",
236048
236083
  code: `function searchMusic({ query = 'Beatles', limit = 5 }) {
236049
- let url = \`https://itunes.apple.com/search?term=\${query}&limit=\${limit}&media=music\`
236084
+ let url = 'https://itunes.apple.com/search?term=' + query + '&limit=' + limit + '&media=music'
236050
236085
  let response = httpFetch({ url, cache: 3600 })
236051
236086
  let tracks = response.results.map(x => ({
236052
236087
  artist: x.artistName,
@@ -236061,7 +236096,7 @@ var examples = [
236061
236096
  description: "Search GitHub repositories",
236062
236097
  group: "api",
236063
236098
  code: `function searchRepos({ query = 'tosijs', perPage = 5 }) {
236064
- let url = \`https://api.github.com/search/repositories?q=\${query}&per_page=\${perPage}&sort=stars\`
236099
+ let url = 'https://api.github.com/search/repositories?q=' + query + '&per_page=' + perPage + '&sort=stars'
236065
236100
  let response = httpFetch({ url, cache: 300 })
236066
236101
  let repos = response.items.map(x => ({
236067
236102
  name: x.full_name,
@@ -236090,12 +236125,10 @@ var examples = [
236090
236125
  code: `function summarize({ source = 'coffee-origins' }) {
236091
236126
  // Fetch text from our sample documents
236092
236127
  // Options: 'coffee-origins', 'ai-history', 'renewable-energy'
236093
- let url = \`/texts/\${source}.txt\`
236128
+ let url = '/texts/' + source + '.txt'
236094
236129
  let text = httpFetch({ url })
236095
236130
 
236096
- let prompt = \`Summarize the following text in 2-3 sentences:
236097
-
236098
- \${text}\`
236131
+ let prompt = 'Summarize the following text in 2-3 sentences:\\n\\n' + text
236099
236132
  let summary = llmPredict({ prompt })
236100
236133
  return { source, summary }
236101
236134
  }`
@@ -236115,7 +236148,7 @@ var examples = [
236115
236148
  hobbies: ['']
236116
236149
  })
236117
236150
 
236118
- let prompt = \`Extract person info from this text: \${text}\`
236151
+ let prompt = 'Extract person info from this text: ' + text
236119
236152
  let response = llmPredict({ prompt, options: { responseFormat: schema } })
236120
236153
  let person = JSON.parse(response)
236121
236154
  return { person }
@@ -236129,12 +236162,12 @@ var examples = [
236129
236162
  code: `function findCovers({ song = 'Yesterday', artist = 'Beatles' }) {
236130
236163
  // Search iTunes for the song
236131
236164
  let query = song + ' ' + artist
236132
- let url = \`https://itunes.apple.com/search?term=\${query}&limit=25&media=music\`
236165
+ let url = 'https://itunes.apple.com/search?term=' + query + '&limit=25&media=music'
236133
236166
  let response = httpFetch({ url, cache: 3600 })
236134
236167
 
236135
236168
  // Format results for LLM analysis
236136
236169
  let results = response.results || []
236137
- let tracks = results.map(x => \`"\${x.trackName}" by \${x.artistName} (\${x.collectionName})\`)
236170
+ let tracks = results.map(x => '"' + x.trackName + '" by ' + x.artistName + ' (' + x.collectionName + ')')
236138
236171
  let trackList = tracks.join('\\n')
236139
236172
 
236140
236173
  // Schema.response from example - much cleaner!
@@ -236142,11 +236175,7 @@ var examples = [
236142
236175
  covers: [{ track: '', artist: '', album: '' }]
236143
236176
  })
236144
236177
 
236145
- let prompt = \`Search results for "\${song}" by \${artist}:
236146
-
236147
- \${trackList}
236148
-
236149
- List cover versions (tracks NOT by \${artist}).\`
236178
+ let prompt = 'Search results for "' + song + '" by ' + artist + ':\\n\\n' + trackList + '\\n\\nList cover versions (tracks NOT by ' + artist + ').'
236150
236179
 
236151
236180
  let llmResponse = llmPredict({ prompt, options: { responseFormat: schema } })
236152
236181
  let parsed = JSON.parse(llmResponse)
@@ -236160,19 +236189,15 @@ List cover versions (tracks NOT by \${artist}).\`
236160
236189
  requiresApi: true,
236161
236190
  code: `function mathAssistant({ question = 'What is 23 * 47 + 156?' }) {
236162
236191
  // First, ask LLM to extract the calculation
236163
- let extractPrompt = \`Extract the math expression from this question. Return ONLY the expression, nothing else.
236164
- Question: \${question}\`
236192
+ let extractPrompt = 'Extract the math expression from this question. Return ONLY the expression, nothing else.\\nQuestion: ' + question
236165
236193
  let expression = llmPredict({ prompt: extractPrompt })
236166
236194
 
236167
236195
  // Evaluate the expression (simple eval simulation)
236168
- let calcPrompt = \`Calculate: \${expression}
236169
- Return ONLY the numeric result.\`
236196
+ let calcPrompt = 'Calculate: ' + expression + '\\nReturn ONLY the numeric result.'
236170
236197
  let calcResult = llmPredict({ prompt: calcPrompt })
236171
236198
 
236172
236199
  // Format the answer
236173
- let answerPrompt = \`The user asked: "\${question}"
236174
- The calculated result is: \${calcResult}
236175
- Write a brief, friendly response with the answer.\`
236200
+ let answerPrompt = 'The user asked: "' + question + '"\\nThe calculated result is: ' + calcResult + '\\nWrite a brief, friendly response with the answer.'
236176
236201
  let answer = llmPredict({ prompt: answerPrompt })
236177
236202
 
236178
236203
  return { question, expression: expression.trim(), result: calcResult.trim(), answer }
@@ -236185,26 +236210,15 @@ Write a brief, friendly response with the answer.\`
236185
236210
  requiresApi: true,
236186
236211
  code: `function collaborativeWriting({ topic = 'the future of renewable energy' }) {
236187
236212
  // Agent 1: Research Agent - generates key points
236188
- let researchPrompt = \`You are a research agent. Generate 3 key facts or points about: \${topic}
236189
- Format as a numbered list. Be concise.\`
236213
+ let researchPrompt = 'You are a research agent. Generate 3 key facts or points about: ' + topic + '\\nFormat as a numbered list. Be concise.'
236190
236214
  let research = llmPredict({ prompt: researchPrompt })
236191
236215
 
236192
236216
  // Agent 2: Writer Agent - creates content from research
236193
- let writerPrompt = \`You are a writer agent. Using these research points:
236194
-
236195
- \${research}
236196
-
236197
- Write a short, engaging paragraph (2-3 sentences) about \${topic}.
236198
- Make it informative and accessible.\`
236217
+ let writerPrompt = 'You are a writer agent. Using these research points:\\n\\n' + research + '\\n\\nWrite a short, engaging paragraph (2-3 sentences) about ' + topic + '.\\nMake it informative and accessible.'
236199
236218
  let article = llmPredict({ prompt: writerPrompt })
236200
236219
 
236201
236220
  // Agent 3: Editor Agent - reviews and improves
236202
- let editorPrompt = \`You are an editor agent. Review this draft:
236203
-
236204
- "\${article}"
236205
-
236206
- Suggest one specific improvement. Then provide the improved version.
236207
- Format: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"\`
236221
+ let editorPrompt = 'You are an editor agent. Review this draft:\\n\\n"' + article + '"\\n\\nSuggest one specific improvement. Then provide the improved version.\\nFormat: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"'
236208
236222
  let edited = llmPredict({ prompt: editorPrompt })
236209
236223
 
236210
236224
  return {
@@ -236290,36 +236304,16 @@ Format: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"\`
236290
236304
  requiresApi: true,
236291
236305
  code: `function solveWithCode({ problem = 'Calculate the 10th Fibonacci number' }) {
236292
236306
  // System prompt with AsyncJS rules and example
236293
- let systemContext = \`You write AsyncJS code. AsyncJS is a JavaScript subset.
236294
-
236295
- RULES:
236296
- - NO: async, await, new, class, this, var, for loops
236297
- - Use let for variables, while for loops
236298
- - Return an object: return { result }
236299
-
236300
- EXAMPLE (factorial):
236301
- function solve() {
236302
- let result = 1
236303
- let i = 5
236304
- while (i > 1) {
236305
- result = result * i
236306
- i = i - 1
236307
- }
236308
- return { result }
236309
- }
236310
-
236311
- Return ONLY the function code, nothing else.\`
236307
+ let systemContext = 'You write AsyncJS code. AsyncJS is a JavaScript subset.\\n\\nRULES:\\n- NO: async, await, new, class, this, var, for loops\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE (factorial):\\nfunction solve() {\\n let result = 1\\n let i = 5\\n while (i > 1) {\\n result = result * i\\n i = i - 1\\n }\\n return { result }\\n}\\n\\nReturn ONLY the function code, nothing else.'
236312
236308
 
236313
- let prompt = \`\${systemContext}
236314
-
236315
- Write a function called "solve" that: \${problem}\`
236309
+ let prompt = systemContext + '\\n\\nWrite a function called "solve" that: ' + problem
236316
236310
 
236317
236311
  let response = llmPredict({ prompt })
236318
236312
 
236319
236313
  // Clean up code - remove markdown fences, fix escapes, extract function
236320
236314
  let code = response
236321
- code = code.replace(/\`\`\`(?:javascript|js|asyncjs)?\\n?/g, '')
236322
- code = code.replace(/\\n?\`\`\`/g, '')
236315
+ code = code.replace(/\\\`\\\`\\\`(?:javascript|js|asyncjs)?\\n?/g, '')
236316
+ code = code.replace(/\\n?\\\`\\\`\\\`/g, '')
236323
236317
  code = code.replace(/\\\\n/g, '\\n')
236324
236318
  code = code.replace(/\\\\t/g, '\\t')
236325
236319
  code = code.replace(/\\\\"/g, '"')
@@ -236357,43 +236351,22 @@ Write a function called "solve" that: \${problem}\`
236357
236351
  requiresApi: true,
236358
236352
  code: `function generateCode({ task = 'Calculate the factorial of n' }) {
236359
236353
  // System prompt with AsyncJS rules and complete example
236360
- let systemContext = \`You write AsyncJS code. AsyncJS is a subset of JavaScript.
236361
-
236362
- RULES:
236363
- - Types by example: fn(n: 5) means required number param with example value 5
236364
- - NO: async, await, new, class, this, var, for, generator functions (function*)
236365
- - Use let for variables, while for loops
236366
- - Return an object: return { result }
236367
-
236368
- EXAMPLE - calculating sum of 1 to n:
236369
- function sumTo(n: 10) {
236370
- let sum = 0
236371
- let i = 1
236372
- while (i <= n) {
236373
- sum = sum + i
236374
- i = i + 1
236375
- }
236376
- return { result: sum }
236377
- }\`
236354
+ let systemContext = 'You write AsyncJS code. AsyncJS is a subset of JavaScript.\\n\\nRULES:\\n- Types by example: fn(n: 5) means required number param with example value 5\\n- NO: async, await, new, class, this, var, for, generator functions (function*)\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE - calculating sum of 1 to n:\\nfunction sumTo(n: 10) {\\n let sum = 0\\n let i = 1\\n while (i <= n) {\\n sum = sum + i\\n i = i + 1\\n }\\n return { result: sum }\\n}'
236378
236355
 
236379
236356
  let schema = Schema.response('generated_code', {
236380
236357
  code: '',
236381
236358
  description: ''
236382
236359
  })
236383
236360
 
236384
- let prompt = \`\${systemContext}
236385
-
236386
- Write an AsyncJS function for: \${task}
236387
-
236388
- Return ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).\`
236361
+ let prompt = systemContext + '\\n\\nWrite an AsyncJS function for: ' + task + '\\n\\nReturn ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).'
236389
236362
 
236390
236363
  let response = llmPredict({ prompt, options: { responseFormat: schema } })
236391
236364
  let result = JSON.parse(response)
236392
236365
 
236393
236366
  // Clean up any markdown fences and fix escaped newlines
236394
236367
  let code = result.code
236395
- code = code.replace(/\`\`\`(?:javascript|js)?\\n?/g, '')
236396
- code = code.replace(/\\n?\`\`\`/g, '')
236368
+ code = code.replace(/\\\`\\\`\\\`(?:javascript|js)?\\n?/g, '')
236369
+ code = code.replace(/\\n?\\\`\\\`\\\`/g, '')
236397
236370
  code = code.replace(/\\\\n/g, '\\n')
236398
236371
  code = code.replace(/\\\\t/g, '\\t')
236399
236372
  code = code.trim()
@@ -243813,6 +243786,489 @@ async function resolveImports(source) {
243813
243786
  return { importMap: { imports }, errors, localModules };
243814
243787
  }
243815
243788
 
243789
+ // demo/src/playground-shared.ts
243790
+ var TJS_RUNTIME_STUB = `
243791
+ globalThis.__tjs = {
243792
+ version: '0.0.0',
243793
+ pushStack: () => {},
243794
+ popStack: () => {},
243795
+ getStack: () => [],
243796
+ typeError: (path, expected, value) => {
243797
+ const actual = value === null ? 'null' : typeof value;
243798
+ const err = new Error(\\\`Expected \\\${expected} for '\\\${path}', got \\\${actual}\\\`);
243799
+ err.name = 'MonadicError';
243800
+ err.path = path;
243801
+ err.expected = expected;
243802
+ err.actual = actual;
243803
+ return err;
243804
+ },
243805
+ createRuntime: function() { return this; },
243806
+ Is: (a, b) => {
243807
+ if (a === b) return true;
243808
+ if (a === null || b === null) return a === b;
243809
+ if (typeof a !== typeof b) return false;
243810
+ if (typeof a !== 'object') return false;
243811
+ if (Array.isArray(a) && Array.isArray(b)) {
243812
+ if (a.length !== b.length) return false;
243813
+ return a.every((v, i) => globalThis.__tjs.Is(v, b[i]));
243814
+ }
243815
+ const keysA = Object.keys(a);
243816
+ const keysB = Object.keys(b);
243817
+ if (keysA.length !== keysB.length) return false;
243818
+ return keysA.every(k => globalThis.__tjs.Is(a[k], b[k]));
243819
+ },
243820
+ IsNot: (a, b) => !globalThis.__tjs.Is(a, b),
243821
+ };`;
243822
+ var CONSOLE_CAPTURE_SCRIPT = `
243823
+ const _log = console.log;
243824
+ console.log = (...args) => {
243825
+ _log(...args);
243826
+ parent.postMessage({ type: 'console', message: args.map(a => {
243827
+ if (typeof a !== 'object' || a === null) return String(a);
243828
+ try {
243829
+ return JSON.stringify(a, null, 2);
243830
+ } catch {
243831
+ return String(a);
243832
+ }
243833
+ }).join(' ') }, '*');
243834
+ };`;
243835
+ function buildIframeDoc(options2) {
243836
+ const {
243837
+ cssContent,
243838
+ htmlContent,
243839
+ importMapScript,
243840
+ jsCode,
243841
+ importStatements = [],
243842
+ parentBindings = false,
243843
+ autoCallTjsFunction = false
243844
+ } = options2;
243845
+ const parentBindingsScript = parentBindings ? `
243846
+ if (parent.run) window.run = parent.run.bind(parent);
243847
+ if (parent.runAgent) window.runAgent = parent.runAgent.bind(parent);
243848
+ if (parent.getIdToken) window.getIdToken = parent.getIdToken.bind(parent);` : "";
243849
+ const useSeparateScripts = importStatements.length > 0;
243850
+ const executionCode = autoCallTjsFunction ? `
243851
+ const __execStart = performance.now();
243852
+ ${jsCode}
243853
+
243854
+ // Try to call the function if it exists and show result
243855
+ const funcName = Object.keys(window).find(k => {
243856
+ try { return typeof window[k] === 'function' && window[k].__tjs; }
243857
+ catch { return false; }
243858
+ });
243859
+ if (funcName) {
243860
+ const __callStart = performance.now();
243861
+ const result = window[funcName]();
243862
+ const __execTime = performance.now() - __callStart;
243863
+ parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
243864
+ if (result !== undefined) {
243865
+ if (result instanceof Node) {
243866
+ document.body.append(result);
243867
+ parent.postMessage({ type: 'hasPreviewContent' }, '*');
243868
+ } else {
243869
+ console.log('Result:', result);
243870
+ }
243871
+ }
243872
+ } else {
243873
+ const __execTime = performance.now() - __execStart;
243874
+ parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
243875
+ }` : `
243876
+ const __execStart = performance.now();
243877
+ ${jsCode}
243878
+ const __execTime = performance.now() - __execStart;
243879
+ parent.postMessage({ type: 'timing', execTime: __execTime }, '*');`;
243880
+ if (useSeparateScripts) {
243881
+ return `<!DOCTYPE html>
243882
+ <html>
243883
+ <head>
243884
+ <style>${cssContent}</style>
243885
+ ${importMapScript}
243886
+ </head>
243887
+ <body>
243888
+ ${htmlContent}
243889
+ <script>${parentBindingsScript}
243890
+ ${TJS_RUNTIME_STUB}
243891
+ </script>
243892
+ <script type="module">
243893
+ ${importStatements.join(`
243894
+ `)}
243895
+ ${CONSOLE_CAPTURE_SCRIPT}
243896
+
243897
+ const __childrenBefore = document.body.children.length;
243898
+ try {${executionCode}
243899
+ if (document.body.children.length > __childrenBefore) {
243900
+ parent.postMessage({ type: 'hasPreviewContent' }, '*');
243901
+ }
243902
+ } catch (e) {
243903
+ parent.postMessage({ type: 'error', message: e.message }, '*');
243904
+ }
243905
+ </script>
243906
+ </body>
243907
+ </html>`;
243908
+ }
243909
+ return `<!DOCTYPE html>
243910
+ <html>
243911
+ <head>
243912
+ <style>${cssContent}</style>
243913
+ ${importMapScript}
243914
+ </head>
243915
+ <body>
243916
+ ${htmlContent}
243917
+ <script type="module">${parentBindingsScript}
243918
+ ${TJS_RUNTIME_STUB}
243919
+ ${CONSOLE_CAPTURE_SCRIPT}
243920
+
243921
+ const __childrenBefore = document.body.children.length;
243922
+ try {${executionCode}
243923
+ if (document.body.children.length > __childrenBefore) {
243924
+ parent.postMessage({ type: 'hasPreviewContent' }, '*');
243925
+ }
243926
+ } catch (e) {
243927
+ parent.postMessage({ type: 'error', message: e.message }, '*');
243928
+ }
243929
+ </script>
243930
+ </body>
243931
+ </html>`;
243932
+ }
243933
+ function createIframeMessageHandler(callbacks) {
243934
+ return (event) => {
243935
+ if (event.data?.type === "console") {
243936
+ callbacks.onConsole(event.data.message);
243937
+ } else if (event.data?.type === "timing") {
243938
+ callbacks.onTiming(event.data.execTime);
243939
+ } else if (event.data?.type === "hasPreviewContent") {
243940
+ callbacks.onPreviewContent();
243941
+ } else if (event.data?.type === "error") {
243942
+ callbacks.onError(event.data.message);
243943
+ }
243944
+ };
243945
+ }
243946
+ function renderConsoleMessages(messages, consoleEl, goToLine) {
243947
+ const linePattern = /(?:at line |line |Line )(\d+)(?:[:,]?\s*(?:column |col )?(\d+))?|:(\d+):(\d+)/g;
243948
+ const html2 = messages.map((msg) => {
243949
+ const escaped = msg.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
243950
+ return escaped.replace(linePattern, (match, l1, c12, l22, c22) => {
243951
+ const line = l1 || l22;
243952
+ const col = c12 || c22 || "1";
243953
+ return `<span class="clickable-line" data-line="${line}" data-col="${col}">${match}</span>`;
243954
+ });
243955
+ }).join(`
243956
+ `);
243957
+ consoleEl.innerHTML = html2;
243958
+ consoleEl.scrollTop = consoleEl.scrollHeight;
243959
+ consoleEl.querySelectorAll(".clickable-line").forEach((el2) => {
243960
+ el2.addEventListener("click", (e) => {
243961
+ const target = e.currentTarget;
243962
+ const line = parseInt(target.dataset.line || "0", 10);
243963
+ const col = parseInt(target.dataset.col || "1", 10);
243964
+ if (line > 0) {
243965
+ goToLine(line, col);
243966
+ }
243967
+ });
243968
+ });
243969
+ }
243970
+ function renderTestResults(tests, outputEl, editor, goToLine) {
243971
+ if (!tests || tests.length === 0) {
243972
+ outputEl.textContent = "No tests defined";
243973
+ editor.clearMarkers();
243974
+ return { passed: 0, failed: 0 };
243975
+ }
243976
+ const passed = tests.filter((t2) => t2.passed).length;
243977
+ const failed = tests.filter((t2) => !t2.passed).length;
243978
+ const failedTests = tests.filter((t2) => !t2.passed && t2.line);
243979
+ if (failedTests.length > 0) {
243980
+ editor.setMarkers(failedTests.map((t2) => ({
243981
+ line: t2.line,
243982
+ message: t2.error || t2.description,
243983
+ severity: "error"
243984
+ })));
243985
+ } else {
243986
+ editor.clearMarkers();
243987
+ }
243988
+ let html2 = `<div class="test-summary">`;
243989
+ html2 += `<strong>${passed} passed</strong>`;
243990
+ if (failed > 0) {
243991
+ html2 += `, <strong class="test-failed">${failed} failed</strong>`;
243992
+ }
243993
+ html2 += `</div><ul class="test-list">`;
243994
+ for (const test of tests) {
243995
+ const icon = test.passed ? "✓" : "✗";
243996
+ const cls = test.passed ? "test-pass" : "test-fail";
243997
+ const sigBadge = test.isSignatureTest ? ' <span class="sig-badge">signature</span>' : "";
243998
+ const dataLine = test.line ? ` data-line="${test.line}"` : "";
243999
+ html2 += `<li class="${cls}"${dataLine}>${icon} ${test.description}${sigBadge}`;
244000
+ if (!test.passed && test.error) {
244001
+ html2 += `<div class="test-error${test.line ? " clickable-error" : ""}"${dataLine}>${test.error}</div>`;
244002
+ }
244003
+ html2 += `</li>`;
244004
+ }
244005
+ html2 += `</ul>`;
244006
+ outputEl.innerHTML = html2;
244007
+ outputEl.querySelectorAll(".clickable-error").forEach((el2) => {
244008
+ el2.addEventListener("click", (e) => {
244009
+ const line = parseInt(e.currentTarget.dataset.line || "0", 10);
244010
+ if (line > 0) {
244011
+ goToLine(line);
244012
+ }
244013
+ });
244014
+ });
244015
+ return { passed, failed };
244016
+ }
244017
+ function formatExecTime(ms2) {
244018
+ return ms2 < 1 ? `${(ms2 * 1000).toFixed(0)}μs` : `${ms2.toFixed(2)}ms`;
244019
+ }
244020
+ var sharedPlaygroundStyles = {
244021
+ ":host": {
244022
+ display: "flex",
244023
+ flexDirection: "column",
244024
+ height: "100%",
244025
+ flex: "1 1 auto",
244026
+ background: "var(--background, #fff)",
244027
+ color: "var(--text-color, #1f2937)",
244028
+ fontFamily: "system-ui, sans-serif"
244029
+ },
244030
+ ":host .run-btn": {
244031
+ display: "flex",
244032
+ alignItems: "center",
244033
+ gap: "4px",
244034
+ padding: "6px 12px",
244035
+ background: "var(--brand-color, #3d4a6b)",
244036
+ color: "var(--brand-text-color, white)",
244037
+ border: "none",
244038
+ borderRadius: "6px",
244039
+ cursor: "pointer",
244040
+ fontWeight: "500",
244041
+ fontSize: "14px"
244042
+ },
244043
+ ":host .run-btn:hover:not(:disabled)": {
244044
+ filter: "brightness(1.1)"
244045
+ },
244046
+ ":host .run-btn:disabled": {
244047
+ opacity: "0.6",
244048
+ cursor: "not-allowed"
244049
+ },
244050
+ ":host .toolbar-separator": {
244051
+ width: "1px",
244052
+ height: "20px",
244053
+ background: "var(--code-border, #d1d5db)"
244054
+ },
244055
+ ":host .build-flags": {
244056
+ display: "flex",
244057
+ alignItems: "center",
244058
+ gap: "12px"
244059
+ },
244060
+ ":host .flag-label": {
244061
+ display: "flex",
244062
+ alignItems: "center",
244063
+ gap: "4px",
244064
+ fontSize: "13px",
244065
+ color: "var(--text-color, #6b7280)",
244066
+ cursor: "pointer",
244067
+ userSelect: "none"
244068
+ },
244069
+ ":host .flag-label:hover": {
244070
+ color: "var(--text-color, #374151)"
244071
+ },
244072
+ ':host .flag-label input[type="checkbox"]': {
244073
+ margin: "0",
244074
+ cursor: "pointer",
244075
+ accentColor: "var(--brand-color, #3d4a6b)"
244076
+ },
244077
+ ":host .revert-btn": {
244078
+ display: "flex",
244079
+ alignItems: "center",
244080
+ gap: "4px",
244081
+ padding: "6px 12px",
244082
+ background: "var(--code-background, #e5e7eb)",
244083
+ color: "var(--text-color, #374151)",
244084
+ border: "1px solid var(--code-border, #d1d5db)",
244085
+ borderRadius: "6px",
244086
+ cursor: "pointer",
244087
+ fontWeight: "500",
244088
+ fontSize: "14px",
244089
+ transition: "opacity 0.2s"
244090
+ },
244091
+ ":host .revert-btn:hover:not(:disabled)": {
244092
+ background: "#fef3c7",
244093
+ borderColor: "#f59e0b",
244094
+ color: "#92400e"
244095
+ },
244096
+ ":host .revert-btn:disabled": {
244097
+ cursor: "default"
244098
+ },
244099
+ ":host .elastic": {
244100
+ flex: "1"
244101
+ },
244102
+ ":host .status-bar": {
244103
+ fontSize: "13px",
244104
+ color: "var(--text-color, #6b7280)",
244105
+ opacity: "0.7"
244106
+ },
244107
+ ":host .status-bar.error": {
244108
+ color: "#dc2626",
244109
+ opacity: "1"
244110
+ },
244111
+ ":host tosi-tabs > [name]": {
244112
+ background: "var(--background, #fff)",
244113
+ color: "var(--text-color, #1f2937)"
244114
+ },
244115
+ ":host .editor-wrapper": {
244116
+ flex: "1 1 auto",
244117
+ height: "100%",
244118
+ minHeight: "300px",
244119
+ position: "relative",
244120
+ overflow: "hidden"
244121
+ },
244122
+ ":host .editor-wrapper code-mirror": {
244123
+ display: "block",
244124
+ position: "absolute",
244125
+ top: "0",
244126
+ left: "0",
244127
+ right: "0",
244128
+ bottom: "0"
244129
+ },
244130
+ ":host .preview-frame": {
244131
+ width: "100%",
244132
+ height: "100%",
244133
+ border: "none",
244134
+ background: "var(--background, #fff)"
244135
+ },
244136
+ ":host .docs-output": {
244137
+ display: "block",
244138
+ padding: "12px 16px",
244139
+ fontSize: "14px",
244140
+ fontFamily: "system-ui, sans-serif",
244141
+ color: "var(--text-color, inherit)",
244142
+ background: "var(--background, #fff)",
244143
+ height: "100%",
244144
+ overflow: "auto"
244145
+ },
244146
+ ":host .docs-output h2": {
244147
+ fontSize: "1.25em",
244148
+ marginTop: "0",
244149
+ marginBottom: "0.5em",
244150
+ color: "var(--text-color, #1f2937)"
244151
+ },
244152
+ ":host .docs-output pre": {
244153
+ background: "var(--code-background, #f3f4f6)",
244154
+ padding: "8px 12px",
244155
+ borderRadius: "6px",
244156
+ overflow: "auto",
244157
+ fontSize: "13px"
244158
+ },
244159
+ ":host .docs-output code": {
244160
+ fontFamily: "ui-monospace, monospace",
244161
+ fontSize: "0.9em"
244162
+ },
244163
+ ":host .docs-output p": {
244164
+ margin: "0.75em 0",
244165
+ lineHeight: "1.5"
244166
+ },
244167
+ ":host .docs-output h3": {
244168
+ fontSize: "1em",
244169
+ marginTop: "1em",
244170
+ marginBottom: "0.5em"
244171
+ },
244172
+ ":host .docs-output ul": {
244173
+ paddingLeft: "1.5em",
244174
+ margin: "0.5em 0"
244175
+ },
244176
+ ":host .docs-output li": {
244177
+ marginBottom: "0.25em"
244178
+ },
244179
+ ":host .docs-output hr": {
244180
+ border: "none",
244181
+ borderTop: "1px solid var(--code-border, #e5e7eb)",
244182
+ margin: "1.5em 0"
244183
+ },
244184
+ ":host .tests-output": {
244185
+ padding: "12px",
244186
+ fontSize: "14px",
244187
+ fontFamily: "system-ui, sans-serif",
244188
+ color: "var(--text-color, inherit)",
244189
+ background: "var(--background, #fff)",
244190
+ height: "100%",
244191
+ overflow: "auto"
244192
+ },
244193
+ ":host .test-summary": {
244194
+ marginBottom: "12px",
244195
+ paddingBottom: "8px",
244196
+ borderBottom: "1px solid var(--code-border, #e5e7eb)"
244197
+ },
244198
+ ":host .test-failed": {
244199
+ color: "#dc2626"
244200
+ },
244201
+ ":host .test-list": {
244202
+ listStyle: "none",
244203
+ padding: "0",
244204
+ margin: "0"
244205
+ },
244206
+ ":host .test-list li": {
244207
+ padding: "4px 0"
244208
+ },
244209
+ ":host .test-pass": {
244210
+ color: "#16a34a"
244211
+ },
244212
+ ":host .test-fail": {
244213
+ color: "#dc2626"
244214
+ },
244215
+ ":host .test-error": {
244216
+ marginLeft: "20px",
244217
+ marginTop: "4px",
244218
+ padding: "8px",
244219
+ background: "rgba(220, 38, 38, 0.1)",
244220
+ borderRadius: "4px",
244221
+ fontSize: "13px",
244222
+ fontFamily: "var(--font-mono, monospace)"
244223
+ },
244224
+ ":host .clickable-error": {
244225
+ cursor: "pointer",
244226
+ textDecoration: "underline",
244227
+ textDecorationStyle: "dotted"
244228
+ },
244229
+ ":host .clickable-error:hover": {
244230
+ background: "rgba(220, 38, 38, 0.2)"
244231
+ },
244232
+ ":host .sig-badge": {
244233
+ fontSize: "11px",
244234
+ padding: "2px 6px",
244235
+ marginLeft: "8px",
244236
+ background: "rgba(99, 102, 241, 0.1)",
244237
+ color: "#6366f1",
244238
+ borderRadius: "4px"
244239
+ },
244240
+ ":host .console-header": {
244241
+ padding: "4px 12px",
244242
+ background: "var(--code-background, #f3f4f6)",
244243
+ fontSize: "12px",
244244
+ fontWeight: "500",
244245
+ color: "var(--text-color, #6b7280)",
244246
+ opacity: "0.7",
244247
+ borderBottom: "1px solid var(--code-border, #e5e7eb)"
244248
+ },
244249
+ ":host .console-output": {
244250
+ flex: "1",
244251
+ margin: "0",
244252
+ padding: "8px 12px",
244253
+ background: "var(--code-background, #f3f4f6)",
244254
+ color: "var(--text-color, #1f2937)",
244255
+ fontSize: "12px",
244256
+ fontFamily: "ui-monospace, monospace",
244257
+ overflow: "auto",
244258
+ whiteSpace: "pre-wrap"
244259
+ },
244260
+ ":host .clickable-line": {
244261
+ cursor: "pointer",
244262
+ color: "#2563eb",
244263
+ textDecoration: "underline",
244264
+ textDecorationStyle: "dotted"
244265
+ },
244266
+ ":host .clickable-line:hover": {
244267
+ color: "#1d4ed8",
244268
+ background: "rgba(37, 99, 235, 0.1)"
244269
+ }
244270
+ };
244271
+
243816
244272
  // demo/src/tjs-playground.ts
243817
244273
  init_module_store();
243818
244274
 
@@ -244088,28 +244544,7 @@ class TJSPlayground extends g {
244088
244544
  iframe.src = "about:blank";
244089
244545
  };
244090
244546
  renderConsole() {
244091
- const linePattern = /(?:at line |line |Line )(\d+)(?:[:,]?\s*(?:column |col )?(\d+))?|:(\d+):(\d+)/g;
244092
- const html2 = this.consoleMessages.map((msg) => {
244093
- const escaped = msg.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
244094
- return escaped.replace(linePattern, (match, l1, c12, l22, c22) => {
244095
- const line = l1 || l22;
244096
- const col = c12 || c22 || "1";
244097
- return `<span class="clickable-line" data-line="${line}" data-col="${col}">${match}</span>`;
244098
- });
244099
- }).join(`
244100
- `);
244101
- this.parts.console.innerHTML = html2;
244102
- this.parts.console.scrollTop = this.parts.console.scrollHeight;
244103
- this.parts.console.querySelectorAll(".clickable-line").forEach((el2) => {
244104
- el2.addEventListener("click", (e) => {
244105
- const target = e.currentTarget;
244106
- const line = parseInt(target.dataset.line || "0", 10);
244107
- const col = parseInt(target.dataset.col || "1", 10);
244108
- if (line > 0) {
244109
- this.goToSourceLine(line, col);
244110
- }
244111
- });
244112
- });
244547
+ renderConsoleMessages(this.consoleMessages, this.parts.console, (line, col) => this.goToSourceLine(line, col));
244113
244548
  }
244114
244549
  toggleTests = () => {
244115
244550
  this.buildFlags.tests = this.parts.testsToggle.checked;
@@ -244156,7 +244591,7 @@ class TJSPlayground extends g {
244156
244591
  this.updateDocs(result);
244157
244592
  const tests = result.testResults || [];
244158
244593
  const failed = tests.filter((t2) => !t2.passed).length;
244159
- const timeStr = this.lastTranspileTime < 1 ? `${(this.lastTranspileTime * 1000).toFixed(0)}μs` : `${this.lastTranspileTime.toFixed(2)}ms`;
244594
+ const timeStr = formatExecTime(this.lastTranspileTime);
244160
244595
  if (failed > 0) {
244161
244596
  this.parts.statusBar.textContent = `Transpiled in ${timeStr} with ${failed} test failure${failed > 1 ? "s" : ""}`;
244162
244597
  this.parts.statusBar.classList.add("error");
@@ -244203,53 +244638,8 @@ class TJSPlayground extends g {
244203
244638
  };
244204
244639
  updateTestResults(result) {
244205
244640
  const tests = result.testResults;
244206
- if (!tests || tests.length === 0) {
244207
- this.parts.testsOutput.textContent = "No tests defined";
244208
- this.updateTestsTabLabel(0, 0);
244209
- this.parts.tjsEditor.clearMarkers();
244210
- return;
244211
- }
244212
- const passed = tests.filter((t2) => t2.passed).length;
244213
- const failed = tests.filter((t2) => !t2.passed).length;
244641
+ const { passed, failed } = renderTestResults(tests, this.parts.testsOutput, this.parts.tjsEditor, (line) => this.goToSourceLine(line));
244214
244642
  this.updateTestsTabLabel(passed, failed);
244215
- const failedTests = tests.filter((t2) => !t2.passed && t2.line);
244216
- if (failedTests.length > 0) {
244217
- this.parts.tjsEditor.setMarkers(failedTests.map((t2) => ({
244218
- line: t2.line,
244219
- message: t2.error || t2.description,
244220
- severity: "error"
244221
- })));
244222
- } else {
244223
- this.parts.tjsEditor.clearMarkers();
244224
- }
244225
- let html2 = `<div class="test-summary">`;
244226
- html2 += `<strong>${passed} passed</strong>`;
244227
- if (failed > 0) {
244228
- html2 += `, <strong class="test-failed">${failed} failed</strong>`;
244229
- }
244230
- html2 += `</div><ul class="test-list">`;
244231
- for (const test of tests) {
244232
- const icon = test.passed ? "✓" : "✗";
244233
- const cls = test.passed ? "test-pass" : "test-fail";
244234
- const sigBadge = test.isSignatureTest ? ' <span class="sig-badge">signature</span>' : "";
244235
- const clickable = !test.passed && test.line ? ' class="clickable-error"' : "";
244236
- const dataLine = test.line ? ` data-line="${test.line}"` : "";
244237
- html2 += `<li class="${cls}"${dataLine}>${icon} ${test.description}${sigBadge}`;
244238
- if (!test.passed && test.error) {
244239
- html2 += `<div${clickable}${dataLine} class="test-error${test.line ? " clickable-error" : ""}">${test.error}</div>`;
244240
- }
244241
- html2 += `</li>`;
244242
- }
244243
- html2 += `</ul>`;
244244
- this.parts.testsOutput.innerHTML = html2;
244245
- this.parts.testsOutput.querySelectorAll(".clickable-error").forEach((el2) => {
244246
- el2.addEventListener("click", (e) => {
244247
- const line = parseInt(e.currentTarget.dataset.line || "0", 10);
244248
- if (line > 0) {
244249
- this.goToSourceLine(line);
244250
- }
244251
- });
244252
- });
244253
244643
  }
244254
244644
  updateTestsTabLabel(passed, failed) {
244255
244645
  const tabs = this.parts.outputTabs;
@@ -244498,6 +244888,7 @@ class TJSPlayground extends g {
244498
244888
  this.parts.runBtn.disabled = false;
244499
244889
  return;
244500
244890
  }
244891
+ this.parts.outputTabs.value = 0;
244501
244892
  this.parts.statusBar.textContent = "Running...";
244502
244893
  try {
244503
244894
  const wasmCompiled = this.lastTranspileResult.wasmCompiled;
@@ -244538,131 +244929,29 @@ class TJSPlayground extends g {
244538
244929
  importStatements.push(match);
244539
244930
  return "";
244540
244931
  });
244541
- const iframeDoc = `<!DOCTYPE html>
244542
- <html>
244543
- <head>
244544
- <style>${cssContent}</style>
244545
- ${importMapScript}
244546
- </head>
244547
- <body>
244548
- ${htmlContent}
244549
- <!-- TJS Runtime stub must be set up BEFORE imports execute -->
244550
- <script>
244551
- // Expose parent's run/runAgent/getIdToken in iframe for playground convenience
244552
- if (parent.run) window.run = parent.run.bind(parent);
244553
- if (parent.runAgent) window.runAgent = parent.runAgent.bind(parent);
244554
- if (parent.getIdToken) window.getIdToken = parent.getIdToken.bind(parent);
244555
-
244556
- // TJS runtime stub - must stay in sync with src/lang/runtime.ts
244557
- // TODO: Eliminate this once transpiler emits self-contained code
244558
- // See: src/lang/emitters/js.ts for the plan to inline runtime functions
244559
- globalThis.__tjs = {
244560
- version: '0.0.0',
244561
- pushStack: () => {},
244562
- popStack: () => {},
244563
- getStack: () => [],
244564
- typeError: (path, expected, value) => {
244565
- const actual = value === null ? 'null' : typeof value;
244566
- const err = new Error(\`Expected \${expected} for '\${path}', got \${actual}\`);
244567
- err.name = 'MonadicError';
244568
- err.path = path;
244569
- err.expected = expected;
244570
- err.actual = actual;
244571
- return err;
244572
- },
244573
- createRuntime: function() { return this; },
244574
- Is: (a, b) => {
244575
- if (a === b) return true;
244576
- if (a === null || b === null) return a === b;
244577
- if (typeof a !== typeof b) return false;
244578
- if (typeof a !== 'object') return false;
244579
- if (Array.isArray(a) && Array.isArray(b)) {
244580
- if (a.length !== b.length) return false;
244581
- return a.every((v, i) => globalThis.__tjs.Is(v, b[i]));
244582
- }
244583
- const keysA = Object.keys(a);
244584
- const keysB = Object.keys(b);
244585
- if (keysA.length !== keysB.length) return false;
244586
- return keysA.every(k => globalThis.__tjs.Is(a[k], b[k]));
244587
- },
244588
- IsNot: (a, b) => !globalThis.__tjs.Is(a, b),
244589
- };
244590
- </script>
244591
- <script type="module">
244592
- // Import statements must be at the top of the module
244593
- ${importStatements.join(`
244594
- `)}
244595
-
244596
- // Capture console.log
244597
- const _log = console.log;
244598
- console.log = (...args) => {
244599
- _log(...args);
244600
- parent.postMessage({ type: 'console', message: args.map(a => {
244601
- if (typeof a !== 'object' || a === null) return String(a);
244602
- try {
244603
- return JSON.stringify(a, null, 2);
244604
- } catch {
244605
- return String(a);
244606
- }
244607
- }).join(' ') }, '*');
244608
- };
244609
-
244610
- try {
244611
- // WASM blocks are pre-compiled and embedded in the transpiled code
244612
- // They auto-instantiate via the async IIFE at the top of the code
244613
-
244614
- const __execStart = performance.now();
244615
- ${codeWithoutImports}
244616
-
244617
- // Try to call the function if it exists and show result
244618
- const funcName = Object.keys(window).find(k => {
244619
- try { return typeof window[k] === 'function' && window[k].__tjs; }
244620
- catch { return false; }
244932
+ const iframeDoc = buildIframeDoc({
244933
+ cssContent,
244934
+ htmlContent,
244935
+ importMapScript,
244936
+ jsCode: codeWithoutImports,
244937
+ importStatements,
244938
+ parentBindings: true,
244939
+ autoCallTjsFunction: true
244621
244940
  });
244622
- if (funcName) {
244623
- const __callStart = performance.now();
244624
- const result = window[funcName]();
244625
- const __execTime = performance.now() - __callStart;
244626
- parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
244627
- if (result !== undefined) {
244628
- // If result is a DOM node, append it; otherwise log it
244629
- if (result instanceof Node) {
244630
- document.body.append(result);
244631
- parent.postMessage({ type: 'hasPreviewContent' }, '*');
244632
- } else {
244633
- console.log('Result:', result);
244634
- }
244635
- }
244636
- } else {
244637
- // No TJS function found, report total parse/exec time
244638
- const __execTime = performance.now() - __execStart;
244639
- parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
244640
- }
244641
- // Check if body has content after execution
244642
- if (document.body.children.length > 0) {
244643
- parent.postMessage({ type: 'hasPreviewContent' }, '*');
244644
- }
244645
- } catch (e) {
244646
- parent.postMessage({ type: 'error', message: e.message }, '*');
244647
- }
244648
- </script>
244649
- </body>
244650
- </html>`;
244651
- const messageHandler = (event) => {
244652
- if (event.data?.type === "console") {
244653
- this.log(event.data.message);
244654
- } else if (event.data?.type === "timing") {
244655
- const execTime = event.data.execTime;
244656
- const execStr = execTime < 1 ? `${(execTime * 1000).toFixed(0)}μs` : `${execTime.toFixed(2)}ms`;
244657
- this.parts.consoleHeader.textContent = `Console — executed in ${execStr}`;
244658
- } else if (event.data?.type === "hasPreviewContent") {
244941
+ const messageHandler = createIframeMessageHandler({
244942
+ onConsole: (message) => this.log(message),
244943
+ onTiming: (execTime) => {
244944
+ this.parts.consoleHeader.textContent = `Console — executed in ${formatExecTime(execTime)}`;
244945
+ },
244946
+ onPreviewContent: () => {
244659
244947
  this.parts.outputTabs.value = 1;
244660
- } else if (event.data?.type === "error") {
244661
- this.log(`Error: ${event.data.message}`);
244948
+ },
244949
+ onError: (message) => {
244950
+ this.log(`Error: ${message}`);
244662
244951
  this.parts.statusBar.textContent = "Runtime error";
244663
244952
  this.parts.statusBar.classList.add("error");
244664
244953
  }
244665
- };
244954
+ });
244666
244955
  window.addEventListener("message", messageHandler);
244667
244956
  const iframe = this.parts.previewFrame;
244668
244957
  const blob = new Blob([iframeDoc], { type: "text/html" });
@@ -244729,15 +245018,7 @@ class TJSPlayground extends g {
244729
245018
  var tjsPlayground = TJSPlayground.elementCreator({
244730
245019
  tag: "tjs-playground",
244731
245020
  styleSpec: {
244732
- ":host": {
244733
- display: "flex",
244734
- flexDirection: "column",
244735
- height: "100%",
244736
- flex: "1 1 auto",
244737
- background: "var(--background, #fff)",
244738
- color: "var(--text-color, #1f2937)",
244739
- fontFamily: "system-ui, sans-serif"
244740
- },
245021
+ ...sharedPlaygroundStyles,
244741
245022
  ":host .tjs-toolbar": {
244742
245023
  display: "flex",
244743
245024
  alignItems: "center",
@@ -244746,53 +245027,6 @@ var tjsPlayground = TJSPlayground.elementCreator({
244746
245027
  background: "var(--code-background, #f3f4f6)",
244747
245028
  borderBottom: "1px solid var(--code-border, #e5e7eb)"
244748
245029
  },
244749
- ":host .run-btn": {
244750
- display: "flex",
244751
- alignItems: "center",
244752
- gap: "4px",
244753
- padding: "6px 12px",
244754
- background: "var(--brand-color, #3d4a6b)",
244755
- color: "var(--brand-text-color, white)",
244756
- border: "none",
244757
- borderRadius: "6px",
244758
- cursor: "pointer",
244759
- fontWeight: "500",
244760
- fontSize: "14px"
244761
- },
244762
- ":host .run-btn:hover:not(:disabled)": {
244763
- filter: "brightness(1.1)"
244764
- },
244765
- ":host .run-btn:disabled": {
244766
- opacity: "0.6",
244767
- cursor: "not-allowed"
244768
- },
244769
- ":host .toolbar-separator": {
244770
- width: "1px",
244771
- height: "20px",
244772
- background: "var(--code-border, #d1d5db)"
244773
- },
244774
- ":host .build-flags": {
244775
- display: "flex",
244776
- alignItems: "center",
244777
- gap: "12px"
244778
- },
244779
- ":host .flag-label": {
244780
- display: "flex",
244781
- alignItems: "center",
244782
- gap: "4px",
244783
- fontSize: "13px",
244784
- color: "var(--text-color, #6b7280)",
244785
- cursor: "pointer",
244786
- userSelect: "none"
244787
- },
244788
- ":host .flag-label:hover": {
244789
- color: "var(--text-color, #374151)"
244790
- },
244791
- ':host .flag-label input[type="checkbox"]': {
244792
- margin: "0",
244793
- cursor: "pointer",
244794
- accentColor: "var(--brand-color, #3d4a6b)"
244795
- },
244796
245030
  ":host .module-name-input": {
244797
245031
  padding: "6px 10px",
244798
245032
  border: "1px solid var(--code-border, #d1d5db)",
@@ -244830,40 +245064,6 @@ var tjsPlayground = TJSPlayground.elementCreator({
244830
245064
  color: "var(--brand-text-color, white)",
244831
245065
  borderColor: "var(--brand-color, #3d4a6b)"
244832
245066
  },
244833
- ":host .revert-btn": {
244834
- display: "flex",
244835
- alignItems: "center",
244836
- gap: "4px",
244837
- padding: "6px 12px",
244838
- background: "var(--code-background, #e5e7eb)",
244839
- color: "var(--text-color, #374151)",
244840
- border: "1px solid var(--code-border, #d1d5db)",
244841
- borderRadius: "6px",
244842
- cursor: "pointer",
244843
- fontWeight: "500",
244844
- fontSize: "14px",
244845
- transition: "opacity 0.2s"
244846
- },
244847
- ":host .revert-btn:hover:not(:disabled)": {
244848
- background: "#fef3c7",
244849
- borderColor: "#f59e0b",
244850
- color: "#92400e"
244851
- },
244852
- ":host .revert-btn:disabled": {
244853
- cursor: "default"
244854
- },
244855
- ":host .elastic": {
244856
- flex: "1"
244857
- },
244858
- ":host .status-bar": {
244859
- fontSize: "13px",
244860
- color: "var(--text-color, #6b7280)",
244861
- opacity: "0.7"
244862
- },
244863
- ":host .status-bar.error": {
244864
- color: "#dc2626",
244865
- opacity: "1"
244866
- },
244867
245067
  ":host .tjs-main": {
244868
245068
  display: "flex",
244869
245069
  flex: "1 1 auto",
@@ -244879,25 +245079,6 @@ var tjsPlayground = TJSPlayground.elementCreator({
244879
245079
  background: "var(--background, #fff)",
244880
245080
  overflow: "hidden"
244881
245081
  },
244882
- ":host tosi-tabs > [name]": {
244883
- background: "var(--background, #fff)",
244884
- color: "var(--text-color, #1f2937)"
244885
- },
244886
- ":host .editor-wrapper": {
244887
- flex: "1 1 auto",
244888
- height: "100%",
244889
- minHeight: "300px",
244890
- position: "relative",
244891
- overflow: "hidden"
244892
- },
244893
- ":host .editor-wrapper code-mirror": {
244894
- display: "block",
244895
- position: "absolute",
244896
- top: "0",
244897
- left: "0",
244898
- right: "0",
244899
- bottom: "0"
244900
- },
244901
245082
  ":host .js-output": {
244902
245083
  margin: "0",
244903
245084
  padding: "12px",
@@ -244909,151 +245090,11 @@ var tjsPlayground = TJSPlayground.elementCreator({
244909
245090
  height: "100%",
244910
245091
  whiteSpace: "pre-wrap"
244911
245092
  },
244912
- ":host .preview-frame": {
244913
- width: "100%",
244914
- height: "100%",
244915
- border: "none",
244916
- background: "var(--background, #fff)"
244917
- },
244918
- ":host .docs-output": {
244919
- display: "block",
244920
- padding: "12px 16px",
244921
- fontSize: "14px",
244922
- fontFamily: "system-ui, sans-serif",
244923
- color: "var(--text-color, inherit)",
244924
- background: "var(--background, #fff)",
244925
- height: "100%",
244926
- overflow: "auto"
244927
- },
244928
- ":host .docs-output h2": {
244929
- fontSize: "1.25em",
244930
- marginTop: "0",
244931
- marginBottom: "0.5em",
244932
- color: "var(--text-color, #1f2937)"
244933
- },
244934
- ":host .docs-output pre": {
244935
- background: "var(--code-background, #f3f4f6)",
244936
- padding: "8px 12px",
244937
- borderRadius: "6px",
244938
- overflow: "auto",
244939
- fontSize: "13px"
244940
- },
244941
- ":host .docs-output code": {
244942
- fontFamily: "ui-monospace, monospace",
244943
- fontSize: "0.9em"
244944
- },
244945
- ":host .docs-output p": {
244946
- margin: "0.75em 0",
244947
- lineHeight: "1.5"
244948
- },
244949
- ":host .docs-output h3": {
244950
- fontSize: "1em",
244951
- marginTop: "1em",
244952
- marginBottom: "0.5em"
244953
- },
244954
- ":host .docs-output ul": {
244955
- paddingLeft: "1.5em",
244956
- margin: "0.5em 0"
244957
- },
244958
- ":host .docs-output li": {
244959
- marginBottom: "0.25em"
244960
- },
244961
- ":host .docs-output hr": {
244962
- border: "none",
244963
- borderTop: "1px solid var(--code-border, #e5e7eb)",
244964
- margin: "1.5em 0"
244965
- },
244966
- ":host .tests-output": {
244967
- padding: "12px",
244968
- fontSize: "14px",
244969
- fontFamily: "system-ui, sans-serif",
244970
- color: "var(--text-color, inherit)",
244971
- background: "var(--background, #fff)",
244972
- height: "100%",
244973
- overflow: "auto"
244974
- },
244975
- ":host .test-summary": {
244976
- marginBottom: "12px",
244977
- paddingBottom: "8px",
244978
- borderBottom: "1px solid var(--code-border, #e5e7eb)"
244979
- },
244980
- ":host .test-failed": {
244981
- color: "#dc2626"
244982
- },
244983
- ":host .test-list": {
244984
- listStyle: "none",
244985
- padding: 0,
244986
- margin: 0
244987
- },
244988
- ":host .test-list li": {
244989
- padding: "4px 0"
244990
- },
244991
- ":host .test-pass": {
244992
- color: "#16a34a"
244993
- },
244994
- ":host .test-fail": {
244995
- color: "#dc2626"
244996
- },
244997
- ":host .test-error": {
244998
- marginLeft: "20px",
244999
- marginTop: "4px",
245000
- padding: "8px",
245001
- background: "rgba(220, 38, 38, 0.1)",
245002
- borderRadius: "4px",
245003
- fontSize: "13px",
245004
- fontFamily: "var(--font-mono, monospace)"
245005
- },
245006
- ":host .clickable-error": {
245007
- cursor: "pointer",
245008
- textDecoration: "underline",
245009
- textDecorationStyle: "dotted"
245010
- },
245011
- ":host .clickable-error:hover": {
245012
- background: "rgba(220, 38, 38, 0.2)"
245013
- },
245014
- ":host .sig-badge": {
245015
- fontSize: "11px",
245016
- padding: "2px 6px",
245017
- marginLeft: "8px",
245018
- background: "rgba(99, 102, 241, 0.1)",
245019
- color: "#6366f1",
245020
- borderRadius: "4px"
245021
- },
245022
245093
  ":host .tjs-console": {
245023
245094
  height: "120px",
245024
245095
  borderTop: "1px solid var(--code-border, #e5e7eb)",
245025
245096
  display: "flex",
245026
245097
  flexDirection: "column"
245027
- },
245028
- ":host .console-header": {
245029
- padding: "4px 12px",
245030
- background: "var(--code-background, #f3f4f6)",
245031
- fontSize: "12px",
245032
- fontWeight: "500",
245033
- color: "var(--text-color, #6b7280)",
245034
- opacity: "0.7",
245035
- borderBottom: "1px solid var(--code-border, #e5e7eb)"
245036
- },
245037
- ":host .console-output": {
245038
- flex: "1",
245039
- margin: "0",
245040
- padding: "8px 12px",
245041
- background: "var(--code-background, #f3f4f6)",
245042
- color: "var(--text-color, #1f2937)",
245043
- fontSize: "12px",
245044
- fontFamily: "ui-monospace, monospace",
245045
- overflow: "auto",
245046
- whiteSpace: "pre-wrap"
245047
- },
245048
- ":host .clickable-line": {
245049
- cursor: "pointer",
245050
- color: "#2563eb",
245051
- textDecoration: "underline",
245052
- textDecorationStyle: "dotted"
245053
- },
245054
- ":host .clickable-line:hover": {
245055
- color: "#1d4ed8",
245056
- background: "rgba(37, 99, 235, 0.1)"
245057
245098
  }
245058
245099
  }
245059
245100
  });
@@ -245174,28 +245215,7 @@ class TSPlayground extends g {
245174
245215
  this.parts.consoleHeader.textContent = "Console";
245175
245216
  };
245176
245217
  renderConsole() {
245177
- const linePattern = /(?:at line |line |Line )(\d+)(?:[:,]?\s*(?:column |col )?(\d+))?|:(\d+):(\d+)/g;
245178
- const html2 = this.consoleMessages.map((msg) => {
245179
- const escaped = msg.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
245180
- return escaped.replace(linePattern, (match, l1, c12, l22, c22) => {
245181
- const line = l1 || l22;
245182
- const col = c12 || c22 || "1";
245183
- return `<span class="clickable-line" data-line="${line}" data-col="${col}">${match}</span>`;
245184
- });
245185
- }).join(`
245186
- `);
245187
- this.parts.console.innerHTML = html2;
245188
- this.parts.console.scrollTop = this.parts.console.scrollHeight;
245189
- this.parts.console.querySelectorAll(".clickable-line").forEach((el2) => {
245190
- el2.addEventListener("click", (e) => {
245191
- const target = e.currentTarget;
245192
- const line = parseInt(target.dataset.line || "0", 10);
245193
- const col = parseInt(target.dataset.col || "1", 10);
245194
- if (line > 0) {
245195
- this.goToSourceLine(line, col);
245196
- }
245197
- });
245198
- });
245218
+ renderConsoleMessages(this.consoleMessages, this.parts.console, (line, col) => this.goToSourceLine(line, col));
245199
245219
  }
245200
245220
  goToSourceLine(line, column = 1) {
245201
245221
  this.parts.inputTabs.value = 0;
@@ -245251,8 +245271,7 @@ class TSPlayground extends g {
245251
245271
  this.parts.jsOutput.textContent = jsResult.code;
245252
245272
  this.updateTestResults(jsResult.testResults || []);
245253
245273
  this.updateDocs(jsResult);
245254
- const formatTime2 = (t2) => t2 < 1 ? `${(t2 * 1000).toFixed(0)}μs` : `${t2.toFixed(2)}ms`;
245255
- this.parts.statusBar.textContent = `TS→TJS ${formatTime2(this.lastTsToTjsTime)} + TJS→JS ${formatTime2(this.lastTjsToJsTime)} = ${formatTime2(this.lastTranspileTime)}`;
245274
+ this.parts.statusBar.textContent = `TS→TJS ${formatExecTime(this.lastTsToTjsTime)} + TJS→JS ${formatExecTime(this.lastTjsToJsTime)} = ${formatExecTime(this.lastTranspileTime)}`;
245256
245275
  this.parts.statusBar.classList.remove("error");
245257
245276
  } catch (jsError) {
245258
245277
  this.parts.jsOutput.textContent = `// TJS -> JS Error:
@@ -245291,50 +245310,7 @@ class TSPlayground extends g {
245291
245310
  `);
245292
245311
  }
245293
245312
  updateTestResults(tests) {
245294
- if (!tests || tests.length === 0) {
245295
- this.parts.testsOutput.textContent = "No tests defined";
245296
- this.parts.tsEditor.clearMarkers();
245297
- return;
245298
- }
245299
- const passed = tests.filter((t2) => t2.passed).length;
245300
- const failed = tests.filter((t2) => !t2.passed).length;
245301
- const failedTests = tests.filter((t2) => !t2.passed && t2.line);
245302
- if (failedTests.length > 0) {
245303
- this.parts.tsEditor.setMarkers(failedTests.map((t2) => ({
245304
- line: t2.line,
245305
- message: t2.error || t2.description,
245306
- severity: "error"
245307
- })));
245308
- } else {
245309
- this.parts.tsEditor.clearMarkers();
245310
- }
245311
- let html2 = `<div class="test-summary">`;
245312
- html2 += `<strong>${passed} passed</strong>`;
245313
- if (failed > 0) {
245314
- html2 += `, <strong class="test-failed">${failed} failed</strong>`;
245315
- }
245316
- html2 += `</div><ul class="test-list">`;
245317
- for (const test of tests) {
245318
- const icon = test.passed ? "✓" : "✗";
245319
- const cls = test.passed ? "test-pass" : "test-fail";
245320
- const sigBadge = test.isSignatureTest ? ' <span class="sig-badge">signature</span>' : "";
245321
- const dataLine = test.line ? ` data-line="${test.line}"` : "";
245322
- html2 += `<li class="${cls}"${dataLine}>${icon} ${test.description}${sigBadge}`;
245323
- if (!test.passed && test.error) {
245324
- html2 += `<div class="test-error${test.line ? " clickable-error" : ""}"${dataLine}>${test.error}</div>`;
245325
- }
245326
- html2 += `</li>`;
245327
- }
245328
- html2 += `</ul>`;
245329
- this.parts.testsOutput.innerHTML = html2;
245330
- this.parts.testsOutput.querySelectorAll(".clickable-error").forEach((el2) => {
245331
- el2.addEventListener("click", (e) => {
245332
- const line = parseInt(e.currentTarget.dataset.line || "0", 10);
245333
- if (line > 0) {
245334
- this.goToSourceLine(line);
245335
- }
245336
- });
245337
- });
245313
+ renderTestResults(tests, this.parts.testsOutput, this.parts.tsEditor, (line) => this.goToSourceLine(line));
245338
245314
  }
245339
245315
  updateDocs(result) {
245340
245316
  const source = this.parts.tsEditor.value;
@@ -245363,6 +245339,7 @@ class TSPlayground extends g {
245363
245339
  this.log("Cannot run - transpilation failed");
245364
245340
  return;
245365
245341
  }
245342
+ this.parts.outputTabs.value = 1;
245366
245343
  this.parts.statusBar.textContent = "Running...";
245367
245344
  try {
245368
245345
  const htmlContent = this.parts.htmlEditor.value;
@@ -245382,81 +245359,26 @@ class TSPlayground extends g {
245382
245359
  importMapScript = `<script type="importmap">${JSON.stringify(importMap)}</script>`;
245383
245360
  }
245384
245361
  }
245385
- const iframeDoc = `<!DOCTYPE html>
245386
- <html>
245387
- <head>
245388
- <style>${cssContent}</style>
245389
- ${importMapScript}
245390
- </head>
245391
- <body>
245392
- ${htmlContent}
245393
- <script type="module">
245394
- // TJS Runtime stub for iframe execution
245395
- globalThis.__tjs = {
245396
- version: '0.0.0',
245397
- pushStack: () => {},
245398
- popStack: () => {},
245399
- getStack: () => [],
245400
- typeError: (path, expected, value) => {
245401
- const actual = value === null ? 'null' : typeof value;
245402
- const err = new Error(\`Expected \${expected} for '\${path}', got \${actual}\`);
245403
- err.name = 'MonadicError';
245404
- err.path = path;
245405
- err.expected = expected;
245406
- err.actual = actual;
245407
- return err;
245408
- },
245409
- createRuntime: function() { return this; },
245410
- Is: (a, b) => {
245411
- if (a === b) return true;
245412
- if (a === null || b === null) return a === b;
245413
- if (typeof a !== typeof b) return false;
245414
- if (typeof a !== 'object') return false;
245415
- if (Array.isArray(a) && Array.isArray(b)) {
245416
- if (a.length !== b.length) return false;
245417
- return a.every((v, i) => globalThis.__tjs.Is(v, b[i]));
245418
- }
245419
- const keysA = Object.keys(a);
245420
- const keysB = Object.keys(b);
245421
- if (keysA.length !== keysB.length) return false;
245422
- return keysA.every(k => globalThis.__tjs.Is(a[k], b[k]));
245423
- },
245424
- IsNot: (a, b) => !globalThis.__tjs.Is(a, b),
245425
- };
245426
-
245427
- // Capture console.log
245428
- const _log = console.log;
245429
- console.log = (...args) => {
245430
- _log(...args);
245431
- parent.postMessage({ type: 'console', message: args.map(a =>
245432
- typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)
245433
- ).join(' ') }, '*');
245434
- };
245435
-
245436
- try {
245437
- const __execStart = performance.now();
245438
- ${jsCode}
245439
- const __execTime = performance.now() - __execStart;
245440
- parent.postMessage({ type: 'timing', execTime: __execTime }, '*');
245441
- } catch (e) {
245442
- parent.postMessage({ type: 'error', message: e.message }, '*');
245443
- }
245444
- </script>
245445
- </body>
245446
- </html>`;
245447
- const messageHandler = (event) => {
245448
- if (event.data?.type === "console") {
245449
- this.log(event.data.message);
245450
- } else if (event.data?.type === "timing") {
245451
- const execTime = event.data.execTime;
245452
- const execStr = execTime < 1 ? `${(execTime * 1000).toFixed(0)}μs` : `${execTime.toFixed(2)}ms`;
245453
- this.parts.consoleHeader.textContent = `Console — executed in ${execStr}`;
245454
- } else if (event.data?.type === "error") {
245455
- this.log(`Error: ${event.data.message}`);
245362
+ const iframeDoc = buildIframeDoc({
245363
+ cssContent,
245364
+ htmlContent,
245365
+ importMapScript,
245366
+ jsCode
245367
+ });
245368
+ const messageHandler = createIframeMessageHandler({
245369
+ onConsole: (message) => this.log(message),
245370
+ onTiming: (execTime) => {
245371
+ this.parts.consoleHeader.textContent = `Console executed in ${formatExecTime(execTime)}`;
245372
+ },
245373
+ onPreviewContent: () => {
245374
+ this.parts.outputTabs.value = 2;
245375
+ },
245376
+ onError: (message) => {
245377
+ this.log(`Error: ${message}`);
245456
245378
  this.parts.statusBar.textContent = "Runtime error";
245457
245379
  this.parts.statusBar.classList.add("error");
245458
245380
  }
245459
- };
245381
+ });
245460
245382
  window.addEventListener("message", messageHandler);
245461
245383
  this.parts.previewFrame.srcdoc = iframeDoc;
245462
245384
  setTimeout(() => {
@@ -245497,15 +245419,7 @@ class TSPlayground extends g {
245497
245419
  var tsPlayground = TSPlayground.elementCreator({
245498
245420
  tag: "ts-playground",
245499
245421
  styleSpec: {
245500
- ":host": {
245501
- display: "flex",
245502
- flexDirection: "column",
245503
- height: "100%",
245504
- flex: "1 1 auto",
245505
- background: "var(--background, #fff)",
245506
- color: "var(--text-color, #1f2937)",
245507
- fontFamily: "system-ui, sans-serif"
245508
- },
245422
+ ...sharedPlaygroundStyles,
245509
245423
  ":host .ts-toolbar": {
245510
245424
  display: "flex",
245511
245425
  alignItems: "center",
@@ -245514,83 +245428,11 @@ var tsPlayground = TSPlayground.elementCreator({
245514
245428
  background: "var(--code-background, #f3f4f6)",
245515
245429
  borderBottom: "1px solid var(--code-border, #e5e7eb)"
245516
245430
  },
245517
- ":host .run-btn": {
245518
- display: "flex",
245519
- alignItems: "center",
245520
- gap: "4px",
245521
- padding: "6px 12px",
245522
- background: "var(--brand-color, #3178c6)",
245523
- color: "var(--brand-text-color, white)",
245524
- border: "none",
245525
- borderRadius: "6px",
245526
- cursor: "pointer",
245527
- fontWeight: "500",
245528
- fontSize: "14px"
245529
- },
245530
- ":host .run-btn:hover": {
245531
- filter: "brightness(1.1)"
245532
- },
245533
- ":host .toolbar-separator": {
245534
- width: "1px",
245535
- height: "20px",
245536
- background: "var(--code-border, #d1d5db)"
245537
- },
245538
- ":host .build-flags": {
245539
- display: "flex",
245540
- alignItems: "center",
245541
- gap: "12px"
245542
- },
245543
- ":host .flag-label": {
245544
- display: "flex",
245545
- alignItems: "center",
245546
- gap: "4px",
245547
- fontSize: "13px",
245548
- color: "var(--text-color, #6b7280)",
245549
- cursor: "pointer",
245550
- userSelect: "none"
245551
- },
245552
- ":host .flag-label:hover": {
245553
- color: "var(--text-color, #374151)"
245554
- },
245555
245431
  ':host .flag-label input[type="checkbox"]': {
245556
245432
  margin: "0",
245557
245433
  cursor: "pointer",
245558
245434
  accentColor: "var(--brand-color, #3178c6)"
245559
245435
  },
245560
- ":host .revert-btn": {
245561
- display: "flex",
245562
- alignItems: "center",
245563
- gap: "4px",
245564
- padding: "6px 12px",
245565
- background: "var(--code-background, #e5e7eb)",
245566
- color: "var(--text-color, #374151)",
245567
- border: "1px solid var(--code-border, #d1d5db)",
245568
- borderRadius: "6px",
245569
- cursor: "pointer",
245570
- fontWeight: "500",
245571
- fontSize: "14px",
245572
- transition: "opacity 0.2s"
245573
- },
245574
- ":host .revert-btn:hover:not(:disabled)": {
245575
- background: "#fef3c7",
245576
- borderColor: "#f59e0b",
245577
- color: "#92400e"
245578
- },
245579
- ":host .revert-btn:disabled": {
245580
- cursor: "default"
245581
- },
245582
- ":host .elastic": {
245583
- flex: "1"
245584
- },
245585
- ":host .status-bar": {
245586
- fontSize: "13px",
245587
- color: "var(--text-color, #6b7280)",
245588
- opacity: "0.7"
245589
- },
245590
- ":host .status-bar.error": {
245591
- color: "#dc2626",
245592
- opacity: "1"
245593
- },
245594
245436
  ":host .ts-main": {
245595
245437
  display: "flex",
245596
245438
  flex: "1 1 auto",
@@ -245613,25 +245455,6 @@ var tsPlayground = TSPlayground.elementCreator({
245613
245455
  minHeight: "0",
245614
245456
  height: "100%"
245615
245457
  },
245616
- ":host tosi-tabs > [name]": {
245617
- background: "var(--background, #fff)",
245618
- color: "var(--text-color, #1f2937)"
245619
- },
245620
- ":host .editor-wrapper": {
245621
- flex: "1 1 auto",
245622
- height: "100%",
245623
- minHeight: "300px",
245624
- position: "relative",
245625
- overflow: "hidden"
245626
- },
245627
- ":host .editor-wrapper code-mirror": {
245628
- display: "block",
245629
- position: "absolute",
245630
- top: "0",
245631
- left: "0",
245632
- right: "0",
245633
- bottom: "0"
245634
- },
245635
245458
  ":host .output-toolbar": {
245636
245459
  display: "flex",
245637
245460
  gap: "8px",
@@ -245669,112 +245492,11 @@ var tsPlayground = TSPlayground.elementCreator({
245669
245492
  height: "100%",
245670
245493
  background: "var(--background, #fff)"
245671
245494
  },
245672
- ":host .preview-frame": {
245673
- width: "100%",
245674
- height: "100%",
245675
- border: "none"
245676
- },
245677
- ":host .docs-output": {
245678
- display: "block",
245679
- padding: "12px 16px",
245680
- fontSize: "14px",
245681
- fontFamily: "system-ui, sans-serif",
245682
- color: "var(--text-color, inherit)",
245683
- background: "var(--background, #fff)",
245684
- height: "100%",
245685
- overflow: "auto"
245686
- },
245687
- ":host .tests-output": {
245688
- padding: "12px",
245689
- fontSize: "14px",
245690
- fontFamily: "system-ui, sans-serif",
245691
- color: "var(--text-color, inherit)",
245692
- background: "var(--background, #fff)",
245693
- height: "100%",
245694
- overflow: "auto"
245695
- },
245696
- ":host .test-summary": {
245697
- marginBottom: "12px",
245698
- paddingBottom: "8px",
245699
- borderBottom: "1px solid var(--code-border, #e5e7eb)"
245700
- },
245701
- ":host .test-failed": {
245702
- color: "#dc2626"
245703
- },
245704
- ":host .test-list": {
245705
- listStyle: "none",
245706
- padding: 0,
245707
- margin: 0
245708
- },
245709
- ":host .test-list li": {
245710
- padding: "4px 0"
245711
- },
245712
- ":host .test-pass": {
245713
- color: "#16a34a"
245714
- },
245715
- ":host .test-fail": {
245716
- color: "#dc2626"
245717
- },
245718
- ":host .test-error": {
245719
- marginLeft: "20px",
245720
- marginTop: "4px",
245721
- padding: "8px",
245722
- background: "rgba(220, 38, 38, 0.1)",
245723
- borderRadius: "4px",
245724
- fontSize: "13px",
245725
- fontFamily: "var(--font-mono, monospace)"
245726
- },
245727
- ":host .clickable-error": {
245728
- cursor: "pointer",
245729
- textDecoration: "underline",
245730
- textDecorationStyle: "dotted"
245731
- },
245732
- ":host .clickable-error:hover": {
245733
- background: "rgba(220, 38, 38, 0.2)"
245734
- },
245735
- ":host .sig-badge": {
245736
- fontSize: "11px",
245737
- padding: "2px 6px",
245738
- marginLeft: "8px",
245739
- background: "rgba(99, 102, 241, 0.1)",
245740
- color: "#6366f1",
245741
- borderRadius: "4px"
245742
- },
245743
245495
  ":host .ts-console": {
245744
245496
  height: "120px",
245745
245497
  borderTop: "1px solid var(--code-border, #e5e7eb)",
245746
245498
  display: "flex",
245747
245499
  flexDirection: "column"
245748
- },
245749
- ":host .console-header": {
245750
- padding: "4px 12px",
245751
- background: "var(--code-background, #f3f4f6)",
245752
- fontSize: "12px",
245753
- fontWeight: "500",
245754
- color: "var(--text-color, #6b7280)",
245755
- opacity: "0.7",
245756
- borderBottom: "1px solid var(--code-border, #e5e7eb)"
245757
- },
245758
- ":host .console-output": {
245759
- flex: "1",
245760
- margin: "0",
245761
- padding: "8px 12px",
245762
- background: "var(--code-background, #f3f4f6)",
245763
- color: "var(--text-color, #1f2937)",
245764
- fontSize: "12px",
245765
- fontFamily: "ui-monospace, monospace",
245766
- overflow: "auto",
245767
- whiteSpace: "pre-wrap"
245768
- },
245769
- ":host .clickable-line": {
245770
- cursor: "pointer",
245771
- color: "#2563eb",
245772
- textDecoration: "underline",
245773
- textDecorationStyle: "dotted"
245774
- },
245775
- ":host .clickable-line:hover": {
245776
- color: "#1d4ed8",
245777
- background: "rgba(37, 99, 235, 0.1)"
245778
245500
  }
245779
245501
  }
245780
245502
  });
@@ -246135,25 +245857,26 @@ main()
246135
245857
  description: "Classes are supported with metadata",
246136
245858
  group: "advanced",
246137
245859
  code: `// Classes with typed methods
245860
+ // Note: return types omitted on methods — TJS infers them
246138
245861
 
246139
245862
  class Calculator {
246140
245863
  private value: number = 0
246141
245864
 
246142
- add(n: number): Calculator {
245865
+ add(n: number) {
246143
245866
  this.value += n
246144
245867
  return this
246145
245868
  }
246146
245869
 
246147
- multiply(n: number): Calculator {
245870
+ multiply(n: number) {
246148
245871
  this.value *= n
246149
245872
  return this
246150
245873
  }
246151
245874
 
246152
- getResult(): number {
245875
+ getResult() {
246153
245876
  return this.value
246154
245877
  }
246155
245878
 
246156
- reset(): void {
245879
+ reset() {
246157
245880
  this.value = 0
246158
245881
  }
246159
245882
  }
@@ -266724,11 +266447,10 @@ main()`,
266724
266447
  const todos = new Map()
266725
266448
  let nextId = 1
266726
266449
 
266727
- // Types
266728
- type Todo = { id: number, title: string, completed: boolean, createdAt: string }
266729
- type CreateInput = { title: 'Buy milk' }
266730
- type UpdateInput = { id: 1, title: 'Buy milk', completed: false }
266731
- type FilterInput = { completed: true } | { completed: false } | {}
266450
+ // Types are inferred from function signatures below
266451
+ // Todo: { id: 0, title: '', completed: false, createdAt: '' }
266452
+ // CreateInput: { title: 'Buy milk' }
266453
+ // UpdateInput: { id: 1, title: 'Buy milk', completed: false }
266732
266454
 
266733
266455
  // POST /todos - Create
266734
266456
  export function createTodo(input: { title: 'New todo' })
@@ -267059,7 +266781,9 @@ function createEvent(input: {
267059
266781
  title: input.title,
267060
266782
  start: format(start, 'yyyy-MM-dd'),
267061
266783
  end: format(end, 'yyyy-MM-dd'),
267062
- formatted: \`\\\${input.title}: \\\${format(start, 'MMM d')} - \\\${format(end, 'MMM d, yyyy')}\\`,
266784
+ formatted: \`\${input.title}: \${format(start, 'MMM d')} - \${format(end, 'MMM d, yyyy')}\`
266785
+ }
266786
+ }`,
267063
266787
  language: "tjs",
267064
266788
  description: "Uses date-fns for date formatting via ESM import"
267065
266789
  },
@@ -267081,7 +266805,34 @@ You can import from modules saved in the playground!
267081
266805
  2. Import it by name from another file
267082
266806
 
267083
266807
  ## Try it:
267084
- 1. First, create and save a module named "mymath":`,
266808
+ 1. First, create and save a module named "mymath":
266809
+
266810
+ export function add(a: 0, b: 0) -> 0 {
266811
+ return a + b
266812
+ }
266813
+
266814
+ export function multiply(a: 0, b: 0) -> 0 {
266815
+ return a * b
266816
+ }
266817
+
266818
+ 2. Then run this code (it imports from your saved module)
266819
+ */
266820
+
266821
+ // This imports from a module you saved in the playground
266822
+ // Change 'mymath' to match whatever name you used when saving
266823
+ import { add, multiply } from 'mymath'
266824
+
266825
+ function calculate(x: 0, y: 0) -> 0 {
266826
+ // (x + y) * 2
266827
+ return multiply(add(x, y), 2)
266828
+ }
266829
+
266830
+ test 'calculate combines add and multiply' {
266831
+ expect(calculate(3, 4)).toBe(14) // (3 + 4) * 2 = 14
266832
+ }
266833
+
266834
+ console.log('calculate(3, 4) =', calculate(3, 4))
266835
+ console.log('calculate(10, 5) =', calculate(10, 5))`,
267085
266836
  language: "tjs",
267086
266837
  description: "Import from modules you save in the playground"
267087
266838
  },
@@ -267313,22 +267064,31 @@ document.body.append(
267313
267064
  requiresApi: true,
267314
267065
  code: `function mathAssistant({ question = 'What is 23 * 47 + 156?' }) {
267315
267066
  // First, ask LLM to extract the calculation
267316
- let extractPrompt = \\\`Extract the math expression from this question. Return ONLY the expression, nothing else.
267317
- Question: \\\${question}\\\`
267067
+ let extractPrompt =
267068
+ 'Extract the math expression from this question. Return ONLY the expression, nothing else.\\nQuestion: ' +
267069
+ question
267318
267070
  let expression = llmPredict({ prompt: extractPrompt })
267319
267071
 
267320
267072
  // Evaluate the expression (simple eval simulation)
267321
- let calcPrompt = \\\`Calculate: \\\${expression}
267322
- Return ONLY the numeric result.\\\`
267073
+ let calcPrompt =
267074
+ 'Calculate: ' + expression + '\\nReturn ONLY the numeric result.'
267323
267075
  let calcResult = llmPredict({ prompt: calcPrompt })
267324
267076
 
267325
267077
  // Format the answer
267326
- let answerPrompt = \\\`The user asked: "\\\${question}"
267327
- The calculated result is: \\\${calcResult}
267328
- Write a brief, friendly response with the answer.\\\`
267078
+ let answerPrompt =
267079
+ 'The user asked: "' +
267080
+ question +
267081
+ '"\\nThe calculated result is: ' +
267082
+ calcResult +
267083
+ '\\nWrite a brief, friendly response with the answer.'
267329
267084
  let answer = llmPredict({ prompt: answerPrompt })
267330
267085
 
267331
- return { question, expression: expression.trim(), result: calcResult.trim(), answer }
267086
+ return {
267087
+ question,
267088
+ expression: expression.trim(),
267089
+ result: calcResult.trim(),
267090
+ answer,
267091
+ }
267332
267092
  }`,
267333
267093
  language: "javascript",
267334
267094
  description: "LLM uses a calculator tool (requires llm capability)"
@@ -267344,33 +267104,33 @@ Write a brief, friendly response with the answer.\\\`
267344
267104
  requiresApi: true,
267345
267105
  code: `function collaborativeWriting({ topic = 'the future of renewable energy' }) {
267346
267106
  // Agent 1: Research Agent - generates key points
267347
- let researchPrompt = \\\`You are a research agent. Generate 3 key facts or points about: \\\${topic}
267348
- Format as a numbered list. Be concise.\\\`
267107
+ let researchPrompt =
267108
+ 'You are a research agent. Generate 3 key facts or points about: ' +
267109
+ topic +
267110
+ '\\nFormat as a numbered list. Be concise.'
267349
267111
  let research = llmPredict({ prompt: researchPrompt })
267350
267112
 
267351
267113
  // Agent 2: Writer Agent - creates content from research
267352
- let writerPrompt = \\\`You are a writer agent. Using these research points:
267353
-
267354
- \\\${research}
267355
-
267356
- Write a short, engaging paragraph (2-3 sentences) about \\\${topic}.
267357
- Make it informative and accessible.\\\`
267114
+ let writerPrompt =
267115
+ 'You are a writer agent. Using these research points:\\n\\n' +
267116
+ research +
267117
+ '\\n\\nWrite a short, engaging paragraph (2-3 sentences) about ' +
267118
+ topic +
267119
+ '.\\nMake it informative and accessible.'
267358
267120
  let article = llmPredict({ prompt: writerPrompt })
267359
267121
 
267360
267122
  // Agent 3: Editor Agent - reviews and improves
267361
- let editorPrompt = \\\`You are an editor agent. Review this draft:
267362
-
267363
- "\\\${article}"
267364
-
267365
- Suggest one specific improvement. Then provide the improved version.
267366
- Format: "Suggestion: [your suggestion]\\\\n\\\\nImproved: [improved text]"\\\`
267123
+ let editorPrompt =
267124
+ 'You are an editor agent. Review this draft:\\n\\n"' +
267125
+ article +
267126
+ '"\\n\\nSuggest one specific improvement. Then provide the improved version.\\nFormat: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"'
267367
267127
  let edited = llmPredict({ prompt: editorPrompt })
267368
267128
 
267369
267129
  return {
267370
267130
  topic,
267371
267131
  researchPoints: research,
267372
267132
  firstDraft: article,
267373
- editedVersion: edited
267133
+ editedVersion: edited,
267374
267134
  }
267375
267135
  }`,
267376
267136
  language: "javascript",
@@ -267454,65 +267214,17 @@ Format: "Suggestion: [your suggestion]\\\\n\\\\nImproved: [improved text]"\\\`
267454
267214
  requiresApi: true,
267455
267215
  code: `function solveWithCode({ problem = 'Calculate the 10th Fibonacci number' }) {
267456
267216
  // System prompt with AsyncJS rules and example
267457
- let systemContext = \\\`You write AsyncJS code. AsyncJS is a JavaScript subset.
267217
+ let systemContext =
267218
+ 'You write AsyncJS code. AsyncJS is a JavaScript subset.\\n\\nRULES:\\n- NO: async, await, new, class, this, var, for loops\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE (factorial):\\nfunction solve() {\\n let result = 1\\n let i = 5\\n while (i > 1) {\\n result = result * i\\n i = i - 1\\n }\\n return { result }\\n}\\n\\nReturn ONLY the function code, nothing else.'
267458
267219
 
267459
- RULES:
267460
- - NO: async, await, new, class, this, var, for loops
267461
- - Use let for variables, while for loops
267462
- - Return an object: return { result }
267463
-
267464
- EXAMPLE (factorial):
267465
- function solve() {
267466
- let result = 1
267467
- let i = 5
267468
- while (i > 1) {
267469
- result = result * i
267470
- i = i - 1
267471
- }
267472
- return { result }
267473
- }
267474
-
267475
- Return ONLY the function code, nothing else.\\\`
267476
-
267477
- let prompt = \\\`\\\${systemContext}
267478
-
267479
- Write a function called "solve" that: \\\${problem}\\\`
267220
+ let prompt =
267221
+ systemContext + '\\n\\nWrite a function called "solve" that: ' + problem
267480
267222
 
267481
267223
  let response = llmPredict({ prompt })
267482
267224
 
267483
267225
  // Clean up code - remove markdown fences, fix escapes, extract function
267484
267226
  let code = response
267485
- code = code.replace(/\\\`\\\`\\\`(?:javascript|js|asyncjs)?\\\\n?/g, '')
267486
- code = code.replace(/\\\\n?\\\`\\\`\\\`/g, '')
267487
- code = code.replace(/\\\\\\\\n/g, '\\\\n')
267488
- code = code.replace(/\\\\\\\\t/g, '\\\\t')
267489
- code = code.replace(/\\\\\\\\"/g, '"')
267490
- code = code.trim()
267491
-
267492
- // Try to extract just the function if there's extra text
267493
- let funcMatch = code.match(/function\\\\s+solve\\\\s*\\\\([^)]*\\\\)\\\\s*\\\\{[\\\\s\\\\S]*\\\\}/)
267494
- if (funcMatch) {
267495
- code = funcMatch[0]
267496
- }
267497
-
267498
- // Validate it looks like a function before running
267499
- if (!code.startsWith('function')) {
267500
- return {
267501
- problem,
267502
- error: 'LLM did not generate valid code',
267503
- rawResponse: response
267504
- }
267505
- }
267506
-
267507
- // Execute the generated code
267508
- let output = runCode({ code, args: {} })
267509
-
267510
- return {
267511
- problem,
267512
- generatedCode: code,
267513
- result: output.result
267514
- }
267515
- }`,
267227
+ code = code.replace(/`,
267516
267228
  language: "javascript",
267517
267229
  description: "LLM writes and runs code to solve a problem (requires llm capability)"
267518
267230
  },
@@ -267527,53 +267239,26 @@ Write a function called "solve" that: \\\${problem}\\\`
267527
267239
  requiresApi: true,
267528
267240
  code: `function generateCode({ task = 'Calculate the factorial of n' }) {
267529
267241
  // System prompt with AsyncJS rules and complete example
267530
- let systemContext = \\\`You write AsyncJS code. AsyncJS is a subset of JavaScript.
267531
-
267532
- RULES:
267533
- - Types by example: fn(n: 5) means required number param with example value 5
267534
- - NO: async, await, new, class, this, var, for, generator functions (function*)
267535
- - Use let for variables, while for loops
267536
- - Return an object: return { result }
267537
-
267538
- EXAMPLE - calculating sum of 1 to n:
267539
- function sumTo(n: 10) {
267540
- let sum = 0
267541
- let i = 1
267542
- while (i <= n) {
267543
- sum = sum + i
267544
- i = i + 1
267545
- }
267546
- return { result: sum }
267547
- }\\\`
267242
+ let systemContext =
267243
+ 'You write AsyncJS code. AsyncJS is a subset of JavaScript.\\n\\nRULES:\\n- Types by example: fn(n: 5) means required number param with example value 5\\n- NO: async, await, new, class, this, var, for, generator functions (function*)\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE - calculating sum of 1 to n:\\nfunction sumTo(n: 10) {\\n let sum = 0\\n let i = 1\\n while (i <= n) {\\n sum = sum + i\\n i = i + 1\\n }\\n return { result: sum }\\n}'
267548
267244
 
267549
267245
  let schema = Schema.response('generated_code', {
267550
267246
  code: '',
267551
- description: ''
267247
+ description: '',
267552
267248
  })
267553
267249
 
267554
- let prompt = \\\`\\\${systemContext}
267555
-
267556
- Write an AsyncJS function for: \\\${task}
267557
-
267558
- Return ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).\\\`
267250
+ let prompt =
267251
+ systemContext +
267252
+ '\\n\\nWrite an AsyncJS function for: ' +
267253
+ task +
267254
+ '\\n\\nReturn ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).'
267559
267255
 
267560
267256
  let response = llmPredict({ prompt, options: { responseFormat: schema } })
267561
267257
  let result = JSON.parse(response)
267562
267258
 
267563
267259
  // Clean up any markdown fences and fix escaped newlines
267564
267260
  let code = result.code
267565
- code = code.replace(/\\\`\\\`\\\`(?:javascript|js)?\\\\n?/g, '')
267566
- code = code.replace(/\\\\n?\\\`\\\`\\\`/g, '')
267567
- code = code.replace(/\\\\\\\\n/g, '\\\\n')
267568
- code = code.replace(/\\\\\\\\t/g, '\\\\t')
267569
- code = code.trim()
267570
-
267571
- return {
267572
- task,
267573
- code,
267574
- description: result.description
267575
- }
267576
- }`,
267261
+ code = code.replace(/`,
267577
267262
  language: "javascript",
267578
267263
  description: "LLM writes AsyncJS code from a description (requires llm capability)"
267579
267264
  },
@@ -267586,7 +267271,12 @@ Return ONLY valid AsyncJS code in the code field. Must start with "function" and
267586
267271
  group: "api",
267587
267272
  order: 7,
267588
267273
  code: `function getWeather({ lat = 37.7749, lon = -122.4194 }) {
267589
- let url = \\\`https://api.open-meteo.com/v1/forecast?latitude=\\\${lat}&longitude=\\\${lon}&current_weather=true\\\`
267274
+ let url =
267275
+ 'https://api.open-meteo.com/v1/forecast?latitude=' +
267276
+ lat +
267277
+ '&longitude=' +
267278
+ lon +
267279
+ '&current_weather=true'
267590
267280
  let response = httpFetch({ url, cache: 1800 })
267591
267281
  let weather = response.current_weather
267592
267282
  return { weather }
@@ -267603,12 +267293,17 @@ Return ONLY valid AsyncJS code in the code field. Must start with "function" and
267603
267293
  group: "api",
267604
267294
  order: 8,
267605
267295
  code: `function searchMusic({ query = 'Beatles', limit = 5 }) {
267606
- let url = \\\`https://itunes.apple.com/search?term=\\\${query}&limit=\\\${limit}&media=music\\\`
267296
+ let url =
267297
+ 'https://itunes.apple.com/search?term=' +
267298
+ query +
267299
+ '&limit=' +
267300
+ limit +
267301
+ '&media=music'
267607
267302
  let response = httpFetch({ url, cache: 3600 })
267608
- let tracks = response.results.map(x => ({
267303
+ let tracks = response.results.map((x) => ({
267609
267304
  artist: x.artistName,
267610
267305
  track: x.trackName,
267611
- album: x.collectionName
267306
+ album: x.collectionName,
267612
267307
  }))
267613
267308
  return { tracks }
267614
267309
  }`,
@@ -267624,12 +267319,17 @@ Return ONLY valid AsyncJS code in the code field. Must start with "function" and
267624
267319
  group: "api",
267625
267320
  order: 9,
267626
267321
  code: `function searchRepos({ query = 'tosijs', perPage = 5 }) {
267627
- let url = \\\`https://api.github.com/search/repositories?q=\\\${query}&per_page=\\\${perPage}&sort=stars\\\`
267322
+ let url =
267323
+ 'https://api.github.com/search/repositories?q=' +
267324
+ query +
267325
+ '&per_page=' +
267326
+ perPage +
267327
+ '&sort=stars'
267628
267328
  let response = httpFetch({ url, cache: 300 })
267629
- let repos = response.items.map(x => ({
267329
+ let repos = response.items.map((x) => ({
267630
267330
  name: x.full_name,
267631
267331
  stars: x.stargazers_count,
267632
- description: x.description
267332
+ description: x.description,
267633
267333
  }))
267634
267334
  return { repos }
267635
267335
  }`,
@@ -269033,24 +268733,33 @@ This minimal dependency approach reduces supply chain risk and bundle size.
269033
268733
  code: `function findCovers({ song = 'Yesterday', artist = 'Beatles' }) {
269034
268734
  // Search iTunes for the song
269035
268735
  let query = song + ' ' + artist
269036
- let url = \\\`https://itunes.apple.com/search?term=\\\${query}&limit=25&media=music\\\`
268736
+ let url =
268737
+ 'https://itunes.apple.com/search?term=' + query + '&limit=25&media=music'
269037
268738
  let response = httpFetch({ url, cache: 3600 })
269038
268739
 
269039
268740
  // Format results for LLM analysis
269040
268741
  let results = response.results || []
269041
- let tracks = results.map(x => \\\`"\\\${x.trackName}" by \\\${x.artistName} (\\\${x.collectionName})\\\`)
269042
- let trackList = tracks.join('\\\\n')
268742
+ let tracks = results.map(
268743
+ (x) =>
268744
+ '"' + x.trackName + '" by ' + x.artistName + ' (' + x.collectionName + ')'
268745
+ )
268746
+ let trackList = tracks.join('\\n')
269043
268747
 
269044
268748
  // Schema.response from example - much cleaner!
269045
268749
  let schema = Schema.response('cover_versions', {
269046
- covers: [{ track: '', artist: '', album: '' }]
268750
+ covers: [{ track: '', artist: '', album: '' }],
269047
268751
  })
269048
268752
 
269049
- let prompt = \\\`Search results for "\\\${song}" by \\\${artist}:
269050
-
269051
- \\\${trackList}
269052
-
269053
- List cover versions (tracks NOT by \\\${artist}).\\\`
268753
+ let prompt =
268754
+ 'Search results for "' +
268755
+ song +
268756
+ '" by ' +
268757
+ artist +
268758
+ ':\\n\\n' +
268759
+ trackList +
268760
+ '\\n\\nList cover versions (tracks NOT by ' +
268761
+ artist +
268762
+ ').'
269054
268763
 
269055
268764
  let llmResponse = llmPredict({ prompt, options: { responseFormat: schema } })
269056
268765
  let parsed = JSON.parse(llmResponse)
@@ -269109,10 +268818,10 @@ List cover versions (tracks NOT by \\\${artist}).\\\`
269109
268818
  code: `function summarize({ source = 'coffee-origins' }) {
269110
268819
  // Fetch text from our sample documents
269111
268820
  // Options: 'coffee-origins', 'ai-history', 'renewable-energy'
269112
- let url = \\\`/texts/\\\${source}.txt\\\`
268821
+ let url = '/texts/' + source + '.txt'
269113
268822
  let text = httpFetch({ url })
269114
268823
 
269115
- let prompt = \\\`Summarize the following text in 2-3 sentences:\\n\\n\\\${text}\\\`
268824
+ let prompt = 'Summarize the following text in 2-3 sentences:\\n\\n' + text
269116
268825
  let summary = llmPredict({ prompt })
269117
268826
  return { source, summary }
269118
268827
  }`,
@@ -269128,17 +268837,19 @@ List cover versions (tracks NOT by \\\${artist}).\\\`
269128
268837
  group: "llm",
269129
268838
  order: 12,
269130
268839
  requiresApi: true,
269131
- code: `function extractInfo({ text = 'John Smith is a 35-year-old software engineer from San Francisco who loves hiking and photography.' }) {
268840
+ code: `function extractInfo({
268841
+ text = 'John Smith is a 35-year-old software engineer from San Francisco who loves hiking and photography.',
268842
+ }) {
269132
268843
  // Schema.response builds responseFormat from an example
269133
268844
  let schema = Schema.response('person_info', {
269134
268845
  name: '',
269135
268846
  age: 0,
269136
268847
  occupation: '',
269137
268848
  location: '',
269138
- hobbies: ['']
268849
+ hobbies: [''],
269139
268850
  })
269140
268851
 
269141
- let prompt = \\\`Extract person info from this text: \\\${text}\\\`
268852
+ let prompt = 'Extract person info from this text: ' + text
269142
268853
  let response = llmPredict({ prompt, options: { responseFormat: schema } })
269143
268854
  let person = JSON.parse(response)
269144
268855
  return { person }
@@ -271270,7 +270981,7 @@ The transpiler generates code like:
271270
270981
  * )
271271
270982
  */
271272
270983
  const add = await (async () => {
271273
- const bytes = Uint8Array.from(atob('AGFzbQEAAAA...'), c => c.charCodeAt(0))
270984
+ const bytes = Uint8Array.from(atob('AGFzbQEAAAA...'), (c) => c.charCodeAt(0))
271274
270985
  const { instance } = await WebAssembly.instantiate(bytes)
271275
270986
  return instance.exports.fn
271276
270987
  })()
@@ -271510,6 +271221,25 @@ Comment inside function`,
271510
271221
  filename: "from-ts.test.ts",
271511
271222
  path: "src/lang/from-ts.test.ts"
271512
271223
  },
271224
+ {
271225
+ title: "icebox",
271226
+ filename: "tasks-f6d70f.md",
271227
+ path: ".haltija/tasks-f6d70f.md",
271228
+ text: `# icebox
271229
+
271230
+ # queued
271231
+
271232
+ # in_progress
271233
+
271234
+ # blocked
271235
+
271236
+ # review
271237
+
271238
+ # done
271239
+
271240
+ # trash
271241
+ `
271242
+ },
271513
271243
  {
271514
271244
  text: `# TJS Platform Cloud Functions
271515
271245
 
@@ -275179,4 +274909,4 @@ if (main) {
275179
274909
  }
275180
274910
  console.log(`%c tjs-lang %c v${VERSION} `, "background: #6366f1; color: white; padding: 2px 6px; border-radius: 3px 0 0 3px;", "background: #374151; color: white; padding: 2px 6px; border-radius: 0 3px 3px 0;");
275181
274911
 
275182
- //# debugId=16FB3CD88E3A52B764756E2164756E21
274912
+ //# debugId=9B23A9B33C90485E64756E2164756E21