dsh-plugin-workbench 0.0.3 → 0.0.5
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/CHANGELOG.md +54 -0
- package/README.md +17 -7
- package/lib/client.js +641 -160
- package/lib/client.js.map +1 -1
- package/lib/index.js +169 -6
- package/package.json +1 -1
- package/src/client/FileExplorer.tsx +357 -64
- package/src/client/FilePreview.tsx +171 -34
- package/src/client/files.module.css +144 -3
- package/src/client/index.ts +5 -1
- package/src/client/locales.ts +30 -0
- package/src/client/store.ts +25 -0
- package/src/dsh.d.ts +9 -0
- package/src/index.ts +204 -6
package/lib/client.js
CHANGED
|
@@ -30,7 +30,7 @@ window.__ModuleLoader__.load({
|
|
|
30
30
|
let react = require("react");
|
|
31
31
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
32
|
//#region \0dsh-css:src/client/files.module.css.mjs
|
|
33
|
-
const css$1 = "[data-fe-theme=light]{--fe-bg:#fff;--fe-sidebar-bg:#f3f3f3;--fe-fg:#1f1f1f;--fe-muted:#6e6e6e;--fe-faint:#9d9d9d;--fe-border:#e5e5e5;--fe-border-strong:#c8c8c8;--fe-accent:#005fb8;--fe-hover:#e8e8e8;--fe-selection:#add6ff;--fe-token-keyword:#00f;--fe-token-string:#a31515;--fe-token-comment:green;--fe-token-number:#098658;--fe-token-function:#795e26;--fe-token-type:#267f99;--fe-token-variable:#001080;--fe-token-tag:maroon;--fe-token-meta:#6e6e6e}[data-fe-theme=dark]{--fe-bg:#1e1e1e;--fe-sidebar-bg:#252526;--fe-fg:#d4d4d4;--fe-muted:#ccc;--fe-faint:#9d9d9d;--fe-border:#3c3c3c;--fe-border-strong:#454545;--fe-accent:#007acc;--fe-hover:#2a2d2e;--fe-selection:#264f78;--fe-token-keyword:#569cd6;--fe-token-string:#ce9178;--fe-token-comment:#6a9955;--fe-token-number:#b5cea8;--fe-token-function:#dcdcaa;--fe-token-type:#4ec9b0;--fe-token-variable:#9cdcfe;--fe-token-tag:#569cd6;--fe-token-meta:#9b9b9b}.Myjg4a_column{box-sizing:border-box;background:var(--fe-sidebar-bg);min-width:0;height:100%;color:var(--fe-fg);flex-direction:column;font-size:13px;display:flex;overflow:hidden}.Myjg4a_header{box-sizing:border-box;border-bottom:1px solid var(--fe-border);flex:none;align-items:center;gap:6px;height:34px;padding:0 8px;display:flex}.Myjg4a_headerTitle{text-overflow:ellipsis;white-space:nowrap;text-transform:uppercase;letter-spacing:.04em;min-width:0;color:var(--fe-muted);flex:1;font-size:11px;font-weight:600;overflow:hidden}.Myjg4a_headerActions{flex:none;align-items:center;gap:2px;display:flex}.Myjg4a_action{appearance:none;color:var(--fe-muted);cursor:pointer;background:0 0;border:none;border-radius:5px;justify-content:center;align-items:center;width:22px;height:22px;padding:0;font-size:13px;line-height:1;display:inline-flex}.Myjg4a_action:hover:not(:disabled){background:var(--fe-hover);color:var(--fe-fg)}.Myjg4a_action:disabled{opacity:.5;cursor:default}.Myjg4a_treeArea{flex:1;min-height:0;padding:4px;overflow-y:auto}.Myjg4a_row{cursor:pointer;user-select:none;white-space:nowrap;height:22px;color:var(--fe-fg);border-radius:3px;align-items:center;gap:6px;font-size:13px;display:flex}.Myjg4a_row:hover{background:var(--fe-hover)}.Myjg4a_rowSelected{background:var(--fe-selection)}.Myjg4a_rowSelected .Myjg4a_name{color:var(--fe-fg)}.Myjg4a_chevron{text-align:center;width:12px;color:var(--fe-muted);flex:none;font-size:10px;line-height:22px}.Myjg4a_icon{text-align:center;flex:none;width:16px;font-size:13px;line-height:22px}.Myjg4a_fileBadge{letter-spacing:-.3px;box-sizing:border-box;vertical-align:middle;user-select:none;border-radius:3px;justify-content:center;align-items:center;width:16px;height:15px;padding:0 1px;font-size:8px;font-weight:700;line-height:1;display:inline-flex}.Myjg4a_fileBadgeLong{letter-spacing:-.4px;font-size:6.5px}.Myjg4a_name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.Myjg4a_size{color:var(--fe-faint);flex:none;font-size:11px}.Myjg4a_actions{opacity:0;flex:none;align-items:center;gap:2px;transition:opacity .12s;display:flex}.Myjg4a_row:hover .Myjg4a_actions,.Myjg4a_row:focus-within .Myjg4a_actions{opacity:1}.Myjg4a_rowHint{color:var(--fe-faint);padding:2px 8px 2px 0;font-size:12px;line-height:20px}.Myjg4a_rowError{color:#f48771;padding:2px 8px 2px 0;font-size:12px;line-height:18px}.Myjg4a_preview{border-right:1px solid var(--fe-border);background:var(--fe-bg);flex-direction:column;flex:0 0 55%;min-width:240px;min-height:0;animation:.18s Myjg4a_fe-preview-in;display:flex;overflow:hidden}.Myjg4a_previewClosing{animation:.2s forwards Myjg4a_fe-preview-out}@keyframes Myjg4a_fe-preview-in{0%{opacity:0}to{opacity:1}}@keyframes Myjg4a_fe-preview-out{0%{opacity:1}to{opacity:0}}.Myjg4a_handle{cursor:col-resize;touch-action:none;z-index:2;background:0 0;flex:none;width:6px;margin-left:-4px;margin-right:-2px}.Myjg4a_handle:hover,.Myjg4a_handle:active{background:var(--fe-accent);opacity:.35}.Myjg4a_tabBar{border-bottom:1px solid var(--fe-border);background:var(--fe-sidebar-bg);flex:none;align-items:stretch;height:34px;display:flex}.Myjg4a_tabScroller{scrollbar-width:thin;flex:1;align-items:stretch;min-width:0;display:flex;overflow-x:auto}.Myjg4a_collapse,.Myjg4a_tabAction{appearance:none;color:var(--fe-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;width:26px;height:26px;margin:auto 4px;padding:0;font-size:14px;line-height:1;display:inline-flex}.Myjg4a_collapse:hover,.Myjg4a_tabAction:hover{background:var(--fe-hover);color:var(--fe-fg)}.Myjg4a_tabActionActive{background:var(--fe-hover);color:var(--fe-accent)}.Myjg4a_tab{max-width:180px;color:var(--fe-muted);border-right:1px solid var(--fe-border);cursor:pointer;user-select:none;white-space:nowrap;flex:none;align-items:center;gap:6px;padding:0 8px 0 12px;font-size:13px;display:flex}.Myjg4a_tab:hover{background:var(--fe-hover)}.Myjg4a_tabActive{background:var(--fe-bg);color:var(--fe-fg);box-shadow:inset 0 -1px 0 var(--fe-accent)}.Myjg4a_tabName{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.Myjg4a_tabIcon{flex:none;justify-content:center;align-items:center;width:16px;font-size:13px;line-height:1;display:inline-flex}.Myjg4a_tabClose{appearance:none;color:var(--fe-faint);cursor:pointer;background:0 0;border:none;border-radius:3px;justify-content:center;align-items:center;width:16px;height:16px;padding:0;font-size:11px;display:inline-flex}.Myjg4a_tabClose:hover{background:var(--fe-hover);color:var(--fe-fg)}.Myjg4a_tabDot{background:var(--fe-fg);opacity:.85;border-radius:50%;flex:none;width:8px;height:8px}.Myjg4a_previewBody{flex:1;min-height:0;overflow:auto}.Myjg4a_editor{height:100%;min-height:0;position:relative}.Myjg4a_editorHighlight{pointer-events:none;white-space:pre;tab-size:2;color:var(--fe-fg);background:0 0;margin:0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;position:absolute;inset:0;overflow:auto}.Myjg4a_editorHighlight code{font-family:inherit;font-size:inherit;line-height:inherit;white-space:inherit}.Myjg4a_editorTextarea{box-sizing:border-box;resize:none;color:#0000;caret-color:var(--fe-fg);white-space:pre;tab-size:2;background:0 0;border:none;outline:none;margin:0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;position:absolute;inset:0;overflow:auto}.Myjg4a_editor[data-wrap=on] .Myjg4a_editorHighlight,.Myjg4a_editor[data-wrap=on] .Myjg4a_editorTextarea{white-space:pre-wrap;overflow-wrap:anywhere}.Myjg4a_editor[data-wrap=on] .Myjg4a_editorTextarea{overflow-x:hidden}.Myjg4a_saveError{color:#1e1e1e;background:#f48771;border-radius:4px;padding:4px 10px;font-size:12px;position:absolute;bottom:8px;right:8px}.Myjg4a_previewHint{color:var(--fe-faint);padding:12px;font-size:12px;line-height:18px}.Myjg4a_previewMeta{color:var(--fe-faint);font-size:11px}.Myjg4a_placeholder{text-align:center;color:var(--fe-faint);flex:1;justify-content:center;align-items:center;padding:16px;font-size:12px;line-height:18px;display:flex}.Myjg4a_editorHighlight .hljs-keyword,.Myjg4a_editorHighlight .hljs-selector-tag,.Myjg4a_editorHighlight .hljs-literal{color:var(--fe-token-keyword)}.Myjg4a_editorHighlight .hljs-string,.Myjg4a_editorHighlight .hljs-regexp,.Myjg4a_editorHighlight .hljs-addition{color:var(--fe-token-string)}.Myjg4a_editorHighlight .hljs-comment,.Myjg4a_editorHighlight .hljs-quote{color:var(--fe-token-comment);font-style:italic}.Myjg4a_editorHighlight .hljs-number,.Myjg4a_editorHighlight .hljs-symbol,.Myjg4a_editorHighlight .hljs-bullet{color:var(--fe-token-number)}.Myjg4a_editorHighlight .hljs-title,.Myjg4a_editorHighlight .hljs-section,.Myjg4a_editorHighlight .hljs-title.function_{color:var(--fe-token-function)}.Myjg4a_editorHighlight .hljs-type,.Myjg4a_editorHighlight .hljs-built_in,.Myjg4a_editorHighlight .hljs-class .hljs-title{color:var(--fe-token-type)}.Myjg4a_editorHighlight .hljs-attr,.Myjg4a_editorHighlight .hljs-attribute,.Myjg4a_editorHighlight .hljs-variable,.Myjg4a_editorHighlight .hljs-template-variable,.Myjg4a_editorHighlight .hljs-params{color:var(--fe-token-variable)}.Myjg4a_editorHighlight .hljs-tag,.Myjg4a_editorHighlight .hljs-name{color:var(--fe-token-tag)}.Myjg4a_editorHighlight .hljs-meta{color:var(--fe-token-meta)}.Myjg4a_editorHighlight .hljs-emphasis{font-style:italic}.Myjg4a_editorHighlight .hljs-strong{font-weight:700}body[data-dsh-maid-atelier] [data-skin-chrome=top-trim],body[data-dsh-maid-atelier] [data-skin-chrome=bottom-trim]{translate:calc(var(--maid-sidebar-width,0px) + var(--dsh-explorer-width,0px) + var(--dsh-preview-width,0px)) 0!important}[class*=explorerCol]{border-right:1px solid var(--fe-border)!important}";
|
|
33
|
+
const css$1 = "[data-fe-theme=light]{--fe-bg:#fff;--fe-sidebar-bg:#f3f3f3;--fe-fg:#1f1f1f;--fe-muted:#6e6e6e;--fe-faint:#9d9d9d;--fe-border:#e5e5e5;--fe-border-strong:#c8c8c8;--fe-accent:#005fb8;--fe-hover:#e8e8e8;--fe-selection:#add6ff;--fe-token-keyword:#00f;--fe-token-string:#a31515;--fe-token-comment:green;--fe-token-number:#098658;--fe-token-function:#795e26;--fe-token-type:#267f99;--fe-token-variable:#001080;--fe-token-tag:maroon;--fe-token-meta:#6e6e6e}[data-fe-theme=dark]{--fe-bg:#1e1e1e;--fe-sidebar-bg:#252526;--fe-fg:#d4d4d4;--fe-muted:#ccc;--fe-faint:#9d9d9d;--fe-border:#3c3c3c;--fe-border-strong:#454545;--fe-accent:#007acc;--fe-hover:#2a2d2e;--fe-selection:#264f78;--fe-token-keyword:#569cd6;--fe-token-string:#ce9178;--fe-token-comment:#6a9955;--fe-token-number:#b5cea8;--fe-token-function:#dcdcaa;--fe-token-type:#4ec9b0;--fe-token-variable:#9cdcfe;--fe-token-tag:#569cd6;--fe-token-meta:#9b9b9b}.Myjg4a_column{box-sizing:border-box;background:var(--fe-sidebar-bg);min-width:0;height:100%;color:var(--fe-fg);flex-direction:column;font-size:13px;display:flex;overflow:hidden}.Myjg4a_header{box-sizing:border-box;border-bottom:1px solid var(--fe-border);flex:none;align-items:center;gap:6px;height:34px;padding:0 8px;display:flex}.Myjg4a_headerTitle{text-overflow:ellipsis;white-space:nowrap;text-transform:uppercase;letter-spacing:.04em;min-width:0;color:var(--fe-muted);flex:1;font-size:11px;font-weight:600;overflow:hidden}.Myjg4a_headerActions{flex:none;align-items:center;gap:2px;display:flex}.Myjg4a_action{appearance:none;color:var(--fe-muted);cursor:pointer;background:0 0;border:none;border-radius:5px;justify-content:center;align-items:center;width:22px;height:22px;padding:0;font-size:13px;line-height:1;display:inline-flex}.Myjg4a_action:hover:not(:disabled){background:var(--fe-hover);color:var(--fe-fg)}.Myjg4a_action:disabled{opacity:.5;cursor:default}.Myjg4a_treeArea{flex:1;min-height:0;padding:4px;overflow-y:auto}.Myjg4a_row{cursor:pointer;user-select:none;white-space:nowrap;height:22px;color:var(--fe-fg);border-radius:3px;align-items:center;gap:6px;font-size:13px;display:flex}.Myjg4a_row:hover{background:var(--fe-hover)}.Myjg4a_rowSelected{background:var(--fe-selection)}.Myjg4a_rowSelected .Myjg4a_name{color:var(--fe-fg)}.Myjg4a_chevron{text-align:center;width:12px;color:var(--fe-muted);flex:none;font-size:10px;line-height:22px}.Myjg4a_icon{text-align:center;flex:none;width:16px;font-size:13px;line-height:22px}.Myjg4a_fileBadge{letter-spacing:-.3px;box-sizing:border-box;vertical-align:middle;user-select:none;border-radius:3px;justify-content:center;align-items:center;width:16px;height:15px;padding:0 1px;font-size:8px;font-weight:700;line-height:1;display:inline-flex}.Myjg4a_fileBadgeLong{letter-spacing:-.4px;font-size:6.5px}.Myjg4a_name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.Myjg4a_size{color:var(--fe-faint);flex:none;font-size:11px}.Myjg4a_actions{opacity:0;flex:none;align-items:center;gap:2px;transition:opacity .12s;display:flex}.Myjg4a_row:hover .Myjg4a_actions,.Myjg4a_row:focus-within .Myjg4a_actions{opacity:1}.Myjg4a_rowHint{color:var(--fe-faint);padding:2px 8px 2px 0;font-size:12px;line-height:20px}.Myjg4a_rowError{color:#f48771;padding:2px 8px 2px 0;font-size:12px;line-height:18px}.Myjg4a_contextMenu{z-index:10000;box-sizing:border-box;border:1px solid var(--fe-border-strong);user-select:none;border-radius:6px;min-width:180px;padding:4px;font-size:13px;position:fixed;box-shadow:0 6px 24px #00000047;background:var(--fe-bg)!important;color:var(--fe-fg)!important}.Myjg4a_contextMenuItem{cursor:pointer;white-space:nowrap;color:inherit;border-radius:4px;align-items:center;gap:8px;padding:5px 10px;display:flex}.Myjg4a_contextMenuItem:hover{background:var(--fe-selection)!important;color:var(--fe-fg)!important}.Myjg4a_contextMenuItemDanger{color:#f48771!important}.Myjg4a_contextMenuDivider{background:var(--fe-border);height:1px;margin:4px 6px}.Myjg4a_preview{border-right:1px solid var(--fe-border);background:var(--fe-bg);flex-direction:column;flex:0 0 55%;min-width:240px;min-height:0;animation:.18s Myjg4a_fe-preview-in;display:flex;overflow:hidden}.Myjg4a_previewClosing{animation:.2s forwards Myjg4a_fe-preview-out}@keyframes Myjg4a_fe-preview-in{0%{opacity:0}to{opacity:1}}@keyframes Myjg4a_fe-preview-out{0%{opacity:1}to{opacity:0}}.Myjg4a_handle{cursor:col-resize;touch-action:none;z-index:2;background:0 0;flex:none;width:6px;margin-left:-4px;margin-right:-2px}.Myjg4a_handle:hover,.Myjg4a_handle:active{background:var(--fe-accent);opacity:.35}.Myjg4a_tabBar{border-bottom:1px solid var(--fe-border);background:var(--fe-sidebar-bg);flex:none;align-items:stretch;height:34px;display:flex}.Myjg4a_tabScroller{scrollbar-width:thin;flex:1;align-items:stretch;min-width:0;display:flex;overflow-x:auto}.Myjg4a_collapse,.Myjg4a_tabAction{appearance:none;color:var(--fe-muted);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;width:26px;height:26px;margin:auto 4px;padding:0;font-size:14px;line-height:1;display:inline-flex}.Myjg4a_collapse:hover,.Myjg4a_tabAction:hover{background:var(--fe-hover);color:var(--fe-fg)}.Myjg4a_tabActionActive{background:var(--fe-hover);color:var(--fe-accent)}.Myjg4a_tab{max-width:180px;color:var(--fe-muted);border-right:1px solid var(--fe-border);cursor:pointer;user-select:none;white-space:nowrap;flex:none;align-items:center;gap:6px;padding:0 8px 0 12px;font-size:13px;display:flex}.Myjg4a_tab:hover{background:var(--fe-hover)}.Myjg4a_tabActive{background:var(--fe-bg);color:var(--fe-fg);box-shadow:inset 0 -1px 0 var(--fe-accent)}.Myjg4a_tabName{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.Myjg4a_tabIcon{flex:none;justify-content:center;align-items:center;width:16px;font-size:13px;line-height:1;display:inline-flex}.Myjg4a_tabClose{appearance:none;color:var(--fe-faint);cursor:pointer;background:0 0;border:none;border-radius:3px;justify-content:center;align-items:center;width:16px;height:16px;padding:0;font-size:11px;display:inline-flex}.Myjg4a_tabClose:hover{background:var(--fe-hover);color:var(--fe-fg)}.Myjg4a_tabDot{background:var(--fe-fg);opacity:.85;border-radius:50%;flex:none;width:8px;height:8px}.Myjg4a_previewBody{flex:1;min-height:0;overflow:auto}.Myjg4a_editor{--fe-gutter:44px;contain:layout paint;height:100%;min-height:0;position:relative}.Myjg4a_gutter{width:var(--fe-gutter);box-sizing:border-box;border-right:1px solid var(--fe-border);background:var(--fe-sidebar-bg);color:var(--fe-faint);user-select:none;pointer-events:none;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;position:absolute;top:0;bottom:0;left:0;overflow:hidden}.Myjg4a_gutterNumbers{white-space:pre;text-align:right;padding:12px 8px}.Myjg4a_editorPlain .Myjg4a_editorTextarea{color:var(--fe-fg)}.Myjg4a_editorHint{left:calc(var(--fe-gutter) + 8px);background:var(--fe-hover);color:var(--fe-muted);pointer-events:none;border-radius:4px;padding:4px 10px;font-size:11px;position:absolute;bottom:8px}.Myjg4a_editorHighlight{top:0;bottom:0;left:var(--fe-gutter);pointer-events:none;white-space:pre;tab-size:2;color:var(--fe-fg);background:0 0;margin:0;padding:12px 12px calc(12px + 1.5em);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;position:absolute;right:0;overflow:auto}.Myjg4a_editorHighlight code{font-family:inherit;font-size:inherit;line-height:inherit;white-space:inherit}.Myjg4a_editorTextarea{top:0;bottom:0;left:var(--fe-gutter);box-sizing:border-box;resize:none;color:#0000;caret-color:var(--fe-fg);white-space:pre;tab-size:2;background:0 0;border:none;outline:none;margin:0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.5;position:absolute;right:0;overflow:auto}.Myjg4a_editor[data-wrap=on] .Myjg4a_editorHighlight,.Myjg4a_editor[data-wrap=on] .Myjg4a_editorTextarea{white-space:pre-wrap;overflow-wrap:anywhere}.Myjg4a_editor[data-wrap=on] .Myjg4a_editorTextarea{overflow-x:hidden}.Myjg4a_saveError{color:#1e1e1e;background:#f48771;border-radius:4px;padding:4px 10px;font-size:12px;position:absolute;bottom:8px;right:8px}.Myjg4a_previewHint{color:var(--fe-faint);padding:12px;font-size:12px;line-height:18px}.Myjg4a_imageView{box-sizing:border-box;background:var(--fe-bg);flex-direction:column;justify-content:center;align-items:center;gap:8px;height:100%;min-height:0;padding:12px;display:flex;overflow:auto}.Myjg4a_image{object-fit:contain;max-width:100%;max-height:100%;box-shadow:0 0 0 1px var(--fe-border);border-radius:4px}.Myjg4a_previewMeta{color:var(--fe-faint);font-size:11px}.Myjg4a_placeholder{text-align:center;color:var(--fe-faint);flex:1;justify-content:center;align-items:center;padding:16px;font-size:12px;line-height:18px;display:flex}.Myjg4a_editorHighlight .hljs-keyword,.Myjg4a_editorHighlight .hljs-selector-tag,.Myjg4a_editorHighlight .hljs-literal{color:var(--fe-token-keyword)}.Myjg4a_editorHighlight .hljs-string,.Myjg4a_editorHighlight .hljs-regexp,.Myjg4a_editorHighlight .hljs-addition{color:var(--fe-token-string)}.Myjg4a_editorHighlight .hljs-comment,.Myjg4a_editorHighlight .hljs-quote{color:var(--fe-token-comment);font-style:italic}.Myjg4a_editorHighlight .hljs-number,.Myjg4a_editorHighlight .hljs-symbol,.Myjg4a_editorHighlight .hljs-bullet{color:var(--fe-token-number)}.Myjg4a_editorHighlight .hljs-title,.Myjg4a_editorHighlight .hljs-section,.Myjg4a_editorHighlight .hljs-title.function_{color:var(--fe-token-function)}.Myjg4a_editorHighlight .hljs-type,.Myjg4a_editorHighlight .hljs-built_in,.Myjg4a_editorHighlight .hljs-class .hljs-title{color:var(--fe-token-type)}.Myjg4a_editorHighlight .hljs-attr,.Myjg4a_editorHighlight .hljs-attribute,.Myjg4a_editorHighlight .hljs-variable,.Myjg4a_editorHighlight .hljs-template-variable,.Myjg4a_editorHighlight .hljs-params{color:var(--fe-token-variable)}.Myjg4a_editorHighlight .hljs-tag,.Myjg4a_editorHighlight .hljs-name{color:var(--fe-token-tag)}.Myjg4a_editorHighlight .hljs-meta{color:var(--fe-token-meta)}.Myjg4a_editorHighlight .hljs-emphasis{font-style:italic}.Myjg4a_editorHighlight .hljs-strong{color:var(--fe-accent);font-weight:inherit}body[data-dsh-maid-atelier] [data-skin-chrome=top-trim],body[data-dsh-maid-atelier] [data-skin-chrome=bottom-trim]{translate:calc(var(--maid-sidebar-width,0px) + var(--dsh-explorer-width,0px) + var(--dsh-preview-width,0px)) 0!important}[class*=explorerCol]{border-right:1px solid var(--fe-border)!important}";
|
|
34
34
|
const tagId = "dsh-plugin-workbench/files.module.css";
|
|
35
35
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
36
36
|
const tag = document.createElement("style");
|
|
@@ -45,18 +45,28 @@ window.__ModuleLoader__.load({
|
|
|
45
45
|
"chevron": "Myjg4a_chevron",
|
|
46
46
|
"collapse": "Myjg4a_collapse",
|
|
47
47
|
"column": "Myjg4a_column",
|
|
48
|
+
"contextMenu": "Myjg4a_contextMenu",
|
|
49
|
+
"contextMenuDivider": "Myjg4a_contextMenuDivider",
|
|
50
|
+
"contextMenuItem": "Myjg4a_contextMenuItem",
|
|
51
|
+
"contextMenuItemDanger": "Myjg4a_contextMenuItemDanger",
|
|
48
52
|
"editor": "Myjg4a_editor",
|
|
49
53
|
"editorHighlight": "Myjg4a_editorHighlight",
|
|
54
|
+
"editorHint": "Myjg4a_editorHint",
|
|
55
|
+
"editorPlain": "Myjg4a_editorPlain",
|
|
50
56
|
"editorTextarea": "Myjg4a_editorTextarea",
|
|
51
57
|
"fe-preview-in": "Myjg4a_fe-preview-in",
|
|
52
58
|
"fe-preview-out": "Myjg4a_fe-preview-out",
|
|
53
59
|
"fileBadge": "Myjg4a_fileBadge",
|
|
54
60
|
"fileBadgeLong": "Myjg4a_fileBadgeLong",
|
|
61
|
+
"gutter": "Myjg4a_gutter",
|
|
62
|
+
"gutterNumbers": "Myjg4a_gutterNumbers",
|
|
55
63
|
"handle": "Myjg4a_handle",
|
|
56
64
|
"header": "Myjg4a_header",
|
|
57
65
|
"headerActions": "Myjg4a_headerActions",
|
|
58
66
|
"headerTitle": "Myjg4a_headerTitle",
|
|
59
67
|
"icon": "Myjg4a_icon",
|
|
68
|
+
"image": "Myjg4a_image",
|
|
69
|
+
"imageView": "Myjg4a_imageView",
|
|
60
70
|
"name": "Myjg4a_name",
|
|
61
71
|
"placeholder": "Myjg4a_placeholder",
|
|
62
72
|
"preview": "Myjg4a_preview",
|
|
@@ -666,6 +676,37 @@ window.__ModuleLoader__.load({
|
|
|
666
676
|
active: path
|
|
667
677
|
});
|
|
668
678
|
}
|
|
679
|
+
/** Point any open tab at a new path after a rename (the disk path changed). */
|
|
680
|
+
function retargetFile(oldPath, newPath) {
|
|
681
|
+
updateCurrent((ws) => {
|
|
682
|
+
if (!ws.tabs.includes(oldPath)) return ws;
|
|
683
|
+
const tabs = ws.tabs.map((t) => t === oldPath ? newPath : t);
|
|
684
|
+
return {
|
|
685
|
+
...ws,
|
|
686
|
+
tabs,
|
|
687
|
+
active: ws.active === oldPath ? newPath : ws.active
|
|
688
|
+
};
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/** Close every open tab at or under a path (used after deleting it). */
|
|
692
|
+
function closeFilesUnder(path) {
|
|
693
|
+
updateCurrent((ws) => {
|
|
694
|
+
const sep = path.includes("\\") ? "\\" : "/";
|
|
695
|
+
const prefix = path.endsWith("\\") || path.endsWith("/") ? path : path + sep;
|
|
696
|
+
const kept = ws.tabs.filter((t) => t !== path && !t.startsWith(prefix));
|
|
697
|
+
if (kept.length === ws.tabs.length) return ws;
|
|
698
|
+
let active = ws.active;
|
|
699
|
+
if (active !== void 0 && (active === path || active.startsWith(prefix))) {
|
|
700
|
+
const index = ws.tabs.indexOf(active);
|
|
701
|
+
active = kept[Math.min(index, kept.length - 1)];
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
...ws,
|
|
705
|
+
tabs: kept,
|
|
706
|
+
active
|
|
707
|
+
};
|
|
708
|
+
});
|
|
709
|
+
}
|
|
669
710
|
/** Move a tab before another tab (drag-to-reorder). */
|
|
670
711
|
function moveTab(dragged, target) {
|
|
671
712
|
updateCurrent((ws) => {
|
|
@@ -726,6 +767,16 @@ window.__ModuleLoader__.load({
|
|
|
726
767
|
const idx = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
727
768
|
return idx >= 0 ? trimmed.slice(idx + 1) : trimmed;
|
|
728
769
|
}
|
|
770
|
+
/** Parent directory of a path ('' for a bare drive root — never used for such). */
|
|
771
|
+
function parentOf(path) {
|
|
772
|
+
const idx = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
773
|
+
return idx > 0 ? path.slice(0, idx) : path;
|
|
774
|
+
}
|
|
775
|
+
/** Append a child name to a directory path, honoring its separator style. */
|
|
776
|
+
function joinPath(dir, name) {
|
|
777
|
+
const sep = dir.includes("\\") ? "\\" : "/";
|
|
778
|
+
return dir.endsWith("\\") || dir.endsWith("/") ? dir + name : dir + sep + name;
|
|
779
|
+
}
|
|
729
780
|
function formatSize$1(bytes) {
|
|
730
781
|
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
731
782
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -735,6 +786,10 @@ window.__ModuleLoader__.load({
|
|
|
735
786
|
}
|
|
736
787
|
/** How often the visible tree is re-listed to pick up disk changes. */
|
|
737
788
|
const REFRESH_MS = 2e3;
|
|
789
|
+
/** How many previously-expanded folders to re-list at once on workspace switch. */
|
|
790
|
+
const DIR_LOAD_BATCH = 4;
|
|
791
|
+
/** Pause between batches when restoring expanded folders. */
|
|
792
|
+
const DIR_LOAD_GAP_MS = 50;
|
|
738
793
|
const EMPTY_EXPANDED = /* @__PURE__ */ new Set();
|
|
739
794
|
/** True when two directory listings are identical (name/kind/size). */
|
|
740
795
|
function sameEntries(a, b) {
|
|
@@ -742,7 +797,71 @@ window.__ModuleLoader__.load({
|
|
|
742
797
|
for (let i = 0; i < a.length; i += 1) if (a[i].name !== b[i].name || a[i].kind !== b[i].kind || a[i].size !== b[i].size) return false;
|
|
743
798
|
return true;
|
|
744
799
|
}
|
|
745
|
-
|
|
800
|
+
/**
|
|
801
|
+
* One tree row, memoized so opening/activating a file (which changes the
|
|
802
|
+
* active-path highlight) re-renders only the affected row — with a workspace
|
|
803
|
+
* full of files, re-rendering the whole tree on every click is what made
|
|
804
|
+
* opening files feel laggy.
|
|
805
|
+
*/
|
|
806
|
+
const TreeRow = (0, react.memo)(function TreeRow({ entry, depth, isActive, isExpanded, onToggleDir, onRefreshDir, onSelect, onDoubleClick, onOpenExternal, onContextMenu, t }) {
|
|
807
|
+
const isDir = entry.kind === "dir";
|
|
808
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
809
|
+
className: `${files_module_css_default.row} ${isActive ? files_module_css_default.rowSelected : ""}`,
|
|
810
|
+
style: { paddingLeft: 8 + depth * 14 },
|
|
811
|
+
onClick: () => onSelect(entry),
|
|
812
|
+
onDoubleClick: () => onDoubleClick(entry),
|
|
813
|
+
onContextMenu: (e) => onContextMenu(e, entry),
|
|
814
|
+
role: "treeitem",
|
|
815
|
+
"aria-selected": isActive,
|
|
816
|
+
"aria-expanded": isDir ? isExpanded : void 0,
|
|
817
|
+
title: entry.name,
|
|
818
|
+
children: [
|
|
819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
820
|
+
className: files_module_css_default.chevron,
|
|
821
|
+
onClick: (e) => {
|
|
822
|
+
e.stopPropagation();
|
|
823
|
+
if (isDir) onToggleDir(entry.path);
|
|
824
|
+
},
|
|
825
|
+
children: isDir ? isExpanded ? "▾" : "▸" : ""
|
|
826
|
+
}),
|
|
827
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
828
|
+
className: files_module_css_default.icon,
|
|
829
|
+
children: isDir ? isExpanded ? "📂" : "📁" : entry.kind === "file" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileIcon, { name: entry.name }) : "·"
|
|
830
|
+
}),
|
|
831
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
832
|
+
className: files_module_css_default.name,
|
|
833
|
+
children: entry.name
|
|
834
|
+
}),
|
|
835
|
+
entry.kind === "file" && entry.size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
836
|
+
className: files_module_css_default.size,
|
|
837
|
+
children: formatSize$1(entry.size)
|
|
838
|
+
}),
|
|
839
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
840
|
+
className: files_module_css_default.actions,
|
|
841
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
842
|
+
type: "button",
|
|
843
|
+
className: files_module_css_default.action,
|
|
844
|
+
title: t("action.open"),
|
|
845
|
+
onClick: (e) => {
|
|
846
|
+
e.stopPropagation();
|
|
847
|
+
onOpenExternal(entry.path);
|
|
848
|
+
},
|
|
849
|
+
children: "↗"
|
|
850
|
+
}), isDir && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
851
|
+
type: "button",
|
|
852
|
+
className: files_module_css_default.action,
|
|
853
|
+
title: t("action.refresh"),
|
|
854
|
+
onClick: (e) => {
|
|
855
|
+
e.stopPropagation();
|
|
856
|
+
onRefreshDir(entry.path);
|
|
857
|
+
},
|
|
858
|
+
children: "⟳"
|
|
859
|
+
})]
|
|
860
|
+
})
|
|
861
|
+
]
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
function FileExplorer({ width, useSessions, t, listDir, openPath, createFile, createDir, renameFile, removePath }) {
|
|
746
865
|
const sessionList = useSessions((s) => s);
|
|
747
866
|
const currentId = sessionList.current;
|
|
748
867
|
const cwd = currentId !== void 0 ? sessionList.byId[currentId]?.cwd : void 0;
|
|
@@ -757,6 +876,42 @@ window.__ModuleLoader__.load({
|
|
|
757
876
|
const [children, setChildren] = (0, react.useState)({});
|
|
758
877
|
const [loadingDirs, setLoadingDirs] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
759
878
|
const [dirErrors, setDirErrors] = (0, react.useState)({});
|
|
879
|
+
const [menu, setMenu] = (0, react.useState)(void 0);
|
|
880
|
+
const [menuPos, setMenuPos] = (0, react.useState)(void 0);
|
|
881
|
+
const menuRef = (0, react.useRef)(null);
|
|
882
|
+
(0, react.useLayoutEffect)(() => {
|
|
883
|
+
if (menu === void 0) {
|
|
884
|
+
setMenuPos(void 0);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const el = menuRef.current;
|
|
888
|
+
if (el === null) return;
|
|
889
|
+
const rect = el.getBoundingClientRect();
|
|
890
|
+
setMenuPos({
|
|
891
|
+
x: Math.max(4, Math.min(menu.x, window.innerWidth - rect.width - 4)),
|
|
892
|
+
y: Math.max(4, Math.min(menu.y, window.innerHeight - rect.height - 4))
|
|
893
|
+
});
|
|
894
|
+
}, [menu]);
|
|
895
|
+
(0, react.useEffect)(() => {
|
|
896
|
+
if (menu === void 0) return void 0;
|
|
897
|
+
const onKeyDown = (e) => {
|
|
898
|
+
if (e.key === "Escape") setMenu(void 0);
|
|
899
|
+
};
|
|
900
|
+
const onMouseDown = (e) => {
|
|
901
|
+
const el = menuRef.current;
|
|
902
|
+
if (el !== null && e.target instanceof Node && el.contains(e.target)) return;
|
|
903
|
+
setMenu(void 0);
|
|
904
|
+
};
|
|
905
|
+
const onBlur = () => setMenu(void 0);
|
|
906
|
+
document.addEventListener("keydown", onKeyDown);
|
|
907
|
+
document.addEventListener("mousedown", onMouseDown);
|
|
908
|
+
window.addEventListener("blur", onBlur);
|
|
909
|
+
return () => {
|
|
910
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
911
|
+
document.removeEventListener("mousedown", onMouseDown);
|
|
912
|
+
window.removeEventListener("blur", onBlur);
|
|
913
|
+
};
|
|
914
|
+
}, [menu]);
|
|
760
915
|
const treeRef = (0, react.useRef)({
|
|
761
916
|
root,
|
|
762
917
|
children,
|
|
@@ -769,6 +924,8 @@ window.__ModuleLoader__.load({
|
|
|
769
924
|
expanded,
|
|
770
925
|
rootLoading
|
|
771
926
|
};
|
|
927
|
+
const childrenRef = (0, react.useRef)(children);
|
|
928
|
+
childrenRef.current = children;
|
|
772
929
|
(0, react.useEffect)(() => {
|
|
773
930
|
document.body.style.setProperty("--dsh-explorer-width", `${width}px`);
|
|
774
931
|
return () => {
|
|
@@ -790,12 +947,18 @@ window.__ModuleLoader__.load({
|
|
|
790
947
|
setRootLoading(true);
|
|
791
948
|
const controller = new AbortController();
|
|
792
949
|
rootAbortRef.current = controller;
|
|
793
|
-
listDir(cwd, controller.signal).then((result) => {
|
|
950
|
+
listDir(cwd, controller.signal).then(async (result) => {
|
|
794
951
|
if (controller.signal.aborted) return;
|
|
795
952
|
setRoot(result.root);
|
|
796
953
|
setChildren({ [result.root]: result.entries });
|
|
797
954
|
setRootError(void 0);
|
|
798
|
-
|
|
955
|
+
const dirs = [...expanded].filter((dirPath) => dirPath !== result.root);
|
|
956
|
+
for (let i = 0; i < dirs.length; i += DIR_LOAD_BATCH) {
|
|
957
|
+
if (controller.signal.aborted) return;
|
|
958
|
+
const batch = dirs.slice(i, i + DIR_LOAD_BATCH);
|
|
959
|
+
await Promise.all(batch.map((dirPath) => loadDir(dirPath)));
|
|
960
|
+
if (i + DIR_LOAD_BATCH < dirs.length) await new Promise((resolve) => setTimeout(resolve, DIR_LOAD_GAP_MS));
|
|
961
|
+
}
|
|
799
962
|
}).catch((error) => {
|
|
800
963
|
if (controller.signal.aborted) return;
|
|
801
964
|
setRootError(error instanceof Error ? error.message : String(error));
|
|
@@ -905,12 +1068,8 @@ window.__ModuleLoader__.load({
|
|
|
905
1068
|
[cwdKey]: next
|
|
906
1069
|
};
|
|
907
1070
|
});
|
|
908
|
-
if (!
|
|
909
|
-
}, [
|
|
910
|
-
cwdKey,
|
|
911
|
-
children,
|
|
912
|
-
loadDir
|
|
913
|
-
]);
|
|
1071
|
+
if (!childrenRef.current[dirPath]) loadDir(dirPath);
|
|
1072
|
+
}, [cwdKey, loadDir]);
|
|
914
1073
|
const refreshDir = (0, react.useCallback)((dirPath) => {
|
|
915
1074
|
setChildren((prev) => {
|
|
916
1075
|
const next = { ...prev };
|
|
@@ -919,6 +1078,88 @@ window.__ModuleLoader__.load({
|
|
|
919
1078
|
});
|
|
920
1079
|
loadDir(dirPath);
|
|
921
1080
|
}, [loadDir]);
|
|
1081
|
+
const openContextMenu = (0, react.useCallback)((e, entry) => {
|
|
1082
|
+
e.preventDefault();
|
|
1083
|
+
e.stopPropagation();
|
|
1084
|
+
setMenu({
|
|
1085
|
+
kind: entry.kind === "dir" ? "dir" : "file",
|
|
1086
|
+
path: entry.path,
|
|
1087
|
+
x: e.clientX,
|
|
1088
|
+
y: e.clientY
|
|
1089
|
+
});
|
|
1090
|
+
}, []);
|
|
1091
|
+
const runAction = (0, react.useCallback)((action) => {
|
|
1092
|
+
setMenu(void 0);
|
|
1093
|
+
action();
|
|
1094
|
+
}, []);
|
|
1095
|
+
const applyMutation = (0, react.useCallback)(async (op, refreshPath) => {
|
|
1096
|
+
try {
|
|
1097
|
+
await op();
|
|
1098
|
+
refreshDir(refreshPath);
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1101
|
+
if (refreshPath === root) setRootError(message);
|
|
1102
|
+
else setDirErrors((prev) => ({
|
|
1103
|
+
...prev,
|
|
1104
|
+
[refreshPath]: message
|
|
1105
|
+
}));
|
|
1106
|
+
}
|
|
1107
|
+
}, [refreshDir, root]);
|
|
1108
|
+
const onNewFile = (0, react.useCallback)((dirPath) => {
|
|
1109
|
+
const name = window.prompt(`${t("prompt.newFileName")}:`, "");
|
|
1110
|
+
if (name === null) return;
|
|
1111
|
+
const trimmed = name.trim();
|
|
1112
|
+
if (trimmed === "") return;
|
|
1113
|
+
applyMutation(() => createFile(joinPath(dirPath, trimmed)), dirPath);
|
|
1114
|
+
}, [
|
|
1115
|
+
applyMutation,
|
|
1116
|
+
createFile,
|
|
1117
|
+
t
|
|
1118
|
+
]);
|
|
1119
|
+
const onNewFolder = (0, react.useCallback)((dirPath) => {
|
|
1120
|
+
const name = window.prompt(`${t("prompt.newFolderName")}:`, "");
|
|
1121
|
+
if (name === null) return;
|
|
1122
|
+
const trimmed = name.trim();
|
|
1123
|
+
if (trimmed === "") return;
|
|
1124
|
+
applyMutation(() => createDir(joinPath(dirPath, trimmed)), dirPath);
|
|
1125
|
+
}, [
|
|
1126
|
+
applyMutation,
|
|
1127
|
+
createDir,
|
|
1128
|
+
t
|
|
1129
|
+
]);
|
|
1130
|
+
const onRename = (0, react.useCallback)((path) => {
|
|
1131
|
+
const current = basenameOf$1(path);
|
|
1132
|
+
const name = window.prompt(`${t("prompt.renameTo")}:`, current);
|
|
1133
|
+
if (name === null) return;
|
|
1134
|
+
const trimmed = name.trim();
|
|
1135
|
+
if (trimmed === "" || trimmed === current) return;
|
|
1136
|
+
const parent = parentOf(path);
|
|
1137
|
+
const to = joinPath(parent, trimmed);
|
|
1138
|
+
applyMutation(async () => {
|
|
1139
|
+
await renameFile(path, to);
|
|
1140
|
+
retargetFile(path, to);
|
|
1141
|
+
}, parent);
|
|
1142
|
+
}, [
|
|
1143
|
+
applyMutation,
|
|
1144
|
+
renameFile,
|
|
1145
|
+
t
|
|
1146
|
+
]);
|
|
1147
|
+
const onDelete = (0, react.useCallback)((path, kind) => {
|
|
1148
|
+
const name = basenameOf$1(path);
|
|
1149
|
+
const message = kind === "dir" ? t("confirm.deleteDir", { name }) : t("confirm.deleteFile", { name });
|
|
1150
|
+
if (!window.confirm(message)) return;
|
|
1151
|
+
applyMutation(async () => {
|
|
1152
|
+
await removePath(path);
|
|
1153
|
+
closeFilesUnder(path);
|
|
1154
|
+
}, parentOf(path));
|
|
1155
|
+
}, [
|
|
1156
|
+
applyMutation,
|
|
1157
|
+
removePath,
|
|
1158
|
+
t
|
|
1159
|
+
]);
|
|
1160
|
+
const onCopyPath = (0, react.useCallback)((path) => {
|
|
1161
|
+
navigator.clipboard.writeText(path).catch(() => {});
|
|
1162
|
+
}, []);
|
|
922
1163
|
const onRowClick = (0, react.useCallback)((entry) => {
|
|
923
1164
|
if (entry.kind === "dir") {
|
|
924
1165
|
toggleDir(entry.path);
|
|
@@ -946,59 +1187,18 @@ window.__ModuleLoader__.load({
|
|
|
946
1187
|
const isLoading = isDir && loadingDirs.has(entry.path);
|
|
947
1188
|
const childEntries = isDir && isExpanded ? children[entry.path] : void 0;
|
|
948
1189
|
const error = isDir ? dirErrors[entry.path] : void 0;
|
|
949
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
onClick: (e) => {
|
|
962
|
-
e.stopPropagation();
|
|
963
|
-
if (isDir) toggleDir(entry.path);
|
|
964
|
-
},
|
|
965
|
-
children: isDir ? isExpanded ? "▾" : "▸" : ""
|
|
966
|
-
}),
|
|
967
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
968
|
-
className: files_module_css_default.icon,
|
|
969
|
-
children: isDir ? isExpanded ? "📂" : "📁" : entry.kind === "file" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileIcon, { name: entry.name }) : "·"
|
|
970
|
-
}),
|
|
971
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
972
|
-
className: files_module_css_default.name,
|
|
973
|
-
children: entry.name
|
|
974
|
-
}),
|
|
975
|
-
entry.kind === "file" && entry.size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
976
|
-
className: files_module_css_default.size,
|
|
977
|
-
children: formatSize$1(entry.size)
|
|
978
|
-
}),
|
|
979
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
980
|
-
className: files_module_css_default.actions,
|
|
981
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
982
|
-
type: "button",
|
|
983
|
-
className: files_module_css_default.action,
|
|
984
|
-
title: t("action.open"),
|
|
985
|
-
onClick: (e) => {
|
|
986
|
-
e.stopPropagation();
|
|
987
|
-
openPath(entry.path);
|
|
988
|
-
},
|
|
989
|
-
children: "↗"
|
|
990
|
-
}), isDir && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
991
|
-
type: "button",
|
|
992
|
-
className: files_module_css_default.action,
|
|
993
|
-
title: t("action.refresh"),
|
|
994
|
-
onClick: (e) => {
|
|
995
|
-
e.stopPropagation();
|
|
996
|
-
refreshDir(entry.path);
|
|
997
|
-
},
|
|
998
|
-
children: "⟳"
|
|
999
|
-
})]
|
|
1000
|
-
})
|
|
1001
|
-
]
|
|
1190
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TreeRow, {
|
|
1191
|
+
entry,
|
|
1192
|
+
depth,
|
|
1193
|
+
isActive: entry.kind === "file" && activePath === entry.path,
|
|
1194
|
+
isExpanded,
|
|
1195
|
+
onToggleDir: toggleDir,
|
|
1196
|
+
onRefreshDir: refreshDir,
|
|
1197
|
+
onSelect: onRowClick,
|
|
1198
|
+
onDoubleClick: onRowDoubleClick,
|
|
1199
|
+
onOpenExternal: openPath,
|
|
1200
|
+
onContextMenu: openContextMenu,
|
|
1201
|
+
t
|
|
1002
1202
|
}), isDir && isExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
1003
1203
|
isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1004
1204
|
className: files_module_css_default.rowHint,
|
|
@@ -1018,61 +1218,173 @@ window.__ModuleLoader__.load({
|
|
|
1018
1218
|
childEntries !== void 0 && renderEntries(childEntries, depth + 1)
|
|
1019
1219
|
] })] }, entry.path);
|
|
1020
1220
|
});
|
|
1221
|
+
const menuItems = (m) => {
|
|
1222
|
+
if (m.kind === "file") return [
|
|
1223
|
+
{
|
|
1224
|
+
label: t("menu.open"),
|
|
1225
|
+
onClick: () => openFile(m.path)
|
|
1226
|
+
},
|
|
1227
|
+
"divider",
|
|
1228
|
+
{
|
|
1229
|
+
label: t("menu.copyPath"),
|
|
1230
|
+
onClick: () => onCopyPath(m.path)
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
label: t("menu.openSystem"),
|
|
1234
|
+
onClick: () => void openPath(m.path)
|
|
1235
|
+
},
|
|
1236
|
+
"divider",
|
|
1237
|
+
{
|
|
1238
|
+
label: t("menu.rename"),
|
|
1239
|
+
onClick: () => onRename(m.path)
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
label: t("menu.delete"),
|
|
1243
|
+
danger: true,
|
|
1244
|
+
onClick: () => onDelete(m.path, "file")
|
|
1245
|
+
}
|
|
1246
|
+
];
|
|
1247
|
+
if (m.kind === "dir") return [
|
|
1248
|
+
{
|
|
1249
|
+
label: t("menu.newFile"),
|
|
1250
|
+
onClick: () => onNewFile(m.path)
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
label: t("menu.newFolder"),
|
|
1254
|
+
onClick: () => onNewFolder(m.path)
|
|
1255
|
+
},
|
|
1256
|
+
"divider",
|
|
1257
|
+
{
|
|
1258
|
+
label: t("menu.copyPath"),
|
|
1259
|
+
onClick: () => onCopyPath(m.path)
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
label: t("menu.openSystem"),
|
|
1263
|
+
onClick: () => void openPath(m.path)
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
label: t("menu.refresh"),
|
|
1267
|
+
onClick: () => refreshDir(m.path)
|
|
1268
|
+
},
|
|
1269
|
+
"divider",
|
|
1270
|
+
{
|
|
1271
|
+
label: t("menu.rename"),
|
|
1272
|
+
onClick: () => onRename(m.path)
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
label: t("menu.delete"),
|
|
1276
|
+
danger: true,
|
|
1277
|
+
onClick: () => onDelete(m.path, "dir")
|
|
1278
|
+
}
|
|
1279
|
+
];
|
|
1280
|
+
return [
|
|
1281
|
+
{
|
|
1282
|
+
label: t("menu.newFile"),
|
|
1283
|
+
onClick: () => onNewFile(m.path)
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
label: t("menu.newFolder"),
|
|
1287
|
+
onClick: () => onNewFolder(m.path)
|
|
1288
|
+
},
|
|
1289
|
+
"divider",
|
|
1290
|
+
{
|
|
1291
|
+
label: t("menu.copyPath"),
|
|
1292
|
+
onClick: () => onCopyPath(m.path)
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
label: t("menu.refresh"),
|
|
1296
|
+
onClick: () => refreshDir(m.path)
|
|
1297
|
+
}
|
|
1298
|
+
];
|
|
1299
|
+
};
|
|
1021
1300
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1022
1301
|
className: files_module_css_default.column,
|
|
1023
1302
|
style: { width: width > 0 ? width : void 0 },
|
|
1024
1303
|
"data-pane": "explorer",
|
|
1025
1304
|
"data-fe-theme": theme,
|
|
1026
|
-
children: [
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1305
|
+
children: [
|
|
1306
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1307
|
+
className: files_module_css_default.header,
|
|
1308
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1309
|
+
className: files_module_css_default.headerTitle,
|
|
1310
|
+
title: root ?? cwd,
|
|
1311
|
+
children: basenameOf$1(root ?? cwd)
|
|
1312
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1313
|
+
className: files_module_css_default.headerActions,
|
|
1314
|
+
children: [
|
|
1315
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1316
|
+
type: "button",
|
|
1317
|
+
className: files_module_css_default.action,
|
|
1318
|
+
title: t("action.theme"),
|
|
1319
|
+
onClick: toggleTheme,
|
|
1320
|
+
children: theme === "dark" ? "☀" : "🌙"
|
|
1321
|
+
}),
|
|
1322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1323
|
+
type: "button",
|
|
1324
|
+
className: files_module_css_default.action,
|
|
1325
|
+
title: t("action.open"),
|
|
1326
|
+
onClick: () => void openPath(cwd),
|
|
1327
|
+
children: "↗"
|
|
1328
|
+
}),
|
|
1329
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1330
|
+
type: "button",
|
|
1331
|
+
className: files_module_css_default.action,
|
|
1332
|
+
title: t("tab.expand"),
|
|
1333
|
+
onClick: expandPreview,
|
|
1334
|
+
children: ">"
|
|
1335
|
+
})
|
|
1336
|
+
]
|
|
1337
|
+
})]
|
|
1338
|
+
}),
|
|
1339
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1340
|
+
className: files_module_css_default.treeArea,
|
|
1341
|
+
onContextMenu: (e) => {
|
|
1342
|
+
e.preventDefault();
|
|
1343
|
+
const target = root ?? cwd;
|
|
1344
|
+
if (target === void 0) return;
|
|
1345
|
+
setMenu({
|
|
1346
|
+
kind: "root",
|
|
1347
|
+
path: target,
|
|
1348
|
+
x: e.clientX,
|
|
1349
|
+
y: e.clientY
|
|
1350
|
+
});
|
|
1351
|
+
},
|
|
1034
1352
|
children: [
|
|
1035
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
title: t("action.theme"),
|
|
1039
|
-
onClick: toggleTheme,
|
|
1040
|
-
children: theme === "dark" ? "☀" : "🌙"
|
|
1353
|
+
rootLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1354
|
+
className: files_module_css_default.rowHint,
|
|
1355
|
+
children: t("preview.loading")
|
|
1041
1356
|
}),
|
|
1042
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
title: t("action.open"),
|
|
1046
|
-
onClick: () => void openPath(cwd),
|
|
1047
|
-
children: "↗"
|
|
1357
|
+
rootError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1358
|
+
className: files_module_css_default.rowError,
|
|
1359
|
+
children: rootError
|
|
1048
1360
|
}),
|
|
1049
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
children: ">"
|
|
1055
|
-
})
|
|
1361
|
+
!rootLoading && rootError === void 0 && root !== void 0 && children[root] !== void 0 && children[root].length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1362
|
+
className: files_module_css_default.rowHint,
|
|
1363
|
+
children: t("preview.emptyDir")
|
|
1364
|
+
}),
|
|
1365
|
+
root !== void 0 && children[root] !== void 0 && renderEntries(children[root], 0)
|
|
1056
1366
|
]
|
|
1057
|
-
})
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1367
|
+
}),
|
|
1368
|
+
menu !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1369
|
+
ref: menuRef,
|
|
1370
|
+
className: files_module_css_default.contextMenu,
|
|
1371
|
+
role: "menu",
|
|
1372
|
+
style: menuPos !== void 0 ? {
|
|
1373
|
+
left: menuPos.x,
|
|
1374
|
+
top: menuPos.y
|
|
1375
|
+
} : {
|
|
1376
|
+
left: menu.x,
|
|
1377
|
+
top: menu.y,
|
|
1378
|
+
visibility: "hidden"
|
|
1379
|
+
},
|
|
1380
|
+
children: menuItems(menu).map((item, index) => item === "divider" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: files_module_css_default.contextMenuDivider }, index) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1381
|
+
role: "menuitem",
|
|
1382
|
+
className: `${files_module_css_default.contextMenuItem}${item.danger ? ` ${files_module_css_default.contextMenuItemDanger}` : ""}`,
|
|
1383
|
+
onClick: () => runAction(item.onClick),
|
|
1384
|
+
children: item.label
|
|
1385
|
+
}, index))
|
|
1386
|
+
})
|
|
1387
|
+
]
|
|
1076
1388
|
});
|
|
1077
1389
|
}
|
|
1078
1390
|
var core_default = (/* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -8705,6 +9017,44 @@ window.__ModuleLoader__.load({
|
|
|
8705
9017
|
const PREVIEW_MIN = 240;
|
|
8706
9018
|
const CHAT_MIN = 240;
|
|
8707
9019
|
const PREVIEW_TOO_LARGE_LABEL = "512KB";
|
|
9020
|
+
/** Same-origin raw-bytes route registered by the host half (see src/index.ts). */
|
|
9021
|
+
const RAW_PREFIX = "/dsh-plugin-files/raw";
|
|
9022
|
+
const IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
9023
|
+
"png",
|
|
9024
|
+
"jpg",
|
|
9025
|
+
"jpeg",
|
|
9026
|
+
"gif",
|
|
9027
|
+
"webp",
|
|
9028
|
+
"avif",
|
|
9029
|
+
"bmp",
|
|
9030
|
+
"ico",
|
|
9031
|
+
"svg"
|
|
9032
|
+
]);
|
|
9033
|
+
/**
|
|
9034
|
+
* Same-origin URL serving the file's bytes when it is a previewable image;
|
|
9035
|
+
* undefined otherwise. Image tabs never go through the text RPC read — the
|
|
9036
|
+
* host route resolves the path through the sandboxed fs service itself.
|
|
9037
|
+
*/
|
|
9038
|
+
function imageSrcOf(path) {
|
|
9039
|
+
const idx = path.lastIndexOf(".");
|
|
9040
|
+
if (idx < 0 || idx === path.length - 1) return void 0;
|
|
9041
|
+
const ext = path.slice(idx + 1).toLowerCase();
|
|
9042
|
+
return IMAGE_EXTENSIONS.has(ext) ? `${RAW_PREFIX}/${encodeURIComponent(path)}` : void 0;
|
|
9043
|
+
}
|
|
9044
|
+
/**
|
|
9045
|
+
* Above this size the overlay editor (syntax-highlight layer + transparent
|
|
9046
|
+
* textarea) falls back to a plain textarea: re-injecting and re-laying out
|
|
9047
|
+
* hundreds of KB of wrapped text on every keystroke is what makes the page
|
|
9048
|
+
* lag. The plain textarea keeps editing, wrapping and scrolling — it only
|
|
9049
|
+
* loses the colors, which files this big rarely need anyway.
|
|
9050
|
+
*/
|
|
9051
|
+
const HIGHLIGHT_MAX_BYTES = 65536;
|
|
9052
|
+
/**
|
|
9053
|
+
* Languages always rendered as a plain textarea, never the overlay: the
|
|
9054
|
+
* highlight layer costs a full extra layout pass (and with CJK text a
|
|
9055
|
+
* fragile alignment surface) for prose formats where colors add little.
|
|
9056
|
+
*/
|
|
9057
|
+
const PLAIN_LANGUAGES = /* @__PURE__ */ new Set(["markdown"]);
|
|
8708
9058
|
function basenameOf(path) {
|
|
8709
9059
|
const trimmed = path.replace(/[\\/]+$/, "");
|
|
8710
9060
|
const idx = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
@@ -8738,6 +9088,18 @@ window.__ModuleLoader__.load({
|
|
|
8738
9088
|
size: result.size
|
|
8739
9089
|
};
|
|
8740
9090
|
}
|
|
9091
|
+
/**
|
|
9092
|
+
* Gutter text for an editor: one logical line number per source line, as a
|
|
9093
|
+
* single pre-formatted block (VS Code shows logical numbers even when soft
|
|
9094
|
+
* wrap makes a line occupy several visual rows).
|
|
9095
|
+
*/
|
|
9096
|
+
function lineNumbersOf(content) {
|
|
9097
|
+
const count = content.split("\n").length;
|
|
9098
|
+
if (count <= 1) return "1";
|
|
9099
|
+
const parts = new Array(count);
|
|
9100
|
+
for (let i = 0; i < count; i += 1) parts[i] = String(i + 1);
|
|
9101
|
+
return parts.join("\n");
|
|
9102
|
+
}
|
|
8741
9103
|
function FilePreview({ t, readFile, writeFile }) {
|
|
8742
9104
|
const { tabs, active, theme, collapsed, wrap } = useTabsState();
|
|
8743
9105
|
const [previewWidth, setPreviewWidth] = (0, react.useState)(null);
|
|
@@ -8747,41 +9109,54 @@ window.__ModuleLoader__.load({
|
|
|
8747
9109
|
const cacheRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
8748
9110
|
const hasOpenedRef = (0, react.useRef)(false);
|
|
8749
9111
|
const [, bump] = (0, react.useState)(0);
|
|
9112
|
+
const [imageFailed, setImageFailed] = (0, react.useState)(void 0);
|
|
8750
9113
|
const previewRef = (0, react.useRef)(null);
|
|
8751
9114
|
const handleRef = (0, react.useRef)(null);
|
|
8752
9115
|
const highlightRef = (0, react.useRef)(null);
|
|
9116
|
+
const textareaRef = (0, react.useRef)(null);
|
|
9117
|
+
const gutterRef = (0, react.useRef)(null);
|
|
8753
9118
|
const refresh = (0, react.useCallback)(() => bump((v) => v + 1), []);
|
|
8754
9119
|
(0, react.useEffect)(() => {
|
|
8755
9120
|
const cache = cacheRef.current;
|
|
8756
9121
|
for (const [path] of cache) if (!tabs.includes(path)) cache.delete(path);
|
|
8757
|
-
const
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
}
|
|
8768
|
-
}).catch((error) => {
|
|
8769
|
-
if (!controller.signal.aborted) {
|
|
8770
|
-
cache.set(path, {
|
|
8771
|
-
status: "error",
|
|
8772
|
-
message: error instanceof Error ? error.message : String(error)
|
|
8773
|
-
});
|
|
8774
|
-
refresh();
|
|
8775
|
-
}
|
|
8776
|
-
});
|
|
9122
|
+
const target = active ?? tabs[0];
|
|
9123
|
+
if (target === void 0 || cache.has(target)) {
|
|
9124
|
+
refresh();
|
|
9125
|
+
return;
|
|
9126
|
+
}
|
|
9127
|
+
const controller = new AbortController();
|
|
9128
|
+
if (imageSrcOf(target) !== void 0) {
|
|
9129
|
+
cache.set(target, { status: "image" });
|
|
9130
|
+
refresh();
|
|
9131
|
+
return () => controller.abort();
|
|
8777
9132
|
}
|
|
9133
|
+
cache.set(target, { status: "loading" });
|
|
9134
|
+
readFile(target, controller.signal).then((result) => {
|
|
9135
|
+
if (!controller.signal.aborted) {
|
|
9136
|
+
cache.set(target, previewDataOf(result));
|
|
9137
|
+
if (result.size > HIGHLIGHT_MAX_BYTES) requestAnimationFrame(refresh);
|
|
9138
|
+
else refresh();
|
|
9139
|
+
}
|
|
9140
|
+
}).catch((error) => {
|
|
9141
|
+
if (!controller.signal.aborted) {
|
|
9142
|
+
cache.set(target, {
|
|
9143
|
+
status: "error",
|
|
9144
|
+
message: error instanceof Error ? error.message : String(error)
|
|
9145
|
+
});
|
|
9146
|
+
refresh();
|
|
9147
|
+
}
|
|
9148
|
+
});
|
|
8778
9149
|
refresh();
|
|
8779
|
-
return () =>
|
|
9150
|
+
return () => controller.abort();
|
|
8780
9151
|
}, [
|
|
8781
9152
|
tabs,
|
|
9153
|
+
active,
|
|
8782
9154
|
readFile,
|
|
8783
9155
|
refresh
|
|
8784
9156
|
]);
|
|
9157
|
+
(0, react.useEffect)(() => {
|
|
9158
|
+
setImageFailed(void 0);
|
|
9159
|
+
}, [active]);
|
|
8785
9160
|
if (tabs.length > 0) hasOpenedRef.current = true;
|
|
8786
9161
|
(0, react.useEffect)(() => {
|
|
8787
9162
|
if (tabs.length > 0) {
|
|
@@ -8798,6 +9173,33 @@ window.__ModuleLoader__.load({
|
|
|
8798
9173
|
}
|
|
8799
9174
|
}, [tabs.length]);
|
|
8800
9175
|
const isOpen = !collapsed && (tabs.length > 0 || closing || hasOpenedRef.current);
|
|
9176
|
+
const activeData = active !== void 0 ? cacheRef.current.get(active) : void 0;
|
|
9177
|
+
const language = active !== void 0 ? detectLanguage(active) : void 0;
|
|
9178
|
+
const tooBig = (activeData?.size ?? 0) > HIGHLIGHT_MAX_BYTES;
|
|
9179
|
+
const plain = language === void 0 || tooBig || language !== void 0 && PLAIN_LANGUAGES.has(language);
|
|
9180
|
+
const gutterNumbers = (0, react.useMemo)(() => activeData?.status === "loaded" ? lineNumbersOf(activeData.draft ?? "") : "", [activeData?.status, activeData?.draft]);
|
|
9181
|
+
(0, react.useEffect)(() => {
|
|
9182
|
+
const pre = highlightRef.current;
|
|
9183
|
+
const ta = textareaRef.current;
|
|
9184
|
+
const gutter = gutterRef.current;
|
|
9185
|
+
if (ta === null || gutter === null) return void 0;
|
|
9186
|
+
let raf = 0;
|
|
9187
|
+
const tick = () => {
|
|
9188
|
+
if (pre !== null) {
|
|
9189
|
+
pre.scrollTop = ta.scrollTop;
|
|
9190
|
+
pre.scrollLeft = ta.scrollLeft;
|
|
9191
|
+
}
|
|
9192
|
+
gutter.scrollTop = ta.scrollTop;
|
|
9193
|
+
raf = requestAnimationFrame(tick);
|
|
9194
|
+
};
|
|
9195
|
+
raf = requestAnimationFrame(tick);
|
|
9196
|
+
return () => cancelAnimationFrame(raf);
|
|
9197
|
+
}, [
|
|
9198
|
+
isOpen,
|
|
9199
|
+
active,
|
|
9200
|
+
activeData?.status,
|
|
9201
|
+
plain
|
|
9202
|
+
]);
|
|
8801
9203
|
(0, react.useEffect)(() => {
|
|
8802
9204
|
if (!isOpen) {
|
|
8803
9205
|
document.body.style.removeProperty("--dsh-preview-width");
|
|
@@ -8873,11 +9275,13 @@ window.__ModuleLoader__.load({
|
|
|
8873
9275
|
}, [onSave]);
|
|
8874
9276
|
const onTextareaScroll = (0, react.useCallback)((e) => {
|
|
8875
9277
|
const pre = highlightRef.current;
|
|
9278
|
+
const gutter = gutterRef.current;
|
|
8876
9279
|
const ta = e.currentTarget;
|
|
8877
9280
|
if (pre !== null) {
|
|
8878
9281
|
pre.scrollTop = ta.scrollTop;
|
|
8879
9282
|
pre.scrollLeft = ta.scrollLeft;
|
|
8880
9283
|
}
|
|
9284
|
+
if (gutter !== null) gutter.scrollTop = ta.scrollTop;
|
|
8881
9285
|
}, []);
|
|
8882
9286
|
const onTabDragStart = (0, react.useCallback)((e, path) => {
|
|
8883
9287
|
setDragPath(path);
|
|
@@ -8894,7 +9298,6 @@ window.__ModuleLoader__.load({
|
|
|
8894
9298
|
const onTabDragEnd = (0, react.useCallback)(() => {
|
|
8895
9299
|
setDragPath(void 0);
|
|
8896
9300
|
}, []);
|
|
8897
|
-
const activeData = active !== void 0 ? cacheRef.current.get(active) : void 0;
|
|
8898
9301
|
if (!isOpen) return null;
|
|
8899
9302
|
const renderBody = () => {
|
|
8900
9303
|
if (activeData === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -8906,33 +9309,70 @@ window.__ModuleLoader__.load({
|
|
|
8906
9309
|
className: files_module_css_default.previewHint,
|
|
8907
9310
|
children: t("preview.loading")
|
|
8908
9311
|
});
|
|
8909
|
-
case "loaded": {
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
children:
|
|
8920
|
-
}),
|
|
8921
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
8922
|
-
className: files_module_css_default.editorTextarea,
|
|
8923
|
-
value: activeData.draft ?? "",
|
|
8924
|
-
onChange: (e) => onTextareaChange(e.target.value),
|
|
8925
|
-
onScroll: onTextareaScroll,
|
|
8926
|
-
onKeyDown,
|
|
8927
|
-
spellCheck: false,
|
|
8928
|
-
wrap: wrap ? "soft" : "off",
|
|
8929
|
-
title: t("action.saveHint")
|
|
8930
|
-
}),
|
|
8931
|
-
saveError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8932
|
-
className: files_module_css_default.saveError,
|
|
8933
|
-
children: saveError
|
|
9312
|
+
case "loaded": return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9313
|
+
className: `${files_module_css_default.editor}${plain ? ` ${files_module_css_default.editorPlain}` : ""}`,
|
|
9314
|
+
"data-wrap": wrap ? "on" : "off",
|
|
9315
|
+
children: [
|
|
9316
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9317
|
+
ref: gutterRef,
|
|
9318
|
+
className: files_module_css_default.gutter,
|
|
9319
|
+
"aria-hidden": "true",
|
|
9320
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9321
|
+
className: files_module_css_default.gutterNumbers,
|
|
9322
|
+
children: gutterNumbers
|
|
8934
9323
|
})
|
|
8935
|
-
|
|
9324
|
+
}),
|
|
9325
|
+
!plain && active !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
9326
|
+
ref: highlightRef,
|
|
9327
|
+
className: files_module_css_default.editorHighlight,
|
|
9328
|
+
"aria-hidden": "true",
|
|
9329
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { dangerouslySetInnerHTML: { __html: highlightCode(activeData.draft ?? "", active) } })
|
|
9330
|
+
}),
|
|
9331
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
9332
|
+
ref: textareaRef,
|
|
9333
|
+
className: files_module_css_default.editorTextarea,
|
|
9334
|
+
value: activeData.draft ?? "",
|
|
9335
|
+
onChange: (e) => onTextareaChange(e.target.value),
|
|
9336
|
+
onScroll: onTextareaScroll,
|
|
9337
|
+
onKeyDown,
|
|
9338
|
+
spellCheck: false,
|
|
9339
|
+
wrap: wrap ? "soft" : "off"
|
|
9340
|
+
}),
|
|
9341
|
+
language !== void 0 && tooBig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9342
|
+
className: files_module_css_default.editorHint,
|
|
9343
|
+
children: t("preview.highlightOff")
|
|
9344
|
+
}),
|
|
9345
|
+
saveError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9346
|
+
className: files_module_css_default.saveError,
|
|
9347
|
+
children: saveError
|
|
9348
|
+
})
|
|
9349
|
+
]
|
|
9350
|
+
});
|
|
9351
|
+
case "image": {
|
|
9352
|
+
const src = active !== void 0 ? imageSrcOf(active) : void 0;
|
|
9353
|
+
const name = active !== void 0 ? basenameOf(active) : "";
|
|
9354
|
+
if (src === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9355
|
+
className: files_module_css_default.previewHint,
|
|
9356
|
+
children: t("preview.binary")
|
|
9357
|
+
});
|
|
9358
|
+
if (imageFailed === active) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9359
|
+
className: files_module_css_default.previewHint,
|
|
9360
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("preview.imageFailed") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9361
|
+
className: files_module_css_default.previewMeta,
|
|
9362
|
+
children: name
|
|
9363
|
+
})]
|
|
9364
|
+
});
|
|
9365
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9366
|
+
className: files_module_css_default.imageView,
|
|
9367
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
9368
|
+
className: files_module_css_default.image,
|
|
9369
|
+
src,
|
|
9370
|
+
alt: name,
|
|
9371
|
+
onError: () => setImageFailed(active)
|
|
9372
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9373
|
+
className: files_module_css_default.previewMeta,
|
|
9374
|
+
children: name
|
|
9375
|
+
})]
|
|
8936
9376
|
});
|
|
8937
9377
|
}
|
|
8938
9378
|
case "binary": return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -9047,11 +9487,26 @@ window.__ModuleLoader__.load({
|
|
|
9047
9487
|
"tab.close": "关闭标签页",
|
|
9048
9488
|
"tab.collapse": "收起文件详情",
|
|
9049
9489
|
"tab.expand": "弹出文件详情",
|
|
9490
|
+
"menu.open": "打开预览",
|
|
9491
|
+
"menu.newFile": "新建文件",
|
|
9492
|
+
"menu.newFolder": "新建文件夹",
|
|
9493
|
+
"menu.rename": "重命名",
|
|
9494
|
+
"menu.delete": "删除",
|
|
9495
|
+
"menu.copyPath": "复制路径",
|
|
9496
|
+
"menu.refresh": "刷新",
|
|
9497
|
+
"menu.openSystem": "在系统中打开",
|
|
9498
|
+
"prompt.newFileName": "新文件名",
|
|
9499
|
+
"prompt.newFolderName": "新文件夹名",
|
|
9500
|
+
"prompt.renameTo": "重命名为",
|
|
9501
|
+
"confirm.deleteFile": "确定删除文件「{name}」吗?",
|
|
9502
|
+
"confirm.deleteDir": "确定删除文件夹「{name}」及其全部内容吗?",
|
|
9050
9503
|
"preview.loading": "加载中…",
|
|
9051
9504
|
"preview.binary": "二进制文件,无法预览",
|
|
9505
|
+
"preview.imageFailed": "图片加载失败",
|
|
9052
9506
|
"preview.tooLarge": "文件过大,仅支持预览不超过 {limit}",
|
|
9053
9507
|
"preview.emptyDir": "空目录",
|
|
9054
|
-
"preview.error": "加载失败"
|
|
9508
|
+
"preview.error": "加载失败",
|
|
9509
|
+
"preview.highlightOff": "大文件:已关闭语法高亮(仍可编辑)"
|
|
9055
9510
|
};
|
|
9056
9511
|
const en = {
|
|
9057
9512
|
"placeholder.noSession": "No session",
|
|
@@ -9065,11 +9520,26 @@ window.__ModuleLoader__.load({
|
|
|
9065
9520
|
"tab.close": "Close tab",
|
|
9066
9521
|
"tab.collapse": "Collapse file details",
|
|
9067
9522
|
"tab.expand": "Expand file details",
|
|
9523
|
+
"menu.open": "Open preview",
|
|
9524
|
+
"menu.newFile": "New File",
|
|
9525
|
+
"menu.newFolder": "New Folder",
|
|
9526
|
+
"menu.rename": "Rename",
|
|
9527
|
+
"menu.delete": "Delete",
|
|
9528
|
+
"menu.copyPath": "Copy Path",
|
|
9529
|
+
"menu.refresh": "Refresh",
|
|
9530
|
+
"menu.openSystem": "Open in System",
|
|
9531
|
+
"prompt.newFileName": "New file name",
|
|
9532
|
+
"prompt.newFolderName": "New folder name",
|
|
9533
|
+
"prompt.renameTo": "Rename to",
|
|
9534
|
+
"confirm.deleteFile": "Delete file \"{name}\"?",
|
|
9535
|
+
"confirm.deleteDir": "Delete folder \"{name}\" and all its contents?",
|
|
9068
9536
|
"preview.loading": "Loading…",
|
|
9069
9537
|
"preview.binary": "Binary file, cannot preview",
|
|
9538
|
+
"preview.imageFailed": "Failed to load image",
|
|
9070
9539
|
"preview.tooLarge": "File too large to preview (limit {limit})",
|
|
9071
9540
|
"preview.emptyDir": "Empty directory",
|
|
9072
|
-
"preview.error": "Load failed"
|
|
9541
|
+
"preview.error": "Load failed",
|
|
9542
|
+
"preview.highlightOff": "Large file: syntax highlighting off (still editable)"
|
|
9073
9543
|
};
|
|
9074
9544
|
//#endregion
|
|
9075
9545
|
//#region src/client/index.ts
|
|
@@ -9093,13 +9563,24 @@ window.__ModuleLoader__.load({
|
|
|
9093
9563
|
path,
|
|
9094
9564
|
content
|
|
9095
9565
|
}, signal));
|
|
9566
|
+
const createFile = (path, signal) => unwrap(ctx.connection.rpc.call(CHANNEL, "createFile", { path }, signal));
|
|
9567
|
+
const createDir = (path, signal) => unwrap(ctx.connection.rpc.call(CHANNEL, "createDir", { path }, signal));
|
|
9568
|
+
const renameFile = (path, to, signal) => unwrap(ctx.connection.rpc.call(CHANNEL, "rename", {
|
|
9569
|
+
path,
|
|
9570
|
+
to
|
|
9571
|
+
}, signal));
|
|
9572
|
+
const removePath = (path, signal) => unwrap(ctx.connection.rpc.call(CHANNEL, "delete", { path }, signal));
|
|
9096
9573
|
const openPath = (path) => ctx.workspaces.openPath(path);
|
|
9097
9574
|
ctx.slots.inject("explorer", () => ctx.slots.register({
|
|
9098
9575
|
name: "explorer",
|
|
9099
9576
|
locale: NS,
|
|
9100
9577
|
inject: () => ({
|
|
9101
9578
|
listDir,
|
|
9102
|
-
openPath
|
|
9579
|
+
openPath,
|
|
9580
|
+
createFile,
|
|
9581
|
+
createDir,
|
|
9582
|
+
renameFile,
|
|
9583
|
+
removePath
|
|
9103
9584
|
})
|
|
9104
9585
|
}, FileExplorer));
|
|
9105
9586
|
ctx.slots.inject("explorer.preview", () => ctx.slots.register({
|