@mak-98/dirvi-cli 0.4.0 → 0.6.0
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/application/effect-to-action.d.ts +5 -7
- package/dist/application/effect-to-action.d.ts.map +1 -1
- package/dist/application/effect-to-action.js +83 -86
- package/dist/application/reducer-action.d.ts +23 -23
- package/dist/application/reducer-action.d.ts.map +1 -1
- package/dist/application/reducer.d.ts +4 -3
- package/dist/application/reducer.d.ts.map +1 -1
- package/dist/application/reducer.js +67 -42
- package/dist/domain/app-api.d.ts +47 -0
- package/dist/domain/app-api.d.ts.map +1 -0
- package/dist/domain/app-api.js +21 -0
- package/dist/domain/event-message.d.ts +12 -15
- package/dist/domain/event-message.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -45
- package/dist/infrastructure/get-dir-entries.d.ts +3 -0
- package/dist/infrastructure/get-dir-entries.d.ts.map +1 -0
- package/dist/infrastructure/get-dir-entries.js +33 -0
- package/dist/infrastructure/get-dir-lazy-entries.d.ts +3 -0
- package/dist/infrastructure/get-dir-lazy-entries.d.ts.map +1 -0
- package/dist/infrastructure/get-dir-lazy-entries.js +33 -0
- package/dist/infrastructure/get-unix-abs-path.d.ts +3 -0
- package/dist/infrastructure/get-unix-abs-path.d.ts.map +1 -0
- package/dist/infrastructure/get-unix-abs-path.js +14 -0
- package/dist/infrastructure/ink-input-to-user-input.d.ts +2 -5
- package/dist/infrastructure/ink-input-to-user-input.js +42 -42
- package/dist/infrastructure/load-posix-app-api.d.ts +4 -0
- package/dist/infrastructure/load-posix-app-api.d.ts.map +1 -0
- package/dist/infrastructure/load-posix-app-api.js +82 -0
- package/dist/ui/App.d.ts +16 -15
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +124 -129
- package/dist/ui/AppShell.d.ts +6 -16
- package/dist/ui/AppShell.d.ts.map +1 -1
- package/dist/ui/AppShell.js +10 -13
- package/dist/ui/LoadingApp.d.ts +11 -0
- package/dist/ui/LoadingApp.d.ts.map +1 -0
- package/dist/ui/LoadingApp.js +67 -0
- package/dist/ui/components/StatusBar.d.ts +3 -5
- package/dist/ui/components/StatusBar.d.ts.map +1 -1
- package/dist/ui/components/StatusBar.js +3 -28
- package/dist/ui/components/ViewRowComponent.d.ts +4 -6
- package/dist/ui/components/ViewRowComponent.js +10 -30
- package/dist/ui/hooks/useView.d.ts +4 -7
- package/dist/ui/hooks/useView.d.ts.map +1 -1
- package/dist/ui/hooks/useView.js +20 -29
- package/dist/ui/view.d.ts +13 -17
- package/dist/ui/view.d.ts.map +1 -1
- package/dist/ui/view.js +84 -64
- package/package.json +2 -2
- package/dist/application/action.d.ts +0 -26
- package/dist/application/action.d.ts.map +0 -1
- package/dist/application/action.js +0 -1
- package/dist/application/dir-buffer-helpers.d.ts +0 -6
- package/dist/application/dir-buffer-helpers.d.ts.map +0 -1
- package/dist/application/dir-buffer-helpers.js +0 -21
- package/dist/application/display-rows.d.ts +0 -11
- package/dist/application/display-rows.d.ts.map +0 -1
- package/dist/application/display-rows.js +0 -63
- package/dist/application/fold-helpers.d.ts +0 -24
- package/dist/application/fold-helpers.d.ts.map +0 -1
- package/dist/application/fold-helpers.js +0 -47
- package/dist/application/intent-to-effect.d.ts +0 -59
- package/dist/application/intent-to-effect.d.ts.map +0 -1
- package/dist/application/intent-to-effect.js +0 -131
- package/dist/application/user-input-to-intent.d.ts +0 -39
- package/dist/application/user-input-to-intent.d.ts.map +0 -1
- package/dist/application/user-input-to-intent.js +0 -110
- package/dist/domain/print-message.d.ts +0 -11
- package/dist/domain/print-message.d.ts.map +0 -1
- package/dist/domain/print-message.js +0 -1
- package/dist/infrastructure/input.d.ts +0 -12
- package/dist/infrastructure/input.d.ts.map +0 -1
- package/dist/infrastructure/input.js +0 -81
- package/dist/infrastructure/load-initial-props.d.ts +0 -3
- package/dist/infrastructure/load-initial-props.d.ts.map +0 -1
- package/dist/infrastructure/load-initial-props.js +0 -36
- package/dist/infrastructure/user-input.d.ts +0 -32
- package/dist/infrastructure/user-input.d.ts.map +0 -1
- package/dist/infrastructure/user-input.js +0 -45
- package/dist/ui/App2.d.ts +0 -16
- package/dist/ui/App2.d.ts.map +0 -1
- package/dist/ui/App2.js +0 -107
- package/dist/ui/components/DirEntries.d.ts +0 -11
- package/dist/ui/components/DirEntries.d.ts.map +0 -1
- package/dist/ui/components/DirEntries.js +0 -64
- package/dist/ui/components/UnixEntry.d.ts +0 -9
- package/dist/ui/components/UnixEntry.d.ts.map +0 -1
- package/dist/ui/components/UnixEntry.js +0 -19
- package/dist/ui/components/UnixEntryComponent.d.ts +0 -9
- package/dist/ui/components/UnixEntryComponent.d.ts.map +0 -1
- package/dist/ui/components/UnixEntryComponent.js +0 -19
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readdir, readlink } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { UnixPathSchema, PosixNameSchema } from 'dirvi-lib';
|
|
4
|
+
export async function getDirEntries(address) {
|
|
5
|
+
const directoryEntries = await readdir(address, {
|
|
6
|
+
withFileTypes: true,
|
|
7
|
+
});
|
|
8
|
+
return Promise.all(directoryEntries.map(async (directoryEntry) => {
|
|
9
|
+
const id = PosixNameSchema.parse(directoryEntry.name);
|
|
10
|
+
if (directoryEntry.isSymbolicLink()) {
|
|
11
|
+
const target = UnixPathSchema.parse(await readlink(join(address, directoryEntry.name)));
|
|
12
|
+
return {
|
|
13
|
+
kind: 'symlink',
|
|
14
|
+
id,
|
|
15
|
+
target,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (directoryEntry.isDirectory()) {
|
|
19
|
+
return {
|
|
20
|
+
kind: 'directory',
|
|
21
|
+
id: id,
|
|
22
|
+
children: null, // Directory not expanded.
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (directoryEntry.isFile()) {
|
|
26
|
+
return {
|
|
27
|
+
kind: 'file',
|
|
28
|
+
id: id,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Unsupported filesystem entry "${join(address, directoryEntry.name)}"`);
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-dir-lazy-entries.d.ts","sourceRoot":"","sources":["../../src/infrastructure/get-dir-lazy-entries.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG7D,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,SAAS,EAAE,CAAC,CAyCtB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readdir, readlink } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { UnixPathSchema, PosixNameSchema } from 'dirvi-lib';
|
|
4
|
+
export async function getDirLazyEntries(address) {
|
|
5
|
+
const directoryEntries = await readdir(address, {
|
|
6
|
+
withFileTypes: true,
|
|
7
|
+
});
|
|
8
|
+
return Promise.all(directoryEntries.map(async (directoryEntry) => {
|
|
9
|
+
const name = PosixNameSchema.parse(directoryEntry.name);
|
|
10
|
+
if (directoryEntry.isSymbolicLink()) {
|
|
11
|
+
const target = UnixPathSchema.parse(await readlink(join(address, directoryEntry.name)));
|
|
12
|
+
return {
|
|
13
|
+
kind: 'symlink',
|
|
14
|
+
name,
|
|
15
|
+
target,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (directoryEntry.isDirectory()) {
|
|
19
|
+
return {
|
|
20
|
+
kind: 'directory',
|
|
21
|
+
name,
|
|
22
|
+
branches: null, // Directory not expanded.
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (directoryEntry.isFile()) {
|
|
26
|
+
return {
|
|
27
|
+
kind: 'file',
|
|
28
|
+
name,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Unsupported filesystem entry "${join(address, directoryEntry.name)}"`);
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-unix-abs-path.d.ts","sourceRoot":"","sources":["../../src/infrastructure/get-unix-abs-path.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGlD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAgBlE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { UnixAbsolutePathSchema } from 'dirvi-lib';
|
|
3
|
+
export function getUnixAbsPath(directory) {
|
|
4
|
+
const absolutePath = resolve(directory);
|
|
5
|
+
const result = UnixAbsolutePathSchema.safeParse(absolutePath);
|
|
6
|
+
if (!result.success) {
|
|
7
|
+
const details = result.error.issues
|
|
8
|
+
.map((issue) => issue.message)
|
|
9
|
+
.join('; ');
|
|
10
|
+
throw new Error(`Bad path for directory "${directory}" ` +
|
|
11
|
+
`(resolved to "${absolutePath}"): ${details}`);
|
|
12
|
+
}
|
|
13
|
+
return result.data;
|
|
14
|
+
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import type { Key } from 'ink';
|
|
2
2
|
import { UserInput } from 'dirvi-lib';
|
|
3
|
-
export declare function inkInputToUserInput(
|
|
4
|
-
|
|
5
|
-
key: Key,
|
|
6
|
-
): UserInput | undefined;
|
|
7
|
-
//# sourceMappingURL=ink-input-to-user-input.d.ts.map
|
|
3
|
+
export declare function inkInputToUserInput(input: string, key: Key): UserInput | undefined;
|
|
4
|
+
//# sourceMappingURL=ink-input-to-user-input.d.ts.map
|
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
// Interprets raw user input to typed UserInput.
|
|
2
2
|
export function inkInputToUserInput(input, key) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
3
|
+
if (key.escape) {
|
|
4
|
+
return {
|
|
5
|
+
userInputType: 'esc',
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
if (key.return) {
|
|
9
|
+
return {
|
|
10
|
+
userInputType: 'enter',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (key.backspace) {
|
|
14
|
+
return {
|
|
15
|
+
userInputType: 'backspace',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (key.rightArrow) {
|
|
19
|
+
return {
|
|
20
|
+
userInputType: 'rightArrow',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (key.leftArrow) {
|
|
24
|
+
return {
|
|
25
|
+
userInputType: 'leftArrow',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (key.upArrow) {
|
|
29
|
+
return {
|
|
30
|
+
userInputType: 'upArrow',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (key.downArrow) {
|
|
34
|
+
return {
|
|
35
|
+
userInputType: 'downArrow',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (input !== '') {
|
|
39
|
+
return {
|
|
40
|
+
userInputType: 'character',
|
|
41
|
+
string: input,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
45
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-posix-app-api.d.ts","sourceRoot":"","sources":["../../src/infrastructure/load-posix-app-api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAI7D,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAkB9B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { watch } from 'node:fs';
|
|
3
|
+
import { createAppApis } from '../domain/app-api.js';
|
|
4
|
+
import { getUnixAbsPath } from './get-unix-abs-path.js';
|
|
5
|
+
import { getDirEntries } from './get-dir-entries.js';
|
|
6
|
+
export function loadPosixAppApi(directory) {
|
|
7
|
+
const unixAbsPath = getUnixAbsPath(directory ?? process.cwd());
|
|
8
|
+
const apis = createAppApis();
|
|
9
|
+
const subscribeToResync = createFsResyncSubscription(join(unixAbsPath));
|
|
10
|
+
return {
|
|
11
|
+
name: `Posix(${unixAbsPath})`,
|
|
12
|
+
emptyForestMessage: 'The directory is empty.',
|
|
13
|
+
loadBranches: (path) => {
|
|
14
|
+
const address = join(unixAbsPath, ...path);
|
|
15
|
+
return getDirEntries(address);
|
|
16
|
+
},
|
|
17
|
+
subscribeToResync,
|
|
18
|
+
...apis,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// `recursive: true` is supported by macOS and Windows, but
|
|
22
|
+
// recursive watching is not supported on all Node/POSIX
|
|
23
|
+
// platforms: Linux unsupported.
|
|
24
|
+
function createFsResyncSubscription(directory) {
|
|
25
|
+
const listeners = new Set();
|
|
26
|
+
let watcher;
|
|
27
|
+
let notificationTimer;
|
|
28
|
+
function notifyListeners() {
|
|
29
|
+
for (const listener of listeners) {
|
|
30
|
+
listener();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function scheduleNotification() {
|
|
34
|
+
// fs.watch can emit several events for one filesystem operation.
|
|
35
|
+
// Debounce them into a single resync request.
|
|
36
|
+
if (notificationTimer !== undefined) {
|
|
37
|
+
clearTimeout(notificationTimer);
|
|
38
|
+
}
|
|
39
|
+
notificationTimer = setTimeout(() => {
|
|
40
|
+
notificationTimer = undefined;
|
|
41
|
+
if (listeners.size > 0) {
|
|
42
|
+
notifyListeners();
|
|
43
|
+
}
|
|
44
|
+
}, 100);
|
|
45
|
+
}
|
|
46
|
+
function startWatcher() {
|
|
47
|
+
if (watcher !== undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
watcher = watch(directory, {
|
|
51
|
+
recursive: true,
|
|
52
|
+
}, (_eventType, _filename) => {
|
|
53
|
+
scheduleNotification();
|
|
54
|
+
});
|
|
55
|
+
watcher.on('error', () => {
|
|
56
|
+
// The next loadBranches call will report the relevant error.
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function stopWatcher() {
|
|
60
|
+
if (notificationTimer !== undefined) {
|
|
61
|
+
clearTimeout(notificationTimer);
|
|
62
|
+
notificationTimer = undefined;
|
|
63
|
+
}
|
|
64
|
+
watcher?.close();
|
|
65
|
+
watcher = undefined;
|
|
66
|
+
}
|
|
67
|
+
return (listener) => {
|
|
68
|
+
listeners.add(listener);
|
|
69
|
+
startWatcher();
|
|
70
|
+
let subscribed = true;
|
|
71
|
+
return () => {
|
|
72
|
+
if (!subscribed) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
subscribed = false;
|
|
76
|
+
listeners.delete(listener);
|
|
77
|
+
if (listeners.size === 0) {
|
|
78
|
+
stopWatcher();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
}
|
package/dist/ui/App.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { State } from 'dirvi-lib';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { IntentToEffect, SerializableKey, State, TreeNode } from 'dirvi-lib';
|
|
2
|
+
import { EffectToAction } from '../application/effect-to-action.js';
|
|
3
|
+
import { AppApi } from '../domain/app-api.js';
|
|
4
|
+
import { Reducer } from '../application/reducer.js';
|
|
5
|
+
type AppProps<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = {
|
|
6
|
+
appApi: AppApi<Id, BufferNode>;
|
|
7
|
+
initialState: State<Id, BufferNode>;
|
|
8
|
+
reducer: Reducer<Id, BufferNode>;
|
|
9
|
+
intentToEffect: IntentToEffect<Id, BufferNode>;
|
|
10
|
+
effectToAction: EffectToAction<Id, BufferNode>;
|
|
11
|
+
stdout?: (message: string) => void;
|
|
12
|
+
clipboard?: (value: string) => void;
|
|
13
|
+
onError?: (error: Error) => void;
|
|
9
14
|
};
|
|
10
|
-
export declare function App({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
print,
|
|
14
|
-
onError,
|
|
15
|
-
}: AppProps): import('react').JSX.Element | null;
|
|
16
|
-
//# sourceMappingURL=App.d.ts.map
|
|
15
|
+
export declare function App<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>({ appApi, initialState, reducer, intentToEffect, effectToAction, stdout, clipboard, onError, }: AppProps<Id, BufferNode>): import("react").JSX.Element | null;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=App.d.ts.map
|
package/dist/ui/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,cAAc,EACd,eAAe,EACf,KAAK,EACL,QAAQ,EAET,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAGpE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAGpD,KAAK,QAAQ,CACX,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IACzC;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/B,YAAY,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/C,cAAc,EAAE,cAAc,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,wBAAgB,GAAG,CACjB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,EACA,MAAM,EACN,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,MAAM,EACe,SAAS,EAC9B,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,sCA0K1B"}
|
package/dist/ui/App.js
CHANGED
|
@@ -1,139 +1,134 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from
|
|
2
|
-
import { join } from 'node:path';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { Box, Text, useApp, useInput, useWindowSize } from 'ink';
|
|
4
|
-
import { useEffect, useMemo, useReducer, useState } from 'react';
|
|
5
|
-
import {
|
|
6
|
-
getDirLazyEntries,
|
|
7
|
-
intentToEffect,
|
|
8
|
-
NavigationNode,
|
|
9
|
-
userInputToIntent,
|
|
10
|
-
} from 'dirvi-lib';
|
|
11
|
-
import { useView } from './hooks/useView.js';
|
|
12
|
-
import { reducer } from '../application/reducer.js';
|
|
3
|
+
import { useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
4
|
+
import { userInputToIntent, } from 'dirvi-lib';
|
|
13
5
|
import { ViewRowComponent } from './components/ViewRowComponent.js';
|
|
14
|
-
import { effectToAction } from '../application/effect-to-action.js';
|
|
15
6
|
import { StatusBar } from './components/StatusBar.js';
|
|
16
7
|
import { inkInputToUserInput } from '../infrastructure/ink-input-to-user-input.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const { rows: terminalRows } = useWindowSize();
|
|
28
|
-
const view = useView(navigation, state, terminalRows);
|
|
29
|
-
const [exitStatus, setExitStatus] = useState();
|
|
30
|
-
// Print view on changes
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
print?.({ type: 'view', view: state });
|
|
33
|
-
const visibleFilesPaths = NavigationNode.visibleFilesPaths(navigation).map(
|
|
34
|
-
(path) => join(...path),
|
|
35
|
-
);
|
|
36
|
-
print?.({
|
|
37
|
-
type: 'displayed-files-paths',
|
|
38
|
-
paths: visibleFilesPaths,
|
|
8
|
+
import { useView } from './hooks/useView.js';
|
|
9
|
+
export function App({ appApi, initialState, reducer, intentToEffect, effectToAction, stdout, clipboard, onError, }) {
|
|
10
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
11
|
+
// Give `subscribeToResync` callback a way to see current state:
|
|
12
|
+
const stateRef = useRef(state);
|
|
13
|
+
stateRef.current = state;
|
|
14
|
+
const navigation = useMemo(() => appApi.navNodeApi.from(state.buffer, state.foldNode), [appApi.navNodeApi, state.buffer, state.foldNode]);
|
|
15
|
+
const [inputState, setInputState] = useState({
|
|
16
|
+
inputMode: 'normal',
|
|
17
|
+
normalBuffer: '',
|
|
39
18
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.then((entries) => {
|
|
65
|
-
dispatch({
|
|
66
|
-
kind: 'updateDir',
|
|
67
|
-
path: effect.path,
|
|
68
|
-
entries,
|
|
19
|
+
const { rows: terminalRows } = useWindowSize();
|
|
20
|
+
const view = useView(navigation, state, appApi.cursorApi, terminalRows);
|
|
21
|
+
const [exitStatus, setExitStatus] = useState();
|
|
22
|
+
// Subscribe to directory watcher, do not
|
|
23
|
+
// resubscribe on every state change.
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let active = true;
|
|
26
|
+
const unsubscribe = appApi.subscribeToResync(() => {
|
|
27
|
+
const oldState = stateRef.current;
|
|
28
|
+
void appApi.stateApi
|
|
29
|
+
.resync(oldState, appApi.loadBranches)
|
|
30
|
+
.then((newState) => {
|
|
31
|
+
if (!active) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
dispatch({
|
|
35
|
+
kind: 'setState',
|
|
36
|
+
oldState: oldState,
|
|
37
|
+
newState: newState,
|
|
38
|
+
});
|
|
39
|
+
})
|
|
40
|
+
.catch((error) => {
|
|
41
|
+
const appError = error instanceof Error ? error : new Error(String(error));
|
|
42
|
+
onError?.(appError);
|
|
69
43
|
});
|
|
70
|
-
})
|
|
71
|
-
.catch((error) => {
|
|
72
|
-
const appError =
|
|
73
|
-
error instanceof Error ? error : new Error(String(error));
|
|
74
|
-
onError?.(appError);
|
|
75
|
-
setExitStatus(`Unable to open directory: ${appError.message}`);
|
|
76
|
-
});
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
case 'printFile':
|
|
80
|
-
print?.({
|
|
81
|
-
type: 'file',
|
|
82
|
-
path: effect.path,
|
|
83
44
|
});
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
45
|
+
return () => {
|
|
46
|
+
active = false;
|
|
47
|
+
unsubscribe();
|
|
48
|
+
};
|
|
49
|
+
}, [appApi, onError]);
|
|
50
|
+
function executeEffect(effect) {
|
|
51
|
+
if (effect === undefined) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
switch (effect.effectType) {
|
|
55
|
+
case 'dispatchEffectAction':
|
|
56
|
+
const action = effectToAction(effect.action, navigation, state);
|
|
57
|
+
if (action === undefined) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
dispatch(action);
|
|
61
|
+
setInputState({
|
|
62
|
+
inputMode: 'normal',
|
|
63
|
+
normalBuffer: '',
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
case 'setInputState':
|
|
67
|
+
setInputState(effect.inputState);
|
|
68
|
+
return;
|
|
69
|
+
case 'loadBranchEntries': {
|
|
70
|
+
void appApi
|
|
71
|
+
.loadBranches(effect.path)
|
|
72
|
+
.then((entries) => {
|
|
73
|
+
dispatch({
|
|
74
|
+
kind: 'updateBranch',
|
|
75
|
+
path: effect.path,
|
|
76
|
+
entries,
|
|
77
|
+
});
|
|
78
|
+
})
|
|
79
|
+
.catch((error) => {
|
|
80
|
+
const appError = error instanceof Error ? error : new Error(String(error));
|
|
81
|
+
onError?.(appError);
|
|
82
|
+
setExitStatus(`Unable to open directory: ${appError.message}`);
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
case 'emitVisibleLeavesPaths':
|
|
87
|
+
const visibleLeavesPaths = appApi.navNodeApi.visibleLeavesPaths(navigation);
|
|
88
|
+
stdout?.(JSON.stringify({
|
|
89
|
+
type: 'displayed-leaves-paths',
|
|
90
|
+
paths: visibleLeavesPaths,
|
|
91
|
+
}));
|
|
92
|
+
setInputState({
|
|
93
|
+
inputMode: 'normal',
|
|
94
|
+
normalBuffer: '',
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
case 'quit':
|
|
98
|
+
setExitStatus(effect.exitMessage);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
111
101
|
}
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
// --- Input Hook ---
|
|
103
|
+
useInput((input, key) => {
|
|
104
|
+
const userInput = inkInputToUserInput(input, key);
|
|
105
|
+
if (userInput === undefined) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const intent = userInputToIntent(userInput, inputState);
|
|
109
|
+
if (intent === undefined) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const effectResult = intentToEffect(intent, state);
|
|
113
|
+
if (effectResult === undefined) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
executeEffect(effectResult);
|
|
117
|
+
});
|
|
118
|
+
// --- Exit Logic ---
|
|
119
|
+
const { exit } = useApp();
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (exitStatus === undefined) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (exitStatus !== '') {
|
|
125
|
+
onError?.(new Error(exitStatus));
|
|
126
|
+
}
|
|
127
|
+
exit();
|
|
128
|
+
}, [exitStatus, exit, onError]);
|
|
129
|
+
if (exitStatus !== undefined) {
|
|
130
|
+
return null;
|
|
114
131
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (exitStatus !== undefined) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
// --- JSX ---
|
|
121
|
-
return _jsxs(Box, {
|
|
122
|
-
flexDirection: 'column',
|
|
123
|
-
height: terminalRows,
|
|
124
|
-
children: [
|
|
125
|
-
_jsx(Box, {
|
|
126
|
-
flexDirection: 'column',
|
|
127
|
-
flexGrow: 1,
|
|
128
|
-
flexShrink: 1,
|
|
129
|
-
children:
|
|
130
|
-
view.rows.length === 0
|
|
131
|
-
? _jsx(Text, { dimColor: true, children: 'Directory is empty.' })
|
|
132
|
-
: view.rows.map((row) =>
|
|
133
|
-
_jsx(ViewRowComponent, { row: row }, row.id),
|
|
134
|
-
),
|
|
135
|
-
}),
|
|
136
|
-
_jsx(StatusBar, { inputState: inputState }),
|
|
137
|
-
],
|
|
138
|
-
});
|
|
132
|
+
// --- JSX ---
|
|
133
|
+
return (_jsxs(Box, { flexDirection: "column", height: terminalRows, children: [_jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, children: view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "Empty." })) : (view.rows.map((row) => _jsx(ViewRowComponent, { row: row }, row.id))) }), _jsx(StatusBar, { inputState: inputState })] }));
|
|
139
134
|
}
|
package/dist/ui/AppShell.d.ts
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import { UnixAbsolutePath, State } from 'dirvi-lib';
|
|
2
|
-
import { EventMessage } from '../domain/event-message.js';
|
|
3
|
-
export type EmptyView = {
|
|
4
|
-
cursor: undefined;
|
|
5
|
-
};
|
|
6
1
|
export type ShellAppProps = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
directory?: string;
|
|
3
|
+
stdout?: (message: string) => void;
|
|
4
|
+
clipboard?: (value: string) => void;
|
|
5
|
+
onError?: (error: Error) => void;
|
|
11
6
|
};
|
|
12
|
-
export declare function AppShell({
|
|
13
|
-
|
|
14
|
-
initialState,
|
|
15
|
-
print,
|
|
16
|
-
onError,
|
|
17
|
-
}: ShellAppProps): import('react').JSX.Element;
|
|
18
|
-
//# sourceMappingURL=AppShell.d.ts.map
|
|
7
|
+
export declare function AppShell({ directory, stdout, clipboard, onError }: ShellAppProps): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=AppShell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/ui/AppShell.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/ui/AppShell.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAIF,wBAAgB,QAAQ,CAAC,EACtB,SAAS,EACT,MAAM,EACN,SAAS,EACT,OAAO,EACT,EAAE,aAAa,+BAYf"}
|