@jxsuite/studio 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/css-meta.json +1487 -0
- package/data/elements-meta.json +370 -0
- package/data/html-meta.json +366 -0
- package/data/stylebook-meta.json +200 -0
- package/data/webdata.json +1621 -0
- package/dist/chunks/{Overlay.dev-ymy3et3y.js → Overlay.dev-d2ajy6sj.js} +7 -7
- package/dist/chunks/{content-source-r2n7v682.js → content-source-56xpvx83.js} +13 -12
- package/dist/chunks/{convert-to-repeater-cnpyvj74.js → convert-to-repeater-0xh9wwek.js} +12 -11
- package/dist/chunks/{extension-sections-vpcm4jvx.js → extension-sections-g34239vm.js} +17 -16
- package/dist/chunks/{files-zeg88k2d.js → files-gkz202dp.js} +18 -19
- package/dist/chunks/{format-host-taymsw55.js → format-host-pej3srnw.js} +2 -2
- package/dist/chunks/{git-panel-qp4rrwr9.js → git-panel-4j6xbh8g.js} +12 -11
- package/dist/chunks/{layers-panel-kj93w2zg.js → layers-panel-8mvn8sgd.js} +15 -14
- package/dist/chunks/{monaco-setup-xy3bs05d.js → monaco-setup-a5wkmbgh.js} +6 -3
- package/dist/chunks/{publish-panel-6qz6kp41.js → publish-panel-ptddc3kg.js} +6 -6
- package/dist/chunks/{quick-search-9wwa09hh.js → quick-search-j88azfs2.js} +19 -18
- package/dist/chunks/right-panel-egkaxy89.js +38 -0
- package/dist/chunks/{sp-overlay.dev-h6kdx076.js → sp-overlay.dev-s18hymn9.js} +8 -8
- package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +15 -0
- package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +15 -0
- package/dist/chunks/{studio-753znj4c.js → studio-0e4h32g6.js} +5 -5
- package/dist/chunks/{studio-yz9s88s0.js → studio-10s469da.js} +25 -9
- package/dist/chunks/{studio-6f0am8f2.js → studio-2w11grav.js} +2 -2
- package/dist/chunks/{studio-zqv3w24n.js → studio-3q65xvzv.js} +5 -5
- package/dist/chunks/{studio-qhh8r5fz.js → studio-4058pg09.js} +4 -4
- package/dist/chunks/{studio-hekaagev.js → studio-4enps87v.js} +17 -17
- package/dist/chunks/{studio-tqemhphj.js → studio-50eed4c9.js} +4 -4
- package/dist/chunks/studio-53n8bp04.js +16 -0
- package/dist/chunks/{studio-nqq8jk8f.js → studio-5amdrjm4.js} +14 -14
- package/dist/chunks/{studio-6zzbyx17.js → studio-5ne8mdx9.js} +2 -2
- package/dist/chunks/{studio-3z8jjm2p.js → studio-7ejemhej.js} +3 -3
- package/dist/chunks/{studio-1kgfcv7s.js → studio-a11d3a1b.js} +12 -11
- package/dist/chunks/{studio-rgwcpast.js → studio-ay8wcr2b.js} +2 -2
- package/dist/chunks/{studio-fys6z73y.js → studio-bak2cyjr.js} +7 -7
- package/dist/chunks/{studio-xmvbhg31.js → studio-c31yb4t6.js} +2 -2
- package/dist/chunks/{studio-8sdyjfd6.js → studio-cf4rk7kv.js} +2 -2
- package/dist/chunks/{studio-yphy4z9m.js → studio-cv606rs1.js} +5 -5
- package/dist/chunks/{studio-5kt960pk.js → studio-hk523hby.js} +133 -147
- package/dist/chunks/{studio-x0et2z9g.js → studio-hs8x23fv.js} +15 -15
- package/dist/chunks/{studio-p471gg5n.js → studio-kvfx9z2c.js} +7 -3
- package/dist/chunks/{studio-22xr4q8q.js → studio-ncnet0ba.js} +18 -29
- package/dist/chunks/{studio-ckwz4d3x.js → studio-twsafr1e.js} +2 -2
- package/dist/chunks/{studio-qrsfh1w7.js → studio-tzx3xmvx.js} +5 -5
- package/dist/chunks/{studio-g8t7g0gp.js → studio-vdpcpada.js} +3 -3
- package/dist/chunks/{studio-6a0h7sne.js → studio-vhk5fmfv.js} +2 -2
- package/dist/chunks/{studio-hrajjyt2.js → studio-wdt9y92a.js} +2 -2
- package/dist/chunks/{studio-r8vx7bkz.js → studio-x3etxs0s.js} +14 -13
- package/dist/chunks/{studio-pv8m0h4a.js → studio-yxzgjq8q.js} +28 -23
- package/dist/chunks/{studio-300zsz1k.js → studio-zdkgwhkv.js} +6 -6
- package/dist/codicon.ttf +0 -0
- package/dist/iframe-entry.js +8 -1
- package/dist/manifest.json +82 -0
- package/dist/studio.js +232 -103
- package/index.html +0 -50
- package/package.json +12 -5
- package/src/canvas/iframe-entry.ts +5 -0
- package/src/canvas/iframe-host.ts +45 -4
- package/src/content/entry-editor.ts +2 -1
- package/src/editor/context-menu.ts +13 -2
- package/src/editor/slash-menu.ts +26 -11
- package/src/files/files.ts +11 -23
- package/src/grid/grid-panel.ts +27 -6
- package/src/hosting/document.ts +109 -0
- package/src/hosting/layout.ts +211 -0
- package/src/hosting/stage.ts +166 -0
- package/src/hosting/tsconfig.json +18 -0
- package/src/panels/ai-chat/chat-view.ts +25 -14
- package/src/panels/git-panel.ts +17 -14
- package/src/panels/head-panel.ts +32 -14
- package/src/panels/imports-panel.ts +32 -13
- package/src/panels/layers-panel.ts +9 -1
- package/src/panels/panel-registry.ts +0 -1
- package/src/panels/properties-panel.ts +3 -3
- package/src/panels/settings-pane.ts +1 -1
- package/src/panels/style-panel.ts +3 -3
- package/src/panels/stylebook-panel.ts +10 -1
- package/src/panels/toolbar.ts +1 -1
- package/src/services/bundle-base.ts +69 -0
- package/src/services/monaco-setup.ts +19 -13
- package/src/shell/tree.ts +155 -0
- package/src/shell.ts +0 -2
- package/src/studio.ts +22 -2
- package/src/types.ts +15 -0
- package/src/ui/regions.ts +10 -45
- package/styles/overlays.css +35 -0
- package/dist/chunks/2020-xp8vnwma.js.map +0 -24
- package/dist/chunks/Overlay.dev-ymy3et3y.js.map +0 -9
- package/dist/chunks/adapter-jkcyhgtr.js.map +0 -9
- package/dist/chunks/cf-settings-g5fbf2zp.js.map +0 -9
- package/dist/chunks/components-z6cnkyy1.js.map +0 -9
- package/dist/chunks/content-source-r2n7v682.js.map +0 -9
- package/dist/chunks/convert-to-repeater-cnpyvj74.js.map +0 -10
- package/dist/chunks/editor-cptq5jp1.js.map +0 -9
- package/dist/chunks/extension-sections-vpcm4jvx.js.map +0 -11
- package/dist/chunks/files-zeg88k2d.js.map +0 -9
- package/dist/chunks/flow-qbfgrpeh.js.map +0 -12
- package/dist/chunks/focus-trap.esm-ggawf1hy.js.map +0 -11
- package/dist/chunks/focus-visible-6eg70z18.js.map +0 -10
- package/dist/chunks/format-host-taymsw55.js.map +0 -9
- package/dist/chunks/git-panel-qp4rrwr9.js.map +0 -9
- package/dist/chunks/index-nj44jpzj.js.map +0 -14
- package/dist/chunks/index-zqxmqxmh.js.map +0 -9
- package/dist/chunks/javascript-q8kpmfah.js.map +0 -11
- package/dist/chunks/jsonMode-dkr040ga.js.map +0 -18
- package/dist/chunks/jx-validate-jj9rnt4n.js.map +0 -9
- package/dist/chunks/layers-panel-kj93w2zg.js.map +0 -9
- package/dist/chunks/monaco-binding-rj6xpx9y.js.map +0 -10
- package/dist/chunks/monaco-setup-xy3bs05d.js.map +0 -15
- package/dist/chunks/publish-panel-6qz6kp41.js.map +0 -9
- package/dist/chunks/quick-search-9wwa09hh.js.map +0 -9
- package/dist/chunks/right-panel-faf816ch.js +0 -37
- package/dist/chunks/right-panel-faf816ch.js.map +0 -9
- package/dist/chunks/section-registry-sx1ykvkw.js.map +0 -9
- package/dist/chunks/sp-overlay.dev-h6kdx076.js.map +0 -9
- package/dist/chunks/sp-popover.dev-dj9x7e7a.js.map +0 -9
- package/dist/chunks/sp-progress-circle.dev-0fpvhm52.js +0 -15
- package/dist/chunks/sp-progress-circle.dev-0fpvhm52.js.map +0 -9
- package/dist/chunks/sp-tooltip.dev-rvepkw4r.js +0 -15
- package/dist/chunks/sp-tooltip.dev-rvepkw4r.js.map +0 -9
- package/dist/chunks/sp-tray.dev-64v4pxw8.js.map +0 -12
- package/dist/chunks/studio-0a5qrh01.js.map +0 -9
- package/dist/chunks/studio-0rqqhrdy.js.map +0 -12
- package/dist/chunks/studio-18j3bmcz.js.map +0 -10
- package/dist/chunks/studio-1kgfcv7s.js.map +0 -13
- package/dist/chunks/studio-22xr4q8q.js.map +0 -13
- package/dist/chunks/studio-300zsz1k.js.map +0 -39
- package/dist/chunks/studio-3fk1ac9z.js.map +0 -12
- package/dist/chunks/studio-3n4ndrzh.js.map +0 -41
- package/dist/chunks/studio-3z8jjm2p.js.map +0 -13
- package/dist/chunks/studio-4mqb9a0j.js.map +0 -14
- package/dist/chunks/studio-4q0wznzf.js.map +0 -73
- package/dist/chunks/studio-5ehhackm.js.map +0 -10
- package/dist/chunks/studio-5kt960pk.js.map +0 -38
- package/dist/chunks/studio-5x5774ak.js.map +0 -10
- package/dist/chunks/studio-685s0ytq.js.map +0 -76
- package/dist/chunks/studio-6a0h7sne.js.map +0 -15
- package/dist/chunks/studio-6f0am8f2.js.map +0 -10
- package/dist/chunks/studio-6kv3apa9.js.map +0 -10
- package/dist/chunks/studio-6zzbyx17.js.map +0 -12
- package/dist/chunks/studio-753znj4c.js.map +0 -16
- package/dist/chunks/studio-7afd21nw.js.map +0 -10
- package/dist/chunks/studio-8jpzqk5s.js.map +0 -10
- package/dist/chunks/studio-8q517dfr.js.map +0 -10
- package/dist/chunks/studio-8sdyjfd6.js.map +0 -10
- package/dist/chunks/studio-a0tswy0w.js.map +0 -9
- package/dist/chunks/studio-a7pzveq1.js.map +0 -11
- package/dist/chunks/studio-ahnzd9rg.js.map +0 -10
- package/dist/chunks/studio-b9b6t1dg.js.map +0 -651
- package/dist/chunks/studio-bw5dz6km.js.map +0 -21
- package/dist/chunks/studio-cjm7fdjf.js.map +0 -14
- package/dist/chunks/studio-ckwz4d3x.js.map +0 -12
- package/dist/chunks/studio-d1ks410h.js.map +0 -10
- package/dist/chunks/studio-dg7zyt5x.js.map +0 -10
- package/dist/chunks/studio-fys6z73y.js.map +0 -10
- package/dist/chunks/studio-g1wdche0.js.map +0 -56
- package/dist/chunks/studio-g8t7g0gp.js.map +0 -10
- package/dist/chunks/studio-h4rxscfc.js.map +0 -10
- package/dist/chunks/studio-hekaagev.js.map +0 -18
- package/dist/chunks/studio-hrajjyt2.js.map +0 -10
- package/dist/chunks/studio-nqq8jk8f.js.map +0 -14
- package/dist/chunks/studio-p471gg5n.js.map +0 -14
- package/dist/chunks/studio-pv8m0h4a.js.map +0 -98
- package/dist/chunks/studio-qhh8r5fz.js.map +0 -13
- package/dist/chunks/studio-qrsfh1w7.js.map +0 -41
- package/dist/chunks/studio-r8vx7bkz.js.map +0 -235
- package/dist/chunks/studio-rgwcpast.js.map +0 -12
- package/dist/chunks/studio-s519zc99.js.map +0 -10
- package/dist/chunks/studio-sas7mt3k.js.map +0 -10
- package/dist/chunks/studio-t5wqwzdh.js.map +0 -10
- package/dist/chunks/studio-tqemhphj.js.map +0 -11
- package/dist/chunks/studio-tr20m8g8.js.map +0 -9
- package/dist/chunks/studio-tvgyw9xw.js.map +0 -14
- package/dist/chunks/studio-x0et2z9g.js.map +0 -32
- package/dist/chunks/studio-xm3dbxvn.js.map +0 -19
- package/dist/chunks/studio-xmvbhg31.js.map +0 -10
- package/dist/chunks/studio-ynxwkrzd.js.map +0 -246
- package/dist/chunks/studio-yphy4z9m.js.map +0 -10
- package/dist/chunks/studio-yz9s88s0.js.map +0 -54
- package/dist/chunks/studio-z6nw2ek2.js.map +0 -10
- package/dist/chunks/studio-zqv3w24n.js.map +0 -11
- package/dist/chunks/tsMode-76kr53q0.js.map +0 -13
- package/dist/chunks/ws-client-6shc5rze.js.map +0 -12
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/packages/ensure-deps.ts", "../src/content/entry-model.ts", "../src/recent-projects.ts", "../src/files/files.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/// <reference lib=\"dom\" />\n/**\n * Ensure the active project's dependencies are installed before the editor loads. When the backend\n * reports node_modules is missing, run `bun install` behind a blocking progress modal so component\n * and format resolution see the real dependency tree. No-op on platforms without package support.\n */\n\nimport { getPlatform } from \"../platform\";\nimport { showProgressModal } from \"../ui/progress-modal\";\nimport { shouldInstallAutomation } from \"../services/automation\";\n\nexport async function ensureDependenciesInstalled(): Promise<void> {\n // Automation/screenshot runs open projects read-only to drive the canvas — they must never mutate\n // The project on disk (a `bun install` would litter node_modules/bun.lock into starter templates).\n if (shouldInstallAutomation(location.search)) {\n return;\n }\n const platform = getPlatform();\n if (!platform.dependenciesNeedInstall || !platform.installDependencies) {\n return;\n }\n let needs = false;\n try {\n needs = await platform.dependenciesNeedInstall();\n } catch {\n return;\n }\n if (!needs) {\n return;\n }\n const progress = showProgressModal({\n status: \"Running bun install…\",\n title: \"Installing dependencies\",\n });\n try {\n const result = await platform.installDependencies();\n if (result.ok) {\n progress.done();\n } else {\n progress.fail(result.log ?? \"bun install failed\");\n }\n } catch (error) {\n progress.fail(error instanceof Error ? error.message : String(error));\n }\n}\n",
|
|
6
|
-
"/**\n * The content-entry model — which collection an entry belongs to, and what a NEW one contains. Pure\n * over its inputs (the project config it reads, the schema it is handed), so the seeding rules can\n * be asserted without a DOM, a platform or a tab.\n *\n * **Two facts this module exists to stop being restated.**\n *\n * 1. **A new entry is seeded, not blank.** `library-pane.ts`'s collection branch created the file\n * through the shared creation flow with no extension and no body, so a new `blog` entry landed\n * as `content/blog/untitled` — no `.md`, therefore matched by no format, therefore not an entry\n * of the collection it was created in, and its required fields were absent rather than empty.\n * {@link seedEntry} answers with the schema's own defaults, and every REQUIRED property that\n * declares none gets a typed empty, because \"present and blank\" is a valid entry the author can\n * fill in and \"absent\" is a validation failure they did not cause.\n * 2. **Where the entry files are** is `collectionInfo`'s answer, imported rather than recomputed.\n * Three copies of \"which collection owns this path, and what extension do its entries carry\"\n * already exist (`utils/studio-utils.ts`'s `findContentTypeSchema`, `grid/sources/content-\n * source.ts`'s `collectionInfo`, `browse/library-model.ts`'s `contentTypeFor`); this module adds\n * no fourth and calls the two that are exported.\n *\n * **The draft axis is NOT here.** `content/draft-state.ts` owns what a draft is and whether drafts\n * are listed, because this module imports `content-source.ts` and the grid source must be able to\n * ask what a draft is — the reverse edge would be a cycle, and `import/no-cycle` is an error here.\n */\n\nimport { collectionInfo } from \"../grid/sources/content-source\";\nimport { projectState } from \"../store\";\nimport { findContentTypeSchema } from \"../utils/studio-utils\";\nimport type { ContentSectionEntry } from \"../types\";\nimport type { ContentTypeSchema, ContentTypeSchemaField } from \"@jxsuite/schema/types\";\n\n// ─── Collections ─────────────────────────────────────────────────────────────\n\n/** One content collection, as the entry surfaces need it. */\nexport interface EntryCollection {\n name: string;\n /** Source directory, project-relative and without a trailing slash. */\n dir: string;\n /** The extension a new entry of this collection is written with (`.md`, `.json`, …). */\n ext: string;\n schema: ContentTypeSchema | null;\n def: ContentSectionEntry;\n}\n\n/** Every content type the project declares, in declaration order. */\nexport function collectionNames(): string[] {\n return Object.keys((projectState?.projectConfig?.content ?? {}) as Record<string, unknown>);\n}\n\n/**\n * The collection named `name`, or null when the project declares no such directory-backed type.\n *\n * A collection whose `source` names a FILE (a CSV catalogue) has no per-entry files, so it has no\n * entry editor and no New Entry — `collectionInfo` still answers for it, and this rejects it here\n * rather than letting a caller compose `products/catalog.csv/untitled.csv`.\n */\nexport function entryCollection(name: string): EntryCollection | null {\n const info = collectionInfo(name);\n if (!info) {\n return null;\n }\n if (/\\.[a-z\\d]+$/i.test(info.dir)) {\n return null;\n }\n return {\n def: info.def,\n dir: info.dir,\n ext: info.ext,\n name: info.name,\n schema: info.def.schema ?? null,\n };\n}\n\n/** The collections a New Entry can be created in — directory-backed types only. */\nexport function entryCollections(): EntryCollection[] {\n return collectionNames()\n .map((name) => entryCollection(name))\n .filter((collection): collection is EntryCollection => collection !== null);\n}\n\n/**\n * The collection a document path belongs to, or null.\n *\n * Delegates to `findContentTypeSchema`, which is the matcher the frontmatter surfaces already use —\n * including its Windows backslash normalisation and its file-source (CSV) branch. A path that\n * matches a type with no `schema` is NOT a match there, and that is the right answer here too: an\n * entry editor over a collection that declares no shape has no form to draw.\n */\nexport function collectionOfPath(path: string | null): EntryCollection | null {\n const matched = findContentTypeSchema(path ?? null, projectState?.projectConfig);\n return matched ? entryCollection(matched.name) : null;\n}\n\n// ─── Seeding a new entry ─────────────────────────────────────────────────────\n\n/** Deep copy of a schema `default`, so two entries seeded from one schema never share an object. */\nfunction cloneDefault(value: unknown): unknown {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n // oxlint-disable-next-line unicorn/prefer-structured-clone\n return JSON.parse(JSON.stringify(value)) as unknown;\n}\n\n/**\n * The empty value of a declared type — what a REQUIRED property with no `default` is seeded with.\n *\n * A reference (`$ref` to another collection) seeds as `\"\"`: it is required, so the key must exist,\n * and picking an arbitrary entry of the target collection on the author's behalf would be inventing\n * a relationship they never stated.\n */\nfunction emptyForField(field: ContentTypeSchemaField): unknown {\n if (typeof field.$ref === \"string\") {\n return \"\";\n }\n switch (field.type) {\n case \"boolean\": {\n return false;\n }\n case \"integer\":\n case \"number\": {\n return 0;\n }\n case \"array\": {\n return [];\n }\n case \"object\": {\n return seedEntry(field as ContentTypeSchema);\n }\n default: {\n return \"\";\n }\n }\n}\n\n/**\n * The frontmatter a new entry of this schema starts life with.\n *\n * Two rules, and nothing else:\n *\n * - A property that declares a `default` gets it (cloned);\n * - A property listed in `required` that declares no default gets its type's empty value.\n *\n * An optional property with no default is **omitted**. Seeding every declared field would write the\n * author's file full of keys they never asked for, and \"the schema mentions it\" is not a reason for\n * it to exist on disk — the form still draws a row for it, because the form draws the SCHEMA.\n */\nexport function seedEntry(schema: ContentTypeSchema | null | undefined): Record<string, unknown> {\n const properties = schema?.properties;\n if (!properties) {\n return {};\n }\n const required = new Set(Array.isArray(schema?.required) ? schema.required : []);\n const seed: Record<string, unknown> = {};\n for (const [name, field] of Object.entries(properties)) {\n if (field.default !== undefined) {\n seed[name] = cloneDefault(field.default);\n } else if (required.has(name)) {\n seed[name] = emptyForField(field);\n }\n }\n return seed;\n}\n\n/**\n * Required properties this record leaves absent — what a caller checks before claiming an entry is\n * valid. `seedEntry`'s whole promise is that this is empty for a freshly seeded entry.\n */\nexport function missingRequired(\n schema: ContentTypeSchema | null | undefined,\n value: Record<string, unknown>,\n): string[] {\n const required = Array.isArray(schema?.required) ? schema.required : [];\n return required.filter((name) => value[name] === undefined);\n}\n",
|
|
7
|
-
"/// <reference lib=\"dom\" />\nimport { getPlatform, hasPlatform } from \"./platform\";\nimport { now } from \"./services/clock\";\nimport type { RecentProjectEntry } from \"./types\";\n\ninterface RecentFile {\n path: string;\n name: string;\n /** The project root the file belongs to, so recents can be scoped to the open project. */\n root: string;\n timestamp: number;\n}\n\nconst STORAGE_KEY = \"jx-studio-recent-projects\";\nconst FILES_STORAGE_KEY = \"jx-studio-recent-files\";\nconst MAX_RECENT = 8;\nconst MAX_RECENT_FILES = 10;\n\n/**\n * In-memory mirror of the backend recent-projects store, hydrated once at startup. Render is\n * synchronous, so reads come from here (or directly from localStorage on the dev server) rather\n * than awaiting the backend.\n */\nlet cache: RecentProjectEntry[] = [];\n\n/**\n * The active backend store, or null when none is available (dev server). Desktop and chromium\n * persist a user-level file shared across all projects/windows; the dev server falls back to\n * per-origin localStorage, which already survives reloads.\n */\nfunction backend(): StudioRecentStore | null {\n if (!hasPlatform()) {\n return null;\n }\n const platform = getPlatform();\n return platform.getRecentProjects && platform.saveRecentProjects\n ? (platform as StudioRecentStore)\n : null;\n}\n\ninterface StudioRecentStore {\n getRecentProjects: () => Promise<RecentProjectEntry[]>;\n saveRecentProjects: (projects: RecentProjectEntry[]) => Promise<void>;\n}\n\nfunction loadFromLocalStorage(): RecentProjectEntry[] {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) {\n return [];\n }\n const parsed = JSON.parse(raw) as RecentProjectEntry[];\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\n/** The current list from whichever store is active (cache for backend, localStorage otherwise). */\nfunction currentList(): RecentProjectEntry[] {\n return backend() ? cache : loadFromLocalStorage();\n}\n\n/** Persist a new list to the active store and keep the in-memory cache in sync. */\nfunction commit(list: RecentProjectEntry[]): void {\n cache = list;\n const store = backend();\n if (store) {\n void store.saveRecentProjects(list);\n } else {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(list));\n } catch {\n /* Storage unavailable — best effort */\n }\n }\n}\n\n/**\n * Load the recent-projects list into the in-memory cache. Call once after the platform is\n * registered; a no-op (other than priming the cache) on the dev server, where reads hit\n * localStorage directly.\n */\nexport async function hydrateRecentProjects(): Promise<void> {\n const store = backend();\n if (!store) {\n return;\n }\n try {\n cache = await store.getRecentProjects();\n } catch {\n cache = [];\n }\n}\n\n/** @returns {RecentProjectEntry[]} Newest-first */\nexport function getRecentProjects(): RecentProjectEntry[] {\n return currentList().toSorted((a, b) => b.timestamp - a.timestamp);\n}\n\n/**\n * @param {string} name\n * @param {string} root\n */\nexport function addRecentProject(name: string, root: string) {\n const projects = currentList().filter((p) => p.root !== root);\n projects.unshift({ name, root, timestamp: now() });\n if (projects.length > MAX_RECENT) {\n projects.length = MAX_RECENT;\n }\n commit(projects);\n}\n\n/** Drop a single project from the recent list (used by the UI remove action + auto-prune). */\nexport function removeRecentProject(root: string) {\n commit(currentList().filter((p) => p.root !== root));\n}\n\nexport function clearRecentProjects() {\n cache = [];\n const store = backend();\n if (store) {\n void store.saveRecentProjects([]);\n } else {\n localStorage.removeItem(STORAGE_KEY);\n }\n}\n\nfunction loadRecentFiles(): RecentFile[] {\n try {\n const raw = localStorage.getItem(FILES_STORAGE_KEY);\n if (!raw) {\n return [];\n }\n const parsed = JSON.parse(raw) as RecentFile[];\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Recently-opened files, newest-first. Pass a project `root` to scope the list to that project (the\n * Quick Access modal does this so it only ever shows files from the open project).\n *\n * @param {string} [root]\n * @returns {RecentFile[]}\n */\nexport function getRecentFiles(root?: string) {\n const all = loadRecentFiles().toSorted((a, b) => b.timestamp - a.timestamp);\n return root == null ? all : all.filter((f) => f.root === root);\n}\n\n/** @param {{ path: string; name: string; root: string }} file */\nexport function trackRecentFile(file: { path: string; name: string; root: string }) {\n const all = loadRecentFiles().filter((f) => !(f.root === file.root && f.path === file.path));\n all.unshift({ name: file.name, path: file.path, root: file.root, timestamp: now() });\n // Cap per project so a busy project can't evict another project's history.\n const perRoot = new Map<string, number>();\n const kept: RecentFile[] = [];\n for (const f of all.toSorted((a, b) => b.timestamp - a.timestamp)) {\n const n = (perRoot.get(f.root) ?? 0) + 1;\n perRoot.set(f.root, n);\n if (n <= MAX_RECENT_FILES) {\n kept.push(f);\n }\n }\n localStorage.setItem(FILES_STORAGE_KEY, JSON.stringify(kept));\n}\n",
|
|
8
|
-
"/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n/**\n * File tree management — project loading, file tree rendering, and file CRUD.\n *\n * Functions that mutate state accept a context object with callbacks, following the same pattern as\n * file-ops.js. Every name the user supplies (new file, rename) is collected with the Spectrum\n * prompt dialog from ui/layers.ts — never a native browser prompt (studio-ui-guidelines.md §8.7).\n *\n * **The tree is a flat list of rows, and the DOM holds a window onto it** ({@link FileRow},\n * `ui/virtual-window.ts`). It used to recurse a template per directory level, which is why it drew\n * every expanded row of every expanded directory — a `node_modules` expanded by accident is tens of\n * thousands of `sp-icon` custom elements, built synchronously, on every repaint. A recursion has no\n * row list to window, so the recursion moved into {@link collectFileRows}, which produces the rows\n * in display order and nothing else; the render is then a window over that array.\n *\n * Flattening costs the `role=\"group\"` wrappers, and pays for them with `aria-level` +\n * `aria-posinset` / `aria-setsize` on every row — the same shape the Outline has always had, and\n * the only shape that stays TRUE when the tree draws eleven rows out of ten thousand.\n *\n * @docs studio/interface\n */\n\nimport { html, nothing } from \"lit-html\";\nimport { errorMessage } from \"@jxsuite/schema/parse\";\nimport { localeLabel, localeOfPath, resolveI18n } from \"@jxsuite/schema/locale\";\nimport { classMap } from \"lit-html/directives/class-map.js\";\nimport { ref } from \"lit-html/directives/ref.js\";\nimport { renderPopover, showPromptDialog } from \"../ui/layers\";\nimport { projectState, requireProjectState, setProjectState } from \"../store\";\nimport { getPlatform } from \"../platform\";\nimport { notify } from \"../services/notify\";\nimport { loadComponentRegistry } from \"./components\";\nimport { ensureDependenciesInstalled } from \"../packages/ensure-deps\";\nimport { maybePromptJxsuiteUpdate } from \"../packages/jxsuite-update\";\nimport { autoSyncProjectOnOpen } from \"../packages/pull-package-sync\";\nimport { markLocalMutation } from \"./fs-events\";\nimport { registerPanel } from \"../panels/panel-registry\";\nimport { UPLOAD_ACCEPT, isImage, uploadAssets } from \"./media-upload\";\nimport { isCollabPath } from \"../collab/collab-state\";\nimport {\n draggable,\n dropTargetForElements,\n monitorForElements,\n} from \"@atlaskit/pragmatic-drag-and-drop/element/adapter\";\nimport { combine } from \"@atlaskit/pragmatic-drag-and-drop/combine\";\nimport {\n activateTab,\n moveTabToPane,\n openTab,\n paneOfTab,\n receivingPane,\n renameTab,\n replaceAllTabs,\n setWorkspaceProject,\n workspace,\n} from \"../workspace/workspace\";\nimport { openCsvGridTab, openPagesGrid } from \"../grid/grid-open\";\nimport { collectionDirs } from \"../grid/sources/content-source\";\nimport { activeRegistry } from \"../commands/active-registry\";\nimport { collectionOfPath } from \"../content/entry-model\";\nimport { confirmFileDelete, parseSourceForPath, renamePromptMessage } from \"./file-ops\";\nimport { invalidateUsages } from \"../services/references\";\nimport {\n documentExtensions,\n formatForPath,\n loadFormats,\n noFormatError,\n refreshExtensionUi,\n refreshFormats,\n} from \"../format/format-host\";\nimport { markSessionRestored, persistedSession, resetProjectShell, setActivityTab } from \"../shell\";\nimport { restoreSession } from \"../workspace/session\";\nimport { cleanupGitPanel } from \"../panels/git-panel\";\nimport { addRecentProject, trackRecentFile } from \"../recent-projects\";\nimport {\n listWindow,\n measuredRowHeight,\n revealListRow,\n watchListWindow,\n} from \"../ui/virtual-window\";\nimport type { TemplateResult } from \"lit-html\";\nimport type { JxMutableNode } from \"@jxsuite/schema/types\";\nimport type { ResolvedI18n } from \"@jxsuite/schema/locale\";\nimport type { DirEntry, RenameResult } from \"../types\";\nimport type { ListWindowWatch } from \"../ui/virtual-window\";\nimport { rectOf } from \"../utils/geometry\";\nimport { repeat } from \"lit-html/directives/repeat.js\";\n\n// ─── File icon map ────────────────────────────────────────────────────────────\n\nconst fileIconMap = {\n \"sp-icon-document\": html`<sp-icon-document></sp-icon-document>`,\n \"sp-icon-file-code\": html`<sp-icon-file-code></sp-icon-file-code>`,\n \"sp-icon-file-txt\": html`<sp-icon-file-txt></sp-icon-file-txt>`,\n \"sp-icon-folder\": html`<sp-icon-folder></sp-icon-folder>`,\n \"sp-icon-folder-open\": html`<sp-icon-folder-open></sp-icon-folder-open>`,\n \"sp-icon-image\": html`<sp-icon-image></sp-icon-image>`,\n} as Record<string, TemplateResult>;\n\n// ─── File management ──────────────────────────────────────────────────────────\n\nexport async function loadDirectory(dirPath: string) {\n if (!projectState) {\n return;\n }\n try {\n const platform = getPlatform();\n const entries = await platform.listDirectory(dirPath);\n projectState.dirs.set(dirPath, entries);\n } catch {\n projectState.dirs.set(dirPath, []);\n }\n}\n\n/** Probe the dev server for a root project and populate projectState. */\nexport async function loadProject() {\n try {\n const platform = getPlatform();\n const result = await platform.probeRootProject();\n if (!result) {\n return;\n }\n const { meta, info } = result;\n\n refreshFormats();\n void loadFormats();\n refreshExtensionUi(platform);\n\n setProjectState({\n dirs: new Map(),\n expanded: new Set(),\n isSiteProject: info.isSiteProject,\n name: info.isSiteProject ? info.projectConfig?.name || meta.name : meta.name,\n projectConfig: (info.isSiteProject ? info.projectConfig : null) || null,\n projectDirs: info.directories || [],\n projectRoot: \".\",\n root: meta.root,\n searchQuery: \"\",\n selectedPath: null,\n });\n // Only a site project counts as an open project for the workspace (a bare monorepo root keeps\n // The assistant in bootstrap mode).\n if (info.isSiteProject) {\n setWorkspaceProject(meta.root || \".\", info.projectConfig || null);\n }\n\n if (info.isSiteProject) {\n addRecentProject(requireProjectState().name, meta.root);\n await autoSyncProjectOnOpen();\n await ensureDependenciesInstalled();\n await loadDirectory(\".\");\n await loadComponentRegistry();\n await openLastSessionOrHome();\n void maybePromptJxsuiteUpdate(meta.root);\n }\n // If not a site project (monorepo) — show welcome prompt, don't load tree\n } catch {\n // Not on dev server — project features disabled\n }\n}\n\n// ─── Open Project (PAL-based) ─────────────────────────────────────────────\n\n/**\n * Open a project via the platform adapter, into THIS window.\n *\n * Reports whether a project was actually opened: a cancelled picker and a failed open both leave\n * the window on the project it already had, and the caller announces the outcome — \"Opening the\n * project…\" over a dialog the user just dismissed is a report of something that did not happen.\n *\n * @param {{ renderLeftPanel: () => void }} ctx\n * @returns {Promise<boolean>} Whether the window changed project.\n */\nexport async function openProject({\n renderLeftPanel,\n}: {\n renderLeftPanel: () => void;\n}): Promise<boolean> {\n try {\n const platform = getPlatform();\n const result = await platform.openProject();\n if (!result) {\n return false;\n } // User cancelled\n\n const { config, handle } = result;\n\n replaceAllTabs({\n document: { children: [], tagName: \"div\" },\n id: \"initial\",\n });\n\n refreshFormats();\n void loadFormats();\n refreshExtensionUi(platform);\n\n setProjectState({\n .../** @type {ProjectState} */ projectState,\n dirs: new Map(),\n expanded: new Set(),\n isSiteProject: true,\n name: config.name || handle.name,\n projectConfig: config,\n projectRoot: handle.root,\n searchQuery: \"\",\n selectedPath: null,\n });\n setWorkspaceProject(handle.root, config);\n\n await autoSyncProjectOnOpen();\n await ensureDependenciesInstalled();\n await loadDirectory(\".\");\n await loadComponentRegistry();\n\n // Auto-expand key directories and populate projectDirs for Browse view\n const conventionalDirs = new Set([\n \"pages\",\n \"layouts\",\n \"components\",\n \"content\",\n \"data\",\n \"public\",\n \"styles\",\n ]);\n const entries = requireProjectState().dirs.get(\".\") || [];\n const foundDirs = [];\n for (const e of entries) {\n if (e.type === \"directory\" && conventionalDirs.has(e.name)) {\n foundDirs.push(e.name);\n requireProjectState().expanded.add(e.path || e.name);\n await loadDirectory(e.path || e.name);\n }\n }\n requireProjectState().projectDirs = foundDirs;\n\n // Source control, the stylebook selection and the settings tab describe the project being\n // Left behind. The poll timer goes with them; the panel re-arms it on its next render.\n cleanupGitPanel();\n resetProjectShell();\n setActivityTab(\"files\");\n addRecentProject(requireProjectState().name, requireProjectState().projectRoot);\n renderLeftPanel();\n // The project's name is permanent state in the status bar's PROJECT field now.\n\n await openLastSessionOrHome();\n void maybePromptJxsuiteUpdate(requireProjectState().projectRoot);\n return true;\n } catch (error) {\n notify.error(\"Could not open the project.\", {\n detail: errorMessage(error),\n source: \"Open Project\",\n });\n return false;\n }\n}\n\n/**\n * Give a freshly created project a git repository, so its first irreversible action is recoverable.\n *\n * Runs on the create/import path only, immediately after the backend has written the project — the\n * scaffold is not a repository and nothing else in Studio says so, while Delete and Rename are one\n * confirm click away. `activate` binds the backend to the new root first, because `gitInit` takes\n * no argument and would otherwise run against whichever project the window was serving.\n *\n * Never re-initialises, and never runs on repository-backed platforms (`createDestination: \"repo\"`\n * — a cloud project _is_ a GitHub repository, and its `gitInit` is a no-op by design).\n *\n * @param {string} root Absolute root of the project just created.\n * @returns {Promise<boolean>} Whether a repository was initialised.\n */\nexport async function initProjectRepo(root: string): Promise<boolean> {\n const platform = getPlatform();\n if (platform.createDestination !== \"path\") {\n return false;\n }\n try {\n await platform.activate(root);\n const status = await platform.gitStatus();\n if (status.isRepo) {\n return false;\n }\n await platform.gitInit();\n notify.success(\"Initialized a git repository for this project.\");\n return true;\n } catch (error) {\n // Version control is a safety net, not a precondition — a project that was written stays\n // Written. Say so rather than failing the create the user just completed.\n notify.warn(\"Could not initialize a git repository — the project itself was written.\", {\n detail: errorMessage(error),\n source: \"Source Control\",\n });\n return false;\n }\n}\n\n/**\n * The project's home page path (`pages/index.<page-ext>` or `pages/index.json`), or null if none.\n * Lists `pages/` once and matches by name — a directory read returns 200 with `[]` for a missing\n * dir, so this never provokes the console 404s a blind per-candidate read would.\n */\nexport async function findHomePage(): Promise<string | null> {\n await loadFormats();\n const exts = [...documentExtensions(\"page\"), \".json\"];\n let entries: DirEntry[];\n try {\n entries = await getPlatform().listDirectory(\"pages\");\n } catch {\n return null;\n }\n const files = new Set(entries.filter((e) => e.type === \"file\").map((e) => e.name));\n for (const ext of exts) {\n if (files.has(`index${ext}`)) {\n return `pages/index${ext}`;\n }\n }\n return null;\n}\n\nexport async function openHomePage() {\n const home = await findHomePage();\n if (home) {\n await openFileInTab(home);\n }\n}\n\n/**\n * Reopen the documents this project was last left with, or its home page if there are none.\n *\n * Plan §4.4, and P3's \"Newly possible\": **the session survives a relaunch.** Every one of the three\n * ways into a project — the `?project=` bootstrap, the PAL picker and a recent-project open — ended\n * at `openHomePage()`, so nine open documents, a split and the mode you were in were lost each\n * time. The per-project record's own interface said \"session state grows into this shape\".\n *\n * ONE function for all three, because the three used to be three calls to `openHomePage` and this\n * is exactly the kind of behaviour that lands on two of them.\n *\n * A path that no longer resolves is skipped, and a session that restores NOTHING falls through to\n * the home page rather than leaving an empty window: files move, and a stale record must not cost\n * you the one page a project can always show.\n *\n * @returns Whether a SESSION was restored — `false` means the home page was opened instead.\n */\nexport async function openLastSessionOrHome(): Promise<boolean> {\n // `workspace.projectRoot`, and NOT a root passed in: that is the key `persistProjectShell` writes\n // Under, and the three callers each know the project by a slightly different name — `meta.root`,\n // `projectState.projectRoot`, the recent-list entry. One reader of one field cannot disagree with\n // The writer; three callers passing three spellings silently restore nothing.\n const session = persistedSession(workspace.projectRoot);\n // Read first, THEN allow writes: the persist effect fires the moment `workspace.projectRoot` is\n // Set, and an empty workspace captured at that instant would overwrite the very record this line\n // Reads. See `markSessionRestored`.\n markSessionRestored(workspace.projectRoot);\n if (session) {\n const opened = await restoreSession(session, {\n ensureSecondPane: () => {\n receivingPane();\n },\n openFile: (path, paneId) => openFileInTab(path, { focus: false, paneId }),\n });\n if (opened > 0) {\n return true;\n }\n }\n await openHomePage();\n // Whether a SESSION was restored, which is not the same as whether anything opened: the\n // `?project=` boot needs to know if it should still run its own inline open, and a home page is\n // Not an answer to that question.\n return false;\n}\n\n// ─── File tree templates ──────────────────────────────────────────────────────\n\nfunction fileTypeIconTpl(name: string, type: string) {\n let tag;\n if (type === \"directory\") {\n tag = projectState?.expanded?.has(name) ? \"sp-icon-folder-open\" : \"sp-icon-folder\";\n } else {\n const ext = name.split(\".\").pop()?.toLowerCase();\n switch (ext) {\n case \"json\": {\n tag = \"sp-icon-file-code\";\n break;\n }\n case \"md\": {\n tag = \"sp-icon-file-txt\";\n break;\n }\n case \"js\":\n case \"ts\": {\n tag = \"sp-icon-file-code\";\n break;\n }\n case \"css\": {\n tag = \"sp-icon-file-code\";\n break;\n }\n default: {\n // Every image extension the media layer knows about, so an uploaded .avif/.ico gets the\n // Same icon as a .png instead of falling through to the generic document glyph.\n tag = ext && isImage(ext) ? \"sp-icon-image\" : \"sp-icon-document\";\n break;\n }\n }\n }\n return fileIconMap[tag] || fileIconMap[\"sp-icon-document\"];\n}\n\n/**\n * Render the file tree template for the left panel.\n *\n * @param {{\n * openProject: () => void;\n * openFileFromTree: (path: string) => void;\n * renderLeftPanel: () => void;\n * }} ctx\n */\nexport function renderFilesTemplate({\n openProject: openProjectFn,\n openFileFromTree: openFileFn,\n renderLeftPanel,\n}: {\n openProject: () => void;\n openFileFromTree: (path: string) => void;\n renderLeftPanel: () => void;\n}) {\n if (!projectState) {\n return html`<div class=\"file-tree-empty\">No project loaded</div>`;\n }\n\n // No project selected in a monorepo — show welcome prompt\n if (!projectState.isSiteProject && projectState.projectRoot === \".\") {\n return html`<div class=\"file-tree-empty\">\n <p style=\"margin:0 0 12px\">Open a project folder to get started.</p>\n <sp-button variant=\"accent\" size=\"s\" @click=${openProjectFn}>Open Project</sp-button>\n </div>`;\n }\n\n return html`\n ${\n projectState.isSiteProject\n ? html`\n <div class=\"project-header\">\n <span class=\"project-name\"\n >${projectState.projectConfig?.name || projectState.name}</span\n >\n </div>\n `\n : nothing\n }\n <div class=\"files-toolbar\">\n <sp-action-group size=\"xs\" compact quiet>\n <sp-action-button\n size=\"xs\"\n label=\"New File\"\n @click=${() => createNewFile(\".\", renderLeftPanel)}\n >\n <sp-icon-add slot=\"icon\"></sp-icon-add>\n </sp-action-button>\n <sp-action-button\n size=\"xs\"\n label=\"Refresh\"\n @click=${async () => {\n requireProjectState().dirs.clear();\n await loadDirectory(\".\");\n for (const dir of requireProjectState().expanded) {\n await loadDirectory(dir);\n }\n renderLeftPanel();\n }}\n >\n <sp-icon-refresh slot=\"icon\"></sp-icon-refresh>\n </sp-action-button>\n </sp-action-group>\n <sp-search\n size=\"s\"\n quiet\n placeholder=\"Filter files…\"\n value=${requireProjectState().searchQuery}\n @input=${(e: Event) => {\n requireProjectState().searchQuery = (e.target as HTMLInputElement).value;\n renderLeftPanel();\n }}\n @submit=${(e: Event) => e.preventDefault()}\n ></sp-search>\n </div>\n <div\n class=\"file-tree\"\n role=\"tree\"\n aria-label=\"Project files\"\n ${ref((el) => {\n if (el) {\n afterFileTreeRender(el as HTMLElement);\n }\n })}\n >\n ${fileTreeBodyTemplate({ openFileFn, renderLeftPanel })}\n </div>\n `;\n}\n\n// ─── The row model, and the window onto it ───────────────────────────────────\n\n/**\n * One row the Files tree would draw, in display order.\n *\n * An expanded directory contributes its children immediately after its own row, so an index into\n * this array is what \"the row below this one\" MEANS for the keyboard — and it goes on meaning it\n * whether or not the row below happens to be painted.\n */\ninterface FileRow {\n /**\n * Lit's `repeat` key.\n *\n * Keyed, where the recursive form was positional: a windowed list re-uses its DOM nodes for\n * DIFFERENT rows as the window slides, so positional reuse would leave the keyboard focused on an\n * element that has since become another file. The prefix is what keeps a directory's own row and\n * the \"Loading…\" row underneath it apart — they name the same path.\n */\n key: string;\n path: string;\n name: string;\n type: string;\n depth: number;\n expanded: boolean;\n /** A placeholder for a directory whose listing has not arrived yet. Not a `treeitem`. */\n loading: boolean;\n /** 1-based position among this row's siblings (`aria-posinset`). */\n posInSet: number;\n /** How many siblings the row has, itself included (`aria-setsize`). */\n setSize: number;\n /**\n * The declared locale whose directory this row sits under, or absent.\n *\n * A chip, not a filter term: the tree's search still matches `entry.name` alone, because a query\n * that silently also matched a language would make \"why is this file here\" unanswerable from what\n * is on screen.\n */\n locale?: string | undefined;\n}\n\n/**\n * The declared height of one row — `styles/panels.css` `.file-tree-item { block-size: 24px }`.\n *\n * The first paint of a session windows by this constant, because nothing has been laid out yet to\n * measure; {@link fileRowHeight} measures a real row from then on and believes the measurement.\n */\nexport const FILE_ROW_HEIGHT = 24;\n\n/** The rows the tree last built, in display order. */\nlet _fileRows: FileRow[] = [];\n/** The `.file-tree` element, kept between renders so the next one can be windowed. */\nlet _fileList: HTMLElement | null = null;\n/** The scroll watch that repaints the tree as its scroller moves. */\nlet _fileWatch: ListWindowWatch | null = null;\n/** The Navigator repaint, captured per render so the scroll watch never holds a stale one. */\nlet _filesRerender: (() => void) | null = null;\n/** A keyboard jump that had to scroll first, spent by the repaint it provoked. */\nlet _pendingFocusPath: string | null = null;\n\n/** The height one row actually has; the declared constant until a row has been laid out. */\nfunction fileRowHeight(): number {\n return measuredRowHeight(_fileList, \".file-tree-item\", FILE_ROW_HEIGHT);\n}\n\n/**\n * Append one directory's rows, and every expanded child directory's rows after their own row.\n *\n * The listing side effect stays exactly where the recursive template had it: a directory nobody has\n * listed yet is fetched, and a placeholder holds its place until the repaint arrives.\n */\nfunction collectFileRows(\n dirPath: string,\n depth: number,\n rows: FileRow[],\n ctx: { renderLeftPanel: () => void },\n i18n: ResolvedI18n | null,\n): void {\n const entries = requireProjectState().dirs.get(dirPath);\n if (!entries) {\n void loadDirectory(dirPath).then(() => ctx.renderLeftPanel());\n rows.push({\n depth,\n expanded: false,\n key: `loading:${dirPath}`,\n loading: true,\n name: \"Loading…\",\n path: dirPath,\n posInSet: 1,\n setSize: 1,\n type: \"file\",\n });\n return;\n }\n\n const sorted = [...entries].toSorted((a, b) => {\n if (a.type === \"directory\" && b.type !== \"directory\") {\n return -1;\n }\n if (a.type !== \"directory\" && b.type === \"directory\") {\n return 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n const query = requireProjectState().searchQuery.toLowerCase();\n const filtered = query\n ? sorted.filter((e) => e.type === \"directory\" || e.name.toLowerCase().includes(query))\n : sorted;\n\n for (const [index, entry] of filtered.entries()) {\n const isDir = entry.type === \"directory\";\n const isExpanded = isDir && requireProjectState().expanded.has(entry.path);\n rows.push({\n depth,\n expanded: isExpanded,\n key: entry.path,\n loading: false,\n name: entry.name,\n path: entry.path,\n locale: localeOfPath(entry.path, i18n) ?? undefined,\n posInSet: index + 1,\n setSize: filtered.length,\n type: entry.type,\n });\n if (isExpanded) {\n collectFileRows(entry.path, depth + 1, rows, ctx, i18n);\n }\n }\n}\n\n/** The model index of `path`, or -1. A \"Loading…\" row is keyed apart and never matches. */\nfunction fileIndexOfPath(path: string | undefined): number {\n return path === undefined ? -1 : _fileRows.findIndex((row) => !row.loading && row.path === path);\n}\n\n/** The next focusable row from `index`, walking by `step`; -1 at the ends. */\nfunction fileStep(index: number, step: 1 | -1): number {\n for (let i = index + step; i >= 0 && i < _fileRows.length; i += step) {\n if (!_fileRows[i]!.loading) {\n return i;\n }\n }\n return -1;\n}\n\n/**\n * Repaint the tree because its window changed.\n *\n * Deferred to a microtask so a scroll arriving mid-commit cannot re-enter the render producing the\n * rows, and skipped during a drag: `registerFileTreeDnD` holds pragmatic-dnd registrations on the\n * rows it can see, and re-rendering under an active drag would drop every one of them.\n */\nfunction fileWindowChanged(): void {\n if (_fileList?.isConnected !== true || _fileList.querySelector(\".file-tree-item.dragging\")) {\n return;\n }\n queueMicrotask(() => _filesRerender?.());\n}\n\n/**\n * Adopt the rendered tree: remember it, keep it watching whatever scrolls it, and hand the keyboard\n * the row a jump asked for once that row exists.\n *\n * The first paint of a session draws every row, because nothing can be measured before it exists;\n * the watch's opening measurement is what asks for the second, windowed pass. Idempotent by\n * construction — `watchListWindow` hands back the same watch for the same element and scroller — so\n * calling it after every render costs a comparison.\n */\nfunction adoptFileTree(tree: HTMLElement): void {\n _fileList = tree;\n _fileWatch = watchListWindow(_fileWatch, tree, {\n count: () => _fileRows.length,\n onChange: fileWindowChanged,\n rowHeight: fileRowHeight,\n });\n const wanted = _pendingFocusPath;\n if (wanted !== null) {\n // One shot: a focus request that outlived its own repaint is stale, and moving the keyboard\n // Later is worse than never having moved it.\n _pendingFocusPath = null;\n fileRowElement(tree, wanted)?.focus();\n }\n}\n\n/**\n * Adopt the tree once its rows are in the document.\n *\n * Deferred by a microtask on purpose, exactly as the Outline's `afterTreeRender` is: a `ref` on the\n * tree element commits BEFORE the child part holding the rows, so on a first render the callback\n * would otherwise measure an empty tree.\n */\nfunction afterFileTreeRender(tree: HTMLElement): void {\n queueMicrotask(() => adoptFileTree(tree));\n}\n\n/** The rendered row for a path, or null when the window does not currently hold it. */\nfunction fileRowElement(tree: HTMLElement, path: string): HTMLElement | null {\n return tree.querySelector<HTMLElement>(`.file-tree-item[data-path=\"${CSS.escape(path)}\"]`);\n}\n\n/**\n * The tree's rows, windowed, with the scroll height of the rows it left out reserved either side.\n *\n * The two spacers are `aria-hidden` because a `role=\"tree\"` owns `treeitem`s, and an empty div that\n * exists to be 4 000 pixels tall is not one.\n */\nfunction fileTreeBodyTemplate(ctx: {\n openFileFn: (path: string) => void;\n renderLeftPanel: () => void;\n}): TemplateResult {\n // The scroll watch outlives this call and must never repaint through a closure from an earlier\n // One — the Navigator's scheduler is the only thing that knows how to draw this panel.\n _filesRerender = ctx.renderLeftPanel;\n _fileRows = [];\n // Resolved once for the whole tree, not once per row: this function runs on EVERY repaint of the\n // Navigator, and `resolveI18n` canonicalizes every declared tag through `Intl.Locale`.\n const { i18n } = resolveI18n(projectState?.projectConfig ?? {});\n collectFileRows(\".\", 0, _fileRows, ctx, i18n);\n // Windowed against the PREVIOUS render's element, the only one that exists while this template is\n // Being built. There is none on the first paint, and `listWindow` then answers \"all of them\".\n const range = listWindow(_fileList, { count: _fileRows.length, rowHeight: fileRowHeight() });\n // The roving tab stop is decided from the MODEL — the first DRAWN row of a windowed tree is\n // Usually not the first row of the tree — and then clamped INTO the window, because a tab stop\n // That is not in the document is not a tab stop: a tree whose selected row has scrolled away\n // Would otherwise have no tabbable row at all, and Tab would skip the whole panel.\n const wanted = Math.max(0, fileIndexOfPath(requireProjectState().selectedPath ?? undefined));\n const tabStop = Math.min(Math.max(wanted, range.start), Math.max(range.start, range.end - 1));\n const slice = _fileRows\n .slice(range.start, range.end)\n .map((row, offset) => ({ row, tabStop: range.start + offset === tabStop }));\n return html`\n <div style=\"height:${range.padTop}px\" aria-hidden=\"true\"></div>\n ${repeat(\n slice,\n (entry) => entry.row.key,\n (entry) => fileRowTemplate(entry.row, entry.tabStop, ctx),\n )}\n <div style=\"height:${range.padBottom}px\" aria-hidden=\"true\"></div>\n `;\n}\n\n/** One row, drawn. */\nfunction fileRowTemplate(\n row: FileRow,\n tabStop: boolean,\n ctx: { openFileFn: (path: string) => void; renderLeftPanel: () => void },\n): TemplateResult {\n if (row.loading) {\n return html`<div\n class=\"file-tree-item\"\n style=\"padding-left:${8 + row.depth * 16}px;color:var(--fg-dim);font-style:italic\"\n >\n Loading…\n </div>`;\n }\n const isDir = row.type === \"directory\";\n return html`\n <div\n class=${classMap({\n \"file-tree-item\": true,\n selected: requireProjectState().selectedPath === row.path,\n })}\n style=\"padding-left:${8 + row.depth * 16}px\"\n role=\"treeitem\"\n aria-level=${row.depth + 1}\n aria-posinset=${row.posInSet}\n aria-setsize=${row.setSize}\n tabindex=${tabStop ? \"0\" : \"-1\"}\n data-path=${row.path}\n data-type=${row.type}\n aria-expanded=${isDir ? String(row.expanded) : nothing}\n @click=${async (e: MouseEvent) => {\n e.stopPropagation();\n if (isDir) {\n await toggleTreeDirectory(row.path);\n ctx.renderLeftPanel();\n } else {\n ctx.openFileFn(row.path);\n }\n }}\n @contextmenu=${(e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n showFileContextMenu(e, { name: row.name, path: row.path, type: row.type }, ctx);\n }}\n >\n ${\n isDir\n ? html`<span class=\"file-tree-toggle\">${row.expanded ? \"▼\" : \"▶\"}</span>`\n : html`<span class=\"file-tree-toggle empty\"> </span>`\n }\n <span class=\"file-tree-icon\">${fileTypeIconTpl(row.path, row.type)}</span>\n <span class=\"file-tree-name\">${row.name}</span>\n ${\n row.locale === undefined\n ? nothing\n : html`<span class=\"file-tree-locale\">${localeLabel(row.locale)}</span>`\n }\n </div>\n `;\n}\n\n/** Expand or collapse one directory, listing it the first time it is opened. */\nasync function toggleTreeDirectory(path: string): Promise<void> {\n const state = requireProjectState();\n if (state.expanded.has(path)) {\n state.expanded.delete(path);\n return;\n }\n state.expanded.add(path);\n if (!state.dirs.has(path)) {\n await loadDirectory(path);\n }\n}\n\n/** Tree elements this module has already given a keydown listener. */\nconst _keyboardTrees = new WeakSet<HTMLElement>();\n\n/**\n * Give the tree its keyboard, once per tree ELEMENT.\n *\n * The panel's `afterRender` calls this after every repaint and lit re-uses the `.file-tree` element\n * across all of them, so the unguarded `addEventListener` it replaces accumulated one listener per\n * render — after ten repaints a single Down keystroke walked ten rows. The WeakSet still lets a\n * remounted panel's new element bind its own.\n */\nexport function setupTreeKeyboard(tree: HTMLElement) {\n if (_keyboardTrees.has(tree)) {\n return;\n }\n _keyboardTrees.add(tree);\n tree.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n onFileTreeKeydown(e, tree);\n });\n}\n\n/**\n * The tree's keyboard model: ↑↓ walk the rows, → expands, ← collapses, Enter opens.\n *\n * ↑ and ↓ step through the MODEL, not through the rendered rows. A DOM-indexed walk stopped dead at\n * the last row of the window — three rows past the bottom of the viewport, with thousands of files\n * still below it — because there was simply no next element to focus. When the step lands outside\n * the window the scroller moves instead, and the focus follows on the repaint ({@link\n * adoptFileTree}).\n */\nfunction onFileTreeKeydown(e: KeyboardEvent, tree: HTMLElement): void {\n const focused = tree.querySelector(\".file-tree-item:focus\") as HTMLElement | null;\n if (!focused) {\n return;\n }\n let handled = true;\n\n switch (e.key) {\n case \"ArrowDown\":\n case \"ArrowUp\": {\n const from = fileIndexOfPath(focused.dataset.path);\n focusFileRow(tree, fileStep(from, e.key === \"ArrowDown\" ? 1 : -1));\n break;\n }\n case \"ArrowRight\": {\n const path = collapsedDirectoryAt(focused);\n if (path !== null) {\n // The expansion repaints THROUGH the panel. It used to synthesise a click on the focused\n // Row to get a repaint, which ran that row's own toggle a second time and only did the\n // Right thing because the handler's captured `isExpanded` was already stale.\n void toggleTreeDirectory(path).then(() => _filesRerender?.());\n }\n break;\n }\n case \"ArrowLeft\": {\n const path = expandedDirectoryAt(focused);\n if (path !== null) {\n requireProjectState().expanded.delete(path);\n // It used to leave the repaint to \"the caller who sets up keyboard\", and there was no such\n // Caller: ← changed the state and left the children on screen until something else\n // Happened to redraw the panel.\n _filesRerender?.();\n }\n break;\n }\n case \"Enter\": {\n focused.click();\n break;\n }\n default: {\n handled = false;\n }\n }\n if (handled) {\n e.preventDefault();\n }\n}\n\n/** The row's directory path when it is a COLLAPSED directory — what → acts on, and nothing else. */\nfunction collapsedDirectoryAt(row: HTMLElement): string | null {\n const { path, type } = row.dataset;\n return type === \"directory\" && path !== undefined && !requireProjectState().expanded.has(path)\n ? path\n : null;\n}\n\n/** The row's directory path when it is an EXPANDED directory — what ← acts on, and nothing else. */\nfunction expandedDirectoryAt(row: HTMLElement): string | null {\n const { path, type } = row.dataset;\n return type === \"directory\" && path !== undefined && requireProjectState().expanded.has(path)\n ? path\n : null;\n}\n\n/** Move the keyboard to the model row at `index`, bringing it into the window if it is outside. */\nfunction focusFileRow(tree: HTMLElement, index: number): void {\n const row = _fileRows[index];\n if (!row) {\n return;\n }\n const el = fileRowElement(tree, row.path);\n if (el) {\n el.focus();\n return;\n }\n if (revealListRow(_fileList, index, fileRowHeight())) {\n _pendingFocusPath = row.path;\n _filesRerender?.();\n }\n}\n\nlet _fileTreeDndCleanups: (() => void)[] = [];\n\n/** Whether a drag carries OS files (as opposed to a pragmatic in-app drag). */\nexport function isFileDrag(e: DragEvent): boolean {\n return [...(e.dataTransfer?.types ?? [])].includes(\"Files\");\n}\n\n/**\n * Attach external-file drop handling to one tree row. Pragmatic-dnd only sees pragmatic sources, so\n * an OS file drag needs the native listeners; `dir` is where a drop lands (the row itself for a\n * directory, its parent for a file, `.` for the tree background).\n */\nfunction registerFileDropTarget(\n element: HTMLElement,\n dir: string,\n activeClass: string,\n renderLeftPanel: () => void,\n): () => void {\n const onDragOver = (e: DragEvent) => {\n if (!isFileDrag(e)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = \"copy\";\n }\n element.classList.add(activeClass);\n };\n const onDragLeave = () => element.classList.remove(activeClass);\n const onDrop = (e: DragEvent) => {\n element.classList.remove(activeClass);\n const files = e.dataTransfer?.files;\n if (!files?.length) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n void uploadFilesToDir(files, dir, renderLeftPanel);\n };\n element.addEventListener(\"dragover\", onDragOver);\n element.addEventListener(\"dragleave\", onDragLeave);\n element.addEventListener(\"drop\", onDrop);\n return () => {\n element.removeEventListener(\"dragover\", onDragOver);\n element.removeEventListener(\"dragleave\", onDragLeave);\n element.removeEventListener(\"drop\", onDrop);\n };\n}\n\n/**\n * Upload files into `dir` and reveal them: expand the target directory so the new entries are\n * visible. The listing refresh itself runs in the shared post-upload handler.\n */\nexport async function uploadFilesToDir(\n files: FileList | File[],\n dir: string,\n renderLeftPanel: () => void,\n): Promise<void> {\n const uploaded = await uploadAssets([...files], { dir });\n if (uploaded.length === 0) {\n return;\n }\n if (dir !== \".\") {\n requireProjectState().expanded.add(dir);\n }\n renderLeftPanel();\n}\n\n/**\n * Open the OS file picker and upload the choice into `dir` (the tree's \"Upload Files…\" item). The\n * input is created per invocation and discarded after — the tree template is re-rendered often.\n */\nexport function pickAndUploadTo(dir: string, renderLeftPanel: () => void): void {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = true;\n input.accept = UPLOAD_ACCEPT;\n input.addEventListener(\"change\", () => {\n if (input.files?.length) {\n void uploadFilesToDir(input.files, dir, renderLeftPanel);\n }\n });\n input.click();\n}\n\n/**\n * Register drag-and-drop on file tree items. Called after each file tree render.\n *\n * @param {{ renderLeftPanel: () => void }} ctx\n */\nexport function registerFileTreeDnD({ renderLeftPanel }: { renderLeftPanel: () => void }) {\n // Clean up previous registrations\n for (const fn of _fileTreeDndCleanups) {\n fn();\n }\n _fileTreeDndCleanups = [];\n\n requestAnimationFrame(() => {\n const tree = document.querySelector(\".file-tree\") as HTMLElement | null;\n if (!tree) {\n return;\n }\n\n const items = tree.querySelectorAll(\".file-tree-item\") as NodeListOf<HTMLElement>;\n\n for (const row of items) {\n const { path } = row.dataset;\n const { type } = row.dataset;\n if (!path) {\n continue;\n }\n\n const cleanups = [\n draggable({\n element: row,\n getInitialData() {\n return { entryType: type, path, type: \"file-tree\" };\n },\n onDragStart() {\n row.classList.add(\"dragging\");\n },\n onDrop() {\n row.classList.remove(\"dragging\");\n },\n }),\n // Files dropped from the OS land in the row's own directory; a file row targets its parent\n // So dropping next to a sibling puts the upload beside it.\n registerFileDropTarget(\n row,\n type === \"directory\" ? path : parentDir(path),\n \"drag-over\",\n renderLeftPanel,\n ),\n ];\n\n if (type === \"directory\") {\n cleanups.push(\n dropTargetForElements({\n canDrop({ source }) {\n if (source.data.type !== \"file-tree\") {\n return false;\n }\n const srcPath = source.data.path as string;\n if (srcPath === path) {\n return false;\n }\n if (srcPath.startsWith(`${path}/`)) {\n return false;\n }\n const srcParent = parentDir(srcPath);\n if (srcParent === path) {\n return false;\n }\n return true;\n },\n element: row,\n getData() {\n return { targetDir: path, type: \"file-tree-target\" };\n },\n onDrag() {\n if (!row.classList.contains(\"drag-over\")) {\n row.classList.add(\"drag-over\");\n }\n },\n onDragEnter() {\n row.classList.add(\"drag-over\");\n },\n onDragLeave() {\n row.classList.remove(\"drag-over\");\n },\n onDrop() {\n row.classList.remove(\"drag-over\");\n },\n }),\n );\n }\n\n _fileTreeDndCleanups.push(combine(...cleanups));\n }\n\n // Root-level drop target (move to project root)\n const rootCleanup = dropTargetForElements({\n canDrop({ source }) {\n if (source.data.type !== \"file-tree\") {\n return false;\n }\n const srcPath = source.data.path as string;\n return parentDir(srcPath) !== \".\";\n },\n element: tree,\n getData() {\n return { targetDir: \".\", type: \"file-tree-target\" };\n },\n onDragEnter() {\n tree.classList.add(\"drag-over-root\");\n },\n onDragLeave() {\n tree.classList.remove(\"drag-over-root\");\n },\n onDrop() {\n tree.classList.remove(\"drag-over-root\");\n },\n });\n // The tree background is the project root's drop target for OS files. Row handlers\n // StopPropagation, so a drop on a row never also fires here.\n _fileTreeDndCleanups.push(\n rootCleanup,\n registerFileDropTarget(tree, \".\", \"drag-over-root\", renderLeftPanel),\n );\n\n // Monitor for drop events\n const monitorCleanup = monitorForElements({\n onDrop({ source, location }) {\n const [target] = location.current.dropTargets;\n if (!target) {\n return;\n }\n if (source.data.type !== \"file-tree\") {\n return;\n }\n if (target.data.type !== \"file-tree-target\") {\n return;\n }\n\n const srcPath = source.data.path as string;\n const targetDirPath = target.data.targetDir as string;\n const fileName = srcPath.split(\"/\").pop();\n const newPath = targetDirPath === \".\" ? fileName : `${targetDirPath}/${fileName}`;\n\n if (newPath === srcPath) {\n return;\n }\n\n void moveFileEntry(srcPath, newPath!, renderLeftPanel);\n },\n });\n _fileTreeDndCleanups.push(monitorCleanup);\n });\n}\n\n/**\n * Move a file/directory and update all affected state.\n *\n * @param {string} oldPath\n * @param {string} newPath\n * @param {() => void} renderLeftPanel\n */\nasync function moveFileEntry(oldPath: string, newPath: string, renderLeftPanel: () => void) {\n const platform = getPlatform();\n markLocalMutation(oldPath, newPath);\n try {\n const report = await platform.renameFile(oldPath, newPath);\n\n // Update open tabs referencing the moved path\n for (const [id] of workspace.tabs.entries()) {\n if (id === oldPath) {\n renameTab(oldPath, newPath, newPath);\n } else if (id.startsWith(`${oldPath}/`)) {\n const newTabPath = newPath + id.slice(oldPath.length);\n renameTab(id, newTabPath, newTabPath);\n }\n }\n\n // Refresh affected directories\n const oldParent = parentDir(oldPath);\n const newParent = parentDir(newPath);\n await loadDirectory(oldParent);\n if (newParent !== oldParent) {\n await loadDirectory(newParent);\n }\n\n // Auto-expand target directory\n if (newParent !== \".\") {\n requireProjectState().expanded.add(newParent);\n }\n\n reloadRewrittenTabs(report, newPath);\n renderLeftPanel();\n notify.success(`Moved to ${newPath}`);\n } catch (error) {\n notify.error(`Could not move ${oldPath}.`, {\n detail: errorMessage(error),\n path: oldPath,\n source: \"Files\",\n });\n }\n}\n\n/** @param {string} path @returns {string} */\nfunction parentDir(path: string) {\n const normalized = path.replaceAll(\"\\\\\", \"/\");\n const lastSlash = normalized.lastIndexOf(\"/\");\n return lastSlash === -1 ? \".\" : normalized.slice(0, lastSlash);\n}\n\n// ─── Context menu ─────────────────────────────────────────────────────────────\n\nlet _fileCtxHandle: ReturnType<typeof renderPopover> | null = null;\n\nfunction dismissFileContextMenu() {\n if (_fileCtxHandle) {\n _fileCtxHandle.dismiss();\n _fileCtxHandle = null;\n }\n}\n\n/** One row of the file menu. The divider is the em-dash label; `disabled` rows explain themselves. */\ninterface FileMenuItem {\n label: string;\n action?: () => void;\n danger?: boolean;\n disabled?: boolean;\n /** The `requires` sentence, printed under a disabled row. */\n reason?: string;\n}\n\n/**\n * A file row addresses ONE thing, and this is everything it can say about it — keyed by the\n * ARGUMENT NAME that asks for it.\n *\n * `menus: [\"context/file\"]` names a placement, and a placement nothing renders is the same defect\n * as a command nothing registers, one layer down: `content.openEntry` declared this menu and the\n * tree drew a hand-built list beside it, so the row simply never existed. Rendering the placement\n * is the fix, and it needs the one thing the element menu does not — an argument.\n * `content.openEntry` wants a `path`, `collection.editInGrid` wants a `name`, and only the row\n * knows either.\n *\n * A fact is stated ONLY when it is true of this row, and that is what decides whether a command\n * appears at all: `styles/main.css` is an entry of no collection, so it states no `path`, so \"Open\n * Entry Form\" is not offered on it. A command whose required arguments this row cannot answer is\n * skipped — never rendered into a refusal the author cannot act on.\n */\nfunction fileRowFacts(entry: { path: string; type: string }): Record<string, unknown> {\n const facts: Record<string, unknown> = {};\n if (entry.type === \"directory\") {\n const collection = collectionDirs().find(\n ({ dir }) => entry.path === dir || entry.path.endsWith(`/${dir}`),\n );\n if (collection) {\n facts.name = collection.name;\n }\n } else if (collectionOfPath(entry.path)) {\n facts.path = entry.path;\n }\n return facts;\n}\n\n/**\n * The declared `context/file` commands this row can offer.\n *\n * Everything a row prints comes off the record — its title, its position (`forPlacement` sorts by\n * `group`), whether it is enabled and the sentence saying why not. Nothing here names a command, so\n * a new `context/file` record appears in the tree with no edit to this file.\n */\nfunction placedFileItems(entry: { path: string; type: string }): FileMenuItem[] {\n const registry = activeRegistry();\n if (!registry) {\n return [];\n }\n const facts = fileRowFacts(entry);\n const items: FileMenuItem[] = [];\n for (const command of registry.forPlacement(\"context/file\")) {\n const schema = command.args as\n | { properties?: Record<string, unknown>; required?: readonly string[] }\n | undefined;\n if (!(schema?.required ?? []).every((key) => key in facts)) {\n continue;\n }\n const args: Record<string, unknown> = {};\n for (const key of Object.keys(schema?.properties ?? {})) {\n if (key in facts) {\n args[key] = facts[key];\n }\n }\n const reason = registry.disabledReason(command.id);\n items.push(\n reason === undefined\n ? {\n action: () => {\n void registry.run(command.id, args);\n },\n label: command.title,\n }\n : { disabled: true, label: command.title, reason },\n );\n }\n return items;\n}\n\nfunction showFileContextMenu(\n e: MouseEvent,\n entry: { name: string; path: string; type: string },\n ctx: { openFileFn: (path: string) => void; renderLeftPanel: () => void },\n) {\n e.preventDefault();\n dismissFileContextMenu();\n const isDir = entry.type === \"directory\";\n\n const items: FileMenuItem[] = [];\n\n if (!isDir) {\n items.push({ action: () => ctx.openFileFn(entry.path), label: \"Open\" });\n }\n if (isDir) {\n items.push(\n {\n action: () => createNewFile(entry.path, ctx.renderLeftPanel),\n label: \"New File\\u2026\",\n },\n {\n action: () => pickAndUploadTo(entry.path, ctx.renderLeftPanel),\n label: \"Upload Files\\u2026\",\n },\n );\n if (entry.path === \"pages\" || entry.path.endsWith(\"/pages\")) {\n // The one hand-built row left: no command declares \"open the pages grid\". `grid-open.ts`'s\n // `collection.editInGrid` has no pages sibling, so there is nothing here to render yet.\n items.push({\n action: () => {\n openPagesGrid();\n },\n label: \"Edit Pages in Grid\",\n });\n }\n }\n items.push(\n // The declared rows sit between what the TREE does to a file (open it, create in it, upload to\n // It) and what it does to the file's existence (rename, delete).\n ...placedFileItems(entry),\n { label: \"\\u2014\" },\n {\n action: () => renameFile(entry, ctx.renderLeftPanel),\n label: \"Rename\\u2026\",\n },\n {\n action: () => deleteFile(entry, ctx.renderLeftPanel),\n danger: true,\n label: \"Delete\",\n },\n );\n\n let x = e.clientX;\n let y = e.clientY;\n\n _fileCtxHandle = renderPopover(\n html`<sp-popover\n open\n style=\"position:fixed;z-index:10000;left:${x}px;top:${y}px\"\n ${ref((el) => {\n if (!el) {\n return;\n }\n requestAnimationFrame(() => {\n const popover = el as HTMLElement;\n const menuRect = rectOf(popover);\n if (x + menuRect.width > window.innerWidth) {\n x = window.innerWidth - menuRect.width - 4;\n }\n if (y + menuRect.height > window.innerHeight) {\n y = window.innerHeight - menuRect.height - 4;\n }\n popover.style.left = `${x}px`;\n popover.style.top = `${y}px`;\n });\n })}\n >\n <sp-menu>\n ${items.map((item) =>\n item.label === \"\\u2014\"\n ? html`<sp-menu-divider></sp-menu-divider>`\n : html`<sp-menu-item\n style=${item.danger ? \"color: var(--danger)\" : \"\"}\n ?disabled=${item.disabled === true}\n aria-disabled=${item.disabled === true ? \"true\" : \"false\"}\n @click=${() => {\n if (item.disabled === true) {\n return;\n }\n dismissFileContextMenu();\n void item.action?.();\n }}\n >${item.label}${\n // A disabled row says what it needs, the same sentence the palette and the agent\n // Print — `requires`, off the record, never re-worded here.\n item.reason ? html`<span slot=\"description\">Needs ${item.reason}</span>` : nothing\n }</sp-menu-item\n >`,\n )}\n </sp-menu>\n </sp-popover>`,\n {\n dismissOnOutsideClick: true,\n onDismiss: () => {\n _fileCtxHandle = null;\n },\n },\n );\n}\n\n// ─── File CRUD ────────────────────────────────────────────────────────────────\n\n/** The default body for a path no format claims — a document, since that is what Jx authors. */\nconst BLANK_DOCUMENT = JSON.stringify(\n { children: [{ children: [], tagName: \"p\" }], tagName: \"div\" },\n null,\n 2,\n);\n\n/**\n * One creation, named.\n *\n * `dir` is required and has no default. That is the whole point of the type: the Library used to\n * derive its destination from whichever CATEGORY filter happened to be active — and \"All\" derived\n * nothing, so a new page landed wherever the writer's fallback pointed. A creation flow that cannot\n * say where the file is going has no business asking for its name.\n */\nexport interface NewFileRequest {\n /** Destination directory, project-relative. `\".\"` is the project root. */\n dir: string;\n /** Dialog title — \"New File\", \"New Page\", \"New Post\". Defaults to \"New File\". */\n title?: string;\n /** Pre-filled value; its stem is selected so typing replaces the name and keeps the extension. */\n suggestedName?: string;\n /**\n * When set, the field asks for a DISPLAY NAME and this extension is appended to its slug — \"My\n * First Post\" becomes `my-first-post.md`. When absent, the field asks for a file name and takes\n * it verbatim, which is what the Files tree has always done.\n */\n ext?: string;\n /** Body to write. Defaults to the resolved format's `newFileTemplate`. */\n content?: string;\n /** Who is creating, for the Problem's `source` line. Defaults to \"Files\". */\n source?: string;\n}\n\n/**\n * Create one file, from the one flow both the Files tree and the Library use.\n *\n * Two behaviours the callers used to disagree about, settled here:\n *\n * - **A name that is already taken is refused in the FIELD**, not discovered afterwards. Both\n * predecessors called `writeFile` straight onto the composed path, so creating `about.md` in a\n * directory that had one silently replaced it — with no undo, because the file was never open.\n * The destination is listed once before the prompt so `validate` can say so while it can still be\n * fixed.\n * - **A failure is a Problem carrying the path**, not a toast that scrolls away, since the thing the\n * author must do next is about that path.\n *\n * @returns The created path, or `null` when the author cancelled or the write failed.\n */\nexport async function createFileIn(request: NewFileRequest): Promise<string | null> {\n const { dir, ext, source = \"Files\" } = request;\n await loadFormats();\n\n // One listing, before the field opens: the names it must refuse are known while typing.\n let taken = new Set<string>();\n try {\n const listing = await getPlatform().listDirectory(dir);\n taken = new Set(listing.map((entry) => entry.name));\n } catch {\n // A directory that cannot be listed is usually one that does not exist yet — the write below\n // Is the authority on whether that is a problem, and it reports with the real reason.\n }\n\n const fileNameFor = (input: string) =>\n ext === undefined\n ? input.trim()\n : `${input\n .trim()\n .toLowerCase()\n .replaceAll(/\\s+/g, \"-\")\n .replaceAll(/[^a-z\\d-]/g, \"\")}${ext}`;\n\n const entered = await showPromptDialog(request.title ?? \"New File\", {\n confirmLabel: \"Create\",\n message: dir === \".\" ? \"Creating in the project root.\" : `Creating in ${dir}/`,\n ...(request.suggestedName === undefined ? {} : { placeholder: request.suggestedName }),\n select: \"stem\",\n validate: (value) => {\n if (!value.trim()) {\n return ext === undefined ? \"Enter a file name.\" : \"Enter a name.\";\n }\n const candidate = fileNameFor(value);\n if (!candidate || candidate === ext) {\n return \"Enter at least one letter or number.\";\n }\n return taken.has(candidate) ? `${candidate} already exists in ${dir}/.` : \"\";\n },\n value: request.suggestedName ?? \"untitled.json\",\n });\n if (!entered) {\n return null;\n }\n\n const fileName = fileNameFor(entered);\n const path = dir === \".\" ? fileName : `${dir}/${fileName}`;\n markLocalMutation(path);\n const format = formatForPath(fileName);\n const content =\n request.content ?? format?.studio?.newFileTemplate ?? (format ? \"\" : BLANK_DOCUMENT);\n try {\n await getPlatform().writeFile(path, content);\n await loadDirectory(dir);\n notify.success(`Created ${path}`);\n return path;\n } catch (error) {\n notify.error(`Could not create ${path}.`, {\n detail: errorMessage(error),\n path,\n source,\n });\n return null;\n }\n}\n\nasync function createNewFile(dirPath: string, renderLeftPanel: () => void) {\n const created = await createFileIn({ dir: dirPath, suggestedName: \"untitled.json\" });\n if (created !== null) {\n renderLeftPanel();\n }\n}\n\n/**\n * The rename dialog, carrying what moves with the file.\n *\n * `renamePromptMessage` is awaited before the field opens, so the count is on screen when the name\n * is typed rather than after it is confirmed — the refactor pass is about to rewrite every one of\n * those references, and silently doing that much work was the previous behaviour.\n *\n * @param {string} currentName @param {string} path @returns {Promise<string | null>}\n */\nasync function showRenameFileDialog(currentName: string, path: string): Promise<string | null> {\n const message = await renamePromptMessage(path);\n return showPromptDialog(\"Rename\", {\n confirmLabel: \"Rename\",\n ...(message === undefined ? {} : { message }),\n select: \"stem\",\n validate: (v) => (v.trim() ? \"\" : \"Enter a file name.\"),\n value: currentName,\n });\n}\n\n/** Build the status-bar message for a rename, summarising any reference/tag rewrites. */\nfunction renameStatus(newName: string, report: RenameResult): string {\n const refs = report.references;\n const tagNote = report.tag ? `; tag → <${report.tag.to}> (${report.tag.refsUpdated})` : \"\";\n if (refs && refs.refsUpdated > 0) {\n return `Renamed to ${newName}; updated ${refs.refsUpdated} reference(s) in ${refs.filesChanged} file(s)${tagNote}`;\n }\n if (tagNote) {\n return `Renamed to ${newName}${tagNote}`;\n }\n return `Renamed to ${newName}`;\n}\n\n/** Reload any open tabs whose references the refactor rewrote (so the editor shows new paths). */\nfunction reloadRewrittenTabs(report: RenameResult, skipPath: string): void {\n for (const f of report.references?.files ?? []) {\n if (f.path !== skipPath && workspace.tabs.has(f.path)) {\n void reloadFileInTab(f.path);\n }\n }\n}\n\nasync function renameFile(\n entry: { name: string; path: string; type: string },\n renderLeftPanel: () => void,\n) {\n const newName = await showRenameFileDialog(entry.name, entry.path);\n if (!newName || newName === entry.name) {\n return;\n }\n const entryPath = entry.path.replaceAll(\"\\\\\", \"/\");\n const parentDirPath = entryPath.includes(\"/\")\n ? entryPath.slice(0, entryPath.lastIndexOf(\"/\"))\n : \".\";\n const newPath = parentDirPath === \".\" ? newName : `${parentDirPath}/${newName}`;\n markLocalMutation(entry.path, newPath);\n try {\n const platform = getPlatform();\n const report = await platform.renameFile(entry.path, newPath);\n // The refactor pass just rewrote references project-wide, and `markLocalMutation` suppresses\n // The watcher echo that would otherwise say so — hence the explicit drop.\n invalidateUsages();\n await loadDirectory(parentDirPath);\n if (requireProjectState().selectedPath === entry.path) {\n requireProjectState().selectedPath = newPath;\n }\n if (workspace.tabs.has(entry.path)) {\n renameTab(entry.path, newPath, newPath);\n }\n reloadRewrittenTabs(report, newPath);\n renderLeftPanel();\n notify.success(renameStatus(newName, report));\n } catch (error) {\n notify.error(`Could not rename ${entry.name}.`, {\n detail: errorMessage(error),\n path: entry.path,\n source: \"Files\",\n });\n }\n}\n\nasync function deleteFile(\n entry: { name: string; path: string; type: string },\n renderLeftPanel: () => void,\n) {\n const confirmed = await confirmFileDelete(entry);\n if (!confirmed) {\n return;\n }\n try {\n const platform = getPlatform();\n markLocalMutation(entry.path);\n await platform.deleteFile(entry.path);\n invalidateUsages();\n const delPath = entry.path.replaceAll(\"\\\\\", \"/\");\n const parentDirPath = delPath.includes(\"/\") ? delPath.slice(0, delPath.lastIndexOf(\"/\")) : \".\";\n await loadDirectory(parentDirPath);\n if (requireProjectState().selectedPath === entry.path) {\n requireProjectState().selectedPath = null;\n }\n renderLeftPanel();\n notify.success(`Deleted ${entry.name}`);\n } catch (error) {\n notify.error(`Could not delete ${entry.name}.`, {\n detail: errorMessage(error),\n path: entry.path,\n source: \"Files\",\n });\n }\n}\n\n/**\n * What an open can say beyond the path. Every field defaults to today's answer.\n *\n * Deliberately one inline-typed `opts` object rather than a `paneId` parameter:\n * `scripts/check-pane-singletons.ts` rule 4 charges any function whose parameters NAME a pane for\n * reading the focus, one hop in — and {@link openFileInTab} legitimately falls back to the focused\n * pane when nobody names one. {@link openFileInPane} is the named sibling for readers who want the\n * pane in the signature; it is pane-scoped and reads no focus of its own.\n */\nexport interface OpenFileOpts {\n /** Which pane. Defaults to the focused one. */\n paneId?: string;\n /** Open as a disposable preview tab (§4.3) — browsing rather than committing. */\n preview?: boolean;\n /** False leaves the keyboard where it is. Defaults to true. */\n focus?: boolean;\n}\n\n/**\n * Bring an ALREADY-OPEN tab to where the caller asked for it.\n *\n * Three cases, and the third is the one a following pane depends on:\n *\n * | the tab is… | behaviour |\n * | ------------------------------------------------ | ---------------------------------------------- |\n * | in the requested pane (or no pane was requested) | activate it there, honouring `focus` |\n * | elsewhere, and **not** its pane's active tab | move it — one tab is one document in one strip |\n * | elsewhere, and **is** its pane's active tab | **nothing.** You are already looking at it |\n *\n * The third exists because moving it would oscillate: a derivation that re-resolves to the document\n * the author is editing would yank it out of their pane and into the assistant one, and the follow\n * would then re-resolve against whatever landed in its place.\n */\nfunction revealOpenTab(tabId: string, opts: OpenFileOpts): void {\n const wanted = opts.paneId;\n const holder = paneOfTab(tabId);\n if (wanted !== undefined && holder && holder.id !== wanted) {\n if (holder.activeTabId === tabId) {\n return;\n }\n moveTabToPane(tabId, wanted);\n }\n activateTab(tabId, { focus: opts.focus !== false });\n}\n\n/**\n * Open a file from the tree into a tab. Activates the existing tab if it is already open.\n *\n * @param {string} path\n * @param {OpenFileOpts} [opts]\n */\nexport async function openFileInTab(path: string, opts: OpenFileOpts = {}) {\n const follows = opts.focus !== false;\n for (const [id, tab] of workspace.tabs.entries()) {\n if (tab.documentPath === path) {\n revealOpenTab(id, opts);\n // The tree's cursor answers \"where is the author\", so a side-open that deliberately left the\n // Keyboard behind must not move it.\n if (follows) {\n requireProjectState().selectedPath = path;\n }\n return;\n }\n }\n\n // CSV files open in the grid editor (source mode remains as the raw-text alternate).\n if (path.toLowerCase().endsWith(\".csv\")) {\n try {\n await openCsvGridTab(path);\n requireProjectState().selectedPath = path;\n trackRecentFile({\n name: path.split(\"/\").pop() || path,\n path,\n root: requireProjectState().projectRoot,\n });\n } catch (error) {\n notify.error(`Could not open ${path}.`, {\n detail: errorMessage(error),\n path,\n source: \"Open File\",\n });\n }\n return;\n }\n\n const platform = getPlatform();\n try {\n const content = await platform.readFile(path);\n if (!content) {\n return;\n }\n\n await loadFormats();\n let document: Record<string, unknown>;\n let frontmatter: Record<string, unknown> | undefined;\n const format = formatForPath(path);\n if (format) {\n const result = await parseSourceForPath(path, content);\n ({ document } = result);\n ({ frontmatter } = result);\n } else if (path.endsWith(\".json\")) {\n document = JSON.parse(content) as Record<string, unknown>;\n } else {\n throw noFormatError(path);\n }\n\n const id = path;\n openTab({\n id,\n documentPath: path,\n document,\n ...(frontmatter != null && { frontmatter }),\n sourceFormat: format?.name ?? null,\n ...(opts.paneId !== undefined && { paneId: opts.paneId }),\n ...(opts.preview === true && { preview: true }),\n ...(opts.focus === false && { focus: false }),\n });\n if (follows) {\n requireProjectState().selectedPath = path;\n }\n trackRecentFile({\n name: path.split(\"/\").pop() || path,\n path,\n root: requireProjectState().projectRoot,\n });\n } catch (error) {\n notify.error(`Could not open ${path}.`, {\n detail: errorMessage(error),\n path,\n source: \"Open File\",\n });\n }\n}\n\n/**\n * Open a file into a NAMED pane, browsing rather than committing, leaving the keyboard behind.\n *\n * The same body as {@link openFileInTab} with the three options a side-open always wants, given a\n * signature that says which pane in the first parameter. It is what \"open it beside this\" means\n * everywhere it is asked for — drilling into a component, following a layout, `pane.compareWith`.\n *\n * @param {string} paneId\n * @param {string} path\n */\nexport async function openFileInPane(paneId: string, path: string): Promise<void> {\n await openFileInTab(path, { focus: false, paneId, preview: true });\n}\n\n/**\n * Reload an already-open tab from disk without changing the active tab. Used to refresh after AI\n * assistant writes to a file.\n *\n * @param {string} path\n */\n/** Reload an open tab from disk when an external change arrives — but only if it is not dirty. */\nexport function reloadCleanTab(path: string): void {\n // Co-edited docs never reload from disk: the shared Y.Doc is ahead of the provider's write-back\n // (Which is what produced this event), and genuine external changes arrive as a collab reset.\n if (isCollabPath(path)) {\n return;\n }\n for (const [, tab] of workspace.tabs.entries()) {\n if (tab.documentPath === path && !tab.doc.dirty) {\n void reloadFileInTab(path);\n return;\n }\n }\n}\n\nexport async function reloadFileInTab(path: string) {\n for (const [, tab] of workspace.tabs.entries()) {\n if (tab.documentPath === path) {\n const platform = getPlatform();\n try {\n const content = await platform.readFile(path);\n if (!content) {\n return;\n }\n await loadFormats();\n if (formatForPath(path)) {\n const { document, frontmatter } = await parseSourceForPath(path, content);\n tab.doc.document = document;\n tab.doc.content.frontmatter = frontmatter;\n } else if (path.endsWith(\".json\")) {\n tab.doc.document = JSON.parse(content) as JxMutableNode;\n }\n tab.doc.dirty = false;\n } catch (error) {\n // A file that changed on disk and cannot be re-read leaves the open tab showing the OLD\n // Document with no indication that it is stale — the most expensive silence in this file.\n notify.error(`Could not reload ${path} after it changed on disk.`, {\n detail: errorMessage(error),\n key: `reload:${path}`,\n path,\n source: \"Files\",\n });\n }\n return;\n }\n }\n}\n\n/**\n * Contribute the Files panel.\n *\n * `level: \"project\"` because it WRITES project files — create, rename, delete, move. It reads the\n * focused document only to highlight a row, and principle 3 files a surface by what it writes.\n */\nexport function registerFilesPanel(): void {\n registerPanel({\n id: \"files\",\n title: \"Files\",\n level: \"project\",\n dock: \"navigator\",\n icon: \"sp-icon-folder\",\n render: (ctx) => ctx.deps.renderFilesTemplate(),\n afterRender: (ctx, host) => {\n const tree = host.querySelector(\".file-tree\") as HTMLElement | null;\n if (tree) {\n ctx.deps.setupTreeKeyboard(tree);\n }\n ctx.deps.registerFileTreeDnD({ renderLeftPanel: ctx.rerender });\n },\n });\n}\n"
|
|
9
|
-
],
|
|
10
|
-
"mappings": "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",
|
|
11
|
-
"debugId": "FB716457707AF69D64756E2164756E21",
|
|
12
|
-
"names": []
|
|
13
|
-
}
|