Files

4.1 KiB

Archipelago app starter

This is a small standalone Vue/Vite app that demonstrates Archipelago's initial design contract: semantic --archy-* tokens, dark native controls, responsive cards, bottom actions, visible focus, 44px targets, and loading/empty/error/ success states. It intentionally has no dashboard dependency or host handshake.

Generate a separate source project from the repository root:

python3 scripts/create-app-starter.py my-notes /existing/parent/my-notes

The parent must exist and the output directory must not. Generation never overwrites existing work, installs dependencies, builds or activates an app. Use a clean template: symlinks, Git/build/cache state and an old GENERATION.json receipt are refused before output creation. The receipt binds each input and generated file hash; it is source provenance, not review or runtime acceptance. Keep source and output directories under your control during generation. The manifest ID stays quoted so YAML keywords such as null and off remain the exact requested string. Generator regression tests require Python's PyYAML package, also used by the contributor manifest preflight; generation itself uses only Python's standard library.

The example saves items in this browser's local storage, not node storage or a server API. Reload retains them; unreadable, invalid or full storage reports an error without resetting existing data. This is not a multi-tab transaction store or a wallet/secret store. npm test runs dependency-free storage regressions. Append refusals (capacity, serialized-size limit or storage quota) leave readable items visible and report the refusal separately. The size limit counts serialized JavaScript string characters, not filesystem bytes or a browser quota guarantee.

From the repository root, source-only component checks can use an existing, trusted frontend dependency directory without installing or building anything:

node --max-old-space-size=192 scripts/test-app-starter-component.cjs /path/to/neode-ui/node_modules

This compiles the actual Vue component and exercises it in jsdom. It requires Vue/compiler-sfc, TypeScript and jsdom already available in that directory. It does not establish browser/layout, TypeScript typechecking, packaged runtime, embedding or managed-app acceptance; qualify those separately before shipping.

When dependency installation is separately authorized, use npm ci --ignore-scripts from the generated directory, then npm run dev. The checked-in package and lock use matching exact versions; do not replace the lock with a mutable install. Generation requires the package/lock, Dockerfile, nginx config, manifest, Vite config, HTML entry and starter source files. Missing inputs or mismatched package/lock identity, version or direct dependencies are refused before output. The generator validates captured input bytes and preserves all lock resolution and integrity entries while substituting the app ID in package and lock names. This source check does not validate the complete transitive dependency graph or replace npm ci and subsequent artifact qualification.

The Dockerfile uses digest-pinned Node/nginx bases, a locked npm ci build and nginx directly as UID/GID 101:101, without upstream configuration-mutating startup scripts. Its config serves port 8080; the gated loopback manifest interface is 8180, with an HTTP health probe on 8080. The read-only root has only a bounded 16 MiB /tmp tmpfs for nginx scratch; there is no /data bind or node-persistence claim. Browser-local items still belong to this browser/origin, not the container.

These are packaging source inputs, not a qualified reproducible image. Before shipping, independently review the exact source/output hashes, qualify the locked build and actual image's own entrypoint/config/assets under appropriate admission, and validate the manifest using the repository app developer guide. Real-browser, design/accessibility, embedded/Companion and managed lifecycle checks remain separate. If your app requires server persistence, supply and qualify its API and data-preserving lifecycle separately; this browser-local example does not do so.