dsh-better-workspace 0.17.0 → 0.17.2
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/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +119 -26
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-better-workspace",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2",
|
|
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.17.
|
|
3
|
+
"version": "0.17.2",
|
|
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
|
@@ -151,6 +151,7 @@ window.__ModuleLoader__.load({
|
|
|
151
151
|
'group.new.nameHint': "名称中的 / 即嵌套层级,例如 前端/组件",
|
|
152
152
|
'group.new.empty': "请输入分组名称",
|
|
153
153
|
'group.new.root': "顶层",
|
|
154
|
+
'group.new.independent': "新建顶层分组",
|
|
154
155
|
'group.new.pickWorkspace': "选择一个工作区",
|
|
155
156
|
'group.new.noWorkspace': "还没有工作区,请先添加一个",
|
|
156
157
|
'group.new.pickFirst': "请先选择一个工作区",
|
|
@@ -279,6 +280,7 @@ window.__ModuleLoader__.load({
|
|
|
279
280
|
'group.new.nameHint': "A \"/\" nests further, e.g. frontend/components",
|
|
280
281
|
'group.new.empty': "Enter a group name",
|
|
281
282
|
'group.new.root': "Top level",
|
|
283
|
+
'group.new.independent': "New top-level group",
|
|
282
284
|
'group.new.pickWorkspace': "Pick a workspace",
|
|
283
285
|
'group.new.noWorkspace': "No workspaces yet — add one first",
|
|
284
286
|
'group.new.pickFirst': "Pick a workspace first",
|
|
@@ -415,6 +417,7 @@ window.__ModuleLoader__.load({
|
|
|
415
417
|
'group.new.nameHint': "الشرطة المائلة \"/\" تنشئ مستوى أعمق، مثل frontend/components",
|
|
416
418
|
'group.new.empty': "أدخل اسم المجموعة",
|
|
417
419
|
'group.new.root': "المستوى الأعلى",
|
|
420
|
+
'group.new.independent': "مجموعة جديدة في المستوى الأعلى",
|
|
418
421
|
'group.new.pickWorkspace': "اختر مساحة عمل",
|
|
419
422
|
'group.new.noWorkspace': "لا توجد مساحات عمل بعد — أضف واحدة أولاً",
|
|
420
423
|
'group.new.pickFirst': "اختر مساحة عمل أولاً",
|
|
@@ -543,6 +546,7 @@ window.__ModuleLoader__.load({
|
|
|
543
546
|
'group.new.nameHint': "Ein „/\" verschachtelt weiter, z. B. frontend/components",
|
|
544
547
|
'group.new.empty': "Gruppennamen eingeben",
|
|
545
548
|
'group.new.root': "Oberste Ebene",
|
|
549
|
+
'group.new.independent': "Neue Gruppe auf oberster Ebene",
|
|
546
550
|
'group.new.pickWorkspace': "Arbeitsbereich wählen",
|
|
547
551
|
'group.new.noWorkspace': "Noch keine Arbeitsbereiche — zuerst einen hinzufügen",
|
|
548
552
|
'group.new.pickFirst': "Zuerst einen Arbeitsbereich wählen",
|
|
@@ -671,6 +675,7 @@ window.__ModuleLoader__.load({
|
|
|
671
675
|
'group.new.nameHint': "Un « / » imbrique davantage, par ex. frontend/components",
|
|
672
676
|
'group.new.empty': "Saisissez un nom de groupe",
|
|
673
677
|
'group.new.root': "Niveau supérieur",
|
|
678
|
+
'group.new.independent': "Nouveau groupe de premier niveau",
|
|
674
679
|
'group.new.pickWorkspace': "Choisissez un espace de travail",
|
|
675
680
|
'group.new.noWorkspace': "Aucun espace de travail — ajoutez-en un d’abord",
|
|
676
681
|
'group.new.pickFirst': "Choisissez d’abord un espace de travail",
|
|
@@ -799,6 +804,7 @@ window.__ModuleLoader__.load({
|
|
|
799
804
|
'group.new.nameHint': "\"/\" और गहरा स्तर बनाता है, जैसे frontend/components",
|
|
800
805
|
'group.new.empty': "समूह का नाम दर्ज करें",
|
|
801
806
|
'group.new.root': "शीर्ष स्तर",
|
|
807
|
+
'group.new.independent': "नया शीर्ष-स्तरीय समूह",
|
|
802
808
|
'group.new.pickWorkspace': "एक कार्यस्थान चुनें",
|
|
803
809
|
'group.new.noWorkspace': "अभी कोई कार्यस्थान नहीं — पहले एक जोड़ें",
|
|
804
810
|
'group.new.pickFirst': "पहले एक कार्यस्थान चुनें",
|
|
@@ -927,6 +933,7 @@ window.__ModuleLoader__.load({
|
|
|
927
933
|
'group.new.nameHint': "\"/\" membuat tingkat lebih dalam, mis. frontend/components",
|
|
928
934
|
'group.new.empty': "Masukkan nama grup",
|
|
929
935
|
'group.new.root': "Tingkat teratas",
|
|
936
|
+
'group.new.independent': "Grup tingkat teratas baru",
|
|
930
937
|
'group.new.pickWorkspace': "Pilih ruang kerja",
|
|
931
938
|
'group.new.noWorkspace': "Belum ada ruang kerja — tambahkan dulu",
|
|
932
939
|
'group.new.pickFirst': "Pilih ruang kerja dulu",
|
|
@@ -1055,6 +1062,7 @@ window.__ModuleLoader__.load({
|
|
|
1055
1062
|
'group.new.nameHint': "Una «/» annida ulteriormente, es. frontend/components",
|
|
1056
1063
|
'group.new.empty': "Inserisci un nome di gruppo",
|
|
1057
1064
|
'group.new.root': "Livello superiore",
|
|
1065
|
+
'group.new.independent': "Nuovo gruppo di primo livello",
|
|
1058
1066
|
'group.new.pickWorkspace': "Scegli un’area di lavoro",
|
|
1059
1067
|
'group.new.noWorkspace': "Nessuna area di lavoro — aggiungine una prima",
|
|
1060
1068
|
'group.new.pickFirst': "Scegli prima un’area di lavoro",
|
|
@@ -1183,6 +1191,7 @@ window.__ModuleLoader__.load({
|
|
|
1183
1191
|
'group.new.nameHint': "/ でさらに深い階層になります(例: frontend/components)",
|
|
1184
1192
|
'group.new.empty': "グループ名を入力してください",
|
|
1185
1193
|
'group.new.root': "最上位",
|
|
1194
|
+
'group.new.independent': "最上位に新しいグループ",
|
|
1186
1195
|
'group.new.pickWorkspace': "ワークスペースを選択",
|
|
1187
1196
|
'group.new.noWorkspace': "ワークスペースがありません。先に追加してください",
|
|
1188
1197
|
'group.new.pickFirst': "先にワークスペースを選択してください",
|
|
@@ -1311,6 +1320,7 @@ window.__ModuleLoader__.load({
|
|
|
1311
1320
|
'group.new.nameHint': "\"/\"로 더 깊은 단계, 예: frontend/components",
|
|
1312
1321
|
'group.new.empty': "그룹 이름을 입력하세요",
|
|
1313
1322
|
'group.new.root': "최상위",
|
|
1323
|
+
'group.new.independent': "최상위 새 그룹",
|
|
1314
1324
|
'group.new.pickWorkspace': "워크스페이스 선택",
|
|
1315
1325
|
'group.new.noWorkspace': "워크스페이스가 없습니다. 먼저 추가하세요",
|
|
1316
1326
|
'group.new.pickFirst': "먼저 워크스페이스를 선택하세요",
|
|
@@ -1439,6 +1449,7 @@ window.__ModuleLoader__.load({
|
|
|
1439
1449
|
'group.new.nameHint': "Een \"/\" nest dieper, bijv. frontend/components",
|
|
1440
1450
|
'group.new.empty': "Voer een groepsnaam in",
|
|
1441
1451
|
'group.new.root': "Bovenste niveau",
|
|
1452
|
+
'group.new.independent': "Nieuwe groep op hoogste niveau",
|
|
1442
1453
|
'group.new.pickWorkspace': "Kies een werkruimte",
|
|
1443
1454
|
'group.new.noWorkspace': "Nog geen werkruimten — voeg er eerst een toe",
|
|
1444
1455
|
'group.new.pickFirst': "Kies eerst een werkruimte",
|
|
@@ -1567,6 +1578,7 @@ window.__ModuleLoader__.load({
|
|
|
1567
1578
|
'group.new.nameHint': "„/” zagnieżdża głębiej, np. frontend/components",
|
|
1568
1579
|
'group.new.empty': "Podaj nazwę grupy",
|
|
1569
1580
|
'group.new.root': "Najwyższy poziom",
|
|
1581
|
+
'group.new.independent': "Nowa grupa najwyższego poziomu",
|
|
1570
1582
|
'group.new.pickWorkspace': "Wybierz obszar roboczy",
|
|
1571
1583
|
'group.new.noWorkspace': "Brak obszarów roboczych — najpierw dodaj jeden",
|
|
1572
1584
|
'group.new.pickFirst': "Najpierw wybierz obszar roboczy",
|
|
@@ -1695,6 +1707,7 @@ window.__ModuleLoader__.load({
|
|
|
1695
1707
|
'group.new.nameHint': "Uma \"/\" aninha mais, ex. frontend/components",
|
|
1696
1708
|
'group.new.empty': "Digite um nome de grupo",
|
|
1697
1709
|
'group.new.root': "Nível superior",
|
|
1710
|
+
'group.new.independent': "Novo grupo de nível superior",
|
|
1698
1711
|
'group.new.pickWorkspace': "Escolha uma área de trabalho",
|
|
1699
1712
|
'group.new.noWorkspace': "Ainda não há áreas de trabalho — adicione uma primeiro",
|
|
1700
1713
|
'group.new.pickFirst': "Escolha uma área de trabalho primeiro",
|
|
@@ -1823,6 +1836,7 @@ window.__ModuleLoader__.load({
|
|
|
1823
1836
|
'group.new.nameHint': "«/» вкладывает глубже, напр. frontend/components",
|
|
1824
1837
|
'group.new.empty': "Введите имя группы",
|
|
1825
1838
|
'group.new.root': "Верхний уровень",
|
|
1839
|
+
'group.new.independent': "Новая группа верхнего уровня",
|
|
1826
1840
|
'group.new.pickWorkspace': "Выберите рабочую область",
|
|
1827
1841
|
'group.new.noWorkspace': "Рабочих областей пока нет — сначала добавьте одну",
|
|
1828
1842
|
'group.new.pickFirst': "Сначала выберите рабочую область",
|
|
@@ -1951,6 +1965,7 @@ window.__ModuleLoader__.load({
|
|
|
1951
1965
|
'group.new.nameHint': "Ett \"/\" nästlar djupare, t.ex. frontend/components",
|
|
1952
1966
|
'group.new.empty': "Ange ett gruppnamn",
|
|
1953
1967
|
'group.new.root': "Översta nivån",
|
|
1968
|
+
'group.new.independent': "Ny grupp på översta nivån",
|
|
1954
1969
|
'group.new.pickWorkspace': "Välj en arbetsyta",
|
|
1955
1970
|
'group.new.noWorkspace': "Inga arbetsytor än — lägg till en först",
|
|
1956
1971
|
'group.new.pickFirst': "Välj en arbetsyta först",
|
|
@@ -2079,6 +2094,7 @@ window.__ModuleLoader__.load({
|
|
|
2079
2094
|
'group.new.nameHint': "/ ใช้ซ้อนระดับลึกขึ้น เช่น frontend/components",
|
|
2080
2095
|
'group.new.empty': "กรอกชื่อกลุ่ม",
|
|
2081
2096
|
'group.new.root': "ระดับบนสุด",
|
|
2097
|
+
'group.new.independent': "กลุ่มระดับบนสุดใหม่",
|
|
2082
2098
|
'group.new.pickWorkspace': "เลือกเวิร์กสเปซ",
|
|
2083
2099
|
'group.new.noWorkspace': "ยังไม่มีเวิร์กสเปซ — เพิ่มก่อน",
|
|
2084
2100
|
'group.new.pickFirst': "เลือกเวิร์กสเปซก่อน",
|
|
@@ -2207,6 +2223,7 @@ window.__ModuleLoader__.load({
|
|
|
2207
2223
|
'group.new.nameHint': "\"/\" daha derin iç içe koyar, örn. frontend/components",
|
|
2208
2224
|
'group.new.empty': "Bir grup adı girin",
|
|
2209
2225
|
'group.new.root': "En üst düzey",
|
|
2226
|
+
'group.new.independent': "Yeni üst düzey grup",
|
|
2210
2227
|
'group.new.pickWorkspace': "Bir çalışma alanı seçin",
|
|
2211
2228
|
'group.new.noWorkspace': "Henüz çalışma alanı yok — önce bir tane ekleyin",
|
|
2212
2229
|
'group.new.pickFirst': "Önce bir çalışma alanı seçin",
|
|
@@ -2335,6 +2352,7 @@ window.__ModuleLoader__.load({
|
|
|
2335
2352
|
'group.new.nameHint': "\"/\" lồng sâu hơn, ví dụ frontend/components",
|
|
2336
2353
|
'group.new.empty': "Nhập tên nhóm",
|
|
2337
2354
|
'group.new.root': "Cấp cao nhất",
|
|
2355
|
+
'group.new.independent': "Nhóm cấp cao nhất mới",
|
|
2338
2356
|
'group.new.pickWorkspace': "Chọn một không gian làm việc",
|
|
2339
2357
|
'group.new.noWorkspace': "Chưa có không gian làm việc — hãy thêm trước",
|
|
2340
2358
|
'group.new.pickFirst': "Chọn không gian làm việc trước",
|
|
@@ -2463,6 +2481,7 @@ window.__ModuleLoader__.load({
|
|
|
2463
2481
|
'group.new.nameHint': "名入面嘅 / 即嵌套層級,例如 前端/組件",
|
|
2464
2482
|
'group.new.empty': "請輸入分組名",
|
|
2465
2483
|
'group.new.root': "最頂層",
|
|
2484
|
+
'group.new.independent': "新增頂層分組",
|
|
2466
2485
|
'group.new.pickWorkspace': "揀一個工作區",
|
|
2467
2486
|
'group.new.noWorkspace': "仲未有工作區,請先加一個",
|
|
2468
2487
|
'group.new.pickFirst': "請先揀一個工作區",
|
|
@@ -2591,6 +2610,7 @@ window.__ModuleLoader__.load({
|
|
|
2591
2610
|
'group.new.nameHint': "名入面嘅 / 即嵌套層級,例如 前端/組件",
|
|
2592
2611
|
'group.new.empty': "請輸入分組名",
|
|
2593
2612
|
'group.new.root': "最頂層",
|
|
2613
|
+
'group.new.independent': "新增頂層分組",
|
|
2594
2614
|
'group.new.pickWorkspace': "揀一個工作區",
|
|
2595
2615
|
'group.new.noWorkspace': "仲未有工作區,請先加一個",
|
|
2596
2616
|
'group.new.pickFirst': "請先揀一個工作區",
|
|
@@ -2719,6 +2739,7 @@ window.__ModuleLoader__.load({
|
|
|
2719
2739
|
'group.new.nameHint': "名稱中的 / 即巢狀層級,例如 前端/元件",
|
|
2720
2740
|
'group.new.empty': "請輸入分組名稱",
|
|
2721
2741
|
'group.new.root': "最頂層",
|
|
2742
|
+
'group.new.independent': "新增頂層分組",
|
|
2722
2743
|
'group.new.pickWorkspace': "選擇一個工作區",
|
|
2723
2744
|
'group.new.noWorkspace': "還沒有工作區,請先新增一個",
|
|
2724
2745
|
'group.new.pickFirst': "請先選擇一個工作區",
|
|
@@ -3101,6 +3122,14 @@ window.__ModuleLoader__.load({
|
|
|
3101
3122
|
return Array.isArray(raw) && raw.length > 0
|
|
3102
3123
|
}
|
|
3103
3124
|
|
|
3125
|
+
/**
|
|
3126
|
+
* Display name of a tree folder node. compressTree materializes a
|
|
3127
|
+
* single-child chain as a `kind:'ws'` row, and THAT shape carries `segs`
|
|
3128
|
+
* but no `name` — reading .name alone rendered a blank row (a lone
|
|
3129
|
+
* "E" above its only member F came out empty).
|
|
3130
|
+
*/
|
|
3131
|
+
const folderLabelOf = (node) => String(node.name || (Array.isArray(node.segs) ? node.segs.join('/') : '') || node.path || '')
|
|
3132
|
+
|
|
3104
3133
|
const explicitSegsOf = (explicit, scope) => {
|
|
3105
3134
|
const raw = explicit && typeof explicit === 'object' ? explicit[String(scope)] : undefined
|
|
3106
3135
|
const out = []
|
|
@@ -3294,6 +3323,15 @@ window.__ModuleLoader__.load({
|
|
|
3294
3323
|
'.bw-seg-btn-on{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.16));color:var(--dsw-alias-label-primary,#e6e6e6);font-weight:500}',
|
|
3295
3324
|
'.bw-seg-btn+.bw-seg-btn{border-left:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.25))}',
|
|
3296
3325
|
'.bw-gn-hint{font-size:12px;color:var(--dsw-alias-label-tertiary,#9a9a9a)}',
|
|
3326
|
+
// A container-only node (a workspace name-group in the session layer):
|
|
3327
|
+
// still expandable, but it carries no workspaceId to file against.
|
|
3328
|
+
// The trailing "new top-level group" row: separated from the containers
|
|
3329
|
+
// above it, because it belongs to none of them.
|
|
3330
|
+
'.bw-gn-row-new{margin-top:4px;border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.22));border-radius:0 0 6px 6px;padding-top:4px}',
|
|
3331
|
+
'.bw-gn-row-new .bw-gn-label{color:var(--dsw-alias-label-secondary,#b8b8b8)}',
|
|
3332
|
+
'.bw-gn-row-hold{cursor:default}',
|
|
3333
|
+
'.bw-gn-row-hold:hover{background:transparent}',
|
|
3334
|
+
'.bw-gn-row-hold .bw-gn-label{color:var(--dsw-alias-label-secondary,#b8b8b8)}',
|
|
3297
3335
|
'.bw-gn-empty{font-size:12px;color:var(--dsw-alias-label-tertiary,#9a9a9a);padding:10px 8px;text-align:center}',
|
|
3298
3336
|
'.bw-gn-tree{max-height:250px;overflow:auto;padding:4px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.22));border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.06))}',
|
|
3299
3337
|
'.bw-gn-row{display:flex;align-items:center;gap:6px;min-height:26px;padding-right:8px;border-radius:6px;cursor:pointer;user-select:none}',
|
|
@@ -3829,7 +3867,7 @@ window.__ModuleLoader__.load({
|
|
|
3829
3867
|
* writes the declaration itself, which is why it is not the move dialog.
|
|
3830
3868
|
*/
|
|
3831
3869
|
function GroupNewDialog(props) {
|
|
3832
|
-
const { kinds, initialKind, treeOf, onConfirm, onClose, t } = props
|
|
3870
|
+
const { kinds, initialKind, treeOf, label, onConfirm, onClose, t } = props
|
|
3833
3871
|
const [kind, setKind] = React.useState(initialKind)
|
|
3834
3872
|
const [picked, setPicked] = React.useState(null)
|
|
3835
3873
|
const [openKeys, setOpenKeys] = React.useState({})
|
|
@@ -3837,7 +3875,7 @@ window.__ModuleLoader__.load({
|
|
|
3837
3875
|
const [error, setError] = React.useState('')
|
|
3838
3876
|
const plan = treeOf(kind)
|
|
3839
3877
|
const toggle = (id) => setOpenKeys((prev) => ({ ...prev, [id]: !prev[id] }))
|
|
3840
|
-
const pick = (node) => { setPicked(node); setError('') }
|
|
3878
|
+
const pick = (node) => { if (node.pickable === false) return; setPicked(node); setError('') }
|
|
3841
3879
|
const submit = () => {
|
|
3842
3880
|
const clean = String(name).split('/').filter((s) => s !== '').join('/')
|
|
3843
3881
|
if (clean === '') { setError(t('group.new.empty')); return }
|
|
@@ -3854,7 +3892,7 @@ window.__ModuleLoader__.load({
|
|
|
3854
3892
|
const expanded = openKeys[node.id] === true || (depth === 0 && openKeys[node.id] !== false)
|
|
3855
3893
|
out.push(E('div', {
|
|
3856
3894
|
key: 'n-' + node.id,
|
|
3857
|
-
className: cls('bw-gn-row', picked && picked.id === node.id && 'bw-gn-row-on'),
|
|
3895
|
+
className: cls('bw-gn-row', node.pickable === false && 'bw-gn-row-hold', node.kind === 'new' && 'bw-gn-row-new', picked && picked.id === node.id && 'bw-gn-row-on'),
|
|
3858
3896
|
style: { paddingLeft: 6 + depth * 14 },
|
|
3859
3897
|
role: 'treeitem',
|
|
3860
3898
|
'aria-selected': picked && picked.id === node.id ? 'true' : 'false',
|
|
@@ -3867,7 +3905,10 @@ window.__ModuleLoader__.load({
|
|
|
3867
3905
|
'aria-label': node.label,
|
|
3868
3906
|
onClick: (e) => { e.stopPropagation(); toggle(node.id) },
|
|
3869
3907
|
}, E('span', { className: 'bw-chevron' })) : E('span', { className: 'bw-gn-twist' }),
|
|
3870
|
-
E('span', { className: 'bw-gn-icon' },
|
|
3908
|
+
E('span', { className: 'bw-gn-icon' },
|
|
3909
|
+
node.kind === 'new' ? icon('IconPlusOutline16', 15)
|
|
3910
|
+
: node.kind === 'ws' ? icon('IconFolderOpen16', 15)
|
|
3911
|
+
: icon('IconFolderClose16', 15)),
|
|
3871
3912
|
E('span', { className: 'bw-gn-label' }, node.label),
|
|
3872
3913
|
))
|
|
3873
3914
|
if (hasKids && expanded) out.push(...renderNodes(node.children, depth + 1))
|
|
@@ -3878,7 +3919,7 @@ window.__ModuleLoader__.load({
|
|
|
3878
3919
|
open: true,
|
|
3879
3920
|
onClose,
|
|
3880
3921
|
closeLabel: t('close'),
|
|
3881
|
-
title:
|
|
3922
|
+
title: label,
|
|
3882
3923
|
footer: E('div', { className: 'bw-modal-actions' },
|
|
3883
3924
|
E(BTN, { variant: 'outline', onClick: onClose }, t('cancel')),
|
|
3884
3925
|
E(BTN, { variant: 'primary', onClick: submit }, t('confirm')),
|
|
@@ -5165,6 +5206,13 @@ window.__ModuleLoader__.load({
|
|
|
5165
5206
|
if (!compactChains || draggingWorkspace) return built
|
|
5166
5207
|
return { ...built, folders: built.folders.map((f) => materializeChain(compressTree(f))), workspaces: built.workspaces }
|
|
5167
5208
|
}, [items, compactChains, draggingWorkspace, workspaceSlash, wsFolders])
|
|
5209
|
+
// UNCOMPRESSED tree, for the new-group dialog only. compressTree merges a
|
|
5210
|
+
// single-child chain into one row that stands for BOTH a group and the
|
|
5211
|
+
// workspace under it — fine for drawing, useless for choosing a
|
|
5212
|
+
// container: one half must stay unselectable and the other must be
|
|
5213
|
+
// selectable, and a merged row cannot be both. (It also carried no
|
|
5214
|
+
// `name`, which is how a lone "E" rendered blank.)
|
|
5215
|
+
const rawTree = React.useMemo(() => buildTree(items, workspaceSlash, wsFolders), [items, workspaceSlash, wsFolders])
|
|
5168
5216
|
// Disk-layer ancestry for drag semantics (same derivation as buildTree).
|
|
5169
5217
|
const diskParentMap = React.useMemo(() => diskParentMapOf(items), [items])
|
|
5170
5218
|
|
|
@@ -5366,40 +5414,81 @@ window.__ModuleLoader__.load({
|
|
|
5366
5414
|
// write target: `scope` is the store's bags key (the disk level's idPrefix
|
|
5367
5415
|
// for workspaces, the workspaceId for sessions) and `path` is the parent
|
|
5368
5416
|
// group's name path — the dialog appends the new name to it.
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
}
|
|
5380
|
-
return out
|
|
5381
|
-
}
|
|
5417
|
+
// Workspace-GROUP containers: name-group folders only, never the
|
|
5418
|
+
// workspaces themselves. A workspace title is "group/leaf", so with
|
|
5419
|
+
// members A/B, A/C, D and E/F the only groups that exist are A and E —
|
|
5420
|
+
// B, C, D and F are leaves (they are workspaces, not containers), and
|
|
5421
|
+
// offering them would invite a group named after a leaf. Disk nesting is
|
|
5422
|
+
// a workspace fact too, so it is skipped for the same reason.
|
|
5423
|
+
const wsFolderNodesOf = (node) => (node.folders || []).map((f) => ({
|
|
5424
|
+
id: f.scope + '|' + f.path, scope: f.scope, path: f.path, label: folderLabelOf(f), kind: 'folder',
|
|
5425
|
+
children: wsFolderNodesOf(f),
|
|
5426
|
+
}))
|
|
5382
5427
|
const sessGroupNodesOf = (node, workspaceId) => (node.groups || []).map((g) => ({
|
|
5383
5428
|
id: workspaceId + '|' + g.path, scope: workspaceId, path: g.path, label: g.name, kind: 'sgroup',
|
|
5384
5429
|
children: sessGroupNodesOf(g, workspaceId),
|
|
5385
5430
|
}))
|
|
5386
5431
|
const newGroupTreeOf = (kind) => {
|
|
5432
|
+
// The container tree MIRRORS the sidebar's current view mode (v0.17.1):
|
|
5433
|
+
// "workspace tree" offers the real hierarchy (disk nesting + name
|
|
5434
|
+
// groups); the one-level modes ("workspaces" / "flat") render no
|
|
5435
|
+
// hierarchy to mirror, so they fall back to a flat workspace list.
|
|
5436
|
+
// Offering a nesting the sidebar is not showing would be a lie about
|
|
5437
|
+
// where the new group lands.
|
|
5438
|
+
const treeView = groupBy === 'workspace-tree'
|
|
5387
5439
|
if (kind === 'workspace') {
|
|
5388
5440
|
// "Top level" IS a real target here (a group at the root of the
|
|
5389
5441
|
// workspace tree), so it is a node like any other.
|
|
5390
|
-
|
|
5442
|
+
// A dedicated top-level row, not a root node that both expands AND
|
|
5443
|
+
// accepts the new name: that read as "pick a spot inside the top
|
|
5444
|
+
// level", and it left no way to say "give me a group NEXT TO A and
|
|
5445
|
+
// E". The row below is that way, and A / E sit beside it as the
|
|
5446
|
+
// top-level groups they actually are.
|
|
5447
|
+
const groups = treeView ? wsFolderNodesOf(rawTree) : []
|
|
5448
|
+
return {
|
|
5449
|
+
hint: t('group.new.where'),
|
|
5450
|
+
// The "top level" row sits LAST (user's call): it reads as "and
|
|
5451
|
+
// here is one more, belonging to none of the above" instead of as
|
|
5452
|
+
// a header the others hang under.
|
|
5453
|
+
nodes: [...groups, { id: '@top', scope: '', path: '', label: t('group.new.independent'), kind: 'new', children: [] }],
|
|
5454
|
+
}
|
|
5391
5455
|
}
|
|
5392
5456
|
// A session group lives INSIDE one workspace (folders.sess is keyed by
|
|
5393
|
-
// workspaceId), so there is no "top level" to declare into
|
|
5394
|
-
// workspace
|
|
5395
|
-
//
|
|
5396
|
-
//
|
|
5397
|
-
|
|
5457
|
+
// workspaceId), so there is no "top level" to declare into — the
|
|
5458
|
+
// workspace tree IS the top level, introduced by a plain hint rather
|
|
5459
|
+
// than wrapped in a fake root node (a header row that looked like a
|
|
5460
|
+
// tree node read as a container you could pick).
|
|
5461
|
+
//
|
|
5462
|
+
// The walk follows the SAME tree the sidebar renders (disk nesting
|
|
5463
|
+
// included), not the flat registry list: a child workspace must appear
|
|
5464
|
+
// nested under its parent exactly as it does in "workspace tree" view.
|
|
5465
|
+
// Name-group folders are containers here — a session group cannot be
|
|
5466
|
+
// filed against one (they carry no workspaceId) — so they expand but
|
|
5467
|
+
// refuse selection.
|
|
5468
|
+
const sessNodesOf = (node) => {
|
|
5469
|
+
const out = []
|
|
5470
|
+
for (const f of node.folders || []) {
|
|
5471
|
+
out.push({ id: f.scope + '|' + f.path, scope: '', path: '', label: folderLabelOf(f), kind: 'folder', pickable: false, children: sessNodesOf(f) })
|
|
5472
|
+
}
|
|
5473
|
+
for (const w of node.workspaces || []) {
|
|
5474
|
+
const inner = sessGroupNodesOf(buildSessionTree(sessionsOf(w), sessionSlash, sessFolders, w.workspaceId), w.workspaceId)
|
|
5475
|
+
const subNodes = w.sub ? sessNodesOf(w.sub) : []
|
|
5476
|
+
out.push({
|
|
5477
|
+
id: w.workspaceId + '|', scope: w.workspaceId, path: '', label: w.leaf || w.title, kind: 'ws',
|
|
5478
|
+
children: [...inner, ...subNodes],
|
|
5479
|
+
})
|
|
5480
|
+
}
|
|
5481
|
+
return out
|
|
5482
|
+
}
|
|
5483
|
+
if (treeView) return { hint: t('group.new.pickWorkspace'), nodes: sessNodesOf(rawTree) }
|
|
5484
|
+
// One-level modes: every workspace sits at the root, exactly as the
|
|
5485
|
+
// sidebar draws it (raw items — there is no .sub level to nest here).
|
|
5486
|
+
const flat = []
|
|
5398
5487
|
for (const w of items) {
|
|
5399
5488
|
const inner = sessGroupNodesOf(buildSessionTree(sessionsOf(w), sessionSlash, sessFolders, w.workspaceId), w.workspaceId)
|
|
5400
|
-
|
|
5489
|
+
flat.push({ id: w.workspaceId + '|', scope: w.workspaceId, path: '', label: w.leaf || w.title, kind: 'ws', children: inner })
|
|
5401
5490
|
}
|
|
5402
|
-
return { hint: t('group.new.pickWorkspace'), nodes }
|
|
5491
|
+
return { hint: t('group.new.pickWorkspace'), nodes: flat }
|
|
5403
5492
|
}
|
|
5404
5493
|
/**
|
|
5405
5494
|
* Picker entries for the header button: every workspace, then its
|
|
@@ -6229,6 +6318,10 @@ window.__ModuleLoader__.load({
|
|
|
6229
6318
|
kinds: newGroupLayers(),
|
|
6230
6319
|
initialKind: dialog.layer === 'workspace' ? 'workspace' : 'session',
|
|
6231
6320
|
treeOf: newGroupTreeOf,
|
|
6321
|
+
// The heading names the layer(s) the button promised: with both
|
|
6322
|
+
// layers live the switch below decides, so the generic wording is
|
|
6323
|
+
// accurate there and only there.
|
|
6324
|
+
label: newGroupLabel(),
|
|
6232
6325
|
onConfirm: (kind, scope, path) => { actions.addFolder(kind, scope, path); setDialog(null) },
|
|
6233
6326
|
onClose: () => setDialog(null),
|
|
6234
6327
|
t,
|