dsh-coding-sidebar 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/client-editor.js +257 -178
- package/lib/client-registry.js +959 -345
- package/lib/client-terminal.js +307 -172
- package/lib/client.js +952 -338
- package/lib/index.js +214 -2
- package/lib/types/changes-ops.d.ts +31 -0
- package/lib/types/client/DiffView.d.ts +33 -1
- package/lib/types/client/EditorHost.d.ts +3 -0
- package/lib/types/client/FileTree.d.ts +4 -0
- package/lib/types/client/SessionLens.d.ts +4 -0
- package/lib/types/client/TerminalWaitBanner.d.ts +6 -0
- package/lib/types/client/TreePanel.d.ts +3 -0
- package/lib/types/client/api.d.ts +36 -0
- package/lib/types/client/locales.d.ts +20 -0
- package/lib/types/client/redact.d.ts +14 -0
- package/lib/types/client/state.d.ts +15 -0
- package/lib/types/fs-operations.d.ts +42 -0
- package/lib/types/git.d.ts +15 -0
- package/package.json +1 -1
- package/src/changes-ops.ts +78 -0
- package/src/client/DiffTab.tsx +10 -1
- package/src/client/DiffView.tsx +174 -19
- package/src/client/EditorHost.tsx +8 -1
- package/src/client/FileTree.tsx +147 -4
- package/src/client/GitView.tsx +37 -0
- package/src/client/SessionLens.tsx +95 -0
- package/src/client/Sidebar.tsx +54 -3
- package/src/client/TerminalView.tsx +28 -0
- package/src/client/TerminalWaitBanner.tsx +32 -0
- package/src/client/TreePanel.tsx +6 -1
- package/src/client/api.ts +24 -0
- package/src/client/locales-ar.ts +20 -0
- package/src/client/locales-de.ts +20 -0
- package/src/client/locales-fr.ts +20 -0
- package/src/client/locales-hi.ts +20 -0
- package/src/client/locales-id.ts +20 -0
- package/src/client/locales-it.ts +20 -0
- package/src/client/locales-ja.ts +20 -0
- package/src/client/locales-ko.ts +20 -0
- package/src/client/locales-nl.ts +20 -0
- package/src/client/locales-pl.ts +20 -0
- package/src/client/locales-pt.ts +20 -0
- package/src/client/locales-ru.ts +20 -0
- package/src/client/locales-sv.ts +20 -0
- package/src/client/locales-th.ts +20 -0
- package/src/client/locales-tr.ts +20 -0
- package/src/client/locales-vi.ts +20 -0
- package/src/client/locales-zh-HK.ts +20 -0
- package/src/client/locales-zh-MO.ts +20 -0
- package/src/client/locales-zh-TW.ts +20 -0
- package/src/client/locales.ts +40 -0
- package/src/client/redact.ts +39 -0
- package/src/client/sidebar.module.css +183 -0
- package/src/client/state.ts +42 -3
- package/src/fs-operations.ts +126 -4
- package/src/git.ts +39 -2
- package/src/index.ts +56 -1
package/src/client/locales-pt.ts
CHANGED
|
@@ -138,6 +138,26 @@ export const pt: Record<string, string> = {
|
|
|
138
138
|
produced: 'Produzidos',
|
|
139
139
|
producedOpen: 'Abrir na barra lateral',
|
|
140
140
|
disconnected: 'Terminal desconectado, reconectando…',
|
|
141
|
+
terminalWaitBanner: 'O agente está aguardando {needle}',
|
|
142
|
+
terminalSkipWait: 'Pular espera',
|
|
143
|
+
gitFoldExpand: 'Mostrar as {count} linhas de contexto',
|
|
144
|
+
gitFoldLoading: 'A expandir…',
|
|
145
|
+
gitFoldFailed: 'Falha ao expandir',
|
|
146
|
+
rename: 'Renomear',
|
|
147
|
+
renameInvalid: 'O nome não pode estar vazio nem conter separadores de caminho.',
|
|
148
|
+
delete: 'Eliminar',
|
|
149
|
+
deleteTitle: 'Eliminar "{name}"?',
|
|
150
|
+
deleteDescFile: 'Isto elimina definitivamente o ficheiro. Não pode ser anulado.',
|
|
151
|
+
deleteDescDir: 'Isto elimina definitivamente o diretório e todo o seu conteúdo. Não pode ser anulado.',
|
|
152
|
+
dismiss: 'Fechar',
|
|
153
|
+
changesSessionGit: 'Alterações do Git',
|
|
154
|
+
changesSessionLens: 'Alterações da sessão',
|
|
155
|
+
changesEmpty: 'Sem operações de ficheiro nesta sessão',
|
|
156
|
+
changesCount: '{count} operações de ficheiro',
|
|
157
|
+
changesRedacted: '[MASCARADO]',
|
|
158
|
+
changesBinary: 'Ficheiro binário — sem pré-visualização',
|
|
159
|
+
changesPreviewError: 'Falha ao ler a pré-visualização: {message}',
|
|
160
|
+
changesLens: 'Vista',
|
|
141
161
|
exited: 'O processo do terminal saiu',
|
|
142
162
|
noSession: 'Selecione uma conversa para usar a barra lateral',
|
|
143
163
|
pluginNotLoaded: 'Plugin não carregado; aba indisponível:',
|
package/src/client/locales-ru.ts
CHANGED
|
@@ -151,6 +151,26 @@ export const ru: Record<string, string> = {
|
|
|
151
151
|
produced: 'Результаты',
|
|
152
152
|
producedOpen: 'Открыть в боковой панели',
|
|
153
153
|
disconnected: 'Терминал отключён, переподключение…',
|
|
154
|
+
terminalWaitBanner: 'Агент ожидает {needle}',
|
|
155
|
+
terminalSkipWait: 'Пропустить ожидание',
|
|
156
|
+
gitFoldExpand: 'Показать {count} строк контекста',
|
|
157
|
+
gitFoldLoading: 'Развертывание…',
|
|
158
|
+
gitFoldFailed: 'Не удалось развернуть',
|
|
159
|
+
rename: 'Переименовать',
|
|
160
|
+
renameInvalid: 'Имя не может быть пустым или содержать разделители пути.',
|
|
161
|
+
delete: 'Удалить',
|
|
162
|
+
deleteTitle: 'Удалить «{name}»?',
|
|
163
|
+
deleteDescFile: 'Файл будет удалён безвозвратно. Действие необратимо.',
|
|
164
|
+
deleteDescDir: 'Каталог и всё его содержимое будут удалены безвозвратно. Действие необратимо.',
|
|
165
|
+
dismiss: 'Закрыть',
|
|
166
|
+
changesSessionGit: 'Изменения Git',
|
|
167
|
+
changesSessionLens: 'Изменения сессии',
|
|
168
|
+
changesEmpty: 'В этой сессии нет операций с файлами',
|
|
169
|
+
changesCount: 'Операций с файлами: {count}',
|
|
170
|
+
changesRedacted: '[СКРЫТО]',
|
|
171
|
+
changesBinary: 'Двоичный файл — предпросмотр недоступен',
|
|
172
|
+
changesPreviewError: 'Не удалось прочитать предпросмотр: {message}',
|
|
173
|
+
changesLens: 'Вид',
|
|
154
174
|
exited: 'Процесс терминала завершился',
|
|
155
175
|
noSession: 'Выберите сессию, чтобы использовать боковую панель',
|
|
156
176
|
pluginNotLoaded: 'Плагин не загружен — вкладка недоступна:',
|
package/src/client/locales-sv.ts
CHANGED
|
@@ -138,6 +138,26 @@ export const sv: Record<string, string> = {
|
|
|
138
138
|
produced: 'Producerat',
|
|
139
139
|
producedOpen: 'Öppna i sidopanelen',
|
|
140
140
|
disconnected: 'Terminalen frånkopplad, ansluter igen…',
|
|
141
|
+
terminalWaitBanner: 'Agenten väntar på {needle}',
|
|
142
|
+
terminalSkipWait: 'Hoppa över väntan',
|
|
143
|
+
gitFoldExpand: 'Visa {count} sammanhangsrader',
|
|
144
|
+
gitFoldLoading: 'Expanderar…',
|
|
145
|
+
gitFoldFailed: 'Kunde inte expandera',
|
|
146
|
+
rename: 'Byt namn',
|
|
147
|
+
renameInvalid: 'Namnet får inte vara tomt eller innehålla sökvägsavgränsare.',
|
|
148
|
+
delete: 'Ta bort',
|
|
149
|
+
deleteTitle: 'Ta bort "{name}"?',
|
|
150
|
+
deleteDescFile: 'Filen tas bort permanent. Detta kan inte ångras.',
|
|
151
|
+
deleteDescDir: 'Katalogen och allt i den tas bort permanent. Detta kan inte ångras.',
|
|
152
|
+
dismiss: 'Stäng',
|
|
153
|
+
changesSessionGit: 'Git-ändringar',
|
|
154
|
+
changesSessionLens: 'Sessionsändringar',
|
|
155
|
+
changesEmpty: 'Inga filåtgärder i den här sessionen',
|
|
156
|
+
changesCount: '{count} filåtgärder',
|
|
157
|
+
changesRedacted: '[SMETAT]',
|
|
158
|
+
changesBinary: 'Binärfil — ingen förhandsvisning',
|
|
159
|
+
changesPreviewError: 'Kunde inte läsa förhandsvisning: {message}',
|
|
160
|
+
changesLens: 'Vy',
|
|
141
161
|
exited: 'Terminalprocess avslutad',
|
|
142
162
|
noSession: 'Välj en konversation för att använda sidopanelen',
|
|
143
163
|
pluginNotLoaded: 'Plugin inte laddad; flik otillgänglig:',
|
package/src/client/locales-th.ts
CHANGED
|
@@ -155,6 +155,26 @@ export const th: Record<string, string> = {
|
|
|
155
155
|
produced: 'ผลลัพธ์ที่สร้าง',
|
|
156
156
|
producedOpen: 'เปิดในแถบด้านข้าง',
|
|
157
157
|
disconnected: 'เทอร์มินัลถูกตัดการเชื่อมต่อ กำลังเชื่อมต่อใหม่…',
|
|
158
|
+
terminalWaitBanner: 'เอเจนต์กำลังรอ {needle}',
|
|
159
|
+
terminalSkipWait: 'ข้ามการรอ',
|
|
160
|
+
gitFoldExpand: 'แสดงบริบท {count} บรรทัด',
|
|
161
|
+
gitFoldLoading: 'กำลังขยาย…',
|
|
162
|
+
gitFoldFailed: 'ขยายไม่สำเร็จ',
|
|
163
|
+
rename: 'เปลี่ยนชื่อ',
|
|
164
|
+
renameInvalid: 'ชื่อต้องไม่ว่างและไม่มีอักขระคั่นพาธ',
|
|
165
|
+
delete: 'ลบ',
|
|
166
|
+
deleteTitle: 'ลบ "{name}"?',
|
|
167
|
+
deleteDescFile: 'ไฟล์นี้จะถูกลบอย่างถาวร ย้อนกลับไม่ได้',
|
|
168
|
+
deleteDescDir: 'ไดเรกทอรีนี้และเนื้อหาทั้งหมดจะถูกลบอย่างถาวร ย้อนกลับไม่ได้',
|
|
169
|
+
dismiss: 'ปิด',
|
|
170
|
+
changesSessionGit: 'การเปลี่ยนแปลง Git',
|
|
171
|
+
changesSessionLens: 'การเปลี่ยนแปลงในเซสชัน',
|
|
172
|
+
changesEmpty: 'ไม่มีการดำเนินการไฟล์ในเซสชันนี้',
|
|
173
|
+
changesCount: 'การดำเนินการไฟล์ {count} รายการ',
|
|
174
|
+
changesRedacted: '[ถูกปกปิด]',
|
|
175
|
+
changesBinary: 'ไฟล์ไบนารี — ไม่มีตัวอย่าง',
|
|
176
|
+
changesPreviewError: 'อ่านตัวอย่างไม่สำเร็จ: {message}',
|
|
177
|
+
changesLens: 'มุมมอง',
|
|
158
178
|
exited: 'กระบวนการเทอร์มินัลออกแล้ว',
|
|
159
179
|
noSession: 'เลือกแชทเพื่อใช้แถบด้านข้าง',
|
|
160
180
|
pluginNotLoaded: 'ปลั๊กอินไม่ได้โหลด; tab ไม่พร้อมใช้งาน:',
|
package/src/client/locales-tr.ts
CHANGED
|
@@ -155,6 +155,26 @@ export const tr: Record<string, string> = {
|
|
|
155
155
|
produced: 'Üretilenler',
|
|
156
156
|
producedOpen: 'Kenar çubuğunda aç',
|
|
157
157
|
disconnected: 'Terminal bağlantısı kesildi, yeniden bağlanılıyor…',
|
|
158
|
+
terminalWaitBanner: 'Ajan {needle} için bekliyor',
|
|
159
|
+
terminalSkipWait: 'Beklemeyi atla',
|
|
160
|
+
gitFoldExpand: '{count} bağlam satırını göster',
|
|
161
|
+
gitFoldLoading: 'Genişletiliyor…',
|
|
162
|
+
gitFoldFailed: 'Genişletilemedi',
|
|
163
|
+
rename: 'Yeniden adlandır',
|
|
164
|
+
renameInvalid: 'Ad boş olamaz veya yol ayracı içeremez.',
|
|
165
|
+
delete: 'Sil',
|
|
166
|
+
deleteTitle: '"{name}" silinsin mi?',
|
|
167
|
+
deleteDescFile: 'Bu dosya kalıcı olarak silinir. Bu işlem geri alınamaz.',
|
|
168
|
+
deleteDescDir: 'Bu dizin ve içeriği kalıcı olarak silinir. Bu işlem geri alınamaz.',
|
|
169
|
+
dismiss: 'Kapat',
|
|
170
|
+
changesSessionGit: 'Git değişiklikleri',
|
|
171
|
+
changesSessionLens: 'Oturum değişiklikleri',
|
|
172
|
+
changesEmpty: 'Bu oturumda dosya işlemi yok',
|
|
173
|
+
changesCount: '{count} dosya işlemi',
|
|
174
|
+
changesRedacted: '[MASKELENDİ]',
|
|
175
|
+
changesBinary: 'İkili dosya — önizleme yok',
|
|
176
|
+
changesPreviewError: 'Önizleme okunamadı: {message}',
|
|
177
|
+
changesLens: 'Görünüm',
|
|
158
178
|
exited: 'Terminal süreci sonlandı',
|
|
159
179
|
noSession: 'Kenar çubuğunu kullanmak için bir oturum seçin',
|
|
160
180
|
pluginNotLoaded: 'Eklenti yüklenmedi; sekme kullanılamıyor:',
|
package/src/client/locales-vi.ts
CHANGED
|
@@ -155,6 +155,26 @@ export const vi: Record<string, string> = {
|
|
|
155
155
|
produced: 'Đã tạo',
|
|
156
156
|
producedOpen: 'Mở trong thanh bên',
|
|
157
157
|
disconnected: 'Terminal mất kết nối, đang kết nối lại…',
|
|
158
|
+
terminalWaitBanner: 'Agent đang chờ {needle}',
|
|
159
|
+
terminalSkipWait: 'Bỏ qua chờ',
|
|
160
|
+
gitFoldExpand: 'Hiện {count} dòng ngữ cảnh',
|
|
161
|
+
gitFoldLoading: 'Đang mở…',
|
|
162
|
+
gitFoldFailed: 'Không thể mở',
|
|
163
|
+
rename: 'Đổi tên',
|
|
164
|
+
renameInvalid: 'Tên không được trống hoặc chứa dấu phân cách đường dẫn.',
|
|
165
|
+
delete: 'Xóa',
|
|
166
|
+
deleteTitle: 'Xóa "{name}"?',
|
|
167
|
+
deleteDescFile: 'Thao tác này sẽ xóa vĩnh viễn tệp. Không thể hoàn tác.',
|
|
168
|
+
deleteDescDir: 'Thao tác này sẽ xóa vĩnh viễn thư mục và mọi thứ bên trong. Không thể hoàn tác.',
|
|
169
|
+
dismiss: 'Đóng',
|
|
170
|
+
changesSessionGit: 'Thay đổi Git',
|
|
171
|
+
changesSessionLens: 'Thay đổi phiên',
|
|
172
|
+
changesEmpty: 'Không có thao tác tệp trong phiên này',
|
|
173
|
+
changesCount: '{count} thao tác tệp',
|
|
174
|
+
changesRedacted: '[ĐÃ CHE]',
|
|
175
|
+
changesBinary: 'Tệp nhị phân — không có xem trước',
|
|
176
|
+
changesPreviewError: 'Lỗi đọc xem trước: {message}',
|
|
177
|
+
changesLens: 'Chế độ xem',
|
|
158
178
|
exited: 'Tiến trình terminal đã thoát',
|
|
159
179
|
noSession: 'Chọn một phiên để dùng thanh bên',
|
|
160
180
|
pluginNotLoaded: 'Plugin chưa tải, tab tạm không khả dụng:',
|
|
@@ -170,6 +170,26 @@ export const zhHK: Record<string, string> = {
|
|
|
170
170
|
produced: '本次產出',
|
|
171
171
|
producedOpen: '在側邊欄中開啟',
|
|
172
172
|
disconnected: '終端連線斷開,重新連線中…',
|
|
173
|
+
terminalWaitBanner: 'Agent 正在等待 {needle}',
|
|
174
|
+
terminalSkipWait: '跳過等待',
|
|
175
|
+
gitFoldExpand: '展開 {count} 行上下文',
|
|
176
|
+
gitFoldLoading: '展開中…',
|
|
177
|
+
gitFoldFailed: '展開失敗',
|
|
178
|
+
rename: '重新命名',
|
|
179
|
+
renameInvalid: '名稱不能為空,且不能包含路徑分隔符。',
|
|
180
|
+
delete: '刪除',
|
|
181
|
+
deleteTitle: '刪除「{name}」?',
|
|
182
|
+
deleteDescFile: '將永久刪除該檔案,此操作無法復原。',
|
|
183
|
+
deleteDescDir: '將永久刪除該目錄及其全部內容,此操作無法復原。',
|
|
184
|
+
dismiss: '關閉',
|
|
185
|
+
changesSessionGit: 'Git 變動',
|
|
186
|
+
changesSessionLens: '會話變動',
|
|
187
|
+
changesEmpty: '本會話尚無檔案操作',
|
|
188
|
+
changesCount: '{count} 個檔案操作',
|
|
189
|
+
changesRedacted: '[已脫敏]',
|
|
190
|
+
changesBinary: '二進位檔案,無法預覽',
|
|
191
|
+
changesPreviewError: '預覽讀取失敗:{message}',
|
|
192
|
+
changesLens: 'View',
|
|
173
193
|
exited: '終端程序已退出',
|
|
174
194
|
noSession: '選擇一個工作階段以使用側邊欄',
|
|
175
195
|
pluginNotLoaded: '插件未載入,標籤暫不可用:',
|
|
@@ -170,6 +170,26 @@ export const zhMO: Record<string, string> = {
|
|
|
170
170
|
produced: '本次產出',
|
|
171
171
|
producedOpen: '在側邊欄中開啟',
|
|
172
172
|
disconnected: '終端連線斷開,重新連線中…',
|
|
173
|
+
terminalWaitBanner: 'Agent 正在等待 {needle}',
|
|
174
|
+
terminalSkipWait: '跳過等待',
|
|
175
|
+
gitFoldExpand: '展開 {count} 行上下文',
|
|
176
|
+
gitFoldLoading: '展開中…',
|
|
177
|
+
gitFoldFailed: '展開失敗',
|
|
178
|
+
rename: '重新命名',
|
|
179
|
+
renameInvalid: '名稱不能為空,且不能包含路徑分隔符。',
|
|
180
|
+
delete: '刪除',
|
|
181
|
+
deleteTitle: '刪除「{name}」?',
|
|
182
|
+
deleteDescFile: '將永久刪除該檔案,此操作無法復原。',
|
|
183
|
+
deleteDescDir: '將永久刪除該目錄及其全部內容,此操作無法復原。',
|
|
184
|
+
dismiss: '關閉',
|
|
185
|
+
changesSessionGit: 'Git 變動',
|
|
186
|
+
changesSessionLens: '會話變動',
|
|
187
|
+
changesEmpty: '本會話尚無檔案操作',
|
|
188
|
+
changesCount: '{count} 個檔案操作',
|
|
189
|
+
changesRedacted: '[已脫敏]',
|
|
190
|
+
changesBinary: '二進位檔案,無法預覽',
|
|
191
|
+
changesPreviewError: '預覽讀取失敗:{message}',
|
|
192
|
+
changesLens: 'View',
|
|
173
193
|
exited: '終端程序已退出',
|
|
174
194
|
noSession: '選擇一個工作階段以使用側邊欄',
|
|
175
195
|
pluginNotLoaded: '插件未載入,標籤暫不可用:',
|
|
@@ -170,6 +170,26 @@ export const zhTW: Record<string, string> = {
|
|
|
170
170
|
produced: '本次產出',
|
|
171
171
|
producedOpen: '在側邊欄中開啟',
|
|
172
172
|
disconnected: '終端連線斷開,重新連線中…',
|
|
173
|
+
terminalWaitBanner: 'Agent 正在等待 {needle}',
|
|
174
|
+
terminalSkipWait: '跳過等待',
|
|
175
|
+
gitFoldExpand: '展開 {count} 行上下文',
|
|
176
|
+
gitFoldLoading: '展開中…',
|
|
177
|
+
gitFoldFailed: '展開失敗',
|
|
178
|
+
rename: '重新命名',
|
|
179
|
+
renameInvalid: '名稱不能為空,且不能包含路徑分隔符。',
|
|
180
|
+
delete: '刪除',
|
|
181
|
+
deleteTitle: '刪除「{name}」?',
|
|
182
|
+
deleteDescFile: '將永久刪除該檔案,此操作無法復原。',
|
|
183
|
+
deleteDescDir: '將永久刪除該目錄及其全部內容,此操作無法復原。',
|
|
184
|
+
dismiss: '關閉',
|
|
185
|
+
changesSessionGit: '偵測 Git 變動',
|
|
186
|
+
changesSessionLens: '會話變動',
|
|
187
|
+
changesEmpty: '本會話尚無檔案操作',
|
|
188
|
+
changesCount: '{count} 個檔案操作',
|
|
189
|
+
changesRedacted: '[已遮蔽]',
|
|
190
|
+
changesBinary: '二進位檔案,無法預覽',
|
|
191
|
+
changesPreviewError: '預覽讀取失敗:{message}',
|
|
192
|
+
changesLens: 'View',
|
|
173
193
|
exited: '終端程序已退出',
|
|
174
194
|
noSession: '選擇一個工作階段以使用側邊欄',
|
|
175
195
|
pluginNotLoaded: '插件未載入,標籤暫不可用:',
|
package/src/client/locales.ts
CHANGED
|
@@ -160,6 +160,26 @@ export const zh = {
|
|
|
160
160
|
producedOpen: '在侧边栏中打开',
|
|
161
161
|
showInFolder: '在文件夹中显示',
|
|
162
162
|
disconnected: '终端连接断开,重连中…',
|
|
163
|
+
terminalWaitBanner: 'Agent 正在等待 {needle}',
|
|
164
|
+
terminalSkipWait: '跳过等待',
|
|
165
|
+
gitFoldExpand: '展开 {count} 行上下文',
|
|
166
|
+
gitFoldLoading: '展开中…',
|
|
167
|
+
gitFoldFailed: '展开失败',
|
|
168
|
+
rename: '重命名',
|
|
169
|
+
renameInvalid: '名称不能为空,且不能包含路径分隔符。',
|
|
170
|
+
delete: '删除',
|
|
171
|
+
deleteTitle: '删除「{name}」?',
|
|
172
|
+
deleteDescFile: '将永久删除该文件,此操作不可撤销。',
|
|
173
|
+
deleteDescDir: '将永久删除该目录及其全部内容,此操作不可撤销。',
|
|
174
|
+
dismiss: '关闭',
|
|
175
|
+
changesSessionGit: 'Git 变动',
|
|
176
|
+
changesSessionLens: '会话变动',
|
|
177
|
+
changesEmpty: '本会话尚无文件操作',
|
|
178
|
+
changesCount: '{count} 个文件操作',
|
|
179
|
+
changesRedacted: '[已脱敏]',
|
|
180
|
+
changesBinary: '二进制文件,无法预览',
|
|
181
|
+
changesPreviewError: '预览读取失败:{message}',
|
|
182
|
+
changesLens: '视角',
|
|
163
183
|
exited: '终端进程已退出',
|
|
164
184
|
noSession: '选择一个会话以使用侧边栏',
|
|
165
185
|
pluginNotLoaded: '插件未加载,标签页暂不可用:',
|
|
@@ -497,6 +517,26 @@ export const en: Record<keyof typeof zh, string> = {
|
|
|
497
517
|
producedOpen: 'Open in sidebar',
|
|
498
518
|
showInFolder: 'Show in folder',
|
|
499
519
|
disconnected: 'Terminal disconnected, reconnecting…',
|
|
520
|
+
terminalWaitBanner: 'Agent is waiting for {needle}',
|
|
521
|
+
terminalSkipWait: 'Skip wait',
|
|
522
|
+
gitFoldExpand: 'Expand {count} context lines',
|
|
523
|
+
gitFoldLoading: 'Expanding…',
|
|
524
|
+
gitFoldFailed: 'Failed to expand',
|
|
525
|
+
rename: 'Rename',
|
|
526
|
+
renameInvalid: 'The name cannot be empty or contain path separators.',
|
|
527
|
+
delete: 'Delete',
|
|
528
|
+
deleteTitle: 'Delete "{name}"?',
|
|
529
|
+
deleteDescFile: 'This permanently deletes the file. This cannot be undone.',
|
|
530
|
+
deleteDescDir: 'This permanently deletes the directory and everything inside it. This cannot be undone.',
|
|
531
|
+
dismiss: 'Dismiss',
|
|
532
|
+
changesSessionGit: 'Git changes',
|
|
533
|
+
changesSessionLens: 'Session changes',
|
|
534
|
+
changesEmpty: 'No file operations in this session',
|
|
535
|
+
changesCount: '{count} file operations',
|
|
536
|
+
changesRedacted: '[REDACTED]',
|
|
537
|
+
changesBinary: 'Binary file — no preview',
|
|
538
|
+
changesPreviewError: 'Preview read failed: {message}',
|
|
539
|
+
changesLens: 'Lens',
|
|
500
540
|
exited: 'Terminal process exited',
|
|
501
541
|
noSession: 'Select a conversation to use the sidebar',
|
|
502
542
|
pluginNotLoaded: 'Plugin not loaded; tab unavailable:',
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redaction for the session lens' file preview: heuristic masking of
|
|
3
|
+
* credential-shaped strings (API keys, bearer tokens, private key blocks,
|
|
4
|
+
* password assignments) before file content is shown in the sidebar. This
|
|
5
|
+
* layer applies ONLY to the session lens' preview pane — ordinary file reads
|
|
6
|
+
* (editor, untracked diff fallback) never pass through it, so ordinary
|
|
7
|
+
* files' content is untouched.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** One redaction rule: a pattern plus the replacement it masks matches to. */
|
|
11
|
+
interface RedactRule {
|
|
12
|
+
pattern: RegExp
|
|
13
|
+
replacement: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The heuristics, applied in order; `$1` keeps the leading keyword where the
|
|
17
|
+
* rule matches an assignment shape. */
|
|
18
|
+
const RULES: RedactRule[] = [
|
|
19
|
+
// PEM private key blocks (the whole block is one secret).
|
|
20
|
+
{ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, replacement: '[REDACTED PRIVATE KEY]' },
|
|
21
|
+
// Known token shapes (sk- keys, GitHub tokens, AWS ids, Slack, Google).
|
|
22
|
+
{ pattern: /\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{16}|xox[abprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{30,})\b/g, replacement: '[REDACTED KEY]' },
|
|
23
|
+
// Authorization headers and bearer tokens.
|
|
24
|
+
{ pattern: /([Aa]uthorization\s*:\s*)[^\n"']{4,}/g, replacement: '$1[REDACTED]' },
|
|
25
|
+
{ pattern: /\b([Bb]earer\s+)[A-Za-z0-9._-]{16,}\b/g, replacement: '$1[REDACTED]' },
|
|
26
|
+
// Credential assignment shapes (password/secret/token/api-key = value).
|
|
27
|
+
{ pattern: /(\b[A-Za-z_-]*(?:password|passwd|secret|token|apikey|api_key)[A-Za-z_-]*(?:\s*[:=]\s*))['"]?[^\s"',;){]{3,}/gi, replacement: '$1[REDACTED]' },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mask credential-shaped strings in `text`. Best-effort by design: the goal
|
|
32
|
+
* is to keep the common accident (a key echoed into a file the model wrote)
|
|
33
|
+
* out of the sidebar, not to parse every secret format ever shipped.
|
|
34
|
+
*/
|
|
35
|
+
export function redactSecrets(text: string): string {
|
|
36
|
+
let out = text
|
|
37
|
+
for (const rule of RULES) out = out.replace(rule.pattern, rule.replacement)
|
|
38
|
+
return out
|
|
39
|
+
}
|
|
@@ -1651,6 +1651,189 @@ body[data-dsh-sidebar-dragging] .panel {
|
|
|
1651
1651
|
color: var(--dsw-alias-label-primary);
|
|
1652
1652
|
}
|
|
1653
1653
|
|
|
1654
|
+
/* A hunk-gap fold in a diff: the "expand hidden context" chip between two
|
|
1655
|
+
hunks (loading is inert, failure degrades to a static marker). */
|
|
1656
|
+
.gitFoldRow {
|
|
1657
|
+
display: block;
|
|
1658
|
+
width: 100%;
|
|
1659
|
+
box-sizing: border-box;
|
|
1660
|
+
padding: 2px 12px;
|
|
1661
|
+
border: none;
|
|
1662
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
1663
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1664
|
+
font: var(--dsw-font-xxxs-11);
|
|
1665
|
+
text-align: center;
|
|
1666
|
+
cursor: pointer;
|
|
1667
|
+
outline: none;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
.gitFoldRow:hover {
|
|
1671
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1672
|
+
color: var(--dsw-alias-label-secondary);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
.gitFoldRow:disabled {
|
|
1676
|
+
cursor: default;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.gitFoldRowFailed {
|
|
1680
|
+
color: var(--dsw-alias-state-error-primary);
|
|
1681
|
+
opacity: 0.7;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
/* The tree row's inline rename editor: fills the row's name slot, keeps the
|
|
1685
|
+
row's metrics so the tree does not jump when editing starts. */
|
|
1686
|
+
.explorerRenameInput {
|
|
1687
|
+
flex: 1;
|
|
1688
|
+
min-width: 0;
|
|
1689
|
+
height: 20px;
|
|
1690
|
+
padding: 0 4px;
|
|
1691
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
1692
|
+
border-radius: 4px;
|
|
1693
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
1694
|
+
color: var(--dsw-alias-label-primary);
|
|
1695
|
+
font: var(--dsw-font-xxs-12);
|
|
1696
|
+
outline: none;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
/* ── The unified changes tab's dual-lens toggle + session lens ─────────── */
|
|
1700
|
+
|
|
1701
|
+
.changesLensToggle {
|
|
1702
|
+
flex: none;
|
|
1703
|
+
display: flex;
|
|
1704
|
+
gap: 2px;
|
|
1705
|
+
padding: 2px;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.changesLensTab {
|
|
1709
|
+
flex: 1;
|
|
1710
|
+
height: 24px;
|
|
1711
|
+
border: none;
|
|
1712
|
+
border-radius: 6px;
|
|
1713
|
+
background: transparent;
|
|
1714
|
+
color: var(--dsw-alias-label-secondary);
|
|
1715
|
+
font: var(--dsw-font-xxxs-strong-11);
|
|
1716
|
+
cursor: pointer;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
.changesLensTab:hover {
|
|
1720
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
.changesLensTabActive,
|
|
1724
|
+
.changesLensTabActive:hover {
|
|
1725
|
+
background: var(--dsw-alias-interactive-bg-active);
|
|
1726
|
+
color: var(--dsw-alias-label-primary);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
.sessionLens {
|
|
1730
|
+
flex: 1;
|
|
1731
|
+
min-height: 0;
|
|
1732
|
+
overflow-y: auto;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
.sessionLensBar {
|
|
1736
|
+
display: flex;
|
|
1737
|
+
align-items: center;
|
|
1738
|
+
gap: 6px;
|
|
1739
|
+
height: 26px;
|
|
1740
|
+
padding: 0 4px;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
.sessionLensCount {
|
|
1744
|
+
flex: 1;
|
|
1745
|
+
min-width: 0;
|
|
1746
|
+
font: var(--dsw-font-xxxs-11);
|
|
1747
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.sessionLensEmpty {
|
|
1751
|
+
padding: 16px;
|
|
1752
|
+
font: var(--dsw-font-xxs-12);
|
|
1753
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1754
|
+
text-align: center;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
.sessionLensItem {
|
|
1758
|
+
border-bottom: 1px solid var(--dsw-alias-border-l1);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
.sessionLensRow {
|
|
1762
|
+
display: flex;
|
|
1763
|
+
align-items: center;
|
|
1764
|
+
gap: 8px;
|
|
1765
|
+
width: 100%;
|
|
1766
|
+
box-sizing: border-box;
|
|
1767
|
+
min-height: 30px;
|
|
1768
|
+
padding: 4px 10px;
|
|
1769
|
+
border: none;
|
|
1770
|
+
border-radius: 6px;
|
|
1771
|
+
background: transparent;
|
|
1772
|
+
color: var(--dsw-alias-label-primary);
|
|
1773
|
+
text-align: left;
|
|
1774
|
+
cursor: pointer;
|
|
1775
|
+
outline: none;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
.sessionLensRow:hover {
|
|
1779
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
.sessionLensPath {
|
|
1783
|
+
flex: 1;
|
|
1784
|
+
min-width: 0;
|
|
1785
|
+
overflow: hidden;
|
|
1786
|
+
text-overflow: ellipsis;
|
|
1787
|
+
white-space: nowrap;
|
|
1788
|
+
direction: rtl;
|
|
1789
|
+
text-align: left;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
.sessionLensMeta {
|
|
1793
|
+
flex: none;
|
|
1794
|
+
font: var(--dsw-font-xxxs-11);
|
|
1795
|
+
color: var(--dsw-alias-label-tertiary);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
.sessionLensPreview {
|
|
1799
|
+
max-height: 260px;
|
|
1800
|
+
overflow: auto;
|
|
1801
|
+
margin: 0 8px 6px;
|
|
1802
|
+
padding: 6px 8px;
|
|
1803
|
+
border: 1px solid var(--dsw-alias-border-l1);
|
|
1804
|
+
border-radius: 6px;
|
|
1805
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
1806
|
+
font-family: var(--ds-font-family-code);
|
|
1807
|
+
font-size: var(--dsw-font-xxxs-11-font-size);
|
|
1808
|
+
line-height: 1.5;
|
|
1809
|
+
color: var(--dsw-alias-label-primary);
|
|
1810
|
+
white-space: pre-wrap;
|
|
1811
|
+
word-break: break-word;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
/* The "Agent 正在等待 {needle}" wait banner: a warn-toned strip above the
|
|
1815
|
+
terminal surface while the model blocks in terminal_wait_for; the needle
|
|
1816
|
+
ellipsizes inline and the full text rides the title tooltip. */
|
|
1817
|
+
.terminalWaitBanner {
|
|
1818
|
+
flex: none;
|
|
1819
|
+
display: flex;
|
|
1820
|
+
align-items: center;
|
|
1821
|
+
gap: 8px;
|
|
1822
|
+
padding: 3px 10px;
|
|
1823
|
+
font: var(--dsw-font-xxxs-11);
|
|
1824
|
+
color: var(--dsw-alias-state-warn-label);
|
|
1825
|
+
background: var(--dsw-alias-state-warn-tertiary);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
.terminalWaitNeedle {
|
|
1829
|
+
flex: 1;
|
|
1830
|
+
min-width: 0;
|
|
1831
|
+
overflow: hidden;
|
|
1832
|
+
text-overflow: ellipsis;
|
|
1833
|
+
white-space: nowrap;
|
|
1834
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1654
1837
|
/* The node-pty dependency failure banner (issue #140): a taller warn block
|
|
1655
1838
|
carrying the pasteable repair command (bash / cmd / PowerShell). */
|
|
1656
1839
|
.terminalDepsBanner {
|
package/src/client/state.ts
CHANGED
|
@@ -104,6 +104,14 @@ export interface SidebarState {
|
|
|
104
104
|
splits: SplitNode
|
|
105
105
|
/** Free windows (tabs dragged out onto the conversation area). */
|
|
106
106
|
floats: FloatWindow[]
|
|
107
|
+
/**
|
|
108
|
+
* Live agent-terminal wait state (uuid → the wait the model currently
|
|
109
|
+
* blocks on in `terminal_wait_for`), mirrored from the host's
|
|
110
|
+
* agent-terminals push. Transient by design: sanitizeState never restores
|
|
111
|
+
* it, so a reload starts clean and the next push (sent immediately on WS
|
|
112
|
+
* attach) repopulates it.
|
|
113
|
+
*/
|
|
114
|
+
agentWaits: Record<string, { needle: string; since: number }>
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
export const PANEL_MIN = 280
|
|
@@ -209,6 +217,7 @@ export function makeDefaultState(width = PANEL_DEFAULT, panelOpen = true, seed:
|
|
|
209
217
|
revealed: [],
|
|
210
218
|
splits: leaf,
|
|
211
219
|
floats: [],
|
|
220
|
+
agentWaits: {},
|
|
212
221
|
}
|
|
213
222
|
}
|
|
214
223
|
|
|
@@ -854,6 +863,23 @@ export function agentTabId(uuid: string): string {
|
|
|
854
863
|
return `${AGENT_TAB_PREFIX}${uuid}`
|
|
855
864
|
}
|
|
856
865
|
|
|
866
|
+
/** Shallow equality of two agent-wait maps (same keys, same needle+since). */
|
|
867
|
+
function sameAgentWaits(
|
|
868
|
+
a: SidebarState['agentWaits'] | undefined,
|
|
869
|
+
b: Record<string, { needle: string; since: number }>,
|
|
870
|
+
): boolean {
|
|
871
|
+
if (a === undefined) return Object.keys(b).length === 0
|
|
872
|
+
const aKeys = Object.keys(a)
|
|
873
|
+
if (aKeys.length !== Object.keys(b).length) return false
|
|
874
|
+
for (const key of aKeys) {
|
|
875
|
+
const av = a[key]
|
|
876
|
+
const bv = b[key]
|
|
877
|
+
if (av === undefined || bv === undefined) return false
|
|
878
|
+
if (av.needle !== bv.needle || av.since !== bv.since) return false
|
|
879
|
+
}
|
|
880
|
+
return true
|
|
881
|
+
}
|
|
882
|
+
|
|
857
883
|
/**
|
|
858
884
|
* Reconcile the sidebar's agent-terminal tabs with the host's live list.
|
|
859
885
|
* The host pushes the current list of agent terminals (created by the model
|
|
@@ -868,7 +894,7 @@ export function agentTabId(uuid: string): string {
|
|
|
868
894
|
*/
|
|
869
895
|
export function reconcileAgentTerminals(
|
|
870
896
|
state: SidebarState,
|
|
871
|
-
agentTerminals: ReadonlyArray<{ uuid: string; title: string }>,
|
|
897
|
+
agentTerminals: ReadonlyArray<{ uuid: string; title: string; waiting?: { needle: string; since: number } | null }>,
|
|
872
898
|
): SidebarState {
|
|
873
899
|
const existingTabs = allLeaves(state.splits).flatMap(leaf => leaf.tabs)
|
|
874
900
|
.concat(state.floats.map(float => float.tab))
|
|
@@ -883,7 +909,17 @@ export function reconcileAgentTerminals(
|
|
|
883
909
|
// convergence: no title suffix, no meta write — the tab keeps its uuid
|
|
884
910
|
// so a later reconcile push revives it if the agent reopens the same one).
|
|
885
911
|
const toRemove = existingAgentTabs.filter(tab => !serverUuids.has(agentUuidOf(tab.id)) && tab.pin === undefined)
|
|
886
|
-
|
|
912
|
+
// Mirror the live wait state from the push (authoritative: a vanished
|
|
913
|
+
// waiting field simply drops the entry). A waits-only change must still
|
|
914
|
+
// produce a new state — the tab add/remove no-change check alone would
|
|
915
|
+
// swallow banner updates.
|
|
916
|
+
const serverWaits: Record<string, { needle: string; since: number }> = {}
|
|
917
|
+
for (const terminal of agentTerminals) {
|
|
918
|
+
if (terminal.waiting !== undefined && terminal.waiting !== null) {
|
|
919
|
+
serverWaits[terminal.uuid] = { needle: terminal.waiting.needle, since: terminal.waiting.since }
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
if (toAdd.length === 0 && toRemove.length === 0 && sameAgentWaits(state.agentWaits, serverWaits)) return state
|
|
887
923
|
// Remove tabs whose uuids vanished from the server list (the agent closed
|
|
888
924
|
// them, or the pty exited and was reaped). Reuse closeTab's leaf cleanup;
|
|
889
925
|
// a FLOATED agent terminal leaves with its window.
|
|
@@ -900,7 +936,7 @@ export function reconcileAgentTerminals(
|
|
|
900
936
|
}
|
|
901
937
|
// Add tabs for new uuids (the agent created a terminal). They land in the
|
|
902
938
|
// active pane via openTabInActivePane; the next reconcile is a no-op for them.
|
|
903
|
-
let next: SidebarState = { ...state, splits, floats }
|
|
939
|
+
let next: SidebarState = { ...state, splits, floats, agentWaits: serverWaits }
|
|
904
940
|
for (const terminal of toAdd) {
|
|
905
941
|
const tab: SidebarTab = {
|
|
906
942
|
id: agentTabId(terminal.uuid),
|
|
@@ -1150,6 +1186,9 @@ export function sanitizeState(parsed: unknown): SidebarState | undefined {
|
|
|
1150
1186
|
revealed: [],
|
|
1151
1187
|
splits: migratedSplits,
|
|
1152
1188
|
floats,
|
|
1189
|
+
// Live wait state is transient: a reload starts clean; the next
|
|
1190
|
+
// agent-terminals push (sent on WS attach) repopulates it.
|
|
1191
|
+
agentWaits: {},
|
|
1153
1192
|
}
|
|
1154
1193
|
}
|
|
1155
1194
|
|