@mak-98/dirvi-app 0.0.0-stage → 0.11.0-beta.13
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/LICENSE +674 -0
- package/dist/application/app-api-applicator.d.ts +12 -0
- package/dist/application/app-api-applicator.d.ts.map +1 -0
- package/dist/application/app-api-applicator.js +1 -0
- package/dist/application/app-pack-ref.d.ts +12 -0
- package/dist/application/app-pack-ref.d.ts.map +1 -0
- package/dist/application/app-pack-ref.js +37 -0
- package/dist/application/create-app-api.d.ts +11 -0
- package/dist/application/create-app-api.d.ts.map +1 -0
- package/dist/application/create-app-api.js +60 -0
- package/dist/application/create-app-methods.d.ts +11 -0
- package/dist/application/create-app-methods.d.ts.map +1 -0
- package/dist/application/create-app-methods.js +60 -0
- package/dist/application/create-dirvi-api.d.ts +10 -0
- package/dist/application/create-dirvi-api.d.ts.map +1 -0
- package/dist/application/create-dirvi-api.js +23 -0
- package/dist/application/init-runtime.d.ts +40 -0
- package/dist/application/init-runtime.d.ts.map +1 -0
- package/dist/application/init-runtime.error.d.ts +53 -0
- package/dist/application/init-runtime.error.d.ts.map +1 -0
- package/dist/application/init-runtime.error.js +77 -0
- package/dist/application/init-runtime.js +165 -0
- package/dist/application/package-loader.d.ts +9 -0
- package/dist/application/package-loader.d.ts.map +1 -0
- package/dist/application/package-loader.js +1 -0
- package/dist/application/shell-api.d.ts +5 -0
- package/dist/application/shell-api.d.ts.map +1 -0
- package/dist/application/shell-api.js +1 -0
- package/dist/domain/app-input.d.ts +2 -0
- package/dist/domain/app-input.d.ts.map +1 -0
- package/dist/domain/app-input.js +1 -0
- package/dist/domain/app-pack-ref.d.ts +17 -0
- package/dist/domain/app-pack-ref.d.ts.map +1 -0
- package/dist/domain/app-pack-ref.js +37 -0
- package/dist/domain/app.d.ts +39 -0
- package/dist/domain/app.d.ts.map +1 -0
- package/dist/domain/app.js +25 -0
- package/dist/domain/config.d.ts +386 -0
- package/dist/domain/config.d.ts.map +1 -0
- package/dist/domain/config.js +44 -0
- package/dist/domain/dirvi-api/app-api.d.ts +7 -0
- package/dist/domain/dirvi-api/app-api.d.ts.map +1 -0
- package/dist/domain/dirvi-api/app-api.js +1 -0
- package/dist/domain/dirvi-api/dirvi-api.d.ts +17 -0
- package/dist/domain/dirvi-api/dirvi-api.d.ts.map +1 -0
- package/dist/domain/dirvi-api/dirvi-api.js +1 -0
- package/dist/domain/dirvi-api/shell-api.d.ts +35 -0
- package/dist/domain/dirvi-api/shell-api.d.ts.map +1 -0
- package/dist/domain/dirvi-api/shell-api.js +1 -0
- package/dist/domain/dirvi-api.d.ts +24 -0
- package/dist/domain/dirvi-api.d.ts.map +1 -0
- package/dist/domain/dirvi-api.js +1 -0
- package/dist/domain/ex-command.d.ts +9 -0
- package/dist/domain/ex-command.d.ts.map +1 -0
- package/dist/domain/ex-command.js +1 -0
- package/dist/domain/init-runtime.d.ts +41 -0
- package/dist/domain/init-runtime.d.ts.map +1 -0
- package/dist/domain/init-runtime.js +1 -0
- package/dist/domain/initialization.d.ts +26 -0
- package/dist/domain/initialization.d.ts.map +1 -0
- package/dist/domain/initialization.js +1 -0
- package/dist/domain/node-presentation.d.ts +12 -0
- package/dist/domain/node-presentation.d.ts.map +1 -0
- package/dist/domain/node-presentation.js +1 -0
- package/dist/domain/public-init-api.d.ts +48 -0
- package/dist/domain/public-init-api.d.ts.map +1 -0
- package/dist/domain/public-init-api.js +1 -0
- package/dist/domain/shell-api.d.ts +9 -0
- package/dist/domain/shell-api.d.ts.map +1 -0
- package/dist/domain/shell-api.js +1 -0
- package/dist/domain/theme.d.ts +524 -0
- package/dist/domain/theme.d.ts.map +1 -0
- package/dist/domain/theme.js +164 -0
- package/dist/domain/view-name-schema.d.ts +18 -0
- package/dist/domain/view-name-schema.d.ts.map +1 -0
- package/dist/domain/view-name-schema.js +68 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/theme.d.ts +536 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +170 -0
- package/dist/ui/App.d.ts +21 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +248 -0
- package/dist/ui/AppSetup.d.ts +18 -0
- package/dist/ui/AppSetup.d.ts.map +1 -0
- package/dist/ui/AppSetup.js +60 -0
- package/dist/ui/Shell.d.ts +44 -0
- package/dist/ui/Shell.d.ts.map +1 -0
- package/dist/ui/Shell.js +73 -0
- package/dist/ui/app-screen-renderer.d.ts +27 -0
- package/dist/ui/app-screen-renderer.d.ts.map +1 -0
- package/dist/ui/app-screen-renderer.js +1 -0
- package/dist/ui/color-resolver.d.ts +3 -0
- package/dist/ui/color-resolver.d.ts.map +1 -0
- package/dist/ui/color-resolver.js +61 -0
- package/dist/ui/hooks/useView.d.ts +5 -0
- package/dist/ui/hooks/useView.d.ts.map +1 -0
- package/dist/ui/hooks/useView.js +32 -0
- package/dist/ui/loading-screen-renderer.d.ts +3 -0
- package/dist/ui/loading-screen-renderer.d.ts.map +1 -0
- package/dist/ui/loading-screen-renderer.js +1 -0
- package/dist/ui/view.d.ts +28 -0
- package/dist/ui/view.d.ts.map +1 -0
- package/dist/ui/view.js +131 -0
- package/package.json +34 -3
- package/README.md +0 -3
package/dist/theme.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// Terminal colors
|
|
4
|
+
// =============================================================================
|
|
5
|
+
const HexColorSchema = z.string().regex(/^#[0-9a-fA-F]{6}$/, {
|
|
6
|
+
message: 'Expected a #rrggbb color',
|
|
7
|
+
});
|
|
8
|
+
const Ansi256ColorSchema = z.number().int().min(0).max(255);
|
|
9
|
+
/**
|
|
10
|
+
* Named ANSI foreground colors supported by Dirvi themes.
|
|
11
|
+
*
|
|
12
|
+
* Renderers translate these symbolic names into their own color APIs. The CLI
|
|
13
|
+
* renderer currently maps them to terminal colors; another renderer does not
|
|
14
|
+
* need to depend on Chalk to interpret the same theme.
|
|
15
|
+
*/
|
|
16
|
+
export const basicColorNames = [
|
|
17
|
+
'black',
|
|
18
|
+
'red',
|
|
19
|
+
'green',
|
|
20
|
+
'yellow',
|
|
21
|
+
'blue',
|
|
22
|
+
'cyan',
|
|
23
|
+
'magenta',
|
|
24
|
+
'white',
|
|
25
|
+
// Common aliases for blackBright.
|
|
26
|
+
'gray',
|
|
27
|
+
'grey',
|
|
28
|
+
'blackBright',
|
|
29
|
+
'redBright',
|
|
30
|
+
'greenBright',
|
|
31
|
+
'yellowBright',
|
|
32
|
+
'blueBright',
|
|
33
|
+
'cyanBright',
|
|
34
|
+
'magentaBright',
|
|
35
|
+
'whiteBright',
|
|
36
|
+
];
|
|
37
|
+
const BasicColorSchema = z.enum(basicColorNames);
|
|
38
|
+
export const ColorConfigSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
hex: HexColorSchema.optional(),
|
|
41
|
+
ansi256: Ansi256ColorSchema.optional(),
|
|
42
|
+
basic: BasicColorSchema.optional(),
|
|
43
|
+
})
|
|
44
|
+
.refine((value) => value.hex !== undefined ||
|
|
45
|
+
value.ansi256 !== undefined ||
|
|
46
|
+
value.basic !== undefined, {
|
|
47
|
+
message: 'Expected at least one terminal color profile',
|
|
48
|
+
});
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// Highlights
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Note: these schemas are meant to be non-strict
|
|
53
|
+
export const ForegroundHighlightConfigSchema = z.object({
|
|
54
|
+
fg: ColorConfigSchema,
|
|
55
|
+
});
|
|
56
|
+
export const OptionalForegroundHighlightConfigSchema = z.object({
|
|
57
|
+
fg: ColorConfigSchema.optional(),
|
|
58
|
+
});
|
|
59
|
+
export const BackgroundHighlightConfigSchema = z.object({
|
|
60
|
+
bg: ColorConfigSchema,
|
|
61
|
+
});
|
|
62
|
+
export const ForegroundBackgroundHighlightConfigSchema = z.object({
|
|
63
|
+
fg: ColorConfigSchema,
|
|
64
|
+
bg: ColorConfigSchema,
|
|
65
|
+
});
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// Theme defaults
|
|
68
|
+
// =============================================================================
|
|
69
|
+
const black = {
|
|
70
|
+
hex: '#28282a',
|
|
71
|
+
ansi256: 235,
|
|
72
|
+
basic: 'black',
|
|
73
|
+
};
|
|
74
|
+
const lightGray = {
|
|
75
|
+
hex: '#eaeaec',
|
|
76
|
+
ansi256: 254,
|
|
77
|
+
basic: 'gray',
|
|
78
|
+
};
|
|
79
|
+
const leaf = {
|
|
80
|
+
hex: '#e5e5e5',
|
|
81
|
+
ansi256: 254,
|
|
82
|
+
basic: 'white',
|
|
83
|
+
};
|
|
84
|
+
const branch = {
|
|
85
|
+
hex: '#2472c8',
|
|
86
|
+
ansi256: 26,
|
|
87
|
+
basic: 'blue',
|
|
88
|
+
};
|
|
89
|
+
const folded = {
|
|
90
|
+
hex: '#808080',
|
|
91
|
+
ansi256: 245,
|
|
92
|
+
basic: 'gray',
|
|
93
|
+
};
|
|
94
|
+
const indentGuide = {
|
|
95
|
+
hex: '#808080',
|
|
96
|
+
ansi256: 245,
|
|
97
|
+
basic: 'gray',
|
|
98
|
+
};
|
|
99
|
+
const cursorLine = {
|
|
100
|
+
hex: '#3a3a3c',
|
|
101
|
+
ansi256: 237,
|
|
102
|
+
basic: 'gray',
|
|
103
|
+
};
|
|
104
|
+
const success = {
|
|
105
|
+
hex: '#238c4b',
|
|
106
|
+
ansi256: 28,
|
|
107
|
+
basic: 'green',
|
|
108
|
+
};
|
|
109
|
+
const error = {
|
|
110
|
+
hex: '#be2d2d',
|
|
111
|
+
ansi256: 160,
|
|
112
|
+
basic: 'red',
|
|
113
|
+
};
|
|
114
|
+
const themeDefaults = {
|
|
115
|
+
statusBar: {
|
|
116
|
+
command: { fg: black },
|
|
117
|
+
normal: { fg: black },
|
|
118
|
+
background: { bg: lightGray },
|
|
119
|
+
},
|
|
120
|
+
tree: {
|
|
121
|
+
leaf: { fg: leaf },
|
|
122
|
+
branch: { fg: branch },
|
|
123
|
+
folded: { fg: folded },
|
|
124
|
+
cursorLine: { bg: cursorLine },
|
|
125
|
+
indentGuide: { fg: indentGuide },
|
|
126
|
+
},
|
|
127
|
+
feedbackBar: {
|
|
128
|
+
message: {
|
|
129
|
+
fg: black,
|
|
130
|
+
bg: lightGray,
|
|
131
|
+
},
|
|
132
|
+
success: {
|
|
133
|
+
fg: success,
|
|
134
|
+
bg: lightGray,
|
|
135
|
+
},
|
|
136
|
+
error: {
|
|
137
|
+
fg: error,
|
|
138
|
+
bg: lightGray,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
// =============================================================================
|
|
143
|
+
// Theme schema
|
|
144
|
+
// =============================================================================
|
|
145
|
+
export const ThemeSchema = z.object({
|
|
146
|
+
statusBar: z
|
|
147
|
+
.object({
|
|
148
|
+
command: ForegroundHighlightConfigSchema.default(themeDefaults.statusBar.command),
|
|
149
|
+
normal: ForegroundHighlightConfigSchema.default(themeDefaults.statusBar.normal),
|
|
150
|
+
background: BackgroundHighlightConfigSchema.default(themeDefaults.statusBar.background),
|
|
151
|
+
})
|
|
152
|
+
.default(themeDefaults.statusBar),
|
|
153
|
+
tree: z
|
|
154
|
+
.object({
|
|
155
|
+
leaf: ForegroundHighlightConfigSchema.default(themeDefaults.tree.leaf),
|
|
156
|
+
branch: ForegroundHighlightConfigSchema.default(themeDefaults.tree.branch),
|
|
157
|
+
folded: ForegroundHighlightConfigSchema.default(themeDefaults.tree.folded),
|
|
158
|
+
cursorLine: BackgroundHighlightConfigSchema.default(themeDefaults.tree.cursorLine),
|
|
159
|
+
indentGuide: OptionalForegroundHighlightConfigSchema.default(themeDefaults.tree.indentGuide),
|
|
160
|
+
})
|
|
161
|
+
.default(themeDefaults.tree),
|
|
162
|
+
feedbackBar: z
|
|
163
|
+
.object({
|
|
164
|
+
message: ForegroundBackgroundHighlightConfigSchema.default(themeDefaults.feedbackBar.message),
|
|
165
|
+
success: ForegroundBackgroundHighlightConfigSchema.default(themeDefaults.feedbackBar.success),
|
|
166
|
+
error: ForegroundBackgroundHighlightConfigSchema.default(themeDefaults.feedbackBar.error),
|
|
167
|
+
})
|
|
168
|
+
.default(themeDefaults.feedbackBar),
|
|
169
|
+
});
|
|
170
|
+
export const defaultTheme = ThemeSchema.parse({});
|
package/dist/ui/App.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Config, ShellApi } from 'dirvi-apis';
|
|
2
|
+
import type { AppMethods, EffectToAction, IntentToEffect, Reducer, SerializableKey, State } from 'dirvi-lib';
|
|
3
|
+
import type { AppScreenRenderer } from './app-screen-renderer.js';
|
|
4
|
+
import { ExCommand } from '../domain/ex-command.js';
|
|
5
|
+
type AppProps<Id extends SerializableKey, Value, ViewKey = string> = {
|
|
6
|
+
screenRenderer: AppScreenRenderer;
|
|
7
|
+
shellApi: ShellApi;
|
|
8
|
+
appMethods: AppMethods<Id, Value, ViewKey>;
|
|
9
|
+
exCommands: ReadonlyMap<string, ExCommand>;
|
|
10
|
+
config: Readonly<Config>;
|
|
11
|
+
initialState: State<Id, Value>;
|
|
12
|
+
reducer: Reducer<Id, Value>;
|
|
13
|
+
intentToEffect: IntentToEffect<Id, Value>;
|
|
14
|
+
effectToAction: EffectToAction<Id, Value>;
|
|
15
|
+
stdout?: (message: string) => void;
|
|
16
|
+
clipboard?: (value: string) => void;
|
|
17
|
+
onError?: (error: Error) => void;
|
|
18
|
+
};
|
|
19
|
+
export declare function App<Id extends SerializableKey, Value, ViewKey = string>({ screenRenderer, shellApi, appMethods, exCommands, config, initialState, reducer, intentToEffect, effectToAction, stdout, clipboard, onError, }: AppProps<Id, Value, ViewKey>): import("react").JSX.Element;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=App.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AASA,OAAO,EAAU,MAAM,EAAgB,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EACV,UAAU,EAEV,cAAc,EAEd,cAAc,EAEd,OAAO,EACP,eAAe,EACf,KAAK,EAGN,MAAM,WAAW,CAAC;AAInB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,KAAK,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,IAAI;IACnE,cAAc,EAAE,iBAAiB,CAAC;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3C,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC3C,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,YAAY,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AAEF,wBAAgB,GAAG,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,EACvE,cAAc,EACd,QAAQ,EACR,UAAU,EACV,UAAU,EACV,MAAM,EACN,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,+BAyV9B"}
|
package/dist/ui/App.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useReducer, useRef, useState, } from 'react';
|
|
3
|
+
import { createFeedbackApi, userInputToIntent } from 'dirvi-lib';
|
|
4
|
+
import { useView } from './hooks/useView.js';
|
|
5
|
+
export function App({ screenRenderer, shellApi, appMethods, exCommands, config, initialState, reducer, intentToEffect, effectToAction, stdout, clipboard, onError, }) {
|
|
6
|
+
const ScreenRenderer = screenRenderer;
|
|
7
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
8
|
+
const stateRef = useRef(state); // Gives `subscribeToResync` callback a way to
|
|
9
|
+
// see current state
|
|
10
|
+
stateRef.current = state;
|
|
11
|
+
const navEntry = useMemo(() => {
|
|
12
|
+
const root = appMethods.stateMethods.getRoot(state);
|
|
13
|
+
const folds = appMethods.stateMethods.getFolds(state);
|
|
14
|
+
const navigation = appMethods.navMethods.from(root, folds);
|
|
15
|
+
if (navigation === undefined) {
|
|
16
|
+
throw new Error('Tree-surfer state root must be a loaded branch.');
|
|
17
|
+
}
|
|
18
|
+
return navigation;
|
|
19
|
+
}, [appMethods.navMethods, appMethods.stateMethods, state]);
|
|
20
|
+
const clearInputState = { inputMode: 'normal', normalBuffer: '' };
|
|
21
|
+
const [inputState, setInputState] = useState(clearInputState);
|
|
22
|
+
const [feedback, setFeedback] = useState();
|
|
23
|
+
const [rowCount, setRowCount] = useState(0);
|
|
24
|
+
const handleRowsChange = useCallback((nextRows) => {
|
|
25
|
+
setRowCount((currentRows) => currentRows === nextRows ? currentRows : nextRows);
|
|
26
|
+
}, []);
|
|
27
|
+
const view = useView(navEntry, state, appMethods.stateMethods, appMethods.navMethods, appMethods.stateMethods.cursorMethods, rowCount, appMethods.presentNode);
|
|
28
|
+
// ===========================================================================
|
|
29
|
+
// Resync Subscriptions
|
|
30
|
+
// ===========================================================================
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
let active = true;
|
|
33
|
+
const unsubscribe = appMethods.subscribeToResync(() => {
|
|
34
|
+
const oldState = stateRef.current;
|
|
35
|
+
void appMethods.stateMethods
|
|
36
|
+
.resync(oldState, appMethods.loadChildren)
|
|
37
|
+
.then((newState) => {
|
|
38
|
+
if (!active) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
dispatch({
|
|
42
|
+
kind: 'setState',
|
|
43
|
+
oldState: oldState,
|
|
44
|
+
newState: newState,
|
|
45
|
+
});
|
|
46
|
+
})
|
|
47
|
+
.catch((error) => {
|
|
48
|
+
const appError = error instanceof Error ? error : new Error(String(error));
|
|
49
|
+
onError?.(appError);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return () => {
|
|
53
|
+
active = false;
|
|
54
|
+
unsubscribe();
|
|
55
|
+
};
|
|
56
|
+
}, [appMethods, onError]);
|
|
57
|
+
// ===========================================================================
|
|
58
|
+
// App API
|
|
59
|
+
// ===========================================================================
|
|
60
|
+
const feedbackApi = useMemo(() => createFeedbackApi((nextFeedback) => {
|
|
61
|
+
setFeedback(nextFeedback);
|
|
62
|
+
}), []);
|
|
63
|
+
const appApi = useMemo(() => ({
|
|
64
|
+
pathAtCursor: () => appMethods.stateMethods.cursorPath(stateRef.current),
|
|
65
|
+
visiblePaths: () => {
|
|
66
|
+
const state = stateRef.current;
|
|
67
|
+
const root = appMethods.stateMethods.getRoot(state);
|
|
68
|
+
const folds = appMethods.stateMethods.getFolds(state);
|
|
69
|
+
const navigation = appMethods.navMethods.from(root, folds);
|
|
70
|
+
if (navigation === undefined) {
|
|
71
|
+
throw new Error('Tree-surfer state root must be a loaded branch.');
|
|
72
|
+
}
|
|
73
|
+
return navigation.children === null
|
|
74
|
+
? []
|
|
75
|
+
: appMethods.navMethods.visiblePaths(navigation.children);
|
|
76
|
+
},
|
|
77
|
+
// TODO: Implement with appMethods
|
|
78
|
+
valueAtCursor: () => 'test value',
|
|
79
|
+
addMessage: (message, type) => {
|
|
80
|
+
feedbackApi.addMessage(message, type);
|
|
81
|
+
},
|
|
82
|
+
}), [feedbackApi, appMethods.stateMethods, appMethods.navMethods]);
|
|
83
|
+
// ===========================================================================
|
|
84
|
+
// Executing Effects
|
|
85
|
+
// ===========================================================================
|
|
86
|
+
const executeEffect = useCallback((effect) => {
|
|
87
|
+
if (effect === undefined) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
switch (effect.effectType) {
|
|
91
|
+
/** Execute a command, using shell API and app API. */
|
|
92
|
+
case 'userExCommand': {
|
|
93
|
+
setInputState(clearInputState);
|
|
94
|
+
const command = exCommands.get(effect.name);
|
|
95
|
+
if (command === undefined) {
|
|
96
|
+
appApi.addMessage(`Command is no longer registered: ${effect.name}`, 'error');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
void Promise.resolve()
|
|
100
|
+
.then(() => command.execute({ shell: shellApi, app: appApi }, effect.args))
|
|
101
|
+
.catch((error) => {
|
|
102
|
+
const appError = error instanceof Error ? error : new Error(String(error));
|
|
103
|
+
appApi.addMessage(`Command "${effect.name}" failed: ${appError.message}`, 'error');
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
case 'quit':
|
|
108
|
+
shellApi.quit(effect.exitMessage);
|
|
109
|
+
return;
|
|
110
|
+
case 'listApps': {
|
|
111
|
+
const appIds = shellApi.listApps();
|
|
112
|
+
setInputState(clearInputState);
|
|
113
|
+
appApi.addMessage(['Apps:', ...appIds].join('\n'));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
case 'switchApp':
|
|
117
|
+
void shellApi.loadApp(effect.appId, effect.input).then((result) => {
|
|
118
|
+
if (result.kind !== 'loaded') {
|
|
119
|
+
appApi.addMessage(result.message, 'error');
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
case 'dispatchEffectAction':
|
|
124
|
+
const effectActionResult = effectToAction(effect.action, navEntry, state);
|
|
125
|
+
setInputState(clearInputState);
|
|
126
|
+
switch (effectActionResult.kind) {
|
|
127
|
+
case 'action':
|
|
128
|
+
dispatch(effectActionResult.action);
|
|
129
|
+
return;
|
|
130
|
+
case 'rejected':
|
|
131
|
+
appApi.addMessage(effectActionResult.message, 'error');
|
|
132
|
+
return;
|
|
133
|
+
case 'ignored':
|
|
134
|
+
return;
|
|
135
|
+
default:
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
case 'setInputState':
|
|
139
|
+
setInputState(effect.inputState);
|
|
140
|
+
return;
|
|
141
|
+
case 'loadBranchEntries': {
|
|
142
|
+
void appMethods
|
|
143
|
+
.loadChildren(effect.path)
|
|
144
|
+
.then((entries) => {
|
|
145
|
+
dispatch({
|
|
146
|
+
kind: 'updateBranch',
|
|
147
|
+
path: effect.path,
|
|
148
|
+
entries,
|
|
149
|
+
});
|
|
150
|
+
})
|
|
151
|
+
.catch((error) => {
|
|
152
|
+
const appError = error instanceof Error ? error : new Error(String(error));
|
|
153
|
+
appApi.addMessage(`Unable to open directory: ${appError.message}`);
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
case 'saveView': {
|
|
158
|
+
// void appApi.viewApi
|
|
159
|
+
// .save(appApi.viewKey, effect.name, stateRef.current)
|
|
160
|
+
// .then(() => {
|
|
161
|
+
// setInputState({
|
|
162
|
+
// inputMode: 'normal',
|
|
163
|
+
// normalBuffer: '',
|
|
164
|
+
// });
|
|
165
|
+
// feedbackApi.addMessage(`Saved view: ${effect.name}`);
|
|
166
|
+
// })
|
|
167
|
+
// .catch((error: unknown) => {
|
|
168
|
+
// feedbackApi.addMessage(
|
|
169
|
+
// `Unable to save view: ${toError(error).message}`,
|
|
170
|
+
// );
|
|
171
|
+
// });
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
case 'loadView': {
|
|
175
|
+
// void appApi.viewApi
|
|
176
|
+
// .load(appApi.viewKey, effect.name)
|
|
177
|
+
// .then((loadedState) => {
|
|
178
|
+
// if (loadedState === undefined) {
|
|
179
|
+
// throw new Error(`View not found: ${effect.name}`);
|
|
180
|
+
// }
|
|
181
|
+
//
|
|
182
|
+
// // The saved buffer may be stale because the filesystem could have
|
|
183
|
+
// // changed since the view was saved.
|
|
184
|
+
// return appApi.stateApi.resync(loadedState, appApi.loadBranches);
|
|
185
|
+
// })
|
|
186
|
+
// .then((newState) => {
|
|
187
|
+
// const oldState = stateRef.current;
|
|
188
|
+
//
|
|
189
|
+
// dispatch({
|
|
190
|
+
// kind: 'setState',
|
|
191
|
+
// oldState,
|
|
192
|
+
// newState,
|
|
193
|
+
// });
|
|
194
|
+
//
|
|
195
|
+
// setInputState({
|
|
196
|
+
// inputMode: 'normal',
|
|
197
|
+
// normalBuffer: '',
|
|
198
|
+
// });
|
|
199
|
+
// })
|
|
200
|
+
// .catch((error: unknown) => {
|
|
201
|
+
// onError?.(toError(error));
|
|
202
|
+
// });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
case 'listFolds': {
|
|
206
|
+
const folds = appMethods.stateMethods.getFolds(state);
|
|
207
|
+
const foldTrees = [
|
|
208
|
+
...appMethods.stateMethods.foldMethods.indexes(folds),
|
|
209
|
+
].map((index) => ({
|
|
210
|
+
index,
|
|
211
|
+
info: appMethods.stateMethods.foldMethods.getInfoAtIndex(folds, index),
|
|
212
|
+
active: appMethods.stateMethods.foldMethods.isActiveAtIndex(folds, index),
|
|
213
|
+
}));
|
|
214
|
+
const indexWidth = Math.max(...foldTrees.map(({ index }) => String(index).length));
|
|
215
|
+
const lines = foldTrees.map(({ index, info, active }) => {
|
|
216
|
+
const status = active === true ? 'active' : 'inactive';
|
|
217
|
+
const name = info?.name ?? '<unavailable>';
|
|
218
|
+
return `${String(index).padStart(indexWidth)} ${status.padEnd(8)} ${name}`;
|
|
219
|
+
});
|
|
220
|
+
setInputState(clearInputState);
|
|
221
|
+
appApi.addMessage(['Fold trees:', ...lines].join('\n'));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
case 'emitError':
|
|
225
|
+
appApi.addMessage(effect.message, 'error');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}, [appMethods, effectToAction, navEntry, state, shellApi, appApi, clearInputState, exCommands]);
|
|
229
|
+
// ===========================================================================
|
|
230
|
+
// Input hook
|
|
231
|
+
// ===========================================================================
|
|
232
|
+
const handleInput = useCallback((userInput) => {
|
|
233
|
+
const intent = userInputToIntent(userInput, inputState);
|
|
234
|
+
if (intent === undefined) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const effectResult = intentToEffect(intent, state);
|
|
238
|
+
if (effectResult === undefined) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
setFeedback(undefined);
|
|
242
|
+
executeEffect(effectResult);
|
|
243
|
+
}, [executeEffect, inputState, intentToEffect, state]);
|
|
244
|
+
// ===========================================================================
|
|
245
|
+
// Render
|
|
246
|
+
// ===========================================================================
|
|
247
|
+
return (_jsx(ScreenRenderer, { view: view, feedback: feedback, feedbackApi: feedbackApi, config: config, inputState: inputState, handleInput: handleInput, onRowsChange: handleRowsChange }));
|
|
248
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AppMethods, SerializableKey } from 'dirvi-lib';
|
|
2
|
+
import type { LoadingScreenRenderer } from './loading-screen-renderer.js';
|
|
3
|
+
import type { AppScreenRenderer } from './app-screen-renderer.js';
|
|
4
|
+
import type { Initialization } from '../domain/initialization.js';
|
|
5
|
+
import type { ShellApi } from 'dirvi-apis';
|
|
6
|
+
type AppSetupProps<Id extends SerializableKey, Value, ViewKey = string> = {
|
|
7
|
+
appScreenRenderer: AppScreenRenderer;
|
|
8
|
+
loadingScreenRenderer: LoadingScreenRenderer;
|
|
9
|
+
appId: string;
|
|
10
|
+
initialization: Initialization;
|
|
11
|
+
shellApi: ShellApi;
|
|
12
|
+
appMethods: AppMethods<Id, Value, ViewKey>;
|
|
13
|
+
stdout?: (message: string) => void;
|
|
14
|
+
clipboard?: (value: string) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function AppSetup<Id extends SerializableKey, Value, ViewKey = string>({ appScreenRenderer, loadingScreenRenderer, appId, initialization, shellApi, appMethods, stdout, clipboard, }: AppSetupProps<Id, Value, ViewKey>): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=AppSetup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppSetup.d.ts","sourceRoot":"","sources":["../../src/ui/AppSetup.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAqB,eAAe,EAAS,MAAM,WAAW,CAAC;AAOlF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,KAAK,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,IAAI;IACxE,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAkBF,wBAAgB,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,EAC5E,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,MAAM,EACN,SAAS,GACV,EAAE,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,+BAwGnC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { createTreeMethods } from 'dirvi-lib';
|
|
4
|
+
import { createEffectToAction, createIntentToEffect, createReducer, } from 'dirvi-lib';
|
|
5
|
+
import { App } from './App.js';
|
|
6
|
+
// TODO Later: configuration watched for changes
|
|
7
|
+
const emptyExCommands = new Map();
|
|
8
|
+
export function AppSetup({ appScreenRenderer, loadingScreenRenderer, appId, initialization, shellApi, appMethods, stdout, clipboard, }) {
|
|
9
|
+
const [loadState, setLoadState] = useState();
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
let mounted = true;
|
|
12
|
+
setLoadState({ status: 'loading', appMethods: appMethods });
|
|
13
|
+
async function initialize() {
|
|
14
|
+
try {
|
|
15
|
+
const root = await appMethods.loadRoot();
|
|
16
|
+
if (!mounted) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const rootId = createTreeMethods().id(root);
|
|
20
|
+
const folds = appMethods.stateMethods.foldMethods.create(rootId, {
|
|
21
|
+
name: 'default',
|
|
22
|
+
description: null,
|
|
23
|
+
});
|
|
24
|
+
const initialState = appMethods.stateMethods.create(root, folds, { path: [] });
|
|
25
|
+
if (initialState === undefined) {
|
|
26
|
+
throw new Error('Cannot create initial app state');
|
|
27
|
+
}
|
|
28
|
+
setLoadState({ status: 'ready', appMethods: appMethods, initialState });
|
|
29
|
+
}
|
|
30
|
+
catch (cause) {
|
|
31
|
+
if (!mounted) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
35
|
+
setLoadState({ status: 'error', appMethods: appMethods, error });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
void initialize();
|
|
39
|
+
return () => {
|
|
40
|
+
mounted = false;
|
|
41
|
+
};
|
|
42
|
+
}, [appMethods]);
|
|
43
|
+
// --- Pure function deps ---
|
|
44
|
+
const exCommands = useMemo(() => initialization.exCommands.get(appId) ?? emptyExCommands, [initialization.exCommands, appId]);
|
|
45
|
+
const reducer = useMemo(() => createReducer(appMethods.stateMethods, appMethods.navMethods), [appMethods.stateMethods, appMethods.navMethods]);
|
|
46
|
+
const intentToEffect = useMemo(() => createIntentToEffect(new Set(exCommands.keys()), appMethods.stateMethods), [appMethods.stateMethods, exCommands]);
|
|
47
|
+
const effectToAction = useMemo(() => createEffectToAction(appMethods.stateMethods, appMethods.navMethods), [appMethods.navMethods, appMethods.stateMethods]);
|
|
48
|
+
// --- JSX ---
|
|
49
|
+
const LoadingScreenRenderer = loadingScreenRenderer;
|
|
50
|
+
if (loadState === undefined || loadState.appMethods !== appMethods) {
|
|
51
|
+
return _jsx(LoadingScreenRenderer, {});
|
|
52
|
+
}
|
|
53
|
+
if (loadState.status === 'loading') {
|
|
54
|
+
return _jsx(LoadingScreenRenderer, {});
|
|
55
|
+
}
|
|
56
|
+
if (loadState.status === 'error') {
|
|
57
|
+
throw loadState.error;
|
|
58
|
+
}
|
|
59
|
+
return (_jsx(App, { screenRenderer: appScreenRenderer, shellApi: shellApi, appMethods: appMethods, exCommands: exCommands, config: initialization.config, initialState: loadState.initialState, reducer: reducer, intentToEffect: intentToEffect, effectToAction: effectToAction, ...(stdout === undefined ? {} : { stdout }), ...(clipboard === undefined ? {} : { clipboard }) }));
|
|
60
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AppScreenRenderer } from './app-screen-renderer.js';
|
|
2
|
+
import type { LoadingScreenRenderer } from './loading-screen-renderer.js';
|
|
3
|
+
import type { Initialization } from '../domain/initialization.js';
|
|
4
|
+
import type { PackageLoader } from '../application/package-loader.js';
|
|
5
|
+
export type InitialAppOptions = Readonly<{
|
|
6
|
+
/**
|
|
7
|
+
* Requested app ID at startup.
|
|
8
|
+
*
|
|
9
|
+
* The Shell will later use this to override or select the app chosen by
|
|
10
|
+
* initialization.
|
|
11
|
+
*/
|
|
12
|
+
readonly app?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Host-provided startup input interpreted by the selected app.
|
|
15
|
+
*/
|
|
16
|
+
readonly input?: string;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Dependencies and output callbacks required by {@link Shell}.
|
|
20
|
+
*
|
|
21
|
+
* The API loader functions are injected so the shell does not depend directly
|
|
22
|
+
* on a platform-specific infrastructure implementation.
|
|
23
|
+
*/
|
|
24
|
+
export type ShellAppProps = {
|
|
25
|
+
/**
|
|
26
|
+
* Host-specific React implementation.
|
|
27
|
+
*/
|
|
28
|
+
readonly loadingScreenRenderer: LoadingScreenRenderer;
|
|
29
|
+
readonly appScreenRenderer: AppScreenRenderer;
|
|
30
|
+
readonly initialAppOptions: InitialAppOptions;
|
|
31
|
+
readonly initialization: Initialization;
|
|
32
|
+
readonly packageLoader: PackageLoader;
|
|
33
|
+
/** Request to exit dirvi with an optional message. */
|
|
34
|
+
readonly onQuit: (message?: string) => void;
|
|
35
|
+
/** Writes non-interactive application output. */
|
|
36
|
+
readonly stdout?: (message: string) => void;
|
|
37
|
+
/** Copies a value to the platform clipboard. */
|
|
38
|
+
readonly clipboard?: (value: string) => void;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Creates `ShellApi` and the `AppMethods` for a selected application.
|
|
42
|
+
*/
|
|
43
|
+
export declare function Shell({ onQuit, loadingScreenRenderer, appScreenRenderer, initialAppOptions, initialization, packageLoader, stdout, clipboard, }: ShellAppProps): import("react").JSX.Element;
|
|
44
|
+
//# sourceMappingURL=Shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Shell.d.ts","sourceRoot":"","sources":["../../src/ui/Shell.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAItE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;OAEG;IACH,QAAQ,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IACtD,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAE9C,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IAEtC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF;;GAEG;AACH,wBAAgB,KAAK,CAAC,EACpB,MAAM,EACN,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,MAAM,EACN,SAAS,GACV,EAAE,aAAa,+BA6Ff"}
|
package/dist/ui/Shell.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { AppSetup } from './AppSetup.js';
|
|
4
|
+
import { createAppMethodsApplicator } from '../application/create-app-methods.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates `ShellApi` and the `AppMethods` for a selected application.
|
|
7
|
+
*/
|
|
8
|
+
export function Shell({ onQuit, loadingScreenRenderer, appScreenRenderer, initialAppOptions, initialization, packageLoader, stdout, clipboard, }) {
|
|
9
|
+
const [loadedApp, setLoadedApp] = useState();
|
|
10
|
+
const [appError, setAppError] = useState(undefined);
|
|
11
|
+
/**
|
|
12
|
+
* Create the Shell API, based on given {@link Initialization}.
|
|
13
|
+
*/
|
|
14
|
+
const shellApi = useMemo(() => {
|
|
15
|
+
return {
|
|
16
|
+
listApps: () => [...initialization.app.registered.keys()],
|
|
17
|
+
loadApp: async (appId, input) => {
|
|
18
|
+
try {
|
|
19
|
+
const appReference = initialization.app.registered.get(appId);
|
|
20
|
+
if (appReference === undefined) {
|
|
21
|
+
return {
|
|
22
|
+
kind: 'notRegistered',
|
|
23
|
+
appId,
|
|
24
|
+
message: `App "${appId}" has not been registered in initial configuration script.`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const appPlugin = await packageLoader.loadAppPlugin(appReference);
|
|
28
|
+
const loadedAppMethodsApplicator = createAppMethodsApplicator(appPlugin.createAppDefinitionApplicator(input));
|
|
29
|
+
setLoadedApp({
|
|
30
|
+
appId,
|
|
31
|
+
appMethodsApplicator: loadedAppMethodsApplicator,
|
|
32
|
+
});
|
|
33
|
+
return { kind: 'loaded' };
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
37
|
+
return {
|
|
38
|
+
kind: 'loadFailed',
|
|
39
|
+
appId,
|
|
40
|
+
message: `Unable to load app "${appId}": ${message}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
quit: onQuit,
|
|
45
|
+
};
|
|
46
|
+
}, [packageLoader, onQuit, initialization.app.registered]);
|
|
47
|
+
/**
|
|
48
|
+
* Load initial `AppMethods` on startup, using the shell API just created and
|
|
49
|
+
* {@link Initialization}.
|
|
50
|
+
*/
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
void shellApi
|
|
53
|
+
.loadApp(initialAppOptions.app ?? initialization.app.initial, initialAppOptions.input)
|
|
54
|
+
.then((result) => {
|
|
55
|
+
if (result.kind !== 'loaded') {
|
|
56
|
+
setAppError(new Error(result.message));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}, [
|
|
60
|
+
initialAppOptions,
|
|
61
|
+
initialization.app.initial,
|
|
62
|
+
shellApi,
|
|
63
|
+
]);
|
|
64
|
+
if (appError !== undefined) {
|
|
65
|
+
throw appError;
|
|
66
|
+
}
|
|
67
|
+
// TODO Later: Loading messages
|
|
68
|
+
if (loadedApp === undefined) {
|
|
69
|
+
const LoadingScreenRenderer = loadingScreenRenderer;
|
|
70
|
+
return _jsx(LoadingScreenRenderer, {});
|
|
71
|
+
}
|
|
72
|
+
return loadedApp.appMethodsApplicator((appMethods) => (_jsx(AppSetup, { appScreenRenderer: appScreenRenderer, loadingScreenRenderer: loadingScreenRenderer, appId: loadedApp.appId, initialization: initialization, shellApi: shellApi, appMethods: appMethods, ...(stdout === undefined ? {} : { stdout }), ...(clipboard === undefined ? {} : { clipboard }) })));
|
|
73
|
+
}
|