epiq 0.0.1 → 0.0.3

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 (134) hide show
  1. package/dist/app.js +11 -4
  2. package/dist/board/components/Board.js +4 -12
  3. package/dist/board/components/CommandLine.d.ts +2 -0
  4. package/dist/board/components/CommandLine.js +19 -0
  5. package/dist/{components/Help.d.ts → board/components/ContextBar.d.ts} +1 -1
  6. package/dist/board/components/ContextBar.js +8 -0
  7. package/dist/board/components/Help.js +18 -11
  8. package/dist/board/components/Swimlane.js +6 -3
  9. package/dist/board/components/Swimlanes.js +1 -1
  10. package/dist/board/components/TextInput.d.ts +6 -0
  11. package/dist/board/components/TextInput.js +9 -0
  12. package/dist/board/components/TicketListItem.js +5 -4
  13. package/dist/board/components/TicketUI.js +6 -7
  14. package/dist/board/hints/hints.d.ts +7 -0
  15. package/dist/board/hints/hints.js +29 -0
  16. package/dist/board/mock/board.js +526 -457
  17. package/dist/cli.d.ts +2 -1
  18. package/dist/cli.js +37 -14
  19. package/dist/debug-logger.d.ts +2 -0
  20. package/dist/debug-logger.js +15 -0
  21. package/dist/navigation/actions/add/add-actions.d.ts +2 -0
  22. package/dist/navigation/actions/add/add-actions.js +25 -0
  23. package/dist/navigation/actions/add/add-item-actions.d.ts +2 -0
  24. package/dist/navigation/actions/add/add-item-actions.js +25 -0
  25. package/dist/navigation/actions/add-item/add-item-actions.d.ts +3 -0
  26. package/dist/navigation/actions/add-item/add-item-actions.js +35 -0
  27. package/dist/navigation/actions/board-action-map.d.ts +10 -0
  28. package/dist/navigation/actions/board-action-map.js +12 -0
  29. package/dist/{lib/utils/default-actions.d.ts → navigation/actions/default/default-action-utils.d.ts} +2 -2
  30. package/dist/{lib/utils → navigation/actions/default}/default-action-utils.js +11 -10
  31. package/dist/navigation/actions/default/default-actions.d.ts +2 -0
  32. package/dist/navigation/actions/default/default-actions.js +78 -0
  33. package/dist/navigation/actions/input/command-line-input.d.ts +2 -0
  34. package/dist/navigation/actions/input/command-line-input.js +18 -0
  35. package/dist/navigation/actions/input/input-actions-utils.d.ts +8 -0
  36. package/dist/navigation/actions/input/input-actions-utils.js +47 -0
  37. package/dist/navigation/actions/input/input-actions.d.ts +2 -0
  38. package/dist/navigation/actions/input/input-actions.js +43 -0
  39. package/dist/navigation/actions/move/move-actions-routes.d.ts +4 -0
  40. package/dist/navigation/actions/move/move-actions-routes.js +55 -0
  41. package/dist/{lib/actions → navigation/actions/move}/move-actions-utils.d.ts +1 -1
  42. package/dist/{board/actions/move-actions.js → navigation/actions/move/move-actions-utils.js} +13 -9
  43. package/dist/navigation/actions/move/move-actions.d.ts +4 -0
  44. package/dist/navigation/actions/move/move-actions.js +53 -0
  45. package/dist/{lib/utils/default-action-utils.d.ts → navigation/actions/navigate/navigation-action-utils.d.ts} +1 -1
  46. package/dist/navigation/actions/navigate/navigation-actions.d.ts +2 -0
  47. package/dist/navigation/actions/navigate/navigation-actions.js +78 -0
  48. package/dist/navigation/command-line/command-line-actions.d.ts +2 -0
  49. package/dist/navigation/command-line/command-line-actions.js +32 -0
  50. package/dist/navigation/command-line/command-line-intent.d.ts +7 -0
  51. package/dist/navigation/command-line/command-line-intent.js +29 -0
  52. package/dist/navigation/command-line/command-line-sequence-actions.d.ts +2 -0
  53. package/dist/navigation/command-line/command-line-sequence-actions.js +31 -0
  54. package/dist/navigation/command-line/command-line-sequence-intent.d.ts +7 -0
  55. package/dist/navigation/command-line/command-line-sequence-intent.js +29 -0
  56. package/dist/navigation/model/action-map.model.d.ts +17 -5
  57. package/dist/navigation/model/action-map.model.js +2 -0
  58. package/dist/navigation/model/navigation-ctx.model.d.ts +2 -2
  59. package/dist/navigation/model/navigation-tree.model.d.ts +1 -0
  60. package/dist/navigation/navigation.d.ts +1 -1
  61. package/dist/navigation/navigation.js +48 -34
  62. package/dist/navigation/state/command-line.state.d.ts +13 -0
  63. package/dist/navigation/state/command-line.state.js +43 -0
  64. package/dist/navigation/state/state.d.ts +7 -6
  65. package/dist/navigation/state/state.js +31 -2
  66. package/dist/navigation/utils/get-command-line-intent.d.ts +3 -0
  67. package/dist/navigation/utils/get-command-line-intent.js +17 -0
  68. package/dist/navigation/utils/getKeyIntent.d.ts +5 -0
  69. package/dist/navigation/utils/getKeyIntent.js +71 -0
  70. package/dist/navigation/utils/key-intent.d.ts +19 -10
  71. package/dist/navigation/utils/key-intent.js +103 -43
  72. package/package.json +2 -2
  73. package/dist/board/actions/board-action-map.d.ts +0 -11
  74. package/dist/board/actions/board-action-map.js +0 -11
  75. package/dist/board/actions/move-actions-routes.d.ts +0 -4
  76. package/dist/board/actions/move-actions-routes.js +0 -31
  77. package/dist/board/actions/move-actions.d.ts +0 -5
  78. package/dist/board/board-action-map.d.ts +0 -9
  79. package/dist/board/board-action-map.js +0 -7
  80. package/dist/board/move-actions-routes.d.ts +0 -4
  81. package/dist/board/move-actions-routes.js +0 -31
  82. package/dist/board/move-actions.d.ts +0 -5
  83. package/dist/board/move-actions.js +0 -49
  84. package/dist/components/Board.d.ts +0 -6
  85. package/dist/components/Board.js +0 -15
  86. package/dist/components/Help.js +0 -21
  87. package/dist/components/Swimlane.d.ts +0 -8
  88. package/dist/components/Swimlane.js +0 -10
  89. package/dist/components/TicketListItem.d.ts +0 -6
  90. package/dist/components/TicketListItem.js +0 -12
  91. package/dist/lib/actions/board-action-map.d.ts +0 -9
  92. package/dist/lib/actions/board-action-map.js +0 -7
  93. package/dist/lib/actions/move-actions-utils.js +0 -43
  94. package/dist/lib/actions/move-actions.d.ts +0 -4
  95. package/dist/lib/actions/move-actions.js +0 -31
  96. package/dist/lib/board/board-action-map.d.ts +0 -9
  97. package/dist/lib/board/board-action-map.js +0 -7
  98. package/dist/lib/board/move-actions-routes.d.ts +0 -4
  99. package/dist/lib/board/move-actions-routes.js +0 -31
  100. package/dist/lib/board/move-actions.d.ts +0 -5
  101. package/dist/lib/board/move-actions.js +0 -43
  102. package/dist/lib/board-utils.d.ts +0 -7
  103. package/dist/lib/board-utils.js +0 -33
  104. package/dist/lib/default-actions-routes.d.ts +0 -4
  105. package/dist/lib/default-actions-routes.js +0 -47
  106. package/dist/lib/default-actions.d.ts +0 -4
  107. package/dist/lib/default-actions.js +0 -47
  108. package/dist/lib/navigation-context.d.ts +0 -16
  109. package/dist/lib/navigation.d.ts +0 -10
  110. package/dist/lib/navigation.js +0 -74
  111. package/dist/lib/render-utils.d.ts +0 -10
  112. package/dist/lib/render-utils.js +0 -12
  113. package/dist/lib/state.d.ts +0 -8
  114. package/dist/lib/state.js +0 -8
  115. package/dist/lib/types/action-map.model.d.ts +0 -14
  116. package/dist/lib/types/action-map.model.js +0 -4
  117. package/dist/lib/types/board.model.d.ts +0 -21
  118. package/dist/lib/types/board.model.js +0 -5
  119. package/dist/lib/types/navigation.model.d.ts +0 -9
  120. package/dist/lib/utils/array-utils.d.ts +0 -5
  121. package/dist/lib/utils/array-utils.js +0 -7
  122. package/dist/lib/utils/default-actions.js +0 -50
  123. package/dist/lib/utils/key-intent.d.ts +0 -16
  124. package/dist/lib/utils/key-intent.js +0 -65
  125. package/dist/lib/utils.d.ts +0 -10
  126. package/dist/lib/utils.js +0 -12
  127. package/dist/mock/board.d.ts +0 -2
  128. package/dist/mock/board.js +0 -139
  129. package/dist/navigation/actions/default-actions-routes.d.ts +0 -4
  130. package/dist/navigation/actions/default-actions-routes.js +0 -47
  131. package/dist/navigation/actions/default-actions.d.ts +0 -7
  132. /package/dist/navigation/actions/{default-actions.js → navigate/navigation-action-utils.js} +0 -0
  133. /package/dist/{lib/navigation-context.js → navigation/navigation-ctx.d.ts} +0 -0
  134. /package/dist/{lib/types/navigation.model.js → navigation/navigation-ctx.js} +0 -0
@@ -0,0 +1,53 @@
1
+ import { Mode } from '../../model/action-map.model.js';
2
+ import { patchState } from '../../state/state.js';
3
+ import { KeyIntent } from '../../utils/key-intent.js';
4
+ import { moveChildNextWithinParent, moveChildPreviousWithinParent, moveChildToNextParent, moveChildToPreviousParent, } from './move-actions-utils.js';
5
+ export const toggleMoveMode = [
6
+ {
7
+ intent: KeyIntent.ToggleMove,
8
+ mode: Mode.DEFAULT,
9
+ description: '[Y] Toggle move/yank mode',
10
+ action: () => {
11
+ patchState({
12
+ mode: Mode.MOVE,
13
+ });
14
+ },
15
+ },
16
+ {
17
+ intent: KeyIntent.ToggleMove,
18
+ mode: Mode.MOVE,
19
+ action: () => {
20
+ patchState({
21
+ mode: Mode.DEFAULT,
22
+ });
23
+ },
24
+ },
25
+ ];
26
+ export const moveWithinParent = [
27
+ {
28
+ mode: Mode.MOVE,
29
+ description: '[Y, ARROW KEYS] Move item',
30
+ },
31
+ {
32
+ intent: KeyIntent.MovePreviousItem,
33
+ mode: Mode.MOVE,
34
+ action: moveChildPreviousWithinParent,
35
+ },
36
+ {
37
+ intent: KeyIntent.MoveNextItem,
38
+ mode: Mode.MOVE,
39
+ action: moveChildNextWithinParent,
40
+ },
41
+ ];
42
+ export const moveAcrossParents = [
43
+ {
44
+ intent: KeyIntent.MoveToNextContainer,
45
+ mode: Mode.MOVE,
46
+ action: moveChildToNextParent,
47
+ },
48
+ {
49
+ intent: KeyIntent.MoveToPreviousContainer,
50
+ mode: Mode.MOVE,
51
+ action: moveChildToPreviousParent,
52
+ },
53
+ ];
@@ -1,4 +1,4 @@
1
- import { NavigateCtx } from '../navigation-context.js';
1
+ import { NavigateCtx } from '../../model/navigation-ctx.model.js';
2
2
  export declare const navigateToNextItem: (ctx: NavigateCtx) => void;
3
3
  export declare const navigateToPreviousItem: (ctx: NavigateCtx) => void;
4
4
  export declare const navigateToNextContainer: (ctx: NavigateCtx) => void;
@@ -0,0 +1,2 @@
1
+ import { ActionEntry } from '../../model/action-map.model.js';
2
+ export declare const DefaultActions: ActionEntry[];
@@ -0,0 +1,78 @@
1
+ import { Mode } from '../../model/action-map.model.js';
2
+ import { patchState } from '../../state/state.js';
3
+ import { KeyIntent } from '../../utils/key-intent.js';
4
+ import { enterChildNode, exitToParentNode, navigateToNextContainer, navigateToNextItem, navigateToPreviousContainer, navigateToPreviousItem, } from './navigation-action-utils.js';
5
+ export const DefaultActions = [
6
+ {
7
+ intent: KeyIntent.ToggleCommandLine,
8
+ mode: Mode.DEFAULT,
9
+ description: '[:] Toggle command line',
10
+ action: () => patchState({
11
+ mode: Mode.COMMAND_LINE,
12
+ }),
13
+ },
14
+ {
15
+ intent: KeyIntent.ToggleCommandLine,
16
+ mode: Mode.COMMAND_LINE,
17
+ description: '[ESC] Toggle command line',
18
+ action: () => patchState({
19
+ mode: Mode.DEFAULT,
20
+ }),
21
+ },
22
+ // {
23
+ // intent: KeyIntent.ToggleHelp,
24
+ // mode: Mode.DEFAULT,
25
+ // description: '[F1] Toggle HELP menu',
26
+ // action: () =>
27
+ // setState(state => ({
28
+ // ...state,
29
+ // mode: Mode.HELP,
30
+ // })),
31
+ // },
32
+ // {
33
+ // intent: KeyIntent.ToggleHelp,
34
+ // mode: Mode.HELP,
35
+ // description: '[F1] Close HELP menu',
36
+ // action: () =>
37
+ // setState(state => ({
38
+ // ...state,
39
+ // mode: Mode.DEFAULT,
40
+ // })),
41
+ // },
42
+ {
43
+ intent: KeyIntent.Confirm,
44
+ mode: Mode.DEFAULT,
45
+ description: '[ENTER/E] Confirm/Enter context',
46
+ action: enterChildNode,
47
+ },
48
+ {
49
+ intent: KeyIntent.Exit,
50
+ mode: Mode.DEFAULT,
51
+ description: '[ESC/Q] Exit application',
52
+ action: exitToParentNode,
53
+ },
54
+ {
55
+ mode: Mode.DEFAULT,
56
+ description: '[ARROWS/HJKL] Navigate',
57
+ },
58
+ {
59
+ intent: KeyIntent.NavPreviousItem,
60
+ mode: Mode.DEFAULT,
61
+ action: navigateToPreviousItem,
62
+ },
63
+ {
64
+ intent: KeyIntent.NavNextItem,
65
+ mode: Mode.DEFAULT,
66
+ action: navigateToNextItem,
67
+ },
68
+ {
69
+ intent: KeyIntent.NavToPreviousContainer,
70
+ mode: Mode.DEFAULT,
71
+ action: navigateToPreviousContainer,
72
+ },
73
+ {
74
+ intent: KeyIntent.NavToNextContainer,
75
+ mode: Mode.DEFAULT,
76
+ action: navigateToNextContainer,
77
+ },
78
+ ];
@@ -0,0 +1,2 @@
1
+ import { CommandLineActionEntry } from '../model/action-map.model.js';
2
+ export declare const commandLineActions: CommandLineActionEntry[];
@@ -0,0 +1,32 @@
1
+ import { addSwimlaneAction, addTicketAction, } from '../actions/add-item/add-item-actions.js';
2
+ import { Mode } from '../model/action-map.model.js';
3
+ import { patchState } from '../state/state.js';
4
+ import { CommandLineIntent } from './command-line-sequence-intent.js';
5
+ export const commandLineActions = [
6
+ {
7
+ intent: CommandLineIntent.ViewHelp,
8
+ mode: Mode.COMMAND_LINE,
9
+ action: () => {
10
+ patchState({
11
+ mode: Mode.HELP,
12
+ commandLineInput: '',
13
+ });
14
+ },
15
+ },
16
+ {
17
+ intent: CommandLineIntent.AddSwimlane,
18
+ mode: Mode.COMMAND_LINE,
19
+ action: (...args) => {
20
+ addSwimlaneAction(...args);
21
+ patchState({ commandLineInput: '', mode: Mode.DEFAULT });
22
+ },
23
+ },
24
+ {
25
+ intent: CommandLineIntent.AddTicket,
26
+ mode: Mode.COMMAND_LINE,
27
+ action: (...args) => {
28
+ addTicketAction(...args);
29
+ patchState({ commandLineInput: '', mode: Mode.DEFAULT });
30
+ },
31
+ },
32
+ ];
@@ -0,0 +1,7 @@
1
+ export declare const CommandLineIntent: {
2
+ readonly None: "none";
3
+ readonly AddSwimlane: "add-swimlane";
4
+ readonly AddTicket: "add-ticket";
5
+ readonly ViewHelp: "view-help";
6
+ };
7
+ export declare const getCommandLineIntent: (input: string) => (typeof CommandLineIntent)[keyof typeof CommandLineIntent];
@@ -0,0 +1,29 @@
1
+ import { BoardItemTypes } from '../../board/model/board.model.js';
2
+ import { navigationState } from '../state/state.js';
3
+ export const CommandLineIntent = {
4
+ None: 'none',
5
+ AddSwimlane: 'add-swimlane',
6
+ AddTicket: 'add-ticket',
7
+ ViewHelp: 'view-help',
8
+ };
9
+ export const getCommandLineIntent = (input) => {
10
+ const actionContext = navigationState?.currentNode?.actionContext;
11
+ if (!actionContext)
12
+ return CommandLineIntent.None;
13
+ switch (input) {
14
+ case 'help':
15
+ case 'he':
16
+ return CommandLineIntent.ViewHelp;
17
+ case 'add':
18
+ switch (actionContext) {
19
+ case BoardItemTypes.SWIMLANE:
20
+ return CommandLineIntent.AddSwimlane;
21
+ case BoardItemTypes.TICKET:
22
+ return CommandLineIntent.AddTicket;
23
+ default:
24
+ return CommandLineIntent.None;
25
+ }
26
+ default:
27
+ return CommandLineIntent.None;
28
+ }
29
+ };
@@ -0,0 +1,2 @@
1
+ import { CommandLineActionEntry } from '../model/action-map.model.js';
2
+ export declare const commandLineSequenceActions: CommandLineActionEntry[];
@@ -0,0 +1,31 @@
1
+ import { addSwimlaneAction, addTicketAction, } from '../actions/add-item/add-item-actions.js';
2
+ import { Mode } from '../model/action-map.model.js';
3
+ import { patchState } from '../state/state.js';
4
+ import { CommandLineSequenceIntent } from './command-line-sequence-intent.js';
5
+ export const commandLineSequenceActions = [
6
+ {
7
+ intent: CommandLineSequenceIntent.ViewHelp,
8
+ mode: Mode.COMMAND_LINE,
9
+ action: () => {
10
+ patchState({
11
+ mode: Mode.HELP,
12
+ });
13
+ },
14
+ },
15
+ {
16
+ intent: CommandLineSequenceIntent.AddSwimlane,
17
+ mode: Mode.COMMAND_LINE,
18
+ action: (...args) => {
19
+ addSwimlaneAction(...args);
20
+ patchState({ mode: Mode.DEFAULT });
21
+ },
22
+ },
23
+ {
24
+ intent: CommandLineSequenceIntent.AddTicket,
25
+ mode: Mode.COMMAND_LINE,
26
+ action: (...args) => {
27
+ addTicketAction(...args);
28
+ patchState({ mode: Mode.DEFAULT });
29
+ },
30
+ },
31
+ ];
@@ -0,0 +1,7 @@
1
+ export declare const CommandLineSequenceIntent: {
2
+ readonly None: "none";
3
+ readonly AddSwimlane: "add-swimlane";
4
+ readonly AddTicket: "add-ticket";
5
+ readonly ViewHelp: "view-help";
6
+ };
7
+ export declare const getCommandLineIntent: (command: string) => (typeof CommandLineSequenceIntent)[keyof typeof CommandLineSequenceIntent];
@@ -0,0 +1,29 @@
1
+ import { BoardItemTypes } from '../../board/model/board.model.js';
2
+ import { navigationState } from '../state/state.js';
3
+ export const CommandLineSequenceIntent = {
4
+ None: 'none',
5
+ AddSwimlane: 'add-swimlane',
6
+ AddTicket: 'add-ticket',
7
+ ViewHelp: 'view-help',
8
+ };
9
+ export const getCommandLineIntent = (command) => {
10
+ const actionContext = navigationState?.currentNode?.actionContext;
11
+ if (!actionContext)
12
+ return CommandLineSequenceIntent.None;
13
+ switch (command) {
14
+ case 'help':
15
+ case 'he':
16
+ return CommandLineSequenceIntent.ViewHelp;
17
+ case 'add':
18
+ switch (actionContext) {
19
+ case BoardItemTypes.SWIMLANE:
20
+ return CommandLineSequenceIntent.AddSwimlane;
21
+ case BoardItemTypes.TICKET:
22
+ return CommandLineSequenceIntent.AddTicket;
23
+ default:
24
+ return CommandLineSequenceIntent.None;
25
+ }
26
+ default:
27
+ return CommandLineSequenceIntent.None;
28
+ }
29
+ };
@@ -1,14 +1,26 @@
1
+ import { NavigateCtx } from './navigation-ctx.model.js';
2
+ import readline from 'readline';
1
3
  export declare const Mode: {
2
4
  readonly DEFAULT: "default";
3
5
  readonly MOVE: "move";
6
+ readonly HELP: "help";
7
+ readonly COMMAND_LINE: "command-line";
4
8
  };
5
- export type ModeOptions = (typeof Mode)[keyof typeof Mode];
6
- export type ActionEntry<TArgs extends any[] = []> = {
9
+ export type ModeUnion = (typeof Mode)[keyof typeof Mode];
10
+ export type ActionEntry = {
7
11
  intent?: string;
8
- mode: string;
12
+ mode: ModeUnion | ModeUnion[];
9
13
  description?: `[${string}] ${string}`;
10
- action?: (...args: TArgs) => void;
14
+ action?: (...args: [NavigateCtx, ActionEntry, readline.Key]) => void | Promise<void>;
11
15
  };
12
16
  export type ActionMap<T extends Record<string, any[]>> = {
13
- [K in keyof T]: ActionEntry<T[K]>[];
17
+ [K in keyof T]: ActionEntry[];
14
18
  };
19
+ type CommandLineInput = {
20
+ value: string;
21
+ command: string;
22
+ };
23
+ export type CommandLineActionEntry = Omit<ActionEntry, 'action'> & {
24
+ action?: (...args: [NavigateCtx, CommandLineActionEntry, CommandLineInput]) => void | Promise<void>;
25
+ };
26
+ export {};
@@ -1,4 +1,6 @@
1
1
  export const Mode = {
2
2
  DEFAULT: 'default',
3
3
  MOVE: 'move',
4
+ HELP: 'help',
5
+ COMMAND_LINE: 'command-line',
4
6
  };
@@ -1,11 +1,11 @@
1
1
  import { NavigationTree } from './navigation-tree.model.js';
2
2
  export interface NavigateCtx {
3
- _selectedIndex: number;
3
+ _selectionIndex: number;
4
4
  breadCrumb: Array<NavigationTree<NavigationTree>>;
5
5
  navigationNode: NavigationTree<NavigationTree>;
6
6
  children: NavigationTree[];
7
7
  getSelectedIndex(): number;
8
- select(index: number): void;
8
+ updateSelection(index: number): void;
9
9
  selectNone(): void;
10
10
  reInvokeNavigate(index: number, breadCrumb: NavigationTree<NavigationTree>[]): void;
11
11
  render(): void;
@@ -3,6 +3,7 @@ export type NavigationTree<TMeta = Record<string, unknown>> = {
3
3
  isSelected: boolean;
4
4
  childrenRenderAxis: 'vertical' | 'horizontal';
5
5
  name: string;
6
+ description?: string;
6
7
  children: NavigationTree<unknown>[];
7
8
  actionContext: string;
8
9
  enableChildNavigationAcrossContainers?: boolean;
@@ -4,7 +4,7 @@ export declare function navigate<T extends NavigationTree>({ index, breadCrumb,
4
4
  breadCrumb: Array<NavigationTree<NavigationTree>>;
5
5
  callbacks: Partial<{
6
6
  render: () => void;
7
- onSelectChange: (s: T['children'][number], breadCrumb: T['children']) => void;
7
+ onSelectChange: (s: T['children'][number] | undefined, breadCrumb: T['children']) => void;
8
8
  onConfirm: (s: T['children'][number]) => void;
9
9
  }>;
10
10
  }): void;
@@ -2,38 +2,51 @@ import readline from 'readline';
2
2
  import { navigationState } from './state/state.js';
3
3
  import { getKeyIntent } from './utils/key-intent.js';
4
4
  export function navigate({ index = 0, breadCrumb, callbacks, }) {
5
- const { render = () => { }, onSelectChange = () => { }, onConfirm = () => { }, } = callbacks;
6
5
  const ctx = {
7
- breadCrumb,
6
+ _selectionIndex: 0,
7
+ breadCrumb: breadCrumb,
8
+ render() {
9
+ callbacks.render?.();
10
+ },
8
11
  get navigationNode() {
9
- return this.breadCrumb.at(-1);
12
+ return this.breadCrumb?.at(-1) ?? breadCrumb[0];
10
13
  },
11
14
  get children() {
12
15
  return this.navigationNode.children ?? [];
13
16
  },
14
- selectNone: () => {
15
- ctx.select(-1);
17
+ selectNone() {
18
+ ctx.updateSelection(-1);
16
19
  },
17
- _selectedIndex: 0,
18
20
  getSelectedIndex() {
19
- return this._selectedIndex;
21
+ return this._selectionIndex;
22
+ },
23
+ updateSelection(idx) {
24
+ this._selectionIndex = idx;
25
+ const children = this.navigationNode.children ?? [];
26
+ children.forEach((c, i) => (c.isSelected = i === idx));
27
+ if (idx < 0 || idx >= children.length) {
28
+ callbacks.onSelectChange?.(undefined, breadCrumb);
29
+ return;
30
+ }
31
+ callbacks.onSelectChange?.(children[idx], breadCrumb);
20
32
  },
21
- select(i) {
22
- this._selectedIndex = i;
23
- updateSelection(ctx.navigationNode, breadCrumb, i, onSelectChange);
33
+ reInvokeNavigate(idx, crumb) {
34
+ cleanup();
35
+ navigate({ index: idx, breadCrumb: [...crumb], callbacks });
24
36
  },
25
- render,
26
- reInvokeNavigate(index, breadCrumb) {
27
- return reInvokeNavigate(index, breadCrumb);
37
+ confirm(sel) {
38
+ return callbacks.onConfirm?.(sel);
28
39
  },
29
- confirm: sel => onConfirm(sel),
30
- exit: () => {
40
+ exit() {
31
41
  cleanup();
32
42
  process.exit(0);
33
43
  },
34
- enterChildNode: node => reInvokeNavigate(0, [...breadCrumb, node]),
35
- enterParentNode: () => {
36
- ctx.select(-1); // Clear all on this level
44
+ enterChildNode(childNode) {
45
+ this.updateSelection(-1); // Clear all on this level
46
+ this.reInvokeNavigate(0, [...breadCrumb, childNode]);
47
+ },
48
+ enterParentNode() {
49
+ ctx.updateSelection(-1); // Clear all on this level
37
50
  if (breadCrumb.length < 2)
38
51
  return; // Need at least grandparent + parent
39
52
  const ancestors = breadCrumb;
@@ -42,33 +55,34 @@ export function navigate({ index = 0, breadCrumb, callbacks, }) {
42
55
  if (!parent || !grandParent)
43
56
  return;
44
57
  const parentIndex = grandParent?.children.findIndex(x => parent.id === x.id);
45
- reInvokeNavigate(parentIndex, breadCrumb.slice(0, -1)); // Go to parent level
58
+ this.reInvokeNavigate(parentIndex, breadCrumb.slice(0, -1)); // Go to parent level
46
59
  },
47
60
  };
48
- ctx.select(index);
49
- function onKeyPress(_, key) {
61
+ ctx.updateSelection(index);
62
+ async function onKeyPress(_, key) {
50
63
  if (key.ctrl && key.name === 'c')
51
64
  return ctx.exit();
52
65
  const filteredActions = navigationState.availableActions.filter(x => x.mode === navigationState.mode);
53
- const action = filteredActions?.find(actionMeta => {
54
- const intent = getKeyIntent(key, ctx);
55
- return intent === actionMeta.intent;
66
+ const actionMeta = filteredActions.find(actionMetaItem => {
67
+ const intent = getKeyIntent(key, ctx, navigationState.mode);
68
+ return intent === actionMetaItem.intent;
56
69
  });
57
- action?.action?.(ctx);
70
+ if (actionMeta?.action) {
71
+ try {
72
+ const res = actionMeta.action(ctx, actionMeta, key);
73
+ if (res instanceof Promise)
74
+ await res;
75
+ }
76
+ catch (err) {
77
+ console.error(err);
78
+ }
79
+ }
58
80
  ctx.render();
59
81
  }
60
82
  const cleanup = () => process.stdin.removeListener('keypress', onKeyPress);
61
- const reInvokeNavigate = (idx, crumb) => {
62
- cleanup();
63
- navigate({ index: idx, breadCrumb: [...crumb], callbacks });
64
- };
65
- render();
83
+ callbacks.render?.();
66
84
  readline.emitKeypressEvents(process.stdin);
67
85
  if (process.stdin.isTTY)
68
86
  process.stdin.setRawMode(true);
69
87
  process.stdin.on('keypress', onKeyPress);
70
88
  }
71
- function updateSelection({ children }, breadCrumb, idx, onSelectChange) {
72
- children.forEach((c, i) => (c.isSelected = i === idx));
73
- onSelectChange(children[idx], breadCrumb);
74
- }
@@ -0,0 +1,13 @@
1
+ export type CommandLineState = {
2
+ value: string;
3
+ commandHistory: string[];
4
+ commandHistoryIndex: number;
5
+ };
6
+ export declare let commandLineState: CommandLineState;
7
+ export declare const updateCommandLineState: (cb: (old: CommandLineState) => CommandLineState) => void;
8
+ export declare const updateCommandLineInput: (cb: (previous: string) => string) => void;
9
+ export declare const updateCommandHistory: () => void;
10
+ export declare const getPrevCommand: () => void;
11
+ export declare const getNextCommand: () => void;
12
+ export declare const clearCommandLine: () => void;
13
+ export declare const getCommandLineInput: () => string;
@@ -0,0 +1,43 @@
1
+ export let commandLineState = {
2
+ commandHistory: [],
3
+ value: '',
4
+ commandHistoryIndex: -1,
5
+ };
6
+ export const updateCommandLineState = (cb) => {
7
+ bug(commandLineState);
8
+ commandLineState = cb(structuredClone(commandLineState));
9
+ };
10
+ export const updateCommandLineInput = (cb) => {
11
+ updateCommandLineState(state => ({
12
+ ...state,
13
+ value: cb(state.value),
14
+ }));
15
+ };
16
+ export const updateCommandHistory = () => {
17
+ updateCommandLineState(state => ({
18
+ ...state,
19
+ commandHistory: [state.value, ...state.commandHistory].slice(0, 20),
20
+ }));
21
+ };
22
+ export const getPrevCommand = () => {
23
+ updateCommandLineState(s => {
24
+ const nextIndex = Math.min(s.commandHistoryIndex + 1, s.commandHistory.length - 1);
25
+ return {
26
+ ...s,
27
+ commandHistoryIndex: nextIndex,
28
+ value: s.commandHistory[nextIndex] ?? '',
29
+ };
30
+ });
31
+ };
32
+ export const getNextCommand = () => {
33
+ updateCommandLineState(s => {
34
+ const nextIndex = Math.min(s.commandHistoryIndex - 1, s.commandHistory.length - 1);
35
+ return {
36
+ ...s,
37
+ commandHistoryIndex: nextIndex,
38
+ value: s.commandHistory[nextIndex] ?? '',
39
+ };
40
+ });
41
+ };
42
+ export const clearCommandLine = () => updateCommandLineInput(() => '');
43
+ export const getCommandLineInput = () => commandLineState.value;
@@ -1,11 +1,12 @@
1
- import { ActionEntry, ModeOptions } from '../model/action-map.model.js';
2
- import { NavigateCtx } from '../model/navigation-ctx.model.js';
1
+ import { ActionEntry, ModeUnion } from '../model/action-map.model.js';
3
2
  import { NavigationTree } from '../model/navigation-tree.model.js';
4
3
  export declare let navigationState: {
5
- mode: ModeOptions;
6
- availableActions: ActionEntry<[NavigateCtx]>[];
7
- viewHelp: boolean;
4
+ mode: ModeUnion;
5
+ availableActions: ActionEntry[];
6
+ availableHints: readonly string[];
8
7
  currentNode: NavigationTree<NavigationTree> | null;
9
8
  breadCrumb: NavigationTree<NavigationTree>[];
10
9
  };
11
- export declare const setState: (cb: (oldState: typeof navigationState) => typeof navigationState) => void;
10
+ export declare const updateState: (cb: (oldState: typeof navigationState) => typeof navigationState) => void;
11
+ export declare const patchState: (patch: Partial<typeof navigationState>) => void;
12
+ export declare const flashHint: (hints: string[]) => Promise<void>;
@@ -1,10 +1,39 @@
1
+ import { Hints } from '../../board/hints/hints.js';
2
+ import { triggerRender } from '../../cli.js';
1
3
  export let navigationState = {
2
4
  mode: 'default',
3
5
  availableActions: [],
4
- viewHelp: false,
6
+ availableHints: [],
5
7
  currentNode: null,
6
8
  breadCrumb: [],
7
9
  };
8
- export const setState = (cb) => {
10
+ export const updateState = (cb) => {
9
11
  navigationState = cb(navigationState);
10
12
  };
13
+ export const patchState = (patch) => updateState(oldState => ({
14
+ ...oldState,
15
+ ...patch,
16
+ }));
17
+ // Utilities for updating specific parts of the state
18
+ export const flashHint = async (hints) => {
19
+ await new Promise(resolve => {
20
+ setTimeout(() => {
21
+ updateState(state => {
22
+ const { currentNode, mode } = state;
23
+ const contextualHints = (currentNode &&
24
+ Hints[currentNode.actionContext + mode]) ??
25
+ (currentNode && Hints[currentNode.actionContext]) ?? [''];
26
+ return {
27
+ ...state,
28
+ availableHints: contextualHints,
29
+ };
30
+ });
31
+ triggerRender();
32
+ resolve();
33
+ }, 3000);
34
+ patchState({
35
+ availableHints: hints,
36
+ });
37
+ triggerRender();
38
+ });
39
+ };
@@ -0,0 +1,3 @@
1
+ import { KeyIntent } from './key-intent.js';
2
+ import readline from 'readline';
3
+ export declare const getCommandLineIntent: (key: readline.Key) => KeyIntent.Confirm | KeyIntent.ToggleCommandLine | KeyIntent.CaptureInput | KeyIntent.EraseInput | KeyIntent.GetLastCommandFromHistory | KeyIntent.GetNextCommandFromHistory;
@@ -0,0 +1,17 @@
1
+ import { KeyIntent } from './key-intent.js';
2
+ export const getCommandLineIntent = (key) => {
3
+ switch (key.name) {
4
+ case 'up':
5
+ return KeyIntent.GetLastCommandFromHistory;
6
+ case 'down':
7
+ return KeyIntent.GetNextCommandFromHistory;
8
+ case 'return':
9
+ return KeyIntent.Confirm;
10
+ case 'backspace':
11
+ return KeyIntent.EraseInput;
12
+ case 'escape':
13
+ return KeyIntent.ToggleCommandLine;
14
+ default:
15
+ return KeyIntent.CaptureInput;
16
+ }
17
+ };
@@ -0,0 +1,5 @@
1
+ import readline from 'readline';
2
+ import { ModeUnion } from '../model/action-map.model.js';
3
+ import { NavigateCtx } from '../model/navigation-ctx.model.js';
4
+ import { KeyIntent } from './key-intent.js';
5
+ export declare function getKeyIntent(key: readline.Key, ctx: NavigateCtx, mode: ModeUnion): KeyIntent | null;