dirvi-lib 0.7.0 → 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 (72) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/tree-surfer/cursor.d.ts +52 -26
  5. package/dist/tree-surfer/cursor.d.ts.map +1 -1
  6. package/dist/tree-surfer/cursor.js +19 -44
  7. package/dist/tree-surfer/fold-node/fold-node.arb.d.ts +2 -0
  8. package/dist/tree-surfer/fold-node/fold-node.arb.d.ts.map +1 -0
  9. package/dist/tree-surfer/fold-node/fold-node.arb.js +1 -0
  10. package/dist/tree-surfer/fold-node/fold-node.impl.d.ts +3 -4
  11. package/dist/tree-surfer/fold-node/fold-node.impl.d.ts.map +1 -1
  12. package/dist/tree-surfer/fold-node/fold-node.impl.js +73 -67
  13. package/dist/tree-surfer/fold-node/fold-node.schema.d.ts +3 -5
  14. package/dist/tree-surfer/fold-node/fold-node.schema.d.ts.map +1 -1
  15. package/dist/tree-surfer/fold-node/fold-node.schema.js +2 -9
  16. package/dist/tree-surfer/fold-node/fold-node.types.d.ts +169 -46
  17. package/dist/tree-surfer/fold-node/fold-node.types.d.ts.map +1 -1
  18. package/dist/tree-surfer/fold-node/tree-node.test.d.ts +2 -0
  19. package/dist/tree-surfer/fold-node/tree-node.test.d.ts.map +1 -0
  20. package/dist/tree-surfer/fold-node/tree-node.test.js +90 -0
  21. package/dist/tree-surfer/index.d.ts +3 -1
  22. package/dist/tree-surfer/index.d.ts.map +1 -1
  23. package/dist/tree-surfer/index.js +3 -1
  24. package/dist/tree-surfer/nav-node/nav-node.impl.d.ts +5 -5
  25. package/dist/tree-surfer/nav-node/nav-node.impl.d.ts.map +1 -1
  26. package/dist/tree-surfer/nav-node/nav-node.impl.js +84 -99
  27. package/dist/tree-surfer/nav-node/nav-node.types.d.ts +62 -34
  28. package/dist/tree-surfer/nav-node/nav-node.types.d.ts.map +1 -1
  29. package/dist/tree-surfer/nav-node/nav-node.types.js +1 -3
  30. package/dist/tree-surfer/state/state.impl.d.ts +7 -0
  31. package/dist/tree-surfer/state/state.impl.d.ts.map +1 -0
  32. package/dist/tree-surfer/state/state.impl.js +89 -0
  33. package/dist/tree-surfer/state/state.schemas.d.ts +9 -0
  34. package/dist/tree-surfer/state/state.schemas.d.ts.map +1 -0
  35. package/dist/tree-surfer/state/state.schemas.js +8 -0
  36. package/dist/tree-surfer/state/state.types.d.ts +21 -0
  37. package/dist/tree-surfer/state/state.types.d.ts.map +1 -0
  38. package/dist/tree-surfer/state/state.types.js +1 -0
  39. package/dist/tree-surfer/tree-node/tree-node.arb.d.ts +82 -17
  40. package/dist/tree-surfer/tree-node/tree-node.arb.d.ts.map +1 -1
  41. package/dist/tree-surfer/tree-node/tree-node.arb.debug.test.d.ts +2 -0
  42. package/dist/tree-surfer/tree-node/tree-node.arb.debug.test.d.ts.map +1 -0
  43. package/dist/tree-surfer/tree-node/tree-node.arb.debug.test.js +34 -0
  44. package/dist/tree-surfer/tree-node/tree-node.arb.js +164 -56
  45. package/dist/tree-surfer/tree-node/tree-node.impl.d.ts +1 -1
  46. package/dist/tree-surfer/tree-node/tree-node.impl.d.ts.map +1 -1
  47. package/dist/tree-surfer/tree-node/tree-node.impl.js +41 -48
  48. package/dist/tree-surfer/tree-node/tree-node.schemas.d.ts +2 -2
  49. package/dist/tree-surfer/tree-node/tree-node.schemas.d.ts.map +1 -1
  50. package/dist/tree-surfer/tree-node/tree-node.schemas.js +2 -2
  51. package/dist/tree-surfer/tree-node/tree-node.test.js +144 -76
  52. package/dist/tree-surfer/tree-node/tree-node.types.d.ts +89 -107
  53. package/dist/tree-surfer/tree-node/tree-node.types.d.ts.map +1 -1
  54. package/dist/tree-surfer-ui/application/intent-to-effect.d.ts +6 -2
  55. package/dist/tree-surfer-ui/application/intent-to-effect.d.ts.map +1 -1
  56. package/dist/tree-surfer-ui/application/intent-to-effect.js +1 -6
  57. package/dist/tree-surfer-ui/application/user-input-to-intent.d.ts +10 -2
  58. package/dist/tree-surfer-ui/application/user-input-to-intent.d.ts.map +1 -1
  59. package/dist/tree-surfer-ui/application/user-input-to-intent.js +12 -5
  60. package/dist/tree-surfer-ui/domain/effect.d.ts +9 -8
  61. package/dist/tree-surfer-ui/domain/effect.d.ts.map +1 -1
  62. package/dist/tree-surfer-ui/domain/index.d.ts +1 -1
  63. package/dist/tree-surfer-ui/domain/index.d.ts.map +1 -1
  64. package/dist/tree-surfer-ui/domain/index.js +1 -1
  65. package/dist/tree-surfer-ui/domain/{input-state.d.ts → input-mode-state.d.ts} +2 -2
  66. package/dist/tree-surfer-ui/domain/input-mode-state.d.ts.map +1 -0
  67. package/package.json +3 -2
  68. package/dist/tree-surfer/state.d.ts +0 -25
  69. package/dist/tree-surfer/state.d.ts.map +0 -1
  70. package/dist/tree-surfer/state.js +0 -118
  71. package/dist/tree-surfer-ui/domain/input-state.d.ts.map +0 -1
  72. /package/dist/tree-surfer-ui/domain/{input-state.js → input-mode-state.js} +0 -0
@@ -1,44 +1,76 @@
1
- import { CursorKind } from '../cursor.js';
2
- import { isNavBranch, } from './nav-node.types.js';
3
- export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
4
- const navNodeApi = {
5
- // Return a nav node from the BufferNode tree and FoldNode tree.
6
- from(entries, foldNode) {
7
- const visibleEntriesSoFar = [];
8
- const foldedEntriesSoFar = [];
9
- for (const entry of entries) {
10
- // Find the node
11
- let navigationEntry;
12
- if (treeNodeApi.isBranch(entry)) {
13
- // If fold node does not have children (recursively no folds),
14
- // then just use empty.
15
- const childFoldNode = foldNodeApi.getChildById(foldNode, entry.id) ??
16
- createEmptyFoldNode();
17
- navigationEntry = {
18
- ...entry,
19
- children: entry.children === null
20
- ? null
21
- : navNodeApi.from(entry.children, childFoldNode),
22
- };
23
- }
24
- else if (treeNodeApi.isLeaf(entry)) {
25
- navigationEntry = entry;
26
- }
27
- else {
28
- // This should be unreachable if BufferNode correctly extends TreeNode.
29
- throw new Error('Unsupported tree node');
30
- }
31
- if (foldNode.folds.has(entry.id)) {
32
- foldedEntriesSoFar.push(navigationEntry);
33
- }
34
- else {
35
- visibleEntriesSoFar.push(navigationEntry);
36
- }
1
+ export function createNavNodeApi(treeNodeApi, foldNodeService, cursorApi) {
2
+ function entryIsBranch(entry) {
3
+ return 'children' in entry;
4
+ }
5
+ function createNavNode(entries, foldRoot, parentPath) {
6
+ const visibleEntries = [];
7
+ const foldedEntries = [];
8
+ const foldedEntryIds = foldRoot === undefined
9
+ ? undefined
10
+ : foldNodeService.foldedEntriesAtPath(foldRoot, parentPath);
11
+ for (const entry of entries) {
12
+ const entryPath = [...parentPath, entry.id];
13
+ const isFolded = foldRoot !== undefined &&
14
+ foldNodeService.getIfEntryFoldedAtPath(foldRoot, parentPath, entry.id);
15
+ const navigationEntry = createNavEntry(entry, foldRoot, entryPath);
16
+ if (isFolded) {
17
+ foldedEntries.push(navigationEntry);
18
+ }
19
+ else {
20
+ visibleEntries.push(navigationEntry);
37
21
  }
22
+ }
23
+ return {
24
+ entries: visibleEntries,
25
+ folded: foldedEntryIds === undefined || foldedEntryIds.length === 0
26
+ ? null
27
+ : {
28
+ entries: foldedEntries,
29
+ },
30
+ };
31
+ }
32
+ function createNavEntry(entry, foldRoot, entryPath) {
33
+ if (treeNodeApi.isLeaf(entry)) {
38
34
  return {
39
- entries: visibleEntriesSoFar,
40
- foldedEntries: foldedEntriesSoFar,
35
+ id: entry.id,
41
36
  };
37
+ }
38
+ return createNavBranch(entry, foldRoot, entryPath);
39
+ }
40
+ function createNavBranch(entry, foldRoot, entryPath) {
41
+ if (!treeNodeApi.isBranch(entry)) {
42
+ throw new Error('Navigation root must be a branch');
43
+ }
44
+ return {
45
+ id: entry.id,
46
+ children: entry.children === null
47
+ ? null
48
+ : createNavNode(entry.children, foldRoot, entryPath),
49
+ };
50
+ }
51
+ function rootCursors(rootNode) {
52
+ if (rootNode.children === null) {
53
+ return [[]];
54
+ }
55
+ return [[], ...cursorsInNode(rootNode.children, [])];
56
+ }
57
+ function cursorsInNode(node, parentPath) {
58
+ const cursors = [];
59
+ for (const entry of node.entries) {
60
+ const entryPath = [...parentPath, entry.id];
61
+ cursors.push(entryPath);
62
+ if (!entryIsBranch(entry) || entry.children === null) {
63
+ continue;
64
+ }
65
+ cursors.push(...cursorsInNode(entry.children, entryPath));
66
+ }
67
+ return cursors;
68
+ }
69
+ const navNodeApi = {
70
+ entryIsBranch,
71
+ // Return a nav node from the TreeNode and FoldNode trees.
72
+ from(root, foldRoot) {
73
+ return createNavBranch(root, foldRoot, []);
42
74
  },
43
75
  getNodeAtPath(navigation, path) {
44
76
  const [currentId, ...remainingPath] = path;
@@ -46,11 +78,11 @@ export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
46
78
  if (currentId === undefined) {
47
79
  return navigation;
48
80
  }
49
- const entry = [...navigation.entries, ...navigation.foldedEntries].find((candidate) => candidate.id === currentId);
81
+ const entry = navigation.entries.find((candidate) => candidate.id === currentId);
50
82
  if (entry === undefined) {
51
83
  return undefined;
52
84
  }
53
- if (!isNavBranch(entry)) {
85
+ if (!entryIsBranch(entry)) {
54
86
  return undefined;
55
87
  }
56
88
  // The directory has not been loaded/opened.
@@ -64,20 +96,20 @@ export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
64
96
  if (currentId === undefined) {
65
97
  return undefined;
66
98
  }
67
- const entry = [...navigation.entries, ...navigation.foldedEntries].find((candidate) => candidate.id === currentId);
99
+ const entry = navigation.entries.find((candidate) => candidate.id === currentId);
68
100
  if (entry === undefined) {
69
101
  return undefined;
70
102
  }
71
103
  if (remainingPath.length === 0) {
72
104
  return entry;
73
105
  }
74
- if (!isNavBranch(entry) || entry.children === null) {
106
+ if (!entryIsBranch(entry) || entry.children === null) {
75
107
  return undefined;
76
108
  }
77
109
  return navNodeApi.getEntryAtPath(entry.children, remainingPath);
78
110
  },
79
111
  nextCursor(rootNode, cursor) {
80
- const cursors = cursorsInNode(rootNode, []);
112
+ const cursors = rootCursors(rootNode);
81
113
  const currentIndex = cursors.findIndex((candidate) => cursorApi.equal(candidate, cursor));
82
114
  if (currentIndex === -1 || currentIndex === cursors.length - 1) {
83
115
  return undefined;
@@ -85,7 +117,7 @@ export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
85
117
  return cursors[currentIndex + 1];
86
118
  },
87
119
  previousCursor(rootNode, cursor) {
88
- const cursors = cursorsInNode(rootNode, []);
120
+ const cursors = rootCursors(rootNode);
89
121
  const currentIndex = cursors.findIndex((candidate) => cursorApi.equal(candidate, cursor));
90
122
  if (currentIndex <= 0) {
91
123
  return undefined;
@@ -93,52 +125,32 @@ export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
93
125
  return cursors[currentIndex - 1];
94
126
  },
95
127
  cursorAfterFold(rootNode, cursor) {
96
- if (cursorApi.isFold(cursor)) {
128
+ if (cursor.length === 0) {
97
129
  return undefined;
98
130
  }
99
- const cursors = cursorsInNode(rootNode, []);
131
+ const cursors = rootCursors(rootNode);
100
132
  const currentIndex = cursors.findIndex((candidate) => cursorApi.equal(candidate, cursor));
101
133
  if (currentIndex === -1) {
102
134
  return undefined;
103
135
  }
104
- const foldedPath = [...cursor.parentPath, cursor.entryId];
105
136
  for (let index = currentIndex + 1; index < cursors.length; index += 1) {
106
137
  const candidate = cursors[index];
107
138
  if (candidate === undefined) {
108
139
  continue;
109
140
  }
110
- if (cursorApi.cursorBelongsToSubtree(candidate, foldedPath)) {
141
+ if (cursorApi.cursorBelongsToSubtree(candidate, cursor)) {
111
142
  continue;
112
143
  }
113
144
  return candidate;
114
145
  }
115
146
  // The fold row will be created at the end of this directory.
116
- return {
117
- kind: CursorKind.Fold,
118
- parentPath: cursor.parentPath,
119
- };
147
+ return [...cursor];
120
148
  },
121
- parentCursor(navigation, cursor) {
122
- const parentPath = cursor.parentPath;
123
- // The cursor is already in the root directory.
124
- if (parentPath.length === 0) {
125
- return undefined;
126
- }
127
- const entryId = parentPath.at(-1);
128
- if (entryId === undefined) {
129
- return undefined;
130
- }
131
- const containingPath = parentPath.slice(0, -1);
132
- const entry = navNodeApi.getEntryAtPath(navigation, [...parentPath]);
133
- // The parent path must identify a navigable branch.
134
- if (entry === undefined || !isNavBranch(entry)) {
149
+ parentCursor(_navigation, cursor) {
150
+ if (cursor.length === 0) {
135
151
  return undefined;
136
152
  }
137
- return {
138
- kind: CursorKind.Entry,
139
- parentPath: containingPath,
140
- entryId: entryId,
141
- };
153
+ return cursor.slice(0, -1);
142
154
  },
143
155
  cursors(navigation, parentPath = []) {
144
156
  return cursorsInNode(navigation, parentPath);
@@ -147,7 +159,7 @@ export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
147
159
  const paths = [];
148
160
  for (const entry of navigation.entries) {
149
161
  const entryPath = [...parentPath, entry.id];
150
- if (isNavBranch(entry)) {
162
+ if (entryIsBranch(entry)) {
151
163
  // A null children value means that the directory has not been
152
164
  // loaded/opened, so there are no visible descendant files.
153
165
  if (entry.children !== null) {
@@ -162,30 +174,3 @@ export function createNavNodeApi(treeNodeApi, foldNodeApi, cursorApi) {
162
174
  };
163
175
  return navNodeApi;
164
176
  }
165
- function createEmptyFoldNode() {
166
- return {
167
- children: [],
168
- folds: new Set(),
169
- };
170
- }
171
- function cursorsInNode(node, parentPath) {
172
- const cursors = [];
173
- for (const entry of node.entries) {
174
- cursors.push({
175
- kind: CursorKind.Entry,
176
- parentPath,
177
- entryId: entry.id,
178
- });
179
- if (!isNavBranch(entry) || entry.children === null) {
180
- continue;
181
- }
182
- cursors.push(...cursorsInNode(entry.children, [...parentPath, entry.id]));
183
- }
184
- if (node.foldedEntries.length > 0) {
185
- cursors.push({
186
- kind: CursorKind.Fold,
187
- parentPath,
188
- });
189
- }
190
- return cursors;
191
- }
@@ -1,42 +1,70 @@
1
- import { LeafTreeNode, SerializableKey, TreeNode } from '../tree-node/tree-node.types.js';
2
- import { FoldNodeRoot } from '../fold-node/fold-node.types.js';
1
+ import { BranchTreeNode, SerializableKey, TreeNode } from '../tree-node/tree-node.types.js';
3
2
  import { Cursor } from '../cursor.js';
4
- /**
5
- * A navigation entry preserves the original buffer-node properties, but
6
- * replaces a branch's buffer children with a NavNode.
7
- */
8
- export type NavEntry<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = NavLeaf<Id, BufferNode> | NavBranch<Id, BufferNode>;
9
- export type NavLeaf<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = BufferNode & LeafTreeNode<Id>;
10
- type ReplaceChildren<Node, Children> = Node extends {
11
- children: unknown;
12
- } ? Omit<Node, 'children'> & {
13
- children: Children;
14
- } : never;
15
- export type NavBranch<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = ReplaceChildren<BufferNode, NavNode<Id, BufferNode> | null>;
16
- export declare function isNavBranch<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(entry: NavEntry<Id, BufferNode>): entry is NavBranch<Id, BufferNode>;
17
- /**
18
- * The derived tree as navigated: list of visible entries and folded entries.
19
- */
20
- export type NavNode<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = {
3
+ import { FoldNode } from '../fold-node/fold-node.types.js';
4
+ export type NavNode<Id extends SerializableKey> = {
21
5
  /**
22
- * Currently visible entries.
6
+ * Synthetic UI entry representing folded children.
7
+ *
8
+ * It is absent when this directory has no folded children.
23
9
  */
24
- entries: NavEntry<Id, BufferNode>[];
10
+ folded: NavFoldEntry<Id> | null;
25
11
  /**
26
- * Currently loaded entries hidden by this directory's fold.
12
+ * Loaded entries that are not folded at this directory, corresponding to
13
+ * navigable rows. Entries retain the order from the TreeNode.
27
14
  */
28
- foldedEntries: NavEntry<Id, BufferNode>[];
15
+ entries: NavEntry<Id>[];
29
16
  };
30
- export type NavNodeApi<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = {
31
- from(entries: BufferNode[], foldNode: FoldNodeRoot<Id>): NavNode<Id, BufferNode>;
32
- getNodeAtPath(navigation: NavNode<Id, BufferNode>, path: Id[]): NavNode<Id, BufferNode> | undefined;
33
- getEntryAtPath(navigation: NavNode<Id, BufferNode>, path: Id[]): NavEntry<Id, BufferNode> | undefined;
34
- nextCursor(rootNode: NavNode<Id, BufferNode>, cursor: Cursor<Id>): Cursor<Id> | undefined;
35
- previousCursor(rootNode: NavNode<Id, BufferNode>, cursor: Cursor<Id>): Cursor<Id> | undefined;
36
- cursorAfterFold(rootNode: NavNode<Id, BufferNode>, cursor: Cursor<Id>): Cursor<Id> | undefined;
37
- parentCursor(navigation: NavNode<Id, BufferNode>, cursor: Cursor<Id>): Cursor<Id> | undefined;
38
- cursors(navigation: NavNode<Id, BufferNode>, parentPath?: Id[]): Cursor<Id>[];
39
- visibleLeavesPaths(navigation: NavNode<Id, BufferNode>, parentPath?: Id[]): Id[][];
17
+ export type NavFoldEntry<Id extends SerializableKey> = {
18
+ /**
19
+ * Folded entries that are present in the loaded TreeNode.
20
+ *
21
+ * This is information for rendering the folded row.
22
+ */
23
+ entries: NavEntry<Id>[];
24
+ };
25
+ export type NavEntry<Id extends SerializableKey> = NavLeaf<Id> | NavBranch<Id>;
26
+ export type NavLeaf<Id extends SerializableKey> = {
27
+ id: Id;
28
+ };
29
+ export type NavBranch<Id extends SerializableKey> = {
30
+ id: Id;
31
+ children: NavNode<Id> | null;
32
+ };
33
+ export type NavNodeApi<Id extends SerializableKey, Node extends TreeNode<Id, Node>> = {
34
+ /**
35
+ * Tests whether a navigation entry represents a branch.
36
+ *
37
+ * A branch is an entry with a `children` property. Its `children` value is
38
+ * `null` when the corresponding tree branch has not been loaded or opened.
39
+ */
40
+ entryIsBranch(entry: NavEntry<Id>): entry is NavBranch<Id>;
41
+ /**
42
+ * Creates a navigation projection of the tree node root.
43
+ *
44
+ * The root must be a branch node because navigation is rooted at a
45
+ * directory-like node. The returned navigation entry is always a `NavBranch`.
46
+ *
47
+ * The TreeNode is authoritative for:
48
+ *
49
+ * - entry identity and order;
50
+ * - whether the entry is a leaf or branch;
51
+ * - whether branch children are loaded; and
52
+ * - the loaded child entries.
53
+ *
54
+ * The optional FoldNode is authoritative for folding. When `foldRoot` is
55
+ * undefined, the projection contains no folded entries.
56
+ *
57
+ * A folded entry is omitted from its parent's navigable entries and is
58
+ * represented by that parent's synthetic `folded` entry instead.
59
+ */
60
+ from(root: Node & BranchTreeNode<Id, Node>, foldRoot: FoldNode<Id> | undefined): NavBranch<Id>;
61
+ getNodeAtPath(navigation: NavNode<Id>, path: readonly Id[]): NavNode<Id> | undefined;
62
+ getEntryAtPath(navigation: NavNode<Id>, path: readonly Id[]): NavEntry<Id> | undefined;
63
+ nextCursor(rootNode: NavBranch<Id>, cursor: Cursor<Id>): Cursor<Id> | undefined;
64
+ previousCursor(rootNode: NavBranch<Id>, cursor: Cursor<Id>): Cursor<Id> | undefined;
65
+ cursorAfterFold(rootNode: NavBranch<Id>, cursor: Cursor<Id>): Cursor<Id> | undefined;
66
+ parentCursor(navigation: NavNode<Id>, cursor: Cursor<Id>): Cursor<Id> | undefined;
67
+ cursors(navigation: NavNode<Id>, parentPath?: Id[]): Cursor<Id>[];
68
+ visibleLeavesPaths(navigation: NavNode<Id>, parentPath?: readonly Id[]): Id[][];
40
69
  };
41
- export {};
42
70
  //# sourceMappingURL=nav-node.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nav-node.types.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/nav-node/nav-node.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nav-node.types.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/nav-node/nav-node.types.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1 @@
1
- export function isNavBranch(entry) {
2
- return 'children' in entry;
3
- }
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { SerializableKey, TreeNode, TreeNodeApi } from '../tree-node/tree-node.types.js';
2
+ import { FoldNodeApi } from '../fold-node/fold-node.types.js';
3
+ import { CursorApi } from '../cursor.js';
4
+ import { NavNodeApi } from '../nav-node/nav-node.types.js';
5
+ import { StateApi } from './state.types.js';
6
+ export declare function createStateApi<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(treeNodeApi: TreeNodeApi<Id, Node>, foldNodeApi: FoldNodeApi<Id>, cursorApi: CursorApi<Id>, navNodeApi: NavNodeApi<Id, Node>): StateApi<Id, Node>;
7
+ //# sourceMappingURL=state.impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.impl.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/state/state.impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACzG,OAAO,EAAY,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAU,SAAS,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAS,MAAM,kBAAkB,CAAC;AAEnD,wBAAgB,cAAc,CAC5B,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAE/B,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,EAClC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,EAC5B,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,GAC/B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,CAoIpB"}
@@ -0,0 +1,89 @@
1
+ export function createStateApi(treeNodeApi, foldNodeApi, cursorApi, navNodeApi) {
2
+ /**
3
+ * Create a new root and fold root.
4
+ *
5
+ * Works on fold root and descendant fold nodes using the generic.
6
+ *
7
+ * TODO: Later: Filter the fold node
8
+ */
9
+ async function reload(oldRoot, oldFoldNode, parentPath, loadBranches) {
10
+ // Do not load a branch that was already closed.
11
+ if (!treeNodeApi.isOpenBranch(oldRoot)) {
12
+ return {
13
+ root: oldRoot,
14
+ foldRoot: oldFoldNode,
15
+ };
16
+ }
17
+ const newChildren = await loadBranches(parentPath);
18
+ let newRoot = {
19
+ ...oldRoot,
20
+ children: newChildren,
21
+ };
22
+ let newFoldNode = oldFoldNode; // Start with assumption of no changes
23
+ for (const oldEntry of oldRoot.children) {
24
+ if (!treeNodeApi.isBranch(oldEntry) ||
25
+ !treeNodeApi.isOpenBranch(oldEntry)) {
26
+ continue;
27
+ }
28
+ const oldChildFoldNode = oldFoldNode === undefined
29
+ ? undefined
30
+ : foldNodeApi.getChildById(oldFoldNode, oldEntry.id);
31
+ const entryPath = [...parentPath, oldEntry.id];
32
+ const reloadedChild = await reload(oldEntry, oldChildFoldNode, entryPath, loadBranches);
33
+ const updatedRoot = treeNodeApi.modifyAtPath(newRoot, [oldEntry.id], (entry) => {
34
+ if (!treeNodeApi.isBranch(entry)) {
35
+ return undefined;
36
+ }
37
+ return {
38
+ ...entry,
39
+ children: reloadedChild.root.children,
40
+ };
41
+ });
42
+ if (updatedRoot !== undefined && treeNodeApi.isOpenBranch(updatedRoot)) {
43
+ newRoot = updatedRoot;
44
+ }
45
+ }
46
+ return {
47
+ root: newRoot,
48
+ foldRoot: newFoldNode,
49
+ };
50
+ }
51
+ /**
52
+ * Currently the cursor just goes to root.
53
+ *
54
+ * TODO: Cursor policy:
55
+ * 1. Keep the exact cursor if it survives,
56
+ * 2. Otherwise, choose the deepest surviving ancestor entry,
57
+ * 3. Otherwise, choose the nearest preceding old cursor that still survives,
58
+ * 4. Otherwise, choose the first new cursor.
59
+ * 5. If no cursor exists, use the root fold cursor.
60
+ */
61
+ function resyncCursor(oldState, newRoot, newFoldNode) {
62
+ return [];
63
+ }
64
+ return {
65
+ getNodeAtCursor(state) {
66
+ const path = cursorApi.getPath(state.cursor);
67
+ return treeNodeApi.getAtPath(state.root, path, (node) => node);
68
+ },
69
+ async resync(oldState, loadChildren) {
70
+ const reloaded = await reload(oldState.root, oldState.foldRoot, [], loadChildren);
71
+ /*
72
+ * `oldState.foldNode` is always present. At the root reload starts with
73
+ * that node, so `reloaded.foldNode` should also always be present.
74
+ *
75
+ * The fallback expresses the State invariant while satisfying the
76
+ * optional return type needed by recursive child reloads.
77
+ */
78
+ const foldRoot = reloaded.foldRoot ?? oldState.foldRoot;
79
+ const cursor = resyncCursor(oldState, reloaded.root, foldRoot) ?? [];
80
+ const root = reloaded.root;
81
+ return {
82
+ ...oldState,
83
+ root,
84
+ foldRoot,
85
+ cursor,
86
+ };
87
+ },
88
+ };
89
+ }
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ import { SerializableKey, TreeNode } from '../tree-node/tree-node.types.js';
3
+ import { State } from './state.types.js';
4
+ export declare function createStateSchema<Id extends SerializableKey, Node extends TreeNode<Id, Node>>(rootSchema: z.ZodType<State<Id, Node>['root']>, foldNodeRootSchema: z.ZodType<State<Id, Node>['foldRoot']>, cursorSchema: z.ZodType<State<Id, Node>['cursor']>): z.ZodObject<{
5
+ root: z.ZodType<Node & import("../tree-node/tree-node.types.js").BranchTreeNode<Id, Node>, unknown, z.core.$ZodTypeInternals<Node & import("../tree-node/tree-node.types.js").BranchTreeNode<Id, Node>, unknown>>;
6
+ foldRoot: z.ZodType<import("../index.js").FoldNode<Id>, unknown, z.core.$ZodTypeInternals<import("../index.js").FoldNode<Id>, unknown>>;
7
+ cursor: z.ZodType<import("../cursor.js").Cursor<Id>, unknown, z.core.$ZodTypeInternals<import("../cursor.js").Cursor<Id>, unknown>>;
8
+ }, z.core.$strip>;
9
+ //# sourceMappingURL=state.schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.schemas.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/state/state.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,wBAAgB,iBAAiB,CAC/B,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAE/B,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,EAC9C,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,EAC1D,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;;;;kBAOnD"}
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export function createStateSchema(rootSchema, foldNodeRootSchema, cursorSchema) {
3
+ return z.object({
4
+ root: rootSchema,
5
+ foldRoot: foldNodeRootSchema,
6
+ cursor: cursorSchema,
7
+ });
8
+ }
@@ -0,0 +1,21 @@
1
+ import type { Cursor } from '../cursor.js';
2
+ import { BranchTreeNode, SerializableKey, TreeNode } from '../tree-node/tree-node.types.js';
3
+ import type { FoldNode } from '../fold-node/fold-node.types.js';
4
+ export type State<Id extends SerializableKey, Node extends TreeNode<Id, Node>> = {
5
+ root: Node & BranchTreeNode<Id, Node>;
6
+ foldRoot: FoldNode<Id>;
7
+ cursor: Cursor<Id>;
8
+ };
9
+ export type StateApi<Id extends SerializableKey, Node extends TreeNode<Id, Node>> = {
10
+ getNodeAtCursor(state: State<Id, Node>): Node | undefined;
11
+ /**
12
+ * Reloads the root's children and all descendant branches that were loaded in
13
+ * the previous state.
14
+ *
15
+ * `loadBranches([])` loads the root's direct children.
16
+ * `loadBranches(['src'])` loads the children of `src`, where 'src' is the
17
+ * id of a root's child.
18
+ */
19
+ resync(oldState: State<Id, Node>, loadBranches: (path: Id[]) => Promise<Node[]>): Promise<State<Id, Node>>;
20
+ };
21
+ //# sourceMappingURL=state.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.types.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/state/state.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EACL,cAAc,EACd,eAAe,EACf,QAAQ,EACT,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAEhE,MAAM,MAAM,KAAK,CACf,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAC7B;IACF,IAAI,EAAE,IAAI,GAAG,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI;IAClF,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAE1D;;;;;;;OAOG;IACH,MAAM,CACJ,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,EACzB,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,GAC5C,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;CAC7B,CAAC"}
@@ -0,0 +1 @@
1
+ export {};