@michengai/dsh-codex-ui 1.1.12 → 1.1.13

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
@@ -8,11 +8,167 @@ window.__ModuleLoader__.load({
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
10
  let react_dom = require("react-dom");
11
+ //#region src/client/session-host.ts
12
+ var UnknownSessionError = class extends Error {
13
+ constructor() {
14
+ super("unknown session");
15
+ this.name = "UnknownSessionError";
16
+ }
17
+ };
18
+ /** 旧宿主读 list.current;alpha.2 主视图改由 retainedBy.mainView 标记。 */
19
+ function currentSessionId(state) {
20
+ if (state === void 0) return void 0;
21
+ if (typeof state.current === "string" && state.current !== "") return state.current;
22
+ const byId = state.byId ?? {};
23
+ const ids = Array.isArray(state.ids) && state.ids.length > 0 ? state.ids : Object.keys(byId);
24
+ const pick = (id, row) => {
25
+ if (row === void 0 || (row.retainedBy?.mainView ?? 0) <= 0) return void 0;
26
+ const explicit = row.id?.trim();
27
+ return explicit !== void 0 && explicit !== "" ? explicit : id;
28
+ };
29
+ for (const id of ids) {
30
+ const found = pick(id, byId[id]);
31
+ if (found !== void 0) return found;
32
+ }
33
+ for (const id of Object.keys(byId)) {
34
+ if (ids.includes(id)) continue;
35
+ const found = pick(id, byId[id]);
36
+ if (found !== void 0) return found;
37
+ }
38
+ }
39
+ /** 官方树在全局面板打开时取消会话选中。 */
40
+ function visibleSelectedSessionId(state, panelActive = false) {
41
+ return panelActive ? void 0 : currentSessionId(state);
42
+ }
43
+ function sessionRowUnread(localUnread, status, selected = false) {
44
+ return localUnread || status?.completionUnread === true && !selected;
45
+ }
46
+ function sessionRunningFlags(byId, status) {
47
+ const ids = new Set(Object.keys(byId));
48
+ if (status !== void 0) for (const id of status.keys()) ids.add(id);
49
+ const next = {};
50
+ for (const id of ids) next[id] = sessionIsRunning(byId[id], status?.get(id));
51
+ return next;
52
+ }
53
+ /** 旧宿主看 current 是否空会话;alpha.2 看主视图 retain 的 blank。 */
54
+ function isBlankOnboardingSession(state) {
55
+ if (state?.phase !== "ready") return false;
56
+ const id = currentSessionId(state);
57
+ return id === void 0 || state.byId?.[id]?.blank === true;
58
+ }
59
+ function hostAccess(ctx) {
60
+ return ctx;
61
+ }
62
+ /** 有 reflect 时只 probe,不能硬读未注入服务,否则 Cordis 会挡住插件激活。 */
63
+ function probeService(ctx, name) {
64
+ if (ctx === void 0) return void 0;
65
+ const access = hostAccess(ctx);
66
+ const reflectGet = access.reflect?.get;
67
+ const hasReflect = typeof reflectGet === "function";
68
+ if (hasReflect) try {
69
+ const found = reflectGet.call(access.reflect, name);
70
+ if (found !== void 0) return found;
71
+ } catch {}
72
+ if (typeof access.get === "function") try {
73
+ const found = access.get(name);
74
+ if (found !== void 0) return found;
75
+ } catch {
76
+ return;
77
+ }
78
+ if (hasReflect) return void 0;
79
+ try {
80
+ return access[name];
81
+ } catch {
82
+ return;
83
+ }
84
+ }
85
+ function openHostSession(ctx, id) {
86
+ if (id === "") return false;
87
+ const access = hostAccess(ctx);
88
+ const uiWorkspace = probeService(access, "uiWorkspace");
89
+ if (typeof uiWorkspace?.openSession === "function") {
90
+ uiWorkspace.openSession(id);
91
+ return true;
92
+ }
93
+ if (typeof access.sessions?.retain === "function") return false;
94
+ if (typeof access.sessions?.open === "function") {
95
+ access.sessions.open(id);
96
+ return true;
97
+ }
98
+ return false;
99
+ }
100
+ async function withSessionBinding(sessions, sessionId, operation) {
101
+ const host = sessions === void 0 ? void 0 : hostAccess({ sessions }).sessions;
102
+ if (host === void 0) throw new UnknownSessionError();
103
+ if (typeof host.using === "function") return host.using(sessionId, { source: "controllerOperation" }, async (reference) => {
104
+ if (reference.ready !== void 0) await reference.ready;
105
+ if (reference.binding === void 0) throw new UnknownSessionError();
106
+ return operation(reference.binding);
107
+ });
108
+ const existing = host.binding?.(sessionId);
109
+ if (existing !== void 0) return operation(existing);
110
+ if (typeof host.retain !== "function") throw new UnknownSessionError();
111
+ const reference = host.retain(sessionId, { source: "controllerOperation" });
112
+ try {
113
+ if (reference.ready !== void 0) await reference.ready;
114
+ if (reference.binding === void 0) throw new UnknownSessionError();
115
+ return await operation(reference.binding);
116
+ } finally {
117
+ reference.release?.();
118
+ }
119
+ }
120
+ async function renameHostSession(ctx, sessionId, title) {
121
+ await withSessionBinding(hostAccess(ctx).sessions, sessionId, async (binding) => {
122
+ if (typeof binding.session?.rename !== "function") throw new UnknownSessionError();
123
+ const result = await binding.session.rename(title);
124
+ if (result.ok === false) throw result.error ?? /* @__PURE__ */ new Error("rename failed");
125
+ });
126
+ }
127
+ async function forkHostSession(ctx, sessionId) {
128
+ const access = hostAccess(ctx);
129
+ const uiWorkspace = probeService(access, "uiWorkspace");
130
+ if (typeof uiWorkspace?.forkSession === "function") {
131
+ await uiWorkspace.forkSession(sessionId);
132
+ return;
133
+ }
134
+ const childId = await access.sessions?.fork?.({
135
+ sessionId,
136
+ increaseTitle: true
137
+ });
138
+ if (typeof childId === "string" && childId !== "") openHostSession(access, childId);
139
+ }
140
+ async function archiveHostSession(ctx, sessionId) {
141
+ const access = hostAccess(ctx);
142
+ const uiWorkspace = probeService(access, "uiWorkspace");
143
+ if (typeof uiWorkspace?.archiveSession === "function") {
144
+ await uiWorkspace.archiveSession(sessionId);
145
+ return;
146
+ }
147
+ await access.workspaces?.archiveSession?.(sessionId);
148
+ }
149
+ function mergePendingInteractions(legacy, status) {
150
+ const next = new Map(legacy);
151
+ for (const [id, row] of status) if (row?.pendingInteraction !== void 0) next.set(id, row.pendingInteraction);
152
+ return next;
153
+ }
154
+ function sessionIsRunning(session, status) {
155
+ if (status?.running !== void 0) return status.running === true;
156
+ return session?.running === true;
157
+ }
158
+ //#endregion
11
159
  //#region src/client/session-navigation.ts
12
- /** 新版会话控制器不再负责主面板切换;打开会话时同时退出全局面板。 */
13
- function openConversation(sessions, layout, id) {
14
- sessions.open(id);
15
- selectGlobalPanel(layout, null);
160
+ function openConversation(host, layout, id) {
161
+ const opened = openHostSession(host, id);
162
+ if (opened) selectGlobalPanel(layout, null);
163
+ return opened;
164
+ }
165
+ /** 先打开再写草稿,避免 using 释放后 composer 换新 binding。 */
166
+ async function openConversationWithDraft(host, layout, sessions, sessionId, writeDraft) {
167
+ return withSessionBinding(sessions, sessionId, (binding) => {
168
+ const opened = openConversation(host, layout, sessionId);
169
+ writeDraft(binding);
170
+ return opened;
171
+ });
16
172
  }
17
173
  /** 统一检测旧宿主是否提供面板切换能力,保留宿主方法的 this。 */
18
174
  function selectGlobalPanel(layout, id) {
@@ -720,17 +876,112 @@ window.__ModuleLoader__.load({
720
876
  key: "17ys0d"
721
877
  }]]);
722
878
  //#endregion
879
+ //#region src/client/settings-navigation.ts
880
+ /**
881
+ * DSH 设置壳暂未提供按 section id 打开的公开 API。该兼容层只从本插件渲染
882
+ * 的设置入口触发,再按壳的可访问名称选择页面;宿主升级时可集中替换。
883
+ */
884
+ function pickSettingsSectionButton(buttons, labels) {
885
+ for (const label of labels) {
886
+ const match = buttons.find((button) => button.textContent?.trim() === label);
887
+ if (match !== void 0) return match;
888
+ }
889
+ }
890
+ /**
891
+ * 可选插件未注册设置入口时,直接打开兜底页,避免先展示通用设置并等待导航超时。
892
+ */
893
+ function routeOptionalSettingsSection(available, openRequested, openFallback) {
894
+ if (available) {
895
+ openRequested();
896
+ return;
897
+ }
898
+ openFallback();
899
+ }
900
+ /** 官方 alpha.2 插件管理页占用 sidebar.panellist / main 的同一 id。 */
901
+ const OFFICIAL_PLUGINS_PANEL_ID = "plugins";
902
+ let cancelPendingNavigation;
903
+ const SETTINGS_NAVIGATION_TIMEOUT_MS = 4e3;
904
+ const SETTINGS_TRIGGER_SELECTOR = "[data-dcu-settings-trigger],[aria-haspopup=\"dialog\"]";
905
+ const SETTINGS_OPEN_SECTION_EVENT = "dcu-settings-open-section";
906
+ /** 根入口与分区跳转使用同一触发器合约,兼容保留的宿主设置壳。 */
907
+ function openSettingsRoot(root) {
908
+ root?.querySelector(SETTINGS_TRIGGER_SELECTOR)?.click();
909
+ }
910
+ function openSettingsSection(root, label, onMissing, onSelected) {
911
+ const labels = typeof label === "string" ? [label] : label;
912
+ const trigger = root?.querySelector(SETTINGS_TRIGGER_SELECTOR);
913
+ if (trigger === null || trigger === void 0) {
914
+ onMissing?.();
915
+ return;
916
+ }
917
+ const opening = cancelPendingNavigation !== void 0;
918
+ cancelPendingNavigation?.();
919
+ const request = new CustomEvent(SETTINGS_OPEN_SECTION_EVENT, {
920
+ detail: { labels },
921
+ cancelable: true
922
+ });
923
+ if (!trigger.dispatchEvent(request)) {
924
+ onSelected?.();
925
+ return;
926
+ }
927
+ const pageSelector = trigger.hasAttribute("data-dcu-settings-trigger") ? "[data-dcu-settings-page]" : "[role=\"dialog\"]";
928
+ if (!opening && document.querySelector(pageSelector) === null) trigger.click();
929
+ let frame;
930
+ let finished = false;
931
+ const observer = new MutationObserver(() => {
932
+ schedule();
933
+ });
934
+ const cleanup = () => {
935
+ if (finished) return;
936
+ finished = true;
937
+ observer.disconnect();
938
+ window.clearTimeout(timeout);
939
+ if (frame !== void 0) window.cancelAnimationFrame(frame);
940
+ if (cancelPendingNavigation === cleanup) cancelPendingNavigation = void 0;
941
+ };
942
+ const select = () => {
943
+ const target = pickSettingsSectionButton([...document.querySelectorAll(`${pageSelector} nav button`)], labels);
944
+ if (target === void 0) return false;
945
+ cleanup();
946
+ target.click();
947
+ onSelected?.();
948
+ return true;
949
+ };
950
+ const schedule = () => {
951
+ if (finished || frame !== void 0) return;
952
+ frame = window.requestAnimationFrame(() => {
953
+ frame = void 0;
954
+ select();
955
+ });
956
+ };
957
+ const timeout = window.setTimeout(() => {
958
+ if (select()) return;
959
+ cleanup();
960
+ console.warn(`[michengai-codex-ui] 未找到设置分区:${labels.join(" / ")}`);
961
+ onMissing?.();
962
+ }, SETTINGS_NAVIGATION_TIMEOUT_MS);
963
+ observer.observe(document.body, {
964
+ childList: true,
965
+ subtree: true
966
+ });
967
+ cancelPendingNavigation = cleanup;
968
+ schedule();
969
+ }
970
+ //#endregion
723
971
  //#region src/client/global-panels.tsx
724
972
  /** 从宿主注册表读取面板元数据;缓存快照以满足 React 外部状态订阅契约。 */
725
973
  function createGlobalPanelSource(slots, locale) {
726
974
  let cached = [];
727
975
  return {
728
976
  getSnapshot() {
729
- const next = slots.entriesOfSlot("sidebar.panellist").flatMap(({ options }) => options.id === void 0 ? [] : [{
730
- id: options.id,
731
- order: options.order ?? 0,
732
- label: (typeof options.label === "function" ? options.label() : options.label) ?? options.id
733
- }]).sort((a, b) => a.order - b.order);
977
+ const next = slots.entriesOfSlot("sidebar.panellist").flatMap(({ options }) => {
978
+ if (options.id === void 0 || options.id === "plugins") return [];
979
+ return [{
980
+ id: options.id,
981
+ order: options.order ?? 0,
982
+ label: (typeof options.label === "function" ? options.label() : options.label) ?? options.id
983
+ }];
984
+ }).sort((a, b) => a.order - b.order);
734
985
  if (next.length !== cached.length || next.some((panel, index) => {
735
986
  const previous = cached[index];
736
987
  return previous === void 0 || panel.id !== previous.id || panel.label !== previous.label || panel.order !== previous.order;
@@ -907,96 +1158,6 @@ window.__ModuleLoader__.load({
907
1158
  } catch {}
908
1159
  }
909
1160
  //#endregion
910
- //#region src/client/settings-navigation.ts
911
- /**
912
- * DSH 设置壳暂未提供按 section id 打开的公开 API。该兼容层只从本插件渲染
913
- * 的设置入口触发,再按壳的可访问名称选择页面;宿主升级时可集中替换。
914
- */
915
- function pickSettingsSectionButton(buttons, labels) {
916
- for (const label of labels) {
917
- const match = buttons.find((button) => button.textContent?.trim() === label);
918
- if (match !== void 0) return match;
919
- }
920
- }
921
- /**
922
- * 可选插件未注册设置入口时,直接打开兜底页,避免先展示通用设置并等待导航超时。
923
- */
924
- function routeOptionalSettingsSection(available, openRequested, openFallback) {
925
- if (available) {
926
- openRequested();
927
- return;
928
- }
929
- openFallback();
930
- }
931
- let cancelPendingNavigation;
932
- const SETTINGS_NAVIGATION_TIMEOUT_MS = 4e3;
933
- const SETTINGS_TRIGGER_SELECTOR = "[data-dcu-settings-trigger],[aria-haspopup=\"dialog\"]";
934
- const SETTINGS_OPEN_SECTION_EVENT = "dcu-settings-open-section";
935
- /** 根入口与分区跳转使用同一触发器合约,兼容保留的宿主设置壳。 */
936
- function openSettingsRoot(root) {
937
- root?.querySelector(SETTINGS_TRIGGER_SELECTOR)?.click();
938
- }
939
- function openSettingsSection(root, label, onMissing, onSelected) {
940
- const labels = typeof label === "string" ? [label] : label;
941
- const trigger = root?.querySelector(SETTINGS_TRIGGER_SELECTOR);
942
- if (trigger === null || trigger === void 0) {
943
- onMissing?.();
944
- return;
945
- }
946
- const opening = cancelPendingNavigation !== void 0;
947
- cancelPendingNavigation?.();
948
- const request = new CustomEvent(SETTINGS_OPEN_SECTION_EVENT, {
949
- detail: { labels },
950
- cancelable: true
951
- });
952
- if (!trigger.dispatchEvent(request)) {
953
- onSelected?.();
954
- return;
955
- }
956
- const pageSelector = trigger.hasAttribute("data-dcu-settings-trigger") ? "[data-dcu-settings-page]" : "[role=\"dialog\"]";
957
- if (!opening && document.querySelector(pageSelector) === null) trigger.click();
958
- let frame;
959
- let finished = false;
960
- const observer = new MutationObserver(() => {
961
- schedule();
962
- });
963
- const cleanup = () => {
964
- if (finished) return;
965
- finished = true;
966
- observer.disconnect();
967
- window.clearTimeout(timeout);
968
- if (frame !== void 0) window.cancelAnimationFrame(frame);
969
- if (cancelPendingNavigation === cleanup) cancelPendingNavigation = void 0;
970
- };
971
- const select = () => {
972
- const target = pickSettingsSectionButton([...document.querySelectorAll(`${pageSelector} nav button`)], labels);
973
- if (target === void 0) return false;
974
- cleanup();
975
- target.click();
976
- onSelected?.();
977
- return true;
978
- };
979
- const schedule = () => {
980
- if (finished || frame !== void 0) return;
981
- frame = window.requestAnimationFrame(() => {
982
- frame = void 0;
983
- select();
984
- });
985
- };
986
- const timeout = window.setTimeout(() => {
987
- if (select()) return;
988
- cleanup();
989
- console.warn(`[michengai-codex-ui] 未找到设置分区:${labels.join(" / ")}`);
990
- onMissing?.();
991
- }, SETTINGS_NAVIGATION_TIMEOUT_MS);
992
- observer.observe(document.body, {
993
- childList: true,
994
- subtree: true
995
- });
996
- cancelPendingNavigation = cleanup;
997
- schedule();
998
- }
999
- //#endregion
1000
1161
  //#region src/client/sidebar-search.ts
1001
1162
  /** 按用户可见名称和辅助关键词筛选侧栏搜索结果。 */
1002
1163
  function filterSidebarSearchItems(items, query) {
@@ -1587,9 +1748,20 @@ window.__ModuleLoader__.load({
1587
1748
  //#endregion
1588
1749
  //#region src/client/session-pending.ts
1589
1750
  const EMPTY_PENDING_INTERACTIONS = /* @__PURE__ */ new Map();
1751
+ const EMPTY_SESSION_STATUS = /* @__PURE__ */ new Map();
1590
1752
  function useEmptySessionPendingInteraction(selector) {
1591
1753
  return selector(EMPTY_PENDING_INTERACTIONS);
1592
1754
  }
1755
+ function useEmptySessionStatus(selector) {
1756
+ return selector(EMPTY_SESSION_STATUS);
1757
+ }
1758
+ function useHostSessionStatus(useSessionStatus) {
1759
+ return (useSessionStatus ?? useEmptySessionStatus)((state) => state);
1760
+ }
1761
+ /** 旧宿主订阅 pending Store;alpha.2 改走 useSessionStatus。两路都要订阅,避免条件 Hook。 */
1762
+ function useHostPendingInteractions(useSessionPendingInteraction, useSessionStatus) {
1763
+ return mergePendingInteractions((useSessionPendingInteraction ?? useEmptySessionPendingInteraction)((state) => state), (useSessionStatus ?? useEmptySessionStatus)((state) => state));
1764
+ }
1593
1765
  function visiblePendingKind(kind) {
1594
1766
  switch (kind) {
1595
1767
  case "approval":
@@ -2729,6 +2901,9 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2729
2901
  function hasConnectWorkspace(value) {
2730
2902
  return value !== null && typeof value === "object" && "connectWorkspace" in value && typeof value.connectWorkspace === "function";
2731
2903
  }
2904
+ function hasOpenWorkspace(value) {
2905
+ return value !== null && typeof value === "object" && "openWorkspace" in value && typeof value.openWorkspace === "function";
2906
+ }
2732
2907
  /** 与 DSH 官方最近工作区策略一致;时间相同时保留 Host 工作区顺序。 */
2733
2908
  function recentWorkspaceId(workspaces, sessions) {
2734
2909
  let selected;
@@ -2963,10 +3138,23 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2963
3138
  lower = after ? rows[index + 1]?.rect.top : edge.rect.top;
2964
3139
  } else if (target.matches(".dcu-wb-pin-start,.dcu-wb-pin-end")) {
2965
3140
  if (anchor.width <= 0 || anchor.height <= 0) return void 0;
2966
- upper = rows.filter((row) => row.rect.bottom <= anchor.top).at(-1)?.rect.bottom;
2967
- lower = rows.find((row) => row.rect.top >= anchor.bottom)?.rect.top;
2968
- if (lower === void 0) lower = anchor.bottom;
2969
- if (upper === void 0) upper = anchor.top;
3141
+ const itemRows = rows.filter((row) => !row.node.matches(".dcu-wb-section-head,.dcu-wb-empty,.dcu-wb-nochat"));
3142
+ if (target.matches(".dcu-wb-pin-end") && itemRows.length > 0) {
3143
+ const last = itemRows[itemRows.length - 1];
3144
+ anchor = last.rect;
3145
+ upper = last.rect.bottom;
3146
+ lower = rows.find((row) => row.rect.top >= last.rect.bottom - .5)?.rect.top;
3147
+ } else if (target.matches(".dcu-wb-pin-start") && itemRows.length > 0) {
3148
+ const first = itemRows[0];
3149
+ anchor = first.rect;
3150
+ lower = first.rect.top;
3151
+ upper = rows.filter((row) => row.rect.bottom <= first.rect.top + .5).at(-1)?.rect.bottom;
3152
+ } else {
3153
+ upper = rows.filter((row) => row.rect.bottom <= anchor.top).at(-1)?.rect.bottom;
3154
+ lower = rows.find((row) => row.rect.top >= anchor.bottom)?.rect.top;
3155
+ if (lower === void 0) lower = anchor.bottom;
3156
+ if (upper === void 0) upper = anchor.top;
3157
+ }
2970
3158
  } else return void 0;
2971
3159
  const center = upper === void 0 ? lower - 4 : lower === void 0 ? upper + 4 : (upper + lower) / 2;
2972
3160
  const box = root.getBoundingClientRect();
@@ -3145,7 +3333,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3145
3333
  .dcu-wb-project-head:hover,.dcu-wb-project-head.dcu-wb-menu-open,.dcu-wb-session:hover,.dcu-wb-session.dcu-wb-selected,.dcu-wb-session.dcu-wb-menu-open{background:var(--dcu-sidebar-hover)}.dcu-wb-project.dcu-wb-session-drop>.dcu-wb-project-head{border-radius:8px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 9%,transparent);outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.dcu-wb-project.dcu-wb-session-move-drop{border-radius:8px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 9%,transparent);outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.dcu-wb-project.dcu-wb-session-move-drop>.dcu-wb-project-head{background:transparent;outline:0}.dcu-wb-project-body>.dcu-wb-session:first-child,.dcu-wb-project-body>.dcu-wb-nochat:first-child{margin-top:4px}.dcu-wb-session+.dcu-wb-session{margin-top:2px}
3146
3334
  .dcu-wb-project-head[draggable=true],.dcu-wb-session[draggable=true]{cursor:grab}
3147
3335
  .dcu-wb-project-head[draggable=true]:active,.dcu-wb-session[draggable=true]:active{cursor:grabbing}
3148
- .dcu-wb-section,.dcu-wb-section:focus,.dcu-wb-section:focus-visible,.dcu-wb-project-head:focus,.dcu-wb-session:focus{outline:0}.dcu-wb-pin-end,.dcu-wb-pin-start{position:relative;height:8px}.dcu-wb-project.dcu-wb-drop::before,.dcu-wb-session.dcu-wb-drop::before,.dcu-wb-pin-end.dcu-wb-drop::before,.dcu-wb-pin-start.dcu-wb-drop::before{content:"";position:absolute;z-index:2;left:7px;right:8px;top:-8px;height:8px;pointer-events:none;background:radial-gradient(circle at 4px 50%,transparent 1.75px,var(--dsw-alias-state-business-primary) 2px 3.75px,transparent 4px),linear-gradient(var(--dsw-alias-state-business-primary),var(--dsw-alias-state-business-primary)) 10px 50%/calc(100% - 10px) 2px no-repeat}.dcu-wb-pin-start.dcu-wb-drop::before{top:0}.dcu-wb-pinned-list>.dcu-wb-project:first-child.dcu-wb-drop::before{top:-4px}.dcu-wb-project.dcu-wb-drop-after::before,.dcu-wb-session.dcu-wb-drop-after::before{top:auto;bottom:-8px}.dcu-wb-dragging{opacity:.28}.dcu-wb-drag-ghost{position:fixed;top:0;left:0;transform:translate(-200vw,-200vh);z-index:10040;display:flex;align-items:center;gap:8px;max-width:220px;height:30px;padding:0 10px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:var(--dcu-sidebar-hover);box-shadow:0 4px 12px rgba(0,0,0,.24);color:var(--dcu-sidebar-primary);font:14px/20px var(--dcu-font,inherit);white-space:nowrap;pointer-events:none}.dcu-wb-drag-ghost-icon{display:grid;place-items:center;flex:none;width:16px;height:16px;color:var(--dcu-sidebar-icon)}.dcu-wb-drag-ghost-icon svg{display:block;width:16px;height:16px}.dcu-wb-drag-ghost-title{min-width:0;overflow:hidden;text-overflow:ellipsis}
3336
+ .dcu-wb-section,.dcu-wb-section:focus,.dcu-wb-section:focus-visible,.dcu-wb-project-head:focus,.dcu-wb-session:focus{outline:0}.dcu-wb-pinned-list{position:relative}.dcu-wb-pin-end,.dcu-wb-pin-start{position:absolute;left:0;right:0;height:8px;z-index:2;pointer-events:none}.dcu-wb-pin-end.dcu-wb-drop,.dcu-wb-pin-start.dcu-wb-drop{pointer-events:auto}.dcu-wb-pin-start{top:0}.dcu-wb-pin-end{bottom:-8px}.dcu-wb-project.dcu-wb-drop::before,.dcu-wb-session.dcu-wb-drop::before,.dcu-wb-pin-end.dcu-wb-drop::before,.dcu-wb-pin-start.dcu-wb-drop::before{content:"";position:absolute;z-index:2;left:7px;right:8px;top:-8px;height:8px;pointer-events:none;background:radial-gradient(circle at 4px 50%,transparent 1.75px,var(--dsw-alias-state-business-primary) 2px 3.75px,transparent 4px),linear-gradient(var(--dsw-alias-state-business-primary),var(--dsw-alias-state-business-primary)) 10px 50%/calc(100% - 10px) 2px no-repeat}.dcu-wb-pin-start.dcu-wb-drop::before{top:0}.dcu-wb-pinned-list>.dcu-wb-project:first-child.dcu-wb-drop::before{top:-4px}.dcu-wb-project.dcu-wb-drop-after::before,.dcu-wb-session.dcu-wb-drop-after::before{top:auto;bottom:-8px}.dcu-wb-dragging{opacity:.28}.dcu-wb-drag-ghost{position:fixed;top:8px;left:-9999px;z-index:10040;display:flex;align-items:center;gap:8px;max-width:220px;height:30px;padding:0 10px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:var(--dcu-sidebar-hover);box-shadow:0 4px 12px rgba(0,0,0,.24);color:var(--dcu-sidebar-primary);font:14px/20px var(--dcu-font,inherit);white-space:nowrap;pointer-events:none}.dcu-wb-drag-ghost-icon{display:grid;place-items:center;flex:none;width:16px;height:16px;color:var(--dcu-sidebar-icon)}.dcu-wb-drag-ghost-icon svg{display:block;width:16px;height:16px}.dcu-wb-drag-ghost-title{min-width:0;overflow:hidden;text-overflow:ellipsis}
3149
3337
  .dcu-wb-folder{display:grid;place-items:center;flex:none;width:16px;height:20px;color:var(--dcu-sidebar-icon)}.dcu-wb-folder.dcu-wb-folder-current{color:var(--dsw-alias-state-business-primary)}.dcu-wb-brand{display:block;width:16px;height:16px}
3150
3338
  .dcu-wb-project-title,.dcu-wb-session-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px}
3151
3339
  .dcu-wb-project-title{flex:1;font-weight:400;color:var(--dcu-sidebar-primary)}
@@ -3181,7 +3369,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3181
3369
  .dcu-wb-drop-indicator::before{content:"";position:absolute;left:6px;right:0;top:3px;height:2px;border-radius:999px;background:currentColor}
3182
3370
  .dcu-wb-drop-indicator::after{content:"";position:absolute;box-sizing:border-box;left:0;top:0;width:8px;height:8px;border:2px solid currentColor;border-radius:50%}
3183
3371
  .dcu-wb-project-body:has(>.dcu-wb-session)::after{content:"";display:block;height:4px;pointer-events:none}
3184
- .dcu-wb-section-body[data-open=true]:has(.dcu-wb-group-order-drop){overflow:visible}
3372
+ .dcu-wb-section-body[data-open=true]:has(.dcu-wb-group-order-drop),.dcu-wb-section-body[data-open=true]:has(.dcu-wb-pin-end),.dcu-wb-section-body[data-open=true]:has(.dcu-wb-pin-start){overflow:visible}
3185
3373
  .dcu-wb-collection-body{padding-left:0}
3186
3374
  .dcu-wb-collection-body>.dcu-wb-group-member:first-child{padding-top:4px}
3187
3375
  .dcu-wb-collection-body .dcu-wb-project-head{padding-left:8px}
@@ -3223,10 +3411,12 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3223
3411
  function CodexWorkspaceBrowser(props) {
3224
3412
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HoverShell, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexWorkspaceTree, { ...props }) });
3225
3413
  }
3226
- function CodexWorkspaceTree({ wide, useSessions, useSessionPendingInteraction, useWorkspaces, t, archiveSession, deleteSession, deleteWorkspace, forkSession, insertSessionBefore, insertWorkspaceBefore, moveSession, openPath, openSession, renameSession, renameWorkspace, startSession, canDeleteSession }) {
3414
+ function CodexWorkspaceTree({ wide, useSessions, useSessionPendingInteraction, useSessionStatus, useWorkspaces, t, archiveSession, deleteSession, deleteWorkspace, forkSession, insertSessionBefore, insertWorkspaceBefore, moveSession, openPath, openSession, renameSession, renameWorkspace, startSession, canDeleteSession, panelActive = false }) {
3227
3415
  const sessions = useSessions((state) => state);
3228
3416
  const now = useSharedNow();
3229
- const pendingInteractions = (useSessionPendingInteraction ?? useEmptySessionPendingInteraction)((state) => state);
3417
+ const pendingInteractions = useHostPendingInteractions(useSessionPendingInteraction, useSessionStatus);
3418
+ const sessionStatus = useHostSessionStatus(useSessionStatus);
3419
+ const selectedId = visibleSelectedSessionId(sessions, panelActive);
3230
3420
  const workspaces = useWorkspaces((state) => state);
3231
3421
  const baselinesReady = workspaceBaselinesReady(workspaces, sessions);
3232
3422
  const [expanded, setExpanded] = (0, react.useState)(() => readTreeExpansionState(storage$1(), WORKSPACE_EXPANSION_STORAGE_KEY));
@@ -3288,6 +3478,8 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3288
3478
  setWorkspaceGroupsState(next);
3289
3479
  };
3290
3480
  const [unreadSessionIds, setUnreadSessionIds] = (0, react.useState)(() => readSessionIds(storage$1(), SESSION_UNREAD_STORAGE_KEY));
3481
+ const rowUnread = (id) => sessionRowUnread(unreadSessionIds.includes(id), sessionStatus.get(id), selectedId === id);
3482
+ const rowRunning = (id, session) => sessionIsRunning(session ?? sessions.byId[id], sessionStatus.get(id));
3291
3483
  const previousSessionRunningRef = (0, react.useRef)();
3292
3484
  const [menu, setMenu] = (0, react.useState)();
3293
3485
  const [renameTarget, setRenameTarget] = (0, react.useState)();
@@ -3407,17 +3599,20 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3407
3599
  }
3408
3600
  }, [baselinesReady, workspaces.items]);
3409
3601
  (0, react.useEffect)(() => {
3410
- const current = sessions.current;
3411
- if (current !== void 0) setUnreadSessionIds((ids) => ids.filter((id) => id !== current));
3412
- }, [sessions.current]);
3602
+ if (selectedId !== void 0) setUnreadSessionIds((ids) => ids.filter((id) => id !== selectedId));
3603
+ }, [selectedId]);
3413
3604
  (0, react.useEffect)(() => {
3414
- const next = Object.fromEntries(Object.entries(sessions.byId).map(([id, session]) => [id, session.running === true]));
3605
+ const next = sessionRunningFlags(sessions.byId, sessionStatus);
3415
3606
  const previous = previousSessionRunningRef.current;
3416
3607
  previousSessionRunningRef.current = next;
3417
3608
  if (previous === void 0) return;
3418
- const completed = completedBackgroundSessionIds(previous, next, sessions.current);
3609
+ const completed = completedBackgroundSessionIds(previous, next, selectedId);
3419
3610
  if (completed.length > 0) setUnreadSessionIds((ids) => [...completed, ...ids.filter((id) => !completed.includes(id))]);
3420
- }, [sessions.byId, sessions.current]);
3611
+ }, [
3612
+ selectedId,
3613
+ sessionStatus,
3614
+ sessions.byId
3615
+ ]);
3421
3616
  const groups = (0, react.useMemo)(() => {
3422
3617
  const archived = workspaces.archivedSessionIds;
3423
3618
  const visible = (ids) => visibleSessionIds(ids, sessions.byId, archived);
@@ -3448,7 +3643,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3448
3643
  (0, react.useEffect)(() => {
3449
3644
  const onProject = (event) => {
3450
3645
  const detail = event.detail;
3451
- const current = sessions.current;
3646
+ const current = selectedId;
3452
3647
  const workspace = groups.items.find((item) => current !== void 0 && item.visibleIds.includes(String(current)));
3453
3648
  if (workspace === void 0 || detail === void 0) return;
3454
3649
  if (detail.toggle === true && isShowing("workspace", workspace.workspaceId)) {
@@ -3461,7 +3656,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3461
3656
  title: workspace.title,
3462
3657
  path: workspace.path,
3463
3658
  count: workspace.visibleIds.length,
3464
- unreadCount: workspace.visibleIds.filter((id) => unreadSessionIds.includes(String(id))).length,
3659
+ unreadCount: workspace.visibleIds.filter((id) => rowUnread(String(id))).length,
3465
3660
  pinned: pinnedWorkspaceIds.includes(String(workspace.workspaceId)),
3466
3661
  left: detail.left,
3467
3662
  top: detail.top
@@ -3469,7 +3664,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3469
3664
  };
3470
3665
  const onMenu = (event) => {
3471
3666
  const detail = event.detail;
3472
- const current = sessions.current;
3667
+ const current = selectedId;
3473
3668
  if (current === void 0 || detail === void 0) return;
3474
3669
  dismissTip();
3475
3670
  if (performance.now() - headerMenuPointerAt.current < 500) {
@@ -3490,9 +3685,9 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3490
3685
  };
3491
3686
  }, [
3492
3687
  groups.items,
3493
- sessions.current,
3494
3688
  menu,
3495
3689
  pinnedWorkspaceIds,
3690
+ selectedId,
3496
3691
  unreadSessionIds
3497
3692
  ]);
3498
3693
  const projectPinned = (id) => pinnedWorkspaceIds.includes(String(id));
@@ -3721,7 +3916,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3721
3916
  };
3722
3917
  const canDelete = canDeleteSession?.() === true;
3723
3918
  const sessionMenu = (sessionId, path) => sessionMenuItems(t, {
3724
- unread: unreadSessionIds.includes(sessionId),
3919
+ unread: rowUnread(sessionId),
3725
3920
  path,
3726
3921
  includePath: true,
3727
3922
  moveTargets: sessionMoveTargets(groups.items, sessionId).map((target) => ({
@@ -3776,7 +3971,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3776
3971
  const renderGroup = (workspace, zone) => {
3777
3972
  const expandKey = zone === "pinned" ? `pin:${workspace.workspaceId}` : String(workspace.workspaceId);
3778
3973
  const isExpanded = expanded[expandKey] ?? true;
3779
- const currentSessionId = sessions.current === void 0 ? void 0 : String(sessions.current);
3974
+ const currentSessionId = selectedId;
3780
3975
  const folder = projectFolderPresentation(isExpanded, workspace.visibleIds.some((id) => id === currentSessionId));
3781
3976
  const shownIds = workspace.visibleIds;
3782
3977
  const menuOpen = menu?.type === "workspace" && menu.id === workspace.workspaceId;
@@ -3904,7 +4099,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3904
4099
  title: workspace.title,
3905
4100
  path: workspace.path,
3906
4101
  count: workspace.visibleIds.length,
3907
- unreadCount: workspace.visibleIds.filter((id) => unreadSessionIds.includes(String(id))).length,
4102
+ unreadCount: workspace.visibleIds.filter((id) => rowUnread(String(id))).length,
3908
4103
  pinned: projectPinned(workspace.workspaceId),
3909
4104
  left: box.left,
3910
4105
  top: box.top
@@ -3933,7 +4128,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3933
4128
  title: workspace.title,
3934
4129
  path: workspace.path,
3935
4130
  count: workspace.visibleIds.length,
3936
- unreadCount: workspace.visibleIds.filter((id) => unreadSessionIds.includes(String(id))).length,
4131
+ unreadCount: workspace.visibleIds.filter((id) => rowUnread(String(id))).length,
3937
4132
  pinned: projectPinned(workspace.workspaceId),
3938
4133
  left: box.left,
3939
4134
  top: box.top
@@ -4001,7 +4196,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4001
4196
  if (session === void 0) return null;
4002
4197
  const pendingInteraction = pendingInteractionForSession(id, pendingInteractions, legacyPendingInteraction(session));
4003
4198
  const path = session.cwd ?? workspace.path;
4004
- const selected = sessions.current === id;
4199
+ const selected = selectedId === id;
4005
4200
  const sessionMenuOpen = menu?.type === "session" && menu.id === id;
4006
4201
  const sessionMenuAt = sessionMenuOpen && menu.x !== void 0 && menu.y !== void 0 ? {
4007
4202
  x: menu.x,
@@ -4096,8 +4291,8 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4096
4291
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRowTitle, { title: session.displayTitle }),
4097
4292
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionState, {
4098
4293
  pendingInteraction,
4099
- unread: unreadSessionIds.includes(id),
4100
- running: session.running === true,
4294
+ unread: rowUnread(id),
4295
+ running: rowRunning(id, session),
4101
4296
  t
4102
4297
  }),
4103
4298
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionTime, { time: formatCompactTime(session.updatedAt, t, now) }),
@@ -4556,7 +4751,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4556
4751
  return mountWorkspaceDropIndicator(treeRef.current);
4557
4752
  }, [wide, sorting]);
4558
4753
  (0, react.useEffect)(() => {
4559
- const current = sessions.current === void 0 ? void 0 : String(sessions.current);
4754
+ const current = selectedId;
4560
4755
  if (current === void 0 || lastRevealedSessionIdRef.current === current) return;
4561
4756
  const tree = {
4562
4757
  workspaces: workspaces.items.map((item) => ({
@@ -4577,7 +4772,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4577
4772
  }, [
4578
4773
  pinnedWorkspaceIds,
4579
4774
  recentIds,
4580
- sessions.current,
4775
+ selectedId,
4581
4776
  workspaceGroups,
4582
4777
  workspaces.items
4583
4778
  ]);
@@ -4847,15 +5042,15 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4847
5042
  const session = sessions.byId[id];
4848
5043
  if (session === void 0) return null;
4849
5044
  const title = session.displayTitle;
4850
- const unread = unreadSessionIds.includes(id);
5045
+ const unread = rowUnread(id);
4851
5046
  const pendingInteraction = pendingInteractionForSession(id, pendingInteractions, legacyPendingInteraction(session));
4852
5047
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow, {
4853
5048
  id,
4854
5049
  title,
4855
- selected: sessions.current === id,
5050
+ selected: selectedId === id,
4856
5051
  menuOpen: menu?.type === "session" && menu.id === id,
4857
5052
  unread,
4858
- running: session.running === true,
5053
+ running: rowRunning(id, session),
4859
5054
  pendingInteraction,
4860
5055
  time: formatCompactTime(session.updatedAt, t, now),
4861
5056
  t,
@@ -5550,15 +5745,17 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
5550
5745
  })
5551
5746
  });
5552
5747
  }
5553
- function ChannelBrowserTree({ openSession, archiveSession, deleteSession, forkSession, moveSession, renameSession, useSessions, useSessionPendingInteraction, useWorkspaces, t, canDeleteSession, menu, setMenu }) {
5748
+ function ChannelBrowserTree({ openSession, archiveSession, deleteSession, forkSession, moveSession, renameSession, useSessions, useSessionPendingInteraction, useSessionStatus, useWorkspaces, t, canDeleteSession, panelActive = false, menu, setMenu }) {
5554
5749
  const sessions = useSessions((state) => state);
5555
5750
  const now = useSharedNow();
5556
5751
  const workspaces = useWorkspaces?.((state) => state);
5557
- const pendingInteractions = (useSessionPendingInteraction ?? useEmptySessionPendingInteraction)((state) => state);
5752
+ const pendingInteractions = useHostPendingInteractions(useSessionPendingInteraction, useSessionStatus);
5753
+ const sessionStatus = useHostSessionStatus(useSessionStatus);
5754
+ const selectedId = visibleSelectedSessionId(sessions, panelActive);
5558
5755
  const [groups, setGroups] = (0, react.useState)([]);
5559
5756
  const [pollError, setPollError] = (0, react.useState)();
5560
5757
  const [expanded, setExpanded] = (0, react.useState)(() => readTreeExpansionState(browserStorage(), CHANNEL_EXPANSION_STORAGE_KEY));
5561
- const flags = useSessionFlags(sessions.current);
5758
+ const flags = useSessionFlags(selectedId);
5562
5759
  const { showTip, hideTip, dismissTip } = useHoverDispatch();
5563
5760
  const { busy, error, setError, run } = useBusyAction(t, () => {
5564
5761
  setMenu(void 0);
@@ -5654,10 +5851,11 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
5654
5851
  children: group.sessions.map((session) => {
5655
5852
  const id = session.sessionId;
5656
5853
  const title = session.title;
5657
- const selected = sessions.current === id;
5658
- const running = session.running || sessions.byId[id]?.running === true;
5854
+ const selected = selectedId === id;
5855
+ const status = sessionStatus.get(id);
5856
+ const running = sessionIsRunning({ running: session.running === true || sessions.byId[id]?.running === true }, status);
5659
5857
  const updatedAt = session.updatedAt ?? sessions.byId[id]?.updatedAt;
5660
- const unread = flags.unreadSessionIds.includes(id);
5858
+ const unread = sessionRowUnread(flags.unreadSessionIds.includes(id), status, selected);
5661
5859
  const pendingInteraction = pendingInteractionForSession(id, pendingInteractions, sessions.byId[id]?.pendingInteraction);
5662
5860
  const moveTargets = moveSession === void 0 || workspaces === void 0 ? void 0 : sessionMoveTargets(workspaces.items, id).map((target) => ({
5663
5861
  ...target,
@@ -5826,15 +6024,17 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
5826
6024
  })]
5827
6025
  });
5828
6026
  }
5829
- function ScheduleBrowserTree({ openSession, archiveSession, deleteSession, forkSession, moveSession, renameSession, useSessions, useSessionPendingInteraction, useWorkspaces, t, canDeleteSession, openTaskSettings, menu, setMenu }) {
6027
+ function ScheduleBrowserTree({ openSession, archiveSession, deleteSession, forkSession, moveSession, renameSession, useSessions, useSessionPendingInteraction, useSessionStatus, useWorkspaces, t, canDeleteSession, panelActive = false, openTaskSettings, menu, setMenu }) {
5830
6028
  const sessions = useSessions((state) => state);
5831
6029
  const now = useSharedNow();
5832
- const pendingInteractions = (useSessionPendingInteraction ?? useEmptySessionPendingInteraction)((state) => state);
6030
+ const pendingInteractions = useHostPendingInteractions(useSessionPendingInteraction, useSessionStatus);
6031
+ const sessionStatus = useHostSessionStatus(useSessionStatus);
6032
+ const selectedId = visibleSelectedSessionId(sessions, panelActive);
5833
6033
  const workspaces = useWorkspaces((state) => state);
5834
6034
  const [expanded, setExpanded] = (0, react.useState)(() => readTreeExpansionState(browserStorage(), SCHEDULE_EXPANSION_STORAGE_KEY));
5835
6035
  const [groupMenu, setGroupMenu] = (0, react.useState)();
5836
6036
  const [archiveGroupTarget, setArchiveGroupTarget] = (0, react.useState)();
5837
- const flags = useSessionFlags(sessions.current);
6037
+ const flags = useSessionFlags(selectedId);
5838
6038
  const { showTip, hideTip, dismissTip } = useHoverDispatch();
5839
6039
  const { busy, error, setError, run } = useBusyAction(t, () => {
5840
6040
  setMenu(void 0);
@@ -5963,7 +6163,9 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
5963
6163
  children: group.sessions.map((session) => {
5964
6164
  const id = session.id;
5965
6165
  const title = session.title;
5966
- const unread = flags.unreadSessionIds.includes(id);
6166
+ const selected = selectedId === id;
6167
+ const status = sessionStatus.get(id);
6168
+ const unread = sessionRowUnread(flags.unreadSessionIds.includes(id), status, selected);
5967
6169
  const pendingInteraction = pendingInteractionForSession(id, pendingInteractions, sessions.byId[id]?.pendingInteraction);
5968
6170
  const moveTargets = moveSession === void 0 ? void 0 : sessionMoveTargets(workspaces.items ?? [], id).map((target) => ({
5969
6171
  ...target,
@@ -5973,10 +6175,10 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
5973
6175
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow, {
5974
6176
  id,
5975
6177
  title,
5976
- selected: sessions.current === id,
6178
+ selected,
5977
6179
  menuOpen: menu?.id === id,
5978
6180
  unread,
5979
- running: session.running,
6181
+ running: sessionIsRunning(sessions.byId[id], status),
5980
6182
  pendingInteraction,
5981
6183
  time: session.updatedAt === void 0 ? void 0 : formatCompactTime(session.updatedAt, t, now),
5982
6184
  t,
@@ -6740,7 +6942,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6740
6942
  });
6741
6943
  }
6742
6944
  /** 搜索状态与大侧栏隔离:输入、悬停和开关弹窗都不能让工作区树跟着重渲染。 */
6743
- const SidebarSearch = (0, react.forwardRef)(function SidebarSearch({ imSettingsAvailable, openSession, startSession, t, useSessions, useWorkspaces, settingsSeat }, ref) {
6945
+ const SidebarSearch = (0, react.forwardRef)(function SidebarSearch({ imSettingsAvailable, openPlugins, openSession, startSession, t, useSessions, useWorkspaces, settingsSeat }, ref) {
6744
6946
  const [open, setOpen] = (0, react.useState)(false);
6745
6947
  const [query, setQuery] = (0, react.useState)("");
6746
6948
  const [activeIndex, setActiveIndex] = (0, react.useState)(0);
@@ -6758,9 +6960,6 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6758
6960
  const selectSection = (0, react.useCallback)((label) => {
6759
6961
  openSettingsSection(settingsSeat.current, label);
6760
6962
  }, [settingsSeat]);
6761
- const selectPluginSection = (0, react.useCallback)(() => {
6762
- openSettingsSection(settingsSeat.current, [t("sidebar.marketplace"), t("sidebar.plugins")]);
6763
- }, [settingsSeat, t]);
6764
6963
  const selectExternalSection = (0, react.useCallback)((label) => {
6765
6964
  openSettingsSection(settingsSeat.current, label, () => {
6766
6965
  selectSection(t("about.nav"));
@@ -6837,7 +7036,17 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6837
7036
  keywords: t("search.settings"),
6838
7037
  run: () => {
6839
7038
  close();
6840
- selectPluginSection();
7039
+ openPlugins();
7040
+ }
7041
+ },
7042
+ {
7043
+ id: "settings:plugin-config",
7044
+ group: "settings",
7045
+ label: t("settings.pluginConfig"),
7046
+ keywords: t("search.settings"),
7047
+ run: () => {
7048
+ close();
7049
+ openSettingsSection(settingsSeat.current, t("settings.pluginConfig"));
6841
7050
  }
6842
7051
  },
6843
7052
  {
@@ -6898,10 +7107,10 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6898
7107
  }, [
6899
7108
  close,
6900
7109
  openImSettings,
7110
+ openPlugins,
6901
7111
  openSession,
6902
7112
  openSettings,
6903
7113
  selectExternalSection,
6904
- selectPluginSection,
6905
7114
  selectSection,
6906
7115
  sessions.byId,
6907
7116
  sessions.ids,
@@ -7012,10 +7221,11 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7012
7221
  });
7013
7222
  });
7014
7223
  /** Codex 风格的 DSH 侧栏,只替换导航外观,项目浏览和设置仍由 DSH 官方组件提供。 */
7015
- function CodexSidebar({ globalPanels, footerActions, selectPanel, usePanelInfo = useLegacyPanelInfo, collapsed, width, openSession, startSession, toggleSidebar, archiveSession, canDeleteSession, deleteSession, forkSession, moveSession, renameSession, openPath, companionSlots, renderSlot, t, useSessions, useSessionPendingInteraction, useWorkspaces, prefillNewConversation, newConversationDraft }) {
7224
+ function CodexSidebar({ globalPanels, footerActions, selectPanel, usePanelInfo = useLegacyPanelInfo, collapsed, width, openSession, startSession, toggleSidebar, archiveSession, canDeleteSession, deleteSession, forkSession, moveSession, renameSession, openPath, companionSlots, renderSlot, t, useSessions, useSessionPendingInteraction, useSessionStatus, useWorkspaces, prefillNewConversation, newConversationDraft }) {
7016
7225
  const panels = (0, react.useSyncExternalStore)(globalPanels?.subscribe ?? subscribeEmptyCompanionTabs, globalPanels?.getSnapshot ?? getEmptyPanels, globalPanels?.getSnapshot ?? getEmptyPanels);
7017
7226
  const visibleFooterActions = (0, react.useSyncExternalStore)(footerActions?.subscribe ?? subscribeEmptyCompanionTabs, footerActions?.getSnapshot ?? getEmptyFooterActions, footerActions?.getSnapshot ?? getEmptyFooterActions);
7018
7227
  const activePanelId = usePanelInfo((info) => info.activePanelId);
7228
+ const panelActive = activePanelId !== null && activePanelId !== "";
7019
7229
  const panelButtons = (wide) => selectPanel === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GlobalPanelButtons, {
7020
7230
  panels,
7021
7231
  activeId: activePanelId,
@@ -7046,8 +7256,8 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7046
7256
  const selectSection = (label) => {
7047
7257
  openSettingsSection(settingsSeat.current, label);
7048
7258
  };
7049
- const selectPluginSection = () => {
7050
- openSettingsSection(settingsSeat.current, [t("sidebar.marketplace"), t("sidebar.plugins")]);
7259
+ const openPlugins = () => {
7260
+ openSettingsSection(settingsSeat.current, [t("sidebar.marketplace"), t("sidebar.builtinPlugins")]);
7051
7261
  };
7052
7262
  const selectExternalSection = (label) => {
7053
7263
  openSettingsSection(settingsSeat.current, label, () => {
@@ -7166,10 +7376,17 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7166
7376
  window.clearTimeout(timer);
7167
7377
  };
7168
7378
  }, [compact, visualCompact]);
7169
- const workspaceSlot = (0, react.useMemo)(() => renderSlot("sidebar.workspaces", {
7170
- wide: true,
7171
- expandSidebar
7172
- }), [expandSidebar, renderSlot]);
7379
+ const workspaceSlot = (0, react.useMemo)(() => {
7380
+ return renderSlot("sidebar.workspaces", {
7381
+ wide: true,
7382
+ expandSidebar,
7383
+ panelActive
7384
+ });
7385
+ }, [
7386
+ expandSidebar,
7387
+ panelActive,
7388
+ renderSlot
7389
+ ]);
7173
7390
  const scheduleOverviewSlot = (0, react.useMemo)(() => renderSlot("sidebar.schedule", {
7174
7391
  wide: true,
7175
7392
  expandSidebar,
@@ -7305,7 +7522,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7305
7522
  type: "button",
7306
7523
  tabIndex: extensionsOpen ? 0 : -1,
7307
7524
  onClick: () => {
7308
- selectPluginSection();
7525
+ openPlugins();
7309
7526
  },
7310
7527
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuIcon, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPersonalizationOutline16, { size: 16 }) }), t("sidebar.plugins")]
7311
7528
  }),
@@ -7381,7 +7598,9 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7381
7598
  renameSession,
7382
7599
  useSessions,
7383
7600
  useSessionPendingInteraction,
7601
+ useSessionStatus,
7384
7602
  useWorkspaces,
7603
+ panelActive,
7385
7604
  t
7386
7605
  })
7387
7606
  }) : imTab === "schedule" && showSchedule ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -7396,7 +7615,9 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7396
7615
  renameSession,
7397
7616
  useSessions,
7398
7617
  useSessionPendingInteraction,
7618
+ useSessionStatus,
7399
7619
  useWorkspaces,
7620
+ panelActive,
7400
7621
  t,
7401
7622
  overviewContent: scheduleOverviewSlot,
7402
7623
  openTaskSettings: (request) => {
@@ -7469,7 +7690,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7469
7690
  className: "dcu-icon",
7470
7691
  "aria-label": t("sidebar.plugins"),
7471
7692
  onClick: () => {
7472
- selectPluginSection();
7693
+ openPlugins();
7473
7694
  },
7474
7695
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPersonalizationOutline16, { size: 16 })
7475
7696
  }),
@@ -7515,6 +7736,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7515
7736
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSearch, {
7516
7737
  ref: search,
7517
7738
  imSettingsAvailable: showChannels,
7739
+ openPlugins,
7518
7740
  settingsSeat,
7519
7741
  openSession,
7520
7742
  startSession,
@@ -8054,7 +8276,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8054
8276
  });
8055
8277
  }
8056
8278
  function NativeConnectorList({ sessionStore, t }) {
8057
- const sessionId = (0, react.useSyncExternalStore)(sessionStore.subscribe, () => sessionStore.getSnapshot().current);
8279
+ const sessionId = (0, react.useSyncExternalStore)(sessionStore.subscribe, () => currentSessionId(sessionStore.getSnapshot()));
8058
8280
  const [connectors, setConnectors] = (0, react.useState)([]);
8059
8281
  const [state, setState] = (0, react.useState)("loading");
8060
8282
  const [failure, setFailure] = (0, react.useState)();
@@ -8209,6 +8431,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8209
8431
  "settings.search": "搜索设置…",
8210
8432
  "settings.personal": "个人",
8211
8433
  "settings.integrations": "集成",
8434
+ "settings.pluginConfig": "插件配置",
8212
8435
  "settings.records": "记录与应用",
8213
8436
  "settings.general": "常规",
8214
8437
  "settings.permissions": "权限",
@@ -8231,6 +8454,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8231
8454
  "sidebar.skills": "技能",
8232
8455
  "sidebar.plugins": "插件",
8233
8456
  "sidebar.marketplace": "插件市场",
8457
+ "sidebar.builtinPlugins": "内置插件",
8234
8458
  "sidebar.connectors": "连接器",
8235
8459
  "sidebar.schedule": "定时任务",
8236
8460
  "sidebar.assistant": "IM助理",
@@ -8497,6 +8721,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8497
8721
  "settings.search": "Search settings…",
8498
8722
  "settings.personal": "Personal",
8499
8723
  "settings.integrations": "Integrations",
8724
+ "settings.pluginConfig": "Plugin configuration",
8500
8725
  "settings.records": "Records and app",
8501
8726
  "settings.general": "General",
8502
8727
  "settings.permissions": "Permissions",
@@ -8519,6 +8744,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8519
8744
  "sidebar.skills": "Skills",
8520
8745
  "sidebar.plugins": "Plugins",
8521
8746
  "sidebar.marketplace": "Plugin Market",
8747
+ "sidebar.builtinPlugins": "Built-in plugins",
8522
8748
  "sidebar.connectors": "Connectors",
8523
8749
  "sidebar.schedule": "Scheduled tasks",
8524
8750
  "sidebar.assistant": "IM Assistant",
@@ -8811,6 +9037,8 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
8811
9037
  };
8812
9038
  const LABEL_ALIASES = [
8813
9039
  [["插件市场", "Plugin Market"], "plugins"],
9040
+ [["内置插件", "Built-in plugins"], "plugins"],
9041
+ [["插件配置", "Plugin configuration"], "plugins"],
8814
9042
  [["专家", "Experts"], "experts"],
8815
9043
  [["技能", "Skills"], "skills"],
8816
9044
  [["插件", "Plugins"], "plugins"],
@@ -9112,6 +9340,160 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
9112
9340
  }, "michengai-codex-ui: usage navigation");
9113
9341
  }
9114
9342
  //#endregion
9343
+ //#region src/client/PluginConfigSection.tsx
9344
+ function bindPluginConfigLocale(bind) {
9345
+ const cache = /* @__PURE__ */ new Map();
9346
+ return (ns) => {
9347
+ const existing = cache.get(ns);
9348
+ if (existing !== void 0) return existing;
9349
+ const translate = bind(ns);
9350
+ cache.set(ns, translate);
9351
+ return translate;
9352
+ };
9353
+ }
9354
+ const FORWARDED_CONFIG_SLOTS = /* @__PURE__ */ new Set([
9355
+ "plugins.item",
9356
+ "plugins.bundle.config",
9357
+ "plugins.row.config"
9358
+ ]);
9359
+ var SlotEntryBoundary = class extends react.Component {
9360
+ state = { failed: false };
9361
+ static getDerivedStateFromError() {
9362
+ return { failed: true };
9363
+ }
9364
+ render() {
9365
+ return this.state.failed ? this.props.fallback : this.props.children;
9366
+ }
9367
+ };
9368
+ function isSnapshotSource(value) {
9369
+ return typeof value === "object" && value !== null && typeof value.getSnapshot === "function" && typeof value.subscribe === "function";
9370
+ }
9371
+ function hookPropName(name) {
9372
+ return `use${name[0]?.toUpperCase() ?? ""}${name.slice(1)}`;
9373
+ }
9374
+ function bindSnapshotSelector(source) {
9375
+ const subscribe = (listener) => source.subscribe(listener);
9376
+ const getSnapshot = () => source.getSnapshot();
9377
+ return function useSelected(selector = (value) => value) {
9378
+ return (0, react.useSyncExternalStore)(subscribe, () => selector(getSnapshot()), () => selector(getSnapshot()));
9379
+ };
9380
+ }
9381
+ function bindHookSources(hooks, bound) {
9382
+ for (const [name, source] of Object.entries(hooks ?? {})) if (isSnapshotSource(source)) bound[hookPropName(name)] = bindSnapshotSelector(source);
9383
+ }
9384
+ function bindInjectFace(inject) {
9385
+ try {
9386
+ if (typeof inject !== "function") return {};
9387
+ const face = inject();
9388
+ if (typeof face !== "object" || face === null) return {};
9389
+ const { hooks, keyedHooks, ...rest } = face;
9390
+ const bound = { ...rest };
9391
+ if (keyedHooks !== void 0) bound.keyedHooks = keyedHooks;
9392
+ bindHookSources(hooks, bound);
9393
+ bindHookSources(keyedHooks, bound);
9394
+ return bound;
9395
+ } catch {
9396
+ return {};
9397
+ }
9398
+ }
9399
+ function DelegatedOfficialSlot({ slots, name, owner, opts, bindLocale }) {
9400
+ (0, react.useSyncExternalStore)((listener) => slots.subscribe(name, listener), () => slots.getVersion(name));
9401
+ const nodes = slots.entriesOfSlot(name).filter((entry) => opts?.entryKey !== void 0 ? entry.options.key === opts.entryKey : opts?.only === void 0 || entry.options.id === opts.only).flatMap((entry, index) => {
9402
+ if (typeof entry.component !== "function") return [];
9403
+ const t = entry.locale !== void 0 && bindLocale !== void 0 ? bindLocale(entry.locale) : void 0;
9404
+ return [(0, react.createElement)(SlotEntryBoundary, {
9405
+ key: entry.options.key ?? entry.options.id ?? String(index),
9406
+ fallback: opts?.fallback ?? null,
9407
+ children: (0, react.createElement)(entry.component, {
9408
+ ...owner,
9409
+ ...bindInjectFace(entry.inject),
9410
+ ...t === void 0 ? {} : { t }
9411
+ })
9412
+ })];
9413
+ });
9414
+ return nodes.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9415
+ "data-slot": name,
9416
+ style: { display: "contents" },
9417
+ children: nodes
9418
+ }) : opts?.fallback ?? null;
9419
+ }
9420
+ function wrapRenderSlot(settingsRenderSlot, slots, bindLocale) {
9421
+ return (name, owner = {}, opts) => {
9422
+ if (slots !== void 0 && FORWARDED_CONFIG_SLOTS.has(name)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DelegatedOfficialSlot, {
9423
+ slots,
9424
+ name,
9425
+ owner,
9426
+ opts,
9427
+ bindLocale
9428
+ });
9429
+ return typeof settingsRenderSlot === "function" ? settingsRenderSlot(name, owner, opts) : null;
9430
+ };
9431
+ }
9432
+ /** 设置里的官方插件管理页副本。官方卡片要 inject 和文案;单卡崩溃不得拆掉整页。 */
9433
+ function PluginConfigSection({ Official, officialLabel, slots, bindLocale, ...official }) {
9434
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
9435
+ className: "dcu-plugin-config",
9436
+ "aria-label": officialLabel,
9437
+ children: (0, react.createElement)(Official, {
9438
+ ...official,
9439
+ renderSlot: wrapRenderSlot(official.renderSlot, slots, bindLocale)
9440
+ })
9441
+ });
9442
+ }
9443
+ function createPluginConfigSection(Official, officialLabel, slots, bindLocale) {
9444
+ return function PluginConfigSectionBound(props) {
9445
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginConfigSection, {
9446
+ Official,
9447
+ officialLabel: officialLabel(),
9448
+ slots,
9449
+ bindLocale,
9450
+ ...props
9451
+ });
9452
+ };
9453
+ }
9454
+ //#endregion
9455
+ //#region src/client/plugin-config.ts
9456
+ const PLUGIN_CONFIG_SECTION_ID = "plugin-config";
9457
+ const OFFICIAL_PLUGIN_MANAGER_NS = "pluginManager";
9458
+ function officialPluginsPage(entry) {
9459
+ if (entry === void 0 || entry.options.key !== "plugins") return void 0;
9460
+ if (typeof entry.component !== "function" || typeof entry.inject !== "function" || entry.locale !== OFFICIAL_PLUGIN_MANAGER_NS) return void 0;
9461
+ return entry.component;
9462
+ }
9463
+ function registerPluginConfigSection(ctx) {
9464
+ const t = ctx.locale.bind(NS);
9465
+ ctx.slots.inject("settings.section", () => {
9466
+ let current;
9467
+ let remove;
9468
+ const refresh = () => {
9469
+ const entry = ctx.slots.entriesOfSlot("main").find((item) => item.options.key === OFFICIAL_PLUGINS_PANEL_ID);
9470
+ if (entry === current) return;
9471
+ remove?.();
9472
+ remove = void 0;
9473
+ current = entry;
9474
+ const Official = officialPluginsPage(entry);
9475
+ if (Official === void 0 || entry?.inject === void 0 || entry.locale !== OFFICIAL_PLUGIN_MANAGER_NS) return;
9476
+ remove = ctx.slots.register({
9477
+ name: "settings.section",
9478
+ id: PLUGIN_CONFIG_SECTION_ID,
9479
+ order: 16,
9480
+ label: () => t("settings.pluginConfig"),
9481
+ locale: entry.locale,
9482
+ inject: entry.inject
9483
+ }, createPluginConfigSection(Official, () => t("settings.pluginConfig"), ctx.slots, bindPluginConfigLocale((ns) => {
9484
+ const translate = ctx.locale.bind(ns);
9485
+ return (key, params) => translate(key, params);
9486
+ })));
9487
+ };
9488
+ const unsubscribe = ctx.slots.subscribe("main", refresh);
9489
+ refresh();
9490
+ return () => {
9491
+ unsubscribe();
9492
+ remove?.();
9493
+ };
9494
+ });
9495
+ }
9496
+ //#endregion
9115
9497
  //#region src/client/settings-page-model.ts
9116
9498
  function settingsGroup(id) {
9117
9499
  if (/archive|about|^usage-statistics$/.test(id)) return "records";
@@ -9213,6 +9595,10 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
9213
9595
  .dcu-settings-inner:has(.dcu-usage-section){height:100%;display:flex;flex-direction:column;padding-bottom:0}
9214
9596
  .dcu-settings-inner:has(.dcu-usage-section)>.dcu-settings-heading{display:none}
9215
9597
  .dcu-settings-inner:has(.dcu-usage-section)>[data-slot="settings.section"]{display:flex;flex:1;min-height:0;flex-direction:column}
9598
+ .dcu-plugin-config [data-plugin-panel]{height:auto;padding:0;overflow:visible;align-items:stretch;gap:24px}
9599
+ .dcu-plugin-config [data-plugin-panel]>*{max-width:none}
9600
+ .dcu-plugin-config [data-plugin-panel]>header :is(h1,p){display:none}
9601
+ .dcu-plugin-config [data-plugin-panel]>header{justify-content:flex-end}
9216
9602
  .dcu-usage-section{display:flex;flex-direction:column;flex:1;min-height:0;gap:16px}
9217
9603
  .dcu-usage-section h1{font-size:24px;font-weight:500;margin:0}
9218
9604
  .dcu-usage-section p{color:var(--sp-muted)}
@@ -9273,7 +9659,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
9273
9659
  const rows = (0, react.useSyncExternalStore)(sections.subscribe, sections.getSnapshot);
9274
9660
  const steps = (0, react.useSyncExternalStore)(onboarding.subscribe, onboarding.getSnapshot);
9275
9661
  const connection = (0, react.useSyncExternalStore)(connectionState.subscribe, connectionState.getSnapshot);
9276
- const onboardingActive = useSessions((state) => state.phase === "ready" && (state.current === void 0 || state.byId[state.current]?.blank === true));
9662
+ const onboardingActive = useSessions((state) => isBlankOnboardingSession(state));
9277
9663
  const [completed, setCompleted] = (0, react.useState)(() => /* @__PURE__ */ new Set());
9278
9664
  const [open, setOpen] = (0, react.useState)(false);
9279
9665
  const [activeId, setActiveId] = (0, react.useState)("general");
@@ -9428,6 +9814,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
9428
9814
  if (main.current !== null) main.current.scrollTop = 0;
9429
9815
  }, [active?.id]);
9430
9816
  const visible = filterSettingsRows(rows, query);
9817
+ const ownTitle = active?.id === "general" ? t("settings.general") : active?.id === "plugin-config" ? t("settings.pluginConfig") : void 0;
9431
9818
  const connectionIndicator = connection === "disconnected" ? "disconnected" : connection === "connecting" ? "connecting" : recovered ? "recovered" : void 0;
9432
9819
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9433
9820
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: settingsPageStyles }),
@@ -9537,8 +9924,8 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
9537
9924
  "data-settings-section": active?.id,
9538
9925
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
9539
9926
  className: "dcu-settings-heading",
9540
- "data-own-title": active?.id === "general",
9541
- children: [active?.id === "general" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: t("settings.general") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9927
+ "data-own-title": ownTitle !== void 0,
9928
+ children: [ownTitle !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: ownTitle }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9542
9929
  className: "dcu-settings-actions",
9543
9930
  children: renderSlot("settings.action", {})
9544
9931
  })]
@@ -10470,7 +10857,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10470
10857
  }
10471
10858
  /** Archive Manager replaces the official ui-workspace row with this optional service. */
10472
10859
  function startWorkspaceSession(ctx, workspaceId) {
10473
- const uiWorkspace = ctx.get("uiWorkspace");
10860
+ const uiWorkspace = probeService(ctx, "uiWorkspace");
10474
10861
  if (hasStartSession(uiWorkspace)) {
10475
10862
  uiWorkspace.startSession(workspaceId);
10476
10863
  return;
@@ -10483,6 +10870,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10483
10870
  }
10484
10871
  /** 替换 DSH 的官方 sidebar 插槽,不修改 DSH 源码或会话数据。 */
10485
10872
  function apply(ctx) {
10873
+ Object.assign(globalThis, { __dcuCurrentSessionId: currentSessionId });
10486
10874
  const widthStorage = browserStorage();
10487
10875
  if (widthStorage) initializeComposerWidth(widthStorage);
10488
10876
  ctx.effect(() => ctx.locale.register(NS, {
@@ -10494,6 +10882,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10494
10882
  registerInputHistory(ctx);
10495
10883
  registerSettingsPage(ctx);
10496
10884
  registerUsageStatistics(ctx);
10885
+ registerPluginConfigSection(ctx);
10497
10886
  const connection = ctx.get("connection");
10498
10887
  const openPath = (path) => openPathInHost(connection, path);
10499
10888
  ctx.effect(() => observeSlimSidebar(), "michengai-codex-ui: slim sidebar");
@@ -10505,7 +10894,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10505
10894
  ctx.effect(() => observeConversationHeader(), "michengai-codex-ui: conversation header");
10506
10895
  ctx.effect(() => observeOfficialTurnNavigators(), "michengai-codex-ui: official turn navigator");
10507
10896
  const newConversationDraft = createDraftPresenceSource(ctx.sessions.list, () => {
10508
- const id = ctx.sessions.list.getSnapshot().current;
10897
+ const id = currentSessionId(ctx.sessions.list.getSnapshot());
10509
10898
  const binding = id === void 0 ? void 0 : ctx.sessions.binding(id);
10510
10899
  return binding === void 0 ? void 0 : ctx.conversation.input.for(binding.ctx).state;
10511
10900
  });
@@ -10545,12 +10934,12 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10545
10934
  inject: () => ({
10546
10935
  newConversationDraft,
10547
10936
  prefillNewConversation: (text) => {
10548
- const id = ctx.sessions.list.getSnapshot().current;
10937
+ const id = currentSessionId(ctx.sessions.list.getSnapshot());
10549
10938
  const binding = id === void 0 ? void 0 : ctx.sessions.binding(id);
10550
10939
  return prefillNewConversation(binding === void 0 ? void 0 : ctx.conversation.input.for(binding.ctx), text);
10551
10940
  },
10552
10941
  openSession: (sessionId) => {
10553
- openConversation(ctx.sessions, ctx.layout, sessionId);
10942
+ openConversation(ctx, ctx.layout, sessionId);
10554
10943
  },
10555
10944
  startSession: (workspaceId) => {
10556
10945
  startWorkspaceSession(ctx, workspaceId);
@@ -10580,20 +10969,16 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10580
10969
  locale: NS
10581
10970
  }, TurnNavigator));
10582
10971
  const forkSession = async (sessionId) => {
10583
- await runHostAction("fork", async () => {
10584
- const childId = await ctx.sessions.fork({
10585
- sessionId,
10586
- increaseTitle: true
10587
- });
10588
- openConversation(ctx.sessions, ctx.layout, childId);
10589
- });
10972
+ await runHostAction("fork", () => forkHostSession(ctx, sessionId));
10590
10973
  };
10591
10974
  const renameSession = async (sessionId, title) => {
10592
- const session = ctx.sessions.binding(sessionId)?.session;
10593
- if (session === void 0) throw new UserFacingError(t("sessions.unknown"));
10594
10975
  await runHostAction("rename", async () => {
10595
- const result = await session.rename(title);
10596
- if (!result.ok) throw result.error;
10976
+ try {
10977
+ await renameHostSession(ctx, sessionId, title);
10978
+ } catch (reason) {
10979
+ if (reason instanceof UnknownSessionError) throw new UserFacingError(t("sessions.unknown"));
10980
+ throw reason;
10981
+ }
10597
10982
  });
10598
10983
  };
10599
10984
  const deleteSession = async (sessionId) => {
@@ -10604,7 +10989,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10604
10989
  if (!result.ok) throw result.error === void 0 ? new UserFacingError(t("sessions.deleteUnavailable")) : result.error;
10605
10990
  });
10606
10991
  };
10607
- const archiveSession = (sessionId) => runHostAction("archive", () => ctx.workspaces.archiveSession(sessionId));
10992
+ const archiveSession = (sessionId) => runHostAction("archive", () => archiveHostSession(ctx, sessionId));
10608
10993
  const moveSession = async (sessionId, targetWorkspaceId) => {
10609
10994
  try {
10610
10995
  await requestSessionMove(sessionId, targetWorkspaceId);
@@ -10625,22 +11010,36 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10625
11010
  if (prompt === "") throw new UserFacingError(t("connectors.promptRequired"));
10626
11011
  const workspaces = ctx.workspaces.list.getSnapshot();
10627
11012
  const sessionSnapshot = ctx.sessions.list.getSnapshot();
10628
- const currentSessionId = sessionSnapshot.current;
10629
- const currentWorkspaceId = currentSessionId === void 0 ? void 0 : workspaces.items.find((workspace) => workspace.sessionIds.includes(currentSessionId))?.workspaceId;
11013
+ const selectedSessionId = currentSessionId(sessionSnapshot);
11014
+ const currentWorkspaceId = selectedSessionId === void 0 ? void 0 : workspaces.items.find((workspace) => workspace.sessionIds.includes(selectedSessionId))?.workspaceId;
10630
11015
  const baselinesReady = workspaceBaselinesReady(workspaces, sessionSnapshot);
10631
11016
  const targetWorkspaceId = currentWorkspaceId ?? (baselinesReady ? recentWorkspaceId(workspaces.items, sessionSnapshot.byId) : void 0);
10632
11017
  if (targetWorkspaceId === void 0 && !baselinesReady) throw new UserFacingError(t("connectors.workspacesLoading"));
10633
11018
  if (targetWorkspaceId === void 0) throw new UserFacingError(t("connectors.workspaceRequired"));
10634
- const uiWorkspace = ctx.get("uiWorkspace");
10635
- const workspaceNavigation = hasConnectWorkspace(uiWorkspace) ? uiWorkspace : hasConnectWorkspace(ctx.workspaces) ? ctx.workspaces : void 0;
10636
- if (workspaceNavigation === void 0) throw new UserFacingError(t("connectors.workspaceUnavailable"));
10637
- const sessionId = await workspaceNavigation.connectWorkspace(targetWorkspaceId);
11019
+ const uiWorkspace = probeService(ctx, "uiWorkspace");
10638
11020
  const conversation = ctx.get("conversation");
10639
11021
  if (conversation === void 0) throw new UserFacingError(t("connectors.conversationUnavailable"));
10640
- const binding = ctx.sessions.binding(sessionId);
10641
- if (binding === void 0) throw new UserFacingError(t("connectors.sessionPending"));
10642
- conversation.input.for(binding.ctx).setDraft(prompt);
10643
- openConversation(ctx.sessions, ctx.layout, sessionId);
11022
+ try {
11023
+ if (hasOpenWorkspace(uiWorkspace)) {
11024
+ await Promise.resolve(uiWorkspace.openWorkspace(targetWorkspaceId, (id) => {
11025
+ const binding = ctx.sessions.binding(id);
11026
+ if (binding?.ctx === void 0) throw new UnknownSessionError();
11027
+ conversation.input.for(binding.ctx).setDraft(prompt);
11028
+ }));
11029
+ selectGlobalPanel(ctx.layout, null);
11030
+ return;
11031
+ }
11032
+ const workspaceNavigation = hasConnectWorkspace(uiWorkspace) ? uiWorkspace : hasConnectWorkspace(ctx.workspaces) ? ctx.workspaces : void 0;
11033
+ if (workspaceNavigation === void 0) throw new UserFacingError(t("connectors.workspaceUnavailable"));
11034
+ const sessionId = await workspaceNavigation.connectWorkspace(targetWorkspaceId);
11035
+ await openConversationWithDraft(ctx, ctx.layout, ctx.sessions, sessionId, (binding) => {
11036
+ if (binding.ctx === void 0) throw new UnknownSessionError();
11037
+ conversation.input.for(binding.ctx).setDraft(prompt);
11038
+ });
11039
+ } catch (reason) {
11040
+ if (reason instanceof UnknownSessionError) throw new UserFacingError(t("connectors.sessionPending"));
11041
+ throw reason;
11042
+ }
10644
11043
  };
10645
11044
  ctx.slots.inject("sidebar.workspaces", () => ctx.slots.register({
10646
11045
  name: "sidebar.workspaces",
@@ -10655,7 +11054,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10655
11054
  moveSession,
10656
11055
  openPath,
10657
11056
  openSession: (sessionId) => {
10658
- openConversation(ctx.sessions, ctx.layout, sessionId);
11057
+ openConversation(ctx, ctx.layout, sessionId);
10659
11058
  },
10660
11059
  renameSession,
10661
11060
  renameWorkspace: (workspaceId, title) => ctx.workspaces.rename(workspaceId, title),
@@ -10674,7 +11073,7 @@ html[data-dsh-native-backdrop=mica] .dcu-settings-page,html[data-dsh-native-back
10674
11073
  const openDeepLink = () => {
10675
11074
  if (opened || ctx.sessions.list.getSnapshot().byId[sessionId] === void 0) return;
10676
11075
  opened = true;
10677
- openConversation(ctx.sessions, ctx.layout, sessionId);
11076
+ openConversation(ctx, ctx.layout, sessionId);
10678
11077
  };
10679
11078
  openDeepLink();
10680
11079
  return ctx.sessions.list.subscribe(openDeepLink);