dsh-coding-sidebar 1.0.3 → 1.0.4

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.
Files changed (89) hide show
  1. package/README.md +27 -42
  2. package/lib/client-editor.js +297 -4179
  3. package/lib/client-registry.js +639 -1341
  4. package/lib/client-terminal.js +176 -266
  5. package/lib/client.js +640 -1342
  6. package/lib/index.js +4 -110
  7. package/lib/types/bundle-route.d.ts +1 -1
  8. package/lib/types/client/SideCardSection.d.ts +3 -3
  9. package/lib/types/client/TextEditor.d.ts +20 -10
  10. package/lib/types/client/add-plugin-modal.d.ts +0 -4
  11. package/lib/types/client/api.d.ts +8 -11
  12. package/lib/types/client/builtins/index.d.ts +10 -10
  13. package/lib/types/client/chunk-loader.d.ts +7 -8
  14. package/lib/types/client/locales.d.ts +0 -26
  15. package/lib/types/client/native-avoid.d.ts +2 -0
  16. package/lib/types/client/plugins-shared.d.ts +6 -6
  17. package/lib/types/client/service.d.ts +13 -83
  18. package/lib/types/index.d.ts +1 -1
  19. package/lib/types/prefs-shared.d.ts +1 -24
  20. package/package.json +26 -29
  21. package/src/bundle-route.ts +1 -1
  22. package/src/client/EditorHost.tsx +52 -122
  23. package/src/client/SandboxStatusBar.tsx +12 -9
  24. package/src/client/SideCardSection.tsx +28 -81
  25. package/src/client/TextEditor.tsx +89 -266
  26. package/src/client/add-plugin-modal.tsx +16 -24
  27. package/src/client/api.ts +1 -25
  28. package/src/client/builtins/index.ts +10 -14
  29. package/src/client/chunk-loader.ts +7 -8
  30. package/src/client/index.tsx +12 -3
  31. package/src/client/locales-ar.ts +0 -6
  32. package/src/client/locales-de.ts +0 -6
  33. package/src/client/locales-fr.ts +0 -6
  34. package/src/client/locales-hi.ts +0 -6
  35. package/src/client/locales-id.ts +0 -6
  36. package/src/client/locales-it.ts +0 -6
  37. package/src/client/locales-ja.ts +0 -6
  38. package/src/client/locales-ko.ts +0 -6
  39. package/src/client/locales-nl.ts +0 -6
  40. package/src/client/locales-pl.ts +0 -6
  41. package/src/client/locales-pt.ts +0 -6
  42. package/src/client/locales-ru.ts +0 -6
  43. package/src/client/locales-sv.ts +0 -6
  44. package/src/client/locales-th.ts +0 -6
  45. package/src/client/locales-tr.ts +0 -6
  46. package/src/client/locales-vi.ts +0 -6
  47. package/src/client/locales-zh-HK.ts +0 -6
  48. package/src/client/locales-zh-MO.ts +0 -6
  49. package/src/client/locales-zh-TW.ts +0 -6
  50. package/src/client/locales.ts +0 -52
  51. package/src/client/native-avoid.ts +155 -0
  52. package/src/client/plugins-shared.ts +6 -6
  53. package/src/client/prefs.ts +0 -7
  54. package/src/client/service.ts +13 -150
  55. package/src/client/sidebar.module.css +1 -425
  56. package/src/config.ts +1 -4
  57. package/src/index.ts +7 -74
  58. package/src/prefs-shared.ts +1 -27
  59. package/lib/client-mermaid.js +0 -200776
  60. package/lib/types/client/MarkdownHtml.d.ts +0 -32
  61. package/lib/types/client/PdfView.d.ts +0 -6
  62. package/lib/types/client/builtins/viewers.d.ts +0 -3
  63. package/lib/types/client/chunks/mermaid.d.ts +0 -10
  64. package/lib/types/client/editor-load.d.ts +0 -66
  65. package/lib/types/client/image-types.d.ts +0 -3
  66. package/lib/types/client/markdown-html.d.ts +0 -96
  67. package/lib/types/client/markdown-images.d.ts +0 -45
  68. package/lib/types/client/md-toc.d.ts +0 -4
  69. package/lib/types/client/mermaid-blocks.d.ts +0 -47
  70. package/lib/types/client/mermaid-sanitize.d.ts +0 -2
  71. package/lib/types/client/mermaid.d.ts +0 -12
  72. package/lib/types/client/pdf-types.d.ts +0 -2
  73. package/lib/types/client/plugins-viewers.d.ts +0 -3
  74. package/lib/types/html-route.d.ts +0 -59
  75. package/src/client/MarkdownHtml.tsx +0 -296
  76. package/src/client/PdfView.tsx +0 -110
  77. package/src/client/builtins/viewers.tsx +0 -125
  78. package/src/client/chunks/mermaid.tsx +0 -10
  79. package/src/client/editor-load.ts +0 -92
  80. package/src/client/image-types.ts +0 -8
  81. package/src/client/markdown-html.ts +0 -331
  82. package/src/client/markdown-images.ts +0 -137
  83. package/src/client/md-toc.tsx +0 -127
  84. package/src/client/mermaid-blocks.ts +0 -110
  85. package/src/client/mermaid-sanitize.ts +0 -93
  86. package/src/client/mermaid.tsx +0 -406
  87. package/src/client/pdf-types.ts +0 -4
  88. package/src/client/plugins-viewers.ts +0 -30
  89. package/src/html-route.ts +0 -106
package/lib/index.js CHANGED
@@ -42,15 +42,12 @@ const SIDEBAR_PREFS_DEFAULTS = {
42
42
  customCss: "",
43
43
  titleBarCompat: false,
44
44
  titleBarStripPx: 40,
45
- htmlViewerNoSandbox: false,
46
- htmlViewerDefaultUnsafe: false,
47
45
  browserNoSandbox: false,
48
46
  browserInterceptLinks: true,
49
47
  browserInterceptHttp: true,
50
48
  browserInterceptHttps: false,
51
49
  browserAllowedLoopback: "",
52
50
  tabsEnabled: {},
53
- viewersEnabled: {},
54
51
  pluginSettings: {}
55
52
  };
56
53
  //#endregion
@@ -114,15 +111,12 @@ const PrefsSchema = z.object({
114
111
  customCss: z.string(),
115
112
  titleBarCompat: z.boolean().default(false),
116
113
  titleBarStripPx: z.number().step(1).min(0).max(120).default(40),
117
- htmlViewerNoSandbox: z.boolean().default(false),
118
- htmlViewerDefaultUnsafe: z.boolean().default(false),
119
114
  browserNoSandbox: z.boolean().default(false),
120
115
  browserInterceptLinks: z.boolean().default(true),
121
116
  browserInterceptHttp: z.boolean().default(true),
122
117
  browserInterceptHttps: z.boolean().default(false),
123
118
  browserAllowedLoopback: z.string().default(""),
124
119
  tabsEnabled: z.dict(z.boolean()).default({}),
125
- viewersEnabled: z.dict(z.boolean()).default({}),
126
120
  pluginSettings: z.dict(z.dict(z.any())).default({})
127
121
  });
128
122
  //#endregion
@@ -541,60 +535,6 @@ async function searchFiles(root, query, opts = {}) {
541
535
  truncated
542
536
  };
543
537
  }
544
- /**
545
- * Decode a route pathname into the session + absolute file path. Rejects
546
- * a wrong prefix (404), an empty path, malformed percent encoding, and a
547
- * missing sessionId or file path (400). The caller still must bound the
548
- * decoded path with the workspace real-path guard — a decoded `..`
549
- * segment resolves outside the cwd and is refused there.
550
- */
551
- function decodeHtmlUrl(pathname) {
552
- if (!pathname.startsWith("/sidebar/html/")) return {
553
- ok: false,
554
- status: 404,
555
- message: "not an html route"
556
- };
557
- const rest = pathname.slice(14);
558
- if (rest === "") return {
559
- ok: false,
560
- status: 400,
561
- message: "invalid html route path"
562
- };
563
- let segments;
564
- try {
565
- segments = rest.split("/").map((segment) => decodeURIComponent(segment));
566
- } catch {
567
- return {
568
- ok: false,
569
- status: 400,
570
- message: "malformed URL encoding"
571
- };
572
- }
573
- const [sessionId, ...pathSegments] = segments;
574
- if (sessionId === void 0 || sessionId === "") return {
575
- ok: false,
576
- status: 400,
577
- message: "sessionId and file path are required"
578
- };
579
- const unc = pathSegments[0] === "";
580
- const tail = unc ? pathSegments.slice(1) : pathSegments;
581
- if (tail.length === 0 || tail.some((segment) => segment === "")) return {
582
- ok: false,
583
- status: 400,
584
- message: "sessionId and file path are required"
585
- };
586
- let path;
587
- if (unc) path = `//${tail.join("/")}`;
588
- else if (/^[A-Za-z]:$/.test(tail[0] ?? "")) path = tail.join("/");
589
- else path = `/${tail.join("/")}`;
590
- return {
591
- ok: true,
592
- ref: {
593
- sessionId,
594
- path
595
- }
596
- };
597
- }
598
538
  //#endregion
599
539
  //#region src/browser-probe.ts
600
540
  /**
@@ -692,11 +632,7 @@ function isTrustedApiRequest(request, trustedHosts) {
692
632
  * only allowlisted chunk names are servable (no path traversal).
693
633
  */
694
634
  /** The chunk names the client may request (mirror of src/client/chunk-loader.ts). */
695
- const CHUNK_NAMES = [
696
- "terminal",
697
- "editor",
698
- "mermaid"
699
- ];
635
+ const CHUNK_NAMES = ["terminal", "editor"];
700
636
  /** Directory of this host-half module (lib/ — the chunk scripts live next to it). */
701
637
  const LIB_DIR = dirname(fileURLToPath(import.meta.url));
702
638
  /** sha1 content hash shortened to 12 hex chars (same shape as the client-modules rev). */
@@ -3689,8 +3625,8 @@ function buildSidechatApi(ctx) {
3689
3625
  //#region src/index.ts
3690
3626
  /**
3691
3627
  * dsh-coding-sidebar host half: the /sidebar JSON API (explorer listing, file
3692
- * read/write, git), the /sidebar/file media route (images), the /sidebar/html
3693
- * preview route, the /sidebar/bundle lazy-chunk route (client code splits),
3628
+ * read/write, git), the /sidebar/file media route (file bytes / downloads),
3629
+ * the /sidebar/bundle lazy-chunk route (client code splits),
3694
3630
  * and the terminal WebSocket upgrade. Every route passes the same
3695
3631
  * browser-trust fence as the /api gateway — Host-header loopback or the
3696
3632
  * web runtime's `trustedHosts` (LAN IP literals sampled at boot plus
@@ -3721,10 +3657,7 @@ const MEDIA_TYPES = {
3721
3657
  ".svg": "image/svg+xml",
3722
3658
  ".bmp": "image/bmp",
3723
3659
  ".ico": "image/x-icon",
3724
- ".avif": "image/avif",
3725
- ".pdf": "application/pdf",
3726
- ".html": "text/html",
3727
- ".htm": "text/html"
3660
+ ".avif": "image/avif"
3728
3661
  };
3729
3662
  /** Content type served by /sidebar/file (binary-safe fallback for unknowns). */
3730
3663
  function mediaTypeForPath(path) {
@@ -4302,45 +4235,6 @@ function apply(ctx, config) {
4302
4235
  }
4303
4236
  }
4304
4237
  }), "dsh-coding-sidebar: /sidebar/file media route");
4305
- ctx.effect(() => ctx.webServer.register({
4306
- kind: "prefix",
4307
- path: "/sidebar/html",
4308
- handler: async (req, res) => {
4309
- if (!fence(req)) {
4310
- res.writeHead(403);
4311
- res.end("forbidden");
4312
- return;
4313
- }
4314
- if (req.method !== "GET") {
4315
- res.writeHead(405);
4316
- res.end();
4317
- return;
4318
- }
4319
- try {
4320
- const decoded = decodeHtmlUrl(new URL(req.url ?? "/", "http://dsh.internal").pathname);
4321
- if (!decoded.ok) {
4322
- writeError(res, new SidebarError("bad-request", decoded.message, decoded.status));
4323
- return;
4324
- }
4325
- const { sessionId, path } = decoded.ref;
4326
- const absolute = await ensureWorkspacePath(await sessionCwdOf(ctx, sessionId), path);
4327
- const info = await stat(absolute);
4328
- if (!info.isFile() || info.size > resolved.mediaLimit) throw new SidebarError("fs-error", "not a file or too large", 400);
4329
- const type = mediaTypeForPath(absolute);
4330
- const body = await readFile(absolute);
4331
- res.writeHead(200, {
4332
- "content-type": type === "text/html" ? "text/html; charset=utf-8" : type,
4333
- "cache-control": "no-cache",
4334
- "x-content-type-options": "nosniff",
4335
- "referrer-policy": "no-referrer",
4336
- "content-security-policy": "sandbox allow-scripts allow-popups allow-downloads allow-modals; object-src 'none'"
4337
- });
4338
- res.end(body);
4339
- } catch (error) {
4340
- writeError(res, error);
4341
- }
4342
- }
4343
- }), "dsh-coding-sidebar: /sidebar/html preview route");
4344
4238
  const wss = new WebSocketServer({ noServer: true });
4345
4239
  ctx.effect(() => ctx.webServer.registerUpgrade({
4346
4240
  path: "/sidebar/ws/terminal",
@@ -1,6 +1,6 @@
1
1
  import type { Context, SidebarHttpRequest, SidebarHttpResponse } from './context-types.ts';
2
2
  /** The chunk names the client may request (mirror of src/client/chunk-loader.ts). */
3
- export declare const CHUNK_NAMES: readonly ["terminal", "editor", "mermaid"];
3
+ export declare const CHUNK_NAMES: readonly ["terminal", "editor"];
4
4
  export type ChunkName = (typeof CHUNK_NAMES)[number];
5
5
  /**
6
6
  * Build the /sidebar/bundle route handler. `fence` is the shared browser-
@@ -1,7 +1,7 @@
1
1
  import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
2
  import { type SidebarPrefs } from '../prefs-shared.ts';
3
3
  import type { SidebarStore } from './state.ts';
4
- import type { BetterSidebarService, FileViewerDescriptor, SidebarSettingToggle, TabDescriptor } from './service.ts';
4
+ import type { BetterSidebarService, SidebarSettingToggle, TabDescriptor } from './service.ts';
5
5
  /** Injected business face: the shared store (prefs cache) + the sidebar service (registries). */
6
6
  export interface SideCardSectionInjected {
7
7
  store: SidebarStore;
@@ -43,7 +43,7 @@ export declare function FeatureSettingsRows(props: {
43
43
  valueSource?: (key: string) => unknown;
44
44
  }): import("react").JSX.Element;
45
45
  /**
46
- * The secondary settings popup body of one feature (tab or viewer):
46
+ * The secondary settings popup body of one registered tab:
47
47
  * - the host-prefs `toggles` rows, then the plugin-owned `pluginToggles`
48
48
  * rows (their values live in `pluginSettings[feature.id]`, projected onto
49
49
  * the prefs face so the shared row renderer reads them);
@@ -53,7 +53,7 @@ export declare function FeatureSettingsRows(props: {
53
53
  * open-behavior picker) and still ship a custom configuration area.
54
54
  */
55
55
  export declare function SettingsBody(props: {
56
- feature: TabDescriptor | FileViewerDescriptor;
56
+ feature: TabDescriptor;
57
57
  prefs: SidebarPrefs;
58
58
  store: SidebarStore;
59
59
  service: BetterSidebarService;
@@ -1,10 +1,20 @@
1
- import type { FileViewerProps } from './service.ts';
2
- /**
3
- * The sandbox tokens of the HTML preview iframe. NO allow-same-origin (the
4
- * preview must stay in an opaque origin — with the route's own origin it
5
- * could read session data) and NO allow-top-navigation (a previewed page
6
- * must not hijack the GUI). The user can disable the sandbox per-feature
7
- * in the side card settings (warned); the toggle below reflects it.
8
- */
9
- export declare const HTML_IFRAME_SANDBOX = "allow-scripts allow-popups allow-downloads allow-modals";
10
- export declare function TextEditor(props: FileViewerProps): import("react").JSX.Element;
1
+ import type { EditorToolbarControls, EditorToolbarState } from './service.ts';
2
+ import type { Context } from '../context-types.ts';
3
+ import type { SessionScope } from './api.ts';
4
+ import type { SidebarStore } from './state.ts';
5
+ /** Props of the sidebar text editor (the editor tab's content). */
6
+ export interface TextEditorProps {
7
+ ctx: Context;
8
+ store?: SidebarStore;
9
+ scope: SessionScope;
10
+ path: string;
11
+ /** fs.read text content (undefined while loading / for non-editable reads). */
12
+ content?: string;
13
+ truncated?: boolean;
14
+ /** 'host' skips the own toolbar row — the editor host's merged-mode header
15
+ * renders it instead, fed through the two callbacks below. */
16
+ toolbar?: 'self' | 'host';
17
+ onToolbarState?: (state: EditorToolbarState) => void;
18
+ onToolbarControls?: (controls: EditorToolbarControls | null) => void;
19
+ }
20
+ export declare function TextEditor(props: TextEditorProps): import("react").JSX.Element;
@@ -1,15 +1,11 @@
1
1
  import type { BetterSidebarService } from './service.ts';
2
- /** Which extension point the modal is adding a plugin for. */
3
- export type PluginKind = 'tab' | 'viewer';
4
2
  /** The modal body: the GitHub topic button + the recommended plugin list
5
3
  * with per-entry jump/copy buttons (extracted for direct testing). */
6
4
  export declare function PluginListBody(props: {
7
5
  service: BetterSidebarService;
8
- kind: PluginKind;
9
6
  }): import("react").JSX.Element;
10
7
  /** The modal itself (mounted only while open — see the module comment). */
11
8
  export declare function AddPluginModal(props: {
12
9
  service: BetterSidebarService;
13
10
  onClose: () => void;
14
- kind: PluginKind;
15
11
  }): import("react").JSX.Element;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Typed fetch wrapper over the /sidebar JSON API. Every call posts to
3
+ * `/sidebar/api/<method>` with the sessionId and — when known — the session's
4
+ * cwd from the client's own list summary. The host prefers its attached
5
+ * session header and uses the summary cwd only while the session is still
6
+ * hydrating at page load (a detached session would otherwise fail the
7
+ * request). Failures surface as {@link SidebarApiError} with the wire code.
8
+ */
1
9
  import type { LastActivity } from '../subagent-activity.ts';
2
10
  import type { SidechatThreadInfo } from '../sidechat-core.ts';
3
11
  import type { BrowserProbeResult } from './browser.ts';
@@ -259,17 +267,6 @@ export declare const api: {
259
267
  started: boolean;
260
268
  }>;
261
269
  };
262
- /** Absolute URL of the media route for one path (images only). */
263
- export declare function mediaUrl(scope: SessionScope, path: string): string;
264
270
  /** Absolute URL of the download route: serves raw bytes (binary-safe) with
265
271
  * `Content-Disposition: attachment`, so the browser saves the file. */
266
272
  export declare function downloadUrl(scope: SessionScope, path: string): string;
267
- /**
268
- * Absolute URL of the HTML preview route (see html-route.ts): the path is
269
- * fully encoded so the previewed page's relative assets resolve back into
270
- * the same route with the session scope intact. The UNC marker is
271
- * platform-neutral — the host's requireAbsolute resolves the decoded
272
- * forward-slash `//server/share/...` form on both win32 and POSIX — so no
273
- * client-side platform signal is needed.
274
- */
275
- export declare function htmlUrl(scope: SessionScope, path: string): string;
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Built-in registration: the plugin registers its own tab pages and file
3
- * previewers through the same {@link BetterSidebarService} external plugins
4
- * use — eating its own dogfood. The descriptors live next to their feature
5
- * modules (tabs.tsx / viewers.tsx); this module only aggregates them and
6
- * owns the disposer lifecycle (cordis auto-invokes it on fiber disposal,
7
- * HMR-safe).
2
+ * Built-in registration: the plugin registers its own tab pages through the
3
+ * same {@link BetterSidebarService} external plugins use — eating its own
4
+ * dogfood. The descriptors live next to their feature modules (tabs.tsx);
5
+ * this module only aggregates them and owns the disposer lifecycle (cordis
6
+ * auto-invokes it on fiber disposal, HMR-safe).
7
+ *
8
+ * (v1.0.4: the built-in file viewers were retired with the file-viewer
9
+ * registry — file preview is the host's job now.)
8
10
  */
9
11
  import type { Context } from '../../context-types.ts';
10
12
  import type { BetterSidebarService } from '../service.ts';
11
13
  import { type BuiltinTabOptions } from './tabs.tsx';
12
14
  /**
13
- * Register all built-in tabs and viewers with the service. Returns a
14
- * disposer that unregisters everything (cordis auto-invokes it on fiber
15
- * disposal). The `ctx` is threaded into tab descriptors that need it
16
- * (EditorHost reads `ctx.betterSidebar` for file-viewer matching).
15
+ * Register all built-in tabs with the service. Returns a disposer that
16
+ * unregisters everything (cordis auto-invokes it on fiber disposal).
17
17
  */
18
18
  export declare function registerBuiltins(ctx: Context, service: BetterSidebarService, options?: BuiltinTabOptions): () => void;
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Lazy chunk loader for the client bundle. The heavy preview/terminal
3
- * libraries (CodeMirror, xterm — the editor/terminal stacks, several MB)
4
- * live in separate build-time bundles (`lib/client-<name>.js`) fetched only
5
- * on first use of the feature that needs them, so startup downloads/parses
6
- * only the ~1MB core bundle. (The office stack Univer / docx-preview /
7
- * pptx-renderer is no longer bundled here: Office previews moved to the
8
- * recommended office plugin, see plugins-viewers.ts.)
2
+ * Lazy chunk loader for the client bundle. The heavy editor/terminal
3
+ * libraries (CodeMirror, xterm — several MB) live in separate build-time
4
+ * bundles (`lib/client-<name>.js`) fetched only on first use of the feature
5
+ * that needs them, so startup downloads/parses only the ~1MB core bundle.
6
+ * (v1.0.4: the mermaid chunk was retired with the file-viewer line; the
7
+ * office stack moved to the ecosystem even earlier.)
9
8
  *
10
9
  * How a chunk script works (see tsdown.config.ts chunkBundle):
11
10
  *
@@ -49,7 +48,7 @@
49
48
  * client.js); an edit that does land while a core HMR happens is caught by
50
49
  * the ETag comparison on the next activation.
51
50
  */
52
- export type ChunkName = 'terminal' | 'editor' | 'mermaid';
51
+ export type ChunkName = 'terminal' | 'editor';
53
52
  /** The module exports a chunk factory provides (namespace-ish record). */
54
53
  export type ChunkExports = Record<string, unknown>;
55
54
  /**
@@ -94,14 +94,6 @@ export declare const zh: {
94
94
  terminalDepsFailed: string;
95
95
  terminalDepsHint: string;
96
96
  terminalDepsProfile: string;
97
- preview: string;
98
- toc: string;
99
- edit: string;
100
- mermaidError: string;
101
- mermaidZoomIn: string;
102
- mermaidZoomOut: string;
103
- mermaidZoomReset: string;
104
- mermaidZoomHint: string;
105
97
  refresh: string;
106
98
  refreshUnsavedConfirm: string;
107
99
  save: string;
@@ -244,16 +236,8 @@ export declare const zh: {
244
236
  settingsShellArgsDesc: string;
245
237
  settingsShellArgsPlaceholder: string;
246
238
  settingsTabsTitle: string;
247
- settingsViewersTitle: string;
248
239
  settingsGeneralTitle: string;
249
240
  settingsPopup: string;
250
- settingsViewerCatchAll: string;
251
- viewerImage: string;
252
- viewerPdf: string;
253
- viewerMarkdown: string;
254
- viewerCode: string;
255
- viewerBinary: string;
256
- viewerHtml: string;
257
241
  browser: string;
258
242
  browserPlaceholder: string;
259
243
  browserGo: string;
@@ -264,14 +248,9 @@ export declare const zh: {
264
248
  browserBlockedLoopback: string;
265
249
  browserInvalid: string;
266
250
  browserNoSandboxWarning: string;
267
- htmlNoSandboxWarning: string;
268
251
  sandboxStatusOn: string;
269
252
  sandboxUnlock: string;
270
253
  sandboxRestore: string;
271
- settingsHtmlDefaultUnsafeTitle: string;
272
- settingsHtmlDefaultUnsafeDesc: string;
273
- settingsHtmlSandboxTitle: string;
274
- settingsHtmlSandboxDesc: string;
275
254
  settingsBrowserSandboxTitle: string;
276
255
  settingsBrowserSandboxDesc: string;
277
256
  settingsBrowserLinksTitle: string;
@@ -352,10 +331,7 @@ export declare const zh: {
352
331
  jobKillError: string;
353
332
  addPluginsTabCard: string;
354
333
  addPluginsTabCardDesc: string;
355
- addPluginsViewerCard: string;
356
- addPluginsViewerCardDesc: string;
357
334
  addPluginsTabDesc: string;
358
- addPluginsViewerDesc: string;
359
335
  addPluginsBrowseMore: string;
360
336
  addPluginsSearch: string;
361
337
  addPluginsNoMatch: string;
@@ -363,7 +339,6 @@ export declare const zh: {
363
339
  addPluginsEmpty: string;
364
340
  openPlugin: string;
365
341
  copyInstall: string;
366
- pluginOfficeDesc: string;
367
342
  pluginFlowglassDesc: string;
368
343
  pluginGitForgeDesc: string;
369
344
  pluginGitRemotesDesc: string;
@@ -371,7 +346,6 @@ export declare const zh: {
371
346
  pluginSidebarQaDesc: string;
372
347
  pluginSshTunnelDesc: string;
373
348
  pluginTurnReviewDesc: string;
374
- pluginVideoPreviewDesc: string;
375
349
  pluginDocsPanelDesc: string;
376
350
  pluginEgoBrowserDesc: string;
377
351
  };
@@ -0,0 +1,2 @@
1
+ import type { SidebarStore } from './state.ts';
2
+ export declare function installNativeSidebarAvoidance(store: SidebarStore): () => void;
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Shared vocabulary of the recommended plugin catalogs: the entry shape and
3
- * the GitHub topic URL. The two catalogs live in sibling modules —
4
- * `plugins-tabs.ts` (tab registrations) and `plugins-viewers.ts` (file
5
- * previewer registrations) and are shown in the two "add plugin" modals
6
- * (Side card settings → the dashed cards at the end of the 侧边栏内容 /
7
- * 文件预览 grids).
2
+ * Shared vocabulary of the recommended plugin catalog: the entry shape and
3
+ * the GitHub topic URL. The catalog lives in the sibling module
4
+ * `plugins-tabs.ts` (tab registrations) and is shown in the "add plugin"
5
+ * modal (Side card settings the dashed card at the end of the 侧边栏内容
6
+ * grid). (v1.0.4: the file-previewer catalog was retired with the
7
+ * file-viewer registry.)
8
8
  */
9
9
  /** The GitHub topic page listing every repo tagged `dsh-coding-sidebar`. */
10
10
  export declare const PLUGIN_TOPIC_URL = "https://github.com/topics/dsh-coding-sidebar";
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * The BetterSidebar client service: a registry that external plugins use
3
- * to contribute sidebar tab types and file previewers. The service is
4
- * published to the cordis context as `ctx.betterSidebar` (see
5
- * {@link ../context-types.ts}); consumers declare it in `inject` and call
6
- * `registerTab` / `registerFileViewer`, both returning a disposer that
7
- * cordis auto-invokes on fiber disposal (HMR-safe).
3
+ * to contribute sidebar tab types. The service is published to the cordis
4
+ * context as `ctx.betterSidebar` (see {@link ../context-types.ts});
5
+ * consumers declare it in `inject` and call `registerTab`, which returns a
6
+ * disposer that cordis auto-invokes on fiber disposal (HMR-safe).
7
+ *
8
+ * (v1.0.4: the file-viewer registry — `registerFileViewer` /
9
+ * `matchFileViewer` — was retired; file preview is the host's job now and
10
+ * the sidebar edits text only.)
8
11
  *
9
12
  * Design notes:
10
13
  * - The registry is synchronous-snapshot (Map + listener set) so React
@@ -15,9 +18,6 @@
15
18
  * `single: true` is sugar for `dedupeKey: () => id`.
16
19
  * - `createTab` lets a descriptor own tab instantiation (the terminal
17
20
  * builtin uses it to mint `terminal:<n>` ids and bump `nextTerminal`).
18
- * - `matchFileViewer` walks descriptors in priority order (desc, stable):
19
- * per descriptor it tries `detect` first (when `head` bytes are given),
20
- * then `exts`; `exts: []` is a catch-all that matches any path.
21
21
  */
22
22
  import type { ReactNode } from 'react';
23
23
  import type { Context } from '../context-types.ts';
@@ -50,7 +50,7 @@ export interface SidebarSettingSelectOption {
50
50
  * option's icon too; without icons both are a single line of text. */
51
51
  icon?: ReactNode | ((size: number) => ReactNode);
52
52
  }
53
- /** One declarative setting of a tab/viewer, rendered as a nested row in the
53
+ /** One declarative setting of a registered tab, rendered as a nested row in the
54
54
  * Side card settings page (e.g. the Subagent page's "auto-open when a
55
55
  * subagent appears" switch, or the terminal's custom font rows). `type`
56
56
  * selects the control: 'switch' (default) renders the custom switch,
@@ -93,7 +93,7 @@ export interface SidebarSettingsRenderProps {
93
93
  /** Close the settings popup. */
94
94
  close(): void;
95
95
  }
96
- /** Declarative settings of one registered tab or file viewer. */
96
+ /** Declarative settings of one registered tab. */
97
97
  export interface SidebarSettingsDeclaration {
98
98
  /**
99
99
  * Extra settings rows rendered under the feature's own row in the
@@ -223,78 +223,17 @@ export interface TabDescriptor {
223
223
  onClose?: (tab: SidebarTab, scope: SessionScope) => void;
224
224
  component: (props: TabComponentProps) => ReactNode;
225
225
  }
226
- /** How the host loads a file's bytes for one viewer. */
227
- export type FileFetchStrategy = 'none' | 'fsRead' | 'mediaUrl' | 'custom' | 'binary-download';
228
- /** Props every file viewer component receives. */
229
- export interface FileViewerProps {
230
- ctx: Context;
231
- store: SidebarStore;
232
- scope: SessionScope;
233
- path: string;
234
- title: string;
235
- /** The matching descriptor's id (`'code'`, `'my-plugin:csv'`). */
236
- viewerId: string;
237
- /** fsRead text content (fetchStrategy='fsRead'). */
238
- content?: string;
239
- truncated?: boolean;
240
- /** mediaUrl for the path (fetchStrategy='mediaUrl'). */
241
- mediaUrl?: string;
242
- /** custom load() return value (fetchStrategy='custom'). */
243
- customData?: unknown;
244
- /** Internal (built-in text editor): 'host' asks the viewer to skip its own
245
- * toolbar row — the editor host's merged-mode header renders it instead,
246
- * fed through the two callbacks below. Viewers that ignore these fields
247
- * render exactly as before. */
248
- toolbar?: 'self' | 'host';
249
- /** Internal: the viewer reports its toolbar state (mode/dirty/save). */
250
- onToolbarState?: (state: EditorToolbarState) => void;
251
- /** Internal: the viewer registers its toolbar commands on mount (null on
252
- * unmount). */
253
- onToolbarControls?: (controls: EditorToolbarControls | null) => void;
254
- }
255
- /** The toolbar state a text editor reports to the host's merged-mode header. */
226
+ /** The toolbar state the text editor reports to the host's merged-mode header. */
256
227
  export interface EditorToolbarState {
257
- /** Whether the preview/edit mode toggle applies (markdown/html). */
258
- modes: boolean;
259
- mode: 'preview' | 'edit';
260
228
  dirty: boolean;
261
229
  /** Whether saving applies (text content loaded). */
262
230
  editable: boolean;
263
231
  saveState: 'idle' | 'saving' | 'saved' | 'failed';
264
232
  }
265
- /** The commands the host's merged-mode header sends back to the viewer. */
233
+ /** The commands the host's merged-mode header sends back to the editor. */
266
234
  export interface EditorToolbarControls {
267
- setMode(mode: 'preview' | 'edit'): void;
268
235
  save(): void;
269
236
  }
270
- /** Describes one file previewer (builtins register themselves too). */
271
- export interface FileViewerDescriptor {
272
- /** Unique id (`'image'`, `'pdf'`, `'my-plugin:csv'`). */
273
- id: string;
274
- /** Display name for the settings inventory (falls back to `id` when absent). */
275
- title?: string | (() => string);
276
- /** Icon shown in the settings inventory. */
277
- icon?: ReactNode | ((size: number) => ReactNode);
278
- /** Lowercase extensions without leading dot (`['png','jpg']`). `[]` = match any (catch-all). */
279
- exts: readonly string[];
280
- /** Higher wins; default 0. Builtins use 0; the catch-all `code` viewer uses -100. */
281
- priority?: number;
282
- fetchStrategy: FileFetchStrategy;
283
- /**
284
- * Content sniff: when `head` bytes are available the descriptor's `detect`
285
- * is consulted before its `exts` (per-descriptor, in priority order).
286
- */
287
- detect?: (path: string, head: Uint8Array) => boolean;
288
- /** fetchStrategy='custom' loader. `signal` (v0.12.0+) aborts on viewer
289
- * teardown / re-match; loaders that ignore it keep working. */
290
- load?: (path: string, scope: SessionScope, signal?: AbortSignal) => Promise<unknown>;
291
- /**
292
- * Declarative settings shown in the Side card settings page: every
293
- * registered viewer gets an enable/disable switch (icon + title + exts).
294
- */
295
- settings?: SidebarSettingsDeclaration;
296
- component: (props: FileViewerProps) => ReactNode;
297
- }
298
237
  /** One `openTab` request. */
299
238
  export interface OpenTabSeed {
300
239
  type: string;
@@ -316,9 +255,7 @@ export interface OpenTabSeed {
316
255
  */
317
256
  export interface BetterSidebarService {
318
257
  registerTab(descriptor: TabDescriptor): () => void;
319
- registerFileViewer(descriptor: FileViewerDescriptor): () => void;
320
258
  getTabs(): readonly TabDescriptor[];
321
- getFileViewers(): readonly FileViewerDescriptor[];
322
259
  /** Find a tab descriptor by id (undefined if not registered). */
323
260
  getTab(id: string): TabDescriptor | undefined;
324
261
  /**
@@ -328,13 +265,6 @@ export interface BetterSidebarService {
328
265
  * derived flows gate on it).
329
266
  */
330
267
  isTabEnabled(id: string): boolean;
331
- /** Whether a file viewer is enabled (absent `viewersEnabled[id]` = enabled). */
332
- isViewerEnabled(id: string): boolean;
333
- /**
334
- * Find a file viewer for a path (priority desc; detect first, then exts).
335
- * Disabled viewers are skipped, so files fall through to the next match.
336
- */
337
- matchFileViewer(path: string, head?: Uint8Array): FileViewerDescriptor | undefined;
338
268
  /**
339
269
  * Open a tab (used by external tabs and the + menu). `title` overrides
340
270
  * the descriptor's title when given (the editor tab shows the file name);
@@ -433,7 +363,7 @@ export declare const SIDEBAR_SERVICE_VERSION: string;
433
363
  export declare const SIDEBAR_FEATURES: readonly ["badge", "tabLifecycle", "updateTab", "openFile", "targetedOpen", "stateSubscription", "tabMeta", "pluginSettings", "urlTarget", "settingSelect", "floatWindows"];
434
364
  /**
435
365
  * Create one BetterSidebar service bound to a store. The service owns the
436
- * tab/viewer registries (Map + listener set) and proxies openTab/closeTab
366
+ * tab registry (Map + listener set) and proxies openTab/closeTab
437
367
  * to the store's reducer. One instance per client plugin activation.
438
368
  */
439
369
  export declare function createBetterSidebarService(store: SidebarStore): BetterSidebarService;
@@ -3,7 +3,7 @@ import { Config, type ResolvedSidebarConfig, type SidebarConfig } from './config
3
3
  export { Config };
4
4
  export type { SidebarConfig, ResolvedSidebarConfig };
5
5
  export type { Context } from './context-types.ts';
6
- export type { BetterSidebarService, TabDescriptor, TabComponentProps, FileViewerDescriptor, FileViewerProps, FileFetchStrategy, } from './client/service.ts';
6
+ export type { BetterSidebarService, TabDescriptor, TabComponentProps, } from './client/service.ts';
7
7
  /** Plugin identity for cordis.yml rows. */
8
8
  export declare const name = "dsh-coding-sidebar";
9
9
  /** Services required before mounting: the webserver routes, the session store, the web runtime's trusted hosts, and the tool registry. */
@@ -124,22 +124,6 @@ export interface SidebarPrefs {
124
124
  * starts `strip` px below its top edge.
125
125
  */
126
126
  titleBarStripPx: number;
127
- /**
128
- * Whether the HTML previewer drops its sandboxed iframe. Sandbox ON (the
129
- * default) renders previewed HTML in an opaque-origin iframe that cannot
130
- * touch the GUI; turning it OFF runs the previewed page with the GUI's
131
- * own origin — full read/write access to session files and internal
132
- * APIs. Only for trusted local content; the setting copy warns.
133
- */
134
- htmlViewerNoSandbox: boolean;
135
- /**
136
- * Whether a newly opened HTML preview starts UNSANDBOXED (the per-surface
137
- * temporary unlock pre-applied). Off by default: previews open sandboxed
138
- * and the status row offers the one-tap unlock; when on, previews open
139
- * in the red unsandboxed state and the status row offers a one-tap
140
- * restore for the current file.
141
- */
142
- htmlViewerDefaultUnsafe: boolean;
143
127
  /**
144
128
  * Whether the browser tab drops its sandboxed iframe. Sandbox ON (the
145
129
  * default) keeps browsed sites in an opaque origin with no GUI access;
@@ -194,16 +178,9 @@ export interface SidebarPrefs {
194
178
  * own layouts" rule.
195
179
  */
196
180
  tabsEnabled: Record<string, boolean>;
197
- /**
198
- * Per-viewer enable switches, keyed by file viewer descriptor id
199
- * (`'image'`, `'my-plugin:csv'`). An ABSENT key means enabled; a disabled
200
- * viewer is skipped by `matchFileViewer` so files fall through to the
201
- * next matching viewer (or the download button when none match).
202
- */
203
- viewersEnabled: Record<string, boolean>;
204
181
  /**
205
182
  * Plugin-owned settings blobs (v0.12.0+), keyed by descriptor id: each
206
- * registered tab/viewer that declares `settings.pluginToggles` (or writes
183
+ * registered tab that declares `settings.pluginToggles` (or writes
207
184
  * through `settings.render`'s `updatePluginSetting`) persists its values
208
185
  * here — an open map, so third-party keys need no host PrefsSchema field.
209
186
  * Values are JSON-serializable (the row controls produce strings /