@aigne/afs-cli 1.11.0-beta.5 → 1.11.0-beta.7
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/dist/cli.cjs +25 -328
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.mts +2 -1
- package/dist/cli.mjs +26 -328
- package/dist/cli.mjs.map +1 -1
- package/dist/config/afs-loader.cjs +123 -0
- package/dist/config/afs-loader.d.cts +14 -0
- package/dist/config/afs-loader.d.cts.map +1 -0
- package/dist/config/afs-loader.d.mts +14 -0
- package/dist/config/afs-loader.d.mts.map +1 -0
- package/dist/config/afs-loader.mjs +122 -0
- package/dist/config/afs-loader.mjs.map +1 -0
- package/dist/config/loader.cjs +14 -3
- package/dist/config/loader.mjs +14 -3
- package/dist/config/loader.mjs.map +1 -1
- package/dist/{commands/mount.cjs → config/mount-commands.cjs} +13 -49
- package/dist/config/mount-commands.d.cts +20 -0
- package/dist/config/mount-commands.d.cts.map +1 -0
- package/dist/config/mount-commands.d.mts +20 -0
- package/dist/config/mount-commands.d.mts.map +1 -0
- package/dist/{commands/mount.mjs → config/mount-commands.mjs} +14 -49
- package/dist/config/mount-commands.mjs.map +1 -0
- package/dist/config/schema.cjs +9 -1
- package/dist/config/schema.mjs +9 -1
- package/dist/config/schema.mjs.map +1 -1
- package/dist/core/commands/delete.cjs +41 -0
- package/dist/core/commands/delete.d.cts +18 -0
- package/dist/core/commands/delete.d.cts.map +1 -0
- package/dist/core/commands/delete.d.mts +18 -0
- package/dist/core/commands/delete.d.mts.map +1 -0
- package/dist/core/commands/delete.mjs +42 -0
- package/dist/core/commands/delete.mjs.map +1 -0
- package/dist/core/commands/exec.cjs +95 -0
- package/dist/core/commands/exec.d.cts +26 -0
- package/dist/core/commands/exec.d.cts.map +1 -0
- package/dist/core/commands/exec.d.mts +26 -0
- package/dist/core/commands/exec.d.mts.map +1 -0
- package/dist/core/commands/exec.mjs +96 -0
- package/dist/core/commands/exec.mjs.map +1 -0
- package/dist/core/commands/explain.cjs +254 -0
- package/dist/core/commands/explain.d.cts +25 -0
- package/dist/core/commands/explain.d.cts.map +1 -0
- package/dist/core/commands/explain.d.mts +25 -0
- package/dist/core/commands/explain.d.mts.map +1 -0
- package/dist/core/commands/explain.mjs +255 -0
- package/dist/core/commands/explain.mjs.map +1 -0
- package/dist/core/commands/explore.cjs +30 -0
- package/dist/core/commands/explore.d.mts +2 -0
- package/dist/core/commands/explore.mjs +31 -0
- package/dist/core/commands/explore.mjs.map +1 -0
- package/dist/core/commands/index.cjs +36 -0
- package/dist/core/commands/index.d.cts +21 -0
- package/dist/core/commands/index.d.cts.map +1 -0
- package/dist/core/commands/index.d.mts +24 -0
- package/dist/core/commands/index.d.mts.map +1 -0
- package/dist/core/commands/index.mjs +37 -0
- package/dist/core/commands/index.mjs.map +1 -0
- package/dist/core/commands/ls.cjs +57 -0
- package/dist/core/commands/ls.d.cts +21 -0
- package/dist/core/commands/ls.d.cts.map +1 -0
- package/dist/core/commands/ls.d.mts +21 -0
- package/dist/core/commands/ls.d.mts.map +1 -0
- package/dist/core/commands/ls.mjs +58 -0
- package/dist/core/commands/ls.mjs.map +1 -0
- package/dist/core/commands/mount.cjs +139 -0
- package/dist/core/commands/mount.d.cts +33 -0
- package/dist/core/commands/mount.d.cts.map +1 -0
- package/dist/core/commands/mount.d.mts +33 -0
- package/dist/core/commands/mount.d.mts.map +1 -0
- package/dist/core/commands/mount.mjs +140 -0
- package/dist/core/commands/mount.mjs.map +1 -0
- package/dist/core/commands/read.cjs +48 -0
- package/dist/core/commands/read.d.cts +17 -0
- package/dist/core/commands/read.d.cts.map +1 -0
- package/dist/core/commands/read.d.mts +17 -0
- package/dist/core/commands/read.d.mts.map +1 -0
- package/dist/core/commands/read.mjs +49 -0
- package/dist/core/commands/read.mjs.map +1 -0
- package/dist/core/commands/search.cjs +40 -0
- package/dist/core/commands/search.d.mts +2 -0
- package/dist/core/commands/search.mjs +41 -0
- package/dist/core/commands/search.mjs.map +1 -0
- package/dist/core/commands/serve.cjs +242 -0
- package/dist/core/commands/serve.d.mts +2 -0
- package/dist/core/commands/serve.mjs +242 -0
- package/dist/core/commands/serve.mjs.map +1 -0
- package/dist/core/commands/stat.cjs +53 -0
- package/dist/core/commands/stat.d.cts +17 -0
- package/dist/core/commands/stat.d.cts.map +1 -0
- package/dist/core/commands/stat.d.mts +17 -0
- package/dist/core/commands/stat.d.mts.map +1 -0
- package/dist/core/commands/stat.mjs +54 -0
- package/dist/core/commands/stat.mjs.map +1 -0
- package/dist/core/commands/types.cjs +13 -0
- package/dist/core/commands/types.d.cts +54 -0
- package/dist/core/commands/types.d.cts.map +1 -0
- package/dist/core/commands/types.d.mts +54 -0
- package/dist/core/commands/types.d.mts.map +1 -0
- package/dist/core/commands/types.mjs +14 -0
- package/dist/core/commands/types.mjs.map +1 -0
- package/dist/core/commands/write.cjs +70 -0
- package/dist/core/commands/write.d.cts +20 -0
- package/dist/core/commands/write.d.cts.map +1 -0
- package/dist/core/commands/write.d.mts +20 -0
- package/dist/core/commands/write.d.mts.map +1 -0
- package/dist/core/commands/write.mjs +71 -0
- package/dist/core/commands/write.mjs.map +1 -0
- package/dist/core/executor/index.cjs +196 -0
- package/dist/core/executor/index.d.cts +77 -0
- package/dist/core/executor/index.d.cts.map +1 -0
- package/dist/core/executor/index.d.mts +77 -0
- package/dist/core/executor/index.d.mts.map +1 -0
- package/dist/core/executor/index.mjs +195 -0
- package/dist/core/executor/index.mjs.map +1 -0
- package/dist/core/formatters/delete.cjs +37 -0
- package/dist/core/formatters/delete.d.cts +18 -0
- package/dist/core/formatters/delete.d.cts.map +1 -0
- package/dist/core/formatters/delete.d.mts +18 -0
- package/dist/core/formatters/delete.d.mts.map +1 -0
- package/dist/core/formatters/delete.mjs +37 -0
- package/dist/core/formatters/delete.mjs.map +1 -0
- package/dist/core/formatters/exec.cjs +60 -0
- package/dist/core/formatters/exec.d.cts +18 -0
- package/dist/core/formatters/exec.d.cts.map +1 -0
- package/dist/core/formatters/exec.d.mts +18 -0
- package/dist/core/formatters/exec.d.mts.map +1 -0
- package/dist/core/formatters/exec.mjs +60 -0
- package/dist/core/formatters/exec.mjs.map +1 -0
- package/dist/core/formatters/explain.cjs +97 -0
- package/dist/core/formatters/explain.d.cts +11 -0
- package/dist/core/formatters/explain.d.cts.map +1 -0
- package/dist/core/formatters/explain.d.mts +11 -0
- package/dist/core/formatters/explain.d.mts.map +1 -0
- package/dist/core/formatters/explain.mjs +96 -0
- package/dist/core/formatters/explain.mjs.map +1 -0
- package/dist/core/formatters/index.d.mts +9 -0
- package/dist/core/formatters/ls.cjs +179 -0
- package/dist/core/formatters/ls.d.cts +20 -0
- package/dist/core/formatters/ls.d.cts.map +1 -0
- package/dist/core/formatters/ls.d.mts +20 -0
- package/dist/core/formatters/ls.d.mts.map +1 -0
- package/dist/core/formatters/ls.mjs +179 -0
- package/dist/core/formatters/ls.mjs.map +1 -0
- package/dist/core/formatters/mount.cjs +55 -0
- package/dist/core/formatters/mount.d.cts +15 -0
- package/dist/core/formatters/mount.d.cts.map +1 -0
- package/dist/core/formatters/mount.d.mts +15 -0
- package/dist/core/formatters/mount.d.mts.map +1 -0
- package/dist/core/formatters/mount.mjs +55 -0
- package/dist/core/formatters/mount.mjs.map +1 -0
- package/dist/core/formatters/read.cjs +100 -0
- package/dist/core/formatters/read.d.cts +22 -0
- package/dist/core/formatters/read.d.cts.map +1 -0
- package/dist/core/formatters/read.d.mts +22 -0
- package/dist/core/formatters/read.d.mts.map +1 -0
- package/dist/core/formatters/read.mjs +100 -0
- package/dist/core/formatters/read.mjs.map +1 -0
- package/dist/core/formatters/search.cjs +44 -0
- package/dist/core/formatters/search.d.mts +1 -0
- package/dist/core/formatters/search.mjs +44 -0
- package/dist/core/formatters/search.mjs.map +1 -0
- package/dist/core/formatters/stat.cjs +155 -0
- package/dist/core/formatters/stat.d.cts +15 -0
- package/dist/core/formatters/stat.d.cts.map +1 -0
- package/dist/core/formatters/stat.d.mts +15 -0
- package/dist/core/formatters/stat.d.mts.map +1 -0
- package/dist/core/formatters/stat.mjs +155 -0
- package/dist/core/formatters/stat.mjs.map +1 -0
- package/dist/core/formatters/write.cjs +51 -0
- package/dist/core/formatters/write.d.cts +22 -0
- package/dist/core/formatters/write.d.cts.map +1 -0
- package/dist/core/formatters/write.d.mts +22 -0
- package/dist/core/formatters/write.d.mts.map +1 -0
- package/dist/core/formatters/write.mjs +51 -0
- package/dist/core/formatters/write.mjs.map +1 -0
- package/dist/core/helpers/exec-args.cjs +142 -0
- package/dist/core/helpers/exec-args.d.cts +46 -0
- package/dist/core/helpers/exec-args.d.cts.map +1 -0
- package/dist/core/helpers/exec-args.d.mts +46 -0
- package/dist/core/helpers/exec-args.d.mts.map +1 -0
- package/dist/core/helpers/exec-args.mjs +139 -0
- package/dist/core/helpers/exec-args.mjs.map +1 -0
- package/dist/core/helpers/stdin.cjs +41 -0
- package/dist/core/helpers/stdin.d.cts +15 -0
- package/dist/core/helpers/stdin.d.cts.map +1 -0
- package/dist/core/helpers/stdin.d.mts +15 -0
- package/dist/core/helpers/stdin.d.mts.map +1 -0
- package/dist/core/helpers/stdin.mjs +41 -0
- package/dist/core/helpers/stdin.mjs.map +1 -0
- package/dist/core/index.cjs +49 -0
- package/dist/core/index.d.cts +24 -0
- package/dist/core/index.d.mts +25 -0
- package/dist/core/index.mjs +24 -0
- package/dist/core/path-utils.cjs +1 -0
- package/dist/core/path-utils.mjs +3 -0
- package/dist/core/types.d.cts +24 -0
- package/dist/core/types.d.cts.map +1 -0
- package/dist/core/types.d.mts +24 -0
- package/dist/core/types.d.mts.map +1 -0
- package/dist/errors.cjs +0 -11
- package/dist/errors.mjs +1 -11
- package/dist/errors.mjs.map +1 -1
- package/dist/explorer/actions.cjs +113 -48
- package/dist/explorer/actions.mjs +113 -48
- package/dist/explorer/actions.mjs.map +1 -1
- package/dist/explorer/components/dialog.cjs +287 -10
- package/dist/explorer/components/dialog.mjs +287 -10
- package/dist/explorer/components/dialog.mjs.map +1 -1
- package/dist/explorer/components/file-list.mjs.map +1 -1
- package/dist/explorer/components/metadata-panel.cjs +121 -24
- package/dist/explorer/components/metadata-panel.mjs +121 -24
- package/dist/explorer/components/metadata-panel.mjs.map +1 -1
- package/dist/explorer/screen.cjs +72 -21
- package/dist/explorer/screen.d.cts +23 -0
- package/dist/explorer/screen.d.cts.map +1 -0
- package/dist/explorer/screen.d.mts +23 -0
- package/dist/explorer/screen.d.mts.map +1 -0
- package/dist/explorer/screen.mjs +73 -22
- package/dist/explorer/screen.mjs.map +1 -1
- package/dist/explorer/theme.cjs +4 -2
- package/dist/explorer/theme.mjs +4 -2
- package/dist/explorer/theme.mjs.map +1 -1
- package/dist/index.cjs +7 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.mts +4 -1
- package/dist/index.mjs +4 -1
- package/dist/mcp/http-transport.cjs +68 -0
- package/dist/mcp/http-transport.mjs +68 -0
- package/dist/mcp/http-transport.mjs.map +1 -0
- package/dist/mcp/prompts.cjs +48 -0
- package/dist/mcp/prompts.mjs +48 -0
- package/dist/mcp/prompts.mjs.map +1 -0
- package/dist/mcp/resources.cjs +25 -0
- package/dist/mcp/resources.mjs +25 -0
- package/dist/mcp/resources.mjs.map +1 -0
- package/dist/mcp/server.cjs +30 -0
- package/dist/mcp/server.mjs +30 -0
- package/dist/mcp/server.mjs.map +1 -0
- package/dist/mcp/tools.cjs +196 -0
- package/dist/mcp/tools.mjs +196 -0
- package/dist/mcp/tools.mjs.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.cts +10 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.cts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.mts +10 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.mts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.cts +46 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.cts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.mts +46 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.mts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.cjs +902 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.mjs +902 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.mjs.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.cjs +6 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.mjs +8 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.mjs.map +1 -0
- package/dist/path-utils.cjs +2 -1
- package/dist/path-utils.d.cts +50 -0
- package/dist/path-utils.d.cts.map +1 -0
- package/dist/path-utils.d.mts +50 -0
- package/dist/path-utils.d.mts.map +1 -0
- package/dist/path-utils.mjs +1 -1
- package/dist/repl.cjs +485 -0
- package/dist/repl.d.cts +15 -0
- package/dist/repl.d.cts.map +1 -0
- package/dist/repl.d.mts +16 -0
- package/dist/repl.d.mts.map +1 -0
- package/dist/repl.mjs +485 -0
- package/dist/repl.mjs.map +1 -0
- package/dist/serve.cjs +146 -0
- package/dist/serve.d.cts +41 -0
- package/dist/serve.d.cts.map +1 -0
- package/dist/serve.d.mts +41 -0
- package/dist/serve.d.mts.map +1 -0
- package/dist/serve.mjs +146 -0
- package/dist/serve.mjs.map +1 -0
- package/dist/ui/header.cjs +1 -49
- package/dist/ui/header.mjs +1 -47
- package/dist/ui/header.mjs.map +1 -1
- package/dist/ui/index.cjs +2 -11
- package/dist/ui/index.mjs +2 -8
- package/dist/ui/index.mjs.map +1 -1
- package/dist/ui/terminal.cjs +1 -10
- package/dist/ui/terminal.mjs +1 -8
- package/dist/ui/terminal.mjs.map +1 -1
- package/package.json +32 -9
- package/dist/commands/exec.cjs +0 -46
- package/dist/commands/exec.mjs +0 -45
- package/dist/commands/exec.mjs.map +0 -1
- package/dist/commands/explain.cjs +0 -244
- package/dist/commands/explain.mjs +0 -242
- package/dist/commands/explain.mjs.map +0 -1
- package/dist/commands/index.cjs +0 -8
- package/dist/commands/index.mjs +0 -10
- package/dist/commands/ls.cjs +0 -143
- package/dist/commands/ls.mjs +0 -143
- package/dist/commands/ls.mjs.map +0 -1
- package/dist/commands/mount.mjs.map +0 -1
- package/dist/commands/read.cjs +0 -65
- package/dist/commands/read.mjs +0 -64
- package/dist/commands/read.mjs.map +0 -1
- package/dist/commands/serve.cjs +0 -144
- package/dist/commands/serve.mjs +0 -143
- package/dist/commands/serve.mjs.map +0 -1
- package/dist/commands/stat.cjs +0 -113
- package/dist/commands/stat.mjs +0 -112
- package/dist/commands/stat.mjs.map +0 -1
- package/dist/commands/write.cjs +0 -52
- package/dist/commands/write.mjs +0 -51
- package/dist/commands/write.mjs.map +0 -1
- package/dist/config/provider-factory.cjs +0 -93
- package/dist/config/provider-factory.mjs +0 -94
- package/dist/config/provider-factory.mjs.map +0 -1
- package/dist/config/uri-parser.cjs +0 -92
- package/dist/config/uri-parser.mjs +0 -92
- package/dist/config/uri-parser.mjs.map +0 -1
- package/dist/runtime.cjs +0 -96
- package/dist/runtime.mjs +0 -96
- package/dist/runtime.mjs.map +0 -1
|
@@ -9,6 +9,7 @@ function createDialogManager(blessed, options) {
|
|
|
9
9
|
const { parent } = options;
|
|
10
10
|
let currentDialog = null;
|
|
11
11
|
let currentOverlay = null;
|
|
12
|
+
let currentCloseHandler = null;
|
|
12
13
|
/**
|
|
13
14
|
* Create a modal overlay to capture mouse events behind the dialog
|
|
14
15
|
*/
|
|
@@ -79,6 +80,12 @@ function createDialogManager(blessed, options) {
|
|
|
79
80
|
* Close current dialog
|
|
80
81
|
*/
|
|
81
82
|
function close() {
|
|
83
|
+
if (currentCloseHandler) {
|
|
84
|
+
parent.unkey("escape", currentCloseHandler);
|
|
85
|
+
parent.unkey("q", currentCloseHandler);
|
|
86
|
+
parent.unkey("enter", currentCloseHandler);
|
|
87
|
+
currentCloseHandler = null;
|
|
88
|
+
}
|
|
82
89
|
if (currentOverlay) {
|
|
83
90
|
currentOverlay.destroy();
|
|
84
91
|
currentOverlay = null;
|
|
@@ -148,7 +155,7 @@ function createDialogManager(blessed, options) {
|
|
|
148
155
|
},
|
|
149
156
|
showActionResult(action, success, message, data) {
|
|
150
157
|
close();
|
|
151
|
-
const dialog = createDialog(`Action: ${action}`, "60%", "50%");
|
|
158
|
+
const dialog = createDialog(`Action: ${action}`, "60%", "50%", { skipDefaultKeys: true });
|
|
152
159
|
currentDialog = dialog;
|
|
153
160
|
const lines = [];
|
|
154
161
|
if (success) lines.push(" {green-fg}✓ Action completed successfully{/green-fg}");
|
|
@@ -167,6 +174,14 @@ function createDialogManager(blessed, options) {
|
|
|
167
174
|
lines.push("");
|
|
168
175
|
lines.push(" {gray-fg}Press Esc, Enter, or Q to close{/gray-fg}");
|
|
169
176
|
dialog.setContent(lines.join("\n"));
|
|
177
|
+
currentCloseHandler = () => {
|
|
178
|
+
close();
|
|
179
|
+
};
|
|
180
|
+
parent.key([
|
|
181
|
+
"escape",
|
|
182
|
+
"q",
|
|
183
|
+
"enter"
|
|
184
|
+
], currentCloseHandler);
|
|
170
185
|
dialog.focus();
|
|
171
186
|
parent.render();
|
|
172
187
|
},
|
|
@@ -196,16 +211,33 @@ function createDialogManager(blessed, options) {
|
|
|
196
211
|
close();
|
|
197
212
|
const dialog = createDialog("Confirm", "50%", "25%", { skipDefaultKeys: true });
|
|
198
213
|
currentDialog = dialog;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
let selectedYes = false;
|
|
215
|
+
const renderContent = () => {
|
|
216
|
+
const yesStyle = selectedYes ? "{inverse} Yes {/inverse}" : " Yes ";
|
|
217
|
+
const noStyle = selectedYes ? " No " : "{inverse} No {/inverse}";
|
|
218
|
+
const lines = [
|
|
219
|
+
"",
|
|
220
|
+
` ${message}`,
|
|
221
|
+
"",
|
|
222
|
+
` [ ${yesStyle} ] [ ${noStyle} ]`,
|
|
223
|
+
"",
|
|
224
|
+
" {gray-fg}←/→: switch | Enter: confirm | Y/N: quick select | Esc: cancel{/gray-fg}"
|
|
225
|
+
];
|
|
226
|
+
dialog.setContent(lines.join("\n"));
|
|
227
|
+
parent.render();
|
|
228
|
+
};
|
|
229
|
+
renderContent();
|
|
230
|
+
dialog.key(["left", "right"], () => {
|
|
231
|
+
selectedYes = !selectedYes;
|
|
232
|
+
renderContent();
|
|
233
|
+
});
|
|
234
|
+
dialog.key(["enter"], async () => {
|
|
207
235
|
close();
|
|
208
|
-
onConfirm();
|
|
236
|
+
if (selectedYes) await onConfirm();
|
|
237
|
+
});
|
|
238
|
+
dialog.key(["y", "Y"], async () => {
|
|
239
|
+
close();
|
|
240
|
+
await onConfirm();
|
|
209
241
|
});
|
|
210
242
|
dialog.key([
|
|
211
243
|
"n",
|
|
@@ -217,6 +249,251 @@ function createDialogManager(blessed, options) {
|
|
|
217
249
|
dialog.focus();
|
|
218
250
|
parent.render();
|
|
219
251
|
},
|
|
252
|
+
showParamsInput(path, inputSchema, onSubmit) {
|
|
253
|
+
close();
|
|
254
|
+
const properties = inputSchema?.properties || {};
|
|
255
|
+
const required = inputSchema?.required || [];
|
|
256
|
+
const propNames = Object.keys(properties);
|
|
257
|
+
if (propNames.length === 0) {
|
|
258
|
+
this.showConfirm(`Execute action on ${path}?`, () => {
|
|
259
|
+
onSubmit({});
|
|
260
|
+
});
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const dialogHeight = Math.min(propNames.length * 3 + 8, 25);
|
|
264
|
+
const dialog = createDialog(`Exec: ${path}`, "70%", dialogHeight, { skipDefaultKeys: true });
|
|
265
|
+
currentDialog = dialog;
|
|
266
|
+
const form = blessed.form({
|
|
267
|
+
parent: dialog,
|
|
268
|
+
top: 1,
|
|
269
|
+
left: 1,
|
|
270
|
+
right: 1,
|
|
271
|
+
bottom: 3,
|
|
272
|
+
keys: true,
|
|
273
|
+
vi: false
|
|
274
|
+
});
|
|
275
|
+
const inputs = [];
|
|
276
|
+
let yPos = 0;
|
|
277
|
+
for (const propName of propNames) {
|
|
278
|
+
const prop = properties[propName];
|
|
279
|
+
const isRequired = required.includes(propName);
|
|
280
|
+
const typeStr = prop?.type || "string";
|
|
281
|
+
const description = prop?.description || "";
|
|
282
|
+
const labelText = `${propName}${isRequired ? "{red-fg}*{/red-fg}" : ""} {gray-fg}(${typeStr}){/gray-fg}${description ? ` - ${description}` : ""}`;
|
|
283
|
+
blessed.text({
|
|
284
|
+
parent: form,
|
|
285
|
+
top: yPos,
|
|
286
|
+
left: 0,
|
|
287
|
+
tags: true,
|
|
288
|
+
content: labelText,
|
|
289
|
+
style: { fg: Colors.fg.normal }
|
|
290
|
+
});
|
|
291
|
+
yPos += 1;
|
|
292
|
+
const inputBox = blessed.box({
|
|
293
|
+
parent: form,
|
|
294
|
+
top: yPos,
|
|
295
|
+
left: 0,
|
|
296
|
+
width: "100%-2",
|
|
297
|
+
height: 1,
|
|
298
|
+
tags: true,
|
|
299
|
+
style: {
|
|
300
|
+
fg: Colors.fg.normal,
|
|
301
|
+
bg: Colors.bg.input
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
inputs.push({
|
|
305
|
+
name: propName,
|
|
306
|
+
value: "",
|
|
307
|
+
cursor: 0,
|
|
308
|
+
box: inputBox
|
|
309
|
+
});
|
|
310
|
+
yPos += 2;
|
|
311
|
+
}
|
|
312
|
+
blessed.text({
|
|
313
|
+
parent: dialog,
|
|
314
|
+
bottom: 1,
|
|
315
|
+
left: 1,
|
|
316
|
+
tags: true,
|
|
317
|
+
content: "{gray-fg}Tab: next | ←→: move cursor | Enter: submit | Esc: cancel{/gray-fg}"
|
|
318
|
+
});
|
|
319
|
+
let focusIndex = 0;
|
|
320
|
+
const renderInput = (state, focused) => {
|
|
321
|
+
if (focused) {
|
|
322
|
+
const before = state.value.slice(0, state.cursor);
|
|
323
|
+
const cursorChar = state.value[state.cursor] || "█";
|
|
324
|
+
const after = state.value.slice(state.cursor + 1);
|
|
325
|
+
state.box.setContent(`${before}{white-bg}{black-fg}${cursorChar}{/black-fg}{/white-bg}${after}`);
|
|
326
|
+
state.box.style.bg = Colors.bg.inputFocus;
|
|
327
|
+
} else {
|
|
328
|
+
state.box.setContent(state.value || "");
|
|
329
|
+
state.box.style.bg = Colors.bg.input;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const focusInput = (index) => {
|
|
333
|
+
const prevState = inputs[focusIndex];
|
|
334
|
+
const nextState = inputs[index];
|
|
335
|
+
if (nextState) {
|
|
336
|
+
if (prevState) renderInput(prevState, false);
|
|
337
|
+
focusIndex = index;
|
|
338
|
+
renderInput(nextState, true);
|
|
339
|
+
parent.render();
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
const keypressHandler = (ch, key) => {
|
|
343
|
+
const state = inputs[focusIndex];
|
|
344
|
+
if (!state) return;
|
|
345
|
+
const keyName = key?.name || "";
|
|
346
|
+
if (keyName === "tab") {
|
|
347
|
+
if (key.shift) focusInput((focusIndex - 1 + inputs.length) % inputs.length);
|
|
348
|
+
else focusInput((focusIndex + 1) % inputs.length);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (keyName === "enter" || keyName === "return") {
|
|
352
|
+
submitForm();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (keyName === "escape") return;
|
|
356
|
+
if (keyName === "left") {
|
|
357
|
+
state.cursor = Math.max(0, state.cursor - 1);
|
|
358
|
+
renderInput(state, true);
|
|
359
|
+
parent.render();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (keyName === "right") {
|
|
363
|
+
state.cursor = Math.min(state.value.length, state.cursor + 1);
|
|
364
|
+
renderInput(state, true);
|
|
365
|
+
parent.render();
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (keyName === "home") {
|
|
369
|
+
state.cursor = 0;
|
|
370
|
+
renderInput(state, true);
|
|
371
|
+
parent.render();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (keyName === "end") {
|
|
375
|
+
state.cursor = state.value.length;
|
|
376
|
+
renderInput(state, true);
|
|
377
|
+
parent.render();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (keyName === "backspace") {
|
|
381
|
+
if (state.cursor > 0) {
|
|
382
|
+
state.value = state.value.slice(0, state.cursor - 1) + state.value.slice(state.cursor);
|
|
383
|
+
state.cursor--;
|
|
384
|
+
renderInput(state, true);
|
|
385
|
+
parent.render();
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (keyName === "delete") {
|
|
390
|
+
if (state.cursor < state.value.length) {
|
|
391
|
+
state.value = state.value.slice(0, state.cursor) + state.value.slice(state.cursor + 1);
|
|
392
|
+
renderInput(state, true);
|
|
393
|
+
parent.render();
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (ch && ch.length === 1 && !key.ctrl && !key.meta) {
|
|
398
|
+
state.value = state.value.slice(0, state.cursor) + ch + state.value.slice(state.cursor);
|
|
399
|
+
state.cursor++;
|
|
400
|
+
renderInput(state, true);
|
|
401
|
+
parent.render();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
const submitForm = () => {
|
|
405
|
+
parent.removeListener("keypress", keypressHandler);
|
|
406
|
+
const params = {};
|
|
407
|
+
for (const { name, value } of inputs) {
|
|
408
|
+
const trimmed = value.trim();
|
|
409
|
+
if (trimmed) {
|
|
410
|
+
const expectedType = properties[name]?.type || "string";
|
|
411
|
+
if (expectedType === "string") params[name] = trimmed;
|
|
412
|
+
else if (expectedType === "number" || expectedType === "integer") {
|
|
413
|
+
const num = Number(trimmed);
|
|
414
|
+
params[name] = Number.isNaN(num) ? trimmed : num;
|
|
415
|
+
} else if (expectedType === "boolean") params[name] = trimmed.toLowerCase() === "true";
|
|
416
|
+
else if (expectedType === "object" || expectedType === "array") try {
|
|
417
|
+
params[name] = JSON.parse(trimmed);
|
|
418
|
+
} catch {
|
|
419
|
+
params[name] = trimmed;
|
|
420
|
+
}
|
|
421
|
+
else try {
|
|
422
|
+
params[name] = JSON.parse(trimmed);
|
|
423
|
+
} catch {
|
|
424
|
+
params[name] = trimmed;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
close();
|
|
429
|
+
onSubmit(params);
|
|
430
|
+
};
|
|
431
|
+
const originalClose = close;
|
|
432
|
+
const closeWithCleanup = () => {
|
|
433
|
+
parent.removeListener("keypress", keypressHandler);
|
|
434
|
+
originalClose();
|
|
435
|
+
};
|
|
436
|
+
dialog.key(["escape"], () => {
|
|
437
|
+
closeWithCleanup();
|
|
438
|
+
});
|
|
439
|
+
dialog.focus();
|
|
440
|
+
parent.render();
|
|
441
|
+
setTimeout(() => {
|
|
442
|
+
parent.on("keypress", keypressHandler);
|
|
443
|
+
focusInput(0);
|
|
444
|
+
}, 50);
|
|
445
|
+
},
|
|
446
|
+
showActionPicker(nodePath, actions, onSelect) {
|
|
447
|
+
close();
|
|
448
|
+
const nodeName = nodePath.split("/").pop() || nodePath;
|
|
449
|
+
const dialogHeight = Math.min(actions.length + 6, 20);
|
|
450
|
+
const dialog = createDialog(`Actions: ${nodeName}`, "50%", dialogHeight, { skipDefaultKeys: true });
|
|
451
|
+
currentDialog = dialog;
|
|
452
|
+
let selectedIndex = 0;
|
|
453
|
+
const renderContent = () => {
|
|
454
|
+
const lines = [""];
|
|
455
|
+
for (let i = 0; i < actions.length; i++) {
|
|
456
|
+
const action = actions[i];
|
|
457
|
+
if (!action) continue;
|
|
458
|
+
const prefix = i === selectedIndex ? " {inverse}" : " ";
|
|
459
|
+
const suffix = i === selectedIndex ? "{/inverse}" : "";
|
|
460
|
+
lines.push(`${prefix}[${i + 1}] ${action.name}${suffix}`);
|
|
461
|
+
if (action.description) lines.push(` {gray-fg}${action.description}{/gray-fg}`);
|
|
462
|
+
}
|
|
463
|
+
lines.push("");
|
|
464
|
+
lines.push(" {gray-fg}↑/↓: select | Enter: execute | Esc: cancel{/gray-fg}");
|
|
465
|
+
dialog.setContent(lines.join("\n"));
|
|
466
|
+
parent.render();
|
|
467
|
+
};
|
|
468
|
+
renderContent();
|
|
469
|
+
dialog.key(["up", "k"], () => {
|
|
470
|
+
selectedIndex = (selectedIndex - 1 + actions.length) % actions.length;
|
|
471
|
+
renderContent();
|
|
472
|
+
});
|
|
473
|
+
dialog.key(["down", "j"], () => {
|
|
474
|
+
selectedIndex = (selectedIndex + 1) % actions.length;
|
|
475
|
+
renderContent();
|
|
476
|
+
});
|
|
477
|
+
for (let i = 1; i <= Math.min(9, actions.length); i++) dialog.key([String(i)], () => {
|
|
478
|
+
const action = actions[i - 1];
|
|
479
|
+
if (action) {
|
|
480
|
+
close();
|
|
481
|
+
onSelect(action);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
dialog.key(["enter"], () => {
|
|
485
|
+
const action = actions[selectedIndex];
|
|
486
|
+
if (action) {
|
|
487
|
+
close();
|
|
488
|
+
onSelect(action);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
dialog.key(["escape", "q"], () => {
|
|
492
|
+
close();
|
|
493
|
+
});
|
|
494
|
+
dialog.focus();
|
|
495
|
+
parent.render();
|
|
496
|
+
},
|
|
220
497
|
close,
|
|
221
498
|
isOpen() {
|
|
222
499
|
return currentDialog !== null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.mjs","names":["options"],"sources":["../../../src/explorer/components/dialog.ts"],"sourcesContent":["/**\n * AFS Explorer Dialog Component\n *\n * Modal dialogs for help, explain output, file view, etc.\n */\n\nimport type Blessed from \"blessed\";\nimport { formatKeyName, type KeyBindingRegistry } from \"../keybindings.js\";\nimport { Colors } from \"../theme.js\";\n\nexport interface DialogOptions {\n parent: Blessed.Widgets.Screen;\n}\n\n/**\n * Create dialog manager\n */\nexport function createDialogManager(blessed: typeof Blessed, options: DialogOptions) {\n const { parent } = options;\n let currentDialog: Blessed.Widgets.BoxElement | null = null;\n let currentOverlay: Blessed.Widgets.BoxElement | null = null;\n\n /**\n * Create a modal overlay to capture mouse events behind the dialog\n */\n function createOverlay() {\n const overlay = blessed.box({\n parent,\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n mouse: true,\n style: {\n transparent: true,\n },\n });\n\n // Capture and ignore all mouse events on the overlay\n overlay.on(\"wheeldown\", () => {});\n overlay.on(\"wheelup\", () => {});\n overlay.on(\"click\", () => {});\n\n return overlay;\n }\n\n /**\n * Create a basic dialog box\n */\n function createDialog(\n title: string,\n width: string | number,\n height: string | number,\n options?: { skipDefaultKeys?: boolean },\n ) {\n // Create overlay first to capture events behind dialog\n currentOverlay = createOverlay();\n\n const dialog = blessed.box({\n parent,\n top: \"center\",\n left: \"center\",\n width,\n height,\n tags: true,\n keys: true,\n vi: true,\n mouse: true,\n scrollable: true,\n alwaysScroll: true,\n scrollbar: {\n ch: \" \",\n track: {\n bg: Colors.bg.main,\n },\n style: {\n inverse: true,\n },\n },\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n border: {\n fg: Colors.fg.border,\n },\n },\n border: {\n type: \"line\",\n },\n label: ` ${title} `,\n shadow: true,\n });\n\n // Capture mouse events to prevent them from reaching elements behind the dialog\n dialog.on(\"wheeldown\", () => {\n dialog.scroll(1);\n parent.render();\n });\n dialog.on(\"wheelup\", () => {\n dialog.scroll(-1);\n parent.render();\n });\n\n // Close on escape or q (unless skipped)\n if (!options?.skipDefaultKeys) {\n dialog.key([\"escape\", \"q\", \"enter\"], () => {\n close();\n });\n }\n\n return dialog;\n }\n\n /**\n * Close current dialog\n */\n function close(): void {\n if (currentOverlay) {\n currentOverlay.destroy();\n currentOverlay = null;\n }\n if (currentDialog) {\n currentDialog.destroy();\n currentDialog = null;\n parent.render();\n }\n }\n\n return {\n /**\n * Show help dialog\n */\n showHelp(registry: KeyBindingRegistry): void {\n close();\n\n const dialog = createDialog(\"Help - AFS Explorer\", \"60%\", \"70%\");\n currentDialog = dialog;\n\n const lines: string[] = [\n \" AFS Explorer - Navigate your Agentic File System\",\n \"\",\n \" {bold}Commands:{/bold}\",\n \"\",\n ];\n\n // Get function bar bindings\n const bindings = registry.getFunctionBarBindings();\n for (const binding of bindings) {\n const key = formatKeyName(binding.key).padEnd(6);\n lines.push(` ${key} ${binding.description}`);\n }\n\n lines.push(\"\");\n lines.push(\" {bold}Navigation:{/bold}\");\n lines.push(\"\");\n lines.push(\" ↑/k Move up\");\n lines.push(\" ↓/j Move down\");\n lines.push(\" Enter/l Enter directory or view file\");\n lines.push(\" Bksp/h Go to parent directory\");\n lines.push(\" g/Home Go to first item\");\n lines.push(\" G/End Go to last item\");\n lines.push(\" ^U/PgUp Page up\");\n lines.push(\" ^D/PgDn Page down\");\n lines.push(\"\");\n lines.push(\" {bold}Other:{/bold}\");\n lines.push(\"\");\n lines.push(\" / Filter entries\");\n lines.push(\" ? Show this help\");\n lines.push(\"\");\n lines.push(\" {gray-fg}Press Esc, Enter, or Q to close{/gray-fg}\");\n\n dialog.setContent(lines.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show explain output dialog\n */\n showExplain(path: string, content: string): void {\n close();\n\n const dialog = createDialog(`Explain: ${path}`, \"80%\", \"80%\");\n currentDialog = dialog;\n\n dialog.setContent(` ${content.split(\"\\n\").join(\"\\n \")}`);\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show file view dialog\n */\n showFileView(path: string, content: string): void {\n close();\n\n const dialog = createDialog(`View: ${path}`, \"90%\", \"90%\");\n currentDialog = dialog;\n\n // Add line numbers\n // Escape curly braces in content to prevent blessed tag parsing issues\n const lines = content.split(\"\\n\");\n const numbered = lines.map((line, i) => {\n const num = (i + 1).toString().padStart(4);\n // Escape { and } in line content to prevent tag interpretation\n const escaped = line.replace(/\\{/g, \"\\\\{\").replace(/\\}/g, \"\\\\}\");\n return `{gray-fg}${num}{/gray-fg} │ ${escaped}`;\n });\n\n dialog.setContent(numbered.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show action result dialog\n */\n showActionResult(action: string, success: boolean, message?: string, data?: unknown): void {\n close();\n\n const dialog = createDialog(`Action: ${action}`, \"60%\", \"50%\");\n currentDialog = dialog;\n\n const lines: string[] = [];\n\n if (success) {\n lines.push(\" {green-fg}✓ Action completed successfully{/green-fg}\");\n } else {\n lines.push(\" {red-fg}✗ Action failed{/red-fg}\");\n }\n\n if (message) {\n lines.push(\"\");\n lines.push(` ${message}`);\n }\n\n if (data !== undefined) {\n lines.push(\"\");\n lines.push(\" {bold}Result:{/bold}\");\n lines.push(\"\");\n const formatted = JSON.stringify(data, null, 2);\n for (const line of formatted.split(\"\\n\")) {\n lines.push(` ${line}`);\n }\n }\n\n lines.push(\"\");\n lines.push(\" {gray-fg}Press Esc, Enter, or Q to close{/gray-fg}\");\n\n dialog.setContent(lines.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show error dialog\n */\n showError(title: string, error: string): void {\n close();\n\n const dialog = createDialog(title, \"50%\", \"30%\");\n currentDialog = dialog;\n\n const lines = [\n \" {red-fg}Error:{/red-fg}\",\n \"\",\n ` ${error}`,\n \"\",\n \" {gray-fg}Press Esc to close{/gray-fg}\",\n ];\n\n dialog.setContent(lines.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show loading indicator\n */\n showLoading(message: string): void {\n close();\n\n const dialog = createDialog(\"Loading\", \"40%\", \"20%\");\n currentDialog = dialog;\n\n dialog.setContent(`\\n ${message}...`);\n parent.render();\n },\n\n /**\n * Show confirmation dialog\n */\n showConfirm(message: string, onConfirm: () => void): void {\n close();\n\n const dialog = createDialog(\"Confirm\", \"50%\", \"25%\", { skipDefaultKeys: true });\n currentDialog = dialog;\n\n const lines = [\n \"\",\n ` ${message}`,\n \"\",\n \" {gray-fg}Press Y to confirm, N or Esc to cancel{/gray-fg}\",\n ];\n\n dialog.setContent(lines.join(\"\\n\"));\n\n // Custom key handling for confirmation\n dialog.key([\"y\", \"Y\"], () => {\n close();\n onConfirm();\n });\n dialog.key([\"n\", \"N\", \"escape\"], () => {\n close();\n });\n\n dialog.focus();\n parent.render();\n },\n\n /**\n * Close current dialog\n */\n close,\n\n /**\n * Check if a dialog is open\n */\n isOpen(): boolean {\n return currentDialog !== null;\n },\n\n /**\n * Destroy dialog manager\n */\n destroy(): void {\n close();\n },\n };\n}\n\nexport type DialogManager = ReturnType<typeof createDialogManager>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dialog.mjs","names":["options"],"sources":["../../../src/explorer/components/dialog.ts"],"sourcesContent":["/**\n * AFS Explorer Dialog Component\n *\n * Modal dialogs for help, explain output, file view, etc.\n */\n\nimport type Blessed from \"blessed\";\nimport { formatKeyName, type KeyBindingRegistry } from \"../keybindings.js\";\nimport { Colors } from \"../theme.js\";\nimport type { ActionItem } from \"../types.js\";\n\nexport interface DialogOptions {\n parent: Blessed.Widgets.Screen;\n}\n\n/**\n * Create dialog manager\n */\nexport function createDialogManager(blessed: typeof Blessed, options: DialogOptions) {\n const { parent } = options;\n let currentDialog: Blessed.Widgets.BoxElement | null = null;\n let currentOverlay: Blessed.Widgets.BoxElement | null = null;\n let currentCloseHandler: (() => void) | null = null;\n\n /**\n * Create a modal overlay to capture mouse events behind the dialog\n */\n function createOverlay() {\n const overlay = blessed.box({\n parent,\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n mouse: true,\n style: {\n transparent: true,\n },\n });\n\n // Capture and ignore all mouse events on the overlay\n overlay.on(\"wheeldown\", () => {});\n overlay.on(\"wheelup\", () => {});\n overlay.on(\"click\", () => {});\n\n return overlay;\n }\n\n /**\n * Create a basic dialog box\n */\n function createDialog(\n title: string,\n width: string | number,\n height: string | number,\n options?: { skipDefaultKeys?: boolean },\n ) {\n // Create overlay first to capture events behind dialog\n currentOverlay = createOverlay();\n\n const dialog = blessed.box({\n parent,\n top: \"center\",\n left: \"center\",\n width,\n height,\n tags: true,\n keys: true,\n vi: true,\n mouse: true,\n scrollable: true,\n alwaysScroll: true,\n scrollbar: {\n ch: \" \",\n track: {\n bg: Colors.bg.main,\n },\n style: {\n inverse: true,\n },\n },\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n border: {\n fg: Colors.fg.border,\n },\n },\n border: {\n type: \"line\",\n },\n label: ` ${title} `,\n shadow: true,\n });\n\n // Capture mouse events to prevent them from reaching elements behind the dialog\n dialog.on(\"wheeldown\", () => {\n dialog.scroll(1);\n parent.render();\n });\n dialog.on(\"wheelup\", () => {\n dialog.scroll(-1);\n parent.render();\n });\n\n // Close on escape or q (unless skipped)\n if (!options?.skipDefaultKeys) {\n dialog.key([\"escape\", \"q\", \"enter\"], () => {\n close();\n });\n }\n\n return dialog;\n }\n\n /**\n * Close current dialog\n */\n function close(): void {\n // Clean up screen-level key handler if any\n if (currentCloseHandler) {\n parent.unkey(\"escape\", currentCloseHandler);\n parent.unkey(\"q\", currentCloseHandler);\n parent.unkey(\"enter\", currentCloseHandler);\n currentCloseHandler = null;\n }\n if (currentOverlay) {\n currentOverlay.destroy();\n currentOverlay = null;\n }\n if (currentDialog) {\n currentDialog.destroy();\n currentDialog = null;\n parent.render();\n }\n }\n\n return {\n /**\n * Show help dialog\n */\n showHelp(registry: KeyBindingRegistry): void {\n close();\n\n const dialog = createDialog(\"Help - AFS Explorer\", \"60%\", \"70%\");\n currentDialog = dialog;\n\n const lines: string[] = [\n \" AFS Explorer - Navigate your Agentic File System\",\n \"\",\n \" {bold}Commands:{/bold}\",\n \"\",\n ];\n\n // Get function bar bindings\n const bindings = registry.getFunctionBarBindings();\n for (const binding of bindings) {\n const key = formatKeyName(binding.key).padEnd(6);\n lines.push(` ${key} ${binding.description}`);\n }\n\n lines.push(\"\");\n lines.push(\" {bold}Navigation:{/bold}\");\n lines.push(\"\");\n lines.push(\" ↑/k Move up\");\n lines.push(\" ↓/j Move down\");\n lines.push(\" Enter/l Enter directory or view file\");\n lines.push(\" Bksp/h Go to parent directory\");\n lines.push(\" g/Home Go to first item\");\n lines.push(\" G/End Go to last item\");\n lines.push(\" ^U/PgUp Page up\");\n lines.push(\" ^D/PgDn Page down\");\n lines.push(\"\");\n lines.push(\" {bold}Other:{/bold}\");\n lines.push(\"\");\n lines.push(\" / Filter entries\");\n lines.push(\" ? Show this help\");\n lines.push(\"\");\n lines.push(\" {gray-fg}Press Esc, Enter, or Q to close{/gray-fg}\");\n\n dialog.setContent(lines.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show explain output dialog\n */\n showExplain(path: string, content: string): void {\n close();\n\n const dialog = createDialog(`Explain: ${path}`, \"80%\", \"80%\");\n currentDialog = dialog;\n\n dialog.setContent(` ${content.split(\"\\n\").join(\"\\n \")}`);\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show file view dialog\n */\n showFileView(path: string, content: string): void {\n close();\n\n const dialog = createDialog(`View: ${path}`, \"90%\", \"90%\");\n currentDialog = dialog;\n\n // Add line numbers\n // Escape curly braces in content to prevent blessed tag parsing issues\n const lines = content.split(\"\\n\");\n const numbered = lines.map((line, i) => {\n const num = (i + 1).toString().padStart(4);\n // Escape { and } in line content to prevent tag interpretation\n const escaped = line.replace(/\\{/g, \"\\\\{\").replace(/\\}/g, \"\\\\}\");\n return `{gray-fg}${num}{/gray-fg} │ ${escaped}`;\n });\n\n dialog.setContent(numbered.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show action result dialog\n */\n showActionResult(action: string, success: boolean, message?: string, data?: unknown): void {\n close();\n\n // Use skipDefaultKeys and add our own to ensure they work after async callback\n const dialog = createDialog(`Action: ${action}`, \"60%\", \"50%\", { skipDefaultKeys: true });\n currentDialog = dialog;\n\n const lines: string[] = [];\n\n if (success) {\n lines.push(\" {green-fg}✓ Action completed successfully{/green-fg}\");\n } else {\n lines.push(\" {red-fg}✗ Action failed{/red-fg}\");\n }\n\n if (message) {\n lines.push(\"\");\n lines.push(` ${message}`);\n }\n\n if (data !== undefined) {\n lines.push(\"\");\n lines.push(\" {bold}Result:{/bold}\");\n lines.push(\"\");\n const formatted = JSON.stringify(data, null, 2);\n for (const line of formatted.split(\"\\n\")) {\n lines.push(` ${line}`);\n }\n }\n\n lines.push(\"\");\n lines.push(\" {gray-fg}Press Esc, Enter, or Q to close{/gray-fg}\");\n\n dialog.setContent(lines.join(\"\\n\"));\n\n // Use screen-level key handler to ensure it works after async operations\n currentCloseHandler = () => {\n close();\n };\n parent.key([\"escape\", \"q\", \"enter\"], currentCloseHandler);\n\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show error dialog\n */\n showError(title: string, error: string): void {\n close();\n\n const dialog = createDialog(title, \"50%\", \"30%\");\n currentDialog = dialog;\n\n const lines = [\n \" {red-fg}Error:{/red-fg}\",\n \"\",\n ` ${error}`,\n \"\",\n \" {gray-fg}Press Esc to close{/gray-fg}\",\n ];\n\n dialog.setContent(lines.join(\"\\n\"));\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show loading indicator\n */\n showLoading(message: string): void {\n close();\n\n const dialog = createDialog(\"Loading\", \"40%\", \"20%\");\n currentDialog = dialog;\n\n dialog.setContent(`\\n ${message}...`);\n parent.render();\n },\n\n /**\n * Show confirmation dialog\n */\n showConfirm(message: string, onConfirm: () => void | Promise<void>): void {\n close();\n\n const dialog = createDialog(\"Confirm\", \"50%\", \"25%\", { skipDefaultKeys: true });\n currentDialog = dialog;\n\n // Track which button is selected (false = No, true = Yes)\n let selectedYes = false;\n\n const renderContent = () => {\n const yesStyle = selectedYes ? \"{inverse} Yes {/inverse}\" : \" Yes \";\n const noStyle = selectedYes ? \" No \" : \"{inverse} No {/inverse}\";\n\n const lines = [\n \"\",\n ` ${message}`,\n \"\",\n ` [ ${yesStyle} ] [ ${noStyle} ]`,\n \"\",\n \" {gray-fg}←/→: switch | Enter: confirm | Y/N: quick select | Esc: cancel{/gray-fg}\",\n ];\n\n dialog.setContent(lines.join(\"\\n\"));\n parent.render();\n };\n\n // Initial render\n renderContent();\n\n // Arrow keys to switch selection\n dialog.key([\"left\", \"right\"], () => {\n selectedYes = !selectedYes;\n renderContent();\n });\n\n // Enter to execute selected action\n dialog.key([\"enter\"], async () => {\n close();\n if (selectedYes) {\n await onConfirm();\n }\n });\n\n // Quick select keys\n dialog.key([\"y\", \"Y\"], async () => {\n close();\n await onConfirm();\n });\n dialog.key([\"n\", \"N\", \"escape\"], () => {\n close();\n });\n\n dialog.focus();\n parent.render();\n },\n\n /**\n * Show params input dialog for exec\n *\n * @param path - Path being executed\n * @param inputSchema - JSON Schema for input parameters\n * @param onSubmit - Callback when params are submitted\n */\n showParamsInput(\n path: string,\n inputSchema: Record<string, unknown> | undefined,\n onSubmit: (params: Record<string, unknown>) => void,\n ): void {\n close();\n\n // Parse schema to get required and optional properties\n const properties = (inputSchema?.properties as Record<string, unknown>) || {};\n const required = (inputSchema?.required as string[]) || [];\n const propNames = Object.keys(properties);\n\n // If no properties, show confirmation dialog instead of executing directly\n if (propNames.length === 0) {\n this.showConfirm(`Execute action on ${path}?`, () => {\n onSubmit({});\n });\n return;\n }\n\n // Calculate dialog height based on number of fields (2 lines per field + header/footer)\n const dialogHeight = Math.min(propNames.length * 3 + 8, 25);\n const dialog = createDialog(`Exec: ${path}`, \"70%\", dialogHeight, { skipDefaultKeys: true });\n currentDialog = dialog;\n\n // Create form container (vi: false to allow normal cursor movement)\n const form = blessed.form({\n parent: dialog,\n top: 1,\n left: 1,\n right: 1,\n bottom: 3,\n keys: true,\n vi: false,\n });\n\n // Custom input state with cursor support\n interface InputState {\n name: string;\n value: string;\n cursor: number;\n box: Blessed.Widgets.BoxElement;\n }\n const inputs: InputState[] = [];\n let yPos = 0;\n\n for (const propName of propNames) {\n const prop = properties[propName] as Record<string, unknown>;\n const isRequired = required.includes(propName);\n const typeStr = (prop?.type as string) || \"string\";\n const description = (prop?.description as string) || \"\";\n\n // Label with type info\n const reqMark = isRequired ? \"{red-fg}*{/red-fg}\" : \"\";\n const labelText = `${propName}${reqMark} {gray-fg}(${typeStr}){/gray-fg}${description ? ` - ${description}` : \"\"}`;\n\n blessed.text({\n parent: form,\n top: yPos,\n left: 0,\n tags: true,\n content: labelText,\n style: { fg: Colors.fg.normal },\n });\n\n yPos += 1;\n\n // Custom box for input with cursor display\n const inputBox = blessed.box({\n parent: form,\n top: yPos,\n left: 0,\n width: \"100%-2\",\n height: 1,\n tags: true,\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.input,\n },\n });\n\n inputs.push({ name: propName, value: \"\", cursor: 0, box: inputBox });\n yPos += 2;\n }\n\n // Instructions at bottom\n blessed.text({\n parent: dialog,\n bottom: 1,\n left: 1,\n tags: true,\n content: \"{gray-fg}Tab: next | ←→: move cursor | Enter: submit | Esc: cancel{/gray-fg}\",\n });\n\n // Track current focused input index\n let focusIndex = 0;\n\n // Render input with cursor\n const renderInput = (state: InputState, focused: boolean) => {\n if (focused) {\n const before = state.value.slice(0, state.cursor);\n const cursorChar = state.value[state.cursor] || \"█\";\n const after = state.value.slice(state.cursor + 1);\n // Use white-bg black-fg for cursor visibility\n state.box.setContent(\n `${before}{white-bg}{black-fg}${cursorChar}{/black-fg}{/white-bg}${after}`,\n );\n state.box.style.bg = Colors.bg.inputFocus;\n } else {\n state.box.setContent(state.value || \"\");\n state.box.style.bg = Colors.bg.input;\n }\n };\n\n const focusInput = (index: number) => {\n const prevState = inputs[focusIndex];\n const nextState = inputs[index];\n if (nextState) {\n if (prevState) renderInput(prevState, false);\n focusIndex = index;\n renderInput(nextState, true);\n parent.render();\n }\n };\n\n // Screen-level keypress handler for full input control\n const keypressHandler = (\n ch: string | undefined,\n key: Blessed.Widgets.Events.IKeyEventArg,\n ) => {\n const state = inputs[focusIndex];\n if (!state) return;\n\n const keyName = key?.name || \"\";\n\n // Tab navigation\n if (keyName === \"tab\") {\n if (key.shift) {\n focusInput((focusIndex - 1 + inputs.length) % inputs.length);\n } else {\n focusInput((focusIndex + 1) % inputs.length);\n }\n return;\n }\n\n // Submit\n if (keyName === \"enter\" || keyName === \"return\") {\n submitForm();\n return;\n }\n\n // Cancel - handled by dialog.key below\n if (keyName === \"escape\") {\n return;\n }\n\n // Cursor movement\n if (keyName === \"left\") {\n state.cursor = Math.max(0, state.cursor - 1);\n renderInput(state, true);\n parent.render();\n return;\n }\n if (keyName === \"right\") {\n state.cursor = Math.min(state.value.length, state.cursor + 1);\n renderInput(state, true);\n parent.render();\n return;\n }\n if (keyName === \"home\") {\n state.cursor = 0;\n renderInput(state, true);\n parent.render();\n return;\n }\n if (keyName === \"end\") {\n state.cursor = state.value.length;\n renderInput(state, true);\n parent.render();\n return;\n }\n\n // Backspace\n if (keyName === \"backspace\") {\n if (state.cursor > 0) {\n state.value = state.value.slice(0, state.cursor - 1) + state.value.slice(state.cursor);\n state.cursor--;\n renderInput(state, true);\n parent.render();\n }\n return;\n }\n\n // Delete\n if (keyName === \"delete\") {\n if (state.cursor < state.value.length) {\n state.value = state.value.slice(0, state.cursor) + state.value.slice(state.cursor + 1);\n renderInput(state, true);\n parent.render();\n }\n return;\n }\n\n // Character input (printable characters)\n if (ch && ch.length === 1 && !key.ctrl && !key.meta) {\n state.value = state.value.slice(0, state.cursor) + ch + state.value.slice(state.cursor);\n state.cursor++;\n renderInput(state, true);\n parent.render();\n }\n };\n\n const submitForm = () => {\n // Remove keypress handler\n parent.removeListener(\"keypress\", keypressHandler);\n\n const params: Record<string, unknown> = {};\n for (const { name, value } of inputs) {\n const trimmed = value.trim();\n if (trimmed) {\n // Get the expected type from inputSchema\n const prop = properties[name] as Record<string, unknown> | undefined;\n const expectedType = (prop?.type as string) || \"string\";\n\n // Parse value based on expected type\n if (expectedType === \"string\") {\n // Always keep as string for string type\n params[name] = trimmed;\n } else if (expectedType === \"number\" || expectedType === \"integer\") {\n const num = Number(trimmed);\n params[name] = Number.isNaN(num) ? trimmed : num;\n } else if (expectedType === \"boolean\") {\n params[name] = trimmed.toLowerCase() === \"true\";\n } else if (expectedType === \"object\" || expectedType === \"array\") {\n try {\n params[name] = JSON.parse(trimmed);\n } catch {\n params[name] = trimmed;\n }\n } else {\n // Default: try JSON parse, fallback to string\n try {\n params[name] = JSON.parse(trimmed);\n } catch {\n params[name] = trimmed;\n }\n }\n }\n }\n\n close();\n onSubmit(params);\n };\n\n // Override close to clean up handler\n const originalClose = close;\n const closeWithCleanup = () => {\n parent.removeListener(\"keypress\", keypressHandler);\n originalClose();\n };\n\n // Update dialog escape handler to use cleanup\n dialog.key([\"escape\"], () => {\n closeWithCleanup();\n });\n\n // Focus dialog and initialize first input\n // Use setTimeout to avoid capturing the Enter key that triggered this dialog\n dialog.focus();\n parent.render();\n setTimeout(() => {\n parent.on(\"keypress\", keypressHandler);\n focusInput(0);\n }, 50);\n },\n\n /**\n * Show action picker dialog\n *\n * @param nodePath - Path of the node being acted upon\n * @param actions - List of available actions\n * @param onSelect - Callback when an action is selected\n */\n showActionPicker(\n nodePath: string,\n actions: ActionItem[],\n onSelect: (action: ActionItem) => void,\n ): void {\n close();\n\n const nodeName = nodePath.split(\"/\").pop() || nodePath;\n const dialogHeight = Math.min(actions.length + 6, 20);\n const dialog = createDialog(`Actions: ${nodeName}`, \"50%\", dialogHeight, {\n skipDefaultKeys: true,\n });\n currentDialog = dialog;\n\n // Track selected index\n let selectedIndex = 0;\n\n const renderContent = () => {\n const lines: string[] = [\"\"];\n\n for (let i = 0; i < actions.length; i++) {\n const action = actions[i];\n if (!action) continue;\n\n const prefix = i === selectedIndex ? \" {inverse}\" : \" \";\n const suffix = i === selectedIndex ? \"{/inverse}\" : \"\";\n lines.push(`${prefix}[${i + 1}] ${action.name}${suffix}`);\n\n // Show description if present\n if (action.description) {\n lines.push(` {gray-fg}${action.description}{/gray-fg}`);\n }\n }\n\n lines.push(\"\");\n lines.push(\" {gray-fg}↑/↓: select | Enter: execute | Esc: cancel{/gray-fg}\");\n\n dialog.setContent(lines.join(\"\\n\"));\n parent.render();\n };\n\n // Initial render\n renderContent();\n\n // Navigation\n dialog.key([\"up\", \"k\"], () => {\n selectedIndex = (selectedIndex - 1 + actions.length) % actions.length;\n renderContent();\n });\n\n dialog.key([\"down\", \"j\"], () => {\n selectedIndex = (selectedIndex + 1) % actions.length;\n renderContent();\n });\n\n // Number keys for quick selection (1-9)\n for (let i = 1; i <= Math.min(9, actions.length); i++) {\n dialog.key([String(i)], () => {\n const action = actions[i - 1];\n if (action) {\n close();\n onSelect(action);\n }\n });\n }\n\n // Enter to select\n dialog.key([\"enter\"], () => {\n const action = actions[selectedIndex];\n if (action) {\n close();\n onSelect(action);\n }\n });\n\n // Escape to cancel\n dialog.key([\"escape\", \"q\"], () => {\n close();\n });\n\n dialog.focus();\n parent.render();\n },\n\n /**\n * Close current dialog\n */\n close,\n\n /**\n * Check if a dialog is open\n */\n isOpen(): boolean {\n return currentDialog !== null;\n },\n\n /**\n * Destroy dialog manager\n */\n destroy(): void {\n close();\n },\n };\n}\n\nexport type DialogManager = ReturnType<typeof createDialogManager>;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-list.mjs","names":[],"sources":["../../../src/explorer/components/file-list.ts"],"sourcesContent":["/**\n * AFS Explorer File List Component\n *\n * Main file listing component using blessed list widget.\n */\n\nimport type Blessed from \"blessed\";\nimport { navigation } from \"../actions.js\";\nimport type { ExplorerStore } from \"../state.js\";\nimport { Colors, formatSize, Icons, Symbols } from \"../theme.js\";\nimport type { ExplorerEntry, ExplorerState } from \"../types.js\";\n\nexport interface FileListOptions {\n parent: Blessed.Widgets.Node;\n store: ExplorerStore;\n width: string | number;\n height: string | number;\n top?: string | number;\n left?: string | number;\n}\n\n/**\n * Format an entry for display in the list\n */\nexport function formatEntry(entry: ExplorerEntry, maxNameWidth: number): string {\n const icon = Icons[entry.type] || Icons.file;\n const name = entry.name.padEnd(maxNameWidth);\n const size = entry.size !== undefined ? formatSize(entry.size).padStart(8) : \" \";\n const modified = entry.modified ? formatDate(entry.modified) : \" \";\n\n return `${icon} ${name} ${size} ${modified}`;\n}\n\n/**\n * Format date for display\n */\nfunction formatDate(date: Date): string {\n const now = new Date();\n const isThisYear = date.getFullYear() === now.getFullYear();\n\n const month = date.toLocaleString(\"en\", { month: \"short\" });\n const day = date.getDate().toString().padStart(2, \" \");\n\n if (isThisYear) {\n const hours = date.getHours().toString().padStart(2, \"0\");\n const mins = date.getMinutes().toString().padStart(2, \"0\");\n return `${month} ${day} ${hours}:${mins}`;\n }\n return `${month} ${day} ${date.getFullYear()}`;\n}\n\n/**\n * Create file list component\n */\nexport function createFileList(blessed: typeof Blessed, options: FileListOptions) {\n const { parent, store, width, height, top = 0, left = 0 } = options;\n\n // Create list widget\n const list = blessed.list({\n parent,\n top,\n left,\n width,\n height,\n tags: true,\n keys: false,\n mouse: true,\n scrollable: true,\n alwaysScroll: true,\n scrollbar: {\n ch: Symbols.scrollbar,\n track: {\n bg: Colors.bg.main,\n },\n style: {\n inverse: true,\n },\n },\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n selected: {\n fg: Colors.fg.selected,\n bg: Colors.bg.selected,\n },\n item: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n },\n border: {\n type: \"line\",\n },\n });\n\n // Calculate max name width based on entries\n function getMaxNameWidth(entries: ExplorerEntry[]): number {\n if (entries.length === 0) return 20;\n const maxLen = Math.max(...entries.map((e) => e.name.length));\n return Math.min(Math.max(maxLen, 10), 40);\n }\n\n // Update list content from state\n function updateContent(state: ExplorerState): void {\n const maxNameWidth = getMaxNameWidth(state.entries);\n const items = state.entries.map((entry) => {\n const formatted = formatEntry(entry, maxNameWidth);\n // Apply color based on type\n const color = getEntryColor(entry.type);\n return `{${color}-fg}${formatted}{/${color}-fg}`;\n });\n\n list.setItems(items);\n list.select(state.selectedIndex);\n list.scrollTo(state.selectedIndex);\n }\n\n // Get color name for entry type\n function getEntryColor(type: ExplorerEntry[\"type\"]): string {\n switch (type) {\n case \"directory\":\n return Colors.fg.directory;\n case \"exec\":\n return Colors.fg.exec;\n case \"link\":\n return Colors.fg.link;\n case \"up\":\n return Colors.fg.up;\n default:\n return Colors.fg.file;\n }\n }\n\n // Subscribe to state changes\n store.subscribe((state) => {\n updateContent(state);\n (list.screen as Blessed.Widgets.Screen)?.render();\n });\n\n // Initial render\n updateContent(store.getState());\n\n // Return component interface\n return {\n element: list,\n\n /**\n * Focus the list\n */\n focus(): void {\n list.focus();\n },\n\n /**\n * Get visible height (for page calculations)\n */\n getVisibleHeight(): number {\n // Account for border\n const h = typeof list.height === \"number\" ? list.height : 20;\n return Math.max(1, h - 2);\n },\n\n /**\n * Get selected entry\n */\n getSelected(): ExplorerEntry | undefined {\n return navigation.getSelected(store.getState());\n },\n\n /**\n * Destroy the component\n */\n destroy(): void {\n list.destroy();\n },\n };\n}\n\nexport type FileList = ReturnType<typeof createFileList>;\n"],"mappings":";;;;;;;AAwBA,SAAgB,YAAY,OAAsB,cAA8B;
|
|
1
|
+
{"version":3,"file":"file-list.mjs","names":[],"sources":["../../../src/explorer/components/file-list.ts"],"sourcesContent":["/**\n * AFS Explorer File List Component\n *\n * Main file listing component using blessed list widget.\n */\n\nimport type Blessed from \"blessed\";\nimport { navigation } from \"../actions.js\";\nimport type { ExplorerStore } from \"../state.js\";\nimport { Colors, formatSize, Icons, Symbols } from \"../theme.js\";\nimport type { ExplorerEntry, ExplorerState } from \"../types.js\";\n\nexport interface FileListOptions {\n parent: Blessed.Widgets.Node;\n store: ExplorerStore;\n width: string | number;\n height: string | number;\n top?: string | number;\n left?: string | number;\n}\n\n/**\n * Format an entry for display in the list\n */\nexport function formatEntry(entry: ExplorerEntry, maxNameWidth: number): string {\n // Use default ASCII icons based on entry type (emoji icons not displayed due to terminal rendering issues)\n const icon = Icons[entry.type] || Icons.file;\n const name = entry.name.padEnd(maxNameWidth);\n const size = entry.size !== undefined ? formatSize(entry.size).padStart(8) : \" \";\n const modified = entry.modified ? formatDate(entry.modified) : \" \";\n\n return `${icon} ${name} ${size} ${modified}`;\n}\n\n/**\n * Format date for display\n */\nfunction formatDate(date: Date): string {\n const now = new Date();\n const isThisYear = date.getFullYear() === now.getFullYear();\n\n const month = date.toLocaleString(\"en\", { month: \"short\" });\n const day = date.getDate().toString().padStart(2, \" \");\n\n if (isThisYear) {\n const hours = date.getHours().toString().padStart(2, \"0\");\n const mins = date.getMinutes().toString().padStart(2, \"0\");\n return `${month} ${day} ${hours}:${mins}`;\n }\n return `${month} ${day} ${date.getFullYear()}`;\n}\n\n/**\n * Create file list component\n */\nexport function createFileList(blessed: typeof Blessed, options: FileListOptions) {\n const { parent, store, width, height, top = 0, left = 0 } = options;\n\n // Create list widget\n const list = blessed.list({\n parent,\n top,\n left,\n width,\n height,\n tags: true,\n keys: false,\n mouse: true,\n scrollable: true,\n alwaysScroll: true,\n scrollbar: {\n ch: Symbols.scrollbar,\n track: {\n bg: Colors.bg.main,\n },\n style: {\n inverse: true,\n },\n },\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n selected: {\n fg: Colors.fg.selected,\n bg: Colors.bg.selected,\n },\n item: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n },\n border: {\n type: \"line\",\n },\n });\n\n // Calculate max name width based on entries\n function getMaxNameWidth(entries: ExplorerEntry[]): number {\n if (entries.length === 0) return 20;\n const maxLen = Math.max(...entries.map((e) => e.name.length));\n return Math.min(Math.max(maxLen, 10), 40);\n }\n\n // Update list content from state\n function updateContent(state: ExplorerState): void {\n const maxNameWidth = getMaxNameWidth(state.entries);\n const items = state.entries.map((entry) => {\n const formatted = formatEntry(entry, maxNameWidth);\n // Apply color based on type\n const color = getEntryColor(entry.type);\n return `{${color}-fg}${formatted}{/${color}-fg}`;\n });\n\n list.setItems(items);\n list.select(state.selectedIndex);\n list.scrollTo(state.selectedIndex);\n }\n\n // Get color name for entry type\n function getEntryColor(type: ExplorerEntry[\"type\"]): string {\n switch (type) {\n case \"directory\":\n return Colors.fg.directory;\n case \"exec\":\n return Colors.fg.exec;\n case \"link\":\n return Colors.fg.link;\n case \"up\":\n return Colors.fg.up;\n default:\n return Colors.fg.file;\n }\n }\n\n // Subscribe to state changes\n store.subscribe((state) => {\n updateContent(state);\n (list.screen as Blessed.Widgets.Screen)?.render();\n });\n\n // Initial render\n updateContent(store.getState());\n\n // Return component interface\n return {\n element: list,\n\n /**\n * Focus the list\n */\n focus(): void {\n list.focus();\n },\n\n /**\n * Get visible height (for page calculations)\n */\n getVisibleHeight(): number {\n // Account for border\n const h = typeof list.height === \"number\" ? list.height : 20;\n return Math.max(1, h - 2);\n },\n\n /**\n * Get selected entry\n */\n getSelected(): ExplorerEntry | undefined {\n return navigation.getSelected(store.getState());\n },\n\n /**\n * Destroy the component\n */\n destroy(): void {\n list.destroy();\n },\n };\n}\n\nexport type FileList = ReturnType<typeof createFileList>;\n"],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ function formatMetadata(entry, metadata) {
|
|
|
10
10
|
const prefix = icon ? `${icon} ` : "";
|
|
11
11
|
lines.push(`${prefix}${entry.name}`);
|
|
12
12
|
lines.push("");
|
|
13
|
-
lines.push(`
|
|
13
|
+
lines.push(`Path: ${entry.path}`);
|
|
14
14
|
if (entry.size !== void 0 || metadata?.size !== void 0) {
|
|
15
15
|
const size = metadata?.size ?? entry.size;
|
|
16
16
|
lines.push(`Size: ${require_theme.formatSize(size)}`);
|
|
@@ -29,20 +29,131 @@ function formatMetadata(entry, metadata) {
|
|
|
29
29
|
const display = hash.length > 20 ? `${hash.slice(0, 20)}...` : hash;
|
|
30
30
|
lines.push(`Hash: ${display}`);
|
|
31
31
|
}
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if (metadata?.mountPath) lines.push(`Mount: ${metadata.mountPath}`);
|
|
33
|
+
if (metadata?.uri) lines.push(`URI: ${metadata.uri}`);
|
|
34
|
+
const kinds = entry.kinds && entry.kinds.length > 0 ? entry.kinds : metadata?.extra?.kinds;
|
|
35
|
+
const kind = entry.kind ?? metadata?.extra?.kind;
|
|
36
|
+
if (kinds && kinds.length > 0) lines.push(`Kinds: ${kinds.join(" → ")}`);
|
|
37
|
+
else if (kind) lines.push(`Kind: ${kind}`);
|
|
38
|
+
if (metadata?.extra && Object.keys(metadata.extra).length > 0) {
|
|
39
|
+
const builtInFields = new Set([
|
|
40
|
+
"size",
|
|
41
|
+
"mimeType",
|
|
42
|
+
"childrenCount",
|
|
43
|
+
"hash",
|
|
44
|
+
"provider",
|
|
45
|
+
"mountPath",
|
|
46
|
+
"uri",
|
|
47
|
+
"permissions",
|
|
48
|
+
"kind",
|
|
49
|
+
"kinds"
|
|
50
|
+
]);
|
|
51
|
+
for (const [key, value] of Object.entries(metadata.extra)) {
|
|
52
|
+
if (builtInFields.has(key) || value === void 0) continue;
|
|
53
|
+
if (key === "inputSchema" && typeof value === "object" && value !== null) {
|
|
54
|
+
const schema = value;
|
|
55
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
56
|
+
lines.push("");
|
|
57
|
+
lines.push("InputSchema:");
|
|
58
|
+
const props = schema.properties;
|
|
59
|
+
for (const [propName, propSchema] of Object.entries(props)) {
|
|
60
|
+
const ps = propSchema;
|
|
61
|
+
const type = ps.type || "any";
|
|
62
|
+
const desc = ps.description ? ` - ${ps.description}` : "";
|
|
63
|
+
const req = Array.isArray(schema.required) && schema.required.includes(propName) ? "*" : "";
|
|
64
|
+
lines.push(` • ${propName}${req}: ${type}${desc}`);
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const displayKey = key.charAt(0).toUpperCase() + key.slice(1);
|
|
70
|
+
const displayValue = formatValue(value);
|
|
71
|
+
if (displayValue.includes("\n")) {
|
|
72
|
+
lines.push("");
|
|
73
|
+
lines.push(`${displayKey}:`);
|
|
74
|
+
for (const line of displayValue.split("\n")) lines.push(` ${line}`);
|
|
75
|
+
} else lines.push(`${displayKey}: ${displayValue}`);
|
|
76
|
+
}
|
|
37
77
|
}
|
|
38
|
-
|
|
78
|
+
const actions = metadata?.actions ?? entry.actions;
|
|
79
|
+
lines.push("");
|
|
80
|
+
lines.push("─────── Actions ───────");
|
|
81
|
+
if (actions && actions.length > 0) {
|
|
82
|
+
for (let i = 0; i < actions.length; i++) {
|
|
83
|
+
const action = actions[i];
|
|
84
|
+
if (action) lines.push(`[${i + 1}] ${action.name}`);
|
|
85
|
+
}
|
|
39
86
|
lines.push("");
|
|
40
|
-
lines.push(
|
|
41
|
-
}
|
|
42
|
-
if (metadata?.uri) lines.push(`URI: ${metadata.uri}`);
|
|
87
|
+
lines.push("(F4 to execute)");
|
|
88
|
+
} else lines.push("(none)");
|
|
43
89
|
return lines;
|
|
44
90
|
}
|
|
45
91
|
/**
|
|
92
|
+
* Format a value for display
|
|
93
|
+
*/
|
|
94
|
+
function formatValue(value) {
|
|
95
|
+
if (value === null || value === void 0) return "";
|
|
96
|
+
if (typeof value === "string") return value;
|
|
97
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
if (value.length === 0) return "[]";
|
|
100
|
+
if (value.every((v) => typeof v === "string" || typeof v === "number")) return value.join(", ");
|
|
101
|
+
if (value.every((v) => typeof v === "object" && v !== null && "name" in v)) return formatNamedItems(value);
|
|
102
|
+
try {
|
|
103
|
+
return JSON.stringify(value, null, 2);
|
|
104
|
+
} catch {
|
|
105
|
+
return "[unable to display]";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === "object") {
|
|
109
|
+
const obj = value;
|
|
110
|
+
if (isSchemaProperties(obj)) return formatSchemaProperties(obj);
|
|
111
|
+
try {
|
|
112
|
+
return JSON.stringify(value, null, 2);
|
|
113
|
+
} catch {
|
|
114
|
+
return "[unable to display]";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return String(value);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if object looks like JSON Schema properties
|
|
121
|
+
*/
|
|
122
|
+
function isSchemaProperties(obj) {
|
|
123
|
+
const values = Object.values(obj);
|
|
124
|
+
if (values.length === 0) return false;
|
|
125
|
+
return values.every((v) => typeof v === "object" && v !== null && ("type" in v || "$ref" in v));
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Format JSON Schema properties object
|
|
129
|
+
*/
|
|
130
|
+
function formatSchemaProperties(props) {
|
|
131
|
+
const lines = [];
|
|
132
|
+
for (const [name, schema] of Object.entries(props)) {
|
|
133
|
+
const s = schema;
|
|
134
|
+
const type = s.type || s.$ref || "any";
|
|
135
|
+
const desc = s.description ? ` - ${s.description}` : "";
|
|
136
|
+
lines.push(`• ${name}: ${type}${desc}`);
|
|
137
|
+
}
|
|
138
|
+
return lines.join("\n");
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Format array of named items (columns, fields, etc.)
|
|
142
|
+
*/
|
|
143
|
+
function formatNamedItems(items) {
|
|
144
|
+
const lines = [];
|
|
145
|
+
for (const item of items) {
|
|
146
|
+
const type = item.type ? `: ${item.type}` : "";
|
|
147
|
+
const desc = item.description ? ` - ${item.description}` : "";
|
|
148
|
+
const flags = [];
|
|
149
|
+
if (item.primaryKey) flags.push("PK");
|
|
150
|
+
if (item.nullable) flags.push("nullable");
|
|
151
|
+
const flagsStr = flags.length > 0 ? ` (${flags.join(", ")})` : "";
|
|
152
|
+
lines.push(`• ${item.name}${type}${flagsStr}${desc}`);
|
|
153
|
+
}
|
|
154
|
+
return lines.join("\n");
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
46
157
|
* Format date and time
|
|
47
158
|
*/
|
|
48
159
|
function formatDateTime(date) {
|
|
@@ -55,20 +166,6 @@ function formatDateTime(date) {
|
|
|
55
166
|
});
|
|
56
167
|
}
|
|
57
168
|
/**
|
|
58
|
-
* Word wrap text
|
|
59
|
-
*/
|
|
60
|
-
function wrapText(text, maxWidth) {
|
|
61
|
-
const words = text.split(/\s+/);
|
|
62
|
-
const lines = [];
|
|
63
|
-
let current = "";
|
|
64
|
-
for (const word of words) if (current.length + word.length + 1 > maxWidth) {
|
|
65
|
-
if (current) lines.push(current);
|
|
66
|
-
current = word;
|
|
67
|
-
} else current = current ? `${current} ${word}` : word;
|
|
68
|
-
if (current) lines.push(current);
|
|
69
|
-
return lines;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
169
|
* Create metadata panel component
|
|
73
170
|
*/
|
|
74
171
|
function createMetadataPanel(blessed, options) {
|