@mak-98/dirvi-cli 0.7.1 → 0.8.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 (33) hide show
  1. package/dist/application/effect-to-action.d.ts +3 -3
  2. package/dist/application/effect-to-action.d.ts.map +1 -1
  3. package/dist/application/effect-to-action.js +14 -25
  4. package/dist/application/reducer-action.d.ts +2 -3
  5. package/dist/application/reducer-action.d.ts.map +1 -1
  6. package/dist/application/reducer.d.ts +1 -1
  7. package/dist/application/reducer.d.ts.map +1 -1
  8. package/dist/application/reducer.js +14 -9
  9. package/dist/domain/app-api.d.ts +15 -11
  10. package/dist/domain/app-api.d.ts.map +1 -1
  11. package/dist/domain/app-api.js +4 -8
  12. package/dist/domain/posix-tree-node.d.ts +5 -5
  13. package/dist/domain/posix-tree-node.d.ts.map +1 -1
  14. package/dist/domain/posix-tree-node.js +10 -6
  15. package/dist/infrastructure/load-posix-app-api.d.ts +8 -7
  16. package/dist/infrastructure/load-posix-app-api.d.ts.map +1 -1
  17. package/dist/infrastructure/load-posix-app-api.js +35 -33
  18. package/dist/ui/App.d.ts +1 -1
  19. package/dist/ui/App.d.ts.map +1 -1
  20. package/dist/ui/App.js +10 -6
  21. package/dist/ui/LoadingApp.d.ts.map +1 -1
  22. package/dist/ui/LoadingApp.js +9 -22
  23. package/dist/ui/components/StatusBar.d.ts +2 -2
  24. package/dist/ui/components/StatusBar.d.ts.map +1 -1
  25. package/dist/ui/components/ViewRowComponent.d.ts.map +1 -1
  26. package/dist/ui/components/ViewRowComponent.js +3 -6
  27. package/dist/ui/hooks/useView.d.ts +2 -3
  28. package/dist/ui/hooks/useView.d.ts.map +1 -1
  29. package/dist/ui/hooks/useView.js +3 -3
  30. package/dist/ui/view.d.ts +12 -7
  31. package/dist/ui/view.d.ts.map +1 -1
  32. package/dist/ui/view.js +40 -64
  33. package/package.json +5 -3
@@ -1,6 +1,6 @@
1
1
  import type { ReducerAction } from './reducer-action.js';
2
2
  import { SerializableKey, TreeNode, TreeNodeApi } from 'dirvi-lib/dist/tree-surfer/tree-node/tree-node.types.js';
3
- import { CursorApi, EffectAction, NavNode, NavNodeApi, State } from 'dirvi-lib';
4
- export type EffectToAction<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = (effectAction: EffectAction<Id, BufferNode>, navigation: NavNode<Id, BufferNode>, state: State<Id, BufferNode>) => ReducerAction<Id, BufferNode> | undefined;
5
- export declare function createEffectToAction<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(treeNodeApi: TreeNodeApi<Id, BufferNode>, cursorApi: CursorApi<Id>, navNodeApi: NavNodeApi<Id, BufferNode>): EffectToAction<Id, BufferNode>;
3
+ import { CursorApi, EffectAction, NavBranch, NavNodeApi, State } from 'dirvi-lib';
4
+ export type EffectToAction<Id extends SerializableKey, Node extends TreeNode<Id, Node>> = (effectAction: EffectAction<Id, Node>, navigation: NavBranch<Id>, state: State<Id, Node>) => ReducerAction<Id, Node> | undefined;
5
+ export declare function createEffectToAction<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(treeNodeApi: TreeNodeApi<Id, Node>, cursorApi: CursorApi<Id>, navNodeApi: NavNodeApi<Id, Node>): EffectToAction<Id, Node>;
6
6
  //# sourceMappingURL=effect-to-action.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"effect-to-action.d.ts","sourceRoot":"","sources":["../../src/application/effect-to-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,eAAe,EACf,QAAQ,EACR,WAAW,EACZ,MAAM,yDAAyD,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEhF,MAAM,MAAM,cAAc,CACxB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IACzC,CACF,YAAY,EAAE,YAAY,CAAC,EAAE,EAAE,UAAU,CAAC,EAC1C,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,EACnC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,KACzB,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC;AAE/C,wBAAgB,oBAAoB,CAClC,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAE3C,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,EACxC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,GACrC,cAAc,CAAC,EAAE,EAAE,UAAU,CAAC,CA+GhC"}
1
+ {"version":3,"file":"effect-to-action.d.ts","sourceRoot":"","sources":["../../src/application/effect-to-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,eAAe,EACf,QAAQ,EACR,WAAW,EACZ,MAAM,yDAAyD,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAW,UAAU,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAE3F,MAAM,MAAM,cAAc,CACxB,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAC7B,CACF,YAAY,EAAE,YAAY,CAAC,EAAE,EAAE,IAAI,CAAC,EACpC,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC,EACzB,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,KACnB,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC;AAEzC,wBAAgB,oBAAoB,CAClC,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAE/B,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,EAClC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,GAC/B,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAgG1B"}
@@ -26,7 +26,11 @@ export function createEffectToAction(treeNodeApi, cursorApi, navNodeApi) {
26
26
  entries: effectAction.entries,
27
27
  };
28
28
  case 'navigateToParent': {
29
- const cursor = navNodeApi.parentCursor(navigation, state.cursor);
29
+ const navNode = navigation.children;
30
+ if (navNode === null) {
31
+ return undefined;
32
+ }
33
+ const cursor = navNodeApi.parentCursor(navNode, state.cursor);
30
34
  return cursor === undefined
31
35
  ? undefined
32
36
  : {
@@ -35,12 +39,7 @@ export function createEffectToAction(treeNodeApi, cursorApi, navNodeApi) {
35
39
  };
36
40
  }
37
41
  case 'fold': {
38
- if (state.cursor.kind === 'fold') {
39
- return undefined;
40
- }
41
- const path = [...state.cursor.parentPath, state.cursor.entryId];
42
- const entry = treeNodeApi.getAtPath(state.buffer, path, (node) => node);
43
- if (entry === undefined) {
42
+ if (state.cursor.length === 0) {
44
43
  return undefined;
45
44
  }
46
45
  const cursor = navNodeApi.cursorAfterFold(navigation, state.cursor);
@@ -49,36 +48,26 @@ export function createEffectToAction(treeNodeApi, cursorApi, navNodeApi) {
49
48
  }
50
49
  return {
51
50
  kind: 'fold',
52
- parentPath: [...state.cursor.parentPath],
53
- entry,
51
+ path: state.cursor,
54
52
  cursor,
55
53
  };
56
54
  }
57
55
  case 'unfold': {
58
- if (state.cursor.kind !== 'fold') {
56
+ const currentPath = cursorApi.getPath(state.cursor);
57
+ const navNode = navigation.children;
58
+ if (navNode === null) {
59
59
  return undefined;
60
60
  }
61
- const node = navNodeApi.getNodeAtPath(navigation, [
62
- ...state.cursor.parentPath,
63
- ]);
64
- if (node === undefined || node.foldedEntries.length === 0) {
61
+ const node = navNodeApi.getNodeAtPath(navNode, currentPath);
62
+ if (node === undefined || node.folded?.entries.length === 0) {
65
63
  return undefined;
66
64
  }
67
- const firstFoldedEntry = node.foldedEntries[0];
68
65
  return {
69
66
  kind: 'unfold',
70
- parentPath: [...state.cursor.parentPath],
71
- cursor: {
72
- kind: 'entry',
73
- parentPath: state.cursor.parentPath,
74
- entryId: firstFoldedEntry.id,
75
- },
67
+ path: currentPath,
68
+ cursor: state.cursor,
76
69
  };
77
70
  }
78
- case 'toggleFold':
79
- return effectToAction(state.cursor.kind === 'fold'
80
- ? { effectActionType: 'unfold' }
81
- : { effectActionType: 'fold' }, navigation, state);
82
71
  }
83
72
  }
84
73
  return effectToAction;
@@ -12,12 +12,11 @@ export type ReducerAction<Id extends SerializableKey, BufferNode extends TreeNod
12
12
  newState: State<Id, BufferNode>;
13
13
  } | {
14
14
  kind: 'fold';
15
- parentPath: Id[];
16
- entry: BufferNode;
15
+ path: readonly Id[];
17
16
  cursor: Cursor<Id>;
18
17
  } | {
19
18
  kind: 'unfold';
20
- parentPath: Id[];
19
+ path: Id[];
21
20
  cursor: Cursor<Id>;
22
21
  };
23
22
  //# sourceMappingURL=reducer-action.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reducer-action.d.ts","sourceRoot":"","sources":["../../src/application/reducer-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,MAAM,aAAa,CACvB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IAEzC;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,GACD;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;CAC9B,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAChC,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;CACjC,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,EAAE,EAAE,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,EAAE,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,CAAC"}
1
+ {"version":3,"file":"reducer-action.d.ts","sourceRoot":"","sources":["../../src/application/reducer-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,MAAM,aAAa,CACvB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IAEzC;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,GACD;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;CAC9B,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAChC,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;CACjC,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,EAAE,EAAE,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { FoldNodeService, SerializableKey, State, TreeNode, TreeNodeApi } from 'dirvi-lib';
2
2
  import { ReducerAction } from './reducer-action.js';
3
3
  export type Reducer<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = (state: State<Id, BufferNode>, action: ReducerAction<Id, BufferNode>) => State<Id, BufferNode>;
4
- export declare function createReducer<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(treeNodeApi: TreeNodeApi<Id, BufferNode>, foldNodeService: FoldNodeService<Id>): Reducer<Id, BufferNode>;
4
+ export declare function createReducer<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(treeNodeApi: TreeNodeApi<Id, Node>, foldNodeService: FoldNodeService<Id>): Reducer<Id, Node>;
5
5
  //# sourceMappingURL=reducer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/application/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EACf,eAAe,EACf,KAAK,EACL,QAAQ,EACR,WAAW,EACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,MAAM,OAAO,CACjB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IACzC,CACF,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAC5B,MAAM,EAAE,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,KAClC,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;AAE3B,wBAAgB,aAAa,CAC3B,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAE3C,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,EACxC,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC,GACnC,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,CA0FzB"}
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/application/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,KAAK,EACL,QAAQ,EACR,WAAW,EACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,MAAM,OAAO,CACjB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IACzC,CACF,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAC5B,MAAM,EAAE,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,KAClC,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;AAE3B,wBAAgB,aAAa,CAC3B,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAE/B,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,EAClC,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC,GACnC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAkGnB"}
@@ -7,7 +7,7 @@ export function createReducer(treeNodeApi, foldNodeService) {
7
7
  cursor: action.cursor,
8
8
  };
9
9
  case 'updateBranch':
10
- const buffer = treeNodeApi.modifyAtPath(state.buffer, action.path, (node) => {
10
+ const root = treeNodeApi.modifyAtPath(state.root, action.path, (node) => {
11
11
  // A branch update cannot turn a leaf into a branch.
12
12
  if (!treeNodeApi.isBranch(node)) {
13
13
  return undefined;
@@ -17,12 +17,12 @@ export function createReducer(treeNodeApi, foldNodeService) {
17
17
  children: action.entries,
18
18
  };
19
19
  });
20
- if (buffer === undefined) {
20
+ if (root === undefined) {
21
21
  return state;
22
22
  }
23
23
  return {
24
24
  ...state,
25
- buffer,
25
+ root,
26
26
  };
27
27
  case 'setState': {
28
28
  // A newer update has already been applied.
@@ -34,32 +34,37 @@ export function createReducer(treeNodeApi, foldNodeService) {
34
34
  };
35
35
  }
36
36
  case 'fold': {
37
- const foldNode = foldNodeService.addFoldedEntryAtPath(state.foldNode, action.parentPath, action.entry.id);
37
+ const entryId = action.path.at(-1);
38
+ if (entryId === undefined) {
39
+ return state;
40
+ }
41
+ const parentPath = action.path.slice(0, -1);
42
+ const foldRoot = foldNodeService.addFoldedEntryAtPath(state.foldRoot, parentPath, entryId);
38
43
  /*
39
44
  * addFoldedEntryAtPath creates missing fold paths, so this should
40
45
  * normally never be undefined. Preserve the existing state if it is.
41
46
  */
42
- if (foldNode === undefined) {
47
+ if (foldRoot === undefined) {
43
48
  return state;
44
49
  }
45
50
  return {
46
51
  ...state,
47
- foldNode,
52
+ foldRoot,
48
53
  cursor: action.cursor,
49
54
  };
50
55
  }
51
56
  case 'unfold': {
52
- const foldNode = foldNodeService.clearFoldedEntriesAtPath(state.foldNode, action.parentPath);
57
+ const foldRoot = foldNodeService.clearFoldedEntriesAtPath(state.foldRoot, action.path);
53
58
  /*
54
59
  * Unlike adding a fold, clearing only operates on an existing path.
55
60
  * An undefined result means that the fold path no longer exists.
56
61
  */
57
- if (foldNode === undefined) {
62
+ if (foldRoot === undefined) {
58
63
  return state;
59
64
  }
60
65
  return {
61
66
  ...state,
62
- foldNode,
67
+ foldRoot,
63
68
  cursor: action.cursor,
64
69
  };
65
70
  }
@@ -1,22 +1,26 @@
1
- import { CursorApi, FoldNodeApi, FoldNodeService, NavNodeApi, SerializableKey, State, StateApi, TreeNode, TreeNodeApi } from 'dirvi-lib';
1
+ import { BranchTreeNode, CursorApi, FoldNodeApi, FoldNodeService, NavNodeApi, SerializableKey, State, StateApi, TreeNode, TreeNodeApi } from 'dirvi-lib';
2
2
  import { ViewApi } from './view-api.js';
3
- export type AppApi<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>, ViewKey = string> = {
3
+ export type AppApi<Id extends SerializableKey, Node extends TreeNode<Id, Node>, ViewKey = string> = {
4
+ /**
5
+ * Name used to distinguish the app, e.g. 'posix' for POSIX directory app.
6
+ */
4
7
  appId: string;
5
8
  /**
6
- * Name displayed to distinguish app instance.
9
+ * Name to distinguish app instance.
7
10
  */
8
11
  name: string;
12
+ rootId: Id;
9
13
  /**
10
- * Message displayed when the entries are empty and there is nothing
11
- * to display.
14
+ * Message displayed when there are no root children.
12
15
  */
13
- emptyForestMessage: string;
16
+ emptyRootMessage: string;
14
17
  /**
15
18
  * Loads the children of the branch at `path`.
16
19
  *
17
20
  * The empty path represents the root branch.
18
21
  */
19
- loadBranches: (path: Id[]) => Promise<BufferNode[]>;
22
+ loadBranches: (path: Id[]) => Promise<Node[]>;
23
+ createRoot: () => Promise<Node & BranchTreeNode<Id, Node>>;
20
24
  /**
21
25
  * Subscribes to external data changes that require the tree state
22
26
  * to be loaded again.
@@ -24,7 +28,7 @@ export type AppApi<Id extends SerializableKey, BufferNode extends TreeNode<Id, B
24
28
  * Returns an unsubscribe function.
25
29
  */
26
30
  subscribeToResync: (listener: () => void) => () => void;
27
- treeNodeApi: TreeNodeApi<Id, BufferNode>;
31
+ treeNodeApi: TreeNodeApi<Id, Node>;
28
32
  /**
29
33
  * Structural fold-tree traversal and immutable path updates.
30
34
  */
@@ -35,10 +39,10 @@ export type AppApi<Id extends SerializableKey, BufferNode extends TreeNode<Id, B
35
39
  */
36
40
  foldNodeService: FoldNodeService<Id>;
37
41
  cursorApi: CursorApi<Id>;
38
- stateApi: StateApi<Id, BufferNode>;
39
- navNodeApi: NavNodeApi<Id, BufferNode>;
42
+ stateApi: StateApi<Id, Node>;
43
+ navNodeApi: NavNodeApi<Id, Node>;
40
44
  viewKey: ViewKey;
41
- viewApi: ViewApi<State<Id, BufferNode>, ViewKey>;
45
+ viewApi: ViewApi<State<Id, Node>, ViewKey>;
42
46
  };
43
47
  export declare function createAppApis<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(): {
44
48
  treeNodeApi: TreeNodeApi<Id, BufferNode>;
@@ -1 +1 @@
1
- {"version":3,"file":"app-api.d.ts","sourceRoot":"","sources":["../../src/domain/app-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,SAAS,EACT,WAAW,EACX,eAAe,EACf,UAAU,EACV,eAAe,EACf,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,MAAM,MAAM,CAChB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,IACd;IAIF,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAEpD;;;;;OAKG;IACH,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAExD,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAEzC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAE7B;;;OAGG;IACH,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;IAErC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;IAEzB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAEnC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAEvC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;CAClD,CAAC;AAEF,wBAAgB,aAAa,CAC3B,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,KACxC;IACH,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACzC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;IACrC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACnC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;CACxC,CAkCA"}
1
+ {"version":3,"file":"app-api.d.ts","sourceRoot":"","sources":["../../src/domain/app-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAMd,SAAS,EAET,WAAW,EACX,eAAe,EACf,UAAU,EACV,eAAe,EACf,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,MAAM,MAAM,CAChB,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAC/B,OAAO,GAAG,MAAM,IACd;IACF;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,EAAE,EAAE,CAAC;IAEX;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,GAAG,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAE3D;;;;;OAKG;IACH,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAExD,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAEnC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAE7B;;;OAGG;IACH,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;IAErC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;IAEzB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAE7B,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAEjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;CAC5C,CAAC;AAEF,wBAAgB,aAAa,CAC3B,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,KACxC;IACH,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACzC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,eAAe,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;IACrC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACnC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;CACxC,CA4BA"}
@@ -1,14 +1,10 @@
1
- import { createCursorApi, createFoldNodeApi, createFoldNodeService, createNavNodeApi, createStateApi, createTreeNodeApi, } from 'dirvi-lib';
1
+ import { createCursorApi, createFoldNodeService, createNavNodeApi, createStateApi, createTreeNodeApi, } from 'dirvi-lib';
2
2
  export function createAppApis() {
3
3
  const treeNodeApi = createTreeNodeApi();
4
- const foldNodeApi = createFoldNodeApi();
5
- const foldNodeService = createFoldNodeService(foldNodeApi, (id) => ({
6
- id,
7
- children: [],
8
- folds: new Set(),
9
- }));
4
+ const foldNodeApi = createTreeNodeApi();
5
+ const foldNodeService = createFoldNodeService(foldNodeApi);
10
6
  const cursorApi = createCursorApi();
11
- const navNodeApi = createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi);
7
+ const navNodeApi = createNavNodeApi(treeNodeApi, foldNodeService, cursorApi);
12
8
  const stateApi = createStateApi(treeNodeApi, foldNodeApi, cursorApi, navNodeApi);
13
9
  return {
14
10
  treeNodeApi,
@@ -1,9 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { UnixPath } from './unix-path.js';
3
- import { Cursor, FoldNode, FoldNodeRoot, NavNode, State } from 'dirvi-lib';
3
+ import { BranchTreeNode, Cursor, FoldNode, NavNode, State } from 'dirvi-lib';
4
4
  export declare const PosixNameSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodString>;
5
5
  export type PosixName = z.output<typeof PosixNameSchema>;
6
6
  export declare const PosixTreeNodeSchema: z.ZodType<PosixTreeNode>;
7
+ export declare const PosixBranchTreeNodeSchema: z.ZodType<PosixTreeNode & BranchTreeNode<PosixName, PosixTreeNode>>;
7
8
  export type PosixTreeNode = {
8
9
  kind: 'file';
9
10
  id: PosixName;
@@ -18,14 +19,13 @@ export type PosixTreeNode = {
18
19
  };
19
20
  export declare const PosixTreeNodeApi: import("dirvi-lib").TreeNodeApi<string, PosixTreeNode>;
20
21
  export declare const PosixFoldNodeSchema: z.ZodType<PosixFoldNode>;
21
- export declare const PosixFoldNodeRootSchema: z.ZodType<PosixFoldNodeRoot>;
22
22
  export type PosixFoldNode = FoldNode<PosixName>;
23
- export type PosixFoldNodeRoot = FoldNodeRoot<PosixName>;
24
- export declare const PosixFoldNodeApi: import("dirvi-lib").FoldNodeApi<string>;
23
+ export declare const PosixFoldNodeApi: import("dirvi-lib").TreeNodeApi<string, FoldNode<string>>;
24
+ export declare const PosixFoldNodeService: import("dirvi-lib").FoldNodeService<string>;
25
25
  export declare const PosixCursorSchema: z.ZodType<PosixCursor>;
26
26
  export type PosixCursor = Cursor<PosixName>;
27
27
  export declare const PosixCursorApi: import("dirvi-lib").CursorApi<string>;
28
- export type PosixNavNode = NavNode<PosixName, PosixTreeNode>;
28
+ export type PosixNavNode = NavNode<PosixName>;
29
29
  export declare const PosixNavApi: import("dirvi-lib").NavNodeApi<string, PosixTreeNode>;
30
30
  export declare const PosixStateSchema: z.ZodType<PosixState>;
31
31
  export type PosixState = State<PosixName, PosixTreeNode>;
@@ -1 +1 @@
1
- {"version":3,"file":"posix-tree-node.d.ts","sourceRoot":"","sources":["../../src/domain/posix-tree-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAkB,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAUL,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,OAAO,EAEP,KAAK,EAEN,MAAM,WAAW,CAAC;AAQnB,eAAO,MAAM,eAAe,yEAU3B,CAAC;AAGF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAUzD,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAsBvD,CAAC;AAGH,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,SAAS,CAAC;CACf,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,QAAQ,CAAC;CAClB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,SAAS,CAAC;IACd,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;CAClC,CAAC;AAQN,eAAO,MAAM,gBAAgB,wDAAgD,CAAC;AAQ9E,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAkB,CAAC;AAC5E,eAAO,MAAM,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAC7C,CAAC;AAGrB,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;AAChD,MAAM,MAAM,iBAAiB,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;AAGxD,eAAO,MAAM,gBAAgB,yCAAiC,CAAC;AAK/D,eAAO,MAAM,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAChB,CAAC;AAGtC,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAE5C,eAAO,MAAM,cAAc,uCAA+B,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAE7D,eAAO,MAAM,WAAW,uDAIvB,CAAC;AAKF,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAIlD,CAAC;AAGF,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAGzD,eAAO,MAAM,aAAa,qDAKzB,CAAC"}
1
+ {"version":3,"file":"posix-tree-node.d.ts","sourceRoot":"","sources":["../../src/domain/posix-tree-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAkB,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,cAAc,EASd,MAAM,EACN,QAAQ,EACR,OAAO,EAEP,KAAK,EAEN,MAAM,WAAW,CAAC;AAQnB,eAAO,MAAM,eAAe,yEAU3B,CAAC;AAGF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAUzD,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAsBvD,CAAC;AAEH,eAAO,MAAM,yBAAyB,EAAE,CAAC,CAAC,OAAO,CAC/C,aAAa,GAAG,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,CAOzD,CAAC;AAGF,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,SAAS,CAAC;CACf,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,QAAQ,CAAC;CAClB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,SAAS,CAAC;IACd,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;CAClC,CAAC;AAQN,eAAO,MAAM,gBAAgB,wDAAgD,CAAC;AAM9E,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAkB,CAAC;AAE5E,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEhD,eAAO,MAAM,gBAAgB,2DAAsD,CAAC;AAEpF,eAAO,MAAM,oBAAoB,6CAAqD,CAAA;AAKtF,eAAO,MAAM,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAChB,CAAC;AAEtC,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAC5C,eAAO,MAAM,cAAc,uCAA+B,CAAC;AAE3D,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAC9C,eAAO,MAAM,WAAW,uDAIvB,CAAC;AAKF,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAIlD,CAAC;AAGF,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAGzD,eAAO,MAAM,aAAa,qDAKzB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { UnixPathSchema } from './unix-path.js';
3
- import { createCursorApi, createCursorSchema, createFoldNodeApi, createFoldNodeSchemas, createNavNodeApi, createStateApi, createStateSchema, createTreeNodeApi, createTreeNodeSchemas, serializableKeySchema, } from 'dirvi-lib';
3
+ import { createCursorApi, createCursorSchema, createFoldNodeSchemas, createFoldNodeService, createNavNodeApi, createStateApi, createStateSchema, createTreeNodeApi, createTreeNodeSchemas, serializableKeySchema, } from 'dirvi-lib';
4
4
  // --- PosixName extends PropertyKey ---
5
5
  // Schema
6
6
  export const PosixNameSchema = serializableKeySchema.pipe(z
@@ -32,22 +32,26 @@ export const PosixTreeNodeSchema = z.lazy(() => {
32
32
  }),
33
33
  ]);
34
34
  });
35
+ export const PosixBranchTreeNodeSchema = PosixTreeNodeSchema.refine((node) => 'children' in node, {
36
+ message: 'The state root must be a branch node.',
37
+ });
35
38
  // API
36
39
  export const PosixTreeNodeApi = createTreeNodeApi();
37
40
  // --- PosixFoldNode ---
38
41
  // Schema
39
- const { foldNodeSchema, foldNodeRootSchema } = createFoldNodeSchemas(PosixNameSchema);
42
+ const foldNodeSchema = createFoldNodeSchemas(PosixNameSchema);
40
43
  export const PosixFoldNodeSchema = foldNodeSchema;
41
- export const PosixFoldNodeRootSchema = foldNodeRootSchema;
42
44
  // API
43
- export const PosixFoldNodeApi = createFoldNodeApi();
45
+ export const PosixFoldNodeApi = createTreeNodeApi();
46
+ // Service
47
+ export const PosixFoldNodeService = createFoldNodeService(PosixFoldNodeApi);
44
48
  // --- PosixCursor ---
45
49
  // Schema
46
50
  export const PosixCursorSchema = createCursorSchema(PosixNameSchema);
47
51
  export const PosixCursorApi = createCursorApi();
48
- export const PosixNavApi = createNavNodeApi(PosixTreeNodeApi, PosixFoldNodeApi, PosixCursorApi);
52
+ export const PosixNavApi = createNavNodeApi(PosixTreeNodeApi, PosixFoldNodeService, PosixCursorApi);
49
53
  // --- PosixState ---
50
54
  // Schema
51
- export const PosixStateSchema = createStateSchema(PosixTreeNodeSchema, PosixFoldNodeRootSchema, PosixCursorSchema);
55
+ export const PosixStateSchema = createStateSchema(PosixBranchTreeNodeSchema, PosixFoldNodeSchema, PosixCursorSchema);
52
56
  // API
53
57
  export const PosixStateApi = createStateApi(PosixTreeNodeApi, PosixFoldNodeApi, PosixCursorApi, PosixNavApi);
@@ -4,14 +4,15 @@ import { UnixAbsolutePath } from '../domain/unix-path.js';
4
4
  export type StoredPosixFoldNode = {
5
5
  id: PosixName;
6
6
  children: StoredPosixFoldNode[];
7
- folds: PosixName[];
7
+ foldedChildren: StoredPosixFoldNode[];
8
8
  };
9
- export type StoredPosixFoldNodeRoot = {
10
- children: StoredPosixFoldNode[];
11
- folds: PosixName[];
12
- };
13
- export type StoredPosixState = Omit<PosixState, 'foldNode'> & {
14
- foldNode: StoredPosixFoldNodeRoot;
9
+ export type StoredPosixState = Omit<PosixState, 'foldRoot'> & {
10
+ foldRoot: StoredPosixFoldNode;
15
11
  };
12
+ /**
13
+ * Creates the AppApi for the POSIX app.
14
+ *
15
+ * @param directory - optional directory path for loading the app not at the cwd.
16
+ */
16
17
  export declare function loadPosixAppApi(directory?: string): AppApi<PosixName, PosixTreeNode, UnixAbsolutePath>;
17
18
  //# sourceMappingURL=load-posix-app-api.d.ts.map
@@ -1 +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,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAM7D,OAAO,EAIL,SAAS,EAET,UAAU,EACV,aAAa,EAEd,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAkB1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAsBF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;IAC5D,QAAQ,EAAE,uBAAuB,CAAC;CACnC,CAAC;AA2DF,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAyBpD"}
1
+ {"version":3,"file":"load-posix-app-api.d.ts","sourceRoot":"","sources":["../../src/infrastructure/load-posix-app-api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAM7D,OAAO,EAIL,SAAS,EAET,UAAU,EACV,aAAa,EAEd,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAiB1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,cAAc,EAAE,mBAAmB,EAAE,CAAC;CACvC,CAAC;AAoBF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG;IAC5D,QAAQ,EAAE,mBAAmB,CAAC;CAC/B,CAAC;AA0CF;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAoCpD"}
@@ -1,4 +1,4 @@
1
- import { join } from 'node:path';
1
+ import { basename, join } from 'node:path';
2
2
  import { watch } from 'node:fs';
3
3
  import { createAppApis } from '../domain/app-api.js';
4
4
  import { getUnixAbsPath } from './get-unix-abs-path.js';
@@ -6,89 +6,91 @@ import { getDirEntries } from './get-dir-entries.js';
6
6
  import envPaths from 'env-paths';
7
7
  import { createFileViewApi } from './create-file-view-api.js';
8
8
  import { createHash } from 'node:crypto';
9
- import { PosixCursorSchema, PosixNameSchema, PosixTreeNodeSchema, } from '../domain/posix-tree-node.js';
9
+ import { PosixBranchTreeNodeSchema, PosixCursorSchema, PosixNameSchema, } from '../domain/posix-tree-node.js';
10
10
  import { z } from 'zod';
11
11
  // ---*--- App Data ---*---
12
12
  // Paths for app data:
13
- const paths = envPaths('dirvi');
14
- const posixAppDataDirectory = join(paths.data, 'apps', 'posix');
13
+ const environmentPaths = envPaths('dirvi');
14
+ const posixAppDataDirectory = join(environmentPaths.data, 'apps', 'posix');
15
15
  const posixAppViewsDirectory = join(posixAppDataDirectory, 'views');
16
16
  // Helpers for saving app data:
17
- // The key for the Posix app instance is just the directory working in.
18
17
  function encodeKey(key) {
19
18
  return createHash('sha256').update(key).digest('hex');
20
19
  }
21
20
  const StoredPosixFoldNodeSchema = z.lazy(() => z.object({
22
21
  id: PosixNameSchema,
23
22
  children: z.array(StoredPosixFoldNodeSchema),
24
- folds: z.array(PosixNameSchema),
23
+ foldedChildren: z.array(StoredPosixFoldNodeSchema),
25
24
  }));
26
- const StoredPosixFoldNodeRootSchema = z.object({
27
- children: z.array(StoredPosixFoldNodeSchema),
28
- folds: z.array(PosixNameSchema),
29
- });
30
- const StoredPosixStateSchema = z.object({
31
- buffer: z.array(PosixTreeNodeSchema),
32
- foldNode: StoredPosixFoldNodeRootSchema,
25
+ const StoredPosixStateSchema = z
26
+ .object({
27
+ root: PosixBranchTreeNodeSchema,
28
+ foldRoot: StoredPosixFoldNodeSchema,
33
29
  cursor: PosixCursorSchema,
30
+ })
31
+ .refine((state) => state.root.id !== state.foldRoot.id, {
32
+ message: 'The fold root ID must match the tree root ID.',
34
33
  });
35
34
  // ---*--- Codec ---*---
36
35
  function encodePosixFoldNode(node) {
37
36
  return {
38
37
  id: node.id,
39
38
  children: node.children.map(encodePosixFoldNode),
40
- folds: [...node.folds],
39
+ foldedChildren: [...node.foldedChildren],
41
40
  };
42
41
  }
43
42
  function decodePosixFoldNode(node) {
44
43
  return {
45
44
  id: node.id,
46
45
  children: node.children.map(decodePosixFoldNode),
47
- folds: new Set(node.folds),
48
- };
49
- }
50
- function encodePosixFoldNodeRoot(root) {
51
- return {
52
- children: root.children.map(encodePosixFoldNode),
53
- folds: [...root.folds],
54
- };
55
- }
56
- function decodePosixFoldNodeRoot(root) {
57
- return {
58
- children: root.children.map(decodePosixFoldNode),
59
- folds: new Set(root.folds),
46
+ foldedChildren: node.foldedChildren.map(decodePosixFoldNode),
60
47
  };
61
48
  }
62
49
  const posixStateCodec = {
63
50
  schema: StoredPosixStateSchema,
64
51
  encode(state) {
65
52
  return {
66
- buffer: state.buffer,
67
- foldNode: encodePosixFoldNodeRoot(state.foldNode),
53
+ root: state.root,
54
+ foldRoot: encodePosixFoldNode(state.foldRoot),
68
55
  cursor: state.cursor,
69
56
  };
70
57
  },
71
58
  decode(state) {
72
59
  return {
73
- buffer: state.buffer,
74
- foldNode: decodePosixFoldNodeRoot(state.foldNode),
60
+ root: state.root,
61
+ foldRoot: decodePosixFoldNode(state.foldRoot),
75
62
  cursor: state.cursor,
76
63
  };
77
64
  },
78
65
  };
79
66
  // --- Creating Posix App API ---
67
+ /**
68
+ * Creates the AppApi for the POSIX app.
69
+ *
70
+ * @param directory - optional directory path for loading the app not at the cwd.
71
+ */
80
72
  export function loadPosixAppApi(directory) {
81
73
  const unixAbsPath = getUnixAbsPath(directory ?? process.cwd());
74
+ const rootId = PosixNameSchema.parse(basename(unixAbsPath));
82
75
  const apis = createAppApis();
83
76
  return {
84
77
  appId: 'posix',
85
- name: `Posix(${unixAbsPath})`,
86
- emptyForestMessage: 'The directory is empty.',
78
+ name: unixAbsPath,
79
+ rootId,
80
+ emptyRootMessage: 'The directory is empty.',
87
81
  loadBranches: (path) => {
88
82
  const address = join(unixAbsPath, ...path);
89
83
  return getDirEntries(address);
90
84
  },
85
+ createRoot: async () => {
86
+ return {
87
+ id: rootId,
88
+ kind: 'directory',
89
+ children: null
90
+ };
91
+ },
91
92
  subscribeToResync: createFsResyncSubscription(join(unixAbsPath)),
93
+ // The key for the Posix app instance is just the directory working in:
92
94
  viewKey: unixAbsPath,
93
95
  viewApi: createFileViewApi({
94
96
  directory: posixAppViewsDirectory,
package/dist/ui/App.d.ts CHANGED
@@ -12,6 +12,6 @@ type AppProps<Id extends SerializableKey, BufferNode extends TreeNode<Id, Buffer
12
12
  clipboard?: (value: string) => void;
13
13
  onError?: (error: Error) => void;
14
14
  };
15
- export declare function App<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>, ViewKey = string>({ appApi, initialState, reducer, intentToEffect, effectToAction, stdout, clipboard, onError, }: AppProps<Id, BufferNode, ViewKey>): import("react").JSX.Element | null;
15
+ export declare function App<Id extends SerializableKey, Node extends TreeNode<Id, Node>, ViewKey = string>({ appApi, initialState, reducer, intentToEffect, effectToAction, stdout, clipboard, onError, }: AppProps<Id, Node, ViewKey>): import("react").JSX.Element | null;
16
16
  export {};
17
17
  //# sourceMappingURL=App.d.ts.map
@@ -1 +1 @@
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;AAOpD,KAAK,QAAQ,CACX,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,IACd;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACxC,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,OAAO,GAAG,MAAM,EAChB,EACA,MAAM,EACN,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,sCAgOnC"}
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;AAOpD,KAAK,QAAQ,CACX,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,IACd;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACxC,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,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAC/B,OAAO,GAAG,MAAM,EAChB,EACA,MAAM,EACN,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,sCA4O7B"}
package/dist/ui/App.js CHANGED
@@ -14,16 +14,15 @@ export function App({ appApi, initialState, reducer, intentToEffect, effectToAct
14
14
  // Give `subscribeToResync` callback a way to see current state:
15
15
  const stateRef = useRef(state);
16
16
  stateRef.current = state;
17
- const navigation = useMemo(() => appApi.navNodeApi.from(state.buffer, state.foldNode), [appApi.navNodeApi, state.buffer, state.foldNode]);
17
+ const navEntry = useMemo(() => appApi.navNodeApi.from(state.root, state.foldRoot), [appApi.navNodeApi, state.root, state.foldRoot]);
18
18
  const [inputState, setInputState] = useState({
19
19
  inputMode: 'normal',
20
20
  normalBuffer: '',
21
21
  });
22
22
  const { rows: terminalRows } = useWindowSize();
23
- const view = useView(navigation, state, appApi.cursorApi, terminalRows);
23
+ const view = useView(navEntry, state, appApi.navNodeApi, appApi.cursorApi, terminalRows);
24
24
  const [exitStatus, setExitStatus] = useState();
25
- // Subscribe to directory watcher, do not
26
- // resubscribe on every state change.
25
+ // Subscribe to directory watcher, do not resubscribe on every state change.
27
26
  useEffect(() => {
28
27
  let active = true;
29
28
  const unsubscribe = appApi.subscribeToResync(() => {
@@ -56,7 +55,7 @@ export function App({ appApi, initialState, reducer, intentToEffect, effectToAct
56
55
  }
57
56
  switch (effect.effectType) {
58
57
  case 'dispatchEffectAction':
59
- const action = effectToAction(effect.action, navigation, state);
58
+ const action = effectToAction(effect.action, navEntry, state);
60
59
  if (action === undefined) {
61
60
  return;
62
61
  }
@@ -87,8 +86,13 @@ export function App({ appApi, initialState, reducer, intentToEffect, effectToAct
87
86
  return;
88
87
  }
89
88
  case 'peekFold':
90
- // TODO: Need a peek state to know what to display
89
+ // TODO: Need a peek state to know what to display
90
+ return;
91
91
  case 'emitVisibleLeavesPaths':
92
+ const navigation = navEntry.children;
93
+ if (navigation === null) {
94
+ return;
95
+ }
92
96
  const visibleLeavesPaths = appApi.navNodeApi.visibleLeavesPaths(navigation);
93
97
  stdout?.(JSON.stringify({
94
98
  type: 'displayed-leaves-paths',
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingApp.d.ts","sourceRoot":"","sources":["../../src/ui/LoadingApp.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,eAAe,EAEf,QAAQ,EACT,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAI9C,KAAK,eAAe,CAClB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,IACd;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACxC,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;AA0BF,wBAAgB,UAAU,CACxB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,EAChB,EACA,MAAM,EACN,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,+BAgG1C"}
1
+ {"version":3,"file":"LoadingApp.d.ts","sourceRoot":"","sources":["../../src/ui/LoadingApp.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,eAAe,EAEf,QAAQ,EACT,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAI9C,KAAK,eAAe,CAClB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,IACd;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACxC,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,UAAU,CACxB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,OAAO,GAAG,MAAM,EAChB,EACA,MAAM,EACN,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,+BA4F1C"}
@@ -5,37 +5,24 @@ import { createIntentToEffect, } from 'dirvi-lib';
5
5
  import { App } from './App.js';
6
6
  import { createReducer } from '../application/reducer.js';
7
7
  import { createEffectToAction } from '../application/effect-to-action.js';
8
- function createInitialState(rootBranches, createEmptyFoldRoot) {
9
- if (rootBranches.length === 0) {
10
- throw new Error('createInitialState cannot create a state for an empty forest');
11
- }
12
- return {
13
- buffer: rootBranches,
14
- foldNode: createEmptyFoldRoot(),
15
- cursor: {
16
- kind: 'entry',
17
- parentPath: [],
18
- entryId: rootBranches[0].id,
19
- },
20
- };
21
- }
22
8
  export function LoadingApp({ appApi, stdout, clipboard, onError, }) {
23
9
  const [initialState, setInitialState] = useState();
24
10
  const [empty, setEmpty] = useState(false);
25
11
  const [error, setError] = useState();
12
+ // Loading and setting initial state.
26
13
  useEffect(() => {
27
14
  let mounted = true;
28
15
  appApi
29
- .loadBranches([])
30
- .then((rootBranches) => {
16
+ .createRoot()
17
+ .then((root) => {
31
18
  if (!mounted) {
32
19
  return;
33
20
  }
34
- if (rootBranches.length === 0) {
35
- setEmpty(true);
36
- return;
37
- }
38
- setInitialState(createInitialState(rootBranches, () => appApi.foldNodeService.createEmptyRoot()));
21
+ setInitialState({
22
+ root,
23
+ foldRoot: appApi.foldNodeService.createEmptyNode(appApi.rootId),
24
+ cursor: [],
25
+ });
39
26
  })
40
27
  .catch((cause) => {
41
28
  const nextError = cause instanceof Error ? cause : new Error(String(cause));
@@ -58,7 +45,7 @@ export function LoadingApp({ appApi, stdout, clipboard, onError, }) {
58
45
  return _jsx(Text, { color: "red", children: error.message });
59
46
  }
60
47
  if (empty) {
61
- return _jsx(Text, { dimColor: true, children: appApi.emptyForestMessage });
48
+ return _jsx(Text, { dimColor: true, children: appApi.emptyRootMessage });
62
49
  }
63
50
  if (initialState === undefined) {
64
51
  return _jsx(Text, { dimColor: true, children: "Loading." });
@@ -1,7 +1,7 @@
1
- import { InputState } from 'dirvi-lib';
1
+ import { InputModeState } from 'dirvi-lib';
2
2
  export declare const STATUS_BAR_HEIGHT = 1;
3
3
  type StatusBarProps = {
4
- inputState: InputState;
4
+ inputState: InputModeState;
5
5
  };
6
6
  export declare function StatusBar({ inputState }: StatusBarProps): import("react").JSX.Element;
7
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../src/ui/components/StatusBar.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,+BAyBvD"}
1
+ {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../src/ui/components/StatusBar.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,cAAc,CAAC;CAC5B,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,+BAyBvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"ViewRowComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/ViewRowComponent.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,wBAAgB,gBAAgB,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,+BAkCzD"}
1
+ {"version":3,"file":"ViewRowComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/ViewRowComponent.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,wBAAgB,gBAAgB,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,+BAwBzD"}
@@ -1,13 +1,10 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  export function ViewRowComponent({ row }) {
4
- const prefix = '';
5
4
  switch (row.type) {
6
5
  case 'leaf':
7
- return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsxs(Text, { inverse: row.selected, children: [prefix, row.content] }) }));
6
+ return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsx(Text, { inverse: row.cursor, children: row.content }) }));
8
7
  case 'branch':
9
- return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsxs(Text, { inverse: row.selected, color: "blue", children: [prefix, row.content] }) }));
10
- case 'fold':
11
- return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsxs(Text, { inverse: row.selected, dimColor: true, children: [prefix, row.content] }) }));
8
+ return (_jsxs(Box, { paddingLeft: row.indent * 2, children: [_jsx(Text, { inverse: row.cursor, color: "blue", children: row.content }), row.foldedCount > 0 && (_jsx(Text, { color: "gray", dimColor: true, children: ` … ${row.foldedCount} folded` }))] }));
12
9
  }
13
10
  }
@@ -1,5 +1,4 @@
1
- import { CursorApi, NavNode, SerializableKey, State, TreeNode } from 'dirvi-lib';
1
+ import { CursorApi, NavBranch, NavNodeApi, SerializableKey, State, TreeNode } from 'dirvi-lib';
2
2
  import { View } from '../view.js';
3
- export type UseView = <Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, state: State<Id, BufferNode>, terminalRows: number) => View;
4
- export declare function useView<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, state: State<Id, BufferNode>, cursorApi: CursorApi<Id>, terminalRows: number): View;
3
+ export declare function useView<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(navNode: NavBranch<Id>, state: State<Id, Node>, navNodeApi: NavNodeApi<Id, Node>, cursorApi: CursorApi<Id>, terminalRows: number): View;
5
4
  //# sourceMappingURL=useView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useView.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useView.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,OAAO,EACP,eAAe,EACf,KAAK,EACL,QAAQ,EACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,IAAI,EAAW,MAAM,YAAY,CAAC;AAI3C,MAAM,MAAM,OAAO,GAAG,CACpB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAE3C,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,EACnC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAC5B,YAAY,EAAE,MAAM,KACjB,IAAI,CAAC;AAIV,wBAAgB,OAAO,CACrB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAE3C,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,EACnC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAC5B,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,YAAY,EAAE,MAAM,GACnB,IAAI,CAoBN"}
1
+ {"version":3,"file":"useView.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useView.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAAE,SAAS,EACX,UAAU,EACnB,eAAe,EACf,KAAK,EACL,QAAQ,EACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,IAAI,EAAW,MAAM,YAAY,CAAC;AAM3C,wBAAgB,OAAO,CACrB,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAE/B,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,EACtB,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,EACtB,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,EAChC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,YAAY,EAAE,MAAM,GACnB,IAAI,CAyBN"}
@@ -3,12 +3,12 @@ import { useRef } from 'react';
3
3
  import { STATUS_BAR_HEIGHT } from '../components/StatusBar.js';
4
4
  // Hooks that keeps Ref of the viewport's start, and returns View sliced to
5
5
  // only the rows that should be visible.
6
- export function useView(navigation, state, cursorApi, terminalRows) {
6
+ export function useView(navNode, state, navNodeApi, cursorApi, terminalRows) {
7
7
  const viewportStartRef = useRef(0);
8
8
  const viewportHeight = Math.max(1, terminalRows - STATUS_BAR_HEIGHT);
9
- const rows = View.createRows(navigation, state.cursor, cursorApi);
9
+ const rows = View.createRows(navNode, navNodeApi, state.cursor, cursorApi);
10
10
  viewportStartRef.current = viewportStart(rows, viewportHeight, viewportStartRef.current);
11
- return View.create(navigation, state.cursor, cursorApi, viewportHeight, viewportStartRef.current);
11
+ return View.create(navNode, navNodeApi, state.cursor, cursorApi, viewportHeight, viewportStartRef.current);
12
12
  }
13
13
  function viewportStart(rows, viewportHeight, currentViewportStart) {
14
14
  const scrollMargin = 3;
package/dist/ui/view.d.ts CHANGED
@@ -1,19 +1,24 @@
1
- import { CursorApi, SerializableKey, TreeNode } from 'dirvi-lib';
2
- import { Cursor, NavNode } from 'dirvi-lib';
3
- export type ViewRowType = 'leaf' | 'branch' | 'fold';
4
- export type ViewRow = {
1
+ import { CursorApi, NavBranch, NavNodeApi, SerializableKey, TreeNode } from 'dirvi-lib';
2
+ import { Cursor } from 'dirvi-lib';
3
+ type ViewRowBase = {
5
4
  id: string;
6
5
  indent: number;
7
6
  selected: boolean;
8
7
  cursor: boolean;
9
8
  content: string;
10
- type: ViewRowType;
11
9
  };
10
+ export type ViewRow = (ViewRowBase & {
11
+ type: 'leaf';
12
+ }) | (ViewRowBase & {
13
+ type: 'branch';
14
+ foldedCount: number;
15
+ });
12
16
  export type View = {
13
17
  rows: ViewRow[];
14
18
  };
15
19
  export declare const View: {
16
- createRows<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, cursor: Cursor<Id>, cursorApi: CursorApi<Id>): ViewRow[];
17
- create<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, cursor: Cursor<Id>, cursorApi: CursorApi<Id>, viewportHeight: number, viewportStart: number): View;
20
+ createRows<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(rootNode: NavBranch<Id>, navNodeApi: NavNodeApi<Id, Node>, cursor: Cursor<Id>, cursorApi: CursorApi<Id>): ViewRow[];
21
+ create<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(rootNode: NavBranch<Id>, navNodeApi: NavNodeApi<Id, Node>, cursor: Cursor<Id>, cursorApi: CursorApi<Id>, viewportHeight: number, viewportStart: number): View;
18
22
  };
23
+ export {};
19
24
  //# sourceMappingURL=view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/ui/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,MAAM,EAAyB,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAMrD,MAAM,MAAM,OAAO,GAAG;IAEpB,EAAE,EAAE,MAAM,CAAC;IAGX,MAAM,EAAE,MAAM,CAAC;IAGf,QAAQ,EAAE,OAAO,CAAC;IAGlB,MAAM,EAAE,OAAO,CAAC;IAGhB,OAAO,EAAE,MAAM,CAAC;IAGhB,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,IAAI;eAEb,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,cAE/B,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,UAC3B,MAAM,CAAC,EAAE,CAAC,aACP,SAAS,CAAC,EAAE,CAAC,GACvB,OAAO,EAAE;WAKV,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,cAE/B,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,UAC3B,MAAM,CAAC,EAAE,CAAC,aACP,SAAS,CAAC,EAAE,CAAC,kBACR,MAAM,iBACP,MAAM,GACpB,IAAI;CAOR,CAAC"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/ui/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACxF,OAAO,EAAE,MAAM,EAAqB,MAAM,WAAW,CAAC;AAEtD,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,OAAO,GACf,CAAC,WAAW,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,GACF,CAAC,WAAW,GAAG;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAAC;AAEP,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,IAAI;eAEb,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,YAErB,SAAS,CAAC,EAAE,CAAC,cACX,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,UACxB,MAAM,CAAC,EAAE,CAAC,aACP,SAAS,CAAC,EAAE,CAAC,GACvB,OAAO,EAAE;WAqBV,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,YAErB,SAAS,CAAC,EAAE,CAAC,cACX,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,UACxB,MAAM,CAAC,EAAE,CAAC,aACP,SAAS,CAAC,EAAE,CAAC,kBACR,MAAM,iBACP,MAAM,GACpB,IAAI;CAOR,CAAC"}
package/dist/ui/view.js CHANGED
@@ -1,10 +1,17 @@
1
- import { isNavBranch } from 'dirvi-lib';
2
1
  export const View = {
3
- createRows(navigation, cursor, cursorApi) {
4
- return viewRowsAtNode(navigation, [], cursor, cursorApi);
2
+ createRows(rootNode, navNodeApi, cursor, cursorApi) {
3
+ const rootCursor = [];
4
+ const rootRow = viewRowForEntry(rootNode, null, cursorApi.equal(cursor, rootCursor), navNodeApi);
5
+ if (rootNode.children === null) {
6
+ return [rootRow];
7
+ }
8
+ return [
9
+ rootRow,
10
+ ...viewRowsAtNode(rootNode.children, [], navNodeApi, cursor, cursorApi),
11
+ ];
5
12
  },
6
- create(navigation, cursor, cursorApi, viewportHeight, viewportStart) {
7
- const rows = View.createRows(navigation, cursor, cursorApi);
13
+ create(rootNode, navNodeApi, cursor, cursorApi, viewportHeight, viewportStart) {
14
+ const rows = View.createRows(rootNode, navNodeApi, cursor, cursorApi);
8
15
  return {
9
16
  rows: rows.slice(viewportStart, viewportStart + viewportHeight),
10
17
  };
@@ -20,79 +27,48 @@ export const View = {
20
27
  * [] root directory
21
28
  * ['src'] root/src
22
29
  * ['src', 'lib'] root/src/lib
23
- *
24
- * The function uses a depth-first, pre-order traversal:
25
- *
26
- * 1. Add an entry's row.
27
- * 2. If the entry is an opened branch, recursively add its children.
28
- * 3. After all visible entries, add the directory's fold row.
29
- *
30
- * This ordering is what makes the flat row list visually represent a tree.
31
30
  */
32
- function viewRowsAtNode(node, parentPath, cursor, cursorApi) {
31
+ function viewRowsAtNode(node, parentPath, navNodeApi, cursor, cursorApi) {
33
32
  const rows = [];
34
- /*
35
- * `node.entries` contains only the entries currently visible in this
36
- * directory. Folded entries are kept separately in `node.foldedEntries`
37
- * and are represented by one fold row added below.
38
- */
39
33
  for (const entry of node.entries) {
40
- const entryCursor = {
41
- kind: 'entry',
42
- parentPath,
43
- entryId: entry.id,
44
- };
45
- // If cursor at entry, needs to be rendered differently
34
+ const entryCursor = [...parentPath, entry.id];
46
35
  const isCursor = cursorApi.equal(cursor, entryCursor);
47
- // Add entry before visiting branches
48
- rows.push(viewRowForEntry(entry, parentPath, isCursor));
36
+ rows.push(viewRowForEntry(entry, parentPath, isCursor, navNodeApi));
49
37
  // Case where there's no branches
50
- if (!isNavBranch(entry) || entry.children === null) {
38
+ if (!navNodeApi.entryIsBranch(entry) || entry.children === null) {
51
39
  continue;
52
40
  }
53
- rows.push(...viewRowsAtNode(entry.children, [...parentPath, entry.id], cursor, cursorApi));
54
- }
55
- /*
56
- * Folded entries are not rendered individually. They are represented
57
- * by one synthetic row placed after all visible entries and descendants
58
- * of this directory.
59
- */
60
- if (node.foldedEntries.length > 0) {
61
- const foldCursor = {
62
- kind: 'fold',
63
- parentPath,
64
- };
65
- const isCursor = cursorApi.equal(cursor, foldCursor);
66
- rows.push({
67
- id: foldId(parentPath),
68
- indent: parentPath.length,
69
- selected: isCursor,
70
- cursor: isCursor,
71
- content: `⋯ ${node.foldedEntries.length} folded`,
72
- type: 'fold',
73
- });
41
+ rows.push(...viewRowsAtNode(entry.children, [...parentPath, entry.id], navNodeApi, cursor, cursorApi));
74
42
  }
75
43
  return rows;
76
44
  }
77
- function viewRowForEntry(entry, parentPath, cursor) {
45
+ function viewRowForEntry(entry, parentPath, cursor, navNodeApi) {
46
+ const id = entryId(parentPath ?? [], entry.id);
47
+ const indent = parentPath ? parentPath.length + 1 : 0;
48
+ const selected = false;
49
+ if (navNodeApi.entryIsBranch(entry)) {
50
+ const foldedCount = entry.children === null || entry.children.folded === null
51
+ ? 0
52
+ : entry.children.folded.entries.length;
53
+ return {
54
+ id,
55
+ indent,
56
+ selected,
57
+ cursor,
58
+ content: `${String(entry.id)}/`,
59
+ type: 'branch',
60
+ foldedCount,
61
+ };
62
+ }
78
63
  return {
79
- id: entryId(parentPath, entry.id),
80
- indent: parentPath.length,
81
- selected: cursor,
64
+ id,
65
+ indent,
66
+ selected,
82
67
  cursor,
83
- content: content(entry),
84
- type: type(entry),
68
+ content: String(entry.id),
69
+ type: 'leaf',
85
70
  };
86
71
  }
87
72
  function entryId(parentPath, entryName) {
88
73
  return `entry:${JSON.stringify([...parentPath, entryName])}`;
89
74
  }
90
- function foldId(parentPath) {
91
- return `fold:${JSON.stringify(parentPath)}`;
92
- }
93
- function content(entry) {
94
- return isNavBranch(entry) ? `${String(entry.id)}/` : String(entry.id);
95
- }
96
- function type(entry) {
97
- return isNavBranch(entry) ? 'branch' : 'leaf';
98
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mak-98/dirvi-cli",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "A Vim-inspired TUI for browsing directories.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-or-later",
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "dependencies": {
15
15
  "commander": "^13.0.0",
16
- "dirvi-lib": "0.7.1",
16
+ "dirvi-lib": "0.8.0",
17
17
  "env-paths": "^4.0.0",
18
18
  "ink": "^7.1.1",
19
19
  "react": "^19.0.0",
@@ -31,6 +31,8 @@
31
31
  "start": "node dist/index.js",
32
32
  "typecheck": "tsc --noEmit",
33
33
  "format": "prettier . --write",
34
- "update-global": "npm install --global @mak-98/dirvi-cli@latest"
34
+ "update-global": "npm install --global @mak-98/dirvi-cli@latest",
35
+ "debug": "node --inspect=9229 --import=tsx src/index.tsx 2>/tmp/dirvi-debug.log",
36
+ "debug:watch": "tail -f /tmp/dirvi-debug.log"
35
37
  }
36
38
  }