dsh-better-workspace 0.32.2 → 0.34.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 +4 -4
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +829 -172
package/src/client.js
CHANGED
|
@@ -90,7 +90,11 @@ window.__ModuleLoader__.load({
|
|
|
90
90
|
'wsPreset.title': '修改工作区默认预设',
|
|
91
91
|
'wsPreset.current': '当前默认预设',
|
|
92
92
|
'wsPreset.currentUnknown': '当前预设未知',
|
|
93
|
-
'
|
|
93
|
+
'presetStandardName': '标准模式',
|
|
94
|
+
'presetPtcName': 'PTC 模式',
|
|
95
|
+
'presetMinimalName': '极简模式',
|
|
96
|
+
'presetCordisName': '创造模式',
|
|
97
|
+
'wsPreset.createHint': '该工作区暂无待命的新会话,确认后将预备一个使用所选预设的新会话作为工作区默认',
|
|
94
98
|
'wsPreset.empty': '没有可用的预设',
|
|
95
99
|
'wsPreset.loadFailed': '预设列表读取失败',
|
|
96
100
|
'wsPreset.done': '已更新工作区默认预设',
|
|
@@ -109,6 +113,13 @@ window.__ModuleLoader__.load({
|
|
|
109
113
|
'settings.compactChains.hint': '单层链合并为一行,出现多个子级时自动展开为树状;拖拽工作区期间单链临时展开回文件夹树,可放入任意一级;展开状态与自定义外观保存在当前浏览器。',
|
|
110
114
|
'settings.statusPulse': '状态呼吸灯',
|
|
111
115
|
'settings.statusPulse.hint': '被折叠藏起的状态灯(完成绿 / 运行蓝 / 待交互琥珀)沿层级向外冒泡:工作区与分组行以图标呼吸发光(颜色随状态,自定义过发光的标题一起呼吸),会话分组行显示呼吸状态灯;默认开启,可在此关闭。',
|
|
116
|
+
'settings.density': '行密度',
|
|
117
|
+
'settings.density.compact': '紧凑',
|
|
118
|
+
'settings.density.comfortable': '舒适(默认)',
|
|
119
|
+
'settings.density.roomy': '宽松',
|
|
120
|
+
'settings.density.hint': '行高跟随官方侧栏:舒适 = 官方默认行高;紧凑更省纵向空间,宽松有更大的点击范围。改动立即生效,保存在当前浏览器。',
|
|
121
|
+
'settings.showCount': '会话数角标',
|
|
122
|
+
'settings.showCount.hint': '官方侧栏已移除工作区行尾的会话计数;开启后在本插件行尾恢复显示(悬停时让位给操作按钮)。默认关闭。',
|
|
112
123
|
'settings.workspaceGroup': "工作区分组",
|
|
113
124
|
'settings.workspaceGroup.hint': "磁盘目录 = 只按官方文件夹(磁盘目录)嵌套;磁盘 + 名称 = 在磁盘层之上再按标题里的 / 分组;仅名称 = 只按标题里的 / 分组,完全不理会磁盘目录的嵌套关系。旧版本的开关状态会自动迁移。",
|
|
114
125
|
'settings.sessionLimit': "展开时显示会话数",
|
|
@@ -257,7 +268,11 @@ window.__ModuleLoader__.load({
|
|
|
257
268
|
'wsPreset.title': 'Change workspace default preset',
|
|
258
269
|
'wsPreset.current': 'Current default preset',
|
|
259
270
|
'wsPreset.currentUnknown': 'Current preset unknown',
|
|
260
|
-
'
|
|
271
|
+
'presetStandardName': 'Standard mode',
|
|
272
|
+
'presetPtcName': 'PTC mode',
|
|
273
|
+
'presetMinimalName': 'Minimal mode',
|
|
274
|
+
'presetCordisName': 'Creator mode',
|
|
275
|
+
'wsPreset.createHint': 'No standing draft in this workspace; confirming prepares a new draft with the picked preset as the workspace default',
|
|
261
276
|
'wsPreset.empty': 'No presets available',
|
|
262
277
|
'wsPreset.loadFailed': 'Failed to load the preset list',
|
|
263
278
|
'wsPreset.done': 'Workspace default preset updated',
|
|
@@ -276,6 +291,13 @@ window.__ModuleLoader__.load({
|
|
|
276
291
|
'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.',
|
|
277
292
|
'settings.statusPulse': 'Status breathing light',
|
|
278
293
|
'settings.statusPulse.hint': 'Status dots hidden by collapse (done green / running blue / pending amber) bubble outward: workspace and folder rows breathe on their icon in the status color (custom-glow labels breathe along), session-group rows show a breathing dot; on by default, turn it off here.',
|
|
294
|
+
'settings.density': 'Row density',
|
|
295
|
+
'settings.density.compact': 'Compact',
|
|
296
|
+
'settings.density.comfortable': 'Comfortable (default)',
|
|
297
|
+
'settings.density.roomy': 'Roomy',
|
|
298
|
+
'settings.density.hint': 'Row heights follow the shipped sidebar: Comfortable is the official default; Compact saves vertical space, Roomy adds larger hit targets. Applies immediately, stored in this browser.',
|
|
299
|
+
'settings.showCount': 'Session count badge',
|
|
300
|
+
'settings.showCount.hint': 'The shipped sidebar retired the per-workspace session count; turn this on to restore it at the row tail (it yields to the row actions on hover). Off by default.',
|
|
279
301
|
'settings.workspaceGroup': "Workspace grouping",
|
|
280
302
|
'settings.workspaceGroup.hint': "Disk folders: nest only by the official folders (disk directories). Disk + names: layer slash-name groups on top of the disk tree. Names only: group purely by slashes in titles and ignore disk nesting entirely. A previous version's switch state migrates automatically.",
|
|
281
303
|
'settings.sessionLimit': "Sessions shown when expanded",
|
|
@@ -432,7 +454,11 @@ window.__ModuleLoader__.load({
|
|
|
432
454
|
'wsPreset.title': 'تغيير الإعداد المسبق الافتراضي لمساحة العمل',
|
|
433
455
|
'wsPreset.current': 'الإعداد المسبق الافتراضي الحالي',
|
|
434
456
|
'wsPreset.currentUnknown': 'الإعداد المسبق الحالي غير معروف',
|
|
435
|
-
'
|
|
457
|
+
'presetStandardName': 'الوضع القياسي',
|
|
458
|
+
'presetPtcName': 'وضع PTC',
|
|
459
|
+
'presetMinimalName': 'الوضع البسيط',
|
|
460
|
+
'presetCordisName': 'وضع المبدع',
|
|
461
|
+
'wsPreset.createHint': 'لا توجد جلسة جديدة بانتظار في مساحة العمل هذه؛ عند التأكيد سيُجهَّز جلسة جديدة بالإعداد المسبق المختار كافتراضي لمساحة العمل',
|
|
436
462
|
'wsPreset.empty': 'لا توجد إعدادات مسبقة متاحة',
|
|
437
463
|
'wsPreset.loadFailed': 'فشل تحميل قائمة الإعدادات المسبقة',
|
|
438
464
|
'wsPreset.done': 'تم تحديث الإعداد المسبق الافتراضي لمساحة العمل',
|
|
@@ -451,6 +477,13 @@ window.__ModuleLoader__.load({
|
|
|
451
477
|
'settings.compactChains.hint': 'تُدمج المستويات ذات الفرع الواحد في سطر واحد، وتتوسع الشجرة تلقائياً عند وجود أكثر من فرع؛ أثناء سحب مساحة عمل تُفكّ السلاسل مؤقتاً إلى مجلدات ليصبح الإفلات ممكناً في أي مستوى؛ تُحفظ حالة التوسيع والمظهر المخصص في هذا المتصفح.',
|
|
452
478
|
'settings.statusPulse': 'مؤشر حالة نابض',
|
|
453
479
|
'settings.statusPulse.hint': 'مؤشرات الحالة التي يخفيها الطيّ (مكتمل أخضر / قيد التشغيل أزرق / بانتظار تفاعل كهرماني) تتصاعد عبر المستويات: تصدر أسطر مساحات العمل والمجلدات وميضاً نابضاً للأيقونة بلون الحالة (وتنبض معها العناوين ذات التوهج المخصص)، وتعرض أسطر مجموعات الجلسات نقطة حالة نابضة؛ مفعّل افتراضياً ويمكن إيقافه هنا.',
|
|
480
|
+
'settings.density': 'كثافة الصفوف',
|
|
481
|
+
'settings.density.compact': 'مضغوط',
|
|
482
|
+
'settings.density.comfortable': 'مريح (افتراضي)',
|
|
483
|
+
'settings.density.roomy': 'واسع',
|
|
484
|
+
'settings.density.hint': 'ارتفاعات الصفوف تتبع الشريط الجانبي الرسمي: «مريح» هو الافتراضي الرسمي؛ «مضغوط» يوفر المساحة العمودية، «واسع» يمنح أهداف لمس أكبر. يسري فوراً ويُحفظ في هذا المتصفح.',
|
|
485
|
+
'settings.showCount': 'شارة عدد الجلسات',
|
|
486
|
+
'settings.showCount.hint': 'أزال الشريط الجانبي الرسمي عدّاد الجلسات لكل مساحة عمل؛ فعّله لإعادته في نهاية الصف (يتنازل لأزرار الصف عند التحويم). معطّل افتراضياً.',
|
|
454
487
|
'settings.workspaceGroup': "تجميع مساحات العمل",
|
|
455
488
|
'settings.workspaceGroup.hint': "مجلدات القرص: التداخل حسب المجلدات الرسمية (أدلة القرص) فقط. القرص + الأسماء: إضافة مجموعات الأسماء بشرطة مائلة فوق شجرة القرص. الأسماء فقط: التجميع بحسب الشرطة المائلة في العناوين وتجاهل تداخل القرص تماماً. تُنقل حالة مفتاح الإصدار السابق تلقائياً.",
|
|
456
489
|
'settings.sessionLimit': "عدد الجلسات عند التوسيع",
|
|
@@ -599,7 +632,11 @@ window.__ModuleLoader__.load({
|
|
|
599
632
|
'wsPreset.title': 'Workspace-Standardpreset ändern',
|
|
600
633
|
'wsPreset.current': 'Aktueller Standardpreset',
|
|
601
634
|
'wsPreset.currentUnknown': 'Aktueller Preset unbekannt',
|
|
602
|
-
'
|
|
635
|
+
'presetStandardName': 'Standardmodus',
|
|
636
|
+
'presetPtcName': 'PTC-Modus',
|
|
637
|
+
'presetMinimalName': 'Minimalmodus',
|
|
638
|
+
'presetCordisName': 'Kreativmodus',
|
|
639
|
+
'wsPreset.createHint': 'Kein wartender Sitzungsentwurf in diesem Workspace; nach dem Bestätigen wird ein neuer Entwurf mit dem gewählten Preset als Workspace-Standard vorbereitet',
|
|
603
640
|
'wsPreset.empty': 'Keine Presets verfügbar',
|
|
604
641
|
'wsPreset.loadFailed': 'Preset-Liste konnte nicht geladen werden',
|
|
605
642
|
'wsPreset.done': 'Workspace-Standardpreset aktualisiert',
|
|
@@ -618,6 +655,13 @@ window.__ModuleLoader__.load({
|
|
|
618
655
|
'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.',
|
|
619
656
|
'settings.statusPulse': 'Status-Pulslicht',
|
|
620
657
|
'settings.statusPulse.hint': 'Vom Einklappen verdeckte Statuspunkte (fertig grün / läuft blau / wartet amber) steigen die Hierarchie hinauf: Arbeitsbereich- und Ordnerzeilen lassen ihr Symbol in der Statusfarbe pulsieren (eigens gefärbte Titel pulsieren mit), Sitzungsgruppen zeigen einen pulsierenden Statuspunkt; standardmäßig an, hier abschaltbar.',
|
|
658
|
+
'settings.density': 'Zeilendichte',
|
|
659
|
+
'settings.density.compact': 'Kompakt',
|
|
660
|
+
'settings.density.comfortable': 'Komfortabel (Standard)',
|
|
661
|
+
'settings.density.roomy': 'Großzügig',
|
|
662
|
+
'settings.density.hint': 'Die Zeilenhöhen folgen der offiziellen Seitenleiste: Komfortabel ist der offizielle Standard; Kompakt spart vertikalen Platz, Großzügig bietet größere Trefferflächen. Wirkt sofort, wird in diesem Browser gespeichert.',
|
|
663
|
+
'settings.showCount': 'Sitzungszähler',
|
|
664
|
+
'settings.showCount.hint': 'Die offizielle Seitenleiste hat den Sitzungszähler entfernt; schalte ihn hier wieder ein (er weicht beim Hover den Zeilenaktionen). Standardmäßig aus.',
|
|
621
665
|
'settings.workspaceGroup': "Arbeitsbereich-Gruppierung",
|
|
622
666
|
'settings.workspaceGroup.hint': "Ordner: verschachtelt nur nach den offiziellen Ordnern (Festplattenpfade). Ordner + Namen: schichtet Namensgruppen über den Ordnerbaum. Nur Namen: gruppiert rein nach Schrägstrichen im Titel und ignoriert die Festplatten-Verschachtelung. Ein früherer Schalterzustand wandert automatisch mit.",
|
|
623
667
|
'settings.sessionLimit': "Sitzungen beim Aufklappen",
|
|
@@ -766,7 +810,11 @@ window.__ModuleLoader__.load({
|
|
|
766
810
|
'wsPreset.title': 'Modifier le préréglage par défaut du workspace',
|
|
767
811
|
'wsPreset.current': 'Préréglage par défaut actuel',
|
|
768
812
|
'wsPreset.currentUnknown': 'Préréglage actuel inconnu',
|
|
769
|
-
'
|
|
813
|
+
'presetStandardName': 'Mode standard',
|
|
814
|
+
'presetPtcName': 'Mode PTC',
|
|
815
|
+
'presetMinimalName': 'Mode minimal',
|
|
816
|
+
'presetCordisName': 'Mode créateur',
|
|
817
|
+
'wsPreset.createHint': 'Aucune nouvelle session en attente dans ce workspace ; la confirmation prépare une nouvelle session avec le préréglage choisi comme valeur par défaut',
|
|
770
818
|
'wsPreset.empty': 'Aucun préréglage disponible',
|
|
771
819
|
'wsPreset.loadFailed': 'Échec du chargement de la liste des préréglages',
|
|
772
820
|
'wsPreset.done': 'Préréglage par défaut du workspace mis à jour',
|
|
@@ -785,6 +833,13 @@ window.__ModuleLoader__.load({
|
|
|
785
833
|
'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.',
|
|
786
834
|
'settings.statusPulse': 'Voyant d\'état pulsant',
|
|
787
835
|
'settings.statusPulse.hint': 'Les voyants masqués par le repli (terminé vert / en cours bleu / en attente ambre) remontent la hiérarchie: les lignes d\'espace de travail et de dossier font pulser leur icône dans la couleur d\'état (les titres à lueur personnalisée pulsent aussi), les lignes de groupe de sessions affichent un voyant pulsant; activé par défaut, désactivable ici.',
|
|
836
|
+
'settings.density': 'Densité des lignes',
|
|
837
|
+
'settings.density.compact': 'Compact',
|
|
838
|
+
'settings.density.comfortable': 'Confortable (par défaut)',
|
|
839
|
+
'settings.density.roomy': 'Spacieux',
|
|
840
|
+
'settings.density.hint': 'Les hauteurs de lignes suivent la barre latérale officielle : Confortable est la valeur officielle ; Compact gagne de la place verticale, Spacieux offre de plus grandes cibles. Effectif immédiatement, enregistré dans ce navigateur.',
|
|
841
|
+
'settings.showCount': 'Compteur de sessions',
|
|
842
|
+
'settings.showCount.hint': 'La barre officielle a retiré le compteur de sessions ; réactivez-le ici (il s\'efface devant les actions au survol). Désactivé par défaut.',
|
|
788
843
|
'settings.workspaceGroup': "Regroupement des espaces",
|
|
789
844
|
'settings.workspaceGroup.hint': "Dossiers disque : imbrique uniquement selon les dossiers officiels (répertoires disque). Disque + noms : ajoute les groupes de noms par barre oblique au-dessus de l’arborescence disque. Noms seuls : regroupe uniquement par barre oblique dans les titres et ignore l’imbrication disque. L’état de l’ancien interrupteur migre automatiquement.",
|
|
790
845
|
'settings.sessionLimit': "Sessions affichées à l’ouverture",
|
|
@@ -933,7 +988,11 @@ window.__ModuleLoader__.load({
|
|
|
933
988
|
'wsPreset.title': 'वर्कस्पेस डिफ़ॉल्ट प्रीसेट बदलें',
|
|
934
989
|
'wsPreset.current': 'वर्तमान डिफ़ॉल्ट प्रीसेट',
|
|
935
990
|
'wsPreset.currentUnknown': 'वर्तमान प्रीसेट अज्ञात',
|
|
936
|
-
'
|
|
991
|
+
'presetStandardName': 'मानक मोड',
|
|
992
|
+
'presetPtcName': 'PTC मोड',
|
|
993
|
+
'presetMinimalName': 'मिनिमल मोड',
|
|
994
|
+
'presetCordisName': 'क्रिएटर मोड',
|
|
995
|
+
'wsPreset.createHint': 'इस वर्कस्पेस में कोई प्रतीक्षारत नया सेशन नहीं है; पुष्टि करने पर चयनित प्रीसेट के साथ एक नया सेशन वर्कस्पेस डिफ़ॉल्ट के रूप में तैयार किया जाएगा',
|
|
937
996
|
'wsPreset.empty': 'कोई प्रीसेट उपलब्ध नहीं',
|
|
938
997
|
'wsPreset.loadFailed': 'प्रीसेट सूची लोड करने में विफल',
|
|
939
998
|
'wsPreset.done': 'वर्कस्पेस डिफ़ॉल्ट प्रीसेट अपडेट हुआ',
|
|
@@ -952,6 +1011,13 @@ window.__ModuleLoader__.load({
|
|
|
952
1011
|
'settings.compactChains.hint': 'जिन स्तरों पर केवल एक संतान होती है वे एक पंक्ति में मिल जाते हैं; कई संतानें होने पर वृक्ष खुल जाता है; कार्यस्थान खींचते समय श्रृंखलाएँ अस्थायी रूप से फ़ोल्डरों में खुल जाती हैं ताकि किसी भी स्तर पर छोड़ा जा सके; खुलने की स्थिति और अनुकूलित रूप इसी ब्राउज़र में सहेजे जाते हैं।',
|
|
953
1012
|
'settings.statusPulse': 'स्थिति की साँस लेती बत्ती',
|
|
954
1013
|
'settings.statusPulse.hint': 'समेटने से छिपी स्थिति बत्तियाँ (पूर्ण हरी / चल रहा नीली / प्रतीक्षारत अंबर) पदानुक्रम में ऊपर उठती हैं: कार्यस्थान और फ़ोल्डर पंक्तियों का चिह्न स्थिति के रंग में साँस लेता है (अनुकूलित चमक वाले शीर्षक भी साथ साँस लेते हैं), सत्र समूह पंक्तियों पर साँस लेता स्थिति बिंदु दिखता है; डिफ़ॉल्ट रूप से चालू, यहाँ बंद किया जा सकता है।',
|
|
1014
|
+
'settings.density': 'पंक्ति घनत्व',
|
|
1015
|
+
'settings.density.compact': 'संकुचित',
|
|
1016
|
+
'settings.density.comfortable': 'आरामदायक (डिफ़ॉल्ट)',
|
|
1017
|
+
'settings.density.roomy': 'विस्तृत',
|
|
1018
|
+
'settings.density.hint': 'पंक्ति ऊँचाइयाँ आधिकारिक साइडबार के अनुसार: आरामदायक आधिकारिक डिफ़ॉल्ट है; संकुचित ऊर्ध्वाधर जगह बचाता है, विस्तृत बड़े क्लिक क्षेत्र देता है। तुरंत लागू, इस ब्राउज़र में सहेजा जाता है।',
|
|
1019
|
+
'settings.showCount': 'सेशन गिनती बैज',
|
|
1020
|
+
'settings.showCount.hint': 'आधिकारिक साइडबार ने सेशन गिनती हटा दी; इसे पंक्ति के अंत में वापस लाने के लिए चालू करें (होवर पर पंक्ति क्रियाओं को जगह देता है)। डिफ़ॉल्ट रूप से बंद।',
|
|
955
1021
|
'settings.workspaceGroup': "वर्कस्पेस समूहीकरण",
|
|
956
1022
|
'settings.workspaceGroup.hint': "डिस्क फ़ोल्डर: केवल आधिकारिक फ़ोल्डर (डिस्क निर्देशिका) से नेस्ट। डिस्क + नाम: डिस्क ट्री के ऊपर स्लैश नाम समूह जोड़ता है। केवल नाम: केवल शीर्षक के स्लैश से समूहित करता है, डिस्क नेस्टिंग की अनदेखी करता है। पुराने स्विच की स्थिति स्वतः माइग्रेट होती है।",
|
|
957
1023
|
'settings.sessionLimit': "विस्तार पर दिखने वाले सेशन",
|
|
@@ -1100,7 +1166,11 @@ window.__ModuleLoader__.load({
|
|
|
1100
1166
|
'wsPreset.title': 'Ubah preset bawaan ruang kerja',
|
|
1101
1167
|
'wsPreset.current': 'Preset bawaan saat ini',
|
|
1102
1168
|
'wsPreset.currentUnknown': 'Preset saat ini tidak diketahui',
|
|
1103
|
-
'
|
|
1169
|
+
'presetStandardName': 'Mode standar',
|
|
1170
|
+
'presetPtcName': 'Mode PTC',
|
|
1171
|
+
'presetMinimalName': 'Mode minimal',
|
|
1172
|
+
'presetCordisName': 'Mode kreator',
|
|
1173
|
+
'wsPreset.createHint': 'Tidak ada sesi baru yang menunggu di ruang kerja ini; setelah dikonfirmasi, sesi baru dengan prasetel pilihan akan disiapkan sebagai bawaan ruang kerja',
|
|
1104
1174
|
'wsPreset.empty': 'Tidak ada preset yang tersedia',
|
|
1105
1175
|
'wsPreset.loadFailed': 'Gagal memuat daftar preset',
|
|
1106
1176
|
'wsPreset.done': 'Preset bawaan ruang kerja diperbarui',
|
|
@@ -1119,6 +1189,13 @@ window.__ModuleLoader__.load({
|
|
|
1119
1189
|
'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.',
|
|
1120
1190
|
'settings.statusPulse': 'Lampu status bernapas',
|
|
1121
1191
|
'settings.statusPulse.hint': 'Lampu status yang tersembunyi karena pelipatan (selesai hijau / berjalan biru / menunggu kuning) menggelembung ke tingkat atas: baris ruang kerja dan folder membuat ikonnya bernapas dalam warna status (judul dengan pendar khusus ikut bernapas), baris grup sesi menampilkan titik status yang bernapas; aktif secara bawaan, bisa dimatikan di sini.',
|
|
1192
|
+
'settings.density': 'Kepadatan baris',
|
|
1193
|
+
'settings.density.compact': 'Ringkas',
|
|
1194
|
+
'settings.density.comfortable': 'Nyaman (bawaan)',
|
|
1195
|
+
'settings.density.roomy': 'Lega',
|
|
1196
|
+
'settings.density.hint': 'Tinggi baris mengikuti bilah samping resmi: Nyaman adalah bawaan resmi; Ringkas menghemat ruang vertikal, Lega memberi area klik lebih besar. Berlaku langsung, disimpan di peramban ini.',
|
|
1197
|
+
'settings.showCount': 'Lencana jumlah sesi',
|
|
1198
|
+
'settings.showCount.hint': 'Bilah samping resmi menghapus hitungan sesi; aktifkan untuk menampilkannya lagi di ujung baris (memberi tempat untuk tombol baris saat hover). Mati secara bawaan.',
|
|
1122
1199
|
'settings.workspaceGroup': "Pengelompokan ruang kerja",
|
|
1123
1200
|
'settings.workspaceGroup.hint': "Folder disk: bersarang hanya mengikuti folder resmi (direktori disk). Disk + nama: menambahkan grup nama garis miring di atas pohon disk. Hanya nama: mengelompokkan murni dari garis miring pada judul dan mengabaikan struktur disk. Status sakelar versi lama bermigrasi otomatis.",
|
|
1124
1201
|
'settings.sessionLimit': "Sesi saat dibuka",
|
|
@@ -1267,7 +1344,11 @@ window.__ModuleLoader__.load({
|
|
|
1267
1344
|
'wsPreset.title': 'Modifica il preset predefinito dello workspace',
|
|
1268
1345
|
'wsPreset.current': 'Preset predefinito attuale',
|
|
1269
1346
|
'wsPreset.currentUnknown': 'Preset attuale sconosciuto',
|
|
1270
|
-
'
|
|
1347
|
+
'presetStandardName': 'Modalità standard',
|
|
1348
|
+
'presetPtcName': 'Modalità PTC',
|
|
1349
|
+
'presetMinimalName': 'Modalità minimale',
|
|
1350
|
+
'presetCordisName': 'Modalità creatore',
|
|
1351
|
+
'wsPreset.createHint': 'Nessuna nuova sessione in attesa in questo workspace; confermando verrà preparata una nuova sessione con il preset scelto come predefinito del workspace',
|
|
1271
1352
|
'wsPreset.empty': 'Nessun preset disponibile',
|
|
1272
1353
|
'wsPreset.loadFailed': 'Impossibile caricare la lista dei preset',
|
|
1273
1354
|
'wsPreset.done': 'Preset predefinito dello workspace aggiornato',
|
|
@@ -1286,6 +1367,13 @@ window.__ModuleLoader__.load({
|
|
|
1286
1367
|
'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.',
|
|
1287
1368
|
'settings.statusPulse': 'Spia di stato pulsante',
|
|
1288
1369
|
'settings.statusPulse.hint': 'Le spie nascoste dalla compressione (completato verde / in esecuzione blu / in attesa ambra) risalgono la gerarchia: le righe di aree di lavoro e cartelle fanno pulsare l\'icona nel colore di stato (anche i titoli con bagliore personalizzato pulsano), le righe dei gruppi di sessioni mostrano una spia pulsante; attiva per impostazione predefinita, disattivabile qui.',
|
|
1370
|
+
'settings.density': 'Densità delle righe',
|
|
1371
|
+
'settings.density.compact': 'Compatto',
|
|
1372
|
+
'settings.density.comfortable': 'Comodo (predefinito)',
|
|
1373
|
+
'settings.density.roomy': 'Ampio',
|
|
1374
|
+
'settings.density.hint': 'Le altezze delle righe seguono la barra laterale ufficiale: Comodo è il predefinito ufficiale; Compatto risparmia spazio verticale, Ampio offre aree di clic maggiori. Immediato, salvato in questo browser.',
|
|
1375
|
+
'settings.showCount': 'Conteggio sessioni',
|
|
1376
|
+
'settings.showCount.hint': 'La barra ufficiale ha rimosso il conteggio sessioni; attivalo per ripristinarlo in coda alla riga (cede ai pulsanti della riga al passaggio del mouse). Disattivato per impostazione predefinita.',
|
|
1289
1377
|
'settings.workspaceGroup': "Raggruppamento aree di lavoro",
|
|
1290
1378
|
'settings.workspaceGroup.hint': "Cartelle disco: annida solo secondo le cartelle ufficiali (directory disco). Disco + nomi: aggiunge gruppi di nomi per barra obliqua sopra l’albero disco. Solo nomi: raggruppa solo per barra obliqua nei titoli ignorando l’annidamento disco. Lo stato del vecchio interruttore migra automaticamente.",
|
|
1291
1379
|
'settings.sessionLimit': "Sessioni mostrate all’apertura",
|
|
@@ -1434,7 +1522,11 @@ window.__ModuleLoader__.load({
|
|
|
1434
1522
|
'wsPreset.title': 'ワークスペース既定プリセットを変更',
|
|
1435
1523
|
'wsPreset.current': '現在の既定プリセット',
|
|
1436
1524
|
'wsPreset.currentUnknown': '現在のプリセットは不明',
|
|
1437
|
-
'
|
|
1525
|
+
'presetStandardName': '標準モード',
|
|
1526
|
+
'presetPtcName': 'PTC モード',
|
|
1527
|
+
'presetMinimalName': 'ミニマルモード',
|
|
1528
|
+
'presetCordisName': 'クリエイターモード',
|
|
1529
|
+
'wsPreset.createHint': 'このワークスペースには待機中の新規セッションがありません。確定すると、選択したプリセットで新しいセッションをワークスペースの既定として用意します',
|
|
1438
1530
|
'wsPreset.empty': '利用可能なプリセットがありません',
|
|
1439
1531
|
'wsPreset.loadFailed': 'プリセット一覧の読み込みに失敗しました',
|
|
1440
1532
|
'wsPreset.done': 'ワークスペース既定プリセットを更新しました',
|
|
@@ -1453,6 +1545,13 @@ window.__ModuleLoader__.load({
|
|
|
1453
1545
|
'settings.compactChains.hint': '子が 1 つだけの階層は 1 行にまとまり,複数の子がある階層はツリーとして展開されます;ワークスペースのドラッグ中は一時的にフォルダーツリーへ戻り,どの階層にもドロップできます;展開状態とカスタム外観はこのブラウザーに保存されます。',
|
|
1454
1546
|
'settings.statusPulse': 'ステータス表示灯',
|
|
1455
1547
|
'settings.statusPulse.hint': '折りたたみで隠れたステータス表示(完了は緑 / 実行中は青 / 操作待ちは琥珀)が上位の階層へ伝わります:ワークスペースとフォルダーの行はアイコンがステータス色で明滅し(グローを設定したタイトルも連動),セッショングループの行には明滅するステータスドットが出ます;既定はオンで,ここでオフにできます。',
|
|
1548
|
+
'settings.density': '行の密度',
|
|
1549
|
+
'settings.density.compact': 'コンパクト',
|
|
1550
|
+
'settings.density.comfortable': '標準(既定)',
|
|
1551
|
+
'settings.density.roomy': 'ゆったり',
|
|
1552
|
+
'settings.density.hint': '行の高さは公式サイドバーに従います:標準(既定)が公式の既定値。コンパクトは縦幅を節約、ゆったりはクリック領域を広く。変更は即時反映され、このブラウザーに保存されます。',
|
|
1553
|
+
'settings.showCount': 'セッション数バッジ',
|
|
1554
|
+
'settings.showCount.hint': '公式サイドバーはワークスペース行のセッション数を廃止しました。オンにすると行末に再表示します(ホバー時は操作ボタンに譲ります)。既定はオフ。',
|
|
1456
1555
|
'settings.workspaceGroup': "ワークスペースのグループ化",
|
|
1457
1556
|
'settings.workspaceGroup.hint': "フォルダー:公式フォルダー(ディスク ディレクトリ)のみで階層化。フォルダー + 名前:ディスク ツリーに加えてタイトルのスラッシュでもグループ化。名前のみ:タイトルのスラッシュだけでグループ化し、ディスクの階層は無視。旧バージョンのスイッチ状態は自動的に引き継がれます。",
|
|
1458
1557
|
'settings.sessionLimit': "展開時に表示するセッション数",
|
|
@@ -1601,7 +1700,11 @@ window.__ModuleLoader__.load({
|
|
|
1601
1700
|
'wsPreset.title': '작업 공간 기본 프리셋 변경',
|
|
1602
1701
|
'wsPreset.current': '현재 기본 프리셋',
|
|
1603
1702
|
'wsPreset.currentUnknown': '현재 프리셋을 알 수 없음',
|
|
1604
|
-
'
|
|
1703
|
+
'presetStandardName': '표준 모드',
|
|
1704
|
+
'presetPtcName': 'PTC 모드',
|
|
1705
|
+
'presetMinimalName': '미니멀 모드',
|
|
1706
|
+
'presetCordisName': '크리에이터 모드',
|
|
1707
|
+
'wsPreset.createHint': '이 작업 공간에는 대기 중인 새 세션이 없습니다. 확인하면 선택한 프리셋으로 새 세션을 준비해 작업 공간 기본값으로 사용합니다',
|
|
1605
1708
|
'wsPreset.empty': '사용 가능한 프리셋이 없습니다',
|
|
1606
1709
|
'wsPreset.loadFailed': '프리셋 목록을 불러오지 못했습니다',
|
|
1607
1710
|
'wsPreset.done': '작업 공간 기본 프리셋이 업데이트되었습니다',
|
|
@@ -1620,6 +1723,13 @@ window.__ModuleLoader__.load({
|
|
|
1620
1723
|
'settings.compactChains.hint': '자식이 하나뿐인 단계는 한 줄로 합쳐지고, 자식이 여러 개면 트리로 펼쳐집니다; 워크스페이스를 끌어 놓는 동안에는 체인이 잠시 폴더 트리로 펼쳐져 어느 단계에나 놓을 수 있습니다; 펼침 상태와 사용자 지정 모양은 이 브라우저에 저장됩니다.',
|
|
1621
1724
|
'settings.statusPulse': '상태 표시등',
|
|
1622
1725
|
'settings.statusPulse.hint': '접혀서 가려진 상태 표시(완료는 초록 / 실행은 파랑 / 대기는 호박색)가 상위 단계로 번집니다: 워크스페이스와 폴더 행은 아이콘이 상태 색으로 깜빡이고(글로우를 지정한 제목도 함께 깜빡임), 세션 그룹 행에는 깜빡이는 상태 점이 표시됩니다; 기본값은 켜짐이며 여기서 끌 수 있습니다.',
|
|
1726
|
+
'settings.density': '행 밀도',
|
|
1727
|
+
'settings.density.compact': '컴팩트',
|
|
1728
|
+
'settings.density.comfortable': '편안함(기본값)',
|
|
1729
|
+
'settings.density.roomy': '여유',
|
|
1730
|
+
'settings.density.hint': '행 높이는 공식 사이드바를 따릅니다: 편안함이 공식 기본값이며, 컴팩트는 세로 공간을 아끼고 여유는 더 큰 클릭 영역을 줍니다. 즉시 적용되며 이 브라우저에 저장됩니다.',
|
|
1731
|
+
'settings.showCount': '세션 수 배지',
|
|
1732
|
+
'settings.showCount.hint': '공식 사이드바는 워크스페이스 행의 세션 수를 제거했습니다. 켜면 행 끝에 다시 표시합니다(호버 시 작업 버튼에 양보). 기본값은 꺼짐입니다.',
|
|
1623
1733
|
'settings.workspaceGroup': "작업 공간 그룹화",
|
|
1624
1734
|
'settings.workspaceGroup.hint': "디스크 폴더: 공식 폴더(디스크 디렉터리)로만 중첩합니다. 디스크 + 이름: 디스크 트리 위에 슬래시 이름 그룹을 얹습니다. 이름만: 제목의 슬래시로만 그룹화하고 디스크 중첩은 무시합니다. 이전 버전 스위치 상태는 자동으로 이전됩니다.",
|
|
1625
1735
|
'settings.sessionLimit': "펼칠 때 표시할 세션",
|
|
@@ -1768,7 +1878,11 @@ window.__ModuleLoader__.load({
|
|
|
1768
1878
|
'wsPreset.title': 'Standaardpreset van workspace wijzigen',
|
|
1769
1879
|
'wsPreset.current': 'Huidige standaardpreset',
|
|
1770
1880
|
'wsPreset.currentUnknown': 'Huidige preset onbekend',
|
|
1771
|
-
'
|
|
1881
|
+
'presetStandardName': 'Standaardmodus',
|
|
1882
|
+
'presetPtcName': 'PTC-modus',
|
|
1883
|
+
'presetMinimalName': 'Minimale modus',
|
|
1884
|
+
'presetCordisName': 'Creatormodus',
|
|
1885
|
+
'wsPreset.createHint': 'Geen wachtende nieuwe sessie in deze workspace; bij bevestigen wordt een nieuwe sessie met de gekozen preset als workspace-standaard voorbereid',
|
|
1772
1886
|
'wsPreset.empty': 'Geen presets beschikbaar',
|
|
1773
1887
|
'wsPreset.loadFailed': 'Laden van de presetlijst is mislukt',
|
|
1774
1888
|
'wsPreset.done': 'Standaardpreset van workspace bijgewerkt',
|
|
@@ -1787,6 +1901,13 @@ window.__ModuleLoader__.load({
|
|
|
1787
1901
|
'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.',
|
|
1788
1902
|
'settings.statusPulse': 'Pulserend statuslampje',
|
|
1789
1903
|
'settings.statusPulse.hint': 'Statuslampjes die door het inklappen verborgen zijn (voltooid groen / actief blauw / wachtend amber) borrelen door de hiërarchie omhoog: werkruimte- en mapregels laten hun pictogram in de statuskleur pulseren (titels met eigen gloed pulseren mee), sessiegroepregels tonen een pulserend statusstipje; standaard aan, hier uit te zetten.',
|
|
1904
|
+
'settings.density': 'Rijdichtheid',
|
|
1905
|
+
'settings.density.compact': 'Compact',
|
|
1906
|
+
'settings.density.comfortable': 'Comfortabel (standaard)',
|
|
1907
|
+
'settings.density.roomy': 'Ruim',
|
|
1908
|
+
'settings.density.hint': 'Rijhoogtes volgen de officiële zijbalk: Comfortabel is de officiële standaard; Compact bespaart verticale ruimte, Ruim geeft grotere klikgebieden. Direct actief, opgeslagen in deze browser.',
|
|
1909
|
+
'settings.showCount': 'Sessieteller',
|
|
1910
|
+
'settings.showCount.hint': 'De officiële zijbalk heeft de sessieteller verwijderd; zet hem hier weer aan (hij wijkt bij hover voor de rijacties). Standaard uit.',
|
|
1790
1911
|
'settings.workspaceGroup': "Werkruimtegroepering",
|
|
1791
1912
|
'settings.workspaceGroup.hint': "Schijfmappen: nest alleen volgens de officiële mappen (schijfdirectories). Schijf + namen: stapelt naamgroepen bovenop de schijfboom. Alleen namen: groept puur op schuine strepen in titels en negeert schijfnesting. De oude schakelaarstand migreert automatisch.",
|
|
1792
1913
|
'settings.sessionLimit': "Sessies bij uitklappen",
|
|
@@ -1935,7 +2056,11 @@ window.__ModuleLoader__.load({
|
|
|
1935
2056
|
'wsPreset.title': 'Zmień domyślny preset obszaru roboczego',
|
|
1936
2057
|
'wsPreset.current': 'Aktualny domyślny preset',
|
|
1937
2058
|
'wsPreset.currentUnknown': 'Aktualny preset nieznany',
|
|
1938
|
-
'
|
|
2059
|
+
'presetStandardName': 'Tryb standardowy',
|
|
2060
|
+
'presetPtcName': 'Tryb PTC',
|
|
2061
|
+
'presetMinimalName': 'Tryb minimalny',
|
|
2062
|
+
'presetCordisName': 'Tryb twórcy',
|
|
2063
|
+
'wsPreset.createHint': 'Brak oczekującej nowej sesji w tym obszarze roboczym; po potwierdzeniu zostanie przygotowana nowa sesja z wybranym ustawieniem jako domyślnym obszaru roboczego',
|
|
1939
2064
|
'wsPreset.empty': 'Brak dostępnych presetów',
|
|
1940
2065
|
'wsPreset.loadFailed': 'Nie udało się wczytać listy presetów',
|
|
1941
2066
|
'wsPreset.done': 'Zaktualizowano domyślny preset obszaru roboczego',
|
|
@@ -1954,6 +2079,13 @@ window.__ModuleLoader__.load({
|
|
|
1954
2079
|
'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.',
|
|
1955
2080
|
'settings.statusPulse': 'Pulsująca lampka stanu',
|
|
1956
2081
|
'settings.statusPulse.hint': 'Lampki ukryte przez zwinięcie (ukończono zielona / trwa niebieska / czeka bursztynowa) bąbelkują w górę hierarchii: wiersze obszarów roboczych i folderów pulsują ikoną w kolorze stanu (tytuły z własną poświatą pulsują razem), wiersze grup sesji pokazują pulsującą kropkę stanu; domyślnie włączone, tutaj można wyłączyć.',
|
|
2082
|
+
'settings.density': 'Gęstość wierszy',
|
|
2083
|
+
'settings.density.compact': 'Zwięzły',
|
|
2084
|
+
'settings.density.comfortable': 'Wygodny (domyślny)',
|
|
2085
|
+
'settings.density.roomy': 'Przestronny',
|
|
2086
|
+
'settings.density.hint': 'Wysokości wierszy są zgodne z oficjalnym panelem: Wygodny to oficjalny domyślny; Zwięzły oszczędza miejsce w pionie, Przestronny daje większe pola klikania. Działa natychmiast, zapis w tej przeglądarce.',
|
|
2087
|
+
'settings.showCount': 'Licznik sesji',
|
|
2088
|
+
'settings.showCount.hint': 'Oficjalny panel usunął licznik sesji; włącz, aby przywrócić go na końcu wiersza (ustępuje przyciskom wiersza po najechaniu). Domyślnie wyłączone.',
|
|
1957
2089
|
'settings.workspaceGroup': "Grupowanie obszarów roboczych",
|
|
1958
2090
|
'settings.workspaceGroup.hint': "Foldery dysku: zagnieżdżanie wyłącznie według oficjalnych folderów (katalogów dysku). Dysk + nazwy: dokłada grupy nazw nad drzewem dysku. Tylko nazwy: grupuje wyłącznie po ukośnikach w tytułach, ignorując zagnieżdżanie dyskowe. Stan starego przełącznika migruje automatycznie.",
|
|
1959
2091
|
'settings.sessionLimit': "Sesje po rozwinięciu",
|
|
@@ -2102,7 +2234,11 @@ window.__ModuleLoader__.load({
|
|
|
2102
2234
|
'wsPreset.title': 'Alterar o preset padrão do workspace',
|
|
2103
2235
|
'wsPreset.current': 'Preset padrão atual',
|
|
2104
2236
|
'wsPreset.currentUnknown': 'Preset atual desconhecido',
|
|
2105
|
-
'
|
|
2237
|
+
'presetStandardName': 'Modo padrão',
|
|
2238
|
+
'presetPtcName': 'Modo PTC',
|
|
2239
|
+
'presetMinimalName': 'Modo mínimo',
|
|
2240
|
+
'presetCordisName': 'Modo criador',
|
|
2241
|
+
'wsPreset.createHint': 'Nenhuma nova sessão em espera neste workspace; ao confirmar, uma nova sessão com o predefinido escolhido será preparada como padrão do workspace',
|
|
2106
2242
|
'wsPreset.empty': 'Nenhum preset disponível',
|
|
2107
2243
|
'wsPreset.loadFailed': 'Falha ao carregar a lista de presets',
|
|
2108
2244
|
'wsPreset.done': 'Preset padrão do workspace atualizado',
|
|
@@ -2121,6 +2257,13 @@ window.__ModuleLoader__.load({
|
|
|
2121
2257
|
'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.',
|
|
2122
2258
|
'settings.statusPulse': 'Luz de status pulsante',
|
|
2123
2259
|
'settings.statusPulse.hint': 'As luzes escondidas pelo recolhimento (concluído verde / em execução azul / aguardando âmbar) sobem pela hierarquia: linhas de espaço de trabalho e pasta pulsam o ícone na cor do status (títulos com brilho personalizado pulsam junto), linhas de grupo de sessões mostram um ponto pulsante; ativado por padrão, pode ser desligado aqui.',
|
|
2260
|
+
'settings.density': 'Densidade das linhas',
|
|
2261
|
+
'settings.density.compact': 'Compacto',
|
|
2262
|
+
'settings.density.comfortable': 'Confortável (padrão)',
|
|
2263
|
+
'settings.density.roomy': 'Espaçoso',
|
|
2264
|
+
'settings.density.hint': 'As alturas das linhas seguem a barra lateral oficial: Confortável é o padrão oficial; Compacto economiza espaço vertical, Espaçoso dá áreas de clique maiores. Aplica na hora, salvo neste navegador.',
|
|
2265
|
+
'settings.showCount': 'Contador de sessões',
|
|
2266
|
+
'settings.showCount.hint': 'A barra oficial removeu a contagem de sessões; ative para restaurá-la no fim da linha (cede aos botões da linha ao passar o mouse). Desativado por padrão.',
|
|
2124
2267
|
'settings.workspaceGroup': "Agrupamento de espaços",
|
|
2125
2268
|
'settings.workspaceGroup.hint': "Pastas do disco: aninhar apenas pelas pastas oficiais (diretórios de disco). Disco + nomes: acrescenta grupos de nomes sobre a árvore de disco. Apenas nomes: agrupa só pela barra nos títulos, ignorando o aninhamento de disco. O estado do interruptor antigo migra automaticamente.",
|
|
2126
2269
|
'settings.sessionLimit': "Sessões ao expandir",
|
|
@@ -2269,7 +2412,11 @@ window.__ModuleLoader__.load({
|
|
|
2269
2412
|
'wsPreset.title': 'Изменить пресет воркспейса по умолчанию',
|
|
2270
2413
|
'wsPreset.current': 'Текущий пресет по умолчанию',
|
|
2271
2414
|
'wsPreset.currentUnknown': 'Текущий пресет неизвестен',
|
|
2272
|
-
'
|
|
2415
|
+
'presetStandardName': 'Стандартный режим',
|
|
2416
|
+
'presetPtcName': 'Режим PTC',
|
|
2417
|
+
'presetMinimalName': 'Минимальный режим',
|
|
2418
|
+
'presetCordisName': 'Режим создателя',
|
|
2419
|
+
'wsPreset.createHint': 'В этом воркспейсе нет ожидающей новой сессии; после подтверждения будет подготовлена новая сессия с выбранным пресетом в качестве дефолта воркспейса',
|
|
2273
2420
|
'wsPreset.empty': 'Нет доступных пресетов',
|
|
2274
2421
|
'wsPreset.loadFailed': 'Не удалось загрузить список пресетов',
|
|
2275
2422
|
'wsPreset.done': 'Пресет воркспейса по умолчанию обновлён',
|
|
@@ -2288,6 +2435,13 @@ window.__ModuleLoader__.load({
|
|
|
2288
2435
|
'settings.compactChains.hint': 'Уровни с единственным потомком сливаются в одну строку; при нескольких потомках дерево разворачивается; во время перетаскивания рабочей области цепочки временно разворачиваются в папки, чтобы можно было положить элемент на любой уровень; состояние сворачивания и оформление хранятся в этом браузере.',
|
|
2289
2436
|
'settings.statusPulse': 'Пульсирующий индикатор состояния',
|
|
2290
2437
|
'settings.statusPulse.hint': 'Индикаторы, скрытые сворачиванием (завершено — зелёный / выполняется — синий / ожидает — янтарный), всплывают вверх по иерархии: строки рабочих областей и папок пульсируют значком в цвете состояния (заголовки со своим свечением пульсируют вместе с ними), строки групп сессий показывают пульсирующую точку; включено по умолчанию, здесь можно отключить.',
|
|
2438
|
+
'settings.density': 'Плотность строк',
|
|
2439
|
+
'settings.density.compact': 'Компактно',
|
|
2440
|
+
'settings.density.comfortable': 'Обычно (по умолчанию)',
|
|
2441
|
+
'settings.density.roomy': 'Просторно',
|
|
2442
|
+
'settings.density.hint': 'Высоты строк соответствуют официальной боковой панели: «Обычно» — официальный стандарт; «Компактно» экономит место по вертикали, «Просторно» увеличивает зоны нажатия. Применяется сразу, хранится в этом браузере.',
|
|
2443
|
+
'settings.showCount': 'Счётчик сессий',
|
|
2444
|
+
'settings.showCount.hint': 'Официальная панель убрала счётчик сессий; включите, чтобы вернуть его в конец строки (уступает кнопкам строки при наведении). По умолчанию выключено.',
|
|
2291
2445
|
'settings.workspaceGroup': "Группировка рабочих областей",
|
|
2292
2446
|
'settings.workspaceGroup.hint': "Папки диска: вложенность только по официальным папкам (каталогам диска). Диск + имена: добавляет группы имён поверх дерева диска. Только имена: группирует лишь по косой черте в названиях, игнорируя вложенность диска. Состояние старого переключателя переносится автоматически.",
|
|
2293
2447
|
'settings.sessionLimit': "Сессий при разворачивании",
|
|
@@ -2436,7 +2590,11 @@ window.__ModuleLoader__.load({
|
|
|
2436
2590
|
'wsPreset.title': 'Ändra standardförinställning för arbetsyta',
|
|
2437
2591
|
'wsPreset.current': 'Aktuell standardförinställning',
|
|
2438
2592
|
'wsPreset.currentUnknown': 'Aktuell förinställning okänd',
|
|
2439
|
-
'
|
|
2593
|
+
'presetStandardName': 'Standardläge',
|
|
2594
|
+
'presetPtcName': 'PTC-läge',
|
|
2595
|
+
'presetMinimalName': 'Minimalt läge',
|
|
2596
|
+
'presetCordisName': 'Skaparläge',
|
|
2597
|
+
'wsPreset.createHint': 'Ingen väntande ny session i denna arbetsyta; vid bekräftelse förbereds en ny session med den valda förinställningen som arbetsytans standard',
|
|
2440
2598
|
'wsPreset.empty': 'Inga förinställningar tillgängliga',
|
|
2441
2599
|
'wsPreset.loadFailed': 'Kunde inte läsa in förinställningslistan',
|
|
2442
2600
|
'wsPreset.done': 'Standardförinställning för arbetsyta uppdaterad',
|
|
@@ -2455,6 +2613,13 @@ window.__ModuleLoader__.load({
|
|
|
2455
2613
|
'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.',
|
|
2456
2614
|
'settings.statusPulse': 'Pulserande statuslampa',
|
|
2457
2615
|
'settings.statusPulse.hint': 'Statuslampor som döljs av ihopfällningen (klart grön / körs blå / väntar bärnsten) bubblar uppåt i hierarkin: rader för arbetsytor och mappar pulserar ikonen i statusfärgen (titlar med egen glöd pulserar med), sessionsgrupper visar en pulserande prick; på som standard, stängs av här.',
|
|
2616
|
+
'settings.density': 'Radtäthet',
|
|
2617
|
+
'settings.density.compact': 'Kompakt',
|
|
2618
|
+
'settings.density.comfortable': 'Bekväm (standard)',
|
|
2619
|
+
'settings.density.roomy': 'Rymlig',
|
|
2620
|
+
'settings.density.hint': 'Radhöjder följer det officiella sidofältet: Bekväm är officiell standard; Kompakt sparar vertikalt utrymme, Rymlig ger större klickytor. Verkar direkt, sparas i webbläsaren.',
|
|
2621
|
+
'settings.showCount': 'Sessionsräknare',
|
|
2622
|
+
'settings.showCount.hint': 'Det officiella sidofältet tog bort sessionsräknaren; slå på den för att återställa den i radens slut (den väjer för radknapparna vid hover). Av som standard.',
|
|
2458
2623
|
'settings.workspaceGroup': "Arbetsytegruppering",
|
|
2459
2624
|
'settings.workspaceGroup.hint': "Skivmappar: kapslar bara enligt de officiella mapparna (skivkataloger). Skiva + namn: lägger namngrupper ovanpå skivträdet. Endast namn: grupperar rent efter snedstreck i titlar och ignorerar skivkapsling. Den gamla reglagestatusen migreras automatiskt.",
|
|
2460
2625
|
'settings.sessionLimit': "Sessioner vid utfällning",
|
|
@@ -2603,7 +2768,11 @@ window.__ModuleLoader__.load({
|
|
|
2603
2768
|
'wsPreset.title': 'เปลี่ยนพรีเซ็ตเริ่มต้นของพื้นที่ทำงาน',
|
|
2604
2769
|
'wsPreset.current': 'พรีเซ็ตเริ่มต้นปัจจุบัน',
|
|
2605
2770
|
'wsPreset.currentUnknown': 'ไม่ทราบพรีเซ็ตปัจจุบัน',
|
|
2606
|
-
'
|
|
2771
|
+
'presetStandardName': 'โหมดมาตรฐาน',
|
|
2772
|
+
'presetPtcName': 'โหมด PTC',
|
|
2773
|
+
'presetMinimalName': 'โหมดมินิมอล',
|
|
2774
|
+
'presetCordisName': 'โหมดครีเอเตอร์',
|
|
2775
|
+
'wsPreset.createHint': 'ยังไม่มีเซสชันใหม่ที่รออยู่ในพื้นที่ทำงานนี้; เมื่อยืนยันจะเตรียมเซสชันใหม่ที่ใช้พรีเซ็ตที่เลือกเป็นค่าเริ่มต้นของพื้นที่ทำงาน',
|
|
2607
2776
|
'wsPreset.empty': 'ไม่มีพรีเซ็ตที่ใช้ได้',
|
|
2608
2777
|
'wsPreset.loadFailed': 'โหลดรายการพรีเซ็ตไม่สำเร็จ',
|
|
2609
2778
|
'wsPreset.done': 'อัปเดตพรีเซ็ตเริ่มต้นของพื้นที่ทำงานแล้ว',
|
|
@@ -2622,6 +2791,13 @@ window.__ModuleLoader__.load({
|
|
|
2622
2791
|
'settings.compactChains.hint': 'ระดับที่มีลูกเพียงหนึ่งจะถูกรวมเป็นบรรทัดเดียว เมื่อมีลูกหลายตัวต้นไม้จะขยายออก ขณะลากพื้นที่ทำงาน สายจะขยายกลับเป็นโฟลเดอร์ชั่วคราวเพื่อให้วางได้ทุกระดับ สถานะการขยายและรูปลักษณ์ที่กำหนดเองจะถูกเก็บไว้ในเบราว์เซอร์นี้',
|
|
2623
2792
|
'settings.statusPulse': 'ไฟสถานะหายใจ',
|
|
2624
2793
|
'settings.statusPulse.hint': 'ไฟสถานะที่ถูกย่อซ่อนไว้ (เสร็จแล้วสีเขียว / กำลังทำงานสีน้ำเงิน / รอตอบสนองสีเหลืองอำพัน) จะลอยขึ้นตามลำดับชั้น: แถวพื้นที่ทำงานและโฟลเดอร์จะให้ไอคอนหายใจเป็นสีตามสถานะ (ชื่อที่มีการเรืองแสงกำหนดเองก็หายใจตาม) แถวกลุ่มเซสชันจะแสดงจุดสถานะที่หายใจ เปิดไว้เป็นค่าเริ่มต้น ปิดได้ที่นี่',
|
|
2794
|
+
'settings.density': 'ความหนาแน่นของแถว',
|
|
2795
|
+
'settings.density.compact': 'กระชับ',
|
|
2796
|
+
'settings.density.comfortable': 'ปกติ (ค่าเริ่มต้น)',
|
|
2797
|
+
'settings.density.roomy': 'กว้าง',
|
|
2798
|
+
'settings.density.hint': 'ความสูงของแถวเป็นไปตามแถบข้างทางการ: ปกติคือค่าเริ่มต้นทางการ; กระชับประหยัดพื้นที่แนวตั้ง กว้างให้พื้นที่คลิกใหญ่ขึ้น ใช้ได้ทันทีและบันทึกในเบราว์เซอร์นี้',
|
|
2799
|
+
'settings.showCount': 'ป้ายจำนวนเซสชัน',
|
|
2800
|
+
'settings.showCount.hint': 'แถบข้างทางการได้ลบตัวนับเซสชันออก; เปิดเพื่อแสดงอีกครั้งที่ท้ายแถว (เว้นให้ปุ่มการทำงานของแถวเมื่อชี้เมาส์) ปิดอยู่เป็นค่าเริ่มต้น',
|
|
2625
2801
|
'settings.workspaceGroup': "การจัดกลุ่มพื้นที่ทำงาน",
|
|
2626
2802
|
'settings.workspaceGroup.hint': "โฟลเดอร์ดิสก์: ซ้อนตามโฟลเดอร์ทางการ (ไดเรกทอรีดิสก์) เท่านั้น ดิสก์ + ชื่อ: เพิ่มกลุ่มชื่อแบบเส้นทับทับต้นไม้ดิสก์ เฉพาะชื่อ: จัดกลุ่มจากเครื่องหมายทับในชื่อเท่านั้นโดยไม่สนการซ้อนดิสก์ สถานะสวิตช์เวอร์ชันเก่าจะย้ายมาโดยอัตโนมัติ",
|
|
2627
2803
|
'settings.sessionLimit': "จำนวนเซสชันเมื่อกาง",
|
|
@@ -2770,7 +2946,11 @@ window.__ModuleLoader__.load({
|
|
|
2770
2946
|
'wsPreset.title': 'Çalışma alanı varsayılan ön ayarını değiştir',
|
|
2771
2947
|
'wsPreset.current': 'Geçerli varsayılan ön ayar',
|
|
2772
2948
|
'wsPreset.currentUnknown': 'Geçerli ön ayar bilinmiyor',
|
|
2773
|
-
'
|
|
2949
|
+
'presetStandardName': 'Standart mod',
|
|
2950
|
+
'presetPtcName': 'PTC modu',
|
|
2951
|
+
'presetMinimalName': 'Minimal mod',
|
|
2952
|
+
'presetCordisName': 'Oluşturucu modu',
|
|
2953
|
+
'wsPreset.createHint': 'Bu çalışma alanında bekleyen yeni oturum yok; onaylayınca seçilen ön ayarla yeni bir oturum çalışma alanı varsayılanı olarak hazırlanır',
|
|
2774
2954
|
'wsPreset.empty': 'Kullanılabilir ön ayar yok',
|
|
2775
2955
|
'wsPreset.loadFailed': 'Ön ayar listesi yüklenemedi',
|
|
2776
2956
|
'wsPreset.done': 'Çalışma alanı varsayılan ön ayarı güncellendi',
|
|
@@ -2789,6 +2969,13 @@ window.__ModuleLoader__.load({
|
|
|
2789
2969
|
'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.',
|
|
2790
2970
|
'settings.statusPulse': 'Nefes alan durum ışığı',
|
|
2791
2971
|
'settings.statusPulse.hint': 'Daraltmayla gizlenen durum ışıkları (tamamlandı yeşil / çalışıyor mavi / bekliyor amber) hiyerarşide yukarı taşar: çalışma alanı ve klasör satırlarının simgesi durum renginde nefes alır (özel ışıması olan başlıklar da birlikte nefes alır), oturum grubu satırları nefes alan bir durum noktası gösterir; varsayılan olarak açık, buradan kapatılabilir.',
|
|
2972
|
+
'settings.density': 'Satır yoğunluğu',
|
|
2973
|
+
'settings.density.compact': 'Sıkı',
|
|
2974
|
+
'settings.density.comfortable': 'Rahat (varsayılan)',
|
|
2975
|
+
'settings.density.roomy': 'Geniş',
|
|
2976
|
+
'settings.density.hint': 'Satır yükseklikleri resmi kenar çubuğunu izler: Rahat resmi varsayılan; Sıkı dikey alan tasarrufu sağlar, Geniş daha büyük tıklama alanı verir. Anında uygulanır, bu tarayıcıda saklanır.',
|
|
2977
|
+
'settings.showCount': 'Oturum sayacı',
|
|
2978
|
+
'settings.showCount.hint': 'Resmi kenar çubuğu oturum sayacını kaldırdı; satır sonunda geri getirmek için açın (üzerine gelince satır düğmelerine yer verir). Varsayılan olarak kapalı.',
|
|
2792
2979
|
'settings.workspaceGroup': "Çalışma alanı gruplaması",
|
|
2793
2980
|
'settings.workspaceGroup.hint': "Disk klasörleri: yalnızca resmî klasörlere (disk dizinleri) göre iç içe. Disk + adlar: disk ağacının üzerine ad grupları ekler. Yalnız adlar: yalnızca başlıklardaki bölü işaretiyle gruplar, disk iç içeliğini yok sayar. Eski anahtar durumu otomatik taşınır.",
|
|
2794
2981
|
'settings.sessionLimit': "Açıldığında gösterilecek oturum",
|
|
@@ -2937,7 +3124,11 @@ window.__ModuleLoader__.load({
|
|
|
2937
3124
|
'wsPreset.title': 'Thay đổi preset mặc định của không gian làm việc',
|
|
2938
3125
|
'wsPreset.current': 'Preset mặc định hiện tại',
|
|
2939
3126
|
'wsPreset.currentUnknown': 'Không rõ preset hiện tại',
|
|
2940
|
-
'
|
|
3127
|
+
'presetStandardName': 'Chế độ chuẩn',
|
|
3128
|
+
'presetPtcName': 'Chế độ PTC',
|
|
3129
|
+
'presetMinimalName': 'Chế độ tối giản',
|
|
3130
|
+
'presetCordisName': 'Chế độ người sáng tạo',
|
|
3131
|
+
'wsPreset.createHint': 'Không gian làm việc này chưa có phiên mới đang chờ; xác nhận sẽ chuẩn bị một phiên mới dùng cấu hình đã chọn làm mặc định của không gian làm việc',
|
|
2941
3132
|
'wsPreset.empty': 'Không có preset khả dụng',
|
|
2942
3133
|
'wsPreset.loadFailed': 'Không tải được danh sách preset',
|
|
2943
3134
|
'wsPreset.done': 'Đã cập nhật preset mặc định của không gian làm việc',
|
|
@@ -2956,6 +3147,13 @@ window.__ModuleLoader__.load({
|
|
|
2956
3147
|
'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.',
|
|
2957
3148
|
'settings.statusPulse': 'Đèn trạng thái nhấp nháy',
|
|
2958
3149
|
'settings.statusPulse.hint': 'Những đèn trạng thái bị thu gọn che đi (xong màu xanh lá / đang chạy màu xanh dương / đang chờ màu hổ phách) sẽ nổi dần lên các tầng trên: hàng không gian làm việc và thư mục cho biểu tượng nhấp nháy theo màu trạng thái (tiêu đề có đặt phát sáng cũng nhấp nháy theo), hàng nhóm phiên hiện một chấm trạng thái nhấp nháy; mặc định bật, có thể tắt tại đây.',
|
|
3150
|
+
'settings.density': 'Mật độ hàng',
|
|
3151
|
+
'settings.density.compact': 'Gọn',
|
|
3152
|
+
'settings.density.comfortable': 'Thoải mái (mặc định)',
|
|
3153
|
+
'settings.density.roomy': 'Rộng',
|
|
3154
|
+
'settings.density.hint': 'Chiều cao hàng theo thanh bên chính thức: Thoải mái là mặc định chính thức; Gọn tiết kiệm không gian dọc, Rộng cho vùng nhấp lớn hơn. Áp dụng ngay, lưu trong trình duyệt này.',
|
|
3155
|
+
'settings.showCount': 'Nhãn số phiên',
|
|
3156
|
+
'settings.showCount.hint': 'Thanh bên chính thức đã bỏ bộ đếm phiên; bật để hiện lại ở cuối hàng (nhường chỗ cho các nút hàng khi rê chuột). Mặc định tắt.',
|
|
2959
3157
|
'settings.workspaceGroup': "Nhóm không gian làm việc",
|
|
2960
3158
|
'settings.workspaceGroup.hint': "Thư mục đĩa: lồng theo thư mục chính thức (thư mục đĩa) mà thôi. Đĩa + tên: thêm nhóm tên lên trên cây đĩa. Chỉ tên: nhóm chỉ theo dấu gạch chéo trong tiêu đề, bỏ qua lồng đĩa. Trạng thái công tắc bản cũ tự động chuyển đổi.",
|
|
2961
3159
|
'settings.sessionLimit': "Số phiên khi mở rộng",
|
|
@@ -3104,7 +3302,11 @@ window.__ModuleLoader__.load({
|
|
|
3104
3302
|
'wsPreset.title': '修改工作區預設',
|
|
3105
3303
|
'wsPreset.current': '目前預設',
|
|
3106
3304
|
'wsPreset.currentUnknown': '目前預設未知',
|
|
3107
|
-
'
|
|
3305
|
+
'presetStandardName': '標準模式',
|
|
3306
|
+
'presetPtcName': 'PTC 模式',
|
|
3307
|
+
'presetMinimalName': '極簡模式',
|
|
3308
|
+
'presetCordisName': '創造模式',
|
|
3309
|
+
'wsPreset.createHint': '該工作區暫無待命的新對話,確認後將預備一個使用所選預設的新對話作為工作區預設',
|
|
3108
3310
|
'wsPreset.empty': '沒有可用的預設',
|
|
3109
3311
|
'wsPreset.loadFailed': '預設清單讀取失敗',
|
|
3110
3312
|
'wsPreset.done': '已更新工作區預設',
|
|
@@ -3123,6 +3325,13 @@ window.__ModuleLoader__.load({
|
|
|
3123
3325
|
'settings.compactChains.hint': '單層鏈會合併成一行,出現多個子級就會自動展開成樹狀;拖拽工作區期間單鏈會臨時展開返做文件夾樹,可以放入任何一級;展開狀態同自訂外觀會喺呢個瀏覽器保存。',
|
|
3124
3326
|
'settings.statusPulse': '狀態呼吸燈',
|
|
3125
3327
|
'settings.statusPulse.hint': '被摺疊遮住嘅狀態燈(完成綠 / 運行藍 / 待互動琥珀)會沿住層級向外冒泡:工作區同分組行會以圖示呼吸發光(顏色跟狀態,自訂過發光嘅標題一齊呼吸),對話分組行就會顯示呼吸狀態燈;預設開啟,可以喺度閂咗佢。',
|
|
3328
|
+
'settings.density': '行密度',
|
|
3329
|
+
'settings.density.compact': '緊湊',
|
|
3330
|
+
'settings.density.comfortable': '舒適(預設)',
|
|
3331
|
+
'settings.density.roomy': '寬鬆',
|
|
3332
|
+
'settings.density.hint': '行高跟隨官方側欄:舒適 = 官方預設行高;緊湊更省縱向空間,寬鬆有更大的點擊範圍。變更立即生效,儲存在當前瀏覽器。',
|
|
3333
|
+
'settings.showCount': '對話數角標',
|
|
3334
|
+
'settings.showCount.hint': '官方側欄已移除工作區行尾的對話計數;開啟後在本插件行尾恢復顯示(懸停時讓位給操作按鈕)。預設關閉。',
|
|
3126
3335
|
'settings.workspaceGroup': "工作區分組",
|
|
3127
3336
|
'settings.workspaceGroup.hint': "磁碟目錄 = 只按官方資料夾(磁碟目錄)巢狀;磁碟 + 名稱 = 在磁碟層之上再按標題裏的 / 分組;僅名稱 = 只按標題裏的 / 分組,完全不理會磁碟目錄的巢狀關係。舊版本的開關狀態會自動遷移。",
|
|
3128
3337
|
'settings.sessionLimit': "展開時顯示會話數",
|
|
@@ -3271,7 +3480,11 @@ window.__ModuleLoader__.load({
|
|
|
3271
3480
|
'wsPreset.title': '修改工作區預設',
|
|
3272
3481
|
'wsPreset.current': '目前預設',
|
|
3273
3482
|
'wsPreset.currentUnknown': '目前預設未知',
|
|
3274
|
-
'
|
|
3483
|
+
'presetStandardName': '標準模式',
|
|
3484
|
+
'presetPtcName': 'PTC 模式',
|
|
3485
|
+
'presetMinimalName': '極簡模式',
|
|
3486
|
+
'presetCordisName': '創造模式',
|
|
3487
|
+
'wsPreset.createHint': '該工作區暫無待命的新對話,確認後將預備一個使用所選預設的新對話作為工作區預設',
|
|
3275
3488
|
'wsPreset.empty': '沒有可用的預設',
|
|
3276
3489
|
'wsPreset.loadFailed': '預設清單讀取失敗',
|
|
3277
3490
|
'wsPreset.done': '已更新工作區預設',
|
|
@@ -3290,6 +3503,13 @@ window.__ModuleLoader__.load({
|
|
|
3290
3503
|
'settings.compactChains.hint': '單層鏈會合併成一行,出現多個子級就會自動展開成樹狀;拖拽工作區期間單鏈會臨時展開返做文件夾樹,可以放入任何一級;展開狀態同自訂外觀會喺呢個瀏覽器保存。',
|
|
3291
3504
|
'settings.statusPulse': '狀態呼吸燈',
|
|
3292
3505
|
'settings.statusPulse.hint': '被摺疊遮住嘅狀態燈(完成綠 / 運行藍 / 待互動琥珀)會沿住層級向外冒泡:工作區同分組行會以圖示呼吸發光(顏色跟狀態,自訂過發光嘅標題一齊呼吸),對話分組行就會顯示呼吸狀態燈;預設開啟,可以喺度閂咗佢。',
|
|
3506
|
+
'settings.density': '行密度',
|
|
3507
|
+
'settings.density.compact': '緊湊',
|
|
3508
|
+
'settings.density.comfortable': '舒適(預設)',
|
|
3509
|
+
'settings.density.roomy': '寬鬆',
|
|
3510
|
+
'settings.density.hint': '行高跟隨官方側欄:舒適 = 官方預設行高;緊湊慳位,寬鬆有更大嘅點擊範圍。改動即時生效,儲存喺當前瀏覽器。',
|
|
3511
|
+
'settings.showCount': '對話數角標',
|
|
3512
|
+
'settings.showCount.hint': '官方側欄已移除工作區行尾嘅對話計數;開啟後喺本插件行尾恢復顯示(懸停時讓位畀操作按鈕)。預設關閉。',
|
|
3293
3513
|
'settings.workspaceGroup': "工作區分組",
|
|
3294
3514
|
'settings.workspaceGroup.hint': "磁碟目錄 = 只按官方資料夾(磁碟目錄)巢狀;磁碟 + 名稱 = 在磁碟層之上再按標題裏的 / 分組;僅名稱 = 只按標題裏的 / 分組,完全不理會磁碟目錄的巢狀關係。舊版本的開關狀態會自動遷移。",
|
|
3295
3515
|
'settings.sessionLimit': "展開時顯示會話數",
|
|
@@ -3438,7 +3658,11 @@ window.__ModuleLoader__.load({
|
|
|
3438
3658
|
'wsPreset.title': '修改工作區預設',
|
|
3439
3659
|
'wsPreset.current': '目前預設',
|
|
3440
3660
|
'wsPreset.currentUnknown': '目前預設未知',
|
|
3441
|
-
'
|
|
3661
|
+
'presetStandardName': '標準模式',
|
|
3662
|
+
'presetPtcName': 'PTC 模式',
|
|
3663
|
+
'presetMinimalName': '極簡模式',
|
|
3664
|
+
'presetCordisName': '創造模式',
|
|
3665
|
+
'wsPreset.createHint': '此工作區暫無待命的新工作階段,確認後將準備一個使用所選預設的新工作階段作為工作區預設',
|
|
3442
3666
|
'wsPreset.empty': '沒有可用的預設',
|
|
3443
3667
|
'wsPreset.loadFailed': '預設清單讀取失敗',
|
|
3444
3668
|
'wsPreset.done': '已更新工作區預設',
|
|
@@ -3457,6 +3681,13 @@ window.__ModuleLoader__.load({
|
|
|
3457
3681
|
'settings.compactChains.hint': '單層鏈會合併成一行,出現多個子層級時自動展開為樹狀;拖曳工作區期間單鏈會暫時展開回資料夾樹,可放入任一層級;展開狀態與自訂外觀會保存在本瀏覽器。',
|
|
3458
3682
|
'settings.statusPulse': '狀態呼吸燈',
|
|
3459
3683
|
'settings.statusPulse.hint': '被收合藏起的狀態燈(完成綠 / 執行藍 / 待互動琥珀)會沿著層級向外冒出:工作區與群組列以圖示呼吸發光(顏色隨狀態,自訂過發光的標題一起呼吸),工作階段群組列則顯示呼吸狀態燈;預設開啟,可在此關閉。',
|
|
3684
|
+
'settings.density': '列密度',
|
|
3685
|
+
'settings.density.compact': '緊湊',
|
|
3686
|
+
'settings.density.comfortable': '舒適 (預設)',
|
|
3687
|
+
'settings.density.roomy': '寬鬆',
|
|
3688
|
+
'settings.density.hint': '列高跟隨官方側邊欄:舒適 = 官方預設列高;緊湊更省垂直空間,寬鬆有較大的點擊範圍。變更立即生效,儲存在目前瀏覽器。',
|
|
3689
|
+
'settings.showCount': '工作階段數標記',
|
|
3690
|
+
'settings.showCount.hint': '官方側邊欄已移除工作區列尾的工作階段計數;開啟後在本外掛列尾恢復顯示 (暫留時讓位給動作按鈕)。預設關閉。',
|
|
3460
3691
|
'settings.workspaceGroup': "工作區分組",
|
|
3461
3692
|
'settings.workspaceGroup.hint': "磁碟資料夾 = 只按官方資料夾(磁碟目錄)巢狀;磁碟 + 名稱 = 在磁碟層之上再按標題裏的 / 分組;僅名稱 = 只按標題裏的 / 分組,完全不理會磁碟目錄的巢狀關係。舊版本的開關狀態會自動遷移。",
|
|
3462
3693
|
'settings.sessionLimit': "展開時顯示會話數",
|
|
@@ -3931,9 +4162,14 @@ window.__ModuleLoader__.load({
|
|
|
3931
4162
|
* React discipline — the occupant mounts once per row and keeps its own
|
|
3932
4163
|
* state across our re-renders).
|
|
3933
4164
|
*/
|
|
3934
|
-
function ShippedSeatOccupant({ seat, ownerProps, t }) {
|
|
4165
|
+
function ShippedSeatOccupant({ seat, ownerProps, t, useMenuOpenState }) {
|
|
3935
4166
|
const bound = React.useMemo(() => bindOccupantFace(seat.face), [seat])
|
|
3936
|
-
return E(seat.component, Object.assign({}, ownerProps, bound, t ? { t } : null
|
|
4167
|
+
return E(seat.component, Object.assign({}, ownerProps, bound, t ? { t } : null,
|
|
4168
|
+
// The menu.item slot's hookContext factory (ui-slots) hands its
|
|
4169
|
+
// occupants the menu open-state pair through a useMenuOpenState
|
|
4170
|
+
// hook; the slot renderer would inject it, our in-row menu
|
|
4171
|
+
// reproduces the same shape here.
|
|
4172
|
+
typeof useMenuOpenState === 'function' ? { useMenuOpenState } : null))
|
|
3937
4173
|
}
|
|
3938
4174
|
|
|
3939
4175
|
/**
|
|
@@ -4304,6 +4540,12 @@ window.__ModuleLoader__.load({
|
|
|
4304
4540
|
'.bw-header-title{flex:1;font-size:14px;font-weight:400;line-height:20px;color:var(--dsw-alias-label-tertiary,#9a9a9a);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
4305
4541
|
'.bw-icon-btn{flex:none;width:24px;height:24px;border:none;background:transparent;border-radius:var(--dsw-radius-sm,6px);display:grid;place-items:center;color:var(--dsw-alias-label-secondary,#b8b8b8);cursor:pointer;padding:0}',
|
|
4306
4542
|
'.bw-icon-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.15));color:var(--dsw-alias-label-primary,#e6e6e6)}',
|
|
4543
|
+
// Official row iconButton (Rows.module.css): a bare 16px tertiary
|
|
4544
|
+
// square — the shipped strip buttons ride this, not the 24px
|
|
4545
|
+
// toolbox button above.
|
|
4546
|
+
'.bw-row-btn{flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;border-radius:var(--dsw-radius-xs,4px);padding:0;background:transparent;color:var(--dsw-alias-label-tertiary,#9a9a9a);cursor:pointer}',
|
|
4547
|
+
'.bw-row-btn:hover{color:var(--dsw-alias-label-primary,#e6e6e6)}',
|
|
4548
|
+
'.bw-row-btn:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#5b8def));outline-offset:-2px}',
|
|
4307
4549
|
// Focus ring parity with the shipped icon buttons (dsh 0.1.7-rc.2
|
|
4308
4550
|
// focus.css + WorkspaceBrowser.module.css): the host publishes the ring
|
|
4309
4551
|
// width/colour as tokens and keeps offsets inside controls that clip an
|
|
@@ -4327,10 +4569,12 @@ window.__ModuleLoader__.load({
|
|
|
4327
4569
|
// as the chevron-only button beside an input.
|
|
4328
4570
|
'.bw-menu-block{display:block;width:100%}',
|
|
4329
4571
|
'.bw-menu-inline{display:inline-block;width:auto;flex:none}',
|
|
4330
|
-
'.bw-tree{flex:1;overflow-y:auto;overflow-x:hidden;padding:2px 6px 12px;min-height:0}',
|
|
4331
|
-
'.bw-
|
|
4332
|
-
'.bw-
|
|
4333
|
-
'.bw-
|
|
4572
|
+
'.bw-tree{--bw-row-h:34px;--bw-session-h:32px;flex:1;overflow-y:auto;overflow-x:hidden;padding:2px 6px 12px;min-height:0}',
|
|
4573
|
+
'.bw-tree[data-bw-density="compact"]{--bw-row-h:28px;--bw-session-h:26px}',
|
|
4574
|
+
'.bw-tree[data-bw-density="roomy"]{--bw-row-h:40px;--bw-session-h:38px}',
|
|
4575
|
+
'.bw-row{display:flex;align-items:center;gap:6px;height:var(--bw-row-h,34px);padding:0 8px;padding-inline-start:calc(8px + var(--bw-indent,0px));border-radius:var(--dsw-radius-md,8px);cursor:pointer;user-select:none;font-size:14px;line-height:20px;color:var(--dsw-alias-label-primary,#e6e6e6);position:relative}',
|
|
4576
|
+
'.bw-drop-before::before{content:"";position:absolute;z-index:1;left:0;right:4px;top:-7px;height:12px;pointer-events:none;background:linear-gradient(55deg,transparent 0,var(--dsw-alias-state-business-primary,#5b8def) 50%,transparent 100%) 0 0/5px 7px no-repeat,linear-gradient(125deg,transparent 0,var(--dsw-alias-state-business-primary,#5b8def) 50%,transparent 100%) 0 5px/5px 7px no-repeat,linear-gradient(var(--dsw-alias-state-business-primary,#5b8def),var(--dsw-alias-state-business-primary,#5b8def)) 4px 5px/calc(100% - 4px) 2px no-repeat}',
|
|
4577
|
+
'.bw-drop-after::after{content:"";position:absolute;z-index:1;left:0;right:4px;bottom:-7px;height:12px;pointer-events:none;background:linear-gradient(55deg,transparent 0,var(--dsw-alias-state-business-primary,#5b8def) 50%,transparent 100%) 0 0/5px 7px no-repeat,linear-gradient(125deg,transparent 0,var(--dsw-alias-state-business-primary,#5b8def) 50%,transparent 100%) 0 5px/5px 7px no-repeat,linear-gradient(var(--dsw-alias-state-business-primary,#5b8def),var(--dsw-alias-state-business-primary,#5b8def)) 4px 5px/calc(100% - 4px) 2px no-repeat}',
|
|
4334
4578
|
'.bw-drop-into{outline:1.5px dashed var(--dsw-alias-brand-primary,#5b8def);outline-offset:-1.5px}',
|
|
4335
4579
|
// Grouping drop lands on the header button / a group row: solid brand
|
|
4336
4580
|
// rim + a tinted wash so "this drop joins a group" reads stronger than
|
|
@@ -4368,13 +4612,26 @@ window.__ModuleLoader__.load({
|
|
|
4368
4612
|
'.bw-gn-icon{flex:none;display:grid;place-items:center;width:16px;height:16px;color:var(--dsw-alias-label-secondary,#b8b8b8)}',
|
|
4369
4613
|
'.bw-gn-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--dsw-alias-label-primary,#e6e6e6)}',
|
|
4370
4614
|
'.bw-gn-input{width:100%;box-sizing:border-box}',
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
'.bw-
|
|
4377
|
-
'.bw-
|
|
4615
|
+
// Row hover/selection follow the background plugin's interface scheme
|
|
4616
|
+
// (body[data-ds-dark-theme], flipped by dsh-any-background): dark
|
|
4617
|
+
// surfaces tint BLACK — every label token is rewritten toward bright ink
|
|
4618
|
+
// on dark wallpapers, so any label-derived mix still reads as white.
|
|
4619
|
+
// Light surfaces keep the white mix.
|
|
4620
|
+
'.bw-row:hover,.bw-row.menu-open{background:color-mix(in srgb,#000 12%,transparent)}',
|
|
4621
|
+
'body:not([data-ds-dark-theme]) .bw-row:hover,body:not([data-ds-dark-theme]) .bw-row.menu-open{background:color-mix(in srgb,#fff 12%,transparent)}',
|
|
4622
|
+
'',
|
|
4623
|
+
'.bw-row-current{background:color-mix(in srgb,#000 18%,transparent)}',
|
|
4624
|
+
'body:not([data-ds-dark-theme]) .bw-row-current{background:color-mix(in srgb,#fff 20%,transparent)}',
|
|
4625
|
+
'',
|
|
4626
|
+
'.bw-row-icon{flex:none;width:16px;height:20px;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#9a9a9a)}',
|
|
4627
|
+
'.bw-chevron{flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-caption,#adb2b8)}',
|
|
4628
|
+
'.bw-chevron-open .bw-arrow{transform:rotate(90deg)}',
|
|
4629
|
+
'.bw-container-row .bw-chevron{display:none}',
|
|
4630
|
+
'.bw-container-row:hover .bw-chevron,.bw-container-row.menu-open .bw-chevron{display:inline-flex}',
|
|
4631
|
+
'.bw-container-row:hover .bw-folder-cell,.bw-container-row.menu-open .bw-folder-cell{display:none}',
|
|
4632
|
+
'.bw-arrow{flex:none;display:inline-flex;transition:transform 150ms var(--ds-ease-in-out,ease-in-out)}',
|
|
4633
|
+
'@media (prefers-reduced-motion:reduce){.bw-arrow{transition:none}}',
|
|
4634
|
+
'.bw-folder-active{color:var(--dsw-alias-state-business-primary,#5b8def)}',
|
|
4378
4635
|
// Hero picker rows (v0.22.0): hierarchy rides the row itself — an
|
|
4379
4636
|
// indentation gutter, a folder chevron or workspace glyph, and the
|
|
4380
4637
|
// trailing check. The row mirrors the primitives menu cell (same hover
|
|
@@ -4393,23 +4650,28 @@ window.__ModuleLoader__.load({
|
|
|
4393
4650
|
// padding covers the widest rim (2px at the top of the slider). margin
|
|
4394
4651
|
// cancels it, so text still starts and truncates where it did.
|
|
4395
4652
|
'.bw-row-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:3px;margin:-3px}',
|
|
4396
|
-
'.bw-row-
|
|
4397
|
-
'.bw-row-
|
|
4653
|
+
'.bw-session-row .bw-row-label{padding:0;margin:0 6px 0 4px}',
|
|
4654
|
+
'.bw-session-row .bw-row-label[data-scrolled]{mask-image:linear-gradient(to right,transparent,#000 12px);-webkit-mask-image:linear-gradient(to right,transparent,#000 12px)}',
|
|
4655
|
+
'.bw-session-row .bw-row-label[data-clipped]{mask-image:linear-gradient(to left,transparent,#000 12px);-webkit-mask-image:linear-gradient(to left,transparent,#000 12px)}',
|
|
4656
|
+
'.bw-session-row .bw-row-label[data-scrolled][data-clipped]{mask-image:linear-gradient(to right,transparent,#000 12px,#000 calc(100% - 12px),transparent);-webkit-mask-image:linear-gradient(to right,transparent,#000 12px,#000 calc(100% - 12px),transparent)}',
|
|
4657
|
+
'.bw-row-count{flex:none;display:none;font-size:12px;color:var(--dsw-alias-label-quaternary,#8a8a8a)}',
|
|
4658
|
+
'.bw-tree[data-bw-count] .bw-row-count{display:inline}',
|
|
4659
|
+
'.bw-row-time{flex:none;font-size:10px;line-height:16px;color:var(--dsw-alias-label-tertiary,#9a9a9a)}',
|
|
4398
4660
|
// The outline is inherited from the row: the 10-12px meta column (session
|
|
4399
4661
|
// count / relative time) opts out — stroked meta text outshines the title
|
|
4400
4662
|
// it is supposed to support.
|
|
4401
4663
|
'.bw-row-count,.bw-row-time{-webkit-text-stroke-width:0}',
|
|
4402
4664
|
'.bw-schedule-badge{flex:none;display:inline-flex;align-items:center;color:var(--dsw-alias-label-tertiary,#9a9a9a);margin:0 6px}',
|
|
4403
|
-
'.bw-row-actions{flex:none;display:none;align-items:center;gap:
|
|
4404
|
-
'.bw-row:hover .bw-row-actions{display:flex}',
|
|
4405
|
-
'.bw-row:hover .bw-row-time,.bw-row:hover .bw-row-count,.bw-row:hover .bw-schedule-badge{display:none}',
|
|
4406
|
-
'.bw-row-archived{
|
|
4407
|
-
'.bw-pin-indicator{flex:none;display:inline-flex;align-items:center;color:var(--dsw-alias-
|
|
4665
|
+
'.bw-row-actions{flex:none;display:none;align-items:center;gap:15px}',
|
|
4666
|
+
'.bw-row:hover .bw-row-actions,.bw-row.menu-open .bw-row-actions{display:flex}',
|
|
4667
|
+
'.bw-row:hover .bw-row-time,.bw-row:hover .bw-row-count,.bw-row:hover .bw-schedule-badge,.bw-row:hover .bw-pin-indicator,.bw-row.menu-open .bw-row-time,.bw-row.menu-open .bw-row-count,.bw-row.menu-open .bw-schedule-badge,.bw-row.menu-open .bw-pin-indicator{display:none}',
|
|
4668
|
+
'.bw-row-archived .bw-row-label{color:var(--dsw-alias-label-caption,#8a8a8a)}',
|
|
4669
|
+
'.bw-pin-indicator{flex:none;display:inline-flex;align-items:center;justify-content:center;width:16px;height:20px;color:var(--dsw-alias-label-caption,#adb2b8);margin:0 0 0 6px}',
|
|
4408
4670
|
'.bw-overflow-row{display:block;width:100%;text-align:left;border:0;background:transparent;color:var(--dsw-alias-label-tertiary,#9a9a9a);font-size:12px;cursor:pointer;padding:4px 6px;font-family:inherit}',
|
|
4409
4671
|
'.bw-overflow-row:hover{color:var(--dsw-alias-label-secondary,#b8b8b8)}',
|
|
4410
4672
|
'.bw-dot{flex:none;width:6px;height:6px;border-radius:50%;background:transparent}',
|
|
4411
|
-
'.bw-session-row{font-size:14px;line-height:20px;color:var(--dsw-alias-label-primary,#e6e6e6);
|
|
4412
|
-
'.bw-sgroup-row{font-size:14px;line-height:20px;color:var(--dsw-alias-label-tertiary,#9a9a9a);
|
|
4673
|
+
'.bw-session-row{font-size:14px;line-height:20px;color:var(--dsw-alias-label-primary,#e6e6e6);height:var(--bw-session-h,32px)}',
|
|
4674
|
+
'.bw-sgroup-row{font-size:14px;line-height:20px;color:var(--dsw-alias-label-tertiary,#9a9a9a);height:var(--bw-session-h,32px)}',
|
|
4413
4675
|
'.bw-sgroup-row:hover{color:var(--dsw-alias-label-secondary,#b8b8b8)}',
|
|
4414
4676
|
'.bw-session-row:hover{color:var(--dsw-alias-label-primary,#e6e6e6)}',
|
|
4415
4677
|
'.bw-empty{padding:28px 12px;text-align:center;font-size:13px;color:var(--dsw-alias-label-tertiary,#9a9a9a)}',
|
|
@@ -4421,6 +4683,14 @@ window.__ModuleLoader__.load({
|
|
|
4421
4683
|
// Row toast for the shipped fork shortcut's async failure (the shipped
|
|
4422
4684
|
// notice rides the shadowed overlay). Token chain kept transparent-safe
|
|
4423
4685
|
// like every other self-drawn surface.
|
|
4686
|
+
// Session/workspace hover-card body (official Rows.module.css palette:
|
|
4687
|
+
// fixed colors, independent of theme aliases).
|
|
4688
|
+
'.bw-hover-card{display:flex;flex-direction:column;gap:8px}',
|
|
4689
|
+
'.bw-hover-title{font-size:14px;line-height:20px;color:#FFFFFF;overflow-wrap:break-word}',
|
|
4690
|
+
'.bw-hover-path{font-size:12px;line-height:16px;color:#CFD3D6;word-break:break-all}',
|
|
4691
|
+
'.bw-hover-time{font-size:12px;line-height:16px;color:#CFD3D6}',
|
|
4692
|
+
'.bw-hover-status{display:flex;align-items:center;gap:8px;font-size:12px;line-height:20px;color:#ADB2B8}',
|
|
4693
|
+
'.bw-hover-archived svg{flex-shrink:0;margin:0 -4px}',
|
|
4424
4694
|
'.bw-toast{position:absolute;left:10px;right:10px;bottom:12px;z-index:30;display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.3));border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-toast-bg,var(--dsw-specific-menu,rgba(28,28,32,.92)));color:var(--dsw-alias-toast-label,var(--dsw-alias-label-primary,#e6e6e6));font-size:12px;box-shadow:0 8px 24px rgba(0,0,0,.35)}',
|
|
4425
4695
|
'.bw-toast-text{flex:1;min-width:0}',
|
|
4426
4696
|
'.bw-toast-close{flex:none;width:20px;height:20px;border:none;background:transparent;color:inherit;border-radius:var(--dsw-radius-xs,4px);display:grid;place-items:center;cursor:pointer;padding:0;opacity:.75}',
|
|
@@ -4479,7 +4749,8 @@ window.__ModuleLoader__.load({
|
|
|
4479
4749
|
'.bw-plugin-body .bw-hint{font-size:12px;color:var(--dsw-alias-label-tertiary,#9a9a9a)}',
|
|
4480
4750
|
'.bw-rail{display:flex;flex-direction:column;align-items:center;gap:6px;padding:6px 0}',
|
|
4481
4751
|
'.bw-rail-btn{width:36px;height:36px;border:none;background:transparent;border-radius:var(--dsw-radius-sm,8px);display:grid;place-items:center;color:var(--dsw-alias-label-secondary,#b8b8b8);cursor:pointer;padding:0}',
|
|
4482
|
-
'.bw-rail-btn:hover{background:
|
|
4752
|
+
'.bw-rail-btn:hover{background:color-mix(in srgb,#000 12%,transparent);color:var(--dsw-alias-label-primary,#e6e6e6)}',
|
|
4753
|
+
'body:not([data-ds-dark-theme]) .bw-rail-btn:hover{background:color-mix(in srgb,#fff 12%,transparent)}',
|
|
4483
4754
|
'.bw-modal-body{display:flex;flex-direction:column;gap:10px;min-width:300px;max-width:380px;box-sizing:border-box}',
|
|
4484
4755
|
// issue #10: Modal 高度契约 — 卡片按 Modal 的 padding box 限高,超高内容滚 contentClassName 区
|
|
4485
4756
|
// (官方 RiskConfirmation 同款范式;.dialog 为 overflow:hidden,不限高则上下被裁)
|
|
@@ -5250,8 +5521,25 @@ window.__ModuleLoader__.load({
|
|
|
5250
5521
|
* applyPreset is null exactly when no blank exists (or the face is
|
|
5251
5522
|
* absent); the roster is a feature-probed plugin-side read.
|
|
5252
5523
|
*/
|
|
5253
|
-
|
|
5254
|
-
|
|
5524
|
+
/* BUILT-IN preset id → this plugin's dictionary key. Mirrors the official
|
|
5525
|
+
* display fold (agent-preset-registry display.ts isBuiltInPreset: built-ins
|
|
5526
|
+
* publish NO name) because that registry package is not on the client boot
|
|
5527
|
+
* graph and cannot be required here. Custom presets keep their authored
|
|
5528
|
+
* name; the id itself is the last-resort fallback. Host-follow: delete this
|
|
5529
|
+
* fold and require the official one once it ships on the boot graph. */
|
|
5530
|
+
const BUILT_IN_PRESET_LABEL_KEYS = {
|
|
5531
|
+
standard: 'presetStandardName',
|
|
5532
|
+
ptc: 'presetPtcName',
|
|
5533
|
+
minimal: 'presetMinimalName',
|
|
5534
|
+
cordis: 'presetCordisName',
|
|
5535
|
+
}
|
|
5536
|
+
const presetDisplayLabel = (row, t) => {
|
|
5537
|
+
if (row && row.name === undefined && BUILT_IN_PRESET_LABEL_KEYS[row.id] !== undefined) return t(BUILT_IN_PRESET_LABEL_KEYS[row.id])
|
|
5538
|
+
return (row && row.name) || (row && row.id) || ''
|
|
5539
|
+
}
|
|
5540
|
+
|
|
5541
|
+
function WorkspacePresetDialog({ applyPreset, hasBlank, loadRoster, current, onApplied, onClose, t }) {
|
|
5542
|
+
const editable = typeof applyPreset === 'function' && hasBlank !== false
|
|
5255
5543
|
const menuReady = typeof ui.Menu === 'function'
|
|
5256
5544
|
const [status, setStatus] = React.useState('loading') // loading | ready | error
|
|
5257
5545
|
const [rows, setRows] = React.useState([])
|
|
@@ -5285,12 +5573,12 @@ window.__ModuleLoader__.load({
|
|
|
5285
5573
|
return () => { alive = false }
|
|
5286
5574
|
}, [])
|
|
5287
5575
|
const nameOf = (id) => {
|
|
5288
|
-
for (const row of rows) { if (row.id === id) return row
|
|
5576
|
+
for (const row of rows) { if (row.id === id) return presetDisplayLabel(row, t) }
|
|
5289
5577
|
return id
|
|
5290
5578
|
}
|
|
5291
5579
|
const options = rows.map((row) => ({
|
|
5292
5580
|
value: row.id,
|
|
5293
|
-
label: (row
|
|
5581
|
+
label: presetDisplayLabel(row, t) + (row.isDefault ? ' (' + t('wsPreset.globalDefault') + ')' : ''),
|
|
5294
5582
|
}))
|
|
5295
5583
|
const commit = () => {
|
|
5296
5584
|
if (busy || !picked) { onClose(); return }
|
|
@@ -5307,7 +5595,9 @@ window.__ModuleLoader__.load({
|
|
|
5307
5595
|
setErrorText(reason instanceof Error ? reason.message : String(reason || t('wsPreset.loadFailed')))
|
|
5308
5596
|
})
|
|
5309
5597
|
}
|
|
5310
|
-
|
|
5598
|
+
// No standing draft → the confirm PREPARES one with the picked preset
|
|
5599
|
+
// (see the ws-preset menu branch); the hint line spells that out.
|
|
5600
|
+
const currentLine = !editable ? t('wsPreset.createHint')
|
|
5311
5601
|
: current ? t('wsPreset.current') + ': ' + nameOf(current)
|
|
5312
5602
|
: t('wsPreset.currentUnknown')
|
|
5313
5603
|
return E(ui.Modal, {
|
|
@@ -5316,15 +5606,13 @@ window.__ModuleLoader__.load({
|
|
|
5316
5606
|
closeLabel: t('close'),
|
|
5317
5607
|
title: t('wsPreset.title'),
|
|
5318
5608
|
footer: E('div', { className: 'bw-modal-actions' },
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
? E(BTN, { variant: 'primary', onClick: commit, disabled: busy || !picked || picked === current }, t('confirm'))
|
|
5322
|
-
: E(BTN, { variant: 'primary', onClick: onClose }, t('close')),
|
|
5609
|
+
E(BTN, { variant: 'outline', onClick: onClose, disabled: busy }, t('cancel')),
|
|
5610
|
+
E(BTN, { variant: 'primary', onClick: commit, disabled: busy || !picked || picked === current }, t('confirm')),
|
|
5323
5611
|
),
|
|
5324
5612
|
},
|
|
5325
5613
|
E('div', { className: 'bw-modal-body' },
|
|
5326
5614
|
status === 'error' ? E('div', { className: 'bw-error-text', role: 'alert' }, errorText) : null,
|
|
5327
|
-
status === 'ready'
|
|
5615
|
+
status === 'ready' ? (
|
|
5328
5616
|
rows.length === 0
|
|
5329
5617
|
? E('div', { className: 'bw-hint' }, t('wsPreset.empty'))
|
|
5330
5618
|
: E('div', { className: 'bw-field' },
|
|
@@ -5341,7 +5629,7 @@ window.__ModuleLoader__.load({
|
|
|
5341
5629
|
)
|
|
5342
5630
|
) : null,
|
|
5343
5631
|
E('div', { className: 'bw-hint' }, currentLine),
|
|
5344
|
-
status === 'ready' &&
|
|
5632
|
+
status === 'ready' && errorText !== ''
|
|
5345
5633
|
? E('div', { className: 'bw-error-text', role: 'alert' }, errorText)
|
|
5346
5634
|
: null,
|
|
5347
5635
|
),
|
|
@@ -5584,7 +5872,7 @@ window.__ModuleLoader__.load({
|
|
|
5584
5872
|
}
|
|
5585
5873
|
}
|
|
5586
5874
|
|
|
5587
|
-
function FolderRow({ node, depth, expanded, onToggle, onContextMenu, dropInto, dragEvents, custStyle, iconMode, pulse, t }) {
|
|
5875
|
+
function FolderRow({ node, depth, expanded, onToggle, onContextMenu, dropInto, dragEvents, custStyle, iconMode, pulse, active, t }) {
|
|
5588
5876
|
const total = countWorkspaces(node)
|
|
5589
5877
|
const iconEl = iconOf(iconMode, expanded)
|
|
5590
5878
|
// Hidden status dots breathe on the icon; with the icon hidden (mode
|
|
@@ -5593,44 +5881,58 @@ window.__ModuleLoader__.load({
|
|
|
5593
5881
|
? E(PulseGlow, { state: pulse }, iconEl || (typeof ui.StateDot === 'function' ? E(ui.StateDot, { state: pulse, size: 10 }) : null))
|
|
5594
5882
|
: iconEl
|
|
5595
5883
|
return E('div', {
|
|
5596
|
-
className: cls('bw-row', dropInto && 'bw-drop-into', dropInto && 'bw-drop-into-strong'),
|
|
5597
|
-
style: {
|
|
5884
|
+
className: cls('bw-row', 'bw-container-row', dropInto && 'bw-drop-into', dropInto && 'bw-drop-into-strong'),
|
|
5885
|
+
style: { '--bw-indent': (depth * 12) + 'px', ...(custStyle || {}), ...(pulse ? { '--bw-pulse-color': PULSE_COLORS[pulse] || PULSE_COLORS.ongoing } : null) },
|
|
5598
5886
|
onClick: onToggle,
|
|
5599
5887
|
onContextMenu: onContextMenu,
|
|
5600
5888
|
role: 'treeitem',
|
|
5601
5889
|
'aria-expanded': expanded,
|
|
5602
5890
|
...(dragEvents || {}),
|
|
5603
5891
|
},
|
|
5604
|
-
|
|
5605
|
-
|
|
5892
|
+
// Official projectRow duality (Rows.module.css): the chevron hides
|
|
5893
|
+
// until hover, swapping in for the folder glyph; the arrow spins
|
|
5894
|
+
// open with the row.
|
|
5895
|
+
E('span', { className: cls('bw-chevron', expanded && 'bw-chevron-open') }, E('span', { className: 'bw-arrow' }, icon('IconTriangleRightFill14', 14))),
|
|
5896
|
+
E('span', { className: cls('bw-row-icon', 'bw-folder-cell', active && !pulse && 'bw-folder-active') }, iconChild),
|
|
5606
5897
|
// A row that already carries a custom label glow breathes in sync.
|
|
5607
5898
|
E('span', { className: cls('bw-row-label', pulse && custStyle && custStyle.textShadow && 'bw-pulse-text') }, node.name),
|
|
5608
5899
|
total > 0 ? E('span', { className: 'bw-row-count' }, String(total)) : null,
|
|
5609
5900
|
)
|
|
5610
5901
|
}
|
|
5611
5902
|
|
|
5612
|
-
function WorkspaceRow({ workspace, depth, count, sessionsOpen, onToggle, onStart, onContextMenu, currentInside, dropHalf, dropInto, dragEvents, custStyle, iconMode, pulse, newSessionLabel, newSessionAria, t }) {
|
|
5903
|
+
function WorkspaceRow({ workspace, depth, count, sessionsOpen, onToggle, onStart, onMenu, onContextMenu, currentInside, menuOpen, dropHalf, dropInto, dragEvents, custStyle, iconMode, pulse, newSessionLabel, newSessionAria, menuAria, hoverBody, t }) {
|
|
5613
5904
|
const iconEl = iconOf(iconMode, sessionsOpen)
|
|
5614
5905
|
const iconChild = pulse
|
|
5615
5906
|
? E(PulseGlow, { state: pulse }, iconEl || (typeof ui.StateDot === 'function' ? E(ui.StateDot, { state: pulse, size: 10 }) : null))
|
|
5616
5907
|
: iconEl
|
|
5617
|
-
|
|
5618
|
-
className: cls('bw-row', currentInside && 'bw-row-current', dropHalf === 'before' && 'bw-drop-before', dropHalf === 'after' && 'bw-drop-after', dropInto && 'bw-drop-into', dropInto && 'bw-drop-into-strong'),
|
|
5619
|
-
style: {
|
|
5908
|
+
let row = E('div', {
|
|
5909
|
+
className: cls('bw-row', 'bw-container-row', currentInside && 'bw-row-current', menuOpen && 'menu-open', dropHalf === 'before' && 'bw-drop-before', dropHalf === 'after' && 'bw-drop-after', dropInto && 'bw-drop-into', dropInto && 'bw-drop-into-strong'),
|
|
5910
|
+
style: { '--bw-indent': (depth * 12) + 'px', ...(custStyle || {}), ...(pulse ? { '--bw-pulse-color': PULSE_COLORS[pulse] || PULSE_COLORS.ongoing } : null) },
|
|
5620
5911
|
onClick: onToggle,
|
|
5621
5912
|
onContextMenu: onContextMenu,
|
|
5622
5913
|
role: 'treeitem',
|
|
5623
5914
|
'aria-expanded': sessionsOpen,
|
|
5624
5915
|
...(dragEvents || {}),
|
|
5625
5916
|
},
|
|
5626
|
-
|
|
5627
|
-
|
|
5917
|
+
// Official projectRow duality (Rows.module.css): the chevron hides
|
|
5918
|
+
// until hover, swapping in for the folder glyph; the arrow spins
|
|
5919
|
+
// open with the row; a subtree holding the current session tints
|
|
5920
|
+
// the folder glyph blue (folderActive), not the whole row.
|
|
5921
|
+
E('span', { className: cls('bw-chevron', sessionsOpen && 'bw-chevron-open') }, E('span', { className: 'bw-arrow' }, icon('IconTriangleRightFill14', 14))),
|
|
5922
|
+
E('span', { className: cls('bw-row-icon', 'bw-folder-cell', currentInside && !pulse && 'bw-folder-active') }, iconChild),
|
|
5628
5923
|
E('span', { className: cls('bw-row-label', pulse && custStyle && custStyle.textShadow && 'bw-pulse-text'), title: workspace.title || workspace.leaf }, workspace.leaf),
|
|
5629
5924
|
count > 0 ? E('span', { className: 'bw-row-count' }, String(count)) : null,
|
|
5630
5925
|
E('span', { className: 'bw-row-actions', onClick: (e) => e.stopPropagation() },
|
|
5631
5926
|
E('button', {
|
|
5632
5927
|
type: 'button',
|
|
5633
|
-
className: 'bw-
|
|
5928
|
+
className: 'bw-row-btn',
|
|
5929
|
+
'aria-label': menuAria || '...',
|
|
5930
|
+
title: menuAria || undefined,
|
|
5931
|
+
onClick: (e) => { e.stopPropagation(); if (onMenu) onMenu(e) },
|
|
5932
|
+
}, icon('IconEllipsisOutline16', 15)),
|
|
5933
|
+
E('button', {
|
|
5934
|
+
type: 'button',
|
|
5935
|
+
className: 'bw-row-btn',
|
|
5634
5936
|
'aria-label': newSessionLabel || t('session.new'),
|
|
5635
5937
|
'aria-keyshortcuts': newSessionAria,
|
|
5636
5938
|
title: newSessionLabel || t('session.new'),
|
|
@@ -5638,6 +5940,13 @@ window.__ModuleLoader__.load({
|
|
|
5638
5940
|
}, icon('IconPlusOutline16')),
|
|
5639
5941
|
),
|
|
5640
5942
|
)
|
|
5943
|
+
// Official workspace hover card (Rows.tsx WorkspaceHoverContent): the
|
|
5944
|
+
// full label plus the abbreviated path. Workspace rows carry no
|
|
5945
|
+
// createdAt here, so the created line is absent.
|
|
5946
|
+
if (typeof ui.HoverCard === 'function' && typeof hoverBody === 'function') {
|
|
5947
|
+
row = E(ui.HoverCard, { key: 'hover', anchor: row, content: hoverBody(workspace), openDelayMs: 800, grouped: true })
|
|
5948
|
+
}
|
|
5949
|
+
return row
|
|
5641
5950
|
}
|
|
5642
5951
|
|
|
5643
5952
|
// Official status priority: pending interaction > running > running
|
|
@@ -5678,6 +5987,60 @@ window.__ModuleLoader__.load({
|
|
|
5678
5987
|
return s === 'ongoing' ? null : s
|
|
5679
5988
|
}
|
|
5680
5989
|
|
|
5990
|
+
// Official title marquee (Rows.tsx useTitleMarquee): on hover an
|
|
5991
|
+
// overflowing title scrolls at the shipped constant speed so the hidden
|
|
5992
|
+
// tail becomes readable; scroll progress is exposed through data
|
|
5993
|
+
// attributes that drive the CSS edge-fade masks. Reduced motion jumps
|
|
5994
|
+
// straight to the end; leaving the row resets instantly.
|
|
5995
|
+
const MIN_TITLE_REVEAL_PX = 8
|
|
5996
|
+
const TITLE_MARQUEE_PX_PER_MS = 0.03
|
|
5997
|
+
const marqueePlace = (title, left, range) => {
|
|
5998
|
+
if (!title) return
|
|
5999
|
+
if (typeof title.scrollTo === 'function') title.scrollTo({ left, behavior: 'instant' })
|
|
6000
|
+
else title.scrollLeft = left
|
|
6001
|
+
if (left > 0) title.dataset.scrolled = ''
|
|
6002
|
+
else delete title.dataset.scrolled
|
|
6003
|
+
if (left < range) title.dataset.clipped = ''
|
|
6004
|
+
else delete title.dataset.clipped
|
|
6005
|
+
}
|
|
6006
|
+
function useTitleMarquee() {
|
|
6007
|
+
const titleRef = React.useRef(null)
|
|
6008
|
+
const frameRef = React.useRef(0)
|
|
6009
|
+
React.useEffect(() => () => {
|
|
6010
|
+
if (frameRef.current) cancelAnimationFrame(frameRef.current)
|
|
6011
|
+
frameRef.current = 0
|
|
6012
|
+
}, [])
|
|
6013
|
+
const onPointerEnter = () => {
|
|
6014
|
+
const title = titleRef.current
|
|
6015
|
+
if (!title) return
|
|
6016
|
+
const range = title.scrollWidth - title.clientWidth
|
|
6017
|
+
if (!(range > MIN_TITLE_REVEAL_PX)) return
|
|
6018
|
+
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
6019
|
+
marqueePlace(title, range, range)
|
|
6020
|
+
return
|
|
6021
|
+
}
|
|
6022
|
+
let position = 0
|
|
6023
|
+
let previous = performance.now()
|
|
6024
|
+
const step = (timestamp) => {
|
|
6025
|
+
position = Math.min(position + (timestamp - previous) * TITLE_MARQUEE_PX_PER_MS, range)
|
|
6026
|
+
previous = timestamp
|
|
6027
|
+
marqueePlace(titleRef.current, position, range)
|
|
6028
|
+
if (position < range) frameRef.current = requestAnimationFrame(step)
|
|
6029
|
+
}
|
|
6030
|
+
frameRef.current = requestAnimationFrame(step)
|
|
6031
|
+
}
|
|
6032
|
+
const onPointerLeave = () => {
|
|
6033
|
+
if (frameRef.current) cancelAnimationFrame(frameRef.current)
|
|
6034
|
+
frameRef.current = 0
|
|
6035
|
+
const title = titleRef.current
|
|
6036
|
+
if (!title) return
|
|
6037
|
+
marqueePlace(title, 0, 0)
|
|
6038
|
+
delete title.dataset.scrolled
|
|
6039
|
+
delete title.dataset.clipped
|
|
6040
|
+
}
|
|
6041
|
+
return { titleRef, onPointerEnter, onPointerLeave }
|
|
6042
|
+
}
|
|
6043
|
+
|
|
5681
6044
|
function SessionRow({ node, depth, current, onOpen, onContextMenu, now, dropHalf, dragEvents, custStyle, breathing, sessionActions, scheduleSeat, t }) {
|
|
5682
6045
|
const state = sessionStateOf(node)
|
|
5683
6046
|
const status = state === null ? null : {
|
|
@@ -5718,23 +6081,65 @@ window.__ModuleLoader__.load({
|
|
|
5718
6081
|
}
|
|
5719
6082
|
const archivedTitle = official('row.archived')
|
|
5720
6083
|
const pinnedTitle = official('row.pinned')
|
|
5721
|
-
|
|
6084
|
+
// In-row "..." menu (official Rows.tsx): the ellipsis button anchors a
|
|
6085
|
+
// primitives Menu whose entries are the SAME shared list the
|
|
6086
|
+
// right-click menu builds (sessionActions.menuItems) and whose trailing
|
|
6087
|
+
// children are the shipped menu.item seat occupants; picks dispatch
|
|
6088
|
+
// through the SAME handler (sessionActions.pick), so the two surfaces
|
|
6089
|
+
// cannot drift. Hosts without the primitives Menu keep the old
|
|
6090
|
+
// open-context-menu trigger button.
|
|
6091
|
+
const inlineMenu = !node.blank && sessionActions && typeof ui.Menu === 'function'
|
|
6092
|
+
&& typeof sessionActions.menuItems === 'function' && typeof sessionActions.pick === 'function'
|
|
6093
|
+
const [menuOpen, setMenuOpen] = React.useState(false)
|
|
6094
|
+
const menuOpenPair = React.useMemo(() => [menuOpen, setMenuOpen], [menuOpen])
|
|
6095
|
+
const menuSeatChildren = inlineMenu && Array.isArray(sessionActions.menuSeat)
|
|
6096
|
+
? sessionActions.menuSeat.map((seat, index) => E(ShippedSeatOccupant, {
|
|
6097
|
+
key: 'menu-seat-' + index,
|
|
6098
|
+
seat,
|
|
6099
|
+
ownerProps: { sessionId: node.id, displayTitle: node.leaf || node.title },
|
|
6100
|
+
t: seat.t,
|
|
6101
|
+
// The menu.item hook-context factory (ui-slots) hands occupants
|
|
6102
|
+
// the open-state pair; reproduced here for the in-row menu.
|
|
6103
|
+
useMenuOpenState: () => menuOpenPair,
|
|
6104
|
+
}))
|
|
6105
|
+
: null
|
|
6106
|
+
// The shipped row.action seat renders the OFFICIAL pin/archive quick
|
|
6107
|
+
// buttons; our own fallback buttons step aside so the row never shows
|
|
6108
|
+
// two sets (same rule as the schedule mark's leading cell).
|
|
6109
|
+
const quickSeatUsed = Array.isArray(sessionActions.rowActionSeat) && sessionActions.rowActionSeat.length > 0
|
|
6110
|
+
const strip = !node.blank && sessionActions && (inlineMenu || sessionActions.trigger || (sessionActions.quick && !quickSeatUsed))
|
|
5722
6111
|
? E('span', { className: 'bw-row-actions', onClick: (e) => e.stopPropagation() },
|
|
5723
|
-
|
|
6112
|
+
inlineMenu ? E(ui.Menu, {
|
|
6113
|
+
open: menuOpen,
|
|
6114
|
+
onClose: () => setMenuOpen(false),
|
|
6115
|
+
portal: true,
|
|
6116
|
+
closeOnPointerLeave: true,
|
|
6117
|
+
dense: true,
|
|
6118
|
+
anchor: E('button', {
|
|
6119
|
+
type: 'button',
|
|
6120
|
+
className: 'bw-row-btn',
|
|
6121
|
+
'aria-label': official('actions.session.aria', { name: node.leaf || node.title }) || '...',
|
|
6122
|
+
title: sessionActions.triggerTitle,
|
|
6123
|
+
onClick: () => setMenuOpen((open) => !open),
|
|
6124
|
+
}, icon('IconEllipsisOutline16', 15)),
|
|
6125
|
+
items: sessionActions.menuItems(node),
|
|
6126
|
+
onSelect: (id) => { setMenuOpen(false); sessionActions.pick(node, id) },
|
|
6127
|
+
}, menuSeatChildren)
|
|
6128
|
+
: sessionActions.trigger ? E('button', {
|
|
5724
6129
|
type: 'button',
|
|
5725
6130
|
className: 'bw-icon-btn',
|
|
5726
6131
|
'aria-label': official('actions.session.aria', { name: node.leaf || node.title }) || '...',
|
|
5727
6132
|
title: sessionActions.triggerTitle,
|
|
5728
6133
|
onClick: (e) => sessionActions.openMenu(node, e),
|
|
5729
6134
|
}, icon('IconEllipsisOutline16', 15)) : null,
|
|
5730
|
-
sessionActions.quick && sessionActions.pin && !node.archived ? E('button', {
|
|
6135
|
+
!quickSeatUsed && sessionActions.quick && sessionActions.pin && !node.archived ? E('button', {
|
|
5731
6136
|
type: 'button',
|
|
5732
6137
|
className: 'bw-icon-btn',
|
|
5733
6138
|
'aria-label': official(node.pinned ? 'menu.unpinSession' : 'menu.pinSession') || '',
|
|
5734
6139
|
title: official(node.pinned ? 'actions.unpin' : 'actions.pin') || undefined,
|
|
5735
6140
|
onClick: (e) => { e.stopPropagation(); sessionActions.pin(node.id, !node.pinned) },
|
|
5736
6141
|
}, icon(node.pinned ? 'IconPinFillRegular' : 'IconPinOutlineRegular', 14)) : null,
|
|
5737
|
-
sessionActions.quick && sessionActions.archive ? E('button', {
|
|
6142
|
+
!quickSeatUsed && sessionActions.quick && sessionActions.archive ? E('button', {
|
|
5738
6143
|
type: 'button',
|
|
5739
6144
|
className: 'bw-icon-btn',
|
|
5740
6145
|
'aria-label': official(node.archived ? 'menu.unarchiveSession' : 'menu.archiveSession') || '',
|
|
@@ -5743,11 +6148,18 @@ window.__ModuleLoader__.load({
|
|
|
5743
6148
|
}, icon(node.archived ? 'IconUnarchiveOutlineRegular' : 'IconArchiveOutline20', 14)) : null,
|
|
5744
6149
|
)
|
|
5745
6150
|
: null
|
|
5746
|
-
|
|
6151
|
+
const marquee = useTitleMarquee()
|
|
6152
|
+
const title = node.leaf || node.title
|
|
6153
|
+
// Official HoverCard (Rows.tsx 693-703): the row is its own anchor; the
|
|
6154
|
+
// card yields while the row menu is open. Title copy rides the official
|
|
6155
|
+
// copy/copyLabels; a blank row copies nothing (official rule).
|
|
6156
|
+
const hoverCard = typeof ui.HoverCard === 'function' && sessionActions && typeof sessionActions.hoverBody === 'function'
|
|
6157
|
+
let row = E('div', {
|
|
5747
6158
|
className: cls('bw-row', 'bw-session-row', current && 'bw-row-current',
|
|
5748
6159
|
node.archived && 'bw-row-archived',
|
|
6160
|
+
menuOpen && 'menu-open',
|
|
5749
6161
|
dropHalf === 'before' && 'bw-drop-before', dropHalf === 'after' && 'bw-drop-after'),
|
|
5750
|
-
style: {
|
|
6162
|
+
style: { '--bw-indent': (depth * 12) + 'px', ...(custStyle || {}) },
|
|
5751
6163
|
onClick: () => {
|
|
5752
6164
|
if (node.archived) {
|
|
5753
6165
|
// Official semantics (guardedOpen): an archived row is not
|
|
@@ -5757,6 +6169,14 @@ window.__ModuleLoader__.load({
|
|
|
5757
6169
|
}
|
|
5758
6170
|
onOpen(node.id)
|
|
5759
6171
|
},
|
|
6172
|
+
onDoubleClick: node.blank ? undefined : (e) => {
|
|
6173
|
+
// Official rename affordance (Rows.tsx title onDoubleClick): the
|
|
6174
|
+
// double click never reaches the row's open handler.
|
|
6175
|
+
e.stopPropagation()
|
|
6176
|
+
if (sessionActions && typeof sessionActions.rename === 'function') sessionActions.rename(node)
|
|
6177
|
+
},
|
|
6178
|
+
onPointerEnter: marquee.onPointerEnter,
|
|
6179
|
+
onPointerLeave: marquee.onPointerLeave,
|
|
5760
6180
|
onContextMenu: onContextMenu,
|
|
5761
6181
|
role: 'treeitem',
|
|
5762
6182
|
'aria-description': node.archived ? archivedTitle || undefined : undefined,
|
|
@@ -5766,21 +6186,41 @@ window.__ModuleLoader__.load({
|
|
|
5766
6186
|
status && typeof ui.StateDot === 'function'
|
|
5767
6187
|
? (state === 'ongoing' && breathing
|
|
5768
6188
|
// Running breathes blue ON the session row only (never relays);
|
|
5769
|
-
// the settings toggle covers this breathing too.
|
|
5770
|
-
|
|
5771
|
-
|
|
6189
|
+
// the settings toggle covers this breathing too. The dot takes
|
|
6190
|
+
// its official size (14px spinner / 10px lamp), inside the
|
|
6191
|
+
// fixed 16px leading cell.
|
|
6192
|
+
? E(PulseGlow, { state: 'ongoing' }, E(ui.StateDot, { state: status.state }))
|
|
6193
|
+
: E(ui.StateDot, { state: status.state }))
|
|
5772
6194
|
: (markNode || E('span', { className: 'bw-dot' })),
|
|
5773
6195
|
),
|
|
5774
|
-
E('span', { className: 'bw-row-label'
|
|
6196
|
+
E('span', { className: 'bw-row-label', ref: marquee.titleRef }, title),
|
|
5775
6197
|
// Legacy projection mark (see markNode): only ever renders where the
|
|
5776
6198
|
// shipped seat does not exist, so the row never shows two marks.
|
|
5777
6199
|
node.hasActiveSchedule && !markNode
|
|
5778
6200
|
? E('span', { className: 'bw-schedule-badge', role: 'img', 'aria-label': t('schedule.active'), title: t('schedule.active') }, icon('IconAlarmClockOutline16', 14))
|
|
5779
6201
|
: null,
|
|
5780
|
-
|
|
5781
|
-
|
|
6202
|
+
// Official blank rows carry no time, no pin, no actions (a standing
|
|
6203
|
+
// "new session" placeholder, not a session).
|
|
6204
|
+
!node.blank ? E('span', { className: 'bw-row-time' }, timeLabel(node.updatedAt, now, t)) : null,
|
|
6205
|
+
!node.blank && node.pinned ? E('span', { className: 'bw-pin-indicator', role: 'img', 'aria-label': pinnedTitle || undefined, title: pinnedTitle || undefined }, icon('IconPinFillRegular', 13)) : null,
|
|
5782
6206
|
strip,
|
|
5783
6207
|
)
|
|
6208
|
+
if (hoverCard) {
|
|
6209
|
+
const copyText = node.blank || title === '' ? undefined : title
|
|
6210
|
+
const copyLabel = official('copy')
|
|
6211
|
+
const copiedLabel = official('hover.copied')
|
|
6212
|
+
row = E(ui.HoverCard, Object.assign({
|
|
6213
|
+
key: 'hover',
|
|
6214
|
+
anchor: row,
|
|
6215
|
+
content: sessionActions.hoverBody(node, status, state),
|
|
6216
|
+
openDelayMs: 800,
|
|
6217
|
+
grouped: true,
|
|
6218
|
+
disabled: menuOpen,
|
|
6219
|
+
// copyText present REQUIRES both labels (primitives HoverCard
|
|
6220
|
+
// contract): copy only renders where the official copy survives.
|
|
6221
|
+
}, copyText && copyLabel && copiedLabel ? { copyText, copyLabel, copiedLabel } : {}))
|
|
6222
|
+
}
|
|
6223
|
+
return row
|
|
5784
6224
|
}
|
|
5785
6225
|
|
|
5786
6226
|
/**
|
|
@@ -5791,14 +6231,16 @@ window.__ModuleLoader__.load({
|
|
|
5791
6231
|
function SessionGroupRow({ name, depth, expanded, count, onToggle, onContextMenu, dropInto, dragEvents, custStyle, pulse, t }) {
|
|
5792
6232
|
return E('div', {
|
|
5793
6233
|
className: cls('bw-row', 'bw-sgroup-row', dropInto && 'bw-drop-into', dropInto && 'bw-drop-into-strong'),
|
|
5794
|
-
style: {
|
|
6234
|
+
style: { '--bw-indent': (depth * 12) + 'px', ...(custStyle || {}) },
|
|
5795
6235
|
onClick: onToggle,
|
|
5796
6236
|
onContextMenu: onContextMenu,
|
|
5797
6237
|
role: 'treeitem',
|
|
5798
6238
|
'aria-expanded': expanded,
|
|
5799
6239
|
...(dragEvents || {}),
|
|
5800
6240
|
},
|
|
5801
|
-
|
|
6241
|
+
// Session groups are a plugin concept: their chevron stays visible
|
|
6242
|
+
// (only the official container rows hide it until hover).
|
|
6243
|
+
E('span', { className: cls('bw-chevron', expanded && 'bw-chevron-open') }, E('span', { className: 'bw-arrow' }, icon('IconTriangleRightFill14', 14))),
|
|
5802
6244
|
// Session groups have no icon slot: a collapsed group with hidden
|
|
5803
6245
|
// status relays through a breathing official StateDot instead.
|
|
5804
6246
|
pulse ? E(PulseGlow, { state: pulse }, typeof ui.StateDot === 'function' ? E(ui.StateDot, { state: pulse, size: 10 }) : null) : null,
|
|
@@ -6395,6 +6837,12 @@ window.__ModuleLoader__.load({
|
|
|
6395
6837
|
// Progressive listing quota (v0.21.0, issue #8): 0 = unlimited.
|
|
6396
6838
|
const rawLimit = Math.floor(Number(prefs.sessionLimit))
|
|
6397
6839
|
const sessionLimit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 500) : 0
|
|
6840
|
+
// Row density (v0.34.0): Comfortable rides the shipped sidebar row
|
|
6841
|
+
// heights (the default); Compact/Roomy drive the --bw-row-h dials.
|
|
6842
|
+
const densityRaw = String(prefs.density || 'comfortable')
|
|
6843
|
+
const density = densityRaw === 'compact' || densityRaw === 'roomy' ? densityRaw : 'comfortable'
|
|
6844
|
+
// Session count badge: the shipped sidebar retired it; default OFF.
|
|
6845
|
+
const showCount = prefs.showCount === true
|
|
6398
6846
|
// Grouping strategy (v0.21.0, issue #7): rides the view store top-level
|
|
6399
6847
|
// key next to groupBy/orderBy and stays browser-local, like every other
|
|
6400
6848
|
// view option. The legacy workspaceTitleSlash boolean is consulted only
|
|
@@ -6576,6 +7024,32 @@ window.__ModuleLoader__.load({
|
|
|
6576
7024
|
}, E('span', { className: 'bw-switch-thumb' })),
|
|
6577
7025
|
),
|
|
6578
7026
|
E('div', { className: 'bw-hint' }, t('settings.statusPulse.hint')),
|
|
7027
|
+
E('div', { className: 'bw-setting-row' },
|
|
7028
|
+
E('div', { className: 'bw-setting-label' }, t('settings.density')),
|
|
7029
|
+
E('div', { className: 'bw-seg' },
|
|
7030
|
+
['compact', 'comfortable', 'roomy'].map((mode) =>
|
|
7031
|
+
E('button', {
|
|
7032
|
+
type: 'button',
|
|
7033
|
+
key: mode,
|
|
7034
|
+
className: cls('bw-seg-btn', density === mode && 'bw-seg-btn-active'),
|
|
7035
|
+
onClick: () => { setPref('density', mode) },
|
|
7036
|
+
}, t('settings.density.' + mode)),
|
|
7037
|
+
),
|
|
7038
|
+
),
|
|
7039
|
+
),
|
|
7040
|
+
E('div', { className: 'bw-hint' }, t('settings.density.hint')),
|
|
7041
|
+
E('div', { className: 'bw-setting-row' },
|
|
7042
|
+
E('div', { className: 'bw-setting-label' }, t('settings.showCount')),
|
|
7043
|
+
E('button', {
|
|
7044
|
+
type: 'button',
|
|
7045
|
+
role: 'switch',
|
|
7046
|
+
'aria-checked': showCount,
|
|
7047
|
+
'aria-label': t('settings.showCount'),
|
|
7048
|
+
className: cls('bw-switch', showCount && 'bw-switch-on'),
|
|
7049
|
+
onClick: () => { setPref('showCount', !showCount) },
|
|
7050
|
+
}, E('span', { className: 'bw-switch-thumb' })),
|
|
7051
|
+
),
|
|
7052
|
+
E('div', { className: 'bw-hint' }, t('settings.showCount.hint')),
|
|
6579
7053
|
E('div', { className: 'bw-setting-row' },
|
|
6580
7054
|
E('div', { className: 'bw-setting-label' }, t('settings.autoStyle')),
|
|
6581
7055
|
E('button', {
|
|
@@ -6935,7 +7409,7 @@ window.__ModuleLoader__.load({
|
|
|
6935
7409
|
useDirectoryFlow,
|
|
6936
7410
|
pickDirectory, listDirectory, createDirectory,
|
|
6937
7411
|
sidebarFlowEntry,
|
|
6938
|
-
shippedBrowserFace, scheduleSeatReader,
|
|
7412
|
+
shippedBrowserFace, scheduleSeatReader, seatEntriesReader,
|
|
6939
7413
|
officialT,
|
|
6940
7414
|
presetFaceOf,
|
|
6941
7415
|
t,
|
|
@@ -7006,6 +7480,10 @@ window.__ModuleLoader__.load({
|
|
|
7006
7480
|
// Progressive listing (v0.21.0, issue #8): 0 means unlimited (default).
|
|
7007
7481
|
const rawLimit = Math.floor(Number(prefsMap.sessionLimit))
|
|
7008
7482
|
const sessionLimit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 500) : 0
|
|
7483
|
+
// Tree density + count badge (v0.34.0), read fresh every render.
|
|
7484
|
+
const densityRaw = String(prefsMap.density || 'comfortable')
|
|
7485
|
+
const density = densityRaw === 'compact' || densityRaw === 'roomy' ? densityRaw : 'comfortable'
|
|
7486
|
+
const showCount = prefsMap.showCount === true
|
|
7009
7487
|
// Dual-write wrappers: every preference mutation lands in the local
|
|
7010
7488
|
// store (immediate echo + scope-less fallback) AND the host settings
|
|
7011
7489
|
// store (durable cross-device copy for the manual pull on the other
|
|
@@ -7149,6 +7627,15 @@ window.__ModuleLoader__.load({
|
|
|
7149
7627
|
// The shipped schedule mark's seat occupant (see scheduleSeatOf); absent
|
|
7150
7628
|
// whenever the Schedule overlay is off, which is the shipped default.
|
|
7151
7629
|
const scheduleSeat = typeof scheduleSeatReader === 'function' ? scheduleSeatReader() : null
|
|
7630
|
+
// The shipped session-row seats (official Rows.tsx): menu.item
|
|
7631
|
+
// occupants become the in-row menu's trailing children, row.action
|
|
7632
|
+
// occupants become the official pin/archive quick buttons (our own
|
|
7633
|
+
// fallback buttons step aside), session.row.hover decorates the hover
|
|
7634
|
+
// card. All read off the ledger like the schedule mark.
|
|
7635
|
+
const seatEntriesOf = typeof seatEntriesReader === 'function' ? seatEntriesReader : null
|
|
7636
|
+
const rowMenuSeat = seatEntriesOf ? seatEntriesOf('sidebar.workspaces.session.menu.item') : []
|
|
7637
|
+
const rowActionSeat = seatEntriesOf ? seatEntriesOf('sidebar.workspaces.session.row.action') : []
|
|
7638
|
+
const hoverSeat = seatEntriesOf ? seatEntriesOf('sidebar.session.row.hover') : []
|
|
7152
7639
|
/** Effective catalog row of one shipped command (current keybinding, label). */
|
|
7153
7640
|
const shortcutRowOf = (id) => {
|
|
7154
7641
|
const rows = Array.isArray(shortcutRows) ? shortcutRows : EMPTY_LIST
|
|
@@ -7275,6 +7762,34 @@ window.__ModuleLoader__.load({
|
|
|
7275
7762
|
}
|
|
7276
7763
|
}, [])
|
|
7277
7764
|
|
|
7765
|
+
// Modal surface handoff for dsh-any-background: that plugin sorts dialog
|
|
7766
|
+
// surfaces by [aria-labelledby] (settings panels have it; confirm dialogs
|
|
7767
|
+
// do not and are pinned OPAQUE by design, exemptDefaultRule). The official
|
|
7768
|
+
// ui-primitives Modal never emits aria-labelledby (Modal.tsx: root gets
|
|
7769
|
+
// aria-label only, the <h2> has no id), so every Modal-built dialog lands
|
|
7770
|
+
// in the confirm bucket and stays solid no matter the sliders. Patch ONLY
|
|
7771
|
+
// our own dialogs (recognizable by .bw-modal-body/.bw-modal-actions):
|
|
7772
|
+
// point aria-labelledby at the header <h2>. Host confirm dialogs keep
|
|
7773
|
+
// their intentional opaque treatment.
|
|
7774
|
+
React.useEffect(() => {
|
|
7775
|
+
if (typeof MutationObserver !== 'function' || !document.body) return undefined
|
|
7776
|
+
let seq = 0
|
|
7777
|
+
const sweep = () => {
|
|
7778
|
+
const dialogs = document.querySelectorAll('[role="dialog"][aria-modal="true"][aria-label]:not([aria-labelledby])')
|
|
7779
|
+
for (const dlg of dialogs) {
|
|
7780
|
+
if (!dlg.querySelector('.bw-modal-body, .bw-modal-actions')) continue
|
|
7781
|
+
const heading = dlg.querySelector('h2')
|
|
7782
|
+
if (!heading) continue
|
|
7783
|
+
if (!heading.id) { seq += 1; heading.id = 'bw-dialog-label-' + seq }
|
|
7784
|
+
dlg.setAttribute('aria-labelledby', heading.id)
|
|
7785
|
+
}
|
|
7786
|
+
}
|
|
7787
|
+
const observer = new MutationObserver(sweep)
|
|
7788
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
7789
|
+
sweep()
|
|
7790
|
+
return () => observer.disconnect()
|
|
7791
|
+
}, [])
|
|
7792
|
+
|
|
7278
7793
|
const [query, setQuery] = React.useState('')
|
|
7279
7794
|
const [searchOpen, setSearchOpen] = React.useState(false)
|
|
7280
7795
|
const [flowBusy, setFlowBusy] = React.useState(false) // createWorkspace in flight after a pick
|
|
@@ -7698,6 +8213,41 @@ window.__ModuleLoader__.load({
|
|
|
7698
8213
|
const value = typeof officialT === 'function' ? officialT(key, params) : null
|
|
7699
8214
|
return typeof value === 'string' && value !== '' && value !== key ? value : fallback
|
|
7700
8215
|
}
|
|
8216
|
+
|
|
8217
|
+
// Official hover-card bodies (Rows.tsx WorkspaceHoverContent /
|
|
8218
|
+
// SessionHoverContent): the palette is FIXED in the shipped card
|
|
8219
|
+
// (#FFFFFF title, #CFD3D6 meta, #ADB2B8 status), and the copy rides
|
|
8220
|
+
// the official 'workspace' dictionary; a missing key degrades to the
|
|
8221
|
+
// aligned bw time label.
|
|
8222
|
+
const workspaceHoverBody = (workspace) => E('div', { className: 'bw-hover-card' },
|
|
8223
|
+
E('div', { className: 'bw-hover-title' }, workspace.title || workspace.leaf),
|
|
8224
|
+
workspace.path ? E('div', { className: 'bw-hover-path' }, workspace.path) : null,
|
|
8225
|
+
)
|
|
8226
|
+
const sessionHoverTime = (node) => {
|
|
8227
|
+
if (typeof ui.relativeTime !== 'function') return timeLabel(node.updatedAt, now, t)
|
|
8228
|
+
const r = ui.relativeTime(node.updatedAt, now)
|
|
8229
|
+
if (!r) return ''
|
|
8230
|
+
if (r.unit === 'now') return officialText('time.now', undefined, t('time.now'))
|
|
8231
|
+
const inner = officialText('time.' + r.unit, { n: r.n }, timeLabel(node.updatedAt, now, t))
|
|
8232
|
+
const ago = officialText('time.ago', { t: inner }, null)
|
|
8233
|
+
return ago || inner
|
|
8234
|
+
}
|
|
8235
|
+
const sessionHoverBody = (node, status) => E('div', { className: 'bw-hover-card' },
|
|
8236
|
+
E('div', { className: 'bw-hover-title' }, node.leaf || node.title),
|
|
8237
|
+
!node.blank ? E('div', { className: 'bw-hover-time' }, sessionHoverTime(node)) : null,
|
|
8238
|
+
// The shipped session.hover decorations render inside the card,
|
|
8239
|
+
// like the official renderSlot('sidebar.session.row.hover').
|
|
8240
|
+
...(Array.isArray(hoverSeat) ? hoverSeat.map((seat, index) => E(QuietBoundary, { key: 'hover-seat-' + index }, E(ShippedSeatOccupant, {
|
|
8241
|
+
seat,
|
|
8242
|
+
ownerProps: { sessionId: node.id },
|
|
8243
|
+
t: seat.t,
|
|
8244
|
+
}))) : []),
|
|
8245
|
+
status && typeof ui.StateDot === 'function' ? E('div', { className: 'bw-hover-status' },
|
|
8246
|
+
E(ui.StateDot, { state: status.state }), E('span', null, status.title)) : null,
|
|
8247
|
+
node.archived && officialText('row.archived', undefined, '')
|
|
8248
|
+
? E('div', { className: 'bw-hover-status' }, icon('IconArchiveOutline20', 14), E('span', null, officialText('row.archived', undefined, '')))
|
|
8249
|
+
: null,
|
|
8250
|
+
)
|
|
7701
8251
|
const pinAvailable = typeof pinSession === 'function' && typeof unpinSession === 'function'
|
|
7702
8252
|
const unarchiveAvailable = typeof unarchiveSession === 'function'
|
|
7703
8253
|
const sessionActions = {
|
|
@@ -7734,6 +8284,24 @@ window.__ModuleLoader__.load({
|
|
|
7734
8284
|
}
|
|
7735
8285
|
: undefined,
|
|
7736
8286
|
openMenu: (node, e) => openCtx('session', node, e),
|
|
8287
|
+
// In-row "..." menu (official Rows.tsx): the SAME shared entry list
|
|
8288
|
+
// (sessionMenuEntriesFor) and the SAME pick dispatch the right-click
|
|
8289
|
+
// menu uses, so the two surfaces cannot drift. menuSeat carries the
|
|
8290
|
+
// shipped menu.item occupants (rendered as Menu children);
|
|
8291
|
+
// rowActionSeat the official quick buttons (our own buttons step
|
|
8292
|
+
// aside while it has entries). An id the shared dispatcher does not
|
|
8293
|
+
// own (bw extensions: move-group / customize) falls back to the
|
|
8294
|
+
// ctx surface's handler through a transient payload.
|
|
8295
|
+
menuItems: (node) => (typeof sessionMenuEntriesFor === 'function' ? sessionMenuEntriesFor(node) : []),
|
|
8296
|
+
menuSeat: rowMenuSeat,
|
|
8297
|
+
rowActionSeat,
|
|
8298
|
+
pick: (node, id) => {
|
|
8299
|
+
if (typeof pickSessionAction === 'function' && pickSessionAction(node, id)) return
|
|
8300
|
+
setCtx({ kind: 'session', payload: node, x: 0, y: 0 })
|
|
8301
|
+
if (typeof handleCtxPick === 'function') handleCtxPick(id)
|
|
8302
|
+
},
|
|
8303
|
+
rename: (node) => setDialog({ kind: 'sess-rename', session: node }),
|
|
8304
|
+
hoverBody: (node, status) => sessionHoverBody(node, status),
|
|
7737
8305
|
explainArchived: () => {
|
|
7738
8306
|
const text = officialText('toast.archivedNotOpenable', undefined, '') || officialText('row.archived', undefined, '')
|
|
7739
8307
|
if (text) setErrorText(text)
|
|
@@ -8541,12 +9109,20 @@ window.__ModuleLoader__.load({
|
|
|
8541
9109
|
depth,
|
|
8542
9110
|
count,
|
|
8543
9111
|
sessionsOpen: searching ? true : sessionsOpenOf(workspace.workspaceId),
|
|
8544
|
-
currentInside:
|
|
9112
|
+
currentInside: wsHasCurrent(workspace),
|
|
8545
9113
|
onToggle: () => { if (!searching) actions.setSessionsExpanded(workspace.workspaceId, !sessionsOpenOf(workspace.workspaceId)) },
|
|
8546
9114
|
// Starting a session force-expands the workspace row: the user must
|
|
8547
9115
|
// SEE the new session appear, even if the row was collapsed.
|
|
8548
9116
|
onStart: () => { actions.setSessionsExpanded(workspace.workspaceId, true); startSession(workspace.workspaceId) },
|
|
8549
9117
|
onContextMenu: (e) => openCtx('workspace', workspace, e),
|
|
9118
|
+
// Official Rows.tsx: the ellipsis button anchors the row's own
|
|
9119
|
+
// menu; the ctx surface stays the right-click path. While the
|
|
9120
|
+
// menu is open the row pins (hover fill + visible actions),
|
|
9121
|
+
// matching the official menuOpen state.
|
|
9122
|
+
onMenu: (e) => openCtx('workspace', workspace, e),
|
|
9123
|
+
menuOpen: !!(ctx && ctx.kind === 'workspace' && ctx.payload && ctx.payload.workspaceId === workspace.workspaceId),
|
|
9124
|
+
menuAria: officialText('actions.workspace.aria', { name: workspace.title || workspace.leaf }) || undefined,
|
|
9125
|
+
hoverBody: workspaceHoverBody,
|
|
8550
9126
|
dropHalf: wsDropHalf(workspace.workspaceId),
|
|
8551
9127
|
dropInto: wsRootDropActive(workspace.workspaceId),
|
|
8552
9128
|
dragEvents: workspaceDragEvents(workspace),
|
|
@@ -8570,6 +9146,26 @@ window.__ModuleLoader__.load({
|
|
|
8570
9146
|
return rows
|
|
8571
9147
|
}
|
|
8572
9148
|
|
|
9149
|
+
// Official folderActive semantics (Rows.tsx ProjectRow): a folder or
|
|
9150
|
+
// workspace whose SUBTREE holds the current session tints its folder
|
|
9151
|
+
// glyph blue, regardless of expansion. The plugin tree is path-folded,
|
|
9152
|
+
// so ancestor folders match by disk-path prefix, and disk-nested
|
|
9153
|
+
// workspace chains are walked through the sub levels.
|
|
9154
|
+
const pathHoldsCurrent = (ancestor, child) => !!ancestor && !!child
|
|
9155
|
+
&& (child === ancestor || child.startsWith(ancestor.endsWith('/') ? ancestor : ancestor + '/'))
|
|
9156
|
+
const subHasCurrent = (level) => {
|
|
9157
|
+
if (!level || !currentId) return false
|
|
9158
|
+
for (const w of level.workspaces || []) if (wsHasCurrent(w)) return true
|
|
9159
|
+
for (const f of level.folders || []) {
|
|
9160
|
+
if (f.kind === 'ws') { if (wsHasCurrent(f.workspace)) return true } else if (subHasCurrent(f)) return true
|
|
9161
|
+
}
|
|
9162
|
+
return false
|
|
9163
|
+
}
|
|
9164
|
+
const wsHasCurrent = (w) => !!currentId && !!w
|
|
9165
|
+
&& ((w.sessionIds || []).includes(currentId) || subHasCurrent(w.sub))
|
|
9166
|
+
const currentWsPath = currentId
|
|
9167
|
+
? ((items || []).find((w) => (w.sessionIds || []).includes(currentId)) || {}).path
|
|
9168
|
+
: undefined
|
|
8573
9169
|
const renderLevelRows = (level, depth) => {
|
|
8574
9170
|
const rows = []
|
|
8575
9171
|
for (const folder of level.folders) rows.push(...renderPlainFolder(folder, depth))
|
|
@@ -8598,6 +9194,7 @@ window.__ModuleLoader__.load({
|
|
|
8598
9194
|
custStyle: rowStyleOf('folder:' + idPath),
|
|
8599
9195
|
iconMode: (styleEntry('folder:' + idPath) || {}).icon || 'solid',
|
|
8600
9196
|
pulse: expanded ? null : folderPulseOf(node),
|
|
9197
|
+
active: pathHoldsCurrent(currentWsPath, node.path),
|
|
8601
9198
|
t,
|
|
8602
9199
|
})]
|
|
8603
9200
|
if (expanded) {
|
|
@@ -8627,12 +9224,12 @@ window.__ModuleLoader__.load({
|
|
|
8627
9224
|
rows.push(E('div', {
|
|
8628
9225
|
key: 'ungrouped-label',
|
|
8629
9226
|
className: 'bw-row bw-sgroup-row',
|
|
8630
|
-
style: {
|
|
9227
|
+
style: { '--bw-indent': '0px' },
|
|
8631
9228
|
onClick: () => { if (!searching && actions && typeof actions.setUngroupedOpen === 'function') actions.setUngroupedOpen(!open) },
|
|
8632
9229
|
role: 'treeitem',
|
|
8633
9230
|
'aria-expanded': open,
|
|
8634
9231
|
},
|
|
8635
|
-
E('span', { className: cls('bw-chevron', open && 'bw-chevron-open') }, icon('IconTriangleRightFill14', 14)),
|
|
9232
|
+
E('span', { className: cls('bw-chevron', open && 'bw-chevron-open') }, E('span', { className: 'bw-arrow' }, icon('IconTriangleRightFill14', 14))),
|
|
8636
9233
|
E('span', { className: 'bw-row-label' }, t('group.ungrouped')),
|
|
8637
9234
|
E('span', { className: 'bw-row-count' }, String(ungrouped.length)),
|
|
8638
9235
|
))
|
|
@@ -8688,6 +9285,102 @@ window.__ModuleLoader__.load({
|
|
|
8688
9285
|
// provide is the bw-only surface: name-group / session-group rename
|
|
8689
9286
|
// and Customize appearance, appended after a separator.
|
|
8690
9287
|
const ot = typeof officialT === 'function' ? officialT : null
|
|
9288
|
+
// Shared session-menu builder + dispatcher: the right-click menu AND
|
|
9289
|
+
// the row's own "..." menu render the SAME entry list (official
|
|
9290
|
+
// mirror + bw extensions + trailing customize) and dispatch through
|
|
9291
|
+
// the SAME handler, so the two surfaces cannot drift. menuEntries
|
|
9292
|
+
// feeds it the ctx payload; sessionActions feeds it the row node.
|
|
9293
|
+
const sessionMenuEntriesFor = (payload) => {
|
|
9294
|
+
const customizeEntry = { id: 'customize', label: t('custom.title'), icon: icon('IconPersonalizationOutline16', 16) }
|
|
9295
|
+
const groupItem = { id: 'move-group', label: t('menu.moveToGroup'), icon: icon('IconFolderOpenOutline16', 16) }
|
|
9296
|
+
const ungroupItem = { id: 'move-out', label: t('menu.moveOutGroup'), icon: icon('IconRightUpOutline16', 16) }
|
|
9297
|
+
const sgroupItems = sessionSlash ? [groupItem] : []
|
|
9298
|
+
const sessionUngroupItems = sessionSlash && payload && groupPrefixOf(String(payload.title || '')) !== '' ? [ungroupItem] : []
|
|
9299
|
+
const official = (key, fallback) => {
|
|
9300
|
+
const value = typeof ot === 'function' ? ot(key) : null
|
|
9301
|
+
return typeof value === 'string' && value !== '' && value !== key ? value : fallback
|
|
9302
|
+
}
|
|
9303
|
+
const archived = !!(payload.archived || (archivedSet && archivedSet.has(payload.id)))
|
|
9304
|
+
const pinned = !!(payload.pinned || (pinnedSet && pinnedSet.has(payload.id)))
|
|
9305
|
+
// Official 0.1.7 mirror (Rows.tsx session menu + the shipped slot
|
|
9306
|
+
// entries): pin, rename, fork, archive — same order, same icons,
|
|
9307
|
+
// same archived-state behavior (pin hides; archive becomes
|
|
9308
|
+
// unarchive). Copy Session ID (order 450) rides the official
|
|
9309
|
+
// dictionary, so upstream rewording AND future item additions land
|
|
9310
|
+
// without a plugin release; every verb is gated on its service
|
|
9311
|
+
// method so older hosts simply never offer what they cannot do.
|
|
9312
|
+
return [
|
|
9313
|
+
...(pinAvailable && !archived ? [{
|
|
9314
|
+
id: pinned ? 'unpin' : 'pin',
|
|
9315
|
+
label: official(pinned ? 'menu.unpinSession' : 'menu.pinSession', t('menu.archive')),
|
|
9316
|
+
icon: icon(pinned ? 'IconPinFillRegular' : 'IconPinOutlineRegular', 16),
|
|
9317
|
+
}] : []),
|
|
9318
|
+
{ id: 'rename', label: official('rename', t('menu.rename')), icon: icon('IconEditOutline16', 16) },
|
|
9319
|
+
{ id: 'fork', label: official('menu.fork', t('menu.fork')), icon: icon('IconBranchOutline16', 16) },
|
|
9320
|
+
// Official semantics: archive hides the row through the
|
|
9321
|
+
// registry-global archive set and never touches the session log
|
|
9322
|
+
// — deliberately NOT styled destructive. An archived row gets
|
|
9323
|
+
// the restore entry instead.
|
|
9324
|
+
archived
|
|
9325
|
+
? { id: 'unarchive', label: official('menu.unarchiveSession', t('menu.archive')), icon: icon('IconUnarchiveOutlineRegular', 16) }
|
|
9326
|
+
: { id: 'archive', label: official('menu.archiveSession', t('menu.archive')), icon: icon('IconArchiveOutline20', 16) },
|
|
9327
|
+
...(typeof ui.writeClipboard === 'function' ? [{
|
|
9328
|
+
id: 'copy-id',
|
|
9329
|
+
label: official('menu.sessionId', t('menu.sessionId')),
|
|
9330
|
+
icon: icon('IconCopyOutlineRegular', 16),
|
|
9331
|
+
}] : []),
|
|
9332
|
+
{ type: 'separator', id: 'bw-sep' },
|
|
9333
|
+
...sessionUngroupItems,
|
|
9334
|
+
...sgroupItems,
|
|
9335
|
+
customizeEntry,
|
|
9336
|
+
]
|
|
9337
|
+
}
|
|
9338
|
+
const pickSessionAction = (payload, id) => {
|
|
9339
|
+
if (id === 'rename') { setDialog({ kind: 'sess-rename', session: payload }); return true }
|
|
9340
|
+
if (id === 'fork') { forkSession(payload.id); return true }
|
|
9341
|
+
if ((id === 'pin' || id === 'unpin') && pinAvailable) {
|
|
9342
|
+
Promise.resolve().then(() => (id === 'pin' ? pinSession : unpinSession)(payload.id)).catch((reason) => {
|
|
9343
|
+
fail(officialText(id === 'pin' ? 'toast.pinFailed' : 'toast.unpinFailed', undefined, reason instanceof Error ? reason.message : String(reason || 'pin failed')))
|
|
9344
|
+
})
|
|
9345
|
+
return true
|
|
9346
|
+
}
|
|
9347
|
+
if (id === 'unarchive' && unarchiveAvailable) {
|
|
9348
|
+
Promise.resolve().then(() => unarchiveSession(payload.id)).catch((reason) => { console.warn('session unarchive rejected:', reason) })
|
|
9349
|
+
return true
|
|
9350
|
+
}
|
|
9351
|
+
if (id === 'archive' && typeof archiveSession === 'function') {
|
|
9352
|
+
Promise.resolve().then(() => archiveSession(payload.id)).catch((reason) => {
|
|
9353
|
+
// Official refusal flow: running work refuses the plain archive
|
|
9354
|
+
// (WorkspaceArchiveError / workspace/session-active) and the
|
|
9355
|
+
// confirmation offers to stop it first. Anything else reports
|
|
9356
|
+
// through the error modal.
|
|
9357
|
+
if (reason && reason.name === 'WorkspaceArchiveError') {
|
|
9358
|
+
setDialog({ kind: 'sess-archive-confirm', sessionId: payload.id })
|
|
9359
|
+
return
|
|
9360
|
+
}
|
|
9361
|
+
fail(reason)
|
|
9362
|
+
})
|
|
9363
|
+
return true
|
|
9364
|
+
}
|
|
9365
|
+
if (id === 'copy-id' && typeof ui.writeClipboard === 'function') {
|
|
9366
|
+
// Official mirror: copied → success notice, anything else → the
|
|
9367
|
+
// failure notice (official sessionIdInjected wiring).
|
|
9368
|
+
Promise.resolve(ui.writeClipboard(payload.id)).then((copied) => {
|
|
9369
|
+
setCopyNotice((prev) => ({ seq: (prev ? prev.seq : 0) + 1, ok: copied === true }))
|
|
9370
|
+
}, () => {
|
|
9371
|
+
setCopyNotice((prev) => ({ seq: (prev ? prev.seq : 0) + 1, ok: false }))
|
|
9372
|
+
})
|
|
9373
|
+
return true
|
|
9374
|
+
}
|
|
9375
|
+
// Plain retitle back to the leaf: the row leaves its group and
|
|
9376
|
+
// returns to the top level of its workspace (v0.18.0).
|
|
9377
|
+
if (id === 'move-out') {
|
|
9378
|
+
const leaf = leafOf(String(payload.title || ''))
|
|
9379
|
+
if (leaf !== payload.title) Promise.resolve().then(() => renameByUser(payload.id, leaf)).catch(fail)
|
|
9380
|
+
return true
|
|
9381
|
+
}
|
|
9382
|
+
return false
|
|
9383
|
+
}
|
|
8691
9384
|
const menuEntries = () => {
|
|
8692
9385
|
if (ctx === null) return []
|
|
8693
9386
|
const customizeEntry = { id: 'customize', label: t('custom.title'), icon: icon('IconPersonalizationOutline16', 16) }
|
|
@@ -8701,14 +9394,8 @@ window.__ModuleLoader__.load({
|
|
|
8701
9394
|
// probing it silently yielded a glyph-less row. IconFolderOpenOutline16
|
|
8702
9395
|
// is present and reads as "move into a group" just as well.
|
|
8703
9396
|
const groupItem = { id: 'move-group', label: t('menu.moveToGroup'), icon: icon('IconFolderOpenOutline16', 16) }
|
|
8704
|
-
// "Leave the group" (v0.18.0) — the counterpart of move-group, offered
|
|
8705
|
-
// only where a group actually exists to leave: a SESSION row whose own
|
|
8706
|
-
// title carries a "/" prefix, with the session name layer on. A
|
|
8707
|
-
// top-level row gets no entry (there is nothing to leave).
|
|
8708
|
-
const ungroupItem = { id: 'move-out', label: t('menu.moveOutGroup'), icon: icon('IconRightUpOutline16', 16) }
|
|
8709
9397
|
const wsGroupItems = workspaceSlash ? [groupItem] : []
|
|
8710
9398
|
const sgroupItems = sessionSlash ? [groupItem] : []
|
|
8711
|
-
const sessionUngroupItems = sessionSlash && ctx.kind === 'session' && ctx.payload && groupPrefixOf(String(ctx.payload.title || '')) !== '' ? [ungroupItem] : []
|
|
8712
9399
|
// "Delete group" only makes sense for a DECLARED group that holds
|
|
8713
9400
|
// nothing (v0.17.0): a group with members is a projection of their
|
|
8714
9401
|
// titles, so removing the declaration would change nothing on screen
|
|
@@ -8735,49 +9422,10 @@ window.__ModuleLoader__.load({
|
|
|
8735
9422
|
...(presetFace ? [{ id: 'ws-preset', label: t('menu.workspacePreset'), icon: icon('IconAgentPresetOutlineRegular', 16) }] : []),
|
|
8736
9423
|
]
|
|
8737
9424
|
if (ctx.kind === 'session') {
|
|
8738
|
-
//
|
|
8739
|
-
//
|
|
8740
|
-
// same
|
|
8741
|
-
|
|
8742
|
-
// (Copy Session ID, order 450), mirrored below the archive row.
|
|
8743
|
-
// Copy rides the official dictionary, so upstream
|
|
8744
|
-
// rewording AND future item additions land without a plugin
|
|
8745
|
-
// release; every verb is gated on its service method so older
|
|
8746
|
-
// hosts simply never offer what they cannot do.
|
|
8747
|
-
const payload = ctx.payload || {}
|
|
8748
|
-
const archived = !!(payload.archived || (archivedSet && archivedSet.has(payload.id)))
|
|
8749
|
-
const pinned = !!(payload.pinned || (pinnedSet && pinnedSet.has(payload.id)))
|
|
8750
|
-
const official = (key, fallback) => {
|
|
8751
|
-
const value = typeof ot === 'function' ? ot(key) : null
|
|
8752
|
-
return typeof value === 'string' && value !== '' && value !== key ? value : fallback
|
|
8753
|
-
}
|
|
8754
|
-
return [
|
|
8755
|
-
...(pinAvailable && !archived ? [{
|
|
8756
|
-
id: pinned ? 'unpin' : 'pin',
|
|
8757
|
-
label: official(pinned ? 'menu.unpinSession' : 'menu.pinSession', t('menu.archive')),
|
|
8758
|
-
icon: icon(pinned ? 'IconPinFillRegular' : 'IconPinOutlineRegular', 16),
|
|
8759
|
-
}] : []),
|
|
8760
|
-
{ id: 'rename', label: official('rename', t('menu.rename')), icon: icon('IconEditOutline16', 16) },
|
|
8761
|
-
{ id: 'fork', label: official('menu.fork', t('menu.fork')), icon: icon('IconBranchOutline16', 16) },
|
|
8762
|
-
// Official semantics: archive hides the row through the
|
|
8763
|
-
// registry-global archive set and never touches the session log
|
|
8764
|
-
// — deliberately NOT styled destructive. An archived row gets
|
|
8765
|
-
// the restore entry instead.
|
|
8766
|
-
archived
|
|
8767
|
-
? { id: 'unarchive', label: official('menu.unarchiveSession', t('menu.archive')), icon: icon('IconUnarchiveOutlineRegular', 16) }
|
|
8768
|
-
: { id: 'archive', label: official('menu.archiveSession', t('menu.archive')), icon: icon('IconArchiveOutline20', 16) },
|
|
8769
|
-
// Official order 450: clipboard write rides the host primitive,
|
|
8770
|
-
// so the whole row hides where the primitives lack it.
|
|
8771
|
-
...(typeof ui.writeClipboard === 'function' ? [{
|
|
8772
|
-
id: 'copy-id',
|
|
8773
|
-
label: official('menu.sessionId', t('menu.sessionId')),
|
|
8774
|
-
icon: icon('IconCopyOutlineRegular', 16),
|
|
8775
|
-
}] : []),
|
|
8776
|
-
{ type: 'separator', id: 'bw-sep' },
|
|
8777
|
-
...sessionUngroupItems,
|
|
8778
|
-
...sgroupItems,
|
|
8779
|
-
customizeEntry,
|
|
8780
|
-
]
|
|
9425
|
+
// The shared builder owns the session list (see
|
|
9426
|
+
// sessionMenuEntriesFor): the row's own "..." menu renders the
|
|
9427
|
+
// exact same entries, so the two surfaces cannot drift.
|
|
9428
|
+
return sessionMenuEntriesFor(ctx.payload || {})
|
|
8781
9429
|
}
|
|
8782
9430
|
return [
|
|
8783
9431
|
{ id: 'rename-sgroup', label: t('menu.renameSgroup'), icon: icon('IconEditOutline16', 16) },
|
|
@@ -8818,9 +9466,13 @@ window.__ModuleLoader__.load({
|
|
|
8818
9466
|
// Freeze the target NOW (the dialog outlives the menu ctx): the
|
|
8819
9467
|
// blank lookup mirrors the official createInWorkspace rule — a
|
|
8820
9468
|
// standing blank IS the workspace default (its preset projection
|
|
8821
|
-
// is what the hero chip shows)
|
|
8822
|
-
//
|
|
8823
|
-
//
|
|
9469
|
+
// is what the hero chip shows). With NO standing blank the edit
|
|
9470
|
+
// still applies: ClientSessions.create prepares one WITH the
|
|
9471
|
+
// picked preset (the same channel as the official new-session
|
|
9472
|
+
// button), which makes it the workspace default. Creating a
|
|
9473
|
+
// session here is deliberate, not a side effect — the host has
|
|
9474
|
+
// no other per-workspace default store (agent-preset-registry
|
|
9475
|
+
// selectedDefault is GLOBAL; the standing blank IS the carrier).
|
|
8824
9476
|
const raw = (storedItems || []).find((item) => item.workspaceId === payload.workspaceId)
|
|
8825
9477
|
const blankIds = raw && Array.isArray(raw.sessionIds) ? raw.sessionIds : []
|
|
8826
9478
|
const blankId = raw && raw.path
|
|
@@ -8834,44 +9486,23 @@ window.__ModuleLoader__.load({
|
|
|
8834
9486
|
const projection = blankSummary ? blankSummary.projectionValues : undefined
|
|
8835
9487
|
setDialog({
|
|
8836
9488
|
kind: 'ws-preset',
|
|
8837
|
-
|
|
9489
|
+
hasBlank: blankId !== undefined,
|
|
9490
|
+
applyPreset: blankId !== undefined
|
|
9491
|
+
? (presetId) => presetFace.select(blankId, presetId)
|
|
9492
|
+
// Reuse declined / no draft to reuse: create WITH the preset.
|
|
9493
|
+
// Shape matches select's { ok } contract; a SessionCreateError
|
|
9494
|
+
// lands in the dialog as an inline refusal line.
|
|
9495
|
+
: (presetId) => Promise.resolve()
|
|
9496
|
+
.then(() => sessions.create({ cwd: raw.path, agentPreset: presetId }))
|
|
9497
|
+
.then(() => ({ ok: true }), (reason) => ({ ok: false, refusal: reason instanceof Error ? reason.message : String(reason || 'session create failed') })),
|
|
8838
9498
|
current: projection && typeof projection.agentPreset === 'string' ? projection.agentPreset : undefined,
|
|
8839
9499
|
})
|
|
8840
9500
|
}
|
|
8841
9501
|
else if (kind === 'sgroup' && id === 'rename-sgroup') setDialog({ kind: 'sgroup-rename', target: payload })
|
|
8842
|
-
else if (kind === 'session'
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
fail(officialText(id === 'pin' ? 'toast.pinFailed' : 'toast.unpinFailed', undefined, reason instanceof Error ? reason.message : String(reason || 'pin failed')))
|
|
8847
|
-
})
|
|
8848
|
-
}
|
|
8849
|
-
else if (kind === 'session' && id === 'unarchive' && unarchiveAvailable) {
|
|
8850
|
-
Promise.resolve().then(() => unarchiveSession(payload.id)).catch((reason) => { console.warn('session unarchive rejected:', reason) })
|
|
8851
|
-
}
|
|
8852
|
-
else if (kind === 'session' && id === 'archive' && typeof archiveSession === 'function') {
|
|
8853
|
-
Promise.resolve().then(() => archiveSession(payload.id)).catch((reason) => {
|
|
8854
|
-
if (reason && reason.name === 'WorkspaceArchiveError') {
|
|
8855
|
-
setDialog({ kind: 'sess-archive-confirm', sessionId: payload.id })
|
|
8856
|
-
return
|
|
8857
|
-
}
|
|
8858
|
-
fail(reason)
|
|
8859
|
-
})
|
|
8860
|
-
}
|
|
8861
|
-
else if (kind === 'session' && id === 'copy-id' && typeof ui.writeClipboard === 'function') {
|
|
8862
|
-
// Official mirror: copied → success notice, anything else → the
|
|
8863
|
-
// failure notice (official sessionIdInjected wiring).
|
|
8864
|
-
Promise.resolve(ui.writeClipboard(payload.id)).then((copied) => {
|
|
8865
|
-
setCopyNotice((prev) => ({ seq: (prev ? prev.seq : 0) + 1, ok: copied === true }))
|
|
8866
|
-
}, () => {
|
|
8867
|
-
setCopyNotice((prev) => ({ seq: (prev ? prev.seq : 0) + 1, ok: false }))
|
|
8868
|
-
})
|
|
8869
|
-
}
|
|
8870
|
-
// Plain retitle back to the leaf: the row leaves its group and returns
|
|
8871
|
-
// to the top level of its workspace (v0.18.0).
|
|
8872
|
-
else if (kind === 'session' && id === 'move-out') {
|
|
8873
|
-
const leaf = leafOf(String(payload.title || ''))
|
|
8874
|
-
if (leaf !== payload.title) Promise.resolve().then(() => renameByUser(payload.id, leaf)).catch(fail)
|
|
9502
|
+
else if (kind === 'session') {
|
|
9503
|
+
// The shared dispatcher owns every session verb (see
|
|
9504
|
+
// pickSessionAction); an unknown id falls through silently.
|
|
9505
|
+
if (!pickSessionAction(payload, id)) return
|
|
8875
9506
|
}
|
|
8876
9507
|
}
|
|
8877
9508
|
|
|
@@ -9255,7 +9886,14 @@ window.__ModuleLoader__.load({
|
|
|
9255
9886
|
disabled: flowBusy,
|
|
9256
9887
|
}, icon('IconProjectAddOutline16')) : null,
|
|
9257
9888
|
),
|
|
9258
|
-
E('div', {
|
|
9889
|
+
E('div', {
|
|
9890
|
+
ref: treeRef,
|
|
9891
|
+
className: 'bw-tree',
|
|
9892
|
+
role: 'tree',
|
|
9893
|
+
'aria-label': t('title'),
|
|
9894
|
+
'data-bw-density': density,
|
|
9895
|
+
...(showCount ? { 'data-bw-count': '' } : {}),
|
|
9896
|
+
}, bodyRows),
|
|
9259
9897
|
dialogElement,
|
|
9260
9898
|
(ctx !== null && typeof ui.Menu === 'function') ? E(ui.Menu, {
|
|
9261
9899
|
// Right-click opens the SAME menu surface the official ⋯ button
|
|
@@ -9540,6 +10178,25 @@ window.__ModuleLoader__.load({
|
|
|
9540
10178
|
}
|
|
9541
10179
|
return { component: seat.component, face: seat.face, t: seatT }
|
|
9542
10180
|
},
|
|
10181
|
+
// The shipped session-row seat occupants, read off the ledger in a
|
|
10182
|
+
// LIST (same shape as scheduleSeatReader): one reader serves every
|
|
10183
|
+
// seat name (menu.item / row.action / session.row.hover).
|
|
10184
|
+
seatEntriesReader: (name) => {
|
|
10185
|
+
try {
|
|
10186
|
+
const entries = slots.entries(name)
|
|
10187
|
+
if (!Array.isArray(entries)) return []
|
|
10188
|
+
return entries.map((entry) => {
|
|
10189
|
+
if (!entry || !entry.component || typeof entry.inject !== 'function') return null
|
|
10190
|
+
let seatT = null
|
|
10191
|
+
if (entry.locale && ctx.locale && typeof ctx.locale.bind === 'function') {
|
|
10192
|
+
try { seatT = ctx.locale.bind(entry.locale) } catch (error) { seatT = null }
|
|
10193
|
+
}
|
|
10194
|
+
return { component: entry.component, face: entry.inject(), t: seatT }
|
|
10195
|
+
}).filter(Boolean)
|
|
10196
|
+
} catch (error) {
|
|
10197
|
+
return []
|
|
10198
|
+
}
|
|
10199
|
+
},
|
|
9543
10200
|
startSession: (workspaceId) => { uiWorkspace.startSession(workspaceId) },
|
|
9544
10201
|
open: (sessionId) => { uiWorkspace.openSession(sessionId) },
|
|
9545
10202
|
searchSessions,
|