@fraczak/k 6.8.0 โ†’ 6.10.1

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 (45) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/CLAUDE.md +1 -1
  3. package/DOCS/BIG_PICTURE.md +3 -5
  4. package/DOCS/CODECS.md +80 -181
  5. package/DOCS/CODEC_ARCHITECTURE.md +194 -0
  6. package/DOCS/REPL.md +69 -51
  7. package/Examples/arithmetics.k +1 -0
  8. package/Examples/poly.k +46 -0
  9. package/backends/llvm/runtime/krt.c +4 -0
  10. package/backends/llvm/src/kvm2llvm.mjs +2 -2
  11. package/backends/llvm/tests/test-llvm.mjs +19 -0
  12. package/backends/wasm/src/kvm2wasm.mjs +4 -0
  13. package/backends/wasm/tests/test-wasm.mjs +4 -0
  14. package/browser/repl-browser.mjs +131 -779
  15. package/browser/shims/process.mjs +7 -0
  16. package/browser/tree-view.mjs +5 -2
  17. package/browser/vfs-data.mjs +14 -14
  18. package/codecs/README.md +7 -0
  19. package/codecs/ieee.mjs +50 -75
  20. package/codecs/int.mjs +83 -62
  21. package/codecs/json-codec.mjs +5 -77
  22. package/codecs/json.mjs +12 -56
  23. package/codecs/k-parse.mjs +3 -19
  24. package/codecs/runtime/codec-sdk.mjs +94 -0
  25. package/codecs/runtime/pattern-k.mjs +45 -0
  26. package/codecs/string-codec.mjs +48 -74
  27. package/codecs/unit.mjs +13 -61
  28. package/codecs/utf16.mjs +13 -56
  29. package/codecs/utf8.mjs +11 -55
  30. package/package.json +1 -1
  31. package/patterns/from-k.mjs +2 -22
  32. package/repl-codecs.mjs +69 -196
  33. package/repl.mjs +177 -281
  34. package/scripts/build-repl-html.mjs +114 -385
  35. package/scripts/run-tests.mjs +1 -0
  36. package/tests/test-codecs.mjs +186 -0
  37. package/tests/test-poly.mjs +44 -0
  38. package/tests/test-repl-html.mjs +163 -68
  39. package/tests/test-repl.mjs +210 -156
  40. package/tests/test-structural-values.mjs +17 -0
  41. package/tests/test-tree-view.mjs +3 -0
  42. package/codecs/example-pipeline.mjs +0 -163
  43. package/codecs/runtime/ieee-pattern.mjs +0 -30
  44. package/codecs/runtime/test-codec.mjs +0 -89
  45. package/codecs/runtime/typeFromValue.mjs +0 -34
@@ -12,7 +12,40 @@ const workspaceRoot = path.resolve(projectRoot, "..");
12
12
 
13
13
  console.log("=== Building Self-Contained K REPL HTML ===");
14
14
 
15
- // 1. Scan and generate browser/vfs-data.mjs
15
+ // 1. Node shims plugin for esbuild
16
+ const nodeShimsPlugin = {
17
+ name: "node-shims",
18
+ setup(build) {
19
+ const shims = {
20
+ "fs": path.join(projectRoot, "browser/shims/fs.mjs"),
21
+ "node:fs": path.join(projectRoot, "browser/shims/fs.mjs"),
22
+ "path": path.join(projectRoot, "browser/shims/path.mjs"),
23
+ "node:path": path.join(projectRoot, "browser/shims/path.mjs"),
24
+ "crypto": path.join(projectRoot, "browser/shims/crypto.mjs"),
25
+ "node:crypto": path.join(projectRoot, "browser/shims/crypto.mjs"),
26
+ "buffer": path.join(projectRoot, "browser/shims/buffer.mjs"),
27
+ "node:buffer": path.join(projectRoot, "browser/shims/buffer.mjs"),
28
+ "os": path.join(projectRoot, "browser/shims/os.mjs"),
29
+ "node:os": path.join(projectRoot, "browser/shims/os.mjs"),
30
+ "process": path.join(projectRoot, "browser/shims/process.mjs"),
31
+ "node:process": path.join(projectRoot, "browser/shims/process.mjs"),
32
+ "readline": path.join(projectRoot, "browser/shims/readline.mjs"),
33
+ "node:readline": path.join(projectRoot, "browser/shims/readline.mjs"),
34
+ "url": path.join(projectRoot, "browser/shims/url.mjs"),
35
+ "node:url": path.join(projectRoot, "browser/shims/url.mjs"),
36
+ "assert": path.join(projectRoot, "browser/shims/assert.mjs"),
37
+ "node:assert": path.join(projectRoot, "browser/shims/assert.mjs"),
38
+ };
39
+
40
+ build.onResolve({ filter: /^(node:)?(fs|path|crypto|buffer|os|process|readline|url|assert)$/ }, (args) => {
41
+ if (shims[args.path]) {
42
+ return { path: shims[args.path] };
43
+ }
44
+ });
45
+ }
46
+ };
47
+
48
+ // 2. Scan and generate browser/vfs-data.mjs
16
49
  console.log("1. Generating VFS data...");
17
50
  const vfsFiles = {};
18
51
 
@@ -40,15 +73,48 @@ for (const filename of verifiedExamples) {
40
73
  }
41
74
  }
42
75
 
43
- // Add verified codecs: json.mjs, utf8.mjs, int.mjs, unit.mjs, ieee.mjs
76
+ // Bundle verified codecs for browser VFS as 100% self-contained ES modules
44
77
  const verifiedCodecs = ["json.mjs", "utf8.mjs", "int.mjs", "unit.mjs", "ieee.mjs"];
45
78
  const codecsDir = path.join(projectRoot, "codecs");
79
+
80
+ const browserCodecSdkShim = `
81
+ export { Value, isProduct, isVariant, withPattern } from "../../Value.mjs";
82
+ export function runCodecCLI() {}
83
+ export function encodeToWire() {}
84
+ export function decodeWire() {}
85
+ export function patternFromFilter(script, options) {
86
+ if (typeof globalThis !== "undefined" && typeof globalThis.patternFromFilter === "function") {
87
+ return globalThis.patternFromFilter(script, options);
88
+ }
89
+ throw new Error("patternFromFilter is not available in browser environment");
90
+ }
91
+ `;
92
+
46
93
  for (const filename of verifiedCodecs) {
47
94
  const fullPath = path.join(codecsDir, filename);
48
95
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
49
- const content = fs.readFileSync(fullPath, "utf8");
50
- vfsFiles[filename] = content;
51
- vfsFiles[`codecs/${filename}`] = content;
96
+ const buildRes = await esbuild.build({
97
+ entryPoints: [fullPath],
98
+ bundle: true,
99
+ format: "esm",
100
+ write: false,
101
+ plugins: [{
102
+ name: "codec-sdk-shim",
103
+ setup(build) {
104
+ build.onResolve({ filter: /(codec-sdk|pattern-k)\.mjs$/ }, (args) => ({
105
+ path: args.path,
106
+ namespace: "codec-sdk-shim"
107
+ }));
108
+ build.onLoad({ filter: /.*/, namespace: "codec-sdk-shim" }, () => ({
109
+ contents: browserCodecSdkShim,
110
+ resolveDir: path.join(codecsDir, "runtime")
111
+ }));
112
+ }
113
+ }]
114
+ });
115
+ const bundledContent = buildRes.outputFiles[0].text;
116
+ vfsFiles[filename] = bundledContent;
117
+ vfsFiles[`codecs/${filename}`] = bundledContent;
52
118
  }
53
119
  }
54
120
 
@@ -59,39 +125,6 @@ const vfsDataPath = path.join(projectRoot, "browser/vfs-data.mjs");
59
125
  fs.writeFileSync(vfsDataPath, vfsDataContent, "utf8");
60
126
  console.log(` Generated ${vfsDataPath} with ${Object.keys(vfsFiles).length} files.`);
61
127
 
62
- // 2. Node shims plugin for esbuild
63
- const nodeShimsPlugin = {
64
- name: "node-shims",
65
- setup(build) {
66
- const shims = {
67
- "fs": path.join(projectRoot, "browser/shims/fs.mjs"),
68
- "node:fs": path.join(projectRoot, "browser/shims/fs.mjs"),
69
- "path": path.join(projectRoot, "browser/shims/path.mjs"),
70
- "node:path": path.join(projectRoot, "browser/shims/path.mjs"),
71
- "crypto": path.join(projectRoot, "browser/shims/crypto.mjs"),
72
- "node:crypto": path.join(projectRoot, "browser/shims/crypto.mjs"),
73
- "buffer": path.join(projectRoot, "browser/shims/buffer.mjs"),
74
- "node:buffer": path.join(projectRoot, "browser/shims/buffer.mjs"),
75
- "os": path.join(projectRoot, "browser/shims/os.mjs"),
76
- "node:os": path.join(projectRoot, "browser/shims/os.mjs"),
77
- "process": path.join(projectRoot, "browser/shims/process.mjs"),
78
- "node:process": path.join(projectRoot, "browser/shims/process.mjs"),
79
- "readline": path.join(projectRoot, "browser/shims/readline.mjs"),
80
- "node:readline": path.join(projectRoot, "browser/shims/readline.mjs"),
81
- "url": path.join(projectRoot, "browser/shims/url.mjs"),
82
- "node:url": path.join(projectRoot, "browser/shims/url.mjs"),
83
- "assert": path.join(projectRoot, "browser/shims/assert.mjs"),
84
- "node:assert": path.join(projectRoot, "browser/shims/assert.mjs"),
85
- };
86
-
87
- build.onResolve({ filter: /^(node:)?(fs|path|crypto|buffer|os|process|readline|url|assert)$/ }, (args) => {
88
- if (shims[args.path]) {
89
- return { path: shims[args.path] };
90
- }
91
- });
92
- }
93
- };
94
-
95
128
  // 3. Bundle browser/repl-browser.mjs
96
129
  console.log("2. Bundling with esbuild...");
97
130
  const entryPoint = path.join(projectRoot, "browser/repl-browser.mjs");
@@ -214,6 +247,25 @@ const htmlContent = `<!DOCTYPE html>
214
247
  border: 1px solid rgba(88, 166, 255, 0.3);
215
248
  padding: 2px 8px;
216
249
  border-radius: 12px;
250
+ cursor: pointer;
251
+ user-select: none;
252
+ transition: all 0.15s ease;
253
+ }
254
+
255
+ .engine-badge:hover {
256
+ background: rgba(88, 166, 255, 0.3);
257
+ border-color: rgba(88, 166, 255, 0.6);
258
+ }
259
+
260
+ .engine-badge.js-mode {
261
+ background: rgba(240, 180, 41, 0.15);
262
+ color: #e3b341;
263
+ border-color: rgba(240, 180, 41, 0.4);
264
+ }
265
+
266
+ .engine-badge.js-mode:hover {
267
+ background: rgba(240, 180, 41, 0.3);
268
+ border-color: rgba(240, 180, 41, 0.7);
217
269
  }
218
270
 
219
271
  .status-indicator {
@@ -1531,110 +1583,6 @@ const htmlContent = `<!DOCTYPE html>
1531
1583
  min-height: 20px;
1532
1584
  }
1533
1585
 
1534
- /* Input Popup Modal */
1535
- .input-popup-dialog {
1536
- max-width: 580px;
1537
- width: 90%;
1538
- }
1539
-
1540
- .input-popup-body {
1541
- padding: 16px 20px;
1542
- display: flex;
1543
- flex-direction: column;
1544
- gap: 12px;
1545
- }
1546
-
1547
- .input-popup-banner {
1548
- background: var(--bg-base);
1549
- border: 1px solid var(--border-color);
1550
- border-radius: 6px;
1551
- padding: 10px 14px;
1552
- font-size: 12px;
1553
- color: var(--text-main);
1554
- display: flex;
1555
- align-items: center;
1556
- gap: 8px;
1557
- }
1558
-
1559
- .input-popup-form-row {
1560
- display: flex;
1561
- gap: 12px;
1562
- flex-wrap: wrap;
1563
- }
1564
-
1565
- .input-popup-field {
1566
- display: flex;
1567
- flex-direction: column;
1568
- }
1569
-
1570
- .input-popup-textarea {
1571
- width: 100%;
1572
- background: #090d12;
1573
- color: var(--text-main);
1574
- border: 1px solid var(--border-color);
1575
- border-radius: 6px;
1576
- padding: 10px 12px;
1577
- font-family: var(--font-mono);
1578
- font-size: 13px;
1579
- line-height: 1.4;
1580
- resize: vertical;
1581
- min-height: 90px;
1582
- max-height: 280px;
1583
- }
1584
-
1585
- .input-popup-textarea:focus {
1586
- border-color: var(--border-focus);
1587
- box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
1588
- outline: none;
1589
- }
1590
-
1591
- .input-popup-samples {
1592
- display: flex;
1593
- align-items: center;
1594
- gap: 8px;
1595
- font-size: 11px;
1596
- color: var(--text-muted);
1597
- flex-wrap: wrap;
1598
- }
1599
-
1600
- .samples-list {
1601
- display: flex;
1602
- gap: 6px;
1603
- flex-wrap: wrap;
1604
- }
1605
-
1606
- .sample-pill {
1607
- background: var(--bg-subtle);
1608
- border: 1px solid var(--border-color);
1609
- color: var(--color-prompt);
1610
- padding: 2px 8px;
1611
- border-radius: 12px;
1612
- cursor: pointer;
1613
- font-family: var(--font-mono);
1614
- font-size: 11px;
1615
- transition: all 0.15s;
1616
- }
1617
-
1618
- .sample-pill:hover {
1619
- background: rgba(88, 166, 255, 0.2);
1620
- border-color: var(--border-focus);
1621
- }
1622
-
1623
- .input-popup-status {
1624
- font-size: 12px;
1625
- min-height: 22px;
1626
- border-radius: 4px;
1627
- padding: 4px 8px;
1628
- background: rgba(22, 27, 34, 0.5);
1629
- }
1630
-
1631
- .input-popup-actions {
1632
- display: flex;
1633
- justify-content: flex-end;
1634
- gap: 8px;
1635
- border-top: 1px solid var(--border-color);
1636
- padding-top: 12px;
1637
- }
1638
1586
 
1639
1587
  /* Responsive */
1640
1588
  @media (max-width: 768px) {
@@ -1680,7 +1628,7 @@ const htmlContent = `<!DOCTYPE html>
1680
1628
  <div class="nav-left">
1681
1629
  <div class="brand">
1682
1630
  <span class="brand-name">k repl</span>
1683
- <span class="engine-badge">wasm-in-process</span>
1631
+ <span id="engine-badge" class="engine-badge" title="Click to toggle engine (wasm/js)">wasm-in-process</span>
1684
1632
  </div>
1685
1633
  <div class="status-indicator">
1686
1634
  <span id="status-dot" class="status-dot ready"></span>
@@ -1706,18 +1654,22 @@ const htmlContent = `<!DOCTYPE html>
1706
1654
  <option value="expr:{10 x, 5 y} plus">{10 x, 5 y} plus (bits)</option>
1707
1655
  <option value="expr:1 succ">1 succ</option>
1708
1656
  <option value="expr:{10 int x, 5 int y} plus">{10 int x, 5 int y} plus (int)</option>
1709
- <option value="expr::codes">:codes (list types)</option>
1657
+ <option value="expr::types">:types (list types)</option>
1710
1658
  <option value="expr::rels">:rels (list relations)</option>
1659
+ <option value="expr::engine">:engine (show engine)</option>
1660
+ <option value="expr::engine wasm">:engine wasm</option>
1661
+ <option value="expr::engine js">:engine js</option>
1711
1662
  </optgroup>
1712
1663
  <optgroup label="Codecs &amp; Format Adapters">
1713
1664
  <option value="expr::codecs">:codecs</option>
1714
1665
  <option value="expr::input">:input (interactive dialog)</option>
1715
- <option value="expr::codec load int">:codec load int</option>
1716
- <option value="expr::codec load utf8">:codec load utf8</option>
1717
- <option value="expr::codec load json">:codec load json</option>
1718
- <option value="expr::codec load ieee">:codec load ieee</option>
1719
- <option value="expr::codec load unit">:codec load unit</option>
1720
- <option value="expr::input {float64 x, string n} json">:input {float64 x, string n} json</option>
1666
+ <option value="expr::codec load int.mjs">:codec load int.mjs</option>
1667
+ <option value="expr::codec load utf8.mjs">:codec load utf8.mjs</option>
1668
+ <option value="expr::codec load json.mjs">:codec load json.mjs</option>
1669
+ <option value="expr::codec load ieee.mjs">:codec load ieee.mjs</option>
1670
+ <option value="expr::codec load unit.mjs">:codec load unit.mjs</option>
1671
+ <option value="expr::input json.mjs {&quot;a&quot;:12,&quot;b&quot;:&quot;hello&quot;}">:input json.mjs {"a":12,"b":"hello"}</option>
1672
+ <option value="expr::input int.mjs [0,1,2]">:input int.mjs [0,1,2]</option>
1721
1673
  </optgroup>
1722
1674
  </select>
1723
1675
  <button id="btn-load-example" class="btn btn-primary">Load</button>
@@ -1735,8 +1687,6 @@ const htmlContent = `<!DOCTYPE html>
1735
1687
  <!-- Codecs Subsystem -->
1736
1688
  <button id="btn-codecs" class="btn btn-secondary" title="Codecs &amp; Custom Serializers (:codecs)">โš™ Codecs <span id="codecs-count-badge" class="badge-count">0</span></button>
1737
1689
 
1738
- <!-- Codec Input Popup -->
1739
- <button id="btn-input-popup" class="btn btn-secondary" title="Open Codec Input Popup (:input)">๐Ÿ“ฅ Input</button>
1740
1690
 
1741
1691
  <!-- Clear -->
1742
1692
  <button id="btn-clear" class="btn btn-secondary" title="Clear terminal screen (Ctrl+L)">โŒซ Clear</button>
@@ -1805,245 +1755,25 @@ const htmlContent = `<!DOCTYPE html>
1805
1755
  <div class="modal-dialog codecs-dialog">
1806
1756
  <div class="modal-header">
1807
1757
  <div style="display: flex; align-items: center; gap: 10px;">
1808
- <h3>Format Codecs &amp; Custom Serializers / Deserializers</h3>
1758
+ <h3>Enabled Codecs</h3>
1809
1759
  <span class="engine-badge">:codecs subsystem</span>
1810
1760
  </div>
1811
1761
  <button id="btn-close-codecs" class="btn-close">&times;</button>
1812
1762
  </div>
1813
1763
  <div class="modal-body codecs-body">
1814
- <!-- Section 1: Available Format Codecs -->
1815
- <div class="codecs-section">
1816
- <div class="codecs-section-header">
1817
- <h4>Available Format Codecs</h4>
1818
- <span class="section-subtitle">External adapters translating between text formats and typed <code>(filter, tree)</code> values. Click 'Load' to activate in current session.</span>
1819
- </div>
1820
- <div class="codec-grid">
1821
- <!-- int card -->
1822
- <div class="codec-card" id="card-codec-int">
1823
- <div class="codec-card-top">
1824
- <div class="codec-card-title">
1825
- <span class="codec-icon">๐Ÿ”ข</span>
1826
- <span class="codec-name">int</span>
1827
- <span id="codec-badge-int" class="codec-status-badge">Inactive</span>
1828
- </div>
1829
- <div class="codec-types">Target: <span class="badge-type">$ int</span></div>
1830
- <div class="codec-desc">Converts decimal integers to/from <code>$ int = &lt; bits '+', bits '-' &gt;</code>.</div>
1831
- </div>
1832
- <div class="codec-card-footer">
1833
- <div class="codec-caps">
1834
- <span class="cap-badge print">Serializer</span>
1835
- <span class="cap-badge parse">Deserializer</span>
1836
- </div>
1837
- <button id="btn-toggle-codec-int" class="btn btn-sm btn-primary">Load</button>
1838
- </div>
1839
- </div>
1840
-
1841
- <!-- utf8 card -->
1842
- <div class="codec-card" id="card-codec-utf8">
1843
- <div class="codec-card-top">
1844
- <div class="codec-card-title">
1845
- <span class="codec-icon">๐Ÿ”ค</span>
1846
- <span class="codec-name">utf8</span>
1847
- <span id="codec-badge-utf8" class="codec-status-badge">Inactive</span>
1848
- </div>
1849
- <div class="codec-types">Target: <span class="badge-type">$ string ($ utf8)</span></div>
1850
- <div class="codec-desc">Converts text to/from <code>$ string</code>, a list of Unicode scalar values (<code>$ unicode</code>) partitioned by Unicode planes.</div>
1851
- </div>
1852
- <div class="codec-card-footer">
1853
- <div class="codec-caps">
1854
- <span class="cap-badge print">Serializer</span>
1855
- <span class="cap-badge parse">Deserializer</span>
1856
- </div>
1857
- <button id="btn-toggle-codec-utf8" class="btn btn-sm btn-primary">Load</button>
1858
- </div>
1859
- </div>
1860
-
1861
- <!-- json card -->
1862
- <div class="codec-card" id="card-codec-json">
1863
- <div class="codec-card-top">
1864
- <div class="codec-card-title">
1865
- <span class="codec-icon">๐Ÿ“‹</span>
1866
- <span class="codec-name">json</span>
1867
- <span id="codec-badge-json" class="codec-status-badge">Inactive</span>
1868
- </div>
1869
- <div class="codec-types">Target: <span class="badge-type parameterized">Pattern / Universal</span></div>
1870
- <div class="codec-desc">Universal / pattern-driven codec. Deserializes JSON text into <i>k</i> values using a pattern expression (e.g. <code>:input ? {float64 x, string n} json</code> or <code>:input json</code>), or serializes <i>k</i> values to JSON.</div>
1871
- </div>
1872
- <div class="codec-card-footer">
1873
- <div class="codec-caps">
1874
- <span class="cap-badge print">Serializer</span>
1875
- <span class="cap-badge parse">Deserializer</span>
1876
- </div>
1877
- <button id="btn-toggle-codec-json" class="btn btn-sm btn-primary">Load</button>
1878
- </div>
1879
- </div>
1880
-
1881
- <!-- ieee card -->
1882
- <div class="codec-card" id="card-codec-ieee">
1883
- <div class="codec-card-top">
1884
- <div class="codec-card-title">
1885
- <span class="codec-icon">๐Ÿงฎ</span>
1886
- <span class="codec-name">ieee</span>
1887
- <span id="codec-badge-ieee" class="codec-status-badge">Inactive</span>
1888
- </div>
1889
- <div class="codec-types">Target: <span class="badge-type">$ float64</span></div>
1890
- <div class="codec-desc">IEEE 754 floating point numbers serialized to/from sign, exponent, and fraction fields.</div>
1891
- </div>
1892
- <div class="codec-card-footer">
1893
- <div class="codec-caps">
1894
- <span class="cap-badge print">Serializer</span>
1895
- <span class="cap-badge parse">Deserializer</span>
1896
- </div>
1897
- <button id="btn-toggle-codec-ieee" class="btn btn-sm btn-primary">Load</button>
1898
- </div>
1899
- </div>
1900
- </div>
1901
- </div>
1902
-
1903
- <!-- Section 2: Active Registered Codecs -->
1904
1764
  <div class="codecs-section">
1905
1765
  <div class="codecs-section-header">
1906
- <h4>Active Registered Codecs in Current Session</h4>
1907
- <span class="section-subtitle">Active codecs intercept evaluation formatting and :input commands</span>
1766
+ <h4>Active Codecs in Current Session</h4>
1767
+ <span class="section-subtitle">Codecs format evaluation results and parse input for <code>:input &lt;codec.mjs&gt; [text]</code>. Codecs can only be loaded through <code>:codec load &lt;file.mjs&gt;</code> in the REPL or through the <b>Files</b> explorer.</span>
1908
1768
  </div>
1909
- <div class="codecs-table-container">
1910
- <table class="codec-table">
1911
- <thead>
1912
- <tr>
1913
- <th>Codec Name</th>
1914
- <th>Target Type Binding</th>
1915
- <th>Capabilities</th>
1916
- <th>Source</th>
1917
- <th>Action</th>
1918
- </tr>
1919
- </thead>
1920
- <tbody id="active-codecs-tbody">
1921
- <!-- dynamically populated by renderCodecsModal() -->
1922
- </tbody>
1923
- </table>
1924
- </div>
1925
- </div>
1926
-
1927
- <!-- Section 3: Custom Codec Studio -->
1928
- <div class="codecs-section">
1929
- <div class="codecs-section-header">
1930
- <h4>Custom Serializer &amp; Deserializer Studio</h4>
1931
- <span class="section-subtitle">Codecs convert external text representations to/from <i>k</i> values defined as pairs <code>(filter, tree)</code>.</span>
1932
- </div>
1933
-
1934
- <div class="codec-studio-box">
1935
- <div class="studio-top-bar">
1936
- <div class="studio-field">
1937
- <label for="codec-preset-select">Preset Template:</label>
1938
- <select id="codec-preset-select">
1939
- <option value="yn">Yes / No ($ yes_no)</option>
1940
- <option value="hex">Hexadecimal 0x... ($ int)</option>
1941
- <option value="currency">Currency $XX.YY ($ int cents)</option>
1942
- <option value="blank">Blank / Custom</option>
1943
- </select>
1944
- </div>
1945
-
1946
- <div class="studio-field">
1947
- <label for="codec-custom-name">Codec Name:</label>
1948
- <input type="text" id="codec-custom-name" placeholder="e.g. yn" value="yn">
1949
- </div>
1950
-
1951
- <div class="studio-field" style="flex: 1;">
1952
- <label for="codec-custom-type">Target Type:</label>
1953
- <select id="codec-custom-type">
1954
- <!-- populated dynamically from state.typeAliases -->
1955
- </select>
1956
- </div>
1957
- </div>
1958
-
1959
- <div class="studio-editors-grid">
1960
- <div class="studio-editor-pane">
1961
- <div class="pane-title">
1962
- <span class="cap-badge parse">Deserializer (parse)</span>
1963
- <span class="pane-hint"><code>(text, context) =&gt; Value</code></span>
1964
- </div>
1965
- <textarea id="codec-custom-parse" class="codec-code-area" spellcheck="false" rows="7"></textarea>
1966
- </div>
1967
-
1968
- <div class="studio-editor-pane">
1969
- <div class="pane-title">
1970
- <span class="cap-badge print">Serializer (print)</span>
1971
- <span class="pane-hint"><code>(value, context) =&gt; string</code></span>
1972
- </div>
1973
- <textarea id="codec-custom-print" class="codec-code-area" spellcheck="false" rows="7"></textarea>
1974
- </div>
1975
- </div>
1976
-
1977
- <div class="studio-test-bar">
1978
- <div class="studio-test-input-wrap">
1979
- <label for="codec-test-input">Test Input:</label>
1980
- <input type="text" id="codec-test-input" placeholder="e.g. yes or 0xFF or $42.50" value="yes">
1981
- </div>
1982
- <button id="btn-test-parse" class="btn btn-secondary btn-sm" title="Test parse function against test input">๐Ÿงช Test Parse</button>
1983
- <button id="btn-test-print" class="btn btn-secondary btn-sm" title="Test print function against current REPL value">๐Ÿ–จ Test Print</button>
1984
- <button id="btn-register-codec" class="btn btn-primary btn-sm" title="Register codec in active REPL state">โœ“ Register in REPL</button>
1985
- <button id="btn-save-codec-vfs" class="btn btn-secondary btn-sm" title="Export as standalone .mjs file to VFS">๐Ÿ’พ Save to VFS</button>
1986
- </div>
1987
-
1988
- <div id="codec-studio-status" class="studio-status-box"></div>
1769
+ <div id="enabled-codecs-grid" class="codec-grid">
1770
+ <!-- dynamically populated: cards for only enabled/loaded codecs -->
1989
1771
  </div>
1990
1772
  </div>
1991
1773
  </div>
1992
1774
  </div>
1993
1775
  </div>
1994
1776
 
1995
- <!-- Input Popup Modal -->
1996
- <div id="input-popup-modal" class="modal">
1997
- <div class="modal-dialog input-popup-dialog">
1998
- <div class="modal-header">
1999
- <div style="display: flex; align-items: center; gap: 10px;">
2000
- <h3>๐Ÿ“ฅ Code Input</h3>
2001
- <span class="engine-badge">:input directive</span>
2002
- </div>
2003
- <button id="btn-close-input-popup" class="btn-close">&times;</button>
2004
- </div>
2005
- <div class="modal-body input-popup-body">
2006
- <div id="input-popup-banner" class="input-popup-banner">
2007
- <span id="input-popup-hint">Enter input string to deserialize into a K value:</span>
2008
- </div>
2009
-
2010
- <div class="input-popup-form-row">
2011
- <div class="studio-field" style="flex: 1;">
2012
- <label for="input-popup-type">Target Pattern:</label>
2013
- <input type="text" id="input-popup-type" list="input-popup-type-list" placeholder="e.g. (...) or $ int or ? {float64 x, string n}" autocomplete="off">
2014
- <datalist id="input-popup-type-list"></datalist>
2015
- </div>
2016
- <div class="studio-field" style="flex: 1;">
2017
- <label for="input-popup-codec">Deserializer Codec:</label>
2018
- <select id="input-popup-codec">
2019
- <!-- populated dynamically -->
2020
- </select>
2021
- <div id="input-popup-codec-hint" class="input-popup-codec-hint" style="font-size:11px;color:var(--text-muted);margin-top:4px;"></div>
2022
- </div>
2023
- </div>
2024
-
2025
- <div class="input-popup-field">
2026
- <div class="pane-title" style="margin-bottom: 6px;">
2027
- <label for="input-popup-text" style="font-size: 11px; color: var(--text-muted); font-weight: 500;">Input String / Payload:</label>
2028
- <span class="pane-hint">Enter: submit &bull; Shift+Enter: newline</span>
2029
- </div>
2030
- <textarea id="input-popup-text" class="input-popup-textarea" rows="4" spellcheck="false" placeholder="Type or paste input string (e.g. 42, yes, hello, {...})..."></textarea>
2031
- </div>
2032
-
2033
- <div id="input-popup-samples" class="input-popup-samples">
2034
- <span class="samples-label">Quick samples:</span>
2035
- <div id="input-samples-list" class="samples-list"></div>
2036
- </div>
2037
-
2038
- <div id="input-popup-status" class="input-popup-status"></div>
2039
-
2040
- <div class="input-popup-actions">
2041
- <button id="btn-cancel-input" class="btn btn-secondary">Cancel</button>
2042
- <button id="btn-submit-input" class="btn btn-primary">โœ“ Submit to REPL</button>
2043
- </div>
2044
- </div>
2045
- </div>
2046
- </div>
2047
1777
 
2048
1778
  <!-- Help Modal -->
2049
1779
  <div id="help-modal" class="modal">
@@ -2148,17 +1878,16 @@ const htmlContent = `<!DOCTYPE html>
2148
1878
  <div class="help-section">
2149
1879
  <h4>REPL Commands</h4>
2150
1880
  <table class="help-table">
1881
+ <tr><td><code>:engine [wasm|js]</code></td><td>Display or switch evaluation engine (<code>wasm</code> or <code>js</code>)</td></tr>
2151
1882
  <tr><td><code>:load &lt;file&gt;</code></td><td>Load a <code>.k</code> source or <code>.klib</code> library into current state</td></tr>
2152
- <tr><td><code>:t &lt;name&gt;</code></td><td>Display the derived type of relation <code>&lt;name&gt;</code></td></tr>
2153
- <tr><td><code>:d &lt;name&gt;</code></td><td>Display definition of relation <code>&lt;name&gt;</code></td></tr>
1883
+ <tr><td><code>:rel &lt;name&gt;</code></td><td>Display definition of relation <code>&lt;name&gt;</code></td></tr>
2154
1884
  <tr><td><code>:type &lt;name&gt;</code></td><td>Display definition of type <code>&lt;name&gt;</code></td></tr>
2155
- <tr><td><code>:codes</code></td><td>List all type aliases and canonical hashes</td></tr>
1885
+ <tr><td><code>:types</code></td><td>List all type aliases and canonical hashes</td></tr>
2156
1886
  <tr><td><code>:rels</code></td><td>List all relation aliases</td></tr>
2157
- <tr><td><code>:codecs</code></td><td>List all registered format codecs</td></tr>
2158
- <tr><td><code>:codec load &lt;name|file&gt;</code></td><td>Load a format codec (<code>int</code>, <code>utf8</code>, <code>json</code>, <code>ieee</code>, <code>unit</code>) or file</td></tr>
2159
- <tr><td><code>:codec unload &lt;name&gt;</code></td><td>Unload a registered codec</td></tr>
2160
- <tr><td><code>:input [&lt;filter=(...)&gt; [codec]]</code></td><td>Read next input line using specified codec (opens dialog in browser)</td></tr>
2161
- <tr><td><code>:time &lt;expr&gt;</code></td><td>Evaluate <code>&lt;expr&gt;</code> and report execution time</td></tr>
1887
+ <tr><td><code>:codecs</code></td><td>List all loaded format codecs</td></tr>
1888
+ <tr><td><code>:codec load &lt;file.mjs&gt;</code></td><td>Load a codec from file (or load through Files)</td></tr>
1889
+ <tr><td><code>:codec unload &lt;file.mjs&gt;</code></td><td>Unload a loaded codec</td></tr>
1890
+ <tr><td><code>:input &lt;codec.mjs&gt; [text]</code></td><td>Parse input using specified codec (opens dialog if text omitted)</td></tr>
2162
1891
  <tr><td><code>:klib &lt;file&gt;</code></td><td>Export current state as a <code>.klib</code> binary file</td></tr>
2163
1892
  <tr><td><code>:reset</code></td><td>Clear state and reset to initial environment</td></tr>
2164
1893
  <tr><td><code>:help</code></td><td>Show command reference</td></tr>
@@ -19,6 +19,7 @@ const tests = [
19
19
  ["tests/test-hash-fuzz.mjs", node, ["tests/test-hash-fuzz.mjs"]],
20
20
  ["tests/test-hash-normalization.mjs", node, ["tests/test-hash-normalization.mjs"]],
21
21
  ["tests/test-structural-values.mjs", node, ["tests/test-structural-values.mjs"]],
22
+ ["tests/test-codecs.mjs", node, ["tests/test-codecs.mjs"]],
22
23
  ["tests/test-deep-wire-value.mjs", node, ["tests/test-deep-wire-value.mjs"]],
23
24
  ["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
24
25
  ["tests/test-kir.mjs", node, ["tests/test-kir.mjs"]],