@lijian-ui/dsh-file-manager 0.1.21 → 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 +611 -542
- package/lib/client.js.map +1 -1
- package/lib/tsconfig.client.tsbuildinfo +1 -1
- package/lib/types/client/DockItem.d.ts +15 -0
- package/lib/types/client/DockItem.d.ts.map +1 -0
- package/lib/types/client/FilePanelDockItem.d.ts +16 -0
- package/lib/types/client/FilePanelDockItem.d.ts.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/layout.d.ts +0 -4
- package/lib/types/client/layout.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +21 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/mount.d.ts +2 -1
- package/lib/types/client/mount.d.ts.map +1 -1
- package/lib/types/client/picker/FilePickerModal.d.ts.map +1 -1
- package/lib/types/client/store.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/DockItem.tsx +42 -0
- package/src/client/FilePanelDockItem.tsx +41 -0
- package/src/client/index.ts +23 -2
- package/src/client/layout.ts +31 -54
- package/src/client/locales.ts +46 -0
- package/src/client/mount.tsx +18 -2
- package/src/client/picker/FilePickerModal.tsx +15 -14
- package/src/client/preview/PreviewPanel.tsx +1 -1
- package/src/client/store.ts +2 -1
- package/src/client/styles/tokens.module.css +2 -74
- package/lib/assets/mermaid.min.js +0 -3587
- package/src/client/floating.ts +0 -63
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" ? "
|
|
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,
|
|
@@ -2348,28 +2616,6 @@ window.__ModuleLoader__.load({
|
|
|
2348
2616
|
function maximizedOverlay(availableWidth) {
|
|
2349
2617
|
return availableWidth > 0 && availableWidth < 640;
|
|
2350
2618
|
}
|
|
2351
|
-
/** The WCO titlebar height when visible; 0 in a plain browser tab. */
|
|
2352
|
-
function titlebarAreaHeight() {
|
|
2353
|
-
const wco = navigator.windowControlsOverlay;
|
|
2354
|
-
if (wco === void 0 || !wco.visible || wco.getTitlebarAreaRect === void 0) return 0;
|
|
2355
|
-
try {
|
|
2356
|
-
const height = wco.getTitlebarAreaRect()?.height ?? 0;
|
|
2357
|
-
return height > 0 ? Math.round(height) : 0;
|
|
2358
|
-
} catch {
|
|
2359
|
-
return 0;
|
|
2360
|
-
}
|
|
2361
|
-
}
|
|
2362
|
-
/** Clamp a requested top px into the usable vertical range. */
|
|
2363
|
-
function clampFloatingTop(top, viewportHeight, buttonHeight, titlebar) {
|
|
2364
|
-
const min = titlebar + 6;
|
|
2365
|
-
const max = Math.max(min, viewportHeight - buttonHeight - 6);
|
|
2366
|
-
if (!Number.isFinite(top)) return min;
|
|
2367
|
-
return Math.min(max, Math.max(min, top));
|
|
2368
|
-
}
|
|
2369
|
-
/** The default top: aligned with the collapse chevron at the top-right. */
|
|
2370
|
-
function topAlignedFloatingTop(viewportHeight, buttonHeight, titlebar) {
|
|
2371
|
-
return clampFloatingTop(titlebar + 6, viewportHeight, buttonHeight, titlebar);
|
|
2372
|
-
}
|
|
2373
2619
|
//#endregion
|
|
2374
2620
|
//#region src/client/layout.ts
|
|
2375
2621
|
/**
|
|
@@ -2457,7 +2703,6 @@ window.__ModuleLoader__.load({
|
|
|
2457
2703
|
explorerCol = null;
|
|
2458
2704
|
explorerHandle = null;
|
|
2459
2705
|
previewHandle = null;
|
|
2460
|
-
floatingButton = null;
|
|
2461
2706
|
styleObserver = null;
|
|
2462
2707
|
sizeObserver = null;
|
|
2463
2708
|
waitObserver = null;
|
|
@@ -2519,23 +2764,6 @@ window.__ModuleLoader__.load({
|
|
|
2519
2764
|
this.previewHandle = this.createHandle("filemgr-preview-handle", 20, true, "preview");
|
|
2520
2765
|
frame.appendChild(this.explorerHandle);
|
|
2521
2766
|
frame.appendChild(this.previewHandle);
|
|
2522
|
-
this.floatingButton = document.createElement("button");
|
|
2523
|
-
this.floatingButton.type = "button";
|
|
2524
|
-
this.floatingButton.className = "filemgr-floating-expand";
|
|
2525
|
-
this.floatingButton.setAttribute("aria-label", "Expand explorer");
|
|
2526
|
-
this.floatingButton.innerHTML = "<svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M6 3l5 5-5 5\"/></svg>";
|
|
2527
|
-
this.floatingButton.addEventListener("click", () => {
|
|
2528
|
-
this.toggleExplorer();
|
|
2529
|
-
});
|
|
2530
|
-
document.body.appendChild(this.floatingButton);
|
|
2531
|
-
const overlay = navigator.windowControlsOverlay;
|
|
2532
|
-
if (overlay !== void 0) {
|
|
2533
|
-
const onGeometryChange = () => {
|
|
2534
|
-
this.positionFloatingButton();
|
|
2535
|
-
};
|
|
2536
|
-
overlay.addEventListener("geometrychange", onGeometryChange);
|
|
2537
|
-
this.disposers.push(() => overlay.removeEventListener("geometrychange", onGeometryChange));
|
|
2538
|
-
}
|
|
2539
2767
|
const onKeyDown = (event) => {
|
|
2540
2768
|
if (event.key !== "Escape") return;
|
|
2541
2769
|
const target = event.target instanceof Element ? event.target : null;
|
|
@@ -2552,483 +2780,231 @@ window.__ModuleLoader__.load({
|
|
|
2552
2780
|
if (el === null) return;
|
|
2553
2781
|
const inline = el.style.gridTemplateColumns;
|
|
2554
2782
|
if (inline === "") return;
|
|
2555
|
-
const tracks = parseGridTracks(inline);
|
|
2556
|
-
if (tracks.length >= 2 && tracks.length <= 3) {
|
|
2557
|
-
this.shellTracks = tracks;
|
|
2558
|
-
this.applyGrid();
|
|
2559
|
-
return;
|
|
2560
|
-
}
|
|
2561
|
-
if (tracks.length === 5 && this.shellTracks.length === 3) return;
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
this.
|
|
2584
|
-
}
|
|
2585
|
-
this.sizeObserver
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
el
|
|
2603
|
-
el.
|
|
2604
|
-
el.style.
|
|
2605
|
-
el.style.
|
|
2606
|
-
|
|
2607
|
-
el.
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
this.
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
const
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
const
|
|
2718
|
-
const
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
}
|
|
2723
|
-
this.
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
this.
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
if (
|
|
2758
|
-
if (this.
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
this.explorerCol?.
|
|
2773
|
-
this.
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
this.
|
|
2778
|
-
|
|
2779
|
-
this.sizeObserver?.disconnect();
|
|
2780
|
-
for (const dispose of this.disposers) dispose();
|
|
2781
|
-
this.previewCol?.remove();
|
|
2782
|
-
this.explorerCol?.remove();
|
|
2783
|
-
this.explorerHandle?.remove();
|
|
2784
|
-
this.previewHandle?.remove();
|
|
2785
|
-
this.floatingButton?.remove();
|
|
2786
|
-
if (this.instantTimer !== void 0) clearTimeout(this.instantTimer);
|
|
2787
|
-
if (frameElement === this.frame) frameElement = null;
|
|
2788
|
-
this.frame = null;
|
|
2789
|
-
}
|
|
2790
|
-
};
|
|
2791
|
-
//#endregion
|
|
2792
|
-
//#region src/client/locales.ts
|
|
2793
|
-
/**
|
|
2794
|
-
* Locale strings for the panel surfaces (zh/en). The client registers the
|
|
2795
|
-
* dictionary through the locale service like the sibling plugins; copy is
|
|
2796
|
-
* deliberately short and technical.
|
|
2797
|
-
* @module dsh-filemgr/client/locales
|
|
2798
|
-
*/
|
|
2799
|
-
const zh = {
|
|
2800
|
-
"explorer.tabs.files": "文件",
|
|
2801
|
-
"explorer.tabs.changes": "变更",
|
|
2802
|
-
"explorer.search.placeholder": "按文件名搜索",
|
|
2803
|
-
"explorer.search.searching": "搜索中…",
|
|
2804
|
-
"explorer.search.empty": "没有匹配的文件",
|
|
2805
|
-
"explorer.search.error": "搜索失败",
|
|
2806
|
-
"explorer.search.truncated": "结果过多,仅显示前 {count} 条",
|
|
2807
|
-
"explorer.tree.empty": "项目为空",
|
|
2808
|
-
"explorer.collapse": "收起面板",
|
|
2809
|
-
"explorer.expand": "展开面板",
|
|
2810
|
-
"explorer.maximize": "最大化文件面板",
|
|
2811
|
-
"explorer.restore": "还原面板",
|
|
2812
|
-
"explorer.openPreview": "打开预览",
|
|
2813
|
-
"explorer.drag.dropHint": "松手插入文件路径",
|
|
2814
|
-
"scm.repositories": "存储库",
|
|
2815
|
-
"scm.changes": "变更",
|
|
2816
|
-
"scm.staged": "已暂存",
|
|
2817
|
-
"scm.unstaged": "变更",
|
|
2818
|
-
"scm.untracked": "未跟踪",
|
|
2819
|
-
"scm.conflicted": "冲突",
|
|
2820
|
-
"scm.stage": "暂存",
|
|
2821
|
-
"scm.unstage": "取消暂存",
|
|
2822
|
-
"scm.discard": "放弃更改",
|
|
2823
|
-
"scm.stageAll": "全部暂存",
|
|
2824
|
-
"scm.discardAll": "全部放弃",
|
|
2825
|
-
"scm.empty": "没有更改",
|
|
2826
|
-
"scm.notRepo": "当前目录不是 git 仓库",
|
|
2827
|
-
"scm.gitMissing": "未检测到 git,请先安装 git 后重试",
|
|
2828
|
-
"scm.loading": "读取状态中…",
|
|
2829
|
-
"scm.failed": "操作失败",
|
|
2830
|
-
"scm.viewList": "列表视图",
|
|
2831
|
-
"scm.viewTree": "树视图",
|
|
2832
|
-
"scm.discardConfirmTracked": "放弃对 {count} 个文件的更改?此操作不可恢复。",
|
|
2833
|
-
"scm.discardConfirmUntracked": "删除 {count} 个未跟踪文件?此操作不可恢复。",
|
|
2834
|
-
"preview.noTabs": "没有打开的预览",
|
|
2835
|
-
"preview.newUrlTab": "新建 URL 预览",
|
|
2836
|
-
"preview.collapsePanel": "收起预览面板",
|
|
2837
|
-
"preview.maximize": "最大化预览面板",
|
|
2838
|
-
"preview.restore": "还原面板",
|
|
2839
|
-
"preview.source": "源码",
|
|
2840
|
-
"preview.preview": "预览",
|
|
2841
|
-
"preview.editor": "编辑器",
|
|
2842
|
-
"preview.split": "分屏",
|
|
2843
|
-
"preview.refresh": "刷新",
|
|
2844
|
-
"preview.refresh.updated": "文件已在磁盘更新",
|
|
2845
|
-
"preview.save": "保存",
|
|
2846
|
-
"preview.download": "下载",
|
|
2847
|
-
"preview.copyCode": "复制代码",
|
|
2848
|
-
"preview.copyCodeDone": "已复制",
|
|
2849
|
-
"preview.openExternal": "在系统应用中打开",
|
|
2850
|
-
"preview.dirty": "未保存的更改",
|
|
2851
|
-
"preview.closeLeft": "关闭左侧",
|
|
2852
|
-
"preview.closeRight": "关闭右侧",
|
|
2853
|
-
"preview.closeOthers": "关闭其他",
|
|
2854
|
-
"preview.closeAll": "关闭全部",
|
|
2855
|
-
"preview.closeConfirmTitle": "关闭未保存的标签页",
|
|
2856
|
-
"preview.closeConfirmBody": "{count} 个标签页有未保存的更改,关闭将丢失这些更改。",
|
|
2857
|
-
"preview.saved": "已保存",
|
|
2858
|
-
"preview.saveConflict": "文件已在磁盘上被修改,保存冲突:请刷新后重试",
|
|
2859
|
-
"preview.errorOversized": "文件过大,仅加载前 80,000 字符",
|
|
2860
|
-
"preview.unsupported": "此格式暂不支持预览",
|
|
2861
|
-
"preview.downloadHint": "可在系统应用中打开或下载查看",
|
|
2862
|
-
"preview.url.placeholder": "输入网址,回车打开",
|
|
2863
|
-
"preview.url.hint": "按 Esc 还原",
|
|
2864
|
-
"common.cancel": "取消",
|
|
2865
|
-
"common.confirm": "确定",
|
|
2866
|
-
"common.close": "关闭",
|
|
2867
|
-
"common.delete": "删除",
|
|
2868
|
-
"common.copyPath": "复制路径",
|
|
2869
|
-
"common.copied": "已复制",
|
|
2870
|
-
"explorer.menu.copyPath": "复制路径",
|
|
2871
|
-
"explorer.menu.copyName": "复制名称",
|
|
2872
|
-
"explorer.menu.reveal": "在文件管理器中显示",
|
|
2873
|
-
"explorer.menu.openWithDefault": "用默认应用打开",
|
|
2874
|
-
"explorer.menu.rename": "重命名",
|
|
2875
|
-
"explorer.menu.newFile": "新建文件",
|
|
2876
|
-
"explorer.menu.newFolder": "新建文件夹",
|
|
2877
|
-
"explorer.menu.delete": "删除",
|
|
2878
|
-
"explorer.rename.title": "重命名",
|
|
2879
|
-
"explorer.newFile.title": "新建文件",
|
|
2880
|
-
"explorer.newFolder.title": "新建文件夹",
|
|
2881
|
-
"explorer.deleteConfirmTitle": "删除确认",
|
|
2882
|
-
"explorer.deleteConfirmBody": "确定要删除「{name}」吗?此操作不可恢复。",
|
|
2883
|
-
"explorer.opFailed": "操作失败",
|
|
2884
|
-
"explorer.refToInput": "引用到输入框",
|
|
2885
|
-
"settings.title": "右侧面板",
|
|
2886
|
-
"settings.description": "Explorer / Preview 与 SCM 变更面板的总开关。",
|
|
2887
|
-
"settings.enabled": "启用右侧面板",
|
|
2888
|
-
"settings.enabledHint": "关闭后不再挂载右侧文件树与预览列、不显示浮动展开按钮、不注册 /filemgr 路由,也不做工作区监视与 git 轮询;聚合包其余插件不受影响。",
|
|
2889
|
-
"settings.overridden": "已覆盖",
|
|
2890
|
-
"settings.reset": "恢复默认",
|
|
2891
|
-
"settings.notExposed": "当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。",
|
|
2892
|
-
"settings.readOnly": "当前部署的设置只读。",
|
|
2893
|
-
"settings.inherit": "继承",
|
|
2894
|
-
"settings.on": "开",
|
|
2895
|
-
"settings.off": "关",
|
|
2896
|
-
"settings.expand": "展开设置",
|
|
2897
|
-
"settings.collapse": "收起设置",
|
|
2898
|
-
"settings.save": "保存",
|
|
2899
|
-
"settings.saving": "保存中…",
|
|
2900
|
-
"settings.discard": "放弃",
|
|
2901
|
-
"settings.unsaved": "未保存",
|
|
2902
|
-
"settings.saveFailed": "部署未接受这些值,已保留供你修改。",
|
|
2903
|
-
"settings.invalidNumber": "请输入数字,留空则使用默认值。"
|
|
2904
|
-
};
|
|
2905
|
-
const en = {
|
|
2906
|
-
"explorer.tabs.files": "Files",
|
|
2907
|
-
"explorer.tabs.changes": "Changes",
|
|
2908
|
-
"explorer.search.placeholder": "Search file names",
|
|
2909
|
-
"explorer.search.searching": "Searching…",
|
|
2910
|
-
"explorer.search.empty": "No matching files",
|
|
2911
|
-
"explorer.search.error": "Search failed",
|
|
2912
|
-
"explorer.search.truncated": "Too many results, showing first {count}",
|
|
2913
|
-
"explorer.tree.empty": "The project is empty",
|
|
2914
|
-
"explorer.collapse": "Collapse panel",
|
|
2915
|
-
"explorer.expand": "Expand panel",
|
|
2916
|
-
"explorer.maximize": "Maximize files panel",
|
|
2917
|
-
"explorer.restore": "Restore panel",
|
|
2918
|
-
"explorer.openPreview": "Open preview",
|
|
2919
|
-
"explorer.drag.dropHint": "Release to insert the file path",
|
|
2920
|
-
"scm.repositories": "Repositories",
|
|
2921
|
-
"scm.changes": "Changes",
|
|
2922
|
-
"scm.staged": "Staged",
|
|
2923
|
-
"scm.unstaged": "Changes",
|
|
2924
|
-
"scm.untracked": "Untracked",
|
|
2925
|
-
"scm.conflicted": "Conflict",
|
|
2926
|
-
"scm.stage": "Stage",
|
|
2927
|
-
"scm.unstage": "Unstage",
|
|
2928
|
-
"scm.discard": "Discard",
|
|
2929
|
-
"scm.stageAll": "Stage all",
|
|
2930
|
-
"scm.discardAll": "Discard all",
|
|
2931
|
-
"scm.empty": "No changes",
|
|
2932
|
-
"scm.notRepo": "Not a git repository",
|
|
2933
|
-
"scm.gitMissing": "Git is not installed. Install git and reload to use the changes panel",
|
|
2934
|
-
"scm.loading": "Loading status…",
|
|
2935
|
-
"scm.failed": "Operation failed",
|
|
2936
|
-
"scm.viewList": "List view",
|
|
2937
|
-
"scm.viewTree": "Tree view",
|
|
2938
|
-
"scm.discardConfirmTracked": "Discard changes in {count} files? This cannot be undone.",
|
|
2939
|
-
"scm.discardConfirmUntracked": "Delete {count} untracked files? This cannot be undone.",
|
|
2940
|
-
"preview.noTabs": "No open previews",
|
|
2941
|
-
"preview.newUrlTab": "New URL preview",
|
|
2942
|
-
"preview.collapsePanel": "Collapse preview panel",
|
|
2943
|
-
"preview.maximize": "Maximize preview panel",
|
|
2944
|
-
"preview.restore": "Restore panel",
|
|
2945
|
-
"preview.source": "Source",
|
|
2946
|
-
"preview.preview": "Preview",
|
|
2947
|
-
"preview.editor": "Editor",
|
|
2948
|
-
"preview.split": "Split",
|
|
2949
|
-
"preview.refresh": "Refresh",
|
|
2950
|
-
"preview.refresh.updated": "File updated on disk",
|
|
2951
|
-
"preview.save": "Save",
|
|
2952
|
-
"preview.download": "Download",
|
|
2953
|
-
"preview.copyCode": "Copy code",
|
|
2954
|
-
"preview.copyCodeDone": "Copied",
|
|
2955
|
-
"preview.openExternal": "Open in system app",
|
|
2956
|
-
"preview.dirty": "Unsaved changes",
|
|
2957
|
-
"preview.closeLeft": "Close left",
|
|
2958
|
-
"preview.closeRight": "Close right",
|
|
2959
|
-
"preview.closeOthers": "Close others",
|
|
2960
|
-
"preview.closeAll": "Close all",
|
|
2961
|
-
"preview.closeConfirmTitle": "Close unsaved tabs",
|
|
2962
|
-
"preview.closeConfirmBody": "{count} tabs have unsaved changes. Closing will lose them.",
|
|
2963
|
-
"preview.saved": "Saved",
|
|
2964
|
-
"preview.saveConflict": "File changed on disk. Save conflict: refresh and retry",
|
|
2965
|
-
"preview.errorOversized": "File too large, only the first 80,000 characters loaded",
|
|
2966
|
-
"preview.unsupported": "Preview not supported for this format",
|
|
2967
|
-
"preview.downloadHint": "Open in a system app or download to view",
|
|
2968
|
-
"preview.url.placeholder": "Enter a URL and press Enter",
|
|
2969
|
-
"preview.url.hint": "Press Esc to revert",
|
|
2970
|
-
"common.cancel": "Cancel",
|
|
2971
|
-
"common.confirm": "OK",
|
|
2972
|
-
"common.close": "Close",
|
|
2973
|
-
"common.delete": "Delete",
|
|
2974
|
-
"common.copyPath": "Copy path",
|
|
2975
|
-
"common.copied": "Copied",
|
|
2976
|
-
"explorer.menu.copyPath": "Copy path",
|
|
2977
|
-
"explorer.menu.copyName": "Copy name",
|
|
2978
|
-
"explorer.menu.reveal": "Reveal in file manager",
|
|
2979
|
-
"explorer.menu.openWithDefault": "Open with default app",
|
|
2980
|
-
"explorer.menu.rename": "Rename",
|
|
2981
|
-
"explorer.menu.newFile": "New file",
|
|
2982
|
-
"explorer.menu.newFolder": "New folder",
|
|
2983
|
-
"explorer.menu.delete": "Delete",
|
|
2984
|
-
"explorer.rename.title": "Rename",
|
|
2985
|
-
"explorer.newFile.title": "New file",
|
|
2986
|
-
"explorer.newFolder.title": "New folder",
|
|
2987
|
-
"explorer.deleteConfirmTitle": "Confirm delete",
|
|
2988
|
-
"explorer.deleteConfirmBody": "Delete \"{name}\"? This cannot be undone.",
|
|
2989
|
-
"explorer.opFailed": "Operation failed",
|
|
2990
|
-
"explorer.refToInput": "Reference to input",
|
|
2991
|
-
"settings.title": "Right panel",
|
|
2992
|
-
"settings.description": "Master switch for the Explorer / Preview and SCM changes panels.",
|
|
2993
|
-
"settings.enabled": "Enable the right panel",
|
|
2994
|
-
"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.",
|
|
2995
|
-
"settings.overridden": "Overridden",
|
|
2996
|
-
"settings.reset": "Reset",
|
|
2997
|
-
"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.",
|
|
2998
|
-
"settings.readOnly": "Settings are read-only in this deployment.",
|
|
2999
|
-
"settings.inherit": "Inherit",
|
|
3000
|
-
"settings.on": "On",
|
|
3001
|
-
"settings.off": "Off",
|
|
3002
|
-
"settings.expand": "Expand settings",
|
|
3003
|
-
"settings.collapse": "Collapse settings",
|
|
3004
|
-
"settings.save": "Save",
|
|
3005
|
-
"settings.saving": "Saving…",
|
|
3006
|
-
"settings.discard": "Discard",
|
|
3007
|
-
"settings.unsaved": "Unsaved",
|
|
3008
|
-
"settings.saveFailed": "The deployment rejected these values; your edits are kept.",
|
|
3009
|
-
"settings.invalidNumber": "Enter a number, or leave it empty to use the default."
|
|
3010
|
-
};
|
|
3011
|
-
/** The dictionary namespace this plugin owns. */
|
|
3012
|
-
const NS = "filemgr";
|
|
3013
|
-
/** Format one copy string with {name} placeholders. */
|
|
3014
|
-
function format(template, params) {
|
|
3015
|
-
return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
|
|
3016
|
-
}
|
|
3017
|
-
/** Simple dictionary access (zh/en by a global flag the client sets). */
|
|
3018
|
-
const dictionaries = {
|
|
3019
|
-
zh,
|
|
3020
|
-
en
|
|
2783
|
+
const tracks = parseGridTracks(inline);
|
|
2784
|
+
if (tracks.length >= 2 && tracks.length <= 3) {
|
|
2785
|
+
this.shellTracks = tracks;
|
|
2786
|
+
this.applyGrid();
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
if (tracks.length === 5 && this.shellTracks.length === 3) return;
|
|
2790
|
+
if (tracks.length === 6 && tracks.includes("[dsh-term]") && this.shellTracks.length === 3) {
|
|
2791
|
+
this.applyGrid();
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
this.styleObserver = new MutationObserver(syncGrid);
|
|
2796
|
+
this.styleObserver.observe(frame, {
|
|
2797
|
+
attributes: true,
|
|
2798
|
+
attributeFilter: ["style"]
|
|
2799
|
+
});
|
|
2800
|
+
const measure = () => {
|
|
2801
|
+
if (this.frame === null) return;
|
|
2802
|
+
this.frameWidth = this.frame.getBoundingClientRect().width;
|
|
2803
|
+
const sidebar = this.shellTracks.length >= 1 ? trackPx(this.shellTracks[0]) : 0;
|
|
2804
|
+
const details = this.shellTracks.length >= 3 ? trackPx(this.shellTracks[2]) : 0;
|
|
2805
|
+
const available = Math.max(0, this.frameWidth - sidebar - details);
|
|
2806
|
+
const state = this.layout.getSnapshot();
|
|
2807
|
+
if (Math.abs(state.availableWidth - available) > .5) this.layout.update((prev) => ({
|
|
2808
|
+
...prev,
|
|
2809
|
+
availableWidth: available
|
|
2810
|
+
}));
|
|
2811
|
+
this.layout.shrinkToFit(this.layout.getSnapshot());
|
|
2812
|
+
};
|
|
2813
|
+
this.sizeObserver = new ResizeObserver(() => {
|
|
2814
|
+
measure();
|
|
2815
|
+
this.applyGrid();
|
|
2816
|
+
});
|
|
2817
|
+
this.sizeObserver.observe(frame);
|
|
2818
|
+
this.disposers.push(this.layout.subscribe(() => this.applyGrid()));
|
|
2819
|
+
const initial = frame.style.gridTemplateColumns;
|
|
2820
|
+
if (initial !== "") {
|
|
2821
|
+
const tracks = parseGridTracks(initial);
|
|
2822
|
+
if (tracks.length >= 2 && tracks.length <= 3) this.shellTracks = tracks;
|
|
2823
|
+
else if (tracks.length === 5 && trackPx(tracks[0]) > 0) this.shellTracks = tracks.slice(0, 3);
|
|
2824
|
+
}
|
|
2825
|
+
measure();
|
|
2826
|
+
this.applyGrid();
|
|
2827
|
+
}
|
|
2828
|
+
/** Create one drag handle element with its pointer wiring. */
|
|
2829
|
+
createHandle(className, hitWidth, reverse, kind) {
|
|
2830
|
+
const el = document.createElement("div");
|
|
2831
|
+
el.className = className;
|
|
2832
|
+
el.style.position = "absolute";
|
|
2833
|
+
el.style.top = "0";
|
|
2834
|
+
el.style.bottom = "0";
|
|
2835
|
+
el.style.zIndex = "30";
|
|
2836
|
+
el.style.cursor = "col-resize";
|
|
2837
|
+
el.style.width = `${hitWidth}px`;
|
|
2838
|
+
if (reverse) el.style.marginLeft = `-${hitWidth}px`;
|
|
2839
|
+
el.addEventListener("pointerdown", (event) => {
|
|
2840
|
+
const isExplorer = kind === "explorer";
|
|
2841
|
+
handlePointerDragStart(event, el, {
|
|
2842
|
+
reverse,
|
|
2843
|
+
getStartWidth: () => {
|
|
2844
|
+
const state = this.layout.getSnapshot();
|
|
2845
|
+
return isExplorer ? state.explorerWidth : state.previewWidth;
|
|
2846
|
+
},
|
|
2847
|
+
compute: (startWidth, deltaX) => dragTargetWidth(isExplorer ? "explorer" : "preview", startWidth, deltaX, this.layout.getSnapshot()),
|
|
2848
|
+
onFrame: (width) => {
|
|
2849
|
+
this.layout.update((prev) => isExplorer ? {
|
|
2850
|
+
...prev,
|
|
2851
|
+
explorerWidth: width
|
|
2852
|
+
} : {
|
|
2853
|
+
...prev,
|
|
2854
|
+
previewWidth: width
|
|
2855
|
+
});
|
|
2856
|
+
},
|
|
2857
|
+
onEnd: (width) => {
|
|
2858
|
+
writeStoredNumber(isExplorer ? KEY_EXPLORER_WIDTH : KEY_PREVIEW_WIDTH, width);
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
});
|
|
2862
|
+
el.addEventListener("dblclick", () => {
|
|
2863
|
+
this.instant(() => {
|
|
2864
|
+
const width = kind === "explorer" ? 260 : 480;
|
|
2865
|
+
this.layout.update((prev) => kind === "explorer" ? {
|
|
2866
|
+
...prev,
|
|
2867
|
+
explorerWidth: width
|
|
2868
|
+
} : {
|
|
2869
|
+
...prev,
|
|
2870
|
+
previewWidth: width
|
|
2871
|
+
});
|
|
2872
|
+
writeStoredNumber(kind === "explorer" ? KEY_EXPLORER_WIDTH : KEY_PREVIEW_WIDTH, width);
|
|
2873
|
+
this.applyGrid();
|
|
2874
|
+
});
|
|
2875
|
+
});
|
|
2876
|
+
return el;
|
|
2877
|
+
}
|
|
2878
|
+
/** Toggle explorer collapse (width 0, kept mounted; no transition). */
|
|
2879
|
+
toggleExplorer() {
|
|
2880
|
+
const state = this.layout.getSnapshot();
|
|
2881
|
+
const next = !state.explorerCollapsed;
|
|
2882
|
+
this.instant(() => {
|
|
2883
|
+
this.layout.update((prev) => ({
|
|
2884
|
+
...prev,
|
|
2885
|
+
explorerCollapsed: next
|
|
2886
|
+
}));
|
|
2887
|
+
try {
|
|
2888
|
+
localStorage.setItem(`project-panel-collapse:${state.root}`, next ? "collapsed" : "expanded");
|
|
2889
|
+
} catch {}
|
|
2890
|
+
this.applyGrid();
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
/** Toggle the preview region (open = tabs exist; close keeps tabs). */
|
|
2894
|
+
setPreviewOpen(open) {
|
|
2895
|
+
this.instant(() => {
|
|
2896
|
+
this.layout.update((prev) => ({
|
|
2897
|
+
...prev,
|
|
2898
|
+
previewOpen: open
|
|
2899
|
+
}));
|
|
2900
|
+
this.applyGrid();
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Locate the shell conversation header: its bottom border is the
|
|
2905
|
+
* horizontal divider under the "Session log" row the button should
|
|
2906
|
+
* sit below. Resolved per call (the shell may mount it late); null when
|
|
2907
|
+
* the shell has no header (standalone installs, desktop variants).
|
|
2908
|
+
*/
|
|
2909
|
+
findHeaderBottom() {
|
|
2910
|
+
const frame = this.frame;
|
|
2911
|
+
if (frame === null) return null;
|
|
2912
|
+
const header = frame.querySelector("[data-pane=\"conversation\"] header, [class*=\"centerCol\"] header");
|
|
2913
|
+
if (header === null) return null;
|
|
2914
|
+
const bottom = header.getBoundingClientRect().bottom;
|
|
2915
|
+
return Number.isFinite(bottom) ? bottom : null;
|
|
2916
|
+
}
|
|
2917
|
+
/** Apply one store update with transitions disabled for exactly one frame. */
|
|
2918
|
+
instant(fn) {
|
|
2919
|
+
const frame = this.frame;
|
|
2920
|
+
if (frame === null) {
|
|
2921
|
+
fn();
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
frame.setAttribute("data-filemgr-instant", "");
|
|
2925
|
+
if (this.instantTimer !== void 0) clearTimeout(this.instantTimer);
|
|
2926
|
+
this.instantTimer = setTimeout(() => {
|
|
2927
|
+
this.instantTimer = void 0;
|
|
2928
|
+
frame.removeAttribute("data-filemgr-instant");
|
|
2929
|
+
}, 0);
|
|
2930
|
+
fn();
|
|
2931
|
+
}
|
|
2932
|
+
/** Re-write the frame grid and reposition handles + floating button. */
|
|
2933
|
+
applyGrid() {
|
|
2934
|
+
const frame = this.frame;
|
|
2935
|
+
if (frame === null) return;
|
|
2936
|
+
if (this.shellTracks.length !== 3) return;
|
|
2937
|
+
const state = this.layout.getSnapshot();
|
|
2938
|
+
const width = this.frameWidth > 0 ? this.frameWidth : frame.getBoundingClientRect().width;
|
|
2939
|
+
if (state.maximized !== null) {
|
|
2940
|
+
this.applyMaximized(frame, state.maximized, width);
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
this.clearMaximizedChrome();
|
|
2944
|
+
const explorer = this.layout.explorerWidthPx(state);
|
|
2945
|
+
const preview = this.layout.previewWidthPx(state);
|
|
2946
|
+
const dshTermCol = parseGridTracks(frame.style.gridTemplateColumns).includes("[dsh-term]") ? frame.querySelector("[data-dsh-term-col]") : null;
|
|
2947
|
+
const dshTermOpen = dshTermCol !== null && dshTermCol.style.display !== "none" && dshTermCol.getBoundingClientRect().width > 0;
|
|
2948
|
+
const dshTermWidth = dshTermOpen ? Math.round(dshTermCol.getBoundingClientRect().width) : 0;
|
|
2949
|
+
const baseGrid = `${this.shellTracks[0]} minmax(0, 1fr) ${this.shellTracks[2]} ${Math.round(preview)}px ${Math.round(explorer)}px`;
|
|
2950
|
+
frame.style.gridTemplateColumns = dshTermOpen ? `${baseGrid} [dsh-term] ${dshTermWidth}px` : baseGrid;
|
|
2951
|
+
if (this.explorerCol !== null) this.explorerCol.style.visibility = explorer > 0 ? "visible" : "hidden";
|
|
2952
|
+
if (this.previewCol !== null) this.previewCol.style.visibility = preview > 0 ? "visible" : "hidden";
|
|
2953
|
+
if (this.explorerHandle !== null) {
|
|
2954
|
+
const left = Math.round(width - explorer - dshTermWidth);
|
|
2955
|
+
this.explorerHandle.style.left = `${left}px`;
|
|
2956
|
+
this.explorerHandle.style.marginLeft = `${-12 / 2}px`;
|
|
2957
|
+
this.explorerHandle.style.display = explorer > 0 && state.root !== "" ? "block" : "none";
|
|
2958
|
+
}
|
|
2959
|
+
if (this.previewHandle !== null) {
|
|
2960
|
+
const left = Math.round(width - explorer - preview - dshTermWidth);
|
|
2961
|
+
this.previewHandle.style.left = `${left}px`;
|
|
2962
|
+
this.previewHandle.style.display = preview > 0 && state.root !== "" ? "block" : "none";
|
|
2963
|
+
}
|
|
2964
|
+
const detailsTrack = trackPx(this.shellTracks[2]);
|
|
2965
|
+
if (this.detailsHandle === null || !this.detailsHandle.isConnected) this.detailsHandle = frame.querySelector("[data-side=\"details\"]");
|
|
2966
|
+
if (this.detailsHandle !== null) this.detailsHandle.style.left = `${Math.round(width - detailsTrack - preview - explorer - dshTermWidth)}px`;
|
|
2967
|
+
}
|
|
2968
|
+
/**
|
|
2969
|
+
* Maximize layout: the target column takes over the whole frame row (the
|
|
2970
|
+
* other tracks collapse to 0px). On narrow viewports the takeover grid is
|
|
2971
|
+
* skipped and the column renders as a fixed full-screen overlay instead
|
|
2972
|
+
* (issue #315). Everything stays mounted — only geometry changes.
|
|
2973
|
+
*/
|
|
2974
|
+
applyMaximized(frame, target, width) {
|
|
2975
|
+
const overlay = maximizedOverlay(this.layout.getSnapshot().availableWidth);
|
|
2976
|
+
if (!overlay) frame.style.gridTemplateColumns = maximizedGridTracks(target, width);
|
|
2977
|
+
if (this.explorerCol !== null) {
|
|
2978
|
+
this.explorerCol.style.visibility = target === "explorer" ? "visible" : "hidden";
|
|
2979
|
+
this.explorerCol.classList.toggle("filemgr-maximized", target === "explorer" && overlay);
|
|
2980
|
+
}
|
|
2981
|
+
if (this.previewCol !== null) {
|
|
2982
|
+
this.previewCol.style.visibility = target === "preview" ? "visible" : "hidden";
|
|
2983
|
+
this.previewCol.classList.toggle("filemgr-maximized", target === "preview" && overlay);
|
|
2984
|
+
}
|
|
2985
|
+
if (this.explorerHandle !== null) this.explorerHandle.style.display = "none";
|
|
2986
|
+
if (this.previewHandle !== null) this.previewHandle.style.display = "none";
|
|
2987
|
+
}
|
|
2988
|
+
/** Remove the narrow-screen overlay class from both columns. */
|
|
2989
|
+
clearMaximizedChrome() {
|
|
2990
|
+
this.explorerCol?.classList.remove("filemgr-maximized");
|
|
2991
|
+
this.previewCol?.classList.remove("filemgr-maximized");
|
|
2992
|
+
}
|
|
2993
|
+
/** Detach everything (plugin unload). */
|
|
2994
|
+
dispose() {
|
|
2995
|
+
this.waitObserver?.disconnect();
|
|
2996
|
+
this.styleObserver?.disconnect();
|
|
2997
|
+
this.sizeObserver?.disconnect();
|
|
2998
|
+
for (const dispose of this.disposers) dispose();
|
|
2999
|
+
this.previewCol?.remove();
|
|
3000
|
+
this.explorerCol?.remove();
|
|
3001
|
+
this.explorerHandle?.remove();
|
|
3002
|
+
this.previewHandle?.remove();
|
|
3003
|
+
if (this.instantTimer !== void 0) clearTimeout(this.instantTimer);
|
|
3004
|
+
if (frameElement === this.frame) frameElement = null;
|
|
3005
|
+
this.frame = null;
|
|
3006
|
+
}
|
|
3021
3007
|
};
|
|
3022
|
-
let currentLanguage = "zh";
|
|
3023
|
-
/** Set the active language (the client mirrors the locale service). */
|
|
3024
|
-
function setLanguage(language) {
|
|
3025
|
-
currentLanguage = language === "en" ? "en" : "zh";
|
|
3026
|
-
}
|
|
3027
|
-
/** Translate one key with optional params. */
|
|
3028
|
-
function t(key, params) {
|
|
3029
|
-
const template = (dictionaries[currentLanguage] ?? zh)[key] ?? zh[key];
|
|
3030
|
-
return params === void 0 ? template : format(template, params);
|
|
3031
|
-
}
|
|
3032
3008
|
//#endregion
|
|
3033
3009
|
//#region src/client/hooks/useStore.ts
|
|
3034
3010
|
/**
|
|
@@ -4104,7 +4080,7 @@ window.__ModuleLoader__.load({
|
|
|
4104
4080
|
};
|
|
4105
4081
|
//#endregion
|
|
4106
4082
|
//#region \0dsh-css:src/client/styles/tokens.module.css.mjs
|
|
4107
|
-
const css$4 = ":root{--aion-bg-base:#fff;--aion-bg-1:#f9fafb;--aion-bg-2:#f2f3f5;--aion-bg-3:#e5e6eb;--aion-bg-hover:#f3f4f6;--aion-bg-active:#e5e6eb;--aion-text-primary:#000;--aion-text-secondary:#454d5f;--aion-text-tertiary:#86909c;--aion-text-disabled:#c9cdd4;--aion-primary:#165dff;--aion-success:#00b42a;--aion-warning:#ff7d00;--aion-danger:#f53f3f;--aion-brand:#7583b2;--aion-aou-1:#eff0f6;--aion-aou-2:#e5e7f0;--aion-aou-3:#d1d5e5;--aion-aou-4:#b5bcd6;--aion-aou-5:#97a0c5;--aion-aou-6:#7583b2;--aion-fill-2:#f2f3f5;--aion-fill-3:#e5e6eb;--aion-border-base:#e5e6eb;--aion-overlay-shadow:0 8px 24px #0f172a1f;--aion-font-sans:-apple-system, \"system-ui\", \"Segoe UI\", Roboto, \"Helvetica Neue\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif;--aion-font-mono:ui-monospace, \"SF Mono\", SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace}body[data-ds-dark-theme]{--aion-bg-base:#0e0e0e;--aion-bg-1:#1a1a1a;--aion-bg-2:#262626;--aion-bg-3:#333;--aion-bg-hover:#1f1f1f;--aion-bg-active:#2d2d2d;--aion-text-primary:#fff;--aion-text-secondary:#ced3da;--aion-text-tertiary:#737373;--aion-text-disabled:#737373;--aion-primary:#4d9fff;--aion-success:#23c343;--aion-warning:#ff9a2e;--aion-danger:#f76560;--aion-brand:#a1aacb;--aion-aou-1:#2a2a2a;--aion-aou-2:#3d4150;--aion-aou-3:#525a77;--aion-aou-4:#6a749b;--aion-aou-5:#838fba;--aion-aou-6:#a1aacb;--aion-fill-2:#ffffff14;--aion-fill-3:#ffffff1f;--aion-border-base:#333;--aion-overlay-shadow:0 12px 32px #00000073;color-scheme:dark}.filemgr-root{font-family:var(--aion-font-sans);color:var(--aion-text-primary);background-color:var(--aion-bg-1);font-size:13px}.filemgr-root *,.filemgr-root :before,.filemgr-root :after{box-sizing:border-box}.filemgr-root ::-webkit-scrollbar{width:8px;height:8px}.filemgr-root ::-webkit-scrollbar-thumb{background:var(--aion-bg-3);border-radius:4px}.filemgr-root ::-webkit-scrollbar-thumb:hover{background:var(--aion-bg-4,#c9cdd4)}.filemgr-root ::-webkit-scrollbar-track{background:0 0}@media (prefers-reduced-motion:reduce){.filemgr-root *,.filemgr-root :before,.filemgr-root :after{transition:none!important;animation:none!important}}[data-dsh-frame][data-filemgr-instant]{transition:none!important}.filemgr-preview-col,.filemgr-explorer-col{background-color:var(--aion-bg-1);z-index:30;flex-direction:column;min-width:0;display:flex;overflow:hidden}.filemgr-preview-col{border-left:1px solid var(--aion-bg-3)}.filemgr-preview-col.filemgr-maximized,.filemgr-explorer-col.filemgr-maximized{z-index:60;inset:0;position:fixed!important}.filemgr-explorer-col{border-left:1px solid var(--aion-bg-3)}.filemgr-preview-col.filemgr-preview-enter{animation:.25s cubic-bezier(.4,0,.2,1) aPVr2q_filemgr-preview-enter}@keyframes aPVr2q_filemgr-preview-enter{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.filemgr-explorer-handle,.filemgr-preview-handle{touch-action:none
|
|
4083
|
+
const css$4 = ":root{--aion-bg-base:#fff;--aion-bg-1:#f9fafb;--aion-bg-2:#f2f3f5;--aion-bg-3:#e5e6eb;--aion-bg-hover:#f3f4f6;--aion-bg-active:#e5e6eb;--aion-text-primary:#000;--aion-text-secondary:#454d5f;--aion-text-tertiary:#86909c;--aion-text-disabled:#c9cdd4;--aion-primary:#165dff;--aion-success:#00b42a;--aion-warning:#ff7d00;--aion-danger:#f53f3f;--aion-brand:#7583b2;--aion-aou-1:#eff0f6;--aion-aou-2:#e5e7f0;--aion-aou-3:#d1d5e5;--aion-aou-4:#b5bcd6;--aion-aou-5:#97a0c5;--aion-aou-6:#7583b2;--aion-fill-2:#f2f3f5;--aion-fill-3:#e5e6eb;--aion-border-base:#e5e6eb;--aion-overlay-shadow:0 8px 24px #0f172a1f;--aion-font-sans:-apple-system, \"system-ui\", \"Segoe UI\", Roboto, \"Helvetica Neue\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif;--aion-font-mono:ui-monospace, \"SF Mono\", SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace}body[data-ds-dark-theme]{--aion-bg-base:#0e0e0e;--aion-bg-1:#1a1a1a;--aion-bg-2:#262626;--aion-bg-3:#333;--aion-bg-hover:#1f1f1f;--aion-bg-active:#2d2d2d;--aion-text-primary:#fff;--aion-text-secondary:#ced3da;--aion-text-tertiary:#737373;--aion-text-disabled:#737373;--aion-primary:#4d9fff;--aion-success:#23c343;--aion-warning:#ff9a2e;--aion-danger:#f76560;--aion-brand:#a1aacb;--aion-aou-1:#2a2a2a;--aion-aou-2:#3d4150;--aion-aou-3:#525a77;--aion-aou-4:#6a749b;--aion-aou-5:#838fba;--aion-aou-6:#a1aacb;--aion-fill-2:#ffffff14;--aion-fill-3:#ffffff1f;--aion-border-base:#333;--aion-overlay-shadow:0 12px 32px #00000073;color-scheme:dark}.filemgr-root{font-family:var(--aion-font-sans);color:var(--aion-text-primary);background-color:var(--aion-bg-1);font-size:13px}.filemgr-root *,.filemgr-root :before,.filemgr-root :after{box-sizing:border-box}.filemgr-root ::-webkit-scrollbar{width:8px;height:8px}.filemgr-root ::-webkit-scrollbar-thumb{background:var(--aion-bg-3);border-radius:4px}.filemgr-root ::-webkit-scrollbar-thumb:hover{background:var(--aion-bg-4,#c9cdd4)}.filemgr-root ::-webkit-scrollbar-track{background:0 0}@media (prefers-reduced-motion:reduce){.filemgr-root *,.filemgr-root :before,.filemgr-root :after{transition:none!important;animation:none!important}}[data-dsh-frame][data-filemgr-instant]{transition:none!important}.filemgr-preview-col,.filemgr-explorer-col{background-color:var(--aion-bg-1);z-index:30;flex-direction:column;min-width:0;display:flex;overflow:hidden}.filemgr-preview-col{border-left:1px solid var(--aion-bg-3)}.filemgr-preview-col.filemgr-maximized,.filemgr-explorer-col.filemgr-maximized{z-index:60;inset:0;position:fixed!important}.filemgr-explorer-col{border-left:1px solid var(--aion-bg-3)}.filemgr-preview-col.filemgr-preview-enter{animation:.25s cubic-bezier(.4,0,.2,1) aPVr2q_filemgr-preview-enter}@keyframes aPVr2q_filemgr-preview-enter{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.filemgr-explorer-handle,.filemgr-preview-handle{touch-action:none;cursor:col-resize}.filemgr-collapse-chevron{top:calc(6px + env(aPVr2q_titlebar-area-height,0px));z-index:30;width:24px;height:24px;color:var(--aion-text-secondary);cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;transition:background-color .15s cubic-bezier(.4,0,.2,1);display:flex;position:absolute;right:8px}.filemgr-collapse-chevron:hover{background-color:var(--aion-bg-3);color:var(--aion-text-primary)}.filemgr-collapse-chevron:active{background-color:var(--aion-bg-active)}.filemgr-collapse-chevron:focus-visible{outline:2px solid var(--aion-primary);outline-offset:2px}.filemgr-overlay{z-index:1000;background:#00000059;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.filemgr-dialog{background:var(--aion-bg-base);width:400px;max-width:calc(100vw - 48px);box-shadow:var(--aion-overlay-shadow);font-family:var(--aion-font-sans);border-radius:16px;overflow:hidden}.filemgr-dialog-title{color:var(--aion-text-primary);padding:16px 20px 8px;font-size:14px;font-weight:600}.filemgr-dialog-body{color:var(--aion-text-secondary);padding:0 20px;font-size:13px;line-height:1.6}.filemgr-dialog-actions{justify-content:flex-end;gap:8px;padding:16px 20px 20px;display:flex}.filemgr-btn{border:1px solid var(--aion-bg-3);background:var(--aion-bg-base);height:28px;color:var(--aion-text-primary);cursor:pointer;border-radius:4px;padding:0 14px;font-size:13px;transition:background-color .15s cubic-bezier(.4,0,.2,1)}.filemgr-btn:hover{background:var(--aion-bg-hover)}.filemgr-btn:active{background:var(--aion-bg-active)}.filemgr-btn:focus-visible{outline:2px solid var(--aion-primary);outline-offset:2px}.filemgr-btn-primary{background:var(--aion-primary);border-color:var(--aion-primary);color:#fff}.filemgr-btn-primary:hover{background:var(--aion-primary);opacity:.9}.filemgr-btn-primary:active{opacity:.75;filter:saturate(.9)}.filemgr-btn-danger{background:var(--aion-danger);border-color:var(--aion-danger);color:#fff}.filemgr-btn-danger:hover{opacity:.9}.filemgr-btn-danger:active{opacity:.75}.filemgr-menu{z-index:1100;background:var(--aion-bg-base);min-width:160px;box-shadow:var(--aion-overlay-shadow);border:1px solid var(--aion-bg-3);font-family:var(--aion-font-sans);border-radius:8px;padding:4px;position:fixed}.filemgr-menu-item{height:28px;color:var(--aion-text-primary);cursor:pointer;white-space:nowrap;border-radius:4px;align-items:center;padding:0 10px;font-size:13px;display:flex}.filemgr-menu-item:hover{background:var(--aion-fill-2)}.filemgr-menu-item:focus-visible{outline:2px solid var(--aion-primary);outline-offset:-2px}.filemgr-menu-item-disabled{color:var(--aion-text-disabled);cursor:default}.filemgr-menu-item-disabled:hover{background:0 0}.filemgr-menu-sep{background:var(--aion-bg-3);height:1px;margin:4px 8px}.filemgr-input{border:1px solid var(--aion-bg-3);background:var(--aion-bg-base);width:100%;height:32px;color:var(--aion-text-primary);font-size:13px;font-family:var(--aion-font-sans);box-sizing:border-box;border-radius:6px;outline:none;padding:0 10px}.filemgr-input:focus{border-color:var(--aion-primary)}.filemgr-toast{z-index:1200;background:var(--aion-bg-base);max-width:70vw;color:var(--aion-text-primary);font-size:13px;font-family:var(--aion-font-sans);box-shadow:var(--aion-overlay-shadow);border:1px solid var(--aion-bg-3);border-radius:6px;padding:8px 14px;animation:.2s cubic-bezier(.4,0,.2,1) aPVr2q_filemgr-toast-in;position:fixed;bottom:32px;left:50%;transform:translate(-50%)}@keyframes aPVr2q_filemgr-toast-in{0%{opacity:0;transform:translate(-50%)translateY(8px)}to{opacity:1;transform:translate(-50%)translateY(0)}}";
|
|
4108
4084
|
const tagId$4 = "@lijian-ui/dsh-file-manager/tokens.module.css";
|
|
4109
4085
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
|
|
4110
4086
|
const tag = document.createElement("style");
|
|
@@ -6195,7 +6171,7 @@ window.__ModuleLoader__.load({
|
|
|
6195
6171
|
onClick: handleAddToChat,
|
|
6196
6172
|
role: "button",
|
|
6197
6173
|
tabIndex: -1,
|
|
6198
|
-
children: "
|
|
6174
|
+
children: t("preview.addToChat")
|
|
6199
6175
|
}),
|
|
6200
6176
|
closingIds !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
|
|
6201
6177
|
title: t("preview.closeConfirmTitle"),
|
|
@@ -6220,6 +6196,15 @@ window.__ModuleLoader__.load({
|
|
|
6220
6196
|
* failure degrades the panels, never the GUI boot.
|
|
6221
6197
|
* @module dsh-filemgr/client/mount
|
|
6222
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
|
+
}
|
|
6223
6208
|
const EXPLORER_COL_SELECTOR = "[data-filemgr-explorer-col]";
|
|
6224
6209
|
const PREVIEW_COL_SELECTOR = "[data-filemgr-preview-col]";
|
|
6225
6210
|
/** Wait for one selector (the shell/frame mounts after boot settlement). */
|
|
@@ -6258,23 +6243,29 @@ window.__ModuleLoader__.load({
|
|
|
6258
6243
|
* a preview selection (path + start/end line).
|
|
6259
6244
|
* @returns a disposer unmounting both trees.
|
|
6260
6245
|
*/
|
|
6261
|
-
function mountPanels(stores, onToggleExplorer, onReference, onAddFileReference) {
|
|
6246
|
+
function mountPanels(stores, onToggleExplorer, onReference, onAddFileReference, locale) {
|
|
6262
6247
|
let explorerRoot;
|
|
6263
6248
|
let previewRoot;
|
|
6264
6249
|
const disposers = [];
|
|
6265
6250
|
disposers.push(waitForElement(EXPLORER_COL_SELECTOR, (el) => {
|
|
6266
6251
|
explorerRoot = (0, react_dom_client.createRoot)(el);
|
|
6267
|
-
explorerRoot.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
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
|
+
})
|
|
6271
6259
|
}));
|
|
6272
6260
|
}));
|
|
6273
6261
|
disposers.push(waitForElement(PREVIEW_COL_SELECTOR, (el) => {
|
|
6274
6262
|
previewRoot = (0, react_dom_client.createRoot)(el);
|
|
6275
|
-
previewRoot.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
6276
|
-
|
|
6277
|
-
|
|
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
|
+
})
|
|
6278
6269
|
}));
|
|
6279
6270
|
}));
|
|
6280
6271
|
return () => {
|
|
@@ -6852,11 +6843,11 @@ window.__ModuleLoader__.load({
|
|
|
6852
6843
|
}), isDir && isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6853
6844
|
className: picker_module_css_default.note,
|
|
6854
6845
|
style: { paddingLeft: 8 + (depth + 1) * 16 },
|
|
6855
|
-
children: "
|
|
6846
|
+
children: t("picker.loading")
|
|
6856
6847
|
}) : children.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6857
6848
|
className: picker_module_css_default.note,
|
|
6858
6849
|
style: { paddingLeft: 8 + (depth + 1) * 16 },
|
|
6859
|
-
children: "
|
|
6850
|
+
children: t("picker.emptyDir")
|
|
6860
6851
|
}) : children.map((child) => renderNode(child, depth + 1)) })] }, entry.path);
|
|
6861
6852
|
};
|
|
6862
6853
|
return renderNode;
|
|
@@ -6891,12 +6882,12 @@ window.__ModuleLoader__.load({
|
|
|
6891
6882
|
className: picker_module_css_default.titleRow,
|
|
6892
6883
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6893
6884
|
className: picker_module_css_default.title,
|
|
6894
|
-
children: "
|
|
6885
|
+
children: t("picker.title")
|
|
6895
6886
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6896
6887
|
type: "button",
|
|
6897
6888
|
className: picker_module_css_default.closeBtn,
|
|
6898
6889
|
onClick: onClose,
|
|
6899
|
-
"aria-label": "
|
|
6890
|
+
"aria-label": t("picker.closeAria"),
|
|
6900
6891
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 16 })
|
|
6901
6892
|
})]
|
|
6902
6893
|
}),
|
|
@@ -6909,7 +6900,7 @@ window.__ModuleLoader__.load({
|
|
|
6909
6900
|
}),
|
|
6910
6901
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6911
6902
|
className: picker_module_css_default.searchInput,
|
|
6912
|
-
placeholder: "
|
|
6903
|
+
placeholder: t("picker.searchPlaceholder"),
|
|
6913
6904
|
value: search,
|
|
6914
6905
|
onChange: (e) => setSearch(e.target.value),
|
|
6915
6906
|
autoFocus: true
|
|
@@ -6918,7 +6909,7 @@ window.__ModuleLoader__.load({
|
|
|
6918
6909
|
type: "button",
|
|
6919
6910
|
className: picker_module_css_default.searchClear,
|
|
6920
6911
|
onClick: () => setSearch(""),
|
|
6921
|
-
"aria-label": "
|
|
6912
|
+
"aria-label": t("picker.clearAria"),
|
|
6922
6913
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 13 })
|
|
6923
6914
|
})
|
|
6924
6915
|
]
|
|
@@ -6934,10 +6925,10 @@ window.__ModuleLoader__.load({
|
|
|
6934
6925
|
className: picker_module_css_default.body,
|
|
6935
6926
|
children: searchResults !== null ? searching ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6936
6927
|
className: picker_module_css_default.note,
|
|
6937
|
-
children: "
|
|
6928
|
+
children: t("explorer.search.searching")
|
|
6938
6929
|
}) : searchResults.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6939
6930
|
className: picker_module_css_default.note,
|
|
6940
|
-
children: "
|
|
6931
|
+
children: t("explorer.search.empty")
|
|
6941
6932
|
}) : searchResults.map((hit) => {
|
|
6942
6933
|
const isSel = selected.has(hit.path);
|
|
6943
6934
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -6969,10 +6960,10 @@ window.__ModuleLoader__.load({
|
|
|
6969
6960
|
}, hit.path);
|
|
6970
6961
|
}) : rootLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6971
6962
|
className: picker_module_css_default.note,
|
|
6972
|
-
children: "
|
|
6963
|
+
children: t("picker.loading")
|
|
6973
6964
|
}) : rootEntries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6974
6965
|
className: picker_module_css_default.note,
|
|
6975
|
-
children: "
|
|
6966
|
+
children: t("picker.emptyDir")
|
|
6976
6967
|
}) : rootEntries.map((entry) => tree(entry, 0))
|
|
6977
6968
|
}),
|
|
6978
6969
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -6981,7 +6972,7 @@ window.__ModuleLoader__.load({
|
|
|
6981
6972
|
className: picker_module_css_default.chips,
|
|
6982
6973
|
children: selectedList.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6983
6974
|
className: picker_module_css_default.chipsEmpty,
|
|
6984
|
-
children: "
|
|
6975
|
+
children: t("picker.emptySelection")
|
|
6985
6976
|
}) : selectedList.map((pick) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6986
6977
|
className: picker_module_css_default.chip,
|
|
6987
6978
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
@@ -6992,7 +6983,7 @@ window.__ModuleLoader__.load({
|
|
|
6992
6983
|
type: "button",
|
|
6993
6984
|
className: picker_module_css_default.chipX,
|
|
6994
6985
|
onClick: () => toggleSelect(pick.path, pick.isDir),
|
|
6995
|
-
"aria-label": "
|
|
6986
|
+
"aria-label": t("picker.removeAria"),
|
|
6996
6987
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 11 })
|
|
6997
6988
|
})]
|
|
6998
6989
|
}, pick.path))
|
|
@@ -7002,13 +6993,13 @@ window.__ModuleLoader__.load({
|
|
|
7002
6993
|
type: "button",
|
|
7003
6994
|
className: picker_module_css_default.cancelBtn,
|
|
7004
6995
|
onClick: onClose,
|
|
7005
|
-
children: "
|
|
6996
|
+
children: t("common.cancel")
|
|
7006
6997
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7007
6998
|
type: "button",
|
|
7008
6999
|
className: picker_module_css_default.insertBtn,
|
|
7009
7000
|
disabled: selectedList.length === 0,
|
|
7010
7001
|
onClick: () => onInsert(selectedList),
|
|
7011
|
-
children: ["
|
|
7002
|
+
children: [t("picker.insert"), selectedList.length > 0 ? ` (${selectedList.length})` : ""]
|
|
7012
7003
|
})]
|
|
7013
7004
|
})]
|
|
7014
7005
|
})
|
|
@@ -7339,6 +7330,75 @@ window.__ModuleLoader__.load({
|
|
|
7339
7330
|
document.head.appendChild(tag);
|
|
7340
7331
|
}
|
|
7341
7332
|
//#endregion
|
|
7333
|
+
//#region src/client/DockItem.tsx
|
|
7334
|
+
const STYLE_ID = "dsh-dock-item-style";
|
|
7335
|
+
function ensureStyle() {
|
|
7336
|
+
if (typeof document === "undefined") return;
|
|
7337
|
+
if (document.getElementById(STYLE_ID) !== null) return;
|
|
7338
|
+
const tag = document.createElement("style");
|
|
7339
|
+
tag.id = STYLE_ID;
|
|
7340
|
+
tag.textContent = ".dsh-dock-item{width:32px;height:32px;border:none;border-radius:9999px;background:#000;color:#fff;cursor:pointer;position:relative;transition:transform .15s ease,background .18s ease;display:inline-flex;align-items:center;justify-content:center}.dsh-dock-item:hover{transform:scale(1.15);background:#1a1a1a}.dsh-dock-item-active{background:var(--aion-primary,#6ea0ff)}.dsh-dock-tooltip{position:absolute;top:calc(100% + 6px);left:50%;transform:translateX(-50%);padding:2px 8px;font-size:12px;line-height:1.4;white-space:nowrap;color:#fff;background:rgba(20,20,30,.9);border:1px solid rgba(255,255,255,.12);border-radius:6px;opacity:0;pointer-events:none;transition:opacity .15s}.dsh-dock-item:hover .dsh-dock-tooltip{opacity:1}";
|
|
7341
|
+
document.head.appendChild(tag);
|
|
7342
|
+
}
|
|
7343
|
+
ensureStyle();
|
|
7344
|
+
function DockItem(props) {
|
|
7345
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7346
|
+
type: "button",
|
|
7347
|
+
className: `dsh-dock-item${props.active ? " dsh-dock-item-active" : ""}`,
|
|
7348
|
+
onClick: props.onClick,
|
|
7349
|
+
"aria-label": props.label,
|
|
7350
|
+
"aria-pressed": props.active,
|
|
7351
|
+
title: props.label,
|
|
7352
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7353
|
+
className: "dsh-dock-tooltip",
|
|
7354
|
+
children: props.label
|
|
7355
|
+
}), props.children]
|
|
7356
|
+
});
|
|
7357
|
+
}
|
|
7358
|
+
//#endregion
|
|
7359
|
+
//#region src/client/FilePanelDockItem.tsx
|
|
7360
|
+
/**
|
|
7361
|
+
* FilePanelDockItem — the file-panel dock button rendered into the conversation
|
|
7362
|
+
* session header (the `conversation.session.header.utilities` slot).
|
|
7363
|
+
*
|
|
7364
|
+
* This plugin owns its own dock button independently: dsh-term renders its own
|
|
7365
|
+
* terminal button, dsh-file-manager renders this file-panel button. The two
|
|
7366
|
+
* are fully decoupled — install either alone and you get just its button;
|
|
7367
|
+
* install both and both buttons appear side by side with shared magnification.
|
|
7368
|
+
*
|
|
7369
|
+
* Clicking broadcasts `dsh-dock:toggle-filepanel` (handled in dsh-file-manager
|
|
7370
|
+
* index); the open state is mirrored back through `dsh-dock:filepanel-state`.
|
|
7371
|
+
* @module dsh-file-manager/client/FilePanelDockItem
|
|
7372
|
+
*/
|
|
7373
|
+
const EV = {
|
|
7374
|
+
toggleFile: "dsh-dock:toggle-filepanel",
|
|
7375
|
+
fileState: "dsh-dock:filepanel-state"
|
|
7376
|
+
};
|
|
7377
|
+
function FilePanelDockItem() {
|
|
7378
|
+
const [active, setActive] = (0, react.useState)(false);
|
|
7379
|
+
(0, react.useEffect)(() => {
|
|
7380
|
+
const onState = (e) => setActive(Boolean(e.detail));
|
|
7381
|
+
window.addEventListener(EV.fileState, onState);
|
|
7382
|
+
return () => window.removeEventListener(EV.fileState, onState);
|
|
7383
|
+
}, []);
|
|
7384
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DockItem, {
|
|
7385
|
+
active,
|
|
7386
|
+
label: t("dock.filePanel.label"),
|
|
7387
|
+
onClick: () => window.dispatchEvent(new CustomEvent(EV.toggleFile)),
|
|
7388
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
7389
|
+
width: "16",
|
|
7390
|
+
height: "16",
|
|
7391
|
+
viewBox: "0 0 24 24",
|
|
7392
|
+
fill: "none",
|
|
7393
|
+
stroke: "currentColor",
|
|
7394
|
+
strokeWidth: "2",
|
|
7395
|
+
strokeLinecap: "round",
|
|
7396
|
+
strokeLinejoin: "round",
|
|
7397
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m6 14 1.45-2.9a2 2 0 0 1 1.79-1.1h11.76a2 2 0 0 1 1.94 1.5l1.55 6a2 2 0 0 1-1.94 2.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.5a2 2 0 0 1 1.59.79l.91 1.21a2 2 0 0 0 1.59.79H18a2 2 0 0 1 2 2v2" })
|
|
7398
|
+
})
|
|
7399
|
+
});
|
|
7400
|
+
}
|
|
7401
|
+
//#endregion
|
|
7342
7402
|
//#region src/client/index.ts
|
|
7343
7403
|
/**
|
|
7344
7404
|
* FileManager right-panel system — browser half: mounts the explorer and preview
|
|
@@ -7365,7 +7425,16 @@ window.__ModuleLoader__.load({
|
|
|
7365
7425
|
const panelApi = new PanelApi();
|
|
7366
7426
|
/** Apply the browser half. */
|
|
7367
7427
|
function apply(ctx) {
|
|
7428
|
+
ctx.inject(["slots"], (scope) => {
|
|
7429
|
+
scope.slots.inject("conversation.session.header.utilities", () => scope.slots.register({
|
|
7430
|
+
name: "conversation.session.header.utilities",
|
|
7431
|
+
id: "filemgr-dock-item",
|
|
7432
|
+
order: -2,
|
|
7433
|
+
inject: () => ({})
|
|
7434
|
+
}, FilePanelDockItem));
|
|
7435
|
+
});
|
|
7368
7436
|
ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-filemgr: dictionaries");
|
|
7437
|
+
bindT(ctx.locale.bind(NS));
|
|
7369
7438
|
ctx.effect(() => registerFileReferenceSource(ctx), "dsh-filemgr: file reference source");
|
|
7370
7439
|
ctx.inject([
|
|
7371
7440
|
"slots",
|
|
@@ -7561,7 +7630,7 @@ window.__ModuleLoader__.load({
|
|
|
7561
7630
|
syncLanguage();
|
|
7562
7631
|
try {
|
|
7563
7632
|
layout.mount();
|
|
7564
|
-
mountPanels(stores, () => layout.toggleExplorer(), onReference, onAddFileReference);
|
|
7633
|
+
mountPanels(stores, () => layout.toggleExplorer(), onReference, onAddFileReference, ctx.locale);
|
|
7565
7634
|
} catch (error) {
|
|
7566
7635
|
console.error("[dsh-filemgr] mount failed:", error);
|
|
7567
7636
|
}
|