@dxos/plugin-client 0.7.5-main.9d2a38b → 0.7.5-main.e94eead

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.
Files changed (148) hide show
  1. package/dist/lib/browser/{app-graph-builder-6LOTDB4T.mjs → app-graph-builder-ON64R7SO.mjs} +2 -2
  2. package/dist/lib/browser/{chunk-E3OUNOEL.mjs → chunk-7DPLXOMO.mjs} +14 -2
  3. package/dist/lib/browser/chunk-7DPLXOMO.mjs.map +7 -0
  4. package/dist/lib/browser/chunk-EYBZZHSM.mjs +271 -0
  5. package/dist/lib/browser/chunk-EYBZZHSM.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-SVBQRT7I.mjs → chunk-GHEQL2E6.mjs} +1 -2
  7. package/dist/lib/browser/{chunk-SVBQRT7I.mjs.map → chunk-GHEQL2E6.mjs.map} +3 -3
  8. package/dist/lib/browser/{chunk-R4Q6H7RD.mjs → chunk-RGMLBYIF.mjs} +2 -2
  9. package/dist/lib/browser/{chunk-R4Q6H7RD.mjs.map → chunk-RGMLBYIF.mjs.map} +2 -2
  10. package/dist/lib/browser/{client-25GYH6TN.mjs → client-A7YSVURS.mjs} +4 -13
  11. package/dist/lib/browser/client-A7YSVURS.mjs.map +7 -0
  12. package/dist/lib/browser/index.mjs +28 -15
  13. package/dist/lib/browser/index.mjs.map +3 -3
  14. package/dist/lib/browser/intent-resolver-A6SKE7WJ.mjs +267 -0
  15. package/dist/lib/browser/intent-resolver-A6SKE7WJ.mjs.map +7 -0
  16. package/dist/lib/browser/meta.json +1 -1
  17. package/dist/lib/browser/{react-context-M3RT6SSG.mjs → react-context-R3SG55F5.mjs} +2 -2
  18. package/dist/lib/browser/{react-surface-XXONPG5T.mjs → react-surface-XGOUHVIH.mjs} +23 -7
  19. package/dist/lib/browser/react-surface-XGOUHVIH.mjs.map +7 -0
  20. package/dist/lib/browser/schema-J4MJIXRP.mjs +23 -0
  21. package/dist/lib/browser/schema-J4MJIXRP.mjs.map +7 -0
  22. package/dist/lib/browser/types.mjs +1 -1
  23. package/dist/lib/node/{app-graph-builder-JCND3P6G.cjs → app-graph-builder-RW7ESEX3.cjs} +6 -6
  24. package/dist/lib/node/chunk-7LVBFGKQ.cjs +299 -0
  25. package/dist/lib/node/chunk-7LVBFGKQ.cjs.map +7 -0
  26. package/dist/lib/node/{chunk-NVYWMDF3.cjs → chunk-TREVKZCJ.cjs} +17 -5
  27. package/dist/lib/node/chunk-TREVKZCJ.cjs.map +7 -0
  28. package/dist/lib/node/{chunk-3OB25QN2.cjs → chunk-UHIGZJQL.cjs} +4 -5
  29. package/dist/lib/node/{chunk-3OB25QN2.cjs.map → chunk-UHIGZJQL.cjs.map} +3 -3
  30. package/dist/lib/node/{chunk-IXVWJCYB.cjs → chunk-XPC4JOPP.cjs} +5 -5
  31. package/dist/lib/node/{chunk-IXVWJCYB.cjs.map → chunk-XPC4JOPP.cjs.map} +2 -2
  32. package/dist/lib/node/{client-52L4TUYU.cjs → client-2RQATBB6.cjs} +9 -18
  33. package/dist/lib/node/client-2RQATBB6.cjs.map +7 -0
  34. package/dist/lib/node/index.cjs +30 -17
  35. package/dist/lib/node/index.cjs.map +3 -3
  36. package/dist/lib/node/intent-resolver-DKQHARC2.cjs +275 -0
  37. package/dist/lib/node/intent-resolver-DKQHARC2.cjs.map +7 -0
  38. package/dist/lib/node/meta.json +1 -1
  39. package/dist/lib/node/{react-context-B2DNULH4.cjs → react-context-TD6VGBVA.cjs} +6 -6
  40. package/dist/lib/node/{react-surface-7USN5NOO.cjs → react-surface-IY7GXEGU.cjs} +28 -16
  41. package/dist/lib/node/react-surface-IY7GXEGU.cjs.map +7 -0
  42. package/dist/lib/node/schema-JXJS75HQ.cjs +39 -0
  43. package/dist/lib/node/schema-JXJS75HQ.cjs.map +7 -0
  44. package/dist/lib/node/types.cjs +2 -2
  45. package/dist/lib/node/types.cjs.map +1 -1
  46. package/dist/lib/node-esm/{app-graph-builder-ZLGKFBLY.mjs → app-graph-builder-TDYRWNI7.mjs} +2 -2
  47. package/dist/lib/node-esm/chunk-CRF22MJJ.mjs +272 -0
  48. package/dist/lib/node-esm/chunk-CRF22MJJ.mjs.map +7 -0
  49. package/dist/lib/node-esm/{chunk-74T33KTB.mjs → chunk-QJMF2MWP.mjs} +14 -2
  50. package/dist/lib/node-esm/chunk-QJMF2MWP.mjs.map +7 -0
  51. package/dist/lib/node-esm/{chunk-73M5MK5Q.mjs → chunk-QR72BXJK.mjs} +2 -2
  52. package/dist/lib/node-esm/{chunk-73M5MK5Q.mjs.map → chunk-QR72BXJK.mjs.map} +2 -2
  53. package/dist/lib/node-esm/{chunk-OBGRL3DF.mjs → chunk-T35SFKK3.mjs} +1 -2
  54. package/dist/lib/node-esm/{chunk-OBGRL3DF.mjs.map → chunk-T35SFKK3.mjs.map} +3 -3
  55. package/dist/lib/node-esm/{client-K52NWOUN.mjs → client-36DKBSB7.mjs} +4 -13
  56. package/dist/lib/node-esm/client-36DKBSB7.mjs.map +7 -0
  57. package/dist/lib/node-esm/index.mjs +28 -15
  58. package/dist/lib/node-esm/index.mjs.map +3 -3
  59. package/dist/lib/node-esm/intent-resolver-HFYRSXUG.mjs +268 -0
  60. package/dist/lib/node-esm/intent-resolver-HFYRSXUG.mjs.map +7 -0
  61. package/dist/lib/node-esm/meta.json +1 -1
  62. package/dist/lib/node-esm/{react-context-IGEHJQK5.mjs → react-context-ZOGDOWJ2.mjs} +2 -2
  63. package/dist/lib/node-esm/{react-surface-CXLBCMPN.mjs → react-surface-F3PDFJMV.mjs} +23 -7
  64. package/dist/lib/node-esm/react-surface-F3PDFJMV.mjs.map +7 -0
  65. package/dist/lib/node-esm/schema-XM4HBLIX.mjs +24 -0
  66. package/dist/lib/node-esm/schema-XM4HBLIX.mjs.map +7 -0
  67. package/dist/lib/node-esm/types.mjs +1 -1
  68. package/dist/types/src/ClientPlugin.d.ts.map +1 -1
  69. package/dist/types/src/capabilities/app-graph-builder.d.ts +110 -110
  70. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  71. package/dist/types/src/capabilities/capabilities.d.ts +0 -1
  72. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  73. package/dist/types/src/capabilities/client.d.ts.map +1 -1
  74. package/dist/types/src/capabilities/index.d.ts +119 -117
  75. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  76. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -1
  77. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  78. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  79. package/dist/types/src/capabilities/schema.d.ts +4 -0
  80. package/dist/types/src/capabilities/schema.d.ts.map +1 -0
  81. package/dist/types/src/components/IdentityDialog.d.ts +1 -2
  82. package/dist/types/src/components/IdentityDialog.d.ts.map +1 -1
  83. package/dist/types/src/components/JoinDialog.d.ts +1 -2
  84. package/dist/types/src/components/JoinDialog.d.ts.map +1 -1
  85. package/dist/types/src/components/ManageCredentialsDialog.d.ts +3 -0
  86. package/dist/types/src/components/ManageCredentialsDialog.d.ts.map +1 -0
  87. package/dist/types/src/components/ManageCredentialsDialog.stories.d.ts +8 -0
  88. package/dist/types/src/components/ManageCredentialsDialog.stories.d.ts.map +1 -0
  89. package/dist/types/src/components/RecoveryCodeDialog.d.ts +1 -2
  90. package/dist/types/src/components/RecoveryCodeDialog.d.ts.map +1 -1
  91. package/dist/types/src/components/RecoverySetupDialog.d.ts +3 -0
  92. package/dist/types/src/components/RecoverySetupDialog.d.ts.map +1 -0
  93. package/dist/types/src/components/RecoverySetupDialog.stories.d.ts +8 -0
  94. package/dist/types/src/components/RecoverySetupDialog.stories.d.ts.map +1 -0
  95. package/dist/types/src/components/index.d.ts +1 -0
  96. package/dist/types/src/components/index.d.ts.map +1 -1
  97. package/dist/types/src/events.d.ts +1 -1
  98. package/dist/types/src/translations.d.ts +7 -0
  99. package/dist/types/src/translations.d.ts.map +1 -1
  100. package/dist/types/src/types.d.ts +17 -1
  101. package/dist/types/src/types.d.ts.map +1 -1
  102. package/package.json +23 -17
  103. package/src/ClientPlugin.ts +11 -6
  104. package/src/capabilities/capabilities.ts +0 -1
  105. package/src/capabilities/client.ts +1 -9
  106. package/src/capabilities/index.ts +1 -0
  107. package/src/capabilities/intent-resolver.ts +171 -67
  108. package/src/capabilities/react-surface.tsx +21 -6
  109. package/src/capabilities/schema.ts +26 -0
  110. package/src/components/IdentityDialog.tsx +15 -3
  111. package/src/components/JoinDialog.tsx +1 -1
  112. package/src/components/ManageCredentialsDialog.stories.tsx +57 -0
  113. package/src/components/ManageCredentialsDialog.tsx +64 -0
  114. package/src/components/RecoveryCodeDialog.stories.tsx +5 -5
  115. package/src/components/RecoveryCodeDialog.tsx +2 -1
  116. package/src/components/RecoverySetupDialog.stories.tsx +77 -0
  117. package/src/components/RecoverySetupDialog.tsx +52 -0
  118. package/src/components/index.ts +1 -0
  119. package/src/events.ts +1 -1
  120. package/src/translations.ts +10 -1
  121. package/src/types.ts +11 -1
  122. package/dist/lib/browser/chunk-6IF6PREG.mjs +0 -158
  123. package/dist/lib/browser/chunk-6IF6PREG.mjs.map +0 -7
  124. package/dist/lib/browser/chunk-E3OUNOEL.mjs.map +0 -7
  125. package/dist/lib/browser/client-25GYH6TN.mjs.map +0 -7
  126. package/dist/lib/browser/intent-resolver-IWWLWOZD.mjs +0 -132
  127. package/dist/lib/browser/intent-resolver-IWWLWOZD.mjs.map +0 -7
  128. package/dist/lib/browser/react-surface-XXONPG5T.mjs.map +0 -7
  129. package/dist/lib/node/chunk-N3U7KVO3.cjs +0 -186
  130. package/dist/lib/node/chunk-N3U7KVO3.cjs.map +0 -7
  131. package/dist/lib/node/chunk-NVYWMDF3.cjs.map +0 -7
  132. package/dist/lib/node/client-52L4TUYU.cjs.map +0 -7
  133. package/dist/lib/node/intent-resolver-H3Y64MIP.cjs +0 -140
  134. package/dist/lib/node/intent-resolver-H3Y64MIP.cjs.map +0 -7
  135. package/dist/lib/node/react-surface-7USN5NOO.cjs.map +0 -7
  136. package/dist/lib/node-esm/chunk-74T33KTB.mjs.map +0 -7
  137. package/dist/lib/node-esm/chunk-EHGD3UJH.mjs +0 -159
  138. package/dist/lib/node-esm/chunk-EHGD3UJH.mjs.map +0 -7
  139. package/dist/lib/node-esm/client-K52NWOUN.mjs.map +0 -7
  140. package/dist/lib/node-esm/intent-resolver-JCKD6R6L.mjs +0 -133
  141. package/dist/lib/node-esm/intent-resolver-JCKD6R6L.mjs.map +0 -7
  142. package/dist/lib/node-esm/react-surface-CXLBCMPN.mjs.map +0 -7
  143. /package/dist/lib/browser/{app-graph-builder-6LOTDB4T.mjs.map → app-graph-builder-ON64R7SO.mjs.map} +0 -0
  144. /package/dist/lib/browser/{react-context-M3RT6SSG.mjs.map → react-context-R3SG55F5.mjs.map} +0 -0
  145. /package/dist/lib/node/{app-graph-builder-JCND3P6G.cjs.map → app-graph-builder-RW7ESEX3.cjs.map} +0 -0
  146. /package/dist/lib/node/{react-context-B2DNULH4.cjs.map → react-context-TD6VGBVA.cjs.map} +0 -0
  147. /package/dist/lib/node-esm/{app-graph-builder-ZLGKFBLY.mjs.map → app-graph-builder-TDYRWNI7.mjs.map} +0 -0
  148. /package/dist/lib/node-esm/{react-context-IGEHJQK5.mjs.map → react-context-ZOGDOWJ2.mjs.map} +0 -0
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@dxos/plugin-client",
3
- "version": "0.7.5-main.9d2a38b",
3
+ "version": "0.7.5-main.e94eead",
4
4
  "description": "DXOS Surface plugin for DXOS Client",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
7
  "license": "MIT",
8
8
  "author": "DXOS.org",
9
9
  "sideEffects": true,
10
+ "type": "module",
10
11
  "exports": {
11
12
  ".": {
12
13
  "types": "./dist/types/src/index.d.ts",
@@ -32,33 +33,38 @@
32
33
  "src"
33
34
  ],
34
35
  "dependencies": {
35
- "@phosphor-icons/react": "^2.1.5",
36
- "@dxos/app-framework": "0.7.5-main.9d2a38b",
37
- "@dxos/config": "0.7.5-main.9d2a38b",
38
- "@dxos/echo-schema": "0.7.5-main.9d2a38b",
39
- "@dxos/echo-signals": "0.7.5-main.9d2a38b",
40
- "@dxos/log": "0.7.5-main.9d2a38b",
41
- "@dxos/local-storage": "0.7.5-main.9d2a38b",
42
- "@dxos/invariant": "0.7.5-main.9d2a38b",
43
- "@dxos/plugin-graph": "0.7.5-main.9d2a38b",
44
- "@dxos/plugin-observability": "0.7.5-main.9d2a38b",
45
- "@dxos/react-client": "0.7.5-main.9d2a38b",
46
- "@dxos/react-ui": "0.7.5-main.9d2a38b",
47
- "@dxos/shell": "0.7.5-main.9d2a38b",
48
- "@dxos/util": "0.7.5-main.9d2a38b"
36
+ "@preact/signals-core": "^1.6.0",
37
+ "@dxos/app-framework": "0.7.5-main.e94eead",
38
+ "@dxos/echo-schema": "0.7.5-main.e94eead",
39
+ "@dxos/config": "0.7.5-main.e94eead",
40
+ "@dxos/invariant": "0.7.5-main.e94eead",
41
+ "@dxos/echo-signals": "0.7.5-main.e94eead",
42
+ "@dxos/local-storage": "0.7.5-main.e94eead",
43
+ "@dxos/log": "0.7.5-main.e94eead",
44
+ "@dxos/plugin-graph": "0.7.5-main.e94eead",
45
+ "@dxos/plugin-observability": "0.7.5-main.e94eead",
46
+ "@dxos/react-client": "0.7.5-main.e94eead",
47
+ "@dxos/shell": "0.7.5-main.e94eead",
48
+ "@dxos/util": "0.7.5-main.e94eead"
49
49
  },
50
50
  "devDependencies": {
51
+ "@phosphor-icons/react": "^2.1.5",
51
52
  "@types/react": "~18.2.0",
52
53
  "@types/react-dom": "~18.2.0",
53
54
  "react": "~18.2.0",
54
55
  "react-dom": "~18.2.0",
55
56
  "vite": "5.4.7",
56
- "@dxos/storybook-utils": "0.7.5-main.9d2a38b"
57
+ "@dxos/react-ui": "0.7.5-main.e94eead",
58
+ "@dxos/live-object": "0.7.5-main.e94eead",
59
+ "@dxos/react-ui-theme": "0.7.5-main.e94eead",
60
+ "@dxos/storybook-utils": "0.7.5-main.e94eead"
57
61
  },
58
62
  "peerDependencies": {
59
63
  "@phosphor-icons/react": "^2.1.5",
60
64
  "react": "~18.2.0",
61
- "react-dom": "~18.2.0"
65
+ "react-dom": "~18.2.0",
66
+ "@dxos/react-ui-theme": "0.7.5-main.e94eead",
67
+ "@dxos/react-ui": "0.7.5-main.e94eead"
62
68
  },
63
69
  "publishConfig": {
64
70
  "access": "public"
@@ -2,9 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, contributes, defineModule, definePlugin, Events } from '@dxos/app-framework';
5
+ import { Capabilities, contributes, defineModule, definePlugin, Events, oneOf } from '@dxos/app-framework';
6
6
 
7
- import { Client, AppGraphBuilder, IntentResolver, ReactContext, ReactSurface } from './capabilities';
7
+ import { Client, AppGraphBuilder, IntentResolver, ReactContext, ReactSurface, Schema } from './capabilities';
8
8
  import { ClientEvents } from './events';
9
9
  import { meta } from './meta';
10
10
  import translations from './translations';
@@ -25,11 +25,16 @@ export const ClientPlugin = ({
25
25
  return definePlugin(meta, [
26
26
  defineModule({
27
27
  id: `${meta.id}/module/client`,
28
- activatesOn: Events.Startup,
29
- activatesBefore: [ClientEvents.SetupClient],
28
+ activatesOn: oneOf(Events.Startup, Events.SetupAppGraph),
30
29
  activatesAfter: [ClientEvents.ClientReady],
31
30
  activate: (context) => Client({ ...options, context }),
32
31
  }),
32
+ defineModule({
33
+ id: `${meta.id}/module/schema`,
34
+ activatesOn: ClientEvents.ClientReady,
35
+ activatesBefore: [ClientEvents.SetupSchema],
36
+ activate: Schema,
37
+ }),
33
38
  defineModule({
34
39
  id: `${meta.id}/module/react-context`,
35
40
  activatesOn: Events.Startup,
@@ -37,7 +42,7 @@ export const ClientPlugin = ({
37
42
  }),
38
43
  defineModule({
39
44
  id: `${meta.id}/module/react-surface`,
40
- activatesOn: Events.Startup,
45
+ activatesOn: Events.SetupReactSurface,
41
46
  activate: () => ReactSurface({ createInvitationUrl }),
42
47
  }),
43
48
  defineModule({
@@ -47,7 +52,7 @@ export const ClientPlugin = ({
47
52
  }),
48
53
  defineModule({
49
54
  id: `${meta.id}/module/intent-resolver`,
50
- activatesOn: Events.SetupIntents,
55
+ activatesOn: Events.SetupIntentResolver,
51
56
  activate: (context) => IntentResolver({ context, onReset }),
52
57
  }),
53
58
  defineModule({
@@ -11,5 +11,4 @@ import { CLIENT_PLUGIN } from '../meta';
11
11
  export namespace ClientCapabilities {
12
12
  export const Client = defineCapability<Client>(`${CLIENT_PLUGIN}/capability/client`);
13
13
  export const Schema = defineCapability<TypedObject[]>(`${CLIENT_PLUGIN}/capability/schema`);
14
- export const SystemSchema = defineCapability<TypedObject[]>(`${CLIENT_PLUGIN}/capability/system-schema`);
15
14
  }
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  import { contributes, type PluginsContext } from '@dxos/app-framework';
6
- import { Config, Defaults, Envs, Local, Storage } from '@dxos/config';
7
6
  import { Client } from '@dxos/react-client';
8
7
 
9
8
  import { ClientCapabilities } from './capabilities';
@@ -15,17 +14,10 @@ type ClientCapabilityOptions = Omit<ClientPluginOptions, 'appKey' | 'invitationU
15
14
  };
16
15
 
17
16
  export default async ({ context, onClientInitialized, ...options }: ClientCapabilityOptions) => {
18
- const config = new Config(await Storage(), Envs(), Local(), Defaults());
19
- const client = new Client({ config, ...options });
20
-
17
+ const client = new Client(options);
21
18
  await client.initialize();
22
19
  await onClientInitialized?.(context, client);
23
20
 
24
- const systemSchemas = Array.from(new Set(context.requestCapabilities(ClientCapabilities.SystemSchema).flat()));
25
- const schemas = Array.from(new Set(context.requestCapabilities(ClientCapabilities.Schema).flat()));
26
- client.addTypes(systemSchemas);
27
- client.addTypes(schemas);
28
-
29
21
  // TODO(wittjosiah): Remove. This is a hack to get the app to boot with the new identity after a reset.
30
22
  client.reloaded.on(() => {
31
23
  client.halo.identity.subscribe(async (identity) => {
@@ -9,5 +9,6 @@ export const Client = lazy(async () => import('./client'));
9
9
  export const IntentResolver = lazy(async () => import('./intent-resolver'));
10
10
  export const ReactContext = lazy(async () => import('./react-context'));
11
11
  export const ReactSurface = lazy(async () => import('./react-surface'));
12
+ export const Schema = lazy(async () => import('./schema'));
12
13
 
13
14
  export * from './capabilities';
@@ -12,6 +12,7 @@ import {
12
12
  } from '@dxos/app-framework';
13
13
  import { invariant } from '@dxos/invariant';
14
14
  import { ObservabilityAction } from '@dxos/plugin-observability/types';
15
+ import { PublicKey } from '@dxos/react-client';
15
16
  import { type JoinPanelProps } from '@dxos/shell/react';
16
17
 
17
18
  import { ClientCapabilities } from './capabilities';
@@ -21,81 +22,184 @@ import { ClientAction, type ClientPluginOptions } from '../types';
21
22
 
22
23
  type IntentResolverOptions = Pick<ClientPluginOptions, 'onReset'> & {
23
24
  context: PluginsContext;
25
+ appName?: string;
24
26
  };
25
27
 
26
- export default ({ context, onReset }: IntentResolverOptions) =>
28
+ export default ({ context, appName = 'Composer', onReset }: IntentResolverOptions) =>
27
29
  contributes(Capabilities.IntentResolver, [
28
- createResolver(ClientAction.CreateIdentity, async () => {
29
- const manager = context.requestCapability(Capabilities.PluginManager);
30
- const client = context.requestCapability(ClientCapabilities.Client);
31
- const data = await client.halo.createIdentity();
32
- await manager.activate(ClientEvents.IdentityCreated);
33
- return { data, intents: [createIntent(ObservabilityAction.SendEvent, { name: 'identity.create' })] };
30
+ createResolver({
31
+ intent: ClientAction.CreateIdentity,
32
+ resolve: async () => {
33
+ const manager = context.requestCapability(Capabilities.PluginManager);
34
+ const client = context.requestCapability(ClientCapabilities.Client);
35
+ const data = await client.halo.createIdentity();
36
+ await manager.activate(ClientEvents.IdentityCreated);
37
+ return { data, intents: [createIntent(ObservabilityAction.SendEvent, { name: 'identity.create' })] };
38
+ },
34
39
  }),
35
- createResolver(ClientAction.JoinIdentity, async (data) => {
36
- return {
37
- intents: [
38
- createIntent(LayoutAction.SetLayout, {
39
- element: 'dialog',
40
- component: JOIN_DIALOG,
41
- dialogBlockAlign: 'start',
42
- subject: {
43
- initialInvitationCode: data.invitationCode,
44
- initialDisposition: 'accept-halo-invitation',
45
- } satisfies Partial<JoinPanelProps>,
46
- }),
47
- ],
48
- };
40
+ createResolver({
41
+ intent: ClientAction.JoinIdentity,
42
+ resolve: async (data) => {
43
+ return {
44
+ intents: [
45
+ createIntent(LayoutAction.UpdateDialog, {
46
+ part: 'dialog',
47
+ subject: JOIN_DIALOG,
48
+ options: {
49
+ blockAlign: 'start',
50
+ props: {
51
+ initialInvitationCode: data.invitationCode,
52
+ initialDisposition: 'accept-halo-invitation',
53
+ },
54
+ },
55
+ }),
56
+ ],
57
+ };
58
+ },
49
59
  }),
50
- createResolver(ClientAction.ShareIdentity, async () => {
51
- return {
52
- intents: [
53
- createIntent(LayoutAction.SetLayout, {
54
- element: 'dialog',
55
- component: IDENTITY_DIALOG,
56
- dialogBlockAlign: 'start',
57
- }),
58
- createIntent(ObservabilityAction.SendEvent, { name: 'identity.share' }),
59
- ],
60
- };
60
+ createResolver({
61
+ intent: ClientAction.ShareIdentity,
62
+ resolve: async () => {
63
+ return {
64
+ intents: [
65
+ createIntent(LayoutAction.UpdateDialog, {
66
+ part: 'dialog',
67
+ subject: IDENTITY_DIALOG,
68
+ options: {
69
+ blockAlign: 'start',
70
+ },
71
+ }),
72
+ createIntent(ObservabilityAction.SendEvent, { name: 'identity.share' }),
73
+ ],
74
+ };
75
+ },
61
76
  }),
62
- createResolver(ClientAction.RecoverIdentity, async () => {
63
- return {
64
- intents: [
65
- createIntent(LayoutAction.SetLayout, {
66
- element: 'dialog',
67
- component: JOIN_DIALOG,
68
- dialogBlockAlign: 'start',
69
- subject: { initialDisposition: 'recover-identity' } satisfies Partial<JoinPanelProps>,
70
- }),
71
- ],
72
- };
77
+ createResolver({
78
+ intent: ClientAction.RecoverIdentity,
79
+ resolve: async () => {
80
+ return {
81
+ intents: [
82
+ createIntent(LayoutAction.UpdateDialog, {
83
+ part: 'dialog',
84
+ subject: JOIN_DIALOG,
85
+ options: {
86
+ blockAlign: 'start',
87
+ props: {
88
+ initialDisposition: 'recover-identity',
89
+ } satisfies Partial<JoinPanelProps>,
90
+ },
91
+ }),
92
+ ],
93
+ };
94
+ },
73
95
  }),
74
- createResolver(ClientAction.ResetStorage, async (data) => {
75
- await onReset?.({ target: data.target });
76
- return {};
96
+ createResolver({
97
+ intent: ClientAction.ResetStorage,
98
+ resolve: async (data) => {
99
+ await onReset?.({ target: data.target });
100
+ },
77
101
  }),
78
- createResolver(ClientAction.CreateAgent, async () => {
79
- const client = context.requestCapability(ClientCapabilities.Client);
80
- invariant(client.services.services.EdgeAgentService, 'Missing EdgeAgentService');
81
- await client.services.services.EdgeAgentService.createAgent(null as any, { timeout: 10_000 });
102
+ createResolver({
103
+ intent: ClientAction.CreateAgent,
104
+ resolve: async () => {
105
+ const client = context.requestCapability(ClientCapabilities.Client);
106
+ invariant(client.services.services.EdgeAgentService, 'Missing EdgeAgentService');
107
+ await client.services.services.EdgeAgentService.createAgent(null as any, { timeout: 10_000 });
108
+ },
82
109
  }),
83
- createResolver(ClientAction.CreateRecoveryCode, async () => {
84
- const client = context.requestCapability(ClientCapabilities.Client);
85
- invariant(client.services.services.IdentityService, 'IdentityService not available');
86
- // TODO(wittjosiah): This needs a proper api. Rename property.
87
- const { seedphrase } = await client.services.services.IdentityService.createRecoveryPhrase();
88
- return {
89
- intents: [
90
- createIntent(LayoutAction.SetLayout, {
91
- element: 'dialog',
92
- dialogBlockAlign: 'start',
93
- dialogType: 'alert',
94
- state: true,
95
- component: RECOVER_CODE_DIALOG,
96
- subject: { code: seedphrase },
97
- }),
98
- ],
99
- };
110
+ createResolver({
111
+ intent: ClientAction.CreateRecoveryCode,
112
+ resolve: async () => {
113
+ const client = context.requestCapability(ClientCapabilities.Client);
114
+ invariant(client.services.services.IdentityService, 'IdentityService not available');
115
+ // TODO(wittjosiah): This needs a proper api.
116
+ const { recoveryCode } = await client.services.services.IdentityService.createRecoveryCredential({});
117
+ return {
118
+ intents: [
119
+ createIntent(LayoutAction.UpdateDialog, {
120
+ part: 'dialog',
121
+ subject: RECOVER_CODE_DIALOG,
122
+ options: {
123
+ blockAlign: 'start',
124
+ type: 'alert',
125
+ props: { code: recoveryCode },
126
+ },
127
+ }),
128
+ ],
129
+ };
130
+ },
131
+ }),
132
+ createResolver({
133
+ intent: ClientAction.CreatePasskey,
134
+ resolve: async () => {
135
+ const client = context.requestCapability(ClientCapabilities.Client);
136
+ const identity = client.halo.identity.get();
137
+ invariant(identity, 'Identity not available');
138
+
139
+ // TODO(wittjosiah): Consider factoring out passkey creation to the halo api.
140
+ const lookupKey = PublicKey.random();
141
+ const credential = await navigator.credentials.create({
142
+ publicKey: {
143
+ challenge: new Uint8Array(),
144
+ rp: { id: location.hostname, name: appName },
145
+ user: {
146
+ id: lookupKey.asUint8Array(),
147
+ name: identity.did,
148
+ displayName: identity.profile?.displayName ?? '',
149
+ },
150
+ pubKeyCredParams: [
151
+ { type: 'public-key', alg: -8 }, // Ed25519 (not yet supported across all browsers)
152
+ { type: 'public-key', alg: -7 }, // ES256
153
+ ],
154
+ // https://web.dev/articles/webauthn-discoverable-credentials#resident-key
155
+ authenticatorSelection: {
156
+ residentKey: 'required',
157
+ requireResidentKey: true,
158
+ },
159
+ },
160
+ });
161
+
162
+ invariant(credential, 'Credential not available');
163
+ const recoveryKey = PublicKey.from(new Uint8Array((credential as any).response.getPublicKey()));
164
+ const algorithm = (credential as any).response.getPublicKeyAlgorithm() === -7 ? 'ES256' : 'ED25519';
165
+
166
+ // TODO(wittjosiah): This needs a proper api.
167
+ invariant(client.services.services.IdentityService, 'IdentityService not available');
168
+ await client.services.services.IdentityService.createRecoveryCredential({
169
+ data: {
170
+ recoveryKey,
171
+ algorithm,
172
+ lookupKey,
173
+ },
174
+ });
175
+ },
176
+ }),
177
+ createResolver({
178
+ intent: ClientAction.RedeemPasskey,
179
+ resolve: async () => {
180
+ const client = context.requestCapability(ClientCapabilities.Client);
181
+ // TODO(wittjosiah): This needs a proper api.
182
+ invariant(client.services.services.IdentityService, 'IdentityService not available');
183
+ const { deviceKey, controlFeedKey, challenge } =
184
+ await client.services.services.IdentityService.requestRecoveryChallenge();
185
+ const credential = await navigator.credentials.get({
186
+ publicKey: {
187
+ challenge: Buffer.from(challenge, 'base64'),
188
+ rpId: location.hostname,
189
+ userVerification: 'required',
190
+ },
191
+ });
192
+ const lookupKey = PublicKey.from(new Uint8Array((credential as any).response.userHandle));
193
+ await client.services.services.IdentityService.recoverIdentity({
194
+ external: {
195
+ lookupKey,
196
+ deviceKey,
197
+ controlFeedKey,
198
+ signature: Buffer.from((credential as any).response.signature),
199
+ clientDataJson: Buffer.from((credential as any).response.clientDataJSON),
200
+ authenticatorData: Buffer.from((credential as any).response.authenticatorData),
201
+ },
202
+ });
203
+ },
100
204
  }),
101
205
  ]);
@@ -13,9 +13,12 @@ import {
13
13
  JOIN_DIALOG,
14
14
  JoinDialog,
15
15
  RECOVER_CODE_DIALOG,
16
+ RECOVER_SETUP_DIALOG,
16
17
  RecoveryCodeDialog,
18
+ RecoverySetupDialog,
17
19
  type RecoveryCodeDialogProps,
18
20
  } from '../components';
21
+ import { MANAGE_CREDENTIALS_DIALOG, ManageCredentialsDialog } from '../components/ManageCredentialsDialog';
19
22
 
20
23
  type ReactSurfaceOptions = {
21
24
  createInvitationUrl: (invitationCode: string) => string;
@@ -26,19 +29,31 @@ export default ({ createInvitationUrl }: ReactSurfaceOptions) =>
26
29
  createSurface({
27
30
  id: IDENTITY_DIALOG,
28
31
  role: 'dialog',
29
- filter: (data): data is { subject: IdentityPanelProps } => data.component === IDENTITY_DIALOG,
30
- component: ({ data }) => <IdentityDialog {...data.subject} createInvitationUrl={createInvitationUrl} />,
32
+ filter: (data): data is { props: IdentityPanelProps } => data.component === IDENTITY_DIALOG,
33
+ component: ({ data }) => <IdentityDialog {...data.props} createInvitationUrl={createInvitationUrl} />,
31
34
  }),
32
35
  createSurface({
33
36
  id: JOIN_DIALOG,
34
37
  role: 'dialog',
35
- filter: (data): data is { subject: JoinPanelProps } => data.component === JOIN_DIALOG,
36
- component: ({ data }) => <JoinDialog {...data.subject} />,
38
+ filter: (data): data is { props: JoinPanelProps } => data.component === JOIN_DIALOG,
39
+ component: ({ data }) => <JoinDialog {...data.props} />,
37
40
  }),
38
41
  createSurface({
39
42
  id: RECOVER_CODE_DIALOG,
40
43
  role: 'dialog',
41
- filter: (data): data is { subject: RecoveryCodeDialogProps } => data.component === RECOVER_CODE_DIALOG,
42
- component: ({ data }) => <RecoveryCodeDialog {...data.subject} />,
44
+ filter: (data): data is { props: RecoveryCodeDialogProps } => data.component === RECOVER_CODE_DIALOG,
45
+ component: ({ data }) => <RecoveryCodeDialog {...data.props} />,
46
+ }),
47
+ createSurface({
48
+ id: RECOVER_SETUP_DIALOG,
49
+ role: 'dialog',
50
+ filter: (data): data is any => data.component === RECOVER_SETUP_DIALOG,
51
+ component: () => <RecoverySetupDialog />,
52
+ }),
53
+ createSurface({
54
+ id: MANAGE_CREDENTIALS_DIALOG,
55
+ role: 'dialog',
56
+ filter: (data): data is any => data.component === MANAGE_CREDENTIALS_DIALOG,
57
+ component: () => <ManageCredentialsDialog />,
43
58
  }),
44
59
  ]);
@@ -0,0 +1,26 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { effect } from '@preact/signals-core';
6
+
7
+ import { Capabilities, contributes, type PluginsContext } from '@dxos/app-framework';
8
+ import { type TypedObject } from '@dxos/echo-schema';
9
+
10
+ import { ClientCapabilities } from './capabilities';
11
+
12
+ export default (context: PluginsContext) => {
13
+ const client = context.requestCapability(ClientCapabilities.Client);
14
+
15
+ // TODO(wittjosiah): Unregister schemas when they are disabled.
16
+ let previous: TypedObject[] = [];
17
+ const unsubscribe = effect(() => {
18
+ const schemas = Array.from(new Set(context.requestCapabilities(ClientCapabilities.Schema).flat()));
19
+ // TODO(wittjosiah): Filter out schemas which the client has already registered.
20
+ const newSchemas = schemas.filter((schema) => !previous.includes(schema));
21
+ previous = schemas;
22
+ client.addTypes(newSchemas);
23
+ });
24
+
25
+ return contributes(Capabilities.Null, null, () => unsubscribe());
26
+ };
@@ -9,6 +9,7 @@ import { useClient } from '@dxos/react-client';
9
9
  import { Clipboard, Dialog } from '@dxos/react-ui';
10
10
  import { IdentityPanel, type IdentityPanelProps } from '@dxos/shell/react';
11
11
 
12
+ import { MANAGE_CREDENTIALS_DIALOG } from './ManageCredentialsDialog';
12
13
  import { CLIENT_PLUGIN } from '../meta';
13
14
  import { ClientAction } from '../types';
14
15
 
@@ -21,9 +22,9 @@ export const IdentityDialog = (props: IdentityPanelProps) => {
21
22
  const handleDone = useCallback(
22
23
  () =>
23
24
  dispatch(
24
- createIntent(LayoutAction.SetLayout, {
25
- element: 'dialog',
26
- state: false,
25
+ createIntent(LayoutAction.UpdateDialog, {
26
+ part: 'dialog',
27
+ options: { state: false },
27
28
  }),
28
29
  ),
29
30
  [dispatch],
@@ -44,6 +45,16 @@ export const IdentityDialog = (props: IdentityPanelProps) => {
44
45
  await dispatch(createIntent(ClientAction.ResetStorage, { target: 'deviceInvitation' }));
45
46
  }, [dispatch]);
46
47
 
48
+ const handleManageCredentials = useCallback(async () => {
49
+ await dispatch(
50
+ createIntent(LayoutAction.UpdateDialog, {
51
+ part: 'dialog',
52
+ subject: MANAGE_CREDENTIALS_DIALOG,
53
+ options: { state: true, blockAlign: 'start' },
54
+ }),
55
+ );
56
+ }, [dispatch]);
57
+
47
58
  return (
48
59
  <Dialog.Content>
49
60
  <Clipboard.Provider>
@@ -54,6 +65,7 @@ export const IdentityDialog = (props: IdentityPanelProps) => {
54
65
  onResetStorage={handleResetStorage}
55
66
  onRecover={handleRecover}
56
67
  onJoinNewIdentity={handleJoinNewIdentity}
68
+ onManageCredentials={handleManageCredentials}
57
69
  />
58
70
  </Clipboard.Provider>
59
71
  </Dialog.Content>
@@ -24,7 +24,7 @@ export const JoinDialog = (props: JoinPanelProps) => {
24
24
  async (result: InvitationResult | null) => {
25
25
  if (result?.identityKey) {
26
26
  await Promise.all([
27
- dispatch(createIntent(LayoutAction.SetLayout, { element: 'dialog', state: false })),
27
+ dispatch(createIntent(LayoutAction.UpdateDialog, { part: 'dialog', options: { state: false } })),
28
28
  dispatch(
29
29
  createIntent(ObservabilityAction.SendEvent, {
30
30
  name: props.initialDisposition === 'recover-identity' ? 'identity.recover' : 'identity.join',
@@ -0,0 +1,57 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type StoryObj, type Meta } from '@storybook/react';
8
+ import React from 'react';
9
+
10
+ import { IntentPlugin } from '@dxos/app-framework';
11
+ import { withPluginManager } from '@dxos/app-framework/testing';
12
+ import { AlertDialog } from '@dxos/react-ui';
13
+ import { withTheme, withLayout } from '@dxos/storybook-utils';
14
+
15
+ import { ManageCredentialsDialog } from './ManageCredentialsDialog';
16
+ import { ClientPlugin } from '../ClientPlugin';
17
+ import translations from '../translations';
18
+
19
+ const Render = () => {
20
+ return (
21
+ <AlertDialog.Root open>
22
+ <AlertDialog.Overlay>
23
+ <ManageCredentialsDialog />
24
+ </AlertDialog.Overlay>
25
+ </AlertDialog.Root>
26
+ );
27
+ };
28
+
29
+ const meta: Meta = {
30
+ title: 'plugins/plugin-client/ManageCredentialsDialog',
31
+ component: ManageCredentialsDialog,
32
+ render: Render,
33
+ decorators: [
34
+ withPluginManager({
35
+ plugins: [
36
+ IntentPlugin(),
37
+ ClientPlugin({
38
+ onClientInitialized: async (_, client) => {
39
+ await client.halo.createIdentity();
40
+ },
41
+ }),
42
+ ],
43
+ }),
44
+ withTheme,
45
+ withLayout({ tooltips: true }),
46
+ ],
47
+ parameters: {
48
+ layout: 'fullscreen',
49
+ translations,
50
+ },
51
+ };
52
+
53
+ export default meta;
54
+
55
+ type Story = StoryObj<typeof ManageCredentialsDialog>;
56
+
57
+ export const Default: Story = {};