dsh-vscode-workbench 0.2.4 → 0.2.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/lib/client.js CHANGED
@@ -44,9 +44,310 @@ window.__ModuleLoader__.load({
44
44
  enumerable: true
45
45
  }) : target, mod));
46
46
  //#endregion
47
+ let _deepseek_ai_dsh_api_terminal_controller_client = require("@deepseek-ai/dsh-api-terminal-controller/client");
47
48
  let react = require("react");
48
49
  let react_jsx_runtime = require("react/jsx-runtime");
49
50
  let react_dom = require("react-dom");
51
+ //#region src/client/terminal.ts
52
+ const STORAGE_PREFIX = "dvw:native-terminal-tab:v1:";
53
+ /** Own workbench membership without overwriting the official sidebar's terminal inventory. */
54
+ var TerminalWorkspaces = class {
55
+ models;
56
+ workspaces = /* @__PURE__ */ new Map();
57
+ saved = Object.create(null);
58
+ constructor(models) {
59
+ this.models = models;
60
+ try {
61
+ this.saved = this.readSaved();
62
+ } catch {}
63
+ this.retain();
64
+ window.addEventListener("storage", this.onStorage);
65
+ }
66
+ get(sessionId) {
67
+ let workspace = this.workspaces.get(sessionId);
68
+ if (workspace === void 0) {
69
+ workspace = new TerminalWorkspaceController(this, sessionId, this.saved[sessionId] ?? []);
70
+ this.workspaces.set(sessionId, workspace);
71
+ }
72
+ return workspace;
73
+ }
74
+ save(sessionId, ids) {
75
+ const previous = this.saved[sessionId] ?? [];
76
+ try {
77
+ for (const id of ids) if (!previous.includes(id)) localStorage.setItem(STORAGE_PREFIX + JSON.stringify([sessionId, id]), "open");
78
+ for (const id of previous) if (!ids.includes(id)) localStorage.removeItem(STORAGE_PREFIX + JSON.stringify([sessionId, id]));
79
+ this.sync(this.readSaved());
80
+ } catch {
81
+ this.saved[sessionId] = ids;
82
+ this.retain();
83
+ }
84
+ }
85
+ readSaved() {
86
+ const saved = Object.create(null);
87
+ for (let index = 0; index < localStorage.length; index++) {
88
+ const key = localStorage.key(index);
89
+ if (!key?.startsWith(STORAGE_PREFIX)) continue;
90
+ try {
91
+ const value = JSON.parse(key.slice(27));
92
+ if (!Array.isArray(value) || value.length !== 2) continue;
93
+ const [sessionId, id] = value;
94
+ if (typeof sessionId !== "string" || typeof id !== "string" || !id.startsWith("dvw-terminal:")) continue;
95
+ (saved[sessionId] ??= []).push(id);
96
+ } catch {}
97
+ }
98
+ return saved;
99
+ }
100
+ sync(saved) {
101
+ this.saved = saved;
102
+ for (const [sessionId, workspace] of this.workspaces) workspace.reconcile(saved[sessionId] ?? []);
103
+ this.retain();
104
+ }
105
+ onStorage = (event) => {
106
+ if (event.storageArea !== localStorage || event.key !== null && !event.key.startsWith(STORAGE_PREFIX)) return;
107
+ this.sync(this.readSaved());
108
+ };
109
+ retain() {
110
+ this.models.retainTabs(Object.entries(this.saved).flatMap(([sessionId, ids]) => ids.map((id) => ({
111
+ sessionId,
112
+ tabId: id,
113
+ contentId: id
114
+ }))));
115
+ }
116
+ dispose() {
117
+ window.removeEventListener("storage", this.onStorage);
118
+ for (const workspace of this.workspaces.values()) workspace.dispose();
119
+ this.models.retainTabs([]);
120
+ }
121
+ };
122
+ /** Layout only; the official model owns processes, stream ordering, recovery and input control. */
123
+ var TerminalWorkspaceController = class {
124
+ owner;
125
+ sessionId;
126
+ closeFailures;
127
+ snapshot;
128
+ listeners = /* @__PURE__ */ new Set();
129
+ views = /* @__PURE__ */ new Map();
130
+ constructor(owner, sessionId, ids) {
131
+ this.owner = owner;
132
+ this.sessionId = sessionId;
133
+ this.closeFailures = owner.models.closeFailures;
134
+ this.snapshot = {
135
+ terminals: ids.map((id) => ({
136
+ id,
137
+ title: "终端"
138
+ })),
139
+ loading: false
140
+ };
141
+ }
142
+ view(id) {
143
+ const existing = this.views.get(id);
144
+ if (existing !== void 0) return existing.model;
145
+ const model = this.owner.models.view(this.sessionId, id, id);
146
+ const unsubscribe = model.state.subscribe(() => this.updateTitles());
147
+ this.views.set(id, {
148
+ model,
149
+ unsubscribe
150
+ });
151
+ this.updateTitles();
152
+ return model;
153
+ }
154
+ async refresh() {
155
+ this.set({
156
+ ...this.snapshot,
157
+ loading: true,
158
+ error: void 0
159
+ });
160
+ try {
161
+ await Promise.all(this.snapshot.terminals.map((entry) => this.view(entry.id).refresh()));
162
+ this.set({
163
+ ...this.snapshot,
164
+ loading: false
165
+ });
166
+ return true;
167
+ } catch (error) {
168
+ this.fail(error);
169
+ return false;
170
+ }
171
+ }
172
+ async create() {
173
+ const entry = {
174
+ id: `dvw-terminal:${crypto.randomUUID()}`,
175
+ title: "终端"
176
+ };
177
+ this.set({
178
+ terminals: [...this.snapshot.terminals, entry],
179
+ loading: false
180
+ });
181
+ this.save();
182
+ await this.view(entry.id).refresh();
183
+ return this.snapshot.terminals.find((row) => row.id === entry.id) ?? entry;
184
+ }
185
+ close(id) {
186
+ this.owner.models.close(this.sessionId, id, id);
187
+ this.views.get(id)?.unsubscribe();
188
+ this.views.delete(id);
189
+ this.set({
190
+ ...this.snapshot,
191
+ terminals: this.snapshot.terminals.filter((entry) => entry.id !== id)
192
+ });
193
+ this.save();
194
+ }
195
+ async rename(id, title) {
196
+ try {
197
+ await this.view(id).rename(title);
198
+ } catch (error) {
199
+ this.fail(error);
200
+ }
201
+ }
202
+ retryClose(id) {
203
+ this.owner.models.retryClose(id);
204
+ }
205
+ reconcile(ids) {
206
+ const entries = new Map(this.snapshot.terminals.map((entry) => [entry.id, entry]));
207
+ for (const [id, view] of this.views) if (!ids.includes(id)) {
208
+ view.unsubscribe();
209
+ this.views.delete(id);
210
+ }
211
+ this.set({
212
+ ...this.snapshot,
213
+ terminals: ids.map((id) => entries.get(id) ?? {
214
+ id,
215
+ title: "终端"
216
+ })
217
+ });
218
+ }
219
+ getSnapshot = () => this.snapshot;
220
+ subscribe = (listener) => {
221
+ this.listeners.add(listener);
222
+ return () => this.listeners.delete(listener);
223
+ };
224
+ dispose() {
225
+ for (const view of this.views.values()) view.unsubscribe();
226
+ this.views.clear();
227
+ }
228
+ save() {
229
+ this.owner.save(this.sessionId, this.snapshot.terminals.map((entry) => entry.id));
230
+ }
231
+ updateTitles() {
232
+ this.set({
233
+ ...this.snapshot,
234
+ terminals: this.snapshot.terminals.map((entry) => ({
235
+ ...entry,
236
+ title: this.views.get(entry.id)?.model.state.getSnapshot().title ?? entry.title
237
+ }))
238
+ });
239
+ }
240
+ fail(error) {
241
+ this.set({
242
+ ...this.snapshot,
243
+ loading: false,
244
+ error: error instanceof Error ? error.message : String(error)
245
+ });
246
+ }
247
+ set(snapshot) {
248
+ this.snapshot = snapshot;
249
+ for (const listener of this.listeners) listener();
250
+ }
251
+ };
252
+ function terminalIssue(state) {
253
+ return state.issue === void 0 ? state.error ?? state.info?.error : {
254
+ missingTerminal: "此终端已不存在,请关闭后新建终端。",
255
+ inputFull: "终端输入缓冲已满,请重新连接。",
256
+ attachmentEnded: "终端连接已结束,请重新连接。",
257
+ invalidOutput: "终端输出无法解析,请重新连接。",
258
+ terminalLimit: "终端数量已达上限,请关闭不用的终端。"
259
+ }[state.issue];
260
+ }
261
+ //#endregion
262
+ //#region src/client/browser.ts
263
+ /** Navigate official browser tabs; their controller, renderer and persistence stay with DSH. */
264
+ var NativeBrowser = class {
265
+ ctx;
266
+ snapshot = {
267
+ available: false,
268
+ tabs: []
269
+ };
270
+ listeners = /* @__PURE__ */ new Set();
271
+ lifetime = new AbortController();
272
+ disposers;
273
+ constructor(ctx) {
274
+ this.ctx = ctx;
275
+ this.disposers = [ctx.sidebarRight.openTabs.subscribe(() => this.update()), ctx.sidebarRightTabs.subscribe(() => this.update())];
276
+ this.update();
277
+ }
278
+ getSnapshot = () => this.snapshot;
279
+ subscribe = (listener) => {
280
+ this.listeners.add(listener);
281
+ return () => this.listeners.delete(listener);
282
+ };
283
+ async open(sessionId, request) {
284
+ const signal = request === void 0 ? this.lifetime.signal : AbortSignal.any([request, this.lifetime.signal]);
285
+ signal.throwIfAborted();
286
+ if (!this.snapshot.available) throw new Error("官方浏览器尚未启用,请重新加载 Harness。");
287
+ const ready = () => this.ctx.sidebarRight.mounted.getSnapshot() === sessionId && [...document.querySelectorAll("[data-sidebar-right-session]")].some((node) => node.getAttribute("data-sidebar-right-session") === sessionId);
288
+ if (!ready()) await new Promise((resolve, reject) => {
289
+ const clean = () => {
290
+ clearTimeout(timer);
291
+ unsubscribe();
292
+ observer.disconnect();
293
+ signal.removeEventListener("abort", abort);
294
+ };
295
+ const abort = () => {
296
+ clean();
297
+ reject(signal.reason);
298
+ };
299
+ const timer = setTimeout(() => {
300
+ clean();
301
+ reject(/* @__PURE__ */ new Error("工作区尚未就绪,请稍后重试。"));
302
+ }, 1e4);
303
+ const check = () => {
304
+ if (ready()) {
305
+ clean();
306
+ resolve();
307
+ }
308
+ };
309
+ const unsubscribe = this.ctx.sidebarRight.mounted.subscribe(check);
310
+ const observer = new MutationObserver(check);
311
+ observer.observe(document.body, {
312
+ childList: true,
313
+ subtree: true
314
+ });
315
+ signal.addEventListener("abort", abort, { once: true });
316
+ });
317
+ signal.throwIfAborted();
318
+ const before = new Set(this.ctx.sidebarRight.tabsIn(sessionId).map((tab) => tab.id));
319
+ this.ctx.sidebarRight.openTabIn(sessionId, "browser", { revealIfOpened: false });
320
+ const tab = this.ctx.sidebarRight.tabsIn(sessionId).find((tab) => tab.kind === "browser" && !before.has(tab.id));
321
+ if (tab === void 0) throw new Error("浏览器页面未能打开,请重试。");
322
+ this.update();
323
+ return tab.id;
324
+ }
325
+ focus(id) {
326
+ this.ctx.sidebarRight.focus(id);
327
+ if (!this.ctx.sidebarRight.isExpanded()) this.ctx.sidebarRight.toggleExpanded();
328
+ }
329
+ close(sessionId, id) {
330
+ this.ctx.sidebarRight.closeIn(sessionId, id);
331
+ this.update();
332
+ }
333
+ dispose() {
334
+ this.lifetime.abort();
335
+ for (const dispose of this.disposers) dispose();
336
+ this.listeners.clear();
337
+ }
338
+ update() {
339
+ this.snapshot = {
340
+ available: this.ctx.sidebarRightTabs.get("browser") !== void 0,
341
+ tabs: this.ctx.sidebarRight.openTabs.getSnapshot().filter((tab) => tab.kind === "browser").map((tab) => ({
342
+ id: tab.tabId,
343
+ sessionId: tab.sessionId,
344
+ title: this.ctx.sidebarRight.tabsIn(tab.sessionId).find((record) => record.id === tab.tabId)?.title ?? "浏览器"
345
+ }))
346
+ };
347
+ for (const listener of this.listeners) listener();
348
+ }
349
+ };
350
+ //#endregion
50
351
  //#region src/client/service.ts
51
352
  const SNAPSHOT_KEY = "dvw:workbench:snapshot:v1";
52
353
  const DEFAULT_SNAPSHOT = {
@@ -7409,273 +7710,6 @@ window.__ModuleLoader__.load({
7409
7710
  });
7410
7711
  }
7411
7712
  //#endregion
7412
- //#region src/client/api.ts
7413
- var ApiError = class extends Error {
7414
- code;
7415
- constructor(code, message) {
7416
- super(message);
7417
- this.code = code;
7418
- }
7419
- };
7420
- /** Back off while DSH publishes the current session after startup; every view shares this retry. */
7421
- function sessionRetryDelay(error, attempt) {
7422
- if (!(error instanceof ApiError) || error.code !== "session-not-found" || attempt >= 10) return void 0;
7423
- return Math.min(250 * (attempt + 1), 1e3);
7424
- }
7425
- function wait(ms, signal) {
7426
- return new Promise((resolve, reject) => {
7427
- const abort = () => {
7428
- clearTimeout(timer);
7429
- reject(signal?.reason ?? /* @__PURE__ */ new Error("aborted"));
7430
- };
7431
- const timer = setTimeout(() => {
7432
- signal?.removeEventListener("abort", abort);
7433
- resolve();
7434
- }, ms);
7435
- if (signal?.aborted === true) {
7436
- abort();
7437
- return;
7438
- }
7439
- signal?.addEventListener("abort", abort, { once: true });
7440
- });
7441
- }
7442
- async function request$2(method, body, signal) {
7443
- const response = await fetch(`/dsh-vscode/api/${method}`, {
7444
- method: "POST",
7445
- headers: { "content-type": "application/json" },
7446
- body: JSON.stringify(body),
7447
- signal
7448
- });
7449
- const answer = await response.json();
7450
- if (!response.ok || !answer.ok) throw new ApiError(answer.error?.code ?? "request-failed", answer.error?.message ?? `request failed (${response.status})`);
7451
- return answer.value;
7452
- }
7453
- async function api(method, body, signal) {
7454
- return withSessionRetry(() => request$2(method, body, signal), signal);
7455
- }
7456
- /** Retry an operation while DSH is still publishing the selected session. */
7457
- async function withSessionRetry(operation, signal) {
7458
- for (let attempt = 0;; attempt++) try {
7459
- return await operation();
7460
- } catch (error) {
7461
- const delay = sessionRetryDelay(error, attempt);
7462
- if (delay === void 0) throw error;
7463
- await wait(delay, signal);
7464
- }
7465
- }
7466
- //#endregion
7467
- //#region src/client/websocket.ts
7468
- const WEB_PAGE_PROTOCOLS = /* @__PURE__ */ new Set([
7469
- "http:",
7470
- "https:",
7471
- "blob:",
7472
- "data:",
7473
- "about:",
7474
- "filesystem:"
7475
- ]);
7476
- /** Parse a shell-published base only when it is a credential-free HTTP(S) origin. */
7477
- function networkBase(value) {
7478
- if (typeof value !== "string" || value === "") return void 0;
7479
- let url;
7480
- try {
7481
- url = new URL(value);
7482
- } catch {
7483
- return;
7484
- }
7485
- if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
7486
- if (url.username !== "" || url.password !== "" || url.host === "") return void 0;
7487
- return {
7488
- protocol: url.protocol,
7489
- host: url.host
7490
- };
7491
- }
7492
- /** Read the Host origin published by the DSH Desktop shell before application boot. */
7493
- function transportStreamBaseUrl(globals = globalThis) {
7494
- const transport = globals.__DSH_TRANSPORT__;
7495
- return typeof transport?.streamBaseUrl === "string" ? transport.streamBaseUrl : void 0;
7496
- }
7497
- /**
7498
- * Resolve an absolute workbench route to a WebSocket URL.
7499
- *
7500
- * Network pages keep their own origin. Application-delivered pages such as
7501
- * `dsh-app://app/` have no WebSocket transport of their own, so they must use
7502
- * the local Host origin published by the desktop shell.
7503
- */
7504
- function websocketUrl(path, location = globalThis.location, streamBaseUrl = transportStreamBaseUrl()) {
7505
- const route = path.startsWith("/") ? path : `/${path}`;
7506
- if (WEB_PAGE_PROTOCOLS.has(location.protocol)) {
7507
- if (location.host === "") return void 0;
7508
- return `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}${route}`;
7509
- }
7510
- const base = networkBase(streamBaseUrl);
7511
- if (base === void 0) return void 0;
7512
- return `${base.protocol === "https:" ? "wss" : "ws"}://${base.host}${route}`;
7513
- }
7514
- //#endregion
7515
- //#region src/client/terminal.ts
7516
- async function request$1(method, body) {
7517
- const response = await fetch(`/dsh-vscode-terminal/api/${method}`, {
7518
- method: "POST",
7519
- headers: { "content-type": "application/json" },
7520
- body: JSON.stringify(body)
7521
- });
7522
- const text = await response.text();
7523
- let answer;
7524
- try {
7525
- answer = JSON.parse(text);
7526
- } catch {
7527
- throw new Error(`terminal request returned invalid JSON (${response.status})`);
7528
- }
7529
- if (!response.ok || answer.ok !== true) throw new ApiError(answer.error?.code ?? "terminal-request-failed", answer.error?.message ?? "terminal request failed");
7530
- return answer.value;
7531
- }
7532
- var SocketConnection = class {
7533
- socket;
7534
- listeners = /* @__PURE__ */ new Set();
7535
- errorListeners = /* @__PURE__ */ new Set();
7536
- closed = false;
7537
- queue = [];
7538
- constructor(sessionId, id) {
7539
- const url = websocketUrl(`/dsh-vscode-terminal/ws?sessionId=${encodeURIComponent(sessionId)}&id=${encodeURIComponent(id)}`);
7540
- if (url === void 0) throw new Error("无法连接终端:桌面端没有发布本地 Host 地址");
7541
- this.socket = new WebSocket(url);
7542
- this.socket.onopen = () => {
7543
- for (const value of this.queue) this.socket.send(value);
7544
- this.queue = [];
7545
- };
7546
- this.socket.onerror = () => {
7547
- for (const listener of this.errorListeners) listener("终端 WebSocket 连接失败");
7548
- };
7549
- this.socket.onclose = () => {
7550
- if (!this.closed) for (const listener of this.errorListeners) listener("终端 WebSocket 连接已断开");
7551
- };
7552
- this.socket.onmessage = (event) => {
7553
- try {
7554
- const value = JSON.parse(String(event.data));
7555
- if (typeof value.sequence !== "number" || typeof value.data !== "string") return;
7556
- for (const listener of this.listeners) listener({
7557
- sequence: value.sequence,
7558
- data: value.data,
7559
- replay: value.type === "replay"
7560
- });
7561
- } catch {}
7562
- };
7563
- }
7564
- send(value) {
7565
- const text = JSON.stringify(value);
7566
- if (this.socket.readyState === WebSocket.OPEN) this.socket.send(text);
7567
- else this.queue.push(text);
7568
- }
7569
- write(data) {
7570
- this.send({
7571
- type: "input",
7572
- data
7573
- });
7574
- }
7575
- resize(cols, rows) {
7576
- this.send({
7577
- type: "resize",
7578
- cols,
7579
- rows
7580
- });
7581
- }
7582
- close() {
7583
- this.closed = true;
7584
- this.socket.close();
7585
- }
7586
- subscribe(listener) {
7587
- this.listeners.add(listener);
7588
- return () => this.listeners.delete(listener);
7589
- }
7590
- subscribeError(listener) {
7591
- this.errorListeners.add(listener);
7592
- return () => this.errorListeners.delete(listener);
7593
- }
7594
- };
7595
- /** Identify automatic terminal-emulator replies that must not be echoed during replay. */
7596
- function isTerminalProtocolReply(data) {
7597
- return /^\x1b\](?:10|11|12);(?:rgb:[\da-f]{4}\/[\da-f]{4}\/[\da-f]{4}|#[\da-f]{6})(?:\x07|\x1b\\)$/i.test(data) || /^\x1b\[\??[\d;]*[cR]$/.test(data);
7598
- }
7599
- /** Maintain one terminal collection for a DSH session. */
7600
- var TerminalWorkspaceController = class {
7601
- sessionId;
7602
- snapshot = {
7603
- terminals: [],
7604
- loading: false
7605
- };
7606
- listeners = /* @__PURE__ */ new Set();
7607
- constructor(sessionId) {
7608
- this.sessionId = sessionId;
7609
- }
7610
- async refresh() {
7611
- this.set({
7612
- ...this.snapshot,
7613
- loading: true,
7614
- error: void 0
7615
- });
7616
- try {
7617
- this.set({
7618
- terminals: await withSessionRetry(() => request$1("list", { sessionId: this.sessionId })),
7619
- loading: false
7620
- });
7621
- return true;
7622
- } catch (error) {
7623
- this.set({
7624
- ...this.snapshot,
7625
- loading: false,
7626
- error: error instanceof Error ? error.message : String(error)
7627
- });
7628
- return false;
7629
- }
7630
- }
7631
- async create(title) {
7632
- try {
7633
- const terminal = await withSessionRetry(() => request$1("create", {
7634
- sessionId: this.sessionId,
7635
- ...title === void 0 ? {} : { title }
7636
- }));
7637
- await this.refresh();
7638
- return terminal;
7639
- } catch (error) {
7640
- this.set({
7641
- ...this.snapshot,
7642
- loading: false,
7643
- error: error instanceof Error ? error.message : String(error)
7644
- });
7645
- throw error;
7646
- }
7647
- }
7648
- async close(id) {
7649
- await withSessionRetry(() => request$1("close", {
7650
- sessionId: this.sessionId,
7651
- id
7652
- }));
7653
- await this.refresh();
7654
- }
7655
- async rename(id, title) {
7656
- await withSessionRetry(() => request$1("rename", {
7657
- sessionId: this.sessionId,
7658
- id,
7659
- title
7660
- }));
7661
- await this.refresh();
7662
- }
7663
- connect(id) {
7664
- return new SocketConnection(this.sessionId, id);
7665
- }
7666
- getSnapshot() {
7667
- return this.snapshot;
7668
- }
7669
- subscribe(listener) {
7670
- this.listeners.add(listener);
7671
- return () => this.listeners.delete(listener);
7672
- }
7673
- set(snapshot) {
7674
- this.snapshot = snapshot;
7675
- for (const listener of this.listeners) listener();
7676
- }
7677
- };
7678
- //#endregion
7679
7713
  //#region src/client/TerminalPanel.tsx
7680
7714
  const LIGHT_ANSI = {
7681
7715
  black: "#000000",
@@ -7710,102 +7744,109 @@ window.__ModuleLoader__.load({
7710
7744
  };
7711
7745
  }
7712
7746
  function XtermView({ workspace, id, colorScheme }) {
7747
+ const model = workspace.view(id);
7748
+ const state = (0, react.useSyncExternalStore)(model.state.subscribe, model.state.getSnapshot);
7713
7749
  const root = (0, react.useRef)(null);
7714
- const terminalRef = (0, react.useRef)();
7715
- const replaying = (0, react.useRef)(false);
7716
- const replayGuard = (0, react.useRef)();
7717
- const [error, setError] = (0, react.useState)();
7718
- (0, react.useEffect)(() => {
7750
+ const screen = (0, react.useRef)();
7751
+ const fit = (0, react.useRef)();
7752
+ const revision = (0, react.useRef)(0);
7753
+ const current = (0, react.useRef)(state);
7754
+ current.current = state;
7755
+ const measure = () => {
7756
+ const node = root.current;
7757
+ const dimensions = fit.current?.proposeDimensions();
7758
+ if (!node?.clientWidth || !node.clientHeight || !current.current.writable || dimensions === void 0) return;
7759
+ const cols = Math.min(dimensions.cols, current.current.environment?.maxCols ?? 500);
7760
+ const rows = Math.min(dimensions.rows, current.current.environment?.maxRows ?? 200);
7761
+ if (cols < 2 || rows < 1) return;
7762
+ screen.current?.resize(cols, rows);
7763
+ model.resize(cols, rows);
7764
+ };
7765
+ (0, react.useLayoutEffect)(() => {
7719
7766
  if (root.current === null) return;
7720
- const background = root.current === null ? void 0 : getComputedStyle(root.current).backgroundColor;
7721
7767
  const terminal = new import_xterm.Terminal({
7722
- convertEol: false,
7723
7768
  cursorBlink: true,
7724
7769
  fontFamily: "ui-monospace,SFMono-Regular,Menlo,Consolas,monospace",
7725
7770
  fontSize: 13,
7726
7771
  minimumContrastRatio: 4.5,
7727
7772
  theme: {
7728
7773
  ...terminalTheme(colorScheme),
7729
- ...background === void 0 ? {} : { background }
7730
- }
7774
+ background: getComputedStyle(root.current).backgroundColor
7775
+ },
7776
+ scrollback: 1e3
7731
7777
  });
7732
- terminalRef.current = terminal;
7733
- const fit = new import_addon_fit.FitAddon();
7734
- terminal.loadAddon(fit);
7778
+ const addon = new import_addon_fit.FitAddon();
7779
+ terminal.loadAddon(addon);
7735
7780
  terminal.open(root.current);
7736
- let connection;
7737
- try {
7738
- connection = workspace.connect(id);
7739
- } catch (exception) {
7740
- setError(exception instanceof Error ? exception.message : String(exception));
7741
- terminal.dispose();
7742
- return;
7743
- }
7744
- const output = connection.subscribe((value) => {
7745
- if (value.replay) {
7746
- replaying.current = true;
7747
- clearTimeout(replayGuard.current);
7748
- }
7749
- terminal.write(value.data, () => {
7750
- if (value.replay) replayGuard.current = setTimeout(() => {
7751
- replaying.current = false;
7752
- }, 250);
7753
- });
7754
- });
7755
- const connectionError = connection.subscribeError(setError);
7756
- const input = terminal.onData((data) => {
7757
- if (!replaying.current || !isTerminalProtocolReply(data)) connection.write(data);
7758
- });
7759
- const resize = terminal.onResize(({ cols, rows }) => connection.resize(cols, rows));
7760
- const observer = new ResizeObserver(() => {
7761
- try {
7762
- fit.fit();
7763
- } catch {}
7764
- });
7781
+ terminal.textarea?.setAttribute("aria-label", "终端输入");
7782
+ screen.current = terminal;
7783
+ fit.current = addon;
7784
+ revision.current = 0;
7785
+ const input = terminal.onData((data) => model.write(data));
7786
+ const observer = new ResizeObserver(measure);
7765
7787
  observer.observe(root.current);
7766
- try {
7767
- fit.fit();
7768
- terminal.focus();
7769
- } catch (exception) {
7770
- setError(exception instanceof Error ? exception.message : String(exception));
7771
- }
7772
7788
  return () => {
7773
- terminalRef.current = void 0;
7774
- clearTimeout(replayGuard.current);
7775
7789
  observer.disconnect();
7776
- connectionError();
7777
7790
  input.dispose();
7778
- resize.dispose();
7779
- output();
7780
- connection.close();
7781
7791
  terminal.dispose();
7792
+ screen.current = void 0;
7793
+ fit.current = void 0;
7782
7794
  };
7783
- }, [workspace, id]);
7795
+ }, [model]);
7796
+ (0, react.useEffect)(() => model.mount(), [model]);
7797
+ (0, react.useLayoutEffect)(() => {
7798
+ const terminal = screen.current;
7799
+ const render = state.render;
7800
+ if (terminal === void 0 || render === void 0 || render.revision <= revision.current) return;
7801
+ revision.current = render.revision;
7802
+ if (render.frame.type === "snapshot") {
7803
+ terminal.reset();
7804
+ terminal.resize(render.frame.info.cols, render.frame.info.rows);
7805
+ }
7806
+ terminal.write(render.frame.type === "snapshot" ? render.frame.screen : render.frame.data, () => model.acknowledge(render.revision));
7807
+ }, [state.render, model]);
7808
+ (0, react.useLayoutEffect)(() => {
7809
+ if (screen.current === void 0) return;
7810
+ screen.current.options.disableStdin = !state.writable;
7811
+ if (state.writable) {
7812
+ measure();
7813
+ screen.current.focus();
7814
+ } else if (state.info !== void 0) screen.current.resize(state.info.cols, state.info.rows);
7815
+ }, [
7816
+ state.writable,
7817
+ state.info?.cols,
7818
+ state.info?.rows,
7819
+ model
7820
+ ]);
7784
7821
  (0, react.useEffect)(() => {
7785
- if (terminalRef.current === void 0) return;
7786
- const background = root.current === null ? void 0 : getComputedStyle(root.current).backgroundColor;
7787
- terminalRef.current.options.theme = {
7822
+ if (screen.current !== void 0) screen.current.options.theme = {
7788
7823
  ...terminalTheme(colorScheme),
7789
- ...background === void 0 ? {} : { background }
7824
+ ...root.current === null ? {} : { background: getComputedStyle(root.current).backgroundColor }
7790
7825
  };
7791
7826
  }, [colorScheme]);
7792
- return error === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7793
- ref: root,
7794
- className: "dvw-terminal-host"
7795
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7796
- className: "dvw-terminal-error",
7797
- children: error
7827
+ const error = terminalIssue(state);
7828
+ const retry = state.phase === "failed" || state.phase === "disconnected";
7829
+ const readOnly = state.phase === "connected" && state.info?.state === "running" && !state.writable;
7830
+ const ended = state.info?.state === "exited";
7831
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
7832
+ className: "dvw-terminal-pane",
7833
+ children: [(error || retry || readOnly || ended || state.info === void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7834
+ className: "dvw-terminal-notice",
7835
+ role: "status",
7836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: error ?? (ended ? `进程已退出(${state.info?.exitCode ?? "—"})` : readOnly ? "其他页面正在控制此终端" : retry ? "终端连接已断开" : "正在启动终端…") }), (retry || readOnly) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7837
+ onClick: () => state.info === void 0 ? void model.refresh() : model.connect(),
7838
+ children: readOnly ? "接管输入" : "重新连接"
7839
+ })]
7840
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7841
+ ref: root,
7842
+ className: "dvw-terminal-host"
7843
+ })]
7798
7844
  });
7799
7845
  }
7800
- /** Render interactive terminal tabs in the workbench bottom panel. */
7801
- function TerminalPanel({ sessionId, colorScheme }) {
7802
- const workspaceRef = (0, react.useRef)();
7803
- if (workspaceRef.current?.sessionId !== sessionId) workspaceRef.current = {
7804
- sessionId,
7805
- value: new TerminalWorkspaceController(sessionId)
7806
- };
7807
- const workspace = workspaceRef.current.value;
7808
- const snapshot = (0, react.useSyncExternalStore)(workspace.subscribe.bind(workspace), workspace.getSnapshot.bind(workspace));
7846
+ /** Keep the incumbent terminal layout while using the official process and stream models. */
7847
+ function TerminalPanel({ workspace, colorScheme }) {
7848
+ const snapshot = (0, react.useSyncExternalStore)(workspace.subscribe, workspace.getSnapshot);
7849
+ const closeFailures = (0, react.useSyncExternalStore)(workspace.closeFailures.subscribe, workspace.closeFailures.getSnapshot);
7809
7850
  const [active, setActive] = (0, react.useState)();
7810
7851
  const [context, setContext] = (0, react.useState)();
7811
7852
  const [renaming, setRenaming] = (0, react.useState)();
@@ -7872,18 +7913,34 @@ window.__ModuleLoader__.load({
7872
7913
  children: [
7873
7914
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7874
7915
  className: "dvw-terminal-main",
7875
- children: [snapshot.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7876
- className: "dvw-terminal-error",
7877
- children: snapshot.error
7878
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7879
- className: "dvw-terminal-panes",
7880
- "data-split": shown.length > 1 || void 0,
7881
- children: shown.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XtermView, {
7882
- workspace,
7883
- id,
7884
- colorScheme
7885
- }, id))
7886
- })]
7916
+ children: [
7917
+ snapshot.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7918
+ className: "dvw-terminal-error",
7919
+ children: snapshot.error
7920
+ }),
7921
+ closeFailures.map((failure) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7922
+ className: "dvw-terminal-notice",
7923
+ role: "alert",
7924
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
7925
+ "终端“",
7926
+ failure.title,
7927
+ "”未能关闭:",
7928
+ failure.message
7929
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7930
+ onClick: () => workspace.retryClose(failure.id),
7931
+ children: "重试关闭"
7932
+ })]
7933
+ }, failure.id)),
7934
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7935
+ className: "dvw-terminal-panes",
7936
+ "data-split": shown.length > 1 || void 0,
7937
+ children: shown.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(XtermView, {
7938
+ workspace,
7939
+ id,
7940
+ colorScheme
7941
+ }, id))
7942
+ })
7943
+ ]
7887
7944
  }),
7888
7945
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7889
7946
  className: "dvw-terminal-resizer",
@@ -7992,6 +8049,61 @@ window.__ModuleLoader__.load({
7992
8049
  });
7993
8050
  }
7994
8051
  //#endregion
8052
+ //#region src/client/api.ts
8053
+ var ApiError = class extends Error {
8054
+ code;
8055
+ constructor(code, message) {
8056
+ super(message);
8057
+ this.code = code;
8058
+ }
8059
+ };
8060
+ /** Back off while DSH publishes the current session after startup; every view shares this retry. */
8061
+ function sessionRetryDelay(error, attempt) {
8062
+ if (!(error instanceof ApiError) || error.code !== "session-not-found" || attempt >= 10) return void 0;
8063
+ return Math.min(250 * (attempt + 1), 1e3);
8064
+ }
8065
+ function wait(ms, signal) {
8066
+ return new Promise((resolve, reject) => {
8067
+ const abort = () => {
8068
+ clearTimeout(timer);
8069
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("aborted"));
8070
+ };
8071
+ const timer = setTimeout(() => {
8072
+ signal?.removeEventListener("abort", abort);
8073
+ resolve();
8074
+ }, ms);
8075
+ if (signal?.aborted === true) {
8076
+ abort();
8077
+ return;
8078
+ }
8079
+ signal?.addEventListener("abort", abort, { once: true });
8080
+ });
8081
+ }
8082
+ async function request$1(method, body, signal) {
8083
+ const response = await fetch(`/dsh-vscode/api/${method}`, {
8084
+ method: "POST",
8085
+ headers: { "content-type": "application/json" },
8086
+ body: JSON.stringify(body),
8087
+ signal
8088
+ });
8089
+ const answer = await response.json();
8090
+ if (!response.ok || !answer.ok) throw new ApiError(answer.error?.code ?? "request-failed", answer.error?.message ?? `request failed (${response.status})`);
8091
+ return answer.value;
8092
+ }
8093
+ async function api(method, body, signal) {
8094
+ return withSessionRetry(() => request$1(method, body, signal), signal);
8095
+ }
8096
+ /** Retry an operation while DSH is still publishing the selected session. */
8097
+ async function withSessionRetry(operation, signal) {
8098
+ for (let attempt = 0;; attempt++) try {
8099
+ return await operation();
8100
+ } catch (error) {
8101
+ const delay = sessionRetryDelay(error, attempt);
8102
+ if (delay === void 0) throw error;
8103
+ await wait(delay, signal);
8104
+ }
8105
+ }
8106
+ //#endregion
7995
8107
  //#region node_modules/.pnpm/vscode-icons-js@11.6.1/node_modules/vscode-icons-js/dist/generated/FileExtensions1ToIcon.js
7996
8108
  var require_FileExtensions1ToIcon = /* @__PURE__ */ __commonJSMin(((exports) => {
7997
8109
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -14683,6 +14795,196 @@ Please report this to https://github.com/markedjs/marked.`, e) {
14683
14795
  });
14684
14796
  }
14685
14797
  //#endregion
14798
+ //#region src/client/dshPanels.ts
14799
+ /** Locate the central panel DSH renders when a global panel (Plugins, market…) is open. */
14800
+ function findDshGlobalPanel(documentRoot = document) {
14801
+ return documentRoot.querySelector("[data-global-panel]");
14802
+ }
14803
+ const DSH_PANEL_TITLES = {
14804
+ plugins: "插件",
14805
+ "dsh-market": "插件市场",
14806
+ automations: "自动化任务",
14807
+ ssh: "SSH"
14808
+ };
14809
+ /** Tab-style label for an embedded DSH panel; unknown ids keep their own name. */
14810
+ function dshPanelLabel(id) {
14811
+ return DSH_PANEL_TITLES[id] ?? id;
14812
+ }
14813
+ //#endregion
14814
+ //#region src/client/DshPanelPage.tsx
14815
+ /**
14816
+ * Pin a DSH global panel over the workbench editor while its tab is active.
14817
+ * The official page keeps its own DOM and state; switching to a file only
14818
+ * hides it, so returning to the tab does not rebuild the page.
14819
+ */
14820
+ function DshPanelPage({ panelId, active }) {
14821
+ const activeRef = (0, react.useRef)(active);
14822
+ activeRef.current = active;
14823
+ (0, react.useLayoutEffect)(() => {
14824
+ let panel = null;
14825
+ let frame = 0;
14826
+ const clear = (node) => {
14827
+ node.removeAttribute("data-dvw-dsh-page");
14828
+ node.removeAttribute("data-dvw-dsh-hidden");
14829
+ for (const property of [
14830
+ "left",
14831
+ "top",
14832
+ "width",
14833
+ "height"
14834
+ ]) node.style.removeProperty(property);
14835
+ };
14836
+ const place = () => {
14837
+ const next = findDshGlobalPanel();
14838
+ if (next !== panel) {
14839
+ if (panel !== null) clear(panel);
14840
+ panel = next;
14841
+ panel?.setAttribute("data-dvw-dsh-page", "");
14842
+ }
14843
+ if (panel === null) return;
14844
+ panel.toggleAttribute("data-dvw-dsh-hidden", !activeRef.current);
14845
+ if (!activeRef.current) return;
14846
+ const editor = document.querySelector(".dvw-editor");
14847
+ if (editor === null) return;
14848
+ const rect = editor.getBoundingClientRect();
14849
+ if (rect.width <= 0 || rect.height <= 0) return;
14850
+ panel.style.setProperty("left", `${Math.round(rect.left)}px`);
14851
+ panel.style.setProperty("top", `${Math.round(rect.top)}px`);
14852
+ panel.style.setProperty("width", `${Math.round(rect.width)}px`);
14853
+ panel.style.setProperty("height", `${Math.round(rect.height)}px`);
14854
+ };
14855
+ const schedule = () => {
14856
+ if (frame !== 0) return;
14857
+ frame = requestAnimationFrame(() => {
14858
+ frame = 0;
14859
+ place();
14860
+ });
14861
+ };
14862
+ place();
14863
+ const resize = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
14864
+ const editor = document.querySelector(".dvw-editor");
14865
+ if (editor !== null) resize?.observe(editor);
14866
+ window.addEventListener("resize", schedule);
14867
+ const observer = new MutationObserver((records) => {
14868
+ if (records.every((record) => record.target instanceof Element && record.target.closest(".dvw-overlay") !== null)) return;
14869
+ schedule();
14870
+ });
14871
+ observer.observe(document.body, {
14872
+ childList: true,
14873
+ subtree: true
14874
+ });
14875
+ return () => {
14876
+ observer.disconnect();
14877
+ resize?.disconnect();
14878
+ cancelAnimationFrame(frame);
14879
+ window.removeEventListener("resize", schedule);
14880
+ if (panel !== null) clear(panel);
14881
+ };
14882
+ }, [panelId, active]);
14883
+ return null;
14884
+ }
14885
+ //#endregion
14886
+ //#region src/client/DshBrowserPage.tsx
14887
+ /** Present the original browser DOM without moving React-owned nodes or rebuilding webviews. */
14888
+ const PALETTE = {
14889
+ "--dsw-alias-bg-base": "--dvw-surface-1",
14890
+ "--dsw-alias-bg-layer-1": "--dvw-surface-2",
14891
+ "--dsw-alias-bg-layer-2": "--dvw-surface-3",
14892
+ "--dsw-alias-label-primary": "--dvw-text",
14893
+ "--dsw-alias-label-secondary": "--dvw-text-2",
14894
+ "--dsw-alias-label-tertiary": "--dvw-text-3",
14895
+ "--dsw-alias-border-l2": "--dvw-border",
14896
+ "--dsw-alias-border-l3": "--dvw-border-soft",
14897
+ "--dsw-alias-interactive-bg-hover": "--dvw-hover",
14898
+ "--dsw-alias-interactive-bg-active": "--dvw-active"
14899
+ };
14900
+ function DshBrowserPage({ sessionId, tabId, active, colorScheme }) {
14901
+ (0, react.useLayoutEffect)(() => {
14902
+ let page;
14903
+ let restore;
14904
+ let frame = 0;
14905
+ const place = () => {
14906
+ const next = [...document.querySelectorAll("div[data-sidebar-right-tab]")].find((node) => node.dataset.sidebarRightTab === tabId && node.closest("[data-sidebar-right-session]")?.getAttribute("data-sidebar-right-session") === sessionId);
14907
+ if (next !== page) {
14908
+ restore?.();
14909
+ page = next;
14910
+ if (page !== void 0) {
14911
+ const node = page;
14912
+ const popover = node.getAttribute("popover");
14913
+ const styles = [
14914
+ "left",
14915
+ "top",
14916
+ "width",
14917
+ "height",
14918
+ "color-scheme",
14919
+ ...Object.keys(PALETTE)
14920
+ ].map((property) => [
14921
+ property,
14922
+ node.style.getPropertyValue(property),
14923
+ node.style.getPropertyPriority(property)
14924
+ ]);
14925
+ node.setAttribute("popover", "manual");
14926
+ node.setAttribute("data-dvw-browser-page", "");
14927
+ restore = () => {
14928
+ if (node.matches(":popover-open")) node.hidePopover();
14929
+ if (popover === null) node.removeAttribute("popover");
14930
+ else node.setAttribute("popover", popover);
14931
+ node.removeAttribute("data-dvw-browser-page");
14932
+ for (const [property, value, priority] of styles) node.style.setProperty(property, value, priority);
14933
+ };
14934
+ }
14935
+ }
14936
+ if (page === void 0) return;
14937
+ const rect = document.querySelector(".dvw-editor")?.getBoundingClientRect();
14938
+ if (!(active && rect !== void 0 && rect.width > 0 && rect.height > 0)) {
14939
+ if (page.matches(":popover-open")) page.hidePopover();
14940
+ return;
14941
+ }
14942
+ for (const [property, value] of Object.entries({
14943
+ left: rect.left,
14944
+ top: rect.top,
14945
+ width: rect.width,
14946
+ height: rect.height
14947
+ })) page.style.setProperty(property, `${Math.round(value)}px`, "important");
14948
+ const overlay = document.querySelector(".dvw-overlay");
14949
+ if (overlay !== null) {
14950
+ const palette = getComputedStyle(overlay);
14951
+ for (const [alias, token] of Object.entries(PALETTE)) page.style.setProperty(alias, palette.getPropertyValue(token));
14952
+ }
14953
+ page.style.setProperty("color-scheme", colorScheme === "dark" ? "dark" : "light");
14954
+ if (!page.matches(":popover-open")) page.showPopover();
14955
+ };
14956
+ const schedule = () => {
14957
+ if (frame === 0) frame = requestAnimationFrame(() => {
14958
+ frame = 0;
14959
+ place();
14960
+ });
14961
+ };
14962
+ place();
14963
+ const resize = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
14964
+ const editor = document.querySelector(".dvw-editor");
14965
+ if (editor !== null) resize?.observe(editor);
14966
+ const observer = new MutationObserver(schedule);
14967
+ observer.observe(document.body, {
14968
+ childList: true,
14969
+ subtree: true
14970
+ });
14971
+ window.addEventListener("resize", schedule);
14972
+ return () => {
14973
+ observer.disconnect();
14974
+ resize?.disconnect();
14975
+ window.removeEventListener("resize", schedule);
14976
+ cancelAnimationFrame(frame);
14977
+ restore?.();
14978
+ };
14979
+ }, [
14980
+ sessionId,
14981
+ tabId,
14982
+ active,
14983
+ colorScheme
14984
+ ]);
14985
+ return null;
14986
+ }
14987
+ //#endregion
14686
14988
  //#region src/client/ActivityBar.tsx
14687
14989
  /** Keep known ids in saved order, appending newly added views at the end. */
14688
14990
  function normalizeActivityOrder(value, known) {
@@ -15077,6 +15379,54 @@ Please report this to https://github.com/markedjs/marked.`, e) {
15077
15379
  }), document.body);
15078
15380
  }
15079
15381
  //#endregion
15382
+ //#region src/client/websocket.ts
15383
+ const WEB_PAGE_PROTOCOLS = /* @__PURE__ */ new Set([
15384
+ "http:",
15385
+ "https:",
15386
+ "blob:",
15387
+ "data:",
15388
+ "about:",
15389
+ "filesystem:"
15390
+ ]);
15391
+ /** Parse a shell-published base only when it is a credential-free HTTP(S) origin. */
15392
+ function networkBase(value) {
15393
+ if (typeof value !== "string" || value === "") return void 0;
15394
+ let url;
15395
+ try {
15396
+ url = new URL(value);
15397
+ } catch {
15398
+ return;
15399
+ }
15400
+ if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
15401
+ if (url.username !== "" || url.password !== "" || url.host === "") return void 0;
15402
+ return {
15403
+ protocol: url.protocol,
15404
+ host: url.host
15405
+ };
15406
+ }
15407
+ /** Read the Host origin published by the DSH Desktop shell before application boot. */
15408
+ function transportStreamBaseUrl(globals = globalThis) {
15409
+ const transport = globals.__DSH_TRANSPORT__;
15410
+ return typeof transport?.streamBaseUrl === "string" ? transport.streamBaseUrl : void 0;
15411
+ }
15412
+ /**
15413
+ * Resolve an absolute workbench route to a WebSocket URL.
15414
+ *
15415
+ * Network pages keep their own origin. Application-delivered pages such as
15416
+ * `dsh-app://app/` have no WebSocket transport of their own, so they must use
15417
+ * the local Host origin published by the desktop shell.
15418
+ */
15419
+ function websocketUrl(path, location = globalThis.location, streamBaseUrl = transportStreamBaseUrl()) {
15420
+ const route = path.startsWith("/") ? path : `/${path}`;
15421
+ if (WEB_PAGE_PROTOCOLS.has(location.protocol)) {
15422
+ if (location.host === "") return void 0;
15423
+ return `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}${route}`;
15424
+ }
15425
+ const base = networkBase(streamBaseUrl);
15426
+ if (base === void 0) return void 0;
15427
+ return `${base.protocol === "https:" ? "wss" : "ws"}://${base.host}${route}`;
15428
+ }
15429
+ //#endregion
15080
15430
  //#region src/client/lsp.ts
15081
15431
  /** Minimal Language Server Protocol client that rides a WebSocket bridge to a host-spawned server. */
15082
15432
  /** LSP CompletionItemKind and SymbolKind share the numeric layout 1–25; Monaco's enums differ, so map explicitly. */
@@ -49735,6 +50085,14 @@ section.${c}>footer { z-index: 1; }
49735
50085
  } else workbench.toggleBottomPanel("terminal");
49736
50086
  return true;
49737
50087
  }
50088
+ /** Match the official desktop browser shortcut while the workbench owns focus. */
50089
+ function handleBrowserShortcut(event, workbench) {
50090
+ if (!workbench.getSnapshot().visible || event.isComposing || !(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey || event.code !== "KeyT") return false;
50091
+ event.preventDefault();
50092
+ event.stopImmediatePropagation();
50093
+ window.dispatchEvent(new Event("dvw-browser-new"));
50094
+ return true;
50095
+ }
49738
50096
  //#endregion
49739
50097
  //#region src/client/workspaceState.ts
49740
50098
  /** Remove editor state that belongs to the previous workspace. */
@@ -51565,7 +51923,7 @@ section.${c}>footer { z-index: 1; }
51565
51923
  const ExplorerView = (0, react.memo)(Explorer);
51566
51924
  const SearchView = (0, react.memo)(Search);
51567
51925
  const GitView = (0, react.memo)(Git);
51568
- function Workbench({ service, sessionId, cwd, navigation }) {
51926
+ function Workbench({ service, browser, sessionId, cwd, navigation, dshPanelId: hostDshPanelId, onOpenDshPanel, onRestoreConversation, onCloseDshPanel }) {
51569
51927
  const [snapshot, setSnapshot] = (0, react.useState)(() => service.getSnapshot());
51570
51928
  const [panels, setPanels] = (0, react.useState)(() => service.getPanels());
51571
51929
  (0, react.useEffect)(() => service.subscribe(() => {
@@ -51575,6 +51933,9 @@ section.${c}>footer { z-index: 1; }
51575
51933
  const [workspace, setWorkspace] = (0, react.useState)();
51576
51934
  const [tabs, setTabs] = (0, react.useState)([]);
51577
51935
  const [active, setActive] = (0, react.useState)();
51936
+ const [dshTabActive, setDshTabActive] = (0, react.useState)(hostDshPanelId !== void 0);
51937
+ const [dshTabId, setDshTabId] = (0, react.useState)(hostDshPanelId);
51938
+ const dshPanelId = dshTabId;
51578
51939
  const [error, setError] = (0, react.useState)();
51579
51940
  const [panelHeight, setPanelHeight] = (0, react.useState)(260);
51580
51941
  const [sideWidth, setSideWidth] = (0, react.useState)(210);
@@ -51595,8 +51956,96 @@ section.${c}>footer { z-index: 1; }
51595
51956
  return Number.isFinite(saved) && saved >= 11 && saved <= 20 ? saved : 14;
51596
51957
  });
51597
51958
  const [tabMenu, setTabMenu] = (0, react.useState)();
51959
+ const [browserSnapshot, setBrowserSnapshot] = (0, react.useState)(() => browser?.getSnapshot() ?? {
51960
+ available: false,
51961
+ tabs: []
51962
+ });
51963
+ const [browserId, setBrowserId] = (0, react.useState)();
51964
+ const browserTabs = browserSnapshot.tabs.filter((tab) => tab.sessionId === sessionId);
51965
+ const previousBrowserIds = (0, react.useRef)(new Set(browserTabs.map((tab) => tab.id)));
51966
+ (0, react.useEffect)(() => {
51967
+ if (browser === void 0) return;
51968
+ setBrowserSnapshot(browser.getSnapshot());
51969
+ return browser.subscribe(() => setBrowserSnapshot(browser.getSnapshot()));
51970
+ }, [browser]);
51971
+ (0, react.useEffect)(() => {
51972
+ const next = new Set(browserTabs.map((tab) => tab.id));
51973
+ const added = browserTabs.find((tab) => !previousBrowserIds.current.has(tab.id));
51974
+ previousBrowserIds.current = next;
51975
+ if (added !== void 0 && snapshot.visible) {
51976
+ setBrowserId(added.id);
51977
+ setDshTabActive(false);
51978
+ } else if (browserId !== void 0 && !next.has(browserId)) setBrowserId(void 0);
51979
+ }, [
51980
+ browserSnapshot,
51981
+ sessionId,
51982
+ browserId,
51983
+ snapshot.visible
51984
+ ]);
51985
+ const browserRequest = (0, react.useRef)();
51986
+ (0, react.useEffect)(() => () => browserRequest.current?.abort(), [
51987
+ browser,
51988
+ sessionId,
51989
+ snapshot.visible
51990
+ ]);
51991
+ const newBrowser = async () => {
51992
+ if (browser === void 0 || sessionId === void 0) return;
51993
+ setDshTabActive(false);
51994
+ onRestoreConversation?.();
51995
+ browserRequest.current?.abort();
51996
+ const request = new AbortController();
51997
+ browserRequest.current = request;
51998
+ try {
51999
+ setBrowserId(await browser.open(sessionId, request.signal));
52000
+ } catch (error) {
52001
+ if (!request.signal.aborted) setError(error instanceof Error ? error.message : String(error));
52002
+ }
52003
+ };
52004
+ (0, react.useEffect)(() => {
52005
+ if (browserId !== void 0 && !dshTabActive) browser?.focus(browserId);
52006
+ }, [
52007
+ browser,
52008
+ browserId,
52009
+ dshTabActive
52010
+ ]);
52011
+ (0, react.useEffect)(() => {
52012
+ if (!snapshot.visible) return;
52013
+ const open = () => void newBrowser();
52014
+ window.addEventListener("dvw-browser-new", open);
52015
+ return () => window.removeEventListener("dvw-browser-new", open);
52016
+ }, [
52017
+ browser,
52018
+ sessionId,
52019
+ snapshot.visible
52020
+ ]);
52021
+ (0, react.useEffect)(() => {
52022
+ if (hostDshPanelId !== void 0) setBrowserId(void 0);
52023
+ }, [hostDshPanelId]);
51598
52024
  const tabsRef = (0, react.useRef)(tabs);
51599
52025
  tabsRef.current = tabs;
52026
+ const dshTabActiveRef = (0, react.useRef)(dshTabActive);
52027
+ dshTabActiveRef.current = dshTabActive;
52028
+ const dshTabIdRef = (0, react.useRef)(dshTabId);
52029
+ dshTabIdRef.current = dshTabId;
52030
+ const hostDshPanelIdRef = (0, react.useRef)(hostDshPanelId);
52031
+ hostDshPanelIdRef.current = hostDshPanelId;
52032
+ const openDshPanelRef = (0, react.useRef)(onOpenDshPanel);
52033
+ openDshPanelRef.current = onOpenDshPanel;
52034
+ const restoreConversationRef = (0, react.useRef)(onRestoreConversation);
52035
+ restoreConversationRef.current = onRestoreConversation;
52036
+ const leaveDshPanel = (0, react.useCallback)(() => {
52037
+ setBrowserId(void 0);
52038
+ const wasActive = dshTabActiveRef.current;
52039
+ setDshTabActive(false);
52040
+ if (wasActive) restoreConversationRef.current?.();
52041
+ }, []);
52042
+ (0, react.useCallback)(() => {
52043
+ setBrowserId(void 0);
52044
+ const id = dshTabIdRef.current;
52045
+ if (id === void 0) return;
52046
+ setDshTabActive(true);
52047
+ if (hostDshPanelIdRef.current !== id) openDshPanelRef.current?.(id);
52048
+ }, []);
51600
52049
  const [settingsSection, setSettingsSection] = (0, react.useState)("appearance");
51601
52050
  const tabListRef = (0, react.useRef)(null);
51602
52051
  const workspaceRequests = (0, react.useRef)(new WorkspaceRequestScope());
@@ -51645,6 +52094,28 @@ section.${c}>footer { z-index: 1; }
51645
52094
  (0, react.useEffect)(() => localStorage.setItem("dvw:primary-side-visible", primarySideVisible ? "open" : "closed"), [primarySideVisible]);
51646
52095
  (0, react.useEffect)(() => localStorage.setItem("dvw:status-bar-visible", statusVisible ? "open" : "closed"), [statusVisible]);
51647
52096
  (0, react.useEffect)(() => localStorage.setItem("dvw:font-size", String(fontSize)), [fontSize]);
52097
+ (0, react.useEffect)(() => {
52098
+ if (hostDshPanelId !== void 0) {
52099
+ setDshTabId(hostDshPanelId);
52100
+ setDshTabActive(true);
52101
+ return;
52102
+ }
52103
+ if (dshTabActiveRef.current) setDshTabId(void 0);
52104
+ }, [hostDshPanelId]);
52105
+ (0, react.useEffect)(() => {
52106
+ if (dshTabActive || dshTabId === void 0 || hostDshPanelId === void 0) return;
52107
+ restoreConversationRef.current?.();
52108
+ }, [
52109
+ dshTabActive,
52110
+ dshTabId,
52111
+ hostDshPanelId
52112
+ ]);
52113
+ (0, react.useEffect)(() => {
52114
+ if (!dshTabActive) return;
52115
+ const id = dshTabIdRef.current;
52116
+ if (id === void 0 || hostDshPanelId === id) return;
52117
+ openDshPanelRef.current?.(id);
52118
+ }, [dshTabActive, hostDshPanelId]);
51648
52119
  (0, react.useEffect)(() => {
51649
52120
  if (tabMenu === void 0) return;
51650
52121
  const close = () => setTabMenu(void 0);
@@ -51704,6 +52175,7 @@ section.${c}>footer { z-index: 1; }
51704
52175
  highlight,
51705
52176
  ...line !== void 0 ? { preview: false } : {}
51706
52177
  }]);
52178
+ leaveDshPanel();
51707
52179
  setActive(path);
51708
52180
  };
51709
52181
  (0, react.useEffect)(() => {
@@ -52129,8 +52601,9 @@ section.${c}>footer { z-index: 1; }
52129
52601
  diffMode: "inline",
52130
52602
  diffSourcePath: source
52131
52603
  }]);
52604
+ leaveDshPanel();
52132
52605
  setActive(key);
52133
- }, [openPath]);
52606
+ }, [openPath, leaveDshPanel]);
52134
52607
  (0, react.useEffect)(() => {
52135
52608
  const listener = (event) => void open(event.detail);
52136
52609
  window.addEventListener("dvw-open-workspace-path", listener);
@@ -52209,6 +52682,7 @@ section.${c}>footer { z-index: 1; }
52209
52682
  "search",
52210
52683
  "git",
52211
52684
  "terminal",
52685
+ "browser",
52212
52686
  "chat"
52213
52687
  ];
52214
52688
  const [activityOrder, setActivityOrder] = (0, react.useState)(() => normalizeActivityOrder(readActivityJSON("dvw:activity-order"), ACTIVITY_IDS));
@@ -52257,6 +52731,17 @@ section.${c}>footer { z-index: 1; }
52257
52731
  ] })
52258
52732
  })]
52259
52733
  });
52734
+ const showingDshPanel = dshTabId !== void 0 && dshTabActive && !settingsOpen;
52735
+ const closeDshPanel = () => {
52736
+ setDshTabActive(false);
52737
+ setDshTabId(void 0);
52738
+ onCloseDshPanel?.();
52739
+ };
52740
+ const openFromView = (path, line, column, highlight) => {
52741
+ leaveDshPanel();
52742
+ openPath(path, line, column, highlight);
52743
+ };
52744
+ const chatShown = chatVisible && !settingsOpen && !showingDshPanel;
52260
52745
  const markdown = activeTab !== void 0 && !activeTab.path.startsWith("diff:") && /\.md$/i.test(activeTab.path);
52261
52746
  const diffMarkdown = activeTab?.diff !== void 0 && /\.md$/i.test(activeTab.diffSourcePath ?? "");
52262
52747
  const markdownPreview = markdown || diffMarkdown && activeTab?.preview === true;
@@ -52265,7 +52750,7 @@ section.${c}>footer { z-index: 1; }
52265
52750
  className: "dvw-overlay",
52266
52751
  "data-dvw-theme": colorScheme,
52267
52752
  "data-dvw-theme-name": theme,
52268
- "data-dvw-chat": chatVisible || void 0,
52753
+ "data-dvw-chat": chatShown || void 0,
52269
52754
  "data-dvw-primary-side": primarySideVisible || void 0,
52270
52755
  "data-dvw-status": statusVisible || void 0,
52271
52756
  "data-dvw-bottom-panel": snapshot.bottomPanel || void 0,
@@ -52280,17 +52765,21 @@ section.${c}>footer { z-index: 1; }
52280
52765
  onDropCapture: suppressAttachmentDrop,
52281
52766
  children: [
52282
52767
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkbenchTitlebar, { name: workspace?.name }),
52768
+ dshTabId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DshPanelPage, {
52769
+ panelId: dshTabId,
52770
+ active: showingDshPanel
52771
+ }),
52283
52772
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DshConversationPanel, {
52284
- visible: chatVisible && !settingsOpen,
52773
+ visible: chatShown,
52285
52774
  width: chatWidth,
52286
52775
  statusHeight: statusVisible ? 30 : 6
52287
52776
  }),
52288
52777
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DshConversationTheme, {
52289
- visible: chatVisible,
52778
+ visible: chatShown,
52290
52779
  colorScheme,
52291
52780
  followDsh: false
52292
52781
  }),
52293
- chatVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
52782
+ chatShown && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
52294
52783
  className: "dvw-chat",
52295
52784
  children: [
52296
52785
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelResizer, {
@@ -52595,6 +53084,14 @@ section.${c}>footer { z-index: 1; }
52595
53084
  disabled: !terminalAvailable,
52596
53085
  onActivate: () => toggleTerminal(service)
52597
53086
  },
53087
+ {
53088
+ id: "browser",
53089
+ label: "新建浏览器",
53090
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "globe" }),
53091
+ active: browserId !== void 0,
53092
+ disabled: !browserSnapshot.available || sessionId === void 0,
53093
+ onActivate: () => void newBrowser()
53094
+ },
52598
53095
  {
52599
53096
  id: "chat",
52600
53097
  label: chatVisible ? "关闭 DSH 对话栏 (Ctrl+Alt+B)" : "显示 DSH 对话栏 (Ctrl+Alt+B)",
@@ -52636,19 +53133,22 @@ section.${c}>footer { z-index: 1; }
52636
53133
  sessionId,
52637
53134
  cwd: workspace.cwd,
52638
53135
  selected: active,
52639
- onOpen: openPath,
53136
+ onOpen: openFromView,
52640
53137
  onAddToDsh: addEntryToDsh
52641
53138
  }),
52642
53139
  " ",
52643
53140
  snapshot.view === "search" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchView, {
52644
53141
  sessionId,
52645
- onOpen: openPath
53142
+ onOpen: openFromView
52646
53143
  }),
52647
53144
  " ",
52648
53145
  snapshot.view === "git" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitView, {
52649
53146
  sessionId,
52650
53147
  preferences: commitPreferences,
52651
- onDiff: openDiff
53148
+ onDiff: (path, diff, title, source) => {
53149
+ leaveDshPanel();
53150
+ openDiff(path, diff, title, source);
53151
+ }
52652
53152
  })
52653
53153
  ]
52654
53154
  }),
@@ -52681,57 +53181,113 @@ section.${c}>footer { z-index: 1; }
52681
53181
  children: [
52682
53182
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
52683
53183
  className: "dvw-tabs",
52684
- "data-empty": tabs.length === 0 || void 0,
52685
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
53184
+ "data-empty": tabs.length === 0 && dshPanelId === void 0 && browserTabs.length === 0 || void 0,
53185
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
52686
53186
  className: "dvw-tab-list",
52687
53187
  ref: tabListRef,
52688
- children: tabs.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
52689
- className: "dvw-tab",
52690
- "data-active": tab.path === active,
52691
- title: tab.diffSourcePath ?? tab.path,
52692
- onClick: () => setActive(tab.path),
52693
- onAuxClick: (event) => {
52694
- if (event.button === 1) {
52695
- event.preventDefault();
52696
- close(tab);
52697
- }
52698
- },
52699
- onMouseDown: (event) => {
52700
- if (event.button === 1) event.preventDefault();
52701
- },
52702
- onContextMenu: (event) => {
52703
- event.preventDefault();
52704
- setActive(tab.path);
52705
- setTabMenu({
52706
- x: event.clientX,
52707
- y: event.clientY,
52708
- path: tab.path
52709
- });
52710
- },
52711
- children: [
52712
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileIcon, {
52713
- name: tabLabel(tab),
52714
- className: "dvw-tab-icon"
52715
- }),
52716
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
52717
- className: "dvw-tab-name",
52718
- children: [tab.dirty ? "● " : "", tabLabel(tab)]
52719
- }),
52720
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
52721
- className: "dvw-tab-close",
52722
- onClick: (event) => {
52723
- event.stopPropagation();
53188
+ children: [
53189
+ browserTabs.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
53190
+ className: "dvw-tab",
53191
+ "data-dvw-browser-tab": tab.id,
53192
+ "data-active": browserId === tab.id && !dshTabActive,
53193
+ title: tab.title,
53194
+ onClick: () => {
53195
+ leaveDshPanel();
53196
+ setBrowserId(tab.id);
53197
+ },
53198
+ children: [
53199
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "globe" }),
53200
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53201
+ className: "dvw-tab-name",
53202
+ children: tab.title
53203
+ }),
53204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53205
+ className: "dvw-tab-close",
53206
+ onClick: (event) => {
53207
+ event.stopPropagation();
53208
+ browser?.close(tab.sessionId, tab.id);
53209
+ },
53210
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
53211
+ })
53212
+ ]
53213
+ }, tab.id)),
53214
+ dshPanelId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
53215
+ className: "dvw-tab",
53216
+ "data-active": dshTabActive,
53217
+ "data-dvw-dsh-tab": "",
53218
+ title: `${dshPanelLabel(dshPanelId)}(DSH 官方页面)`,
53219
+ onClick: () => setDshTabActive(true),
53220
+ children: [
53221
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dvw-dsh-tab-mark" }),
53222
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53223
+ className: "dvw-tab-name",
53224
+ children: dshPanelLabel(dshPanelId)
53225
+ }),
53226
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53227
+ className: "dvw-tab-close",
53228
+ onClick: (event) => {
53229
+ event.stopPropagation();
53230
+ closeDshPanel();
53231
+ },
53232
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
53233
+ })
53234
+ ]
53235
+ }),
53236
+ tabs.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
53237
+ className: "dvw-tab",
53238
+ "data-active": tab.path === active && !dshTabActive && browserId === void 0,
53239
+ title: tab.diffSourcePath ?? tab.path,
53240
+ onClick: () => {
53241
+ setBrowserId(void 0);
53242
+ setDshTabActive(false);
53243
+ setActive(tab.path);
53244
+ },
53245
+ onAuxClick: (event) => {
53246
+ if (event.button === 1) {
53247
+ event.preventDefault();
52724
53248
  close(tab);
52725
- },
52726
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
52727
- })
52728
- ]
52729
- }, tab.path))
53249
+ }
53250
+ },
53251
+ onMouseDown: (event) => {
53252
+ if (event.button === 1) event.preventDefault();
53253
+ },
53254
+ onContextMenu: (event) => {
53255
+ event.preventDefault();
53256
+ setBrowserId(void 0);
53257
+ setDshTabActive(false);
53258
+ setActive(tab.path);
53259
+ setTabMenu({
53260
+ x: event.clientX,
53261
+ y: event.clientY,
53262
+ path: tab.path
53263
+ });
53264
+ },
53265
+ children: [
53266
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileIcon, {
53267
+ name: tabLabel(tab),
53268
+ className: "dvw-tab-icon"
53269
+ }),
53270
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
53271
+ className: "dvw-tab-name",
53272
+ children: [tab.dirty ? "● " : "", tabLabel(tab)]
53273
+ }),
53274
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53275
+ className: "dvw-tab-close",
53276
+ onClick: (event) => {
53277
+ event.stopPropagation();
53278
+ close(tab);
53279
+ },
53280
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "close" })
53281
+ })
53282
+ ]
53283
+ }, tab.path))
53284
+ ]
52730
53285
  })
52731
53286
  }),
52732
53287
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
52733
53288
  className: "dvw-editor",
52734
53289
  "data-outline": markdownPreview && outlineVisible && headings.length > 0,
53290
+ "data-browser-active": browserId !== void 0 || void 0,
52735
53291
  children: [
52736
53292
  error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
52737
53293
  className: "dvw-error",
@@ -52830,7 +53386,22 @@ section.${c}>footer { z-index: 1; }
52830
53386
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
52831
53387
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "DSH Workbench" }),
52832
53388
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}),
52833
- "从资源管理器或搜索结果打开文件"
53389
+ "从资源管理器或搜索结果打开文件",
53390
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
53391
+ className: "dvw-start-tools",
53392
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
53393
+ onClick: () => {
53394
+ if (snapshot.bottomPanel !== "terminal") service.toggleBottomPanel("terminal");
53395
+ queueMicrotask(() => service.runCommand("terminal.new"));
53396
+ },
53397
+ disabled: !terminalAvailable,
53398
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "terminal" }), "新建终端"]
53399
+ }), browser !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
53400
+ onClick: () => void newBrowser(),
53401
+ disabled: !browserSnapshot.available,
53402
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "globe" }), "浏览器"]
53403
+ })]
53404
+ })
52834
53405
  ] })
52835
53406
  }) : activeTab.diff ? diffMarkdown && activeTab.preview ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownPreview, {
52836
53407
  source: activeTab.diff.modified,
@@ -52947,6 +53518,12 @@ section.${c}>footer { z-index: 1; }
52947
53518
  })
52948
53519
  ]
52949
53520
  }),
53521
+ sessionId !== void 0 && browserTabs.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DshBrowserPage, {
53522
+ sessionId,
53523
+ tabId: tab.id,
53524
+ colorScheme,
53525
+ active: browserId === tab.id && !dshTabActive && !settingsOpen && !switcherOpen && snapshot.visible
53526
+ }, tab.id)),
52950
53527
  snapshot.bottomPanel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
52951
53528
  className: "dvw-bottom",
52952
53529
  style: { height: panelHeight },
@@ -53037,8 +53614,8 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
53037
53614
  .dvw-overlay[data-dvw-theme=dark]{--dvw-base:#191a1b;--dvw-surface-1:#121314;--dvw-surface-2:#202122;--dvw-surface-3:#252526;--dvw-side:#191a1b;--dvw-tab-active:#2a2b2c;--dvw-gutter:#191a1b;--dvw-graph-ref:#3794ff;--dvw-text:#bfbfbf;--dvw-text-2:#8c8c8c;--dvw-text-3:#6e6e6e;--dvw-border:#2a2b2c;--dvw-border-soft:#242526;--dvw-hover:#ffffff12;--dvw-active:#ffffff1f;--dvw-accent:#307e9f;--dvw-error:#e06c75;--dvw-success:#98c379;--dvw-warn:#e5c07b}
53038
53615
  .dvw-overlay[data-dvw-theme=light]{--dvw-base:#f9fafb;--dvw-surface-1:#fff;--dvw-surface-2:#fff;--dvw-surface-3:#fff;--dvw-side:#f9fafb;--dvw-tab-active:#e8e8e8;--dvw-gutter:#f9fafb;--dvw-graph-ref:#0063d3;--dvw-text:#1f1f1f;--dvw-text-2:#616161;--dvw-text-3:#8a8a8a;--dvw-border:#e5e5e5;--dvw-border-soft:#ececec;--dvw-hover:#0000000d;--dvw-active:#00000014;--dvw-accent:#0078d4;--dvw-error:#e51400;--dvw-success:#388a34;--dvw-warn:#bf8803;color-scheme:light}
53039
53616
  .dvw-overlay *{box-sizing:border-box}.dvw-overlay ::-webkit-scrollbar{width:10px;height:10px}.dvw-overlay ::-webkit-scrollbar-track{background:transparent}.dvw-overlay ::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--dvw-text) 20%,transparent);border:2px solid transparent;background-clip:padding-box;border-radius:6px}.dvw-overlay ::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--dvw-text) 32%,transparent);background-clip:padding-box}.dvw-overlay ::-webkit-scrollbar-corner{background:transparent}.dvw-activity{grid-row:1/3;background:var(--dvw-base);border:1px solid var(--dvw-border);border-radius:8px 0 0 8px;display:flex;flex-direction:column;align-items:center;overflow:hidden}.dvw-activity-menu{min-width:220px}.dvw-activity-empty{flex:1}.dvw-activity-bottom{width:100%;margin-top:auto;display:flex;flex-direction:column;align-items:center}.dvw-menu-separator{height:1px;margin:4px 0;background:var(--dvw-border-soft)}.dvw-menu-check{display:inline-block;width:14px;flex:none;color:var(--dvw-text)}.dvw-act{touch-action:none;user-select:none}.dvw-act[data-dragging=true]{opacity:.35}.dvw-activity-ghost{position:fixed;z-index:120;width:48px;height:48px;display:grid;place-items:center;pointer-events:none;border-radius:8px;background:var(--dvw-tab-active);color:var(--dvw-text);font-size:22px;box-shadow:0 8px 22px rgba(0,0,0,.3);transform:translate(-50%,-50%) scale(1.08);opacity:.95}.dvw-activity-ghost .codicon{font-size:18px}.dvw-act{position:relative;width:48px;height:48px;padding:0;border:0;background:transparent;color:var(--dvw-text-3);font-size:22px;cursor:pointer;display:grid;place-items:center}.dvw-act::before{content:"";position:absolute;left:50%;top:50%;width:32px;height:32px;border-radius:6px;background:transparent;transform:translate(-50%,-50%)}.dvw-act .codicon,.dvw-act svg{position:relative;z-index:1}.dvw-act .codicon{font-size:18px}.dvw-act:hover,.dvw-act[data-active=true]{color:var(--dvw-text)}.dvw-act:hover::before{background:var(--dvw-hover)}.dvw-act[data-active=true]::before{background:var(--dvw-tab-active)}.dvw-act.dsh{margin-top:auto}.dvw-side{grid-column:2;grid-row:1/3;min-width:0;width:100%;background:var(--dvw-side);border-top:1px solid var(--dvw-border);border-bottom:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border);border-top-right-radius:8px;border-bottom-right-radius:8px;overflow:hidden;display:flex;flex-direction:column}.dvw-side-resizer{grid-column:2;grid-row:1/3;justify-self:end;width:4px;height:100%;z-index:4;cursor:ew-resize;transform:translateX(2px);touch-action:none}.dvw-side-resizer:hover,.dvw-side-resizer:focus-visible,.dvw-side-resizer[data-dragging=true]{background:var(--dvw-accent)}.dvw-side-head{height:35px;display:flex;align-items:center;padding:0 12px;text-transform:uppercase;font-size:11px;letter-spacing:.4px}.dvw-toolbar{margin-left:auto;display:flex;gap:2px}.dvw-icon{border:0;background:transparent;color:var(--dvw-text-2);border-radius:4px;min-width:24px;height:24px;display:inline-grid;place-items:center;cursor:pointer}.dvw-icon:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-icon:disabled{opacity:.45;cursor:default}.dvw-file-picker{display:none}.dvw-tree,.dvw-results,.dvw-git{overflow:auto;flex:1}.dvw-tree[data-drop-target=true]{outline:1px dashed var(--dvw-accent);outline-offset:-3px}.dvw-tree-row{display:flex;align-items:center;height:22px;padding-right:6px;cursor:pointer;white-space:nowrap}.dvw-tree-row:hover{background:var(--dvw-hover)}.dvw-tree-row[data-selected=true],.dvw-tree-row[data-drop-target=true]{background:var(--dvw-active)}.dvw-chevron{width:16px;flex:none;text-align:center;color:var(--dvw-text-3)}.dvw-file-icon{width:16px;height:16px;flex:none;display:block;object-fit:contain;color:var(--dvw-text-2)}.dvw-file-icon.folder{color:var(--dvw-accent)}.dvw-tree-name{overflow:hidden;text-overflow:ellipsis}.dvw-tree-rename,.dvw-tree-create-input{min-width:0;flex:1;height:20px;border:1px solid var(--dvw-accent);border-radius:2px;background:var(--dvw-surface-1);color:var(--dvw-text);padding:0 3px;font:inherit;letter-spacing:0;outline:0}.dvw-tree-create-row{cursor:text}.dvw-tree-create-row:hover{background:transparent}.dvw-tree-create-input::placeholder{color:var(--dvw-text-3)}.dvw-tree-create-input[aria-invalid=true]{border-color:var(--dvw-error)}.dvw-tree-create-error{min-height:20px;padding-top:2px;padding-right:6px;padding-bottom:3px;color:var(--dvw-error);font-size:11px;line-height:15px;white-space:normal;overflow-wrap:anywhere}.dvw-tree-status{margin-left:auto;padding-left:6px;font-size:11px;font-weight:600;color:var(--dvw-warn)}.dvw-tree-status[data-status=M]{color:var(--dvw-warn)}.dvw-tree-status[data-status=A]{color:var(--dvw-success)}.dvw-tree-status[data-status=D]{color:var(--dvw-error)}.dvw-context-menu{position:fixed;z-index:40;min-width:148px;padding:4px;border:1px solid var(--dvw-border);border-radius:5px;background:var(--dvw-surface-3);box-shadow:0 6px 20px rgba(0,0,0,.3)}.dvw-context-menu button{width:100%;height:28px;border:0;border-radius:3px;background:transparent;color:var(--dvw-text);display:flex;align-items:center;gap:8px;padding:0 8px;text-align:left;font:inherit;cursor:pointer}.dvw-context-menu button:hover{background:var(--dvw-hover)}.dvw-context-menu .dvw-context-delete{color:var(--dvw-error)}.dvw-tab-menu{min-width:220px}.dvw-tab-menu button{justify-content:space-between;gap:24px}.dvw-tab-menu button span{color:var(--dvw-text-3);font-size:11px}.dvw-tab-menu button:disabled{opacity:.45;cursor:default}.dvw-tab-menu button:disabled:hover{background:transparent}
53040
- .dvw-main{grid-column:3;grid-row:1;position:relative;display:flex;min-width:0;min-height:0;flex-direction:column;background:var(--dvw-surface-1);border-left:1px solid var(--dvw-border);border-top:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border);border-bottom:1px solid var(--dvw-border);border-radius:8px;margin-left:4px;overflow:hidden}.dvw-tabs{height:35px;background:linear-gradient(to top,var(--dvw-border) 0 1px,var(--dvw-base) 1px 100%);display:flex;overflow:hidden}.dvw-tabs[data-empty]{display:none}.dvw-tab{position:relative;height:35px;margin:0;min-width:110px;max-width:260px;border:0;border-right:1px solid var(--dvw-border-soft);border-radius:8px 8px 0 0;background:transparent;color:var(--dvw-text-2);padding:0 8px 0 12px;display:flex;align-items:center;gap:6px;cursor:pointer;flex:none;white-space:nowrap}.dvw-tab:hover{background:var(--dvw-hover)}.dvw-tab[data-active=true]{background:var(--dvw-surface-1);color:var(--dvw-text);border-top:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border)}.dvw-tab-icon{width:16px;height:16px;flex:none;object-fit:contain}.dvw-tab-close{width:16px;height:16px;flex:none;display:grid;place-items:center;border-radius:3px;opacity:0}.dvw-tab-close:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-tab[data-active=true] .dvw-tab-close,.dvw-tab:hover .dvw-tab-close{opacity:1}.dvw-tab-name{overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;text-align:left;white-space:nowrap}.dvw-editor{position:relative;flex:1;min-height:0}.dvw-editor-toolbar{position:absolute;z-index:3;top:0;right:0;left:0;height:35px;display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:0 10px;border-bottom:1px solid var(--dvw-border-soft);background:var(--dvw-surface-1)}.dvw-editor-body{position:absolute;inset:0;min-height:0}.dvw-editor-body[data-toolbar=true]{top:35px}.dvw-mode-button{height:27px;padding:0 10px;display:flex;align-items:center;gap:6px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--dvw-text-2);cursor:pointer}.dvw-mode-button:hover{background:var(--dvw-hover)}.dvw-mode-button[data-active=true]{border-color:var(--dvw-border);background:var(--dvw-active);color:var(--dvw-text)}.dvw-empty{height:100%;display:grid;place-items:center;color:var(--dvw-text-3);text-align:center}.dvw-empty strong{font-size:24px;color:var(--dvw-text-2)}.dvw-message{padding:12px;color:var(--dvw-warn)}.dvw-message button{margin-left:10px;padding:2px 10px;border:1px solid var(--dvw-border);border-radius:4px;background:var(--dvw-surface-2);color:var(--dvw-text);font:inherit;cursor:pointer}.dvw-message button:hover{background:var(--dvw-hover)}
53041
- .dvw-bottom-resizer{height:4px;cursor:ns-resize}.dvw-bottom-resizer:hover{background:var(--dvw-accent)}.dvw-bottom-reveal{position:absolute;left:0;right:0;bottom:0;height:6px;z-index:4;cursor:ns-resize}.dvw-bottom-reveal::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:transparent}.dvw-bottom-reveal:hover::after{background:var(--dvw-accent)}.dvw-bottom{grid-column:3;grid-row:2;height:260px;min-height:120px;border-top:1px solid var(--dvw-border);border-left:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border);border-bottom:1px solid var(--dvw-border);border-radius:8px;margin-top:4px;margin-left:4px;background:var(--dvw-base);display:flex;flex-direction:column}.dvw-bottom-head{height:35px;display:flex;align-items:center;gap:14px;padding:0 12px}.dvw-bottom-title{border:0;border-bottom:1px solid var(--dvw-text);background:transparent;color:var(--dvw-text);height:35px}.dvw-bottom-body{flex:1;min-height:0}.dvw-status{grid-column:1/4;grid-row:3;background:var(--dvw-base);color:var(--dvw-text);display:flex;align-items:center;padding:6px 8px 0;gap:18px;font-size:12px}.dvw-theme-control{position:relative;margin-left:auto}.dvw-theme-trigger{height:22px;min-width:112px;display:flex;align-items:center;gap:7px;padding:0 8px;border:0;border-radius:5px;background:color-mix(in srgb,#000 17%,transparent);color:#fff;cursor:pointer}.dvw-theme-trigger:hover{background:color-mix(in srgb,#000 27%,transparent)}.dvw-theme-orb{width:13px;height:13px;border:2px solid #fff;border-radius:50%;background:linear-gradient(90deg,#191a1b 50%,#fff 50%)}.dvw-theme-orb.dark{background:#191a1b}.dvw-theme-orb.light{background:#fff}.dvw-theme-menu{position:absolute;right:0;bottom:29px;width:224px;padding:6px;border:1px solid var(--dvw-border);border-radius:10px;background:var(--dvw-surface-3);box-shadow:0 10px 35px rgba(0,0,0,.28);color:var(--dvw-text)}.dvw-theme-menu button{width:100%;min-height:48px;border:0;border-radius:7px;background:transparent;color:inherit;display:grid;grid-template-columns:30px 1fr 20px;align-items:center;text-align:left;padding:6px;cursor:pointer}.dvw-theme-menu button:hover,.dvw-theme-menu button[aria-checked=true]{background:var(--dvw-hover)}.dvw-theme-menu strong,.dvw-theme-menu small{display:block}.dvw-theme-menu small{color:var(--dvw-text-3);margin-top:2px}.dvw-theme-swatch{width:22px;height:22px;border-radius:6px;border:1px solid var(--dvw-border);background:linear-gradient(135deg,#191a1b 0 50%,#fff 50%)}.dvw-theme-swatch.dark{background:#191a1b}.dvw-theme-swatch.light{background:#fff}
53617
+ .dvw-main{grid-column:3;grid-row:1;position:relative;display:flex;min-width:0;min-height:0;flex-direction:column;background:var(--dvw-surface-1);border-left:1px solid var(--dvw-border);border-top:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border);border-bottom:1px solid var(--dvw-border);border-radius:8px;margin-left:3px;overflow:hidden}.dvw-tabs{height:35px;background:linear-gradient(to top,var(--dvw-border) 0 1px,var(--dvw-base) 1px 100%);display:flex;overflow:hidden}.dvw-tabs[data-empty]{display:none}.dvw-tab{position:relative;height:35px;margin:0;min-width:110px;max-width:260px;border:0;border-right:1px solid var(--dvw-border-soft);border-radius:8px 8px 0 0;background:transparent;color:var(--dvw-text-2);padding:0 8px 0 12px;display:flex;align-items:center;gap:6px;cursor:pointer;flex:none;white-space:nowrap}.dvw-tab:hover{background:var(--dvw-hover)}.dvw-tab[data-active=true]{background:var(--dvw-surface-1);color:var(--dvw-text);border-top:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border)}.dvw-tab-icon{width:16px;height:16px;flex:none;object-fit:contain}.dvw-tab-close{width:16px;height:16px;flex:none;display:grid;place-items:center;border-radius:3px;opacity:0}.dvw-tab-close:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-tab[data-active=true] .dvw-tab-close,.dvw-tab:hover .dvw-tab-close{opacity:1}.dvw-tab-name{overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;text-align:left;white-space:nowrap}.dvw-editor{position:relative;flex:1;min-height:0}.dvw-editor-toolbar{position:absolute;z-index:3;top:0;right:0;left:0;height:35px;display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:0 10px;border-bottom:1px solid var(--dvw-border-soft);background:var(--dvw-surface-1)}.dvw-editor-body{position:absolute;inset:0;min-height:0}.dvw-editor-body[data-toolbar=true]{top:35px}.dvw-mode-button{height:27px;padding:0 10px;display:flex;align-items:center;gap:6px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--dvw-text-2);cursor:pointer}.dvw-mode-button:hover{background:var(--dvw-hover)}.dvw-mode-button[data-active=true]{border-color:var(--dvw-border);background:var(--dvw-active);color:var(--dvw-text)}.dvw-empty{height:100%;display:grid;place-items:center;color:var(--dvw-text-3);text-align:center}.dvw-empty strong{font-size:24px;color:var(--dvw-text-2)}.dvw-message{padding:12px;color:var(--dvw-warn)}.dvw-message button{margin-left:10px;padding:2px 10px;border:1px solid var(--dvw-border);border-radius:4px;background:var(--dvw-surface-2);color:var(--dvw-text);font:inherit;cursor:pointer}.dvw-message button:hover{background:var(--dvw-hover)}
53618
+ .dvw-bottom-resizer{height:4px;cursor:ns-resize}.dvw-bottom-resizer:hover{background:var(--dvw-accent)}.dvw-bottom-reveal{position:absolute;left:0;right:0;bottom:0;height:6px;z-index:4;cursor:ns-resize}.dvw-bottom-reveal::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:transparent}.dvw-bottom-reveal:hover::after{background:var(--dvw-accent)}.dvw-bottom{grid-column:3;grid-row:2;height:260px;min-height:120px;border-top:1px solid var(--dvw-border);border-left:1px solid var(--dvw-border);border-right:1px solid var(--dvw-border);border-bottom:1px solid var(--dvw-border);border-radius:8px;margin-top:4px;margin-left:3px;background:var(--dvw-base);display:flex;flex-direction:column}.dvw-bottom-head{height:35px;display:flex;align-items:center;gap:14px;padding:0 12px}.dvw-bottom-title{border:0;border-bottom:1px solid var(--dvw-text);background:transparent;color:var(--dvw-text);height:35px}.dvw-bottom-body{flex:1;min-height:0}.dvw-status{grid-column:1/4;grid-row:3;background:var(--dvw-base);color:var(--dvw-text);display:flex;align-items:center;padding:6px 8px 0;gap:18px;font-size:12px}.dvw-theme-control{position:relative;margin-left:auto}.dvw-theme-trigger{height:22px;min-width:112px;display:flex;align-items:center;gap:7px;padding:0 8px;border:0;border-radius:5px;background:color-mix(in srgb,#000 17%,transparent);color:#fff;cursor:pointer}.dvw-theme-trigger:hover{background:color-mix(in srgb,#000 27%,transparent)}.dvw-theme-orb{width:13px;height:13px;border:2px solid #fff;border-radius:50%;background:linear-gradient(90deg,#191a1b 50%,#fff 50%)}.dvw-theme-orb.dark{background:#191a1b}.dvw-theme-orb.light{background:#fff}.dvw-theme-menu{position:absolute;right:0;bottom:29px;width:224px;padding:6px;border:1px solid var(--dvw-border);border-radius:10px;background:var(--dvw-surface-3);box-shadow:0 10px 35px rgba(0,0,0,.28);color:var(--dvw-text)}.dvw-theme-menu button{width:100%;min-height:48px;border:0;border-radius:7px;background:transparent;color:inherit;display:grid;grid-template-columns:30px 1fr 20px;align-items:center;text-align:left;padding:6px;cursor:pointer}.dvw-theme-menu button:hover,.dvw-theme-menu button[aria-checked=true]{background:var(--dvw-hover)}.dvw-theme-menu strong,.dvw-theme-menu small{display:block}.dvw-theme-menu small{color:var(--dvw-text-3);margin-top:2px}.dvw-theme-swatch{width:22px;height:22px;border-radius:6px;border:1px solid var(--dvw-border);background:linear-gradient(135deg,#191a1b 0 50%,#fff 50%)}.dvw-theme-swatch.dark{background:#191a1b}.dvw-theme-swatch.light{background:#fff}
53042
53619
  .dvw-search-form{padding:8px;display:grid;gap:6px}.dvw-input,.dvw-textarea,.dvw-select{width:100%;border:1px solid var(--dvw-border);border-radius:2px;background:var(--dvw-surface-2);color:var(--dvw-text);padding:3px 7px;outline:none}.dvw-input:focus,.dvw-textarea:focus,.dvw-select:focus{border-color:var(--dvw-accent)}.dvw-options{display:flex;align-items:center;gap:8px;font-size:11px}.dvw-result-file{height:22px;display:flex;align-items:center;gap:4px;padding:0 8px;font-weight:600}.dvw-result{height:22px;align-items:center;padding:0 8px 0 20px;cursor:pointer;display:flex;gap:8px}.dvw-result:hover{background:var(--dvw-hover)}.dvw-line{color:var(--dvw-text-3);min-width:30px;text-align:right}.dvw-preview{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-search-hit{background:color-mix(in srgb,var(--dvw-accent) 28%,transparent);border-radius:2px}
53043
53620
  .dvw-git-head{padding:7px 10px;border-bottom:1px solid var(--dvw-border);display:flex;align-items:center;gap:8px;position:relative}.dvw-branch{min-width:0;border:0;background:transparent;color:var(--dvw-text);font:inherit;font-weight:600;display:flex;align-items:center;gap:5px;cursor:pointer}.dvw-branch-caret{margin-left:2px;font-size:14px;color:var(--dvw-text-3)}.dvw-branch-menu{position:absolute;z-index:12;top:calc(100% + 2px);left:6px;right:6px;padding:4px;border:1px solid var(--dvw-border);border-radius:8px;background:var(--dvw-surface-2);box-shadow:0 10px 28px rgba(0,0,0,.28);color:var(--dvw-text)}.dvw-branch-current{padding:4px 8px;color:var(--dvw-text-3);font-size:11px}.dvw-branch-list{max-height:min(300px,45vh);overflow:auto}.dvw-branch-list button{width:100%;height:26px;display:flex;align-items:center;gap:6px;border:0;border-radius:4px;background:transparent;color:var(--dvw-text);padding:0 8px;text-align:left;font:inherit;cursor:pointer}.dvw-branch-list button:hover{background:var(--dvw-hover)}.dvw-branch-list button[data-active=true]{background:var(--dvw-active)}.dvw-branch-list button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-branch-worktree{margin-left:auto;flex:none;padding:1px 5px;border-radius:8px;background:var(--dvw-surface-3);color:var(--dvw-text-3);font-size:10px;line-height:14px}.dvw-branch-create{display:flex;gap:4px;align-items:center;margin-top:4px;padding-top:6px;border-top:1px solid var(--dvw-border-soft)}.dvw-branch-create .dvw-input{flex:1;min-width:0}.dvw-branch-create-button{flex:none;width:24px;min-width:24px;height:24px;border:1px solid var(--dvw-border);border-radius:4px}.dvw-commit{display:flex;flex-direction:column;gap:6px;padding:9px}.dvw-commit-actions{display:grid;grid-template-columns:1fr auto;gap:6px}.dvw-commit-button{height:31px;border:0;border-radius:6px;background:color-mix(in srgb,var(--dvw-accent) 24%,var(--dvw-surface-2));color:var(--dvw-text);display:flex;align-items:center;justify-content:center;gap:7px;cursor:pointer}.dvw-commit-button:hover{background:color-mix(in srgb,var(--dvw-accent) 34%,var(--dvw-surface-2))}.dvw-commit-button:disabled{opacity:.45;cursor:default}.dvw-commit-ai{height:31px;border:1px solid var(--dvw-border);border-radius:6px;background:var(--dvw-surface-2);color:var(--dvw-text-2);display:flex;align-items:center;justify-content:center;gap:6px;font:inherit;cursor:pointer}.dvw-commit-ai:hover:not(:disabled){background:var(--dvw-hover);color:var(--dvw-text)}.dvw-commit-ai:disabled{opacity:.45;cursor:default}.dvw-group-head{height:30px;display:flex;align-items:center;gap:5px;padding:0 6px 0 9px;font-weight:600;position:sticky;top:0;background:var(--dvw-side);z-index:1}.dvw-group-head:hover{background:var(--dvw-hover)}.dvw-group-head .dvw-count{margin-left:auto;min-width:20px;height:20px;border-radius:10px;display:grid;place-items:center;background:var(--dvw-surface-3);color:var(--dvw-text-2);font-size:11px}.dvw-change{display:flex;align-items:center;height:22px;padding:0 5px 0 19px;gap:5px;cursor:pointer}.dvw-change:hover{background:var(--dvw-hover)}.dvw-change .dvw-icon{opacity:0}.dvw-change:hover .dvw-icon,.dvw-change:focus-within .dvw-icon{opacity:1}.dvw-change-path{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.dvw-badge{font-size:11px;color:var(--dvw-warn)}.dvw-graph{border-top:1px solid var(--dvw-border-soft);margin-top:8px;padding-top:3px}.dvw-graph-list{padding-bottom:8px}.dvw-graph-row{width:100%;height:38px;border:0;background:transparent;color:var(--dvw-text);display:flex;align-items:center;padding:0 7px;text-align:left;cursor:pointer;overflow:hidden}.dvw-graph-row:hover{background:var(--dvw-hover)}.dvw-graph-svg{flex:none;align-self:stretch}.dvw-graph-line{fill:none;stroke-width:1px;stroke-linecap:round;vector-effect:non-scaling-stroke}.dvw-graph-node{stroke:var(--dvw-side);stroke-width:2px;fill:var(--dvw-side)}.dvw-graph-copy{display:flex;min-width:0;flex:1;flex-direction:column;justify-content:center}.dvw-graph-subject{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-graph-meta{font-size:11px;color:var(--dvw-text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-ref{max-width:82px;margin-left:4px;padding:1px 5px;border:1px solid color-mix(in srgb,var(--dvw-accent) 50%,transparent);border-radius:9px;color:var(--dvw-accent);font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-error{padding:8px;color:var(--dvw-error);white-space:pre-wrap}.dvw-diff{margin:0;height:100%;overflow:auto;padding:8px 0;background:var(--dvw-surface-1);font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre}.dvw-diff-line{display:block;min-height:18px;padding:0 14px}.dvw-diff-line.add{background:color-mix(in srgb,var(--dvw-success) 15%,transparent);color:var(--dvw-success)}.dvw-diff-line.remove{background:color-mix(in srgb,var(--dvw-error) 13%,transparent);color:var(--dvw-error)}.dvw-diff-line.hunk{background:color-mix(in srgb,var(--dvw-accent) 12%,transparent);color:var(--dvw-accent)}.dvw-diff-line.meta{color:var(--dvw-text-3)}.dvw-diff-empty{padding:24px;color:var(--dvw-text-3)}
53044
53621
  .dvw-tab-list{display:flex;min-width:0;flex:1;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none}.dvw-tab-list::-webkit-scrollbar{display:none}.dvw-monaco-diff{position:absolute;inset:0}.dvw-diff-toolbar{justify-content:flex-end}.dvw-group-head{gap:2px;padding-left:2px}.dvw-group-toggle{min-width:0;height:30px;flex:1;border:0;border-radius:5px;background:transparent;color:var(--dvw-text);font:inherit;font-weight:600;display:flex;align-items:center;gap:5px;padding:0 7px;cursor:pointer;text-align:left}.dvw-group-toggle:hover{background:var(--dvw-hover)}.dvw-graph-entry[data-expanded=true]{background:color-mix(in srgb,var(--dvw-accent) 5%,transparent)}.dvw-commit-files{padding:1px 0 5px 26px}.dvw-commit-file{width:100%;height:22px;border:0;background:transparent;color:var(--dvw-text);display:flex;align-items:center;gap:6px;padding:0 7px;text-align:left;cursor:pointer}.dvw-commit-file:hover{background:var(--dvw-hover)}.dvw-commit-file-name{white-space:nowrap}.dvw-commit-file-dir{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dvw-text-3);font-size:11px}
@@ -53051,7 +53628,7 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
53051
53628
  .dvw-overlay[data-dvw-chat=true]:not([data-dvw-primary-side=true]){grid-template-columns:48px 0 minmax(0,1fr) var(--dvw-chat-width)}
53052
53629
  .dvw-overlay:not([data-dvw-primary-side=true]) .dvw-side{visibility:hidden;border-right:0}.dvw-overlay:not([data-dvw-primary-side=true]) .dvw-activity{border-radius:8px}.dvw-overlay:not([data-dvw-primary-side=true]) .dvw-side-resizer{display:none}
53053
53630
  .dvw-overlay:not([data-dvw-status=true]){grid-template-rows:minmax(0,1fr) auto 6px}.dvw-overlay:not([data-dvw-status=true]) .dvw-status{display:none}
53054
- .dvw-overlay[data-dvw-chat=true] .dvw-status{grid-column:1/-1;padding-right:calc(var(--dvw-chat-width) + 8px)}
53631
+ .dvw-overlay[data-dvw-chat=true] .dvw-status{grid-column:1/-1;padding-right:calc(var(--dvw-chat-width) + 6px)}
53055
53632
  .dvw-chat{grid-column:4;grid-row:1/3;min-width:0;background:var(--dvw-gutter);border-left:1px solid var(--dvw-border);position:relative}
53056
53633
  .dvw-chat-head{height:36px;display:flex;align-items:center;justify-content:space-between;padding:0 6px 0 12px;border-bottom:1px solid var(--dvw-border);background:var(--dvw-base);font-weight:600}
53057
53634
  .dvw-chat-head>span{display:flex;align-items:center;gap:7px}.dvw-chat-body{height:calc(100% - 36px);background:var(--dvw-base)}
@@ -53067,7 +53644,7 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
53067
53644
  .dvw-chat{border-left-color:transparent!important}
53068
53645
  .dvw-side-resizer::after,.dvw-chat-resizer::after{background:transparent;left:3px;width:4px}
53069
53646
  .dvw-side-resizer:hover::after,.dvw-side-resizer:focus-visible::after,.dvw-side-resizer[data-dragging=true]::after,.dvw-chat-resizer:hover::after,.dvw-chat-resizer:focus-visible::after,.dvw-chat-resizer[data-dragging=true]::after{background:var(--dvw-accent)}
53070
- [data-dvw-conversation-panel]{position:fixed!important;top:calc(var(--dvw-titlebar-height) + 6px)!important;right:4px!important;bottom:var(--dvw-status-height,30px)!important;left:auto!important;width:calc(var(--dvw-chat-width) - 8px)!important;height:auto!important;min-width:0!important;z-index:21!important;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:8px!important;overflow:hidden!important;box-shadow:none!important}
53647
+ [data-dvw-conversation-panel]{position:fixed!important;top:calc(var(--dvw-titlebar-height) + 6px)!important;right:3px!important;bottom:var(--dvw-status-height,30px)!important;left:auto!important;width:calc(var(--dvw-chat-width) - 6px)!important;height:auto!important;min-width:0!important;z-index:21!important;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:8px!important;overflow:hidden!important;box-shadow:none!important}
53071
53648
  html[data-platform=darwin] [data-dvw-conversation-panel]{top:var(--dvw-titlebar-height)!important}
53072
53649
  body[data-dvw-palette=light] [data-dvw-conversation-panel]{--dsw-alias-bg-base:#f9fafb;--dsw-specific-sidebar-fill:#f9fafb;--dsw-alias-border-l2:#e5e5e5;background:#f9fafb!important}body[data-dvw-palette=dark] [data-dvw-conversation-panel]{--dsw-alias-bg-base:#191a1b;--dsw-specific-sidebar-fill:#191a1b;--dsw-alias-bg-layer-1:#202122;--dsw-alias-bg-layer-2:#252526;--dsw-alias-bg-layer-3:#2a2b2c;--dsw-alias-label-primary:#bfbfbf;--dsw-alias-label-secondary:#8c8c8c;--dsw-alias-label-tertiary:#6e6e6e;--dsw-alias-border-l2:#2a2b2c;--dsw-alias-border-l3:#303132;--dsw-alias-interactive-bg-hover:#ffffff12;--dsw-alias-interactive-bg-active:#ffffff1f}
53073
53650
  body[data-dvw-workbench-active] [data-sidebar-right-expand],body[data-dvw-conversation-active] [data-sidebar-right-expand]{display:none!important}
@@ -53089,7 +53666,7 @@ body[data-dvw-workbench-active] [data-sidebar-toggle]{display:none!important}
53089
53666
  .dvw-settings-themes{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.dvw-settings-themes button{height:56px;display:grid;grid-template-columns:28px 1fr 18px;align-items:center;gap:7px;padding:0 9px;border:1px solid var(--dvw-border);border-radius:7px;background:var(--dvw-base);color:var(--dvw-text);text-align:left;cursor:pointer}.dvw-settings-themes button:hover{background:var(--dvw-hover)}.dvw-settings-themes button[data-active=true]{border-color:var(--dvw-accent);background:color-mix(in srgb,var(--dvw-accent) 10%,var(--dvw-base))}
53090
53667
  .dvw-setting-check{height:36px;display:flex;align-items:center;gap:9px;border-bottom:1px solid var(--dvw-border-soft);cursor:pointer}.dvw-setting-check input{accent-color:var(--dvw-accent)}.dvw-setting-range{display:grid;grid-template-columns:145px 1fr 52px;align-items:center;gap:10px;min-height:42px}.dvw-setting-range input{accent-color:var(--dvw-accent)}.dvw-setting-range output{text-align:right;color:var(--dvw-text-3);font-variant-numeric:tabular-nums}
53091
53668
  .dvw-setting-choice{display:grid;grid-template-columns:145px 1fr;align-items:center;gap:10px;min-height:42px}.dvw-setting-choice>span{color:var(--dvw-text-2);font-size:12px}.dvw-setting-segment{display:inline-flex;justify-self:start;padding:2px;border:1px solid var(--dvw-border);border-radius:6px;background:var(--dvw-base)}.dvw-setting-segment button{min-width:64px;height:26px;border:0;border-radius:4px;background:transparent;color:var(--dvw-text-2);font:inherit;font-size:12px;cursor:pointer}.dvw-setting-segment button:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-setting-segment button[data-active=true]{background:var(--dvw-active);color:var(--dvw-text)}
53092
- @media(max-width:900px){.dvw-overlay[data-dvw-chat=true]{--dvw-chat-effective:max(440px,min(var(--dvw-chat-width),calc(100vw - 254px)));grid-template-columns:44px min(210px,var(--dvw-side-width),max(0px,calc(100vw - 164px - var(--dvw-chat-effective)))) minmax(0,1fr) var(--dvw-chat-effective)}.dvw-overlay[data-dvw-chat=true] .dvw-status{padding-right:calc(var(--dvw-chat-effective) + 8px)}.dvw-chat-resizer{right:calc(var(--dvw-chat-effective) - 5px)}[data-dvw-conversation-panel]{width:calc(max(440px,min(var(--dvw-chat-width),calc(100vw - 254px))) - 8px)!important}}
53669
+ @media(max-width:900px){.dvw-overlay[data-dvw-chat=true]{--dvw-chat-effective:max(440px,min(var(--dvw-chat-width),calc(100vw - 254px)));grid-template-columns:44px min(210px,var(--dvw-side-width),max(0px,calc(100vw - 164px - var(--dvw-chat-effective)))) minmax(0,1fr) var(--dvw-chat-effective)}.dvw-overlay[data-dvw-chat=true] .dvw-status{padding-right:calc(var(--dvw-chat-effective) + 6px)}.dvw-chat-resizer{right:calc(var(--dvw-chat-effective) - 5px)}[data-dvw-conversation-panel]{width:calc(max(440px,min(var(--dvw-chat-width),calc(100vw - 254px))) - 6px)!important}}
53093
53670
  .dvw-setting-textarea{width:100%;min-height:84px;resize:vertical;padding:8px 10px;border:1px solid var(--dvw-border);border-radius:6px;background:var(--dvw-base);color:var(--dvw-text);font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
53094
53671
  .dvw-setting-textarea:focus{outline:none;border-color:var(--dvw-accent)}
53095
53672
  .dvw-setting-extension{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--dvw-border);border-radius:7px;background:var(--dvw-base)}.dvw-setting-extension>div{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px}.dvw-setting-extension span{color:var(--dvw-text-3);font-size:12px}.dvw-setting-extension button{height:26px;padding:0 14px;border:1px solid var(--dvw-border);border-radius:4px;background:transparent;color:var(--dvw-text);font:inherit;cursor:pointer}.dvw-setting-extension button:hover{background:var(--dvw-hover)}.dvw-setting-extension button.dvw-primary-button{border-color:var(--dvw-accent);background:var(--dvw-accent);color:#fff}.dvw-setting-hint{margin:8px 0 0;color:var(--dvw-text-3);font-size:11px;line-height:1.5}
@@ -53114,9 +53691,10 @@ body[data-dvw-workbench-active] [data-sidebar-toggle]{display:none!important}
53114
53691
  .dvw-tab{border-radius:10px 10px 0 0;border-right:0}
53115
53692
  .dvw-tabs{background:color-mix(in srgb,var(--dvw-text) 4%,var(--dvw-base))}
53116
53693
  .dvw-tab[data-active=true]{border-top:0;border-right-color:transparent}
53694
+ .dvw-tab:hover:not([data-active=true]){background:color-mix(in srgb,var(--dvw-text) 3%,transparent)}
53117
53695
  .dvw-tab[data-active=true]::before,.dvw-tab[data-active=true]::after{content:"";position:absolute;bottom:0;width:10px;height:10px;background:var(--dvw-surface-1);pointer-events:none}
53118
- .dvw-tab[data-active=true]::before{left:-10px;-webkit-mask:radial-gradient(circle 10px at 0 0,transparent 9px,#000 9.6px);mask:radial-gradient(circle 10px at 0 0,transparent 9px,#000 9.6px)}
53119
- .dvw-tab[data-active=true]::after{right:-10px;-webkit-mask:radial-gradient(circle 10px at 100% 0,transparent 9px,#000 9.6px);mask:radial-gradient(circle 10px at 100% 0,transparent 9px,#000 9.6px)}
53696
+ .dvw-tab[data-active=true]::before{left:-10px;clip-path:path("M10 0C10 5.5 5.5 10 0 10L10 10Z")}
53697
+ .dvw-tab[data-active=true]::after{right:-10px;clip-path:path("M0 0C0 5.5 4.5 10 10 10L0 10Z")}
53120
53698
  .dvw-commit-box{position:relative;display:flex;align-items:stretch}
53121
53699
  .dvw-commit-input{width:100%;min-height:30px;max-height:96px;field-sizing:content;resize:none;padding:5px 32px 5px 8px;line-height:18px}
53122
53700
  .dvw-commit-ai{position:absolute;right:4px;top:50%;transform:translateY(-50%);width:24px;min-width:24px;height:24px;border:0;background:transparent;color:var(--dvw-text-3)}
@@ -53208,6 +53786,16 @@ body[data-dvw-palette=eyecare] [data-dvw-conversation-panel]{--dsw-alias-bg-base
53208
53786
  .dvw-workspace-switcher>footer button{height:32px;border-radius:9px;padding:0 16px}
53209
53787
  .dvw-workspace-switcher .dvw-icon{border-radius:9px}
53210
53788
  @media(max-width:760px){.dvw-settings-body{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr)}.dvw-settings-nav{grid-column:1;grid-row:1;flex-direction:row;border-right:0;border-bottom:1px solid var(--dvw-border-soft);padding:8px}.dvw-settings-nav button{flex:none}.dvw-settings-body>section{grid-column:1;grid-row:2;padding:16px 18px 24px}}
53789
+ .dvw-dsh-tab-mark{width:16px;height:16px;flex:none;background:currentColor;-webkit-mask:${DSH_MARK_MASK} center/contain no-repeat;mask:${DSH_MARK_MASK} center/contain no-repeat}
53790
+ .codicon-globe:before{content:"\eb01"}
53791
+ .dvw-terminal-pane{display:flex;flex:1;min-width:0;min-height:0;flex-direction:column}.dvw-terminal-panes[data-split=true]>.dvw-terminal-pane+.dvw-terminal-pane{border-left:1px solid var(--dvw-border)}
53792
+ .dvw-terminal-notice{display:flex;align-items:center;gap:8px;padding:4px 10px;color:var(--dvw-text-2);font-size:12px}.dvw-terminal-notice button,.dvw-start-tools button{border:1px solid var(--dvw-border);border-radius:4px;background:var(--dvw-surface-2);color:var(--dvw-text);font:inherit;cursor:pointer}.dvw-terminal-notice button:hover,.dvw-start-tools button:hover{background:var(--dvw-hover)}
53793
+ .dvw-start-tools{display:flex;justify-content:center;gap:10px;margin-top:20px}.dvw-start-tools button{display:flex;align-items:center;gap:8px;padding:8px 16px}.dvw-start-tools button:disabled{opacity:.45;cursor:default}
53794
+ .dvw-editor[data-browser-active]>.dvw-editor-body,.dvw-editor[data-browser-active]>.dvw-editor-toolbar,.dvw-editor[data-browser-active]>.dvw-outline{visibility:hidden}
53795
+ [data-dvw-browser-page]:popover-open{position:fixed!important;inset:auto!important;margin:0!important;padding:0!important;border:0!important;border-radius:0!important;box-sizing:border-box!important;overflow:hidden!important;background:var(--dsw-alias-bg-base,#fff)!important;color:var(--dsw-alias-label-primary,#171717)!important;display:flex!important;flex-direction:column!important}
53796
+ [data-dvw-browser-page]::backdrop{background:transparent;pointer-events:none}
53797
+ [data-dvw-dsh-page]{position:fixed!important;z-index:106!important;margin:0!important;display:flex!important;flex-direction:column!important;box-sizing:border-box!important;overflow:hidden!important;background:var(--dsw-alias-bg-base,#fff)!important;border:0!important;border-radius:0!important;box-shadow:none!important;--dsh-frame-top-clearance:0px;--dsh-frame-chrome-top:0px}
53798
+ [data-dvw-dsh-page][data-dvw-dsh-hidden]{display:none!important}
53211
53799
  `;
53212
53800
  function installStyles() {
53213
53801
  const id = "dsh-vscode-workbench/styles";
@@ -53284,14 +53872,21 @@ body[data-dvw-palette=eyecare] [data-dvw-conversation-panel]{--dsw-alias-bg-base
53284
53872
  }
53285
53873
  //#endregion
53286
53874
  //#region src/client/index.tsx
53287
- const Overlay = ({ service, useSessions, navigation }) => {
53875
+ const Overlay = ({ service, browser, useSessions, usePanelInfo, navigation, openDshPanel, restoreConversation, closeDshPanel }) => {
53288
53876
  const session = useSessions(mainViewSession);
53289
53877
  const cwd = session?.projectionValues?.workingDirectory ?? session?.cwd;
53878
+ const activePanelId = usePanelInfo((info) => info.activePanelId);
53879
+ const dshPanelId = activePanelId === null || activePanelId === WORKBENCH_PANEL_ID ? void 0 : activePanelId;
53290
53880
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Workbench, {
53291
53881
  service,
53882
+ browser,
53292
53883
  sessionId: session?.id,
53293
53884
  cwd,
53294
- navigation
53885
+ navigation,
53886
+ dshPanelId,
53887
+ onOpenDshPanel: openDshPanel,
53888
+ onRestoreConversation: restoreConversation,
53889
+ onCloseDshPanel: closeDshPanel
53295
53890
  }, cwd ?? "no-workspace");
53296
53891
  };
53297
53892
  const WORKBENCH_PANEL_ID = "vscode-workbench";
@@ -53313,9 +53908,20 @@ body[data-dvw-palette=eyecare] [data-dvw-conversation-panel]{--dsw-alias-bg-base
53313
53908
  }, [service, restoreConversation]);
53314
53909
  return null;
53315
53910
  }
53316
- const inject = ["slots", "layout"];
53911
+ const inject = [
53912
+ "slots",
53913
+ "layout",
53914
+ "remote",
53915
+ "remote.terminal",
53916
+ "sidebarRight",
53917
+ "sidebarRightTabs"
53918
+ ];
53317
53919
  function apply(ctx) {
53318
53920
  const service = new WorkbenchController();
53921
+ const browser = new NativeBrowser(ctx);
53922
+ ctx.effect(() => () => browser.dispose(), "dsh-vscode-workbench: browser");
53923
+ const terminals = new TerminalWorkspaces(new _deepseek_ai_dsh_api_terminal_controller_client.ClientTerminals(ctx.isolate("webTerminals"), ctx.remote.terminal));
53924
+ ctx.effect(() => () => terminals.dispose(), "dsh-vscode-workbench: terminal membership");
53319
53925
  const navigation = createDshNavigation((name) => ctx.get?.(name));
53320
53926
  ctx.effect(() => installStyles(), "dsh-vscode-workbench: styles");
53321
53927
  ctx.effect(() => {
@@ -53327,7 +53933,10 @@ body[data-dvw-palette=eyecare] [data-dvw-conversation-panel]{--dsw-alias-bg-base
53327
53933
  ctx.effect(() => service.registerBottomPanel({
53328
53934
  id: "terminal",
53329
53935
  title: "终端",
53330
- component: TerminalPanel
53936
+ component: (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TerminalPanel, {
53937
+ ...props,
53938
+ workspace: terminals.get(props.sessionId)
53939
+ }, props.sessionId)
53331
53940
  }), "dsh-vscode-workbench: terminal panel");
53332
53941
  ctx.effect(() => service.registerCommand({
53333
53942
  id: "terminal.new",
@@ -53338,6 +53947,14 @@ body[data-dvw-palette=eyecare] [data-dvw-conversation-panel]{--dsw-alias-bg-base
53338
53947
  const restoreConversation = () => {
53339
53948
  ctx.layout?.selectPanel(null);
53340
53949
  };
53950
+ const openDshPanel = (panelId) => {
53951
+ try {
53952
+ ctx.layout?.selectPanel(panelId);
53953
+ } catch {}
53954
+ };
53955
+ const closeDshPanel = () => {
53956
+ ctx.layout?.selectPanel(null);
53957
+ };
53341
53958
  ctx.slots.inject("main", () => ctx.slots.inject("sidebar.panellist", () => {
53342
53959
  const disposeMain = ctx.slots.register({
53343
53960
  name: "main",
@@ -53363,12 +53980,17 @@ body[data-dvw-palette=eyecare] [data-dvw-conversation-panel]{--dsw-alias-bg-base
53363
53980
  id: "dsh-vscode-workbench-overlay",
53364
53981
  inject: () => ({
53365
53982
  service,
53366
- navigation
53983
+ browser,
53984
+ navigation,
53985
+ openDshPanel,
53986
+ restoreConversation,
53987
+ closeDshPanel
53367
53988
  })
53368
53989
  }, Overlay));
53369
53990
  ctx.effect(() => {
53370
53991
  const onKey = (event) => {
53371
53992
  if (handleTerminalShortcut(event, service)) return;
53993
+ if (handleBrowserShortcut(event, service)) return;
53372
53994
  if (event.isComposing || !event.ctrlKey || event.altKey) return;
53373
53995
  const view = event.shiftKey && event.code === "KeyE" ? "explorer" : event.shiftKey && event.code === "KeyF" ? "search" : event.shiftKey && event.code === "KeyG" ? "git" : void 0;
53374
53996
  if (view !== void 0) {