@dxos/plugin-client 0.10.0 → 0.11.1
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/ClientPlugin.mjs +65 -0
- package/dist/lib/ClientPlugin.mjs.map +1 -0
- package/dist/lib/ClientPlugin.node.mjs +904 -0
- package/dist/lib/ClientPlugin.node.mjs.map +1 -0
- package/dist/lib/ClientPlugin.workerd.mjs +10 -0
- package/dist/lib/ClientPlugin.workerd.mjs.map +1 -0
- package/dist/lib/capabilities/node.mjs +13 -0
- package/dist/lib/capabilities/node.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +23 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-AccountContainer.mjs +190 -0
- package/dist/lib/chunk-AccountContainer.mjs.map +1 -0
- package/dist/lib/chunk-DevicesContainer.mjs +275 -0
- package/dist/lib/chunk-DevicesContainer.mjs.map +1 -0
- package/dist/lib/chunk-InvitationsContainer.mjs +149 -0
- package/dist/lib/chunk-InvitationsContainer.mjs.map +1 -0
- package/dist/lib/chunk-JoinDialog.mjs +35 -0
- package/dist/lib/chunk-JoinDialog.mjs.map +1 -0
- package/dist/lib/chunk-ProfileContainer.mjs +145 -0
- package/dist/lib/chunk-ProfileContainer.mjs.map +1 -0
- package/dist/lib/chunk-RecoveryCodeDialog.mjs +72 -0
- package/dist/lib/chunk-RecoveryCodeDialog.mjs.map +1 -0
- package/dist/lib/chunk-RecoveryCredentialsContainer.mjs +65 -0
- package/dist/lib/chunk-RecoveryCredentialsContainer.mjs.map +1 -0
- package/dist/lib/chunk-ResetDialog.mjs +47 -0
- package/dist/lib/chunk-ResetDialog.mjs.map +1 -0
- package/dist/lib/chunk-UsageContainer.mjs +217 -0
- package/dist/lib/chunk-UsageContainer.mjs.map +1 -0
- package/dist/lib/{neutral/account-cache-YVQKLNV2.mjs → chunk-account-cache.mjs} +10 -12
- package/dist/lib/chunk-account-cache.mjs.map +1 -0
- package/dist/lib/chunk-app-graph-builder.mjs +111 -0
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
- package/dist/lib/chunk-client.mjs +90 -0
- package/dist/lib/chunk-client.mjs.map +1 -0
- package/dist/lib/{neutral/chunk-GSKMAS3S.mjs → chunk-constants.mjs} +5 -8
- package/dist/lib/chunk-constants.mjs.map +1 -0
- package/dist/lib/chunk-create-agent.mjs +23 -0
- package/dist/lib/chunk-create-agent.mjs.map +1 -0
- package/dist/lib/chunk-create-identity.mjs +28 -0
- package/dist/lib/chunk-create-identity.mjs.map +1 -0
- package/dist/lib/chunk-create-passkey.mjs +85 -0
- package/dist/lib/chunk-create-passkey.mjs.map +1 -0
- package/dist/lib/chunk-create-recovery-code.mjs +31 -0
- package/dist/lib/chunk-create-recovery-code.mjs.map +1 -0
- package/dist/lib/chunk-hooks.mjs +29 -0
- package/dist/lib/chunk-hooks.mjs.map +1 -0
- package/dist/lib/chunk-hub-http-client.mjs +14 -0
- package/dist/lib/chunk-hub-http-client.mjs.map +1 -0
- package/dist/lib/chunk-join-identity.mjs +20 -0
- package/dist/lib/chunk-join-identity.mjs.map +1 -0
- package/dist/lib/chunk-layer-specs.mjs +107 -0
- package/dist/lib/chunk-layer-specs.mjs.map +1 -0
- package/dist/lib/{neutral/chunk-6G4KZLBG.mjs → chunk-meta.mjs} +13 -20
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-migrations.mjs +29 -0
- package/dist/lib/chunk-migrations.mjs.map +1 -0
- package/dist/lib/chunk-navigation-handler.mjs +71 -0
- package/dist/lib/chunk-navigation-handler.mjs.map +1 -0
- package/dist/lib/chunk-navigation-target-loader.mjs +46 -0
- package/dist/lib/chunk-navigation-target-loader.mjs.map +1 -0
- package/dist/lib/chunk-open-usage.mjs +14 -0
- package/dist/lib/chunk-open-usage.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +11 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-operations.mjs +212 -0
- package/dist/lib/chunk-operations.mjs.map +1 -0
- package/dist/lib/chunk-progress.mjs +10 -0
- package/dist/lib/chunk-progress.mjs.map +1 -0
- package/dist/lib/chunk-react-context.mjs +30 -0
- package/dist/lib/chunk-react-context.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +70 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-recover-identity.mjs +17 -0
- package/dist/lib/chunk-recover-identity.mjs.map +1 -0
- package/dist/lib/chunk-redeem-passkey.mjs +72 -0
- package/dist/lib/chunk-redeem-passkey.mjs.map +1 -0
- package/dist/lib/chunk-redeem-token.mjs +24 -0
- package/dist/lib/chunk-redeem-token.mjs.map +1 -0
- package/dist/lib/chunk-remote-trace-monitor.mjs +37 -0
- package/dist/lib/chunk-remote-trace-monitor.mjs.map +1 -0
- package/dist/lib/chunk-reset-storage.mjs +17 -0
- package/dist/lib/chunk-reset-storage.mjs.map +1 -0
- package/dist/lib/chunk-resolve-navigation-targets.mjs +32 -0
- package/dist/lib/chunk-resolve-navigation-targets.mjs.map +1 -0
- package/dist/lib/chunk-schema-defs.mjs +53 -0
- package/dist/lib/chunk-schema-defs.mjs.map +1 -0
- package/dist/lib/chunk-share-identity.mjs +16 -0
- package/dist/lib/chunk-share-identity.mjs.map +1 -0
- package/dist/lib/chunk-space-replication-progress.mjs +94 -0
- package/dist/lib/chunk-space-replication-progress.mjs.map +1 -0
- package/dist/lib/chunk-trace-progress.mjs +69 -0
- package/dist/lib/chunk-trace-progress.mjs.map +1 -0
- package/dist/lib/chunk-update-profile.mjs +15 -0
- package/dist/lib/chunk-update-profile.mjs.map +1 -0
- package/dist/lib/components.mjs +0 -0
- package/dist/lib/containers.mjs +15 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/index.mjs +27 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +2 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/testing.mjs +21 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +111 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +139 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/ClientPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/account-cache.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/client.d.ts +1 -1
- package/dist/types/src/capabilities/client.d.ts.map +1 -1
- package/dist/types/src/capabilities/hub-http-client.d.ts +1 -1
- package/dist/types/src/capabilities/index.d.ts +6 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/layer-specs.d.ts +1 -1
- package/dist/types/src/capabilities/migrations.d.ts +1 -1
- package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts +3 -1
- package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts.map +1 -1
- package/dist/types/src/capabilities/navigation-target-loader.d.ts +11 -0
- package/dist/types/src/capabilities/navigation-target-loader.d.ts.map +1 -0
- package/dist/types/src/capabilities/node.d.ts +2 -1
- package/dist/types/src/capabilities/node.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/react-context.d.ts +1 -1
- package/dist/types/src/capabilities/react-context.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/remote-trace-monitor.d.ts +15 -0
- package/dist/types/src/capabilities/remote-trace-monitor.d.ts.map +1 -0
- package/dist/types/src/capabilities/schema-defs.d.ts +1 -1
- package/dist/types/src/capabilities/space-replication-progress.d.ts +11 -0
- package/dist/types/src/capabilities/space-replication-progress.d.ts.map +1 -0
- package/dist/types/src/capabilities/trace-progress.d.ts +17 -0
- package/dist/types/src/capabilities/trace-progress.d.ts.map +1 -0
- package/dist/types/src/commands/account/login/login.d.ts +2 -1
- package/dist/types/src/commands/account/login/login.d.ts.map +1 -1
- package/dist/types/src/commands/account/logout/logout.d.ts +1 -1
- package/dist/types/src/commands/account/logout/logout.d.ts.map +1 -1
- package/dist/types/src/commands/halo/index.d.ts.map +1 -1
- package/dist/types/src/commands/halo/share/share.d.ts.map +1 -1
- package/dist/types/src/commands/profile/create.d.ts.map +1 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts +4 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts.map +1 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts +4 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts.map +1 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts +4 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts.map +1 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.d.ts +4 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.d.ts.map +1 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.stories.d.ts +4 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.stories.d.ts.map +1 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts +4 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts.map +1 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.d.ts +4 -1
- package/dist/types/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.d.ts.map +1 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts +4 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts.map +1 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.d.ts +4 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.d.ts.map +1 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.stories.d.ts +4 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.stories.d.ts.map +1 -1
- package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts +4 -5
- package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts.map +1 -1
- package/dist/types/src/containers/UsageContainer/UsageView.d.ts +4 -7
- package/dist/types/src/containers/UsageContainer/UsageView.d.ts.map +1 -1
- package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts +4 -2
- package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts.map +1 -1
- package/dist/types/src/halo-services-layer.d.ts +19 -0
- package/dist/types/src/halo-services-layer.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/operations/definitions.d.ts +9 -1
- package/dist/types/src/operations/definitions.d.ts.map +1 -1
- package/dist/types/src/operations/errors.d.ts +109 -0
- package/dist/types/src/operations/errors.d.ts.map +1 -0
- package/dist/types/src/operations/errors.test.d.ts +2 -0
- package/dist/types/src/operations/errors.test.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +1 -0
- package/dist/types/src/operations/index.d.ts.map +1 -1
- package/dist/types/src/operations/redeem-passkey.d.ts.map +1 -1
- package/dist/types/src/operations/resolve-navigation-targets.d.ts +5 -0
- package/dist/types/src/operations/resolve-navigation-targets.d.ts.map +1 -0
- package/dist/types/src/operations/update-profile.d.ts +5 -0
- package/dist/types/src/operations/update-profile.d.ts.map +1 -0
- package/dist/types/src/progress/index.d.ts +2 -0
- package/dist/types/src/progress/index.d.ts.map +1 -0
- package/dist/types/src/progress/progress-keys.d.ts +6 -0
- package/dist/types/src/progress/progress-keys.d.ts.map +1 -0
- package/dist/types/src/testing/initializeIdentity.d.ts +5 -1
- package/dist/types/src/testing/initializeIdentity.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/capabilities.d.ts +10 -1
- package/dist/types/src/types/capabilities.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +6 -0
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +60 -55
- package/src/ClientPlugin.ts +25 -2
- package/src/capabilities/app-graph-builder.ts +1 -1
- package/src/capabilities/client.ts +26 -0
- package/src/capabilities/index.ts +10 -0
- package/src/capabilities/layer-specs.ts +64 -2
- package/src/capabilities/migrations.ts +9 -1
- package/src/capabilities/navigation-handler/navigation-handler.ts +20 -3
- package/src/capabilities/navigation-target-loader.ts +70 -0
- package/src/capabilities/react-context.tsx +20 -2
- package/src/capabilities/{react-surface.tsx → react-surface.ts} +14 -12
- package/src/capabilities/remote-trace-monitor.ts +62 -0
- package/src/capabilities/space-replication-progress.ts +143 -0
- package/src/capabilities/trace-progress.ts +84 -0
- package/src/commands/account/login/login.ts +48 -6
- package/src/commands/account/logout/logout.ts +4 -0
- package/src/commands/halo/index.ts +6 -2
- package/src/commands/halo/share/share.ts +16 -5
- package/src/commands/profile/create.ts +23 -40
- package/src/containers/AccountContainer/AccountContainer.tsx +6 -5
- package/src/containers/DevicesContainer/DevicesContainer.tsx +2 -0
- package/src/containers/InvitationsContainer/InvitationsContainer.tsx +2 -0
- package/src/containers/JoinDialog/JoinDialog.tsx +2 -0
- package/src/containers/ProfileContainer/ProfileContainer.tsx +19 -13
- package/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.tsx +2 -0
- package/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.tsx +7 -9
- package/src/containers/ResetDialog/ResetDialog.tsx +2 -0
- package/src/containers/UsageContainer/UsageContainer.tsx +10 -1
- package/src/containers/UsageContainer/UsageView.stories.tsx +0 -12
- package/src/containers/UsageContainer/UsageView.tsx +9 -10
- package/src/halo-services-layer.ts +29 -0
- package/src/index.ts +9 -0
- package/src/operations/definitions.ts +9 -1
- package/src/operations/errors.test.ts +27 -0
- package/src/operations/errors.ts +47 -0
- package/src/operations/index.ts +3 -0
- package/src/operations/open-usage.ts +3 -3
- package/src/operations/redeem-passkey.ts +85 -55
- package/src/operations/resolve-navigation-targets.ts +71 -0
- package/src/operations/share-identity.ts +3 -3
- package/src/operations/update-profile.ts +20 -0
- package/src/progress/index.ts +5 -0
- package/src/progress/progress-keys.ts +15 -0
- package/src/testing/initializeIdentity.ts +10 -2
- package/src/translations.ts +2 -0
- package/src/types/capabilities.ts +15 -1
- package/src/types/schema.ts +7 -0
- package/dist/lib/neutral/AccountContainer-7SF53UON.mjs +0 -186
- package/dist/lib/neutral/AccountContainer-7SF53UON.mjs.map +0 -7
- package/dist/lib/neutral/ClientPlugin.mjs +0 -72
- package/dist/lib/neutral/ClientPlugin.mjs.map +0 -7
- package/dist/lib/neutral/ClientPlugin.node.mjs +0 -1156
- package/dist/lib/neutral/ClientPlugin.node.mjs.map +0 -7
- package/dist/lib/neutral/ClientPlugin.workerd.mjs +0 -16
- package/dist/lib/neutral/ClientPlugin.workerd.mjs.map +0 -7
- package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs +0 -271
- package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs.map +0 -7
- package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs +0 -143
- package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs.map +0 -7
- package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs +0 -53
- package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs.map +0 -7
- package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs +0 -169
- package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs.map +0 -7
- package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs +0 -59
- package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs.map +0 -7
- package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs +0 -60
- package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs.map +0 -7
- package/dist/lib/neutral/ResetDialog-KUWX45JY.mjs +0 -57
- package/dist/lib/neutral/ResetDialog-KUWX45JY.mjs.map +0 -7
- package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs +0 -241
- package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs.map +0 -7
- package/dist/lib/neutral/account-cache-YVQKLNV2.mjs.map +0 -7
- package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs +0 -159
- package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/index.mjs +0 -34
- package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/node.mjs +0 -21
- package/dist/lib/neutral/capabilities/node.mjs.map +0 -7
- package/dist/lib/neutral/chunk-6G4KZLBG.mjs.map +0 -7
- package/dist/lib/neutral/chunk-7DEIKHB7.mjs +0 -199
- package/dist/lib/neutral/chunk-7DEIKHB7.mjs.map +0 -7
- package/dist/lib/neutral/chunk-A7WDDIWY.mjs +0 -162
- package/dist/lib/neutral/chunk-A7WDDIWY.mjs.map +0 -7
- package/dist/lib/neutral/chunk-GSKMAS3S.mjs.map +0 -7
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-JY35S2CN.mjs +0 -27
- package/dist/lib/neutral/chunk-JY35S2CN.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZFABGG2E.mjs +0 -8
- package/dist/lib/neutral/chunk-ZFABGG2E.mjs.map +0 -7
- package/dist/lib/neutral/client-7I5Y5SOA.mjs +0 -62
- package/dist/lib/neutral/client-7I5Y5SOA.mjs.map +0 -7
- package/dist/lib/neutral/components/index.mjs +0 -1
- package/dist/lib/neutral/components/index.mjs.map +0 -7
- package/dist/lib/neutral/containers/index.mjs +0 -25
- package/dist/lib/neutral/containers/index.mjs.map +0 -7
- package/dist/lib/neutral/create-agent-6UKJMZS5.mjs +0 -26
- package/dist/lib/neutral/create-agent-6UKJMZS5.mjs.map +0 -7
- package/dist/lib/neutral/create-identity-ULCRB3YH.mjs +0 -42
- package/dist/lib/neutral/create-identity-ULCRB3YH.mjs.map +0 -7
- package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs +0 -81
- package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs.map +0 -7
- package/dist/lib/neutral/create-recovery-code-DH2B6EGP.mjs +0 -36
- package/dist/lib/neutral/create-recovery-code-DH2B6EGP.mjs.map +0 -7
- package/dist/lib/neutral/hub-http-client-WOJUCDKV.mjs +0 -19
- package/dist/lib/neutral/hub-http-client-WOJUCDKV.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -25
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/join-identity-PJCHDNLH.mjs +0 -27
- package/dist/lib/neutral/join-identity-PJCHDNLH.mjs.map +0 -7
- package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs +0 -59
- package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/meta.mjs +0 -8
- package/dist/lib/neutral/meta.mjs.map +0 -7
- package/dist/lib/neutral/migrations-7FTW55ER.mjs +0 -23
- package/dist/lib/neutral/migrations-7FTW55ER.mjs.map +0 -7
- package/dist/lib/neutral/navigation-handler-MQ6MZXLX.mjs +0 -46
- package/dist/lib/neutral/navigation-handler-MQ6MZXLX.mjs.map +0 -7
- package/dist/lib/neutral/open-usage-OJVTPGOY.mjs +0 -27
- package/dist/lib/neutral/open-usage-OJVTPGOY.mjs.map +0 -7
- package/dist/lib/neutral/operation-handler-E3HB7E2T.mjs +0 -13
- package/dist/lib/neutral/operation-handler-E3HB7E2T.mjs.map +0 -7
- package/dist/lib/neutral/operations/index.mjs +0 -12
- package/dist/lib/neutral/operations/index.mjs.map +0 -7
- package/dist/lib/neutral/plugin.mjs +0 -17
- package/dist/lib/neutral/plugin.mjs.map +0 -7
- package/dist/lib/neutral/react-context-V74T6JOJ.mjs +0 -23
- package/dist/lib/neutral/react-context-V74T6JOJ.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-HPXRA65U.mjs +0 -75
- package/dist/lib/neutral/react-surface-HPXRA65U.mjs.map +0 -7
- package/dist/lib/neutral/recover-identity-B7YM2BR4.mjs +0 -26
- package/dist/lib/neutral/recover-identity-B7YM2BR4.mjs.map +0 -7
- package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs +0 -66
- package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs.map +0 -7
- package/dist/lib/neutral/redeem-token-2J33R3PV.mjs +0 -29
- package/dist/lib/neutral/redeem-token-2J33R3PV.mjs.map +0 -7
- package/dist/lib/neutral/reset-storage-K47HZTGP.mjs +0 -26
- package/dist/lib/neutral/reset-storage-K47HZTGP.mjs.map +0 -7
- package/dist/lib/neutral/schema-defs-IHBCKTMM.mjs +0 -51
- package/dist/lib/neutral/schema-defs-IHBCKTMM.mjs.map +0 -7
- package/dist/lib/neutral/share-identity-TOROBLWL.mjs +0 -31
- package/dist/lib/neutral/share-identity-TOROBLWL.mjs.map +0 -7
- package/dist/lib/neutral/testing/index.mjs +0 -25
- package/dist/lib/neutral/testing/index.mjs.map +0 -7
- package/dist/lib/neutral/translations.mjs +0 -124
- package/dist/lib/neutral/translations.mjs.map +0 -7
- package/dist/lib/neutral/types/index.mjs +0 -16
- package/dist/lib/neutral/types/index.mjs.map +0 -7
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { hexToEmoji, hexToHue } from "@dxos/util";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { meta } from "#meta";
|
|
4
|
+
import { ClientOperation } from "#operations";
|
|
5
|
+
import { useCallback, useMemo, useState } from "react";
|
|
6
|
+
import { useOperationInvoker } from "@dxos/app-framework/ui";
|
|
7
|
+
import { useIdentity } from "@dxos/halo-react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { ButtonGroup, Clipboard, Input, useTranslation } from "@dxos/react-ui";
|
|
10
|
+
import { Form } from "@dxos/react-ui-form";
|
|
11
|
+
import { debounce } from "@dxos/async";
|
|
12
|
+
import { EmojiPickerBlock, HuePicker } from "@dxos/react-ui-pickers";
|
|
13
|
+
//#region src/containers/ProfileContainer/ProfileContainer.tsx
|
|
14
|
+
var UserProfile = Schema.Struct({
|
|
15
|
+
did: Schema.String.annotations({ title: "DID" }),
|
|
16
|
+
displayName: Schema.String.annotations({ title: "Display name" }),
|
|
17
|
+
emoji: Schema.String.annotations({ title: "Avatar" }),
|
|
18
|
+
hue: Schema.String.annotations({ title: "Color" })
|
|
19
|
+
});
|
|
20
|
+
var getDefaultHueValue = (identity) => hexToHue(identity?.identityKey ?? "0");
|
|
21
|
+
var getHueValue = (identity) => identity?.data?.hue || getDefaultHueValue(identity);
|
|
22
|
+
var getDefaultEmojiValue = (identity) => hexToEmoji(identity?.identityKey ?? "0");
|
|
23
|
+
var getEmojiValue = (identity) => identity?.data?.emoji || getDefaultEmojiValue(identity);
|
|
24
|
+
var ProfileContainer = () => {
|
|
25
|
+
const { t } = useTranslation(meta.profile.key);
|
|
26
|
+
const { invokePromise } = useOperationInvoker();
|
|
27
|
+
const identity = useIdentity();
|
|
28
|
+
const [displayName, setDisplayNameDirectly] = useState(identity?.displayName ?? "");
|
|
29
|
+
const [emoji, setEmojiDirectly] = useState(getEmojiValue(identity));
|
|
30
|
+
const [hue, setHueDirectly] = useState(getHueValue(identity));
|
|
31
|
+
const updateProfile = useMemo(() => debounce((profile, currentData) => invokePromise(ClientOperation.UpdateProfile, {
|
|
32
|
+
displayName: profile.displayName,
|
|
33
|
+
data: {
|
|
34
|
+
...currentData,
|
|
35
|
+
emoji: profile.emoji,
|
|
36
|
+
hue: profile.hue
|
|
37
|
+
}
|
|
38
|
+
}), 2e3), [invokePromise]);
|
|
39
|
+
const handleChange = useCallback((profile, meta) => {
|
|
40
|
+
for (const [path, changed] of Object.entries(meta.changed)) if (changed) switch (path) {
|
|
41
|
+
case "displayName":
|
|
42
|
+
setDisplayNameDirectly(profile.displayName ?? "");
|
|
43
|
+
break;
|
|
44
|
+
case "emoji":
|
|
45
|
+
setEmojiDirectly(profile.emoji ?? getDefaultEmojiValue(identity));
|
|
46
|
+
break;
|
|
47
|
+
case "hue":
|
|
48
|
+
setHueDirectly(profile.hue ?? getDefaultHueValue(identity));
|
|
49
|
+
break;
|
|
50
|
+
default: break;
|
|
51
|
+
}
|
|
52
|
+
updateProfile(profile, identity?.data);
|
|
53
|
+
}, [identity, updateProfile]);
|
|
54
|
+
const values = useMemo(() => ({
|
|
55
|
+
did: identity?.did,
|
|
56
|
+
displayName,
|
|
57
|
+
emoji,
|
|
58
|
+
hue
|
|
59
|
+
}), [
|
|
60
|
+
identity,
|
|
61
|
+
displayName,
|
|
62
|
+
emoji,
|
|
63
|
+
hue
|
|
64
|
+
]);
|
|
65
|
+
const fieldMap = useMemo(() => ({
|
|
66
|
+
displayName: ({ type, label, getValue, onValueChange }) => {
|
|
67
|
+
const handleChange = useCallback(({ target: { value } }) => onValueChange(type, value), [onValueChange, type]);
|
|
68
|
+
return /* @__PURE__ */ jsx(Form.Row, {
|
|
69
|
+
label,
|
|
70
|
+
description: t("display-name.description"),
|
|
71
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
|
|
72
|
+
value: getValue(),
|
|
73
|
+
onChange: handleChange,
|
|
74
|
+
placeholder: t("display-name-input.placeholder"),
|
|
75
|
+
classNames: "min-w-64"
|
|
76
|
+
}) })
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
emoji: ({ type, label, getValue, onValueChange }) => {
|
|
80
|
+
const handleChange = useCallback((nextEmoji) => onValueChange(type, nextEmoji), [onValueChange, type]);
|
|
81
|
+
const handleEmojiReset = useCallback(() => onValueChange(type, getDefaultEmojiValue(identity)), [onValueChange, type]);
|
|
82
|
+
return /* @__PURE__ */ jsx(Form.Row, {
|
|
83
|
+
label,
|
|
84
|
+
description: t("icon.description"),
|
|
85
|
+
children: /* @__PURE__ */ jsx(EmojiPickerBlock, {
|
|
86
|
+
triggerVariant: "default",
|
|
87
|
+
emoji: getValue(),
|
|
88
|
+
onChangeEmoji: handleChange,
|
|
89
|
+
onClickClear: handleEmojiReset,
|
|
90
|
+
classNames: "justify-self-end"
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
hue: ({ type, label, getValue, onValueChange }) => {
|
|
95
|
+
const handleChange = useCallback((nextHue) => onValueChange(type, nextHue), [onValueChange, type]);
|
|
96
|
+
const handleHueReset = useCallback(() => onValueChange(type, getDefaultHueValue(identity)), [onValueChange, type]);
|
|
97
|
+
return /* @__PURE__ */ jsx(Form.Row, {
|
|
98
|
+
label,
|
|
99
|
+
description: t("hue.description"),
|
|
100
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
101
|
+
className: "flex justify-self-end",
|
|
102
|
+
children: /* @__PURE__ */ jsx(HuePicker, {
|
|
103
|
+
value: getValue(),
|
|
104
|
+
onChange: handleChange,
|
|
105
|
+
onReset: handleHueReset
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
did: ({ label, getValue }) => {
|
|
111
|
+
return /* @__PURE__ */ jsx(Form.Row, {
|
|
112
|
+
label,
|
|
113
|
+
description: t("did.description"),
|
|
114
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsxs(ButtonGroup, {
|
|
115
|
+
classNames: "w-full",
|
|
116
|
+
children: [/* @__PURE__ */ jsx(Input.TextInput, {
|
|
117
|
+
value: getValue(),
|
|
118
|
+
disabled: true,
|
|
119
|
+
classNames: "min-w-64"
|
|
120
|
+
}), /* @__PURE__ */ jsx(Clipboard.IconButton, { value: getValue() ?? "" })]
|
|
121
|
+
}) })
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}), [t]);
|
|
125
|
+
return /* @__PURE__ */ jsx(Clipboard.Provider, { children: /* @__PURE__ */ jsx(Form.Root, {
|
|
126
|
+
variant: "settings",
|
|
127
|
+
schema: UserProfile,
|
|
128
|
+
values,
|
|
129
|
+
fieldMap,
|
|
130
|
+
onValuesChanged: handleChange,
|
|
131
|
+
children: /* @__PURE__ */ jsx(Form.Viewport, {
|
|
132
|
+
scroll: true,
|
|
133
|
+
children: /* @__PURE__ */ jsx(Form.Content, { children: /* @__PURE__ */ jsx(Form.Section, {
|
|
134
|
+
title: t("profile.label"),
|
|
135
|
+
description: t("profile.description"),
|
|
136
|
+
children: /* @__PURE__ */ jsx(Form.FieldSet, {})
|
|
137
|
+
}) })
|
|
138
|
+
})
|
|
139
|
+
}) });
|
|
140
|
+
};
|
|
141
|
+
ProfileContainer.displayName = "ProfileContainer";
|
|
142
|
+
//#endregion
|
|
143
|
+
export { ProfileContainer as default };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=chunk-ProfileContainer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-ProfileContainer.mjs","names":[],"sources":["../../src/containers/ProfileContainer/ProfileContainer.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\nimport React, { type ChangeEvent, useCallback, useMemo, useState } from 'react';\n\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { debounce } from '@dxos/async';\nimport { type Identity } from '@dxos/halo';\nimport { useIdentity } from '@dxos/halo-react';\nimport { ButtonGroup, Clipboard, Input, useTranslation } from '@dxos/react-ui';\nimport { Form, type FormFieldMap, type FormUpdateMeta } from '@dxos/react-ui-form';\nimport { EmojiPickerBlock, HuePicker } from '@dxos/react-ui-pickers';\nimport { hexToEmoji, hexToHue } from '@dxos/util';\n\nimport { meta } from '#meta';\nimport { ClientOperation } from '#operations';\n\n// TOOD(burdon): Factor out?\n// TODO(wittjosiah): Integrate annotations with translations.\nconst UserProfile = Schema.Struct({\n did: Schema.String.annotations({ title: 'DID' }),\n displayName: Schema.String.annotations({ title: 'Display name' }),\n emoji: Schema.String.annotations({ title: 'Avatar' }),\n hue: Schema.String.annotations({ title: 'Color' }),\n});\n\ntype UserProfile = Schema.Schema.Type<typeof UserProfile>;\n\n// TODO(thure): Factor out?\nconst getDefaultHueValue = (identity?: Identity.Info): string => hexToHue(identity?.identityKey ?? '0');\nconst getHueValue = (identity?: Identity.Info): string => identity?.data?.hue || getDefaultHueValue(identity);\nconst getDefaultEmojiValue = (identity?: Identity.Info): string => hexToEmoji(identity?.identityKey ?? '0');\nconst getEmojiValue = (identity?: Identity.Info): string => identity?.data?.emoji || getDefaultEmojiValue(identity);\n\nexport const ProfileContainer = () => {\n const { t } = useTranslation(meta.profile.key);\n const { invokePromise } = useOperationInvoker();\n const identity = useIdentity();\n const [displayName, setDisplayNameDirectly] = useState(identity?.displayName ?? '');\n const [emoji, setEmojiDirectly] = useState<string>(getEmojiValue(identity));\n const [hue, setHueDirectly] = useState<string>(getHueValue(identity));\n\n const updateProfile = useMemo(\n () =>\n debounce(\n // Merge onto the current profile data so unrelated metadata is preserved.\n (profile: Partial<UserProfile>, currentData?: Record<string, unknown>) =>\n invokePromise(ClientOperation.UpdateProfile, {\n displayName: profile.displayName,\n data: {\n ...currentData,\n emoji: profile.emoji,\n hue: profile.hue,\n },\n }),\n 2_000,\n ),\n [invokePromise],\n );\n\n const handleChange = useCallback(\n (profile: Partial<UserProfile>, meta: FormUpdateMeta<UserProfile>) => {\n for (const [path, changed] of Object.entries(meta.changed)) {\n if (changed) {\n switch (path) {\n case 'displayName':\n setDisplayNameDirectly(profile.displayName ?? '');\n break;\n case 'emoji':\n setEmojiDirectly(profile.emoji ?? getDefaultEmojiValue(identity));\n break;\n case 'hue':\n setHueDirectly(profile.hue ?? getDefaultHueValue(identity));\n break;\n default:\n break;\n }\n }\n }\n\n void updateProfile(profile, identity?.data);\n },\n [identity, updateProfile],\n );\n\n const values = useMemo(\n () => ({\n did: identity?.did,\n displayName,\n emoji,\n hue,\n }),\n [identity, displayName, emoji, hue],\n );\n\n // TODO(wittjosiah): Integrate descriptions with the form schema.\n const fieldMap = useMemo<FormFieldMap>(\n () => ({\n displayName: ({ type, label, getValue, onValueChange }) => {\n const handleChange = useCallback(\n ({ target: { value } }: ChangeEvent<HTMLInputElement>) => onValueChange(type, value),\n [onValueChange, type],\n );\n\n return (\n <Form.Row label={label} description={t('display-name.description')}>\n <Input.Root>\n <Input.TextInput\n value={getValue()}\n onChange={handleChange}\n placeholder={t('display-name-input.placeholder')}\n classNames='min-w-64'\n />\n </Input.Root>\n </Form.Row>\n );\n },\n emoji: ({ type, label, getValue, onValueChange }) => {\n const handleChange = useCallback((nextEmoji: string) => onValueChange(type, nextEmoji), [onValueChange, type]);\n const handleEmojiReset = useCallback(\n () => onValueChange(type, getDefaultEmojiValue(identity)),\n [onValueChange, type],\n );\n\n return (\n <Form.Row label={label} description={t('icon.description')}>\n <EmojiPickerBlock\n triggerVariant='default'\n emoji={getValue()}\n onChangeEmoji={handleChange}\n onClickClear={handleEmojiReset}\n classNames='justify-self-end'\n />\n </Form.Row>\n );\n },\n hue: ({ type, label, getValue, onValueChange }) => {\n const handleChange = useCallback((nextHue: string) => onValueChange(type, nextHue), [onValueChange, type]);\n const handleHueReset = useCallback(\n () => onValueChange(type, getDefaultHueValue(identity)),\n [onValueChange, type],\n );\n\n return (\n <Form.Row label={label} description={t('hue.description')}>\n <div className='flex justify-self-end'>\n <HuePicker value={getValue()} onChange={handleChange} onReset={handleHueReset} />\n </div>\n </Form.Row>\n );\n },\n // TODO(wittjosiah): We need text input annotations for disabled and copyable.\n did: ({ label, getValue }) => {\n return (\n <Form.Row label={label} description={t('did.description')}>\n <Input.Root>\n <ButtonGroup classNames='w-full'>\n <Input.TextInput value={getValue()} disabled classNames='min-w-64' />\n <Clipboard.IconButton value={getValue() ?? ''} />\n </ButtonGroup>\n </Input.Root>\n </Form.Row>\n );\n },\n }),\n [t],\n );\n\n return (\n <Clipboard.Provider>\n <Form.Root\n variant='settings'\n schema={UserProfile}\n values={values}\n fieldMap={fieldMap}\n onValuesChanged={handleChange}\n >\n <Form.Viewport scroll>\n <Form.Content>\n <Form.Section title={t('profile.label')} description={t('profile.description')}>\n <Form.FieldSet />\n </Form.Section>\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n </Clipboard.Provider>\n );\n};\n\nProfileContainer.displayName = 'ProfileContainer';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { meta } from "#meta";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { AlertDialog, Button, Clipboard, Input, useTranslation } from "@dxos/react-ui";
|
|
5
|
+
//#region src/containers/RecoveryCodeDialog/RecoveryCodeDialog.tsx
|
|
6
|
+
var RecoveryCodeDialog = ({ code }) => {
|
|
7
|
+
const { t } = useTranslation(meta.profile.key);
|
|
8
|
+
const [confirmation, setConfirmation] = useState(false);
|
|
9
|
+
const handleConfirmation = useCallback((checked) => setConfirmation(checked), []);
|
|
10
|
+
return /* @__PURE__ */ jsxs(AlertDialog.Content, {
|
|
11
|
+
size: "md",
|
|
12
|
+
classNames: "min-h-[15rem]",
|
|
13
|
+
children: [/* @__PURE__ */ jsxs(AlertDialog.Body, { children: [
|
|
14
|
+
/* @__PURE__ */ jsx(AlertDialog.Title, { children: t("recovery-code-dialog.title") }),
|
|
15
|
+
/* @__PURE__ */ jsx(AlertDialog.Description, {
|
|
16
|
+
classNames: "py-4",
|
|
17
|
+
children: t("recovery-code-dialog.description")
|
|
18
|
+
}),
|
|
19
|
+
/* @__PURE__ */ jsx(Clipboard.Provider, { children: /* @__PURE__ */ jsx(Code, { code }) }),
|
|
20
|
+
/* @__PURE__ */ jsxs("div", {
|
|
21
|
+
className: "flex flex-col py-4 gap-2",
|
|
22
|
+
children: [/* @__PURE__ */ jsx("p", { children: t("recovery-code-dialog-warning-1.message") }), /* @__PURE__ */ jsx("p", { children: t("recovery-code-dialog-warning-2.message") })]
|
|
23
|
+
}),
|
|
24
|
+
/* @__PURE__ */ jsx("div", {
|
|
25
|
+
className: "flex items-center gap-2 pb-4",
|
|
26
|
+
children: /* @__PURE__ */ jsxs(Input.Root, { children: [/* @__PURE__ */ jsx(Input.Checkbox, {
|
|
27
|
+
"data-testid": "recoveryCode.confirm",
|
|
28
|
+
checked: confirmation,
|
|
29
|
+
onCheckedChange: handleConfirmation
|
|
30
|
+
}), /* @__PURE__ */ jsx(Input.Label, { children: t("recovery-code-confirmation.label") })] })
|
|
31
|
+
})
|
|
32
|
+
] }), /* @__PURE__ */ jsx(AlertDialog.ActionBar, { children: /* @__PURE__ */ jsx(AlertDialog.Action, {
|
|
33
|
+
asChild: true,
|
|
34
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
35
|
+
"data-testid": "recoveryCode.continue",
|
|
36
|
+
variant: "primary",
|
|
37
|
+
disabled: !confirmation,
|
|
38
|
+
children: t("continue.label")
|
|
39
|
+
})
|
|
40
|
+
}) })]
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
var Code = ({ code }) => {
|
|
44
|
+
const words = code.split(" ");
|
|
45
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
46
|
+
className: "relative p-2 border border-separator rounded-sm group",
|
|
47
|
+
children: [/* @__PURE__ */ jsx(Clipboard.IconButton, {
|
|
48
|
+
value: code,
|
|
49
|
+
classNames: "absolute top-2 right-2 invisible group-hover:visible"
|
|
50
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
51
|
+
className: "grid grid-cols-4",
|
|
52
|
+
children: words.map((word, i) => /* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: "flex items-center p-2 gap-2",
|
|
54
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
55
|
+
className: "w-4 text-xs text-center text-subdued",
|
|
56
|
+
children: i + 1
|
|
57
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
58
|
+
className: "text-sm",
|
|
59
|
+
children: word
|
|
60
|
+
})]
|
|
61
|
+
}, i))
|
|
62
|
+
})]
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
RecoveryCodeDialog.displayName = "RecoveryCodeDialog";
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/containers/RecoveryCodeDialog/index.ts
|
|
68
|
+
var RecoveryCodeDialog_default = RecoveryCodeDialog;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { RecoveryCodeDialog, RecoveryCodeDialog_default as default };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=chunk-RecoveryCodeDialog.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-RecoveryCodeDialog.mjs","names":[],"sources":["../../src/containers/RecoveryCodeDialog/RecoveryCodeDialog.tsx","../../src/containers/RecoveryCodeDialog/index.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { useCallback, useState } from 'react';\n\nimport { AlertDialog, Button, Clipboard, Input, useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\n\nexport type RecoveryCodeDialogProps = {\n code: string;\n};\n\n// TODO(burdon): Should have cancel button.\nexport const RecoveryCodeDialog = ({ code }: RecoveryCodeDialogProps) => {\n const { t } = useTranslation(meta.profile.key);\n const [confirmation, setConfirmation] = useState(false);\n\n const handleConfirmation = useCallback((checked: boolean) => setConfirmation(checked), []);\n\n return (\n <AlertDialog.Content size='md' classNames='min-h-[15rem]'>\n <AlertDialog.Body>\n <AlertDialog.Title>{t('recovery-code-dialog.title')}</AlertDialog.Title>\n <AlertDialog.Description classNames='py-4'>{t('recovery-code-dialog.description')}</AlertDialog.Description>\n <Clipboard.Provider>\n <Code code={code} />\n </Clipboard.Provider>\n <div className='flex flex-col py-4 gap-2'>\n <p>{t('recovery-code-dialog-warning-1.message')}</p>\n <p>{t('recovery-code-dialog-warning-2.message')}</p>\n </div>\n <div className='flex items-center gap-2 pb-4'>\n <Input.Root>\n <Input.Checkbox\n data-testid='recoveryCode.confirm'\n checked={confirmation}\n onCheckedChange={handleConfirmation}\n />\n <Input.Label>{t('recovery-code-confirmation.label')}</Input.Label>\n </Input.Root>\n </div>\n </AlertDialog.Body>\n <AlertDialog.ActionBar>\n <AlertDialog.Action asChild>\n <Button data-testid='recoveryCode.continue' variant='primary' disabled={!confirmation}>\n {t('continue.label')}\n </Button>\n </AlertDialog.Action>\n </AlertDialog.ActionBar>\n </AlertDialog.Content>\n );\n};\n\nconst Code = ({ code }: { code: string }) => {\n const words = code.split(' ');\n return (\n <div className='relative p-2 border border-separator rounded-sm group'>\n <Clipboard.IconButton value={code} classNames='absolute top-2 right-2 invisible group-hover:visible' />\n <div className='grid grid-cols-4'>\n {words.map((word, i) => (\n <div key={i} className='flex items-center p-2 gap-2'>\n <div className='w-4 text-xs text-center text-subdued'>{i + 1}</div>\n <div className='text-sm'>{word}</div>\n </div>\n ))}\n </div>\n </div>\n );\n};\n\nRecoveryCodeDialog.displayName = 'RecoveryCodeDialog';\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { RecoveryCodeDialog } from './RecoveryCodeDialog';\n\nexport * from './RecoveryCodeDialog';\nexport default RecoveryCodeDialog;\n"],"mappings":";;;;;AAeA,IAAa,sBAAsB,EAAE,WAAoC;CACvE,MAAM,EAAE,MAAM,eAAe,KAAK,QAAQ,GAAG;CAC7C,MAAM,CAAC,cAAc,mBAAmB,SAAS,KAAK;CAEtD,MAAM,qBAAqB,aAAa,YAAqB,gBAAgB,OAAO,GAAG,CAAC,CAAC;CAEzF,OACE,qBAAC,YAAY,SAAb;EAAqB,MAAK;EAAK,YAAW;YAA1C,CACE,qBAAC,YAAY,MAAb,EAAA,UAAA;GACE,oBAAC,YAAY,OAAb,EAAA,UAAoB,EAAE,4BAA4B,EAAqB,CAAA;GACvE,oBAAC,YAAY,aAAb;IAAyB,YAAW;cAAQ,EAAE,kCAAkC;GAA2B,CAAA;GAC3G,oBAAC,UAAU,UAAX,EAAA,UACE,oBAAC,MAAD,EAAY,KAAO,CAAA,EACD,CAAA;GACpB,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,KAAD,EAAA,UAAI,EAAE,wCAAwC,EAAK,CAAA,GACnD,oBAAC,KAAD,EAAA,UAAI,EAAE,wCAAwC,EAAK,CAAA,CAChD;;GACL,oBAAC,OAAD;IAAK,WAAU;cACb,qBAAC,MAAM,MAAP,EAAA,UAAA,CACE,oBAAC,MAAM,UAAP;KACE,eAAY;KACZ,SAAS;KACT,iBAAiB;IAClB,CAAA,GACD,oBAAC,MAAM,OAAP,EAAA,UAAc,EAAE,kCAAkC,EAAe,CAAA,CACvD,EAAA,CAAA;GACT,CAAA;EACW,EAAA,CAAA,GAClB,oBAAC,YAAY,WAAb,EAAA,UACE,oBAAC,YAAY,QAAb;GAAoB,SAAA;aAClB,oBAAC,QAAD;IAAQ,eAAY;IAAwB,SAAQ;IAAU,UAAU,CAAC;cACtE,EAAE,gBAAgB;GACb,CAAA;EACU,CAAA,EACC,CAAA,CACJ;;AAEzB;AAEA,IAAM,QAAQ,EAAE,WAA6B;CAC3C,MAAM,QAAQ,KAAK,MAAM,GAAG;CAC5B,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,UAAU,YAAX;GAAsB,OAAO;GAAM,YAAW;EAAwD,CAAA,GACtG,oBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,MAAM,MAChB,qBAAC,OAAD;IAAa,WAAU;cAAvB,CACE,oBAAC,OAAD;KAAK,WAAU;eAAwC,IAAI;IAAO,CAAA,GAClE,oBAAC,OAAD;KAAK,WAAU;eAAW;IAAU,CAAA,CACjC;MAHK,CAGL,CACN;EACE,CAAA,CACF;;AAET;AAEA,mBAAmB,cAAc;;;ACjEjC,IAAA,6BAAe"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { meta } from "#meta";
|
|
2
|
+
import { ClientOperation } from "#operations";
|
|
3
|
+
import "react";
|
|
4
|
+
import { useOperationInvoker } from "@dxos/app-framework/ui";
|
|
5
|
+
import { useCredentials } from "@dxos/halo-react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { Icon, IconButton, Message, useTranslation } from "@dxos/react-ui";
|
|
8
|
+
import { Form } from "@dxos/react-ui-form";
|
|
9
|
+
import { Listbox } from "@dxos/react-ui-list";
|
|
10
|
+
`${meta.profile.key}`;
|
|
11
|
+
var RecoveryCredentialsContainer = () => {
|
|
12
|
+
const { t } = useTranslation(meta.profile.key);
|
|
13
|
+
const { invokePromise } = useOperationInvoker();
|
|
14
|
+
const recoveryCredentials = useCredentials().filter((credential) => credential.type === "dxos.halo.credentials.IdentityRecovery");
|
|
15
|
+
return /* @__PURE__ */ jsx(Form.Root, {
|
|
16
|
+
variant: "settings",
|
|
17
|
+
children: /* @__PURE__ */ jsx(Form.Viewport, {
|
|
18
|
+
scroll: true,
|
|
19
|
+
children: /* @__PURE__ */ jsxs(Form.Content, { children: [/* @__PURE__ */ jsxs(Form.Section, {
|
|
20
|
+
title: t("recovery-setup-dialog.title"),
|
|
21
|
+
description: t("recovery-setup-dialog.description"),
|
|
22
|
+
children: [/* @__PURE__ */ jsx(Form.Row, {
|
|
23
|
+
label: t("create-passkey.label"),
|
|
24
|
+
description: t("create-passkey.description"),
|
|
25
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
26
|
+
label: t("create-passkey.label"),
|
|
27
|
+
icon: "ph--key--duotone",
|
|
28
|
+
variant: "primary",
|
|
29
|
+
onClick: () => invokePromise(ClientOperation.CreatePasskey)
|
|
30
|
+
})
|
|
31
|
+
}), /* @__PURE__ */ jsx(Form.Row, {
|
|
32
|
+
label: t("create-recovery-code.label"),
|
|
33
|
+
description: t("create-recovery-code.description"),
|
|
34
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
35
|
+
label: t("create-recovery-code.label"),
|
|
36
|
+
icon: "ph--receipt--duotone",
|
|
37
|
+
variant: "default",
|
|
38
|
+
onClick: () => invokePromise(ClientOperation.CreateRecoveryCode)
|
|
39
|
+
})
|
|
40
|
+
})]
|
|
41
|
+
}), /* @__PURE__ */ jsx(Form.Section, {
|
|
42
|
+
title: t("credentials-list.label"),
|
|
43
|
+
children: recoveryCredentials.length < 1 ? /* @__PURE__ */ jsxs(Message.Root, {
|
|
44
|
+
valence: "error",
|
|
45
|
+
children: [/* @__PURE__ */ jsx(Message.Title, {
|
|
46
|
+
icon: "ph--shield-warning--duotone",
|
|
47
|
+
children: t("no-credentials.title")
|
|
48
|
+
}), /* @__PURE__ */ jsx(Message.Body, { children: t("no-credentials.message") })]
|
|
49
|
+
}) : /* @__PURE__ */ jsx(Listbox.Root, { children: /* @__PURE__ */ jsx(Listbox.Content, {
|
|
50
|
+
classNames: "gap-1",
|
|
51
|
+
children: recoveryCredentials.map((credential, index) => /* @__PURE__ */ jsxs(Listbox.Item, {
|
|
52
|
+
id: credential.id ?? `${index}`,
|
|
53
|
+
classNames: "gap-2",
|
|
54
|
+
children: [/* @__PURE__ */ jsx(Icon, { icon: "ph--key--regular" }), /* @__PURE__ */ jsx(Listbox.ItemLabel, { children: credential.issuanceDate?.toLocaleString() })]
|
|
55
|
+
}, credential.id ?? index))
|
|
56
|
+
}) })
|
|
57
|
+
})] })
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
RecoveryCredentialsContainer.displayName = "RecoveryCredentialsContainer";
|
|
62
|
+
//#endregion
|
|
63
|
+
export { RecoveryCredentialsContainer as default };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=chunk-RecoveryCredentialsContainer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-RecoveryCredentialsContainer.mjs","names":[],"sources":["../../src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.tsx"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport React from 'react';\n\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { useCredentials } from '@dxos/halo-react';\nimport { Icon, IconButton, Message, useTranslation } from '@dxos/react-ui';\nimport { Form } from '@dxos/react-ui-form';\nimport { Listbox } from '@dxos/react-ui-list';\n\nimport { meta } from '#meta';\nimport { ClientOperation } from '#operations';\n\nexport const MANAGE_CREDENTIALS_DIALOG = `${meta.profile.key}.ManageCredentialsDialog`;\n\nexport const RecoveryCredentialsContainer = () => {\n const { t } = useTranslation(meta.profile.key);\n const { invokePromise } = useOperationInvoker();\n const credentials = useCredentials();\n const recoveryCredentials = credentials.filter(\n (credential) => credential.type === 'dxos.halo.credentials.IdentityRecovery',\n );\n\n return (\n <Form.Root variant='settings'>\n <Form.Viewport scroll>\n <Form.Content>\n <Form.Section title={t('recovery-setup-dialog.title')} description={t('recovery-setup-dialog.description')}>\n <Form.Row label={t('create-passkey.label')} description={t('create-passkey.description')}>\n <IconButton\n label={t('create-passkey.label')}\n icon='ph--key--duotone'\n variant='primary'\n onClick={() => invokePromise(ClientOperation.CreatePasskey)}\n />\n </Form.Row>\n <Form.Row label={t('create-recovery-code.label')} description={t('create-recovery-code.description')}>\n <IconButton\n label={t('create-recovery-code.label')}\n icon='ph--receipt--duotone'\n variant='default'\n onClick={() => invokePromise(ClientOperation.CreateRecoveryCode)}\n />\n </Form.Row>\n </Form.Section>\n <Form.Section title={t('credentials-list.label')}>\n {recoveryCredentials.length < 1 ? (\n <Message.Root valence='error'>\n <Message.Title icon='ph--shield-warning--duotone'>{t('no-credentials.title')}</Message.Title>\n <Message.Body>{t('no-credentials.message')}</Message.Body>\n </Message.Root>\n ) : (\n <Listbox.Root>\n <Listbox.Content classNames='gap-1'>\n {recoveryCredentials.map((credential, index) => (\n <Listbox.Item key={credential.id ?? index} id={credential.id ?? `${index}`} classNames='gap-2'>\n <Icon icon='ph--key--regular' />\n <Listbox.ItemLabel>{credential.issuanceDate?.toLocaleString()}</Listbox.ItemLabel>\n </Listbox.Item>\n ))}\n </Listbox.Content>\n </Listbox.Root>\n )}\n </Form.Section>\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n );\n};\n\nRecoveryCredentialsContainer.displayName = 'RecoveryCredentialsContainer';\n"],"mappings":";;;;;;;;;AAeyC,GAAG,KAAK,QAAQ,IAAhB;AAEzC,IAAa,qCAAqC;CAChD,MAAM,EAAE,MAAM,eAAe,KAAK,QAAQ,GAAG;CAC7C,MAAM,EAAE,kBAAkB,oBAAoB;CAE9C,MAAM,sBADc,eACQ,CAAA,CAAY,QACrC,eAAe,WAAW,SAAS,wCACtC;CAEA,OACE,oBAAC,KAAK,MAAN;EAAW,SAAQ;YACjB,oBAAC,KAAK,UAAN;GAAe,QAAA;aACb,qBAAC,KAAK,SAAN,EAAA,UAAA,CACE,qBAAC,KAAK,SAAN;IAAc,OAAO,EAAE,6BAA6B;IAAG,aAAa,EAAE,mCAAmC;cAAzG,CACE,oBAAC,KAAK,KAAN;KAAU,OAAO,EAAE,sBAAsB;KAAG,aAAa,EAAE,4BAA4B;eACrF,oBAAC,YAAD;MACE,OAAO,EAAE,sBAAsB;MAC/B,MAAK;MACL,SAAQ;MACR,eAAe,cAAc,gBAAgB,aAAa;KAC3D,CAAA;IACO,CAAA,GACV,oBAAC,KAAK,KAAN;KAAU,OAAO,EAAE,4BAA4B;KAAG,aAAa,EAAE,kCAAkC;eACjG,oBAAC,YAAD;MACE,OAAO,EAAE,4BAA4B;MACrC,MAAK;MACL,SAAQ;MACR,eAAe,cAAc,gBAAgB,kBAAkB;KAChE,CAAA;IACO,CAAA,CACE;OACd,oBAAC,KAAK,SAAN;IAAc,OAAO,EAAE,wBAAwB;cAC5C,oBAAoB,SAAS,IAC5B,qBAAC,QAAQ,MAAT;KAAc,SAAQ;eAAtB,CACE,oBAAC,QAAQ,OAAT;MAAe,MAAK;gBAA+B,EAAE,sBAAsB;KAAiB,CAAA,GAC5F,oBAAC,QAAQ,MAAT,EAAA,UAAe,EAAE,wBAAwB,EAAgB,CAAA,CAC7C;SAEd,oBAAC,QAAQ,MAAT,EAAA,UACE,oBAAC,QAAQ,SAAT;KAAiB,YAAW;eACzB,oBAAoB,KAAK,YAAY,UACpC,qBAAC,QAAQ,MAAT;MAA2C,IAAI,WAAW,MAAM,GAAG;MAAS,YAAW;gBAAvF,CACE,oBAAC,MAAD,EAAM,MAAK,mBAAoB,CAAA,GAC/B,oBAAC,QAAQ,WAAT,EAAA,UAAoB,WAAW,cAAc,eAAe,EAAqB,CAAA,CACrE;QAHK,WAAW,MAAM,KAGtB,CACf;IACc,CAAA,EACL,CAAA;GAEJ,CAAA,CACF,EAAA,CAAA;EACD,CAAA;CACN,CAAA;AAEf;AAEA,6BAA6B,cAAc"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Capability } from "@dxos/app-framework";
|
|
2
|
+
import { LayoutOperation } from "@dxos/app-toolkit";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { useCallback } from "react";
|
|
5
|
+
import { ConfirmReset, translationKey } from "@dxos/shell/react";
|
|
6
|
+
import { EffectEx } from "@dxos/effect";
|
|
7
|
+
import { useOperationInvoker } from "@dxos/app-framework/ui";
|
|
8
|
+
import { useClient } from "@dxos/react-client";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { Dialog, useTranslation } from "@dxos/react-ui";
|
|
11
|
+
//#region src/containers/ResetDialog/ResetDialog.tsx
|
|
12
|
+
var ResetDialog = ({ mode, onReset, onBeforeReset, capabilityManager }) => {
|
|
13
|
+
const { t } = useTranslation(translationKey);
|
|
14
|
+
const { invokePromise } = useOperationInvoker();
|
|
15
|
+
const client = useClient();
|
|
16
|
+
const handleReset = useCallback(async () => {
|
|
17
|
+
if (onBeforeReset) await onBeforeReset();
|
|
18
|
+
await client.reset();
|
|
19
|
+
const target = mode === "join-new-identity" ? "deviceInvitation" : mode === "recover" ? "recoverIdentity" : void 0;
|
|
20
|
+
if (onReset) await EffectEx.runAndForwardErrors(onReset({ target }).pipe(Effect.provideService(Capability.Service, capabilityManager)));
|
|
21
|
+
}, [
|
|
22
|
+
client,
|
|
23
|
+
mode,
|
|
24
|
+
onReset,
|
|
25
|
+
capabilityManager
|
|
26
|
+
]);
|
|
27
|
+
const handleCancel = useCallback(() => {
|
|
28
|
+
invokePromise(LayoutOperation.UpdateDialog, { state: false });
|
|
29
|
+
}, [invokePromise]);
|
|
30
|
+
return /* @__PURE__ */ jsxs(Dialog.Content, { children: [/* @__PURE__ */ jsx(Dialog.Header, { children: /* @__PURE__ */ jsx(Dialog.Title, { children: t("reset-dialog.title") }) }), /* @__PURE__ */ jsxs(Dialog.Body, { children: [/* @__PURE__ */ jsx(Dialog.Description, {
|
|
31
|
+
classNames: "sr-only",
|
|
32
|
+
children: t("reset-dialog.description")
|
|
33
|
+
}), /* @__PURE__ */ jsx(ConfirmReset, {
|
|
34
|
+
active: true,
|
|
35
|
+
mode,
|
|
36
|
+
onConfirm: handleReset,
|
|
37
|
+
onCancel: handleCancel
|
|
38
|
+
})] })] });
|
|
39
|
+
};
|
|
40
|
+
ResetDialog.displayName = "ResetDialog";
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/containers/ResetDialog/index.ts
|
|
43
|
+
var ResetDialog_default = ResetDialog;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { ResetDialog, ResetDialog_default as default };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=chunk-ResetDialog.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-ResetDialog.mjs","names":[],"sources":["../../src/containers/ResetDialog/ResetDialog.tsx","../../src/containers/ResetDialog/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport React, { useCallback } from 'react';\n\nimport { Capability, type CapabilityManager } from '@dxos/app-framework';\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { LayoutOperation } from '@dxos/app-toolkit';\nimport { EffectEx } from '@dxos/effect';\nimport { useClient } from '@dxos/react-client';\nimport { Dialog, useTranslation } from '@dxos/react-ui';\nimport { ConfirmReset, type ConfirmResetProps, translationKey } from '@dxos/shell/react';\n\nimport { type ClientPluginOptions } from '#types';\n\nexport type ResetDialogProps = Pick<ConfirmResetProps, 'mode'> &\n Pick<ClientPluginOptions, 'onReset'> & {\n capabilityManager: CapabilityManager.CapabilityManager;\n /**\n * Optional async action run before `client.reset()`. Throwing here aborts the\n * reset so callers can surface errors without wiping local state.\n */\n onBeforeReset?: () => Promise<void>;\n };\n\nexport const ResetDialog = ({ mode, onReset, onBeforeReset, capabilityManager }: ResetDialogProps) => {\n const { t } = useTranslation(translationKey);\n const { invokePromise } = useOperationInvoker();\n const client = useClient();\n\n const handleReset = useCallback(async () => {\n if (onBeforeReset) {\n await onBeforeReset();\n }\n await client.reset();\n const target =\n mode === 'join-new-identity' ? 'deviceInvitation' : mode === 'recover' ? 'recoverIdentity' : undefined;\n if (onReset) {\n await EffectEx.runAndForwardErrors(\n onReset({ target }).pipe(Effect.provideService(Capability.Service, capabilityManager)),\n );\n }\n }, [client, mode, onReset, capabilityManager]);\n\n const handleCancel = useCallback(() => {\n void invokePromise(LayoutOperation.UpdateDialog, { state: false });\n }, [invokePromise]);\n\n return (\n <Dialog.Content>\n <Dialog.Header>\n <Dialog.Title>{t('reset-dialog.title')}</Dialog.Title>\n </Dialog.Header>\n <Dialog.Body>\n <Dialog.Description classNames='sr-only'>{t('reset-dialog.description')}</Dialog.Description>\n <ConfirmReset active mode={mode} onConfirm={handleReset} onCancel={handleCancel} />\n </Dialog.Body>\n </Dialog.Content>\n );\n};\n\nResetDialog.displayName = 'ResetDialog';\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { ResetDialog } from './ResetDialog';\n\nexport * from './ResetDialog';\nexport default ResetDialog;\n"],"mappings":";;;;;;;;;;;AA2BA,IAAa,eAAe,EAAE,MAAM,SAAS,eAAe,wBAA0C;CACpG,MAAM,EAAE,MAAM,eAAe,cAAc;CAC3C,MAAM,EAAE,kBAAkB,oBAAoB;CAC9C,MAAM,SAAS,UAAU;CAEzB,MAAM,cAAc,YAAY,YAAY;EAC1C,IAAI,eACF,MAAM,cAAc;EAEtB,MAAM,OAAO,MAAM;EACnB,MAAM,SACJ,SAAS,sBAAsB,qBAAqB,SAAS,YAAY,oBAAoB,KAAA;EAC/F,IAAI,SACF,MAAM,SAAS,oBACb,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,WAAW,SAAS,iBAAiB,CAAC,CACvF;CAEJ,GAAG;EAAC;EAAQ;EAAM;EAAS;CAAiB,CAAC;CAE7C,MAAM,eAAe,kBAAkB;EACrC,cAAmB,gBAAgB,cAAc,EAAE,OAAO,MAAM,CAAC;CACnE,GAAG,CAAC,aAAa,CAAC;CAElB,OACE,qBAAC,OAAO,SAAR,EAAA,UAAA,CACE,oBAAC,OAAO,QAAR,EAAA,UACE,oBAAC,OAAO,OAAR,EAAA,UAAe,EAAE,oBAAoB,EAAgB,CAAA,EACxC,CAAA,GACf,qBAAC,OAAO,MAAR,EAAA,UAAA,CACE,oBAAC,OAAO,aAAR;EAAoB,YAAW;YAAW,EAAE,0BAA0B;CAAsB,CAAA,GAC5F,oBAAC,cAAD;EAAc,QAAA;EAAa;EAAM,WAAW;EAAa,UAAU;CAAe,CAAA,CACvE,EAAA,CAAA,CACC,EAAA,CAAA;AAEpB;AAEA,YAAY,cAAc;;;ACxD1B,IAAA,sBAAe"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { t as useHubHttpClient } from "./chunk-hooks.mjs";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { meta } from "#meta";
|
|
4
|
+
import { Context } from "@dxos/context";
|
|
5
|
+
import { log } from "@dxos/log";
|
|
6
|
+
import { useCallback, useMemo, useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { Message, Status, useAsyncEffect, useTranslation } from "@dxos/react-ui";
|
|
9
|
+
import { Form } from "@dxos/react-ui-form";
|
|
10
|
+
//#region src/containers/UsageContainer/UsageView.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Whether a usage row falls under a limit. Subtype segments are matched positionally against
|
|
13
|
+
* `subtypePattern` where `*` matches any segment.
|
|
14
|
+
*/
|
|
15
|
+
var usageMatchesLimit = (item, limit) => {
|
|
16
|
+
if (item.eventType !== limit.eventType || item.valueKey !== limit.valueKey) return false;
|
|
17
|
+
return limit.subtypePattern.every((pattern, index) => pattern === "*" || pattern === item.subtypePattern[index]);
|
|
18
|
+
};
|
|
19
|
+
/** Sum every usage item that falls under the limit. */
|
|
20
|
+
var sumUsageForLimit = (usage, limit) => usage.reduce((total, item) => usageMatchesLimit(item, limit) ? total + item.amount : total, 0);
|
|
21
|
+
/** Humanize a camelCase value key, e.g. `outputTokens` → `output tokens`. */
|
|
22
|
+
var formatValueKey = (valueKey) => valueKey.replace(/([A-Z])/g, " $1").trim().toLowerCase();
|
|
23
|
+
/**
|
|
24
|
+
* Human label for a limit: a scope followed by the value key, e.g.
|
|
25
|
+
* `{ eventType: 'ai', valueKey: 'outputTokens', subtypePattern: ['*'] }` → `All models · output tokens`,
|
|
26
|
+
* `{ eventType: 'ai', valueKey: 'inputTokens', subtypePattern: ['claude-opus-4'] }` → `claude-opus-4 · input tokens`.
|
|
27
|
+
*/
|
|
28
|
+
var formatLimitLabel = (limit, t) => {
|
|
29
|
+
const metric = formatValueKey(limit.valueKey);
|
|
30
|
+
const subtype = limit.subtypePattern.filter((segment) => segment && segment !== "*").join("/");
|
|
31
|
+
if (limit.eventType === "ai") return `${subtype || t("usage-all-models.label")} · ${metric}`;
|
|
32
|
+
return `${subtype ? `${limit.eventType}/${subtype}` : limit.eventType} · ${metric}`;
|
|
33
|
+
};
|
|
34
|
+
var formatAmount = (amount) => amount.toLocaleString();
|
|
35
|
+
var SECONDS_PER_HOUR = 3600;
|
|
36
|
+
/** Render `windowHours` as a localized "N months / days / hours" string. */
|
|
37
|
+
var formatWindow = (windowHours, t) => {
|
|
38
|
+
if (windowHours % 720 === 0) return t("usage-window-months.label", { count: windowHours / 720 });
|
|
39
|
+
if (windowHours % 24 === 0) return t("usage-window-days.label", { count: windowHours / 24 });
|
|
40
|
+
return t("usage-window-hours.label", { count: windowHours });
|
|
41
|
+
};
|
|
42
|
+
/** One display row per limit, shorter windows first. A `null` limit is reported as unlimited. */
|
|
43
|
+
var computeRows = (data, t) => data.limits.map((limit) => {
|
|
44
|
+
const used = sumUsageForLimit(data.usage, limit);
|
|
45
|
+
const label = formatLimitLabel(limit, t);
|
|
46
|
+
const windowHours = limit.windowDuration / SECONDS_PER_HOUR;
|
|
47
|
+
const window = formatWindow(windowHours, t);
|
|
48
|
+
const key = `${limit.eventType}/${limit.subtypePattern.join(",")}/${limit.valueKey}/${limit.windowDuration}/${limit.limit ?? "unlimited"}`;
|
|
49
|
+
if (limit.limit === null) return {
|
|
50
|
+
key,
|
|
51
|
+
label,
|
|
52
|
+
caption: t("usage-unlimited.description", {
|
|
53
|
+
used: formatAmount(used),
|
|
54
|
+
window
|
|
55
|
+
}),
|
|
56
|
+
windowHours
|
|
57
|
+
};
|
|
58
|
+
const percent = limit.limit > 0 ? Math.min(100, Math.round(used / limit.limit * 100)) : used > 0 ? 100 : 0;
|
|
59
|
+
return {
|
|
60
|
+
key,
|
|
61
|
+
label,
|
|
62
|
+
caption: `${t("usage-percent-used.label", { percent })} · ${t("usage-limit.description", {
|
|
63
|
+
used: formatAmount(used),
|
|
64
|
+
limit: formatAmount(limit.limit),
|
|
65
|
+
window
|
|
66
|
+
})}`,
|
|
67
|
+
percent,
|
|
68
|
+
windowHours
|
|
69
|
+
};
|
|
70
|
+
}).sort((left, right) => left.windowHours - right.windowHours);
|
|
71
|
+
/**
|
|
72
|
+
* A single usage field: an optional number carrying its label/caption as `title`/`description`
|
|
73
|
+
* annotations and the consumption percentage as its value. Optional so the form renders no required
|
|
74
|
+
* markers; `Schema.Number` keeps the field context-free (`R = never`) so the struct stays assignable to `Form`.
|
|
75
|
+
*/
|
|
76
|
+
var usageField = (title, description) => Schema.optional(Schema.Number.annotations({
|
|
77
|
+
title,
|
|
78
|
+
description
|
|
79
|
+
}));
|
|
80
|
+
/**
|
|
81
|
+
* Build the Effect schema (one annotated field per limit) and matching values. Unlimited limits have no
|
|
82
|
+
* value, so their field reads `undefined` and the renderer shows the "Unlimited" label instead of a meter.
|
|
83
|
+
*/
|
|
84
|
+
var buildUsageForm = (rows) => {
|
|
85
|
+
const fields = {};
|
|
86
|
+
const values = {};
|
|
87
|
+
rows.forEach((row, index) => {
|
|
88
|
+
const field = `metric_${index}`;
|
|
89
|
+
fields[field] = usageField(row.label, row.caption);
|
|
90
|
+
if (row.percent !== void 0) values[field] = row.percent;
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
schema: Schema.Struct(fields),
|
|
94
|
+
values
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/** Non-data states shown as a single `Message`, keyed by message variant (translation keys for the copy). */
|
|
98
|
+
var STATE_MESSAGES = {
|
|
99
|
+
unavailable: {
|
|
100
|
+
valence: "warning",
|
|
101
|
+
icon: "ph--cloud-slash--duotone",
|
|
102
|
+
title: "usage-unavailable.title",
|
|
103
|
+
description: "usage-unavailable.description"
|
|
104
|
+
},
|
|
105
|
+
error: {
|
|
106
|
+
valence: "error",
|
|
107
|
+
icon: "ph--cloud-x--duotone",
|
|
108
|
+
title: "usage-offline.title",
|
|
109
|
+
description: "usage-offline.description"
|
|
110
|
+
},
|
|
111
|
+
empty: {
|
|
112
|
+
valence: "neutral",
|
|
113
|
+
icon: "ph--chart-bar--duotone",
|
|
114
|
+
title: "usage-empty.title",
|
|
115
|
+
description: "usage-empty.description"
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Presentational view of profile usage: renders loading/error/unavailable/empty states, or a
|
|
120
|
+
* schema-driven settings form (one read-only field per limit), plus a refresh control and a
|
|
121
|
+
* collapsible raw-response viewer. Pure — all data via props.
|
|
122
|
+
*/
|
|
123
|
+
var UsageView = ({ state, data, lastUpdated, onRefresh }) => {
|
|
124
|
+
const { t } = useTranslation(meta.profile.key);
|
|
125
|
+
const [rawExpanded, setRawExpanded] = useState(false);
|
|
126
|
+
const { schema, values, empty } = useMemo(() => {
|
|
127
|
+
const rows = data ? computeRows(data, t) : [];
|
|
128
|
+
return {
|
|
129
|
+
...buildUsageForm(rows),
|
|
130
|
+
empty: rows.length === 0
|
|
131
|
+
};
|
|
132
|
+
}, [data, t]);
|
|
133
|
+
const message = state === "unavailable" ? STATE_MESSAGES.unavailable : state === "error" ? STATE_MESSAGES.error : state === "ready" && empty ? STATE_MESSAGES.empty : void 0;
|
|
134
|
+
const meterFieldProvider = useCallback(({ fieldProps: { label, description, getValue } }) => {
|
|
135
|
+
const percent = getValue();
|
|
136
|
+
return /* @__PURE__ */ jsx(Form.Row, {
|
|
137
|
+
label,
|
|
138
|
+
description,
|
|
139
|
+
children: typeof percent === "number" ? /* @__PURE__ */ jsx(Status, {
|
|
140
|
+
progress: percent / 100,
|
|
141
|
+
"aria-label": t("usage-percent-used.label", { percent })
|
|
142
|
+
}) : t("usage-unlimited.label")
|
|
143
|
+
});
|
|
144
|
+
}, [t]);
|
|
145
|
+
return /* @__PURE__ */ jsx(Form.Root, {
|
|
146
|
+
variant: "settings",
|
|
147
|
+
layout: "static",
|
|
148
|
+
schema,
|
|
149
|
+
values,
|
|
150
|
+
children: /* @__PURE__ */ jsx(Form.Viewport, {
|
|
151
|
+
scroll: true,
|
|
152
|
+
children: /* @__PURE__ */ jsx(Form.Content, { children: /* @__PURE__ */ jsx(Form.Section, {
|
|
153
|
+
title: t("usage-section.title"),
|
|
154
|
+
description: t("usage-section.description"),
|
|
155
|
+
children: message ? /* @__PURE__ */ jsxs(Message.Root, {
|
|
156
|
+
valence: message.valence,
|
|
157
|
+
children: [/* @__PURE__ */ jsx(Message.Title, {
|
|
158
|
+
icon: message.icon,
|
|
159
|
+
children: t(message.title)
|
|
160
|
+
}), /* @__PURE__ */ jsx(Message.Body, { children: t(message.description) })]
|
|
161
|
+
}) : /* @__PURE__ */ jsx(Form.FieldSet, { fieldProvider: meterFieldProvider })
|
|
162
|
+
}) })
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
UsageView.displayName = "UsageView";
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/containers/UsageContainer/UsageContainer.tsx
|
|
169
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-client/src/containers/UsageContainer/UsageContainer.tsx";
|
|
170
|
+
/**
|
|
171
|
+
* Fetches rolling-window profile usage from the hub service and renders it.
|
|
172
|
+
* Connected wrapper around the presentational {@link UsageView}.
|
|
173
|
+
*/
|
|
174
|
+
var UsageContainer = () => {
|
|
175
|
+
const hubHttp = useHubHttpClient();
|
|
176
|
+
const [fetchState, setFetchState] = useState({ state: "loading" });
|
|
177
|
+
const [lastUpdated, setLastUpdated] = useState();
|
|
178
|
+
const [refreshCount, setRefreshCount] = useState(0);
|
|
179
|
+
useAsyncEffect(async () => {
|
|
180
|
+
if (!hubHttp) {
|
|
181
|
+
setFetchState({ state: "unavailable" });
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
setFetchState((previous) => previous.state === "ready" ? previous : { state: "loading" });
|
|
185
|
+
try {
|
|
186
|
+
const result = await hubHttp.getProfileUsage(new Context(void 0, {
|
|
187
|
+
"~LogMeta": "~LogMeta",
|
|
188
|
+
F: __dxlog_file,
|
|
189
|
+
L: 43
|
|
190
|
+
}));
|
|
191
|
+
setFetchState({
|
|
192
|
+
state: "ready",
|
|
193
|
+
data: result
|
|
194
|
+
});
|
|
195
|
+
setLastUpdated(Date.now());
|
|
196
|
+
} catch (err) {
|
|
197
|
+
log.catch(err, void 0, {
|
|
198
|
+
"~LogMeta": "~LogMeta",
|
|
199
|
+
F: __dxlog_file,
|
|
200
|
+
L: 47,
|
|
201
|
+
S: void 0
|
|
202
|
+
});
|
|
203
|
+
setFetchState({ state: "error" });
|
|
204
|
+
}
|
|
205
|
+
}, [hubHttp, refreshCount]);
|
|
206
|
+
const handleRefresh = useCallback(() => setRefreshCount((count) => count + 1), []);
|
|
207
|
+
return /* @__PURE__ */ jsx(UsageView, {
|
|
208
|
+
...fetchState,
|
|
209
|
+
lastUpdated,
|
|
210
|
+
onRefresh: handleRefresh
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
UsageContainer.displayName = "UsageContainer";
|
|
214
|
+
//#endregion
|
|
215
|
+
export { UsageContainer as default };
|
|
216
|
+
|
|
217
|
+
//# sourceMappingURL=chunk-UsageContainer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-UsageContainer.mjs","names":[],"sources":["../../src/containers/UsageContainer/UsageView.tsx","../../src/containers/UsageContainer/UsageContainer.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\nimport React, { useCallback, useMemo, useState } from 'react';\n\nimport { type GetProfileUsageResponse, type MeteringLimit, type MeteringUsageItem } from '@dxos/protocols';\nimport { Message, Status, useTranslation } from '@dxos/react-ui';\nimport { Form, type FormFieldProvider } from '@dxos/react-ui-form';\n\nimport { meta } from '#meta';\n\nexport type UsageViewState = 'loading' | 'ready' | 'error' | 'unavailable';\n\ntype UsageViewCommonProps = {\n /** Epoch milliseconds of the last successful fetch. */\n lastUpdated?: number;\n /** Invoked when the user requests a refresh. */\n onRefresh?: () => void;\n};\n\n/**\n * Discriminated on `state`: the payload is required (and only present) when `state` is `'ready'`, so a\n * missing payload can't be silently rendered as an empty plan.\n */\nexport type UsageViewProps =\n | (UsageViewCommonProps & { state: Exclude<UsageViewState, 'ready'>; data?: undefined })\n | (UsageViewCommonProps & { state: 'ready'; data: GetProfileUsageResponse });\n\ntype TFunction = (key: string, options?: Record<string, unknown>) => string;\n\n/**\n * Whether a usage row falls under a limit. Subtype segments are matched positionally against\n * `subtypePattern` where `*` matches any segment.\n */\nconst usageMatchesLimit = (item: MeteringUsageItem, limit: MeteringLimit): boolean => {\n if (item.eventType !== limit.eventType || item.valueKey !== limit.valueKey) {\n return false;\n }\n return limit.subtypePattern.every((pattern, index) => pattern === '*' || pattern === item.subtypePattern[index]);\n};\n\n/** Sum every usage item that falls under the limit. */\nconst sumUsageForLimit = (usage: GetProfileUsageResponse['usage'], limit: MeteringLimit): number =>\n usage.reduce((total, item) => (usageMatchesLimit(item, limit) ? total + item.amount : total), 0);\n\n/** Humanize a camelCase value key, e.g. `outputTokens` → `output tokens`. */\nconst formatValueKey = (valueKey: string): string =>\n valueKey\n .replace(/([A-Z])/g, ' $1')\n .trim()\n .toLowerCase();\n\n/**\n * Human label for a limit: a scope followed by the value key, e.g.\n * `{ eventType: 'ai', valueKey: 'outputTokens', subtypePattern: ['*'] }` → `All models · output tokens`,\n * `{ eventType: 'ai', valueKey: 'inputTokens', subtypePattern: ['claude-opus-4'] }` → `claude-opus-4 · input tokens`.\n */\nconst formatLimitLabel = (limit: MeteringLimit, t: TFunction): string => {\n const metric = formatValueKey(limit.valueKey);\n const subtype = limit.subtypePattern.filter((segment) => segment && segment !== '*').join('/');\n if (limit.eventType === 'ai') {\n return `${subtype || t('usage-all-models.label')} · ${metric}`;\n }\n return `${subtype ? `${limit.eventType}/${subtype}` : limit.eventType} · ${metric}`;\n};\n\nconst formatAmount = (amount: number): string => amount.toLocaleString();\n\nconst SECONDS_PER_HOUR = 60 * 60;\n\n/** Render `windowHours` as a localized \"N months / days / hours\" string. */\nconst formatWindow = (windowHours: number, t: TFunction): string => {\n if (windowHours % (24 * 30) === 0) {\n return t('usage-window-months.label', { count: windowHours / (24 * 30) });\n }\n if (windowHours % 24 === 0) {\n return t('usage-window-days.label', { count: windowHours / 24 });\n }\n return t('usage-window-hours.label', { count: windowHours });\n};\n\ntype UsageRow = {\n key: string;\n /** Field label (schema `title` annotation). */\n label: string;\n /** Field caption (schema `description` annotation). */\n caption: string;\n /** Consumption as a whole percentage (`0`–`100`); `undefined` for an unlimited limit. */\n percent?: number;\n windowHours: number;\n};\n\n/** One display row per limit, shorter windows first. A `null` limit is reported as unlimited. */\nconst computeRows = (data: GetProfileUsageResponse, t: TFunction): UsageRow[] =>\n data.limits\n .map((limit): UsageRow => {\n const used = sumUsageForLimit(data.usage, limit);\n const label = formatLimitLabel(limit, t);\n const windowHours = limit.windowDuration / SECONDS_PER_HOUR;\n const window = formatWindow(windowHours, t);\n const key = `${limit.eventType}/${limit.subtypePattern.join(',')}/${limit.valueKey}/${limit.windowDuration}/${limit.limit ?? 'unlimited'}`;\n if (limit.limit === null) {\n return {\n key,\n label,\n caption: t('usage-unlimited.description', { used: formatAmount(used), window }),\n windowHours,\n };\n }\n const percent = limit.limit > 0 ? Math.min(100, Math.round((used / limit.limit) * 100)) : used > 0 ? 100 : 0;\n return {\n key,\n label,\n caption: `${t('usage-percent-used.label', { percent })} · ${t('usage-limit.description', { used: formatAmount(used), limit: formatAmount(limit.limit), window })}`,\n percent,\n windowHours,\n };\n })\n .sort((left, right) => left.windowHours - right.windowHours);\n\n/**\n * A single usage field: an optional number carrying its label/caption as `title`/`description`\n * annotations and the consumption percentage as its value. Optional so the form renders no required\n * markers; `Schema.Number` keeps the field context-free (`R = never`) so the struct stays assignable to `Form`.\n */\nconst usageField = (title: string, description: string) =>\n Schema.optional(Schema.Number.annotations({ title, description }));\n\n/**\n * Build the Effect schema (one annotated field per limit) and matching values. Unlimited limits have no\n * value, so their field reads `undefined` and the renderer shows the \"Unlimited\" label instead of a meter.\n */\nconst buildUsageForm = (rows: UsageRow[]) => {\n const fields: Record<string, ReturnType<typeof usageField>> = {};\n const values: Record<string, number> = {};\n rows.forEach((row, index) => {\n const field = `metric_${index}`;\n fields[field] = usageField(row.label, row.caption);\n if (row.percent !== undefined) {\n values[field] = row.percent;\n }\n });\n return { schema: Schema.Struct(fields), values };\n};\n\n/** Non-data states shown as a single `Message`, keyed by message variant (translation keys for the copy). */\nconst STATE_MESSAGES = {\n unavailable: {\n valence: 'warning',\n icon: 'ph--cloud-slash--duotone',\n title: 'usage-unavailable.title',\n description: 'usage-unavailable.description',\n },\n error: {\n valence: 'error',\n icon: 'ph--cloud-x--duotone',\n title: 'usage-offline.title',\n description: 'usage-offline.description',\n },\n empty: {\n valence: 'neutral',\n icon: 'ph--chart-bar--duotone',\n title: 'usage-empty.title',\n description: 'usage-empty.description',\n },\n} as const;\n\n/**\n * Presentational view of profile usage: renders loading/error/unavailable/empty states, or a\n * schema-driven settings form (one read-only field per limit), plus a refresh control and a\n * collapsible raw-response viewer. Pure — all data via props.\n */\nexport const UsageView = ({ state, data, lastUpdated, onRefresh }: UsageViewProps) => {\n const { t } = useTranslation(meta.profile.key);\n const [rawExpanded, setRawExpanded] = useState(false);\n\n const { schema, values, empty } = useMemo(() => {\n const rows = data ? computeRows(data, t) : [];\n return { ...buildUsageForm(rows), empty: rows.length === 0 };\n }, [data, t]);\n\n // Non-data states resolve to a `Message`; `loading` shows a spinner and `ready` with data shows the form.\n const message =\n state === 'unavailable'\n ? STATE_MESSAGES.unavailable\n : state === 'error'\n ? STATE_MESSAGES.error\n : state === 'ready' && empty\n ? STATE_MESSAGES.empty\n : undefined;\n\n // Render each metric as a meter from its percentage value; unlimited limits (no value) show a label.\n const meterFieldProvider = useCallback<FormFieldProvider>(\n ({ fieldProps: { label, description, getValue } }) => {\n const percent = getValue();\n return (\n <Form.Row label={label} description={description}>\n {typeof percent === 'number' ? (\n <Status progress={percent / 100} aria-label={t('usage-percent-used.label', { percent })} />\n ) : (\n t('usage-unlimited.label')\n )}\n </Form.Row>\n );\n },\n [t],\n );\n\n return (\n <Form.Root variant='settings' layout='static' schema={schema} values={values}>\n <Form.Viewport scroll>\n <Form.Content>\n <Form.Section title={t('usage-section.title')} description={t('usage-section.description')}>\n {message ? (\n <Message.Root valence={message.valence}>\n <Message.Title icon={message.icon}>{t(message.title)}</Message.Title>\n <Message.Body>{t(message.description)}</Message.Body>\n </Message.Root>\n ) : (\n <Form.FieldSet fieldProvider={meterFieldProvider} />\n )}\n </Form.Section>\n\n {/* {state === 'ready' && data && (\n <Form.Section>\n <Form.Row\n label={\n lastUpdated !== undefined\n ? t('usage-last-updated.label', { time: new Date(lastUpdated).toLocaleTimeString() })\n : t('usage-refresh.label')\n }\n >\n {onRefresh && (\n <IconButton\n iconOnly\n variant='ghost'\n icon='ph--arrow-clockwise--regular'\n label={t('usage-refresh.label')}\n onClick={onRefresh}\n />\n )}\n </Form.Row>\n </Form.Section>\n )} */}\n\n {/* {state === 'ready' && data && (\n <Form.Section>\n <Form.Row label={t('usage-raw-json.label')}>\n <ToggleIconButton\n iconOnly\n variant='ghost'\n active={rawExpanded}\n icon='ph--caret-right--regular'\n label={t('usage-raw-json.label')}\n onClick={() => setRawExpanded((value) => !value)}\n />\n </Form.Row>\n {rawExpanded && <JsonHighlighter data={data} testId='usage-raw-json' />}\n </Form.Section>\n )} */}\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n );\n};\n\nUsageView.displayName = 'UsageView';\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React, { useCallback, useState } from 'react';\n\nimport { Context } from '@dxos/context';\nimport { log } from '@dxos/log';\nimport { type GetProfileUsageResponse } from '@dxos/protocols';\nimport { useAsyncEffect } from '@dxos/react-ui';\n\nimport { useHubHttpClient } from '../../hooks';\nimport { UsageView, type UsageViewState } from './UsageView';\n\n/** State + payload kept together so `ready` always carries data (mirrors the discriminated `UsageViewProps`). */\ntype UsageFetchState =\n | {\n state: Exclude<UsageViewState, 'ready'>;\n }\n | {\n state: 'ready';\n data: GetProfileUsageResponse;\n };\n\n/**\n * Fetches rolling-window profile usage from the hub service and renders it.\n * Connected wrapper around the presentational {@link UsageView}.\n */\nexport const UsageContainer = () => {\n const hubHttp = useHubHttpClient();\n const [fetchState, setFetchState] = useState<UsageFetchState>({ state: 'loading' });\n const [lastUpdated, setLastUpdated] = useState<number | undefined>();\n const [refreshCount, setRefreshCount] = useState(0);\n\n useAsyncEffect(async () => {\n if (!hubHttp) {\n setFetchState({ state: 'unavailable' });\n return;\n }\n // Keep prior data visible while refetching; only show the loading state on first load.\n setFetchState((previous) => (previous.state === 'ready' ? previous : { state: 'loading' }));\n try {\n const result = await hubHttp.getProfileUsage(new Context());\n setFetchState({ state: 'ready', data: result });\n setLastUpdated(Date.now());\n } catch (err) {\n log.catch(err);\n setFetchState({ state: 'error' });\n }\n }, [hubHttp, refreshCount]);\n\n const handleRefresh = useCallback(() => setRefreshCount((count) => count + 1), []);\n\n return <UsageView {...fetchState} lastUpdated={lastUpdated} onRefresh={handleRefresh} />;\n};\n\nUsageContainer.displayName = 'UsageContainer';\n"],"mappings":"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"}
|