epiq 0.0.3 → 0.0.4
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.
- package/dist/app.js +4 -5
- package/dist/board/components/Board.js +2 -2
- package/dist/board/components/CommandLine.js +11 -4
- package/dist/board/components/ContextBar.js +12 -3
- package/dist/board/components/Help.js +4 -4
- package/dist/board/components/Swimlane.d.ts +1 -0
- package/dist/board/components/Swimlane.js +3 -4
- package/dist/board/components/Swimlanes.js +8 -11
- package/dist/board/components/TicketListItem.js +3 -3
- package/dist/board/components/TicketUI.js +1 -1
- package/dist/board/hints/hints.d.ts +4 -4
- package/dist/board/hints/hints.js +7 -15
- package/dist/board/mock/board.js +500 -523
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +20 -61
- package/dist/debug-logger.d.ts +1 -1
- package/dist/debug-logger.js +5 -4
- package/dist/init-project.d.ts +1 -0
- package/dist/init-project.js +1 -0
- package/dist/navigation/actions/add-item/add-item-actions.js +21 -11
- package/dist/navigation/actions/board-action-map.js +3 -3
- package/dist/navigation/actions/default/default-action-utils.d.ts +8 -7
- package/dist/navigation/actions/default/default-action-utils.js +67 -17
- package/dist/navigation/actions/default/default-actions.js +30 -44
- package/dist/navigation/actions/default/navigation-action-utils.d.ts +16 -0
- package/dist/navigation/actions/default/navigation-action-utils.js +109 -0
- package/dist/navigation/actions/input/command-line-input.js +4 -4
- package/dist/navigation/actions/input/input-actions.js +13 -12
- package/dist/navigation/actions/move/move-actions-utils.d.ts +4 -5
- package/dist/navigation/actions/move/move-actions-utils.js +44 -31
- package/dist/navigation/actions/move/move-actions.js +17 -11
- package/dist/navigation/command-line/command-line-sequence-actions.d.ts +1 -1
- package/dist/navigation/command-line/command-line-sequence-actions.js +24 -11
- package/dist/navigation/command-line/command-line-sequence-intent.d.ts +3 -2
- package/dist/navigation/command-line/command-line-sequence-intent.js +13 -10
- package/dist/navigation/command-line/commands.d.ts +2 -0
- package/dist/navigation/command-line/commands.js +58 -0
- package/dist/navigation/keypress-listener.d.ts +1 -0
- package/dist/navigation/keypress-listener.js +33 -0
- package/dist/navigation/model/action-map.model.d.ts +4 -4
- package/dist/navigation/model/navigation-ctx.model.d.ts +1 -16
- package/dist/navigation/model/navigation-ctx.model.js +0 -1
- package/dist/navigation/model/navigation-tree.model.d.ts +2 -1
- package/dist/navigation/navigation.d.ts +3 -10
- package/dist/navigation/navigation.js +73 -79
- package/dist/navigation/state/command-line.state.d.ts +5 -0
- package/dist/navigation/state/command-line.state.js +21 -4
- package/dist/navigation/state/state.d.ts +17 -9
- package/dist/navigation/state/state.js +34 -35
- package/dist/navigation/utils/get-command-line-intent.d.ts +2 -2
- package/dist/navigation/utils/get-command-line-intent.js +7 -7
- package/dist/navigation/utils/key-intent.d.ts +5 -5
- package/dist/navigation/utils/key-intent.js +68 -74
- package/package.json +2 -2
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const moveChildNextWithinParent: (ctx: NavigateCtx) => void;
|
|
1
|
+
export declare const moveChildToNextParent: () => void;
|
|
2
|
+
export declare const moveChildToPreviousParent: () => void;
|
|
3
|
+
export declare const moveChildPreviousWithinParent: () => void;
|
|
4
|
+
export declare const moveChildNextWithinParent: () => void;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { appState } from '../../state/state.js';
|
|
2
|
+
import { navigator } from '../default/navigation-action-utils.js';
|
|
1
3
|
function moveItemInArray({ array, from, to, }) {
|
|
2
4
|
if (from < 0 || from >= array.length || to < 0 || to >= array.length)
|
|
3
5
|
return;
|
|
@@ -5,49 +7,60 @@ function moveItemInArray({ array, from, to, }) {
|
|
|
5
7
|
if (item)
|
|
6
8
|
array.splice(to, 0, item);
|
|
7
9
|
}
|
|
8
|
-
function moveNodeToSiblingContainer(
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
10
|
+
function moveNodeToSiblingContainer(direction) {
|
|
11
|
+
const currentNode = appState.breadCrumb.at(-1);
|
|
12
|
+
const parentNode = appState.breadCrumb.at(-2);
|
|
13
|
+
if (!currentNode || !parentNode)
|
|
12
14
|
return;
|
|
13
|
-
if (!Array.isArray(
|
|
15
|
+
if (!Array.isArray(parentNode.children))
|
|
14
16
|
return;
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const currentNodeIndex = parentNode.children.findIndex(x => x.id === currentNode.id);
|
|
18
|
+
const targetNodeIndex = currentNodeIndex + direction;
|
|
19
|
+
if (currentNodeIndex < 0 ||
|
|
20
|
+
targetNodeIndex < 0 ||
|
|
21
|
+
targetNodeIndex >= parentNode.children.length)
|
|
20
22
|
return;
|
|
21
|
-
const
|
|
22
|
-
if (!
|
|
23
|
+
const siblingNode = parentNode.children[targetNodeIndex];
|
|
24
|
+
if (!siblingNode)
|
|
23
25
|
return;
|
|
24
|
-
if (!Array.isArray(
|
|
26
|
+
if (!Array.isArray(currentNode.children))
|
|
25
27
|
return;
|
|
26
|
-
if (!Array.isArray(
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
28
|
+
if (!Array.isArray(siblingNode.children))
|
|
29
|
+
siblingNode.children = []; // normalize empty container
|
|
30
|
+
const currentSelectionIndex = appState.selectedIndex;
|
|
31
|
+
if (currentSelectionIndex < 0 ||
|
|
32
|
+
currentSelectionIndex >= currentNode.children.length)
|
|
30
33
|
return;
|
|
31
|
-
const [
|
|
32
|
-
if (!
|
|
34
|
+
const [moveNode] = currentNode.children.splice(currentSelectionIndex, 1);
|
|
35
|
+
if (!moveNode)
|
|
33
36
|
return;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
siblingNode.children.push(moveNode);
|
|
38
|
+
navigator.navigate({
|
|
39
|
+
selectedIndex: siblingNode.children.length - 1,
|
|
40
|
+
currentNode: siblingNode,
|
|
41
|
+
});
|
|
37
42
|
}
|
|
38
|
-
export const moveChildToNextParent = (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
export const moveChildToNextParent = () => {
|
|
44
|
+
moveNodeToSiblingContainer(1);
|
|
45
|
+
};
|
|
46
|
+
export const moveChildToPreviousParent = () => {
|
|
47
|
+
moveNodeToSiblingContainer(-1);
|
|
48
|
+
};
|
|
49
|
+
function moveChildWithinParent(direction) {
|
|
50
|
+
const from = appState.selectedIndex;
|
|
42
51
|
const to = from + direction;
|
|
43
|
-
if (to < 0 || to >=
|
|
52
|
+
if (to < 0 || to >= appState.currentNode.children.length)
|
|
44
53
|
return;
|
|
45
54
|
moveItemInArray({
|
|
46
|
-
array:
|
|
55
|
+
array: appState.currentNode.children,
|
|
47
56
|
from,
|
|
48
57
|
to,
|
|
49
58
|
});
|
|
50
|
-
|
|
59
|
+
navigator.navigate({ selectedIndex: to });
|
|
51
60
|
}
|
|
52
|
-
export const moveChildPreviousWithinParent = (
|
|
53
|
-
|
|
61
|
+
export const moveChildPreviousWithinParent = () => {
|
|
62
|
+
moveChildWithinParent(-1);
|
|
63
|
+
};
|
|
64
|
+
export const moveChildNextWithinParent = () => {
|
|
65
|
+
moveChildWithinParent(1);
|
|
66
|
+
};
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { Mode } from '../../model/action-map.model.js';
|
|
2
2
|
import { patchState } from '../../state/state.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Intent } from '../../utils/key-intent.js';
|
|
4
4
|
import { moveChildNextWithinParent, moveChildPreviousWithinParent, moveChildToNextParent, moveChildToPreviousParent, } from './move-actions-utils.js';
|
|
5
5
|
export const toggleMoveMode = [
|
|
6
6
|
{
|
|
7
|
-
intent:
|
|
7
|
+
intent: Intent.Exit,
|
|
8
|
+
mode: Mode.MOVE,
|
|
9
|
+
description: '[Y] Exit yank mode',
|
|
10
|
+
action: () => {
|
|
11
|
+
patchState({
|
|
12
|
+
mode: Mode.DEFAULT,
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
intent: Intent.InitMove,
|
|
8
18
|
mode: Mode.DEFAULT,
|
|
9
19
|
description: '[Y] Toggle move/yank mode',
|
|
10
20
|
action: () => {
|
|
@@ -14,7 +24,7 @@ export const toggleMoveMode = [
|
|
|
14
24
|
},
|
|
15
25
|
},
|
|
16
26
|
{
|
|
17
|
-
intent:
|
|
27
|
+
intent: Intent.InitMove, // Change name to toggle move?
|
|
18
28
|
mode: Mode.MOVE,
|
|
19
29
|
action: () => {
|
|
20
30
|
patchState({
|
|
@@ -25,28 +35,24 @@ export const toggleMoveMode = [
|
|
|
25
35
|
];
|
|
26
36
|
export const moveWithinParent = [
|
|
27
37
|
{
|
|
28
|
-
|
|
29
|
-
description: '[Y, ARROW KEYS] Move item',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
intent: KeyIntent.MovePreviousItem,
|
|
38
|
+
intent: Intent.MovePreviousItem,
|
|
33
39
|
mode: Mode.MOVE,
|
|
34
40
|
action: moveChildPreviousWithinParent,
|
|
35
41
|
},
|
|
36
42
|
{
|
|
37
|
-
intent:
|
|
43
|
+
intent: Intent.MoveNextItem,
|
|
38
44
|
mode: Mode.MOVE,
|
|
39
45
|
action: moveChildNextWithinParent,
|
|
40
46
|
},
|
|
41
47
|
];
|
|
42
48
|
export const moveAcrossParents = [
|
|
43
49
|
{
|
|
44
|
-
intent:
|
|
50
|
+
intent: Intent.MoveToNextContainer,
|
|
45
51
|
mode: Mode.MOVE,
|
|
46
52
|
action: moveChildToNextParent,
|
|
47
53
|
},
|
|
48
54
|
{
|
|
49
|
-
intent:
|
|
55
|
+
intent: Intent.MoveToPreviousContainer,
|
|
50
56
|
mode: Mode.MOVE,
|
|
51
57
|
action: moveChildToPreviousParent,
|
|
52
58
|
},
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { CommandLineActionEntry } from '../model/action-map.model.js';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const commands: CommandLineActionEntry[];
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { addSwimlaneAction, addTicketAction, } from '../actions/add-item/add-item-actions.js';
|
|
2
2
|
import { Mode } from '../model/action-map.model.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { getCommandLineArgumentValue } from '../state/command-line.state.js';
|
|
4
|
+
import { patchState, updateState } from '../state/state.js';
|
|
5
|
+
import { CmdIntent } from './command-line-sequence-intent.js';
|
|
6
|
+
export const commands = [
|
|
6
7
|
{
|
|
7
|
-
intent:
|
|
8
|
+
intent: CmdIntent.ViewHelp,
|
|
8
9
|
mode: Mode.COMMAND_LINE,
|
|
9
|
-
action: () => {
|
|
10
|
-
patchState({
|
|
11
|
-
mode: Mode.HELP,
|
|
12
|
-
});
|
|
13
|
-
},
|
|
10
|
+
action: () => patchState({ mode: Mode.HELP }),
|
|
14
11
|
},
|
|
15
12
|
{
|
|
16
|
-
intent:
|
|
13
|
+
intent: CmdIntent.AddSwimlane,
|
|
17
14
|
mode: Mode.COMMAND_LINE,
|
|
18
15
|
action: (...args) => {
|
|
19
16
|
addSwimlaneAction(...args);
|
|
@@ -21,11 +18,27 @@ export const commandLineSequenceActions = [
|
|
|
21
18
|
},
|
|
22
19
|
},
|
|
23
20
|
{
|
|
24
|
-
intent:
|
|
21
|
+
intent: CmdIntent.AddTicket,
|
|
25
22
|
mode: Mode.COMMAND_LINE,
|
|
26
23
|
action: (...args) => {
|
|
27
24
|
addTicketAction(...args);
|
|
28
25
|
patchState({ mode: Mode.DEFAULT });
|
|
29
26
|
},
|
|
30
27
|
},
|
|
28
|
+
{
|
|
29
|
+
intent: CmdIntent.Rename,
|
|
30
|
+
mode: Mode.COMMAND_LINE,
|
|
31
|
+
action: () => {
|
|
32
|
+
updateState(s => ({
|
|
33
|
+
...s,
|
|
34
|
+
mode: Mode.DEFAULT,
|
|
35
|
+
currentNode: s.currentNode
|
|
36
|
+
? {
|
|
37
|
+
...s.currentNode,
|
|
38
|
+
name: getCommandLineArgumentValue(),
|
|
39
|
+
}
|
|
40
|
+
: null,
|
|
41
|
+
}));
|
|
42
|
+
},
|
|
43
|
+
},
|
|
31
44
|
];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const CmdIntent: {
|
|
2
2
|
readonly None: "none";
|
|
3
3
|
readonly AddSwimlane: "add-swimlane";
|
|
4
4
|
readonly AddTicket: "add-ticket";
|
|
5
5
|
readonly ViewHelp: "view-help";
|
|
6
|
+
readonly Rename: "rename";
|
|
6
7
|
};
|
|
7
|
-
export declare const
|
|
8
|
+
export declare const getCommandIntent: (command: string) => (typeof CmdIntent)[keyof typeof CmdIntent];
|
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
import { BoardItemTypes } from '../../board/model/board.model.js';
|
|
2
|
-
import {
|
|
3
|
-
export const
|
|
2
|
+
import { appState } from '../state/state.js';
|
|
3
|
+
export const CmdIntent = {
|
|
4
4
|
None: 'none',
|
|
5
5
|
AddSwimlane: 'add-swimlane',
|
|
6
6
|
AddTicket: 'add-ticket',
|
|
7
7
|
ViewHelp: 'view-help',
|
|
8
|
+
Rename: 'rename',
|
|
8
9
|
};
|
|
9
|
-
export const
|
|
10
|
-
const actionContext =
|
|
10
|
+
export const getCommandIntent = (command) => {
|
|
11
|
+
const actionContext = appState?.currentNode?.actionContext;
|
|
11
12
|
if (!actionContext)
|
|
12
|
-
return
|
|
13
|
+
return CmdIntent.None;
|
|
13
14
|
switch (command) {
|
|
14
15
|
case 'help':
|
|
15
16
|
case 'he':
|
|
16
|
-
return
|
|
17
|
+
return CmdIntent.ViewHelp;
|
|
18
|
+
case 'rename':
|
|
19
|
+
return CmdIntent.Rename;
|
|
17
20
|
case 'add':
|
|
18
21
|
switch (actionContext) {
|
|
19
22
|
case BoardItemTypes.SWIMLANE:
|
|
20
|
-
return
|
|
23
|
+
return CmdIntent.AddSwimlane;
|
|
21
24
|
case BoardItemTypes.TICKET:
|
|
22
|
-
return
|
|
25
|
+
return CmdIntent.AddTicket;
|
|
23
26
|
default:
|
|
24
|
-
return
|
|
27
|
+
return CmdIntent.None;
|
|
25
28
|
}
|
|
26
29
|
default:
|
|
27
|
-
return
|
|
30
|
+
return CmdIntent.None;
|
|
28
31
|
}
|
|
29
32
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { addSwimlaneAction, addTicketAction, } from '../actions/add-item/add-item-actions.js';
|
|
2
|
+
import { Mode } from '../model/action-map.model.js';
|
|
3
|
+
import { getCommandLineArgumentValue } from '../state/command-line.state.js';
|
|
4
|
+
import { appState, patchState, updateState } from '../state/state.js';
|
|
5
|
+
import { CmdIntent } from './command-line-sequence-intent.js';
|
|
6
|
+
export const commands = [
|
|
7
|
+
{
|
|
8
|
+
intent: CmdIntent.ViewHelp,
|
|
9
|
+
mode: Mode.COMMAND_LINE,
|
|
10
|
+
action: () => patchState({ mode: Mode.HELP }),
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
intent: CmdIntent.AddSwimlane,
|
|
14
|
+
mode: Mode.COMMAND_LINE,
|
|
15
|
+
action: (...args) => {
|
|
16
|
+
addSwimlaneAction(...args);
|
|
17
|
+
patchState({ mode: Mode.DEFAULT });
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
intent: CmdIntent.AddTicket,
|
|
22
|
+
mode: Mode.COMMAND_LINE,
|
|
23
|
+
action: (...args) => {
|
|
24
|
+
addTicketAction(...args);
|
|
25
|
+
patchState({ mode: Mode.DEFAULT });
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
intent: CmdIntent.Rename,
|
|
30
|
+
mode: Mode.COMMAND_LINE,
|
|
31
|
+
action: () => {
|
|
32
|
+
updateState(s => {
|
|
33
|
+
// Update board on file
|
|
34
|
+
const newBoard = appState.rootNode;
|
|
35
|
+
const currentId = s.currentNode?.id;
|
|
36
|
+
if (!currentId)
|
|
37
|
+
return s;
|
|
38
|
+
const findItemInBoard = (item, id) => {
|
|
39
|
+
if (item.id === id)
|
|
40
|
+
return item;
|
|
41
|
+
return (item.children.find((child) => findItemInBoard(child, id)) ||
|
|
42
|
+
undefined);
|
|
43
|
+
};
|
|
44
|
+
const newName = getCommandLineArgumentValue();
|
|
45
|
+
const itemInBoard = findItemInBoard(newBoard, currentId);
|
|
46
|
+
if (!itemInBoard)
|
|
47
|
+
return s;
|
|
48
|
+
itemInBoard.name = newName;
|
|
49
|
+
return {
|
|
50
|
+
...s,
|
|
51
|
+
mode: Mode.DEFAULT,
|
|
52
|
+
rootNode: newBoard,
|
|
53
|
+
currentNode: itemInBoard,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initListeners(): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
import { appState } from './state/state.js';
|
|
3
|
+
import { getKeyIntent } from './utils/key-intent.js';
|
|
4
|
+
import { navigator } from './actions/default/navigation-action-utils.js';
|
|
5
|
+
let currentKeypressListener;
|
|
6
|
+
const getKeyPressListener = () => {
|
|
7
|
+
return async function onKeyPress(_, key) {
|
|
8
|
+
if (key.ctrl && key.name === 'c')
|
|
9
|
+
return navigator.exit();
|
|
10
|
+
const filteredActions = appState.availableActions.filter(a => a.mode === appState.mode);
|
|
11
|
+
const actionMeta = filteredActions.find(({ intent, mode }) => getKeyIntent(key, mode) === intent);
|
|
12
|
+
if (!actionMeta?.action)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
const res = actionMeta.action(navigator, actionMeta, key);
|
|
16
|
+
if (res instanceof Promise)
|
|
17
|
+
await res;
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
console.error(err);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export function initListeners() {
|
|
25
|
+
if (currentKeypressListener) {
|
|
26
|
+
process.stdin.removeListener('keypress', currentKeypressListener);
|
|
27
|
+
}
|
|
28
|
+
currentKeypressListener = getKeyPressListener();
|
|
29
|
+
readline.emitKeypressEvents(process.stdin);
|
|
30
|
+
if (process.stdin.isTTY)
|
|
31
|
+
process.stdin.setRawMode(true);
|
|
32
|
+
process.stdin.on('keypress', currentKeypressListener);
|
|
33
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Navigator } from '../actions/default/navigation-action-utils.js';
|
|
2
2
|
import readline from 'readline';
|
|
3
3
|
export declare const Mode: {
|
|
4
4
|
readonly DEFAULT: "default";
|
|
@@ -9,9 +9,9 @@ export declare const Mode: {
|
|
|
9
9
|
export type ModeUnion = (typeof Mode)[keyof typeof Mode];
|
|
10
10
|
export type ActionEntry = {
|
|
11
11
|
intent?: string;
|
|
12
|
-
mode: ModeUnion
|
|
12
|
+
mode: ModeUnion;
|
|
13
13
|
description?: `[${string}] ${string}`;
|
|
14
|
-
action?: (...args: [
|
|
14
|
+
action?: (...args: [Navigator, ActionEntry, readline.Key]) => void | Promise<void>;
|
|
15
15
|
};
|
|
16
16
|
export type ActionMap<T extends Record<string, any[]>> = {
|
|
17
17
|
[K in keyof T]: ActionEntry[];
|
|
@@ -21,6 +21,6 @@ type CommandLineInput = {
|
|
|
21
21
|
command: string;
|
|
22
22
|
};
|
|
23
23
|
export type CommandLineActionEntry = Omit<ActionEntry, 'action'> & {
|
|
24
|
-
action?: (...args: [
|
|
24
|
+
action?: (...args: [Navigator, CommandLineActionEntry, CommandLineInput]) => void | Promise<void>;
|
|
25
25
|
};
|
|
26
26
|
export {};
|
|
@@ -1,16 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export interface NavigateCtx {
|
|
3
|
-
_selectionIndex: number;
|
|
4
|
-
breadCrumb: Array<NavigationTree<NavigationTree>>;
|
|
5
|
-
navigationNode: NavigationTree<NavigationTree>;
|
|
6
|
-
children: NavigationTree[];
|
|
7
|
-
getSelectedIndex(): number;
|
|
8
|
-
updateSelection(index: number): void;
|
|
9
|
-
selectNone(): void;
|
|
10
|
-
reInvokeNavigate(index: number, breadCrumb: NavigationTree<NavigationTree>[]): void;
|
|
11
|
-
render(): void;
|
|
12
|
-
confirm(selected: NavigationTree<NavigationTree>): void;
|
|
13
|
-
exit(): void;
|
|
14
|
-
enterChildNode(node: NavigationTree<NavigationTree>): void;
|
|
15
|
-
enterParentNode(): void;
|
|
16
|
-
}
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BoardItemTypes } from '../../board/model/board.model.js';
|
|
1
2
|
export type NavigationTree<TMeta = Record<string, unknown>> = {
|
|
2
3
|
id: string;
|
|
3
4
|
isSelected: boolean;
|
|
@@ -5,6 +6,6 @@ export type NavigationTree<TMeta = Record<string, unknown>> = {
|
|
|
5
6
|
name: string;
|
|
6
7
|
description?: string;
|
|
7
8
|
children: NavigationTree<unknown>[];
|
|
8
|
-
actionContext:
|
|
9
|
+
actionContext: (typeof BoardItemTypes)[keyof typeof BoardItemTypes];
|
|
9
10
|
enableChildNavigationAcrossContainers?: boolean;
|
|
10
11
|
} & TMeta;
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
breadCrumb: Array<NavigationTree<NavigationTree>>;
|
|
5
|
-
callbacks: Partial<{
|
|
6
|
-
render: () => void;
|
|
7
|
-
onSelectChange: (s: T['children'][number] | undefined, breadCrumb: T['children']) => void;
|
|
8
|
-
onConfirm: (s: T['children'][number]) => void;
|
|
9
|
-
}>;
|
|
10
|
-
}): void;
|
|
1
|
+
import { NavigateUtils } from './model/navigation-ctx.model.js';
|
|
2
|
+
export declare const navigationUtils: NavigateUtils;
|
|
3
|
+
export declare function initListeners(): void;
|
|
@@ -1,88 +1,82 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
|
-
import {
|
|
2
|
+
import { appState, patchState, updateState } from './state/state.js';
|
|
3
3
|
import { getKeyIntent } from './utils/key-intent.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (breadCrumb.length < 2)
|
|
51
|
-
return; // Need at least grandparent + parent
|
|
52
|
-
const ancestors = breadCrumb;
|
|
53
|
-
const parent = ancestors[ancestors.length - 1];
|
|
54
|
-
const grandParent = ancestors[ancestors.length - 2];
|
|
55
|
-
if (!parent || !grandParent)
|
|
56
|
-
return;
|
|
57
|
-
const parentIndex = grandParent?.children.findIndex(x => parent.id === x.id);
|
|
58
|
-
this.reInvokeNavigate(parentIndex, breadCrumb.slice(0, -1)); // Go to parent level
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
ctx.updateSelection(index);
|
|
62
|
-
async function onKeyPress(_, key) {
|
|
63
|
-
if (key.ctrl && key.name === 'c')
|
|
64
|
-
return ctx.exit();
|
|
65
|
-
const filteredActions = navigationState.availableActions.filter(x => x.mode === navigationState.mode);
|
|
66
|
-
const actionMeta = filteredActions.find(actionMetaItem => {
|
|
67
|
-
const intent = getKeyIntent(key, ctx, navigationState.mode);
|
|
68
|
-
return intent === actionMetaItem.intent;
|
|
4
|
+
let currentKeypressListener;
|
|
5
|
+
export const navigationUtils = {
|
|
6
|
+
updateSelection: (idx) => {
|
|
7
|
+
updateState(oldState => {
|
|
8
|
+
const currentNode = oldState.breadCrumb.at(-1) ?? oldState.breadCrumb[0];
|
|
9
|
+
if (!currentNode)
|
|
10
|
+
return oldState;
|
|
11
|
+
currentNode.children?.forEach((child, i) => (child.isSelected = i === idx));
|
|
12
|
+
return {
|
|
13
|
+
...oldState,
|
|
14
|
+
selectedIndex: idx,
|
|
15
|
+
currentNode: currentNode, // keep currentNode in sync
|
|
16
|
+
breadCrumb: [...oldState.breadCrumb], // keep reference fresh if you rely on it
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
selectNone() {
|
|
21
|
+
this.updateSelection(-1);
|
|
22
|
+
},
|
|
23
|
+
confirm(sel) {
|
|
24
|
+
console.log(sel);
|
|
25
|
+
},
|
|
26
|
+
exit() {
|
|
27
|
+
process.exit(0);
|
|
28
|
+
},
|
|
29
|
+
enterChildNode(childNode) {
|
|
30
|
+
patchState({
|
|
31
|
+
breadCrumb: [...appState.breadCrumb, childNode],
|
|
32
|
+
currentNode: childNode,
|
|
33
|
+
selectedIndex: 0,
|
|
34
|
+
});
|
|
35
|
+
this.updateSelection(0);
|
|
36
|
+
},
|
|
37
|
+
enterParentNode() {
|
|
38
|
+
const crumb = appState.breadCrumb;
|
|
39
|
+
if (crumb.length < 2)
|
|
40
|
+
return;
|
|
41
|
+
const newCrumb = crumb.slice(0, -1);
|
|
42
|
+
const parentLevelNode = newCrumb.at(-1);
|
|
43
|
+
// find old parent index inside grandparent
|
|
44
|
+
const grandParent = newCrumb.at(-2);
|
|
45
|
+
const parentIndex = grandParent?.children?.findIndex(x => x.id === parentLevelNode.id) ?? 0;
|
|
46
|
+
patchState({
|
|
47
|
+
breadCrumb: newCrumb,
|
|
48
|
+
currentNode: parentLevelNode,
|
|
49
|
+
selectedIndex: parentIndex,
|
|
69
50
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
this.updateSelection(parentIndex);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const getKeyPressListener = () => {
|
|
55
|
+
navigationUtils.updateSelection(appState.selectedIndex ?? 0);
|
|
56
|
+
return async function onKeyPress(_, key) {
|
|
57
|
+
if (key.ctrl && key.name === 'c')
|
|
58
|
+
return navigationUtils.exit();
|
|
59
|
+
const filteredActions = appState.availableActions.filter(a => a.mode === appState.mode);
|
|
60
|
+
const actionMeta = filteredActions.find(({ intent, mode }) => getKeyIntent(key, mode) === intent);
|
|
61
|
+
if (!actionMeta?.action)
|
|
62
|
+
return;
|
|
63
|
+
try {
|
|
64
|
+
const res = actionMeta.action(navigationUtils, actionMeta, key);
|
|
65
|
+
if (res instanceof Promise)
|
|
66
|
+
await res;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error(err);
|
|
79
70
|
}
|
|
80
|
-
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
export function initListeners() {
|
|
74
|
+
if (currentKeypressListener) {
|
|
75
|
+
process.stdin.removeListener('keypress', currentKeypressListener);
|
|
81
76
|
}
|
|
82
|
-
|
|
83
|
-
callbacks.render?.();
|
|
77
|
+
currentKeypressListener = getKeyPressListener();
|
|
84
78
|
readline.emitKeypressEvents(process.stdin);
|
|
85
79
|
if (process.stdin.isTTY)
|
|
86
80
|
process.stdin.setRawMode(true);
|
|
87
|
-
process.stdin.on('keypress',
|
|
81
|
+
process.stdin.on('keypress', currentKeypressListener);
|
|
88
82
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
export declare const commandDelimiter = " ";
|
|
1
2
|
export type CommandLineState = {
|
|
2
3
|
value: string;
|
|
3
4
|
commandHistory: string[];
|
|
4
5
|
commandHistoryIndex: number;
|
|
5
6
|
};
|
|
6
7
|
export declare let commandLineState: CommandLineState;
|
|
8
|
+
type Listener = () => void;
|
|
9
|
+
export declare const subscribeCommandLineState: (listener: Listener) => () => boolean;
|
|
7
10
|
export declare const updateCommandLineState: (cb: (old: CommandLineState) => CommandLineState) => void;
|
|
8
11
|
export declare const updateCommandLineInput: (cb: (previous: string) => string) => void;
|
|
9
12
|
export declare const updateCommandHistory: () => void;
|
|
@@ -11,3 +14,5 @@ export declare const getPrevCommand: () => void;
|
|
|
11
14
|
export declare const getNextCommand: () => void;
|
|
12
15
|
export declare const clearCommandLine: () => void;
|
|
13
16
|
export declare const getCommandLineInput: () => string;
|
|
17
|
+
export declare const getCommandLineArgumentValue: () => string;
|
|
18
|
+
export {};
|