dsh-session-manager 0.5.1 → 0.5.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/CHANGELOG.md +43 -1
- package/README.md +54 -72
- package/README.zh.md +50 -78
- package/lib/annotation-store.js +198 -158
- package/lib/client.js +939 -98
- package/lib/clipboard-parser.js +207 -207
- package/lib/index.js +147 -4
- package/lib/session-files.js +211 -179
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -39,6 +39,18 @@ window.__ModuleLoader__.load({
|
|
|
39
39
|
}
|
|
40
40
|
} catch (_) { /* react-dom/client not available */ }
|
|
41
41
|
}
|
|
42
|
+
// Monotonically-increasing z-index for popup dialogs so the most-recently
|
|
43
|
+
// opened popup always sits on top of any previously opened popup. The
|
|
44
|
+
// CSS-defined z-index values (panel 9999 / confirm 10000 / annotation +
|
|
45
|
+
// bulk 10002) are static; without a counter the user can open an
|
|
46
|
+
// annotation popup (10002) after a confirm popup (10000) and the older
|
|
47
|
+
// confirm stays below. Each dialog component claims a fresh slot from
|
|
48
|
+
// this counter on mount, writes it to inline style.zIndex, and never
|
|
49
|
+
// recycles within a session, so the most-recently opened popup is
|
|
50
|
+
// always the topmost one.
|
|
51
|
+
let dialogZStackCounter = 11000;
|
|
52
|
+
function nextDialogZ() { return ++dialogZStackCounter; }
|
|
53
|
+
|
|
42
54
|
|
|
43
55
|
const NS = "session-manager";
|
|
44
56
|
const API = "/session-manager/api";
|
|
@@ -116,6 +128,7 @@ window.__ModuleLoader__.load({
|
|
|
116
128
|
"footer.aria": "会话管理",
|
|
117
129
|
"footer.label": "会话管理",
|
|
118
130
|
"panel.title": "会话管理",
|
|
131
|
+
"panel.github.title": "欢迎Star和Issue",
|
|
119
132
|
"panel.search": "搜索标题、ID、标签或备注",
|
|
120
133
|
"panel.workspace": "工作区",
|
|
121
134
|
"panel.workspace.all": "全部工作区",
|
|
@@ -170,7 +183,62 @@ window.__ModuleLoader__.load({
|
|
|
170
183
|
"busy.processing": "处理中…",
|
|
171
184
|
"error.operation": "操作失败:{message}",
|
|
172
185
|
"workspace.default": "默认工作区",
|
|
173
|
-
"workspace.sessions": "{count} 个会话"
|
|
186
|
+
"workspace.sessions": "{count} 个会话",
|
|
187
|
+
"bulk.title": "批量管理",
|
|
188
|
+
"bulk.selectedFiltered": "已选 {selected} / {filtered} 项",
|
|
189
|
+
"bulk.selectAllFiltered": "全选当前筛选",
|
|
190
|
+
"bulk.clear": "清空选择",
|
|
191
|
+
"bulk.archive": "归档",
|
|
192
|
+
"bulk.unarchive": "取消归档",
|
|
193
|
+
"bulk.delete": "删除会话",
|
|
194
|
+
"bulk.move": "移动至工作区",
|
|
195
|
+
"bulk.moveBtn": "移动至工作区",
|
|
196
|
+
"bulk.presetMigrate": "迁移预设",
|
|
197
|
+
"bulk.favorite": "收藏",
|
|
198
|
+
"bulk.unfavorite": "取消收藏",
|
|
199
|
+
"bulk.review": "待回看",
|
|
200
|
+
"bulk.unreview": "取消待回看",
|
|
201
|
+
"bulk.tagsAdd": "添加标签",
|
|
202
|
+
"bulk.tagsRemove": "清空标签",
|
|
203
|
+
"bulk.priority": "设置优先级",
|
|
204
|
+
"bulk.tags.placeholder": "用逗号分隔,例如:排障, 方案",
|
|
205
|
+
"bulk.priority.aria": "选择优先级",
|
|
206
|
+
"bulk.workspace.aria": "选择目标工作区",
|
|
207
|
+
"bulk.preset.aria": "选择目标 preset",
|
|
208
|
+
"bulk.dialog.title": "批量操作:{action}",
|
|
209
|
+
"bulk.dialog.summary": "将会对 {count} 个会话执行「{action}」。",
|
|
210
|
+
"bulk.dialog.warning.delete": "删除操作不可撤销,请确认后再继续。",
|
|
211
|
+
"bulk.confirm.archive": "确认归档",
|
|
212
|
+
"bulk.confirm.unarchive": "确认还原",
|
|
213
|
+
"bulk.confirm.favorite": "确认收藏",
|
|
214
|
+
"bulk.confirm.unfavorite": "确认移除",
|
|
215
|
+
"bulk.confirm.review": "确认标记",
|
|
216
|
+
"bulk.confirm.unreview": "确认清除",
|
|
217
|
+
"bulk.confirm.tagsAdd": "确认添加",
|
|
218
|
+
"bulk.confirm.tagsRemove": "确认清空",
|
|
219
|
+
"bulk.confirm.priority": "确认设置",
|
|
220
|
+
"bulk.confirm.move": "确认移动",
|
|
221
|
+
"bulk.confirm.presetMigrate": "确认迁移",
|
|
222
|
+
"bulk.confirm.delete": "确认删除",
|
|
223
|
+
"bulk.dialog.execute": "执行",
|
|
224
|
+
"bulk.dialog.cancel": "取消",
|
|
225
|
+
"bulk.progress.title": "正在执行:{action}",
|
|
226
|
+
"bulk.progress.current": "处理 {done} / {total}",
|
|
227
|
+
"bulk.progress.cancel": "取消",
|
|
228
|
+
"bulk.progress.completed": "已完成 {done} / {total}",
|
|
229
|
+
"bulk.result.title": "批量操作完成:{action}",
|
|
230
|
+
"bulk.result.successGroup": "成功 ({count})",
|
|
231
|
+
"bulk.result.failedGroup": "失败 ({count})",
|
|
232
|
+
"bulk.result.skippedGroup": "跳过 ({count})",
|
|
233
|
+
"bulk.result.retry": "重试失败项",
|
|
234
|
+
"bulk.result.close": "关闭",
|
|
235
|
+
"bulk.error.tooMany": "批量操作最多支持 200 项,请缩小筛选范围后重试",
|
|
236
|
+
"bulk.checkbox.aria": "选择 {title}",
|
|
237
|
+
"bulk.heading": "批量操作",
|
|
238
|
+
"panel.selectMode": "批量处理",
|
|
239
|
+
"panel.selectMode.exit": "退出批量处理",
|
|
240
|
+
"panel.selectMode.aria": "进入批量管理模式",
|
|
241
|
+
"panel.selectMode.active.aria": "退出批量管理模式",
|
|
174
242
|
};
|
|
175
243
|
const en = {
|
|
176
244
|
"marks.filter.favorite": "Favorites",
|
|
@@ -245,6 +313,7 @@ window.__ModuleLoader__.load({
|
|
|
245
313
|
"footer.aria": "Session manager",
|
|
246
314
|
"footer.label": "Sessions",
|
|
247
315
|
"panel.title": "Session manager",
|
|
316
|
+
"panel.github.title": "Star or open an issue",
|
|
248
317
|
"panel.search": "Search title, ID, tags or notes",
|
|
249
318
|
"panel.workspace": "Workspace",
|
|
250
319
|
"panel.workspace.all": "All workspaces",
|
|
@@ -261,6 +330,61 @@ window.__ModuleLoader__.load({
|
|
|
261
330
|
"panel.count": "Showing {shown} / {total} sessions",
|
|
262
331
|
"panel.reset": "Reset filters",
|
|
263
332
|
"panel.empty.filtered": "No matching sessions. Adjust your search or filters.",
|
|
333
|
+
"bulk.title": "Bulk management",
|
|
334
|
+
"bulk.selectedFiltered": "Selected {selected} / {filtered}",
|
|
335
|
+
"bulk.selectAllFiltered": "Select all in filter",
|
|
336
|
+
"bulk.clear": "Clear selection",
|
|
337
|
+
"bulk.archive": "Archive",
|
|
338
|
+
"bulk.unarchive": "Unarchive",
|
|
339
|
+
"bulk.delete": "Delete session",
|
|
340
|
+
"bulk.move": "Move to workspace",
|
|
341
|
+
"bulk.moveBtn": "Move",
|
|
342
|
+
"bulk.presetMigrate": "Migrate preset",
|
|
343
|
+
"bulk.favorite": "Favorite",
|
|
344
|
+
"bulk.unfavorite": "Unfavorite",
|
|
345
|
+
"bulk.review": "Mark for review",
|
|
346
|
+
"bulk.unreview": "Clear review flag",
|
|
347
|
+
"bulk.tagsAdd": "Add tags",
|
|
348
|
+
"bulk.tagsRemove": "Clear tags",
|
|
349
|
+
"bulk.priority": "Set priority",
|
|
350
|
+
"bulk.tags.placeholder": "Comma-separated, e.g. bug, design",
|
|
351
|
+
"bulk.priority.aria": "Choose priority",
|
|
352
|
+
"bulk.workspace.aria": "Choose target workspace",
|
|
353
|
+
"bulk.preset.aria": "Choose target preset",
|
|
354
|
+
"bulk.dialog.title": "Bulk action: {action}",
|
|
355
|
+
"bulk.dialog.summary": "Apply \"{action}\" to {count} sessions.",
|
|
356
|
+
"bulk.dialog.warning.delete": "Deletion is permanent and cannot be undone.",
|
|
357
|
+
"bulk.confirm.archive": "Archive",
|
|
358
|
+
"bulk.confirm.unarchive": "Unarchive",
|
|
359
|
+
"bulk.confirm.favorite": "Favorite",
|
|
360
|
+
"bulk.confirm.unfavorite": "Unfavorite",
|
|
361
|
+
"bulk.confirm.review": "Mark review",
|
|
362
|
+
"bulk.confirm.unreview": "Clear review",
|
|
363
|
+
"bulk.confirm.tagsAdd": "Add tags",
|
|
364
|
+
"bulk.confirm.tagsRemove": "Clear tags",
|
|
365
|
+
"bulk.confirm.priority": "Set priority",
|
|
366
|
+
"bulk.confirm.move": "Move",
|
|
367
|
+
"bulk.confirm.presetMigrate": "Migrate",
|
|
368
|
+
"bulk.confirm.delete": "Delete",
|
|
369
|
+
"bulk.dialog.execute": "Execute",
|
|
370
|
+
"bulk.dialog.cancel": "Cancel",
|
|
371
|
+
"bulk.progress.title": "Running: {action}",
|
|
372
|
+
"bulk.progress.current": "Processing {done} / {total}",
|
|
373
|
+
"bulk.progress.cancel": "Cancel",
|
|
374
|
+
"bulk.progress.completed": "Finished {done} / {total}",
|
|
375
|
+
"bulk.result.title": "Bulk action finished: {action}",
|
|
376
|
+
"bulk.result.successGroup": "Success ({count})",
|
|
377
|
+
"bulk.result.failedGroup": "Failed ({count})",
|
|
378
|
+
"bulk.result.skippedGroup": "Skipped ({count})",
|
|
379
|
+
"bulk.result.retry": "Retry failed",
|
|
380
|
+
"bulk.result.close": "Close",
|
|
381
|
+
"bulk.error.tooMany": "Batch actions support at most 200 sessions; please narrow the filter.",
|
|
382
|
+
"bulk.checkbox.aria": "Select {title}",
|
|
383
|
+
"bulk.heading": "Bulk actions",
|
|
384
|
+
"panel.selectMode": "Batch process",
|
|
385
|
+
"panel.selectMode.exit": "Exit batch process",
|
|
386
|
+
"panel.selectMode.aria": "Enter bulk selection mode",
|
|
387
|
+
"panel.selectMode.active.aria": "Exit bulk selection mode",
|
|
264
388
|
"panel.filter.all": "All",
|
|
265
389
|
"panel.filter.active": "Active",
|
|
266
390
|
"panel.filter.archived": "Archived",
|
|
@@ -315,7 +439,7 @@ window.__ModuleLoader__.load({
|
|
|
315
439
|
".sm-footerBtn svg{flex:none}",
|
|
316
440
|
".sm-panel{display:flex;flex-direction:column;gap:10px;min-height:280px;max-height:min(70vh,640px);min-width:480px}",
|
|
317
441
|
".sm-filter{display:flex;gap:4px;flex-wrap:wrap}",
|
|
318
|
-
".sm-filterBtn{cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:3px
|
|
442
|
+
".sm-filterBtn{cursor:pointer;display:inline-flex;align-items:center;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;height:28px;padding:3px 10px;font-size:12px;line-height:18px;white-space:nowrap;box-sizing:border-box}",
|
|
319
443
|
".sm-filterBtn:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
320
444
|
".sm-filterBtn.sm-on{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-fill-l2);border-color:transparent}",
|
|
321
445
|
".sm-list{display:flex;flex-direction:column;gap:4px;min-height:0;overflow:auto;flex:1}",
|
|
@@ -323,7 +447,7 @@ window.__ModuleLoader__.load({
|
|
|
323
447
|
".sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
324
448
|
".sm-rowCurrent{border-color:var(--dsw-alias-accent-primary,var(--dsw-alias-label-secondary))}",
|
|
325
449
|
".sm-rowMain{min-width:0;display:flex;flex-direction:column;gap:2px;flex:1 1 280px}",
|
|
326
|
-
".sm-rowTitle{color:var(--dsw-alias-label-primary);font-size:13px;line-height:
|
|
450
|
+
".sm-rowTitle{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;display:flex;align-items:center;gap:6px;min-width:0}",
|
|
327
451
|
".sm-rowTitle>span:first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}",
|
|
328
452
|
".sm-rowMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}",
|
|
329
453
|
".sm-rowActions{display:flex;gap:4px;flex:none;flex-wrap:wrap;justify-content:flex-end}",
|
|
@@ -347,32 +471,70 @@ window.__ModuleLoader__.load({
|
|
|
347
471
|
".sm-moveItemSelected{border-color:var(--dsw-alias-accent-primary);background:var(--dsw-alias-accent-primary-bg)}",
|
|
348
472
|
".sm-moveItemName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}",
|
|
349
473
|
".sm-moveItemMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.sm-panelModal{width:760px;max-width:92vw}.sm-panelModal .sm-rowActions{flex-wrap:wrap;justify-content:flex-end}.sm-headerMenu{position:absolute;right:0;top:100%;z-index:50;display:flex;flex-direction:column;min-width:160px;padding:4px;background:var(--dsw-alias-surface-l1);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.12)}.sm-headerMenuItem{display:flex;align-items:center;width:100%;text-align:left;background:0 0;border:0;border-radius:6px;color:var(--dsw-alias-label-primary);cursor:pointer;padding:6px 10px;font-size:13px;line-height:18px}.sm-headerMenuItem:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-headerMenuItemDanger{color:var(--dsw-alias-state-error-primary)}.sm-headerBtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;height:auto;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;padding:4px 12px;font-size:13px;line-height:18px;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}.sm-headerBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1)}.sm-headerBtn:focus-visible{outline:2px solid var(--dsw-alias-accent-primary);outline-offset:1px}.sm-headerBtn svg{width:16px;height:16px}.sm-migratePanel{display:flex;flex-direction:column;gap:14px;min-width:480px}.sm-migrateRow{display:flex;flex-direction:column;gap:8px}.sm-migrateRow>label{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px;font-weight:500}.sm-migrateChips{display:flex;flex-wrap:wrap;gap:6px}.sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:transparent;cursor:pointer;font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary);white-space:nowrap}.sm-migrateChip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-migrateChip:disabled{opacity:.5;cursor:not-allowed}.sm-migrateChipOn{background:var(--dsw-alias-accent-primary);color:#fff;border-color:var(--dsw-alias-accent-primary)}.sm-migrateEmpty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.sm-migratePreview{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}.sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background:rgba(217,119,6,.08);color:var(--dsw-alias-label-secondary);font-size:13px;line-height:18px}.sm-migrateList{max-height:200px;overflow:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:8px}.sm-headerBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1)}.sm-headerBtnDanger{background:var(--dsw-alias-state-error-tertiary);color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);font-weight:500}.sm-headerBtnDanger:hover:not(:disabled){background:#d92d20!important;color:#fff!important;border-color:#d92d20!important}.sm-header{display:inline-flex;align-items:center;gap:6px}.sm-annotationToggleOpen{position:relative;z-index:10004}.sm-headerBtnActive{background:var(--dsw-alias-fill-l3);color:var(--dsw-alias-label-primary)}",
|
|
350
|
-
".sm-panelDialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:600px;max-width:calc(100vw - 32px);max-height:560px;height:auto;box-sizing:border-box;display:flex;flex-direction:column;gap:0;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);overflow:hidden;z-index:9999;opacity:1}.sm-panelDialog .sm-nativeDialogHeader{padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none}.sm-panelDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600}.sm-panelDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:flex-end;gap:6px;margin-top:0}.sm-panelDialog .sm-panel{display:flex;flex-direction:column;gap:6px;min-height:0;flex:1 1 auto;overflow:auto;padding:8px 12px}.sm-panelDialog .sm-list{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-height:0;overflow:auto}.sm-panelDialog .sm-row{display:flex;align-items:center;gap:8px;padding:0 10px;height:36px;min-height:36px;border:1px solid transparent;border-radius:8px;background:transparent}.sm-panelDialog .sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-rowCurrent{background:var(--dsw-alias-fill-l2);border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-panelDialog .sm-rowMain{display:flex;align-items:stretch!important;gap:8px;min-width:0;flex:1 1 auto;overflow:hidden;text-align:left!important}.sm-panelDialog .sm-rowTitle{min-width:0;flex:1 1 0%;width:100%;align-self:stretch;display:flex;align-items:center;justify-content:flex-start;gap:6px;font-size:12px;line-height:16px;overflow:hidden;text-align:left!important;direction:ltr!important}.sm-confirmDialog{z-index:10000}.sm-confirmDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:300px;max-width:calc(100vw - 32px);max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;box-sizing:border-box;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-confirmDialog .sm-nativeDialogHeader{padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee)}.sm-confirmDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600}.sm-confirmDialog .sm-nativeDialogBody{padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0}.sm-confirmDialog .sm-nativeDialogDescription{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap}.sm-confirmDialog .sm-warn{padding:6px 10px;font-size:12px;line-height:16px;margin-top:0}.sm-confirmDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:0}.sm-confirmDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary)}.sm-confirmDialog .sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-confirmDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-state-error-primary);color:#fff;border-color:var(--dsw-alias-state-error-primary)}.sm-confirmDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}@keyframes sm-confirmPop{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sm-confirmDialog.sm-nativeDialog{padding:0;width:auto;max-width:none}.sm-panelDialog .sm-rowTitle>span:first-child{display:block;min-width:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;flex:1 1 auto;width:auto;text-align:left!important;direction:ltr!important;unicode-bidi:plaintext}.sm-panelDialog .sm-rowMeta{display:none}.sm-panelDialog .sm-rowActions{display:flex;gap:2px;flex:none;justify-content:flex-end}.sm-panelDialog .sm-rowActions .sm-rowBtn{min-height:22px;height:22px;padding:0 8px;font-size:11px;line-height:14px;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}.sm-panelDialog .sm-rowActions .sm-rowBtn:hover{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger{color:var(--dsw-alias-state-error-primary)}.sm-panelDialog .sm-filter{flex:none;padding:6px 12px 4px;gap:4px;display:flex;flex-wrap:wrap}.sm-panelDialog .sm-filterBtn{min-height:24px;padding:2px 10px;font-size:11px;line-height:16px;border-radius:4px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}.sm-panelDialog .sm-filterBtn.sm-on{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-badge{font-size:10px;line-height:14px;padding:0 5px;border-radius:6px;flex:none}.sm-panelDialog .sm-error{padding:6px 10px;margin:0;font-size:12px;line-height:16px}.sm-panelDialog .sm-nativeDialogButton{min-height:28px;padding:3px 12px;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer}.sm-panelDialog .sm-nativeDialogCancel{background:transparent}.sm-panelDialog .sm-nativeDialogGhost{background:transparent;border-color:var(--dsw-alias-border-l2)}.sm-panelDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-panelDialog .sm-empty{padding:18px 0;font-size:12px;color:var(--dsw-alias-label-tertiary);text-align:center}.sm-panelDialog .sm-footError{font-size:11px;color:var(--dsw-alias-state-error-primary);margin-right:auto;line-height:20px}.sm-panelDialog .sm-moveLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.sm-migrateDialog .sm-nativeDialogFooter{flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:14px}.sm-migrateDialog .sm-migrateChip{cursor:pointer;pointer-events:auto;position:relative;z-index:1}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog.sm-nativeDialogLayer{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:auto;z-index:10001}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;max-height:min(70vh,520px);box-sizing:border-box;background-color:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.45);overflow:hidden;padding:0;display:flex;flex-direction:column;gap:0;opacity:1}.sm-migrateDialog{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:min(70vh,520px);box-sizing:border-box;display:flex;flex-direction:column;gap:0;background-color:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.45);overflow:hidden;opacity:1;z-index:10001;backdrop-filter:none;-webkit-backdrop-filter:none}.sm-migrateDialog .sm-nativeDialogHeader{padding:12px 18px;border-bottom:1px solid #eee;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogTitle{margin:0;font-size:15px;line-height:22px;font-weight:600;color:#111;color:var(--dsw-alias-label-primary,#111)}.sm-migrateDialog .sm-migratePanel{padding:12px 18px;display:flex;flex-direction:column;gap:14px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogBody{padding:0;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogFooter{padding:12px 18px;border-top:1px solid #eee;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:flex-end;gap:8px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-migrateRow>label{color:#444;color:var(--dsw-alias-label-secondary,#444)}.sm-migrateDialog .sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px;cursor:pointer;pointer-events:auto;position:relative;z-index:1;white-space:nowrap}.sm-migrateDialog .sm-migrateChip:hover:not(:disabled){background-color:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-migrateChipOn{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-migrateChip.sm-migrateChipOn:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-hover,#4576f0);filter:none}.sm-migrateDialog .sm-migrateChipOn:hover:not(:disabled){filter:brightness(.92)}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog .sm-migrateEmpty{color:#888;color:var(--dsw-alias-label-tertiary,#888)}.sm-migrateDialog .sm-migratePreview{padding:10px 12px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px}.sm-migrateDialog .sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background-color:rgba(217,119,6,.08);color:#444;color:var(--dsw-alias-label-secondary,#444);font-size:13px;line-height:18px}.sm-migrateDialog .sm-nativeDialogButton{min-height:32px;padding:4px 14px;font-size:13px;line-height:20px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background-color:#fff;background-color:var(--dsw-alias-fill-l2,#fff);color:#111;color:var(--dsw-alias-label-primary,#111)}.sm-migrateDialog .sm-nativeDialogButton:hover:not(:disabled){background-color:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-nativeDialogConfirm{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}",
|
|
351
|
-
".sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;gap:8px}.sm-panelHeaderClose{min-height:26px;padding:2px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;font-size:12px;line-height:18px}.sm-panelHeaderClose:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderClose:disabled{opacity:.5;cursor:not-allowed}.sm-migrateCurrent{padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background:var(--dsw-alias-fill-l2,#f5f5f5);font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#111)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer{width:
|
|
474
|
+
".sm-panelDialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:600px;max-width:calc(100vw - 32px);max-height:560px;height:auto;box-sizing:border-box;display:flex;flex-direction:column;gap:0;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);overflow:hidden;z-index:9999;opacity:1}.sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;gap:8px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none}.sm-panelDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center;flex:1 1 auto}.sm-panelDialog .sm-nativeDialogHeaderActions{display:flex;align-items:center;gap:6px;flex:none}.sm-panelDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;flex:1 1 auto}.sm-nativeDialogFooter,.sm-bulkDialog .sm-nativeDialogFooter,.sm-confirmDialog .sm-nativeDialogFooter,.sm-migrateDialog .sm-nativeDialogFooter{display:flex;padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-panelDialog .sm-panel{display:flex;flex-direction:column;gap:6px;min-height:0;flex:1 1 auto;overflow:auto;padding:8px 12px}.sm-panelDialog .sm-list{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-height:0;overflow:auto}.sm-panelDialog .sm-row{display:flex;align-items:center;gap:8px;padding:6px 10px;min-height:36px;border:1px solid transparent;border-radius:8px;background:transparent;flex-shrink:0}.sm-panelDialog .sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-rowCurrent{background:var(--dsw-alias-fill-l2);border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-panelDialog .sm-rowMain{display:flex;align-items:stretch!important;gap:8px;min-width:0;flex:1 1 auto;overflow:visible;text-align:left!important}.sm-panelDialog .sm-rowTitle{min-width:0;flex:1 1 0%;width:100%;align-self:stretch;display:flex;align-items:center;justify-content:flex-start;gap:6px;font-size:12px;line-height:16px;overflow:hidden;text-align:left!important;direction:ltr!important}.sm-confirmDialog{z-index:10000}.sm-confirmDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:360px;max-width:calc(100vw - 32px);max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;box-sizing:border-box;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-confirmDialog .sm-nativeDialogHeader{padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee)}.sm-confirmDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;width:100%}.sm-confirmDialog .sm-nativeDialogBody{padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0}.sm-confirmDialog .sm-nativeDialogDescription{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap}.sm-confirmDialog .sm-warn{padding:6px 10px;font-size:12px;line-height:16px;margin-top:0}.sm-confirmDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-confirmDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary);text-align:center}.sm-confirmDialog .sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-confirmDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-confirmDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}@keyframes sm-confirmPop{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sm-confirmDialog.sm-nativeDialog{padding:0;width:auto;max-width:none}.sm-panelDialog .sm-rowTitle>span:first-child{display:block;min-width:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;flex:1 1 auto;width:auto;text-align:left!important;direction:ltr!important;unicode-bidi:plaintext}.sm-panelDialog .sm-rowMeta{display:none}.sm-panelDialog .sm-rowActions{display:flex;gap:2px;flex:none;justify-content:flex-end}.sm-panelDialog .sm-rowActions .sm-rowBtn{min-height:22px;padding:0 7px;font-size:11px;line-height:18px;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap}.sm-rowBtn.sm-markIcon{padding:0 6px;font-size:13px;line-height:18px}.sm-panelDialog .sm-rowActions .sm-rowBtn:hover{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger{color:var(--dsw-alias-state-error-primary)}.sm-panelDialog .sm-filter{flex:none;padding:6px 12px 4px;gap:4px;display:flex;flex-wrap:wrap}.sm-panelDialog .sm-filterBtn{min-height:24px;padding:2px 10px;font-size:11px;line-height:16px;border-radius:4px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}.sm-panelDialog .sm-filterBtn.sm-on{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-badge{font-size:10px;line-height:14px;padding:0 5px;border-radius:6px;flex:none}.sm-panelDialog .sm-error{padding:6px 10px;margin:0;font-size:12px;line-height:16px}.sm-panelDialog .sm-nativeDialogButton{min-height:28px;padding:3px 12px;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer}.sm-panelDialog .sm-nativeDialogCancel{background:transparent}.sm-panelDialog .sm-nativeDialogGhost{background:transparent;border-color:var(--dsw-alias-border-l2)}.sm-panelDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:#fff;border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-confirmDialog .sm-nativeDialogDanger,.sm-migrateDialog .sm-nativeDialogDanger,.sm-bulkDialog .sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:#fff;border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-confirmDialog .sm-nativeDialogDanger:hover:not(:disabled),.sm-migrateDialog .sm-nativeDialogDanger:hover:not(:disabled),.sm-bulkDialog .sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-panelDialog .sm-empty{padding:18px 0;font-size:12px;color:var(--dsw-alias-label-tertiary);text-align:center}.sm-panelDialog .sm-footError{font-size:11px;color:var(--dsw-alias-state-error-primary);margin-right:auto;line-height:20px}.sm-panelDialog .sm-moveLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.sm-migrateDialog .sm-nativeDialogFooter{flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-migrateDialog .sm-migrateChip{cursor:pointer;pointer-events:auto;position:relative;z-index:1}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog.sm-nativeDialogLayer{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:auto;z-index:10001}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:360px;max-width:calc(100vw - 32px);max-height:min(70vh,520px);box-sizing:border-box;background-color:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.45);overflow:hidden;padding:0;display:flex;flex-direction:column;gap:0;opacity:1}.sm-migrateDialog{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:min(70vh,520px);box-sizing:border-box;display:flex;flex-direction:column;gap:0;background-color:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.45);overflow:hidden;opacity:1;z-index:10001;backdrop-filter:none;-webkit-backdrop-filter:none}.sm-migrateDialog .sm-nativeDialogHeader{padding:12px 18px;border-bottom:1px solid #eee;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;color:#111;color:var(--dsw-alias-label-primary,#111);text-align:center;width:100%}.sm-migrateDialog .sm-migratePanel{padding:12px 18px;display:flex;flex-direction:column;gap:14px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogBody{padding:0;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0;gap:8px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-migrateRow>label{color:#444;color:var(--dsw-alias-label-secondary,#444)}.sm-migrateDialog .sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px;cursor:pointer;pointer-events:auto;position:relative;z-index:1;white-space:nowrap}.sm-migrateDialog .sm-migrateChip:hover:not(:disabled){background-color:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-migrateChipOn{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-migrateChip.sm-migrateChipOn:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-hover,#4576f0);filter:none}.sm-migrateDialog .sm-migrateChipOn:hover:not(:disabled){filter:brightness(.92)}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog .sm-migrateEmpty{color:#888;color:var(--dsw-alias-label-tertiary,#888)}.sm-migrateDialog .sm-migratePreview{padding:10px 12px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px}.sm-migrateDialog .sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background-color:rgba(217,119,6,.08);color:#444;color:var(--dsw-alias-label-secondary,#444);font-size:13px;line-height:18px}.sm-migrateDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background-color:#fff;background-color:var(--dsw-alias-fill-l2,#fff);color:#111;color:var(--dsw-alias-label-primary,#111);text-align:center}.sm-migrateDialog .sm-nativeDialogButton:hover:not(:disabled){background-color:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-nativeDialogConfirm{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}",
|
|
475
|
+
".sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;gap:8px}.sm-panelHeaderClose{min-height:26px;padding:2px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;font-size:12px;line-height:18px;flex:none}.sm-panelHeaderGithub{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;color:var(--dsw-alias-label-secondary,#555);text-decoration:none;flex:none;transition:background .15s,color .15s;cursor:pointer}.sm-panelHeaderGithub:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderGithub svg{display:block;width:16px;height:16px;fill:currentColor}.sm-panelHeaderClose:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderClose:disabled{opacity:.5;cursor:not-allowed}.sm-panelHeaderSelect{min-height:26px;padding:2px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;font-size:12px;line-height:18px;display:inline-flex;align-items:center;gap:4px}.sm-panelHeaderSelect:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderSelect:disabled{opacity:.5;cursor:not-allowed}.sm-panelHeaderSelect.sm-on{background:var(--dsw-alias-accent-primary-bg,rgba(53,106,230,.12));border-color:var(--dsw-alias-accent-primary,#356ae6);color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateCurrent{padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background:var(--dsw-alias-fill-l2,#f5f5f5);font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#111)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer{width:360px;inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer .sm-migrateDialog{position:relative;left:auto;top:auto;bottom:auto;transform:none;width:100%;max-width:none;max-height:min(70vh,520px);z-index:auto}.sm-migrateConfirmLayer .sm-migratePanel{min-width:0}.sm-migrateConfirmLayer .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-hover,#4576f0);filter:none}",
|
|
352
476
|
|
|
353
477
|
// Theme colors are scoped to the plugin dialogs. DSH exposes its
|
|
354
478
|
// actual theme through `body[data-ds-dark-theme]` and color-scheme;
|
|
355
479
|
// keeping these values local avoids changing DSH's global variables.
|
|
356
480
|
"[data-sm-theme=light] .sm-panelDialog,[data-sm-theme=light] .sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog,[data-sm-theme=light] .sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{--dsw-alias-surface-l1:#fff;--dsw-alias-label-primary:#1f1f1f;--dsw-alias-label-secondary:#555;--dsw-alias-label-tertiary:#888;--dsw-alias-border-l1:#d4d4d8;--dsw-alias-border-l2:#e5e5e5;--dsw-alias-fill-l2:#f5f5f5;--dsw-alias-interactive-bg-hover:#eee;--dsw-alias-state-error-primary:#d92d20;--dsw-alias-state-warning-primary:#d97706;--dsw-alias-accent-primary:#356ae6;--dsw-alias-accent-primary-bg:rgba(53,106,230,.12);background:#fff!important;background-color:#fff!important;color:#1f1f1f!important;border-color:#e5e5e5!important}",
|
|
357
|
-
"[data-sm-theme=dark] .sm-panelDialog,[data-sm-theme=dark] .sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog,[data-sm-theme=dark] .sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{--dsw-alias-surface-l1:#1f1f23;--dsw-alias-label-primary:#ececec;--dsw-alias-label-secondary:#b3b3b3;--dsw-alias-label-tertiary:#999;--dsw-alias-border-l1:#555;--dsw-alias-border-l2:#3a3a40;--dsw-alias-fill-l1:#1a1a1e;--dsw-alias-fill-l2:#2a2a30;--dsw-alias-interactive-bg-hover:rgba(255,255,255,.06);--dsw-alias-state-error-primary:#f47171;--dsw-alias-state-warning-primary:#f0a040;--dsw-alias-accent-primary:#5d8aff;--dsw-alias-accent-primary-bg:rgba(93,138,255,.18);background:#1f1f23!important;background-color:#1f1f23!important;color:#ececec!important;border-color:#3a3a40!important}","[data-sm-theme=dark] .sm-header{--dsw-alias-surface-l1:#1f1f23;--dsw-alias-label-primary:#ececec;--dsw-alias-label-secondary:#b3b3b3;--dsw-alias-label-tertiary:#999;--dsw-alias-border-l1:#555;--dsw-alias-border-l2:#3a3a40;--dsw-alias-fill-l1:#1a1a1e;--dsw-alias-fill-l2:#2a2a30;--dsw-alias-interactive-bg-hover:rgba(255,255,255,.06);--dsw-alias-accent-primary:#5d8aff;--dsw-alias-accent-primary-bg:rgba(93,138,255,.18)}",
|
|
481
|
+
"[data-sm-theme=dark] .sm-panelDialog,[data-sm-theme=dark] .sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog,[data-sm-theme=dark] .sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog,[data-sm-theme=dark] .sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialog{--dsw-alias-surface-l1:#1f1f23;--dsw-alias-label-primary:#ececec;--dsw-alias-label-secondary:#b3b3b3;--dsw-alias-label-tertiary:#999;--dsw-alias-border-l1:#555;--dsw-alias-border-l2:#3a3a40;--dsw-alias-fill-l1:#1a1a1e;--dsw-alias-fill-l2:#2a2a30;--dsw-alias-interactive-bg-hover:rgba(255,255,255,.06);--dsw-alias-state-error-primary:#f47171;--dsw-alias-state-warning-primary:#f0a040;--dsw-alias-accent-primary:#5d8aff;--dsw-alias-accent-primary-bg:rgba(93,138,255,.18);background:#1f1f23!important;background-color:#1f1f23!important;color:#ececec!important;border-color:#3a3a40!important}","[data-sm-theme=dark] .sm-header{--dsw-alias-surface-l1:#1f1f23;--dsw-alias-label-primary:#ececec;--dsw-alias-label-secondary:#b3b3b3;--dsw-alias-label-tertiary:#999;--dsw-alias-border-l1:#555;--dsw-alias-border-l2:#3a3a40;--dsw-alias-fill-l1:#1a1a1e;--dsw-alias-fill-l2:#2a2a30;--dsw-alias-interactive-bg-hover:rgba(255,255,255,.06);--dsw-alias-accent-primary:#5d8aff;--dsw-alias-accent-primary-bg:rgba(93,138,255,.18)}",
|
|
358
482
|
"[data-sm-theme=light] .sm-panelDialog .sm-nativeDialogHeader,[data-sm-theme=light] .sm-panelDialog .sm-nativeDialogFooter,[data-sm-theme=light] .sm-confirmDialog .sm-nativeDialogHeader,[data-sm-theme=light] .sm-confirmDialog .sm-nativeDialogFooter,[data-sm-theme=light] .sm-migrateDialog .sm-nativeDialogHeader,[data-sm-theme=light] .sm-migrateDialog .sm-nativeDialogBody,[data-sm-theme=light] .sm-migrateDialog .sm-nativeDialogFooter{background:#fff!important;background-color:#fff!important;border-color:#e5e5e5!important}",
|
|
359
|
-
"[data-sm-theme=dark] .sm-panelDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-panelDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-confirmDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-confirmDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogBody,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogFooter{background:#1f1f23!important;background-color:#1f1f23!important;border-color:#3a3a40!important}",
|
|
483
|
+
"[data-sm-theme=dark] .sm-panelDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-panelDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-confirmDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-confirmDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogBody,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-bulkDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-bulkDialog .sm-nativeDialogBody,[data-sm-theme=dark] .sm-bulkDialog .sm-nativeDialogFooter{background:#1f1f23!important;background-color:#1f1f23!important;border-color:#3a3a40!important}[data-sm-theme=dark] .sm-bulkDialog .sm-bulkSessionList,[data-sm-theme=dark] .sm-bulkDialog .sm-bulkResultList,[data-sm-theme=dark] .sm-bulkDialog .sm-bulkProgressBar{background:#1a1a1e!important;background-color:#1a1a1e!important;border-color:#3a3a40!important;color:var(--dsw-alias-label-primary,#ececec)}[data-sm-theme=dark] .sm-bulkDialog .sm-bulkSessionItem,[data-sm-theme=dark] .sm-bulkDialog .sm-bulkResultItemTitle,[data-sm-theme=dark] .sm-bulkDialog .sm-bulkResultItem{color:var(--dsw-alias-label-primary,#ececec)}",
|
|
360
484
|
".sm-panelDialog{max-height:min(640px,calc(100vh - 32px))}.sm-panelDialog .sm-panel{min-width:0;overflow:hidden}",
|
|
361
485
|
".sm-panelTools{display:flex;flex-direction:column;gap:8px;flex:none;padding:2px 0 6px}",
|
|
362
486
|
".sm-queryInput,.sm-viewSelect{box-sizing:border-box;width:100%;min-width:0;height:32px;padding:5px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:12px;line-height:20px}",
|
|
363
487
|
".sm-queryInput::placeholder{color:var(--dsw-alias-label-tertiary,#888)}.sm-queryInput:focus-visible,.sm-viewSelect:focus-visible,.sm-resetFilters:focus-visible{outline:2px solid var(--dsw-alias-accent-primary,#356ae6);outline-offset:2px}.sm-viewSelect:disabled{opacity:.6;cursor:wait}",
|
|
364
|
-
".sm-panelSelects{display:grid;grid-template-columns:
|
|
488
|
+
".sm-panelSelects{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.sm-panelSelects .sm-viewField>span{display:none}.sm-panelSelects .sm-viewField .sm-viewSelect{font-size:11px;padding:4px 7px;height:28px;line-height:18px}.sm-viewField{display:flex;flex-direction:column;gap:3px;min-width:0;color:var(--dsw-alias-label-secondary,#666);font-size:11px;line-height:16px}",
|
|
365
489
|
".sm-filterBar{display:flex;align-items:center;justify-content:space-between;gap:6px;flex-wrap:wrap}.sm-resultSummary{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--dsw-alias-label-tertiary,#888)}.sm-resetFilters{padding:2px 0;border:0;background:none;color:var(--dsw-alias-accent-primary,#356ae6);font:inherit;cursor:pointer}.sm-resetFilters:hover{text-decoration:underline}.sm-workspaceNotice{display:flex;gap:8px;align-items:center;font-size:12px;color:var(--dsw-alias-label-secondary,#666)}",
|
|
366
490
|
"@media(max-width:540px){.sm-panelSelects{grid-template-columns:minmax(0,1fr)}.sm-panelDialog .sm-row{height:auto;min-height:36px;flex-shrink:0;flex-wrap:wrap;padding:6px 8px}.sm-panelDialog .sm-rowMain{flex-basis:100%}.sm-panelDialog .sm-rowActions{width:100%;min-width:0;flex:1 1 100%;justify-content:flex-start}}",
|
|
367
|
-
".sm-panelDialog:not(.sm-annotationSurface){width:680px}.sm-panelDialog .sm-row{
|
|
491
|
+
".sm-panelDialog:not(.sm-annotationSurface){width:680px}.sm-panelDialog .sm-row{flex-shrink:0;padding:5px 8px}.sm-panelDialog .sm-rowMain{flex:1 1 200px;flex-direction:column;gap:4px}.sm-panelDialog .sm-rowActions{max-width:100%}.sm-rowSecondary{display:flex;flex-wrap:nowrap;align-items:center;gap:6px;min-width:0;line-height:18px}.sm-rowBadges{display:flex;align-items:center;gap:5px;min-width:0;flex:1 1 auto;flex-wrap:wrap;font-size:11px;line-height:18px}.sm-rowMeta{color:var(--dsw-alias-label-tertiary,#888);font-size:11px;line-height:18px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:none}.sm-rowAnnotations{display:flex;align-items:center;gap:5px;min-width:0;font-size:10px;line-height:16px}",
|
|
368
492
|
".sm-markIcon{min-width:25px;padding-left:5px;padding-right:5px;font-size:15px}.sm-markIcon.sm-markOn{color:var(--dsw-alias-accent-primary,#356ae6)!important;background:var(--dsw-alias-fill-l2,#eee)}.sm-priorityBadge{padding:0 5px;border-radius:4px;background:var(--dsw-alias-fill-l2,#eee);color:var(--dsw-alias-label-secondary,#666)}.sm-priority-1{color:#c2410c;background:rgba(234,88,12,.12)}.sm-priority-2{color:#a16207;background:rgba(202,138,4,.12)}",
|
|
369
493
|
"[data-sm-theme=dark] .sm-priority-1{color:#fdba74}[data-sm-theme=dark] .sm-priority-2{color:#fde68a}",
|
|
370
494
|
".sm-tagChip,.sm-noteBadge{max-width:88px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border:0;border-radius:4px;background:var(--dsw-alias-fill-l2,#eee);color:var(--dsw-alias-label-secondary,#666);font:inherit;line-height:16px;padding:0 5px;cursor:pointer}.sm-noteBadge{background:transparent}.sm-tagChip:hover,.sm-noteBadge:hover{color:var(--dsw-alias-accent-primary,#356ae6)}",
|
|
371
|
-
".sm-markFilters{display:
|
|
372
|
-
".sm-annotationDialog.sm-nativeDialogLayer{position:fixed;display:block;pointer-events:none;z-index:10002;background:transparent}.sm-annotationDialog .sm-nativeDialogBackdrop{display:none}.sm-annotationDialog.sm-annotationDialog-anchored.sm-nativeDialogLayer{inset:auto;background:transparent}.sm-annotationDialog.sm-confirmDialog.sm-nativeDialogLayer:not(.sm-annotationDialog-anchored){inset:50% auto auto calc(50vw + 308px);transform:translateY(-50%);max-height:calc(100vh - 32px)}.sm-panelDialog.sm-annotationSurface{position:absolute;width:
|
|
495
|
+
".sm-markFilters{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.sm-markFiltersGroup{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.sm-markFiltersSpacer{flex:1 1 auto;min-width:12px}.sm-markFilterToggle{height:28px;padding:3px 10px;white-space:nowrap}.sm-markFilterToggle:disabled{opacity:.5;cursor:not-allowed}.sm-bulkSelectBtn{height:28px;padding:3px 10px;white-space:nowrap}.sm-bulkSelectBtn:disabled{opacity:.5;cursor:not-allowed}.sm-header{flex-wrap:wrap}",
|
|
496
|
+
".sm-annotationDialog.sm-nativeDialogLayer{position:fixed;display:block;pointer-events:none;z-index:10002;background:transparent}.sm-annotationDialog .sm-nativeDialogBackdrop{display:none}.sm-annotationDialog.sm-annotationDialog-anchored.sm-nativeDialogLayer{inset:auto;background:transparent}.sm-annotationDialog.sm-confirmDialog.sm-nativeDialogLayer:not(.sm-annotationDialog-anchored){inset:50% auto auto calc(50vw + 308px);transform:translateY(-50%);max-height:calc(100vh - 32px)}.sm-panelDialog.sm-annotationSurface{position:absolute;width:360px;max-height:min(680px,calc(100vh - 32px));pointer-events:auto;z-index:10003;box-shadow:0 16px 48px rgba(0,0,0,.25)}.sm-panelDialog.sm-annotationSurface.sm-annotationSurface-anchored{position:relative;transform:none;left:auto;top:auto;margin:0}.sm-annotationCloseHint{margin:0 16px 8px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}",
|
|
373
497
|
".sm-annotationBody{padding:14px 16px;display:flex;flex-direction:column;gap:12px;min-height:0;overflow:auto;flex:1 1 auto}.sm-annotationTarget{font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary,#666)}.sm-annotationChecks{display:grid;grid-template-columns:auto 1fr;column-gap:14px;row-gap:4px;align-items:center;font-size:13px}.sm-annotationChecks>label{display:flex;gap:6px;align-items:center;cursor:pointer}.sm-annotationChecks .sm-annotationPriority{display:inline-flex;align-items:center;gap:6px;font-size:13px;line-height:18px;color:inherit}.sm-annotationChecks .sm-prioritySelectInline{height:24px;padding:2px 6px;font-size:12px;line-height:18px;min-width:104px;width:auto;border-radius:6px;color:var(--dsw-alias-label-primary,#111)}.sm-annotationChecks>small{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888);justify-self:start}.sm-annotationHint,.sm-annotationBody small{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888)}",
|
|
374
498
|
".sm-noteInput{box-sizing:border-box;width:100%;min-height:60px;resize:vertical;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;padding:8px;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;line-height:19px}.sm-noteInput:focus-visible{outline:2px solid var(--dsw-alias-accent-primary,#356ae6);outline-offset:2px}.sm-markConflict{display:flex;flex-direction:column;gap:5px;font-size:12px;padding:8px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:6px}.sm-fieldWithClear .sm-fieldRow{position:relative;display:flex;align-items:flex-start;gap:4px}.sm-fieldRow .sm-queryInput{flex:1 1 auto;min-width:0}.sm-fieldRowNote{align-items:stretch}.sm-fieldClear{position:absolute;top:5px;right:6px;width:22px;height:22px;border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;border-radius:4px;font-size:14px;line-height:18px;display:inline-flex;align-items:center;justify-content:center;padding:0}.sm-fieldClear:hover,.sm-fieldClear:focus-visible{color:var(--dsw-alias-accent-primary);background:var(--dsw-alias-interactive-bg-hover);outline:none}.sm-fieldClearNote{top:6px}.sm-importGroup{display:flex;flex-direction:column;gap:8px;padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);background:var(--dsw-alias-fill-l1,#fafbfc)}.sm-importGroupButtons{display:flex;justify-content:space-between;align-items:center;gap:8px}.sm-importBtn{cursor:pointer;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-surface-l1,#fff);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;padding:5px 12px;font-size:12px;line-height:18px}.sm-importBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.sm-importBtn:disabled{opacity:.5;cursor:not-allowed}.sm-importStatus{margin:0 16px;padding:8px 10px;font-size:12px;line-height:18px;background:var(--dsw-alias-accent-primary-bg,rgba(53,106,230,.08));color:var(--dsw-alias-label-secondary);border-radius:6px;white-space:pre-wrap;word-break:break-word}",
|
|
375
|
-
"
|
|
499
|
+
".sm-bulkBar{box-sizing:border-box;display:grid;grid-template-columns:repeat(6,minmax(0,1fr));align-items:stretch;gap:6px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:10px;background:var(--dsw-alias-fill-l1,#fafbfc)}.sm-bulkBar .sm-bulkSummary{grid-column:1/-1;color:var(--dsw-alias-label-primary,#111);font-size:12px;line-height:18px;font-weight:500;margin:0 0 2px}",
|
|
500
|
+
".sm-bulkSummary{color:var(--dsw-alias-label-primary,#111);font-size:12px;line-height:18px;font-weight:500}",
|
|
501
|
+
"",
|
|
502
|
+
".sm-bulkBar .sm-bulkBtn{cursor:pointer;color:var(--dsw-alias-label-secondary,#666);background:var(--dsw-alias-surface-l1,#fff);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;padding:4px 10px;font-size:12px;line-height:18px;display:inline-flex;align-items:center;justify-content:center;gap:4px;white-space:nowrap;text-align:center}",
|
|
503
|
+
".sm-bulkBar .sm-bulkBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary,#111)}",
|
|
504
|
+
".sm-bulkBar .sm-bulkBtn:disabled{opacity:.5;cursor:not-allowed}",
|
|
505
|
+
".sm-bulkBar .sm-bulkBtnDanger{color:var(--dsw-alias-state-error-primary);border-color:rgba(217,80,80,.4)}",
|
|
506
|
+
".sm-bulkBar .sm-bulkBtnDanger:hover:not(:disabled){color:#fff;background:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}",
|
|
507
|
+
"",
|
|
508
|
+
".sm-bulkRowCheckbox{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;flex:none}",
|
|
509
|
+
".sm-bulkRowCheckbox input{width:14px;height:14px;cursor:pointer;margin:0}",
|
|
510
|
+
".sm-bulkRowCheckbox input:disabled{cursor:not-allowed}",
|
|
511
|
+
".sm-rowBulkSelected{border-color:var(--dsw-alias-accent-primary,#356ae6);background:var(--dsw-alias-accent-primary-bg,rgba(53,106,230,.06))}",
|
|
512
|
+
".sm-bulkDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:360px;max-width:calc(100vw - 32px);max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px);z-index:10002}.sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;box-sizing:border-box;background:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-bulkDialog .sm-nativeDialogHeader{padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;display:flex;align-items:center;gap:8px}.sm-bulkDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;width:100%}.sm-bulkDialog .sm-nativeDialogBody{padding:12px 16px;display:flex;flex-direction:column;gap:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}.sm-bulkDialog .sm-nativeDialogFooter{padding:10px 16px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:6px}.sm-bulkDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary,#111);text-align:center}.sm-bulkDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-bulkDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-bulkDialog .sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:#fff;border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-bulkDialog .sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-bulkDialog .sm-nativeDialog select.sm-viewSelect{font-size:13px;line-height:20px;height:32px;padding:5px 9px}",
|
|
513
|
+
".sm-bulkDialog .sm-nativeDialog{width:100%;max-height:min(640px,calc(100vh - 32px))}",
|
|
514
|
+
".sm-bulkDialogBody{display:flex;flex-direction:column;gap:12px;min-height:0}",
|
|
515
|
+
".sm-bulkDialogSummary{font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}",
|
|
516
|
+
".sm-bulkDialogWarn{display:flex;align-items:flex-start;gap:8px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;background:rgba(217,80,80,.08);border:1px solid rgba(217,80,80,.25);border-radius:8px;padding:8px 10px}",
|
|
517
|
+
".sm-bulkSessionList{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow:auto;border:1px solid var(--dsw-alias-border-l2,#eee);border-radius:8px;padding:6px}",
|
|
518
|
+
".sm-bulkSessionItem{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
519
|
+
".sm-bulkProgressBar{position:relative;height:8px;background:var(--dsw-alias-fill-l2,#eee);border-radius:4px;overflow:hidden}",
|
|
520
|
+
".sm-bulkProgressFill{position:absolute;top:0;left:0;bottom:0;background:var(--dsw-alias-accent-primary,#356ae6);transition:width .2s ease}",
|
|
521
|
+
".sm-bulkResultGroup{display:flex;flex-direction:column;gap:6px}",
|
|
522
|
+
".sm-bulkResultHeader{font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-secondary,#666)}",
|
|
523
|
+
".sm-bulkResultList{display:flex;flex-direction:column;gap:2px;max-height:160px;overflow:auto;border:1px solid var(--dsw-alias-border-l2,#eee);border-radius:8px;padding:6px}",
|
|
524
|
+
".sm-bulkResultItem{display:flex;flex-direction:column;gap:2px;padding:4px 6px;font-size:12px;line-height:18px;border-radius:6px;cursor:default}",
|
|
525
|
+
".sm-bulkResultItem:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
526
|
+
".sm-bulkResultItemTitle{color:var(--dsw-alias-label-primary,#111);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
527
|
+
".sm-bulkResultItemReason{color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;word-break:break-word}",
|
|
528
|
+
".sm-bulkResultItemReasonSkip{color:var(--dsw-alias-label-tertiary,#888)}",
|
|
529
|
+
".sm-bulkResultFooter{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap}",
|
|
530
|
+
".sm-footerBtn-wide{flex:1 1 auto;min-width:0;min-height:36px;justify-content:flex-start;padding:0 10px;border-radius:12px;font-size:13px;line-height:20px;gap:8px}",
|
|
531
|
+
".sm-footerBtn-wide:hover,.sm-footerBtn-wide:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
|
|
532
|
+
".sm-footerBtn-rail{width:36px;height:36px;padding:0;justify-content:center;border-radius:12px;color:var(--dsw-alias-label-secondary)}",
|
|
533
|
+
".sm-footerBtn-rail:hover,.sm-footerBtn-rail:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
|
|
534
|
+
".sm-footerBtn-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%}",
|
|
535
|
+
".sm-footer-wide{display:flex;width:100%}",
|
|
536
|
+
".sm-footer-rail{display:flex;width:100%;justify-content:center}",
|
|
537
|
+
"@media(max-width:720px){.sm-panelSelects{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.sm-panelDialog .sm-row{flex-wrap:wrap}.sm-panelDialog .sm-rowMain{flex-basis:100%}.sm-panelDialog .sm-rowActions{width:100%;min-width:0;flex:1 1 100%;justify-content:flex-start}}@media(max-width:540px){.sm-panelSelects{grid-template-columns:minmax(0,1fr)}}",
|
|
376
538
|
].join("");
|
|
377
539
|
|
|
378
540
|
// Auto-follow DSH's theme. We detect DSH's current theme from
|
|
@@ -497,6 +659,35 @@ window.__ModuleLoader__.load({
|
|
|
497
659
|
const h = (type, props, ...children) => React.createElement(type, props, ...children);
|
|
498
660
|
const callApi = (path, payload) => fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload || {}) }).then((r) => r.json());
|
|
499
661
|
const fetchApi = (path, options) => fetch(API + path, options).then((r) => r.json());
|
|
662
|
+
// Run a bulk action across many sessionIds. /batch already does per-id
|
|
663
|
+
// error reporting, so partial failures come back as {summary, items}
|
|
664
|
+
// with per-id success/failed/skipped status. Cancellation is best-effort
|
|
665
|
+
// because the v0.1.7 host does not reliably support req.on("close").
|
|
666
|
+
async function runBatch(args, onProgress) {
|
|
667
|
+
const { action, sessionIds, payload } = args || {};
|
|
668
|
+
const controller = new AbortController();
|
|
669
|
+
try {
|
|
670
|
+
const response = await fetch(API + "/batch", {
|
|
671
|
+
method: "POST",
|
|
672
|
+
headers: { "content-type": "application/json" },
|
|
673
|
+
body: JSON.stringify({ action, sessionIds: sessionIds || [], payload: payload || {} }),
|
|
674
|
+
signal: controller.signal
|
|
675
|
+
});
|
|
676
|
+
const data = await response.json();
|
|
677
|
+
if (data && data.ok) {
|
|
678
|
+
const result = data.result || {};
|
|
679
|
+
const items = Array.isArray(result.items) ? result.items : [];
|
|
680
|
+
if (typeof onProgress === "function") onProgress({ done: items.length, total: (sessionIds || []).length, lastItem: items[items.length - 1] || null });
|
|
681
|
+
return { summary: result.summary || { total: items.length, success: 0, failed: 0, skipped: 0 }, items, aborted: !!result.aborted };
|
|
682
|
+
}
|
|
683
|
+
const error = new Error((data && data.error) || "batch failed");
|
|
684
|
+
error.code = (data && data.code) || "bad-request";
|
|
685
|
+
throw error;
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (error && error.name === "AbortError") return { summary: { total: 0, success: 0, failed: 0, skipped: 0 }, items: [], aborted: true };
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
500
691
|
|
|
501
692
|
const EMPTY_ANNOTATION = Object.freeze({ favorite: false, reviewLater: false, tags: Object.freeze([]), note: "", priority: 3, revision: 0 });
|
|
502
693
|
const validAnnotation = value => value && typeof value.favorite === "boolean" && typeof value.reviewLater === "boolean" && Array.isArray(value.tags) && value.tags.every(tag => typeof tag === "string") && typeof value.note === "string" && (value.priority === null || Number.isInteger(value.priority) && value.priority >= 1 && value.priority <= 5) && Number.isSafeInteger(value.revision) && value.revision >= 0;
|
|
@@ -710,7 +901,239 @@ window.__ModuleLoader__.load({
|
|
|
710
901
|
].join(String.fromCharCode(10))
|
|
711
902
|
};
|
|
712
903
|
const promptFor = lang => CLIPBOARD_PROMPT_TEXT[lang] || CLIPBOARD_PROMPT_TEXT.zh;
|
|
904
|
+
function BatchPreviewDialog(props) {
|
|
905
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
906
|
+
const open = props.open === true;
|
|
907
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
908
|
+
const onExecute = typeof props.onExecute === "function" ? props.onExecute : () => {};
|
|
909
|
+
const actionLabel = props.actionLabel || "";
|
|
910
|
+
const sessions = Array.isArray(props.sessions) ? props.sessions : [];
|
|
911
|
+
const warning = typeof props.warning === "string" ? props.warning : "";
|
|
912
|
+
const confirmLabel = props.confirmLabel || t("bulk.dialog.execute");
|
|
913
|
+
const cancelLabel = props.cancelLabel || t("bulk.dialog.cancel");
|
|
914
|
+
const danger = props.danger === true;
|
|
915
|
+
const [expanded, setExpanded] = useState(false);
|
|
916
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
917
|
+
const escStateRef = useRef({});
|
|
918
|
+
escStateRef.current = { onCancel };
|
|
919
|
+
useEffect(() => {
|
|
920
|
+
if (!open) return;
|
|
921
|
+
const handler = (e) => { if (e.key !== "Escape" || e.defaultPrevented) return; e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel(); };
|
|
922
|
+
window.addEventListener("keydown", handler);
|
|
923
|
+
return () => window.removeEventListener("keydown", handler);
|
|
924
|
+
}, [open]);
|
|
925
|
+
if (!open) return null;
|
|
926
|
+
const showExpand = sessions.length > 12;
|
|
927
|
+
const visible = expanded ? sessions : sessions.slice(0, 12);
|
|
928
|
+
return h("div", { className: "sm-nativeDialogLayer sm-bulkDialog", role: "presentation", style: { zIndex: zIndex || undefined } },
|
|
929
|
+
h("section", { className: "sm-nativeDialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-preview-title" },
|
|
930
|
+
h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-preview-title", className: "sm-nativeDialogTitle" }, t("bulk.dialog.title", { action: actionLabel }))),
|
|
931
|
+
h("div", { className: "sm-nativeDialogBody" },
|
|
932
|
+
h("div", { className: "sm-bulkDialogSummary" }, t("bulk.dialog.summary", { count: sessions.length, action: actionLabel })),
|
|
933
|
+
warning ? h("div", { className: "sm-bulkDialogWarn" }, warning) : null,
|
|
934
|
+
h("div", { className: "sm-bulkSessionList" },
|
|
935
|
+
visible.map((s, i) => h("div", { key: (s.id || i) + "-" + i, className: "sm-bulkSessionItem", title: s.id || "" }, s.displayTitle || s.id || "—"))
|
|
936
|
+
),
|
|
937
|
+
showExpand ? h("button", { type: "button", className: "sm-resetFilters", onClick: () => setExpanded(value => !value) }, expanded ? t("panel.reset") : "…(+" + (sessions.length - 12) + ")") : null
|
|
938
|
+
),
|
|
939
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
940
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", onClick: onCancel }, cancelLabel),
|
|
941
|
+
h("button", { type: "button", className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"), onClick: onExecute }, confirmLabel)
|
|
942
|
+
)
|
|
943
|
+
)
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function BatchProgressDialog(props) {
|
|
948
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
949
|
+
const open = props.open === true;
|
|
950
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
951
|
+
const actionLabel = props.actionLabel || "";
|
|
952
|
+
const done = Number.isInteger(props.done) ? props.done : 0;
|
|
953
|
+
const total = Number.isInteger(props.total) ? props.total : 0;
|
|
954
|
+
const completed = props.completed === true;
|
|
955
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
956
|
+
const cancelLabel = props.cancelLabel || t("bulk.progress.cancel");
|
|
957
|
+
if (!open) return null;
|
|
958
|
+
const pct = total > 0 ? Math.max(0, Math.min(100, Math.round((done / total) * 100))) : 0;
|
|
959
|
+
return h("div", { className: "sm-nativeDialogLayer sm-bulkDialog", role: "presentation", style: { zIndex: zIndex || undefined } },
|
|
960
|
+
h("section", { className: "sm-nativeDialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-progress-title" },
|
|
961
|
+
h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-progress-title", className: "sm-nativeDialogTitle" }, t("bulk.progress.title", { action: actionLabel }))),
|
|
962
|
+
h("div", { className: "sm-nativeDialogBody" },
|
|
963
|
+
h("div", { className: "sm-bulkDialogSummary" }, completed ? t("bulk.progress.completed", { done, total }) : t("bulk.progress.current", { done, total })),
|
|
964
|
+
h("div", { className: "sm-bulkProgressBar", role: "progressbar", "aria-valuemin": 0, "aria-valuemax": total, "aria-valuenow": done },
|
|
965
|
+
h("div", { className: "sm-bulkProgressFill", style: { width: pct + "%" } })
|
|
966
|
+
)
|
|
967
|
+
),
|
|
968
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
969
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", disabled: completed, onClick: onCancel }, cancelLabel)
|
|
970
|
+
)
|
|
971
|
+
)
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function BatchResultDialog(props) {
|
|
976
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
977
|
+
const open = props.open === true;
|
|
978
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
979
|
+
const actionLabel = props.actionLabel || "";
|
|
980
|
+
const items = Array.isArray(props.items) ? props.items : [];
|
|
981
|
+
const onClose = typeof props.onClose === "function" ? props.onClose : () => {};
|
|
982
|
+
const onRetry = typeof props.onRetry === "function" ? props.onRetry : () => {};
|
|
983
|
+
const closeLabel = props.closeLabel || t("bulk.result.close");
|
|
984
|
+
const retryLabel = props.retryLabel || t("bulk.result.retry");
|
|
985
|
+
const escStateRef = useRef({});
|
|
986
|
+
escStateRef.current = { onClose };
|
|
987
|
+
useEffect(() => {
|
|
988
|
+
if (!open) return;
|
|
989
|
+
const handler = (e) => { if (e.key !== "Escape" || e.defaultPrevented) return; e.preventDefault(); e.stopPropagation(); escStateRef.current.onClose(); };
|
|
990
|
+
window.addEventListener("keydown", handler);
|
|
991
|
+
return () => window.removeEventListener("keydown", handler);
|
|
992
|
+
}, [open]);
|
|
993
|
+
if (!open) return null;
|
|
994
|
+
const groups = { success: [], failed: [], skipped: [] };
|
|
995
|
+
for (const item of items) {
|
|
996
|
+
const status = item && item.status;
|
|
997
|
+
if (status === "success") groups.success.push(item);
|
|
998
|
+
else if (status === "skipped") groups.skipped.push(item);
|
|
999
|
+
else groups.failed.push(item);
|
|
1000
|
+
}
|
|
1001
|
+
const renderGroup = (labelKey, group, reasonClass) => {
|
|
1002
|
+
if (group.length === 0) return null;
|
|
1003
|
+
return h("div", { key: labelKey, className: "sm-bulkResultGroup" },
|
|
1004
|
+
h("div", { className: "sm-bulkResultHeader" }, t(labelKey, { count: group.length })),
|
|
1005
|
+
h("div", { className: "sm-bulkResultList" },
|
|
1006
|
+
group.map((item, i) => h("div", { key: (item.sessionId || i) + "-" + i, className: "sm-bulkResultItem", title: item.sessionId || "" },
|
|
1007
|
+
h("div", { className: "sm-bulkResultItemTitle" }, item.displayTitle || item.sessionId || "—"),
|
|
1008
|
+
reasonClass && (item.reason || item.error) ? h("div", { className: reasonClass }, item.reason || item.error) : null
|
|
1009
|
+
))
|
|
1010
|
+
)
|
|
1011
|
+
);
|
|
1012
|
+
};
|
|
1013
|
+
return h("div", { className: "sm-nativeDialogLayer sm-bulkDialog", role: "presentation", style: { zIndex: zIndex || undefined } },
|
|
1014
|
+
h("section", { className: "sm-nativeDialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-result-title" },
|
|
1015
|
+
h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-result-title", className: "sm-nativeDialogTitle" }, t("bulk.result.title", { action: actionLabel }))),
|
|
1016
|
+
h("div", { className: "sm-nativeDialogBody" },
|
|
1017
|
+
renderGroup("bulk.result.successGroup", groups.success),
|
|
1018
|
+
renderGroup("bulk.result.failedGroup", groups.failed, "sm-bulkResultItemReason"),
|
|
1019
|
+
renderGroup("bulk.result.skippedGroup", groups.skipped, "sm-bulkResultItemReasonSkip")
|
|
1020
|
+
),
|
|
1021
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
1022
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", onClick: onClose }, closeLabel),
|
|
1023
|
+
groups.failed.length > 0 ? h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogConfirm", onClick: () => onRetry(groups.failed.map(item => item.sessionId)) }, retryLabel) : null
|
|
1024
|
+
)
|
|
1025
|
+
)
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function BulkActionBar(props) {
|
|
1030
|
+
const t = props.t;
|
|
1031
|
+
const selectedCount = props.selectedCount || 0;
|
|
1032
|
+
const filteredCount = props.filteredCount || 0;
|
|
1033
|
+
const busy = props.busy === true;
|
|
1034
|
+
const onAction = props.onAction;
|
|
1035
|
+
const onOpenTagDialog = props.onOpenTagDialog;
|
|
1036
|
+
const onOpenPriorityDialog = props.onOpenPriorityDialog;
|
|
1037
|
+
const onOpenMoveDialog = props.onOpenMoveDialog;
|
|
1038
|
+
const onOpenPresetDialog = props.onOpenPresetDialog;
|
|
1039
|
+
const disableNonDestructive = selectedCount === 0 || busy;
|
|
1040
|
+
const destructiveBusy = selectedCount === 0 || busy;
|
|
1041
|
+
return h("div", { className: "sm-bulkBar", role: "toolbar", "aria-label": t("bulk.heading") },
|
|
1042
|
+
h("span", { className: "sm-bulkSummary" }, t("bulk.selectedFiltered", { selected: selectedCount, filtered: filteredCount })),
|
|
1043
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("archive") }, t("bulk.archive")),
|
|
1044
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("unarchive") }, t("bulk.unarchive")),
|
|
1045
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("favorite") }, t("bulk.favorite")),
|
|
1046
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("unfavorite") }, t("bulk.unfavorite")),
|
|
1047
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("review") }, t("bulk.review")),
|
|
1048
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("unreview") }, t("bulk.unreview")),
|
|
1049
|
+
// Row 2 of the 3x6 grid (添加标签 / 清空标签 / 设置优先级 / 移动至工作区 / 迁移预设 / 删除会话).
|
|
1050
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: onOpenTagDialog }, t("bulk.tagsAdd")),
|
|
1051
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: () => onAction("remove-tags", { tags: [] }) }, t("bulk.tagsRemove")),
|
|
1052
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: onOpenPriorityDialog }, t("bulk.priority")),
|
|
1053
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: onOpenMoveDialog }, t("bulk.moveBtn")),
|
|
1054
|
+
h("button", { type: "button", className: "sm-bulkBtn", disabled: disableNonDestructive, onClick: onOpenPresetDialog }, t("bulk.presetMigrate")),
|
|
1055
|
+
h("button", { type: "button", className: "sm-bulkBtn sm-bulkBtnDanger", disabled: destructiveBusy, onClick: () => onAction("delete", null, true) }, t("bulk.delete"))
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
713
1058
|
|
|
1059
|
+
function BulkTagInputDialog(props) {
|
|
1060
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
1061
|
+
const open = props.open === true;
|
|
1062
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
1063
|
+
const title = props.title || t("bulk.tagsAdd");
|
|
1064
|
+
const placeholder = props.placeholder || t("bulk.tags.placeholder");
|
|
1065
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
1066
|
+
const onApply = typeof props.onApply === "function" ? props.onApply : () => {};
|
|
1067
|
+
const confirmLabel = props.confirmLabel || t("bulk.confirm.tagsAdd");
|
|
1068
|
+
const [value, setValue] = useState("");
|
|
1069
|
+
useEffect(() => { if (open) setValue(""); }, [open]);
|
|
1070
|
+
const escStateRef = useRef({});
|
|
1071
|
+
escStateRef.current = { onCancel };
|
|
1072
|
+
useEffect(() => {
|
|
1073
|
+
if (!open) return;
|
|
1074
|
+
const handler = (e) => { if (e.key !== "Escape" || e.defaultPrevented) return; e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel(); };
|
|
1075
|
+
window.addEventListener("keydown", handler);
|
|
1076
|
+
return () => window.removeEventListener("keydown", handler);
|
|
1077
|
+
}, [open]);
|
|
1078
|
+
if (!open) return null;
|
|
1079
|
+
const submit = () => {
|
|
1080
|
+
const tags = (value || "").split(/[,,\n]/).map(s => s.trim()).filter(Boolean);
|
|
1081
|
+
onApply(tags);
|
|
1082
|
+
};
|
|
1083
|
+
return h("div", { className: "sm-nativeDialogLayer sm-bulkDialog", role: "presentation", style: { zIndex: zIndex || undefined } },
|
|
1084
|
+
h("section", { className: "sm-nativeDialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-tags-title" },
|
|
1085
|
+
h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-bulk-tags-title", className: "sm-nativeDialogTitle" }, title)),
|
|
1086
|
+
h("div", { className: "sm-nativeDialogBody" },
|
|
1087
|
+
h("textarea", { className: "sm-noteInput", "aria-label": placeholder, value, placeholder, rows: 3, onChange: (e) => setValue(e.target.value), autoFocus: true })
|
|
1088
|
+
),
|
|
1089
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
1090
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", onClick: onCancel }, t("bulk.dialog.cancel")),
|
|
1091
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogConfirm", onClick: submit }, confirmLabel)
|
|
1092
|
+
)
|
|
1093
|
+
)
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
function BulkChoiceDialog(props) {
|
|
1098
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
1099
|
+
const open = props.open === true;
|
|
1100
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
1101
|
+
const title = props.title || "";
|
|
1102
|
+
const description = props.description || "";
|
|
1103
|
+
const options = Array.isArray(props.options) ? props.options : [];
|
|
1104
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
1105
|
+
const onApply = typeof props.onApply === "function" ? props.onApply : () => {};
|
|
1106
|
+
const confirmLabel = props.confirmLabel || t("bulk.dialog.execute");
|
|
1107
|
+
const [value, setValue] = useState("");
|
|
1108
|
+
useEffect(() => { if (open) setValue(""); }, [open]);
|
|
1109
|
+
const escStateRef = useRef({});
|
|
1110
|
+
escStateRef.current = { onCancel };
|
|
1111
|
+
useEffect(() => {
|
|
1112
|
+
if (!open) return;
|
|
1113
|
+
const handler = (e) => { if (e.key !== "Escape" || e.defaultPrevented) return; e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel(); };
|
|
1114
|
+
window.addEventListener("keydown", handler);
|
|
1115
|
+
return () => window.removeEventListener("keydown", handler);
|
|
1116
|
+
}, [open]);
|
|
1117
|
+
if (!open) return null;
|
|
1118
|
+
return h("div", { className: "sm-nativeDialogLayer sm-bulkDialog", role: "presentation", style: { zIndex: zIndex || undefined } },
|
|
1119
|
+
h("section", { className: "sm-nativeDialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-choice-title" },
|
|
1120
|
+
h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-bulk-choice-title", className: "sm-nativeDialogTitle" }, title)),
|
|
1121
|
+
h("div", { className: "sm-nativeDialogBody" },
|
|
1122
|
+
description ? h("div", { className: "sm-bulkDialogSummary" }, description) : null,
|
|
1123
|
+
options.length === 0
|
|
1124
|
+
? h("div", { className: "sm-empty" }, t("migrate.noPresets"))
|
|
1125
|
+
: h("select", { className: "sm-viewSelect", value, onChange: (e) => setValue(e.target.value), "aria-label": title, autoFocus: true },
|
|
1126
|
+
h("option", { value: "" }, "—"),
|
|
1127
|
+
options.map((opt) => h("option", { key: opt.value, value: opt.value }, opt.label))
|
|
1128
|
+
)
|
|
1129
|
+
),
|
|
1130
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
1131
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", onClick: onCancel }, t("bulk.dialog.cancel")),
|
|
1132
|
+
h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogConfirm", disabled: value === "", onClick: () => onApply(value) }, confirmLabel)
|
|
1133
|
+
)
|
|
1134
|
+
)
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
714
1137
|
function AnnotationDialog({ sessionId, displayTitle, t, onClose, anchor }) {
|
|
715
1138
|
const annotations = useAnnotations();
|
|
716
1139
|
const latest = annotationOf(annotations, sessionId);
|
|
@@ -723,6 +1146,11 @@ window.__ModuleLoader__.load({
|
|
|
723
1146
|
const [busy, setBusy] = useState(false);
|
|
724
1147
|
const [error, setError] = useState(null);
|
|
725
1148
|
const dialogRef = useRef(null);
|
|
1149
|
+
// Claim a fresh z-index so this popup always sits above any earlier
|
|
1150
|
+
// popup the user may have opened (annotation popup typically opens on
|
|
1151
|
+
// top of a confirm/move/migrate dialog, so we need a guaranteed-
|
|
1152
|
+
// higher z-index than those static CSS values).
|
|
1153
|
+
const [zIndex] = useState(() => nextDialogZ());
|
|
726
1154
|
const dirty = favorite !== base.favorite || reviewLater !== base.reviewLater || tagsText !== base.tags.join(", ") || note !== base.note || priority !== String(base.priority ?? 3);
|
|
727
1155
|
const cancel = () => { if (busy || dirty && !window.confirm(t("marks.discard"))) return false; onClose(); return true; };
|
|
728
1156
|
const reload = () => {
|
|
@@ -817,9 +1245,10 @@ window.__ModuleLoader__.load({
|
|
|
817
1245
|
const surfaceClass = "sm-nativeDialog sm-panelDialog sm-annotationSurface" + (hasAnchor ? " sm-annotationSurface-anchored" : "");
|
|
818
1246
|
// Default position matches ConfirmDialog / MoveDialog so all inner
|
|
819
1247
|
// dialogs line up at top calc(50vh - 90px), left calc(50vw + 308px).
|
|
820
|
-
const layerStyle =
|
|
821
|
-
|
|
822
|
-
:
|
|
1248
|
+
const layerStyle = {
|
|
1249
|
+
zIndex: zIndex || undefined,
|
|
1250
|
+
...(hasAnchor ? { position: "fixed", top: anchor.top + "px", left: anchor.left + "px" } : {})
|
|
1251
|
+
};
|
|
823
1252
|
return h("div", { className: layerClass, style: layerStyle, role: "presentation" },
|
|
824
1253
|
h("section", { className: surfaceClass, role: "dialog", "aria-labelledby": "sm-annotation-title", ref: dialogRef, onKeyDown },
|
|
825
1254
|
h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-annotation-title", className: "sm-nativeDialogTitle" }, t("marks.title"))),
|
|
@@ -888,6 +1317,9 @@ window.__ModuleLoader__.load({
|
|
|
888
1317
|
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
889
1318
|
const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
|
|
890
1319
|
const busy = props.busy === true;
|
|
1320
|
+
const danger = props.danger === true;
|
|
1321
|
+
// Claim a fresh z-index so this popup sits above any earlier popup.
|
|
1322
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
891
1323
|
const title = props.title || "";
|
|
892
1324
|
const description = props.description || "";
|
|
893
1325
|
const warning = props.warning || "";
|
|
@@ -927,9 +1359,12 @@ window.__ModuleLoader__.load({
|
|
|
927
1359
|
|
|
928
1360
|
return h("div", {
|
|
929
1361
|
className: "sm-nativeDialogLayer sm-confirmDialog",
|
|
930
|
-
style:
|
|
931
|
-
|
|
932
|
-
|
|
1362
|
+
style: {
|
|
1363
|
+
zIndex: zIndex || undefined,
|
|
1364
|
+
...(props.anchor && typeof props.anchor.top === "number"
|
|
1365
|
+
? { position: "fixed", top: props.anchor.top + "px", left: props.anchor.left + "px" }
|
|
1366
|
+
: {})
|
|
1367
|
+
},
|
|
933
1368
|
role: "presentation"
|
|
934
1369
|
},
|
|
935
1370
|
|
|
@@ -955,7 +1390,7 @@ window.__ModuleLoader__.load({
|
|
|
955
1390
|
}, cancelLabel),
|
|
956
1391
|
h("button", {
|
|
957
1392
|
type: "button",
|
|
958
|
-
className: "sm-nativeDialogButton sm-nativeDialogConfirm",
|
|
1393
|
+
className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"),
|
|
959
1394
|
disabled: busy,
|
|
960
1395
|
onClick: () => { if (!busy) onConfirm(); }
|
|
961
1396
|
}, confirmLabel)
|
|
@@ -976,6 +1411,8 @@ window.__ModuleLoader__.load({
|
|
|
976
1411
|
const confirmLabel = props.confirmLabel || t("confirm.move.confirm");
|
|
977
1412
|
const cancelLabel = props.cancelLabel || t("confirm.cancel");
|
|
978
1413
|
const error = props.error || "";
|
|
1414
|
+
// Claim a fresh z-index so this popup sits above any earlier popup.
|
|
1415
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
979
1416
|
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState("");
|
|
980
1417
|
// Esc handled at window level (issue #7) -- Enter stays on the
|
|
981
1418
|
// layer because (a) it gates on selectedWorkspaceId which is set
|
|
@@ -1042,9 +1479,12 @@ window.__ModuleLoader__.load({
|
|
|
1042
1479
|
};
|
|
1043
1480
|
return h("div", {
|
|
1044
1481
|
className: "sm-nativeDialogLayer sm-confirmDialog",
|
|
1045
|
-
style:
|
|
1046
|
-
|
|
1047
|
-
|
|
1482
|
+
style: {
|
|
1483
|
+
zIndex: zIndex || undefined,
|
|
1484
|
+
...(props.anchor && typeof props.anchor.top === "number"
|
|
1485
|
+
? { position: "fixed", top: props.anchor.top + "px", left: props.anchor.left + "px" }
|
|
1486
|
+
: {})
|
|
1487
|
+
},
|
|
1048
1488
|
role: "presentation",
|
|
1049
1489
|
onKeyDown
|
|
1050
1490
|
},
|
|
@@ -1087,6 +1527,8 @@ window.__ModuleLoader__.load({
|
|
|
1087
1527
|
const fallbackPreset = props.currentPreset || "";
|
|
1088
1528
|
const [scan, setScan] = useState(null);
|
|
1089
1529
|
const [target, setTarget] = useState("");
|
|
1530
|
+
// Claim a fresh z-index so this popup sits above any earlier popup.
|
|
1531
|
+
const [zIndex] = useState(() => open ? nextDialogZ() : 0);
|
|
1090
1532
|
useEffect(() => {
|
|
1091
1533
|
if (!open || !sessionId) return;
|
|
1092
1534
|
let alive = true;
|
|
@@ -1171,7 +1613,8 @@ window.__ModuleLoader__.load({
|
|
|
1171
1613
|
);
|
|
1172
1614
|
return h("div", {
|
|
1173
1615
|
className: "sm-nativeDialogLayer sm-confirmDialog sm-migrateConfirmLayer",
|
|
1174
|
-
role: "presentation"
|
|
1616
|
+
role: "presentation",
|
|
1617
|
+
style: { zIndex: zIndex || undefined }
|
|
1175
1618
|
},
|
|
1176
1619
|
|
|
1177
1620
|
h("section", {
|
|
@@ -1300,6 +1743,193 @@ window.__ModuleLoader__.load({
|
|
|
1300
1743
|
const [migrating, setMigrating] = useState(false);
|
|
1301
1744
|
const [migrateFor, setMigrateFor] = useState(null);
|
|
1302
1745
|
const sessionMembershipKey = useMemo(() => [...list.ids].sort().join("\0"), [list.ids]);
|
|
1746
|
+
// ----- Bulk-action selection state -----
|
|
1747
|
+
const [selectedIds, setSelectedIds] = useState(() => new Set());
|
|
1748
|
+
const [lastClickedRank, setLastClickedRank] = useState(null);
|
|
1749
|
+
const [bulkPreview, setBulkPreview] = useState(null);
|
|
1750
|
+
const [bulkProgress, setBulkProgress] = useState(null);
|
|
1751
|
+
const [bulkResult, setBulkResult] = useState(null);
|
|
1752
|
+
const [bulkBusy, setBulkBusy] = useState(false);
|
|
1753
|
+
const [bulkTagDraft, setBulkTagDraft] = useState(null);
|
|
1754
|
+
// Select-mode is the user-controlled switch that reveals the row
|
|
1755
|
+
// checkboxes and the bulk action bar. It lives outside selectedIds
|
|
1756
|
+
// so the row checkboxes can be visible even before anything is
|
|
1757
|
+
// ticked (otherwise the BulkActionBar's own "select all" button
|
|
1758
|
+
// is unreachable).
|
|
1759
|
+
const [selectModeEnabled, setSelectModeEnabled] = useState(false);
|
|
1760
|
+
// Closing the panel resets everything bulk-related.
|
|
1761
|
+
useEffect(() => {
|
|
1762
|
+
if (!open) {
|
|
1763
|
+
setSelectedIds(new Set());
|
|
1764
|
+
setLastClickedRank(null);
|
|
1765
|
+
setBulkPreview(null);
|
|
1766
|
+
setBulkProgress(null);
|
|
1767
|
+
setBulkResult(null);
|
|
1768
|
+
setBulkTagDraft(null);
|
|
1769
|
+
setBulkPresetDraft(null);
|
|
1770
|
+
setBulkPresetOptions([]);
|
|
1771
|
+
setBulkBusy(false);
|
|
1772
|
+
setSelectModeEnabled(false);
|
|
1773
|
+
}
|
|
1774
|
+
}, [open]);
|
|
1775
|
+
const toggleSelectMode = () => {
|
|
1776
|
+
setSelectModeEnabled(value => {
|
|
1777
|
+
if (value) clearSelection();
|
|
1778
|
+
return !value;
|
|
1779
|
+
});
|
|
1780
|
+
};
|
|
1781
|
+
const toggleSelect = (id, shift, currentIndex) => {
|
|
1782
|
+
setSelectedIds(prev => {
|
|
1783
|
+
const next = new Set(prev);
|
|
1784
|
+
if (shift && lastClickedRank !== null && Number.isInteger(currentIndex)) {
|
|
1785
|
+
const lo = Math.min(lastClickedRank, currentIndex);
|
|
1786
|
+
const hi = Math.max(lastClickedRank, currentIndex);
|
|
1787
|
+
const allInRange = rows.slice(lo, hi + 1).every(r => next.has(r.id));
|
|
1788
|
+
for (let i = lo; i <= hi; i++) {
|
|
1789
|
+
const rowId = rows[i] && rows[i].id;
|
|
1790
|
+
if (!rowId) continue;
|
|
1791
|
+
if (allInRange) next.delete(rowId); else next.add(rowId);
|
|
1792
|
+
}
|
|
1793
|
+
} else {
|
|
1794
|
+
if (next.has(id)) next.delete(id); else next.add(id);
|
|
1795
|
+
}
|
|
1796
|
+
return next;
|
|
1797
|
+
});
|
|
1798
|
+
if (!shift) setLastClickedRank(Number.isInteger(currentIndex) ? currentIndex : null);
|
|
1799
|
+
};
|
|
1800
|
+
// Clear the selected-id set without touching selectModeEnabled.
|
|
1801
|
+
// Earlier this also reset selectModeEnabled to false, which made
|
|
1802
|
+
// clicking the "清空选择" toolbar button silently exit bulk
|
|
1803
|
+
// mode. Bulk mode is now exited exclusively by toggleSelectMode()
|
|
1804
|
+
// (the "退出批量处理" toggle button in the toolbar).
|
|
1805
|
+
const clearSelection = () => { setSelectedIds(new Set()); setLastClickedRank(null); };
|
|
1806
|
+
const selectAllFiltered = () => {
|
|
1807
|
+
if (rows.length > 200) { setError(t("bulk.error.tooMany")); return; }
|
|
1808
|
+
setSelectedIds(new Set(rows.map(r => r.id)));
|
|
1809
|
+
};
|
|
1810
|
+
const selectedSessions = useMemo(() => {
|
|
1811
|
+
const out = [];
|
|
1812
|
+
for (const id of selectedIds) {
|
|
1813
|
+
const s = list.byId[id];
|
|
1814
|
+
if (!s) continue;
|
|
1815
|
+
out.push({ id, displayTitle: s.blank ? t("panel.session.new") : (s.displayTitle || id) });
|
|
1816
|
+
}
|
|
1817
|
+
return out;
|
|
1818
|
+
}, [selectedIds, list.byId, t]);
|
|
1819
|
+
const executeBulk = async ({ action, payload, actionLabel, ids, sessions }) => {
|
|
1820
|
+
if (ids.length === 0) return;
|
|
1821
|
+
if (ids.length > 200) { setError(t("bulk.error.tooMany")); return; }
|
|
1822
|
+
const cancelController = { abort: () => {} };
|
|
1823
|
+
setBulkProgress({ action, actionLabel, done: 0, total: ids.length, completed: false, cancel: () => cancelController.abort() });
|
|
1824
|
+
setBulkBusy(true);
|
|
1825
|
+
let result;
|
|
1826
|
+
try {
|
|
1827
|
+
result = await runBatch({ action, sessionIds: ids, payload }, ({ done }) => {
|
|
1828
|
+
setBulkProgress(prev => prev ? { ...prev, done, completed: false } : prev);
|
|
1829
|
+
});
|
|
1830
|
+
} catch (error) {
|
|
1831
|
+
setBulkProgress(null);
|
|
1832
|
+
setBulkBusy(false);
|
|
1833
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
const items = (result.items || []).map(item => {
|
|
1837
|
+
const session = sessions.find(s => s.id === item.sessionId);
|
|
1838
|
+
return { ...item, displayTitle: session ? session.displayTitle : (item.sessionId || "—") };
|
|
1839
|
+
});
|
|
1840
|
+
setBulkProgress(null);
|
|
1841
|
+
setBulkResult({ action, actionLabel, items });
|
|
1842
|
+
setBulkBusy(false);
|
|
1843
|
+
try { await annotationStore.load(true); } catch (_) { /* ignore */ }
|
|
1844
|
+
if (typeof props.onRefreshAfterBulk === "function") {
|
|
1845
|
+
try { await props.onRefreshAfterBulk(action); } catch (_) { /* ignore */ }
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
const runBulk = ({ action, payload, actionLabel, needsConfirm }) => {
|
|
1849
|
+
if (bulkBusy) return;
|
|
1850
|
+
const ids = [...selectedIds];
|
|
1851
|
+
if (ids.length === 0) return;
|
|
1852
|
+
if (ids.length > 200) { setError(t("bulk.error.tooMany")); return; }
|
|
1853
|
+
const sessions = selectedSessions;
|
|
1854
|
+
const resolvedLabel = actionLabel || ({
|
|
1855
|
+
archive: t("bulk.archive"), unarchive: t("bulk.unarchive"), delete: t("bulk.delete"),
|
|
1856
|
+
move: t("bulk.move"), "preset-migrate": t("bulk.presetMigrate"),
|
|
1857
|
+
favorite: t("bulk.favorite"), unfavorite: t("bulk.unfavorite"),
|
|
1858
|
+
review: t("bulk.review"), unreview: t("bulk.unreview"),
|
|
1859
|
+
"add-tags": t("bulk.tagsAdd"), "remove-tags": t("bulk.tagsRemove"),
|
|
1860
|
+
"set-priority": t("bulk.priority")
|
|
1861
|
+
})[action] || action;
|
|
1862
|
+
if (needsConfirm) {
|
|
1863
|
+
setBulkPreview({ action, payload, actionLabel: resolvedLabel, sessions, warning: action === "delete" ? t("bulk.dialog.warning.delete") : "" });
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
void executeBulk({ action, payload, actionLabel: resolvedLabel, ids, sessions });
|
|
1867
|
+
};
|
|
1868
|
+
const retryFailedBulk = (failedIds, action, payload, actionLabel) => {
|
|
1869
|
+
setBulkResult(null);
|
|
1870
|
+
const filtered = (failedIds || []).filter(id => typeof id === "string" && id !== "");
|
|
1871
|
+
setSelectedIds(new Set(filtered));
|
|
1872
|
+
if (filtered.length === 0) return;
|
|
1873
|
+
const needsConfirm = action === "delete" || action === "move" || action === "preset-migrate" || action === "add-tags" || action === "remove-tags" || action === "set-priority";
|
|
1874
|
+
runBulk({ action, payload, actionLabel, needsConfirm });
|
|
1875
|
+
};
|
|
1876
|
+
const needsConfirmFor = (action) => action === "delete";
|
|
1877
|
+
const onBulkAction = (action, payload, forceNeedsConfirm) => {
|
|
1878
|
+
// Toggle: if a preview or result dialog is already open for this
|
|
1879
|
+
// same action, click the button again to close it.
|
|
1880
|
+
if (bulkPreview && bulkPreview.action === action) { setBulkPreview(null); return; }
|
|
1881
|
+
if (bulkResult && bulkResult.action === action) { setBulkResult(null); return; }
|
|
1882
|
+
const needsConfirm = forceNeedsConfirm === true || needsConfirmFor(action);
|
|
1883
|
+
runBulk({ action, payload, needsConfirm });
|
|
1884
|
+
};
|
|
1885
|
+
const onBulkSelectAll = () => { setSelectModeEnabled(true); selectAllFiltered(); };
|
|
1886
|
+
const onBulkClear = () => clearSelection();
|
|
1887
|
+
const onBulkTagsAdd = () => { if (bulkTagDraft && bulkTagDraft.mode === "add") { setBulkTagDraft(null); return; } setBulkTagDraft({ mode: "add" }); };
|
|
1888
|
+
const onBulkRemoveAllTags = () => onBulkAction("remove-tags", { tags: [] });
|
|
1889
|
+
const onBulkPriority = () => { if (bulkTagDraft && bulkTagDraft.mode === "priority") { setBulkTagDraft(null); return; } setBulkTagDraft({ mode: "priority" }); };
|
|
1890
|
+
const onBulkMove = () => { if (bulkTagDraft && bulkTagDraft.mode === "move") { setBulkTagDraft(null); return; } setBulkTagDraft({ mode: "move" }); };
|
|
1891
|
+
const onBulkTagApply = (tags) => {
|
|
1892
|
+
const mode = bulkTagDraft && bulkTagDraft.mode;
|
|
1893
|
+
setBulkTagDraft(null);
|
|
1894
|
+
if (mode === "add") onBulkAction("add-tags", { tags }, true);
|
|
1895
|
+
};
|
|
1896
|
+
const onBulkPriorityApply = (value) => {
|
|
1897
|
+
const priority = Number(value);
|
|
1898
|
+
setBulkTagDraft(null);
|
|
1899
|
+
if (Number.isInteger(priority) && priority >= 1 && priority <= 5) onBulkAction("set-priority", { priority }, true);
|
|
1900
|
+
};
|
|
1901
|
+
const onBulkMoveApply = (value) => {
|
|
1902
|
+
setBulkTagDraft(null);
|
|
1903
|
+
if (value) onBulkAction("move", { targetWorkspaceId: value }, true);
|
|
1904
|
+
};
|
|
1905
|
+
const [bulkPresetDraft, setBulkPresetDraft] = useState(null);
|
|
1906
|
+
const [bulkPresetOptions, setBulkPresetOptions] = useState([]);
|
|
1907
|
+
const onBulkPreset = () => {
|
|
1908
|
+
if (selectedIds.size === 0) return;
|
|
1909
|
+
// Toggle: close the dialog if it is already open.
|
|
1910
|
+
if (bulkPresetDraft) { setBulkPresetDraft(null); return; }
|
|
1911
|
+
|
|
1912
|
+
// Scan using the first selected session; /preset-scan returns
|
|
1913
|
+
// the global available preset list regardless of session, so any
|
|
1914
|
+
// selected id works.
|
|
1915
|
+
const firstId = [...selectedIds][0];
|
|
1916
|
+
setBulkPresetDraft({ mode: "preset" });
|
|
1917
|
+
setBulkPresetOptions([]);
|
|
1918
|
+
fetchApi("/preset-scan?sessionId=" + encodeURIComponent(firstId)).then((data) => {
|
|
1919
|
+
if (!data || !data.ok) return;
|
|
1920
|
+
const result = data.result || {};
|
|
1921
|
+
const presets = Array.isArray(result.availablePresets)
|
|
1922
|
+
? result.availablePresets
|
|
1923
|
+
: (result.rows && Array.isArray(result.rows.availablePresets) ? result.rows.availablePresets : []);
|
|
1924
|
+
setBulkPresetOptions(presets.filter(p => typeof p === "string" && p.length > 0).map(p => ({ value: p, label: p })));
|
|
1925
|
+
}).catch(() => { /* surface empty list */ });
|
|
1926
|
+
};
|
|
1927
|
+
const onBulkPresetApply = (value) => {
|
|
1928
|
+
setBulkPresetDraft(null);
|
|
1929
|
+
if (value) onBulkAction("preset-migrate", { toPreset: value }, true);
|
|
1930
|
+
};
|
|
1931
|
+
const bulkMoveOptions = useMemo(() => workspaces.map(ws => ({ value: ws.id, label: ws.title || ws.name || ws.id })), [workspaces]);
|
|
1932
|
+
const bulkPriorityOptions = useMemo(() => [1, 2, 3, 4, 5].map(value => ({ value: String(value), label: t("marks.priority." + value) })), [t]);
|
|
1303
1933
|
useEffect(() => {
|
|
1304
1934
|
const timer = setInterval(() => setNow(Date.now()), 30000);
|
|
1305
1935
|
return () => clearInterval(timer);
|
|
@@ -1382,6 +2012,25 @@ window.__ModuleLoader__.load({
|
|
|
1382
2012
|
.sort((a, b) => a.time === b.time ? a.rank - b.rank : a.time === null ? 1 : b.time === null ? -1 : direction * (a.time - b.time))
|
|
1383
2013
|
.map(item => item.session);
|
|
1384
2014
|
}, [allRows, archived, filter, query, workspaceFilter, workspaceIndex, sortOrder, createdTimes, annotations.entries, onlyFavorite, onlyReview, tagFilter, priorityFilter, t]);
|
|
2015
|
+
// The set of ids that survive the current filter; bulk-selection
|
|
2016
|
+
// bookkeeping reads from this so it stays in sync with row visibility.
|
|
2017
|
+
const currentFilterIds = useMemo(() => rows.map(r => r.id), [rows]);
|
|
2018
|
+
const currentFilterRank = useMemo(() => {
|
|
2019
|
+
const m = new Map();
|
|
2020
|
+
rows.forEach((r, i) => m.set(r.id, i));
|
|
2021
|
+
return m;
|
|
2022
|
+
}, [rows]);
|
|
2023
|
+
// Drop ids from the selection that no longer match the current filter
|
|
2024
|
+
// (e.g. user narrowed the workspace filter or hid archived rows).
|
|
2025
|
+
useEffect(() => {
|
|
2026
|
+
const allowed = new Set(currentFilterIds);
|
|
2027
|
+
setSelectedIds(prev => {
|
|
2028
|
+
let changed = false;
|
|
2029
|
+
const next = new Set();
|
|
2030
|
+
for (const id of prev) if (allowed.has(id)) next.add(id); else changed = true;
|
|
2031
|
+
return changed ? next : prev;
|
|
2032
|
+
});
|
|
2033
|
+
}, [currentFilterIds]);
|
|
1385
2034
|
const creationTimeUnavailable = sortOrder.startsWith("created-") && allRows.length > 0
|
|
1386
2035
|
&& !allRows.some(session => (sessionTime(session.createdAt) ?? sessionTime(createdTimes[session.id])) !== null);
|
|
1387
2036
|
const hasViewChanges = onlyFavorite || onlyReview || tagFilter !== "all" || priorityFilter !== "all" || query !== "" || filter !== "all" || workspaceFilter !== "all" || sortOrder !== DEFAULT_SORT;
|
|
@@ -1405,16 +2054,29 @@ window.__ModuleLoader__.load({
|
|
|
1405
2054
|
"aria-pressed": filter === id,
|
|
1406
2055
|
onClick: () => setFilter(id)
|
|
1407
2056
|
}, label);
|
|
1408
|
-
const renderRow = (s) => {
|
|
2057
|
+
const renderRow = (s, rank) => {
|
|
1409
2058
|
const isArchived = archived.has(s.id);
|
|
1410
2059
|
const isCurrent = s.id === list.current;
|
|
1411
2060
|
const busy = busyId === s.id;
|
|
1412
2061
|
const mark = annotationOf(annotations, s.id);
|
|
2062
|
+
const isSelected = selectedIds.has(s.id);
|
|
2063
|
+
const checkbox = (selectedIds.size === 0 && !selectModeEnabled)
|
|
2064
|
+
? null
|
|
2065
|
+
: h("label", { className: "sm-bulkRowCheckbox", onClick: (e) => e.stopPropagation() },
|
|
2066
|
+
h("input", {
|
|
2067
|
+
type: "checkbox",
|
|
2068
|
+
checked: isSelected,
|
|
2069
|
+
disabled: busy || bulkBusy,
|
|
2070
|
+
"aria-label": t("bulk.checkbox.aria", { title: s.displayTitle || s.id }),
|
|
2071
|
+
onChange: (e) => toggleSelect(s.id, e.shiftKey === true, rank)
|
|
2072
|
+
})
|
|
2073
|
+
);
|
|
1413
2074
|
return h("div", {
|
|
1414
2075
|
key: s.id,
|
|
1415
2076
|
"data-session-id": s.id,
|
|
1416
|
-
className: "sm-row" + (isCurrent ? " sm-rowCurrent" : "")
|
|
2077
|
+
className: "sm-row" + (isCurrent ? " sm-rowCurrent" : "") + (isSelected ? " sm-rowBulkSelected" : "")
|
|
1417
2078
|
},
|
|
2079
|
+
h("div", { className: "sm-rowCheckboxSlot" + ((selectedIds.size > 0 || selectModeEnabled) ? " sm-rowCheckboxSlot-visible" : "") }, checkbox),
|
|
1418
2080
|
h("div", { className: "sm-rowMain" },
|
|
1419
2081
|
h("div", { className: "sm-rowTitle" },
|
|
1420
2082
|
h("span", null, s.blank ? t("panel.session.new") : (s.displayTitle || s.id)),
|
|
@@ -1422,27 +2084,29 @@ window.__ModuleLoader__.load({
|
|
|
1422
2084
|
s.running ? h("span", { className: "sm-badge sm-badgeRunning" }, t("panel.running")) : null,
|
|
1423
2085
|
isCurrent ? h("span", { className: "sm-badge sm-badgeCurrent" }, t("panel.current")) : null
|
|
1424
2086
|
),
|
|
1425
|
-
|
|
1426
|
-
h("
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
2087
|
+
h("div", { className: "sm-rowSecondary" },
|
|
2088
|
+
h("div", { className: "sm-rowBadges" },
|
|
2089
|
+
h("span", { className: "sm-priorityBadge sm-priority-" + (mark.priority ?? 3), title: t("marks.priority." + (mark.priority ?? 3)) }, "P" + (mark.priority ?? 3)),
|
|
2090
|
+
mark.tags.slice(0, 2).map(tag => h("button", { key: tag, type: "button", className: "sm-tagChip", title: tag, onClick: () => setTagFilter("tag:" + tag.toLowerCase()) }, tag)),
|
|
2091
|
+
mark.tags.length > 2 ? h("span", { title: mark.tags.join(", ") }, "+" + (mark.tags.length - 2)) : null,
|
|
2092
|
+
mark.note ? h("button", { type: "button", className: "sm-noteBadge", title: t("marks.note.exists"), onClick: () => { setAnnotationAnchor(null); setAnnotationFor(current => current?.id === s.id ? null : s); } }, "✎") : null
|
|
2093
|
+
),
|
|
2094
|
+
h("div", { className: "sm-rowMeta" }, shortPath(s.cwd) + (s.cwd ? " · " : "") + formatRelative(s.updatedAt, now)),
|
|
2095
|
+
h("div", { className: "sm-rowActions" },
|
|
2096
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (isArchived) { /* DSH refuses to surface an archived session as the current view: clicking "Open" on an archived row flashes an empty chat panel and bounces back to "no session selected". Unarchive first, then open. (issue #17.4) */ runRow(s.id, async () => { await onUnarchive(s.id); await onOpen(s.id); }); } else { onOpen(s.id); } } }, t("row.open")),
|
|
2097
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => runRow(s.id, () => isArchived ? onUnarchive(s.id) : onArchive(s.id)) }, t(isArchived ? "row.unarchive" : "row.archive")),
|
|
2098
|
+
annotationButtons(s.id, s.displayTitle || s.id, t, annotations, "sm-rowBtn", patch => runRow(s.id, () => annotationStore.save(s.id, patch)), () => { setAnnotationAnchor(null); setAnnotationFor(current => current?.id === s.id ? null : s); }, busy),
|
|
2099
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (moveFor && moveFor.id === s.id) setMoveFor(null); else setMoveFor(s); } }, t("row.move")),
|
|
2100
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (migrateFor && migrateFor.id === s.id) setMigrateFor(null); else setMigrateFor(s); } }, t("row.migrate")),
|
|
2101
|
+
h("button", { type: "button", className: "sm-rowBtn sm-rowBtnDanger", disabled: busy, onClick: () => { if (confirmFor && confirmFor.id === s.id) setConfirmFor(null); else setConfirmFor(s); } }, t("row.delete"))
|
|
2102
|
+
)
|
|
2103
|
+
)
|
|
1440
2104
|
)
|
|
1441
2105
|
);
|
|
1442
2106
|
};
|
|
1443
2107
|
const rowsList = rows.length === 0
|
|
1444
2108
|
? h("div", { className: "sm-empty" }, allRows.length > 0 ? t("panel.empty.filtered") : filter === "archived" ? t("panel.empty.archived") : t("panel.empty"))
|
|
1445
|
-
: h("div", { className: "sm-list" }, rows.map(renderRow));
|
|
2109
|
+
: h("div", { className: "sm-list" }, rows.map((s, i) => renderRow(s, i)));
|
|
1446
2110
|
const onPanelClose = () => {
|
|
1447
2111
|
if (busyId !== null) return;
|
|
1448
2112
|
setConfirmFor(null);
|
|
@@ -1495,13 +2159,30 @@ window.__ModuleLoader__.load({
|
|
|
1495
2159
|
"aria-labelledby": "sm-panel-dialog-title"
|
|
1496
2160
|
},
|
|
1497
2161
|
h("div", { className: "sm-nativeDialogHeader" },
|
|
2162
|
+
h("a", {
|
|
2163
|
+
href: "https://github.com/hkkz9522/dsh-session-manager",
|
|
2164
|
+
target: "_blank",
|
|
2165
|
+
rel: "noopener noreferrer",
|
|
2166
|
+
className: "sm-panelHeaderGithub",
|
|
2167
|
+
title: t("panel.github.title"),
|
|
2168
|
+
"aria-label": t("panel.github.title")
|
|
2169
|
+
},
|
|
2170
|
+
h("svg", { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": "true" },
|
|
2171
|
+
h("path", {
|
|
2172
|
+
fill: "currentColor",
|
|
2173
|
+
d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
2174
|
+
})
|
|
2175
|
+
)
|
|
2176
|
+
),
|
|
1498
2177
|
h("h2", { id: "sm-panel-dialog-title", className: "sm-nativeDialogTitle" }, t("panel.title")),
|
|
1499
|
-
h("
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
2178
|
+
h("div", { className: "sm-nativeDialogHeaderActions" },
|
|
2179
|
+
h("button", {
|
|
2180
|
+
type: "button",
|
|
2181
|
+
className: "sm-panelHeaderClose",
|
|
2182
|
+
disabled: busyId !== null || migrating,
|
|
2183
|
+
onClick: onPanelClose
|
|
2184
|
+
}, t("panel.close"))
|
|
2185
|
+
)
|
|
1505
2186
|
),
|
|
1506
2187
|
h("div", { className: "sm-panel" },
|
|
1507
2188
|
h("div", { className: "sm-panelTools" },
|
|
@@ -1529,32 +2210,49 @@ window.__ModuleLoader__.load({
|
|
|
1529
2210
|
className: "sm-viewSelect", "aria-label": t("panel.sort"), value: sortOrder,
|
|
1530
2211
|
onChange: event => setSortOrder(event.target.value)
|
|
1531
2212
|
}, SORT_ORDERS.map(value => h("option", { key: value, value, disabled: value === "priority" && !marksReady }, t(value === "priority" ? "marks.sort" : "panel.sort." + value))))
|
|
2213
|
+
),
|
|
2214
|
+
h("label", { className: "sm-viewField" },
|
|
2215
|
+
h("span", null, t("marks.tags")),
|
|
2216
|
+
h("select", { className: "sm-viewSelect", "aria-label": t("marks.filter.tags"), value: tagFilter, disabled: !marksReady, onChange: e => setTagFilter(e.target.value) },
|
|
2217
|
+
h("option", { value: "all" }, t("marks.tags.all")), h("option", { value: "none" }, t("marks.tags.none")),
|
|
2218
|
+
availableTags.map(([key, tag]) => h("option", { key, value: "tag:" + key }, tag)),
|
|
2219
|
+
tagFilter.startsWith("tag:") && !availableTags.some(([key]) => "tag:" + key === tagFilter) ? h("option", { value: tagFilter }, t("marks.tags.missing", { tag: tagFilter.slice(4) })) : null
|
|
2220
|
+
)
|
|
2221
|
+
),
|
|
2222
|
+
h("label", { className: "sm-viewField" },
|
|
2223
|
+
h("span", null, t("marks.priority")),
|
|
2224
|
+
h("select", { className: "sm-viewSelect", "aria-label": t("marks.filter.priority"), value: priorityFilter, disabled: !marksReady, onChange: e => setPriorityFilter(e.target.value) },
|
|
2225
|
+
h("option", { value: "all" }, t("marks.priority.all")),
|
|
2226
|
+
[1,2,3,4,5].map(value => h("option", { key: value, value: String(value) }, t("marks.priority." + value)))
|
|
2227
|
+
)
|
|
1532
2228
|
)
|
|
1533
2229
|
),
|
|
1534
2230
|
h("div", { className: "sm-markFilters" },
|
|
1535
|
-
h("
|
|
1536
|
-
h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyReview ? " sm-on" : ""), "aria-label": t("marks.filter.review.aria"), "aria-pressed": onlyReview, disabled: !marksReady, onClick: () => setOnlyReview(value => !value) }, "◷ " + t("marks.filter.review")),
|
|
1537
|
-
h("label", { className: "sm-viewField" }, h("span", null, t("marks.tags")), h("select", { className: "sm-viewSelect", "aria-label": t("marks.filter.tags"), value: tagFilter, disabled: !marksReady, onChange: e => setTagFilter(e.target.value) },
|
|
1538
|
-
h("option", { value: "all" }, t("marks.tags.all")), h("option", { value: "none" }, t("marks.tags.none")),
|
|
1539
|
-
availableTags.map(([key, tag]) => h("option", { key, value: "tag:" + key }, tag)),
|
|
1540
|
-
tagFilter.startsWith("tag:") && !availableTags.some(([key]) => "tag:" + key === tagFilter) ? h("option", { value: tagFilter }, t("marks.tags.missing", { tag: tagFilter.slice(4) })) : null
|
|
1541
|
-
)),
|
|
1542
|
-
h("label", { className: "sm-viewField" }, h("span", null, t("marks.priority")), h("select", { className: "sm-viewSelect", "aria-label": t("marks.filter.priority"), value: priorityFilter, disabled: !marksReady, onChange: e => setPriorityFilter(e.target.value) },
|
|
1543
|
-
h("option", { value: "all" }, t("marks.priority.all")),
|
|
1544
|
-
[1,2,3,4,5].map(value => h("option", { key: value, value: String(value) }, t("marks.priority." + value)))
|
|
1545
|
-
))
|
|
1546
|
-
),
|
|
1547
|
-
h("div", { className: "sm-filterBar" },
|
|
1548
|
-
h("div", { className: "sm-filter" },
|
|
2231
|
+
h("div", { className: "sm-markFiltersGroup" },
|
|
1549
2232
|
filterBtn("all", t("panel.filter.all")),
|
|
1550
2233
|
filterBtn("active", t("panel.filter.active")),
|
|
1551
|
-
filterBtn("archived", t("panel.filter.archived"))
|
|
2234
|
+
filterBtn("archived", t("panel.filter.archived")),
|
|
2235
|
+
h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyFavorite ? " sm-on" : ""), "aria-label": t("marks.filter.favorite.aria"), "aria-pressed": onlyFavorite, disabled: !marksReady, onClick: () => setOnlyFavorite(value => !value) }, "★ " + t("marks.filter.favorite")),
|
|
2236
|
+
h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyReview ? " sm-on" : ""), "aria-label": t("marks.filter.review.aria"), "aria-pressed": onlyReview, disabled: !marksReady, onClick: () => setOnlyReview(value => !value) }, "◷ " + t("marks.filter.review"))
|
|
1552
2237
|
),
|
|
1553
|
-
h("div", { className: "sm-
|
|
1554
|
-
|
|
1555
|
-
|
|
2238
|
+
h("div", { className: "sm-markFiltersSpacer" }),
|
|
2239
|
+
h("div", { className: "sm-markFiltersGroup" },
|
|
2240
|
+
h("button", {
|
|
2241
|
+
type: "button",
|
|
2242
|
+
className: "sm-filterBtn" + (selectModeEnabled ? " sm-on" : ""),
|
|
2243
|
+
disabled: busyId !== null || migrating,
|
|
2244
|
+
"aria-pressed": selectModeEnabled,
|
|
2245
|
+
"aria-label": t(selectModeEnabled ? "panel.selectMode.active.aria" : "panel.selectMode.aria"),
|
|
2246
|
+
onClick: toggleSelectMode
|
|
2247
|
+
}, selectModeEnabled ? t("panel.selectMode.exit") : t("panel.selectMode")),
|
|
2248
|
+
h("button", { type: "button", className: "sm-filterBtn sm-bulkSelectBtn", disabled: rows.length === 0 || rows.length === selectedIds.size || bulkBusy, onClick: onBulkSelectAll, "aria-label": t("bulk.selectAllFiltered") }, t("bulk.selectAllFiltered")),
|
|
2249
|
+
h("button", { type: "button", className: "sm-filterBtn sm-bulkSelectBtn", disabled: selectedIds.size === 0 || bulkBusy, onClick: onBulkClear, "aria-label": t("bulk.clear") }, t("bulk.clear"))
|
|
1556
2250
|
)
|
|
1557
2251
|
),
|
|
2252
|
+
h("div", { className: "sm-resultSummary" },
|
|
2253
|
+
h("span", { role: "status", "aria-live": "polite", "aria-atomic": "true" }, t("panel.count", { shown: rows.length, total: allRows.length })),
|
|
2254
|
+
hasViewChanges ? h("button", { type: "button", className: "sm-resetFilters", onClick: resetView }, t("panel.reset")) : null
|
|
2255
|
+
),
|
|
1558
2256
|
annotations.loading && !annotations.loaded ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("marks.loading")) : null,
|
|
1559
2257
|
annotations.error ? h("div", { className: "sm-workspaceNotice", role: "alert" }, t("marks.error", { message: annotations.error }), h("button", { type: "button", className: "sm-resetFilters", onClick: () => annotationStore.load(true) }, t("marks.retry"))) : null,
|
|
1560
2258
|
creationTimeUnavailable && !workspacesLoading ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("panel.sort.unavailable")) : null,
|
|
@@ -1565,6 +2263,15 @@ window.__ModuleLoader__.load({
|
|
|
1565
2263
|
) : null
|
|
1566
2264
|
),
|
|
1567
2265
|
error ? h("div", { className: "sm-error", role: "alert" }, t("error.operation", { message: error })) : null,
|
|
2266
|
+
(selectedIds.size > 0 || selectModeEnabled) ? h(BulkActionBar, {
|
|
2267
|
+
t, selectedCount: selectedIds.size, filteredCount: rows.length, busy: bulkBusy,
|
|
2268
|
+
onSelectAll: onBulkSelectAll, onClear: onBulkClear,
|
|
2269
|
+
onAction: onBulkAction,
|
|
2270
|
+
onOpenTagDialog: onBulkTagsAdd,
|
|
2271
|
+
onOpenPriorityDialog: onBulkPriority,
|
|
2272
|
+
onOpenMoveDialog: onBulkMove,
|
|
2273
|
+
onOpenPresetDialog: onBulkPreset
|
|
2274
|
+
}) : null,
|
|
1568
2275
|
rowsList
|
|
1569
2276
|
)
|
|
1570
2277
|
)
|
|
@@ -1574,11 +2281,12 @@ window.__ModuleLoader__.load({
|
|
|
1574
2281
|
open: true,
|
|
1575
2282
|
onClose: () => { if (busyId === null) setConfirmFor(null); },
|
|
1576
2283
|
title: t("confirm.delete.title"),
|
|
1577
|
-
description: t("confirm.delete.desc", { title: confirmFor.displayTitle ||
|
|
2284
|
+
description: (confirmFor.displayTitle && t("confirm.delete.desc", { title: confirmFor.displayTitle })) || "",
|
|
1578
2285
|
warning: confirmFor.running ? t("confirm.delete.running") : null,
|
|
1579
2286
|
confirmLabel: t("confirm.delete.confirm"),
|
|
1580
2287
|
cancelLabel: t("confirm.cancel"),
|
|
1581
2288
|
busy: busyId !== null,
|
|
2289
|
+
danger: true,
|
|
1582
2290
|
onCancel: () => { if (busyId === null) setConfirmFor(null); },
|
|
1583
2291
|
onConfirm: () => {
|
|
1584
2292
|
const target = confirmFor;
|
|
@@ -1589,9 +2297,12 @@ window.__ModuleLoader__.load({
|
|
|
1589
2297
|
const moveDialog = moveFor === null ? null : h(MoveDialog, {
|
|
1590
2298
|
open: true,
|
|
1591
2299
|
onClose: () => { if (busyId === null) setMoveFor(null); },
|
|
2300
|
+
/* Forward the locale function so the inner labels (e.g. confirm.move.select, confirm.move.empty) get translated; without this they render as raw keys. (issue #17.2) */
|
|
2301
|
+
t: t,
|
|
1592
2302
|
title: t("confirm.move.title"),
|
|
1593
|
-
|
|
1594
|
-
|
|
2303
|
+
/* Show the real session display title only (no raw id); falls back to empty so the description
|
|
2304
|
+
stays blank when the name is unknown instead of leaking the id into the dialog body. (issue #17.2 follow-up) */
|
|
2305
|
+
description: (moveFor.displayTitle && t("confirm.move.desc", { title: moveFor.displayTitle })) || "",
|
|
1595
2306
|
currentWorkspaceId: moveFor.workspaceId,
|
|
1596
2307
|
workspaces: workspaces,
|
|
1597
2308
|
confirmLabel: t("confirm.move.confirm"),
|
|
@@ -1639,13 +2350,115 @@ window.__ModuleLoader__.load({
|
|
|
1639
2350
|
// panel area entirely (the inner modal is centered).
|
|
1640
2351
|
const annotationDialog = annotationFor ? h(AnnotationDialog, { key: annotationFor.id, sessionId: annotationFor.id, displayTitle: annotationFor.displayTitle || annotationFor.id, t, anchor: annotationAnchor, onClose: () => { setAnnotationFor(null); setAnnotationAnchor(null); } }) : null;
|
|
1641
2352
|
// Render panel + ALL inner dialogs together; z-index ordering puts
|
|
1642
|
-
// whichever dialog is open above the panel.
|
|
1643
|
-
|
|
2353
|
+
// whichever dialog is open above the panel. The bulk dialogs are
|
|
2354
|
+
// appended here so destructive actions get a preview, in-flight
|
|
2355
|
+
// operations get a progress bar, and finished runs show per-id
|
|
2356
|
+
// success / failed / skipped counts.
|
|
2357
|
+
// Map bulk action -> 4-char confirm label ("确认<verb>"). Delete is
|
|
2358
|
+
// the only destructive action routed through BatchPreviewDialog, so it
|
|
2359
|
+
// also gets danger: true so the confirm button paints red.
|
|
2360
|
+
const bulkConfirmLabelFor = (action) => ({
|
|
2361
|
+
archive: t("bulk.confirm.archive"),
|
|
2362
|
+
unarchive: t("bulk.confirm.unarchive"),
|
|
2363
|
+
favorite: t("bulk.confirm.favorite"),
|
|
2364
|
+
unfavorite: t("bulk.confirm.unfavorite"),
|
|
2365
|
+
review: t("bulk.confirm.review"),
|
|
2366
|
+
unreview: t("bulk.confirm.unreview"),
|
|
2367
|
+
"add-tags": t("bulk.confirm.tagsAdd"),
|
|
2368
|
+
"remove-tags": t("bulk.confirm.tagsRemove"),
|
|
2369
|
+
"set-priority": t("bulk.confirm.priority"),
|
|
2370
|
+
move: t("bulk.confirm.move"),
|
|
2371
|
+
"preset-migrate": t("bulk.confirm.presetMigrate"),
|
|
2372
|
+
delete: t("bulk.confirm.delete")
|
|
2373
|
+
})[action] || t("bulk.dialog.execute");
|
|
2374
|
+
const bulkPreviewDialog = bulkPreview ? h(BatchPreviewDialog, {
|
|
2375
|
+
open: true, t, actionLabel: bulkPreview.actionLabel,
|
|
2376
|
+
sessions: bulkPreview.sessions, warning: bulkPreview.warning,
|
|
2377
|
+
confirmLabel: bulkConfirmLabelFor(bulkPreview.action),
|
|
2378
|
+
danger: bulkPreview.action === "delete",
|
|
2379
|
+
onCancel: () => setBulkPreview(null),
|
|
2380
|
+
onExecute: () => {
|
|
2381
|
+
const preview = bulkPreview;
|
|
2382
|
+
setBulkPreview(null);
|
|
2383
|
+
void executeBulk({
|
|
2384
|
+
action: preview.action, payload: preview.payload,
|
|
2385
|
+
actionLabel: preview.actionLabel,
|
|
2386
|
+
ids: preview.sessions.map(s => s.id), sessions: preview.sessions
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
}) : null;
|
|
2390
|
+
const bulkProgressDialog = bulkProgress ? h(BatchProgressDialog, {
|
|
2391
|
+
open: true, t, actionLabel: bulkProgress.actionLabel,
|
|
2392
|
+
done: bulkProgress.done, total: bulkProgress.total, completed: false,
|
|
2393
|
+
onCancel: () => { try { bulkProgress.cancel(); } catch (_) {} }
|
|
2394
|
+
}) : null;
|
|
2395
|
+
const bulkResultDialog = bulkResult ? h(BatchResultDialog, {
|
|
2396
|
+
open: true, t, actionLabel: bulkResult.actionLabel,
|
|
2397
|
+
items: bulkResult.items,
|
|
2398
|
+
onClose: () => { setBulkResult(null); clearSelection(); },
|
|
2399
|
+
onRetry: (failedIds) => {
|
|
2400
|
+
const last = bulkResult;
|
|
2401
|
+
setBulkResult(null);
|
|
2402
|
+
retryFailedBulk(failedIds, last.action, last.payload, last.actionLabel);
|
|
2403
|
+
}
|
|
2404
|
+
}) : null;
|
|
2405
|
+
const bulkTagDialog = bulkTagDraft && bulkTagDraft.mode === "add" ? h(BulkTagInputDialog, {
|
|
2406
|
+
open: true, t, title: t("bulk.tagsAdd"),
|
|
2407
|
+
confirmLabel: t("bulk.confirm.tagsAdd"),
|
|
2408
|
+
onCancel: () => setBulkTagDraft(null),
|
|
2409
|
+
onApply: onBulkTagApply
|
|
2410
|
+
}) : null;
|
|
2411
|
+
const bulkPriorityDialog = bulkTagDraft && bulkTagDraft.mode === "priority" ? h(BulkChoiceDialog, {
|
|
2412
|
+
open: true, t, title: t("bulk.priority"),
|
|
2413
|
+
description: t("bulk.dialog.summary", { count: selectedIds.size, action: t("bulk.priority") }),
|
|
2414
|
+
options: bulkPriorityOptions,
|
|
2415
|
+
confirmLabel: t("bulk.confirm.priority"),
|
|
2416
|
+
onCancel: () => setBulkTagDraft(null),
|
|
2417
|
+
onApply: onBulkPriorityApply
|
|
2418
|
+
}) : null;
|
|
2419
|
+
const bulkMoveDialog = bulkTagDraft && bulkTagDraft.mode === "move" ? h(BulkChoiceDialog, {
|
|
2420
|
+
open: true, t, title: t("bulk.move"),
|
|
2421
|
+
description: t("bulk.dialog.summary", { count: selectedIds.size, action: t("bulk.move") }),
|
|
2422
|
+
options: bulkMoveOptions,
|
|
2423
|
+
confirmLabel: t("bulk.confirm.move"),
|
|
2424
|
+
onCancel: () => setBulkTagDraft(null),
|
|
2425
|
+
onApply: onBulkMoveApply
|
|
2426
|
+
}) : null;
|
|
2427
|
+
const bulkPresetDialog = bulkPresetDraft ? h(BulkChoiceDialog, {
|
|
2428
|
+
open: true, t, title: t("bulk.presetMigrate"),
|
|
2429
|
+
description: t("bulk.dialog.summary", { count: selectedIds.size, action: t("bulk.presetMigrate") }),
|
|
2430
|
+
options: bulkPresetOptions,
|
|
2431
|
+
confirmLabel: t("bulk.confirm.presetMigrate"),
|
|
2432
|
+
onCancel: () => setBulkPresetDraft(null),
|
|
2433
|
+
onApply: onBulkPresetApply
|
|
2434
|
+
}) : null;
|
|
2435
|
+
// Bulk dialogs sit at z-index 10002 (set in CSS) so they overlap the
|
|
2436
|
+
return h(React.Fragment, null, panelModal, annotationDialog, moveDialog, deleteDialog, migrateDialog, bulkPreviewDialog, bulkProgressDialog, bulkResultDialog, bulkTagDialog, bulkPriorityDialog, bulkMoveDialog, bulkPresetDialog);
|
|
1644
2437
|
}
|
|
1645
2438
|
|
|
1646
2439
|
function HeaderAction(props) {
|
|
1647
2440
|
const t = props.t;
|
|
1648
2441
|
const sessionId = props.sessionId;
|
|
2442
|
+
const useSessions = props.useSessions;
|
|
2443
|
+
// Resolve the friendly display title once per render. Earlier this
|
|
2444
|
+
// lived inside a realDisplayTitle() helper that was called from the
|
|
2445
|
+
// setMoveFor / setConfirmFor / setAnnotationFor onClick handlers,
|
|
2446
|
+
// which invoked useSessions() outside React's render phase. The
|
|
2447
|
+
// slot framework's useSyncExternalStore subscriber reads .current off
|
|
2448
|
+
// the active fiber and that handle goes null during an event
|
|
2449
|
+
// dispatch, so the hook threw "Cannot read properties of null
|
|
2450
|
+
// (reading 'current')". The surrounding SlotErrorBoundary then
|
|
2451
|
+
// replaced the whole conversation.session.header.actions slot with
|
|
2452
|
+
// <div data-slot-error="…" />, taking every title-bar button down
|
|
2453
|
+
// and leaving 移动 / 删除 with no click target. Compute the value
|
|
2454
|
+
// here once per render, then reuse the captured const in handlers.
|
|
2455
|
+
const displayTitle = (() => {
|
|
2456
|
+
const fallback = props.displayTitle;
|
|
2457
|
+
if (fallback && fallback !== sessionId) return fallback;
|
|
2458
|
+
const list = useSessions ? useSessions((s) => s) : null;
|
|
2459
|
+
const found = list ? list.byId[sessionId] : null;
|
|
2460
|
+
return (found && found.displayTitle) || "";
|
|
2461
|
+
})();
|
|
1649
2462
|
const annotations = useAnnotations();
|
|
1650
2463
|
const [annotationFor, setAnnotationFor] = useState(null);
|
|
1651
2464
|
const [annotationAnchor, setAnnotationAnchor] = useState(null);
|
|
@@ -1682,12 +2495,15 @@ window.__ModuleLoader__.load({
|
|
|
1682
2495
|
});
|
|
1683
2496
|
return () => { alive = false; };
|
|
1684
2497
|
}, [moveFor ? (moveFor.id || "x") : null]);
|
|
2498
|
+
// All three HeaderAction dialogs (annotation / move / delete)
|
|
2499
|
+
// share a 360px width. Anchor the dialog's right edge to the
|
|
2500
|
+
// trigger button's right edge, with 8px screen-edge padding.
|
|
1685
2501
|
const anchorFor = (el) => {
|
|
1686
2502
|
if (!el || typeof el.getBoundingClientRect !== "function") return null;
|
|
1687
2503
|
const r = el.getBoundingClientRect();
|
|
1688
2504
|
return {
|
|
1689
2505
|
top: Math.round(r.top + r.height + 6),
|
|
1690
|
-
left: Math.max(8, Math.round(r.right -
|
|
2506
|
+
left: Math.max(8, Math.round(r.right - 360))
|
|
1691
2507
|
};
|
|
1692
2508
|
};
|
|
1693
2509
|
const archiveLabelKey = isArchived ? "row.unarchive" : "row.archive";
|
|
@@ -1705,49 +2521,56 @@ window.__ModuleLoader__.load({
|
|
|
1705
2521
|
|
|
1706
2522
|
annotationButtons(sessionId, props.displayTitle || sessionId, t, annotations, "sm-headerBtn", patch => runWithAlert("annotations", () => annotationStore.save(sessionId, patch)), (e) => {
|
|
1707
2523
|
const nextOpen = !(annotationFor && annotationFor.id === sessionId);
|
|
1708
|
-
|
|
2524
|
+
// Do NOT clear moveFor / confirmFor here: the z-index counter
|
|
2525
|
+
// (see nextDialogZ) decides stacking, so opening the annotation
|
|
2526
|
+
// popup must not silently dismiss a previously opened move or
|
|
2527
|
+
// delete popup on the same session.
|
|
1709
2528
|
if (nextOpen && e && e.currentTarget) {
|
|
1710
2529
|
const r = e.currentTarget.getBoundingClientRect();
|
|
1711
|
-
setAnnotationAnchor({ top: Math.round(r.top + r.height + 6), left: Math.max(8, Math.round(r.right -
|
|
2530
|
+
setAnnotationAnchor({ top: Math.round(r.top + r.height + 6), left: Math.max(8, Math.round(r.right - 360)) });
|
|
1712
2531
|
} else setAnnotationAnchor(null);
|
|
1713
|
-
setAnnotationFor(current => current?.id === sessionId ? null : { id: sessionId, displayTitle
|
|
2532
|
+
setAnnotationFor(current => current?.id === sessionId ? null : { id: sessionId, displayTitle });
|
|
1714
2533
|
}, false, annotationFor?.id === sessionId),
|
|
1715
2534
|
annotations.error ? h("button", { type: "button", className: "sm-headerBtn", title: t("marks.error", { message: annotations.error }), "aria-label": t("marks.retry"), onClick: () => annotationStore.load(true) }, "!") : null,
|
|
1716
2535
|
|
|
1717
2536
|
h("button", {
|
|
1718
2537
|
type: "button",
|
|
1719
2538
|
className: "sm-headerBtn" + (moveFor ? " sm-headerBtnActive" : ""),
|
|
1720
|
-
"aria-label": t ? t("row.move") : "
|
|
2539
|
+
"aria-label": t ? t("row.move") : "移动至工作区",
|
|
1721
2540
|
onClick: (e) => {
|
|
1722
2541
|
e.stopPropagation();
|
|
1723
|
-
|
|
1724
|
-
|
|
2542
|
+
// Do NOT clear confirmFor / confirmAnchor here: opening the
|
|
2543
|
+
// move popup must not silently dismiss a previously opened
|
|
2544
|
+
// delete popup on the same session. The z-index counter
|
|
2545
|
+
// (see nextDialogZ) handles stacking.
|
|
1725
2546
|
if (moveFor && moveFor.id === sessionId) {
|
|
1726
2547
|
setMoveFor(null);
|
|
1727
2548
|
setMoveAnchor(null);
|
|
1728
2549
|
} else {
|
|
1729
2550
|
setMoveAnchor(anchorFor(e.currentTarget));
|
|
1730
|
-
setMoveFor({ id: sessionId, displayTitle
|
|
2551
|
+
setMoveFor({ id: sessionId, displayTitle, workspaceId: "" });
|
|
1731
2552
|
}
|
|
1732
2553
|
}
|
|
1733
|
-
}, t ? t("row.move") : "
|
|
2554
|
+
}, t ? t("row.move") : "移动至工作区"),
|
|
1734
2555
|
h("button", {
|
|
1735
2556
|
type: "button",
|
|
1736
2557
|
className: "sm-headerBtn sm-headerBtnDanger" + (confirmFor ? " sm-headerBtnActive" : ""),
|
|
1737
2558
|
"aria-label": t ? t("row.delete") : "删除会话",
|
|
1738
2559
|
onClick: (e) => {
|
|
1739
2560
|
e.stopPropagation();
|
|
1740
|
-
|
|
1741
|
-
|
|
2561
|
+
// Do NOT clear moveFor / moveAnchor here: opening the delete
|
|
2562
|
+
// popup must not silently dismiss a previously opened move
|
|
2563
|
+
// popup on the same session. The z-index counter (see
|
|
2564
|
+
// nextDialogZ) handles stacking.
|
|
1742
2565
|
if (confirmFor && confirmFor.id === sessionId) {
|
|
1743
2566
|
setConfirmFor(null);
|
|
1744
2567
|
setConfirmAnchor(null);
|
|
1745
2568
|
} else {
|
|
1746
2569
|
setConfirmAnchor(anchorFor(e.currentTarget));
|
|
1747
|
-
setConfirmFor({ id: sessionId, displayTitle
|
|
2570
|
+
setConfirmFor({ id: sessionId, displayTitle });
|
|
1748
2571
|
}
|
|
1749
2572
|
}
|
|
1750
|
-
}, t ? t("row.delete") : "
|
|
2573
|
+
}, t ? t("row.delete") : "删除会话")
|
|
1751
2574
|
),
|
|
1752
2575
|
confirmFor === null ? null : h(ConfirmDialog, {
|
|
1753
2576
|
open: true,
|
|
@@ -1760,10 +2583,11 @@ window.__ModuleLoader__.load({
|
|
|
1760
2583
|
runWithAlert("delete", () => onRemove(target.id));
|
|
1761
2584
|
},
|
|
1762
2585
|
title: t ? t("confirm.delete.title") : "删除会话",
|
|
1763
|
-
description: (confirmFor && (t ? t("confirm.delete.desc", { title: confirmFor.displayTitle
|
|
2586
|
+
description: (confirmFor && confirmFor.displayTitle && (t ? t("confirm.delete.desc", { title: confirmFor.displayTitle }) : ("将删除 " + confirmFor.displayTitle))) || "",
|
|
1764
2587
|
confirmLabel: t ? t("confirm.delete.confirm") : "确认删除",
|
|
1765
2588
|
cancelLabel: t ? t("confirm.cancel") : "取消",
|
|
1766
|
-
busy: false
|
|
2589
|
+
busy: false,
|
|
2590
|
+
danger: true
|
|
1767
2591
|
}),
|
|
1768
2592
|
moveFor === null ? null : h(MoveDialog, {
|
|
1769
2593
|
open: true,
|
|
@@ -1774,8 +2598,7 @@ window.__ModuleLoader__.load({
|
|
|
1774
2598
|
onClose: () => { setMoveFor(null); setMoveAnchor(null); },
|
|
1775
2599
|
onCancel: () => { setMoveFor(null); setMoveAnchor(null); },
|
|
1776
2600
|
title: t ? t("confirm.move.title") : "移动会话到工作区",
|
|
1777
|
-
description: (moveFor && (t ? t("confirm.move.desc", { title: moveFor.displayTitle
|
|
1778
|
-
sessionTitle: moveFor.displayTitle || moveFor.id,
|
|
2601
|
+
description: (moveFor && moveFor.displayTitle && (t ? t("confirm.move.desc", { title: moveFor.displayTitle }) : "")) || "",
|
|
1779
2602
|
confirmLabel: t ? t("confirm.move.confirm") : "确认移动",
|
|
1780
2603
|
cancelLabel: t ? t("confirm.cancel") : "取消",
|
|
1781
2604
|
busy: false,
|
|
@@ -1801,6 +2624,12 @@ window.__ModuleLoader__.load({
|
|
|
1801
2624
|
useSessions: this.props.useSessions,
|
|
1802
2625
|
useWorkspaces: this.props.useWorkspaces,
|
|
1803
2626
|
t: this.props.t,
|
|
2627
|
+
// Bulk operations need to refresh ctx.sessions.list and the
|
|
2628
|
+
// workspace projection after /batch mutates disk state so the
|
|
2629
|
+
// panel reflects the new layout.
|
|
2630
|
+
onRefreshAfterBulk: async () => {
|
|
2631
|
+
try { await this.props.props.onBulkRefresh(); } catch (_) { /* ignore */ }
|
|
2632
|
+
},
|
|
1804
2633
|
onOpen: (id) => { try { this.props.panelStore.set(false); this.props.props.onOpen(id); } catch (_) {} },
|
|
1805
2634
|
onArchive: this.props.props.onArchive,
|
|
1806
2635
|
onUnarchive: this.props.props.onUnarchive,
|
|
@@ -1814,19 +2643,31 @@ window.__ModuleLoader__.load({
|
|
|
1814
2643
|
function FooterAction(props) {
|
|
1815
2644
|
const t = props.t;
|
|
1816
2645
|
const onOpenPanel = props.onOpenPanel;
|
|
2646
|
+
// props.wide comes from SidebarRoot via SidebarFooterActionOwnerProps.
|
|
2647
|
+
// When the sidebar is collapsed into the 56px rail, render a 36x36
|
|
2648
|
+
// icon-only button so the Chinese label does not word-wrap vertically
|
|
2649
|
+
// (issue #16); when expanded, render a full-width row with icon +
|
|
2650
|
+
// label, left-aligned to match neighbouring footer entries.
|
|
2651
|
+
const wide = props.wide === true;
|
|
1817
2652
|
const open = useSyncExternalStore(
|
|
1818
2653
|
(cb) => panelStore.subscribe(cb),
|
|
1819
2654
|
() => panelStore.getSnapshot(),
|
|
1820
2655
|
() => panelStore.getSnapshot()
|
|
1821
2656
|
);
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
2657
|
+
const footerClass = "sm-footer" + (wide ? " sm-footer-wide" : " sm-footer-rail");
|
|
2658
|
+
const btnClass = "sm-footerBtn" + (wide ? " sm-footerBtn-wide" : " sm-footerBtn-rail");
|
|
2659
|
+
const btnLabel = t("footer.aria");
|
|
2660
|
+
return h("div", { className: footerClass },
|
|
2661
|
+
h("button", {
|
|
2662
|
+
type: "button",
|
|
2663
|
+
className: btnClass,
|
|
2664
|
+
"aria-label": btnLabel,
|
|
2665
|
+
title: wide ? undefined : btnLabel,
|
|
2666
|
+
onClick: () => onOpenPanel()
|
|
2667
|
+
},
|
|
2668
|
+
h(P.IconArchiveOutlineRegular, { size: wide ? 14 : 18 }),
|
|
2669
|
+
wide ? h("span", { className: "sm-footerBtn-label" }, t("footer.label")) : null
|
|
1826
2670
|
),
|
|
1827
|
-
// The panel is mounted ONLY while the user has the modal open. When
|
|
1828
|
-
// The wrap guards against an in-panel crash by returning null on its
|
|
1829
|
-
// own error boundary rather than letting it bubble up to the slot.
|
|
1830
2671
|
open ? h(SafePanel, {
|
|
1831
2672
|
panelStore: panelStore,
|
|
1832
2673
|
props: props,
|
|
@@ -1914,9 +2755,8 @@ window.__ModuleLoader__.load({
|
|
|
1914
2755
|
if (!response.ok) throw new Error(response.error || "preset migration failed");
|
|
1915
2756
|
// The current DSH client session store derives agentPreset from
|
|
1916
2757
|
// the refreshed projection; older builds exposed noteAgentPreset,
|
|
1917
|
-
|
|
2758
|
+
onOpenPanel: () => panelStore.toggle(),
|
|
1918
2759
|
await ctx.sessions.refresh();
|
|
1919
|
-
if (wasCurrent) ctx.sessions.open(sessionId);
|
|
1920
2760
|
// The migrate rewrite happens in-process on the live session, so
|
|
1921
2761
|
// we just need a delayed baseline to let the api-gateway's projection
|
|
1922
2762
|
// settle before re-activating the in-memory session.
|
|
@@ -1926,7 +2766,8 @@ window.__ModuleLoader__.load({
|
|
|
1926
2766
|
}).catch(() => {});
|
|
1927
2767
|
}, 250);
|
|
1928
2768
|
},
|
|
1929
|
-
onOpenPanel: () => panelStore.toggle()
|
|
2769
|
+
onOpenPanel: () => panelStore.toggle(),
|
|
2770
|
+
onBulkRefresh: async () => { try { await ctx.sessions.refresh(); } catch (_) { /* ignore */ } try { await ctx.workspaces.refresh(); } catch (_) { /* ignore */ } },
|
|
1930
2771
|
});
|
|
1931
2772
|
ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
|
|
1932
2773
|
name: "conversation.session.header.actions",
|
|
@@ -1950,4 +2791,4 @@ window.__ModuleLoader__.load({
|
|
|
1950
2791
|
exports.inject = inject;
|
|
1951
2792
|
return module.exports;
|
|
1952
2793
|
}
|
|
1953
|
-
});
|
|
2794
|
+
});
|