dsh-coding-sidebar 1.0.3 → 1.0.5
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 +27 -42
- package/lib/client-editor.js +315 -4179
- package/lib/client-registry.js +913 -1303
- package/lib/client-terminal.js +189 -261
- package/lib/client.js +911 -1301
- package/lib/index.js +13 -110
- package/lib/types/bundle-route.d.ts +1 -1
- package/lib/types/client/LiveView.d.ts +1 -0
- package/lib/types/client/SideCardSection.d.ts +3 -3
- package/lib/types/client/TextEditor.d.ts +20 -10
- package/lib/types/client/add-plugin-modal.d.ts +0 -4
- package/lib/types/client/api.d.ts +16 -11
- package/lib/types/client/builtins/index.d.ts +10 -10
- package/lib/types/client/chunk-loader.d.ts +7 -8
- package/lib/types/client/locales.d.ts +6 -26
- package/lib/types/client/native-avoid.d.ts +2 -0
- package/lib/types/client/plugins-shared.d.ts +6 -6
- package/lib/types/client/service.d.ts +13 -83
- package/lib/types/index.d.ts +1 -1
- package/lib/types/prefs-shared.d.ts +1 -24
- package/lib/types/wire.d.ts +1 -1
- package/package.json +26 -29
- package/src/bundle-route.ts +1 -1
- package/src/client/BrowserView.tsx +18 -2
- package/src/client/EditorHost.tsx +52 -122
- package/src/client/LiveView.tsx +229 -0
- package/src/client/SandboxStatusBar.tsx +12 -9
- package/src/client/SideCardSection.tsx +28 -81
- package/src/client/TextEditor.tsx +89 -266
- package/src/client/add-plugin-modal.tsx +16 -24
- package/src/client/api.ts +4 -25
- package/src/client/builtins/index.ts +10 -14
- package/src/client/chunk-loader.ts +7 -8
- package/src/client/index.tsx +12 -3
- package/src/client/locales-ar.ts +0 -6
- package/src/client/locales-de.ts +0 -6
- package/src/client/locales-fr.ts +0 -6
- package/src/client/locales-hi.ts +0 -6
- package/src/client/locales-id.ts +0 -6
- package/src/client/locales-it.ts +0 -6
- package/src/client/locales-ja.ts +0 -6
- package/src/client/locales-ko.ts +0 -6
- package/src/client/locales-nl.ts +0 -6
- package/src/client/locales-pl.ts +0 -6
- package/src/client/locales-pt.ts +0 -6
- package/src/client/locales-ru.ts +0 -6
- package/src/client/locales-sv.ts +0 -6
- package/src/client/locales-th.ts +0 -6
- package/src/client/locales-tr.ts +0 -6
- package/src/client/locales-vi.ts +0 -6
- package/src/client/locales-zh-HK.ts +0 -6
- package/src/client/locales-zh-MO.ts +0 -6
- package/src/client/locales-zh-TW.ts +0 -6
- package/src/client/locales.ts +12 -52
- package/src/client/native-avoid.ts +155 -0
- package/src/client/plugins-shared.ts +6 -6
- package/src/client/prefs.ts +0 -7
- package/src/client/service.ts +13 -150
- package/src/client/sidebar.module.css +28 -420
- package/src/config.ts +1 -4
- package/src/index.ts +17 -74
- package/src/prefs-shared.ts +1 -27
- package/src/wire.ts +1 -0
- package/lib/client-mermaid.js +0 -200776
- package/lib/types/client/MarkdownHtml.d.ts +0 -32
- package/lib/types/client/PdfView.d.ts +0 -6
- package/lib/types/client/builtins/viewers.d.ts +0 -3
- package/lib/types/client/chunks/mermaid.d.ts +0 -10
- package/lib/types/client/editor-load.d.ts +0 -66
- package/lib/types/client/image-types.d.ts +0 -3
- package/lib/types/client/markdown-html.d.ts +0 -96
- package/lib/types/client/markdown-images.d.ts +0 -45
- package/lib/types/client/md-toc.d.ts +0 -4
- package/lib/types/client/mermaid-blocks.d.ts +0 -47
- package/lib/types/client/mermaid-sanitize.d.ts +0 -2
- package/lib/types/client/mermaid.d.ts +0 -12
- package/lib/types/client/pdf-types.d.ts +0 -2
- package/lib/types/client/plugins-viewers.d.ts +0 -3
- package/lib/types/html-route.d.ts +0 -59
- package/src/client/MarkdownHtml.tsx +0 -296
- package/src/client/PdfView.tsx +0 -110
- package/src/client/builtins/viewers.tsx +0 -125
- package/src/client/chunks/mermaid.tsx +0 -10
- package/src/client/editor-load.ts +0 -92
- package/src/client/image-types.ts +0 -8
- package/src/client/markdown-html.ts +0 -331
- package/src/client/markdown-images.ts +0 -137
- package/src/client/md-toc.tsx +0 -127
- package/src/client/mermaid-blocks.ts +0 -110
- package/src/client/mermaid-sanitize.ts +0 -93
- package/src/client/mermaid.tsx +0 -406
- package/src/client/pdf-types.ts +0 -4
- package/src/client/plugins-viewers.ts +0 -30
- 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 (
|
|
3693
|
-
*
|
|
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) {
|
|
@@ -4038,6 +3971,15 @@ function buildApi(ctx, ptyManager, agentPtyRegistry, resolved, terminalShell, ge
|
|
|
4038
3971
|
throw new SidebarError("settings-rejected", error instanceof Error ? error.message : String(error), 400);
|
|
4039
3972
|
}
|
|
4040
3973
|
},
|
|
3974
|
+
"cdp.targets": async () => {
|
|
3975
|
+
const res = await fetch("http://127.0.0.1:9223/json/list", { signal: AbortSignal.timeout(3e3) });
|
|
3976
|
+
if (!res.ok) throw new SidebarError("cdp-down", `browser host unreachable (${res.status})`, 502);
|
|
3977
|
+
return { targets: (await res.json()).filter((t) => t.type === "page").map((t) => ({
|
|
3978
|
+
id: t.id,
|
|
3979
|
+
url: t.url,
|
|
3980
|
+
title: t.title
|
|
3981
|
+
})) };
|
|
3982
|
+
},
|
|
4041
3983
|
"browser.probe": async (payload) => {
|
|
4042
3984
|
const raw = requireString(payload, "url");
|
|
4043
3985
|
let parsed;
|
|
@@ -4302,45 +4244,6 @@ function apply(ctx, config) {
|
|
|
4302
4244
|
}
|
|
4303
4245
|
}
|
|
4304
4246
|
}), "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
4247
|
const wss = new WebSocketServer({ noServer: true });
|
|
4345
4248
|
ctx.effect(() => ctx.webServer.registerUpgrade({
|
|
4346
4249
|
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"
|
|
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-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function LiveView(): React.JSX.Element;
|
|
@@ -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,
|
|
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
|
|
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
|
|
56
|
+
feature: TabDescriptor;
|
|
57
57
|
prefs: SidebarPrefs;
|
|
58
58
|
store: SidebarStore;
|
|
59
59
|
service: BetterSidebarService;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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';
|
|
@@ -245,6 +253,14 @@ export declare const api: {
|
|
|
245
253
|
/** Probe a URL's response headers (the sidebar browser's embeddability
|
|
246
254
|
* check; see the host's browser.probe route). */
|
|
247
255
|
browserProbe: (url: string, signal?: AbortSignal) => Promise<BrowserProbeResult>;
|
|
256
|
+
/** Agent 浏览器宿主的 page target 列表(host 代理 CDP /json/list)。 */
|
|
257
|
+
cdpTargets: (signal?: AbortSignal) => Promise<{
|
|
258
|
+
targets: Array<{
|
|
259
|
+
id: string;
|
|
260
|
+
url: string;
|
|
261
|
+
title: string;
|
|
262
|
+
}>;
|
|
263
|
+
}>;
|
|
248
264
|
/** External open for the file tree's "open with" menu: reveal a path in
|
|
249
265
|
* the OS file manager, or hand a custom-scheme URL (vscode://, cursor://,
|
|
250
266
|
* zed://, custom editors) to its registered handler. The host launches
|
|
@@ -259,17 +275,6 @@ export declare const api: {
|
|
|
259
275
|
started: boolean;
|
|
260
276
|
}>;
|
|
261
277
|
};
|
|
262
|
-
/** Absolute URL of the media route for one path (images only). */
|
|
263
|
-
export declare function mediaUrl(scope: SessionScope, path: string): string;
|
|
264
278
|
/** Absolute URL of the download route: serves raw bytes (binary-safe) with
|
|
265
279
|
* `Content-Disposition: attachment`, so the browser saves the file. */
|
|
266
280
|
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
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
14
|
-
*
|
|
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
|
|
3
|
-
* libraries (CodeMirror, xterm —
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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'
|
|
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,34 +236,27 @@ 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;
|
|
260
244
|
browserBack: string;
|
|
261
245
|
browserForward: string;
|
|
262
246
|
browserStart: string;
|
|
247
|
+
browserLive: string;
|
|
248
|
+
browserLiveFree: string;
|
|
249
|
+
browserLiveConnecting: string;
|
|
250
|
+
browserLiveDown: string;
|
|
251
|
+
browserLiveTargetNone: string;
|
|
252
|
+
browserLiveFollowLatest: string;
|
|
263
253
|
browserBlockedScheme: string;
|
|
264
254
|
browserBlockedLoopback: string;
|
|
265
255
|
browserInvalid: string;
|
|
266
256
|
browserNoSandboxWarning: string;
|
|
267
|
-
htmlNoSandboxWarning: string;
|
|
268
257
|
sandboxStatusOn: string;
|
|
269
258
|
sandboxUnlock: string;
|
|
270
259
|
sandboxRestore: string;
|
|
271
|
-
settingsHtmlDefaultUnsafeTitle: string;
|
|
272
|
-
settingsHtmlDefaultUnsafeDesc: string;
|
|
273
|
-
settingsHtmlSandboxTitle: string;
|
|
274
|
-
settingsHtmlSandboxDesc: string;
|
|
275
260
|
settingsBrowserSandboxTitle: string;
|
|
276
261
|
settingsBrowserSandboxDesc: string;
|
|
277
262
|
settingsBrowserLinksTitle: string;
|
|
@@ -352,10 +337,7 @@ export declare const zh: {
|
|
|
352
337
|
jobKillError: string;
|
|
353
338
|
addPluginsTabCard: string;
|
|
354
339
|
addPluginsTabCardDesc: string;
|
|
355
|
-
addPluginsViewerCard: string;
|
|
356
|
-
addPluginsViewerCardDesc: string;
|
|
357
340
|
addPluginsTabDesc: string;
|
|
358
|
-
addPluginsViewerDesc: string;
|
|
359
341
|
addPluginsBrowseMore: string;
|
|
360
342
|
addPluginsSearch: string;
|
|
361
343
|
addPluginsNoMatch: string;
|
|
@@ -363,7 +345,6 @@ export declare const zh: {
|
|
|
363
345
|
addPluginsEmpty: string;
|
|
364
346
|
openPlugin: string;
|
|
365
347
|
copyInstall: string;
|
|
366
|
-
pluginOfficeDesc: string;
|
|
367
348
|
pluginFlowglassDesc: string;
|
|
368
349
|
pluginGitForgeDesc: string;
|
|
369
350
|
pluginGitRemotesDesc: string;
|
|
@@ -371,7 +352,6 @@ export declare const zh: {
|
|
|
371
352
|
pluginSidebarQaDesc: string;
|
|
372
353
|
pluginSshTunnelDesc: string;
|
|
373
354
|
pluginTurnReviewDesc: string;
|
|
374
|
-
pluginVideoPreviewDesc: string;
|
|
375
355
|
pluginDocsPanelDesc: string;
|
|
376
356
|
pluginEgoBrowserDesc: string;
|
|
377
357
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared vocabulary of the recommended plugin
|
|
3
|
-
* the GitHub topic URL. The
|
|
4
|
-
* `plugins-tabs.ts` (tab registrations) and
|
|
5
|
-
*
|
|
6
|
-
* (
|
|
7
|
-
*
|
|
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
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -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,
|
|
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. */
|