Commit25b7b30cRecorded31 Mar 2026Repositorysigil-web-repl
Use unbuffered xterm output and binary lib.bundle
Message
Replace ConsoleStdout.lineBuffered with custom unbuffered Fd so the REPL prompt (which doesn't end with newline) renders immediately.
Switch from per-file HTTP fetches to single lib.bundle binary file for loading compiled Sigil modules into the WASI virtual filesystem.
Changed
assets/index.html | 68 ++++++++++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 44 insertions(+), 24 deletions(-)Diff
assets/index.htmlmodified
@@ -1118,29 +1118,37 @@
1118
term.onData(handleInput); 1119
window.addEventListener('resize', () => { if (ready) fitAddon.fit(); }); 1120
−1121
// Build virtual filesystem from lib/ directory+1121
// Load module bundle into virtual filesystem+1122
// Bundle format: [uint32 count] then per file:+1123
// [uint16 path-len] [path bytes] [uint32 data-len] [data bytes] 1124
async function buildFs() { 1125
const { File, Directory } = await import("https://cdn.jsdelivr.net/npm/@bjorn3/[email protected]/dist/index.js"); 1126
const files = {}; 1127
try {−1126
const resp = await fetch('lib-manifest.json');+1128
const resp = await fetch('lib.bundle'); 1129
if (!resp.ok) return { files, File, Directory };−1128
const manifest = await resp.json();−1129
await Promise.all(manifest.map(async (path) => {−1130
const fileResp = await fetch('lib/' + path);−1131
if (fileResp.ok) {−1132
const data = new Uint8Array(await fileResp.arrayBuffer());−1133
const parts = path.split('/');−1134
let dir = files;−1135
for (let i = 0; i < parts.length - 1; i++) {−1136
if (!dir[parts[i]]) dir[parts[i]] = {};−1137
dir = dir[parts[i]];−1138
}−1139
dir[parts[parts.length - 1]] = new File(data);+1130
const buf = await resp.arrayBuffer();+1131
const view = new DataView(buf);+1132
const decoder = new TextDecoder();+1133
let offset = 0;+1134
+1135
const count = view.getUint32(offset, true); offset += 4;+1136
for (let i = 0; i < count; i++) {+1137
const pathLen = view.getUint16(offset, true); offset += 2;+1138
const path = decoder.decode(new Uint8Array(buf, offset, pathLen)); offset += pathLen;+1139
const dataLen = view.getUint32(offset, true); offset += 4;+1140
const data = new Uint8Array(buf, offset, dataLen); offset += dataLen;+1141
+1142
const parts = path.split('/');+1143
let dir = files;+1144
for (let j = 0; j < parts.length - 1; j++) {+1145
if (!dir[parts[j]]) dir[parts[j]] = {};+1146
dir = dir[parts[j]]; 1147
}−1141
}));+1148
dir[parts[parts.length - 1]] = new File(new Uint8Array(data));+1149
} 1150
} catch (e) {−1143
console.warn('Module loading:', e.message);+1151
console.warn('Bundle loading:', e.message); 1152
} 1153
return { files, File, Directory }; 1154
}@@ -1167,21 +1175,33 @@
1175
return result; 1176
} 1177
+1178
// Custom WASI Fd that writes to xterm immediately (unbuffered)+1179
function makeTermWriter(termRef, wasiModule) {+1180
const decoder = new TextDecoder();+1181
const fd = new wasiModule.Fd();+1182
fd.fd_write = function(data) {+1183
const text = decoder.decode(data, { stream: true });+1184
termRef.write(text.replace(/\n/g, '\r\n'));+1185
return { ret: 0, nwritten: data.byteLength };+1186
};+1187
fd.fd_fdstat_get = function() {+1188
const { Fdstat } = wasiModule.wasi;+1189
return { ret: 0, fdstat: new Fdstat(2, 0) };+1190
};+1191
return fd;+1192
}+1193
1194
// Initialize WASI and load WASM 1195
async function init() {−1172
const { WASI, File: WasiFile, OpenFile, ConsoleStdout, PreopenDirectory } =−1173
await import("https://cdn.jsdelivr.net/npm/@bjorn3/[email protected]/dist/index.js");+1196
const wasiModule = await import("https://cdn.jsdelivr.net/npm/@bjorn3/[email protected]/dist/index.js");+1197
const { WASI, File: WasiFile, Fd, Fdstat, OpenFile, PreopenDirectory } = wasiModule; 1198
1199
const { files: libFiles, File: FileClass, Directory: DirectoryClass } = await buildFs(); 1200
1201
const fds = [ 1202
new OpenFile(new WasiFile([])), // stdin−1179
ConsoleStdout.lineBuffered(msg => {−1180
term.write(msg.replace(/\n/g, '\r\n') + '\r\n');−1181
}),−1182
ConsoleStdout.lineBuffered(msg => {−1183
term.write(msg.replace(/\n/g, '\r\n') + '\r\n');−1184
}),+1203
makeTermWriter(term, wasiModule), // stdout+1204
makeTermWriter(term, wasiModule), // stderr 1205
new PreopenDirectory("/", new Map([ 1206
["lib", makeDir(libFiles, FileClass, DirectoryClass)] 1207
])),