@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.
Files changed (92) hide show
  1. package/dist/application/effect-to-action.d.ts +5 -7
  2. package/dist/application/effect-to-action.d.ts.map +1 -1
  3. package/dist/application/effect-to-action.js +83 -86
  4. package/dist/application/reducer-action.d.ts +23 -23
  5. package/dist/application/reducer-action.d.ts.map +1 -1
  6. package/dist/application/reducer.d.ts +4 -3
  7. package/dist/application/reducer.d.ts.map +1 -1
  8. package/dist/application/reducer.js +67 -42
  9. package/dist/domain/app-api.d.ts +47 -0
  10. package/dist/domain/app-api.d.ts.map +1 -0
  11. package/dist/domain/app-api.js +21 -0
  12. package/dist/domain/event-message.d.ts +12 -15
  13. package/dist/domain/event-message.d.ts.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +56 -45
  16. package/dist/infrastructure/get-dir-entries.d.ts +3 -0
  17. package/dist/infrastructure/get-dir-entries.d.ts.map +1 -0
  18. package/dist/infrastructure/get-dir-entries.js +33 -0
  19. package/dist/infrastructure/get-dir-lazy-entries.d.ts +3 -0
  20. package/dist/infrastructure/get-dir-lazy-entries.d.ts.map +1 -0
  21. package/dist/infrastructure/get-dir-lazy-entries.js +33 -0
  22. package/dist/infrastructure/get-unix-abs-path.d.ts +3 -0
  23. package/dist/infrastructure/get-unix-abs-path.d.ts.map +1 -0
  24. package/dist/infrastructure/get-unix-abs-path.js +14 -0
  25. package/dist/infrastructure/ink-input-to-user-input.d.ts +2 -5
  26. package/dist/infrastructure/ink-input-to-user-input.js +42 -42
  27. package/dist/infrastructure/load-posix-app-api.d.ts +4 -0
  28. package/dist/infrastructure/load-posix-app-api.d.ts.map +1 -0
  29. package/dist/infrastructure/load-posix-app-api.js +82 -0
  30. package/dist/ui/App.d.ts +16 -15
  31. package/dist/ui/App.d.ts.map +1 -1
  32. package/dist/ui/App.js +124 -129
  33. package/dist/ui/AppShell.d.ts +6 -16
  34. package/dist/ui/AppShell.d.ts.map +1 -1
  35. package/dist/ui/AppShell.js +10 -13
  36. package/dist/ui/LoadingApp.d.ts +11 -0
  37. package/dist/ui/LoadingApp.d.ts.map +1 -0
  38. package/dist/ui/LoadingApp.js +67 -0
  39. package/dist/ui/components/StatusBar.d.ts +3 -5
  40. package/dist/ui/components/StatusBar.d.ts.map +1 -1
  41. package/dist/ui/components/StatusBar.js +3 -28
  42. package/dist/ui/components/ViewRowComponent.d.ts +4 -6
  43. package/dist/ui/components/ViewRowComponent.js +10 -30
  44. package/dist/ui/hooks/useView.d.ts +4 -7
  45. package/dist/ui/hooks/useView.d.ts.map +1 -1
  46. package/dist/ui/hooks/useView.js +20 -29
  47. package/dist/ui/view.d.ts +13 -17
  48. package/dist/ui/view.d.ts.map +1 -1
  49. package/dist/ui/view.js +84 -64
  50. package/package.json +2 -2
  51. package/dist/application/action.d.ts +0 -26
  52. package/dist/application/action.d.ts.map +0 -1
  53. package/dist/application/action.js +0 -1
  54. package/dist/application/dir-buffer-helpers.d.ts +0 -6
  55. package/dist/application/dir-buffer-helpers.d.ts.map +0 -1
  56. package/dist/application/dir-buffer-helpers.js +0 -21
  57. package/dist/application/display-rows.d.ts +0 -11
  58. package/dist/application/display-rows.d.ts.map +0 -1
  59. package/dist/application/display-rows.js +0 -63
  60. package/dist/application/fold-helpers.d.ts +0 -24
  61. package/dist/application/fold-helpers.d.ts.map +0 -1
  62. package/dist/application/fold-helpers.js +0 -47
  63. package/dist/application/intent-to-effect.d.ts +0 -59
  64. package/dist/application/intent-to-effect.d.ts.map +0 -1
  65. package/dist/application/intent-to-effect.js +0 -131
  66. package/dist/application/user-input-to-intent.d.ts +0 -39
  67. package/dist/application/user-input-to-intent.d.ts.map +0 -1
  68. package/dist/application/user-input-to-intent.js +0 -110
  69. package/dist/domain/print-message.d.ts +0 -11
  70. package/dist/domain/print-message.d.ts.map +0 -1
  71. package/dist/domain/print-message.js +0 -1
  72. package/dist/infrastructure/input.d.ts +0 -12
  73. package/dist/infrastructure/input.d.ts.map +0 -1
  74. package/dist/infrastructure/input.js +0 -81
  75. package/dist/infrastructure/load-initial-props.d.ts +0 -3
  76. package/dist/infrastructure/load-initial-props.d.ts.map +0 -1
  77. package/dist/infrastructure/load-initial-props.js +0 -36
  78. package/dist/infrastructure/user-input.d.ts +0 -32
  79. package/dist/infrastructure/user-input.d.ts.map +0 -1
  80. package/dist/infrastructure/user-input.js +0 -45
  81. package/dist/ui/App2.d.ts +0 -16
  82. package/dist/ui/App2.d.ts.map +0 -1
  83. package/dist/ui/App2.js +0 -107
  84. package/dist/ui/components/DirEntries.d.ts +0 -11
  85. package/dist/ui/components/DirEntries.d.ts.map +0 -1
  86. package/dist/ui/components/DirEntries.js +0 -64
  87. package/dist/ui/components/UnixEntry.d.ts +0 -9
  88. package/dist/ui/components/UnixEntry.d.ts.map +0 -1
  89. package/dist/ui/components/UnixEntry.js +0 -19
  90. package/dist/ui/components/UnixEntryComponent.d.ts +0 -9
  91. package/dist/ui/components/UnixEntryComponent.d.ts.map +0 -1
  92. 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,3 @@
1
+ import type { UnixAbsolutePath, PosixNode } from 'dirvi-lib';
2
+ export declare function getDirLazyEntries(address: UnixAbsolutePath): Promise<PosixNode[]>;
3
+ //# sourceMappingURL=get-dir-lazy-entries.d.ts.map
@@ -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,3 @@
1
+ import type { UnixAbsolutePath } from 'dirvi-lib';
2
+ export declare function getUnixAbsPath(directory: string): UnixAbsolutePath;
3
+ //# sourceMappingURL=get-unix-abs-path.d.ts.map
@@ -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
- input: string,
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
- 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;
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,4 @@
1
+ import { PosixName, PosixNode } from 'dirvi-lib';
2
+ import { AppApi } from '../domain/app-api.js';
3
+ export declare function loadPosixAppApi(directory?: string): AppApi<PosixName, PosixNode>;
4
+ //# sourceMappingURL=load-posix-app-api.d.ts.map
@@ -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 type { UnixAbsolutePath } from 'dirvi-lib';
3
- import { EventMessage } from '../domain/event-message.js';
4
- export type AppProps = {
5
- cwdAddress: UnixAbsolutePath;
6
- initialState: State;
7
- print?: (message: EventMessage) => void;
8
- onError?: (error: Error) => void;
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
- cwdAddress,
12
- initialState,
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
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAIA,OAAO,EAML,KAAK,EAEN,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAIlD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAM1D,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,wBAAgB,GAAG,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,sCA2IzE"}
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 'react/jsx-runtime';
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
- export function App({ cwdAddress, initialState, print, onError }) {
18
- const [state, dispatch] = useReducer(reducer, initialState);
19
- const navigation = useMemo(
20
- () => NavigationNode.from(state.buffer, state.folds),
21
- [state.buffer, state.folds],
22
- );
23
- const [inputState, setInputState] = useState({
24
- inputMode: 'normal',
25
- normalBuffer: '',
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
- }, [state, print, navigation, cwdAddress]);
41
- function executeEffect(effect) {
42
- if (effect === undefined) {
43
- return;
44
- }
45
- switch (effect.effectType) {
46
- case 'dispatchEffectAction':
47
- const action = effectToAction(effect.action, navigation, state);
48
- if (action === undefined) {
49
- return;
50
- }
51
- dispatch(action);
52
- setInputState({
53
- inputMode: 'normal',
54
- normalBuffer: '',
55
- });
56
- return;
57
- case 'setInputState':
58
- console.log('setInputState', effect.inputState);
59
- setInputState(effect.inputState);
60
- return;
61
- case 'loadDir': {
62
- const address = join(cwdAddress, ...effect.path);
63
- void getDirLazyEntries(address)
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
- case 'quit':
86
- setExitStatus(effect.exitMessage);
87
- return;
88
- }
89
- }
90
- // --- Input Hook ---
91
- useInput((input, key) => {
92
- const userInput = inkInputToUserInput(input, key);
93
- if (userInput === undefined) {
94
- return;
95
- }
96
- const intent = userInputToIntent(userInput, inputState);
97
- if (intent === undefined) {
98
- return;
99
- }
100
- const effectResult = intentToEffect(intent, state);
101
- if (effectResult === undefined) {
102
- return;
103
- }
104
- executeEffect(effectResult);
105
- });
106
- // --- Exit Logic ---
107
- const { exit } = useApp();
108
- useEffect(() => {
109
- if (exitStatus === undefined) {
110
- return;
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
- if (exitStatus !== '') {
113
- onError?.(new Error(exitStatus));
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
- exit();
116
- }, [exitStatus, exit, onError]);
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
  }
@@ -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
- cwdAddress: UnixAbsolutePath;
8
- initialState: State | EmptyView;
9
- print?: (message: EventMessage) => void;
10
- onError?: (error: Error) => void;
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
- cwdAddress,
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":"AAEA,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAI1D,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,YAAY,EAAE,KAAK,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,UAAU,EACV,YAAY,EACZ,KAAK,EACL,OAAO,GACR,EAAE,aAAa,+BAaf"}
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"}