@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.
Files changed (70) hide show
  1. package/README.md +224 -31
  2. package/dist/cli.cjs +119 -27
  3. package/dist/cli.mjs +119 -27
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/commands/ls.cjs +6 -4
  6. package/dist/commands/ls.mjs +7 -4
  7. package/dist/commands/ls.mjs.map +1 -1
  8. package/dist/commands/mount.cjs +39 -15
  9. package/dist/commands/mount.mjs +39 -15
  10. package/dist/commands/mount.mjs.map +1 -1
  11. package/dist/commands/serve.cjs +9 -6
  12. package/dist/commands/serve.mjs +9 -6
  13. package/dist/commands/serve.mjs.map +1 -1
  14. package/dist/config/loader.cjs +30 -10
  15. package/dist/config/loader.mjs +30 -10
  16. package/dist/config/loader.mjs.map +1 -1
  17. package/dist/config/provider-factory.cjs +1 -0
  18. package/dist/config/provider-factory.mjs +1 -0
  19. package/dist/config/provider-factory.mjs.map +1 -1
  20. package/dist/config/schema.cjs +58 -3
  21. package/dist/config/schema.mjs +57 -3
  22. package/dist/config/schema.mjs.map +1 -1
  23. package/dist/explorer/actions.cjs +246 -0
  24. package/dist/explorer/actions.mjs +240 -0
  25. package/dist/explorer/actions.mjs.map +1 -0
  26. package/dist/explorer/components/dialog.cjs +231 -0
  27. package/dist/explorer/components/dialog.mjs +232 -0
  28. package/dist/explorer/components/dialog.mjs.map +1 -0
  29. package/dist/explorer/components/file-list.cjs +107 -0
  30. package/dist/explorer/components/file-list.mjs +107 -0
  31. package/dist/explorer/components/file-list.mjs.map +1 -0
  32. package/dist/explorer/components/function-bar.cjs +55 -0
  33. package/dist/explorer/components/function-bar.mjs +55 -0
  34. package/dist/explorer/components/function-bar.mjs.map +1 -0
  35. package/dist/explorer/components/index.cjs +5 -0
  36. package/dist/explorer/components/index.mjs +7 -0
  37. package/dist/explorer/components/metadata-panel.cjs +122 -0
  38. package/dist/explorer/components/metadata-panel.mjs +122 -0
  39. package/dist/explorer/components/metadata-panel.mjs.map +1 -0
  40. package/dist/explorer/components/status-bar.cjs +53 -0
  41. package/dist/explorer/components/status-bar.mjs +54 -0
  42. package/dist/explorer/components/status-bar.mjs.map +1 -0
  43. package/dist/explorer/keybindings.cjs +214 -0
  44. package/dist/explorer/keybindings.mjs +213 -0
  45. package/dist/explorer/keybindings.mjs.map +1 -0
  46. package/dist/explorer/screen.cjs +200 -0
  47. package/dist/explorer/screen.mjs +199 -0
  48. package/dist/explorer/screen.mjs.map +1 -0
  49. package/dist/explorer/state.cjs +53 -0
  50. package/dist/explorer/state.mjs +53 -0
  51. package/dist/explorer/state.mjs.map +1 -0
  52. package/dist/explorer/theme.cjs +158 -0
  53. package/dist/explorer/theme.mjs +155 -0
  54. package/dist/explorer/theme.mjs.map +1 -0
  55. package/dist/path-utils.cjs +104 -0
  56. package/dist/path-utils.mjs +104 -0
  57. package/dist/path-utils.mjs.map +1 -0
  58. package/dist/runtime.cjs +47 -33
  59. package/dist/runtime.mjs +47 -33
  60. package/dist/runtime.mjs.map +1 -1
  61. package/dist/ui/header.cjs +60 -0
  62. package/dist/ui/header.mjs +59 -0
  63. package/dist/ui/header.mjs.map +1 -0
  64. package/dist/ui/index.cjs +17 -0
  65. package/dist/ui/index.mjs +15 -0
  66. package/dist/ui/index.mjs.map +1 -0
  67. package/dist/ui/terminal.cjs +97 -0
  68. package/dist/ui/terminal.mjs +95 -0
  69. package/dist/ui/terminal.mjs.map +1 -0
  70. 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"}