@mak-98/dirvi-cli 0.2.0 → 0.3.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 (60) hide show
  1. package/dist/application/action.d.ts +19 -0
  2. package/dist/application/action.d.ts.map +1 -0
  3. package/dist/application/action.js +1 -0
  4. package/dist/application/display-rows.d.ts +3 -3
  5. package/dist/application/display-rows.d.ts.map +1 -1
  6. package/dist/application/display-rows.js +7 -7
  7. package/dist/application/effect-to-action.d.ts +5 -0
  8. package/dist/application/effect-to-action.d.ts.map +1 -0
  9. package/dist/application/effect-to-action.js +79 -0
  10. package/dist/application/fold-helpers.d.ts +2 -2
  11. package/dist/application/fold-helpers.d.ts.map +1 -1
  12. package/dist/application/fold-helpers.js +0 -17
  13. package/dist/application/intent-to-effect.d.ts +39 -0
  14. package/dist/application/intent-to-effect.d.ts.map +1 -0
  15. package/dist/application/intent-to-effect.js +118 -0
  16. package/dist/application/reducer-action.d.ts +19 -0
  17. package/dist/application/reducer-action.d.ts.map +1 -0
  18. package/dist/application/reducer-action.js +1 -0
  19. package/dist/application/reducer.d.ts +3 -28
  20. package/dist/application/reducer.d.ts.map +1 -1
  21. package/dist/application/reducer.js +16 -172
  22. package/dist/application/user-input-to-intent.d.ts +15 -0
  23. package/dist/application/user-input-to-intent.d.ts.map +1 -0
  24. package/dist/application/user-input-to-intent.js +56 -0
  25. package/dist/domain/event-message.d.ts +3 -3
  26. package/dist/domain/event-message.d.ts.map +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/infrastructure/input.d.ts +3 -3
  29. package/dist/infrastructure/input.d.ts.map +1 -1
  30. package/dist/infrastructure/input.js +16 -7
  31. package/dist/infrastructure/load-initial-props.d.ts.map +1 -1
  32. package/dist/infrastructure/load-initial-props.js +6 -8
  33. package/dist/infrastructure/user-input.d.ts +17 -0
  34. package/dist/infrastructure/user-input.d.ts.map +1 -0
  35. package/dist/infrastructure/user-input.js +35 -0
  36. package/dist/ui/App.d.ts +4 -5
  37. package/dist/ui/App.d.ts.map +1 -1
  38. package/dist/ui/App.js +54 -59
  39. package/dist/ui/App2.d.ts +11 -0
  40. package/dist/ui/App2.d.ts.map +1 -0
  41. package/dist/ui/App2.js +96 -0
  42. package/dist/ui/AppShell.d.ts +3 -3
  43. package/dist/ui/AppShell.d.ts.map +1 -1
  44. package/dist/ui/AppShell.js +3 -3
  45. package/dist/ui/components/DirEntries.d.ts +2 -1
  46. package/dist/ui/components/DirEntries.d.ts.map +1 -1
  47. package/dist/ui/components/DirEntries.js +6 -7
  48. package/dist/ui/components/UnixEntryComponent.d.ts +6 -0
  49. package/dist/ui/components/UnixEntryComponent.d.ts.map +1 -0
  50. package/dist/ui/components/UnixEntryComponent.js +12 -0
  51. package/dist/ui/components/ViewRowComponent.d.ts +5 -0
  52. package/dist/ui/components/ViewRowComponent.d.ts.map +1 -0
  53. package/dist/ui/components/ViewRowComponent.js +13 -0
  54. package/dist/ui/hooks/useView.d.ts +4 -0
  55. package/dist/ui/hooks/useView.d.ts.map +1 -0
  56. package/dist/ui/hooks/useView.js +30 -0
  57. package/dist/ui/view.d.ts +18 -0
  58. package/dist/ui/view.d.ts.map +1 -0
  59. package/dist/ui/view.js +74 -0
  60. package/package.json +4 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-input-to-intent.d.ts","sourceRoot":"","sources":["../../src/application/user-input-to-intent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAE5D,MAAM,MAAM,MAAM,GACd;IACE,UAAU,EAAE,eAAe,CAAC;CAC7B,GACD;IACE,UAAU,EAAE,cAAc,CAAC;CAC5B,GACD;IACE,UAAU,EAAE,cAAc,CAAC;CAC5B,GACD;IACE,UAAU,EAAE,YAAY,CAAC;CAC1B,GACD;IACE,UAAU,EAAE,qBAAqB,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAqCN,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,SAAS,CAiCpB"}
@@ -0,0 +1,56 @@
1
+ function characterToIntent(character, commandBuffer) {
2
+ if (commandBuffer === '') {
3
+ switch (character) {
4
+ case 'l':
5
+ return {
6
+ intentType: 'interactRight',
7
+ };
8
+ case 'h':
9
+ return {
10
+ intentType: 'interactLeft',
11
+ };
12
+ case 'j':
13
+ return {
14
+ intentType: 'interactDown',
15
+ };
16
+ case 'k':
17
+ return {
18
+ intentType: 'interactUp',
19
+ };
20
+ }
21
+ }
22
+ return {
23
+ intentType: 'updateCommandBuffer',
24
+ updatedCommandBuffer: commandBuffer + character,
25
+ };
26
+ }
27
+ // Derive the intent from the user input and the commandBuffer.
28
+ export function userInputToIntent(userInput, commandBuffer) {
29
+ if (userInput.userInputType === 'esc') {
30
+ return {
31
+ intentType: 'updateCommandBuffer',
32
+ updatedCommandBuffer: '',
33
+ };
34
+ }
35
+ if (userInput.userInputType === 'character') {
36
+ return characterToIntent(userInput.string, commandBuffer);
37
+ }
38
+ switch (userInput.userInputType) {
39
+ case 'rightArrow':
40
+ return {
41
+ intentType: 'interactRight',
42
+ };
43
+ case 'leftArrow':
44
+ return {
45
+ intentType: 'interactLeft',
46
+ };
47
+ case 'downArrow':
48
+ return {
49
+ intentType: 'interactDown',
50
+ };
51
+ case 'upArrow':
52
+ return {
53
+ intentType: 'interactUp',
54
+ };
55
+ }
56
+ }
@@ -1,12 +1,12 @@
1
- import { EntryPath, View } from 'dirvi-lib';
1
+ import { UnixEntryPath, State } from 'dirvi-lib';
2
2
  export type EventMessage = {
3
3
  type: 'view';
4
- view: View;
4
+ view: State;
5
5
  } | {
6
6
  type: 'displayed-files-paths';
7
7
  paths: string[];
8
8
  } | {
9
9
  type: 'file';
10
- path: EntryPath;
10
+ path: UnixEntryPath;
11
11
  };
12
12
  //# sourceMappingURL=event-message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-message.d.ts","sourceRoot":"","sources":["../../src/domain/event-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,MAAM,YAAY,GACpB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;CACZ,GACD;IACE,IAAI,EAAE,uBAAuB,CAAC;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"event-message.d.ts","sourceRoot":"","sources":["../../src/domain/event-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,MAAM,YAAY,GACpB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;CACb,GACD;IACE,IAAI,EAAE,uBAAuB,CAAC;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;CACrB,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { render } from 'ink';
5
5
  import { AppShell } from './ui/AppShell.js';
6
6
  import { loadInitialProps } from './infrastructure/load-initial-props.js';
7
7
  const program = new Command();
8
- // ---*--- TERMINAL ALT SCREEN ---*---
8
+ // ---*--- TERMINAL ALTERNATE SCREEN ---*---
9
9
  // Restore the terminal during normal cleanup and as a
10
10
  // last-resort fallback when Node is exiting.
11
11
  // Write UI to stderr
@@ -1,7 +1,7 @@
1
1
  import type { Key } from 'ink';
2
- import type { View } from 'dirvi-lib';
3
- import type { Action } from '../application/reducer.js';
2
+ import { State } from 'dirvi-lib';
3
+ import { Action } from '../application/action.js';
4
4
  export type PendingInput = 'z';
5
5
  export type InputResult = Action | PendingInput | undefined;
6
- export declare function inputToInputResult(input: string, key: Key, view: View, pendingInput?: PendingInput): InputResult;
6
+ export declare function userInputToInputResult(input: string, key: Key, view: State, pendingInput?: PendingInput): InputResult;
7
7
  //# sourceMappingURL=input.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/infrastructure/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE/B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC;AAC/B,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;AAE5D,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,YAAY,CAAC,EAAE,YAAY,GAC1B,WAAW,CAsFb"}
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/infrastructure/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE/B,OAAO,EAAU,KAAK,EAAQ,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC;AAC/B,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;AAE5D,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,KAAK,EACX,YAAY,CAAC,EAAE,YAAY,GAC1B,WAAW,CA8Fb"}
@@ -1,4 +1,5 @@
1
- export function inputToInputResult(input, key, view, pendingInput) {
1
+ import { Cursor, View } from 'dirvi-lib';
2
+ export function userInputToInputResult(input, key, view, pendingInput) {
2
3
  // Adding Pending Input
3
4
  if (pendingInput === undefined && input === 'z') {
4
5
  return 'z';
@@ -25,18 +26,26 @@ export function inputToInputResult(input, key, view, pendingInput) {
25
26
  }
26
27
  // Not prefixed inputs
27
28
  if (input === 'l' || key.rightArrow) {
28
- // To do: `L` does recursive opening directories
29
- if (view.cursor.kind !== 'entry') {
29
+ const currentEntry = View.getEntryAtCursor(view);
30
+ if (currentEntry === undefined) {
30
31
  return undefined;
31
32
  }
32
- const path = [...view.cursor.parentPath, view.cursor.entry.name];
33
- if (view.cursor.entry.kind === 'directory') {
33
+ if (currentEntry.kind === 'directory') {
34
+ const path = Cursor.getPath(view.cursor);
35
+ if (path === undefined) {
36
+ return undefined;
37
+ }
34
38
  return {
35
- kind: 'toggleDir',
39
+ kind: 'updateDir',
36
40
  path,
41
+ entries: currentEntry.entries,
37
42
  };
38
43
  }
39
- if (view.cursor.entry.kind === 'file') {
44
+ if (currentEntry.kind === 'file') {
45
+ const path = Cursor.getPath(view.cursor);
46
+ if (path === undefined) {
47
+ return undefined;
48
+ }
40
49
  return {
41
50
  kind: 'printFile',
42
51
  path,
@@ -1 +1 @@
1
- {"version":3,"file":"load-initial-props.d.ts","sourceRoot":"","sources":["../../src/infrastructure/load-initial-props.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAmC/D"}
1
+ {"version":3,"file":"load-initial-props.d.ts","sourceRoot":"","sources":["../../src/infrastructure/load-initial-props.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAiC/D"}
@@ -1,11 +1,11 @@
1
- import { getCwdAbsPath, getDirLazyEntries, unixEntryToDisplayEntry, createEmptyFoldNode, } from 'dirvi-lib';
1
+ import { getCwdAbsPath, getDirLazyEntries, FoldNode, DisplayEntry, } from 'dirvi-lib';
2
2
  export async function loadInitialProps() {
3
3
  const cwdAddress = getCwdAbsPath();
4
4
  const lazyEntries = await getDirLazyEntries(cwdAddress);
5
5
  if (lazyEntries.length === 0) {
6
6
  return {
7
7
  cwdAddress,
8
- initialView: {
8
+ initialState: {
9
9
  cursor: undefined,
10
10
  },
11
11
  };
@@ -16,16 +16,14 @@ export async function loadInitialProps() {
16
16
  }
17
17
  return {
18
18
  cwdAddress,
19
- initialView: {
20
- buffer: {
21
- entries: lazyEntries,
22
- },
19
+ initialState: {
20
+ buffer: lazyEntries,
23
21
  cursor: {
24
22
  kind: 'entry',
25
23
  parentPath: [],
26
- entry: unixEntryToDisplayEntry(firstEntry),
24
+ entryName: DisplayEntry.fromUnixEntry(firstEntry).name,
27
25
  },
28
- folds: createEmptyFoldNode(),
26
+ folds: FoldNode.createEmpty(),
29
27
  },
30
28
  };
31
29
  }
@@ -0,0 +1,17 @@
1
+ import type { Key } from 'ink';
2
+ export type UserInput = {
3
+ userInputType: 'character';
4
+ string: string;
5
+ } | {
6
+ userInputType: 'esc';
7
+ } | {
8
+ userInputType: 'rightArrow';
9
+ } | {
10
+ userInputType: 'leftArrow';
11
+ } | {
12
+ userInputType: 'downArrow';
13
+ } | {
14
+ userInputType: 'upArrow';
15
+ };
16
+ export declare function inkInputToUserInput(input: string, key: Key): UserInput | undefined;
17
+ //# sourceMappingURL=user-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-input.d.ts","sourceRoot":"","sources":["../../src/infrastructure/user-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE/B,MAAM,MAAM,SAAS,GACjB;IACE,aAAa,EAAE,WAAW,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,aAAa,EAAE,KAAK,CAAC;CACtB,GACD;IACE,aAAa,EAAE,YAAY,CAAC;CAC7B,GACD;IACE,aAAa,EAAE,WAAW,CAAC;CAC5B,GACD;IACE,aAAa,EAAE,WAAW,CAAC;CAC5B,GACD;IACE,aAAa,EAAE,SAAS,CAAC;CAC1B,CAAC;AAGN,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,SAAS,GAAG,SAAS,CAuClF"}
@@ -0,0 +1,35 @@
1
+ // Interprets raw user input to typed UserInput.
2
+ export function inkInputToUserInput(input, key) {
3
+ if (key.escape) {
4
+ return {
5
+ userInputType: 'esc',
6
+ };
7
+ }
8
+ if (key.rightArrow) {
9
+ return {
10
+ userInputType: 'rightArrow',
11
+ };
12
+ }
13
+ if (key.leftArrow) {
14
+ return {
15
+ userInputType: 'leftArrow',
16
+ };
17
+ }
18
+ if (key.upArrow) {
19
+ return {
20
+ userInputType: 'upArrow',
21
+ };
22
+ }
23
+ if (key.downArrow) {
24
+ return {
25
+ userInputType: 'downArrow',
26
+ };
27
+ }
28
+ if (input !== '') {
29
+ return {
30
+ userInputType: 'character',
31
+ string: input,
32
+ };
33
+ }
34
+ return undefined;
35
+ }
package/dist/ui/App.d.ts CHANGED
@@ -1,12 +1,11 @@
1
- import { DisplayRow } from 'dirvi-lib';
2
- import type { UnixAbsolutePath, View } from 'dirvi-lib';
1
+ import { State } from 'dirvi-lib';
2
+ import type { UnixAbsolutePath } from 'dirvi-lib';
3
3
  import { EventMessage } from '../domain/event-message.js';
4
4
  export type AppProps = {
5
5
  cwdAddress: UnixAbsolutePath;
6
- initialView: View;
6
+ initialState: State;
7
7
  print?: (message: EventMessage) => void;
8
8
  onError?: (error: Error) => void;
9
9
  };
10
- export declare function App({ cwdAddress, initialView, print, onError }: AppProps): import("react").JSX.Element | null;
11
- export declare function displayedFilePaths(rows: readonly DisplayRow[]): string[];
10
+ export declare function App({ cwdAddress, initialState, print, onError }: AppProps): import("react").JSX.Element | null;
12
11
  //# sourceMappingURL=App.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAqB,MAAM,WAAW,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMxD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAI1D,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,WAAW,EAAE,IAAI,CAAC;IAClB,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,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,sCA+GxE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,EAAE,CAYxE"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAqC,KAAK,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAIlD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAO1D,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,sCAwHzE"}
package/dist/ui/App.js CHANGED
@@ -1,52 +1,49 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { join } from 'node:path';
3
3
  import { Box, Text, useApp, useInput } from 'ink';
4
- import { useEffect, useReducer, useRef, useState } from 'react';
5
- import { getDirLazyEntries } from 'dirvi-lib';
4
+ import { useEffect, useMemo, useReducer, useState } from 'react';
5
+ import { getDirLazyEntries, NavigationNode } from 'dirvi-lib';
6
+ import { useView } from './hooks/useView.js';
6
7
  import { reducer } from '../application/reducer.js';
7
- import { inputToInputResult } from '../infrastructure/input.js';
8
- import DirEntries from './components/DirEntries.js';
9
- import { createDisplayRows } from '../application/display-rows.js';
10
- import { entryAtPath } from '../application/dir-buffer-helpers.js';
11
- import * as path from 'node:path';
12
- export function App({ cwdAddress, initialView, print, onError }) {
13
- const [view, dispatch] = useReducer(reducer, initialView);
14
- const { buffer, folds, cursor } = view;
15
- const displayRows = createDisplayRows(buffer, folds);
16
- const pendingInput = useRef(undefined);
8
+ import { ViewRowComponent } from './components/ViewRowComponent.js';
9
+ import { inkInputToUserInput } from '../infrastructure/user-input.js';
10
+ import { userInputToIntent } from '../application/user-input-to-intent.js';
11
+ import { intentToEffect } from '../application/intent-to-effect.js';
12
+ import { effectToAction } from '../application/effect-to-action.js';
13
+ export function App({ cwdAddress, initialState, print, onError }) {
14
+ const [state, dispatch] = useReducer(reducer, initialState);
15
+ const navigation = useMemo(() => NavigationNode.from(state.buffer, state.folds), [state.buffer, state.folds]);
16
+ const [commandBuffer, setCommandBuffer] = useState('');
17
+ const view = useView(navigation, state);
17
18
  const [exitStatus, setExitStatus] = useState();
18
19
  // Print view on changes
19
20
  useEffect(() => {
20
- print?.({ type: 'view', view: view });
21
+ print?.({ type: 'view', view: state });
22
+ const visibleFilesPaths = NavigationNode.visibleFilesPaths(navigation).map((path) => join(cwdAddress, ...path));
21
23
  print?.({
22
24
  type: 'displayed-files-paths',
23
- paths: displayedFilePaths(displayRows),
25
+ paths: visibleFilesPaths,
24
26
  });
25
- }, [view, print]);
26
- // --- Input ---
27
- useInput((input, key) => {
28
- const result = inputToInputResult(input, key, view, pendingInput.current);
29
- if (result === undefined) {
30
- return;
31
- }
32
- if (result === 'z') {
33
- pendingInput.current = result;
27
+ }, [state, print, navigation, cwdAddress]);
28
+ function executeEffect(effect) {
29
+ if (effect === undefined) {
34
30
  return;
35
31
  }
36
- pendingInput.current = undefined;
37
- const action = result;
38
- // May need to load entries:
39
- if (action.kind === 'toggleDir') {
40
- const currentEntry = entryAtPath(view.buffer, action.path);
41
- // If the entry at the path is directory with no entries loaded:
42
- if (currentEntry?.kind === 'directory' &&
43
- currentEntry.entries === undefined) {
44
- const address = join(cwdAddress, ...action.path);
32
+ switch (effect.effectType) {
33
+ case 'dispatchEffectAsAction':
34
+ const action = effectToAction(effect.action, navigation, state);
35
+ if (action === undefined) {
36
+ return;
37
+ }
38
+ dispatch(action);
39
+ return;
40
+ case 'loadDir': {
41
+ const address = join(cwdAddress, ...effect.path);
45
42
  void getDirLazyEntries(address)
46
43
  .then((entries) => {
47
44
  dispatch({
48
- kind: 'directoryLoaded',
49
- path: action.path,
45
+ kind: 'updateDir',
46
+ path: effect.path,
50
47
  entries,
51
48
  });
52
49
  })
@@ -55,26 +52,35 @@ export function App({ cwdAddress, initialView, print, onError }) {
55
52
  onError?.(appError);
56
53
  setExitStatus(`Unable to open directory: ${appError.message}`);
57
54
  });
55
+ return;
58
56
  }
59
- else if (currentEntry?.kind === 'directory') {
60
- dispatch({
61
- kind: 'toggleDir',
62
- path: action.path,
57
+ case 'printFile':
58
+ print?.({
59
+ type: 'file',
60
+ path: effect.path,
63
61
  });
64
- }
62
+ return;
63
+ case 'exit':
64
+ setExitStatus(effect.exitMessage);
65
+ return;
66
+ }
67
+ }
68
+ // --- Input Hook ---
69
+ useInput((input, key) => {
70
+ const userInput = inkInputToUserInput(input, key);
71
+ if (userInput === undefined) {
65
72
  return;
66
73
  }
67
- if (action.kind === 'printFile') {
68
- print?.({
69
- type: 'file',
70
- path: action.path,
71
- });
74
+ const intent = userInputToIntent(userInput, commandBuffer);
75
+ if (intent === undefined) {
72
76
  return;
73
77
  }
74
- if (action.kind === 'exit') {
75
- setExitStatus(action.exitMessage);
78
+ const effectResult = intentToEffect(intent, state);
79
+ if (effectResult === undefined) {
80
+ return;
76
81
  }
77
- dispatch(action);
82
+ setCommandBuffer(effectResult.commandBuffer);
83
+ executeEffect(effectResult.effect);
78
84
  });
79
85
  // --- Exit Logic ---
80
86
  const { exit } = useApp();
@@ -91,16 +97,5 @@ export function App({ cwdAddress, initialView, print, onError }) {
91
97
  return null;
92
98
  }
93
99
  // --- JSX ---
94
- return (_jsx(Box, { flexDirection: "column", children: buffer.entries.length === 0 || cursor === undefined ? (_jsx(Text, { dimColor: true, children: "Directory is empty." })) : (_jsx(DirEntries, { rows: displayRows, cursor: cursor })) }));
95
- }
96
- export function displayedFilePaths(rows) {
97
- return rows.flatMap((row) => {
98
- if (row.kind !== 'entry') {
99
- return [];
100
- }
101
- if (row.entry.kind !== 'file') {
102
- return [];
103
- }
104
- return [path.posix.join(String(row.parentPath), String(row.entry.name))];
105
- });
100
+ return (_jsx(Box, { flexDirection: "column", children: view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "Directory is empty." })) : (view.rows.map((row) => (_jsx(ViewRowComponent, { row: row }, row.id)))) }));
106
101
  }
@@ -0,0 +1,11 @@
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;
9
+ };
10
+ export declare function App2({ cwdAddress, initialState, print, onError }: AppProps): import("react").JSX.Element | null;
11
+ //# sourceMappingURL=App2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App2.d.ts","sourceRoot":"","sources":["../../src/ui/App2.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA6B,KAAK,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAQlD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,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,IAAI,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,sCAsH1E"}
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { join } from 'node:path';
3
+ import { Box, Text, useApp, useInput } from 'ink';
4
+ import { useEffect, useReducer, useRef, useState } from 'react';
5
+ import { Cursor, getDirLazyEntries } from 'dirvi-lib';
6
+ import { useView } from './hooks/useView.js';
7
+ import { reducer } from '../application/reducer.js';
8
+ import { userInputToInputResult, } from '../infrastructure/input.js';
9
+ import { ViewRowComponent } from './components/ViewRowComponent.js';
10
+ export function App2({ cwdAddress, initialState, print, onError }) {
11
+ const [state, dispatch] = useReducer(reducer, initialState);
12
+ const view = useView(state);
13
+ const pendingInput = useRef(undefined);
14
+ const [exitStatus, setExitStatus] = useState();
15
+ // Print view on changes
16
+ useEffect(() => {
17
+ print?.({ type: 'view', view: state });
18
+ // print?.({
19
+ // type: 'displayed-files-paths',
20
+ // paths: displayedFilePaths(displayRows),
21
+ // });
22
+ }, [state, print]);
23
+ // --- Input ---
24
+ useInput((input, key) => {
25
+ const result = userInputToInputResult(input, key, state, pendingInput.current);
26
+ if (result === undefined) {
27
+ return;
28
+ }
29
+ if (result === 'z') {
30
+ pendingInput.current = result;
31
+ return;
32
+ }
33
+ pendingInput.current = undefined;
34
+ const action = result;
35
+ // Contents of `updateDir` change meaning at this boundary:
36
+ if (action.kind === 'updateDir') {
37
+ // If the entry at the path is directory with no entries loaded:
38
+ if (action.entries === undefined) {
39
+ const path = Cursor.getPath(state.cursor);
40
+ if (path === undefined) {
41
+ return undefined;
42
+ }
43
+ const address = join(cwdAddress, ...path);
44
+ void getDirLazyEntries(address)
45
+ .then((entries) => {
46
+ dispatch({
47
+ kind: 'updateDir',
48
+ path: path,
49
+ entries,
50
+ });
51
+ })
52
+ .catch((error) => {
53
+ const appError = error instanceof Error ? error : new Error(String(error));
54
+ onError?.(appError);
55
+ setExitStatus(`Unable to open directory: ${appError.message}`);
56
+ });
57
+ }
58
+ else {
59
+ // Otherwise need to wipe the entries:
60
+ dispatch({
61
+ kind: 'updateDir',
62
+ path: action.path,
63
+ entries: undefined,
64
+ });
65
+ }
66
+ return;
67
+ }
68
+ if (action.kind === 'printFile') {
69
+ print?.({
70
+ type: 'file',
71
+ path: action.path,
72
+ });
73
+ return;
74
+ }
75
+ if (action.kind === 'exit') {
76
+ setExitStatus(action.exitMessage);
77
+ }
78
+ dispatch(action);
79
+ });
80
+ // --- Exit Logic ---
81
+ const { exit } = useApp();
82
+ useEffect(() => {
83
+ if (exitStatus === undefined) {
84
+ return;
85
+ }
86
+ if (exitStatus !== '') {
87
+ onError?.(new Error(exitStatus));
88
+ }
89
+ exit();
90
+ }, [exitStatus, exit, onError]);
91
+ if (exitStatus !== undefined) {
92
+ return null;
93
+ }
94
+ // --- JSX ---
95
+ return (_jsx(Box, { flexDirection: "column", children: view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "Directory is empty." })) : (view.rows.map((row) => (_jsx(ViewRowComponent, { row: row }, row.id)))) }));
96
+ }
@@ -1,13 +1,13 @@
1
- import { UnixAbsolutePath, View } from 'dirvi-lib';
1
+ import { UnixAbsolutePath, State } from 'dirvi-lib';
2
2
  import { EventMessage } from '../domain/event-message.js';
3
3
  export type EmptyView = {
4
4
  cursor: undefined;
5
5
  };
6
6
  export type ShellAppProps = {
7
7
  cwdAddress: UnixAbsolutePath;
8
- initialView: View | EmptyView;
8
+ initialState: State | EmptyView;
9
9
  print?: (message: EventMessage) => void;
10
10
  onError?: (error: Error) => void;
11
11
  };
12
- export declare function AppShell({ cwdAddress, initialView, print, onError, }: ShellAppProps): import("react").JSX.Element;
12
+ export declare function AppShell({ cwdAddress, initialState, print, onError, }: ShellAppProps): import("react").JSX.Element;
13
13
  //# 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,IAAI,EAAE,MAAM,WAAW,CAAC;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,WAAW,EAAE,IAAI,GAAG,SAAS,CAAC;IAC9B,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,WAAW,EACX,KAAK,EACL,OAAO,GACR,EAAE,aAAa,+BAaf"}
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,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Text } from 'ink';
3
3
  import { App } from './App.js';
4
- export function AppShell({ cwdAddress, initialView, print, onError, }) {
5
- if (initialView.cursor === undefined) {
4
+ export function AppShell({ cwdAddress, initialState, print, onError, }) {
5
+ if (initialState.cursor === undefined) {
6
6
  return _jsx(Text, { dimColor: true, children: "Directory is empty." });
7
7
  }
8
- return (_jsx(App, { cwdAddress: cwdAddress, initialView: initialView, print: print, onError: onError }));
8
+ return (_jsx(App, { cwdAddress: cwdAddress, initialState: initialState, print: print, onError: onError }));
9
9
  }
@@ -1,4 +1,5 @@
1
- import type { Cursor, DisplayRow } from 'dirvi-lib';
1
+ import { Cursor } from 'dirvi-lib';
2
+ import { DisplayRow } from 'dirvi-lib';
2
3
  declare function DirEntries({ rows, cursor }: {
3
4
  rows: DisplayRow[];
4
5
  cursor: Cursor;
@@ -1 +1 @@
1
- {"version":3,"file":"DirEntries.d.ts","sourceRoot":"","sources":["../../../src/ui/components/DirEntries.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAQpD,iBAAS,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,+BA0D3E;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"DirEntries.d.ts","sourceRoot":"","sources":["../../../src/ui/components/DirEntries.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAKvC,iBAAS,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,+BA2D3E;AAED,eAAe,UAAU,CAAC"}
@@ -1,15 +1,13 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text, useWindowSize } from 'ink';
3
- import { rowsEqual } from 'dirvi-lib';
4
- import { UnixEntry as UnixEntryComponent } from './UnixEntry.js';
3
+ import { Cursor } from 'dirvi-lib';
4
+ import { UnixEntryComponent as UnixEntryComponent } from './UnixEntryComponent.js';
5
5
  import { useRef } from 'react';
6
- // Display column of entries in directory, indented, and optional
7
- // cursor in position.
8
6
  function DirEntries({ rows, cursor }) {
9
7
  const scrollMargin = 3;
10
8
  const { rows: terminalRows } = useWindowSize();
11
9
  const viewportStartRef = useRef(0);
12
- const cursorIndex = rows.findIndex((row) => rowsEqual(cursor, row));
10
+ const cursorIndex = rows.findIndex((row) => Cursor.matchesDisplayRow(cursor, row));
13
11
  const viewportHeight = Math.max(1, terminalRows);
14
12
  const maximumViewportStart = Math.max(0, rows.length - viewportHeight);
15
13
  let viewportStart = viewportStartRef.current;
@@ -29,10 +27,11 @@ function DirEntries({ rows, cursor }) {
29
27
  viewportStartRef.current = viewportStart;
30
28
  const visibleRows = rows.slice(viewportStart, viewportStart + viewportHeight);
31
29
  return (_jsx(_Fragment, { children: visibleRows.map((row) => {
32
- const selected = rowsEqual(cursor, row);
30
+ const selected = Cursor.matchesDisplayRow(cursor, row);
33
31
  const indent = row.parentPath.length;
34
32
  if (row.kind === 'fold') {
35
- return (_jsx(Box, { paddingLeft: indent * 2, children: _jsx(Text, { inverse: selected, dimColor: !selected, children: "..." }) }, `fold-${row.parentPath.join('/')}-${row.entryNames[0]}`));
33
+ const key = `fold-${row.parentPath.join('/')}-${row.entryNames[0]}`;
34
+ return (_jsx(Box, { paddingLeft: indent * 2, children: _jsx(Text, { inverse: selected, dimColor: !selected, children: "..." }) }, key));
36
35
  }
37
36
  return (_jsx(Box, { paddingLeft: indent * 2, children: _jsx(UnixEntryComponent, { entry: row.entry, selected: selected }) }, [...row.parentPath, row.entry.name].join('/')));
38
37
  }) }));
@@ -0,0 +1,6 @@
1
+ import { UnixEntry } from 'dirvi-lib';
2
+ export declare function UnixEntryComponent({ entry, selected, }: {
3
+ entry: UnixEntry;
4
+ selected: boolean;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=UnixEntryComponent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnixEntryComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/UnixEntryComponent.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,+BAmBA"}