@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 @@
|
|
|
1
|
+
:root{--font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;--font-mono:"SF Mono","JetBrains Mono","Fira Code",ui-monospace,Menlo,Consolas,monospace;--radius:8px;--radius-sm:5px;--activity-w:52px;--sidebar-w:280px;--statusbar-h:24px;--ease:cubic-bezier(.2,.7,.2,1);--focus:var(--accent);--focus-halo:color-mix(in srgb,var(--accent)32%,transparent)}:root,:root[data-theme=dark]{--bg:#181a1f;--bg-elevated:#1f2228;--bg-sidebar:#1b1d23;--bg-activity:#16181d;--bg-input:#12141a;--bg-hover:#ffffff0d;--bg-active:#ffffff14;--bg-selected:#2d6fd128;--border:#ffffff14;--border-strong:#ffffff26;--text:#d7dce5;--text-dim:#9aa4b2;--text-faint:#6b7280;--accent:#6ea8fe;--accent-2:#a679f0;--accent-text:#0b1020;--danger:#f2607a;--warn:#e5b567;--success:#6cc48a;--shadow:0 12px 40px -8px #000000a8,0 2px 8px -2px #0008;--scrollbar:#fff2}:root[data-theme=midnight]{--bg:#0c0e14;--bg-elevated:#12151e;--bg-sidebar:#0e111a;--bg-activity:#090b11;--bg-input:#080a10;--bg-hover:#ffffff0a;--bg-active:#ffffff12;--bg-selected:#4b7bff22;--border:#ffffff10;--border-strong:#ffffff1f;--text:#cdd6f4;--text-dim:#8b95b0;--text-faint:#5a6482;--accent:#89b4fa;--accent-2:#cba6f7;--danger:#f38ba8;--warn:#f9e2af;--success:#a6e3a1;--shadow:0 16px 50px -10px #000c,0 2px 10px -2px #000a;--scrollbar:#ffffff1a}:root[data-theme=light]{--bg:#f6f7f9;--bg-elevated:#fff;--bg-sidebar:#eef0f3;--bg-activity:#e6e9ee;--bg-input:#fff;--bg-hover:#0000000a;--bg-active:#00000012;--bg-selected:#2d6fd11f;--border:#00000014;--border-strong:#00000024;--text:#1c2430;--text-dim:#55606f;--text-faint:#8a95a4;--accent:#2d6fd1;--accent-2:#7c50c8;--accent-text:#fff;--danger:#d63b5b;--warn:#b3801f;--success:#2f9e57;--shadow:0 12px 40px -10px #0000002e,0 2px 8px -3px #0000001f;--scrollbar:#00000026}*{box-sizing:border-box}html,body{height:100%;margin:0}*,*:before,*:after{-webkit-tap-highlight-color:transparent}body{font-family:var(--font-ui);color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow:hidden;font-size:13px}button{font:inherit;color:inherit;cursor:pointer;text-align:inherit;background:0 0;border:0}input,select,textarea{font:inherit;color:inherit}::selection{background:var(--accent);color:var(--accent-text)}:focus-visible{outline:2px solid var(--focus);outline-offset:2px}:focus:not(:focus-visible){outline:none}.btn:focus-visible{outline:none;border-color:var(--focus);box-shadow:0 0 0 3px var(--focus-halo)}.btn.primary:focus-visible{box-shadow:0 0 0 3px var(--focus-halo)}.iconbtn:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.act-action:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.launch-clear:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.pb-status:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.pb-brand:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.vn-back:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.c-link:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.react:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.launch-box:focus-within{border-color:var(--focus);box-shadow:var(--shadow),0 0 0 3px var(--focus-halo)}.launch-input:focus-visible{outline:none}.launch-item:focus-visible{outline:none;background:var(--bg-selected);box-shadow:inset 3px 0 0 0 var(--focus),0 0 0 1px var(--focus-halo)}.sheet-row:focus-visible{outline:none;background:var(--bg-selected);box-shadow:inset 3px 0 0 0 var(--focus),0 0 0 1px var(--focus-halo)}.inbox-item:focus-visible{outline:none;background:var(--bg-selected);box-shadow:inset 3px 0 0 0 var(--focus),0 0 0 1px var(--focus-halo)}.activitybar .item:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.activitybar .item:focus-visible:before{content:"";position:absolute;background:linear-gradient(var(--accent),var(--accent-2));border-radius:0 3px 3px 0;width:3px;top:8px;bottom:8px;left:-6px}.activitybar .brand-mark:focus-visible{outline:none;background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}.pb-tab:focus-visible{outline:none;color:var(--accent);box-shadow:inset 0 3px 0 0 var(--focus)}.statusbar .seg:focus-visible{outline:none;background:var(--bg-active);color:var(--text);box-shadow:inset 0 -2px 0 0 var(--focus)}.md a:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:3px}.link:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:3px}.launch-up:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:3px}.lh-example:focus-visible{outline:none;border-color:var(--focus);box-shadow:0 0 0 3px var(--focus-halo)}::-webkit-scrollbar{width:11px;height:11px}::-webkit-scrollbar-thumb{background:var(--scrollbar);background-clip:content-box;border:3px solid #0000;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:var(--border-strong);background-clip:content-box}.workbench{height:100%}.shell{display:grid;grid-template-rows:1fr var(--statusbar-h);height:100vh;height:100dvh}.body{display:grid;grid-template-columns:var(--activity-w)1fr;overflow:hidden;min-height:0}kbd{font-family:var(--font-ui);color:var(--text-dim);background:var(--bg-hover);border:1px solid var(--border);text-align:center;border-radius:4px;min-width:16px;padding:1px 5px;font-size:10.5px}.kbd-edit{display:inline-flex;color:var(--text-dim);font:inherit;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;align-items: center;gap:6px;padding:2px 4px}.kbd-edit:hover{border-color:var(--border);background:var(--bg-hover)}.kbd-edit:focus-visible{outline:none;border-color:var(--focus);box-shadow:0 0 0 3px var(--focus-halo)}.kbd-clash{display:inline-flex;color:var(--warn);align-items: center;margin-right:4px}.kbd-edit.clash kbd{border-color:color-mix(in srgb,var(--warn)50%,transparent);color:var(--warn)}.kbd-edit.listening{border-color:var(--accent);background:var(--bg-active);color:var(--accent);padding:3px 8px;font-size:11px}.activitybar{display:flex;background:var(--bg-activity);border-right:1px solid var(--border);flex-direction:column;align-items: center;gap:4px;padding:8px 0 10px}.activitybar .brand-mark{display:grid;cursor:pointer;transition:background .12s var(--ease),transform .12s var(--ease);background:0 0;border:0;border-radius:10px;place-items: center;width:40px;height:40px;margin-bottom:6px}.activitybar .brand-mark img{width:24px;height:24px}.activitybar .brand-mark:hover{background:var(--bg-hover)}.activitybar .brand-mark:active{transform:translateY(1px)}.activitybar .item{position:relative;color:var(--text-faint);display:grid;transition:color .12s var(--ease),background .12s var(--ease);background:0 0;border:0;border-radius:9px;place-items: center;width:40px;height:40px}.activitybar .item:hover{color:var(--text);background:var(--bg-hover)}.activitybar .item.active{color:var(--text);background:var(--bg-active)}.activitybar .item.active:before{content:"";position:absolute;background:linear-gradient(var(--accent),var(--accent-2));border-radius:0 3px 3px 0;width:3px;top:8px;bottom:8px;left:-6px}.activitybar .item .badge{position:absolute;background:var(--accent);color:var(--accent-text);display:grid;border-radius:8px;place-items: center;min-width:15px;height:15px;padding:0 3px;font-size:9.5px;font-weight:700;top:3px;right:3px}.activitybar .spacer{flex:1}.activitybar .bell-wrap{display:grid;place-items: center}.activitybar .iconbtn{color:var(--text-faint);border-radius:9px;width:40px;height:40px}.activitybar .iconbtn:hover{color:var(--text);background:var(--bg-hover)}.activitybar .principal{cursor:default;margin:4px 0 0}.sidebar{width:var(--sidebar-w);background:var(--bg-sidebar);border-right:1px solid var(--border);display:flex;overflow:hidden;flex-direction:column;min-width:0}.sidebar .head{display:flex;text-transform:uppercase;letter-spacing:.6px;color:var(--text-dim);align-items: center;gap:6px;padding:10px 12px 8px;font-size:11px;font-weight:600}.sidebar .head .actions{display:flex;gap:2px;margin-left:auto}.sidebar .scroll{overflow-y:auto;overflow-x:hidden;flex:1}.iconbtn{color:var(--text-dim);display:grid;transition:all .12s var(--ease);background:0 0;border:0;border-radius:6px;place-items: center;width:26px;height:26px}.iconbtn:hover{background:var(--bg-hover);color:var(--text)}.iconbtn:active{transform:translateY(1px)}.iconbtn.danger:hover{color:var(--danger)}.collection-switch{display:flex;border:1px solid var(--border);background:var(--bg-elevated);color:var(--text);transition:border-color .12s var(--ease);border-radius:8px;align-items: center;gap:8px;width:calc(100% - 16px);margin:4px 8px 0;padding:7px 10px}.collection-switch:hover{border-color:var(--accent)}.collection-switch .cs-name{text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;font-weight:550}.collection-switch svg:last-child{color:var(--text-faint)}.breadcrumb{display:flex;border-bottom:1px solid var(--border);color:var(--text-dim);flex-wrap:wrap;align-items: center;gap:2px;padding:6px 10px;font-size:12px}.breadcrumb .crumb{color:inherit;background:0 0;border:0;border-radius:5px;padding:2px 6px}.breadcrumb .crumb:hover{background:var(--bg-hover);color:var(--text)}.breadcrumb .sep{opacity:.4}.breadcrumb .crumb.current{color:var(--text);font-weight:500}.filelist{padding:4px}.row{display:flex;cursor:default;user-select:none;color:var(--text);transition:background 80ms var(--ease);border-radius:6px;align-items: center;gap:9px;height:30px;padding:0 8px}.compact .row{height:26px}.row:hover{background:var(--bg-hover)}.row.selected{background:var(--bg-selected);box-shadow:inset 0 0 0 1px var(--accent)}.row.drop-target{box-shadow:inset 0 0 0 2px var(--accent);background:var(--bg-selected)}.row .ico{color:var(--text-dim);display:grid;flex:none;place-items: center}.row .ico.folder{color:var(--accent)}.row .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.row .meta{color:var(--text-faint);font-variant-numeric:tabular-nums;font-size:11px}.row .pin-dot{color:var(--success);display:grid;flex:none;place-items: center}.statusbar .offline-badge,.statusbar .offline-badge span{color:var(--warn)}.row .rename-input{background:var(--bg-input);border:1px solid var(--accent);outline:none;color:var(--text);border-radius:4px;flex:1;padding:2px 6px}.empty{display:flex;color:var(--text-faint);text-align:center;flex-direction:column;justify-content:center;align-items: center;gap:10px;height:100%;padding:32px}.empty .big{opacity:.5;font-size:34px}.empty .cta{border:1px dashed var(--border-strong);color:var(--text-dim);background:0 0;border-radius:8px;margin-top:4px;padding:7px 14px}.empty .cta:hover{border-color:var(--accent);color:var(--text)}.editor{display:flex;background:var(--bg);overflow:hidden;flex-direction:column;min-width:0}.tabs{display:flex;background:var(--bg-activity);border-bottom:1px solid var(--border);overflow-x:auto;align-items:stretch;height:36px}.tab{display:flex;border:0;border-right:1px solid var(--border);color:var(--text-dim);position:relative;white-space:nowrap;background:0 0;align-items: center;gap:8px;max-width:220px;padding:0 10px 0 14px}.tab.active{background:var(--bg);color:var(--text)}.tab.active:before{content:"";position:absolute;background:linear-gradient(90deg,var(--accent),var(--accent-2));height:2px;top:0;left:0;right:0}.tab .label{overflow:hidden;text-overflow:ellipsis}.tab .close{display:grid;opacity:.5;border-radius:4px;place-items: center;width:18px;height:18px}.tab .close:hover{background:var(--bg-hover);opacity:1}.editor .stage{overflow:auto;position:relative;flex:1;min-height:0}.welcome{display:grid;place-items: center;height:100%}.welcome .card{text-align:center;max-width:460px;padding:40px}.welcome h1{margin:12px 0 6px;font-size:26px;font-weight:650}.welcome .logo{font-size:46px}.welcome p{color:var(--text-dim);line-height:1.6}.welcome .hints{display:grid;text-align:left;gap:8px;margin-top:22px}.welcome .hint{display:flex;color:var(--text-dim);align-items: center;gap:10px}.statusbar{display:flex;background:linear-gradient(90deg,color-mix(in srgb,var(--accent)20%,var(--bg-activity)),var(--bg-activity));border-top:1px solid var(--border);color:var(--text-dim);align-items: center;gap:2px;padding:0 8px;font-size:11.5px}.statusbar .seg{display:flex;color:inherit;background:0 0;border:0;align-items: center;gap:5px;height:100%;padding:0 8px}.statusbar .seg:hover{background:var(--bg-hover);color:var(--text)}.statusbar .spacer{flex:1}.btn{display:inline-flex;border:1px solid var(--border-strong);background:var(--bg-elevated);color:var(--text);transition:all .12s var(--ease);border-radius:7px;align-items: center;gap:7px;padding:7px 13px}.btn:hover{border-color:var(--accent)}.btn.primary{background:linear-gradient(180deg,var(--accent),color-mix(in srgb,var(--accent)82%,black));color:var(--accent-text);border:0;font-weight:600}.btn.small{border-radius:6px;gap:5px;padding:4px 9px;font-size:12px}.btn.ghost{color:var(--text-dim);background:0 0;border-color:#0000}.btn.ghost:hover{background:var(--bg-hover);border-color:var(--border);color:var(--text)}.btn.on{border-color:var(--accent);color:var(--accent);background:var(--bg-active)}.btn.primary:hover{filter:brightness(1.08)}.btn.danger{color:var(--danger);border-color:color-mix(in srgb,var(--danger)40%,var(--border))}.btn.danger:hover{background:color-mix(in srgb,var(--danger)14%,transparent)}.btn:disabled{opacity:.45;pointer-events:none}.field{display:flex;flex-direction:column;gap:6px}.field label{color:var(--text-dim);font-size:12px}.input,select.input{background:var(--bg-input);color:var(--text);border:1px solid var(--border-strong);outline:none;transition:border-color .12s var(--ease),box-shadow .12s var(--ease);border-radius:7px;width:100%;padding:8px 11px}.input:focus{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent)22%,transparent)}.switch{position:relative;flex:none;width:38px;height:22px}.switch input{position:absolute;opacity:0;cursor:pointer;z-index:1;width:100%;height:100%;margin:0;inset:0}.switch .track{position:absolute;background:var(--border-strong);transition:background .15s var(--ease);border-radius:22px;inset:0}.switch .track:before{content:"";position:absolute;transition:transform .16s var(--ease);background:#fff;border-radius:50%;width:16px;height:16px;top:3px;left:3px}.switch input:focus-visible+.track{box-shadow:0 0 0 3px var(--focus-halo);outline:1px solid var(--focus)}.switch input:checked+.track{background:var(--accent)}.switch input:checked+.track:before{transform:translate(16px)}.scrim{position:fixed;backdrop-filter:blur(1.5px);z-index:40;background:#0006;inset:0}.palette{position:fixed;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);z-index:50;overflow:hidden;animation:pop .13s var(--ease);border-radius:12px;width:min(640px,92vw);top:12vh;left:50%;transform:translate(-50%)}@keyframes pop{0%{opacity:0;transform:translate(-50%,-6px)scale(.98)}to{opacity:1;transform:translate(-50%)scale(1)}}.palette .search{display:flex;border-bottom:1px solid var(--border);align-items: center;gap:9px;padding:12px 14px}.palette .search input{outline:none;color:var(--text);background:0 0;border:0;flex:1;font-size:15px}.palette .results{overflow-y:auto;max-height:52vh;padding:6px}.palette .opt{display:flex;cursor:default;border-radius:7px;align-items: center;gap:10px;padding:8px 10px}.palette .opt.active{background:var(--bg-active)}.palette .opt .ico{color:var(--text-dim)}.palette .opt .title{flex:1}.palette .opt .cat{color:var(--text-faint);font-size:11px}.palette .opt .kbd{margin-left:auto}.palette .opt .sub{color:var(--text-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}.palette .opt.unavailable{opacity:.5}.palette .opt.unavailable .title{text-decoration:line-through;text-decoration-color:var(--text-faint)}.offline-tag{text-transform:uppercase;letter-spacing:.5px;color:var(--warn);border:1px solid color-mix(in srgb,var(--warn)40%,transparent);border-radius:5px;padding:1px 5px;font-size:9.5px}.plugin-features{border-top:1px solid var(--border);margin-top:12px;padding-top:10px}.pf-head{display:flex;text-transform:uppercase;letter-spacing:.5px;color:var(--text-dim);justify-content:space-between;margin-bottom:8px;font-size:11px}.pf-list{display:flex;flex-direction:column;gap:4px}.pf-row{display:flex;border-radius:6px;align-items: center;gap:8px;padding:4px 6px;font-size:12.5px}.pf-row.off{opacity:.55}.pf-row .pf-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pf-row .pf-kind{color:var(--text-faint);font-size:10px}.pf-badge{color:var(--text-faint);border:1px solid var(--border);white-space:nowrap;border-radius:5px;margin-left:auto;padding:1px 5px;font-size:9.5px}.pf-badge.offline-ok{color:var(--success);border:1px solid color-mix(in srgb,var(--success)40%,transparent);border-radius:5px;padding:1px 5px;font-size:9.5px}.pf-dot{border-radius:50%;flex:none;width:8px;height:8px}.pf-dot.on{background:var(--success)}.pf-dot.no{background:var(--text-faint)}.palette .none{text-align:center;color:var(--text-faint);display:flex;justify-content:center;align-items: center;gap:8px;padding:22px}.palette .none.error{color:var(--danger,#e5534b)}.palette .none .spinner{width:13px;height:13px}.dialog{position:fixed;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);z-index:50;animation:pop .13s var(--ease);border-radius:12px;width:min(440px,92vw);padding:20px;top:50%;left:50%;transform:translate(-50%,-50%)}.dialog h3{margin:0 0 12px;font-size:16px}.dialog .body{display:block;color:var(--text-dim);margin-bottom:16px;line-height:1.5}.dialog .row-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.opener-chooser .opener-list{display:flex;flex-direction:column;gap:6px;margin:4px 0 12px}.opener-opt{display:flex;border:1px solid var(--border);cursor:pointer;border-radius:9px;align-items: center;gap:10px;padding:9px 11px}.opener-opt:hover{background:var(--bg-hover)}.opener-opt.sel{border-color:var(--accent);background:color-mix(in srgb,var(--accent)10%,transparent)}.opener-opt input{accent-color:var(--accent)}.opener-opt .oo-main{display:flex;flex-direction:column;gap:1px}.opener-opt .oo-title{font-size:13px;font-weight:500}.opener-opt .oo-src{color:var(--text-dim);font-size:11px}.opener-remember{display:flex;color:var(--text-dim);cursor:pointer;align-items: center;gap:8px;font-size:12.5px}.opener-remember input{accent-color:var(--accent)}.menu{overflow-y:auto;overscroll-behavior:contain;position:fixed;z-index:60;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);animation:pop .1s var(--ease);border-radius:9px;min-width:200px;max-height:calc(100vh - 24px);padding:5px}.menu .mi{display:flex;cursor:default;border-radius:6px;align-items: center;gap:10px;padding:7px 9px}.menu .mi:hover{background:var(--bg-active)}.menu .mi.danger:hover{background:color-mix(in srgb,var(--danger)16%,transparent);color:var(--danger)}.menu .mi .kbd{color:var(--text-faint);margin-left:auto}.menu .sep{background:var(--border);height:1px;margin:5px 2px}.toasts{position:fixed;bottom:calc(var(--statusbar-h) + 12px);z-index:70;display:flex;flex-direction:column;gap:8px;width:340px;right:14px}.toast{display:flex;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);animation:slidein .16s var(--ease);border-radius:10px;gap:10px;padding:11px 13px}@keyframes slidein{0%{opacity:0;transform:translate(12px)}}.toast .bar{border-radius:3px;flex:none;width:3px}.toast.info .bar{background:var(--accent)}.toast.success .bar{background:var(--success)}.toast.warn .bar{background:var(--warn)}.toast.error .bar{background:var(--danger)}.toast .msg{flex:1;line-height:1.4}.toast .x{color:var(--text-faint);align-self: flex-start}.tray{position:fixed;bottom:calc(var(--statusbar-h) + 12px);left:calc(var(--activity-w) + 12px);z-index:65;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);overflow:hidden;border-radius:12px;width:320px}.tray .head{display:flex;border-bottom:1px solid var(--border);align-items: center;padding:10px 12px;font-size:12px;font-weight:600}.tray .head .actions{margin-left:auto}.tray .items{overflow-y:auto;max-height:300px}.xfer{border-bottom:1px solid var(--border);padding:9px 12px}.xfer:last-child{border-bottom:0}.xfer .top{display:flex;align-items: center;gap:8px}.xfer .top .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:12px}.xfer .pct{color:var(--text-faint);font-variant-numeric:tabular-nums;font-size:11px}.progress{background:var(--bg-hover);overflow:hidden;border-radius:3px;height:4px;margin-top:7px}.progress>div{background:linear-gradient(90deg,var(--accent),var(--accent-2));transition:width .2s var(--ease);height:100%}.xfer.error .progress>div{background:var(--danger)}.xfer.cancelled{opacity:.55}.searchview{display:flex;flex-direction:column;gap:10px;height:100%;padding:10px}.searchview .box{display:flex;gap:6px}.searchview .modes{display:flex;gap:4px}.chip{border:1px solid var(--border);color:var(--text-dim);background:0 0;border-radius:20px;padding:4px 10px;font-size:11.5px}.chip.active{background:var(--bg-selected);border-color:var(--accent);color:var(--text)}.results{overflow-y:auto;display:flex;flex-direction:column;flex:1;gap:6px}.result{border:1px solid var(--border);background:var(--bg-elevated);cursor:default;border-radius:9px;padding:10px 11px}.result:hover{border-color:var(--border-strong)}.result .top{display:flex;align-items: center;gap:8px}.result .top .name{font-weight:550}.result .top .score{color:var(--text-faint);margin-left:auto;font-size:10.5px}.result .path{color:var(--text-faint);margin:2px 0 0 24px;font-size:11px}.result .snippet{color:var(--text-dim);margin:6px 0 0 24px;font-size:12px;line-height:1.5}.result .snippet mark{background:color-mix(in srgb,var(--accent)32%,transparent);color:var(--text);border-radius:3px;padding:0 2px}.result .badge{background:var(--bg-hover);color:var(--text-dim);border-radius:5px;padding:1px 6px;font-size:10px}.settings{max-width:720px;margin:0 auto;padding:26px 24px 60px}.settings h2{margin:0 0 4px;font-size:20px}.settings .sub{color:var(--text-dim);margin-bottom:22px}.settings .group{margin-bottom:28px}.settings .group h3{text-transform:uppercase;letter-spacing:.6px;color:var(--accent);margin:0 0 10px;font-size:12px}.setting{display:flex;border-bottom:1px solid var(--border);align-items: center;gap:16px;padding:12px 0}.setting .info{flex:1}.setting .info .t{font-weight:550}.setting .info .d{color:var(--text-faint);margin-top:2px;font-size:12px;line-height:1.4}.setting .control{display:flex;flex:none;justify-content:flex-end;width:200px}.plugins{display:flex;flex-direction:column;gap:12px;padding:16px}.plugin-card{border:1px solid var(--border);background:var(--bg-elevated);border-radius:11px;padding:14px}.plugin-card .top{display:flex;align-items: center;gap:10px}.plugin-card .avatar{background:linear-gradient(135deg,var(--accent),var(--accent-2));display:grid;color:var(--accent-text);border-radius:9px;place-items: center;width:38px;height:38px;font-weight:700}.plugin-card .name{font-weight:600}.plugin-card .status{border-radius:12px;margin-left:auto;padding:2px 8px;font-size:11px}.plugin-card .status.active{background:color-mix(in srgb,var(--success)20%,transparent);color:var(--success)}.plugin-card .status.error{background:color-mix(in srgb,var(--danger)20%,transparent);color:var(--danger)}.plugin-card .status.loading{background:var(--bg-hover);color:var(--text-dim)}.plugin-card .caps{display:flex;flex-wrap:wrap;gap:5px;margin:10px 0 0}.plugin-card .cap{background:var(--bg-hover);color:var(--text-dim);border-radius:6px;padding:2px 7px;font-size:10.5px}.plugin-card .desc{color:var(--text-dim);margin-top:8px;font-size:12px;line-height:1.5}.plugin-card .actions{display:flex;gap:8px;margin-top:12px}.plugin-panel{position:fixed;z-index:55;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);overflow:hidden;animation:pop .13s var(--ease);border-radius:12px;top:12px;right:12px}.plugin-panel .head{display:flex;border-bottom:1px solid var(--border);align-items: center;gap:8px;padding:9px 12px;font-size:12px;font-weight:600}.plugin-panel .head .x{margin-left:auto}.plugin-panel .host{display:block}.plugin-install{display:flex;gap:8px;margin-top:10px}.trust{display:inline-flex;border:1px solid var(--border);white-space:nowrap;border-radius:12px;align-items: center;gap:4px;padding:2px 7px;font-size:10.5px}.trust.verified{color:var(--success);border-color:color-mix(in srgb,var(--success)45%,transparent);background:color-mix(in srgb,var(--success)12%,transparent)}.trust.signed{color:var(--text-dim)}.trust.unverified{color:var(--warn);border-color:color-mix(in srgb,var(--warn)40%,transparent)}.trust.invalid{color:var(--danger,#e5484d);border-color:color-mix(in srgb,var(--danger,#e5484d)55%,transparent);background:color-mix(in srgb,var(--danger,#e5484d)14%,transparent);font-weight:600}.dialog.review{overflow:hidden;padding:0}.review-head{display:flex;border-bottom:1px solid var(--border);align-items: center;gap:12px;padding:16px 18px}.review-head .avatar{border-radius:11px;width:44px;height:44px}.review-head .rh-main{flex:1}.review-head .rh-name{display:flex;align-items:baseline;gap:8px;font-size:16px;font-weight:650}.review-head .rh-ver{color:var(--text-faint);font-size:11px;font-weight:400}.review-head .rh-author{color:var(--text-dim);margin-top:2px;font-size:12px}.review-body{overflow-y:auto;max-height:60vh;padding:14px 18px}.review-desc{color:var(--text-dim);margin:0 0 14px;line-height:1.55}.review-section{margin-bottom:16px}.review-section .rs-title{text-transform:uppercase;letter-spacing:.5px;color:var(--accent);margin-bottom:8px;font-size:11px}.cap-list{display:flex;flex-direction:column;gap:8px}.cap-row{display:flex;border:1px solid var(--border);cursor:pointer;border-radius:9px;align-items: flex-start;gap:10px;padding:8px 10px}.cap-row:hover{border-color:var(--border-strong)}.cap-row.blocked,.contrib-row.blocked{opacity:.6;cursor:not-allowed}.contrib-row.blocked .pf-title{text-decoration:line-through}.cap-row input{accent-color:var(--accent);margin-top:2px}.cap-row .cap-name{font-size:13px;font-weight:600}.cap-row .cap-desc{color:var(--text-dim);margin-top:2px;font-size:12px;line-height:1.4}.contrib-list{display:flex;flex-direction:column;gap:3px}.contrib-row{display:flex;align-items: center;gap:8px;padding:3px 4px;font-size:12.5px}.contrib-row .cr-title{color:var(--text)}.contrib-row .cr-detail{color:var(--text-faint);font-size:11px}.contrib-row .pf-kind{color:var(--text-faint);margin-left:auto;font-size:10px}.review-meta{color:var(--text-faint);margin-top:6px;font-size:11px}.dialog.review .row-actions{border-top:1px solid var(--border);margin:0;padding:14px 18px}.viewer{display:flex;flex-direction:column;height:100%}.viewer.text pre{font-family:var(--font-mono);white-space:pre-wrap;word-break:break-word;color:var(--text);margin:0;padding:18px 20px;font-size:12.5px;line-height:1.65}.viewer.image{display:grid;background:repeating-conic-gradient(var(--bg-elevated)0% 25%,var(--bg)0% 50%)50%/22px 22px;place-items: center}.viewer.image img{object-fit:contain;box-shadow:var(--shadow);max-width:100%;max-height:100%}.viewer .loading,.viewer .fallback{color:var(--text-faint);display:flex;text-align:center;flex-direction:column;align-items: center;gap:12px;margin:auto;padding:40px}.viewer.plugin-viewer{display:block;position:relative}.viewer.plugin-viewer .pv-host{width:100%;height:100%}.viewer.plugin-viewer .pv-status{position:absolute;z-index:2;display:grid;background:var(--bg);place-items: center;inset:0}.viewer.plugin-viewer .pv-status .loading,.viewer.plugin-viewer .pv-status .fallback{color:var(--text-faint);display:flex;text-align:center;flex-direction:column;align-items: center;gap:12px;padding:40px}.viewer-dock{position:fixed;z-index:60;display:none;box-shadow:var(--shadow);background:0 0;border-radius:12px;flex-direction:column;bottom:18px;right:18px}.viewer-dock .vd-bar{display:flex;background:var(--bg-elevated);border:1px solid var(--border-strong);border-bottom:0;border-radius:12px 12px 0 0;align-items: center;gap:6px;height:26px;padding:0 4px 0 10px}.viewer-dock .vd-title{color:var(--text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;font-size:11.5px}.viewer-dock .vd-bar button{color:var(--text-dim);cursor:pointer;display:grid;background:0 0;border:0;border-radius:5px;place-items: center;width:20px;height:20px;padding:0;font-size:12px}.viewer-dock .vd-bar button:hover{background:var(--bg-hover);color:var(--text)}.viewer-dock .vd-body{border:1px solid var(--border-strong);background:0 0;border-top:0;border-radius:0 0 12px 12px;flex:1;min-height:0}.audiobook-host{height:100%;min-height:0}.audiobook{display:grid;grid-template-columns:300px 1fr;height:100%;min-height:0}.audiobook .cover-pane{background:var(--bg-elevated);border-right:1px solid var(--border);display:flex;overflow-y:auto;flex-direction:column;align-items: center;gap:18px;padding:26px}.audiobook .cover{box-shadow:var(--shadow);object-fit:cover;background:linear-gradient(135deg,var(--accent),var(--accent-2));display:grid;color:#fff;border-radius:14px;place-items: center;width:220px;height:220px;font-size:60px}.audiobook .meta{text-align:center}.audiobook .meta .title{font-size:17px;font-weight:650}.audiobook .meta .author{color:var(--text-dim);margin-top:3px}.audiobook .meta .narrator{color:var(--text-faint);margin-top:2px;font-size:12px}.audiobook .transport{display:flex;flex-direction:column;gap:12px;width:100%}.audiobook .scrub{display:flex;align-items: center;gap:10px}.audiobook .scrub input[type=range]{accent-color:var(--accent);flex:1}.audiobook .time{font-variant-numeric:tabular-nums;color:var(--text-faint);min-width:42px;font-size:11px}.audiobook .buttons{display:flex;justify-content:center;align-items: center;gap:14px}.audiobook .buttons .play{background:linear-gradient(180deg,var(--accent),var(--accent-2));color:var(--accent-text);display:grid;box-shadow:var(--shadow);border:0;border-radius:50%;place-items: center;width:54px;height:54px}.audiobook .buttons .play:hover{filter:brightness(1.08)}.audiobook .buttons .skip{background:var(--bg-hover);color:var(--text);display:grid;border:0;border-radius:50%;place-items: center;width:40px;height:40px}.audiobook .buttons .rate{background:var(--bg-hover);color:var(--text-dim);font-variant-numeric:tabular-nums;border:0;border-radius:8px;min-width:46px;padding:6px 8px}.audiobook .chapters{overflow-y:auto;padding:10px}.audiobook .chapters .ch-head{text-transform:uppercase;letter-spacing:.6px;color:var(--text-dim);display:flex;padding:8px 10px;font-size:11px}.audiobook .chapters .ch-head .count{color:var(--text-faint);margin-left:auto}.audiobook .chapter{display:flex;cursor:default;border-radius:8px;align-items: center;gap:10px;padding:9px 11px}.audiobook .chapter:hover{background:var(--bg-hover)}.audiobook .chapter.current{background:var(--bg-selected)}.audiobook .chapter .n{text-align:right;color:var(--text-faint);font-variant-numeric:tabular-nums;width:22px;font-size:12px}.audiobook .chapter .t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.audiobook .chapter .d{color:var(--text-faint);font-variant-numeric:tabular-nums;font-size:11px}.audiobook .chapter.current .n{color:var(--accent)}.spinner{border:2px solid var(--border-strong);border-top-color:var(--accent);animation:spin .7s linear infinite;border-radius:50%;width:20px;height:20px}@keyframes spin{to{transform:rotate(360deg)}}.dragging-file .editor:after,.dragging-file .sidebar:after{content:"Drop to upload";position:absolute;border:2px dashed var(--accent);display:grid;color:var(--accent);background:color-mix(in srgb,var(--accent)8%,transparent);pointer-events:none;z-index:30;border-radius:12px;place-items: center;font-weight:600;inset:8px}.editor,.sidebar{position:relative}.principal{overflow:hidden;display:grid;background:linear-gradient(135deg,var(--accent),var(--accent-2));color:#fff;border-radius:50%;place-items: center;width:30px;height:30px;font-size:12px;font-weight:700}.principal .avatar-img{object-fit:cover;width:100%;height:100%}.bell-wrap{position:relative}.bell-badge{position:absolute;background:var(--danger);color:#fff;display:grid;border-radius:8px;place-items: center;min-width:15px;height:15px;padding:0 3px;font-size:9.5px;font-weight:700;top:3px;right:3px}.inbox{position:fixed;left:calc(var(--activity-w) + 8px);background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);z-index:55;overflow:hidden;animation:pop .12s var(--ease);border-radius:12px;width:340px;bottom:8px}.inbox-head{display:flex;border-bottom:1px solid var(--border);justify-content:space-between;align-items: center;padding:11px 13px;font-size:12px;font-weight:600}.inbox-head .link,.link{color:var(--accent);background:0 0;border:0;font-size:12px}.inbox-head .muted,.muted{color:var(--text-faint)}.inbox-items{overflow-y:auto;max-height:380px}.inbox-item{border-bottom:1px solid var(--border);cursor:default;padding:10px 13px}.inbox-item:hover{background:var(--bg-hover)}.inbox-item.unread{border-left:2px solid var(--accent)}.inbox-item .ii-title{font-size:12.5px;font-weight:550}.inbox-item .ii-excerpt{color:var(--text-dim);margin-top:2px;font-size:12px}.inbox-item .ii-time{color:var(--text-faint);margin-top:3px;font-size:10.5px}.inbox-empty{display:flex;color:var(--text-faint);flex-direction:column;align-items: center;gap:8px;padding:30px}.editor-split{display:grid;grid-template-columns:1fr 340px;overflow:hidden;min-width:0}.infopanel{border-left:1px solid var(--border);background:var(--bg-sidebar);display:flex;flex-direction:column;min-width:0;min-height:0}.infopanel>*{min-width:0}.ip-head{display:flex;border-bottom:1px solid var(--border);align-items: center;gap:8px;padding:10px 12px;font-size:12px;font-weight:600}.ip-head .iconbtn{margin-left:auto}.ip-body{overflow-y:auto;flex:1}.ip-empty{color:var(--text-faint);text-align:center;padding:30px}.ip-file{border-bottom:1px solid var(--border);padding:12px}.ip-file .ip-name{font-weight:600}.ip-file .ip-path{color:var(--text-faint);margin-top:2px;font-size:11px}.ip-section{border-bottom:1px solid var(--border);padding:12px}.ip-body>:last-child{border-bottom:0}.ip-label{text-transform:uppercase;letter-spacing:.5px;color:var(--text-dim);margin-bottom:9px;font-size:11px}.tags{display:flex;flex-wrap:wrap;align-items: center;gap:6px}.tag{display:inline-flex;background:var(--bg-selected);border:1px solid var(--border);border-radius:14px;align-items: center;gap:5px;padding:3px 4px 3px 9px;font-size:12px}.tag-x{color:var(--text-faint);display:grid;background:0 0;border:0;border-radius:50%;place-items: center;width:16px;height:16px}.tag-x:hover{background:var(--bg-active);color:var(--danger)}.tag-input{background:var(--bg-input);border:1px solid var(--border);outline:none;color:var(--text);border-radius:14px;width:84px;padding:4px 10px;font-size:12px}.tag-input:focus{border-color:var(--accent);width:120px}.conv .thread{display:flex;flex-direction:column;gap:12px;margin-bottom:12px}.conv-empty{color:var(--text-faint);margin-bottom:12px;font-size:12px;line-height:1.5}.conv-signin{display:flex;color:var(--text-faint);flex-wrap:wrap;align-items:baseline;gap:6px;padding:8px 0;font-size:12px}.comment{font-size:13px}.c-head{display:flex;align-items: center;gap:7px}.c-avatar{background:linear-gradient(135deg,var(--accent-2),var(--accent));color:#fff;display:grid;border-radius:50%;flex:none;place-items: center;width:22px;height:22px;font-size:11px;font-weight:700}.c-author{font-size:12.5px;font-weight:600}.c-time{color:var(--text-faint);font-size:11px}.c-body{white-space:pre-wrap;word-break:break-word;margin:4px 0 4px 29px;line-height:1.5}.c-body.deleted{color:var(--text-faint);font-style:italic}.c-body .mention{color:var(--accent);background:color-mix(in srgb,var(--accent)14%,transparent);border-radius:4px;padding:0 3px;font-weight:600}.c-actions{display:flex;flex-wrap:wrap;align-items: center;gap:4px;margin-left:29px}.react{display:inline-flex;background:0 0;border:0;border-radius:12px;align-items: center;gap:3px;padding:2px 6px;font-size:12px}.react:hover{background:var(--bg-hover)}.react.on{background:var(--bg-selected)}.react .rc{color:var(--text-dim);font-size:10px}.c-link{color:var(--text-faint);background:0 0;border:0;border-radius:5px;padding:2px 5px;font-size:11.5px}.c-link:hover{color:var(--text);background:var(--bg-hover)}.c-link.danger:hover{color:var(--danger)}.replies{border-left:2px solid var(--border);margin-top:10px}.composer{display:flex;flex-direction:column;gap:6px}.composer .replying{color:var(--text-dim);display:flex;align-items: center;gap:8px;font-size:11px}.composer-input{background:var(--bg-input);border:1px solid var(--border-strong);resize:vertical;outline:none;color:var(--text);border-radius:9px;padding:8px 10px;font-family:inherit}.composer-input:focus{border-color:var(--accent)}.composer-actions{display:flex;justify-content:space-between;align-items: center}.composer-actions .btn{padding:5px 12px}@media (max-width:720px){:root{--sidebar-w:220px}.audiobook{grid-template-columns:1fr;grid-template-rows:auto 1fr}.audiobook .cover-pane{border-right:0;border-bottom:1px solid var(--border)}.editor-split{grid-template-columns:1fr}.editor-split .infopanel{display:none}}.launcher{display:flex;flex-direction:column;width:100%;max-width:820px;height:100%;margin:0 auto;padding:0 20px}.launch-box{display:flex;background:var(--bg-input);border:1px solid var(--border-strong);box-shadow:var(--shadow);border-radius:12px;align-items: center;gap:10px;margin:min(12vh,90px) 0 4px;padding:12px 16px}.launch-box>svg{color:var(--text-dim);flex:none}.launch-box button>svg{flex:none}.launch-resolved{display:flex;color:var(--text-dim);flex-wrap:wrap;align-items: center;gap:7px;margin:8px 4px 0;font-size:12px}.launch-resolved .rq-label{text-transform:uppercase;letter-spacing:.05em;color:var(--text-faint);font-size:10px;font-weight:600}.launch-resolved .rq-text{color:var(--text)}.launch-resolved .rq-chip{display:inline-flex;background:var(--bg-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:12px;align-items: center;gap:4px;padding:2px 8px}.launch-input{outline:none;color:var(--text);font:500 17px/1.3 var(--font-ui);background:0 0;border:0;flex:1}.launch-input::placeholder{color:var(--text-faint);font-weight:400}.launch-clear{display:inline-flex;color:var(--text-faint);cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items: center;width:24px;height:24px}.launch-clear:hover{background:var(--bg-hover);color:var(--text)}.launch-mic{display:inline-flex;color:var(--text-faint);cursor:pointer;background:0 0;border:0;border-radius:7px;flex:none;justify-content:center;align-items: center;width:26px;height:26px}.launch-mic:hover{background:var(--bg-hover);color:var(--text)}.launch-mic.on{color:var(--accent-text);background:var(--accent);animation:mic-pulse 1.4s var(--ease)infinite}@keyframes mic-pulse{0%,to{box-shadow:0 0 0 0 var(--focus-halo)}50%{box-shadow:0 0 0 5px #0000}}@media (prefers-reduced-motion:reduce){.launch-mic.on{animation:none}}.launch-body{overflow-y:auto;flex:1;padding:8px 0 40px}.launch-group{margin-top:14px}.launch-h{display:flex;letter-spacing:.04em;text-transform:uppercase;color:var(--text-faint);justify-content:space-between;align-items: center;gap:8px;padding:4px 10px;font-size:11px;font-weight:600}.launch-list{display:flex;flex-direction:column}.launch-item{display:flex;cursor:pointer;color:var(--text);border-radius:8px;align-items: center;gap:10px;padding:8px 10px}.launch-item svg{color:var(--text-dim);flex:none}.launch-item .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0}.launch-item:hover{background:var(--bg-hover)}.launch-item.active{background:var(--bg-selected);box-shadow:inset 3px 0 0 0 var(--focus)}.launch-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-faint);flex:0 auto;min-width:0;font-size:12.5px}.launch-more{display:inline-flex;color:var(--text-faint);cursor:pointer;opacity:0;transition:opacity .1s var(--ease),background .1s var(--ease);background:0 0;border:0;border-radius:7px;flex:none;justify-content:center;align-items: center;width:26px;height:26px;margin:-4px -4px -4px 0}.launch-item:hover .launch-more,.launch-item.active .launch-more{opacity:1}.launch-more:focus-visible{opacity:1}.launch-more:hover{background:var(--bg-active);color:var(--text)}.launch-more:focus-visible{outline:none;color:var(--text);background:var(--bg-active);box-shadow:0 0 0 2px var(--focus-halo)}@media (hover:none){.launch-more{opacity:1}}.launch-kind{color:var(--text-faint);border:1px solid var(--border);border-radius:5px;flex:none;padding:1px 6px;font-size:11px}.launch-up{display:inline-flex;color:var(--text-dim);cursor:pointer;background:0 0;border:0;border-radius:5px;align-items: center;gap:3px;padding:2px 4px;font-size:12px}.launch-up:hover{background:var(--bg-hover);color:var(--text)}.launch-empty{color:var(--text-faint);padding:8px 12px;font-size:13px}.view-switch{display:inline-flex;border-left:1px solid var(--border);flex:none;align-items: center;gap:2px;margin-left:2px;padding-left:4px}.vs-btn{display:inline-flex;color:var(--text-faint);cursor:pointer;background:0 0;border:0;border-radius:7px;justify-content:center;align-items: center;width:26px;height:26px}.vs-btn:hover{background:var(--bg-hover);color:var(--text)}.vs-btn.on{background:var(--bg-active);color:var(--text)}.vs-btn:focus-visible{outline:none;color:var(--text);box-shadow:0 0 0 2px var(--focus-halo)}.view-error{display:flex;border:1px solid var(--border);background:var(--bg-hover);color:var(--text-dim);border-radius:8px;align-items: center;gap:6px;margin:8px 10px 0;padding:7px 10px;font-size:12.5px}.view-error svg{color:var(--warn);flex:none}.grid-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));gap:10px;padding:4px 10px}.grid-tile{display:flex;cursor:pointer;color:var(--text);border-radius:10px;flex-direction:column;gap:6px;padding:6px}.grid-tile:hover{background:var(--bg-hover)}.grid-tile.active{background:var(--bg-selected);box-shadow:inset 0 0 0 2px var(--focus)}.gt-media{position:relative;display:grid;aspect-ratio:1;background:var(--bg-hover);overflow:hidden;border-radius:8px;place-items: center}.gt-media>svg{color:var(--text-faint)}.gt-img{position:absolute;object-fit:cover;width:100%;height:100%;inset:0}.gt-badge{position:absolute;color:#fff;background:#0009;border-radius:5px;padding:1px 6px;font-size:10.5px;bottom:5px;left:5px}.gt-media .launch-more{position:absolute;background:var(--bg-elevated);margin:0;top:3px;right:3px}.grid-tile:hover .launch-more,.grid-tile.active .launch-more{opacity:1}.gt-name{overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;word-break:break-word;-webkit-box-orient:vertical;font-size:12.5px;line-height:1.35}.viewer-nav{display:flex;background:var(--bg-activity);border-bottom:1px solid var(--border);align-items: center;gap:6px;height:40px;padding:0 8px}.vn-back{display:inline-flex;border:1px solid var(--border);background:var(--bg-elevated);color:var(--text-dim);border-radius:7px;justify-content:center;align-items: center;width:30px;height:28px}.vn-back:hover{color:var(--text);background:var(--bg-hover)}.vn-trail{display:flex;overflow-x:auto;flex:1;align-items: center;gap:2px;min-width:0}.vn-crumb{display:inline-flex;color:var(--text-dim);white-space:nowrap;background:0 0;border:0;border-radius:6px;align-items: center;gap:5px;max-width:240px;padding:4px 8px}.vn-crumb .label{overflow:hidden;text-overflow:ellipsis}.vn-crumb:hover{background:var(--bg-hover);color:var(--text)}.vn-crumb.active{color:var(--text);font-weight:550}.vn-crumb:not(:last-child):after{content:"›";color:var(--text-faint);margin-left:6px}.vn-actions{display:flex;gap:2px}.search-modal{position:fixed;z-index:60;inset:0}.search-modal .scrim{position:absolute;z-index:0;background:#00000073;inset:0}.search-modal-panel{z-index:1;position:absolute;background:var(--bg-elevated);border:1px solid var(--border-strong);box-shadow:var(--shadow);overflow:hidden;display:flex;border-radius:12px;width:min(680px,94vw);max-height:72vh;top:12vh;left:50%;transform:translate(-50%)}.search-modal-panel .launcher{max-width:none;height:100%;padding:0 12px}.search-modal-panel .launch-box{margin:12px 0 4px}.search-modal-panel .launch-body{max-height:56vh}.ip-uri{display:flex;border:1px solid var(--border);background:var(--bg-elevated);color:var(--text-dim);font:11px/1.4 var(--font-mono,ui-monospace,monospace);text-align:left;cursor:pointer;overflow:hidden;border-radius:6px;align-items: center;gap:6px;width:100%;min-width:0;padding:6px 8px}.ip-uri>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.ip-uri:hover{color:var(--text);border-color:var(--accent)}.ip-backlinks{display:flex;flex-direction:column;gap:4px}.ip-backlink{display:flex;color:var(--text-dim);text-align:left;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:1px solid #0000;border-radius:6px;align-items: center;gap:6px;width:100%;padding:5px 8px;font-size:12px}.ip-backlink:hover{background:var(--bg-elevated);color:var(--text)}.ip-muted{color:var(--text-faint);padding:2px 0;font-size:12px}.ip-muted.error{color:var(--danger,#e5534b)}.viewer.markdown{overflow:auto;padding:32px 40px}.md{color:var(--text);max-width:760px;margin:0 auto;font-size:14px;line-height:1.65}.md-h{margin:1.6em 0 .6em;font-weight:650;line-height:1.3}.md-h:first-child{margin-top:0}.md-h1{font-size:1.7em}.md-h2{font-size:1.35em}.md-h3{font-size:1.12em}.md-h4{font-size:1em}.md-h5{color:var(--text-dim);font-size:.92em}.md-h6{color:var(--text-dim);text-transform:uppercase;letter-spacing:.04em;font-size:.88em}.md-p{margin:0 0 1em}.md-list{margin:0 0 1em;padding-left:1.4em}.md-li{margin:.25em 0}.md-quote{border-left:3px solid var(--border);color:var(--text-dim);margin:0 0 1em;padding:.1em 0 .1em 1em}.md-hr{border:none;border-top:1px solid var(--border);margin:1.8em 0}.md-code{overflow-x:auto;background:var(--bg-elevated);border:1px solid var(--border);font:12px/1.55 var(--font-mono,ui-monospace,monospace);border-radius:8px;margin:0 0 1em;padding:12px 14px}.md-inline-code{background:var(--bg-elevated);font:.9em var(--font-mono,ui-monospace,monospace);border-radius:4px;padding:.12em .35em}.md-link{color:var(--accent);text-decoration:none;border-bottom:1px solid #0000}.md-link:hover{border-bottom-color:currentColor}.md-trove{border-bottom:1px dashed color-mix(in srgb,var(--accent)55%,transparent)}.md-deadlink{color:var(--text-faint);text-decoration:line-through;cursor:not-allowed}.md-truncated{border:1px dashed var(--border);color:var(--text-faint);border-radius:8px;margin:1.5em 0 0;padding:10px 12px;font-size:12.5px}.activity-panel{position:fixed;right:12px;bottom:calc(var(--statusbar-h) + 10px);z-index:60;overflow-y:auto;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:var(--radius);box-shadow:var(--shadow);display:flex;flex-direction:column;width:380px;max-height:62vh}.act-head{display:flex;border-bottom:1px solid var(--border);justify-content:space-between;align-items: center;padding:10px 12px}.act-head h3{margin:0;font-size:13px;font-weight:600}.act-section{border-bottom:1px solid var(--border);padding:10px 12px}.act-section:last-of-type{border-bottom:none}.act-section-title{text-transform:uppercase;letter-spacing:.06em;color:var(--text-faint);margin-bottom:8px;font-size:10.5px}.act-empty{color:var(--text-faint);margin:0;font-size:12.5px}.act-row{display:flex;align-items: flex-start;gap:8px}.act-body{flex:1;min-width:0}.act-title{overflow-wrap:anywhere;font-size:12.5px}.act-detail,.act-meta{color:var(--text-dim);overflow-wrap:anywhere;margin-top:2px;font-size:11.5px}.act-meta{color:var(--text-faint)}.act-error{color:var(--danger);overflow-wrap:anywhere;margin-top:2px;font-size:11.5px}.act-amount{color:var(--text-faint);margin-top:3px;font-size:11px}.act-action{color:var(--text-faint);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:2px}.act-action:hover{background:var(--bg-hover);color:var(--text)}.act-task,.act-issue{padding:7px 0}.act-task+.act-task,.act-issue+.act-issue{border-top:1px solid var(--border)}.act-actions{display:flex;gap:6px;margin:6px 0 0 21px}.act-done .act-title,.act-cancelled .act-title{color:var(--text-dim)}.act-failed .act-title{color:var(--danger)}.act-sev-warning .act-title{color:var(--warn)}.act-offline{display:flex;color:var(--warn);background:var(--bg-hover);align-items: center;gap:6px;padding:8px 12px;font-size:11.5px}.act-foot{padding:10px 12px}.act-bar{background:var(--bg-input);overflow:hidden;border-radius:2px;height:3px;margin:6px 0 0 21px}.act-fill{background:var(--accent);transition:width .25s var(--ease);border-radius:2px;height:100%}.act-bar.indeterminate .act-fill{animation:act-slide 1.1s var(--ease)infinite;width:35%}@keyframes act-slide{0%{margin-left:-35%}to{margin-left:100%}}.sb-attention,.sb-attention:hover{color:var(--warn)}.sb-usage{gap:6px}.usage-bar{background:var(--bg-input);overflow:hidden;border-radius:2px;width:42px;height:4px}.usage-fill{background:var(--text-dim);transition:width .3s var(--ease);border-radius:2px;height:100%}.sb-usage.low .usage-fill{background:var(--warn)}.sb-usage.low{color:var(--warn)}.sb-usage.critical .usage-fill{background:var(--danger)}.sb-usage.critical{color:var(--danger)}:root{--phonebar-h:46px;--phonetabs-h:56px}.shell.phone{grid-template-rows:calc(var(--phonebar-h) + env(safe-area-inset-top))1fr calc(var(--phonetabs-h) + env(safe-area-inset-bottom))}.shell.phone .body{grid-template-columns:1fr}.phonebar{display:flex;background:var(--bg-activity);color:var(--text-dim);align-items: center}.phonebar.top{padding:env(safe-area-inset-top)6px 0;border-bottom:1px solid var(--border);gap:8px}.pb-brand{display:grid;background:0 0;border:0;border-radius:9px;flex:none;place-items: center;width:34px;height:34px}.pb-brand img{width:21px;height:21px}.pb-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text);flex:1;min-width:0;font-size:15px;font-weight:600}.pb-status{position:relative;display:grid;color:var(--text-dim);background:0 0;border:0;border-radius:10px;flex:none;place-items: center;width:38px;height:38px}.pb-status:active{background:var(--bg-hover)}.pb-status.warn{color:var(--warn)}.pb-status.danger{color:var(--danger)}.pb-badge{position:absolute;background:var(--danger);color:#fff;display:grid;border-radius:8px;place-items: center;min-width:15px;height:15px;padding:0 3px;font-size:9.5px;font-weight:700;top:2px;right:2px}.phonebar.bottom{padding:0 4px env(safe-area-inset-bottom);border-top:1px solid var(--border);background:color-mix(in srgb,var(--accent)6%,var(--bg-activity));justify-content:space-around}.pb-tab{position:relative;height:var(--phonetabs-h);color:var(--text-faint);display:flex;background:0 0;border:0;border-radius:10px;flex-direction:column;flex:1;justify-content:center;align-items: center;gap:3px}.pb-tab .pb-label{font-size:10.5px;font-weight:550}.pb-tab.active{color:var(--accent)}.pb-tab:active{background:var(--bg-hover)}.pb-tab .pb-badge{background:var(--accent);color:var(--accent-text);top:6px;right:22%}.sheet-wrap{position:fixed;z-index:58;inset:0}.sheet-wrap .scrim{position:absolute;z-index:0;background:#00000080;inset:0}.sheet{position:absolute;z-index:41;overflow-y:auto;background:var(--bg-elevated);border-top:1px solid var(--border-strong);box-shadow:var(--shadow);padding-bottom:calc(12px + env(safe-area-inset-bottom));animation:sheet-up .18s var(--ease);border-radius:16px 16px 0 0;max-height:82vh;bottom:0;left:0;right:0}@keyframes sheet-up{0%{transform:translateY(18px);opacity:.4}to{transform:none;opacity:1}}.sheet-grip{display:grid;background:0 0;border:0;place-items: center;width:100%;height:22px}.sheet-grip:before{content:"";background:var(--border-strong);border-radius:2px;width:38px;height:4px}.sheet-body{display:flex;flex-direction:column;padding:2px 10px 6px}.sheet-title{letter-spacing:.04em;text-transform:uppercase;color:var(--text-faint);padding:4px 8px 10px;font-size:12px;font-weight:600}.sheet-row{display:flex;color:var(--text);text-align:left;background:0 0;border:0;border-radius:10px;align-items: center;gap:12px;width:100%;min-height:46px;padding:0 8px;font-size:14.5px}.sheet-row:active{background:var(--bg-hover)}.sheet-row.static{color:var(--text-dim)}.sheet-row.danger{color:var(--danger)}.sheet-row svg{color:var(--text-dim);flex:none}.sheet-row.danger svg{color:var(--danger)}.sheet-row .sr-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.sheet-row .sr-value{color:var(--text-dim);font-size:13px}.sheet-row .sr-go{color:var(--text-faint)}.sheet-row .sb-usage{padding:0}.sheet-actions{display:flex;border-top:1px solid var(--border);gap:8px;margin-top:8px;padding:12px 8px 4px}.sheet-actions .btn{flex:1;justify-content:center}.sheet-me{display:flex;border-bottom:1px solid var(--border);align-items: center;gap:10px;margin-bottom:6px;padding:4px 8px 12px}.sheet-me .avatar-img,.sheet-me .avatar-txt{display:grid;background:linear-gradient(135deg,var(--accent-2),var(--accent));color:#fff;border-radius:50%;flex:none;place-items: center;width:36px;height:36px;font-weight:700}.sheet-me .sm-name{color:var(--text);font-size:14.5px;font-weight:600}.sheet-me .sm-sub{color:var(--text-faint);font-size:12px}.shell.phone .editor-split{grid-template-columns:1fr}.shell.phone .infopanel{border-left:0}.shell.phone .launcher{padding:0 12px}.shell.phone .launch-box{margin-top:14px;padding:10px 12px}.shell.phone .launch-input{font-size:16px}.shell.phone .search-modal-panel{border-radius:0;width:100vw;height:100dvh;max-height:100dvh;top:0}.shell.phone .activity-panel{left:8px;right:8px;bottom:calc(var(--phonetabs-h) + env(safe-area-inset-bottom) + 8px);width:auto;max-height:68vh}.shell.phone .inbox,.shell.phone .toasts,.shell.phone .tray{left:8px;right:8px;bottom:calc(var(--phonetabs-h) + env(safe-area-inset-bottom) + 8px);width:auto}.shell.phone .viewer-dock{left:8px;right:8px;bottom:calc(var(--phonetabs-h) + env(safe-area-inset-bottom) + 8px);width:auto!important}.shell.tv{--activity-w:84px;--statusbar-h:34px;padding:2.2% 2.6%;font-size:17px}.shell.tv .activitybar{gap:10px;padding:16px 0 18px}.shell.tv .activitybar .item,.shell.tv .activitybar .iconbtn,.shell.tv .activitybar .brand-mark{border-radius:14px;width:64px;height:64px}.shell.tv .statusbar{font-size:15px}.shell.tv .launcher{max-width:1100px}.shell.tv .launch-input{font-size:24px}.shell.tv .launch-box{border-radius:16px;padding:16px 22px}.shell.tv .launch-mic{border-radius:11px;width:40px;height:40px}.shell.tv .launch-item{border-radius:12px;padding:14px 16px;font-size:18px}.shell.tv .launch-item .name{max-width:none}.shell.tv .launch-h{font-size:14px}.shell.tv .btn{padding:11px 20px;font-size:16px}.shell.tv .viewer.text pre{padding:28px 34px;font-size:19px;line-height:1.7}.shell.tv .md{max-width:1000px;font-size:21px}.shell.tv .viewer.markdown{padding:40px 52px}.shell.tv .viewer-nav{height:54px;padding:0 14px}.shell.tv .vn-crumb{max-width:420px;padding:8px 12px;font-size:17px}.shell.tv .vn-back{width:44px;height:40px}.shell.tv .settings{max-width:1000px;font-size:17px}.shell.tv .settings .setting .info .d{font-size:15px}.shell.tv .infopanel{font-size:16px}.shell.tv .toasts,.shell.tv .activity-panel{right:2.6%;bottom:calc(var(--statusbar-h) + 2.2%)}.shell.tv .plugin-panel{top:2.2%;right:2.6%}.shell.tv .tray{left:calc(var(--activity-w) + 2.6%);bottom:calc(var(--statusbar-h) + 2.2%)}.shell.tv .inbox{left:calc(var(--activity-w) + 2.6%);bottom:2.2%}.shell.tv .viewer-dock{right:2.6%;bottom:calc(var(--statusbar-h) + 2.2%)}.shell.tv :focus{outline:3px solid var(--accent);outline-offset:3px}.shell.tv :focus-visible{outline:3px solid var(--accent);outline-offset:3px}.shell.tv .launch-item:focus-visible{box-shadow:none}.shell.tv .sheet-row:focus-visible{box-shadow:none}.shell.tv .inbox-item:focus-visible{box-shadow:none}.shell.tv .activitybar .item:focus-visible{box-shadow:none}.shell.tv .activitybar .brand-mark:focus-visible{box-shadow:none}.shell.tv .statusbar .seg:focus-visible{box-shadow:none}.shell.tv .pb-tab:focus-visible{box-shadow:none}.shell.tv .btn:focus-visible{box-shadow:none}.shell.tv .iconbtn:focus-visible{box-shadow:none}.shell.tv .act-action:focus-visible{box-shadow:none}.shell.tv .launch-clear:focus-visible{box-shadow:none}.shell.tv .vn-back:focus-visible{box-shadow:none}.shell.tv .c-link:focus-visible{box-shadow:none}.shell.tv .react:focus-visible{box-shadow:none}.shell.tv .lh-example:focus-visible{box-shadow:none}.shell.tv .kbd-edit:focus-visible{box-shadow:none}.shell.tv .activitybar .item:focus-visible:before{content:none}.shell.tv .launch-input:focus{outline:none}.shell.tv .launch-input:focus-visible{outline:none}.shell.tv .launch-box:focus-within{outline:3px solid var(--accent);outline-offset:3px;border-color:var(--border-strong);box-shadow:var(--shadow)}.shell.tv .launch-item:focus,.shell.tv .sheet-row:focus{background:var(--bg-selected)}.shell.tv .launch-item.active{outline:3px solid var(--accent);outline-offset:3px;background:var(--bg-selected);box-shadow:none}.shell.tv .launch-body:focus-within .launch-item.active:not(:focus){outline:none;background:var(--bg-hover)}.shell.tv .launch-body{padding-left:8px;padding-right:8px}.launch-help{border-top:1px solid var(--border);margin:6px 10px 0;padding:12px 0 0}.lh-hint{display:flex;color:var(--text-dim);align-items: flex-start;gap:8px;font-size:12.5px;line-height:1.55}.lh-hint svg{color:var(--text-faint);flex:none;margin-top:2px}.lh-examples{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.lh-example{display:inline-flex;border:1px solid var(--border);background:var(--bg-hover);color:var(--text);border-radius:8px;align-items:baseline;gap:7px;padding:5px 10px;font-size:12.5px}.lh-example:hover{border-color:var(--accent)}.lh-example .lh-q{font-family:var(--font-mono);font-size:12px}.lh-example .lh-label{color:var(--text-faint);font-size:11.5px}.mcp-url{display:flex;align-items: center;gap:8px;min-width:0}.mcp-url .mono{font-family:var(--font-mono);color:var(--text);background:var(--bg-input);border:1px solid var(--border);overflow-x:auto;white-space:nowrap;border-radius:6px;max-width:100%;padding:5px 9px;font-size:12.5px}.mcp-url .mono.small{color:var(--text-dim);font-size:11.5px}.settings .control .mono{font-family:var(--font-mono);color:var(--text-dim);font-size:12.5px}.mcp-warn{display:flex;color:var(--warn);background:color-mix(in srgb,var(--warn)10%,transparent);border:1px solid color-mix(in srgb,var(--warn)34%,transparent);border-radius:9px;align-items: flex-start;gap:9px;margin:4px 0 10px;padding:10px 12px;font-size:12.5px;line-height:1.55}.mcp-warn svg{flex:none;margin-top:1px}.shell.phone .settings{padding:18px 14px 40px}.shell.phone .setting{flex-direction:column;align-items:stretch;gap:8px}.shell.phone .setting .control{justify-content:stretch;width:100%}.shell.phone .setting .control>*{flex:1}.shell.phone .setting .control .switch,.shell.phone .setting .control .iconbtn,.shell.phone .mcp-url .btn{flex:none}.shell.phone .setting .control .switch{align-self: flex-start}.shell.phone .mcp-url .mono{flex:1;min-width:0}.shell.phone .setting:has(.control>.iconbtn:only-child){flex-direction:row;align-items: center}.shell.phone .setting:has(.control>.iconbtn:only-child) .control{justify-content:flex-end;width:auto}.launch-h .lh-title{display:flex;align-items:baseline;gap:8px;min-width:0}.launch-h .lh-actions{display:flex;flex:none;align-items: center;gap:2px}.launch-h .lh-verbatim{text-transform:none;color:var(--text-dim);letter-spacing:0;font-weight:500}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#6ea8fe"/>
|
|
5
|
+
<stop offset="1" stop-color="#a679f0"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="32" height="32" rx="7" fill="#181a1f"/>
|
|
9
|
+
<path d="M6 11a2 2 0 0 1 2-2h5l2 2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2z" fill="url(#g)"/>
|
|
10
|
+
<circle cx="16" cy="17" r="3.2" fill="#181a1f"/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
6
|
+
<meta name="theme-color" content="#181a1f" />
|
|
7
|
+
<title>Trove</title>
|
|
8
|
+
<link rel="icon" href="/icon.svg" />
|
|
9
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
10
|
+
<link rel="stylesheet" href="/assets/styles-kcx1x337.css" />
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div class="workbench"></div>
|
|
14
|
+
<script type="module" src="/assets/main-4cxs7prw.js"></script>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// Trove service worker — offline support + web-push. Three caches:
|
|
2
|
+
// trove-shell : the app shell (HTML/JS/CSS/icons) — so the whole workbench,
|
|
3
|
+
// including every BUILT-IN media player & previewer (they're in
|
|
4
|
+
// the bundle), works offline. Stale-while-revalidate.
|
|
5
|
+
// trove-files : bytes of files the user "made available offline" (pinned).
|
|
6
|
+
// Cache-first, so openers read them with no network.
|
|
7
|
+
// trove-api : GET /api responses (listings, sidecars…) seen while online,
|
|
8
|
+
// network-first with a cache fallback for read-only offline use.
|
|
9
|
+
// Writes offline aren't proxied here — the client queues sidecar changes itself
|
|
10
|
+
// and replays them (CRDT-merge) on reconnect. On push (bodyless) we pull the
|
|
11
|
+
// inbox and notify.
|
|
12
|
+
|
|
13
|
+
// The build stamps SHELL with a hash of the emitted assets (see build.mjs), so a deploy
|
|
14
|
+
// gives the new worker a new shell cache and `activate` below retires the old one. It
|
|
15
|
+
// was `trove-shell-v1` and never changed, which made that sweep a no-op — two builds
|
|
16
|
+
// shared one cache and whichever entry landed first won.
|
|
17
|
+
//
|
|
18
|
+
// API and FILES deliberately do NOT rotate. API is data, not code, and FILES holds the
|
|
19
|
+
// bytes of files the user pinned for offline use — naming it per build would throw away
|
|
20
|
+
// someone's offline library every time the app was redeployed.
|
|
21
|
+
const SHELL = 'trove-shell-7f6efdf273';
|
|
22
|
+
const API = 'trove-api-v1';
|
|
23
|
+
const FILES = 'trove-files-v1';
|
|
24
|
+
const KEEP = new Set([SHELL, API, FILES]);
|
|
25
|
+
|
|
26
|
+
self.addEventListener('install', (e) => {
|
|
27
|
+
e.waitUntil(caches.open(SHELL).then((c) => c.addAll(['/', '/index.html', '/icon.svg']).catch(() => {})).then(() => self.skipWaiting()));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
self.addEventListener('activate', (e) => {
|
|
31
|
+
e.waitUntil(
|
|
32
|
+
(async () => {
|
|
33
|
+
const names = await caches.keys();
|
|
34
|
+
await Promise.all(names.filter((n) => !KEEP.has(n)).map((n) => caches.delete(n)));
|
|
35
|
+
await self.clients.claim();
|
|
36
|
+
})(),
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
self.addEventListener('fetch', (event) => {
|
|
41
|
+
const req = event.request;
|
|
42
|
+
const url = new URL(req.url);
|
|
43
|
+
if (url.origin !== self.location.origin || req.method !== 'GET') return; // only same-origin GETs
|
|
44
|
+
|
|
45
|
+
if (url.pathname === '/api/items/download') {
|
|
46
|
+
event.respondWith(pinnedFirst(req));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (url.pathname.startsWith('/api/')) {
|
|
50
|
+
event.respondWith(networkFirst(req));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
event.respondWith(staleWhileRevalidate(req));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Pinned files live in trove-files (keyed by the full download URL). If present,
|
|
57
|
+
// serve from cache (works offline and avoids re-downloading); else go to network.
|
|
58
|
+
async function pinnedFirst(req) {
|
|
59
|
+
const cache = await caches.open(FILES);
|
|
60
|
+
const hit = await cache.match(req.url, { ignoreVary: true });
|
|
61
|
+
if (hit) return sliceForRange(hit, req.headers.get('range'));
|
|
62
|
+
try {
|
|
63
|
+
return await fetch(req);
|
|
64
|
+
} catch {
|
|
65
|
+
return new Response('Offline and not available offline', { status: 504 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Serve the range the caller actually asked for.
|
|
71
|
+
*
|
|
72
|
+
* The cache is keyed by URL alone, so a ranged request matched the full 200 response and
|
|
73
|
+
* got the WHOLE file back. The MP4 parser walks a container with 16-byte range reads and
|
|
74
|
+
* re-read offset 0 every time — 64 fetches, no chapters, no cover — and each of those
|
|
75
|
+
* materialised the entire body, so a pinned 600 MB audiobook read tens of gigabytes out
|
|
76
|
+
* of CacheStorage behind a spinner. `readTextCapped` lost its 512 KB cap the same way.
|
|
77
|
+
*/
|
|
78
|
+
async function sliceForRange(res, header) {
|
|
79
|
+
if (!header) return res;
|
|
80
|
+
const m = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
|
|
81
|
+
if (!m) return res;
|
|
82
|
+
const buf = new Uint8Array(await res.clone().arrayBuffer());
|
|
83
|
+
const total = buf.byteLength;
|
|
84
|
+
let start;
|
|
85
|
+
let end;
|
|
86
|
+
if (m[1] === '') {
|
|
87
|
+
const n = Math.min(Number(m[2] || 0), total);
|
|
88
|
+
if (!(n > 0)) return new Response(null, { status: 416, headers: { 'content-range': `bytes */${total}` } });
|
|
89
|
+
start = total - n;
|
|
90
|
+
end = total - 1;
|
|
91
|
+
} else {
|
|
92
|
+
start = Number(m[1]);
|
|
93
|
+
end = m[2] === '' ? total - 1 : Math.min(Number(m[2]), total - 1);
|
|
94
|
+
}
|
|
95
|
+
if (!(start >= 0) || start > end || start >= total) {
|
|
96
|
+
return new Response(null, { status: 416, headers: { 'content-range': `bytes */${total}` } });
|
|
97
|
+
}
|
|
98
|
+
const headers = new Headers(res.headers);
|
|
99
|
+
headers.set('content-range', `bytes ${start}-${end}/${total}`);
|
|
100
|
+
headers.set('content-length', String(end - start + 1));
|
|
101
|
+
headers.set('accept-ranges', 'bytes');
|
|
102
|
+
return new Response(buf.subarray(start, end + 1), { status: 206, statusText: 'Partial Content', headers });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function networkFirst(req) {
|
|
106
|
+
const cache = await caches.open(API);
|
|
107
|
+
try {
|
|
108
|
+
const res = await fetch(req);
|
|
109
|
+
if (res.ok) cache.put(req, res.clone());
|
|
110
|
+
return res;
|
|
111
|
+
} catch {
|
|
112
|
+
const hit = await cache.match(req);
|
|
113
|
+
if (hit) return hit;
|
|
114
|
+
return new Response(JSON.stringify({ error: { code: 'offline', message: 'Offline — this data isn’t cached.' } }), {
|
|
115
|
+
status: 503, headers: { 'content-type': 'application/json' },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// What a request asked to BE, against what came back. The server no longer answers a
|
|
121
|
+
// miss under /assets/ with index.html, which is what used to produce this — but the
|
|
122
|
+
// consequence was bad enough to be worth refusing here as well. A 200 whose body is
|
|
123
|
+
// HTML, stored under a .js URL, is a module that fails to link on every load from then
|
|
124
|
+
// on; and because a cache hit is served without ever asking the network again, it does
|
|
125
|
+
// not heal. Nothing else in this worker is load-bearing enough to leave that to one
|
|
126
|
+
// check in another process.
|
|
127
|
+
const EXPECTED_TYPE = {
|
|
128
|
+
script: /javascript|ecmascript/i,
|
|
129
|
+
style: /text\/css/i,
|
|
130
|
+
font: /font/i,
|
|
131
|
+
image: /^image\//i,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
function typeMatches(req, res) {
|
|
135
|
+
const want = EXPECTED_TYPE[req.destination];
|
|
136
|
+
if (!want) return true; // documents, and anything the browser has no opinion about
|
|
137
|
+
return want.test(res.headers.get('content-type') || '');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function staleWhileRevalidate(req) {
|
|
141
|
+
const cache = await caches.open(SHELL);
|
|
142
|
+
const cached = await cache.match(req, { ignoreSearch: true });
|
|
143
|
+
const network = fetch(req).then((res) => {
|
|
144
|
+
// Let a mismatch through — the browser's own error is clearer than anything we
|
|
145
|
+
// could synthesise — but never keep it.
|
|
146
|
+
if (res.ok && typeMatches(req, res)) cache.put(req, res.clone());
|
|
147
|
+
return res;
|
|
148
|
+
}).catch(() => null);
|
|
149
|
+
return cached || (await network) || new Response('Offline', { status: 504 });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// --- web push (bodyless → pull inbox) --------------------------------------
|
|
153
|
+
self.addEventListener('push', (event) => {
|
|
154
|
+
event.waitUntil(
|
|
155
|
+
(async () => {
|
|
156
|
+
let title = 'New activity in Trove';
|
|
157
|
+
let body = '';
|
|
158
|
+
try {
|
|
159
|
+
const res = await fetch('/api/notifications', { credentials: 'include' });
|
|
160
|
+
if (res.ok) {
|
|
161
|
+
const inbox = await res.json();
|
|
162
|
+
const latest = inbox.items?.find((n) => !n.read) || inbox.items?.[0];
|
|
163
|
+
if (latest) {
|
|
164
|
+
title = latest.title || title;
|
|
165
|
+
body = latest.items?.[0]?.excerpt ? `“${latest.items[0].excerpt}”` : '';
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch { /* offline */ }
|
|
169
|
+
await self.registration.showNotification(title, { body, icon: '/icon.svg', badge: '/icon.svg', tag: 'trove-mentions', renotify: true });
|
|
170
|
+
const clients = await self.clients.matchAll({ type: 'window' });
|
|
171
|
+
for (const c of clients) c.postMessage({ type: 'trove-push' });
|
|
172
|
+
})(),
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
self.addEventListener('notificationclick', (event) => {
|
|
177
|
+
event.notification.close();
|
|
178
|
+
event.waitUntil(
|
|
179
|
+
(async () => {
|
|
180
|
+
const clients = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
|
|
181
|
+
const existing = clients.find((c) => 'focus' in c);
|
|
182
|
+
if (existing) return existing.focus();
|
|
183
|
+
return self.clients.openWindow('/');
|
|
184
|
+
})(),
|
|
185
|
+
);
|
|
186
|
+
});
|