@mak-98/dirvi-app 0.11.0-beta.6 → 0.11.0-beta.8
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/create-app-api.d.ts +1 -1
- package/dist/application/create-app-api.d.ts.map +1 -1
- package/dist/application/create-app-api.js +41 -14
- package/dist/application/init-runtime.d.ts +24 -12
- package/dist/application/init-runtime.d.ts.map +1 -1
- 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 +110 -76
- 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/initialization.d.ts +9 -8
- package/dist/domain/initialization.d.ts.map +1 -1
- 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/shell-api.d.ts +1 -1
- package/dist/domain/shell-api.d.ts.map +1 -1
- package/dist/domain/theme.d.ts +2 -14
- package/dist/domain/theme.d.ts.map +1 -1
- package/dist/domain/theme.js +9 -15
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +13 -11
- package/dist/ui/AppSetup.d.ts +1 -1
- package/dist/ui/AppSetup.d.ts.map +1 -1
- package/dist/ui/AppSetup.js +37 -25
- package/dist/ui/Shell.d.ts.map +1 -1
- package/dist/ui/Shell.js +6 -1
- package/dist/ui/color-resolver.d.ts +1 -1
- package/dist/ui/color-resolver.d.ts.map +1 -1
- package/dist/ui/hooks/useView.d.ts +2 -2
- package/dist/ui/hooks/useView.d.ts.map +1 -1
- package/dist/ui/hooks/useView.js +3 -3
- package/dist/ui/view.d.ts +7 -3
- package/dist/ui/view.d.ts.map +1 -1
- package/dist/ui/view.js +76 -19
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SerializableKey, AppApi } from 'dirvi-lib';
|
|
2
2
|
import type { AppApiApplicator, AppDefinition, AppDefinitionApplicator } from 'dirvi-apis';
|
|
3
3
|
/**
|
|
4
4
|
* Turns a plugin-owned app definition into a host-owned runtime AppApi.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-app-api.d.ts","sourceRoot":"","sources":["../../src/application/create-app-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"create-app-api.d.ts","sourceRoot":"","sources":["../../src/application/create-app-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,MAAM,EACP,MAAM,WAAW,CAAC;AAKnB,OAAO,KAAK,EAAE,gBAAgB,EAAkB,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE3G;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,uBAAuB,EAAE,uBAAuB,GAC/C,gBAAgB,CAQlB;AAED,wBAAgB,YAAY,CAC1B,EAAE,SAAS,eAAe,EAC1B,KAAK,EACL,aAAa,EAAE,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAiE5D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createFoldMethods, createCursorMethods, createUnorderedNavMethods, createOrderedNavMethods, } from 'dirvi-lib';
|
|
2
|
+
import { createTreeMethods, createStateApi, } from 'dirvi-lib';
|
|
2
3
|
/**
|
|
3
4
|
* Turns a plugin-owned app definition into a host-owned runtime AppApi.
|
|
4
5
|
*
|
|
@@ -12,22 +13,48 @@ export function createAppApiApplicator(appDefinitionApplicator) {
|
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
export function createAppApi(appDefinition) {
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const stateApi = createStateApi(
|
|
20
|
-
const
|
|
16
|
+
const treeMethods = createTreeMethods();
|
|
17
|
+
const foldTreeMethods = createTreeMethods();
|
|
18
|
+
const foldMethods = createFoldMethods(foldTreeMethods);
|
|
19
|
+
const cursorMethods = createCursorMethods();
|
|
20
|
+
const stateApi = createStateApi(treeMethods, foldMethods, cursorMethods);
|
|
21
|
+
const navMethods = appDefinition.semanticOrder ?
|
|
22
|
+
createOrderedNavMethods(treeMethods, foldMethods, cursorMethods) :
|
|
23
|
+
createUnorderedNavMethods(treeMethods, foldMethods, cursorMethods);
|
|
24
|
+
async function loadChildren(path) {
|
|
25
|
+
const children = await appDefinition.loadChildren(path);
|
|
26
|
+
const seenIds = new Set();
|
|
27
|
+
return children.map((child) => {
|
|
28
|
+
if (seenIds.has(child.id)) {
|
|
29
|
+
throw new Error('Application returned duplicate sibling IDs');
|
|
30
|
+
}
|
|
31
|
+
seenIds.add(child.id);
|
|
32
|
+
switch (child.kind) {
|
|
33
|
+
case 'leaf':
|
|
34
|
+
return treeMethods.createLeaf(child.id, child.value);
|
|
35
|
+
case 'unloaded-branch':
|
|
36
|
+
return treeMethods.createUnloadedBranch(child.id, child.value);
|
|
37
|
+
default:
|
|
38
|
+
// Also protects against invalid output from JavaScript plugins.
|
|
39
|
+
throw new Error('Application returned an invalid child kind');
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
21
43
|
return {
|
|
22
44
|
name: appDefinition.displayName,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
45
|
+
loadChildren: (path) => loadChildren(path),
|
|
46
|
+
loadRoot: async () => {
|
|
47
|
+
const rootData = await appDefinition.loadRoot();
|
|
48
|
+
const children = await loadChildren([]);
|
|
49
|
+
const root = treeMethods.createLoadedBranch(rootData.id, rootData.value, children);
|
|
50
|
+
if (root === undefined) {
|
|
51
|
+
throw new Error('Cannot create root: duplicate child IDs');
|
|
52
|
+
}
|
|
53
|
+
return root;
|
|
54
|
+
},
|
|
55
|
+
presentNode: (id, isBranch) => appDefinition.presentNode(id, isBranch),
|
|
26
56
|
subscribeToResync: appDefinition.subscribeToResync,
|
|
27
|
-
treeNodeApi,
|
|
28
|
-
foldsApi,
|
|
29
|
-
cursorApi,
|
|
30
57
|
stateApi,
|
|
31
|
-
|
|
58
|
+
navMethods,
|
|
32
59
|
};
|
|
33
60
|
}
|
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
import type { Initialization } from '../domain/initialization.js';
|
|
2
2
|
import type { PublicInitApi } from '../domain/public-init-api.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* A session for collecting and resolving initialization-script registrations.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* evaluation completes
|
|
6
|
+
* Expose `api` while evaluating `init.mjs`, then call `finish()` after module
|
|
7
|
+
* evaluation completes. Finishing validates the collected state and returns an
|
|
8
|
+
* immutable-by-contract snapshot of the resolved initialization.
|
|
8
9
|
*/
|
|
9
10
|
export type InitRuntime = {
|
|
11
|
+
/** The temporary API made available to `init.mjs` during evaluation. */
|
|
10
12
|
readonly api: PublicInitApi;
|
|
13
|
+
/**
|
|
14
|
+
* Validates and seals this session, returning its resolved startup state.
|
|
15
|
+
*
|
|
16
|
+
* The session is sealed only after successful validation. Once sealed,
|
|
17
|
+
* further API calls and calls to `finish()` throw
|
|
18
|
+
* `InitializationAlreadyFinishedError`.
|
|
19
|
+
*/
|
|
11
20
|
readonly finish: () => Initialization;
|
|
12
21
|
};
|
|
13
22
|
/**
|
|
14
|
-
* Creates an isolated
|
|
23
|
+
* Creates an isolated session for evaluating one `init.mjs` script.
|
|
15
24
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* the merged configuration. The session keeps its mutable state private.
|
|
25
|
+
* Option patches are merged with the current config and the complete result is
|
|
26
|
+
* validated. App and command registrations are checked as they are made.
|
|
19
27
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
28
|
+
* The runtime keeps its working state inside the closure and exposes it only
|
|
29
|
+
* through `api` and `finish`. finish() returns new maps, so later changes to
|
|
30
|
+
* the runtime’s maps cannot alter the returned maps.
|
|
23
31
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
32
|
+
* If no startup app is selected explicitly, the most recently registered app
|
|
33
|
+
* is chosen. At least one app must be registered before the session can finish.
|
|
34
|
+
*
|
|
35
|
+
* @returns A fresh initialization API and its corresponding `finish` function.
|
|
36
|
+
* @throws {InitRuntimeError} If a registration is invalid, validation fails,
|
|
37
|
+
* no app was registered, or the session has already been finished.
|
|
26
38
|
*/
|
|
27
39
|
export declare function createInitRuntime(): InitRuntime;
|
|
28
40
|
//# sourceMappingURL=init-runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-runtime.d.ts","sourceRoot":"","sources":["../../src/application/init-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"init-runtime.d.ts","sourceRoot":"","sources":["../../src/application/init-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAWlE;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,wEAAwE;IACxE,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAE5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,cAAc,CAAC;CACvC,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAuI/C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Stable categories for errors raised while collecting startup initialization. */
|
|
2
|
+
export type InitRuntimeErrorCode = 'ALREADY_FINISHED' | 'INVALID_VALUE' | 'DUPLICATE_APP' | 'DUPLICATE_EX_COMMAND' | 'INVALID_EX_COMMAND_HANDLER' | 'INVALID_CONFIG' | 'INVALID_APP_PACKAGE_REFERENCE' | 'NO_APPS_REGISTERED' | 'INITIAL_APP_NOT_REGISTERED' | 'INTERNAL_INVARIANT';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for errors raised by the initialization runtime.
|
|
5
|
+
*
|
|
6
|
+
* Consumers can use `code` for reliable error handling without depending on
|
|
7
|
+
* message text. `cause` is set when an underlying validation error is wrapped.
|
|
8
|
+
*/
|
|
9
|
+
export declare class InitRuntimeError extends Error {
|
|
10
|
+
readonly code: InitRuntimeErrorCode;
|
|
11
|
+
constructor(code: InitRuntimeErrorCode, message: string, options?: ErrorOptions);
|
|
12
|
+
}
|
|
13
|
+
/** An initialization API was used after its runtime had been sealed. */
|
|
14
|
+
export declare class InitializationAlreadyFinishedError extends InitRuntimeError {
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
/** A registration API received a value of the wrong type or an empty string. */
|
|
18
|
+
export declare class InvalidInitializationValueError extends InitRuntimeError {
|
|
19
|
+
constructor(label: string, expected: string);
|
|
20
|
+
}
|
|
21
|
+
/** An app ID was registered more than once. */
|
|
22
|
+
export declare class DuplicateAppRegistrationError extends InitRuntimeError {
|
|
23
|
+
constructor(id: string);
|
|
24
|
+
}
|
|
25
|
+
/** An Ex command name was registered more than once. */
|
|
26
|
+
export declare class DuplicateExCommandRegistrationError extends InitRuntimeError {
|
|
27
|
+
constructor(name: string);
|
|
28
|
+
}
|
|
29
|
+
/** An Ex command registration supplied a value that is not a handler function. */
|
|
30
|
+
export declare class InvalidExCommandHandlerError extends InitRuntimeError {
|
|
31
|
+
constructor();
|
|
32
|
+
}
|
|
33
|
+
/** The initialization script supplied options that do not resolve to valid config. */
|
|
34
|
+
export declare class InvalidInitializationConfigError extends InitRuntimeError {
|
|
35
|
+
constructor(cause: unknown);
|
|
36
|
+
}
|
|
37
|
+
/** An app registration supplied a package reference that failed validation. */
|
|
38
|
+
export declare class InvalidAppPackageReferenceError extends InitRuntimeError {
|
|
39
|
+
constructor(cause: unknown);
|
|
40
|
+
}
|
|
41
|
+
/** Initialization completed without registering any apps. */
|
|
42
|
+
export declare class NoAppsRegisteredError extends InitRuntimeError {
|
|
43
|
+
constructor();
|
|
44
|
+
}
|
|
45
|
+
/** The explicitly selected startup app was not registered. */
|
|
46
|
+
export declare class InitialAppNotRegisteredError extends InitRuntimeError {
|
|
47
|
+
constructor(id: string);
|
|
48
|
+
}
|
|
49
|
+
/** An internal invariant required by the initialization runtime was violated. */
|
|
50
|
+
export declare class InitRuntimeInvariantError extends InitRuntimeError {
|
|
51
|
+
constructor(message: string);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=init-runtime.error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-runtime.error.d.ts","sourceRoot":"","sources":["../../src/application/init-runtime.error.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,MAAM,oBAAoB,GAC5B,kBAAkB,GAClB,eAAe,GACf,eAAe,GACf,sBAAsB,GACtB,4BAA4B,GAC5B,gBAAgB,GAChB,+BAA+B,GAC/B,oBAAoB,GACpB,4BAA4B,GAC5B,oBAAoB,CAAC;AAEzB;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IAEvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB;gBAA1B,IAAI,EAAE,oBAAoB,EACnC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY;CAKzB;AAED,wEAAwE;AACxE,qBAAa,kCAAmC,SAAQ,gBAAgB;;CAOvE;AAED,gFAAgF;AAChF,qBAAa,+BAAgC,SAAQ,gBAAgB;gBACvD,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAG5C;AAED,+CAA+C;AAC/C,qBAAa,6BAA8B,SAAQ,gBAAgB;gBACrD,EAAE,EAAE,MAAM;CAMvB;AAED,wDAAwD;AACxD,qBAAa,mCAAoC,SAAQ,gBAAgB;gBAC3D,IAAI,EAAE,MAAM;CAMzB;AAED,kFAAkF;AAClF,qBAAa,4BAA6B,SAAQ,gBAAgB;;CAOjE;AAED,sFAAsF;AACtF,qBAAa,gCAAiC,SAAQ,gBAAgB;gBACxD,KAAK,EAAE,OAAO;CAO3B;AAED,+EAA+E;AAC/E,qBAAa,+BAAgC,SAAQ,gBAAgB;gBACvD,KAAK,EAAE,OAAO;CAO3B;AAED,6DAA6D;AAC7D,qBAAa,qBAAsB,SAAQ,gBAAgB;;CAO1D;AAED,8DAA8D;AAC9D,qBAAa,4BAA6B,SAAQ,gBAAgB;gBACpD,EAAE,EAAE,MAAM;CAMvB;AAED,iFAAiF;AACjF,qBAAa,yBAA0B,SAAQ,gBAAgB;gBACjD,OAAO,EAAE,MAAM;CAG5B"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for errors raised by the initialization runtime.
|
|
3
|
+
*
|
|
4
|
+
* Consumers can use `code` for reliable error handling without depending on
|
|
5
|
+
* message text. `cause` is set when an underlying validation error is wrapped.
|
|
6
|
+
*/
|
|
7
|
+
export class InitRuntimeError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(code, message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.name = new.target.name;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** An initialization API was used after its runtime had been sealed. */
|
|
16
|
+
export class InitializationAlreadyFinishedError extends InitRuntimeError {
|
|
17
|
+
constructor() {
|
|
18
|
+
super('ALREADY_FINISHED', 'Initialization has finished. Make all registrations while init.mjs is evaluated.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** A registration API received a value of the wrong type or an empty string. */
|
|
22
|
+
export class InvalidInitializationValueError extends InitRuntimeError {
|
|
23
|
+
constructor(label, expected) {
|
|
24
|
+
super('INVALID_VALUE', `${label} ${expected}.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** An app ID was registered more than once. */
|
|
28
|
+
export class DuplicateAppRegistrationError extends InitRuntimeError {
|
|
29
|
+
constructor(id) {
|
|
30
|
+
super('DUPLICATE_APP', `An app with ID "${id}" was registered multiple times.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** An Ex command name was registered more than once. */
|
|
34
|
+
export class DuplicateExCommandRegistrationError extends InitRuntimeError {
|
|
35
|
+
constructor(name) {
|
|
36
|
+
super('DUPLICATE_EX_COMMAND', `An Ex command named "${name}" was registered multiple times.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** An Ex command registration supplied a value that is not a handler function. */
|
|
40
|
+
export class InvalidExCommandHandlerError extends InitRuntimeError {
|
|
41
|
+
constructor() {
|
|
42
|
+
super('INVALID_EX_COMMAND_HANDLER', 'Ex command handler must be a function.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** The initialization script supplied options that do not resolve to valid config. */
|
|
46
|
+
export class InvalidInitializationConfigError extends InitRuntimeError {
|
|
47
|
+
constructor(cause) {
|
|
48
|
+
super('INVALID_CONFIG', `Initialization produced invalid configuration: ${getErrorMessage(cause)}`, { cause });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** An app registration supplied a package reference that failed validation. */
|
|
52
|
+
export class InvalidAppPackageReferenceError extends InitRuntimeError {
|
|
53
|
+
constructor(cause) {
|
|
54
|
+
super('INVALID_APP_PACKAGE_REFERENCE', `Invalid app package reference: ${getErrorMessage(cause)}`, { cause });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Initialization completed without registering any apps. */
|
|
58
|
+
export class NoAppsRegisteredError extends InitRuntimeError {
|
|
59
|
+
constructor() {
|
|
60
|
+
super('NO_APPS_REGISTERED', 'Initialization did not register any apps. Call dirvi.app.register(id, appReference) at least once in init.mjs.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** The explicitly selected startup app was not registered. */
|
|
64
|
+
export class InitialAppNotRegisteredError extends InitRuntimeError {
|
|
65
|
+
constructor(id) {
|
|
66
|
+
super('INITIAL_APP_NOT_REGISTERED', `Startup app "${id}" has not been registered.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** An internal invariant required by the initialization runtime was violated. */
|
|
70
|
+
export class InitRuntimeInvariantError extends InitRuntimeError {
|
|
71
|
+
constructor(message) {
|
|
72
|
+
super('INTERNAL_INVARIANT', message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function getErrorMessage(error) {
|
|
76
|
+
return error instanceof Error ? error.message : String(error);
|
|
77
|
+
}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { ConfigSchema, defaultConfig, merge } from '../domain/config.js';
|
|
2
2
|
import { AppPackageReference } from '../domain/app-pack-ref.js';
|
|
3
|
+
import { DuplicateAppRegistrationError, DuplicateExCommandRegistrationError, InitializationAlreadyFinishedError, InitRuntimeInvariantError, InvalidAppPackageReferenceError, InvalidExCommandHandlerError, InvalidInitializationConfigError, InvalidInitializationValueError, } from './init-runtime.error.js';
|
|
3
4
|
/**
|
|
4
|
-
* Creates an isolated
|
|
5
|
+
* Creates an isolated session for evaluating one `init.mjs` script.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the merged configuration. The session keeps its mutable state private.
|
|
7
|
+
* Option patches are merged with the current config and the complete result is
|
|
8
|
+
* validated. App and command registrations are checked as they are made.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The runtime keeps its working state inside the closure and exposes it only
|
|
11
|
+
* through `api` and `finish`. finish() returns new maps, so later changes to
|
|
12
|
+
* the runtime’s maps cannot alter the returned maps.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* If no startup app is selected explicitly, the most recently registered app
|
|
15
|
+
* is chosen. At least one app must be registered before the session can finish.
|
|
16
|
+
*
|
|
17
|
+
* @returns A fresh initialization API and its corresponding `finish` function.
|
|
18
|
+
* @throws {InitRuntimeError} If a registration is invalid, validation fails,
|
|
19
|
+
* no app was registered, or the session has already been finished.
|
|
16
20
|
*/
|
|
17
21
|
export function createInitRuntime() {
|
|
18
22
|
let config = defaultConfig;
|
|
@@ -20,98 +24,128 @@ export function createInitRuntime() {
|
|
|
20
24
|
const registeredApps = new Map();
|
|
21
25
|
const exCommands = new Map();
|
|
22
26
|
let finished = false;
|
|
23
|
-
|
|
27
|
+
// === Error Helpers =========================================================
|
|
28
|
+
/**
|
|
29
|
+
* Rejects API use after a successful call to `finish()`.
|
|
30
|
+
*
|
|
31
|
+
* Failed calls to `finish()` do not seal the runtime, allowing the caller to
|
|
32
|
+
* correct registrations and try again.
|
|
33
|
+
*/
|
|
34
|
+
const ensureUnfinished = () => {
|
|
24
35
|
if (finished) {
|
|
25
|
-
throw new
|
|
36
|
+
throw new InitializationAlreadyFinishedError();
|
|
26
37
|
}
|
|
27
38
|
};
|
|
28
|
-
|
|
39
|
+
/** Validates values. */
|
|
40
|
+
const validateNonEmptyString = (value, label) => {
|
|
29
41
|
if (typeof value !== 'string') {
|
|
30
|
-
throw new
|
|
42
|
+
throw new InvalidInitializationValueError(label, 'must be a string');
|
|
31
43
|
}
|
|
32
44
|
if (value.trim().length === 0) {
|
|
33
|
-
throw new
|
|
45
|
+
throw new InvalidInitializationValueError(label, 'must not be empty');
|
|
34
46
|
}
|
|
35
47
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
//
|
|
48
|
+
// === Public Initialization API =============================================
|
|
49
|
+
const api = {
|
|
50
|
+
options: {
|
|
51
|
+
set: (patch) => {
|
|
52
|
+
ensureUnfinished();
|
|
53
|
+
try {
|
|
54
|
+
// Validate the merged result so patches cannot break config
|
|
55
|
+
// invariants outside the fields they directly update.
|
|
43
56
|
config = ConfigSchema.parse(merge(config, patch));
|
|
44
|
-
}
|
|
57
|
+
}
|
|
58
|
+
catch (cause) {
|
|
59
|
+
throw new InvalidInitializationConfigError(cause);
|
|
60
|
+
}
|
|
45
61
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
},
|
|
63
|
+
app: {
|
|
64
|
+
register: (id, appReference) => {
|
|
65
|
+
ensureUnfinished();
|
|
66
|
+
validateNonEmptyString(id, 'App ID');
|
|
67
|
+
if (registeredApps.has(id)) {
|
|
68
|
+
throw new DuplicateAppRegistrationError(id);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
53
71
|
registeredApps.set(id, AppPackageReference.parse(appReference));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
initialAppId = appId;
|
|
59
|
-
},
|
|
72
|
+
}
|
|
73
|
+
catch (cause) {
|
|
74
|
+
throw new InvalidAppPackageReferenceError(cause);
|
|
75
|
+
}
|
|
60
76
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (typeof execute !== 'function') {
|
|
66
|
-
throw new Error('Ex command handler must be a function.');
|
|
67
|
-
}
|
|
68
|
-
if (exCommands.has(name)) {
|
|
69
|
-
throw new Error(`An Ex command named "${name}" was registered multiple times.`);
|
|
70
|
-
}
|
|
71
|
-
exCommands.set(name, { name, execute });
|
|
72
|
-
},
|
|
77
|
+
initial: (appId) => {
|
|
78
|
+
ensureUnfinished();
|
|
79
|
+
validateNonEmptyString(appId, 'App ID');
|
|
80
|
+
initialAppId = appId;
|
|
73
81
|
},
|
|
74
82
|
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
let initial;
|
|
82
|
-
if (initialAppId !== undefined) {
|
|
83
|
-
if (!registeredApps.has(initialAppId)) {
|
|
84
|
-
throw new Error(`Startup app "${initialAppId}" has not been registered.`);
|
|
83
|
+
ex: {
|
|
84
|
+
register: (name, execute) => {
|
|
85
|
+
ensureUnfinished();
|
|
86
|
+
validateNonEmptyString(name, 'Ex command name');
|
|
87
|
+
if (typeof execute !== 'function') {
|
|
88
|
+
throw new InvalidExCommandHandlerError();
|
|
85
89
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
finished = true;
|
|
92
|
-
return {
|
|
93
|
-
config,
|
|
94
|
-
app: {
|
|
95
|
-
initial,
|
|
96
|
-
registered: new Map(registeredApps),
|
|
97
|
-
},
|
|
98
|
-
exCommands: new Map(exCommands),
|
|
99
|
-
};
|
|
90
|
+
if (exCommands.has(name)) {
|
|
91
|
+
throw new DuplicateExCommandRegistrationError(name);
|
|
92
|
+
}
|
|
93
|
+
exCommands.set(name, { name, execute });
|
|
94
|
+
},
|
|
100
95
|
},
|
|
101
96
|
};
|
|
97
|
+
/**
|
|
98
|
+
* === FINISH ================================================================
|
|
99
|
+
* Resolves the startup app, seals the session, and returns its snapshot.
|
|
100
|
+
*
|
|
101
|
+
* Validation happens before sealing, so a failed finish can be retried after
|
|
102
|
+
* the script corrects its registrations.
|
|
103
|
+
* ===========================================================================
|
|
104
|
+
*/
|
|
105
|
+
const finish = () => {
|
|
106
|
+
ensureUnfinished();
|
|
107
|
+
if (registeredApps.size === 0) {
|
|
108
|
+
throw new Error('Initialization did not register any apps. Call ' +
|
|
109
|
+
'dirvi.apps.register(id, appReference) at least once in init.mjs.');
|
|
110
|
+
}
|
|
111
|
+
let initial;
|
|
112
|
+
if (initialAppId !== undefined) {
|
|
113
|
+
if (!registeredApps.has(initialAppId)) {
|
|
114
|
+
throw new Error(`Startup app "${initialAppId}" has not been registered.`);
|
|
115
|
+
}
|
|
116
|
+
initial = initialAppId;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
initial = getLastRegisteredAppId(registeredApps);
|
|
120
|
+
}
|
|
121
|
+
finished = true;
|
|
122
|
+
return {
|
|
123
|
+
config,
|
|
124
|
+
app: {
|
|
125
|
+
initial,
|
|
126
|
+
registered: new Map(registeredApps),
|
|
127
|
+
},
|
|
128
|
+
exCommands: new Map(exCommands),
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
api,
|
|
133
|
+
finish,
|
|
134
|
+
};
|
|
102
135
|
}
|
|
103
136
|
/**
|
|
104
|
-
* Returns the
|
|
137
|
+
* Returns the app registered last, using `Map` insertion order.
|
|
138
|
+
*
|
|
139
|
+
* This is only the fallback when `init.mjs` does not explicitly select an
|
|
140
|
+
* initial app. Callers must check that the map is non-empty first.
|
|
105
141
|
*
|
|
106
|
-
*
|
|
107
|
-
* fallback only; init.mjs should set initialAppId explicitly when startup
|
|
108
|
-
* selection is semantically important.
|
|
142
|
+
* @throws {InitRuntimeInvariantError} If called with an empty map.
|
|
109
143
|
*/
|
|
110
144
|
function getLastRegisteredAppId(apps) {
|
|
111
145
|
const appIds = [...apps.keys()];
|
|
112
146
|
const lastAppId = appIds.at(-1);
|
|
113
147
|
if (lastAppId === undefined) {
|
|
114
|
-
throw new
|
|
148
|
+
throw new InitRuntimeInvariantError('Expected at least one registered app when selecting the startup app.');
|
|
115
149
|
}
|
|
116
150
|
return lastAppId;
|
|
117
151
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-input.d.ts","sourceRoot":"","sources":["../../src/domain/app-input.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { Config } from './config.js';
|
|
2
2
|
import { ExCommand } from './ex-command.js';
|
|
3
3
|
import { AppPackageReference } from './app-pack-ref.js';
|
|
4
|
-
export type AppInput = string | undefined;
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
5
|
+
* Resolved startup state produced by evaluating `init.mjs`.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* initialAppId is always the ID of one app in apps: either the app explicitly
|
|
13
|
-
* selected by init.mjs or the most recently registered app.
|
|
7
|
+
* The initialization script runs before the terminal UI starts. It uses the
|
|
8
|
+
* temporary `dirvi` configuration facade to set shared options, register app
|
|
9
|
+
* packages, choose the initial app, and register Ex commands.
|
|
14
10
|
*/
|
|
15
11
|
export type Initialization = {
|
|
12
|
+
/** Dirvi-wide configuration resolved from the initialization script. */
|
|
16
13
|
readonly config: Readonly<Config>;
|
|
14
|
+
/** Registered apps and the app selected for startup. */
|
|
17
15
|
readonly app: {
|
|
16
|
+
/** ID of the app to open initially; must be present in `registered`. */
|
|
18
17
|
initial: string;
|
|
18
|
+
/** App package references keyed by app ID. */
|
|
19
19
|
registered: ReadonlyMap<string, AppPackageReference>;
|
|
20
20
|
};
|
|
21
|
+
/** Ex commands registered by the initialization script, keyed by name. */
|
|
21
22
|
readonly exCommands: ReadonlyMap<string, ExCommand>;
|
|
22
23
|
};
|
|
23
24
|
//# sourceMappingURL=initialization.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initialization.d.ts","sourceRoot":"","sources":["../../src/domain/initialization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD
|
|
1
|
+
{"version":3,"file":"initialization.d.ts","sourceRoot":"","sources":["../../src/domain/initialization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAElC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE;QACZ,wEAAwE;QACxE,OAAO,EAAE,MAAM,CAAC;QAEhB,8CAA8C;QAC9C,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;KACtD,CAAC;IAEF,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACrD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A semantic highlight name, not a concrete terminal color.
|
|
3
|
+
*
|
|
4
|
+
* Dot-separated names allow fallback to broader categories:
|
|
5
|
+
* "filesystem.directory" can fall back to "filesystem".
|
|
6
|
+
*/
|
|
7
|
+
export type HighlightToken = string;
|
|
8
|
+
export type NodePresentation = Readonly<{
|
|
9
|
+
content: string;
|
|
10
|
+
highlight: HighlightToken;
|
|
11
|
+
}>;
|
|
12
|
+
//# sourceMappingURL=node-presentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-presentation.d.ts","sourceRoot":"","sources":["../../src/domain/node-presentation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,cAAc,CAAC;CAC3B,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-api.d.ts","sourceRoot":"","sources":["../../src/domain/shell-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"shell-api.d.ts","sourceRoot":"","sources":["../../src/domain/shell-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE,CAAC"}
|
package/dist/domain/theme.d.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Renderers translate these symbolic names into their own color APIs. The CLI
|
|
6
|
-
* renderer currently maps them to terminal colors; another renderer does not
|
|
7
|
-
* need to depend on Chalk to interpret the same theme.
|
|
8
|
-
*/
|
|
2
|
+
import type { Theme } from 'dirvi-apis';
|
|
3
|
+
/** Named ANSI colors accepted by the theme schema. */
|
|
9
4
|
export declare const basicColorNames: readonly ["black", "red", "green", "yellow", "blue", "cyan", "magenta", "white", "gray", "grey", "blackBright", "redBright", "greenBright", "yellowBright", "blueBright", "cyanBright", "magentaBright", "whiteBright"];
|
|
10
|
-
export type BasicColorName = (typeof basicColorNames)[number];
|
|
11
5
|
export declare const ColorConfigSchema: z.ZodObject<{
|
|
12
6
|
hex: z.ZodOptional<z.ZodString>;
|
|
13
7
|
ansi256: z.ZodOptional<z.ZodNumber>;
|
|
@@ -32,7 +26,6 @@ export declare const ColorConfigSchema: z.ZodObject<{
|
|
|
32
26
|
whiteBright: "whiteBright";
|
|
33
27
|
}>>;
|
|
34
28
|
}, z.core.$strip>;
|
|
35
|
-
export type ColorConfig = z.infer<typeof ColorConfigSchema>;
|
|
36
29
|
export declare const ForegroundHighlightConfigSchema: z.ZodObject<{
|
|
37
30
|
fg: z.ZodObject<{
|
|
38
31
|
hex: z.ZodOptional<z.ZodString>;
|
|
@@ -161,10 +154,6 @@ export declare const ForegroundBackgroundHighlightConfigSchema: z.ZodObject<{
|
|
|
161
154
|
}>>;
|
|
162
155
|
}, z.core.$strip>;
|
|
163
156
|
}, z.core.$strip>;
|
|
164
|
-
export type ForegroundHighlightConfig = z.infer<typeof ForegroundHighlightConfigSchema>;
|
|
165
|
-
export type OptionalForegroundHighlightConfig = z.infer<typeof OptionalForegroundHighlightConfigSchema>;
|
|
166
|
-
export type BackgroundHighlightConfig = z.infer<typeof BackgroundHighlightConfigSchema>;
|
|
167
|
-
export type ForegroundBackgroundHighlightConfig = z.infer<typeof ForegroundBackgroundHighlightConfigSchema>;
|
|
168
157
|
export declare const ThemeSchema: z.ZodObject<{
|
|
169
158
|
statusBar: z.ZodDefault<z.ZodObject<{
|
|
170
159
|
command: z.ZodDefault<z.ZodObject<{
|
|
@@ -531,6 +520,5 @@ export declare const ThemeSchema: z.ZodObject<{
|
|
|
531
520
|
}, z.core.$strip>>;
|
|
532
521
|
}, z.core.$strip>>;
|
|
533
522
|
}, z.core.$strip>;
|
|
534
|
-
export type Theme = z.infer<typeof ThemeSchema>;
|
|
535
523
|
export declare const defaultTheme: Theme;
|
|
536
524
|
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/domain/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/domain/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAOV,KAAK,EACN,MAAM,YAAY,CAAC;AAMpB,sDAAsD;AACtD,eAAO,MAAM,eAAe,yNAsBkB,CAAC;AAU/C,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;iBAcM,CAAC;AAOrC,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;iBAEK,CAAC;AAElD,eAAO,MAAM,uCAAuC;;;;;;;;;;;;;;;;;;;;;;;;;iBAEK,CAAC;AAE1D,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;iBAEK,CAAC;AAElD,eAAO,MAAM,yCAAyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGK,CAAC;AA6F5D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CK,CAAC;AAE9B,eAAO,MAAM,YAAY,EAAE,KAA6B,CAAC"}
|
package/dist/domain/theme.js
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
// =============================================================================
|
|
3
|
-
//
|
|
3
|
+
// Colors: Schemas
|
|
4
4
|
// =============================================================================
|
|
5
|
-
|
|
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
|
-
*/
|
|
5
|
+
/** Named ANSI colors accepted by the theme schema. */
|
|
16
6
|
export const basicColorNames = [
|
|
17
7
|
'black',
|
|
18
8
|
'red',
|
|
@@ -34,6 +24,10 @@ export const basicColorNames = [
|
|
|
34
24
|
'magentaBright',
|
|
35
25
|
'whiteBright',
|
|
36
26
|
];
|
|
27
|
+
const HexColorSchema = z.string().regex(/^#[0-9a-fA-F]{6}$/, {
|
|
28
|
+
message: 'Expected a #rrggbb color',
|
|
29
|
+
});
|
|
30
|
+
const Ansi256ColorSchema = z.number().int().min(0).max(255);
|
|
37
31
|
const BasicColorSchema = z.enum(basicColorNames);
|
|
38
32
|
export const ColorConfigSchema = z
|
|
39
33
|
.object({
|
|
@@ -47,9 +41,9 @@ export const ColorConfigSchema = z
|
|
|
47
41
|
message: 'Expected at least one terminal color profile',
|
|
48
42
|
});
|
|
49
43
|
// =============================================================================
|
|
50
|
-
// Highlights
|
|
44
|
+
// Highlights: Schemas
|
|
51
45
|
// =============================================================================
|
|
52
|
-
//
|
|
46
|
+
// These schemas are intentionally non-strict.
|
|
53
47
|
export const ForegroundHighlightConfigSchema = z.object({
|
|
54
48
|
fg: ColorConfigSchema,
|
|
55
49
|
});
|
|
@@ -140,7 +134,7 @@ const themeDefaults = {
|
|
|
140
134
|
},
|
|
141
135
|
};
|
|
142
136
|
// =============================================================================
|
|
143
|
-
// Theme
|
|
137
|
+
// Theme: Schema
|
|
144
138
|
// =============================================================================
|
|
145
139
|
export const ThemeSchema = z.object({
|
|
146
140
|
statusBar: z
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,YAAY,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,YAAY,EACV,cAAc,EACd,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
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":"AAUA,OAAO,KAAK,EACV,MAAM,EAEN,cAAc,EAEd,cAAc,EAEd,OAAO,EACP,eAAe,EACf,KAAK,EAGN,MAAM,WAAW,CAAC;AAGnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,KAAK,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,IAAI;IACnE,cAAc,EAAE,iBAAiB,CAAC;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACnC,cAAc,EAAE,cAAc,CAAC;IAC/B,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,MAAM,EACN,cAAc,EACd,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,+
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,MAAM,EAEN,cAAc,EAEd,cAAc,EAEd,OAAO,EACP,eAAe,EACf,KAAK,EAGN,MAAM,WAAW,CAAC;AAGnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,KAAK,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,IAAI;IACnE,cAAc,EAAE,iBAAiB,CAAC;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACnC,cAAc,EAAE,cAAc,CAAC;IAC/B,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,MAAM,EACN,cAAc,EACd,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,EACd,MAAM,EACN,SAAS,EACT,OAAO,GACR,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,+BAoW9B"}
|
package/dist/ui/App.js
CHANGED
|
@@ -9,12 +9,14 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
9
9
|
// see current state
|
|
10
10
|
stateRef.current = state;
|
|
11
11
|
const navEntry = useMemo(() => {
|
|
12
|
-
const
|
|
12
|
+
const root = appApi.stateApi.getRoot(state);
|
|
13
|
+
const folds = appApi.stateApi.getFolds(state);
|
|
14
|
+
const navigation = appApi.navMethods.from(root, folds);
|
|
13
15
|
if (navigation === undefined) {
|
|
14
16
|
throw new Error('Tree-surfer state root must be a loaded branch.');
|
|
15
17
|
}
|
|
16
18
|
return navigation;
|
|
17
|
-
}, [appApi.
|
|
19
|
+
}, [appApi.navMethods, appApi.stateApi, state]);
|
|
18
20
|
const [inputState, setInputState] = useState({
|
|
19
21
|
inputMode: 'normal',
|
|
20
22
|
normalBuffer: '',
|
|
@@ -31,7 +33,7 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
31
33
|
const feedbackApi = useMemo(() => createFeedbackApi((nextFeedback) => {
|
|
32
34
|
setFeedback(nextFeedback);
|
|
33
35
|
}), []);
|
|
34
|
-
const view = useView(navEntry, state, appApi.
|
|
36
|
+
const view = useView(navEntry, state, appApi.stateApi, appApi.navMethods, appApi.stateApi.cursorApi, rowCount, appApi.presentNode);
|
|
35
37
|
// ===========================================================================
|
|
36
38
|
// Resync Subscriptions
|
|
37
39
|
// ===========================================================================
|
|
@@ -40,7 +42,7 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
40
42
|
const unsubscribe = appApi.subscribeToResync(() => {
|
|
41
43
|
const oldState = stateRef.current;
|
|
42
44
|
void appApi.stateApi
|
|
43
|
-
.resync(oldState, appApi.
|
|
45
|
+
.resync(oldState, appApi.loadChildren)
|
|
44
46
|
.then((newState) => {
|
|
45
47
|
if (!active) {
|
|
46
48
|
return;
|
|
@@ -128,7 +130,7 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
128
130
|
return;
|
|
129
131
|
case 'loadBranchEntries': {
|
|
130
132
|
void appApi
|
|
131
|
-
.
|
|
133
|
+
.loadChildren(effect.path)
|
|
132
134
|
.then((entries) => {
|
|
133
135
|
dispatch({
|
|
134
136
|
kind: 'updateBranch',
|
|
@@ -147,7 +149,7 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
147
149
|
if (navigation === null) {
|
|
148
150
|
return;
|
|
149
151
|
}
|
|
150
|
-
const visibleLeavesPaths = appApi.
|
|
152
|
+
const visibleLeavesPaths = appApi.navMethods.visibleLeavesPaths(navigation);
|
|
151
153
|
stdout?.(JSON.stringify({
|
|
152
154
|
type: 'displayed-leaves-paths',
|
|
153
155
|
paths: visibleLeavesPaths,
|
|
@@ -207,11 +209,11 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
207
209
|
}
|
|
208
210
|
// TODO: Add descriptions with `...` truncation.
|
|
209
211
|
case 'listFolds': {
|
|
210
|
-
const folds = state
|
|
211
|
-
const foldTrees = [...appApi.foldsApi.indexes(folds)].map((index) => ({
|
|
212
|
+
const folds = appApi.stateApi.getFolds(state);
|
|
213
|
+
const foldTrees = [...appApi.stateApi.foldsApi.indexes(folds)].map((index) => ({
|
|
212
214
|
index,
|
|
213
|
-
info: appApi.foldsApi.getInfoAtIndex(folds, index),
|
|
214
|
-
active: appApi.foldsApi.isActiveAtIndex(folds, index),
|
|
215
|
+
info: appApi.stateApi.foldsApi.getInfoAtIndex(folds, index),
|
|
216
|
+
active: appApi.stateApi.foldsApi.isActiveAtIndex(folds, index),
|
|
215
217
|
}));
|
|
216
218
|
const indexWidth = Math.max(...foldTrees.map(({ index }) => String(index).length));
|
|
217
219
|
const lines = foldTrees.map(({ index, info, active }) => {
|
|
@@ -230,7 +232,7 @@ export function App({ screenRenderer, shellApi, appApi, initialization, initialS
|
|
|
230
232
|
feedbackApi.addMessage(effect.message, 'error');
|
|
231
233
|
return;
|
|
232
234
|
}
|
|
233
|
-
}, [appApi, effectToAction, feedbackApi, navEntry, state, stdout, initialization.app.registered, shellApi]);
|
|
235
|
+
}, [appApi, effectToAction, feedbackApi, navEntry, state, stdout, initialization.app.registered, shellApi, initialization.exCommands]);
|
|
234
236
|
// ===========================================================================
|
|
235
237
|
// Input hook
|
|
236
238
|
// ===========================================================================
|
package/dist/ui/AppSetup.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AppApi, SerializableKey } from 'dirvi-lib';
|
|
2
2
|
import type { LoadingScreenRenderer } from './loading-screen-renderer.js';
|
|
3
3
|
import type { AppScreenRenderer } from './app-screen-renderer.js';
|
|
4
4
|
import { ShellApi } from '../domain/shell-api.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSetup.d.ts","sourceRoot":"","sources":["../../src/ui/AppSetup.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"AppSetup.d.ts","sourceRoot":"","sources":["../../src/ui/AppSetup.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAqB,eAAe,EAAS,MAAM,WAAW,CAAC;AAO9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAI7D,KAAK,eAAe,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,IAAI;IAC1E,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACnC,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,cAAc,EACd,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE,eAAe,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,+BAoGrC"}
|
package/dist/ui/AppSetup.js
CHANGED
|
@@ -1,46 +1,58 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { createTreeMethods } from 'dirvi-lib';
|
|
3
4
|
import { createEffectToAction, createIntentToEffect, createReducer, } from 'dirvi-lib';
|
|
4
5
|
import { App } from './App.js';
|
|
5
6
|
export function AppSetup({ appScreenRenderer, loadingScreenRenderer, initialization, shellApi, appApi, stdout, clipboard, }) {
|
|
6
|
-
const [
|
|
7
|
+
const [loadState, setLoadState] = useState();
|
|
7
8
|
// Loading and setting initial state.
|
|
8
9
|
useEffect(() => {
|
|
9
10
|
let mounted = true;
|
|
10
|
-
appApi
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
root
|
|
18
|
-
folds
|
|
11
|
+
setLoadState({ status: 'loading', appApi });
|
|
12
|
+
async function initialize() {
|
|
13
|
+
try {
|
|
14
|
+
const root = await appApi.loadRoot();
|
|
15
|
+
if (!mounted) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const rootId = createTreeMethods().id(root);
|
|
19
|
+
const folds = appApi.stateApi.foldsApi.create(rootId, {
|
|
19
20
|
name: 'default',
|
|
20
21
|
description: null,
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
});
|
|
23
|
+
const initialState = appApi.stateApi.create(root, folds, { path: [] });
|
|
24
|
+
if (initialState === undefined) {
|
|
25
|
+
throw new Error('Cannot create initial app state');
|
|
26
|
+
}
|
|
27
|
+
setLoadState({ status: 'ready', appApi, initialState });
|
|
28
|
+
}
|
|
29
|
+
catch (cause) {
|
|
30
|
+
if (!mounted) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
34
|
+
setLoadState({ status: 'error', appApi, error });
|
|
29
35
|
}
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
}
|
|
37
|
+
void initialize();
|
|
32
38
|
return () => {
|
|
33
39
|
mounted = false;
|
|
34
40
|
};
|
|
35
41
|
}, [appApi]);
|
|
36
42
|
// --- Pure function deps ---
|
|
37
|
-
const reducer = useMemo(() => createReducer(appApi.
|
|
38
|
-
const intentToEffect = useMemo(() => createIntentToEffect(new Set(initialization.exCommands.keys()), appApi.stateApi
|
|
39
|
-
const effectToAction = useMemo(() => createEffectToAction(appApi.
|
|
43
|
+
const reducer = useMemo(() => createReducer(appApi.stateApi, appApi.navMethods), [appApi.stateApi, appApi.navMethods]);
|
|
44
|
+
const intentToEffect = useMemo(() => createIntentToEffect(new Set(initialization.exCommands.keys()), appApi.stateApi), [appApi.stateApi, initialization.exCommands]);
|
|
45
|
+
const effectToAction = useMemo(() => createEffectToAction(appApi.stateApi, appApi.navMethods), [appApi.navMethods, appApi.stateApi]);
|
|
40
46
|
// --- JSX ---
|
|
41
47
|
const LoadingScreenRenderer = loadingScreenRenderer;
|
|
42
|
-
if (
|
|
48
|
+
if (loadState === undefined || loadState.appApi !== appApi) {
|
|
43
49
|
return _jsx(LoadingScreenRenderer, {});
|
|
44
50
|
}
|
|
45
|
-
|
|
51
|
+
if (loadState.status === 'loading') {
|
|
52
|
+
return _jsx(LoadingScreenRenderer, {});
|
|
53
|
+
}
|
|
54
|
+
if (loadState.status === 'error') {
|
|
55
|
+
throw loadState.error;
|
|
56
|
+
}
|
|
57
|
+
return (_jsx(App, { screenRenderer: appScreenRenderer, shellApi: shellApi, appApi: appApi, initialization: initialization, initialState: loadState.initialState, reducer: reducer, intentToEffect: intentToEffect, effectToAction: effectToAction, ...(stdout === undefined ? {} : { stdout }), ...(clipboard === undefined ? {} : { clipboard }) }));
|
|
46
58
|
}
|
package/dist/ui/Shell.d.ts.map
CHANGED
|
@@ -1 +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,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,MAAM,EACN,SAAS,GACV,EAAE,aAAa,+
|
|
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,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5C,gDAAgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,MAAM,EACN,SAAS,GACV,EAAE,aAAa,+BAyEf"}
|
package/dist/ui/Shell.js
CHANGED
|
@@ -36,7 +36,12 @@ export function Shell({ loadingScreenRenderer, appScreenRenderer, initialAppOpti
|
|
|
36
36
|
.catch((error) => {
|
|
37
37
|
setAppError(error instanceof Error ? error : new Error(String(error)));
|
|
38
38
|
});
|
|
39
|
-
}, [
|
|
39
|
+
}, [
|
|
40
|
+
initialAppOptions.app,
|
|
41
|
+
initialAppOptions.input,
|
|
42
|
+
initialization.app.initial,
|
|
43
|
+
shellApi,
|
|
44
|
+
]);
|
|
40
45
|
if (appError !== undefined) {
|
|
41
46
|
throw appError;
|
|
42
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-resolver.d.ts","sourceRoot":"","sources":["../../src/ui/color-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"color-resolver.d.ts","sourceRoot":"","sources":["../../src/ui/color-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAuB9C,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,EACxB,UAAU,GAAE,MAAuC,GAClD,MAAM,CA2BR"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CursorMethods, NavBranch, NavMethods, PresentNode, SerializableKey, State, StateApi } from 'dirvi-lib';
|
|
2
2
|
import { View } from '../view.js';
|
|
3
|
-
export declare function useView<Id extends SerializableKey, Value>(navNode: NavBranch<Id>, state: State<Id, Value>, navNodeApi:
|
|
3
|
+
export declare function useView<Id extends SerializableKey, Value>(navNode: NavBranch<Id>, state: State<Id, Value>, stateApi: StateApi<Id, Value>, navNodeApi: NavMethods<Id, Value>, cursorMethods: CursorMethods<Id>, rowCount: number, presentNode: PresentNode<Id>): View;
|
|
4
4
|
//# sourceMappingURL=useView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useView.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useView.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"useView.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useView.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,UAAU,EAAE,WAAW,EACvB,eAAe,EACf,KAAK,EAAE,QAAQ,EAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,IAAI,EAAgB,MAAM,YAAY,CAAC;AAMhD,wBAAgB,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EACvD,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,EACtB,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,EACvB,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EAC7B,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,EACjC,aAAa,EAAE,aAAa,CAAC,EAAE,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,GAC3B,IAAI,CAyBN"}
|
package/dist/ui/hooks/useView.js
CHANGED
|
@@ -3,15 +3,15 @@ import { View } from '../view.js';
|
|
|
3
3
|
const STATUS_BAR_HEIGHT = 1;
|
|
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(navNode, state, navNodeApi,
|
|
6
|
+
export function useView(navNode, state, stateApi, navNodeApi, cursorMethods, rowCount, presentNode) {
|
|
7
7
|
const viewportStartRef = useRef(0);
|
|
8
8
|
const viewportHeight = Math.max(0, rowCount - STATUS_BAR_HEIGHT);
|
|
9
9
|
if (viewportHeight === 0) {
|
|
10
10
|
return { rows: [] };
|
|
11
11
|
}
|
|
12
|
-
const rows = View.createRows(navNode, navNodeApi, state
|
|
12
|
+
const rows = View.createRows(navNode, navNodeApi, stateApi.getCursor(state), cursorMethods, presentNode);
|
|
13
13
|
viewportStartRef.current = viewportStart(rows, viewportHeight, viewportStartRef.current);
|
|
14
|
-
return View.create(navNode, navNodeApi, state
|
|
14
|
+
return View.create(navNode, navNodeApi, stateApi.getCursor(state), cursorMethods, viewportHeight, viewportStartRef.current, presentNode);
|
|
15
15
|
}
|
|
16
16
|
function viewportStart(rows, viewportHeight, currentViewportStart) {
|
|
17
17
|
const scrollMargin = 3;
|
package/dist/ui/view.d.ts
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import type { Cursor,
|
|
1
|
+
import type { Cursor, HighlightToken, NavBranch, NavMethods, PresentNode, SerializableKey, CursorMethods } from 'dirvi-lib';
|
|
2
2
|
type ViewRowBase = {
|
|
3
3
|
id: string;
|
|
4
4
|
indent: number;
|
|
5
5
|
content: string;
|
|
6
6
|
cursor: boolean;
|
|
7
7
|
selected: boolean;
|
|
8
|
+
highlight: HighlightToken;
|
|
8
9
|
};
|
|
9
10
|
export type ViewRow = (ViewRowBase & {
|
|
10
11
|
type: 'leaf';
|
|
11
12
|
}) | (ViewRowBase & {
|
|
12
13
|
type: 'branch';
|
|
13
14
|
foldedCount: number;
|
|
15
|
+
}) | (ViewRowBase & {
|
|
16
|
+
type: 'fold';
|
|
17
|
+
foldedCount: number;
|
|
14
18
|
});
|
|
15
19
|
export type View = {
|
|
16
20
|
rows: ViewRow[];
|
|
17
21
|
};
|
|
18
22
|
export declare const View: {
|
|
19
|
-
createRows<Id extends SerializableKey, Value>(rootNode: NavBranch<Id>, navNodeApi:
|
|
20
|
-
create<Id extends SerializableKey, Value>(rootNode: NavBranch<Id>, navNodeApi:
|
|
23
|
+
createRows<Id extends SerializableKey, Value>(rootNode: NavBranch<Id>, navNodeApi: NavMethods<Id, Value>, cursor: Cursor<Id>, cursorMethods: CursorMethods<Id>, presentNode: PresentNode<Id>): ViewRow[];
|
|
24
|
+
create<Id extends SerializableKey, Value>(rootNode: NavBranch<Id>, navNodeApi: NavMethods<Id, Value>, cursor: Cursor<Id>, cursorMethods: CursorMethods<Id>, viewportHeight: number, viewportStart: number, presentNode: PresentNode<Id>): View;
|
|
21
25
|
};
|
|
22
26
|
export {};
|
|
23
27
|
//# 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,KAAK,EACV,MAAM,EACN,
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/ui/view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,cAAc,EACd,SAAS,EAET,UAAU,EACV,WAAW,EACX,eAAe,EAAE,aAAa,EAC/B,MAAM,WAAW,CAAC;AAGnB,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,cAAc,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,OAAO,GACf,CAAC,WAAW,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,GACF,CAAC,WAAW,GAAG;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,GACF,CAAC,WAAW,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAAC;AAEP,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,CAAC;AASF,eAAO,MAAM,IAAI;eACJ,EAAE,SAAS,eAAe,EAAE,KAAK,YAChC,SAAS,CAAC,EAAE,CAAC,cACX,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,UACzB,MAAM,CAAC,EAAE,CAAC,iBACH,aAAa,CAAC,EAAE,CAAC,eACnB,WAAW,CAAC,EAAE,CAAC,GAC3B,OAAO,EAAE;WAwCL,EAAE,SAAS,eAAe,EAAE,KAAK,YAC5B,SAAS,CAAC,EAAE,CAAC,cACX,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,UACzB,MAAM,CAAC,EAAE,CAAC,iBACH,aAAa,CAAC,EAAE,CAAC,kBAChB,MAAM,iBACP,MAAM,eACR,WAAW,CAAC,EAAE,CAAC,GAC3B,IAAI;CAOR,CAAC"}
|
package/dist/ui/view.js
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
|
+
function foldId(parentPath, firstId) {
|
|
2
|
+
return `fold:${JSON.stringify([...parentPath, firstId])}`;
|
|
3
|
+
}
|
|
1
4
|
export const View = {
|
|
2
|
-
createRows(rootNode, navNodeApi, cursor,
|
|
3
|
-
const rootCursor = [];
|
|
4
|
-
const
|
|
5
|
+
createRows(rootNode, navNodeApi, cursor, cursorMethods, presentNode) {
|
|
6
|
+
const rootCursor = { path: [] };
|
|
7
|
+
const inlineFoldIds = new Set();
|
|
8
|
+
if (rootNode.children !== null) {
|
|
9
|
+
for (const position of navNodeApi.cursors(rootNode.children)) {
|
|
10
|
+
if (cursorMethods.isFoldCursor(position)) {
|
|
11
|
+
inlineFoldIds.add(foldId(position.parentPath, position.firstId));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const rootRow = viewRowForEntry(rootNode, null, cursorMethods.equal(cursor, rootCursor), navNodeApi, presentNode, inlineFoldIds);
|
|
5
16
|
if (rootNode.children === null) {
|
|
6
17
|
return [rootRow];
|
|
7
18
|
}
|
|
8
19
|
return [
|
|
9
20
|
rootRow,
|
|
10
|
-
...viewRowsAtNode(rootNode.children, [], navNodeApi, cursor,
|
|
21
|
+
...viewRowsAtNode(rootNode.children, [], navNodeApi, cursor, cursorMethods, presentNode, inlineFoldIds),
|
|
11
22
|
];
|
|
12
23
|
},
|
|
13
|
-
create(rootNode, navNodeApi, cursor,
|
|
14
|
-
const rows = View.createRows(rootNode, navNodeApi, cursor,
|
|
24
|
+
create(rootNode, navNodeApi, cursor, cursorMethods, viewportHeight, viewportStart, presentNode) {
|
|
25
|
+
const rows = View.createRows(rootNode, navNodeApi, cursor, cursorMethods, presentNode);
|
|
15
26
|
return {
|
|
16
27
|
rows: rows.slice(viewportStart, viewportStart + viewportHeight),
|
|
17
28
|
};
|
|
@@ -20,52 +31,98 @@ export const View = {
|
|
|
20
31
|
/**
|
|
21
32
|
* Converts one NavNode and its descendants into display ViewRows.
|
|
22
33
|
*
|
|
23
|
-
* `node` is the directory currently being visited.
|
|
24
|
-
*
|
|
25
34
|
* `parentPath` is the path of that directory. For example:
|
|
26
35
|
*
|
|
27
36
|
* [] root directory
|
|
28
37
|
* ['src'] root/src
|
|
29
38
|
* ['src', 'lib'] root/src/lib
|
|
30
39
|
*/
|
|
31
|
-
function viewRowsAtNode(node, parentPath, navNodeApi, cursor,
|
|
40
|
+
function viewRowsAtNode(node, parentPath, navNodeApi, cursor, cursorMethods, presentNode, inlineFoldIds) {
|
|
32
41
|
const rows = [];
|
|
33
42
|
for (const entry of node.entries) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
if (navNodeApi.entryIsFold(entry)) {
|
|
44
|
+
const firstEntry = entry.folded[0];
|
|
45
|
+
// Match navigation's guard for empty or richer future fold contents.
|
|
46
|
+
if (firstEntry === undefined || navNodeApi.entryIsFold(firstEntry)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const id = foldId(parentPath, firstEntry.id);
|
|
50
|
+
// Unordered summaries belong to the parent, not separate rows.
|
|
51
|
+
if (!inlineFoldIds.has(id)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const entryCursor = {
|
|
55
|
+
parentPath: [...parentPath],
|
|
56
|
+
firstId: firstEntry.id,
|
|
57
|
+
};
|
|
58
|
+
// Reuse the first hidden entry's highlight until folds have a presenter.
|
|
59
|
+
const { highlight } = presentNode(firstEntry.id, navNodeApi.entryIsBranch(firstEntry));
|
|
60
|
+
rows.push({
|
|
61
|
+
id,
|
|
62
|
+
indent: parentPath.length + 1,
|
|
63
|
+
content: `… ${entry.folded.length} folded`,
|
|
64
|
+
cursor: cursorMethods.equal(cursor, entryCursor),
|
|
65
|
+
selected: false,
|
|
66
|
+
highlight,
|
|
67
|
+
type: 'fold',
|
|
68
|
+
foldedCount: entry.folded.length,
|
|
69
|
+
});
|
|
70
|
+
// Hidden descendants are never rendered separately.
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const entryCursor = {
|
|
74
|
+
path: [...parentPath, entry.id],
|
|
75
|
+
};
|
|
76
|
+
const isCursor = cursorMethods.equal(cursor, entryCursor);
|
|
77
|
+
rows.push(viewRowForEntry(entry, parentPath, isCursor, navNodeApi, presentNode, inlineFoldIds));
|
|
37
78
|
// Case where there's no branches
|
|
38
79
|
if (!navNodeApi.entryIsBranch(entry) || entry.children === null) {
|
|
39
80
|
continue;
|
|
40
81
|
}
|
|
41
|
-
rows.push(...viewRowsAtNode(entry.children, [...parentPath, entry.id], navNodeApi, cursor,
|
|
82
|
+
rows.push(...viewRowsAtNode(entry.children, [...parentPath, entry.id], navNodeApi, cursor, cursorMethods, presentNode, inlineFoldIds));
|
|
42
83
|
}
|
|
43
84
|
return rows;
|
|
44
85
|
}
|
|
45
|
-
function viewRowForEntry(entry, parentPath, cursor, navNodeApi) {
|
|
86
|
+
function viewRowForEntry(entry, parentPath, cursor, navNodeApi, presentNode, inlineFoldIds) {
|
|
46
87
|
const id = entryId(parentPath ?? [], entry.id);
|
|
47
88
|
const indent = parentPath ? parentPath.length + 1 : 0;
|
|
48
89
|
const selected = false;
|
|
49
90
|
if (navNodeApi.entryIsBranch(entry)) {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
const { content, highlight } = presentNode(entry.id, true);
|
|
92
|
+
const entryPath = parentPath === null ? [] : [...parentPath, entry.id];
|
|
93
|
+
const foldedCount = entry.children?.entries.reduce((count, entry) => {
|
|
94
|
+
if (!navNodeApi.entryIsFold(entry)) {
|
|
95
|
+
return count;
|
|
96
|
+
}
|
|
97
|
+
const firstEntry = entry.folded[0];
|
|
98
|
+
if (firstEntry === undefined || navNodeApi.entryIsFold(firstEntry)) {
|
|
99
|
+
return count;
|
|
100
|
+
}
|
|
101
|
+
// Ordered folds already have their own rows.
|
|
102
|
+
if (inlineFoldIds.has(foldId(entryPath, firstEntry.id))) {
|
|
103
|
+
return count;
|
|
104
|
+
}
|
|
105
|
+
return count + entry.folded.length;
|
|
106
|
+
}, 0) ?? 0;
|
|
53
107
|
return {
|
|
54
108
|
id,
|
|
55
109
|
indent,
|
|
56
110
|
selected,
|
|
57
111
|
cursor,
|
|
58
|
-
content:
|
|
112
|
+
content: content,
|
|
113
|
+
highlight,
|
|
59
114
|
type: 'branch',
|
|
60
115
|
foldedCount,
|
|
61
116
|
};
|
|
62
117
|
}
|
|
118
|
+
const { content, highlight } = presentNode(entry.id, false);
|
|
63
119
|
return {
|
|
64
120
|
id,
|
|
65
121
|
indent,
|
|
66
122
|
selected,
|
|
67
123
|
cursor,
|
|
68
|
-
content
|
|
124
|
+
content,
|
|
125
|
+
highlight,
|
|
69
126
|
type: 'leaf',
|
|
70
127
|
};
|
|
71
128
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mak-98/dirvi-app",
|
|
3
|
-
"version": "0.11.0-beta.
|
|
3
|
+
"version": "0.11.0-beta.8",
|
|
4
4
|
"description": "Shared React application logic for dirvi hosts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"dirvi-apis": "0.11.0-beta.
|
|
20
|
-
"dirvi-lib": "0.11.0-beta.
|
|
19
|
+
"dirvi-apis": "0.11.0-beta.8",
|
|
20
|
+
"dirvi-lib": "0.11.0-beta.8",
|
|
21
21
|
"zod": "^4.4.3"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|