dsh-sessions-manager 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +21 -21
- package/README.md +21 -21
- package/assets/screenshot-session-autoarch.png +0 -0
- package/assets/screenshot-session-details.png +0 -0
- package/assets/screenshot-session-settings.png +0 -0
- package/assets/screenshot-session-starred.png +0 -0
- package/assets/screenshot-session-storage.png +0 -0
- package/assets/screenshot-session-trash.png +0 -0
- package/lib/client.js +161 -4
- package/lib/client.js.map +2 -2
- package/lib/index.js +305 -25
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
- package/src/auto-archive.js +168 -0
- package/src/client/index.jsx +177 -5
- package/src/index.js +161 -5
- package/src/storage-stats.js +94 -0
- package/assets/screenshot-session-settingsmenu.png +0 -0
package/README.en.md
CHANGED
|
@@ -14,29 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
A persistent DSH plugin (host + browser halves). Starting with v3.0.0, it covers both the **settings panel** and the **main sidebar**, so frequent session actions don't require opening Settings.
|
|
16
16
|
|
|
17
|
-
## Changelog
|
|
18
|
-
|
|
19
|
-
- **v3.3.0**: new **starred sessions** — an always-visible star button on each row and a dedicated "Starred" view, backed by a plugin-owned schema v3 index (reusing the recycle bin's version-migration / serialized-mutation / atomic-write pattern); new **single-session Markdown export** (human-readable transcript, sectioned by turn, with user / assistant / tool-call summaries); the detail panel gains an **export section** whose "Download raw log (ZIP)" action reuses DSH's official `session.export` endpoint (subsessions + attachments included, auto-hidden when the backend lacks raw-artifact support) instead of duplicating it.
|
|
20
|
-
- **v3.2.2**: engineering hardening, no behavior change. Sidebar decision logic extracted to `src/client/logic.js` (dot semantics / drop validation / fiber row detection) with 13 new regression tests; CI now smoke-tests each runtime version against real cordis and tracks runtime drift.
|
|
21
|
-
- **v3.2.1**: zstd session-log safety fix (frame-boundary scan mismatching zstd magic inside compressed data).
|
|
22
|
-
- **v3.2.0**: sidebar cross-workspace drag & drop + full session management workbench.
|
|
23
|
-
|
|
24
|
-
## What's new in v3.2
|
|
25
|
-
|
|
26
|
-
- **Cross-workspace sidebar drag-and-drop**: drop a session on a workspace heading to move it. It coexists with DSH's native same-group sorting and provides target highlighting, same-workspace protection, failure feedback, and a keyboard-accessible menu fallback.
|
|
27
|
-
- **Complete session-management workspace**: All / Active / Archived / Recycle bin in one place, with full search, workspace filters, creation-time or title sorting, batch actions, and result counts.
|
|
28
|
-
- **Reliable recycle-bin lifecycle**: soft delete, in-place restore, single permanent purge, empty-all, 7 / 30 / 90-day retention, and log-integrity verification, backed by schema v2, serialized mutations, and atomic writes.
|
|
29
|
-
- **No post-delete resurrection**: permanent purge persists a tombstone first, waits for the live Session and persistence controller to retire, removes the log, and rebuilds workspace indexes so neither sessions nor an empty Ungrouped section reappear.
|
|
30
|
-
- **Archive-state round trips**: a trashed session returns to the active or archived state it had before deletion; concurrent archive and restore mutations are serialized to prevent duplicate IDs, overwrites, and races.
|
|
31
|
-
- **Authoritative cold-start titles**: the latest log-folded `session/title` corrects stale sidebar caches before a renamed session is opened.
|
|
32
|
-
- **Safe migration for open sessions**: moving synchronizes the log header, persistence state, live Session, and WorkspaceRegistry, with backup, verification, and rollback on failure.
|
|
33
|
-
- **Consistent and accessible UI**: tags use DSH's official 4px radius; destructive actions are confirmed, busy / empty / error states are explicit, and every drag action has a keyboard path.
|
|
34
|
-
|
|
35
17
|
## Features
|
|
36
18
|
|
|
37
19
|
### Settings panel: Session Manager
|
|
38
20
|
|
|
39
|
-
- **Unified panel**: five top-level views — **All / Active / Archived / Starred / Recycle bin** — with search across title, session ID, and workspace, plus workspace filters and creation-time/title sorting.
|
|
21
|
+
- **Unified panel**: five top-level views — **All / Active / Archived / Starred / Recycle bin** — with search across title, session ID, and workspace, plus workspace filters and creation-time/title sorting. A maintenance row below the filters holds the **Auto-archive** and **Storage usage** tools, which expand on demand instead of taking up a view.
|
|
40
22
|
- **Starred sessions**: an always-visible star button on each session row toggles starring optimistically with rollback on failure; the Starred view is orthogonal to DSH's active/archived states and they stack. The star index is plugin-owned (schema v3) and never touches DSH logs; stars are cleaned up automatically when a session is permanently purged.
|
|
41
23
|
- **Cold-title synchronization**: the sidebar is corrected from the latest log-backed `session/title`, so renamed cold sessions no longer need to be opened before showing their current name.
|
|
42
24
|
- **Sidebar workspace drag-and-drop**: drag a session onto a workspace heading to move it, with a highlighted drop target, same-workspace protection, failure feedback, and the More → Move session menu retained for keyboard access.
|
|
@@ -44,6 +26,8 @@ A persistent DSH plugin (host + browser halves). Starting with v3.0.0, it covers
|
|
|
44
26
|
- **Move to a workspace**: pick an **existing workspace**, or enter a **new directory path** (auto-created); the new-path mode also lets you open the **native OS directory picker** with a **「浏览…」** button. The session's working directory and log are migrated together, and even an open session can be moved safely.
|
|
45
27
|
- **Session details**: expand any session to see **disk usage**, **turns / steps / user·assistant messages / tool calls / image attachments** stats, **tool-usage breakdown**, **search·fetch records**, the **write/edit file list** (already filtered for paths that no longer exist on disk), and **lineage** (parent session / child sessions / subagents).
|
|
46
28
|
- **Export**: the detail panel offers two actions — "**Download raw log (ZIP)**" goes straight to DSH's official `session.export` endpoint (subsessions + attachments included, hidden automatically when the persistence backend lacks raw-artifact support); "**Export Markdown**" renders the session into a human-readable transcript (front matter + per-turn sections + user / assistant / tool-call summaries, with no streaming-delta duplication).
|
|
29
|
+
- **Storage analysis**: a **Storage usage** button in the maintenance row expands on demand to aggregate per-workspace disk usage of session logs (share bars + session counts) and list the Top 10 largest sessions. Read-only statistics that never modify any data; collapsed by default, so nothing is scanned until you open it.
|
|
30
|
+
- **Auto-archive**: optionally move sessions that have been **inactive for 30 / 60 / 90 days** into **Archived**, **off by default**. The session you are currently using and any starred session (that guard can be turned off) are never auto-archived; it only sets an archived flag and never deletes data, so recovery is always one click away. The check is lazily triggered when you open the panel, at most once per day, and can also be run manually with "Check now".
|
|
47
31
|
- **Batch multi-select**: select-all / archive selected / restore selected / delete selected (single confirmation for batch delete).
|
|
48
32
|
|
|
49
33
|
### Main sidebar: session ⋯ menu augmentation
|
|
@@ -80,14 +64,25 @@ The plugin hides DSH's own status dot and re-renders it using the palette above,
|
|
|
80
64
|
|
|
81
65
|
## Screenshots
|
|
82
66
|
|
|
67
|
+
<details>
|
|
68
|
+
<summary>Expand screenshots (settings panel / auto-archive / storage analysis / starred / recycle bin / session details / sidebar menu)</summary>
|
|
69
|
+
|
|
83
70
|

|
|
84
71
|
|
|
85
|
-

|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+

|
|
86
79
|
|
|
87
80
|

|
|
88
81
|
|
|
89
82
|

|
|
90
83
|
|
|
84
|
+
</details>
|
|
85
|
+
|
|
91
86
|
## Install
|
|
92
87
|
|
|
93
88
|
```sh
|
|
@@ -118,6 +113,8 @@ package.json npm metadata + dsh.bundle.patch + dsh.client (browser-half re
|
|
|
118
113
|
cordis.patch.yml inserts this plugin's row into the profile bundle
|
|
119
114
|
src/index.js host source (/archived-sessions/* JSON routes)
|
|
120
115
|
src/client/index.jsx client source (React, settings.section + sidebar DOM augmentation)
|
|
116
|
+
src/auto-archive.js auto-archive settings (schema v4) + candidate-selection pure functions
|
|
117
|
+
src/storage-stats.js storage-usage aggregation (pure functions, by workspace / Top N)
|
|
121
118
|
build.mjs esbuild build script (regenerates lib/)
|
|
122
119
|
lib/index.js pre-built host (ESM)
|
|
123
120
|
lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
@@ -147,8 +144,11 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
147
144
|
| POST | `/archived-sessions/move` | Move a session to a target workspace `{ sessionId, targetPath }` |
|
|
148
145
|
| POST | `/archived-sessions/details` | Session details (disk / stats / tools / fetch / files / lineage) `{ sessionId }` |
|
|
149
146
|
| POST | `/archived-sessions/sidebar-state` | Authoritative sidebar titles, recycle-bin IDs, and permanent-deletion tombstones |
|
|
150
|
-
| POST | `/archived-sessions/star/set` | Star / unstar sessions `{ sessionId
|
|
147
|
+
| POST | `/archived-sessions/star/set` | Star / unstar sessions `{ sessionId or sessionIds, starred }` |
|
|
151
148
|
| GET | `/archived-sessions/export-md?sessionId=` | Single-session Markdown export (human-readable transcript) |
|
|
149
|
+
| POST | `/archived-sessions/storage` | Storage-usage aggregation (per-workspace ranking + largest sessions) `{ topN }` |
|
|
150
|
+
| POST | `/archived-sessions/auto-archive/settings` | Read or update the auto-archive policy `{ inactiveDays, skipStarred }`; reading lazily triggers the daily check |
|
|
151
|
+
| POST | `/archived-sessions/auto-archive/run` | Run a one-off auto-archive check immediately (ignores the daily throttle) |
|
|
152
152
|
|
|
153
153
|
> Deleting a session moves it to the recycle bin by default; only "permanently delete" inside the recycle bin physically removes the log. Permanently deleted sessions are hidden forever on the client side so they don't reappear in the sidebar or "ungrouped" due to DSH runtime caching.
|
|
154
154
|
|
package/README.md
CHANGED
|
@@ -14,29 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
一个 DSH 持久化插件(host + browser 双半)。v3.0.0 起同时覆盖「设置面板」与「主页侧边栏」两个入口,无需打开设置即可完成高频会话操作。
|
|
16
16
|
|
|
17
|
-
## 更新日志
|
|
18
|
-
|
|
19
|
-
- **v3.3.0**:新增**收藏(星标)**——会话行内常驻星标按钮与独立「已收藏」视图,索引采用 schema v3(沿用回收站的版本迁移 / 串行变更 / 原子写入模式);新增**单会话 Markdown 导出**(人类可读对话记录,按轮分节,含用户 / 助手 / 工具调用摘要);详情面板新增**导出区**,「下载原始日志 (ZIP)」直接复用 DSH 官方 `session.export` 端点(含子会话与附件,后端不支持时自动降级隐藏),不重复造轮子。
|
|
20
|
-
- **v3.2.2**:工程加固版,无功能变化。侧栏判定纯逻辑抽取到 `src/client/logic.js`(状态点语义 / 拖拽校验 / fiber 行识别),新增 13 个回归测试;CI 新增 runtime 版本冒烟(真实 cordis DI 契约校验 + 版本漂移追踪)。
|
|
21
|
-
- **v3.2.1**:zstd 会话日志安全修复(帧边界定位误匹配压缩数据内的 magic)。
|
|
22
|
-
- **v3.2.0**:侧栏跨工作区拖拽 + 完整会话管理工作台。
|
|
23
|
-
|
|
24
|
-
## v3.2 亮点
|
|
25
|
-
|
|
26
|
-
- **侧栏跨工作区拖拽**:把会话拖到目标工作区标题即可完成迁移;与 DSH 原生同组排序兼容,提供目标高亮、同工作区拦截、失败反馈,并保留键盘可达的菜单入口。
|
|
27
|
-
- **完整会话管理工作台**:全部 / 活动 / 已归档 / 回收站统一入口,加入全文搜索、工作区筛选、创建时间与标题排序、批量操作和结果计数。
|
|
28
|
-
- **可靠回收站生命周期**:软删除、原位恢复、单条彻底删除、批量清空、7 / 30 / 90 天自动清理和日志完整性校验;索引采用 schema v2、串行变更和原子写入。
|
|
29
|
-
- **删除后不再“复活”**:永久删除先持久化墓碑,再等待 live Session 与持久化控制器退役、物理删除日志并重建工作区索引,避免会话或空的「未分组」重新出现。
|
|
30
|
-
- **归档状态往返一致**:活动或归档会话进入回收站后,恢复时回到删除前状态;并发归档 / 恢复串行执行,避免重复 ID、覆盖和竞态。
|
|
31
|
-
- **冷启动标题权威同步**:从日志折叠最新 `session/title`,改名后的会话无需先打开即可在侧栏显示最新名称。
|
|
32
|
-
- **打开会话也能安全迁移**:移动过程同步日志 header、持久化状态、live Session 与 WorkspaceRegistry,配合备份、校验和失败回滚。
|
|
33
|
-
- **界面一致性与可访问性**:标签统一采用 DSH 官方 4px R 角;危险操作二次确认、忙碌 / 空 / 错误状态清晰,拖拽之外保留键盘操作路径。
|
|
34
|
-
|
|
35
17
|
## 功能
|
|
36
18
|
|
|
37
19
|
### 设置面板:会话管理
|
|
38
20
|
|
|
39
|
-
- **统一面板**:顶部提供「全部 / 活动 / 已归档 / 已收藏 / 回收站」五个视图,支持按标题、会话 ID
|
|
21
|
+
- **统一面板**:顶部提供「全部 / 活动 / 已归档 / 已收藏 / 回收站」五个视图,支持按标题、会话 ID、工作区搜索,按工作区筛选,并按创建时间或标题排序。筛选栏下方另有一行维护栏,收纳「自动归档」与「存储占用」两个按需展开的工具,不占用视图位置。
|
|
40
22
|
- **收藏(星标)**:会话行左侧常驻星标按钮,单击即收藏 / 取消(乐观更新、失败回滚);「已收藏」视图与 DSH 的活动 / 归档状态正交、可叠加;收藏索引为插件自有 schema v3,不触碰 DSH 日志,会话被彻底删除时自动清理。
|
|
41
23
|
- **冷态标题同步**:侧栏使用日志中最新的 `session/title` 修正冷启动缓存,改名后的会话无需先打开即可显示新名称。
|
|
42
24
|
- **侧栏跨工作区拖拽**:直接把会话拖到目标工作区标题即可切换工作区;目标高亮、同工作区拦截、失败反馈,并保留“更多 → 移动会话”作为键盘操作入口。
|
|
@@ -44,6 +26,8 @@
|
|
|
44
26
|
- **移动到工作区**:任选**已有工作区**或**新建目录路径**(自动创建),新建目录支持点击 **「浏览…」** 调用系统目录选择窗口。会话的工作目录与日志一起迁移;即使会话处于打开状态也可安全移动。
|
|
45
27
|
- **会话详情**:展开单条会话查看**磁盘占用**、**轮次 / 步骤 / 用户·助手消息 / 工具调用 / 图片附件**统计、**工具使用分布**、**搜索·抓取记录**、**write/edit 写过的文件列表**(已过滤磁盘上已不存在的路径),以及**血统**(父会话 / 子会话 / 子代理)。
|
|
46
28
|
- **导出**:详情面板底部提供两个入口——「**下载原始日志 (ZIP)**」直接走 DSH 官方 `session.export` 端点(含子会话与附件,持久化后端不支持时自动隐藏);「**导出 Markdown**」由本插件把会话渲染为人类可读对话记录(front matter + 按轮分节 + 用户 / 助手 / 工具调用摘要,流式增量不重复)。
|
|
29
|
+
- **存储占用分析**:维护栏的「存储占用」按钮按需展开,按工作区聚合会话日志的磁盘占用(占比条 + 会话数),并列出占用最大的会话 Top 10。纯只读统计,不修改任何数据;默认收起、展开时才统计,因此不会拖慢会话列表的加载。
|
|
30
|
+
- **自动归档**:可设为把 **30 / 60 / 90 天未活跃**的会话自动收进「已归档」,**默认关闭**。当前正在使用的会话与已收藏的会话(该保护可关闭)永不自动归档;只做归档标记,不删除任何数据,随时可恢复。检查在打开面板时惰性触发,每天最多一次,也可点「立即检查」手动执行。
|
|
47
31
|
- **批量多选**:全选 / 批量归档 / 恢复所选 / 删除所选(批量删除一次二次确认)。
|
|
48
32
|
|
|
49
33
|
### 主页侧边栏:会话 ⋯ 菜单增强
|
|
@@ -80,14 +64,25 @@
|
|
|
80
64
|
|
|
81
65
|
## 截图
|
|
82
66
|
|
|
67
|
+
<details>
|
|
68
|
+
<summary>展开查看截图(设置面板 / 自动归档 / 存储占用 / 已收藏 / 回收站 / 会话详情 / 侧边栏菜单)</summary>
|
|
69
|
+
|
|
83
70
|

|
|
84
71
|
|
|
85
|
-

|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+

|
|
77
|
+
|
|
78
|
+

|
|
86
79
|
|
|
87
80
|

|
|
88
81
|
|
|
89
82
|

|
|
90
83
|
|
|
84
|
+
</details>
|
|
85
|
+
|
|
91
86
|
## 安装
|
|
92
87
|
|
|
93
88
|
```sh
|
|
@@ -118,6 +113,8 @@ package.json npm 元数据 + dsh.bundle.patch + dsh.client(浏览器半
|
|
|
118
113
|
cordis.patch.yml 向 profile bundle 插入本插件行
|
|
119
114
|
src/index.js host 源码(/archived-sessions/* JSON 路由)
|
|
120
115
|
src/client/index.jsx client 源码(React,settings.section + 侧栏 DOM 增强)
|
|
116
|
+
src/auto-archive.js 自动归档设置(schema v4)+ 候选判定纯函数
|
|
117
|
+
src/storage-stats.js 存储占用聚合(纯函数,按工作区 / Top N)
|
|
121
118
|
build.mjs esbuild 构建脚本(本地开发时生成 lib/)
|
|
122
119
|
lib/index.js 预构建 host(ESM)
|
|
123
120
|
lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
@@ -147,8 +144,11 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
147
144
|
| POST | `/archived-sessions/move` | 把会话移动到目标工作区 `{ sessionId, targetPath }` |
|
|
148
145
|
| POST | `/archived-sessions/details` | 会话详情(磁盘/统计/工具/fetch/文件/血统)`{ sessionId }` |
|
|
149
146
|
| POST | `/archived-sessions/sidebar-state` | 返回侧栏权威标题、回收站 ID 与永久删除墓碑 |
|
|
150
|
-
| POST | `/archived-sessions/star/set` | 收藏 / 取消收藏 `{ sessionId
|
|
147
|
+
| POST | `/archived-sessions/star/set` | 收藏 / 取消收藏 `{ sessionId 或 sessionIds, starred }` |
|
|
151
148
|
| GET | `/archived-sessions/export-md?sessionId=` | 单会话 Markdown 导出(人类可读对话记录) |
|
|
149
|
+
| POST | `/archived-sessions/storage` | 存储占用聚合(按工作区排行 + 最大的会话)`{ topN }` |
|
|
150
|
+
| POST | `/archived-sessions/auto-archive/settings` | 读取或更新自动归档策略 `{ inactiveDays, skipStarred }`;读取时惰性触发每日检查 |
|
|
151
|
+
| POST | `/archived-sessions/auto-archive/run` | 立即执行一次自动归档检查(忽略每日节流) |
|
|
152
152
|
|
|
153
153
|
> 删除会话默认进入回收站,只有回收站内的「彻底删除」才会物理移除日志。被彻底删除的会话由前端永久隐藏,避免 DSH 运行时缓存使其重新出现在侧栏或「未分组」中。
|
|
154
154
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -304,9 +304,17 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
304
304
|
const [detailsLoading, setDetailsLoading] = (0, import_react.useState)(null);
|
|
305
305
|
const [mdBusy, setMdBusy] = (0, import_react.useState)(null);
|
|
306
306
|
const [zipOk, setZipOk] = (0, import_react.useState)(true);
|
|
307
|
+
const [storage, setStorage] = (0, import_react.useState)(null);
|
|
308
|
+
const [storageBusy, setStorageBusy] = (0, import_react.useState)(false);
|
|
309
|
+
const [storageOpen, setStorageOpen] = (0, import_react.useState)(false);
|
|
310
|
+
const [storageError, setStorageError] = (0, import_react.useState)(null);
|
|
311
|
+
const [aa, setAa] = (0, import_react.useState)({ settings: { inactiveDays: 0, skipStarred: true }, lastRunAt: null, lastArchivedCount: 0 });
|
|
312
|
+
const [aaOpen, setAaOpen] = (0, import_react.useState)(false);
|
|
313
|
+
const [aaBusy, setAaBusy] = (0, import_react.useState)(false);
|
|
307
314
|
const zipChecked = (0, import_react.useRef)(false);
|
|
308
315
|
const [openMenu, setOpenMenu] = (0, import_react.useState)(null);
|
|
309
316
|
const timer = (0, import_react.useRef)(null);
|
|
317
|
+
const storageDirty = (0, import_react.useRef)(false);
|
|
310
318
|
const menuRef = (0, import_react.useRef)(null);
|
|
311
319
|
const dialogRef = (0, import_react.useRef)(null);
|
|
312
320
|
const showToast = (msg) => {
|
|
@@ -329,6 +337,8 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
329
337
|
setConfirmBatch(false);
|
|
330
338
|
if (!targetWs && works.items && works.items.length) setTargetWs(works.items[0].workspaceId);
|
|
331
339
|
loadTrash();
|
|
340
|
+
if (storageOpen) loadStorage();
|
|
341
|
+
else storageDirty.current = true;
|
|
332
342
|
}).catch((e) => setError(String(e && e.message || e)));
|
|
333
343
|
};
|
|
334
344
|
(0, import_react.useEffect)(() => {
|
|
@@ -343,6 +353,9 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
343
353
|
} catch (e) {
|
|
344
354
|
}
|
|
345
355
|
}, [filter, workspaceFilter, sortBy]);
|
|
356
|
+
(0, import_react.useEffect)(() => {
|
|
357
|
+
loadAutoArchive();
|
|
358
|
+
}, []);
|
|
346
359
|
const toggleStar = async (it) => {
|
|
347
360
|
const next = !it.starred;
|
|
348
361
|
setSessions((s) => s && s.map((x) => x.sessionId === it.sessionId ? { ...x, starred: next } : x));
|
|
@@ -434,6 +447,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
434
447
|
});
|
|
435
448
|
}, [sessions, filter, query, workspaceFilter, sortBy]);
|
|
436
449
|
const selIds = Object.keys(selected).filter((k) => selected[k]);
|
|
450
|
+
const showSessionList = filter !== "trash";
|
|
437
451
|
const toggle = (id) => setSelected((s) => ({ ...s, [id]: !s[id] }));
|
|
438
452
|
const clearSel = () => setSelected({});
|
|
439
453
|
const selectAll = () => {
|
|
@@ -500,6 +514,52 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
500
514
|
setError(String(e && e.message || e));
|
|
501
515
|
});
|
|
502
516
|
};
|
|
517
|
+
const loadStorage = () => {
|
|
518
|
+
setStorageBusy(true);
|
|
519
|
+
setStorageError(null);
|
|
520
|
+
postJSON("/archived-sessions/storage", { topN: 10 }).then((r) => {
|
|
521
|
+
setStorageBusy(false);
|
|
522
|
+
setStorage(r);
|
|
523
|
+
storageDirty.current = false;
|
|
524
|
+
}).catch((e) => {
|
|
525
|
+
setStorageBusy(false);
|
|
526
|
+
setStorageError(String(e && e.message || e));
|
|
527
|
+
});
|
|
528
|
+
};
|
|
529
|
+
const applyAa = (r) => setAa({ settings: r.settings || { inactiveDays: 0, skipStarred: true }, lastRunAt: r.lastRunAt ?? null, lastArchivedCount: r.lastArchivedCount || 0 });
|
|
530
|
+
const loadAutoArchive = () => {
|
|
531
|
+
postJSON("/archived-sessions/auto-archive/settings", {}).then(applyAa).catch(() => {
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
const updateAutoArchive = (patch) => {
|
|
535
|
+
if (aaBusy) return;
|
|
536
|
+
setAaBusy(true);
|
|
537
|
+
postJSON("/archived-sessions/auto-archive/settings", patch).then((r) => {
|
|
538
|
+
setAaBusy(false);
|
|
539
|
+
applyAa(r);
|
|
540
|
+
showToast("\u5DF2\u66F4\u65B0\u81EA\u52A8\u5F52\u6863\u7B56\u7565");
|
|
541
|
+
}).catch((e) => {
|
|
542
|
+
setAaBusy(false);
|
|
543
|
+
setError(String(e && e.message || e));
|
|
544
|
+
});
|
|
545
|
+
};
|
|
546
|
+
const runAutoArchive = () => {
|
|
547
|
+
if (aaBusy) return;
|
|
548
|
+
setAaBusy(true);
|
|
549
|
+
postJSON("/archived-sessions/auto-archive/run", {}).then((r) => {
|
|
550
|
+
setAaBusy(false);
|
|
551
|
+
applyAa(r);
|
|
552
|
+
const n = r.archived || 0;
|
|
553
|
+
if (r.skipped === "disabled") showToast("\u81EA\u52A8\u5F52\u6863\u672A\u542F\u7528");
|
|
554
|
+
else {
|
|
555
|
+
showToast(`\u5DF2\u81EA\u52A8\u5F52\u6863 ${n} \u4E2A\u4F1A\u8BDD`);
|
|
556
|
+
if (n > 0) refresh();
|
|
557
|
+
}
|
|
558
|
+
}).catch((e) => {
|
|
559
|
+
setAaBusy(false);
|
|
560
|
+
setError(String(e && e.message || e));
|
|
561
|
+
});
|
|
562
|
+
};
|
|
503
563
|
const restoreTrash = (sid) => {
|
|
504
564
|
if (trashBusy) return;
|
|
505
565
|
setTrashBusy(sid);
|
|
@@ -747,7 +807,89 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
747
807
|
")"
|
|
748
808
|
] })
|
|
749
809
|
] }),
|
|
750
|
-
|
|
810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "maint-bar", children: [
|
|
811
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn" + (aa.settings.inactiveDays ? " archv-go" : ""), "aria-expanded": aaOpen, onClick: () => setAaOpen(!aaOpen), children: [
|
|
812
|
+
"\u81EA\u52A8\u5F52\u6863",
|
|
813
|
+
aa.settings.inactiveDays ? `\uFF1A${aa.settings.inactiveDays} \u5929\u672A\u6D3B\u8DC3` : "\uFF1A\u672A\u542F\u7528"
|
|
814
|
+
] }),
|
|
815
|
+
aa.lastRunAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "maint-note", children: [
|
|
816
|
+
"\u4E0A\u6B21\u68C0\u67E5 ",
|
|
817
|
+
fmtDate(aa.lastRunAt),
|
|
818
|
+
"\uFF0C\u5F52\u6863 ",
|
|
819
|
+
aa.lastArchivedCount,
|
|
820
|
+
" \u4E2A"
|
|
821
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "maint-note", children: "\u5C1A\u672A\u68C0\u67E5" }),
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn maint-bar-right", "aria-expanded": storageOpen, onClick: () => {
|
|
823
|
+
const next = !storageOpen;
|
|
824
|
+
setStorageOpen(next);
|
|
825
|
+
if (next && (!storage || storageDirty.current) && !storageBusy) loadStorage();
|
|
826
|
+
}, children: [
|
|
827
|
+
"\u5B58\u50A8\u5360\u7528",
|
|
828
|
+
storage ? ` \xB7 ${fmtBytes(storage.totalBytes) || "0 B"}` : ""
|
|
829
|
+
] })
|
|
830
|
+
] }),
|
|
831
|
+
aaOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", "aria-label": "\u81EA\u52A8\u5F52\u6863\u8BBE\u7F6E", children: [
|
|
832
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "mv-sheet-title", children: "\u81EA\u52A8\u5F52\u6863" }),
|
|
834
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "mv-sheet-close", "aria-label": "\u5173\u95ED", onClick: () => setAaOpen(false), children: "\xD7" })
|
|
835
|
+
] }),
|
|
836
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-field", children: [
|
|
837
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mv-field-label", htmlFor: "dsm-aa-days", children: "\u5C06\u591A\u4E45\u672A\u6D3B\u8DC3\u7684\u4F1A\u8BDD\u81EA\u52A8\u5F52\u6863" }),
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { id: "dsm-aa-days", value: aa.settings.inactiveDays, disabled: aaBusy, onChange: (e) => updateAutoArchive({ inactiveDays: Number(e.target.value) }), children: [
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "0", children: "\u4E0D\u81EA\u52A8\u5F52\u6863" }),
|
|
840
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "30", children: "30 \u5929\u672A\u6D3B\u8DC3" }),
|
|
841
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "60", children: "60 \u5929\u672A\u6D3B\u8DC3" }),
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "90", children: "90 \u5929\u672A\u6D3B\u8DC3" })
|
|
843
|
+
] })
|
|
844
|
+
] }),
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "aa-check", children: [
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: aa.settings.skipStarred !== false, disabled: aaBusy, onChange: (e) => updateAutoArchive({ skipStarred: e.target.checked }) }),
|
|
847
|
+
"\u8DF3\u8FC7\u5DF2\u6536\u85CF\u7684\u4F1A\u8BDD"
|
|
848
|
+
] }),
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mv-foot", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: aaBusy || !aa.settings.inactiveDays, onClick: runAutoArchive, children: aaBusy ? "\u68C0\u67E5\u4E2D\u2026" : "\u7ACB\u5373\u68C0\u67E5" }) }),
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u81EA\u52A8\u5F52\u6863\u53EA\u662F\u628A\u4F1A\u8BDD\u6536\u8FDB\u300C\u5DF2\u5F52\u6863\u300D\uFF0C\u4E0D\u5220\u9664\u4EFB\u4F55\u6570\u636E\uFF0C\u968F\u65F6\u53EF\u6062\u590D\u3002\u5F53\u524D\u6B63\u5728\u4F7F\u7528\u7684\u4F1A\u8BDD\u6C38\u8FDC\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5F52\u6863\u3002\u68C0\u67E5\u5728\u6253\u5F00\u672C\u9762\u677F\u65F6\u89E6\u53D1\uFF0C\u6BCF\u5929\u6700\u591A\u4E00\u6B21\u3002" })
|
|
851
|
+
] }),
|
|
852
|
+
storageOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", "aria-label": "\u5B58\u50A8\u5360\u7528", children: [
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
|
|
854
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "mv-sheet-title", children: "\u5B58\u50A8\u5360\u7528" }),
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-actions", children: [
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: storageBusy, onClick: loadStorage, children: storageBusy ? "\u7EDF\u8BA1\u4E2D\u2026" : "\u91CD\u65B0\u7EDF\u8BA1" }),
|
|
857
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "mv-sheet-close", "aria-label": "\u5173\u95ED", onClick: () => setStorageOpen(false), children: "\xD7" })
|
|
858
|
+
] })
|
|
859
|
+
] }),
|
|
860
|
+
storageError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-err", role: "alert", children: [
|
|
861
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: storageError }),
|
|
862
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-errretry", onClick: loadStorage, children: "\u91CD\u8BD5" })
|
|
863
|
+
] }) : !storage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", children: "\u7EDF\u8BA1\u4E2D\u2026" }) : storage.sessionCount === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", children: "\u6682\u65E0\u4F1A\u8BDD\uFF0C\u6CA1\u6709\u53EF\u7EDF\u8BA1\u7684\u5B58\u50A8\u5360\u7528\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-storage-sum", children: [
|
|
865
|
+
"\u5171 ",
|
|
866
|
+
fmtBytes(storage.totalBytes) || "0 B",
|
|
867
|
+
" \xB7 ",
|
|
868
|
+
storage.sessionCount,
|
|
869
|
+
" \u4E2A\u4F1A\u8BDD",
|
|
870
|
+
storage.unknownSessions ? ` \xB7 ${storage.unknownSessions} \u4E2A\u5927\u5C0F\u672A\u77E5` : ""
|
|
871
|
+
] }),
|
|
872
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-storage-list", children: storage.workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-storage-row", children: [
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-name", title: w.path || "\u672A\u5206\u7EC4", children: w.title || (w.path ? pathName(w.path) : "\u672A\u5206\u7EC4") }),
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-bar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-fill", style: { width: `${Math.round((w.share || 0) * 100)}%` } }) }),
|
|
875
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-size", children: fmtBytes(w.bytes) || "\u2014" }),
|
|
876
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-storage-count", children: [
|
|
877
|
+
w.sessions,
|
|
878
|
+
" \u4E2A"
|
|
879
|
+
] })
|
|
880
|
+
] }, w.key)) }),
|
|
881
|
+
storage.top.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtl-sec", children: [
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-sec-t", children: "\u5360\u7528\u6700\u5927\u7684\u4F1A\u8BDD" }),
|
|
883
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-storage-list", children: storage.top.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-storage-row", children: [
|
|
884
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-name", title: s.sessionId, children: s.title || s.sessionId }),
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-size", children: fmtBytes(s.sizeBytes) || "\u2014" }),
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-storage-count", children: s.workspaceTitle || (s.workspacePath ? pathName(s.workspacePath) : "\u672A\u5206\u7EC4") })
|
|
887
|
+
] }, s.sessionId)) })
|
|
888
|
+
] }),
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u7EDF\u8BA1\u7684\u662F\u4F1A\u8BDD\u65E5\u5FD7\u6587\u4EF6\u7684\u78C1\u76D8\u5360\u7528\uFF08\u538B\u7F29\u540E\u7684\u5B9E\u9645\u5927\u5C0F\uFF09\uFF0C\u53EA\u8BFB\uFF0C\u4E0D\u4FEE\u6539\u4EFB\u4F55\u6570\u636E\u3002" })
|
|
890
|
+
] })
|
|
891
|
+
] }),
|
|
892
|
+
showSessionList && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
751
893
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-tools", "aria-label": "\u67E5\u627E\u548C\u6574\u7406\u4F1A\u8BDD", children: [
|
|
752
894
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
|
|
753
895
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-search", children: "\u641C\u7D22\u4F1A\u8BDD" }),
|
|
@@ -776,7 +918,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
776
918
|
query || workspaceFilter !== "all" ? `\uFF0C\u5171 ${filter === "archived" ? archivedList.length : filter === "active" ? activeList.length : filter === "starred" ? starredList.length : sessions.length} \u4E2A` : ""
|
|
777
919
|
] })
|
|
778
920
|
] }),
|
|
779
|
-
|
|
921
|
+
showSessionList && list.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-batch", children: [
|
|
780
922
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "sess-btntext", children: selIds.length ? `\u5DF2\u9009 ${selIds.length} \u9879` : filter === "archived" ? `\u5171 ${archivedList.length} \u4E2A\u5F52\u6863\u4F1A\u8BDD` : filter === "starred" ? `\u5171 ${starredList.length} \u4E2A\u6536\u85CF\u4F1A\u8BDD` : `\u5171 ${sessions.length} \u4E2A\u4F1A\u8BDD\uFF08\u6D3B\u52A8 ${activeList.length} / \u5DF2\u5F52\u6863 ${archivedList.length}\uFF09` }),
|
|
781
923
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: list.length === 0, onClick: selectAll, children: "\u5168\u9009" }),
|
|
782
924
|
selIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
@@ -788,7 +930,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
788
930
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", onClick: clearSel, children: "\u53D6\u6D88\u9009\u62E9" })
|
|
789
931
|
] })
|
|
790
932
|
] }),
|
|
791
|
-
|
|
933
|
+
showSessionList && list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", children: query || workspaceFilter !== "all" ? "\u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD\u3002\u8BF7\u8C03\u6574\u641C\u7D22\u8BCD\u6216\u5DE5\u4F5C\u533A\u7B5B\u9009\u3002" : filter === "archived" ? "\u76EE\u524D\u6CA1\u6709\u5F52\u6863\u4F1A\u8BDD\u3002\u5728\u201C\u5168\u90E8\u201D\u91CC\u9009\u4E2D\u4F1A\u8BDD\u70B9\u201C\u5F52\u6863\u201D\u5373\u53EF\u6536\u7EB3\u8FDB\u6765\u3002" : filter === "active" ? "\u76EE\u524D\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD\u3002" : filter === "starred" ? "\u8FD8\u6CA1\u6709\u6536\u85CF\u7684\u4F1A\u8BDD\u3002\u70B9\u51FB\u4F1A\u8BDD\u5DE6\u4FA7\u7684\u661F\u6807\u5373\u53EF\u6536\u85CF\u3002" : "\u6682\u65E0\u53EF\u7BA1\u7406\u7684\u4F1A\u8BDD\u3002" }) : showSessionList ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-list", role: "list", children: list.map((it) => {
|
|
792
934
|
const date = fmtDate(it.createdAt);
|
|
793
935
|
const expanded = openMove === it.sessionId;
|
|
794
936
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-card" + (expanded ? " archv-card-exp" : ""), role: "listitem", children: [
|
|
@@ -815,7 +957,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
815
957
|
e.stopPropagation();
|
|
816
958
|
toggleStar(it);
|
|
817
959
|
},
|
|
818
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "
|
|
960
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2.5l2.9 5.9 6.6.9-4.8 4.6 1.2 6.5-5.9-3.1-5.9 3.1 1.2-6.5L2.5 9.3l6.6-.9z" }) })
|
|
819
961
|
}
|
|
820
962
|
),
|
|
821
963
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-body", children: [
|
|
@@ -1129,6 +1271,21 @@ var SIDEBAR_AUG_CSS = `
|
|
|
1129
1271
|
.dsm-trash-date{font-size:11px;color:var(--dsw-alias-label-tertiary);flex:none;white-space:nowrap}
|
|
1130
1272
|
.dsm-trash-actions{display:flex;gap:6px;flex:none}.dsm-trash-actions .archv-btn{min-width:72px}
|
|
1131
1273
|
.dsm-trash-empty{font-size:12px;color:var(--dsw-alias-label-tertiary);padding:6px 2px}
|
|
1274
|
+
.dsm-storage-sum{font-size:12px;color:var(--dsw-alias-label-secondary);margin-bottom:10px}
|
|
1275
|
+
.dsm-storage-list{display:flex;flex-direction:column;gap:6px}
|
|
1276
|
+
.dsm-storage-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-fill-elevated)}
|
|
1277
|
+
.dsm-storage-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;color:var(--dsw-alias-label-primary)}
|
|
1278
|
+
.dsm-storage-bar{flex:0 0 96px;height:6px;border-radius:999px;background:var(--dsw-alias-fill-subtle);overflow:hidden}
|
|
1279
|
+
.dsm-storage-fill{display:block;height:100%;border-radius:999px;background:var(--dsw-alias-state-business-primary)}
|
|
1280
|
+
.dsm-storage-size{font-size:12px;color:var(--dsw-alias-label-secondary);flex:none;white-space:nowrap}
|
|
1281
|
+
.dsm-storage-count{font-size:11px;color:var(--dsw-alias-label-tertiary);flex:none;white-space:nowrap;max-width:32%;overflow:hidden;text-overflow:ellipsis}
|
|
1282
|
+
.maint-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 8px}
|
|
1283
|
+
.maint-bar-right{margin-left:auto}
|
|
1284
|
+
.maint-note{font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
1285
|
+
.mv-sheet-actions{display:flex;align-items:center;gap:6px}
|
|
1286
|
+
.aa-check{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer}
|
|
1287
|
+
.aa-check input{width:15px;height:15px;accent-color:var(--dsw-alias-state-business-primary);cursor:pointer;flex:none}
|
|
1288
|
+
@media (max-width:640px){.dsm-storage-bar{display:none}.dsm-storage-count{max-width:40%}}
|
|
1132
1289
|
`;
|
|
1133
1290
|
var DSM_KEY_MANUAL = "dsm-manual-unread-v1";
|
|
1134
1291
|
var dsmManualUnread = null;
|