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/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
- function FileExplorer({ width, useSessions, t, listDir, openPath }) {
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
- for (const dirPath of expanded) if (dirPath !== result.root) loadDir(dirPath);
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 (!children[dirPath]) loadDir(dirPath);
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.jsxs)("div", {
950
- className: `${files_module_css_default.row} ${entry.kind === "file" && activePath === entry.path ? files_module_css_default.rowSelected : ""}`,
951
- style: { paddingLeft: 8 + depth * 14 },
952
- onClick: () => onRowClick(entry),
953
- onDoubleClick: () => onRowDoubleClick(entry),
954
- role: "treeitem",
955
- "aria-selected": entry.kind === "file" && activePath === entry.path,
956
- "aria-expanded": isDir ? isExpanded : void 0,
957
- title: entry.name,
958
- children: [
959
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
960
- className: files_module_css_default.chevron,
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: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1027
- className: files_module_css_default.header,
1028
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1029
- className: files_module_css_default.headerTitle,
1030
- title: root ?? cwd,
1031
- children: basenameOf$1(root ?? cwd)
1032
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1033
- className: files_module_css_default.headerActions,
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)("button", {
1036
- type: "button",
1037
- className: files_module_css_default.action,
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)("button", {
1043
- type: "button",
1044
- className: files_module_css_default.action,
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)("button", {
1050
- type: "button",
1051
- className: files_module_css_default.action,
1052
- title: t("tab.expand"),
1053
- onClick: expandPreview,
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
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1059
- className: files_module_css_default.treeArea,
1060
- children: [
1061
- rootLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1062
- className: files_module_css_default.rowHint,
1063
- children: t("preview.loading")
1064
- }),
1065
- rootError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1066
- className: files_module_css_default.rowError,
1067
- children: rootError
1068
- }),
1069
- !rootLoading && rootError === void 0 && root !== void 0 && children[root] !== void 0 && children[root].length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1070
- className: files_module_css_default.rowHint,
1071
- children: t("preview.emptyDir")
1072
- }),
1073
- root !== void 0 && children[root] !== void 0 && renderEntries(children[root], 0)
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 controllers = [];
8758
- for (const path of tabs) {
8759
- if (cache.has(path)) continue;
8760
- cache.set(path, { status: "loading" });
8761
- const controller = new AbortController();
8762
- controllers.push(controller);
8763
- readFile(path, controller.signal).then((result) => {
8764
- if (!controller.signal.aborted) {
8765
- cache.set(path, previewDataOf(result));
8766
- refresh();
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 () => controllers.forEach((c) => c.abort());
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
- const highlighted = active !== void 0 ? highlightCode(activeData.draft ?? "", active) : "";
8911
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8912
- className: files_module_css_default.editor,
8913
- "data-wrap": wrap ? "on" : "off",
8914
- children: [
8915
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
8916
- ref: highlightRef,
8917
- className: files_module_css_default.editorHighlight,
8918
- "aria-hidden": "true",
8919
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { dangerouslySetInnerHTML: { __html: highlighted } })
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({