@dxos/shell 0.4.10-main.c32f430 → 0.4.10-main.c5e8686

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 (220) hide show
  1. package/dist/bundle/index-BhenUoML.mjs +94 -0
  2. package/dist/bundle/index-BhenUoML.mjs.map +1 -0
  3. package/dist/bundle/index-DLLGn1Su.mjs +96609 -0
  4. package/dist/bundle/index-DLLGn1Su.mjs.map +1 -0
  5. package/dist/bundle/shell.mjs +9 -81037
  6. package/dist/bundle/shell.mjs.map +1 -1
  7. package/dist/bundle/shell.umd.js +156 -88
  8. package/dist/bundle/shell.umd.js.map +1 -1
  9. package/dist/bundle/style.css +1 -1
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/react.mjs +1162 -1039
  12. package/dist/lib/browser/react.mjs.map +4 -4
  13. package/dist/lib/browser/testing/index.mjs +8 -8
  14. package/dist/lib/browser/testing/index.mjs.map +3 -3
  15. package/dist/types/src/components/AgentConfig.d.ts +3 -0
  16. package/dist/types/src/components/AgentConfig.d.ts.map +1 -0
  17. package/dist/types/src/components/AgentConfig.js +37 -0
  18. package/dist/types/src/components/AgentConfig.js.map +1 -0
  19. package/dist/types/src/components/DeviceList/DeviceList.d.ts +3 -0
  20. package/dist/types/src/components/DeviceList/DeviceList.d.ts.map +1 -0
  21. package/dist/types/src/components/DeviceList/DeviceList.js +29 -0
  22. package/dist/types/src/components/DeviceList/DeviceList.js.map +1 -0
  23. package/dist/types/src/components/DeviceList/DeviceListItem.d.ts +6 -0
  24. package/dist/types/src/components/DeviceList/DeviceListItem.d.ts.map +1 -0
  25. package/dist/types/src/components/DeviceList/DeviceListItem.js +72 -0
  26. package/dist/types/src/components/DeviceList/DeviceListItem.js.map +1 -0
  27. package/dist/types/src/components/DeviceList/DeviceListProps.d.ts +27 -0
  28. package/dist/types/src/components/DeviceList/DeviceListProps.d.ts.map +1 -0
  29. package/dist/types/src/components/DeviceList/DeviceListProps.js +6 -0
  30. package/dist/types/src/components/DeviceList/DeviceListProps.js.map +1 -0
  31. package/dist/types/src/components/DeviceList/index.d.ts +3 -0
  32. package/dist/types/src/components/DeviceList/index.d.ts.map +1 -0
  33. package/dist/types/src/components/DeviceList/index.js +22 -0
  34. package/dist/types/src/components/DeviceList/index.js.map +1 -0
  35. package/dist/types/src/components/EmojiPicker.d.ts +9 -2
  36. package/dist/types/src/components/EmojiPicker.d.ts.map +1 -1
  37. package/dist/types/src/components/EmojiPicker.js +57 -3
  38. package/dist/types/src/components/EmojiPicker.js.map +1 -1
  39. package/dist/types/src/components/HuePicker.d.ts +9 -2
  40. package/dist/types/src/components/HuePicker.d.ts.map +1 -1
  41. package/dist/types/src/components/HuePicker.js +75 -6
  42. package/dist/types/src/components/HuePicker.js.map +1 -1
  43. package/dist/types/src/components/IdentityList/index.d.ts +0 -1
  44. package/dist/types/src/components/IdentityList/index.d.ts.map +1 -1
  45. package/dist/types/src/components/IdentityList/index.js +0 -1
  46. package/dist/types/src/components/IdentityList/index.js.map +1 -1
  47. package/dist/types/src/components/InvitationList/InvitationListItem.js +5 -5
  48. package/dist/types/src/components/InvitationList/InvitationListItem.js.map +1 -1
  49. package/dist/types/src/components/Panel/Action.d.ts +2 -2
  50. package/dist/types/src/components/Viewport/Viewport.d.ts +1 -1
  51. package/dist/types/src/components/index.d.ts +2 -0
  52. package/dist/types/src/components/index.d.ts.map +1 -1
  53. package/dist/types/src/components/index.js +2 -0
  54. package/dist/types/src/components/index.js.map +1 -1
  55. package/dist/types/src/composites/IdentityDialog/IdentityDialog.js +1 -1
  56. package/dist/types/src/composites/IdentityDialog/IdentityDialog.js.map +1 -1
  57. package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -1
  58. package/dist/types/src/composites/Shell/Shell.js +14 -6
  59. package/dist/types/src/composites/Shell/Shell.js.map +1 -1
  60. package/dist/types/src/composites/StatusDialog/StatusDialog.d.ts +2 -0
  61. package/dist/types/src/composites/StatusDialog/StatusDialog.d.ts.map +1 -0
  62. package/dist/types/src/composites/StatusDialog/StatusDialog.js +22 -0
  63. package/dist/types/src/composites/StatusDialog/StatusDialog.js.map +1 -0
  64. package/dist/types/src/composites/StatusDialog/index.d.ts +2 -0
  65. package/dist/types/src/composites/StatusDialog/index.d.ts.map +1 -0
  66. package/dist/types/src/composites/StatusDialog/index.js +21 -0
  67. package/dist/types/src/composites/StatusDialog/index.js.map +1 -0
  68. package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts +1 -1
  69. package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts.map +1 -1
  70. package/dist/types/src/panels/IdentityPanel/IdentityPanel.js +89 -42
  71. package/dist/types/src/panels/IdentityPanel/IdentityPanel.js.map +1 -1
  72. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts +0 -3
  73. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts.map +1 -1
  74. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js +1 -43
  75. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js.map +1 -1
  76. package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts +11 -12
  77. package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts.map +1 -1
  78. package/dist/types/src/panels/IdentityPanel/identityMachine.d.ts +3 -1
  79. package/dist/types/src/panels/IdentityPanel/identityMachine.d.ts.map +1 -1
  80. package/dist/types/src/panels/IdentityPanel/identityMachine.js +13 -24
  81. package/dist/types/src/panels/IdentityPanel/identityMachine.js.map +1 -1
  82. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts +4 -4
  83. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts.map +1 -1
  84. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js +9 -39
  85. package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js.map +1 -1
  86. package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.d.ts +1 -1
  87. package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.d.ts.map +1 -1
  88. package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.js +2 -2
  89. package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.js.map +1 -1
  90. package/dist/types/src/panels/IdentityPanel/steps/index.d.ts +0 -3
  91. package/dist/types/src/panels/IdentityPanel/steps/index.d.ts.map +1 -1
  92. package/dist/types/src/panels/IdentityPanel/steps/index.js +0 -3
  93. package/dist/types/src/panels/IdentityPanel/steps/index.js.map +1 -1
  94. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.d.ts.map +1 -1
  95. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.js +5 -1
  96. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.js.map +1 -1
  97. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +1 -1
  98. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -1
  99. package/dist/types/src/panels/JoinPanel/JoinPanel.js +11 -5
  100. package/dist/types/src/panels/JoinPanel/JoinPanel.js.map +1 -1
  101. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.d.ts +1 -1
  102. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.d.ts.map +1 -1
  103. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.js +2 -1
  104. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.js.map +1 -1
  105. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +8 -5
  106. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -1
  107. package/dist/types/src/panels/JoinPanel/joinMachine.d.ts +3 -2
  108. package/dist/types/src/panels/JoinPanel/joinMachine.d.ts.map +1 -1
  109. package/dist/types/src/panels/JoinPanel/joinMachine.js +7 -0
  110. package/dist/types/src/panels/JoinPanel/joinMachine.js.map +1 -1
  111. package/dist/types/src/panels/JoinPanel/steps/index.d.ts +0 -1
  112. package/dist/types/src/panels/JoinPanel/steps/index.d.ts.map +1 -1
  113. package/dist/types/src/panels/JoinPanel/steps/index.js +0 -1
  114. package/dist/types/src/panels/JoinPanel/steps/index.js.map +1 -1
  115. package/dist/types/src/panels/SpacePanel/steps/SpaceManager.d.ts.map +1 -1
  116. package/dist/types/src/panels/SpacePanel/steps/SpaceManager.js +3 -1
  117. package/dist/types/src/panels/SpacePanel/steps/SpaceManager.js.map +1 -1
  118. package/dist/types/src/panels/StatusPanel/StatusPanel.d.ts +4 -0
  119. package/dist/types/src/panels/StatusPanel/StatusPanel.d.ts.map +1 -0
  120. package/dist/types/src/panels/StatusPanel/StatusPanel.js +19 -0
  121. package/dist/types/src/panels/StatusPanel/StatusPanel.js.map +1 -0
  122. package/dist/types/src/panels/StatusPanel/index.d.ts +2 -0
  123. package/dist/types/src/panels/StatusPanel/index.d.ts.map +1 -0
  124. package/dist/types/src/panels/StatusPanel/index.js +21 -0
  125. package/dist/types/src/panels/StatusPanel/index.js.map +1 -0
  126. package/dist/types/src/panels/index.d.ts +1 -0
  127. package/dist/types/src/panels/index.d.ts.map +1 -1
  128. package/dist/types/src/panels/index.js +1 -0
  129. package/dist/types/src/panels/index.js.map +1 -1
  130. package/dist/types/src/steps/ConfirmReset.d.ts +16 -0
  131. package/dist/types/src/steps/ConfirmReset.d.ts.map +1 -0
  132. package/dist/types/src/steps/ConfirmReset.js +71 -0
  133. package/dist/types/src/steps/ConfirmReset.js.map +1 -0
  134. package/dist/types/src/steps/InvitationManager.d.ts +1 -1
  135. package/dist/types/src/steps/InvitationManager.d.ts.map +1 -1
  136. package/dist/types/src/steps/InvitationManager.js +3 -4
  137. package/dist/types/src/steps/InvitationManager.js.map +1 -1
  138. package/dist/types/src/steps/StepProps.d.ts +4 -1
  139. package/dist/types/src/steps/StepProps.d.ts.map +1 -1
  140. package/dist/types/src/steps/index.d.ts +1 -0
  141. package/dist/types/src/steps/index.d.ts.map +1 -1
  142. package/dist/types/src/steps/index.js +1 -0
  143. package/dist/types/src/steps/index.js.map +1 -1
  144. package/dist/types/src/stories/Invitations.e2e-stories.d.ts.map +1 -1
  145. package/dist/types/src/stories/Invitations.e2e-stories.js +2 -2
  146. package/dist/types/src/stories/Invitations.e2e-stories.js.map +1 -1
  147. package/dist/types/src/testing/shell-manager.d.ts.map +1 -1
  148. package/dist/types/src/testing/shell-manager.js +8 -8
  149. package/dist/types/src/testing/shell-manager.js.map +1 -1
  150. package/dist/types/src/translations/locales/en-US.d.ts +16 -3
  151. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  152. package/dist/types/src/translations/locales/en-US.js +20 -7
  153. package/dist/types/src/translations/locales/en-US.js.map +1 -1
  154. package/dist/types/src/types/index.js +1 -0
  155. package/dist/types/src/types/index.js.map +1 -1
  156. package/package.json +21 -20
  157. package/src/components/AgentConfig.tsx +66 -0
  158. package/src/components/DeviceList/DeviceList.tsx +54 -0
  159. package/src/components/DeviceList/DeviceListItem.tsx +120 -0
  160. package/src/components/DeviceList/DeviceListProps.ts +33 -0
  161. package/src/components/DeviceList/index.ts +6 -0
  162. package/src/components/EmojiPicker.tsx +101 -4
  163. package/src/components/HuePicker.tsx +99 -5
  164. package/src/components/IdentityList/index.ts +0 -1
  165. package/src/components/InvitationList/InvitationListItem.tsx +5 -5
  166. package/src/components/index.ts +2 -0
  167. package/src/composites/IdentityDialog/IdentityDialog.tsx +1 -1
  168. package/src/composites/Shell/Shell.tsx +17 -5
  169. package/src/composites/StatusDialog/StatusDialog.tsx +24 -0
  170. package/src/composites/StatusDialog/index.ts +5 -0
  171. package/src/panels/IdentityPanel/IdentityPanel.stories.tsx +0 -63
  172. package/src/panels/IdentityPanel/IdentityPanel.tsx +149 -43
  173. package/src/panels/IdentityPanel/IdentityPanelProps.ts +15 -15
  174. package/src/panels/IdentityPanel/identityMachine.ts +17 -25
  175. package/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx +23 -67
  176. package/src/panels/IdentityPanel/steps/ProfileForm.tsx +12 -4
  177. package/src/panels/IdentityPanel/steps/index.ts +0 -3
  178. package/src/panels/IdentityPanel/useAgentHandlers.ts +5 -1
  179. package/src/panels/JoinPanel/JoinPanel.stories.tsx +3 -2
  180. package/src/panels/JoinPanel/JoinPanel.tsx +21 -9
  181. package/src/panels/JoinPanel/JoinPanelProps.ts +9 -5
  182. package/src/panels/JoinPanel/joinMachine.ts +12 -2
  183. package/src/panels/JoinPanel/steps/index.ts +0 -1
  184. package/src/panels/SpacePanel/steps/SpaceManager.tsx +3 -1
  185. package/src/panels/StatusPanel/StatusPanel.tsx +19 -0
  186. package/src/panels/StatusPanel/index.ts +5 -0
  187. package/src/panels/index.ts +1 -0
  188. package/src/steps/ConfirmReset.tsx +112 -0
  189. package/src/steps/InvitationManager.tsx +4 -4
  190. package/src/steps/StepProps.ts +2 -1
  191. package/src/steps/index.ts +1 -0
  192. package/src/stories/Invitations.e2e-stories.tsx +3 -3
  193. package/src/testing/shell-manager.ts +8 -8
  194. package/src/translations/locales/en-US.ts +22 -7
  195. package/src/types/index.ts +2 -0
  196. package/dist/types/src/components/IdentityList/DeviceList.d.ts +0 -7
  197. package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +0 -1
  198. package/dist/types/src/components/IdentityList/DeviceList.js +0 -34
  199. package/dist/types/src/components/IdentityList/DeviceList.js.map +0 -1
  200. package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts +0 -3
  201. package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts.map +0 -1
  202. package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js +0 -26
  203. package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js.map +0 -1
  204. package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts +0 -10
  205. package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts.map +0 -1
  206. package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js +0 -23
  207. package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js.map +0 -1
  208. package/dist/types/src/panels/IdentityPanel/steps/SignOutChooser.d.ts +0 -16
  209. package/dist/types/src/panels/IdentityPanel/steps/SignOutChooser.d.ts.map +0 -1
  210. package/dist/types/src/panels/IdentityPanel/steps/SignOutChooser.js +0 -40
  211. package/dist/types/src/panels/IdentityPanel/steps/SignOutChooser.js.map +0 -1
  212. package/dist/types/src/panels/JoinPanel/steps/ResetIdentity.d.ts +0 -15
  213. package/dist/types/src/panels/JoinPanel/steps/ResetIdentity.d.ts.map +0 -1
  214. package/dist/types/src/panels/JoinPanel/steps/ResetIdentity.js +0 -67
  215. package/dist/types/src/panels/JoinPanel/steps/ResetIdentity.js.map +0 -1
  216. package/src/components/IdentityList/DeviceList.tsx +0 -49
  217. package/src/panels/IdentityPanel/steps/AgentForm.tsx +0 -61
  218. package/src/panels/IdentityPanel/steps/DeviceManager.tsx +0 -41
  219. package/src/panels/IdentityPanel/steps/SignOutChooser.tsx +0 -77
  220. package/src/panels/JoinPanel/steps/ResetIdentity.tsx +0 -92
@@ -1,12 +1,21 @@
1
1
  //
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
- import { ArrowCounterClockwise, CaretDown, Check } from '@phosphor-icons/react';
4
+ import { ArrowCounterClockwise, CaretDown, Check, Palette } from '@phosphor-icons/react';
5
5
  import { useControllableState } from '@radix-ui/react-use-controllable-state';
6
- import React, { type Dispatch, type SetStateAction } from 'react';
6
+ import React, { type Dispatch, type SetStateAction, useRef, useState } from 'react';
7
7
 
8
- import { Button, useTranslation, Tooltip, DropdownMenu, useThemeContext, type ButtonProps } from '@dxos/react-ui';
9
- import { getSize, hueTokenThemes } from '@dxos/react-ui-theme';
8
+ import {
9
+ Button,
10
+ useTranslation,
11
+ Tooltip,
12
+ DropdownMenu,
13
+ useThemeContext,
14
+ type ButtonProps,
15
+ Toolbar,
16
+ type ThemedClassName,
17
+ } from '@dxos/react-ui';
18
+ import { getSize, hueTokenThemes, mx } from '@dxos/react-ui-theme';
10
19
 
11
20
  const HuePreview = ({ hue }: { hue: string }) => {
12
21
  const { tx } = useThemeContext();
@@ -25,7 +34,92 @@ export type HuePickerProps = {
25
34
  onClickClear?: ButtonProps['onClick'];
26
35
  };
27
36
 
28
- export const HuePicker = ({ disabled, hue, onChangeHue, defaultHue, onClickClear }: HuePickerProps) => {
37
+ /**
38
+ * A toolbar button for picking hue. Use only in `role=toolbar` elements. Unable to unset the value.
39
+ */
40
+ export const HuePickerToolbarButton = ({
41
+ disabled,
42
+ hue,
43
+ onChangeHue,
44
+ classNames,
45
+ defaultHue,
46
+ }: ThemedClassName<Omit<HuePickerProps, 'onClickClear'>>) => {
47
+ const { t } = useTranslation('os');
48
+
49
+ const [hueValue, setHueValue] = useControllableState<string>({
50
+ prop: hue,
51
+ onChange: onChangeHue,
52
+ defaultProp: defaultHue,
53
+ });
54
+
55
+ const [huePickerOpen, setHuePickerOpen] = useState<boolean>(false);
56
+
57
+ const suppressNextTooltip = useRef<boolean>(false);
58
+ const [triggerTooltipOpen, setTriggerTooltipOpen] = useState(false);
59
+
60
+ return (
61
+ <Tooltip.Root
62
+ open={triggerTooltipOpen}
63
+ onOpenChange={(nextOpen) => {
64
+ if (suppressNextTooltip.current) {
65
+ setTriggerTooltipOpen(false);
66
+ suppressNextTooltip.current = false;
67
+ } else {
68
+ setTriggerTooltipOpen(nextOpen);
69
+ }
70
+ }}
71
+ >
72
+ <DropdownMenu.Root
73
+ modal={false}
74
+ open={huePickerOpen}
75
+ onOpenChange={(nextOpen) => {
76
+ setHuePickerOpen(nextOpen);
77
+ suppressNextTooltip.current = true;
78
+ }}
79
+ >
80
+ <Tooltip.Trigger asChild>
81
+ <DropdownMenu.Trigger asChild>
82
+ <Toolbar.Button classNames={mx('gap-2 plb-1', classNames)} disabled={disabled}>
83
+ <span className='sr-only'>{t('select hue label')}</span>
84
+ <Palette className={getSize(5)} />
85
+ </Toolbar.Button>
86
+ </DropdownMenu.Trigger>
87
+ </Tooltip.Trigger>
88
+ <Tooltip.Portal>
89
+ <Tooltip.Content side='bottom' classNames='z-50'>
90
+ {t('select hue label')}
91
+ <Tooltip.Arrow />
92
+ </Tooltip.Content>
93
+ </Tooltip.Portal>
94
+ <DropdownMenu.Content side='bottom'>
95
+ <DropdownMenu.Viewport>
96
+ {Object.keys(hueTokenThemes).map((hue) => {
97
+ return (
98
+ <DropdownMenu.CheckboxItem
99
+ key={hue}
100
+ checked={hue === hueValue}
101
+ onCheckedChange={() => setHueValue(hue)}
102
+ >
103
+ <HuePreview hue={hue} />
104
+ <span className='grow'>{t(`${hue} label`)}</span>
105
+ <DropdownMenu.ItemIndicator>
106
+ <Check />
107
+ </DropdownMenu.ItemIndicator>
108
+ </DropdownMenu.CheckboxItem>
109
+ );
110
+ })}
111
+ </DropdownMenu.Viewport>
112
+ <DropdownMenu.Arrow />
113
+ </DropdownMenu.Content>
114
+ </DropdownMenu.Root>
115
+ </Tooltip.Root>
116
+ );
117
+ };
118
+
119
+ /**
120
+ * A button for picking hue alongside a button for unsetting it.
121
+ */
122
+ export const HuePickerBlock = ({ disabled, hue, onChangeHue, defaultHue, onClickClear }: HuePickerProps) => {
29
123
  const { t } = useTranslation('os');
30
124
 
31
125
  const [hueValue, setHueValue] = useControllableState<string>({
@@ -2,6 +2,5 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export * from './DeviceList';
6
5
  export * from './IdentityListItem';
7
6
  export * from './SpaceMemberList';
@@ -96,14 +96,14 @@ export const InvitationListItemImpl = ({
96
96
  ...props
97
97
  }: InvitationListItemImplProps) => {
98
98
  const { t } = useTranslation('os');
99
- const { cancel, status: invitationStatus, invitationCode, authCode, type } = propsInvitationStatus;
99
+ const { cancel, status: invitationStatus, invitationCode, authCode, multiUse } = propsInvitationStatus;
100
100
 
101
101
  const isCancellable = !(
102
102
  [Invitation.State.ERROR, Invitation.State.TIMEOUT, Invitation.State.CANCELLED].indexOf(invitationStatus) >= 0
103
103
  );
104
104
 
105
105
  const showShare =
106
- type === Invitation.Type.MULTIUSE ||
106
+ multiUse ||
107
107
  [Invitation.State.INIT, Invitation.State.CONNECTING, Invitation.State.CONNECTED].indexOf(invitationStatus) >= 0;
108
108
 
109
109
  const showAuthCode = invitationStatus === Invitation.State.READY_FOR_AUTHENTICATION;
@@ -147,9 +147,9 @@ export const InvitationListItemImpl = ({
147
147
  classNames={['flex gap-2 pis-3 pie-1 items-center relative', props.classNames]}
148
148
  >
149
149
  <ListItem.Heading classNames='sr-only'>
150
- {t(type === Invitation.Type.MULTIUSE ? 'invite many list item label' : 'invite one list item label')}
150
+ {t(multiUse ? 'invite many list item label' : 'invite one list item label')}
151
151
  </ListItem.Heading>
152
- {type === Invitation.Type.MULTIUSE && (
152
+ {multiUse && (
153
153
  <AvatarStackEffect status={avatarStatus} animation={avatarAnimation} reverseEffects={reverseEffects} />
154
154
  )}
155
155
  <Tooltip.Root>
@@ -162,7 +162,7 @@ export const InvitationListItemImpl = ({
162
162
  </Avatar.Root>
163
163
  <Tooltip.Portal>
164
164
  <Tooltip.Content side='left' classNames='z-[70]'>
165
- {t(type === Invitation.Type.MULTIUSE ? 'invite many qr label' : 'invite one qr label')}
165
+ {t(multiUse ? 'invite many qr label' : 'invite one qr label')}
166
166
  <Tooltip.Arrow />
167
167
  </Tooltip.Content>
168
168
  </Tooltip.Portal>
@@ -2,11 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './AgentConfig';
5
6
  export * from './Clipboard';
6
7
  export * from './CompoundButton';
7
8
  export * from './EmojiPicker';
8
9
  export * from './HuePicker';
9
10
  export * from './IdentityList';
11
+ export * from './DeviceList';
10
12
  export * from './InvitationList';
11
13
  export * from './Panel';
12
14
  export * from './Viewport';
@@ -16,7 +16,7 @@ export interface IdentityDialogProps
16
16
  }
17
17
 
18
18
  export const IdentityDialog = (props: IdentityDialogProps) => {
19
- const titleId = useId('spaceDialog__title');
19
+ const titleId = useId('identityDialog__title', props.title);
20
20
  return (
21
21
  <Dialog.Root defaultOpen onOpenChange={(open) => open || props.onDone?.()}>
22
22
  <Dialog.Portal>
@@ -11,6 +11,7 @@ import { useSpace } from '@dxos/react-client/echo';
11
11
  import { IdentityDialog } from '../IdentityDialog';
12
12
  import { JoinDialog } from '../JoinDialog';
13
13
  import { SpaceDialog } from '../SpaceDialog';
14
+ import { StatusDialog } from '../StatusDialog';
14
15
 
15
16
  export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: string }) => {
16
17
  const [{ layout, invitationCode, spaceKey, target }, setLayout] = useState<LayoutRequest>({
@@ -41,12 +42,18 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
41
42
  }, [runtime, layout, space]);
42
43
 
43
44
  switch (layout) {
45
+ case ShellLayout.STATUS:
46
+ return <StatusDialog />;
44
47
  case ShellLayout.INITIALIZE_IDENTITY:
48
+ case ShellLayout.INITIALIZE_IDENTITY_FROM_INVITATION:
45
49
  return (
46
50
  <JoinDialog
47
51
  mode='halo-only'
52
+ initialDisposition={
53
+ layout === ShellLayout.INITIALIZE_IDENTITY_FROM_INVITATION ? 'accept-halo-invitation' : 'default'
54
+ }
48
55
  initialInvitationCode={invitationCode}
49
- onCancelResetIdentity={() => runtime.setLayout({ layout: ShellLayout.IDENTITY })}
56
+ onCancelResetStorage={() => runtime.setLayout({ layout: ShellLayout.IDENTITY })}
50
57
  onDone={() => {
51
58
  void runtime.setAppContext({ display: ShellDisplay.NONE });
52
59
  runtime.setLayout({ layout: ShellLayout.DEFAULT });
@@ -54,22 +61,27 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
54
61
  />
55
62
  );
56
63
 
57
- // TODO(wittjosiah): Jump straight to specific step if SHARE or EDIT are specified.
58
64
  case ShellLayout.IDENTITY:
59
65
  case ShellLayout.SHARE_IDENTITY:
60
66
  case ShellLayout.EDIT_PROFILE:
61
67
  return (
62
68
  <IdentityDialog
63
69
  createInvitationUrl={(invitationCode) => `${origin}?deviceInvitationCode=${invitationCode}`}
64
- onResetDevice={async () => {
70
+ onResetStorage={async () => {
71
+ runtime.setLayout({ layout: ShellLayout.STATUS });
65
72
  await client.reset();
66
- await runtime.setAppContext({ display: ShellDisplay.NONE, reload: true });
73
+ return runtime.setAppContext({ display: ShellDisplay.NONE, reload: true });
74
+ }}
75
+ onJoinNewIdentity={async () => {
76
+ runtime.setLayout({ layout: ShellLayout.STATUS });
77
+ await client.reset();
78
+ return runtime.setLayout({ layout: ShellLayout.INITIALIZE_IDENTITY_FROM_INVITATION });
67
79
  }}
68
- onJoinNewIdentity={() => runtime.setLayout({ layout: ShellLayout.INITIALIZE_IDENTITY })}
69
80
  onDone={async () => {
70
81
  await runtime.setAppContext({ display: ShellDisplay.NONE });
71
82
  runtime.setLayout({ layout: ShellLayout.DEFAULT });
72
83
  }}
84
+ initialDisposition={layout === ShellLayout.SHARE_IDENTITY ? 'manage-device-invitation' : 'default'}
73
85
  />
74
86
  );
75
87
 
@@ -0,0 +1,24 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { AlertDialog, useId } from '@dxos/react-ui';
8
+
9
+ import { StatusPanel } from '../../panels';
10
+
11
+ export const StatusDialog = () => {
12
+ const titleId = useId('statusDialog__title');
13
+ return (
14
+ <AlertDialog.Root open>
15
+ <AlertDialog.Portal>
16
+ <AlertDialog.Overlay>
17
+ <AlertDialog.Content aria-labelledby={titleId}>
18
+ <StatusPanel titleId={titleId} />
19
+ </AlertDialog.Content>
20
+ </AlertDialog.Overlay>
21
+ </AlertDialog.Portal>
22
+ </AlertDialog.Root>
23
+ );
24
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './StatusDialog';
@@ -8,7 +8,6 @@ import React from 'react';
8
8
 
9
9
  import { faker } from '@dxos/random';
10
10
  import { PublicKey } from '@dxos/react-client';
11
- import { Device, DeviceKind } from '@dxos/react-client/halo';
12
11
  import { Invitation } from '@dxos/react-client/invitations';
13
12
  import { withTheme } from '@dxos/storybook-utils';
14
13
 
@@ -53,58 +52,6 @@ export const IdentityActionChooser = () => {
53
52
  );
54
53
  };
55
54
 
56
- export const UpdateProfileForm = () => {
57
- return (
58
- <StorybookDialog inOverlayLayout>
59
- <IdentityPanelImpl
60
- {...noOpProps}
61
- activeView='update profile form'
62
- IdentityActionChooser={IdentityActionChooserImpl}
63
- />
64
- </StorybookDialog>
65
- );
66
- };
67
-
68
- export const DeviceManager = () => {
69
- return (
70
- <StorybookDialog inOverlayLayout>
71
- <IdentityPanelImpl
72
- {...{
73
- ...noOpProps,
74
- // TODO(nf): update
75
- devices: [
76
- {
77
- deviceKey: PublicKey.random(),
78
- kind: DeviceKind.CURRENT,
79
- presence: Device.PresenceState.ONLINE,
80
- },
81
- {
82
- deviceKey: PublicKey.random(),
83
- kind: DeviceKind.TRUSTED,
84
- presence: Device.PresenceState.ONLINE,
85
- },
86
- {
87
- deviceKey: PublicKey.random(),
88
- kind: DeviceKind.TRUSTED,
89
- presence: Device.PresenceState.OFFLINE,
90
- },
91
- ],
92
- }}
93
- activeView='device manager'
94
- IdentityActionChooser={IdentityActionChooserImpl}
95
- />
96
- </StorybookDialog>
97
- );
98
- };
99
-
100
- export const DeviceManagerEmpty = () => {
101
- return (
102
- <StorybookDialog inOverlayLayout>
103
- <IdentityPanelImpl {...noOpProps} activeView='device manager' IdentityActionChooser={IdentityActionChooserImpl} />
104
- </StorybookDialog>
105
- );
106
- };
107
-
108
55
  const DeviceInvitationManagerWithState = (extraProps: InvitationManagerProps) => (
109
56
  <StorybookDialog inOverlayLayout>
110
57
  <IdentityPanelImpl
@@ -156,13 +103,3 @@ export const DeviceInvitationManagerCancelled = () =>
156
103
  DeviceInvitationManagerWithState({
157
104
  status: Invitation.State.CANCELLED,
158
105
  });
159
-
160
- // export const IdentityActionChooser = {
161
- // decorators: [ClientDecorator()],
162
- // args: { activeView: 'identity action chooser' },
163
- // };
164
-
165
- // export const DeviceManager = {
166
- // decorators: [ClientDecorator()],
167
- // args: { activeView: 'device manager' },
168
- // };
@@ -1,7 +1,8 @@
1
1
  //
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
- import React, { useEffect, useMemo } from 'react';
4
+ import { CopySimple, Plugs, PlugsConnected } from '@phosphor-icons/react';
5
+ import React, { useEffect, useMemo, useState } from 'react';
5
6
 
6
7
  import { generateName } from '@dxos/display-name';
7
8
  import { log } from '@dxos/log';
@@ -9,8 +10,10 @@ import { useClient } from '@dxos/react-client';
9
10
  import { type Identity, useIdentity, useDevices, useHaloInvitations } from '@dxos/react-client/halo';
10
11
  import { useInvitationStatus } from '@dxos/react-client/invitations';
11
12
  import type { CancellableInvitationObservable } from '@dxos/react-client/invitations';
12
- import { Avatar, DensityProvider, useId, useTranslation } from '@dxos/react-ui';
13
- import { keyToFallback } from '@dxos/util';
13
+ import { useNetworkStatus, ConnectionState } from '@dxos/react-client/mesh';
14
+ import { Avatar, DensityProvider, Input, Toolbar, Tooltip, useId, useTranslation } from '@dxos/react-ui';
15
+ import { getSize } from '@dxos/react-ui-theme';
16
+ import { hexToEmoji, hexToHue, keyToFallback } from '@dxos/util';
14
17
 
15
18
  import {
16
19
  type IdentityPanelHeadingProps,
@@ -18,24 +21,122 @@ import {
18
21
  type IdentityPanelProps,
19
22
  } from './IdentityPanelProps';
20
23
  import { useIdentityMachine } from './identityMachine';
21
- import { AgentForm, DeviceManager, IdentityActionChooser, ProfileForm, SignOutChooser } from './steps';
24
+ import { IdentityActionChooser } from './steps';
22
25
  import { useAgentHandlers } from './useAgentHandlers';
23
- import { Viewport, Heading, CloseButton } from '../../components';
24
- import { InvitationManager } from '../../steps';
26
+ import {
27
+ Viewport,
28
+ Heading,
29
+ CloseButton,
30
+ EmojiPickerToolbarButton,
31
+ useClipboardContext,
32
+ HuePickerToolbarButton,
33
+ } from '../../components';
34
+ import { ConfirmReset, InvitationManager } from '../../steps';
25
35
 
26
36
  const viewStyles = 'pbs-1 pbe-3 pli-3';
27
37
 
28
- const IdentityHeading = ({ titleId, title, identity, onDone }: IdentityPanelHeadingProps) => {
38
+ // TODO(thure): Factor out?
39
+ const getHueValue = (identity?: Identity) =>
40
+ identity?.profile?.data?.hue || hexToHue(identity?.identityKey.toHex() ?? '0');
41
+ const getEmojiValue = (identity?: Identity) =>
42
+ identity?.profile?.data?.emoji || hexToEmoji(identity?.identityKey.toHex() ?? '0');
43
+
44
+ const IdentityHeading = ({
45
+ titleId,
46
+ title,
47
+ identity,
48
+ onDone,
49
+ onUpdateProfile,
50
+ connectionState,
51
+ onChangeConnectionState,
52
+ }: IdentityPanelHeadingProps) => {
29
53
  const fallbackValue = keyToFallback(identity.identityKey);
54
+ const { t } = useTranslation('os');
55
+ const [displayName, setDisplayName] = useState(identity.profile?.displayName ?? '');
56
+ const [emoji, setEmoji] = useState<string>(getEmojiValue(identity));
57
+ const [hue, setHue] = useState<string>(getHueValue(identity));
58
+ const { textValue, setTextValue } = useClipboardContext();
59
+ const identityHex = identity.identityKey.toHex();
60
+ const copied = textValue === identityHex;
61
+
62
+ const handleUpdateProfile = async () => {
63
+ const nextProfile = {
64
+ ...(displayName && { displayName }),
65
+ ...((emoji || hue) && { data: { ...(emoji && { emoji }), ...(hue && { hue }) } }),
66
+ };
67
+ return onUpdateProfile?.(nextProfile);
68
+ };
69
+
70
+ useEffect(() => {
71
+ void handleUpdateProfile();
72
+ }, [emoji, hue]);
73
+
74
+ const isConnected = connectionState === ConnectionState.ONLINE;
75
+
30
76
  return (
31
77
  <Heading titleId={titleId} title={title} corner={<CloseButton onDone={onDone} />}>
32
- <Avatar.Root size={12} variant='circle' status='active' hue={identity.profile?.data?.hue || fallbackValue.hue}>
33
- <Avatar.Frame classNames='block mbs-4 mbe-2 mli-auto chromatic-ignore'>
34
- <Avatar.Fallback text={identity.profile?.data?.emoji || fallbackValue.emoji} />
35
- </Avatar.Frame>
36
- <Avatar.Label classNames='block text-center font-light text-xl' data-testid='identityHeading.displayName'>
78
+ <Avatar.Root size={16} variant='circle' status={isConnected ? 'active' : 'error'} hue={hue || fallbackValue.hue}>
79
+ <Toolbar.Root classNames='grid grid-cols-[1fr_var(--rail-action)_min-content_var(--rail-action)_1fr] items-center gap-2'>
80
+ <Tooltip.Root>
81
+ <Tooltip.Trigger asChild>
82
+ <Toolbar.Button
83
+ classNames='bs-[--rail-action] is-[--rail-action] justify-self-end'
84
+ onClick={() =>
85
+ onChangeConnectionState?.(isConnected ? ConnectionState.OFFLINE : ConnectionState.ONLINE)
86
+ }
87
+ >
88
+ <span className='sr-only'>{t(isConnected ? 'disconnect label' : 'connect label')}</span>
89
+ {isConnected ? <PlugsConnected className={getSize(5)} /> : <Plugs className={getSize(5)} />}
90
+ </Toolbar.Button>
91
+ </Tooltip.Trigger>
92
+ <Tooltip.Portal>
93
+ <Tooltip.Content side='bottom' classNames='z-50'>
94
+ {t(isConnected ? 'disconnect label' : 'connect label')}
95
+ <Tooltip.Arrow />
96
+ </Tooltip.Content>
97
+ </Tooltip.Portal>
98
+ </Tooltip.Root>
99
+ <EmojiPickerToolbarButton emoji={emoji} onChangeEmoji={setEmoji} classNames='bs-[--rail-action]' />
100
+ <Avatar.Frame classNames='relative z-[2] -mli-4 chromatic-ignore'>
101
+ <Avatar.Fallback text={emoji || fallbackValue.emoji} />
102
+ </Avatar.Frame>
103
+ <HuePickerToolbarButton hue={hue} onChangeHue={setHue} classNames='bs-[--rail-action]' />
104
+ <Tooltip.Root>
105
+ <Tooltip.Trigger asChild>
106
+ <Toolbar.Button
107
+ classNames='bs-[--rail-action] justify-self-start'
108
+ data-testid='update-profile-form-copy-key'
109
+ onClick={() => setTextValue(identityHex)}
110
+ >
111
+ <span className='sr-only'>{t(copied ? 'copy success label' : 'copy self public key label')}</span>
112
+ <CopySimple className={getSize(5)} />
113
+ </Toolbar.Button>
114
+ </Tooltip.Trigger>
115
+ <Tooltip.Portal>
116
+ <Tooltip.Content side='bottom' classNames='z-50'>
117
+ {t(copied ? 'copy success label' : 'copy self public key label')}
118
+ <Tooltip.Arrow />
119
+ </Tooltip.Content>
120
+ </Tooltip.Portal>
121
+ </Tooltip.Root>
122
+ </Toolbar.Root>
123
+ <Avatar.Label classNames='sr-only' data-testid='identityHeading.displayName'>
37
124
  {identity.profile?.displayName ?? generateName(identity.identityKey.toHex())}
38
125
  </Avatar.Label>
126
+ <Input.Root>
127
+ <Input.Label srOnly>{t('display name input label')}</Input.Label>
128
+ <Input.TextInput
129
+ variant='subdued'
130
+ data-testid='display-name-input'
131
+ placeholder={t('display name input placeholder')}
132
+ classNames='mbs-2 text-center font-light text-xl'
133
+ value={displayName}
134
+ onChange={({ target: { value } }) => {
135
+ setDisplayName(value);
136
+ }}
137
+ onBlur={handleUpdateProfile}
138
+ />
139
+ </Input.Root>
39
140
  </Avatar.Root>
40
141
  </Heading>
41
142
  );
@@ -47,18 +148,18 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
47
148
  titleId,
48
149
  activeView,
49
150
  onUpdateProfile,
50
- onResetDevice,
151
+ onResetStorage,
51
152
  onJoinNewIdentity,
52
153
  IdentityActionChooser: IdentityActionChooserComponent = IdentityActionChooser,
53
154
  InvitationManager: InvitationManagerComponent = InvitationManager,
155
+ connectionState,
156
+ onChangeConnectionState,
54
157
  onDone,
55
158
  ...rest
56
159
  } = props;
57
160
  const { t } = useTranslation('os');
58
161
  const title = useMemo(() => {
59
162
  switch (activeView) {
60
- case 'agent manager':
61
- return 'Manage Agent';
62
163
  case 'device invitation manager':
63
164
  return t('choose add device label');
64
165
  default:
@@ -66,9 +167,13 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
66
167
  }
67
168
  }, [activeView, t]);
68
169
 
170
+ const onCancelReset = () => rest.send?.('unchooseAction');
171
+
69
172
  return (
70
173
  <DensityProvider density='fine'>
71
- <IdentityHeading {...{ identity, titleId, title, onDone }} />
174
+ <IdentityHeading
175
+ {...{ identity, titleId, title, onDone, onUpdateProfile, connectionState, onChangeConnectionState }}
176
+ />
72
177
  <Viewport.Root activeView={activeView}>
73
178
  <Viewport.Views>
74
179
  <Viewport.View id='identity action chooser' classNames={viewStyles}>
@@ -81,28 +186,24 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
81
186
  invitationUrl={rest.createInvitationUrl(rest.invitationCode!)}
82
187
  />
83
188
  </Viewport.View>
84
- <Viewport.View id='device manager' classNames={viewStyles}>
85
- <DeviceManager active={activeView === 'device manager'} {...rest} />
86
- </Viewport.View>
87
- <Viewport.View classNames={viewStyles} id='update profile form'>
88
- <ProfileForm
89
- send={rest.send}
90
- active={activeView === 'update profile form'}
91
- identity={identity}
92
- onUpdateProfile={onUpdateProfile}
189
+ <Viewport.View classNames={viewStyles} id='confirm join new identity'>
190
+ <ConfirmReset
191
+ active={activeView === 'confirm join new identity'}
192
+ {...rest}
193
+ mode='join new identity'
194
+ onConfirm={onJoinNewIdentity}
195
+ onCancel={onCancelReset}
93
196
  />
94
197
  </Viewport.View>
95
- <Viewport.View classNames={viewStyles} id='signing out'>
96
- <SignOutChooser
97
- send={rest.send}
98
- active={activeView === 'signing out'}
99
- onResetDevice={onResetDevice}
100
- onJoinNewIdentity={onJoinNewIdentity}
198
+ <Viewport.View classNames={viewStyles} id='confirm reset storage'>
199
+ <ConfirmReset
200
+ active={activeView === 'confirm reset storage'}
201
+ {...rest}
202
+ mode='reset storage'
203
+ onConfirm={onResetStorage}
204
+ onCancel={onCancelReset}
101
205
  />
102
206
  </Viewport.View>
103
- <Viewport.View classNames={viewStyles} id='agent manager'>
104
- <AgentForm send={rest.send} active={activeView === 'agent manager'} {...rest} />
105
- </Viewport.View>
106
207
  </Viewport.Views>
107
208
  </Viewport.Root>
108
209
  </DensityProvider>
@@ -120,6 +221,7 @@ const IdentityPanelWithInvitationImpl = ({
120
221
  export const IdentityPanel = ({
121
222
  titleId: propsTitleId,
122
223
  createInvitationUrl = (code) => code,
224
+ initialDisposition = 'default',
123
225
  ...props
124
226
  }: IdentityPanelProps) => {
125
227
  const titleId = useId('identityPanel__heading', propsTitleId);
@@ -132,7 +234,13 @@ export const IdentityPanel = ({
132
234
  log.error('IdentityPanel rendered with no active identity.');
133
235
  return null;
134
236
  }
135
- const [identityState, identitySend, identityService] = useIdentityMachine(client);
237
+ const [identityState, identitySend, identityService] = useIdentityMachine(client, {
238
+ context: { initialDisposition },
239
+ });
240
+
241
+ const { swarm: connectionState } = useNetworkStatus();
242
+
243
+ const handleChangeConnectionState = async (nextState: ConnectionState) => client?.mesh.updateConfig(nextState);
136
244
 
137
245
  useEffect(() => {
138
246
  const subscription = identityService.subscribe((state) => {
@@ -148,14 +256,10 @@ export const IdentityPanel = ({
148
256
  return 'identity action chooser';
149
257
  case identityState.matches('managingDeviceInvitation'):
150
258
  return 'device invitation manager';
151
- case identityState.matches('managingDevices'):
152
- return 'device manager';
153
- case [{ managingProfile: 'idle' }, { managingProfile: 'pending' }].some(identityState.matches):
154
- return 'update profile form';
155
- case identityState.matches('signingOut'):
156
- return 'signing out';
157
- case [{ managingAgent: 'idle' }, { managingAgent: 'pending' }].some(identityState.matches):
158
- return 'agent manager';
259
+ case identityState.matches('confirmingResetStorage'):
260
+ return 'confirm reset storage';
261
+ case identityState.matches('confirmingJoinNewIdentity'):
262
+ return 'confirm join new identity';
159
263
  default:
160
264
  return 'identity action chooser';
161
265
  }
@@ -175,6 +279,8 @@ export const IdentityPanel = ({
175
279
  titleId,
176
280
  createInvitationUrl,
177
281
  onUpdateProfile,
282
+ connectionState,
283
+ onChangeConnectionState: handleChangeConnectionState,
178
284
  ...agentProps,
179
285
  } satisfies IdentityPanelImplProps;
180
286