@3sln/trove 0.0.2
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/README.md +1227 -0
- package/package.json +75 -0
- package/packages/core/src/collections/index.js +249 -0
- package/packages/core/src/errors.js +186 -0
- package/packages/core/src/identity/discovery.js +210 -0
- package/packages/core/src/identity/index.js +188 -0
- package/packages/core/src/identity/jwt.js +199 -0
- package/packages/core/src/index.js +104 -0
- package/packages/core/src/indexers/contribution.js +115 -0
- package/packages/core/src/indexers/registry.js +162 -0
- package/packages/core/src/indexing.js +340 -0
- package/packages/core/src/issues.js +150 -0
- package/packages/core/src/kv.js +0 -0
- package/packages/core/src/links.js +141 -0
- package/packages/core/src/metadata/cursor.js +73 -0
- package/packages/core/src/metadata/interface.js +244 -0
- package/packages/core/src/metadata/memory.js +270 -0
- package/packages/core/src/metadata/sqlite.js +412 -0
- package/packages/core/src/notifications/index.js +139 -0
- package/packages/core/src/notifications/webpush.js +217 -0
- package/packages/core/src/plugins/contributions.js +177 -0
- package/packages/core/src/plugins/identity.js +98 -0
- package/packages/core/src/plugins/index.js +225 -0
- package/packages/core/src/plugins/indexers.js +142 -0
- package/packages/core/src/plugins/installStore.js +134 -0
- package/packages/core/src/plugins/package.js +102 -0
- package/packages/core/src/plugins/packageStore.js +61 -0
- package/packages/core/src/plugins/runtime.js +101 -0
- package/packages/core/src/plugins/sql.js +52 -0
- package/packages/core/src/retry.js +74 -0
- package/packages/core/src/scan.js +302 -0
- package/packages/core/src/search/embeddings.js +128 -0
- package/packages/core/src/search/index.js +200 -0
- package/packages/core/src/search/keywordStore.js +107 -0
- package/packages/core/src/search/sqliteStores.js +455 -0
- package/packages/core/src/search/tagMatch.js +59 -0
- package/packages/core/src/search/transformer.js +195 -0
- package/packages/core/src/search/vectorStore.js +274 -0
- package/packages/core/src/search/vectorize.js +249 -0
- package/packages/core/src/sidecar/document.js +213 -0
- package/packages/core/src/sidecar/index.js +174 -0
- package/packages/core/src/sidecar/manager.js +239 -0
- package/packages/core/src/sidecar/store.js +46 -0
- package/packages/core/src/signedUrls.js +170 -0
- package/packages/core/src/sqlite-d1.js +162 -0
- package/packages/core/src/sqlite-driver.js +42 -0
- package/packages/core/src/sqlite.js +162 -0
- package/packages/core/src/storage/filesystem.js +283 -0
- package/packages/core/src/storage/interface.js +222 -0
- package/packages/core/src/storage/memory.js +113 -0
- package/packages/core/src/storage/prefixed.js +75 -0
- package/packages/core/src/storage/s3.js +316 -0
- package/packages/core/src/storage/s3sigv4.js +185 -0
- package/packages/core/src/tasks.js +228 -0
- package/packages/core/src/uploads.js +386 -0
- package/packages/core/src/util.js +125 -0
- package/packages/core/src/vfs.js +666 -0
- package/packages/plugin-sdk/src/browser.js +316 -0
- package/packages/plugin-sdk/src/index.js +32 -0
- package/packages/plugin-sdk/src/protocol.js +59 -0
- package/packages/plugin-sdk/src/rpc.js +95 -0
- package/packages/server/src/adapters/bun.js +78 -0
- package/packages/server/src/adapters/node.js +115 -0
- package/packages/server/src/adapters/staticAssets.js +123 -0
- package/packages/server/src/adapters/webDist.js +70 -0
- package/packages/server/src/adapters/worker-tasks.js +206 -0
- package/packages/server/src/adapters/worker.js +159 -0
- package/packages/server/src/cachePolicy.js +34 -0
- package/packages/server/src/engine/README.md +88 -0
- package/packages/server/src/engine/actions/scanCollection.js +114 -0
- package/packages/server/src/engine/index.js +95 -0
- package/packages/server/src/engine/lazy.js +25 -0
- package/packages/server/src/engine/providers/access.js +363 -0
- package/packages/server/src/engine/providers/core.js +405 -0
- package/packages/server/src/engine/providers/scan.js +67 -0
- package/packages/server/src/index.js +698 -0
- package/packages/server/src/manifest.js +98 -0
- package/packages/server/src/mcp/auth.js +40 -0
- package/packages/server/src/mcp/index.js +213 -0
- package/packages/server/src/mcp/protocol.js +181 -0
- package/packages/server/src/mcp/tools.js +351 -0
- package/packages/server/src/router.js +229 -0
- package/packages/server/src/routes.js +1066 -0
- package/packages/server/src/scope.js +43 -0
- package/packages/web/dist/assets/chunk-4xqbzebh.js +5 -0
- package/packages/web/dist/assets/chunk-4xqbzebh.js.map +9 -0
- package/packages/web/dist/assets/chunk-h05bxfbs.js +5 -0
- package/packages/web/dist/assets/chunk-h05bxfbs.js.map +10 -0
- package/packages/web/dist/assets/main-4cxs7prw.js +356 -0
- package/packages/web/dist/assets/main-4cxs7prw.js.map +103 -0
- package/packages/web/dist/assets/styles-kcx1x337.css +1 -0
- package/packages/web/dist/icon.svg +11 -0
- package/packages/web/dist/index.html +16 -0
- package/packages/web/dist/sql-wasm.wasm +0 -0
- package/packages/web/dist/sw.js +186 -0
- package/packages/web/src/bl/actions.js +410 -0
- package/packages/web/src/bl/activity.js +306 -0
- package/packages/web/src/bl/commands.js +274 -0
- package/packages/web/src/bl/fileType.js +49 -0
- package/packages/web/src/bl/index.js +70 -0
- package/packages/web/src/bl/links.js +54 -0
- package/packages/web/src/bl/offline.js +268 -0
- package/packages/web/src/bl/openers.js +71 -0
- package/packages/web/src/bl/pluginInstall.js +59 -0
- package/packages/web/src/bl/services.js +143 -0
- package/packages/web/src/bl/social.js +234 -0
- package/packages/web/src/bl/tagQuery.js +44 -0
- package/packages/web/src/main.js +10 -0
- package/packages/web/src/platform/api.js +529 -0
- package/packages/web/src/platform/commands.js +89 -0
- package/packages/web/src/platform/context.js +77 -0
- package/packages/web/src/platform/contributions.js +156 -0
- package/packages/web/src/platform/index.js +150 -0
- package/packages/web/src/platform/keybindings.js +199 -0
- package/packages/web/src/platform/mediaUrls.js +137 -0
- package/packages/web/src/platform/navigation.js +131 -0
- package/packages/web/src/platform/notifications.js +50 -0
- package/packages/web/src/platform/overlay.js +81 -0
- package/packages/web/src/platform/pluginClientDb.js +132 -0
- package/packages/web/src/platform/pluginDock.js +141 -0
- package/packages/web/src/platform/pluginFrames.js +194 -0
- package/packages/web/src/platform/pluginHost.js +648 -0
- package/packages/web/src/platform/pluginMedia.js +62 -0
- package/packages/web/src/platform/pluginModules.js +90 -0
- package/packages/web/src/platform/pluginNet.js +71 -0
- package/packages/web/src/platform/pluginPackage.js +247 -0
- package/packages/web/src/platform/pluginRpc.js +377 -0
- package/packages/web/src/platform/pluginSigning.js +168 -0
- package/packages/web/src/platform/pluginStore.js +67 -0
- package/packages/web/src/platform/settings.js +101 -0
- package/packages/web/src/platform/spatialNav.js +286 -0
- package/packages/web/src/platform/viewport.js +123 -0
- package/packages/web/src/platform/voice.js +133 -0
- package/packages/web/src/platform/voiceSearch.js +155 -0
- package/packages/web/src/platform/whenclause.js +162 -0
- package/packages/web/src/platform/workbench.js +156 -0
- package/packages/web/src/runtime.js +73 -0
- package/packages/web/src/styles.css +1382 -0
- package/packages/web/src/ui/components/activityBar.js +35 -0
- package/packages/web/src/ui/components/activityPanel.js +132 -0
- package/packages/web/src/ui/components/commandPalette.js +154 -0
- package/packages/web/src/ui/components/editorArea.js +75 -0
- package/packages/web/src/ui/components/launcher.js +392 -0
- package/packages/web/src/ui/components/openers/index.js +212 -0
- package/packages/web/src/ui/components/openers/markdown.js +222 -0
- package/packages/web/src/ui/components/overlays.js +255 -0
- package/packages/web/src/ui/components/phoneChrome.js +188 -0
- package/packages/web/src/ui/components/pluginReview.js +151 -0
- package/packages/web/src/ui/components/pluginsView.js +120 -0
- package/packages/web/src/ui/components/settingsView.js +258 -0
- package/packages/web/src/ui/components/social.js +290 -0
- package/packages/web/src/ui/components/statusBar.js +198 -0
- package/packages/web/src/ui/components/views/grid.js +115 -0
- package/packages/web/src/ui/components/views/index.js +155 -0
- package/packages/web/src/ui/components/views/list.js +50 -0
- package/packages/web/src/ui/components/views/parts.js +58 -0
- package/packages/web/src/ui/compositions/workbench.js +125 -0
- package/packages/web/src/ui/format.js +33 -0
- package/packages/web/src/ui/icon.js +81 -0
- package/packages/web/src/ui/media.js +114 -0
- package/packages/web/src/ui/sanitize.js +86 -0
- package/packages/web/src/workbench.js +205 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../node_modules/@3sln/dodo/src/vdom.js", "../../../node_modules/@3sln/dodo/src/html.js", "../../../node_modules/@3sln/dodo/src/scheduler.js", "../../../node_modules/@3sln/dodo/index.js", "../../../node_modules/@3sln/dodo/src/settings.js", "../../../node_modules/@3sln/dodo/src/reactive.js", "../../../node_modules/@3sln/dodo/reactive.js", "../src/runtime.js", "../../core/src/errors.js", "../../core/src/util.js", "../../core/src/plugins/identity.js", "../../core/src/plugins/contributions.js", "../src/platform/contributions.js", "../src/platform/whenclause.js", "../src/platform/context.js", "../src/platform/commands.js", "../src/platform/keybindings.js", "../src/platform/settings.js", "../src/platform/notifications.js", "../../core/src/retry.js", "../src/platform/api.js", "../../../node_modules/fflate/esm/browser.js", "../src/platform/pluginStore.js", "../src/platform/pluginClientDb.js", "../src/platform/pluginSigning.js", "../src/platform/pluginNet.js", "../src/platform/pluginPackage.js", "../src/platform/pluginMedia.js", "../src/platform/pluginDock.js", "../../plugin-sdk/src/rpc.js", "../../plugin-sdk/src/protocol.js", "../../../node_modules/es-module-lexer/dist/lexer.js", "../src/platform/pluginModules.js", "../src/platform/pluginFrames.js", "../../core/src/plugins/sql.js", "../src/platform/pluginRpc.js", "../../core/src/plugins/package.js", "../src/platform/pluginHost.js", "../src/platform/overlay.js", "../src/platform/navigation.js", "../src/platform/workbench.js", "../src/platform/viewport.js", "../src/platform/spatialNav.js", "../src/platform/voice.js", "../src/platform/voiceSearch.js", "../src/platform/mediaUrls.js", "../src/platform/index.js", "../../../node_modules/@3sln/ngin/src/internal.js", "../../../node_modules/@3sln/ngin/src/providers.js", "../../../node_modules/@3sln/ngin/src/actions.js", "../../../node_modules/@3sln/ngin/src/queries.js", "../../../node_modules/@3sln/ngin/src/engine.js", "../src/bl/services.js", "../src/bl/social.js", "../../core/src/search/embeddings.js", "../../core/src/links.js", "../../core/src/indexers/registry.js", "../../core/src/search/keywordStore.js", "../src/bl/fileType.js", "../src/bl/offline.js", "../src/bl/activity.js", "../../core/src/search/tagMatch.js", "../src/bl/tagQuery.js", "../src/bl/openers.js", "../src/bl/actions.js", "../src/bl/pluginInstall.js", "../src/bl/commands.js", "../src/bl/index.js", "../src/ui/icon.js", "../src/ui/format.js", "../src/ui/components/social.js", "../src/ui/components/activityBar.js", "../src/ui/sanitize.js", "../src/ui/components/statusBar.js", "../src/ui/components/views/parts.js", "../src/ui/components/views/list.js", "../src/ui/media.js", "../src/ui/components/views/grid.js", "../src/ui/components/views/index.js", "../src/ui/components/launcher.js", "../src/ui/components/settingsView.js", "../src/ui/components/pluginsView.js", "../src/bl/links.js", "../src/ui/components/openers/markdown.js", "../src/ui/components/openers/index.js", "../src/ui/components/editorArea.js", "../src/ui/components/commandPalette.js", "../src/ui/components/pluginReview.js", "../src/ui/components/overlays.js", "../src/ui/components/activityPanel.js", "../src/ui/components/phoneChrome.js", "../src/ui/compositions/workbench.js", "../src/workbench.js", "../src/main.js"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"const ELEMENT_NODE = Symbol('ELEMENT_NODE');\nconst ALIAS_NODE = Symbol('ALIAS_NODE');\nconst SPECIAL_NODE = Symbol('SPECIAL_NODE');\nconst OPAQUE_NODE = Symbol('OPAQUE_NODE');\nconst NODE_STATE = Symbol('NODE_STATE');\nconst HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\n\n// Property names that must never be written through to a DOM node: assigning\n// `__proto__` swaps the node's prototype, which corrupts the element for every\n// later operation. Relevant whenever props are built from untrusted data.\nconst UNSAFE_PROP_NAMES = new Set(['__proto__', 'constructor', 'prototype']);\n\nconst hasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k);\n\n// HTML tag names come back from the DOM upper cased while SVG/MathML keep their\n// authored case. A plain case-insensitive compare covers both, and unlike\n// `localeCompare` it neither builds a collator nor depends on the host locale.\nfunction sameTagName(nodeName, tag) {\n return typeof tag === 'string' && nodeName.toLowerCase() === tag.toLowerCase();\n}\n\nclass VNode {\n constructor(type, tag, args) {\n this.type = type;\n this.tag = tag;\n this.args = args;\n }\n\n key(k) {\n this.k = k;\n return this;\n }\n\n on(hooks) {\n this.hooks = hooks;\n return this;\n }\n\n opaque() {\n if (this.type !== ELEMENT_NODE) {\n throw new Error('.opaque() can only be used on element nodes (h).');\n }\n this.type = OPAQUE_NODE;\n return this;\n }\n}\n\nfunction defaultShouldUpdate(a, b) {\n if (a === b) return false;\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n if (a.constructor !== b.constructor) return true;\n\n if (Array.isArray(a)) {\n if (a.length !== b.length) return true;\n for (let j = 0; j < a.length; j++) {\n if (a[j] !== b[j]) return true;\n }\n return false;\n }\n\n if (a.constructor === Object) {\n const keysA = Object.keys(a);\n if (keysA.length !== Object.keys(b).length) return true;\n for (const key of keysA) {\n if (!hasOwn(b, key) || a[key] !== b[key]) return true;\n }\n return false;\n }\n }\n return true;\n}\n\nfunction isIterable(x) {\n return (\n Array.isArray(x) ||\n (x != null && typeof x[Symbol.iterator] === 'function' && typeof x !== 'string')\n );\n}\n\nfunction alias(f) {\n return (...args) => new VNode(ALIAS_NODE, f, args);\n}\n\nfunction special(o) {\n return (...args) => new VNode(SPECIAL_NODE, o, args);\n}\n\nfunction newElementNamespace(parentNode, newNodeTag) {\n if (parentNode.namespaceURI === HTML_NAMESPACE) {\n switch (newNodeTag) {\n case 'svg':\n return 'http://www.w3.org/2000/svg';\n case 'math':\n return 'http://www.w3.org/1998/Math/MathML';\n }\n }\n return parentNode.namespaceURI ?? parentNode.host?.namespaceURI ?? HTML_NAMESPACE;\n}\n\n// `originalProps` is null-prototyped so that prop names like `__proto__` or\n// `toString` are recorded as ordinary own keys rather than hitting Object.prototype.\nfunction newNodeState(vdom) {\n return {originalProps: Object.create(null), newVdom: vdom};\n}\n\nfunction createElementNode(parentNode, tag, vdom) {\n const el = parentNode.ownerDocument.createElementNS(newElementNamespace(parentNode, tag), tag);\n el[NODE_STATE] = newNodeState(vdom);\n return el;\n}\n\nconst documentToFocusWithinSet = new WeakMap();\n\nfunction getPathFromElement(element) {\n const path = [];\n let current = element;\n while (current) {\n path.push(current);\n if (current.parentElement) {\n current = current.parentElement;\n } else {\n current = current.getRootNode()?.host;\n }\n }\n return path;\n}\n\nfunction addPathToFocusWithinSet(set, path) {\n for (const node of path) {\n set.add(node);\n }\n}\n\n// The realm's own `Node.prototype` is what we want, so that a node from an\n// iframe or a detached document is moved by its own implementation. Falls back\n// through the configured window and then the ambient one, either of which may\n// be all that is available for a document with no `defaultView`.\nfunction nodePrototypeFor(doc, settingsWindow) {\n const view = settingsWindow ?? doc?.defaultView ?? globalThis;\n return view.Node?.prototype ?? view.Element?.prototype;\n}\n\nfunction installFocusTrackingForDocument(doc) {\n const focusWithinSet = new Set();\n // The set is rebuilt on every focusin rather than accumulated. `focusin`\n // fires after `focusout`, and its composed path is the path of the element\n // that just gained focus, so this is always exactly the current focus path.\n // Accumulating instead would leave every previously focused element marked as\n // holding focus for the rest of the document's life.\n doc.addEventListener('focusin', event => {\n focusWithinSet.clear();\n addPathToFocusWithinSet(focusWithinSet, event.composedPath());\n });\n doc.addEventListener('focusout', event => {\n // A focusout with no relatedTarget means focus left the document entirely;\n // otherwise the focusin that follows will rebuild the set.\n if (!event.relatedTarget) focusWithinSet.clear();\n });\n addPathToFocusWithinSet(focusWithinSet, getPathFromElement(doc.activeElement));\n documentToFocusWithinSet.set(doc, focusWithinSet);\n return focusWithinSet;\n}\n\nexport default userSettings => {\n const shouldUpdate = userSettings?.shouldUpdate ?? defaultShouldUpdate;\n const isMap = userSettings?.isMap ?? (x => x?.constructor === Object);\n const mapIter = userSettings?.mapIter ?? (m => Object.entries(m));\n const mapGet = userSettings?.mapGet ?? ((m, k) => m[k]);\n const mapMerge = userSettings?.mapMerge ?? ((...maps) => Object.assign({}, ...maps));\n const newMap = userSettings?.newMap ?? (obj => ({...obj}));\n const mapPut =\n userSettings?.mapPut ??\n ((m, k, v) => {\n m[k] = v;\n return m;\n });\n const isSeq = userSettings?.isSeq ?? isIterable;\n const seqIter = userSettings?.seqIter ?? (s => s);\n const convertName = userSettings?.convertName ?? (x => x);\n const convertTagName = userSettings?.convertTagName ?? convertName;\n const convertPropName = userSettings?.convertPropName ?? convertName;\n const convertStyleName = userSettings?.convertStyleName ?? convertName;\n const convertDataName = userSettings?.convertDataName ?? convertName;\n const convertClassName = userSettings?.convertClassName ?? convertName;\n const convertHookName = userSettings?.convertHookName ?? convertName;\n const listenerKey = userSettings?.listenerKey ?? 'listener';\n const captureKey = userSettings?.captureKey ?? 'capture';\n const passiveKey = userSettings?.passiveKey ?? 'passive';\n\n const EMPTY_MAP = newMap({});\n\n function toIterator(iterableOrIterator) {\n if (iterableOrIterator == null) return [][Symbol.iterator]();\n if (typeof iterableOrIterator[Symbol.iterator] === 'function') {\n return iterableOrIterator[Symbol.iterator]();\n }\n if (typeof iterableOrIterator.next === 'function') {\n return iterableOrIterator;\n }\n return [iterableOrIterator][Symbol.iterator]();\n }\n\n // Reconciling one element walks its props, styling, attrs, dataset and hooks,\n // so building an array of [name, value] pairs for each of those maps is the\n // single biggest source of garbage in an update. `mapEach` is the iteration\n // primitive the reconciler actually uses: it visits entries without\n // materialising them.\n //\n // Supply `mapEach` for a custom collection and it is as cheap as a plain\n // object — ClojureScript has `reduce-kv`, Immutable.js has `forEach`. Supply\n // only `mapIter` and it still works, by way of the pairs it yields; that is\n // the existing contract and it is unchanged.\n //\n // The extra `a`/`b`/`c` slots let call sites pass context to a hoisted\n // visitor, so iterating does not allocate a closure either.\n // `for...in` with an own-key guard, not `Object.keys`: it allocates no key\n // array and measures ~3.5x faster, while enumerating exactly the same keys.\n // The guard has to be the hoisted `hasOwnProperty.call` — `Object.hasOwn`\n // reads better but benchmarks about the same as building the key array,\n // which defeats the point.\n function defaultMapEach(map, visit, a, b, c) {\n if (map == null) return;\n for (const key in map) {\n if (hasOwn(map, key)) visit(key, map[key], a, b, c);\n }\n }\n\n function mapEachViaIter(map, visit, a, b, c) {\n if (map == null) return;\n const iterator = toIterator(mapIter(map));\n let result;\n while (!(result = iterator.next()).done) {\n const entry = result.value;\n visit(entry[0], entry[1], a, b, c);\n }\n }\n\n // Only fall back to the plain-object walk when the user has told us nothing\n // about their maps. Overriding `mapIter` alone still routes through it.\n const mapEach =\n userSettings?.mapEach ?? (userSettings?.mapIter ? mapEachViaIter : defaultMapEach);\n\n // `null`, `undefined` and `false` are the \"render nothing\" values. Note the\n // checks are strict: `0` and `''` are legitimate text children.\n function isBlank(item) {\n return item === null || item === undefined || item === false;\n }\n\n function flattenSeqIntoArray(array, items, excludeFalsey) {\n const iterator = toIterator(seqIter(items));\n let result;\n while (!(result = iterator.next()).done) {\n const item = result.value;\n if (excludeFalsey && isBlank(item)) {\n continue;\n }\n\n if (!isSeq(item)) {\n array.push(item);\n } else {\n flattenSeqIntoArray(array, item, excludeFalsey);\n }\n }\n }\n\n function defaultFlattenSeq(items, excludeFalsey) {\n const array = [];\n flattenSeqIntoArray(array, items, excludeFalsey);\n return array;\n }\n\n const flattenSeq = userSettings?.flattenSeq ?? defaultFlattenSeq;\n\n function flattenVNodeChildren(children, startIndex) {\n const array = [];\n for (let i = startIndex; i < children.length; i++) {\n const item = children[i];\n if (isBlank(item)) continue;\n if (!isSeq(item)) {\n array.push(item);\n } else {\n flattenSeqIntoArray(array, item, true);\n }\n }\n return array;\n }\n\n // Blank children are filtered out before they reach here, but a custom\n // `flattenSeq` may not do that filtering, so never assume `.toString()` exists.\n function toText(value) {\n return isBlank(value) ? '' : String(value);\n }\n\n function h(tag, props, ...children) {\n if (!isMap(props)) {\n // An explicit nullish props slot means \"no props\", not \"render nothing\".\n if (props !== null && props !== undefined) children.unshift(props);\n props = EMPTY_MAP;\n }\n return new VNode(ELEMENT_NODE, convertTagName(tag), [props, ...children]);\n }\n\n // Visitors are hoisted to the factory closure rather than written inline, so\n // that iterating a map allocates neither entries nor a callback.\n function setStyleProperty(name, value, style) {\n style.setProperty(convertStyleName(name), value);\n }\n\n function removeStaleStyleProperty(name, _value, style, newStyling) {\n if (mapGet(newStyling, name) !== undefined) return;\n style.removeProperty(convertStyleName(name));\n }\n\n function reconcileElementStyling(target, oldStyling, newStyling) {\n const style = target.style;\n mapEach(newStyling, setStyleProperty, style);\n mapEach(oldStyling, removeStaleStyleProperty, style, newStyling);\n }\n\n function setAttribute(name, value, target) {\n target.setAttribute(convertPropName(name), value);\n }\n\n function removeStaleAttribute(name, _value, target, newAttrs) {\n if (mapGet(newAttrs, name) !== undefined) return;\n target.removeAttribute(convertPropName(name));\n }\n\n function reconcileElementAttributes(target, oldAttrs, newAttrs) {\n mapEach(newAttrs, setAttribute, target);\n mapEach(oldAttrs, removeStaleAttribute, target, newAttrs);\n }\n\n function setDataProperty(name, value, target) {\n target.dataset[convertDataName(name)] = value;\n }\n\n function removeStaleDataProperty(name, _value, target, newDataset) {\n if (mapGet(newDataset, name) !== undefined) return;\n delete target.dataset[convertDataName(name)];\n }\n\n function reconcileElementDataset(target, oldDataset, newDataset) {\n mapEach(newDataset, setDataProperty, target);\n mapEach(oldDataset, removeStaleDataProperty, target, newDataset);\n }\n\n function reconcileElementClasses(target, oldClasses, newClasses) {\n // Both sides are compared post-conversion, otherwise a non-identity\n // `convertClassName` would never match an old class against a new one.\n const classesToRemove = new Set();\n for (const c of flattenSeq(oldClasses, true)) {\n const className = convertClassName(c);\n if (className) classesToRemove.add(className);\n }\n for (const c of flattenSeq(newClasses, true)) {\n const className = convertClassName(c);\n // `classList.add('')` throws, and empty class names carry no meaning.\n if (!className) continue;\n classesToRemove.delete(className);\n target.classList.add(className);\n }\n for (const className of classesToRemove) {\n target.classList.remove(className);\n }\n }\n\n function setElementProp(target, nodeState, propName, newValue) {\n if (UNSAFE_PROP_NAMES.has(propName)) {\n console.error(`Refusing to assign unsafe prop name '${propName}' to a DOM node.`);\n return;\n }\n const originalProps = nodeState.originalProps;\n if (!(propName in originalProps)) {\n originalProps[propName] = target[propName];\n }\n target[propName] = newValue === undefined ? originalProps[propName] : newValue;\n }\n\n function restoreElementProp(target, nodeState, propName) {\n const originalProps = nodeState.originalProps;\n if (propName in originalProps) {\n target[propName] = originalProps[propName];\n delete originalProps[propName];\n }\n }\n\n function applyProp(name, newValue, target, oldProps, isHtml) {\n const propName = convertPropName(name);\n const oldValue = mapGet(oldProps, name);\n\n if (Object.is(newValue, oldValue)) return;\n\n switch (propName) {\n case '$styling': {\n if (!isMap(newValue)) throw new Error('invalid value for styling prop');\n reconcileElementStyling(target, oldValue ?? EMPTY_MAP, newValue ?? EMPTY_MAP);\n break;\n }\n case '$classes': {\n if (!isSeq(newValue)) throw new Error('invalid value for classes prop');\n reconcileElementClasses(target, oldValue ?? [], newValue ?? []);\n break;\n }\n case '$attrs': {\n if (!isMap(newValue)) throw new Error('invalid value for attrs prop');\n reconcileElementAttributes(target, oldValue ?? EMPTY_MAP, newValue ?? EMPTY_MAP);\n break;\n }\n case '$dataset': {\n if (!isMap(newValue)) throw new Error('invalid value for dataset prop');\n reconcileElementDataset(target, oldValue ?? EMPTY_MAP, newValue ?? EMPTY_MAP);\n break;\n }\n default: {\n if (isHtml) {\n setElementProp(target, target[NODE_STATE], propName, newValue);\n } else {\n if (newValue === undefined) {\n target.removeAttribute(propName);\n } else {\n target.setAttribute(propName, newValue);\n }\n }\n break;\n }\n }\n }\n\n function restoreRemovedProp(name, oldValue, target, props, isHtml) {\n if (mapGet(props, name) !== undefined) return; // it wasn't removed\n\n const propName = convertPropName(name);\n switch (propName) {\n case '$styling':\n reconcileElementStyling(target, oldValue ?? EMPTY_MAP, EMPTY_MAP);\n break;\n case '$classes':\n reconcileElementClasses(target, oldValue ?? [], []);\n break;\n case '$attrs':\n reconcileElementAttributes(target, oldValue ?? EMPTY_MAP, EMPTY_MAP);\n break;\n case '$dataset':\n reconcileElementDataset(target, oldValue ?? EMPTY_MAP, EMPTY_MAP);\n break;\n default: {\n if (isHtml) {\n restoreElementProp(target, target[NODE_STATE], propName);\n } else {\n target.removeAttribute(propName);\n }\n break;\n }\n }\n }\n\n function reconcileElementProps(target, props) {\n const nodeState = target[NODE_STATE];\n const isHtml = target.namespaceURI === HTML_NAMESPACE;\n const oldProps = nodeState.vdom?.args[0] ?? EMPTY_MAP;\n\n mapEach(props, applyProp, target, oldProps, isHtml);\n mapEach(oldProps, restoreRemovedProp, target, props, isHtml);\n }\n\n function addListener(target, hookName, listener) {\n if (typeof listener === 'function') {\n target.addEventListener(hookName, listener);\n } else if (listener != null) {\n target.addEventListener(hookName, mapGet(listener, listenerKey), {\n capture: !!mapGet(listener, captureKey),\n passive: !!mapGet(listener, passiveKey),\n });\n }\n }\n\n function removeListener(target, hookName, listener) {\n if (typeof listener === 'function') {\n target.removeEventListener(hookName, listener);\n } else if (listener != null) {\n target.removeEventListener(\n hookName,\n mapGet(listener, listenerKey),\n !!mapGet(listener, captureKey),\n );\n }\n }\n\n function addInitialListener(name, listener, target) {\n const hookName = convertHookName(name);\n if (hookName[0] === '$') return;\n addListener(target, hookName, listener);\n }\n\n function swapListener(name, listener, target, oldHooks) {\n const hookName = convertHookName(name);\n if (hookName[0] === '$') return;\n const oldListener = mapGet(oldHooks, name);\n if (listener === oldListener) return;\n removeListener(target, hookName, oldListener);\n addListener(target, hookName, listener);\n }\n\n function removeStaleListener(name, oldListener, target, newHooks) {\n const hookName = convertHookName(name);\n if (hookName[0] === '$' || mapGet(newHooks, name) !== undefined) return;\n removeListener(target, hookName, oldListener);\n }\n\n function reconcileListeners(target, hooks) {\n const state = target[NODE_STATE];\n const newHooks = hooks ?? EMPTY_MAP;\n\n if (!state.vdom) {\n mapEach(newHooks, addInitialListener, target);\n return;\n }\n\n const oldHooks = state.vdom.hooks ?? EMPTY_MAP;\n mapEach(newHooks, swapListener, target, oldHooks);\n mapEach(oldHooks, removeStaleListener, target, newHooks);\n }\n\n function reconcileNode(target) {\n const state = target[NODE_STATE];\n const newVdom = state.newVdom;\n const oldVdom = state.vdom;\n const args = newVdom.args;\n\n switch (newVdom.type) {\n case ELEMENT_NODE: {\n reconcileElementProps(target, args[0]);\n reconcileElementChildren(target, flattenVNodeChildren(args, 1));\n break;\n }\n case OPAQUE_NODE: {\n reconcileElementProps(target, args[0]);\n break;\n }\n case ALIAS_NODE: {\n const innerVdom = newVdom.tag.apply(target, newVdom.args);\n if (isBlank(innerVdom)) {\n reconcileElementChildren(target, []);\n } else if (isSeq(innerVdom)) {\n reconcileElementChildren(target, flattenSeq(innerVdom, true));\n } else {\n reconcileElementChildren(target, [innerVdom]);\n }\n break;\n }\n case SPECIAL_NODE: {\n try {\n newVdom.tag.update?.(target, newVdom.args, oldVdom?.args);\n } catch (err) {\n console.error(err);\n }\n break;\n }\n }\n\n if (newVdom.hooks || oldVdom?.hooks) {\n reconcileListeners(target, newVdom.hooks ?? EMPTY_MAP);\n }\n try {\n newVdom.hooks?.$update?.(target, newVdom, oldVdom);\n } catch (err) {\n console.error(err);\n }\n state.vdom = newVdom;\n delete state.newVdom;\n }\n\n function createNode(parentNode, vdom) {\n if (typeof vdom !== 'object' || vdom === null) {\n return parentNode.ownerDocument.createTextNode(toText(vdom));\n }\n\n let domNode;\n switch (vdom.type) {\n case ELEMENT_NODE:\n case OPAQUE_NODE:\n domNode = createElementNode(parentNode, vdom.tag, vdom);\n break;\n case ALIAS_NODE:\n domNode = createElementNode(parentNode, 'udom-alias', vdom);\n domNode.style.display = 'contents';\n break;\n case SPECIAL_NODE:\n domNode = createElementNode(parentNode, 'udom-special', vdom);\n domNode.style.display = 'contents';\n break;\n default:\n throw new Error('Invalid VDOM node');\n }\n return domNode;\n }\n\n function cleanupTargetChildren(target) {\n if (target.children) {\n for (const child of target.children) {\n cleanupTarget(child);\n }\n }\n }\n\n function cleanupTarget(target) {\n const state = target[NODE_STATE];\n if (!state) {\n return;\n }\n\n const {vdom} = state;\n if (!vdom) {\n // The node was created but never reconciled (a builder threw part way\n // through); there is nothing attached to tear down.\n delete target[NODE_STATE];\n return;\n }\n\n if (vdom.hooks) {\n reconcileListeners(target, EMPTY_MAP);\n }\n\n if (vdom.type === ELEMENT_NODE || vdom.type === OPAQUE_NODE) {\n reconcileElementProps(target, EMPTY_MAP);\n }\n\n if (vdom.type === ELEMENT_NODE || vdom.type === ALIAS_NODE) {\n cleanupTargetChildren(target);\n }\n\n delete target[NODE_STATE];\n\n try {\n if (vdom.type === SPECIAL_NODE) {\n vdom.tag.detach?.(target);\n }\n vdom.hooks?.$detach?.(target);\n } catch (err) {\n console.error(err);\n }\n }\n\n // A reused node only needs a second pass when its arguments or its hooks\n // changed; comparing hooks too means a node whose props are stable but whose\n // listeners are freshly bound each render still gets those listeners swapped.\n function claimExistingNode(domNode, newVdom) {\n const state = domNode[NODE_STATE];\n if (\n shouldUpdate(state.vdom.args, newVdom.args) ||\n shouldUpdate(state.vdom.hooks, newVdom.hooks)\n ) {\n state.newVdom = newVdom;\n }\n return domNode;\n }\n\n function collectOldChildren(target) {\n // Keyed by vdom tag; each bucket keeps a native Map of key -> nodes so that\n // user supplied keys such as 'constructor' or '__proto__' cannot collide\n // with Object.prototype. `cursor` replaces Array#shift, which would make\n // reconciling a long unkeyed list quadratic.\n const byTag = new Map();\n const textNodes = {nodes: [], cursor: 0};\n\n for (const oldChild of target.childNodes) {\n if (oldChild.nodeType === 3 /* TEXT_NODE */) {\n textNodes.nodes.push(oldChild);\n continue;\n }\n\n const oldChildState = oldChild[NODE_STATE];\n const vdom = oldChildState?.vdom;\n if (vdom === undefined) {\n if (oldChildState?.newVdom) {\n throw new Error(\n 'Attempt to reconcile against a target while already working on a reconciliation against that same target, this is not allowed',\n );\n }\n continue;\n }\n\n let poolForTag = byTag.get(vdom.tag);\n if (!poolForTag) {\n poolForTag = {nodesForKey: null, nodesWithoutKey: {nodes: [], cursor: 0}};\n byTag.set(vdom.tag, poolForTag);\n }\n\n if (vdom.k !== undefined) {\n if (!poolForTag.nodesForKey) poolForTag.nodesForKey = new Map();\n const nodesForKey = poolForTag.nodesForKey.get(vdom.k);\n if (nodesForKey) {\n nodesForKey.nodes.push(oldChild);\n } else {\n poolForTag.nodesForKey.set(vdom.k, {nodes: [oldChild], cursor: 0});\n }\n } else {\n poolForTag.nodesWithoutKey.nodes.push(oldChild);\n }\n }\n\n return {byTag, textNodes};\n }\n\n function takeFromPool(pool) {\n if (!pool || pool.cursor >= pool.nodes.length) return undefined;\n return pool.nodes[pool.cursor++];\n }\n\n // Places children in order, walking the existing siblings alongside the\n // desired list so that only nodes that are genuinely out of position move.\n function placeChildren(target, newDomChildren, insertBefore, moveBefore) {\n let ref = target.firstChild;\n for (let i = 0; i < newDomChildren.length; i++) {\n const newChild = newDomChildren[i];\n if (newChild === ref) {\n ref = ref.nextSibling;\n continue;\n }\n const op = moveBefore && newChild.isConnected ? moveBefore : insertBefore;\n op.call(target, newChild, ref);\n }\n }\n\n // Same result, but reached without ever passing the focused child to\n // insertBefore. Detaching a focused node blurs it, so instead it is treated\n // as a fixed anchor and every other child is arranged around it: the children\n // that belong before it are placed immediately before it, and the rest are\n // walked into place after it. The anchor still ends up at its correct index,\n // because exactly the right number of children end up on either side.\n //\n // Only needed where `moveBefore` is unavailable — it relocates a node without\n // detaching it, so focus survives an ordinary move.\n function placeChildrenAroundAnchor(target, newDomChildren, insertBefore, anchorIndex) {\n const anchor = newDomChildren[anchorIndex];\n\n // Right to left, so each child lands directly before the one that follows it.\n let before = anchor;\n for (let i = anchorIndex - 1; i >= 0; i--) {\n const child = newDomChildren[i];\n if (child !== before.previousSibling) {\n insertBefore.call(target, child, before);\n }\n before = child;\n }\n\n // Left to right over whatever still follows the anchor. Children that were\n // stranded on the other side of it get pulled across here.\n let ref = anchor.nextSibling;\n for (let i = anchorIndex + 1; i < newDomChildren.length; i++) {\n const child = newDomChildren[i];\n if (child === ref) {\n ref = ref.nextSibling;\n } else {\n insertBefore.call(target, child, ref);\n }\n }\n }\n\n // At most one child of a given parent can be on the focus path, so there is\n // never more than one anchor to preserve.\n function findFocusedChildIndex(newDomChildren, focusWithin) {\n for (let i = 0; i < newDomChildren.length; i++) {\n if (focusWithin.has(newDomChildren[i])) return i;\n }\n return -1;\n }\n\n function attachAndReconcile(newChild) {\n const state = newChild[NODE_STATE];\n if (!state?.newVdom) return;\n if (!state.vdom) {\n try {\n state.newVdom.hooks?.$attach?.(newChild);\n if (state.newVdom.type === SPECIAL_NODE) {\n state.newVdom.tag.attach?.(newChild);\n }\n } catch (err) {\n console.error(err);\n }\n }\n reconcileNode(newChild);\n }\n\n function reconcileElementChildren(target, newChildren) {\n const oldNodesToRemove = target.firstChild ? new Set(target.childNodes) : null;\n const {byTag, textNodes} = collectOldChildren(target);\n\n const newDomChildren = [];\n for (const newVdom of newChildren) {\n let newDomNode;\n if (newVdom instanceof VNode) {\n const poolForTag = byTag.get(newVdom.tag);\n const reusable = poolForTag\n ? takeFromPool(\n newVdom.k !== undefined\n ? poolForTag.nodesForKey?.get(newVdom.k)\n : poolForTag.nodesWithoutKey,\n )\n : undefined;\n newDomNode = reusable ? claimExistingNode(reusable, newVdom) : createNode(target, newVdom);\n } else {\n const reusableText = takeFromPool(textNodes);\n const text = toText(newVdom);\n if (reusableText) {\n newDomNode = reusableText;\n if (newDomNode.nodeValue !== text) newDomNode.nodeValue = text;\n } else {\n newDomNode = target.ownerDocument.createTextNode(text);\n }\n }\n oldNodesToRemove?.delete(newDomNode);\n newDomChildren.push(newDomNode);\n }\n\n if (oldNodesToRemove) {\n for (const nodeToRemove of oldNodesToRemove) {\n cleanupTarget(nodeToRemove);\n target.removeChild(nodeToRemove);\n }\n }\n\n if (newDomChildren.length === 0) return;\n\n const doc = target.ownerDocument;\n const nodeProto = nodePrototypeFor(doc, userSettings?.window);\n const insertBefore = nodeProto.insertBefore;\n const moveBefore = typeof nodeProto.moveBefore === 'function' ? nodeProto.moveBefore : null;\n const connected = target.isConnected;\n\n let anchorIndex = -1;\n if (connected && !moveBefore) {\n const focusWithin = documentToFocusWithinSet.get(doc) ?? installFocusTrackingForDocument(doc);\n anchorIndex = findFocusedChildIndex(newDomChildren, focusWithin);\n // A focused child is always one we pooled from the existing children, so\n // it is already under this target. The guard is only here so that a\n // surprising focus set can never turn into a thrown insertBefore.\n if (anchorIndex !== -1 && newDomChildren[anchorIndex].parentNode !== target) {\n anchorIndex = -1;\n }\n }\n\n if (anchorIndex === -1) {\n placeChildren(target, newDomChildren, insertBefore, connected ? moveBefore : null);\n } else {\n placeChildrenAroundAnchor(target, newDomChildren, insertBefore, anchorIndex);\n }\n\n // Placement happens first so that lifecycle hooks and alias builders run\n // against a fully arranged sibling list rather than a half built one.\n for (let i = 0; i < newDomChildren.length; i++) {\n attachAndReconcile(newDomChildren[i]);\n }\n }\n\n function reconcile(target, vdom) {\n const state = target[NODE_STATE];\n if (vdom === null || vdom === undefined) {\n if (state) {\n cleanupTarget(target);\n } else {\n cleanupTargetChildren(target);\n }\n target.replaceChildren();\n return;\n }\n\n if (isSeq(vdom)) {\n reconcileElementChildren(target, flattenSeq(vdom, true));\n return;\n }\n\n if (vdom instanceof VNode) {\n if (state) {\n // The tag has to match as well as the type: swapping the alias function\n // or the special descriptor is a different component, and reusing the\n // state would skip the old one's detach and the new one's attach.\n if (state.vdom.type === vdom.type && state.vdom.tag === vdom.tag) {\n if (\n shouldUpdate(state.vdom.args, vdom.args) ||\n shouldUpdate(state.vdom.hooks, vdom.hooks)\n ) {\n state.newVdom = vdom;\n reconcileNode(target);\n }\n return;\n }\n cleanupTarget(target);\n }\n\n switch (vdom.type) {\n case ELEMENT_NODE:\n case OPAQUE_NODE:\n if (!sameTagName(target.nodeName, convertTagName(vdom.tag))) {\n throw new Error('incompatible target for vdom');\n }\n break;\n }\n\n target[NODE_STATE] = newNodeState(vdom);\n try {\n vdom.hooks?.$attach?.(target);\n if (vdom.type === SPECIAL_NODE) {\n vdom.tag.attach?.(target);\n }\n } catch (err) {\n console.error(err);\n }\n reconcileNode(target);\n return;\n }\n\n throw new Error('invalid vdom');\n }\n\n return {\n h,\n alias,\n special,\n reconcile,\n settings: {\n shouldUpdate,\n isMap,\n mapIter,\n mapEach,\n mapGet,\n mapMerge,\n newMap,\n mapPut,\n isSeq,\n flattenSeq,\n seqIter,\n convertTagName,\n convertPropName,\n convertStyleName,\n convertDataName,\n convertClassName,\n convertHookName,\n convertName,\n listenerKey,\n captureKey,\n passiveKey,\n },\n };\n};\n",
|
|
6
|
+
"export default function htmlFactory({h}) {\n return {\n // Document metadata\n title: (...args) => h('title', ...args),\n meta: props => h('meta', props),\n link: props => h('link', props),\n style: (...args) => h('style', ...args),\n script: (...args) => h('script', ...args),\n noscript: (...args) => h('noscript', ...args),\n\n // Content sectioning\n address: (...args) => h('address', ...args),\n article: (...args) => h('article', ...args),\n aside: (...args) => h('aside', ...args),\n footer: (...args) => h('footer', ...args),\n header: (...args) => h('header', ...args),\n h1: (...args) => h('h1', ...args),\n h2: (...args) => h('h2', ...args),\n h3: (...args) => h('h3', ...args),\n h4: (...args) => h('h4', ...args),\n h5: (...args) => h('h5', ...args),\n h6: (...args) => h('h6', ...args),\n main: (...args) => h('main', ...args),\n nav: (...args) => h('nav', ...args),\n section: (...args) => h('section', ...args),\n\n // Text content\n blockquote: (...args) => h('blockquote', ...args),\n dd: (...args) => h('dd', ...args),\n div: (...args) => h('div', ...args),\n dl: (...args) => h('dl', ...args),\n dt: (...args) => h('dt', ...args),\n figcaption: (...args) => h('figcaption', ...args),\n figure: (...args) => h('figure', ...args),\n hr: () => h('hr'),\n li: (...args) => h('li', ...args),\n ol: (...args) => h('ol', ...args),\n p: (...args) => h('p', ...args),\n pre: (...args) => h('pre', ...args),\n ul: (...args) => h('ul', ...args),\n\n // Inline text semantics\n a: (...args) => h('a', ...args),\n abbr: (...args) => h('abbr', ...args),\n b: (...args) => h('b', ...args),\n br: () => h('br'),\n cite: (...args) => h('cite', ...args),\n code: (...args) => h('code', ...args),\n data: (...args) => h('data', ...args),\n del: (...args) => h('del', ...args),\n dfn: (...args) => h('dfn', ...args),\n em: (...args) => h('em', ...args),\n i: (...args) => h('i', ...args),\n ins: (...args) => h('ins', ...args),\n kbd: (...args) => h('kbd', ...args),\n mark: (...args) => h('mark', ...args),\n q: (...args) => h('q', ...args),\n s: (...args) => h('s', ...args),\n samp: (...args) => h('samp', ...args),\n small: (...args) => h('small', ...args),\n span: (...args) => h('span', ...args),\n strong: (...args) => h('strong', ...args),\n sub: (...args) => h('sub', ...args),\n sup: (...args) => h('sup', ...args),\n time: (...args) => h('time', ...args),\n u: (...args) => h('u', ...args),\n wbr: () => h('wbr'),\n\n // Image and multimedia\n area: props => h('area', props),\n audio: (...args) => h('audio', ...args),\n img: props => h('img', props),\n map: (...args) => h('map', ...args),\n track: props => h('track', props),\n video: (...args) => h('video', ...args),\n\n // Embedded content\n embed: props => h('embed', props),\n iframe: (...args) => h('iframe', ...args),\n object: (...args) => h('object', ...args),\n param: props => h('param', props),\n picture: (...args) => h('picture', ...args),\n portal: (...args) => h('portal', ...args),\n source: props => h('source', props),\n\n // SVG and MathML\n svg: (...args) => h('svg', ...args),\n math: (...args) => h('math', ...args),\n\n // Scripting\n canvas: (...args) => h('canvas', ...args),\n\n // Table content\n caption: (...args) => h('caption', ...args),\n col: props => h('col', props),\n colgroup: (...args) => h('colgroup', ...args),\n table: (...args) => h('table', ...args),\n tbody: (...args) => h('tbody', ...args),\n td: (...args) => h('td', ...args),\n tfoot: (...args) => h('tfoot', ...args),\n th: (...args) => h('th', ...args),\n thead: (...args) => h('thead', ...args),\n tr: (...args) => h('tr', ...args),\n\n // Forms\n button: (...args) => h('button', ...args),\n datalist: (...args) => h('datalist', ...args),\n fieldset: (...args) => h('fieldset', ...args),\n form: (...args) => h('form', ...args),\n input: props => h('input', props),\n label: (...args) => h('label', ...args),\n legend: (...args) => h('legend', ...args),\n meter: (...args) => h('meter', ...args),\n optgroup: (...args) => h('optgroup', ...args),\n option: (...args) => h('option', ...args),\n output: (...args) => h('output', ...args),\n progress: (...args) => h('progress', ...args),\n select: (...args) => h('select', ...args),\n textarea: (...args) => h('textarea', ...args),\n\n // Interactive elements\n details: (...args) => h('details', ...args),\n dialog: (...args) => h('dialog', ...args),\n summary: (...args) => h('summary', ...args),\n\n // Web Components\n slot: (...args) => h('slot', ...args),\n template: (...args) => h('template', ...args),\n };\n}\n",
|
|
7
|
+
"const FRAME_BUDGET = 10; // ms\nconst CHUNK_SIZE = 100;\n\nexport default function factory({window} = {}) {\n let scheduled = false;\n let frameId = 0;\n let queue = [];\n\n // Resolved lazily: the default dodo instance is built at import time, which\n // in Node or a jsdom/happy-dom test may run before any animation frame or\n // performance API exists on the global object.\n function host() {\n return window ?? globalThis;\n }\n\n function requestFrame(f) {\n const raf = host().requestAnimationFrame;\n // ~1 frame at 60Hz. setTimeout also keeps the queue draining in\n // environments with no rAF at all, such as SSR and background workers.\n return typeof raf === 'function' ? raf.call(host(), f) : setTimeout(f, 16);\n }\n\n function cancelFrame(id) {\n const caf = host().cancelAnimationFrame;\n if (typeof caf === 'function') {\n caf.call(host(), id);\n } else {\n clearTimeout(id);\n }\n }\n\n function now() {\n return host().performance?.now() ?? Date.now();\n }\n\n function _runTasks(tasks) {\n for (const f of tasks) {\n try {\n f();\n } catch (err) {\n console.error('Error in scheduled function:', err);\n }\n }\n }\n\n function runQueue() {\n const startTime = now();\n frameId = 0;\n\n while (queue.length > 0) {\n const chunk = queue.splice(0, CHUNK_SIZE);\n _runTasks(chunk);\n\n if (now() - startTime > FRAME_BUDGET && queue.length > 0) {\n frameId = requestFrame(runQueue);\n return;\n }\n }\n\n scheduled = false;\n }\n\n // Schedules a function to be executed on the next animation frame.\n function schedule(f, {signal} = {}) {\n if (signal?.aborted) {\n return;\n }\n\n let task = f;\n // If a signal is provided, wrap the task to check for abortion before execution.\n if (signal) {\n task = () => {\n if (!signal.aborted) {\n f();\n }\n };\n }\n\n queue.push(task);\n\n if (!scheduled) {\n scheduled = true;\n frameId = requestFrame(runQueue);\n }\n }\n\n // Immediately runs all queued tasks synchronously.\n function flush() {\n if (frameId) {\n cancelFrame(frameId);\n frameId = 0;\n }\n // Tasks commonly schedule more work (a reconcile that queues a follow-up\n // render). Draining in a loop means flush() leaves an empty queue rather\n // than an orphaned one that no frame is pending for.\n scheduled = true;\n while (queue.length > 0) {\n const toRun = queue;\n queue = [];\n _runTasks(toRun);\n }\n scheduled = false;\n }\n\n // Clears all pending tasks.\n function clear() {\n if (frameId) {\n cancelFrame(frameId);\n }\n queue = [];\n scheduled = false;\n frameId = 0;\n }\n\n return {schedule, flush, clear};\n}\n",
|
|
8
|
+
"import vdomFactory from './src/vdom.js';\nimport htmlFactory from './src/html.js';\nimport schedulerFactory from './src/scheduler.js';\n\nfunction dodoFactory(userSettings) {\n const vdomInstance = vdomFactory(userSettings);\n const htmlInstance = htmlFactory(vdomInstance);\n // `scheduler` replaces the built in animation frame scheduler wholesale. It\n // must provide {schedule, flush, clear}. Useful for rendering synchronously,\n // for driving frames from a test, or for scheduling on idle instead.\n const schedulerInstance = userSettings?.scheduler ?? schedulerFactory(userSettings);\n return {\n ...vdomInstance,\n ...htmlInstance,\n ...schedulerInstance,\n };\n}\n\nconst defaultDodo = dodoFactory();\n\nexport {vdomFactory as vdom, htmlFactory as html, dodoFactory as dodo};\n\nexport const {\n h,\n alias,\n special,\n reconcile,\n settings,\n title,\n meta,\n link,\n style,\n script,\n noscript,\n address,\n article,\n aside,\n footer,\n header,\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n main,\n nav,\n section,\n blockquote,\n dd,\n div,\n dl,\n dt,\n figcaption,\n figure,\n hr,\n li,\n ol,\n p,\n pre,\n ul,\n a,\n abbr,\n b,\n br,\n cite,\n code,\n data,\n del,\n dfn,\n em,\n i,\n ins,\n kbd,\n mark,\n q,\n s,\n samp,\n small,\n span,\n strong,\n sub,\n sup,\n time,\n u,\n wbr,\n area,\n audio,\n img,\n map,\n track,\n video,\n embed,\n iframe,\n object,\n param,\n picture,\n portal,\n source,\n svg,\n math,\n canvas,\n caption,\n col,\n colgroup,\n table,\n tbody,\n td,\n tfoot,\n th,\n thead,\n tr,\n button,\n datalist,\n fieldset,\n form,\n input,\n label,\n legend,\n meter,\n optgroup,\n option,\n output,\n progress,\n select,\n textarea,\n details,\n dialog,\n summary,\n slot,\n template,\n schedule,\n flush,\n clear,\n} = defaultDodo;\n",
|
|
9
|
+
"/**\n * Shared settings resolution for dodo's optional modules.\n *\n * The modules follow the same factory shape as the rest of the project: you\n * hand a factory a `userSettings` object carrying a `dodo` instance, and it\n * returns a configured API.\n *\n * const {watch} = reactive({dodo});\n * const {withContext, useContext} = context({dodo, reactive: {watch}});\n *\n * Dependencies between modules are injected, not discovered. `context` renders\n * through a `watch`, so it takes one — build it once and pass it in. That\n * matters more than it looks: a `special` component's identity *is* its\n * descriptor object, and the reconciler uses that identity to decide whether a\n * DOM node can be reused, so two independently built `watch` components would\n * never reuse each other's nodes.\n *\n * As a convenience a bare dodo instance is accepted in place of the settings\n * object:\n *\n * const {watch} = reactive(dodo);\n */\n\nfunction microtaskSchedule(fn, {signal} = {}) {\n queueMicrotask(() => {\n if (!signal?.aborted) fn();\n });\n}\n\nfunction defaultRenderError(h) {\n return error =>\n h(\n 'pre',\n {\n $styling: {\n 'background-color': '#fdd',\n color: '#330',\n padding: '1em',\n 'white-space': 'pre-wrap',\n },\n },\n h('strong', null, `Error: ${error?.message ?? String(error)}`),\n '\\n\\n',\n error?.stack ?? '',\n );\n}\n\nfunction looksLikeDodo(value) {\n return (\n value != null && typeof value.special === 'function' && typeof value.reconcile === 'function'\n );\n}\n\n/**\n * Merges user settings with defaults.\n *\n * | setting | default |\n * | ------------- | ------------------------------------------- |\n * | `dodo` | required |\n * | `schedule` | the instance's `schedule`, else a microtask |\n * | `renderError` | a `<pre>` with the message and stack |\n */\nexport function settings(userSettings) {\n const source = looksLikeDodo(userSettings) ? {dodo: userSettings} : userSettings;\n const dodo = source?.dodo;\n if (!looksLikeDodo(dodo)) {\n throw new Error('a dodo instance must be provided in settings, e.g. reactive({dodo})');\n }\n\n return {\n // Rendering is scheduled rather than run inline so that a burst of changes\n // collapses into one pass. Replace it to render synchronously, to render on\n // idle, or to drive frames from a test.\n schedule: typeof dodo.schedule === 'function' ? dodo.schedule : microtaskSchedule,\n renderError: defaultRenderError(dodo.h),\n ...source,\n dodo,\n // Map handling and change detection always come from the dodo instance. A\n // module that disagreed with its renderer about what a map is would be\n // worse than useless, so these are not overridable here.\n ...dodo.settings,\n };\n}\n",
|
|
10
|
+
"/**\n * Optional reactivity for dodo.\n *\n * Nothing in dodo's core imports this module, and this module only ever reads\n * the public dodo API it is handed. It is safe to ignore entirely.\n *\n * Everything here is built on one small protocol, the **Cell**:\n *\n * { onDirty(fn) -> unsubscribe, getValue() -> any }\n *\n * `onDirty` registers a callback that fires when the value *may* have changed,\n * and `getValue` pulls the current value. That is the whole contract. It is\n * deliberately push-to-invalidate / pull-to-read: dodo can coalesce any number\n * of invalidations into a single render and then read once.\n *\n * Because the contract is this small, most reactive libraries adapt to it in a\n * few lines. `fromObservable`, `fromSubscribe` and `fromSignal` cover the\n * common shapes (RxJS-style observables, callback subscriptions such as Svelte\n * stores, and preact signals respectively).\n *\n * Two values are special:\n * - `PENDING` means \"no value yet\"; `watch` renders its placeholder instead\n * of calling the builder.\n * - An error is reported by *throwing* from `getValue()`; `watch` catches it\n * and renders its error view.\n */\n\nimport {settings as resolveSettings} from './settings.js';\n\nexport const PENDING = Symbol('dodo.reactive.PENDING');\n\nconst NOTHING = Symbol('dodo.reactive.NOTHING');\n\n/** Duck-types a Cell. Anything with both methods qualifies. */\nexport function isCell(value) {\n return (\n value != null && typeof value.onDirty === 'function' && typeof value.getValue === 'function'\n );\n}\n\n/** Reads a Cell, passing plain values straight through. */\nexport function readCell(value) {\n return isCell(value) ? value.getValue() : value;\n}\n\nfunction toUnsubscribe(subscription) {\n if (typeof subscription === 'function') return subscription;\n if (subscription && typeof subscription.unsubscribe === 'function') {\n return () => subscription.unsubscribe();\n }\n return () => {};\n}\n\n/**\n * A listener set plus a safe fan-out, for writing your own cells:\n *\n * const {listeners, notify} = notifier();\n */\nexport function notifier() {\n const listeners = new Set();\n return {\n listeners,\n notify() {\n // Copied because a listener may unsubscribe itself while being notified.\n for (const listener of Array.from(listeners)) {\n try {\n listener();\n } catch (err) {\n console.error('Error in dodo cell listener:', err);\n }\n }\n },\n };\n}\n\n/**\n * Shared plumbing for adapted cells: connect to the upstream source on the\n * first listener and disconnect on the last. Staying disconnected while nobody\n * is listening is what keeps adapted sources from leaking subscriptions once\n * their `watch` has been detached — cleanup is structural rather than something\n * every adapter has to remember.\n *\n * `connect` receives a `notify` callback and returns either an unsubscribe\n * function or `{unsubscribe}`. `read` supplies `getValue`, and should cope with\n * being called while disconnected.\n *\n * const online = connectable(\n * notify => {\n * addEventListener('online', notify);\n * addEventListener('offline', notify);\n * return () => {\n * removeEventListener('online', notify);\n * removeEventListener('offline', notify);\n * };\n * },\n * () => navigator.onLine,\n * );\n */\nexport function connectable(connect, read) {\n const {listeners, notify} = notifier();\n let disconnect = null;\n\n return {\n onDirty(fn) {\n listeners.add(fn);\n if (listeners.size === 1) {\n try {\n disconnect = toUnsubscribe(connect(notify));\n } catch (err) {\n // Leaving the listener in place would make the cell look connected\n // for good, so a later subscriber would never retry.\n listeners.delete(fn);\n throw err;\n }\n }\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n listeners.delete(fn);\n if (listeners.size === 0 && disconnect) {\n const stop = disconnect;\n disconnect = null;\n stop();\n }\n };\n },\n getValue() {\n return read();\n },\n };\n}\n\n/**\n * A writable cell. The simplest possible source, and enough on its own for\n * applications that do not want another dependency.\n *\n * const count = cell(0);\n * count.setValue(1);\n * count.update(n => n + 1);\n */\nexport function cell(initialValue) {\n let value = initialValue;\n const {listeners, notify} = notifier();\n\n return {\n onDirty(fn) {\n listeners.add(fn);\n return () => listeners.delete(fn);\n },\n getValue() {\n return value;\n },\n setValue(newValue) {\n if (Object.is(newValue, value)) return;\n value = newValue;\n notify();\n },\n update(fn) {\n this.setValue(fn(value));\n },\n };\n}\n\n/** A cell that never changes. Useful for filling a slot in `derive`. */\nexport function constant(value) {\n return {\n onDirty() {\n return () => {};\n },\n getValue() {\n return value;\n },\n };\n}\n\n/**\n * Derives a cell from other cells (or plain values).\n *\n * const total = derive([price, qty], (p, q) => p * q);\n *\n * The result is memoised while subscribed: `compute` runs on the first read\n * after an upstream invalidation and not again until the next one. If any\n * dependency is `PENDING` the derived cell is `PENDING` too and `compute` is\n * not called at all.\n */\nexport function derive(dependencies, compute) {\n const deps = Array.isArray(dependencies) ? dependencies : [dependencies];\n const {listeners, notify} = notifier();\n let unsubscribes = null;\n let cached = NOTHING;\n\n const invalidate = () => {\n cached = NOTHING;\n notify();\n };\n\n const recompute = () => {\n const values = new Array(deps.length);\n for (let i = 0; i < deps.length; i++) {\n const value = readCell(deps[i]);\n if (value === PENDING) return PENDING;\n values[i] = value;\n }\n return compute(...values);\n };\n\n return {\n onDirty(fn) {\n listeners.add(fn);\n if (listeners.size === 1) {\n cached = NOTHING;\n unsubscribes = deps.map(dep => (isCell(dep) ? dep.onDirty(invalidate) : null));\n }\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n listeners.delete(fn);\n if (listeners.size === 0 && unsubscribes) {\n for (const unsubscribe of unsubscribes) unsubscribe?.();\n unsubscribes = null;\n cached = NOTHING;\n }\n };\n },\n getValue() {\n // Without listeners there is nothing telling us when the cache went\n // stale, so an unsubscribed derived cell always recomputes.\n if (cached === NOTHING || !unsubscribes) {\n cached = recompute();\n }\n return cached;\n },\n };\n}\n\n/** Convenience for the single dependency case. */\nexport function mapCell(source, fn) {\n return derive([source], fn);\n}\n\n/**\n * Adapts an observer-style observable: `subscribe({next, error, complete})`,\n * as produced by RxJS, the TC39 proposal, and bones' own `Observable`.\n *\n * The cell reports `PENDING` until the first emission unless `initial` is\n * given, and rethrows from `getValue()` if the source errors.\n */\nexport function fromObservable(observable, {initial = PENDING} = {}) {\n return fromSource(observable, {initial}, (source, handlers) =>\n source.subscribe({\n next: handlers.next,\n error: handlers.error,\n complete: handlers.complete,\n }),\n );\n}\n\n/**\n * Adapts a callback-style subscription: `subscribe(value => ...)` returning\n * either an unsubscribe function or `{unsubscribe}`. This covers Svelte stores,\n * preact signals' own `.subscribe`, and most hand-rolled stores.\n */\nexport function fromSubscribe(subscribable, {initial = PENDING} = {}) {\n return fromSource(subscribable, {initial}, (source, handlers) => {\n const subscribe = typeof source === 'function' ? source : source.subscribe.bind(source);\n return subscribe(handlers.next);\n });\n}\n\nfunction fromSource(source, {initial}, subscribe) {\n let value = initial;\n let failure = NOTHING;\n\n const cellApi = connectable(\n notify => {\n const handlers = {\n next(newValue) {\n value = newValue;\n failure = NOTHING;\n notify();\n },\n error(err) {\n failure = err;\n notify();\n },\n complete() {},\n };\n return subscribe(source, handlers);\n },\n () => {\n if (failure !== NOTHING) throw failure;\n return value;\n },\n );\n\n return cellApi;\n}\n\n/**\n * Adapts a preact signal (`@preact/signals-core` and friends).\n *\n * Reads go through `signal.peek()`, so consuming a signal through a cell never\n * registers a tracking dependency on whatever effect happens to be running, and\n * the cell can never hand back a stale copy of the signal's value.\n *\n * Invalidation uses `signal.subscribe` when available; pass `{effect}` (the\n * library's own `effect` function) if you would rather drive it that way, or if\n * you are adapting a computed from a build that has no `subscribe`.\n */\nexport function fromSignal(signal, {effect} = {}) {\n if (signal == null || (typeof signal !== 'object' && typeof signal !== 'function')) {\n throw new TypeError('fromSignal expects a signal object');\n }\n const read = typeof signal.peek === 'function' ? () => signal.peek() : () => signal.value;\n\n return connectable(notify => {\n // Both `effect` and `signal.subscribe` invoke their callback once up front.\n // That first call carries no news — the consumer is about to pull anyway —\n // so it is swallowed to avoid an immediate redundant render.\n let primed = false;\n const onChange = () => {\n if (!primed) {\n primed = true;\n return;\n }\n notify();\n };\n\n if (typeof effect === 'function') {\n return effect(() => {\n signal.value; // establishes the dependency\n onChange();\n });\n }\n if (typeof signal.subscribe === 'function') {\n return signal.subscribe(onChange);\n }\n throw new TypeError('fromSignal needs either signal.subscribe or an {effect} option');\n }, read);\n}\n\n/**\n * Exposes a cell as a minimal observer-style observable, for handing dodo state\n * back to code that speaks that protocol.\n */\nexport function toObservable(source) {\n return {\n subscribe(observerOrNext) {\n const observer =\n typeof observerOrNext === 'function' ? {next: observerOrNext} : (observerOrNext ?? {});\n const emit = () => {\n let value;\n try {\n value = readCell(source);\n } catch (err) {\n observer.error?.(err);\n return;\n }\n if (value !== PENDING) observer.next?.(value);\n };\n const unsubscribe = isCell(source) ? source.onDirty(emit) : () => {};\n emit();\n return {unsubscribe};\n },\n };\n}\n\n/**\n * Runs `fn` with the cell's value now and again on every change. Returns a\n * dispose function. This is the escape hatch for reacting to state outside of\n * rendering (logging, persistence, imperative DOM work).\n */\nexport function effect(source, fn) {\n const run = () => {\n let value;\n try {\n value = readCell(source);\n } catch (err) {\n console.error('Error reading cell in effect:', err);\n return;\n }\n if (value === PENDING) return;\n fn(value);\n };\n const unsubscribe = isCell(source) ? source.onDirty(run) : () => {};\n run();\n return unsubscribe;\n}\n\nconst WATCH_STATE = Symbol('dodo.reactive.watch');\n\n/**\n * Builds the dodo-aware half of the module: the `watch` component.\n *\n * const {watch} = reactive({dodo});\n *\n * Settings:\n *\n * | setting | purpose |\n * | ------------- | -------------------------------------------------------- |\n * | `dodo` | the instance to render into (required) |\n * | `schedule` | how re-renders are deferred and coalesced |\n * | `renderError` | the fallback error view, when `watch` is given no `error` |\n *\n * Each call builds a new `watch`, and a `special` component's identity is its\n * descriptor object. Build one per application and pass it around — in\n * particular, hand it to `context` — rather than calling this per module.\n *\n * Cells themselves are instance-independent; only `watch` needs to know which\n * dodo it renders into.\n */\nexport default function reactiveFactory(userSettings) {\n const settings = resolveSettings(userSettings);\n const {dodo, schedule, renderError} = settings;\n const {special, reconcile} = dodo;\n const rawMapGet = settings.mapGet ?? ((m, k) => m[k]);\n // `options` is optional at every call site, and a custom `mapGet` is under no\n // obligation to tolerate a nullish map.\n const mapGet = (map, key) => (map == null ? undefined : rawMapGet(map, key));\n const shouldUpdate = settings.shouldUpdate ?? ((a, b) => a !== b);\n\n const watch = special({\n attach(element) {\n element[WATCH_STATE] = {\n source: NOTHING,\n builder: null,\n placeholder: undefined,\n errorBuilder: undefined,\n unsubscribe: null,\n abortController: null,\n renderScheduled: false,\n lastValue: NOTHING,\n lastBuilder: NOTHING,\n };\n },\n\n update(element, [source, builder, options]) {\n const state = element[WATCH_STATE];\n if (!state) return;\n\n state.builder = builder;\n state.placeholder = mapGet(options, 'placeholder');\n state.errorBuilder = mapGet(options, 'error');\n\n if (state.source !== source) {\n state.unsubscribe?.();\n state.unsubscribe = null;\n state.abortController?.abort();\n state.abortController = new AbortController();\n state.source = source;\n state.lastValue = NOTHING;\n if (isCell(source)) {\n try {\n // Subscribing can emit synchronously (a subject replaying its\n // current value), which schedules a render that the direct call\n // below then finds nothing left to do for.\n state.unsubscribe = source.onDirty(() => this.invalidate(element));\n } catch (err) {\n // Connecting is as much a part of reading the cell as getValue is —\n // an adapter with no observer to attach to fails here, not there.\n this.renderError(element, err);\n return;\n }\n }\n }\n\n this.render(element);\n },\n\n invalidate(element) {\n const state = element[WATCH_STATE];\n if (!state || state.renderScheduled) return;\n state.renderScheduled = true;\n schedule(() => this.render(element), {signal: state.abortController?.signal});\n },\n\n render(element) {\n const state = element[WATCH_STATE];\n if (!state) return;\n state.renderScheduled = false;\n\n let value;\n try {\n value = readCell(state.source === NOTHING ? undefined : state.source);\n } catch (err) {\n this.renderError(element, err);\n return;\n }\n\n // Re-rendering identical output is the common case for a cell shared by\n // several watchers, so it is worth short-circuiting before the builder runs.\n if (\n state.builder === state.lastBuilder &&\n state.lastValue !== NOTHING &&\n !shouldUpdate(state.lastValue, value)\n ) {\n return;\n }\n state.lastValue = value;\n state.lastBuilder = state.builder;\n\n try {\n if (value === PENDING) {\n reconcile(element, state.placeholder ? [state.placeholder()] : []);\n } else {\n reconcile(element, [state.builder(value)]);\n }\n } catch (err) {\n this.renderError(element, err);\n }\n },\n\n renderError(element, error) {\n const state = element[WATCH_STATE];\n if (!state) return;\n state.lastValue = NOTHING;\n state.lastBuilder = NOTHING;\n console.error('Error in watched cell:', error);\n try {\n reconcile(element, [(state.errorBuilder ?? renderError)(error)]);\n } catch (err) {\n console.error('Error rendering the error view:', err);\n }\n },\n\n detach(element) {\n const state = element[WATCH_STATE];\n if (!state) return;\n state.unsubscribe?.();\n state.abortController?.abort();\n delete element[WATCH_STATE];\n reconcile(element, null);\n },\n });\n\n return {watch};\n}\n",
|
|
11
|
+
"/**\n * `@3sln/dodo/reactive` — the reactive module bound to dodo's default instance.\n *\n * import {cell, watch} from '@3sln/dodo/reactive';\n *\n * `@3sln/dodo/context` is built with this exact `watch` injected, so the two\n * entry points render through one component.\n *\n * If you build your own dodo with `dodo(userSettings)`, or you want to replace\n * the scheduler or the error view, bake your own copy — and inject it into\n * `context` so the two stay one system:\n *\n * import reactive from '@3sln/dodo/src/reactive.js';\n * import context from '@3sln/dodo/src/context.js';\n *\n * const {watch} = reactive({dodo: myDodo});\n * const {withContext} = context({dodo: myDodo, reactive: {watch}});\n */\nimport * as dodo from './index.js';\nimport reactiveFactory from './src/reactive.js';\n\nexport * from './src/reactive.js';\nexport {default as reactive} from './src/reactive.js';\nexport {settings} from './src/settings.js';\n\nexport const {watch} = reactiveFactory({dodo});\n",
|
|
12
|
+
"// Wire the 3sln stack for Trove's web client.\n//\n// - dodo: the VDOM (h, reconcile, element helpers, alias/special)\n// - dodo/reactive: cells, and the `watch` component that renders one\n//\n// bones is gone; what Trove used of it — `watch`, a writable subject, and a way\n// to combine several of them — moved into dodo as the **Cell** protocol:\n//\n// { onDirty(fn) -> unsubscribe, getValue() -> any }\n//\n// That is a smaller contract than the observable it replaces, and the difference\n// shows up in two places we care about. It is push-to-invalidate / pull-to-read,\n// so any number of invalidations in one frame coalesce into a single render;\n// and a cell always HAS a value, so a `watch` renders its store's current state\n// on the first pass instead of waiting for an emission that already happened.\n//\n// `PENDING` is how a cell says \"no value yet\" — `watch` renders its placeholder\n// instead of calling the builder. An error is reported by THROWING from\n// `getValue()`, which `watch` renders as its error view. Both matter for\n// `fromAsync` below, which is the only shape of ours the module does not ship.\n\nimport * as dodo from '@3sln/dodo';\nimport {\n watch, cell, derive, constant, mapCell, connectable,\n fromObservable, toObservable, effect, isCell, readCell, PENDING,\n} from '@3sln/dodo/reactive';\n\nexport const dd = dodo;\n\nexport {\n watch, cell, derive, constant, mapCell, connectable,\n fromObservable, toObservable, effect, isCell, readCell, PENDING,\n};\n\n/**\n * A cell backed by one async call: `PENDING` until it settles, the value after,\n * and a throw from `getValue()` if it rejects — which is exactly the vocabulary\n * `watch`'s `placeholder` and `error` options are written against.\n *\n * The work starts on the first listener, not at construction, so building one\n * for a view that is never rendered costs nothing. It runs ONCE: a cell that\n * refetched every time its last watcher went away and came back would reload a\n * document on every remount.\n */\nexport function fromAsync(work) {\n let state = { status: 'idle' };\n return connectable(\n (notify) => {\n if (state.status === 'idle') {\n state = { status: 'running' };\n Promise.resolve()\n .then(work)\n .then(\n (value) => { state = { status: 'done', value }; notify(); },\n (error) => { state = { status: 'failed', error }; notify(); },\n );\n }\n // Nothing to disconnect: a promise cannot be un-awaited, and the result is\n // kept so a later listener reads it rather than starting again.\n return () => {};\n },\n () => {\n if (state.status === 'failed') throw state.error;\n return state.status === 'done' ? state.value : PENDING;\n },\n );\n}\n\n/** The reactive API as one object, for `platform.reactive` (and plugin-facing code). */\nexport const reactive = {\n watch, cell, derive, constant, mapCell, connectable,\n fromObservable, fromAsync, toObservable, effect, isCell, readCell, PENDING,\n};\n",
|
|
13
|
+
"// A small, deliberate error taxonomy. Everything that can fail in Trove throws\n// (or rejects with) a TroveError subclass so callers — and ultimately the user —\n// get a stable `code`, a human `message`, and a `retryable` signal that the\n// retry helper and the HTTP layer both key off of. Wrapping a lower-level cause\n// (an S3 SDK error, a fetch TypeError) preserves it under `.cause` for logs\n// while presenting a clean surface upward.\n\n/** Stable, machine-readable codes. Mirrored by the HTTP layer → status codes. */\nexport const ErrorCode = Object.freeze({\n NOT_FOUND: 'not_found',\n ALREADY_EXISTS: 'already_exists',\n INVALID: 'invalid', // bad request / validation\n CONFLICT: 'conflict', // version/etag mismatch, concurrent edit\n UNAUTHORIZED: 'unauthorized',\n FORBIDDEN: 'forbidden',\n UNSUPPORTED: 'unsupported', // backend can't do this (e.g. presign on fs)\n QUOTA: 'quota', // out of space (507) / rate limited by capacity (429) — see below\n TOO_LARGE: 'too_large', // this request is bigger than a configured limit allows\n BAD_RANGE: 'bad_range', // the requested byte range doesn't exist in this object (416)\n TRANSIENT: 'transient', // network blip, 5xx, throttle — safe to retry\n TIMEOUT: 'timeout',\n ABORTED: 'aborted', // caller cancelled (AbortSignal)\n INTERNAL: 'internal', // unexpected; a bug\n});\n\n// Which codes are, by default, safe to retry with backoff.\nconst RETRYABLE = new Set([ErrorCode.TRANSIENT, ErrorCode.TIMEOUT, ErrorCode.QUOTA]);\n\n// HTTP status for each code, so the server layer stays declarative.\nconst HTTP_STATUS = {\n [ErrorCode.NOT_FOUND]: 404,\n [ErrorCode.ALREADY_EXISTS]: 409,\n [ErrorCode.INVALID]: 400,\n [ErrorCode.CONFLICT]: 412,\n [ErrorCode.UNAUTHORIZED]: 401,\n [ErrorCode.FORBIDDEN]: 403,\n [ErrorCode.UNSUPPORTED]: 501,\n [ErrorCode.QUOTA]: 429,\n [ErrorCode.TOO_LARGE]: 413,\n [ErrorCode.BAD_RANGE]: 416,\n [ErrorCode.TRANSIENT]: 503,\n [ErrorCode.TIMEOUT]: 504,\n [ErrorCode.ABORTED]: 499,\n [ErrorCode.INTERNAL]: 500,\n};\n\nexport class TroveError extends Error {\n /**\n * @param {string} code one of ErrorCode\n * @param {string} message human-readable, safe to surface to the user\n * @param {{cause?: unknown, retryable?: boolean, details?: object}} [opts]\n */\n constructor(code, message, opts = {}) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = 'TroveError';\n this.code = code || ErrorCode.INTERNAL;\n // Explicit override wins; otherwise derive from the code.\n this.retryable = opts.retryable ?? RETRYABLE.has(this.code);\n this.details = opts.details ?? null;\n this.status = HTTP_STATUS[this.code] ?? 500;\n // QUOTA covers two failures that deserve different statuses. A rate limit is 429 —\n // back off and try again. Being out of DISK is 507: retrying changes nothing, and\n // telling a client to retry sends it into a loop against a condition only a human\n // can clear. `retryable` is what tells them apart.\n //\n // \"Your file is bigger than we allow\" is neither, and has its own code (TOO_LARGE,\n // 413): the store is not full, and nothing about waiting or freeing space helps.\n if (this.code === ErrorCode.QUOTA && !this.retryable) this.status = 507;\n }\n\n /** Shape sent over the wire and logged. Never leaks the raw cause. */\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n retryable: this.retryable,\n ...(this.details ? { details: this.details } : {}),\n },\n };\n }\n\n // Ergonomic constructors — read like prose at the call site.\n static notFound(what, opts) {\n return new TroveError(ErrorCode.NOT_FOUND, what ? `${what} not found` : 'Not found', opts);\n }\n static alreadyExists(what, opts) {\n return new TroveError(ErrorCode.ALREADY_EXISTS, `${what} already exists`, opts);\n }\n static invalid(message, opts) {\n return new TroveError(ErrorCode.INVALID, message, opts);\n }\n static conflict(message, opts) {\n return new TroveError(ErrorCode.CONFLICT, message, opts);\n }\n static unsupported(message, opts) {\n return new TroveError(ErrorCode.UNSUPPORTED, message, opts);\n }\n static transient(message, opts) {\n return new TroveError(ErrorCode.TRANSIENT, message, { retryable: true, ...opts });\n }\n static timeout(message, opts) {\n return new TroveError(ErrorCode.TIMEOUT, message, { retryable: true, ...opts });\n }\n static aborted(message = 'Operation aborted', opts) {\n return new TroveError(ErrorCode.ABORTED, message, { retryable: false, ...opts });\n }\n static unauthorized(message = 'Unauthorized', opts) {\n return new TroveError(ErrorCode.UNAUTHORIZED, message, opts);\n }\n static tooLarge(message = 'Too large', opts) {\n return new TroveError(ErrorCode.TOO_LARGE, message, { retryable: false, ...opts });\n }\n /** The requested byte range doesn't exist in this object — 416, not 400 or 500. */\n static badRange(message = 'Range not satisfiable', opts) {\n return new TroveError(ErrorCode.BAD_RANGE, message, { retryable: false, ...opts });\n }\n static forbidden(message = 'Forbidden', opts) {\n return new TroveError(ErrorCode.FORBIDDEN, message, opts);\n }\n static internal(message = 'Internal error', opts) {\n return new TroveError(ErrorCode.INTERNAL, message, opts);\n }\n}\n\n/**\n * Normalize any thrown value into a TroveError. Recognises AbortError, common\n * fetch/network failures, and Node fs errno codes so lower layers can just\n * `throw wrapError(e)` and get sane classification for free.\n */\nexport function wrapError(err, fallbackMessage = 'Unexpected error') {\n if (err instanceof TroveError) return err;\n\n // Cancellation.\n if (err?.name === 'AbortError' || err?.code === 'ABORT_ERR') {\n return TroveError.aborted(err.message || 'Operation aborted', { cause: err });\n }\n\n // Node/browser network failures are transient by nature.\n const netCodes = new Set([\n 'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EAI_AGAIN', 'EPIPE', 'ENOTFOUND', 'UND_ERR_SOCKET',\n ]);\n if (netCodes.has(err?.code) || (err instanceof TypeError && /fetch|network/i.test(err.message || ''))) {\n return TroveError.transient(err.message || 'Network error', { cause: err });\n }\n\n // Node fs errnos.\n switch (err?.code) {\n case 'ENOENT':\n return TroveError.notFound(null, { cause: err });\n case 'EEXIST':\n return TroveError.alreadyExists('Path', { cause: err });\n case 'EACCES':\n case 'EPERM':\n return new TroveError(ErrorCode.FORBIDDEN, 'Permission denied', { cause: err });\n // Out of room. NOT retryable, unlike the rate-limit sense of QUOTA that shares this\n // code — retrying a full disk just burns the user's time to reach the same answer.\n // The message is what someone can act on, rather than the kernel's phrasing.\n case 'ENOSPC':\n return new TroveError(ErrorCode.QUOTA, 'The storage volume is full — free some space and try again', { cause: err, retryable: false });\n case 'EDQUOT':\n return new TroveError(ErrorCode.QUOTA, 'The storage quota for this volume has been reached', { cause: err, retryable: false });\n case 'EFBIG':\n return new TroveError(ErrorCode.QUOTA, 'That file is larger than this filesystem can store', { cause: err, retryable: false });\n }\n\n return TroveError.internal(err?.message || fallbackMessage, { cause: err });\n}\n\n/**\n * Did this fail because the store is out of room?\n *\n * QUOTA covers two different things — \"no space left\" and \"you are being rate limited\"\n * — and only the first is a standing condition someone has to go and fix. Callers that\n * want to raise a persistent, actionable problem need to tell them apart, and the\n * distinguishing fact is that a full disk is not retryable.\n */\nexport function isOutOfSpace(err) {\n return err instanceof TroveError && err.code === ErrorCode.QUOTA && err.retryable === false;\n}\n\n/** True if the thrown value should be retried. */\nexport function isRetryable(err) {\n if (err instanceof TroveError) return err.retryable;\n return wrapError(err).retryable;\n}\n",
|
|
14
|
+
"// Small runtime-agnostic helpers. No Node or browser globals beyond what's\n// standard in both (crypto.randomUUID, TextEncoder, Web Streams).\n\nimport { TroveError } from './errors.js';\n\n/** URL-safe unique id. */\nexport function newId(prefix = '') {\n const uuid = (globalThis.crypto?.randomUUID?.() ?? fallbackUuid()).replace(/-/g, '');\n return prefix ? `${prefix}_${uuid}` : uuid;\n}\n\nfunction fallbackUuid() {\n // Only used where crypto.randomUUID is unavailable; good enough for ids.\n let s = '';\n for (let i = 0; i < 32; i++) s += Math.floor(Math.random() * 16).toString(16);\n return s;\n}\n\n// ---- item names -------------------------------------------------------------\n// There are no paths: a collection is a flat namespace, and a name is the whole\n// address within it. Slashes are rejected because a name has to survive the `/name`\n// shorthand of a trove: link without splitting.\n\n/**\n * A user-visible ITEM name. Distinct from the `isValidName` in plugins/identity.js,\n * which validates a *name segment in the contribution address space* — same word, very\n * different rule, so they get different names rather than colliding across the two\n * public entry points that export them.\n */\nexport function isValidItemName(name) {\n return (\n typeof name === 'string' &&\n name.length > 0 &&\n name.length <= 255 &&\n !name.includes('/') &&\n name !== '.' &&\n name !== '..' &&\n // eslint-disable-next-line no-control-regex\n !/[\\x00-\\x1f]/.test(name)\n );\n}\n\n/** The lowercased extension of an item name, including the dot ('' if none). */\nexport function extname(name) {\n const i = String(name || '').lastIndexOf('.');\n return i > 0 ? String(name).slice(i).toLowerCase() : '';\n}\n\n/**\n * Does a contribution/opener/indexer `selector` match a file node? One matcher shared\n * by the client contribution registry and the server indexers so they can't drift.\n * A selector has any of: `match(node)` (a predicate, built-ins only), `ext` (with or\n * without leading dot), `mime`/`contentType` (exact or a `type/*` prefix).\n */\nexport function selectorMatches(selector, node) {\n if (!selector || !node) return false;\n if (typeof selector.match === 'function') {\n try { if (selector.match(node)) return true; } catch { /* ignore a bad matcher */ }\n }\n const ext = extname(node.name || '');\n if (ext && (selector.ext || []).some((e) => (e.startsWith('.') ? e : '.' + e).toLowerCase() === ext)) return true;\n const ct = node.contentType || '';\n const mimes = selector.mime || selector.contentType || [];\n return mimes.some((m) => (m.endsWith('/*') ? ct.startsWith(m.slice(0, -1)) : ct === m));\n}\n\n// ---- byte helpers -----------------------------------------------------------\n\n/** Concatenate Uint8Array chunks into one. */\nexport function concatBytes(chunks) {\n let total = 0;\n for (const c of chunks) total += c.length;\n const out = new Uint8Array(total);\n let off = 0;\n for (const c of chunks) { out.set(c, off); off += c.length; }\n return out;\n}\n\n/** Drain a web ReadableStream into a single Uint8Array. */\nexport async function readAll(stream) {\n const reader = stream.getReader();\n const chunks = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value instanceof Uint8Array ? value : new Uint8Array(value));\n }\n return concatBytes(chunks);\n}\n\n\n/**\n * Refuse a URL that would make the server fetch something on its own network.\n *\n * Anywhere a caller supplies a URL the SERVER will later request, this is the gate. The\n * dangerous targets are not exotic: `169.254.169.254` is cloud instance metadata (and\n * with it, credentials), `127.0.0.1` and private ranges are whatever else the box is\n * running, and `.internal`/`.local` are the names those things usually have.\n *\n * DNS names that resolve to private addresses are a residual risk — rebinding cannot be\n * settled at this layer — so a deployment that cares should egress-filter too.\n *\n * @param {string} url\n * @param {string} what named in the error, so the message says which field was wrong\n */\nexport function assertPublicUrl(url, what = 'URL') {\n let u;\n try { u = new URL(String(url)); } catch { throw TroveError.invalid(`${what} is not a valid URL`); }\n if (u.protocol !== 'https:' && u.protocol !== 'http:') {\n throw TroveError.invalid(`${what} must be http(s)`);\n }\n const h = u.hostname.toLowerCase();\n if (h === 'localhost' || h.endsWith('.localhost') || h.endsWith('.local') || h.endsWith('.internal')) {\n throw TroveError.invalid(`${what} points at an internal host`);\n }\n if (/^\\d{1,3}(\\.\\d{1,3}){3}$/.test(h)) {\n const [a, b] = h.split('.').map(Number);\n if (a === 10 || a === 127 || a === 0 || (a === 192 && b === 168)\n || (a === 172 && b >= 16 && b <= 31) || (a === 169 && b === 254) || a >= 224) {\n throw TroveError.invalid(`${what} points at a private address`);\n }\n }\n if (h.includes(':') || h.startsWith('[')) throw TroveError.invalid(`${what} may not be an IP literal`);\n return u;\n}\n",
|
|
15
|
+
"// Plugin identity and contribution URIs.\n//\n// A plugin is identified by its DOMAIN plus its NAME — never by a self-chosen opaque\n// id. The domain is proven (the package is signed, and the domain publishes the\n// signing key via assetlinks), so identity is verifiable rather than claimed. That\n// gives us three things at once:\n//\n// • no squatting: only acme.com can publish acme.com/docs\n// • versioning: acme.com/docs@2 is the same plugin as acme.com/docs@1, and a\n// different plugin from acme.com/sheets — which a self-chosen id can't express\n// • no cross-kind collisions: every contribution lives under its plugin, so an\n// opener, a status slot, a register and a command can all be called \"status\"\n// without colliding with each other or with another plugin's \"status\".\n//\n// Every contribution is addressed by a URI:\n//\n// trove+contrib:<domain>/<plugin>/<contribution>\n// e.g. trove+contrib:acme.com/docs/pdfViewer\n//\n// The host's own built-in contributions use the reserved `core` domain, so the\n// address space has exactly one shape: trove+contrib:core/workbench/explorer.delete\n\nimport { TroveError } from '../errors.js';\n\nexport const CONTRIB_SCHEME = 'trove+contrib:';\nexport const CORE_DOMAIN = 'core';\n\n// A DNS-ish domain (lowercase labels, dots) — or the reserved `core`.\nconst DOMAIN_RE = /^(?:core|[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+)$/;\n// A plugin or contribution name: lowercase-ish segment, no slashes or spaces.\nconst NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;\n\nexport function isValidDomain(domain) {\n return typeof domain === 'string' && DOMAIN_RE.test(domain);\n}\nexport function isValidName(name) {\n return typeof name === 'string' && NAME_RE.test(name);\n}\n\n/** The plugin's identity: `<domain>/<name>`. Stable across versions. */\nexport function pluginId(manifest) {\n assertIdentity(manifest);\n return `${manifest.domain}/${manifest.name}`;\n}\n\n/** The full URI for one of a plugin's contributions. */\nexport function contribUri(manifest, contributionName) {\n if (!isValidName(contributionName)) {\n throw TroveError.invalid(`Invalid contribution name \"${contributionName}\"`);\n }\n return `${CONTRIB_SCHEME}${pluginId(manifest)}/${contributionName}`;\n}\n\n// The host's own contributions are addressed exactly like a plugin's: the reserved\n// `core` domain, and `workbench` as the package within it. So the address space has\n// one shape everywhere, and core names share a single namespace just as a plugin's do.\nexport const CORE_PACKAGE = 'workbench';\n\n/** A URI for one of the host's own built-in contributions. */\nexport function coreUri(name) {\n return `${CONTRIB_SCHEME}${CORE_DOMAIN}/${CORE_PACKAGE}/${name}`;\n}\n\n/** Parse a contribution URI into its parts, or null if it isn't one. */\nexport function parseContribUri(uri) {\n if (typeof uri !== 'string' || !uri.startsWith(CONTRIB_SCHEME)) return null;\n const rest = uri.slice(CONTRIB_SCHEME.length);\n const i = rest.indexOf('/');\n const j = rest.indexOf('/', i + 1);\n if (i < 0 || j < 0) return null;\n const domain = rest.slice(0, i);\n const plugin = rest.slice(i + 1, j);\n const name = rest.slice(j + 1);\n if (!domain || !plugin || !name) return null;\n return { domain, plugin, name, pluginId: `${domain}/${plugin}` };\n}\n\n/** Whether `uri` belongs to the plugin described by `manifest`. */\nexport function ownsUri(manifest, uri) {\n const p = parseContribUri(uri);\n return !!p && p.pluginId === `${manifest.domain}/${manifest.name}`;\n}\n\n/**\n * Every package must carry a verifiable identity: a domain it belongs to and a name\n * within that domain. Anonymous/self-identified packages are not installable.\n */\nexport function assertIdentity(manifest) {\n if (!isValidDomain(manifest?.domain)) {\n throw TroveError.invalid(`Plugin manifest needs a valid \"domain\" (got ${JSON.stringify(manifest?.domain)})`);\n }\n if (!isValidName(manifest?.name)) {\n throw TroveError.invalid(`Plugin manifest needs a valid \"name\" (got ${JSON.stringify(manifest?.name)})`);\n }\n if (manifest.domain === CORE_DOMAIN) {\n throw TroveError.invalid('The \"core\" domain is reserved for built-in contributions');\n }\n}\n",
|
|
16
|
+
"// What a package contributes — declared in its manifest as ONE map of\n// `name -> contribution`, where each contribution states its own `type` and that\n// type's options:\n//\n// \"contributes\": {\n// \"player\": { \"type\": \"opener\", \"title\": \"Demo Player\",\n// \"match\": { \"ext\": [\".demo\"] }, \"entry\": \"src/openers/player.js\" },\n// \"pdf\": { \"type\": \"indexer\", \"match\": { \"ext\": [\".pdf\"] }, \"entry\": \"src/indexers/pdf.js\" },\n// \"tap\": { \"type\": \"command\", \"title\": \"Demo: Tap\" },\n// \"status\": { \"type\": \"statusItem\", \"slot\": \"right\" },\n// \"busy\": { \"type\": \"register\", \"default\": false },\n// \"keys\": { \"type\": \"keymap\", \"path\": \"keymaps/default.json\" }\n// }\n//\n// One map, not a collection per kind, because the kinds never needed separate\n// namespaces — they needed a shared one. Here \"status\" is a single name under this\n// plugin, so its status item, its register and its command can't shadow each other,\n// and `trove+contrib:acme.com/docs/status` names exactly one thing in the world.\n//\n// Each contribution's `entry` (openers, indexers) points into the plugin's ONE module\n// tree — they are not nested sub-packages, so everything in a plugin shares modules\n// and code. What gets opened or indexed depends only on which entry module runs.\n\nimport { TroveError } from '../errors.js';\nimport { contribUri, isValidName, pluginId } from './identity.js';\n\n/** Every contribution type a package may declare, with how its options are normalized. */\nconst TYPES = {\n // A command in the palette. Its handler lives in the plugin's primary frame.\n command: {\n normalize: (c) => ({\n title: c.title || null, category: c.category || null, icon: c.icon || null,\n when: c.when || null, palette: c.palette !== false, offline: !!c.offline,\n }),\n },\n // A viewer for matching files, rendered by `entry` in its own sandboxed frame.\n opener: {\n needsEntry: true,\n normalize: (c) => ({\n title: c.title || null, match: normalizeMatch(c.match), priority: c.priority ?? 50,\n when: c.when || null, offline: !!c.offline, dock: c.dock || null,\n }),\n },\n // Indexes matching files. ALWAYS runs on the server (see serverIndexers).\n indexer: {\n needsEntry: true,\n normalize: (c) => ({ title: c.title || null, match: normalizeMatch(c.match) }),\n },\n // A slot in the status bar. The plugin pushes content into it at runtime; for now\n // the only content type is sanitized HTML.\n statusItem: {\n normalize: (c) => {\n const slot = c.slot === 'left' ? 'left' : 'right';\n const render = c.render || 'html';\n if (render !== 'html') throw TroveError.invalid(`statusItem \"render\" must be \"html\" (got ${JSON.stringify(render)})`);\n return { slot, render, order: c.order ?? 0, when: c.when || null, offline: !!c.offline, command: c.command || null };\n },\n },\n // A context value slot the plugin drives at runtime, referenceable from when-clauses\n // in keymaps, commands and other contributions (the equivalent of a VS Code context key).\n register: {\n normalize: (c) => ({ default: c.default ?? null, description: c.description || null }),\n },\n // A keymap JSON file in the package: [{ key, command, when?, args? }, …].\n keymap: {\n normalize: (c) => {\n if (!c.path || typeof c.path !== 'string') throw TroveError.invalid('keymap contribution needs a \"path\" to a JSON file in the package');\n return { path: c.path };\n },\n },\n};\n\nexport const CONTRIBUTION_TYPES = Object.keys(TYPES);\n\n/**\n * Every contribution a manifest declares, normalized and addressed by URI.\n * Throws INVALID on a malformed declaration — a package that lies about what it\n * contributes must not install, because the install review is what the user approved.\n * @returns {Array<{name:string, uri:string, type:string, entry?:string}>}\n */\nexport function declaredContributions(manifest) {\n const map = manifest?.contributes;\n if (map == null) return [];\n if (typeof map !== 'object' || Array.isArray(map)) {\n throw TroveError.invalid('manifest \"contributes\" must be a map of name -> contribution');\n }\n const out = [];\n for (const [name, raw] of Object.entries(map)) {\n if (!raw || typeof raw !== 'object') throw TroveError.invalid(`Contribution \"${name}\" must be an object`);\n if (!isValidName(name)) throw TroveError.invalid(`Invalid contribution name \"${name}\"`);\n const spec = TYPES[raw.type];\n if (!spec) {\n throw TroveError.invalid(`Contribution \"${name}\" has unknown type ${JSON.stringify(raw.type)} (expected one of ${CONTRIBUTION_TYPES.join(', ')})`);\n }\n const entry = raw.entry || manifest.entry;\n if (spec.needsEntry && !entry) throw TroveError.invalid(`Contribution \"${name}\" (${raw.type}) needs an \"entry\" module`);\n out.push({\n name,\n uri: contribUri(manifest, name),\n pluginId: pluginId(manifest),\n type: raw.type,\n ...spec.normalize(raw),\n ...(spec.needsEntry ? { entry } : {}),\n });\n }\n return out;\n}\n\n/** Just the declared contributions of one type. */\nexport function contributionsOfType(manifest, type) {\n return declaredContributions(manifest).filter((c) => c.type === type);\n}\n\n/**\n * The indexers the server will run. Indexing is a property of the DRIVE, not of\n * whoever happens to have a tab open: it must happen once per upload regardless of\n * which client did the uploading, so an indexer contribution is always server-side.\n * (Not to be confused with the `indexer` capability, which lets a plugin push its own\n * contributions for a node it's looking at — that one is a client action.)\n */\nexport function serverIndexers(manifest) {\n return contributionsOfType(manifest, 'indexer').map((i) => ({\n id: i.uri, name: i.name, title: i.title || i.name, match: i.match, entry: i.entry,\n }));\n}\n\n/** Openers a package declares, each pointing at the entry module that renders it. */\nexport function declaredOpeners(manifest) {\n return contributionsOfType(manifest, 'opener');\n}\n\n/**\n * Parse a keymap file's contents: a JSON array of `{ key, command, when?, args? }`\n * (an object with a `bindings` array is also accepted). Entries missing a key or a\n * command are dropped rather than failing the whole map — one bad line shouldn't cost\n * a plugin every other shortcut it ships.\n */\nexport function parseKeymap(text) {\n let doc;\n try {\n doc = JSON.parse(text);\n } catch (err) {\n throw TroveError.invalid('Keymap file is not valid JSON', { cause: err });\n }\n const list = Array.isArray(doc) ? doc : Array.isArray(doc?.bindings) ? doc.bindings : null;\n if (!list) throw TroveError.invalid('Keymap file must be a JSON array of bindings');\n return list\n .filter((b) => b && typeof b.key === 'string' && typeof b.command === 'string')\n .map((b) => ({\n key: b.key, command: b.command,\n when: typeof b.when === 'string' ? b.when : null,\n args: Array.isArray(b.args) ? b.args : undefined,\n }));\n}\n\n/**\n * A file-match selector, coerced into the shape `selectorMatches` actually indexes.\n *\n * It was passed through verbatim, so `\"match\": { \"ext\": \".demo\" }` — a string where an\n * array belongs, which no validation rejected — installed cleanly and then threw\n * `(selector.ext || []).some is not a function` at selection time. Server-side the\n * throw was swallowed, so the plugin backfilled nothing and indexed nothing with no\n * error anywhere; in the browser `openersFor` is unguarded, so ONE bad declaration\n * broke opener selection for every file in the drive.\n */\nfunction normalizeMatch(match) {\n if (!match || typeof match !== 'object' || Array.isArray(match)) return {};\n const list = (v) => (v == null ? undefined : (Array.isArray(v) ? v : [v]).filter((x) => typeof x === 'string'));\n const out = {};\n const ext = list(match.ext);\n const mime = list(match.mime ?? match.contentType);\n if (ext) out.ext = ext;\n if (mime) out.mime = mime;\n // A `match` function can only come from in-process code, never from a manifest.\n if (typeof match.match === 'function') out.match = match.match;\n return out;\n}\n",
|
|
17
|
+
"// ContributionRegistry — ONE map of `uri -> contribution`, where every contribution\n// carries its own `type` and that type's options.\n//\n// There used to be a separate collection per kind (commands, openers, statusItems…),\n// which meant a name could mean different things in different collections and nothing\n// tied a contribution back to a verifiable owner. Now there is a single address space:\n//\n// trove+contrib:acme.com/docs/pdfViewer { type: 'opener', match, entry }\n// trove+contrib:acme.com/docs/status { type: 'statusItem', slot, render }\n// trove+contrib:acme.com/docs/busy { type: 'register', default }\n// trove+contrib:acme.com/docs/keys { type: 'keymap', bindings }\n// trove+contrib:core/workbench/… built-ins, reserved `core` domain\n//\n// So a plugin's opener, status slot, register and command may all be called \"status\"\n// without colliding — with each other, or with another plugin's.\n//\n// Contribution types (see @3sln/trove/core/plugins/contributions.js for the manifest form):\n// command { title, category?, icon?, when?, offline?, palette? }\n// opener { title, match:{ext,mime}, entry, priority?, offline?, dock? }\n// indexer { title, match, entry } (declared here; the SERVER runs it)\n// statusItem { slot:'left'|'right', render:'html', order?, when?, offline? }\n// register { default?, description? } (a context value slot)\n// keymap { bindings:[{key, command, when?, args?}] }\n// view { title, icon?, match?, priority?, render, move? }\n\nimport { cell, derive } from '../runtime.js';\nimport { selectorMatches } from '@3sln/trove/core/util.js';\nimport { parseContribUri, coreUri, CONTRIB_SCHEME } from '@3sln/trove/core/plugins/identity.js';\nimport { CONTRIBUTION_TYPES as PACKAGE_TYPES } from '@3sln/trove/core/plugins/contributions.js';\n\n// Everything a package can declare, plus the workbench's own. `view` — how a list of\n// results is drawn — is the second kind: it is not part of the package format, so core\n// (which is where the manifest is parsed) has no reason to know the word.\nexport const CONTRIBUTION_TYPES = [...PACKAGE_TYPES, 'view'];\n\n/**\n * Address normalization — the one rule: a bare name belongs to the host's reserved\n * `core` domain, anything from a plugin is always fully qualified. So the workbench\n * keeps saying `exec('explorer.delete')` while every contribution still has a real,\n * unambiguous URI (`trove+contrib:core/workbench/explorer.delete`).\n */\nexport function toUri(nameOrUri) {\n return String(nameOrUri).startsWith(CONTRIB_SCHEME) ? nameOrUri : coreUri(nameOrUri);\n}\n\nexport class ContributionRegistry {\n constructor() {\n this.items = new Map(); // uri -> { uri, type, id, name, pluginId, ...options }\n this.cell = cell([]);\n this.byType = new Map(); // type -> derived cell (lazily created)\n }\n\n #emit() {\n this.cell.setValue([...this.items.values()]);\n }\n\n /**\n * Register one contribution at its URI. Returns a dispose fn.\n * @param {string} nameOrUri a bare core name, or trove+contrib:<domain>/<plugin>/<name>\n * @param {{type: string}} contribution\n */\n register(nameOrUri, contribution) {\n const type = contribution?.type;\n if (!CONTRIBUTION_TYPES.includes(type)) {\n throw new Error(`Unknown contribution type \"${type}\" for ${nameOrUri}`);\n }\n const uri = toUri(nameOrUri);\n const parsed = parseContribUri(uri);\n if (!parsed) throw new Error(`Not a contribution URI: ${uri}`);\n const pluginId = contribution.pluginId ?? (parsed.domain === 'core' ? null : parsed.pluginId);\n const entry = {\n ...contribution, uri, type,\n // `id` is the short name the workbench uses to address it (bare for built-ins,\n // the URI for plugin contributions, which are only ever addressed fully).\n id: parsed.domain === 'core' ? parsed.name : uri,\n name: parsed.name,\n pluginId,\n };\n this.items.set(uri, entry);\n this.#emit();\n return () => this.unregister(uri);\n }\n\n /** Merge new options into an existing contribution (a plugin driving its own slot). */\n update(nameOrUri, patch) {\n const uri = toUri(nameOrUri);\n const cur = this.items.get(uri);\n if (!cur) return false;\n this.items.set(uri, { ...cur, ...patch });\n this.#emit();\n return true;\n }\n\n unregister(nameOrUri) {\n if (this.items.delete(toUri(nameOrUri))) this.#emit();\n }\n /** Drop everything a plugin contributed (uninstall / reload). */\n unregisterPlugin(pluginId) {\n let changed = false;\n for (const [uri, c] of this.items) if (c.pluginId === pluginId) { this.items.delete(uri); changed = true; }\n if (changed) this.#emit();\n }\n\n get(nameOrUri) {\n return nameOrUri ? this.items.get(toUri(nameOrUri)) || null : null;\n }\n all() {\n return [...this.items.values()];\n }\n ofType(type) {\n return this.all().filter((c) => c.type === type);\n }\n /** One plugin's contributions, optionally of a single type. */\n ofPlugin(pluginId, type) {\n return this.all().filter((c) => c.pluginId === pluginId && (!type || c.type === type));\n }\n observe() {\n return this.cell;\n }\n /**\n * A reactive view of one type (status items, openers, …).\n *\n * Derived rather than a second subject fanned out to by hand: `derive` recomputes\n * from the one list, and a watcher of the result is only re-rendered when its own\n * slice actually differs — so registering an opener no longer redraws the status bar.\n */\n observeType(type) {\n let view = this.byType.get(type);\n if (!view) {\n view = derive([this.cell], (all) => all.filter((c) => c.type === type));\n this.byType.set(type, view);\n }\n return view;\n }\n\n // --- typed lookups ---------------------------------------------------------\n\n /** Every opener whose selector matches `node`, best (highest priority) first. */\n openersFor(node) {\n return this.ofType('opener')\n .filter((o) => selectorMatches(o.match, node))\n .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n\n /** Pick the best opener for a node, honouring `when` and availability. */\n openerFor(node, evaluate, isAvailable) {\n return this.openersFor(node)\n .find((o) => (!o.when || evaluate(o.when)) && (!isAvailable || isAvailable(o))) || null;\n }\n\n /** Every keybinding from every registered keymap, in registration order. */\n keybindings() {\n return this.ofType('keymap').flatMap((k) =>\n (k.bindings || []).map((b) => ({ ...b, keymap: k.uri, pluginId: k.pluginId })));\n }\n}\n",
|
|
18
|
+
"// A parser/evaluator for \"when\" clauses — the same conditional mini-language VS\n// Code uses to gate commands, keybindings, and menu items on UI state. It reads\n// a context (a flat key→value map from the ContextKeyService) and returns a\n// boolean.\n//\n// Supported: bare keys (truthy), !key, ==, !=, >, <, >=, <=, =~ /re/, && , ||,\n// parentheses, string/number/boolean literals. Expressions are parsed once into\n// a predicate function and cached, so evaluation on every keydown is cheap.\n//\n// A key is either a core context key (`view.active`, `explorer.hasSelection`) or a\n// contribution URI naming a plugin's `register` — `trove+contrib:acme.com/docs/busy`.\n// Registers are addressed by their full URI precisely because they're contributions\n// like any other: a plugin can only ever gate on a value someone declared and owns.\n\nconst cache = new Map();\n\nexport function compileWhen(expr) {\n if (!expr) return () => true;\n if (cache.has(expr)) return cache.get(expr);\n let fn;\n try {\n fn = new Parser(expr).parseExpression();\n } catch (err) {\n console.warn(`Invalid when clause: \"${expr}\" — ${err.message}`);\n fn = () => false;\n }\n cache.set(expr, fn);\n return fn;\n}\n\nexport function evaluateWhen(expr, ctx) {\n return compileWhen(expr)(ctx || {});\n}\n\n// Note the `trove+contrib:` alternative comes before the regex-literal one: a URI's\n// slashes would otherwise start a /…/ literal and swallow the rest of the expression.\nconst TOKEN = /\\s*(=~|==|!=|>=|<=|&&|\\|\\||[()!<>]|trove\\+contrib:[A-Za-z0-9_./-]+|\\/(?:\\\\.|[^/])*\\/|'(?:\\\\.|[^'])*'|\"(?:\\\\.|[^\"])*\"|[A-Za-z0-9_.:-]+)/y;\n\nclass Parser {\n constructor(src) {\n this.src = src;\n this.tokens = this.#lex(src);\n this.pos = 0;\n }\n #lex(src) {\n const out = [];\n let i = 0;\n TOKEN.lastIndex = 0;\n while (i < src.length) {\n TOKEN.lastIndex = i;\n const m = TOKEN.exec(src);\n if (!m || m.index == null) {\n if (!src.slice(i).trim()) break;\n throw new Error(`Unexpected \"${src.slice(i)}\"`);\n }\n out.push(m[1]);\n i = TOKEN.lastIndex;\n }\n return out;\n }\n #peek() {\n return this.tokens[this.pos];\n }\n #next() {\n return this.tokens[this.pos++];\n }\n #eat(t) {\n if (this.#peek() !== t) throw new Error(`Expected \"${t}\"`);\n this.pos++;\n }\n\n parseExpression() {\n const fn = this.#or();\n if (this.pos !== this.tokens.length) throw new Error(`Trailing \"${this.#peek()}\"`);\n return fn;\n }\n #or() {\n let left = this.#and();\n while (this.#peek() === '||') {\n this.#next();\n const right = this.#and();\n const l = left;\n left = (c) => l(c) || right(c);\n }\n return left;\n }\n #and() {\n let left = this.#unary();\n while (this.#peek() === '&&') {\n this.#next();\n const right = this.#unary();\n const l = left;\n left = (c) => l(c) && right(c);\n }\n return left;\n }\n #unary() {\n if (this.#peek() === '!') {\n this.#next();\n const operand = this.#unary();\n return (c) => !operand(c);\n }\n return this.#comparison();\n }\n #comparison() {\n const left = this.#primary();\n const op = this.#peek();\n if (['==', '!=', '>=', '<=', '>', '<', '=~'].includes(op)) {\n this.#next();\n if (op === '=~') {\n const reTok = this.#next();\n const re = parseRegex(reTok);\n return (c) => re.test(String(left(c) ?? ''));\n }\n const right = this.#primary();\n return (c) => compare(op, left(c), right(c));\n }\n // Bare value → truthiness.\n return (c) => truthy(left(c));\n }\n #primary() {\n const t = this.#next();\n if (t === undefined) throw new Error('Unexpected end');\n if (t === '(') {\n const inner = this.#or();\n this.#eat(')');\n return inner;\n }\n if (t === 'true') return () => true;\n if (t === 'false') return () => false;\n if (/^-?\\d+(\\.\\d+)?$/.test(t)) {\n const n = Number(t);\n return () => n;\n }\n if (t[0] === \"'\" || t[0] === '\"') {\n const s = t.slice(1, -1).replace(/\\\\(.)/g, '$1');\n return () => s;\n }\n // A context key.\n return (c) => c[t];\n }\n}\n\nfunction parseRegex(tok) {\n const m = /^\\/((?:\\\\.|[^/])*)\\/([a-z]*)$/.exec(tok);\n if (!m) throw new Error(`Bad regex ${tok}`);\n return new RegExp(m[1], m[2]);\n}\nfunction truthy(v) {\n return !(v === undefined || v === null || v === false || v === '' || v === 0);\n}\nfunction compare(op, a, b) {\n switch (op) {\n case '==': return a == b; // eslint-disable-line eqeqeq\n case '!=': return a != b; // eslint-disable-line eqeqeq\n case '>': return a > b;\n case '<': return a < b;\n case '>=': return a >= b;\n case '<=': return a <= b;\n }\n return false;\n}\n",
|
|
19
|
+
"// ContextKeyService — the reactive bag of boolean/string/number flags that\n// describe the workbench's current state (which view is focused, whether a file\n// is open, its type, whether the palette is showing…). when-clauses evaluate\n// against it, so commands/keybindings/menus light up and dim as state changes.\n//\n// It's a single cell of the whole context object, so any consumer can `watch` it and\n// re-render, and keybinding resolution reads a plain snapshot.\n// Plugins get a *scoped* setter (keys they set are namespaced under their id) so\n// they can drive their own when-clauses without stomping core keys.\n\nimport { cell } from '../runtime.js';\nimport { evaluateWhen } from './whenclause.js';\n\nexport class ContextKeyService {\n constructor(initial = {}) {\n this.state = {\n platform: navigatorPlatform(),\n isMac: /mac/i.test(navigatorPlatform()),\n ...initial,\n };\n this.cell = cell(this.state);\n }\n\n get(key) {\n return this.state[key];\n }\n snapshot() {\n return this.state;\n }\n observe() {\n return this.cell;\n }\n\n set(key, value) {\n if (this.state[key] === value) return;\n this.state = { ...this.state, [key]: value };\n this.cell.setValue(this.state);\n }\n setMany(obj) {\n let changed = false;\n const next = { ...this.state };\n for (const [k, v] of Object.entries(obj)) {\n if (next[k] !== v) {\n next[k] = v;\n changed = true;\n }\n }\n if (changed) {\n this.state = next;\n this.cell.setValue(next);\n }\n }\n remove(key) {\n if (!(key in this.state)) return;\n const { [key]: _drop, ...rest } = this.state;\n this.state = rest;\n this.cell.setValue(rest);\n }\n\n /** True if `whenExpr` holds against the current context. */\n evaluate(whenExpr) {\n return evaluateWhen(whenExpr, this.state);\n }\n\n /** A setter namespaced under a plugin id, so plugin keys can't collide. */\n scopedFor(pluginId) {\n const prefix = `${pluginId}.`;\n return {\n set: (key, value) => this.set(prefix + key, value),\n remove: (key) => this.remove(prefix + key),\n };\n }\n}\n\nfunction navigatorPlatform() {\n return typeof navigator !== 'undefined' ? navigator.platform || navigator.userAgent || '' : '';\n}\n",
|
|
20
|
+
"// CommandService — the one way anything happens in the workbench. Buttons,\n// menu items, keybindings, the palette, and plugins all funnel through\n// `execute(id, ...args)`. A command's metadata (title/category/icon/when/palette)\n// lives in the contribution registry; its handler lives here (core handlers are\n// plain functions, plugin handlers proxy over RPC). Errors surface as a\n// notification instead of a silent console log — failure is always visible.\n\nexport class CommandService {\n /**\n * @param {import('./contributions.js').ContributionRegistry} contributions\n * @param {import('./context.js').ContextKeyService} context\n * @param {import('./notifications.js').NotificationService} notifications\n */\n constructor(contributions, context, notifications) {\n this.contributions = contributions;\n this.context = context;\n this.notifications = notifications;\n this.handlers = new Map(); // id -> fn\n }\n\n /**\n * Register a command. Accepts either register(id, handler) or a full spec\n * object { id, title, handler, category, icon, when, palette }.\n */\n register(idOrSpec, handler) {\n const spec = typeof idOrSpec === 'string' ? { id: idOrSpec, handler } : idOrSpec;\n if (spec.handler) this.handlers.set(spec.id, spec.handler);\n const dispose = this.contributions.register(spec.id, {\n type: 'command',\n title: spec.title ?? spec.id,\n category: spec.category,\n icon: spec.icon,\n when: spec.when,\n palette: spec.palette ?? true,\n // Provenance + offline capability, for availability filtering.\n pluginId: spec.pluginId,\n offline: spec.offline,\n });\n return () => {\n this.handlers.delete(spec.id);\n dispose();\n };\n }\n\n has(id) {\n return this.handlers.has(id);\n }\n\n isEnabled(id) {\n const cmd = this.contributions.get(id);\n if (cmd?.when && !this.context.evaluate(cmd.when)) return false;\n return this.isAvailable(cmd);\n }\n\n /** A command is available unless it's a plugin command that's currently offline/dead. */\n isAvailable(cmd) {\n if (!cmd) return true;\n return this.availability ? this.availability(cmd) : true;\n }\n\n async execute(id, ...args) {\n const handler = this.handlers.get(id);\n if (!handler) {\n this.notifications.error(`Command not found: ${id}`);\n return;\n }\n const cmd = this.contributions.get(id);\n if (!this.isAvailable(cmd)) {\n this.notifications.warn(`“${cmd?.title || id}” isn’t available${this.availability ? ' offline' : ''} right now.`);\n return;\n }\n if (!this.isEnabled(id)) return; // gated by when-clause\n try {\n return await handler(...args);\n } catch (err) {\n console.error(`Command ${id} failed`, err);\n this.notifications.error(err?.message || `Command failed: ${id}`);\n throw err;\n }\n }\n\n /** Commands eligible for the palette right now (respecting when-clauses). */\n paletteCommands() {\n return this.contributions\n .ofType('command')\n .filter((c) => c.palette !== false && (!c.when || this.context.evaluate(c.when)))\n .sort((a, b) => (a.category || '').localeCompare(b.category || '') || a.title.localeCompare(b.title));\n }\n}\n",
|
|
21
|
+
"// KeybindingService — turns keystrokes into command executions, honouring\n// when-clauses and chords (multi-key sequences like \"ctrl+k ctrl+s\").\n//\n// Bindings come from `keymap` CONTRIBUTIONS: the host contributes one built-in keymap,\n// and a plugin contributes one by declaring `{ type: 'keymap', path: 'keymaps/x.json' }`\n// in its manifest (the host reads and validates that file at install). So core and\n// plugins share exactly one keymap and the shortcuts UI can render all of it.\n// User rebinds live in settings under `keybindings.overrides` (command -> key) and win\n// over whatever a keymap declared. Typing in an input is respected: only bindings with\n// a modifier (or Escape) fire while editing.\n\nexport const OVERRIDES_KEY = 'keybindings.overrides';\n\nexport function normalizeKey(str) {\n // Canonical form: sorted modifiers + key, lowercased. \"Cmd+Shift+P\" → \"meta+shift+p\"\n const parts = str.trim().toLowerCase().split('+').map((p) => p.trim());\n const mods = new Set();\n let key = '';\n for (const p of parts) {\n if (['ctrl', 'control'].includes(p)) mods.add('ctrl');\n else if (['cmd', 'meta', 'super', 'win'].includes(p)) mods.add('meta');\n else if (p === 'alt' || p === 'option') mods.add('alt');\n else if (p === 'shift') mods.add('shift');\n else if (['ctrlcmd', 'mod'].includes(p)) mods.add(isMac() ? 'meta' : 'ctrl');\n else key = p;\n }\n const order = ['ctrl', 'meta', 'alt', 'shift'];\n return [...order.filter((m) => mods.has(m)), key].filter(Boolean).join('+');\n}\n\nexport function eventToKey(e) {\n const mods = [];\n if (e.ctrlKey) mods.push('ctrl');\n if (e.metaKey) mods.push('meta');\n if (e.altKey) mods.push('alt');\n if (e.shiftKey) mods.push('shift');\n let key = e.key.toLowerCase();\n const named = { ' ': 'space', escape: 'escape', enter: 'enter', arrowup: 'up', arrowdown: 'down', arrowleft: 'left', arrowright: 'right' };\n key = named[key] || key;\n return [...mods.filter((m) => key !== m), key].join('+');\n}\n\nexport class KeybindingService {\n /**\n * @param {import('./contributions.js').ContributionRegistry} contributions\n * @param {import('./commands.js').CommandService} commands\n * @param {import('./context.js').ContextKeyService} context\n * @param {import('./settings.js').SettingsService} [settings] source of user rebinds\n */\n constructor(contributions, commands, context, settings = null) {\n this.contributions = contributions;\n this.commands = commands;\n this.context = context;\n this.settings = settings;\n this.chordPrefix = null;\n this.chordTimer = null;\n this._onKeyDown = this.#onKeyDown.bind(this);\n }\n\n install(target = window) {\n target.addEventListener('keydown', this._onKeyDown);\n return () => target.removeEventListener('keydown', this._onKeyDown);\n }\n\n /**\n * User rebinds, keyed by BINDING rather than by command.\n *\n * Keyed by command, one shortcut could not be changed without changing the others:\n * `workbench.view.home` ships bound twice (⌘⇧F and ⌘⇧E), so rebinding either\n * collapsed both onto the new chord — two identical rows in Settings and one shortcut\n * silently gone. A binding's identity is (command, the key it was DECLARED with),\n * which is stable across restarts and unique per row.\n */\n overrides() {\n return this.settings?.get(OVERRIDES_KEY) || {};\n }\n\n /** The override key for one declared binding. */\n static bindingId(binding) {\n return `${binding.command}\\u0000${normalizeKey(binding.defaultKey || binding.key)}`;\n }\n\n /**\n * Rebind one BINDING (null clears it back to what its keymap declared).\n * @param {{command: string, defaultKey?: string, key: string}|string} binding\n * a resolved binding, or a bare command id for the legacy single-binding case.\n */\n rebind(binding, key) {\n if (!this.settings) return;\n const id = typeof binding === 'string'\n ? this.#idForCommand(binding)\n : KeybindingService.bindingId(binding);\n if (!id) return;\n const next = { ...this.overrides() };\n if (key) next[id] = key; else delete next[id];\n this.settings.set(OVERRIDES_KEY, next);\n }\n\n /** Resolve a bare command id to its (single) binding id, for older callers. */\n #idForCommand(command) {\n const b = this.contributions.keybindings().find((x) => x?.command === command && x.key);\n return b ? KeybindingService.bindingId({ ...b, defaultKey: b.key }) : null;\n }\n\n /** Current effective bindings (every keymap's, plus user overrides). */\n resolved() {\n const overrides = this.overrides();\n return this.contributions.keybindings()\n .filter((b) => b?.key && b.command)\n .map((b) => {\n // `defaultKey` is what the keymap declared — the row's stable identity, and what\n // \"reset\" restores to.\n const defaultKey = normalizeKey(b.key);\n const id = KeybindingService.bindingId({ command: b.command, defaultKey });\n return { ...b, defaultKey, bindingId: id, key: normalizeKey(overrides[id] || b.key) };\n });\n }\n\n #matchFor(keyChord) {\n const snapshot = this.context.snapshot();\n // Later registrations win (plugins can override), so scan in reverse.\n const list = this.resolved();\n for (let i = list.length - 1; i >= 0; i--) {\n const b = list[i];\n if (b.key !== keyChord) continue;\n if (b.when && !this.context.evaluate(b.when)) continue;\n return b;\n }\n return null;\n }\n\n #onKeyDown(e) {\n if (e.defaultPrevented) return;\n const chord = eventToKey(e);\n const hasMod = e.ctrlKey || e.metaKey || e.altKey;\n const typing = isTyping(e.target);\n\n // While typing, only modifier chords and Escape are eligible (don't eat text).\n if (typing && !hasMod && e.key !== 'Escape') {\n this.#resetChord();\n return;\n }\n\n const full = this.chordPrefix ? `${this.chordPrefix} ${chord}` : chord;\n const binding = this.#matchFor(full);\n\n if (binding) {\n e.preventDefault();\n this.#resetChord();\n this.commands.execute(binding.command, ...(binding.args || []));\n return;\n }\n\n // Could this be the first key of a chord?\n if (!this.chordPrefix && this.resolved().some((b) => b.key.startsWith(chord + ' '))) {\n e.preventDefault();\n this.chordPrefix = chord;\n this.chordTimer = setTimeout(() => this.#resetChord(), 1500);\n return;\n }\n this.#resetChord();\n }\n\n #resetChord() {\n this.chordPrefix = null;\n if (this.chordTimer) clearTimeout(this.chordTimer);\n this.chordTimer = null;\n }\n\n /** Human-readable label for a command's binding, for menus/tooltips. */\n labelFor(command) {\n const b = this.resolved().find((x) => x.command === command);\n return b ? prettyKey(b.key) : null;\n }\n}\n\nfunction isTyping(el) {\n if (!el) return false;\n const tag = el.tagName;\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;\n}\nfunction isMac() {\n return typeof navigator !== 'undefined' && /mac/i.test(navigator.platform || navigator.userAgent || '');\n}\nexport function prettyKey(key) {\n const mac = isMac();\n const map = mac\n ? { ctrl: '⌃', meta: '⌘', alt: '⌥', shift: '⇧', enter: '↵', escape: 'esc', up: '↑', down: '↓', left: '←', right: '→', space: '␣' }\n : { ctrl: 'Ctrl', meta: 'Win', alt: 'Alt', shift: 'Shift', enter: 'Enter', escape: 'Esc', up: '↑', down: '↓', left: '←', right: '→', space: 'Space' };\n return key\n .split(' ')\n .map((chord) =>\n chord\n .split('+')\n .map((p) => map[p] || (p.length === 1 ? p.toUpperCase() : p[0].toUpperCase() + p.slice(1)))\n .join(mac ? '' : '+'),\n )\n .join(' ');\n}\n",
|
|
22
|
+
"// SettingsService — schema-driven, persisted, reactive settings (VS Code's\n// settings model). Core and plugins register typed setting schemas; values are\n// stored in localStorage, namespaced, and merged over defaults. The Settings UI\n// is generated from the schema, so adding a setting never means touching UI code.\n// Plugin keys are namespaced under the plugin id.\n\nimport { cell } from '../runtime.js';\n\nconst STORAGE_KEY = 'trove.settings';\n\nexport class SettingsService {\n constructor() {\n this.schema = new Map(); // key -> { type, default, title, description, enum?, minimum?, maximum?, category, order }\n this.values = read();\n this.cell = cell(this.effective());\n }\n\n /**\n * Register one or more settings. Each: { key, type, default, title,\n * description?, enum?, enumLabels?, minimum?, maximum?, category?, order? }\n */\n register(schemas) {\n for (const s of [].concat(schemas)) this.schema.set(s.key, s);\n this.cell.setValue(this.effective());\n return () => {\n for (const s of [].concat(schemas)) this.schema.delete(s.key);\n this.cell.setValue(this.effective());\n };\n }\n\n get(key) {\n if (key in this.values) return this.values[key];\n return this.schema.get(key)?.default;\n }\n\n set(key, value) {\n const schema = this.schema.get(key);\n if (schema && value === schema.default) delete this.values[key];\n else this.values[key] = value;\n write(this.values);\n this.cell.setValue(this.effective());\n }\n\n reset(key) {\n delete this.values[key];\n write(this.values);\n this.cell.setValue(this.effective());\n }\n\n /** Full resolved values (defaults ⊕ overrides). */\n effective() {\n const out = {};\n for (const [key, s] of this.schema) out[key] = key in this.values ? this.values[key] : s.default;\n // Include stored keys with no (current) schema, so unknown/plugin values survive.\n for (const [key, v] of Object.entries(this.values)) if (!(key in out)) out[key] = v;\n return out;\n }\n\n observe() {\n return this.cell;\n }\n\n /** Schema entries grouped by category, for the Settings UI. */\n grouped() {\n const groups = new Map();\n for (const s of [...this.schema.values()].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))) {\n // `hidden` settings are state the app keeps under the settings key, not choices a\n // person makes — the opener associations, for one, which are edited through the\n // opener chooser and have their own section below. Rendering them anyway put a row\n // reading \"[object Object]\" at the top of Settings.\n if (s.hidden) continue;\n const cat = s.category || 'General';\n if (!groups.has(cat)) groups.set(cat, []);\n groups.get(cat).push({ ...s, value: this.get(s.key) });\n }\n return [...groups.entries()].map(([category, items]) => ({ category, items }));\n }\n\n scopedFor(pluginId) {\n const prefix = `${pluginId}.`;\n return {\n register: (schemas) =>\n this.register([].concat(schemas).map((s) => ({ ...s, key: prefix + s.key, category: s.category || pluginId }))),\n get: (key) => this.get(prefix + key),\n set: (key, value) => this.set(prefix + key, value),\n };\n }\n}\n\nfunction read() {\n try {\n return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};\n } catch {\n return {};\n }\n}\nfunction write(values) {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(values));\n } catch { /* quota / private mode */ }\n}\n",
|
|
23
|
+
"// NotificationService — transient toasts + a small log. Everything user-facing\n// that fails routes here so problems are never silent. Reactive: the status/UI\n// watches `observe()`.\n\nimport { cell } from '../runtime.js';\n\nlet seq = 0;\n\nexport class NotificationService {\n constructor() {\n this.items = [];\n this.cell = cell([]);\n }\n observe() {\n return this.cell;\n }\n #push(level, message, opts = {}) {\n const item = {\n id: ++seq, level, message,\n actions: opts.actions || null,\n createdAt: Date.now(),\n sticky: opts.sticky || level === 'error',\n };\n this.items = [...this.items, item];\n this.cell.setValue(this.items);\n if (!item.sticky) setTimeout(() => this.dismiss(item.id), opts.timeout ?? 4000);\n return item.id;\n }\n info(m, o) {\n return this.#push('info', m, o);\n }\n success(m, o) {\n return this.#push('success', m, o);\n }\n warn(m, o) {\n return this.#push('warn', m, o);\n }\n error(m, o) {\n return this.#push('error', m, o);\n }\n /** Update a notification in place (e.g. a progress message). */\n update(id, patch) {\n this.items = this.items.map((i) => (i.id === id ? { ...i, ...patch } : i));\n this.cell.setValue(this.items);\n }\n dismiss(id) {\n this.items = this.items.filter((i) => i.id !== id);\n this.cell.setValue(this.items);\n }\n}\n",
|
|
24
|
+
"// Retry with exponential backoff + full jitter. Only retries errors classified\n// retryable (see errors.js), respects an AbortSignal between attempts, and\n// surfaces the last error untouched when it gives up — so the caller still sees\n// a clean TroveError, not a generic \"retries exhausted\".\n\nimport { isRetryable, wrapError, TroveError } from './errors.js';\n\nconst DEFAULTS = {\n retries: 4, // attempts after the first = 5 total tries\n minDelayMs: 250,\n maxDelayMs: 8000,\n factor: 2,\n jitter: true,\n};\n\nfunction sleep(ms, signal) {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(TroveError.aborted());\n // `{ once: true }` removes the listener when it FIRES — not when the timer wins,\n // which is the normal case. One long-lived signal driving a few retried operations\n // therefore accumulated a listener per attempt and released none of them.\n const done = (fn) => (arg) => {\n clearTimeout(t);\n signal?.removeEventListener('abort', onAbort);\n fn(arg);\n };\n const t = setTimeout(() => done(resolve)(), ms);\n const onAbort = () => done(reject)(TroveError.aborted());\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * @template T\n * @param {(attempt: number) => Promise<T>} fn\n * @param {object} [opts]\n * @param {number} [opts.retries]\n * @param {AbortSignal} [opts.signal]\n * @param {(info: {attempt: number, delayMs: number, error: TroveError}) => void} [opts.onRetry]\n * @param {(err: unknown) => boolean} [opts.shouldRetry] override classification\n * @returns {Promise<T>}\n */\nexport async function withRetry(fn, opts = {}) {\n const cfg = { ...DEFAULTS, ...opts };\n const shouldRetry = cfg.shouldRetry ?? isRetryable;\n let attempt = 0;\n\n for (;;) {\n if (cfg.signal?.aborted) throw TroveError.aborted();\n try {\n return await fn(attempt);\n } catch (raw) {\n const err = wrapError(raw);\n const canRetry = attempt < cfg.retries && shouldRetry(err) && err.code !== 'aborted';\n if (!canRetry) throw err;\n\n const base = Math.min(cfg.maxDelayMs, cfg.minDelayMs * cfg.factor ** attempt);\n const delayMs = cfg.jitter ? Math.random() * base : base;\n cfg.onRetry?.({ attempt: attempt + 1, delayMs, error: err });\n await sleep(delayMs, cfg.signal);\n attempt++;\n }\n }\n}\n\n/** Reject after `ms`, unless `promise` settles first. Cleans up its timer. */\nexport function withTimeout(promise, ms, message = 'Operation timed out') {\n if (!ms || ms <= 0) return promise;\n let t;\n const timeout = new Promise((_, reject) => {\n t = setTimeout(() => reject(TroveError.timeout(message)), ms);\n });\n return Promise.race([promise, timeout]).finally(() => clearTimeout(t));\n}\n",
|
|
25
|
+
"// TroveApiClient — the browser's view of the server API. Thin JSON calls with\n// transient-error retry, plus the upload orchestrator, which is where the \"handle\n// large uploads well\" requirement lives:\n//\n// • Chooses nothing itself — it follows the plan the server returns (single /\n// presigned-multipart / direct-multipart), so an S3 deployment uploads parts\n// straight to S3 and a filesystem deployment streams through the server, with\n// the same client code.\n// • Uploads parts concurrently (bounded), each with its own retry, and reports\n// aggregate byte progress for a live progress bar.\n// • Resumable: on retry it asks the server which parts already landed and skips\n// them, so a dropped connection doesn't restart a 4 GB upload.\n//\n// Uploads use XMLHttpRequest because fetch can't report upload progress.\n\nimport { withRetry } from '@3sln/trove/core/retry.js';\nimport { TroveError } from '@3sln/trove/core/errors.js';\n\nexport class TroveApiClient {\n /**\n * @param {{baseUrl?: string, fetch?: Function, token?: string|(() => string|null)}} [opts]\n * `token` is a bearer JWT, or a function returning one. Optional because the common\n * deployment puts an authenticating proxy in front (Cloudflare Access sets its own\n * header and the browser sends nothing) — but a deployment where the user HOLDS a\n * token has no way to present it otherwise, which is why this exists.\n */\n constructor({ baseUrl = '', fetch: f = globalThis.fetch.bind(globalThis), token = null } = {}) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this._fetch = f;\n this._token = token;\n }\n\n /** The bearer token to present, if any. Read per request so a refresh takes effect. */\n token() {\n const t = typeof this._token === 'function' ? this._token() : this._token;\n return t || null;\n }\n /** Auth headers for a request, or nothing at all when there is no token to send. */\n authHeaders() {\n const t = this.token();\n return t ? { authorization: `Bearer ${t}` } : {};\n }\n\n async request(method, path, { body, query, signal, raw } = {}) {\n const url = this.baseUrl + path + (query ? '?' + new URLSearchParams(query) : '');\n return withRetry(\n async () => {\n const res = await this._fetch(url, {\n method,\n headers: { ...this.authHeaders(), ...(body ? { 'content-type': 'application/json' } : {}) },\n body: body ? JSON.stringify(body) : undefined,\n signal,\n });\n if (res.status === 429 || res.status >= 500) {\n // Read the structured error to decide retry: an explicitly non-retryable\n // failure (e.g. a per-file size limit) must fail fast with its real message,\n // not be retried 3× into a generic \"Server 429\". `raw` requests can't consume\n // the body here, so they stay status-based.\n if (!raw) {\n let e;\n try { const t = await res.text(); e = (t ? JSON.parse(t) : null)?.error; } catch { /* non-JSON body */ }\n if (e && e.retryable === false) throw new TroveError(e.code, e.message, { retryable: false, details: e.details });\n throw TroveError.transient(e?.message || `Server ${res.status}`);\n }\n throw TroveError.transient(`Server ${res.status}`);\n }\n if (raw) return res;\n const text = await res.text();\n const json = text ? JSON.parse(text) : null;\n if (!res.ok) {\n const e = json?.error || { code: 'internal', message: `Request failed (${res.status})` };\n throw new TroveError(e.code, e.message, { retryable: e.retryable, details: e.details });\n }\n return json;\n },\n { signal, retries: 3 },\n );\n }\n\n // --- browse / mutate -------------------------------------------------------\n\n capabilities() {\n return this.request('GET', '/api/capabilities');\n }\n /** Single-shot reachability probe (short timeout, no retries) — is the server\n * actually reachable, vs. `navigator.onLine` merely reporting an up interface? */\n async reachable(timeoutMs = 4000) {\n try {\n const signal = AbortSignal.timeout ? AbortSignal.timeout(timeoutMs) : undefined;\n const res = await this._fetch(this.baseUrl + '/api/capabilities', { method: 'GET', signal, headers: this.authHeaders() });\n return res.ok;\n } catch { return false; }\n }\n /** Every item in a collection. */\n list(opts = {}) {\n return this.request('GET', '/api/items', { query: opts });\n }\n /** Resolve an item by id, by `trove:` URI, or by name within a collection. */\n stat(ref, opts = {}) {\n const key = String(ref).startsWith('trove:') ? 'uri' : 'id';\n return this.request('GET', '/api/items/resolve', { query: { [key]: ref, ...opts } });\n }\n /** What links to this item. */\n backlinks(id, opts = {}) {\n return this.request('GET', '/api/items/backlinks', { query: { id, ...opts } });\n }\n rename(id, newName) {\n return this.request('POST', '/api/items/rename', { body: { id, newName } });\n }\n remove(id) {\n return this.request('POST', '/api/items/delete', { body: { id } });\n }\n /** What's been deleted but not yet destroyed. */\n trash(collection) {\n return this.request('GET', '/api/trash', { query: collection ? { collection } : {} });\n }\n restore(id) {\n return this.request('POST', '/api/trash/restore', { body: { id } });\n }\n /** Destroy for real — one item, or everything in a collection's trash. */\n purgeTrash({ id, collection } = {}) {\n return this.request('POST', '/api/trash/purge', { body: id ? { id } : { collection } });\n }\n search(q, opts = {}) {\n return this.request('GET', '/api/search', { query: { q, ...opts } });\n }\n // Unified query: server transforms the raw string (parse/LLM) → runs it → returns\n // { query, results, resolved }. `resolved` is what was actually searched.\n query(q, opts = {}) {\n return this.request('POST', '/api/query', { body: { q, ...opts } });\n }\n // Drive-wide tag/property filter (launcher #tag / #key:op:value).\n tagSearch(filters, q, opts = {}) {\n return this.request('POST', '/api/tags/search', { body: { filters, q, ...opts } });\n }\n indexers() {\n return this.request('GET', '/api/indexers');\n }\n\n // --- background work + standing problems -----------------------------------\n // Tasks are in-flight and ephemeral; issues are what a failure left behind and are\n // durable. Ids can contain anything (an issue id embeds a node id), so every one of\n // these encodes before it becomes a path segment.\n tasks() {\n return this.request('GET', '/api/tasks');\n }\n cancelTask(id) {\n return this.request('POST', `/api/tasks/${encodeURIComponent(id)}/cancel`);\n }\n dismissTask(id) {\n return this.request('DELETE', `/api/tasks/${encodeURIComponent(id)}`);\n }\n issues() {\n return this.request('GET', '/api/issues');\n }\n retryIssue(id) {\n return this.request('POST', `/api/issues/${encodeURIComponent(id)}/retry`);\n }\n dismissIssue(id) {\n return this.request('DELETE', `/api/issues/${encodeURIComponent(id)}`);\n }\n reindex() {\n return this.request('POST', '/api/reindex');\n }\n /** Reconcile a collection against its object store (changes made outside Trove). */\n scanCollection(id) {\n return this.request('POST', `/api/collections/${encodeURIComponent(id)}/scan`);\n }\n\n // --- server plugin installs (account-scoped, synced across devices) ---------\n /** Upload a package zip for account install; returns the server install record. */\n async installPlugin(bytes, grants) {\n const q = grants && grants.length ? '?grants=' + encodeURIComponent(grants.join(',')) : '';\n const res = await this._fetch(this.baseUrl + '/api/plugins/install' + q, { method: 'POST', body: bytes, headers: this.authHeaders() });\n const json = await res.json().catch(() => null);\n if (!res.ok) {\n const e = json?.error || { code: 'internal', message: `Install failed (${res.status})` };\n throw new TroveError(e.code, e.message, { details: e.details });\n }\n return json.install;\n }\n installedPlugins() {\n return this.request('GET', '/api/plugins/installed');\n }\n async pluginPackage(id) {\n const res = await this._fetch(`${this.baseUrl}/api/plugins/${encodeURIComponent(id)}/package`, { headers: this.authHeaders() });\n if (!res.ok) throw new TroveError('not_found', `Package for \"${id}\" not found`);\n return new Uint8Array(await res.arrayBuffer());\n }\n uninstallPluginServer(id) {\n return this.request('DELETE', `/api/plugins/${encodeURIComponent(id)}/install`);\n }\n // contribution: { semanticTexts?, tags?, metadata? } (legacy { documents, facet } ok).\n pushIndex(indexerId, nodeId, contribution) {\n return this.request('POST', `/api/index/${encodeURIComponent(indexerId)}`, { body: { nodeId, ...contribution } });\n }\n\n // --- identity --------------------------------------------------------------\n me() {\n return this.request('GET', '/api/me');\n }\n\n // --- collections -----------------------------------------------------------\n collections() {\n return this.request('GET', '/api/collections');\n }\n createCollection(body) {\n return this.request('POST', '/api/collections', { body });\n }\n\n // --- conversations, tags, sidecar ------------------------------------------\n sidecar(id) {\n return this.request('GET', `/api/items/${encodeURIComponent(id)}/sidecar`);\n }\n addComment(id, { body, parentId, mentions } = {}) {\n return this.request('POST', `/api/items/${encodeURIComponent(id)}/comments`, { body: { body, parentId, mentions } });\n }\n editComment(id, cid, body) {\n return this.request('POST', `/api/items/${encodeURIComponent(id)}/comments/${encodeURIComponent(cid)}/edit`, { body: { body } });\n }\n deleteComment(id, cid) {\n return this.request('DELETE', `/api/items/${encodeURIComponent(id)}/comments/${encodeURIComponent(cid)}`);\n }\n reactComment(id, cid, emoji, on) {\n return this.request('POST', `/api/items/${encodeURIComponent(id)}/comments/${encodeURIComponent(cid)}/react`, { body: { emoji, on } });\n }\n setTag(id, name, value) {\n return this.request('POST', `/api/items/${encodeURIComponent(id)}/tags`, { body: { name, value } });\n }\n removeTag(id, name) {\n return this.request('DELETE', `/api/items/${encodeURIComponent(id)}/tags/${encodeURIComponent(name)}`);\n }\n\n // --- notifications & push --------------------------------------------------\n notifications() {\n return this.request('GET', '/api/notifications');\n }\n markNotificationsRead(ids) {\n return this.request('POST', '/api/notifications/read', { body: { ids } });\n }\n vapidKey() {\n return this.request('GET', '/api/push/vapid');\n }\n subscribePush(subscription) {\n return this.request('POST', '/api/push/subscribe', { body: { subscription } });\n }\n\n /** URL for GET-ing bytes (used by <img>/<audio>/<video> and downloads). */\n downloadUrl(id, { attachment } = {}) {\n return `${this.baseUrl}/api/items/download?id=${encodeURIComponent(id)}${attachment ? '&disposition=attachment' : ''}`;\n }\n\n /**\n * Mint URLs that carry their own authorization, for the things that cannot send a\n * header — an <img src>, a <video src>, cache.add(). Batched: a gallery asks for what\n * it is about to draw, in one request. See platform/mediaUrls.js.\n */\n mintUrls(ids, op = 'media') {\n return this.request('POST', '/api/items/urls', { body: { ids, op } });\n }\n\n /**\n * Start a download in the browser.\n *\n * With no bearer token this is a plain navigation to the download URL — the browser\n * streams it, handles Range, and never buffers a 4 GB file in a tab. When a token IS\n * in play that isn't possible: an `<a href>` cannot carry an Authorization header, so\n * the navigation would arrive unauthenticated and 401. Rather than move the token into\n * the query string (where it lands in logs, history, and referrers), fetch the bytes\n * with the header and hand the browser a blob.\n *\n * The cost is honest and worth stating: the blob path holds the file in memory. It is\n * the price of bearer auth without a token-bearing URL, and it does not apply to the\n * common proxy-authenticated deployment, which takes the streaming path.\n */\n async download(id, name, { attachment = true } = {}) {\n const url = this.downloadUrl(id, { attachment });\n if (!this.token()) return { url, streamed: true };\n const res = await this._fetch(url, { headers: this.authHeaders() });\n if (!res.ok) throw new TroveError('internal', `Download failed (${res.status})`);\n return { url: URL.createObjectURL(await res.blob()), streamed: false, revoke: true };\n }\n\n /** Read a whole file as text/bytes (small files, indexers). */\n async readBytes(id, { signal } = {}) {\n const res = await this._fetch(this.downloadUrl(id), { signal, headers: this.authHeaders() });\n if (!res.ok) throw new TroveError('internal', `Download failed (${res.status})`);\n return new Uint8Array(await res.arrayBuffer());\n }\n async readText(id, opts) {\n return new TextDecoder().decode(await this.readBytes(id, opts));\n }\n\n /**\n * Read at most `maxBytes` of a file as text.\n *\n * A viewer must never pull a whole file just to show the top of it. A drive holds\n * logs, exports and dumps that are hundreds of megabytes; `readText` on one of those\n * buffers the entire thing in the tab before a single character is drawn, and the\n * browser is gone before the user learns anything. A Range request costs one extra\n * byte over the wire and bounds the damage at the transfer rather than the render.\n *\n * The cut is at a BYTE boundary, which can land mid-character in UTF-8, so the\n * decoder is told to tolerate it — a trailing replacement glyph beats throwing away\n * the read. The last partial line is dropped for the same reason.\n *\n * @returns {Promise<{text: string, truncated: boolean, total: number|null}>}\n */\n async readTextCapped(id, { maxBytes = 512 * 1024, size = null, signal } = {}) {\n // Skip the Range when we already know the file fits. An empty file has no\n // satisfiable range, so asking for one turned \"open an empty note\" into an error\n // page; a 416 from any backend would do the same.\n const ranged = size == null || size > maxBytes;\n const res = await this._fetch(this.downloadUrl(id), {\n signal,\n headers: { ...this.authHeaders(), ...(ranged ? { range: `bytes=0-${maxBytes - 1}` } : {}) },\n });\n if (res.status === 416) return { text: '', truncated: false, total: 0 };\n if (!res.ok && res.status !== 206) throw new TroveError('internal', `Download failed (${res.status})`);\n const bytes = new Uint8Array(await res.arrayBuffer());\n // `Content-Range: bytes 0-N/TOTAL` is how we learn the real size; a server that\n // ignored the Range header just sent everything, which is also fine.\n const total = Number(/\\/(\\d+)$/.exec(res.headers.get('content-range') || '')?.[1]) || null;\n const truncated = bytes.length >= maxBytes && (total == null || total > bytes.length);\n let text = new TextDecoder('utf-8', { fatal: false }).decode(bytes);\n if (truncated) {\n const lastBreak = text.lastIndexOf('\\n');\n if (lastBreak > 0) text = text.slice(0, lastBreak);\n }\n return { text, truncated, total };\n }\n\n /**\n * Upload a File/Blob with progress + resume.\n * @param {Blob & {name?:string}} file\n * @param {object} opts\n * @param {string} opts.parentId\n * @param {string} [opts.name]\n * @param {(p:{loaded:number,total:number,ratio:number}) => void} [opts.onProgress]\n * @param {AbortSignal} [opts.signal]\n * @param {number} [opts.concurrency]\n * @returns {Promise<object>} the created node\n */\n async upload(file, opts) {\n const name = opts.name || file.name || 'untitled';\n const size = file.size;\n const plan = await this.request('POST', '/api/uploads', {\n body: { collection: opts.collection, name, size, contentType: file.type || undefined },\n signal: opts.signal,\n });\n // Hand the caller the server upload id so a cancel/failure can abort the session\n // (otherwise a multipart upload leaks server + storage state).\n if (plan.uploadId) opts.onStart?.(plan.uploadId);\n\n const progress = new ProgressAggregator(size, opts.onProgress);\n const t = plan.transfer || {};\n const completeUrl = plan.endpoints?.complete || `/api/uploads/${plan.uploadId}/complete`;\n\n if (plan.strategy === 'single') {\n // One presigned PUT straight to storage (bytes never touch our server).\n await xhrPut(t.url || plan.url, file, { headers: t.requiredHeaders, signal: opts.signal, onProgress: (l) => progress.set('single', l) });\n const done = await this.request('POST', completeUrl, { body: {}, signal: opts.signal });\n return done.node;\n }\n if (plan.strategy === 'direct-single') {\n await xhrPut(this.baseUrl + this.#partUrl(plan, 1), file, {\n // Our own server, so it needs our bearer token; `t.authHeaders` lets the plan\n // add its own and wins on a clash.\n headers: { ...this.authHeaders(), ...t.authHeaders },\n signal: opts.signal, onProgress: (l) => progress.set(1, l),\n });\n const done = await this.request('POST', completeUrl, { body: {}, signal: opts.signal });\n return done.node;\n }\n\n // Multipart (presign or direct).\n const partSize = plan.partSize;\n const partCount = plan.partCount ?? Math.ceil(size / partSize);\n // Resume: which parts already exist?\n let received = new Set();\n try {\n const statusUrl = plan.endpoints?.status || `/api/uploads/${plan.uploadId}/status`;\n const status = await this.request('GET', statusUrl, { signal: opts.signal });\n received = new Set(status.received || []);\n } catch { /* fresh upload */ }\n\n const parts = [];\n const jobs = [];\n for (let n = 1; n <= partCount; n++) {\n const start = (n - 1) * partSize;\n const blob = file.slice(start, Math.min(start + partSize, size));\n jobs.push({ n, blob });\n }\n\n const results = new Array(partCount);\n const concurrency = Math.min(opts.concurrency ?? 4, jobs.length);\n let cursor = 0;\n // When one part fails for good, abort the sibling workers' in-flight parts instead\n // of letting them keep PUTting bytes whose result we're about to discard. The inner\n // controller is chained to the caller's signal so a user cancel still propagates.\n const inner = new AbortController();\n const onOuterAbort = () => inner.abort();\n if (opts.signal) {\n if (opts.signal.aborted) inner.abort();\n else opts.signal.addEventListener('abort', onOuterAbort, { once: true });\n }\n const worker = async () => {\n while (cursor < jobs.length) {\n if (inner.signal.aborted) return;\n const { n, blob } = jobs[cursor++];\n if (received.has(n)) {\n progress.set(n, blob.size);\n continue;\n }\n const etag = await withRetry(\n () => this.#uploadPart(plan, n, blob, {\n signal: inner.signal, onProgress: (l) => progress.set(n, l),\n }),\n { signal: inner.signal, retries: 4 },\n );\n results[n - 1] = { partNumber: n, etag };\n }\n };\n try {\n await Promise.all(Array.from({ length: concurrency }, worker));\n } catch (err) {\n inner.abort(); // stop the other workers before surfacing the failure\n throw err;\n } finally {\n opts.signal?.removeEventListener('abort', onOuterAbort);\n }\n\n const reportedParts = results.filter(Boolean);\n const done = await this.request('POST', completeUrl, {\n body: { parts: reportedParts }, signal: opts.signal,\n });\n return done.node;\n }\n\n // Expand an endpoint/transfer URL template's {partNumber} placeholder.\n #partUrl(plan, n) {\n const tmpl = plan.transfer?.partUrl || `/api/uploads/${plan.uploadId}/parts/{partNumber}`;\n return tmpl.replace('{partNumber}', String(n));\n }\n\n async #uploadPart(plan, n, blob, { signal, onProgress }) {\n const t = plan.transfer || {};\n if (plan.strategy === 'presign') {\n const part = (t.parts || plan.parts)?.find((p) => p.partNumber === n);\n let url = part?.url;\n if (!url) {\n const signTmpl = plan.endpoints?.sign || `/api/uploads/${plan.uploadId}/parts/{partNumber}/sign`;\n const r = await this.request('POST', signTmpl.replace('{partNumber}', String(n)), { signal });\n url = r.url;\n }\n const res = await xhrPut(url, blob, { signal, onProgress, wantEtag: true });\n const etag = res.etag;\n const reportTmpl = plan.endpoints?.report || `/api/uploads/${plan.uploadId}/parts/{partNumber}/report`;\n await this.request('POST', reportTmpl.replace('{partNumber}', String(n)), { body: { etag }, signal });\n return etag;\n }\n // proxied: server records the etag; response body has it.\n const res = await xhrPut(this.baseUrl + this.#partUrl(plan, n), blob, {\n headers: { ...this.authHeaders(), ...t.authHeaders },\n signal, onProgress, wantJson: true,\n });\n return res.json?.etag;\n }\n\n async abortUpload(uploadId) {\n return this.request('DELETE', `/api/uploads/${uploadId}`);\n }\n}\n\nclass ProgressAggregator {\n constructor(total, cb) {\n this.total = total;\n this.cb = cb;\n this.parts = new Map();\n }\n set(key, loaded) {\n // Monotonic per part: a retried part restarts its XHR progress at 0, which would\n // otherwise pull the aggregate bar backwards. Never let a part's reported bytes drop.\n const prev = this.parts.get(key) || 0;\n if (loaded < prev) return;\n this.parts.set(key, loaded);\n if (!this.cb) return;\n let sum = 0;\n for (const v of this.parts.values()) sum += v;\n const loadedTotal = Math.min(sum, this.total);\n this.cb({ loaded: loadedTotal, total: this.total, ratio: this.total ? loadedTotal / this.total : 1 });\n }\n}\n\n// XHR PUT with upload progress + abort. Returns { status, etag?, json? }.\nfunction xhrPut(url, body, { signal, onProgress, wantEtag, wantJson, headers } = {}) {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('PUT', url, true);\n for (const [k, v] of Object.entries(headers || {})) { try { xhr.setRequestHeader(k, v); } catch { /* forbidden header */ } }\n if (signal) {\n if (signal.aborted) {\n xhr.abort();\n return reject(TroveError.aborted());\n }\n signal.addEventListener('abort', () => xhr.abort(), { once: true });\n }\n xhr.upload.onprogress = (e) => onProgress?.(e.loaded);\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n const out = { status: xhr.status };\n if (wantEtag) out.etag = xhr.getResponseHeader('ETag') || xhr.getResponseHeader('etag');\n if (wantJson) {\n try {\n out.json = JSON.parse(xhr.responseText);\n } catch { /* ignore */ }\n }\n resolve(out);\n } else if (xhr.status === 429 || xhr.status >= 500) {\n reject(TroveError.transient(`Upload part failed (${xhr.status})`));\n } else {\n reject(new TroveError('internal', `Upload failed (${xhr.status})`));\n }\n };\n xhr.onerror = () => reject(TroveError.transient('Network error during upload'));\n xhr.onabort = () => reject(TroveError.aborted());\n xhr.send(body);\n });\n}\n",
|
|
26
|
+
"// DEFLATE is a complex format; to read this code, you should probably check the RFC first:\n// https://tools.ietf.org/html/rfc1951\n// You may also wish to take a look at the guide I made about this program:\n// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad\n// Some of the following code is similar to that of UZIP.js:\n// https://github.com/photopea/UZIP.js\n// However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.\n// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint\n// is better for memory in most engines (I *think*).\nvar ch2 = {};\nvar wk = (function (c, id, msg, transfer, cb) {\n var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([\n c + ';addEventListener(\"error\",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'\n ], { type: 'text/javascript' }))));\n w.onmessage = function (e) {\n var d = e.data, ed = d.$e$;\n if (ed) {\n var err = new Error(ed[0]);\n err['code'] = ed[1];\n err.stack = ed[2];\n cb(err, null);\n }\n else\n cb(null, d);\n };\n w.postMessage(msg, transfer);\n return w;\n});\n\n// aliases for shorter compressed code (most minifers don't do this)\nvar u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;\n// fixed length extra bits\nvar fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);\n// fixed distance extra bits\nvar fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);\n// code length index map\nvar clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n// get base, reverse index map from extra bits\nvar freb = function (eb, start) {\n var b = new u16(31);\n for (var i = 0; i < 31; ++i) {\n b[i] = start += 1 << eb[i - 1];\n }\n // numbers here are at max 18 bits\n var r = new i32(b[30]);\n for (var i = 1; i < 30; ++i) {\n for (var j = b[i]; j < b[i + 1]; ++j) {\n r[j] = ((j - b[i]) << 5) | i;\n }\n }\n return { b: b, r: r };\n};\nvar _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;\n// we can ignore the fact that the other numbers are wrong; they never happen anyway\nfl[28] = 258, revfl[258] = 28;\nvar _b = freb(fdeb, 0), fd = _b.b, revfd = _b.r;\n// map of value to reverse (assuming 16 bits)\nvar rev = new u16(32768);\nfor (var i = 0; i < 32768; ++i) {\n // reverse table algorithm from SO\n var x = ((i & 0xAAAA) >> 1) | ((i & 0x5555) << 1);\n x = ((x & 0xCCCC) >> 2) | ((x & 0x3333) << 2);\n x = ((x & 0xF0F0) >> 4) | ((x & 0x0F0F) << 4);\n rev[i] = (((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8)) >> 1;\n}\n// create huffman tree from u8 \"map\": index -> code length for code index\n// mb (max bits) must be at most 15\n// TODO: optimize/split up?\nvar hMap = (function (cd, mb, r) {\n var s = cd.length;\n // index\n var i = 0;\n // u16 \"map\": index -> # of codes with bit length = index\n var l = new u16(mb);\n // length of cd must be 288 (total # of codes)\n for (; i < s; ++i) {\n if (cd[i])\n ++l[cd[i] - 1];\n }\n // u16 \"map\": index -> minimum code for bit length = index\n var le = new u16(mb);\n for (i = 1; i < mb; ++i) {\n le[i] = (le[i - 1] + l[i - 1]) << 1;\n }\n var co;\n if (r) {\n // u16 \"map\": index -> number of actual bits, symbol for code\n co = new u16(1 << mb);\n // bits to remove for reverser\n var rvb = 15 - mb;\n for (i = 0; i < s; ++i) {\n // ignore 0 lengths\n if (cd[i]) {\n // num encoding both symbol and bits read\n var sv = (i << 4) | cd[i];\n // free bits\n var r_1 = mb - cd[i];\n // start value\n var v = le[cd[i] - 1]++ << r_1;\n // m is end value\n for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {\n // every 16 bit value starting with the code yields the same result\n co[rev[v] >> rvb] = sv;\n }\n }\n }\n }\n else {\n co = new u16(s);\n for (i = 0; i < s; ++i) {\n if (cd[i]) {\n co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);\n }\n }\n }\n return co;\n});\n// fixed length tree\nvar flt = new u8(288);\nfor (var i = 0; i < 144; ++i)\n flt[i] = 8;\nfor (var i = 144; i < 256; ++i)\n flt[i] = 9;\nfor (var i = 256; i < 280; ++i)\n flt[i] = 7;\nfor (var i = 280; i < 288; ++i)\n flt[i] = 8;\n// fixed distance tree\nvar fdt = new u8(32);\nfor (var i = 0; i < 32; ++i)\n fdt[i] = 5;\n// fixed length map\nvar flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);\n// fixed distance map\nvar fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);\n// find max of array\nvar max = function (a) {\n var m = a[0];\n for (var i = 1; i < a.length; ++i) {\n if (a[i] > m)\n m = a[i];\n }\n return m;\n};\n// read d, starting at bit p and mask with m\nvar bits = function (d, p, m) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;\n};\n// read d, starting at bit p continuing for at least 16 bits\nvar bits16 = function (d, p) {\n var o = (p / 8) | 0;\n return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7));\n};\n// get end of byte\nvar shft = function (p) { return ((p + 7) / 8) | 0; };\n// typed array slice - allows garbage collector to free original reference,\n// while being more compatible than .slice\nvar slc = function (v, s, e) {\n if (s == null || s < 0)\n s = 0;\n if (e == null || e > v.length)\n e = v.length;\n // can't use .constructor in case user-supplied\n return new u8(v.subarray(s, e));\n};\n/**\n * Codes for errors generated within this library\n */\nexport var FlateErrorCode = {\n UnexpectedEOF: 0,\n InvalidBlockType: 1,\n InvalidLengthLiteral: 2,\n InvalidDistance: 3,\n StreamFinished: 4,\n NoStreamHandler: 5,\n InvalidHeader: 6,\n NoCallback: 7,\n InvalidUTF8: 8,\n ExtraFieldTooLong: 9,\n InvalidDate: 10,\n FilenameTooLong: 11,\n StreamFinishing: 12,\n InvalidZipData: 13,\n UnknownCompressionMethod: 14\n};\n// error codes\nvar ec = [\n 'unexpected EOF',\n 'invalid block type',\n 'invalid length/literal',\n 'invalid distance',\n 'stream finished',\n 'no stream handler',\n , // determined by compression function\n 'no callback',\n 'invalid UTF-8 data',\n 'extra field too long',\n 'date not in range 1980-2099',\n 'filename too long',\n 'stream finishing',\n 'invalid zip data'\n // determined by unknown compression method\n];\n;\nvar err = function (ind, msg, nt) {\n var e = new Error(msg || ec[ind]);\n e.code = ind;\n if (Error.captureStackTrace)\n Error.captureStackTrace(e, err);\n if (!nt)\n throw e;\n return e;\n};\n// expands raw DEFLATE data\nvar inflt = function (dat, st, buf, dict) {\n // source length dict length\n var sl = dat.length, dl = dict ? dict.length : 0;\n if (!sl || st.f && !st.l)\n return buf || new u8(0);\n var noBuf = !buf;\n // have to estimate size\n var resize = noBuf || st.i != 2;\n // no state\n var noSt = st.i;\n // Assumes roughly 33% compression ratio average\n if (noBuf)\n buf = new u8(sl * 3);\n // ensure buffer can fit at least l elements\n var cbuf = function (l) {\n var bl = buf.length;\n // need to increase size to fit\n if (l > bl) {\n // Double or set to necessary, whichever is greater\n var nbuf = new u8(Math.max(bl * 2, l));\n nbuf.set(buf);\n buf = nbuf;\n }\n };\n // last chunk bitpos bytes\n var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;\n // total bits\n var tbts = sl * 8;\n do {\n if (!lm) {\n // BFINAL - this is only 1 when last chunk is next\n final = bits(dat, pos, 1);\n // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman\n var type = bits(dat, pos + 1, 3);\n pos += 3;\n if (!type) {\n // go to end of byte boundary\n var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;\n if (t > sl) {\n if (noSt)\n err(0);\n break;\n }\n // ensure size\n if (resize)\n cbuf(bt + l);\n // Copy over uncompressed data\n buf.set(dat.subarray(s, t), bt);\n // Get new bitpos, update byte count\n st.b = bt += l, st.p = pos = t * 8, st.f = final;\n continue;\n }\n else if (type == 1)\n lm = flrm, dm = fdrm, lbt = 9, dbt = 5;\n else if (type == 2) {\n // literal lengths\n var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;\n var tl = hLit + bits(dat, pos + 5, 31) + 1;\n pos += 14;\n // length+distance tree\n var ldt = new u8(tl);\n // code length tree\n var clt = new u8(19);\n for (var i = 0; i < hcLen; ++i) {\n // use index map to get real code\n clt[clim[i]] = bits(dat, pos + i * 3, 7);\n }\n pos += hcLen * 3;\n // code lengths bits\n var clb = max(clt), clbmsk = (1 << clb) - 1;\n // code lengths map\n var clm = hMap(clt, clb, 1);\n for (var i = 0; i < tl;) {\n var r = clm[bits(dat, pos, clbmsk)];\n // bits read\n pos += r & 15;\n // symbol\n var s = r >> 4;\n // code length to copy\n if (s < 16) {\n ldt[i++] = s;\n }\n else {\n // copy count\n var c = 0, n = 0;\n if (s == 16)\n n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];\n else if (s == 17)\n n = 3 + bits(dat, pos, 7), pos += 3;\n else if (s == 18)\n n = 11 + bits(dat, pos, 127), pos += 7;\n while (n--)\n ldt[i++] = c;\n }\n }\n // length tree distance tree\n var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);\n // max length bits\n lbt = max(lt);\n // max dist bits\n dbt = max(dt);\n lm = hMap(lt, lbt, 1);\n dm = hMap(dt, dbt, 1);\n }\n else\n err(1);\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n }\n // Make sure the buffer can hold this + the largest possible addition\n // Maximum chunk size (practically, theoretically infinite) is 2^17\n if (resize)\n cbuf(bt + 131072);\n var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;\n var lpos = pos;\n for (;; lpos = pos) {\n // bits read, code\n var c = lm[bits16(dat, pos) & lms], sym = c >> 4;\n pos += c & 15;\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (!c)\n err(2);\n if (sym < 256)\n buf[bt++] = sym;\n else if (sym == 256) {\n lpos = pos, lm = null;\n break;\n }\n else {\n var add = sym - 254;\n // no extra bits needed if less\n if (sym > 264) {\n // index\n var i = sym - 257, b = fleb[i];\n add = bits(dat, pos, (1 << b) - 1) + fl[i];\n pos += b;\n }\n // dist\n var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;\n if (!d)\n err(3);\n pos += d & 15;\n var dt = fd[dsym];\n if (dsym > 3) {\n var b = fdeb[dsym];\n dt += bits16(dat, pos) & (1 << b) - 1, pos += b;\n }\n if (pos > tbts) {\n if (noSt)\n err(0);\n break;\n }\n if (resize)\n cbuf(bt + 131072);\n var end = bt + add;\n if (bt < dt) {\n var shift = dl - dt, dend = Math.min(dt, end);\n if (shift + bt < 0)\n err(3);\n for (; bt < dend; ++bt)\n buf[bt] = dict[shift + bt];\n }\n for (; bt < end; ++bt)\n buf[bt] = buf[bt - dt];\n }\n }\n st.l = lm, st.p = lpos, st.b = bt, st.f = final;\n if (lm)\n final = 1, st.m = lbt, st.d = dm, st.n = dbt;\n } while (!final);\n // don't reallocate for streams or user buffers\n return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);\n};\n// starting at p, write the minimum number of bits that can hold v to d\nvar wbits = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >> 8;\n};\n// starting at p, write the minimum number of bits (>8) that can hold v to d\nvar wbits16 = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) | 0;\n d[o] |= v;\n d[o + 1] |= v >> 8;\n d[o + 2] |= v >> 16;\n};\n// creates code lengths from a frequency table\nvar hTree = function (d, mb) {\n // Need extra info to make a tree\n var t = [];\n for (var i = 0; i < d.length; ++i) {\n if (d[i])\n t.push({ s: i, f: d[i] });\n }\n var s = t.length;\n var t2 = t.slice();\n if (!s)\n return { t: et, l: 0 };\n if (s == 1) {\n var v = new u8(t[0].s + 1);\n v[t[0].s] = 1;\n return { t: v, l: 1 };\n }\n t.sort(function (a, b) { return a.f - b.f; });\n // after i2 reaches last ind, will be stopped\n // freq must be greater than largest possible number of symbols\n t.push({ s: -1, f: 25001 });\n var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;\n t[0] = { s: -1, f: l.f + r.f, l: l, r: r };\n // efficient algorithm from UZIP.js\n // i0 is lookbehind, i2 is lookahead - after processing two low-freq\n // symbols that combined have high freq, will start processing i2 (high-freq,\n // non-composite) symbols instead\n // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/\n while (i1 != s - 1) {\n l = t[t[i0].f < t[i2].f ? i0++ : i2++];\n r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];\n t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };\n }\n var maxSym = t2[0].s;\n for (var i = 1; i < s; ++i) {\n if (t2[i].s > maxSym)\n maxSym = t2[i].s;\n }\n // code lengths\n var tr = new u16(maxSym + 1);\n // max bits in tree\n var mbt = ln(t[i1 - 1], tr, 0);\n if (mbt > mb) {\n // more algorithms from UZIP.js\n // TODO: find out how this code works (debt)\n // ind debt\n var i = 0, dt = 0;\n // left cost\n var lft = mbt - mb, cst = 1 << lft;\n t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });\n for (; i < s; ++i) {\n var i2_1 = t2[i].s;\n if (tr[i2_1] > mb) {\n dt += cst - (1 << (mbt - tr[i2_1]));\n tr[i2_1] = mb;\n }\n else\n break;\n }\n dt >>= lft;\n while (dt > 0) {\n var i2_2 = t2[i].s;\n if (tr[i2_2] < mb)\n dt -= 1 << (mb - tr[i2_2]++ - 1);\n else\n ++i;\n }\n for (; i >= 0 && dt; --i) {\n var i2_3 = t2[i].s;\n if (tr[i2_3] == mb) {\n --tr[i2_3];\n ++dt;\n }\n }\n mbt = mb;\n }\n return { t: new u8(tr), l: mbt };\n};\n// get the max length and assign length codes\nvar ln = function (n, l, d) {\n return n.s == -1\n ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))\n : (l[n.s] = d);\n};\n// length codes generation\nvar lc = function (c) {\n var s = c.length;\n // Note that the semicolon was intentional\n while (s && !c[--s])\n ;\n var cl = new u16(++s);\n // ind num streak\n var cli = 0, cln = c[0], cls = 1;\n var w = function (v) { cl[cli++] = v; };\n for (var i = 1; i <= s; ++i) {\n if (c[i] == cln && i != s)\n ++cls;\n else {\n if (!cln && cls > 2) {\n for (; cls > 138; cls -= 138)\n w(32754);\n if (cls > 2) {\n w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);\n cls = 0;\n }\n }\n else if (cls > 3) {\n w(cln), --cls;\n for (; cls > 6; cls -= 6)\n w(8304);\n if (cls > 2)\n w(((cls - 3) << 5) | 8208), cls = 0;\n }\n while (cls--)\n w(cln);\n cls = 1;\n cln = c[i];\n }\n }\n return { c: cl.subarray(0, cli), n: s };\n};\n// calculate the length of output from tree, code lengths\nvar clen = function (cf, cl) {\n var l = 0;\n for (var i = 0; i < cl.length; ++i)\n l += cf[i] * cl[i];\n return l;\n};\n// writes a fixed block\n// returns the new bit pos\nvar wfblk = function (out, pos, dat) {\n // no need to write 00 as type: TypedArray defaults to 0\n var s = dat.length;\n var o = shft(pos + 2);\n out[o] = s & 255;\n out[o + 1] = s >> 8;\n out[o + 2] = out[o] ^ 255;\n out[o + 3] = out[o + 1] ^ 255;\n for (var i = 0; i < s; ++i)\n out[o + i + 4] = dat[i];\n return (o + 4 + s) * 8;\n};\n// writes a block\nvar wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n wbits(out, p++, final);\n ++lf[256];\n var _a = hTree(lf, 15), dlt = _a.t, mlb = _a.l;\n var _b = hTree(df, 15), ddt = _b.t, mdb = _b.l;\n var _c = lc(dlt), lclt = _c.c, nlc = _c.n;\n var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;\n var lcfreq = new u16(19);\n for (var i = 0; i < lclt.length; ++i)\n ++lcfreq[lclt[i] & 31];\n for (var i = 0; i < lcdt.length; ++i)\n ++lcfreq[lcdt[i] & 31];\n var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;\n var nlcc = 19;\n for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n ;\n var flen = (bl + 5) << 3;\n var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];\n if (bs >= 0 && flen <= ftlen && flen <= dtlen)\n return wfblk(out, p, dat.subarray(bs, bs + bl));\n var lm, ll, dm, dl;\n wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n if (dtlen < ftlen) {\n lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n var llm = hMap(lct, mlcb, 0);\n wbits(out, p, nlc - 257);\n wbits(out, p + 5, ndc - 1);\n wbits(out, p + 10, nlcc - 4);\n p += 14;\n for (var i = 0; i < nlcc; ++i)\n wbits(out, p + 3 * i, lct[clim[i]]);\n p += 3 * nlcc;\n var lcts = [lclt, lcdt];\n for (var it = 0; it < 2; ++it) {\n var clct = lcts[it];\n for (var i = 0; i < clct.length; ++i) {\n var len = clct[i] & 31;\n wbits(out, p, llm[len]), p += lct[len];\n if (len > 15)\n wbits(out, p, (clct[i] >> 5) & 127), p += clct[i] >> 12;\n }\n }\n }\n else {\n lm = flm, ll = flt, dm = fdm, dl = fdt;\n }\n for (var i = 0; i < li; ++i) {\n var sym = syms[i];\n if (sym > 255) {\n var len = (sym >> 18) & 31;\n wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n if (len > 7)\n wbits(out, p, (sym >> 23) & 31), p += fleb[len];\n var dst = sym & 31;\n wbits16(out, p, dm[dst]), p += dl[dst];\n if (dst > 3)\n wbits16(out, p, (sym >> 5) & 8191), p += fdeb[dst];\n }\n else {\n wbits16(out, p, lm[sym]), p += ll[sym];\n }\n }\n wbits16(out, p, lm[256]);\n return p + ll[256];\n};\n// deflate options (nice << 13) | chain\nvar deo = /*#__PURE__*/ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n// empty\nvar et = /*#__PURE__*/ new u8(0);\n// compresses data into a raw DEFLATE buffer\nvar dflt = function (dat, lvl, plvl, pre, post, st) {\n var s = st.z || dat.length;\n var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);\n // writing to this writes to the output buffer\n var w = o.subarray(pre, o.length - post);\n var lst = st.l;\n var pos = (st.r || 0) & 7;\n if (lvl) {\n if (pos)\n w[0] = st.r >> 3;\n var opt = deo[lvl - 1];\n var n = opt >> 13, c = opt & 8191;\n var msk_1 = (1 << plvl) - 1;\n // prev 2-byte val map curr 2-byte val map\n var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);\n var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };\n // 24576 is an arbitrary number of maximum symbols per block\n // 424 buffer for last block\n var syms = new i32(25000);\n // length/literal freq distance freq\n var lf = new u16(288), df = new u16(32);\n // l/lcnt exbits index l/lind waitdx blkpos\n var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;\n for (; i + 2 < s; ++i) {\n // hash value\n var hv = hsh(i);\n // index mod 32768 previous index mod\n var imod = i & 32767, pimod = head[hv];\n prev[imod] = pimod;\n head[hv] = imod;\n // We always should modify head and prev, but only add symbols if\n // this data is not yet processed (\"wait\" for wait index)\n if (wi <= i) {\n // bytes remaining\n var rem = s - i;\n if ((lc_1 > 7000 || li > 24576) && (rem > 423 || !lst)) {\n pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);\n li = lc_1 = eb = 0, bs = i;\n for (var j = 0; j < 286; ++j)\n lf[j] = 0;\n for (var j = 0; j < 30; ++j)\n df[j] = 0;\n }\n // len dist chain\n var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;\n if (rem > 2 && hv == hsh(i - dif)) {\n var maxn = Math.min(n, rem) - 1;\n var maxd = Math.min(32767, i);\n // max possible length\n // not capped at dif because decompressors implement \"rolling\" index population\n var ml = Math.min(258, rem);\n while (dif <= maxd && --ch_1 && imod != pimod) {\n if (dat[i + l] == dat[i + l - dif]) {\n var nl = 0;\n for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)\n ;\n if (nl > l) {\n l = nl, d = dif;\n // break out early when we reach \"nice\" (we are satisfied enough)\n if (nl > maxn)\n break;\n // now, find the rarest 2-byte sequence within this\n // length of literals and search for that instead.\n // Much faster than just using the start\n var mmd = Math.min(dif, nl - 2);\n var md = 0;\n for (var j = 0; j < mmd; ++j) {\n var ti = i - dif + j & 32767;\n var pti = prev[ti];\n var cd = ti - pti & 32767;\n if (cd > md)\n md = cd, pimod = ti;\n }\n }\n }\n // check the previous match\n imod = pimod, pimod = prev[imod];\n dif += imod - pimod & 32767;\n }\n }\n // d will be nonzero only when a match was found\n if (d) {\n // store both dist and len data in one int32\n // Make sure this is recognized as a len/dist with 28th bit (2^28)\n syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];\n var lin = revfl[l] & 31, din = revfd[d] & 31;\n eb += fleb[lin] + fdeb[din];\n ++lf[257 + lin];\n ++df[din];\n wi = i + l;\n ++lc_1;\n }\n else {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n }\n }\n for (i = Math.max(i, wi); i < s; ++i) {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);\n if (!lst) {\n st.r = (pos & 7) | w[(pos / 8) | 0] << 3;\n // shft(pos) now 1 less if pos & 7 != 0\n pos -= 7;\n st.h = head, st.p = prev, st.i = i, st.w = wi;\n }\n }\n else {\n for (var i = st.w || 0; i < s + lst; i += 65535) {\n // end\n var e = i + 65535;\n if (e >= s) {\n // write final block\n w[(pos / 8) | 0] = lst;\n e = s;\n }\n pos = wfblk(w, pos + 1, dat.subarray(i, e));\n }\n st.i = s;\n }\n return slc(o, 0, pre + shft(pos) + post);\n};\n// CRC32 table\nvar crct = /*#__PURE__*/ (function () {\n var t = new Int32Array(256);\n for (var i = 0; i < 256; ++i) {\n var c = i, k = 9;\n while (--k)\n c = ((c & 1) && -306674912) ^ (c >>> 1);\n t[i] = c;\n }\n return t;\n})();\n// CRC32\nvar crc = function () {\n var c = -1;\n return {\n p: function (d) {\n // closures have awful performance\n var cr = c;\n for (var i = 0; i < d.length; ++i)\n cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);\n c = cr;\n },\n d: function () { return ~c; }\n };\n};\n// Adler32\nvar adler = function () {\n var a = 1, b = 0;\n return {\n p: function (d) {\n // closures have awful performance\n var n = a, m = b;\n var l = d.length | 0;\n for (var i = 0; i != l;) {\n var e = Math.min(i + 2655, l);\n for (; i < e; ++i)\n m += n += d[i];\n n = (n & 65535) + 15 * (n >> 16), m = (m & 65535) + 15 * (m >> 16);\n }\n a = n, b = m;\n },\n d: function () {\n a %= 65521, b %= 65521;\n return (a & 255) << 24 | (a & 0xFF00) << 8 | (b & 255) << 8 | (b >> 8);\n }\n };\n};\n;\n// deflate with opts\nvar dopt = function (dat, opt, pre, post, st) {\n if (!st) {\n st = { l: 1 };\n if (opt.dictionary) {\n var dict = opt.dictionary.subarray(-32768);\n var newDat = new u8(dict.length + dat.length);\n newDat.set(dict);\n newDat.set(dat, dict.length);\n dat = newDat;\n st.w = dict.length;\n }\n }\n return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? (st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20) : (12 + opt.mem), pre, post, st);\n};\n// Walmart object spread\nvar mrg = function (a, b) {\n var o = {};\n for (var k in a)\n o[k] = a[k];\n for (var k in b)\n o[k] = b[k];\n return o;\n};\n// worker clone\n// This is possibly the craziest part of the entire codebase, despite how simple it may seem.\n// The only parameter to this function is a closure that returns an array of variables outside of the function scope.\n// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.\n// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).\n// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.\n// This took me three weeks to figure out how to do.\nvar wcln = function (fn, fnStr, td) {\n var dt = fn();\n var st = fn.toString();\n var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\\s+/g, '').split(',');\n for (var i = 0; i < dt.length; ++i) {\n var v = dt[i], k = ks[i];\n if (typeof v == 'function') {\n fnStr += ';' + k + '=';\n var st_1 = v.toString();\n if (v.prototype) {\n // for global objects\n if (st_1.indexOf('[native code]') != -1) {\n var spInd = st_1.indexOf(' ', 8) + 1;\n fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));\n }\n else {\n fnStr += st_1;\n for (var t in v.prototype)\n fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();\n }\n }\n else\n fnStr += st_1;\n }\n else\n td[k] = v;\n }\n return fnStr;\n};\nvar ch = [];\n// clone bufs\nvar cbfs = function (v) {\n var tl = [];\n for (var k in v) {\n if (v[k].buffer) {\n tl.push((v[k] = new v[k].constructor(v[k])).buffer);\n }\n }\n return tl;\n};\n// use a worker to execute code\nvar wrkr = function (fns, init, id, cb) {\n if (!ch[id]) {\n var fnStr = '', td_1 = {}, m = fns.length - 1;\n for (var i = 0; i < m; ++i)\n fnStr = wcln(fns[i], fnStr, td_1);\n ch[id] = { c: wcln(fns[m], fnStr, td_1), e: td_1 };\n }\n var td = mrg({}, ch[id].e);\n return wk(ch[id].c + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);\n};\n// base async inflate fn\nvar bInflt = function () { return [u8, u16, i32, fleb, fdeb, clim, fl, fd, flrm, fdrm, rev, ec, hMap, max, bits, bits16, shft, slc, err, inflt, inflateSync, pbf, gopt]; };\nvar bDflt = function () { return [u8, u16, i32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };\n// gzip extra\nvar gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };\n// gunzip extra\nvar guze = function () { return [gzs, gzl]; };\n// zlib extra\nvar zle = function () { return [zlh, wbytes, adler]; };\n// unzlib extra\nvar zule = function () { return [zls]; };\n// post buf\nvar pbf = function (msg) { return postMessage(msg, [msg.buffer]); };\n// get opts\nvar gopt = function (o) { return o && {\n out: o.size && new u8(o.size),\n dictionary: o.dictionary\n}; };\n// async helper\nvar cbify = function (dat, opts, fns, init, id, cb) {\n var w = wrkr(fns, init, id, function (err, dat) {\n w.terminate();\n cb(err, dat);\n });\n w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);\n return function () { w.terminate(); };\n};\n// auto stream\nvar astrm = function (strm) {\n strm.ondata = function (dat, final) { return postMessage([dat, final], [dat.buffer]); };\n return function (ev) {\n if (ev.data[0]) {\n strm.push(ev.data[0], ev.data[1]);\n postMessage([ev.data[0].length]);\n }\n else\n strm.flush(ev.data[1]);\n };\n};\n// async stream attach\nvar astrmify = function (fns, strm, opts, init, id, flush, ext) {\n var t;\n var w = wrkr(fns, init, id, function (err, dat) {\n if (err)\n w.terminate(), strm.ondata.call(strm, err);\n else if (!Array.isArray(dat))\n ext(dat);\n else if (dat.length == 1) {\n strm.queuedSize -= dat[0];\n if (strm.ondrain)\n strm.ondrain(dat[0]);\n }\n else {\n if (dat[1])\n w.terminate();\n strm.ondata.call(strm, err, dat[0], dat[1]);\n }\n });\n w.postMessage(opts);\n strm.queuedSize = 0;\n strm.push = function (d, f) {\n if (!strm.ondata)\n err(5);\n if (t)\n strm.ondata(err(4, 0, 1), null, !!f);\n strm.queuedSize += d.length;\n // can fail for cross-realm Uint8Array, but ok - only a small performance penalty\n w.postMessage([d, t = f], d.buffer instanceof ArrayBuffer ? [d.buffer] : []);\n };\n strm.terminate = function () { w.terminate(); };\n if (flush) {\n strm.flush = function (sync) { w.postMessage([0, sync]); };\n }\n};\n// read 2 bytes\nvar b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };\n// read 4 bytes\nvar b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16) | (d[b + 3] << 24)) >>> 0; };\n// read 8 bytes\nvar b8 = function (d, b) { return b4(d, b) + (b4(d, b + 4) * 4294967296); };\n// write bytes\nvar wbytes = function (d, b, v) {\n for (; v; ++b)\n d[b] = v, v >>>= 8;\n};\n// gzip header\nvar gzh = function (c, o) {\n var fn = o.filename;\n c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix\n if (o.mtime != 0)\n wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));\n if (fn) {\n c[3] = 8;\n for (var i = 0; i <= fn.length; ++i)\n c[i + 10] = fn.charCodeAt(i);\n }\n};\n// gzip footer: -8 to -4 = CRC, -4 to -0 is length\n// gzip start\nvar gzs = function (d) {\n if (d[0] != 31 || d[1] != 139 || d[2] != 8)\n err(6, 'invalid gzip data');\n var flg = d[3];\n var st = 10;\n if (flg & 4)\n st += (d[10] | d[11] << 8) + 2;\n for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])\n ;\n return st + (flg & 2);\n};\n// gzip length\nvar gzl = function (d) {\n var l = d.length;\n return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16 | d[l - 1] << 24) >>> 0;\n};\n// gzip header length\nvar gzhl = function (o) { return 10 + (o.filename ? o.filename.length + 1 : 0); };\n// zlib header\nvar zlh = function (c, o) {\n var lv = o.level, fl = lv == 0 ? 0 : lv < 6 ? 1 : lv == 9 ? 3 : 2;\n c[0] = 120, c[1] = (fl << 6) | (o.dictionary && 32);\n c[1] |= 31 - ((c[0] << 8) | c[1]) % 31;\n if (o.dictionary) {\n var h = adler();\n h.p(o.dictionary);\n wbytes(c, 2, h.d());\n }\n};\n// zlib start\nvar zls = function (d, dict) {\n if ((d[0] & 15) != 8 || (d[0] >> 4) > 7 || ((d[0] << 8 | d[1]) % 31))\n err(6, 'invalid zlib data');\n if ((d[1] >> 5 & 1) == +!dict)\n err(6, 'invalid zlib data: ' + (d[1] & 32 ? 'need' : 'unexpected') + ' dictionary');\n return (d[1] >> 3 & 4) + 2;\n};\nfunction StrmOpt(opts, cb) {\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n return opts;\n}\n/**\n * Streaming DEFLATE compression\n */\nvar Deflate = /*#__PURE__*/ (function () {\n function Deflate(opts, cb) {\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n this.o = opts || {};\n this.s = { l: 0, i: 32768, w: 32768, z: 32768 };\n // Buffer length must always be 0 mod 32768 for index calculations to be correct when modifying head and prev\n // 98304 = 32768 (lookback) + 65536 (common chunk size)\n this.b = new u8(98304);\n if (this.o.dictionary) {\n var dict = this.o.dictionary.subarray(-32768);\n this.b.set(dict, 32768 - dict.length);\n this.s.i = 32768 - dict.length;\n }\n }\n Deflate.prototype.p = function (c, f) {\n this.ondata(dopt(c, this.o, 0, 0, this.s), f);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Deflate.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.s.l)\n err(4);\n var endLen = chunk.length + this.s.z;\n if (endLen > this.b.length) {\n if (endLen > 2 * this.b.length - 32768) {\n var newBuf = new u8(endLen & -32768);\n newBuf.set(this.b.subarray(0, this.s.z));\n this.b = newBuf;\n }\n var split = this.b.length - this.s.z;\n this.b.set(chunk.subarray(0, split), this.s.z);\n this.s.z = this.b.length;\n this.p(this.b, false);\n this.b.set(this.b.subarray(-32768));\n this.b.set(chunk.subarray(split), 32768);\n this.s.z = chunk.length - split + 32768;\n this.s.i = 32766, this.s.w = 32768;\n }\n else {\n this.b.set(chunk, this.s.z);\n this.s.z += chunk.length;\n }\n this.s.l = final & 1;\n if (this.s.z > this.s.w + 8191 || final) {\n this.p(this.b, final || false);\n this.s.w = this.s.i, this.s.i -= 2;\n }\n if (final) {\n // cleanup unneeded buffers/state to reduce memory usage\n this.s = this.o = {};\n this.b = et;\n }\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * deflated output for small inputs.\n * @param sync Whether to flush to a byte boundary. A sync flush takes 4-5\n * extra bytes, but guarantees all pushed data is immediately\n * decompressible. A separate DEFLATE stream may be concatenated\n * with the current output after a sync flush.\n */\n Deflate.prototype.flush = function (sync) {\n if (!this.ondata)\n err(5);\n if (this.s.l)\n err(4);\n this.p(this.b, false);\n this.s.w = this.s.i, this.s.i -= 2;\n // could technically skip writing the type-0 block for (this.s.r & 7) == 0,\n // but the deterministic trailer (00 00 FF FF) is useful in some situations\n if (sync) {\n var c = new u8(6);\n c[0] = this.s.r >> 3;\n // write empty, non-final type-0 block\n var ep = wfblk(c, this.s.r, et);\n this.s.r = 0;\n this.ondata(c.subarray(0, ep >> 3), false);\n }\n };\n return Deflate;\n}());\nexport { Deflate };\n/**\n * Asynchronous streaming DEFLATE compression\n */\nvar AsyncDeflate = /*#__PURE__*/ (function () {\n function AsyncDeflate(opts, cb) {\n astrmify([\n bDflt,\n function () { return [astrm, Deflate]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Deflate(ev.data);\n onmessage = astrm(strm);\n }, 6, 1);\n }\n return AsyncDeflate;\n}());\nexport { AsyncDeflate };\nexport function deflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n ], function (ev) { return pbf(deflateSync(ev.data[0], ev.data[1])); }, 0, cb);\n}\n/**\n * Compresses data with DEFLATE without any wrapper\n * @param data The data to compress\n * @param opts The compression options\n * @returns The deflated version of the data\n */\nexport function deflateSync(data, opts) {\n return dopt(data, opts || {}, 0, 0);\n}\n/**\n * Streaming DEFLATE decompression\n */\nvar Inflate = /*#__PURE__*/ (function () {\n function Inflate(opts, cb) {\n // no StrmOpt here to avoid adding to workerizer\n if (typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n var dict = opts && opts.dictionary && opts.dictionary.subarray(-32768);\n this.s = { i: 0, b: dict ? dict.length : 0 };\n this.o = new u8(32768);\n this.p = new u8(0);\n if (dict)\n this.o.set(dict);\n }\n Inflate.prototype.e = function (c) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n if (!this.p.length)\n this.p = c;\n else if (c.length) {\n var n = new u8(this.p.length + c.length);\n n.set(this.p), n.set(c, this.p.length), this.p = n;\n }\n };\n Inflate.prototype.c = function (final) {\n this.s.i = +(this.d = final || false);\n var bts = this.s.b;\n var dt = inflt(this.p, this.s, this.o);\n this.ondata(slc(dt, bts, this.s.b), this.d);\n this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;\n this.p = slc(this.p, (this.s.p / 8) | 0), this.s.p &= 7;\n };\n /**\n * Pushes a chunk to be inflated\n * @param chunk The chunk to push\n * @param final Whether this is the final chunk\n */\n Inflate.prototype.push = function (chunk, final) {\n this.e(chunk), this.c(final);\n };\n return Inflate;\n}());\nexport { Inflate };\n/**\n * Asynchronous streaming DEFLATE decompression\n */\nvar AsyncInflate = /*#__PURE__*/ (function () {\n function AsyncInflate(opts, cb) {\n astrmify([\n bInflt,\n function () { return [astrm, Inflate]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Inflate(ev.data);\n onmessage = astrm(strm);\n }, 7, 0);\n }\n return AsyncInflate;\n}());\nexport { AsyncInflate };\nexport function inflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt\n ], function (ev) { return pbf(inflateSync(ev.data[0], gopt(ev.data[1]))); }, 1, cb);\n}\nexport function inflateSync(data, opts) {\n return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);\n}\n// before you yell at me for not just using extends, my reason is that TS inheritance is hard to workerize.\n/**\n * Streaming GZIP compression\n */\nvar Gzip = /*#__PURE__*/ (function () {\n function Gzip(opts, cb) {\n this.c = crc();\n this.l = 0;\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gzip.prototype.push = function (chunk, final) {\n this.c.p(chunk);\n this.l += chunk.length;\n Deflate.prototype.push.call(this, chunk, final);\n };\n Gzip.prototype.p = function (c, f) {\n var raw = dopt(c, this.o, this.v && gzhl(this.o), f && 8, this.s);\n if (this.v)\n gzh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 8, this.c.d()), wbytes(raw, raw.length - 4, this.l);\n this.ondata(raw, f);\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * GZIPped output for small inputs.\n * @param sync Whether to flush to a byte boundary. A sync flush takes 4-5\n * extra bytes, but guarantees all pushed data is immediately\n * decompressible.\n */\n Gzip.prototype.flush = function (sync) {\n Deflate.prototype.flush.call(this, sync);\n };\n return Gzip;\n}());\nexport { Gzip };\n/**\n * Asynchronous streaming GZIP compression\n */\nvar AsyncGzip = /*#__PURE__*/ (function () {\n function AsyncGzip(opts, cb) {\n astrmify([\n bDflt,\n gze,\n function () { return [astrm, Deflate, Gzip]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Gzip(ev.data);\n onmessage = astrm(strm);\n }, 8, 1);\n }\n return AsyncGzip;\n}());\nexport { AsyncGzip };\nexport function gzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n gze,\n function () { return [gzipSync]; }\n ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);\n}\n/**\n * Compresses data with GZIP\n * @param data The data to compress\n * @param opts The compression options\n * @returns The gzipped version of the data\n */\nexport function gzipSync(data, opts) {\n if (!opts)\n opts = {};\n var c = crc(), l = data.length;\n c.p(data);\n var d = dopt(data, opts, gzhl(opts), 8), s = d.length;\n return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;\n}\n/**\n * Streaming single or multi-member GZIP decompression\n */\nvar Gunzip = /*#__PURE__*/ (function () {\n function Gunzip(opts, cb) {\n this.v = 1;\n this.r = 0;\n Inflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GUNZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gunzip.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n this.r += chunk.length;\n if (this.v) {\n var p = this.p.subarray(this.v - 1);\n var s = p.length > 3 ? gzs(p) : 4;\n if (s > p.length) {\n if (!final)\n return;\n }\n else if (this.v > 1 && this.onmember) {\n this.onmember(this.r - p.length);\n }\n this.p = p.subarray(s), this.v = 0;\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, 0);\n // process concatenated GZIP\n if (this.s.f && !this.s.l) {\n this.v = shft(this.s.p) + 9;\n this.s = { i: 0 };\n this.o = new u8(0);\n this.push(new u8(0), final);\n }\n else if (final) {\n Inflate.prototype.c.call(this, final);\n }\n };\n return Gunzip;\n}());\nexport { Gunzip };\n/**\n * Asynchronous streaming single or multi-member GZIP decompression\n */\nvar AsyncGunzip = /*#__PURE__*/ (function () {\n function AsyncGunzip(opts, cb) {\n var _this = this;\n astrmify([\n bInflt,\n guze,\n function () { return [astrm, Inflate, Gunzip]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Gunzip(ev.data);\n strm.onmember = function (offset) { return postMessage(offset); };\n onmessage = astrm(strm);\n }, 9, 0, function (offset) { return _this.onmember && _this.onmember(offset); });\n }\n return AsyncGunzip;\n}());\nexport { AsyncGunzip };\nexport function gunzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n guze,\n function () { return [gunzipSync]; }\n ], function (ev) { return pbf(gunzipSync(ev.data[0], ev.data[1])); }, 3, cb);\n}\nexport function gunzipSync(data, opts) {\n var st = gzs(data);\n if (st + 8 > data.length)\n err(6, 'invalid gzip data');\n return inflt(data.subarray(st, -8), { i: 2 }, opts && opts.out || new u8(gzl(data)), opts && opts.dictionary);\n}\n/**\n * Streaming Zlib compression\n */\nvar Zlib = /*#__PURE__*/ (function () {\n function Zlib(opts, cb) {\n this.c = adler();\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be zlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Zlib.prototype.push = function (chunk, final) {\n this.c.p(chunk);\n Deflate.prototype.push.call(this, chunk, final);\n };\n Zlib.prototype.p = function (c, f) {\n var raw = dopt(c, this.o, this.v && (this.o.dictionary ? 6 : 2), f && 4, this.s);\n if (this.v)\n zlh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 4, this.c.d());\n this.ondata(raw, f);\n };\n /**\n * Flushes buffered uncompressed data. Useful to immediately retrieve the\n * zlibbed output for small inputs.\n * @param sync Whether to flush to a byte boundary. A sync flush takes 4-5\n * extra bytes, but guarantees all pushed data is immediately\n * decompressible.\n */\n Zlib.prototype.flush = function (sync) {\n Deflate.prototype.flush.call(this, sync);\n };\n return Zlib;\n}());\nexport { Zlib };\n/**\n * Asynchronous streaming Zlib compression\n */\nvar AsyncZlib = /*#__PURE__*/ (function () {\n function AsyncZlib(opts, cb) {\n astrmify([\n bDflt,\n zle,\n function () { return [astrm, Deflate, Zlib]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Zlib(ev.data);\n onmessage = astrm(strm);\n }, 10, 1);\n }\n return AsyncZlib;\n}());\nexport { AsyncZlib };\nexport function zlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bDflt,\n zle,\n function () { return [zlibSync]; }\n ], function (ev) { return pbf(zlibSync(ev.data[0], ev.data[1])); }, 4, cb);\n}\n/**\n * Compress data with Zlib\n * @param data The data to compress\n * @param opts The compression options\n * @returns The zlib-compressed version of the data\n */\nexport function zlibSync(data, opts) {\n if (!opts)\n opts = {};\n var a = adler();\n a.p(data);\n var d = dopt(data, opts, opts.dictionary ? 6 : 2, 4);\n return zlh(d, opts), wbytes(d, d.length - 4, a.d()), d;\n}\n/**\n * Streaming Zlib decompression\n */\nvar Unzlib = /*#__PURE__*/ (function () {\n function Unzlib(opts, cb) {\n Inflate.call(this, opts, cb);\n this.v = opts && opts.dictionary ? 2 : 1;\n }\n /**\n * Pushes a chunk to be unzlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzlib.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n if (this.p.length < 6 && !final)\n return;\n this.p = this.p.subarray(zls(this.p, this.v - 1)), this.v = 0;\n }\n if (final) {\n if (this.p.length < 4)\n err(6, 'invalid zlib data');\n this.p = this.p.subarray(0, -4);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Unzlib;\n}());\nexport { Unzlib };\n/**\n * Asynchronous streaming Zlib decompression\n */\nvar AsyncUnzlib = /*#__PURE__*/ (function () {\n function AsyncUnzlib(opts, cb) {\n astrmify([\n bInflt,\n zule,\n function () { return [astrm, Inflate, Unzlib]; }\n ], this, StrmOpt.call(this, opts, cb), function (ev) {\n var strm = new Unzlib(ev.data);\n onmessage = astrm(strm);\n }, 11, 0);\n }\n return AsyncUnzlib;\n}());\nexport { AsyncUnzlib };\nexport function unzlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return cbify(data, opts, [\n bInflt,\n zule,\n function () { return [unzlibSync]; }\n ], function (ev) { return pbf(unzlibSync(ev.data[0], gopt(ev.data[1]))); }, 5, cb);\n}\nexport function unzlibSync(data, opts) {\n return inflt(data.subarray(zls(data, opts && opts.dictionary), -4), { i: 2 }, opts && opts.out, opts && opts.dictionary);\n}\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzip as compress, AsyncGzip as AsyncCompress };\nexport { gzipSync as compressSync, Gzip as Compress };\n/**\n * Streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar Decompress = /*#__PURE__*/ (function () {\n function Decompress(opts, cb) {\n this.o = StrmOpt.call(this, opts, cb) || {};\n this.G = Gunzip;\n this.I = Inflate;\n this.Z = Unzlib;\n }\n // init substream\n // overriden by AsyncDecompress\n Decompress.prototype.i = function () {\n var _this = this;\n this.s.ondata = function (dat, final) {\n _this.ondata(dat, final);\n };\n };\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Decompress.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (!this.s) {\n if (this.p && this.p.length) {\n var n = new u8(this.p.length + chunk.length);\n n.set(this.p), n.set(chunk, this.p.length);\n }\n else\n this.p = chunk;\n if (this.p.length > 2) {\n this.s = (this.p[0] == 31 && this.p[1] == 139 && this.p[2] == 8)\n ? new this.G(this.o)\n : ((this.p[0] & 15) != 8 || (this.p[0] >> 4) > 7 || ((this.p[0] << 8 | this.p[1]) % 31))\n ? new this.I(this.o)\n : new this.Z(this.o);\n this.i();\n this.s.push(this.p, final);\n this.p = null;\n }\n }\n else\n this.s.push(chunk, final);\n };\n return Decompress;\n}());\nexport { Decompress };\n/**\n * Asynchronous streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar AsyncDecompress = /*#__PURE__*/ (function () {\n function AsyncDecompress(opts, cb) {\n Decompress.call(this, opts, cb);\n this.queuedSize = 0;\n this.G = AsyncGunzip;\n this.I = AsyncInflate;\n this.Z = AsyncUnzlib;\n }\n AsyncDecompress.prototype.i = function () {\n var _this = this;\n this.s.ondata = function (err, dat, final) {\n _this.ondata(err, dat, final);\n };\n this.s.ondrain = function (size) {\n _this.queuedSize -= size;\n if (_this.ondrain)\n _this.ondrain(size);\n };\n };\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncDecompress.prototype.push = function (chunk, final) {\n this.queuedSize += chunk.length;\n Decompress.prototype.push.call(this, chunk, final);\n };\n return AsyncDecompress;\n}());\nexport { AsyncDecompress };\nexport function decompress(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzip(data, opts, cb)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflate(data, opts, cb)\n : unzlib(data, opts, cb);\n}\n/**\n * Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format\n * @param data The data to decompress\n * @param opts The decompression options\n * @returns The decompressed version of the data\n */\nexport function decompressSync(data, opts) {\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzipSync(data, opts)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflateSync(data, opts)\n : unzlibSync(data, opts);\n}\n// flatten a directory structure\nvar fltn = function (d, p, t, o) {\n for (var k in d) {\n var val = d[k], n = p + k, op = o;\n if (Array.isArray(val))\n op = mrg(o, val[1]), val = val[0];\n if (ArrayBuffer.isView(val))\n t[n] = [val, op];\n else {\n t[n += '/'] = [new u8(0), op];\n fltn(val, n, t, o);\n }\n }\n};\n// text encoder\nvar te = typeof TextEncoder != 'undefined' && /*#__PURE__*/ new TextEncoder();\n// text decoder\nvar td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();\n// text decoder stream\nvar tds = 0;\ntry {\n td.decode(et, { stream: true });\n tds = 1;\n}\ncatch (e) { }\n// decode UTF8\nvar dutf8 = function (d) {\n for (var r = '', i = 0;;) {\n var c = d[i++];\n var eb = (c > 127) + (c > 223) + (c > 239);\n if (i + eb > d.length)\n return { s: r, r: slc(d, i - 1) };\n if (!eb)\n r += String.fromCharCode(c);\n else if (eb == 3) {\n c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63)) - 65536,\n r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));\n }\n else if (eb & 1)\n r += String.fromCharCode((c & 31) << 6 | (d[i++] & 63));\n else\n r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63));\n }\n};\n/**\n * Streaming UTF-8 decoding\n */\nvar DecodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is decoded\n */\n function DecodeUTF8(cb) {\n this.ondata = cb;\n if (tds)\n this.t = new TextDecoder();\n else\n this.p = et;\n }\n /**\n * Pushes a chunk to be decoded from UTF-8 binary\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n DecodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n final = !!final;\n if (this.t) {\n this.ondata(this.t.decode(chunk, { stream: true }), final);\n if (final) {\n if (this.t.decode().length)\n err(8);\n this.t = null;\n }\n return;\n }\n if (!this.p)\n err(4);\n var dat = new u8(this.p.length + chunk.length);\n dat.set(this.p);\n dat.set(chunk, this.p.length);\n var _a = dutf8(dat), s = _a.s, r = _a.r;\n if (final) {\n if (r.length)\n err(8);\n this.p = null;\n }\n else\n this.p = r;\n this.ondata(s, final);\n };\n return DecodeUTF8;\n}());\nexport { DecodeUTF8 };\n/**\n * Streaming UTF-8 encoding\n */\nvar EncodeUTF8 = /*#__PURE__*/ (function () {\n /**\n * Creates a UTF-8 decoding stream\n * @param cb The callback to call whenever data is encoded\n */\n function EncodeUTF8(cb) {\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be encoded to UTF-8\n * @param chunk The string data to push\n * @param final Whether this is the last chunk\n */\n EncodeUTF8.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n if (this.d)\n err(4);\n this.ondata(strToU8(chunk), this.d = final || false);\n };\n return EncodeUTF8;\n}());\nexport { EncodeUTF8 };\n/**\n * Converts a string into a Uint8Array for use with compression/decompression methods\n * @param str The string to encode\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless decoding a binary string.\n * @returns The string encoded in UTF-8/Latin-1 binary\n */\nexport function strToU8(str, latin1) {\n if (latin1) {\n var ar_1 = new u8(str.length);\n for (var i = 0; i < str.length; ++i)\n ar_1[i] = str.charCodeAt(i);\n return ar_1;\n }\n if (te)\n return te.encode(str);\n var l = str.length;\n var ar = new u8(str.length + (str.length >> 1));\n var ai = 0;\n var w = function (v) { ar[ai++] = v; };\n for (var i = 0; i < l; ++i) {\n if (ai + 5 > ar.length) {\n var n = new u8(ai + 8 + ((l - i) << 1));\n n.set(ar);\n ar = n;\n }\n var c = str.charCodeAt(i);\n if (c < 128 || latin1)\n w(c);\n else if (c < 2048)\n w(192 | (c >> 6)), w(128 | (c & 63));\n else if (c > 55295 && c < 57344)\n c = 65536 + (c & 1023 << 10) | (str.charCodeAt(++i) & 1023),\n w(240 | (c >> 18)), w(128 | ((c >> 12) & 63)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n else\n w(224 | (c >> 12)), w(128 | ((c >> 6) & 63)), w(128 | (c & 63));\n }\n return slc(ar, 0, ai);\n}\n/**\n * Converts a Uint8Array to a string\n * @param dat The data to decode to string\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless encoding to binary string.\n * @returns The original UTF-8/Latin-1 string\n */\nexport function strFromU8(dat, latin1) {\n if (latin1) {\n var r = '';\n for (var i = 0; i < dat.length; i += 16384)\n r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));\n return r;\n }\n else if (td) {\n return td.decode(dat);\n }\n else {\n var _a = dutf8(dat), s = _a.s, r = _a.r;\n if (r.length)\n err(8);\n return s;\n }\n}\n;\n// deflate bit flag\nvar dbf = function (l) { return l == 1 ? 3 : l < 6 ? 2 : l == 9 ? 1 : 0; };\n// skip local zip header\nvar slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };\n// read zip header\nvar zh = function (d, b, z) {\n var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;\n var _a = z64hs(d, es, efl, z, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a[0], su = _a[1], off = _a[2];\n return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];\n};\n// read zip64 header sizes\nvar z64hs = function (d, b, l, z, sc, su, off) {\n var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;\n var nf = nsc + nsu + noff;\n if (z && nf) {\n for (; b + 4 < e; b += 4 + b2(d, b + 2)) {\n if (b2(d, b) == 1) {\n return [\n nsc ? b8(d, b + 4 + 8 * nsu) : sc,\n nsu ? b8(d, b + 4) : su,\n noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,\n 1\n ];\n }\n }\n // z == 2 for unknown whether or not zip64\n if (z < 2)\n err(13);\n }\n return [sc, su, off, 0];\n};\n// extra field length\nvar exfl = function (ex) {\n var le = 0;\n if (ex) {\n for (var k in ex) {\n var l = ex[k].length;\n if (l > 65535)\n err(9);\n le += l + 4;\n }\n }\n return le;\n};\n// write zip header\nvar wzh = function (d, b, f, fn, u, c, ce, co) {\n var fl = fn.length, ex = f.extra, col = co && co.length;\n var exl = exfl(ex);\n wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;\n if (ce != null)\n d[b++] = 20, d[b++] = f.os;\n d[b] = 20, b += 2; // spec compliance? what's that?\n d[b++] = (f.flag << 1) | (c < 0 && 8), d[b++] = u && 8;\n d[b++] = f.compression & 255, d[b++] = f.compression >> 8;\n var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;\n if (y < 0 || y > 119)\n err(10);\n wbytes(d, b, (y << 25) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >> 1)), b += 4;\n if (c != -1) {\n wbytes(d, b, f.crc);\n wbytes(d, b + 4, c < 0 ? -c - 2 : c);\n wbytes(d, b + 8, f.size);\n }\n wbytes(d, b + 12, fl);\n wbytes(d, b + 14, exl), b += 16;\n if (ce != null) {\n wbytes(d, b, col);\n wbytes(d, b + 6, f.attrs);\n wbytes(d, b + 10, ce), b += 14;\n }\n d.set(fn, b);\n b += fl;\n if (exl) {\n for (var k in ex) {\n var exf = ex[k], l = exf.length;\n wbytes(d, b, +k);\n wbytes(d, b + 2, l);\n d.set(exf, b + 4), b += 4 + l;\n }\n }\n if (col)\n d.set(co, b), b += col;\n return b;\n};\n// write zip footer (end of central directory)\nvar wzf = function (o, b, c, d, e) {\n wbytes(o, b, 0x6054B50); // skip disk\n wbytes(o, b + 8, c);\n wbytes(o, b + 10, c);\n wbytes(o, b + 12, d);\n wbytes(o, b + 16, e);\n};\n/**\n * A pass-through stream to keep data uncompressed in a ZIP archive.\n */\nvar ZipPassThrough = /*#__PURE__*/ (function () {\n /**\n * Creates a pass-through stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n */\n function ZipPassThrough(filename) {\n this.filename = filename;\n this.c = crc();\n this.size = 0;\n this.compression = 0;\n }\n /**\n * Processes a chunk and pushes to the output stream. You can override this\n * method in a subclass for custom behavior, but by default this passes\n * the data through. You must call this.ondata(err, chunk, final) at some\n * point in this method.\n * @param chunk The chunk to process\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.process = function (chunk, final) {\n this.ondata(null, chunk, final);\n };\n /**\n * Pushes a chunk to be added. If you are subclassing this with a custom\n * compression algorithm, note that you must push data from the source\n * file only, pre-compression.\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipPassThrough.prototype.push = function (chunk, final) {\n if (!this.ondata)\n err(5);\n this.c.p(chunk);\n this.size += chunk.length;\n if (final)\n this.crc = this.c.d();\n // we shouldn't really do this cast, but properly handling ArrayBufferLike\n // makes the API unergonomic with Buffer\n this.process(chunk, final || false);\n };\n return ZipPassThrough;\n}());\nexport { ZipPassThrough };\n// I don't extend because TypeScript extension adds 1kB of runtime bloat\n/**\n * Streaming DEFLATE compression for ZIP archives. Prefer using AsyncZipDeflate\n * for better performance\n */\nvar ZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function ZipDeflate(filename, opts) {\n var _this = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new Deflate(opts, function (dat, final) {\n _this.ondata(null, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n }\n ZipDeflate.prototype.process = function (chunk, final) {\n try {\n this.d.push(chunk, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n ZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return ZipDeflate;\n}());\nexport { ZipDeflate };\n/**\n * Asynchronous streaming DEFLATE compression for ZIP archives\n */\nvar AsyncZipDeflate = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous DEFLATE stream that can be added to ZIP archives\n * @param filename The filename to associate with this data stream\n * @param opts The compression options\n */\n function AsyncZipDeflate(filename, opts) {\n var _this = this;\n if (!opts)\n opts = {};\n ZipPassThrough.call(this, filename);\n this.d = new AsyncDeflate(opts, function (err, dat, final) {\n _this.ondata(err, dat, final);\n });\n this.compression = 8;\n this.flag = dbf(opts.level);\n this.terminate = this.d.terminate;\n }\n AsyncZipDeflate.prototype.process = function (chunk, final) {\n this.d.push(chunk, final);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncZipDeflate.prototype.push = function (chunk, final) {\n ZipPassThrough.prototype.push.call(this, chunk, final);\n };\n return AsyncZipDeflate;\n}());\nexport { AsyncZipDeflate };\n// TODO: Better tree shaking\n/**\n * A zippable archive to which files can incrementally be added\n */\nvar Zip = /*#__PURE__*/ (function () {\n /**\n * Creates an empty ZIP archive to which files can be added\n * @param cb The callback to call whenever data for the generated ZIP archive\n * is available\n */\n function Zip(cb) {\n this.ondata = cb;\n this.u = [];\n this.d = 1;\n }\n /**\n * Adds a file to the ZIP archive\n * @param file The file stream to add\n */\n Zip.prototype.add = function (file) {\n var _this = this;\n if (!this.ondata)\n err(5);\n // finishing or finished\n if (this.d & 2)\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, false);\n else {\n var f = strToU8(file.filename), fl_1 = f.length;\n var com = file.comment, o = com && strToU8(com);\n var u = fl_1 != file.filename.length || (o && (com.length != o.length));\n var hl_1 = fl_1 + exfl(file.extra) + 30;\n if (fl_1 > 65535)\n this.ondata(err(11, 0, 1), null, false);\n var header = new u8(hl_1);\n wzh(header, 0, file, f, u, -1);\n var chks_1 = [header];\n var pAll_1 = function () {\n for (var _i = 0, chks_2 = chks_1; _i < chks_2.length; _i++) {\n var chk = chks_2[_i];\n _this.ondata(null, chk, false);\n }\n chks_1 = [];\n };\n var tr_1 = this.d;\n this.d = 0;\n var ind_1 = this.u.length;\n var uf_1 = mrg(file, {\n f: f,\n u: u,\n o: o,\n t: function () {\n if (file.terminate)\n file.terminate();\n },\n r: function () {\n pAll_1();\n if (tr_1) {\n var nxt = _this.u[ind_1 + 1];\n if (nxt)\n nxt.r();\n else\n _this.d = 1;\n }\n tr_1 = 1;\n }\n });\n var cl_1 = 0;\n file.ondata = function (err, dat, final) {\n if (err) {\n _this.ondata(err, dat, final);\n _this.terminate();\n }\n else {\n cl_1 += dat.length;\n chks_1.push(dat);\n if (final) {\n var dd = new u8(16);\n wbytes(dd, 0, 0x8074B50);\n wbytes(dd, 4, file.crc);\n wbytes(dd, 8, cl_1);\n wbytes(dd, 12, file.size);\n chks_1.push(dd);\n uf_1.c = cl_1, uf_1.b = hl_1 + cl_1 + 16, uf_1.crc = file.crc, uf_1.size = file.size;\n if (tr_1)\n uf_1.r();\n tr_1 = 1;\n }\n else if (tr_1)\n pAll_1();\n }\n };\n this.u.push(uf_1);\n }\n };\n /**\n * Ends the process of adding files and prepares to emit the final chunks.\n * This *must* be called after adding all desired files for the resulting\n * ZIP file to work properly.\n */\n Zip.prototype.end = function () {\n var _this = this;\n if (this.d & 2) {\n this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, true);\n return;\n }\n if (this.d)\n this.e();\n else\n this.u.push({\n r: function () {\n if (!(_this.d & 1))\n return;\n _this.u.splice(-1, 1);\n _this.e();\n },\n t: function () { }\n });\n this.d = 3;\n };\n Zip.prototype.e = function () {\n var bt = 0, l = 0, tl = 0;\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n tl += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0);\n }\n var out = new u8(tl + 22);\n for (var _b = 0, _c = this.u; _b < _c.length; _b++) {\n var f = _c[_b];\n wzh(out, bt, f, f.f, f.u, -f.c - 2, l, f.o);\n bt += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0), l += f.b;\n }\n wzf(out, bt, this.u.length, tl, l);\n this.ondata(null, out, true);\n this.d = 2;\n };\n /**\n * A method to terminate any internal workers used by the stream. Subsequent\n * calls to add() will fail.\n */\n Zip.prototype.terminate = function () {\n for (var _i = 0, _a = this.u; _i < _a.length; _i++) {\n var f = _a[_i];\n f.t();\n }\n this.d = 2;\n };\n return Zip;\n}());\nexport { Zip };\nexport function zip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var r = {};\n fltn(data, '', r, opts);\n var k = Object.keys(r);\n var lft = k.length, o = 0, tot = 0;\n var slft = lft, files = new Array(lft);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var cbf = function () {\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n tot = 0;\n for (var i = 0; i < slft; ++i) {\n var f = files[i];\n try {\n var l = f.c.length;\n wzh(out, tot, f, f.f, f.u, l);\n var badd = 30 + f.f.length + exfl(f.extra);\n var loc = tot + badd;\n out.set(f.c, loc);\n wzh(out, o, f, f.f, f.u, l, tot, f.m), o += 16 + badd + (f.m ? f.m.length : 0), tot = loc + l;\n }\n catch (e) {\n return cbd(e, null);\n }\n }\n wzf(out, o, files.length, cdl, oe);\n cbd(null, out);\n };\n if (!lft)\n cbf();\n var _loop_1 = function (i) {\n var fn = k[i];\n var _a = r[fn], file = _a[0], p = _a[1];\n var c = crc(), size = file.length;\n c.p(file);\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n var compression = p.level == 0 ? 0 : 8;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n var l = d.length;\n files[i] = mrg(p, {\n size: size,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n compression: compression\n });\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n if (!--lft)\n cbf();\n }\n };\n if (s > 65535)\n cbl(err(11, 0, 1), null);\n if (!compression)\n cbl(null, file);\n else if (size < 160000) {\n try {\n cbl(null, deflateSync(file, p));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(deflate(file, p, cbl));\n };\n // Cannot use lft because it can decrease\n for (var i = 0; i < slft; ++i) {\n _loop_1(i);\n }\n return tAll;\n}\n/**\n * Synchronously creates a ZIP file. Prefer using `zip` for better performance\n * with more than one file.\n * @param data The directory structure for the ZIP archive\n * @param opts The main options, merged with per-file options\n * @returns The generated ZIP archive\n */\nexport function zipSync(data, opts) {\n if (!opts)\n opts = {};\n var r = {};\n var files = [];\n fltn(data, '', r, opts);\n var o = 0;\n var tot = 0;\n for (var fn in r) {\n var _a = r[fn], file = _a[0], p = _a[1];\n var compression = p.level == 0 ? 0 : 8;\n var f = strToU8(fn), s = f.length;\n var com = p.comment, m = com && strToU8(com), ms = m && m.length;\n var exl = exfl(p.extra);\n if (s > 65535)\n err(11);\n var d = compression ? deflateSync(file, p) : file, l = d.length;\n var c = crc();\n c.p(file);\n files.push(mrg(p, {\n size: file.length,\n crc: c.d(),\n c: d,\n f: f,\n m: m,\n u: s != fn.length || (m && (com.length != ms)),\n o: o,\n compression: compression\n }));\n o += 30 + s + exl + l;\n tot += 76 + 2 * (s + exl) + (ms || 0) + l;\n }\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n for (var i = 0; i < files.length; ++i) {\n var f = files[i];\n wzh(out, f.o, f, f.f, f.u, f.c.length);\n var badd = 30 + f.f.length + exfl(f.extra);\n out.set(f.c, f.o + badd);\n wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);\n }\n wzf(out, o, files.length, cdl, oe);\n return out;\n}\n/**\n * Streaming pass-through decompression for ZIP archives\n */\nvar UnzipPassThrough = /*#__PURE__*/ (function () {\n function UnzipPassThrough() {\n }\n UnzipPassThrough.prototype.push = function (chunk, final) {\n // same as ZipPassThrough: cast to retain Buffer ergonomics\n this.ondata(null, chunk, final);\n };\n UnzipPassThrough.compression = 0;\n return UnzipPassThrough;\n}());\nexport { UnzipPassThrough };\n/**\n * Streaming DEFLATE decompression for ZIP archives. Prefer AsyncZipInflate for\n * better performance.\n */\nvar UnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function UnzipInflate() {\n var _this = this;\n this.i = new Inflate(function (dat, final) {\n _this.ondata(null, dat, final);\n });\n }\n UnzipInflate.prototype.push = function (chunk, final) {\n try {\n this.i.push(chunk, final);\n }\n catch (e) {\n this.ondata(e, null, final);\n }\n };\n UnzipInflate.compression = 8;\n return UnzipInflate;\n}());\nexport { UnzipInflate };\n/**\n * Asynchronous streaming DEFLATE decompression for ZIP archives\n */\nvar AsyncUnzipInflate = /*#__PURE__*/ (function () {\n /**\n * Creates a DEFLATE decompression that can be used in ZIP archives\n */\n function AsyncUnzipInflate(_, sz) {\n var _this = this;\n if (sz < 320000) {\n this.i = new Inflate(function (dat, final) {\n _this.ondata(null, dat, final);\n });\n }\n else {\n this.i = new AsyncInflate(function (err, dat, final) {\n _this.ondata(err, dat, final);\n });\n this.terminate = this.i.terminate;\n }\n }\n AsyncUnzipInflate.prototype.push = function (chunk, final) {\n if (this.i.terminate)\n chunk = slc(chunk, 0);\n this.i.push(chunk, final);\n };\n AsyncUnzipInflate.compression = 8;\n return AsyncUnzipInflate;\n}());\nexport { AsyncUnzipInflate };\n/**\n * A ZIP archive decompression stream that emits files as they are discovered\n */\nvar Unzip = /*#__PURE__*/ (function () {\n /**\n * Creates a ZIP decompression stream\n * @param cb The callback to call whenever a file in the ZIP archive is found\n */\n function Unzip(cb) {\n this.onfile = cb;\n this.k = [];\n this.o = {\n 0: UnzipPassThrough\n };\n this.p = et;\n }\n /**\n * Pushes a chunk to be unzipped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzip.prototype.push = function (chunk, final) {\n var _this = this;\n if (!this.onfile)\n err(5);\n if (!this.p)\n err(4);\n if (this.c > 0) {\n var len = Math.min(this.c, chunk.length);\n var toAdd = chunk.subarray(0, len);\n this.c -= len;\n if (this.d)\n this.d.push(toAdd, !this.c);\n else\n this.k[0].push(toAdd);\n chunk = chunk.subarray(len);\n if (chunk.length)\n return this.push(chunk, final);\n }\n else {\n var f = 0, i = 0, is = void 0, buf = void 0;\n if (!this.p.length)\n buf = chunk;\n else if (!chunk.length)\n buf = this.p;\n else {\n buf = new u8(this.p.length + chunk.length);\n buf.set(this.p), buf.set(chunk, this.p.length);\n }\n var l = buf.length, oc = this.c, add = oc && this.d;\n var _loop_2 = function () {\n var sig = b4(buf, i);\n if (sig == 0x4034B50) {\n f = 1, is = i;\n this_1.d = null;\n this_1.c = 0;\n var bf = b2(buf, i + 6), cmp_1 = b2(buf, i + 8), u = bf & 2048, dd = bf & 8, fnl = b2(buf, i + 26), es = b2(buf, i + 28);\n if (l > i + 30 + fnl + es) {\n var chks_3 = [];\n this_1.k.unshift(chks_3);\n f = 2;\n var lsc = b4(buf, i + 18), lsu = b4(buf, i + 22);\n var fn_1 = strFromU8(buf.subarray(i + 30, i += 30 + fnl), !u);\n var _a = z64hs(buf, i, es, 2, lsc, lsu, 0), sc_1 = _a[0], su_1 = _a[1], z64 = _a[3];\n if (dd)\n sc_1 = -1 - z64;\n i += es;\n this_1.c = sc_1;\n var d_1;\n var file_1 = {\n name: fn_1,\n compression: cmp_1,\n start: function () {\n if (!file_1.ondata)\n err(5);\n if (!sc_1)\n file_1.ondata(null, et, true);\n else {\n var ctr = _this.o[cmp_1];\n if (!ctr)\n file_1.ondata(err(14, 'unknown compression type ' + cmp_1, 1), null, false);\n d_1 = sc_1 < 0 ? new ctr(fn_1) : new ctr(fn_1, sc_1, su_1);\n d_1.ondata = function (err, dat, final) { file_1.ondata(err, dat, final); };\n for (var _i = 0, chks_4 = chks_3; _i < chks_4.length; _i++) {\n var dat = chks_4[_i];\n d_1.push(dat, false);\n }\n if (_this.k[0] == chks_3 && _this.c)\n _this.d = d_1;\n else\n d_1.push(et, true);\n }\n },\n terminate: function () {\n if (d_1 && d_1.terminate)\n d_1.terminate();\n }\n };\n if (sc_1 >= 0)\n file_1.size = sc_1, file_1.originalSize = su_1;\n this_1.onfile(file_1);\n }\n return \"break\";\n }\n else if (oc) {\n if (sig == 0x8074B50) {\n is = i += 12 + (oc == -2 && 8), f = 3, this_1.c = 0;\n return \"break\";\n }\n else if (sig == 0x2014B50) {\n is = i -= 4, f = 3, this_1.c = 0;\n return \"break\";\n }\n }\n };\n var this_1 = this;\n for (; i < l - 4; ++i) {\n var state_1 = _loop_2();\n if (state_1 === \"break\")\n break;\n }\n this.p = et;\n if (oc < 0) {\n var dat = f ? buf.subarray(0, is - 12 - (oc == -2 && 8) - (b4(buf, is - 16) == 0x8074B50 && 4)) : buf.subarray(0, i);\n if (add)\n add.push(dat, !!f);\n else\n this.k[+(f == 2)].push(dat);\n }\n if (f & 2)\n return this.push(buf.subarray(i), final);\n this.p = buf.subarray(i);\n }\n if (final) {\n if (this.c)\n err(13);\n this.p = null;\n }\n };\n /**\n * Registers a decoder with the stream, allowing for files compressed with\n * the compression type provided to be expanded correctly\n * @param decoder The decoder constructor\n */\n Unzip.prototype.register = function (decoder) {\n this.o[decoder.compression] = decoder;\n };\n return Unzip;\n}());\nexport { Unzip };\nvar mt = typeof queueMicrotask == 'function' ? queueMicrotask : typeof setTimeout == 'function' ? setTimeout : function (fn) { fn(); };\nexport function unzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n err(7);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var files = {};\n var cbd = function (a, b) {\n mt(function () { cb(a, b); });\n };\n mt(function () { cbd = cb; });\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558) {\n cbd(err(13, 0, 1), null);\n return tAll;\n }\n }\n ;\n var lft = b2(data, e + 8);\n if (lft) {\n var c = lft;\n var o = b4(data, e + 16);\n var z = b4(data, e - 20) == 0x7064B50;\n if (z) {\n var ze = b4(data, e - 12);\n z = b4(data, ze) == 0x6064B50;\n if (z) {\n c = lft = b4(data, ze + 32);\n o = b4(data, ze + 48);\n }\n }\n var fltr = opts && opts.filter;\n var _loop_3 = function (i) {\n var _a = zh(data, o, z), c_1 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cbd(e, null);\n }\n else {\n if (d)\n files[fn] = d;\n if (!--lft)\n cbd(null, files);\n }\n };\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_1\n })) {\n if (!c_1)\n cbl(null, slc(data, b, b + sc));\n else if (c_1 == 8) {\n var infl = data.subarray(b, b + sc);\n // Synchronously decompress under 512KB, or barely-compressed data\n if (su < 524288 || sc > 0.8 * su) {\n try {\n cbl(null, inflateSync(infl, { out: new u8(su) }));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(inflate(infl, { size: su }, cbl));\n }\n else\n cbl(err(14, 'unknown compression type ' + c_1, 1), null);\n }\n else\n cbl(null, null);\n };\n for (var i = 0; i < c; ++i) {\n _loop_3(i);\n }\n }\n else\n cbd(null, {});\n return tAll;\n}\n/**\n * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better\n * performance with more than one file.\n * @param data The raw compressed ZIP file\n * @param opts The ZIP extraction options\n * @returns The decompressed files\n */\nexport function unzipSync(data, opts) {\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558)\n err(13);\n }\n ;\n var c = b2(data, e + 8);\n if (!c)\n return {};\n var o = b4(data, e + 16);\n var z = b4(data, e - 20) == 0x7064B50;\n if (z) {\n var ze = b4(data, e - 12);\n z = b4(data, ze) == 0x6064B50;\n if (z) {\n c = b4(data, ze + 32);\n o = b4(data, ze + 48);\n }\n }\n var fltr = opts && opts.filter;\n for (var i = 0; i < c; ++i) {\n var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n if (!fltr || fltr({\n name: fn,\n size: sc,\n originalSize: su,\n compression: c_2\n })) {\n if (!c_2)\n files[fn] = slc(data, b, b + sc);\n else if (c_2 == 8)\n files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });\n else\n err(14, 'unknown compression type ' + c_2);\n }\n }\n return files;\n}\n",
|
|
27
|
+
"// PluginRegistry — install records (manifest, package files, granted caps, trust\n// status, settings, secrets) persisted in IndexedDB so plugins survive reloads on\n// this device. Package bytes stay on-device (they can be large); non-secret\n// settings can also be mirrored to the server (see the settings service) so they\n// follow the user. A plugin's OWN data lives in per-scope SQLite databases (server\n// via the host API; on-device via the wasm store) — not here.\n\nconst DB = 'trove-plugins';\nconst STORE = 'installs';\n\nfunction openDb() {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE, { keyPath: 'id' });\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\nfunction tx(db, mode, fn) {\n return new Promise((resolve, reject) => {\n const t = db.transaction(STORE, mode);\n const s = t.objectStore(STORE);\n let out;\n Promise.resolve(fn(s)).then((r) => (out = r));\n t.oncomplete = () => resolve(out);\n t.onerror = () => reject(t.error);\n });\n}\nconst reqP = (r) => new Promise((res, rej) => { r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); });\n\nexport class PluginRegistry {\n constructor() {\n this._db = null;\n }\n async #db() {\n return (this._db ??= await openDb());\n }\n /** record: { id, manifest, files:{path:Uint8Array}, grants, trust, settings, secrets, installedAt } */\n async save(record) {\n const db = await this.#db();\n await tx(db, 'readwrite', (s) => s.put(record));\n return record;\n }\n async get(id) {\n const db = await this.#db();\n return tx(db, 'readonly', (s) => reqP(s.get(id)));\n }\n async list() {\n const db = await this.#db();\n return tx(db, 'readonly', (s) => reqP(s.getAll()));\n }\n async remove(id) {\n const db = await this.#db();\n await tx(db, 'readwrite', (s) => s.delete(id));\n }\n async patch(id, patch) {\n const rec = await this.get(id);\n if (!rec) return null;\n const next = { ...rec, ...patch };\n await this.save(next);\n return next;\n }\n}\n\n",
|
|
28
|
+
"// Client-side plugin storage: an on-device SQLite database per scope, run in the\n// HOST with sql.js (wasm) and persisted to IndexedDB. The sandboxed iframe can't\n// touch IndexedDB (opaque origin), so — like everything else — the host holds the\n// store and the plugin reaches it over RPC. It exposes the SAME async\n// SqliteDatabase surface as the server side, so ctx.storage.*.client mirrors\n// .server. Lazy: the wasm only loads the first time a plugin uses client storage.\n\nlet sqlPromise = null;\nfunction loadSql() {\n // locateFile points at the wasm we serve at the app root (build copies it; dev &\n // test serve it via middleware). Only fetched on first use. On failure we clear\n // the cached promise so a later attempt can retry rather than wedging forever.\n if (!sqlPromise) {\n sqlPromise = import('sql.js')\n .then((m) => (m.default || m)({ locateFile: () => '/sql-wasm.wasm' }))\n .catch((err) => { sqlPromise = null; throw err; });\n }\n return sqlPromise;\n}\n\n// --- IndexedDB blob persistence (host origin) --------------------------------\n\nconst IDB = 'trove-plugin-clientdb';\nconst STORE = 'dbs';\n\nfunction openIdb() {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(IDB, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\nfunction idbOp(mode, fn) {\n return openIdb().then((db) => new Promise((resolve, reject) => {\n const t = db.transaction(STORE, mode);\n let out;\n Promise.resolve(fn(t.objectStore(STORE))).then((r) => (out = r));\n t.oncomplete = () => { db.close(); resolve(out); };\n t.onerror = () => { db.close(); reject(t.error); };\n }));\n}\nconst reqP = (r) => new Promise((res, rej) => { r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); });\nconst loadBytes = (key) => idbOp('readonly', (s) => reqP(s.get(key)));\nconst saveBytes = (key, bytes) => idbOp('readwrite', (s) => s.put(bytes, key));\nconst dropBytes = (key) => idbOp('readwrite', (s) => s.delete(key));\n\n// --- a single scoped database (conforms to SqliteDatabase) -------------------\n\nclass ClientDatabase {\n constructor(key, db) {\n this.key = key;\n this.db = db;\n this._saveTimer = null;\n }\n // Debounce persistence: export the whole (small, per-scope) db to IndexedDB\n // shortly after the last write.\n //\n // The write already returned `{ok:true}` to the plugin by the time this runs, so a\n // failure here is data the plugin believes it saved and hasn't. Nothing can be\n // returned to it any more — but it must at least be visible, not swallowed.\n #dirty() {\n clearTimeout(this._saveTimer);\n this._saveTimer = setTimeout(() => {\n this.flush().catch((err) => {\n console.error(`persisting plugin storage \"${this.key}\" failed`, err);\n this.onError?.(err, this.key);\n });\n }, 150);\n }\n async flush() {\n clearTimeout(this._saveTimer);\n this._saveTimer = null;\n await saveBytes(this.key, this.db.export());\n }\n async exec(sql) { this.db.exec(sql); this.#dirty(); return { ok: true }; }\n async run(sql, ...params) { this.db.run(sql, params); this.#dirty(); return { ok: true }; }\n async get(sql, ...params) {\n const stmt = this.db.prepare(sql);\n try { stmt.bind(params); return stmt.step() ? stmt.getAsObject() : null; }\n finally { stmt.free(); }\n }\n async all(sql, ...params) {\n const stmt = this.db.prepare(sql);\n const out = [];\n try { stmt.bind(params); while (stmt.step()) out.push(stmt.getAsObject()); }\n finally { stmt.free(); }\n return out;\n }\n async batch(statements = []) {\n this.db.run('BEGIN');\n try {\n for (const { sql, params = [] } of statements) this.db.run(sql, params);\n this.db.run('COMMIT');\n } catch (err) {\n try { this.db.run('ROLLBACK'); } catch { /* already rolled back */ }\n throw err;\n }\n this.#dirty();\n return { ok: true };\n }\n close() { clearTimeout(this._saveTimer); this.db.close(); }\n}\n\n// --- the pool ----------------------------------------------------------------\n\nexport class ClientSqlProvider {\n constructor({ onError } = {}) { this._pool = new Map(); this.onError = onError || null; }\n\n async obtain(key) {\n let db = this._pool.get(key);\n if (!db) {\n const SQL = await loadSql();\n const bytes = await loadBytes(key);\n db = new ClientDatabase(key, new SQL.Database(bytes || undefined));\n db.onError = this.onError;\n this._pool.set(key, db);\n }\n return db;\n }\n\n /** Drop a scope's database. Throws if the bytes can't be deleted — the caller\n * (uninstall) reports leftover on-device data rather than claiming a clean wipe. */\n async drop(key) {\n const db = this._pool.get(key);\n if (db) { db.close(); this._pool.delete(key); }\n await dropBytes(key);\n }\n}\n",
|
|
29
|
+
"// Plugin package signing + domain verification, on Web Crypto.\n//\n// A package is a zip of files including manifest.json. Signing binds two things:\n// 1. every non-manifest file, via `contentHash` (SHA-256 over the sorted file\n// set) recorded in the manifest — tamper any file and it breaks;\n// 2. the manifest itself, via an ECDSA-P256 `signature` over its canonical\n// JSON (with `signature` removed) — tamper the manifest and it breaks.\n// The signer's public key (SPKI) travels in the manifest; its fingerprint is\n// SHA-256 of that key. A plugin is \"domain verified\" when the manifest declares\n// a `domain` and that domain publishes the signer's fingerprint at\n// https://<domain>/.well-known/trove-assetlinks.json\n// (Digital-Asset-Links style), proving the domain owner vouches for the key.\n//\n// Verification is best-effort and layered: unsigned → \"unverified\"; signed but\n// the domain doesn't list the key → \"signed (self)\"; signed + listed → \"verified\n// · <domain>\". None of this sandboxes the plugin (the iframe does); it's a trust\n// signal for the human deciding whether to install.\n\nconst enc = new TextEncoder();\n\nfunction b64ToBytes(b64) {\n const bin = atob(b64.replace(/-/g, '+').replace(/_/g, '/'));\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\nfunction bytesToB64(bytes) {\n let s = '';\n const b = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n for (let i = 0; i < b.length; i++) s += String.fromCharCode(b[i]);\n return btoa(s);\n}\nfunction hex(buf) {\n const b = new Uint8Array(buf);\n let s = '';\n for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, '0');\n return s;\n}\nasync function sha256(bytes) {\n return new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n}\n\n/** Stable JSON: recursively sorted keys, `signature` stripped from the top. */\nexport function canonicalManifest(manifest) {\n const strip = { ...manifest };\n delete strip.signature;\n return JSON.stringify(sortKeys(strip));\n}\nfunction sortKeys(v) {\n if (Array.isArray(v)) return v.map(sortKeys);\n if (v && typeof v === 'object') {\n const out = {};\n for (const k of Object.keys(v).sort()) out[k] = sortKeys(v[k]);\n return out;\n }\n return v;\n}\n\n/** SHA-256 over every file except manifest.json, in sorted-path order. */\nexport async function contentHash(files) {\n const paths = [...files.keys()].filter((p) => p !== 'manifest.json').sort();\n const chunks = [];\n for (const p of paths) {\n const bytes = files.get(p);\n const pathBytes = enc.encode(p);\n const len = new Uint8Array(4);\n new DataView(len.buffer).setUint32(0, bytes.length);\n chunks.push(pathBytes, new Uint8Array([0]), len, bytes);\n }\n const total = chunks.reduce((n, c) => n + c.length, 0);\n const buf = new Uint8Array(total);\n let o = 0;\n for (const c of chunks) {\n buf.set(c, o);\n o += c.length;\n }\n return hex(await sha256(buf));\n}\n\n/** SHA-256 fingerprint of an SPKI public key (lowercase hex). */\nexport async function fingerprintOf(publicKeyB64) {\n return hex(await sha256(b64ToBytes(publicKeyB64)));\n}\nexport function displayFingerprint(hexStr) {\n return (hexStr.match(/.{2}/g) || []).join(':');\n}\n\n/**\n * Verify a package's integrity + signature (does NOT check the domain).\n * @returns {Promise<{signed:boolean, valid:boolean, fingerprint?:string, reason?:string}>}\n */\nexport async function verifyPackage({ manifest, files }) {\n if (!manifest.signature || !manifest.publicKey) return { signed: false, valid: false };\n // 1. content hash binds all other files.\n const expected = await contentHash(files);\n if (manifest.contentHash && manifest.contentHash !== expected) {\n return { signed: true, valid: false, reason: 'File contents do not match the manifest hash' };\n }\n // 2. signature binds the manifest.\n try {\n const key = await crypto.subtle.importKey('spki', b64ToBytes(manifest.publicKey), { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);\n const ok = await crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, key, b64ToBytes(manifest.signature), enc.encode(canonicalManifest(manifest)));\n if (!ok) return { signed: true, valid: false, reason: 'Signature does not verify' };\n return { signed: true, valid: true, fingerprint: await fingerprintOf(manifest.publicKey) };\n } catch (err) {\n return { signed: true, valid: false, reason: 'Bad key/signature: ' + err.message };\n }\n}\n\n/**\n * Given the domain's assetlinks doc, is this key fingerprint vouched for this plugin?\n * Format:\n * { \"version\": 1, \"keys\": [ { \"fingerprint\": \"<hex|colon-hex>\", \"plugins\": [\"docs\" | \"*\"] } ] }\n *\n * The doc is served BY the domain, so a plugin is named by its name within that domain\n * (\"docs\"); the fully-qualified \"acme.com/docs\" is accepted too, for docs that prefer\n * to be explicit.\n */\nexport function checkAssetlinks(assetlinks, fingerprint, manifest) {\n const norm = (f) => (f || '').toLowerCase().replace(/:/g, '');\n const want = norm(fingerprint);\n const names = [manifest.name, `${manifest.domain}/${manifest.name}`];\n for (const k of assetlinks?.keys || []) {\n if (norm(k.fingerprint) !== want) continue;\n if ((k.plugins || []).some((p) => p === '*' || names.includes(p))) return true;\n }\n return false;\n}\n\n/**\n * Full trust status for a package.\n * @param {(domain:string)=>Promise<object|null>} fetchAssetlinks resolves the domain's assetlinks doc (or null)\n * @returns {Promise<{status:'unverified'|'invalid'|'signed'|'verified', domain?:string, fingerprint?:string, reason?:string}>}\n */\nexport async function assessTrust({ manifest, files }, fetchAssetlinks) {\n const sig = await verifyPackage({ manifest, files });\n if (!sig.signed) return { status: 'unverified', reason: 'Package is not signed' };\n // A present-but-failed signature is evidence of tampering — a distinct, alarming\n // status, NOT the same grey \"unverified\" as an ordinary unsigned package.\n if (!sig.valid) return { status: 'invalid', reason: sig.reason || 'Invalid signature — the package may have been tampered with' };\n let doc = null;\n try {\n doc = await fetchAssetlinks(manifest.domain);\n } catch { /* unreachable domain */ }\n if (doc && checkAssetlinks(doc, sig.fingerprint, manifest)) {\n return { status: 'verified', domain: manifest.domain, fingerprint: sig.fingerprint };\n }\n return { status: 'signed', domain: manifest.domain, fingerprint: sig.fingerprint, reason: doc ? 'Domain does not list this key' : 'Could not reach the domain' };\n}\n\n// --- signing (tooling / tests) ---------------------------------------------\n\nexport async function generateSigningKey() {\n return crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']);\n}\n\n/** Produce a signed manifest for a package. Returns the manifest with\n * contentHash + publicKey + signature filled in. */\nexport async function signManifest(manifest, files, keyPair) {\n const withHash = { ...manifest, contentHash: await contentHash(files) };\n const spki = new Uint8Array(await crypto.subtle.exportKey('spki', keyPair.publicKey));\n withHash.publicKey = bytesToB64(spki);\n const sig = new Uint8Array(await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, keyPair.privateKey, enc.encode(canonicalManifest(withHash))));\n withHash.signature = bytesToB64(sig);\n return withHash;\n}\n\nexport { b64ToBytes, bytesToB64 };\n",
|
|
30
|
+
"// Declared network endpoints — the allowlist a plugin must publish before it can\n// talk to the web. The sandboxed iframe can't reach the network at all (strict\n// `connect-src 'none'` CSP + opaque origin); every request is brokered by the host\n// over the MessagePort, and the host only makes it if the URL matches one of these\n// declared endpoints. So the manifest's `network` array is the *complete* list of\n// hosts a plugin can ever reach, shown to the user at install time and enforced at\n// runtime.\n//\n// An endpoint is a URL prefix: scheme + host (optionally `*.`-wildcarded) + port +\n// path prefix. A request matches when its scheme, host, port, and path-prefix all\n// agree. Query strings and fragments are ignored in the pattern.\n\n/** Parse one endpoint pattern; throws on anything that isn't an http(s) URL. */\nexport function parseEndpoint(pattern) {\n // Strip a leading `*.` wildcard BEFORE parsing — `*` isn't a valid host code\n // point, and URL parsers disagree on how to mangle it (Chromium vs ada/Node),\n // so we never hand it to `new URL()`.\n const wild = /^([a-z][a-z0-9+.-]*:\\/\\/)\\*\\.(.+)$/i.exec(pattern);\n const wildcard = !!wild;\n let u;\n try {\n u = new URL(wildcard ? wild[1] + wild[2] : pattern);\n } catch {\n throw new Error(`Invalid network endpoint \"${pattern}\" (must be an absolute http(s) URL)`);\n }\n if (u.protocol !== 'http:' && u.protocol !== 'https:') {\n throw new Error(`Network endpoint \"${pattern}\" must use http or https`);\n }\n const host = u.hostname.toLowerCase();\n if (!host) throw new Error(`Network endpoint \"${pattern}\" has no host`);\n return { scheme: u.protocol, host, wildcard, port: u.port, pathPrefix: u.pathname || '/', raw: pattern };\n}\n\n/** Parse a manifest's `network` list, silently dropping malformed entries. */\nexport function normalizeEndpoints(list) {\n const out = [];\n for (const p of list || []) {\n try { out.push(parseEndpoint(p)); } catch { /* skip — validated separately at parse time */ }\n }\n return out;\n}\n\n/** Does a request URL fall under a single parsed endpoint? */\nexport function matchesEndpoint(ep, url) {\n let u;\n try { u = new URL(url); } catch { return false; }\n if (u.protocol !== ep.scheme) return false;\n const host = u.hostname.toLowerCase();\n if (ep.wildcard) {\n if (host !== ep.host && !host.endsWith('.' + ep.host)) return false;\n } else if (host !== ep.host) {\n return false;\n }\n if (u.port !== ep.port) return false; // '' === '' for default ports (browser normalizes)\n return u.pathname.startsWith(ep.pathPrefix);\n}\n\n/** Is `url` allowed by the plugin's declared endpoint list? */\nexport function isAllowedUrl(endpoints, url) {\n return normalizeEndpoints(endpoints).some((ep) => matchesEndpoint(ep, url));\n}\n\n/** A human-friendly summary of declared endpoints for the review UI. */\nexport function endpointSummary(endpoints) {\n return normalizeEndpoints(endpoints).map((e) => ({\n scheme: e.scheme.replace(':', ''),\n host: (e.wildcard ? '*.' : '') + e.host + (e.port ? ':' + e.port : ''),\n path: e.pathPrefix === '/' ? '' : e.pathPrefix,\n raw: e.raw,\n }));\n}\n",
|
|
31
|
+
"// Plugin packages — a plugin is a zip: manifest.json + an entry script + assets.\n// This parses/validates a package (from an uploaded file or a fetched URL) and\n// produces the summary shown in the pre-install review. Nothing here runs plugin\n// code — that only happens later, inside a sandboxed iframe.\n\nimport { unzipSync } from 'fflate';\nimport { parseEndpoint, endpointSummary } from './pluginNet.js';\nimport { assertIdentity, pluginId, ownsUri, parseContribUri } from '@3sln/trove/core/plugins/identity.js';\nimport { declaredContributions } from '@3sln/trove/core/plugins/contributions.js';\n\nconst CAP_DESCRIPTIONS = {\n files: 'Read your files, folders, and search index (via the host).',\n storage: 'Keep its own SQLite database(s) — on the server and/or on this device.',\n ui: 'Show a popup panel and toasts.',\n commands: 'Add commands to the palette, and run the specific host commands it lists.',\n opener: 'Preview/open file types.',\n // Distinct from a declared indexer (which the SERVER runs on every upload): this\n // capability lets the plugin itself push searchable content for a file it's looking at.\n indexer: 'Add searchable content to your files.',\n network: 'Connect to the internet — only the endpoints it declares (shown below).',\n media: 'Show playback controls on your lock screen and notifications while it plays media.',\n dock: 'Keep its viewer in a small floating window when you navigate away.',\n};\n\n// Capabilities that need an admin to grant (they touch shared data). None at the\n// moment — the `storage.domain` scope is instead gated on domain verification.\nexport const ADMIN_ONLY_CAPS = new Set();\n\n/**\n * The storage scopes a manifest declares: `{ plugin, domain }`. Each is a separate\n * lazily-created SQLite database (server + client). The `domain` scope — shared\n * across a vendor's plugins — is only usable by a domain-verified package.\n */\nexport function storageScopes(manifest) {\n const opt = capabilityOptions(manifest, 'storage');\n if (!opt) return { plugin: false, domain: false };\n // `storage: true` (or `{}`) → the private plugin scope, nothing shared.\n if (opt === true || (typeof opt === 'object' && !('plugin' in opt) && !('domain' in opt))) {\n return { plugin: true, domain: false };\n }\n return { plugin: !!opt.plugin, domain: !!opt.domain };\n}\n\nexport function describeCapability(cap) {\n return CAP_DESCRIPTIONS[cap] || cap;\n}\n\n// Capabilities are declared as an object: each key is a capability, each value is\n// that capability's options. A capability that takes no options uses `true` (or an\n// empty object). e.g. { ui: true, network: { endpoints: [\"https://api.example.com/\"] } }.\n// A plain array of ids is also accepted (each treated as options-less) so simple\n// or hand-written manifests stay easy.\nexport function capabilityEntries(manifest) {\n const caps = manifest && manifest.capabilities;\n const out = {};\n if (Array.isArray(caps)) {\n for (const id of caps) if (id) out[id] = {};\n } else if (caps && typeof caps === 'object') {\n for (const [id, val] of Object.entries(caps)) {\n if (val === false || val == null) continue; // explicitly not requested\n out[id] = val === true ? {} : val;\n }\n }\n return out;\n}\n\n/** The declared capability ids. */\nexport function capabilityList(manifest) {\n return Object.keys(capabilityEntries(manifest));\n}\n\n/** Options declared for one capability, or null if it isn't declared. */\nexport function capabilityOptions(manifest, id) {\n const e = capabilityEntries(manifest);\n return Object.prototype.hasOwnProperty.call(e, id) ? e[id] : null;\n}\n\n/** The network capability's declared endpoint prefixes (accepts a few shapes). */\nexport function networkEndpoints(manifest) {\n const opt = capabilityOptions(manifest, 'network');\n if (opt) {\n if (Array.isArray(opt)) return opt; // network: [\"https://…\"]\n if (Array.isArray(opt.endpoints)) return opt.endpoints;\n if (Array.isArray(opt.prefixes)) return opt.prefixes;\n }\n if (Array.isArray(manifest.network)) return manifest.network; // legacy top-level\n return [];\n}\n\n/** The human-facing name: `displayName` if given, else the package name. */\nexport function displayName(manifest) {\n // A STRING, always — and this is a security boundary, not tidiness.\n //\n // dodo decides \"is this argument a props map or a child?\" by `x.constructor === Object`,\n // and every prop on an HTML element is assigned as a DOM PROPERTY. `JSON.parse` gives\n // objects whose constructor is Object, so a manifest with\n // `\"displayName\": {\"innerHTML\": \"<img src=x onerror=…>\"}` — nothing validates it —\n // reaches `span(plugin.name)` in the plugin panel and `h3(g.category)` in Settings as\n // the FIRST argument, becomes the element's prop map, and executes in the host page.\n // That is a complete escape from the opaque-origin, connect-src 'none' iframe.\n const name = manifest?.displayName ?? manifest?.name;\n return typeof name === 'string' && name.trim() ? name : 'Plugin';\n}\n\n/**\n * The exact commands this plugin may ASK THE HOST TO RUN (ctx.commands.execute), each\n * named by the URI it's contributed at — a built-in like `explorer.download`, or\n * another plugin's `trove+contrib:acme.com/docs/export`. Like `network`, the\n * capability carries its allowlist rather than being a blanket grant: \"can run\n * commands\" is meaningless as a yes/no, because the interesting question is always\n * *which* ones (`explorer.delete` is not `workbench.view.home`).\n * Accepted shapes:\n * commands: true → contribute-only; executes nothing external\n * commands: [\"explorer.download\", …] → exactly these\n * commands: { execute: [\"…\"] } → exactly these\n * A plugin may always execute its own commands — that's just calling itself.\n */\nexport function executableCommands(manifest) {\n const opt = capabilityOptions(manifest, 'commands');\n if (!opt) return [];\n if (Array.isArray(opt)) return opt.filter(Boolean);\n if (Array.isArray(opt.execute)) return opt.execute.filter(Boolean);\n return [];\n}\n\n/**\n * Whether `commandUri` is executable by the plugin described by `manifest`. A plugin\n * always owns its own contributions (the URI is scoped under its domain and name), so\n * ownership is decided by the address itself rather than by a naming convention or a\n * registry lookup that could be raced.\n */\nexport function canExecuteCommand(manifest, commandUri, _ownerPluginId) {\n if (!commandUri) return false;\n if (parseContribUri(commandUri) && ownsUri(manifest, commandUri)) return true;\n return executableCommands(manifest).includes(commandUri);\n}\n\n/** Parse zip bytes into { manifest, files:Map<path,Uint8Array>, raw }. */\nexport function parsePackage(zipBytes) {\n let entries;\n try {\n entries = unzipSync(zipBytes instanceof Uint8Array ? zipBytes : new Uint8Array(zipBytes));\n } catch (err) {\n throw new Error('Not a valid zip archive: ' + err.message);\n }\n const files = new Map();\n for (const [path, bytes] of Object.entries(entries)) {\n if (!path.endsWith('/')) files.set(path.replace(/^\\.?\\//, ''), bytes);\n }\n const manifestBytes = files.get('manifest.json');\n if (!manifestBytes) throw new Error('Package is missing manifest.json');\n let manifest;\n try {\n manifest = JSON.parse(new TextDecoder().decode(manifestBytes));\n } catch (err) {\n throw new Error('manifest.json is not valid JSON: ' + err.message);\n }\n validateManifest(manifest, files);\n return { manifest, files, raw: zipBytes };\n}\n\nfunction validateManifest(m, files) {\n const need = (c, msg) => { if (!c) throw new Error(msg); };\n // Identity is mandatory: a package must say which DOMAIN it belongs to and its NAME\n // within that domain. There is no anonymous/self-identified install — every\n // contribution is addressed under `<domain>/<name>`, and the domain is what makes\n // that address verifiable rather than merely claimed.\n assertIdentity(m);\n need(m.entry, 'manifest.entry (path to the plugin script) is required');\n need(files.has(m.entry), `entry \"${m.entry}\" is not in the package`);\n if (m.capabilities != null) {\n need(Array.isArray(m.capabilities) || typeof m.capabilities === 'object',\n 'capabilities must be an object of { capability: options } (or an array of ids)');\n }\n if (m.icon) need(files.has(m.icon), `icon \"${m.icon}\" is not in the package`);\n // Throws on a bad type, a missing entry module, or a malformed option — the review\n // dialog must be able to show exactly what will be registered.\n for (const c of declaredContributions(m)) {\n if (c.entry) need(files.has(c.entry), `contribution \"${c.name}\" points at \"${c.entry}\", which is not in the package`);\n if (c.type === 'keymap') need(files.has(c.path), `keymap \"${c.name}\" points at \"${c.path}\", which is not in the package`);\n }\n for (const ep of networkEndpoints(m)) parseEndpoint(ep); // throws on anything but an http(s) URL\n}\n\n/** Fetch a package from a URL (must be a zip). */\nexport async function fetchPackage(url, fetchFn = globalThis.fetch.bind(globalThis)) {\n let res;\n try {\n res = await fetchFn(url);\n } catch (err) {\n throw new Error('Could not fetch the plugin: ' + err.message);\n }\n if (!res.ok) throw new Error(`Could not fetch the plugin (HTTP ${res.status})`);\n const bytes = new Uint8Array(await res.arrayBuffer());\n return parsePackage(bytes);\n}\n\n/** A flat, review-friendly summary of everything the package declares. */\nexport function reviewSummary(pkg, trust) {\n const m = pkg.manifest;\n const contributions = declaredContributions(m).map((c) => ({\n kind: c.type, name: c.name, uri: c.uri,\n title: c.title || c.name,\n detail: c.type === 'opener' || c.type === 'indexer' ? selectorText(c.match)\n // What it RUNS, not just where it sits. A status item can carry a command, and a\n // review that only said \"right of the status bar\" gave the user nothing to refuse.\n : c.type === 'statusItem' ? `${c.slot} of the status bar${c.command ? ` — runs ${c.command}` : ''}`\n : c.type === 'keymap' ? c.path : '',\n offline: !!c.offline,\n }));\n const verified = trust?.status === 'verified';\n const scopes = storageScopes(m);\n return {\n id: pluginId(m), name: displayName(m), version: m.version || '0.0.0', description: m.description || '',\n author: m.author || 'Unknown', domain: m.domain,\n capabilities: capabilityList(m).map((cap) => ({ id: cap, description: describeCapability(cap), adminOnly: ADMIN_ONLY_CAPS.has(cap) })),\n contributions,\n settings: (m.settings || []).map((s) => ({ key: s.key, title: s.title || s.key, type: s.type, secret: !!s.secret })),\n network: endpointSummary(networkEndpoints(m)),\n // Exactly which host commands it may run. Resolved to titles by the review UI,\n // which has the command registry; here we just surface the declared ids.\n commands: executableCommands(m),\n // Which SQLite stores it wants. `domain` (shared across a vendor's plugins) is\n // only grantable for a verified package; flag it so the review can say why.\n storage: (scopes.plugin || scopes.domain)\n ? { plugin: scopes.plugin, domain: scopes.domain, domainBlocked: scopes.domain && !verified }\n : null,\n fileCount: pkg.files.size,\n sizeBytes: [...pkg.files.values()].reduce((n, b) => n + b.length, 0),\n trust,\n };\n}\n\n/** The storage scopes actually granted at install: plugin if declared, domain if\n * declared AND the package is domain-verified. */\nexport function grantedStorageScopes(manifest, trust) {\n const s = storageScopes(manifest);\n return { plugin: s.plugin, domain: s.domain && trust?.status === 'verified' };\n}\n\nfunction selectorText(sel) {\n if (!sel) return '';\n const parts = [];\n if (sel.ext?.length) parts.push(sel.ext.join(', '));\n if (sel.mime?.length) parts.push(sel.mime.join(', '));\n return parts.join(' · ');\n}\n",
|
|
32
|
+
"// MediaController — bridges a plugin frame's playback to the OS via\n// navigator.mediaSession, so a sandboxed audio/video viewer surfaces real transport\n// controls (lock screen, media keys, notification shade).\n//\n// The host owns the single global mediaSession; the LAST frame to touch it becomes\n// the owner. OS actions fire back over that frame's own RPC channel, so a handler is\n// only ever wired to a live frame — and the handlers a frame registered are released\n// when it goes away (otherwise the OS would call into a dead channel).\n\nexport class MediaController {\n constructor() {\n this.owner = null; // the frame currently driving navigator.mediaSession\n }\n\n #session() {\n return typeof navigator !== 'undefined' ? navigator.mediaSession : null;\n }\n\n /** Apply one media operation for `frame`. kind: metadata|playbackState|position|action|clear */\n apply(frame, kind, params = {}) {\n const ms = this.#session();\n if (!ms) return { ok: false };\n if (frame && kind !== 'clear') { this.owner = frame; frame.mediaOwner = true; }\n try {\n if (kind === 'metadata') {\n ms.metadata = typeof MediaMetadata !== 'undefined'\n ? new MediaMetadata({ title: params.title || '', artist: params.artist || '', album: params.album || '', artwork: params.artwork || [] })\n : ms.metadata;\n } else if (kind === 'playbackState') {\n ms.playbackState = params.state || 'none';\n } else if (kind === 'position' && ms.setPositionState) {\n ms.setPositionState({ duration: params.duration || 0, position: params.position || 0, playbackRate: params.playbackRate || 1 });\n } else if (kind === 'action') {\n ms.setActionHandler?.(params.action, params.on ? () => frame?.channel?.emit('media:action', { action: params.action }) : null);\n // Remember which actions this frame registered, so we can release them when\n // the frame goes away (otherwise the OS handler points at a dead channel).\n if (frame) { frame.mediaActions ||= new Set(); params.on ? frame.mediaActions.add(params.action) : frame.mediaActions.delete(params.action); }\n } else if (kind === 'clear') {\n if (frame && this.owner && this.owner !== frame) return { ok: true }; // don't clear someone else's session\n this.releaseActions(frame);\n ms.metadata = null;\n ms.playbackState = 'none';\n this.owner = null;\n }\n } catch { /* unsupported action/state */ }\n return { ok: true };\n }\n\n /** Detach the OS media-transport handlers a frame registered (on teardown). */\n releaseActions(frame) {\n const ms = this.#session();\n if (!ms || !frame?.mediaActions) return;\n for (const action of frame.mediaActions) { try { ms.setActionHandler?.(action, null); } catch { /* ignore */ } }\n frame.mediaActions.clear();\n }\n\n /** Frame teardown: give up the session if this frame owned it, then drop its handlers. */\n releaseFrame(frame) {\n if (this.owner === frame) { this.owner = null; this.apply(frame, 'clear', {}); }\n this.releaseActions(frame); // drop handlers even if this frame wasn't the owner\n }\n}\n",
|
|
33
|
+
"// FrameDock — where a plugin frame's iframe is *shown*, and the floating dock (PiP).\n//\n// A frame's iframe is created once and stays a child of <body> for its life — we\n// NEVER re-parent it, because moving an <iframe> in the DOM reloads its document (per\n// the HTML spec) and would kill the running plugin + its MessagePort. To show a frame\n// \"inside\" a panel/viewer/dock we float it as a position:fixed overlay whose inset\n// tracks the target element's box (`place`), recomputed on intersection/resize/scroll\n// rather than every animation frame — the viewer area only moves on layout changes,\n// so this stays idle when nothing shifts.\n//\n// The dock is the single floating mini-player a viewer becomes when you navigate away\n// (video → PiP, audio → mini transport), if the viewer opted in via `dock.enable`.\n\nexport class FrameDock {\n /**\n * @param {object} deps\n * @param {(frame:object)=>void} deps.destroyFrame tear a frame down for good\n * @param {(node:object, openerId:string)=>void} deps.openFile reopen the docked file\n * @param {()=>void} [deps.onChange] notify listeners (the dock affects plugin state)\n */\n constructor({ destroyFrame, openFile, onChange } = {}) {\n this.destroyFrame = destroyFrame;\n this.openFile = openFile;\n this.onChange = onChange || (() => {});\n this.docked = null; // the frame currently in the dock\n this.el = null; // the dock's DOM host (created lazily)\n }\n\n // --- placement -------------------------------------------------------------\n\n /** Float `frame`'s iframe over `targetEl` and keep its inset aligned. */\n place(frame, targetEl, z, radius = '') {\n this.stopPlace(frame);\n const f = frame.iframe;\n f.style.cssText = `position:fixed;border:0;visibility:visible;display:block;background:transparent;z-index:${z};margin:0;padding:0;border-radius:${radius};`;\n const sync = () => {\n const r = targetEl.getBoundingClientRect();\n const shown = r.width > 0 && r.height > 0 && document.contains(targetEl);\n f.style.left = `${r.left}px`;\n f.style.top = `${r.top}px`;\n f.style.width = `${r.width}px`;\n f.style.height = `${r.height}px`;\n f.style.visibility = shown ? 'visible' : 'hidden';\n };\n sync();\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(sync) : null;\n ro?.observe(targetEl);\n if (document.body) ro?.observe(document.body);\n const io = typeof IntersectionObserver !== 'undefined' ? new IntersectionObserver(sync, { threshold: [0, 1] }) : null;\n io?.observe(targetEl);\n window.addEventListener('scroll', sync, true);\n window.addEventListener('resize', sync);\n frame.place = {\n target: targetEl,\n stop: () => {\n ro?.disconnect();\n io?.disconnect();\n window.removeEventListener('scroll', sync, true);\n window.removeEventListener('resize', sync);\n },\n };\n }\n\n stopPlace(frame) {\n if (frame.place) { frame.place.stop(); frame.place = null; }\n }\n\n /** Stop tracking and park the iframe offscreen (kept alive, just not shown). */\n hide(frame) {\n this.stopPlace(frame);\n frame.iframe.style.cssText = 'position:fixed;left:0;top:0;width:0;height:0;border:0;visibility:hidden;';\n }\n\n /** Frame teardown hook: stop placement and drop the dock if this frame was in it. */\n releaseFrame(frame) {\n this.stopPlace(frame);\n if (this.docked === frame) { this.docked = null; this.#hideEl(); }\n }\n\n // --- dock ------------------------------------------------------------------\n\n #hideEl() {\n if (this.el) this.el.style.display = 'none';\n }\n\n /** Float a viewer frame as the single floating dock, sized by its declared minimums. */\n dock(frame) {\n if (this.docked && this.docked !== frame) this.closeDock(this.docked);\n const min = frame.dock?.minSize || { width: 300, height: 90 };\n const el = this.#dockEl();\n el.style.width = `${clampDim(min.width, 200, 480)}px`;\n el.style.height = `${clampDim(min.height, 56, 360) + 26}px`; // + header\n el.querySelector('.vd-title').textContent = frame.node?.name || (frame.record.manifest.displayName || frame.record.manifest.name);\n el.style.display = 'flex';\n // Round the frame's bottom corners to match the dock's rounded body.\n this.place(frame, el.querySelector('.vd-body'), 61, '0 0 11px 11px');\n this.docked = frame;\n frame.channel?.emit('dock:state', { docked: true });\n this.onChange();\n }\n\n /** Un-dock without destroying — used when a docked frame is re-adopted into a viewer. */\n undock(frame) {\n this.#hideEl();\n if (this.docked === frame) this.docked = null;\n frame.channel?.emit('dock:state', { docked: false });\n }\n\n /** Dismiss the dock: the user closed it, or the viewer disabled docking. The frame\n * is done, so tear it down. */\n closeDock(frame) {\n this.#hideEl();\n if (this.docked === frame) this.docked = null;\n frame.channel?.emit('dock:state', { docked: false, closed: true });\n this.destroyFrame?.(frame);\n this.onChange();\n }\n\n #dockEl() {\n if (this.el) return this.el;\n const el = document.createElement('div');\n el.className = 'viewer-dock';\n el.innerHTML = '<div class=\"vd-bar\"><span class=\"vd-title\"></span><button class=\"vd-expand\" title=\"Reopen\">↗</button><button class=\"vd-close\" title=\"Close\">✕</button></div><div class=\"vd-body\"></div>';\n el.querySelector('.vd-expand').addEventListener('click', () => {\n const frame = this.docked;\n if (frame?.node) this.openFile?.(frame.node, frame.openerId);\n });\n el.querySelector('.vd-close').addEventListener('click', () => {\n if (this.docked) this.closeDock(this.docked);\n });\n document.body.appendChild(el);\n this.el = el;\n return el;\n }\n}\n\n// Clamp a viewer-declared dock dimension into a sane host range (defaulting to the\n// low bound when the viewer gives nothing).\nfunction clampDim(v, lo, hi) {\n return Math.max(lo, Math.min(hi, v || lo));\n}\n",
|
|
34
|
+
"// A minimal, symmetric JSON-RPC-ish channel over a MessagePort. Used on both\n// ends of the host↔plugin boundary: the host keeps one port, the sandboxed\n// iframe gets the other (transferred cross-origin at init), so neither side ever\n// touches the other's globals — everything is messages. Requests get a reply;\n// events are fire-and-forget. Handlers may be async and may throw; the error is\n// serialized (code/message) back to the caller as a rejected promise.\n\nexport class RpcChannel {\n /**\n * @param {MessagePort|Window} port a MessagePort (preferred) or window\n * @param {object} [opts]\n * @param {(method:string, params:any) => any} [opts.onCall] handler for incoming requests\n * @param {(method:string, params:any) => void} [opts.onEvent] handler for incoming events\n * @param {string} [opts.targetOrigin] required when port is a Window\n */\n constructor(port, opts = {}) {\n this.port = port;\n this.onCall = opts.onCall;\n this.onEvent = opts.onEvent;\n this.targetOrigin = opts.targetOrigin;\n this.seq = 0;\n this.pending = new Map();\n this._listener = (e) => this._receive(e.data, e);\n if (typeof port.addEventListener === 'function') port.addEventListener('message', this._listener);\n else port.onmessage = this._listener;\n port.start?.();\n }\n\n _post(msg, transfer) {\n if (this.targetOrigin) this.port.postMessage(msg, this.targetOrigin, transfer);\n else this.port.postMessage(msg, transfer);\n }\n\n /** Call a remote method, awaiting its result. */\n call(method, params, { timeout = 30000, transfer } = {}) {\n const id = ++this.seq;\n return new Promise((resolve, reject) => {\n const timer = timeout\n ? setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`RPC timeout: ${method}`));\n }, timeout)\n : null;\n this.pending.set(id, { resolve, reject, timer });\n this._post({ __trove: 'req', id, method, params }, transfer);\n });\n }\n\n /** Fire an event; no reply expected. */\n emit(method, params, transfer) {\n this._post({ __trove: 'event', method, params }, transfer);\n }\n\n async _receive(msg, event) {\n if (!msg || msg.__trove == null) return;\n if (msg.__trove === 'res') {\n const p = this.pending.get(msg.id);\n if (!p) return;\n this.pending.delete(msg.id);\n if (p.timer) clearTimeout(p.timer);\n if (msg.error) p.reject(Object.assign(new Error(msg.error.message), msg.error));\n else p.resolve(msg.result);\n return;\n }\n if (msg.__trove === 'event') {\n try {\n await this.onEvent?.(msg.method, msg.params, event);\n } catch (err) {\n console.error('rpc event handler error', err);\n }\n return;\n }\n if (msg.__trove === 'req') {\n let result, error;\n try {\n result = await this.onCall?.(msg.method, msg.params, event);\n } catch (err) {\n error = { message: err?.message || String(err), code: err?.code || 'error' };\n }\n this._post({ __trove: 'res', id: msg.id, result, error });\n }\n }\n\n dispose() {\n if (typeof this.port.removeEventListener === 'function') {\n this.port.removeEventListener('message', this._listener);\n }\n for (const p of this.pending.values()) {\n if (p.timer) clearTimeout(p.timer);\n p.reject(new Error('RPC channel disposed'));\n }\n this.pending.clear();\n this.port.close?.();\n }\n}\n",
|
|
35
|
+
"// The host ↔ plugin wire protocol, in one place.\n//\n// Envelope (over the transferred MessagePort):\n// { __trove: 'req', id, method, params } plugin → host, expects a 'res'\n// { __trove: 'res', id, result | error } host → plugin\n// { __trove: 'event', method, params } either direction, fire-and-forget\n// Bootstrap (over postMessage, before the port exists):\n// { __trove: 'ready', protocolVersion } plugin → host\n// { __trove: 'init', manifest, capabilities, storage, online, role, protocolVersion }\n// { __trove: 'boot-error', error } plugin → host (module load failed)\n//\n// NOTE: browser.js is injected into the sandboxed frame as a TEXT blob (it must be a\n// self-contained IIFE with no imports), so it cannot import this module. It declares\n// its own `SDK_PROTOCOL_VERSION` constant instead, and protocol.test.js asserts the\n// two stay equal — a drift guard in place of an import.\n\n/** Bumped MAJOR when a change breaks older plugins; MINOR for additive changes. */\nexport const PROTOCOL_VERSION = '1.0';\n\nexport function majorOf(version) {\n return String(version || '').split('.')[0] || '0';\n}\n\n/** Whether a plugin built against `version` can talk to this host. */\nexport function isCompatible(version) {\n // An SDK older than versioning itself reports nothing — accept it (it predates the\n // field and the protocol hasn't broken yet); a differing MAJOR is a hard mismatch.\n if (!version) return true;\n return majorOf(version) === majorOf(PROTOCOL_VERSION);\n}\n\n// There are no `contribute:*` methods: contributions are DECLARED IN THE MANIFEST and\n// registered by the host before the plugin boots. A plugin can only ever drive what it\n// declared (`ui:status`, `context:setRegister`), never add to it.\n\n/** Canonical host methods a plugin may call. Grouped by namespace. */\nexport const METHODS = {\n activated: 'activated',\n // Note: 'command:execute' travels BOTH ways on the same channel — the host calls it\n // to run a plugin-contributed command, and a plugin calls it (needs that command in\n // its `commands` allowlist) to run someone else's. The direction disambiguates.\n command: { execute: 'command:execute' },\n resources: { list: 'resources:list', read: 'resources:read' },\n files: { read: 'files:read', list: 'files:list', stat: 'files:stat', downloadUrl: 'files:downloadUrl', index: 'files:index' },\n net: { fetch: 'net:fetch' },\n storage: { sql: 'storage:sql' },\n settings: { get: 'settings:get', set: 'settings:set', getSecret: 'settings:getSecret' },\n ui: { showPanel: 'ui:showPanel', status: 'ui:status' },\n context: { setRegister: 'context:setRegister' },\n media: { metadata: 'media:metadata', playbackState: 'media:playbackState', position: 'media:position', action: 'media:action', clear: 'media:clear' },\n dock: { enable: 'dock:enable', disable: 'dock:disable', close: 'dock:close' },\n};\n\n/** Events a plugin may emit to the host (fire-and-forget, no reply). */\nexport const EVENTS = {\n manifest: 'manifest',\n uiToast: 'ui:toast',\n uiBadge: 'ui:badge',\n};\n",
|
|
36
|
+
"/* es-module-lexer 1.7.0 */\nexport var ImportType;!function(A){A[A.Static=1]=\"Static\",A[A.Dynamic=2]=\"Dynamic\",A[A.ImportMeta=3]=\"ImportMeta\",A[A.StaticSourcePhase=4]=\"StaticSourcePhase\",A[A.DynamicSourcePhase=5]=\"DynamicSourcePhase\",A[A.StaticDeferPhase=6]=\"StaticDeferPhase\",A[A.DynamicDeferPhase=7]=\"DynamicDeferPhase\"}(ImportType||(ImportType={}));const A=1===new Uint8Array(new Uint16Array([1]).buffer)[0];export function parse(E,g=\"@\"){if(!C)return init.then((()=>parse(E)));const I=E.length+1,w=(C.__heap_base.value||C.__heap_base)+4*I-C.memory.buffer.byteLength;w>0&&C.memory.grow(Math.ceil(w/65536));const K=C.sa(I-1);if((A?B:Q)(E,new Uint16Array(C.memory.buffer,K,I)),!C.parse())throw Object.assign(new Error(`Parse error ${g}:${E.slice(0,C.e()).split(\"\\n\").length}:${C.e()-E.lastIndexOf(\"\\n\",C.e()-1)}`),{idx:C.e()});const o=[],D=[];for(;C.ri();){const A=C.is(),Q=C.ie(),B=C.it(),g=C.ai(),I=C.id(),w=C.ss(),K=C.se();let D;C.ip()&&(D=k(E.slice(-1===I?A-1:A,-1===I?Q+1:Q))),o.push({n:D,t:B,s:A,e:Q,ss:w,se:K,d:I,a:g})}for(;C.re();){const A=C.es(),Q=C.ee(),B=C.els(),g=C.ele(),I=E.slice(A,Q),w=I[0],K=B<0?void 0:E.slice(B,g),o=K?K[0]:\"\";D.push({s:A,e:Q,ls:B,le:g,n:'\"'===w||\"'\"===w?k(I):I,ln:'\"'===o||\"'\"===o?k(K):K})}function k(A){try{return(0,eval)(A)}catch(A){}}return[o,D,!!C.f(),!!C.ms()]}function Q(A,Q){const B=A.length;let C=0;for(;C<B;){const B=A.charCodeAt(C);Q[C++]=(255&B)<<8|B>>>8}}function B(A,Q){const B=A.length;let C=0;for(;C<B;)Q[C]=A.charCodeAt(C++)}let C;const E=()=>{return A=\"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GzYCHAtBAEEAKAKwCkECajYCsAoCQAJAAkACQEEAECkiAUHhAEYNACABQfcARg0BQQAoArAKIQEMAgtBACgCsAoiAUECakHACEEKEC8NAUEGIQIMAgtBACgCsAoiAS8BAkHpAEcNACABLwEEQfQARw0AQQQhAiABLwEGQegARg0BC0EAIAFBfmo2ArAKDwtBACABIAJBAXRqNgKwCgJAQQEQKUH7AEYNAEEAIAE2ArAKDwtBACgCsAoiACECA0BBACACQQJqNgKwCgJAAkACQEEBECkiAkEiRg0AIAJBJ0cNAUEnEBpBAEEAKAKwCkECajYCsApBARApIQIMAgtBIhAaQQBBACgCsApBAmo2ArAKQQEQKSECDAELIAIQLCECCwJAIAJBOkYNAEEAIAE2ArAKDwtBAEEAKAKwCkECajYCsAoCQEEBECkiAkEiRg0AIAJBJ0YNAEEAIAE2ArAKDwsgAhAaQQBBACgCsApBAmo2ArAKAkACQEEBECkiAkEsRg0AIAJB/QBGDQFBACABNgKwCg8LQQBBACgCsApBAmo2ArAKQQEQKUH9AEYNAEEAKAKwCiECDAELC0EAKALwCSIBIAA2AhAgAUEAKAKwCkECajYCDAttAQJ/AkACQANAAkAgAEH//wNxIgFBd2oiAkEXSw0AQQEgAnRBn4CABHENAgsgAUGgAUYNASAAIQIgARAoDQJBACECQQBBACgCsAoiAEECajYCsAogAC8BAiIADQAMAgsLIAAhAgsgAkH//wNxC6sBAQR/AkACQEEAKAKwCiICLwEAIgNB4QBGDQAgASEEIAAhBQwBC0EAIAJBBGo2ArAKQQEQKSECQQAoArAKIQUCQAJAIAJBIkYNACACQSdGDQAgAhAsGkEAKAKwCiEEDAELIAIQGkEAQQAoArAKQQJqIgQ2ArAKC0EBECkhA0EAKAKwCiECCwJAIAIgBUYNACAFIARBACAAIAAgAUYiAhtBACABIAIbEAILIAMLcgEEf0EAKAKwCiEAQQAoArQKIQECQAJAA0AgAEECaiECIAAgAU8NAQJAAkAgAi8BACIDQaR/ag4CAQQACyACIQAgA0F2ag4EAgEBAgELIABBBGohAAwACwtBACACNgKwChAlQQAPC0EAIAI2ArAKQd0AC0kBA39BACEDAkAgAkUNAAJAA0AgAC0AACIEIAEtAAAiBUcNASABQQFqIQEgAEEBaiEAIAJBf2oiAg0ADAILCyAEIAVrIQMLIAMLC+wBAgBBgAgLzgEAAHgAcABvAHIAdABtAHAAbwByAHQAZgBvAHIAZQB0AGEAbwB1AHIAYwBlAHIAbwBtAHUAbgBjAHQAaQBvAG4AcwBzAGUAcgB0AHYAbwB5AGkAZQBkAGUAbABlAGMAbwBuAHQAaQBuAGkAbgBzAHQAYQBuAHQAeQBiAHIAZQBhAHIAZQB0AHUAcgBkAGUAYgB1AGcAZwBlAGEAdwBhAGkAdABoAHIAdwBoAGkAbABlAGkAZgBjAGEAdABjAGYAaQBuAGEAbABsAGUAbABzAABB0AkLEAEAAAACAAAAAAQAAEA5AAA=\",\"undefined\"!=typeof Buffer?Buffer.from(A,\"base64\"):Uint8Array.from(atob(A),(A=>A.charCodeAt(0)));var A};export const init=WebAssembly.compile(E()).then(WebAssembly.instantiate).then((({exports:A})=>{C=A}));export const initSync=()=>{if(C)return;const A=new WebAssembly.Module(E());C=new WebAssembly.Instance(A).exports};",
|
|
37
|
+
"// Multi-file plugins — native ESM without a bundler. A package can put its code\n// under `src/` and use ordinary relative imports (`import './util.js'`); assets\n// live anywhere else and are reached as opaque resources. We can't just let the\n// sandboxed frame import these: it's on an opaque origin and can't fetch its own\n// files, and a blob: module's base URL is meaningless so `./util.js` wouldn't\n// resolve. So the host loads every `src/*.js` module as a blob: URL inside the\n// frame and wires them together with an import map — and, because an import map is\n// keyed by name (not text order), circular imports and any load order just work.\n//\n// The one preprocessing step: relative specifiers are rewritten to a canonical\n// `trove:/<path>` key (resolved against the importing module) so they match the\n// import map instead of resolving against the opaque blob base. We use\n// es-module-lexer to find the exact specifier spans, so strings/comments/regex are\n// never touched. The SDK is exposed as the bare specifier `trove`, so plugins can\n// write either `import { activate } from 'trove'` or use the `globalThis.trove`\n// the injected SDK also sets.\n\nimport { init, parse } from 'es-module-lexer';\n\nconst JS_RE = /\\.m?js$/i;\n\n/** A file that participates in the module graph (code under `src/`). */\nexport function isSourceModule(path) {\n return /^src\\//i.test(path) && JS_RE.test(path);\n}\n\n/** Whether a package should run in ESM module mode (its entry is a src module). */\nexport function isModuleEntry(manifest) {\n return isSourceModule(manifest.entry || '');\n}\n\nfunction dirParts(p) {\n const i = p.lastIndexOf('/');\n return (i < 0 ? '' : p.slice(0, i)).split('/').filter(Boolean);\n}\n\n/** Resolve a relative specifier against the importer's package path. */\nfunction resolvePath(importer, spec) {\n const stack = dirParts(importer);\n for (const part of spec.split('/')) {\n if (part === '' || part === '.') continue;\n else if (part === '..') stack.pop();\n else stack.push(part);\n }\n return stack.join('/');\n}\n\n/**\n * Build the rewritten module set + entry for a package. Returns\n * { modules: { path: code }, entry } where each module's relative imports point at\n * canonical `trove:/<path>` keys. Only `src/*.js` files are included.\n */\nexport async function buildModuleGraph(pkg) {\n await init;\n const modules = {};\n for (const [path, bytes] of pkg.files) {\n if (isSourceModule(path)) modules[path] = new TextDecoder().decode(bytes);\n }\n const has = (p) => Object.prototype.hasOwnProperty.call(modules, p);\n const resolveModule = (importer, spec) => {\n const r = resolvePath(importer, spec);\n if (has(r)) return r;\n if (has(r + '.js')) return r + '.js';\n if (has(r + '.mjs')) return r + '.mjs';\n if (has(r + '/index.js')) return r + '/index.js';\n return r; // unresolved → import fails with a clear \"no such module\" error\n };\n const rewritten = {};\n for (const [path, code] of Object.entries(modules)) {\n rewritten[path] = rewriteSpecifiers(code, path, resolveModule);\n }\n return { modules: rewritten, entry: pkg.manifest.entry };\n}\n\n/** Rewrite relative import specifiers in one module to canonical map keys. */\nexport function rewriteSpecifiers(code, path, resolveModule) {\n const [imports] = parse(code);\n let out = '';\n let last = 0;\n for (const imp of imports) {\n if (imp.n == null) continue; // dynamic import with a non-literal argument\n if (imp.n[0] !== '.') continue; // only relative specifiers are remapped\n const key = 'trove:/' + resolveModule(path, imp.n);\n // Static spans exclude the quotes; dynamic import() spans include them.\n const replacement = imp.d === -1 ? key : JSON.stringify(key);\n out += code.slice(last, imp.s) + replacement;\n last = imp.e;\n }\n return out + code.slice(last);\n}\n",
|
|
38
|
+
"// FrameManager — the sandbox seam: everything about creating, handshaking with, and\n// destroying a plugin's iframe.\n//\n// A plugin's code runs inside a sandboxed iframe on an OPAQUE origin (sandbox\n// allow-scripts, no allow-same-origin): it can't touch the host DOM, can't read\n// cookies/storage, and can't even fetch its own package files. The host injects our\n// browser SDK + the plugin's entry into the frame's srcdoc; everything else flows over\n// a single transferred MessagePort, capability-gated by what the user granted.\n//\n// Every frame — the plugin's primary (background) instance and each viewer/panel —\n// comes from `spawn()`, so they all share the same wiring and capabilities.\n\nimport { RpcChannel } from '@3sln/trove/plugin-sdk/rpc.js';\nimport SDK_SOURCE from '@3sln/trove/plugin-sdk/browser.js' with { type: 'text' };\nimport { PROTOCOL_VERSION, isCompatible } from '@3sln/trove/plugin-sdk/protocol.js';\nimport { buildModuleGraph, isModuleEntry } from './pluginModules.js';\n\nconst HANDSHAKE_TIMEOUT_MS = 15000;\n\nexport class FrameManager {\n /**\n * @param {object} deps\n * @param {import('./pluginMedia.js').MediaController} deps.media\n * @param {import('./pluginDock.js').FrameDock} deps.dock\n */\n constructor({ media, dock } = {}) {\n this.media = media;\n this.dock = dock;\n }\n\n /**\n * Create ONE sandboxed iframe wired with `record`'s granted capabilities, run the\n * handshake, and resolve to a live frame `{ role, iframe, channel, record }`.\n * @param {object} record the plugin runtime record\n * @param {'primary'|'viewer'} role\n * @param {{onCall:Function, onEvent:Function, online:boolean}} wiring\n */\n async spawn(record, role, wiring) {\n const iframe = document.createElement('iframe');\n iframe.style.cssText = HIDDEN_STYLE;\n // Opaque-origin sandbox: scripts only, no same-origin, no top navigation.\n //\n // Deliberately NO `allow-popups`. The CSP below blocks fetch/XHR/WebSocket/beacon\n // so that all network access is brokered by the host against the plugin's declared\n // endpoints — but no CSP directive covers `window.open`, so a popup is a hole\n // straight through that broker: one real click and a plugin granted `files` but not\n // `network` can navigate a new context to `https://attacker/?d=<file bytes>`.\n // A plugin that genuinely needs to send the user somewhere asks the host to do it.\n iframe.setAttribute('sandbox', 'allow-scripts allow-forms');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n // A frame can boot at a different ENTRY MODULE of the plugin's one module tree —\n // that's all an opener is. Same package, same shared code, different entry.\n const entry = wiring?.entry;\n if (entry) {\n record._entrySrcdoc ||= {};\n record._entrySrcdoc[entry] ||= await buildSrcdoc({ ...record.manifest, entry }, record.files);\n iframe.srcdoc = record._entrySrcdoc[entry];\n } else {\n record._srcdoc ||= await buildSrcdoc(record.manifest, record.files);\n iframe.srcdoc = record._srcdoc;\n }\n const frame = { role, iframe, channel: null, record, place: null, dock: null, mediaActions: null };\n document.body.appendChild(iframe);\n try {\n await this.#handshake(record, frame, wiring);\n } catch (err) {\n // Never leave a hung/failed frame's iframe (or its channel) behind.\n try { frame.channel?.dispose(); } catch { /* ignore */ }\n iframe.remove();\n throw err;\n }\n return frame;\n }\n\n // Resolves once the frame's plugin calls activate(); rejects (and fully cleans up\n // its global message listener + timer) on boot error, iframe error, or timeout.\n #handshake(record, frame, { onCall, onEvent, online }) {\n return new Promise((resolve, reject) => {\n const { iframe } = frame;\n const manifest = record.manifest;\n let settled = false;\n let timer = 0;\n const cleanup = () => { window.removeEventListener('message', onReady); if (timer) clearTimeout(timer); };\n const fail = (msg) => { if (settled) return; settled = true; cleanup(); reject(new Error(msg)); };\n const onReady = (e) => {\n if (e.source !== iframe.contentWindow) return;\n if (e.data?.__trove === 'boot-error') return fail(e.data.error || 'Plugin failed to load its modules');\n if (e.data?.__trove !== 'ready') return;\n // A plugin built against an incompatible protocol MAJOR can't be talked to —\n // fail loudly here rather than letting calls misbehave in obscure ways.\n if (!isCompatible(e.data.protocolVersion)) {\n return fail(`Plugin speaks protocol ${e.data.protocolVersion}, this host speaks ${PROTOCOL_VERSION}`);\n }\n // Ready received — the transferred port takes over; stop listening globally.\n window.removeEventListener('message', onReady);\n const channel = new MessageChannel();\n frame.channel = new RpcChannel(channel.port1, {\n onCall: (m, p) => onCall(record, m, p, frame),\n onEvent: (m, p) => onEvent(record, m, p, frame),\n });\n // activate() success/failure settles this promise (and clears the timer).\n frame.resolveActivated = (f) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); resolve(f); };\n frame.rejectActivated = (err) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); reject(err); };\n // Opaque origin → target '*'; the transferred port is the real capability.\n iframe.contentWindow.postMessage(\n { __trove: 'init', manifest, capabilities: record.grants, storage: record.storage, online, role: frame.role, protocolVersion: PROTOCOL_VERSION },\n '*',\n [channel.port2],\n );\n };\n timer = setTimeout(() => fail('Plugin handshake timed out'), HANDSHAKE_TIMEOUT_MS);\n window.addEventListener('message', onReady);\n iframe.addEventListener('error', () => fail('Failed to load plugin iframe'));\n });\n }\n\n /**\n * Tear a frame down for good: stop tracking it, drop the dock/media session if it\n * held them, close its RPC channel, and remove the iframe from the DOM.\n */\n destroy(frame) {\n if (!frame) return;\n this.dock?.releaseFrame(frame);\n this.media?.releaseFrame(frame);\n try { frame.channel?.emit('deactivate'); } catch { /* ignore */ }\n try { frame.channel?.dispose(); } catch { /* ignore */ }\n frame.iframe.remove();\n frame.record?.frames?.delete(frame);\n }\n}\n\nconst HIDDEN_STYLE = 'position:fixed;left:0;top:0;width:0;height:0;border:0;visibility:hidden;';\n\n// The frame's CSP. `connect-src 'none'` means it cannot make ANY direct network\n// request (fetch/XHR/WebSocket/beacon) — all web access is brokered by the host and\n// confined to the plugin's declared endpoints. img/media/font are limited to\n// in-frame blob:/data: so remote loads can't be an exfiltration side-channel.\n// `blob:` in script-src is only for the plugin's own package modules (below), which\n// are same-origin blobs — no external code can load.\nconst CSP = [\n \"default-src 'none'\",\n \"script-src 'unsafe-inline' 'unsafe-eval' blob:\",\n \"style-src 'unsafe-inline'\",\n 'img-src blob: data:',\n 'media-src blob: data:',\n \"font-src 'none'\",\n \"connect-src 'none'\",\n \"base-uri 'none'\",\n \"form-action 'none'\",\n].join('; ');\n\nconst HEAD = `<!doctype html><html><head><meta charset=\"utf-8\"><meta http-equiv=\"Content-Security-Policy\" content=\"${CSP}\"></head><body>`;\n\n/** Build the iframe document — classic single-file, or ESM module mode (src/). */\nexport async function buildSrcdoc(manifest, files) {\n if (isModuleEntry(manifest)) return moduleBootstrapHtml(await buildModuleGraph({ manifest, files }));\n const entryJs = new TextDecoder().decode(files.get(manifest.entry) ?? new Uint8Array());\n return `${HEAD}\n<script>${SDK_SOURCE}<\\/script>\n<script>${entryJs}<\\/script>\n</body></html>`;\n}\n\n// Escape a value for safe inlining inside <script> (so `</script>` and JS line\n// separators in package code can't break out of the tag).\nfunction safeJson(value) {\n return JSON.stringify(value).replace(/</g, '\\\\u003c').replace(/\\u2028/g, '\\\\u2028').replace(/\\u2029/g, '\\\\u2029');\n}\n\n// ESM module mode: ship every src/ module as a blob: URL and wire them with an\n// import map keyed by canonical `trove:/<path>` specifiers (the specifiers in the\n// code were rewritten to match). `trove` resolves to a shim over the injected SDK.\nfunction moduleBootstrapHtml(graph) {\n const entryKey = safeJson('trove:/' + graph.entry);\n return `${HEAD}\n<script>${SDK_SOURCE}<\\/script>\n<script id=\"__trove_src\" type=\"application/json\">${safeJson(graph.modules)}<\\/script>\n<script>\n(function () {\n var src = JSON.parse(document.getElementById('__trove_src').textContent);\n var imports = {};\n for (var p in src) imports['trove:/' + p] = URL.createObjectURL(new Blob([src[p] + '\\\\n//# sourceURL=trove:/' + p], { type: 'text/javascript' }));\n imports['trove'] = URL.createObjectURL(new Blob(['export const activate = globalThis.trove.activate;\\\\nexport default globalThis.trove;'], { type: 'text/javascript' }));\n var im = document.createElement('script');\n im.type = 'importmap';\n im.textContent = JSON.stringify({ imports: imports });\n document.head.appendChild(im);\n import(${entryKey}).catch(function (e) {\n try { parent.postMessage({ __trove: 'boot-error', error: String((e && e.message) || e) }, '*'); } catch (_) {}\n });\n})();\n<\\/script>\n</body></html>`;\n}\n",
|
|
39
|
+
"// Which SQL a plugin may run against its own database.\n//\n// Its own module rather than part of sqlite.js because BOTH sides of the mirror need it\n// and only one of them can load a driver: the server checks before touching the on-disk\n// store, and the browser checks before touching the wasm one. Importing sqlite.js from\n// the web bundle would drag `bun:sqlite` in behind it.\n\nimport { TroveError } from '../errors.js';\n\n// Plugins run SQL against their OWN isolated database, but on a shared-filesystem\n// provider the sibling scope files are guessable, so `ATTACH DATABASE` would be an\n// isolation escape (and `DETACH` its pair). Strip comments + string/identifier\n// literals first so the keyword can't hide inside a value, then reject.\n\n// ATTACH/DETACH would reach a sibling scope's file. VACUUM INTO is worse and less\n// obvious: it writes a complete SQLite database to any path the server process can\n// create, whose pages contain rows the caller chose — attacker-chosen bytes at an\n// attacker-chosen path (a cron file, a webroot, authorized_keys). PRAGMA is refused\n// because several of them (database_list, temp_store_directory) either disclose host\n// paths or move where files land.\nconst DANGEROUS_SQL = /\\b(ATTACH|DETACH|VACUUM|PRAGMA)\\b/i;\n\n/** Blank out --/**-comments and '..'/\"..\"/`..`/[..] literals, preserving length-ish. */\nexport function stripSqlLiterals(sql) {\n let out = '';\n let i = 0;\n while (i < sql.length) {\n const c = sql[i];\n if (c === '-' && sql[i + 1] === '-') { const nl = sql.indexOf('\\n', i); i = nl < 0 ? sql.length : nl; continue; }\n if (c === '/' && sql[i + 1] === '*') { const e = sql.indexOf('*/', i + 2); i = e < 0 ? sql.length : e + 2; out += ' '; continue; }\n if (c === \"'\" || c === '\"' || c === '`') {\n const q = c; i++;\n while (i < sql.length) {\n if (sql[i] === q) { if (sql[i + 1] === q) { i += 2; continue; } i++; break; }\n i++;\n }\n out += ' '; continue;\n }\n if (c === '[') { const e = sql.indexOf(']', i); i = e < 0 ? sql.length : e + 1; out += ' '; continue; }\n out += c; i++;\n }\n return out;\n}\n\n/** Throw if plugin-supplied SQL tries to escape its isolated database. */\nexport function assertSafePluginSql(sql) {\n if (typeof sql !== 'string' || !sql) throw TroveError.invalid('SQL statement is required');\n if (DANGEROUS_SQL.test(stripSqlLiterals(sql))) {\n throw TroveError.invalid('ATTACH, DETACH, VACUUM and PRAGMA are not permitted in plugin storage');\n }\n}\n\n",
|
|
40
|
+
"// PluginRpcRouter — the trusted boundary: every call a sandboxed plugin makes into\n// the host lands here, is capability-gated against what the user granted at install\n// time, and is then serviced against host APIs the plugin can never reach directly.\n//\n// The frame has NO ambient authority: no host DOM, no cookies, no network\n// (connect-src 'none'), no access to its own package bytes. Everything it can do is\n// one of the methods below, and each one that touches something sensitive calls\n// `cap(...)` first. Extracted from PluginHost so the allow/deny logic is one focused\n// unit rather than sharing a class with iframe lifecycle and DOM placement.\n\nimport { networkEndpoints, canExecuteCommand, displayName } from './pluginPackage.js';\nimport { isAllowedUrl } from './pluginNet.js';\nimport { isSourceModule } from './pluginModules.js';\nimport { contribUri, parseContribUri } from '@3sln/trove/core/plugins/identity.js';\nimport { assertSafePluginSql } from '@3sln/trove/core/plugins/sql.js';\n\n// Response bodies larger than this are refused, so a plugin can't exhaust host\n// memory through the brokered fetch.\nconst MAX_FETCH_BYTES = 25 * 1024 * 1024;\n\n/**\n * Read a response body, aborting the moment it exceeds `max`.\n *\n * `await res.arrayBuffer()` then checking `.byteLength` does not achieve the thing the\n * cap exists for: by the time the check runs the whole body is already resident, so a\n * declared endpoint returning a multi-GB stream OOMs the tab before we ever refuse it.\n * Check the declared length first, then enforce while streaming in case it lied.\n */\nasync function readCappedBody(res, max) {\n const declared = Number(res.headers.get('content-length') || 0);\n if (declared && declared > max) throw new Error('Response too large');\n const reader = res.body?.getReader?.();\n if (!reader) {\n const buf = await res.arrayBuffer();\n if (buf.byteLength > max) throw new Error('Response too large');\n return buf;\n }\n const chunks = [];\n let total = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > max) { await reader.cancel().catch(() => {}); throw new Error('Response too large'); }\n chunks.push(value);\n }\n const out = new Uint8Array(total);\n let at = 0;\n for (const c of chunks) { out.set(c, at); at += c.byteLength; }\n return out.buffer;\n}\n\nexport class PluginRpcRouter {\n /**\n * @param {object} deps\n * @param {object} deps.platform host services (api, commands, contributions, settings, …)\n * @param {object} deps.clientDb on-device scoped SQLite provider\n * @param {import('./pluginMedia.js').MediaController} deps.media\n * @param {import('./pluginDock.js').FrameDock} deps.dock\n * @param {()=>void} deps.onChange notify listeners that plugin state changed\n */\n constructor({ platform, clientDb, media, dock, onChange } = {}) {\n this.platform = platform;\n this.clientDb = clientDb;\n this.media = media;\n this.dock = dock;\n this.onChange = onChange || (() => {});\n }\n\n // --- plugin → host RPC (calls: awaited, may return a value) -----------------\n\n async hostCall(record, method, params, frame) {\n const cap = (c) => { if (!record.grants.includes(c)) throw new Error(`Capability \"${c}\" not granted`); };\n const pid = record.id;\n switch (method) {\n case 'activated': {\n const f = frame || record.frame;\n clearTimeout(f?._timer);\n if (params.ok) f?.resolveActivated?.(f);\n else f?.rejectActivated?.(new Error(params.error || 'activate() failed'));\n return { ok: true };\n }\n\n // Run a command (the SDK's ctx.commands.execute). Gated per COMMAND, not per\n // capability: the plugin's manifest lists exactly which commands it may run\n // (plus its own, implicitly). A blanket \"commands\" grant would let a plugin that\n // wanted `workbench.view.home` also call `explorer.delete`.\n case 'command:execute': {\n const target = this.#resolveCommand(record, params.id);\n if (!canExecuteCommand(record.manifest, target)) {\n throw new Error(`Command \"${params.id}\" is not in this plugin's declared commands`);\n }\n const result = await this.platform.commands.execute(target, ...(params.args || []));\n return { ok: true, result: result ?? null };\n }\n\n // Drive a DECLARED status slot: push sanitized HTML into it, or show/hide it.\n // The slot itself is a manifest contribution — this only fills one in, so a\n // plugin can never grow its footprint in the shell past what was approved.\n case 'ui:status': {\n cap('ui');\n const slot = this.#ownContribution(record, params.name, 'statusItem');\n this.platform.contributions.update(slot.uri, {\n ...(params.html !== undefined ? { html: String(params.html ?? '') } : {}),\n ...(params.tooltip !== undefined ? { tooltip: String(params.tooltip ?? '') } : {}),\n ...(params.visible !== undefined ? { visible: !!params.visible } : { visible: true }),\n });\n return { ok: true };\n }\n\n // Set a DECLARED register — a context value slot other contributions' when-clauses\n // can read, addressed by its contribution URI.\n case 'context:setRegister': {\n const slot = this.#ownContribution(record, params.name, 'register');\n this.platform.context.set(slot.uri, params.value);\n return { ok: true };\n }\n // Package resources — opaque byte handles (transferred, no host URLs). Code\n // under src/ and the manifest are not resources (src/ is loaded as modules).\n case 'resources:list':\n return [...record.files.keys()].filter(isResourcePath);\n case 'resources:read': {\n if (!isResourcePath(params.path)) throw new Error(`No such resource ${params.path}`);\n const bytes = record.files.get(params.path);\n if (!bytes) throw new Error(`No such resource ${params.path}`);\n return { bytes: bytes.slice().buffer }; // copied into the iframe by structured clone\n }\n\n // Files — inherit the host's authenticated API client.\n case 'files:read': return cap('files'), { text: await this.platform.api.readText(params.id) };\n // ONE options object — `api.list(opts)`. Called as `list(pathOrId, params)` the\n // collection became the query string's key and everything else was dropped, so a\n // plugin asking for `photos` silently got the default collection unsorted.\n case 'files:list': return cap('files'), this.platform.api.list({\n collection: params.collection || params.pathOrId || undefined,\n sort: params.sort, order: params.order, limit: params.limit, cursor: params.cursor,\n });\n case 'files:stat': return cap('files'), this.platform.api.stat(params.id);\n case 'files:downloadUrl': return cap('files'), { url: this.platform.api.downloadUrl(params.id) };\n case 'files:index': {\n cap('indexer');\n const ns = parseContribUri(params.indexerId) ? params.indexerId : contribUri(record.manifest, params.indexerId || 'default');\n return this.platform.api.pushIndex(ns, params.nodeId, {\n semanticTexts: params.semanticTexts, tags: params.tags, metadata: params.metadata,\n documents: params.documents, facet: params.facet, // legacy\n });\n }\n\n // Network — brokered by the host and confined to declared endpoints. The\n // sandboxed frame has no direct network at all (connect-src 'none').\n case 'net:fetch': cap('network'); return this.#brokerFetch(record, params);\n\n // Persistent storage — an isolated SQLite database per scope (plugin/domain),\n // on the server or on-device. SQL runs only against the plugin's own scoped db.\n case 'storage:sql': cap('storage'); return this.#pluginSql(record, params);\n\n // Plugin settings + secrets.\n case 'settings:get': return this.platform.settings.get(`${pid}.${params.key}`);\n case 'settings:getSecret': return record.secrets?.[params.key] ?? null;\n case 'settings:set':\n this.platform.settings.set(`${pid}.${params.key}`, params.value);\n return { ok: true };\n\n // Putting a panel on screen is a UI action, and this was the one `ui:` method with\n // no check — so a plugin granted nothing at all could open its own panel (and, with\n // an unvalidated displayName, render into the host page from `activate()`).\n case 'ui:showPanel':\n cap('ui');\n record.hasUi = true;\n this.platform.openPluginPanel?.(pid);\n this.onChange();\n return { ok: true };\n\n // Media session — the host owns navigator.mediaSession; the calling frame\n // (a viewer) surfaces its playback so the OS shows transport controls. Actions\n // fire back over that same frame's RPC channel.\n // Artwork is a URL the BROWSER fetches on the plugin's behalf, so it is egress\n // and belongs under the same allowlist the broker enforces. Left open, `media`\n // alone bought an outbound GET to any URL the plugin chose — the exfiltration\n // channel `connect-src 'none'` and the broker exist to close.\n case 'media:metadata': {\n cap('media');\n const artwork = (params.artwork || []).filter((a) => this.#artworkAllowed(record, a?.src));\n return this.media.apply(frame, 'metadata', { ...params, artwork });\n }\n case 'media:playbackState': cap('media'); return this.media.apply(frame, 'playbackState', params);\n case 'media:position': cap('media'); return this.media.apply(frame, 'position', params);\n case 'media:action': cap('media'); return this.media.apply(frame, 'action', params);\n case 'media:clear': return this.media.apply(frame, 'clear', params);\n\n // Dock — the calling frame registers/unregisters itself for the floating dock.\n case 'dock:enable': cap('dock'); if (frame) frame.dock = { enabled: true, minSize: params.minSize, maxSize: params.maxSize, dismissed: false }; return { ok: true };\n case 'dock:disable': cap('dock'); if (frame?.dock) frame.dock.enabled = false; if (frame && this.dock.docked === frame) this.dock.closeDock(frame); return { ok: true };\n case 'dock:close': if (frame) this.dock.closeDock(frame); return { ok: true };\n\n default:\n throw new Error(`Unknown host method ${method}`);\n }\n }\n\n // --- plugin → host events (fire-and-forget) --------------------------------\n\n hostEvent(record, method, params, frame) {\n switch (method) {\n case 'manifest':\n // Only the primary frame's manifest defines the plugin's live feature list;\n // a viewer frame re-announces its own (opener-only) manifest — ignore it.\n if (frame && frame.role !== 'primary') break;\n record.live = params; record.responsive = true;\n this.onChange();\n break;\n case 'ui:toast':\n this.platform.notifications[params.level || 'info'](`${displayName(record.manifest)}: ${params.text}`);\n break;\n case 'ui:badge':\n record.badge = params.text; this.onChange();\n break;\n }\n }\n\n // --- addressing ------------------------------------------------------------\n\n /**\n * Resolve a command reference from inside a plugin frame. A plugin names its OWN\n * commands by their short contribution name (that's the only name it knows); anything\n * else must be a full address — a built-in like `explorer.download`, or another\n * plugin's contribution URI.\n */\n #resolveCommand(record, id) {\n if (!id || parseContribUri(id)) return id;\n const own = this.platform.contributions.get(contribUri(record.manifest, id));\n return own?.type === 'command' ? own.uri : id;\n }\n\n /**\n * The plugin's own contribution called `name`, of the expected type. Anything else —\n * a name it never declared, or one of the wrong type — is refused: a plugin drives\n * only slots the user saw and approved at install.\n */\n #ownContribution(record, name, type) {\n const c = name ? this.platform.contributions.get(contribUri(record.manifest, name)) : null;\n if (!c || c.pluginId !== record.id || c.type !== type) {\n throw new Error(`\"${name}\" is not a ${type} declared by this plugin`);\n }\n return c;\n }\n\n // --- brokered capabilities -------------------------------------------------\n\n /**\n * Perform a network request on a plugin's behalf, but only to a URL it declared\n * in its manifest `network` allowlist. The request runs from the host with\n * credentials omitted (no ambient cookies/auth), and any redirect that lands\n * off the allowlist is rejected.\n */\n /** blob:/data: carry their own bytes; anything else must be a declared endpoint. */\n #artworkAllowed(record, src) {\n if (!src) return false;\n if (/^(blob:|data:image\\/)/i.test(src)) return true;\n return isAllowedUrl(networkEndpoints(record.manifest), src);\n }\n\n async #brokerFetch(record, { url, method = 'GET', headers, body }) {\n const allow = networkEndpoints(record.manifest);\n // The drive itself is never a \"network endpoint\".\n //\n // The broker runs in the HOST page, which is same-origin with the API. A manifest\n // declaring `network: [\"https://*.com/\"]` matches essentially any drive host, so a\n // plugin approved only for \"connect to the internet\" could call /api/items,\n // /api/items/delete, and other plugins' /api/plugins/:id/sql directly — collecting\n // the whole `files` capability, the per-command grant system, and every other\n // plugin's server-side store in one hop, none of which the user approved. Those\n // routes have a legitimate caller: the host, through the capability-gated methods\n // above.\n if (sameOrigin(url, this.platform.api?.baseUrl)) {\n throw new Error('Blocked: a plugin may not call the drive\\'s own API through the network broker');\n }\n if (!isAllowedUrl(allow, url)) {\n throw new Error(`Blocked: \"${url}\" is not one of this plugin's declared network endpoints`);\n }\n const init = { method, credentials: 'omit', redirect: 'follow', headers: sanitizeHeaders(headers) };\n if (body != null && method !== 'GET' && method !== 'HEAD') {\n init.body = body instanceof ArrayBuffer ? new Uint8Array(body) : body;\n }\n const res = await fetch(url, init);\n // A redirect chain must not escape the declared endpoints — nor land back on the\n // drive, which a declared endpoint is free to redirect to.\n if (res.url && res.url !== url) {\n if (sameOrigin(res.url, this.platform.api?.baseUrl)) {\n throw new Error('Blocked: request redirected onto the drive\\'s own API');\n }\n if (!isAllowedUrl(allow, res.url)) {\n throw new Error(`Blocked: request redirected off this plugin's declared endpoints (${res.url})`);\n }\n }\n const buf = await readCappedBody(res, MAX_FETCH_BYTES);\n const outHeaders = {};\n res.headers.forEach((v, k) => { outHeaders[k] = v; });\n return { ok: res.ok, status: res.status, statusText: res.statusText, url: res.url, headers: outHeaders, bytes: buf };\n }\n\n /**\n * Plugin storage: run a SQL op against one scoped, isolated database. `scope` is\n * 'plugin' or 'domain' (each granted separately); `side` is 'server' or 'client'.\n */\n async #pluginSql(record, { scope = 'plugin', side = 'server', op, sql, params = [], statements }) {\n if (!record.storage?.[scope]) {\n throw new Error(`Storage scope \"${scope}\" not granted${scope === 'domain' ? ' (needs a verified domain)' : ''}`);\n }\n if (side === 'client') {\n // On-device: an isolated wasm SQLite db per scope, held by the host. Domain\n // scope keys by the verified domain so a vendor's plugins share it.\n //\n // Same guard as the server path. The client dbs share ONE emscripten module\n // across every scope, so ATTACH / VACUUM INTO / PRAGMA are an isolation escape\n // here for the same reason they are on disk — the blast radius is the browser's\n // in-memory filesystem rather than the host's, which makes it smaller, not fine.\n if (op === 'batch') for (const s of (Array.isArray(statements) ? statements : [])) assertSafePluginSql(s?.sql);\n else assertSafePluginSql(sql);\n const key = scope === 'domain' ? `dom:${record.manifest.domain}` : `plg:${record.id}`;\n const db = await this.clientDb.obtain(key);\n return runSqlOp(db, op, sql, params, statements);\n }\n // Server: the host proxies to the scoped db over the authenticated API; the\n // domain (for the shared scope) comes from the verified install record, never\n // the plugin.\n const body = { scope, op, sql, params, statements, domain: scope === 'domain' ? record.manifest.domain : undefined };\n const res = await this.platform.api.request('POST', `/api/plugins/${encodeURIComponent(record.id)}/sql`, { body });\n return res.result;\n }\n}\n\n/**\n * Is `url` on the drive's own origin?\n *\n * `baseUrl` is '' in the shipped app (the API is served from the same origin as the\n * page), so the comparison is against `location.origin`; a library caller pointing the\n * client at another host gets that one. A redirect is checked separately, on the way\n * back, since a declared endpoint could redirect here.\n */\nfunction sameOrigin(url, baseUrl) {\n try {\n const here = new URL(baseUrl || '', globalThis.location?.href || 'http://localhost/');\n return new URL(url, here).origin === here.origin;\n } catch {\n return false;\n }\n}\n\n/** True for files a plugin can read as opaque resources (not code, not manifest). */\nfunction isResourcePath(path) {\n return path !== 'manifest.json' && !isSourceModule(path);\n}\n\n// Drop headers the plugin isn't allowed to set (the browser forbids most of these\n// anyway; we strip explicitly so intent is clear and cookies never ride along).\nconst FORBIDDEN_HEADERS = new Set(['host', 'cookie', 'cookie2', 'set-cookie', 'origin', 'referer', 'content-length', 'connection']);\nfunction sanitizeHeaders(headers) {\n const out = {};\n for (const [k, v] of Object.entries(headers || {})) {\n if (!FORBIDDEN_HEADERS.has(String(k).toLowerCase())) out[k] = v;\n }\n return out;\n}\n\n// Dispatch one SQL op onto a SqliteDatabase-shaped handle (the client store; the\n// server mirrors this in routes.js).\nfunction runSqlOp(db, op, sql, params = [], statements) {\n switch (op) {\n case 'exec': return db.exec(sql);\n case 'run': return db.run(sql, ...params);\n case 'get': return db.get(sql, ...params);\n case 'all': return db.all(sql, ...params);\n case 'batch': return db.batch(statements);\n default: throw new Error(`Unknown storage op \"${op}\"`);\n }\n}\n",
|
|
41
|
+
"// Server-side plugin package parsing. The server independently re-parses an uploaded\n// package (never trusting the client that produced it), pulling out the manifest, the\n// verified identity, the declared capabilities and contributions, and a content digest\n// used for dedupe/integrity.\n\nimport { unzipSync, strFromU8 } from 'fflate';\nimport { TroveError } from '../errors.js';\nimport { assertIdentity, pluginId } from './identity.js';\nimport { declaredContributions, serverIndexers, declaredOpeners } from './contributions.js';\n\nexport const ALL_CAPABILITIES = ['files', 'storage', 'ui', 'commands', 'indexer', 'opener', 'network', 'media', 'dock'];\n\nexport { serverIndexers, declaredOpeners, declaredContributions };\n\n/** SHA-256 hex digest of the raw package bytes (content address). */\nexport async function digestBytes(bytes) {\n const buf = await crypto.subtle.digest('SHA-256', bytes);\n return 'sha256:' + [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** Declared capability ids from a manifest (object `{cap: opts}` or array form). */\nexport function capabilityList(manifest) {\n const caps = manifest && manifest.capabilities;\n if (Array.isArray(caps)) return caps.filter(Boolean);\n if (caps && typeof caps === 'object') {\n return Object.entries(caps).filter(([, v]) => v !== false && v != null).map(([k]) => k);\n }\n return [];\n}\n\n/** Whether the granted storage includes the shared `domain` scope. */\nexport function usesSharedStorage(manifest) {\n const opt = manifest?.capabilities?.storage;\n return !!(opt && typeof opt === 'object' && opt.domain);\n}\n\n// A package's COMPRESSED size is capped at the route; its INFLATED size was not, and a\n// zip will happily turn 1 MB into 1 GB. `unzipSync` is synchronous, so that is a\n// gigabyte of RSS and thirteen seconds of blocked event loop for one request — from an\n// unauthenticated caller on the zero-config drive, since the install route only asks for\n// a principal and the shared anonymous one satisfies it.\n//\n// fflate's `filter` runs against the central directory BEFORE any entry is inflated, so\n// the declared sizes are the cheapest possible place to refuse. 64 MiB and 2,000 files\n// are far beyond any real plugin (the largest thing in one is a wasm blob) and far below\n// what hurts.\nconst MAX_INFLATED_BYTES = 64 * 1024 * 1024;\nconst MAX_ENTRIES = 2000;\n\nexport function boundedUnzip(bytes) {\n let total = 0;\n let count = 0;\n try {\n return unzipSync(bytes, {\n filter(file) {\n if (++count > MAX_ENTRIES) throw TroveError.tooLarge(`Package has more than ${MAX_ENTRIES} files`);\n total += file.originalSize || 0;\n if (total > MAX_INFLATED_BYTES) {\n throw TroveError.tooLarge(`Package expands to more than ${Math.round(MAX_INFLATED_BYTES / 1024 / 1024)} MB`);\n }\n return true;\n },\n });\n } catch (err) {\n if (err instanceof TroveError) throw err;\n throw TroveError.invalid('Package is not a valid zip', { cause: err });\n }\n}\n\n/**\n * Parse an uploaded package zip. Returns { manifest, pluginId, files, capabilities,\n * contributions, indexers, openers, digest }. Throws INVALID on a missing/malformed\n * manifest, an unverifiable identity, or a bad contribution declaration.\n * @param {Uint8Array} bytes\n */\nexport async function parsePluginPackage(bytes) {\n const files = boundedUnzip(bytes);\n const manifestRaw = files['manifest.json'];\n if (!manifestRaw) throw TroveError.invalid('Package is missing manifest.json');\n let manifest;\n try {\n manifest = JSON.parse(strFromU8(manifestRaw));\n } catch (err) {\n throw TroveError.invalid('manifest.json is not valid JSON', { cause: err });\n }\n // Identity first: everything else (contribution URIs, install records, storage\n // scopes) is addressed under `<domain>/<name>`, so an anonymous package has no\n // address space to live in and is rejected outright.\n assertIdentity(manifest);\n const capabilities = capabilityList(manifest).filter((c) => ALL_CAPABILITIES.includes(c));\n return {\n manifest,\n pluginId: pluginId(manifest),\n files,\n capabilities,\n contributions: declaredContributions(manifest),\n indexers: serverIndexers(manifest),\n openers: declaredOpeners(manifest),\n sharedStorage: usesSharedStorage(manifest),\n digest: await digestBytes(bytes),\n };\n}\n",
|
|
42
|
+
"// PluginHost — the plugin lifecycle orchestrator. It owns the set of installed\n// plugins and their runtime records, and composes four focused collaborators:\n//\n// FrameManager (pluginFrames.js) — sandboxed iframe spawn/handshake/destroy\n// PluginRpcRouter (pluginRpc.js) — the trusted boundary: capability-gated RPC\n// FrameDock (pluginDock.js) — where a frame is shown; the floating dock\n// MediaController (pluginMedia.js) — navigator.mediaSession bridging\n//\n// What stays here is the lifecycle proper: install/restore/reconcile/uninstall, the\n// availability heartbeat, and mounting a plugin into a panel or viewer.\n//\n// A plugin's code runs inside a sandboxed iframe on an OPAQUE origin: it can't touch\n// the host DOM, read cookies/storage, or fetch its own package files. Everything it\n// can do flows over one transferred MessagePort, gated by the capabilities the user\n// granted at install time. Plugins are persisted (PluginRegistry, IndexedDB) so they\n// survive reloads, and re-announce a live capability manifest so the workbench knows\n// what works right now (incl. offline). See pluginPackage.js / pluginSigning.js for\n// install-time validation and the domain-verified trust signal.\n\nimport { zipSync } from 'fflate';\nimport { PluginRegistry } from './pluginStore.js';\nimport { ClientSqlProvider } from './pluginClientDb.js';\nimport { assessTrust } from './pluginSigning.js';\nimport { ADMIN_ONLY_CAPS, capabilityList, networkEndpoints, grantedStorageScopes, parsePackage, displayName, canExecuteCommand } from './pluginPackage.js';\nimport { declaredContributions, parseKeymap, serverIndexers } from '@3sln/trove/core/plugins/contributions.js';\nimport { pluginId, contribUri } from '@3sln/trove/core/plugins/identity.js';\nimport { endpointSummary } from './pluginNet.js';\nimport { MediaController } from './pluginMedia.js';\nimport { FrameDock } from './pluginDock.js';\nimport { FrameManager } from './pluginFrames.js';\nimport { PluginRpcRouter } from './pluginRpc.js';\n// The canonical capability list lives in core (the server's authority); import it so\n// client and server can't drift when a capability is added.\nimport { ALL_CAPABILITIES } from '@3sln/trove/core/plugins/package.js';\n\nexport class PluginHost {\n constructor(platform, { heartbeatMs = 20000 } = {}) {\n this.platform = platform;\n this.plugins = new Map(); // id -> runtime record\n this.registry = new PluginRegistry();\n // On-device per-scope SQLite (wasm). A failed background persist is data a plugin\n // already believes it saved, so it surfaces rather than disappearing into a log.\n this.clientDb = new ClientSqlProvider({\n onError: (err, key) => this.platform.notifications.error(`A plugin's on-device data couldn't be saved (${key}): ${err.message}`),\n });\n this.online = typeof navigator !== 'undefined' ? navigator.onLine : true;\n this.heartbeatMs = heartbeatMs;\n this._heartbeat = null;\n this._probing = false;\n this.cell = platform.reactive.cell([]);\n\n // Collaborators. The dock destroys frames through the frame manager (assigned\n // just below — the callback is lazy), and both are consulted on frame teardown.\n this.media = new MediaController();\n this.dock = new FrameDock({\n destroyFrame: (frame) => this.frames.destroy(frame),\n openFile: (node, openerId) => this.platform.workbench.openFile(node, openerId),\n onChange: () => this.#emit(),\n });\n this.frames = new FrameManager({ media: this.media, dock: this.dock });\n this.rpc = new PluginRpcRouter({\n platform, clientDb: this.clientDb, media: this.media, dock: this.dock,\n onChange: () => this.#emit(),\n });\n }\n\n observe() {\n return this.cell;\n }\n #emit() {\n this.cell.setValue(this.list());\n }\n\n /**\n * Spawn one sandboxed frame for `record`, wired to this host's RPC router.\n * `entry` boots a different module of the plugin's tree (that's what an opener is).\n */\n #spawnFrame(record, role, entry) {\n return this.frames.spawn(record, role, {\n onCall: (rec, m, p, f) => this.rpc.hostCall(rec, m, p, f),\n onEvent: (rec, m, p, f) => this.rpc.hostEvent(rec, m, p, f),\n online: this.online,\n entry,\n });\n }\n\n /**\n * Register everything the MANIFEST declares, each at its own contribution URI. The\n * manifest is authoritative — this runs before the plugin boots, so: the user's\n * install-time review matches exactly what gets registered, openers/indexers exist\n * without activating the plugin, and they survive a plugin whose primary frame is\n * broken or unresponsive. Nothing a plugin does at runtime can add a contribution;\n * it can only drive the ones declared here (push status content, set a register).\n */\n #registerContributions(runtime) {\n const pid = runtime.id;\n const reg = this.platform.contributions;\n const keep = (dispose) => runtime.disposers.push(dispose);\n const label = displayName(runtime.manifest);\n\n for (const c of declaredContributions(runtime.manifest)) {\n const base = { ...c, pluginId: pid };\n switch (c.type) {\n // A viewer: `entry` names the module that renders it, in its own frame.\n // Skipped entirely if the user declined \"provides viewers\" at install.\n case 'opener':\n if (runtime.grants.includes('opener')) keep(reg.register(c.uri, base));\n break;\n\n // Declared, implemented by the plugin's primary frame. The handler proxies\n // over RPC using the contribution's short name — inside its own frame a\n // plugin addresses its commands by name, not by URI.\n case 'command':\n keep(this.platform.commands.register({\n id: c.uri, title: c.title || `${label}: ${c.name}`,\n category: c.category || label, icon: c.icon,\n when: c.when, offline: c.offline, palette: c.palette, pluginId: pid,\n handler: (...args) => runtime.channel?.call('command:execute', { id: c.name, args }),\n }));\n break;\n\n // A slot in the status bar, empty and hidden until the plugin pushes content.\n // A CLICKABLE one is the plugin asking the host to run something — the same ask\n // a keyboard shortcut makes — so it goes through the same per-command grant.\n // Without this, a manifest holding only the `ui` capability could put an\n // inviting button in the status bar wired to `explorer.delete` or\n // `plugins.installFromUrl`, and the install review never mentioned the field.\n case 'statusItem': {\n const command = c.command\n ? this.#resolveCommand(runtime, c.command, label, `status item “${c.name}”`)\n : null;\n keep(reg.register(c.uri, { ...base, command, html: '', visible: false }));\n break;\n }\n\n // A context value slot. Seed the context with its declared default so\n // when-clauses referencing it evaluate sensibly before the plugin runs.\n case 'register':\n keep(reg.register(c.uri, base));\n this.platform.context.set(c.uri, c.default);\n keep(() => this.platform.context.remove(c.uri));\n break;\n\n // A keymap JSON file inside the package. Read + validated here, at register\n // time, so a malformed keymap is a visible install-time problem rather than a\n // shortcut that silently never fires.\n case 'keymap': {\n const bytes = runtime.files.get(c.path);\n if (!bytes) {\n this.platform.notifications.warn(`Plugin \"${label}\": keymap file \"${c.path}\" is not in the package.`);\n break;\n }\n try {\n const bindings = this.#resolveBindings(runtime, parseKeymap(new TextDecoder().decode(bytes)), label);\n keep(reg.register(c.uri, { ...base, bindings }));\n } catch (err) {\n this.platform.notifications.warn(`Plugin \"${label}\": ${err.message}`);\n }\n break;\n }\n\n // Indexers are deliberately NOT registered client-side. They run on the server\n // (PluginIndexers registers them from the install record), because indexing\n // must happen once per upload for the drive — not in whichever tab is open.\n case 'indexer':\n break;\n }\n }\n }\n\n /**\n * Resolve a keymap's bindings into real addresses, and refuse the ones the plugin\n * isn't allowed to trigger. A binding naming one of the plugin's own commands is\n * rewritten to that contribution's URI (inside its package a plugin knows only short\n * names); anything else is a foreign address and must be in the manifest's `commands`\n * allowlist — a shortcut is still the plugin asking the host to run something, so it\n * can't be a way around the per-command grant the user approved.\n */\n #resolveBindings(runtime, bindings, label) {\n const out = [];\n for (const b of bindings) {\n const command = this.#resolveCommand(runtime, b.command, label, `shortcut ${b.key}`);\n if (command) out.push({ ...b, command });\n }\n return out;\n }\n\n /**\n * One command address the plugin wants to be able to trigger, resolved and checked.\n *\n * A short name is one of the plugin's own commands and becomes its contribution URI.\n * Anything else is a FOREIGN address and must appear in the manifest's `commands`\n * allowlist. Returns null (with a warning naming `what`) when it isn't allowed, so\n * every route by which a plugin can ask the host to run something — keymaps, status\n * items — passes through the same grant the user actually approved.\n */\n #resolveCommand(runtime, wanted, label, what) {\n const uri = contribUri(runtime.manifest, wanted);\n const own = declaredContributions(runtime.manifest).some((c) => c.uri === uri && c.type === 'command');\n if (own) return uri;\n if (!canExecuteCommand(runtime.manifest, wanted)) {\n this.platform.notifications.warn(`Plugin \"${label}\": ${what} runs \"${wanted}\", which it isn't allowed to run — ignored.`);\n return null;\n }\n return wanted;\n }\n\n list() {\n return [...this.plugins.values()].map((p) => ({\n id: p.id, name: displayName(p.manifest), version: p.manifest.version, status: p.status,\n capabilities: p.grants, error: p.error || null, hasUi: p.hasUi, badge: p.badge || null,\n trust: p.trust || null, settingsSchema: p.manifest.settings || [],\n endpoints: endpointSummary(networkEndpoints(p.manifest)),\n responsive: !!p.responsive, manifest: p.live || null, features: this.#featureList(p),\n }));\n }\n\n /**\n * What this plugin contributes — read from the MANIFEST, so the list is known\n * without the plugin running (and stays accurate if it stops responding). `live`\n * is only used for liveness/availability, not for what exists.\n */\n #featureList(record) {\n let declared;\n try { declared = declaredContributions(record.manifest); } catch { return []; }\n return declared.map((c) => ({\n kind: c.type, id: c.uri, name: c.name, title: c.title || c.name,\n offline: !!c.offline, available: this.#availableSpec(record, c),\n }));\n }\n #availableSpec(record, spec) {\n if (record.status !== 'active' || !record.responsive) return false;\n return this.online || !!spec.offline;\n }\n isAvailable(contrib) {\n if (!contrib?.pluginId) return true;\n const record = this.plugins.get(contrib.pluginId);\n if (!record) return false;\n return this.#availableSpec(record, contrib);\n }\n\n // --- install / restore -----------------------------------------------------\n\n /** Whether the current user may grant a capability (some need admin). */\n canGrant(cap, isAdmin) {\n return !ADMIN_ONLY_CAPS.has(cap) || !!isAdmin;\n }\n\n /**\n * Install a parsed package. `grants` is the subset of requested capabilities\n * the user approved (the review UI filters admin-only caps for non-admins).\n */\n async install(pkg, { grants, trust } = {}) {\n const id = pluginId(pkg.manifest);\n const requested = capabilityList(pkg.manifest);\n const granted = (grants || requested).filter((c) => ALL_CAPABILITIES.includes(c) && requested.includes(c));\n // Account-scoped plugins (server storage or a server indexer) upload their full\n // package to the server: it re-validates, gates admin-only packages, and becomes\n // the canonical copy so the plugin syncs to the user's other devices and its\n // capabilities are enforced server-side. Throws if the server rejects (e.g. admin\n // required) — surfaced to the user by the install UI. Device-scoped plugins stay local.\n const scope = accountScoped(pkg.manifest, granted) ? 'account' : 'device';\n if (scope === 'account') {\n if (!pkg.raw) throw new Error('Package bytes unavailable for a server install');\n await this.platform.api.installPlugin(pkg.raw, granted);\n }\n const storage = granted.includes('storage')\n ? grantedStorageScopes(pkg.manifest, trust)\n : { plugin: false, domain: false };\n const record = {\n id, manifest: pkg.manifest,\n files: Object.fromEntries([...pkg.files.entries()]),\n grants: granted, storage, trust: trust || null, scope,\n settings: {}, secrets: {}, installedAt: Date.now(),\n };\n await this.registry.save(record);\n this.#registerSettings(record);\n await this.#run(record);\n return this.plugins.get(id);\n }\n\n /** Load all persisted plugins (call once at startup), then sync account plugins. */\n async restore() {\n let records = [];\n try {\n records = await this.registry.list();\n } catch { /* no IndexedDB */ }\n for (const rec of records) {\n // A single corrupt persisted record (missing manifest, etc.) must not abort the\n // restore of every other plugin — guard per record.\n try {\n this.#registerSettings(rec);\n this.#run(rec).catch((e) => console.error('restore plugin failed', rec.id, e));\n } catch (e) {\n console.error('skipping corrupt plugin record', rec?.id, e);\n this.platform.notifications.warn(`Couldn't restore plugin \"${rec?.manifest ? displayName(rec.manifest) : rec?.id || 'unknown'}\" — its saved data looks corrupt.`);\n }\n }\n // Reconcile with the server's account plugins (best-effort, offline-tolerant).\n this.#reconcile(records).catch(() => {});\n }\n\n // Two-way sync of account plugins with the server: push any local account plugin the\n // server is missing (migration for pre-existing local installs), and pull any server\n // account plugin not yet on this device (cross-device sync).\n async #reconcile(localRecords) {\n let serverList;\n try { serverList = (await this.platform.api.installedPlugins()).plugins || []; } catch { return; }\n const serverIds = new Set(serverList.map((p) => p.pluginId));\n const localIds = new Set(localRecords.map((r) => r.id));\n let pushFailures = 0;\n let pulled = 0;\n let pullFailures = 0;\n\n // Push: local account plugins the server doesn't have → re-upload (re-zipped).\n for (const rec of localRecords) {\n if (rec.scope !== 'account' || serverIds.has(rec.id)) continue;\n try {\n await this.platform.api.installPlugin(zipSync(toU8Files(rec.files)), rec.grants || []);\n } catch (e) { console.error('re-upload plugin failed', rec.id, e); pushFailures++; }\n }\n\n // Pull: server account plugins not on this device → download + enable.\n for (const rec of serverList) {\n if (localIds.has(rec.pluginId)) continue;\n try {\n const pkg = parsePackage(await this.platform.api.pluginPackage(rec.pluginId));\n const grants = rec.grants || [];\n const record = {\n id: rec.pluginId, manifest: pkg.manifest,\n files: Object.fromEntries([...pkg.files.entries()]),\n grants,\n // The SHARED domain scope comes from the server's install record, not from\n // the package's own manifest. The install path gates it on verified domain\n // ownership (`grantedStorageScopes` requires trust.status === 'verified') and\n // the review UI shows it as blocked; this sync path set `trust: null` and read\n // the self-declared manifest — so a package refused the shared store on the\n // device it was installed on got it on the next device that synced.\n storage: grants.includes('storage')\n ? { plugin: true, domain: !!rec.sharedStorage }\n : { plugin: false, domain: false },\n trust: null, scope: 'account',\n settings: {}, secrets: {}, installedAt: rec.createdAt || Date.now(),\n };\n await this.registry.save(record);\n this.#registerSettings(record);\n await this.#run(record);\n pulled++;\n } catch (e) { console.error('sync plugin failed', rec.pluginId, e); pullFailures++; }\n }\n\n if (pulled) this.platform.notifications.info(`Synced ${pulled} account plugin${pulled > 1 ? 's' : ''} from the server.`);\n if (pushFailures || pullFailures) {\n const parts = [];\n if (pullFailures) parts.push(`${pullFailures} couldn't be downloaded`);\n if (pushFailures) parts.push(`${pushFailures} couldn't be uploaded`);\n this.platform.notifications.warn(`Some account plugins didn't sync: ${parts.join(', ')}.`);\n }\n }\n\n #registerSettings(record) {\n const schema = (record.manifest.settings || []).filter((s) => !s.secret);\n if (schema.length) {\n record._settingsDispose = this.platform.settings.scopedFor(record.id).register(\n schema.map((s) => ({ ...s, category: displayName(record.manifest) })),\n );\n }\n }\n\n // --- run (create the plugin's primary sandboxed iframe) --------------------\n\n async #run(record) {\n if (this.plugins.get(record.id)?.status === 'active') return;\n const runtime = {\n ...record, iframe: null, status: 'loading', error: null, disposers: [], channel: null,\n hasUi: false, responsive: false, frame: null, frames: new Set(),\n // Storage scopes (defaulted for records saved before this field existed).\n storage: record.storage || (record.grants?.includes('storage') ? grantedStorageScopes(record.manifest, record.trust) : { plugin: false, domain: false }),\n files: mapFromFiles(record.files),\n };\n this.plugins.set(record.id, runtime);\n // Register declared contributions BEFORE booting — they don't depend on the\n // plugin running, and this keeps them alive if its frame never comes up.\n try { this.#registerContributions(runtime); } catch (e) { console.error('registering contributions failed', record.id, e); }\n this.#emit();\n\n try {\n // The primary (background) frame: registers commands/indexers, does one-time\n // setup. Its channel is the record's channel used for probes and commands.\n const frame = await this.#spawnFrame(runtime, 'primary');\n // Uninstalled while the handshake was in flight? The record is no longer in the\n // map — tear this frame down instead of leaving a live, unreachable iframe.\n if (this.plugins.get(record.id) !== runtime) { this.frames.destroy(frame); return runtime; }\n runtime.frame = frame;\n runtime.iframe = frame.iframe;\n runtime.channel = frame.channel;\n runtime.status = 'active';\n runtime.responsive = true;\n this.#probe(runtime).then(() => this.#emit());\n this.#startHeartbeat();\n } catch (err) {\n runtime.status = 'error';\n runtime.error = err?.message || String(err);\n this.platform.notifications.error(`Plugin \"${displayName(record.manifest)}\" failed to load: ${runtime.error}`);\n }\n this.#emit();\n return runtime;\n }\n\n // --- settings & secrets (host-side, for the UI) ----------------------------\n\n async setSecret(pluginId, key, value) {\n const rec = await this.registry.get(pluginId);\n if (!rec) return;\n rec.secrets = { ...rec.secrets, [key]: value };\n await this.registry.save(rec);\n const runtime = this.plugins.get(pluginId);\n if (runtime) runtime.secrets = rec.secrets;\n runtime?.channel?.emit('settings:changed', { key, value: '••••' });\n }\n async getSecret(pluginId, key) {\n const rec = await this.registry.get(pluginId);\n return rec?.secrets?.[key] ?? '';\n }\n\n // --- heartbeat / availability ----------------------------------------------\n\n #startHeartbeat() {\n if (this._heartbeat || !this.heartbeatMs) return;\n this._heartbeat = setInterval(() => this.#heartbeat(), this.heartbeatMs);\n if (this._heartbeat?.unref) this._heartbeat.unref();\n }\n #stopHeartbeat() {\n if (this._heartbeat) clearInterval(this._heartbeat);\n this._heartbeat = null;\n }\n async #heartbeat() {\n if (this._probing) return;\n this._probing = true;\n try {\n let changed = false;\n for (const r of this.plugins.values()) if (r.status === 'active') changed = (await this.#probe(r)) || changed;\n if (changed) this.#emit();\n } finally {\n this._probing = false;\n }\n }\n setHeartbeat(ms) {\n this.heartbeatMs = ms;\n this.#stopHeartbeat();\n if (ms && [...this.plugins.values()].some((r) => r.status === 'active')) this.#startHeartbeat();\n }\n async #probe(record) {\n if (record.status !== 'active' || !record.channel) return false;\n const before = { responsive: record.responsive, sig: signature(record.live) };\n try {\n record.live = await record.channel.call('manifest', {}, { timeout: 4000 });\n record.responsive = true;\n } catch {\n record.responsive = false;\n }\n return before.responsive !== record.responsive || before.sig !== signature(record.live);\n }\n async setOnline(online) {\n if (this.online === online) return;\n this.online = online;\n await Promise.all([...this.plugins.values()].filter((r) => r.status === 'active').map(async (r) => {\n try { r.channel?.emit('connectivity', { online }); } catch { /* ignore */ }\n for (const frame of r.frames || []) { try { frame.channel?.emit('connectivity', { online }); } catch { /* ignore */ } }\n await this.#probe(r);\n }));\n this.#emit();\n }\n async refresh(pluginId) {\n const record = this.plugins.get(pluginId);\n if (record) { await this.#probe(record); this.#emit(); }\n }\n\n // --- mounting (panel / viewer) ---------------------------------------------\n //\n // Where a frame is *shown* is FrameDock's job (position:fixed overlay tracking a\n // target element — we never re-parent an iframe, which would reload it). These\n // methods own the mount lifecycle: which frame, opened with what, and what happens\n // when it detaches.\n\n mountPanel(pluginId, container, { width = 380, height = 480 } = {}) {\n const record = this.plugins.get(pluginId);\n if (!record?.frame) return null;\n container.style.width = `${width}px`;\n container.style.height = `${height}px`;\n this.dock.place(record.frame, container, 50);\n record.hasUi = true;\n return () => this.dock.hide(record.frame);\n }\n\n /**\n * Mount a plugin opener as the viewer for `node`. Each viewer runs in its OWN\n * sandboxed iframe (spawned with the plugin's capabilities), so viewers are\n * isolated from the background instance and from each other. Returns a detach fn:\n * if the viewer registered `dock`, its frame is kept alive as a floating dock,\n * otherwise it is destroyed.\n */\n mountViewer(pluginId, container, node, openerId, hooks = {}) {\n const record = this.plugins.get(pluginId);\n if (!record) { hooks.onError?.('Plugin is not available'); return null; }\n\n // Re-adopt the docked frame if it's the same file — preserves live playback\n // (the docked <video>/<audio> keeps running) instead of spawning a fresh viewer.\n const docked = this.dock.docked;\n if (docked && docked.record === record && docked.node?.id === node.id && docked.openerId === openerId) {\n this.dock.undock(docked);\n this.dock.place(docked, container, 3);\n hooks.onReady?.();\n return () => this.#detachViewer(docked);\n }\n\n // Boot the frame at the OPENER's entry module — not the plugin's main entry, so a\n // viewer loads only the code it needs and never re-runs the plugin's background setup.\n const opener = this.platform.contributions.get(openerId);\n const mount = { cancelled: false, frame: null };\n this.#spawnFrame(record, 'viewer', opener?.entry).then(async (frame) => {\n if (mount.cancelled) { this.frames.destroy(frame); return; }\n mount.frame = frame;\n frame.node = node;\n frame.openerId = openerId;\n record.frames.add(frame);\n this.dock.place(frame, container, 3);\n try {\n await frame.channel?.call('opener:open', { openerId, file: node, context: {} });\n if (!mount.cancelled) hooks.onReady?.();\n } catch (err) {\n if (!mount.cancelled) hooks.onError?.(err?.message || 'Failed to open');\n }\n }).catch((e) => {\n console.error('viewer frame failed to load', e);\n if (!mount.cancelled) hooks.onError?.(e?.message || 'This viewer failed to load');\n });\n record.hasUi = true;\n\n return () => {\n mount.cancelled = true;\n if (mount.frame) this.#detachViewer(mount.frame);\n };\n }\n\n // A viewer went off-screen: dock it (if it opted in and isn't dismissed) or destroy it.\n #detachViewer(frame) {\n if (frame.dock?.enabled && !frame.dock.dismissed) this.dock.dock(frame);\n else this.frames.destroy(frame);\n }\n\n // --- uninstall --------------------------------------------------------------\n\n /** Uninstall: stop the plugin, forget it, and wipe everything it owns. */\n async uninstall(pluginId, { wipeData = true } = {}) {\n const record = this.plugins.get(pluginId);\n const name = record?.manifest ? displayName(record.manifest) : pluginId;\n const isAccount = record?.scope === 'account'\n || (await this.registry.get(pluginId).catch(() => null))?.scope === 'account';\n\n // Account plugins: the server holds the canonical copy. Remove it there FIRST — if\n // that fails we must NOT tear down locally, or the plugin silently resurrects on the\n // next reload (server still lists it) with no sign anything went wrong.\n if (wipeData && isAccount) {\n try {\n await this.platform.api.uninstallPluginServer(pluginId);\n } catch (err) {\n this.platform.notifications.error(`Couldn't uninstall \"${name}\" from the server: ${err.message}. It's still installed.`);\n return;\n }\n }\n\n if (record) {\n try { record.channel?.emit('deactivate'); } catch { /* ignore */ }\n for (const d of record.disposers) { try { d(); } catch { /* ignore */ } }\n record._settingsDispose?.();\n for (const frame of [...(record.frames || [])]) this.frames.destroy(frame); // viewer/dock frames\n // The primary frame may have an open panel placement (observers + listeners).\n if (record.frame) { this.dock.stopPlace(record.frame); this.media.releaseActions(record.frame); }\n record.channel?.dispose();\n record.iframe?.remove();\n this.plugins.delete(pluginId);\n }\n // Best-effort cleanup of the plugin's private stores. A failure here doesn't block\n // uninstall, but the user is told so leftover data isn't silently orphaned.\n const wipeFailures = [];\n if (wipeData) {\n if (!isAccount) await this.platform.api.request('DELETE', `/api/plugins/${encodeURIComponent(pluginId)}/data`).catch(() => wipeFailures.push('server-side data'));\n await this.clientDb.drop(`plg:${pluginId}`).catch(() => wipeFailures.push('on-device data'));\n }\n // The persisted record is what restore() reads: if this fails and we say nothing,\n // the plugin silently reappears on the next reload looking like a bug in uninstall.\n let resurrects = false;\n try {\n await this.registry.remove(pluginId);\n } catch (err) {\n resurrects = true;\n console.error('removing the persisted plugin record failed', pluginId, err);\n }\n if (![...this.plugins.values()].some((r) => r.status === 'active')) this.#stopHeartbeat();\n this.#emit();\n if (resurrects) {\n this.platform.notifications.error(`Removed \"${name}\", but its saved record couldn't be deleted — it may come back when you reload.`);\n } else if (wipeFailures.length) {\n this.platform.notifications.warn(`Uninstalled \"${name}\", but couldn't clear its ${wipeFailures.join(' and ')}.`);\n }\n }\n\n // --- trust ------------------------------------------------------------------\n\n /** Fetch a domain's assetlinks doc through the server (avoids CORS). */\n fetchAssetlinks = async (domain) => {\n const res = await this.platform.api.request('GET', '/api/plugins/assetlinks', { query: { domain } });\n return res?.assetlinks || null;\n };\n assessTrust(pkg) {\n return assessTrust(pkg, this.fetchAssetlinks);\n }\n}\n\nfunction mapFromFiles(files) {\n const m = new Map();\n for (const [k, v] of Object.entries(files)) m.set(k, v instanceof Uint8Array ? v : new Uint8Array(v));\n return m;\n}\n\n// Coerce a stored files object (path -> bytes, possibly ArrayBuffer after IndexedDB\n// round-trip) into the { path: Uint8Array } shape fflate's zipSync expects.\nfunction toU8Files(files) {\n const out = {};\n for (const [k, v] of Object.entries(files || {})) out[k] = v instanceof Uint8Array ? v : new Uint8Array(v);\n return out;\n}\n\n// A plugin is account-scoped (must install to the server) if it has a server\n// footprint: server storage, or an indexer (which the SERVER runs). Purely\n// client-side plugins stay device-local.\nfunction accountScoped(manifest, granted) {\n if (granted.includes('storage')) return true;\n return serverIndexers(manifest).length > 0;\n}\n\n// A cheap fingerprint of a plugin's live self-report, used to tell whether a heartbeat\n// changed anything worth re-rendering for.\nfunction signature(live) {\n if (!live) return '';\n return `${live.online ? 1 : 0}|${[...(live.handlers || [])].sort().join(',')}`;\n}\n",
|
|
43
|
+
"// OverlayService — the shell's transient overlays: the command palette, modal\n// dialogs, the right-click context menu, and the plugin popup panel. Extracted from\n// WorkbenchService (which kept ~10 concerns); these four are independent of the panel\n// stack, so they own their own state + subject. The workbench holds one and delegates,\n// coordinating only the Esc/close-topmost ordering (which also spans the stack).\n\nimport { cell } from '../runtime.js';\n\nexport class OverlayService {\n constructor(context) {\n this.context = context;\n this.state = {\n palette: null, // { mode: 'commands'|'files', query, index } when open\n dialog: null,\n contextMenu: null,\n pluginPanel: null,\n };\n this.cell = cell(this.state);\n }\n\n observe() {\n return this.cell;\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n\n // --- command palette -------------------------------------------------------\n openPalette(mode = 'commands', query = '') {\n this.#set({ palette: { mode, query, index: 0 } });\n this.context.set('palette.open', true);\n }\n setPaletteQuery(query) {\n if (this.state.palette) this.#set({ palette: { ...this.state.palette, query, index: 0 } });\n }\n movePalette(delta, count) {\n if (!this.state.palette || !count) return;\n this.#set({ palette: { ...this.state.palette, index: wrapIndex(this.state.palette.index, delta, count) } });\n }\n setPaletteIndex(index) {\n if (this.state.palette) this.#set({ palette: { ...this.state.palette, index } });\n }\n closePalette() {\n this.#set({ palette: null });\n this.context.set('palette.open', false);\n }\n\n // --- dialog ----------------------------------------------------------------\n showDialog(dialog) {\n this.#set({ dialog });\n }\n /** Merge a patch into the open dialog's state (reactive dialogs, e.g. the chooser). */\n updateDialog(patch) {\n if (this.state.dialog) this.#set({ dialog: { ...this.state.dialog, ...patch } });\n }\n closeDialog() {\n this.#set({ dialog: null });\n }\n\n // --- context menu ----------------------------------------------------------\n showContextMenu(x, y, items) {\n this.#set({ contextMenu: { x, y, items } });\n }\n closeContextMenu() {\n this.#set({ contextMenu: null });\n }\n\n // --- plugin popup panel ----------------------------------------------------\n openPluginPanel(pluginId) {\n this.#set({ pluginPanel: pluginId });\n }\n closePluginPanel() {\n this.#set({ pluginPanel: null });\n }\n}\n\n/** Wrap a list cursor by `delta` within `[0, count)` (shared by the palette + launcher). */\nexport function wrapIndex(index, delta, count) {\n return (index + delta + count) % count;\n}\n",
|
|
44
|
+
"// NavigationService — the main panel STACK and its browser-history mirror. The base\n// of the stack is the launcher (search); opening a file pushes a viewer panel; back\n// pops. The stack is mirrored into window.history (pushState/popState) so the browser\n// back/forward buttons navigate it. Also owns the \"recent files\" list. Extracted from\n// WorkbenchService; the workbench coordinates the couplings it can't own (setting the\n// activity to 'home' and closing the modal search when a file opens).\n\nimport { cell } from '../runtime.js';\n\nconst RECENTS_KEY = 'trove.recents';\nconst RECENTS_MAX = 12;\n\nexport class NavigationService {\n constructor(context) {\n this.context = context;\n this.state = {\n stack: [{ kind: 'search' }], // [{kind:'search'}, {kind:'file', id, node, openerId}, …]\n activeTabId: null, // top file panel id (or null)\n activeFile: null, // top file panel node (or null)\n recents: loadRecents(),\n };\n this.cell = cell(this.state);\n }\n\n observe() {\n return this.cell;\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n\n #top() {\n return this.state.stack[this.state.stack.length - 1];\n }\n /** The active file panel (top of the stack, if it's a file). */\n activeTab() {\n const t = this.#top();\n return t?.kind === 'file' ? t : null; // panel kind, not node kind\n }\n\n #applyStack(stack, { history = true } = {}) {\n const top = stack[stack.length - 1];\n const file = top && top.kind === 'file' ? top : null;\n this.#set({ stack, activeTabId: file ? file.id : null, activeFile: file ? file.node : null });\n this.context.setMany({ 'editor.open': !!file, 'editor.openerId': file?.openerId || '', 'editor.contentType': file?.node.contentType || '' });\n if (history) this.#pushHistory();\n }\n\n /** Reset to the base launcher (used by showHome). */\n reset() {\n this.#applyStack([{ kind: 'search' }]);\n }\n\n /**\n * Open a file into the viewer stack. `reset` (used by the modal search) starts a\n * fresh stack (base search + this file). Opening a file already in the stack\n * jumps back to it; otherwise it's pushed on top.\n */\n openFile(node, openerId, { reset = false } = {}) {\n const panel = { kind: 'file', id: node.id, node, openerId };\n let stack;\n if (reset) {\n stack = [{ kind: 'search' }, panel];\n } else {\n const at = this.state.stack.findIndex((p) => p.kind === 'file' && p.id === node.id);\n if (at >= 0) {\n // Already open — jump back to it, but adopt the (possibly new) opener so\n // reopening with a different one actually switches the viewer.\n stack = this.state.stack.slice(0, at + 1);\n stack[at] = panel;\n } else {\n stack = [...this.state.stack, panel];\n }\n }\n this.#pushRecent(node);\n this.#applyStack(stack);\n }\n back() {\n if (this.state.stack.length <= 1) return;\n try { history.back(); } catch { this.pop(); }\n }\n pop() {\n if (this.state.stack.length > 1) this.#applyStack(this.state.stack.slice(0, -1), { history: false });\n }\n closeTab(id) {\n const stack = this.state.stack.filter((p) => !(p.kind === 'file' && p.id === id));\n this.#applyStack(stack.length ? stack : [{ kind: 'search' }], { history: false });\n }\n updateTabNode(node) {\n const stack = this.state.stack.map((p) => (p.kind === 'file' && p.id === node.id ? { ...p, node } : p));\n this.#applyStack(stack, { history: false });\n }\n\n // --- history sync ----------------------------------------------------------\n #pushHistory(replace = false) {\n const top = this.#top();\n const n = top?.node;\n const entry = {\n troveDepth: this.state.stack.length,\n node: n ? { id: n.id, name: n.name, contentType: n.contentType, collectionId: n.collectionId } : null,\n openerId: top?.openerId || null,\n };\n try { (replace ? history.replaceState : history.pushState).call(history, entry, ''); } catch { /* no history API */ }\n }\n /** Handle a browser back/forward: sync the stack depth (re-open on forward). */\n onPopState(e) {\n const s = e?.state || {};\n const depth = s.troveDepth || 1;\n if (depth <= this.state.stack.length) {\n this.#applyStack(this.state.stack.slice(0, depth), { history: false });\n } else if (s.node) {\n this.#applyStack([...this.state.stack, { kind: 'file', id: s.node.id, node: s.node, openerId: s.openerId }], { history: false });\n }\n }\n\n #pushRecent(node) {\n if (!node) return;\n const entry = { id: node.id, name: node.name, contentType: node.contentType || '', collectionId: node.collectionId };\n const recents = [entry, ...this.state.recents.filter((r) => r.id !== node.id)].slice(0, RECENTS_MAX);\n this.#set({ recents });\n saveRecents(recents);\n }\n}\n\nfunction loadRecents() {\n try { return JSON.parse(localStorage.getItem(RECENTS_KEY)) || []; } catch { return []; }\n}\nfunction saveRecents(recents) {\n try { localStorage.setItem(RECENTS_KEY, JSON.stringify(recents)); } catch { /* private mode / no storage */ }\n}\n",
|
|
45
|
+
"// WorkbenchService — the shell's ephemeral UI state (not the file data, which\n// ngin owns). The main panel is a STACK of panels: the base is the root search\n// (launcher); opening a file pushes a viewer panel; back pops. The stack is mirrored\n// into the browser history (pushState/popState) so back/forward navigate it. A\n// double-shift opens a modal search overlay; picking from it resets the stack.\n// It also mirrors key bits into the ContextKeyService for when-clauses.\n\nimport { cell } from '../runtime.js';\nimport { OverlayService, wrapIndex } from './overlay.js';\nimport { NavigationService } from './navigation.js';\n\nexport class WorkbenchService {\n constructor(context) {\n this.context = context;\n // Two focused sub-services own their own state + subject; the workbench delegates\n // and coordinates the couplings across them (Esc close-order, opening a file).\n this.overlay = new OverlayService(context);\n this.nav = new NavigationService(context);\n this.state = {\n activity: 'home', // home (stack) | plugins | settings\n sidebarVisible: true,\n launch: { query: '', index: 0 }, // the launcher's query\n searchModal: false, // the double-shift modal search overlay\n infoPanel: false,\n // Phone chrome: which bottom sheet is up, if any ('status' | 'more'). A phone has\n // no room for a permanent status bar or a left rail, so both fold into a sheet\n // that is pulled up on demand.\n sheet: null,\n };\n this.cell = cell(this.state);\n }\n\n observe() {\n return this.cell;\n }\n observeOverlay() {\n return this.overlay.observe();\n }\n observeNav() {\n return this.nav.observe();\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n\n /**\n * Say that something the shell renders from changed without going through here.\n *\n * There is exactly one such thing — `platform.capabilities`, which arrives from the\n * server after boot and is read straight off the platform by the status bar. A cell\n * drops a write of the value it already holds, so re-pushing `state` is not a nudge;\n * a fresh object is.\n */\n touch() {\n this.#set({});\n }\n\n // --- overlay delegations (state lives in OverlayService) -------------------\n openPalette(mode, query) { this.overlay.openPalette(mode, query); }\n setPaletteQuery(query) { this.overlay.setPaletteQuery(query); }\n movePalette(delta, count) { this.overlay.movePalette(delta, count); }\n closePalette() { this.overlay.closePalette(); }\n showDialog(dialog) { this.overlay.showDialog(dialog); }\n updateDialog(patch) { this.overlay.updateDialog(patch); }\n closeDialog() { this.overlay.closeDialog(); }\n showContextMenu(x, y, items) { this.overlay.showContextMenu(x, y, items); }\n closeContextMenu() { this.overlay.closeContextMenu(); }\n openPluginPanel(pluginId) { this.overlay.openPluginPanel(pluginId); }\n closePluginPanel() { this.overlay.closePluginPanel(); }\n\n // --- navigation delegations (state lives in NavigationService) -------------\n activeTab() { return this.nav.activeTab(); }\n back() { this.nav.back(); }\n pop() { this.nav.pop(); }\n closeTab(id) { this.nav.closeTab(id); }\n updateTabNode(node) { this.nav.updateTabNode(node); }\n onPopState(e) { this.nav.onPopState(e); }\n\n setActivity(activity) {\n this.#set({ activity, sidebarVisible: true });\n this.context.set('view.active', activity);\n }\n /** Return to the launcher home (reset the panel stack to the base search). */\n showHome() {\n this.#set({ activity: 'home', searchModal: false });\n this.nav.reset();\n this.context.set('view.active', 'home');\n }\n\n // --- launcher --------------------------------------------------------------\n setLaunchQuery(query) {\n this.#set({ launch: { query, index: 0 } });\n }\n moveLaunch(delta, count) {\n if (!count) return;\n this.#set({ launch: { ...this.state.launch, index: wrapIndex(this.state.launch.index, delta, count) } });\n }\n /** The command palette's highlighted row. Missing entirely, so every `mouseenter`\n * threw and the highlight never followed the pointer — hover row 5, press Enter,\n * run row 0. */\n setPaletteIndex(index) {\n this.overlay.setPaletteIndex?.(index);\n }\n setLaunchIndex(index) {\n this.#set({ launch: { ...this.state.launch, index } });\n }\n\n // --- modal search (double-shift) ------------------------------------------\n openSearchModal() {\n this.#set({ searchModal: true, launch: { query: '', index: 0 } });\n this.context.set('searchModal.open', true);\n }\n closeSearchModal() {\n this.#set({ searchModal: false });\n this.context.set('searchModal.open', false);\n }\n\n /**\n * Open a file into the viewer stack (delegates to NavigationService). Coordinates the\n * shell state the nav service can't own: switch to the home activity and close the\n * modal search overlay.\n */\n openFile(node, openerId, opts = {}) {\n this.#set({ activity: 'home', searchModal: false });\n this.nav.openFile(node, openerId, opts);\n }\n /** Raise (or swap, or drop) the phone bottom sheet. Tapping the open one closes it. */\n openSheet(name) {\n this.#set({ sheet: this.state.sheet === name ? null : name });\n this.context.set('sheet.open', this.state.sheet || '');\n }\n closeSheet() {\n if (!this.state.sheet) return;\n this.#set({ sheet: null });\n this.context.set('sheet.open', '');\n }\n toggleInfoPanel(force) {\n this.#set({ infoPanel: force ?? !this.state.infoPanel });\n this.context.set('infoPanel.open', this.state.infoPanel);\n }\n\n /** Close any transient overlay (Esc), in priority order across overlay + stack.\n * Returns true if something closed. */\n closeOverlays() {\n const o = this.overlay.state;\n if (o.contextMenu) return this.overlay.closeContextMenu(), true;\n if (o.dialog) return this.overlay.closeDialog(), true;\n if (this.state.sheet) return this.closeSheet(), true;\n if (this.state.searchModal) return this.closeSearchModal(), true;\n if (o.palette) return this.overlay.closePalette(), true;\n if (o.pluginPanel) return this.overlay.closePluginPanel(), true;\n if (this.nav.state.stack.length > 1) return this.nav.back(), true; // pop the top viewer panel\n return false;\n }\n}\n",
|
|
46
|
+
"// What kind of screen is this, and how is it being driven?\n//\n// The drive runs on three shapes of device that want genuinely different chrome: a\n// desktop with a mouse and a left rail, a phone held in one hand, and a TV across the\n// room driven by a d-pad. This is the one place that decides which, so components ask\n// `state.vp.mode` instead of each inventing its own breakpoint — and so a single\n// override flips the whole shell at once.\n//\n// Detection is a guess, and guesses about TVs are especially bad (a browser on a set-top\n// box reports a large screen and, often, a mouse it does not have). So the guess is only\n// the default: `?ui=phone` in the URL, or the `workbench.layout` setting, wins outright.\n// Someone whose TV we mis-detect can fix it in Settings rather than living with it.\n\nimport { cell, effect } from '../runtime.js';\n\nexport const LAYOUTS = ['auto', 'desktop', 'phone', 'tv'];\n\n// Below this, the left rail plus a panel leaves nothing for the panel.\nconst PHONE_MAX = 720;\n// A TV is a big screen you sit far away from. Only consulted alongside a UA hint —\n// width alone would call every large monitor a television.\nconst TV_MIN = 1100;\nconst TV_UA = /\\b(smart-?tv|smarttv|appletv|googletv|android\\s*tv|hbbtv|netcast|web0s|webos|tizen|viera|bravia|aquos|crkey|nettv|dtv|philipstv|roku|aft[a-z]{1,3})\\b/i;\n\n/** Does the user agent say, unprompted, that it is a television? */\nexport function looksLikeTv(ua = '', width = 0) {\n if (!ua) return false;\n return TV_UA.test(ua) && width >= TV_MIN;\n}\n\nexport class ViewportService {\n /**\n * @param {object} [deps]\n * @param {Window} [deps.window] the window to measure and listen to\n * @param {import('./settings.js').SettingsService} [deps.settings]\n * @param {import('./context.js').ContextKeyService} [deps.context]\n */\n constructor({ window: win = globalThis, settings = null, context = null } = {}) {\n this.window = win;\n this.settings = settings;\n this.context = context;\n // A URL override outranks the setting: it is how someone checks the phone layout on\n // a laptop, and how the e2e suite drives each shell without a device farm.\n this.urlOverride = readUrlOverride(win);\n this.state = this.#measure();\n this.cell = cell(this.state);\n this._onResize = () => this.refresh();\n }\n\n observe() {\n return this.cell;\n }\n\n /** Start listening. Separate from the constructor so tests can measure without hooks. */\n install() {\n this.window.addEventListener?.('resize', this._onResize);\n this.window.addEventListener?.('orientationchange', this._onResize);\n // `effect` runs now and on every change. The old form was\n // `settings.observe().subscribe?.(…)` — optional-chained, so when `observe()`\n // stopped returning something with `.subscribe` it would have become a silent\n // no-op and the viewport would have stopped following its own setting.\n if (this.settings) effect(this.settings.observe(), () => this.refresh());\n this.#publish(this.state);\n return this;\n }\n\n dispose() {\n this.window.removeEventListener?.('resize', this._onResize);\n this.window.removeEventListener?.('orientationchange', this._onResize);\n }\n\n refresh() {\n const next = this.#measure();\n const same = next.mode === this.state.mode && next.width === this.state.width\n && next.height === this.state.height && next.coarse === this.state.coarse;\n if (same) return;\n this.state = next;\n this.#publish(next);\n this.cell.setValue(next);\n }\n\n #publish(vp) {\n // Context keys so plugin `when` clauses and keybindings can target a form factor\n // the same way they target a view.\n this.context?.set('viewport.mode', vp.mode);\n this.context?.set('viewport.phone', vp.mode === 'phone');\n this.context?.set('viewport.tv', vp.mode === 'tv');\n // The root element carries it too, so CSS can respond without every rule needing a\n // media query that would disagree with the JS branch above it.\n const el = this.window.document?.documentElement;\n if (el) el.dataset.layout = vp.mode;\n }\n\n #measure() {\n const win = this.window;\n const width = win.innerWidth || 1280;\n const height = win.innerHeight || 800;\n const coarse = matches(win, '(pointer: coarse)');\n const ua = win.navigator?.userAgent || '';\n const forced = this.urlOverride || this.settings?.get?.('workbench.layout') || 'auto';\n const mode = forced !== 'auto' && LAYOUTS.includes(forced) ? forced : detect({ width, coarse, ua });\n return { mode, width, height, coarse, forced: forced !== 'auto' };\n }\n}\n\nfunction detect({ width, coarse, ua }) {\n if (looksLikeTv(ua, width)) return 'tv';\n // Narrow is a phone whether or not the pointer is coarse — a desktop window dragged\n // small has the same problem a phone does, and the layout that fits one fits the other.\n if (width <= PHONE_MAX) return 'phone';\n return 'desktop';\n}\n\nfunction matches(win, query) {\n try { return !!win.matchMedia?.(query)?.matches; } catch { return false; }\n}\n\nfunction readUrlOverride(win) {\n try {\n const value = new URL(win.location.href).searchParams.get('ui');\n return value && LAYOUTS.includes(value) ? value : null;\n } catch { return null; }\n}\n",
|
|
47
|
+
"// Driving the drive with a remote control.\n//\n// A TV has four arrows, an OK button, and a Back button. It has no pointer and no Tab\n// key, so a layout that is perfectly usable with a mouse can be completely unreachable\n// from a sofa: focus lands somewhere, the arrows do nothing, and there is no way out.\n//\n// This maps the arrows onto GEOMETRY rather than DOM order. Pressing right should move\n// to the thing that is visually to the right, which is what someone holding a remote\n// expects and what tab order — written for reading order — does not give. Two rules keep\n// it from fighting the rest of the app:\n//\n// 1. If something already handled the key (the launcher's own list navigation, a text\n// field's caret), it is left alone. `defaultPrevented` is the contract.\n// 2. Anything the app made clickable is reachable, whether or not the browser thinks\n// it is focusable. A div with a click handler is a destination on a TV; the\n// alternative is a list of files that cannot be opened.\n//\n// Only installed when the layout is 'tv'. On a desktop the pointer and Tab already work,\n// and remapping the arrow keys there would break scrolling.\n\n// Natively focusable, plus the app's own clickable rows. Keeping this list explicit\n// (rather than \"anything with a click handler\", which the DOM cannot tell us) means a\n// new clickable component is opted in deliberately.\nimport { effect } from '../runtime.js';\n\nconst NATIVE = 'a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n// `.setting-row` used to be listed here and has never existed — the rendered class is\n// `.setting`, and it isn't clickable anyway (its control is). Every entry below is\n// checked by probe15-tv-reach, so a renamed class fails a test instead of silently\n// dropping a destination.\nconst APP_CLICKABLE = '.launch-item, .grid-tile, .sheet-row, .inbox-item, .plugin-card, .act-task, .act-issue, .chapter';\nconst SELECTOR = `${NATIVE}, ${APP_CLICKABLE}`;\n\n// Keys a television sends for \"back\". The named ones are the web standard; the numbers\n// are Tizen (Samsung) and webOS (LG), which predate it and still ship.\nconst BACK_KEYS = new Set(['BrowserBack', 'GoBack', 'Backspace']);\nconst BACK_CODES = new Set([10009, 461]);\n\nconst DIRS = { ArrowLeft: 'left', ArrowRight: 'right', ArrowUp: 'up', ArrowDown: 'down' };\n// Elements the browser already fires a click on when Enter is pressed.\nconst ACTIVATES_ITSELF = new Set(['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA']);\n\nexport class SpatialNavigationService {\n /**\n * @param {object} deps\n * @param {import('./workbench.js').WorkbenchService} deps.workbench\n * @param {import('./viewport.js').ViewportService} deps.viewport\n * @param {Window} [deps.window]\n */\n constructor({ workbench, viewport, window: win = globalThis }) {\n this.workbench = workbench;\n this.viewport = viewport;\n this.window = win;\n this.active = false;\n this._onKey = (e) => this.handleKey(e);\n this._observer = null;\n this._pending = 0;\n this._bootstrapped = false;\n }\n\n /** Follow the viewport: arrows are remapped only while the TV layout is showing. */\n install() {\n if (this.viewport) effect(this.viewport.observe(), (vp) => this.setActive(vp.mode === 'tv'));\n this.setActive(this.viewport?.state?.mode === 'tv');\n return this;\n }\n\n setActive(on) {\n if (on === this.active) return;\n this.active = on;\n // Bubble phase, so a component that wants the key gets it first and this only sees\n // what nothing else claimed.\n if (on) {\n this.window.addEventListener?.('keydown', this._onKey);\n this.#watchDom();\n // The first frame may not have rendered yet; a TV with nothing focused is a TV\n // where the first arrow press has no origin and appears to do nothing at all.\n this.prime();\n this.window.setTimeout?.(() => this.prime(), 60);\n } else {\n this.window.removeEventListener?.('keydown', this._onKey);\n this._observer?.disconnect();\n this._observer = null;\n this._bootstrapped = false;\n }\n }\n\n /**\n * Make everything clickable focusable, and make sure something IS focused.\n *\n * Both halves matter. A div with a click handler is a destination on a TV but the\n * browser will not focus it without a tabindex, and the app re-renders constantly, so\n * this has to run again after every render rather than once at startup.\n */\n prime() {\n this.candidates(); // stamps tabindex as a side effect\n this.focusFirst();\n }\n\n #watchDom() {\n const Obs = this.window.MutationObserver;\n if (!Obs || this._observer) return;\n // Coalesced to one pass per frame: the workbench replaces whole subtrees on a\n // re-render, and stamping per mutation would be thousands of calls per second.\n this._observer = new Obs(() => {\n if (this._pending) return;\n this._pending = this.window.requestAnimationFrame?.(() => {\n this._pending = 0;\n if (this.active) this.prime();\n }) || 0;\n });\n this._observer.observe(this.window.document.body, { childList: true, subtree: true });\n }\n\n handleKey(e) {\n if (!this.active || e.defaultPrevented || e.altKey || e.ctrlKey || e.metaKey) return;\n const doc = this.window.document;\n const from = doc.activeElement;\n\n if (BACK_KEYS.has(e.key) || BACK_CODES.has(e.keyCode)) {\n // Backspace with something to delete is a backspace. Getting this wrong throws\n // away what someone typed on an on-screen keyboard. But an EMPTY field has nothing\n // to delete, and a remote whose only Back button silently does nothing while a\n // search box happens to hold focus is a remote that can't leave the screen.\n if (e.key === 'Backspace' && isTextEntry(from) && (from.value ?? '').length > 0) return;\n e.preventDefault();\n this.goBack();\n return;\n }\n\n const dir = DIRS[e.key];\n if (!dir) {\n // Enter on a real button already activates it. Enter on one of the app's clickable\n // divs does nothing at all unless we say so — and note the test is the TAG, not\n // \"is it focusable\": we stamped a tabindex on those divs ourselves, so asking\n // whether they look focusable would answer yes for every one of them and OK would\n // silently do nothing on every file in the list.\n if (e.key === 'Enter' && from && !ACTIVATES_ITSELF.has(from.tagName) && from.matches(APP_CLICKABLE)) {\n e.preventDefault();\n from.click();\n }\n return;\n }\n // Inside a text field, left and right move the caret — until the caret runs out of\n // text. Then the next press leaves the field. Without the second half the search box\n // is a trap: focus goes in and the only way out is a key a remote doesn't have.\n if ((dir === 'left' || dir === 'right') && isTextEntry(from) && !atTextEdge(from, dir)) return;\n\n // A re-render can drop the focused node, leaving focus on <body> with no origin to\n // navigate from. Any arrow press should recover rather than appear to do nothing.\n const origin = from && from !== doc.body ? from : null;\n const next = this.find(dir, origin);\n if (!next) return;\n e.preventDefault();\n focusIt(next);\n }\n\n goBack() {\n // An open overlay is what \"back\" means while one is open. Only past that does back\n // mean the viewer stack, and only past THAT does it mean the browser.\n if (this.workbench?.closeOverlays()) return;\n this.window.history?.back?.();\n }\n\n /** Every candidate currently on screen, in whatever order the DOM gives them. */\n candidates() {\n const doc = this.window.document;\n const out = [];\n for (const el of doc.querySelectorAll(SELECTOR)) {\n if (el.getAttribute?.('aria-hidden') === 'true' || el.hasAttribute?.('disabled')) continue;\n const r = el.getBoundingClientRect();\n if (r.width < 2 || r.height < 2) continue;\n // Off-screen is not a destination — a remote can't scroll to something it can't\n // move to, so anything outside the viewport is skipped until it scrolls in.\n if (r.bottom < 0 || r.top > this.window.innerHeight || r.right < 0 || r.left > this.window.innerWidth) continue;\n // The app's clickable rows aren't focusable until we make them so. Doing it here,\n // lazily, keeps tabindex out of every component for a mode most users never see.\n if (!el.matches(NATIVE) && !el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');\n out.push({ el, rect: r });\n }\n return out;\n }\n\n /** The best thing to move to from `from` in `dir`, or null if there isn't one. */\n find(dir, from) {\n const all = this.candidates();\n if (!all.length) return null;\n const origin = from && from.getBoundingClientRect && from.getBoundingClientRect().width\n ? from.getBoundingClientRect()\n : null;\n if (!origin) return all[0].el;\n\n let best = null;\n let bestScore = Infinity;\n for (const { el, rect } of all) {\n if (el === from) continue;\n const score = scoreCandidate(origin, rect, dir);\n if (score == null || score >= bestScore) continue;\n bestScore = score;\n best = el;\n }\n return best;\n }\n\n /**\n * Put focus somewhere sensible, once.\n *\n * Deliberately only ONCE. Every re-render replaces DOM nodes and drops focus to\n * <body>, and re-running this each time would repeatedly yank the selection back to\n * the top of the screen while someone is halfway down a list. Recovering from lost\n * focus is the arrow handler's job instead — it navigates from the first candidate\n * when there is no origin, so a press always does something.\n */\n focusFirst() {\n if (this._bootstrapped) return;\n const doc = this.window.document;\n if (doc?.activeElement && doc.activeElement !== doc.body) return;\n const all = this.candidates();\n if (!all.length) return;\n // Search is what the drive is for, so that is where a remote should start.\n const target = all.find((c) => c.el.classList?.contains('launch-input')) || all[0];\n focusIt(target.el);\n this._bootstrapped = true;\n }\n}\n\n/** Is the caret already at the end it would be moving toward? */\nfunction atTextEdge(el, dir) {\n const start = el.selectionStart;\n const end = el.selectionEnd;\n // contenteditable and some input types report nothing; treat \"can't tell\" as at the\n // edge, since being unable to leave is the worse failure.\n if (start == null || end == null) return true;\n if (start !== end) return false; // a selection: the arrow collapses it\n return dir === 'left' ? start === 0 : end >= (el.value ?? '').length;\n}\n\nfunction isTextEntry(el) {\n if (!el) return false;\n const tag = el.tagName;\n if (tag === 'TEXTAREA') return true;\n if (el.isContentEditable) return true;\n if (tag !== 'INPUT') return false;\n return !['checkbox', 'radio', 'button', 'submit', 'range', 'file'].includes(el.type);\n}\n\nfunction focusIt(el) {\n el.focus?.({ preventScroll: true });\n // A TV screen is mostly not where your eye is; scrolling the target to the middle is\n // what makes a long list navigable without the selection hugging an edge.\n el.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });\n}\n\n/**\n * How good a move is, lower being better — or null if the candidate isn't in that\n * direction at all.\n *\n * Two distances matter and they are not equal. How far it is ALONG the direction of\n * travel is the honest cost. How far it is OFF to the side is worse than it looks: a\n * candidate that is 10px further right but 400px down is not \"to the right\" in any sense\n * that matters to someone pressing right, so the sideways distance is weighted heavily.\n */\nfunction scoreCandidate(from, to, dir) {\n const vertical = dir === 'up' || dir === 'down';\n // Near edges along the axis of travel.\n const [fromNear, toNear] = vertical\n ? (dir === 'down' ? [from.bottom, to.top] : [from.top, to.bottom])\n : (dir === 'right' ? [from.right, to.left] : [from.left, to.right]);\n const forward = dir === 'down' || dir === 'right';\n const along = forward ? toNear - fromNear : fromNear - toNear;\n // A tolerance, so items in the same row that overlap by a pixel or two still count.\n if (along < -2) return null;\n\n // Overlap on the cross axis is what makes something feel \"in line\". Where they overlap\n // the sideways cost is zero; where they don't, it is the gap between them.\n const [fs, fe, ts, te] = vertical\n ? [from.left, from.right, to.left, to.right]\n : [from.top, from.bottom, to.top, to.bottom];\n const aside = Math.max(0, Math.max(fs, ts) - Math.min(fe, te));\n // Also break ties by centre alignment, so a wide row doesn't beat the one directly\n // under the cursor just because they both overlap.\n const centerGap = Math.abs((fs + fe) / 2 - (ts + te) / 2);\n return Math.max(0, along) + aside * 3 + centerGap * 0.2;\n}\n\nexport { scoreCandidate };\n",
|
|
48
|
+
"// Dictating into the search box.\n//\n// THE REMOTE'S MIC BUTTON IS NOT OURS. On every TV platform it belongs to the system:\n// webOS's SDK exposes no way for an app to listen to the Magic Remote, Tizen's voice\n// control is a packaged-app API that does not exist for a page in the TV browser, and\n// on Android TV the button raises Assistant. No key event reaches the document, so\n// there is nothing to intercept and no shortcut to bind.\n//\n// What the button DOES do is dictate into a focused text field, through the platform's\n// own keyboard. That is the whole opportunity: the mic is useful exactly when there is\n// somewhere for its text to land. `search.voice` therefore does not try to capture a\n// button — it makes sure the search field is open and focused, so the mic on the remote\n// has a target. That half works on every TV, with no API at all.\n//\n// This module is the OTHER half, for browsers that can transcribe themselves (Chrome,\n// so Android TV and the desktop). It is deliberately narrow:\n//\n// ON-DEVICE ONLY. By default `SpeechRecognition` streams audio to the browser\n// vendor's servers. Trove is a drive you host so that your files stay yours, and\n// shipping a microphone that quietly forwards your voice to a third party would\n// contradict the entire point of it. `processLocally` asks for recognition that never\n// leaves the machine; a browser that cannot promise that is simply reported as\n// unsupported, and the button is never offered.\n\n/**\n * Resolved per call, not captured at import.\n *\n * A module-level constant would freeze the answer at load time, which is wrong twice\n * over: the page may gain the API later (a polyfill, a flag flipped behind a restart),\n * and a test could never stand in for it. Reading it when asked costs a property lookup.\n */\nfunction impl() {\n if (typeof window === 'undefined') return null;\n return window.SpeechRecognition || window.webkitSpeechRecognition || null;\n}\n\n/**\n * Whether this browser can transcribe WITHOUT sending audio anywhere.\n *\n * `available()` is the on-device half of the API; a browser that lacks it has no notion\n * of local recognition, which for us is the same as having no recognition. Detecting\n * the constructor alone would be the bug: every Chrome has that, and most of them would\n * happily record you into a datacentre.\n */\nexport function canTranscribeLocally() {\n const Impl = impl();\n return !!Impl && typeof Impl.available === 'function';\n}\n\nconst optionsFor = (lang) => ({ langs: [lang], processLocally: true });\n\n/**\n * `'available' | 'downloadable' | 'downloading' | 'unavailable'`\n *\n * A language pack that is merely `downloadable` is not usable yet, and downloading one\n * unasked would spend someone's bandwidth on a button they have not pressed.\n */\nexport async function localAvailability(lang = navigator.language || 'en-US') {\n if (!canTranscribeLocally()) return 'unavailable';\n try {\n return await impl().available(optionsFor(lang));\n } catch {\n return 'unavailable';\n }\n}\n\n/** Fetch the on-device language pack. Only ever called from an explicit user action. */\nexport async function installLocal(lang = navigator.language || 'en-US') {\n if (typeof impl()?.install !== 'function') return false;\n try {\n return await impl().install(optionsFor(lang));\n } catch {\n return false;\n }\n}\n\n/**\n * Listen, and report what was heard.\n *\n * `onText(text, { final })` fires for interim results too, so the query updates as\n * someone speaks rather than landing in one lump at the end — on a TV that feedback is\n * the difference between \"it is listening\" and \"did it hear me\".\n *\n * @returns {{stop: () => void}}\n */\nexport function listen({ lang = navigator.language || 'en-US', onText, onEnd, onError } = {}) {\n if (!canTranscribeLocally()) throw new Error('On-device speech recognition is not available here');\n const rec = new (impl())();\n rec.lang = lang;\n rec.interimResults = true;\n rec.continuous = false;\n // Both spellings: `options` is the shape the explainer settled on, and shipping\n // builds also read the flag straight off the instance. Setting one the browser\n // ignores is harmless; setting neither would mean asking for local processing and\n // silently getting the remote kind.\n rec.options = optionsFor(lang);\n rec.processLocally = true;\n\n let stopped = false;\n rec.onresult = (event) => {\n let text = '';\n let final = false;\n for (let i = event.resultIndex; i < event.results.length; i++) {\n text += event.results[i][0].transcript;\n if (event.results[i].isFinal) final = true;\n }\n onText?.(text.trim(), { final });\n };\n // `no-speech` and `aborted` are what happens when someone changes their mind. They\n // are not failures worth a toast.\n rec.onerror = (event) => {\n if (event.error === 'no-speech' || event.error === 'aborted') return;\n onError?.(new Error(event.error || 'Speech recognition failed'));\n };\n rec.onend = () => { if (!stopped) { stopped = true; onEnd?.(); } };\n\n try {\n rec.start();\n } catch (err) {\n stopped = true;\n onError?.(err);\n onEnd?.();\n }\n\n return {\n stop() {\n if (stopped) return;\n stopped = true;\n try { rec.stop(); } catch { /* already finished */ }\n onEnd?.();\n },\n };\n}\n",
|
|
49
|
+
"// The \"speak to search\" surface, and the part of it that works with no API at all.\n//\n// Two jobs, and they are independent on purpose:\n//\n// 1. PUT THE SEARCH FIELD UNDER THE MICROPHONE. A TV remote's mic dictates into\n// whatever text field the platform keyboard is attached to, and does nothing at\n// all when there isn't one. So the command opens the search surface and focuses\n// its input. On webOS and Tizen that is the entire feature — the platform does\n// the listening, the text arrives as ordinary input events, and Trove never asks\n// for a microphone.\n//\n// 2. TRANSCRIBE OURSELVES, where the browser can do it on-device (see voice.js).\n// Additive: if it isn't available the field is still focused and the remote's own\n// mic still works.\n\nimport { cell } from '../runtime.js';\nimport { canTranscribeLocally, localAvailability, installLocal, listen } from './voice.js';\n\nexport class VoiceSearchService {\n /**\n * @param {object} deps\n * @param {import('./workbench.js').WorkbenchService} deps.workbench\n * @param {object} deps.notifications\n */\n constructor({ workbench, notifications, settings } = {}) {\n this.workbench = workbench;\n this.notifications = notifications;\n this.settings = settings;\n this.session = null;\n this.state = { supported: canTranscribeLocally(), status: 'unknown', listening: false };\n this.cell = cell(this.state);\n }\n\n observe() {\n return this.cell;\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n\n /** Ask once whether an on-device language pack is ready, and remember the answer. */\n async refresh() {\n if (!this.state.supported) return 'unavailable';\n const status = await localAvailability(this.#lang());\n this.#set({ status });\n return status;\n }\n\n #lang() {\n return this.settings?.get?.('search.voiceLanguage') || navigator.language || 'en-US';\n }\n\n /** Can we offer a microphone button right now? */\n canListen() {\n return this.state.supported && (this.state.status === 'available' || this.state.status === 'downloadable');\n }\n\n /**\n * The command behind `search.voice`.\n *\n * `focus` is what makes this worth binding on a TV even when we cannot transcribe:\n * it is the difference between the remote's mic having a target and being swallowed\n * by the platform assistant.\n */\n async run({ onText } = {}) {\n this.openSearchSurface();\n // Focus AFTER the surface has rendered — the input does not exist yet on the frame\n // that opened it.\n await this.focusInput();\n if (!this.state.supported) return { listening: false, reason: 'unsupported' };\n\n const status = this.state.status === 'unknown' ? await this.refresh() : this.state.status;\n if (status === 'downloadable') {\n // Pressing the button IS the consent to fetch it; nothing downloads before that.\n this.notifications?.info('Downloading the on-device voice model — this happens once.');\n const ok = await installLocal(this.#lang());\n this.#set({ status: ok ? 'available' : 'unavailable' });\n if (!ok) return { listening: false, reason: 'install-failed' };\n } else if (status !== 'available') {\n return { listening: false, reason: status };\n }\n return this.toggle({ onText });\n }\n\n /** Start listening, or stop if already. */\n toggle({ onText } = {}) {\n if (this.session) {\n this.stop();\n return { listening: false };\n }\n try {\n this.session = listen({\n lang: this.#lang(),\n onText,\n onEnd: () => { this.session = null; this.#set({ listening: false }); },\n onError: (err) => {\n this.session = null;\n this.#set({ listening: false });\n // A refused microphone is a decision, not a fault worth shouting about.\n if (!/not-allowed|service-not-allowed|denied/i.test(err.message)) {\n this.notifications?.warn(`Couldn't listen: ${err.message}`);\n }\n },\n });\n this.#set({ listening: true });\n return { listening: true };\n } catch (err) {\n this.notifications?.warn(`Couldn't listen: ${err.message}`);\n return { listening: false, reason: 'error' };\n }\n }\n\n stop() {\n this.session?.stop();\n this.session = null;\n this.#set({ listening: false });\n }\n\n /**\n * Make sure a search field is on screen.\n *\n * Already on the launcher with nothing open → it is the search surface, use it. Any\n * other view, or a file open over it → the modal, which is the one search box that\n * can appear over anything.\n */\n openSearchSurface() {\n const wb = this.workbench;\n if (!wb) return;\n const onLauncher = wb.state.activity === 'home' && !wb.nav?.state?.activeTabId;\n if (onLauncher) wb.closeSearchModal?.();\n else wb.openSearchModal();\n }\n\n /** @returns {Promise<boolean>} whether an input actually took focus */\n focusInput({ tries = 12 } = {}) {\n return new Promise((resolve) => {\n let left = tries;\n const attempt = () => {\n const el = document.querySelector('.search-modal .launch-input') || document.querySelector('.launch-input');\n if (el) {\n el.focus();\n // Put the caret at the end so dictation appends rather than overwriting a\n // query someone already typed.\n try { el.setSelectionRange(el.value.length, el.value.length); } catch { /* not a text input */ }\n resolve(document.activeElement === el);\n return;\n }\n if (--left <= 0) { resolve(false); return; }\n requestAnimationFrame(attempt);\n };\n attempt();\n });\n }\n}\n",
|
|
50
|
+
"// URLs for things that cannot send a header.\n//\n// An `<img src>`, a `<video src>` and `cache.add()` all fetch without our Authorization\n// header, so on a token-authenticated deployment they 401 and the preview shows a\n// fallback. The server can mint a URL that carries its own grant (see\n// docs/design/signed-urls.md); this is the client half.\n//\n// Two things it exists to get right:\n//\n// BATCHING — a gallery draws hundreds of tiles at once. Per-object signing is right for\n// scoping and hopeless as one round trip each, so ids asked for in the same tick go out\n// together.\n//\n// EXPIRY — a minted URL dies. Callers get `expiresAt` alongside and are expected to\n// cycle (see ui/media.js); this service will not hand back a URL that is about to\n// expire, so a caller that re-asks always gets something usable.\n\n// Re-mint at 80% of the remaining life rather than at the end: a URL handed out at 99%\n// of its life is one the caller will be back for almost immediately.\nconst FRESH_ENOUGH = 0.8;\n// Ids asked for within this window go out as one request. A frame is 16ms; this is long\n// enough to collect a grid's first paint and short enough not to be felt.\nconst BATCH_MS = 20;\nconst MAX_BATCH = 200;\n\nexport class MediaUrlService {\n /** @param {{api: object, settings?: object}} deps */\n constructor({ api, settings = null }) {\n this.api = api;\n this.settings = settings;\n this.cache = new Map(); // `${op}:${id}` -> { url, expiresAt }\n this._queue = new Map(); // op -> Map(id -> {resolve, reject})\n this._timer = null;\n }\n\n /**\n * Whether URLs need minting at all.\n *\n * With no bearer token the browser authenticates itself — a cookie or a proxy header\n * rides along on a subresource load — so the plain route URL works, needs no round\n * trip, and stays stable enough to be a cache key. Minting there would be a cost with\n * nothing bought. This is the one branch, and it lives here so callers have none.\n *\n * `media.signedUrls` overrides the guess: a deployment behind a proxy that still wants\n * media fetched straight from S3 sets `always`.\n */\n get needed() {\n const mode = this.settings?.get('media.signedUrls');\n if (mode === 'always') return true;\n if (mode === 'never') return false;\n return !!this.api.token?.();\n }\n\n /**\n * A stable identifier for this node's bytes.\n *\n * NOT the URL to fetch — a minted one carries a signature and changes every time. The\n * offline cache keys on this, because a key that changes per mint means `unpin` stops\n * matching what `pin` stored and the bytes are never reclaimed.\n */\n cacheKey(id) {\n return this.api.downloadUrl(id);\n }\n\n /**\n * A URL for this node's bytes, good for a while.\n *\n * @returns {Promise<{url: string, expiresAt: number}>} `expiresAt` is Infinity when\n * nothing was minted, so a caller's cycling logic simply never fires.\n */\n async url(id, { op = 'media' } = {}) {\n if (!this.needed) return { url: this.api.downloadUrl(id, { attachment: op === 'download' }), expiresAt: Infinity };\n const key = `${op}:${id}`;\n const hit = this.cache.get(key);\n if (hit && this.#fresh(hit)) return hit;\n return this.#enqueue(id, op);\n }\n\n /** Forget what we hold for a node, so the next ask re-mints. */\n invalidate(id, op = 'media') {\n if (id == null) this.cache.clear();\n else this.cache.delete(`${op}:${id}`);\n }\n\n #fresh(entry) {\n if (entry.expiresAt === Infinity) return true;\n const life = entry.expiresAt - entry.mintedAt;\n return Date.now() < entry.mintedAt + life * FRESH_ENOUGH;\n }\n\n #enqueue(id, op) {\n if (!this._queue.has(op)) this._queue.set(op, new Map());\n const forOp = this._queue.get(op);\n const existing = forOp.get(id);\n if (existing) return existing.promise;\n\n let resolve;\n let reject;\n const promise = new Promise((res, rej) => { resolve = res; reject = rej; });\n forOp.set(id, { resolve, reject, promise });\n\n // Flush immediately at the cap rather than waiting out the window — a drive view that\n // asks for a thousand tiles should not hold the first two hundred hostage.\n if (forOp.size >= MAX_BATCH) this.#flush();\n else if (!this._timer) this._timer = setTimeout(() => this.#flush(), BATCH_MS);\n return promise;\n }\n\n async #flush() {\n clearTimeout(this._timer);\n this._timer = null;\n const queue = this._queue;\n this._queue = new Map();\n\n for (const [op, waiting] of queue) {\n const ids = [...waiting.keys()];\n try {\n const res = await this.api.mintUrls(ids, op);\n const now = Date.now();\n for (const [id, w] of waiting) {\n const got = res.urls?.[id];\n if (got) {\n const entry = { url: got.url, expiresAt: got.expiresAt, mintedAt: now };\n this.cache.set(`${op}:${id}`, entry);\n w.resolve(entry);\n } else {\n // Absent from the answer means this caller may not read it. A rejection, not\n // a broken URL — the tile shows its icon rather than a broken-image glyph.\n w.reject(new Error(res.failed?.[id] === 'not_found' ? 'That file is gone' : 'You cannot open that file'));\n }\n }\n } catch (err) {\n for (const w of waiting.values()) w.reject(err);\n }\n }\n }\n}\n",
|
|
51
|
+
"// createPlatform — assemble every service into one object the whole app shares.\n// This is the \"workbench platform\": the reactive registries and services that\n// core features and plugins both build on. It also registers the default\n// settings schema and keybindings so the shell has sensible behaviour out of the\n// box. UI-facing shell state (which activity is open, is the palette showing…)\n// lives in WorkbenchService; data and mutations go through ngin (see ../bl).\n\nimport { reactive } from '../runtime.js';\nimport { ContributionRegistry } from './contributions.js';\nimport { ContextKeyService } from './context.js';\nimport { CommandService } from './commands.js';\nimport { KeybindingService } from './keybindings.js';\nimport { SettingsService } from './settings.js';\nimport { NotificationService } from './notifications.js';\nimport { TroveApiClient } from './api.js';\nimport { PluginHost } from './pluginHost.js';\nimport { WorkbenchService } from './workbench.js';\nimport { ViewportService } from './viewport.js';\nimport { SpatialNavigationService } from './spatialNav.js';\nimport { VoiceSearchService } from './voiceSearch.js';\nimport { MediaUrlService } from './mediaUrls.js';\n\n/**\n * The bearer token for this browser, if any.\n *\n * `localStorage` rather than a cookie because the token is presented as an\n * Authorization header, not sent ambiently — which is what makes CSRF a non-issue here.\n * Wrapped in try/catch because storage access throws outright in some privacy modes,\n * and a drive that white-screens because it couldn't read a key that is usually absent\n * would be a poor trade.\n */\nexport function readToken() {\n try { return localStorage.getItem('trove.token') || null; } catch { return null; }\n}\n/** Store (or clear, with null) the bearer token this browser presents. */\nexport function writeToken(token) {\n try {\n if (token) localStorage.setItem('trove.token', token);\n else localStorage.removeItem('trove.token');\n } catch { /* storage unavailable; the session is simply not persisted */ }\n}\n\nexport function createPlatform({ baseUrl = '' } = {}) {\n const contributions = new ContributionRegistry();\n // 'home' is where the app actually starts (WorkbenchService's initial `activity`), and\n // nothing writes this key until the user navigates. Seeding it with a view that no\n // longer exists left every `when: view.active == 'home'` binding — the Delete\n // shortcut among them — dead from boot until the first click on the rail, while the\n // row menu cheerfully advertised \"Del\" as the way to delete.\n const context = new ContextKeyService({ 'view.active': 'home', 'sidebar.visible': true });\n const notifications = new NotificationService();\n const commands = new CommandService(contributions, context, notifications);\n const settings = new SettingsService();\n const keybindings = new KeybindingService(contributions, commands, context, settings);\n // A bearer token, when this deployment uses one. Most don't: an authenticating proxy\n // (Cloudflare Access, oauth2-proxy) sets its own header and the browser sends nothing.\n // But a deployment where the user HOLDS a token has no other way to present it, and\n // reading it here — once, from one place — keeps that out of every call site.\n const api = new TroveApiClient({ baseUrl, token: () => readToken() });\n const workbench = new WorkbenchService(context);\n // Which shell to render — phone, desktop, or TV. Constructed before the defaults are\n // registered, so it reads the setting through `settings.get` once that exists.\n const viewport = new ViewportService({ settings, context });\n // Arrow keys → geometry, but only on a TV. Inert everywhere else.\n const spatialNav = new SpatialNavigationService({ workbench, viewport });\n // Speak to search. Mostly this just puts the search field under the remote's mic —\n // see voiceSearch.js for why that is the whole feature on a TV.\n const voice = new VoiceSearchService({ workbench, notifications, settings });\n\n const platform = {\n reactive,\n contributions, context, commands, keybindings, settings, notifications, api, workbench,\n viewport, spatialNav, voice,\n capabilities: null,\n openPluginPanel: null, // set by the workbench UI\n };\n platform.mediaUrls = new MediaUrlService({ api: platform.api, settings });\n platform.plugins = new PluginHost(platform);\n // Commands consult the plugin host to hide/disable plugin commands that aren't\n // available right now (offline, or the plugin isn't responding).\n commands.availability = (cmd) => platform.plugins.isAvailable(cmd);\n\n registerDefaults(platform);\n // Defaults are in place now, so re-measure: `workbench.layout` was unreadable a moment\n // ago and a forced layout must not need a resize to take effect.\n viewport.refresh();\n return platform;\n}\n\nfunction registerDefaults(p) {\n // --- default settings schema ----------------------------------------------\n p.settings.register([\n { key: 'workbench.theme', type: 'enum', enum: ['dark', 'light', 'midnight'], enumLabels: ['Dark', 'Light', 'Midnight'], default: 'dark', title: 'Color theme', category: 'Appearance', order: 1 },\n { key: 'workbench.density', type: 'enum', enum: ['comfortable', 'compact'], default: 'comfortable', title: 'List density', category: 'Appearance', order: 2 },\n // Auto gets it right for phones and desktops. TVs are the reason this is a setting:\n // a browser on a set-top box usually looks like a large desktop, so someone using\n // one with a remote needs a way to say so.\n { key: 'workbench.layout', type: 'enum', enum: ['auto', 'desktop', 'phone', 'tv'],\n enumLabels: ['Automatic', 'Desktop', 'Phone', 'TV / remote'], default: 'auto',\n title: 'Layout', description: 'Automatic follows the screen size. Choose TV for d-pad remote navigation.',\n category: 'Appearance', order: 3 },\n { key: 'explorer.sort', type: 'enum', enum: ['name', 'size', 'updatedAt'], enumLabels: ['Name', 'Size', 'Modified'], default: 'name', title: 'Sort files by', category: 'Explorer', order: 1 },\n { key: 'explorer.sortOrder', type: 'enum', enum: ['asc', 'desc'], default: 'asc', title: 'Sort order', category: 'Explorer', order: 2 },\n { key: 'explorer.confirmDelete', type: 'boolean', default: true, title: 'Confirm before deleting', category: 'Explorer', order: 3 },\n { key: 'search.mode', type: 'enum', enum: ['hybrid', 'semantic', 'keyword'], default: 'hybrid', title: 'Search mode', description: 'Hybrid blends semantic meaning with keyword matches.', category: 'Search', order: 1 },\n { key: 'uploads.concurrency', type: 'number', minimum: 1, maximum: 8, default: 4, title: 'Parallel upload parts', category: 'Transfers', order: 1 },\n // Which viewer opens which file type. When several openers match a file and there\n // is no entry here, the user is asked (and can save their answer) — see bl/openers.\n // Markdown ships with an answer already: it matches both the markdown renderer and\n // the plain-text viewer, and since documents are how things are grouped now,\n // prompting on every first .md would be friction over a decision that's obvious.\n // It stays changeable in Settings, and the text viewer is still reachable through\n // the opener switcher.\n { key: 'openers.associations', type: 'object', hidden: true, title: 'Default viewers',\n default: { '.md': 'core.markdown', '.markdown': 'core.markdown' } },\n // How results are drawn (see ui/components/views). Hidden and with no default on\n // purpose: unset means \"you decide\", and the launcher then offers the grid by itself\n // for a collection full of pictures. Picking one in the switcher pins it.\n { key: 'explorer.view', type: 'string', hidden: true, title: 'Results view' },\n // Whether media fetches use a URL that carries its own grant (see\n // platform/mediaUrls.js). `auto` mints only where the browser cannot authenticate\n // itself, which is the right answer almost everywhere — hence hidden.\n { key: 'media.signedUrls', type: 'enum', enum: ['auto', 'always', 'never'], default: 'auto',\n hidden: true, title: 'Signed media URLs' },\n ]);\n\n // --- the built-in keymap (commands themselves are registered in ../bl) -----\n // One `keymap` contribution, exactly like a plugin's — the host has no privileged\n // path into the keybinding service.\n p.contributions.register('keymap.default', {\n type: 'keymap',\n bindings: [\n { key: 'mod+shift+p', command: 'workbench.showCommandPalette' },\n { key: 'mod+p', command: 'workbench.quickOpen' },\n { key: 'mod+shift+f', command: 'workbench.view.home' },\n { key: 'mod+shift+e', command: 'workbench.view.home' },\n { key: 'mod+,', command: 'workbench.openSettings' },\n { key: 'mod+shift+l', command: 'explorer.copyLink' },\n { key: 'mod+u', command: 'explorer.upload' },\n { key: 'delete', command: 'explorer.delete', when: \"view.active == 'home' && explorer.hasSelection\" },\n { key: 'escape', command: 'workbench.closeOverlays' },\n { key: 'f5', command: 'explorer.refresh' },\n { key: 'mod+shift+i', command: 'workbench.toggleInfoPanel' },\n // Some remotes and keyboards send a dedicated search key; where they do, it lands\n // on the one command that opens the search field ready for dictation.\n { key: 'mod+shift+v', command: 'search.voice' },\n { key: 'browsersearch', command: 'search.voice' },\n ],\n });\n}\n",
|
|
52
|
+
"// Shared internals. Not part of the public API.\n\n// Reports an error that has nowhere to go: one thrown by an observer callback,\n// by a teardown routine, or by a provider being disposed. Funnelled through a\n// single helper so every \"swallowed\" error surfaces the same way.\nexport function report(error) {\n console.error(error);\n}\n",
|
|
53
|
+
"// Layer 1: dependency injection.\n//\n// This module is self contained: it knows nothing about actions or queries.\n// Import it on its own if all you want is providers and a container.\n\nimport { report } from './internal.js';\n\nconst DISPOSED = 'The provider has been disposed';\n\n// The base class for all providers, which manage external resources.\nexport class Provider {\n static deps = [];\n\n async obtain(options = {}) {\n throw new Error('obtain() not implemented');\n }\n\n release(resource, options = {}) {}\n\n async flush() {}\n\n // The `create`, `destroy` and `dispose` callbacks below are handed the\n // dependency *providers*, not their resources -- the same thing a provider\n // written by hand gets in its constructor. That is deliberate: a resource\n // built by `create` usually outlives the call, so it has to be able to hold\n // a dependency for its own lifetime (obtain in `create`, release in\n // `destroy`). Handing over an already-obtained resource would release it\n // out from under the thing still using it.\n\n // Creates a provider that manages a single shared resource (singleton).\n static fromSingleton(resource, { dispose, deps = [] } = {}) {\n return class SingletonProvider extends Provider {\n static deps = deps;\n\n #deps;\n #disposed = false;\n\n constructor(injectedDeps = {}) {\n super();\n this.#deps = injectedDeps;\n }\n\n async obtain() {\n return resource;\n }\n\n release() {\n // Nothing to do for a singleton.\n }\n\n async dispose() {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n\n if (typeof dispose === 'function') {\n await dispose(resource, this.#deps);\n }\n }\n };\n }\n\n // Creates a provider that manages a pool of at most `size` resources.\n //\n // Every `obtain` joins a FIFO queue which is drained against the available\n // resources and the unused slots, so concurrent obtains can never overshoot\n // `size`, and a failed `create` hands the failure to the caller that is\n // actually waiting on it instead of deadlocking the queue.\n static fromPool(create, destroy, { size = 1, deps = [] } = {}) {\n if (!Number.isInteger(size) || size < 1) {\n throw new RangeError('fromPool() requires an integer size of at least 1');\n }\n\n return class PoolProvider extends Provider {\n static deps = deps;\n\n #deps;\n #available = [];\n #inUse = new Set();\n #waiting = [];\n #slots = 0;\n #disposed = false;\n\n constructor(injectedDeps = {}) {\n super();\n this.#deps = injectedDeps;\n }\n\n async #destroySafely(resource) {\n try {\n await destroy(resource, this.#deps);\n } catch (err) {\n report(err);\n }\n }\n\n // Hands resources to waiters, creating new ones while slots remain.\n #pump() {\n while (this.#waiting.length > 0) {\n if (this.#available.length > 0) {\n const resource = this.#available.shift();\n this.#inUse.add(resource);\n this.#waiting.shift().resolve(resource);\n continue;\n }\n\n if (this.#slots >= size) {\n return;\n }\n\n const waiter = this.#waiting.shift();\n this.#slots++;\n // The slot is reserved before awaiting, so a concurrent obtain sees\n // it as taken and the pool never exceeds `size`.\n Promise.resolve()\n .then(() => create(this.#deps))\n .then(\n (resource) => {\n if (this.#disposed) {\n this.#slots--;\n this.#destroySafely(resource);\n waiter.reject(new Error(DISPOSED));\n return;\n }\n this.#inUse.add(resource);\n waiter.resolve(resource);\n },\n (err) => {\n this.#slots--;\n waiter.reject(err);\n this.#pump();\n }\n );\n }\n }\n\n async obtain() {\n if (this.#disposed) {\n throw new Error(DISPOSED);\n }\n\n const waiter = Promise.withResolvers();\n this.#waiting.push(waiter);\n this.#pump();\n return await waiter.promise;\n }\n\n release(resource) {\n if (!this.#inUse.delete(resource)) {\n return;\n }\n\n if (this.#disposed) {\n this.#slots--;\n this.#destroySafely(resource);\n return;\n }\n\n this.#available.push(resource);\n this.#pump();\n }\n\n async dispose() {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n\n const waiting = this.#waiting.splice(0);\n for (const { reject } of waiting) {\n reject(new Error(DISPOSED));\n }\n\n const available = this.#available.splice(0);\n this.#slots -= available.length;\n // Resources still checked out are destroyed as they come back in.\n await Promise.all(available.map((resource) => this.#destroySafely(resource)));\n }\n };\n }\n\n // Creates a provider for a single shared resource that is built lazily, shared\n // by every consumer at once, and destroyed only when the container is.\n //\n // This is the shape a long-lived backbone has -- a database handle, a storage\n // client, a search index -- and none of the three above fits it.\n // `fromSingleton` cannot build one, because building is asynchronous and often\n // ordered (`await db.connect()`). `fromPool` with a size of one is a MUTEX:\n // the second `obtain` blocks until the first releases, so one slow consumer\n // stops every other. `fromRefCounted` shares correctly but destroys at zero,\n // so an ordinary lease/release cycle tears the resource down and the next\n // consumer silently gets a different one while the old is still referenced.\n //\n // `release` is a synchronous no-op here, deliberately: a consumer that leases\n // the backbone on every request should not pay teardown on the way out.\n static fromLazySingleton(create, destroy, { deps = [] } = {}) {\n return class LazySingletonProvider extends Provider {\n static deps = deps;\n\n #deps;\n #creation = null;\n #resource = null;\n #disposed = false;\n\n constructor(injectedDeps = {}) {\n super();\n this.#deps = injectedDeps;\n }\n\n async obtain() {\n if (this.#disposed) {\n throw new Error(DISPOSED);\n }\n\n // Memoized on the promise rather than the value, so two obtains during a\n // slow `create` get the same resource instead of racing to build two.\n this.#creation ??= Promise.resolve().then(() => create(this.#deps));\n\n try {\n this.#resource = await this.#creation;\n return this.#resource;\n } catch (err) {\n // A failed creation must not poison every later obtain.\n this.#creation = null;\n this.#resource = null;\n throw err;\n }\n }\n\n // Nothing: the resource outlives whoever borrowed it.\n release() {}\n\n async dispose() {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n\n if (!this.#creation || typeof destroy !== 'function') {\n return;\n }\n\n // Creation may still be in flight; destroy what it produces rather than\n // leaking a half-built resource.\n const resource = this.#resource ?? (await this.#creation.catch(() => null));\n if (resource != null) {\n await destroy(resource, this.#deps);\n }\n }\n };\n }\n\n // Creates a provider for a single shared resource that is lazily created and\n // ref-counted, and destroyed when the count drops to zero.\n static fromRefCounted(create, destroy, { deps = [] } = {}) {\n return class RefCountedProvider extends Provider {\n static deps = deps;\n\n #deps;\n #refCount = 0;\n #creation = null;\n #resource = null;\n #teardown = null;\n #disposed = false;\n\n constructor(injectedDeps = {}) {\n super();\n this.#deps = injectedDeps;\n }\n\n async obtain() {\n if (this.#disposed) {\n throw new Error(DISPOSED);\n }\n\n this.#refCount++;\n\n // A previous resource may still be tearing down. Let it finish so we\n // never hold a half-destroyed resource.\n while (this.#teardown) {\n await this.#teardown;\n }\n\n if (this.#disposed) {\n this.#refCount--;\n throw new Error(DISPOSED);\n }\n\n if (!this.#creation) {\n this.#creation = Promise.resolve().then(() => create(this.#deps));\n }\n\n try {\n this.#resource = await this.#creation;\n return this.#resource;\n } catch (err) {\n // A failed creation must not poison every later obtain.\n this.#refCount--;\n if (this.#refCount <= 0) {\n this.#refCount = 0;\n this.#creation = null;\n this.#resource = null;\n }\n throw err;\n }\n }\n\n async release() {\n if (this.#refCount <= 0) {\n return;\n }\n\n this.#refCount--;\n if (this.#refCount > 0) {\n return;\n }\n\n const resource = this.#resource;\n const creation = this.#creation;\n this.#resource = null;\n this.#creation = null;\n\n if (!creation) {\n return;\n }\n\n this.#teardown = (async () => {\n try {\n // Creation may still be in flight; destroy what it produces.\n const created = resource ?? (await creation.catch(() => null));\n if (created != null) {\n await destroy(created, this.#deps);\n }\n } catch (err) {\n report(err);\n } finally {\n this.#teardown = null;\n }\n })();\n\n await this.#teardown;\n }\n\n async dispose() {\n this.#disposed = true;\n\n if (this.#refCount > 0) {\n this.#refCount = 1;\n await this.release();\n }\n\n while (this.#teardown) {\n await this.#teardown;\n }\n }\n };\n }\n}\n\n// A resolved dependency set held for the duration of some work. Leases are the\n// single seam between this layer and the ones above it: actions take a lease\n// for one dispatch, queries hold one from boot until kill.\nclass Lease {\n #entries;\n #released = false;\n\n constructor(resources, entries) {\n this.resources = resources;\n this.#entries = entries;\n }\n\n get released() {\n return this.#released;\n }\n\n // Idempotent: releasing twice never double-releases a resource.\n async release() {\n if (this.#released) {\n return;\n }\n this.#released = true;\n\n for (const { name, provider, options } of this.#entries) {\n try {\n await provider.release(this.resources[name], options);\n } catch (err) {\n report(err);\n }\n }\n this.#entries = [];\n }\n}\n\n// Instantiates and owns a graph of providers, and hands out their resources.\n//\n// Consumers above this layer only ever need `feed`, `resolve`, `lease` and\n// `use`, so anything implementing those four can stand in for a Container.\nexport class Container {\n #providers = new Map();\n #resolutions = new WeakMap();\n #feed;\n #disposed = false;\n\n constructor({ providers, feed } = {}) {\n this.#feed = feed ?? new EventTarget();\n\n if (!providers) {\n return;\n }\n\n // Memoized so a provider named by two dependents is built exactly once,\n // and so a diamond-shaped graph is walked in linear time.\n const build = (name, path) => {\n if (this.#providers.has(name)) {\n return this.#providers.get(name);\n }\n\n const ProviderClass = providers[name];\n if (!ProviderClass) {\n throw new Error(`Dependency not found: ${name}`);\n }\n\n const nextPath = [...path, name];\n if (path.includes(name)) {\n throw new Error(`Cyclic dependency detected: ${nextPath.join(' -> ')}`);\n }\n\n const resolvedDeps = {};\n for (const depName of ProviderClass.deps ?? []) {\n resolvedDeps[depName] = build(depName, nextPath);\n }\n\n const provider = new ProviderClass(resolvedDeps, {\n engineFeed: this.#feed,\n container: this,\n });\n this.#providers.set(name, provider);\n return provider;\n };\n\n for (const name of Object.keys(providers)) {\n build(name, []);\n }\n }\n\n // The shared event bus. Providers, actions and queries all see this one.\n get feed() {\n return this.#feed;\n }\n\n get disposed() {\n return this.#disposed;\n }\n\n has(name) {\n return this.#providers.has(name);\n }\n\n get(name) {\n const provider = this.#providers.get(name);\n if (!provider) {\n throw new Error(`Dependency not found: ${name}`);\n }\n return provider;\n }\n\n // Turns a deps declaration -- either `['a', 'b']` or `{ a: options }` -- into\n // a map of `{ provider, options }`. Results are memoized against the\n // declaration object, so declarations must not be mutated after first use.\n resolve(depsConfig) {\n if (!depsConfig) {\n return {};\n }\n\n const cached = this.#resolutions.get(depsConfig);\n if (cached) {\n return cached;\n }\n\n const resolved = {};\n if (Array.isArray(depsConfig)) {\n for (const name of depsConfig) {\n resolved[name] = { provider: this.get(name), options: undefined };\n }\n } else {\n for (const name of Object.keys(depsConfig)) {\n resolved[name] = { provider: this.get(name), options: depsConfig[name] };\n }\n }\n\n this.#resolutions.set(depsConfig, resolved);\n return resolved;\n }\n\n // Merges several declarations; later ones win.\n resolveAll(...depsConfigs) {\n const present = depsConfigs.filter(Boolean);\n if (present.length === 1) {\n return this.resolve(present[0]);\n }\n return Object.assign({}, ...present.map((config) => this.resolve(config)));\n }\n\n // Obtains every declared resource, or none of them: if one `obtain` fails,\n // the ones already obtained are released before the error propagates.\n async lease(...depsConfigs) {\n if (this.#disposed) {\n throw new Error('The container has been disposed');\n }\n\n const deps = this.resolveAll(...depsConfigs);\n const resources = {};\n const entries = [];\n\n try {\n for (const [name, { provider, options }] of Object.entries(deps)) {\n resources[name] = await provider.obtain(options);\n entries.push({ name, provider, options });\n }\n } catch (err) {\n await new Lease(resources, entries).release();\n throw err;\n }\n\n return new Lease(resources, entries);\n }\n\n // Scoped lease: obtain, run, release -- even if `fn` throws.\n async use(depsConfig, fn) {\n const lease = await this.lease(depsConfig);\n try {\n return await fn(lease.resources);\n } finally {\n await lease.release();\n }\n }\n\n // Flushes then disposes every provider. Idempotent.\n async dispose() {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n\n // Providers are built dependencies-first, so tearing down in reverse\n // insertion order means a provider is always disposed before anything it\n // depends on -- its flush/dispose can still use its dependencies.\n const teardownOrder = [...this.#providers.values()].reverse();\n\n for (const provider of teardownOrder) {\n try {\n await provider.flush?.();\n } catch (err) {\n report(err);\n }\n }\n\n for (const provider of teardownOrder) {\n try {\n await provider.dispose?.();\n } catch (err) {\n report(err);\n }\n }\n }\n}\n",
|
|
54
|
+
"// Layer 2: actions and interceptors.\n//\n// Depends only on the container seam (`feed`, `resolve`, `lease`, `use`), so it\n// can be used with providers but without queries.\n\nimport { Container } from './providers.js';\n\n// `ErrorEvent` is not a global everywhere JavaScript runs, so fall back to an\n// equivalent shape. Listeners read `event.error` either way.\nconst DispatchErrorEvent =\n typeof ErrorEvent === 'function'\n ? ErrorEvent\n : class DispatchErrorEvent extends Event {\n constructor(type, { error, message = '' } = {}) {\n super(type);\n this.error = error;\n this.message = message;\n }\n };\n\nfunction messageOf(error) {\n return error && typeof error.message === 'string' ? error.message : String(error);\n}\n\n// The event an aborted dispatch ends on. `reason` is what `abort()` was given;\n// `error` is set only if the action threw on its way out, so nothing is lost by\n// reporting the abort instead of the throw.\nclass DispatchAbortEvent extends Event {\n constructor({ reason, error = null } = {}) {\n super('abort');\n this.reason = reason;\n this.error = error;\n }\n}\n\n// How a dispatch can end. All three are terminal: exactly one fires, once.\nconst TERMINAL = ['complete', 'error', 'abort'];\n\n// What `abort()` uses when given no reason. The same shape an AbortController\n// picks for itself, so `feed.reason` and `signal.reason` agree; `DOMException`\n// is not a global absolutely everywhere, hence the fallback.\nfunction abortError() {\n return typeof DOMException === 'function'\n ? new DOMException('The dispatch was aborted', 'AbortError')\n : Object.assign(new Error('The dispatch was aborted'), { name: 'AbortError' });\n}\n\nexport class Action {\n execute() {\n throw new Error('execute() not implemented');\n }\n}\n\n// The channel a dispatch reports on.\n//\n// Still an EventTarget, and still the only way results leave an action -- an\n// action that emits `progress` five times and then `result` is saying more than\n// a return value could. Two things are layered on top:\n//\n// feed.abort(reason) -- the caller is no longer interested. `signal` is\n// handed to the action and its interceptors, so the\n// work can stop; cooperatively, since nothing can\n// interrupt a running function.\n// feed.next(names) -- await one of `names`. This is the shape a request\n// handler wants: dispatch, await the event that\n// carries the answer, reply.\n//\n// A dispatch ends on exactly one of `complete`, `error` or `abort`. An aborted\n// one ends on `abort` and never on `complete`, because a scan that was stopped\n// at thirty percent did not complete, and saying it did is how \"it worked\" gets\n// reported about work that did not happen.\n//\n// `next` treats all three as terminal unless you ask for one by name. A promise\n// that never settles is the worst thing this could do to a caller -- there is no\n// timeout, no cancellation, and no error to log -- and an action that ends\n// without emitting what was awaited would otherwise hang it forever.\nexport class DispatchFeed extends EventTarget {\n #controller = new AbortController();\n #reason; // why this was aborted -- see abort()\n #settled = null; // { type, event } once complete/error has fired\n\n constructor() {\n super();\n // Registered before any caller's listener, so `settled` is already true by\n // the time their handler runs. This is what lets `next()` answer a caller\n // that arrives after the action is over instead of waiting for an event\n // that can no longer fire.\n for (const type of TERMINAL) {\n this.addEventListener(\n type,\n (event) => { this.#settled ??= { type, event }; },\n { once: true }\n );\n }\n }\n\n // Aborted when the caller gives up. Handed to the action, and to every\n // interceptor, as `signal`.\n get signal() {\n return this.#controller.signal;\n }\n\n // The terminal event, if the dispatch is over: `'complete'`, `'error'`, or\n // null while it is still running.\n get settled() {\n return this.#settled?.type ?? null;\n }\n\n // Why this was aborted, once it has been. Held here rather than read back off\n // `signal.reason` at the point of use: that value belongs to the runtime, and\n // it is what `next()` rejects with, so it must never come back undefined --\n // rejecting with `undefined` breaks every caller that writes `catch (e) {\n // e.message }`. (Bun 1.3 does drop it under memory pressure; even without\n // that, the guarantee is cheaper to make than to depend on.)\n get reason() {\n return this.#reason;\n }\n\n // Stop caring about this dispatch. The action decides when to notice --\n // there is no way to interrupt a running function -- but anything waiting on\n // `next()` rejects at once, because the point of aborting is not to wait.\n abort(reason) {\n if (this.#controller.signal.aborted) {\n return; // first abort wins, as with AbortController\n }\n // Recorded BEFORE aborting. `abort()` dispatches synchronously, so a\n // `next()` waiting on the signal rejects during this call -- with the reason\n // if it is already here, and with undefined if it is not.\n this.#reason = reason ?? abortError();\n this.#controller.abort(this.#reason);\n }\n\n /**\n * Resolve with the first of `names` to fire.\n *\n * @param {string|string[]} names event name, or names, to wait for\n * @param {{signal?: AbortSignal}} [options] the caller's own cancellation,\n * separate from `feed.abort()` -- an HTTP handler passes the request's\n * signal here so a disconnect unblocks it without aborting the action.\n * @returns {Promise<Event>} the event that fired\n */\n next(names, { signal } = {}) {\n const wanted = new Set(Array.isArray(names) ? names : [names]);\n if (wanted.size === 0) {\n throw new TypeError('next() requires at least one event name');\n }\n\n // Aborting and then awaiting `abort` is the ordinary wind-down: you have\n // stopped caring about the answer but still need to know the work stopped,\n // to release something or to reply. So a wait for the abort event is not\n // pre-empted by the abort itself -- everything else is, because the point of\n // giving up is not to keep waiting for the action to notice.\n const awaitingTheEnd = wanted.has('abort');\n\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n if (this.#controller.signal.aborted && !awaitingTheEnd && !this.#settled) {\n reject(this.#reason);\n return;\n }\n // Already over. Answer from what was recorded rather than waiting for an\n // event that has fired once and will not fire again.\n if (this.#settled) {\n this.#finish(resolve, reject, this.#settled, wanted);\n return;\n }\n\n // One controller unregisters every listener below at once, on whichever\n // path settles first.\n const stop = new AbortController();\n const options = { signal: stop.signal };\n const settle = (fn) => (value) => {\n stop.abort();\n fn(value);\n };\n\n for (const name of wanted) {\n this.addEventListener(name, settle(resolve), options);\n }\n // Every terminal event not asked for by name ends the wait, because after\n // one of them nothing else will ever fire.\n for (const type of TERMINAL) {\n if (wanted.has(type)) {\n continue;\n }\n this.addEventListener(\n type,\n (event) => settle(reject)(endedWithout(type, event, wanted)),\n options\n );\n }\n if (!awaitingTheEnd) {\n this.#controller.signal.addEventListener(\n 'abort',\n () => settle(reject)(this.#reason),\n options\n );\n }\n signal?.addEventListener('abort', () => settle(reject)(signal.reason), options);\n });\n }\n\n #finish(resolve, reject, settled, wanted) {\n if (wanted.has(settled.type)) {\n resolve(settled.event);\n } else {\n reject(endedWithout(settled.type, settled.event, wanted));\n }\n }\n}\n\n// Why a wait ended without what it was waiting for. The action's own error and\n// the abort reason are handed back unwrapped, so a caller mapping them onto\n// something else -- an HTTP status, say -- still can.\nfunction endedWithout(type, event, wanted) {\n if (type === 'error') {\n return event.error;\n }\n if (type === 'abort') {\n return event.reason;\n }\n return missing(wanted);\n}\n\nfunction missing(wanted) {\n return new Error(\n `The dispatch completed without emitting ${[...wanted].map((n) => `'${n}'`).join(' or ')}`\n );\n}\n\n// Runs actions through a stack of interceptors.\nexport class Dispatcher {\n #container;\n #interceptors;\n\n constructor({ container, interceptors } = {}) {\n this.#container = container ?? new Container();\n this.#interceptors = interceptors ? [...interceptors] : [];\n }\n\n get container() {\n return this.#container;\n }\n\n get feed() {\n return this.#container.feed;\n }\n\n // Dispatch an action. Returns the dispatch feed immediately; the action\n // itself runs on a later turn of the event loop.\n dispatch(actionInstance) {\n if (!(actionInstance instanceof Action)) {\n throw new TypeError('dispatch() requires an instance of Action');\n }\n\n const container = this.#container;\n const interceptors = this.#interceptors;\n const engineFeed = container.feed;\n const dispatchFeed = new DispatchFeed();\n\n setTimeout(async () => {\n const enteredInterceptors = [];\n let state = {};\n let error = null;\n\n const applyState = (nextState) => {\n if (nextState !== undefined) {\n state = nextState;\n }\n };\n\n try {\n // Phase 1: Run 'enter' interceptors.\n for (const interceptor of interceptors) {\n // Recorded before running so a failing 'enter' still gets unwound.\n enteredInterceptors.push(interceptor);\n if (!interceptor.enter) {\n continue;\n }\n\n applyState(\n await container.use(interceptor.deps, (resources) =>\n interceptor.enter(resources, {\n dispatchFeed,\n engineFeed,\n signal: dispatchFeed.signal,\n state,\n action: actionInstance,\n })\n )\n );\n }\n\n // Phase 2: Obtain and execute the main action. Instance deps override\n // static ones of the same name.\n const lease = await container.lease(\n actionInstance.constructor.deps,\n actionInstance.deps\n );\n try {\n await actionInstance.execute(lease.resources, {\n dispatchFeed,\n engineFeed,\n // Cooperative: aborting cannot interrupt a running function, so an\n // action that wants to be stoppable checks this, or hands it to\n // something that does (fetch, a scan loop, another dispatch).\n signal: dispatchFeed.signal,\n state,\n });\n } finally {\n await lease.release();\n }\n } catch (e) {\n error = e;\n } finally {\n // Phase 3: Unwind the stack in reverse.\n //\n // Every interceptor that entered gets exactly ONE unwind call. That is\n // what makes it safe to acquire something in `enter`, so `abort` falls\n // back to the hook that would have run without it rather than to\n // nothing -- an interceptor that does not know about aborting must\n // still be given the chance to close what it opened.\n //\n // aborted -> abort ?? (error ? error : leave)\n // error -> error ?? nothing\n // otherwise -> leave ?? nothing\n //\n // Which matters because of what `leave` means to the obvious\n // interceptor: enter/begin, leave/commit, error/rollback. Without an\n // `abort` hook that one commits the work of a dispatch that was\n // cancelled half way through -- the same lie the feed used to tell by\n // ending an aborted dispatch on `complete`.\n for (let i = enteredInterceptors.length - 1; i >= 0; i--) {\n const interceptor = enteredInterceptors[i];\n // Read per iteration: an abort landing mid-unwind is reflected by the\n // interceptors that have not run yet, rather than being missed.\n const aborted = dispatchFeed.signal.aborted;\n try {\n if (aborted && interceptor.abort) {\n applyState(\n await container.use(interceptor.deps, (resources) =>\n interceptor.abort(resources, {\n dispatchFeed,\n engineFeed,\n signal: dispatchFeed.signal,\n action: actionInstance,\n state,\n reason: dispatchFeed.reason,\n // Set when the action threw on its way out -- usually\n // because it honoured the signal. There is no `handled()`\n // here: a cancellation cannot be handled into a success,\n // because the work did not happen.\n error,\n })\n )\n );\n } else if (error && interceptor.error) {\n const errorContext = {\n dispatchFeed,\n engineFeed,\n signal: dispatchFeed.signal,\n action: actionInstance,\n state,\n error,\n handled: () => {\n error = null;\n },\n };\n applyState(\n await container.use(interceptor.deps, (resources) =>\n interceptor.error(resources, errorContext)\n )\n );\n } else if (!error && interceptor.leave) {\n applyState(\n await container.use(interceptor.deps, (resources) =>\n interceptor.leave(resources, {\n dispatchFeed,\n engineFeed,\n signal: dispatchFeed.signal,\n state,\n action: actionInstance,\n })\n )\n );\n }\n } catch (e) {\n error = e;\n }\n }\n\n // An aborted dispatch ends on `abort`, whatever the action did on its\n // way out. It did not complete -- it was stopped -- and an action that\n // threw because it honoured the signal threw *because* of the abort, so\n // reporting the throw would name the symptom rather than the cause. The\n // error is carried on the event either way, so nothing is lost.\n if (dispatchFeed.signal.aborted) {\n dispatchFeed.dispatchEvent(\n new DispatchAbortEvent({ reason: dispatchFeed.reason, error })\n );\n } else if (error) {\n dispatchFeed.dispatchEvent(\n new DispatchErrorEvent('error', { error, message: messageOf(error) })\n );\n } else {\n dispatchFeed.dispatchEvent(new Event('complete'));\n }\n }\n });\n\n return dispatchFeed;\n }\n}\n",
|
|
55
|
+
"// Layer 3: queries.\n//\n// Depends on the container seam, and optionally on *anything* with a\n// `dispatch(action)` method for queries that declare a `bootAction` or\n// `killAction`. It does not import the actions layer, so queries can be used\n// with providers alone.\n\nimport { Container } from './providers.js';\nimport { report } from './internal.js';\n\nexport class Query {\n // A query that implements `fetch` and not `boot` is a ONE-SHOT: subscribing to\n // it fetches once, hands the value over, and completes. See QueryController's\n // boot() for why that is the fallback rather than an error.\n boot() {\n throw new Error('boot() not implemented, and no fetch() to fall back to');\n }\n kill() {}\n}\n\n// One live realization of a query instance, shared by all of its observers.\nclass QueryController {\n #store;\n #query;\n #lease = null;\n #booting = null;\n #booted = false;\n #killing = null;\n #received = Promise.withResolvers();\n #hasValue = false;\n #currentValue = undefined;\n #dead = false;\n #bootFeed = undefined;\n #killFeed = undefined;\n\n observers = new Set();\n\n constructor(store, query) {\n this.#store = store;\n this.#query = query;\n this.notify = this.notify.bind(this);\n }\n\n get hasValue() {\n return this.#hasValue;\n }\n\n get currentValue() {\n return this.#currentValue;\n }\n\n // Resolves true once a value has arrived, false if the query died first.\n // A live query that never notifies leaves this pending -- that is what makes\n // peek() on such a query wait rather than fall back to fetch().\n get received() {\n return this.#received.promise;\n }\n\n get bootFeed() {\n return this.#bootFeed;\n }\n\n get killFeed() {\n return this.#killFeed;\n }\n\n // A lifecycle action is fire-and-forget -- nothing is waiting on its feed, so\n // a failure would otherwise vanish. Route it to the same channel as every\n // other unpropagatable error, with enough context to place it.\n #dispatchLifecycleAction(action, label) {\n const feed = this.#store._dispatch(action);\n\n feed.addEventListener(\n 'error',\n (event) => {\n report(\n new Error(\n `${this.#query.constructor.name} ${label} (${action.constructor.name}) failed`,\n { cause: event.error }\n )\n );\n },\n { once: true }\n );\n\n return feed;\n }\n\n notify(nextValue) {\n if (this.#dead) {\n return;\n }\n\n this.#currentValue = nextValue;\n\n if (!this.#hasValue) {\n this.#hasValue = true;\n this.#received.resolve(true);\n }\n\n // Copied so an observer unsubscribing mid-notify doesn't skip its peers.\n for (const observer of [...this.observers]) {\n try {\n observer.next?.(nextValue);\n } catch (err) {\n report(err);\n }\n }\n }\n\n boot() {\n if (this.#booting) {\n return this.#booting;\n }\n\n this.#booting = (async () => {\n try {\n if (this.#query.bootAction) {\n this.#bootFeed = this.#dispatchLifecycleAction(\n this.#query.bootAction,\n 'bootAction'\n );\n }\n\n this.#lease = await this.#store._container.lease(\n this.#query.constructor.deps,\n this.#query.deps\n );\n\n const context = {\n notify: this.notify,\n engineFeed: this.#store._container.feed,\n bootFeed: this.#bootFeed,\n };\n\n // A query that never overrode `boot` is a read: it has one answer and no\n // way to learn of a second. Rather than failing -- which is what the\n // base `boot` would do, and it failed QUIETLY, leaving the subscriber\n // waiting for a value that was never coming -- fetch once, hand the\n // value over, and complete. That is an ordinary single-value\n // observable, so a consumer can subscribe to any query without knowing\n // in advance whether it is live.\n if (this.#query.boot === Query.prototype.boot) {\n if (typeof this.#query.fetch !== 'function') {\n throw new Error(\n `${this.#query.constructor.name} implements neither boot() nor fetch()`\n );\n }\n this.notify(await this.#query.fetch(this.#lease.resources, context));\n // Evicts, completes every observer, and releases the lease -- and\n // evicting is what makes the NEXT subscribe fetch again rather than\n // serving a value that has no way of ever being refreshed.\n await this.#teardown();\n } else {\n await this.#query.boot(this.#lease.resources, context);\n }\n } catch (err) {\n report(err);\n // A query that failed to boot is not a query anyone can wait on.\n await this.#teardown(err);\n } finally {\n this.#booted = true;\n }\n })();\n\n return this.#booting;\n }\n\n kill() {\n if (this.#killing) {\n return this.#killing;\n }\n\n // Never tear down on top of an in-flight boot -- but when boot has already\n // settled, run the teardown eagerly rather than punting it to a later tick.\n this.#killing = this.#booted\n ? this.#runKill()\n : Promise.resolve(this.#booting).then(() => this.#runKill());\n\n return this.#killing;\n }\n\n async #runKill() {\n if (this.#dead) {\n return;\n }\n\n try {\n if (this.#query.killAction) {\n this.#killFeed = this.#dispatchLifecycleAction(\n this.#query.killAction,\n 'killAction'\n );\n }\n if (this.#query.kill) {\n await this.#query.kill(this.#lease?.resources ?? {}, {\n bootFeed: this.#bootFeed,\n killFeed: this.#killFeed,\n engineFeed: this.#store._container.feed,\n notify: this.notify,\n });\n }\n } catch (err) {\n report(err);\n } finally {\n await this.#teardown();\n }\n }\n\n /**\n * @param {any} [error] when the query is ending BECAUSE something failed.\n * Delivered to observers that implement `error`, which matters most for a\n * one-shot read: a fetch that threw must not look to its subscriber like an\n * empty success. Observers that do not implement it still get `complete`,\n * so nothing that worked before behaves differently, and `report` still runs\n * either way so a failure is never simply swallowed.\n */\n async #teardown(error) {\n if (this.#dead) {\n return;\n }\n this.#dead = true;\n\n this.#store._evict(this.#query, this);\n\n const observers = [...this.observers];\n this.observers.clear();\n for (const observer of observers) {\n try {\n if (error !== undefined && observer.error) {\n observer.error(error);\n } else {\n observer.complete?.();\n }\n } catch (err) {\n report(err);\n }\n }\n\n if (!this.#hasValue) {\n this.#received.resolve(false);\n }\n\n const lease = this.#lease;\n this.#lease = null;\n if (lease) {\n await lease.release();\n }\n }\n}\n\n// Realizes queries and keeps one live controller per query instance.\nexport class QueryStore {\n #container;\n #dispatcher;\n #controllers;\n #disposed = false;\n\n constructor({ container, dispatcher, createControllerMap } = {}) {\n this.#container = container ?? new Container();\n this.#dispatcher = dispatcher ?? null;\n this.#controllers = createControllerMap?.() ?? new Map();\n }\n\n get container() {\n return this.#container;\n }\n\n get feed() {\n return this.#container.feed;\n }\n\n // Internal seams used by QueryController.\n get _container() {\n return this.#container;\n }\n\n _dispatch(action) {\n if (!this.#dispatcher) {\n throw new Error(\n 'A query declared a bootAction/killAction but no dispatcher was given to QueryStore'\n );\n }\n return this.#dispatcher.dispatch(action);\n }\n\n _evict(queryInstance, controller) {\n if (this.#controllers.get(queryInstance) === controller) {\n this.#controllers.delete(queryInstance);\n }\n }\n\n #queryDeps(queryInstance) {\n return [queryInstance.constructor.deps, queryInstance.deps];\n }\n\n // Realizes a query. Call `subscribe` on the returned object to use it as an\n // RxJS style observable, or `peek` to get a look at the current value if the\n // query is active, or fetch it otherwise.\n query(queryInstance) {\n if (!(queryInstance instanceof Query)) {\n throw new TypeError('query() requires an instance of Query');\n }\n\n return {\n subscribe: (observerOrNext) => {\n if (this.#disposed) {\n throw new Error('The query store has been disposed');\n }\n\n const observer =\n typeof observerOrNext === 'function'\n ? { next: observerOrNext }\n : observerOrNext || {};\n\n let controller = this.#controllers.get(queryInstance);\n if (controller) {\n controller.observers.add(observer);\n if (controller.hasValue) {\n try {\n observer.next?.(controller.currentValue);\n } catch (err) {\n report(err);\n }\n }\n } else {\n controller = new QueryController(this, queryInstance);\n this.#controllers.set(queryInstance, controller);\n controller.observers.add(observer);\n controller.boot();\n }\n\n let closed = false;\n return {\n get closed() {\n return closed;\n },\n unsubscribe: () => {\n if (closed) {\n return;\n }\n closed = true;\n\n // No complete() for the unsubscriber: withdrawing is not the query\n // ending. complete() stays reserved for a query dying underneath\n // observers that are still attached (see #teardown).\n controller.observers.delete(observer);\n if (controller.observers.size === 0) {\n // Dropped from the registry first, so a subscribe that arrives\n // while the controller is dying gets a fresh one.\n this._evict(queryInstance, controller);\n controller.kill();\n }\n },\n };\n },\n\n // Resolves with the active query's current value, waiting for its first\n // one if it has not emitted yet. If the query is not active -- or dies\n // before emitting -- falls through to the query's fetch().\n //\n // A live query that never emits therefore leaves this pending until it\n // is killed. That is deliberate: peek() on an active query answers from\n // that query, and a query with subscribers is expected to produce a\n // value. Callers that cannot block should race it themselves.\n peek: async () => {\n const controller = this.#controllers.get(queryInstance);\n if (controller && (await controller.received)) {\n return controller.currentValue;\n }\n\n if (typeof queryInstance.fetch !== 'function') {\n throw new TypeError(\n `${queryInstance.constructor.name} is not active and does not implement fetch()`\n );\n }\n\n const lease = await this.#container.lease(...this.#queryDeps(queryInstance));\n try {\n return await queryInstance.fetch(lease.resources, {\n engineFeed: this.#container.feed,\n bootFeed: controller?.bootFeed,\n killFeed: controller?.killFeed,\n });\n } finally {\n await lease.release();\n }\n },\n };\n }\n\n // Kills every live query. Idempotent.\n async dispose() {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n\n // A custom controller map is not required to be enumerable.\n for (const controller of [...(this.#controllers.values?.() ?? [])]) {\n await controller.kill();\n }\n }\n}\n",
|
|
56
|
+
"// The full stack: providers + actions + queries, wired together.\n//\n// `Engine` is a facade. Everything it does is available from the three layers\n// directly; it exists so the common case stays a one-liner.\n\nimport { Container } from './providers.js';\nimport { Dispatcher } from './actions.js';\nimport { QueryStore } from './queries.js';\n\nexport class Engine {\n #container;\n #dispatcher;\n #queries;\n #disposed = false;\n\n constructor({ providers, interceptors, hooks, container, dispatcher, queries } = {}) {\n // Each layer can be supplied pre-built, which is how you swap one out\n // (a stub container in tests, a dispatcher shared with another engine).\n this.#container = container ?? new Container({ providers });\n this.#dispatcher =\n dispatcher ?? new Dispatcher({ container: this.#container, interceptors });\n this.#queries =\n queries ??\n new QueryStore({\n container: this.#container,\n dispatcher: this.#dispatcher,\n createControllerMap: hooks?.createQueryControllersMap,\n });\n }\n\n get container() {\n return this.#container;\n }\n\n get dispatcher() {\n return this.#dispatcher;\n }\n\n get queries() {\n return this.#queries;\n }\n\n get feed() {\n return this.#container.feed;\n }\n\n // Dispatch an action.\n dispatch(actionInstance) {\n return this.#dispatcher.dispatch(actionInstance);\n }\n\n // Realizes a query.\n query(queryInstance) {\n return this.#queries.query(queryInstance);\n }\n\n async dispose() {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n\n // Queries first: killing them releases resources back to the providers\n // that are about to be disposed.\n await this.#queries.dispose();\n await this.#container.dispose();\n }\n}\n",
|
|
57
|
+
"// Reactive data services the workbench renders from. These hold *data* state\n// (the current collection's items, search results, in-flight transfers) as\n// opposed to the shell's UI state (WorkbenchService). ngin Actions mutate them;\n// the UI `watch`es them. Each is a plain cell wrapper so the render layer stays\n// declarative.\n\nimport { cell } from '../runtime.js';\n\nexport class ExplorerService {\n constructor(settings) {\n this.settings = settings;\n this.state = {\n items: [], loading: false, error: null,\n selection: [], sort: settings.get('explorer.sort'), order: settings.get('explorer.sortOrder'),\n collectionId: 'default', collections: [], canCreateCollection: false,\n // `stats` is the whole collection; `items` is the page on screen. Keeping both\n // is what lets the UI say \"500 of 3,006\" instead of quietly claiming 500.\n stats: null, usage: null, nextCursor: null, loadingMore: false, trash: null,\n };\n this.cell = cell(this.state);\n }\n observe() {\n return this.cell;\n }\n set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n /**\n * @param {string[]} ids\n * @param {{additive?: boolean, nodes?: object[]}} [opts] `nodes` is the caller's own\n * copy of what it selected — pass it whenever you have it (see selectedNodes).\n */\n select(ids, { additive = false, nodes = null } = {}) {\n const next = additive ? Array.from(new Set([...this.state.selection, ...ids])) : ids;\n // Selecting what is already selected must not emit. The launcher syncs the\n // highlighted row into here on every mouseenter, and a state push per mouse move\n // would re-render the list under the pointer.\n const same = next.length === this.state.selection.length\n && next.every((id, i) => id === this.state.selection[i]);\n if (same) return;\n this.set({ selection: next, selectionNodes: nodes && !additive ? nodes : null });\n }\n /**\n * The nodes the selection refers to.\n *\n * Resolving ids against `items` only finds rows on the LOADED PAGE of the CURRENT\n * collection — and the launcher's rows come from search (which the server scopes to\n * every readable collection) and from recents (which survive a collection switch).\n * So every row reached by searching resolved to nothing, and rename / move-to-trash /\n * copy-link returned silently while `explorer.hasSelection` said there was a\n * selection. Preferring the nodes the selecting caller already held fixes the whole\n * class; the `items` lookup stays for callers that only have ids.\n */\n selectedNodes() {\n const held = this.state.selectionNodes;\n if (held?.length) return held.filter((n) => this.state.selection.includes(n.id));\n return this.state.items.filter((i) => this.state.selection.includes(i.id));\n }\n}\n\nexport class SearchClientService {\n constructor() {\n this.state = { query: '', mode: 'hybrid', results: [], loading: false, error: null, ran: false, paletteFiles: [], paletteQuery: '', paletteLoading: false, paletteError: null };\n this.cell = cell(this.state);\n }\n observe() {\n return this.cell;\n }\n set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n}\n\n/**\n * Uploads in flight.\n *\n * This owns the DETAIL an upload needs and a generic task doesn't — bytes moved, an\n * AbortController, per-file retry. It also projects each transfer into ActivityService\n * so the drive has ONE list of \"what is happening\", covering work on both sides of the\n * wire: an upload running in this browser and a reindex running on the server show up\n * together, because a user doesn't care which machine is busy.\n *\n * The projection is one-way and this stays the owner. Two writable copies of the same\n * fact is how they drift.\n */\nexport class TransfersService {\n /** @param {import('./activity.js').ActivityService} [activity] */\n constructor(activity = null) {\n this.state = { items: [] }; // { id, name, direction, ratio, loaded, total, status, error }\n this.cell = cell(this.state);\n this._controllers = new Map();\n this.activity = activity;\n this._tasks = new Map(); // transfer id -> activity task handle\n }\n observe() {\n return this.cell;\n }\n #emit() {\n this.cell.setValue(this.state);\n }\n start(id, name, total, controller) {\n this._controllers.set(id, controller);\n this.state = { items: [...this.state.items, { id, name, direction: 'up', ratio: 0, loaded: 0, total, status: 'active', error: null }] };\n this.#emit();\n const task = this.activity?.start({\n kind: 'transfer', title: `Uploading ${name}`, total: total || null, unit: 'bytes',\n onCancel: () => this.cancel(id),\n });\n if (task) this._tasks.set(id, task);\n }\n progress(id, { loaded, total, ratio }) {\n this.state = { items: this.state.items.map((t) => (t.id === id ? { ...t, loaded, total, ratio } : t)) };\n this.#emit();\n this._tasks.get(id)?.progress({ done: loaded, total: total || null });\n }\n finish(id, status = 'done', error = null) {\n this.state = { items: this.state.items.map((t) => (t.id === id ? { ...t, status, error, ratio: status === 'done' ? 1 : t.ratio } : t)) };\n this._controllers.delete(id);\n this.#emit();\n const task = this._tasks.get(id);\n if (task) {\n if (status === 'done') task.succeed();\n else if (status === 'cancelled') task.cancel();\n else task.fail(error || new Error('Upload failed'));\n this._tasks.delete(id);\n }\n if (status === 'done') setTimeout(() => this.dismiss(id), 4000);\n }\n cancel(id) {\n this._controllers.get(id)?.abort();\n this.finish(id, 'cancelled');\n }\n dismiss(id) {\n this.state = { items: this.state.items.filter((t) => t.id !== id) };\n this.#emit();\n }\n clearDone() {\n this.state = { items: this.state.items.filter((t) => t.status === 'active') };\n this.#emit();\n }\n}\n",
|
|
58
|
+
"// SocialService — the client side of identity, conversations, tags, and mention\n// notifications. Reactive (the workbench `watch`es it): it holds the current\n// principal, the notification inbox (polled + refreshed on demand), and the\n// active file's sidecar (tags + threaded comments). It also owns the web-push\n// subscription flow (register the service worker, subscribe with the server's\n// VAPID key). All mutations optimistically reload the affected sidecar.\n\nimport { cell } from '../runtime.js';\n\nexport class SocialService {\n constructor(platform) {\n this.platform = platform;\n this.api = platform.api;\n this.state = {\n me: null,\n notifications: { items: [], unread: 0 },\n inboxOpen: false,\n pushSupported: 'serviceWorker' in navigator && 'PushManager' in window,\n pushEnabled: false,\n sidecar: null, // { nodeId, tags, comments, subscribers, loading, error }\n backlinks: null, // { nodeId, items, loading, error } — what links to the open item\n posting: false,\n replyTo: null, // { id, author } when composing a reply\n };\n this.cell = cell(this.state);\n this._pollTimer = null;\n }\n\n observe() {\n return this.cell;\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n\n async init() {\n try {\n const me = await this.api.me();\n this.#set({ me: me.principal, admin: !!me.admin });\n } catch { /* anonymous / offline */ }\n await this.refreshNotifications();\n // Light polling; the service worker also nudges on push.\n this._pollTimer = setInterval(() => this.refreshNotifications(), 45_000);\n if (this._pollTimer.unref) this._pollTimer.unref();\n this.#detectPush();\n window.addEventListener('focus', () => this.refreshNotifications());\n navigator.serviceWorker?.addEventListener?.('message', (e) => {\n if (e.data?.type === 'trove-push') this.refreshNotifications();\n });\n }\n\n async refreshNotifications() {\n try {\n const n = await this.api.notifications();\n this.#set({ notifications: n });\n } catch { /* not enabled / offline */ }\n }\n\n toggleInbox(open) {\n const next = open ?? !this.state.inboxOpen;\n this.#set({ inboxOpen: next });\n if (next && this.state.notifications.unread) this.markAllRead();\n }\n async markAllRead() {\n try {\n await this.api.markNotificationsRead();\n this.#set({ notifications: { ...this.state.notifications, unread: 0, items: this.state.notifications.items.map((i) => ({ ...i, read: true })) } });\n } catch { /* ignore */ }\n }\n\n // --- sidecar (per active file) ---------------------------------------------\n\n async loadSidecar(nodeId) {\n if (!nodeId) return this.#set({ sidecar: null, backlinks: null });\n this.#set({ sidecar: { nodeId, loading: true, tags: [], comments: [], subscribers: [] } });\n this.loadBacklinks(nodeId);\n try {\n const view = await this.api.sidecar(nodeId);\n // A slower request for a previously-open item must not land on this one — the\n // same guard loadBacklinks already had. Without it, opening A then quickly opening\n // B let A resolve last, so `state.sidecar.nodeId` became A while B was on screen\n // and comment()/addTag()/removeTag() all wrote to A.\n if (this.state.sidecar?.nodeId !== nodeId) return;\n this.#set({ sidecar: { ...view, loading: false } });\n } catch (err) {\n if (this.state.sidecar?.nodeId !== nodeId) return;\n this.#set({ sidecar: { nodeId, loading: false, error: err.message, tags: [], comments: [] } });\n }\n }\n\n /**\n * What links to this item. With no folders, this is how you find out where an item\n * \"lives\" — which documents gather it up — so a failure has to be visible rather\n * than rendering as \"nothing links here\", which would be a claim about the drive.\n */\n async loadBacklinks(nodeId) {\n if (!nodeId) return this.#set({ backlinks: null });\n this.#set({ backlinks: { nodeId, items: [], loading: true, error: null } });\n try {\n const res = await this.api.backlinks(nodeId);\n // A slower request for a previously-open item must not land on this one.\n if (this.state.backlinks?.nodeId !== nodeId) return;\n this.#set({ backlinks: { nodeId, items: res.items || [], loading: false, error: null } });\n } catch (err) {\n if (this.state.backlinks?.nodeId !== nodeId) return;\n this.#set({ backlinks: { nodeId, items: [], loading: false, error: err.message } });\n }\n }\n async #reload() {\n if (this.state.sidecar?.nodeId) await this.loadSidecar(this.state.sidecar.nodeId);\n }\n\n setReplyTo(target) {\n this.#set({ replyTo: target });\n }\n async comment(body) {\n const nodeId = this.state.sidecar?.nodeId;\n if (!nodeId || !body.trim()) return;\n const parentId = this.state.replyTo?.id || null;\n // Offline → queue the op; it replays (CRDT-merges) on reconnect.\n if (this.offline && !this.offline.state.online) {\n await this.offline.queueOp({ method: 'POST', path: `/api/items/${encodeURIComponent(nodeId)}/comments`, body: { body, parentId } });\n this.#set({ replyTo: null });\n this.platform.notifications.info('Offline — your comment will post when you reconnect.');\n return;\n }\n this.#set({ posting: true });\n try {\n await this.api.addComment(nodeId, { body, parentId });\n this.#set({ replyTo: null });\n await this.#reload();\n } catch (err) {\n this.platform.notifications.error(`Couldn't post comment: ${err.message}`);\n } finally {\n this.#set({ posting: false });\n }\n }\n async deleteComment(cid) {\n const nodeId = this.state.sidecar?.nodeId;\n await this.api.deleteComment(nodeId, cid).catch((e) => this.platform.notifications.error(e.message));\n await this.#reload();\n }\n async react(cid, emoji) {\n const nodeId = this.state.sidecar?.nodeId;\n const comment = findComment(this.state.sidecar?.comments || [], cid);\n const on = !(comment?.reactions?.[emoji] || []).includes(this.state.me?.id);\n try {\n await this.api.reactComment(nodeId, cid, emoji, on);\n } catch (err) {\n this.platform.notifications.error(`Couldn't react: ${err.message}`);\n }\n await this.#reload();\n }\n async addTag(name, value) {\n const nodeId = this.state.sidecar?.nodeId;\n if (!name.trim()) return;\n if (this.offline && !this.offline.state.online) {\n await this.offline.queueOp({ method: 'POST', path: `/api/items/${encodeURIComponent(nodeId)}/tags`, body: { name: name.trim(), value } });\n this.platform.notifications.info('Offline — tag will sync when you reconnect.');\n return;\n }\n await this.api.setTag(nodeId, name.trim(), value).catch((e) => this.platform.notifications.error(e.message));\n await this.#reload();\n }\n async removeTag(name) {\n const nodeId = this.state.sidecar?.nodeId;\n // Offline → queue like addTag so the removal isn't silently lost.\n if (this.offline && !this.offline.state.online) {\n await this.offline.queueOp({ method: 'DELETE', path: `/api/items/${encodeURIComponent(nodeId)}/tags/${encodeURIComponent(name)}` });\n this.platform.notifications.info('Offline — tag removal will sync when you reconnect.');\n return;\n }\n try {\n await this.api.removeTag(nodeId, name);\n } catch (err) {\n this.platform.notifications.error(`Couldn't remove tag: ${err.message}`);\n }\n await this.#reload();\n }\n\n // --- web push --------------------------------------------------------------\n\n async #detectPush() {\n if (!this.state.pushSupported) return;\n try {\n const reg = await navigator.serviceWorker.getRegistration();\n const sub = await reg?.pushManager?.getSubscription();\n this.#set({ pushEnabled: !!sub });\n } catch { /* ignore */ }\n }\n\n async enablePush() {\n if (!this.state.pushSupported) {\n this.platform.notifications.warn('Push notifications are not supported in this browser.');\n return;\n }\n try {\n const { publicKey } = await this.api.vapidKey();\n if (!publicKey) {\n this.platform.notifications.warn('This server has no VAPID key configured for web push.');\n return;\n }\n const reg = await navigator.serviceWorker.register('/sw.js');\n await navigator.serviceWorker.ready;\n const perm = await Notification.requestPermission();\n if (perm !== 'granted') return this.platform.notifications.info('Notifications permission was denied.');\n const sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(publicKey) });\n await this.api.subscribePush(sub.toJSON());\n this.#set({ pushEnabled: true });\n this.platform.notifications.success('Notifications enabled — you’ll be pinged when someone @mentions you.');\n } catch (err) {\n this.platform.notifications.error(`Couldn't enable notifications: ${err.message}`);\n }\n }\n}\n\nfunction findComment(list, cid) {\n for (const c of list) {\n if (c.id === cid) return c;\n const inner = findComment(c.replies || [], cid);\n if (inner) return inner;\n }\n return null;\n}\n\nfunction urlBase64ToUint8Array(base64) {\n const padding = '='.repeat((4 - (base64.length % 4)) % 4);\n const b64 = (base64 + padding).replace(/-/g, '+').replace(/_/g, '/');\n const raw = atob(b64);\n const out = new Uint8Array(raw.length);\n for (let i = 0; i < raw.length; i++) out[i] = raw.charCodeAt(i);\n return out;\n}\n",
|
|
59
|
+
"// EmbeddingProvider — turns text into vectors. Pluggable so deployments choose\n// their model/cost tradeoff. Two are bundled:\n// - LocalHashEmbedding: zero-dependency, offline, deterministic. A hashed\n// bag-of-bigrams projected to a fixed dim and L2-normalised. Not as good as\n// a real model, but makes semantic search work out of the box and in tests.\n// - HttpEmbedding: POSTs to any OpenAI-compatible /embeddings endpoint (OpenAI,\n// Ollama, LM Studio, a self-hosted model). This is the production path.\n// Both expose `dimensions` and `embed(texts) -> number[][]`.\n\nimport { withRetry } from '../retry.js';\nimport { TroveError, wrapError } from '../errors.js';\n\nexport class EmbeddingProvider {\n get dimensions() {\n return 0;\n }\n /** @param {string[]} texts @returns {Promise<number[][]>} */\n async embed(texts) {\n throw TroveError.unsupported('embed not implemented');\n }\n async embedOne(text) {\n return (await this.embed([text]))[0];\n }\n}\n\nconst STOP = new Set('a an the of to in on for and or is are be as at by with from this that it'.split(' '));\n\nfunction tokenize(text) {\n return String(text)\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, ' ')\n .split(/\\s+/)\n .filter((t) => t.length > 1 && !STOP.has(t));\n}\n\n// FNV-1a → 32-bit, used to bucket features into vector dims.\nfunction fnv(str) {\n let h = 2166136261;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 16777619);\n }\n return h >>> 0;\n}\n\nexport class LocalHashEmbedding extends EmbeddingProvider {\n constructor({ dimensions = 256 } = {}) {\n super();\n this._dim = dimensions;\n }\n get dimensions() {\n return this._dim;\n }\n async embed(texts) {\n return texts.map((t) => this.#vec(t));\n }\n #vec(text) {\n const v = new Float64Array(this._dim);\n const tokens = tokenize(text);\n // Unigrams + bigrams give a little word-order sensitivity.\n const feats = [...tokens];\n for (let i = 0; i < tokens.length - 1; i++) feats.push(tokens[i] + '_' + tokens[i + 1]);\n for (const f of feats) {\n const h = fnv(f);\n const idx = h % this._dim;\n const sign = (h >> 31) & 1 ? -1 : 1; // signed hashing reduces collisions\n v[idx] += sign;\n }\n // L2 normalise so dot product == cosine similarity.\n let norm = 0;\n for (let i = 0; i < v.length; i++) norm += v[i] * v[i];\n norm = Math.sqrt(norm) || 1;\n return Array.from(v, (x) => x / norm);\n }\n}\n\nexport class HttpEmbedding extends EmbeddingProvider {\n /**\n * @param {object} cfg\n * @param {string} cfg.url e.g. https://api.openai.com/v1/embeddings\n * @param {string} [cfg.apiKey]\n * @param {string} [cfg.model] e.g. text-embedding-3-small\n * @param {number} cfg.dimensions the model's output dim (must match your index)\n * @param {number} [cfg.batchSize]\n */\n constructor(cfg) {\n super();\n if (!cfg?.url || !cfg?.dimensions) throw TroveError.invalid('HttpEmbedding requires url and dimensions');\n this.cfg = cfg;\n }\n get dimensions() {\n return this.cfg.dimensions;\n }\n async embed(texts) {\n const batchSize = this.cfg.batchSize ?? 64;\n const out = [];\n for (let i = 0; i < texts.length; i += batchSize) {\n const batch = texts.slice(i, i + batchSize);\n out.push(...(await this.#embedBatch(batch)));\n }\n return out;\n }\n async #embedBatch(batch) {\n return withRetry(async () => {\n let res;\n try {\n res = await fetch(this.cfg.url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...(this.cfg.apiKey ? { authorization: `Bearer ${this.cfg.apiKey}` } : {}),\n },\n body: JSON.stringify({ model: this.cfg.model, input: batch }),\n });\n } catch (err) {\n throw wrapError(err);\n }\n if (res.status === 429 || res.status >= 500) {\n throw TroveError.transient(`Embedding endpoint ${res.status}`);\n }\n if (!res.ok) throw TroveError.internal(`Embedding endpoint failed: ${res.status}`);\n const json = await res.json();\n // OpenAI shape: { data: [{ embedding: [...] }, ...] }\n const data = json.data ?? json.embeddings ?? [];\n return data.map((d) => d.embedding ?? d);\n });\n }\n}\n",
|
|
60
|
+
"// `trove:` — the URI that addresses an ITEM in the drive.\n//\n// There is no folder hierarchy. A collection is a flat namespace of uniquely-named\n// items, and structure comes from items linking to each other — a markdown document\n// that links its sources is what a folder used to be, except it can say *why* things\n// belong together, an item can appear in several of them, and the grouping is\n// searchable content rather than an invisible container.\n//\n// So a link has to be something a person can type into a markdown file:\n//\n// trove:default?name=sailing.txt canonical, by name\n// trove:default/sailing.txt shorthand for the same thing\n// trove:default?id=fil_01H2X8F4KQ by id — survives a rename, unreadable\n//\n// The selector is EXPLICIT (`?name=` vs `?id=`) rather than inferred from the shape\n// of the segment: a name that happens to look like an id would otherwise resolve to\n// something else entirely, and a link that silently retargets is worse than one that\n// visibly breaks. The `/name` shorthand is unambiguous for the same reason — that\n// slot only ever means a name.\n//\n// Names are unique per collection, which is what makes `?name=` resolve to exactly one\n// item. Renaming therefore breaks inbound links, on purpose and visibly: the alternative\n// is opaque ids nobody can hand-write, which would put linking back in the UI's hands.\n// `?id=` is there for links a tool inserts, where stability matters more than legibility.\n\nimport { TroveError } from './errors.js';\n\nexport const TROVE_SCHEME = 'trove:';\n\n// An item name is capped at 255 chars (isValidItemName), so a longer selector can never\n// resolve to anything. Refusing it at parse time keeps garbage out of the links index\n// rather than storing kilobytes that will never match.\nconst MAX_SELECTOR = 255;\n// How many distinct links one item may record. A document is a grouping, not a database\n// dump; past this the value is in the search index, not the link graph.\nexport const MAX_LINKS_PER_ITEM = 500;\n\n// Collection ids are slugs (see CollectionService); a name is anything but a slash,\n// since the pathname shorthand has to stop somewhere.\nconst COLLECTION_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;\n\n/**\n * Parse a `trove:` URI into `{ collection, by: 'name'|'id', value }`, or null if it\n * isn't one. Never throws — callers parse untrusted document text with this, where a\n * malformed link is a thing to render as broken, not an exception.\n */\nexport function parseTroveUri(uri) {\n if (typeof uri !== 'string' || !uri.toLowerCase().startsWith(TROVE_SCHEME)) return null;\n let url;\n try {\n url = new URL(uri);\n } catch {\n return null;\n }\n // `trove:default/sailing.txt` → pathname \"default/sailing.txt\"; the slash-free case\n // is `trove:default?name=…`, where the whole pathname is the collection.\n const path = url.pathname;\n const slash = path.indexOf('/');\n const collection = slash < 0 ? path : path.slice(0, slash);\n if (!COLLECTION_RE.test(collection)) return null;\n\n const id = url.searchParams.get('id');\n const name = url.searchParams.get('name');\n let shorthand = '';\n if (slash >= 0) {\n // A malformed percent-escape is a malformed link, not an exception to propagate\n // out of a document render.\n try { shorthand = decodeURIComponent(path.slice(slash + 1)); } catch { return null; }\n }\n\n // Both selectors at once is a contradiction, not something to silently pick between.\n if (id != null && (name != null || shorthand)) return null;\n const by = id != null ? 'id' : 'name';\n const value = id != null ? id : (name != null ? name : shorthand);\n if (!value || value.length > MAX_SELECTOR) return null;\n return { collection, by, value };\n}\n\n/** Whether `uri` is a well-formed trove: reference. */\nexport function isTroveUri(uri) {\n return parseTroveUri(uri) != null;\n}\n\n/**\n * Build a `trove:` URI. `by` defaults to 'name' — the legible form, which is what\n * gets written into documents; pass 'id' for a link that must survive a rename.\n */\nexport function troveUri(node, by = 'name') {\n const collection = node?.collectionId || 'default';\n if (by === 'id') {\n if (!node?.id) throw TroveError.invalid('trove: link by id needs a node id');\n return `${TROVE_SCHEME}${collection}?id=${encodeURIComponent(node.id)}`;\n }\n if (!node?.name) throw TroveError.invalid('trove: link by name needs a node name');\n return `${TROVE_SCHEME}${collection}?name=${encodeURIComponent(node.name)}`;\n}\n\n/**\n * Every distinct `trove:` reference in a block of text, in first-appearance order.\n *\n * Deliberately scans raw text rather than parsing markdown: a link is just as real in\n * an HTML `href`, a bare mention, or a front-matter list, and the point of extraction\n * is to know what an item references — not to reproduce one renderer's idea of a link.\n * Trailing punctuation is trimmed so `see trove:default/a.md.` doesn't capture the dot.\n */\nexport function extractTroveLinks(text, { limit = MAX_LINKS_PER_ITEM } = {}) {\n if (typeof text !== 'string' || !text) return [];\n const out = [];\n const seen = new Set();\n // Stop at whitespace and at the delimiters that wrap a URL in markdown/HTML.\n const re = /trove:[^\\s<>\"'`)\\]}]+/gi;\n for (const m of text.matchAll(re)) {\n if (out.length >= limit) break;\n const raw = m[0].replace(/[.,;:!?]+$/, '');\n const parsed = parseTroveUri(raw);\n if (!parsed) continue;\n const canonical = `${TROVE_SCHEME}${parsed.collection}?${parsed.by}=${encodeURIComponent(parsed.value)}`;\n if (seen.has(canonical)) continue;\n seen.add(canonical);\n out.push({ uri: canonical, raw, ...parsed });\n }\n return out;\n}\n\n/**\n * The canonical form of a reference, for storing and comparing. Two links that address\n * the same item by the same selector must produce the same string, or backlinks would\n * miss `trove:default/a.md` when the target stored `trove:default?name=a.md`.\n */\nexport function canonicalTroveUri(uri) {\n const p = parseTroveUri(uri);\n return p ? `${TROVE_SCHEME}${p.collection}?${p.by}=${encodeURIComponent(p.value)}` : null;\n}\n\n/** Both canonical forms an item can be addressed by — what a backlink query looks for. */\nexport function troveUrisFor(node) {\n const out = [];\n if (node?.collectionId && node?.name) out.push(troveUri(node, 'name'));\n if (node?.collectionId && node?.id) out.push(troveUri(node, 'id'));\n return out;\n}\n",
|
|
61
|
+
"// IndexerRegistry — server-side indexers that extract structured content from\n// files. An indexer declares which nodes it handles and, for each, produces a\n// *contribution* with up to three scopes:\n// - semanticTexts: text chunks that become embeddings + keyword entries (search)\n// - tags: filterable key/values (merged into the node's queryable tags)\n// - metadata: arbitrary structured data (e.g. an audiobook's chapter index)\n// Everything is namespaced under the indexer's `id`, so indexers never clobber one\n// another and an indexer's whole contribution can be removed independently.\n//\n// This complements *plugin* indexers, which run in the browser sandbox and push\n// the same contribution shape through the API under their own namespace.\n//\n// Two built-ins ship here: a text/markdown extractor (the reference indexer) and a\n// links extractor, which is what makes a drive with no folders navigable.\n\nimport { extname } from '../util.js';\nimport { extractTroveLinks } from '../links.js';\nimport { LINKS_CONTRIBUTOR, LINKS_KEY } from '../metadata/interface.js';\n\n/**\n * @typedef {object} Indexer\n * @property {string} id unique namespace\n * @property {string} [displayName]\n * @property {(node: object) => boolean} match\n * @property {(node: object, ctx: IndexContext) => Promise<Contribution>} index\n *\n * @typedef {object} Contribution\n * @property {Doc[]} [semanticTexts] text chunks → embeddings + keyword search\n * @property {object} [tags] filterable key/values, e.g. { language: 'en' }\n * @property {object} [metadata] arbitrary structured data, e.g. { chapters: [...] }\n *\n * @typedef {object} Doc\n * @property {string} [id] stable per (node, chunk); defaults to `${node.id}:${i}`\n * @property {string} text\n * @property {object} [fields] e.g. { title, page, chapter }\n *\n * @typedef {object} IndexContext\n * @property {() => Promise<Uint8Array>} readBytes\n * @property {() => Promise<string>} readText\n * @property {number} maxBytes\n */\n\nexport class IndexerRegistry {\n /** @param {{builtins?: boolean}} [opts] register the reference text indexer (default on) */\n constructor({ builtins = true } = {}) {\n this.indexers = new Map();\n if (builtins) {\n this.register(textIndexer);\n this.register(linksIndexer);\n }\n }\n register(indexer) {\n if (!indexer?.id || typeof indexer.index !== 'function') {\n throw new Error('Indexer requires an id and an index() function');\n }\n this.indexers.set(indexer.id, indexer);\n // Only unregister if THIS registration is still the live one. Indexer ids are\n // contribution URIs, which are account-independent, so a second account installing\n // the same package replaces the first's entry — and the first's stale closure then\n // deleted the second's, taking that account's indexer out of the drive-wide registry\n // and purging its contributions.\n return () => { if (this.indexers.get(indexer.id) === indexer) this.indexers.delete(indexer.id); };\n }\n unregister(id) {\n this.indexers.delete(id);\n }\n list() {\n return [...this.indexers.values()].map((i) => ({ id: i.id, displayName: i.displayName || i.id }));\n }\n matching(node) {\n return [...this.indexers.values()].filter((i) => {\n try {\n return i.match(node);\n } catch {\n return false;\n }\n });\n }\n}\n\n// --- Reference indexer: plain text / markdown / code ------------------------\n\nconst TEXT_EXTS = new Set([\n '.txt', '.md', '.markdown', '.rst', '.log', '.csv', '.tsv',\n '.json', '.yaml', '.yml', '.toml', '.ini', '.env',\n '.js', '.mjs', '.ts', '.jsx', '.tsx', '.py', '.rb', '.go', '.rs', '.java',\n '.c', '.h', '.cpp', '.css', '.html', '.xml', '.sh',\n]);\n\nfunction isTexty(node) {\n if (node.contentType?.startsWith('text/')) return true;\n return TEXT_EXTS.has(extname(node.name));\n}\n\nexport const textIndexer = {\n id: 'core.text',\n displayName: 'Text & code',\n match: isTexty,\n async index(node, ctx) {\n const text = await ctx.readText();\n if (!text.trim()) return { semanticTexts: [] };\n const chunks = chunkText(text, 1200, 200);\n // Fields describe the CHUNK (which chunk it is), not the node. The node's name\n // deliberately isn't copied in: field values are indexed, so a name duplicated\n // across every chunk goes stale the moment the item is renamed — leaving the file\n // findable under a name it no longer has. Name search belongs to the `core.name`\n // index, which is a single document and is refreshed on rename.\n const semanticTexts = chunks.map((chunk, i) => ({\n id: `${node.id}:${i}`,\n text: chunk,\n fields: { chunk: i },\n }));\n return {\n semanticTexts,\n metadata: { chars: text.length, chunks: chunks.length, excerpt: text.slice(0, 280) },\n };\n },\n};\n\n// --- Links indexer: what this item points at --------------------------------\n//\n// With no folder hierarchy, an item's place in the drive is defined by what links to\n// it. This indexer records the outbound `trove:` URIs found in an item's text, which\n// is what MetadataStore.findLinksTo reads to answer the inverse question — \"what\n// gathers this up?\" — for the backlinks panel.\n//\n// It stores the links as `metadata`, not `semanticTexts`: a URI is an edge, not prose,\n// and embedding it would only pollute semantic results. `links` is also surfaced as a\n// tag count so `#links > 0` finds the documents that act as indexes.\n\nexport const linksIndexer = {\n id: LINKS_CONTRIBUTOR,\n displayName: 'Links',\n match: isTexty,\n async index(node, ctx) {\n const text = await ctx.readText();\n const links = extractTroveLinks(text).map((l) => l.uri);\n return {\n metadata: { [LINKS_KEY]: links },\n tags: { links: links.length },\n };\n },\n};\n\n/** Split into overlapping windows on sentence/line boundaries where possible. */\nexport function chunkText(text, size, overlap) {\n const out = [];\n let i = 0;\n while (i < text.length) {\n let end = Math.min(i + size, text.length);\n if (end < text.length) {\n // Prefer to break at a paragraph/sentence boundary within the last 25%.\n const window = text.slice(i, end);\n const brk = Math.max(window.lastIndexOf('\\n\\n'), window.lastIndexOf('. '), window.lastIndexOf('\\n'));\n if (brk > size * 0.75) end = i + brk + 1;\n }\n out.push(text.slice(i, end).trim());\n if (end >= text.length) break;\n i = end - overlap;\n }\n return out.filter(Boolean);\n}\n",
|
|
62
|
+
"// KeywordStore — the pluggable lexical (sparse) half of hybrid search, mirroring\n// VectorStore's async, injectable shape. The in-memory default keeps a token set\n// per document and scores by query-term coverage; swap in an implementation over\n// SQLite FTS5, Postgres tsvector, Elasticsearch, etc. for durability/scale.\n// SearchService composes a VectorStore + a KeywordStore, so a deployment can mix\n// (e.g. Qdrant for vectors, Postgres FTS for keywords) — core hardcodes neither.\n\nimport { TroveError } from '../errors.js';\n\nconst STOP = new Set('a an the of to in on for and or is are be as at by with from this that it'.split(' '));\n\nexport function tokenize(text) {\n return String(text)\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, ' ')\n .split(/\\s+/)\n .filter((t) => t.length > 1 && !STOP.has(t));\n}\n\nexport class KeywordStore {\n async add(docs) {\n throw TroveError.unsupported('add not implemented');\n }\n async removeByNode(nodeId) {\n throw TroveError.unsupported('removeByNode not implemented');\n }\n async removeByIndexer(indexerId) {\n throw TroveError.unsupported('removeByIndexer not implemented');\n }\n async removeByNodeIndexer(nodeId, indexerId) {\n throw TroveError.unsupported('removeByNodeIndexer not implemented');\n }\n /** @returns {Promise<Array<{docId,nodeId,indexerId,score,fields}>>} */\n async search(query, opts) {\n throw TroveError.unsupported('search not implemented');\n }\n /** Optional: a highlighted excerpt for a doc (null if unavailable). */\n async snippet(docId, query) {\n return null;\n }\n /** Optional: number of stored documents. Null when the store can't say — which is\n * not the same as zero, and the startup rebuild check depends on the difference. */\n async count() {\n return null;\n }\n}\n\nexport class MemoryKeywordStore extends KeywordStore {\n constructor() {\n super();\n this.docs = new Map(); // docId -> { nodeId, indexerId, text, tokens:Set, fields }\n }\n\n async add(docs) {\n for (const d of docs) {\n const text = d.text || '';\n this.docs.set(d.id, {\n nodeId: d.nodeId, indexerId: d.indexerId, text,\n tokens: new Set(tokenize(text + ' ' + Object.values(d.fields || {}).join(' '))),\n fields: d.fields || {},\n });\n }\n }\n async removeByNode(nodeId) {\n for (const [id, r] of this.docs) if (r.nodeId === nodeId) this.docs.delete(id);\n }\n async removeByIndexer(indexerId) {\n for (const [id, r] of this.docs) if (r.indexerId === indexerId) this.docs.delete(id);\n }\n async removeByNodeIndexer(nodeId, indexerId) {\n for (const [id, r] of this.docs) if (r.nodeId === nodeId && r.indexerId === indexerId) this.docs.delete(id);\n }\n\n async search(query, opts = {}) {\n const q = tokenize(query);\n if (!q.length) return [];\n const allow = opts.indexers ? new Set(opts.indexers) : null;\n const out = [];\n for (const [docId, r] of this.docs) {\n if (allow && !allow.has(r.indexerId)) continue;\n let hit = 0;\n for (const t of q) if (r.tokens.has(t)) hit++;\n if (hit > 0) out.push({ docId, nodeId: r.nodeId, indexerId: r.indexerId, score: hit / q.length, fields: r.fields });\n }\n out.sort((a, b) => b.score - a.score);\n return out.slice(0, opts.limit ?? 40);\n }\n\n async snippet(docId, query) {\n const r = this.docs.get(docId);\n if (!r?.text) return null;\n const q = tokenize(query);\n const lower = r.text.toLowerCase();\n let at = -1;\n for (const t of q) {\n const i = lower.indexOf(t);\n if (i >= 0) { at = i; break; }\n }\n if (at < 0) return r.text.slice(0, 160).trim();\n const start = Math.max(0, at - 60);\n return (start > 0 ? '…' : '') + r.text.slice(start, start + 200).trim() + '…';\n }\n\n async count() {\n return this.docs.size;\n }\n}\n",
|
|
63
|
+
"// One place that knows how to classify a file by type — extension extraction and a\n// single format table. Previously the ext/mime lists were re-derived (and drifted)\n// across openers.extOf, contributions.matchesSelector, iconForNode, and offline.isTexty;\n// now the icon shown, the \"is this text?\" decision, and the type label all agree.\n\n/** The extension (\".pdf\") for a node, lowercased, or '' if none. */\nexport function extOf(node) {\n const name = (node?.name || '').toLowerCase();\n return name.includes('.') ? name.slice(name.lastIndexOf('.')) : '';\n}\n\n// Category → { mime: prefixes/exact it matches, ext: extensions }. Order matters:\n// kindOf checks these top-down.\nconst CATEGORIES = [\n { kind: 'audio', icon: 'file-audio', mime: ['audio/'], ext: ['.mp3', '.flac', '.wav', '.opus', '.ogg', '.m4a', '.m4b'] },\n { kind: 'image', icon: 'file-image', mime: ['image/'], ext: ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.avif'] },\n { kind: 'video', icon: 'file-video', mime: ['video/'], ext: ['.mp4', '.webm', '.mkv', '.mov'] },\n {\n kind: 'text', icon: 'file-text', mime: ['text/', 'application/json'],\n ext: ['.txt', '.md', '.markdown', '.json', '.js', '.mjs', '.ts', '.jsx', '.tsx', '.css', '.html',\n '.xml', '.yaml', '.yml', '.toml', '.ini', '.log', '.csv', '.py', '.rb', '.go', '.rs', '.sh', '.c', '.h', '.cpp', '.java'],\n },\n];\n\nfunction matchesCategory(cat, ext, ct) {\n if (cat.ext.includes(ext)) return true;\n // A mime entry ending in '/' is a prefix (audio/), otherwise an exact type.\n return cat.mime.some((m) => (m.endsWith('/') ? ct.startsWith(m) : ct === m));\n}\n\n/** Classify an item → 'audio'|'image'|'video'|'text'|'file'. */\nexport function kindOf(node) {\n const ext = extOf(node);\n const ct = node?.contentType || '';\n for (const cat of CATEGORIES) if (matchesCategory(cat, ext, ct)) return cat.kind;\n return 'file';\n}\n\nconst ICONS = { ...Object.fromEntries(CATEGORIES.map((c) => [c.kind, c.icon])), file: 'file' };\n\n/** The icon name for a node, derived from its kind. */\nexport function iconForKind(node) {\n return ICONS[kindOf(node)] || 'file';\n}\n\n/** Whether a node holds indexable text (drives offline text extraction). */\nexport function isTexty(node) {\n return kindOf(node) === 'text';\n}\n",
|
|
64
|
+
"// OfflineService — a limited but real offline mode.\n// • Pin (\"make available offline\"): file bytes go into the SW's trove-files\n// cache (so every opener reads them with no network), and — for text — the\n// content is chunked, embedded with the same LocalHash model the server can\n// use, and stored in IndexedDB for offline search.\n// • Offline search: lexical (names/tags/content) blended with local\n// semantic (cosine over the cached chunk vectors) — the same hybrid shape as\n// the server, just over what you took offline.\n// • Sidecar op queue: while offline, comment/tag mutations are queued and\n// replayed on reconnect. Because the sidecar is a CRDT, replay merges cleanly\n// with whatever changed server-side meanwhile.\n// Reactive: the workbench watches `online`, the pinned set, and the queue depth.\n\nimport { cell } from '../runtime.js';\nimport { LocalHashEmbedding } from '@3sln/trove/core/search/embeddings.js';\n// Chunking and tokenizing must MATCH the server's — offline results are compared\n// against embeddings the server produced, so a local variant would quietly skew them.\nimport { chunkText } from '@3sln/trove/core/indexers/registry.js';\nimport { tokenize } from '@3sln/trove/core/search/keywordStore.js';\nimport { isTexty } from './fileType.js';\n\nconst DB = 'trove-offline';\nconst FILES_CACHE = 'trove-files-v1';\nconst embed = new LocalHashEmbedding({ dimensions: 256 });\n\nexport class OfflineService {\n constructor(platform) {\n this.platform = platform;\n this.api = platform.api;\n this.db = null;\n this.state = { online: navigator.onLine, pins: [], queued: 0, syncing: false };\n this.cell = cell(this.state);\n }\n observe() {\n return this.cell;\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n\n async init() {\n this.db = await openDb();\n await this.#refreshPins();\n await this.#refreshQueue();\n window.addEventListener('online', () => this.#onOnline());\n window.addEventListener('offline', () => this.#onOffline());\n // `navigator.onLine` only reflects that an interface is up — on a captive portal or\n // dead uplink it's `true` while the server is unreachable, which would leave us\n // falsely \"online\" with every request silently failing. Confirm real reachability.\n if (this.state.online && !(await this.api.reachable())) this.#set({ online: false });\n // Tell the plugin host our initial connectivity so it can probe plugins.\n this.platform.plugins?.setOnline?.(this.state.online);\n if (this.state.online) this.flushQueue();\n }\n\n async #onOnline() {\n // The interface came up, but verify the server is actually reachable before\n // declaring online — a captive portal fires 'online' with no real connectivity.\n if (!(await this.api.reachable())) { this.#set({ online: false }); await this.platform.plugins?.setOnline?.(false); return; }\n this.#set({ online: true });\n // Plugins re-announce what works now that we're back online.\n await this.platform.plugins?.setOnline?.(true);\n await this.flushQueue();\n }\n async #onOffline() {\n this.#set({ online: false });\n // Plugins re-announce; network-only features become unavailable.\n await this.platform.plugins?.setOnline?.(false);\n }\n\n isPinned(id) {\n return this.state.pins.some((p) => p.id === id);\n }\n\n // --- pinning ---------------------------------------------------------------\n\n async pin(node) {\n if (!node?.id) return;\n try {\n // Two URLs, deliberately. The KEY is the plain route URL — stable, so `unpin` and\n // the service worker's cache-first lookup both find what `pin` stored. The FETCH\n // may need a minted one, because `cache.add` sends no Authorization header and\n // would 401 on a token-authenticated deployment. Keying on the minted URL instead\n // would have worked exactly once: it carries a signature, so the next session's\n // `unpin` looks for a string that was never there and the bytes are never reclaimed.\n if ('caches' in window) {\n const cache = await caches.open(FILES_CACHE);\n const urls = this.platform.mediaUrls;\n const key = urls.cacheKey(node.id);\n const { url } = await urls.url(node.id, { op: 'download' });\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Couldn't fetch the file (${res.status})`);\n await cache.put(new Request(key), res);\n }\n // Index text content for offline search.\n let chunks = [];\n let text = '';\n if (isTexty(node)) {\n try {\n text = await this.api.readText(node.id);\n const parts = chunkText(text, 1000, 150);\n const vectors = await embed.embed(parts.length ? parts : [node.name]);\n chunks = (parts.length ? parts : [node.name]).map((t, i) => ({ text: t, vector: vectors[i] }));\n } catch { /* keep name-only */ }\n }\n const nameVec = (await embed.embed([node.name]))[0];\n await idbPut(this.db, 'pins', node.id, { node, text, chunks, nameVec, pinnedAt: Date.now() });\n await this.#refreshPins();\n this.platform.notifications.success(`“${node.name}” is available offline`);\n } catch (err) {\n this.platform.notifications.error(`Couldn't make available offline: ${err.message}`);\n }\n }\n\n async unpin(id) {\n const pin = this.state.pins.find((p) => p.id === id);\n if (pin && 'caches' in window) {\n const cache = await caches.open(FILES_CACHE);\n // The same stable key `pin` stored under — never a freshly minted URL.\n await cache.delete(this.platform.mediaUrls.cacheKey(id), { ignoreVary: true }).catch(() => {});\n }\n await idbDelete(this.db, 'pins', id);\n await this.#refreshPins();\n }\n\n async #refreshPins() {\n const all = await idbAll(this.db, 'pins');\n this.#set({ pins: all.map((r) => ({ id: r.node.id, name: r.node.name, contentType: r.node.contentType, collectionId: r.node.collectionId, pinnedAt: r.pinnedAt })) });\n }\n\n // --- offline search --------------------------------------------------------\n\n async searchOffline(query, { limit = 40 } = {}) {\n const rows = await idbAll(this.db, 'pins');\n if (!rows.length) return [];\n const qTokens = tokenize(query);\n const qv = (await embed.embed([query]))[0];\n const results = [];\n for (const r of rows) {\n const hay = tokenize(`${r.node.name} ${r.text || ''}`);\n const lex = lexicalScore(qTokens, new Set(hay));\n let dense = cosine(qv, r.nameVec);\n for (const c of r.chunks || []) dense = Math.max(dense, cosine(qv, c.vector));\n const score = 0.6 * dense + 0.4 * lex;\n if (score > 0.02) results.push({ nodeId: r.node.id, node: r.node, score, snippet: snippet(r.text, qTokens), indexerId: 'offline' });\n }\n return results.sort((a, b) => b.score - a.score).slice(0, limit);\n }\n\n // --- offline sidecar op queue ---------------------------------------------\n\n async queueOp(op) {\n await idbAdd(this.db, 'queue', { ...op, at: Date.now() });\n await this.#refreshQueue();\n }\n\n async #refreshQueue() {\n const q = await idbAll(this.db, 'queue');\n this.#set({ queued: q.length });\n }\n\n async flushQueue() {\n if (!this.state.online || this.state.syncing) return;\n // Claim the sync BEFORE any await — two 'online' events (or init + an event) can\n // fire concurrently, and a check-then-act guard after an await let both pass and\n // replay the queue twice (double-posting). Setting syncing synchronously here makes\n // the second caller bail at the guard above.\n this.#set({ syncing: true });\n let synced = 0;\n let dropped = 0;\n try {\n const q = await idbAllWithKeys(this.db, 'queue');\n for (const { key, value } of q) {\n try {\n await this.api.request(value.method, value.path, { body: value.body });\n await idbDelete(this.db, 'queue', key);\n synced++;\n } catch (err) {\n // Anything that could succeed later stays queued. `unauthorized` is the case\n // that mattered: a session expiring while offline is not a permanent failure,\n // and dropping the entry destroyed a comment the user had written and told\n // them it \"couldn't be applied\" — when signing in again would have applied it.\n if (['transient', 'timeout', 'unauthorized', 'quota'].includes(err.code)) break;\n // A permanent failure (e.g. the file was deleted) — drop it so we don't loop,\n // but don't pretend it synced.\n await idbDelete(this.db, 'queue', key);\n dropped++;\n }\n }\n } finally {\n await this.#refreshQueue();\n this.#set({ syncing: false });\n // Be honest: only claim success for what actually applied, and surface drops.\n if (dropped) this.platform.notifications.warn(`${dropped} offline change${dropped > 1 ? 's' : ''} couldn't be applied and ${dropped > 1 ? 'were' : 'was'} discarded.`);\n if (synced && this.state.queued === 0) this.platform.notifications.info(`${synced} offline change${synced > 1 ? 's' : ''} synced.`);\n }\n }\n}\n\n// --- IndexedDB helpers ------------------------------------------------------\n\nfunction openDb() {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains('pins')) db.createObjectStore('pins');\n if (!db.objectStoreNames.contains('queue')) db.createObjectStore('queue', { autoIncrement: true });\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\nfunction tx(db, store, mode, fn) {\n return new Promise((resolve, reject) => {\n const t = db.transaction(store, mode);\n const s = t.objectStore(store);\n let out;\n Promise.resolve(fn(s)).then((r) => (out = r));\n t.oncomplete = () => resolve(out);\n t.onerror = () => reject(t.error);\n });\n}\nconst reqP = (r) => new Promise((res, rej) => { r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); });\nconst idbPut = (db, store, key, val) => tx(db, store, 'readwrite', (s) => s.put(val, key));\nconst idbAdd = (db, store, val) => tx(db, store, 'readwrite', (s) => s.add(val));\nconst idbDelete = (db, store, key) => tx(db, store, 'readwrite', (s) => s.delete(key));\nconst idbAll = (db, store) => tx(db, store, 'readonly', (s) => reqP(s.getAll()));\nfunction idbAllWithKeys(db, store) {\n return tx(db, store, 'readonly', (s) =>\n new Promise((resolve, reject) => {\n const out = [];\n const req = s.openCursor();\n req.onsuccess = () => {\n const cur = req.result;\n if (!cur) return resolve(out);\n out.push({ key: cur.key, value: cur.value });\n cur.continue();\n };\n req.onerror = () => reject(req.error);\n }),\n );\n}\n\n// --- search math (mirrors the server's LocalHash hybrid) --------------------\n\nfunction lexicalScore(q, docTokens) {\n if (!q.length || !docTokens.size) return 0;\n let hit = 0;\n for (const t of q) if (docTokens.has(t)) hit++;\n return hit / q.length;\n}\nfunction cosine(a, b) {\n if (!a || !b) return 0;\n let s = 0;\n for (let i = 0; i < a.length; i++) s += a[i] * b[i];\n return s; // both L2-normalised\n}\nfunction snippet(text, qTokens) {\n if (!text) return null;\n const lower = text.toLowerCase();\n let at = -1;\n for (const t of qTokens) { const i = lower.indexOf(t); if (i >= 0) { at = i; break; } }\n if (at < 0) return text.slice(0, 160).trim();\n const start = Math.max(0, at - 60);\n return (start > 0 ? '…' : '') + text.slice(start, start + 200).trim() + '…';\n}\n",
|
|
65
|
+
"// ActivityService — the client's answer to \"what is going on, and what is stuck\".\n//\n// It holds two lists that look similar and mean different things, and keeping them\n// distinct is the whole design:\n//\n// TASKS work happening now. Ephemeral. A task disappears when it finishes (after a\n// moment on screen) and when the process running it stops. That is correct:\n// the work stopped too.\n// ISSUES a standing problem left behind by something that already failed. Durable on\n// the server. It stays until the thing it complains about actually works —\n// not until it is acknowledged, and not until a reload.\n//\n// WHY ONE SERVICE FOR BOTH SIDES. Work happens in two places — an upload runs in this\n// browser, a reindex runs on the server — and a user does not care which. So this holds\n// one list, with each task tagged by where it lives:\n//\n// local started here, driven here. The owner (TransfersService) reports progress\n// directly, and cancelling calls a function.\n// server a read-only MIRROR of the server's registry. Nothing here drives it;\n// cancelling posts, and the next poll shows the result. Treating a mirror as\n// writable is how two sources of truth get invented.\n//\n// WHY POLLING. There is no streaming transport in this server, and adding SSE through\n// three runtime adapters to move a progress bar is not a trade worth making yet. So:\n// poll fast (1s) only while something is running, drop to nothing when the drive is\n// idle, and re-check when the tab regains focus. At rest this costs one request when\n// the app loads. If a real-time channel ever exists, only `#poll` changes.\n\nimport { cell } from '../runtime.js';\n\nconst ACTIVE_MS = 1000; // while something is running, we want a live-feeling bar\nconst IDLE_MS = 60_000; // otherwise just enough to notice a task someone else started\n\nexport class ActivityService {\n constructor(platform) {\n this.platform = platform;\n this.api = platform.api;\n this.state = {\n tasks: [], // merged: local first, then the server mirror\n issues: [],\n issuesLoading: false,\n // A load failure is state, not a shrug: \"no problems\" and \"we couldn't ask\" look\n // identical on screen unless one of them says so. TWO fields, not one, because\n // these are independent requests — a tasks poll that succeeds must not erase the\n // news that the issues poll failed, which is exactly the bug one shared field\n // produced.\n tasksError: null,\n issuesError: null,\n open: false, // the activity panel\n };\n this.cell = cell(this.state);\n this._local = new Map(); // id -> local task (this browser owns these)\n this._server = [];\n this._timer = null;\n this._pollMs = null;\n }\n\n observe() {\n return this.cell;\n }\n #set(patch) {\n this.state = { ...this.state, ...patch };\n this.cell.setValue(this.state);\n }\n #merge() {\n this.#set({ tasks: [...this._local.values(), ...this._server] });\n }\n\n // --- local tasks -----------------------------------------------------------\n\n /**\n * Register work happening in this browser. Returns a handle with the same shape the\n * server's registry hands its callers, so code that reports progress doesn't need to\n * know which side it is running on.\n *\n * @param {{title: string, kind?: string, detail?: string, total?: number|null,\n * unit?: string, onCancel?: () => void}} spec\n */\n start(spec = {}) {\n const id = spec.id || `local_${Math.random().toString(36).slice(2, 10)}`;\n const task = {\n id,\n source: 'local',\n kind: spec.kind || 'general',\n title: spec.title || 'Working',\n detail: spec.detail ?? null,\n status: 'running',\n done: 0,\n // No total means indeterminate, and that is a legitimate answer. A caller that\n // doesn't know how much work there is must not invent a number: a progress bar\n // that lies is worse than a spinner that admits it doesn't know.\n total: spec.total ?? null,\n unit: spec.unit || null,\n cancellable: !!spec.onCancel,\n startedAt: Date.now(),\n endedAt: null,\n error: null,\n };\n this._local.set(id, task);\n this._cancels ||= new Map();\n if (spec.onCancel) this._cancels.set(id, spec.onCancel);\n this.#merge();\n this.#schedule();\n\n const patch = (p) => {\n const cur = this._local.get(id);\n if (!cur || cur.status !== 'running') return; // finished tasks are final\n this._local.set(id, { ...cur, ...p });\n this.#merge();\n };\n return {\n id,\n progress: (p = {}) => patch({\n done: p.done ?? this._local.get(id)?.done ?? 0,\n total: p.total === undefined ? this._local.get(id)?.total : p.total,\n unit: p.unit ?? this._local.get(id)?.unit,\n detail: p.detail ?? this._local.get(id)?.detail,\n }),\n succeed: (detail) => { patch({ status: 'done', endedAt: Date.now(), detail: detail ?? this._local.get(id)?.detail }); this.#expire(id); },\n fail: (err) => { patch({ status: 'failed', endedAt: Date.now(), error: err?.message || String(err || 'failed') }); },\n cancel: () => { patch({ status: 'cancelled', endedAt: Date.now() }); this.#expire(id); },\n };\n }\n\n /** A finished task lingers briefly so it is seen to finish, then goes. */\n #expire(id, after = 5000) {\n setTimeout(() => {\n this._local.delete(id);\n this._cancels?.delete(id);\n this.#merge();\n }, after);\n }\n\n async cancel(id) {\n const local = this._local.get(id);\n if (local) {\n this._cancels?.get(id)?.();\n return;\n }\n // A server task is a mirror: ask, then let the next poll tell the truth.\n try {\n await this.api.cancelTask(id);\n await this.refreshTasks();\n } catch (err) {\n this.#set({ tasksError: err.message });\n }\n }\n\n dismiss(id) {\n if (this._local.delete(id)) { this.#merge(); return; }\n this.api.dismissTask(id).catch(() => {});\n this._server = this._server.filter((t) => t.id !== id);\n this.#merge();\n }\n\n // --- server mirror + issues ------------------------------------------------\n\n async init() {\n await this.refresh();\n this.#schedule();\n // Coming back to the tab is the moment a stale list is most obvious.\n window.addEventListener('focus', () => this.refresh());\n }\n\n async refresh() {\n await Promise.all([this.refreshTasks(), this.refreshIssues()]);\n }\n\n async refreshTasks() {\n const wasRunning = this.running.length > 0;\n try {\n const { tasks } = await this.api.tasks();\n this._server = (tasks || []).map((t) => ({ ...t, source: 'server' }));\n this.#set({ tasksError: null });\n this.#merge();\n } catch (err) {\n // Offline or a server that doesn't have the endpoint: stop claiming to know.\n this._server = [];\n this.#set({ tasksError: err.message });\n this.#merge();\n }\n // The moment work STOPS is the moment an issue is most likely to have been fixed,\n // so re-read them then. Without this, a retry that succeeds in 50 ms leaves the\n // problem on screen until the next idle poll — up to a minute of the user being\n // told something is broken that isn't.\n if (wasRunning && !this.running.length) this.refreshIssues();\n this.#schedule();\n }\n\n async refreshIssues() {\n this.#set({ issuesLoading: true });\n try {\n const { issues } = await this.api.issues();\n this.#set({ issues: issues || [], issuesLoading: false, issuesError: null });\n } catch (err) {\n // Deliberately NOT clearing `issues`: the last known list is better than an empty\n // one that reads as \"nothing is wrong\".\n this.#set({ issuesLoading: false, issuesError: err.message });\n }\n }\n\n async retryIssue(id) {\n try {\n await this.api.retryIssue(id);\n // The fix runs as a task; adopt it now so the click visibly did something, and\n // let the poll clear the issue when the work actually succeeds. The retry may\n // finish faster than a poll interval, so re-read the issues directly too — a\n // Retry button that leaves the problem sitting there reads as a broken button.\n await this.refreshTasks();\n // A retry can finish faster than one poll interval — in which case there is no\n // running→idle transition to notice and the fixed problem would sit on screen\n // until the next idle poll. A couple of bounded follow-ups cover that window\n // without turning this into a busy loop.\n this.#followUp();\n this.platform.notifications?.info?.('Retrying — watch its progress in Activity');\n } catch (err) {\n this.platform.notifications?.error?.(`Couldn't retry: ${err.message}`);\n }\n }\n\n async dismissIssue(id) {\n const before = this.state.issues;\n this.#set({ issues: before.filter((i) => i.id !== id) });\n try {\n await this.api.dismissIssue(id);\n } catch (err) {\n this.#set({ issues: before }); // put it back rather than pretend\n this.platform.notifications?.error?.(`Couldn't dismiss: ${err.message}`);\n }\n }\n\n /** Two bounded re-reads after an action that may have resolved something. */\n #followUp() {\n for (const ms of [400, 1500]) {\n const t = setTimeout(() => { this.refreshTasks(); this.refreshIssues(); }, ms);\n t?.unref?.();\n }\n }\n\n /** Rebuild the whole search index. Surfaced as a command; reports as a task. */\n async rebuildIndex() {\n try {\n const res = await this.api.reindex();\n await this.refreshTasks();\n this.togglePanel(true);\n if (res.alreadyRunning) this.platform.notifications?.info?.('A rebuild is already running');\n return res;\n } catch (err) {\n this.platform.notifications?.error?.(`Couldn't rebuild the index: ${err.message}`);\n throw err;\n }\n }\n\n /**\n * Reconcile a collection with its object store — the way a drive notices files added,\n * replaced, or removed by anything that isn't Trove. Reports as a task, like a rebuild.\n */\n async scanCollection(collectionId) {\n try {\n const res = await this.api.scanCollection(collectionId);\n await this.refreshTasks();\n this.togglePanel(true);\n if (res.alreadyRunning) this.platform.notifications?.info?.('A scan of this collection is already running');\n this.#followUp();\n return res;\n } catch (err) {\n this.platform.notifications?.error?.(`Couldn't scan “${collectionId}”: ${err.message}`);\n throw err;\n }\n }\n\n togglePanel(open) {\n this.#set({ open: open ?? !this.state.open });\n if (this.state.open) this.refresh();\n }\n\n get running() {\n return this.state.tasks.filter((t) => t.status === 'running');\n }\n\n /**\n * Poll fast while something is running, slowly otherwise. Rescheduled rather than\n * fixed, so an idle drive costs one request a minute and a running rebuild feels\n * live — without a second transport to maintain.\n */\n #schedule() {\n const want = this.running.length ? ACTIVE_MS : IDLE_MS;\n if (this._timer && this._pollMs === want) return;\n clearTimeout(this._timer);\n this._pollMs = want;\n this._timer = setTimeout(() => {\n this._timer = null;\n this.refreshTasks().then(() => {\n // Issues change when tasks end, so re-read them then rather than on their own\n // clock — a retry that fixed something should stop being listed promptly.\n if (!this.running.length) this.refreshIssues();\n });\n }, want);\n this._timer?.unref?.();\n }\n\n dispose() {\n clearTimeout(this._timer);\n this._timer = null;\n }\n}\n",
|
|
66
|
+
"// One tag matcher, for everybody.\n//\n// There were four: this logic in `transformer.js` (post-filtering semantic results), a\n// near-copy in the memory metadata store, a third in the browser (used by the offline\n// path), and SQL in the sqlite store. Each pair \"mirrored\" the others in a comment and\n// diverged in fact:\n//\n// - the client lowercased both operands for `<`/`<=`/`>`/`>=`; the server did not, so\n// `#author:>Bob` against `author: \"alice\"` was false online and true offline;\n// - the sqlite store bound `String(value)` against `json_extract`, which preserves JSON\n// types, so a numeric tag `pages: 120` matched `#pages:!=120` and not `#pages:120`;\n// - the sqlite store treated `present` as `IS NOT NULL`, so a tag explicitly set to\n// `false` counted as present — everywhere else it does not;\n// - the sqlite store never consulted `meta`, which the interface documents as part of\n// what a filter matches.\n//\n// So the semantics live here, once, and the SQL in `metadata/sqlite.js` is written to\n// agree with THIS file rather than with its own history.\n//\n// The rules:\n// present the tag exists and is not `false` and not `''`\n// = / != numeric compare when BOTH sides are numbers, else case-insensitive text\n// < <= > >= same, and text comparison is case-insensitive (it was not on the server,\n// which is the one difference a user could actually observe between an\n// online and an offline search)\n\n/** A node's filterable properties: `meta`, overlaid by its merged tags. */\nexport function tagProps(node, mergedTags = null) {\n return { ...(node?.meta || {}), ...(mergedTags || node?.tags || {}) };\n}\n\n/** Does one value satisfy one parsed filter? */\nexport function matchesFilter(value, filter) {\n if (filter.present) return value != null && value !== false && value !== '';\n if (value == null) return false;\n const na = Number(value);\n const nb = Number(filter.value);\n // `Number('')` is 0, which would make an empty string compare as a number — hence\n // the explicit emptiness checks rather than `!Number.isNaN` alone.\n const numeric = value !== '' && value !== true && value !== false\n && filter.value !== '' && filter.value != null\n && !Number.isNaN(na) && !Number.isNaN(nb);\n const x = numeric ? na : String(value).toLowerCase();\n const y = numeric ? nb : String(filter.value).toLowerCase();\n switch (filter.op) {\n case '!=': return x !== y;\n case '<': return x < y;\n case '<=': return x <= y;\n case '>': return x > y;\n case '>=': return x >= y;\n default: return x === y;\n }\n}\n\n/** Does a node satisfy every filter? `mergedTags` when the caller already computed them. */\nexport function matchTagFilters(node, filters, mergedTags = null) {\n const props = tagProps(node, mergedTags);\n return (filters || []).every((f) => matchesFilter(props[f.key], f));\n}\n",
|
|
67
|
+
"// Parse `#tag` / `#key:op:value` filter tokens out of a launcher query and match\n// nodes against them. Grammar (each token, space-separated):\n// #tag → the property/tag is present\n// #key:=v #key:!=v #key:<v #key:<=v #key:>v #key:>=v → compare\n// #key:v → equality shorthand (same as #key:=v)\n// Values may be \"quoted\" to include spaces. Comparisons are numeric when both\n// sides parse as numbers, otherwise case-insensitive string comparisons.\n\nimport { matchTagFilters } from '@3sln/trove/core/search/tagMatch.js';\n\nconst TOKEN = /#([\\w.-]+)(?::(<=|>=|!=|=|<|>)?(\"[^\"]*\"|[^#\\s]+)?)?/g;\n\n/** @returns {{ text: string, filters: {key,op,value,present}[] }} */\nexport function parseTagQuery(query) {\n const filters = [];\n let text = query;\n for (const m of query.matchAll(TOKEN)) {\n const [, key, op, rawValue] = m;\n const value = rawValue && rawValue.startsWith('\"') ? rawValue.slice(1, -1) : rawValue;\n filters.push(value == null || value === ''\n ? { key, present: true }\n : { key, op: op || '=', value, present: false });\n text = text.replace(m[0], ' ');\n }\n return { text: text.replace(/\\s+/g, ' ').trim(), filters };\n}\n\n/**\n * True if `node` satisfies ALL filters (AND).\n *\n * Delegates to core rather than re-implementing. This file had its own copy, and the\n * two drifted on the ordering operators — the client lowercased both operands, the\n * server did not — so `#author:>Bob` against `author: \"alice\"` answered differently\n * online and offline. Same query, same drive, different files.\n */\nexport function matchesTagFilters(node, filters) {\n if (!filters || !filters.length) return true;\n return matchTagFilters(node, filters);\n}\n\n/** Short human label for a filter (for the results heading). */\nexport function filterLabel(f) {\n return f.present ? `#${f.key}` : `#${f.key}:${f.op}${f.value}`;\n}\n",
|
|
68
|
+
"// Opener choice: which viewer opens a file when several can. Built-in and plugin\n// openers register the same way (a selector + priority), so a file can match more\n// than one — e.g. a plugin PDF viewer alongside a built-in, or a rich player vs the\n// plain audio player for an .m4a. When that happens and the user hasn't expressed a\n// preference, we ask; their choice can be remembered per file type.\n//\n// Preferences live in settings under `openers.associations`: a map from a *type key*\n// (an extension like \".pdf\", an exact mime, or a \"type/*\" wildcard) to an opener id.\n\nimport { extOf } from './fileType.js';\n\nconst ASSOC_KEY = 'openers.associations';\n\nexport { extOf };\n\n/** The canonical type key we remember a choice under: prefer extension, else mime. */\nexport function typeKeyFor(node) {\n return extOf(node) || node?.contentType || '';\n}\n\n/** A human label for the type, for \"Always use … for {label}\". */\nexport function typeLabelFor(node) {\n const ext = extOf(node);\n if (ext) return `${ext} files`;\n const mime = node?.contentType || '';\n return mime ? `${mime} files` : 'this file type';\n}\n\n/** All openers that can handle this node right now (selector + when + availability),\n * best (highest priority) first. */\nexport function availableOpeners(platform, node) {\n const evaluate = (w) => platform.context.evaluate(w);\n const isAvailable = (o) => platform.plugins.isAvailable(o);\n return platform.contributions\n .openersFor(node)\n .filter((o) => (!o.when || evaluate(o.when)) && isAvailable(o))\n .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n}\n\n/** The remembered opener id for a node's type (ext → exact mime → type/* ), or null. */\nexport function rememberedOpenerId(platform, node) {\n const assoc = platform.settings.get(ASSOC_KEY) || {};\n const ext = extOf(node);\n const mime = node?.contentType || '';\n const wild = mime.includes('/') ? `${mime.slice(0, mime.indexOf('/') + 1)}*` : '';\n return (ext && assoc[ext]) || (mime && assoc[mime]) || (wild && assoc[wild]) || null;\n}\n\n/** Remember (or, with a null openerId, forget) the opener for a type key. */\nexport function rememberOpener(platform, typeKey, openerId) {\n if (!typeKey) return;\n const assoc = { ...(platform.settings.get(ASSOC_KEY) || {}) };\n if (openerId) assoc[typeKey] = openerId;\n else delete assoc[typeKey];\n platform.settings.set(ASSOC_KEY, assoc);\n}\n\n/** The saved associations as a list, for the Settings UI. */\nexport function listAssociations(platform) {\n const assoc = platform.settings.get(ASSOC_KEY) || {};\n return Object.entries(assoc).map(([typeKey, openerId]) => {\n const opener = platform.contributions.get(openerId);\n return { typeKey, openerId, openerTitle: opener?.title || openerId, missing: !opener };\n });\n}\n\n/** A short source label for an opener (\"Built-in\" or the plugin's name). */\nexport function openerSource(platform, opener) {\n if (!opener?.pluginId) return 'Built-in';\n return platform.plugins.plugins.get(opener.pluginId)?.manifest?.displayName || opener.pluginId;\n}\n",
|
|
69
|
+
"// ngin Actions — the workbench's business logic as thin, dispatchable verbs\n// (CQRS-style). Each depends on the `app` provider (platform + reactive\n// services) and performs one user intent: navigate, mutate the tree, upload,\n// search, open a file. Errors surface as notifications; the reactive services\n// they update flow straight back into the UI via `watch`.\n\nimport { Action } from '@3sln/ngin';\nimport { newId } from '@3sln/trove/core/util.js';\nimport { matchesTagFilters } from './tagQuery.js';\nimport { availableOpeners, rememberedOpenerId } from './openers.js';\n\nclass AppAction extends Action {\n static deps = ['app'];\n}\n\n/**\n * Load a collection's items. There is nothing to navigate INTO any more — a drive is\n * browsed by search and by following links — so this only ever switches which\n * collection is on screen, or refreshes the current one.\n */\nexport class NavigateAction extends AppAction {\n constructor(collectionId) {\n super();\n this.collectionId = collectionId;\n }\n async execute({ app }) {\n const { explorer, platform } = app;\n const collectionId = this.collectionId || explorer.state.collectionId || 'default';\n const switching = collectionId !== explorer.state.collectionId;\n // A collection's trash belongs to that collection. Carrying it across a switch\n // listed the OLD collection's deleted files under the new one — above an \"Empty\n // trash\" button that purges the new one, so the rows and the button disagreed\n // about which drive they were talking about.\n explorer.set({ loading: true, error: null, collectionId, ...(switching ? { trash: null } : {}) });\n try {\n const sort = platform.settings.get('explorer.sort');\n const order = platform.settings.get('explorer.sortOrder');\n const res = await platform.api.list({ sort, order, collection: collectionId });\n explorer.set({\n items: res.items, loading: false, selection: [], sort, order,\n collectionId: res.collectionId || collectionId,\n // What the COLLECTION holds, as against the page we were handed. Reporting the\n // page length would tell someone with 3,000 files that they have 500.\n stats: res.stats || null,\n usage: res.usage || null,\n nextCursor: res.nextCursor || null,\n });\n // Remember where they were, so the next visit opens there rather than guessing.\n platform.settings.set?.('explorer.lastCollection', collectionId);\n // Explorer→context projection (collectionId/hasSelection) lives in bl/index.js\n // so it stays in sync with selection too — nothing to mirror here.\n } catch (err) {\n explorer.set({ loading: false, error: err.message });\n platform.notifications.error(`Couldn't load this collection: ${err.message}`);\n }\n }\n}\n\nexport class LoadCollectionsAction extends AppAction {\n async execute({ app }) {\n try {\n const res = await app.platform.api.collections();\n const collections = res.collections || [];\n app.explorer.set({ collections, canCreateCollection: !!res.canCreate });\n return collections;\n } catch { /* collections disabled */ return []; }\n }\n}\n\n/**\n * Open the drive the user should land on.\n *\n * NOT simply 'default'. On a multi-user deployment plenty of people have no access to\n * the default collection at all, and starting there shows them a permission error\n * instead of their own files — the drive appears broken to the exact users for whom it\n * is working correctly. So: keep the last collection if it is still readable, otherwise\n * take the first one they can actually see.\n */\nexport class OpenInitialCollectionAction extends AppAction {\n async execute({ app }) {\n // Calls the API directly rather than dispatching LoadCollectionsAction: ngin's\n // dispatch() returns an event feed, not the action's value, so awaiting it would\n // hand back an EventTarget and this would fail in a way nothing reports.\n let collections = [];\n try {\n const res = await app.platform.api.collections();\n collections = res.collections || [];\n app.explorer.set({ collections, canCreateCollection: !!res.canCreate });\n } catch (err) {\n app.explorer.set({ loading: false, error: `Couldn't load your collections: ${err.message}` });\n return;\n }\n const ids = collections.map((c) => c.id);\n const remembered = app.platform.settings.get('explorer.lastCollection');\n if (!ids.length) {\n // A signed-in user with no grants at all. Say so; an empty file list with no\n // explanation reads as \"your drive is empty\", which is a different and much worse\n // thing to believe.\n app.explorer.set({\n loading: false, items: [], collections: [],\n error: 'You do not have access to any collections yet. Ask an administrator to grant you one.',\n });\n return;\n }\n const target = ids.includes(remembered) ? remembered : (ids.includes('default') ? 'default' : ids[0]);\n return app.engine.dispatch(new NavigateAction(target));\n }\n}\n\nexport class CreateCollectionAction extends AppAction {\n constructor(record) {\n super();\n this.record = record;\n }\n async execute({ app }) {\n try {\n const res = await app.platform.api.createCollection(this.record);\n app.platform.notifications.success(`Created collection “${res.collection.name}”`);\n await app.engine.dispatch(new LoadCollectionsAction());\n app.engine.dispatch(new NavigateAction(res.collection.id));\n } catch (err) {\n app.platform.notifications.error(`Couldn’t create collection: ${err.message}`);\n }\n }\n}\n\n/** Append the next page of a collection to what's already on screen. */\nexport class LoadMoreAction extends AppAction {\n async execute({ app }) {\n const { explorer, platform } = app;\n const cursor = explorer.state.nextCursor;\n if (!cursor || explorer.state.loadingMore) return;\n explorer.set({ loadingMore: true });\n try {\n const res = await platform.api.list({\n sort: explorer.state.sort, order: explorer.state.order,\n collection: explorer.state.collectionId, cursor,\n });\n explorer.set({\n items: [...explorer.state.items, ...res.items],\n nextCursor: res.nextCursor || null,\n loadingMore: false,\n });\n } catch (err) {\n explorer.set({ loadingMore: false });\n platform.notifications.error(`Couldn't load more: ${err.message}`);\n }\n }\n}\n\nexport class RefreshAction extends AppAction {\n async execute({ app }) {\n return app.engine.dispatch(new NavigateAction(app.explorer.state.collectionId));\n }\n}\n\n/** Show what's been deleted but not destroyed, and act on it. */\nexport class TrashAction extends AppAction {\n constructor(op = 'list', id = null) {\n super();\n this.op = op;\n this.id = id;\n }\n async execute({ app }) {\n const { explorer, platform } = app;\n const collection = explorer.state.collectionId;\n // Putting it away is one of the things you can do to the trash. Without this the\n // section, once opened, stayed on screen until a page reload — there was no way\n // back to a plain list of files.\n if (this.op === 'hide') {\n explorer.set({ trash: null });\n return;\n }\n try {\n if (this.op === 'restore') {\n const { node } = await platform.api.restore(this.id);\n platform.notifications.success(`Restored “${node.name}”`);\n } else if (this.op === 'purge') {\n await platform.api.purgeTrash({ id: this.id });\n } else if (this.op === 'empty') {\n const { purged } = await platform.api.purgeTrash({ collection });\n platform.notifications.success(`Deleted ${purged} item${purged === 1 ? '' : 's'} for good`);\n }\n const { items } = await platform.api.trash(collection);\n explorer.set({ trash: items });\n // Restoring puts something back in the drive, so the list on screen is now stale.\n if (this.op !== 'list') await app.engine.dispatch(new NavigateAction(collection));\n } catch (err) {\n platform.notifications.error(`Trash: ${err.message}`);\n }\n }\n}\n\nexport class DeleteAction extends AppAction {\n constructor(ids) {\n super();\n this.ids = ids;\n }\n async execute({ app }) {\n try {\n for (const id of this.ids) await app.platform.api.remove(id);\n app.platform.notifications.info(`Deleted ${this.ids.length} item${this.ids.length > 1 ? 's' : ''}`);\n for (const id of this.ids) app.platform.workbench.closeTab(id);\n } catch (err) {\n app.platform.notifications.error(`Couldn’t delete: ${err.message}`);\n }\n app.engine.dispatch(new RefreshAction());\n }\n}\n\nexport class RenameAction extends AppAction {\n constructor(id, newName) {\n super();\n this.id = id;\n this.newName = newName;\n }\n async execute({ app }) {\n try {\n const node = await app.platform.api.rename(this.id, this.newName);\n app.platform.workbench.updateTabNode(node.node);\n app.engine.dispatch(new RefreshAction());\n } catch (err) {\n app.platform.notifications.error(`Couldn’t rename: ${err.message}`);\n }\n }\n}\n\n\nexport class OpenFileAction extends AppAction {\n /** @param {object} node @param {{reset?:boolean}} [opts] reset → start a fresh stack (modal search) */\n constructor(node, opts = {}) {\n super();\n this.node = node;\n this.opts = opts;\n }\n async execute({ app }) {\n // Guard against being invoked with no target (e.g. a command fired with an empty\n // selection) — dereferencing a null node would throw an unhandled rejection.\n if (!this.node) return;\n const { platform } = app;\n const open = (openerId) => platform.workbench.openFile(this.node, openerId, { reset: !!this.opts.reset });\n\n // An explicit opener (e.g. the switch-opener control) wins outright.\n if (this.opts.openerId) return open(this.opts.openerId);\n\n // Only openers available right now (a plugin previewer needing the network while\n // offline is skipped, so we fall back to a built-in one).\n const avail = availableOpeners(platform, this.node);\n\n // A remembered choice for this file type, if that opener is still available.\n const remembered = rememberedOpenerId(platform, this.node);\n if (remembered && avail.some((o) => o.id === remembered)) return open(remembered);\n\n // Several openers and no saved preference → let the user choose (with an option to\n // remember). One or none → just open the best (or the download fallback).\n if (avail.length > 1) {\n return platform.workbench.showDialog({ kind: 'opener-chooser', node: this.node, openers: avail, reset: !!this.opts.reset });\n }\n open(avail[0]?.id || 'core.fallback');\n }\n}\n\nexport class UploadFilesAction extends AppAction {\n constructor(files, collectionId) {\n super();\n this.files = files;\n this.collectionId = collectionId;\n }\n async execute({ app }) {\n const collection = this.collectionId || app.explorer.state.collectionId || 'default';\n const concurrency = app.platform.settings.get('uploads.concurrency');\n const uploads = [...this.files].map((file) => this.#one(app, file, collection, concurrency));\n await Promise.allSettled(uploads);\n app.engine.dispatch(new NavigateAction(collection));\n }\n async #one(app, file, collection, concurrency) {\n const { transfers, platform } = app;\n const tid = newId('xfer');\n const controller = new AbortController();\n transfers.start(tid, file.name, file.size, controller);\n let uploadId = null;\n try {\n const node = await platform.api.upload(file, {\n collection, concurrency, signal: controller.signal,\n onStart: (id) => { uploadId = id; },\n onProgress: (p) => transfers.progress(tid, p),\n });\n transfers.finish(tid, 'done');\n // The server disambiguates a same-name collision rather than overwriting — tell\n // the user when the saved name differs from what they dropped.\n if (node?.name && node.name !== file.name) {\n platform.notifications.info(`\"${file.name}\" already existed — saved as \"${node.name}\".`);\n }\n } catch (err) {\n // Release the server-side session so a cancelled/failed multipart upload doesn't\n // leak an open multipart object (best-effort; the server also sweeps stale ones).\n if (uploadId) platform.api.abortUpload(uploadId).catch(() => {});\n if (err.code === 'aborted') transfers.finish(tid, 'cancelled');\n else {\n transfers.finish(tid, 'error', err.message);\n platform.notifications.error(`Upload failed: ${file.name} — ${err.message}`);\n }\n }\n }\n}\n\n/** Drive-wide tag/property filter (`#tag`, `#key:op:value`), optionally narrowed\n * by free text. Falls back to filtering the loaded folder when offline. */\nexport class FilterAction extends AppAction {\n constructor(filters, text) {\n super();\n this.filters = filters || [];\n this.text = text || '';\n }\n async execute({ app }) {\n const { search, platform } = app;\n if (!this.filters.length) {\n search.set({ results: [], ran: false, filtered: false });\n return;\n }\n search.set({ query: this.text, loading: true, error: null, filtered: true });\n if (app.offline && !app.offline.state.online) {\n const items = (app.explorer.state.items || []).filter((n) => matchesTagFilters(n, this.filters));\n search.set({ results: items.map((node) => ({ node })), loading: false, ran: true, filtered: true, offline: true });\n return;\n }\n try {\n const res = await platform.api.tagSearch(this.filters, this.text.trim() || undefined, { limit: 100 });\n search.set({ results: (res.items || []).map((node) => ({ node })), loading: false, ran: true, filtered: true, offline: false });\n } catch (err) {\n search.set({ loading: false, error: err.message, ran: true, filtered: true });\n }\n }\n}\n\n/** Command-palette quick-open: a keyword file search whose results live in the search\n * service (state.se.paletteFiles) instead of ad-hoc state hung off the UI. */\nexport class QuickOpenAction extends AppAction {\n constructor(query) {\n super();\n this.query = query;\n }\n async execute({ app }) {\n const { search, platform } = app;\n const q = (this.query || '').trim();\n if (!q) { search.set({ paletteFiles: [], paletteQuery: '', paletteError: null, paletteLoading: false }); return; }\n // Keystrokes outrun the network: a slower request for an earlier query must not\n // land on top of a newer one's results. The query itself is the sequence token —\n // whatever the palette input holds now is the only answer worth showing.\n search.set({ paletteQuery: q, paletteLoading: true, paletteError: null });\n try {\n const res = await platform.api.search(q, { mode: 'keyword', limit: 30 });\n if (search.state.paletteQuery !== q) return; // superseded\n search.set({ paletteFiles: res.results || [], paletteLoading: false });\n } catch (err) {\n if (search.state.paletteQuery !== q) return;\n // A failed search must not look like \"no files matched\" — that reads as a fact\n // about the drive when it's actually a fact about the request.\n search.set({ paletteFiles: [], paletteLoading: false, paletteError: err?.message || 'Search failed' });\n }\n }\n}\n\nexport class SearchAction extends AppAction {\n constructor(query, mode) {\n super();\n this.query = query;\n this.mode = mode;\n }\n async execute({ app }) {\n const { search, platform } = app;\n const q = this.query.trim();\n if (!q) {\n search.set({ query: '', results: [], ran: false, error: null, resolved: null });\n return;\n }\n const mode = this.mode || platform.settings.get('search.mode');\n search.set({ query: this.query, mode, loading: true, error: null, resolved: null });\n const offline = app.offline;\n // Offline (or server unreachable) → search the pinned corpus locally.\n if (offline && !offline.state.online) {\n try {\n const results = await offline.searchOffline(q, { limit: 40 });\n search.set({ results, loading: false, ran: true, offline: true, resolved: null });\n } catch (err) {\n search.set({ results: [], loading: false, ran: true, offline: true, error: err.message });\n }\n return;\n }\n try {\n // The server transforms the raw query (parse/LLM) and tells us what it actually\n // searched — surfaced to the user via `resolved` so search is honest.\n //\n // We send the views we can draw with, because the transformer is the only thing in\n // the stack that read the sentence: \"photos from the trip last summer\" asks for a\n // gallery, and by the time the results are back all anyone can do is guess from\n // content types. It can only name a view from this list.\n const views = platform.contributions.ofType('view').map((v) => ({ id: v.id, title: v.title || v.id }));\n const res = await platform.api.query(q, { mode, limit: 40, views });\n search.set({ results: res.results, resolved: res.resolved || null, loading: false, ran: true, offline: false });\n } catch (err) {\n if (offline) {\n const results = await offline.searchOffline(q, { limit: 40 });\n search.set({ results, loading: false, ran: true, offline: true, error: results.length ? null : err.message });\n } else {\n search.set({ loading: false, error: err.message, ran: true });\n }\n }\n }\n}\n",
|
|
70
|
+
"// Install flow: turn an uploaded .zip or a URL into a parsed package, assess its\n// trust (signature + domain verification), and present the pre-install review so\n// the user can make an informed decision before any plugin code runs.\n\nimport { parsePackage, fetchPackage, reviewSummary, displayName } from '../platform/pluginPackage.js';\nimport { pluginId } from '@3sln/trove/core/plugins/identity.js';\n\nexport async function beginInstallFromFile(app, file) {\n try {\n const bytes = new Uint8Array(await file.arrayBuffer());\n await review(app, parsePackage(bytes));\n } catch (err) {\n app.platform.notifications.error(`Couldn't read the plugin: ${err.message}`);\n }\n}\n\nexport async function beginInstallFromUrl(app, url) {\n try {\n const pkg = await fetchPackage(url, (u, o) => fetch(u, o));\n await review(app, pkg);\n } catch (err) {\n app.platform.notifications.error(`Couldn't fetch the plugin: ${err.message}`);\n }\n}\n\nasync function review(app, pkg) {\n const label = displayName(pkg.manifest);\n if (app.platform.plugins.plugins.has(pluginId(pkg.manifest))) {\n app.platform.notifications.warn(`“${label}” is already installed.`);\n return;\n }\n // Trust assessment can hit the network (domain assetlinks); do it before review.\n let trust = { status: 'unverified' };\n try {\n trust = await app.platform.plugins.assessTrust(pkg);\n } catch { /* offline / unreachable */ }\n const summary = reviewSummary(pkg, trust);\n app.platform.workbench.showDialog({\n kind: 'plugin-review',\n summary,\n isAdmin: !!app.social.state.admin,\n onInstall: async (grants) => {\n app.platform.workbench.closeDialog();\n // Account installs upload the package + run a handshake that can take a while —\n // switch to the plugins view (which shows the plugin's loading state) and hold a\n // sticky \"Installing…\" toast so the user isn't left staring at nothing.\n app.platform.workbench.setActivity('plugins');\n const pending = app.platform.notifications.info(`Installing “${label}”…`, { sticky: true });\n try {\n await app.platform.plugins.install(pkg, { grants, trust });\n app.platform.notifications.dismiss(pending);\n app.platform.notifications.success(`Installed “${label}”`);\n } catch (err) {\n app.platform.notifications.dismiss(pending);\n app.platform.notifications.error(`Install failed: ${err.message}`);\n }\n },\n });\n}\n",
|
|
71
|
+
"// Register the workbench's built-in commands. Commands are the single entry\n// point for every action — palette, keybindings, buttons, and menus all call\n// `commands.execute(id)`. Handlers here dispatch ngin Actions for anything that\n// touches data, and poke the reactive shell services for pure-UI toggles.\n\nimport {\n NavigateAction, RefreshAction, DeleteAction, RenameAction,\n UploadFilesAction, OpenFileAction, CreateCollectionAction, LoadMoreAction, TrashAction,\n SearchAction,\n} from './actions.js';\nimport { beginInstallFromFile, beginInstallFromUrl } from './pluginInstall.js';\nimport { troveUri } from '@3sln/trove/core/links.js';\n\nexport function registerCommands(app) {\n const { platform, engine, explorer } = app;\n const { commands, workbench } = platform;\n const go = (a) => engine.dispatch(a);\n\n const cmd = (id, title, handler, extra = {}) => commands.register({ id, title, handler, ...extra });\n\n // --- navigation / views ----------------------------------------------------\n cmd('workbench.showCommandPalette', 'Show All Commands', () => workbench.openPalette('commands'), { category: 'View', icon: 'command' });\n cmd('workbench.quickOpen', 'Go to File…', () => workbench.openPalette('files'), { category: 'File', icon: 'search' });\n cmd('workbench.view.home', 'Go Home (search & browse)', () => workbench.showHome(), { category: 'View', icon: 'search' });\n cmd('workbench.view.plugins', 'Show Plugins', () => workbench.setActivity('plugins'), { category: 'View' });\n cmd('workbench.openSettings', 'Open Settings', () => workbench.setActivity('settings'), { category: 'Preferences', icon: 'gear' });\n cmd('workbench.closeOverlays', 'Close', () => workbench.closeOverlays(), { palette: false });\n\n // Speak to search.\n //\n // On a TV this is mostly NOT about recognising anything: a remote's mic dictates into\n // whatever text field the platform keyboard is attached to, and is swallowed by the\n // system assistant when there isn't one. So the command's first job is to put the\n // search field on screen and focused, which is a feature on every TV whether or not\n // the browser can transcribe. Where it can — on-device only, see platform/voice.js —\n // it also starts listening and types what it hears.\n cmd('search.voice', 'Search by Voice', async () => {\n await platform.voice.run({\n onText: (text, { final }) => {\n if (!text) return;\n workbench.setLaunchQuery(text);\n // Interim results keep the box in step with the speaker; only the settled\n // transcript is worth a round trip to the server.\n if (final) go(new SearchAction(text));\n },\n });\n }, { category: 'View', icon: 'search' });\n\n // --- background work + standing problems -----------------------------------\n cmd('workbench.showActivity', 'Show Activity (running work & problems)',\n () => app.activity.togglePanel(true), { category: 'View', icon: 'refresh' });\n // The manual scan the drive had no way to ask for. Reports as a task rather than\n // blocking, because on a large drive it takes minutes.\n cmd('workbench.rebuildIndex', 'Rebuild Search Index',\n () => app.activity.rebuildIndex().catch(() => {}), { category: 'View', icon: 'refresh' });\n // Trove is not the only thing that can write to the bucket. This is how files added,\n // replaced, or removed by something else get picked up.\n cmd('workbench.scanCollection', 'Scan Collection for Outside Changes',\n () => app.activity.scanCollection(explorer.state.collectionId || 'default').catch(() => {}),\n { category: 'Explorer', icon: 'refresh' });\n\n // --- explorer --------------------------------------------------------------\n cmd('explorer.refresh', 'Refresh', () => go(new RefreshAction()), { category: 'Explorer', icon: 'refresh' });\n cmd('explorer.loadMore', 'Show More Items', () => go(new LoadMoreAction()), { palette: false });\n cmd('explorer.upload', 'Upload Files…', () => {\n pickFiles((files) => files.length && go(new UploadFilesAction(files, explorer.state.collectionId)));\n }, { category: 'Explorer', icon: 'upload' });\n\n // Copy the item's trove: link, which is how one item references another in markdown.\n // \"Nothing is selected\" is a real answer, and it has to be said out loud: these used\n // to return silently, which is indistinguishable from a broken command.\n const subject = (fallbackNode) => {\n const node = fallbackNode || explorer.selectedNodes()[0] || workbench.activeTab()?.node;\n if (!node) platform.notifications.info('Pick a file first — highlight one in the list, or open it.');\n return node;\n };\n\n cmd('explorer.copyLink', 'Copy Link to Item', async () => {\n const node = subject();\n if (!node) return;\n const uri = troveUri(node);\n try {\n await navigator.clipboard.writeText(uri);\n platform.notifications.success(`Copied ${uri}`);\n } catch {\n // Clipboard access can be denied; showing the link is still useful.\n platform.notifications.info(uri, { sticky: true });\n }\n }, { category: 'Explorer', icon: 'link' });\n\n cmd('explorer.rename', 'Rename', () => {\n const node = subject();\n if (!node) return;\n workbench.showDialog({\n kind: 'prompt', title: 'Rename', label: 'New name', value: node.name, confirmLabel: 'Rename',\n onSubmit: (name) => {\n workbench.closeDialog();\n if (name?.trim() && name !== node.name) go(new RenameAction(node.id, name.trim()));\n },\n });\n }, { category: 'Explorer' });\n\n cmd('explorer.delete', 'Delete', () => {\n const nodes = explorer.selectedNodes();\n const fallback = nodes.length ? null : workbench.activeTab()?.node;\n if (fallback) nodes.push(fallback); // deleting the file you have open is the obvious intent\n if (!nodes.length) {\n platform.notifications.info('Pick a file first — highlight one in the list, or open it.');\n return;\n }\n const doDelete = () => go(new DeleteAction(nodes.map((n) => n.id)));\n if (platform.settings.get('explorer.confirmDelete')) {\n workbench.showDialog({\n kind: 'confirm', title: `Move ${nodes.length} item${nodes.length > 1 ? 's' : ''} to the trash?`,\n // Say what actually happens. Telling someone a file will be \"permanently\n // deleted\" when it goes to the trash trains them to fear a safe action; the\n // reverse — promising recovery that doesn't exist — is worse.\n body: nodes.length === 1\n ? `\"${nodes[0].name}\" leaves the drive but is kept, and can be restored from the trash.`\n : 'They leave the drive but are kept, and can be restored from the trash.',\n confirmLabel: 'Move to trash',\n onConfirm: () => {\n workbench.closeDialog();\n doDelete();\n },\n });\n } else doDelete();\n }, { category: 'Explorer', icon: 'trash' });\n\n cmd('explorer.showTrash', 'Show Trash', () => {\n go(new TrashAction('list'));\n workbench.showHome();\n }, { category: 'Explorer', icon: 'trash' });\n cmd('explorer.hideTrash', 'Hide Trash', () => go(new TrashAction('hide')), { palette: false });\n cmd('explorer.restore', 'Restore from Trash', (id) => id && go(new TrashAction('restore', id)), { palette: false });\n cmd('explorer.purgeOne', 'Delete Forever', (id) => id && go(new TrashAction('purge', id)), { palette: false });\n cmd('explorer.emptyTrash', 'Empty Trash', () => {\n workbench.showDialog({\n kind: 'confirm', title: 'Empty the trash?',\n body: 'Everything in the trash will be destroyed. This cannot be undone.',\n danger: true, confirmLabel: 'Delete forever',\n onConfirm: () => { workbench.closeDialog(); go(new TrashAction('empty')); },\n });\n }, { category: 'Explorer', icon: 'trash' });\n\n cmd('explorer.open', 'Open', (node) => go(new OpenFileAction(node || explorer.selectedNodes()[0])), { palette: false });\n cmd('explorer.download', 'Download', async (node) => {\n const target = node || explorer.selectedNodes()[0] || workbench.activeTab()?.node;\n if (!target?.id) return;\n try {\n const { url, revoke } = await platform.api.download(target.id, target.name);\n triggerDownload(url, target.name);\n // A blob URL pins the bytes until it is released; the click has already happened\n // by the time this runs.\n if (revoke) setTimeout(() => URL.revokeObjectURL(url), 60_000);\n } catch (err) {\n platform.notifications.error(`Couldn't download ${target.name}: ${err.message}`);\n }\n }, { category: 'Explorer', icon: 'download' });\n\n // --- offline ---------------------------------------------------------------\n cmd('offline.pin', 'Make Available Offline', (node) => {\n const target = node || explorer.selectedNodes()[0] || workbench.activeTab()?.node;\n if (target?.id) app.offline.pin(target);\n }, { category: 'Offline', icon: 'download' });\n cmd('offline.unpin', 'Remove from Offline', (node) => {\n const target = node || explorer.selectedNodes()[0] || workbench.activeTab()?.node;\n if (target) app.offline.unpin(target.id);\n }, { palette: false });\n\n // --- collections -----------------------------------------------------------\n // The menu of \"where else could I be\". Shared by the palette command and the status\n // bar's collection segment, so both offer the same list.\n const collectionMenu = () => {\n const current = explorer.state.collectionId || 'default';\n const items = (explorer.state.collections || []).map((c) => ({\n label: c.name || c.id,\n icon: c.id === current ? 'check' : 'files',\n run: () => commands.execute('collections.switch', c.id),\n }));\n if (explorer.state.canCreateCollection) {\n if (items.length) items.push({ sep: true });\n items.push({ label: 'New collection…', icon: 'plus', run: () => commands.execute('collections.create') });\n }\n return items;\n };\n app.collectionMenu = collectionMenu;\n\n // `NavigateAction` takes a single collectionId. This passed ('/', cid) — a leftover\n // from a path+collection signature — so every switch navigated to a collection\n // literally named \"/\" and failed to load. It was also unreachable: no caller, and\n // hidden from the palette.\n cmd('collections.switch', 'Switch Collection…', (cid) => {\n if (cid) {\n go(new NavigateAction(cid));\n workbench.showHome();\n return;\n }\n // From the palette or a keybinding there is no pointer to anchor a menu to.\n const items = collectionMenu();\n if (!items.length) return platform.notifications.info('This drive has one collection.');\n const w = typeof window === 'undefined' ? 800 : window.innerWidth;\n workbench.showContextMenu(Math.max(12, Math.round(w / 2) - 110), 120, items);\n }, { category: 'Collections', icon: 'files' });\n cmd('collections.create', 'New Collection…', () => {\n workbench.showDialog({\n kind: 'collection', title: 'New collection',\n onSubmit: (record) => {\n workbench.closeDialog();\n if (record) go(new CreateCollectionAction(record));\n },\n });\n }, { category: 'Collections', icon: 'files' });\n\n // --- conversations & notifications -----------------------------------------\n // The details panel is a view OF a file. With nothing open there is nothing for it to\n // show, and flipping the flag silently was indistinguishable from a broken command.\n cmd('workbench.toggleInfoPanel', 'Toggle Details & Conversation', () => {\n if (!workbench.activeTab()) {\n platform.notifications.info('Open a file to see its details and conversation.');\n return;\n }\n workbench.toggleInfoPanel();\n }, { category: 'View', icon: 'info' });\n cmd('notifications.show', 'Show Notifications', () => app.social.toggleInbox(true), { category: 'View' });\n cmd('notifications.enablePush', 'Enable Push Notifications', () => app.social.enablePush(), { category: 'Notifications' });\n\n // --- plugins ---------------------------------------------------------------\n cmd('plugins.installFromUrl', 'Install Plugin from URL…', () => {\n workbench.showDialog({\n kind: 'prompt', title: 'Install plugin from URL', label: 'Plugin package (.zip) URL',\n placeholder: 'https://example.com/plugin.zip', confirmLabel: 'Fetch',\n onSubmit: (url) => { workbench.closeDialog(); if (url?.trim()) beginInstallFromUrl(app, url.trim()); },\n });\n }, { category: 'Plugins', icon: 'plug' });\n cmd('plugins.installFromFile', 'Install Plugin from File…', () => {\n pickZip((file) => file && beginInstallFromFile(app, file));\n }, { category: 'Plugins', icon: 'plug' });\n}\n\n// --- helpers ----------------------------------------------------------------\n\n// Open a native file picker. The hidden <input> is removed on selection AND on\n// cancel — cancelling fires no `change`, so we also clean up on the next window\n// focus (which the OS dialog returns) to avoid leaking a growing pile of inputs.\nfunction pick(cb, configure) {\n const input = document.createElement('input');\n input.type = 'file';\n configure(input);\n input.style.display = 'none';\n document.body.appendChild(input);\n const cleanup = () => { input.remove(); window.removeEventListener('focus', onFocus); };\n const onFocus = () => setTimeout(() => { if (!input.files.length) cleanup(); }, 300);\n input.addEventListener('change', () => { cb(input.files); cleanup(); }, { once: true });\n window.addEventListener('focus', onFocus);\n input.click();\n}\nfunction pickFiles(cb) {\n pick((files) => cb(files), (input) => { input.multiple = true; });\n}\nfunction pickZip(cb) {\n pick((files) => cb(files[0]), (input) => { input.accept = '.zip,application/zip'; });\n}\n\nfunction triggerDownload(url, name) {\n const a = document.createElement('a');\n a.href = url;\n a.download = name || '';\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\nexport { pickFiles, triggerDownload };\n",
|
|
72
|
+
"// Assemble the ngin engine and the reactive services into one `app` context,\n// register commands and built-in openers, and expose everything the UI needs.\n// The engine holds a single `app` singleton provider so Actions can reach the\n// platform and services; the app object also carries the engine so Actions can\n// dispatch follow-up Actions (the choreographer pattern).\n\nimport { Engine, Provider } from '@3sln/ngin';\nimport { effect } from '../runtime.js';\nimport { ExplorerService, SearchClientService, TransfersService } from './services.js';\nimport { SocialService } from './social.js';\nimport { OfflineService } from './offline.js';\nimport { ActivityService } from './activity.js';\nimport { registerCommands } from './commands.js';\nimport { NavigateAction, LoadCollectionsAction, OpenInitialCollectionAction } from './actions.js';\n\nexport function createApp(platform) {\n const explorer = new ExplorerService(platform.settings);\n const search = new SearchClientService();\n // One place for \"what's running\" and \"what's stuck\", covering both sides of the wire.\n const activity = new ActivityService(platform);\n const transfers = new TransfersService(activity);\n const social = new SocialService(platform);\n const offline = new OfflineService(platform);\n social.offline = offline; // social queues sidecar ops through offline when disconnected\n\n const app = { platform, explorer, search, transfers, social, offline, activity, engine: null };\n\n const engine = new Engine({\n providers: { app: Provider.fromSingleton(app) },\n });\n app.engine = engine;\n\n registerCommands(app);\n\n // Wire the plugin panel opener hook to the workbench.\n platform.openPluginPanel = (pluginId) => platform.workbench.openPluginPanel(pluginId);\n\n // Project explorer state → context keys in ONE place. These drive when-clauses\n // (e.g. the Delete keybinding needs `explorer.hasSelection`), and previously only\n // NavigateAction set them — so selecting a file never flipped hasSelection true and\n // Delete silently did nothing. Deriving them from the observable keeps them honest.\n platform.context.setMany({ 'explorer.collectionId': 'default', 'explorer.hasSelection': false });\n effect(explorer.observe(), (ex) => {\n platform.context.setMany({\n 'explorer.collectionId': ex.collectionId || 'default',\n 'explorer.hasSelection': (ex.selection?.length || 0) > 0,\n });\n });\n\n // Load a file's conversation/tags whenever the active viewer panel changes (the\n // panel stack lives in the navigation sub-service now).\n let lastTab = null;\n effect(platform.workbench.observeNav(), (nav) => {\n if (nav.activeTabId !== lastTab) {\n lastTab = nav.activeTabId;\n // Clear the sidecar when no file is active (last tab closed) so a stale\n // conversation from the previous file doesn't linger.\n social.loadSidecar(nav.activeFile ? nav.activeFile.id : null);\n }\n });\n\n social.init();\n offline.init();\n activity.init();\n engine.dispatch(new LoadCollectionsAction());\n\n return { engine, app };\n}\n\nexport { NavigateAction };\n",
|
|
73
|
+
"// Inline SVG icons. One function, a path table, sized via viewBox 24. Using\n// dodo's element helpers so icons are just vnodes like everything else.\n\nimport { dd } from '../runtime.js';\n\nconst { svg, h } = dd;\n\n// Each entry is an array of <path>/<primitive> specs (attrs use SVG names).\nconst PATHS = {\n link: [{ d: 'M10 13a5 5 0 0 0 7.07 0l3-3A5 5 0 0 0 13 3l-1.5 1.5' }, { d: 'M14 11a5 5 0 0 0-7.07 0l-3 3A5 5 0 0 0 11 21l1.5-1.5' }],\n file: [{ d: 'M6 3h8l5 5v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z' }, { d: 'M14 3v5h5' }],\n 'file-text': [{ d: 'M6 3h8l5 5v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z' }, { d: 'M14 3v5h5M9 13h6M9 17h6M9 9h2' }],\n 'file-image': [{ d: 'M6 3h8l5 5v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z' }, { d: 'M14 3v5h5' }, { d: 'M8.5 15l2-2 3 3 2-1.5' }, { d: 'M10 12a1 1 0 1 0 0-.01' }],\n 'file-audio': [{ d: 'M6 3h8l5 5v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z' }, { d: 'M14 3v5h5M10 12v5M13 10v9' }],\n 'file-video': [{ d: 'M6 3h8l5 5v13a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z' }, { d: 'M14 3v5h5M10 12l4 2.5-4 2.5z' }],\n book: [{ d: 'M4 5a2 2 0 0 1 2-2h13v16H6a2 2 0 0 0-2 2z' }, { d: 'M4 19a2 2 0 0 1 2-2h13' }],\n search: [{ d: 'M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zM20 20l-3.5-3.5' }],\n mic: [{ d: 'M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3z' }, { d: 'M5 11a7 7 0 0 0 14 0M12 18v3M9 21h6' }],\n command: [{ d: 'M8 6a2 2 0 1 0-2 2h12a2 2 0 1 0-2-2v12a2 2 0 1 0 2-2H6a2 2 0 1 0 2 2z' }],\n gear: [{ d: 'M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z' }, { d: 'M19 12a7 7 0 0 0-.1-1.2l2-1.6-2-3.4-2.4 1a7 7 0 0 0-2-1.2L14 2h-4l-.5 2.6a7 7 0 0 0-2 1.2l-2.4-1-2 3.4 2 1.6A7 7 0 0 0 5 12a7 7 0 0 0 .1 1.2l-2 1.6 2 3.4 2.4-1a7 7 0 0 0 2 1.2L10 22h4l.5-2.6a7 7 0 0 0 2-1.2l2.4 1 2-3.4-2-1.6A7 7 0 0 0 19 12z' }],\n upload: [{ d: 'M12 16V4M7 9l5-5 5 5M4 20h16' }],\n download: [{ d: 'M12 4v12M7 11l5 5 5-5M4 20h16' }],\n trash: [{ d: 'M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v6M14 11v6' }],\n refresh: [{ d: 'M20 11a8 8 0 1 0-.5 4M20 5v6h-6' }],\n 'chevron-right': [{ d: 'M9 6l6 6-6 6' }],\n 'chevron-left': [{ d: 'M15 6l-6 6 6 6' }],\n 'chevron-down': [{ d: 'M6 9l6 6 6-6' }],\n close: [{ d: 'M6 6l12 12M18 6L6 18' }],\n tag: [{ d: 'M4 4h6.5a1 1 0 0 1 .7.3l8.5 8.5a1 1 0 0 1 0 1.4l-5.5 5.5a1 1 0 0 1-1.4 0L4.3 11.2a1 1 0 0 1-.3-.7V4z' }, { d: 'M8 8a0.6 0.6 0 1 0 0-.01' }],\n play: [{ d: 'M8 5v14l11-7z', fill: 'currentColor', stroke: 'none' }],\n pause: [{ d: 'M8 5h3v14H8zM13 5h3v14h-3z', fill: 'currentColor', stroke: 'none' }],\n 'skip-back': [{ d: 'M11 6v12L3 12zM13 6l8 6-8 6zM21 6v12', fill: 'none' }],\n 'skip-forward': [{ d: 'M13 6v12l8-6zM3 6l8 6-8 6zM3 6v12' }],\n 'back-30': [{ d: 'M11 8a7 7 0 1 0 7 7' }, { d: 'M11 4l-3 4 4 1' }],\n 'fwd-30': [{ d: 'M13 8a7 7 0 1 1-7 7' }, { d: 'M13 4l3 4-4 1' }],\n plug: [{ d: 'M9 3v6M15 3v6M6 9h12v3a6 6 0 0 1-12 0zM12 18v3' }],\n bell: [{ d: 'M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9z' }, { d: 'M13.73 21a2 2 0 0 1-3.46 0' }],\n files: [{ d: 'M4 4h9l3 3v3M8 8h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2z' }],\n dots: [{ d: 'M5 12h.01M12 12h.01M19 12h.01' }],\n check: [{ d: 'M5 12l5 5L20 6' }],\n plus: [{ d: 'M12 5v14M5 12h14' }],\n 'arrow-up': [{ d: 'M12 19V5M6 11l6-6 6 6' }],\n star: [{ d: 'M12 3l2.7 5.6 6.1.9-4.4 4.3 1 6.1L12 17l-5.4 2.9 1-6.1L3.2 9.5l6.1-.9z' }],\n info: [{ d: 'M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18zM12 11v5M12 8h.01' }],\n warn: [{ d: 'M12 3l9 16H3zM12 10v4M12 17h.01' }],\n x: [{ d: 'M6 6l12 12M18 6L6 18' }],\n list: [{ d: 'M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01' }],\n grid: [{ d: 'M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z' }],\n};\n\n// Map a node to an icon name via the shared file-type classifier.\nexport { iconForKind as iconForNode } from '../bl/fileType.js';\n\nexport function icon(name, { size = 18, strokeWidth = 1.6, className } = {}) {\n // `Object.hasOwn`, not a bare lookup: `PATHS` is a plain object, so a contribution\n // declaring `\"icon\": \"constructor\"` (kept verbatim from the manifest) otherwise\n // resolves to a function and takes down the whole render with `specs.map is not a\n // function`.\n const specs = Object.hasOwn(PATHS, name) ? PATHS[name] : PATHS.file;\n // `class` through `$attrs`, not `className`. An SVGElement's `className` is a\n // read-only SVGAnimatedString, so the prop is written out as a literal `className=\"…\"`\n // attribute — which no selector matches. `.sheet-row .sr-go` was styling nothing.\n return svg(\n {\n $attrs: {\n viewBox: '0 0 24 24', width: size, height: size, fill: 'none',\n stroke: 'currentColor', 'stroke-width': strokeWidth,\n 'stroke-linecap': 'round', 'stroke-linejoin': 'round',\n ...(className ? { class: className } : {}),\n },\n },\n ...specs.map((s) => h('path', { $attrs: filterAttrs(s) })),\n );\n}\n\nfunction filterAttrs(s) {\n const out = { d: s.d };\n if (s.fill) out.fill = s.fill;\n if (s.stroke) out.stroke = s.stroke;\n return out;\n}\n",
|
|
74
|
+
"// Formatting helpers for the UI.\n\nexport function bytes(n) {\n if (n == null || Number.isNaN(n)) return '—';\n const u = ['B', 'KB', 'MB', 'GB', 'TB'];\n let i = 0;\n let v = n;\n while (v >= 1024 && i < u.length - 1) {\n v /= 1024;\n i++;\n }\n return `${v >= 100 || i === 0 ? Math.round(v) : v.toFixed(1)} ${u[i]}`;\n}\n\nexport function duration(sec) {\n if (sec == null || Number.isNaN(sec) || !isFinite(sec)) return '0:00';\n sec = Math.max(0, Math.floor(sec));\n const h = Math.floor(sec / 3600);\n const m = Math.floor((sec % 3600) / 60);\n const s = sec % 60;\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}` : `${m}:${String(s).padStart(2, '0')}`;\n}\n\nexport function relativeDate(ms) {\n if (!ms) return '';\n const diff = Date.now() - ms;\n const day = 86400000;\n if (diff < 60000) return 'just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < day) return `${Math.floor(diff / 3600000)}h ago`;\n if (diff < 7 * day) return `${Math.floor(diff / day)}d ago`;\n return new Date(ms).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: diff > 330 * day ? 'numeric' : undefined });\n}\n",
|
|
75
|
+
"// Social UI: the notification bell (title bar) and the info panel (a right rail\n// with the active file's tags and threaded conversation). Both render from the\n// reactive SocialService; actions call back into it.\n\nimport { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { relativeDate } from '../format.js';\nimport { OpenFileAction } from '../../bl/actions.js';\nimport { troveUri } from '@3sln/trove/core/links.js';\n\nconst { div, span, button, textarea, input, p } = dd;\n\nconst REACTIONS = ['👍', '❤️', '🎉', '👀', '🚀'];\n\n// ---- title-bar principal chip + bell ---------------------------------------\n\nexport function principalChip(state) {\n const me = state.so.me;\n // No identity, no profile. On a deployment with no login the server hands back a\n // shared anonymous stand-in; rendering an avatar and a name for it would describe a\n // user who does not exist, and imply an account there is no way to sign in or out of.\n if (!me || me.anonymous) return null;\n const initial = (me.name || me.id || '?')[0].toUpperCase();\n return div({ className: 'principal', title: me.email || me.name || me.id },\n me.picture ? dd.img({ src: me.picture, alt: '', className: 'avatar-img' }) : span({ className: 'avatar-txt' }, initial),\n );\n}\n\nexport function notificationBell(state, ui) {\n const n = state.so.notifications;\n const social = ui.app.social;\n return div({ className: 'bell-wrap' },\n button({ className: 'iconbtn bell', title: 'Notifications' },\n icon('bell', { size: 19 }),\n n.unread ? span({ className: 'bell-badge' }, String(n.unread > 9 ? '9+' : n.unread)) : null,\n ).on({ click: () => social.toggleInbox() }),\n state.so.inboxOpen ? inboxDropdown(state, ui) : null,\n );\n}\n\nfunction inboxDropdown(state, ui) {\n const items = state.so.notifications.items;\n const social = ui.app.social;\n return div({},\n div({ className: 'scrim', $styling: { background: 'transparent', 'z-index': '54' } }).on({ click: () => social.toggleInbox(false) }),\n div({ className: 'inbox' },\n div({ className: 'inbox-head' },\n span('Notifications'),\n state.so.pushSupported && !state.so.pushEnabled\n ? button({ className: 'link' }, 'Enable push').on({ click: () => social.enablePush() })\n : state.so.pushEnabled ? span({ className: 'muted' }, icon('check', { size: 13 })) : null,\n ),\n items.length\n ? div({ className: 'inbox-items' }, ...items.slice(0, 30).map((note) => inboxItem(note, ui)))\n : div({ className: 'inbox-empty' }, icon('info', { size: 24 }), span('You’re all caught up.')),\n ),\n );\n}\n\nfunction inboxItem(note, ui) {\n const first = note.items?.[0];\n return div({ className: `inbox-item ${note.read ? '' : 'unread'}` },\n div({ className: 'ii-title' }, note.title),\n first?.excerpt ? div({ className: 'ii-excerpt' }, '“' + first.excerpt + '”') : null,\n div({ className: 'ii-time' }, relativeDate(note.createdAt)),\n ).on({\n click: () => {\n if (first?.nodeId) {\n ui.platform.api.stat(first.nodeId).then((r) => {\n ui.go(new OpenFileAction(r.node));\n ui.platform.workbench.toggleInfoPanel(true);\n }).catch((err) => {\n // The item a notification points at can be deleted, or live somewhere the\n // reader lost access to. Either way the click must not just do nothing.\n ui.platform.notifications.warn(\n err?.status === 403 || err?.code === 'forbidden'\n ? 'You no longer have access to that item.'\n : 'That item no longer exists.',\n );\n });\n }\n ui.app.social.toggleInbox(false);\n },\n });\n}\n\n// ---- info panel (tags + conversation) --------------------------------------\n\nexport function infoPanel(state, ui) {\n const nav = state.nav;\n const active = nav.activeFile ? { id: nav.activeTabId, node: nav.activeFile } : null;\n const sc = state.so.sidecar;\n return div({ className: 'infopanel' },\n div({ className: 'ip-head' },\n icon('info', { size: 15 }),\n span('Details'),\n button({ className: 'iconbtn', title: 'Close' }, icon('close', { size: 14 })).on({ click: () => ui.platform.workbench.toggleInfoPanel(false) }),\n ),\n !active\n ? div({ className: 'ip-empty' }, span('Open a file to see its tags and conversation.'))\n : div({ className: 'ip-body' },\n fileHeader(active.node, state, ui),\n linkSection(active.node, state, ui),\n tagSection(sc, ui),\n conversationSection(state, sc, ui),\n ),\n );\n}\n\nfunction fileHeader(node, state, ui) {\n const pinned = ui.app.offline.isPinned(node.id);\n return div({ className: 'ip-file' },\n div({ className: 'ip-name' }, node.name),\n div({ className: 'ip-path' }, node.contentType || ''),\n // Secondary, not primary. Pinning a file for offline is a useful thing to be able\n // to do and not the thing you came to this panel for — as the brightest element on\n // screen it out-shouted the file's own links, tags and conversation.\n button({ className: `btn small ${pinned ? 'on' : ''}`, $styling: { 'margin-top': '10px' } },\n icon(pinned ? 'check' : 'download', { size: 14 }),\n pinned ? 'Available offline' : 'Make available offline',\n ).on({ click: () => (pinned ? ui.exec('offline.unpin', node) : ui.exec('offline.pin', node)) }),\n );\n}\n\n/**\n * The item's own `trove:` link, and what links to it.\n *\n * With no folders, backlinks are the answer to \"where does this live?\" — the documents\n * that gather it up. A load failure says so rather than rendering as an empty list,\n * which would read as a fact about the drive instead of about the request.\n */\nfunction linkSection(node, state, ui) {\n const bl = state.so.backlinks;\n const uri = node.collectionId ? troveUri(node) : null;\n const mine = bl && bl.nodeId === node.id ? bl : null;\n return div({ className: 'ip-section' },\n div({ className: 'ip-label' }, 'Links'),\n uri\n ? button({ className: 'ip-uri', title: 'Copy this item’s link' }, icon('link', { size: 12 }), span(uri))\n .on({ click: () => copyLink(ui, uri) })\n : null,\n div({ className: 'ip-label', $styling: { 'margin-top': '10px' } }, 'Linked from'),\n mine?.loading\n ? div({ className: 'ip-muted' }, 'Loading…')\n : mine?.error\n ? div({ className: 'ip-muted error' }, `Couldn’t load links: ${mine.error}`)\n : mine?.items?.length\n ? div({ className: 'ip-backlinks' }, ...mine.items.map((n) =>\n button({ className: 'ip-backlink', title: n.name }, icon('file-text', { size: 12 }), span(n.name))\n .on({ click: () => ui.go(new OpenFileAction(n)) })))\n : div({ className: 'ip-muted' }, 'Nothing links here yet.'),\n );\n}\n\nasync function copyLink(ui, uri) {\n try {\n await navigator.clipboard.writeText(uri);\n ui.platform.notifications.success(`Copied ${uri}`);\n } catch {\n ui.platform.notifications.info(uri, { sticky: true });\n }\n}\n\nfunction tagSection(sc, ui) {\n const tags = sc?.tags || [];\n return div({ className: 'ip-section' },\n div({ className: 'ip-label' }, 'Tags'),\n div({ className: 'tags' },\n ...tags.map((t) =>\n span({ className: 'tag' },\n t.value ? `${t.name}: ${t.value}` : t.name,\n button({ className: 'tag-x' }, icon('close', { size: 11 })).on({ click: () => ui.app.social.removeTag(t.name) }),\n ),\n ),\n input({ className: 'tag-input', placeholder: '+ tag', value: '' }).on({\n keydown: (e) => {\n if (e.key === 'Enter' && e.target.value.trim()) {\n const raw = e.target.value.trim();\n const [name, ...rest] = raw.split(':');\n ui.app.social.addTag(name.trim(), rest.join(':').trim() || undefined);\n e.target.value = '';\n }\n },\n }),\n ),\n );\n}\n\nfunction conversationSection(state, sc, ui) {\n if (sc?.loading) return div({ className: 'ip-section' }, div({ className: 'spinner' }));\n const comments = sc?.comments || [];\n // On a load failure, don't render a false \"No comments yet\" — say it couldn't load\n // and offer a retry, so the user doesn't post into what looks like an empty thread.\n const body = sc?.error\n ? div({ className: 'conv-empty' },\n span(`Couldn't load the conversation: ${sc.error}`),\n button({ className: 'btn', $styling: { 'margin-top': '8px' } }, 'Retry').on({ click: () => ui.app.social.loadSidecar(sc.nodeId) }))\n : comments.length\n ? div({ className: 'thread' }, ...comments.map((c) => commentNode(c, state, ui, 0)))\n : div({ className: 'conv-empty' }, 'No comments yet. Start the discussion — use @ to mention someone.');\n return div({ className: 'ip-section conv' },\n div({ className: 'ip-label' }, `Conversation${sc?.commentCount ? ` · ${sc.commentCount}` : ''}`),\n body,\n sc?.error ? null : composer(state, ui),\n );\n}\n\nfunction commentNode(c, state, ui, depth) {\n const me = state.so.me;\n const social = ui.app.social;\n const mine = me && c.author?.id === me.id;\n return div({ className: 'comment', $styling: depth ? { marginLeft: '18px' } : {} },\n div({ className: 'c-head' },\n span({ className: 'c-avatar' }, (c.author?.name || '?')[0].toUpperCase()),\n span({ className: 'c-author' }, c.author?.name || c.author?.id || 'Someone'),\n span({ className: 'c-time' }, relativeDate(c.createdAt), c.edited ? ' · edited' : ''),\n ),\n c.deleted\n ? div({ className: 'c-body deleted' }, 'comment deleted')\n : div({ className: 'c-body' }, renderBody(c.body)),\n !c.deleted ? div({ className: 'c-actions' },\n ...REACTIONS.map((emoji) => {\n const users = c.reactions?.[emoji] || [];\n return button({ className: `react ${users.includes(me?.id) ? 'on' : ''}` }, emoji, users.length ? span({ className: 'rc' }, String(users.length)) : null)\n .on({ click: () => social.react(c.id, emoji) });\n }),\n button({ className: 'c-link' }, 'Reply').on({ click: () => social.setReplyTo({ id: c.id, author: c.author }) }),\n mine ? button({ className: 'c-link danger' }, 'Delete').on({ click: () => social.deleteComment(c.id) }) : null,\n ) : null,\n (c.replies || []).length ? div({ className: 'replies' }, ...c.replies.map((r) => commentNode(r, state, ui, depth + 1))) : null,\n ).key(c.id);\n}\n\nfunction composer(state, ui) {\n const social = ui.app.social;\n const me = state.so.me;\n const replyTo = state.so.replyTo;\n // `me` is null only when /api/me didn't answer — offline, or refused. It is NOT the\n // anonymous case (an anonymous deployment returns a real anonymous principal), so\n // \"Sign in to join the conversation\" was shown to people with nowhere to sign in,\n // and to people who were merely offline. Say which it is, and offer the one action\n // that helps: Trove ships no login of its own, so signing in means letting the\n // identity proxy in front of it redirect a fresh page load.\n if (!me) {\n if (!state.off?.online) {\n return div({ className: 'conv-signin' }, 'You’re offline. Conversations come back when you reconnect.');\n }\n return div({ className: 'conv-signin' },\n span('You’re signed out, so you can read this conversation but not add to it.'),\n button({ className: 'c-link' }, 'Reload to sign in').on({ click: () => window.location.reload() }),\n );\n }\n return div({ className: 'composer' },\n replyTo ? div({ className: 'replying' }, `Replying to ${replyTo.author?.name || 'comment'}`,\n button({ className: 'c-link' }, 'cancel').on({ click: () => social.setReplyTo(null) })) : null,\n textarea({ className: 'composer-input', placeholder: 'Write a comment… @mention someone', rows: 2, value: '' }).on({\n keydown: (e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n social.comment(e.target.value);\n e.target.value = '';\n }\n },\n }),\n div({ className: 'composer-actions' },\n span({ className: 'muted' }, '⌘/Ctrl + Enter'),\n button({ className: 'btn primary', disabled: state.so.posting }, 'Comment').on({\n click: (e) => {\n const box = e.target.closest('.composer').querySelector('.composer-input');\n social.comment(box.value);\n box.value = '';\n },\n }),\n ),\n );\n}\n\n// Render @[Name](id) tokens and bare @handles as highlighted mentions.\nfunction renderBody(text) {\n const out = [];\n const re = /@\\[([^\\]]+)\\]\\(([^)]+)\\)|(?:^|\\s)@([a-zA-Z0-9._@-]{2,})/g;\n let last = 0;\n let m;\n while ((m = re.exec(text))) {\n if (m.index > last) out.push(text.slice(last, m.index + (m[3] ? (m[0].startsWith(' ') ? 1 : 0) : 0)));\n out.push(span({ className: 'mention' }, '@' + (m[1] || m[3])));\n last = re.lastIndex;\n }\n if (last < text.length) out.push(text.slice(last));\n return out;\n}\n",
|
|
76
|
+
"// The left rail — the app's only global chrome now that the title bar is gone.\n// Brand mark at the top (→ home), the primary views below, then a bottom cluster\n// of notifications, settings, and the signed-in principal. Search is reached from\n// the Home view itself and via the double-shift modal, so there is no omni bar.\n\nimport { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { notificationBell, principalChip } from './social.js';\n\nconst { div, button, span, img } = dd;\n\nconst ITEMS = [\n { id: 'home', icon: 'search', title: 'Search & files', command: 'workbench.view.home' },\n { id: 'plugins', icon: 'plug', title: 'Plugins', command: 'workbench.view.plugins' },\n];\n\nexport default function activityBar(state, ui) {\n const active = state.wb.activity;\n const pluginCount = state.plugins?.filter((p) => p.status === 'active').length || 0;\n return div({ className: 'activitybar' },\n button({ className: 'brand-mark', title: 'Trove — home' }, img({ src: '/icon.svg', alt: 'Trove' }))\n .on({ click: () => ui.exec('workbench.view.home') }),\n ...ITEMS.map((it) =>\n button({ className: `item ${active === it.id ? 'active' : ''}`, title: it.title },\n icon(it.icon, { size: 22 }),\n it.id === 'plugins' && pluginCount ? span({ className: 'badge' }, String(pluginCount)) : null,\n ).on({ click: () => ui.exec(it.command) }),\n ),\n div({ className: 'spacer' }),\n notificationBell(state, ui),\n button({ className: `item ${active === 'settings' ? 'active' : ''}`, title: 'Settings' }, icon('gear', { size: 21 }))\n .on({ click: () => ui.exec('workbench.openSettings') }),\n principalChip(state),\n );\n}\n",
|
|
77
|
+
"// A small, allowlist-only HTML sanitizer for content a PLUGIN produces (status bar\n// items today). Plugin code runs on an opaque origin with no reach into the host DOM;\n// anything it hands us to render is untrusted string data and must not be able to\n// become script, load a resource, or navigate.\n//\n// Allowlist, not denylist: parse into an inert document, then rebuild only the nodes\n// and attributes named below. Anything unrecognised — an element, an attribute, a\n// URL scheme — is dropped rather than \"cleaned\", so a novel injection has nothing to\n// slip through. `template` parsing is inert: no scripts execute and no images load\n// while we walk it.\n\nconst ALLOWED_TAGS = new Set(['SPAN', 'B', 'STRONG', 'I', 'EM', 'CODE', 'SMALL', 'BR', 'S', 'U']);\nconst ALLOWED_ATTRS = new Set(['class', 'title']);\n// Cap the output so a runaway plugin can't wedge the shell with a megabyte of markup.\nconst MAX_LENGTH = 4096;\nconst MAX_NODES = 200;\n\n/**\n * Sanitize `html` into a DocumentFragment safe to insert into the host DOM.\n * @param {string} html\n * @returns {DocumentFragment}\n */\nexport function sanitizeFragment(html) {\n const out = document.createDocumentFragment();\n if (typeof html !== 'string' || !html) return out;\n const tpl = document.createElement('template');\n tpl.innerHTML = html.slice(0, MAX_LENGTH);\n const budget = { nodes: MAX_NODES };\n for (const child of tpl.content.childNodes) {\n const clean = clone(child, budget);\n if (clean) out.appendChild(clean);\n }\n return out;\n}\n\n/** Sanitize `html` and set it as the only content of `el`. */\nexport function setSanitizedHtml(el, html) {\n el.replaceChildren(sanitizeFragment(html));\n}\n\n/** The plain-text content of `html`, for tooltips and other attribute contexts. */\nexport function htmlToText(html) {\n const frag = sanitizeFragment(html);\n return (frag.textContent || '').trim();\n}\n\n/**\n * Sanitize `html` into VDOM nodes, so plugin content reconciles like everything else\n * instead of being an opaque innerHTML island the renderer can't see into.\n * @param {string} html\n * @param {Record<string, Function>} dd the element factories (dodo's `dd`)\n */\nexport function sanitizedVNodes(html, dd) {\n return [...sanitizeFragment(html).childNodes].map((n) => toVNode(n, dd)).filter((n) => n != null);\n}\n\nfunction toVNode(node, dd) {\n if (node.nodeType === Node.TEXT_NODE) return node.nodeValue;\n const factory = dd[node.tagName.toLowerCase()];\n if (!factory) return node.textContent; // allowed tag the renderer doesn't model\n const props = {};\n if (node.className) props.className = node.className;\n if (node.title) props.title = node.title;\n return factory(props, ...[...node.childNodes].map((c) => toVNode(c, dd)).filter((n) => n != null));\n}\n\nfunction clone(node, budget) {\n if (budget.nodes-- <= 0) return null;\n if (node.nodeType === Node.TEXT_NODE) return document.createTextNode(node.nodeValue);\n if (node.nodeType !== Node.ELEMENT_NODE) return null; // comments, CDATA, …\n if (!ALLOWED_TAGS.has(node.tagName)) return null;\n\n const el = document.createElement(node.tagName.toLowerCase());\n for (const attr of node.attributes) {\n const name = attr.name.toLowerCase();\n // `on*` handlers never reach here (not in the allowlist), but be explicit: no\n // event handler, no `style` (CSS can exfiltrate via url()), no URL attributes.\n if (!ALLOWED_ATTRS.has(name)) continue;\n el.setAttribute(name, attr.value.slice(0, 200));\n }\n for (const child of node.childNodes) {\n const clean = clone(child, budget);\n if (clean) el.appendChild(clean);\n }\n return el;\n}\n",
|
|
78
|
+
"import { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { bytes } from '../format.js';\nimport { sanitizedVNodes, htmlToText } from '../sanitize.js';\n\nconst { div, button, span } = dd;\n\n/**\n * A plugin's status slot. The manifest declares the slot (name + side); the plugin\n * pushes content into it at runtime over RPC. The content is untrusted HTML from a\n * sandboxed frame, so it goes through the allowlist sanitizer before it becomes nodes.\n * A slot with nothing in it, or one whose `when` doesn't hold, renders nothing at all\n * rather than an empty chip.\n */\nfunction statusSlot(item, ui) {\n if (item.visible === false || !item.html) return null;\n if (item.when && !ui.platform.context.evaluate(item.when)) return null;\n if (!ui.platform.plugins.isAvailable(item)) return null;\n const content = sanitizedVNodes(item.html, dd);\n if (!content.length) return null;\n const title = item.tooltip ? htmlToText(item.tooltip) : '';\n return item.command\n ? button({ className: 'seg', title }, ...content).on({ click: () => ui.exec(item.command) })\n : span({ className: 'seg', title }, ...content);\n}\n\n/**\n * The two things that make background work and standing problems discoverable at all.\n *\n * Running work shows only while it is running — a status bar that always carries an\n * \"Activity\" button teaches people to ignore it. The attention badge is the opposite:\n * it stays until the problem is actually fixed, because that is the whole point of an\n * issue as against a toast that scrolls away.\n */\nexport function activityChips(state, ui) {\n const act = state.act || { tasks: [], issues: [] };\n const running = act.tasks.filter((t) => t.status === 'running');\n const open = () => ui.app.activity.togglePanel(true);\n const chips = [];\n\n if (running.length) {\n const first = running[0];\n const determinate = first.total != null && first.total > 0;\n const pct = determinate ? Math.round(((first.done || 0) / first.total) * 100) : null;\n chips.push(button({\n className: 'seg sb-activity',\n title: running.map((t) => t.title).join('\\n'),\n },\n div({ className: 'spinner', $styling: { width: '11px', height: '11px' } }),\n span(running.length > 1\n ? `${running.length} running`\n : `${first.title}${pct == null ? '' : ` ${pct}%`}`)).on({ click: open }));\n }\n\n if (act.issues.length) {\n chips.push(button({\n className: 'seg sb-attention',\n title: act.issues.map((i) => i.title).join('\\n'),\n }, icon('info', { size: 12 }), span(`${act.issues.length} need${act.issues.length === 1 ? 's' : ''} attention`))\n .on({ click: open }));\n }\n return chips;\n}\n\n/**\n * How full the disk is — but only when the backend actually knows.\n *\n * A filesystem or NAS can answer exactly, and that is where it matters: a disk fills\n * up and every upload starts failing with no warning that anything was coming. An\n * object store has no equivalent number, so this renders nothing at all rather than a\n * meter that means nothing. The bar turns amber under 10% free and red under 5%, which\n * is early enough to act on.\n */\nexport function usageChip(ex) {\n const u = ex.usage;\n if (!u?.total) return null;\n const freeRatio = u.available / u.total;\n const pct = Math.min(100, Math.round((u.used / u.total) * 100));\n const level = freeRatio < 0.05 ? 'critical' : freeRatio < 0.1 ? 'low' : '';\n return span({\n className: `seg sb-usage ${level}`,\n title: `${bytes(u.used)} used of ${bytes(u.total)} — ${bytes(u.available)} free on this volume`,\n },\n div({ className: 'usage-bar' }, div({ className: 'usage-fill', $styling: { width: `${pct}%` } })),\n span(`${bytes(u.available)} free`));\n}\n\n/**\n * Everything the status bar reports, derived once.\n *\n * Two shells render these: the desktop as a row of segments along the bottom, the phone\n * as rows in a sheet behind a single icon. Deriving them in one place is what stops the\n * two from quietly telling the user different things about the same drive.\n */\nexport function statusFacts(state, ui) {\n const ex = state.ex;\n const items = ex.items || [];\n const act = state.act || { tasks: [], issues: [] };\n return {\n collectionId: ex.collectionId || 'default',\n // The COLLECTION's totals when the server could give them, not the page's. Summing\n // what happens to be loaded reports a 3,000-file drive as 500 files — a wrong number,\n // not a rounded one. Falls back to the page only when the server didn't say.\n totalItems: ex.stats?.items ?? items.length,\n // Whether that total is the COLLECTION's or just the page we happen to hold. With\n // more pages waiting, the page length is a floor, not a total, and must read as one.\n totalKnown: ex.stats?.items != null,\n totalBytes: ex.stats?.bytes ?? items.reduce((n, i) => n + (i.size || 0), 0),\n shown: items.length,\n partial: !!ex.nextCursor,\n usage: ex.usage,\n uploading: state.tr.items.filter((t) => t.status === 'active'),\n running: act.tasks.filter((t) => t.status === 'running'),\n issues: act.issues,\n off: state.off || { online: true, pins: [], queued: 0, syncing: false },\n caps: ui.platform.capabilities,\n };\n}\n\nexport default function statusBar(state, ui) {\n const ex = state.ex;\n const items = ex.items || [];\n const f = statusFacts(state, ui);\n const { totalItems, totalBytes, caps } = f;\n const active = f.uploading;\n\n // Plugin-contributed status slots, ordered by their declared `order` then name.\n const slots = [...(state.statusItems || [])]\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name));\n const left = slots.filter((s) => s.slot === 'left').map((s) => statusSlot(s, ui));\n const right = slots.filter((s) => s.slot !== 'left').map((s) => statusSlot(s, ui));\n\n const off = f.off;\n return div({ className: 'statusbar' },\n !off.online\n ? span({ className: 'seg offline-badge', title: 'You are offline — pinned files and cached data are available' }, icon('info', { size: 12 }), span('Offline'))\n : off.syncing\n ? span({ className: 'seg', title: 'Syncing offline changes' }, div({ className: 'spinner', $styling: { width: '11px', height: '11px' } }), span('Syncing…'))\n : null,\n off.queued ? span({ className: 'seg', title: 'Changes waiting to sync' }, `${off.queued} queued`) : null,\n // The pinned files themselves. There is no offline-files view anywhere, so a chip\n // counting them that only dropped you on the home screen was a label pretending to\n // be a control — and unpinning meant finding each file again through search.\n off.pins.length\n ? button({ className: 'seg', title: `${off.pins.length} file(s) available offline` },\n icon('download', { size: 12 }), span(`${off.pins.length} offline`), icon('chevron-down', { size: 11 }))\n .on({ click: (e) => {\n const items = off.pins.slice(0, 12).flatMap((p) => [\n { label: p.name, icon: 'file-text', run: () => ui.exec('explorer.open', p) },\n { label: `Remove “${p.name}” from offline`, icon: 'close', run: () => ui.exec('offline.unpin', p) },\n { sep: true },\n ]);\n if (off.pins.length > 12) items.push({ label: `…and ${off.pins.length - 12} more`, run: () => {} });\n const r = e.currentTarget.getBoundingClientRect();\n ui.platform.workbench.showContextMenu(r.left, r.top - 8 - items.length * 34, items);\n } })\n : null,\n button({ className: 'seg', title: 'Switch collection' },\n icon('files', { size: 13 }), span(ex.collectionId || 'default'),\n (ex.collections || []).length > 1 || ex.canCreateCollection ? icon('chevron-down', { size: 11 }) : null)\n .on({ click: (e) => {\n const items = ui.app.collectionMenu?.() || [];\n // With nowhere else to go, the segment is a label, not a dead menu.\n if (items.length > 1 || ex.canCreateCollection) {\n const r = e.currentTarget.getBoundingClientRect();\n ui.platform.workbench.showContextMenu(r.left, r.top - 8 - items.length * 34, items);\n } else ui.exec('workbench.view.home');\n } }),\n active.length\n ? button({ className: 'seg', title: 'Active uploads — click to cancel' }, div({ className: 'spinner', $styling: { width: '11px', height: '11px' } }), span(`${active.length} uploading`))\n .on({ click: (e) => ui.platform.workbench.showContextMenu(e.clientX, e.clientY, active.map((t) => ({\n label: `Cancel ${t.name} (${Math.round((t.ratio || 0) * 100)}%)`, icon: 'close', danger: true,\n run: () => ui.app.transfers.cancel(t.id),\n }))) })\n : span({ className: 'seg', title: ex.nextCursor ? `Showing ${items.length} of ${f.totalKnown ? totalItems : 'more'}` : '' },\n `${totalItems.toLocaleString()}${f.totalKnown || !f.partial ? '' : '+'} item${totalItems === 1 ? '' : 's'}`),\n ...left,\n div({ className: 'spacer' }),\n ...activityChips(state, ui),\n ...right,\n // Same qualifier as the item count beside it. Without it the two numbers in one bar\n // disagreed about their own honesty: \"500+ items\" next to a byte figure that was\n // only the loaded page's, presented as the collection's.\n span({\n className: 'seg',\n title: f.totalKnown || !f.partial\n ? 'Total size of this collection'\n : `Size of the ${f.shown.toLocaleString()} items loaded so far — this server doesn’t report a collection total`,\n }, `${bytes(totalBytes)}${f.totalKnown || !f.partial ? '' : '+'}`),\n usageChip(ex),\n caps ? span({ className: 'seg', title: 'Storage backend capabilities' },\n icon(caps.storage?.presignDownload ? 'download' : 'files', { size: 12 }),\n span(caps.storage?.presignDownload ? 'S3 direct' : 'proxied'),\n ) : null,\n caps?.features?.semanticSearch ? button({ className: 'seg', title: 'Semantic search enabled' }, icon('star', { size: 12 }), span('semantic'))\n .on({ click: () => ui.exec('workbench.view.home') }) : null,\n );\n}\n",
|
|
79
|
+
"// The pieces every view draws the same way.\n//\n// A view decides how results are ARRANGED. It does not get to decide what a group\n// header says, or where the row menu opens, because those are the same promises\n// whichever way the drive is drawn: the header carries the group's action (Upload,\n// Empty trash, Retry) and the menu carries everything you can do to a file. A view that\n// dropped either would quietly take working features off the screen.\n\nimport { dd } from '../../../runtime.js';\nimport { icon } from '../../icon.js';\n\nconst { div, span, button } = dd;\n\n/**\n * A group's heading.\n *\n * `group.title` is uppercased by CSS, which is right for a label and wrong for anything\n * the user typed — it turns their `#draft` into `#DRAFT`, a tag that isn't what they\n * wrote. So a group can carry a verbatim half.\n */\nexport function groupHeader(group) {\n return div({ className: 'launch-h' },\n // Label and value together on the left; `.launch-h` is space-between, so an\n // ungrouped value gets flung to the far edge away from the label it belongs to.\n div({ className: 'lh-title' },\n span(group.title),\n group.verbatim ? span({ className: 'lh-verbatim' }, group.verbatim) : null),\n group.action || null,\n );\n}\n\n/** The \"…\" button, for items that have a menu. */\nexport function menuButton(it, ui, select) {\n if (!it.menu) return null;\n return button({\n className: 'launch-more',\n title: `Actions for ${it.title}`,\n $attrs: { 'aria-label': `Actions for ${it.title}` },\n }, icon('dots', { size: 14 }))\n .on({ click: (e) => { e.stopPropagation(); openRowMenu(e.currentTarget, it, ui, null, select); } });\n}\n\n/**\n * Open an item's menu, anchored to the thing it belongs to.\n *\n * Anchored to the row (or the pointer, when there was one) rather than to a remembered\n * click position, so the keyboard route lands somewhere sensible too.\n */\nexport function openRowMenu(anchor, it, ui, event, select) {\n const r = anchor?.getBoundingClientRect?.();\n const x = event?.clientX ?? (r ? r.right - 8 : 0);\n const y = event?.clientY ?? (r ? r.bottom : 0);\n // Read the coordinates BEFORE selecting: selecting re-renders, and `anchor` is then\n // a detached node whose rect is all zeroes.\n const items = it.menu(ui);\n select?.();\n ui.platform.workbench.showContextMenu(x, y, items);\n}\n",
|
|
80
|
+
"// The list view: one row per item, which is what a drive looks like unless you ask\n// for something else.\n//\n// It is also the fallback for every other view: one that throws lands here, and views\n// are host code with no sandbox to catch them first. So it must not depend on anything\n// beyond the contract itself — no settings, no capabilities, no network.\n\nimport { dd } from '../../../runtime.js';\nimport { icon } from '../../icon.js';\nimport { groupHeader, menuButton, openRowMenu } from './parts.js';\n\nconst { div, span } = dd;\n\nexport function listView({ groups, index, handlers, ui }) {\n let gi = -1;\n return div({ className: 'launch-view view-list' },\n ...groups.map((group) => div({ className: 'launch-group' },\n groupHeader(group),\n group.items.length\n ? div({ className: 'launch-list' }, ...group.items.map((it) => {\n const at = ++gi;\n return itemRow(it, at === index, {\n hover: () => handlers.hover(at),\n select: () => handlers.select(at),\n }, ui);\n }))\n : div({ className: 'launch-empty' }, group.empty || 'Nothing here.'),\n )),\n );\n}\n\nfunction itemRow(it, active, { hover, select }, ui) {\n return div({ className: `launch-item ${active ? 'active' : ''}` },\n icon(it.icon, { size: 15 }),\n span({ className: 'name' }, it.title),\n it.detail ? span({ className: 'launch-detail' }, it.detail) : null,\n it.badge ? span({ className: 'launch-kind' }, it.badge) : null,\n // Everything you can do to a file, on the file. Rename, download, copy link and\n // delete were all commands with no way to reach them: the palette's versions act on\n // \"the selection\", and until the highlight became a selection there never was one.\n menuButton(it, ui, select),\n // One `.on()` call: a second replaces the handler map rather than merging into it,\n // which is how adding `contextmenu` silently removed `click` and stopped every file\n // in the drive from opening.\n ).on({\n click: it.run,\n mouseenter: hover,\n ...(it.menu ? { contextmenu: (e) => { e.preventDefault(); openRowMenu(e.currentTarget, it, ui, e, select); } } : {}),\n });\n}\n",
|
|
81
|
+
"// Keeping a media element pointed at a URL that still works.\n//\n// A minted URL expires (docs/design/signed-urls.md). Content URLs are long — longer than\n// almost any sitting — but \"almost\" is not \"always\": someone pauses a film, closes the\n// laptop, and comes back tomorrow. So the element has to be able to swap its own source,\n// and swapping the source under a playing video is not a matter of assigning `src`.\n//\n// Both refresh paths are needed and neither is sufficient alone:\n//\n// PROACTIVE a timer from `expiresAt`. Handles the common case invisibly.\n// REACTIVE the element's own `error`. A backgrounded tab has its timers throttled,\n// so the proactive refresh may simply never run — which is exactly the\n// paused-overnight case, the one the timer was supposed to cover.\n\nconst REFRESH_AT = 0.8; // of the URL's life, so the swap lands well before it matters.\nconst MAX_TIMER = 2 ** 31 - 1; // setTimeout overflows past this and fires immediately.\n\n/**\n * Point `el` at `node`'s bytes and keep it pointed there.\n *\n * @param {HTMLMediaElement|HTMLImageElement} el\n * @param {object} node\n * @param {object} ui\n * @param {{op?: string, onError?: (msg: string) => void}} [opts]\n * @returns {() => void} detach\n */\nexport function attachMedia(el, node, ui, { op = 'media', onError } = {}) {\n const urls = ui.platform.mediaUrls;\n let timer = null;\n let stopped = false;\n // One retry per URL. A file whose bytes are genuinely gone would otherwise re-mint\n // forever, hammering the server over a 404 that is never going to change.\n let retried = false;\n\n const clear = () => { clearTimeout(timer); timer = null; };\n\n const schedule = (expiresAt) => {\n clear();\n if (!Number.isFinite(expiresAt)) return; // nothing was minted; nothing expires\n const wait = Math.max(1000, (expiresAt - Date.now()) * REFRESH_AT);\n timer = setTimeout(() => apply({ resume: true }), Math.min(wait, MAX_TIMER));\n };\n\n /**\n * Fetch a URL and put it on the element, preserving where the user was.\n *\n * The order matters and every step earns its place: without `resume` the film restarts\n * from the beginning, without `load()` an element that has already errored may not pick\n * up the new source at all, and without the final `play()` it sits there paused looking\n * like a crash.\n */\n async function apply({ resume }) {\n if (stopped) return;\n const media = isMedia(el);\n const at = media && resume ? el.currentTime : 0;\n const wasPlaying = media && resume ? !el.paused && !el.ended : false;\n let got;\n try {\n got = await urls.url(node.id, { op });\n } catch (err) {\n if (!stopped) onError?.(err.message);\n return;\n }\n if (stopped) return;\n el.src = got.url;\n if (media) {\n el.load();\n if (at > 0 || wasPlaying) {\n const restore = () => {\n el.removeEventListener('loadedmetadata', restore);\n // Guard the seek: a live or zero-length source has nowhere to seek to, and\n // assigning currentTime there throws.\n if (at > 0 && Number.isFinite(el.duration) && el.duration > at) el.currentTime = at;\n if (wasPlaying) el.play().catch(() => { /* autoplay policy; the user can press play */ });\n };\n el.addEventListener('loadedmetadata', restore);\n }\n }\n schedule(got.expiresAt);\n }\n\n // The retry budget is spent by a failed LOAD and refunded by a successful one — not by\n // a successful mint. Refunding it in `apply` meant a file whose bytes are genuinely\n // gone re-minted forever: every retry got a perfectly good URL, reset the budget, and\n // failed again, so the fallback was never shown and the request never stopped.\n const onLoaded = () => { retried = false; };\n el.addEventListener('load', onLoaded); // <img>\n el.addEventListener('loadeddata', onLoaded); // <audio>/<video>\n\n // The reactive half. An expired URL and a missing file look identical from here — both\n // are just a failed load — so it re-mints once and, if that fails too, says so.\n const onElementError = () => {\n if (stopped) return;\n if (retried) { onError?.(null); return; }\n retried = true;\n urls.invalidate(node.id, op);\n apply({ resume: true });\n };\n el.addEventListener('error', onElementError);\n\n apply({ resume: false });\n\n return () => {\n stopped = true;\n clear();\n el.removeEventListener('error', onElementError);\n el.removeEventListener('load', onLoaded);\n el.removeEventListener('loadeddata', onLoaded);\n };\n}\n\nfunction isMedia(el) {\n return typeof el?.play === 'function' && typeof el?.load === 'function';\n}\n",
|
|
82
|
+
"// The grid view: tiles, with a picture on them where there is a picture to show.\n//\n// This is the second way to look at a drive, and it exists as much to prove the view\n// contract as to be useful — a contribution type with one implementation is a guess. It\n// draws the SAME groups the list does, headers and all, because the group header is\n// where Upload, Empty trash and Retry live: a view that dropped it would take those off\n// the screen the moment someone switched to tiles.\n//\n// On thumbnails, honestly: there is no thumbnail service yet, so a tile showing a\n// photograph is showing the photograph, scaled down by the browser. That is affordable\n// only because `loading=\"lazy\"` means the ones below the fold are never fetched — the\n// grid asks for what is on screen and nothing else. A hosted build that generates real\n// thumbnails should register its own view rather than patch this one; that is the whole\n// point of the contribution.\n\nimport { dd } from '../../../runtime.js';\nimport { icon, iconForNode } from '../../icon.js';\nimport { groupHeader, menuButton, openRowMenu } from './parts.js';\nimport { attachMedia } from '../../media.js';\n\nconst { div, span, img } = dd;\n\nexport function gridView({ groups, index, handlers, ui }) {\n let gi = -1;\n return div({ className: 'launch-view view-grid' },\n ...groups.map((group) => div({ className: 'launch-group' },\n groupHeader(group),\n group.items.length\n ? div({ className: 'grid-list' }, ...group.items.map((it) => {\n const at = ++gi;\n return tile(it, at === index, {\n hover: () => handlers.hover(at),\n select: () => handlers.select(at),\n }, ui);\n }))\n : div({ className: 'launch-empty' }, group.empty || 'Nothing here.'),\n )),\n );\n}\n\n/**\n * Which way to move for an arrow key.\n *\n * The launcher owns keyboard navigation and thinks in one flat list, which is right for\n * rows and wrong for tiles: down should mean the tile below, not the tile beside. The\n * view is asked, gets to answer in the launcher's own terms (a delta), and the launcher\n * keeps the wrapping, the selection and the scrolling.\n *\n * The column count is measured rather than assumed, because it comes from\n * `auto-fill` — it depends on the window width, the shell, and whether the sidebar\n * is open, none of which this file should be trying to predict.\n *\n * Left and right are only ours when the search box is empty. With a query typed in it,\n * those keys belong to the caret: someone fixing a typo must not find the highlight\n * walking across the grid instead.\n */\nexport function gridMove({ key, textual }) {\n if (key === 'ArrowLeft') return textual ? null : -1;\n if (key === 'ArrowRight') return textual ? null : 1;\n const cols = columns();\n if (key === 'ArrowUp') return -cols;\n if (key === 'ArrowDown') return cols;\n return null;\n}\n\nfunction columns() {\n // Tiles on the same row share an offsetTop. One row's worth is the answer; a grid with\n // a single row (or none on screen) falls back to 1, which is the list's behaviour and\n // never worse than a guess.\n const tiles = document.querySelectorAll('.grid-list .grid-tile');\n if (tiles.length < 2) return 1;\n const top = tiles[0].offsetTop;\n let n = 0;\n for (const el of tiles) {\n if (el.offsetTop !== top) break;\n n++;\n }\n return n || 1;\n}\n\nfunction tile(it, active, { hover, select }, ui) {\n const node = it.node;\n const pictorial = (node?.contentType || '').startsWith('image/');\n return div({ className: `grid-tile ${active ? 'active' : ''}`, title: it.detail ? `${it.title} — ${it.detail}` : it.title },\n div({ className: 'gt-media' },\n // The icon is drawn underneath, always. When the image loads it covers it; when\n // the image fails — a deleted object, a format the browser won't decode, a 401 —\n // hiding the <img> uncovers exactly what the list would have shown, rather than\n // leaving the browser's broken-image glyph in a gallery.\n icon(node ? iconForNode(node) : it.icon, { size: 26 }),\n // The `src` is minted rather than built — a tile fetches without our Authorization\n // header, so the URL has to carry its own grant. Minting is batched (see\n // platform/mediaUrls.js), so a wall of tiles costs one request, not one each.\n pictorial\n ? img({ className: 'gt-img', alt: '', loading: 'lazy', decoding: 'async' }).on({\n $attach: (dom) => {\n dom._detachMedia = attachMedia(dom, node, ui, {\n // Uncovering the icon underneath is exactly what the list would have shown,\n // and better than the browser's broken-image glyph in a gallery.\n onError: () => { dom.style.display = 'none'; },\n });\n },\n $detach: (dom) => { dom._detachMedia?.(); dom._detachMedia = null; },\n }).opaque()\n : null,\n it.badge ? span({ className: 'gt-badge' }, it.badge) : null,\n menuButton(it, ui, select),\n ),\n div({ className: 'gt-name' }, it.title),\n ).on({\n click: it.run,\n mouseenter: hover,\n ...(it.menu ? { contextmenu: (e) => { e.preventDefault(); openRowMenu(e.currentTarget, it, ui, e, select); } } : {}),\n });\n}\n",
|
|
83
|
+
"// How a list of items is DRAWN.\n//\n// An opener renders one file; a view renders the results. They register through the\n// same contribution system, so \"a gallery for a collection full of photographs\" is a\n// contribution rather than an edit to the launcher — which is the whole point: the\n// launcher should not have to know how many ways there are to look at a drive.\n//\n// A view is the workbench's own: the built-ins, plus whatever a build passes to\n// `createWorkbench({ views })`. It runs in the host, so the contract is a dodo vnode.\n//\n// The contract is one function:\n//\n// render({ groups, index, handlers, state, ui }) -> vnode\n//\n// `groups` is what the launcher assembled — `[{ title, verbatim?, action?, items,\n// empty? }]`, each item `{ icon, title, detail, badge, node?, run() }`. `index` is the\n// flat position of the highlighted item across every group, because keyboard\n// navigation is the launcher's job and not the view's: up and down must mean the same\n// thing whichever way the results happen to be drawn.\n\nimport { dd } from '../../../runtime.js';\nimport { icon } from '../../icon.js';\nimport { listView } from './list.js';\nimport { gridView, gridMove } from './grid.js';\n\nconst { div, button, span } = dd;\n\nconst BUILT_IN = {\n 'core.view.list': {\n title: 'List', icon: 'list', priority: 50,\n render: listView,\n },\n 'core.view.grid': {\n // Offered first where the results are pictures, and available everywhere.\n title: 'Grid', icon: 'grid', priority: 40,\n match: { mime: ['image/*', 'video/*'] },\n render: gridView, move: gridMove,\n },\n};\n\nexport function registerBuiltinViews(platform) {\n for (const [name, spec] of Object.entries(BUILT_IN)) {\n platform.contributions.register(name, { type: 'view', ...spec });\n }\n}\n\nconst SETTING = 'explorer.view';\n\n/**\n * Every view that could draw these results, best first.\n *\n * No availability check, unlike openers: a view is host code, so there is no \"its\n * provider isn't answering\" state to be in. A when-clause is the only gate, and it is\n * the same one every other contribution answers to.\n */\nexport function availableViews(platform) {\n return platform.contributions\n .ofType('view')\n .filter((v) => !v.when || platform.context.evaluate(v.when))\n .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n}\n\n/**\n * The view to draw with, in order of how much someone meant it.\n *\n * 1. A saved choice — they pressed a button. Nothing infers its way past that.\n * 2. The search transformer's hint, when there is a search on screen. It read the\n * sentence: \"photos from the trip last summer\" is a request for a gallery as much as\n * it is a query, and nothing downstream can recover that from a list of content types.\n * 3. A view whose `match` suits what is actually there — a collection of photographs\n * opens as a grid without anyone asking.\n * 4. The highest priority, which is the list.\n *\n * A hint naming a view this build doesn't have is ignored, not an error: the transformer\n * is deployment configuration and may outlive the build it was written against.\n */\nexport function activeView(platform, items = [], hint = null) {\n const views = availableViews(platform);\n if (!views.length) return null;\n const saved = platform.settings.get(SETTING);\n const chosen = saved && views.find((v) => v.id === saved);\n if (chosen) return chosen;\n const suggested = hint && views.find((v) => v.id === hint);\n if (suggested) return suggested;\n const nodes = items.map((i) => i.node).filter(Boolean);\n if (nodes.length >= 3) {\n const suits = (v) => v.match && Object.keys(v.match).length\n && nodes.filter((n) => matchesView(v, n)).length / nodes.length > 0.6;\n const fitted = views.find(suits);\n if (fitted) return fitted;\n }\n return views[0];\n}\n\nfunction matchesView(view, node) {\n const ct = node.contentType || '';\n const name = (node.name || '').toLowerCase();\n const ext = name.includes('.') ? name.slice(name.lastIndexOf('.')) : '';\n if ((view.match.ext || []).includes(ext)) return true;\n return (view.match.mime || []).some((m) => (m.endsWith('/*') ? ct.startsWith(m.slice(0, -1)) : ct === m));\n}\n\nexport function chooseView(platform, viewId) {\n platform.settings.set(SETTING, viewId || undefined);\n}\n\n/** The switcher, shown only when there is more than one way to look at this. */\nexport function viewSwitcher(platform, current) {\n const views = availableViews(platform);\n if (views.length < 2) return null;\n return div({ className: 'view-switch' }, ...views.map((v) =>\n button({\n className: `vs-btn ${v.id === current?.id ? 'on' : ''}`,\n title: `${v.title || v.name} view`,\n 'aria-pressed': v.id === current?.id ? 'true' : 'false',\n }, icon(v.icon || 'list', { size: 14 })).on({ click: () => chooseView(platform, v.id) })));\n}\n\n/**\n * Draw the results with `view`, falling back to the list if it throws.\n *\n * A view is the whole results area. One that fails takes the drive's contents off the\n * screen with it, so a broken third-party view degrades to the built-in list rather\n * than to nothing.\n */\n/**\n * The delta an arrow key should move the highlight by, or null for the default.\n *\n * Keyboard navigation stays the launcher's: it owns the index, the selection and the\n * wrapping. But \"down\" means the row below in a list and the tile below in a grid, so\n * the view is asked first and answers in the launcher's own terms.\n */\nexport function viewMove(view, key, ctx) {\n try {\n const d = view?.move?.({ key, ...ctx });\n return Number.isFinite(d) && d !== 0 ? d : null;\n } catch {\n return null; // a view that can't answer doesn't get to break the arrow keys\n }\n}\n\nexport function renderView(view, ctx) {\n try {\n if (typeof view?.render === 'function') return view.render(ctx);\n } catch (err) {\n console.error(`view \"${view?.id}\" failed`, err);\n return div({},\n div({ className: 'view-error' }, icon('warn', { size: 13 }),\n span(`The “${view?.title || view?.id}” view could not be drawn — showing the list instead.`)),\n listView(ctx));\n }\n return listView(ctx);\n}\n\nexport { listView, gridView };\n",
|
|
84
|
+
"// The main-panel launcher — search-first \"home\" that replaces the explorer\n// sidebar. Empty query shows recents + everything in the collection; typing\n// searches files; a leading `!` switches to command execution; a leading `#`\n// filters by tag/property (parsed in the search layer). One keyboard-navigable\n// list across whichever mode is active.\n\nimport { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { OpenFileAction, SearchAction, FilterAction } from '../../bl/actions.js';\nimport { parseTagQuery, filterLabel } from '../../bl/tagQuery.js';\nimport { activeView, renderView, viewSwitcher, viewMove } from './views/index.js';\nimport { openRowMenu } from './views/parts.js';\n\nconst { div, span, input, button } = dd;\n\n// The keys a view may claim. Everything else the search box keeps.\nconst ARROWS = new Set(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']);\n\n// What to put in the search box, and how much of it fits.\n//\n// The server tells us, because the search TRANSFORMER decides what the box accepts and\n// that is deployment configuration — swap in an LLM transformer and \"# filter by tag\"\n// stops being the right advice. The fallback covers the moment before capabilities\n// arrive and any server too old to say; `!` is ours either way, since running a command\n// from here is a client convention the server knows nothing about.\nfunction promptFor(ui, { compact = false, modal = false } = {}) {\n const p = ui.platform.capabilities?.searchPrompt;\n const base = (compact ? p?.short || p?.placeholder : p?.placeholder)\n || (compact ? 'Search files' : 'Search files · # filter by tag');\n // On a phone the box is ~300px wide; adding a second clause guarantees an ellipsis\n // in the middle of the instructions, which reads as broken rather than terse.\n return compact || modal ? base : `${base} · ! run a command`;\n}\n\nlet searchTimer = null;\nfunction runSearch(ui, query) {\n // SearchAction owns search state; the launcher only dispatches (no direct .set).\n clearTimeout(searchTimer);\n searchTimer = setTimeout(() => ui.go(new SearchAction(query)), 240);\n}\nfunction clearSearch(ui) {\n ui.platform.workbench.setLaunchQuery('');\n ui.go(new SearchAction('')); // empty query resets results/ran/error in the service\n}\nfunction runFilter(ui, filters, text) {\n clearTimeout(searchTimer);\n searchTimer = setTimeout(() => ui.go(new FilterAction(filters, text)), 200);\n}\n\nexport default function launcher(state, ui, opts = {}) {\n const wb = ui.platform.workbench;\n const modal = !!opts.modal; // rendered as the double-shift overlay → picks reset the stack\n const q = state.wb.launch.query;\n const mode = q.startsWith('!') ? 'command' : q.includes('#') ? 'filter' : 'search';\n\n const groups = buildContent(state, ui, q, mode, modal);\n const flat = groups.flatMap((g) => g.items);\n const idx = flat.length ? Math.min(state.wb.launch.index, flat.length - 1) : 0;\n\n const onInput = (e) => {\n const v = e.target.value;\n wb.setLaunchQuery(v);\n if (v.startsWith('!')) return; // command mode: no query dispatch\n const { text, filters } = parseTagQuery(v);\n if (filters.length) runFilter(ui, filters, text); // drive-wide tag/property query\n else runSearch(ui, text);\n };\n // Moving the highlight with the KEYBOARD is selecting, as far as the rest of the app\n // is concerned. Without this, `explorer.selectedNodes()` was permanently empty and\n // every command that works on \"the selected item\" — delete above all — silently did\n // nothing.\n //\n // Hovering deliberately does not select. It moves the highlight (so Enter opens what\n // the pointer is over) but leaves the selection alone: a second state push per mouse\n // move re-renders the list under the pointer, and a row replaced between mousedown and\n // mouseup never receives its click. A pointer user acts through the row's own menu,\n // which selects explicitly before it opens.\n const hoverAt = (at) => wb.setLaunchIndex(at);\n const selectAt = (at) => { wb.setLaunchIndex(at); syncSelection(ui, flat[at]); };\n const move = (delta) => {\n wb.moveLaunch(delta, flat.length);\n syncSelection(ui, flat[wb.state.launch.index]);\n };\n // How the results are drawn right now. The view gets a say in what an arrow key\n // means — one row down is one tile down in a grid, not one tile across — but the\n // index, the selection and the wrapping stay here, so up and down mean the same\n // thing whichever view is showing.\n //\n // The search transformer may have suggested one. Only for an actual search: on the\n // home list there is no sentence to have read.\n const view = activeView(ui.platform, flat, mode === 'search' ? state.se.resolved?.view : null);\n const onKey = (e) => {\n // `textual`: there is text in the box, so left/right are the caret's and no view\n // may claim them. Fixing a typo must not move the highlight.\n const claimed = ARROWS.has(e.key)\n ? viewMove(view, e.key, { index: idx, count: flat.length, textual: q.length > 0 })\n : null;\n if (claimed !== null) { e.preventDefault(); move(claimed); }\n else if (e.key === 'ArrowDown') { e.preventDefault(); move(1); }\n else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1); }\n else if (e.key === 'Enter') { e.preventDefault(); flat[idx]?.run(); }\n else if (e.key === 'Escape' && q) { e.preventDefault(); clearSearch(ui); }\n // The row under the highlight is the subject of the row menu, so the key that opens\n // one on every other list opens this one too — without making the user leave the\n // search box to reach it.\n else if (e.key === 'ContextMenu' || (e.shiftKey && e.key === 'F10')) {\n const it = flat[idx];\n // Arrowing here already selected the row, so the menu acts on the right thing.\n if (it?.menu) { e.preventDefault(); openRowMenu(document.querySelector('.launch-item.active, .grid-tile.active'), it, ui); }\n }\n };\n\n // What the server actually searched (transformer output) — shown when it differs\n // from what the user typed, so an LLM-assisted transform stays honest.\n const resolved = mode === 'search' ? state.se.resolved : null;\n const showResolved = resolved && (resolved.source === 'llm'\n || (resolved.tagFilters && resolved.tagFilters.length)\n || (resolved.semanticText || '').trim() !== q.trim());\n\n const inner = div({ className: 'launcher' },\n div({ className: 'launch-box' },\n icon(mode === 'command' ? 'command' : mode === 'filter' ? 'tag' : 'search', { size: 18 }),\n // `false`, not `'false'`: this is set as a property, and the string is truthy —\n // so the search box had spellcheck ON, red-underlining every filename typed into it.\n input({ className: 'launch-input', value: q, autofocus: true, spellcheck: false,\n placeholder: promptFor(ui, { compact: state.vp?.mode === 'phone', modal }) })\n .on({ input: onInput, keydown: onKey }),\n q ? button({ className: 'launch-clear', title: 'Clear' }, icon('close', { size: 14 }))\n .on({ click: () => clearSearch(ui) }) : null,\n // Only where the browser can transcribe WITHOUT sending audio anywhere. A remote's\n // own mic needs no button from us — it dictates into this field once it is focused,\n // which is what `search.voice` is really for.\n ui.platform.voice?.canListen()\n ? button({\n className: `launch-mic ${state.voice?.listening ? 'on' : ''}`,\n title: state.voice?.listening ? 'Stop listening' : 'Search by voice',\n 'aria-pressed': state.voice?.listening ? 'true' : 'false',\n }, icon('mic', { size: 15 })).on({ click: () => ui.exec('search.voice') })\n : null,\n // Which way to look at the drive. Not in the modal search, where the answer is\n // always \"the one thing I am about to press Enter on\".\n modal ? null : viewSwitcher(ui.platform, view),\n ),\n showResolved ? resolvedBar(resolved) : null,\n // The results belong to the active view — see ui/components/views. The launcher says\n // WHAT is on screen (these groups, this highlight); the view says how it looks. That\n // split is why a gallery is a contribution rather than another branch in here.\n div({ className: 'launch-body' },\n renderView(view, { groups, index: idx, handlers: { hover: hoverAt, select: selectAt }, state, ui }),\n searchHelp(state, ui, mode),\n ),\n );\n return modal ? inner : div({ className: 'editor' }, inner);\n}\n\n// An \"under-bar\" reflecting what the search actually ran (the transformer's output):\n// the semantic text it searched for + any tag filters it applied. Read-only and\n// honest — we don't rewrite the user's input, we just show what was dispatched.\nfunction resolvedBar(r) {\n return div({ className: 'launch-resolved' },\n span({ className: 'rq-label' }, r.source === 'llm' ? 'Interpreted as' : 'Searching'),\n (r.semanticText || '').trim() ? span({ className: 'rq-text' }, `“${r.semanticText.trim()}”`) : null,\n ...(r.tagFilters || []).map((f) => span({ className: 'rq-chip' }, icon('tag', { size: 11 }), filterLabel(f))),\n );\n}\n\n// Keep the drive's idea of \"what is selected\" in step with the highlighted row.\n//\n// The NODE goes along with the id. A search result or a recent can be from a collection\n// that isn't loaded — or a page that isn't — so an id alone is something the explorer\n// cannot resolve, and the commands that act on \"the selection\" then do nothing at all.\nfunction syncSelection(ui, item) {\n const node = item?.node;\n ui.app.explorer.select(node?.id ? [node.id] : [], { nodes: node ? [node] : null });\n}\n\n/**\n * How this search box works — shown when, and only when, a search came back empty.\n *\n * That is the one moment the syntax is worth explaining: the user asked for something\n * and got nothing, and \"you typed it wrong\" is a real possibility they can act on.\n * Showing it any earlier is a permanent instruction panel above an empty drive, and\n * showing it when there ARE results would be explaining a thing that just worked.\n *\n * The text comes from the server's transformer, so it describes the grammar this\n * deployment actually runs rather than the one this file was written against.\n */\nfunction searchHelp(state, ui, mode) {\n if (mode === 'command') return null;\n const se = state.se;\n if (!se.ran || se.loading || se.error || (se.results || []).length) return null;\n const p = ui.platform.capabilities?.searchPrompt;\n if (!p?.hint && !p?.examples?.length) return null;\n const wb = ui.platform.workbench;\n const tryIt = (query) => () => {\n wb.setLaunchQuery(query);\n const { text, filters } = parseTagQuery(query);\n if (filters.length) ui.go(new FilterAction(filters, text));\n else ui.go(new SearchAction(text));\n };\n return div({ className: 'launch-help' },\n p.hint ? div({ className: 'lh-hint' }, icon('info', { size: 13 }), span(p.hint)) : null,\n p.examples?.length\n ? div({ className: 'lh-examples' }, ...p.examples.map((ex) =>\n button({ className: 'lh-example', title: ex.label || 'Try this search' },\n span({ className: 'lh-q' }, ex.query),\n ex.label ? span({ className: 'lh-label' }, ex.label) : null,\n ).on({ click: tryIt(ex.query) })))\n : null,\n );\n}\n\nfunction buildContent(state, ui, q, mode, modal) {\n const done = () => { if (modal) ui.platform.workbench.closeSearchModal(); };\n if (mode === 'command') {\n const term = q.slice(1).trim().toLowerCase();\n const items = ui.platform.commands.paletteCommands()\n .map((c) => ({ hay: `${c.category || ''} ${c.title}`.toLowerCase(), c }))\n .map(({ hay, c }) => ({ s: score(hay, term), c }))\n .filter((x) => term === '' || x.s > 0)\n .sort((a, b) => b.s - a.s)\n .slice(0, 40)\n .map(({ c }) => ({ icon: 'command', title: c.title, detail: c.category, badge: 'command', run: () => { ui.exec(c.id); done(); } }));\n return [{ title: 'Commands', items, empty: 'No matching commands.' }];\n }\n\n const { text, filters } = parseTagQuery(q);\n const nodes = (state.se.results || []).map((r) => r.node);\n const err = state.se.error;\n\n // A one-click retry so a transient search/filter failure isn't a dead end.\n const retryBtn = (action) => button({ className: 'launch-up', title: 'Retry' }, icon('refresh', { size: 13 }), 'Retry').on({ click: () => ui.go(action) });\n\n // Drive-wide tag/property filter (server-side), optionally narrowed by free text.\n if (filters.length) {\n const label = filters.map(filterLabel).join(' ') + (text.trim() ? ` · \"${text.trim()}\"` : '');\n return [{\n title: state.se.loading ? 'Filtering…' : err ? 'Filter failed' : 'Filtered',\n // Shown as typed: a tag is a value, not a heading.\n verbatim: state.se.loading || err ? null : label,\n action: err ? retryBtn(new FilterAction(filters, text)) : null,\n items: nodes.map((n) => fileItem(n, ui, modal)),\n empty: state.se.loading ? 'Filtering…' : err ? `Couldn’t filter: ${err}` : 'No files match those filters.',\n }];\n }\n\n // Free-text search.\n if (text.trim()) {\n return [{\n title: state.se.loading ? 'Searching…' : err ? 'Search failed' : 'Results',\n action: err ? retryBtn(new SearchAction(q)) : null,\n items: nodes.map((n) => fileItem(n, ui, modal)),\n empty: state.se.loading ? 'Searching…' : err ? `Couldn’t search: ${err}` : 'No files match.',\n }];\n }\n\n // Home: recents, then everything in the collection. There is nothing to descend\n // into — this is the \"show me everything\" fallback for when search isn't the answer.\n const groups = [];\n const recents = (state.nav.recents || []).map((r) => fileItem(r, ui, modal));\n if (recents.length) groups.push({ title: 'Recent', items: recents });\n\n const ex = state.ex;\n const shown = (ex.items || []).length;\n // `stats` is the collection; `items` is the page. When the server didn't report stats\n // we only know the page — and saying \"500\" while a next page exists is a claim about\n // the drive that is false, so say \"500+\" instead.\n const knownTotal = ex.stats?.items ?? null;\n const total = knownTotal ?? shown;\n const totalLabel = knownTotal != null ? knownTotal.toLocaleString() : `${shown.toLocaleString()}+`;\n const items = (ex.items || []).map((n) => fileItem(n, ui, modal));\n // A partial list must not be titled \"All items\" — that is a claim about the drive,\n // and on a collection bigger than one page it is false. Say what is on screen, and\n // offer the rest rather than leaving it unreachable.\n if (ex.nextCursor) {\n items.push({\n icon: 'refresh',\n title: ex.loadingMore ? 'Loading…'\n : knownTotal != null ? `Show more (${(knownTotal - shown).toLocaleString()} more)` : 'Show more',\n detail: 'or search to jump straight to something',\n run: () => ui.exec('explorer.loadMore'),\n });\n }\n // The trash, when it has been opened. Not shown by default: it is a place you go to\n // recover a mistake, not part of browsing the drive.\n if (ex.trash) {\n groups.push({\n // One line, not two: a header reading \"0 items\" above a body reading \"the trash is\n // empty\" is the same sentence twice, permanently parked above the drive.\n title: ex.trash.length\n ? `Trash · ${ex.trash.length} item${ex.trash.length === 1 ? '' : 's'}`\n : 'Trash',\n action: div({ className: 'lh-actions' },\n ex.trash.length\n ? button({ className: 'launch-up', title: 'Destroy everything in the trash' },\n icon('trash', { size: 13 }), 'Empty trash').on({ click: () => ui.exec('explorer.emptyTrash') })\n : null,\n // The way back out. Opening the trash used to be one-way until a page reload.\n button({ className: 'launch-up', title: 'Hide the trash' },\n icon('close', { size: 13 }), 'Close').on({ click: () => ui.exec('explorer.hideTrash') }),\n ),\n items: ex.trash.length\n ? ex.trash.map((n) => ({\n icon: 'trash',\n title: n.name,\n detail: `deleted ${new Date(n.deletedAt).toLocaleString()} — restore`,\n run: () => ui.exec('explorer.restore', n.id),\n menu: () => trashMenu(n, ui),\n }))\n : [],\n empty: 'The trash is empty.',\n });\n }\n\n groups.push({\n title: ex.nextCursor ? `All items · showing ${shown.toLocaleString()} of ${totalLabel}` : 'All items',\n // The empty state told people to upload a file, and on desktop and TV there was\n // nothing anywhere that would let them. (Phone has the + in its bottom bar.)\n action: modal ? null : button({ className: 'launch-up', title: 'Upload files to this collection' },\n icon('upload', { size: 13 }), 'Upload').on({ click: () => ui.exec('explorer.upload') }),\n items,\n // Don't show a false \"empty\" when the load actually FAILED (e.g. server\n // unreachable) — say so, so the user knows to retry rather than believing the\n // collection is empty. (A toast also fires, but the persistent state must be honest.)\n empty: ex.loading ? 'Loading…' : ex.error ? `Couldn’t load this collection: ${ex.error}` : 'Nothing here yet — upload a file to get started.',\n });\n return groups;\n}\n\nfunction fileItem(node, ui, modal) {\n return {\n icon: fileIcon(node),\n title: node.name,\n detail: node.contentType || '',\n node,\n // From the modal search, `reset` starts a fresh viewer stack; then close it.\n run: () => {\n ui.go(new OpenFileAction(node, { reset: !!modal }));\n if (modal) ui.platform.workbench.closeSearchModal();\n },\n menu: () => fileMenu(node, ui),\n };\n}\n\nfunction fileMenu(node, ui) {\n const pinned = (ui.app.offline?.state?.pins || []).some((p) => p.id === node.id);\n // Ask what the shortcut actually IS. Hardcoding \"⌘⇧L\" told a Windows or Linux user\n // about a key their machine does not have, and told everyone the default even after\n // they had rebound it.\n const kbd = (id) => ui.platform.keybindings.labelFor(id) || undefined;\n return [\n { label: 'Open', icon: 'file-text', run: () => ui.exec('explorer.open', node) },\n { label: 'Download', icon: 'download', run: () => ui.exec('explorer.download', node) },\n { label: 'Copy link', icon: 'link', kbd: kbd('explorer.copyLink'), run: () => ui.exec('explorer.copyLink') },\n { sep: true },\n { label: 'Rename…', run: () => ui.exec('explorer.rename') },\n pinned\n ? { label: 'Remove from offline', icon: 'close', run: () => ui.exec('offline.unpin', node) }\n : { label: 'Make available offline', icon: 'download', run: () => ui.exec('offline.pin', node) },\n { sep: true },\n { label: 'Move to trash', icon: 'trash', danger: true, kbd: kbd('explorer.delete'), run: () => ui.exec('explorer.delete') },\n ];\n}\n\n// The trash is the one place \"delete\" means destroy, so its two verbs live together:\n// put it back, or finish the job on this one item. Emptying the whole trash was\n// previously the only way to purge anything.\nfunction trashMenu(node, ui) {\n return [\n { label: 'Restore', icon: 'refresh', run: () => ui.exec('explorer.restore', node.id) },\n { sep: true },\n { label: 'Delete forever', icon: 'trash', danger: true, run: () => ui.exec('explorer.purgeOne', node.id) },\n ];\n}\n\nfunction fileIcon(node) {\n const t = node.contentType || '';\n if (t.startsWith('image/')) return 'file-image';\n if (t.startsWith('audio/')) return 'file-audio';\n if (t.startsWith('video/')) return 'file-video';\n return 'file-text';\n}\n\n// Substring-first, then subsequence fuzzy score (0 = no match).\nfunction score(hay, term) {\n if (!term) return 1;\n const i = hay.indexOf(term);\n if (i >= 0) return 1000 - i;\n let ti = 0;\n for (let hi = 0; hi < hay.length && ti < term.length; hi++) if (hay[hi] === term[ti]) ti++;\n return ti === term.length ? 1 : 0;\n}\n",
|
|
85
|
+
"import { dd } from '../../runtime.js';\nimport { prettyKey, eventToKey } from '../../platform/keybindings.js';\nimport { icon } from '../icon.js';\nimport { listAssociations, rememberOpener } from '../../bl/openers.js';\n\nconst { div, h2, h3, p, span, select, option, input, label, button } = dd;\n\nexport default function settingsView(state, ui) {\n const groups = ui.platform.settings.grouped();\n return div({ className: 'editor' },\n div({ className: 'stage' },\n div({ className: 'settings' },\n h2('Settings'),\n p({ className: 'sub' }, 'Preferences are stored in this browser. Plugins contribute their own settings here too.'),\n ...groups.map((g) => group(g, ui)),\n mcpSection(ui),\n openersSection(ui),\n keybindingsSection(ui),\n ),\n ),\n );\n}\n\nfunction group(g, ui) {\n return div({ className: 'group' },\n h3(g.category),\n ...g.items.map((s) => setting(s, ui)),\n );\n}\n\nfunction setting(s, ui) {\n return div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, s.title || s.key),\n s.description ? div({ className: 'd' }, s.description) : null,\n ),\n div({ className: 'control' }, control(s, ui)),\n );\n}\n\nfunction control(s, ui) {\n const set = (v) => ui.platform.settings.set(s.key, v);\n if (s.type === 'boolean') {\n return label({ className: 'switch' },\n input({ type: 'checkbox', checked: !!s.value }).on({ change: (e) => set(e.target.checked) }),\n span({ className: 'track' }),\n );\n }\n if (s.type === 'enum') {\n return select({ className: 'input' },\n ...s.enum.map((opt, i) =>\n option({ value: opt, selected: s.value === opt }, (s.enumLabels && s.enumLabels[i]) || opt),\n ),\n ).on({ change: (e) => set(e.target.value) });\n }\n if (s.type === 'number') {\n // Guard against a cleared field (Number('') === 0) or a paste yielding NaN, and\n // clamp to the declared range so downstream consumers never see an invalid value.\n const commit = (e) => {\n let n = Number(e.target.value);\n if (e.target.value === '' || Number.isNaN(n)) { e.target.value = s.value; return; }\n if (s.minimum != null) n = Math.max(s.minimum, n);\n if (s.maximum != null) n = Math.min(s.maximum, n);\n e.target.value = n;\n set(n);\n };\n return input({\n className: 'input', type: 'number', value: s.value,\n $attrs: { min: s.minimum ?? '', max: s.maximum ?? '' },\n }).on({ change: commit });\n }\n return input({ className: 'input', value: s.value ?? '' }).on({ change: (e) => set(e.target.value) });\n}\n// Connecting an AI agent to this drive.\n//\n// Read-only, and deliberately so. Everything here is DEPLOYMENT configuration — which\n// identity provider issues the tokens, where the endpoint lives — supplied by the\n// environment or by whoever constructed the server. Pointing the drive at a different\n// authorization server changes who can reach every file in it, which is a `docker run`\n// decision rather than a preference, and a settings field would make it look otherwise.\n//\n// What this section is FOR is the two questions a self-hoster has and currently cannot\n// answer: what URL do I paste into my assistant, and why does it say 401. The second is\n// why `needsAuthorizationServer` is called out as a problem — a drive with auth on and\n// no authorization server configured looks perfectly set up and cannot work.\n//\n// It reads straight off `capabilities`, which the app already fetched at startup, so\n// there is no loading state and nothing to go stale.\nfunction mcpSection(ui) {\n const caps = ui.platform.capabilities;\n if (!caps) return null; // capabilities never arrived; the shell has bigger problems\n const mcp = caps.mcp;\n const auth = caps.auth || { authorizationServers: [], source: 'none' };\n if (!mcp?.enabled) {\n return div({ className: 'group' }, h3('AI agents (MCP)'),\n p({ className: 'sub' }, 'MCP is switched off on this server. Set TROVE_MCP=on to enable it.'));\n }\n\n const copy = (text) => () => {\n navigator.clipboard?.writeText(text)\n .then(() => ui.platform.notifications.success('Copied'))\n // Clipboard access is denied often enough that failing silently would look like a\n // broken button; showing the value still gets it into the user's hands.\n .catch(() => ui.platform.notifications.info(text, { sticky: true }));\n };\n\n const servers = auth.authorizationServers || [];\n // Where the value came from matters when it is wrong: \"we took this from your JWT\n // issuer\" points at a different file than \"you set this\".\n const provenance = auth.source === 'jwt-issuer' ? 'from TROVE_JWT_ISSUER'\n : auth.source === 'configured' ? 'from TROVE_AUTH_SERVER' : null;\n\n return div({ className: 'group' },\n h3('AI agents (MCP)'),\n p({ className: 'sub' }, 'Point an AI assistant at this drive. It signs in as you and sees exactly the collections you can see.'),\n\n div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, 'Server URL'),\n div({ className: 'd' }, 'Paste this into your assistant\\u2019s MCP settings.'),\n ),\n div({ className: 'control mcp-url' },\n span({ className: 'mono' }, mcp.endpoint),\n button({ className: 'btn small', title: 'Copy' }, icon('link', { size: 13 })).on({ click: copy(mcp.endpoint) }),\n ),\n ),\n\n div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, 'Sign-in required'),\n div({ className: 'd' }, mcp.requiresAuth\n ? 'Agents must present a token, the same one this browser uses.'\n : 'This drive is open, so agents connect without a token \\u2014 exactly like the web app does.'),\n ),\n div({ className: 'control' }, span({ className: 'mono' }, mcp.requiresAuth ? 'Yes' : 'No')),\n ),\n\n // The failure that looks like success. Before the value, because it is the answer\n // to \"I set this up and my agent just says 401\".\n mcp.needsAuthorizationServer\n ? div({ className: 'mcp-warn' }, icon('warn', { size: 15 }),\n span('This drive requires a token but no authorization server is set, so an agent has '\n + 'nowhere to sign in. Set TROVE_AUTH_SERVER (or TROVE_JWT_ISSUER) to the issuer URL '\n + 'of your identity provider and restart.'))\n : null,\n\n div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, 'Authorization server'),\n div({ className: 'd' }, 'Where clients are sent to sign in \\u2014 for the whole drive, not just for agents. '\n + 'Set with TROVE_AUTH_SERVER; defaults to TROVE_JWT_ISSUER when that is set.'),\n ),\n div({ className: 'control' },\n servers.length\n ? span({ className: 'mono' }, servers.join(', ') + (provenance ? ` (${provenance})` : ''))\n : span({ className: 'mono muted' }, 'not set'),\n ),\n ),\n\n div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, 'Discovery document'),\n div({ className: 'd' }, 'What a client reads to find out how to authenticate (RFC 9728).'),\n ),\n div({ className: 'control mcp-url' }, span({ className: 'mono small' }, auth.metadataUrl)),\n ),\n );\n}\n\n// Default openers per file type — the \"always use this\" choices from the opener\n// chooser. Each row shows the type → viewer; the × forgets it (so the next open of\n// that type asks again). Empty when the user hasn't set any defaults.\nfunction openersSection(ui) {\n const rows = listAssociations(ui.platform);\n return div({ className: 'group' },\n h3('Default Openers'),\n rows.length\n ? div({},\n ...rows.map((r) =>\n div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, r.typeKey),\n div({ className: 'd' }, r.missing ? `${r.openerTitle} (no longer installed)` : `Opens with ${r.openerTitle}`),\n ),\n div({ className: 'control' },\n button({ className: 'iconbtn', title: 'Forget this default' }, icon('close', { size: 14 }))\n .on({ click: () => rememberOpener(ui.platform, r.typeKey, null) }),\n ),\n ),\n ),\n )\n : p({ className: 'sub', $styling: { margin: 0 } }, 'No default openers set yet. When a file type has more than one viewer, you can pick one and check “Always use this”.'),\n );\n}\n\n// Which shortcut is currently listening for its new chord. Module-level because the\n// settings view re-renders from scratch and this is transient UI state, not a setting.\nlet capturing = null;\n\nfunction keybindingsSection(ui) {\n const kb = ui.platform.keybindings;\n const bindings = kb.resolved();\n const cmds = ui.platform.contributions;\n const overrides = kb.overrides();\n const stop = () => { capturing = null; ui.rerender?.(); };\n // Which chords more than one command answers to. Nothing rejects a collision, and\n // `#matchFor` scans in reverse so the LAST registration wins — bind Delete onto\n // ⌘P and Quick Open stops opening, with no hint anywhere that that is why.\n const byKey = new Map();\n for (const b of bindings) byKey.set(b.key, (byKey.get(b.key) || 0) + 1);\n return div({ className: 'group' },\n h3('Keyboard Shortcuts'),\n p({ className: 'sub' }, 'Click a shortcut to record a new one. Esc cancels; Backspace clears it.'),\n // Every binding, not the first 40. The cap was arbitrary and silent — with no \"N\n // more\" anywhere, the shortcuts past it simply did not exist as far as anyone could\n // tell, and one plugin keymap was enough to push real ones off the end.\n ...bindings.map((b) => {\n const cmd = cmds.get(b.command);\n const listening = capturing === b.bindingId;\n const custom = !!overrides[b.bindingId];\n const clash = byKey.get(b.key) > 1;\n return div({ className: 'setting' },\n div({ className: 'info' },\n div({ className: 't' }, cmd?.title || b.command),\n div({ className: 'd' }, b.command),\n ),\n div({ className: 'control' },\n clash && !listening\n ? span({ className: 'kbd-clash', title: 'Another command answers to this shortcut too — the one registered last wins' },\n icon('warn', { size: 12 }))\n : null,\n button({ className: `kbd-edit ${listening ? 'listening' : ''} ${clash ? 'clash' : ''}`, title: listening ? 'Press the new shortcut' : 'Click to rebind' },\n listening ? span('Press keys…') : dd.h('kbd', prettyKey(b.key)))\n .on({\n click: () => { capturing = listening ? null : b.bindingId; ui.rerender?.(); },\n blur: () => { if (listening) stop(); },\n keydown: (e) => {\n if (!listening) return;\n e.preventDefault();\n e.stopPropagation();\n if (e.key === 'Escape') return stop();\n if (e.key === 'Backspace') { kb.rebind(b, null); return stop(); }\n // A bare modifier isn't a chord yet — wait for the key it modifies.\n if (['Control', 'Meta', 'Alt', 'Shift'].includes(e.key)) return;\n kb.rebind(b, eventToKey(e));\n stop();\n },\n $attach: (el) => { if (listening) queueMicrotask(() => el.focus()); },\n }),\n custom\n ? button({ className: 'c-link', title: 'Back to the default' }, 'reset')\n .on({ click: () => { kb.rebind(b, null); ui.rerender?.(); } })\n : null,\n ),\n );\n }),\n );\n}\n",
|
|
86
|
+
"import { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\n\nconst { div, span, p, button, h2, input, label } = dd;\n\nexport default function pluginsView(state, ui) {\n const plugins = state.plugins || [];\n return div({ className: 'editor' },\n div({ className: 'stage' },\n div({ className: 'plugins' },\n h2('Plugins'),\n p({ className: 'sub', $styling: { color: 'var(--text-dim)', margin: '0 0 8px' } },\n 'Plugins are sandboxed packages you install from a file or URL. They run in an isolated iframe and reach Trove only through a message channel, using the capabilities you granted.'),\n div({ className: 'plugin-install' },\n button({ className: 'btn primary' }, icon('upload', { size: 15 }), 'Install from file…')\n .on({ click: () => ui.exec('plugins.installFromFile') }),\n button({ className: 'btn' }, icon('plug', { size: 15 }), 'Install from URL…')\n .on({ click: () => ui.exec('plugins.installFromUrl') }),\n ),\n plugins.length\n ? div({ $styling: { display: 'flex', 'flex-direction': 'column', gap: '12px', 'margin-top': '14px' } }, ...plugins.map((pl) => installedCard(pl, state, ui)))\n : div({ className: 'empty', $styling: { padding: '48px' } }, icon('plug', { size: 30 }), span('No plugins installed yet.')),\n ),\n ),\n );\n}\n\nfunction installedCard(pl, state, ui) {\n const connectivity = !pl.responsive ? 'not responding' : pl.manifest?.online === false ? 'offline' : 'connected';\n const features = pl.features || [];\n return div({ className: 'plugin-card' },\n div({ className: 'top' },\n div({ className: 'avatar' }, (pl.name || '?')[0].toUpperCase()),\n div({ $styling: { flex: '1' } },\n div({ className: 'name' }, pl.name, pl.version ? span({ $styling: { color: 'var(--text-faint)', 'font-weight': '400', 'margin-left': '6px' } }, 'v' + pl.version) : null),\n div({ $styling: { 'font-size': '11px', color: 'var(--text-faint)' } }, pl.id),\n ),\n trustBadge(pl.trust),\n span({ className: `status ${pl.status}` }, pl.status),\n ),\n pl.error ? div({ className: 'desc', $styling: { color: 'var(--danger)' } }, pl.error) : null,\n div({ className: 'caps' }, ...(pl.capabilities || []).map((c) => span({ className: 'cap' }, c))),\n (pl.endpoints || []).length\n ? div({ className: 'plugin-endpoints', $styling: { 'font-size': '11px', color: 'var(--text-faint)', 'margin-top': '2px' } },\n icon('plug', { size: 11 }), ' Network: ', (pl.endpoints || []).map((e) => e.host).join(', '))\n : null,\n pl.status === 'active'\n ? div({ className: 'plugin-features' },\n div({ className: 'pf-head' }, span(`Features · ${connectivity}`), span({ className: 'muted' }, `${features.filter((f) => f.available).length}/${features.length} available`)),\n features.length\n ? div({ className: 'pf-list' }, ...features.map(featureRow))\n : div({ className: 'muted', $styling: { 'font-size': '12px' } }, pl.responsive ? 'No contributions announced.' : 'No manifest received — the plugin may not be running.'),\n )\n : null,\n (pl.settingsSchema || []).length ? settingsSection(pl, ui) : null,\n div({ className: 'actions' },\n pl.hasUi ? button({ className: 'btn' }, icon('command', { size: 14 }), 'Open panel').on({ click: () => ui.platform.workbench.openPluginPanel(pl.id) }) : null,\n button({ className: 'btn' }, icon('refresh', { size: 14 }), 'Refresh').on({ click: () => ui.platform.plugins.refresh(pl.id) }),\n button({ className: 'btn danger' }, 'Uninstall').on({\n click: () => ui.platform.workbench.showDialog({\n kind: 'confirm', title: `Uninstall ${pl.name}?`, danger: true, confirmLabel: 'Uninstall',\n body: 'The plugin and all data it stored will be removed.',\n onConfirm: () => { ui.platform.workbench.closeDialog(); ui.uninstallPlugin(pl.id); },\n }),\n }),\n ),\n );\n}\n\nfunction settingsSection(pl, ui) {\n return div({ className: 'plugin-features' },\n div({ className: 'pf-head' }, span('Settings')),\n div({ $styling: { display: 'flex', 'flex-direction': 'column', gap: '8px' } },\n ...pl.settingsSchema.map((s) => settingRow(pl, s, ui)),\n ),\n );\n}\n\nfunction settingRow(pl, s, ui) {\n const key = `${pl.id}.${s.key}`;\n const value = s.secret ? '' : (ui.platform.settings.get(key) ?? s.default ?? '');\n return div({ className: 'setting', $styling: { padding: '6px 0' } },\n div({ className: 'info' }, div({ className: 't' }, s.title || s.key), s.description ? div({ className: 'd' }, s.description) : null),\n div({ className: 'control' },\n s.secret\n ? input({ className: 'input', type: 'password', placeholder: 'Set secret…' }).on({\n change: (e) => { if (e.target.value) { ui.platform.plugins.setSecret(pl.id, s.key, e.target.value); e.target.value = ''; e.target.placeholder = 'Saved ✓'; } },\n })\n : s.type === 'boolean'\n ? label({ className: 'switch' }, input({ type: 'checkbox', checked: !!value }).on({ change: (e) => ui.platform.settings.set(key, e.target.checked) }), span({ className: 'track' }))\n : input({ className: 'input', value }).on({ change: (e) => ui.platform.settings.set(key, e.target.value) }),\n ),\n );\n}\n\nfunction trustBadge(t) {\n if (!t) return null;\n if (t.status === 'verified') return span({ className: 'trust verified', title: 'Signed by ' + t.domain }, icon('check', { size: 12 }), t.domain);\n if (t.status === 'signed') return span({ className: 'trust signed' }, icon('info', { size: 12 }), 'signed');\n // A BROKEN signature is not the same as no signature. The install dialog says so in\n // red; this list used to fall through to the same amber \"unverified\" an ordinary\n // unsigned plugin gets, so a package whose signature failed to verify — the one case\n // that means someone tampered with it — looked like the common, benign one.\n if (t.status === 'invalid') {\n return span({ className: 'trust invalid', title: t.reason || 'The signature did not verify — this package may have been altered' },\n icon('warn', { size: 12 }), 'invalid signature');\n }\n return span({ className: 'trust unverified' }, icon('warn', { size: 12 }), 'unverified');\n}\n\nconst KIND_ICON = { command: 'command', opener: 'file', indexer: 'search', statusItem: 'info' };\nfunction featureRow(f) {\n return div({ className: `pf-row ${f.available ? '' : 'off'}` },\n icon(KIND_ICON[f.kind] || 'command', { size: 13 }),\n span({ className: 'pf-title' }, f.title),\n span({ className: 'pf-kind' }, f.kind),\n f.offline ? span({ className: 'pf-badge offline-ok', title: 'Works offline' }, 'offline ✓') : null,\n span({ className: `pf-dot ${f.available ? 'on' : 'no'}`, title: f.available ? 'Available now' : 'Unavailable now' }),\n );\n}\n",
|
|
87
|
+
"// Following a `trove:` link from a document.\n//\n// Resolution is deliberately lazy — at click time, not at render time. A document can\n// hold dozens of links; resolving them all on every render would be a request per link,\n// and the answer can change between renders anyway (the target may be created, renamed,\n// or deleted while the document is open). So a link is rendered from its text alone, and\n// only what the user actually follows costs a round trip.\n//\n// A broken link is a normal, expected state — names are the legible way to write a link,\n// and renaming breaks them. It has to say so clearly, and say WHY, because \"nothing\n// happened\" is the worst possible response to a click.\n\nimport { parseTroveUri } from '@3sln/trove/core/links.js';\nimport { OpenFileAction } from './actions.js';\n\n/**\n * Open what a `trove:` URI points at.\n * @param {object} ui the workbench ui helper\n * @param {string} uri the link as written in the document\n * @param {{from?: object}} [opts] the item the link was followed FROM, for messages\n */\nexport async function openTroveLink(ui, uri, { from } = {}) {\n const ref = parseTroveUri(uri);\n const notify = ui.platform.notifications;\n if (!ref) {\n notify.warn(`\"${uri}\" isn’t a valid Trove link.`);\n return null;\n }\n try {\n const res = await ui.platform.api.stat(uri);\n if (!res?.node) throw new Error('not found');\n ui.go(new OpenFileAction(res.node));\n return res.node;\n } catch (err) {\n notify.warn(describeBrokenLink(ref, err, from));\n return null;\n }\n}\n\n/**\n * Why a link didn't open, in terms of the link the user is looking at. A 403 is a\n * different situation from a 404 and must not be reported as one: \"no such item\" when\n * the item exists but isn't yours is both wrong and a small information leak in reverse.\n */\nfunction describeBrokenLink(ref, err, from) {\n const where = from?.name ? ` (linked from \"${from.name}\")` : '';\n if (err?.status === 403 || err?.code === 'forbidden') {\n return `You don’t have access to that item in \"${ref.collection}\"${where}.`;\n }\n if (ref.by === 'name') {\n return `Nothing named \"${ref.value}\" in \"${ref.collection}\"${where} — it may have been renamed or deleted.`;\n }\n return `That item no longer exists in \"${ref.collection}\"${where}.`;\n}\n",
|
|
88
|
+
"// The markdown opener. Markdown is load-bearing now: with no folder hierarchy, a\n// document that links its sources IS the grouping, so these files have to render as\n// documents with working links rather than as a wall of `<pre>`.\n//\n// A `trove:` link navigates in-app (see ../../../bl/links.js for resolution); an http(s)\n// link opens in a new tab with `rel=\"noopener noreferrer\"`; anything else — `javascript:`,\n// `data:` — is rendered as inert text, because a document is untrusted content that any\n// user of the drive can write.\n//\n// The parser is deliberately small: headings, lists, block quotes, fenced code, inline\n// code/emphasis/links, and paragraphs. It builds VDOM nodes directly and never produces\n// an HTML string, so there is no innerHTML to get wrong.\n//\n// It is also BOUNDED. The input is a file someone uploaded, so its size and shape are\n// not ours to choose: a document with tens of thousands of emphasis spans used to\n// recurse once per span and overflow the stack mid-render, which left the viewer on a\n// spinner forever with nothing said. Inline scanning is now iterative, and both the\n// document length and the node count are capped — a truncated document with a visible\n// notice beats a hung one.\n\nimport { dd, fromAsync } from '../../../runtime.js';\nimport { icon } from '../../icon.js';\nimport { parseTroveUri } from '@3sln/trove/core/links.js';\nimport { openTroveLink } from '../../../bl/links.js';\n\nconst { div, span, h1, h2, h3, p, ul, ol, li, pre, code, a, em, strong, blockquote, hr, br } = dd;\n\nexport function markdownOpener(node, ui) {\n // Bounded at the TRANSFER, not just the render: MAX_CHARS below stops us laying out a\n // huge document, but without this the whole file is still pulled into the tab first.\n const src = fromAsync(() => ui.platform.api.readTextCapped(node.id, { maxBytes: MAX_CHARS, size: node.size }).then((r) => r.text));\n return dd.alias(() =>\n ui.platform.reactive.watch(\n src,\n // A throw in here is NOT covered by the `error:` handler below — that one is for\n // the observable failing, not for the mapper. Rendering untrusted content is\n // exactly where a throw is plausible, and an uncaught one leaves the placeholder\n // on screen forever, so it is caught where it happens.\n (text) => {\n try {\n return div({ className: 'viewer markdown' }, div({ className: 'md' }, ...renderMarkdown(text, node, ui)));\n } catch (err) {\n console.error('markdown render failed', err);\n return errorView(`This document couldn’t be displayed: ${err.message}`);\n }\n },\n {\n placeholder: () => div({ className: 'viewer' }, div({ className: 'loading' }, div({ className: 'spinner' }), span('Loading…'))),\n error: (e) => errorView(e.message),\n },\n ),\n )();\n}\n\nfunction errorView(message) {\n return div({ className: 'viewer' }, div({ className: 'fallback' }, icon('warn', { size: 40 }), span(message)));\n}\n\n// Bounds. A document past these renders as much as fits, with a notice — the parse is\n// linear, so these are about the size of the resulting DOM, not about parse time.\nconst MAX_CHARS = 512 * 1024;\nconst MAX_BLOCKS = 5_000;\nconst MAX_INLINE_NODES = 2_000; // per block\n\n// --- block level -------------------------------------------------------------\n\nexport function renderMarkdown(text, node, ui) {\n const raw = String(text ?? '');\n const truncated = raw.length > MAX_CHARS;\n const lines = (truncated ? raw.slice(0, MAX_CHARS) : raw).split(/\\r?\\n/);\n const out = [];\n let i = 0;\n\n while (i < lines.length) {\n if (out.length >= MAX_BLOCKS) return [...out, tooLarge()];\n const line = lines[i];\n\n // Fenced code — taken verbatim, including any markdown inside it.\n const fence = /^\\s*(```+|~~~+)(.*)$/.exec(line);\n if (fence) {\n const marker = fence[1][0].repeat(3);\n const body = [];\n i++;\n while (i < lines.length && !lines[i].trimStart().startsWith(marker)) body.push(lines[i++]);\n i++; // closing fence (or EOF — an unterminated fence still renders)\n out.push(pre({ className: 'md-code' }, code(body.join('\\n'))));\n continue;\n }\n\n if (!line.trim()) { i++; continue; }\n\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line);\n if (heading) {\n const level = heading[1].length;\n const factory = level === 1 ? h1 : level === 2 ? h2 : h3;\n out.push(factory({ className: `md-h md-h${level}` }, ...inline(heading[2], node, ui)));\n i++;\n continue;\n }\n\n if (/^\\s*(-{3,}|\\*{3,}|_{3,})\\s*$/.test(line)) { out.push(hr({ className: 'md-hr' })); i++; continue; }\n\n if (/^\\s*>/.test(line)) {\n const body = [];\n while (i < lines.length && /^\\s*>/.test(lines[i])) body.push(lines[i++].replace(/^\\s*>\\s?/, ''));\n out.push(blockquote({ className: 'md-quote' }, ...renderMarkdown(body.join('\\n'), node, ui)));\n continue;\n }\n\n const bullet = /^\\s*([-*+]|\\d+[.)])\\s+/.exec(line);\n if (bullet) {\n const ordered = /\\d/.test(bullet[1]);\n const items = [];\n while (i < lines.length) {\n const m = /^\\s*([-*+]|\\d+[.)])\\s+(.*)$/.exec(lines[i]);\n if (!m || /\\d/.test(m[1]) !== ordered) break;\n items.push(li({ className: 'md-li' }, ...inline(m[2], node, ui)));\n i++;\n }\n out.push((ordered ? ol : ul)({ className: 'md-list' }, ...items));\n continue;\n }\n\n // Paragraph: consecutive non-blank lines that don't start another block.\n const para = [];\n while (i < lines.length && lines[i].trim() && !startsBlock(lines[i])) para.push(lines[i++]);\n const joined = para.join('\\n');\n const kids = [];\n joined.split('\\n').forEach((l, n) => {\n if (n) kids.push(br());\n kids.push(...inline(l, node, ui));\n });\n out.push(p({ className: 'md-p' }, ...kids));\n }\n return truncated ? [...out, tooLarge()] : out;\n}\n\n// Say what happened, rather than silently rendering a prefix as if it were the whole\n// document — a reader who can't tell it was cut off will believe the rest isn't there.\nfunction tooLarge() {\n return p({ className: 'md-truncated' },\n 'This document is too large to display in full. Download it to read the rest.');\n}\n\nfunction startsBlock(line) {\n return /^(#{1,6}\\s|\\s*>|\\s*([-*+]|\\d+[.)])\\s|\\s*(```|~~~))/.test(line)\n || /^\\s*(-{3,}|\\*{3,}|_{3,})\\s*$/.test(line);\n}\n\n// --- inline level ------------------------------------------------------------\n\n// Ordered by precedence: code spans win over emphasis (so `*` inside backticks is\n// literal), and explicit links win over bare autolinks.\nconst INLINE = [\n { re: /`([^`]+)`/, node: (m) => code({ className: 'md-inline-code' }, m[1]) },\n { re: /\\[([^\\]]*)\\]\\(([^)\\s]+)(?:\\s+\"[^\"]*\")?\\)/, node: (m, node, ui) => link(m[2], m[1] || m[2], node, ui) },\n { re: /(\\*\\*|__)(.+?)\\1/, node: (m, node, ui, d) => strong(...inline(m[2], node, ui, d + 1)) },\n { re: /(\\*|_)(.+?)\\1/, node: (m, node, ui, d) => em(...inline(m[2], node, ui, d + 1)) },\n // Bare URLs, so a pasted trove: reference is clickable without link syntax.\n { re: /(trove:[^\\s<>\"'`)\\]}]+|https?:\\/\\/[^\\s<>\"'`)\\]}]+)/, node: (m, node, ui) => link(trimTrailing(m[1]), trimTrailing(m[1]), node, ui) },\n];\n\n/**\n * Scan one line into inline nodes. Iterative on purpose: the obvious recursive form\n * (split on the first match, recurse on both sides) descends once per span, so a line\n * with thousands of them overflows the stack — and the input is a file someone\n * uploaded, so \"thousands of them\" is their choice, not ours.\n *\n * Emphasis still nests, via a bounded recursion on the span's *contents* only. That\n * depth is limited by how deeply markers are nested rather than by how many spans a\n * line contains, so it stays shallow for anything a person would write, and `depth`\n * stops a pathological document regardless.\n */\nfunction inline(text, node, ui, depth = 0) {\n if (!text) return [];\n if (depth > 12) return [text]; // deeper than any real markup; render it literally\n const out = [];\n let rest = text;\n while (rest) {\n if (out.length >= MAX_INLINE_NODES) { out.push(rest); break; }\n let best = null;\n for (const rule of INLINE) {\n const m = rule.re.exec(rest);\n if (m && (best === null || m.index < best.m.index)) best = { m, rule };\n }\n if (!best) { out.push(rest); break; }\n const { m, rule } = best;\n if (m.index) out.push(rest.slice(0, m.index));\n out.push(rule.node(m, node, ui, depth));\n rest = rest.slice(m.index + m[0].length);\n }\n return out;\n}\n\nfunction trimTrailing(url) {\n return url.replace(/[.,;:!?]+$/, '');\n}\n\n/**\n * One link. A `trove:` reference navigates inside the app and is resolved lazily on\n * click — resolving every link up front would mean a request per link on every render,\n * and a link's target can appear or disappear between renders anyway.\n *\n * Only trove: and http(s) are rendered as links at all. A `javascript:` or `data:` URL\n * is shown as plain text: markdown here is untrusted content written by whoever can\n * write to the drive, and an allowlist is the only way to be sure about a scheme.\n */\nfunction link(href, label, node, ui) {\n const trove = parseTroveUri(href);\n if (trove) {\n return a({ className: 'md-link md-trove', href: '#', title: href }, label).on({\n click: (e) => {\n e.preventDefault();\n openTroveLink(ui, href, { from: node });\n },\n });\n }\n if (/^https?:\\/\\//i.test(href)) {\n return a({ className: 'md-link', href, target: '_blank', rel: 'noopener noreferrer', title: href }, label);\n }\n return span({ className: 'md-deadlink', title: `Unsupported link: ${href}` }, label);\n}\n",
|
|
89
|
+
"// Built-in openers, registered through the same contribution system plugins use.\n// Each declares a selector (extensions / mime) and a component(node, ui) → vnode.\n// The editor area picks the highest-priority opener whose selector matches.\n\nimport { dd, cell, fromAsync, watch } from '../../../runtime.js';\nimport { icon } from '../../icon.js';\nimport { bytes } from '../../format.js';\nimport { markdownOpener } from './markdown.js';\nimport { attachMedia } from '../../media.js';\n\nconst { div, pre, img, span, button, video, audio } = dd;\n\n// Built-ins register the same way a plugin's manifest declares an opener — same\n// registry, same `match` selector, same priority ordering. The only difference is\n// that a built-in supplies a `component` (it runs in the host) where a plugin\n// supplies an `entry` module (it runs in its own sandboxed frame).\nconst BUILT_IN = {\n 'core.audio': {\n title: 'Audio Player', priority: 20,\n match: { mime: ['audio/*'], ext: ['.mp3', '.flac', '.wav', '.opus', '.ogg', '.m4a', '.m4b'] },\n component: audioOpener,\n },\n 'core.video': {\n title: 'Video Player', priority: 20,\n match: { mime: ['video/*'], ext: ['.mp4', '.webm', '.mkv', '.mov'] },\n component: videoOpener,\n },\n 'core.image': {\n title: 'Image Viewer', priority: 20,\n match: { mime: ['image/*'], ext: ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.avif'] },\n component: imageOpener,\n },\n // Markdown outranks the plain text viewer: a document that links its sources is how\n // things are grouped now, so those links have to be clickable by default.\n 'core.markdown': {\n title: 'Markdown', priority: 30,\n match: { ext: ['.md', '.markdown'], mime: ['text/markdown'] },\n component: markdownOpener,\n },\n 'core.text': {\n title: 'Text Viewer', priority: 10,\n match: {\n mime: ['text/*', 'application/json'],\n ext: ['.txt', '.md', '.json', '.js', '.mjs', '.ts', '.jsx', '.tsx', '.css', '.html', '.xml', '.yaml', '.yml', '.toml', '.ini', '.log', '.csv', '.py', '.rb', '.go', '.rs', '.sh', '.c', '.h', '.cpp', '.java'],\n },\n component: textOpener,\n },\n};\n\nexport function registerBuiltinOpeners(platform) {\n for (const [name, spec] of Object.entries(BUILT_IN)) {\n platform.contributions.register(name, { type: 'opener', ...spec });\n }\n}\n\n/** Render a node with a resolved opener; falls back gracefully. */\nexport function renderOpener(node, openerId, ui) {\n const opener = ui.platform.contributions.get(openerId);\n if (opener?.component) {\n try {\n return opener.component(node, ui);\n } catch (err) {\n return errorView(err.message);\n }\n }\n if (opener?.pluginId) return pluginOpenerView(opener, node, ui);\n return fallbackOpener(node, ui);\n}\n\n// ---- component openers -----------------------------------------------------\n\n// How much of a text file the viewer will pull and draw. A drive holds logs, CSV\n// exports and database dumps in the hundreds of megabytes; reading one whole just to\n// show its beginning buffers the entire file in the tab and then asks the browser to\n// lay out ten million characters. Both halves are bounded here — the Range request\n// means the bytes never arrive, not merely that they aren't drawn.\nconst TEXT_VIEW_BYTES = 512 * 1024;\n\nfunction textOpener(node, ui) {\n const src = fromAsync(() => ui.platform.api.readTextCapped(node.id, { maxBytes: TEXT_VIEW_BYTES, size: node.size }));\n return dd.alias(() =>\n ui.platform.reactive.watch(\n src,\n ({ text, truncated, total }) => div({ className: 'viewer text' },\n pre(text),\n // Say what is missing rather than trailing off. A viewer that silently shows\n // the first slice of a file is worse than one that refuses: the reader believes\n // they have seen the end of it.\n truncated\n ? div({ className: 'md-truncated' },\n `Showing the first ${Math.round(text.length / 1024)} KB${total ? ` of ${(total / 1048576).toFixed(1)} MB` : ''}. `,\n span('Download the file to read all of it.'))\n : null,\n ),\n {\n placeholder: () => div({ className: 'viewer' }, div({ className: 'loading' }, div({ className: 'spinner' }), span('Loading…'))),\n error: (e) => errorView(e.message),\n },\n ),\n )();\n}\n\n// A media element (img/audio/video) sets its `src` to a download URL that can 404 or\n// hold undecodable bytes. Those failures surface only as an async `error` event on the\n// element itself (media error events don't bubble), which would otherwise leave a\n// broken glyph / black box with no message. `mediaWithError` passes an `onError` the\n// caller MUST wire onto the actual media element, and swaps in the download fallback\n// (with a reason) the moment it fires.\nconst MEDIA_ERR = \"This file couldn't be loaded — it may be missing or in an unsupported format.\";\nfunction mediaWithError(node, ui, makeEl) {\n const state = cell({ error: null });\n const onError = (msg) => state.setValue({ error: msg || MEDIA_ERR });\n const el = makeEl(onError);\n return dd.alias(() =>\n watch(state, (s) => (s.error ? fallbackOpener(node, ui, s.error) : el)),\n )();\n}\n\n/**\n * The `src` is not set here.\n *\n * It is minted — a URL that carries its own grant, because a media element fetches\n * without our Authorization header — and then KEPT current: it expires, and a paused\n * film resumed the next morning has to swap its source without losing its place. All of\n * that is `attachMedia`; see ui/media.js.\n */\nconst sourced = (el, node, ui, onError) => el.on({\n $attach: (dom) => { dom._detachMedia = attachMedia(dom, node, ui, { onError }); },\n $detach: (dom) => { dom._detachMedia?.(); dom._detachMedia = null; },\n}).opaque();\n\nfunction imageOpener(node, ui) {\n return mediaWithError(node, ui, (onError) =>\n div({ className: 'viewer image' }, sourced(img({ alt: node.name }), node, ui, onError)),\n );\n}\n\nfunction audioOpener(node, ui) {\n return mediaWithError(node, ui, (onError) =>\n div({ className: 'viewer', $styling: { display: 'grid', 'place-items': 'center', gap: '16px', padding: '40px' } },\n icon('file-audio', { size: 48 }),\n span({ $styling: { color: 'var(--text-dim)' } }, node.name),\n sourced(audio({ controls: true, $styling: { width: 'min(520px, 90%)' } }), node, ui, onError),\n ),\n );\n}\n\nfunction videoOpener(node, ui) {\n return mediaWithError(node, ui, (onError) =>\n div({ className: 'viewer', $styling: { display: 'grid', 'place-items': 'center', background: '#000' } },\n sourced(video({ controls: true, $styling: { 'max-width': '100%', 'max-height': '100%' } }), node, ui, onError),\n ),\n );\n}\n\nfunction fallbackOpener(node, ui, reason) {\n return div({ className: 'viewer' },\n div({ className: 'fallback' },\n icon(reason ? 'warn' : 'file', { size: 44 }),\n span({ $styling: { 'font-weight': 600 } }, node.name),\n span(`${node.contentType || 'Unknown type'} · ${bytes(node.size)}`),\n span({ $styling: { color: 'var(--text-faint)', 'max-width': '340px' } },\n reason || 'No preview available for this file type. Install a plugin that handles it, or download the file.'),\n button({ className: 'btn primary' }, icon('download', { size: 15 }), 'Download')\n .on({ click: () => ui.exec('explorer.download', node) }),\n ),\n );\n}\n\nfunction pluginOpenerView(opener, node, ui) {\n // A plugin opener renders in its OWN sandboxed iframe (spawned by mountViewer with\n // the plugin's capabilities). That iframe is not a child here — it's a host-owned\n // position:fixed overlay whose box tracks this .pv-host element (moving an <iframe>\n // in the DOM would reload it). $attach hands .pv-host to mountViewer as the tracking\n // target; $detach lets it dock or tear down. `.opaque()` keeps dodo from reconciling\n // these (host-managed) nodes so their lifecycle hooks stay stable across re-renders.\n // A .pv-status overlay shows a spinner until the plugin opens the file (the iframe\n // is transparent while loading) and an error if it never does.\n return div({ className: 'viewer plugin-viewer' },\n div({ className: 'pv-status' }).on({\n $attach: (el) => {\n el.innerHTML = '<div class=\"loading\"><div class=\"spinner\"></div><span>Opening…</span></div>';\n el._ready = () => { el.style.display = 'none'; };\n el._error = (msg) => { el.style.display = 'grid'; el.innerHTML = ''; el.appendChild(errorEl(msg)); };\n },\n }).opaque(),\n div({ className: 'pv-host' }).on({\n $attach: (el) => {\n const status = el.parentElement?.querySelector('.pv-status');\n el._detach = ui.platform.plugins.mountViewer(opener.pluginId, el, node, opener.id, {\n onReady: () => status?._ready?.(),\n onError: (msg) => status?._error?.(msg || 'This viewer failed to load'),\n });\n },\n $detach: (el) => { el._detach?.(); el._detach = null; },\n }).opaque(),\n );\n}\n\n// A plain DOM error node for the imperatively-managed .pv-status overlay.\nfunction errorEl(message) {\n const wrap = document.createElement('div');\n wrap.className = 'fallback';\n const s = document.createElement('span');\n s.textContent = message || 'Failed to open';\n wrap.appendChild(s);\n return wrap;\n}\n\nfunction errorView(message) {\n return div({ className: 'viewer' }, div({ className: 'fallback' }, icon('warn', { size: 40 }), span(message || 'Failed to open')));\n}\n",
|
|
90
|
+
"// The viewer — the top of the panel stack (a file's opener), full-width, with a\n// nav bar: a Back button, a trail of the stacked panels (Search › A › B),\n// and details/close actions. The opener host returns a *stable* keyed alias per\n// panel so dodo reuses its DOM across unrelated workbench re-renders — critical for\n// any media opener, whose <audio>/<video> must keep playing while state churns; the\n// DOM persists as long as the panel stays in the stack.\n\nimport { dd } from '../../runtime.js';\nimport { icon, iconForNode } from '../icon.js';\nimport { renderOpener } from './openers/index.js';\nimport { availableOpeners } from '../../bl/openers.js';\n\nconst { div, span, button } = dd;\n\nconst openerFns = new Map(); // `${panelId}:${openerId}` -> stable render fn\n\nexport default function editorArea(state, ui) {\n const wb = state.wb;\n const files = state.nav.stack.filter((p) => p.kind === 'file');\n pruneOpeners(files);\n const active = files[files.length - 1];\n if (!active) return div({ className: 'editor' });\n return div({ className: 'editor' },\n navBar(files, active, ui),\n div({ className: 'stage' }, openerHost(active, ui)),\n );\n}\n\nfunction navBar(files, active, ui) {\n const w = ui.platform.workbench;\n return div({ className: 'viewer-nav' },\n button({ className: 'vn-back', title: 'Back (Esc)' }, icon('chevron-left', { size: 16 }))\n .on({ click: () => w.back() }),\n div({ className: 'vn-trail' },\n button({ className: 'vn-crumb' }, icon('search', { size: 13 }), span('Search'))\n .on({ click: () => w.showHome() }),\n ...files.map((p) =>\n button({ className: `vn-crumb ${p.id === active.id ? 'active' : ''}`, title: p.node.name },\n icon(iconForNode(p.node), { size: 13 }), span({ className: 'label' }, p.node.name))\n .on({ click: () => w.openFile(p.node, p.openerId) })),\n ),\n div({ className: 'vn-actions' },\n openerSwitch(active, ui),\n button({ className: 'iconbtn', title: 'Details & comments' }, icon('info', { size: 15 }))\n .on({ click: () => w.toggleInfoPanel() }),\n button({ className: 'iconbtn', title: 'Close (Esc)' }, icon('close', { size: 15 }))\n .on({ click: () => w.showHome() }),\n ),\n );\n}\n\n// \"Open with…\" — shown only when more than one opener can handle this file. Opens\n// the chooser (pre-selected to the current opener) so the user can switch viewers,\n// and optionally make the choice their default for this file type.\nfunction openerSwitch(active, ui) {\n const openers = availableOpeners(ui.platform, active.node);\n if (openers.length <= 1) return null;\n return button({ className: 'iconbtn', title: 'Open with…' }, icon('dots', { size: 15 }))\n .on({ click: () => ui.platform.workbench.showDialog({ kind: 'opener-chooser', node: active.node, openers, current: active.openerId }) });\n}\n\nfunction openerHost(panel, ui) {\n const key = `${panel.id}:${panel.openerId}`;\n let fn = openerFns.get(key);\n if (!fn) {\n fn = () => renderOpener(panel.node, panel.openerId, ui);\n openerFns.set(key, fn);\n }\n return dd.alias(fn)().key(panel.id);\n}\n\nfunction pruneOpeners(files) {\n const live = new Set(files.map((p) => `${p.id}:${p.openerId}`));\n for (const k of openerFns.keys()) if (!live.has(k)) openerFns.delete(k);\n}\n",
|
|
91
|
+
"// Command palette + quick-open. Mode 'commands' fuzzy-filters registered\n// commands (respecting when-clauses); mode 'files' searches the drive by name\n// and opens the chosen file. Arrow keys move, Enter runs, Esc closes.\n\nimport { dd } from '../../runtime.js';\nimport { icon, iconForNode } from '../icon.js';\nimport { prettyKey } from '../../platform/keybindings.js';\nimport { OpenFileAction, QuickOpenAction } from '../../bl/actions.js';\n\nconst { div, input, span } = dd;\n\nexport default function commandPalette(state, ui) {\n const pal = state.overlay.palette;\n if (!pal) return null;\n const wb = ui.platform.workbench;\n // With no query there is nothing to show — and showing the PREVIOUS session's hits\n // would be worse than nothing, since they don't match what the field now says.\n const items = pal.mode === 'files'\n ? (pal.query.trim() ? state.se.paletteFiles || [] : [])\n : filterCommands(state, ui, pal.query);\n const index = Math.min(pal.index, Math.max(0, items.length - 1));\n\n const run = (item) => {\n if (!item) return;\n wb.closePalette();\n if (pal.mode === 'files') ui.go(new OpenFileAction(item.node));\n else ui.exec(item.id);\n };\n\n return div({},\n div({ className: 'scrim' }).on({ click: () => wb.closePalette() }),\n div({ className: 'palette' },\n div({ className: 'search' },\n icon(pal.mode === 'files' ? 'search' : 'command', { size: 18 }),\n input({\n type: 'text', value: pal.query, autofocus: true,\n placeholder: pal.mode === 'files' ? 'Search files by name…' : 'Type a command…',\n }).on({\n // NOTE: the mode is read from live state inside onQuery, NOT captured here.\n // A listener belongs to the render that created it, and rendering is async —\n // a keystroke landing between `openPalette('files')` and the re-render would\n // otherwise be handled as a COMMANDS keystroke and silently do nothing.\n input: (e) => onQuery(ui, e.target.value),\n keydown: (e) => onKey(e, ui, items, index, run),\n $attach: (el) => {\n queueMicrotask(() => el.focus());\n // Same reason in reverse: dodo only patches `value` when the vnode prop\n // changed, so a reopened palette whose query is '' both times would keep\n // whatever text the previous session left in the live DOM node.\n if (el.value !== pal.query) el.value = pal.query;\n },\n }),\n ),\n div({ className: 'results' },\n items.length\n ? items.map((item, i) => {\n const hover = () => wb.setPaletteIndex(i);\n return pal.mode === 'files' ? fileOpt(item, i === index, run, hover) : cmdOpt(item, i === index, ui, run, hover);\n })\n : emptyResults(pal, state),\n ),\n ),\n );\n}\n\n// Nothing to show is three different situations, and saying \"No files found\" for all\n// three tells the user something false in two of them.\nfunction emptyResults(pal, state) {\n if (pal.mode !== 'files') return div({ className: 'none' }, 'No matching commands');\n if (state.se.paletteError) return div({ className: 'none error' }, icon('warn', { size: 14 }), ` ${state.se.paletteError}`);\n if (state.se.paletteLoading) return div({ className: 'none' }, div({ className: 'spinner' }), ' Searching…');\n return div({ className: 'none' }, pal.query.trim() ? 'No files found' : 'Type to search files by name');\n}\n\nfunction cmdOpt(cmd, active, ui, run, hover) {\n const key = ui.platform.keybindings.labelFor(cmd.id);\n const available = ui.platform.commands.isAvailable(cmd);\n return div({ className: `opt ${active ? 'active' : ''} ${available ? '' : 'unavailable'}` },\n span({ className: 'ico' }, icon(cmd.icon || 'command', { size: 16 })),\n cmd.category ? span({ className: 'cat' }, cmd.category + ' ›') : null,\n span({ className: 'title' }, cmd.title),\n !available ? span({ className: 'offline-tag' }, 'offline') : null,\n key ? span({ className: 'kbd' }, dd.h('kbd', prettyKey(keyRaw(ui, cmd.id)))) : null,\n ).on({ click: () => run(cmd), mouseenter: hover });\n}\n\nfunction keyRaw(ui, id) {\n const b = ui.platform.keybindings.resolved().find((x) => x.command === id);\n return b ? b.key : '';\n}\n\nfunction fileOpt(item, active, run, hover) {\n return div({ className: `opt ${active ? 'active' : ''}` },\n span({ className: 'ico' }, icon(iconForNode(item.node), { size: 16 })),\n span({ className: 'title' }, item.node.name),\n span({ className: 'sub' }, item.node.contentType || ''),\n ).on({ click: () => run(item), mouseenter: hover });\n}\n\nfunction filterCommands(state, ui, query) {\n const all = ui.platform.commands.paletteCommands();\n const q = query.trim().toLowerCase();\n if (!q) return all.slice(0, 60);\n const scored = [];\n for (const c of all) {\n const hay = `${c.category || ''} ${c.title}`.toLowerCase();\n const s = fuzzyScore(hay, q);\n if (s > 0) scored.push([s, c]);\n }\n return scored.sort((a, b) => b[0] - a[0]).slice(0, 60).map((x) => x[1]);\n}\n\nfunction fuzzyScore(hay, q) {\n let score = 0;\n let hi = 0;\n for (const ch of q) {\n const idx = hay.indexOf(ch, hi);\n if (idx < 0) return 0;\n score += idx === hi ? 3 : 1;\n hi = idx + 1;\n }\n if (hay.includes(q)) score += 10;\n return score;\n}\n\nfunction onKey(e, ui, items, index, run) {\n const wb = ui.platform.workbench;\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n wb.movePalette(1, items.length);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n wb.movePalette(-1, items.length);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n run(items[index]);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n wb.closePalette();\n }\n}\n\nlet fileTimer = null;\nfunction onQuery(ui, value) {\n const wb = ui.platform.workbench;\n wb.setPaletteQuery(value);\n // The palette's CURRENT mode, not whichever one was on screen when this listener\n // was created — see the note at the input.\n clearTimeout(fileTimer);\n if (wb.overlay.state.palette?.mode !== 'files') return;\n // Route the file search through an action; results land in the reactive search\n // service (state.se.paletteFiles), so the palette re-renders without ad-hoc state.\n fileTimer = setTimeout(() => ui.go(new QuickOpenAction(value)), 200);\n}\n",
|
|
92
|
+
"// The pre-install review — everything a user needs to decide whether to trust\n// and install a plugin: its identity + signature/domain-verified status, the\n// capabilities it wants (each explained, admin-only ones flagged), what it\n// contributes, and its settings. Capabilities are opt-in checkboxes; admin-only\n// ones are disabled for non-admins. No plugin code has run at this point.\n\nimport { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { bytes as fmtBytes } from '../format.js';\n\nconst { div, span, button, h3, p, label, input } = dd;\n\n// Grant selection persists across re-renders, keyed to the dialog instance.\nlet sel = { ref: null, grants: null };\n\nexport function pluginReview(d, ui) {\n const wb = ui.platform.workbench;\n const s = d.summary;\n if (sel.ref !== d) {\n // Default: request everything the user is allowed to grant.\n sel = { ref: d, grants: new Set(s.capabilities.filter((c) => !c.adminOnly || d.isAdmin).map((c) => c.id)) };\n }\n const toggle = (cap) => { sel.grants.has(cap) ? sel.grants.delete(cap) : sel.grants.add(cap); ui.rerender?.(); };\n\n return div({},\n div({ className: 'scrim' }).on({ click: () => wb.closeDialog() }),\n div({ className: 'dialog review', $styling: { width: 'min(560px, 96vw)' } },\n header(s),\n div({ className: 'review-body' },\n s.description ? p({ className: 'review-desc' }, s.description) : null,\n section('Capabilities it requests', s.capabilities.length\n ? div({ className: 'cap-list' }, ...s.capabilities.map((c) => capRow(c, d.isAdmin, sel.grants.has(c.id), () => toggle(c.id))))\n : muted('None — this plugin only runs in its sandbox.')),\n (s.network || []).length ? section('Network access', div({ className: 'contrib-list' }, ...s.network.map(endpointRow))) : null,\n (s.commands || []).length\n ? section('Commands it can run', div({ className: 'contrib-list' }, ...s.commands.map((id) => commandRow(id, ui))))\n : null,\n s.storage ? section('Storage', storageRows(s.storage)) : null,\n s.contributions.length ? section('What it adds', div({ className: 'contrib-list' }, ...s.contributions.map(contribRow))) : null,\n s.settings.length ? section('Settings', div({ className: 'contrib-list' }, ...s.settings.map(settingRow))) : null,\n div({ className: 'review-meta' }, `${s.fileCount} files · ${fmtBytes(s.sizeBytes)} · id ${s.id}`),\n ),\n div({ className: 'row-actions' },\n button({ className: 'btn' }, 'Cancel').on({ click: () => wb.closeDialog() }),\n button({ className: 'btn primary' }, icon('plug', { size: 15 }), 'Install').on({ click: () => d.onInstall([...sel.grants]) }),\n ),\n ),\n );\n}\n\nfunction header(s) {\n const t = s.trust || { status: 'unverified' };\n return div({ className: 'review-head' },\n div({ className: 'avatar' }, (s.name || '?')[0].toUpperCase()),\n div({ className: 'rh-main' },\n div({ className: 'rh-name' }, s.name, span({ className: 'rh-ver' }, 'v' + s.version)),\n div({ className: 'rh-author' }, 'by ' + s.author),\n ),\n trustBadge(t),\n );\n}\n\nfunction trustBadge(t) {\n if (t.status === 'verified') {\n return span({ className: 'trust verified', title: `Signed by a key published at ${t.domain}` }, icon('check', { size: 13 }), 'Verified · ' + t.domain);\n }\n if (t.status === 'signed') {\n return span({ className: 'trust signed', title: t.reason || 'Signed, but the domain does not vouch for the key' }, icon('info', { size: 13 }), 'Signed');\n }\n if (t.status === 'invalid') {\n return span({ className: 'trust invalid', title: t.reason || 'Invalid signature — the package may have been tampered with' }, icon('warn', { size: 13 }), 'Invalid signature');\n }\n return span({ className: 'trust unverified', title: t.reason || 'This plugin is not signed' }, icon('warn', { size: 13 }), 'Unverified');\n}\n\nfunction capRow(cap, isAdmin, checked, onToggle) {\n const blocked = cap.adminOnly && !isAdmin;\n return label({ className: `cap-row ${blocked ? 'blocked' : ''}` },\n input({ type: 'checkbox', checked: checked && !blocked, disabled: blocked }).on({ change: () => !blocked && onToggle() }),\n div({ className: 'cap-info' },\n div({ className: 'cap-name' }, cap.id, cap.adminOnly ? span({ className: 'pf-badge', $styling: { color: 'var(--warn)', 'margin-left': '6px' } }, 'admin only') : null),\n div({ className: 'cap-desc' }, cap.description + (blocked ? ' — requires an administrator' : '')),\n ),\n );\n}\n\nfunction contribRow(c) {\n const kindIcon = { command: 'command', opener: 'file', indexer: 'search', statusItem: 'info', register: 'info', keymap: 'command' }[c.kind] || 'command';\n return div({ className: 'contrib-row' },\n icon(kindIcon, { size: 13 }),\n span({ className: 'cr-title' }, c.title),\n c.detail ? span({ className: 'cr-detail' }, c.detail) : null,\n c.offline ? span({ className: 'pf-badge offline-ok' }, 'offline') : null,\n span({ className: 'pf-kind' }, c.kind),\n );\n}\n\n// One command the plugin may ask the host to run. Resolved against the live registry\n// so the user sees the human title and where it comes from (built-in vs another\n// plugin) — an id like \"explorer.delete\" alone doesn't convey what it does.\nfunction commandRow(id, ui) {\n const cmd = ui?.platform?.contributions?.get?.(id);\n const source = cmd?.pluginId ? 'plugin' : cmd ? 'built-in' : 'not installed';\n return div({ className: `contrib-row ${cmd ? '' : 'blocked'}` },\n icon('command', { size: 13 }),\n span({ className: 'cr-title' }, cmd?.title || id),\n span({ className: 'cr-detail' }, id),\n span({ className: 'pf-kind' }, source),\n );\n}\n\nfunction endpointRow(ep) {\n return div({ className: 'contrib-row' },\n icon('plug', { size: 13 }),\n span({ className: 'cr-title' }, ep.host),\n ep.path ? span({ className: 'cr-detail' }, ep.path) : null,\n span({ className: 'pf-kind' }, ep.scheme),\n );\n}\n\nfunction storageRows(st) {\n const rows = [];\n if (st.plugin) rows.push(storageRow('Private database', 'A SQLite store just for this plugin (server + this device).', false));\n if (st.domain) rows.push(storageRow('Shared database', `Shared with other plugins from ${'its domain'} (SQLite).`, st.domainBlocked));\n return div({ className: 'contrib-list' }, ...rows);\n}\nfunction storageRow(title, detail, blocked) {\n return div({ className: `contrib-row ${blocked ? 'blocked' : ''}` },\n icon('plug', { size: 13 }),\n span({ className: 'cr-title' }, title),\n span({ className: 'cr-detail' }, blocked ? detail + ' — needs a verified domain' : detail),\n blocked ? span({ className: 'pf-badge', $styling: { color: 'var(--warn)' } }, 'unavailable') : null,\n );\n}\n\nfunction settingRow(st) {\n return div({ className: 'contrib-row' },\n icon('gear', { size: 13 }),\n span({ className: 'cr-title' }, st.title),\n st.secret\n ? span({ className: 'pf-badge', $styling: { color: 'var(--warn)' } }, 'secret')\n : span({ className: 'pf-kind' }, st.type),\n );\n}\n\nfunction section(title, content) {\n return div({ className: 'review-section' }, div({ className: 'rs-title' }, title), content);\n}\nfunction muted(t) {\n return div({ className: 'muted', $styling: { 'font-size': '12.5px' } }, t);\n}\n",
|
|
93
|
+
"// Small transient overlays: modal dialog (prompt/confirm), right-click context\n// menu, toast notifications, the upload/transfer tray, and the plugin popup\n// panel (which hosts a plugin's own iframe, Chrome-extension style).\n\nimport { dd } from '../../runtime.js';\nimport { icon, iconForNode } from '../icon.js';\nimport { bytes } from '../format.js';\nimport { pluginReview } from './pluginReview.js';\nimport { typeKeyFor, typeLabelFor, rememberOpener, openerSource } from '../../bl/openers.js';\n\nconst { div, span, button, input, h3, p, select, option, label, textarea } = dd;\n\n// ---- Dialog ----------------------------------------------------------------\nexport function dialog(state, ui) {\n const d = state.overlay.dialog;\n if (!d) return null;\n if (d.kind === 'collection') return collectionDialog(d, ui);\n if (d.kind === 'plugin-review') return pluginReview(d, ui);\n if (d.kind === 'opener-chooser') return openerChooserDialog(d, ui);\n const wb = ui.platform.workbench;\n let value = d.value ?? '';\n const submit = () => (d.kind === 'confirm' ? d.onConfirm?.() : d.onSubmit?.(value));\n return div({},\n div({ className: 'scrim' }).on({ click: () => wb.closeDialog() }),\n div({ className: 'dialog' },\n h3(d.title),\n d.body ? div({ className: 'body' }, d.body) : null,\n d.kind === 'prompt'\n ? div({ className: 'field' },\n d.label ? span({ $styling: { 'font-size': '12px', color: 'var(--text-dim)' } }, d.label) : null,\n input({ className: 'input', value: d.value ?? '', placeholder: d.placeholder || '', autofocus: true })\n .on({\n input: (e) => { value = e.target.value; },\n keydown: (e) => { if (e.key === 'Enter') submit(); if (e.key === 'Escape') wb.closeDialog(); },\n $attach: (el) => queueMicrotask(() => { el.focus(); el.select(); }),\n }),\n )\n : null,\n div({ className: 'row-actions' },\n button({ className: 'btn' }, 'Cancel').on({ click: () => wb.closeDialog() }),\n button({ className: `btn ${d.danger ? 'danger' : 'primary'}` }, d.confirmLabel || 'OK').on({ click: submit }),\n ),\n ),\n );\n}\n\n// Choose which viewer opens a file when several openers match. Reactive via the\n// workbench dialog state (updateDialog), so radio/checkbox selection survives\n// re-renders. \"Remember\" persists a per-file-type association in settings.\nfunction openerChooserDialog(d, ui) {\n const wb = ui.platform.workbench;\n const platform = ui.platform;\n const selected = d.openerId || d.current || d.openers[0]?.id;\n const remember = !!d.remember;\n const confirm = () => {\n if (remember && selected) rememberOpener(platform, typeKeyFor(d.node), selected);\n wb.closeDialog();\n wb.openFile(d.node, selected, { reset: !!d.reset });\n };\n return div({},\n div({ className: 'scrim' }).on({ click: () => wb.closeDialog() }),\n div({ className: 'dialog opener-chooser' },\n h3(`Open “${d.node.name}” with…`),\n div({ className: 'opener-list' },\n ...d.openers.map((o) =>\n label({ className: `opener-opt ${o.id === selected ? 'sel' : ''}` },\n input({ type: 'radio', name: 'opener-choice', checked: o.id === selected })\n .on({ change: () => wb.updateDialog({ openerId: o.id }) }),\n icon(o.icon || iconForNode(d.node), { size: 18 }),\n div({ className: 'oo-main' },\n span({ className: 'oo-title' }, o.title || o.id),\n span({ className: 'oo-src' }, openerSource(platform, o)),\n ),\n ).on({ click: () => wb.updateDialog({ openerId: o.id }) }),\n ),\n ),\n label({ className: 'opener-remember' },\n input({ type: 'checkbox', checked: remember }).on({ change: (e) => wb.updateDialog({ remember: e.target.checked }) }),\n span(`Always use this for ${typeLabelFor(d.node)}`),\n ),\n div({ className: 'row-actions' },\n button({ className: 'btn' }, 'Cancel').on({ click: () => wb.closeDialog() }),\n button({ className: 'btn primary' }, 'Open').on({ click: confirm }),\n ),\n ),\n );\n}\n\n// A collection is a backing-store config. This form collects the driver + its\n// fields (filesystem path, or S3 bucket/prefix/keys) so a user with the create\n// capability can provision a new collection dynamically. The form persists\n// across re-renders (keyed to the dialog instance) so switching driver keeps it.\nlet colState = { ref: null, form: null };\nfunction collectionDialog(d, ui) {\n const wb = ui.platform.workbench;\n if (colState.ref !== d) {\n colState = { ref: d, form: { name: '', description: '', driver: 'filesystem', root: '', bucket: '', prefix: '', region: 'auto', endpoint: '', accessKeyId: '', secretAccessKey: '' } };\n }\n const form = colState.form;\n const set = (k) => (e) => { form[k] = e.target.value; if (k === 'driver') ui.rerender?.(); };\n const submit = () => {\n const store = { driver: form.driver };\n if (form.driver === 'filesystem') store.root = form.root;\n if (form.driver === 's3') {\n store.s3 = { bucket: form.bucket, region: form.region, endpoint: form.endpoint || undefined, accessKeyId: form.accessKeyId, secretAccessKey: form.secretAccessKey, forcePathStyle: !!form.endpoint };\n if (form.prefix) store.prefix = form.prefix;\n }\n if (form.driver === 'filesystem' && form.prefix) store.prefix = form.prefix;\n d.onSubmit?.({ name: form.name, description: form.description, store });\n };\n const field = (lbl, k, ph = '') => div({ className: 'field', $styling: { 'margin-bottom': '10px' } },\n label(lbl), input({ className: 'input', placeholder: ph }).on({ input: set(k) }));\n return div({},\n div({ className: 'scrim' }).on({ click: () => wb.closeDialog() }),\n div({ className: 'dialog', $styling: { width: 'min(480px, 94vw)' } },\n h3('New collection'),\n div({ className: 'body' }, 'A collection is a backing store you own. Configure where its files live.'),\n field('Name', 'name', 'Team Vault'),\n div({ className: 'field', $styling: { 'margin-bottom': '10px' } },\n label('Backing store'),\n select({ className: 'input' },\n option({ value: 'filesystem', selected: form.driver === 'filesystem' }, 'Filesystem / NAS'),\n option({ value: 's3', selected: form.driver === 's3' }, 'S3-compatible (S3 · R2 · MinIO)'),\n option({ value: 'memory', selected: form.driver === 'memory' }, 'Memory (ephemeral)'),\n ).on({ change: set('driver') })),\n storeFields(form, set),\n div({ className: 'row-actions' },\n button({ className: 'btn' }, 'Cancel').on({ click: () => wb.closeDialog() }),\n button({ className: 'btn primary' }, 'Create collection').on({ click: submit }),\n ),\n ),\n );\n}\nfunction storeFields(form, set) {\n const f = (lbl, k, ph = '', type = 'text') => div({ className: 'field', $styling: { 'margin-bottom': '10px' } },\n label(lbl), input({ className: 'input', placeholder: ph, type, autocomplete: 'off' }).on({ input: set(k) }));\n if (form.driver === 'filesystem') return div({}, f('Root directory', 'root', './data/team'), f('Prefix (optional)', 'prefix'));\n if (form.driver === 's3') {\n return div({},\n f('Bucket', 'bucket', 'my-bucket'),\n f('Prefix (optional)', 'prefix', 'team-a/'),\n f('Region', 'region', 'auto'),\n f('Endpoint (R2/MinIO; blank for AWS)', 'endpoint', 'https://<acct>.r2.cloudflarestorage.com'),\n f('Access key id', 'accessKeyId'),\n f('Secret access key', 'secretAccessKey', '', 'password'),\n );\n }\n return div({ className: 'body', $styling: { 'font-size': '12px' } }, 'Ephemeral — data is lost on restart. Good for testing.');\n}\n\n// ---- Context menu ----------------------------------------------------------\nexport function contextMenu(state, ui) {\n const m = state.overlay.contextMenu;\n if (!m || !m.items?.length) return null;\n const wb = ui.platform.workbench;\n // Clamp on both edges. Anchoring a menu ABOVE its trigger (the status bar opens\n // upward) produces a negative top, and only the far edge used to be clamped.\n // The height is whatever the menu needs OR whatever the window allows, whichever is\n // smaller — past that the menu scrolls (see `.menu`), so pushing it further up buys\n // nothing and would just leave a gap at the bottom.\n const wanted = Math.min(m.items.length * 34 + 20, window.innerHeight - 24);\n const x = Math.max(8, Math.min(m.x, window.innerWidth - 220));\n const y = Math.max(8, Math.min(m.y, window.innerHeight - wanted));\n return div({},\n div({ className: 'scrim', $styling: { background: 'transparent' } }).on({ click: () => wb.closeContextMenu(), contextmenu: (e) => { e.preventDefault(); wb.closeContextMenu(); } }),\n div({ className: 'menu', $styling: { left: x + 'px', top: y + 'px' } },\n ...m.items.map((it, i) =>\n it.sep\n ? div({ className: 'sep' })\n // A real <button>, not a click-handling div: menu entries were unreachable by\n // keyboard and invisible to the TV shell's spatial navigation, which only\n // considers focusable elements.\n : button({ className: `mi ${it.danger ? 'danger' : ''}`, autofocus: i === 0 },\n it.icon ? icon(it.icon, { size: 15 }) : null,\n span(it.label),\n it.kbd ? span({ className: 'kbd' }, it.kbd) : null,\n ).on({\n click: () => { wb.closeContextMenu(); it.run?.(); },\n // The first item is focused on open, so Escape has to get you back out.\n keydown: (e) => { if (e.key === 'Escape') { e.preventDefault(); wb.closeContextMenu(); } },\n $attach: (el) => { if (i === 0) queueMicrotask(() => el.focus()); },\n }),\n ),\n ),\n );\n}\n\n// ---- Toasts ----------------------------------------------------------------\nexport function toasts(state, ui) {\n const list = state.notif || [];\n if (!list.length) return null;\n return div({ className: 'toasts' },\n ...list.slice(-5).map((n) =>\n div({ className: `toast ${n.level}` },\n div({ className: 'bar' }),\n div({ className: 'msg' }, n.message),\n button({ className: 'x' }, icon('close', { size: 14 })).on({ click: () => ui.platform.notifications.dismiss(n.id) }),\n ).key(n.id),\n ),\n );\n}\n\n// ---- Transfer tray ---------------------------------------------------------\nexport function transferTray(state, ui) {\n const items = state.tr.items;\n if (!items.length) return null;\n return div({ className: 'tray' },\n div({ className: 'head' },\n icon('upload', { size: 14 }),\n span({ $styling: { 'margin-left': '6px' } }, 'Transfers'),\n div({ className: 'actions' },\n button({ className: 'iconbtn', title: 'Clear finished' }, icon('check', { size: 14 }))\n .on({ click: () => ui.app.transfers.clearDone() }),\n ),\n ),\n div({ className: 'items' },\n ...items.map((t) =>\n div({ className: `xfer ${t.status}` },\n div({ className: 'top' },\n icon(t.status === 'error' ? 'warn' : t.status === 'done' ? 'check' : 'upload', { size: 14 }),\n span({ className: 'name' }, t.name),\n t.status === 'active'\n ? button({ className: 'iconbtn', title: 'Cancel' }, icon('close', { size: 13 })).on({ click: () => ui.app.transfers.cancel(t.id) })\n : span({ className: 'pct' }, t.status === 'done' ? bytes(t.total) : t.status),\n ),\n t.status === 'active'\n ? div({ className: 'progress' }, div({ $styling: { width: `${Math.round(t.ratio * 100)}%` } }))\n : t.error ? div({ $styling: { 'font-size': '11px', color: 'var(--danger)', 'margin-top': '4px' } }, t.error) : null,\n t.status === 'active' ? div({ className: 'pct', $styling: { 'margin-top': '4px' } }, `${bytes(t.loaded)} / ${bytes(t.total)}`) : null,\n ).key(t.id),\n ),\n ),\n );\n}\n\n// ---- Plugin popup panel ----------------------------------------------------\nexport function pluginPanel(state, ui) {\n const pid = state.overlay.pluginPanel;\n if (!pid) return null;\n const plugin = (state.plugins || []).find((p) => p.id === pid);\n return div({ className: 'plugin-panel', $styling: { width: '380px' } },\n div({ className: 'head' },\n icon('plug', { size: 14 }),\n span(plugin?.name || pid),\n button({ className: 'iconbtn x' }, icon('close', { size: 14 })).on({ click: () => ui.platform.workbench.closePluginPanel() }),\n ),\n // The host mounts the plugin's iframe into this element on attach.\n div({ className: 'host' }).on({\n $attach: (el) => {\n el._detach = ui.platform.plugins.mountPanel(pid, el, { width: 380, height: 460 });\n },\n $detach: (el) => el._detach?.(),\n }),\n );\n}\n",
|
|
94
|
+
"// The Activity panel — what is running, and what is stuck.\n//\n// Two sections, because they are two different kinds of fact and the user acts on them\n// differently. Running work is watched and possibly cancelled; a standing problem is\n// retried or dismissed. Merging them into one \"notifications\" list would lose that:\n// a task that finished is over, while an issue that is listed is still true right now.\n//\n// A task that doesn't know its total renders as an indeterminate bar rather than a\n// guess. Nothing here fabricates a percentage.\n\nimport { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { bytes } from '../format.js';\n\nconst { div, button, span, h3, p } = dd;\n\nconst STATUS_ICON = { running: 'refresh', done: 'check', failed: 'close', cancelled: 'close' };\n\nfunction amount(task) {\n if (task.total == null) return null;\n return task.unit === 'bytes'\n ? `${bytes(task.done || 0)} of ${bytes(task.total)}`\n : `${task.done || 0} of ${task.total}${task.unit ? ` ${task.unit}` : ''}`;\n}\n\nfunction taskRow(task, ui) {\n const running = task.status === 'running';\n const determinate = running && task.total != null && task.total > 0;\n const pct = determinate ? Math.min(100, Math.round(((task.done || 0) / task.total) * 100)) : 0;\n return div({ className: `act-task act-${task.status}`, 'data-task-id': task.id },\n div({ className: 'act-row' },\n running\n ? div({ className: 'spinner', $styling: { width: '12px', height: '12px' } })\n : icon(STATUS_ICON[task.status] || 'info', { size: 13 }),\n div({ className: 'act-body' },\n div({ className: 'act-title' }, task.title),\n task.detail ? div({ className: 'act-detail' }, task.detail) : null,\n task.error ? div({ className: 'act-error' }, task.error) : null,\n ),\n running && task.cancellable\n ? button({ className: 'act-action', title: 'Cancel' }, icon('close', { size: 12 })).on({ click: () => ui.app.activity.cancel(task.id) })\n : !running\n ? button({ className: 'act-action', title: 'Dismiss' }, icon('close', { size: 12 })).on({ click: () => ui.app.activity.dismiss(task.id) })\n : null,\n ),\n running\n // An indeterminate bar animates without claiming a position. The alternative —\n // picking a number — would be a lie dressed as information.\n ? div({ className: `act-bar ${determinate ? '' : 'indeterminate'}` },\n div({ className: 'act-fill', $styling: determinate ? { width: `${pct}%` } : {} }))\n : null,\n running && amount(task) ? div({ className: 'act-amount' }, amount(task)) : null,\n );\n}\n\nfunction issueRow(issue, ui) {\n const since = new Date(issue.firstAt || issue.lastAt).toLocaleString();\n return div({ className: `act-issue act-sev-${issue.severity || 'error'}`, 'data-issue-id': issue.id },\n div({ className: 'act-row' },\n icon(issue.severity === 'warning' ? 'info' : 'close', { size: 13 }),\n div({ className: 'act-body' },\n div({ className: 'act-title' }, issue.title),\n issue.detail ? div({ className: 'act-detail' }, issue.detail) : null,\n div({ className: 'act-meta' }, issue.count > 1 ? `${issue.count} times, since ${since}` : `since ${since}`),\n ),\n ),\n div({ className: 'act-actions' },\n // Retry only when the server said pressing it will actually do something.\n issue.retryable\n ? button({ className: 'btn small act-retry' }, icon('refresh', { size: 12 }), span('Retry'))\n .on({ click: () => ui.app.activity.retryIssue(issue.id) })\n : null,\n button({ className: 'btn small ghost act-dismiss' }, 'Dismiss')\n .on({ click: () => ui.app.activity.dismissIssue(issue.id) }),\n ),\n );\n}\n\nexport default function activityPanel(state, ui) {\n const act = state.act || { tasks: [], issues: [] };\n if (!act.open) return null;\n const running = act.tasks.filter((t) => t.status === 'running');\n const recent = act.tasks.filter((t) => t.status !== 'running');\n\n return div({ className: 'activity-panel' },\n div({ className: 'act-head' },\n h3('Activity'),\n button({ className: 'act-action', title: 'Close' }, icon('close', { size: 13 }))\n .on({ click: () => ui.app.activity.togglePanel(false) }),\n ),\n\n // \"Nothing is wrong\" and \"we couldn't ask\" look identical unless one says so. Both\n // loads are reported, because either can fail alone.\n act.tasksError || act.issuesError\n ? div({ className: 'act-offline' }, icon('info', { size: 12 }),\n span(`Couldn't reach the server — this list may be out of date (${act.issuesError || act.tasksError})`))\n : null,\n\n div({ className: 'act-section' },\n div({ className: 'act-section-title' }, 'Running'),\n running.length\n ? div(...running.map((t) => taskRow(t, ui)))\n : p({ className: 'act-empty' }, 'Nothing running.'),\n ),\n\n act.issues.length\n ? div({ className: 'act-section' },\n div({ className: 'act-section-title' }, `Needs attention (${act.issues.length})`),\n div(...act.issues.map((i) => issueRow(i, ui))),\n )\n : div({ className: 'act-section' },\n div({ className: 'act-section-title' }, 'Needs attention'),\n // An unreadable list is not an empty one, and must never render as \"all clear\".\n p({ className: 'act-empty' },\n act.issuesError ? 'Could not load the list of problems.' : act.issuesLoading ? 'Checking…' : 'No standing problems.'),\n ),\n\n recent.length\n ? div({ className: 'act-section' },\n div({ className: 'act-section-title' }, 'Recently finished'),\n div(...recent.map((t) => taskRow(t, ui))),\n )\n : null,\n\n div({ className: 'act-foot act-actions' },\n button({ className: 'btn small ghost act-rebuild' }, icon('refresh', { size: 12 }), span('Rebuild search index'))\n .on({ click: () => ui.exec('workbench.rebuildIndex') }),\n button({ className: 'btn small ghost act-scan' }, icon('refresh', { size: 12 }), span('Scan for outside changes'))\n .on({ click: () => ui.exec('workbench.scanCollection') }),\n ),\n );\n}\n",
|
|
95
|
+
"// The phone shell: a thin top bar, a bottom tab bar, and two sheets.\n//\n// A left rail costs 52px of a 390px-wide screen and a status bar costs a row of chrome\n// that is unreadable at that size anyway — on a phone both are worse than useless. So\n// the rail's destinations become bottom tabs (thumb-reachable, which the top of the\n// screen is not), everything past the first few becomes one \"More\" sheet, and the whole\n// status bar folds behind a single icon in the top bar.\n//\n// The icon is not decoration: it changes to say what is happening. Work running shows a\n// spinner, a standing problem shows a badge, and neither is something the user has to go\n// looking for. Everything else — item counts, free space, backend — is a tap away rather\n// than permanently on screen, because on a phone it is reference material, not status.\n\nimport { dd } from '../../runtime.js';\nimport { icon } from '../icon.js';\nimport { bytes } from '../format.js';\nimport { statusFacts, usageChip } from './statusBar.js';\n\nconst { div, button, span, img } = dd;\n\n// The tabs that earn a permanent slot. Everything else lives in More — a bottom bar with\n// six items is a bottom bar nobody can hit.\nconst TABS = [\n { id: 'home', icon: 'search', label: 'Files', command: 'workbench.view.home' },\n { id: 'upload', icon: 'upload', label: 'Upload', command: 'explorer.upload' },\n { id: 'plugins', icon: 'plug', label: 'Plugins', command: 'workbench.view.plugins' },\n];\n\n/** Which single glyph best describes the drive's state right now. */\nfunction statusGlyph(f) {\n if (!f.off.online) return { icon: 'info', tone: 'warn', label: 'Offline' };\n if (f.issues.length) return { icon: 'warn', tone: 'danger', label: `${f.issues.length} need attention`, badge: f.issues.length };\n if (f.running.length || f.uploading.length || f.off.syncing) return { spinner: true, tone: '', label: 'Working…' };\n if (f.usage?.total && f.usage.available / f.usage.total < 0.1) return { icon: 'info', tone: 'warn', label: 'Low on space' };\n return { icon: 'info', tone: '', label: 'Status' };\n}\n\nexport function phoneTopBar(state, ui) {\n const f = statusFacts(state, ui);\n const g = statusGlyph(f);\n // Where you are, at the coarsest level. Deliberately NOT the open file's name: the\n // viewer's own breadcrumb sits directly beneath this bar and already says that, and two\n // rows of chrome repeating \"notes.txt\" costs a fifth of the screen to say it twice.\n const title = state.wb.activity === 'settings' ? 'Settings'\n : state.wb.activity === 'plugins' ? 'Plugins'\n : f.collectionId;\n return div({ className: 'phonebar top' },\n button({ className: 'pb-brand', title: 'Trove — home' }, img({ src: '/icon.svg', alt: 'Trove' }))\n .on({ click: () => ui.exec('workbench.view.home') }),\n div({ className: 'pb-title' }, title),\n button({ className: `pb-status ${g.tone}`, title: g.label, $attrs: { 'aria-label': g.label } },\n g.spinner\n ? div({ className: 'spinner', $styling: { width: '15px', height: '15px' } })\n : icon(g.icon, { size: 18 }),\n g.badge ? span({ className: 'pb-badge' }, String(g.badge > 9 ? '9+' : g.badge)) : null,\n ).on({ click: () => ui.platform.workbench.openSheet('status') }),\n );\n}\n\nexport function phoneBottomBar(state, ui) {\n const active = state.wb.activity;\n const sheet = state.wb.sheet;\n const unread = state.so.notifications.unread;\n return div({ className: 'phonebar bottom' },\n ...TABS.map((t) => button({\n className: `pb-tab ${!sheet && active === t.id ? 'active' : ''}`,\n $attrs: { 'aria-label': t.label },\n }, icon(t.icon, { size: 21 }), span({ className: 'pb-label' }, t.label))\n .on({ click: () => { ui.platform.workbench.closeSheet(); ui.exec(t.command); } })),\n // Settings is reached FROM the More sheet, so while you are in it \"More\" is where\n // you are — otherwise all four tabs read as inactive and the bar claims you are\n // nowhere.\n button({\n className: `pb-tab ${sheet === 'more' || (!sheet && !TABS.some((t) => t.id === active)) ? 'active' : ''}`,\n $attrs: { 'aria-label': 'More' },\n },\n icon('dots', { size: 21 }),\n unread ? span({ className: 'pb-badge' }, String(unread > 9 ? '9+' : unread)) : null,\n span({ className: 'pb-label' }, 'More'),\n ).on({ click: () => ui.platform.workbench.openSheet('more') }),\n );\n}\n\n/** A row in a sheet: an icon, a label, and either a value or a chevron. */\nfunction sheetRow({ icon: name, label, value, danger, onClick }) {\n const body = [\n icon(name, { size: 18 }),\n span({ className: 'sr-label' }, label),\n value != null ? span({ className: 'sr-value' }, value) : null,\n onClick ? icon('chevron-right', { size: 16, className: 'sr-go' }) : null,\n ];\n return onClick\n ? button({ className: `sheet-row ${danger ? 'danger' : ''}` }, ...body).on({ click: onClick })\n : div({ className: 'sheet-row static' }, ...body);\n}\n\nexport function phoneSheet(state, ui) {\n const which = state.wb.sheet;\n if (!which) return null;\n const wb = ui.platform.workbench;\n return div({ className: 'sheet-wrap' },\n div({ className: 'scrim' }).on({ click: () => wb.closeSheet() }),\n div({ className: 'sheet' },\n div({ className: 'sheet-grip' }).on({ click: () => wb.closeSheet() }),\n which === 'status' ? statusSheet(state, ui) : moreSheet(state, ui),\n ),\n );\n}\n\nfunction statusSheet(state, ui) {\n const f = statusFacts(state, ui);\n const wb = ui.platform.workbench;\n const go = (cmd) => () => { wb.closeSheet(); ui.exec(cmd); };\n return div({ className: 'sheet-body' },\n div({ className: 'sheet-title' }, f.collectionId),\n\n // Problems and running work first: they are the reason someone opened this.\n f.issues.length\n ? sheetRow({\n icon: 'warn', danger: true,\n label: `${f.issues.length} need${f.issues.length === 1 ? 's' : ''} attention`,\n onClick: () => { wb.closeSheet(); ui.app.activity.togglePanel(true); },\n })\n : null,\n f.running.length || f.uploading.length\n ? sheetRow({\n icon: 'refresh',\n label: `${f.running.length + f.uploading.length} running`,\n onClick: () => { wb.closeSheet(); ui.app.activity.togglePanel(true); },\n })\n : null,\n !f.off.online ? sheetRow({ icon: 'info', label: 'Offline', value: `${f.off.pins.length} pinned` }) : null,\n f.off.queued ? sheetRow({ icon: 'upload', label: 'Waiting to sync', value: String(f.off.queued) }) : null,\n\n sheetRow({\n icon: 'files',\n label: 'Items',\n value: f.partial\n ? `${f.shown.toLocaleString()} of ${f.totalKnown ? f.totalItems.toLocaleString() : 'more'}`\n : f.totalItems.toLocaleString(),\n }),\n // `+` where the figure covers only what has loaded, matching the desktop bar.\n sheetRow({ icon: 'file', label: 'Size', value: `${bytes(f.totalBytes)}${f.totalKnown || !f.partial ? '' : '+'}` }),\n // Only where the backend can actually answer — see usageChip.\n f.usage?.total\n ? div({ className: 'sheet-row static usage' },\n icon('download', { size: 18 }),\n span({ className: 'sr-label' }, 'Free space'),\n usageChip({ usage: f.usage }))\n : null,\n f.caps ? sheetRow({\n icon: f.caps.storage?.presignDownload ? 'download' : 'files',\n label: 'Transfers',\n value: f.caps.storage?.presignDownload ? 'Direct to storage' : 'Through the server',\n }) : null,\n f.caps?.features?.semanticSearch ? sheetRow({ icon: 'star', label: 'Search', value: 'Semantic + keyword' }) : null,\n\n div({ className: 'sheet-actions' },\n button({ className: 'btn small ghost' }, icon('refresh', { size: 13 }), span('Scan for changes')).on({ click: go('workbench.scanCollection') }),\n button({ className: 'btn small ghost' }, icon('refresh', { size: 13 }), span('Activity'))\n .on({ click: () => { ui.platform.workbench.closeSheet(); ui.app.activity.togglePanel(true); } }),\n ),\n );\n}\n\nfunction moreSheet(state, ui) {\n const wb = ui.platform.workbench;\n const go = (cmd) => () => { wb.closeSheet(); ui.exec(cmd); };\n const me = state.so.me;\n const unread = state.so.notifications.unread;\n return div({ className: 'sheet-body' },\n // No identity, no profile row — the same rule the desktop rail follows. An anonymous\n // deployment has no account to show and nothing to sign into.\n me && !me.anonymous\n ? div({ className: 'sheet-me' },\n me.picture ? img({ src: me.picture, alt: '', className: 'avatar-img' }) : span({ className: 'avatar-txt' }, (me.name || me.id || '?')[0].toUpperCase()),\n div({}, div({ className: 'sm-name' }, me.name || me.id), me.email ? div({ className: 'sm-sub' }, me.email) : null))\n : null,\n\n sheetRow({ icon: 'bell', label: unread ? `Notifications (${unread})` : 'Notifications', onClick: () => { wb.closeSheet(); ui.app.social.toggleInbox(true); } }),\n sheetRow({ icon: 'refresh', label: 'Activity & problems', onClick: () => { wb.closeSheet(); ui.app.activity.togglePanel(true); } }),\n sheetRow({ icon: 'info', label: 'Details & conversation', onClick: go('workbench.toggleInfoPanel') }),\n sheetRow({ icon: 'trash', label: 'Trash', onClick: go('explorer.showTrash') }),\n sheetRow({ icon: 'refresh', label: 'Refresh', onClick: go('explorer.refresh') }),\n sheetRow({ icon: 'command', label: 'All commands', onClick: go('workbench.showCommandPalette') }),\n sheetRow({ icon: 'gear', label: 'Settings', onClick: go('workbench.openSettings') }),\n );\n}\n",
|
|
96
|
+
"// Root composition. Assembles the `ui` helper (stable for the app's life),\n// zips every reactive slice the shell needs into one snapshot, and renders the\n// full workbench from it. Components stay pure — they receive (state, ui) and\n// either call ui.exec(commandId) or ui.go(action). This is the only module that\n// knows about every service at once.\n\nimport { dd, cell, derive, constant } from '../../runtime.js';\nimport { NavigateAction } from '../../bl/actions.js';\nimport activityBar from '../components/activityBar.js';\nimport statusBar from '../components/statusBar.js';\nimport launcher from '../components/launcher.js';\nimport settingsView from '../components/settingsView.js';\nimport pluginsView from '../components/pluginsView.js';\nimport editorArea from '../components/editorArea.js';\nimport commandPalette from '../components/commandPalette.js';\nimport { dialog, contextMenu, toasts, transferTray, pluginPanel } from '../components/overlays.js';\nimport activityPanel from '../components/activityPanel.js';\nimport { infoPanel } from '../components/social.js';\nimport { phoneTopBar, phoneBottomBar, phoneSheet } from '../components/phoneChrome.js';\n\nconst { alias, div } = dd;\n\nexport default function workbench({ engine, app, platform, plugins }) {\n // A counter rather than a timestamp: a cell drops a write of the value it already\n // holds, and two `rerender()` calls in the same millisecond would have compared equal.\n const bump = cell(0);\n const ui = {\n engine, app, platform,\n go: (action) => engine.dispatch(action),\n exec: (id, ...args) => platform.commands.execute(id, ...args),\n rerender: () => bump.update((n) => n + 1),\n uninstallPlugin: (id) => plugins?.uninstall(id),\n };\n\n const { watch } = platform.reactive;\n // One derived snapshot of every slice the shell reads. `derive` invalidates when any\n // of them does and recomputes once on the next read, so fifteen changes in a frame\n // still cost one render.\n const combined = derive(\n [\n platform.workbench.observe(),\n platform.workbench.observeOverlay(),\n platform.workbench.observeNav(),\n app.explorer.observe(),\n app.search.observe(),\n app.transfers.observe(),\n platform.notifications.observe(),\n platform.context.observe(),\n platform.settings.observe(),\n platform.plugins.observe() ?? constant([]),\n platform.contributions.observeType('statusItem'),\n app.social.observe(),\n app.offline.observe(),\n app.activity.observe(),\n platform.viewport.observe(),\n platform.voice.observe(),\n bump,\n ],\n // `_bump` is IN the snapshot, unlike before. `watch` skips a render whose value is\n // shallow-equal to the last one, so a forced re-render that left no trace in the\n // object would be discarded as \"nothing changed\" — which is the opposite of what\n // asking for one means.\n (wb, overlay, nav, ex, se, tr, notif, ctx, settings, pluginList, statusItems, so, off, act, vp, voice, _bump) =>\n ({ wb, overlay, nav, ex, se, tr, notif, ctx, settings, plugins: pluginList, statusItems, so, off, act, vp, voice, _bump }),\n );\n\n return alias(() => watch(combined, (state) => view(state, ui)));\n}\n\nfunction view(state, ui) {\n const mode = state.vp?.mode || 'desktop';\n const phone = mode === 'phone';\n return div({ className: `shell ${mode} ${state.settings['workbench.density'] === 'compact' ? 'compact' : ''}` },\n // On a phone the rail becomes a bottom bar and the status bar folds behind one icon\n // in a top bar — see phoneChrome. Everything between them is identical, which is the\n // point: only the chrome changes shape, not the app.\n phone ? phoneTopBar(state, ui) : null,\n div({ className: 'body' },\n phone ? null : activityBar(state, ui),\n mainArea(state, ui),\n ),\n phone ? phoneBottomBar(state, ui) : statusBar(state, ui),\n phone ? phoneSheet(state, ui) : null,\n // Overlays.\n searchModal(state, ui),\n commandPalette(state, ui),\n dialog(state, ui),\n contextMenu(state, ui),\n pluginPanel(state, ui),\n toasts(state, ui),\n transferTray(state, ui),\n activityPanel(state, ui),\n );\n}\n\nfunction mainArea(state, ui) {\n switch (state.wb.activity) {\n case 'settings': return settingsView(state, ui);\n case 'plugins': return pluginsView(state, ui);\n default: {\n // Home: the top of the panel stack — the launcher (base search) or, once a\n // file is open, its opener full-width (optionally split with the info panel).\n if (!state.nav.activeTabId) return launcher(state, ui);\n if (!state.wb.infoPanel) return editorArea(state, ui);\n // A 340px rail beside a 390px screen leaves nothing on either side. On a phone the\n // details panel takes the whole panel instead — it has its own close button, so\n // there is still a way back to the file.\n return state.vp?.mode === 'phone'\n ? infoPanel(state, ui)\n : div({ className: 'editor-split' }, editorArea(state, ui), infoPanel(state, ui));\n }\n }\n}\n\n// Double-shift search: the launcher as a modal overlay. Picking an item resets the\n// viewer stack (starts over) — the launcher reads state.wb.searchModal to know.\nfunction searchModal(state, ui) {\n if (!state.wb.searchModal) return div();\n return div({ className: 'search-modal' },\n div({ className: 'scrim' }).on({ click: () => ui.platform.workbench.closeSearchModal() }),\n div({ className: 'search-modal-panel' }, launcher(state, ui, { modal: true })),\n );\n}\n\nexport { NavigateAction };\n",
|
|
97
|
+
"// Booting a workbench — the whole client, as a function you call with what you want\n// added to it.\n//\n// This used to be `main.js`: a fixed sequence that registered a fixed set of openers\n// and mounted. Which is fine until someone wants to SHIP a different drive — a hosted\n// build with a rich media player, an image-caption indexer and a gallery view — and\n// finds the only way in is to fork the package. Every contribution the built-ins use\n// goes through the same registry a plugin's does, so there was never a technical reason\n// for that; there was only a hard-coded list.\n//\n// import { createWorkbench } from '@3sln/trove/web/workbench.js';\n//\n// createWorkbench({\n// openers: [{ id: 'acme.player', title: 'Player', priority: 60,\n// match: { mime: ['video/*'] }, component: player }],\n// views: [{ id: 'acme.gallery', title: 'Gallery', icon: 'grid', priority: 70,\n// match: { mime: ['image/*'] }, render: gallery }],\n// });\n//\n// Two ways to displace a built-in, both falling out of how the registry already works:\n// register a HIGHER PRIORITY (selection sorts by it, and the user can still pick the\n// plain one from the chooser), or register the SAME ID (the registry is a map, so it is\n// replaced outright). Prefer the first — it leaves the original reachable.\n//\n// These openers and views run IN PROCESS. That is the difference between a build of the\n// app and a plugin, and it cuts both ways: full access to the platform, and no sandbox\n// to catch a mistake. Anything a user installs is a plugin and stays in its frame.\n\nimport { dd, effect } from './runtime.js';\nimport { createPlatform } from './platform/index.js';\nimport { createApp } from './bl/index.js';\nimport { NavigateAction, UploadFilesAction, OpenInitialCollectionAction } from './bl/actions.js';\nimport { parsePackage } from './platform/pluginPackage.js';\nimport workbenchComposition from './ui/compositions/workbench.js';\nimport { registerBuiltinOpeners } from './ui/components/openers/index.js';\nimport { registerBuiltinViews } from './ui/components/views/index.js';\nimport { attachMedia } from './ui/media.js';\n\n/**\n * Build the platform, mount the workbench, and wire the cross-cutting behaviours that\n * belong to no single component — theme, global keybindings, OS drag-and-drop upload,\n * capability probing, the plugin catalog.\n *\n * @param {object} [options]\n * @param {Element} [options.root] where to mount (default `.workbench`)\n * @param {string} [options.baseUrl] API origin (default same-origin)\n * @param {Array} [options.openers] extra openers: `{ id, title, match, priority?, component }`\n * @param {Array} [options.views] extra views: `{ id, title, icon?, match?, priority?, render, move? }`\n * @param {Array} [options.settings] extra setting schemas, for whatever those contribute\n * @param {boolean} [options.serviceWorker] register /sw.js (default true)\n * @returns {{platform: object, engine: object, app: object}}\n */\nexport function createWorkbench({\n root = document.querySelector('.workbench'),\n baseUrl = '',\n openers = [],\n views = [],\n settings = [],\n serviceWorker = true,\n} = {}) {\n const platform = createPlatform({ baseUrl });\n const { engine, app } = createApp(platform);\n\n // Built-in openers and views are UI, so they register from the composition layer\n // (which is allowed to know both platform and ui) rather than from bl/.\n registerBuiltinOpeners(platform);\n registerBuiltinViews(platform);\n // Then whatever this build adds, so a same-id registration replaces the built-in\n // rather than being replaced by it.\n register(platform, 'opener', openers);\n register(platform, 'view', views);\n if (settings.length) platform.settings.register(settings);\n\n // Plugins are installed by the user (from a .zip or a URL) and persisted locally;\n // restore any that were installed on this device, then run.\n platform.plugins.restore();\n\n const plugins = { uninstall: (id) => platform.plugins.uninstall(id) };\n\n // --- mount ----------------------------------------------------------------\n const App = workbenchComposition({ engine, app, platform, plugins });\n dd.reconcile(root, [App()]);\n\n // --- theme ----------------------------------------------------------------\n const applyTheme = () => {\n document.documentElement.dataset.theme = platform.settings.get('workbench.theme') || 'dark';\n };\n applyTheme();\n effect(platform.settings.observe(), () => applyTheme());\n\n // --- form factor ----------------------------------------------------------\n // Watch the window so rotating a phone, or dragging a desktop window narrow, re-picks\n // the shell. Also stamps data-layout on <html> for the CSS half of the same decision.\n platform.viewport.install();\n // Ask once whether this browser can transcribe on-device, so the launcher knows\n // whether to offer a microphone. Nothing is downloaded and no permission is requested\n // — see platform/voice.js.\n platform.voice.refresh().catch(() => {});\n // Remote-control navigation, which switches itself on and off with the TV layout.\n platform.spatialNav.install();\n\n // --- keybindings ----------------------------------------------------------\n platform.keybindings.install(window);\n\n // --- viewer stack ↔ browser history ---------------------------------------\n window.addEventListener('popstate', (e) => platform.workbench.onPopState(e));\n installDoubleShift(platform);\n\n // --- service worker (offline shell + pinned files + push) -----------------\n if (serviceWorker && 'serviceWorker' in navigator) {\n window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js').catch(() => {}));\n }\n\n installDragAndDrop(engine, app);\n\n // --- initial load ---------------------------------------------------------\n (async () => {\n try {\n platform.capabilities = await platform.api.capabilities();\n // What the server can do is read straight off `platform`, not held in a store, so\n // nothing invalidated when it arrived. Re-pushing the same state object will not\n // do it either: a cell compares with Object.is and drops a write of what it\n // already holds. Saying \"this changed under you\" is the store's job.\n platform.workbench.touch(); // the status bar reads capabilities\n } catch (err) {\n platform.notifications.error(`Cannot reach the Trove server: ${err.message}`);\n }\n // Land on a collection the user can actually read — see OpenInitialCollectionAction.\n engine.dispatch(new OpenInitialCollectionAction());\n })();\n\n // Expose for debugging / e2e.\n window.__trove = {\n platform, engine, app,\n // Test/automation hook: install a package from raw zip bytes.\n test: {\n parsePackage,\n assessTrust: (pkg) => platform.plugins.assessTrust(pkg),\n install: (pkg, opts) => platform.plugins.install(pkg, opts),\n NavigateAction,\n // Keeping a media element pointed at a URL that still works is the half of signed\n // URLs that only shows up over time, so it is drivable from a probe.\n attachMedia,\n },\n };\n\n return { platform, engine, app };\n}\n\n/**\n * Register a build's own contributions.\n *\n * `id` is required and it is not a formality: an unaddressed contribution cannot be\n * remembered as someone's default opener, cannot be pinned as their view, and cannot be\n * named in a keymap. Failing here is far better than shipping a build whose media\n * player silently forgets which one the user chose.\n */\nfunction register(platform, type, list) {\n for (const c of list) {\n if (!c?.id) throw new Error(`A ${type} passed to createWorkbench needs an \"id\"`);\n const { id, ...spec } = c;\n platform.contributions.register(id, { type, ...spec });\n }\n}\n\n// Double-shift → modal search overlay (Raycast-style). Two Shift presses within 400ms,\n// with no other key between, open it.\nfunction installDoubleShift(platform) {\n let lastShift = 0;\n window.addEventListener('keydown', (e) => {\n if (e.key === 'Shift' && !e.repeat) {\n const now = Date.now();\n if (now - lastShift < 400) { platform.workbench.openSearchModal(); lastShift = 0; }\n else lastShift = now;\n } else if (e.key !== 'Shift') {\n lastShift = 0;\n }\n });\n}\n\n// Dropping files anywhere on the window uploads them to the open collection.\nfunction installDragAndDrop(engine, app) {\n let dragDepth = 0;\n window.addEventListener('dragenter', (e) => {\n if (!e.dataTransfer?.types.includes('Files')) return;\n dragDepth++;\n document.body.classList.add('dragging-file');\n });\n window.addEventListener('dragover', (e) => {\n if (e.dataTransfer?.types.includes('Files')) e.preventDefault();\n });\n window.addEventListener('dragleave', () => {\n if (--dragDepth <= 0) {\n dragDepth = 0;\n document.body.classList.remove('dragging-file');\n }\n });\n window.addEventListener('drop', (e) => {\n if (!e.dataTransfer?.files?.length) return;\n e.preventDefault();\n dragDepth = 0;\n document.body.classList.remove('dragging-file');\n engine.dispatch(new UploadFilesAction(e.dataTransfer.files, app.explorer.state.collectionId));\n });\n}\n",
|
|
98
|
+
"// The open-source build's entry point: a workbench with nothing added to it.\n//\n// Everything that used to live here is `createWorkbench` (./workbench.js), which takes\n// the openers and views a build wants to ship. A hosted or bespoke build imports that\n// instead of this file and passes its own — no fork, and the same registry the plugins\n// use.\n\nimport { createWorkbench } from './workbench.js';\n\ncreateWorkbench();\n"
|
|
99
|
+
],
|
|
100
|
+
"mappings": "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SACA,UACA,YACA,WACA,WACA,SACA,UACA,UACA,MACA,MACA,MACA,MACA,MACA,MACA,QACA,OACA,WACA,cACA,MACA,OACA,MACA,MACA,cACA,UACA,MACA,MACA,MACA,KACA,OACA,MACA,KACA,QACA,KACA,MACA,QACA,QACA,QACA,OACA,OACA,MACA,KACA,OACA,OACA,QACA,KACA,KACA,QACA,SACA,QACA,UACA,OACA,OACA,QACA,KACA,OACA,QACA,SACA,OACA,OACA,SACA,SACA,SACA,UACA,UACA,SACA,WACA,UACA,UACA,OACA,QACA,UACA,WACA,OACA,YACA,SACA,SACA,MACA,SACA,MACA,SACA,MACA,UACA,YACA,YACA,QACA,SACA,SACA,UACA,SACA,YACA,UACA,UACA,YACA,UACA,YACA,WACA,UACA,WACA,QACA,YACA,YACA,SACA,UACE,GC/GJ,SAAS,EAAiB,CAAC,GAAK,UAAU,CAAC,EAAG,CAC5C,eAAe,IAAM,CACnB,GAAI,CAAC,GAAQ,QAAS,EAAG,EAC1B,EAGH,SAAS,EAAkB,CAAC,EAAG,CAC7B,MAAO,KACL,EACE,MACA,CACE,SAAU,CACR,mBAAoB,OACpB,MAAO,OACP,QAAS,MACT,cAAe,UACjB,CACF,EACA,EAAE,SAAU,KAAM,UAAU,GAAO,SAAW,OAAO,CAAK,GAAG,EAC7D;AAAA;AAAA,EACA,GAAO,OAAS,EAClB,EAGJ,SAAS,EAAa,CAAC,EAAO,CAC5B,OACE,GAAS,MAAQ,OAAO,EAAM,UAAY,YAAc,OAAO,EAAM,YAAc,WAahF,SAAS,EAAQ,CAAC,EAAc,CACrC,IAAM,EAAS,GAAc,CAAY,EAAI,CAAC,KAAM,CAAY,EAAI,EAC9D,EAAO,GAAQ,KACrB,GAAI,CAAC,GAAc,CAAI,EACrB,MAAU,MAAM,qEAAqE,EAGvF,MAAO,CAIL,SAAU,OAAO,EAAK,WAAa,WAAa,EAAK,SAAW,GAChE,YAAa,GAAmB,EAAK,CAAC,KACnC,EACH,UAIG,EAAK,QACV,ECpDK,IAAM,GAAU,OAAO,uBAAuB,EAE/C,GAAU,OAAO,uBAAuB,EAGvC,SAA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",
|
|
101
|
+
"debugId": "1FB47C976F5B8EDD64756E2164756E21",
|
|
102
|
+
"names": []
|
|
103
|
+
}
|