@dxos/app-framework 0.8.4-main.d05539e30a → 0.8.4-main.d9fc60f731
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/{capability-K5XIVCQU.mjs → capability-JCUNWZ66.mjs} +7 -7
- package/dist/lib/browser/{chunk-5AHASNDW.mjs → chunk-2ZIYRDAF.mjs} +1 -1
- package/dist/lib/browser/{chunk-5AHASNDW.mjs.map → chunk-2ZIYRDAF.mjs.map} +1 -1
- package/dist/lib/browser/{chunk-QLML5QFJ.mjs → chunk-7MBI66M7.mjs} +12 -6
- package/dist/lib/browser/chunk-7MBI66M7.mjs.map +7 -0
- package/dist/lib/browser/{chunk-VJ5PFAWC.mjs → chunk-BMLCIFZW.mjs} +37 -16
- package/dist/lib/browser/chunk-BMLCIFZW.mjs.map +7 -0
- package/dist/lib/browser/{chunk-SLX73WRZ.mjs → chunk-DC3WRPBV.mjs} +14 -5
- package/dist/lib/browser/chunk-DC3WRPBV.mjs.map +7 -0
- package/dist/lib/browser/{chunk-BRK6GYNB.mjs → chunk-E5DTDNDY.mjs} +20 -19
- package/dist/lib/browser/chunk-E5DTDNDY.mjs.map +7 -0
- package/dist/lib/browser/{chunk-KLHQNYJ2.mjs → chunk-KZUDO43J.mjs} +2 -2
- package/dist/lib/browser/chunk-Q2GLJTVV.mjs +12 -0
- package/dist/lib/browser/chunk-Q2GLJTVV.mjs.map +7 -0
- package/dist/lib/browser/{chunk-UVTGHZQF.mjs → chunk-VBG5F5UF.mjs} +51 -88
- package/dist/lib/browser/chunk-VBG5F5UF.mjs.map +7 -0
- package/dist/lib/browser/cli/index.mjs +5 -5
- package/dist/lib/browser/common/activation-events.mjs +3 -3
- package/dist/lib/browser/common/capabilities.mjs +4 -4
- package/dist/lib/browser/core/plugin-manager.mjs +2 -2
- package/dist/lib/browser/core/plugin.mjs +3 -1
- package/dist/lib/browser/core/url-loader.mjs +2 -2
- package/dist/lib/browser/index.mjs +13 -14
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{process-manager-capability-JIWLN7SU.mjs → process-manager-capability-4FUA4TRP.mjs} +6 -6
- package/dist/lib/browser/testing/index.mjs +11 -10
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/testing/react.mjs +6 -6
- package/dist/lib/browser/ui/index.mjs +7 -9
- package/dist/lib/node-esm/{capability-RLKFFLTB.mjs → capability-7TCQK7ZP.mjs} +7 -7
- package/dist/lib/node-esm/{chunk-SFYCO3PT.mjs → chunk-4BX2AE47.mjs} +37 -16
- package/dist/lib/node-esm/chunk-4BX2AE47.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-FPW45EZH.mjs +14 -0
- package/dist/lib/node-esm/chunk-FPW45EZH.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-LJNUFNDO.mjs → chunk-GEWHMHM7.mjs} +12 -6
- package/dist/lib/node-esm/chunk-GEWHMHM7.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-BYHYYJZH.mjs → chunk-KSH4OVMY.mjs} +20 -19
- package/dist/lib/node-esm/chunk-KSH4OVMY.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-PW2VYGOS.mjs → chunk-QBUGLR4H.mjs} +1 -1
- package/dist/lib/node-esm/{chunk-PW2VYGOS.mjs.map → chunk-QBUGLR4H.mjs.map} +1 -1
- package/dist/lib/node-esm/{chunk-42UNAKYO.mjs → chunk-XGBUKRCK.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-6S45OMUP.mjs → chunk-XIGPWXCQ.mjs} +14 -5
- package/dist/lib/node-esm/chunk-XIGPWXCQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-JNT72ZCN.mjs → chunk-Y7JKXNDU.mjs} +51 -88
- package/dist/lib/node-esm/chunk-Y7JKXNDU.mjs.map +7 -0
- package/dist/lib/node-esm/cli/index.mjs +5 -5
- package/dist/lib/node-esm/common/activation-events.mjs +3 -3
- package/dist/lib/node-esm/common/capabilities.mjs +4 -4
- package/dist/lib/node-esm/core/plugin-manager.mjs +2 -2
- package/dist/lib/node-esm/core/plugin.mjs +3 -1
- package/dist/lib/node-esm/core/url-loader.mjs +2 -2
- package/dist/lib/node-esm/index.mjs +13 -14
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{process-manager-capability-PHKLO2BL.mjs → process-manager-capability-F23D62RN.mjs} +6 -6
- package/dist/lib/node-esm/testing/index.mjs +11 -10
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/testing/react.mjs +6 -6
- package/dist/lib/node-esm/ui/index.mjs +7 -9
- package/dist/plugin/node-esm/index.mjs +155 -77
- package/dist/plugin/node-esm/index.mjs.map +4 -4
- package/dist/plugin/node-esm/meta.json +1 -1
- package/dist/types/src/common/capabilities.d.ts +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/translations.d.ts +1 -1
- package/dist/types/src/core/capability-manager.d.ts +4 -0
- package/dist/types/src/core/capability-manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin.d.ts +33 -6
- package/dist/types/src/core/plugin.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/history/history-tracker.d.ts +15 -1
- package/dist/types/src/plugin-process-manager/history/history-tracker.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/history/undo-mapping.d.ts +1 -1
- package/dist/types/src/plugin-process-manager/history/undo-mapping.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/meta.d.ts +1 -1
- package/dist/types/src/plugin-process-manager/meta.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/testing.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/App.d.ts +34 -2
- package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
- package/dist/types/src/ui/components/index.d.ts +0 -1
- package/dist/types/src/ui/components/index.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useApp.d.ts +1 -7
- package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts +6 -0
- package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +26 -23
- package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -35
- package/src/common/capabilities.ts +1 -1
- package/src/common/index.ts +0 -1
- package/src/common/translations.ts +1 -1
- package/src/core/capability-manager.test.ts +3 -0
- package/src/core/capability-manager.ts +22 -1
- package/src/core/plugin-manager.test.ts +51 -21
- package/src/core/plugin-manager.ts +10 -5
- package/src/core/plugin.ts +44 -8
- package/src/core/url-loader.test.ts +2 -1
- package/src/plugin-process-manager/history/history-tracker.test.ts +39 -60
- package/src/plugin-process-manager/history/history-tracker.ts +32 -16
- package/src/plugin-process-manager/history/undo-mapping.ts +1 -1
- package/src/plugin-process-manager/history/undo-registry.test.ts +3 -1
- package/src/plugin-process-manager/meta.ts +6 -4
- package/src/plugin-process-manager/testing.ts +6 -5
- package/src/testing/react.test.tsx +3 -1
- package/src/testing/withPluginManager.tsx +4 -3
- package/src/ui/components/App/App.stories.tsx +9 -11
- package/src/ui/components/App/App.tsx +80 -17
- package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +9 -14
- package/src/ui/components/Surface/SurfaceComponent.stories.tsx +1 -1
- package/src/ui/components/Surface/SurfaceComponent.tsx +1 -1
- package/src/ui/components/Surface/types.test.ts +2 -2
- package/src/ui/components/Surface/types.ts +21 -0
- package/src/ui/components/index.ts +0 -1
- package/src/ui/hooks/useApp.test.tsx +3 -1
- package/src/ui/hooks/useApp.tsx +4 -18
- package/src/ui/hooks/useCapabilities.ts +1 -0
- package/src/ui/hooks/useProcessManagerRuntime.ts +12 -2
- package/src/vite-plugin/boot-loader/BootLoader.solid-stories.tsx +198 -0
- package/src/vite-plugin/boot-loader/loader-app/Loader.tsx +166 -0
- package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +311 -0
- package/src/vite-plugin/boot-loader/loader-app/bridge.ts +25 -0
- package/src/vite-plugin/boot-loader/loader-app/entry.tsx +21 -0
- package/src/vite-plugin/boot-loader/loader-app/mount.tsx +70 -0
- package/src/vite-plugin/boot-loader/loader-app/store.test.ts +137 -0
- package/src/vite-plugin/boot-loader/loader-app/store.ts +149 -0
- package/src/vite-plugin/boot-loader/loader-app/tsconfig.json +11 -0
- package/src/vite-plugin/boot-loader/loader-app/types.ts +78 -0
- package/src/vite-plugin/boot-loader/loader.ts +133 -63
- package/src/vite-plugin/manifest.test.ts +19 -10
- package/src/vite-plugin/manifest.ts +9 -3
- package/tsconfig.json +6 -7
- package/dist/lib/browser/chunk-BRK6GYNB.mjs.map +0 -7
- package/dist/lib/browser/chunk-KFDF7KR3.mjs +0 -42
- package/dist/lib/browser/chunk-KFDF7KR3.mjs.map +0 -7
- package/dist/lib/browser/chunk-QLML5QFJ.mjs.map +0 -7
- package/dist/lib/browser/chunk-SLX73WRZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-UVTGHZQF.mjs.map +0 -7
- package/dist/lib/browser/chunk-VJ5PFAWC.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-6S45OMUP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-BYHYYJZH.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JNT72ZCN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-LJNUFNDO.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-OUEMWPIW.mjs +0 -43
- package/dist/lib/node-esm/chunk-OUEMWPIW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SFYCO3PT.mjs.map +0 -7
- package/dist/types/src/common/operations.d.ts +0 -19
- package/dist/types/src/common/operations.d.ts.map +0 -1
- package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts +0 -64
- package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts.map +0 -1
- package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts +0 -19
- package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts.map +0 -1
- package/dist/types/src/ui/components/Placeholder/index.d.ts +0 -2
- package/dist/types/src/ui/components/Placeholder/index.d.ts.map +0 -1
- package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +0 -34
- package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +0 -1
- package/src/common/operations.ts +0 -35
- package/src/ui/components/Placeholder/Placeholder.stories.tsx +0 -77
- package/src/ui/components/Placeholder/Placeholder.tsx +0 -155
- package/src/ui/components/Placeholder/index.ts +0 -5
- package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +0 -270
- package/src/vite-plugin/boot-loader/boot-loader.css +0 -320
- package/src/vite-plugin/boot-loader/boot-loader.js +0 -325
- /package/dist/lib/browser/{capability-K5XIVCQU.mjs.map → capability-JCUNWZ66.mjs.map} +0 -0
- /package/dist/lib/browser/{chunk-KLHQNYJ2.mjs.map → chunk-KZUDO43J.mjs.map} +0 -0
- /package/dist/lib/browser/{process-manager-capability-JIWLN7SU.mjs.map → process-manager-capability-4FUA4TRP.mjs.map} +0 -0
- /package/dist/lib/node-esm/{capability-RLKFFLTB.mjs.map → capability-7TCQK7ZP.mjs.map} +0 -0
- /package/dist/lib/node-esm/{chunk-42UNAKYO.mjs.map → chunk-XGBUKRCK.mjs.map} +0 -0
- /package/dist/lib/node-esm/{process-manager-capability-PHKLO2BL.mjs.map → process-manager-capability-F23D62RN.mjs.map} +0 -0
|
@@ -7,6 +7,8 @@ import * as Effect from 'effect/Effect';
|
|
|
7
7
|
import * as Fiber from 'effect/Fiber';
|
|
8
8
|
import * as Layer from 'effect/Layer';
|
|
9
9
|
import * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
10
|
+
import * as PubSub from 'effect/PubSub';
|
|
11
|
+
import * as Queue from 'effect/Queue';
|
|
10
12
|
import * as TestClock from 'effect/TestClock';
|
|
11
13
|
import { describe, expect } from 'vitest';
|
|
12
14
|
|
|
@@ -15,7 +17,6 @@ import { OperationInvoker } from '@dxos/operation';
|
|
|
15
17
|
|
|
16
18
|
const testRuntime = ManagedRuntime.make(Layer.empty) as unknown as ManagedRuntime.ManagedRuntime<any, any>;
|
|
17
19
|
|
|
18
|
-
import { UndoOperation } from '../../common';
|
|
19
20
|
import {
|
|
20
21
|
Compute,
|
|
21
22
|
HalveCompute,
|
|
@@ -66,7 +67,7 @@ describe('HistoryTracker', () => {
|
|
|
66
67
|
|
|
67
68
|
yield* invoker.invoke(ToString, { value: 42 });
|
|
68
69
|
|
|
69
|
-
// Wait for
|
|
70
|
+
// Wait for the pending + success events to be processed.
|
|
70
71
|
yield* collector.waitForEvents(1);
|
|
71
72
|
|
|
72
73
|
// Without an undo mapping, canUndo should remain false.
|
|
@@ -123,7 +124,7 @@ describe('HistoryTracker', () => {
|
|
|
123
124
|
const tracker = HistoryTracker.make(invoker, undoRegistry);
|
|
124
125
|
const collector = yield* createEventCollector(invoker);
|
|
125
126
|
|
|
126
|
-
// Fork compute operation (
|
|
127
|
+
// Fork compute operation (emits one success event).
|
|
127
128
|
const fiber = yield* Effect.fork(invoker.invoke(Compute, { value: 2 }));
|
|
128
129
|
yield* TestClock.adjust('20 millis');
|
|
129
130
|
yield* Fiber.join(fiber);
|
|
@@ -134,7 +135,7 @@ describe('HistoryTracker', () => {
|
|
|
134
135
|
// Wait until the tracker has processed the event.
|
|
135
136
|
yield* waitUntil(() => tracker.canUndo());
|
|
136
137
|
|
|
137
|
-
// Undo (should NOT emit
|
|
138
|
+
// Undo (should NOT emit events because it uses _invokeCore).
|
|
138
139
|
yield* tracker.undo();
|
|
139
140
|
|
|
140
141
|
// Verify no new events were emitted.
|
|
@@ -173,7 +174,7 @@ describe('HistoryTracker', () => {
|
|
|
173
174
|
yield* TestClock.adjust('30 millis');
|
|
174
175
|
yield* Fiber.join(fiber2);
|
|
175
176
|
|
|
176
|
-
//
|
|
177
|
+
// Each invoke emits one success event.
|
|
177
178
|
yield* collector.waitForEvents(2);
|
|
178
179
|
|
|
179
180
|
// First undo should halve 6 (the output of the second compute).
|
|
@@ -193,8 +194,10 @@ describe('HistoryTracker', () => {
|
|
|
193
194
|
it.effect('undo on empty history returns error', () =>
|
|
194
195
|
Effect.gen(function* () {
|
|
195
196
|
const invoker = OperationInvoker.make(() => Effect.succeed([]), testRuntime);
|
|
196
|
-
const
|
|
197
|
-
|
|
197
|
+
const tracker = HistoryTracker.make(
|
|
198
|
+
invoker,
|
|
199
|
+
UndoRegistry.make(() => []),
|
|
200
|
+
);
|
|
198
201
|
|
|
199
202
|
const result = yield* tracker.undo().pipe(Effect.either);
|
|
200
203
|
expect(result._tag).toBe('Left');
|
|
@@ -204,7 +207,7 @@ describe('HistoryTracker', () => {
|
|
|
204
207
|
}),
|
|
205
208
|
);
|
|
206
209
|
|
|
207
|
-
it.effect('
|
|
210
|
+
it.effect('publishes an undoable event with the resolved message', () =>
|
|
208
211
|
Effect.gen(function* () {
|
|
209
212
|
const testMessage: [string, { ns: string }] = ['test-undo.message', { ns: 'test' }];
|
|
210
213
|
const undoMapping = UndoMapping.make({
|
|
@@ -214,39 +217,28 @@ describe('HistoryTracker', () => {
|
|
|
214
217
|
message: testMessage,
|
|
215
218
|
});
|
|
216
219
|
|
|
217
|
-
|
|
218
|
-
let showUndoMessage: unknown = undefined;
|
|
219
|
-
const showUndoHandler = Operation.withHandler(UndoOperation.ShowUndo, (input) => {
|
|
220
|
-
showUndoWasCalled = true;
|
|
221
|
-
showUndoMessage = input.message;
|
|
222
|
-
return Effect.succeed(undefined);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const invoker = OperationInvoker.make(
|
|
226
|
-
() => Effect.succeed([computeHandler, halveComputeHandler, showUndoHandler]),
|
|
227
|
-
testRuntime,
|
|
228
|
-
);
|
|
220
|
+
const invoker = OperationInvoker.make(() => Effect.succeed([computeHandler, halveComputeHandler]), testRuntime);
|
|
229
221
|
const undoRegistry = UndoRegistry.make(() => [undoMapping]);
|
|
230
222
|
const tracker = HistoryTracker.make(invoker, undoRegistry);
|
|
231
223
|
|
|
232
|
-
|
|
224
|
+
yield* Effect.scoped(
|
|
225
|
+
Effect.gen(function* () {
|
|
226
|
+
const undoables = yield* PubSub.subscribe(tracker.undoable);
|
|
233
227
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// Wait until the tracker has processed the event.
|
|
240
|
-
yield* waitUntil(() => tracker.canUndo());
|
|
228
|
+
// Fork compute operation and advance clock.
|
|
229
|
+
const fiber = yield* Effect.fork(invoker.invoke(Compute, { value: 2 }));
|
|
230
|
+
yield* TestClock.adjust('20 millis');
|
|
231
|
+
yield* Fiber.join(fiber);
|
|
241
232
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
233
|
+
const undoable = yield* Queue.take(undoables);
|
|
234
|
+
expect(undoable.message).toEqual(testMessage);
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
237
|
+
expect(tracker.canUndo()).toBe(true);
|
|
246
238
|
}),
|
|
247
239
|
);
|
|
248
240
|
|
|
249
|
-
it.effect('
|
|
241
|
+
it.effect('undo message can be derived from input and output', () =>
|
|
250
242
|
Effect.gen(function* () {
|
|
251
243
|
// Dynamic message that depends on input/output.
|
|
252
244
|
const dynamicMessage = (input: { value: number }, output: { value: number }): [string, { ns: string }] => [
|
|
@@ -260,36 +252,23 @@ describe('HistoryTracker', () => {
|
|
|
260
252
|
message: dynamicMessage,
|
|
261
253
|
});
|
|
262
254
|
|
|
263
|
-
|
|
264
|
-
let showUndoMessage: unknown = undefined;
|
|
265
|
-
const showUndoHandler = Operation.withHandler(UndoOperation.ShowUndo, (input) => {
|
|
266
|
-
showUndoWasCalled = true;
|
|
267
|
-
showUndoMessage = input.message;
|
|
268
|
-
return Effect.succeed(undefined);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const invoker = OperationInvoker.make(
|
|
272
|
-
() => Effect.succeed([computeHandler, halveComputeHandler, showUndoHandler]),
|
|
273
|
-
testRuntime,
|
|
274
|
-
);
|
|
255
|
+
const invoker = OperationInvoker.make(() => Effect.succeed([computeHandler, halveComputeHandler]), testRuntime);
|
|
275
256
|
const undoRegistry = UndoRegistry.make(() => [undoMapping]);
|
|
276
257
|
const tracker = HistoryTracker.make(invoker, undoRegistry);
|
|
277
258
|
|
|
278
|
-
|
|
259
|
+
yield* Effect.scoped(
|
|
260
|
+
Effect.gen(function* () {
|
|
261
|
+
const undoables = yield* PubSub.subscribe(tracker.undoable);
|
|
279
262
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// Wait until the tracker has processed the event.
|
|
286
|
-
yield* waitUntil(() => tracker.canUndo());
|
|
263
|
+
// Fork compute operation and advance clock (2 * 2 = 4).
|
|
264
|
+
const fiber = yield* Effect.fork(invoker.invoke(Compute, { value: 2 }));
|
|
265
|
+
yield* TestClock.adjust('20 millis');
|
|
266
|
+
yield* Fiber.join(fiber);
|
|
287
267
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
expect(showUndoMessage).toEqual(['computed-2-to-4', { ns: 'test' }]);
|
|
268
|
+
const undoable = yield* Queue.take(undoables);
|
|
269
|
+
expect(undoable.message).toEqual(['computed-2-to-4', { ns: 'test' }]);
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
293
272
|
}),
|
|
294
273
|
);
|
|
295
274
|
|
|
@@ -320,7 +299,7 @@ describe('HistoryTracker', () => {
|
|
|
320
299
|
yield* TestClock.adjust('20 millis');
|
|
321
300
|
yield* Fiber.join(fiber);
|
|
322
301
|
|
|
323
|
-
// Wait for
|
|
302
|
+
// Wait for the pending + success events to be processed.
|
|
324
303
|
yield* collector.waitForEvents(1);
|
|
325
304
|
|
|
326
305
|
// Even though there's an undo mapping, deriveContext returned undefined,
|
|
@@ -359,7 +338,7 @@ describe('HistoryTracker', () => {
|
|
|
359
338
|
yield* TestClock.adjust('50 millis');
|
|
360
339
|
yield* Fiber.join(fiber);
|
|
361
340
|
|
|
362
|
-
// Wait for
|
|
341
|
+
// Wait for the pending + success events to be processed.
|
|
363
342
|
yield* collector.waitForEvents(1);
|
|
364
343
|
|
|
365
344
|
// Wait until the tracker has processed the event.
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as PubSub from 'effect/PubSub';
|
|
6
7
|
import * as Stream from 'effect/Stream';
|
|
7
8
|
|
|
8
9
|
import { runAndForwardErrors } from '@dxos/effect';
|
|
9
10
|
import { log } from '@dxos/log';
|
|
10
11
|
import { OperationInvoker } from '@dxos/operation';
|
|
11
12
|
|
|
12
|
-
import {
|
|
13
|
+
import { type Label } from '../../common';
|
|
13
14
|
import { EmptyHistoryError } from './errors';
|
|
14
15
|
import type { HistoryEntry } from './types';
|
|
15
16
|
import { resolveMessage } from './undo-mapping';
|
|
@@ -21,6 +22,15 @@ const HISTORY_LIMIT = 100;
|
|
|
21
22
|
// Public Interface
|
|
22
23
|
//
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Emitted when a new undoable action is recorded; consumed by the UI to surface an undo affordance
|
|
27
|
+
* (e.g. the deck notification tracker's undo toast).
|
|
28
|
+
*/
|
|
29
|
+
export type UndoableEvent = {
|
|
30
|
+
/** Message describing the undoable action. */
|
|
31
|
+
message?: Label;
|
|
32
|
+
};
|
|
33
|
+
|
|
24
34
|
/**
|
|
25
35
|
* HistoryTracker interface - tracks operation history and provides undo.
|
|
26
36
|
*/
|
|
@@ -28,6 +38,8 @@ export interface HistoryTracker {
|
|
|
28
38
|
undo: () => Effect.Effect<void, Error>;
|
|
29
39
|
undoPromise: () => Promise<{ error?: Error }>;
|
|
30
40
|
canUndo: () => boolean;
|
|
41
|
+
/** Stream of undoable actions, published as they are recorded. */
|
|
42
|
+
undoable: PubSub.PubSub<UndoableEvent>;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
//
|
|
@@ -35,27 +47,29 @@ export interface HistoryTracker {
|
|
|
35
47
|
//
|
|
36
48
|
|
|
37
49
|
/**
|
|
38
|
-
* Creates a HistoryTracker that subscribes to invocation events
|
|
50
|
+
* Creates a HistoryTracker that subscribes to (successful) invocation events, derives undoability from
|
|
51
|
+
* the undo registry, maintains the undo stack, and publishes an {@link UndoableEvent} for each undoable
|
|
52
|
+
* action so the UI can offer an undo affordance. The invoker itself is undo-agnostic.
|
|
39
53
|
*/
|
|
40
54
|
export const make = (
|
|
41
55
|
invoker: OperationInvoker.OperationInvokerInternal,
|
|
42
56
|
undoRegistry: UndoRegistry,
|
|
43
57
|
): HistoryTracker => {
|
|
44
58
|
const history: HistoryEntry[] = [];
|
|
59
|
+
const undoable = Effect.runSync(PubSub.unbounded<UndoableEvent>());
|
|
45
60
|
|
|
46
|
-
//
|
|
47
|
-
const
|
|
61
|
+
// Record an invocation; returns the undoable event to publish, or undefined if not undoable.
|
|
62
|
+
const recordInvocation = (event: OperationInvoker.InvocationEvent): UndoableEvent | undefined => {
|
|
48
63
|
const mapping = undoRegistry.lookup(event.operation);
|
|
49
64
|
if (!mapping) {
|
|
50
|
-
// Operation is not undoable
|
|
51
|
-
return;
|
|
65
|
+
// Operation is not undoable.
|
|
66
|
+
return undefined;
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
const inverseInput = mapping.deriveContext(event.input, event.output);
|
|
55
70
|
if (inverseInput === undefined) {
|
|
56
71
|
// Operation is conditionally not undoable (deriveContext returned undefined).
|
|
57
|
-
|
|
58
|
-
return;
|
|
72
|
+
return undefined;
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
const entry: HistoryEntry = {
|
|
@@ -75,19 +89,20 @@ export const make = (
|
|
|
75
89
|
history.splice(0, history.length - HISTORY_LIMIT);
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
const resolvedMessage = resolveMessage(mapping.message, event.input, event.output);
|
|
80
|
-
Effect.runFork(
|
|
81
|
-
invoker.invoke(UndoOperation.ShowUndo, {
|
|
82
|
-
message: resolvedMessage,
|
|
83
|
-
}),
|
|
84
|
-
);
|
|
92
|
+
return { message: resolveMessage(mapping.message, event.input, event.output) };
|
|
85
93
|
};
|
|
86
94
|
|
|
87
95
|
// Fork a fiber to consume the invocation stream.
|
|
88
96
|
Effect.runFork(
|
|
89
97
|
Stream.fromPubSub(invoker.invocations).pipe(
|
|
90
|
-
Stream.runForEach((event) =>
|
|
98
|
+
Stream.runForEach((event) =>
|
|
99
|
+
Effect.gen(function* () {
|
|
100
|
+
const undoableEvent = recordInvocation(event);
|
|
101
|
+
if (undoableEvent) {
|
|
102
|
+
yield* PubSub.publish(undoable, undoableEvent);
|
|
103
|
+
}
|
|
104
|
+
}),
|
|
105
|
+
),
|
|
91
106
|
),
|
|
92
107
|
);
|
|
93
108
|
|
|
@@ -124,5 +139,6 @@ export const make = (
|
|
|
124
139
|
undo,
|
|
125
140
|
undoPromise,
|
|
126
141
|
canUndo,
|
|
142
|
+
undoable,
|
|
127
143
|
};
|
|
128
144
|
};
|
|
@@ -7,7 +7,7 @@ import type { Operation } from '@dxos/compute';
|
|
|
7
7
|
/**
|
|
8
8
|
* Label type for translatable text (canonical definition in @dxos/app-toolkit).
|
|
9
9
|
*/
|
|
10
|
-
type Label = string | [string, { ns: string; count?: number; defaultValue?: string }];
|
|
10
|
+
type Label = string | [string, { ns: string | readonly string[]; count?: number; defaultValue?: string }];
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Extract the input type from an OperationDefinition.
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { DXN } from '@dxos/keys';
|
|
8
|
+
|
|
7
9
|
import { Compute, HalveCompute } from '../testing';
|
|
8
10
|
import * as UndoMapping from './undo-mapping';
|
|
9
11
|
import * as UndoRegistry from './undo-registry';
|
|
@@ -19,7 +21,7 @@ describe('UndoRegistry', () => {
|
|
|
19
21
|
|
|
20
22
|
const result = registry.lookup(Compute);
|
|
21
23
|
expect(result).not.toBe(undefined);
|
|
22
|
-
expect(result?.inverse.meta.key).toBe('test.
|
|
24
|
+
expect(result?.inverse.meta.key).toBe(DXN.make('org.dxos.test.halveCompute'));
|
|
23
25
|
});
|
|
24
26
|
|
|
25
27
|
test('returns undefined for unmapped operations', ({ expect }) => {
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { DXN } from '@dxos/keys';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { Plugin } from '../core';
|
|
8
|
+
|
|
9
|
+
export const meta = Plugin.makeMeta({
|
|
10
|
+
key: DXN.make('org.dxos.plugin.processManager'),
|
|
9
11
|
name: 'Process Manager Plugin',
|
|
10
12
|
author: 'DXOS',
|
|
11
13
|
description:
|
|
12
14
|
'Hosts the process-manager runtime that composes contributed layer specs into a service resolver, exposes the operation invoker built on top of it, and registers undo/history tracking.',
|
|
13
15
|
tags: ['system'],
|
|
14
|
-
};
|
|
16
|
+
});
|
|
@@ -10,6 +10,7 @@ import * as Schema from 'effect/Schema';
|
|
|
10
10
|
import * as Stream from 'effect/Stream';
|
|
11
11
|
|
|
12
12
|
import { Operation } from '@dxos/compute';
|
|
13
|
+
import { DXN } from '@dxos/keys';
|
|
13
14
|
import { OperationInvoker } from '@dxos/operation';
|
|
14
15
|
|
|
15
16
|
//
|
|
@@ -19,31 +20,31 @@ import { OperationInvoker } from '@dxos/operation';
|
|
|
19
20
|
export const Compute = Operation.make({
|
|
20
21
|
input: Schema.Struct({ value: Schema.Number }),
|
|
21
22
|
output: Schema.Struct({ value: Schema.Number }),
|
|
22
|
-
meta: { key: 'test.compute' },
|
|
23
|
+
meta: { key: DXN.make('org.dxos.test.compute') },
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
export const HalveCompute = Operation.make({
|
|
26
27
|
input: Schema.Struct({ value: Schema.Number }),
|
|
27
28
|
output: Schema.Struct({ value: Schema.Number }),
|
|
28
|
-
meta: { key: 'test.
|
|
29
|
+
meta: { key: DXN.make('org.dxos.test.halveCompute') },
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
export const ToString = Operation.make({
|
|
32
33
|
input: Schema.Struct({ value: Schema.Number }),
|
|
33
34
|
output: Schema.Struct({ string: Schema.String }),
|
|
34
|
-
meta: { key: 'test.
|
|
35
|
+
meta: { key: DXN.make('org.dxos.test.toString') },
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
export const Add = Operation.make({
|
|
38
39
|
input: Schema.Tuple(Schema.Number, Schema.Number),
|
|
39
40
|
output: Schema.Number,
|
|
40
|
-
meta: { key: 'test.add' },
|
|
41
|
+
meta: { key: DXN.make('org.dxos.test.add') },
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
export const SideEffect = Operation.make({
|
|
44
45
|
input: Schema.Void,
|
|
45
46
|
output: Schema.Void,
|
|
46
|
-
meta: { key: 'test.
|
|
47
|
+
meta: { key: DXN.make('org.dxos.test.sideEffect') },
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
//
|
|
@@ -6,13 +6,15 @@ import * as Effect from 'effect/Effect';
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import { describe, test } from 'vitest';
|
|
8
8
|
|
|
9
|
+
import { DXN } from '@dxos/keys';
|
|
10
|
+
|
|
9
11
|
import { ActivationEvents, Capabilities } from '../common';
|
|
10
12
|
import { Capability, Plugin } from '../core';
|
|
11
13
|
import { Surface } from '../ui';
|
|
12
14
|
import { createTestApp } from './harness';
|
|
13
15
|
import { render, renderSurface } from './react';
|
|
14
16
|
|
|
15
|
-
const testMeta = {
|
|
17
|
+
const testMeta = Plugin.makeMeta({ key: DXN.make('org.dxos.plugin.test.reactHarness'), name: 'ReactHarnessTest' });
|
|
16
18
|
|
|
17
19
|
const TestPlugin = Plugin.define(testMeta).pipe(
|
|
18
20
|
Plugin.addModule({
|
|
@@ -8,6 +8,7 @@ import React, { useEffect, useState } from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { raise } from '@dxos/debug';
|
|
10
10
|
import { runAndForwardErrors } from '@dxos/effect';
|
|
11
|
+
import { DXN } from '@dxos/keys';
|
|
11
12
|
import { useAsyncEffect } from '@dxos/react-hooks';
|
|
12
13
|
import { type MaybeProvider, getProviderValue } from '@dxos/util';
|
|
13
14
|
|
|
@@ -119,11 +120,11 @@ const WithPluginManagerApp = ({ fireEvents, pluginManager, setupEvents, storyId
|
|
|
119
120
|
return <App />;
|
|
120
121
|
};
|
|
121
122
|
|
|
122
|
-
const storyMeta = {
|
|
123
|
-
|
|
123
|
+
const storyMeta = Plugin.makeMeta({
|
|
124
|
+
key: DXN.make('org.dxos.appFramework.story'),
|
|
124
125
|
name: 'Story',
|
|
125
126
|
tags: ['system'],
|
|
126
|
-
};
|
|
127
|
+
});
|
|
127
128
|
|
|
128
129
|
// No-op plugin to ensure there exists at least one plugin for the startup event.
|
|
129
130
|
// This is necessary because `createApp` expects the startup event to complete before the app is ready.
|
|
@@ -6,6 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import * as Effect from 'effect/Effect';
|
|
7
7
|
import React, { useEffect } from 'react';
|
|
8
8
|
|
|
9
|
+
import { DXN } from '@dxos/keys';
|
|
9
10
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
11
|
|
|
11
12
|
import { ActivationEvents, Capabilities } from '../../../common';
|
|
@@ -13,11 +14,13 @@ import { Capability, Plugin } from '../../../core';
|
|
|
13
14
|
import { useApp } from '../../hooks';
|
|
14
15
|
|
|
15
16
|
// Minimal plugin that contributes a ReactRoot.
|
|
16
|
-
const TestPlugin = Plugin.define<{ error?: boolean }>(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const TestPlugin = Plugin.define<{ error?: boolean }>(
|
|
18
|
+
Plugin.makeMeta({
|
|
19
|
+
key: DXN.make('org.dxos.plugin.test'),
|
|
20
|
+
name: 'Test Plugin',
|
|
21
|
+
tags: ['system'],
|
|
22
|
+
}),
|
|
23
|
+
).pipe(
|
|
21
24
|
Plugin.addModule(({ error }) => ({
|
|
22
25
|
id: 'TestMain',
|
|
23
26
|
activatesOn: ActivationEvents.Startup,
|
|
@@ -50,12 +53,7 @@ const TestPlugin = Plugin.define<{ error?: boolean }>({
|
|
|
50
53
|
type DefaultStoryProps = { plugins?: Plugin.Plugin[] };
|
|
51
54
|
|
|
52
55
|
const DefaultStory = ({ plugins }: DefaultStoryProps) => {
|
|
53
|
-
const App = useApp({
|
|
54
|
-
plugins,
|
|
55
|
-
placeholder: () => {
|
|
56
|
-
return <div className='text-description'>Loading...</div>;
|
|
57
|
-
},
|
|
58
|
-
});
|
|
56
|
+
const App = useApp({ plugins });
|
|
59
57
|
|
|
60
58
|
return <App />;
|
|
61
59
|
};
|
|
@@ -2,13 +2,43 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { type PropsWithChildren, useEffect } from 'react';
|
|
5
|
+
import React, { type PropsWithChildren, useEffect, useLayoutEffect } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Capabilities } from '../../../common';
|
|
8
8
|
import { topologicalSort } from '../../../helpers';
|
|
9
9
|
import { LoadingState, type StartupProgress, type UseAppOptions, useCapabilities, useLoading } from '../../hooks';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
declare global {
|
|
12
|
+
interface Window {
|
|
13
|
+
/**
|
|
14
|
+
* Driver injected by `@dxos/app-framework/vite-plugin`'s `bootLoaderPlugin`
|
|
15
|
+
* (a Solid app inlined into `index.html`). Declared here — on a module that
|
|
16
|
+
* is part of the `@dxos/app-framework/ui` export surface — so the React side
|
|
17
|
+
* and host apps (e.g. composer-app) can drive the loader without each
|
|
18
|
+
* re-declaring the type. The canonical definition lives in the plugin's
|
|
19
|
+
* `loader-app/types.ts`; this mirror exists because that source compiles in a
|
|
20
|
+
* separate (Solid) program that doesn't ship its globals to consumers.
|
|
21
|
+
*/
|
|
22
|
+
__bootLoader?: {
|
|
23
|
+
status: (payload: {
|
|
24
|
+
event?: string;
|
|
25
|
+
module?: string;
|
|
26
|
+
humanized: string;
|
|
27
|
+
/**
|
|
28
|
+
* Optional `(index/total)` tick. When present, the loader replaces the
|
|
29
|
+
* current line in place ("Loading plugins (12/80)") instead of appending
|
|
30
|
+
* a new entry — keeps the visible log compact during long counted phases.
|
|
31
|
+
*/
|
|
32
|
+
range?: { index: number; total: number };
|
|
33
|
+
}) => void;
|
|
34
|
+
progress: (fraction?: number) => void;
|
|
35
|
+
ready: () => void;
|
|
36
|
+
dismiss: () => void;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type AppProps = Pick<UseAppOptions, 'debounce'> & {
|
|
12
42
|
ready: boolean;
|
|
13
43
|
error: unknown;
|
|
14
44
|
progress?: StartupProgress;
|
|
@@ -16,22 +46,50 @@ export type AppProps = Pick<UseAppOptions, 'placeholder' | 'debounce'> & {
|
|
|
16
46
|
|
|
17
47
|
const FIRST_INTERACTIVE_MARK = 'app-framework:first-interactive';
|
|
18
48
|
|
|
19
|
-
export const App = ({
|
|
49
|
+
export const App = ({ ready, error, debounce, progress }: AppProps) => {
|
|
20
50
|
const reactContexts = useCapabilities(Capabilities.ReactContext);
|
|
21
51
|
const reactRoots = useCapabilities(Capabilities.ReactRoot);
|
|
22
52
|
const stage = useLoading(ready, debounce);
|
|
23
53
|
const placeholderDismissed = stage >= LoadingState.Done;
|
|
24
54
|
|
|
55
|
+
// Relay the startup lifecycle into the boot loader injected by
|
|
56
|
+
// `@dxos/app-framework/vite-plugin` (a Solid app inlined into `index.html`,
|
|
57
|
+
// the only visible loading UI). Plugin activation fills the `[0.5, 1]` half
|
|
58
|
+
// of the ring; the raw `event` / `module` ids ride along so the loader owns
|
|
59
|
+
// how each transition is rendered and traced. No-op once the outro has
|
|
60
|
+
// started (`FadeOut`), so the dismissal runs uncontended.
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (stage >= LoadingState.FadeOut) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const fraction = progress?.progress ?? 0;
|
|
66
|
+
window.__bootLoader?.progress(0.5 + fraction * 0.5);
|
|
67
|
+
if (progress?.humanizedName) {
|
|
68
|
+
window.__bootLoader?.status({
|
|
69
|
+
event: progress.event,
|
|
70
|
+
module: progress.module,
|
|
71
|
+
humanized: `Activating ${progress.humanizedName}`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}, [stage, progress?.progress, progress?.event, progress?.module, progress?.humanizedName]);
|
|
75
|
+
|
|
76
|
+
// Hand off at fade-out: play the loader's graceful shrink-and-fade outro.
|
|
77
|
+
// `useLayoutEffect` runs before the next paint so the outro begins in the
|
|
78
|
+
// same frame the real shell starts rendering beneath it.
|
|
79
|
+
useLayoutEffect(() => {
|
|
80
|
+
if (stage >= LoadingState.FadeOut) {
|
|
81
|
+
window.__bootLoader?.ready();
|
|
82
|
+
}
|
|
83
|
+
}, [stage]);
|
|
84
|
+
|
|
25
85
|
// Emit a once-per-app `app-framework:first-interactive` mark the first time
|
|
26
|
-
// the
|
|
27
|
-
//
|
|
86
|
+
// the loader is dismissed and the real app shell renders. Closes the gap
|
|
87
|
+
// between `Startup` activated and the first interactive paint.
|
|
28
88
|
//
|
|
29
|
-
// Also
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// that don't (the loader would otherwise sit forever as a `z-index: 10`
|
|
34
|
-
// overlay covering the rendered shell).
|
|
89
|
+
// Also the framework-owned handoff-complete signal: `dismiss()` removes the
|
|
90
|
+
// loader immediately on the fast-load path (where `useLoading` skips
|
|
91
|
+
// `FadeOut`), and is a no-op once an outro is already in flight, so it never
|
|
92
|
+
// cuts the animation short.
|
|
35
93
|
useEffect(() => {
|
|
36
94
|
if (!placeholderDismissed) {
|
|
37
95
|
return;
|
|
@@ -39,21 +97,26 @@ export const App = ({ placeholder: Placeholder, ready, error, debounce, progress
|
|
|
39
97
|
if (performance.getEntriesByName(FIRST_INTERACTIVE_MARK).length === 0) {
|
|
40
98
|
performance.mark(FIRST_INTERACTIVE_MARK);
|
|
41
99
|
}
|
|
42
|
-
|
|
100
|
+
window.__bootLoader?.dismiss();
|
|
43
101
|
}, [placeholderDismissed]);
|
|
44
102
|
|
|
103
|
+
// Used in tests to exercise the error boundary & reset dialog (see
|
|
104
|
+
// composer-app's `basic.spec.ts`). Thrown into the surrounding `ErrorBoundary`.
|
|
105
|
+
if (location.search === '?throw') {
|
|
106
|
+
throw new Error('Test error');
|
|
107
|
+
}
|
|
108
|
+
|
|
45
109
|
if (error) {
|
|
46
110
|
// This triggers the error boundary to provide UI feedback for the startup error.
|
|
47
111
|
throw error;
|
|
48
112
|
}
|
|
49
113
|
|
|
114
|
+
// The boot loader owns the screen until handoff completes; render nothing
|
|
115
|
+
// until then (any DOM here would sit invisibly behind the `z-index: 10`
|
|
116
|
+
// loader anyway).
|
|
50
117
|
// TODO(wittjosiah): Consider using Suspense instead.
|
|
51
118
|
if (!placeholderDismissed) {
|
|
52
|
-
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return <Placeholder stage={stage} progress={progress} />;
|
|
119
|
+
return null;
|
|
57
120
|
}
|
|
58
121
|
|
|
59
122
|
const ComposedContext = composeContexts(reactContexts);
|
|
@@ -7,6 +7,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
7
7
|
import * as Effect from 'effect/Effect';
|
|
8
8
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
|
+
import { DXN } from '@dxos/keys';
|
|
10
11
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
12
|
import { useWebComponentContext } from '@dxos/web-context-react';
|
|
12
13
|
|
|
@@ -117,11 +118,13 @@ const CounterComponent = () => {
|
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
// Plugin that provides the Counter capability and renders the UI
|
|
120
|
-
const CounterPlugin = Plugin.define(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
const CounterPlugin = Plugin.define(
|
|
122
|
+
Plugin.makeMeta({
|
|
123
|
+
key: DXN.make('org.dxos.plugin.counter'),
|
|
124
|
+
name: 'Counter Plugin',
|
|
125
|
+
tags: ['system'],
|
|
126
|
+
}),
|
|
127
|
+
).pipe(
|
|
125
128
|
Plugin.addModule({
|
|
126
129
|
id: 'CounterMain',
|
|
127
130
|
activatesOn: ActivationEvents.Startup,
|
|
@@ -155,17 +158,9 @@ const CounterPlugin = Plugin.define({
|
|
|
155
158
|
)();
|
|
156
159
|
|
|
157
160
|
const plugins = [CounterPlugin];
|
|
158
|
-
const placeholder = () => (
|
|
159
|
-
<div className='flex h-screen items-center justify-center p-4 text-lg text-neutral-500'>
|
|
160
|
-
Initializing Application...
|
|
161
|
-
</div>
|
|
162
|
-
);
|
|
163
161
|
|
|
164
162
|
const DefaultStory = () => {
|
|
165
|
-
const App = useApp({
|
|
166
|
-
plugins,
|
|
167
|
-
placeholder,
|
|
168
|
-
});
|
|
163
|
+
const App = useApp({ plugins });
|
|
169
164
|
|
|
170
165
|
return <App />;
|
|
171
166
|
};
|