-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprd.txt
More file actions
566 lines (304 loc) · 64.8 KB
/
prd.txt
File metadata and controls
566 lines (304 loc) · 64.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
self-modifying-page (1).html
File
environment-detector.html
File
Can you research the idea of single runable file self modifying app that bundles js package files (package json, source files etc) into a single html file. Has this been attempted? I'm thinking it could have edit mode and load its own dev env in a webcontainer.io wasm container. Would it also be possible to arrange contents of html file such that the build version is at the top and the source below and then skip download of source unless requested. This is a conversation I had about this idea. oday I’m exploring space between single html file type artifact and JavaScript package bundle. I want something that is a single file so can be copied, saved, etc but I also want everything I would have with a usual js package. Wondering if you could come up with a way to bundle into the single runnable html file but also pack/unpack to package folder. Luke — Today at 12:20 It should work or bootstrap correctly when saved to disk by user. Eg it needs to work under file:// It should have a way to switch from file:// to some other origin It should be able to load its own dev mode by loading webcontainer.io and populating the vm It should be able to install dependencies (using npm or pnpm) in web container It should save the package state from the container back to the html It should track if there are unsaved changes and allow user to save changes using file picker api It should keep the fd so that it can auto save again in future It would be nice if its possible to download as zip package It would be nice if it can push to git or there is some version which is single file + ref to source git repo. chrisshank — Today at 12:47 You might be interested in tiddy wiki and tom’s self-contained observable notebook (https://bsky.app/profile/larkworthy.bsky.social/post/3lf45haaqcs2h) tom larkworthy (@larkworthy.bsky.social) the hermetic, self-hosted, self-sustainable, recursively exportable, offline-first, file-first, web-based reactive open-source programming substrate built on observable runtime is online finally! tomlarkworthy.github.io/lopecode/web... I am hoping I can explain parts at @hytradboi.bsky.social 🤞 Image Bluesky•07/01/2568, 05:07 Luke — Today at 12:47 Main goal is I want something regular people can use that doesn’t require local dev env chrisshank — Today at 12:48 Import maps and the file system access api (chromium only) are also pretty relevant! Luke — Today at 12:49 Yup for saving it if loaded from disk Will play nice with web applets too chrisshank — Today at 12:50 If you’re interested i can share small wrapper around the file access api that lets you persist the file handler someone opened in indexeddb, so you can only have to choose a file once Luke — Today at 12:52 I think thats what this code does <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Self-Modifying HTML Page</title> Expand self-modifying-page (1).html 16 KB I really like the idea of single file entry point people can pass around and edit as way to show they have agency to modify it That is what html was originally but it was lost when we made things more complex. Most client side js apps are single entry point anyway these days. chrisshank — Today at 12:54 Ah yes, pretty much that! Luke — Today at 12:54 Another somewhat relevant bit <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Environment Detector</title> Expand environment-detector.html 15 KB It should all bootstrap off one esm module that loads other bits as needed on demand I also wonder if the file is arranged with bootstrap html and main esm module then source files at the end if its possible for the download to stop if not in edit mode. chrisshank — Today at 12:55 just abstracted my impl. behind a class https://github.com/folk-systems/folk-canvas/blob/main/packages/labs/src/persistence/file.ts Luke — Today at 12:56 so the whole package doesnt need to be downloaded. probably over optimization but interesting challenge Luke — Today at 12:56 thanks Perhaps with right llm txt you could even teach llm how to output artifacts in the right format. Then you could simply download and run. Just dumping ideas as I go Please research and come up with implementation plan.
ChatGPT said:
Thanks for sharing this detailed concept and files! To conduct the most helpful research and implementation plan, could you please confirm a few things:
Target Environment: Should the single-file app work fully offline under file://, or is it acceptable for some dev features (e.g., npm install) to require a live internet connection?
Runtime & Tooling: Are you specifically committed to using webcontainer.io for dev mode, or open to other WASM-based Node.js environments?
Packaging Flow: Should the HTML file be able to extract the full project to disk (like a package.json + src folder), and re-bundle itself later? Or is it one-way?
Build Separation: When you mention putting build content at the top and source below for selective loading, do you envision this being manually coded, or should a bundler handle it?
Version Control: Is integration with Git (e.g., pushing from the browser) a priority, or a stretch goal?
Let me know so I can tailor the research and plan accordingly!
You said:
1. We want it to work everywhere it should detect its environment and act accordingly. 2. open to others but needs to be node npm compatible and run in wasm locally. go with webcontainer as default but offer alternatives 3. yes it should be able to save as single file, export as zip, or push to git repo. 4. the apps bundler will handle it and build will run as part of the container dev mode 5. we should have basic version without version control but it would be a nice optional addition.
ChatGPT said:
Great, I’ll explore existing tools and efforts that combine bundling JS packages and source files into a self-contained, runnable, and editable HTML file, with environment detection, self-modification, and optional dev mode using WebContainer or similar WASM-based tools. I’ll also look into techniques for packaging, conditional loading, and optional Git integration.
I’ll let you know once I have a full implementation plan and analysis ready.
Great, I’ll explore existing tools and efforts that combine bundling JS packages and source files into a self-contained, runnable, and editable HTML file, with environment detection, self-modification, and optional dev mode using WebContainer or similar WASM-based tools. I’ll also look into techniques for packaging, conditional loading, and optional Git integration.
I’ll let you know once I have a full implementation plan and analysis ready.
Building a Self-Contained Single-File Web App (with Node.js Package Bundled)
Overview and Existing Projects
Creating a single HTML file application that embeds an entire Node.js project (source files, package.json, and dependencies) is an ambitious goal. This idea has parallels in projects like TiddlyWiki and Tom Larkworthy’s LopeCode, as well as modern in-browser Node.js environments like StackBlitz WebContainers. These examples demonstrate pieces of the puzzle:
TiddlyWiki (Single-File Edition): TiddlyWiki is a personal wiki that stores both its application code and user data in a single HTML file. All content (“tiddlers”) and even the core JS logic are embedded within <div> or <script> tags inside the HTML (Data Storage in Single File TiddlyWiki: TiddlyWiki/Dev — documentation for developers) (Data Storage in Single File TiddlyWiki: TiddlyWiki/Dev — documentation for developers). This allows the wiki to be self-contained and offline-first. The user can open the HTML file in a browser, edit the wiki, and save changes back to the file. Traditionally, saving was tricky due to browser security, but modern browsers have introduced new capabilities to make this easier. For example, Chrome/Edge now support the File System Access API, which TiddlyWiki can use to save directly to the file without external extensions (File System Access API and saving locally - Talk @ TiddlyWiki). (In unsupported browsers, TiddlyWiki falls back to downloading the modified file, which the user must manually save.) TiddlyWiki demonstrates that complex apps can live in one file, but it does not bundle a Node.js runtime – it runs entirely in-browser JavaScript.
Observable LopeCode (Moldable Webfile): LopeCode is a recent project by Tom Larkworthy that packages an Observable notebook and runtime into one HTML “webfile.” It’s described as “a self-hosted, self-sustainable, recursively exportable, offline-first, file-first, hermetic, web-based programming substrate based on the Observable runtime” (Future of Coding Weekly (old posts, page 12) | Future of Coding Weekly). In essence, LopeCode is an HTML page that contains a reactive programming environment (via Observable’s JS runtime) along with its own source code. It can be edited in place and even exported recursively (suggesting it can produce an updated copy of itself or other bundles). This is very close in spirit to the requested app: it runs offline, contains its source, and can save changes. However, it’s focused on the Observable notebook style of development (reactive cells), not a full Node.js package with npm. It proves the viability of a file-first offline dev environment in a single HTML file.
WebContainer-Based Apps (e.g. StackBlitz): StackBlitz’s WebContainers allow running a Node.js environment fully in the browser via WebAssembly (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW). They have demonstrated running Node.js servers and package managers (npm) client-side. For example, WebContainer technology has been used to spin up a TiddlyWiki Node.js server in-browser (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW) (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW). In that case, Node and npm (compiled to WASM) run inside the page, fetch packages from npm, and even serve web content via a Service Worker that intercepts requests and forwards them to the Node.js process (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW). StackBlitz’s Web Publisher and Codeflow are products that embed such an environment in a simple interface, allowing users to edit code, see a live preview, and even commit changes to GitHub – all without installing Node locally (Ask HN: Visual interfaces for editing markdown-based static sites? | Hacker News). These services are not single-file HTML apps (they are online IDEs), but the underlying tech (WebContainer API) can be leveraged for our purpose. It provides a WASM-based Node.js runtime and a virtual filesystem accessible from the browser, which are key for “dev mode” in a self-contained app.
Have similar apps been built before? In summary, pieces of this have been done: TiddlyWiki shows self-contained data+app with self-saving, LopeCode shows a self-exportable coding environment in one file, and WebContainer demos show that a Node.js project can run in-browser (with npm installs and all). However, an app that combines all these aspects – full Node project + self-editable source + in-browser Node dev + one-file distribution – is still cutting-edge. The closest might be LopeCode (for the self-contained dev environment aspect) and some StackBlitz templates. It’s an area with active experimentation (as of 2024–2025), so leveraging these projects’ techniques is wise. Below, we outline an implementation plan, with relevant tools, while noting potential limitations.
Environment Detection and Behavior Differences
One of the first challenges is making the app adapt to different contexts: opened as a local file, served from localhost, or from an HTTPS web origin. The capabilities vary:
file:// (Local File) – If the user opens the HTML directly from disk, the origin is a file URI. Many modern web APIs consider this an insecure context, which imposes restrictions. For example, Service Workers won’t register on file://, certain network requests may be blocked, and some APIs (like the File System Access API) might not be fully available. External script imports (from CDN) often fail under file:// due to CORS/security (environment-detector.html) (environment-detector.html). The app should detect this and inform the user or degrade gracefully. A snippet from an environment detector shows different handling: if window.location.protocol === 'file:', it notes that “File:// protocol – Various security restrictions apply” (environment-detector.html). In this mode, the app should avoid reliance on network or service workers. On the positive side, running as a file can allow direct local file access in some cases (e.g., Chrome might allow the File System Access API with a user gesture even on file://). At minimum, the app should warn the user if certain features won’t work until they serve the file on localhost or via HTTPS.
Localhost (http://localhost or 127.0.0.1) – Browsers treat localhost as a secure context (nearly equivalent to HTTPS for API access). This means features like Service Workers, FS Access API, etc., are allowed on http://localhost. The environment detection should recognize this (e.g. hostname == 'localhost') (environment-detector.html) and can enable full functionality. If the user runs a tiny web server to serve the HTML, the app will work more smoothly. In dev scenarios, we might encourage using localhost for full features (possibly even the app’s “dev mode” could spawn a local server via Node WebContainer, though that gets meta). Generally, localhost is ideal for editing and testing.
HTTPS – If the app is hosted on a secure domain, it has access to all browser features (aside from cross-origin limitations). The code should detect location.protocol === 'https:' (environment-detector.html) and treat this as the optimal scenario: full PWA capabilities, no feature restrictions. In this mode, we can use service workers for offline caching, the File System Access API (since it’s a secure origin), etc. The app might also hide any “self-save” button if running on a website (since saving would typically be to some cloud or just rely on normal web app behavior). Conversely, in file/localhost mode we’d show a “Save to file” option.
Environment detection implementation: We can include a small script early in the HTML to set a global env variable or similar flags:
const isFile = location.protocol === 'file:';
const isLocalhost = ['localhost','127.0.0.1'].includes(location.hostname);
const isSecure = location.protocol === 'https:' || isLocalhost;
Using these, adjust the app’s behavior:
If isFile and not supported, display a warning about limitations (e.g., “Running from a local file may restrict certain features. For full functionality, open this on http://localhost or enable appropriate flags.”).
If isSecure (HTTPS or localhost), proceed to load advanced features (like the WebContainer-based dev environment, service worker, etc.).
If not secure (e.g. plain http:// on a non-localhost domain), treat similarly to file – modern APIs will be unavailable or limited; we likely assume the app won’t be served as plain http in production.
By handling this early, we prevent confusing errors when the user tries to use features in the wrong context. For instance, an attempt to use showOpenFilePicker() (File System Access) outside a secure context will fail; our app can check and either disable the self-save or prompt the user appropriately.
Self-Editing and Saving (File System Access API)
A core feature is self-editing: the HTML app should contain its own source code and allow updating it. This breaks down into two parts: editing the content in memory (e.g. through an in-browser code editor UI or programmatically), and then saving the modifications so that next time the HTML file is opened, it reflects the changes.
For the editing interface, we could embed a code editor component (like Monaco or CodeMirror) in the HTML, allowing the user to open the source files (which we have embedded, perhaps in JSON or in a virtual FS) and edit them. Once edits are done, saving can happen via:
File System Access API (FS Access): This API (supported in Chromium-based browsers and now also in some others with flags) lets web apps read/write local files with user permission. The app can request a handle to its own file. For example, the user could click “Select This File” and use the file picker to pick the HTML file itself. We can verify it’s the correct file (maybe by some content hash) and then write changes. This approach is demonstrated in the “self-modifying page” example provided. That page asks the user to choose the current file with showOpenFilePicker, then uses a FileSystemWritableFileStream to overwrite content (such as updating a timestamp in the HTML) and finally prompts a reload. Modern TiddlyWiki editions also use this API: “The new file system API may allow us to once again save single file wikis without any external requirements, at least in Chrome and Edge.” (File System Access API and saving locally - Talk @ TiddlyWiki). Our app can do similarly. Workflow: On first save, call window.showOpenFilePicker() to let user pick the HTML file (or perhaps start with launchQueue if opened as a PWA). Once we have a handle, store it (e.g., in IndexedDB for reuse so the user isn’t prompted every time) (self-modifying-page (1).html) (self-modifying-page (1).html). Then on each save action, write the updated HTML blob to this handle. This can directly overwrite the file on disk. We must be careful to serialize the live state of the app (including any updated source code or data) into the HTML text. Essentially, we’d regenerate the HTML string or the relevant portions (source bundle, etc.) from memory and write that out.
Fallback download: If FS Access is not available (e.g. Firefox or Safari, or if running in an insecure context), we should fall back to offering the updated file as a download. For instance, after editing, we can create a Blob of the new HTML and use URL.createObjectURL to trigger a download link. The user would then manually replace the old file with the new one. This is less seamless but ensures no data loss. (Older TiddlyWikis used this method via an <iframe> hack to download the updated HTML).
Saving to external locations: As an advanced feature, one could also export the project as a separate HTML or as a ZIP archive (discussed later). Exporting to a ZIP might be useful if the user wants the project in a standard folder format (with package.json, src files, etc.). We can implement “Export as ZIP” by collecting all source files and assets from the app’s storage and using a library like JSZip to generate a zip file blob for download.
When implementing self-saving, data format is crucial. We need to embed the source code and project files in the HTML in a way that they can be updated programmatically. Some ideas:
Embed all files as JSON text within a <script type="application/json" id="sourceBundle"> tag. This could contain a JSON structure (perhaps a simple mapping of filenames to content, and maybe other metadata like last modified). The app’s editor can parse this JSON to present the files. On save, we stringify the updated JSON and replace that script tag’s content.
Alternatively, embed a base64 encoded zip of the source. This might be efficient in space, but updating a zip in-place (within the HTML text) is more complex (we’d have to reconstruct and re-encode it on each save). JSON text is simpler to manipulate in JavaScript.
Ensure that whatever format, it’s placed in the HTML in a known marker that we can find and replace. For example, demarcate with comments <!-- BEGIN SOURCE --> ... <!-- END SOURCE --> to help locate insertion point when rewriting.
We also want the build output (the compiled app code) separate and preferably at the top of the HTML, per the requirements. This suggests structuring the HTML like:
<script id="appCompiled" type="module">
// [This is the compiled/transpiled application code for quick load]
</script>
... (maybe the UI HTML here) ...
<script id="sourceBundle" type="application/json">
{ /* JSON with all source files and package.json, etc. */ }
</script>
This way, if the app is just being run (viewed) by an end-user, the top <script id="appCompiled"> executes immediately to start the app, and it doesn’t need to parse or download the large source bundle below. The source bundle could even be loaded on-demand: the compiled script can simply not touch it unless the user enters “edit mode”. We could further optimize by compressing the source JSON (perhaps it could be a base64 zip blob in a <script> tag to shrink size).
Limitation: The HTML file could become very large if the project has many dependencies or files. TiddlyWiki HTML files can be several megabytes for big wikis. This is generally fine for local use, but serving a multi-MB single file might be slow. We should encourage only including what’s necessary. Perhaps for npm dependencies, we do not embed the entire node_modules (which could be huge), but instead rely on installing them inside the WebContainer when needed (with caching). We’ll cover that soon.
In-Browser Dev Mode via WebContainer (WASM Node.js)
For a rich “dev mode”, we want the user to be able to actually run Node.js and install packages inside the browser. The StackBlitz WebContainer API is the primary tool for this. It provides:
A WebAssembly-compiled Node.js runtime that runs fully in the browser (no server).
A virtual filesystem (in-memory by default) that we can populate with files.
Ability to execute commands like npm install or node index.js inside that environment (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW).
Integration with the browser’s networking through a Service Worker, so a Node.js web server can serve content accessible to the browser page (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW).
Using WebContainer: We would include the WebContainer library (via <script src="...webcontainer.js"> or an npm module bundler). Once loaded, we call WebContainer.boot() to start the Node runtime (API Reference | WebContainers). This is an expensive operation (WebContainer’s docs note booting is heavy, so only do it when needed) (API Reference | WebContainers). That’s why we should only initialize it when the user explicitly enters dev/edit mode, not on every page load.
After booting, we’ll get a webcontainer instance with a .fs API to write files. We then populate the container’s filesystem with the project files:
Write the package.json (from our embedded source).
Write each source code file to the appropriate path (e.g., create /src/index.js, etc., matching the original project structure).
If there is a lockfile or node_modules cache, we could write those too. But likely, we will run npm install instead of storing node_modules due to size.
Now, to install dependencies, we have two main options:
Online install: Use the WebContainer’s npm to fetch from the npm registry. We can programmatically run a process: webcontainer.spawn('npm', ['install']). This will make network requests to fetch all dependencies. WebContainer allows network access (likely proxying via StackBlitz’s own servers to avoid CORS issues) (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW). The first time, this could be slow if there are many deps. We should show a console/log UI to inform the user of progress. Once done, the node_modules will reside in the container’s FS.
Offline install: If we want offline capability, we’d need to have cached the dependencies. Perhaps we could embed a minimal cache or use a package like pnpm which can perform offline installs if it has a store. This is complex; an easier approach is to require internet for the first install but then persist the installed state for subsequent runs. Persistence could mean saving the node_modules data to IndexedDB after installation, or even updating the single HTML file with a section containing all modules (not really feasible for large modules). A compromise is to store a compressed tarball of node_modules in browser storage and reuse it.
Given the complexity, a reasonable approach is: assume internet is available for dev mode, perform npm install normally, then use caching to avoid re-download next time. The caching could be implemented by intercepting npm’s requests via Service Worker or by manually downloading tarballs and storing them. This is an advanced optimization; initially, we can simply rely on the browser’s HTTP cache (StackBlitz likely caches packages via its own URL endpoints too).
Dev Server / Running the app: Once dependencies are installed, the user could run any npm scripts or Node commands in the container. For example, if the project has a build script or a dev server (like using Express or Vite), the user can execute it:
Use webcontainer.spawn('npm', ['run', 'dev']) or similar. For front-end frameworks, this might start a dev server on some port.
WebContainer’s service worker will intercept requests from our page to that dev server. Typically, StackBlitz maps a special URL (like https://<project>.webcontainer.io or use of localhost:<port>) to the container. We might not need to worry about the details as the WebContainer API may provide a preview iframe or URL. In a simpler case, if the project is just a Node script, we can run it and capture its output to display to the user.
Alternative runtimes: The question mentions allowing alternatives to WebContainer. Currently, WebContainer by StackBlitz is the most mature solution. Alternatives might include:
WASI Node.js: An experimental build of Node for WASM/WASI (if one exists publicly). This could run in a WebAssembly VM in the browser but would require a lot of integration work (file system, network, etc.). Not as straightforward as WebContainer.
Deno Deploy in-browser: Deno (a Node alternative) might compile to WASM as well, but it’s not mainstream to embed it client-side yet.
CheerpX WebVM: A project like WebVM uses a x86 emulator in WASM to run Linux binaries (Open-sourcing for the webplatform ? · Issue #458 - GitHub). In theory, one could run a Node binary in that VM. But this is heavy and slower, and probably overkill compared to using WebContainer.
Service-worker + SW emulation: Another approach if not using a full Node, is to polyfill certain dev capabilities directly in the browser. For example, forgo running a real Node and instead use bundler libraries in JS to achieve hot-reloading and module resolution. Projects like esbuild or browserify can run in-browser to bundle code. However, implementing package installation and a Node-like module system from scratch is a huge task. Thus, leveraging WebContainer is far more practical for real npm support.
Given these, our plan is to use WebContainer by default. We will design our code such that if WebContainer isn’t available or fails (e.g., in an unsupported browser), we either disable dev mode or have a fallback message (“Dev mode is not supported in this browser. Please use Chrome/Edge or another compatible browser.”). This ensures the single-file app still functions (in view mode and with self-save) on other browsers, even if the Node dev environment only works on Chromium-based ones (currently WebContainer supports Chromium and Firefox Nightly).
WebContainer Integration Steps:
Lazy load WebContainer: Do not include the WebContainer script in the initial HTML to avoid bloat. Instead, use dynamic import when needed:
async function loadDevEnvironment() {
const wc = await import('https://cdn.webcontainers.io/main.js'); // hypothetical URL or bundled source
// boot it
window.webcontainer = await wc.WebContainer.boot();
}
Alternatively, if we bundle the WebContainer npm package, we call WebContainer.boot() from our code after user triggers dev mode. We should show a loading indicator, as boot can take a couple of seconds.
Populate Filesystem: Use webcontainer.fs.writeFile or similar to create the project structure. We will have the list of files from our embedded source JSON. Loop through and write each file. Also create an empty node_modules directory (though npm will do that) and any other needed directories.
Install Dependencies: Provide a UI button or automatically run npm install. We can execute:
const installProc = await webcontainer.spawn('npm', ['install']);
installProc.output.pipeTo(new WritableStream({ write(data){ ... } }));
This way, we capture the installation log output and display it (so the user sees progress, and any errors). Once the process completes, check the exit code. On success, dependencies are now in the FS. (If using pnpm is desired, we could similarly spawn npm i -g pnpm then use pnpm install, but pnpm would need to be fetched. It might be easier to stick to npm for simplicity. We could also allow both by detecting a config and using one or the other.)
Dev Actions: With the environment set, the user can run a dev server or build:
If the project has a start script that serves the app (say an Express server for a backend, or a Vite server for a frontend), running it will “launch” the app within the container. Thanks to WebContainer’s service worker, any HTTP requests that the app’s JS makes to localhost or a designated URL can be fulfilled by the Node process. For example, a React app using npm start (create-react-app) could actually run and we could open an <iframe src="<container_url>"> to show it. In StackBlitz, they expose a preview iframe.
If the project is purely computational (e.g., a library or Node script), the user can run it and see console output. We should display stdout/stderr from the container’s process in a console panel.
The dev mode should also allow editing code and rerunning. We could even watch for file changes: the code editor could on save update the WebContainer FS (writing the file), so the Node process (if it’s a dev server with hot reload) picks up the change. This would give a near-real-time dev experience.
Persisting Dev State: By default, when the page is closed or reloaded, the WebContainer’s filesystem is lost (it’s in-memory). To persist project changes (like new installed packages or code edits done in dev mode), we have two strategies:
Save the changes back to the single HTML file (using the self-save mechanism). For instance, if in dev the user installed a new npm dependency or created a new file, we should update the sourceBundle JSON in our HTML and prompt the user to save the HTML. However, automatically pulling in node_modules into the HTML is not feasible (too large). Instead, perhaps just update package.json (which is manageable) and let future installs fetch the deps again. We might warn that “New dependencies will require internet or must be re-installed each session unless you save a ZIP.”
Alternatively, use IndexedDB as a cache for node_modules. We could serialize the node_modules folder (or individual packages) into IDB. Next time the app loads, if the user enters dev mode, we check IDB for a cached installation and restore those files to the WebContainer FS before running. This would make repeat dev sessions much faster and allow offline use after the first install. This approach would require careful chunking (IDB has size/transaction limits), but is doable (e.g., storing each package tarball or each file).
At minimum, persisting the source code edits is critical (we should not lose changes to user code). This we can handle by updating our in-memory source bundle JSON and calling the save routine (FS API or download). For dependencies, a simple route is to note them in package.json and let the next session install again if needed, or rely on browser HTTP cache to speed it up.
In summary, WebContainer gives us a powerful in-browser Node.js sandbox. The main limitations to note:
Browser support: WebContainer currently works on Chromium-based browsers and Beta/experimental Firefox. It won’t run on Safari due to missing features (no SharedArrayBuffer without cross-origin isolation, etc.). So dev mode will not function in all browsers.
Performance: Running Node and doing npm install in the browser is slower than on a native system. Heavy builds might be slow. There is also memory overhead (large projects could hit browser memory limits).
No Native Modules without WASM ports: Node modules that rely on native C++ addons won’t work unless WebContainer’s environment provides WASM substitutes. StackBlitz has gotten some native modules working (e.g., they compiled the sharp image library to WASM for WebContainer (Bringing Sharp to WebAssembly and WebContainers - StackBlitz Blog)), but many modules might simply fail to install or run if they assume a Node.js environment with full OS access. In practice, most pure JS modules or those with WASM/browser fallbacks will work.
Service Worker networking: The WebContainer uses a service worker to intercept requests to the dev server. This normally just works transparently if we use their API correctly, but it’s something to be aware of. If we run an Express server on port 3000 inside, and then the user’s browser tries to fetch http://localhost:3000, the service worker catches it and routes it. If our environment detection is sloppy, we might accidentally conflict with that (for example, if we also register our own service worker for offline support, we need to ensure it doesn’t interfere with WebContainer’s worker). Probably we should defer registering any service worker until we know whether we’re in normal mode or dev mode to avoid conflicts.
We can provide an option to use alternative backend if not using WebContainer. For example, if the app is running on a real Node environment (say the user actually hosted this HTML on a server with Node), that might defeat the purpose of single-file, but one could imagine an alternative “dev mode” that simply opens the code in an online editor or pushes to a repo for editing. This is out of scope for now; we’ll focus on WebContainer as the local solution.
Conditional Loading and Module Splitting (ESM on-demand)
To keep the initial load lightweight, we will split the code into modules and load features on demand:
The top of the HTML includes the minimal scripts to run the app itself (the compiled application code). This part should be just enough to get the UI up and running if the user just wants to use the app normally.
The heavy parts – e.g., the development environment, large libraries needed only for editing, etc. – will be loaded later. We can use dynamic import() for this. For instance, the code editor library, the WebContainer API, and any additional dev UI can be kept out of the initial bundle. Only when the user clicks “Enter Edit Mode” do we import('./devbundle.js') which then pulls in those dependencies.
Similarly, if the app has features that are only used in certain conditions, those could be dynamically imported. This ESM dynamic import is supported in modern browsers and will ensure the single HTML doesn’t execute all scripts unless needed. (The scripts are still physically in the HTML, unless we load from CDN – but we likely embed them or bundle them to avoid external dependence. We might put them in the HTML as type=module scripts that are not run automatically, or as type=text so they don’t run, and then evaluate them on demand. Another simpler way: include them as base64 strings and use import(/* webpackChunkName: "monaco" */'monaco-editor'), though bundling monaco in one file is huge; perhaps better to load some libs from CDN if not focusing on fully offline for those.)
Environment-based module loading: The app can also conditionally do things based on environment:
If running on file:// and we detect no network, we might avoid even trying to load something from a CDN (since it would fail). We either must embed that resource or warn the user to connect.
If in a secure context with network, we have the flexibility to fetch external resources if we chose not to embed them to save size. For example, the HTML could link to the WebContainer WASM via an external script (hosted by StackBlitz). If offline usage is a priority, embedding is better; if file size is a concern, external with a nice error message if offline might suffice.
We should also ensure any CDN or external scripts are optional. Our environment detector example tries to load Lodash from a CDN and properly handles the failure on file:// (environment-detector.html) (environment-detector.html). We can adopt a similar pattern: attempt to load external scripts only when online/allowed, and catch errors to inform the user.
In summary, ESM dynamic imports and conditional logic will make our single-file app smarter:
Only pay the cost (time/memory) for dev tools when actually doing dev.
Keep initial render fast for normal use.
Possibly skip loading the embedded source bundle entirely in normal mode (we could even enclose the JSON in a script that doesn’t parse until we explicitly fetch its textContent).
Exporting the Project (ZIP or Git)
Besides saving the HTML itself, it’s useful to let the user get the project out in other forms:
Export as ZIP: This is straightforward to implement. We have all the source files (and maybe after a dev session, also the node_modules in memory). Using a library like JSZip, we can create a zip archive. For each file in our source bundle (and potentially each dependency file if we choose), add it to the zip. Then generate the blob and trigger a download. This gives the user a standard project folder they can open in a local IDE or upload to a repository. One challenge: if node_modules is huge, zipping it in the browser might be memory intensive. It might be wise to exclude node_modules and only zip the source and package.json, since dependencies can be re-installed. Or make it an option (“Include dependencies in ZIP”). In many cases, just having package.json is enough, developers can run npm install externally.
Push to Git repository: This is more complex but can greatly streamline collaboration. There are two main ways:
Use a Git library in JS: Projects like isomorphic-git allow performing Git operations (commit, push, pull) from the browser using JavaScript. We could integrate isomorphic-git to initialize a repo, add the files, commit changes, and push to a remote. Pushing would require the user to supply credentials (a personal access token, likely). We’d also need CORS access to the git server (for GitHub, using their HTTPS git endpoints, this might work with a token, but CORS could be an issue unless using their APIs).
Use Git provider APIs: For example, GitHub has a REST API to upload files or create commits. We could use that directly (with a token) to update the repository. This might be simpler for an initial implementation: just POST the file contents to the GitHub repo via the API.
The user interface might allow entering a Git remote URL and token, then a button “Push to Git”. Under the hood, we either assemble a commit via isomorphic-git or call the provider’s API.
Limitations & security: Embedding credentials in a single-page app is risky if the file is shared. We should not store tokens in the file permanently. Perhaps just use them transiently (the user enters each time, or we store in localStorage at most). Also, the WebContainer environment itself does not include a Git binary (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW), so we can’t rely on running git push inside it (StackBlitz solved this by doing server-side git operations when launching from GitHub). So everything must be done via client-side JS. Isomorphic-git is a known solution; it supports pushes via Git pack protocol over HTTP.
Alternatively, we might skip direct git integration and simply advise the user to export a ZIP and handle git externally. But given the question explicitly mentions “pushing to a Git repo (optional advanced feature)”, we can outline it as above with references to known solutions.
Implementation Plan Summary
Bringing it all together, here is a high-level implementation outline for building the single-file app:
Bundle Preparation: Develop the application normally as a Node.js project with a src directory, etc. Use a bundler or custom script to produce the final single HTML. Tools like html-bundler-webpack-plugin can inline JS/CSS into HTML (Bundle Vue project into a single html file that can be embedded into ...). We will extend that to also inline our source files data. The build process might:
Generate the production JS/CSS and inline them in the HTML (this becomes the app’s compiled output).
Inject a JSON of source files (perhaps during build, read all src files and embed).
Embed any needed library scripts (optionally as external references or base64).
Ensure the HTML includes any necessary meta (e.g. to allow COOP/COEP if needed for SharedArrayBuffer which WebContainer might require (API Reference | WebContainers)).
Possibly include a minimal service worker script for offline caching (if we want the app to cache itself or other resources when served online).
Environment Detection & UI: In the HTML’s initial script, check the context (file, localhost, https) (environment-detector.html). Update the UI accordingly:
Show a status bar or indicator of the environment (like the example environment detector did with a table of info).
If file:// and likely restrictions, maybe offer tips (e.g., “You are viewing this as a file. Saving changes is only supported in Chrome/Edge.”).
If not secure, perhaps hide the “Edit” button and say “Edit mode requires a secure context (localhost or HTTPS).”
If secure, enable all features.
Main App Initialization: Run the compiled app script (this could be your actual application’s normal behavior). For example, if it’s a UI, render the UI. Ensure this runs independent of the dev tools. The app could be anything (a game, a document, etc.), but should be functional on its own.
Edit/Dev Mode Activation: Provide a button or keyboard shortcut for the user to toggle edit mode. When activated:
Pause or hide the running app if necessary (maybe switch to an editing interface).
Load the code editor (e.g., load Monaco Editor library dynamically) and display a file tree of the project from the embedded source.
Initialize WebContainer (if available): Load the library and boot it (API Reference | WebContainers). Populate the FS with the project files.
If WebContainer isn’t supported, you can still allow the user to edit the source in the editor; they just won’t be able to run it. They could save changes and then reopen the HTML or export, etc. So editing can be enabled even without the Node runtime; just disable the “Run”/“Dev Server” features in that case.
Running and Testing in Dev Mode: If WebContainer started successfully:
Show controls to run scripts. For example, a dropdown of npm scripts from package.json or a simple “Run App” which executes npm start.
Stream the output logs to a console panel in the page so the user sees server logs or build logs.
If the app being developed is a web app, open a preview. This could be done by using an <iframe> pointing to the WebContainer’s preview URL (StackBlitz typically uses something like https://<random>.stackblitz.io). The WebContainer API might provide webcontainer.url for the root, or we use their convention localhost:<port> and the service worker handles it. For example, after running a dev server on port 3000 inside, we could do:
iframe.src = 'http://localhost:3000';
The service worker will catch that and route to container. If that fails, another method is webcontainer.spawn('print-url') if provided. This part might require referencing WebContainer docs or experimentation.
Allow the user to modify files in the editor; on save (in the editor), update both the in-memory source (so it can be saved back to HTML later) and the WebContainer FS (so the dev server sees changes). We might implement a two-way sync: files changed by the app’s running process (unlikely, unless the dev server generates files) should update the editor, and vice versa.
Saving Changes: When the user is done editing:
They click “Save” in the app UI.
If FS Access is available and we have a handle (or can prompt for one), update the HTML file on disk with the new source bundle (and possibly updated compiled code if they rebuilt it).
Otherwise, trigger a download of the new HTML. (We might include the current timestamp or version in the filename to avoid confusion.)
Also provide “Export ZIP” option: generate a ZIP as discussed, containing at least the project source. This is useful if the user wants to pull the project out into a normal dev environment.
Provide “Push to Git” if configured: likely ask for repo URL and token if not provided yet, then commit & push. (For initial simplicity, this could even be done by calling a cloud function, but since we want it entirely in-page, a library method using GitHub API can be attempted.)
Offline and Persistence Considerations: To make the app offline-first:
If served on the web (HTTPS), register a Service Worker that caches the HTML file itself (and perhaps the WebContainer script, etc.) so that it can load offline. Since it’s a single file, offline availability is mostly inherent, but any external dependency must be cached.
For dependencies: After first npm install, we could cache the tarballs or the node_modules content in IndexedDB as mentioned. This is a complex enhancement; the basic version might skip it. However, being “offline-first” implies you should be able to go offline after initial setup and still use dev mode without re-fetching packages. So at least caching the installed modules in memory across dev sessions would help if the browser doesn’t fully flush them.
In file:// usage, offline is by default (everything’s local), so that’s fine, but no service worker. On localhost, the user likely has the file anyway. So offline-first is mainly relevant if this app is deployed somewhere for download or use as a mini-IDE.
Security and Sandboxing: Ensure that running untrusted code is sandboxed. If this app is for the user’s own projects, it’s fine. But if there’s any case where a third-party code is run (like installing a malicious npm package), remember it’s running in WebContainer which is a sandbox within the browser – it can’t access files outside or do anything the browser disallows. Still, be cautious about not exposing the surrounding page’s context to the container code (e.g., don’t directly eval untrusted code in the main window). WebContainer isolates the Node process, which is good.
Relevant Tools and Libraries
To recap, here are key tools, APIs, and libraries to leverage:
WebContainers (StackBlitz) – The primary enabler for in-browser Node.js and npm. Provides Node runtime, FS, and networking in browser (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW). We will use its public API (available via npm @webcontainer/api or from their CDN) (API Reference | WebContainers). This handles running dev servers, installing packages, etc.
File System Access API – Allows reading/writing local files with permission. We’ll use showOpenFilePicker() and file handles to save changes directly (self-modifying-page (1).html) (self-modifying-page (1).html). Keep in mind it’s Chromium-only (as of 2025) and requires secure context or browser-specific flags. We’ll implement a fallback for others.
IndexedDB – For storing persistent data in the browser. We can store a file handle for reuse (self-modifying-page (1).html), store cached module files, or even store the entire project as backup. IndexedDB gives us a way to keep data between sessions without re-embedding into the HTML until explicitly saved.
Service Workers – Use in two ways: (1) The WebContainer’s internal mechanism uses a service worker to intercept traffic to Node processes (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW). This we don’t manage but must not conflict with. (2) Our own service worker for offline caching of assets. Workbox or manual SW code could cache the WebContainer script, editor library, etc., so that once the HTML is opened, it doesn’t require external files to be fetched.
Monaco Editor or CodeMirror – A code editor component to allow editing multiple files with syntax highlighting, etc. Monaco (the editor in VSCode) can run in the browser but is quite large; CodeMirror is lighter. Depending on size constraints, we might pick CodeMirror for embedding. Either way, load it on-demand.
JSZip – For generating ZIP archives in-browser (if implementing export to ZIP).
isomorphic-git – For Git operations in-browser (if implementing the push-to-git feature). This library can do commits and pushes using the Git protocol over HTTP. Alternatively, use GitHub’s REST API for commits as a simpler but less generic solution.
HTML Bundler / Inline asset tools – To produce the single HTML, tools like html-bundler-webpack-plugin can inline JS, CSS, images into one file (Bundle Vue project into a single html file that can be embedded into ...). We may integrate our build with such a tool. Another approach is a custom script that reads the compiled JS/CSS and base64-encodes or embeds them. Ensuring everything is in one file (even large WASM binaries) may push the limits of some bundlers, but it’s achievable.
Observable runtime (optional) – Since LopeCode uses the ObservableHQ runtime, one could consider using that for reactive programming features. However, it’s likely out of scope for a general app. Still, if interactive notebook style editing is desired, the Observable open-source runtime could be embedded. (This is more relevant if we wanted reactive cells rather than a manual editor + run.)
Potential Challenges and Limitations
While the plan is comprehensive, it’s important to note limitations:
File size and performance: Bundling a full app with source and possibly some binary (WASM) components can make the HTML file very large (several MB easily, possibly tens of MB if many deps are embedded). This affects load time and memory. The user’s browser will parse the entire HTML on open. We mitigate this by splitting the content and not executing heavy parts until needed, but the bytes are still there. In practice, HTML files up to a few MB are fine, but if you try to embed something like an entire Node.js node_modules, the size becomes untenable. So we won’t embed large dependency files; we rely on dynamic install and caching for those.
Browser compatibility: Not all browsers support all features. We’ve addressed FS Access and WebContainer support variations. Also, the app likely needs a fairly modern browser (ES2020+ features, etc.) given ESM modules, WASM, etc. We should test on latest Chrome, Firefox, Edge. Safari might be problematic for dev mode due to lack of SharedArrayBuffer support (as of early 2025, Safari still lags on some WASM threading features).
Security: The concept of a self-editing app blurs the line between code and data. We need to ensure that when the app writes to itself, it doesn’t inadvertently introduce security issues (like XSS or script injection beyond what’s intended). Essentially, the user is intentionally injecting scripts (their own code) as part of editing, so that’s fine. But if this HTML fell into the wrong hands, someone could inject malicious code into it and if a user runs it and saves, it persists. This is similar to running untrusted code in any dev environment – caution is needed if sharing files. It might be out of scope to fully address, but worth noting that this tool is mainly for personal use or trusted code scenarios, not running random unvetted projects from the internet.
State synchronization: Keeping the source-of-truth consistent between the running app, the embedded source JSON, and the WebContainer FS can be tricky. We have to carefully update each when changes occur. A bug there could mean the HTML saved doesn’t actually include the latest edits, etc. Thorough testing is required.
Git integration complexity: Pushing to git from the browser might hit CORS issues or require dealing with OAuth flows for GitHub. It’s an advanced feature and might be better served by instructing users to use their normal tools after exporting. However, tools like StackBlitz Web Publisher show it’s possible to do a smooth in-browser commit/PR experience (Ask HN: Visual interfaces for editing markdown-based static sites? | Hacker News), likely via GitHub’s API or a proxy. We might consider leveraging existing services (maybe an integration with GitHub pages or gists) as an alternative to building git push ourselves.
In-app build vs external build: If the Node project requires a build (e.g., a TypeScript compile or webpack bundle), we have two ways to get the compiled output into the HTML: either do the build externally as part of creating the single HTML (probably easier for initial deployment), or allow building inside the WebContainer for live development. We assumed the compiled output is pre-bundled in the HTML (so the app can run). That means the dev mode should re-bundle if code changes and we want to see the effect in the standalone app. Possibly, after editing, we could run a build tool in WebContainer (like npm run build) to produce new output files, then replace the #appCompiled script content in the HTML string with the new bundle, before saving the HTML. This would effectively update the distribution to match the new source. That is a powerful feature: edit, build, and save new distributable – all in one app. It involves capturing the build output (maybe an output JS file) from the WebContainer FS and injecting it in our document prior to save. This is feasible if the build output isn’t enormous. It’s exactly what our structure (compiled at top, source at bottom) was meant to facilitate.
In conclusion, this single-file app will combine techniques from single-page apps, IDEs, and offline web. By using proven components (like TiddlyWiki’s saving approach (File System Access API and saving locally - Talk @ TiddlyWiki), Observable’s self-contained runtime (Future of Coding Weekly (old posts, page 12) | Future of Coding Weekly), and WebContainer’s Node-in-WASM (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW)) we can create a self-editing, offline-capable development playground in one file. While no off-the-shelf tool provides all this out of the box, our plan stitches together multiple tools and best practices:
Use inlined resources to make the app truly one file (HTML, CSS, JS all embedded).
Implement environment-specific handling to cope with browser security models (environment-detector.html).
Leverage File System Access API for seamless saving on desktop (File System Access API and saving locally - Talk @ TiddlyWiki).
Integrate WebContainer for a Node.js dev environment in-browser, enabling npm install and project execution (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW).
Provide export options (ZIP, Git) to get code out, as seen in StackBlitz and similar systems (StackBlitz’s publisher can even commit to GitHub repos directly (Ask HN: Visual interfaces for editing markdown-based static sites? | Hacker News)).
Embed the project’s source in a modifiable form so the app can modify itself, reminiscent of how TiddlyWiki stores its data in <div>s inside itself (Data Storage in Single File TiddlyWiki: TiddlyWiki/Dev — documentation for developers).
By following this plan, we ensure the app is offline-first and file-first (it can live as a file on your disk, no servers required), yet can elevate to a full “cloud-like” dev experience when needed. It’s a fusion of a document and an IDE – a concept just starting to be explored in projects like LopeCode.
References & Examples:
TiddlyWiki’s method of self-containment and new saving mechanism (Data Storage in Single File TiddlyWiki: TiddlyWiki/Dev — documentation for developers) (File System Access API and saving locally - Talk @ TiddlyWiki)
Tom Larkworthy’s LopeCode (Moldable Webfile) demonstrating a self-sustainable programming environment (Future of Coding Weekly (old posts, page 12) | Future of Coding Weekly)
StackBlitz WebContainer introduction, explaining Node.js via WASM in the browser (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW) (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW)
Discussion of WebContainer limitations (no built-in git, etc.) (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW) and usage in running TiddlyWiki Node edition (More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW)
Hacker News comment on StackBlitz Web Publisher for in-browser Docusaurus (showing real-world use of in-browser dev with git) (Ask HN: Visual interfaces for editing markdown-based static sites? | Hacker News)
Environment detection example highlighting file vs localhost vs https differences (environment-detector.html)
Code snippets from the provided sample files for FS Access usage (self-modifying-page (1).html) (self-modifying-page (1).html) and dynamic script loading tests (environment-detector.html) (environment-detector.html).
With these building blocks and careful engineering, we can create a powerful single-file app that not only runs a packaged application but also serves as its own development and editing platform – truly a “lab on a page” for Node.js projects.
Citations
tiddlywiki.com
Data Storage in Single File TiddlyWiki: TiddlyWiki/Dev — documentation for developers
The single file version of TiddlyWiki saves the tiddlers within the HTML file.
tiddlywiki.com
Data Storage in Single File TiddlyWiki: TiddlyWiki/Dev — documentation for developers
By default, all tiddlers are now stored as an array of JSON objects inside a `<script>` tag with the `class` attribute "tiddlywiki-tiddler-store" and the `type` "application/json". For better readability, every tiddler object begins on a new line. There are no longer any restrictions on characters that are allowed in tiddler field names. However, `<script>` tag store areas must encode the `<` character to `\u003c`.
talk.tiddlywiki.org
File System Access API and saving locally - Talk @ TiddlyWiki
TiddlyWiki talk.tiddlywiki.org The new file system api may allow us to once again save single file wikis without any external requirements, at least in Chrome and Edge.
newsletter.futureofcoding.org
Future of Coding Weekly (old posts, page 12) | Future of Coding Weekly
tomlarkworthy.github.io/lopecode/webpage.html
talk.tiddlywiki.org
More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW
Stackblitz webcontainers use wasm to run nodejs directly inside the browser.
talk.tiddlywiki.org
More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW
Node.js is a program which can run javascript outside of the browser. Node.js is not written in javascript and the stackblitz folks have compiled it to wasm so it can run in the browser. Dealing with node.js projects also means needing to install packages (usually with npm). Stackblitz also makes an npm client program available in the browser by compiling it to wasm.
talk.tiddlywiki.org
More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW
To be useful for, the node.js program typically needs to be able to operate as a web server. Modern browsers have the service worker feature which allows network requests to be intercepted. It looks to me like requests from the browser to the tiddlywiki server running in node.js are intercepted by the service worker and fed directly to the wasm node.js. The response from node.js is returned by the service worker to the browser page and it looks just like a real server provided the response.
news.ycombinator.com
Ask HN: Visual interfaces for editing markdown-based static sites? | Hacker News
You can try a git based CMS like Tina, they have a Docusaurus starter/example.
file-jihymbjgxakxg5ynbwyhif
environment-detector.html
try { // Create script element to load lodash const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js'; script.onload = function() { // Test if lodash loaded successfully if (typeof _ !== 'undefined') { const testArray = [1, 2, 3, 4, 5];
file-jihymbjgxakxg5ynbwyhif
environment-detector.html
} else { resultDiv.innerHTML = '<span class="error">Script loaded but library not available</span>'; } };
file-jihymbjgxakxg5ynbwyhif
environment-detector.html
if (isFileProtocol) { html += '<li><strong>Loading context:</strong> <span class="error">File:// protocol</span> - Various security restrictions apply</li>'; } else if (isLocalhost) { html += '<li><strong>Loading context:</strong> <span class="success">Localhost</span> - Some restrictions relaxed</li>'; } else if (isHTTPS) { html += '<li><strong>Loading context:</strong> <span
file-jihymbjgxakxg5ynbwyhif
environment-detector.html
const isFileProtocol = window.location.protocol === 'file:'; const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'; const isHTTPS = window.location.protocol === 'https:';
file-jihymbjgxakxg5ynbwyhif
environment-detector.html
} else if (isLocalhost) { html += '<li><strong>Loading context:</strong> <span class="success">Localhost</span> - Some restrictions relaxed</li>'; } else if (isHTTPS) { html += '<li><strong>Loading context:</strong> <span class="success">HTTPS domain</span> - Full features available</li>'; } else {
file-lavvswpvlagrxvykdbpwvn
self-modifying-page (1).html
// IndexedDB functions to store and retrieve file handle const dbPromise = (() => { return new Promise((resolve, reject) => { const request = indexedDB.open('SelfModifyingFileDB', 1);
file-lavvswpvlagrxvykdbpwvn
self-modifying-page (1).html
// Save file handle to IndexedDB async function saveFileHandle(handle) { try { const db = await dbPromise; const transaction = db.transaction('handles', 'readwrite'); const store = transaction.objectStore('handles');
webcontainers.io
API Reference | WebContainers
▸ `boot`
webcontainers.io
API Reference | WebContainers
▸ `boot`
talk.tiddlywiki.org
More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW
Npm also requires network access in order to fetch the packages. Code running in wasm is permitted to fetch urls just like any other javascript. From looking at the network requests in developer tools, it looks like the packages are fetched through a stackblitz url (maybe not from npm directly due to CORS issues?).
github.com
Open-sourcing for the webplatform ? · Issue #458 - GitHub
Open-sourcing for the webplatform ? · Issue #458 - GitHub WebVM is a server-less virtual Linux environment running fully client-side in HTML5/WebAssembly. WebVM is powered by the CheerpX virtualization engine.
blog.stackblitz.com
Bringing Sharp to WebAssembly and WebContainers - StackBlitz Blog
Bringing Sharp to WebAssembly and WebContainers - StackBlitz Blog You can now use Sharp - a high-performance image processing library for Node.js, in WebContainers! Try it live on StackBlitz!
talk.tiddlywiki.org
More on using stackblitz webcontainers to run node.js tiddlywiki in the browser - nodejs - Talk TW
Stackblitz also has the feature of being able to launch any nodejs project directly from github. Their webcontainer does not have a git client, so somehow there must be something on their server side which aids in cloning the git repositories into the browser. I’m not sure how this part works, but it is very flexible and even allows you to launch from a specific branch in the respository.
stackoverflow.com
Bundle Vue project into a single html file that can be embedded into ...
... stackoverflow.com To generate a single HTML file with embedded JS, CSS, images you can use the HTML Bundler Plugin for Webpack. This plugin can inline assets into HTML.
webcontainers.io
API Reference | WebContainers
# `BootOptions`#
webcontainers.io
API Reference | WebContainers
The public WebContainer API allows you to build custom applications on top of an in-browser Node.js runtime. Its main entry point is the `WebContainer` class.
file-lavvswpvlagrxvykdbpwvn
self-modifying-page (1).html
<script> // Store our file handle let fileHandle = null; let fileContent = null;
file-lavvswpvlagrxvykdbpwvn
self-modifying-page (1).html
async function saveFileHandle(handle) { try { const db = await dbPromise; const transaction = db.transaction('handles', 'readwrite'); const store = transaction.objectStore('handles');
All Sources
self-mod... (1).html
environm...ctor.html
observablehq
x
bsky
tomlarkworthy.github
2
talk.tiddlywiki
newslett...eofcoding
2
webcontainers
3
reddit
stackblitz
developer.chrome
news.ycombinator
tiddlywiki