@mak-98/dirvi-cli 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/effect-to-action.d.ts +4 -3
- package/dist/application/effect-to-action.d.ts.map +1 -1
- package/dist/application/effect-to-action.js +75 -71
- package/dist/application/reducer-action.d.ts +15 -11
- package/dist/application/reducer-action.d.ts.map +1 -1
- package/dist/application/reducer.d.ts +3 -2
- package/dist/application/reducer.d.ts.map +1 -1
- package/dist/application/reducer.js +66 -29
- package/dist/domain/app-api.d.ts +47 -0
- package/dist/domain/app-api.d.ts.map +1 -0
- package/dist/domain/app-api.js +21 -0
- package/dist/domain/event-message.d.ts +6 -6
- package/dist/domain/event-message.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/infrastructure/get-dir-entries.d.ts +3 -0
- package/dist/infrastructure/get-dir-entries.d.ts.map +1 -0
- package/dist/infrastructure/get-dir-entries.js +33 -0
- package/dist/infrastructure/get-dir-lazy-entries.d.ts +3 -0
- package/dist/infrastructure/get-dir-lazy-entries.d.ts.map +1 -0
- package/dist/infrastructure/get-dir-lazy-entries.js +33 -0
- package/dist/infrastructure/get-unix-abs-path.d.ts +3 -0
- package/dist/infrastructure/get-unix-abs-path.d.ts.map +1 -0
- package/dist/infrastructure/get-unix-abs-path.js +14 -0
- package/dist/infrastructure/ink-input-to-user-input.d.ts +4 -0
- package/dist/infrastructure/ink-input-to-user-input.d.ts.map +1 -0
- package/dist/infrastructure/{user-input.js → ink-input-to-user-input.js} +10 -0
- package/dist/infrastructure/load-posix-app-api.d.ts +4 -0
- package/dist/infrastructure/load-posix-app-api.d.ts.map +1 -0
- package/dist/infrastructure/load-posix-app-api.js +82 -0
- package/dist/ui/App.d.ts +14 -8
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +68 -31
- package/dist/ui/AppShell.d.ts +4 -9
- package/dist/ui/AppShell.d.ts.map +1 -1
- package/dist/ui/AppShell.js +21 -7
- package/dist/ui/LoadingApp.d.ts +11 -0
- package/dist/ui/LoadingApp.d.ts.map +1 -0
- package/dist/ui/LoadingApp.js +67 -0
- package/dist/ui/components/StatusBar.d.ts +8 -0
- package/dist/ui/components/StatusBar.d.ts.map +1 -0
- package/dist/ui/components/StatusBar.js +7 -0
- package/dist/ui/components/ViewRowComponent.js +2 -2
- package/dist/ui/hooks/useView.d.ts +3 -2
- package/dist/ui/hooks/useView.d.ts.map +1 -1
- package/dist/ui/hooks/useView.js +5 -6
- package/dist/ui/view.d.ts +5 -4
- package/dist/ui/view.d.ts.map +1 -1
- package/dist/ui/view.js +55 -31
- package/package.json +3 -2
- package/dist/application/action.d.ts +0 -19
- package/dist/application/action.d.ts.map +0 -1
- package/dist/application/action.js +0 -1
- package/dist/application/dir-buffer-helpers.d.ts +0 -3
- package/dist/application/dir-buffer-helpers.d.ts.map +0 -1
- package/dist/application/dir-buffer-helpers.js +0 -21
- package/dist/application/display-rows.d.ts +0 -4
- package/dist/application/display-rows.d.ts.map +0 -1
- package/dist/application/display-rows.js +0 -50
- package/dist/application/fold-helpers.d.ts +0 -8
- package/dist/application/fold-helpers.d.ts.map +0 -1
- package/dist/application/fold-helpers.js +0 -43
- package/dist/application/intent-to-effect.d.ts +0 -39
- package/dist/application/intent-to-effect.d.ts.map +0 -1
- package/dist/application/intent-to-effect.js +0 -118
- package/dist/application/user-input-to-intent.d.ts +0 -15
- package/dist/application/user-input-to-intent.d.ts.map +0 -1
- package/dist/application/user-input-to-intent.js +0 -56
- package/dist/domain/print-message.d.ts +0 -9
- package/dist/domain/print-message.d.ts.map +0 -1
- package/dist/domain/print-message.js +0 -1
- package/dist/infrastructure/input.d.ts +0 -7
- package/dist/infrastructure/input.d.ts.map +0 -1
- package/dist/infrastructure/input.js +0 -81
- package/dist/infrastructure/load-initial-props.d.ts +0 -3
- package/dist/infrastructure/load-initial-props.d.ts.map +0 -1
- package/dist/infrastructure/load-initial-props.js +0 -29
- package/dist/infrastructure/user-input.d.ts +0 -17
- package/dist/infrastructure/user-input.d.ts.map +0 -1
- package/dist/ui/App2.d.ts +0 -11
- package/dist/ui/App2.d.ts.map +0 -1
- package/dist/ui/App2.js +0 -96
- package/dist/ui/components/DirEntries.d.ts +0 -8
- package/dist/ui/components/DirEntries.d.ts.map +0 -1
- package/dist/ui/components/DirEntries.js +0 -39
- package/dist/ui/components/UnixEntry.d.ts +0 -6
- package/dist/ui/components/UnixEntry.d.ts.map +0 -1
- package/dist/ui/components/UnixEntry.js +0 -12
- package/dist/ui/components/UnixEntryComponent.d.ts +0 -6
- package/dist/ui/components/UnixEntryComponent.d.ts.map +0 -1
- package/dist/ui/components/UnixEntryComponent.js +0 -12
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { watch } from 'node:fs';
|
|
3
|
+
import { createAppApis } from '../domain/app-api.js';
|
|
4
|
+
import { getUnixAbsPath } from './get-unix-abs-path.js';
|
|
5
|
+
import { getDirEntries } from './get-dir-entries.js';
|
|
6
|
+
export function loadPosixAppApi(directory) {
|
|
7
|
+
const unixAbsPath = getUnixAbsPath(directory ?? process.cwd());
|
|
8
|
+
const apis = createAppApis();
|
|
9
|
+
const subscribeToResync = createFsResyncSubscription(join(unixAbsPath));
|
|
10
|
+
return {
|
|
11
|
+
name: `Posix(${unixAbsPath})`,
|
|
12
|
+
emptyForestMessage: 'The directory is empty.',
|
|
13
|
+
loadBranches: (path) => {
|
|
14
|
+
const address = join(unixAbsPath, ...path);
|
|
15
|
+
return getDirEntries(address);
|
|
16
|
+
},
|
|
17
|
+
subscribeToResync,
|
|
18
|
+
...apis,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// `recursive: true` is supported by macOS and Windows, but
|
|
22
|
+
// recursive watching is not supported on all Node/POSIX
|
|
23
|
+
// platforms: Linux unsupported.
|
|
24
|
+
function createFsResyncSubscription(directory) {
|
|
25
|
+
const listeners = new Set();
|
|
26
|
+
let watcher;
|
|
27
|
+
let notificationTimer;
|
|
28
|
+
function notifyListeners() {
|
|
29
|
+
for (const listener of listeners) {
|
|
30
|
+
listener();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function scheduleNotification() {
|
|
34
|
+
// fs.watch can emit several events for one filesystem operation.
|
|
35
|
+
// Debounce them into a single resync request.
|
|
36
|
+
if (notificationTimer !== undefined) {
|
|
37
|
+
clearTimeout(notificationTimer);
|
|
38
|
+
}
|
|
39
|
+
notificationTimer = setTimeout(() => {
|
|
40
|
+
notificationTimer = undefined;
|
|
41
|
+
if (listeners.size > 0) {
|
|
42
|
+
notifyListeners();
|
|
43
|
+
}
|
|
44
|
+
}, 100);
|
|
45
|
+
}
|
|
46
|
+
function startWatcher() {
|
|
47
|
+
if (watcher !== undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
watcher = watch(directory, {
|
|
51
|
+
recursive: true,
|
|
52
|
+
}, (_eventType, _filename) => {
|
|
53
|
+
scheduleNotification();
|
|
54
|
+
});
|
|
55
|
+
watcher.on('error', () => {
|
|
56
|
+
// The next loadBranches call will report the relevant error.
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function stopWatcher() {
|
|
60
|
+
if (notificationTimer !== undefined) {
|
|
61
|
+
clearTimeout(notificationTimer);
|
|
62
|
+
notificationTimer = undefined;
|
|
63
|
+
}
|
|
64
|
+
watcher?.close();
|
|
65
|
+
watcher = undefined;
|
|
66
|
+
}
|
|
67
|
+
return (listener) => {
|
|
68
|
+
listeners.add(listener);
|
|
69
|
+
startWatcher();
|
|
70
|
+
let subscribed = true;
|
|
71
|
+
return () => {
|
|
72
|
+
if (!subscribed) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
subscribed = false;
|
|
76
|
+
listeners.delete(listener);
|
|
77
|
+
if (listeners.size === 0) {
|
|
78
|
+
stopWatcher();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
}
|
package/dist/ui/App.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { State } from 'dirvi-lib';
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { IntentToEffect, SerializableKey, State, TreeNode } from 'dirvi-lib';
|
|
2
|
+
import type { EventMessage } from '../domain/event-message.js';
|
|
3
|
+
import { EffectToAction } from '../application/effect-to-action.js';
|
|
4
|
+
import { AppApi } from '../domain/app-api.js';
|
|
5
|
+
import { Reducer } from '../application/reducer.js';
|
|
6
|
+
type AppProps<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = {
|
|
7
|
+
appApi: AppApi<Id, BufferNode>;
|
|
8
|
+
initialState: State<Id, BufferNode>;
|
|
9
|
+
reducer: Reducer<Id, BufferNode>;
|
|
10
|
+
intentToEffect: IntentToEffect<Id, BufferNode>;
|
|
11
|
+
effectToAction: EffectToAction<Id, BufferNode>;
|
|
12
|
+
print?: (message: EventMessage<Id, BufferNode>) => void;
|
|
8
13
|
onError?: (error: Error) => void;
|
|
9
14
|
};
|
|
10
|
-
export declare function App({
|
|
15
|
+
export declare function App<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>({ appApi, initialState, reducer, intentToEffect, effectToAction, print, onError, }: AppProps<Id, BufferNode>): import("react").JSX.Element | null;
|
|
16
|
+
export {};
|
|
11
17
|
//# sourceMappingURL=App.d.ts.map
|
package/dist/ui/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,cAAc,EAAE,eAAe,EAC/B,KAAK,EACL,QAAQ,EAET,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAGpE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAGpD,KAAK,QAAQ,CACX,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IACzC;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/B,YAAY,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/C,cAAc,EAAE,cAAc,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,IAAI,CAAC;IACxD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,wBAAgB,GAAG,CACjB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,EACA,MAAM,EACN,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,KAAK,EACL,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,sCAoL1B"}
|
package/dist/ui/App.js
CHANGED
|
@@ -1,48 +1,86 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { getDirLazyEntries, NavigationNode } from 'dirvi-lib';
|
|
6
|
-
import { useView } from './hooks/useView.js';
|
|
7
|
-
import { reducer } from '../application/reducer.js';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useApp, useInput, useWindowSize } from 'ink';
|
|
3
|
+
import { useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
4
|
+
import { userInputToIntent, } from 'dirvi-lib';
|
|
8
5
|
import { ViewRowComponent } from './components/ViewRowComponent.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
export function App({ cwdAddress, initialState, print, onError }) {
|
|
6
|
+
import { StatusBar } from './components/StatusBar.js';
|
|
7
|
+
import { inkInputToUserInput } from '../infrastructure/ink-input-to-user-input.js';
|
|
8
|
+
import { useView } from './hooks/useView.js';
|
|
9
|
+
export function App({ appApi, initialState, reducer, intentToEffect, effectToAction, print, onError, }) {
|
|
14
10
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
11
|
+
// Give `subscribeToResync` callback a way to see current state:
|
|
12
|
+
const stateRef = useRef(state);
|
|
13
|
+
stateRef.current = state;
|
|
14
|
+
const navigation = useMemo(() => appApi.navNodeApi.from(state.buffer, state.foldNode), [appApi.navNodeApi, state.buffer, state.foldNode]);
|
|
15
|
+
const [inputState, setInputState] = useState({
|
|
16
|
+
inputMode: 'normal',
|
|
17
|
+
normalBuffer: '',
|
|
18
|
+
});
|
|
19
|
+
const { rows: terminalRows } = useWindowSize();
|
|
20
|
+
const view = useView(navigation, state, appApi.cursorApi, terminalRows);
|
|
18
21
|
const [exitStatus, setExitStatus] = useState();
|
|
19
|
-
// Print view
|
|
22
|
+
// Print on changes: view, paths of visible leaves
|
|
20
23
|
useEffect(() => {
|
|
21
24
|
print?.({ type: 'view', view: state });
|
|
22
|
-
const
|
|
25
|
+
const visibleLeavesPaths = appApi.navNodeApi.visibleLeavesPaths(navigation);
|
|
23
26
|
print?.({
|
|
24
|
-
type: 'displayed-
|
|
25
|
-
paths:
|
|
27
|
+
type: 'displayed-leaves-paths',
|
|
28
|
+
paths: visibleLeavesPaths,
|
|
29
|
+
});
|
|
30
|
+
}, [appApi.navNodeApi, state, print, navigation]);
|
|
31
|
+
// Subscribe to directory watcher, do not
|
|
32
|
+
// resubscribe on every state change.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
let active = true;
|
|
35
|
+
const unsubscribe = appApi.subscribeToResync(() => {
|
|
36
|
+
const oldState = stateRef.current;
|
|
37
|
+
void appApi.stateApi
|
|
38
|
+
.resync(oldState, appApi.loadBranches)
|
|
39
|
+
.then((newState) => {
|
|
40
|
+
if (!active) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
dispatch({
|
|
44
|
+
kind: 'setState',
|
|
45
|
+
oldState: oldState,
|
|
46
|
+
newState: newState,
|
|
47
|
+
});
|
|
48
|
+
})
|
|
49
|
+
.catch((error) => {
|
|
50
|
+
const appError = error instanceof Error ? error : new Error(String(error));
|
|
51
|
+
onError?.(appError);
|
|
52
|
+
});
|
|
26
53
|
});
|
|
27
|
-
|
|
54
|
+
return () => {
|
|
55
|
+
active = false;
|
|
56
|
+
unsubscribe();
|
|
57
|
+
};
|
|
58
|
+
}, [appApi, onError]);
|
|
28
59
|
function executeEffect(effect) {
|
|
29
60
|
if (effect === undefined) {
|
|
30
61
|
return;
|
|
31
62
|
}
|
|
32
63
|
switch (effect.effectType) {
|
|
33
|
-
case '
|
|
64
|
+
case 'dispatchEffectAction':
|
|
34
65
|
const action = effectToAction(effect.action, navigation, state);
|
|
35
66
|
if (action === undefined) {
|
|
36
67
|
return;
|
|
37
68
|
}
|
|
38
69
|
dispatch(action);
|
|
70
|
+
setInputState({
|
|
71
|
+
inputMode: 'normal',
|
|
72
|
+
normalBuffer: '',
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
case 'setInputState':
|
|
76
|
+
setInputState(effect.inputState);
|
|
39
77
|
return;
|
|
40
|
-
case '
|
|
41
|
-
|
|
42
|
-
|
|
78
|
+
case 'loadBranchEntries': {
|
|
79
|
+
void appApi
|
|
80
|
+
.loadBranches(effect.path)
|
|
43
81
|
.then((entries) => {
|
|
44
82
|
dispatch({
|
|
45
|
-
kind: '
|
|
83
|
+
kind: 'updateBranch',
|
|
46
84
|
path: effect.path,
|
|
47
85
|
entries,
|
|
48
86
|
});
|
|
@@ -54,13 +92,13 @@ export function App({ cwdAddress, initialState, print, onError }) {
|
|
|
54
92
|
});
|
|
55
93
|
return;
|
|
56
94
|
}
|
|
57
|
-
case '
|
|
95
|
+
case 'emitPath':
|
|
58
96
|
print?.({
|
|
59
97
|
type: 'file',
|
|
60
98
|
path: effect.path,
|
|
61
99
|
});
|
|
62
100
|
return;
|
|
63
|
-
case '
|
|
101
|
+
case 'quit':
|
|
64
102
|
setExitStatus(effect.exitMessage);
|
|
65
103
|
return;
|
|
66
104
|
}
|
|
@@ -71,7 +109,7 @@ export function App({ cwdAddress, initialState, print, onError }) {
|
|
|
71
109
|
if (userInput === undefined) {
|
|
72
110
|
return;
|
|
73
111
|
}
|
|
74
|
-
const intent = userInputToIntent(userInput,
|
|
112
|
+
const intent = userInputToIntent(userInput, inputState);
|
|
75
113
|
if (intent === undefined) {
|
|
76
114
|
return;
|
|
77
115
|
}
|
|
@@ -79,8 +117,7 @@ export function App({ cwdAddress, initialState, print, onError }) {
|
|
|
79
117
|
if (effectResult === undefined) {
|
|
80
118
|
return;
|
|
81
119
|
}
|
|
82
|
-
|
|
83
|
-
executeEffect(effectResult.effect);
|
|
120
|
+
executeEffect(effectResult);
|
|
84
121
|
});
|
|
85
122
|
// --- Exit Logic ---
|
|
86
123
|
const { exit } = useApp();
|
|
@@ -97,5 +134,5 @@ export function App({ cwdAddress, initialState, print, onError }) {
|
|
|
97
134
|
return null;
|
|
98
135
|
}
|
|
99
136
|
// --- JSX ---
|
|
100
|
-
return (_jsx(Box, { flexDirection: "column", children: view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "
|
|
137
|
+
return (_jsxs(Box, { flexDirection: "column", height: terminalRows, children: [_jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, children: view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "Empty." })) : (view.rows.map((row) => _jsx(ViewRowComponent, { row: row }, row.id))) }), _jsx(StatusBar, { inputState: inputState })] }));
|
|
101
138
|
}
|
package/dist/ui/AppShell.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EventMessage } from '../domain/event-message.js';
|
|
3
|
-
export type EmptyView = {
|
|
4
|
-
cursor: undefined;
|
|
5
|
-
};
|
|
1
|
+
import { SerializableKey, TreeNode } from 'dirvi-lib';
|
|
6
2
|
export type ShellAppProps = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
print?: (message: EventMessage) => void;
|
|
3
|
+
directory?: string;
|
|
4
|
+
print?: (message: string) => void;
|
|
10
5
|
onError?: (error: Error) => void;
|
|
11
6
|
};
|
|
12
|
-
export declare function AppShell({
|
|
7
|
+
export declare function AppShell<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>({ directory, print, onError }: ShellAppProps): import("react").JSX.Element;
|
|
13
8
|
//# sourceMappingURL=AppShell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/ui/AppShell.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../src/ui/AppShell.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAwB,eAAe,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAI5E,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAIF,wBAAgB,QAAQ,CACtB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,aAAa,+BAyB7C"}
|
package/dist/ui/AppShell.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { loadPosixAppApi } from '../infrastructure/load-posix-app-api.js';
|
|
3
|
+
import { LoadingApp } from './LoadingApp.js';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
// App Shell is for choosing the active app, then loading the api,
|
|
7
|
+
// and showing the app.
|
|
8
|
+
export function AppShell({ directory, print, onError }) {
|
|
9
|
+
// Api owns directory watcher and subscriptions
|
|
10
|
+
const posixAppApi = useMemo(() => loadPosixAppApi(directory), [directory]);
|
|
11
|
+
const emitEventMsg = print
|
|
12
|
+
? (message) => {
|
|
13
|
+
const externalMessage = message.type === 'displayed-leaves-paths'
|
|
14
|
+
? {
|
|
15
|
+
...message,
|
|
16
|
+
paths: message.paths.map((path) => join(...path)),
|
|
17
|
+
}
|
|
18
|
+
: message;
|
|
19
|
+
print(`${JSON.stringify(externalMessage)}\n`);
|
|
20
|
+
}
|
|
21
|
+
: undefined;
|
|
22
|
+
return (_jsx(LoadingApp, { appApi: posixAppApi, ...(emitEventMsg === undefined ? {} : { print: emitEventMsg }), ...(onError === undefined ? {} : { onError }) }));
|
|
9
23
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SerializableKey, TreeNode } from 'dirvi-lib';
|
|
2
|
+
import type { EventMessage } from '../domain/event-message.js';
|
|
3
|
+
import { AppApi } from '../domain/app-api.js';
|
|
4
|
+
type LoadingAppProps<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>> = {
|
|
5
|
+
appApi: AppApi<Id, BufferNode>;
|
|
6
|
+
print?: (message: EventMessage<Id, BufferNode>) => void;
|
|
7
|
+
onError?: (error: Error) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function LoadingApp<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>({ appApi, print, onError }: LoadingAppProps<Id, BufferNode>): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=LoadingApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingApp.d.ts","sourceRoot":"","sources":["../../src/ui/LoadingApp.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAwB,eAAe,EAAS,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAI9C,KAAK,eAAe,CAClB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,IACzC;IACF,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,IAAI,CAAC;IACxD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AA0BF,wBAAgB,UAAU,CACxB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAC3C,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,eAAe,CAAC,EAAE,EAAE,UAAU,CAAC,+BA+F5D"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { createIntentToEffect } from 'dirvi-lib';
|
|
5
|
+
import { App } from './App.js';
|
|
6
|
+
import { createReducer } from '../application/reducer.js';
|
|
7
|
+
import { createEffectToAction } from '../application/effect-to-action.js';
|
|
8
|
+
function createInitialState(rootBranches, createEmptyFoldRoot) {
|
|
9
|
+
if (rootBranches.length === 0) {
|
|
10
|
+
throw new Error('createInitialState cannot create a state for an empty forest');
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
buffer: rootBranches,
|
|
14
|
+
foldNode: createEmptyFoldRoot(),
|
|
15
|
+
cursor: {
|
|
16
|
+
kind: 'entry',
|
|
17
|
+
parentPath: [],
|
|
18
|
+
entryId: rootBranches[0].id,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function LoadingApp({ appApi, print, onError }) {
|
|
23
|
+
const [initialState, setInitialState] = useState();
|
|
24
|
+
const [empty, setEmpty] = useState(false);
|
|
25
|
+
const [error, setError] = useState();
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
let mounted = true;
|
|
28
|
+
appApi
|
|
29
|
+
.loadBranches([])
|
|
30
|
+
.then((rootBranches) => {
|
|
31
|
+
if (!mounted) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (rootBranches.length === 0) {
|
|
35
|
+
setEmpty(true);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setInitialState(createInitialState(rootBranches, () => appApi.foldNodeService.createEmptyRoot()));
|
|
39
|
+
})
|
|
40
|
+
.catch((cause) => {
|
|
41
|
+
const nextError = cause instanceof Error ? cause : new Error(String(cause));
|
|
42
|
+
if (!mounted) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
setError(nextError);
|
|
46
|
+
onError?.(nextError);
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
mounted = false;
|
|
50
|
+
};
|
|
51
|
+
}, [appApi, onError]);
|
|
52
|
+
// --- Pure function deps ---
|
|
53
|
+
const reducer = useMemo(() => createReducer(appApi.treeNodeApi, appApi.foldNodeService), [appApi.treeNodeApi, appApi.foldNodeService]);
|
|
54
|
+
const intentToEffect = useMemo(() => createIntentToEffect(appApi.stateApi, appApi.cursorApi, appApi.treeNodeApi), [appApi.stateApi, appApi.cursorApi, appApi.treeNodeApi]);
|
|
55
|
+
const effectToAction = useMemo(() => createEffectToAction(appApi.treeNodeApi, appApi.cursorApi, appApi.navNodeApi), [appApi.treeNodeApi, appApi.navNodeApi, appApi.cursorApi]);
|
|
56
|
+
// --- JSX ---
|
|
57
|
+
if (error !== undefined) {
|
|
58
|
+
return _jsx(Text, { color: "red", children: error.message });
|
|
59
|
+
}
|
|
60
|
+
if (empty) {
|
|
61
|
+
return _jsx(Text, { dimColor: true, children: appApi.emptyForestMessage });
|
|
62
|
+
}
|
|
63
|
+
if (initialState === undefined) {
|
|
64
|
+
return _jsx(Text, { dimColor: true, children: "Loading." });
|
|
65
|
+
}
|
|
66
|
+
return (_jsx(App, { appApi: appApi, initialState: initialState, reducer: reducer, intentToEffect: intentToEffect, effectToAction: effectToAction, ...(print === undefined ? {} : { print }), ...(onError === undefined ? {} : { onError }) }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { InputState } from 'dirvi-lib';
|
|
2
|
+
export declare const STATUS_BAR_HEIGHT = 1;
|
|
3
|
+
type StatusBarProps = {
|
|
4
|
+
inputState: InputState;
|
|
5
|
+
};
|
|
6
|
+
export declare function StatusBar({ inputState }: StatusBarProps): import("react").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=StatusBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../src/ui/components/StatusBar.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,+BAyBvD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
export const STATUS_BAR_HEIGHT = 1;
|
|
4
|
+
export function StatusBar({ inputState }) {
|
|
5
|
+
const inputMode = inputState.inputMode;
|
|
6
|
+
return (_jsxs(Box, { width: "100%", height: STATUS_BAR_HEIGHT, flexDirection: "row", justifyContent: "space-between", flexShrink: 0, backgroundColor: 'gray', children: [_jsx(Box, { flexShrink: 1, children: _jsx(Text, { color: "black", wrap: "truncate-end", children: inputMode === 'command' ? inputState.commandLine : '' }) }), _jsx(Box, { flexShrink: 1, children: _jsx(Text, { color: "black", wrap: "truncate-start", children: inputMode === 'normal' ? inputState.normalBuffer : '' }) })] }));
|
|
7
|
+
}
|
|
@@ -3,9 +3,9 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
export function ViewRowComponent({ row }) {
|
|
4
4
|
const prefix = '';
|
|
5
5
|
switch (row.type) {
|
|
6
|
-
case '
|
|
6
|
+
case 'leaf':
|
|
7
7
|
return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsxs(Text, { inverse: row.selected, children: [prefix, row.content] }) }));
|
|
8
|
-
case '
|
|
8
|
+
case 'branch':
|
|
9
9
|
return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsxs(Text, { inverse: row.selected, color: "blue", children: [prefix, row.content] }) }));
|
|
10
10
|
case 'fold':
|
|
11
11
|
return (_jsx(Box, { paddingLeft: row.indent * 2, children: _jsxs(Text, { inverse: row.selected, dimColor: true, children: [prefix, row.content] }) }));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CursorApi, NavNode, SerializableKey, State, TreeNode } from 'dirvi-lib';
|
|
2
2
|
import { View } from '../view.js';
|
|
3
|
-
export
|
|
3
|
+
export type UseView = <Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, state: State<Id, BufferNode>, terminalRows: number) => View;
|
|
4
|
+
export declare function useView<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, state: State<Id, BufferNode>, cursorApi: CursorApi<Id>, terminalRows: number): View;
|
|
4
5
|
//# sourceMappingURL=useView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useView.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useView.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"useView.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useView.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,OAAO,EACP,eAAe,EACf,KAAK,EACL,QAAQ,EACT,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,IAAI,EAAW,MAAM,YAAY,CAAC;AAI3C,MAAM,MAAM,OAAO,GAAG,CACpB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAE3C,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,EACnC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAC5B,YAAY,EAAE,MAAM,KACjB,IAAI,CAAC;AAIV,wBAAgB,OAAO,CACrB,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,EAE3C,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,EACnC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,EAC5B,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,EACxB,YAAY,EAAE,MAAM,GACnB,IAAI,CAoBN"}
|
package/dist/ui/hooks/useView.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { View } from '../view.js';
|
|
2
|
-
import { useWindowSize } from 'ink';
|
|
3
2
|
import { useRef } from 'react';
|
|
3
|
+
import { STATUS_BAR_HEIGHT } from '../components/StatusBar.js';
|
|
4
4
|
// Hooks that keeps Ref of the viewport's start, and returns View sliced to
|
|
5
5
|
// only the rows that should be visible.
|
|
6
|
-
export function useView(navigation, state) {
|
|
7
|
-
const { rows: terminalRows } = useWindowSize();
|
|
6
|
+
export function useView(navigation, state, cursorApi, terminalRows) {
|
|
8
7
|
const viewportStartRef = useRef(0);
|
|
9
|
-
const viewportHeight = Math.max(1, terminalRows);
|
|
10
|
-
const rows = View.createRows(navigation, state.cursor);
|
|
8
|
+
const viewportHeight = Math.max(1, terminalRows - STATUS_BAR_HEIGHT);
|
|
9
|
+
const rows = View.createRows(navigation, state.cursor, cursorApi);
|
|
11
10
|
viewportStartRef.current = viewportStart(rows, viewportHeight, viewportStartRef.current);
|
|
12
|
-
return View.create(navigation, state.cursor, viewportHeight, viewportStartRef.current);
|
|
11
|
+
return View.create(navigation, state.cursor, cursorApi, viewportHeight, viewportStartRef.current);
|
|
13
12
|
}
|
|
14
13
|
function viewportStart(rows, viewportHeight, currentViewportStart) {
|
|
15
14
|
const scrollMargin = 3;
|
package/dist/ui/view.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { CursorApi, SerializableKey, TreeNode } from 'dirvi-lib';
|
|
2
|
+
import { Cursor, NavNode } from 'dirvi-lib';
|
|
3
|
+
export type ViewRowType = 'leaf' | 'branch' | 'fold';
|
|
3
4
|
export type ViewRow = {
|
|
4
5
|
id: string;
|
|
5
6
|
indent: number;
|
|
@@ -12,7 +13,7 @@ export type View = {
|
|
|
12
13
|
rows: ViewRow[];
|
|
13
14
|
};
|
|
14
15
|
export declare const View: {
|
|
15
|
-
createRows(navigation:
|
|
16
|
-
create(navigation:
|
|
16
|
+
createRows<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, cursor: Cursor<Id>, cursorApi: CursorApi<Id>): ViewRow[];
|
|
17
|
+
create<Id extends SerializableKey, BufferNode extends TreeNode<Id, BufferNode>>(navigation: NavNode<Id, BufferNode>, cursor: Cursor<Id>, cursorApi: CursorApi<Id>, viewportHeight: number, viewportStart: number): View;
|
|
17
18
|
};
|
|
18
19
|
//# sourceMappingURL=view.d.ts.map
|
package/dist/ui/view.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/ui/view.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/ui/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,MAAM,EAAyB,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAMrD,MAAM,MAAM,OAAO,GAAG;IAEpB,EAAE,EAAE,MAAM,CAAC;IAGX,MAAM,EAAE,MAAM,CAAC;IAGf,QAAQ,EAAE,OAAO,CAAC;IAGlB,MAAM,EAAE,OAAO,CAAC;IAGhB,OAAO,EAAE,MAAM,CAAC;IAGhB,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,IAAI;eAEb,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,cAE/B,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,UAC3B,MAAM,CAAC,EAAE,CAAC,aACP,SAAS,CAAC,EAAE,CAAC,GACvB,OAAO,EAAE;WAKV,EAAE,SAAS,eAAe,EAC1B,UAAU,SAAS,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,cAE/B,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,UAC3B,MAAM,CAAC,EAAE,CAAC,aACP,SAAS,CAAC,EAAE,CAAC,kBACR,MAAM,iBACP,MAAM,GACpB,IAAI;CAOR,CAAC"}
|
package/dist/ui/view.js
CHANGED
|
@@ -1,36 +1,74 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isNavBranch } from 'dirvi-lib';
|
|
2
2
|
export const View = {
|
|
3
|
-
createRows(navigation, cursor) {
|
|
4
|
-
return viewRowsAtNode(navigation, [], cursor);
|
|
3
|
+
createRows(navigation, cursor, cursorApi) {
|
|
4
|
+
return viewRowsAtNode(navigation, [], cursor, cursorApi);
|
|
5
5
|
},
|
|
6
|
-
create(navigation, cursor, viewportHeight, viewportStart) {
|
|
7
|
-
const rows = View.createRows(navigation, cursor);
|
|
6
|
+
create(navigation, cursor, cursorApi, viewportHeight, viewportStart) {
|
|
7
|
+
const rows = View.createRows(navigation, cursor, cursorApi);
|
|
8
8
|
return {
|
|
9
9
|
rows: rows.slice(viewportStart, viewportStart + viewportHeight),
|
|
10
10
|
};
|
|
11
11
|
},
|
|
12
12
|
};
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Converts one NavNode and its descendants into display ViewRows.
|
|
15
|
+
*
|
|
16
|
+
* `node` is the directory currently being visited.
|
|
17
|
+
*
|
|
18
|
+
* `parentPath` is the path of that directory. For example:
|
|
19
|
+
*
|
|
20
|
+
* [] root directory
|
|
21
|
+
* ['src'] root/src
|
|
22
|
+
* ['src', 'lib'] root/src/lib
|
|
23
|
+
*
|
|
24
|
+
* The function uses a depth-first, pre-order traversal:
|
|
25
|
+
*
|
|
26
|
+
* 1. Add an entry's row.
|
|
27
|
+
* 2. If the entry is an opened branch, recursively add its children.
|
|
28
|
+
* 3. After all visible entries, add the directory's fold row.
|
|
29
|
+
*
|
|
30
|
+
* This ordering is what makes the flat row list visually represent a tree.
|
|
31
|
+
*/
|
|
32
|
+
function viewRowsAtNode(node, parentPath, cursor, cursorApi) {
|
|
14
33
|
const rows = [];
|
|
34
|
+
/*
|
|
35
|
+
* `node.entries` contains only the entries currently visible in this
|
|
36
|
+
* directory. Folded entries are kept separately in `node.foldedEntries`
|
|
37
|
+
* and are represented by one fold row added below.
|
|
38
|
+
*/
|
|
15
39
|
for (const entry of node.entries) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
40
|
+
const entryCursor = {
|
|
41
|
+
kind: 'entry',
|
|
42
|
+
parentPath,
|
|
43
|
+
entryId: entry.id,
|
|
44
|
+
};
|
|
45
|
+
// If cursor at entry, needs to be rendered differently
|
|
46
|
+
const isCursor = cursorApi.equal(cursor, entryCursor);
|
|
47
|
+
// Add entry before visiting branches
|
|
19
48
|
rows.push(viewRowForEntry(entry, parentPath, isCursor));
|
|
20
|
-
|
|
49
|
+
// Case where there's no branches
|
|
50
|
+
if (!isNavBranch(entry) || entry.children === null) {
|
|
21
51
|
continue;
|
|
22
52
|
}
|
|
23
|
-
rows.push(...viewRowsAtNode(entry.
|
|
53
|
+
rows.push(...viewRowsAtNode(entry.children, [...parentPath, entry.id], cursor, cursorApi));
|
|
24
54
|
}
|
|
55
|
+
/*
|
|
56
|
+
* Folded entries are not rendered individually. They are represented
|
|
57
|
+
* by one synthetic row placed after all visible entries and descendants
|
|
58
|
+
* of this directory.
|
|
59
|
+
*/
|
|
25
60
|
if (node.foldedEntries.length > 0) {
|
|
26
|
-
const
|
|
27
|
-
|
|
61
|
+
const foldCursor = {
|
|
62
|
+
kind: 'fold',
|
|
63
|
+
parentPath,
|
|
64
|
+
};
|
|
65
|
+
const isCursor = cursorApi.equal(cursor, foldCursor);
|
|
28
66
|
rows.push({
|
|
29
67
|
id: foldId(parentPath),
|
|
30
68
|
indent: parentPath.length,
|
|
31
69
|
selected: isCursor,
|
|
32
70
|
cursor: isCursor,
|
|
33
|
-
content: `⋯ ${node.foldedEntries.length} folded
|
|
71
|
+
content: `⋯ ${node.foldedEntries.length} folded`,
|
|
34
72
|
type: 'fold',
|
|
35
73
|
});
|
|
36
74
|
}
|
|
@@ -38,7 +76,7 @@ function viewRowsAtNode(node, parentPath, cursor) {
|
|
|
38
76
|
}
|
|
39
77
|
function viewRowForEntry(entry, parentPath, cursor) {
|
|
40
78
|
return {
|
|
41
|
-
id: entryId(parentPath, entry.
|
|
79
|
+
id: entryId(parentPath, entry.id),
|
|
42
80
|
indent: parentPath.length,
|
|
43
81
|
selected: cursor,
|
|
44
82
|
cursor,
|
|
@@ -53,22 +91,8 @@ function foldId(parentPath) {
|
|
|
53
91
|
return `fold:${JSON.stringify(parentPath)}`;
|
|
54
92
|
}
|
|
55
93
|
function content(entry) {
|
|
56
|
-
|
|
57
|
-
case 'file':
|
|
58
|
-
return entry.name;
|
|
59
|
-
case 'directory':
|
|
60
|
-
return `${entry.name}/`;
|
|
61
|
-
case 'symlink':
|
|
62
|
-
return `${entry.name} -> ${entry.target}`;
|
|
63
|
-
}
|
|
94
|
+
return isNavBranch(entry) ? `${String(entry.id)}/` : String(entry.id);
|
|
64
95
|
}
|
|
65
96
|
function type(entry) {
|
|
66
|
-
|
|
67
|
-
case 'file':
|
|
68
|
-
return 'file';
|
|
69
|
-
case 'directory':
|
|
70
|
-
return 'directory';
|
|
71
|
-
case 'symlink':
|
|
72
|
-
return 'file';
|
|
73
|
-
}
|
|
97
|
+
return isNavBranch(entry) ? 'branch' : 'leaf';
|
|
74
98
|
}
|