@dxos/app-framework 0.7.5-main.9d2a38b → 0.7.5-main.ff8607b
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/lib/browser/app-graph-builder-F7VZ6LRN.mjs +137 -0
- package/dist/lib/browser/app-graph-builder-F7VZ6LRN.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GNLU3GAU.mjs → chunk-ATRNTMSS.mjs} +623 -819
- package/dist/lib/browser/chunk-ATRNTMSS.mjs.map +7 -0
- package/dist/lib/browser/chunk-LDJ3T4V3.mjs +32 -0
- package/dist/lib/browser/chunk-LDJ3T4V3.mjs.map +7 -0
- package/dist/lib/browser/chunk-WS6SU6HI.mjs +285 -0
- package/dist/lib/browser/chunk-WS6SU6HI.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +57 -74
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/intent-dispatcher-E6J7E5Y5.mjs +11 -0
- package/dist/lib/browser/intent-dispatcher-E6J7E5Y5.mjs.map +7 -0
- package/dist/lib/browser/intent-resolver-XLE4L3LS.mjs +38 -0
- package/dist/lib/browser/intent-resolver-XLE4L3LS.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/store-QU2IKFAI.mjs +19 -0
- package/dist/lib/browser/store-QU2IKFAI.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +10 -3
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/worker.mjs +77 -0
- package/dist/lib/browser/worker.mjs.map +7 -0
- package/dist/lib/node/app-graph-builder-JGBADFF7.cjs +146 -0
- package/dist/lib/node/app-graph-builder-JGBADFF7.cjs.map +7 -0
- package/dist/lib/node/chunk-QLVQ6PND.cjs +58 -0
- package/dist/lib/node/chunk-QLVQ6PND.cjs.map +7 -0
- package/dist/lib/node/chunk-WKC6YMEQ.cjs +1433 -0
- package/dist/lib/node/chunk-WKC6YMEQ.cjs.map +7 -0
- package/dist/lib/node/chunk-WRWRZKZU.cjs +308 -0
- package/dist/lib/node/chunk-WRWRZKZU.cjs.map +7 -0
- package/dist/lib/node/index.cjs +106 -118
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/intent-dispatcher-CFBKDZQR.cjs +32 -0
- package/dist/lib/node/intent-dispatcher-CFBKDZQR.cjs.map +7 -0
- package/dist/lib/node/intent-resolver-3TKCXP4S.cjs +45 -0
- package/dist/lib/node/intent-resolver-3TKCXP4S.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/store-4QMUUU2A.cjs +34 -0
- package/dist/lib/node/store-4QMUUU2A.cjs.map +7 -0
- package/dist/lib/node/testing/index.cjs +15 -8
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node/worker.cjs +99 -0
- package/dist/lib/node/worker.cjs.map +7 -0
- package/dist/lib/node-esm/app-graph-builder-2QEX57NX.mjs +138 -0
- package/dist/lib/node-esm/app-graph-builder-2QEX57NX.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-KPMTPXQI.mjs → chunk-44J2VZBB.mjs} +623 -819
- package/dist/lib/node-esm/chunk-44J2VZBB.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-CNJYZNSL.mjs +34 -0
- package/dist/lib/node-esm/chunk-CNJYZNSL.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HTLXL32I.mjs +286 -0
- package/dist/lib/node-esm/chunk-HTLXL32I.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +57 -74
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/intent-dispatcher-LDQGDZ62.mjs +12 -0
- package/dist/lib/node-esm/intent-dispatcher-LDQGDZ62.mjs.map +7 -0
- package/dist/lib/node-esm/intent-resolver-7VJWN67U.mjs +39 -0
- package/dist/lib/node-esm/intent-resolver-7VJWN67U.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/store-VWDAYUQY.mjs +20 -0
- package/dist/lib/node-esm/store-VWDAYUQY.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +10 -3
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/worker.mjs +78 -0
- package/dist/lib/node-esm/worker.mjs.map +7 -0
- package/dist/types/src/App.d.ts.map +1 -1
- package/dist/types/src/common/capabilities.d.ts +63 -110
- package/dist/types/src/common/capabilities.d.ts.map +1 -1
- package/dist/types/src/common/events.d.ts +8 -1
- package/dist/types/src/common/events.d.ts.map +1 -1
- package/dist/types/src/common/file.d.ts +1 -1
- package/dist/types/src/common/file.d.ts.map +1 -1
- package/dist/types/src/common/graph.d.ts +2 -2
- package/dist/types/src/common/graph.d.ts.map +1 -1
- package/dist/types/src/common/index.d.ts +0 -1
- package/dist/types/src/common/index.d.ts.map +1 -1
- package/dist/types/src/common/layout.d.ts +204 -121
- package/dist/types/src/common/layout.d.ts.map +1 -1
- package/dist/types/src/common/surface.d.ts +3 -3
- package/dist/types/src/common/surface.d.ts.map +1 -1
- package/dist/types/src/common/translations.d.ts +7 -7
- package/dist/types/src/common/translations.d.ts.map +1 -1
- package/dist/types/src/core/capabilities.d.ts +6 -2
- package/dist/types/src/core/capabilities.d.ts.map +1 -1
- package/dist/types/src/core/manager.d.ts +2 -9
- package/dist/types/src/core/manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin.d.ts +5 -2
- package/dist/types/src/core/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/generator/Toolbar.d.ts.map +1 -1
- package/dist/types/src/playground/generator/generator.d.ts +2 -0
- package/dist/types/src/playground/generator/generator.d.ts.map +1 -1
- package/dist/types/src/playground/generator/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/logger/Toolbar.d.ts.map +1 -1
- package/dist/types/src/playground/logger/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/logger/schema.d.ts +1 -1
- package/dist/types/src/playground/logger/schema.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/actions.d.ts +1 -1
- package/dist/types/src/plugin-intent/actions.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/index.d.ts +0 -1
- package/dist/types/src/plugin-intent/index.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/intent-dispatcher.d.ts +27 -20
- package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/intent.d.ts +3 -3
- package/dist/types/src/plugin-intent/intent.d.ts.map +1 -1
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -1
- package/dist/types/src/plugin-settings/actions.d.ts +11 -1
- package/dist/types/src/plugin-settings/actions.d.ts.map +1 -1
- package/dist/types/src/plugin-settings/app-graph-builder.d.ts +197 -0
- package/dist/types/src/plugin-settings/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/intent-resolver.d.ts +4 -0
- package/dist/types/src/plugin-settings/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/store.d.ts +5 -0
- package/dist/types/src/plugin-settings/store.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/translations.d.ts +11 -0
- package/dist/types/src/plugin-settings/translations.d.ts.map +1 -0
- package/dist/types/src/{plugin-intent → react}/IntentContext.d.ts +1 -1
- package/dist/types/src/react/IntentContext.d.ts.map +1 -0
- package/dist/types/src/react/Surface.d.ts.map +1 -1
- package/dist/types/src/react/Surface.stories.d.ts +16 -0
- package/dist/types/src/react/Surface.stories.d.ts.map +1 -0
- package/dist/types/src/react/common.d.ts +12 -0
- package/dist/types/src/react/common.d.ts.map +1 -0
- package/dist/types/src/react/index.d.ts +2 -0
- package/dist/types/src/react/index.d.ts.map +1 -1
- package/dist/types/src/react/useIntentResolver.d.ts +3 -0
- package/dist/types/src/react/useIntentResolver.d.ts.map +1 -0
- package/dist/types/src/testing/withPluginManager.d.ts +1 -1
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
- package/dist/types/src/worker.d.ts +4 -0
- package/dist/types/src/worker.d.ts.map +1 -0
- package/package.json +26 -20
- package/project.json +3 -3
- package/src/App.tsx +15 -14
- package/src/common/capabilities.ts +20 -11
- package/src/common/events.ts +10 -1
- package/src/common/file.ts +1 -1
- package/src/common/graph.ts +2 -2
- package/src/common/index.ts +0 -1
- package/src/common/layout.ts +194 -126
- package/src/common/surface.ts +2 -2
- package/src/common/translations.ts +7 -8
- package/src/core/capabilities.ts +16 -7
- package/src/core/manager.test.ts +22 -73
- package/src/core/manager.ts +105 -91
- package/src/core/plugin.ts +6 -3
- package/src/playground/debug/plugin.ts +1 -1
- package/src/playground/generator/Toolbar.tsx +11 -11
- package/src/playground/generator/generator.ts +25 -0
- package/src/playground/generator/plugin.ts +6 -1
- package/src/playground/layout/plugin.ts +1 -1
- package/src/playground/logger/Toolbar.tsx +2 -1
- package/src/playground/logger/plugin.ts +6 -3
- package/src/playground/logger/schema.ts +1 -1
- package/src/plugin-intent/IntentPlugin.tsx +3 -43
- package/src/plugin-intent/actions.ts +1 -1
- package/src/plugin-intent/index.ts +0 -1
- package/src/plugin-intent/intent-dispatcher.test.ts +48 -29
- package/src/plugin-intent/intent-dispatcher.ts +76 -41
- package/src/plugin-intent/intent.ts +5 -5
- package/src/plugin-settings/SettingsPlugin.ts +19 -13
- package/src/plugin-settings/actions.ts +11 -1
- package/src/plugin-settings/app-graph-builder.ts +122 -0
- package/src/plugin-settings/intent-resolver.ts +28 -0
- package/src/plugin-settings/store.ts +20 -0
- package/src/plugin-settings/translations.ts +17 -0
- package/src/{plugin-intent → react}/IntentContext.tsx +2 -2
- package/src/react/Surface.stories.tsx +96 -0
- package/src/react/Surface.tsx +11 -8
- package/src/react/common.ts +12 -0
- package/src/react/index.ts +2 -0
- package/src/react/useIntentResolver.ts +22 -0
- package/src/testing/withPluginManager.tsx +11 -3
- package/src/worker.ts +11 -0
- package/tsconfig.json +3 -3
- package/dist/lib/browser/chunk-GNLU3GAU.mjs.map +0 -7
- package/dist/lib/node/chunk-FBA4BB3J.cjs +0 -1639
- package/dist/lib/node/chunk-FBA4BB3J.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-KPMTPXQI.mjs.map +0 -7
- package/dist/types/src/common/navigation.d.ts +0 -241
- package/dist/types/src/common/navigation.d.ts.map +0 -1
- package/dist/types/src/plugin-intent/IntentContext.d.ts.map +0 -1
- package/src/common/navigation.ts +0 -199
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/app-framework",
|
|
3
|
-
"version": "0.7.5-main.
|
|
3
|
+
"version": "0.7.5-main.ff8607b",
|
|
4
4
|
"description": "A framework for building applications from composible plugins.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
18
18
|
"browser": "./dist/lib/browser/testing/index.mjs",
|
|
19
19
|
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./worker": {
|
|
22
|
+
"types": "./dist/types/src/worker.d.ts",
|
|
23
|
+
"browser": "./dist/lib/browser/worker.mjs",
|
|
24
|
+
"node": "./dist/lib/node-esm/worker.mjs"
|
|
20
25
|
}
|
|
21
26
|
},
|
|
22
27
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -24,38 +29,39 @@
|
|
|
24
29
|
"*": {
|
|
25
30
|
"./testing": [
|
|
26
31
|
"dist/types/src/testing/index.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"./worker": [
|
|
34
|
+
"dist/types/src/worker.d.ts"
|
|
27
35
|
]
|
|
28
36
|
}
|
|
29
37
|
},
|
|
30
38
|
"dependencies": {
|
|
31
39
|
"@effect/schema": "^0.75.5",
|
|
32
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
33
40
|
"@preact/signals-core": "^1.6.0",
|
|
34
41
|
"effect": "^3.12.1",
|
|
35
|
-
"
|
|
36
|
-
"@dxos/async": "0.7.5-main.
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/react-hooks": "0.7.5-main.9d2a38b",
|
|
46
|
-
"@dxos/storybook-utils": "0.7.5-main.9d2a38b",
|
|
47
|
-
"@dxos/util": "0.7.5-main.9d2a38b",
|
|
48
|
-
"@dxos/log": "0.7.5-main.9d2a38b"
|
|
42
|
+
"@dxos/app-graph": "0.7.5-main.ff8607b",
|
|
43
|
+
"@dxos/async": "0.7.5-main.ff8607b",
|
|
44
|
+
"@dxos/debug": "0.7.5-main.ff8607b",
|
|
45
|
+
"@dxos/live-object": "0.7.5-main.ff8607b",
|
|
46
|
+
"@dxos/invariant": "0.7.5-main.ff8607b",
|
|
47
|
+
"@dxos/client-protocol": "0.7.5-main.ff8607b",
|
|
48
|
+
"@dxos/local-storage": "0.7.5-main.ff8607b",
|
|
49
|
+
"@dxos/log": "0.7.5-main.ff8607b",
|
|
50
|
+
"@dxos/util": "0.7.5-main.ff8607b",
|
|
51
|
+
"@dxos/react-hooks": "0.7.5-main.ff8607b"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
54
|
+
"@phosphor-icons/react": "^2.1.5",
|
|
51
55
|
"@types/react": "~18.2.0",
|
|
52
56
|
"react": "~18.2.0",
|
|
53
|
-
"@dxos/echo-
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
57
|
+
"@dxos/echo-schema": "0.7.5-main.ff8607b",
|
|
58
|
+
"@dxos/echo-signals": "0.7.5-main.ff8607b",
|
|
59
|
+
"@dxos/random": "0.7.5-main.ff8607b",
|
|
60
|
+
"@dxos/react-ui": "0.7.5-main.ff8607b",
|
|
61
|
+
"@dxos/react-ui-syntax-highlighter": "0.7.5-main.ff8607b",
|
|
62
|
+
"@dxos/storybook-utils": "0.7.5-main.ff8607b"
|
|
56
63
|
},
|
|
57
64
|
"peerDependencies": {
|
|
58
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
59
65
|
"react": "~18.2.0"
|
|
60
66
|
},
|
|
61
67
|
"publishConfig": {
|
package/project.json
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"options": {
|
|
13
13
|
"entryPoints": [
|
|
14
14
|
"{projectRoot}/src/index.ts",
|
|
15
|
-
"{projectRoot}/src/testing/index.ts"
|
|
15
|
+
"{projectRoot}/src/testing/index.ts",
|
|
16
|
+
"{projectRoot}/src/worker.ts"
|
|
16
17
|
]
|
|
17
18
|
}
|
|
18
19
|
},
|
|
@@ -20,7 +21,6 @@
|
|
|
20
21
|
"test": {}
|
|
21
22
|
},
|
|
22
23
|
"implicitDependencies": [
|
|
23
|
-
"esbuild"
|
|
24
|
-
"node-std"
|
|
24
|
+
"esbuild"
|
|
25
25
|
]
|
|
26
26
|
}
|
package/src/App.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import { create } from '@dxos/live-object';
|
|
|
11
11
|
import { Capabilities, Events } from './common';
|
|
12
12
|
import { PluginManager, type PluginManagerOptions, type Plugin } from './core';
|
|
13
13
|
import { topologicalSort } from './helpers';
|
|
14
|
-
import { ErrorBoundary, PluginManagerProvider } from './react';
|
|
14
|
+
import { ErrorBoundary, PluginManagerProvider, useCapabilities } from './react';
|
|
15
15
|
|
|
16
16
|
const ENABLED_KEY = 'dxos.org/app-framework/enabled';
|
|
17
17
|
|
|
@@ -96,21 +96,27 @@ export const createApp = ({
|
|
|
96
96
|
|
|
97
97
|
setupDevtools(manager);
|
|
98
98
|
|
|
99
|
+
// TODO(wittjosiah): Factor out such that this could be called per surface role when attempting to render.
|
|
100
|
+
void manager.activate(Events.SetupSurfaces);
|
|
99
101
|
void manager.activate(Events.Startup);
|
|
100
102
|
|
|
101
103
|
return () => (
|
|
102
104
|
<ErrorBoundary fallback={fallback}>
|
|
103
|
-
<
|
|
105
|
+
<PluginManagerProvider value={manager}>
|
|
106
|
+
<App placeholder={placeholder} state={state} />
|
|
107
|
+
</PluginManagerProvider>
|
|
104
108
|
</ErrorBoundary>
|
|
105
109
|
);
|
|
106
110
|
};
|
|
107
111
|
|
|
108
112
|
type AppProps = Required<Pick<CreateAppOptions, 'placeholder'>> & {
|
|
109
|
-
manager: PluginManager;
|
|
110
113
|
state: { ready: boolean; error: unknown };
|
|
111
114
|
};
|
|
112
115
|
|
|
113
|
-
const App = ({ placeholder,
|
|
116
|
+
const App = ({ placeholder, state }: AppProps) => {
|
|
117
|
+
const reactContexts = useCapabilities(Capabilities.ReactContext);
|
|
118
|
+
const reactRoots = useCapabilities(Capabilities.ReactRoot);
|
|
119
|
+
|
|
114
120
|
if (state.error) {
|
|
115
121
|
// This trigger the error boundary to provide UI feedback for the startup error.
|
|
116
122
|
throw state.error;
|
|
@@ -121,18 +127,13 @@ const App = ({ placeholder, manager, state }: AppProps) => {
|
|
|
121
127
|
return <>{placeholder}</>;
|
|
122
128
|
}
|
|
123
129
|
|
|
124
|
-
const reactContexts = manager.context.requestCapabilities(Capabilities.ReactContext);
|
|
125
|
-
const reactRoots = manager.context.requestCapabilities(Capabilities.ReactRoot);
|
|
126
|
-
|
|
127
130
|
const ComposedContext = composeContexts(reactContexts);
|
|
128
131
|
return (
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
</ComposedContext>
|
|
135
|
-
</PluginManagerProvider>
|
|
132
|
+
<ComposedContext>
|
|
133
|
+
{reactRoots.map(({ id, root: Component }) => (
|
|
134
|
+
<Component key={id} />
|
|
135
|
+
))}
|
|
136
|
+
</ComposedContext>
|
|
136
137
|
);
|
|
137
138
|
};
|
|
138
139
|
|
|
@@ -2,18 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Schema as S } from '@effect/schema';
|
|
5
6
|
import { type FC, type PropsWithChildren } from 'react';
|
|
6
7
|
|
|
7
8
|
import { type GraphBuilder } from '@dxos/app-graph';
|
|
8
9
|
import { type Space } from '@dxos/client-protocol';
|
|
9
|
-
import { type S } from '@dxos/echo-schema';
|
|
10
10
|
import { type RootSettingsStore } from '@dxos/local-storage';
|
|
11
|
-
import { type DeepReadonly } from '@dxos/util';
|
|
12
11
|
|
|
13
12
|
import { type FileInfo } from './file';
|
|
14
13
|
import { type NodeSerializer } from './graph';
|
|
15
|
-
import { type Layout } from './layout';
|
|
16
|
-
import { type LayoutParts } from './navigation';
|
|
17
14
|
import { type SurfaceDefinition } from './surface';
|
|
18
15
|
import { type Resource } from './translations';
|
|
19
16
|
import { defineCapability, type PluginManager } from '../core';
|
|
@@ -40,13 +37,25 @@ export namespace Capabilities {
|
|
|
40
37
|
'dxos.org/app-framework/capability/intent-dispatcher',
|
|
41
38
|
);
|
|
42
39
|
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
export type Layout = Readonly<{
|
|
41
|
+
mode: string;
|
|
42
|
+
dialogOpen: boolean;
|
|
43
|
+
sidebarOpen: boolean;
|
|
44
|
+
complementarySidebarOpen: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Identifiers of items which are currently active in the application.
|
|
47
|
+
*/
|
|
48
|
+
active: string[];
|
|
49
|
+
/**
|
|
50
|
+
* Identifiers of items which were previously active in the application.
|
|
51
|
+
*/
|
|
52
|
+
inactive: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Identifier of the item which should be scrolled into view.
|
|
55
|
+
*/
|
|
56
|
+
scrollIntoView: string | undefined;
|
|
57
|
+
}>;
|
|
58
|
+
export const Layout = defineCapability<Layout>('dxos.org/app-framework/capability/layout');
|
|
50
59
|
|
|
51
60
|
export const Translations = defineCapability<Readonly<Resource[]>>('dxos.org/app-framework/capability/translations');
|
|
52
61
|
|
package/src/common/events.ts
CHANGED
|
@@ -15,6 +15,16 @@ export namespace Events {
|
|
|
15
15
|
// Dependent Events
|
|
16
16
|
//
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Fired to load any newly available surfaces.
|
|
20
|
+
*/
|
|
21
|
+
export const SetupSurfaces = defineEvent('dxos.org/app-framework/event/setup-surfaces');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Fired to load any newly available metadata.
|
|
25
|
+
*/
|
|
26
|
+
export const SetupMetadata = defineEvent('dxos.org/app-framework/event/setup-metadata');
|
|
27
|
+
|
|
18
28
|
/**
|
|
19
29
|
* Fired before the intent dispatcher is activated.
|
|
20
30
|
*/
|
|
@@ -59,5 +69,4 @@ export namespace Events {
|
|
|
59
69
|
*/
|
|
60
70
|
export const createStateEvent = (specifier: string) => defineEvent('dxos.org/app-framework/event/state', specifier);
|
|
61
71
|
export const LayoutReady = createStateEvent(Capabilities.Layout.identifier);
|
|
62
|
-
export const LocationReady = createStateEvent(Capabilities.Location.identifier);
|
|
63
72
|
}
|
package/src/common/file.ts
CHANGED
package/src/common/graph.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import type { Node } from '@dxos/app-graph';
|
|
6
|
-
import { type MaybePromise } from '@dxos/util';
|
|
6
|
+
import { type MaybePromise, type Position } from '@dxos/util';
|
|
7
7
|
|
|
8
8
|
// TODO(wittjosiah): Factor out.
|
|
9
9
|
export type SerializedNode = {
|
|
@@ -16,7 +16,7 @@ export type SerializedNode = {
|
|
|
16
16
|
export type NodeSerializer<T = any> = {
|
|
17
17
|
inputType: string;
|
|
18
18
|
outputType: string;
|
|
19
|
-
|
|
19
|
+
position?: Position;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Takes a node and serializes it into a format that can be stored.
|
package/src/common/index.ts
CHANGED
package/src/common/layout.ts
CHANGED
|
@@ -2,83 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { S } from '@
|
|
5
|
+
import { Schema as S } from '@effect/schema';
|
|
6
6
|
|
|
7
7
|
import { Label } from '../plugin-intent';
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
// Provides
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
export const Toast = S.Struct({
|
|
14
|
-
id: S.String,
|
|
15
|
-
title: S.optional(Label),
|
|
16
|
-
description: S.optional(Label),
|
|
17
|
-
icon: S.optional(S.String),
|
|
18
|
-
duration: S.optional(S.Number),
|
|
19
|
-
closeLabel: S.optional(Label),
|
|
20
|
-
actionLabel: S.optional(Label),
|
|
21
|
-
actionAlt: S.optional(Label),
|
|
22
|
-
// TODO(wittjosiah): Make class with customizable method?
|
|
23
|
-
onAction: S.optional(S.Any),
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
export type Toast = S.Schema.Type<typeof Toast>;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Basic state provided by a layout plugin.
|
|
30
|
-
*
|
|
31
|
-
* Layout provides the state of global UI landmarks, such as the sidebar, dialog, and popover.
|
|
32
|
-
* Generally only one dialog or popover should be open at a time, a layout plugin should manage this.
|
|
33
|
-
* For other landmarks, such as toasts, rendering them in the layout prevents them from unmounting when navigating.
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
const LayoutMode = S.Union(S.Literal('deck'), S.Literal('solo'), S.Literal('fullscreen'));
|
|
37
|
-
export const isLayoutMode = (value: any): value is LayoutMode => S.is(LayoutMode)(value);
|
|
38
|
-
export type LayoutMode = S.Schema.Type<typeof LayoutMode>;
|
|
39
|
-
|
|
40
|
-
export const Layout = S.mutable(
|
|
41
|
-
S.Struct({
|
|
42
|
-
layoutMode: LayoutMode,
|
|
43
|
-
|
|
44
|
-
sidebarOpen: S.Boolean,
|
|
45
|
-
complementarySidebarOpen: S.Boolean,
|
|
46
|
-
/**
|
|
47
|
-
* @deprecated Data to be passed to the complementary sidebar Surface.
|
|
48
|
-
*/
|
|
49
|
-
complementarySidebarContent: S.optional(S.Any),
|
|
50
|
-
|
|
51
|
-
dialogOpen: S.Boolean,
|
|
52
|
-
/**
|
|
53
|
-
* Data to be passed to the dialog Surface.
|
|
54
|
-
*/
|
|
55
|
-
dialogContent: S.optional(S.Any),
|
|
56
|
-
// TODO(wittjosiah): Custom properties?
|
|
57
|
-
dialogBlockAlign: S.optional(S.Literal('start', 'center')),
|
|
58
|
-
dialogType: S.optional(S.Literal('default', 'alert')),
|
|
59
|
-
|
|
60
|
-
popoverOpen: S.Boolean,
|
|
61
|
-
/**
|
|
62
|
-
* Data to be passed to the popover Surface.
|
|
63
|
-
*/
|
|
64
|
-
popoverContent: S.optional(S.Any),
|
|
65
|
-
popoverAnchorId: S.optional(S.String),
|
|
66
|
-
|
|
67
|
-
toasts: S.mutable(S.Array(Toast)),
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The identifier of a component to scroll into view when it is mounted.
|
|
71
|
-
*/
|
|
72
|
-
scrollIntoView: S.optional(S.String),
|
|
73
|
-
}),
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
export type Layout = S.Schema.Type<typeof Layout>;
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
// Intents
|
|
80
|
-
//
|
|
81
|
-
|
|
82
9
|
export const LAYOUT_PLUGIN = 'dxos.org/plugin/layout';
|
|
83
10
|
export const LAYOUT_ACTION = `${LAYOUT_PLUGIN}/action`;
|
|
84
11
|
|
|
@@ -86,71 +13,212 @@ export const LAYOUT_ACTION = `${LAYOUT_PLUGIN}/action`;
|
|
|
86
13
|
* Expected payload for layout actions.
|
|
87
14
|
*/
|
|
88
15
|
export namespace LayoutAction {
|
|
89
|
-
export
|
|
16
|
+
export const UPDATE_LAYOUT = `${LAYOUT_ACTION}/update-layout`;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generic layout action.
|
|
20
|
+
*/
|
|
21
|
+
export class UpdateLayout extends S.TaggedClass<UpdateLayout>()(UPDATE_LAYOUT, {
|
|
90
22
|
input: S.Struct({
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
*
|
|
99
|
-
* If omitted, the element's state will be toggled or set based on other provided data.
|
|
100
|
-
* For example, if `component` is provided, the state will be set to `true`.
|
|
101
|
-
*/
|
|
102
|
-
state: S.optional(S.Boolean),
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Component to render in the dialog or popover.
|
|
106
|
-
*/
|
|
107
|
-
component: S.optional(S.String),
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Data to be passed to the dialog or popover Surface.
|
|
111
|
-
*/
|
|
112
|
-
subject: S.optional(S.Any),
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Anchor ID for the popover.
|
|
116
|
-
*/
|
|
117
|
-
anchorId: S.optional(S.String),
|
|
118
|
-
|
|
119
|
-
// TODO(wittjosiah): Custom properties?
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Block alignment for the dialog.
|
|
123
|
-
*/
|
|
124
|
-
dialogBlockAlign: S.optional(S.Literal('start', 'center')),
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Type of dialog.
|
|
128
|
-
*/
|
|
129
|
-
dialogType: S.optional(S.Literal('default', 'alert')),
|
|
23
|
+
part: S.String.annotations({ description: 'The part of the layout to mutate.' }),
|
|
24
|
+
subject: S.optional(S.Any.annotations({ description: 'The subject of the layout update.' })),
|
|
25
|
+
options: S.optional(
|
|
26
|
+
S.Record({ key: S.String, value: S.Any }).annotations({
|
|
27
|
+
description: 'Additional options for the layout action.',
|
|
28
|
+
}),
|
|
29
|
+
),
|
|
130
30
|
}),
|
|
131
31
|
output: S.Void,
|
|
132
32
|
}) {}
|
|
133
33
|
|
|
134
|
-
//
|
|
34
|
+
//
|
|
35
|
+
// Common layout actions.
|
|
36
|
+
//
|
|
37
|
+
|
|
38
|
+
// NOTE: These are layout actions which are currently in common use.
|
|
39
|
+
// They constrain the generic layout action types to provide additional type safety.
|
|
40
|
+
// However, they all follow the same generic structure and intent id.
|
|
41
|
+
// This allows for plugins to update the layout without depending on a specific layout plugin.
|
|
42
|
+
// The expectation is that other norms other than these will emerge over time.
|
|
43
|
+
|
|
44
|
+
export class SetLayoutMode extends S.TaggedClass<SetLayoutMode>()(UPDATE_LAYOUT, {
|
|
45
|
+
input: S.Struct({
|
|
46
|
+
part: S.Literal('mode').annotations({ description: 'Setting the layout mode.' }),
|
|
47
|
+
subject: S.optional(S.String.annotations({ description: 'Item which is the subject of the new layout mode.' })),
|
|
48
|
+
options: S.Union(
|
|
49
|
+
S.Struct({ mode: S.String.annotations({ description: 'The new layout mode.' }) }),
|
|
50
|
+
S.Struct({ revert: S.Boolean.annotations({ description: 'Revert to the previous layout mode.' }) }),
|
|
51
|
+
),
|
|
52
|
+
}),
|
|
53
|
+
output: S.Void,
|
|
54
|
+
}) {}
|
|
135
55
|
|
|
136
|
-
export class
|
|
137
|
-
input: S.
|
|
138
|
-
S.
|
|
139
|
-
|
|
56
|
+
export class UpdateSidebar extends S.TaggedClass<UpdateSidebar>()(UPDATE_LAYOUT, {
|
|
57
|
+
input: S.Struct({
|
|
58
|
+
part: S.Literal('sidebar').annotations({ description: 'Updating the sidebar.' }),
|
|
59
|
+
subject: S.optional(S.String.annotations({ description: 'URI of the component to display in the sidebar.' })),
|
|
60
|
+
options: S.optional(
|
|
61
|
+
S.Struct({
|
|
62
|
+
state: S.Literal('closed', 'collapsed', 'expanded').annotations({
|
|
63
|
+
description: 'Whether the sidebar is closed, collapsed, or expanded.',
|
|
64
|
+
}),
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
67
|
+
}),
|
|
68
|
+
output: S.Void,
|
|
69
|
+
}) {}
|
|
70
|
+
|
|
71
|
+
export class UpdateComplementary extends S.TaggedClass<UpdateComplementary>()(UPDATE_LAYOUT, {
|
|
72
|
+
input: S.Struct({
|
|
73
|
+
part: S.Literal('complementary').annotations({ description: 'Updating the complementary sidebar.' }),
|
|
74
|
+
subject: S.optional(
|
|
75
|
+
S.String.annotations({ description: 'URI of the component to display in the complementary area.' }),
|
|
76
|
+
),
|
|
77
|
+
options: S.optional(
|
|
78
|
+
S.Struct({
|
|
79
|
+
state: S.Literal('closed', 'collapsed', 'expanded').annotations({
|
|
80
|
+
description: 'Whether the complementary sidebar is closed, collapsed, or expanded.',
|
|
81
|
+
}),
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
}),
|
|
85
|
+
output: S.Void,
|
|
86
|
+
}) {}
|
|
87
|
+
|
|
88
|
+
export class UpdateDialog extends S.TaggedClass<UpdateDialog>()(UPDATE_LAYOUT, {
|
|
89
|
+
input: S.Struct({
|
|
90
|
+
part: S.Literal('dialog').annotations({ description: 'Updating the dialog.' }),
|
|
91
|
+
subject: S.optional(S.String.annotations({ description: 'URI of the component to display in the dialog.' })),
|
|
92
|
+
options: S.Struct({
|
|
93
|
+
state: S.optional(S.Boolean.annotations({ description: 'Whether the dialog is open or closed.' })),
|
|
94
|
+
blockAlign: S.optional(
|
|
95
|
+
S.Literal('start', 'center').annotations({ description: 'The alignment of the dialog.' }),
|
|
96
|
+
),
|
|
97
|
+
type: S.optional(S.Literal('default', 'alert').annotations({ description: 'The type of dialog.' })),
|
|
98
|
+
props: S.optional(
|
|
99
|
+
S.Record({ key: S.String, value: S.Any }).annotations({
|
|
100
|
+
description: 'Additional props for the dialog.',
|
|
101
|
+
}),
|
|
102
|
+
),
|
|
140
103
|
}),
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
}),
|
|
105
|
+
output: S.Void,
|
|
106
|
+
}) {}
|
|
107
|
+
|
|
108
|
+
export class UpdatePopover extends S.TaggedClass<UpdatePopover>()(UPDATE_LAYOUT, {
|
|
109
|
+
input: S.Struct({
|
|
110
|
+
part: S.Literal('popover').annotations({ description: 'Updating the popover.' }),
|
|
111
|
+
subject: S.optional(S.String.annotations({ description: 'URI of the component to display in the popover.' })),
|
|
112
|
+
options: S.Struct({
|
|
113
|
+
anchorId: S.String.annotations({ description: 'The id of the element to anchor the popover to.' }),
|
|
114
|
+
state: S.optional(S.Boolean.annotations({ description: 'Whether the popover is open or closed.' })),
|
|
115
|
+
props: S.optional(
|
|
116
|
+
S.Record({ key: S.String, value: S.Any }).annotations({
|
|
117
|
+
description: 'Additional props for the popover.',
|
|
118
|
+
}),
|
|
119
|
+
),
|
|
143
120
|
}),
|
|
121
|
+
}),
|
|
122
|
+
output: S.Void,
|
|
123
|
+
}) {}
|
|
124
|
+
|
|
125
|
+
export const Toast = S.Struct({
|
|
126
|
+
id: S.String.annotations({ description: 'The id of the toast.' }),
|
|
127
|
+
title: S.optional(Label.annotations({ description: 'The title of the toast.' })),
|
|
128
|
+
description: S.optional(Label.annotations({ description: 'The description of the toast.' })),
|
|
129
|
+
icon: S.optional(S.String.annotations({ description: 'The icon of the toast.' })),
|
|
130
|
+
duration: S.optional(S.Number.annotations({ description: 'The duration of the toast.' })),
|
|
131
|
+
closeLabel: S.optional(Label.annotations({ description: 'The label of the close button.' })),
|
|
132
|
+
actionLabel: S.optional(Label.annotations({ description: 'The label of the action button.' })),
|
|
133
|
+
actionAlt: S.optional(Label.annotations({ description: 'The alt text of the action button.' })),
|
|
134
|
+
onAction: S.optional(
|
|
135
|
+
S.Any.annotations({ description: 'The action to perform when the action button is clicked.' }),
|
|
144
136
|
),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
export interface Toast extends Omit<S.Schema.Type<typeof Toast>, 'onAction'> {
|
|
140
|
+
onAction?: () => void;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export class AddToast extends S.TaggedClass<AddToast>()(UPDATE_LAYOUT, {
|
|
144
|
+
input: S.Struct({
|
|
145
|
+
part: S.Literal('toast').annotations({ description: 'Adding a toast.' }),
|
|
146
|
+
subject: Toast.annotations({ description: 'The toast to add.' }),
|
|
147
|
+
}),
|
|
148
|
+
output: S.Void,
|
|
149
|
+
}) {}
|
|
150
|
+
|
|
151
|
+
export class SwitchWorkspace extends S.TaggedClass<SwitchWorkspace>()(UPDATE_LAYOUT, {
|
|
152
|
+
input: S.Struct({
|
|
153
|
+
part: S.Literal('workspace').annotations({ description: 'Switching the workspace.' }),
|
|
154
|
+
subject: S.String.annotations({ description: 'The id of the workspace to switch to.' }),
|
|
155
|
+
}),
|
|
156
|
+
output: S.Void,
|
|
157
|
+
}) {}
|
|
158
|
+
|
|
159
|
+
export class Open extends S.TaggedClass<Open>()(UPDATE_LAYOUT, {
|
|
160
|
+
input: S.Struct({
|
|
161
|
+
part: S.Literal('main').annotations({ description: 'Opening an item in the main content area.' }),
|
|
162
|
+
subject: S.Array(S.String.annotations({ description: 'Ids of the items to open.' })),
|
|
163
|
+
options: S.optional(
|
|
164
|
+
S.Struct({
|
|
165
|
+
state: S.optional(S.Literal(true).annotations({ description: 'The items are being added.' })),
|
|
166
|
+
key: S.optional(
|
|
167
|
+
S.String.annotations({ description: 'If provided, will replace item with a matching key (id prefix).' }),
|
|
168
|
+
),
|
|
169
|
+
scrollIntoView: S.optional(S.Boolean.annotations({ description: 'Scroll the items into view.' })),
|
|
170
|
+
pivotId: S.optional(S.String.annotations({ description: 'The id of the item to place new items next to.' })),
|
|
171
|
+
positioning: S.optional(
|
|
172
|
+
S.Union(
|
|
173
|
+
S.Literal('start').annotations({ description: 'The items are being added before the pivot item.' }),
|
|
174
|
+
S.Literal('end').annotations({ description: 'The items are being added after the pivot item.' }),
|
|
175
|
+
),
|
|
176
|
+
),
|
|
177
|
+
}),
|
|
178
|
+
),
|
|
179
|
+
}),
|
|
180
|
+
output: S.Void,
|
|
181
|
+
}) {}
|
|
182
|
+
|
|
183
|
+
export class Set extends S.TaggedClass<Set>()(UPDATE_LAYOUT, {
|
|
184
|
+
input: S.Struct({
|
|
185
|
+
part: S.Literal('main').annotations({ description: 'Setting items in the main content area.' }),
|
|
186
|
+
subject: S.Array(S.String.annotations({ description: 'Ids of the items to set.' })),
|
|
187
|
+
options: S.Struct({
|
|
188
|
+
override: S.Literal(true).annotations({ description: 'Override the current items in the main content area.' }),
|
|
189
|
+
}),
|
|
190
|
+
}),
|
|
191
|
+
output: S.Void,
|
|
192
|
+
}) {}
|
|
193
|
+
|
|
194
|
+
export class Close extends S.TaggedClass<Close>()(UPDATE_LAYOUT, {
|
|
195
|
+
input: S.Struct({
|
|
196
|
+
part: S.Literal('main').annotations({ description: 'Closing items in the main content area.' }),
|
|
197
|
+
subject: S.Array(S.String.annotations({ description: 'Ids of the items to close.' })),
|
|
198
|
+
options: S.Struct({
|
|
199
|
+
state: S.Literal(false).annotations({ description: 'The items are being removed.' }),
|
|
200
|
+
}),
|
|
201
|
+
}),
|
|
202
|
+
output: S.Void,
|
|
203
|
+
}) {}
|
|
204
|
+
|
|
205
|
+
export class ScrollIntoView extends S.TaggedClass<ScrollIntoView>()(UPDATE_LAYOUT, {
|
|
206
|
+
input: S.Struct({
|
|
207
|
+
part: S.Literal('current').annotations({ description: 'Setting the current item' }),
|
|
208
|
+
subject: S.optional(S.String.annotations({ description: 'The id of the item to set as current.' })),
|
|
209
|
+
options: S.optional(
|
|
210
|
+
S.Record({ key: S.String, value: S.Any }).annotations({
|
|
211
|
+
description: 'Additional options for the scroll into view.',
|
|
212
|
+
}),
|
|
213
|
+
),
|
|
214
|
+
}),
|
|
145
215
|
output: S.Void,
|
|
146
216
|
}) {}
|
|
147
217
|
|
|
148
|
-
export class
|
|
218
|
+
export class Expose extends S.TaggedClass<Expose>()(UPDATE_LAYOUT, {
|
|
149
219
|
input: S.Struct({
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
cursor: S.optional(S.String),
|
|
153
|
-
ref: S.optional(S.String),
|
|
220
|
+
part: S.Literal('navigation').annotations({ description: 'Exposing an item in the navigation area.' }),
|
|
221
|
+
subject: S.String.annotations({ description: 'The id of the item to expose.' }),
|
|
154
222
|
}),
|
|
155
223
|
output: S.Void,
|
|
156
224
|
}) {}
|
package/src/common/surface.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type JSX, type ForwardedRef, type PropsWithChildren, type ReactNode } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type GuardedType, type MakeOptional, type
|
|
7
|
+
import { type GuardedType, type MakeOptional, type Position } from '@dxos/util';
|
|
8
8
|
|
|
9
9
|
import { type ErrorBoundary } from '../react';
|
|
10
10
|
|
|
@@ -73,7 +73,7 @@ export type SurfaceComponent<T extends Record<string, any> = Record<string, unkn
|
|
|
73
73
|
export type SurfaceDefinition<T extends Record<string, any> = any> = Readonly<{
|
|
74
74
|
id: string;
|
|
75
75
|
role: string | string[];
|
|
76
|
-
|
|
76
|
+
position?: Position;
|
|
77
77
|
filter?: (data: Record<string, unknown>) => data is T;
|
|
78
78
|
component: SurfaceComponent<GuardedType<SurfaceDefinition<T>['filter']>>;
|
|
79
79
|
}>;
|
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Schema as S } from '@effect/schema';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
export type ResourceKey = z.infer<typeof ResourceKey>;
|
|
7
|
+
export const ResourceKey = S.Union(S.String, S.Record({ key: S.String, value: S.Any }));
|
|
8
|
+
export type ResourceKey = S.Schema.Type<typeof ResourceKey>;
|
|
10
9
|
|
|
11
|
-
export const ResourceLanguage =
|
|
12
|
-
export type ResourceLanguage =
|
|
10
|
+
export const ResourceLanguage = S.Record({ key: S.String, value: ResourceKey });
|
|
11
|
+
export type ResourceLanguage = S.Schema.Type<typeof ResourceLanguage>;
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* A resource is a collection of translations for a language.
|
|
16
15
|
*/
|
|
17
|
-
export const Resource =
|
|
18
|
-
export type Resource =
|
|
16
|
+
export const Resource = S.Record({ key: S.String, value: ResourceLanguage });
|
|
17
|
+
export type Resource = S.Schema.Type<typeof Resource>;
|