@fraczak/k 6.3.3 → 6.5.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/CHANGELOG.md +17 -0
- package/DOCS/CODECS.md +258 -0
- package/DOCS/DICTIONARY.md +1172 -0
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
- package/DOCS/KIR_V1.md +221 -0
- package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
- package/DOCS/PROFILING_AND_TRACING.md +196 -0
- package/DOCS/REPL.md +61 -4
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +108 -82
- package/Examples/bnat.k +1 -1
- package/Examples/buda.k +44 -0
- package/Examples/ieee.k +300 -8671
- package/Examples/poly.k +67 -0
- package/README.md +68 -19
- package/Value.mjs +8 -2
- package/augmentation.mjs +9 -7
- package/backend-api.mjs +11 -2
- package/backends/arm64/README.md +104 -0
- package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
- package/backends/arm64/bin/k-arm64-run.mjs +58 -0
- package/backends/arm64/bin/k-arm64.mjs +88 -0
- package/backends/arm64/scripts/run-tests.mjs +41 -0
- package/backends/arm64/src/arm64.mjs +229 -0
- package/backends/arm64/src/cli.mjs +155 -0
- package/backends/arm64/src/kvm2arm64.mjs +906 -0
- package/backends/arm64/src/runtime.c +728 -0
- package/backends/arm64/tests/integration.sh +112 -0
- package/backends/arm64/tests/test-arm64.mjs +240 -0
- package/backends/llvm/README.md +135 -0
- package/backends/llvm/bin/k-llvm-build.mjs +71 -0
- package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
- package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
- package/backends/llvm/bin/k-llvm-run.mjs +90 -0
- package/backends/llvm/runtime/krt.c +1508 -0
- package/backends/llvm/runtime/krt.h +109 -0
- package/backends/llvm/scripts/conformance.mjs +76 -0
- package/backends/llvm/scripts/run-tests.mjs +28 -0
- package/backends/llvm/src/executable.mjs +562 -0
- package/backends/llvm/src/kvm2llvm.mjs +998 -0
- package/backends/llvm/src/llvm.mjs +1092 -0
- package/backends/llvm/tests/composition-driver.c +22 -0
- package/backends/llvm/tests/identity-driver.c +27 -0
- package/backends/llvm/tests/integration.sh +153 -0
- package/backends/llvm/tests/perf-support.mjs +735 -0
- package/backends/llvm/tests/product-driver.c +23 -0
- package/backends/llvm/tests/projection-driver.c +19 -0
- package/backends/llvm/tests/relation-driver.c +29 -0
- package/backends/llvm/tests/test-llvm.mjs +240 -0
- package/backends/llvm/tests/union-driver.c +31 -0
- package/backends/llvm/tests/variant-driver.c +19 -0
- package/backends/llvm/tests/variant-projection-driver.c +21 -0
- package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
- package/backends/wasm/README.md +97 -0
- package/backends/wasm/TECHNICAL-DEBT.md +58 -0
- package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
- package/backends/wasm/bin/k-wasm-run.mjs +140 -0
- package/backends/wasm/bin/k-wasm.mjs +49 -0
- package/backends/wasm/demo_ieee.mjs +319 -0
- package/backends/wasm/runtime.wat +475 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +697 -0
- package/backends/wasm/src/wasm.mjs +1054 -0
- package/backends/wasm/tests/integration.sh +182 -0
- package/backends/wasm/tests/perf-support.mjs +336 -0
- package/backends/wasm/tests/test-wasm.mjs +625 -0
- package/browser/repl-browser.mjs +1584 -0
- package/browser/shims/assert.mjs +19 -0
- package/browser/shims/buffer.mjs +224 -0
- package/browser/shims/crypto.mjs +103 -0
- package/browser/shims/fs.mjs +159 -0
- package/browser/shims/os.mjs +17 -0
- package/browser/shims/path.mjs +54 -0
- package/browser/shims/process.mjs +44 -0
- package/browser/shims/readline.mjs +11 -0
- package/browser/shims/url.mjs +12 -0
- package/browser/vfs-data.mjs +19 -0
- package/codecs/README.md +3 -0
- package/codecs/runtime/show-value.mjs +12 -3
- package/conformance/helper-reuse/case.json +8 -0
- package/conformance/helper-reuse/expected.kv +1 -0
- package/conformance/helper-reuse/input.kv +1 -0
- package/conformance/helper-reuse/program.k +2 -0
- package/conformance/identity/case.json +8 -0
- package/conformance/identity/expected.kv +1 -0
- package/conformance/identity/input.kv +1 -0
- package/conformance/identity/program.k +1 -0
- package/conformance/product-projection/case.json +8 -0
- package/conformance/product-projection/expected.kv +1 -0
- package/conformance/product-projection/input.kv +1 -0
- package/conformance/product-projection/program.k +1 -0
- package/conformance/run.mjs +149 -0
- package/conformance/variant-construction/case.json +8 -0
- package/conformance/variant-construction/expected.kv +1 -0
- package/conformance/variant-construction/input.kv +1 -0
- package/conformance/variant-construction/program.k +1 -0
- package/harness/adapters/arm64.mjs +105 -0
- package/harness/adapters/kvm.mjs +144 -0
- package/harness/adapters/llvm.mjs +118 -0
- package/harness/adapters/native.mjs +77 -0
- package/harness/adapters/wasm.mjs +169 -0
- package/harness/backend.mjs +95 -0
- package/harness/cli.mjs +130 -0
- package/harness/format.mjs +95 -0
- package/harness/index.mjs +25 -0
- package/harness/persistent.mjs +121 -0
- package/harness/registry.mjs +44 -0
- package/harness/suite.mjs +257 -0
- package/harness/suites/poly.mjs +156 -0
- package/harness/trace.mjs +50 -0
- package/harness/util.mjs +92 -0
- package/intrinsics.mjs +41 -0
- package/kir.mjs +399 -0
- package/kvm.mjs +585 -81
- package/objects/README.md +37 -3
- package/objects/compile.mjs +15 -22
- package/objects/inspect.mjs +96 -0
- package/objects/validate.mjs +415 -0
- package/package.json +51 -19
- package/repl-codecs.mjs +105 -6
- package/repl.mjs +335 -49
- package/run.mjs +2 -7
- package/scripts/benchmark.mjs +406 -22
- package/scripts/build-repl-html.mjs +1784 -0
- package/scripts/perf-ieee.mjs +586 -0
- package/scripts/perf-int.mjs +531 -0
- package/scripts/perf-poly.mjs +945 -0
- package/scripts/render-benchmark.mjs +12 -8
- package/scripts/run-tests.mjs +6 -0
- package/tests/integration.sh +19 -2
- package/tests/test-arithmetics.mjs +54 -0
- package/tests/test-ieee-arithmetic.mjs +4 -0
- package/tests/test-kir.mjs +85 -0
- package/tests/test-kvm-polymorphic.mjs +278 -0
- package/tests/test-kvm.mjs +25 -6
- package/tests/test-poly.mjs +129 -0
- package/tests/test-repl-html.mjs +303 -0
- package/tests/test-repl.mjs +144 -1
- package/tests/test-validate-object.mjs +40 -0
- package/a.klib +0 -1
- package/a.kvm +0 -142676
- package/a.wasm +0 -0
- package/benchmark-results.html +0 -532
- package/benchmark-results.json +0 -480
- package/k-vscode/.vscode/launch.json +0 -14
- package/k-vscode/README.md +0 -75
- package/k-vscode/extension.js +0 -273
- package/k-vscode/language-configuration.json +0 -42
- package/k-vscode/package.json +0 -79
- package/k-vscode/snippets/k.json +0 -92
- package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
- package/k-vscode/test/background-ranges.test.js +0 -50
- package/k-vscode/themes/k-contrast-color-theme.json +0 -104
- package/scripts/git-hooks/pre-commit +0 -10
|
@@ -0,0 +1,1784 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import * as esbuild from "esbuild";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const projectRoot = path.resolve(__dirname, "..");
|
|
11
|
+
const workspaceRoot = path.resolve(projectRoot, "..");
|
|
12
|
+
|
|
13
|
+
console.log("=== Building Self-Contained K REPL HTML ===");
|
|
14
|
+
|
|
15
|
+
// 1. Scan and generate browser/vfs-data.mjs
|
|
16
|
+
console.log("1. Generating VFS data...");
|
|
17
|
+
const vfsFiles = {};
|
|
18
|
+
|
|
19
|
+
// Add runtime.wat
|
|
20
|
+
const runtimeWatPath = path.join(projectRoot, "backends/wasm/runtime.wat");
|
|
21
|
+
if (fs.existsSync(runtimeWatPath)) {
|
|
22
|
+
vfsFiles["backends/wasm/runtime.wat"] = fs.readFileSync(runtimeWatPath, "utf8");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Add core.k
|
|
26
|
+
const coreKPath = path.join(projectRoot, "core.k");
|
|
27
|
+
if (fs.existsSync(coreKPath)) {
|
|
28
|
+
vfsFiles["core.k"] = fs.readFileSync(coreKPath, "utf8");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Add verified k-code files: arithmetics.k, ieee.k
|
|
32
|
+
const verifiedExamples = ["arithmetics.k", "ieee.k"];
|
|
33
|
+
const examplesDir = path.join(projectRoot, "Examples");
|
|
34
|
+
for (const filename of verifiedExamples) {
|
|
35
|
+
const fullPath = path.join(examplesDir, filename);
|
|
36
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
37
|
+
const content = fs.readFileSync(fullPath, "utf8");
|
|
38
|
+
vfsFiles[filename] = content;
|
|
39
|
+
vfsFiles[`Examples/${filename}`] = content;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Add verified codecs: json.mjs, utf8.mjs, int.mjs, unit.mjs, ieee.mjs
|
|
44
|
+
const verifiedCodecs = ["json.mjs", "utf8.mjs", "int.mjs", "unit.mjs", "ieee.mjs"];
|
|
45
|
+
const codecsDir = path.join(projectRoot, "codecs");
|
|
46
|
+
for (const filename of verifiedCodecs) {
|
|
47
|
+
const fullPath = path.join(codecsDir, filename);
|
|
48
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
49
|
+
const content = fs.readFileSync(fullPath, "utf8");
|
|
50
|
+
vfsFiles[filename] = content;
|
|
51
|
+
vfsFiles[`codecs/${filename}`] = content;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const vfsDataContent = `// Auto-generated by scripts/build-repl-html.mjs - DO NOT EDIT MANUALLY\n` +
|
|
56
|
+
`export const vfsData = ${JSON.stringify(vfsFiles, null, 2)};\n`;
|
|
57
|
+
|
|
58
|
+
const vfsDataPath = path.join(projectRoot, "browser/vfs-data.mjs");
|
|
59
|
+
fs.writeFileSync(vfsDataPath, vfsDataContent, "utf8");
|
|
60
|
+
console.log(` Generated ${vfsDataPath} with ${Object.keys(vfsFiles).length} files.`);
|
|
61
|
+
|
|
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
|
+
// 3. Bundle browser/repl-browser.mjs
|
|
96
|
+
console.log("2. Bundling with esbuild...");
|
|
97
|
+
const entryPoint = path.join(projectRoot, "browser/repl-browser.mjs");
|
|
98
|
+
|
|
99
|
+
const buildResult = await esbuild.build({
|
|
100
|
+
entryPoints: [entryPoint],
|
|
101
|
+
bundle: true,
|
|
102
|
+
format: "esm",
|
|
103
|
+
platform: "browser",
|
|
104
|
+
target: ["es2022"],
|
|
105
|
+
plugins: [nodeShimsPlugin],
|
|
106
|
+
define: {
|
|
107
|
+
"global": "globalThis",
|
|
108
|
+
"process.env.NODE_ENV": '"production"'
|
|
109
|
+
},
|
|
110
|
+
write: false,
|
|
111
|
+
minify: false,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (buildResult.errors.length > 0) {
|
|
115
|
+
console.error("Esbuild errors:", buildResult.errors);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const bundledJs = buildResult.outputFiles[0].text;
|
|
120
|
+
console.log(` Bundled JavaScript size: ${(bundledJs.length / 1024).toFixed(1)} KB`);
|
|
121
|
+
|
|
122
|
+
// 4. Read or generate HTML Template
|
|
123
|
+
console.log("3. Generating self-contained HTML...");
|
|
124
|
+
|
|
125
|
+
const htmlContent = `<!DOCTYPE html>
|
|
126
|
+
<html lang="en">
|
|
127
|
+
<head>
|
|
128
|
+
<meta charset="UTF-8">
|
|
129
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
130
|
+
<title>k interactive repl</title>
|
|
131
|
+
<style>
|
|
132
|
+
:root {
|
|
133
|
+
--bg-base: #0d1117;
|
|
134
|
+
--bg-surface: #161b22;
|
|
135
|
+
--bg-subtle: #21262d;
|
|
136
|
+
--bg-input: #090d12;
|
|
137
|
+
--border-color: #30363d;
|
|
138
|
+
--border-focus: #58a6ff;
|
|
139
|
+
--text-main: #e6edf3;
|
|
140
|
+
--text-muted: #8b949e;
|
|
141
|
+
--color-prompt: #58a6ff;
|
|
142
|
+
--color-green: #3fb950;
|
|
143
|
+
--color-yellow: #d29922;
|
|
144
|
+
--color-red: #f85149;
|
|
145
|
+
--color-purple: #bc8cff;
|
|
146
|
+
--color-cyan: #39c5cf;
|
|
147
|
+
--color-blue: #58a6ff;
|
|
148
|
+
--font-mono: ui-monospace, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
* {
|
|
152
|
+
box-sizing: border-box;
|
|
153
|
+
margin: 0;
|
|
154
|
+
padding: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
body, html {
|
|
158
|
+
height: 100%;
|
|
159
|
+
background-color: var(--bg-base);
|
|
160
|
+
color: var(--text-main);
|
|
161
|
+
font-family: var(--font-mono);
|
|
162
|
+
font-size: 14px;
|
|
163
|
+
line-height: 1.5;
|
|
164
|
+
overflow: hidden;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#app {
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
height: 100vh;
|
|
171
|
+
width: 100vw;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Header */
|
|
175
|
+
.navbar {
|
|
176
|
+
height: 48px;
|
|
177
|
+
background: var(--bg-surface);
|
|
178
|
+
border-bottom: 1px solid var(--border-color);
|
|
179
|
+
display: flex;
|
|
180
|
+
align-items: center;
|
|
181
|
+
justify-content: space-between;
|
|
182
|
+
padding: 0 16px;
|
|
183
|
+
flex-shrink: 0;
|
|
184
|
+
gap: 12px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.nav-left {
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
gap: 12px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.brand {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 8px;
|
|
197
|
+
font-weight: 700;
|
|
198
|
+
font-size: 16px;
|
|
199
|
+
user-select: none;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.brand-lambda {
|
|
203
|
+
color: var(--color-prompt);
|
|
204
|
+
font-size: 20px;
|
|
205
|
+
line-height: 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.brand-name {
|
|
209
|
+
letter-spacing: 0.5px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.engine-badge {
|
|
213
|
+
font-size: 11px;
|
|
214
|
+
font-weight: 600;
|
|
215
|
+
background: rgba(88, 166, 255, 0.15);
|
|
216
|
+
color: var(--color-prompt);
|
|
217
|
+
border: 1px solid rgba(88, 166, 255, 0.3);
|
|
218
|
+
padding: 2px 8px;
|
|
219
|
+
border-radius: 12px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.status-indicator {
|
|
223
|
+
display: flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
gap: 6px;
|
|
226
|
+
font-size: 12px;
|
|
227
|
+
color: var(--text-muted);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.status-dot {
|
|
231
|
+
width: 8px;
|
|
232
|
+
height: 8px;
|
|
233
|
+
border-radius: 50%;
|
|
234
|
+
display: inline-block;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.status-dot.ready {
|
|
238
|
+
background: var(--color-green);
|
|
239
|
+
box-shadow: 0 0 8px rgba(63, 185, 80, 0.6);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.status-dot.busy {
|
|
243
|
+
background: var(--color-yellow);
|
|
244
|
+
box-shadow: 0 0 8px rgba(210, 153, 34, 0.6);
|
|
245
|
+
animation: pulse 1s infinite alternate;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@keyframes pulse {
|
|
249
|
+
from { opacity: 0.4; }
|
|
250
|
+
to { opacity: 1; }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.nav-right {
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
gap: 8px;
|
|
257
|
+
flex-wrap: nowrap;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.control-group {
|
|
261
|
+
display: flex;
|
|
262
|
+
align-items: center;
|
|
263
|
+
gap: 4px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
select, button, .btn {
|
|
267
|
+
font-family: inherit;
|
|
268
|
+
font-size: 12px;
|
|
269
|
+
border-radius: 6px;
|
|
270
|
+
padding: 5px 10px;
|
|
271
|
+
cursor: pointer;
|
|
272
|
+
border: 1px solid var(--border-color);
|
|
273
|
+
transition: all 0.15s ease;
|
|
274
|
+
white-space: nowrap;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
select {
|
|
278
|
+
background: var(--bg-base);
|
|
279
|
+
color: var(--text-main);
|
|
280
|
+
max-width: 220px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
select:focus, button:focus {
|
|
284
|
+
outline: none;
|
|
285
|
+
border-color: var(--border-focus);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.btn {
|
|
289
|
+
display: inline-flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: 6px;
|
|
292
|
+
text-decoration: none;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.btn-primary {
|
|
296
|
+
background: #238636;
|
|
297
|
+
color: #ffffff;
|
|
298
|
+
border-color: rgba(240, 246, 252, 0.1);
|
|
299
|
+
font-weight: 500;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.btn-primary:hover {
|
|
303
|
+
background: #2ea043;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.btn-secondary {
|
|
307
|
+
background: var(--bg-subtle);
|
|
308
|
+
color: var(--text-main);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.btn-secondary:hover {
|
|
312
|
+
background: #30363d;
|
|
313
|
+
border-color: #8b949e;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.btn-sm {
|
|
317
|
+
padding: 3px 8px;
|
|
318
|
+
font-size: 11px;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* Terminal */
|
|
322
|
+
.terminal-main {
|
|
323
|
+
flex: 1;
|
|
324
|
+
display: flex;
|
|
325
|
+
flex-direction: column;
|
|
326
|
+
overflow: hidden;
|
|
327
|
+
background: var(--bg-base);
|
|
328
|
+
position: relative;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.terminal-output {
|
|
332
|
+
flex: 1;
|
|
333
|
+
overflow-y: auto;
|
|
334
|
+
padding: 16px;
|
|
335
|
+
scroll-behavior: smooth;
|
|
336
|
+
user-select: text;
|
|
337
|
+
-webkit-user-select: text;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.terminal-output::-webkit-scrollbar,
|
|
341
|
+
.vfs-code-view::-webkit-scrollbar,
|
|
342
|
+
.vfs-files-list::-webkit-scrollbar {
|
|
343
|
+
width: 8px;
|
|
344
|
+
height: 8px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.terminal-output::-webkit-scrollbar-thumb,
|
|
348
|
+
.vfs-code-view::-webkit-scrollbar-thumb,
|
|
349
|
+
.vfs-files-list::-webkit-scrollbar-thumb {
|
|
350
|
+
background: var(--border-color);
|
|
351
|
+
border-radius: 4px;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.terminal-output::-webkit-scrollbar-track,
|
|
355
|
+
.vfs-code-view::-webkit-scrollbar-track,
|
|
356
|
+
.vfs-files-list::-webkit-scrollbar-track {
|
|
357
|
+
background: transparent;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* Entries */
|
|
361
|
+
.terminal-entry {
|
|
362
|
+
margin-bottom: 12px;
|
|
363
|
+
word-break: break-word;
|
|
364
|
+
user-select: text;
|
|
365
|
+
-webkit-user-select: text;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.terminal-entry.system-msg {
|
|
369
|
+
padding: 8px 12px;
|
|
370
|
+
background: rgba(22, 27, 34, 0.6);
|
|
371
|
+
border-left: 3px solid var(--border-color);
|
|
372
|
+
border-radius: 0 6px 6px 0;
|
|
373
|
+
color: var(--text-muted);
|
|
374
|
+
user-select: text;
|
|
375
|
+
-webkit-user-select: text;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.entry-header {
|
|
379
|
+
display: flex;
|
|
380
|
+
align-items: baseline;
|
|
381
|
+
gap: 8px;
|
|
382
|
+
margin-bottom: 4px;
|
|
383
|
+
position: relative;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.entry-prompt {
|
|
387
|
+
color: var(--color-prompt);
|
|
388
|
+
font-weight: 700;
|
|
389
|
+
user-select: none;
|
|
390
|
+
-webkit-user-select: none;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.entry-command {
|
|
394
|
+
color: #ffffff;
|
|
395
|
+
font-weight: 600;
|
|
396
|
+
flex: 1;
|
|
397
|
+
white-space: pre-wrap;
|
|
398
|
+
user-select: text;
|
|
399
|
+
-webkit-user-select: text;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.entry-meta {
|
|
403
|
+
font-size: 11px;
|
|
404
|
+
color: var(--text-muted);
|
|
405
|
+
background: rgba(139, 148, 158, 0.15);
|
|
406
|
+
padding: 1px 6px;
|
|
407
|
+
border-radius: 4px;
|
|
408
|
+
user-select: none;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.btn-copy-entry {
|
|
412
|
+
background: transparent;
|
|
413
|
+
border: none;
|
|
414
|
+
color: var(--text-muted);
|
|
415
|
+
cursor: pointer;
|
|
416
|
+
font-size: 12px;
|
|
417
|
+
padding: 2px 4px;
|
|
418
|
+
opacity: 0.3;
|
|
419
|
+
transition: opacity 0.15s;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.entry-header:hover .btn-copy-entry {
|
|
423
|
+
opacity: 1;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.btn-copy-entry:hover {
|
|
427
|
+
color: var(--text-main);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.entry-line {
|
|
431
|
+
white-space: pre-wrap;
|
|
432
|
+
padding-left: 16px;
|
|
433
|
+
margin-top: 2px;
|
|
434
|
+
user-select: text;
|
|
435
|
+
-webkit-user-select: text;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.line-output {
|
|
439
|
+
color: var(--color-green);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.line-warning {
|
|
443
|
+
color: var(--color-yellow);
|
|
444
|
+
background: rgba(210, 153, 34, 0.1);
|
|
445
|
+
padding: 4px 12px;
|
|
446
|
+
border-radius: 4px;
|
|
447
|
+
margin: 4px 0 4px 16px;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.line-error {
|
|
451
|
+
color: var(--color-red);
|
|
452
|
+
background: rgba(248, 81, 73, 0.1);
|
|
453
|
+
padding: 4px 12px;
|
|
454
|
+
border-radius: 4px;
|
|
455
|
+
margin: 4px 0 4px 16px;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* Welcome banner */
|
|
459
|
+
.welcome-banner {
|
|
460
|
+
padding: 16px;
|
|
461
|
+
background: var(--bg-surface);
|
|
462
|
+
border: 1px solid var(--border-color);
|
|
463
|
+
border-radius: 8px;
|
|
464
|
+
margin-bottom: 16px;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.welcome-title {
|
|
468
|
+
font-size: 18px;
|
|
469
|
+
font-weight: 700;
|
|
470
|
+
color: var(--color-prompt);
|
|
471
|
+
margin-bottom: 4px;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.welcome-desc {
|
|
475
|
+
color: var(--text-muted);
|
|
476
|
+
font-size: 13px;
|
|
477
|
+
margin-bottom: 12px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.welcome-tips {
|
|
481
|
+
display: flex;
|
|
482
|
+
flex-wrap: wrap;
|
|
483
|
+
gap: 8px;
|
|
484
|
+
font-size: 12px;
|
|
485
|
+
color: var(--text-main);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.quick-link {
|
|
489
|
+
color: var(--color-prompt);
|
|
490
|
+
text-decoration: none;
|
|
491
|
+
background: rgba(88, 166, 255, 0.1);
|
|
492
|
+
padding: 2px 6px;
|
|
493
|
+
border-radius: 4px;
|
|
494
|
+
border: 1px solid rgba(88, 166, 255, 0.2);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.quick-link:hover {
|
|
498
|
+
text-decoration: underline;
|
|
499
|
+
background: rgba(88, 166, 255, 0.2);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/* Input Bar */
|
|
503
|
+
.terminal-input-bar {
|
|
504
|
+
background: var(--bg-surface);
|
|
505
|
+
border-top: 1px solid var(--border-color);
|
|
506
|
+
display: flex;
|
|
507
|
+
align-items: flex-start;
|
|
508
|
+
padding: 10px 16px;
|
|
509
|
+
gap: 8px;
|
|
510
|
+
position: relative;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.prompt-label {
|
|
514
|
+
color: var(--color-prompt);
|
|
515
|
+
font-weight: 700;
|
|
516
|
+
font-size: 15px;
|
|
517
|
+
padding-top: 4px;
|
|
518
|
+
user-select: none;
|
|
519
|
+
min-width: 18px;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.input-wrapper {
|
|
523
|
+
flex: 1;
|
|
524
|
+
position: relative;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
#repl-input {
|
|
528
|
+
width: 100%;
|
|
529
|
+
background: var(--bg-input);
|
|
530
|
+
color: var(--text-main);
|
|
531
|
+
border: 1px solid var(--border-color);
|
|
532
|
+
border-radius: 6px;
|
|
533
|
+
padding: 6px 10px;
|
|
534
|
+
font-family: inherit;
|
|
535
|
+
font-size: 14px;
|
|
536
|
+
line-height: 1.4;
|
|
537
|
+
resize: none;
|
|
538
|
+
outline: none;
|
|
539
|
+
min-height: 32px;
|
|
540
|
+
max-height: 240px;
|
|
541
|
+
transition: border-color 0.15s;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
#repl-input:focus {
|
|
545
|
+
border-color: var(--border-focus);
|
|
546
|
+
box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.btn-run {
|
|
550
|
+
background: var(--color-prompt);
|
|
551
|
+
color: #0d1117;
|
|
552
|
+
border: none;
|
|
553
|
+
font-weight: 700;
|
|
554
|
+
padding: 7px 14px;
|
|
555
|
+
border-radius: 6px;
|
|
556
|
+
cursor: pointer;
|
|
557
|
+
align-self: flex-start;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.btn-run:hover {
|
|
561
|
+
background: #79c0ff;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* Autocomplete popup */
|
|
565
|
+
.autocomplete-popup {
|
|
566
|
+
display: none;
|
|
567
|
+
position: absolute;
|
|
568
|
+
bottom: calc(100% + 4px);
|
|
569
|
+
left: 0;
|
|
570
|
+
background: var(--bg-surface);
|
|
571
|
+
border: 1px solid var(--border-color);
|
|
572
|
+
border-radius: 6px;
|
|
573
|
+
max-height: 180px;
|
|
574
|
+
overflow-y: auto;
|
|
575
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
576
|
+
z-index: 100;
|
|
577
|
+
min-width: 240px;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.autocomplete-popup.visible {
|
|
581
|
+
display: block;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.autocomplete-item {
|
|
585
|
+
padding: 6px 12px;
|
|
586
|
+
cursor: pointer;
|
|
587
|
+
font-size: 13px;
|
|
588
|
+
color: var(--text-main);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.autocomplete-item:hover, .autocomplete-item.active {
|
|
592
|
+
background: rgba(88, 166, 255, 0.2);
|
|
593
|
+
color: var(--color-prompt);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* Footer hints */
|
|
597
|
+
.terminal-footer {
|
|
598
|
+
height: 24px;
|
|
599
|
+
background: var(--bg-surface);
|
|
600
|
+
border-top: 1px solid rgba(48, 54, 61, 0.5);
|
|
601
|
+
display: flex;
|
|
602
|
+
align-items: center;
|
|
603
|
+
padding: 0 16px;
|
|
604
|
+
font-size: 11px;
|
|
605
|
+
color: var(--text-muted);
|
|
606
|
+
gap: 8px;
|
|
607
|
+
user-select: none;
|
|
608
|
+
overflow-x: auto;
|
|
609
|
+
white-space: nowrap;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/* Modals */
|
|
613
|
+
.modal {
|
|
614
|
+
display: none;
|
|
615
|
+
position: fixed;
|
|
616
|
+
inset: 0;
|
|
617
|
+
background: rgba(0, 0, 0, 0.7);
|
|
618
|
+
backdrop-filter: blur(4px);
|
|
619
|
+
z-index: 200;
|
|
620
|
+
align-items: center;
|
|
621
|
+
justify-content: center;
|
|
622
|
+
padding: 24px;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.modal.open {
|
|
626
|
+
display: flex;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.modal-dialog {
|
|
630
|
+
background: var(--bg-surface);
|
|
631
|
+
border: 1px solid var(--border-color);
|
|
632
|
+
border-radius: 8px;
|
|
633
|
+
width: 100%;
|
|
634
|
+
max-width: 840px;
|
|
635
|
+
max-height: 85vh;
|
|
636
|
+
display: flex;
|
|
637
|
+
flex-direction: column;
|
|
638
|
+
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.6);
|
|
639
|
+
overflow: hidden;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.modal-header {
|
|
643
|
+
padding: 14px 20px;
|
|
644
|
+
border-bottom: 1px solid var(--border-color);
|
|
645
|
+
display: flex;
|
|
646
|
+
align-items: center;
|
|
647
|
+
justify-content: space-between;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.modal-header h3 {
|
|
651
|
+
font-size: 16px;
|
|
652
|
+
font-weight: 600;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.btn-close {
|
|
656
|
+
background: transparent;
|
|
657
|
+
border: none;
|
|
658
|
+
color: var(--text-muted);
|
|
659
|
+
font-size: 20px;
|
|
660
|
+
cursor: pointer;
|
|
661
|
+
line-height: 1;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.btn-close:hover {
|
|
665
|
+
color: var(--text-main);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.modal-body {
|
|
669
|
+
padding: 20px;
|
|
670
|
+
overflow-y: auto;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* Help table */
|
|
674
|
+
.help-section {
|
|
675
|
+
margin-bottom: 20px;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.help-section h4 {
|
|
679
|
+
font-size: 14px;
|
|
680
|
+
color: var(--color-prompt);
|
|
681
|
+
margin-bottom: 8px;
|
|
682
|
+
border-bottom: 1px solid var(--border-color);
|
|
683
|
+
padding-bottom: 4px;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.help-table {
|
|
687
|
+
width: 100%;
|
|
688
|
+
border-collapse: collapse;
|
|
689
|
+
font-size: 13px;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.help-table td {
|
|
693
|
+
padding: 6px 10px;
|
|
694
|
+
border-bottom: 1px solid rgba(48, 54, 61, 0.4);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.help-table td:first-child {
|
|
698
|
+
width: 35%;
|
|
699
|
+
font-family: var(--font-mono);
|
|
700
|
+
color: var(--color-cyan);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.help-section ul {
|
|
704
|
+
padding-left: 24px;
|
|
705
|
+
font-size: 13px;
|
|
706
|
+
color: var(--text-main);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.help-section li {
|
|
710
|
+
margin-bottom: 6px;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* VFS Dialog */
|
|
714
|
+
.vfs-dialog {
|
|
715
|
+
max-width: 900px;
|
|
716
|
+
height: 75vh;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.vfs-body {
|
|
720
|
+
padding: 0;
|
|
721
|
+
display: flex;
|
|
722
|
+
flex: 1;
|
|
723
|
+
overflow: hidden;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.vfs-sidebar {
|
|
727
|
+
width: 280px;
|
|
728
|
+
background: var(--bg-base);
|
|
729
|
+
border-right: 1px solid var(--border-color);
|
|
730
|
+
display: flex;
|
|
731
|
+
flex-direction: column;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.vfs-sidebar-title {
|
|
735
|
+
padding: 10px 14px;
|
|
736
|
+
font-size: 12px;
|
|
737
|
+
font-weight: 600;
|
|
738
|
+
color: var(--text-muted);
|
|
739
|
+
border-bottom: 1px solid var(--border-color);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.vfs-files-list {
|
|
743
|
+
flex: 1;
|
|
744
|
+
overflow-y: auto;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.vfs-list-item {
|
|
748
|
+
padding: 8px 14px;
|
|
749
|
+
border-bottom: 1px solid rgba(48, 54, 61, 0.3);
|
|
750
|
+
cursor: pointer;
|
|
751
|
+
display: flex;
|
|
752
|
+
align-items: center;
|
|
753
|
+
justify-content: space-between;
|
|
754
|
+
font-size: 12px;
|
|
755
|
+
transition: background 0.15s;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.vfs-list-item:hover {
|
|
759
|
+
background: var(--bg-subtle);
|
|
760
|
+
color: var(--color-prompt);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.vfs-file-size {
|
|
764
|
+
color: var(--text-muted);
|
|
765
|
+
font-size: 11px;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.vfs-viewer {
|
|
769
|
+
flex: 1;
|
|
770
|
+
display: flex;
|
|
771
|
+
flex-direction: column;
|
|
772
|
+
overflow: hidden;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.vfs-viewer-header {
|
|
776
|
+
height: 40px;
|
|
777
|
+
border-bottom: 1px solid var(--border-color);
|
|
778
|
+
display: flex;
|
|
779
|
+
align-items: center;
|
|
780
|
+
justify-content: space-between;
|
|
781
|
+
padding: 0 16px;
|
|
782
|
+
background: var(--bg-surface);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.vfs-preview-title {
|
|
786
|
+
font-weight: 600;
|
|
787
|
+
font-size: 13px;
|
|
788
|
+
color: var(--color-prompt);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.vfs-viewer-actions {
|
|
792
|
+
display: flex;
|
|
793
|
+
gap: 6px;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.vfs-code-view {
|
|
797
|
+
flex: 1;
|
|
798
|
+
padding: 16px;
|
|
799
|
+
overflow: auto;
|
|
800
|
+
font-family: var(--font-mono);
|
|
801
|
+
font-size: 12px;
|
|
802
|
+
line-height: 1.5;
|
|
803
|
+
background: #090d12;
|
|
804
|
+
color: var(--text-main);
|
|
805
|
+
white-space: pre;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/* Drop Overlay */
|
|
809
|
+
.drop-overlay {
|
|
810
|
+
display: none;
|
|
811
|
+
position: fixed;
|
|
812
|
+
inset: 0;
|
|
813
|
+
background: rgba(13, 17, 23, 0.85);
|
|
814
|
+
border: 3px dashed var(--color-prompt);
|
|
815
|
+
z-index: 500;
|
|
816
|
+
align-items: center;
|
|
817
|
+
justify-content: center;
|
|
818
|
+
pointer-events: none;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.drop-overlay.visible {
|
|
822
|
+
display: flex;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.drop-box {
|
|
826
|
+
text-align: center;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.drop-icon {
|
|
830
|
+
font-size: 48px;
|
|
831
|
+
margin-bottom: 12px;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.drop-text {
|
|
835
|
+
font-size: 18px;
|
|
836
|
+
font-weight: 600;
|
|
837
|
+
color: var(--color-prompt);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/* Codecs Subsystem & Badges */
|
|
841
|
+
.badge-count {
|
|
842
|
+
display: inline-block;
|
|
843
|
+
min-width: 18px;
|
|
844
|
+
height: 18px;
|
|
845
|
+
line-height: 16px;
|
|
846
|
+
text-align: center;
|
|
847
|
+
padding: 0 5px;
|
|
848
|
+
border-radius: 9px;
|
|
849
|
+
font-size: 11px;
|
|
850
|
+
font-weight: 700;
|
|
851
|
+
background: rgba(139, 148, 158, 0.2);
|
|
852
|
+
color: var(--text-muted);
|
|
853
|
+
border: 1px solid var(--border-color);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.badge-count.active {
|
|
857
|
+
background: rgba(63, 185, 80, 0.2);
|
|
858
|
+
color: var(--color-green);
|
|
859
|
+
border-color: rgba(63, 185, 80, 0.4);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.codecs-dialog {
|
|
863
|
+
max-width: 960px;
|
|
864
|
+
max-height: 90vh;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.codecs-body {
|
|
868
|
+
padding: 16px 20px;
|
|
869
|
+
display: flex;
|
|
870
|
+
flex-direction: column;
|
|
871
|
+
gap: 20px;
|
|
872
|
+
overflow-y: auto;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.codecs-section {
|
|
876
|
+
display: flex;
|
|
877
|
+
flex-direction: column;
|
|
878
|
+
gap: 8px;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
.codecs-section-header {
|
|
882
|
+
display: flex;
|
|
883
|
+
flex-direction: column;
|
|
884
|
+
gap: 2px;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.codecs-section-header h4 {
|
|
888
|
+
font-size: 14px;
|
|
889
|
+
color: var(--color-prompt);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.section-subtitle {
|
|
893
|
+
font-size: 11px;
|
|
894
|
+
color: var(--text-muted);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.codec-grid {
|
|
898
|
+
display: grid;
|
|
899
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
900
|
+
gap: 12px;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.codec-card {
|
|
904
|
+
background: var(--bg-base);
|
|
905
|
+
border: 1px solid var(--border-color);
|
|
906
|
+
border-radius: 6px;
|
|
907
|
+
padding: 12px;
|
|
908
|
+
display: flex;
|
|
909
|
+
flex-direction: column;
|
|
910
|
+
justify-content: space-between;
|
|
911
|
+
gap: 10px;
|
|
912
|
+
transition: border-color 0.15s;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.codec-card:hover {
|
|
916
|
+
border-color: var(--border-focus);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.codec-card-title {
|
|
920
|
+
display: flex;
|
|
921
|
+
align-items: center;
|
|
922
|
+
gap: 6px;
|
|
923
|
+
font-weight: 700;
|
|
924
|
+
font-size: 14px;
|
|
925
|
+
margin-bottom: 6px;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.codec-icon {
|
|
929
|
+
font-size: 16px;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.codec-name {
|
|
933
|
+
flex: 1;
|
|
934
|
+
color: #ffffff;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.codec-status-badge {
|
|
938
|
+
font-size: 10px;
|
|
939
|
+
font-weight: 600;
|
|
940
|
+
padding: 1px 6px;
|
|
941
|
+
border-radius: 10px;
|
|
942
|
+
background: rgba(139, 148, 158, 0.15);
|
|
943
|
+
color: var(--text-muted);
|
|
944
|
+
border: 1px solid var(--border-color);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.codec-status-badge.active {
|
|
948
|
+
background: rgba(63, 185, 80, 0.15);
|
|
949
|
+
color: var(--color-green);
|
|
950
|
+
border-color: rgba(63, 185, 80, 0.4);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
.codec-types {
|
|
954
|
+
font-size: 11px;
|
|
955
|
+
color: var(--text-muted);
|
|
956
|
+
margin-bottom: 6px;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.codec-desc {
|
|
960
|
+
font-size: 11px;
|
|
961
|
+
color: var(--text-main);
|
|
962
|
+
line-height: 1.4;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.codec-card-footer {
|
|
966
|
+
display: flex;
|
|
967
|
+
align-items: center;
|
|
968
|
+
justify-content: space-between;
|
|
969
|
+
gap: 6px;
|
|
970
|
+
border-top: 1px solid rgba(48, 54, 61, 0.4);
|
|
971
|
+
padding-top: 8px;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.codec-caps {
|
|
975
|
+
display: flex;
|
|
976
|
+
gap: 4px;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.cap-badge {
|
|
980
|
+
font-size: 10px;
|
|
981
|
+
padding: 1px 5px;
|
|
982
|
+
border-radius: 4px;
|
|
983
|
+
font-weight: 500;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.cap-badge.print {
|
|
987
|
+
background: rgba(188, 140, 255, 0.15);
|
|
988
|
+
color: var(--color-purple);
|
|
989
|
+
border: 1px solid rgba(188, 140, 255, 0.3);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.cap-badge.parse {
|
|
993
|
+
background: rgba(57, 197, 207, 0.15);
|
|
994
|
+
color: var(--color-cyan);
|
|
995
|
+
border: 1px solid rgba(57, 197, 207, 0.3);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.badge-type {
|
|
999
|
+
background: rgba(88, 166, 255, 0.15);
|
|
1000
|
+
color: var(--color-prompt);
|
|
1001
|
+
padding: 1px 6px;
|
|
1002
|
+
border-radius: 4px;
|
|
1003
|
+
font-size: 11px;
|
|
1004
|
+
font-family: var(--font-mono);
|
|
1005
|
+
border: 1px solid rgba(88, 166, 255, 0.25);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
.badge-type.universal {
|
|
1009
|
+
background: rgba(210, 153, 34, 0.15);
|
|
1010
|
+
color: var(--color-yellow);
|
|
1011
|
+
border-color: rgba(210, 153, 34, 0.3);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.codecs-table-container {
|
|
1015
|
+
background: var(--bg-base);
|
|
1016
|
+
border: 1px solid var(--border-color);
|
|
1017
|
+
border-radius: 6px;
|
|
1018
|
+
overflow-x: auto;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
.codec-table {
|
|
1022
|
+
width: 100%;
|
|
1023
|
+
border-collapse: collapse;
|
|
1024
|
+
font-size: 12px;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
.codec-table th {
|
|
1028
|
+
background: var(--bg-subtle);
|
|
1029
|
+
text-align: left;
|
|
1030
|
+
padding: 8px 12px;
|
|
1031
|
+
font-size: 11px;
|
|
1032
|
+
color: var(--text-muted);
|
|
1033
|
+
border-bottom: 1px solid var(--border-color);
|
|
1034
|
+
text-transform: uppercase;
|
|
1035
|
+
letter-spacing: 0.5px;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
.codec-table td {
|
|
1039
|
+
padding: 8px 12px;
|
|
1040
|
+
border-bottom: 1px solid rgba(48, 54, 61, 0.3);
|
|
1041
|
+
vertical-align: middle;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
.codec-cell-name {
|
|
1045
|
+
font-weight: 600;
|
|
1046
|
+
color: #ffffff;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.btn-danger {
|
|
1050
|
+
background: rgba(248, 81, 73, 0.1);
|
|
1051
|
+
color: var(--color-red);
|
|
1052
|
+
border-color: rgba(248, 81, 73, 0.3);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.btn-danger:hover {
|
|
1056
|
+
background: rgba(248, 81, 73, 0.25);
|
|
1057
|
+
border-color: var(--color-red);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.codec-studio-box {
|
|
1061
|
+
background: var(--bg-base);
|
|
1062
|
+
border: 1px solid var(--border-color);
|
|
1063
|
+
border-radius: 6px;
|
|
1064
|
+
padding: 14px;
|
|
1065
|
+
display: flex;
|
|
1066
|
+
flex-direction: column;
|
|
1067
|
+
gap: 12px;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.studio-top-bar {
|
|
1071
|
+
display: flex;
|
|
1072
|
+
flex-wrap: wrap;
|
|
1073
|
+
gap: 12px;
|
|
1074
|
+
align-items: flex-end;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
.studio-field {
|
|
1078
|
+
display: flex;
|
|
1079
|
+
flex-direction: column;
|
|
1080
|
+
gap: 4px;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.studio-field label {
|
|
1084
|
+
font-size: 11px;
|
|
1085
|
+
color: var(--text-muted);
|
|
1086
|
+
font-weight: 500;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.studio-field input, .studio-field select {
|
|
1090
|
+
background: var(--bg-input);
|
|
1091
|
+
color: var(--text-main);
|
|
1092
|
+
border: 1px solid var(--border-color);
|
|
1093
|
+
border-radius: 6px;
|
|
1094
|
+
padding: 5px 8px;
|
|
1095
|
+
font-size: 12px;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
.studio-field input:focus, .studio-field select:focus {
|
|
1099
|
+
border-color: var(--border-focus);
|
|
1100
|
+
outline: none;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
.studio-editors-grid {
|
|
1104
|
+
display: grid;
|
|
1105
|
+
grid-template-columns: 1fr 1fr;
|
|
1106
|
+
gap: 12px;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
@media (max-width: 700px) {
|
|
1110
|
+
.studio-editors-grid {
|
|
1111
|
+
grid-template-columns: 1fr;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
.studio-editor-pane {
|
|
1116
|
+
display: flex;
|
|
1117
|
+
flex-direction: column;
|
|
1118
|
+
gap: 6px;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.pane-title {
|
|
1122
|
+
display: flex;
|
|
1123
|
+
align-items: center;
|
|
1124
|
+
justify-content: space-between;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
.pane-hint {
|
|
1128
|
+
font-size: 11px;
|
|
1129
|
+
color: var(--text-muted);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
.codec-code-area {
|
|
1133
|
+
width: 100%;
|
|
1134
|
+
background: #090d12;
|
|
1135
|
+
color: var(--text-main);
|
|
1136
|
+
border: 1px solid var(--border-color);
|
|
1137
|
+
border-radius: 6px;
|
|
1138
|
+
padding: 8px 10px;
|
|
1139
|
+
font-family: var(--font-mono);
|
|
1140
|
+
font-size: 12px;
|
|
1141
|
+
line-height: 1.4;
|
|
1142
|
+
resize: vertical;
|
|
1143
|
+
min-height: 120px;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.codec-code-area:focus {
|
|
1147
|
+
border-color: var(--border-focus);
|
|
1148
|
+
outline: none;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
.studio-test-bar {
|
|
1152
|
+
display: flex;
|
|
1153
|
+
flex-wrap: wrap;
|
|
1154
|
+
align-items: center;
|
|
1155
|
+
gap: 8px;
|
|
1156
|
+
border-top: 1px solid rgba(48, 54, 61, 0.4);
|
|
1157
|
+
padding-top: 10px;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
.studio-test-input-wrap {
|
|
1161
|
+
display: flex;
|
|
1162
|
+
align-items: center;
|
|
1163
|
+
gap: 6px;
|
|
1164
|
+
flex: 1;
|
|
1165
|
+
min-width: 200px;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
.studio-test-input-wrap label {
|
|
1169
|
+
font-size: 11px;
|
|
1170
|
+
color: var(--text-muted);
|
|
1171
|
+
white-space: nowrap;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
.studio-test-input-wrap input {
|
|
1175
|
+
flex: 1;
|
|
1176
|
+
background: var(--bg-input);
|
|
1177
|
+
color: var(--text-main);
|
|
1178
|
+
border: 1px solid var(--border-color);
|
|
1179
|
+
border-radius: 6px;
|
|
1180
|
+
padding: 5px 8px;
|
|
1181
|
+
font-size: 12px;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
.studio-test-input-wrap input:focus {
|
|
1185
|
+
border-color: var(--border-focus);
|
|
1186
|
+
outline: none;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
.studio-status-box {
|
|
1190
|
+
font-size: 12px;
|
|
1191
|
+
min-height: 20px;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/* Input Popup Modal */
|
|
1195
|
+
.input-popup-dialog {
|
|
1196
|
+
max-width: 580px;
|
|
1197
|
+
width: 90%;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
.input-popup-body {
|
|
1201
|
+
padding: 16px 20px;
|
|
1202
|
+
display: flex;
|
|
1203
|
+
flex-direction: column;
|
|
1204
|
+
gap: 12px;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.input-popup-banner {
|
|
1208
|
+
background: var(--bg-base);
|
|
1209
|
+
border: 1px solid var(--border-color);
|
|
1210
|
+
border-radius: 6px;
|
|
1211
|
+
padding: 10px 14px;
|
|
1212
|
+
font-size: 12px;
|
|
1213
|
+
color: var(--text-main);
|
|
1214
|
+
display: flex;
|
|
1215
|
+
align-items: center;
|
|
1216
|
+
gap: 8px;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.input-popup-form-row {
|
|
1220
|
+
display: flex;
|
|
1221
|
+
gap: 12px;
|
|
1222
|
+
flex-wrap: wrap;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.input-popup-field {
|
|
1226
|
+
display: flex;
|
|
1227
|
+
flex-direction: column;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.input-popup-textarea {
|
|
1231
|
+
width: 100%;
|
|
1232
|
+
background: #090d12;
|
|
1233
|
+
color: var(--text-main);
|
|
1234
|
+
border: 1px solid var(--border-color);
|
|
1235
|
+
border-radius: 6px;
|
|
1236
|
+
padding: 10px 12px;
|
|
1237
|
+
font-family: var(--font-mono);
|
|
1238
|
+
font-size: 13px;
|
|
1239
|
+
line-height: 1.4;
|
|
1240
|
+
resize: vertical;
|
|
1241
|
+
min-height: 90px;
|
|
1242
|
+
max-height: 280px;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
.input-popup-textarea:focus {
|
|
1246
|
+
border-color: var(--border-focus);
|
|
1247
|
+
box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.2);
|
|
1248
|
+
outline: none;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.input-popup-samples {
|
|
1252
|
+
display: flex;
|
|
1253
|
+
align-items: center;
|
|
1254
|
+
gap: 8px;
|
|
1255
|
+
font-size: 11px;
|
|
1256
|
+
color: var(--text-muted);
|
|
1257
|
+
flex-wrap: wrap;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.samples-list {
|
|
1261
|
+
display: flex;
|
|
1262
|
+
gap: 6px;
|
|
1263
|
+
flex-wrap: wrap;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
.sample-pill {
|
|
1267
|
+
background: var(--bg-subtle);
|
|
1268
|
+
border: 1px solid var(--border-color);
|
|
1269
|
+
color: var(--color-prompt);
|
|
1270
|
+
padding: 2px 8px;
|
|
1271
|
+
border-radius: 12px;
|
|
1272
|
+
cursor: pointer;
|
|
1273
|
+
font-family: var(--font-mono);
|
|
1274
|
+
font-size: 11px;
|
|
1275
|
+
transition: all 0.15s;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
.sample-pill:hover {
|
|
1279
|
+
background: rgba(88, 166, 255, 0.2);
|
|
1280
|
+
border-color: var(--border-focus);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
.input-popup-status {
|
|
1284
|
+
font-size: 12px;
|
|
1285
|
+
min-height: 22px;
|
|
1286
|
+
border-radius: 4px;
|
|
1287
|
+
padding: 4px 8px;
|
|
1288
|
+
background: rgba(22, 27, 34, 0.5);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.input-popup-actions {
|
|
1292
|
+
display: flex;
|
|
1293
|
+
justify-content: flex-end;
|
|
1294
|
+
gap: 8px;
|
|
1295
|
+
border-top: 1px solid var(--border-color);
|
|
1296
|
+
padding-top: 12px;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/* Responsive */
|
|
1300
|
+
@media (max-width: 768px) {
|
|
1301
|
+
.navbar {
|
|
1302
|
+
height: auto;
|
|
1303
|
+
flex-direction: column;
|
|
1304
|
+
padding: 8px 12px;
|
|
1305
|
+
align-items: stretch;
|
|
1306
|
+
}
|
|
1307
|
+
.nav-right {
|
|
1308
|
+
overflow-x: auto;
|
|
1309
|
+
padding-bottom: 4px;
|
|
1310
|
+
}
|
|
1311
|
+
.vfs-body {
|
|
1312
|
+
flex-direction: column;
|
|
1313
|
+
}
|
|
1314
|
+
.vfs-sidebar {
|
|
1315
|
+
width: 100%;
|
|
1316
|
+
height: 160px;
|
|
1317
|
+
border-right: none;
|
|
1318
|
+
border-bottom: 1px solid var(--border-color);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
</style>
|
|
1322
|
+
</head>
|
|
1323
|
+
<body>
|
|
1324
|
+
<div id="app">
|
|
1325
|
+
<!-- Navbar -->
|
|
1326
|
+
<header class="navbar">
|
|
1327
|
+
<div class="nav-left">
|
|
1328
|
+
<div class="brand">
|
|
1329
|
+
<span class="brand-lambda">λ</span>
|
|
1330
|
+
<span class="brand-name">k repl</span>
|
|
1331
|
+
<span class="engine-badge">wasm-in-process</span>
|
|
1332
|
+
</div>
|
|
1333
|
+
<div class="status-indicator">
|
|
1334
|
+
<span id="status-dot" class="status-dot ready"></span>
|
|
1335
|
+
<span id="status-text">Ready</span>
|
|
1336
|
+
</div>
|
|
1337
|
+
</div>
|
|
1338
|
+
|
|
1339
|
+
<div class="nav-right">
|
|
1340
|
+
<!-- Examples -->
|
|
1341
|
+
<div class="control-group">
|
|
1342
|
+
<select id="example-select" title="Choose an example or expression">
|
|
1343
|
+
<option value="">-- Example / Expression --</option>
|
|
1344
|
+
<optgroup label="Standard Examples">
|
|
1345
|
+
<option value="core.k">core.k</option>
|
|
1346
|
+
<option value="arithmetics.k">arithmetics.k</option>
|
|
1347
|
+
<option value="ieee.k">ieee.k</option>
|
|
1348
|
+
</optgroup>
|
|
1349
|
+
<optgroup label="Quick Expressions">
|
|
1350
|
+
<option value="expr:10">10</option>
|
|
1351
|
+
<option value="expr:{10 int x, 5 int y} plus">{10 int x, 5 int y} plus</option>
|
|
1352
|
+
<option value="expr:1 succ">1 succ</option>
|
|
1353
|
+
<option value="expr:() ()">()</option>
|
|
1354
|
+
<option value="expr::t plus">:t plus</option>
|
|
1355
|
+
<option value="expr::d plus">:d plus</option>
|
|
1356
|
+
<option value="expr::codes">:codes</option>
|
|
1357
|
+
<option value="expr::rels">:rels</option>
|
|
1358
|
+
<option value="expr::val">:val</option>
|
|
1359
|
+
</optgroup>
|
|
1360
|
+
<optgroup label="Codecs & Serializers">
|
|
1361
|
+
<option value="expr::codecs">:codecs</option>
|
|
1362
|
+
<option value="expr::input">:input (codec popup)</option>
|
|
1363
|
+
<option value="expr::codec load int">:codec load int</option>
|
|
1364
|
+
<option value="expr:10 int">10 int</option>
|
|
1365
|
+
<option value="expr::codec load utf8">:codec load utf8</option>
|
|
1366
|
+
<option value="expr::codec load json">:codec load json</option>
|
|
1367
|
+
<option value="expr::codec load ieee">:codec load ieee</option>
|
|
1368
|
+
<option value="expr::codec load unit">:codec load unit</option>
|
|
1369
|
+
</optgroup>
|
|
1370
|
+
</select>
|
|
1371
|
+
<button id="btn-load-example" class="btn btn-primary">Load</button>
|
|
1372
|
+
</div>
|
|
1373
|
+
|
|
1374
|
+
<!-- File Upload -->
|
|
1375
|
+
<label class="btn btn-secondary btn-file" title="Upload local .k or .klib file into VFS">
|
|
1376
|
+
<input type="file" id="file-upload" accept=".k,.klib,.wat,.ko" style="display:none;">
|
|
1377
|
+
<span>📁 Upload</span>
|
|
1378
|
+
</label>
|
|
1379
|
+
|
|
1380
|
+
<!-- VFS Explorer -->
|
|
1381
|
+
<button id="btn-vfs" class="btn btn-secondary" title="Explore Virtual File System">🗂 Files</button>
|
|
1382
|
+
|
|
1383
|
+
<!-- Codecs Subsystem -->
|
|
1384
|
+
<button id="btn-codecs" class="btn btn-secondary" title="Codecs & Custom Serializers (:codecs)">⚙ Codecs <span id="codecs-count-badge" class="badge-count">0</span></button>
|
|
1385
|
+
|
|
1386
|
+
<!-- Codec Input Popup -->
|
|
1387
|
+
<button id="btn-input-popup" class="btn btn-secondary" title="Open Codec Input Popup (:input)">📥 Input</button>
|
|
1388
|
+
|
|
1389
|
+
<!-- Clear -->
|
|
1390
|
+
<button id="btn-clear" class="btn btn-secondary" title="Clear terminal screen (Ctrl+L)">⌫ Clear</button>
|
|
1391
|
+
|
|
1392
|
+
<!-- Reset -->
|
|
1393
|
+
<button id="btn-reset" class="btn btn-secondary" title="Reset REPL state (:reset)">↺ Reset</button>
|
|
1394
|
+
|
|
1395
|
+
<!-- Export -->
|
|
1396
|
+
<button id="btn-export" class="btn btn-secondary" title="Export session to .klib">💾 Save .klib</button>
|
|
1397
|
+
|
|
1398
|
+
<!-- Help -->
|
|
1399
|
+
<button id="btn-help" class="btn btn-secondary" title="Help & Reference (:help)">? Help</button>
|
|
1400
|
+
</div>
|
|
1401
|
+
</header>
|
|
1402
|
+
|
|
1403
|
+
<!-- Terminal Main -->
|
|
1404
|
+
<main class="terminal-main">
|
|
1405
|
+
<div id="terminal-output" class="terminal-output"></div>
|
|
1406
|
+
|
|
1407
|
+
<!-- Input Bar -->
|
|
1408
|
+
<div class="terminal-input-bar">
|
|
1409
|
+
<div id="prompt-label" class="prompt-label">> </div>
|
|
1410
|
+
<div class="input-wrapper">
|
|
1411
|
+
<textarea id="repl-input" rows="1" spellcheck="false" autocomplete="off" placeholder="Enter K expression or command (:help)..."></textarea>
|
|
1412
|
+
<div id="autocomplete-popup" class="autocomplete-popup"></div>
|
|
1413
|
+
</div>
|
|
1414
|
+
<button id="btn-run" class="btn-run" title="Execute (Enter / Ctrl+Enter)">▶ Run</button>
|
|
1415
|
+
</div>
|
|
1416
|
+
|
|
1417
|
+
<!-- Footer Hints -->
|
|
1418
|
+
<div class="terminal-footer">
|
|
1419
|
+
<span>Enter: run</span>
|
|
1420
|
+
<span>• Shift+Enter: newline</span>
|
|
1421
|
+
<span>• Tab: autocomplete</span>
|
|
1422
|
+
<span>• ↑/↓: history</span>
|
|
1423
|
+
<span>• Ctrl+L: clear</span>
|
|
1424
|
+
<span>• Drag & drop .k/.klib files anywhere</span>
|
|
1425
|
+
</div>
|
|
1426
|
+
</main>
|
|
1427
|
+
|
|
1428
|
+
<!-- VFS Modal -->
|
|
1429
|
+
<div id="vfs-modal" class="modal">
|
|
1430
|
+
<div class="modal-dialog vfs-dialog">
|
|
1431
|
+
<div class="modal-header">
|
|
1432
|
+
<h3>Virtual File System (VFS)</h3>
|
|
1433
|
+
<button id="btn-close-vfs" class="btn-close">×</button>
|
|
1434
|
+
</div>
|
|
1435
|
+
<div class="modal-body vfs-body">
|
|
1436
|
+
<div class="vfs-sidebar">
|
|
1437
|
+
<div class="vfs-sidebar-title">Files in VFS</div>
|
|
1438
|
+
<div id="vfs-files-list" class="vfs-files-list"></div>
|
|
1439
|
+
</div>
|
|
1440
|
+
<div class="vfs-viewer">
|
|
1441
|
+
<div class="vfs-viewer-header">
|
|
1442
|
+
<span id="vfs-preview-name" class="vfs-preview-title">No file selected</span>
|
|
1443
|
+
<div class="vfs-viewer-actions">
|
|
1444
|
+
<button id="btn-vfs-load" class="btn btn-primary btn-sm">Load into REPL</button>
|
|
1445
|
+
<button id="btn-vfs-download" class="btn btn-secondary btn-sm">Download</button>
|
|
1446
|
+
</div>
|
|
1447
|
+
</div>
|
|
1448
|
+
<pre id="vfs-preview-content" class="vfs-code-view"></pre>
|
|
1449
|
+
</div>
|
|
1450
|
+
</div>
|
|
1451
|
+
</div>
|
|
1452
|
+
</div>
|
|
1453
|
+
|
|
1454
|
+
<!-- Codecs Modal -->
|
|
1455
|
+
<div id="codecs-modal" class="modal">
|
|
1456
|
+
<div class="modal-dialog codecs-dialog">
|
|
1457
|
+
<div class="modal-header">
|
|
1458
|
+
<div style="display: flex; align-items: center; gap: 10px;">
|
|
1459
|
+
<h3>Codecs & Custom Serializers / Deserializers</h3>
|
|
1460
|
+
<span class="engine-badge">:codecs subsystem</span>
|
|
1461
|
+
</div>
|
|
1462
|
+
<button id="btn-close-codecs" class="btn-close">×</button>
|
|
1463
|
+
</div>
|
|
1464
|
+
<div class="modal-body codecs-body">
|
|
1465
|
+
<!-- Section 1: Standard Built-in Codecs -->
|
|
1466
|
+
<div class="codecs-section">
|
|
1467
|
+
<div class="codecs-section-header">
|
|
1468
|
+
<h4>Standard Built-in Codecs</h4>
|
|
1469
|
+
<span class="section-subtitle">Click 'Load' to activate bidirectional serialization in the REPL session</span>
|
|
1470
|
+
</div>
|
|
1471
|
+
<div class="codec-grid">
|
|
1472
|
+
<!-- int card -->
|
|
1473
|
+
<div class="codec-card" id="card-codec-int">
|
|
1474
|
+
<div class="codec-card-top">
|
|
1475
|
+
<div class="codec-card-title">
|
|
1476
|
+
<span class="codec-icon">🔢</span>
|
|
1477
|
+
<span class="codec-name">int</span>
|
|
1478
|
+
<span id="codec-badge-int" class="codec-status-badge">Inactive</span>
|
|
1479
|
+
</div>
|
|
1480
|
+
<div class="codec-types">Target: <span class="badge-type">$int</span></div>
|
|
1481
|
+
<div class="codec-desc">Arbitrary-precision signed integers serialized to/from sign-magnitude bit sequences.</div>
|
|
1482
|
+
</div>
|
|
1483
|
+
<div class="codec-card-footer">
|
|
1484
|
+
<div class="codec-caps">
|
|
1485
|
+
<span class="cap-badge print">Serializer</span>
|
|
1486
|
+
<span class="cap-badge parse">Deserializer</span>
|
|
1487
|
+
</div>
|
|
1488
|
+
<button id="btn-toggle-codec-int" class="btn btn-sm btn-primary">Load</button>
|
|
1489
|
+
</div>
|
|
1490
|
+
</div>
|
|
1491
|
+
|
|
1492
|
+
<!-- utf8 card -->
|
|
1493
|
+
<div class="codec-card" id="card-codec-utf8">
|
|
1494
|
+
<div class="codec-card-top">
|
|
1495
|
+
<div class="codec-card-title">
|
|
1496
|
+
<span class="codec-icon">🔤</span>
|
|
1497
|
+
<span class="codec-name">utf8</span>
|
|
1498
|
+
<span id="codec-badge-utf8" class="codec-status-badge">Inactive</span>
|
|
1499
|
+
</div>
|
|
1500
|
+
<div class="codec-types">Target: <span class="badge-type">$list $byte</span></div>
|
|
1501
|
+
<div class="codec-desc">UTF-8 strings serialized to/from linked lists of 8-bit bytes ($byte).</div>
|
|
1502
|
+
</div>
|
|
1503
|
+
<div class="codec-card-footer">
|
|
1504
|
+
<div class="codec-caps">
|
|
1505
|
+
<span class="cap-badge print">Serializer</span>
|
|
1506
|
+
<span class="cap-badge parse">Deserializer</span>
|
|
1507
|
+
</div>
|
|
1508
|
+
<button id="btn-toggle-codec-utf8" class="btn btn-sm btn-primary">Load</button>
|
|
1509
|
+
</div>
|
|
1510
|
+
</div>
|
|
1511
|
+
|
|
1512
|
+
<!-- json card -->
|
|
1513
|
+
<div class="codec-card" id="card-codec-json">
|
|
1514
|
+
<div class="codec-card-top">
|
|
1515
|
+
<div class="codec-card-title">
|
|
1516
|
+
<span class="codec-icon">📋</span>
|
|
1517
|
+
<span class="codec-name">json</span>
|
|
1518
|
+
<span id="codec-badge-json" class="codec-status-badge">Inactive</span>
|
|
1519
|
+
</div>
|
|
1520
|
+
<div class="codec-types">Target: <span class="badge-type universal">Universal (*)</span></div>
|
|
1521
|
+
<div class="codec-desc">Arbitrary algebraic products and variants mapped to standard JSON objects and values.</div>
|
|
1522
|
+
</div>
|
|
1523
|
+
<div class="codec-card-footer">
|
|
1524
|
+
<div class="codec-caps">
|
|
1525
|
+
<span class="cap-badge print">Serializer</span>
|
|
1526
|
+
<span class="cap-badge parse">Deserializer</span>
|
|
1527
|
+
</div>
|
|
1528
|
+
<button id="btn-toggle-codec-json" class="btn btn-sm btn-primary">Load</button>
|
|
1529
|
+
</div>
|
|
1530
|
+
</div>
|
|
1531
|
+
|
|
1532
|
+
<!-- ieee card -->
|
|
1533
|
+
<div class="codec-card" id="card-codec-ieee">
|
|
1534
|
+
<div class="codec-card-top">
|
|
1535
|
+
<div class="codec-card-title">
|
|
1536
|
+
<span class="codec-icon">🧮</span>
|
|
1537
|
+
<span class="codec-name">ieee</span>
|
|
1538
|
+
<span id="codec-badge-ieee" class="codec-status-badge">Inactive</span>
|
|
1539
|
+
</div>
|
|
1540
|
+
<div class="codec-types">Target: <span class="badge-type">$float</span></div>
|
|
1541
|
+
<div class="codec-desc">IEEE 754 floating point numbers serialized to sign, exponent, and mantissa fields.</div>
|
|
1542
|
+
</div>
|
|
1543
|
+
<div class="codec-card-footer">
|
|
1544
|
+
<div class="codec-caps">
|
|
1545
|
+
<span class="cap-badge print">Serializer</span>
|
|
1546
|
+
<span class="cap-badge parse">Deserializer</span>
|
|
1547
|
+
</div>
|
|
1548
|
+
<button id="btn-toggle-codec-ieee" class="btn btn-sm btn-primary">Load</button>
|
|
1549
|
+
</div>
|
|
1550
|
+
</div>
|
|
1551
|
+
</div>
|
|
1552
|
+
</div>
|
|
1553
|
+
|
|
1554
|
+
<!-- Section 2: Active Registered Codecs -->
|
|
1555
|
+
<div class="codecs-section">
|
|
1556
|
+
<div class="codecs-section-header">
|
|
1557
|
+
<h4>Active Registered Codecs in Current Session</h4>
|
|
1558
|
+
<span class="section-subtitle">Active codecs intercept evaluation formatting and :input commands</span>
|
|
1559
|
+
</div>
|
|
1560
|
+
<div class="codecs-table-container">
|
|
1561
|
+
<table class="codec-table">
|
|
1562
|
+
<thead>
|
|
1563
|
+
<tr>
|
|
1564
|
+
<th>Codec Name</th>
|
|
1565
|
+
<th>Target Type Binding</th>
|
|
1566
|
+
<th>Capabilities</th>
|
|
1567
|
+
<th>Source</th>
|
|
1568
|
+
<th>Action</th>
|
|
1569
|
+
</tr>
|
|
1570
|
+
</thead>
|
|
1571
|
+
<tbody id="active-codecs-tbody">
|
|
1572
|
+
<!-- dynamically populated by renderCodecsModal() -->
|
|
1573
|
+
</tbody>
|
|
1574
|
+
</table>
|
|
1575
|
+
</div>
|
|
1576
|
+
</div>
|
|
1577
|
+
|
|
1578
|
+
<!-- Section 3: Custom Codec Studio -->
|
|
1579
|
+
<div class="codecs-section">
|
|
1580
|
+
<div class="codecs-section-header">
|
|
1581
|
+
<h4>Custom Serializer & Deserializer Studio</h4>
|
|
1582
|
+
<span class="section-subtitle">Create and test custom domain formats (e.g. Yes/No booleans, Hex, Currency, Dates)</span>
|
|
1583
|
+
</div>
|
|
1584
|
+
|
|
1585
|
+
<div class="codec-studio-box">
|
|
1586
|
+
<div class="studio-top-bar">
|
|
1587
|
+
<div class="studio-field">
|
|
1588
|
+
<label for="codec-preset-select">Preset Template:</label>
|
|
1589
|
+
<select id="codec-preset-select">
|
|
1590
|
+
<option value="yn">Yes / No ($bool)</option>
|
|
1591
|
+
<option value="hex">Hexadecimal 0x... ($int)</option>
|
|
1592
|
+
<option value="currency">Currency $XX.YY ($int cents)</option>
|
|
1593
|
+
<option value="blank">Blank / Custom</option>
|
|
1594
|
+
</select>
|
|
1595
|
+
</div>
|
|
1596
|
+
|
|
1597
|
+
<div class="studio-field">
|
|
1598
|
+
<label for="codec-custom-name">Codec Name:</label>
|
|
1599
|
+
<input type="text" id="codec-custom-name" placeholder="e.g. yn" value="yn">
|
|
1600
|
+
</div>
|
|
1601
|
+
|
|
1602
|
+
<div class="studio-field" style="flex: 1;">
|
|
1603
|
+
<label for="codec-custom-type">Target Type:</label>
|
|
1604
|
+
<select id="codec-custom-type">
|
|
1605
|
+
<!-- populated dynamically from state.typeAliases -->
|
|
1606
|
+
</select>
|
|
1607
|
+
</div>
|
|
1608
|
+
</div>
|
|
1609
|
+
|
|
1610
|
+
<div class="studio-editors-grid">
|
|
1611
|
+
<div class="studio-editor-pane">
|
|
1612
|
+
<div class="pane-title">
|
|
1613
|
+
<span class="cap-badge parse">Deserializer (parse)</span>
|
|
1614
|
+
<span class="pane-hint"><code>(text, context) => Value</code></span>
|
|
1615
|
+
</div>
|
|
1616
|
+
<textarea id="codec-custom-parse" class="codec-code-area" spellcheck="false" rows="7"></textarea>
|
|
1617
|
+
</div>
|
|
1618
|
+
|
|
1619
|
+
<div class="studio-editor-pane">
|
|
1620
|
+
<div class="pane-title">
|
|
1621
|
+
<span class="cap-badge print">Serializer (print)</span>
|
|
1622
|
+
<span class="pane-hint"><code>(value, context) => string</code></span>
|
|
1623
|
+
</div>
|
|
1624
|
+
<textarea id="codec-custom-print" class="codec-code-area" spellcheck="false" rows="7"></textarea>
|
|
1625
|
+
</div>
|
|
1626
|
+
</div>
|
|
1627
|
+
|
|
1628
|
+
<div class="studio-test-bar">
|
|
1629
|
+
<div class="studio-test-input-wrap">
|
|
1630
|
+
<label for="codec-test-input">Test Input:</label>
|
|
1631
|
+
<input type="text" id="codec-test-input" placeholder="e.g. yes or 0xFF or $42.50" value="yes">
|
|
1632
|
+
</div>
|
|
1633
|
+
<button id="btn-test-parse" class="btn btn-secondary btn-sm" title="Test parse function against test input">🧪 Test Parse</button>
|
|
1634
|
+
<button id="btn-test-print" class="btn btn-secondary btn-sm" title="Test print function against current REPL value (:val)">🖨 Test Print</button>
|
|
1635
|
+
<button id="btn-register-codec" class="btn btn-primary btn-sm" title="Register codec in active REPL state">✓ Register in REPL</button>
|
|
1636
|
+
<button id="btn-save-codec-vfs" class="btn btn-secondary btn-sm" title="Export as standalone .mjs file to VFS">💾 Save to VFS</button>
|
|
1637
|
+
</div>
|
|
1638
|
+
|
|
1639
|
+
<div id="codec-studio-status" class="studio-status-box"></div>
|
|
1640
|
+
</div>
|
|
1641
|
+
</div>
|
|
1642
|
+
</div>
|
|
1643
|
+
</div>
|
|
1644
|
+
</div>
|
|
1645
|
+
|
|
1646
|
+
<!-- Input Popup Modal -->
|
|
1647
|
+
<div id="input-popup-modal" class="modal">
|
|
1648
|
+
<div class="modal-dialog input-popup-dialog">
|
|
1649
|
+
<div class="modal-header">
|
|
1650
|
+
<div style="display: flex; align-items: center; gap: 10px;">
|
|
1651
|
+
<h3>📥 Codec Input</h3>
|
|
1652
|
+
<span class="engine-badge">:input directive</span>
|
|
1653
|
+
</div>
|
|
1654
|
+
<button id="btn-close-input-popup" class="btn-close">×</button>
|
|
1655
|
+
</div>
|
|
1656
|
+
<div class="modal-body input-popup-body">
|
|
1657
|
+
<div id="input-popup-banner" class="input-popup-banner">
|
|
1658
|
+
<span id="input-popup-hint">Enter input string to deserialize into a K algebraic value:</span>
|
|
1659
|
+
</div>
|
|
1660
|
+
|
|
1661
|
+
<div class="input-popup-form-row">
|
|
1662
|
+
<div class="studio-field" style="flex: 1;">
|
|
1663
|
+
<label for="input-popup-type">Target Type:</label>
|
|
1664
|
+
<select id="input-popup-type">
|
|
1665
|
+
<!-- populated dynamically -->
|
|
1666
|
+
</select>
|
|
1667
|
+
</div>
|
|
1668
|
+
<div class="studio-field" style="flex: 1;">
|
|
1669
|
+
<label for="input-popup-codec">Deserializer Codec:</label>
|
|
1670
|
+
<select id="input-popup-codec">
|
|
1671
|
+
<!-- populated dynamically -->
|
|
1672
|
+
</select>
|
|
1673
|
+
</div>
|
|
1674
|
+
</div>
|
|
1675
|
+
|
|
1676
|
+
<div class="input-popup-field">
|
|
1677
|
+
<div class="pane-title" style="margin-bottom: 6px;">
|
|
1678
|
+
<label for="input-popup-text" style="font-size: 11px; color: var(--text-muted); font-weight: 500;">Input String / Payload:</label>
|
|
1679
|
+
<span class="pane-hint">Enter: submit • Shift+Enter: newline</span>
|
|
1680
|
+
</div>
|
|
1681
|
+
<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>
|
|
1682
|
+
</div>
|
|
1683
|
+
|
|
1684
|
+
<div id="input-popup-samples" class="input-popup-samples">
|
|
1685
|
+
<span class="samples-label">Quick samples:</span>
|
|
1686
|
+
<div id="input-samples-list" class="samples-list"></div>
|
|
1687
|
+
</div>
|
|
1688
|
+
|
|
1689
|
+
<div id="input-popup-status" class="input-popup-status"></div>
|
|
1690
|
+
|
|
1691
|
+
<div class="input-popup-actions">
|
|
1692
|
+
<button id="btn-cancel-input" class="btn btn-secondary">Cancel</button>
|
|
1693
|
+
<button id="btn-submit-input" class="btn btn-primary">✓ Submit to REPL</button>
|
|
1694
|
+
</div>
|
|
1695
|
+
</div>
|
|
1696
|
+
</div>
|
|
1697
|
+
</div>
|
|
1698
|
+
|
|
1699
|
+
<!-- Help Modal -->
|
|
1700
|
+
<div id="help-modal" class="modal">
|
|
1701
|
+
<div class="modal-dialog help-dialog">
|
|
1702
|
+
<div class="modal-header">
|
|
1703
|
+
<h3>K REPL Help & Reference</h3>
|
|
1704
|
+
<button id="btn-close-help" class="btn-close">×</button>
|
|
1705
|
+
</div>
|
|
1706
|
+
<div class="modal-body help-body">
|
|
1707
|
+
<div class="help-section">
|
|
1708
|
+
<h4>REPL Commands</h4>
|
|
1709
|
+
<table class="help-table">
|
|
1710
|
+
<tr><td><code>:load <file></code></td><td>Load a <code>.k</code> source or <code>.klib</code> library into current state</td></tr>
|
|
1711
|
+
<tr><td><code>:t <name></code></td><td>Display the derived type of relation <code><name></code></td></tr>
|
|
1712
|
+
<tr><td><code>:d <name></code></td><td>Display definition of relation <code><name></code></td></tr>
|
|
1713
|
+
<tr><td><code>:type <name></code></td><td>Show or define a type (<code>:type name = <...></code>)</td></tr>
|
|
1714
|
+
<tr><td><code>:rel <name> = expr</code></td><td>Define a relation alias</td></tr>
|
|
1715
|
+
<tr><td><code>:codes</code></td><td>List all type aliases</td></tr>
|
|
1716
|
+
<tr><td><code>:rels</code></td><td>List all relation aliases</td></tr>
|
|
1717
|
+
<tr><td><code>:codecs</code></td><td>List all loaded codecs / serializers</td></tr>
|
|
1718
|
+
<tr><td><code>:codec load <name|file></code></td><td>Load a built-in codec (<code>int</code>, <code>utf8</code>, <code>json</code>, <code>ieee</code>) or file</td></tr>
|
|
1719
|
+
<tr><td><code>:codec define n t b</code></td><td>Define inline custom codec: <code>name type { parse: ..., print: ... }</code></td></tr>
|
|
1720
|
+
<tr><td><code>:codec unload <name></code></td><td>Unload a registered codec</td></tr>
|
|
1721
|
+
<tr><td><code>:input <type> [codec]</code></td><td>Read next input line using specified codec</td></tr>
|
|
1722
|
+
<tr><td><code>:val</code></td><td>Print current accumulator value</td></tr>
|
|
1723
|
+
<tr><td><code>:klib <file></code></td><td>Export current state as a <code>.klib</code> binary file</td></tr>
|
|
1724
|
+
<tr><td><code>:reset</code></td><td>Clear state and reset to initial environment</td></tr>
|
|
1725
|
+
<tr><td><code>:help</code></td><td>Show command reference</td></tr>
|
|
1726
|
+
</table>
|
|
1727
|
+
</div>
|
|
1728
|
+
|
|
1729
|
+
<div class="help-section">
|
|
1730
|
+
<h4>Expressions & Snippets</h4>
|
|
1731
|
+
<p style="margin-bottom: 8px; color: var(--text-muted);">Any raw K expression entered at the prompt is compiled and evaluated on top of the current state using WebAssembly.</p>
|
|
1732
|
+
<ul>
|
|
1733
|
+
<li><code>10</code> — Integer literal (encodes to bit-path via arithmetics)</li>
|
|
1734
|
+
<li><code>{10 int x, 5 int y} plus</code> — Composition / function call</li>
|
|
1735
|
+
<li><code>succ = {inv x, _ y} _inc;</code> — Define a relation (ends with <code>;</code>)</li>
|
|
1736
|
+
<li><code>$ bit = < {} 0, {} 1 >;</code> — Define a type</li>
|
|
1737
|
+
</ul>
|
|
1738
|
+
</div>
|
|
1739
|
+
|
|
1740
|
+
<div class="help-section">
|
|
1741
|
+
<h4>Keyboard Shortcuts</h4>
|
|
1742
|
+
<table class="help-table">
|
|
1743
|
+
<tr><td><code>Enter</code></td><td>Execute if snippet is complete, otherwise insert newline</td></tr>
|
|
1744
|
+
<tr><td><code>Shift+Enter</code></td><td>Insert newline without executing</td></tr>
|
|
1745
|
+
<tr><td><code>Ctrl+Enter</code> / <code>Cmd+Enter</code></td><td>Force execute whatever is in input</td></tr>
|
|
1746
|
+
<tr><td><code>Tab</code></td><td>Autocomplete commands, aliases, and filenames</td></tr>
|
|
1747
|
+
<tr><td><code>↑</code> / <code>↓</code></td><td>Navigate command history</td></tr>
|
|
1748
|
+
<tr><td><code>Ctrl+L</code></td><td>Clear terminal output</td></tr>
|
|
1749
|
+
<tr><td><code>Esc</code></td><td>Dismiss autocomplete / clear input</td></tr>
|
|
1750
|
+
</table>
|
|
1751
|
+
</div>
|
|
1752
|
+
</div>
|
|
1753
|
+
</div>
|
|
1754
|
+
</div>
|
|
1755
|
+
|
|
1756
|
+
<!-- Drop Overlay -->
|
|
1757
|
+
<div id="drop-overlay" class="drop-overlay">
|
|
1758
|
+
<div class="drop-box">
|
|
1759
|
+
<div class="drop-icon">📥</div>
|
|
1760
|
+
<div class="drop-text">Drop .k or .klib file to load into REPL</div>
|
|
1761
|
+
</div>
|
|
1762
|
+
</div>
|
|
1763
|
+
</div>
|
|
1764
|
+
|
|
1765
|
+
<script type="module">
|
|
1766
|
+
${bundledJs}
|
|
1767
|
+
</script>
|
|
1768
|
+
</body>
|
|
1769
|
+
</html>
|
|
1770
|
+
`;
|
|
1771
|
+
|
|
1772
|
+
// 5. Write to repl.html in package dir and workspace root
|
|
1773
|
+
const outPath = path.join(projectRoot, "repl.html");
|
|
1774
|
+
fs.writeFileSync(outPath, htmlContent, "utf8");
|
|
1775
|
+
console.log(`4. Successfully wrote: ${outPath} (${(htmlContent.length / 1024).toFixed(1)} KB)`);
|
|
1776
|
+
|
|
1777
|
+
// Also copy to workspace root if different
|
|
1778
|
+
if (workspaceRoot !== projectRoot && fs.existsSync(workspaceRoot)) {
|
|
1779
|
+
const wsOutPath = path.join(workspaceRoot, "repl.html");
|
|
1780
|
+
fs.writeFileSync(wsOutPath, htmlContent, "utf8");
|
|
1781
|
+
console.log(` Also wrote to: ${wsOutPath}`);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
console.log("=== Build complete! ===");
|