dsh-better-workspace 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README_EN.md +2 -2
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +145 -12
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
### 其余亮点
|
|
70
70
|
|
|
71
71
|
- **文件夹式收起**:点工作区行 = 收起/展开该工作区的全部会话(收起后行上保留会话数角标);会话分组同样可收起;搜索时自动全部展开。
|
|
72
|
-
- **拖拽还原原版能力**:工作区行可拖拽——拖到另一工作区行上/下半即调整顺序(写回宿主 registry 序),拖到分组行则移动进该分组(改写名称前缀)
|
|
72
|
+
- **拖拽还原原版能力**:工作区行可拖拽——拖到另一工作区行上/下半即调整顺序(写回宿主 registry 序),拖到分组行则移动进该分组(改写名称前缀);会话行可拖拽——同组内拖到另一会话上/下即调整顺序,拖到**别组的会话行**会把名字前缀改写到那个组(拖到根层的行 = 移出分组),拖到会话子分组行即移动进该分组,拖到**自己的工作区行**或**树里任何组外的地方**(含空白区域)同样移出分组 —— 拖拽期间工作区行整行高亮、树底部有一条淡虚线提示这个出口;带分组前缀的会话行右键还多一项「移出分组」。显示顺序 = 宿主手动序,拖拽结果立即生效。**磁盘层是文件系统事实,不随拖拽改变**:跨磁盘层的放置只重组名称分组(工作区落回自身磁盘层的末尾),同层放置才走锚点序。拖拽工作区期间,单链合并显示的行(如 工作/A)临时展开回文件夹树——路径里的每一级都是可投放的分组行,松手后自动合并回去。
|
|
73
73
|
- **不丢原生能力**:会话行(打开/重命名/分叉/归档)、每工作区「新会话」(点 + 总是先展开该工作区,让新会话立刻可见)、当前会话高亮、官方状态灯(蓝色运行环 / 琥珀待交互 / 绿色完成提醒)、**状态呼吸灯**(折叠藏起的状态灯向外冒泡:工作区/分组行图标按状态色呼吸发光——琥珀待交互/绿完成,自定义过发光的标题一起呼吸;运行中只在会话行本层蓝色呼吸,不外渗;可在设置关闭)、**活跃定时任务角标**(有活动 Schedule 的会话显示闹钟图标,与官方行一致)、未分组会话兜底、界面文案跟随 DSH 的语言设置(内置 21 种语言,含简繁中文)。
|
|
74
74
|
- **URL/斜杠标题不拆层**:宿主自动生成的会话标题落地时若含 `/`(如复述了 URL),整串自动包上 `“”` 原样显示,绝不解析成层级——只对本次启动后**新建的会话**生效,并等命名稳定(约 20 秒,让原生 AI 命名先落地)才处理;存量会话与你手动命名的 `/` 分组标题永不被改动。引号内(含手动添加)的 `/` 永不切分,孤立引号只当普通字符;无引号但含 URL 的旧标题也有平铺兜底。
|
|
75
75
|
- **对话空态页同样生效**:会话空态页的「添加工作区」菜单同样回归官方目录流(原生选择器 / 官方应用内浏览器)。
|
|
@@ -116,7 +116,7 @@ DSH 的侧边栏浏览区是一个公开插槽 `sidebar.workspaces`(single),官
|
|
|
116
116
|
|
|
117
117
|
## 限制与路线图
|
|
118
118
|
|
|
119
|
-
-
|
|
119
|
+
- 会话**跨工作区**拖拽仍被拒绝(会话归属工作区);同一工作区内的分组归属已随拖拽生效(v0.18.0:拖到别组的行改写前缀,拖到自己的工作区行移出分组,右键也有「移出分组」)。
|
|
120
120
|
- 搜索为本地标题过滤(两层扁平化后逐工作区匹配);宿主内容搜索(`session.search`)计划接回。
|
|
121
121
|
- flat(平铺)视图未接管,层级树即视图。
|
|
122
122
|
|
package/README_EN.md
CHANGED
|
@@ -69,7 +69,7 @@ Session titles nest on `/` too (e.g. `test1/plugin maintenance`). Session groups
|
|
|
69
69
|
### More highlights
|
|
70
70
|
|
|
71
71
|
- **Folder-style collapse**: clicking a workspace row collapses/expands all of its sessions (the row keeps a session counter when collapsed); session groups collapse too; search force-expands everything.
|
|
72
|
-
- **Native drag restored**: drag a workspace row above/below another to reorder (writes back the host registry order) or onto a folder row to move it into that group (rewrites the title prefix); drag session rows to reorder
|
|
72
|
+
- **Native drag restored**: drag a workspace row above/below another to reorder (writes back the host registry order) or onto a folder row to move it into that group (rewrites the title prefix); drag session rows to reorder inside their group, onto a row of ANOTHER group to move into that group (onto a root-level row to leave the group), onto a session-group row to move into it, or onto their OWN workspace row / anywhere outside a group (empty space included) to leave it — the workspace row highlights and the tree shows a faint dashed hint while such a drag is in flight; a grouped session row also offers "Move out of group" in its context menu. Display order = host manual order, drag results are visible immediately. **The disk layer is a filesystem fact and never changes through dragging**: a cross-level drop only re-groups the name prefix (the workspace lands at the end of its own disk level), same-level drops take the anchor order. While dragging a workspace, merged single-chain rows (e.g. work/A) temporarily re-expand into folder rows — every level of the path becomes a drop target, and chains merge back when the drag ends.
|
|
73
73
|
- **Native capabilities kept**: open/rename/fork/archive sessions, per-workspace new-session (the + button always expands the workspace first so the new session is immediately visible), current-session highlight, official status dots (blue running ring / amber pending / green done), **status breathing lights** (dots hidden by collapse bubble outward: workspace/folder rows breathe on their icon in the status color — amber pending / green done, custom-glow labels breathe along; running breathes blue only on the session row itself and never relays; off-able in settings), **active-schedule badge** (alarm clock icon for sessions with active Schedule records, matching the official rows), ungrouped-session fallback, UI copy in 21 languages following the DSH language preference (Simplified and Traditional Chinese included).
|
|
74
74
|
- **Slash-bearing titles never nest from URLs**: when a host-generated session title lands containing `/` (e.g. echoing a pasted URL), it is auto-wrapped in `“”` and shown verbatim — only for sessions **created after this launch**, and only once the name stays stable for ~20s (letting the native AI naming land first); pre-existing sessions and your manually named `/` groupings are never touched. Quotes (manual ones too) never split on `/`, and a lone quote is just an ordinary character; unquoted URL tails still fall back flat.
|
|
75
75
|
- **Conversation hero too**: the empty-state “Add workspace” menu returns to the official directory flow (native chooser / official in-app browser).
|
|
@@ -102,7 +102,7 @@ Same `dsh-better-*` family, zero overlap: [dsh-better-sidebar](https://github.co
|
|
|
102
102
|
|
|
103
103
|
## Limitations / roadmap
|
|
104
104
|
|
|
105
|
-
-
|
|
105
|
+
- Dragging a session **across workspaces** is still refused (a session belongs to its workspace); inside one workspace the group now follows the drop (v0.18.0: another group's row rewrites the prefix, the workspace row takes it out of every group, and the context menu offers "Move out of group").
|
|
106
106
|
- Search is local title filtering (both layers flattened, per-workspace matching); host content search (`session.search`) is planned.
|
|
107
107
|
- The flat view is not taken over; the hierarchical tree is the view.
|
|
108
108
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-better-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "侧边栏工作区树:按官方磁盘目录嵌套(官方 workspace-tree 语义),可选按名称中的 / 分组(默认关闭,设置里开启);添加工作区由官方目录流承载。Workspace tree for the DSH sidebar workspace list, nested by official disk folders with optional \"/\"-name grouping.",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-better-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "侧边栏工作区树:按官方磁盘目录嵌套(官方 workspace-tree 语义),可选按名称中的 / 分组(默认关闭,设置里开启);添加工作区走官方目录流。Workspace tree for the DSH sidebar: disk-path nesting (official workspace-tree semantics), with optional \"/\"-name grouping (off by default); add-workspace rides the official directory flow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -72,6 +72,7 @@ window.__ModuleLoader__.load({
|
|
|
72
72
|
'menu.renameFolder': '重命名分组',
|
|
73
73
|
'menu.renameSgroup': '重命名会话分组',
|
|
74
74
|
'menu.moveToGroup': '移动到分组…',
|
|
75
|
+
'menu.moveOutGroup': '移出分组',
|
|
75
76
|
'settings.title': '更好的工作区',
|
|
76
77
|
'settings.desc': '工作区树的外观与折叠偏好',
|
|
77
78
|
'settings.expand': '展开',
|
|
@@ -201,6 +202,7 @@ window.__ModuleLoader__.load({
|
|
|
201
202
|
'menu.renameFolder': 'Rename folder',
|
|
202
203
|
'menu.renameSgroup': 'Rename session group',
|
|
203
204
|
'menu.moveToGroup': 'Move to group…',
|
|
205
|
+
'menu.moveOutGroup': 'Move out of group',
|
|
204
206
|
'settings.title': 'Better Workspaces',
|
|
205
207
|
'settings.desc': 'Workspace tree appearance and folding preferences',
|
|
206
208
|
'settings.expand': 'Expand',
|
|
@@ -338,6 +340,7 @@ window.__ModuleLoader__.load({
|
|
|
338
340
|
'menu.renameFolder': 'إعادة تسمية المجلد',
|
|
339
341
|
'menu.renameSgroup': 'إعادة تسمية مجموعة الجلسات',
|
|
340
342
|
'menu.moveToGroup': 'نقل إلى مجموعة…',
|
|
343
|
+
'menu.moveOutGroup': 'إخراج من المجموعة',
|
|
341
344
|
'settings.title': 'مساحات عمل أفضل',
|
|
342
345
|
'settings.desc': 'مظهر شجرة مساحات العمل وتفضيلات الطيّ',
|
|
343
346
|
'settings.expand': 'توسيع',
|
|
@@ -467,6 +470,7 @@ window.__ModuleLoader__.load({
|
|
|
467
470
|
'menu.renameFolder': 'Ordner umbenennen',
|
|
468
471
|
'menu.renameSgroup': 'Sitzungsgruppe umbenennen',
|
|
469
472
|
'menu.moveToGroup': 'In Gruppe verschieben…',
|
|
473
|
+
'menu.moveOutGroup': 'Aus der Gruppe entfernen',
|
|
470
474
|
'settings.title': 'Bessere Arbeitsbereiche',
|
|
471
475
|
'settings.desc': 'Aussehen und Einklappverhalten des Arbeitsbereich-Baums',
|
|
472
476
|
'settings.expand': 'Ausklappen',
|
|
@@ -596,6 +600,7 @@ window.__ModuleLoader__.load({
|
|
|
596
600
|
'menu.renameFolder': 'Renommer le dossier',
|
|
597
601
|
'menu.renameSgroup': 'Renommer le groupe de sessions',
|
|
598
602
|
'menu.moveToGroup': 'Déplacer vers le groupe…',
|
|
603
|
+
'menu.moveOutGroup': 'Retirer du groupe',
|
|
599
604
|
'settings.title': 'Espaces de travail améliorés',
|
|
600
605
|
'settings.desc': 'Apparence et repli de l\'arborescence des espaces de travail',
|
|
601
606
|
'settings.expand': 'Développer',
|
|
@@ -725,6 +730,7 @@ window.__ModuleLoader__.load({
|
|
|
725
730
|
'menu.renameFolder': 'फ़ोल्डर का नाम बदलें',
|
|
726
731
|
'menu.renameSgroup': 'सत्र समूह का नाम बदलें',
|
|
727
732
|
'menu.moveToGroup': 'समूह में ले जाएँ…',
|
|
733
|
+
'menu.moveOutGroup': 'समूह से बाहर निकालें',
|
|
728
734
|
'settings.title': 'बेहतर कार्यस्थान',
|
|
729
735
|
'settings.desc': 'कार्यस्थान वृक्ष का रूप और समेटने की प्राथमिकताएँ',
|
|
730
736
|
'settings.expand': 'फैलाएँ',
|
|
@@ -854,6 +860,7 @@ window.__ModuleLoader__.load({
|
|
|
854
860
|
'menu.renameFolder': 'Ganti nama folder',
|
|
855
861
|
'menu.renameSgroup': 'Ganti nama grup sesi',
|
|
856
862
|
'menu.moveToGroup': 'Pindahkan ke grup…',
|
|
863
|
+
'menu.moveOutGroup': 'Keluarkan dari grup',
|
|
857
864
|
'settings.title': 'Ruang kerja yang lebih baik',
|
|
858
865
|
'settings.desc': 'Tampilan dan pelipatan pohon ruang kerja',
|
|
859
866
|
'settings.expand': 'Bentangkan',
|
|
@@ -983,6 +990,7 @@ window.__ModuleLoader__.load({
|
|
|
983
990
|
'menu.renameFolder': 'Rinomina cartella',
|
|
984
991
|
'menu.renameSgroup': 'Rinomina gruppo di sessioni',
|
|
985
992
|
'menu.moveToGroup': 'Sposta nel gruppo…',
|
|
993
|
+
'menu.moveOutGroup': 'Rimuovi dal gruppo',
|
|
986
994
|
'settings.title': 'Aree di lavoro migliori',
|
|
987
995
|
'settings.desc': 'Aspetto e compressione dell\'albero delle aree di lavoro',
|
|
988
996
|
'settings.expand': 'Espandi',
|
|
@@ -1112,6 +1120,7 @@ window.__ModuleLoader__.load({
|
|
|
1112
1120
|
'menu.renameFolder': 'フォルダー名を変更',
|
|
1113
1121
|
'menu.renameSgroup': 'セッショングループ名を変更',
|
|
1114
1122
|
'menu.moveToGroup': 'グループに移動…',
|
|
1123
|
+
'menu.moveOutGroup': 'グループから出す',
|
|
1115
1124
|
'settings.title': 'より良いワークスペース',
|
|
1116
1125
|
'settings.desc': 'ワークスペースツリーの外観と折りたたみ設定',
|
|
1117
1126
|
'settings.expand': '展開',
|
|
@@ -1241,6 +1250,7 @@ window.__ModuleLoader__.load({
|
|
|
1241
1250
|
'menu.renameFolder': '폴더 이름 바꾸기',
|
|
1242
1251
|
'menu.renameSgroup': '세션 그룹 이름 바꾸기',
|
|
1243
1252
|
'menu.moveToGroup': '그룹으로 이동…',
|
|
1253
|
+
'menu.moveOutGroup': '그룹에서 제거',
|
|
1244
1254
|
'settings.title': '더 나은 워크스페이스',
|
|
1245
1255
|
'settings.desc': '워크스페이스 트리의 모양과 접기 설정',
|
|
1246
1256
|
'settings.expand': '펼치기',
|
|
@@ -1370,6 +1380,7 @@ window.__ModuleLoader__.load({
|
|
|
1370
1380
|
'menu.renameFolder': 'Mapnaam wijzigen',
|
|
1371
1381
|
'menu.renameSgroup': 'Sessiegroep naam wijzigen',
|
|
1372
1382
|
'menu.moveToGroup': 'Naar groep verplaatsen…',
|
|
1383
|
+
'menu.moveOutGroup': 'Uit groep halen',
|
|
1373
1384
|
'settings.title': 'Betere werkruimten',
|
|
1374
1385
|
'settings.desc': 'Uiterlijk en inklapgedrag van de werkruimteboom',
|
|
1375
1386
|
'settings.expand': 'Uitklappen',
|
|
@@ -1499,6 +1510,7 @@ window.__ModuleLoader__.load({
|
|
|
1499
1510
|
'menu.renameFolder': 'Zmień nazwę folderu',
|
|
1500
1511
|
'menu.renameSgroup': 'Zmień nazwę grupy sesji',
|
|
1501
1512
|
'menu.moveToGroup': 'Przenieś do grupy…',
|
|
1513
|
+
'menu.moveOutGroup': 'Przenieś poza grupę',
|
|
1502
1514
|
'settings.title': 'Lepsze obszary robocze',
|
|
1503
1515
|
'settings.desc': 'Wygląd i zwijanie drzewa obszarów roboczych',
|
|
1504
1516
|
'settings.expand': 'Rozwiń',
|
|
@@ -1628,6 +1640,7 @@ window.__ModuleLoader__.load({
|
|
|
1628
1640
|
'menu.renameFolder': 'Renomear pasta',
|
|
1629
1641
|
'menu.renameSgroup': 'Renomear grupo de sessões',
|
|
1630
1642
|
'menu.moveToGroup': 'Mover para o grupo…',
|
|
1643
|
+
'menu.moveOutGroup': 'Remover do grupo',
|
|
1631
1644
|
'settings.title': 'Espaços de trabalho melhores',
|
|
1632
1645
|
'settings.desc': 'Aparência e recolhimento da árvore de espaços de trabalho',
|
|
1633
1646
|
'settings.expand': 'Expandir',
|
|
@@ -1757,6 +1770,7 @@ window.__ModuleLoader__.load({
|
|
|
1757
1770
|
'menu.renameFolder': 'Переименовать папку',
|
|
1758
1771
|
'menu.renameSgroup': 'Переименовать группу сессий',
|
|
1759
1772
|
'menu.moveToGroup': 'Переместить в группу…',
|
|
1773
|
+
'menu.moveOutGroup': 'Убрать из группы',
|
|
1760
1774
|
'settings.title': 'Улучшенные рабочие области',
|
|
1761
1775
|
'settings.desc': 'Внешний вид и сворачивание дерева рабочих областей',
|
|
1762
1776
|
'settings.expand': 'Развернуть',
|
|
@@ -1886,6 +1900,7 @@ window.__ModuleLoader__.load({
|
|
|
1886
1900
|
'menu.renameFolder': 'Byt namn på mapp',
|
|
1887
1901
|
'menu.renameSgroup': 'Byt namn på sessionsgrupp',
|
|
1888
1902
|
'menu.moveToGroup': 'Flytta till grupp…',
|
|
1903
|
+
'menu.moveOutGroup': 'Flytta ut ur gruppen',
|
|
1889
1904
|
'settings.title': 'Bättre arbetsytor',
|
|
1890
1905
|
'settings.desc': 'Utseende och ihopfällning för arbetsyteträdet',
|
|
1891
1906
|
'settings.expand': 'Fäll ut',
|
|
@@ -2015,6 +2030,7 @@ window.__ModuleLoader__.load({
|
|
|
2015
2030
|
'menu.renameFolder': 'เปลี่ยนชื่อโฟลเดอร์',
|
|
2016
2031
|
'menu.renameSgroup': 'เปลี่ยนชื่อกลุ่มเซสชัน',
|
|
2017
2032
|
'menu.moveToGroup': 'ย้ายไปยังกลุ่ม…',
|
|
2033
|
+
'menu.moveOutGroup': 'ย้ายออกจากกลุ่ม',
|
|
2018
2034
|
'settings.title': 'พื้นที่ทำงานที่ดีขึ้น',
|
|
2019
2035
|
'settings.desc': 'รูปลักษณ์และการย่อของต้นไม้พื้นที่ทำงาน',
|
|
2020
2036
|
'settings.expand': 'ขยาย',
|
|
@@ -2144,6 +2160,7 @@ window.__ModuleLoader__.load({
|
|
|
2144
2160
|
'menu.renameFolder': 'Klasörü yeniden adlandır',
|
|
2145
2161
|
'menu.renameSgroup': 'Oturum grubunu yeniden adlandır',
|
|
2146
2162
|
'menu.moveToGroup': 'Gruba taşı…',
|
|
2163
|
+
'menu.moveOutGroup': 'Gruptan çıkar',
|
|
2147
2164
|
'settings.title': 'Daha iyi çalışma alanları',
|
|
2148
2165
|
'settings.desc': 'Çalışma alanı ağacının görünümü ve daraltma tercihleri',
|
|
2149
2166
|
'settings.expand': 'Genişlet',
|
|
@@ -2273,6 +2290,7 @@ window.__ModuleLoader__.load({
|
|
|
2273
2290
|
'menu.renameFolder': 'Đổi tên thư mục',
|
|
2274
2291
|
'menu.renameSgroup': 'Đổi tên nhóm phiên',
|
|
2275
2292
|
'menu.moveToGroup': 'Chuyển vào nhóm…',
|
|
2293
|
+
'menu.moveOutGroup': 'Đưa ra khỏi nhóm',
|
|
2276
2294
|
'settings.title': 'Không gian làm việc tốt hơn',
|
|
2277
2295
|
'settings.desc': 'Giao diện và cách thu gọn của cây không gian làm việc',
|
|
2278
2296
|
'settings.expand': 'Mở rộng',
|
|
@@ -2402,6 +2420,7 @@ window.__ModuleLoader__.load({
|
|
|
2402
2420
|
'menu.renameFolder': '重新命名分組',
|
|
2403
2421
|
'menu.renameSgroup': '重新命名對話分組',
|
|
2404
2422
|
'menu.moveToGroup': '移動到分組…',
|
|
2423
|
+
'menu.moveOutGroup': '移出分組',
|
|
2405
2424
|
'settings.title': '更好嘅工作區',
|
|
2406
2425
|
'settings.desc': '工作區樹嘅外觀同收起偏好',
|
|
2407
2426
|
'settings.expand': '展開',
|
|
@@ -2531,6 +2550,7 @@ window.__ModuleLoader__.load({
|
|
|
2531
2550
|
'menu.renameFolder': '重新命名分組',
|
|
2532
2551
|
'menu.renameSgroup': '重新命名對話分組',
|
|
2533
2552
|
'menu.moveToGroup': '移動到分組…',
|
|
2553
|
+
'menu.moveOutGroup': '移出分組',
|
|
2534
2554
|
'settings.title': '更好嘅工作區',
|
|
2535
2555
|
'settings.desc': '工作區樹嘅外觀同收起偏好',
|
|
2536
2556
|
'settings.expand': '展開',
|
|
@@ -2660,6 +2680,7 @@ window.__ModuleLoader__.load({
|
|
|
2660
2680
|
'menu.renameFolder': '重新命名群組',
|
|
2661
2681
|
'menu.renameSgroup': '重新命名工作階段群組',
|
|
2662
2682
|
'menu.moveToGroup': '移動到分組…',
|
|
2683
|
+
'menu.moveOutGroup': '移出分組',
|
|
2663
2684
|
'settings.title': '更好的工作區',
|
|
2664
2685
|
'settings.desc': '工作區樹的外觀與收合偏好',
|
|
2665
2686
|
'settings.expand': '展開',
|
|
@@ -3314,6 +3335,10 @@ window.__ModuleLoader__.load({
|
|
|
3314
3335
|
// rim + a tinted wash so "this drop joins a group" reads stronger than
|
|
3315
3336
|
// the reorder dashes above.
|
|
3316
3337
|
'.bw-drop-into-strong{outline:2px solid var(--dsw-alias-brand-primary,#5b8def);outline-offset:-2px;background:color-mix(in srgb, var(--dsw-alias-brand-primary,#5b8def) 16%, transparent)}',
|
|
3338
|
+
// Session drag whose source sits in a group: a quiet dashed line at the
|
|
3339
|
+
// bottom of the tree says "anywhere outside a group = out of it". Kept
|
|
3340
|
+
// faint on purpose — the row outlines are the loud feedback.
|
|
3341
|
+
'.bw-root-drop-out::after{content:"";position:absolute;left:10px;right:10px;bottom:6px;border-bottom:1px dashed var(--dsw-alias-border-secondary,rgba(230,230,230,.35));pointer-events:none}',
|
|
3317
3342
|
// v0.17.0: the new-group entry is an ICON button now (same family as the
|
|
3318
3343
|
// search / add buttons beside it); the wording moved into its tooltip.
|
|
3319
3344
|
'.bw-newgroup-btn{flex:none}',
|
|
@@ -4246,13 +4271,13 @@ window.__ModuleLoader__.load({
|
|
|
4246
4271
|
)
|
|
4247
4272
|
}
|
|
4248
4273
|
|
|
4249
|
-
function WorkspaceRow({ workspace, depth, count, sessionsOpen, onToggle, onStart, onContextMenu, currentInside, dropHalf, dragEvents, custStyle, iconMode, pulse, t }) {
|
|
4274
|
+
function WorkspaceRow({ workspace, depth, count, sessionsOpen, onToggle, onStart, onContextMenu, currentInside, dropHalf, dropInto, dragEvents, custStyle, iconMode, pulse, t }) {
|
|
4250
4275
|
const iconEl = iconOf(iconMode, sessionsOpen)
|
|
4251
4276
|
const iconChild = pulse
|
|
4252
4277
|
? E(PulseGlow, { state: pulse }, iconEl || (typeof ui.StateDot === 'function' ? E(ui.StateDot, { state: pulse, size: 10 }) : null))
|
|
4253
4278
|
: iconEl
|
|
4254
4279
|
return E('div', {
|
|
4255
|
-
className: cls('bw-row', currentInside && 'bw-row-current', dropHalf === 'before' && 'bw-drop-before', dropHalf === 'after' && 'bw-drop-after'),
|
|
4280
|
+
className: cls('bw-row', currentInside && 'bw-row-current', dropHalf === 'before' && 'bw-drop-before', dropHalf === 'after' && 'bw-drop-after', dropInto && 'bw-drop-into', dropInto && 'bw-drop-into-strong'),
|
|
4256
4281
|
style: { paddingLeft: 6 + depth * 12, ...(custStyle || {}), ...(pulse ? { '--bw-pulse-color': PULSE_COLORS[pulse] || PULSE_COLORS.ongoing } : null) },
|
|
4257
4282
|
onClick: onToggle,
|
|
4258
4283
|
onContextMenu: onContextMenu,
|
|
@@ -5372,6 +5397,18 @@ window.__ModuleLoader__.load({
|
|
|
5372
5397
|
const s = splitTitleSegs(String(title || ''))
|
|
5373
5398
|
return s.length > 0 ? s[s.length - 1] : (fallback || String(title || ''))
|
|
5374
5399
|
}
|
|
5400
|
+
/**
|
|
5401
|
+
* Retitle `title` so its leaf lands in the group `anchorTitle` lives in
|
|
5402
|
+
* (v0.18.0). '' as the anchor group means the root level. Used by the
|
|
5403
|
+
* cross-group session drop: the visible order of session rows is decided
|
|
5404
|
+
* by these groups, so a drop that only reorders the flat list would leave
|
|
5405
|
+
* the row exactly where it was.
|
|
5406
|
+
*/
|
|
5407
|
+
const titleMovedToGroup = (title, anchorTitle) => {
|
|
5408
|
+
const leaf = leafOf(title)
|
|
5409
|
+
const group = groupPrefixOf(anchorTitle)
|
|
5410
|
+
return group ? group + '/' + leaf : leaf
|
|
5411
|
+
}
|
|
5375
5412
|
/**
|
|
5376
5413
|
* Every existing group path, deduplicated (datalist source). `kind`
|
|
5377
5414
|
* narrows it to the name layer the dialog is about: workspace rows
|
|
@@ -5661,6 +5698,22 @@ window.__ModuleLoader__.load({
|
|
|
5661
5698
|
setDrag(null)
|
|
5662
5699
|
},
|
|
5663
5700
|
onDragOver: (event) => {
|
|
5701
|
+
// A SESSION dragged onto the workspace row lands at the root level of
|
|
5702
|
+
// its session tree — the way OUT of any group (v0.18.0). Only for
|
|
5703
|
+
// rows that actually sit in a group: a top-level session gains
|
|
5704
|
+
// nothing here, so this row must not light up for it (the browser
|
|
5705
|
+
// then shows its plain "no drop" cursor, which tells the truth).
|
|
5706
|
+
if (dragMatches('session')) {
|
|
5707
|
+
if (!sessionSlash || drag.source.workspaceId !== workspace.workspaceId) return
|
|
5708
|
+
if (leafOf(drag.source.title) === drag.source.title) return
|
|
5709
|
+
event.preventDefault()
|
|
5710
|
+
event.stopPropagation()
|
|
5711
|
+
try { event.dataTransfer.dropEffect = 'move' } catch { }
|
|
5712
|
+
setDrag(current => (current && current.over && current.over.kind === 'ws-root' && current.over.target === workspace.workspaceId)
|
|
5713
|
+
? current
|
|
5714
|
+
: (current ? { ...current, over: { kind: 'ws-root', target: workspace.workspaceId } } : current))
|
|
5715
|
+
return
|
|
5716
|
+
}
|
|
5664
5717
|
if (!dragMatches('workspace')) return
|
|
5665
5718
|
event.preventDefault()
|
|
5666
5719
|
event.stopPropagation()
|
|
@@ -5671,6 +5724,14 @@ window.__ModuleLoader__.load({
|
|
|
5671
5724
|
: (current ? { ...current, over: { kind: 'workspace', target: workspace.workspaceId, half } } : current))
|
|
5672
5725
|
},
|
|
5673
5726
|
onDrop: (event) => {
|
|
5727
|
+
if (dragMatches('session')) {
|
|
5728
|
+
if (!sessionSlash || drag.source.workspaceId !== workspace.workspaceId) return
|
|
5729
|
+
if (leafOf(drag.source.title) === drag.source.title) return
|
|
5730
|
+
event.preventDefault()
|
|
5731
|
+
event.stopPropagation()
|
|
5732
|
+
commitSessionMoveOut(workspace.workspaceId)
|
|
5733
|
+
return
|
|
5734
|
+
}
|
|
5674
5735
|
if (!dragMatches('workspace')) return
|
|
5675
5736
|
event.preventDefault()
|
|
5676
5737
|
event.stopPropagation()
|
|
@@ -5814,17 +5875,55 @@ window.__ModuleLoader__.load({
|
|
|
5814
5875
|
const anchor = half === 'after'
|
|
5815
5876
|
? (index === -1 ? undefined : (flat[index + 1] ? flat[index + 1].id : undefined))
|
|
5816
5877
|
: targetSessionId
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
//
|
|
5824
|
-
|
|
5825
|
-
|
|
5878
|
+
// v0.18.0 — a drop on a row of ANOTHER group MOVES the row (the name
|
|
5879
|
+
// prefix is rewritten to the target row's group). Until then only the
|
|
5880
|
+
// flat list was reordered while the visible order is decided by the
|
|
5881
|
+
// groups, so a cross-group drop left the row exactly where it was and
|
|
5882
|
+
// read as "the drag did nothing" (user report). Same group keeps the
|
|
5883
|
+
// pure reorder; with the name layer off every group is '' — which is
|
|
5884
|
+
// the same thing.
|
|
5885
|
+
const target = flat.find(s => s.id === targetSessionId)
|
|
5886
|
+
const nextTitle = sessionSlash && target ? titleMovedToGroup(source.title, target.title) : null
|
|
5887
|
+
const reorder = () => {
|
|
5888
|
+
if (typeof insertSessionBefore === 'function') {
|
|
5889
|
+
return (anchor !== undefined ? insertSessionBefore(workspaceId, source.sessionId, anchor) : insertSessionBefore(workspaceId, source.sessionId))
|
|
5890
|
+
}
|
|
5891
|
+
// Host channel gone: commit the same move into the browser-local order.
|
|
5892
|
+
if (actions && typeof actions.setSessionOrder === 'function') {
|
|
5893
|
+
actions.setSessionOrder(workspaceId, reorderIds(flat.map((s) => s.id), source.sessionId, anchor))
|
|
5894
|
+
}
|
|
5895
|
+
return undefined
|
|
5826
5896
|
}
|
|
5897
|
+
const chain = nextTitle !== null && nextTitle !== source.title
|
|
5898
|
+
? Promise.resolve().then(() => renameByUser(source.sessionId, nextTitle))
|
|
5899
|
+
: Promise.resolve()
|
|
5900
|
+
chain.then(reorder).catch(fail)
|
|
5901
|
+
}
|
|
5902
|
+
/**
|
|
5903
|
+
* Drop a session ON ITS OWN WORKSPACE ROW = leave the group (v0.18.0).
|
|
5904
|
+
* The workspace row is the root of the session tree, so it is the one
|
|
5905
|
+
* drop target that always exists OUTSIDE every group — the user's "drag
|
|
5906
|
+
* it out of 测试1" gesture had nowhere to land before this.
|
|
5907
|
+
*/
|
|
5908
|
+
const commitSessionMoveOut = (workspaceId) => {
|
|
5909
|
+
const source = drag.source
|
|
5910
|
+
setDrag(null)
|
|
5911
|
+
if (!source || source.workspaceId !== workspaceId) return
|
|
5912
|
+
const leaf = leafOf(source.title)
|
|
5913
|
+
if (leaf === source.title) return
|
|
5914
|
+
Promise.resolve()
|
|
5915
|
+
.then(() => renameByUser(source.sessionId, leaf))
|
|
5916
|
+
.then(() => {
|
|
5917
|
+
// Append to the end of the workspace's flat order: the row now
|
|
5918
|
+
// lives at the root level, and the host order is what the tree
|
|
5919
|
+
// renders there — without this it would surface somewhere in the
|
|
5920
|
+
// middle of the top-level rows, wherever its id happens to sit.
|
|
5921
|
+
if (typeof insertSessionBefore === 'function') return insertSessionBefore(workspaceId, source.sessionId)
|
|
5922
|
+
return undefined
|
|
5923
|
+
})
|
|
5924
|
+
.catch(fail)
|
|
5827
5925
|
}
|
|
5926
|
+
const wsRootDropActive = (workspaceId) => dragMatches('session') && drag.over !== null && drag.over.kind === 'ws-root' && drag.over.target === workspaceId
|
|
5828
5927
|
const commitSessionMoveInto = (workspaceId, groupPath) => {
|
|
5829
5928
|
const source = drag.source
|
|
5830
5929
|
setDrag(null)
|
|
@@ -6039,6 +6138,7 @@ window.__ModuleLoader__.load({
|
|
|
6039
6138
|
onStart: () => { actions.setSessionsExpanded(workspace.workspaceId, true); startSession(workspace.workspaceId) },
|
|
6040
6139
|
onContextMenu: (e) => openCtx('workspace', workspace, e),
|
|
6041
6140
|
dropHalf: wsDropHalf(workspace.workspaceId),
|
|
6141
|
+
dropInto: wsRootDropActive(workspace.workspaceId),
|
|
6042
6142
|
dragEvents: workspaceDragEvents(workspace),
|
|
6043
6143
|
custStyle: rowStyleOf('workspace:' + workspace.workspaceId),
|
|
6044
6144
|
iconMode: (styleEntry('workspace:' + workspace.workspaceId) || {}).icon || 'solid',
|
|
@@ -6158,8 +6258,14 @@ window.__ModuleLoader__.load({
|
|
|
6158
6258
|
// probing it silently yielded a glyph-less row. IconFolderOpenOutline16
|
|
6159
6259
|
// is present and reads as "move into a group" just as well.
|
|
6160
6260
|
const groupItem = { id: 'move-group', label: t('menu.moveToGroup'), icon: icon('IconFolderOpenOutline16', 16) }
|
|
6261
|
+
// "Leave the group" (v0.18.0) — the counterpart of move-group, offered
|
|
6262
|
+
// only where a group actually exists to leave: a SESSION row whose own
|
|
6263
|
+
// title carries a "/" prefix, with the session name layer on. A
|
|
6264
|
+
// top-level row gets no entry (there is nothing to leave).
|
|
6265
|
+
const ungroupItem = { id: 'move-out', label: t('menu.moveOutGroup'), icon: icon('IconRightUpOutline16', 16) }
|
|
6161
6266
|
const wsGroupItems = workspaceSlash ? [groupItem] : []
|
|
6162
6267
|
const sgroupItems = sessionSlash ? [groupItem] : []
|
|
6268
|
+
const sessionUngroupItems = sessionSlash && ctx.kind === 'session' && ctx.payload && groupPrefixOf(String(ctx.payload.title || '')) !== '' ? [ungroupItem] : []
|
|
6163
6269
|
// "Delete group" only makes sense for a DECLARED group that holds
|
|
6164
6270
|
// nothing (v0.17.0): a group with members is a projection of their
|
|
6165
6271
|
// titles, so removing the declaration would change nothing on screen
|
|
@@ -6188,6 +6294,7 @@ window.__ModuleLoader__.load({
|
|
|
6188
6294
|
// deliberately NOT styled destructive.
|
|
6189
6295
|
{ id: 'archive', label: ot ? ot('menu.archiveSession') : t('menu.archive'), icon: icon('IconArchiveOutline20', 16) },
|
|
6190
6296
|
{ type: 'separator', id: 'bw-sep' },
|
|
6297
|
+
...sessionUngroupItems,
|
|
6191
6298
|
...sgroupItems,
|
|
6192
6299
|
customizeEntry,
|
|
6193
6300
|
]
|
|
@@ -6230,6 +6337,12 @@ window.__ModuleLoader__.load({
|
|
|
6230
6337
|
else if (kind === 'session' && id === 'rename') setDialog({ kind: 'sess-rename', session: payload })
|
|
6231
6338
|
else if (kind === 'session' && id === 'fork') forkSession(payload.id)
|
|
6232
6339
|
else if (kind === 'session' && id === 'archive') { Promise.resolve().then(() => archiveSession(payload.id)).catch(fail) }
|
|
6340
|
+
// Plain retitle back to the leaf: the row leaves its group and returns
|
|
6341
|
+
// to the top level of its workspace (v0.18.0).
|
|
6342
|
+
else if (kind === 'session' && id === 'move-out') {
|
|
6343
|
+
const leaf = leafOf(String(payload.title || ''))
|
|
6344
|
+
if (leaf !== payload.title) Promise.resolve().then(() => renameByUser(payload.id, leaf)).catch(fail)
|
|
6345
|
+
}
|
|
6233
6346
|
}
|
|
6234
6347
|
|
|
6235
6348
|
/* --------------------------- dialogs --------------------------- */
|
|
@@ -6393,7 +6506,27 @@ window.__ModuleLoader__.load({
|
|
|
6393
6506
|
.catch((reason) => { fail(messageOf(reason)) })
|
|
6394
6507
|
}
|
|
6395
6508
|
|
|
6396
|
-
|
|
6509
|
+
// v0.18.0 — "drag it OUT" must never land on nothing. The row level
|
|
6510
|
+
// already covers the workspace row and the root-level session rows; this
|
|
6511
|
+
// catches everything else (the empty space below the tree, the header
|
|
6512
|
+
// gap), so a drop outside every group always means the same thing:
|
|
6513
|
+
// leave the group. Row handlers stopPropagation, so this only fires
|
|
6514
|
+
// where no row claimed the drop.
|
|
6515
|
+
const sessionCouldLeaveGroup = dragMatches('session') && sessionSlash && drag.source && leafOf(drag.source.title) !== drag.source.title
|
|
6516
|
+
const rootDropEvents = {
|
|
6517
|
+
onDragOver: (event) => {
|
|
6518
|
+
if (!sessionCouldLeaveGroup) return
|
|
6519
|
+
event.preventDefault()
|
|
6520
|
+
try { event.dataTransfer.dropEffect = 'move' } catch { }
|
|
6521
|
+
},
|
|
6522
|
+
onDrop: (event) => {
|
|
6523
|
+
if (!sessionCouldLeaveGroup) return
|
|
6524
|
+
event.preventDefault()
|
|
6525
|
+
commitSessionMoveOut(drag.source.workspaceId)
|
|
6526
|
+
},
|
|
6527
|
+
}
|
|
6528
|
+
|
|
6529
|
+
return E('div', { className: cls('bw-root', sessionCouldLeaveGroup && 'bw-root-drop-out'), ...rootDropEvents },
|
|
6397
6530
|
StyleNode(),
|
|
6398
6531
|
E('div', { className: 'bw-header' },
|
|
6399
6532
|
E('div', { className: 'bw-header-title' }, t('title')),
|