@lijian-ui/dsh-file-manager 0.2.0 → 0.2.1

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/lib/client.js CHANGED
@@ -1083,6 +1083,274 @@ window.__ModuleLoader__.load({
1083
1083
  return `/filemgr/raw?root=${encodeURIComponent(root)}&path=${encodeURIComponent(path)}&v=${nonce}`;
1084
1084
  }
1085
1085
  //#endregion
1086
+ //#region src/client/locales.ts
1087
+ /**
1088
+ * Locale strings for the panel surfaces (zh/en). The client registers the
1089
+ * dictionary through the locale service like the sibling plugins; copy is
1090
+ * deliberately short and technical.
1091
+ * @module dsh-filemgr/client/locales
1092
+ */
1093
+ const zh = {
1094
+ "explorer.tabs.files": "文件",
1095
+ "explorer.tabs.changes": "变更",
1096
+ "explorer.search.placeholder": "按文件名搜索",
1097
+ "explorer.search.searching": "搜索中…",
1098
+ "explorer.search.empty": "没有匹配的文件",
1099
+ "explorer.search.error": "搜索失败",
1100
+ "explorer.search.truncated": "结果过多,仅显示前 {count} 条",
1101
+ "explorer.tree.empty": "项目为空",
1102
+ "explorer.collapse": "收起面板",
1103
+ "explorer.expand": "展开面板",
1104
+ "explorer.maximize": "最大化文件面板",
1105
+ "explorer.restore": "还原面板",
1106
+ "explorer.openPreview": "打开预览",
1107
+ "explorer.drag.dropHint": "松手插入文件路径",
1108
+ "scm.repositories": "存储库",
1109
+ "scm.changes": "变更",
1110
+ "scm.staged": "已暂存",
1111
+ "scm.unstaged": "变更",
1112
+ "scm.untracked": "未跟踪",
1113
+ "scm.conflicted": "冲突",
1114
+ "scm.stage": "暂存",
1115
+ "scm.unstage": "取消暂存",
1116
+ "scm.discard": "放弃更改",
1117
+ "scm.stageAll": "全部暂存",
1118
+ "scm.discardAll": "全部放弃",
1119
+ "scm.empty": "没有更改",
1120
+ "scm.notRepo": "当前目录不是 git 仓库",
1121
+ "scm.gitMissing": "未检测到 git,请先安装 git 后重试",
1122
+ "scm.loading": "读取状态中…",
1123
+ "scm.failed": "操作失败",
1124
+ "scm.viewList": "列表视图",
1125
+ "scm.viewTree": "树视图",
1126
+ "scm.discardConfirmTracked": "放弃对 {count} 个文件的更改?此操作不可恢复。",
1127
+ "scm.discardConfirmUntracked": "删除 {count} 个未跟踪文件?此操作不可恢复。",
1128
+ "preview.noTabs": "没有打开的预览",
1129
+ "preview.newUrlTab": "新建 URL 预览",
1130
+ "preview.collapsePanel": "收起预览面板",
1131
+ "preview.maximize": "最大化预览面板",
1132
+ "preview.restore": "还原面板",
1133
+ "preview.source": "源码",
1134
+ "preview.preview": "预览",
1135
+ "preview.editor": "编辑器",
1136
+ "preview.split": "分屏",
1137
+ "preview.refresh": "刷新",
1138
+ "preview.refresh.updated": "文件已在磁盘更新",
1139
+ "preview.save": "保存",
1140
+ "preview.download": "下载",
1141
+ "preview.copyCode": "复制代码",
1142
+ "preview.copyCodeDone": "已复制",
1143
+ "preview.openExternal": "在系统应用中打开",
1144
+ "preview.dirty": "未保存的更改",
1145
+ "preview.closeLeft": "关闭左侧",
1146
+ "preview.closeRight": "关闭右侧",
1147
+ "preview.closeOthers": "关闭其他",
1148
+ "preview.closeAll": "关闭全部",
1149
+ "preview.closeConfirmTitle": "关闭未保存的标签页",
1150
+ "preview.closeConfirmBody": "{count} 个标签页有未保存的更改,关闭将丢失这些更改。",
1151
+ "preview.saved": "已保存",
1152
+ "preview.saveConflict": "文件已在磁盘上被修改,保存冲突:请刷新后重试",
1153
+ "preview.errorOversized": "文件过大,仅加载前 80,000 字符",
1154
+ "preview.unsupported": "此格式暂不支持预览",
1155
+ "preview.downloadHint": "可在系统应用中打开或下载查看",
1156
+ "preview.url.placeholder": "输入网址,回车打开",
1157
+ "preview.url.hint": "按 Esc 还原",
1158
+ "common.cancel": "取消",
1159
+ "common.confirm": "确定",
1160
+ "common.close": "关闭",
1161
+ "common.delete": "删除",
1162
+ "common.copyPath": "复制路径",
1163
+ "common.copied": "已复制",
1164
+ "explorer.menu.copyPath": "复制路径",
1165
+ "explorer.menu.copyName": "复制名称",
1166
+ "explorer.menu.reveal": "在文件管理器中显示",
1167
+ "explorer.menu.openWithDefault": "用默认应用打开",
1168
+ "explorer.menu.rename": "重命名",
1169
+ "explorer.menu.newFile": "新建文件",
1170
+ "explorer.menu.newFolder": "新建文件夹",
1171
+ "explorer.menu.delete": "删除",
1172
+ "explorer.rename.title": "重命名",
1173
+ "explorer.newFile.title": "新建文件",
1174
+ "explorer.newFolder.title": "新建文件夹",
1175
+ "explorer.deleteConfirmTitle": "删除确认",
1176
+ "explorer.deleteConfirmBody": "确定要删除「{name}」吗?此操作不可恢复。",
1177
+ "explorer.opFailed": "操作失败",
1178
+ "explorer.refToInput": "引用到输入框",
1179
+ "settings.title": "右侧面板",
1180
+ "settings.description": "Explorer / Preview 与 SCM 变更面板的总开关。",
1181
+ "settings.enabled": "启用右侧面板",
1182
+ "settings.enabledHint": "关闭后不再挂载右侧文件树与预览列、不显示浮动展开按钮、不注册 /filemgr 路由,也不做工作区监视与 git 轮询;聚合包其余插件不受影响。",
1183
+ "settings.overridden": "已覆盖",
1184
+ "settings.reset": "恢复默认",
1185
+ "settings.notExposed": "当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。",
1186
+ "settings.readOnly": "当前部署的设置只读。",
1187
+ "settings.inherit": "继承",
1188
+ "settings.on": "开",
1189
+ "settings.off": "关",
1190
+ "settings.expand": "展开设置",
1191
+ "settings.collapse": "收起设置",
1192
+ "settings.save": "保存",
1193
+ "settings.saving": "保存中…",
1194
+ "settings.discard": "放弃",
1195
+ "settings.unsaved": "未保存",
1196
+ "settings.saveFailed": "部署未接受这些值,已保留供你修改。",
1197
+ "settings.invalidNumber": "请输入数字,留空则使用默认值。",
1198
+ "dock.filePanel.label": "文件面板",
1199
+ "preview.addToChat": "添加到对话",
1200
+ "picker.title": "引用项目文件",
1201
+ "picker.searchPlaceholder": "搜索工作区文件…",
1202
+ "picker.loading": "加载中…",
1203
+ "picker.emptyDir": "(空目录)",
1204
+ "picker.emptySelection": "选择文件或目录(可多选)",
1205
+ "picker.insert": "插入",
1206
+ "picker.removeAria": "移除",
1207
+ "picker.clearAria": "清空",
1208
+ "picker.closeAria": "关闭"
1209
+ };
1210
+ const en = {
1211
+ "explorer.tabs.files": "Files",
1212
+ "explorer.tabs.changes": "Changes",
1213
+ "explorer.search.placeholder": "Search file names",
1214
+ "explorer.search.searching": "Searching…",
1215
+ "explorer.search.empty": "No matching files",
1216
+ "explorer.search.error": "Search failed",
1217
+ "explorer.search.truncated": "Too many results, showing first {count}",
1218
+ "explorer.tree.empty": "The project is empty",
1219
+ "explorer.collapse": "Collapse panel",
1220
+ "explorer.expand": "Expand panel",
1221
+ "explorer.maximize": "Maximize files panel",
1222
+ "explorer.restore": "Restore panel",
1223
+ "explorer.openPreview": "Open preview",
1224
+ "explorer.drag.dropHint": "Release to insert the file path",
1225
+ "scm.repositories": "Repositories",
1226
+ "scm.changes": "Changes",
1227
+ "scm.staged": "Staged",
1228
+ "scm.unstaged": "Changes",
1229
+ "scm.untracked": "Untracked",
1230
+ "scm.conflicted": "Conflict",
1231
+ "scm.stage": "Stage",
1232
+ "scm.unstage": "Unstage",
1233
+ "scm.discard": "Discard",
1234
+ "scm.stageAll": "Stage all",
1235
+ "scm.discardAll": "Discard all",
1236
+ "scm.empty": "No changes",
1237
+ "scm.notRepo": "Not a git repository",
1238
+ "scm.gitMissing": "Git is not installed. Install git and reload to use the changes panel",
1239
+ "scm.loading": "Loading status…",
1240
+ "scm.failed": "Operation failed",
1241
+ "scm.viewList": "List view",
1242
+ "scm.viewTree": "Tree view",
1243
+ "scm.discardConfirmTracked": "Discard changes in {count} files? This cannot be undone.",
1244
+ "scm.discardConfirmUntracked": "Delete {count} untracked files? This cannot be undone.",
1245
+ "preview.noTabs": "No open previews",
1246
+ "preview.newUrlTab": "New URL preview",
1247
+ "preview.collapsePanel": "Collapse preview panel",
1248
+ "preview.maximize": "Maximize preview panel",
1249
+ "preview.restore": "Restore panel",
1250
+ "preview.source": "Source",
1251
+ "preview.preview": "Preview",
1252
+ "preview.editor": "Editor",
1253
+ "preview.split": "Split",
1254
+ "preview.refresh": "Refresh",
1255
+ "preview.refresh.updated": "File updated on disk",
1256
+ "preview.save": "Save",
1257
+ "preview.download": "Download",
1258
+ "preview.copyCode": "Copy code",
1259
+ "preview.copyCodeDone": "Copied",
1260
+ "preview.openExternal": "Open in system app",
1261
+ "preview.dirty": "Unsaved changes",
1262
+ "preview.closeLeft": "Close left",
1263
+ "preview.closeRight": "Close right",
1264
+ "preview.closeOthers": "Close others",
1265
+ "preview.closeAll": "Close all",
1266
+ "preview.closeConfirmTitle": "Close unsaved tabs",
1267
+ "preview.closeConfirmBody": "{count} tabs have unsaved changes. Closing will lose them.",
1268
+ "preview.saved": "Saved",
1269
+ "preview.saveConflict": "File changed on disk. Save conflict: refresh and retry",
1270
+ "preview.errorOversized": "File too large, only the first 80,000 characters loaded",
1271
+ "preview.unsupported": "Preview not supported for this format",
1272
+ "preview.downloadHint": "Open in a system app or download to view",
1273
+ "preview.url.placeholder": "Enter a URL and press Enter",
1274
+ "preview.url.hint": "Press Esc to revert",
1275
+ "common.cancel": "Cancel",
1276
+ "common.confirm": "OK",
1277
+ "common.close": "Close",
1278
+ "common.delete": "Delete",
1279
+ "common.copyPath": "Copy path",
1280
+ "common.copied": "Copied",
1281
+ "explorer.menu.copyPath": "Copy path",
1282
+ "explorer.menu.copyName": "Copy name",
1283
+ "explorer.menu.reveal": "Reveal in file manager",
1284
+ "explorer.menu.openWithDefault": "Open with default app",
1285
+ "explorer.menu.rename": "Rename",
1286
+ "explorer.menu.newFile": "New file",
1287
+ "explorer.menu.newFolder": "New folder",
1288
+ "explorer.menu.delete": "Delete",
1289
+ "explorer.rename.title": "Rename",
1290
+ "explorer.newFile.title": "New file",
1291
+ "explorer.newFolder.title": "New folder",
1292
+ "explorer.deleteConfirmTitle": "Confirm delete",
1293
+ "explorer.deleteConfirmBody": "Delete \"{name}\"? This cannot be undone.",
1294
+ "explorer.opFailed": "Operation failed",
1295
+ "explorer.refToInput": "Reference to input",
1296
+ "settings.title": "Right panel",
1297
+ "settings.description": "Master switch for the Explorer / Preview and SCM changes panels.",
1298
+ "settings.enabled": "Enable the right panel",
1299
+ "settings.enabledHint": "When off, the Explorer and Preview columns are not mounted, the floating expand button disappears, the /filemgr routes are not registered, and workspace watching and git polling stop. The rest of the family bundle is unaffected.",
1300
+ "settings.overridden": "Overridden",
1301
+ "settings.reset": "Reset",
1302
+ "settings.notExposed": "This DSH version does not expose this plugin's settings namespace; the form is unavailable. Edit ~/.dsh/settings.yaml directly, or add the namespace to the WEB_SETTINGS_NAMESPACES allowlist of dsh-host-apiproxy and restart.",
1303
+ "settings.readOnly": "Settings are read-only in this deployment.",
1304
+ "settings.inherit": "Inherit",
1305
+ "settings.on": "On",
1306
+ "settings.off": "Off",
1307
+ "settings.expand": "Expand settings",
1308
+ "settings.collapse": "Collapse settings",
1309
+ "settings.save": "Save",
1310
+ "settings.saving": "Saving…",
1311
+ "settings.discard": "Discard",
1312
+ "settings.unsaved": "Unsaved",
1313
+ "settings.saveFailed": "The deployment rejected these values; your edits are kept.",
1314
+ "settings.invalidNumber": "Enter a number, or leave it empty to use the default.",
1315
+ "dock.filePanel.label": "File Panel",
1316
+ "preview.addToChat": "Add to Chat",
1317
+ "picker.title": "Reference Project Files",
1318
+ "picker.searchPlaceholder": "Search workspace files…",
1319
+ "picker.loading": "Loading…",
1320
+ "picker.emptyDir": "(empty directory)",
1321
+ "picker.emptySelection": "Select files or directories (multi-select)",
1322
+ "picker.insert": "Insert",
1323
+ "picker.removeAria": "Remove",
1324
+ "picker.clearAria": "Clear",
1325
+ "picker.closeAria": "Close"
1326
+ };
1327
+ /** The dictionary namespace this plugin owns. */
1328
+ const NS = "filemgr";
1329
+ /** Format one copy string with {name} placeholders. */
1330
+ function format(template, params) {
1331
+ return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
1332
+ }
1333
+ /** Simple dictionary access (zh/en by a global flag the client sets). */
1334
+ const dictionaries = {
1335
+ zh,
1336
+ en
1337
+ };
1338
+ let currentLanguage = "zh";
1339
+ /** Set the active language (the client mirrors the locale service). */
1340
+ function setLanguage(language) {
1341
+ currentLanguage = language === "en" ? "en" : "zh";
1342
+ }
1343
+ let _boundT = null;
1344
+ function bindT(t) {
1345
+ _boundT = t;
1346
+ }
1347
+ /** Translate one key with optional params. */
1348
+ function t(key, params) {
1349
+ if (_boundT !== null) return _boundT(key, params);
1350
+ const template = (dictionaries[currentLanguage] ?? zh)[key] ?? zh[key];
1351
+ return params === void 0 ? template : format(template, params);
1352
+ }
1353
+ //#endregion
1086
1354
  //#region src/client/persist.ts
1087
1355
  /**
1088
1356
  * Persistence helpers for panel preferences: range-validated reads (invalid
@@ -2179,7 +2447,7 @@ window.__ModuleLoader__.load({
2179
2447
  if (!result.ok) return {
2180
2448
  ...item,
2181
2449
  loading: false,
2182
- error: result.error.code === "write-conflict" ? "文件已在磁盘上被修改,保存冲突:请刷新后重试" : result.error.message
2450
+ error: result.error.code === "write-conflict" ? t("preview.saveConflict") : result.error.message
2183
2451
  };
2184
2452
  if (item.content !== sentContent) return {
2185
2453
  ...item,
@@ -2738,247 +3006,6 @@ window.__ModuleLoader__.load({
2738
3006
  }
2739
3007
  };
2740
3008
  //#endregion
2741
- //#region src/client/locales.ts
2742
- /**
2743
- * Locale strings for the panel surfaces (zh/en). The client registers the
2744
- * dictionary through the locale service like the sibling plugins; copy is
2745
- * deliberately short and technical.
2746
- * @module dsh-filemgr/client/locales
2747
- */
2748
- const zh = {
2749
- "explorer.tabs.files": "文件",
2750
- "explorer.tabs.changes": "变更",
2751
- "explorer.search.placeholder": "按文件名搜索",
2752
- "explorer.search.searching": "搜索中…",
2753
- "explorer.search.empty": "没有匹配的文件",
2754
- "explorer.search.error": "搜索失败",
2755
- "explorer.search.truncated": "结果过多,仅显示前 {count} 条",
2756
- "explorer.tree.empty": "项目为空",
2757
- "explorer.collapse": "收起面板",
2758
- "explorer.expand": "展开面板",
2759
- "explorer.maximize": "最大化文件面板",
2760
- "explorer.restore": "还原面板",
2761
- "explorer.openPreview": "打开预览",
2762
- "explorer.drag.dropHint": "松手插入文件路径",
2763
- "scm.repositories": "存储库",
2764
- "scm.changes": "变更",
2765
- "scm.staged": "已暂存",
2766
- "scm.unstaged": "变更",
2767
- "scm.untracked": "未跟踪",
2768
- "scm.conflicted": "冲突",
2769
- "scm.stage": "暂存",
2770
- "scm.unstage": "取消暂存",
2771
- "scm.discard": "放弃更改",
2772
- "scm.stageAll": "全部暂存",
2773
- "scm.discardAll": "全部放弃",
2774
- "scm.empty": "没有更改",
2775
- "scm.notRepo": "当前目录不是 git 仓库",
2776
- "scm.gitMissing": "未检测到 git,请先安装 git 后重试",
2777
- "scm.loading": "读取状态中…",
2778
- "scm.failed": "操作失败",
2779
- "scm.viewList": "列表视图",
2780
- "scm.viewTree": "树视图",
2781
- "scm.discardConfirmTracked": "放弃对 {count} 个文件的更改?此操作不可恢复。",
2782
- "scm.discardConfirmUntracked": "删除 {count} 个未跟踪文件?此操作不可恢复。",
2783
- "preview.noTabs": "没有打开的预览",
2784
- "preview.newUrlTab": "新建 URL 预览",
2785
- "preview.collapsePanel": "收起预览面板",
2786
- "preview.maximize": "最大化预览面板",
2787
- "preview.restore": "还原面板",
2788
- "preview.source": "源码",
2789
- "preview.preview": "预览",
2790
- "preview.editor": "编辑器",
2791
- "preview.split": "分屏",
2792
- "preview.refresh": "刷新",
2793
- "preview.refresh.updated": "文件已在磁盘更新",
2794
- "preview.save": "保存",
2795
- "preview.download": "下载",
2796
- "preview.copyCode": "复制代码",
2797
- "preview.copyCodeDone": "已复制",
2798
- "preview.openExternal": "在系统应用中打开",
2799
- "preview.dirty": "未保存的更改",
2800
- "preview.closeLeft": "关闭左侧",
2801
- "preview.closeRight": "关闭右侧",
2802
- "preview.closeOthers": "关闭其他",
2803
- "preview.closeAll": "关闭全部",
2804
- "preview.closeConfirmTitle": "关闭未保存的标签页",
2805
- "preview.closeConfirmBody": "{count} 个标签页有未保存的更改,关闭将丢失这些更改。",
2806
- "preview.saved": "已保存",
2807
- "preview.saveConflict": "文件已在磁盘上被修改,保存冲突:请刷新后重试",
2808
- "preview.errorOversized": "文件过大,仅加载前 80,000 字符",
2809
- "preview.unsupported": "此格式暂不支持预览",
2810
- "preview.downloadHint": "可在系统应用中打开或下载查看",
2811
- "preview.url.placeholder": "输入网址,回车打开",
2812
- "preview.url.hint": "按 Esc 还原",
2813
- "common.cancel": "取消",
2814
- "common.confirm": "确定",
2815
- "common.close": "关闭",
2816
- "common.delete": "删除",
2817
- "common.copyPath": "复制路径",
2818
- "common.copied": "已复制",
2819
- "explorer.menu.copyPath": "复制路径",
2820
- "explorer.menu.copyName": "复制名称",
2821
- "explorer.menu.reveal": "在文件管理器中显示",
2822
- "explorer.menu.openWithDefault": "用默认应用打开",
2823
- "explorer.menu.rename": "重命名",
2824
- "explorer.menu.newFile": "新建文件",
2825
- "explorer.menu.newFolder": "新建文件夹",
2826
- "explorer.menu.delete": "删除",
2827
- "explorer.rename.title": "重命名",
2828
- "explorer.newFile.title": "新建文件",
2829
- "explorer.newFolder.title": "新建文件夹",
2830
- "explorer.deleteConfirmTitle": "删除确认",
2831
- "explorer.deleteConfirmBody": "确定要删除「{name}」吗?此操作不可恢复。",
2832
- "explorer.opFailed": "操作失败",
2833
- "explorer.refToInput": "引用到输入框",
2834
- "settings.title": "右侧面板",
2835
- "settings.description": "Explorer / Preview 与 SCM 变更面板的总开关。",
2836
- "settings.enabled": "启用右侧面板",
2837
- "settings.enabledHint": "关闭后不再挂载右侧文件树与预览列、不显示浮动展开按钮、不注册 /filemgr 路由,也不做工作区监视与 git 轮询;聚合包其余插件不受影响。",
2838
- "settings.overridden": "已覆盖",
2839
- "settings.reset": "恢复默认",
2840
- "settings.notExposed": "当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。",
2841
- "settings.readOnly": "当前部署的设置只读。",
2842
- "settings.inherit": "继承",
2843
- "settings.on": "开",
2844
- "settings.off": "关",
2845
- "settings.expand": "展开设置",
2846
- "settings.collapse": "收起设置",
2847
- "settings.save": "保存",
2848
- "settings.saving": "保存中…",
2849
- "settings.discard": "放弃",
2850
- "settings.unsaved": "未保存",
2851
- "settings.saveFailed": "部署未接受这些值,已保留供你修改。",
2852
- "settings.invalidNumber": "请输入数字,留空则使用默认值。"
2853
- };
2854
- const en = {
2855
- "explorer.tabs.files": "Files",
2856
- "explorer.tabs.changes": "Changes",
2857
- "explorer.search.placeholder": "Search file names",
2858
- "explorer.search.searching": "Searching…",
2859
- "explorer.search.empty": "No matching files",
2860
- "explorer.search.error": "Search failed",
2861
- "explorer.search.truncated": "Too many results, showing first {count}",
2862
- "explorer.tree.empty": "The project is empty",
2863
- "explorer.collapse": "Collapse panel",
2864
- "explorer.expand": "Expand panel",
2865
- "explorer.maximize": "Maximize files panel",
2866
- "explorer.restore": "Restore panel",
2867
- "explorer.openPreview": "Open preview",
2868
- "explorer.drag.dropHint": "Release to insert the file path",
2869
- "scm.repositories": "Repositories",
2870
- "scm.changes": "Changes",
2871
- "scm.staged": "Staged",
2872
- "scm.unstaged": "Changes",
2873
- "scm.untracked": "Untracked",
2874
- "scm.conflicted": "Conflict",
2875
- "scm.stage": "Stage",
2876
- "scm.unstage": "Unstage",
2877
- "scm.discard": "Discard",
2878
- "scm.stageAll": "Stage all",
2879
- "scm.discardAll": "Discard all",
2880
- "scm.empty": "No changes",
2881
- "scm.notRepo": "Not a git repository",
2882
- "scm.gitMissing": "Git is not installed. Install git and reload to use the changes panel",
2883
- "scm.loading": "Loading status…",
2884
- "scm.failed": "Operation failed",
2885
- "scm.viewList": "List view",
2886
- "scm.viewTree": "Tree view",
2887
- "scm.discardConfirmTracked": "Discard changes in {count} files? This cannot be undone.",
2888
- "scm.discardConfirmUntracked": "Delete {count} untracked files? This cannot be undone.",
2889
- "preview.noTabs": "No open previews",
2890
- "preview.newUrlTab": "New URL preview",
2891
- "preview.collapsePanel": "Collapse preview panel",
2892
- "preview.maximize": "Maximize preview panel",
2893
- "preview.restore": "Restore panel",
2894
- "preview.source": "Source",
2895
- "preview.preview": "Preview",
2896
- "preview.editor": "Editor",
2897
- "preview.split": "Split",
2898
- "preview.refresh": "Refresh",
2899
- "preview.refresh.updated": "File updated on disk",
2900
- "preview.save": "Save",
2901
- "preview.download": "Download",
2902
- "preview.copyCode": "Copy code",
2903
- "preview.copyCodeDone": "Copied",
2904
- "preview.openExternal": "Open in system app",
2905
- "preview.dirty": "Unsaved changes",
2906
- "preview.closeLeft": "Close left",
2907
- "preview.closeRight": "Close right",
2908
- "preview.closeOthers": "Close others",
2909
- "preview.closeAll": "Close all",
2910
- "preview.closeConfirmTitle": "Close unsaved tabs",
2911
- "preview.closeConfirmBody": "{count} tabs have unsaved changes. Closing will lose them.",
2912
- "preview.saved": "Saved",
2913
- "preview.saveConflict": "File changed on disk. Save conflict: refresh and retry",
2914
- "preview.errorOversized": "File too large, only the first 80,000 characters loaded",
2915
- "preview.unsupported": "Preview not supported for this format",
2916
- "preview.downloadHint": "Open in a system app or download to view",
2917
- "preview.url.placeholder": "Enter a URL and press Enter",
2918
- "preview.url.hint": "Press Esc to revert",
2919
- "common.cancel": "Cancel",
2920
- "common.confirm": "OK",
2921
- "common.close": "Close",
2922
- "common.delete": "Delete",
2923
- "common.copyPath": "Copy path",
2924
- "common.copied": "Copied",
2925
- "explorer.menu.copyPath": "Copy path",
2926
- "explorer.menu.copyName": "Copy name",
2927
- "explorer.menu.reveal": "Reveal in file manager",
2928
- "explorer.menu.openWithDefault": "Open with default app",
2929
- "explorer.menu.rename": "Rename",
2930
- "explorer.menu.newFile": "New file",
2931
- "explorer.menu.newFolder": "New folder",
2932
- "explorer.menu.delete": "Delete",
2933
- "explorer.rename.title": "Rename",
2934
- "explorer.newFile.title": "New file",
2935
- "explorer.newFolder.title": "New folder",
2936
- "explorer.deleteConfirmTitle": "Confirm delete",
2937
- "explorer.deleteConfirmBody": "Delete \"{name}\"? This cannot be undone.",
2938
- "explorer.opFailed": "Operation failed",
2939
- "explorer.refToInput": "Reference to input",
2940
- "settings.title": "Right panel",
2941
- "settings.description": "Master switch for the Explorer / Preview and SCM changes panels.",
2942
- "settings.enabled": "Enable the right panel",
2943
- "settings.enabledHint": "When off, the Explorer and Preview columns are not mounted, the floating expand button disappears, the /filemgr routes are not registered, and workspace watching and git polling stop. The rest of the family bundle is unaffected.",
2944
- "settings.overridden": "Overridden",
2945
- "settings.reset": "Reset",
2946
- "settings.notExposed": "This DSH version does not expose this plugin's settings namespace; the form is unavailable. Edit ~/.dsh/settings.yaml directly, or add the namespace to the WEB_SETTINGS_NAMESPACES allowlist of dsh-host-apiproxy and restart.",
2947
- "settings.readOnly": "Settings are read-only in this deployment.",
2948
- "settings.inherit": "Inherit",
2949
- "settings.on": "On",
2950
- "settings.off": "Off",
2951
- "settings.expand": "Expand settings",
2952
- "settings.collapse": "Collapse settings",
2953
- "settings.save": "Save",
2954
- "settings.saving": "Saving…",
2955
- "settings.discard": "Discard",
2956
- "settings.unsaved": "Unsaved",
2957
- "settings.saveFailed": "The deployment rejected these values; your edits are kept.",
2958
- "settings.invalidNumber": "Enter a number, or leave it empty to use the default."
2959
- };
2960
- /** The dictionary namespace this plugin owns. */
2961
- const NS = "filemgr";
2962
- /** Format one copy string with {name} placeholders. */
2963
- function format(template, params) {
2964
- return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
2965
- }
2966
- /** Simple dictionary access (zh/en by a global flag the client sets). */
2967
- const dictionaries = {
2968
- zh,
2969
- en
2970
- };
2971
- let currentLanguage = "zh";
2972
- /** Set the active language (the client mirrors the locale service). */
2973
- function setLanguage(language) {
2974
- currentLanguage = language === "en" ? "en" : "zh";
2975
- }
2976
- /** Translate one key with optional params. */
2977
- function t(key, params) {
2978
- const template = (dictionaries[currentLanguage] ?? zh)[key] ?? zh[key];
2979
- return params === void 0 ? template : format(template, params);
2980
- }
2981
- //#endregion
2982
3009
  //#region src/client/hooks/useStore.ts
2983
3010
  /**
2984
3011
  * React bindings for the framework-free stores: useSyncExternalStore with a
@@ -6144,7 +6171,7 @@ window.__ModuleLoader__.load({
6144
6171
  onClick: handleAddToChat,
6145
6172
  role: "button",
6146
6173
  tabIndex: -1,
6147
- children: "添加到对话"
6174
+ children: t("preview.addToChat")
6148
6175
  }),
6149
6176
  closingIds !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
6150
6177
  title: t("preview.closeConfirmTitle"),
@@ -6169,6 +6196,15 @@ window.__ModuleLoader__.load({
6169
6196
  * failure degrades the panels, never the GUI boot.
6170
6197
  * @module dsh-filemgr/client/mount
6171
6198
  */
6199
+ /**
6200
+ * Wrapper that subscribes to locale revision so createRoot-mounted panels
6201
+ * re-render on language switch (slot outlets do this automatically via
6202
+ * useLocaleRevision; createRoot trees must opt in explicitly).
6203
+ */
6204
+ function LocaleAwareRoot({ children, locale }) {
6205
+ (0, react.useSyncExternalStore)((cb) => locale.subscribe(cb), () => locale.getSnapshot().revision);
6206
+ return children;
6207
+ }
6172
6208
  const EXPLORER_COL_SELECTOR = "[data-filemgr-explorer-col]";
6173
6209
  const PREVIEW_COL_SELECTOR = "[data-filemgr-preview-col]";
6174
6210
  /** Wait for one selector (the shell/frame mounts after boot settlement). */
@@ -6207,23 +6243,29 @@ window.__ModuleLoader__.load({
6207
6243
  * a preview selection (path + start/end line).
6208
6244
  * @returns a disposer unmounting both trees.
6209
6245
  */
6210
- function mountPanels(stores, onToggleExplorer, onReference, onAddFileReference) {
6246
+ function mountPanels(stores, onToggleExplorer, onReference, onAddFileReference, locale) {
6211
6247
  let explorerRoot;
6212
6248
  let previewRoot;
6213
6249
  const disposers = [];
6214
6250
  disposers.push(waitForElement(EXPLORER_COL_SELECTOR, (el) => {
6215
6251
  explorerRoot = (0, react_dom_client.createRoot)(el);
6216
- explorerRoot.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExplorerPanel, {
6217
- stores,
6218
- onToggleCollapse: onToggleExplorer,
6219
- onReference
6252
+ explorerRoot.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocaleAwareRoot, {
6253
+ locale,
6254
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExplorerPanel, {
6255
+ stores,
6256
+ onToggleCollapse: onToggleExplorer,
6257
+ onReference
6258
+ })
6220
6259
  }));
6221
6260
  }));
6222
6261
  disposers.push(waitForElement(PREVIEW_COL_SELECTOR, (el) => {
6223
6262
  previewRoot = (0, react_dom_client.createRoot)(el);
6224
- previewRoot.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreviewPanel, {
6225
- stores,
6226
- onAddFileReference
6263
+ previewRoot.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocaleAwareRoot, {
6264
+ locale,
6265
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PreviewPanel, {
6266
+ stores,
6267
+ onAddFileReference
6268
+ })
6227
6269
  }));
6228
6270
  }));
6229
6271
  return () => {
@@ -6801,11 +6843,11 @@ window.__ModuleLoader__.load({
6801
6843
  }), isDir && isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6802
6844
  className: picker_module_css_default.note,
6803
6845
  style: { paddingLeft: 8 + (depth + 1) * 16 },
6804
- children: "加载中…"
6846
+ children: t("picker.loading")
6805
6847
  }) : children.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6806
6848
  className: picker_module_css_default.note,
6807
6849
  style: { paddingLeft: 8 + (depth + 1) * 16 },
6808
- children: "(空目录)"
6850
+ children: t("picker.emptyDir")
6809
6851
  }) : children.map((child) => renderNode(child, depth + 1)) })] }, entry.path);
6810
6852
  };
6811
6853
  return renderNode;
@@ -6840,12 +6882,12 @@ window.__ModuleLoader__.load({
6840
6882
  className: picker_module_css_default.titleRow,
6841
6883
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6842
6884
  className: picker_module_css_default.title,
6843
- children: "引用项目文件"
6885
+ children: t("picker.title")
6844
6886
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6845
6887
  type: "button",
6846
6888
  className: picker_module_css_default.closeBtn,
6847
6889
  onClick: onClose,
6848
- "aria-label": "关闭",
6890
+ "aria-label": t("picker.closeAria"),
6849
6891
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 16 })
6850
6892
  })]
6851
6893
  }),
@@ -6858,7 +6900,7 @@ window.__ModuleLoader__.load({
6858
6900
  }),
6859
6901
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6860
6902
  className: picker_module_css_default.searchInput,
6861
- placeholder: "搜索工作区文件…",
6903
+ placeholder: t("picker.searchPlaceholder"),
6862
6904
  value: search,
6863
6905
  onChange: (e) => setSearch(e.target.value),
6864
6906
  autoFocus: true
@@ -6867,7 +6909,7 @@ window.__ModuleLoader__.load({
6867
6909
  type: "button",
6868
6910
  className: picker_module_css_default.searchClear,
6869
6911
  onClick: () => setSearch(""),
6870
- "aria-label": "清空",
6912
+ "aria-label": t("picker.clearAria"),
6871
6913
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 13 })
6872
6914
  })
6873
6915
  ]
@@ -6883,10 +6925,10 @@ window.__ModuleLoader__.load({
6883
6925
  className: picker_module_css_default.body,
6884
6926
  children: searchResults !== null ? searching ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6885
6927
  className: picker_module_css_default.note,
6886
- children: "搜索中…"
6928
+ children: t("explorer.search.searching")
6887
6929
  }) : searchResults.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6888
6930
  className: picker_module_css_default.note,
6889
- children: "没有匹配的文件"
6931
+ children: t("explorer.search.empty")
6890
6932
  }) : searchResults.map((hit) => {
6891
6933
  const isSel = selected.has(hit.path);
6892
6934
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6918,10 +6960,10 @@ window.__ModuleLoader__.load({
6918
6960
  }, hit.path);
6919
6961
  }) : rootLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6920
6962
  className: picker_module_css_default.note,
6921
- children: "加载中…"
6963
+ children: t("picker.loading")
6922
6964
  }) : rootEntries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6923
6965
  className: picker_module_css_default.note,
6924
- children: "(空目录)"
6966
+ children: t("picker.emptyDir")
6925
6967
  }) : rootEntries.map((entry) => tree(entry, 0))
6926
6968
  }),
6927
6969
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6930,7 +6972,7 @@ window.__ModuleLoader__.load({
6930
6972
  className: picker_module_css_default.chips,
6931
6973
  children: selectedList.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6932
6974
  className: picker_module_css_default.chipsEmpty,
6933
- children: "选择文件或目录(可多选)"
6975
+ children: t("picker.emptySelection")
6934
6976
  }) : selectedList.map((pick) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6935
6977
  className: picker_module_css_default.chip,
6936
6978
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
@@ -6941,7 +6983,7 @@ window.__ModuleLoader__.load({
6941
6983
  type: "button",
6942
6984
  className: picker_module_css_default.chipX,
6943
6985
  onClick: () => toggleSelect(pick.path, pick.isDir),
6944
- "aria-label": "移除",
6986
+ "aria-label": t("picker.removeAria"),
6945
6987
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 11 })
6946
6988
  })]
6947
6989
  }, pick.path))
@@ -6951,13 +6993,13 @@ window.__ModuleLoader__.load({
6951
6993
  type: "button",
6952
6994
  className: picker_module_css_default.cancelBtn,
6953
6995
  onClick: onClose,
6954
- children: "取消"
6996
+ children: t("common.cancel")
6955
6997
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
6956
6998
  type: "button",
6957
6999
  className: picker_module_css_default.insertBtn,
6958
7000
  disabled: selectedList.length === 0,
6959
7001
  onClick: () => onInsert(selectedList),
6960
- children: ["插入", selectedList.length > 0 ? ` (${selectedList.length})` : ""]
7002
+ children: [t("picker.insert"), selectedList.length > 0 ? ` (${selectedList.length})` : ""]
6961
7003
  })]
6962
7004
  })]
6963
7005
  })
@@ -7341,7 +7383,7 @@ window.__ModuleLoader__.load({
7341
7383
  }, []);
7342
7384
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DockItem, {
7343
7385
  active,
7344
- label: "文件面板",
7386
+ label: t("dock.filePanel.label"),
7345
7387
  onClick: () => window.dispatchEvent(new CustomEvent(EV.toggleFile)),
7346
7388
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
7347
7389
  width: "16",
@@ -7392,6 +7434,7 @@ window.__ModuleLoader__.load({
7392
7434
  }, FilePanelDockItem));
7393
7435
  });
7394
7436
  ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-filemgr: dictionaries");
7437
+ bindT(ctx.locale.bind(NS));
7395
7438
  ctx.effect(() => registerFileReferenceSource(ctx), "dsh-filemgr: file reference source");
7396
7439
  ctx.inject([
7397
7440
  "slots",
@@ -7587,7 +7630,7 @@ window.__ModuleLoader__.load({
7587
7630
  syncLanguage();
7588
7631
  try {
7589
7632
  layout.mount();
7590
- mountPanels(stores, () => layout.toggleExplorer(), onReference, onAddFileReference);
7633
+ mountPanels(stores, () => layout.toggleExplorer(), onReference, onAddFileReference, ctx.locale);
7591
7634
  } catch (error) {
7592
7635
  console.error("[dsh-filemgr] mount failed:", error);
7593
7636
  }