dirvi-lib 0.10.0 → 0.11.0-beta.12

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 (164) hide show
  1. package/dist/app/app-methods.d.ts +53 -0
  2. package/dist/app/app-methods.d.ts.map +1 -0
  3. package/dist/app/index.d.ts +1 -1
  4. package/dist/app/index.d.ts.map +1 -1
  5. package/dist/app/index.js +1 -1
  6. package/dist/app/reducer/effect-to-action.d.ts +2 -2
  7. package/dist/app/reducer/effect-to-action.d.ts.map +1 -1
  8. package/dist/app/reducer/effect-to-action.js +87 -32
  9. package/dist/app/reducer/reducer-action.d.ts +12 -6
  10. package/dist/app/reducer/reducer-action.d.ts.map +1 -1
  11. package/dist/app/reducer/reducer.d.ts +4 -2
  12. package/dist/app/reducer/reducer.d.ts.map +1 -1
  13. package/dist/app/reducer/reducer.js +106 -72
  14. package/dist/feedback/feedback.impl.d.ts.map +1 -1
  15. package/dist/feedback/feedback.types.d.ts +1 -1
  16. package/dist/feedback/feedback.types.d.ts.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/input/application/intent-to-effect.d.ts +2 -2
  21. package/dist/input/application/intent-to-effect.d.ts.map +1 -1
  22. package/dist/input/application/intent-to-effect.js +10 -11
  23. package/dist/input/application/parse-ex-command.d.ts +17 -0
  24. package/dist/input/application/parse-ex-command.d.ts.map +1 -0
  25. package/dist/input/application/{parse-command.js → parse-ex-command.js} +84 -54
  26. package/dist/input/application/parse-normal-command.d.ts +1 -1
  27. package/dist/input/application/parse-normal-command.d.ts.map +1 -1
  28. package/dist/input/domain/effect.d.ts +20 -9
  29. package/dist/input/domain/effect.d.ts.map +1 -1
  30. package/dist/state-and-nav/cursor.d.ts +89 -0
  31. package/dist/state-and-nav/cursor.d.ts.map +1 -0
  32. package/dist/state-and-nav/cursor.js +84 -0
  33. package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.impl.d.ts +1 -1
  34. package/dist/state-and-nav/fold-node/deprecated/fold-node.impl.d.ts.map +1 -0
  35. package/dist/state-and-nav/fold-node/deprecated/fold-node.test.d.ts.map +1 -0
  36. package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.test.js +2 -2
  37. package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.types.d.ts +2 -2
  38. package/dist/state-and-nav/fold-node/deprecated/fold-node.types.d.ts.map +1 -0
  39. package/dist/{trees → state-and-nav}/fold-node/fold-node.impl.d.ts +3 -3
  40. package/dist/state-and-nav/fold-node/fold-node.impl.d.ts.map +1 -0
  41. package/dist/{trees → state-and-nav}/fold-node/fold-node.impl.js +2 -2
  42. package/dist/{trees → state-and-nav}/fold-node/fold-node.model.d.ts +1 -1
  43. package/dist/state-and-nav/fold-node/fold-node.model.d.ts.map +1 -0
  44. package/dist/{trees → state-and-nav}/fold-node/fold-node.types.d.ts +2 -2
  45. package/dist/state-and-nav/fold-node/fold-node.types.d.ts.map +1 -0
  46. package/dist/state-and-nav/fold-node/index.d.ts.map +1 -0
  47. package/dist/state-and-nav/index.d.ts +16 -0
  48. package/dist/state-and-nav/index.d.ts.map +1 -0
  49. package/dist/state-and-nav/index.js +6 -0
  50. package/dist/state-and-nav/nav/nav.ordered.impl.d.ts +12 -0
  51. package/dist/state-and-nav/nav/nav.ordered.impl.d.ts.map +1 -0
  52. package/dist/state-and-nav/nav/nav.ordered.impl.js +77 -0
  53. package/dist/state-and-nav/nav/nav.traversal.d.ts +11 -0
  54. package/dist/state-and-nav/nav/nav.traversal.d.ts.map +1 -0
  55. package/dist/state-and-nav/nav/nav.traversal.js +204 -0
  56. package/dist/{trees/nav-node/nav-node.types.d.ts → state-and-nav/nav/nav.types.d.ts} +37 -23
  57. package/dist/state-and-nav/nav/nav.types.d.ts.map +1 -0
  58. package/dist/state-and-nav/nav/nav.unordered.impl.d.ts +13 -0
  59. package/dist/state-and-nav/nav/nav.unordered.impl.d.ts.map +1 -0
  60. package/dist/state-and-nav/nav/nav.unordered.impl.js +77 -0
  61. package/dist/state-and-nav/state/state.impl.d.ts +9 -0
  62. package/dist/state-and-nav/state/state.impl.d.ts.map +1 -0
  63. package/dist/{trees → state-and-nav}/state/state.impl.js +62 -9
  64. package/dist/state-and-nav/state/state.schemas.d.ts +2 -0
  65. package/dist/state-and-nav/state/state.schemas.d.ts.map +1 -0
  66. package/dist/state-and-nav/state/state.schemas.js +39 -0
  67. package/dist/state-and-nav/state/state.types.d.ts +72 -0
  68. package/dist/state-and-nav/state/state.types.d.ts.map +1 -0
  69. package/dist/state-and-nav/tree/deprecated/tree-node.arb.d.ts.map +1 -0
  70. package/dist/state-and-nav/tree/deprecated/tree-node.arb.debug.test.d.ts.map +1 -0
  71. package/dist/state-and-nav/tree/deprecated/tree-node.impl.d.ts.map +1 -0
  72. package/dist/state-and-nav/tree/deprecated/tree-node.schemas.d.ts.map +1 -0
  73. package/dist/state-and-nav/tree/deprecated/tree-node.test.d.ts.map +1 -0
  74. package/dist/state-and-nav/tree/deprecated/tree-node.types.d.ts.map +1 -0
  75. package/dist/state-and-nav/tree/index.d.ts +7 -0
  76. package/dist/state-and-nav/tree/index.d.ts.map +1 -0
  77. package/dist/state-and-nav/tree/index.js +1 -0
  78. package/dist/state-and-nav/tree/tree-node.arb.d.ts +94 -0
  79. package/dist/state-and-nav/tree/tree-node.arb.d.ts.map +1 -0
  80. package/dist/state-and-nav/tree/tree-node.arb.debug.test.d.ts +2 -0
  81. package/dist/state-and-nav/tree/tree-node.arb.debug.test.d.ts.map +1 -0
  82. package/dist/state-and-nav/tree/tree-node.arb.debug.test.js +54 -0
  83. package/dist/state-and-nav/tree/tree-node.arb.js +188 -0
  84. package/dist/state-and-nav/tree/tree-node.model.d.ts +194 -0
  85. package/dist/state-and-nav/tree/tree-node.model.d.ts.map +1 -0
  86. package/dist/state-and-nav/tree/tree-node.model.js +110 -0
  87. package/dist/state-and-nav/tree/tree-node.test.d.ts +2 -0
  88. package/dist/state-and-nav/tree/tree-node.test.d.ts.map +1 -0
  89. package/dist/state-and-nav/tree/tree-node.test.js +234 -0
  90. package/dist/{trees/tree-node/tree-node.impl.d.ts → state-and-nav/tree/tree.impl.d.ts} +3 -3
  91. package/dist/state-and-nav/tree/tree.impl.d.ts.map +1 -0
  92. package/dist/{trees/tree-node/tree-node.impl.js → state-and-nav/tree/tree.impl.js} +20 -17
  93. package/dist/{trees/tree-node/tree-node.types.d.ts → state-and-nav/tree/tree.types.d.ts} +67 -47
  94. package/dist/state-and-nav/tree/tree.types.d.ts.map +1 -0
  95. package/dist/state-and-nav/tree/tree.types.js +1 -0
  96. package/package.json +4 -3
  97. package/dist/app/app-api.d.ts +0 -74
  98. package/dist/app/app-api.d.ts.map +0 -1
  99. package/dist/app/app-api.js +0 -11
  100. package/dist/input/application/parse-command.d.ts +0 -14
  101. package/dist/input/application/parse-command.d.ts.map +0 -1
  102. package/dist/trees/cursor.d.ts +0 -59
  103. package/dist/trees/cursor.d.ts.map +0 -1
  104. package/dist/trees/cursor.js +0 -55
  105. package/dist/trees/fold-node/deprecated/fold-node.impl.d.ts.map +0 -1
  106. package/dist/trees/fold-node/deprecated/fold-node.test.d.ts.map +0 -1
  107. package/dist/trees/fold-node/deprecated/fold-node.types.d.ts.map +0 -1
  108. package/dist/trees/fold-node/fold-node.impl.d.ts.map +0 -1
  109. package/dist/trees/fold-node/fold-node.model.d.ts.map +0 -1
  110. package/dist/trees/fold-node/fold-node.types.d.ts.map +0 -1
  111. package/dist/trees/fold-node/index.d.ts.map +0 -1
  112. package/dist/trees/index.d.ts +0 -9
  113. package/dist/trees/index.d.ts.map +0 -1
  114. package/dist/trees/index.js +0 -8
  115. package/dist/trees/nav-node/nav-node.impl.d.ts +0 -29
  116. package/dist/trees/nav-node/nav-node.impl.d.ts.map +0 -1
  117. package/dist/trees/nav-node/nav-node.impl.js +0 -223
  118. package/dist/trees/nav-node/nav-node.types.d.ts.map +0 -1
  119. package/dist/trees/state/state.impl.d.ts +0 -8
  120. package/dist/trees/state/state.impl.d.ts.map +0 -1
  121. package/dist/trees/state/state.schemas.d.ts +0 -25
  122. package/dist/trees/state/state.schemas.d.ts.map +0 -1
  123. package/dist/trees/state/state.schemas.js +0 -17
  124. package/dist/trees/state/state.types.d.ts +0 -36
  125. package/dist/trees/state/state.types.d.ts.map +0 -1
  126. package/dist/trees/tree-node/deprecated/tree-node.arb.d.ts.map +0 -1
  127. package/dist/trees/tree-node/deprecated/tree-node.arb.debug.test.d.ts.map +0 -1
  128. package/dist/trees/tree-node/deprecated/tree-node.impl.d.ts.map +0 -1
  129. package/dist/trees/tree-node/deprecated/tree-node.schemas.d.ts.map +0 -1
  130. package/dist/trees/tree-node/deprecated/tree-node.test.d.ts.map +0 -1
  131. package/dist/trees/tree-node/deprecated/tree-node.types.d.ts.map +0 -1
  132. package/dist/trees/tree-node/index.d.ts +0 -5
  133. package/dist/trees/tree-node/index.d.ts.map +0 -1
  134. package/dist/trees/tree-node/index.js +0 -2
  135. package/dist/trees/tree-node/tree-node.arb.d.ts +0 -104
  136. package/dist/trees/tree-node/tree-node.arb.d.ts.map +0 -1
  137. package/dist/trees/tree-node/tree-node.arb.js +0 -188
  138. package/dist/trees/tree-node/tree-node.impl.d.ts.map +0 -1
  139. package/dist/trees/tree-node/tree-node.model.d.ts +0 -78
  140. package/dist/trees/tree-node/tree-node.model.d.ts.map +0 -1
  141. package/dist/trees/tree-node/tree-node.model.js +0 -37
  142. package/dist/trees/tree-node/tree-node.types.d.ts.map +0 -1
  143. /package/dist/{trees/fold-node/deprecated/fold-node.types.js → app/app-methods.js} +0 -0
  144. /package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.impl.js +0 -0
  145. /package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.test.d.ts +0 -0
  146. /package/dist/{trees/fold-node → state-and-nav/fold-node/deprecated}/fold-node.types.js +0 -0
  147. /package/dist/{trees → state-and-nav}/fold-node/fold-node.model.js +0 -0
  148. /package/dist/{trees/nav-node/nav-node.types.js → state-and-nav/fold-node/fold-node.types.js} +0 -0
  149. /package/dist/{trees → state-and-nav}/fold-node/index.d.ts +0 -0
  150. /package/dist/{trees → state-and-nav}/fold-node/index.js +0 -0
  151. /package/dist/{trees/state/state.types.js → state-and-nav/nav/nav.types.js} +0 -0
  152. /package/dist/{trees/tree-node/deprecated/tree-node.types.js → state-and-nav/state/state.types.js} +0 -0
  153. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.d.ts +0 -0
  154. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.debug.test.d.ts +0 -0
  155. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.debug.test.js +0 -0
  156. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.js +0 -0
  157. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.impl.d.ts +0 -0
  158. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.impl.js +0 -0
  159. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.schemas.d.ts +0 -0
  160. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.schemas.js +0 -0
  161. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.test.d.ts +0 -0
  162. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.test.js +0 -0
  163. /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.types.d.ts +0 -0
  164. /package/dist/{trees/tree-node → state-and-nav/tree/deprecated}/tree-node.types.js +0 -0
@@ -1,53 +1,100 @@
1
- function firstAvailableAdditionalFoldIndex(foldsApi, state) {
1
+ function firstAvailableAdditionalFoldIndex(foldsApi, folds) {
2
2
  let index = 1;
3
- while (foldsApi.hasAtIndex(state.folds, index)) {
3
+ while (foldsApi.hasAtIndex(folds, index)) {
4
4
  index += 1;
5
5
  }
6
6
  return index;
7
7
  }
8
- export function createReducer(treeNodeApi, foldsApi) {
8
+ export function createReducer(stateApi, navNodeApi) {
9
+ const { treeMethods, foldMethods, cursorMethods } = stateApi;
10
+ /**
11
+ * Preserve the cursor if its position remains navigable. Otherwise, move it
12
+ * to a fold marker containing its representative entry, or to the nearest
13
+ * visible ancestor.
14
+ */
15
+ function reconcileCursor(state) {
16
+ const navigation = navNodeApi.from(stateApi.getRoot(state), stateApi.getFolds(state));
17
+ if (navigation === undefined || navigation.children === null) {
18
+ // StateApi guarantees that the root is a loaded branch.
19
+ throw new Error('Cannot navigate state: root is not a loaded branch.');
20
+ }
21
+ const cursor = stateApi.getCursor(state);
22
+ const positions = [
23
+ { path: [] },
24
+ ...navNodeApi.cursors(navigation.children),
25
+ ];
26
+ if (positions.some((position) => cursorMethods.equal(position, cursor))) {
27
+ return state;
28
+ }
29
+ const path = cursorMethods.getPath(cursor);
30
+ while (path.length > 0) {
31
+ const entryId = path.at(-1);
32
+ if (entryId !== undefined) {
33
+ const parentPath = path.slice(0, -1);
34
+ const parent = navNodeApi.getNodeAtPath(navigation.children, parentPath);
35
+ const foldEntry = parent?.entries.find((entry) => navNodeApi.entryIsFold(entry) &&
36
+ entry.folded.some((foldedEntry) => !navNodeApi.entryIsFold(foldedEntry) &&
37
+ foldedEntry.id === entryId));
38
+ if (foldEntry !== undefined && navNodeApi.entryIsFold(foldEntry)) {
39
+ const firstEntry = foldEntry.folded.find((entry) => !navNodeApi.entryIsFold(entry));
40
+ if (firstEntry !== undefined && !navNodeApi.entryIsFold(firstEntry)) {
41
+ const foldCursor = {
42
+ parentPath,
43
+ firstId: firstEntry.id,
44
+ };
45
+ if (positions.some((position) => cursorMethods.equal(position, foldCursor))) {
46
+ return stateApi.update(state, { cursor: foldCursor }) ?? state;
47
+ }
48
+ }
49
+ }
50
+ const nodeCursor = { path: [...path] };
51
+ if (positions.some((position) => cursorMethods.equal(position, nodeCursor))) {
52
+ return stateApi.update(state, { cursor: nodeCursor }) ?? state;
53
+ }
54
+ }
55
+ path.pop();
56
+ }
57
+ // The root cursor is always included in `positions`.
58
+ return stateApi.update(state, { cursor: { path: [] } }) ?? state;
59
+ }
60
+ function update(state, changes) {
61
+ const nextState = stateApi.update(state, changes);
62
+ return nextState === undefined ? state : reconcileCursor(nextState);
63
+ }
9
64
  return (state, action) => {
10
65
  switch (action.kind) {
11
66
  case 'changeCursor':
12
- return {
13
- ...state,
14
- cursor: action.cursor,
15
- };
67
+ return update(state, { cursor: action.cursor });
16
68
  case 'updateBranch': {
17
- const root = treeNodeApi.updateAtPath(state.root, action.path, (node) => {
69
+ const root = treeMethods.updateAtPath(stateApi.getRoot(state), action.path, (node) => {
18
70
  /*
19
- * Loading/unloading is a branch-only operation. TreeNodeApi can
20
- * structurally convert any node, but the application reducer must
21
- * not turn a leaf into a branch.
71
+ * Loading/unloading is a branch-only operation. TreeMethods can
72
+ * structurally convert any node, but the reducer must not turn a
73
+ * leaf into a branch.
22
74
  */
23
- if (treeNodeApi.kind(node) === 'leaf') {
75
+ if (treeMethods.kind(node) === 'leaf') {
24
76
  return undefined;
25
77
  }
26
78
  if (action.entries === null) {
27
- return treeNodeApi.toUnloadedBranch(node);
79
+ return treeMethods.toUnloadedBranch(node);
28
80
  }
29
- return treeNodeApi.withLoadedChildren(node, action.entries);
81
+ return treeMethods.withLoadedChildren(node, action.entries);
30
82
  });
31
83
  /*
32
84
  * State requires its root to remain a loaded branch. In particular,
33
85
  * refuse an accidental updateBranch with path [] and entries null.
34
86
  */
35
- if (root === undefined || treeNodeApi.kind(root) !== 'loaded-branch') {
87
+ if (root === undefined || treeMethods.kind(root) !== 'loaded-branch') {
36
88
  return state;
37
89
  }
38
- return {
39
- ...state,
40
- root,
41
- };
90
+ return update(state, { root });
42
91
  }
43
92
  case 'setState': {
44
93
  // A newer update has already been applied.
45
94
  if (state !== action.oldState) {
46
95
  return state;
47
96
  }
48
- return {
49
- ...action.newState,
50
- };
97
+ return reconcileCursor(action.newState);
51
98
  }
52
99
  case 'addToFoldTree': {
53
100
  const entryId = action.path.at(-1);
@@ -55,82 +102,69 @@ export function createReducer(treeNodeApi, foldsApi) {
55
102
  return state;
56
103
  }
57
104
  const parentPath = action.path.slice(0, -1);
58
- const folds = foldsApi.hideEntryAtPath(state.folds, action.foldTreeIndex, parentPath, entryId);
105
+ const folds = foldMethods.hideEntryAtPath(stateApi.getFolds(state), action.foldTreeIndex, parentPath, entryId);
59
106
  if (folds === undefined) {
60
107
  return state;
61
108
  }
62
- return {
63
- ...state,
109
+ return update(state, {
64
110
  folds,
65
111
  cursor: action.cursor,
66
- };
112
+ });
67
113
  }
68
114
  case 'removeFromFoldTree': {
69
- // TODO: Change to show only entry at cursor. mirroring addToFoldTree
70
- const folds = foldsApi.showAllHiddenEntriesAtPath(state.folds, action.foldTreeIndex, action.path);
71
- if (folds === undefined) {
72
- return state;
115
+ let folds = stateApi.getFolds(state);
116
+ if (action.entryIds === null) {
117
+ const nextFolds = foldMethods.showAllHiddenEntriesAtPath(folds, action.foldTreeIndex, action.path);
118
+ if (nextFolds === undefined) {
119
+ return state;
120
+ }
121
+ folds = nextFolds;
122
+ }
123
+ else {
124
+ for (const entryId of action.entryIds) {
125
+ const nextFolds = foldMethods.showEntryAtPath(folds, action.foldTreeIndex, action.path, entryId);
126
+ if (nextFolds === undefined) {
127
+ return state;
128
+ }
129
+ folds = nextFolds;
130
+ }
73
131
  }
74
- return {
75
- ...state,
132
+ return update(state, {
76
133
  folds,
77
134
  cursor: action.cursor,
78
- };
135
+ });
79
136
  }
80
137
  case 'fold': {
81
- const folds = foldsApi.activateAtIndex(state.folds, action.foldTreeIndex);
82
- return folds === undefined
83
- ? state
84
- : {
85
- ...state,
86
- folds,
87
- };
138
+ const folds = foldMethods.activateAtIndex(stateApi.getFolds(state), action.foldTreeIndex);
139
+ return folds === undefined ? state : update(state, { folds });
88
140
  }
89
141
  case 'unfold': {
90
- const folds = foldsApi.deactivateAtIndex(state.folds, action.foldTreeIndex);
91
- return folds === undefined
92
- ? state
93
- : {
94
- ...state,
95
- folds,
96
- };
142
+ const folds = foldMethods.deactivateAtIndex(stateApi.getFolds(state), action.foldTreeIndex);
143
+ return folds === undefined ? state : update(state, { folds });
97
144
  }
98
145
  case 'toggleFold': {
99
- const folds = foldsApi.toggleActiveAtIndex(state.folds, action.foldTreeIndex);
100
- return folds === undefined
101
- ? state
102
- : {
103
- ...state,
104
- folds,
105
- };
146
+ const folds = foldMethods.toggleActiveAtIndex(stateApi.getFolds(state), action.foldTreeIndex);
147
+ return folds === undefined ? state : update(state, { folds });
106
148
  }
107
149
  case 'createFoldTree': {
108
- const index = firstAvailableAdditionalFoldIndex(foldsApi, state);
109
- const folds = foldsApi.setAdditionalFoldAtIndex(state.folds, index,
110
- // Obtain this through the actual TreeNodeApi root-ID operation.
111
- treeNodeApi.id(state.root), {
150
+ const currentFolds = stateApi.getFolds(state);
151
+ const index = firstAvailableAdditionalFoldIndex(foldMethods, currentFolds);
152
+ const folds = foldMethods.setAdditionalFoldAtIndex(currentFolds, index,
153
+ // Obtain this through the actual TreeMethods root-ID operation.
154
+ treeMethods.id(stateApi.getRoot(state)), {
112
155
  name: action.name,
113
156
  description: null,
114
157
  });
115
- return folds === undefined
116
- ? state
117
- : {
118
- ...state,
119
- folds,
120
- };
158
+ return folds === undefined ? state : update(state, { folds });
121
159
  }
122
160
  case 'deleteFoldTree': {
123
- const index = foldsApi.getIndexByName(state.folds, action.name);
161
+ const currentFolds = stateApi.getFolds(state);
162
+ const index = foldMethods.getIndexByName(currentFolds, action.name);
124
163
  if (index === undefined || index === 0) {
125
164
  return state;
126
165
  }
127
- const folds = foldsApi.removeAdditionalFoldAtIndex(state.folds, index);
128
- return folds === undefined
129
- ? state
130
- : {
131
- ...state,
132
- folds,
133
- };
166
+ const folds = foldMethods.removeAdditionalFoldAtIndex(currentFolds, index);
167
+ return folds === undefined ? state : update(state, { folds });
134
168
  }
135
169
  default: {
136
170
  const _exhaustive = action;
@@ -1 +1 @@
1
- {"version":3,"file":"feedback.impl.d.ts","sourceRoot":"","sources":["../../src/feedback/feedback.impl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAgB,MAAM,qBAAqB,CAAC;AA2B/E;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7D,WAAW,CAkBb"}
1
+ {"version":3,"file":"feedback.impl.d.ts","sourceRoot":"","sources":["../../src/feedback/feedback.impl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AA4BjE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7D,WAAW,CAkBb"}
@@ -1,4 +1,4 @@
1
- export type FeedbackType = 'success' | 'message' | 'error';
1
+ import type { FeedbackType } from 'dirvi-apis';
2
2
  declare const feedbackBrand: unique symbol;
3
3
  /**
4
4
  * Opaque runtime feedback message.
@@ -1 +1 @@
1
- {"version":3,"file":"feedback.types.d.ts","sourceRoot":"","sources":["../../src/feedback/feedback.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3D,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC9B,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,aAAa,CAAC;CAChD,CAAC,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC;;;OAGG;IACH,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IAE/D,mDAAmD;IACnD,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,CAAC;IAE3C,wDAAwD;IACxD,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,YAAY,CAAC;CAC/C,CAAC,CAAC"}
1
+ {"version":3,"file":"feedback.types.d.ts","sourceRoot":"","sources":["../../src/feedback/feedback.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC9B,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,aAAa,CAAC;CAChD,CAAC,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC;;;OAGG;IACH,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IAE/D,mDAAmD;IACnD,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,CAAC;IAE3C,wDAAwD;IACxD,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,YAAY,CAAC;CAC/C,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './trees/index.js';
1
+ export * from './state-and-nav/index.js';
2
2
  export * from './feedback/index.js';
3
3
  export * from './app/index.js';
4
4
  export * from './input/application/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AAEjC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,8BAA8B,CAAC;AAE7C,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AAEzC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,8BAA8B,CAAC;AAE7C,cAAc,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export * from './trees/index.js';
1
+ export * from './state-and-nav/index.js';
2
2
  export * from './feedback/index.js';
3
3
  export * from './app/index.js';
4
4
  export * from './input/application/index.js';
@@ -1,4 +1,4 @@
1
- import type { CursorApi, SerializableKey, State, StateApi, TreeNodeApi } from '../../trees/index.js';
1
+ import type { SerializableKey, State, StateMethods } from '../../state-and-nav/index.js';
2
2
  import type { Effect, Intent } from '../domain/index.js';
3
3
  /**
4
4
  * Converts a user intent and the current tree state into an effect.
@@ -10,5 +10,5 @@ import type { Effect, Intent } from '../domain/index.js';
10
10
  * @typeParam Value - Application-owned data stored in each tree node.
11
11
  */
12
12
  export type IntentToEffect<Id extends SerializableKey, Value> = (intent: Intent, state: State<Id, Value>) => Effect<Id, Value> | undefined;
13
- export declare function createIntentToEffect<Id extends SerializableKey, Value>(stateApi: StateApi<Id, Value>, cursorApi: CursorApi<Id>, treeNodeApi: TreeNodeApi<Id, Value>): IntentToEffect<Id, Value>;
13
+ export declare function createIntentToEffect<Id extends SerializableKey, Value>(registeredCommands: ReadonlySet<string>, stateApi: StateMethods<Id, Value>): IntentToEffect<Id, Value>;
14
14
  //# sourceMappingURL=intent-to-effect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"intent-to-effect.d.ts","sourceRoot":"","sources":["../../../src/input/application/intent-to-effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,KAAK,EACL,QAAQ,EACR,WAAW,EACZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAKvE;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,CAC9D,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KACpB,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;AAEnC,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EACpE,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EAC7B,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,GAClC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAkE3B"}
1
+ {"version":3,"file":"intent-to-effect.d.ts","sourceRoot":"","sources":["../../../src/input/application/intent-to-effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,KAAK,EACL,YAAY,EACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAKvE;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,CAC9D,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KACpB,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;AAEnC,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EACpE,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,EACvC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,GAChC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAgE3B"}
@@ -1,14 +1,14 @@
1
- import { parseCommand } from './parse-command.js';
1
+ import { parseExCommand } from './parse-ex-command.js';
2
2
  import { parseNormalCommand } from './parse-normal-command.js';
3
- export function createIntentToEffect(stateApi, cursorApi, treeNodeApi) {
3
+ export function createIntentToEffect(registeredCommands, stateApi) {
4
4
  return (intent, state) => {
5
5
  switch (intent.intentType) {
6
6
  case 'setNormalBuffer':
7
7
  return normalBufferToEffectResult(intent.normalBuffer);
8
8
  case 'normalRight':
9
- return normalInteractRightToEffect(state, stateApi, cursorApi, treeNodeApi);
9
+ return normalInteractRightToEffect(state, stateApi);
10
10
  case 'normalLeft':
11
- return normalInteractLeftToEffect(state, cursorApi);
11
+ return normalInteractLeftToEffect(state, stateApi);
12
12
  case 'normalDown':
13
13
  return dispatchAction({
14
14
  effectActionType: 'nextEntry',
@@ -42,7 +42,7 @@ export function createIntentToEffect(stateApi, cursorApi, treeNodeApi) {
42
42
  },
43
43
  };
44
44
  case 'executeCommandLine':
45
- return (parseCommand(intent.commandLine) ?? {
45
+ return (parseExCommand(intent.commandLine, registeredCommands) ?? {
46
46
  effectType: 'setInputState',
47
47
  inputState: {
48
48
  inputMode: 'normal',
@@ -106,16 +106,16 @@ function normalBufferToEffectResult(updatedNormalBuffer) {
106
106
  * - An open branch dispatches an action that closes/unloads its children.
107
107
  * - A leaf has no right-interaction effect.
108
108
  */
109
- function normalInteractRightToEffect(state, stateApi, cursorApi, treeNodeApi) {
109
+ function normalInteractRightToEffect(state, stateApi) {
110
110
  const currentEntry = stateApi.getNodeAtCursor(state);
111
111
  if (currentEntry === undefined) {
112
112
  return undefined;
113
113
  }
114
- const path = cursorApi.getPath(state.cursor);
114
+ const path = stateApi.cursorPath(state);
115
115
  if (path === undefined) {
116
116
  return undefined;
117
117
  }
118
- switch (treeNodeApi.kind(currentEntry)) {
118
+ switch (stateApi.treeMethods.kind(currentEntry)) {
119
119
  case 'unloaded-branch':
120
120
  return {
121
121
  effectType: 'loadBranchEntries',
@@ -137,9 +137,8 @@ function normalInteractRightToEffect(state, stateApi, cursorApi, treeNodeApi) {
137
137
  * Left from the root has no effect. Otherwise dispatch navigation to the
138
138
  * parent entry.
139
139
  */
140
- function normalInteractLeftToEffect(state, cursorApi) {
141
- const path = cursorApi.getPath(state.cursor);
142
- if (path === undefined || path.length === 0) {
140
+ function normalInteractLeftToEffect(state, stateApi) {
141
+ if (stateApi.isCursorRoot(state)) {
143
142
  return undefined;
144
143
  }
145
144
  return dispatchAction({
@@ -0,0 +1,17 @@
1
+ import { SerializableKey } from 'dirvi-apis';
2
+ import type { Effect } from '../domain/index.js';
3
+ /**
4
+ * Parses a Vim-style Ex command into an application effect.
5
+ *
6
+ * Leading `:` characters are optional. Arguments are split on whitespace;
7
+ * quoting and escaping are not supported.
8
+ *
9
+ * Registered commands take precedence over built-ins and receive their
10
+ * arguments without validation. This function does not execute them.
11
+ *
12
+ * @param commandLine - The raw command line.
13
+ * @param registeredCommands - User-command names that override built-ins.
14
+ * @returns An effect, or `undefined` for an empty command.
15
+ */
16
+ export declare function parseExCommand<Id extends SerializableKey, Value>(commandLine: string, registeredCommands: ReadonlySet<string>): Effect<Id, Value> | undefined;
17
+ //# sourceMappingURL=parse-ex-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-ex-command.d.ts","sourceRoot":"","sources":["../../../src/input/application/parse-ex-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AA6IjD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAC9D,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,GACtC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CA+D/B"}
@@ -1,23 +1,59 @@
1
- const DEFAULT_VIEW_NAME = 'default';
1
+ // const DEFAULT_VIEW_NAME = 'default';
2
2
  const DEFAULT_FOLD_TREE_NAME = 'default';
3
+ function parseAppCommand(commandArgs) {
4
+ const [firstArgument, ...remainingArguments] = commandArgs;
5
+ const badArgs = {
6
+ effectType: 'emitError',
7
+ message: `Unrecognized command arguments: app ${commandArgs.join(' ')}`,
8
+ };
9
+ switch (firstArgument) {
10
+ case 'list':
11
+ return commandArgs.length === 1
12
+ ? {
13
+ effectType: 'listApps',
14
+ }
15
+ : badArgs;
16
+ case 'open':
17
+ case 'switch': {
18
+ const [appId, input] = remainingArguments;
19
+ if (appId === undefined || remainingArguments.length > 2) {
20
+ return badArgs;
21
+ }
22
+ return {
23
+ effectType: 'switchApp',
24
+ appId,
25
+ input,
26
+ };
27
+ }
28
+ default: {
29
+ if (firstArgument === undefined || remainingArguments.length > 1) {
30
+ return badArgs;
31
+ }
32
+ const input = remainingArguments[0];
33
+ return {
34
+ effectType: 'switchApp',
35
+ appId: firstArgument,
36
+ input,
37
+ };
38
+ }
39
+ }
40
+ }
3
41
  function parseFoldCommand(commandArgs) {
4
42
  const [subcommand, name] = commandArgs;
43
+ const badArgs = {
44
+ effectType: 'emitError',
45
+ message: `Unrecognized command arguments: fold ${commandArgs.join(' ')}`,
46
+ };
5
47
  switch (subcommand) {
6
48
  case 'list':
7
49
  return commandArgs.length === 1
8
50
  ? {
9
51
  effectType: 'listFolds',
10
52
  }
11
- : {
12
- effectType: 'unrecognizedCommand',
13
- commandLine: `fold ${commandArgs.join(' ')}`,
14
- };
53
+ : badArgs;
15
54
  case 'create':
16
55
  return name === undefined || commandArgs.length !== 2
17
- ? {
18
- effectType: 'unrecognizedCommand',
19
- commandLine: `fold ${commandArgs.join(' ')}`,
20
- }
56
+ ? badArgs
21
57
  : {
22
58
  effectType: 'dispatchEffectAction',
23
59
  action: {
@@ -27,10 +63,7 @@ function parseFoldCommand(commandArgs) {
27
63
  };
28
64
  case 'delete':
29
65
  return name === undefined || commandArgs.length !== 2
30
- ? {
31
- effectType: 'unrecognizedCommand',
32
- commandLine: `fold ${commandArgs.join(' ')}`,
33
- }
66
+ ? badArgs
34
67
  : {
35
68
  effectType: 'dispatchEffectAction',
36
69
  action: {
@@ -41,10 +74,7 @@ function parseFoldCommand(commandArgs) {
41
74
  case 'enable':
42
75
  case 'close':
43
76
  return commandArgs.length > 2
44
- ? {
45
- effectType: 'unrecognizedCommand',
46
- commandLine: `fold ${commandArgs.join(' ')}`,
47
- }
77
+ ? badArgs
48
78
  : {
49
79
  effectType: 'dispatchEffectAction',
50
80
  action: {
@@ -58,10 +88,7 @@ function parseFoldCommand(commandArgs) {
58
88
  case 'disable':
59
89
  case 'open':
60
90
  return commandArgs.length > 2
61
- ? {
62
- effectType: 'unrecognizedCommand',
63
- commandLine: `fold ${commandArgs.join(' ')}`,
64
- }
91
+ ? badArgs
65
92
  : {
66
93
  effectType: 'dispatchEffectAction',
67
94
  action: {
@@ -74,10 +101,7 @@ function parseFoldCommand(commandArgs) {
74
101
  };
75
102
  case 'toggle':
76
103
  return commandArgs.length > 2
77
- ? {
78
- effectType: 'unrecognizedCommand',
79
- commandLine: `fold ${commandArgs.join(' ')}`,
80
- }
104
+ ? badArgs
81
105
  : {
82
106
  effectType: 'dispatchEffectAction',
83
107
  action: {
@@ -89,55 +113,61 @@ function parseFoldCommand(commandArgs) {
89
113
  },
90
114
  };
91
115
  default:
92
- return {
93
- effectType: 'unrecognizedCommand',
94
- commandLine: `fold ${commandArgs.join(' ')}`,
95
- };
116
+ return badArgs;
96
117
  }
97
118
  }
98
119
  /**
99
- * Parses a command-line command into an application effect.
120
+ * Parses a Vim-style Ex command into an application effect.
100
121
  *
101
- * Command parsing is independent of the concrete tree-node value type, but the
102
- * generic parameters allow its result to compose with the application's
103
- * Effect<Id, Value> pipeline.
122
+ * Leading `:` characters are optional. Arguments are split on whitespace;
123
+ * quoting and escaping are not supported.
104
124
  *
105
- * @typeParam Id - The sibling-unique tree-node ID type.
106
- * @typeParam Value - Application-owned data stored in each tree node.
125
+ * Registered commands take precedence over built-ins and receive their
126
+ * arguments without validation. This function does not execute them.
127
+ *
128
+ * @param commandLine - The raw command line.
129
+ * @param registeredCommands - User-command names that override built-ins.
130
+ * @returns An effect, or `undefined` for an empty command.
107
131
  */
108
- export function parseCommand(commandLine) {
109
- const command = commandLine.trim().replace(/^:/, '');
132
+ export function parseExCommand(commandLine, registeredCommands) {
133
+ const command = commandLine.trim().replace(/^:+/, '');
110
134
  if (command === '') {
111
135
  return undefined;
112
136
  }
113
137
  const [commandName, ...commandArgs] = command.split(/\s+/);
138
+ if (commandName !== undefined && registeredCommands.has(commandName)) {
139
+ return {
140
+ effectType: 'userExCommand',
141
+ name: commandName,
142
+ args: commandArgs,
143
+ };
144
+ }
114
145
  switch (commandName) {
115
146
  case 'q':
116
147
  return {
117
148
  effectType: 'quit',
118
149
  exitMessage: '',
119
150
  };
151
+ case 'app':
152
+ return parseAppCommand(commandArgs);
120
153
  case 'fold':
121
154
  return parseFoldCommand(commandArgs);
122
- case 'evlp':
123
- return {
124
- effectType: 'emitVisibleLeavesPaths',
125
- };
126
- case 'mkview!':
127
- return {
128
- effectType: 'saveView',
129
- name: commandArgs[0] || DEFAULT_VIEW_NAME,
130
- overwrite: true,
131
- };
132
- case 'loadview':
133
- return {
134
- effectType: 'loadView',
135
- name: commandArgs[0] || DEFAULT_VIEW_NAME,
136
- };
155
+ // case 'mkview!':
156
+ // return {
157
+ // effectType: 'saveView',
158
+ // name: commandArgs[0] || DEFAULT_VIEW_NAME,
159
+ // overwrite: true,
160
+ // };
161
+ //
162
+ // case 'loadview':
163
+ // return {
164
+ // effectType: 'loadView',
165
+ // name: commandArgs[0] || DEFAULT_VIEW_NAME,
166
+ // };
137
167
  default:
138
168
  return {
139
- effectType: 'unrecognizedCommand',
140
- commandLine,
169
+ effectType: 'emitError',
170
+ message: `Unrecognized command: "${commandLine}"`,
141
171
  };
142
172
  }
143
173
  /*
@@ -1,4 +1,4 @@
1
- import type { SerializableKey } from '../../trees/index.js';
1
+ import type { SerializableKey } from '../../state-and-nav/index.js';
2
2
  import type { EffectAction } from '../domain/index.js';
3
3
  export type ParseNormalCommandResult<Id extends SerializableKey, Value> = {
4
4
  kind: 'incomplete';
@@ -1 +1 @@
1
- {"version":3,"file":"parse-normal-command.d.ts","sourceRoot":"","sources":["../../../src/input/application/parse-normal-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD,MAAM,MAAM,wBAAwB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAClE;IACE,IAAI,EAAE,YAAY,CAAC;CACpB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;CACjC,GACD;IACE,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC;AAEN,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAClE,YAAY,EAAE,MAAM,GACnB,wBAAwB,CAAC,EAAE,EAAE,KAAK,CAAC,CAwCrC"}
1
+ {"version":3,"file":"parse-normal-command.d.ts","sourceRoot":"","sources":["../../../src/input/application/parse-normal-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD,MAAM,MAAM,wBAAwB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAClE;IACE,IAAI,EAAE,YAAY,CAAC;CACpB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;CACjC,GACD;IACE,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC;AAEN,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAClE,YAAY,EAAE,MAAM,GACnB,wBAAwB,CAAC,EAAE,EAAE,KAAK,CAAC,CAwCrC"}