dsh-better-sidebar 0.10.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/LICENSE +21 -0
- package/README.md +139 -0
- package/README_EN.md +139 -0
- package/lib/client-docx.js +7649 -0
- package/lib/client-editor.js +32677 -0
- package/lib/client-pptx.js +63438 -0
- package/lib/client-registry.js +8021 -0
- package/lib/client-terminal.js +8060 -0
- package/lib/client-xlsx.js +359755 -0
- package/lib/client.js +8021 -0
- package/lib/index.js +2604 -0
- package/lib/invariant.js +22 -0
- package/lib/types/agent-pty.d.ts +220 -0
- package/lib/types/browser-probe.d.ts +15 -0
- package/lib/types/bundle-route.d.ts +13 -0
- package/lib/types/client/BrowserView.d.ts +22 -0
- package/lib/types/client/DiffTab.d.ts +6 -0
- package/lib/types/client/DiffView.d.ts +51 -0
- package/lib/types/client/EditorHost.d.ts +10 -0
- package/lib/types/client/ExplorerView.d.ts +9 -0
- package/lib/types/client/GitView.d.ts +8 -0
- package/lib/types/client/OrphanedTab.d.ts +11 -0
- package/lib/types/client/PdfView.d.ts +6 -0
- package/lib/types/client/PptxView.d.ts +6 -0
- package/lib/types/client/SandboxStatusBar.d.ts +10 -0
- package/lib/types/client/SideCardSection.d.ts +27 -0
- package/lib/types/client/Sidebar.d.ts +6 -0
- package/lib/types/client/SubagentView.d.ts +16 -0
- package/lib/types/client/TabBar.d.ts +38 -0
- package/lib/types/client/TerminalView.d.ts +8 -0
- package/lib/types/client/TextEditor.d.ts +10 -0
- package/lib/types/client/api.d.ts +170 -0
- package/lib/types/client/binary-download.d.ts +12 -0
- package/lib/types/client/breakpoints.d.ts +12 -0
- package/lib/types/client/browser.d.ts +50 -0
- package/lib/types/client/builtins/index.d.ts +17 -0
- package/lib/types/client/builtins/tabs.d.ts +6 -0
- package/lib/types/client/builtins/viewers.d.ts +3 -0
- package/lib/types/client/chunk-loader.d.ts +74 -0
- package/lib/types/client/chunks/docx.d.ts +8 -0
- package/lib/types/client/chunks/editor.d.ts +9 -0
- package/lib/types/client/chunks/pptx.d.ts +8 -0
- package/lib/types/client/chunks/terminal.d.ts +9 -0
- package/lib/types/client/chunks/xlsx.d.ts +9 -0
- package/lib/types/client/cm-themes.d.ts +24 -0
- package/lib/types/client/conversation-draft.d.ts +14 -0
- package/lib/types/client/docx-view.d.ts +8 -0
- package/lib/types/client/editor-load.d.ts +66 -0
- package/lib/types/client/icons.d.ts +47 -0
- package/lib/types/client/image-types.d.ts +3 -0
- package/lib/types/client/ime-guard.d.ts +36 -0
- package/lib/types/client/index.d.ts +10 -0
- package/lib/types/client/intercept.d.ts +20 -0
- package/lib/types/client/lang.d.ts +12 -0
- package/lib/types/client/lazy-chunk.d.ts +25 -0
- package/lib/types/client/link-intercept.d.ts +35 -0
- package/lib/types/client/locales.d.ts +237 -0
- package/lib/types/client/office-shared.d.ts +33 -0
- package/lib/types/client/office-types.d.ts +36 -0
- package/lib/types/client/office-view.d.ts +12 -0
- package/lib/types/client/openpath-intercept.d.ts +41 -0
- package/lib/types/client/paths.d.ts +19 -0
- package/lib/types/client/pdf-types.d.ts +2 -0
- package/lib/types/client/prefs.d.ts +30 -0
- package/lib/types/client/produced-files.d.ts +27 -0
- package/lib/types/client/selection-payload.d.ts +27 -0
- package/lib/types/client/service.d.ts +214 -0
- package/lib/types/client/split-pane.d.ts +29 -0
- package/lib/types/client/state.d.ts +322 -0
- package/lib/types/client/subagent-activity.d.ts +35 -0
- package/lib/types/client/subagent-detect.d.ts +47 -0
- package/lib/types/client/subagent-jobs.d.ts +63 -0
- package/lib/types/client/theme.d.ts +28 -0
- package/lib/types/client/xlsx-to-univer.d.ts +33 -0
- package/lib/types/client/xlsx-view.d.ts +10 -0
- package/lib/types/config.d.ts +41 -0
- package/lib/types/context-types.d.ts +431 -0
- package/lib/types/fs-tree.d.ts +41 -0
- package/lib/types/git.d.ts +75 -0
- package/lib/types/html-route.d.ts +48 -0
- package/lib/types/index.d.ts +39 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/jobs-routes.d.ts +45 -0
- package/lib/types/prefs-shared.d.ts +106 -0
- package/lib/types/pty-manager.d.ts +76 -0
- package/lib/types/tools.d.ts +28 -0
- package/lib/types/trust-fence.d.ts +24 -0
- package/lib/types/wire.d.ts +38 -0
- package/package.json +168 -0
- package/src/agent-pty.ts +519 -0
- package/src/browser-probe.ts +26 -0
- package/src/bundle-route.ts +130 -0
- package/src/client/BrowserView.tsx +253 -0
- package/src/client/DiffTab.tsx +110 -0
- package/src/client/DiffView.tsx +266 -0
- package/src/client/EditorHost.tsx +101 -0
- package/src/client/ExplorerView.tsx +283 -0
- package/src/client/GitView.tsx +540 -0
- package/src/client/OrphanedTab.tsx +27 -0
- package/src/client/PdfView.tsx +110 -0
- package/src/client/PptxView.tsx +131 -0
- package/src/client/SandboxStatusBar.tsx +60 -0
- package/src/client/SideCardSection.module.css +271 -0
- package/src/client/SideCardSection.tsx +426 -0
- package/src/client/Sidebar.tsx +861 -0
- package/src/client/SubagentView.module.css +547 -0
- package/src/client/SubagentView.tsx +866 -0
- package/src/client/TabBar.tsx +188 -0
- package/src/client/TerminalView.tsx +233 -0
- package/src/client/TextEditor.tsx +382 -0
- package/src/client/api.ts +215 -0
- package/src/client/binary-download.tsx +23 -0
- package/src/client/breakpoints.ts +51 -0
- package/src/client/browser.ts +119 -0
- package/src/client/builtins/index.ts +33 -0
- package/src/client/builtins/tabs.tsx +211 -0
- package/src/client/builtins/viewers.tsx +151 -0
- package/src/client/chunk-loader.ts +196 -0
- package/src/client/chunks/docx.tsx +8 -0
- package/src/client/chunks/editor.tsx +9 -0
- package/src/client/chunks/pptx.tsx +8 -0
- package/src/client/chunks/terminal.tsx +9 -0
- package/src/client/chunks/xlsx.tsx +9 -0
- package/src/client/cm-themes.ts +129 -0
- package/src/client/conversation-draft.ts +29 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/docx-view.tsx +113 -0
- package/src/client/editor-load.ts +92 -0
- package/src/client/icons.tsx +137 -0
- package/src/client/image-types.ts +8 -0
- package/src/client/ime-guard.ts +47 -0
- package/src/client/index.tsx +238 -0
- package/src/client/intercept.tsx +94 -0
- package/src/client/lang.ts +95 -0
- package/src/client/layout.css +54 -0
- package/src/client/lazy-chunk.tsx +89 -0
- package/src/client/link-intercept.ts +66 -0
- package/src/client/locales.ts +476 -0
- package/src/client/office-shared.tsx +40 -0
- package/src/client/office-types.ts +48 -0
- package/src/client/office-view.tsx +12 -0
- package/src/client/openpath-intercept.ts +62 -0
- package/src/client/paths.ts +28 -0
- package/src/client/pdf-types.ts +4 -0
- package/src/client/prefs.ts +100 -0
- package/src/client/produced-files.ts +87 -0
- package/src/client/selection-payload.ts +86 -0
- package/src/client/service.ts +434 -0
- package/src/client/sidebar.module.css +1917 -0
- package/src/client/split-pane.tsx +292 -0
- package/src/client/state.ts +1048 -0
- package/src/client/subagent-activity.ts +67 -0
- package/src/client/subagent-detect.ts +125 -0
- package/src/client/subagent-jobs.ts +161 -0
- package/src/client/theme.ts +44 -0
- package/src/client/xlsx-to-univer.ts +260 -0
- package/src/client/xlsx-view.tsx +116 -0
- package/src/config.ts +95 -0
- package/src/context-types.ts +445 -0
- package/src/fs-tree.ts +115 -0
- package/src/git.ts +236 -0
- package/src/html-route.ts +70 -0
- package/src/index.ts +897 -0
- package/src/invariant.ts +32 -0
- package/src/jobs-routes.ts +257 -0
- package/src/prefs-shared.ts +127 -0
- package/src/pty-manager.ts +199 -0
- package/src/tools.ts +477 -0
- package/src/trust-fence.ts +77 -0
- package/src/wire.ts +94 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal zh/en copy for the sidebar. The copy follows the DSH i18n system:
|
|
3
|
+
* the client apply attaches the locale service (`ctx.locale`, provided by
|
|
4
|
+
* `@deepseek-ai/dsh-client-locale`) through {@link attachLocale}, and
|
|
5
|
+
* `t()`/`isZh()` resolve the active locale from it — the Host-backed
|
|
6
|
+
* `locale.preference` wins over the raw browser language and switches live.
|
|
7
|
+
* Without an attached service (standalone/test compositions) the browser
|
|
8
|
+
* language is used, matching the previous behavior. The dictionaries are
|
|
9
|
+
* also registered into the DSH locale registry under {@link LOCALE_NS}.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** The zh dictionary (also registered into the DSH locale registry under {@link LOCALE_NS}). */
|
|
13
|
+
export const zh = {
|
|
14
|
+
explorer: '资源管理器',
|
|
15
|
+
git: '源代码管理',
|
|
16
|
+
terminal: '终端',
|
|
17
|
+
editor: '编辑器',
|
|
18
|
+
newTab: '新建标签页',
|
|
19
|
+
openExplorer: '资源管理器',
|
|
20
|
+
openGit: 'Git 面板',
|
|
21
|
+
newTerminal: '新终端',
|
|
22
|
+
terminalLimit: '终端数量已达上限 (3)',
|
|
23
|
+
close: '关闭',
|
|
24
|
+
collapse: '折叠侧边栏',
|
|
25
|
+
expand: '展开侧边栏',
|
|
26
|
+
collapseBottomPanel: '折叠底部面板',
|
|
27
|
+
expandBottomPanel: '展开底部面板',
|
|
28
|
+
terminalError: '终端连接失败',
|
|
29
|
+
terminalConnectFailed: '终端多次连接失败',
|
|
30
|
+
terminalRetry: '重试',
|
|
31
|
+
preview: '预览',
|
|
32
|
+
edit: '编辑',
|
|
33
|
+
refresh: '刷新',
|
|
34
|
+
save: '保存',
|
|
35
|
+
saved: '已保存',
|
|
36
|
+
unsaved: '未保存',
|
|
37
|
+
saveFailed: '保存失败',
|
|
38
|
+
truncation: '文件过大,仅显示前 512KB',
|
|
39
|
+
binary: '二进制文件,无法预览',
|
|
40
|
+
loading: '加载中…',
|
|
41
|
+
error: '加载失败',
|
|
42
|
+
retry: '重试',
|
|
43
|
+
splitLeft: '向左分栏',
|
|
44
|
+
splitRight: '向右分栏',
|
|
45
|
+
splitUp: '向上分栏',
|
|
46
|
+
splitDown: '向下分栏',
|
|
47
|
+
notRepo: '当前目录不是 git 仓库',
|
|
48
|
+
noChanges: '没有变更',
|
|
49
|
+
stage: '暂存',
|
|
50
|
+
unstage: '取消暂存',
|
|
51
|
+
stageAll: '全部暂存',
|
|
52
|
+
unstageAll: '全部取消暂存',
|
|
53
|
+
commitPlaceholder: '提交信息 (Ctrl+Enter)',
|
|
54
|
+
commit: '提交',
|
|
55
|
+
commitError: '提交失败',
|
|
56
|
+
branch: '分支',
|
|
57
|
+
checkoutError: '切换分支失败',
|
|
58
|
+
history: '历史',
|
|
59
|
+
changes: '变更',
|
|
60
|
+
staged: '已暂存',
|
|
61
|
+
unstaged: '未暂存',
|
|
62
|
+
cancel: '取消',
|
|
63
|
+
diffEmpty: '没有文本差异',
|
|
64
|
+
diffLoadError: '加载差异失败',
|
|
65
|
+
diffBinary: '二进制',
|
|
66
|
+
diffAdded: '新增',
|
|
67
|
+
diffDeleted: '删除',
|
|
68
|
+
diffRenamed: '重命名',
|
|
69
|
+
diffExpand: '展开其余 {count} 行',
|
|
70
|
+
diffCollapse: '收起',
|
|
71
|
+
discard: '放弃更改',
|
|
72
|
+
discardTitle: '放弃更改',
|
|
73
|
+
discardDesc: '将丢弃「{path}」的工作区修改(不可恢复)。',
|
|
74
|
+
viewCommitDiff: '查看提交差异',
|
|
75
|
+
copyShortHash: '复制短哈希',
|
|
76
|
+
copyFullHash: '复制完整哈希',
|
|
77
|
+
copySubject: '复制提交信息',
|
|
78
|
+
revertCommit: '还原此提交',
|
|
79
|
+
revertTitle: '还原此提交',
|
|
80
|
+
revertDesc: '将在当前分支创建一个反转「{subject}」的新提交。',
|
|
81
|
+
cherryPickCommit: '捡取此提交',
|
|
82
|
+
cherryPickTitle: '捡取此提交',
|
|
83
|
+
cherryPickDesc: '将「{subject}」的更改应用到当前分支。',
|
|
84
|
+
timeJustNow: '刚刚',
|
|
85
|
+
timeMinutesAgo: '{n} 分钟前',
|
|
86
|
+
timeHoursAgo: '{n} 小时前',
|
|
87
|
+
timeYesterday: '昨天',
|
|
88
|
+
loadMore: '加载更多',
|
|
89
|
+
historyLoadError: '加载更多历史失败',
|
|
90
|
+
produced: '本次产出',
|
|
91
|
+
producedOpen: '在侧边栏中打开',
|
|
92
|
+
disconnected: '终端连接断开,重连中…',
|
|
93
|
+
exited: '终端进程已退出',
|
|
94
|
+
noSession: '选择一个会话以使用侧边栏',
|
|
95
|
+
pluginNotLoaded: '插件未加载,标签页暂不可用:',
|
|
96
|
+
hiddenFiles: '隐藏文件',
|
|
97
|
+
parent: '上级目录',
|
|
98
|
+
copied: '已复制',
|
|
99
|
+
copy: '复制',
|
|
100
|
+
newFile: '新文件',
|
|
101
|
+
openEditor: '打开编辑器',
|
|
102
|
+
gitDetail: '查看变更详情',
|
|
103
|
+
referenceFile: '@文件',
|
|
104
|
+
addToConversation: '添加到对话',
|
|
105
|
+
copyRelative: '复制相对地址',
|
|
106
|
+
copyAbsolute: '复制绝对地址',
|
|
107
|
+
download: '下载',
|
|
108
|
+
settingsNav: '侧边卡片',
|
|
109
|
+
settingsOpenTitle: '新会话默认打开',
|
|
110
|
+
settingsOpenDesc: '新建会话时自动展开侧边卡片;已存在的会话保持各自布局',
|
|
111
|
+
settingsWidthTitle: '默认宽度占比',
|
|
112
|
+
settingsWidthDesc: '新建会话时侧边卡片占窗口宽度的百分比 (20–60)',
|
|
113
|
+
settingsWidthSuffix: '%',
|
|
114
|
+
settingsOpenPathTitle: '聊天区文件在侧边栏打开',
|
|
115
|
+
settingsOpenPathDesc: '在聊天里点击文件链接(工具行、产物列表、文件提及)时,在侧边栏编辑器中打开,不再调用系统默认应用',
|
|
116
|
+
settingsSaveFailed: '保存失败',
|
|
117
|
+
settingsConflict: '设置已被其他窗口修改,请重试',
|
|
118
|
+
binaryNoPreview: '此文件类型不支持预览',
|
|
119
|
+
downloadToView: '下载查看',
|
|
120
|
+
officeTooLarge: '文件过大,无法预览',
|
|
121
|
+
officeCorrupt: '文件损坏或格式无效',
|
|
122
|
+
officeEncrypted: '不支持加密文件',
|
|
123
|
+
officeLoadFailed: 'Office 预览组件加载失败',
|
|
124
|
+
previousSlide: '上一页',
|
|
125
|
+
nextSlide: '下一页',
|
|
126
|
+
zoom: '缩放',
|
|
127
|
+
zoomHint: 'Alt + 滚轮',
|
|
128
|
+
settingsSubagentTitle: '检测到子代理时自动展开任务管理页',
|
|
129
|
+
settingsSubagentDesc: '当前会话产生新的子代理时,自动展开侧边栏并打开任务管理页;关闭后需手动打开',
|
|
130
|
+
settingsJobsTitle: '有新后台任务时自动展开后台任务页',
|
|
131
|
+
settingsJobsDesc: '当前会话出现新的后台任务时,自动展开侧边栏并打开后台任务页(每个新任务都会触发);关闭后需手动打开',
|
|
132
|
+
settingsToolsTitle: '为模型注入终端工具',
|
|
133
|
+
settingsToolsDesc: '开启后,模型可通过 terminal_create 等 8 个工具创建并操作侧边栏终端(默认关闭)',
|
|
134
|
+
settingsBottomTerminalTitle: '底部面板首次展开自动开终端',
|
|
135
|
+
settingsBottomTerminalDesc: '每次会话中第一次展开底部面板时,尝试在底部面板自动打开一个新终端标签(终端数量上限仍会限制;默认开启)',
|
|
136
|
+
settingsTabsTitle: '侧边栏内容',
|
|
137
|
+
settingsViewersTitle: '文件预览',
|
|
138
|
+
settingsGeneralTitle: '常规',
|
|
139
|
+
settingsPopup: '功能设置',
|
|
140
|
+
settingsViewerCatchAll: '兜底:任意文件',
|
|
141
|
+
viewerImage: '图片',
|
|
142
|
+
viewerPdf: 'PDF',
|
|
143
|
+
viewerDocx: 'Word 文档',
|
|
144
|
+
viewerXlsx: 'Excel 表格',
|
|
145
|
+
viewerPptx: 'PPT 演示',
|
|
146
|
+
viewerMarkdown: 'Markdown',
|
|
147
|
+
viewerCode: '代码',
|
|
148
|
+
viewerBinary: '二进制下载',
|
|
149
|
+
viewerHtml: 'HTML',
|
|
150
|
+
browser: '浏览器',
|
|
151
|
+
browserPlaceholder: '输入网址,例如 example.com',
|
|
152
|
+
browserGo: '前往',
|
|
153
|
+
browserBack: '后退',
|
|
154
|
+
browserForward: '前进',
|
|
155
|
+
browserStart: '输入网址开始浏览(沙箱模式)',
|
|
156
|
+
browserBlockedScheme: '已阻止:仅支持 http/https 链接',
|
|
157
|
+
browserBlockedLoopback: '已阻止:不允许在浏览器中访问本机或内部地址',
|
|
158
|
+
browserInvalid: '无效的网址',
|
|
159
|
+
browserNoSandboxWarning: '沙箱已关闭:当前页面与界面同源,拥有完整会话权限(可在设置中恢复)',
|
|
160
|
+
htmlNoSandboxWarning: '沙箱已关闭:此 HTML 与界面同源,可读取会话文件与内部接口(可在设置中恢复)',
|
|
161
|
+
sandboxStatusOn: '沙箱模式:已启用 · 页面无法访问界面数据与本地文件,登录态与第三方 Cookie 可能不可用',
|
|
162
|
+
sandboxUnlock: '临时解锁(不安全)',
|
|
163
|
+
sandboxRestore: '恢复沙箱',
|
|
164
|
+
settingsHtmlDefaultUnsafeTitle: 'HTML 预览默认以非沙箱模式打开(不安全)',
|
|
165
|
+
settingsHtmlDefaultUnsafeDesc: '开启后,每次打开 HTML 文件时预览默认处于非沙箱状态(与界面同源,可读取会话文件与内部接口);可在状态行临时恢复沙箱',
|
|
166
|
+
settingsHtmlSandboxTitle: '关闭 HTML 预览沙箱(不安全)',
|
|
167
|
+
settingsHtmlSandboxDesc: '关闭后,预览的 HTML 将与界面同源运行,可读取会话文件、本地存储并调用内部接口。仅对完全可信的文件开启',
|
|
168
|
+
settingsBrowserSandboxTitle: '关闭浏览器沙箱(不安全)',
|
|
169
|
+
settingsBrowserSandboxDesc: '关闭后,访问的任何网站都将与界面同源运行,可读取会话数据并冒充你的登录状态。仅对完全可信的站点开启',
|
|
170
|
+
settingsBrowserLinksTitle: '聊天区外链在侧边栏打开',
|
|
171
|
+
settingsBrowserLinksDesc: '开启后,点击聊天或界面中的 http/https 外链时在侧边栏浏览器中打开,不再弹出新窗口;Ctrl/Cmd 点击可临时放行',
|
|
172
|
+
browserOpenExternal: '在浏览器中打开',
|
|
173
|
+
browserEmbedBlocked: '{host} 拒绝了嵌入请求',
|
|
174
|
+
browserEmbedBlockedDesc: '该站点通过 X-Frame-Options / frame-ancestors 禁止在其它页面中显示,无法在侧边栏内加载。可在浏览器中直接打开',
|
|
175
|
+
browserEmbedAnyway: '仍然加载',
|
|
176
|
+
subagent: '任务管理',
|
|
177
|
+
openSubagent: '任务管理',
|
|
178
|
+
subagentMainAgent: '主代理',
|
|
179
|
+
subagentEmpty: '暂无子代理',
|
|
180
|
+
subagentEmptyDesc: '当前主代理派生的子代理将显示在这里',
|
|
181
|
+
subagentRunning: '运行中',
|
|
182
|
+
subagentInactive: '空闲',
|
|
183
|
+
subagentModeOneShot: '一次性',
|
|
184
|
+
subagentModeContinuable: '可续接',
|
|
185
|
+
subagentCount: '{count} 个子代理',
|
|
186
|
+
subagentCountRunning: '{count} 个子代理 · {running} 运行中',
|
|
187
|
+
subagentDiagCorrupt: '目录损坏',
|
|
188
|
+
subagentDiagUnsupported: '不支持的条目',
|
|
189
|
+
subagentDiagUnavailable: '不可用',
|
|
190
|
+
subagentThinking: '思考中…',
|
|
191
|
+
jobs: '后台任务',
|
|
192
|
+
jobsCount: '{count} 个后台任务',
|
|
193
|
+
jobsCountRunning: '{count} 个后台任务 · {running} 运行中',
|
|
194
|
+
jobStatusRunning: '运行中',
|
|
195
|
+
jobStatusStopping: '终止中',
|
|
196
|
+
jobStatusCompleted: '已完成',
|
|
197
|
+
jobStatusKilled: '已终止',
|
|
198
|
+
jobStatusFailed: '失败',
|
|
199
|
+
jobDurationSeconds: '{seconds} 秒',
|
|
200
|
+
jobDurationMinutes: '{minutes} 分 {seconds} 秒',
|
|
201
|
+
jobDurationHours: '{hours} 小时 {minutes} 分',
|
|
202
|
+
jobViewOutput: '查看输出',
|
|
203
|
+
jobHideOutput: '收起输出',
|
|
204
|
+
jobNoOutput: '暂无输出',
|
|
205
|
+
jobNotReadYet: '等待模型读取该任务的输出(模型执行 job_output 后,输出会显示在这里)',
|
|
206
|
+
jobOutputTruncated: '输出过长,已截断显示',
|
|
207
|
+
jobOutputError: '输出读取失败',
|
|
208
|
+
jobKill: '终止',
|
|
209
|
+
jobKillConfirm: '再次点击确认终止',
|
|
210
|
+
jobKillError: '终止失败',
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** The en dictionary (key-set-equal to zh, enforced by the type annotation). */
|
|
214
|
+
export const en: Record<keyof typeof zh, string> = {
|
|
215
|
+
explorer: 'Explorer',
|
|
216
|
+
git: 'Source Control',
|
|
217
|
+
terminal: 'Terminal',
|
|
218
|
+
editor: 'Editor',
|
|
219
|
+
newTab: 'New tab',
|
|
220
|
+
openExplorer: 'Explorer',
|
|
221
|
+
openGit: 'Git panel',
|
|
222
|
+
newTerminal: 'New terminal',
|
|
223
|
+
terminalLimit: 'Terminal limit reached (3)',
|
|
224
|
+
close: 'Close',
|
|
225
|
+
collapse: 'Collapse sidebar',
|
|
226
|
+
expand: 'Expand sidebar',
|
|
227
|
+
collapseBottomPanel: 'Collapse bottom panel',
|
|
228
|
+
expandBottomPanel: 'Expand bottom panel',
|
|
229
|
+
terminalError: 'Terminal connection failed',
|
|
230
|
+
terminalConnectFailed: 'Terminal failed to connect repeatedly',
|
|
231
|
+
terminalRetry: 'Retry',
|
|
232
|
+
preview: 'Preview',
|
|
233
|
+
edit: 'Edit',
|
|
234
|
+
refresh: 'Refresh',
|
|
235
|
+
save: 'Save',
|
|
236
|
+
saved: 'Saved',
|
|
237
|
+
unsaved: 'Unsaved',
|
|
238
|
+
saveFailed: 'Save failed',
|
|
239
|
+
truncation: 'File too large — showing the first 512KB',
|
|
240
|
+
binary: 'Binary file, preview unavailable',
|
|
241
|
+
loading: 'Loading…',
|
|
242
|
+
error: 'Failed to load',
|
|
243
|
+
retry: 'Retry',
|
|
244
|
+
splitLeft: 'Split left',
|
|
245
|
+
splitRight: 'Split right',
|
|
246
|
+
splitUp: 'Split up',
|
|
247
|
+
splitDown: 'Split down',
|
|
248
|
+
notRepo: 'This directory is not a git repository',
|
|
249
|
+
noChanges: 'No changes',
|
|
250
|
+
stage: 'Stage',
|
|
251
|
+
unstage: 'Unstage',
|
|
252
|
+
stageAll: 'Stage all',
|
|
253
|
+
unstageAll: 'Unstage all',
|
|
254
|
+
commitPlaceholder: 'Commit message (Ctrl+Enter)',
|
|
255
|
+
commit: 'Commit',
|
|
256
|
+
commitError: 'Commit failed',
|
|
257
|
+
branch: 'Branch',
|
|
258
|
+
checkoutError: 'Branch switch failed',
|
|
259
|
+
history: 'History',
|
|
260
|
+
changes: 'Changes',
|
|
261
|
+
staged: 'Staged',
|
|
262
|
+
unstaged: 'Unstaged',
|
|
263
|
+
cancel: 'Cancel',
|
|
264
|
+
diffEmpty: 'No text changes',
|
|
265
|
+
diffLoadError: 'Failed to load diff',
|
|
266
|
+
diffBinary: 'Binary',
|
|
267
|
+
diffAdded: 'Added',
|
|
268
|
+
diffDeleted: 'Deleted',
|
|
269
|
+
diffRenamed: 'Renamed',
|
|
270
|
+
diffExpand: 'Expand {count} more rows',
|
|
271
|
+
diffCollapse: 'Collapse',
|
|
272
|
+
discard: 'Discard changes',
|
|
273
|
+
discardTitle: 'Discard changes',
|
|
274
|
+
discardDesc: 'This discards the worktree changes of "{path}" (not recoverable).',
|
|
275
|
+
viewCommitDiff: 'View commit diff',
|
|
276
|
+
copyShortHash: 'Copy short hash',
|
|
277
|
+
copyFullHash: 'Copy full hash',
|
|
278
|
+
copySubject: 'Copy subject',
|
|
279
|
+
revertCommit: 'Revert commit',
|
|
280
|
+
revertTitle: 'Revert commit',
|
|
281
|
+
revertDesc: 'Create a new commit on the current branch that reverts "{subject}".',
|
|
282
|
+
cherryPickCommit: 'Cherry-pick commit',
|
|
283
|
+
cherryPickTitle: 'Cherry-pick commit',
|
|
284
|
+
cherryPickDesc: 'Apply the changes of "{subject}" to the current branch.',
|
|
285
|
+
timeJustNow: 'just now',
|
|
286
|
+
timeMinutesAgo: '{n} min ago',
|
|
287
|
+
timeHoursAgo: '{n} h ago',
|
|
288
|
+
timeYesterday: 'yesterday',
|
|
289
|
+
loadMore: 'Load more',
|
|
290
|
+
historyLoadError: 'Failed to load more history',
|
|
291
|
+
produced: 'Produced',
|
|
292
|
+
producedOpen: 'Open in sidebar',
|
|
293
|
+
disconnected: 'Terminal disconnected, reconnecting…',
|
|
294
|
+
exited: 'Terminal process exited',
|
|
295
|
+
noSession: 'Select a conversation to use the sidebar',
|
|
296
|
+
pluginNotLoaded: 'Plugin not loaded; tab unavailable:',
|
|
297
|
+
hiddenFiles: 'Hidden files',
|
|
298
|
+
parent: 'Parent directory',
|
|
299
|
+
copied: 'Copied',
|
|
300
|
+
copy: 'Copy',
|
|
301
|
+
newFile: 'New file',
|
|
302
|
+
openEditor: 'Open editor',
|
|
303
|
+
gitDetail: 'View change details',
|
|
304
|
+
referenceFile: '@file',
|
|
305
|
+
addToConversation: 'Add to conversation',
|
|
306
|
+
copyRelative: 'Copy relative path',
|
|
307
|
+
copyAbsolute: 'Copy absolute path',
|
|
308
|
+
download: 'Download',
|
|
309
|
+
settingsNav: 'Side card',
|
|
310
|
+
settingsOpenTitle: 'Open by default for new conversations',
|
|
311
|
+
settingsOpenDesc: 'Expand the side card automatically for brand-new conversations; existing conversations keep their own layouts',
|
|
312
|
+
settingsWidthTitle: 'Default width share',
|
|
313
|
+
settingsWidthDesc: 'The side card\'s default share of the window width for new conversations (20–60)',
|
|
314
|
+
settingsWidthSuffix: '%',
|
|
315
|
+
settingsOpenPathTitle: 'Open chat files in the sidebar',
|
|
316
|
+
settingsOpenPathDesc: 'Open file links in the chat (tool rows, produced files, mentions) in the sidebar editor instead of the system default app',
|
|
317
|
+
settingsSaveFailed: 'Failed to save',
|
|
318
|
+
settingsConflict: 'The setting changed in another window — please retry',
|
|
319
|
+
binaryNoPreview: 'This file type cannot be previewed',
|
|
320
|
+
downloadToView: 'Download to view',
|
|
321
|
+
officeTooLarge: 'File too large to preview',
|
|
322
|
+
officeCorrupt: 'File is corrupt or in an invalid format',
|
|
323
|
+
officeEncrypted: 'Encrypted files are not supported',
|
|
324
|
+
officeLoadFailed: 'Office preview component failed to load',
|
|
325
|
+
previousSlide: 'Previous',
|
|
326
|
+
nextSlide: 'Next',
|
|
327
|
+
zoom: 'Zoom',
|
|
328
|
+
zoomHint: 'Alt + wheel',
|
|
329
|
+
settingsSubagentTitle: 'Auto-open the Tasks page when a subagent appears',
|
|
330
|
+
settingsSubagentDesc: 'Expand the side card and open the Tasks page when the current conversation spawns a new subagent; turn off to open it manually',
|
|
331
|
+
settingsJobsTitle: 'Auto-open the Jobs page on a new background job',
|
|
332
|
+
settingsJobsDesc: 'Expand the side card and open the Jobs page whenever a new background job appears for the current conversation (every new job triggers); turn off to open it manually',
|
|
333
|
+
settingsToolsTitle: 'Inject terminal tools for the model',
|
|
334
|
+
settingsToolsDesc: 'When enabled, the model can create and drive sidebar terminals through the 8 terminal_* tools (off by default)',
|
|
335
|
+
settingsBottomTerminalTitle: 'Auto-open a terminal on the bottom panel\'s first expansion',
|
|
336
|
+
settingsBottomTerminalDesc: 'When the bottom panel is expanded for the first time in a session, try to open a fresh terminal tab there (the terminal quota still applies; on by default)',
|
|
337
|
+
settingsTabsTitle: 'Sidebar content',
|
|
338
|
+
settingsViewersTitle: 'File viewers',
|
|
339
|
+
settingsGeneralTitle: 'General',
|
|
340
|
+
settingsPopup: 'Feature settings',
|
|
341
|
+
settingsViewerCatchAll: 'Catch-all: any file',
|
|
342
|
+
viewerImage: 'Image',
|
|
343
|
+
viewerPdf: 'PDF',
|
|
344
|
+
viewerDocx: 'Word',
|
|
345
|
+
viewerXlsx: 'Excel',
|
|
346
|
+
viewerPptx: 'PowerPoint',
|
|
347
|
+
viewerMarkdown: 'Markdown',
|
|
348
|
+
viewerCode: 'Code',
|
|
349
|
+
viewerBinary: 'Binary download',
|
|
350
|
+
viewerHtml: 'HTML',
|
|
351
|
+
browser: 'Browser',
|
|
352
|
+
browserPlaceholder: 'Enter a URL, e.g. example.com',
|
|
353
|
+
browserGo: 'Go',
|
|
354
|
+
browserBack: 'Back',
|
|
355
|
+
browserForward: 'Forward',
|
|
356
|
+
browserStart: 'Enter a URL to start browsing (sandbox mode)',
|
|
357
|
+
browserBlockedScheme: 'Blocked: only http/https URLs are allowed',
|
|
358
|
+
browserBlockedLoopback: 'Blocked: local and internal addresses cannot be browsed here',
|
|
359
|
+
browserInvalid: 'Invalid URL',
|
|
360
|
+
browserNoSandboxWarning: 'Sandbox off: the current page runs with full GUI privileges (re-enable in settings)',
|
|
361
|
+
htmlNoSandboxWarning: 'Sandbox off: this HTML runs with full GUI privileges (re-enable in settings)',
|
|
362
|
+
sandboxStatusOn: 'Sandbox mode: on · pages cannot access the GUI\'s data or local files; logins and third-party cookies may not work',
|
|
363
|
+
sandboxUnlock: 'Temporarily disable (unsafe)',
|
|
364
|
+
sandboxRestore: 'Restore sandbox',
|
|
365
|
+
settingsHtmlDefaultUnsafeTitle: 'Open HTML previews unsandboxed by default (unsafe)',
|
|
366
|
+
settingsHtmlDefaultUnsafeDesc: 'When on, every newly opened HTML preview starts in the unsandboxed state (same origin as the GUI — it can read session files and internal APIs); the status row still offers a one-tap restore',
|
|
367
|
+
settingsHtmlSandboxTitle: 'Disable HTML preview sandbox (unsafe)',
|
|
368
|
+
settingsHtmlSandboxDesc: 'With the sandbox off, previewed HTML runs with the same origin as the GUI: it can read session files, local storage and call internal APIs. Only enable for fully trusted files',
|
|
369
|
+
settingsBrowserSandboxTitle: 'Disable browser sandbox (unsafe)',
|
|
370
|
+
settingsBrowserSandboxDesc: 'With the sandbox off, any visited site runs with the same origin as the GUI: it can read session data and act as your logged-in session. Only enable for fully trusted sites',
|
|
371
|
+
settingsBrowserLinksTitle: 'Open chat external links in the sidebar',
|
|
372
|
+
settingsBrowserLinksDesc: 'When on, clicking an http/https external link in the chat or GUI opens the sidebar browser instead of a new window; Ctrl/Cmd+click always bypasses',
|
|
373
|
+
browserOpenExternal: 'Open in browser',
|
|
374
|
+
browserEmbedBlocked: '{host} refused to be embedded',
|
|
375
|
+
browserEmbedBlockedDesc: 'The site forbids being displayed inside other pages (X-Frame-Options / frame-ancestors), so it cannot load in the sidebar. Open it directly in your browser instead.',
|
|
376
|
+
browserEmbedAnyway: 'Load anyway',
|
|
377
|
+
subagent: 'Tasks',
|
|
378
|
+
openSubagent: 'Tasks',
|
|
379
|
+
subagentMainAgent: 'Main agent',
|
|
380
|
+
subagentEmpty: 'No subagents',
|
|
381
|
+
subagentEmptyDesc: 'Subagents spawned under the main agent will appear here',
|
|
382
|
+
subagentRunning: 'Running',
|
|
383
|
+
subagentInactive: 'Inactive',
|
|
384
|
+
subagentModeOneShot: 'One-shot',
|
|
385
|
+
subagentModeContinuable: 'Continuable',
|
|
386
|
+
subagentCount: '{count} subagents',
|
|
387
|
+
subagentCountRunning: '{count} subagents · {running} running',
|
|
388
|
+
subagentDiagCorrupt: 'Corrupt',
|
|
389
|
+
subagentDiagUnsupported: 'Unsupported',
|
|
390
|
+
subagentDiagUnavailable: 'Unavailable',
|
|
391
|
+
subagentThinking: 'Thinking…',
|
|
392
|
+
jobs: 'Background jobs',
|
|
393
|
+
jobsCount: '{count} background jobs',
|
|
394
|
+
jobsCountRunning: '{count} background jobs · {running} running',
|
|
395
|
+
jobStatusRunning: 'Running',
|
|
396
|
+
jobStatusStopping: 'Stopping',
|
|
397
|
+
jobStatusCompleted: 'Completed',
|
|
398
|
+
jobStatusKilled: 'Killed',
|
|
399
|
+
jobStatusFailed: 'Failed',
|
|
400
|
+
jobDurationSeconds: '{seconds}s',
|
|
401
|
+
jobDurationMinutes: '{minutes}m {seconds}s',
|
|
402
|
+
jobDurationHours: '{hours}h {minutes}m',
|
|
403
|
+
jobViewOutput: 'View output',
|
|
404
|
+
jobHideOutput: 'Hide output',
|
|
405
|
+
jobNoOutput: 'No output yet',
|
|
406
|
+
jobNotReadYet: 'Waiting for the model to read this job; its output appears here once the model runs job_output',
|
|
407
|
+
jobOutputTruncated: 'Output truncated',
|
|
408
|
+
jobOutputError: 'Failed to read output',
|
|
409
|
+
jobKill: 'Kill',
|
|
410
|
+
jobKillConfirm: 'Click again to confirm kill',
|
|
411
|
+
jobKillError: 'Kill failed',
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* The dictionary namespace this plugin owns in the DSH locale registry
|
|
416
|
+
* (`'sidebar'` is taken by DSH's own ui-sidebar, hence this distinct name).
|
|
417
|
+
*/
|
|
418
|
+
export const LOCALE_NS = 'betterSidebar'
|
|
419
|
+
|
|
420
|
+
/** The DSH locale service attached by the client apply (absent → browser detection). */
|
|
421
|
+
let localeService: { getSnapshot(): { active: string } } | undefined
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Attach (or detach, with undefined) the DSH locale service. The sidebar
|
|
425
|
+
* mounts its own React root outside the slot system's locale seat, so the
|
|
426
|
+
* service rides this module-level holder: components keep calling the plain
|
|
427
|
+
* `t()` function, and the Sidebar root's locale subscription re-renders the
|
|
428
|
+
* whole tree on switches.
|
|
429
|
+
*/
|
|
430
|
+
export function attachLocale(service: { getSnapshot(): { active: string } } | undefined): void {
|
|
431
|
+
localeService = service
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* The active locale id ('zh' | 'en'): the DSH locale service's snapshot when
|
|
436
|
+
* attached, else the browser language.
|
|
437
|
+
*/
|
|
438
|
+
function activeLocale(): string {
|
|
439
|
+
return localeService?.getSnapshot().active
|
|
440
|
+
?? (typeof navigator !== 'undefined' ? navigator.language : '')
|
|
441
|
+
?? 'en'
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** Translate a copy key in the active locale (zh → zh, else en). */
|
|
445
|
+
export type CopyKey = keyof typeof zh
|
|
446
|
+
|
|
447
|
+
/** Translate a copy key; `{name}` placeholders interpolate from `params`. */
|
|
448
|
+
export function t(key: CopyKey, params?: Record<string, string | number>): string {
|
|
449
|
+
const dict = activeLocale().toLowerCase().startsWith('zh') ? zh : en
|
|
450
|
+
let text = dict[key]
|
|
451
|
+
if (params !== undefined) {
|
|
452
|
+
for (const [name, value] of Object.entries(params)) {
|
|
453
|
+
text = text.replaceAll(`{${name}}`, String(value))
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return text
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Whether the active locale is Chinese (used for selectors). */
|
|
460
|
+
export function isZh(): boolean {
|
|
461
|
+
return activeLocale().toLowerCase().startsWith('zh')
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** Format an ISO 8601 author date relative to now (刚刚 / N 分钟前 / N 小时前 / 昨天 / date). */
|
|
465
|
+
export function relativeTime(iso: string): string {
|
|
466
|
+
const then = Date.parse(iso)
|
|
467
|
+
if (Number.isNaN(then)) return iso
|
|
468
|
+
const seconds = Math.floor((Date.now() - then) / 1000)
|
|
469
|
+
if (seconds < 60) return t('timeJustNow')
|
|
470
|
+
if (seconds < 3600) return t('timeMinutesAgo', { n: Math.floor(seconds / 60) })
|
|
471
|
+
if (seconds < 86400) return t('timeHoursAgo', { n: Math.floor(seconds / 3600) })
|
|
472
|
+
if (seconds < 172800) return t('timeYesterday')
|
|
473
|
+
const date = new Date(then)
|
|
474
|
+
const pad = (value: number): string => String(value).padStart(2, '0')
|
|
475
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
|
|
476
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared pieces of the Office preview views (docx / xlsx): the load-state
|
|
3
|
+
* union, the shared props, and the download fallback. The two views live in
|
|
4
|
+
* their own modules (docx-view.tsx / xlsx-view.tsx) so the bundler can emit
|
|
5
|
+
* them as separate lazy chunks — docx-preview and the Univer family (several
|
|
6
|
+
* MB) must never share a script (see docs/plans/2026-08-12-lazy-chunks-design.md).
|
|
7
|
+
*/
|
|
8
|
+
import { downloadUrl, type SessionScope } from './api.ts'
|
|
9
|
+
import { t } from './locales.ts'
|
|
10
|
+
import css from './sidebar.module.css'
|
|
11
|
+
|
|
12
|
+
/** Loading / ready / error state shared by both views. */
|
|
13
|
+
export type LoadState =
|
|
14
|
+
| { status: 'loading' }
|
|
15
|
+
| { status: 'ready' }
|
|
16
|
+
| { status: 'error'; message: string }
|
|
17
|
+
|
|
18
|
+
/** Shared props. */
|
|
19
|
+
export interface OfficeViewProps {
|
|
20
|
+
scope: SessionScope
|
|
21
|
+
path: string
|
|
22
|
+
title: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The shared error / fallback affordance: the failure reason plus a download
|
|
27
|
+
* link, so the user always has a path to the file. Used by both DocxView and
|
|
28
|
+
* XlsxView, and matches the binary placeholder download link.
|
|
29
|
+
*/
|
|
30
|
+
export function BinaryFallback(props: { scope: SessionScope; path: string; message: string }): JSX.Element {
|
|
31
|
+
const { scope, path, message } = props
|
|
32
|
+
return (
|
|
33
|
+
<div className={css.editorBinary}>
|
|
34
|
+
<span className={css.editorBinaryNotice}>{message}</span>
|
|
35
|
+
<a className={css.editorDownloadLink} href={downloadUrl(scope, path)} download>
|
|
36
|
+
{t('downloadToView')}
|
|
37
|
+
</a>
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Office file type dispatch — pure functions mapping file extensions to
|
|
3
|
+
* preview kinds. Kept separate from the editor host so the mapping is
|
|
4
|
+
* unit-testable without mounting React or pulling in docx-preview / Univer.
|
|
5
|
+
*
|
|
6
|
+
* The host side never inspects Office extensions: the binary NUL probe in
|
|
7
|
+
* {@link ../index.ts} (readText) tags every Office file as binary, and the
|
|
8
|
+
* /sidebar/file media route serves raw bytes for any extension. This module
|
|
9
|
+
* is the client's sole source of truth for "is this previewable here".
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Extensions rendered with docx-preview (preserved styles/images/tables). */
|
|
13
|
+
export const OFFICE_DOCX_EXT = '.docx' as const
|
|
14
|
+
|
|
15
|
+
/** Extensions rendered with Univer (sheets: data + formulas + formatting). */
|
|
16
|
+
export const OFFICE_XLSX_EXT = '.xlsx' as const
|
|
17
|
+
|
|
18
|
+
/** Extensions rendered with the browser-native PPTX viewer. */
|
|
19
|
+
export const OFFICE_PPTX_EXT = '.pptx' as const
|
|
20
|
+
|
|
21
|
+
/** Extensions that get a download-only affordance (no client-side renderer). */
|
|
22
|
+
export const OFFICE_DOWNLOAD_ONLY_EXT = ['.doc', '.xls', '.ppt'] as readonly string[]
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The preview strategy for one path's extension.
|
|
26
|
+
* - `'docx'` — render via docx-preview.
|
|
27
|
+
* - `'xlsx'` — render via Univer.
|
|
28
|
+
* - `'pptx'` — render via pptx-renderer.
|
|
29
|
+
* - `'download-only'` — show the binary download button (no preview renderer
|
|
30
|
+
* covers legacy OLE .doc/.xls/.ppt on the client).
|
|
31
|
+
* - `null` — not an Office file; the caller's existing image / md / text / binary
|
|
32
|
+
* pipeline handles it.
|
|
33
|
+
*/
|
|
34
|
+
export type OfficeKind = 'docx' | 'xlsx' | 'pptx' | 'download-only' | null
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Map a lowercased extension (with leading dot) to its preview kind.
|
|
38
|
+
*
|
|
39
|
+
* @param ext - the path extension (with leading dot) (`.docx`,
|
|
40
|
+
* `.xlsx`, `.pptx`, `.doc`, `.xls`, `.ppt`, …); `''` when the path has none.
|
|
41
|
+
*/
|
|
42
|
+
export function officeKindForExt(ext: string): OfficeKind {
|
|
43
|
+
if (ext === OFFICE_DOCX_EXT) return 'docx'
|
|
44
|
+
if (ext === OFFICE_XLSX_EXT) return 'xlsx'
|
|
45
|
+
if (ext === OFFICE_PPTX_EXT) return 'pptx'
|
|
46
|
+
if (OFFICE_DOWNLOAD_ONLY_EXT.includes(ext)) return 'download-only'
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compat re-export shim for the historical `src/client/office-view.tsx`
|
|
3
|
+
* path. The office preview views were split into docx-view.tsx /
|
|
4
|
+
* xlsx-view.tsx (+ office-shared.tsx) so the bundler can emit each library
|
|
5
|
+
* as its own lazy chunk (see docs/plans/2026-08-12-lazy-chunks-design.md).
|
|
6
|
+
* This module keeps deep imports of this path resolvable through the
|
|
7
|
+
* `./src/*` exports subpath — it deliberately re-exports BOTH views, so it
|
|
8
|
+
* must never be imported from the core bundle or a chunk entry (it would
|
|
9
|
+
* drag both libraries into that bundle).
|
|
10
|
+
*/
|
|
11
|
+
export { DocxView } from './docx-view.tsx'
|
|
12
|
+
export { XlsxView } from './xlsx-view.tsx'
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interception of the chat's file-open funnel. The client runtime's
|
|
3
|
+
* `ctx.workspaces.openPath` is the SINGLE door every chat-side file open goes
|
|
4
|
+
* through — ui-conversation's apply.ts resolves the path against the session
|
|
5
|
+
* cwd and calls it for tool-row path links, the produced-files row, and
|
|
6
|
+
* prose file mentions alike (verified against the DSH source:
|
|
7
|
+
* `packages/client/ui-conversation/src/client/apply.ts` is the only
|
|
8
|
+
* production caller). Wrapping that one method reroutes those opens into the
|
|
9
|
+
* sidebar editor instead of the Host OS — no DSH modification needed.
|
|
10
|
+
*
|
|
11
|
+
* The wrapper is dependency-free by design (no React / ui-primitives), so
|
|
12
|
+
* the takeover logic is unit-testable and the file stays importable from the
|
|
13
|
+
* test runtime.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** The one service method the wrapper replaces (mirror of the runtime IWorkspaces). */
|
|
17
|
+
export interface OpenPathService {
|
|
18
|
+
openPath(path: string): Promise<void>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Per-call decisions the wrapper needs (wired to the store + ctx in the client half). */
|
|
22
|
+
export interface OpenPathInterceptDeps {
|
|
23
|
+
/**
|
|
24
|
+
* Whether to take over this call: the `interceptOpenPath` pref AND the
|
|
25
|
+
* editor tab's own enable switch must both be on (an editor that cannot
|
|
26
|
+
* open must not swallow opens — they fall through to the Host).
|
|
27
|
+
*/
|
|
28
|
+
takeoverEnabled(): boolean
|
|
29
|
+
/** The session whose scope the sidebar editor loads the file in (current session). */
|
|
30
|
+
currentSessionId(): string | undefined
|
|
31
|
+
/** Route the open into the sidebar editor (the established openSidebarFile). */
|
|
32
|
+
openInSidebar(path: string, sessionId: string): void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Wrap `workspaces.openPath`: intercepted calls open the file in the sidebar
|
|
37
|
+
* editor instead of the Host OS and resolve as success (the original's
|
|
38
|
+
* callers ignore the result); anything that declines falls through to the
|
|
39
|
+
* original method untouched.
|
|
40
|
+
* @param workspaces - the client workspaces service to wrap.
|
|
41
|
+
* @param deps - per-call takeover decisions.
|
|
42
|
+
* @returns the disposer restoring the original method (HMR-safe).
|
|
43
|
+
*/
|
|
44
|
+
export function wrapOpenPath(workspaces: OpenPathService, deps: OpenPathInterceptDeps): () => void {
|
|
45
|
+
// The RAW method reference (never a bound copy): restore must put back the
|
|
46
|
+
// exact original so a chain of wrappers (other plugins wrapping the same
|
|
47
|
+
// method) keeps working across disposals in any order.
|
|
48
|
+
const original = workspaces.openPath
|
|
49
|
+
workspaces.openPath = (path: string): Promise<void> => {
|
|
50
|
+
if (deps.takeoverEnabled()) {
|
|
51
|
+
const sessionId = deps.currentSessionId()
|
|
52
|
+
if (sessionId !== undefined) {
|
|
53
|
+
deps.openInSidebar(path, sessionId)
|
|
54
|
+
return Promise.resolve()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return original.call(workspaces, path)
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
workspaces.openPath = original
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path projection helpers shared by the explorer rows: a path relative to
|
|
3
|
+
* the session cwd (for the @-reference button and "copy relative path").
|
|
4
|
+
* The fs-tree joins with '/' even on Windows, so both separators normalize
|
|
5
|
+
* to '/' before comparison.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The path relative to the session's working directory.
|
|
10
|
+
* @param cwd - the explorer root (absolute).
|
|
11
|
+
* @param path - an absolute entry path from the fs-tree.
|
|
12
|
+
* @returns the relative path with '/' separators ('.' for the cwd itself),
|
|
13
|
+
* or `path` unchanged when it lies outside the cwd.
|
|
14
|
+
*
|
|
15
|
+
* The prefix test is case-insensitive: Windows paths (and macOS's
|
|
16
|
+
* case-insensitive volumes) may arrive with different casing than the cwd
|
|
17
|
+
* row, and the containment decision must not depend on it. The returned
|
|
18
|
+
* relative text keeps the caller's own casing.
|
|
19
|
+
*/
|
|
20
|
+
export function relativeTo(cwd: string, path: string): string {
|
|
21
|
+
const base = cwd.replace(/[\\/]+$/, '')
|
|
22
|
+
const norm = (value: string): string => value.replace(/\\/g, '/')
|
|
23
|
+
const nBase = norm(base)
|
|
24
|
+
const nPath = norm(path)
|
|
25
|
+
if (nPath === nBase) return '.'
|
|
26
|
+
if (nPath.toLowerCase().startsWith(`${nBase.toLowerCase()}/`)) return nPath.slice(nBase.length + 1)
|
|
27
|
+
return path
|
|
28
|
+
}
|