@aigne/afs-cli 1.11.0-beta.3 → 1.11.0-beta.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/README.md +224 -31
- package/dist/cli.cjs +119 -27
- package/dist/cli.mjs +119 -27
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/ls.cjs +6 -4
- package/dist/commands/ls.mjs +7 -4
- package/dist/commands/ls.mjs.map +1 -1
- package/dist/commands/mount.cjs +39 -15
- package/dist/commands/mount.mjs +39 -15
- package/dist/commands/mount.mjs.map +1 -1
- package/dist/commands/serve.cjs +9 -6
- package/dist/commands/serve.mjs +9 -6
- package/dist/commands/serve.mjs.map +1 -1
- package/dist/config/loader.cjs +30 -10
- package/dist/config/loader.mjs +30 -10
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/provider-factory.cjs +1 -0
- package/dist/config/provider-factory.mjs +1 -0
- package/dist/config/provider-factory.mjs.map +1 -1
- package/dist/config/schema.cjs +58 -3
- package/dist/config/schema.mjs +57 -3
- package/dist/config/schema.mjs.map +1 -1
- package/dist/explorer/actions.cjs +246 -0
- package/dist/explorer/actions.mjs +240 -0
- package/dist/explorer/actions.mjs.map +1 -0
- package/dist/explorer/components/dialog.cjs +231 -0
- package/dist/explorer/components/dialog.mjs +232 -0
- package/dist/explorer/components/dialog.mjs.map +1 -0
- package/dist/explorer/components/file-list.cjs +107 -0
- package/dist/explorer/components/file-list.mjs +107 -0
- package/dist/explorer/components/file-list.mjs.map +1 -0
- package/dist/explorer/components/function-bar.cjs +55 -0
- package/dist/explorer/components/function-bar.mjs +55 -0
- package/dist/explorer/components/function-bar.mjs.map +1 -0
- package/dist/explorer/components/index.cjs +5 -0
- package/dist/explorer/components/index.mjs +7 -0
- package/dist/explorer/components/metadata-panel.cjs +122 -0
- package/dist/explorer/components/metadata-panel.mjs +122 -0
- package/dist/explorer/components/metadata-panel.mjs.map +1 -0
- package/dist/explorer/components/status-bar.cjs +53 -0
- package/dist/explorer/components/status-bar.mjs +54 -0
- package/dist/explorer/components/status-bar.mjs.map +1 -0
- package/dist/explorer/keybindings.cjs +214 -0
- package/dist/explorer/keybindings.mjs +213 -0
- package/dist/explorer/keybindings.mjs.map +1 -0
- package/dist/explorer/screen.cjs +200 -0
- package/dist/explorer/screen.mjs +199 -0
- package/dist/explorer/screen.mjs.map +1 -0
- package/dist/explorer/state.cjs +53 -0
- package/dist/explorer/state.mjs +53 -0
- package/dist/explorer/state.mjs.map +1 -0
- package/dist/explorer/theme.cjs +158 -0
- package/dist/explorer/theme.mjs +155 -0
- package/dist/explorer/theme.mjs.map +1 -0
- package/dist/path-utils.cjs +104 -0
- package/dist/path-utils.mjs +104 -0
- package/dist/path-utils.mjs.map +1 -0
- package/dist/runtime.cjs +47 -33
- package/dist/runtime.mjs +47 -33
- package/dist/runtime.mjs.map +1 -1
- package/dist/ui/header.cjs +60 -0
- package/dist/ui/header.mjs +59 -0
- package/dist/ui/header.mjs.map +1 -0
- package/dist/ui/index.cjs +17 -0
- package/dist/ui/index.mjs +15 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/ui/terminal.cjs +97 -0
- package/dist/ui/terminal.mjs +95 -0
- package/dist/ui/terminal.mjs.map +1 -0
- package/package.json +9 -7
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { colors, printLogo } from "../ui/index.mjs";
|
|
2
|
+
import { createInitialState, executeAction, getExplain, loadDirectory, loadMetadata, navigation, readFileContent } from "./actions.mjs";
|
|
3
|
+
import { createDefaultRegistry } from "./keybindings.mjs";
|
|
4
|
+
import { createDialogManager } from "./components/dialog.mjs";
|
|
5
|
+
import { createFileList } from "./components/file-list.mjs";
|
|
6
|
+
import { createFunctionBar } from "./components/function-bar.mjs";
|
|
7
|
+
import { createMetadataPanel } from "./components/metadata-panel.mjs";
|
|
8
|
+
import { createStatusBar } from "./components/status-bar.mjs";
|
|
9
|
+
import "./components/index.mjs";
|
|
10
|
+
import { createStore } from "./state.mjs";
|
|
11
|
+
import blessed from "blessed";
|
|
12
|
+
|
|
13
|
+
//#region src/explorer/screen.ts
|
|
14
|
+
/**
|
|
15
|
+
* AFS Explorer Screen
|
|
16
|
+
*
|
|
17
|
+
* Main screen controller that manages all UI components and user interaction.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Create and run the explorer screen
|
|
21
|
+
*/
|
|
22
|
+
async function createExplorerScreen(options) {
|
|
23
|
+
const { runtime, startPath = "/", version, mountCount } = options;
|
|
24
|
+
const store = createStore(createInitialState(startPath));
|
|
25
|
+
const registry = createDefaultRegistry();
|
|
26
|
+
const screen = blessed.screen({
|
|
27
|
+
smartCSR: true,
|
|
28
|
+
title: "AFS Explorer",
|
|
29
|
+
terminal: "xterm",
|
|
30
|
+
fullUnicode: true,
|
|
31
|
+
warnings: false
|
|
32
|
+
});
|
|
33
|
+
createStatusBar(blessed, {
|
|
34
|
+
parent: screen,
|
|
35
|
+
store,
|
|
36
|
+
width: "100%",
|
|
37
|
+
top: 0
|
|
38
|
+
});
|
|
39
|
+
const fileList = createFileList(blessed, {
|
|
40
|
+
parent: screen,
|
|
41
|
+
store,
|
|
42
|
+
width: "70%",
|
|
43
|
+
height: "100%-2",
|
|
44
|
+
top: 1,
|
|
45
|
+
left: 0
|
|
46
|
+
});
|
|
47
|
+
const metadataPanel = createMetadataPanel(blessed, {
|
|
48
|
+
parent: screen,
|
|
49
|
+
width: "30%",
|
|
50
|
+
height: "100%-2",
|
|
51
|
+
top: 1,
|
|
52
|
+
right: 0
|
|
53
|
+
});
|
|
54
|
+
createFunctionBar(blessed, {
|
|
55
|
+
parent: screen,
|
|
56
|
+
registry,
|
|
57
|
+
width: "100%",
|
|
58
|
+
bottom: 0
|
|
59
|
+
});
|
|
60
|
+
const dialogs = createDialogManager(blessed, { parent: screen });
|
|
61
|
+
async function loadPath(path) {
|
|
62
|
+
store.setState({
|
|
63
|
+
loading: true,
|
|
64
|
+
error: void 0
|
|
65
|
+
});
|
|
66
|
+
const result = await loadDirectory(runtime, path);
|
|
67
|
+
store.setState({
|
|
68
|
+
currentPath: path,
|
|
69
|
+
entries: result.entries,
|
|
70
|
+
selectedIndex: 0,
|
|
71
|
+
scrollOffset: 0,
|
|
72
|
+
loading: false,
|
|
73
|
+
error: result.error
|
|
74
|
+
});
|
|
75
|
+
updateMetadata();
|
|
76
|
+
}
|
|
77
|
+
async function updateMetadata() {
|
|
78
|
+
const selected = navigation.getSelected(store.getState());
|
|
79
|
+
if (selected) {
|
|
80
|
+
const metadata = await loadMetadata(runtime, selected);
|
|
81
|
+
metadataPanel.update(selected, metadata);
|
|
82
|
+
} else metadataPanel.clear();
|
|
83
|
+
}
|
|
84
|
+
async function handleEnter() {
|
|
85
|
+
const selected = navigation.getSelected(store.getState());
|
|
86
|
+
if (!selected) return;
|
|
87
|
+
if (selected.type === "directory" || selected.type === "up") await loadPath(selected.path);
|
|
88
|
+
else if (selected.type === "file") await handleView();
|
|
89
|
+
}
|
|
90
|
+
async function handleBack() {
|
|
91
|
+
const state = store.getState();
|
|
92
|
+
const parentPath = navigation.getParentPath(state.currentPath);
|
|
93
|
+
if (parentPath !== state.currentPath) await loadPath(parentPath);
|
|
94
|
+
}
|
|
95
|
+
async function handleView() {
|
|
96
|
+
const selected = navigation.getSelected(store.getState());
|
|
97
|
+
if (!selected || selected.type === "up") return;
|
|
98
|
+
if (selected.type === "file") {
|
|
99
|
+
dialogs.showLoading("Loading file");
|
|
100
|
+
const result = await readFileContent(runtime, selected.path);
|
|
101
|
+
if (result.error) dialogs.showError("View Error", result.error);
|
|
102
|
+
else dialogs.showFileView(selected.path, result.content);
|
|
103
|
+
} else if (selected.type === "directory") await loadPath(selected.path);
|
|
104
|
+
}
|
|
105
|
+
async function handleExplain() {
|
|
106
|
+
const selected = navigation.getSelected(store.getState());
|
|
107
|
+
if (!selected || selected.type === "up") return;
|
|
108
|
+
dialogs.showLoading("Getting explain info");
|
|
109
|
+
const result = await getExplain(runtime, selected.path);
|
|
110
|
+
if (result.error) dialogs.showError("Explain Error", result.error);
|
|
111
|
+
else dialogs.showExplain(selected.path, result.content);
|
|
112
|
+
}
|
|
113
|
+
async function handleExec() {
|
|
114
|
+
const selected = navigation.getSelected(store.getState());
|
|
115
|
+
if (!selected || selected.type === "up") return;
|
|
116
|
+
dialogs.showLoading("Executing action");
|
|
117
|
+
const result = await executeAction(runtime, selected.path, "default");
|
|
118
|
+
dialogs.showActionResult("default", result.success, result.message, result.data);
|
|
119
|
+
}
|
|
120
|
+
async function handleRefresh() {
|
|
121
|
+
await loadPath(store.getState().currentPath);
|
|
122
|
+
}
|
|
123
|
+
const actionHandlers = {
|
|
124
|
+
help: () => dialogs.showHelp(registry),
|
|
125
|
+
explain: handleExplain,
|
|
126
|
+
view: handleView,
|
|
127
|
+
exec: handleExec,
|
|
128
|
+
refresh: handleRefresh,
|
|
129
|
+
quit: () => {
|
|
130
|
+
dialogs.showConfirm("Are you sure you want to quit?", () => {
|
|
131
|
+
screen.destroy();
|
|
132
|
+
printLogo();
|
|
133
|
+
const versionPart = colors.green(`v${version}`);
|
|
134
|
+
const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? "mount" : "mounts"}`);
|
|
135
|
+
console.log(`${versionPart} ${colors.dim("•")} ${mountPart}`);
|
|
136
|
+
console.log(colors.dim("\nThanks for using AFS Explorer!\n"));
|
|
137
|
+
process.exit(0);
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
"nav:up": () => {
|
|
141
|
+
store.setState(navigation.up(store.getState()));
|
|
142
|
+
updateMetadata();
|
|
143
|
+
},
|
|
144
|
+
"nav:down": () => {
|
|
145
|
+
store.setState(navigation.down(store.getState()));
|
|
146
|
+
updateMetadata();
|
|
147
|
+
},
|
|
148
|
+
"nav:enter": handleEnter,
|
|
149
|
+
"nav:back": handleBack,
|
|
150
|
+
"nav:home": () => {
|
|
151
|
+
store.setState(navigation.home(store.getState()));
|
|
152
|
+
updateMetadata();
|
|
153
|
+
},
|
|
154
|
+
"nav:end": () => {
|
|
155
|
+
store.setState(navigation.end(store.getState()));
|
|
156
|
+
updateMetadata();
|
|
157
|
+
},
|
|
158
|
+
"nav:pageup": () => {
|
|
159
|
+
store.setState(navigation.pageUp(store.getState(), fileList.getVisibleHeight()));
|
|
160
|
+
updateMetadata();
|
|
161
|
+
},
|
|
162
|
+
"nav:pagedown": () => {
|
|
163
|
+
store.setState(navigation.pageDown(store.getState(), fileList.getVisibleHeight()));
|
|
164
|
+
updateMetadata();
|
|
165
|
+
},
|
|
166
|
+
cancel: () => {
|
|
167
|
+
if (dialogs.isOpen()) dialogs.close();
|
|
168
|
+
else actionHandlers.quit?.();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
screen.on("keypress", async (ch, key) => {
|
|
172
|
+
if (dialogs.isOpen()) return;
|
|
173
|
+
const keyName = key.name || ch;
|
|
174
|
+
if (!keyName) return;
|
|
175
|
+
const binding = registry.getBindingForKey(keyName);
|
|
176
|
+
if (binding) {
|
|
177
|
+
const handler = actionHandlers[binding.action];
|
|
178
|
+
if (handler) {
|
|
179
|
+
await handler();
|
|
180
|
+
screen.render();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
screen.on("resize", () => {
|
|
185
|
+
screen.render();
|
|
186
|
+
});
|
|
187
|
+
fileList.focus();
|
|
188
|
+
await loadPath(startPath);
|
|
189
|
+
screen.render();
|
|
190
|
+
return new Promise((resolve) => {
|
|
191
|
+
screen.on("destroy", () => {
|
|
192
|
+
resolve();
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
export { createExplorerScreen };
|
|
199
|
+
//# sourceMappingURL=screen.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen.mjs","names":[],"sources":["../../src/explorer/screen.ts"],"sourcesContent":["/**\n * AFS Explorer Screen\n *\n * Main screen controller that manages all UI components and user interaction.\n */\n\nimport blessed from \"blessed\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport { colors, printLogo } from \"../ui/index.js\";\nimport {\n createInitialState,\n executeAction,\n getExplain,\n loadDirectory,\n loadMetadata,\n navigation,\n readFileContent,\n} from \"./actions.js\";\nimport {\n createDialogManager,\n createFileList,\n createFunctionBar,\n createMetadataPanel,\n createStatusBar,\n} from \"./components/index.js\";\nimport { createDefaultRegistry } from \"./keybindings.js\";\nimport { createStore } from \"./state.js\";\n\nexport interface ExplorerScreenOptions {\n runtime: AFSRuntime;\n startPath?: string;\n version: string;\n mountCount: number;\n}\n\n/**\n * Create and run the explorer screen\n */\nexport async function createExplorerScreen(options: ExplorerScreenOptions): Promise<void> {\n const { runtime, startPath = \"/\", version, mountCount } = options;\n\n // Create store with initial state\n const store = createStore(createInitialState(startPath));\n\n // Create key binding registry\n const registry = createDefaultRegistry();\n\n // Create blessed screen\n // Use 'xterm' terminal to avoid Setulc parsing warnings with xterm-256color\n const screen = blessed.screen({\n smartCSR: true,\n title: \"AFS Explorer\",\n terminal: \"xterm\",\n fullUnicode: true,\n warnings: false,\n });\n\n // Create components\n const _statusBar = createStatusBar(blessed, {\n parent: screen,\n store,\n width: \"100%\",\n top: 0,\n });\n\n const fileList = createFileList(blessed, {\n parent: screen,\n store,\n width: \"70%\",\n height: \"100%-2\",\n top: 1,\n left: 0,\n });\n\n const metadataPanel = createMetadataPanel(blessed, {\n parent: screen,\n width: \"30%\",\n height: \"100%-2\",\n top: 1,\n right: 0,\n });\n\n const _functionBar = createFunctionBar(blessed, {\n parent: screen,\n registry,\n width: \"100%\",\n bottom: 0,\n });\n\n const dialogs = createDialogManager(blessed, {\n parent: screen,\n });\n\n // Load initial directory\n async function loadPath(path: string): Promise<void> {\n store.setState({ loading: true, error: undefined });\n\n const result = await loadDirectory(runtime, path);\n\n store.setState({\n currentPath: path,\n entries: result.entries,\n selectedIndex: 0,\n scrollOffset: 0,\n loading: false,\n error: result.error,\n });\n\n // Update metadata panel for first entry\n updateMetadata();\n }\n\n // Update metadata panel for selected entry\n async function updateMetadata(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (selected) {\n const metadata = await loadMetadata(runtime, selected);\n metadataPanel.update(selected, metadata);\n } else {\n metadataPanel.clear();\n }\n }\n\n // Handle navigation\n async function handleEnter(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected) return;\n\n if (selected.type === \"directory\" || selected.type === \"up\") {\n await loadPath(selected.path);\n } else if (selected.type === \"file\") {\n await handleView();\n }\n }\n\n // Handle back navigation\n async function handleBack(): Promise<void> {\n const state = store.getState();\n const parentPath = navigation.getParentPath(state.currentPath);\n if (parentPath !== state.currentPath) {\n await loadPath(parentPath);\n }\n }\n\n // Handle view action (F3)\n async function handleView(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n if (selected.type === \"file\") {\n dialogs.showLoading(\"Loading file\");\n const result = await readFileContent(runtime, selected.path);\n if (result.error) {\n dialogs.showError(\"View Error\", result.error);\n } else {\n dialogs.showFileView(selected.path, result.content);\n }\n } else if (selected.type === \"directory\") {\n // Enter directory\n await loadPath(selected.path);\n }\n }\n\n // Handle explain action (F2)\n async function handleExplain(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n dialogs.showLoading(\"Getting explain info\");\n const result = await getExplain(runtime, selected.path);\n if (result.error) {\n dialogs.showError(\"Explain Error\", result.error);\n } else {\n dialogs.showExplain(selected.path, result.content);\n }\n }\n\n // Handle exec action (F4)\n async function handleExec(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n dialogs.showLoading(\"Executing action\");\n const result = await executeAction(runtime, selected.path, \"default\");\n dialogs.showActionResult(\"default\", result.success, result.message, result.data);\n }\n\n // Handle refresh action (F5)\n async function handleRefresh(): Promise<void> {\n const state = store.getState();\n await loadPath(state.currentPath);\n }\n\n // Action handlers map\n const actionHandlers: Record<string, () => void | Promise<void>> = {\n help: () => dialogs.showHelp(registry),\n explain: handleExplain,\n view: handleView,\n exec: handleExec,\n refresh: handleRefresh,\n quit: () => {\n dialogs.showConfirm(\"Are you sure you want to quit?\", () => {\n screen.destroy();\n // Show farewell message with version and mount info\n printLogo();\n const versionPart = colors.green(`v${version}`);\n const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? \"mount\" : \"mounts\"}`);\n console.log(`${versionPart} ${colors.dim(\"•\")} ${mountPart}`);\n console.log(colors.dim(\"\\nThanks for using AFS Explorer!\\n\"));\n process.exit(0);\n });\n },\n \"nav:up\": () => {\n store.setState(navigation.up(store.getState()));\n updateMetadata();\n },\n \"nav:down\": () => {\n store.setState(navigation.down(store.getState()));\n updateMetadata();\n },\n \"nav:enter\": handleEnter,\n \"nav:back\": handleBack,\n \"nav:home\": () => {\n store.setState(navigation.home(store.getState()));\n updateMetadata();\n },\n \"nav:end\": () => {\n store.setState(navigation.end(store.getState()));\n updateMetadata();\n },\n \"nav:pageup\": () => {\n store.setState(navigation.pageUp(store.getState(), fileList.getVisibleHeight()));\n updateMetadata();\n },\n \"nav:pagedown\": () => {\n store.setState(navigation.pageDown(store.getState(), fileList.getVisibleHeight()));\n updateMetadata();\n },\n cancel: () => {\n if (dialogs.isOpen()) {\n dialogs.close();\n } else {\n // ESC on main screen triggers quit with confirmation\n actionHandlers.quit?.();\n }\n },\n };\n\n // Bind keys\n screen.on(\"keypress\", async (ch, key) => {\n // Skip if dialog is handling keys\n if (dialogs.isOpen()) return;\n\n const keyName = key.name || ch;\n if (!keyName) return;\n\n // Note: Not passing full context since default bindings don't have conditions\n const binding = registry.getBindingForKey(keyName);\n\n if (binding) {\n const handler = actionHandlers[binding.action];\n if (handler) {\n await handler();\n screen.render();\n }\n }\n });\n\n // Handle resize\n screen.on(\"resize\", () => {\n screen.render();\n });\n\n // Focus file list\n fileList.focus();\n\n // Load initial directory\n await loadPath(startPath);\n\n // Render\n screen.render();\n\n // Return promise that resolves when screen is destroyed\n return new Promise((resolve) => {\n screen.on(\"destroy\", () => {\n resolve();\n });\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/explorer/state.ts
|
|
3
|
+
/**
|
|
4
|
+
* State store for explorer
|
|
5
|
+
*/
|
|
6
|
+
var ExplorerStore = class {
|
|
7
|
+
state;
|
|
8
|
+
listeners = /* @__PURE__ */ new Set();
|
|
9
|
+
constructor(initialState) {
|
|
10
|
+
this.state = initialState;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get current state
|
|
14
|
+
*/
|
|
15
|
+
getState() {
|
|
16
|
+
return this.state;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Update state with partial update
|
|
20
|
+
*/
|
|
21
|
+
setState(update) {
|
|
22
|
+
const prevState = this.state;
|
|
23
|
+
this.state = {
|
|
24
|
+
...this.state,
|
|
25
|
+
...update
|
|
26
|
+
};
|
|
27
|
+
for (const listener of this.listeners) listener(this.state, prevState);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to state changes
|
|
31
|
+
*/
|
|
32
|
+
subscribe(listener) {
|
|
33
|
+
this.listeners.add(listener);
|
|
34
|
+
return () => this.listeners.delete(listener);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reset to initial state
|
|
38
|
+
*/
|
|
39
|
+
reset(initialState) {
|
|
40
|
+
const prevState = this.state;
|
|
41
|
+
this.state = initialState;
|
|
42
|
+
for (const listener of this.listeners) listener(this.state, prevState);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Create a new store with initial state
|
|
47
|
+
*/
|
|
48
|
+
function createStore(initialState) {
|
|
49
|
+
return new ExplorerStore(initialState);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
exports.createStore = createStore;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/explorer/state.ts
|
|
2
|
+
/**
|
|
3
|
+
* State store for explorer
|
|
4
|
+
*/
|
|
5
|
+
var ExplorerStore = class {
|
|
6
|
+
state;
|
|
7
|
+
listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
constructor(initialState) {
|
|
9
|
+
this.state = initialState;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get current state
|
|
13
|
+
*/
|
|
14
|
+
getState() {
|
|
15
|
+
return this.state;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Update state with partial update
|
|
19
|
+
*/
|
|
20
|
+
setState(update) {
|
|
21
|
+
const prevState = this.state;
|
|
22
|
+
this.state = {
|
|
23
|
+
...this.state,
|
|
24
|
+
...update
|
|
25
|
+
};
|
|
26
|
+
for (const listener of this.listeners) listener(this.state, prevState);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Subscribe to state changes
|
|
30
|
+
*/
|
|
31
|
+
subscribe(listener) {
|
|
32
|
+
this.listeners.add(listener);
|
|
33
|
+
return () => this.listeners.delete(listener);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reset to initial state
|
|
37
|
+
*/
|
|
38
|
+
reset(initialState) {
|
|
39
|
+
const prevState = this.state;
|
|
40
|
+
this.state = initialState;
|
|
41
|
+
for (const listener of this.listeners) listener(this.state, prevState);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Create a new store with initial state
|
|
46
|
+
*/
|
|
47
|
+
function createStore(initialState) {
|
|
48
|
+
return new ExplorerStore(initialState);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
export { createStore };
|
|
53
|
+
//# sourceMappingURL=state.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.mjs","names":[],"sources":["../../src/explorer/state.ts"],"sourcesContent":["/**\n * AFS Explorer State Management\n *\n * Simple state management for the explorer.\n */\n\nimport type { ExplorerState } from \"./types.js\";\n\n/**\n * State change listener\n */\nexport type StateListener = (state: ExplorerState, prevState: ExplorerState) => void;\n\n/**\n * State store for explorer\n */\nexport class ExplorerStore {\n private state: ExplorerState;\n private listeners: Set<StateListener> = new Set();\n\n constructor(initialState: ExplorerState) {\n this.state = initialState;\n }\n\n /**\n * Get current state\n */\n getState(): ExplorerState {\n return this.state;\n }\n\n /**\n * Update state with partial update\n */\n setState(update: Partial<ExplorerState>): void {\n const prevState = this.state;\n this.state = { ...this.state, ...update };\n\n // Notify listeners\n for (const listener of this.listeners) {\n listener(this.state, prevState);\n }\n }\n\n /**\n * Subscribe to state changes\n */\n subscribe(listener: StateListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Reset to initial state\n */\n reset(initialState: ExplorerState): void {\n const prevState = this.state;\n this.state = initialState;\n\n for (const listener of this.listeners) {\n listener(this.state, prevState);\n }\n }\n}\n\n/**\n * Create a new store with initial state\n */\nexport function createStore(initialState: ExplorerState): ExplorerStore {\n return new ExplorerStore(initialState);\n}\n"],"mappings":";;;;AAgBA,IAAa,gBAAb,MAA2B;CACzB,AAAQ;CACR,AAAQ,4BAAgC,IAAI,KAAK;CAEjD,YAAY,cAA6B;AACvC,OAAK,QAAQ;;;;;CAMf,WAA0B;AACxB,SAAO,KAAK;;;;;CAMd,SAAS,QAAsC;EAC7C,MAAM,YAAY,KAAK;AACvB,OAAK,QAAQ;GAAE,GAAG,KAAK;GAAO,GAAG;GAAQ;AAGzC,OAAK,MAAM,YAAY,KAAK,UAC1B,UAAS,KAAK,OAAO,UAAU;;;;;CAOnC,UAAU,UAAqC;AAC7C,OAAK,UAAU,IAAI,SAAS;AAC5B,eAAa,KAAK,UAAU,OAAO,SAAS;;;;;CAM9C,MAAM,cAAmC;EACvC,MAAM,YAAY,KAAK;AACvB,OAAK,QAAQ;AAEb,OAAK,MAAM,YAAY,KAAK,UAC1B,UAAS,KAAK,OAAO,UAAU;;;;;;AAQrC,SAAgB,YAAY,cAA4C;AACtE,QAAO,IAAI,cAAc,aAAa"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/explorer/theme.ts
|
|
3
|
+
/**
|
|
4
|
+
* AFS Explorer Theme
|
|
5
|
+
*
|
|
6
|
+
* PC Tools Deluxe classic blue color scheme
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Color definitions for PC Tools theme
|
|
10
|
+
*/
|
|
11
|
+
const Colors = {
|
|
12
|
+
bg: {
|
|
13
|
+
main: "blue",
|
|
14
|
+
selected: "cyan",
|
|
15
|
+
functionKey: "cyan",
|
|
16
|
+
dialog: "blue",
|
|
17
|
+
dialogBorder: "white"
|
|
18
|
+
},
|
|
19
|
+
fg: {
|
|
20
|
+
normal: "cyan",
|
|
21
|
+
selected: "black",
|
|
22
|
+
title: "white",
|
|
23
|
+
border: "cyan",
|
|
24
|
+
directory: "yellow",
|
|
25
|
+
file: "white",
|
|
26
|
+
exec: "green",
|
|
27
|
+
link: "magenta",
|
|
28
|
+
up: "yellow",
|
|
29
|
+
size: "cyan",
|
|
30
|
+
date: "cyan",
|
|
31
|
+
functionKeyLabel: "black",
|
|
32
|
+
functionKeyText: "white",
|
|
33
|
+
status: "yellow",
|
|
34
|
+
error: "red",
|
|
35
|
+
muted: "gray"
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Blessed style objects for components
|
|
40
|
+
*/
|
|
41
|
+
const Styles = {
|
|
42
|
+
screen: { bg: Colors.bg.main },
|
|
43
|
+
border: {
|
|
44
|
+
type: "line",
|
|
45
|
+
fg: Colors.fg.border,
|
|
46
|
+
bg: Colors.bg.main
|
|
47
|
+
},
|
|
48
|
+
title: {
|
|
49
|
+
fg: Colors.fg.title,
|
|
50
|
+
bg: Colors.bg.main,
|
|
51
|
+
bold: true
|
|
52
|
+
},
|
|
53
|
+
listItem: {
|
|
54
|
+
fg: Colors.fg.normal,
|
|
55
|
+
bg: Colors.bg.main
|
|
56
|
+
},
|
|
57
|
+
listItemSelected: {
|
|
58
|
+
fg: Colors.fg.selected,
|
|
59
|
+
bg: Colors.bg.selected,
|
|
60
|
+
bold: true
|
|
61
|
+
},
|
|
62
|
+
directory: {
|
|
63
|
+
fg: Colors.fg.directory,
|
|
64
|
+
bg: Colors.bg.main,
|
|
65
|
+
bold: true
|
|
66
|
+
},
|
|
67
|
+
directorySelected: {
|
|
68
|
+
fg: "black",
|
|
69
|
+
bg: Colors.bg.selected,
|
|
70
|
+
bold: true
|
|
71
|
+
},
|
|
72
|
+
file: {
|
|
73
|
+
fg: Colors.fg.file,
|
|
74
|
+
bg: Colors.bg.main
|
|
75
|
+
},
|
|
76
|
+
exec: {
|
|
77
|
+
fg: Colors.fg.exec,
|
|
78
|
+
bg: Colors.bg.main
|
|
79
|
+
},
|
|
80
|
+
functionKeyLabel: {
|
|
81
|
+
fg: Colors.fg.functionKeyLabel,
|
|
82
|
+
bg: Colors.bg.functionKey
|
|
83
|
+
},
|
|
84
|
+
functionKeyText: {
|
|
85
|
+
fg: Colors.fg.functionKeyText,
|
|
86
|
+
bg: Colors.bg.main
|
|
87
|
+
},
|
|
88
|
+
status: {
|
|
89
|
+
fg: Colors.fg.status,
|
|
90
|
+
bg: Colors.bg.main
|
|
91
|
+
},
|
|
92
|
+
metadata: {
|
|
93
|
+
fg: Colors.fg.normal,
|
|
94
|
+
bg: Colors.bg.main
|
|
95
|
+
},
|
|
96
|
+
metadataLabel: {
|
|
97
|
+
fg: Colors.fg.muted,
|
|
98
|
+
bg: Colors.bg.main
|
|
99
|
+
},
|
|
100
|
+
metadataValue: {
|
|
101
|
+
fg: Colors.fg.normal,
|
|
102
|
+
bg: Colors.bg.main
|
|
103
|
+
},
|
|
104
|
+
error: {
|
|
105
|
+
fg: Colors.fg.error,
|
|
106
|
+
bg: Colors.bg.main,
|
|
107
|
+
bold: true
|
|
108
|
+
},
|
|
109
|
+
dialog: {
|
|
110
|
+
fg: Colors.fg.normal,
|
|
111
|
+
bg: Colors.bg.dialog,
|
|
112
|
+
border: {
|
|
113
|
+
type: "line",
|
|
114
|
+
fg: Colors.bg.dialogBorder,
|
|
115
|
+
bg: Colors.bg.dialog
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Icons for different entry types
|
|
121
|
+
* Using ASCII characters for better terminal compatibility
|
|
122
|
+
*/
|
|
123
|
+
const Icons = {
|
|
124
|
+
up: "[D]",
|
|
125
|
+
directory: "[D]",
|
|
126
|
+
file: " ",
|
|
127
|
+
exec: "[X]",
|
|
128
|
+
link: "[L]",
|
|
129
|
+
selected: ">",
|
|
130
|
+
unselected: " "
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* UI symbols
|
|
134
|
+
* Using ASCII characters for better terminal compatibility
|
|
135
|
+
*/
|
|
136
|
+
const Symbols = {
|
|
137
|
+
folder: "[D]",
|
|
138
|
+
error: "X",
|
|
139
|
+
success: "OK",
|
|
140
|
+
scrollbar: "#",
|
|
141
|
+
loading: "..."
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Format file size for display
|
|
145
|
+
*/
|
|
146
|
+
function formatSize(bytes) {
|
|
147
|
+
if (bytes === void 0) return "";
|
|
148
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
149
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
150
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
151
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//#endregion
|
|
155
|
+
exports.Colors = Colors;
|
|
156
|
+
exports.Icons = Icons;
|
|
157
|
+
exports.Symbols = Symbols;
|
|
158
|
+
exports.formatSize = formatSize;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
//#region src/explorer/theme.ts
|
|
2
|
+
/**
|
|
3
|
+
* AFS Explorer Theme
|
|
4
|
+
*
|
|
5
|
+
* PC Tools Deluxe classic blue color scheme
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Color definitions for PC Tools theme
|
|
9
|
+
*/
|
|
10
|
+
const Colors = {
|
|
11
|
+
bg: {
|
|
12
|
+
main: "blue",
|
|
13
|
+
selected: "cyan",
|
|
14
|
+
functionKey: "cyan",
|
|
15
|
+
dialog: "blue",
|
|
16
|
+
dialogBorder: "white"
|
|
17
|
+
},
|
|
18
|
+
fg: {
|
|
19
|
+
normal: "cyan",
|
|
20
|
+
selected: "black",
|
|
21
|
+
title: "white",
|
|
22
|
+
border: "cyan",
|
|
23
|
+
directory: "yellow",
|
|
24
|
+
file: "white",
|
|
25
|
+
exec: "green",
|
|
26
|
+
link: "magenta",
|
|
27
|
+
up: "yellow",
|
|
28
|
+
size: "cyan",
|
|
29
|
+
date: "cyan",
|
|
30
|
+
functionKeyLabel: "black",
|
|
31
|
+
functionKeyText: "white",
|
|
32
|
+
status: "yellow",
|
|
33
|
+
error: "red",
|
|
34
|
+
muted: "gray"
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Blessed style objects for components
|
|
39
|
+
*/
|
|
40
|
+
const Styles = {
|
|
41
|
+
screen: { bg: Colors.bg.main },
|
|
42
|
+
border: {
|
|
43
|
+
type: "line",
|
|
44
|
+
fg: Colors.fg.border,
|
|
45
|
+
bg: Colors.bg.main
|
|
46
|
+
},
|
|
47
|
+
title: {
|
|
48
|
+
fg: Colors.fg.title,
|
|
49
|
+
bg: Colors.bg.main,
|
|
50
|
+
bold: true
|
|
51
|
+
},
|
|
52
|
+
listItem: {
|
|
53
|
+
fg: Colors.fg.normal,
|
|
54
|
+
bg: Colors.bg.main
|
|
55
|
+
},
|
|
56
|
+
listItemSelected: {
|
|
57
|
+
fg: Colors.fg.selected,
|
|
58
|
+
bg: Colors.bg.selected,
|
|
59
|
+
bold: true
|
|
60
|
+
},
|
|
61
|
+
directory: {
|
|
62
|
+
fg: Colors.fg.directory,
|
|
63
|
+
bg: Colors.bg.main,
|
|
64
|
+
bold: true
|
|
65
|
+
},
|
|
66
|
+
directorySelected: {
|
|
67
|
+
fg: "black",
|
|
68
|
+
bg: Colors.bg.selected,
|
|
69
|
+
bold: true
|
|
70
|
+
},
|
|
71
|
+
file: {
|
|
72
|
+
fg: Colors.fg.file,
|
|
73
|
+
bg: Colors.bg.main
|
|
74
|
+
},
|
|
75
|
+
exec: {
|
|
76
|
+
fg: Colors.fg.exec,
|
|
77
|
+
bg: Colors.bg.main
|
|
78
|
+
},
|
|
79
|
+
functionKeyLabel: {
|
|
80
|
+
fg: Colors.fg.functionKeyLabel,
|
|
81
|
+
bg: Colors.bg.functionKey
|
|
82
|
+
},
|
|
83
|
+
functionKeyText: {
|
|
84
|
+
fg: Colors.fg.functionKeyText,
|
|
85
|
+
bg: Colors.bg.main
|
|
86
|
+
},
|
|
87
|
+
status: {
|
|
88
|
+
fg: Colors.fg.status,
|
|
89
|
+
bg: Colors.bg.main
|
|
90
|
+
},
|
|
91
|
+
metadata: {
|
|
92
|
+
fg: Colors.fg.normal,
|
|
93
|
+
bg: Colors.bg.main
|
|
94
|
+
},
|
|
95
|
+
metadataLabel: {
|
|
96
|
+
fg: Colors.fg.muted,
|
|
97
|
+
bg: Colors.bg.main
|
|
98
|
+
},
|
|
99
|
+
metadataValue: {
|
|
100
|
+
fg: Colors.fg.normal,
|
|
101
|
+
bg: Colors.bg.main
|
|
102
|
+
},
|
|
103
|
+
error: {
|
|
104
|
+
fg: Colors.fg.error,
|
|
105
|
+
bg: Colors.bg.main,
|
|
106
|
+
bold: true
|
|
107
|
+
},
|
|
108
|
+
dialog: {
|
|
109
|
+
fg: Colors.fg.normal,
|
|
110
|
+
bg: Colors.bg.dialog,
|
|
111
|
+
border: {
|
|
112
|
+
type: "line",
|
|
113
|
+
fg: Colors.bg.dialogBorder,
|
|
114
|
+
bg: Colors.bg.dialog
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Icons for different entry types
|
|
120
|
+
* Using ASCII characters for better terminal compatibility
|
|
121
|
+
*/
|
|
122
|
+
const Icons = {
|
|
123
|
+
up: "[D]",
|
|
124
|
+
directory: "[D]",
|
|
125
|
+
file: " ",
|
|
126
|
+
exec: "[X]",
|
|
127
|
+
link: "[L]",
|
|
128
|
+
selected: ">",
|
|
129
|
+
unselected: " "
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* UI symbols
|
|
133
|
+
* Using ASCII characters for better terminal compatibility
|
|
134
|
+
*/
|
|
135
|
+
const Symbols = {
|
|
136
|
+
folder: "[D]",
|
|
137
|
+
error: "X",
|
|
138
|
+
success: "OK",
|
|
139
|
+
scrollbar: "#",
|
|
140
|
+
loading: "..."
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Format file size for display
|
|
144
|
+
*/
|
|
145
|
+
function formatSize(bytes) {
|
|
146
|
+
if (bytes === void 0) return "";
|
|
147
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
148
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
149
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
150
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
export { Colors, Icons, Symbols, formatSize };
|
|
155
|
+
//# sourceMappingURL=theme.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.mjs","names":[],"sources":["../../src/explorer/theme.ts"],"sourcesContent":["/**\n * AFS Explorer Theme\n *\n * PC Tools Deluxe classic blue color scheme\n */\n\n/**\n * Color definitions for PC Tools theme\n */\nexport const Colors = {\n // Background colors\n bg: {\n main: \"blue\",\n selected: \"cyan\",\n functionKey: \"cyan\",\n dialog: \"blue\",\n dialogBorder: \"white\",\n },\n\n // Foreground colors (using standard blessed color names)\n fg: {\n normal: \"cyan\",\n selected: \"black\",\n title: \"white\",\n border: \"cyan\",\n directory: \"yellow\",\n file: \"white\",\n exec: \"green\",\n link: \"magenta\",\n up: \"yellow\",\n size: \"cyan\",\n date: \"cyan\",\n functionKeyLabel: \"black\",\n functionKeyText: \"white\",\n status: \"yellow\",\n error: \"red\",\n muted: \"gray\",\n },\n} as const;\n\n/**\n * Blessed style objects for components\n */\nexport const Styles = {\n /** Main screen background */\n screen: {\n bg: Colors.bg.main,\n },\n\n /** Box/panel borders */\n border: {\n type: \"line\" as const,\n fg: Colors.fg.border,\n bg: Colors.bg.main,\n },\n\n /** Title bar style */\n title: {\n fg: Colors.fg.title,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Normal list item */\n listItem: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Selected list item */\n listItemSelected: {\n fg: Colors.fg.selected,\n bg: Colors.bg.selected,\n bold: true,\n },\n\n /** Directory entry */\n directory: {\n fg: Colors.fg.directory,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Directory entry selected */\n directorySelected: {\n fg: \"black\",\n bg: Colors.bg.selected,\n bold: true,\n },\n\n /** File entry */\n file: {\n fg: Colors.fg.file,\n bg: Colors.bg.main,\n },\n\n /** Exec entry */\n exec: {\n fg: Colors.fg.exec,\n bg: Colors.bg.main,\n },\n\n /** Function key label (e.g., \"F1\") */\n functionKeyLabel: {\n fg: Colors.fg.functionKeyLabel,\n bg: Colors.bg.functionKey,\n },\n\n /** Function key text (e.g., \"Help\") */\n functionKeyText: {\n fg: Colors.fg.functionKeyText,\n bg: Colors.bg.main,\n },\n\n /** Status bar */\n status: {\n fg: Colors.fg.status,\n bg: Colors.bg.main,\n },\n\n /** Metadata panel */\n metadata: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Metadata label */\n metadataLabel: {\n fg: Colors.fg.muted,\n bg: Colors.bg.main,\n },\n\n /** Metadata value */\n metadataValue: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Error message */\n error: {\n fg: Colors.fg.error,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Dialog box */\n dialog: {\n fg: Colors.fg.normal,\n bg: Colors.bg.dialog,\n border: {\n type: \"line\" as const,\n fg: Colors.bg.dialogBorder,\n bg: Colors.bg.dialog,\n },\n },\n} as const;\n\n/**\n * Icons for different entry types\n * Using ASCII characters for better terminal compatibility\n */\nexport const Icons = {\n up: \"[D]\",\n directory: \"[D]\",\n file: \" \",\n exec: \"[X]\",\n link: \"[L]\",\n selected: \">\",\n unselected: \" \",\n} as const;\n\n/**\n * UI symbols\n * Using ASCII characters for better terminal compatibility\n */\nexport const Symbols = {\n folder: \"[D]\",\n error: \"X\",\n success: \"OK\",\n scrollbar: \"#\",\n loading: \"...\",\n} as const;\n\n/**\n * Box drawing characters\n */\nexport const BoxChars = {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n leftT: \"├\",\n rightT: \"┤\",\n topT: \"┬\",\n bottomT: \"┴\",\n cross: \"┼\",\n} as const;\n\n/**\n * Get style for entry based on type and selection state\n */\nexport function getEntryStyle(\n type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\",\n selected: boolean,\n): { fg: string; bg: string; bold?: boolean } {\n if (selected) {\n if (type === \"directory\" || type === \"up\") {\n return Styles.directorySelected;\n }\n return Styles.listItemSelected;\n }\n\n switch (type) {\n case \"directory\":\n case \"up\":\n return Styles.directory;\n case \"exec\":\n return Styles.exec;\n default:\n return Styles.file;\n }\n}\n\n/**\n * Get icon for entry type\n */\nexport function getEntryIcon(type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\"): string {\n return Icons[type] || Icons.file;\n}\n\n/**\n * Format file size for display\n */\nexport function formatSize(bytes: number | undefined): string {\n if (bytes === undefined) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;\n}\n\n/**\n * Format date for display\n */\nexport function formatDate(date: Date | undefined): string {\n if (!date) return \"\";\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\n\n if (days === 0) {\n return date.toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" });\n }\n if (days < 7) {\n return `${days}d ago`;\n }\n return date.toLocaleDateString([], { month: \"short\", day: \"numeric\" });\n}\n\n/**\n * Format an entry line for display (not used in blessed components)\n */\nexport function formatEntryLine(\n name: string,\n type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\",\n size?: number,\n date?: Date,\n): string {\n const icon = getEntryIcon(type);\n const sizeStr = formatSize(size);\n const dateStr = formatDate(date);\n return `${icon} ${name} ${sizeStr} ${dateStr}`;\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,SAAS;CAEpB,IAAI;EACF,MAAM;EACN,UAAU;EACV,aAAa;EACb,QAAQ;EACR,cAAc;EACf;CAGD,IAAI;EACF,QAAQ;EACR,UAAU;EACV,OAAO;EACP,QAAQ;EACR,WAAW;EACX,MAAM;EACN,MAAM;EACN,MAAM;EACN,IAAI;EACJ,MAAM;EACN,MAAM;EACN,kBAAkB;EAClB,iBAAiB;EACjB,QAAQ;EACR,OAAO;EACP,OAAO;EACR;CACF;;;;AAKD,MAAa,SAAS;CAEpB,QAAQ,EACN,IAAI,OAAO,GAAG,MACf;CAGD,QAAQ;EACN,MAAM;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,OAAO;EACL,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,UAAU;EACR,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,kBAAkB;EAChB,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,WAAW;EACT,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,mBAAmB;EACjB,IAAI;EACJ,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,MAAM;EACJ,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,MAAM;EACJ,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,kBAAkB;EAChB,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,iBAAiB;EACf,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,QAAQ;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,UAAU;EACR,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,eAAe;EACb,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,eAAe;EACb,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,OAAO;EACL,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,QAAQ;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,QAAQ;GACN,MAAM;GACN,IAAI,OAAO,GAAG;GACd,IAAI,OAAO,GAAG;GACf;EACF;CACF;;;;;AAMD,MAAa,QAAQ;CACnB,IAAI;CACJ,WAAW;CACX,MAAM;CACN,MAAM;CACN,MAAM;CACN,UAAU;CACV,YAAY;CACb;;;;;AAMD,MAAa,UAAU;CACrB,QAAQ;CACR,OAAO;CACP,SAAS;CACT,WAAW;CACX,SAAS;CACV;;;;AAsDD,SAAgB,WAAW,OAAmC;AAC5D,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,QAAQ,KAAM,QAAO,GAAG,MAAM;AAClC,KAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC7D,KAAI,QAAQ,OAAO,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;AAC7E,QAAO,IAAI,SAAS,OAAO,OAAO,OAAO,QAAQ,EAAE,CAAC"}
|