@michengai/dsh-archive-manager 0.1.2 → 0.1.3

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
@@ -2456,7 +2456,7 @@ window.__ModuleLoader__.load({
2456
2456
  //#endregion
2457
2457
  //#region dsh-archive-manager: settings section
2458
2458
  const ARCHIVE_SETTINGS_CSS = ".dsham_settings{box-sizing:border-box;width:min(100%,768px);margin:64px auto 80px;padding:0 20px;color:var(--dsw-alias-label-primary)}.dsham_settingsHeader{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:48px}.dsham_settings h2{margin:0;font-size:24px;font-weight:650;letter-spacing:-.02em;line-height:32px}.dsham_settingsDanger{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 11px;color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);border:0;border-radius:9px;cursor:pointer;font-size:12px;font-weight:600}.dsham_settingsDanger:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)}.dsham_settingsToolbar{display:flex;gap:8px;margin-bottom:40px}.dsham_settingsSearch{display:flex;align-items:center;gap:8px;min-width:0;flex:1;height:32px;padding:0 12px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:16px}.dsham_settingsSearch:focus-within{border-color:var(--dsw-alias-label-tertiary)}.dsham_settingsSearch input{width:100%;min-width:0;padding:0;color:var(--dsw-alias-label-primary);background:transparent;border:0;outline:0;font:inherit;font-size:12px}.dsham_settingsSearch input::placeholder{color:var(--dsw-alias-label-tertiary)}.dsham_settingsFilter{min-width:132px;height:30px;padding:0 28px 0 11px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:9px;cursor:pointer;font:inherit;font-size:12px}.dsham_settingsGroup{margin:0 0 48px}.dsham_settingsGroupHeading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 14px}.dsham_settingsGroupTitle{display:flex;align-items:center;gap:8px;min-width:0;margin:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.dsham_settingsGroupTitle svg{flex:none;color:var(--dsw-alias-label-secondary)}.dsham_settingsCount{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsham_settingsList{overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:var(--dsw-alias-button-elevated-fill)}.dsham_settingsRow{display:flex;align-items:center;gap:12px;min-height:60px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsRow:last-child{border-bottom:0}.dsham_settingsContent{min-width:0;flex:1}.dsham_settingsTitle{overflow:hidden;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.dsham_settingsMeta{margin-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.dsham_settingsActions{display:flex;align-items:center;gap:8px}.dsham_settingsAction{height:30px;padding:0 10px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);border:0;border-radius:9px;cursor:pointer;font-size:12px;font-weight:600}.dsham_settingsAction:hover{filter:brightness(1.12)}.dsham_settingsDelete{display:flex;align-items:center;justify-content:center;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsDelete:hover{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsEmpty{padding:28px 8px;color:var(--dsw-alias-label-secondary);text-align:center}.dsham_settingsError{margin-top:10px;color:var(--dsw-alias-state-error-primary);font-size:12px}@media(max-width:720px){.dsham_settings{width:100%;margin:28px auto 48px;padding:0 16px}.dsham_settingsHeader{margin-bottom:28px}.dsham_settingsToolbar{flex-wrap:wrap;margin-bottom:28px}.dsham_settingsSearch{flex-basis:100%}.dsham_settingsFilter{flex:1;min-width:0}.dsham_settingsGroup{margin-bottom:32px}.dsham_settingsRow{padding:10px 12px}.dsham_settingsActions{gap:4px}}";
2459
- const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:24px auto 80px!important}.dsham_settingsFilter{color-scheme:dark;background-color:var(--dsw-alias-button-elevated-fill)!important;color:var(--dsw-alias-label-primary)!important}.dsham_settingsFilter option{color-scheme:dark;background-color:#2b2b2b;color:#f1f1f1}@media(max-width:720px){.dsham_settings{margin:16px auto 48px!important}}";
2459
+ const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:24px auto 80px!important}@media(max-width:720px){.dsham_settings{margin:16px auto 48px!important}}";
2460
2460
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
2461
2461
  const tag = document.createElement("style");
2462
2462
  tag.dataset.plugin = "@michengai/dsh-archive-manager";
@@ -2472,7 +2472,6 @@ window.__ModuleLoader__.load({
2472
2472
  const [busy, setBusy] = (0, react.useState)(false);
2473
2473
  const [error, setError] = (0, react.useState)(null);
2474
2474
  const [query, setQuery] = (0, react.useState)("");
2475
- const [chatType, setChatType] = (0, react.useState)("all");
2476
2475
  const [project, setProject] = (0, react.useState)("all");
2477
2476
  const groups = (0, react.useMemo)(() => {
2478
2477
  const archived = new Set(workspaceState.archivedSessionIds);
@@ -2496,9 +2495,9 @@ window.__ModuleLoader__.load({
2496
2495
  const normalizedQuery = query.trim().toLocaleLowerCase();
2497
2496
  return groups.filter((group) => project === "all" || project === group.title).map((group) => ({
2498
2497
  ...group,
2499
- sessions: group.sessions.filter((session) => (chatType === "all" || session.origin === chatType) && (normalizedQuery === "" || displayTitle(session, t).toLocaleLowerCase().includes(normalizedQuery)))
2498
+ sessions: group.sessions.filter((session) => normalizedQuery === "" || displayTitle(session, t).toLocaleLowerCase().includes(normalizedQuery))
2500
2499
  })).filter((group) => group.sessions.length > 0);
2501
- }, [chatType, groups, project, query, t]);
2500
+ }, [groups, project, query, t]);
2502
2501
  const archivedSessionIds = (0, react.useMemo)(() => groups.flatMap((group) => group.sessions.map((session) => session.id)), [groups]);
2503
2502
  const onUnarchive = (sessionId) => {
2504
2503
  setError(null);
@@ -2525,7 +2524,7 @@ window.__ModuleLoader__.load({
2525
2524
  return (0, react_jsx_runtime.jsxs)("section", {
2526
2525
  className: "dsham_settings",
2527
2526
  "aria-label": t("archives.title"),
2528
- children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS }), (0, react_jsx_runtime.jsxs)("header", { className: "dsham_settingsHeader", children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || archivedSessionIds.length === 0, onClick: () => setDeleteTarget({ all: true }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsToolbar", children: [(0, react_jsx_runtime.jsxs)("label", { className: "dsham_settingsSearch", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })] }), (0, react_jsx_runtime.jsx)("select", { className: "dsham_settingsFilter", value: chatType, onChange: (event) => setChatType(event.target.value), "aria-label": t("archives.chatFilter"), children: (0, react_jsx_runtime.jsx)("option", { value: "all", children: t("archives.allChats") }) }), (0, react_jsx_runtime.jsx)("select", { className: "dsham_settingsFilter", value: project, onChange: (event) => setProject(event.target.value), "aria-label": t("archives.projectFilter"), children: [(0, react_jsx_runtime.jsx)("option", { value: "all", children: t("archives.allProjects") }), groups.map((group) => (0, react_jsx_runtime.jsx)("option", { value: group.title, children: group.title }, group.title))] })] }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => (0, react_jsx_runtime.jsxs)("section", {
2527
+ children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS }), (0, react_jsx_runtime.jsxs)("header", { className: "dsham_settingsHeader", children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || archivedSessionIds.length === 0, onClick: () => setDeleteTarget({ all: true }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsToolbar", children: [(0, react_jsx_runtime.jsxs)("label", { className: "dsham_settingsSearch", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })] }), (0, react_jsx_runtime.jsx)("select", { className: "dsham_settingsFilter", value: project, onChange: (event) => setProject(event.target.value), "aria-label": t("archives.projectFilter"), children: [(0, react_jsx_runtime.jsx)("option", { value: "all", children: t("archives.allProjects") }), groups.map((group) => (0, react_jsx_runtime.jsx)("option", { value: group.title, children: group.title }, group.title))] })] }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => (0, react_jsx_runtime.jsxs)("section", {
2529
2528
  className: "dsham_settingsGroup",
2530
2529
  children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsGroupHeading", children: [(0, react_jsx_runtime.jsxs)("h3", { className: "dsham_settingsGroupTitle", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, {}), group.title] }), (0, react_jsx_runtime.jsx)("span", { className: "dsham_settingsCount", children: t("archives.sessionCount", { n: group.sessions.length }) })] }), (0, react_jsx_runtime.jsx)("div", {
2531
2530
  className: "dsham_settingsList",
@@ -2591,8 +2590,6 @@ window.__ModuleLoader__.load({
2591
2590
  "archives.empty": "暂无已归档会话",
2592
2591
  "archives.emptyFiltered": "没有匹配的已归档聊天",
2593
2592
  "archives.searchPlaceholder": "搜索已归档聊天",
2594
- "archives.chatFilter": "按聊天类型筛选",
2595
- "archives.allChats": "全部聊天",
2596
2593
  "archives.projectFilter": "按项目筛选",
2597
2594
  "archives.allProjects": "所有项目",
2598
2595
  "archives.sessionCount": "{n} 个聊天",
@@ -2678,8 +2675,6 @@ window.__ModuleLoader__.load({
2678
2675
  "archives.empty": "No archived sessions.",
2679
2676
  "archives.emptyFiltered": "No archived chats match your filters.",
2680
2677
  "archives.searchPlaceholder": "Search archived chats",
2681
- "archives.chatFilter": "Filter by chat type",
2682
- "archives.allChats": "All chats",
2683
2678
  "archives.projectFilter": "Filter by project",
2684
2679
  "archives.allProjects": "All projects",
2685
2680
  "archives.sessionCount": "{n} chats",
package/lib/projcache.js CHANGED
@@ -10,29 +10,35 @@ import { SessionProjectionCache } from "@deepseek-ai/dsh-session-projection-cach
10
10
  *
11
11
  * - `delete(id)` — permanently remove one session's cached projection row
12
12
  * (`table.delete` on the `session_projcache` domain).
13
- * - `whenIdle()` — resolve once every in-flight fail-soft checkpoint write
14
- * has settled. Session disposal triggers one final write-behind
15
- * (`flushSoft(session, "detach")`); the deletion flow must wait for that
16
- * write to land BEFORE deleting the row, otherwise the row is written back
17
- * after deletion and resurrects the cache entry.
13
+ * - `whenIdle()` — resolve once every in-flight public `write()` has settled.
14
+ * - `delete(id)` installs a tombstone before removing the row, so an earlier
15
+ * write that settles late removes its own stale row instead of resurrecting
16
+ * the deleted cache entry.
18
17
  *
19
18
  * The default export is a Service subclass (same shape as the shipped
20
19
  * `@deepseek-ai/dsh-session-projection-cache` package), so the profile patch
21
20
  * can substitute this package for the `session-projection-cache` row with no
22
21
  * other wiring change.
23
22
  */
24
- var ArchiveProjectionCache = class extends SessionProjectionCache {
25
- /** Tail of in-flight fail-soft checkpoint writes (settled promises only). */
26
- writeTail = Promise.resolve();
23
+ var ArchiveProjectionCache = class extends SessionProjectionCache {
24
+ /** 已永久删除的会话不会再允许投影缓存写入。 */
25
+ deletedSessionIds = /* @__PURE__ */ new Set();
26
+ /** Tail of in-flight checkpoint writes (settled promises only). */
27
+ writeTail = Promise.resolve();
27
28
  constructor(ctx, config) {
28
29
  super(ctx, config);
29
30
  }
30
- /** Fail-soft durable checkpoint, tracked so {@link whenIdle} can observe it. */
31
- flushSoft(session, trigger) {
32
- const task = super.flushSoft(session, trigger);
33
- this.writeTail = Promise.allSettled([this.writeTail, task]).then(() => void 0);
34
- return task;
35
- }
31
+ /** 跟踪公开写入路径,避免依赖上游私有 `flushSoft` 的实现细节。 */
32
+ write(session) {
33
+ const task = this.writeCore(session);
34
+ this.writeTail = Promise.allSettled([this.writeTail, task]).then(() => void 0);
35
+ return task;
36
+ }
37
+ async writeCore(session) {
38
+ if (this.deletedSessionIds.has(session.id)) return;
39
+ await super.write(session);
40
+ if (this.deletedSessionIds.has(session.id)) await this.requireTable().delete(session.id);
41
+ }
36
42
  /**
37
43
  * Resolve once every tracked in-flight write has settled (failures
38
44
  * included). Fresh calls during an idle cache resolve immediately.
@@ -46,9 +52,10 @@ var ArchiveProjectionCache = class extends SessionProjectionCache {
46
52
  * @param id - the session whose cache row to delete.
47
53
  * @returns resolution after the row is gone.
48
54
  */
49
- async delete(id) {
50
- await this.requireTable().delete(id);
51
- }
55
+ async delete(id) {
56
+ this.deletedSessionIds.add(id);
57
+ await this.requireTable().delete(id);
58
+ }
52
59
  };
53
60
  //#endregion
54
61
  export { ArchiveProjectionCache, ArchiveProjectionCache as default };
package/lib/workspace.js CHANGED
@@ -146,8 +146,9 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
146
146
  await rm(transcriptDir, { recursive: true, force: true });
147
147
  } catch (error) {
148
148
  const message = `cannot delete session "${sessionId}": transcript directory "${transcriptDir}" remains after bookkeeping cleanup`;
149
- this.ctx.logger.warn(`archive-manager: ${message}: ${String(error)}`);
150
- throw new Error(`${message}: ${String(error)}`, { cause: error });
149
+ const detail = `${message}: ${String(error)}`;
150
+ this.ctx.logger.warn(`archive-manager: ${detail}`);
151
+ throw new Error(detail, { cause: error });
151
152
  }
152
153
  }
153
154
  /** Drop the id from every workspace record and refresh entity snapshots. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-archive-manager",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "NPM-installable DSH Web plugin for managing archived sessions.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.21.0",