web: the apps directory holds apps, and nothing else (#1008)
Nine of the .js files under web/static/apps/ never called defineApp. They were shared machinery that drifted in next to the apps that use it, which made the directory a poor index of what an app actually IS — you couldn't list it and see the apps. They move to web/static/, alongside the other shared modules (crypto.js, format.js, ui.js, lightbox.js, media.js), imported from an app as ../<name>.js: eleven.js Eleven's scripted-conversation feel (setup, welcome) flow.js the conversation-flow engine setup.flow.js the setup conversation as data welcome.flow.js the welcome conversation as data quickform.js the quick-form planner (shell.js + onboardingui.js use it) schema-engine.js the app-schema interpreter schema-render.js its DOM renderer tutorial.js /tutorial — a floor command, not an app vaultcrypto.js the Vault lock layer's crypto Pure moves: no behaviour changes, only import paths. Apps are imported explicitly (shell.js lists them; nothing globs the directory), so this is a mechanical path rewrite — the existing tests import six of the nine and would fail loudly on a stale path. tutorial.js is the one judgement call: it registers a floor command rather than defining an app, so by the rule it moves. A new test (test/apps-dir.test.mjs) enforces the contract, matching how the flow documents are kept JSON-pure. It matches a defineApp *call*, not a mention — schema-render.js describes the framework in prose and would have slipped past a substring check.
Co-authored-by: Claude <noreply@anthropic.com>
31 files changed,
+76
−39
docs/app-schema.md+1 −1docs/conversation-scripts.md+1 −1docs/conversational-onboarding.md+1 −1ios/LChatCore/Sources/LChatCore/FlowEngine.swift+2 −2ios/LChatCore/Sources/LChatCore/SchemaEngine.swift+1 −1package.json+1 −1test/apps-dir.test.mjs+30 −0test/flow.test.mjs+4 −4test/quickform.test.mjs+3 −3test/schema-engine.test.mjs+2 −2test/tutorial.test.mjs+1 −1test/vaultcrypto.test.mjs+2 −2test/views.test.mjs+1 −1web/static/apps/README.md+8 −1web/static/apps/remind.js+1 −1web/static/apps/setup.js+4 −4web/static/apps/todo.js+1 −1web/static/apps/vault.js+1 −1web/static/apps/views.js+1 −1web/static/apps/welcome.js+4 −4web/static/{apps => }/eleven.js+1 −1web/static/{apps => }/flow.js+0 −0web/static/onboardingui.js+1 −1web/static/{apps => }/quickform.js+0 −0web/static/{apps => }/schema-engine.js+0 −0web/static/{apps => }/schema-render.js+0 −0web/static/{apps => }/setup.flow.js+0 −0web/static/shell.js+2 −2web/static/{apps => }/tutorial.js+1 −1web/static/{apps => }/vaultcrypto.js+1 −1web/static/{apps => }/welcome.flow.js+0 −0
diff --git a/docs/app-schema.md b/docs/app-schema.md| index fbd06b9..8293128 100644 |
| --- a/docs/app-schema.md |
| +++ b/docs/app-schema.md |
| @@ -6,7 +6,7 @@ A mini-app's behaviour has always been specified twice: the JS app file |
| large class of apps: a single JSON file that *formally describes how the app |
| looks and behaves*, interpreted by both runtimes: |
| -- **JS engine + DOM renderer:** `web/static/apps/schema-engine.js` and |
| +- **JS engine + DOM renderer:** `web/static/schema-engine.js` and |
| `schema-render.js` (the app file wires `schemaEvents`/`schemaRender` into |
| defineApp and keeps only the glue a schema doesn't cover: commands, sidebar |
| preview/unread, chat wording, named parsers) |
diff --git a/docs/conversation-scripts.md b/docs/conversation-scripts.md| index 9fd0887..709770b 100644 |
| --- a/docs/conversation-scripts.md |
| +++ b/docs/conversation-scripts.md |
| @@ -2,7 +2,7 @@ |
| Status: **phase 1 complete — schema v1 is frozen against reality.** Both |
| shipped conversations are pure data (`setup.flow.js`, `welcome.flow.js`) |
| -interpreted by the engine (`web/static/apps/flow.js`); `test/flow.test.mjs` |
| +interpreted by the engine (`web/static/flow.js`); `test/flow.test.mjs` |
| drives full transcripts of both in Node with a fake io and enforces JSON |
| purity, so the flow files are shippable fixtures for the Swift interpreter. |
| Next: phase 2, serving the scripts from the binary. Design captured |
diff --git a/docs/conversational-onboarding.md b/docs/conversational-onboarding.md| index a6b4717..b17dd2f 100644 |
| --- a/docs/conversational-onboarding.md |
| +++ b/docs/conversational-onboarding.md |
| @@ -5,7 +5,7 @@ app v1 — landed with this doc); later phases still proposed. Captured from |
| design discussion 2026-07-06. |
| Builds on `onboarding-and-home-server.md` (welcome page, home-server vault, |
| fragility banner — phases 1–3a shipped) and the `/tutorial` mini-app |
| -(`web/static/apps/tutorial.js`), whose client-driven, in-Eleven's-voice pattern |
| +(`web/static/tutorial.js`), whose client-driven, in-Eleven's-voice pattern |
| this design generalises. |
| > **Follow-on: `conversation-scripts.md`.** With iOS/Mac moving to fully |
diff --git a/ios/LChatCore/Sources/LChatCore/FlowEngine.swift b/ios/LChatCore/Sources/LChatCore/FlowEngine.swift| index c1dde4d..abb0fc5 100644 |
| --- a/ios/LChatCore/Sources/LChatCore/FlowEngine.swift |
| +++ b/ios/LChatCore/Sources/LChatCore/FlowEngine.swift |
| @@ -1,6 +1,6 @@ |
| import Foundation |
| -// 🤖 The Swift twin of web/static/apps/flow.js: the interpreter for the |
| +// 🤖 The Swift twin of web/static/flow.js: the interpreter for the |
| // conversation scripts (docs/conversation-scripts.md) — the JSON documents |
| // that ARE Eleven's onboarding conversations (setup.flow.js, welcome.flow.js). |
| // The docs promised "the document the server will ship to native |
| @@ -145,7 +145,7 @@ public final class FlowEnv { |
| // original — never clean a name to nothing. Prefix lists are matched on a |
| // lowercased, straight-apostrophe copy (iOS keyboards type ’), longest |
| // preamble first so "i'm called" wins over "i'm". |
| -// Twin: cleanNameAnswer in web/static/apps/flow.js. |
| +// Twin: cleanNameAnswer in web/static/flow.js. |
| private let nameGreetings = ["hiya", "howdy", "hello", "hey", "hi"] |
| private let namePreambles = [ |
| "you can call me", "everyone calls me", "people call me", "they call me", |
diff --git a/ios/LChatCore/Sources/LChatCore/SchemaEngine.swift b/ios/LChatCore/Sources/LChatCore/SchemaEngine.swift| index ea9c23b..05d658d 100644 |
| --- a/ios/LChatCore/Sources/LChatCore/SchemaEngine.swift |
| +++ b/ios/LChatCore/Sources/LChatCore/SchemaEngine.swift |
| @@ -1,6 +1,6 @@ |
| import Foundation |
| -// 🤖 The Swift twin of web/static/apps/schema-engine.js: the interpreter for |
| +// 🤖 The Swift twin of web/static/schema-engine.js: the interpreter for |
| // the declarative app schema (docs/app-schema.md) that renders To-do and |
| // Reminders (and any schema-described app) with native components. The two |
| // engines MUST behave identically — they are drift-gated by the shared event |
diff --git a/package.json b/package.json| index 4394afe..2f712c0 100644 |
| --- a/package.json |
| +++ b/package.json |
| @@ -3,6 +3,6 @@ |
| "private": true, |
| "type": "module", |
| "scripts": { |
| - "test": "node --test test/module-refs.test.mjs && node test/crypto.test.mjs && node test/vaultcrypto.test.mjs && node test/integrity.test.mjs && node test/ui.test.mjs && node test/blurhash.test.mjs && node test/format.test.mjs && node test/markdown.test.mjs && node test/composer.test.mjs && node test/composer-vectors.test.mjs && node test/bootscene.test.mjs && node test/message.test.mjs && node test/threadkey.test.mjs && node test/nicknames.test.mjs && node test/messageblob.test.mjs && node test/blobbytes.test.mjs && node test/nativeblob.test.mjs && node test/apps.test.mjs && node test/cards.test.mjs && node test/event-vectors.test.mjs && node test/views.test.mjs && node test/schema-engine.test.mjs && node test/flow.test.mjs && node test/quickform.test.mjs && node test/tutorial.test.mjs && node test/actions.test.mjs && node test/commands.test.mjs && node test/context.test.mjs && node test/knowledge.test.mjs && node test/invite.test.mjs && node test/backup.test.mjs && node test/connect.test.mjs && node test/serviceop.test.mjs && node test/github.test.mjs && node test/github-render.test.mjs && node test/linear.test.mjs && node test/linear-render.test.mjs && node test/check-app.test.mjs && node test/sandbox.test.mjs && node test/sandbox-bridge.test.mjs && node test/status-contract.test.mjs && node test/store.test.mjs && node test/memory.test.mjs && node test/search.test.mjs && node home/test/crypto.test.mjs && node home/test/restore-payload.test.mjs && node home/test/serverlist.test.mjs && node home/test/ceremony-errors.test.mjs && node home/test/fader.test.mjs" |
| + "test": "node --test test/module-refs.test.mjs && node test/apps-dir.test.mjs && node test/crypto.test.mjs && node test/vaultcrypto.test.mjs && node test/integrity.test.mjs && node test/ui.test.mjs && node test/blurhash.test.mjs && node test/format.test.mjs && node test/markdown.test.mjs && node test/composer.test.mjs && node test/composer-vectors.test.mjs && node test/bootscene.test.mjs && node test/message.test.mjs && node test/threadkey.test.mjs && node test/nicknames.test.mjs && node test/messageblob.test.mjs && node test/blobbytes.test.mjs && node test/nativeblob.test.mjs && node test/apps.test.mjs && node test/cards.test.mjs && node test/event-vectors.test.mjs && node test/views.test.mjs && node test/schema-engine.test.mjs && node test/flow.test.mjs && node test/quickform.test.mjs && node test/tutorial.test.mjs && node test/actions.test.mjs && node test/commands.test.mjs && node test/context.test.mjs && node test/knowledge.test.mjs && node test/invite.test.mjs && node test/backup.test.mjs && node test/connect.test.mjs && node test/serviceop.test.mjs && node test/github.test.mjs && node test/github-render.test.mjs && node test/linear.test.mjs && node test/linear-render.test.mjs && node test/check-app.test.mjs && node test/sandbox.test.mjs && node test/sandbox-bridge.test.mjs && node test/status-contract.test.mjs && node test/store.test.mjs && node test/memory.test.mjs && node test/search.test.mjs && node home/test/crypto.test.mjs && node home/test/restore-payload.test.mjs && node home/test/serverlist.test.mjs && node home/test/ceremony-errors.test.mjs && node home/test/fader.test.mjs" |
| } |
| } |
diff --git a/test/apps-dir.test.mjs b/test/apps-dir.test.mjs| new file mode 100644 |
| index 0000000..f708e6f |
| --- /dev/null |
| +++ b/test/apps-dir.test.mjs |
| @@ -0,0 +1,30 @@ |
| +// The apps-directory contract: web/static/apps/ holds apps and nothing else. |
| +// Every .js file there calls defineApp — one app per file — so listing the |
| +// directory lists the apps. Shared machinery an app leans on (the schema |
| +// engine + renderer, the flow engine, Eleven's scripted-conversation feel, the |
| +// Vault's lock crypto) is a sibling module in web/static/, imported as |
| +// ../<name>.js. Helpers drifted in here once and made the directory a poor |
| +// index of what an app IS; this test keeps it honest. |
| +// node test/apps-dir.test.mjs |
| +import assert from "node:assert/strict"; |
| +import { readFileSync, readdirSync } from "node:fs"; |
| + |
| +const dir = new URL("../web/static/apps/", import.meta.url); |
| +const files = readdirSync(dir).filter((f) => f.endsWith(".js")).sort(); |
| + |
| +// A defineApp CALL, not a mention: schema-render.js documented the framework in |
| +// prose and would have passed a bare substring match. |
| +const CALLS_DEFINE_APP = /\bdefineApp\s*\(/; |
| + |
| +const notApps = files.filter((f) => !CALLS_DEFINE_APP.test(readFileSync(new URL(f, dir), "utf8"))); |
| +assert.deepStrictEqual( |
| + notApps, |
| + [], |
| + `web/static/apps/ must contain only apps, but these call no defineApp: ${notApps.join(", ")}.\n` + |
| + `Move shared machinery to web/static/ and import it as ../<name>.js.`, |
| +); |
| + |
| +// Guard the guard: a directory that somehow scanned empty would pass vacuously. |
| +assert.ok(files.length > 10, `expected the apps directory to hold apps, found ${files.length} .js files`); |
| + |
| +console.log(`apps-dir tests passed (${files.length} apps, all calling defineApp)`); |
diff --git a/test/flow.test.mjs b/test/flow.test.mjs| index 326b6d9..14e3773 100644 |
| --- a/test/flow.test.mjs |
| +++ b/test/flow.test.mjs |
| @@ -5,9 +5,9 @@ |
| // fixture discipline the Swift interpreter will inherit. |
| import assert from "node:assert/strict"; |
| import { test } from "node:test"; |
| -import { runFlow, renderText, evalCond, cleanNameAnswer } from "../web/static/apps/flow.js"; |
| -import SETUP_FLOW from "../web/static/apps/setup.flow.js"; |
| -import WELCOME_FLOW from "../web/static/apps/welcome.flow.js"; |
| +import { runFlow, renderText, evalCond, cleanNameAnswer } from "../web/static/flow.js"; |
| +import SETUP_FLOW from "../web/static/setup.flow.js"; |
| +import WELCOME_FLOW from "../web/static/welcome.flow.js"; |
| const tick = () => new Promise((r) => setImmediate(r)); |
| @@ -664,7 +664,7 @@ await test("setup: enrolment confirmed while on keys-waiting settles through", a |
| // ---- native bundle drift gate ------------------------------------------------ |
| // The Swift interpreter (ios/.../FlowEngine.swift) runs byte-derived copies of |
| // these flow documents (Flows/*.json). Regenerate after editing a flow: |
| -// node -e 'import("./web/static/apps/setup.flow.js").then(async m => { (await import("fs")).writeFileSync("ios/LChatCore/Sources/LChatCore/Flows/setup.json", JSON.stringify(m.default, null, 2) + "\n"); })' |
| +// node -e 'import("./web/static/setup.flow.js").then(async m => { (await import("fs")).writeFileSync("ios/LChatCore/Sources/LChatCore/Flows/setup.json", JSON.stringify(m.default, null, 2) + "\n"); })' |
| // (and the welcome twin). |
| import { readFileSync } from "fs"; |
| import { fileURLToPath } from "url"; |
diff --git a/test/quickform.test.mjs b/test/quickform.test.mjs| index a0fdd49..e0eee0d 100644 |
| --- a/test/quickform.test.mjs |
| +++ b/test/quickform.test.mjs |
| @@ -4,9 +4,9 @@ |
| // the planner) must change with it, or the two paths' transcripts diverge. |
| import assert from "node:assert/strict"; |
| import { test } from "node:test"; |
| -import { planQuickForm } from "../web/static/apps/quickform.js"; |
| -import SETUP_FLOW from "../web/static/apps/setup.flow.js"; |
| -import WELCOME_FLOW from "../web/static/apps/welcome.flow.js"; |
| +import { planQuickForm } from "../web/static/quickform.js"; |
| +import SETUP_FLOW from "../web/static/setup.flow.js"; |
| +import WELCOME_FLOW from "../web/static/welcome.flow.js"; |
| await test("welcome plan: cleaned name + the photo skip record", () => { |
| // The same clean-before-cap rule as the typed answer ("I'm Dermot" → Dermot). |
diff --git a/test/schema-engine.test.mjs b/test/schema-engine.test.mjs| index 360ff01..bcc7641 100644 |
| --- a/test/schema-engine.test.mjs |
| +++ b/test/schema-engine.test.mjs |
| @@ -1,4 +1,4 @@ |
| -// 🤖 Tests for the app-schema engine (web/static/apps/schema-engine.js) and the |
| +// 🤖 Tests for the app-schema engine (web/static/schema-engine.js) and the |
| // todo/remind schema files. Three layers of proof: |
| // 1. The schema fold reproduces the shared event vectors (the cross-platform |
| // spec native also tests against). |
| @@ -25,7 +25,7 @@ import { |
| tickerRules, |
| evalTemplate, |
| relTime, |
| -} from "../web/static/apps/schema-engine.js"; |
| +} from "../web/static/schema-engine.js"; |
| import { todoVectors, remindVectors } from "./event-vectors/index.js"; |
| import { app as todoApp } from "../web/static/apps/todo.js"; |
| import { app as remindApp, parseReminder } from "../web/static/apps/remind.js"; |
diff --git a/test/tutorial.test.mjs b/test/tutorial.test.mjs| index 77746fb..8fc3a95 100644 |
| --- a/test/tutorial.test.mjs |
| +++ b/test/tutorial.test.mjs |
| @@ -1,6 +1,6 @@ |
| // Tests for Eleven's guided tour (client-side flow). node test/tutorial.test.mjs |
| import assert from "node:assert/strict"; |
| -import { startTour } from "../web/static/apps/tutorial.js"; |
| +import { startTour } from "../web/static/tutorial.js"; |
| import { runCommand, commandCatalog, setDefaultBot } from "../web/static/apps.js"; |
| let failures = 0; |
diff --git a/test/vaultcrypto.test.mjs b/test/vaultcrypto.test.mjs| index 326f346..910b91f 100644 |
| --- a/test/vaultcrypto.test.mjs |
| +++ b/test/vaultcrypto.test.mjs |
| @@ -1,4 +1,4 @@ |
| -// Tests for the vault lock layer's crypto (web/static/apps/vaultcrypto.js), run |
| +// Tests for the vault lock layer's crypto (web/static/vaultcrypto.js), run |
| // in Node (Node 22 has Web Crypto). node test/vaultcrypto.test.mjs |
| // |
| // The WebAuthn ceremonies need an authenticator, so — like home's crypto tests — |
| @@ -22,7 +22,7 @@ import { |
| acceptGrant, |
| wrapVaultPrivRecovery, |
| unwrapVaultPrivRecovery, |
| -} from "../web/static/apps/vaultcrypto.js"; |
| +} from "../web/static/vaultcrypto.js"; |
| let failures = 0; |
| async function test(name, fn) { |
diff --git a/test/views.test.mjs b/test/views.test.mjs| index 1d80d57..d2d2901 100644 |
| --- a/test/views.test.mjs |
| +++ b/test/views.test.mjs |
| @@ -4,7 +4,7 @@ |
| // limits) and proves the expanded schema drives the shared engine. |
| import assert from "assert"; |
| import { viewsApp, viewSchema } from "../web/static/apps/views.js"; |
| -import { listModel, rowModel, initialState, foldSchemaEvent } from "../web/static/apps/schema-engine.js"; |
| +import { listModel, rowModel, initialState, foldSchemaEvent } from "../web/static/schema-engine.js"; |
| let passes = 0; |
| let fails = 0; |
diff --git a/web/static/apps/README.md b/web/static/apps/README.md| index 8120120..ae08dd7 100644 |
| --- a/web/static/apps/README.md |
| +++ b/web/static/apps/README.md |
| @@ -4,6 +4,13 @@ A **mini-app** is a small client-side program that lives alongside chats — a |
| to-do list, a poll, reminders. This guide is written for both humans and LLMs: |
| follow it top to bottom and you can add a new app in one self-contained file. |
| +> **This directory holds apps and nothing else.** Every `.js` file in |
| +> `web/static/apps/` calls `defineApp` — one app per file. Shared machinery an |
| +> app leans on (the schema engine + renderer, the conversation-flow engine, |
| +> Eleven's scripted-conversation feel, the Vault's lock crypto) is a sibling |
| +> module in `web/static/`, imported as `../<name>.js`. So listing this |
| +> directory lists the apps. |
| + |
| ## The one idea |
| An app has no server. It trades **typed events** over the same end-to-end |
| @@ -18,7 +25,7 @@ or even recognise app logic — everything happens in the browser. |
| > **The app schema.** To-do and Reminders are SCHEMA-DESCRIBED: a declarative |
| > JSON schema (`schemas/<name>.json`, spec: `docs/app-schema.md`) declares |
| > their state, folds, actions, and views, and BOTH platforms interpret it — |
| -> the web via `schema-engine.js` + `schema-render.js` (their app files wire |
| +> the web via `../schema-engine.js` + `../schema-render.js` (their app files wire |
| > `schemaEvents`/`schemaRender` into defineApp and keep only the glue: |
| > commands, preview/unread, chat wording, named parsers), the native apps via |
| > the Swift twin engine with SwiftUI components. To change those apps' events, |
diff --git a/web/static/apps/remind.js b/web/static/apps/remind.js| index f8ab5f0..55da526 100644 |
| --- a/web/static/apps/remind.js |
| +++ b/web/static/apps/remind.js |
| @@ -29,7 +29,7 @@ |
| // in Node tests while still resolving to /static/apps.js in the browser. |
| import { defineApp } from "../apps.js"; |
| import { ICON_ALARM_CLOCK } from "../icons.js"; |
| -import { schemaEvents, schemaInitialState, schemaRender, schemaStyles } from "./schema-render.js"; |
| +import { schemaEvents, schemaInitialState, schemaRender, schemaStyles } from "../schema-render.js"; |
| import schema from "./schemas/remind.json" with { type: "json" }; |
| // ---- time parsing (pure, unit-tested) ----------------------------------- |
diff --git a/web/static/apps/setup.js b/web/static/apps/setup.js| index d80cc78..49a4394 100644 |
| --- a/web/static/apps/setup.js |
| +++ b/web/static/apps/setup.js |
| @@ -11,10 +11,10 @@ |
| // persistence) and shell.js's setup bridge (setSetupBridge). Like /tutorial it |
| // must work with no AI and no bot at all (a fresh instance has neither). |
| import { defineApp, setupBridge, awaitAnswer, cancelAnswer, appStateFor, emitAppEvent, spaceInfo, openAppThread } from "../apps.js"; |
| -import { makeSay, makeLiveSay, sayEvents, sayBubble } from "./eleven.js"; |
| -import { runFlow } from "./flow.js"; |
| -import SETUP_FLOW from "./setup.flow.js"; |
| -import { startTour } from "./tutorial.js"; |
| +import { makeSay, makeLiveSay, sayEvents, sayBubble } from "../eleven.js"; |
| +import { runFlow } from "../flow.js"; |
| +import SETUP_FLOW from "../setup.flow.js"; |
| +import { startTour } from "../tutorial.js"; |
| export const app = defineApp({ |
| name: "setup", |
diff --git a/web/static/apps/todo.js b/web/static/apps/todo.js| index bab2d4c..7d98e61 100644 |
| --- a/web/static/apps/todo.js |
| +++ b/web/static/apps/todo.js |
| @@ -14,7 +14,7 @@ |
| // tests while still resolving to /static/apps.js in the browser. |
| import { defineApp } from "../apps.js"; |
| import { ICON_SQUARE_CHECK } from "../icons.js"; |
| -import { schemaEvents, schemaInitialState, schemaRender, schemaStyles } from "./schema-render.js"; |
| +import { schemaEvents, schemaInitialState, schemaRender, schemaStyles } from "../schema-render.js"; |
| import schema from "./schemas/todo.json" with { type: "json" }; |
| // A to-do is { id, text, done, doneAt, by } — see schemas/todo.json for the |
diff --git a/web/static/apps/vault.js b/web/static/apps/vault.js| index 08cb2d8..a3c043f 100644 |
| --- a/web/static/apps/vault.js |
| +++ b/web/static/apps/vault.js |
| @@ -69,7 +69,7 @@ import { |
| unsealValue, |
| grantVaultKey, |
| acceptGrant, |
| -} from "./vaultcrypto.js"; |
| +} from "../vaultcrypto.js"; |
| // How long a revealed value stays visible before it re-locks itself — short, so a |
| // vault left open on screen doesn't sit unmasked. Clicking it again re-arms it. |
diff --git a/web/static/apps/views.js b/web/static/apps/views.js| index 28878c1..21cb19c 100644 |
| --- a/web/static/apps/views.js |
| +++ b/web/static/apps/views.js |
| @@ -10,7 +10,7 @@ |
| // bounded, non-https URLs dropped, item and byte caps enforced, oldest views |
| // evicted past the per-thread cap. |
| import { defineApp, appStateFor, openCommandPanel } from "../apps.js"; |
| -import { schemaRender, schemaStyles } from "./schema-render.js"; |
| +import { schemaRender, schemaStyles } from "../schema-render.js"; |
| // Caps — the fold's half of the two-sided bound (the sidecar's validator in |
| // internal/bot/view.go is the other; keep them in step). |
diff --git a/web/static/apps/welcome.js b/web/static/apps/welcome.js| index 8345a2b..d3c5421 100644 |
| --- a/web/static/apps/welcome.js |
| +++ b/web/static/apps/welcome.js |
| @@ -11,10 +11,10 @@ |
| // eleven.js's feel and shell.js's setup bridge. Like setup it must work with |
| // no AI and no sidecar (the sidecar's welcome DM stood down in its favour). |
| import { defineApp, setupBridge, awaitAnswer, cancelAnswer, appStateFor, emitAppEvent, spaceInfo } from "../apps.js"; |
| -import { makeSay, makeLiveSay, sayEvents, sayBubble } from "./eleven.js"; |
| -import { runFlow } from "./flow.js"; |
| -import WELCOME_FLOW from "./welcome.flow.js"; |
| -import { startTour } from "./tutorial.js"; |
| +import { makeSay, makeLiveSay, sayEvents, sayBubble } from "../eleven.js"; |
| +import { runFlow } from "../flow.js"; |
| +import WELCOME_FLOW from "../welcome.flow.js"; |
| +import { startTour } from "../tutorial.js"; |
| export const app = defineApp({ |
| name: "welcome", |
diff --git a/web/static/apps/eleven.js b/web/static/eleven.js| similarity index 99% |
| rename from web/static/apps/eleven.js |
| rename to web/static/eleven.js |
| index 2a2cb18..96c23f1 100644 |
| --- a/web/static/apps/eleven.js |
| +++ b/web/static/eleven.js |
| @@ -6,7 +6,7 @@ |
| // action chips and composer hints ride a client-only notice (they need the |
| // running script's closures); on a resumed run a line already in history |
| // re-arms its chips instantly, with no typing beat and no duplicate bubble. |
| -import { emitAppEvent, appStateFor, awaitAnswer } from "../apps.js"; |
| +import { emitAppEvent, appStateFor, awaitAnswer } from "./apps.js"; |
| function sleep(ms) { |
| return new Promise((r) => setTimeout(r, ms)); |
diff --git a/web/static/apps/flow.js b/web/static/flow.js| similarity index 100% |
| rename from web/static/apps/flow.js |
| rename to web/static/flow.js |
diff --git a/web/static/onboardingui.js b/web/static/onboardingui.js| index 5d13214..65de222 100644 |
| --- a/web/static/onboardingui.js |
| +++ b/web/static/onboardingui.js |
| @@ -9,7 +9,7 @@ import { session, roster, myUserId, threads, threadKeys, activeId, threadName, e |
| import { encrypt } from "/static/crypto.js"; |
| import { appStateFor, setupBridge, spaceInfo, emitAppEvent } from "/static/apps.js"; |
| import { appendNotice, setNoticeTyping, feedGlideToBottom } from "/static/apps/chat.js"; |
| -import { planQuickForm } from "/static/apps/quickform.js"; |
| +import { planQuickForm } from "/static/quickform.js"; |
| import { startWelcome } from "/static/apps/welcome.js"; |
| import { showDiscover, createThread } from "/static/newchat.js"; |
diff --git a/web/static/apps/quickform.js b/web/static/quickform.js| similarity index 100% |
| rename from web/static/apps/quickform.js |
| rename to web/static/quickform.js |
diff --git a/web/static/apps/schema-engine.js b/web/static/schema-engine.js| similarity index 100% |
| rename from web/static/apps/schema-engine.js |
| rename to web/static/schema-engine.js |
diff --git a/web/static/apps/schema-render.js b/web/static/schema-render.js| similarity index 100% |
| rename from web/static/apps/schema-render.js |
| rename to web/static/schema-render.js |
diff --git a/web/static/apps/setup.flow.js b/web/static/setup.flow.js| similarity index 100% |
| rename from web/static/apps/setup.flow.js |
| rename to web/static/setup.flow.js |
diff --git a/web/static/shell.js b/web/static/shell.js| index 4878901..cf5df70 100644 |
| --- a/web/static/shell.js |
| +++ b/web/static/shell.js |
| @@ -125,10 +125,10 @@ import "/static/apps/linear.js"; |
| import { openThreadView } from "/static/apps/views.js"; // views: bot-created data surfaces (docs/views.md); also registers the app |
| import "/static/apps/emoji.js"; // /emoji — the command-panel reference app |
| import "/static/apps/gif.js"; // /gif — Giphy search in a command panel (bring-your-own key; posts via ctx.host.gif) |
| -import { startTour } from "/static/apps/tutorial.js"; // also registers the /tutorial floor command |
| +import { startTour } from "/static/tutorial.js"; // also registers the /tutorial floor command |
| import { startSetup } from "/static/apps/setup.js"; // the first-run setup conversation (also registers its app) |
| import { startWelcome } from "/static/apps/welcome.js"; // the joiner welcome conversation (also registers its app) |
| -import { planQuickForm } from "/static/apps/quickform.js"; // "Fill in a form instead" — values → verbs+records |
| +import { planQuickForm } from "/static/quickform.js"; // "Fill in a form instead" — values → verbs+records |
| import "/static/apps/admin.js"; // the admin panel (admin-only; opened via /admin or setup's chip) |
| import "/static/apps/appstudio.js"; // make + manage your own member apps (docs/member-apps.md) |
| import { openMemoryPanel } from "/static/apps/memory.js"; // /memory — the AI-memory scratchpad (docs/eleven-memory.md) |
diff --git a/web/static/apps/tutorial.js b/web/static/tutorial.js| similarity index 99% |
| rename from web/static/apps/tutorial.js |
| rename to web/static/tutorial.js |
| index 68faec6..309e2cb 100644 |
| --- a/web/static/apps/tutorial.js |
| +++ b/web/static/tutorial.js |
| @@ -13,7 +13,7 @@ |
| // ephemeral (see buildLocalNotice). The tour reads the group name + optional |
| // administrator identity via spaceInfo(), and shows the AI section only when a |
| // conversational helper is present. |
| -import { registerFloorCommand, defaultBot, openAppThread, spaceInfo, elevenTour } from "../apps.js"; |
| +import { registerFloorCommand, defaultBot, openAppThread, spaceInfo, elevenTour } from "./apps.js"; |
| // The tour is entered two ways — typing /tutorial, and the welcome DM's "Take the |
| // tour" chip — so registration lives here and startTour is exported for the chip. |
diff --git a/web/static/apps/vaultcrypto.js b/web/static/vaultcrypto.js| similarity index 99% |
| rename from web/static/apps/vaultcrypto.js |
| rename to web/static/vaultcrypto.js |
| index 1c1bbba..acfeaef 100644 |
| --- a/web/static/apps/vaultcrypto.js |
| +++ b/web/static/vaultcrypto.js |
| @@ -24,7 +24,7 @@ import { |
| openSealed, |
| wrapThreadKey, |
| unwrapThreadKey, |
| -} from "../crypto.js"; |
| +} from "./crypto.js"; |
| // ---- PRF -> KEK (the browser end) ------------------------------------------ |
diff --git a/web/static/apps/welcome.flow.js b/web/static/welcome.flow.js| similarity index 100% |
| rename from web/static/apps/welcome.flow.js |
| rename to web/static/welcome.flow.js |