dsh-better-workspace 0.27.2 → 0.30.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 CHANGED
@@ -55,7 +55,7 @@
55
55
  <table>
56
56
  <tr>
57
57
  <td align="center" width="58%"><img src="docs/screenshots/5-context-menu.png" alt="右键菜单"/></td>
58
- <td valign="top"><b>右键菜单</b><br/>操作都收进右键:工作区行——重命名 / 删除 / 分叉 / 归档 / 自定义外观;工作区分组行——重命名分组(同步更新组内所有工作区名称)/ 自定义外观;会话与会话分组行——重命名 / 分叉 / 归档 / 自定义外观。</td>
58
+ <td valign="top"><b>右键菜单</b><br/>操作都收进右键:工作区行——重命名 / 删除 / 分叉 / 归档 / 自定义外观;工作区分组行——重命名分组(同步更新组内所有工作区名称)/ 自定义外观;会话与会话分组行——重命名 / 分叉 / 归档 / 自定义外观;会话行另有<b>复制 Session ID</b>(v0.30.0 起,对齐 dsh 0.2.1-alpha.5 官方会话菜单第五项——写剪贴板走宿主原语,复制成功与否均有提示)。</td>
59
59
  </tr>
60
60
  </table>
61
61
 
package/README_EN.md CHANGED
@@ -55,7 +55,7 @@ Session titles nest on `/` too (e.g. `test1/plugin maintenance`). Session groups
55
55
  <table>
56
56
  <tr>
57
57
  <td align="center" width="58%"><img src="docs/screenshots/5-context-menu.png" alt="Context menu"/></td>
58
- <td valign="top"><b>Context menu everywhere</b><br/>Actions live in the right-click menu: workspace rows — rename / delete / fork / archive / customize; folder rows — rename group (updates all descendant workspaces) / customize; session &amp; session-group rows — rename / fork / archive / customize.</td>
58
+ <td valign="top"><b>Context menu everywhere</b><br/>Actions live in the right-click menu: workspace rows — rename / delete / fork / archive / customize; folder rows — rename group (updates all descendant workspaces) / customize; session &amp; session-group rows — rename / fork / archive / customize; session rows also get <b>Copy Session ID</b> (v0.30.0, mirroring dsh 0.2.1-alpha.5's fifth official menu entry — the clipboard write rides the host primitive, with a notice either way).</td>
59
59
  </tr>
60
60
  </table>
61
61
 
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-better-workspace",
3
- "version": "0.27.2",
3
+ "version": "0.30.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; the New Session workspace picker shows the same tree.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-better-workspace",
3
- "version": "0.27.2",
3
+ "version": "0.30.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), and the same tree in the New Session workspace picker; add-workspace rides the official directory flow.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/client.js CHANGED
@@ -27,6 +27,10 @@ window.__ModuleLoader__.load({
27
27
 
28
28
  const E = React.createElement
29
29
  const NS = 'betterWorkspace'
30
+ // Official transient banner (dsh 0.2.1-alpha.5 primitives Toast; body
31
+ // portal, self-fading). Absent on older hosts — the copy-session-id
32
+ // notice then falls back to this plugin's own chip.
33
+ const officialToast = typeof ui.Toast === 'function' ? ui.Toast : null
30
34
 
31
35
  /* ============================== i18n ============================== */
32
36
 
@@ -81,6 +85,9 @@ window.__ModuleLoader__.load({
81
85
  'menu.renameSgroup': '重命名会话分组',
82
86
  'menu.moveToGroup': '移动到分组…',
83
87
  'menu.moveOutGroup': '移出分组',
88
+ 'menu.sessionId': '复制 Session ID',
89
+ 'toast.sessionIdCopied': 'Session ID 已复制',
90
+ 'toast.sessionIdCopyFailed': '复制失败,请重试',
84
91
  'settings.title': '更好的工作区',
85
92
  'settings.desc': '工作区树的外观与折叠偏好',
86
93
  'settings.expand': '展开',
@@ -88,6 +95,7 @@ window.__ModuleLoader__.load({
88
95
  'settings.group.tree': '树与列表',
89
96
  'settings.group.interaction': '会话交互',
90
97
  'settings.group.appearance': '外观',
98
+ 'settings.group.sync': '跨端同步',
91
99
  'settings.compactChains': '单链分组折叠显示',
92
100
  'settings.compactChains.hint': '单层链合并为一行,出现多个子级时自动展开为树状;拖拽工作区期间单链临时展开回文件夹树,可放入任意一级;展开状态与自定义外观保存在当前浏览器。',
93
101
  'settings.statusPulse': '状态呼吸灯',
@@ -134,7 +142,6 @@ window.__ModuleLoader__.load({
134
142
  'custom.done': '完成',
135
143
  'settings.on': '开',
136
144
  'settings.off': '关',
137
- 'sync.title': '跨端同步',
138
145
  'sync.desc': "外观自定义与开关保存在本设备的浏览器里;通过宿主设置存储与另一端互传。平时的新修改会自动写入宿主,另一端点「获取」即可拿到;旧版本的历史数据首次需要点一次「发送」。",
139
146
  'sync.mode.overwrite': '覆盖本设备',
140
147
  'sync.mode.merge': '合并两端',
@@ -234,6 +241,9 @@ window.__ModuleLoader__.load({
234
241
  'menu.renameSgroup': 'Rename session group',
235
242
  'menu.moveToGroup': 'Move to group…',
236
243
  'menu.moveOutGroup': 'Move out of group',
244
+ 'menu.sessionId': 'Copy Session ID',
245
+ 'toast.sessionIdCopied': 'Session ID copied',
246
+ 'toast.sessionIdCopyFailed': 'Copy failed. Try again.',
237
247
  'settings.title': 'Better Workspaces',
238
248
  'settings.desc': 'Workspace tree appearance and folding preferences',
239
249
  'settings.expand': 'Expand',
@@ -241,6 +251,7 @@ window.__ModuleLoader__.load({
241
251
  'settings.group.tree': 'Tree & list',
242
252
  'settings.group.interaction': 'Session interaction',
243
253
  'settings.group.appearance': 'Appearance',
254
+ 'settings.group.sync': 'Cross-device sync',
244
255
  'settings.compactChains': 'Merge single-child chains',
245
256
  'settings.compactChains.hint': 'Single-child chains merge into one row; levels with multiple children expand as a tree. Chains re-expand into folder rows while you drag a workspace, so it can drop into any level. State and custom styling persist in this browser.',
246
257
  'settings.statusPulse': 'Status breathing light',
@@ -287,7 +298,6 @@ window.__ModuleLoader__.load({
287
298
  'custom.done': 'Done',
288
299
  'settings.on': 'On',
289
300
  'settings.off': 'Off',
290
- 'sync.title': 'Cross-device sync',
291
301
  'sync.desc': "Appearance customization and toggles live in this device's browser; they exchange with the other surface (web / desktop app) through the host settings store. New edits are written to the host automatically — the other surface just pulls. History created before this version needs one explicit Send.",
292
302
  'sync.mode.overwrite': 'Overwrite this device',
293
303
  'sync.mode.merge': 'Merge both sides',
@@ -395,6 +405,9 @@ window.__ModuleLoader__.load({
395
405
  'menu.renameSgroup': 'إعادة تسمية مجموعة الجلسات',
396
406
  'menu.moveToGroup': 'نقل إلى مجموعة…',
397
407
  'menu.moveOutGroup': 'إخراج من المجموعة',
408
+ 'menu.sessionId': 'نسخ معرّف الجلسة',
409
+ 'toast.sessionIdCopied': 'تم نسخ معرّف الجلسة',
410
+ 'toast.sessionIdCopyFailed': 'فشل النسخ، حاول مجددًا',
398
411
  'settings.title': 'مساحات عمل أفضل',
399
412
  'settings.desc': 'مظهر شجرة مساحات العمل وتفضيلات الطيّ',
400
413
  'settings.expand': 'توسيع',
@@ -402,6 +415,7 @@ window.__ModuleLoader__.load({
402
415
  'settings.group.tree': 'الشجرة والقائمة',
403
416
  'settings.group.interaction': 'تفاعل الجلسات',
404
417
  'settings.group.appearance': 'المظهر',
418
+ 'settings.group.sync': 'المزامنة بين الأجهزة',
405
419
  'settings.compactChains': 'دمج السلاسل ذات الفرع الواحد',
406
420
  'settings.compactChains.hint': 'تُدمج المستويات ذات الفرع الواحد في سطر واحد، وتتوسع الشجرة تلقائياً عند وجود أكثر من فرع؛ أثناء سحب مساحة عمل تُفكّ السلاسل مؤقتاً إلى مجلدات ليصبح الإفلات ممكناً في أي مستوى؛ تُحفظ حالة التوسيع والمظهر المخصص في هذا المتصفح.',
407
421
  'settings.statusPulse': 'مؤشر حالة نابض',
@@ -448,7 +462,6 @@ window.__ModuleLoader__.load({
448
462
  'custom.done': 'تم',
449
463
  'settings.on': 'تشغيل',
450
464
  'settings.off': 'إيقاف',
451
- 'sync.title': 'المزامنة بين الأجهزة',
452
465
  'sync.desc': "يعيش تخصيص المظهر والمفاتيح في متصفح هذا الجهاز، ويجري تبادلها مع الطرف الآخر (الويب / تطبيق سطح المكتب) عبر مخزن إعدادات المضيف. تُكتب التعديلات الجديدة في المضيف تلقائياً، ويكفي أن يضغط الطرف الآخر «جلب»؛ أما بيانات الإصدارات القديمة فتحتاج ضغطة «إرسال» واحدة.",
453
466
  'sync.mode.overwrite': 'الكتابة فوق هذا الجهاز',
454
467
  'sync.mode.merge': 'دمج الطرفين',
@@ -548,6 +561,9 @@ window.__ModuleLoader__.load({
548
561
  'menu.renameSgroup': 'Sitzungsgruppe umbenennen',
549
562
  'menu.moveToGroup': 'In Gruppe verschieben…',
550
563
  'menu.moveOutGroup': 'Aus der Gruppe entfernen',
564
+ 'menu.sessionId': 'Session-ID kopieren',
565
+ 'toast.sessionIdCopied': 'Session-ID kopiert',
566
+ 'toast.sessionIdCopyFailed': 'Kopieren fehlgeschlagen. Bitte erneut versuchen.',
551
567
  'settings.title': 'Bessere Arbeitsbereiche',
552
568
  'settings.desc': 'Aussehen und Einklappverhalten des Arbeitsbereich-Baums',
553
569
  'settings.expand': 'Ausklappen',
@@ -555,6 +571,7 @@ window.__ModuleLoader__.load({
555
571
  'settings.group.tree': 'Baum & Liste',
556
572
  'settings.group.interaction': 'Sitzungsinteraktion',
557
573
  'settings.group.appearance': 'Aussehen',
574
+ 'settings.group.sync': 'Geräteübergreifende Synchronisierung',
558
575
  'settings.compactChains': 'Einzelketten zusammenfassen',
559
576
  'settings.compactChains.hint': 'Ebenen mit genau einem Kind werden zu einer Zeile zusammengefasst; bei mehreren Kindern klappt der Baum auf; beim Ziehen eines Arbeitsbereichs klappen die Ketten vorübergehend zum Ordnerbaum auf, sodass jede Ebene als Ziel dient; Klappzustand und eigenes Aussehen bleiben in diesem Browser gespeichert.',
560
577
  'settings.statusPulse': 'Status-Pulslicht',
@@ -601,7 +618,6 @@ window.__ModuleLoader__.load({
601
618
  'custom.done': 'Fertig',
602
619
  'settings.on': 'An',
603
620
  'settings.off': 'Aus',
604
- 'sync.title': 'Geräteübergreifende Synchronisierung',
605
621
  'sync.desc': "Erscheinungsbild-Anpassungen und Schalter leben im Browser dieses Geräts; sie werden über den Host-Einstellungsspeicher mit der anderen Oberfläche (Web / Desktop-App) ausgetauscht. Neue Änderungen werden automatisch zum Host geschrieben — die andere Oberfläche ruft sie nur ab. Vor dieser Version entstandene Daten brauchen einmaliges Senden.",
606
622
  'sync.mode.overwrite': 'Dieses Gerät überschreiben',
607
623
  'sync.mode.merge': 'Beide Seiten zusammenführen',
@@ -701,6 +717,9 @@ window.__ModuleLoader__.load({
701
717
  'menu.renameSgroup': 'Renommer le groupe de sessions',
702
718
  'menu.moveToGroup': 'Déplacer vers le groupe…',
703
719
  'menu.moveOutGroup': 'Retirer du groupe',
720
+ 'menu.sessionId': 'Copier l’ID de session',
721
+ 'toast.sessionIdCopied': 'ID de session copié',
722
+ 'toast.sessionIdCopyFailed': 'Échec de la copie. Veuillez réessayer.',
704
723
  'settings.title': 'Espaces de travail améliorés',
705
724
  'settings.desc': 'Apparence et repli de l\'arborescence des espaces de travail',
706
725
  'settings.expand': 'Développer',
@@ -708,6 +727,7 @@ window.__ModuleLoader__.load({
708
727
  'settings.group.tree': 'Arborescence et liste',
709
728
  'settings.group.interaction': 'Interactions de session',
710
729
  'settings.group.appearance': 'Apparence',
730
+ 'settings.group.sync': 'Synchronisation entre appareils',
711
731
  'settings.compactChains': 'Fusionner les chaînes à un seul enfant',
712
732
  'settings.compactChains.hint': 'Les niveaux à un seul enfant se fusionnent en une ligne; dès qu\'il y a plusieurs enfants, l\'arborescence se déploie; pendant le glisser-déposer d\'un espace de travail, les chaînes se déploient temporairement en dossiers pour permettre le dépôt à n\'importe quel niveau; l\'état déplié et l\'apparence personnalisée sont conservés dans ce navigateur.',
713
733
  'settings.statusPulse': 'Voyant d\'état pulsant',
@@ -754,7 +774,6 @@ window.__ModuleLoader__.load({
754
774
  'custom.done': 'Terminé',
755
775
  'settings.on': 'Activé',
756
776
  'settings.off': 'Désactivé',
757
- 'sync.title': 'Synchronisation entre appareils',
758
777
  'sync.desc': "Les personnalisations d'apparence et les interrupteurs vivent dans le navigateur de cet appareil ; ils s'échangent avec l'autre surface (web / application de bureau) via le magasin de paramètres de l'hôte. Les nouvelles modifications sont écrites automatiquement vers l'hôte — l'autre surface se contente de récupérer. Les données antérieures à cette version nécessitent un envoi explicite.",
759
778
  'sync.mode.overwrite': 'Écraser cet appareil',
760
779
  'sync.mode.merge': 'Fusionner les deux côtés',
@@ -854,6 +873,9 @@ window.__ModuleLoader__.load({
854
873
  'menu.renameSgroup': 'सत्र समूह का नाम बदलें',
855
874
  'menu.moveToGroup': 'समूह में ले जाएँ…',
856
875
  'menu.moveOutGroup': 'समूह से बाहर निकालें',
876
+ 'menu.sessionId': 'सेशन आईडी कॉपी करें',
877
+ 'toast.sessionIdCopied': 'सेशन आईडी कॉपी हो गई',
878
+ 'toast.sessionIdCopyFailed': 'कॉपी विफल रहा, कृपया पुनः प्रयास करें',
857
879
  'settings.title': 'बेहतर कार्यस्थान',
858
880
  'settings.desc': 'कार्यस्थान वृक्ष का रूप और समेटने की प्राथमिकताएँ',
859
881
  'settings.expand': 'फैलाएँ',
@@ -861,6 +883,7 @@ window.__ModuleLoader__.load({
861
883
  'settings.group.tree': 'ट्री और सूची',
862
884
  'settings.group.interaction': 'सेशन इंटरैक्शन',
863
885
  'settings.group.appearance': 'उपस्थिति',
886
+ 'settings.group.sync': 'उपकरणों के बीच समन्वयन',
864
887
  'settings.compactChains': 'एकल-संतान श्रृंखलाएँ मिलाएँ',
865
888
  'settings.compactChains.hint': 'जिन स्तरों पर केवल एक संतान होती है वे एक पंक्ति में मिल जाते हैं; कई संतानें होने पर वृक्ष खुल जाता है; कार्यस्थान खींचते समय श्रृंखलाएँ अस्थायी रूप से फ़ोल्डरों में खुल जाती हैं ताकि किसी भी स्तर पर छोड़ा जा सके; खुलने की स्थिति और अनुकूलित रूप इसी ब्राउज़र में सहेजे जाते हैं।',
866
889
  'settings.statusPulse': 'स्थिति की साँस लेती बत्ती',
@@ -907,7 +930,6 @@ window.__ModuleLoader__.load({
907
930
  'custom.done': 'हो गया',
908
931
  'settings.on': 'चालू',
909
932
  'settings.off': 'बंद',
910
- 'sync.title': 'उपकरणों के बीच समन्वयन',
911
933
  'sync.desc': "रूप अनुकूलन और टॉगल इस डिवाइस के ब्राउज़र में रहते हैं; वे होस्ट सेटिंग्स स्टोर के माध्यम से दूसरी सतह (वेब / डेस्कटॉप ऐप) के साथ आदान-प्रदान होते हैं। नए बदलाव स्वचालित रूप से होस्ट पर लिखे जाते हैं — दूसरी सतह को बस खींचना है। इस संस्करण से पहले के डेटा को एक बार भेजना होगा।",
912
934
  'sync.mode.overwrite': 'इस उपकरण को अधिलेखित करें',
913
935
  'sync.mode.merge': 'दोनों सिरे मिलाएँ',
@@ -1007,6 +1029,9 @@ window.__ModuleLoader__.load({
1007
1029
  'menu.renameSgroup': 'Ganti nama grup sesi',
1008
1030
  'menu.moveToGroup': 'Pindahkan ke grup…',
1009
1031
  'menu.moveOutGroup': 'Keluarkan dari grup',
1032
+ 'menu.sessionId': 'Salin ID Sesi',
1033
+ 'toast.sessionIdCopied': 'ID Sesi disalin',
1034
+ 'toast.sessionIdCopyFailed': 'Gagal menyalin, silakan coba lagi',
1010
1035
  'settings.title': 'Ruang kerja yang lebih baik',
1011
1036
  'settings.desc': 'Tampilan dan pelipatan pohon ruang kerja',
1012
1037
  'settings.expand': 'Bentangkan',
@@ -1014,6 +1039,7 @@ window.__ModuleLoader__.load({
1014
1039
  'settings.group.tree': 'Pohon & daftar',
1015
1040
  'settings.group.interaction': 'Interaksi sesi',
1016
1041
  'settings.group.appearance': 'Tampilan',
1042
+ 'settings.group.sync': 'Sinkronisasi antar perangkat',
1017
1043
  'settings.compactChains': 'Gabungkan rantai beranak tunggal',
1018
1044
  'settings.compactChains.hint': 'Tingkat yang hanya punya satu anak digabung jadi satu baris; bila anaknya banyak, pohon terbentang; saat menyeret ruang kerja, rantai sementara terbentang kembali jadi folder sehingga bisa dijatuhkan di tingkat mana pun; status terbentang dan tampilan khusus disimpan di browser ini.',
1019
1045
  'settings.statusPulse': 'Lampu status bernapas',
@@ -1060,7 +1086,6 @@ window.__ModuleLoader__.load({
1060
1086
  'custom.done': 'Selesai',
1061
1087
  'settings.on': 'Aktif',
1062
1088
  'settings.off': 'Nonaktif',
1063
- 'sync.title': 'Sinkronisasi antar perangkat',
1064
1089
  'sync.desc': "Kustomisasi tampilan dan tombol pengalih tersimpan di peramban perangkat ini; keduanya bertukar dengan permukaan lain (web / aplikasi desktop) melalui penyimpanan pengaturan host. Perubahan baru ditulis ke host secara otomatis — permukaan lain tinggal menarik. Data dari sebelum versi ini perlu satu kali Kirim.",
1065
1090
  'sync.mode.overwrite': 'Timpa perangkat ini',
1066
1091
  'sync.mode.merge': 'Gabungkan kedua sisi',
@@ -1160,6 +1185,9 @@ window.__ModuleLoader__.load({
1160
1185
  'menu.renameSgroup': 'Rinomina gruppo di sessioni',
1161
1186
  'menu.moveToGroup': 'Sposta nel gruppo…',
1162
1187
  'menu.moveOutGroup': 'Rimuovi dal gruppo',
1188
+ 'menu.sessionId': 'Copia ID sessione',
1189
+ 'toast.sessionIdCopied': 'ID sessione copiato',
1190
+ 'toast.sessionIdCopyFailed': 'Copia non riuscita. Riprova.',
1163
1191
  'settings.title': 'Aree di lavoro migliori',
1164
1192
  'settings.desc': 'Aspetto e compressione dell\'albero delle aree di lavoro',
1165
1193
  'settings.expand': 'Espandi',
@@ -1167,6 +1195,7 @@ window.__ModuleLoader__.load({
1167
1195
  'settings.group.tree': 'Albero ed elenco',
1168
1196
  'settings.group.interaction': 'Interazione sessioni',
1169
1197
  'settings.group.appearance': 'Aspetto',
1198
+ 'settings.group.sync': 'Sincronizzazione tra dispositivi',
1170
1199
  'settings.compactChains': 'Unisci le catene a figlio unico',
1171
1200
  'settings.compactChains.hint': 'I livelli con un solo figlio si fondono in una riga; con più figli l\'albero si espande; durante il trascinamento di un\'area di lavoro le catene si riespandono temporaneamente in cartelle, così puoi rilasciare su qualsiasi livello; stato di espansione e aspetto personalizzato restano in questo browser.',
1172
1201
  'settings.statusPulse': 'Spia di stato pulsante',
@@ -1213,7 +1242,6 @@ window.__ModuleLoader__.load({
1213
1242
  'custom.done': 'Fine',
1214
1243
  'settings.on': 'Attivo',
1215
1244
  'settings.off': 'Disattivo',
1216
- 'sync.title': 'Sincronizzazione tra dispositivi',
1217
1245
  'sync.desc': "Le personalizzazioni dell'aspetto e gli interruttori vivono nel browser di questo dispositivo; si scambiano con l'altra superficie (web / app desktop) tramite l'archivio impostazioni dell'host. Le nuove modifiche vengono scritte automaticamente sull'host — l'altra superficie deve solo recuperarle. I dati precedenti a questa versione richiedono un invio esplicito.",
1218
1246
  'sync.mode.overwrite': 'Sovrascrivi questo dispositivo',
1219
1247
  'sync.mode.merge': 'Unisci i due lati',
@@ -1313,6 +1341,9 @@ window.__ModuleLoader__.load({
1313
1341
  'menu.renameSgroup': 'セッショングループ名を変更',
1314
1342
  'menu.moveToGroup': 'グループに移動…',
1315
1343
  'menu.moveOutGroup': 'グループから出す',
1344
+ 'menu.sessionId': 'セッション ID をコピー',
1345
+ 'toast.sessionIdCopied': 'セッション ID をコピーしました',
1346
+ 'toast.sessionIdCopyFailed': 'コピーに失敗しました。もう一度お試しください',
1316
1347
  'settings.title': 'より良いワークスペース',
1317
1348
  'settings.desc': 'ワークスペースツリーの外観と折りたたみ設定',
1318
1349
  'settings.expand': '展開',
@@ -1320,6 +1351,7 @@ window.__ModuleLoader__.load({
1320
1351
  'settings.group.tree': 'ツリーとリスト',
1321
1352
  'settings.group.interaction': 'セッション操作',
1322
1353
  'settings.group.appearance': '外観',
1354
+ 'settings.group.sync': 'デバイス間の同期',
1323
1355
  'settings.compactChains': '一本鎖の階層を 1 行にまとめる',
1324
1356
  'settings.compactChains.hint': '子が 1 つだけの階層は 1 行にまとまり,複数の子がある階層はツリーとして展開されます;ワークスペースのドラッグ中は一時的にフォルダーツリーへ戻り,どの階層にもドロップできます;展開状態とカスタム外観はこのブラウザーに保存されます。',
1325
1357
  'settings.statusPulse': 'ステータス表示灯',
@@ -1366,7 +1398,6 @@ window.__ModuleLoader__.load({
1366
1398
  'custom.done': '完了',
1367
1399
  'settings.on': 'オン',
1368
1400
  'settings.off': 'オフ',
1369
- 'sync.title': 'デバイス間の同期',
1370
1401
  'sync.desc': "外観のカスタマイズとトグルはこのデバイスのブラウザに保存され、ホスト設定ストアを通じてもう一方の面(ウェブ/デスクトップアプリ)とやり取りします。新しい変更は自動的にホストへ書き込まれ、もう一方は取得するだけです。このバージョンより前のデータは一度だけ明示的に送信する必要があります。",
1371
1402
  'sync.mode.overwrite': 'このデバイスを上書き',
1372
1403
  'sync.mode.merge': '両方を統合',
@@ -1466,6 +1497,9 @@ window.__ModuleLoader__.load({
1466
1497
  'menu.renameSgroup': '세션 그룹 이름 바꾸기',
1467
1498
  'menu.moveToGroup': '그룹으로 이동…',
1468
1499
  'menu.moveOutGroup': '그룹에서 제거',
1500
+ 'menu.sessionId': '세션 ID 복사',
1501
+ 'toast.sessionIdCopied': '세션 ID가 복사되었습니다',
1502
+ 'toast.sessionIdCopyFailed': '복사에 실패했습니다. 다시 시도해 주세요',
1469
1503
  'settings.title': '더 나은 워크스페이스',
1470
1504
  'settings.desc': '워크스페이스 트리의 모양과 접기 설정',
1471
1505
  'settings.expand': '펼치기',
@@ -1473,6 +1507,7 @@ window.__ModuleLoader__.load({
1473
1507
  'settings.group.tree': '트리와 목록',
1474
1508
  'settings.group.interaction': '세션 상호작용',
1475
1509
  'settings.group.appearance': '모양',
1510
+ 'settings.group.sync': '기기 간 동기화',
1476
1511
  'settings.compactChains': '단일 체인을 한 줄로 접기',
1477
1512
  'settings.compactChains.hint': '자식이 하나뿐인 단계는 한 줄로 합쳐지고, 자식이 여러 개면 트리로 펼쳐집니다; 워크스페이스를 끌어 놓는 동안에는 체인이 잠시 폴더 트리로 펼쳐져 어느 단계에나 놓을 수 있습니다; 펼침 상태와 사용자 지정 모양은 이 브라우저에 저장됩니다.',
1478
1513
  'settings.statusPulse': '상태 표시등',
@@ -1519,7 +1554,6 @@ window.__ModuleLoader__.load({
1519
1554
  'custom.done': '완료',
1520
1555
  'settings.on': '켜기',
1521
1556
  'settings.off': '끄기',
1522
- 'sync.title': '기기 간 동기화',
1523
1557
  'sync.desc': "외관 사용자 지정과 토글은 이 기기의 브라우저에 저장되며, 호스트 설정 저장소를 통해 다른 표면(웹 / 데스크톱 앱)과 주고받습니다. 새 변경 사항은 자동으로 호스트에 기록되고 다른 표면은 가져오기만 하면 됩니다. 이 버전 이전의 데이터는 한 번 명시적으로 보내야 합니다.",
1524
1558
  'sync.mode.overwrite': '이 기기 덮어쓰기',
1525
1559
  'sync.mode.merge': '양쪽 병합',
@@ -1619,6 +1653,9 @@ window.__ModuleLoader__.load({
1619
1653
  'menu.renameSgroup': 'Sessiegroep naam wijzigen',
1620
1654
  'menu.moveToGroup': 'Naar groep verplaatsen…',
1621
1655
  'menu.moveOutGroup': 'Uit groep halen',
1656
+ 'menu.sessionId': 'Sessie-ID kopiëren',
1657
+ 'toast.sessionIdCopied': 'Sessie-ID gekopieerd',
1658
+ 'toast.sessionIdCopyFailed': 'Kopiëren mislukt. Probeer het opnieuw.',
1622
1659
  'settings.title': 'Betere werkruimten',
1623
1660
  'settings.desc': 'Uiterlijk en inklapgedrag van de werkruimteboom',
1624
1661
  'settings.expand': 'Uitklappen',
@@ -1626,6 +1663,7 @@ window.__ModuleLoader__.load({
1626
1663
  'settings.group.tree': 'Boom en lijst',
1627
1664
  'settings.group.interaction': 'Sessie-interactie',
1628
1665
  'settings.group.appearance': 'Uiterlijk',
1666
+ 'settings.group.sync': 'Synchronisatie tussen apparaten',
1629
1667
  'settings.compactChains': 'Enkelvoudige ketens samenvoegen',
1630
1668
  'settings.compactChains.hint': 'Niveaus met één kind worden tot één regel samengevoegd; bij meerdere kinderen klapt de boom open; tijdens het slepen van een werkruimte klappen de ketens tijdelijk terug naar mappen, zodat je op elk niveau kunt neerzetten; de uitgeklapte staat en het eigen uiterlijk blijven in deze browser bewaard.',
1631
1669
  'settings.statusPulse': 'Pulserend statuslampje',
@@ -1672,7 +1710,6 @@ window.__ModuleLoader__.load({
1672
1710
  'custom.done': 'Klaar',
1673
1711
  'settings.on': 'Aan',
1674
1712
  'settings.off': 'Uit',
1675
- 'sync.title': 'Synchronisatie tussen apparaten',
1676
1713
  'sync.desc': "Aanpassingen van het uiterlijk en schakelaars leven in de browser van dit apparaat; ze worden via de host-instellingenopslag uitgewisseld met het andere oppervlak (web / desktop-app). Nieuwe wijzigingen worden automatisch naar de host geschreven — het andere oppervlak hoeft alleen op te halen. Gegevens van vóór deze versie vereisen eenmalig versturen.",
1677
1714
  'sync.mode.overwrite': 'Dit apparaat overschrijven',
1678
1715
  'sync.mode.merge': 'Beide kanten samenvoegen',
@@ -1772,6 +1809,9 @@ window.__ModuleLoader__.load({
1772
1809
  'menu.renameSgroup': 'Zmień nazwę grupy sesji',
1773
1810
  'menu.moveToGroup': 'Przenieś do grupy…',
1774
1811
  'menu.moveOutGroup': 'Przenieś poza grupę',
1812
+ 'menu.sessionId': 'Kopiuj ID sesji',
1813
+ 'toast.sessionIdCopied': 'Skopiowano ID sesji',
1814
+ 'toast.sessionIdCopyFailed': 'Kopiowanie nie powiodło się. Spróbuj ponownie.',
1775
1815
  'settings.title': 'Lepsze obszary robocze',
1776
1816
  'settings.desc': 'Wygląd i zwijanie drzewa obszarów roboczych',
1777
1817
  'settings.expand': 'Rozwiń',
@@ -1779,6 +1819,7 @@ window.__ModuleLoader__.load({
1779
1819
  'settings.group.tree': 'Drzewo i lista',
1780
1820
  'settings.group.interaction': 'Interakcje sesji',
1781
1821
  'settings.group.appearance': 'Wygląd',
1822
+ 'settings.group.sync': 'Synchronizacja między urządzeniami',
1782
1823
  'settings.compactChains': 'Scal łańcuchy z jednym dzieckiem',
1783
1824
  'settings.compactChains.hint': 'Poziomy z jednym dzieckiem scalają się w jeden wiersz; przy większej liczbie dzieci drzewo się rozwija; podczas przeciągania obszaru roboczego łańcuchy tymczasowo rozwijają się do folderów, więc upuszczenie działa na każdym poziomie; stan rozwinięcia i własny wygląd są zapisywane w tej przeglądarce.',
1784
1825
  'settings.statusPulse': 'Pulsująca lampka stanu',
@@ -1825,7 +1866,6 @@ window.__ModuleLoader__.load({
1825
1866
  'custom.done': 'Gotowe',
1826
1867
  'settings.on': 'Wł.',
1827
1868
  'settings.off': 'Wył.',
1828
- 'sync.title': 'Synchronizacja między urządzeniami',
1829
1869
  'sync.desc': "Dostosowania wyglądu i przełączniki żyją w przeglądarce tego urządzenia; wymieniają się z drugą powierzchnią (web / aplikacja desktop) przez magazyn ustawień hosta. Nowe zmiany są zapisywane do hosta automatycznie — druga powierzchnia tylko pobiera. Dane sprzed tej wersji wymagają jednorazowego wysłania.",
1830
1870
  'sync.mode.overwrite': 'Nadpisz to urządzenie',
1831
1871
  'sync.mode.merge': 'Scal obie strony',
@@ -1925,6 +1965,9 @@ window.__ModuleLoader__.load({
1925
1965
  'menu.renameSgroup': 'Renomear grupo de sessões',
1926
1966
  'menu.moveToGroup': 'Mover para o grupo…',
1927
1967
  'menu.moveOutGroup': 'Remover do grupo',
1968
+ 'menu.sessionId': 'Copiar ID da sessão',
1969
+ 'toast.sessionIdCopied': 'ID da sessão copiado',
1970
+ 'toast.sessionIdCopyFailed': 'Falha ao copiar. Tente novamente.',
1928
1971
  'settings.title': 'Espaços de trabalho melhores',
1929
1972
  'settings.desc': 'Aparência e recolhimento da árvore de espaços de trabalho',
1930
1973
  'settings.expand': 'Expandir',
@@ -1932,6 +1975,7 @@ window.__ModuleLoader__.load({
1932
1975
  'settings.group.tree': 'Árvore e lista',
1933
1976
  'settings.group.interaction': 'Interação de sessões',
1934
1977
  'settings.group.appearance': 'Aparência',
1978
+ 'settings.group.sync': 'Sincronização entre dispositivos',
1935
1979
  'settings.compactChains': 'Mesclar cadeias de filho único',
1936
1980
  'settings.compactChains.hint': 'Níveis com um único filho viram uma linha; com vários filhos a árvore se expande; ao arrastar um espaço de trabalho, as cadeias voltam temporariamente a pastas, permitindo soltar em qualquer nível; o estado de expansão e o estilo personalizado ficam neste navegador.',
1937
1981
  'settings.statusPulse': 'Luz de status pulsante',
@@ -1978,7 +2022,6 @@ window.__ModuleLoader__.load({
1978
2022
  'custom.done': 'Concluir',
1979
2023
  'settings.on': 'Ativado',
1980
2024
  'settings.off': 'Desativado',
1981
- 'sync.title': 'Sincronização entre dispositivos',
1982
2025
  'sync.desc': "Personalizações de aparência e interruptores vivem no navegador deste dispositivo; eles trocam com a outra superfície (web / aplicativo de desktop) através do armazenamento de configurações do host. Novas edições são gravadas no host automaticamente — a outra superfície apenas puxa. Dados anteriores a esta versão precisam de um envio explícito.",
1983
2026
  'sync.mode.overwrite': 'Sobrescrever este dispositivo',
1984
2027
  'sync.mode.merge': 'Mesclar os dois lados',
@@ -2078,6 +2121,9 @@ window.__ModuleLoader__.load({
2078
2121
  'menu.renameSgroup': 'Переименовать группу сессий',
2079
2122
  'menu.moveToGroup': 'Переместить в группу…',
2080
2123
  'menu.moveOutGroup': 'Убрать из группы',
2124
+ 'menu.sessionId': 'Копировать ID сессии',
2125
+ 'toast.sessionIdCopied': 'ID сессии скопирован',
2126
+ 'toast.sessionIdCopyFailed': 'Не удалось скопировать. Попробуйте снова.',
2081
2127
  'settings.title': 'Улучшенные рабочие области',
2082
2128
  'settings.desc': 'Внешний вид и сворачивание дерева рабочих областей',
2083
2129
  'settings.expand': 'Развернуть',
@@ -2085,6 +2131,7 @@ window.__ModuleLoader__.load({
2085
2131
  'settings.group.tree': 'Дерево и список',
2086
2132
  'settings.group.interaction': 'Взаимодействие с сессиями',
2087
2133
  'settings.group.appearance': 'Внешний вид',
2134
+ 'settings.group.sync': 'Синхронизация между устройствами',
2088
2135
  'settings.compactChains': 'Объединять цепочки с одним потомком',
2089
2136
  'settings.compactChains.hint': 'Уровни с единственным потомком сливаются в одну строку; при нескольких потомках дерево разворачивается; во время перетаскивания рабочей области цепочки временно разворачиваются в папки, чтобы можно было положить элемент на любой уровень; состояние сворачивания и оформление хранятся в этом браузере.',
2090
2137
  'settings.statusPulse': 'Пульсирующий индикатор состояния',
@@ -2131,7 +2178,6 @@ window.__ModuleLoader__.load({
2131
2178
  'custom.done': 'Готово',
2132
2179
  'settings.on': 'Вкл.',
2133
2180
  'settings.off': 'Выкл.',
2134
- 'sync.title': 'Синхронизация между устройствами',
2135
2181
  'sync.desc': "Настройки внешнего вида и переключатели живут в браузере этого устройства; они обмениваются с другой поверхностью (веб / настольное приложение) через хранилище настроек хоста. Новые правки автоматически записываются на хост — другой поверхности остаётся только получить. Данные, созданные до этой версии, требуют одной явной отправки.",
2136
2182
  'sync.mode.overwrite': 'Перезаписать это устройство',
2137
2183
  'sync.mode.merge': 'Объединить обе стороны',
@@ -2231,6 +2277,9 @@ window.__ModuleLoader__.load({
2231
2277
  'menu.renameSgroup': 'Byt namn på sessionsgrupp',
2232
2278
  'menu.moveToGroup': 'Flytta till grupp…',
2233
2279
  'menu.moveOutGroup': 'Flytta ut ur gruppen',
2280
+ 'menu.sessionId': 'Kopiera sessions-ID',
2281
+ 'toast.sessionIdCopied': 'Sessions-ID kopierat',
2282
+ 'toast.sessionIdCopyFailed': 'Kopieringen misslyckades. Försök igen.',
2234
2283
  'settings.title': 'Bättre arbetsytor',
2235
2284
  'settings.desc': 'Utseende och ihopfällning för arbetsyteträdet',
2236
2285
  'settings.expand': 'Fäll ut',
@@ -2238,6 +2287,7 @@ window.__ModuleLoader__.load({
2238
2287
  'settings.group.tree': 'Träd och lista',
2239
2288
  'settings.group.interaction': 'Sessionsinteraktion',
2240
2289
  'settings.group.appearance': 'Utseende',
2290
+ 'settings.group.sync': 'Synk mellan enheter',
2241
2291
  'settings.compactChains': 'Slå ihop enkelkedjor',
2242
2292
  'settings.compactChains.hint': 'Nivåer med ett enda barn slås ihop till en rad; med flera barn fälls trädet ut; medan du drar en arbetsyta fälls kedjorna tillfälligt ut till mappar så att du kan släppa på vilken nivå som helst; utfällt läge och eget utseende sparas i den här webbläsaren.',
2243
2293
  'settings.statusPulse': 'Pulserande statuslampa',
@@ -2284,7 +2334,6 @@ window.__ModuleLoader__.load({
2284
2334
  'custom.done': 'Klart',
2285
2335
  'settings.on': 'På',
2286
2336
  'settings.off': 'Av',
2287
- 'sync.title': 'Synk mellan enheter',
2288
2337
  'sync.desc': "Utseendeanpassningar och växlar lever i den här enhetens webbläsare; de utbyts med den andra ytan (webb / skrivbordsapp) via värdens inställningslagring. Nya ändringar skrivs automatiskt till värden — den andra ytan behöver bara hämta. Data från före den här versionen kräver ett explicit skicka.",
2289
2338
  'sync.mode.overwrite': 'Skriv över denna enhet',
2290
2339
  'sync.mode.merge': 'Slå ihop båda sidor',
@@ -2384,6 +2433,9 @@ window.__ModuleLoader__.load({
2384
2433
  'menu.renameSgroup': 'เปลี่ยนชื่อกลุ่มเซสชัน',
2385
2434
  'menu.moveToGroup': 'ย้ายไปยังกลุ่ม…',
2386
2435
  'menu.moveOutGroup': 'ย้ายออกจากกลุ่ม',
2436
+ 'menu.sessionId': 'คัดลอก ID เซสชัน',
2437
+ 'toast.sessionIdCopied': 'คัดลอก ID เซสชันแล้ว',
2438
+ 'toast.sessionIdCopyFailed': 'คัดลอกไม่สำเร็จ โปรดลองอีกครั้ง',
2387
2439
  'settings.title': 'พื้นที่ทำงานที่ดีขึ้น',
2388
2440
  'settings.desc': 'รูปลักษณ์และการย่อของต้นไม้พื้นที่ทำงาน',
2389
2441
  'settings.expand': 'ขยาย',
@@ -2391,6 +2443,7 @@ window.__ModuleLoader__.load({
2391
2443
  'settings.group.tree': 'ต้นไม้และรายการ',
2392
2444
  'settings.group.interaction': 'การโต้ตอบของเซสชัน',
2393
2445
  'settings.group.appearance': 'ลักษณะที่ปรากฏ',
2446
+ 'settings.group.sync': 'ซิงก์ข้ามอุปกรณ์',
2394
2447
  'settings.compactChains': 'รวมสายที่มีลูกเดียว',
2395
2448
  'settings.compactChains.hint': 'ระดับที่มีลูกเพียงหนึ่งจะถูกรวมเป็นบรรทัดเดียว เมื่อมีลูกหลายตัวต้นไม้จะขยายออก ขณะลากพื้นที่ทำงาน สายจะขยายกลับเป็นโฟลเดอร์ชั่วคราวเพื่อให้วางได้ทุกระดับ สถานะการขยายและรูปลักษณ์ที่กำหนดเองจะถูกเก็บไว้ในเบราว์เซอร์นี้',
2396
2449
  'settings.statusPulse': 'ไฟสถานะหายใจ',
@@ -2437,7 +2490,6 @@ window.__ModuleLoader__.load({
2437
2490
  'custom.done': 'เสร็จ',
2438
2491
  'settings.on': 'เปิด',
2439
2492
  'settings.off': 'ปิด',
2440
- 'sync.title': 'ซิงก์ข้ามอุปกรณ์',
2441
2493
  'sync.desc': "การปรับแต่งหน้าตาและสวิตช์อยู่ในเบราว์เซอร์ของอุปกรณ์นี้ และแลกเปลี่ยนกับอีกฝั่ง (เว็บ / แอปเดสก์ท็อป) ผ่านที่เก็บการตั้งค่าของโฮสต์ การแก้ไขใหม่เขียนไปยังโฮสต์โดยอัตโนมัติ อีกฝั่งแค่ดึงข้อมูล ข้อมูลก่อนเวอร์ชันนี้ต้องส่งครั้งเดียว",
2442
2494
  'sync.mode.overwrite': 'เขียนทับอุปกรณ์นี้',
2443
2495
  'sync.mode.merge': 'รวมทั้งสองฝั่ง',
@@ -2537,6 +2589,9 @@ window.__ModuleLoader__.load({
2537
2589
  'menu.renameSgroup': 'Oturum grubunu yeniden adlandır',
2538
2590
  'menu.moveToGroup': 'Gruba taşı…',
2539
2591
  'menu.moveOutGroup': 'Gruptan çıkar',
2592
+ 'menu.sessionId': 'Oturum kimliğini kopyala',
2593
+ 'toast.sessionIdCopied': 'Oturum kimliği kopyalandı',
2594
+ 'toast.sessionIdCopyFailed': 'Kopyalama başarısız oldu. Lütfen tekrar deneyin.',
2540
2595
  'settings.title': 'Daha iyi çalışma alanları',
2541
2596
  'settings.desc': 'Çalışma alanı ağacının görünümü ve daraltma tercihleri',
2542
2597
  'settings.expand': 'Genişlet',
@@ -2544,6 +2599,7 @@ window.__ModuleLoader__.load({
2544
2599
  'settings.group.tree': 'Ağaç ve liste',
2545
2600
  'settings.group.interaction': 'Oturum etkileşimi',
2546
2601
  'settings.group.appearance': 'Görünüm',
2602
+ 'settings.group.sync': 'Cihazlar arası eşitleme',
2547
2603
  'settings.compactChains': 'Tek çocuklu zincirleri birleştir',
2548
2604
  'settings.compactChains.hint': 'Tek çocuklu düzeyler tek satırda birleşir; birden çok çocuk olduğunda ağaç açılır; bir çalışma alanını sürüklerken zincirler geçici olarak klasörlere açılır, böylece her düzeye bırakabilirsiniz; açılma durumu ve özel görünüm bu tarayıcıda saklanır.',
2549
2605
  'settings.statusPulse': 'Nefes alan durum ışığı',
@@ -2590,7 +2646,6 @@ window.__ModuleLoader__.load({
2590
2646
  'custom.done': 'Bitti',
2591
2647
  'settings.on': 'Açık',
2592
2648
  'settings.off': 'Kapalı',
2593
- 'sync.title': 'Cihazlar arası eşitleme',
2594
2649
  'sync.desc': "Görünüm özelleştirmeleri ve anahtarlar bu cihazın tarayıcısında yaşar; ana makine ayar deposu aracılığıyla diğer yüzleyle (web / masaüstü uygulaması) alışveriş ederler. Yeni değişiklikler ana makineye otomatik yazılır — diğer yüz yalnızca çeker. Bu sürümden önceki veriler tek seferlik açık bir Gönder gerektirir.",
2595
2650
  'sync.mode.overwrite': 'Bu cihazın üzerine yaz',
2596
2651
  'sync.mode.merge': 'İki tarafı birleştir',
@@ -2690,6 +2745,9 @@ window.__ModuleLoader__.load({
2690
2745
  'menu.renameSgroup': 'Đổi tên nhóm phiên',
2691
2746
  'menu.moveToGroup': 'Chuyển vào nhóm…',
2692
2747
  'menu.moveOutGroup': 'Đưa ra khỏi nhóm',
2748
+ 'menu.sessionId': 'Sao chép ID phiên',
2749
+ 'toast.sessionIdCopied': 'Đã sao chép ID phiên',
2750
+ 'toast.sessionIdCopyFailed': 'Sao chép thất bại. Hãy thử lại.',
2693
2751
  'settings.title': 'Không gian làm việc tốt hơn',
2694
2752
  'settings.desc': 'Giao diện và cách thu gọn của cây không gian làm việc',
2695
2753
  'settings.expand': 'Mở rộng',
@@ -2697,6 +2755,7 @@ window.__ModuleLoader__.load({
2697
2755
  'settings.group.tree': 'Cây và danh sách',
2698
2756
  'settings.group.interaction': 'Tương tác phiên',
2699
2757
  'settings.group.appearance': 'Diện mạo',
2758
+ 'settings.group.sync': 'Đồng bộ giữa các thiết bị',
2700
2759
  'settings.compactChains': 'Gộp chuỗi một nhánh',
2701
2760
  'settings.compactChains.hint': 'Các tầng chỉ có một nhánh con được gộp thành một dòng; khi có nhiều nhánh con, cây tự mở ra; khi kéo không gian làm việc, chuỗi tạm mở lại thành thư mục để có thể thả vào bất kỳ tầng nào; trạng thái mở và giao diện tùy chỉnh được lưu trong trình duyệt này.',
2702
2761
  'settings.statusPulse': 'Đèn trạng thái nhấp nháy',
@@ -2743,7 +2802,6 @@ window.__ModuleLoader__.load({
2743
2802
  'custom.done': 'Xong',
2744
2803
  'settings.on': 'Bật',
2745
2804
  'settings.off': 'Tắt',
2746
- 'sync.title': 'Đồng bộ giữa các thiết bị',
2747
2805
  'sync.desc': "Tùy chỉnh giao diện và các công tắc sống trong trình duyệt của thiết bị này; chúng trao đổi với bề mặt kia (web / ứng dụng máy tính) qua kho cài đặt của máy chủ. Các chỉnh sửa mới được ghi lên máy chủ tự động — bề mặt kia chỉ cần kéo. Dữ liệu từ trước phiên bản này cần một lần Gửi rõ ràng.",
2748
2806
  'sync.mode.overwrite': 'Ghi đè thiết bị này',
2749
2807
  'sync.mode.merge': 'Gộp hai bên',
@@ -2843,6 +2901,9 @@ window.__ModuleLoader__.load({
2843
2901
  'menu.renameSgroup': '重新命名對話分組',
2844
2902
  'menu.moveToGroup': '移動到分組…',
2845
2903
  'menu.moveOutGroup': '移出分組',
2904
+ 'menu.sessionId': '複製 Session ID',
2905
+ 'toast.sessionIdCopied': 'Session ID 已複製',
2906
+ 'toast.sessionIdCopyFailed': '複製失敗,請重試',
2846
2907
  'settings.title': '更好嘅工作區',
2847
2908
  'settings.desc': '工作區樹嘅外觀同收起偏好',
2848
2909
  'settings.expand': '展開',
@@ -2850,6 +2911,7 @@ window.__ModuleLoader__.load({
2850
2911
  'settings.group.tree': '樹與列表',
2851
2912
  'settings.group.interaction': '會話互動',
2852
2913
  'settings.group.appearance': '外觀',
2914
+ 'settings.group.sync': '跨端同步',
2853
2915
  'settings.compactChains': '單鏈分組摺疊顯示',
2854
2916
  'settings.compactChains.hint': '單層鏈會合併成一行,出現多個子級就會自動展開成樹狀;拖拽工作區期間單鏈會臨時展開返做文件夾樹,可以放入任何一級;展開狀態同自訂外觀會喺呢個瀏覽器保存。',
2855
2917
  'settings.statusPulse': '狀態呼吸燈',
@@ -2896,7 +2958,6 @@ window.__ModuleLoader__.load({
2896
2958
  'custom.done': '完成',
2897
2959
  'settings.on': '開',
2898
2960
  'settings.off': '關',
2899
- 'sync.title': '跨端同步',
2900
2961
  'sync.desc': "外觀自訂與開關儲存在本裝置的瀏覽器裡;透過主機設定存放區與另一端互傳。平時的新修改會自動寫入主機,另一端點「取得」即可拿到;舊版本的歷史資料首次需要點一次「傳送」。",
2901
2962
  'sync.mode.overwrite': '覆蓋呢部裝置',
2902
2963
  'sync.mode.merge': '合併兩端',
@@ -2996,6 +3057,9 @@ window.__ModuleLoader__.load({
2996
3057
  'menu.renameSgroup': '重新命名對話分組',
2997
3058
  'menu.moveToGroup': '移動到分組…',
2998
3059
  'menu.moveOutGroup': '移出分組',
3060
+ 'menu.sessionId': '複製 Session ID',
3061
+ 'toast.sessionIdCopied': 'Session ID 已複製',
3062
+ 'toast.sessionIdCopyFailed': '複製失敗,請重試',
2999
3063
  'settings.title': '更好嘅工作區',
3000
3064
  'settings.desc': '工作區樹嘅外觀同收起偏好',
3001
3065
  'settings.expand': '展開',
@@ -3003,6 +3067,7 @@ window.__ModuleLoader__.load({
3003
3067
  'settings.group.tree': '樹與列表',
3004
3068
  'settings.group.interaction': '會話互動',
3005
3069
  'settings.group.appearance': '外觀',
3070
+ 'settings.group.sync': '跨端同步',
3006
3071
  'settings.compactChains': '單鏈分組摺疊顯示',
3007
3072
  'settings.compactChains.hint': '單層鏈會合併成一行,出現多個子級就會自動展開成樹狀;拖拽工作區期間單鏈會臨時展開返做文件夾樹,可以放入任何一級;展開狀態同自訂外觀會喺呢個瀏覽器保存。',
3008
3073
  'settings.statusPulse': '狀態呼吸燈',
@@ -3049,7 +3114,6 @@ window.__ModuleLoader__.load({
3049
3114
  'custom.done': '完成',
3050
3115
  'settings.on': '開',
3051
3116
  'settings.off': '關',
3052
- 'sync.title': '跨端同步',
3053
3117
  'sync.desc': "外觀自訂與開關儲存在本裝置的瀏覽器裡;透過主機設定存放區與另一端互傳。平時的新修改會自動寫入主機,另一端點「取得」即可拿到;舊版本的歷史資料首次需要點一次「傳送」。",
3054
3118
  'sync.mode.overwrite': '覆蓋呢部裝置',
3055
3119
  'sync.mode.merge': '合併兩端',
@@ -3149,6 +3213,9 @@ window.__ModuleLoader__.load({
3149
3213
  'menu.renameSgroup': '重新命名工作階段群組',
3150
3214
  'menu.moveToGroup': '移動到分組…',
3151
3215
  'menu.moveOutGroup': '移出分組',
3216
+ 'menu.sessionId': '複製 Session ID',
3217
+ 'toast.sessionIdCopied': 'Session ID 已複製',
3218
+ 'toast.sessionIdCopyFailed': '複製失敗,請重試',
3152
3219
  'settings.title': '更好的工作區',
3153
3220
  'settings.desc': '工作區樹的外觀與收合偏好',
3154
3221
  'settings.expand': '展開',
@@ -3156,6 +3223,7 @@ window.__ModuleLoader__.load({
3156
3223
  'settings.group.tree': '樹與清單',
3157
3224
  'settings.group.interaction': '會話互動',
3158
3225
  'settings.group.appearance': '外觀',
3226
+ 'settings.group.sync': '跨裝置同步',
3159
3227
  'settings.compactChains': '單鏈群組合併顯示',
3160
3228
  'settings.compactChains.hint': '單層鏈會合併成一行,出現多個子層級時自動展開為樹狀;拖曳工作區期間單鏈會暫時展開回資料夾樹,可放入任一層級;展開狀態與自訂外觀會保存在本瀏覽器。',
3161
3229
  'settings.statusPulse': '狀態呼吸燈',
@@ -3202,7 +3270,6 @@ window.__ModuleLoader__.load({
3202
3270
  'custom.done': '完成',
3203
3271
  'settings.on': '開',
3204
3272
  'settings.off': '關',
3205
- 'sync.title': '跨裝置同步',
3206
3273
  'sync.desc': "外觀自訂與開關儲存在本裝置的瀏覽器裡;透過主機設定存放區與另一端互傳。平時的新修改會自動寫入主機,另一端點「取得」即可拿到;舊版本的歷史資料首次需要點一次「傳送」。",
3207
3274
  'sync.mode.overwrite': '覆寫本裝置',
3208
3275
  'sync.mode.merge': '合併兩端',
@@ -4164,7 +4231,14 @@ window.__ModuleLoader__.load({
4164
4231
  '.bw-plugin-body{border-top:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.18));margin:0 16px;padding:12px 0 16px}',
4165
4232
  '.bw-setting-row{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-secondary,#b8b8b8)}',
4166
4233
  '.bw-setting-label{flex:1;min-width:0;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-secondary,#b8b8b8)}',
4167
- '.bw-settings-group-title{margin-top:14px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e6e6)}',
4234
+ '.bw-fold{border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.18));border-radius:10px;padding:4px 12px 12px;background:var(--dsw-alias-bg-layer-1,transparent)}',
4235
+ '.bw-fold .bw-stack{padding-top:10px}',
4236
+ '.bw-foldHead{display:flex;align-items:center;gap:8px;width:100%;appearance:none;background:none;border:0;padding:6px 0;font:inherit;font-size:13px;font-weight:600;letter-spacing:.02em;color:var(--dsw-alias-label-secondary,#b8b8b8);cursor:pointer;text-align:left}',
4237
+ '.bw-foldHead:hover{color:var(--dsw-alias-label-primary,#e6e6e6)}',
4238
+ '.bw-foldHead:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5b8def);outline-offset:-2px}',
4239
+ '.bw-foldChevron{flex:none;display:inline-block;color:var(--dsw-alias-label-tertiary,#9a9a9a);transition:transform .16s}',
4240
+ '.bw-foldChevron.bw-foldChevronOpen{transform:rotate(90deg)}',
4241
+ '.bw-foldTitle{min-width:0;overflow-wrap:anywhere}',
4168
4242
  '.bw-switch{box-sizing:border-box;position:relative;flex:0 0 auto;width:36px;height:20px;padding:2px;border:0;border-radius:10px;background:var(--dsw-alias-border-l3,rgba(127,127,127,.3));cursor:pointer;transition:background 120ms ease}',
4169
4243
  '.bw-switch:hover{background:var(--dsw-alias-label-dimmed,#7a7a7a)}',
4170
4244
  '.bw-switch-on{background:var(--dsw-alias-brand-primary,#5b8def)}',
@@ -4250,17 +4324,30 @@ window.__ModuleLoader__.load({
4250
4324
  return workspaceSlashOf(legacySlash) ? 'disk-slash' : 'disk'
4251
4325
  }
4252
4326
 
4253
- // Issue #8 progressive listing (official collapsedSessionRows mirror):
4254
- // blank, running, running-subagent and pinned rows stay OUTSIDE the
4255
- // quota; idle rows beyond it hide behind one overflow row. limit 0 =
4256
- // unlimited (default — the plugin's folder-style full listing), and a
4257
- // lifted workspace renders everything again.
4258
- const limitSessionsForRender = (rows, limit, lifted) => {
4327
+ // Issue #8 progressive listing. The official collapsedSessionRows mirror
4328
+ // this quota used to track is GONE in dsh 0.2.1-alpha.5 — the shipped
4329
+ // browser now folds per group behind a client-side watermark (base 5,
4330
+ // auto-raise to 50 for attention rows, +5 per "Show more"). The quota
4331
+ // stays a plugin-side opt-in (limit 0 = unlimited, the folder-style
4332
+ // default), but its always-visible set follows the official attention
4333
+ // semantics: blank, running, running-subagent and pinned rows stay
4334
+ // OUTSIDE the quota unconditionally, and so — within the first 50
4335
+ // non-blank rows, the official SESSION_WATERMARK_AUTO_MAX — do
4336
+ // pending-interaction, completed and the current session, exactly the
4337
+ // rows the shipped watermark refuses to fold away. Idle rows beyond the
4338
+ // quota hide behind one overflow row; a lifted workspace renders
4339
+ // everything again.
4340
+ const SESSION_ATTENTION_WINDOW = 50
4341
+ const limitSessionsForRender = (rows, limit, lifted, currentId) => {
4259
4342
  if (!rows || !limit || lifted) return { rows: rows || [], hidden: 0 }
4260
4343
  const keep = []
4261
4344
  const idle = []
4345
+ let seen = 0
4262
4346
  for (const row of rows) {
4263
- if (row.blank || row.running || row.subagents > 0 || row.pinned) keep.push(row)
4347
+ if (!row.blank) seen += 1
4348
+ const attention = row.blank || row.running || row.subagents > 0 || row.pinned
4349
+ || (seen <= SESSION_ATTENTION_WINDOW && (row.pending || row.completed || row.id === currentId))
4350
+ if (attention) keep.push(row)
4264
4351
  else idle.push(row)
4265
4352
  }
4266
4353
  if (idle.length <= limit) return { rows, hidden: 0 }
@@ -4553,14 +4640,20 @@ window.__ModuleLoader__.load({
4553
4640
  // v0.16.0: the sidebar no longer draws its own in-app browser. The browse
4554
4641
  // backend SHIPS one (dsh-client-ui-directory-picker-browse, mounted as a
4555
4642
  // host+surface pair, its client half registering into both directory-flow
4556
- // holes) — but it can only be rendered by the entry that DECLARED the
4557
- // child hole (the shipped WorkspaceBrowser), and a child declaration is
4558
- // exclusive, so shadowing that seat forfeits the authorization for good
4559
- // (ui-renderer: "not declared by this entry's children"). A native Host
4560
- // still gets the official OS chooser; a browse Host is pointed at the
4561
- // official in-app flow in the conversation empty state instead of a
4562
- // second-hand imitation. The probe stays: it decides which of the two
4563
- // this page gets, and a refused pick still classifies the backend.
4643
+ // holes) — but it can only be RENDER-AUTHORIZED by the entry that
4644
+ // DECLARED the child hole (the shipped WorkspaceBrowser), and a child
4645
+ // declaration is exclusive, so shadowing that seat forfeits that
4646
+ // authorization for good (ui-renderer: "not declared by this entry's
4647
+ // children").
4648
+ // v0.28.0: that gap no longer punts browse hosts to the empty-state page.
4649
+ // The shipped occupant registers into BOTH holes (the sidebar hole
4650
+ // included), so the sidebar reads its component off the slot ledger —
4651
+ // the v0.22.0 hero read, third use of the shipped-occupant pattern — and
4652
+ // renders it with its own owner conversation. A native Host still gets
4653
+ // the official OS chooser; a ledger that hides the entry keeps the
4654
+ // add-guide pointer as the floor. The probe stays: it decides which of
4655
+ // the two this page gets, and a refused pick still classifies the
4656
+ // backend.
4564
4657
  const pickerState = { kind: 'unknown', probe: null, api: null }
4565
4658
  /**
4566
4659
  * True for the refusal a Host serving the OTHER capability throws: the wire
@@ -5935,6 +6028,38 @@ window.__ModuleLoader__.load({
5935
6028
  scopeSet(key, value)
5936
6029
  }
5937
6030
  const setAppearance = (next) => setPref('appearance', readAppearance(next))
6031
+ // Foldable sections, HAND-DRAWN (mirrors dsh-gitbash-shell v0.38.4).
6032
+ // The official disclosure row was tried there first (v0.38.0-0.38.3)
6033
+ // and never rendered on the real machine: the plugin's restricted
6034
+ // require does not reach it (the icon lookup inside the ui-primitives
6035
+ // module comes back null there), so every section silently degraded to
6036
+ // a static title+stack pile. One button + one conditional instead;
6037
+ // header rows stay visible at all times, every section ships collapsed.
6038
+ const foldsState = React.useState({})
6039
+ const folds = foldsState[0]
6040
+ const setFolds = foldsState[1]
6041
+ const toggleFold = (key) => {
6042
+ setFolds((prev) => {
6043
+ const next = { ...prev }
6044
+ next[key] = !prev[key]
6045
+ return next
6046
+ })
6047
+ }
6048
+ const fold = (titleKey, ...body) => {
6049
+ const open = folds[titleKey] === true
6050
+ return E('div', { className: 'bw-fold' },
6051
+ E('button', {
6052
+ type: 'button',
6053
+ className: 'bw-foldHead',
6054
+ 'aria-expanded': open ? 'true' : 'false',
6055
+ onClick: () => { toggleFold(titleKey) },
6056
+ },
6057
+ E('span', { className: cls('bw-foldChevron', open && 'bw-foldChevronOpen'), 'aria-hidden': 'true' }, '▸'),
6058
+ E('span', { className: 'bw-foldTitle' }, t(titleKey)),
6059
+ ),
6060
+ open ? E('div', { className: 'bw-stack' }, body) : null,
6061
+ )
6062
+ }
5938
6063
  // Manual cross-device sync state.
5939
6064
  const [syncMode, setSyncMode] = React.useState('overwrite')
5940
6065
  const [syncMsg, setSyncMsg] = React.useState(null)
@@ -5969,7 +6094,7 @@ window.__ModuleLoader__.load({
5969
6094
  }, label)
5970
6095
  return E('div', { className: 'bw-settings' },
5971
6096
  StyleNode(),
5972
- E('div', { className: 'bw-settings-group-title' }, t('settings.group.tree')),
6097
+ fold('settings.group.tree',
5973
6098
  E('div', { className: 'bw-setting-row' },
5974
6099
  E('div', { className: 'bw-setting-label' }, t('settings.compactChains')),
5975
6100
  E('button', {
@@ -6016,7 +6141,8 @@ window.__ModuleLoader__.load({
6016
6141
  ),
6017
6142
  ),
6018
6143
  E('div', { className: 'bw-hint' }, t('settings.sessionLimit.hint')),
6019
- E('div', { className: 'bw-settings-group-title' }, t('settings.group.interaction')),
6144
+ ),
6145
+ fold('settings.group.interaction',
6020
6146
  E('div', { className: 'bw-setting-row' },
6021
6147
  E('div', { className: 'bw-setting-label' }, t('settings.sessionMenu')),
6022
6148
  E('button', {
@@ -6041,7 +6167,8 @@ window.__ModuleLoader__.load({
6041
6167
  }, E('span', { className: 'bw-switch-thumb' })),
6042
6168
  ),
6043
6169
  E('div', { className: 'bw-hint' }, t('settings.rowActions.hint')),
6044
- E('div', { className: 'bw-settings-group-title' }, t('settings.group.appearance')),
6170
+ ),
6171
+ fold('settings.group.appearance',
6045
6172
  E('div', { className: 'bw-setting-row' },
6046
6173
  E('div', { className: 'bw-setting-label' }, t('settings.statusPulse')),
6047
6174
  E('button', {
@@ -6082,8 +6209,8 @@ window.__ModuleLoader__.load({
6082
6209
  // Manual cross-device sync exists only where a SHARED host home does
6083
6210
  // (dsh <= 0.1.6 settings.yaml; both surfaces read one file). On
6084
6211
  // dsh >= 0.1.7 settings persist per profile, so the section hides.
6085
- prefsScopeVia === 'settingsScope' ? E(React.Fragment, null,
6086
- E('div', { className: 'bw-setting-label', style: { marginTop: 16 } }, t('sync.title')),
6212
+ ),
6213
+ prefsScopeVia === 'settingsScope' ? fold('settings.group.sync',
6087
6214
  E('div', { className: 'bw-hint' }, t('sync.desc')),
6088
6215
  E('div', { className: 'bw-sync-modes' },
6089
6216
  modeButton('overwrite', t('sync.mode.overwrite')),
@@ -6412,6 +6539,7 @@ window.__ModuleLoader__.load({
6412
6539
  insertWorkspaceBefore, insertSessionBefore,
6413
6540
  useDirectoryFlow,
6414
6541
  pickDirectory, listDirectory, createDirectory,
6542
+ sidebarFlowEntry,
6415
6543
  shippedBrowserFace, scheduleSeatReader,
6416
6544
  officialT,
6417
6545
  t,
@@ -6744,6 +6872,7 @@ window.__ModuleLoader__.load({
6744
6872
  // lifted fold resets when the sidebar unmounts.
6745
6873
  const [limitLifted, setLimitLifted] = React.useState({})
6746
6874
  const [ctx, setCtx] = React.useState(null) // context menu { kind, payload, x, y }
6875
+ const [copyNotice, setCopyNotice] = React.useState(null) // { seq, ok } copy-session-id notice
6747
6876
  const [customize, setCustomize] = React.useState(null) // { kind, entryKey, name }
6748
6877
  const [errorText, setErrorText] = React.useState(null)
6749
6878
  const [drag, setDrag] = React.useState(null) // { kind: 'workspace'|'session', source, over } | null
@@ -6831,7 +6960,10 @@ window.__ModuleLoader__.load({
6831
6960
  // The fork notification belongs to the shipped browser's own overlay,
6832
6961
  // which is shadowed with it; without this the shortcut's async failure
6833
6962
  // would be silent. Dismissal rides the shipped channel so both surfaces
6834
- // agree on the notice's lifetime.
6963
+ // agree on the notice's lifetime. dsh 0.2.1-alpha.5 retired the
6964
+ // forkError state (a frame-level shortcutForkFailed toast replaced it),
6965
+ // so on current hosts this mirror idles — the typeof defenses keep it
6966
+ // alive for rc.2-era hosts that still report the field.
6835
6967
  React.useEffect(() => {
6836
6968
  if (forkError === null || forkError === undefined) return
6837
6969
  const timer = setTimeout(() => {
@@ -6839,6 +6971,14 @@ window.__ModuleLoader__.load({
6839
6971
  }, 6000)
6840
6972
  return () => clearTimeout(timer)
6841
6973
  }, [forkError ? forkError.seq : null])
6974
+ // The copy notice normally self-dismisses (the official toast calls
6975
+ // onDone; the fallback chip rides this cap). Keyed on seq so a second
6976
+ // copy restarts the window.
6977
+ React.useEffect(() => {
6978
+ if (!copyNotice) return
6979
+ const timer = setTimeout(() => setCopyNotice(null), 6000)
6980
+ return () => clearTimeout(timer)
6981
+ }, [copyNotice ? copyNotice.seq : null])
6842
6982
 
6843
6983
  React.useEffect(() => {
6844
6984
  if (!list || !list.byId || typeof renameSession !== 'function') return
@@ -7873,9 +8013,11 @@ window.__ModuleLoader__.load({
7873
8013
  const full = sessionsOf(workspace)
7874
8014
  if (full.length === 0 && !hasDeclared(sessFolders, workspace.workspaceId)) return []
7875
8015
  // Issue #8 progressive listing: quota the IDLE rows of this folder
7876
- // (blank/running/subagent/pinned stay visible); one overflow row
7877
- // hides the rest until clicked. Search always shows everything.
7878
- const limited = limitSessionsForRender(full, sessionLimit, limitLifted[workspace.workspaceId])
8016
+ // (attention rows stay visible — see limitSessionsForRender); one
8017
+ // overflow row hides the rest until clicked. Search always shows
8018
+ // everything, and the current session is passed so it can never be
8019
+ // folded away.
8020
+ const limited = limitSessionsForRender(full, sessionLimit, limitLifted[workspace.workspaceId], currentId)
7879
8021
  const out = renderSessionNode(buildSessionTree(limited.rows, sessionSlash, sessFolders, workspace.workspaceId), workspace.workspaceId, depth)
7880
8022
  if (limited.hidden > 0) {
7881
8023
  out.push(E('button', {
@@ -8171,7 +8313,9 @@ window.__ModuleLoader__.load({
8171
8313
  // Official 0.1.7 mirror (Rows.tsx session menu + the shipped slot
8172
8314
  // entries): pin, rename, fork, archive — same order, same icons,
8173
8315
  // same archived-state behavior (pin hides; archive becomes
8174
- // unarchive). Copy rides the official dictionary, so upstream
8316
+ // unarchive). dsh 0.2.1-alpha.5 adds a fifth official entry
8317
+ // (Copy Session ID, order 450), mirrored below the archive row.
8318
+ // Copy rides the official dictionary, so upstream
8175
8319
  // rewording AND future item additions land without a plugin
8176
8320
  // release; every verb is gated on its service method so older
8177
8321
  // hosts simply never offer what they cannot do.
@@ -8197,6 +8341,13 @@ window.__ModuleLoader__.load({
8197
8341
  archived
8198
8342
  ? { id: 'unarchive', label: official('menu.unarchiveSession', t('menu.archive')), icon: icon('IconUnarchiveOutlineRegular', 16) }
8199
8343
  : { id: 'archive', label: official('menu.archiveSession', t('menu.archive')), icon: icon('IconArchiveOutline20', 16) },
8344
+ // Official order 450: clipboard write rides the host primitive,
8345
+ // so the whole row hides where the primitives lack it.
8346
+ ...(typeof ui.writeClipboard === 'function' ? [{
8347
+ id: 'copy-id',
8348
+ label: official('menu.sessionId', t('menu.sessionId')),
8349
+ icon: icon('IconCopyOutlineRegular', 16),
8350
+ }] : []),
8200
8351
  { type: 'separator', id: 'bw-sep' },
8201
8352
  ...sessionUngroupItems,
8202
8353
  ...sgroupItems,
@@ -8258,6 +8409,15 @@ window.__ModuleLoader__.load({
8258
8409
  fail(reason)
8259
8410
  })
8260
8411
  }
8412
+ else if (kind === 'session' && id === 'copy-id' && typeof ui.writeClipboard === 'function') {
8413
+ // Official mirror: copied → success notice, anything else → the
8414
+ // failure notice (official sessionIdInjected wiring).
8415
+ Promise.resolve(ui.writeClipboard(payload.id)).then((copied) => {
8416
+ setCopyNotice((prev) => ({ seq: (prev ? prev.seq : 0) + 1, ok: copied === true }))
8417
+ }, () => {
8418
+ setCopyNotice((prev) => ({ seq: (prev ? prev.seq : 0) + 1, ok: false }))
8419
+ })
8420
+ }
8261
8421
  // Plain retitle back to the leaf: the row leaves its group and returns
8262
8422
  // to the top level of its workspace (v0.18.0).
8263
8423
  else if (kind === 'session' && id === 'move-out') {
@@ -8409,20 +8569,47 @@ window.__ModuleLoader__.load({
8409
8569
  onClose: () => setDialog(null),
8410
8570
  t,
8411
8571
  })
8412
- if (dialog.kind === 'add-guide') return E(ui.Modal, {
8413
- key: 'add-guide',
8414
- open: true,
8415
- onClose: () => setDialog(null),
8416
- closeLabel: t('close'),
8417
- title: t('add.guide.title'),
8418
- footer: E('div', { className: 'bw-modal-actions' },
8419
- E(BTN, { variant: 'primary', onClick: () => setDialog(null) }, t('confirm')),
8420
- ),
8421
- },
8422
- E('div', { className: 'bw-modal-body' },
8423
- E('div', { className: 'bw-hint' }, t('add.guide')),
8424
- ),
8425
- )
8572
+ if (dialog.kind === 'add-guide' || dialog.kind === 'add-flow') {
8573
+ // v0.28.0: a browse host gets the SHIPPED in-app dialog right here.
8574
+ // The occupant's component is read off the sidebar hole's ledger
8575
+ // (the hero read, v0.22.0) and rendered with this seat's owner
8576
+ // conversation: a confirmed directory is adopted exactly like a
8577
+ // native pick, dismissal just closes. The ledger hiding the entry
8578
+ // (or an 'add-guide' outright) keeps the pointer dialog as the
8579
+ // floor. All references below are callback-scoped: adoptDirectory
8580
+ // and fail are defined after this IIFE, so no render-time TDZ.
8581
+ const flowEntry = dialog.kind === 'add-flow'
8582
+ && typeof sidebarFlowEntry === 'function' ? sidebarFlowEntry() : null
8583
+ if (flowEntry && flowEntry.component) {
8584
+ let flowFace = null
8585
+ if (typeof flowEntry.inject === 'function') {
8586
+ try { flowFace = flowEntry.inject() } catch (error) { flowFace = null }
8587
+ }
8588
+ return E(flowEntry.component, {
8589
+ key: 'bw-sidebar-flow',
8590
+ open: true,
8591
+ busy: flowBusy,
8592
+ onPicked: (path) => { setDialog(null); if (path) adoptDirectory(String(path)) },
8593
+ onCancel: () => { setDialog(null) },
8594
+ onError: (message) => { setDialog(null); fail(String(message)) },
8595
+ ...(flowFace || EMPTY_FOLDERS),
8596
+ })
8597
+ }
8598
+ return E(ui.Modal, {
8599
+ key: 'add-guide',
8600
+ open: true,
8601
+ onClose: () => setDialog(null),
8602
+ closeLabel: t('close'),
8603
+ title: t('add.guide.title'),
8604
+ footer: E('div', { className: 'bw-modal-actions' },
8605
+ E(BTN, { variant: 'primary', onClick: () => setDialog(null) }, t('confirm')),
8606
+ ),
8607
+ },
8608
+ E('div', { className: 'bw-modal-body' },
8609
+ E('div', { className: 'bw-hint' }, t('add.guide')),
8610
+ ),
8611
+ )
8612
+ }
8426
8613
  return null
8427
8614
  })()
8428
8615
 
@@ -8445,9 +8632,12 @@ window.__ModuleLoader__.load({
8445
8632
  }
8446
8633
 
8447
8634
  // v0.16.0: the sidebar add affordance rides the OFFICIAL chooser only.
8448
- // A `browse` host refuses that pick by design, so the click explains where
8449
- // the official in-app flow lives instead of opening a plugin-built copy —
8450
- // the shipped dialog cannot be rendered from here (see the capability block).
8635
+ // A `browse` host refuses that pick by design — the click used to
8636
+ // explain where the official in-app flow lives instead. v0.28.0: the
8637
+ // browse host now gets that flow RIGHT HERE: 'add-flow' opens the
8638
+ // dialog that reads the shipped occupant off the sidebar hole's ledger
8639
+ // (see the dialog rendering above; the renderSlot authorization stays
8640
+ // with the shipped entry, but the component is on the ledger).
8451
8641
  //
8452
8642
  // The shipped shortcut channel also reports this interaction
8453
8643
  // (setDirectoryBusy): a second Cmd/Ctrl+O while the chooser is open is
@@ -8458,12 +8648,12 @@ window.__ModuleLoader__.load({
8458
8648
  const settle = () => { if (typeof reportDirectoryBusy === 'function') reportDirectoryBusy(false) }
8459
8649
  Promise.resolve(pickerCapabilityNow())
8460
8650
  .then((kind) => {
8461
- if (kind === 'browse') { setDialog({ kind: 'add-guide' }); return undefined }
8651
+ if (kind === 'browse') { setDialog({ kind: 'add-flow' }); return undefined }
8462
8652
  return Promise.resolve()
8463
8653
  .then(() => pickDirectory())
8464
8654
  .then((path) => (path ? adoptDirectory(String(path)) : undefined))
8465
8655
  .catch((reason) => {
8466
- if (pickerRefusal(reason)) { setDialog({ kind: 'add-guide' }); return }
8656
+ if (pickerRefusal(reason)) { setDialog({ kind: 'add-flow' }); return }
8467
8657
  fail(messageOf(reason))
8468
8658
  })
8469
8659
  })
@@ -8494,12 +8684,20 @@ window.__ModuleLoader__.load({
8494
8684
  },
8495
8685
  }
8496
8686
 
8687
+ // Copy-session-id notice copy: the official 'workspace' dictionary is
8688
+ // the source of truth, this plugin's aligned fallback covers older hosts.
8689
+ const copyNoticeText = copyNotice
8690
+ ? officialText(copyNotice.ok ? 'toast.sessionIdCopied' : 'toast.sessionIdCopyFailed',
8691
+ undefined, t(copyNotice.ok ? 'toast.sessionIdCopied' : 'toast.sessionIdCopyFailed'))
8692
+ : ''
8693
+
8497
8694
  return E('div', { className: cls('bw-root', sessionCouldLeaveGroup && 'bw-root-drop-out'), ...rootDropEvents },
8498
8695
  StyleNode(),
8499
8696
  // The shipped fork notice rides the shipped browser's overlay, which is
8500
8697
  // shadowed with it: the shortcut's async failure would otherwise be
8501
8698
  // silent. Copy follows the shipped dictionary, the lifetime follows the
8502
- // shipped channel (the effect above dismisses it).
8699
+ // shipped channel (the effect above dismisses it). Retired upstream in
8700
+ // dsh 0.2.1-alpha.5 (frame-level toast now); kept for rc.2-era hosts.
8503
8701
  (forkError && typeof forkError === 'object')
8504
8702
  ? E('div', { className: 'bw-toast', role: 'status' },
8505
8703
  E('span', { className: 'bw-toast-text' }, forkError.reason === 'unavailable'
@@ -8514,6 +8712,28 @@ window.__ModuleLoader__.load({
8514
8712
  }, icon('IconCloseOutline16', 12)),
8515
8713
  )
8516
8714
  : null,
8715
+ // Copy-session-id notice (the dsh 0.2.1-alpha.5 menu mirror): the
8716
+ // official primitives Toast when this host ships it (body portal,
8717
+ // official look), otherwise the same chip the fork notice uses.
8718
+ (copyNotice && officialToast)
8719
+ ? E(officialToast, {
8720
+ key: 'bw-copy-' + copyNotice.seq,
8721
+ text: copyNoticeText,
8722
+ tone: copyNotice.ok ? 'success' : undefined,
8723
+ onDone: () => setCopyNotice(null),
8724
+ })
8725
+ : (copyNotice && !officialToast)
8726
+ ? E('div', { className: 'bw-toast', role: 'status' },
8727
+ E('span', { className: 'bw-toast-text' }, copyNoticeText),
8728
+ E('button', {
8729
+ type: 'button',
8730
+ className: 'bw-toast-close',
8731
+ 'aria-label': t('close'),
8732
+ title: t('close'),
8733
+ onClick: () => setCopyNotice(null),
8734
+ }, icon('IconCloseOutline16', 12)),
8735
+ )
8736
+ : null,
8517
8737
  E('div', { className: 'bw-header' },
8518
8738
  E('div', { className: 'bw-header-title' }, t('title')),
8519
8739
  // Group discoverability: an ICON entry point (v0.17.0 — it used to be
@@ -8781,6 +9001,21 @@ window.__ModuleLoader__.load({
8781
9001
  // Host composes the browse backend instead of the OS chooser.
8782
9002
  listDirectory: (path, signal) => uiWorkspace.listDirectory(path, signal),
8783
9003
  createDirectory: (path, name) => uiWorkspace.createDirectory(path, name),
9004
+ // v0.28.0: the shipped occupant's ledger entry for the SIDEBAR flow
9005
+ // hole (same read as the hero one, v0.22.0). The browse surface
9006
+ // registers into both holes, so its in-app dialog renders from the
9007
+ // sidebar too; a native entry read this way is renderless and would
9008
+ // only re-run the OS chooser the native branch already drives
9009
+ // directly, so the browse probe keeps choosing the path.
9010
+ sidebarFlowEntry: () => {
9011
+ try {
9012
+ const list = slots.entries('sidebar.workspaces.directoryFlow')
9013
+ const entry = Array.isArray(list) && list.length > 0 ? list[0] : null
9014
+ return entry && entry.component ? entry : null
9015
+ } catch (error) {
9016
+ return null
9017
+ }
9018
+ },
8784
9019
  hooks: {
8785
9020
  directoryFlow: flowSource(slots, 'sidebar.workspaces.directoryFlow'),
8786
9021
  },
@@ -8799,14 +9034,16 @@ window.__ModuleLoader__.load({
8799
9034
  }
8800
9035
  }
8801
9036
 
8802
- // The SIDEBAR hole is occupied by our own picking flow at priority -1:
8803
- // rendering the official occupant is impossible here (a child-hole
8804
- // declaration is exclusive — the shipped browser entry keeps owning
8805
- // 'sidebar.workspaces.directoryFlow' even after losing the render race,
8806
- // and re-declaring throws), so the flow probes the backend itself — the
8807
- // OS chooser through the same official host service on native Hosts,
8808
- // the polished in-app dialog on browse Hosts. The HERO hole stays
8809
- // untouched: the conversation empty-state flow is purely official.
9037
+ // The SIDEBAR hole's directory flow is NOT occupied by this plugin
9038
+ // (v0.16.0): a child-hole declaration is exclusive — the shipped
9039
+ // browser entry keeps owning 'sidebar.workspaces.directoryFlow' even
9040
+ // after losing the render race, and re-declaring throws — so the add
9041
+ // flow probes the backend itself: the OS chooser through the same
9042
+ // official host service on native Hosts, and since v0.28.0 the SHIPPED
9043
+ // in-app dialog on browse Hosts, read off the hole's ledger and
9044
+ // rendered with this seat's owner conversation (the hero read,
9045
+ // v0.22.0). The HERO hole is likewise never declared here; the
9046
+ // empty-state picker reads its occupant the same way.
8810
9047
 
8811
9048
  // One shared store handle: the browser and the settings page must see the
8812
9049
  // same persisted state (expansion, folder list, prefs, styling).