@michengai/dsh-codex-ui 0.2.58 → 0.2.60
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/assets/branding/dsh-banner.png +0 -0
- package/dist/client.js +359 -57
- package/dist/index.mjs +76 -26
- package/package.json +6 -6
|
Binary file
|
package/dist/client.js
CHANGED
|
@@ -262,6 +262,7 @@ window.__ModuleLoader__.load({
|
|
|
262
262
|
//#endregion
|
|
263
263
|
//#region src/client/session-manager.ts
|
|
264
264
|
const SESSION_PINS_STORAGE_KEY = "dsh.session-pins.v1";
|
|
265
|
+
const SESSION_SECTION_PINS_STORAGE_KEY = "dsh-codex-ui.pinned-section-sessions.v1";
|
|
265
266
|
const SESSION_UNREAD_STORAGE_KEY = "dsh.session-unread.v1";
|
|
266
267
|
function normalizeSessionIds(ids) {
|
|
267
268
|
return [...new Set(ids.filter((id) => id.trim() !== ""))];
|
|
@@ -294,6 +295,15 @@ window.__ModuleLoader__.load({
|
|
|
294
295
|
url.searchParams.set("session", sessionId);
|
|
295
296
|
return url.toString();
|
|
296
297
|
}
|
|
298
|
+
/** 把会话插入置顶列表的指定位置;省略锚点时追加到末尾。 */
|
|
299
|
+
function insertSessionId(ids, id, beforeId) {
|
|
300
|
+
const next = ids.filter((item) => item !== id);
|
|
301
|
+
if (beforeId === void 0) return [...next, id];
|
|
302
|
+
if (beforeId === id) return ids.includes(id) ? [...ids] : [...next, id];
|
|
303
|
+
const index = next.indexOf(beforeId);
|
|
304
|
+
next.splice(index < 0 ? next.length : index, 0, id);
|
|
305
|
+
return next;
|
|
306
|
+
}
|
|
297
307
|
//#endregion
|
|
298
308
|
//#region src/client/session-tree.tsx
|
|
299
309
|
function PinIcon() {
|
|
@@ -430,19 +440,33 @@ window.__ModuleLoader__.load({
|
|
|
430
440
|
function pointerMenuRect(x, y) {
|
|
431
441
|
return new DOMRect(x, y, 0, 0);
|
|
432
442
|
}
|
|
433
|
-
function SessionRow({ id, title, selected, menuOpen, pinned, unread, running, t, menuItems, onOpen, onMenuChange, onSelectAction, onPin, onArchive, onHover, onLeave, onContextMenu, menuPoint }) {
|
|
443
|
+
function SessionRow({ id, title, selected, menuOpen, pinned, unread, running, t, menuItems, onOpen, onMenuChange, onSelectAction, onPin, onArchive, onHover, onLeave, onContextMenu, menuPoint, subtitle, flat, draggable, dropActive, onDragStart, onDragEnd, onDragOver, onDrop }) {
|
|
434
444
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
435
|
-
className: `dcu-wb-session${selected ? " dcu-wb-selected" : ""}${menuOpen ? " dcu-wb-menu-open" : ""}`,
|
|
445
|
+
className: `dcu-wb-session${selected ? " dcu-wb-selected" : ""}${menuOpen ? " dcu-wb-menu-open" : ""}${dropActive === true ? " dcu-wb-drop" : ""}${flat === true ? " dcu-wb-session-flat" : ""}`,
|
|
436
446
|
role: "treeitem",
|
|
437
447
|
"aria-selected": selected,
|
|
448
|
+
draggable,
|
|
449
|
+
onDragStart,
|
|
450
|
+
onDragEnd,
|
|
451
|
+
onDragOver,
|
|
452
|
+
onDrop,
|
|
438
453
|
onClick: onOpen,
|
|
439
454
|
onContextMenu,
|
|
440
455
|
onMouseEnter: onHover,
|
|
441
456
|
onMouseLeave: onLeave,
|
|
442
457
|
children: [
|
|
443
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
458
|
+
subtitle !== void 0 && subtitle !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
459
|
+
className: "dcu-wb-session-copy",
|
|
460
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
461
|
+
className: "dcu-wb-session-title",
|
|
462
|
+
children: title.split(/\r?\n/)[0] ?? title
|
|
463
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
464
|
+
className: "dcu-wb-session-sub",
|
|
465
|
+
children: subtitle
|
|
466
|
+
})]
|
|
467
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
444
468
|
className: "dcu-wb-session-title",
|
|
445
|
-
children: title
|
|
469
|
+
children: title.split(/\r?\n/)[0] ?? title
|
|
446
470
|
}),
|
|
447
471
|
pinned && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
448
472
|
className: "dcu-wb-pin",
|
|
@@ -1097,6 +1121,65 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1097
1121
|
return item === void 0 ? [] : [item];
|
|
1098
1122
|
});
|
|
1099
1123
|
}
|
|
1124
|
+
/** 置顶区的会话按拖入顺序展示;不因父项目已置顶而隐藏。 */
|
|
1125
|
+
function standalonePinnedSessionIds(pinnedSessionIds) {
|
|
1126
|
+
return [...pinnedSessionIds];
|
|
1127
|
+
}
|
|
1128
|
+
/** 置顶区已有会话时,不再同时展示任何项目文件夹,避免旧置顶项目叠成两条。 */
|
|
1129
|
+
function workspaceIdsHiddenByPinnedSessions(workspaces, pinnedSessionIds) {
|
|
1130
|
+
if (pinnedSessionIds.length === 0) return [];
|
|
1131
|
+
return workspaces.map((workspace) => String(workspace.workspaceId));
|
|
1132
|
+
}
|
|
1133
|
+
const SESSION_DRAG_TYPE = "application/x-dcu-session";
|
|
1134
|
+
const WORKSPACE_DRAG_TYPE = "application/x-dcu-workspace";
|
|
1135
|
+
const SESSION_DRAG_PREFIX = "dcu-session:";
|
|
1136
|
+
const WORKSPACE_DRAG_PREFIX = "dcu-workspace:";
|
|
1137
|
+
function dragTypes(data) {
|
|
1138
|
+
return data === void 0 ? [] : Array.from(data.types);
|
|
1139
|
+
}
|
|
1140
|
+
function textPayload(data) {
|
|
1141
|
+
try {
|
|
1142
|
+
return data?.getData("text/plain") ?? "";
|
|
1143
|
+
} catch {
|
|
1144
|
+
return "";
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
/** 拖过置顶区时用来决定是否 preventDefault;只看 types,不读数据。 */
|
|
1148
|
+
function isSidebarItemDrag(data) {
|
|
1149
|
+
const types = dragTypes(data);
|
|
1150
|
+
return types.includes("application/x-dcu-session") || types.includes("application/x-dcu-workspace") || types.includes("text/plain");
|
|
1151
|
+
}
|
|
1152
|
+
/** 写入会话拖拽载荷;drop 时以这个为准,不依赖尚未刷新的 React 状态。 */
|
|
1153
|
+
function writeSessionDrag(data, sessionId, title) {
|
|
1154
|
+
data.effectAllowed = "move";
|
|
1155
|
+
data.setData("text/plain", `${SESSION_DRAG_PREFIX}${sessionId}`);
|
|
1156
|
+
data.setData(SESSION_DRAG_TYPE, sessionId);
|
|
1157
|
+
}
|
|
1158
|
+
function writeWorkspaceDrag(data, workspaceId, title) {
|
|
1159
|
+
data.effectAllowed = "move";
|
|
1160
|
+
data.setData("text/plain", `${WORKSPACE_DRAG_PREFIX}${workspaceId}`);
|
|
1161
|
+
data.setData(WORKSPACE_DRAG_TYPE, workspaceId);
|
|
1162
|
+
}
|
|
1163
|
+
function readSessionDrag(data, fallback) {
|
|
1164
|
+
try {
|
|
1165
|
+
const typed = data?.getData(SESSION_DRAG_TYPE);
|
|
1166
|
+
if (typed !== void 0 && typed.trim() !== "") return typed;
|
|
1167
|
+
} catch {}
|
|
1168
|
+
const text = textPayload(data);
|
|
1169
|
+
if (text.startsWith(SESSION_DRAG_PREFIX)) return text.slice(12);
|
|
1170
|
+
return fallback !== void 0 && fallback.trim() !== "" ? fallback : void 0;
|
|
1171
|
+
}
|
|
1172
|
+
/** 会话拖拽优先;有会话载荷时不得再把父项目置顶。 */
|
|
1173
|
+
function readWorkspaceDrag(data, fallback) {
|
|
1174
|
+
if (readSessionDrag(data) !== void 0) return void 0;
|
|
1175
|
+
try {
|
|
1176
|
+
const typed = data?.getData(WORKSPACE_DRAG_TYPE);
|
|
1177
|
+
if (typed !== void 0 && typed.trim() !== "") return typed;
|
|
1178
|
+
} catch {}
|
|
1179
|
+
const text = textPayload(data);
|
|
1180
|
+
if (text.startsWith(WORKSPACE_DRAG_PREFIX)) return text.slice(14);
|
|
1181
|
+
return fallback !== void 0 && fallback.trim() !== "" ? fallback : void 0;
|
|
1182
|
+
}
|
|
1100
1183
|
//#endregion
|
|
1101
1184
|
//#region src/client/CodexWorkspaceBrowser.tsx
|
|
1102
1185
|
const stylesheet$4 = `
|
|
@@ -1111,13 +1194,13 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1111
1194
|
.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-head+.dcu-wb-session,.dcu-wb-project-head+.dcu-wb-nochat{margin-top:4px}.dcu-wb-session+.dcu-wb-session{margin-top:2px}
|
|
1112
1195
|
.dcu-wb-project-head[draggable=true],.dcu-wb-session[draggable=true]{cursor:grab}
|
|
1113
1196
|
.dcu-wb-project-head[draggable=true]:active,.dcu-wb-session[draggable=true]:active{cursor:grabbing}
|
|
1114
|
-
.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{position:relative;height:8px}.dcu-wb-project-head.dcu-wb-drop::before,.dcu-wb-session.dcu-wb-drop::before,.dcu-wb-pin-end.dcu-wb-drop::before,.dcu-wb-section[data-pin-over=true] .dcu-wb-section-head::after{content:"";position:absolute;left:8px;right:8px;top:-4px;height:8px;pointer-events:none;background:radial-gradient(circle at 4px 50%,var(--dsw-alias-state-business-primary) 3.25px,transparent 3.45px),linear-gradient(var(--dsw-alias-state-business-primary),var(--dsw-alias-state-business-primary)) 10px 50%/calc(100% - 10px) 2px no-repeat}.dcu-wb-drag-ghost{position:fixed;top:-120px;left:-240px;z-index:10040;max-width:220px;height:32px;padding:0 10px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:var(--dcu-sidebar-hover);color:var(--dcu-sidebar-primary);font:13px/32px var(--dcu-font,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}
|
|
1197
|
+
.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{position:relative;height:8px}.dcu-wb-project-head.dcu-wb-drop::before,.dcu-wb-session.dcu-wb-drop::before,.dcu-wb-pin-end.dcu-wb-drop::before,.dcu-wb-section[data-pin-over=true] .dcu-wb-section-head::after{content:"";position:absolute;left:8px;right:8px;top:-4px;height:8px;pointer-events:none;background:radial-gradient(circle at 4px 50%,var(--dsw-alias-state-business-primary) 3.25px,transparent 3.45px),linear-gradient(var(--dsw-alias-state-business-primary),var(--dsw-alias-state-business-primary)) 10px 50%/calc(100% - 10px) 2px no-repeat}.dcu-wb-project-head.dcu-wb-drop-after::before,.dcu-wb-session.dcu-wb-drop-after::before{top:auto;bottom:-4px}.dcu-wb-dragging{opacity:.28}.dcu-wb-drag-ghost{position:fixed;top:-120px;left:-240px;z-index:10040;max-width:220px;height:32px;padding:0 10px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;background:var(--dcu-sidebar-hover);color:var(--dcu-sidebar-primary);font:13px/32px var(--dcu-font,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}
|
|
1115
1198
|
.dcu-wb-folder{display:grid;place-items:center;flex:none;width:16px;height:20px;color:var(--dcu-sidebar-icon)}.dcu-wb-brand{display:block;width:16px;height:16px}
|
|
1116
1199
|
.dcu-wb-project-current .dcu-wb-folder{color:var(--dsw-alias-state-business-primary)}
|
|
1117
1200
|
.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}
|
|
1118
1201
|
.dcu-wb-project-title{flex:1;font-weight:550;color:var(--dcu-sidebar-primary)}
|
|
1119
1202
|
.dcu-wb-session{position:relative;min-height:32px;gap:0;padding-left:28px}
|
|
1120
|
-
.dcu-wb-session-title{flex:1;margin-left:0}
|
|
1203
|
+
.dcu-wb-session-title{flex:1;margin-left:0}.dcu-wb-session-copy{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center}.dcu-wb-session-sub{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dcu-sidebar-tertiary);font-size:12px;line-height:16px}.dcu-wb-session-flat,.dcu-wb-session-top{padding-left:8px}
|
|
1121
1204
|
.dcu-wb-pin{display:grid;place-items:center;flex:none;width:16px;height:20px;margin-left:6px;color:var(--dcu-sidebar-tertiary)}
|
|
1122
1205
|
.dcu-wb-pin svg,.dcu-wb-quick-pin svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.4}
|
|
1123
1206
|
.dcu-wb-actions{display:none;align-items:center;gap:8px;flex:none}
|
|
@@ -1162,6 +1245,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1162
1245
|
const [expanded, setExpanded] = (0, react.useState)({});
|
|
1163
1246
|
const [pinnedWorkspaceIds, setPinnedWorkspaceIds] = (0, react.useState)(() => readPinnedWorkspaceIds(storage$1()));
|
|
1164
1247
|
const [pinnedSessionIds, setPinnedSessionIds] = (0, react.useState)(() => readSessionIds(storage$1(), SESSION_PINS_STORAGE_KEY));
|
|
1248
|
+
const [sectionSessionIds, setSectionSessionIds] = (0, react.useState)(() => readSessionIds(storage$1(), SESSION_SECTION_PINS_STORAGE_KEY));
|
|
1165
1249
|
const [unreadSessionIds, setUnreadSessionIds] = (0, react.useState)(() => readSessionIds(storage$1(), SESSION_UNREAD_STORAGE_KEY));
|
|
1166
1250
|
const [menu, setMenu] = (0, react.useState)();
|
|
1167
1251
|
const [renameTarget, setRenameTarget] = (0, react.useState)();
|
|
@@ -1176,6 +1260,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1176
1260
|
const [error, setError] = (0, react.useState)();
|
|
1177
1261
|
const [workspaceDragId, setWorkspaceDragId] = (0, react.useState)();
|
|
1178
1262
|
const [workspaceDropId, setWorkspaceDropId] = (0, react.useState)();
|
|
1263
|
+
const [workspaceDropAfter, setWorkspaceDropAfter] = (0, react.useState)(false);
|
|
1179
1264
|
const [headerMenu, setHeaderMenu] = (0, react.useState)();
|
|
1180
1265
|
const headerMenuRef = (0, react.useRef)();
|
|
1181
1266
|
headerMenuRef.current = headerMenu;
|
|
@@ -1189,6 +1274,13 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1189
1274
|
(0, react.useEffect)(() => {
|
|
1190
1275
|
writeSessionIds(storage$1(), SESSION_PINS_STORAGE_KEY, pinnedSessionIds);
|
|
1191
1276
|
}, [pinnedSessionIds]);
|
|
1277
|
+
(0, react.useEffect)(() => {
|
|
1278
|
+
writeSessionIds(storage$1(), SESSION_SECTION_PINS_STORAGE_KEY, sectionSessionIds);
|
|
1279
|
+
}, [sectionSessionIds]);
|
|
1280
|
+
(0, react.useEffect)(() => {
|
|
1281
|
+
if (sectionSessionIds.length === 0) return;
|
|
1282
|
+
setPinnedWorkspaceIds((ids) => ids.length === 0 ? ids : []);
|
|
1283
|
+
}, [sectionSessionIds]);
|
|
1192
1284
|
(0, react.useEffect)(() => {
|
|
1193
1285
|
writeSessionIds(storage$1(), SESSION_UNREAD_STORAGE_KEY, unreadSessionIds);
|
|
1194
1286
|
}, [unreadSessionIds]);
|
|
@@ -1275,11 +1367,14 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1275
1367
|
sessions.current,
|
|
1276
1368
|
menu
|
|
1277
1369
|
]);
|
|
1278
|
-
const
|
|
1279
|
-
const
|
|
1370
|
+
const pinSectionSessions = standalonePinnedSessionIds(sectionSessionIds);
|
|
1371
|
+
const hiddenPinnedWorkspaceIds = new Set(workspaceIdsHiddenByPinnedSessions(groups.items, pinSectionSessions));
|
|
1372
|
+
const projectPinned = (id) => pinnedWorkspaceIds.includes(String(id)) && !hiddenPinnedWorkspaceIds.has(String(id));
|
|
1373
|
+
const pinnedGroups = orderByIds(groups.items, pinnedWorkspaceIds, (workspace) => String(workspace.workspaceId)).filter((workspace) => !hiddenPinnedWorkspaceIds.has(String(workspace.workspaceId)));
|
|
1280
1374
|
const regularGroups = groups.items.filter((workspace) => !projectPinned(workspace.workspaceId));
|
|
1375
|
+
const pinDragActive = workspaceDragId !== void 0 || sessionDrag !== void 0;
|
|
1281
1376
|
const assignedIds = workspaces.items.flatMap((workspace) => workspace.sessionIds.map((id) => String(id)));
|
|
1282
|
-
const recentIds = ungroupedSessionIds(sessions.ids ?? Object.keys(sessions.byId), sessions.byId, assignedIds, workspaces.archivedSessionIds).sort((left, right) => (sessions.byId[right]?.updatedAt ?? 0) - (sessions.byId[left]?.updatedAt ?? 0));
|
|
1377
|
+
const recentIds = ungroupedSessionIds(sessions.ids ?? Object.keys(sessions.byId), sessions.byId, assignedIds, workspaces.archivedSessionIds).filter((id) => !pinSectionSessions.includes(id)).sort((left, right) => (sessions.byId[right]?.updatedAt ?? 0) - (sessions.byId[left]?.updatedAt ?? 0));
|
|
1283
1378
|
const run = async (key, action) => {
|
|
1284
1379
|
setBusy(key);
|
|
1285
1380
|
setError(void 0);
|
|
@@ -1317,6 +1412,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1317
1412
|
if (target.kind === "session") {
|
|
1318
1413
|
await deleteSession(target.id);
|
|
1319
1414
|
setPinnedSessionIds((ids) => ids.filter((id) => id !== target.id));
|
|
1415
|
+
setSectionSessionIds((ids) => ids.filter((id) => id !== target.id));
|
|
1320
1416
|
setUnreadSessionIds((ids) => ids.filter((id) => id !== target.id));
|
|
1321
1417
|
} else {
|
|
1322
1418
|
await deleteWorkspace(target.id);
|
|
@@ -1331,10 +1427,10 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1331
1427
|
await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(value);
|
|
1332
1428
|
});
|
|
1333
1429
|
};
|
|
1334
|
-
const toggleGroup = (
|
|
1430
|
+
const toggleGroup = (key, defaultOpen = true) => {
|
|
1335
1431
|
setExpanded((current) => ({
|
|
1336
1432
|
...current,
|
|
1337
|
-
[
|
|
1433
|
+
[key]: !(current[key] ?? defaultOpen)
|
|
1338
1434
|
}));
|
|
1339
1435
|
};
|
|
1340
1436
|
const sectionOpen = (id) => expanded[`section:${id}`] ?? true;
|
|
@@ -1385,8 +1481,14 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1385
1481
|
const pinWorkspaceAt = (id, beforeId) => {
|
|
1386
1482
|
setPinnedWorkspaceIds((ids) => insertPinnedWorkspace(ids, id, beforeId));
|
|
1387
1483
|
};
|
|
1484
|
+
const pinSessionAt = (id, beforeId) => {
|
|
1485
|
+
setSectionSessionIds((ids) => insertSessionId(ids, id, beforeId));
|
|
1486
|
+
setPinnedWorkspaceIds((ids) => ids.length === 0 ? ids : []);
|
|
1487
|
+
};
|
|
1388
1488
|
const renderGroup = (workspace, zone) => {
|
|
1389
|
-
const
|
|
1489
|
+
const expandKey = zone === "pinned" ? `pin:${workspace.workspaceId}` : String(workspace.workspaceId);
|
|
1490
|
+
const isExpanded = expanded[expandKey] ?? zone !== "pinned";
|
|
1491
|
+
const shownIds = workspace.visibleIds.filter((id) => !pinSectionSessions.includes(id));
|
|
1390
1492
|
const menuOpen = menu?.type === "workspace" && menu.id === workspace.workspaceId;
|
|
1391
1493
|
const menuAt = menuOpen && menu.x !== void 0 && menu.y !== void 0 ? {
|
|
1392
1494
|
x: menu.x,
|
|
@@ -1396,27 +1498,43 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1396
1498
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1397
1499
|
className: `dcu-wb-project${isCurrentWorkspace ? " dcu-wb-project-current" : ""}`,
|
|
1398
1500
|
onDragOver: (event) => {
|
|
1399
|
-
if (workspaceDragId === void 0) return;
|
|
1501
|
+
if (workspaceDragId === void 0 && !isSidebarItemDrag(event.dataTransfer) && !(zone === "pinned" && sessionDrag !== void 0)) return;
|
|
1400
1502
|
event.preventDefault();
|
|
1401
1503
|
event.stopPropagation();
|
|
1504
|
+
event.dataTransfer.dropEffect = "move";
|
|
1402
1505
|
const after = event.clientY > event.currentTarget.getBoundingClientRect().top + event.currentTarget.getBoundingClientRect().height / 2;
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1506
|
+
const id = String(workspace.workspaceId);
|
|
1507
|
+
const ids = (zone === "pinned" ? pinnedGroups : regularGroups).map((item) => String(item.workspaceId));
|
|
1508
|
+
const last = ids.length > 0 && ids[ids.length - 1] === id;
|
|
1509
|
+
if (zone === "pinned" && after && last) {
|
|
1510
|
+
setPinSlot("end");
|
|
1511
|
+
setWorkspaceDropId(void 0);
|
|
1512
|
+
setWorkspaceDropAfter(false);
|
|
1407
1513
|
} else {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1514
|
+
setPinSlot(zone === "pinned" ? id : void 0);
|
|
1515
|
+
setWorkspaceDropId(id);
|
|
1516
|
+
setWorkspaceDropAfter(after);
|
|
1411
1517
|
}
|
|
1412
1518
|
},
|
|
1413
1519
|
onDrop: (event) => {
|
|
1414
1520
|
event.preventDefault();
|
|
1415
1521
|
event.stopPropagation();
|
|
1416
|
-
const
|
|
1522
|
+
const droppedSession = readSessionDrag(event.dataTransfer, sessionDrag?.sessionId);
|
|
1523
|
+
if (zone === "pinned" && droppedSession !== void 0) {
|
|
1524
|
+
setSessionDrag(void 0);
|
|
1525
|
+
setSessionDropId(void 0);
|
|
1526
|
+
setWorkspaceDragId(void 0);
|
|
1527
|
+
setWorkspaceDropId(void 0);
|
|
1528
|
+
setWorkspaceDropAfter(false);
|
|
1529
|
+
setPinSlot(void 0);
|
|
1530
|
+
pinSessionAt(droppedSession);
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const dragged = readWorkspaceDrag(event.dataTransfer, workspaceDragId);
|
|
1417
1534
|
const after = event.clientY > event.currentTarget.getBoundingClientRect().top + event.currentTarget.getBoundingClientRect().height / 2;
|
|
1418
1535
|
setWorkspaceDragId(void 0);
|
|
1419
1536
|
setWorkspaceDropId(void 0);
|
|
1537
|
+
setWorkspaceDropAfter(false);
|
|
1420
1538
|
setPinSlot(void 0);
|
|
1421
1539
|
if (dragged === void 0) return;
|
|
1422
1540
|
if (zone === "pinned") {
|
|
@@ -1431,14 +1549,15 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1431
1549
|
},
|
|
1432
1550
|
children: [
|
|
1433
1551
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1434
|
-
className: `dcu-wb-project-head${menuOpen ? " dcu-wb-menu-open" : ""}${workspaceDropId === workspace.workspaceId ? " dcu-wb-drop" : ""}`,
|
|
1552
|
+
className: `dcu-wb-project-head${menuOpen ? " dcu-wb-menu-open" : ""}${workspaceDropId === workspace.workspaceId ? workspaceDropAfter ? " dcu-wb-drop dcu-wb-drop-after" : " dcu-wb-drop" : ""}${workspaceDragId === String(workspace.workspaceId) ? " dcu-wb-dragging" : ""}`,
|
|
1435
1553
|
role: "treeitem",
|
|
1436
1554
|
"aria-expanded": isExpanded,
|
|
1437
1555
|
tabIndex: 0,
|
|
1438
1556
|
draggable: true,
|
|
1439
1557
|
onDragStart: (event) => {
|
|
1440
|
-
event.
|
|
1441
|
-
|
|
1558
|
+
event.stopPropagation();
|
|
1559
|
+
setSessionDrag(void 0);
|
|
1560
|
+
writeWorkspaceDrag(event.dataTransfer, String(workspace.workspaceId), workspace.title);
|
|
1442
1561
|
const preview = document.createElement("div");
|
|
1443
1562
|
preview.className = "dcu-wb-drag-ghost";
|
|
1444
1563
|
preview.textContent = workspace.title;
|
|
@@ -1452,10 +1571,11 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1452
1571
|
onDragEnd: () => {
|
|
1453
1572
|
setWorkspaceDragId(void 0);
|
|
1454
1573
|
setWorkspaceDropId(void 0);
|
|
1574
|
+
setWorkspaceDropAfter(false);
|
|
1455
1575
|
setPinSlot(void 0);
|
|
1456
1576
|
},
|
|
1457
1577
|
onClick: () => {
|
|
1458
|
-
toggleGroup(
|
|
1578
|
+
if (zone !== "pinned") toggleGroup(expandKey, true);
|
|
1459
1579
|
},
|
|
1460
1580
|
onContextMenu: (event) => {
|
|
1461
1581
|
event.preventDefault();
|
|
@@ -1484,7 +1604,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1484
1604
|
onKeyDown: (event) => {
|
|
1485
1605
|
if (event.key === "Enter" || event.key === " ") {
|
|
1486
1606
|
event.preventDefault();
|
|
1487
|
-
toggleGroup(
|
|
1607
|
+
if (zone !== "pinned") toggleGroup(expandKey, true);
|
|
1488
1608
|
}
|
|
1489
1609
|
},
|
|
1490
1610
|
children: [
|
|
@@ -1507,7 +1627,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1507
1627
|
top: box.top
|
|
1508
1628
|
}, { immediate: true });
|
|
1509
1629
|
},
|
|
1510
|
-
children: isExpanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { size: 16 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, { size: 16 })
|
|
1630
|
+
children: zone !== "pinned" && isExpanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { size: 16 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, { size: 16 })
|
|
1511
1631
|
}),
|
|
1512
1632
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1513
1633
|
className: "dcu-wb-project-title",
|
|
@@ -1577,11 +1697,11 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1577
1697
|
})
|
|
1578
1698
|
]
|
|
1579
1699
|
}),
|
|
1580
|
-
isExpanded &&
|
|
1700
|
+
zone !== "pinned" && isExpanded && shownIds.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1581
1701
|
className: "dcu-wb-nochat",
|
|
1582
1702
|
children: t("workspace.noChat")
|
|
1583
1703
|
}),
|
|
1584
|
-
isExpanded &&
|
|
1704
|
+
zone !== "pinned" && isExpanded && shownIds.map((id) => {
|
|
1585
1705
|
const session = sessions.byId[id];
|
|
1586
1706
|
if (session === void 0) return null;
|
|
1587
1707
|
const path = session.cwd ?? workspace.path;
|
|
@@ -1598,7 +1718,16 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1598
1718
|
draggable: true,
|
|
1599
1719
|
onDragStart: (event) => {
|
|
1600
1720
|
event.stopPropagation();
|
|
1601
|
-
|
|
1721
|
+
setWorkspaceDragId(void 0);
|
|
1722
|
+
writeSessionDrag(event.dataTransfer, id, session.displayTitle);
|
|
1723
|
+
const preview = document.createElement("div");
|
|
1724
|
+
preview.className = "dcu-wb-drag-ghost";
|
|
1725
|
+
preview.textContent = session.displayTitle;
|
|
1726
|
+
document.body.appendChild(preview);
|
|
1727
|
+
event.dataTransfer.setDragImage(preview, 16, 18);
|
|
1728
|
+
window.requestAnimationFrame(() => {
|
|
1729
|
+
preview.remove();
|
|
1730
|
+
});
|
|
1602
1731
|
setSessionDrag({
|
|
1603
1732
|
sessionId: id,
|
|
1604
1733
|
workspaceId: workspace.workspaceId
|
|
@@ -1657,7 +1786,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1657
1786
|
children: [
|
|
1658
1787
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1659
1788
|
className: "dcu-wb-session-title",
|
|
1660
|
-
children: session.displayTitle
|
|
1789
|
+
children: session.displayTitle.split(/\r?\n/)[0] ?? session.displayTitle
|
|
1661
1790
|
}),
|
|
1662
1791
|
pinnedSessionIds.includes(id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1663
1792
|
className: "dcu-wb-pin",
|
|
@@ -1788,20 +1917,29 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1788
1917
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1789
1918
|
className: "dcu-wb-section",
|
|
1790
1919
|
"aria-label": t("workspace.pinned"),
|
|
1791
|
-
"data-pin-over":
|
|
1920
|
+
"data-pin-over": pinDragActive && pinSlot === "header",
|
|
1792
1921
|
onDragOver: (event) => {
|
|
1793
|
-
if (
|
|
1922
|
+
if (!pinDragActive && !isSidebarItemDrag(event.dataTransfer)) return;
|
|
1794
1923
|
event.preventDefault();
|
|
1924
|
+
event.dataTransfer.dropEffect = "move";
|
|
1795
1925
|
if (event.target === event.currentTarget || event.target instanceof Element && event.target.closest(".dcu-wb-section-head") !== null) setPinSlot("header");
|
|
1796
1926
|
},
|
|
1797
1927
|
onDrop: (event) => {
|
|
1798
1928
|
event.preventDefault();
|
|
1799
|
-
const
|
|
1929
|
+
const draggedSession = readSessionDrag(event.dataTransfer, sessionDrag?.sessionId);
|
|
1930
|
+
const draggedWorkspace = readWorkspaceDrag(event.dataTransfer, workspaceDragId);
|
|
1800
1931
|
const slot = pinSlot;
|
|
1801
1932
|
setWorkspaceDragId(void 0);
|
|
1802
1933
|
setWorkspaceDropId(void 0);
|
|
1934
|
+
setWorkspaceDropAfter(false);
|
|
1935
|
+
setSessionDrag(void 0);
|
|
1936
|
+
setSessionDropId(void 0);
|
|
1803
1937
|
setPinSlot(void 0);
|
|
1804
|
-
if (
|
|
1938
|
+
if (draggedSession !== void 0) {
|
|
1939
|
+
pinSessionAt(draggedSession, slot === "header" ? pinSectionSessions[0] : slot === "end" || slot === void 0 || !pinSectionSessions.includes(slot) ? void 0 : slot);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
if (draggedWorkspace !== void 0) pinWorkspaceAt(draggedWorkspace, slot === "header" ? pinnedGroups[0] === void 0 ? void 0 : String(pinnedGroups[0].workspaceId) : slot === "end" || slot === void 0 || pinSectionSessions.includes(slot) ? void 0 : slot);
|
|
1805
1943
|
},
|
|
1806
1944
|
onDragLeave: (event) => {
|
|
1807
1945
|
if (event.currentTarget.contains(event.relatedTarget)) return;
|
|
@@ -1837,18 +1975,159 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1837
1975
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1838
1976
|
className: "dcu-wb-section-body",
|
|
1839
1977
|
"data-open": sectionOpen("pinned"),
|
|
1840
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1978
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
1979
|
+
pinSectionSessions.length === 0 && pinnedGroups.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1980
|
+
className: "dcu-wb-empty",
|
|
1981
|
+
children: t("workspace.pinnedEmpty")
|
|
1982
|
+
}),
|
|
1983
|
+
pinSectionSessions.map((id) => {
|
|
1984
|
+
const session = sessions.byId[id];
|
|
1985
|
+
if (session === void 0) return null;
|
|
1986
|
+
const title = session.displayTitle;
|
|
1987
|
+
const workspace = groups.items.find((item) => item.visibleIds.includes(id));
|
|
1988
|
+
const path = session.cwd ?? workspace?.path;
|
|
1989
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow, {
|
|
1990
|
+
id,
|
|
1991
|
+
title,
|
|
1992
|
+
flat: true,
|
|
1993
|
+
selected: sessions.current === id,
|
|
1994
|
+
menuOpen: menu?.type === "session" && menu.id === id,
|
|
1995
|
+
pinned: true,
|
|
1996
|
+
unread: unreadSessionIds.includes(id),
|
|
1997
|
+
running: session.running === true,
|
|
1998
|
+
t,
|
|
1999
|
+
menuItems: sessionMenu(id, title, path),
|
|
2000
|
+
menuPoint: menu?.type === "session" && menu.id === id && menu.x !== void 0 && menu.y !== void 0 ? {
|
|
2001
|
+
x: menu.x,
|
|
2002
|
+
y: menu.y
|
|
2003
|
+
} : void 0,
|
|
2004
|
+
draggable: true,
|
|
2005
|
+
dropActive: sessionDropId === id,
|
|
2006
|
+
onDragStart: (event) => {
|
|
2007
|
+
event.stopPropagation();
|
|
2008
|
+
setWorkspaceDragId(void 0);
|
|
2009
|
+
writeSessionDrag(event.dataTransfer, id, title);
|
|
2010
|
+
const preview = document.createElement("div");
|
|
2011
|
+
preview.className = "dcu-wb-drag-ghost";
|
|
2012
|
+
preview.textContent = title;
|
|
2013
|
+
document.body.appendChild(preview);
|
|
2014
|
+
event.dataTransfer.setDragImage(preview, 16, 18);
|
|
2015
|
+
window.requestAnimationFrame(() => {
|
|
2016
|
+
preview.remove();
|
|
2017
|
+
});
|
|
2018
|
+
setSessionDrag({
|
|
2019
|
+
sessionId: id,
|
|
2020
|
+
workspaceId: workspace?.workspaceId ?? ""
|
|
2021
|
+
});
|
|
2022
|
+
},
|
|
2023
|
+
onDragEnd: () => {
|
|
2024
|
+
setSessionDrag(void 0);
|
|
2025
|
+
setSessionDropId(void 0);
|
|
2026
|
+
setPinSlot(void 0);
|
|
2027
|
+
},
|
|
2028
|
+
onDragOver: (event) => {
|
|
2029
|
+
if (sessionDrag === void 0) return;
|
|
2030
|
+
event.preventDefault();
|
|
2031
|
+
event.stopPropagation();
|
|
2032
|
+
setSessionDropId(id);
|
|
2033
|
+
setPinSlot(id);
|
|
2034
|
+
},
|
|
2035
|
+
onDrop: (event) => {
|
|
2036
|
+
event.preventDefault();
|
|
2037
|
+
event.stopPropagation();
|
|
2038
|
+
const draggedSession = readSessionDrag(event.dataTransfer, sessionDrag?.sessionId);
|
|
2039
|
+
const after = event.clientY > event.currentTarget.getBoundingClientRect().top + event.currentTarget.getBoundingClientRect().height / 2;
|
|
2040
|
+
setSessionDrag(void 0);
|
|
2041
|
+
setSessionDropId(void 0);
|
|
2042
|
+
setPinSlot(void 0);
|
|
2043
|
+
if (draggedSession === void 0) return;
|
|
2044
|
+
pinSessionAt(draggedSession, dropBeforeId(pinSectionSessions, id, after));
|
|
2045
|
+
},
|
|
2046
|
+
onOpen: () => {
|
|
2047
|
+
setUnreadSessionIds((ids) => ids.filter((item) => item !== id));
|
|
2048
|
+
openSession(id);
|
|
2049
|
+
},
|
|
2050
|
+
onMenuChange: (open) => {
|
|
2051
|
+
setMenu(open ? {
|
|
2052
|
+
id,
|
|
2053
|
+
type: "session"
|
|
2054
|
+
} : void 0);
|
|
2055
|
+
},
|
|
2056
|
+
onPin: () => {
|
|
2057
|
+
setSectionSessionIds((ids) => ids.filter((item) => item !== id));
|
|
2058
|
+
},
|
|
2059
|
+
onArchive: () => {
|
|
2060
|
+
run("archive", () => archiveSession(id));
|
|
2061
|
+
},
|
|
2062
|
+
onHover: (event) => {
|
|
2063
|
+
const box = hoverCardAnchor(event.currentTarget.getBoundingClientRect());
|
|
2064
|
+
showTip({
|
|
2065
|
+
kind: "session",
|
|
2066
|
+
id,
|
|
2067
|
+
title,
|
|
2068
|
+
project: workspace?.title,
|
|
2069
|
+
path,
|
|
2070
|
+
time: formatHoverTime(session.updatedAt),
|
|
2071
|
+
left: box.left,
|
|
2072
|
+
top: box.top
|
|
2073
|
+
});
|
|
2074
|
+
},
|
|
2075
|
+
onLeave: hideTip,
|
|
2076
|
+
onContextMenu: (event) => {
|
|
2077
|
+
event.preventDefault();
|
|
2078
|
+
event.stopPropagation();
|
|
2079
|
+
dismissTip();
|
|
2080
|
+
setMenu({
|
|
2081
|
+
id,
|
|
2082
|
+
type: "session",
|
|
2083
|
+
x: event.clientX,
|
|
2084
|
+
y: event.clientY
|
|
2085
|
+
});
|
|
2086
|
+
},
|
|
2087
|
+
onSelectAction: (action) => {
|
|
2088
|
+
if (busy !== void 0) return;
|
|
2089
|
+
if (action === "rename") beginRename("session", id, title);
|
|
2090
|
+
if (action === "pin") {
|
|
2091
|
+
setSectionSessionIds((ids) => ids.filter((item) => item !== id));
|
|
2092
|
+
setMenu(void 0);
|
|
2093
|
+
}
|
|
2094
|
+
if (action === "unread") {
|
|
2095
|
+
setUnreadSessionIds((ids) => toggleSessionId(ids, id));
|
|
2096
|
+
setMenu(void 0);
|
|
2097
|
+
}
|
|
2098
|
+
if (action === "archive") run("archive", () => archiveSession(id));
|
|
2099
|
+
if (action === "delete") {
|
|
2100
|
+
setDeleteTarget({
|
|
2101
|
+
id,
|
|
2102
|
+
kind: "session",
|
|
2103
|
+
title
|
|
2104
|
+
});
|
|
2105
|
+
setError(void 0);
|
|
2106
|
+
setMenu(void 0);
|
|
2107
|
+
}
|
|
2108
|
+
if (action === "fork") run("fork", () => forkSession(id));
|
|
2109
|
+
if (action === "openPath" && path !== void 0) run("open-path", () => openPath(path));
|
|
2110
|
+
if (action === "copyPath") copy(path);
|
|
2111
|
+
if (action === "copyTitle") copy(title);
|
|
2112
|
+
if (action === "copyId") copy(id);
|
|
2113
|
+
if (action === "copyLink") copy(sessionDeepLink(browserBase(), id));
|
|
2114
|
+
}
|
|
2115
|
+
}, id);
|
|
2116
|
+
}),
|
|
2117
|
+
pinnedGroups.map((workspace) => renderGroup(workspace, "pinned")),
|
|
2118
|
+
pinDragActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2119
|
+
className: `dcu-wb-pin-end${pinSlot === "end" ? " dcu-wb-drop" : ""}`,
|
|
2120
|
+
onDragOver: (event) => {
|
|
2121
|
+
if (!pinDragActive && !isSidebarItemDrag(event.dataTransfer)) return;
|
|
2122
|
+
event.preventDefault();
|
|
2123
|
+
event.stopPropagation();
|
|
2124
|
+
event.dataTransfer.dropEffect = "move";
|
|
2125
|
+
setPinSlot("end");
|
|
2126
|
+
setWorkspaceDropId(void 0);
|
|
2127
|
+
setWorkspaceDropAfter(false);
|
|
2128
|
+
}
|
|
2129
|
+
})
|
|
2130
|
+
] })
|
|
1852
2131
|
})]
|
|
1853
2132
|
}),
|
|
1854
2133
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -1954,6 +2233,29 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
1954
2233
|
running: session.running === true,
|
|
1955
2234
|
t,
|
|
1956
2235
|
menuItems: sessionMenu(id, title, session.cwd),
|
|
2236
|
+
draggable: true,
|
|
2237
|
+
onDragStart: (event) => {
|
|
2238
|
+
event.stopPropagation();
|
|
2239
|
+
setWorkspaceDragId(void 0);
|
|
2240
|
+
writeSessionDrag(event.dataTransfer, id, title);
|
|
2241
|
+
const preview = document.createElement("div");
|
|
2242
|
+
preview.className = "dcu-wb-drag-ghost";
|
|
2243
|
+
preview.textContent = title;
|
|
2244
|
+
document.body.appendChild(preview);
|
|
2245
|
+
event.dataTransfer.setDragImage(preview, 16, 18);
|
|
2246
|
+
window.requestAnimationFrame(() => {
|
|
2247
|
+
preview.remove();
|
|
2248
|
+
});
|
|
2249
|
+
setSessionDrag({
|
|
2250
|
+
sessionId: id,
|
|
2251
|
+
workspaceId: ""
|
|
2252
|
+
});
|
|
2253
|
+
},
|
|
2254
|
+
onDragEnd: () => {
|
|
2255
|
+
setSessionDrag(void 0);
|
|
2256
|
+
setSessionDropId(void 0);
|
|
2257
|
+
setPinSlot(void 0);
|
|
2258
|
+
},
|
|
1957
2259
|
menuPoint: menu?.type === "session" && menu.id === id && menu.x !== void 0 && menu.y !== void 0 ? {
|
|
1958
2260
|
x: menu.x,
|
|
1959
2261
|
y: menu.y
|
|
@@ -3417,11 +3719,9 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3417
3719
|
});
|
|
3418
3720
|
}
|
|
3419
3721
|
CodexSidebar.displayName = "michengai-codex-ui";
|
|
3722
|
+
//#endregion
|
|
3723
|
+
//#region src/dependencies.ts
|
|
3420
3724
|
const MANAGED_DEPENDENCIES = [
|
|
3421
|
-
{
|
|
3422
|
-
id: "suite",
|
|
3423
|
-
packageName: "@michengai/dsh-codex-suite"
|
|
3424
|
-
},
|
|
3425
3725
|
{
|
|
3426
3726
|
id: "ui",
|
|
3427
3727
|
packageName: "@michengai/dsh-codex-ui"
|
|
@@ -3711,7 +4011,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3711
4011
|
"search.actions": "快捷操作",
|
|
3712
4012
|
"workspace.label": "工作区会话",
|
|
3713
4013
|
"workspace.pinned": "置顶",
|
|
3714
|
-
"workspace.pinnedEmpty": "
|
|
4014
|
+
"workspace.pinnedEmpty": "拖动项目或会话到此处置顶",
|
|
3715
4015
|
"workspace.projects": "项目",
|
|
3716
4016
|
"workspace.recent": "最近",
|
|
3717
4017
|
"workspace.recentEmpty": "无聊天",
|
|
@@ -3782,7 +4082,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3782
4082
|
"about.feature.conversation": "保留原生消息、工具调用、输入、权限和模型选择,仅调整容器视觉",
|
|
3783
4083
|
"about.feature.navigator": "当前会话提供轮次缩略导航,可快速跳转至每一次用户提问",
|
|
3784
4084
|
"about.dependencies": "配套管理插件",
|
|
3785
|
-
"about.dependenciesDescription": "
|
|
4085
|
+
"about.dependenciesDescription": "每个配套插件都单独安装和更新,互不影响。可分别检查 Codex UI、专家、技能、归档、IM、定时任务和第三方插件市场。",
|
|
3786
4086
|
"about.loading": "正在读取依赖安装状态…",
|
|
3787
4087
|
"about.statusFailed": "暂时无法读取依赖安装状态。",
|
|
3788
4088
|
"about.installed": "已安装",
|
|
@@ -3792,7 +4092,8 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3792
4092
|
"about.update": "更新",
|
|
3793
4093
|
"about.installing": "安装中",
|
|
3794
4094
|
"about.installFailed": "依赖安装失败,请稍后重试。",
|
|
3795
|
-
"about.restartRequired": "
|
|
4095
|
+
"about.restartRequired": "正在热更新插件,窗口会自动刷新。",
|
|
4096
|
+
"about.dependency.dsh": "DeepSeek Harness",
|
|
3796
4097
|
"about.dependency.suite": "Codex 套件",
|
|
3797
4098
|
"about.dependency.ui": "Codex UI",
|
|
3798
4099
|
"about.dependency.experts": "专家管理",
|
|
@@ -3842,7 +4143,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3842
4143
|
"workspace.pin": "Pin project",
|
|
3843
4144
|
"workspace.unpin": "Unpin",
|
|
3844
4145
|
"workspace.pinned": "Pinned",
|
|
3845
|
-
"workspace.pinnedEmpty": "Drag a project here to pin it",
|
|
4146
|
+
"workspace.pinnedEmpty": "Drag a project or chat here to pin it",
|
|
3846
4147
|
"workspace.openPath": "Open in file explorer",
|
|
3847
4148
|
"workspace.delete": "Delete project",
|
|
3848
4149
|
"workspace.deleteDescription": "This removes “{name}” from the project list. The folder and conversation records remain.",
|
|
@@ -3904,7 +4205,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3904
4205
|
"about.feature.conversation": "Keeps native messages, tool calls, composer, permissions, and model selection while refining the container visuals",
|
|
3905
4206
|
"about.feature.navigator": "Navigate directly to each user prompt with the current conversation turn navigator",
|
|
3906
4207
|
"about.dependencies": "Companion management plugins",
|
|
3907
|
-
"about.dependenciesDescription": "
|
|
4208
|
+
"about.dependenciesDescription": "Each companion plugin is installed and updated separately. Check Codex UI, expert, skill, archive, IM, scheduled-task, and marketplace plugins on their own.",
|
|
3908
4209
|
"about.loading": "Loading dependency status…",
|
|
3909
4210
|
"about.statusFailed": "Dependency status is temporarily unavailable.",
|
|
3910
4211
|
"about.installed": "Installed",
|
|
@@ -3914,7 +4215,8 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
|
|
|
3914
4215
|
"about.update": "Update",
|
|
3915
4216
|
"about.installing": "Installing",
|
|
3916
4217
|
"about.installFailed": "Dependency installation failed. Try again later.",
|
|
3917
|
-
"about.restartRequired": "
|
|
4218
|
+
"about.restartRequired": "Updating plugins now. The window will refresh automatically.",
|
|
4219
|
+
"about.dependency.dsh": "DeepSeek Harness",
|
|
3918
4220
|
"about.dependency.suite": "Codex suite",
|
|
3919
4221
|
"about.dependency.ui": "Codex UI",
|
|
3920
4222
|
"about.dependency.experts": "Expert management",
|
package/dist/index.mjs
CHANGED
|
@@ -14,10 +14,6 @@ const SUITE_MEMBER_PACKAGES = [
|
|
|
14
14
|
"@michengai/dsh-automation"
|
|
15
15
|
];
|
|
16
16
|
const MANAGED_DEPENDENCIES = [
|
|
17
|
-
{
|
|
18
|
-
id: "suite",
|
|
19
|
-
packageName: SUITE_PACKAGE
|
|
20
|
-
},
|
|
21
17
|
{
|
|
22
18
|
id: "ui",
|
|
23
19
|
packageName: "@michengai/dsh-codex-ui"
|
|
@@ -52,6 +48,8 @@ function managedDependency(id) {
|
|
|
52
48
|
}
|
|
53
49
|
//#endregion
|
|
54
50
|
//#region src/dependency-manager.ts
|
|
51
|
+
const PROFILE_PENDING_UPDATES_FILE = ".dsh-pending-updates.json";
|
|
52
|
+
const APPLY_PLUGIN_UPDATES_IPC = "apply-plugin-updates";
|
|
55
53
|
function profileDirectory() {
|
|
56
54
|
if (process.env.DSH_PROFILE_DIR !== void 0) return process.env.DSH_PROFILE_DIR;
|
|
57
55
|
return resolve(homedir(), ".dsh", "profiles", "web");
|
|
@@ -68,14 +66,14 @@ async function declaredPluginNames() {
|
|
|
68
66
|
throw error;
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
|
-
/**
|
|
69
|
+
/** 单独更新子插件时先卸套件,避免两套 patch 冲突。 */
|
|
72
70
|
function pluginsToRemoveBeforeInstall(declared, installing) {
|
|
73
|
-
if (installing
|
|
74
|
-
return SUITE_MEMBER_PACKAGES.filter((name) => declared.includes(name));
|
|
71
|
+
if (SUITE_MEMBER_PACKAGES.includes(installing) && declared.includes("@michengai/dsh-codex-suite")) return [SUITE_PACKAGE];
|
|
72
|
+
if (installing === "@michengai/dsh-codex-suite") return SUITE_MEMBER_PACKAGES.filter((name) => declared.includes(name));
|
|
73
|
+
return [];
|
|
75
74
|
}
|
|
76
|
-
/**
|
|
77
|
-
function resolveDshPluginTarget(installing,
|
|
78
|
-
if (installing !== "@michengai/dsh-codex-suite" && SUITE_MEMBER_PACKAGES.includes(installing) && declared.includes("@michengai/dsh-codex-suite")) return SUITE_PACKAGE;
|
|
75
|
+
/** 点击哪个包就更新哪个包,不再把子插件重定向到套件。 */
|
|
76
|
+
function resolveDshPluginTarget(installing, _declared = []) {
|
|
79
77
|
return installing;
|
|
80
78
|
}
|
|
81
79
|
async function installedPackageVersion(packageName) {
|
|
@@ -190,12 +188,55 @@ function resolveDshCliEntry(entry = process.argv[1], cwd = process.cwd()) {
|
|
|
190
188
|
if (entry.startsWith("file:")) return fileURLToPath(entry);
|
|
191
189
|
return resolve(cwd, entry);
|
|
192
190
|
}
|
|
191
|
+
function requestDesktopHotUpdate(send = process.send) {
|
|
192
|
+
if (typeof send !== "function") return false;
|
|
193
|
+
send(APPLY_PLUGIN_UPDATES_IPC);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
function isRestartableInstallError(error) {
|
|
197
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
198
|
+
return /完全退出桌面端|正在运行的插件|pnpm 仓库不一致/.test(message);
|
|
199
|
+
}
|
|
200
|
+
async function recordPendingUpdate(packageName, version) {
|
|
201
|
+
const pendingPath = resolve(profileDirectory(), PROFILE_PENDING_UPDATES_FILE);
|
|
202
|
+
let packages = [];
|
|
203
|
+
try {
|
|
204
|
+
const parsed = JSON.parse(await readFile(pendingPath, "utf8"));
|
|
205
|
+
if (Array.isArray(parsed.packages)) packages = parsed.packages.flatMap((item) => {
|
|
206
|
+
if (item === null || typeof item !== "object") return [];
|
|
207
|
+
const record = item;
|
|
208
|
+
if (typeof record.packageName !== "string" || typeof record.version !== "string") return [];
|
|
209
|
+
return [{
|
|
210
|
+
packageName: record.packageName,
|
|
211
|
+
version: record.version
|
|
212
|
+
}];
|
|
213
|
+
});
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (error.code !== "ENOENT") throw error;
|
|
216
|
+
}
|
|
217
|
+
packages = packages.filter((item) => item.packageName !== packageName);
|
|
218
|
+
packages.push({
|
|
219
|
+
packageName,
|
|
220
|
+
version
|
|
221
|
+
});
|
|
222
|
+
await writeFile(pendingPath, `${JSON.stringify({ packages }, void 0, 2)}\n`, "utf8");
|
|
223
|
+
}
|
|
224
|
+
async function recordDeclaredVersion(packageName, version) {
|
|
225
|
+
const manifestPath = resolve(profileDirectory(), "package.json");
|
|
226
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
227
|
+
manifest.dependencies = {
|
|
228
|
+
...manifest.dependencies,
|
|
229
|
+
[packageName]: version
|
|
230
|
+
};
|
|
231
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, void 0, 2)}\n`, "utf8");
|
|
232
|
+
}
|
|
193
233
|
function pluginCommandError(stderr) {
|
|
194
234
|
const detail = stderr.replace(/\s+/g, " ").trim();
|
|
195
235
|
if (detail.includes("minimumReleaseAge") || detail.includes("Release age")) return /* @__PURE__ */ new Error("更新被 pnpm 发布时间保护拦截。请确认已写入当前版本白名单后重试。");
|
|
196
|
-
if (
|
|
197
|
-
if (
|
|
198
|
-
return /* @__PURE__ */ new Error("
|
|
236
|
+
if (/EPERM|EBUSY|EACCES|unable to unlink|ERR_PNPM_LOCKED|Lock/i.test(detail)) return /* @__PURE__ */ new Error("无法覆盖正在运行的插件文件。请先完全退出桌面端,再重新打开后更新。");
|
|
237
|
+
if (/UNEXPECTED_STORE|Unexpected store location/i.test(detail)) return /* @__PURE__ */ new Error("插件目录和 pnpm 仓库不一致。请完全退出桌面端后再更新。");
|
|
238
|
+
if (/pnpm not found/i.test(detail)) return /* @__PURE__ */ new Error("当前环境找不到 pnpm。请从桌面端启动后再更新。");
|
|
239
|
+
return /* @__PURE__ */ new Error("无法在应用运行时更新插件。请先完全退出桌面端,再重新打开后更新。");
|
|
199
240
|
}
|
|
200
241
|
/**
|
|
201
242
|
* 复用启动当前服务的 DSH CLI:它会通过 pnpm 从 npm 安装或更新,并自动维护
|
|
@@ -220,19 +261,21 @@ function runDshPlugin(args) {
|
|
|
220
261
|
windowsHide: true,
|
|
221
262
|
stdio: [
|
|
222
263
|
"ignore",
|
|
223
|
-
"
|
|
264
|
+
"pipe",
|
|
224
265
|
"pipe"
|
|
225
266
|
]
|
|
226
267
|
});
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
268
|
+
let output = "";
|
|
269
|
+
const collect = (chunk) => {
|
|
270
|
+
output += String(chunk);
|
|
271
|
+
};
|
|
272
|
+
child.stdout?.on("data", collect);
|
|
273
|
+
child.stderr?.on("data", collect);
|
|
231
274
|
child.once("error", () => {
|
|
232
275
|
reject(/* @__PURE__ */ new Error("无法启动 DSH 插件安装命令。请确认 Node.js 与 pnpm 可用后重试。"));
|
|
233
276
|
});
|
|
234
277
|
child.once("exit", (code) => {
|
|
235
|
-
code === 0 ? resolvePromise() : reject(pluginCommandError(
|
|
278
|
+
code === 0 ? resolvePromise() : reject(pluginCommandError(output));
|
|
236
279
|
});
|
|
237
280
|
});
|
|
238
281
|
}
|
|
@@ -260,13 +303,20 @@ async function installDependencyLocked(id) {
|
|
|
260
303
|
const remove = pluginsToRemoveBeforeInstall(declared, target);
|
|
261
304
|
if (remove.length > 0) await runDshPlugin(["remove", ...remove]);
|
|
262
305
|
await ensureLatestReleaseAllowed(target, targetVersion);
|
|
263
|
-
await
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
306
|
+
await recordDeclaredVersion(target, targetVersion);
|
|
307
|
+
await recordPendingUpdate(target, targetVersion);
|
|
308
|
+
if (requestDesktopHotUpdate()) return dependencyStatuses();
|
|
309
|
+
try {
|
|
310
|
+
await runDshPlugin([
|
|
311
|
+
"add",
|
|
312
|
+
`${target}@${targetVersion}`,
|
|
313
|
+
"--registry=https://registry.npmjs.org/"
|
|
314
|
+
]);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (isRestartableInstallError(error)) return dependencyStatuses();
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
if (await installedPackageVersion(target) !== targetVersion) return dependencyStatuses();
|
|
270
320
|
return dependencyStatuses();
|
|
271
321
|
}
|
|
272
322
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michengai/dsh-codex-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.60",
|
|
4
4
|
"description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -51,6 +51,10 @@
|
|
|
51
51
|
"platform": "web"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsc --noEmit && tsdown",
|
|
56
|
+
"test": "tsx tests/permission-i18n.assert.ts && tsx tests/hover-tip.assert.ts && tsx tests/companion-slots.assert.ts && tsx tests/channel-api.assert.ts && tsx tests/schedule-sessions.assert.ts && tsx tests/session-tree.assert.ts && tsx tests/pinned-sessions.assert.ts && tsx tests/session-manager.assert.ts && tsx tests/workspace-browser.assert.ts && tsx tests/sidebar-search.assert.ts && tsx tests/settings-navigation.assert.ts && tsx tests/settings-nav-icons.assert.ts && tsx tests/locales.assert.ts && vitest run tests/client-runtime.integration.spec.ts && tsx tests/settings-integration.assert.ts && tsx tests/about-dependencies.assert.ts && tsx tests/dependency-manager.assert.ts && tsx tests/conversation-bubbles.assert.ts && tsx tests/conversation-header.assert.ts && tsx tests/conversation-visuals.assert.ts && tsdown && tsx tests/client-bundle.assert.ts && tsx tests/codex-suite.assert.ts"
|
|
57
|
+
},
|
|
54
58
|
"peerDependencies": {
|
|
55
59
|
"@deepseek-ai/cordis": ">=4.0.1 <5.0.0",
|
|
56
60
|
"@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.0 <0.2.0",
|
|
@@ -96,9 +100,5 @@
|
|
|
96
100
|
"tsx": "^4.22.4",
|
|
97
101
|
"typescript": "^6.0.3",
|
|
98
102
|
"vitest": "^4.1.8"
|
|
99
|
-
},
|
|
100
|
-
"scripts": {
|
|
101
|
-
"build": "tsc --noEmit && tsdown",
|
|
102
|
-
"test": "tsx tests/permission-i18n.assert.ts && tsx tests/hover-tip.assert.ts && tsx tests/companion-slots.assert.ts && tsx tests/channel-api.assert.ts && tsx tests/schedule-sessions.assert.ts && tsx tests/session-tree.assert.ts && tsx tests/pinned-sessions.assert.ts && tsx tests/session-manager.assert.ts && tsx tests/workspace-browser.assert.ts && tsx tests/sidebar-search.assert.ts && tsx tests/settings-navigation.assert.ts && tsx tests/settings-nav-icons.assert.ts && tsx tests/locales.assert.ts && vitest run tests/client-runtime.integration.spec.ts && tsx tests/settings-integration.assert.ts && tsx tests/about-dependencies.assert.ts && tsx tests/dependency-manager.assert.ts && tsx tests/conversation-bubbles.assert.ts && tsx tests/conversation-header.assert.ts && tsx tests/conversation-visuals.assert.ts && tsdown && tsx tests/client-bundle.assert.ts && tsx tests/codex-suite.assert.ts"
|
|
103
103
|
}
|
|
104
|
-
}
|
|
104
|
+
}
|