@dxos/shell 0.6.14-main.f49f251 → 0.6.14-staging.9e90729

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 (44) hide show
  1. package/dist/lib/browser/meta.json +1 -1
  2. package/dist/lib/browser/testing/index.mjs +1 -39
  3. package/dist/lib/browser/testing/index.mjs.map +4 -4
  4. package/dist/lib/node/chunk-Q7SFCCGT.cjs +33 -0
  5. package/dist/lib/node/chunk-Q7SFCCGT.cjs.map +7 -0
  6. package/dist/lib/node/meta.json +1 -0
  7. package/dist/lib/node/react.cjs +4763 -0
  8. package/dist/lib/node/react.cjs.map +7 -0
  9. package/dist/lib/node/react.css +7 -0
  10. package/dist/lib/node/react.css.map +7 -0
  11. package/dist/lib/node/testing/index.cjs +181 -0
  12. package/dist/lib/node/testing/index.cjs.map +7 -0
  13. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  14. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  15. package/dist/lib/node-esm/meta.json +1 -0
  16. package/dist/lib/node-esm/react.css +7 -0
  17. package/dist/lib/node-esm/react.css.map +7 -0
  18. package/dist/lib/node-esm/react.mjs +4809 -0
  19. package/dist/lib/node-esm/react.mjs.map +7 -0
  20. package/dist/lib/node-esm/testing/index.mjs +161 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/components/IdentityList/SpaceMemberList.stories.js +2 -2
  23. package/dist/types/src/components/IdentityList/SpaceMemberList.stories.js.map +1 -1
  24. package/dist/types/src/components/InvitationList/InvitationList.stories.js +10 -10
  25. package/dist/types/src/components/InvitationList/InvitationList.stories.js.map +1 -1
  26. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.js +15 -15
  27. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.js.map +1 -1
  28. package/dist/types/src/playwright/invitations-manager.d.ts +1 -2
  29. package/dist/types/src/playwright/invitations-manager.d.ts.map +1 -1
  30. package/dist/types/src/playwright/invitations-manager.js +2 -3
  31. package/dist/types/src/playwright/invitations-manager.js.map +1 -1
  32. package/dist/types/src/playwright/invitations.spec.js +13 -9
  33. package/dist/types/src/playwright/invitations.spec.js.map +1 -1
  34. package/dist/types/src/testing/index.d.ts +0 -1
  35. package/dist/types/src/testing/index.d.ts.map +1 -1
  36. package/dist/types/src/testing/index.js +1 -1
  37. package/dist/types/src/testing/index.js.map +1 -1
  38. package/package.json +38 -27
  39. package/src/components/IdentityList/SpaceMemberList.stories.tsx +1 -1
  40. package/src/components/InvitationList/InvitationList.stories.tsx +1 -1
  41. package/src/panels/SpacePanel/SpacePanel.stories.tsx +1 -1
  42. package/src/playwright/invitations-manager.ts +2 -3
  43. package/src/playwright/invitations.spec.ts +14 -9
  44. package/src/testing/index.ts +2 -1
@@ -0,0 +1,4809 @@
1
+ import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+ import {
3
+ __export
4
+ } from "./chunk-HSLMI22Q.mjs";
5
+
6
+ // packages/sdk/shell/src/components/AgentConfig.tsx
7
+ import { ArrowsClockwise, Database, Plus, Power } from "@phosphor-icons/react";
8
+ import React from "react";
9
+ import { Avatar, Button, Link, Tooltip, Trans, useTranslation } from "@dxos/react-ui";
10
+ import { descriptionText, getSize, mx, valenceColorText } from "@dxos/react-ui-theme";
11
+ var AgentConfig = ({ agentStatus, validationMessage, onAgentDestroy, onAgentCreate, onAgentRefresh }) => {
12
+ const { t } = useTranslation("os");
13
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("h2", {
14
+ className: mx(descriptionText, "text-center mbs-4")
15
+ }, t("agent heading")), validationMessage && /* @__PURE__ */ React.createElement("p", {
16
+ role: "alert",
17
+ className: mx(valenceColorText("error"), "mlb-2")
18
+ }, validationMessage), agentStatus === "created" || agentStatus === "creating" || agentStatus === "getting" || agentStatus === "destroying" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
19
+ role: "group",
20
+ className: "mlb-2 flex gap-2 items-center",
21
+ "aria-describedby": "devices-panel.create-agent.description"
22
+ }, /* @__PURE__ */ React.createElement(Avatar.Root, {
23
+ status: agentStatus === "created" ? "warning" : "inactive",
24
+ variant: "square"
25
+ }, /* @__PURE__ */ React.createElement(Avatar.Frame, {
26
+ classNames: "place-self-center"
27
+ }, /* @__PURE__ */ React.createElement(Database, {
28
+ weight: "duotone",
29
+ width: 24,
30
+ height: 24,
31
+ x: 8,
32
+ y: 8,
33
+ ...agentStatus !== "created" && {
34
+ className: "opacity-50"
35
+ }
36
+ })), /* @__PURE__ */ React.createElement(Avatar.Label, {
37
+ classNames: "flex-1 text-sm truncate"
38
+ }, t(agentStatus === "created" ? "agent requested label" : agentStatus === "creating" ? "creating agent label" : agentStatus === "destroying" ? "destroying agent label" : "getting agent label"))), agentStatus === "created" && /* @__PURE__ */ React.createElement(Tooltip.Root, null, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
39
+ asChild: true
40
+ }, /* @__PURE__ */ React.createElement(Button, {
41
+ variant: "ghost",
42
+ classNames: "pli-0 is-[--rail-action] bs-[--rail-action]",
43
+ "data-testid": "agent.destroy",
44
+ onClick: onAgentDestroy
45
+ }, /* @__PURE__ */ React.createElement("span", {
46
+ className: "sr-only"
47
+ }, t("destroy agent label")), /* @__PURE__ */ React.createElement(Power, {
48
+ className: getSize(5)
49
+ }))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, {
50
+ side: "bottom",
51
+ classNames: "z-50"
52
+ }, t("destroy agent label"))))), agentStatus === "created" && /* @__PURE__ */ React.createElement("p", {
53
+ id: "devices-panel.create-agent.description",
54
+ className: mx(descriptionText, "mlb-2")
55
+ }, t("agent requested description"))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Button, {
56
+ variant: "ghost",
57
+ classNames: "mlb-2 is-full justify-start gap-2 !pis-0 !pie-3",
58
+ "data-testid": agentStatus === "creatable" ? "devices-panel.create-agent" : "devices-panel.agent-error",
59
+ onClick: agentStatus === "creatable" ? onAgentCreate : onAgentRefresh,
60
+ "aria-describedby": "devices-panel.create-agent.description"
61
+ }, /* @__PURE__ */ React.createElement("div", {
62
+ role: "img",
63
+ className: mx(getSize(8), "m-1 rounded-sm bg-input grid place-items-center")
64
+ }, agentStatus === "creatable" ? /* @__PURE__ */ React.createElement(Plus, {
65
+ weight: "light",
66
+ className: getSize(6)
67
+ }) : /* @__PURE__ */ React.createElement(ArrowsClockwise, {
68
+ weight: "light",
69
+ className: getSize(6)
70
+ })), /* @__PURE__ */ React.createElement("span", {
71
+ className: "grow font-medium text-start"
72
+ }, t(agentStatus === "creatable" ? "create agent label" : ""))), agentStatus === "creatable" && /* @__PURE__ */ React.createElement("div", {
73
+ role: "none",
74
+ className: "space-y-2",
75
+ id: "devices-panel.create-agent.description"
76
+ }, /* @__PURE__ */ React.createElement("p", {
77
+ className: descriptionText
78
+ }, /* @__PURE__ */ React.createElement(Trans, {
79
+ t,
80
+ i18nKey: "create agent clickwrap",
81
+ components: {
82
+ tosLink: /* @__PURE__ */ React.createElement(Link, {
83
+ target: "_blank",
84
+ rel: "noreferrer"
85
+ })
86
+ }
87
+ })), /* @__PURE__ */ React.createElement("p", {
88
+ className: descriptionText
89
+ }, t("create agent description")))));
90
+ };
91
+
92
+ // packages/sdk/shell/src/components/Clipboard/CopyButton.tsx
93
+ import { Check, Copy } from "@phosphor-icons/react";
94
+ import React3, { useState as useState2 } from "react";
95
+ import { Button as Button2, Tooltip as Tooltip2, useTranslation as useTranslation2 } from "@dxos/react-ui";
96
+ import { getSize as getSize2, mx as mx2 } from "@dxos/react-ui-theme";
97
+
98
+ // packages/sdk/shell/src/components/Clipboard/ClipboardProvider.tsx
99
+ import React2, { createContext, useCallback, useContext, useState } from "react";
100
+ var ClipboardContext = /* @__PURE__ */ createContext({
101
+ textValue: "",
102
+ setTextValue: async (_) => {
103
+ }
104
+ });
105
+ var useClipboardContext = () => useContext(ClipboardContext);
106
+ var ClipboardProvider = ({ children }) => {
107
+ const [textValue, setInternalTextValue] = useState("");
108
+ const setTextValue = useCallback(async (nextValue) => {
109
+ await navigator.clipboard.writeText(nextValue);
110
+ return setInternalTextValue(nextValue);
111
+ }, []);
112
+ return /* @__PURE__ */ React2.createElement(ClipboardContext.Provider, {
113
+ value: {
114
+ textValue,
115
+ setTextValue
116
+ }
117
+ }, children);
118
+ };
119
+
120
+ // packages/sdk/shell/src/components/Clipboard/CopyButton.tsx
121
+ var inactiveLabelStyles = "invisible bs-px -mbe-px overflow-hidden";
122
+ var CopyButton = ({ value, classNames, iconProps: iconProps2, ...props }) => {
123
+ const { t } = useTranslation2("os");
124
+ const { textValue, setTextValue } = useClipboardContext();
125
+ const isCopied = textValue === value;
126
+ return /* @__PURE__ */ React3.createElement(Button2, {
127
+ ...props,
128
+ classNames: [
129
+ "inline-flex flex-col justify-center",
130
+ classNames
131
+ ],
132
+ onClick: () => setTextValue(value),
133
+ "data-testid": "copy-invitation"
134
+ }, /* @__PURE__ */ React3.createElement("div", {
135
+ role: "none",
136
+ className: mx2("flex gap-1 items-center", isCopied && inactiveLabelStyles)
137
+ }, /* @__PURE__ */ React3.createElement("span", {
138
+ className: "pli-1"
139
+ }, t("copy invitation code label")), /* @__PURE__ */ React3.createElement(Copy, {
140
+ className: getSize2(5),
141
+ ...iconProps2
142
+ })), /* @__PURE__ */ React3.createElement("div", {
143
+ role: "none",
144
+ className: mx2("flex gap-1 items-center", !isCopied && inactiveLabelStyles)
145
+ }, /* @__PURE__ */ React3.createElement("span", {
146
+ className: "pli-1"
147
+ }, t("copy success label")), /* @__PURE__ */ React3.createElement(Check, {
148
+ className: getSize2(5),
149
+ ...iconProps2
150
+ })));
151
+ };
152
+ var CopyButtonIconOnly = ({ value, classNames, iconProps: iconProps2, variant, ...props }) => {
153
+ const { t } = useTranslation2("os");
154
+ const { textValue, setTextValue } = useClipboardContext();
155
+ const isCopied = textValue === value;
156
+ const label = isCopied ? t("copy success label") : t("copy invitation code label");
157
+ const [open, setOpen] = useState2(false);
158
+ return /* @__PURE__ */ React3.createElement(Tooltip2.Root, {
159
+ delayDuration: 1500,
160
+ open,
161
+ onOpenChange: setOpen
162
+ }, /* @__PURE__ */ React3.createElement(Tooltip2.Portal, null, /* @__PURE__ */ React3.createElement(Tooltip2.Content, {
163
+ side: "bottom",
164
+ sideOffset: 12,
165
+ classNames: "z-30"
166
+ }, /* @__PURE__ */ React3.createElement("span", null, label), /* @__PURE__ */ React3.createElement(Tooltip2.Arrow, null))), /* @__PURE__ */ React3.createElement(Tooltip2.Trigger, {
167
+ "aria-label": label,
168
+ ...props,
169
+ onClick: () => setTextValue(value).then(() => setOpen(true)),
170
+ "data-testid": "copy-invitation",
171
+ asChild: true
172
+ }, /* @__PURE__ */ React3.createElement(Button2, {
173
+ variant,
174
+ classNames: [
175
+ "inline-flex flex-col justify-center",
176
+ classNames
177
+ ]
178
+ }, /* @__PURE__ */ React3.createElement(Copy, {
179
+ className: getSize2(5),
180
+ ...iconProps2
181
+ }))));
182
+ };
183
+
184
+ // packages/sdk/shell/src/components/CompoundButton/CompoundButton.tsx
185
+ import React4 from "react";
186
+ import { useId, useThemeContext, useElevationContext } from "@dxos/react-ui";
187
+ import { descriptionText as descriptionText2, descriptionTextPrimary, mx as mx3 } from "@dxos/react-ui-theme";
188
+ var CompoundButton = ({ children, description, before, after, variant = "default", elevation: propsElevation, slots = {}, ...buttonProps }) => {
189
+ const labelId = useId("compoundButton-label");
190
+ const descriptionId = useId("compoundButton-description");
191
+ const { tx } = useThemeContext();
192
+ const elevation = useElevationContext(propsElevation);
193
+ const styleProps = {
194
+ ...buttonProps,
195
+ variant,
196
+ elevation,
197
+ textWrap: true
198
+ };
199
+ const buttonClassName = tx("button.root", "button button--compound", styleProps, "flex items-center gap-4 plb-2.5", slots.root?.className);
200
+ return /* @__PURE__ */ React4.createElement("button", {
201
+ ...buttonProps,
202
+ ...slots.root,
203
+ className: buttonClassName,
204
+ "aria-labelledby": labelId,
205
+ ...description && {
206
+ "aria-describedby": descriptionId
207
+ }
208
+ }, before && /* @__PURE__ */ React4.createElement("div", {
209
+ role: "none",
210
+ className: "grow-0"
211
+ }, before), /* @__PURE__ */ React4.createElement("div", {
212
+ role: "none",
213
+ ...slots.middle,
214
+ className: mx3("grow whitespace-normal flex flex-col gap-1 text-left", slots.middle?.className)
215
+ }, /* @__PURE__ */ React4.createElement("p", {
216
+ ...slots.label,
217
+ id: labelId,
218
+ className: mx3(slots.label?.className)
219
+ }, children), description && /* @__PURE__ */ React4.createElement("p", {
220
+ id: descriptionId,
221
+ ...slots.description,
222
+ className: mx3("text-xs mbe-1 font-normal", variant === "primary" ? descriptionTextPrimary : descriptionText2, slots.description?.className)
223
+ }, description)), after && /* @__PURE__ */ React4.createElement("div", {
224
+ role: "none",
225
+ className: "grow-0"
226
+ }, after));
227
+ };
228
+
229
+ // packages/sdk/shell/src/components/EmojiPicker.tsx
230
+ import emojiData from "@emoji-mart/data";
231
+ import EmojiMart from "@emoji-mart/react";
232
+ import { ArrowCounterClockwise, CaretDown, UserCircle } from "@phosphor-icons/react";
233
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
234
+ import React5, { useRef, useState as useState3 } from "react";
235
+ import { Button as Button3, Popover, Toolbar, Tooltip as Tooltip3, useMediaQuery, useThemeContext as useThemeContext2, useTranslation as useTranslation3 } from "@dxos/react-ui";
236
+ import { getSize as getSize3 } from "@dxos/react-ui-theme";
237
+ var EmojiPickerToolbarButton = ({ disabled, defaultEmoji, emoji, onChangeEmoji, classNames }) => {
238
+ const { t } = useTranslation3("os");
239
+ const { themeMode } = useThemeContext2();
240
+ const [_emojiValue, setEmojiValue] = useControllableState({
241
+ prop: emoji,
242
+ onChange: onChangeEmoji,
243
+ defaultProp: defaultEmoji
244
+ });
245
+ const [emojiPickerOpen, setEmojiPickerOpen] = useState3(false);
246
+ const suppressNextTooltip = useRef(false);
247
+ const [triggerTooltipOpen, setTriggerTooltipOpen] = useState3(false);
248
+ return /* @__PURE__ */ React5.createElement(Tooltip3.Root, {
249
+ open: triggerTooltipOpen,
250
+ onOpenChange: (nextOpen) => {
251
+ if (suppressNextTooltip.current) {
252
+ setTriggerTooltipOpen(false);
253
+ suppressNextTooltip.current = false;
254
+ } else {
255
+ setTriggerTooltipOpen(nextOpen);
256
+ }
257
+ }
258
+ }, /* @__PURE__ */ React5.createElement(Popover.Root, {
259
+ open: emojiPickerOpen,
260
+ onOpenChange: (nextOpen) => {
261
+ setEmojiPickerOpen(nextOpen);
262
+ suppressNextTooltip.current = true;
263
+ }
264
+ }, /* @__PURE__ */ React5.createElement(Tooltip3.Trigger, {
265
+ asChild: true
266
+ }, /* @__PURE__ */ React5.createElement(Popover.Trigger, {
267
+ asChild: true
268
+ }, /* @__PURE__ */ React5.createElement(Toolbar.Button, {
269
+ classNames: [
270
+ "gap-2 text-2xl plb-1",
271
+ classNames
272
+ ],
273
+ disabled
274
+ }, /* @__PURE__ */ React5.createElement("span", {
275
+ className: "sr-only"
276
+ }, t("select emoji label")), /* @__PURE__ */ React5.createElement(UserCircle, {
277
+ className: getSize3(5)
278
+ })))), /* @__PURE__ */ React5.createElement(Tooltip3.Portal, null, /* @__PURE__ */ React5.createElement(Tooltip3.Content, {
279
+ side: "bottom",
280
+ classNames: "z-50"
281
+ }, t("select emoji label"), /* @__PURE__ */ React5.createElement(Tooltip3.Arrow, null))), /* @__PURE__ */ React5.createElement(Popover.Content, {
282
+ side: "bottom",
283
+ onKeyDownCapture: (event) => {
284
+ if (event.key === "Escape") {
285
+ event.stopPropagation();
286
+ setEmojiPickerOpen(false);
287
+ suppressNextTooltip.current = true;
288
+ }
289
+ }
290
+ }, /* @__PURE__ */ React5.createElement(EmojiMart, {
291
+ data: emojiData,
292
+ onEmojiSelect: ({ native }) => {
293
+ if (native) {
294
+ setEmojiValue(native);
295
+ setEmojiPickerOpen(false);
296
+ }
297
+ },
298
+ autoFocus: true,
299
+ maxFrequentRows: 0,
300
+ noCountryFlags: true,
301
+ theme: themeMode
302
+ }), /* @__PURE__ */ React5.createElement(Popover.Arrow, null))));
303
+ };
304
+ var EmojiPickerBlock = ({ disabled, defaultEmoji, emoji, onChangeEmoji, onClickClear }) => {
305
+ const { t } = useTranslation3("os");
306
+ const [isMd] = useMediaQuery("md", {
307
+ ssr: false
308
+ });
309
+ const [emojiValue, setEmojiValue] = useControllableState({
310
+ prop: emoji,
311
+ onChange: onChangeEmoji,
312
+ defaultProp: defaultEmoji
313
+ });
314
+ const [emojiPickerOpen, setEmojiPickerOpen] = useState3(false);
315
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Popover.Root, {
316
+ open: emojiPickerOpen,
317
+ onOpenChange: setEmojiPickerOpen
318
+ }, /* @__PURE__ */ React5.createElement(Popover.Trigger, {
319
+ asChild: true
320
+ }, /* @__PURE__ */ React5.createElement(Button3, {
321
+ variant: "ghost",
322
+ classNames: "gap-2 text-2xl plb-1",
323
+ disabled
324
+ }, /* @__PURE__ */ React5.createElement("span", {
325
+ className: "sr-only"
326
+ }, t("select emoji label")), /* @__PURE__ */ React5.createElement("span", {
327
+ className: "grow pis-14"
328
+ }, emojiValue), /* @__PURE__ */ React5.createElement(CaretDown, {
329
+ className: getSize3(4)
330
+ }))), /* @__PURE__ */ React5.createElement(Popover.Content, {
331
+ side: "right",
332
+ sideOffset: isMd ? 0 : -310,
333
+ onKeyDownCapture: (event) => {
334
+ if (event.key === "Escape") {
335
+ event.stopPropagation();
336
+ setEmojiPickerOpen(false);
337
+ }
338
+ }
339
+ }, /* @__PURE__ */ React5.createElement(EmojiMart, {
340
+ data: emojiData,
341
+ onEmojiSelect: ({ native }) => {
342
+ if (native) {
343
+ setEmojiValue(native);
344
+ setEmojiPickerOpen(false);
345
+ }
346
+ },
347
+ autoFocus: true,
348
+ maxFrequentRows: 0,
349
+ noCountryFlags: true
350
+ }), /* @__PURE__ */ React5.createElement(Popover.Arrow, null))), /* @__PURE__ */ React5.createElement(Tooltip3.Root, null, /* @__PURE__ */ React5.createElement(Tooltip3.Trigger, {
351
+ asChild: true
352
+ }, /* @__PURE__ */ React5.createElement(Button3, {
353
+ variant: "ghost",
354
+ onClick: onClickClear,
355
+ disabled
356
+ }, /* @__PURE__ */ React5.createElement("span", {
357
+ className: "sr-only"
358
+ }, t("clear label")), /* @__PURE__ */ React5.createElement(ArrowCounterClockwise, null))), /* @__PURE__ */ React5.createElement(Tooltip3.Portal, null, /* @__PURE__ */ React5.createElement(Tooltip3.Content, {
359
+ style: {
360
+ zIndex: 70
361
+ },
362
+ side: "right"
363
+ }, t("clear label"), /* @__PURE__ */ React5.createElement(Tooltip3.Arrow, null)))));
364
+ };
365
+
366
+ // packages/sdk/shell/src/components/HuePicker.tsx
367
+ import { ArrowCounterClockwise as ArrowCounterClockwise2, CaretDown as CaretDown2, Check as Check2, Palette } from "@phosphor-icons/react";
368
+ import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
369
+ import React6, { useRef as useRef2, useState as useState4 } from "react";
370
+ import { Button as Button4, DropdownMenu, Toolbar as Toolbar2, Tooltip as Tooltip4, useThemeContext as useThemeContext3, useTranslation as useTranslation4 } from "@dxos/react-ui";
371
+ import { getSize as getSize4, hueTokenThemes, mx as mx4 } from "@dxos/react-ui-theme";
372
+ var HuePreview = ({ hue }) => {
373
+ const { tx } = useThemeContext3();
374
+ const size = 20;
375
+ return /* @__PURE__ */ React6.createElement("svg", {
376
+ width: size,
377
+ height: size,
378
+ viewBox: `0 0 ${size} ${size}`
379
+ }, /* @__PURE__ */ React6.createElement("rect", {
380
+ x: 0,
381
+ y: 0,
382
+ width: size,
383
+ height: size,
384
+ className: tx("hue.fill", "select--hue__preview", {
385
+ hue
386
+ })
387
+ }));
388
+ };
389
+ var hueTokens = Object.keys(hueTokenThemes).slice(0, 16);
390
+ var HuePickerToolbarButton = ({ disabled, hue, onChangeHue, classNames, defaultHue }) => {
391
+ const { t } = useTranslation4("os");
392
+ const [hueValue, setHueValue] = useControllableState2({
393
+ prop: hue,
394
+ onChange: onChangeHue,
395
+ defaultProp: defaultHue
396
+ });
397
+ const [huePickerOpen, setHuePickerOpen] = useState4(false);
398
+ const suppressNextTooltip = useRef2(false);
399
+ const [triggerTooltipOpen, setTriggerTooltipOpen] = useState4(false);
400
+ return /* @__PURE__ */ React6.createElement(Tooltip4.Root, {
401
+ open: triggerTooltipOpen,
402
+ onOpenChange: (nextOpen) => {
403
+ if (suppressNextTooltip.current) {
404
+ setTriggerTooltipOpen(false);
405
+ suppressNextTooltip.current = false;
406
+ } else {
407
+ setTriggerTooltipOpen(nextOpen);
408
+ }
409
+ }
410
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu.Root, {
411
+ modal: false,
412
+ open: huePickerOpen,
413
+ onOpenChange: (nextOpen) => {
414
+ setHuePickerOpen(nextOpen);
415
+ suppressNextTooltip.current = true;
416
+ }
417
+ }, /* @__PURE__ */ React6.createElement(Tooltip4.Trigger, {
418
+ asChild: true
419
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu.Trigger, {
420
+ asChild: true
421
+ }, /* @__PURE__ */ React6.createElement(Toolbar2.Button, {
422
+ classNames: mx4("gap-2 plb-1", classNames),
423
+ disabled
424
+ }, /* @__PURE__ */ React6.createElement("span", {
425
+ className: "sr-only"
426
+ }, t("select hue label")), /* @__PURE__ */ React6.createElement(Palette, {
427
+ className: getSize4(5)
428
+ })))), /* @__PURE__ */ React6.createElement(Tooltip4.Portal, null, /* @__PURE__ */ React6.createElement(Tooltip4.Content, {
429
+ side: "bottom",
430
+ classNames: "z-50"
431
+ }, t("select hue label"), /* @__PURE__ */ React6.createElement(Tooltip4.Arrow, null))), /* @__PURE__ */ React6.createElement(DropdownMenu.Content, {
432
+ side: "bottom",
433
+ classNames: "!w-40"
434
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu.Viewport, {
435
+ classNames: "grid grid-cols-4"
436
+ }, hueTokens.map((hue2) => {
437
+ return /* @__PURE__ */ React6.createElement(DropdownMenu.CheckboxItem, {
438
+ key: hue2,
439
+ checked: hue2 === hueValue,
440
+ onCheckedChange: () => setHueValue(hue2),
441
+ classNames: "px-0 py-2 items-center justify-center"
442
+ }, /* @__PURE__ */ React6.createElement(HuePreview, {
443
+ hue: hue2
444
+ }));
445
+ })))));
446
+ };
447
+ var HuePickerBlock = ({ disabled, hue, onChangeHue, defaultHue, onClickClear }) => {
448
+ const { t } = useTranslation4("os");
449
+ const [hueValue, setHueValue] = useControllableState2({
450
+ prop: hue,
451
+ onChange: onChangeHue,
452
+ defaultProp: defaultHue
453
+ });
454
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(DropdownMenu.Root, {
455
+ modal: false
456
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu.Trigger, {
457
+ asChild: true
458
+ }, /* @__PURE__ */ React6.createElement(Button4, {
459
+ variant: "ghost",
460
+ classNames: "gap-2 plb-1",
461
+ disabled
462
+ }, /* @__PURE__ */ React6.createElement("span", {
463
+ className: "sr-only"
464
+ }, t("select hue label")), /* @__PURE__ */ React6.createElement("div", {
465
+ role: "none",
466
+ className: "pis-14 grow flex items-center justify-center gap-2"
467
+ }, /* @__PURE__ */ React6.createElement(HuePreview, {
468
+ hue: hueValue
469
+ }), /* @__PURE__ */ React6.createElement("span", null, t(`${hueValue} label`))), /* @__PURE__ */ React6.createElement(CaretDown2, {
470
+ className: getSize4(4)
471
+ }))), /* @__PURE__ */ React6.createElement(DropdownMenu.Content, {
472
+ side: "right"
473
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu.Viewport, null, Object.keys(hueTokenThemes).map((hue2) => {
474
+ return /* @__PURE__ */ React6.createElement(DropdownMenu.CheckboxItem, {
475
+ key: hue2,
476
+ checked: hue2 === hueValue,
477
+ onCheckedChange: () => setHueValue(hue2)
478
+ }, /* @__PURE__ */ React6.createElement(HuePreview, {
479
+ hue: hue2
480
+ }), /* @__PURE__ */ React6.createElement("span", {
481
+ className: "grow"
482
+ }, t(`${hue2} label`)), /* @__PURE__ */ React6.createElement(DropdownMenu.ItemIndicator, null, /* @__PURE__ */ React6.createElement(Check2, null)));
483
+ })), /* @__PURE__ */ React6.createElement(DropdownMenu.Arrow, null))), /* @__PURE__ */ React6.createElement(Tooltip4.Root, null, /* @__PURE__ */ React6.createElement(Tooltip4.Trigger, {
484
+ asChild: true
485
+ }, /* @__PURE__ */ React6.createElement(Button4, {
486
+ variant: "ghost",
487
+ onClick: onClickClear,
488
+ disabled
489
+ }, /* @__PURE__ */ React6.createElement("span", {
490
+ className: "sr-only"
491
+ }, t("clear label")), /* @__PURE__ */ React6.createElement(ArrowCounterClockwise2, null))), /* @__PURE__ */ React6.createElement(Tooltip4.Portal, null, /* @__PURE__ */ React6.createElement(Tooltip4.Content, {
492
+ style: {
493
+ zIndex: 70
494
+ },
495
+ side: "right"
496
+ }, t("clear label"), /* @__PURE__ */ React6.createElement(Tooltip4.Arrow, null)))));
497
+ };
498
+
499
+ // packages/sdk/shell/src/components/IdentityList/IdentityListItem.tsx
500
+ import React7, { forwardRef } from "react";
501
+ import { generateName } from "@dxos/display-name";
502
+ import { SpaceMember } from "@dxos/react-client/echo";
503
+ import { ListItem, Avatar as Avatar2, useId as useId2 } from "@dxos/react-ui";
504
+ import { mx as mx5 } from "@dxos/react-ui-theme";
505
+ import { keyToFallback } from "@dxos/util";
506
+ var IdentityListItem = /* @__PURE__ */ forwardRef(({ identity, presence, onClick, classNames, ...props }, forwardedRef) => {
507
+ const fallbackValue = keyToFallback(identity.identityKey);
508
+ const labelId = useId2("identityListItem__label");
509
+ const displayName = identity.profile?.displayName ?? generateName(identity.identityKey.toHex());
510
+ return /* @__PURE__ */ React7.createElement(ListItem.Root, {
511
+ ...props,
512
+ classNames: mx5("flex gap-2 pis-3 pie-1 items-center", onClick && "cursor-pointer", classNames),
513
+ onClick: () => onClick?.(),
514
+ "data-testid": "identity-list-item",
515
+ labelId,
516
+ ref: forwardedRef
517
+ }, /* @__PURE__ */ React7.createElement(Avatar2.Root, {
518
+ status: presence === SpaceMember.PresenceState.ONLINE ? "active" : "inactive",
519
+ labelId,
520
+ hue: identity.profile?.data?.hue || fallbackValue.hue
521
+ }, /* @__PURE__ */ React7.createElement(Avatar2.Frame, {
522
+ classNames: "place-self-center"
523
+ }, /* @__PURE__ */ React7.createElement(Avatar2.Fallback, {
524
+ text: identity.profile?.data?.emoji || fallbackValue.emoji
525
+ })), /* @__PURE__ */ React7.createElement(Avatar2.Label, {
526
+ classNames: "text-sm truncate pli-2"
527
+ }, displayName)));
528
+ });
529
+
530
+ // packages/sdk/shell/src/components/IdentityList/SpaceMemberList.tsx
531
+ import React8, { useMemo } from "react";
532
+ import { useClient } from "@dxos/react-client";
533
+ import { useMembers } from "@dxos/react-client/echo";
534
+ import { useTranslation as useTranslation5, List } from "@dxos/react-ui";
535
+ import { descriptionText as descriptionText3, mx as mx6 } from "@dxos/react-ui-theme";
536
+ var SpaceMemberList = ({ spaceKey, includeSelf, onSelect }) => {
537
+ const client = useClient();
538
+ const allUnsortedMembers = useMembers(spaceKey);
539
+ const members = useMemo(() => includeSelf ? allUnsortedMembers.sort((member) => member.identity.identityKey.equals(client.halo.identity.get().identityKey) ? -1 : 1) : allUnsortedMembers.filter((member) => !member.identity.identityKey.equals(client.halo.identity.get().identityKey)), [
540
+ allUnsortedMembers
541
+ ]);
542
+ return /* @__PURE__ */ React8.createElement(SpaceMemberListImpl, {
543
+ members,
544
+ onSelect
545
+ });
546
+ };
547
+ var SpaceMemberListImpl = ({ members, onSelect }) => {
548
+ const { t } = useTranslation5("os");
549
+ const visibleMembers = members.filter((member) => member.identity);
550
+ return visibleMembers.length > 0 ? /* @__PURE__ */ React8.createElement(List, {
551
+ classNames: "flex flex-col gap-2",
552
+ "data-testid": "space-members-list"
553
+ }, visibleMembers.map((member) => {
554
+ return /* @__PURE__ */ React8.createElement(IdentityListItem, {
555
+ key: member.identity.identityKey.toHex(),
556
+ identity: member.identity,
557
+ presence: member.presence,
558
+ onClick: onSelect && (() => onSelect(member))
559
+ });
560
+ })) : /* @__PURE__ */ React8.createElement("div", {
561
+ role: "none",
562
+ className: "grow flex items-center p-2"
563
+ }, /* @__PURE__ */ React8.createElement("p", {
564
+ className: mx6(descriptionText3, "text-center is-full mlb-2")
565
+ }, t("empty space members message")));
566
+ };
567
+
568
+ // packages/sdk/shell/src/components/DeviceList/DeviceList.tsx
569
+ import { CaretRight, Plus as Plus2 } from "@phosphor-icons/react";
570
+ import React10 from "react";
571
+ import { Button as Button6, List as List2, useTranslation as useTranslation7 } from "@dxos/react-ui";
572
+ import { descriptionText as descriptionText4, getSize as getSize6, mx as mx7 } from "@dxos/react-ui-theme";
573
+
574
+ // packages/sdk/shell/src/components/DeviceList/DeviceListItem.tsx
575
+ import { Compass, Desktop, DeviceMobile, Devices, DotsThree, ShareFat, Power as Power2, Robot, Database as Database2, FirstAidKit } from "@phosphor-icons/react";
576
+ import React9, { forwardRef as forwardRef2 } from "react";
577
+ import { generateName as generateName2 } from "@dxos/display-name";
578
+ import { Device, DeviceType, DeviceKind } from "@dxos/react-client/halo";
579
+ import { ConnectionState } from "@dxos/react-client/mesh";
580
+ import { ListItem as ListItem2, Avatar as Avatar3, useId as useId3, Tag, useTranslation as useTranslation6, DropdownMenu as DropdownMenu2, Button as Button5, Tooltip as Tooltip5 } from "@dxos/react-ui";
581
+ import { getSize as getSize5 } from "@dxos/react-ui-theme";
582
+ import { keyToFallback as keyToFallback2 } from "@dxos/util";
583
+ var iconProps = {
584
+ weight: "duotone",
585
+ width: 24,
586
+ height: 24,
587
+ x: 8,
588
+ y: 8
589
+ };
590
+ var DeviceListItem = /* @__PURE__ */ forwardRef2(({ device, onClickAdd, onClickEdit, onClickReset, onClickRecover, onClickJoinExisting, classNames, connectionState, onAgentDestroy, ...props }, forwardedRef) => {
591
+ const { t } = useTranslation6("os");
592
+ const fallbackValue = keyToFallback2(device.deviceKey);
593
+ const labelId = useId3("identityListItem__label");
594
+ const displayName = device.profile ? t("device name placeholder", {
595
+ os: device.profile.os,
596
+ platform: device.profile.platform
597
+ }) : generateName2(device.deviceKey.toHex());
598
+ const isCurrent = device.kind === DeviceKind.CURRENT;
599
+ return /* @__PURE__ */ React9.createElement(ListItem2.Root, {
600
+ ...props,
601
+ classNames: [
602
+ "flex gap-2 items-center mlb-2",
603
+ classNames
604
+ ],
605
+ "data-testid": `device-list-item${isCurrent ? "-current" : ""}`,
606
+ labelId,
607
+ ref: forwardedRef
608
+ }, /* @__PURE__ */ React9.createElement(Avatar3.Root, {
609
+ status: isCurrent && connectionState === ConnectionState.OFFLINE ? "error" : device.presence === Device.PresenceState.ONLINE ? "active" : "inactive",
610
+ labelId,
611
+ hue: fallbackValue.hue,
612
+ variant: "square"
613
+ }, /* @__PURE__ */ React9.createElement(Avatar3.Frame, {
614
+ classNames: "place-self-center"
615
+ }, device.profile?.type ? device.profile.type === DeviceType.AGENT_MANAGED ? /* @__PURE__ */ React9.createElement(Database2, iconProps) : device.profile.type === DeviceType.BROWSER ? /* @__PURE__ */ React9.createElement(Compass, iconProps) : device.profile.type === DeviceType.NATIVE ? /* @__PURE__ */ React9.createElement(Desktop, iconProps) : [
616
+ DeviceType.AGENT,
617
+ DeviceType.AGENT_MANAGED
618
+ ].includes(device.profile.type) ? /* @__PURE__ */ React9.createElement(Robot, iconProps) : device.profile.type === DeviceType.MOBILE ? /* @__PURE__ */ React9.createElement(DeviceMobile, iconProps) : /* @__PURE__ */ React9.createElement(Devices, iconProps) : /* @__PURE__ */ React9.createElement(Avatar3.Fallback, {
619
+ text: fallbackValue.emoji
620
+ })), /* @__PURE__ */ React9.createElement(Avatar3.Label, {
621
+ classNames: "flex-1 text-sm truncate"
622
+ }, displayName), isCurrent && /* @__PURE__ */ React9.createElement(Tag, {
623
+ color: "primary"
624
+ }, t("current device tag label")), device.profile?.type === DeviceType.AGENT_MANAGED ? /* @__PURE__ */ React9.createElement(Tooltip5.Root, null, /* @__PURE__ */ React9.createElement(Tooltip5.Trigger, {
625
+ asChild: true
626
+ }, /* @__PURE__ */ React9.createElement(Button5, {
627
+ variant: "ghost",
628
+ classNames: "pli-0 is-[--rail-action] bs-[--rail-action]",
629
+ "data-testid": "agent.destroy",
630
+ onClick: onAgentDestroy
631
+ }, /* @__PURE__ */ React9.createElement("span", {
632
+ className: "sr-only"
633
+ }, t("destroy agent label")), /* @__PURE__ */ React9.createElement(Power2, {
634
+ className: getSize5(5)
635
+ }))), /* @__PURE__ */ React9.createElement(Tooltip5.Portal, null, /* @__PURE__ */ React9.createElement(Tooltip5.Content, {
636
+ side: "bottom",
637
+ classNames: "z-50"
638
+ }, t("destroy agent label")))) : device.kind === DeviceKind.CURRENT && /* @__PURE__ */ React9.createElement(DropdownMenu2.Root, null, /* @__PURE__ */ React9.createElement(DropdownMenu2.Trigger, {
639
+ asChild: true
640
+ }, /* @__PURE__ */ React9.createElement(Button5, {
641
+ variant: "ghost",
642
+ classNames: "pli-0 is-[--rail-action] bs-[--rail-action]",
643
+ "data-testid": `device-list-item${isCurrent ? "-current" : ""}.options`
644
+ }, /* @__PURE__ */ React9.createElement("span", {
645
+ className: "sr-only"
646
+ }, t("more options label")), /* @__PURE__ */ React9.createElement(DotsThree, {
647
+ className: getSize5(5)
648
+ }))), /* @__PURE__ */ React9.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React9.createElement(DropdownMenu2.Viewport, null, /* @__PURE__ */ React9.createElement(DropdownMenu2.Item, {
649
+ "data-testid": "device-list-item-current.join-existing",
650
+ onClick: onClickJoinExisting
651
+ }, /* @__PURE__ */ React9.createElement(ShareFat, {
652
+ className: getSize5(5)
653
+ }), t("choose join new identity label")), onClickRecover && /* @__PURE__ */ React9.createElement(DropdownMenu2.Item, {
654
+ "data-testid": "device-list-item-current.recover",
655
+ onClick: onClickRecover
656
+ }, /* @__PURE__ */ React9.createElement(FirstAidKit, {
657
+ className: getSize5(5)
658
+ }), t("choose recover identity label")), /* @__PURE__ */ React9.createElement(DropdownMenu2.Item, {
659
+ "data-testid": "device-list-item-current.reset",
660
+ onClick: onClickReset
661
+ }, /* @__PURE__ */ React9.createElement(Power2, {
662
+ className: getSize5(5)
663
+ }), t("reset device label")))))));
664
+ });
665
+
666
+ // packages/sdk/shell/src/components/DeviceList/DeviceList.tsx
667
+ var DeviceList = ({ devices, connectionState, onClickAdd, onClickEdit, onClickReset, onClickRecover, onClickJoinExisting, onAgentDestroy }) => {
668
+ const { t } = useTranslation7("os");
669
+ return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement("h2", {
670
+ className: mx7(descriptionText4, "text-center mbs-4")
671
+ }, t("devices heading")), devices.length > 0 && /* @__PURE__ */ React10.createElement(List2, null, devices.map((device) => {
672
+ return /* @__PURE__ */ React10.createElement(DeviceListItem, {
673
+ key: device.deviceKey.toHex(),
674
+ device,
675
+ onClickEdit: () => onClickEdit?.(device),
676
+ onClickReset,
677
+ onClickRecover,
678
+ onClickJoinExisting,
679
+ connectionState,
680
+ onAgentDestroy
681
+ });
682
+ })), /* @__PURE__ */ React10.createElement(Button6, {
683
+ variant: "ghost",
684
+ classNames: "justify-start gap-2 !pis-0 !pie-3 is-full",
685
+ "data-testid": "devices-panel.create-invitation",
686
+ onClick: onClickAdd
687
+ }, /* @__PURE__ */ React10.createElement("div", {
688
+ role: "img",
689
+ className: mx7(getSize6(8), "m-1 rounded-sm bg-input grid place-items-center")
690
+ }, /* @__PURE__ */ React10.createElement(Plus2, {
691
+ weight: "light",
692
+ className: getSize6(6)
693
+ })), /* @__PURE__ */ React10.createElement("span", {
694
+ className: "grow font-medium text-start"
695
+ }, t("choose add device label")), /* @__PURE__ */ React10.createElement(CaretRight, {
696
+ weight: "bold",
697
+ className: getSize6(4)
698
+ })));
699
+ };
700
+
701
+ // packages/sdk/shell/src/components/InvitationList/InvitationListItem.tsx
702
+ import { X } from "@phosphor-icons/react";
703
+ import { formatDistanceToNow } from "date-fns";
704
+ import React12, { useCallback as useCallback2 } from "react";
705
+ import { Invitation, useInvitationStatus } from "@dxos/react-client/invitations";
706
+ import { Button as Button7, ListItem as ListItem3, useTranslation as useTranslation8, Avatar as Avatar4, useThemeContext as useThemeContext4, Tooltip as Tooltip6 } from "@dxos/react-ui";
707
+ import { focusRing, getSize as getSize7, mx as mx9 } from "@dxos/react-ui-theme";
708
+ import { hexToEmoji } from "@dxos/util";
709
+
710
+ // packages/sdk/shell/src/components/AuthCode.tsx
711
+ import React11, { useRef as useRef3 } from "react";
712
+ import { mx as mx8 } from "@dxos/react-ui-theme";
713
+ var AuthCode = (props) => {
714
+ const { code, large, classNames } = props;
715
+ const l = code?.length ?? 0;
716
+ const left = code?.slice(0, l / 2);
717
+ const right = code?.slice(l / 2);
718
+ const codeRef = useRef3(null);
719
+ const handleClick = () => {
720
+ if (codeRef.current) {
721
+ const selection = window.getSelection();
722
+ selection?.removeAllRanges();
723
+ const range = document.createRange();
724
+ range.selectNodeContents(codeRef.current);
725
+ selection?.addRange(range);
726
+ }
727
+ void navigator.clipboard.writeText(code ?? "");
728
+ };
729
+ return /* @__PURE__ */ React11.createElement("span", {
730
+ className: mx8(large ? "text-6xl" : "text-2xl", "font-mono pli-2.5 rounded cursor-pointer", classNames),
731
+ onClick: handleClick,
732
+ ref: codeRef
733
+ }, /* @__PURE__ */ React11.createElement("span", {
734
+ className: "mie-[0.4em]"
735
+ }, left), /* @__PURE__ */ React11.createElement("span", null, right));
736
+ };
737
+
738
+ // packages/sdk/shell/src/components/InvitationList/InvitationListItem.tsx
739
+ var InvitationListItem = (props) => {
740
+ const { invitation } = props;
741
+ const invitationStatus = useInvitationStatus(invitation);
742
+ return /* @__PURE__ */ React12.createElement(InvitationListItemImpl, {
743
+ ...props,
744
+ invitationStatus
745
+ });
746
+ };
747
+ var avatarProps = {
748
+ size: 10,
749
+ variant: "circle"
750
+ };
751
+ var AvatarStackEffect = ({ animation, status, reverseEffects }) => {
752
+ const { tx } = useThemeContext4();
753
+ return /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement("span", {
754
+ role: "none",
755
+ className: mx9("absolute inline-end-auto opacity-20", reverseEffects ? "inline-start-3" : "inline-start-1", getSize7(avatarProps.size))
756
+ }, /* @__PURE__ */ React12.createElement("span", {
757
+ role: "none",
758
+ className: tx("avatar.ring", "avatar__ring", {
759
+ ...avatarProps,
760
+ status,
761
+ animation
762
+ }),
763
+ style: {
764
+ animationDelay: "400ms"
765
+ }
766
+ })), /* @__PURE__ */ React12.createElement("span", {
767
+ role: "none",
768
+ className: mx9("absolute inline-end-auto opacity-50", reverseEffects ? "inline-start-2" : "inline-start-2", getSize7(avatarProps.size))
769
+ }, /* @__PURE__ */ React12.createElement("span", {
770
+ role: "none",
771
+ className: tx("avatar.ring", "avatar__ring", {
772
+ ...avatarProps,
773
+ status,
774
+ animation
775
+ }),
776
+ style: {
777
+ animationDelay: "200ms"
778
+ }
779
+ })));
780
+ };
781
+ var InvitationListItemImpl = ({ invitation, invitationStatus: propsInvitationStatus, send, onClickRemove, createInvitationUrl, reverseEffects, ...props }) => {
782
+ const { t } = useTranslation8("os");
783
+ const { cancel, status: invitationStatus, invitationCode, authCode, multiUse, shareable } = propsInvitationStatus;
784
+ const isCancellable = !([
785
+ Invitation.State.ERROR,
786
+ Invitation.State.TIMEOUT,
787
+ Invitation.State.CANCELLED
788
+ ].indexOf(invitationStatus) >= 0);
789
+ const showShare = shareable && (multiUse || [
790
+ Invitation.State.INIT,
791
+ Invitation.State.CONNECTING,
792
+ Invitation.State.CONNECTED
793
+ ].indexOf(invitationStatus) >= 0);
794
+ const showAuthCode = invitationStatus === Invitation.State.READY_FOR_AUTHENTICATION;
795
+ const handleClickRemove = useCallback2(() => onClickRemove?.(invitation), [
796
+ invitation,
797
+ onClickRemove
798
+ ]);
799
+ const invitationUrl = invitationCode && createInvitationUrl(invitationCode);
800
+ const invitationId = invitation?.get().invitationId;
801
+ const invitationHasLifetime = invitation?.get().lifetime;
802
+ const invitationTimeLeft = invitation?.expiry ? formatDistanceToNow(invitation?.expiry, {
803
+ addSuffix: true
804
+ }) : void 0;
805
+ const avatarAnimation = [
806
+ Invitation.State.INIT,
807
+ Invitation.State.CONNECTING,
808
+ Invitation.State.CONNECTED,
809
+ Invitation.State.READY_FOR_AUTHENTICATION,
810
+ Invitation.State.AUTHENTICATING
811
+ ].includes(invitationStatus) ? "pulse" : "none";
812
+ const avatarError = [
813
+ Invitation.State.ERROR,
814
+ Invitation.State.TIMEOUT,
815
+ Invitation.State.CANCELLED
816
+ ].includes(invitationStatus);
817
+ const avatarGreen = [
818
+ Invitation.State.CONNECTED,
819
+ Invitation.State.READY_FOR_AUTHENTICATION,
820
+ Invitation.State.AUTHENTICATING,
821
+ Invitation.State.SUCCESS
822
+ ].includes(invitationStatus);
823
+ const avatarStatus = avatarError ? "error" : avatarGreen ? "active" : "inactive";
824
+ return /* @__PURE__ */ React12.createElement(ListItem3.Root, {
825
+ id: invitationCode,
826
+ ...props,
827
+ classNames: [
828
+ "flex gap-2 pis-3 pie-1 items-center relative",
829
+ props.classNames
830
+ ]
831
+ }, /* @__PURE__ */ React12.createElement(ListItem3.Heading, {
832
+ classNames: "sr-only"
833
+ }, t(multiUse ? "invite many list item label" : "invite one list item label")), multiUse && /* @__PURE__ */ React12.createElement(AvatarStackEffect, {
834
+ status: avatarStatus,
835
+ animation: avatarAnimation,
836
+ reverseEffects
837
+ }), /* @__PURE__ */ React12.createElement(Tooltip6.Root, null, /* @__PURE__ */ React12.createElement(Avatar4.Root, {
838
+ ...avatarProps,
839
+ animation: avatarAnimation,
840
+ status: avatarStatus
841
+ }, /* @__PURE__ */ React12.createElement(Tooltip6.Trigger, {
842
+ asChild: true
843
+ }, /* @__PURE__ */ React12.createElement(Avatar4.Frame, {
844
+ tabIndex: 0,
845
+ classNames: [
846
+ focusRing,
847
+ "relative rounded-full place-self-center"
848
+ ]
849
+ }, /* @__PURE__ */ React12.createElement(Avatar4.Fallback, {
850
+ text: hexToEmoji(invitationId)
851
+ })))), /* @__PURE__ */ React12.createElement(Tooltip6.Portal, null, /* @__PURE__ */ React12.createElement(Tooltip6.Content, {
852
+ side: "left",
853
+ classNames: "z-[70]"
854
+ }, t(multiUse ? "invite many qr label" : "invite one qr label"), /* @__PURE__ */ React12.createElement(Tooltip6.Arrow, null)))), showShare && invitationUrl ? /* @__PURE__ */ React12.createElement(Tooltip6.Root, null, /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Tooltip6.Trigger, {
855
+ asChild: true
856
+ }, /* @__PURE__ */ React12.createElement(Button7, {
857
+ variant: "ghost",
858
+ classNames: "grow justify-start font-medium",
859
+ onClick: () => send({
860
+ type: "selectInvitation",
861
+ invitation
862
+ }),
863
+ "data-testid": "show-qrcode"
864
+ }, /* @__PURE__ */ React12.createElement("span", null, t("open share panel label")))), /* @__PURE__ */ React12.createElement(CopyButtonIconOnly, {
865
+ variant: "ghost",
866
+ value: invitationUrl
867
+ })), /* @__PURE__ */ React12.createElement(Tooltip6.Portal, null, /* @__PURE__ */ React12.createElement(Tooltip6.Content, {
868
+ side: "left",
869
+ classNames: "z-[70]"
870
+ }, invitationHasLifetime && /* @__PURE__ */ React12.createElement("span", null, "Expires ", invitationTimeLeft)))) : showAuthCode ? /* @__PURE__ */ React12.createElement(AuthCode, {
871
+ code: authCode,
872
+ classNames: "grow"
873
+ }) : invitationStatus === Invitation.State.CONNECTING ? /* @__PURE__ */ React12.createElement("span", {
874
+ className: "pli-2 grow text-neutral-500"
875
+ }, "Connecting...") : invitationStatus === Invitation.State.AUTHENTICATING ? /* @__PURE__ */ React12.createElement("span", {
876
+ className: "pli-2 grow text-neutral-500"
877
+ }, "Authenticating...") : invitationStatus === Invitation.State.ERROR || invitationStatus === Invitation.State.TIMEOUT ? /* @__PURE__ */ React12.createElement("span", {
878
+ className: "pli-2 grow text-neutral-500"
879
+ }, "Failed") : invitationStatus === Invitation.State.CANCELLED ? /* @__PURE__ */ React12.createElement("span", {
880
+ className: "pli-2 grow text-neutral-500"
881
+ }, "Cancelled") : invitationStatus === Invitation.State.SUCCESS ? /* @__PURE__ */ React12.createElement("span", {
882
+ className: "pli-2 grow truncate"
883
+ }, "User joined") : !shareable ? /* @__PURE__ */ React12.createElement("span", {
884
+ className: "pli-2 grow text-neutral-500"
885
+ }, "Pending Invitation") : /* @__PURE__ */ React12.createElement("span", {
886
+ className: "grow"
887
+ }, " "), isCancellable ? /* @__PURE__ */ React12.createElement(Button7, {
888
+ variant: "ghost",
889
+ classNames: "flex gap-1 pli-0",
890
+ onClick: cancel,
891
+ "data-testid": "cancel-invitation"
892
+ }, /* @__PURE__ */ React12.createElement("span", {
893
+ className: "sr-only"
894
+ }, t("cancel invitation label")), /* @__PURE__ */ React12.createElement(X, {
895
+ className: getSize7(4)
896
+ })) : /* @__PURE__ */ React12.createElement(Button7, {
897
+ variant: "ghost",
898
+ classNames: "flex gap-1 pli-0",
899
+ onClick: handleClickRemove,
900
+ "data-testid": "remove-invitation"
901
+ }, /* @__PURE__ */ React12.createElement("span", {
902
+ className: "sr-only"
903
+ }, t("remove invitation label")), /* @__PURE__ */ React12.createElement(X, {
904
+ className: getSize7(4)
905
+ })));
906
+ };
907
+
908
+ // packages/sdk/shell/src/components/InvitationList/InvitationListContainer.tsx
909
+ import React14, { useCallback as useCallback3 } from "react";
910
+ import { useSpace, useSpaceInvitations } from "@dxos/react-client/echo";
911
+
912
+ // packages/sdk/shell/src/components/InvitationList/InvitationList.tsx
913
+ import React13 from "react";
914
+ import { List as List3 } from "@dxos/react-ui";
915
+ import { mx as mx10 } from "@dxos/react-ui-theme";
916
+ var InvitationList = ({ invitations, send, ...invitationProps }) => {
917
+ const { className, InvitationListItem: Item = InvitationListItem } = invitationProps;
918
+ return /* @__PURE__ */ React13.createElement(List3, {
919
+ classNames: mx10("flex flex-col gap-2", className)
920
+ }, invitations.map((invitation) => {
921
+ const value = invitation.get().invitationId;
922
+ return /* @__PURE__ */ React13.createElement(Item, {
923
+ key: value,
924
+ send,
925
+ invitation,
926
+ ...invitationProps
927
+ });
928
+ }));
929
+ };
930
+
931
+ // packages/sdk/shell/src/components/InvitationList/InvitationListContainer.tsx
932
+ var InvitationListContainer = ({ spaceKey, createInvitationUrl, send }) => {
933
+ const space = useSpace(spaceKey);
934
+ const invitations = useSpaceInvitations(spaceKey);
935
+ const handleClickRemove = useCallback3((invitation) => {
936
+ void invitation.cancel();
937
+ }, [
938
+ space
939
+ ]);
940
+ return /* @__PURE__ */ React14.createElement(InvitationList, {
941
+ invitations,
942
+ onClickRemove: handleClickRemove,
943
+ createInvitationUrl,
944
+ send
945
+ });
946
+ };
947
+
948
+ // packages/sdk/shell/src/components/Panel/Action.tsx
949
+ import { CaretDown as CaretDown3, Check as Check3, Placeholder } from "@phosphor-icons/react";
950
+ import { useControllableState as useControllableState3 } from "@radix-ui/react-use-controllable-state";
951
+ import React15, { forwardRef as forwardRef3 } from "react";
952
+ import { Button as Button8, DropdownMenu as DropdownMenu3, useTranslation as useTranslation9 } from "@dxos/react-ui";
953
+ import { descriptionText as descriptionText5, getSize as getSize8, mx as mx11 } from "@dxos/react-ui-theme";
954
+ var defaultActions = {
955
+ noopAction: {
956
+ label: "No-op",
957
+ description: "",
958
+ icon: Placeholder,
959
+ onClick: () => {
960
+ }
961
+ }
962
+ };
963
+ var BifurcatedAction = /* @__PURE__ */ forwardRef3((props, forwardedRef) => {
964
+ const { classNames, variant, isFull = true, actions = defaultActions, activeAction: propsActiveAction, onChangeActiveAction, defaultActiveAction, ...rest } = props;
965
+ const [activeActionKey2 = Object.keys(actions)[0], setActiveAction] = useControllableState3({
966
+ prop: propsActiveAction,
967
+ onChange: onChangeActiveAction,
968
+ defaultProp: defaultActiveAction
969
+ });
970
+ const activeAction = actions[activeActionKey2] ?? {};
971
+ const { t } = useTranslation9("os");
972
+ return /* @__PURE__ */ React15.createElement("div", {
973
+ role: "none",
974
+ className: mx11("mbs-2 flex gap-px items-center", isFull && "is-full")
975
+ }, /* @__PURE__ */ React15.createElement(Button8, {
976
+ ...rest,
977
+ classNames: [
978
+ "bs-11 flex-1 min-is-0 flex gap-2 rounded-ie-none",
979
+ classNames
980
+ ],
981
+ ref: forwardedRef,
982
+ variant,
983
+ onClick: activeAction.onClick
984
+ }, activeAction.icon && /* @__PURE__ */ React15.createElement(activeAction.icon, {
985
+ className: getSize8(5)
986
+ }), /* @__PURE__ */ React15.createElement("span", null, activeAction.label)), /* @__PURE__ */ React15.createElement(DropdownMenu3.Root, null, /* @__PURE__ */ React15.createElement(DropdownMenu3.Trigger, {
987
+ asChild: true
988
+ }, /* @__PURE__ */ React15.createElement(Button8, {
989
+ classNames: [
990
+ "bs-11 flex-none rounded-is-none",
991
+ classNames
992
+ ]
993
+ }, /* @__PURE__ */ React15.createElement("span", {
994
+ className: "sr-only"
995
+ }, t("invite options label")), /* @__PURE__ */ React15.createElement(CaretDown3, {
996
+ className: getSize8(4)
997
+ }))), /* @__PURE__ */ React15.createElement(DropdownMenu3.Content, null, /* @__PURE__ */ React15.createElement(DropdownMenu3.Viewport, null, Object.entries(actions).map(([id, action]) => {
998
+ return /* @__PURE__ */ React15.createElement(DropdownMenu3.CheckboxItem, {
999
+ key: id,
1000
+ "aria-labelledby": `${id}__label`,
1001
+ "aria-describedby": `${id}__description`,
1002
+ checked: activeActionKey2 === id,
1003
+ onCheckedChange: (checked) => checked && setActiveAction(id),
1004
+ classNames: "gap-2"
1005
+ }, action.icon && /* @__PURE__ */ React15.createElement(action.icon, {
1006
+ className: getSize8(5)
1007
+ }), /* @__PURE__ */ React15.createElement("div", {
1008
+ role: "none",
1009
+ className: "flex-1 min-is-0 space-b-1"
1010
+ }, /* @__PURE__ */ React15.createElement("p", {
1011
+ id: `${id}__label`
1012
+ }, action.label), action.description && /* @__PURE__ */ React15.createElement("p", {
1013
+ id: `${id}__description`,
1014
+ className: descriptionText5
1015
+ }, action.description)), /* @__PURE__ */ React15.createElement(DropdownMenu3.ItemIndicator, {
1016
+ asChild: true
1017
+ }, /* @__PURE__ */ React15.createElement(Check3, {
1018
+ weight: "bold",
1019
+ className: getSize8(4)
1020
+ })));
1021
+ })), /* @__PURE__ */ React15.createElement(DropdownMenu3.Arrow, null))));
1022
+ });
1023
+ var Action = /* @__PURE__ */ forwardRef3((props, forwardedRef) => {
1024
+ const { children, classNames, variant, isFull = true, ...rest } = props;
1025
+ return /* @__PURE__ */ React15.createElement(Button8, {
1026
+ ...rest,
1027
+ classNames: [
1028
+ isFull && "is-full",
1029
+ "bs-11 flex gap-2 mbs-2",
1030
+ classNames
1031
+ ],
1032
+ ref: forwardedRef,
1033
+ variant
1034
+ }, children);
1035
+ });
1036
+
1037
+ // packages/sdk/shell/src/components/Panel/Actions.tsx
1038
+ import React16, { forwardRef as forwardRef4 } from "react";
1039
+ import { mx as mx12 } from "@dxos/react-ui-theme";
1040
+ var Actions = /* @__PURE__ */ forwardRef4(({ classNames, children, ...props }, forwardedRef) => {
1041
+ return /* @__PURE__ */ React16.createElement("div", {
1042
+ ...props,
1043
+ className: mx12("flex flex-col gap-2 mbs-2", Array.isArray(children) && children.length > 1 ? "justify-between" : "justify-center", classNames),
1044
+ ref: forwardedRef
1045
+ }, children);
1046
+ });
1047
+
1048
+ // packages/sdk/shell/src/components/Panel/CloseButton.tsx
1049
+ import { X as X2 } from "@phosphor-icons/react";
1050
+ import React17 from "react";
1051
+ import { Button as Button9, useTranslation as useTranslation10 } from "@dxos/react-ui";
1052
+ import { getSize as getSize9, mx as mx13 } from "@dxos/react-ui-theme";
1053
+ var CloseButton = ({ onDone, ...props }) => {
1054
+ const { t } = useTranslation10("os");
1055
+ return /* @__PURE__ */ React17.createElement(Button9, {
1056
+ variant: "ghost",
1057
+ classNames: mx13("plb-0 pli-2 absolute block-start-0 inline-end-0 z-[1]"),
1058
+ onClick: () => onDone?.(),
1059
+ ...props
1060
+ }, /* @__PURE__ */ React17.createElement(X2, {
1061
+ weight: "bold",
1062
+ className: getSize9(4)
1063
+ }), /* @__PURE__ */ React17.createElement("span", {
1064
+ className: "sr-only"
1065
+ }, t("exit label")));
1066
+ };
1067
+
1068
+ // packages/sdk/shell/src/components/Panel/Emoji.tsx
1069
+ import React18 from "react";
1070
+ import { mx as mx14, getSize as getSize10 } from "@dxos/react-ui-theme";
1071
+ var Emoji = ({ text, className }) => {
1072
+ const size = 14;
1073
+ return /* @__PURE__ */ React18.createElement("div", {
1074
+ role: "none",
1075
+ className: mx14(getSize10(size), "rounded relative pointer-events-none", className)
1076
+ }, /* @__PURE__ */ React18.createElement("svg", {
1077
+ viewBox: `0 0 ${size * 4} ${size * 4}`,
1078
+ width: size * 4,
1079
+ height: size * 4
1080
+ }, /* @__PURE__ */ React18.createElement("text", {
1081
+ x: "50%",
1082
+ y: "50%",
1083
+ textAnchor: "middle",
1084
+ dominantBaseline: "middle",
1085
+ baselineShift: "-0.25rem",
1086
+ fontSize: "2.25rem"
1087
+ }, text)));
1088
+ };
1089
+ var Centered = (props) => {
1090
+ const { children } = props;
1091
+ return /* @__PURE__ */ React18.createElement("div", {
1092
+ role: "none",
1093
+ className: "absolute inset-0 flex items-center justify-center"
1094
+ }, children);
1095
+ };
1096
+
1097
+ // packages/sdk/shell/src/components/Panel/Heading.tsx
1098
+ import React19, { forwardRef as forwardRef5 } from "react";
1099
+ import { descriptionText as descriptionText6, mx as mx15 } from "@dxos/react-ui-theme";
1100
+ var Heading = /* @__PURE__ */ forwardRef5(({ titleId, title, titleSrOnly, children, corner }, forwardedRef) => {
1101
+ return /* @__PURE__ */ React19.createElement("div", {
1102
+ role: "none",
1103
+ className: "pbe-2 relative",
1104
+ ref: forwardedRef
1105
+ }, corner, /* @__PURE__ */ React19.createElement("h1", {
1106
+ ...!titleSrOnly && {
1107
+ id: titleId
1108
+ },
1109
+ className: mx15(descriptionText6, "text-center mlb-2", titleSrOnly && "invisible")
1110
+ }, title), titleSrOnly && /* @__PURE__ */ React19.createElement("span", {
1111
+ id: titleId
1112
+ }, title), children);
1113
+ });
1114
+
1115
+ // packages/sdk/shell/src/components/Panel/Label.tsx
1116
+ import React20 from "react";
1117
+ import { descriptionText as descriptionText7, mx as mx16 } from "@dxos/react-ui-theme";
1118
+ var Label = (props) => {
1119
+ const { children } = props;
1120
+ return /* @__PURE__ */ React20.createElement("span", {
1121
+ className: mx16(descriptionText7, "text-center mli-6 whitespace-normal")
1122
+ }, children);
1123
+ };
1124
+
1125
+ // packages/sdk/shell/src/components/Panel/Input.tsx
1126
+ import React21 from "react";
1127
+ import { Input as UiInput } from "@dxos/react-ui";
1128
+ var Input = ({ validationMessage, label, ...props }) => {
1129
+ return /* @__PURE__ */ React21.createElement(UiInput.Root, null, /* @__PURE__ */ React21.createElement(UiInput.Label, null, label), /* @__PURE__ */ React21.createElement(UiInput.TextInput, {
1130
+ ...props,
1131
+ classNames: "plb-3 mbs-2 text-center"
1132
+ }), /* @__PURE__ */ React21.createElement(UiInput.DescriptionAndValidation, null, validationMessage && /* @__PURE__ */ React21.createElement(UiInput.Validation, null, validationMessage)));
1133
+ };
1134
+
1135
+ // packages/sdk/shell/src/components/Panel/StepHeading.tsx
1136
+ import React22, { forwardRef as forwardRef6 } from "react";
1137
+ import { mx as mx17 } from "@dxos/react-ui-theme";
1138
+ var StepHeading = /* @__PURE__ */ forwardRef6(({ children, className, ...props }, forwardedRef) => {
1139
+ return /* @__PURE__ */ React22.createElement("h2", {
1140
+ ...props,
1141
+ className: mx17("font-normal text-sm mbe-4 mli-1 text-center", className),
1142
+ ref: forwardedRef
1143
+ }, children);
1144
+ });
1145
+
1146
+ // packages/sdk/shell/src/components/Viewport/Viewport.tsx
1147
+ import { useFocusFinders } from "@fluentui/react-tabster";
1148
+ import { createContextScope } from "@radix-ui/react-context";
1149
+ import { useControllableState as useControllableState4 } from "@radix-ui/react-use-controllable-state";
1150
+ import React23, { forwardRef as forwardRef7, useEffect } from "react";
1151
+ import { useForwardedRef } from "@dxos/react-ui";
1152
+ import { mx as mx18 } from "@dxos/react-ui-theme";
1153
+ var VIEWPORT_NAME = "Viewport";
1154
+ var VIEWS_NAME = "ViewportViews";
1155
+ var VIEW_NAME = "ViewportView";
1156
+ var [createViewportContext, createViewportScope] = createContextScope(VIEWPORT_NAME, []);
1157
+ var [ViewportProvider, useViewportContext] = createViewportContext(VIEWPORT_NAME);
1158
+ var ViewportRoot = ({ __viewportScope, classNames, children, defaultActiveView, activeView: propsActiveView, focusManaged = false, onActiveViewChange, ...props }) => {
1159
+ const [activeView = "never", setActiveView] = useControllableState4({
1160
+ prop: propsActiveView,
1161
+ defaultProp: defaultActiveView,
1162
+ onChange: onActiveViewChange
1163
+ });
1164
+ return /* @__PURE__ */ React23.createElement(ViewportProvider, {
1165
+ focusManaged,
1166
+ activeView,
1167
+ setActiveView,
1168
+ scope: __viewportScope
1169
+ }, /* @__PURE__ */ React23.createElement("div", {
1170
+ role: "region",
1171
+ "aria-live": "polite",
1172
+ ...props,
1173
+ className: mx18("is-full overflow-hidden", classNames)
1174
+ }, children));
1175
+ };
1176
+ ViewportRoot.displayName = VIEWPORT_NAME;
1177
+ var ViewportViews = ({ classNames, children, ...props }) => {
1178
+ const size = {
1179
+ inlineSize: `${Math.ceil(children.length) * 100}%`
1180
+ };
1181
+ return /* @__PURE__ */ React23.createElement("div", {
1182
+ role: "none",
1183
+ style: size,
1184
+ ...props,
1185
+ className: mx18("flex", classNames)
1186
+ }, children);
1187
+ };
1188
+ ViewportViews.displayName = VIEWS_NAME;
1189
+ var ViewportView = /* @__PURE__ */ forwardRef7(({ __viewportScope, classNames, children, id, ...props }, forwardedRef) => {
1190
+ const { activeView, focusManaged } = useViewportContext(VIEW_NAME, __viewportScope);
1191
+ const isActive = id === activeView;
1192
+ const ref = useForwardedRef(forwardedRef);
1193
+ const { findFirstFocusable } = useFocusFinders();
1194
+ useEffect(() => {
1195
+ if (!focusManaged && isActive && document.body.hasAttribute("data-is-keyboard") && ref.current) {
1196
+ findFirstFocusable(ref.current)?.focus();
1197
+ }
1198
+ }, [
1199
+ focusManaged,
1200
+ ref.current,
1201
+ isActive
1202
+ ]);
1203
+ return /* @__PURE__ */ React23.createElement("section", {
1204
+ ...props,
1205
+ ...!isActive && {
1206
+ "aria-hidden": true
1207
+ },
1208
+ className: mx18("min-is-0 flex-1 flex flex-col", isActive ? "order-2" : "order-4 invisible", classNames),
1209
+ ref
1210
+ }, children);
1211
+ });
1212
+ ViewportView.displayName = VIEW_NAME;
1213
+ var Viewport = {
1214
+ Root: ViewportRoot,
1215
+ Views: ViewportViews,
1216
+ View: ViewportView
1217
+ };
1218
+
1219
+ // packages/sdk/shell/src/components/SpaceListItem.tsx
1220
+ import React24, { forwardRef as forwardRef8 } from "react";
1221
+ import { Avatar as Avatar5, useId as useId4 } from "@dxos/react-ui";
1222
+ import { mx as mx19 } from "@dxos/react-ui-theme";
1223
+ import { humanize, keyToEmoji } from "@dxos/util";
1224
+ var SpaceListItem = /* @__PURE__ */ forwardRef8(({ space, onClick }, ref) => {
1225
+ const fallbackValue = keyToEmoji(space.key);
1226
+ const labelId = useId4("identityListItem__label");
1227
+ const displayName = space.properties.name ?? humanize(space.key.toHex());
1228
+ return /* @__PURE__ */ React24.createElement("li", {
1229
+ className: mx19("flex gap-2 items-center mbe-2", onClick && "cursor-pointer"),
1230
+ onClick: () => onClick?.(),
1231
+ ref,
1232
+ "data-testid": "space-list-item"
1233
+ }, /* @__PURE__ */ React24.createElement(Avatar5.Root, {
1234
+ labelId
1235
+ }, /* @__PURE__ */ React24.createElement(Avatar5.Frame, null, /* @__PURE__ */ React24.createElement(Avatar5.Fallback, {
1236
+ text: fallbackValue
1237
+ })), /* @__PURE__ */ React24.createElement(Avatar5.Label, {
1238
+ classNames: "text-sm truncate"
1239
+ }, displayName)));
1240
+ });
1241
+
1242
+ // packages/sdk/shell/src/composites/IdentityDialog/IdentityDialog.tsx
1243
+ import React42 from "react";
1244
+ import { Dialog, useId as useId10 } from "@dxos/react-ui";
1245
+
1246
+ // packages/sdk/shell/src/panels/IdentityPanel/IdentityPanel.tsx
1247
+ import { FirstAidKit as FirstAidKit2, Key, Plugs, PlugsConnected } from "@phosphor-icons/react";
1248
+ import React29, { useEffect as useEffect4, useMemo as useMemo3, useState as useState9 } from "react";
1249
+ import { generateName as generateName3 } from "@dxos/display-name";
1250
+ import { invariant as invariant3 } from "@dxos/invariant";
1251
+ import { log as log6 } from "@dxos/log";
1252
+ import { useClient as useClient6 } from "@dxos/react-client";
1253
+ import { useIdentity, useDevices, useHaloInvitations as useHaloInvitations2 } from "@dxos/react-client/halo";
1254
+ import { useInvitationStatus as useInvitationStatus2 } from "@dxos/react-client/invitations";
1255
+ import { useNetworkStatus, ConnectionState as ConnectionState2 } from "@dxos/react-client/mesh";
1256
+ import { Avatar as Avatar6, Input as Input2, Toolbar as Toolbar3, Tooltip as Tooltip7, useId as useId6, useTranslation as useTranslation14 } from "@dxos/react-ui";
1257
+ import { getSize as getSize13 } from "@dxos/react-ui-theme";
1258
+ import { hexToEmoji as hexToEmoji4, hexToHue as hexToHue2, keyToFallback as keyToFallback3 } from "@dxos/util";
1259
+
1260
+ // packages/sdk/shell/src/panels/IdentityPanel/identityMachine.ts
1261
+ import { useMachine } from "@xstate/react";
1262
+ import { assign, createMachine } from "xstate";
1263
+ import { log } from "@dxos/log";
1264
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/IdentityPanel/identityMachine.ts";
1265
+ var identityMachine = createMachine({
1266
+ id: "identity",
1267
+ predictableActionArguments: true,
1268
+ context: {
1269
+ identity: null,
1270
+ identitySubscribable: null
1271
+ },
1272
+ initial: "choosingAction",
1273
+ invoke: {
1274
+ src: (context) => context.identitySubscribable
1275
+ },
1276
+ states: {
1277
+ unknown: {
1278
+ always: [
1279
+ {
1280
+ cond: "initiallyManagingDeviceInvitation",
1281
+ target: "managingDeviceInvitation",
1282
+ actions: "log"
1283
+ },
1284
+ {
1285
+ target: "choosingAction",
1286
+ actions: "log"
1287
+ }
1288
+ ]
1289
+ },
1290
+ choosingAction: {},
1291
+ managingDeviceInvitation: {},
1292
+ confirmingResetStorage: {},
1293
+ confirmingRecover: {},
1294
+ confirmingJoinNewIdentity: {}
1295
+ },
1296
+ on: {
1297
+ unchooseAction: {
1298
+ target: ".choosingAction",
1299
+ actions: [
1300
+ "unsetInvitation",
1301
+ "log"
1302
+ ]
1303
+ },
1304
+ deselectInvitation: {
1305
+ target: ".choosingAction",
1306
+ actions: [
1307
+ "unsetInvitation",
1308
+ "log"
1309
+ ]
1310
+ },
1311
+ selectInvitation: {
1312
+ target: ".managingDeviceInvitation",
1313
+ actions: [
1314
+ "setInvitation",
1315
+ "log"
1316
+ ]
1317
+ },
1318
+ chooseJoinNewIdentity: {
1319
+ target: ".confirmingJoinNewIdentity",
1320
+ actions: "log"
1321
+ },
1322
+ chooseRecoverIdentity: {
1323
+ target: ".confirmingRecover",
1324
+ actions: "log"
1325
+ },
1326
+ chooseResetStorage: {
1327
+ target: ".confirmingResetStorage",
1328
+ actions: "log"
1329
+ }
1330
+ }
1331
+ }, {
1332
+ guards: {
1333
+ initiallyManagingDeviceInvitation: ({ initialDisposition }, _event) => initialDisposition === "manage-device-invitation"
1334
+ },
1335
+ actions: {
1336
+ setIdentity: assign({
1337
+ identity: (context, event) => event?.identity ?? null
1338
+ }),
1339
+ setInvitation: assign({
1340
+ invitation: (context, event) => event?.invitation ?? null
1341
+ }),
1342
+ unsetInvitation: assign({
1343
+ invitation: () => void 0
1344
+ }),
1345
+ log: (context, event) => {
1346
+ log("[transition]", {
1347
+ event,
1348
+ context
1349
+ }, {
1350
+ F: __dxlog_file,
1351
+ L: 98,
1352
+ S: void 0,
1353
+ C: (f, a) => f(...a)
1354
+ });
1355
+ }
1356
+ }
1357
+ });
1358
+ var useIdentityMachine = (client, options) => {
1359
+ return useMachine(identityMachine, {
1360
+ ...options,
1361
+ context: {
1362
+ ...options?.context,
1363
+ identity: client.halo.identity.get(),
1364
+ identitySubscribable: client.halo.identity.map((identity) => ({
1365
+ type: "setIdentity",
1366
+ identity
1367
+ }))
1368
+ }
1369
+ });
1370
+ };
1371
+
1372
+ // packages/sdk/shell/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx
1373
+ import React25 from "react";
1374
+ import { log as log2 } from "@dxos/log";
1375
+ import { useClient as useClient2 } from "@dxos/react-client";
1376
+ import { useHaloInvitations, DeviceType as DeviceType2 } from "@dxos/react-client/halo";
1377
+ import { Invitation as Invitation2, InvitationEncoder } from "@dxos/react-client/invitations";
1378
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx";
1379
+ var IdentityActionChooser = (props) => {
1380
+ const { send } = props;
1381
+ const client = useClient2();
1382
+ const invitations = useHaloInvitations();
1383
+ const createInvitation = () => {
1384
+ invitations.forEach((invitation2) => invitation2.cancel());
1385
+ const invitation = client.halo.share();
1386
+ if (client.config.values.runtime?.app?.env?.DX_ENVIRONMENT !== "production") {
1387
+ const subscription = invitation.subscribe((invitation2) => {
1388
+ const invitationCode = InvitationEncoder.encode(invitation2);
1389
+ if (invitation2.state === Invitation2.State.CONNECTING) {
1390
+ log2.info(JSON.stringify({
1391
+ invitationCode,
1392
+ authCode: invitation2.authCode
1393
+ }), void 0, {
1394
+ F: __dxlog_file2,
1395
+ L: 29,
1396
+ S: void 0,
1397
+ C: (f, a) => f(...a)
1398
+ });
1399
+ subscription.unsubscribe();
1400
+ }
1401
+ });
1402
+ }
1403
+ send?.({
1404
+ type: "selectInvitation",
1405
+ invitation
1406
+ });
1407
+ };
1408
+ const isProduction = client.config.values.runtime?.app?.env?.DX_ENVIRONMENT === "production";
1409
+ return /* @__PURE__ */ React25.createElement(IdentityActionChooserImpl, {
1410
+ ...props,
1411
+ onClickAdd: createInvitation,
1412
+ onClickJoinExisting: () => send?.({
1413
+ type: "chooseJoinNewIdentity"
1414
+ }),
1415
+ onClickRecover: isProduction ? void 0 : () => send?.({
1416
+ type: "chooseRecoverIdentity"
1417
+ }),
1418
+ onClickReset: () => send?.({
1419
+ type: "chooseResetStorage"
1420
+ })
1421
+ });
1422
+ };
1423
+ var IdentityActionChooserImpl = ({ devices, connectionState, agentHostingEnabled, onClickAdd, onClickJoinExisting, onClickRecover, onClickReset, ...agentProps }) => {
1424
+ const managedDeviceAvailable = devices.find((device) => device.profile?.type === DeviceType2.AGENT_MANAGED);
1425
+ return /* @__PURE__ */ React25.createElement("div", {
1426
+ role: "none",
1427
+ className: "bs-40 grow overflow-y-auto overflow-x-hidden"
1428
+ }, /* @__PURE__ */ React25.createElement(DeviceList, {
1429
+ devices,
1430
+ connectionState,
1431
+ onClickAdd,
1432
+ onClickJoinExisting,
1433
+ onClickRecover,
1434
+ onClickReset,
1435
+ onAgentDestroy: agentProps.onAgentDestroy
1436
+ }), !managedDeviceAvailable && agentHostingEnabled && /* @__PURE__ */ React25.createElement(AgentConfig, agentProps));
1437
+ };
1438
+
1439
+ // packages/sdk/shell/src/panels/IdentityPanel/steps/ProfileForm.tsx
1440
+ import React26, { useState as useState5 } from "react";
1441
+ import { log as log3 } from "@dxos/log";
1442
+ import { useTranslation as useTranslation11 } from "@dxos/react-ui";
1443
+ import { hexToEmoji as hexToEmoji2, hexToHue } from "@dxos/util";
1444
+
1445
+ // packages/sdk/shell/src/panels/IdentityPanel/useAgentHandlers.ts
1446
+ import { useClient as useClient5 } from "@dxos/react-client";
1447
+
1448
+ // packages/sdk/shell/src/panels/IdentityPanel/useEdgeAgentsHandlers.ts
1449
+ import { useEffect as useEffect2, useState as useState6 } from "react";
1450
+ import { invariant } from "@dxos/invariant";
1451
+ import { QueryAgentStatusResponse } from "@dxos/protocols/proto/dxos/client/services";
1452
+ import { useClient as useClient3 } from "@dxos/react-client";
1453
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/IdentityPanel/useEdgeAgentsHandlers.ts";
1454
+ var useEdgeAgentHandlers = ({ identity }) => {
1455
+ const [validationMessage, setValidationMessage] = useState6("");
1456
+ const [lastReceivedStatus, setLastReceivedStatus] = useState6(QueryAgentStatusResponse.AgentStatus.UNKNOWN);
1457
+ const [agentStatus, setAgentStatus] = useState6("getting");
1458
+ const client = useClient3();
1459
+ const agentHostingEnabled = Boolean(client.config.values.runtime?.client?.edgeFeatures?.agents);
1460
+ const handleStatus = (status) => {
1461
+ switch (status) {
1462
+ case QueryAgentStatusResponse.AgentStatus.ACTIVE:
1463
+ case QueryAgentStatusResponse.AgentStatus.INACTIVE:
1464
+ setAgentStatus("created");
1465
+ break;
1466
+ case QueryAgentStatusResponse.AgentStatus.NOT_FOUND:
1467
+ setAgentStatus("creatable");
1468
+ break;
1469
+ case QueryAgentStatusResponse.AgentStatus.UNKNOWN:
1470
+ setAgentStatus("getting");
1471
+ break;
1472
+ }
1473
+ };
1474
+ useEffect2(() => {
1475
+ const stream = client.services.services.EdgeAgentService?.queryAgentStatus();
1476
+ stream?.subscribe(({ status }) => {
1477
+ setLastReceivedStatus(status);
1478
+ handleStatus(status);
1479
+ });
1480
+ return () => {
1481
+ void stream?.close().catch(() => {
1482
+ });
1483
+ };
1484
+ }, []);
1485
+ const onAgentCreate = async () => {
1486
+ invariant(identity, "Identity not found", {
1487
+ F: __dxlog_file3,
1488
+ L: 56,
1489
+ S: void 0,
1490
+ A: [
1491
+ "identity",
1492
+ "'Identity not found'"
1493
+ ]
1494
+ });
1495
+ const service = client.services.services.EdgeAgentService;
1496
+ if (!service) {
1497
+ setAgentStatus("error");
1498
+ setValidationMessage("Agent service not initialized.");
1499
+ return;
1500
+ }
1501
+ if (!agentHostingEnabled) {
1502
+ setAgentStatus("error");
1503
+ setValidationMessage("Edge agents are disabled in config.");
1504
+ return;
1505
+ }
1506
+ setAgentStatus("creating");
1507
+ setValidationMessage("");
1508
+ try {
1509
+ await service.createAgent();
1510
+ setAgentStatus("created");
1511
+ } catch (err) {
1512
+ setAgentStatus("error");
1513
+ setValidationMessage(`Error creating an agent: ${err.message}`);
1514
+ }
1515
+ };
1516
+ const onAgentDestroy = async () => {
1517
+ setValidationMessage("Agents are non-destructible.");
1518
+ };
1519
+ const onAgentRefresh = async () => {
1520
+ setValidationMessage("");
1521
+ handleStatus(lastReceivedStatus);
1522
+ };
1523
+ return {
1524
+ onAgentCreate,
1525
+ onAgentDestroy,
1526
+ onAgentRefresh,
1527
+ validationMessage,
1528
+ agentStatus,
1529
+ agentHostingEnabled
1530
+ };
1531
+ };
1532
+
1533
+ // packages/sdk/shell/src/panels/IdentityPanel/useKubeAgentHandlers.ts
1534
+ import { useCallback as useCallback4, useEffect as useEffect3, useState as useState7 } from "react";
1535
+ import { invariant as invariant2 } from "@dxos/invariant";
1536
+ import { log as log4 } from "@dxos/log";
1537
+ import { useClient as useClient4, useAgentHostingClient } from "@dxos/react-client";
1538
+ import { Invitation as Invitation3, InvitationEncoder as InvitationEncoder2 } from "@dxos/react-client/invitations";
1539
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/IdentityPanel/useKubeAgentHandlers.ts";
1540
+ var useKubeAgentHandlers = ({ invitations, identity }) => {
1541
+ const [validationMessage, setValidationMessage] = useState7("");
1542
+ const [agentStatus, setAgentStatus] = useState7("getting");
1543
+ const agentHostingProviderClient = useAgentHostingClient();
1544
+ const client = useClient4();
1545
+ const updateAgentStatus = async () => {
1546
+ if (agentHostingProviderClient) {
1547
+ setAgentStatus("getting");
1548
+ try {
1549
+ const agentId = await agentHostingProviderClient?.getAgent(identity?.identityKey.truncate() ?? "never");
1550
+ if (agentId) {
1551
+ setAgentStatus("created");
1552
+ } else {
1553
+ setAgentStatus("creatable");
1554
+ }
1555
+ } catch (err) {
1556
+ setAgentStatus("error");
1557
+ setValidationMessage(`Couldn\u2019t check agent status: ${err.message}`);
1558
+ }
1559
+ } else {
1560
+ setAgentStatus("error");
1561
+ setValidationMessage("No agent provider configured.");
1562
+ }
1563
+ };
1564
+ const createAgent = async (invitationCode) => {
1565
+ invariant2(identity, "Identity not found", {
1566
+ F: __dxlog_file4,
1567
+ L: 50,
1568
+ S: void 0,
1569
+ A: [
1570
+ "identity",
1571
+ "'Identity not found'"
1572
+ ]
1573
+ });
1574
+ if (agentHostingProviderClient) {
1575
+ setAgentStatus("creating");
1576
+ setValidationMessage("");
1577
+ try {
1578
+ const agentId = await agentHostingProviderClient?.createAgent(invitationCode, identity.identityKey.truncate());
1579
+ if (agentId) {
1580
+ setAgentStatus("created");
1581
+ } else {
1582
+ setAgentStatus("creatable");
1583
+ setValidationMessage("Error creating agent.");
1584
+ }
1585
+ } catch (err) {
1586
+ setValidationMessage(`Error creating agent: ${err.message}`);
1587
+ return updateAgentStatus();
1588
+ }
1589
+ } else {
1590
+ setAgentStatus("error");
1591
+ setValidationMessage("No agent provider configured.");
1592
+ }
1593
+ };
1594
+ const destroyAgent = async () => {
1595
+ invariant2(identity, "Identity not found", {
1596
+ F: __dxlog_file4,
1597
+ L: 73,
1598
+ S: void 0,
1599
+ A: [
1600
+ "identity",
1601
+ "'Identity not found'"
1602
+ ]
1603
+ });
1604
+ if (agentHostingProviderClient) {
1605
+ setAgentStatus("destroying");
1606
+ setValidationMessage("");
1607
+ try {
1608
+ await agentHostingProviderClient?.destroyAgent(identity.identityKey.truncate());
1609
+ setAgentStatus("creatable");
1610
+ } catch (err) {
1611
+ setValidationMessage(`Error destroying agent: ${err.message}`);
1612
+ return updateAgentStatus();
1613
+ }
1614
+ } else {
1615
+ setAgentStatus("error");
1616
+ setValidationMessage("No agent provider configured.");
1617
+ }
1618
+ };
1619
+ useEffect3(() => {
1620
+ void updateAgentStatus();
1621
+ }, []);
1622
+ const handleAgentCreate = useCallback4(async (invitation) => {
1623
+ const invitationCode = InvitationEncoder2.encode(invitation);
1624
+ if (invitation.state === Invitation3.State.CONNECTING) {
1625
+ log4.info(JSON.stringify({
1626
+ invitationCode,
1627
+ authCode: invitation.authCode
1628
+ }), void 0, {
1629
+ F: __dxlog_file4,
1630
+ L: 97,
1631
+ S: void 0,
1632
+ C: (f, a) => f(...a)
1633
+ });
1634
+ return createAgent(invitationCode);
1635
+ }
1636
+ }, []);
1637
+ const onAgentCreate = async () => {
1638
+ invitations.forEach((invitation2) => invitation2.cancel());
1639
+ const invitation = client.halo.share({
1640
+ type: Invitation3.Type.INTERACTIVE,
1641
+ authMethod: Invitation3.AuthMethod.NONE,
1642
+ multiUse: true
1643
+ });
1644
+ invitation.subscribe(handleAgentCreate);
1645
+ };
1646
+ const onAgentRefresh = async () => {
1647
+ setValidationMessage("");
1648
+ await updateAgentStatus();
1649
+ };
1650
+ return {
1651
+ onAgentCreate,
1652
+ onAgentDestroy: destroyAgent,
1653
+ onAgentRefresh,
1654
+ validationMessage,
1655
+ agentStatus,
1656
+ agentHostingEnabled: !!agentHostingProviderClient
1657
+ };
1658
+ };
1659
+
1660
+ // packages/sdk/shell/src/panels/IdentityPanel/useAgentHandlers.ts
1661
+ var useAgentHandlers = (params) => {
1662
+ const client = useClient5();
1663
+ if (client.config.values?.runtime?.client?.edgeFeatures?.agents) {
1664
+ return useEdgeAgentHandlers(params);
1665
+ } else {
1666
+ return useKubeAgentHandlers(params);
1667
+ }
1668
+ };
1669
+
1670
+ // packages/sdk/shell/src/steps/InvitationManager.tsx
1671
+ import { Check as Check4, X as X3 } from "@phosphor-icons/react";
1672
+ import React27, { useMemo as useMemo2 } from "react";
1673
+ import { QR } from "react-qr-rounded";
1674
+ import { useId as useId5, useTranslation as useTranslation12 } from "@dxos/react-ui";
1675
+ import { descriptionText as descriptionText8, getSize as getSize11, mx as mx20 } from "@dxos/react-ui-theme";
1676
+ import { hexToEmoji as hexToEmoji3 } from "@dxos/util";
1677
+
1678
+ // packages/sdk/shell/src/util/invitationStatusValue.ts
1679
+ import { Invitation as Invitation4 } from "@dxos/react-client/invitations";
1680
+ var invitationStatusValue = /* @__PURE__ */ new Map([
1681
+ [
1682
+ Invitation4.State.ERROR,
1683
+ -3
1684
+ ],
1685
+ [
1686
+ Invitation4.State.TIMEOUT,
1687
+ -2
1688
+ ],
1689
+ [
1690
+ Invitation4.State.CANCELLED,
1691
+ -1
1692
+ ],
1693
+ [
1694
+ Invitation4.State.INIT,
1695
+ 0
1696
+ ],
1697
+ [
1698
+ Invitation4.State.CONNECTING,
1699
+ 1
1700
+ ],
1701
+ [
1702
+ Invitation4.State.CONNECTED,
1703
+ 2
1704
+ ],
1705
+ [
1706
+ Invitation4.State.READY_FOR_AUTHENTICATION,
1707
+ 3
1708
+ ],
1709
+ [
1710
+ Invitation4.State.AUTHENTICATING,
1711
+ 4
1712
+ ],
1713
+ [
1714
+ Invitation4.State.SUCCESS,
1715
+ 5
1716
+ ]
1717
+ ]);
1718
+
1719
+ // packages/sdk/shell/src/steps/InvitationManager.tsx
1720
+ var InvitationManagerView = ({ children, ...props }) => {
1721
+ return /* @__PURE__ */ React27.createElement(Viewport.View, {
1722
+ ...props,
1723
+ classNames: "grow flex flex-col justify-around items-center"
1724
+ }, children);
1725
+ };
1726
+ var InvitationManager = ({ invitationUrl, active, send, status, multiUse, authCode, id = "0" }) => {
1727
+ const { t } = useTranslation12("os");
1728
+ const qrLabel = useId5("invitation-manager__qr-code");
1729
+ const statusValue = multiUse ? 0 : invitationStatusValue.get(status) ?? 0;
1730
+ const showAuthCode = statusValue === 3;
1731
+ const emoji = hexToEmoji3(id);
1732
+ const activeView = useMemo2(() => {
1733
+ if (multiUse) {
1734
+ return "showing qr";
1735
+ }
1736
+ switch (true) {
1737
+ case statusValue === 5:
1738
+ case statusValue < 0:
1739
+ return "showing final";
1740
+ case statusValue === 3:
1741
+ return "showing auth code";
1742
+ default:
1743
+ return "showing qr";
1744
+ }
1745
+ }, [
1746
+ statusValue
1747
+ ]);
1748
+ return /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement(Viewport.Root, {
1749
+ activeView,
1750
+ classNames: "grow plb-1"
1751
+ }, /* @__PURE__ */ React27.createElement(Viewport.Views, null, /* @__PURE__ */ React27.createElement(InvitationManagerView, {
1752
+ id: "showing qr",
1753
+ emoji
1754
+ }, /* @__PURE__ */ React27.createElement("p", {
1755
+ className: "text-sm mlb-1 font-normal text-center"
1756
+ }, t(multiUse ? "invite many qr label" : "invite one qr label")), /* @__PURE__ */ React27.createElement("div", {
1757
+ role: "none",
1758
+ className: mx20(descriptionText8, "is-full max-is-[14rem] relative")
1759
+ }, /* @__PURE__ */ React27.createElement(QR, {
1760
+ rounding: 100,
1761
+ backgroundColor: "transparent",
1762
+ color: "currentColor",
1763
+ className: mx20("is-full bs-full p-2", showAuthCode && "invisible"),
1764
+ "aria-labelledby": qrLabel,
1765
+ errorCorrectionLevel: "Q",
1766
+ cutout: true
1767
+ }, invitationUrl ?? "never"), /* @__PURE__ */ React27.createElement(Centered, null, /* @__PURE__ */ React27.createElement(Emoji, {
1768
+ text: emoji
1769
+ }))), /* @__PURE__ */ React27.createElement("span", {
1770
+ id: qrLabel,
1771
+ className: "sr-only"
1772
+ }, t("qr label")), /* @__PURE__ */ React27.createElement(CopyButton, {
1773
+ variant: "ghost",
1774
+ value: invitationUrl ?? "never"
1775
+ })), /* @__PURE__ */ React27.createElement(InvitationManagerView, {
1776
+ id: "showing auth code"
1777
+ }, /* @__PURE__ */ React27.createElement(Label, null, t("auth code message")), /* @__PURE__ */ React27.createElement(AuthCode, {
1778
+ code: authCode,
1779
+ large: true,
1780
+ classNames: "text-black dark:text-white"
1781
+ }), /* @__PURE__ */ React27.createElement(Label, null, t("auth other device emoji message")), emoji && /* @__PURE__ */ React27.createElement(Emoji, {
1782
+ text: emoji
1783
+ })), /* @__PURE__ */ React27.createElement(InvitationManagerView, {
1784
+ id: "showing final"
1785
+ }, statusValue > 0 ? /* @__PURE__ */ React27.createElement(Check4, {
1786
+ className: mx20("m-1.5", getSize11(6))
1787
+ }) : /* @__PURE__ */ React27.createElement(X3, {
1788
+ className: mx20("m-1.5", getSize11(6))
1789
+ })))), /* @__PURE__ */ React27.createElement(Actions, {
1790
+ classNames: "mbs-4"
1791
+ }, /* @__PURE__ */ React27.createElement(Action, {
1792
+ disabled: !active,
1793
+ onClick: () => send?.({
1794
+ type: "deselectInvitation"
1795
+ })
1796
+ }, t("back label"))));
1797
+ };
1798
+
1799
+ // packages/sdk/shell/src/steps/ConfirmReset.tsx
1800
+ import { Warning } from "@phosphor-icons/react";
1801
+ import React28, { useState as useState8 } from "react";
1802
+ import { log as log5 } from "@dxos/log";
1803
+ import { Message, useTranslation as useTranslation13 } from "@dxos/react-ui";
1804
+ import { getSize as getSize12, mx as mx21 } from "@dxos/react-ui-theme";
1805
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/steps/ConfirmReset.tsx";
1806
+ var ConfirmReset = ({ active, onCancel, onConfirm, mode }) => {
1807
+ const { t } = useTranslation13("os");
1808
+ const [validationMessage, setValidationMessage] = useState8("");
1809
+ const [pending, setPending] = useState8(false);
1810
+ const processReset = async () => {
1811
+ setPending(true);
1812
+ return onConfirm?.().then(() => setPending(false), (error) => {
1813
+ log5.catch(error, void 0, {
1814
+ F: __dxlog_file5,
1815
+ L: 38,
1816
+ S: void 0,
1817
+ C: (f, a) => f(...a)
1818
+ });
1819
+ setValidationMessage(t("failed to reset identity message"));
1820
+ setPending(false);
1821
+ });
1822
+ };
1823
+ return /* @__PURE__ */ React28.createElement(ConfirmResetImpl, {
1824
+ disabled: !active,
1825
+ pending,
1826
+ validationMessage,
1827
+ onConfirm: processReset,
1828
+ onCancel,
1829
+ mode
1830
+ });
1831
+ };
1832
+ var ConfirmResetImpl = ({ disabled, pending, validationMessage, onConfirm, onCancel, mode }) => {
1833
+ const { t } = useTranslation13("os");
1834
+ const confirmationValue = t("confirmation value");
1835
+ const [inputValue, setInputValue] = useState8("");
1836
+ const testIdAffix = mode === "join new identity" ? "join-new-identity" : mode === "recover" ? "recover" : "reset-storage";
1837
+ return /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("div", {
1838
+ role: "none",
1839
+ className: "grow flex flex-col gap-2 justify-center"
1840
+ }, /* @__PURE__ */ React28.createElement(Message.Root, {
1841
+ valence: "error"
1842
+ }, /* @__PURE__ */ React28.createElement(Message.Title, null, /* @__PURE__ */ React28.createElement(Warning, {
1843
+ className: mx21(getSize12(6), "inline mie-2")
1844
+ }), t("sign out chooser title")), /* @__PURE__ */ React28.createElement(Message.Body, null, t("sign out chooser message"))), /* @__PURE__ */ React28.createElement(Input, {
1845
+ validationMessage,
1846
+ label: /* @__PURE__ */ React28.createElement(StepHeading, {
1847
+ className: "text-start mlb-2"
1848
+ }, t(mode === "join new identity" ? "join new identity input label" : mode === "recover" ? "recover reset input label" : "reset storage input label", {
1849
+ confirmationValue
1850
+ })),
1851
+ disabled,
1852
+ "data-testid": `${testIdAffix}.reset-identity-input`,
1853
+ placeholder: t("confirmation placeholder", {
1854
+ confirmationValue
1855
+ }),
1856
+ onChange: ({ target: { value } }) => setInputValue(value)
1857
+ })), /* @__PURE__ */ React28.createElement(Actions, null, onCancel && /* @__PURE__ */ React28.createElement(Action, {
1858
+ disabled,
1859
+ onClick: onCancel,
1860
+ "data-testid": `${testIdAffix}.reset-identity-cancel`
1861
+ }, t("cancel label")), onConfirm && /* @__PURE__ */ React28.createElement(Action, {
1862
+ variant: "destructive",
1863
+ disabled: disabled || pending || inputValue !== confirmationValue,
1864
+ onClick: onConfirm,
1865
+ "data-testid": `${testIdAffix}.reset-identity-confirm`
1866
+ }, pending ? t("reset in progress label") : t("reset device label"))));
1867
+ };
1868
+
1869
+ // packages/sdk/shell/src/panels/IdentityPanel/IdentityPanel.tsx
1870
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/IdentityPanel/IdentityPanel.tsx";
1871
+ var viewStyles = "pbs-1 pbe-3 pli-3";
1872
+ var getHueValue = (identity) => identity?.profile?.data?.hue || hexToHue2(identity?.identityKey.toHex() ?? "0");
1873
+ var getEmojiValue = (identity) => identity?.profile?.data?.emoji || hexToEmoji4(identity?.identityKey.toHex() ?? "0");
1874
+ var IdentityHeading = ({ titleId, title, identity, onDone, onUpdateProfile, connectionState, onChangeConnectionState }) => {
1875
+ const client = useClient6();
1876
+ const fallbackValue = keyToFallback3(identity.identityKey);
1877
+ const { t } = useTranslation14("os");
1878
+ const [displayName, setDisplayName] = useState9(identity.profile?.displayName ?? "");
1879
+ const [emoji, setEmojiDirectly] = useState9(getEmojiValue(identity));
1880
+ const [hue, setHueDirectly] = useState9(getHueValue(identity));
1881
+ const { textValue, setTextValue } = useClipboardContext();
1882
+ const identityHex = identity.identityKey.toHex();
1883
+ const publicKeyCopied = textValue === identityHex;
1884
+ const setEmoji = (nextEmoji) => {
1885
+ setEmojiDirectly(nextEmoji);
1886
+ void onUpdateProfile?.({
1887
+ ...identity.profile,
1888
+ data: {
1889
+ ...identity.profile?.data,
1890
+ emoji: nextEmoji
1891
+ }
1892
+ });
1893
+ };
1894
+ const setHue = (nextHue) => {
1895
+ setHueDirectly(nextHue);
1896
+ void onUpdateProfile?.({
1897
+ ...identity.profile,
1898
+ data: {
1899
+ ...identity.profile?.data,
1900
+ hue: nextHue
1901
+ }
1902
+ });
1903
+ };
1904
+ const handleRecoveryCode = async () => {
1905
+ invariant3(client.services.services.IdentityService, "IdentityService not available", {
1906
+ F: __dxlog_file6,
1907
+ L: 76,
1908
+ S: void 0,
1909
+ A: [
1910
+ "client.services.services.IdentityService",
1911
+ "'IdentityService not available'"
1912
+ ]
1913
+ });
1914
+ const { seedphrase } = await client.services.services.IdentityService.createRecoveryPhrase();
1915
+ void setTextValue(seedphrase);
1916
+ };
1917
+ const isConnected = connectionState === ConnectionState2.ONLINE;
1918
+ const isProduction = client.config.values.runtime?.app?.env?.DX_ENVIRONMENT === "production";
1919
+ return /* @__PURE__ */ React29.createElement(Heading, {
1920
+ titleId,
1921
+ title,
1922
+ corner: /* @__PURE__ */ React29.createElement(CloseButton, {
1923
+ onDone
1924
+ })
1925
+ }, /* @__PURE__ */ React29.createElement(Avatar6.Root, {
1926
+ size: 16,
1927
+ variant: "circle",
1928
+ status: isConnected ? "active" : "error",
1929
+ hue: hue || fallbackValue.hue
1930
+ }, /* @__PURE__ */ React29.createElement(Toolbar3.Root, {
1931
+ classNames: "flex justify-center"
1932
+ }, /* @__PURE__ */ React29.createElement(Avatar6.Frame, {
1933
+ classNames: "relative z-[2] -mli-4 chromatic-ignore"
1934
+ }, /* @__PURE__ */ React29.createElement(Avatar6.Fallback, {
1935
+ text: emoji || fallbackValue.emoji
1936
+ }))), /* @__PURE__ */ React29.createElement(Avatar6.Label, {
1937
+ classNames: "sr-only",
1938
+ "data-testid": "identityHeading.displayName"
1939
+ }, identity.profile?.displayName ?? generateName3(identity.identityKey.toHex())), /* @__PURE__ */ React29.createElement(Input2.Root, null, /* @__PURE__ */ React29.createElement(Input2.Label, {
1940
+ srOnly: true
1941
+ }, t("display name input label")), /* @__PURE__ */ React29.createElement(Input2.TextInput, {
1942
+ variant: "subdued",
1943
+ "data-testid": "display-name-input",
1944
+ placeholder: t("display name input placeholder"),
1945
+ classNames: "mbs-2 text-center font-light text-xl",
1946
+ value: displayName,
1947
+ onChange: ({ target: { value } }) => setDisplayName(value),
1948
+ onBlur: ({ target: { value } }) => onUpdateProfile?.({
1949
+ ...identity.profile,
1950
+ displayName: value
1951
+ })
1952
+ })), /* @__PURE__ */ React29.createElement(Toolbar3.Root, {
1953
+ classNames: "flex justify-center items-center gap-1 pt-3"
1954
+ }, /* @__PURE__ */ React29.createElement(EmojiPickerToolbarButton, {
1955
+ emoji,
1956
+ onChangeEmoji: setEmoji,
1957
+ classNames: "bs-[--rail-action]"
1958
+ }), /* @__PURE__ */ React29.createElement(HuePickerToolbarButton, {
1959
+ hue,
1960
+ onChangeHue: setHue,
1961
+ classNames: "bs-[--rail-action]"
1962
+ }), /* @__PURE__ */ React29.createElement(Tooltip7.Root, null, /* @__PURE__ */ React29.createElement(Tooltip7.Trigger, {
1963
+ asChild: true
1964
+ }, /* @__PURE__ */ React29.createElement(Toolbar3.Button, {
1965
+ classNames: "bs-[--rail-action]",
1966
+ "data-testid": "update-profile-form-copy-key",
1967
+ onClick: () => setTextValue(identityHex)
1968
+ }, /* @__PURE__ */ React29.createElement("span", {
1969
+ className: "sr-only"
1970
+ }, t(publicKeyCopied ? "copy success label" : "copy self public key label")), /* @__PURE__ */ React29.createElement(Key, {
1971
+ className: getSize13(5)
1972
+ }))), /* @__PURE__ */ React29.createElement(Tooltip7.Portal, null, /* @__PURE__ */ React29.createElement(Tooltip7.Content, {
1973
+ side: "bottom",
1974
+ classNames: "z-50"
1975
+ }, t(publicKeyCopied ? "copy success label" : "copy self public key label"), /* @__PURE__ */ React29.createElement(Tooltip7.Arrow, null)))), !isProduction && /* @__PURE__ */ React29.createElement(Tooltip7.Root, null, /* @__PURE__ */ React29.createElement(Tooltip7.Trigger, {
1976
+ asChild: true
1977
+ }, /* @__PURE__ */ React29.createElement(Toolbar3.Button, {
1978
+ classNames: "bs-[--rail-action]",
1979
+ onClick: handleRecoveryCode
1980
+ }, /* @__PURE__ */ React29.createElement(FirstAidKit2, {
1981
+ className: getSize13(5)
1982
+ }))), /* @__PURE__ */ React29.createElement(Tooltip7.Portal, null, /* @__PURE__ */ React29.createElement(Tooltip7.Content, {
1983
+ side: "bottom",
1984
+ classNames: "z-50"
1985
+ }, t("create recovery code label"), /* @__PURE__ */ React29.createElement(Tooltip7.Arrow, null)))), /* @__PURE__ */ React29.createElement(Tooltip7.Root, null, /* @__PURE__ */ React29.createElement(Tooltip7.Trigger, {
1986
+ asChild: true
1987
+ }, /* @__PURE__ */ React29.createElement(Toolbar3.Button, {
1988
+ classNames: [
1989
+ "bs-[--rail-action]",
1990
+ !isConnected && "text-error-500"
1991
+ ],
1992
+ onClick: () => onChangeConnectionState?.(isConnected ? ConnectionState2.OFFLINE : ConnectionState2.ONLINE)
1993
+ }, /* @__PURE__ */ React29.createElement("span", {
1994
+ className: "sr-only"
1995
+ }, t(isConnected ? "disconnect label" : "connect label")), isConnected ? /* @__PURE__ */ React29.createElement(PlugsConnected, {
1996
+ className: getSize13(5)
1997
+ }) : /* @__PURE__ */ React29.createElement(Plugs, {
1998
+ className: getSize13(5)
1999
+ }))), /* @__PURE__ */ React29.createElement(Tooltip7.Portal, null, /* @__PURE__ */ React29.createElement(Tooltip7.Content, {
2000
+ side: "bottom",
2001
+ classNames: "z-50"
2002
+ }, t(isConnected ? "disconnect label" : "connect label"), /* @__PURE__ */ React29.createElement(Tooltip7.Arrow, null)))))));
2003
+ };
2004
+ var IdentityPanelImpl = (props) => {
2005
+ const { identity, titleId, activeView, onUpdateProfile, onResetStorage, onRecover, onJoinNewIdentity, IdentityActionChooser: IdentityActionChooserComponent = IdentityActionChooser, InvitationManager: InvitationManagerComponent = InvitationManager, connectionState, onChangeConnectionState, onDone, ...rest } = props;
2006
+ const { t } = useTranslation14("os");
2007
+ const title = useMemo3(() => {
2008
+ switch (activeView) {
2009
+ case "device invitation manager":
2010
+ return t("choose add device label");
2011
+ default:
2012
+ return t("identity heading");
2013
+ }
2014
+ }, [
2015
+ activeView,
2016
+ t
2017
+ ]);
2018
+ const onCancelReset = () => rest.send?.("unchooseAction");
2019
+ return /* @__PURE__ */ React29.createElement(React29.Fragment, null, /* @__PURE__ */ React29.createElement(IdentityHeading, {
2020
+ identity,
2021
+ titleId,
2022
+ title,
2023
+ onDone,
2024
+ onUpdateProfile,
2025
+ connectionState,
2026
+ onChangeConnectionState
2027
+ }), /* @__PURE__ */ React29.createElement(Viewport.Root, {
2028
+ activeView
2029
+ }, /* @__PURE__ */ React29.createElement(Viewport.Views, null, /* @__PURE__ */ React29.createElement(Viewport.View, {
2030
+ id: "identity action chooser",
2031
+ classNames: viewStyles
2032
+ }, /* @__PURE__ */ React29.createElement(IdentityActionChooserComponent, {
2033
+ active: activeView === "identity action chooser",
2034
+ ...rest,
2035
+ connectionState
2036
+ })), /* @__PURE__ */ React29.createElement(Viewport.View, {
2037
+ id: "device invitation manager",
2038
+ classNames: viewStyles
2039
+ }, /* @__PURE__ */ React29.createElement(InvitationManagerComponent, {
2040
+ active: activeView === "device invitation manager",
2041
+ ...rest,
2042
+ invitationUrl: rest.createInvitationUrl(rest.invitationCode)
2043
+ })), /* @__PURE__ */ React29.createElement(Viewport.View, {
2044
+ classNames: viewStyles,
2045
+ id: "confirm join new identity"
2046
+ }, /* @__PURE__ */ React29.createElement(ConfirmReset, {
2047
+ active: activeView === "confirm join new identity",
2048
+ ...rest,
2049
+ mode: "join new identity",
2050
+ onConfirm: onJoinNewIdentity,
2051
+ onCancel: onCancelReset
2052
+ })), /* @__PURE__ */ React29.createElement(Viewport.View, {
2053
+ classNames: viewStyles,
2054
+ id: "confirm recover"
2055
+ }, /* @__PURE__ */ React29.createElement(ConfirmReset, {
2056
+ active: activeView === "confirm recover",
2057
+ ...rest,
2058
+ mode: "recover",
2059
+ onConfirm: onRecover,
2060
+ onCancel: onCancelReset
2061
+ })), /* @__PURE__ */ React29.createElement(Viewport.View, {
2062
+ classNames: viewStyles,
2063
+ id: "confirm reset storage"
2064
+ }, /* @__PURE__ */ React29.createElement(ConfirmReset, {
2065
+ active: activeView === "confirm reset storage",
2066
+ ...rest,
2067
+ mode: "reset storage",
2068
+ onConfirm: onResetStorage,
2069
+ onCancel: onCancelReset
2070
+ })))));
2071
+ };
2072
+ var IdentityPanelWithInvitationImpl = ({ invitation, ...props }) => {
2073
+ const invitationStatus = useInvitationStatus2(invitation);
2074
+ return /* @__PURE__ */ React29.createElement(IdentityPanelImpl, {
2075
+ ...props,
2076
+ ...invitationStatus
2077
+ });
2078
+ };
2079
+ var IdentityPanel = ({ titleId: propsTitleId, createInvitationUrl = (code) => code, initialDisposition = "default", ...props }) => {
2080
+ const titleId = useId6("identityPanel__heading", propsTitleId);
2081
+ const client = useClient6();
2082
+ const devices = useDevices();
2083
+ const identity = useIdentity();
2084
+ const invitations = useHaloInvitations2();
2085
+ const agentProps = useAgentHandlers({
2086
+ identity,
2087
+ invitations
2088
+ });
2089
+ if (!identity) {
2090
+ log6.error("IdentityPanel rendered with no active identity.", void 0, {
2091
+ F: __dxlog_file6,
2092
+ L: 278,
2093
+ S: void 0,
2094
+ C: (f, a) => f(...a)
2095
+ });
2096
+ return null;
2097
+ }
2098
+ const [identityState, identitySend, identityService] = useIdentityMachine(client, {
2099
+ context: {
2100
+ initialDisposition
2101
+ }
2102
+ });
2103
+ const { swarm: connectionState } = useNetworkStatus();
2104
+ const handleChangeConnectionState = async (nextState) => client?.mesh.updateConfig(nextState);
2105
+ useEffect4(() => {
2106
+ const subscription = identityService.subscribe((state) => {
2107
+ log6("[state]", state, {
2108
+ F: __dxlog_file6,
2109
+ L: 291,
2110
+ S: void 0,
2111
+ C: (f, a) => f(...a)
2112
+ });
2113
+ });
2114
+ return subscription.unsubscribe;
2115
+ }, [
2116
+ identityService
2117
+ ]);
2118
+ const activeView = useMemo3(() => {
2119
+ switch (true) {
2120
+ case identityState.matches("choosingAction"):
2121
+ return "identity action chooser";
2122
+ case identityState.matches("managingDeviceInvitation"):
2123
+ return "device invitation manager";
2124
+ case identityState.matches("confirmingResetStorage"):
2125
+ return "confirm reset storage";
2126
+ case identityState.matches("confirmingRecover"):
2127
+ return "confirm recover";
2128
+ case identityState.matches("confirmingJoinNewIdentity"):
2129
+ return "confirm join new identity";
2130
+ default:
2131
+ return "identity action chooser";
2132
+ }
2133
+ }, [
2134
+ identityState
2135
+ ]);
2136
+ const onUpdateProfile = async (profile) => {
2137
+ identitySend({
2138
+ type: "updateProfile"
2139
+ });
2140
+ await client.halo.updateProfile(profile);
2141
+ };
2142
+ const implProps = {
2143
+ ...props,
2144
+ identity,
2145
+ devices,
2146
+ activeView,
2147
+ send: identitySend,
2148
+ titleId,
2149
+ createInvitationUrl,
2150
+ onUpdateProfile,
2151
+ connectionState,
2152
+ onChangeConnectionState: handleChangeConnectionState,
2153
+ ...agentProps
2154
+ };
2155
+ return identityState.context.invitation ? /* @__PURE__ */ React29.createElement(IdentityPanelWithInvitationImpl, {
2156
+ ...implProps,
2157
+ invitation: identityState.context.invitation
2158
+ }) : /* @__PURE__ */ React29.createElement(IdentityPanelImpl, implProps);
2159
+ };
2160
+
2161
+ // packages/sdk/shell/src/panels/JoinPanel/JoinPanel.tsx
2162
+ import React38, { useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo4 } from "react";
2163
+ import { log as log10 } from "@dxos/log";
2164
+ import { useClient as useClient8, useMulticastObservable } from "@dxos/react-client";
2165
+ import { useIdentity as useIdentity2 } from "@dxos/react-client/halo";
2166
+ import { useId as useId8, useThemeContext as useThemeContext5 } from "@dxos/react-ui";
2167
+
2168
+ // packages/sdk/shell/src/panels/JoinPanel/JoinHeading.tsx
2169
+ import { X as X4 } from "@phosphor-icons/react";
2170
+ import React30, { cloneElement, forwardRef as forwardRef9 } from "react";
2171
+ import { Button as Button10, useTranslation as useTranslation15 } from "@dxos/react-ui";
2172
+ import { descriptionText as descriptionText9, getSize as getSize14, mx as mx22 } from "@dxos/react-ui-theme";
2173
+ var JoinHeading = /* @__PURE__ */ forwardRef9(({ mode, titleId, exitActionParent, onExit }, forwardedRef) => {
2174
+ const { t } = useTranslation15("os");
2175
+ const exitButton = /* @__PURE__ */ React30.createElement(Button10, {
2176
+ variant: "ghost",
2177
+ ...onExit && {
2178
+ onClick: onExit
2179
+ },
2180
+ classNames: mx22(descriptionText9, "plb-0 pli-2 absolute block-start-0 inline-end-0 z-[1]"),
2181
+ "data-testid": "join-exit"
2182
+ }, /* @__PURE__ */ React30.createElement(X4, {
2183
+ weight: "bold",
2184
+ className: getSize14(4)
2185
+ }), /* @__PURE__ */ React30.createElement("span", {
2186
+ className: "sr-only"
2187
+ }, t("exit label")));
2188
+ return /* @__PURE__ */ React30.createElement(Heading, {
2189
+ ref: forwardedRef,
2190
+ titleId,
2191
+ title: t(mode === "halo-only" ? "selecting identity heading" : "joining space heading"),
2192
+ ...mode === "halo-only" ? {
2193
+ titleSrOnly: true
2194
+ } : {
2195
+ corner: exitActionParent ? /* @__PURE__ */ cloneElement(exitActionParent, {}, exitButton) : exitButton
2196
+ },
2197
+ titleSrOnly: mode === "halo-only"
2198
+ });
2199
+ });
2200
+
2201
+ // packages/sdk/shell/src/panels/JoinPanel/joinMachine.ts
2202
+ import { useMachine as useMachine2 } from "@xstate/react";
2203
+ import { useCallback as useCallback5 } from "react";
2204
+ import { assign as assign2, createMachine as createMachine2 } from "xstate";
2205
+ import { log as log7 } from "@dxos/log";
2206
+ import { AlreadyJoinedError } from "@dxos/react-client";
2207
+ import { Invitation as Invitation5, InvitationEncoder as InvitationEncoder3 } from "@dxos/react-client/invitations";
2208
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/JoinPanel/joinMachine.ts";
2209
+ var getInvitationSubscribable = (Kind, invitation) => {
2210
+ log7("[subscribing to invitation]", invitation, {
2211
+ F: __dxlog_file7,
2212
+ L: 90,
2213
+ S: void 0,
2214
+ C: (f, a) => f(...a)
2215
+ });
2216
+ return {
2217
+ subscribe: (next, onError, complete) => invitation.subscribe((invitation2) => {
2218
+ switch (invitation2.state) {
2219
+ case Invitation5.State.CONNECTING: {
2220
+ log7("[invitation connecting]", {
2221
+ Kind,
2222
+ invitation: invitation2
2223
+ }, {
2224
+ F: __dxlog_file7,
2225
+ L: 101,
2226
+ S: void 0,
2227
+ C: (f, a) => f(...a)
2228
+ });
2229
+ return next({
2230
+ type: `connecting${Kind}Invitation`,
2231
+ invitation: invitation2
2232
+ });
2233
+ }
2234
+ case Invitation5.State.CONNECTED: {
2235
+ log7("[invitation connected]", {
2236
+ Kind,
2237
+ invitation: invitation2
2238
+ }, {
2239
+ F: __dxlog_file7,
2240
+ L: 106,
2241
+ S: void 0,
2242
+ C: (f, a) => f(...a)
2243
+ });
2244
+ return next({
2245
+ type: `connected${Kind}Invitation`,
2246
+ invitation: invitation2
2247
+ });
2248
+ }
2249
+ case Invitation5.State.READY_FOR_AUTHENTICATION: {
2250
+ log7("[invitation ready for authentication]", {
2251
+ Kind,
2252
+ invitation: invitation2
2253
+ }, {
2254
+ F: __dxlog_file7,
2255
+ L: 111,
2256
+ S: void 0,
2257
+ C: (f, a) => f(...a)
2258
+ });
2259
+ return next({
2260
+ type: `readyForAuthentication${Kind}Invitation`,
2261
+ invitation: invitation2
2262
+ });
2263
+ }
2264
+ case Invitation5.State.AUTHENTICATING: {
2265
+ log7("[invitation authenticating]", {
2266
+ Kind,
2267
+ invitation: invitation2
2268
+ }, {
2269
+ F: __dxlog_file7,
2270
+ L: 116,
2271
+ S: void 0,
2272
+ C: (f, a) => f(...a)
2273
+ });
2274
+ return next({
2275
+ type: `authenticating${Kind}Invitation`,
2276
+ invitation: invitation2
2277
+ });
2278
+ }
2279
+ case Invitation5.State.SUCCESS: {
2280
+ log7("[invitation success]", {
2281
+ Kind,
2282
+ invitation: invitation2
2283
+ }, {
2284
+ F: __dxlog_file7,
2285
+ L: 121,
2286
+ S: void 0,
2287
+ C: (f, a) => f(...a)
2288
+ });
2289
+ next({
2290
+ type: `success${Kind}Invitation`,
2291
+ invitation: invitation2
2292
+ });
2293
+ return complete?.();
2294
+ }
2295
+ case Invitation5.State.CANCELLED: {
2296
+ log7.warn("[invitation cancelled]", {
2297
+ Kind
2298
+ }, {
2299
+ F: __dxlog_file7,
2300
+ L: 127,
2301
+ S: void 0,
2302
+ C: (f, a) => f(...a)
2303
+ });
2304
+ return next({
2305
+ type: `fail${Kind}Invitation`,
2306
+ reason: "cancelled"
2307
+ });
2308
+ }
2309
+ case Invitation5.State.TIMEOUT: {
2310
+ log7.error("[invitation timeout]", {
2311
+ Kind
2312
+ }, {
2313
+ F: __dxlog_file7,
2314
+ L: 132,
2315
+ S: void 0,
2316
+ C: (f, a) => f(...a)
2317
+ });
2318
+ return next({
2319
+ type: `fail${Kind}Invitation`,
2320
+ reason: "timeout"
2321
+ });
2322
+ }
2323
+ }
2324
+ }, (error) => {
2325
+ if (error instanceof AlreadyJoinedError) {
2326
+ log7("[already joined]", {
2327
+ Kind,
2328
+ error
2329
+ }, {
2330
+ F: __dxlog_file7,
2331
+ L: 139,
2332
+ S: void 0,
2333
+ C: (f, a) => f(...a)
2334
+ });
2335
+ next({
2336
+ type: `success${Kind}Invitation`,
2337
+ invitation: invitation.get()
2338
+ });
2339
+ return complete?.();
2340
+ }
2341
+ log7.error("[invitation errored]", {
2342
+ Kind,
2343
+ error
2344
+ }, {
2345
+ F: __dxlog_file7,
2346
+ L: 144,
2347
+ S: void 0,
2348
+ C: (f, a) => f(...a)
2349
+ });
2350
+ next({
2351
+ type: `fail${Kind}Invitation`,
2352
+ reason: "error"
2353
+ });
2354
+ return onError?.(error);
2355
+ })
2356
+ };
2357
+ };
2358
+ var acceptingInvitationTemplate = (Kind, successTarget) => {
2359
+ const config = {
2360
+ initial: `unknown${Kind}`,
2361
+ states: {
2362
+ [`unknown${Kind}`]: {
2363
+ always: [
2364
+ {
2365
+ cond: ({ mode }) => mode === "halo-only" && Kind === "Space",
2366
+ target: "#join.finishingJoiningHalo"
2367
+ },
2368
+ {
2369
+ cond: (context) => !!context[Kind.toLowerCase()].unredeemedCode,
2370
+ target: `acceptingRedeemed${Kind}Invitation`,
2371
+ actions: [
2372
+ `redeem${Kind}InvitationCode`,
2373
+ `invalidate${Kind}InvitationCode`,
2374
+ "log"
2375
+ ]
2376
+ },
2377
+ {
2378
+ target: `inputting${Kind}InvitationCode`,
2379
+ actions: "log"
2380
+ }
2381
+ ]
2382
+ },
2383
+ [`inputting${Kind}InvitationCode`]: {},
2384
+ [`acceptingRedeemed${Kind}Invitation`]: {
2385
+ invoke: {
2386
+ src: (context) => context[Kind.toLowerCase()].invitationSubscribable
2387
+ },
2388
+ initial: `unknown${Kind}Invitation`,
2389
+ states: {
2390
+ [`unknown${Kind}Invitation`]: {
2391
+ always: [
2392
+ {
2393
+ cond: (context) => {
2394
+ if (Kind !== "Space") {
2395
+ return false;
2396
+ } else {
2397
+ const spaceKey = context.space.invitation?.spaceKey?.toHex();
2398
+ const succeededKeys = context.space.succeededKeys;
2399
+ return spaceKey ? !!succeededKeys?.has(spaceKey) : false;
2400
+ }
2401
+ },
2402
+ target: successTarget,
2403
+ actions: "log"
2404
+ },
2405
+ {
2406
+ cond: (context) => {
2407
+ const invitation = context[Kind.toLowerCase()].invitation;
2408
+ return !invitation || invitation?.state === Invitation5.State.CONNECTING;
2409
+ },
2410
+ target: `connecting${Kind}Invitation`,
2411
+ actions: "log"
2412
+ },
2413
+ {
2414
+ target: `inputting${Kind}VerificationCode`,
2415
+ actions: "log"
2416
+ }
2417
+ ]
2418
+ },
2419
+ [`connecting${Kind}Invitation`]: {},
2420
+ [`inputting${Kind}VerificationCode`]: {},
2421
+ [`authenticating${Kind}VerificationCode`]: {
2422
+ on: {
2423
+ [`readyForAuthentication${Kind}Invitation`]: {
2424
+ target: `authenticationFailing${Kind}VerificationCode`,
2425
+ actions: [
2426
+ "setInvitation",
2427
+ "log"
2428
+ ]
2429
+ }
2430
+ }
2431
+ },
2432
+ [`authenticationFailing${Kind}VerificationCode`]: {},
2433
+ [`failing${Kind}Invitation`]: {},
2434
+ [`success${Kind}Invitation`]: {}
2435
+ },
2436
+ on: {
2437
+ [`reset${Kind}Invitation`]: {
2438
+ target: `#join${Kind === "Halo" ? ".choosingIdentity" : ""}.accepting${Kind}Invitation.unknown${Kind}`,
2439
+ actions: [
2440
+ "resetInvitation",
2441
+ "log"
2442
+ ]
2443
+ },
2444
+ [`connecting${Kind}Invitation`]: {
2445
+ target: `.connecting${Kind}Invitation`,
2446
+ actions: [
2447
+ "setInvitation",
2448
+ "log"
2449
+ ]
2450
+ },
2451
+ [`connected${Kind}Invitation`]: {
2452
+ target: `.connecting${Kind}Invitation`,
2453
+ actions: [
2454
+ "setInvitation",
2455
+ "log"
2456
+ ]
2457
+ },
2458
+ [`readyForAuthentication${Kind}Invitation`]: {
2459
+ target: `.inputting${Kind}VerificationCode`,
2460
+ actions: [
2461
+ "setInvitation",
2462
+ "log"
2463
+ ]
2464
+ },
2465
+ [`authenticating${Kind}Invitation`]: {
2466
+ target: `.authenticating${Kind}VerificationCode`,
2467
+ actions: [
2468
+ "setInvitation",
2469
+ "log"
2470
+ ]
2471
+ },
2472
+ [`success${Kind}Invitation`]: {
2473
+ target: successTarget,
2474
+ actions: [
2475
+ "setInvitation",
2476
+ "log"
2477
+ ]
2478
+ },
2479
+ [`fail${Kind}Invitation`]: {
2480
+ target: `.failing${Kind}Invitation`,
2481
+ actions: [
2482
+ assign2({
2483
+ [Kind.toLowerCase()]: (context, event) => ({
2484
+ ...context[Kind.toLowerCase()],
2485
+ failReason: event.reason
2486
+ })
2487
+ }),
2488
+ "log"
2489
+ ]
2490
+ }
2491
+ }
2492
+ }
2493
+ },
2494
+ on: {
2495
+ [`set${Kind}InvitationCode`]: {
2496
+ target: `.acceptingRedeemed${Kind}Invitation`,
2497
+ actions: [
2498
+ assign2({
2499
+ [Kind.toLowerCase()]: (context, event) => ({
2500
+ ...context[Kind.toLowerCase()],
2501
+ unredeemedCode: event.code,
2502
+ succeededKeys: event.succeededKeys
2503
+ })
2504
+ }),
2505
+ `redeem${Kind}InvitationCode`,
2506
+ `invalidate${Kind}InvitationCode`,
2507
+ "log"
2508
+ ]
2509
+ }
2510
+ }
2511
+ };
2512
+ return config;
2513
+ };
2514
+ var joinMachine = createMachine2({
2515
+ id: "join",
2516
+ predictableActionArguments: true,
2517
+ context: {
2518
+ initialDisposition: "default",
2519
+ mode: "default",
2520
+ identity: null,
2521
+ halo: {},
2522
+ space: {}
2523
+ },
2524
+ initial: "unknown",
2525
+ states: {
2526
+ unknown: {
2527
+ always: [
2528
+ {
2529
+ cond: "existingIdentity",
2530
+ target: "resettingIdentity",
2531
+ actions: "log"
2532
+ },
2533
+ {
2534
+ cond: "initiallyAcceptingHaloInvitation",
2535
+ target: "#join.choosingIdentity.acceptingHaloInvitation",
2536
+ actions: "log"
2537
+ },
2538
+ {
2539
+ cond: "initiallyRecoveringIdentity",
2540
+ target: "#join.choosingIdentity.recoveringIdentity",
2541
+ actions: "log"
2542
+ },
2543
+ {
2544
+ cond: "noSelectedIdentity",
2545
+ target: "choosingIdentity",
2546
+ actions: "log"
2547
+ },
2548
+ {
2549
+ target: "acceptingSpaceInvitation",
2550
+ actions: "log"
2551
+ }
2552
+ ]
2553
+ },
2554
+ // TODO(wittjosiah): Remove.
2555
+ choosingIdentity: {
2556
+ initial: "unknownAuthMethod",
2557
+ states: {
2558
+ unknownAuthMethod: {
2559
+ always: [
2560
+ {
2561
+ cond: "hasHaloUnredeemedCode",
2562
+ target: "acceptingHaloInvitation",
2563
+ actions: "log"
2564
+ },
2565
+ {
2566
+ target: "choosingAuthMethod",
2567
+ actions: "log"
2568
+ }
2569
+ ]
2570
+ },
2571
+ choosingAuthMethod: {},
2572
+ recoveringIdentity: {},
2573
+ creatingIdentity: {},
2574
+ acceptingHaloInvitation: acceptingInvitationTemplate("Halo", "#join.acceptingSpaceInvitation"),
2575
+ confirmingAddedIdentity: {}
2576
+ },
2577
+ on: {
2578
+ recoverIdentity: {
2579
+ target: ".recoveringIdentity",
2580
+ actions: "log"
2581
+ },
2582
+ createIdentity: {
2583
+ target: ".creatingIdentity",
2584
+ actions: "log"
2585
+ },
2586
+ acceptHaloInvitation: {
2587
+ target: ".acceptingHaloInvitation",
2588
+ actions: "log"
2589
+ },
2590
+ addIdentity: {
2591
+ target: ".confirmingAddedIdentity",
2592
+ actions: "log"
2593
+ },
2594
+ selectIdentity: {
2595
+ target: "acceptingSpaceInvitation",
2596
+ actions: [
2597
+ "setIdentity",
2598
+ "log"
2599
+ ]
2600
+ },
2601
+ deselectAuthMethod: {
2602
+ target: ".choosingAuthMethod",
2603
+ actions: "log"
2604
+ }
2605
+ }
2606
+ },
2607
+ // TODO(wittjosiah): Factor out to a machine handling recovery and device invitations.
2608
+ resettingIdentity: {},
2609
+ acceptingSpaceInvitation: acceptingInvitationTemplate("Space", "#join.finishingJoiningSpace"),
2610
+ finishingJoiningSpace: {
2611
+ type: "final"
2612
+ },
2613
+ finishingJoiningHalo: {
2614
+ type: "final"
2615
+ }
2616
+ },
2617
+ on: {
2618
+ resetIdentity: {
2619
+ target: "choosingIdentity",
2620
+ actions: "unsetIdentity"
2621
+ }
2622
+ }
2623
+ }, {
2624
+ guards: {
2625
+ existingIdentity: ({ identity, mode }, _event) => mode === "halo-only" && !!identity,
2626
+ noSelectedIdentity: ({ identity }, _event) => !identity,
2627
+ initiallyAcceptingHaloInvitation: ({ identity, initialDisposition }, _event) => !identity && initialDisposition === "accept-halo-invitation",
2628
+ initiallyRecoveringIdentity: ({ identity, initialDisposition }, _event) => !identity && initialDisposition === "recover-identity",
2629
+ hasHaloUnredeemedCode: ({ halo }, _event) => !!halo.unredeemedCode,
2630
+ noSpaceInvitation: ({ space }, _event) => !space.invitation && !space.unredeemedCode
2631
+ },
2632
+ actions: {
2633
+ setIdentity: assign2({
2634
+ identity: (context, event) => event.identity
2635
+ }),
2636
+ unsetIdentity: assign2({
2637
+ identity: () => null
2638
+ }),
2639
+ resetInvitation: assign2({
2640
+ halo: (context, event) => {
2641
+ if (event.type !== "resetHaloInvitation") {
2642
+ return context.halo;
2643
+ }
2644
+ if (context.halo.invitationObservable) {
2645
+ void context.halo.invitationObservable.cancel();
2646
+ }
2647
+ return {
2648
+ ...context.halo,
2649
+ invitation: void 0,
2650
+ invitationObservable: void 0,
2651
+ invitationSubscribable: void 0
2652
+ };
2653
+ },
2654
+ space: (context, event) => {
2655
+ if (event.type !== "resetSpaceInvitation") {
2656
+ return context.space;
2657
+ }
2658
+ if (context.space.invitationObservable) {
2659
+ void context.space.invitationObservable.cancel();
2660
+ }
2661
+ return {
2662
+ ...context.space,
2663
+ invitation: void 0,
2664
+ invitationObservable: void 0,
2665
+ invitationSubscribable: void 0
2666
+ };
2667
+ }
2668
+ }),
2669
+ setInvitation: assign2({
2670
+ halo: (context, event) => event.type.includes("Halo") ? {
2671
+ ...context.halo,
2672
+ invitation: event.invitation
2673
+ } : context.halo,
2674
+ space: (context, event) => event.type.includes("Space") ? {
2675
+ ...context.space,
2676
+ invitation: event.invitation
2677
+ } : context.space
2678
+ }),
2679
+ log: (context, event) => {
2680
+ log7("[transition]", {
2681
+ event,
2682
+ haloInvitation: context.halo.invitation,
2683
+ spaceInvitation: context.space.invitation
2684
+ }, {
2685
+ F: __dxlog_file7,
2686
+ L: 425,
2687
+ S: void 0,
2688
+ C: (f, a) => f(...a)
2689
+ });
2690
+ }
2691
+ }
2692
+ });
2693
+ var defaultCodeFromUrl = (invitationType, text) => {
2694
+ try {
2695
+ const searchParams = new URLSearchParams(text.substring(text.lastIndexOf("?")));
2696
+ return searchParams.get(`${invitationType}InvitationCode`) ?? searchParams.get(`${invitationType}Invitation`) ?? searchParams.get("invitationCode") ?? searchParams.get("invitation") ?? text;
2697
+ } catch (err) {
2698
+ log7.catch(err, void 0, {
2699
+ F: __dxlog_file7,
2700
+ L: 452,
2701
+ S: void 0,
2702
+ C: (f, a) => f(...a)
2703
+ });
2704
+ return text;
2705
+ }
2706
+ };
2707
+ var useJoinMachine = (client, options) => {
2708
+ const redeemHaloInvitationCode = useCallback5(({ halo }) => {
2709
+ if (halo.unredeemedCode) {
2710
+ try {
2711
+ const invitation = InvitationEncoder3.decode(defaultCodeFromUrl("halo", halo.unredeemedCode));
2712
+ const invitationObservable = client.halo.join(invitation);
2713
+ return {
2714
+ ...halo,
2715
+ invitationObservable,
2716
+ invitationSubscribable: getInvitationSubscribable("Halo", invitationObservable)
2717
+ };
2718
+ } catch (err) {
2719
+ log7.error("Could not redeem device invitation code", err, {
2720
+ F: __dxlog_file7,
2721
+ L: 473,
2722
+ S: void 0,
2723
+ C: (f, a) => f(...a)
2724
+ });
2725
+ return halo;
2726
+ }
2727
+ } else {
2728
+ return halo;
2729
+ }
2730
+ }, [
2731
+ client
2732
+ ]);
2733
+ const redeemSpaceInvitationCode = useCallback5(({ space }) => {
2734
+ if (space.unredeemedCode) {
2735
+ try {
2736
+ const invitation = InvitationEncoder3.decode(defaultCodeFromUrl("space", space.unredeemedCode));
2737
+ const invitationObservable = client.spaces.join(invitation);
2738
+ return {
2739
+ ...space,
2740
+ invitationObservable,
2741
+ invitationSubscribable: getInvitationSubscribable("Space", invitationObservable)
2742
+ };
2743
+ } catch (err) {
2744
+ log7.error("Could not redeem space invitation code", err, {
2745
+ F: __dxlog_file7,
2746
+ L: 495,
2747
+ S: void 0,
2748
+ C: (f, a) => f(...a)
2749
+ });
2750
+ return space;
2751
+ }
2752
+ } else {
2753
+ return space;
2754
+ }
2755
+ }, [
2756
+ client
2757
+ ]);
2758
+ const invalidateHaloInvitationCode = useCallback5(({ halo }) => {
2759
+ if (halo.unredeemedCode) {
2760
+ options?.onInvalidateInvitationCode?.(halo.unredeemedCode);
2761
+ return {
2762
+ ...halo,
2763
+ unredeemedCode: void 0
2764
+ };
2765
+ } else {
2766
+ return halo;
2767
+ }
2768
+ }, [
2769
+ options?.onInvalidateInvitationCode
2770
+ ]);
2771
+ const invalidateSpaceInvitationCode = useCallback5(({ space }) => {
2772
+ if (space.unredeemedCode) {
2773
+ options?.onInvalidateInvitationCode?.(space.unredeemedCode);
2774
+ return {
2775
+ ...space,
2776
+ unredeemedCode: void 0
2777
+ };
2778
+ } else {
2779
+ return space;
2780
+ }
2781
+ }, [
2782
+ options?.onInvalidateInvitationCode
2783
+ ]);
2784
+ return useMachine2(joinMachine, {
2785
+ ...options,
2786
+ actions: {
2787
+ ...options?.actions,
2788
+ redeemHaloInvitationCode: assign2({
2789
+ halo: redeemHaloInvitationCode
2790
+ }),
2791
+ redeemSpaceInvitationCode: assign2({
2792
+ space: redeemSpaceInvitationCode
2793
+ }),
2794
+ invalidateHaloInvitationCode: assign2({
2795
+ halo: invalidateHaloInvitationCode
2796
+ }),
2797
+ invalidateSpaceInvitationCode: assign2({
2798
+ space: invalidateSpaceInvitationCode
2799
+ })
2800
+ }
2801
+ });
2802
+ };
2803
+
2804
+ // packages/sdk/shell/src/panels/JoinPanel/steps/AdditionMethodChooser.tsx
2805
+ import { CaretRight as CaretRight2, Plus as Plus3, QrCode, Textbox } from "@phosphor-icons/react";
2806
+ import React31 from "react";
2807
+ import { useTranslation as useTranslation16 } from "@dxos/react-ui";
2808
+ import { getSize as getSize15 } from "@dxos/react-ui-theme";
2809
+ var AdditionMethodChooser = (viewStateProps) => {
2810
+ const disabled = !viewStateProps.active;
2811
+ const { send } = viewStateProps;
2812
+ const { t } = useTranslation16("os");
2813
+ const sharedButtonProps = {
2814
+ disabled,
2815
+ after: /* @__PURE__ */ React31.createElement(CaretRight2, {
2816
+ className: getSize15(4),
2817
+ weight: "bold"
2818
+ }),
2819
+ slots: {
2820
+ label: {
2821
+ className: "text-sm"
2822
+ }
2823
+ }
2824
+ };
2825
+ return /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement(StepHeading, null, t("addition method chooser title")), /* @__PURE__ */ React31.createElement("div", {
2826
+ role: "none",
2827
+ className: "flex flex-col gap-1 grow"
2828
+ }, /* @__PURE__ */ React31.createElement(CompoundButton, {
2829
+ ...sharedButtonProps,
2830
+ description: t("create identity description"),
2831
+ before: /* @__PURE__ */ React31.createElement(Plus3, {
2832
+ className: getSize15(6)
2833
+ }),
2834
+ onClick: () => send({
2835
+ type: "createIdentity"
2836
+ }),
2837
+ "data-autofocus": "choosingAuthMethod",
2838
+ "data-testid": "identity-chooser.create-identity"
2839
+ }, t("create identity label")), /* @__PURE__ */ React31.createElement(CompoundButton, {
2840
+ ...sharedButtonProps,
2841
+ description: t("join identity description"),
2842
+ before: /* @__PURE__ */ React31.createElement(QrCode, {
2843
+ className: getSize15(6)
2844
+ }),
2845
+ onClick: () => send({
2846
+ type: "acceptHaloInvitation"
2847
+ }),
2848
+ "data-testid": "identity-chooser.join-identity"
2849
+ }, t("join identity label")), /* @__PURE__ */ React31.createElement(CompoundButton, {
2850
+ ...sharedButtonProps,
2851
+ description: t("recover identity description"),
2852
+ before: /* @__PURE__ */ React31.createElement(Textbox, {
2853
+ className: getSize15(6)
2854
+ }),
2855
+ onClick: () => send({
2856
+ type: "recoverIdentity"
2857
+ }),
2858
+ "data-testid": "identity-chooser.recover-identity"
2859
+ }, t("recover identity label"))));
2860
+ };
2861
+
2862
+ // packages/sdk/shell/src/panels/JoinPanel/steps/IdentityAdded.tsx
2863
+ import React32, { cloneElement as cloneElement2 } from "react";
2864
+ import { generateName as generateName4 } from "@dxos/display-name";
2865
+ import { Avatar as Avatar7, useId as useId7, useTranslation as useTranslation17 } from "@dxos/react-ui";
2866
+ import { mx as mx23 } from "@dxos/react-ui-theme";
2867
+ import { hexToFallback } from "@dxos/util";
2868
+ var IdentityAdded = (props) => {
2869
+ const { mode, addedIdentity, active, send, onDone, doneActionParent } = props;
2870
+ const disabled = !active;
2871
+ const { t } = useTranslation17("os");
2872
+ const addedIdentityHex = addedIdentity?.identityKey.toHex() ?? "0";
2873
+ const fallbackValue = hexToFallback(addedIdentityHex);
2874
+ const labelId = useId7("identityListItem__label");
2875
+ const displayName = addedIdentity?.profile?.displayName ?? (addedIdentity && generateName4(addedIdentityHex));
2876
+ const doneAction = /* @__PURE__ */ React32.createElement(Action, {
2877
+ ...onDone && {
2878
+ onClick: () => onDone(null)
2879
+ },
2880
+ disabled,
2881
+ "data-autofocus": "confirmingAddedIdentity",
2882
+ "data-testid": "identity-added-done"
2883
+ }, /* @__PURE__ */ React32.createElement("span", null, t("done label")));
2884
+ return /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(StepHeading, null, t("identity added label")), /* @__PURE__ */ React32.createElement("div", {
2885
+ role: "none",
2886
+ className: "grow flex flex-col items-center justify-center text-center gap-2"
2887
+ }, /* @__PURE__ */ React32.createElement(Avatar7.Root, {
2888
+ status: "active",
2889
+ labelId,
2890
+ hue: addedIdentity?.profile?.data?.hue || fallbackValue.hue
2891
+ }, /* @__PURE__ */ React32.createElement(Avatar7.Frame, null, /* @__PURE__ */ React32.createElement(Avatar7.Fallback, {
2892
+ text: addedIdentity?.profile?.data?.emoji || fallbackValue.emoji
2893
+ })), /* @__PURE__ */ React32.createElement(Avatar7.Label, {
2894
+ classNames: mx23("text-lg truncate", !addedIdentity?.profile?.displayName && "font-mono")
2895
+ }, displayName))), /* @__PURE__ */ React32.createElement(Actions, null, mode === "halo-only" ? doneActionParent ? /* @__PURE__ */ cloneElement2(doneActionParent, {}, doneAction) : doneAction : /* @__PURE__ */ React32.createElement(Action, {
2896
+ variant: "primary",
2897
+ disabled: disabled || !addedIdentity,
2898
+ onClick: () => addedIdentity && send({
2899
+ type: "selectIdentity",
2900
+ identity: addedIdentity
2901
+ }),
2902
+ "data-autofocus": "confirmingAddedIdentity"
2903
+ }, t("continue label"))));
2904
+ };
2905
+
2906
+ // packages/sdk/shell/src/panels/JoinPanel/steps/IdentityInput.tsx
2907
+ import React33, { useState as useState10 } from "react";
2908
+ import { log as log8 } from "@dxos/log";
2909
+ import { useClient as useClient7 } from "@dxos/react-client";
2910
+ import { useTranslation as useTranslation18 } from "@dxos/react-ui";
2911
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/JoinPanel/steps/IdentityInput.tsx";
2912
+ var IdentityInput = (props) => {
2913
+ const { send, method } = props;
2914
+ const isRecover = method === "recover identity";
2915
+ const client = useClient7();
2916
+ const { t } = useTranslation18("os");
2917
+ const [validationMessage, setValidationMessage] = useState10("");
2918
+ const createIdentity = (value) => {
2919
+ if (isRecover) {
2920
+ void client.halo.recoverIdentity({
2921
+ seedphrase: value
2922
+ }).then((identity) => {
2923
+ send?.({
2924
+ type: "selectIdentity",
2925
+ identity
2926
+ });
2927
+ }, (error) => {
2928
+ log8.catch(error, void 0, {
2929
+ F: __dxlog_file8,
2930
+ L: 38,
2931
+ S: void 0,
2932
+ C: (f, a) => f(...a)
2933
+ });
2934
+ setValidationMessage(t("failed to recover identity message"));
2935
+ });
2936
+ } else {
2937
+ void client.halo.createIdentity({
2938
+ displayName: value
2939
+ }).then((identity) => {
2940
+ send?.({
2941
+ type: "selectIdentity",
2942
+ identity
2943
+ });
2944
+ }, (error) => {
2945
+ log8.catch(error, void 0, {
2946
+ F: __dxlog_file8,
2947
+ L: 48,
2948
+ S: void 0,
2949
+ C: (f, a) => f(...a)
2950
+ });
2951
+ setValidationMessage(t("failed to create identity message"));
2952
+ });
2953
+ }
2954
+ };
2955
+ return /* @__PURE__ */ React33.createElement(IdentityInputImpl, {
2956
+ ...props,
2957
+ onDisplayNameConfirm: createIdentity,
2958
+ validationMessage
2959
+ });
2960
+ };
2961
+ var IdentityInputImpl = (props) => {
2962
+ const { method, send, active, onDisplayNameConfirm, validationMessage } = props;
2963
+ const disabled = !active;
2964
+ const { t } = useTranslation18("os");
2965
+ const [inputValue, setInputValue] = useState10("");
2966
+ const isRecover = method === "recover identity";
2967
+ return /* @__PURE__ */ React33.createElement(React33.Fragment, null, /* @__PURE__ */ React33.createElement("div", {
2968
+ role: "none",
2969
+ className: "grow flex flex-col justify-center"
2970
+ }, /* @__PURE__ */ React33.createElement(Input, {
2971
+ validationMessage,
2972
+ label: /* @__PURE__ */ React33.createElement(StepHeading, null, t(isRecover ? "recover identity input label" : "new identity input label")),
2973
+ disabled,
2974
+ "data-testid": "identity-input",
2975
+ placeholder: isRecover ? "Type your recovery phrase" : "Type a display name",
2976
+ onChange: ({ target: { value } }) => setInputValue(value)
2977
+ })), /* @__PURE__ */ React33.createElement(Actions, null, /* @__PURE__ */ React33.createElement(Action, {
2978
+ variant: "ghost",
2979
+ disabled,
2980
+ onClick: () => send?.({
2981
+ type: "deselectAuthMethod"
2982
+ }),
2983
+ "data-testid": `${method === "recover identity" ? "recover" : "create"}-identity-input-back`
2984
+ }, t("back label")), /* @__PURE__ */ React33.createElement(Action, {
2985
+ variant: "primary",
2986
+ disabled,
2987
+ onClick: () => onDisplayNameConfirm?.(inputValue),
2988
+ "data-testid": `${method === "recover identity" ? "recover" : "create"}-identity-input-continue`
2989
+ }, t("continue label"))));
2990
+ };
2991
+
2992
+ // packages/sdk/shell/src/panels/JoinPanel/steps/InvitationAccepted.tsx
2993
+ import React34, { cloneElement as cloneElement3 } from "react";
2994
+ import { useTranslation as useTranslation19 } from "@dxos/react-ui";
2995
+
2996
+ // packages/sdk/shell/src/panels/JoinPanel/steps/InvitationInput.tsx
2997
+ import React35, { useState as useState11, useEffect as useEffect5, cloneElement as cloneElement4 } from "react";
2998
+ import { log as log9 } from "@dxos/log";
2999
+ import { useTranslation as useTranslation20 } from "@dxos/react-ui";
3000
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/JoinPanel/steps/InvitationInput.tsx";
3001
+ var invitationCodeFromUrl = (text) => {
3002
+ try {
3003
+ const searchParams = new URLSearchParams(text.substring(text.lastIndexOf("?")));
3004
+ const invitation = searchParams.get("spaceInvitationCode") ?? searchParams.get("deviceInvitationCode");
3005
+ return invitation ?? text;
3006
+ } catch (err) {
3007
+ log9.catch(err, void 0, {
3008
+ F: __dxlog_file9,
3009
+ L: 25,
3010
+ S: void 0,
3011
+ C: (f, a) => f(...a)
3012
+ });
3013
+ return text;
3014
+ }
3015
+ };
3016
+ var InvitationInput = (props) => {
3017
+ const { Kind, active, send, unredeemedCode, onExit, exitActionParent, onDone, doneActionParent, succeededKeys } = props;
3018
+ const disabled = !active;
3019
+ const { t } = useTranslation20("os");
3020
+ const [inputValue, setInputValue] = useState11(unredeemedCode ?? "");
3021
+ useEffect5(() => {
3022
+ unredeemedCode && setInputValue(unredeemedCode ?? "");
3023
+ }, [
3024
+ unredeemedCode
3025
+ ]);
3026
+ const handleNext = () => {
3027
+ if (inputValue.trim().length === 0) {
3028
+ return;
3029
+ }
3030
+ send({
3031
+ type: `set${Kind}InvitationCode`,
3032
+ code: invitationCodeFromUrl(inputValue),
3033
+ ...Kind === "Space" && {
3034
+ succeededKeys
3035
+ }
3036
+ });
3037
+ };
3038
+ const exitAction = /* @__PURE__ */ React35.createElement(Action, {
3039
+ variant: "ghost",
3040
+ disabled,
3041
+ ...onExit ? {
3042
+ onClick: () => onExit()
3043
+ } : {
3044
+ onClick: () => onDone?.(null)
3045
+ },
3046
+ "data-testid": "join-exit"
3047
+ }, t("cancel label"));
3048
+ return /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("div", {
3049
+ role: "none",
3050
+ className: "grow flex flex-col justify-center"
3051
+ }, /* @__PURE__ */ React35.createElement(Input, {
3052
+ label: /* @__PURE__ */ React35.createElement(StepHeading, null, t("invitation input label")),
3053
+ // TODO(burdon): Translation.
3054
+ placeholder: "Type an invitation code",
3055
+ disabled,
3056
+ value: inputValue,
3057
+ onChange: ({ target: { value } }) => setInputValue(value),
3058
+ "data-autofocus": `inputting${Kind}InvitationCode`,
3059
+ "data-testid": `${Kind.toLowerCase()}-invitation-input`,
3060
+ onKeyUp: ({ key }) => key === "Enter" && handleNext()
3061
+ })), /* @__PURE__ */ React35.createElement(Actions, null, Kind === "Halo" ? null : exitActionParent ? /* @__PURE__ */ cloneElement4(exitActionParent, {}, exitAction) : doneActionParent ? /* @__PURE__ */ cloneElement4(doneActionParent, {}, exitAction) : exitAction, /* @__PURE__ */ React35.createElement(Action, {
3062
+ variant: "primary",
3063
+ disabled: disabled || inputValue.trim().length === 0,
3064
+ onClick: handleNext,
3065
+ "data-testid": `${Kind.toLowerCase()}-invitation-input-continue`
3066
+ }, t("continue label"))));
3067
+ };
3068
+
3069
+ // packages/sdk/shell/src/panels/JoinPanel/steps/InvitationRescuer.tsx
3070
+ import React36 from "react";
3071
+ import { useTranslation as useTranslation21 } from "@dxos/react-ui";
3072
+ import { descriptionText as descriptionText10 } from "@dxos/react-ui-theme";
3073
+ var InvitationActions = ({
3074
+ // `invitationState` does report correct state, but here we evaluate whether `failReason` is a better source of truth
3075
+ // for the UI in particular.
3076
+ invitationState,
3077
+ onInvitationCancel,
3078
+ active,
3079
+ send,
3080
+ Kind,
3081
+ failReason
3082
+ }) => {
3083
+ const { t } = useTranslation21("os");
3084
+ if (failReason) {
3085
+ return /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement(StepHeading, {
3086
+ className: descriptionText10
3087
+ }, t(failReason === "timeout" ? "timeout status label" : failReason === "cancelled" ? "cancelled status label" : "error status label")), /* @__PURE__ */ React36.createElement("div", {
3088
+ role: "none",
3089
+ className: "grow"
3090
+ }), /* @__PURE__ */ React36.createElement(Actions, null, /* @__PURE__ */ React36.createElement(Action, {
3091
+ disabled: !active,
3092
+ onClick: () => send({
3093
+ type: `reset${Kind}Invitation`
3094
+ }),
3095
+ "data-testid": "invitation-rescuer-reset"
3096
+ }, t("reset label"))));
3097
+ } else {
3098
+ return /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement(StepHeading, {
3099
+ className: descriptionText10
3100
+ }, t("connecting status label")), /* @__PURE__ */ React36.createElement("div", {
3101
+ role: "none",
3102
+ className: "grow"
3103
+ }), /* @__PURE__ */ React36.createElement(Actions, null, /* @__PURE__ */ React36.createElement(Action, {
3104
+ disabled: true,
3105
+ classNames: "order-2",
3106
+ "data-testid": "next"
3107
+ }, t("next label")), /* @__PURE__ */ React36.createElement(Action, {
3108
+ disabled: !active,
3109
+ onClick: onInvitationCancel,
3110
+ "data-testid": "invitation-rescuer-cancel"
3111
+ }, t("cancel label"))));
3112
+ }
3113
+ };
3114
+ var InvitationRescuer = (props) => {
3115
+ const { Kind, invitationState, active, send } = props;
3116
+ const { t } = useTranslation21("os");
3117
+ return /* @__PURE__ */ React36.createElement(React36.Fragment, null, typeof invitationState === "undefined" ? /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("div", {
3118
+ role: "none",
3119
+ className: "grow flex flex-col justify-center"
3120
+ }, /* @__PURE__ */ React36.createElement(StepHeading, {
3121
+ className: descriptionText10
3122
+ }, "There was a problem joining the space")), /* @__PURE__ */ React36.createElement(Actions, null, /* @__PURE__ */ React36.createElement(Action, {
3123
+ disabled: !active,
3124
+ "data-testid": "invitation-rescuer-blank-reset",
3125
+ onClick: () => send({
3126
+ type: `reset${Kind}Invitation`
3127
+ })
3128
+ }, t("reset label")))) : /* @__PURE__ */ React36.createElement(InvitationActions, props));
3129
+ };
3130
+
3131
+ // packages/sdk/shell/src/panels/JoinPanel/steps/InvitationAuthenticator.tsx
3132
+ import React37, { useState as useState12 } from "react";
3133
+ import { Invitation as Invitation6 } from "@dxos/react-client/invitations";
3134
+ import { Input as Input3, useTranslation as useTranslation22 } from "@dxos/react-ui";
3135
+ import { descriptionText as descriptionText11 } from "@dxos/react-ui-theme";
3136
+ import { hexToEmoji as hexToEmoji5 } from "@dxos/util";
3137
+ var pinLength = 6;
3138
+ var InvitationAuthenticator = ({ failed, Kind, authMethod, active, pending, invitationId, onInvitationAuthenticate, onInvitationCancel }) => {
3139
+ const disabled = !active || pending;
3140
+ const { t } = useTranslation22("os");
3141
+ const invitationType = Kind.toLowerCase();
3142
+ const [authCode, setAuthCode] = useState12("");
3143
+ const onChange = ({ target: { value } }) => {
3144
+ setAuthCode(value);
3145
+ if (value.length === pinLength) {
3146
+ document.querySelector(`[data-autofocus-pinlength="${invitationType}"]`)?.focus();
3147
+ }
3148
+ };
3149
+ return /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("div", {
3150
+ role: "none",
3151
+ className: "grow flex flex-col justify-center gap-4"
3152
+ }, /* @__PURE__ */ React37.createElement(Input3.Root, failed && {
3153
+ validationValence: "error"
3154
+ }, /* @__PURE__ */ React37.createElement(Input3.Label, {
3155
+ asChild: true
3156
+ }, authMethod === Invitation6.AuthMethod.SHARED_SECRET ? /* @__PURE__ */ React37.createElement(StepHeading, null, t("auth code input label")) : /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement(StepHeading, {
3157
+ className: descriptionText11
3158
+ }, t("authenticating label")), /* @__PURE__ */ React37.createElement("div", {
3159
+ role: "none",
3160
+ className: "grow"
3161
+ }))), authMethod === Invitation6.AuthMethod.SHARED_SECRET && /* @__PURE__ */ React37.createElement(Input3.PinInput, {
3162
+ disabled,
3163
+ length: pinLength,
3164
+ onChange,
3165
+ inputMode: "numeric",
3166
+ autoComplete: "off",
3167
+ pattern: "\\d*",
3168
+ "data-autofocus": `connecting${Kind}Invitation inputting${Kind}VerificationCode authenticationFailing${Kind}VerificationCode authenticating${Kind}VerificationCode`,
3169
+ "data-prevent-ios-autofocus": true,
3170
+ "data-testid": `${invitationType}-auth-code-input`,
3171
+ "data-1p-ignore": true
3172
+ }), failed && /* @__PURE__ */ React37.createElement(Input3.DescriptionAndValidation, {
3173
+ classNames: "text-center"
3174
+ }, /* @__PURE__ */ React37.createElement(Input3.Validation, null, t("failed to authenticate message")))), invitationId && authMethod === Invitation6.AuthMethod.SHARED_SECRET && /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement(Label, null, t("auth other device emoji message")), /* @__PURE__ */ React37.createElement("div", {
3175
+ className: "flex justify-center"
3176
+ }, /* @__PURE__ */ React37.createElement(Emoji, {
3177
+ text: hexToEmoji5(invitationId)
3178
+ })))), /* @__PURE__ */ React37.createElement(Actions, null, /* @__PURE__ */ React37.createElement(Action, {
3179
+ variant: "ghost",
3180
+ disabled,
3181
+ onClick: () => onInvitationCancel?.(),
3182
+ "data-testid": `${invitationType}-invitation-authenticator-cancel`
3183
+ }, t("cancel label")), /* @__PURE__ */ React37.createElement(Action, {
3184
+ variant: "primary",
3185
+ disabled,
3186
+ onClick: () => onInvitationAuthenticate?.(authCode),
3187
+ "data-autofocus-pinlength": invitationType,
3188
+ "data-testid": `${invitationType}-invitation-authenticator-next`
3189
+ }, t("next label"))));
3190
+ };
3191
+
3192
+ // packages/sdk/shell/src/styles/panelStyles.ts
3193
+ var stepStyles = "pbs-1 pbe-3 pli-3";
3194
+
3195
+ // packages/sdk/shell/src/styles/invitationStatusStyles.ts
3196
+ import { Invitation as Invitation7 } from "@dxos/react-client/invitations";
3197
+ var inactiveBgColor = "bg-neutral-100 dark:bg-neutral-600";
3198
+ var activeBgColor = "bg-primary-500 dark:bg-primary-400";
3199
+ var successBgColor = "bg-success-500 dark:bg-success-400";
3200
+ var errorBgColor = "bg-error-500 dark:bg-error-400";
3201
+ var cancelledBgColor = "bg-warning-500 dark:bg-warning-400";
3202
+ var inactiveTextColor = "text-neutral-100 dark:text-neutral-600";
3203
+ var activeTextColor = "text-primary-400 dark:text-primary-500";
3204
+ var successTextColor = "text-success-400 dark:text-success-500";
3205
+ var errorTextColor = "text-error-400 dark:text-error-500";
3206
+ var cancelledTextColor = "text-warning-400 dark:text-warning-500";
3207
+ var inactiveStrokeColor = "stroke-neutral-100 dark:stroke-neutral-700";
3208
+ var activeStrokeColor = "stroke-primary-400 dark:stroke-primary-150";
3209
+ var successStrokeColor = "stroke-success-300 dark:stroke-primary-400";
3210
+ var errorStrokeColor = "stroke-error-300 dark:stroke-error-500";
3211
+ var cancelledStrokeColor = "stroke-warning-300 dark:stroke-warning-500";
3212
+ var resolvedStrokeColor = (status) => status === Invitation7.State.ERROR ? errorStrokeColor : status === Invitation7.State.CANCELLED || status === Invitation7.State.TIMEOUT ? cancelledStrokeColor : successStrokeColor;
3213
+ var resolvedBgColor = (status) => status === Invitation7.State.ERROR ? errorBgColor : status === Invitation7.State.CANCELLED || status === Invitation7.State.TIMEOUT ? cancelledBgColor : successBgColor;
3214
+ var resolvedTextColor = (status) => status === Invitation7.State.ERROR ? errorTextColor : status === Invitation7.State.CANCELLED || status === Invitation7.State.TIMEOUT ? cancelledTextColor : successTextColor;
3215
+
3216
+ // packages/sdk/shell/src/panels/JoinPanel/JoinPanel.tsx
3217
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/JoinPanel/JoinPanel.tsx";
3218
+ var JoinPanelImpl = (props) => {
3219
+ const {
3220
+ titleId,
3221
+ send,
3222
+ activeView,
3223
+ failed,
3224
+ mode,
3225
+ unredeemedCodes,
3226
+ invitationIds,
3227
+ invitationStates,
3228
+ invitationAuthMethods,
3229
+ succeededKeys,
3230
+ failReasons,
3231
+ onExit,
3232
+ onHaloDone,
3233
+ // onSpaceDone,
3234
+ exitActionParent,
3235
+ doneActionParent,
3236
+ onHaloInvitationCancel,
3237
+ onSpaceInvitationCancel,
3238
+ onHaloInvitationAuthenticate,
3239
+ onSpaceInvitationAuthenticate,
3240
+ onCancelResetStorage,
3241
+ onConfirmResetStorage,
3242
+ IdentityInput: IdentityInputComponent = IdentityInput,
3243
+ ConfirmReset: ConfirmResetComponent = ConfirmReset
3244
+ } = props;
3245
+ return /* @__PURE__ */ React38.createElement(React38.Fragment, null, mode !== "halo-only" && /* @__PURE__ */ React38.createElement(JoinHeading, {
3246
+ titleId,
3247
+ mode,
3248
+ onExit,
3249
+ exitActionParent
3250
+ }), /* @__PURE__ */ React38.createElement(Viewport.Root, {
3251
+ focusManaged: true,
3252
+ activeView
3253
+ }, /* @__PURE__ */ React38.createElement(Viewport.Views, null, /* @__PURE__ */ React38.createElement(Viewport.View, {
3254
+ classNames: stepStyles,
3255
+ id: "addition method chooser"
3256
+ }, /* @__PURE__ */ React38.createElement(AdditionMethodChooser, {
3257
+ send,
3258
+ active: activeView === "addition method chooser"
3259
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3260
+ classNames: stepStyles,
3261
+ id: "reset storage confirmation"
3262
+ }, /* @__PURE__ */ React38.createElement(ConfirmResetComponent, {
3263
+ send,
3264
+ active: activeView === "reset storage confirmation",
3265
+ onCancel: onCancelResetStorage,
3266
+ onConfirm: onConfirmResetStorage
3267
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3268
+ classNames: stepStyles,
3269
+ id: "create identity input"
3270
+ }, /* @__PURE__ */ React38.createElement(IdentityInputComponent, {
3271
+ send,
3272
+ method: "create identity",
3273
+ active: activeView === "create identity input"
3274
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3275
+ classNames: stepStyles,
3276
+ id: "recover identity input"
3277
+ }, /* @__PURE__ */ React38.createElement(IdentityInputComponent, {
3278
+ send,
3279
+ method: "recover identity",
3280
+ active: activeView === "recover identity input"
3281
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3282
+ classNames: stepStyles,
3283
+ id: "halo invitation input"
3284
+ }, /* @__PURE__ */ React38.createElement(InvitationInput, {
3285
+ send,
3286
+ Kind: "Halo",
3287
+ active: activeView === "halo invitation input",
3288
+ ...unredeemedCodes?.Halo && {
3289
+ unredeemedCode: unredeemedCodes.Halo
3290
+ },
3291
+ ...succeededKeys?.Halo && {
3292
+ succeededKeys: succeededKeys.Halo
3293
+ }
3294
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3295
+ classNames: stepStyles,
3296
+ id: "halo invitation rescuer"
3297
+ }, /* @__PURE__ */ React38.createElement(InvitationRescuer, {
3298
+ send,
3299
+ Kind: "Halo",
3300
+ active: activeView === "halo invitation rescuer",
3301
+ invitationState: invitationStates?.Halo,
3302
+ onInvitationCancel: onHaloInvitationCancel,
3303
+ failReason: failReasons?.Halo
3304
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3305
+ classNames: stepStyles,
3306
+ id: "halo invitation authenticator"
3307
+ }, /* @__PURE__ */ React38.createElement(InvitationAuthenticator, {
3308
+ send,
3309
+ Kind: "Halo",
3310
+ invitationId: invitationIds?.Halo,
3311
+ authMethod: invitationAuthMethods?.Halo,
3312
+ active: activeView === "halo invitation authenticator",
3313
+ onInvitationCancel: onHaloInvitationCancel,
3314
+ onInvitationAuthenticate: onHaloInvitationAuthenticate,
3315
+ ...failed.has("Halo") && {
3316
+ failed: true
3317
+ }
3318
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3319
+ classNames: stepStyles,
3320
+ id: "identity added"
3321
+ }, /* @__PURE__ */ React38.createElement(IdentityAdded, {
3322
+ send,
3323
+ mode,
3324
+ active: activeView === "identity added",
3325
+ doneActionParent,
3326
+ onDone: onHaloDone
3327
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3328
+ classNames: stepStyles,
3329
+ id: "space invitation input"
3330
+ }, /* @__PURE__ */ React38.createElement(InvitationInput, {
3331
+ send,
3332
+ Kind: "Space",
3333
+ active: activeView === "space invitation input",
3334
+ ...unredeemedCodes?.Space && {
3335
+ unredeemedCode: unredeemedCodes.Space
3336
+ },
3337
+ ...succeededKeys?.Space && {
3338
+ succeededKeys: succeededKeys.Space
3339
+ },
3340
+ onExit,
3341
+ exitActionParent
3342
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3343
+ classNames: stepStyles,
3344
+ id: "space invitation rescuer"
3345
+ }, /* @__PURE__ */ React38.createElement(InvitationRescuer, {
3346
+ send,
3347
+ Kind: "Space",
3348
+ active: activeView === "space invitation rescuer",
3349
+ invitationState: invitationStates?.Space,
3350
+ onInvitationCancel: onSpaceInvitationCancel,
3351
+ failReason: failReasons?.Space
3352
+ })), /* @__PURE__ */ React38.createElement(Viewport.View, {
3353
+ classNames: stepStyles,
3354
+ id: "space invitation authenticator"
3355
+ }, /* @__PURE__ */ React38.createElement(InvitationAuthenticator, {
3356
+ send,
3357
+ Kind: "Space",
3358
+ invitationId: invitationIds?.Space,
3359
+ authMethod: invitationAuthMethods?.Space,
3360
+ active: activeView === "space invitation authenticator",
3361
+ onInvitationCancel: onSpaceInvitationCancel,
3362
+ onInvitationAuthenticate: onSpaceInvitationAuthenticate,
3363
+ ...failed.has("Space") && {
3364
+ failed: true
3365
+ }
3366
+ })))));
3367
+ };
3368
+ var JoinPanel = ({ titleId: propsTitleId, mode = "default", initialDisposition = "default", initialInvitationCode, exitActionParent, onExit, doneActionParent, onDone: propsOnDone, onCancelResetStorage }) => {
3369
+ const client = useClient8();
3370
+ const identity = useIdentity2();
3371
+ const { hasIosKeyboard } = useThemeContext5();
3372
+ const titleId = useId8("joinPanel__heading", propsTitleId);
3373
+ const [joinState, joinSend, joinService] = useJoinMachine(client, {
3374
+ context: {
3375
+ mode,
3376
+ initialDisposition,
3377
+ identity,
3378
+ ...initialInvitationCode && {
3379
+ [mode === "halo-only" ? "halo" : "space"]: {
3380
+ unredeemedCode: initialInvitationCode
3381
+ }
3382
+ }
3383
+ }
3384
+ });
3385
+ useEffect6(() => {
3386
+ const subscription = joinService.subscribe((state) => {
3387
+ log10("[state]", state, {
3388
+ F: __dxlog_file10,
3389
+ L: 210,
3390
+ S: void 0,
3391
+ C: (f, a) => f(...a)
3392
+ });
3393
+ });
3394
+ return subscription.unsubscribe;
3395
+ }, [
3396
+ joinService
3397
+ ]);
3398
+ useEffect6(() => {
3399
+ const stateStack = joinState.configuration[0].id.split(".");
3400
+ const innermostState = stateStack[stateStack.length - 1];
3401
+ const autoFocusValue = innermostState === "finishingJoining" ? "successSpaceInvitation" : innermostState;
3402
+ const $nextAutofocus = document.querySelector(`[data-autofocus~="${autoFocusValue}"]`);
3403
+ if ($nextAutofocus && !(hasIosKeyboard && $nextAutofocus.hasAttribute("data-prevent-ios-autofocus"))) {
3404
+ $nextAutofocus.focus();
3405
+ }
3406
+ }, [
3407
+ joinState.value,
3408
+ hasIosKeyboard
3409
+ ]);
3410
+ const activeView = useMemo4(() => {
3411
+ switch (true) {
3412
+ case joinState.matches({
3413
+ choosingIdentity: "choosingAuthMethod"
3414
+ }):
3415
+ return "addition method chooser";
3416
+ case joinState.matches("resettingIdentity"):
3417
+ return "reset storage confirmation";
3418
+ case joinState.matches({
3419
+ choosingIdentity: "creatingIdentity"
3420
+ }):
3421
+ return "create identity input";
3422
+ case joinState.matches({
3423
+ choosingIdentity: "recoveringIdentity"
3424
+ }):
3425
+ return "recover identity input";
3426
+ case joinState.matches({
3427
+ choosingIdentity: {
3428
+ acceptingHaloInvitation: "inputtingHaloInvitationCode"
3429
+ }
3430
+ }):
3431
+ return "halo invitation input";
3432
+ case [
3433
+ {
3434
+ choosingIdentity: {
3435
+ acceptingHaloInvitation: {
3436
+ acceptingRedeemedHaloInvitation: "connectingHaloInvitation"
3437
+ }
3438
+ }
3439
+ },
3440
+ {
3441
+ choosingIdentity: {
3442
+ acceptingHaloInvitation: {
3443
+ acceptingRedeemedHaloInvitation: "failingHaloInvitation"
3444
+ }
3445
+ }
3446
+ }
3447
+ ].some(joinState.matches):
3448
+ return "halo invitation rescuer";
3449
+ case [
3450
+ {
3451
+ choosingIdentity: {
3452
+ acceptingHaloInvitation: {
3453
+ acceptingRedeemedHaloInvitation: "inputtingHaloVerificationCode"
3454
+ }
3455
+ }
3456
+ },
3457
+ {
3458
+ choosingIdentity: {
3459
+ acceptingHaloInvitation: {
3460
+ acceptingRedeemedHaloInvitation: "authenticatingHaloVerificationCode"
3461
+ }
3462
+ }
3463
+ },
3464
+ {
3465
+ choosingIdentity: {
3466
+ acceptingHaloInvitation: {
3467
+ acceptingRedeemedHaloInvitation: "authenticationFailingHaloVerificationCode"
3468
+ }
3469
+ }
3470
+ }
3471
+ ].some(joinState.matches):
3472
+ return "halo invitation authenticator";
3473
+ case [
3474
+ {
3475
+ choosingIdentity: {
3476
+ acceptingHaloInvitation: {
3477
+ acceptingRedeemedHaloInvitation: "successHaloInvitation"
3478
+ }
3479
+ }
3480
+ },
3481
+ "finishingJoiningHalo"
3482
+ ].some(joinState.matches):
3483
+ return "halo invitation accepted";
3484
+ case joinState.matches({
3485
+ choosingIdentity: "confirmingAddedIdentity"
3486
+ }):
3487
+ return "identity added";
3488
+ case joinState.matches({
3489
+ acceptingSpaceInvitation: "inputtingSpaceInvitationCode"
3490
+ }):
3491
+ return "space invitation input";
3492
+ case [
3493
+ {
3494
+ acceptingSpaceInvitation: {
3495
+ acceptingRedeemedSpaceInvitation: "connectingSpaceInvitation"
3496
+ }
3497
+ },
3498
+ {
3499
+ acceptingSpaceInvitation: {
3500
+ acceptingRedeemedSpaceInvitation: "failingSpaceInvitation"
3501
+ }
3502
+ }
3503
+ ].some(joinState.matches):
3504
+ return "space invitation rescuer";
3505
+ case [
3506
+ {
3507
+ acceptingSpaceInvitation: {
3508
+ acceptingRedeemedSpaceInvitation: "inputtingSpaceVerificationCode"
3509
+ }
3510
+ },
3511
+ {
3512
+ acceptingSpaceInvitation: {
3513
+ acceptingRedeemedSpaceInvitation: "authenticatingSpaceVerificationCode"
3514
+ }
3515
+ },
3516
+ {
3517
+ acceptingSpaceInvitation: {
3518
+ acceptingRedeemedSpaceInvitation: "authenticationFailingSpaceVerificationCode"
3519
+ }
3520
+ }
3521
+ ].some(joinState.matches):
3522
+ return "space invitation authenticator";
3523
+ case joinState.matches("finishingJoiningSpace"):
3524
+ return "space invitation accepted";
3525
+ default:
3526
+ return "never";
3527
+ }
3528
+ }, [
3529
+ joinState
3530
+ ]);
3531
+ useEffect6(() => {
3532
+ switch (activeView) {
3533
+ case "halo invitation accepted":
3534
+ onHaloDone();
3535
+ break;
3536
+ case "space invitation accepted":
3537
+ onSpaceDone();
3538
+ break;
3539
+ }
3540
+ }, [
3541
+ activeView
3542
+ ]);
3543
+ const failed = useMemo4(() => {
3544
+ const result = /* @__PURE__ */ new Set();
3545
+ switch (true) {
3546
+ case joinState.matches({
3547
+ choosingIdentity: {
3548
+ acceptingHaloInvitation: {
3549
+ acceptingRedeemedHaloInvitation: "authenticationFailingHaloVerificationCode"
3550
+ }
3551
+ }
3552
+ }):
3553
+ result.add("Halo");
3554
+ break;
3555
+ case joinState.matches({
3556
+ acceptingSpaceInvitation: {
3557
+ acceptingRedeemedSpaceInvitation: "authenticationFailingSpaceVerificationCode"
3558
+ }
3559
+ }):
3560
+ result.add("Space");
3561
+ break;
3562
+ }
3563
+ return result;
3564
+ }, [
3565
+ joinState
3566
+ ]);
3567
+ const unredeemedCodes = useMemo4(() => ({
3568
+ Halo: joinState.context.halo.unredeemedCode,
3569
+ Space: joinState.context.space.unredeemedCode
3570
+ }), [
3571
+ joinState
3572
+ ]);
3573
+ const spaces = useMulticastObservable(client.spaces);
3574
+ const succeededKeys = useMemo4(() => ({
3575
+ Space: spaces ? new Set(spaces.map(({ key }) => key.toHex())) : void 0
3576
+ }), [
3577
+ spaces
3578
+ ]);
3579
+ const invitationStates = useMemo4(() => ({
3580
+ Halo: joinState.context.halo.invitationObservable?.get().state,
3581
+ Space: joinState.context.space.invitationObservable?.get().state
3582
+ }), [
3583
+ joinState
3584
+ ]);
3585
+ const invitationAuthMethods = useMemo4(() => ({
3586
+ Halo: joinState.context.halo.invitationObservable?.get().authMethod,
3587
+ Space: joinState.context.space.invitationObservable?.get().authMethod
3588
+ }), [
3589
+ joinState
3590
+ ]);
3591
+ const invitationIds = useMemo4(() => ({
3592
+ Halo: joinState.context.halo.invitationObservable?.get().invitationId,
3593
+ Space: joinState.context.space.invitationObservable?.get().invitationId
3594
+ }), [
3595
+ joinState
3596
+ ]);
3597
+ const failReasons = useMemo4(() => ({
3598
+ Halo: joinState.context.halo.failReason,
3599
+ Space: joinState.context.space.failReason
3600
+ }), [
3601
+ joinState
3602
+ ]);
3603
+ const onHaloDone = useCallback6(() => {
3604
+ propsOnDone?.({
3605
+ identityKey: joinState.context.halo.invitation?.identityKey ?? null,
3606
+ swarmKey: joinState.context.halo.invitation?.swarmKey ?? null,
3607
+ spaceKey: joinState.context.halo.invitation?.spaceKey ?? null,
3608
+ target: joinState.context.halo.invitation?.target ?? null
3609
+ });
3610
+ }, [
3611
+ joinState,
3612
+ propsOnDone
3613
+ ]);
3614
+ const onSpaceDone = useCallback6(() => {
3615
+ propsOnDone?.({
3616
+ identityKey: joinState.context.space.invitation?.identityKey ?? null,
3617
+ swarmKey: joinState.context.space.invitation?.swarmKey ?? null,
3618
+ spaceKey: joinState.context.space.invitation?.spaceKey ?? null,
3619
+ target: joinState.context.space.invitation?.target ?? null
3620
+ });
3621
+ }, [
3622
+ joinState,
3623
+ propsOnDone
3624
+ ]);
3625
+ const onConfirmResetStorage = useCallback6(() => client.reset().then(() => {
3626
+ joinSend({
3627
+ type: "resetIdentity"
3628
+ });
3629
+ }), [
3630
+ client,
3631
+ joinSend
3632
+ ]);
3633
+ return /* @__PURE__ */ React38.createElement(JoinPanelImpl, {
3634
+ titleId,
3635
+ mode,
3636
+ send: joinSend,
3637
+ activeView,
3638
+ failed,
3639
+ failReasons,
3640
+ pending: [
3641
+ "connecting",
3642
+ "authenticating"
3643
+ ].some((str) => joinState?.configuration[0].id.includes(str)),
3644
+ unredeemedCodes,
3645
+ invitationIds,
3646
+ invitationStates,
3647
+ invitationAuthMethods,
3648
+ succeededKeys,
3649
+ identity,
3650
+ onExit,
3651
+ exitActionParent,
3652
+ doneActionParent,
3653
+ onHaloDone,
3654
+ onSpaceDone,
3655
+ onHaloInvitationCancel: () => joinState.context.halo.invitationObservable?.cancel(),
3656
+ onSpaceInvitationCancel: () => joinState.context.space.invitationObservable?.cancel(),
3657
+ onHaloInvitationAuthenticate: (authCode) => {
3658
+ joinSend({
3659
+ type: "authenticateHaloVerificationCode"
3660
+ });
3661
+ return joinState.context.halo.invitationObservable?.authenticate(authCode);
3662
+ },
3663
+ onSpaceInvitationAuthenticate: (authCode) => {
3664
+ joinSend({
3665
+ type: "authenticateSpaceVerificationCode"
3666
+ });
3667
+ return joinState.context.space.invitationObservable?.authenticate(authCode);
3668
+ },
3669
+ onConfirmResetStorage,
3670
+ onCancelResetStorage
3671
+ });
3672
+ };
3673
+
3674
+ // packages/sdk/shell/src/panels/SpacePanel/SpacePanel.tsx
3675
+ import { Planet } from "@phosphor-icons/react";
3676
+ import React40, { useEffect as useEffect7, useMemo as useMemo5 } from "react";
3677
+ import { log as log13 } from "@dxos/log";
3678
+ import { useInvitationStatus as useInvitationStatus3 } from "@dxos/react-client/invitations";
3679
+ import { useId as useId9, useTranslation as useTranslation24 } from "@dxos/react-ui";
3680
+
3681
+ // packages/sdk/shell/src/panels/SpacePanel/spaceMachine.ts
3682
+ import { useMachine as useMachine3 } from "@xstate/react";
3683
+ import { assign as assign3, createMachine as createMachine3 } from "xstate";
3684
+ import { log as log11 } from "@dxos/log";
3685
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/SpacePanel/spaceMachine.ts";
3686
+ var spaceMachine = createMachine3({
3687
+ id: "space",
3688
+ predictableActionArguments: true,
3689
+ context: {
3690
+ space: null
3691
+ },
3692
+ initial: "managingSpace",
3693
+ states: {
3694
+ managingSpace: {},
3695
+ managingSpaceInvitation: {}
3696
+ },
3697
+ on: {
3698
+ deselectInvitation: {
3699
+ target: ".managingSpace",
3700
+ actions: [
3701
+ "unsetInvitation",
3702
+ "log"
3703
+ ]
3704
+ },
3705
+ selectInvitation: {
3706
+ target: ".managingSpaceInvitation",
3707
+ actions: [
3708
+ "setInvitation",
3709
+ "log"
3710
+ ]
3711
+ }
3712
+ }
3713
+ }, {
3714
+ actions: {
3715
+ setInvitation: assign3({
3716
+ invitation: (context, event) => event?.invitation ?? null
3717
+ }),
3718
+ unsetInvitation: assign3({
3719
+ invitation: () => void 0
3720
+ }),
3721
+ log: (context, event) => {
3722
+ log11("[transition]", {
3723
+ event,
3724
+ context
3725
+ }, {
3726
+ F: __dxlog_file11,
3727
+ L: 53,
3728
+ S: void 0,
3729
+ C: (f, a) => f(...a)
3730
+ });
3731
+ }
3732
+ }
3733
+ });
3734
+ var useSpaceMachine = (options) => {
3735
+ return useMachine3(spaceMachine, {
3736
+ ...options,
3737
+ context: {
3738
+ ...options?.context
3739
+ }
3740
+ });
3741
+ };
3742
+
3743
+ // packages/sdk/shell/src/panels/SpacePanel/steps/SpaceManager.tsx
3744
+ import { Placeholder as Placeholder2, UserPlus, UsersThree } from "@phosphor-icons/react";
3745
+ import React39, { useCallback as useCallback7, useState as useState13 } from "react";
3746
+ import { log as log12 } from "@dxos/log";
3747
+ import { useConfig } from "@dxos/react-client";
3748
+ import { useSpaceInvitations as useSpaceInvitations2 } from "@dxos/react-client/echo";
3749
+ import { Invitation as Invitation8, InvitationEncoder as InvitationEncoder4 } from "@dxos/react-client/invitations";
3750
+ import { ScrollArea, useTranslation as useTranslation23 } from "@dxos/react-ui";
3751
+ import { descriptionText as descriptionText12, mx as mx24 } from "@dxos/react-ui-theme";
3752
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/SpacePanel/steps/SpaceManager.tsx";
3753
+ var activeActionKey = "dxos:react-shell/space-manager/active-action";
3754
+ var handleInvitationEvent = (invitation, subscription) => {
3755
+ const invitationCode = InvitationEncoder4.encode(invitation);
3756
+ if (invitation.state === Invitation8.State.CONNECTING) {
3757
+ log12.info(JSON.stringify({
3758
+ invitationCode,
3759
+ authCode: invitation.authCode
3760
+ }), void 0, {
3761
+ F: __dxlog_file12,
3762
+ L: 40,
3763
+ S: void 0,
3764
+ C: (f, a) => f(...a)
3765
+ });
3766
+ subscription.unsubscribe();
3767
+ }
3768
+ };
3769
+ var SpaceManager = (props) => {
3770
+ const { space, target } = props;
3771
+ const { t } = useTranslation23("os");
3772
+ const config = useConfig();
3773
+ const invitations = useSpaceInvitations2(space?.key);
3774
+ const inviteActions = {
3775
+ inviteOne: {
3776
+ label: t("invite one label"),
3777
+ description: t("invite one description"),
3778
+ icon: UserPlus,
3779
+ onClick: useCallback7(() => {
3780
+ const invitation = space.share?.({
3781
+ type: Invitation8.Type.INTERACTIVE,
3782
+ authMethod: Invitation8.AuthMethod.SHARED_SECRET,
3783
+ multiUse: false,
3784
+ target
3785
+ });
3786
+ if (invitation && config.values.runtime?.app?.env?.DX_ENVIRONMENT !== "production") {
3787
+ const subscription = invitation.subscribe((invitation2) => handleInvitationEvent(invitation2, subscription));
3788
+ }
3789
+ }, [
3790
+ space
3791
+ ])
3792
+ },
3793
+ inviteMany: {
3794
+ label: t("invite many label"),
3795
+ description: t("invite many description"),
3796
+ icon: UsersThree,
3797
+ onClick: useCallback7(() => {
3798
+ const invitation = space.share?.({
3799
+ type: Invitation8.Type.DELEGATED,
3800
+ authMethod: Invitation8.AuthMethod.KNOWN_PUBLIC_KEY,
3801
+ multiUse: true,
3802
+ target
3803
+ });
3804
+ if (invitation && config.values.runtime?.app?.env?.DX_ENVIRONMENT !== "production") {
3805
+ const subscription = invitation.subscribe((invitation2) => handleInvitationEvent(invitation2, subscription));
3806
+ }
3807
+ }, [
3808
+ space
3809
+ ])
3810
+ }
3811
+ };
3812
+ return /* @__PURE__ */ React39.createElement(SpaceManagerImpl, {
3813
+ ...props,
3814
+ invitations,
3815
+ inviteActions
3816
+ });
3817
+ };
3818
+ var headingFragment = "pis-3 pie-1 plb-1 mbe-1 font-medium";
3819
+ var SpaceManagerImpl = (props) => {
3820
+ const { active, space, createInvitationUrl, send, inviteActions: propsInviteActions, invitations, showInactiveInvitations, SpaceMemberList: SpaceMemberListComponent = SpaceMemberList, InvitationList: InvitationListComponent = InvitationList } = props;
3821
+ const { t } = useTranslation23("os");
3822
+ const inviteActions = propsInviteActions ?? {
3823
+ noopInvite: {
3824
+ label: t("create space invitation label"),
3825
+ description: "",
3826
+ icon: Placeholder2,
3827
+ onClick: () => {
3828
+ }
3829
+ }
3830
+ };
3831
+ const [activeAction, setInternalActiveAction] = useState13(localStorage.getItem(activeActionKey) ?? "inviteOne");
3832
+ const setActiveAction = (nextAction) => {
3833
+ setInternalActiveAction(nextAction);
3834
+ localStorage.setItem(activeActionKey, nextAction);
3835
+ };
3836
+ const visibleInvitations = showInactiveInvitations ? invitations : invitations?.filter((invitation) => ![
3837
+ Invitation8.State.CANCELLED
3838
+ ].includes(invitation.get().state));
3839
+ return /* @__PURE__ */ React39.createElement(React39.Fragment, null, /* @__PURE__ */ React39.createElement(ScrollArea.Root, {
3840
+ classNames: "grow shrink basis-28 -mli-2"
3841
+ }, /* @__PURE__ */ React39.createElement(ScrollArea.Viewport, {
3842
+ classNames: "is-full pie-2"
3843
+ }, !!visibleInvitations?.length && /* @__PURE__ */ React39.createElement(React39.Fragment, null, /* @__PURE__ */ React39.createElement("h3", {
3844
+ className: mx24(headingFragment, descriptionText12)
3845
+ }, t("invitation list heading")), /* @__PURE__ */ React39.createElement(InvitationListComponent, {
3846
+ className: "mb-2",
3847
+ send,
3848
+ invitations: visibleInvitations ?? [],
3849
+ onClickRemove: (invitation) => invitation.cancel(),
3850
+ createInvitationUrl
3851
+ }), /* @__PURE__ */ React39.createElement("h3", {
3852
+ className: mx24(headingFragment, descriptionText12, "mbs-2")
3853
+ }, t("space member list heading"))), /* @__PURE__ */ React39.createElement(SpaceMemberListComponent, {
3854
+ spaceKey: space.key,
3855
+ includeSelf: true
3856
+ })), /* @__PURE__ */ React39.createElement(ScrollArea.Scrollbar, {
3857
+ orientation: "vertical"
3858
+ }, /* @__PURE__ */ React39.createElement(ScrollArea.Thumb, null))), /* @__PURE__ */ React39.createElement(Actions, null, /* @__PURE__ */ React39.createElement(BifurcatedAction, {
3859
+ disabled: !active,
3860
+ actions: inviteActions,
3861
+ activeAction,
3862
+ onChangeActiveAction: setActiveAction,
3863
+ "data-testid": "spaces-panel.create-invitation"
3864
+ })));
3865
+ };
3866
+
3867
+ // packages/sdk/shell/src/panels/SpacePanel/SpacePanel.tsx
3868
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/panels/SpacePanel/SpacePanel.tsx";
3869
+ var SpacePanelHeading = ({ titleId, space, onDone }) => {
3870
+ const { t } = useTranslation24("os");
3871
+ const name = space.properties.name;
3872
+ return /* @__PURE__ */ React40.createElement(Heading, {
3873
+ titleId,
3874
+ title: t("space panel heading"),
3875
+ corner: /* @__PURE__ */ React40.createElement(CloseButton, {
3876
+ "data-testid": "identity-panel-done",
3877
+ onDone
3878
+ })
3879
+ }, /* @__PURE__ */ React40.createElement("div", {
3880
+ role: "none",
3881
+ className: "flex gap-4 items-center justify-center mlb-4"
3882
+ }, /* @__PURE__ */ React40.createElement(Planet, {
3883
+ size: 32,
3884
+ weight: "light"
3885
+ }), /* @__PURE__ */ React40.createElement("div", {
3886
+ className: "block text-start font-light text-xl"
3887
+ }, name ?? space.key.truncate())));
3888
+ };
3889
+ var SpacePanelImpl = (props) => {
3890
+ const { titleId, activeView, space, target, SpaceManager: SpaceManagerComponent = SpaceManager, InvitationManager: InvitationManagerComponent = InvitationManager, ...rest } = props;
3891
+ return /* @__PURE__ */ React40.createElement(React40.Fragment, null, /* @__PURE__ */ React40.createElement(SpacePanelHeading, {
3892
+ ...rest,
3893
+ titleId,
3894
+ space
3895
+ }), /* @__PURE__ */ React40.createElement(Viewport.Root, {
3896
+ activeView
3897
+ }, /* @__PURE__ */ React40.createElement(Viewport.Views, null, /* @__PURE__ */ React40.createElement(Viewport.View, {
3898
+ id: "space manager",
3899
+ classNames: stepStyles
3900
+ }, /* @__PURE__ */ React40.createElement(SpaceManagerComponent, {
3901
+ active: activeView === "space manager",
3902
+ space,
3903
+ target,
3904
+ ...rest
3905
+ })), /* @__PURE__ */ React40.createElement(Viewport.View, {
3906
+ id: "space invitation manager",
3907
+ classNames: stepStyles
3908
+ }, /* @__PURE__ */ React40.createElement(InvitationManagerComponent, {
3909
+ active: activeView === "space invitation manager",
3910
+ ...rest,
3911
+ invitationUrl: props.invitationUrl
3912
+ })))));
3913
+ };
3914
+ var SpacePanelWithInvitationImpl = ({ invitation, ...props }) => {
3915
+ const invitationStatus = useInvitationStatus3(invitation);
3916
+ return /* @__PURE__ */ React40.createElement(SpacePanelImpl, {
3917
+ ...props,
3918
+ ...invitationStatus,
3919
+ invitationUrl: props.createInvitationUrl(invitationStatus.invitationCode)
3920
+ });
3921
+ };
3922
+ var SpacePanel = ({ titleId: propsTitleId, createInvitationUrl = (code) => code, ...props }) => {
3923
+ const titleId = useId9("spacePanel__heading", propsTitleId);
3924
+ const [spaceState, spaceSend, spaceService] = useSpaceMachine({
3925
+ context: {
3926
+ space: props.space
3927
+ }
3928
+ });
3929
+ useEffect7(() => {
3930
+ const subscription = spaceService.subscribe((state) => {
3931
+ log13("[state]", state, {
3932
+ F: __dxlog_file13,
3933
+ L: 94,
3934
+ S: void 0,
3935
+ C: (f, a) => f(...a)
3936
+ });
3937
+ });
3938
+ return subscription.unsubscribe;
3939
+ }, [
3940
+ spaceService
3941
+ ]);
3942
+ const activeView = useMemo5(() => {
3943
+ switch (true) {
3944
+ case spaceState.matches("managingSpace"):
3945
+ return "space manager";
3946
+ case spaceState.matches("managingSpaceInvitation"):
3947
+ return "space invitation manager";
3948
+ default:
3949
+ return "never";
3950
+ }
3951
+ }, [
3952
+ spaceState
3953
+ ]);
3954
+ const implProps = {
3955
+ ...props,
3956
+ activeView,
3957
+ send: spaceSend,
3958
+ titleId,
3959
+ createInvitationUrl
3960
+ };
3961
+ return spaceState.context.invitation ? (
3962
+ // TODO(wittjosiah): Unused? Remove?
3963
+ /* @__PURE__ */ React40.createElement(SpacePanelWithInvitationImpl, {
3964
+ ...implProps,
3965
+ invitation: spaceState.context.invitation
3966
+ })
3967
+ ) : /* @__PURE__ */ React40.createElement(SpacePanelImpl, implProps);
3968
+ };
3969
+
3970
+ // packages/sdk/shell/src/panels/StatusPanel/StatusPanel.tsx
3971
+ import React41 from "react";
3972
+ import { Status, useTranslation as useTranslation25 } from "@dxos/react-ui";
3973
+ var StatusPanel = ({ titleId }) => {
3974
+ const { t } = useTranslation25("os");
3975
+ return /* @__PURE__ */ React41.createElement("div", {
3976
+ className: "grid place-items-center p-2 gap-2"
3977
+ }, /* @__PURE__ */ React41.createElement("p", {
3978
+ id: titleId,
3979
+ className: "font-medium text-center"
3980
+ }, t("resetting message")), /* @__PURE__ */ React41.createElement(Status, {
3981
+ indeterminate: true
3982
+ }, t("resetting message")));
3983
+ };
3984
+
3985
+ // packages/sdk/shell/src/composites/IdentityDialog/IdentityDialog.tsx
3986
+ var IdentityDialog = (props) => {
3987
+ const titleId = useId10("identityDialog__title", props.title);
3988
+ return /* @__PURE__ */ React42.createElement(Dialog.Root, {
3989
+ defaultOpen: true,
3990
+ onOpenChange: (open) => open || props.onDone?.()
3991
+ }, /* @__PURE__ */ React42.createElement(Dialog.Portal, null, /* @__PURE__ */ React42.createElement(Dialog.Overlay, null, /* @__PURE__ */ React42.createElement(Dialog.Content, {
3992
+ "aria-labelledby": titleId,
3993
+ onOpenAutoFocus: (e) => e.preventDefault()
3994
+ }, /* @__PURE__ */ React42.createElement(ClipboardProvider, null, /* @__PURE__ */ React42.createElement(IdentityPanel, {
3995
+ ...props,
3996
+ titleId,
3997
+ doneActionParent: /* @__PURE__ */ React42.createElement(Dialog.Close, {
3998
+ asChild: true
3999
+ })
4000
+ }))))));
4001
+ };
4002
+
4003
+ // packages/sdk/shell/src/composites/JoinDialog/JoinDialog.tsx
4004
+ import React43 from "react";
4005
+ import { AlertDialog, useId as useId11, useVisualViewport } from "@dxos/react-ui";
4006
+ var JoinDialog = (joinPanelProps) => {
4007
+ const titleId = useId11("joinDialog__title");
4008
+ const { height } = useVisualViewport();
4009
+ return /* @__PURE__ */ React43.createElement(AlertDialog.Root, {
4010
+ defaultOpen: true,
4011
+ onOpenChange: (open) => open || (joinPanelProps.onExit ? joinPanelProps.onExit() : joinPanelProps.onDone?.(null))
4012
+ }, /* @__PURE__ */ React43.createElement(AlertDialog.Portal, null, /* @__PURE__ */ React43.createElement(AlertDialog.Overlay, {
4013
+ classNames: "backdrop-blur",
4014
+ ...height && {
4015
+ style: {
4016
+ blockSize: `${height}px`
4017
+ }
4018
+ }
4019
+ }, /* @__PURE__ */ React43.createElement(AlertDialog.Content, {
4020
+ "aria-labelledby": titleId
4021
+ }, /* @__PURE__ */ React43.createElement(JoinPanel, {
4022
+ ...joinPanelProps,
4023
+ titleId,
4024
+ exitActionParent: /* @__PURE__ */ React43.createElement(AlertDialog.Cancel, {
4025
+ asChild: true
4026
+ }),
4027
+ doneActionParent: /* @__PURE__ */ React43.createElement(AlertDialog.Action, {
4028
+ asChild: true
4029
+ })
4030
+ })))));
4031
+ };
4032
+
4033
+ // packages/sdk/shell/src/composites/Shell/run-shell.tsx
4034
+ import React47, { StrictMode } from "react";
4035
+ import { createRoot } from "react-dom/client";
4036
+ import { DEFAULT_CLIENT_CHANNEL, DEFAULT_SHELL_CHANNEL } from "@dxos/client-protocol";
4037
+ import { AgentHostingProvider, ClientProvider, ClientServicesProxy, Config, ShellDisplay as ShellDisplay2 } from "@dxos/react-client";
4038
+ import { Button as Button11, Dialog as Dialog3, ThemeProvider, Tooltip as Tooltip8, useTranslation as useTranslation26 } from "@dxos/react-ui";
4039
+ import { defaultTx } from "@dxos/react-ui-theme";
4040
+ import { createIFramePort } from "@dxos/rpc-tunnel";
4041
+
4042
+ // packages/sdk/shell/src/composites/Shell/Shell.tsx
4043
+ import React46, { useEffect as useEffect8, useState as useState14 } from "react";
4044
+ import { log as log14 } from "@dxos/log";
4045
+ import { useClient as useClient9 } from "@dxos/react-client";
4046
+ import { ShellDisplay, ShellLayout } from "@dxos/react-client";
4047
+ import { useSpace as useSpace2 } from "@dxos/react-client/echo";
4048
+
4049
+ // packages/sdk/shell/src/composites/SpaceDialog/SpaceDialog.tsx
4050
+ import React44 from "react";
4051
+ import { Dialog as Dialog2, useId as useId12 } from "@dxos/react-ui";
4052
+ var SpaceDialog = (spacePanelProps) => {
4053
+ const titleId = useId12("spaceDialog__title");
4054
+ return /* @__PURE__ */ React44.createElement(Dialog2.Root, {
4055
+ defaultOpen: true,
4056
+ onOpenChange: (open) => open || spacePanelProps.onDone?.()
4057
+ }, /* @__PURE__ */ React44.createElement(Dialog2.Portal, null, /* @__PURE__ */ React44.createElement(Dialog2.Overlay, null, /* @__PURE__ */ React44.createElement(Dialog2.Content, {
4058
+ "aria-labelledby": titleId
4059
+ }, /* @__PURE__ */ React44.createElement(ClipboardProvider, null, /* @__PURE__ */ React44.createElement(SpacePanel, {
4060
+ ...spacePanelProps,
4061
+ titleId,
4062
+ doneActionParent: /* @__PURE__ */ React44.createElement(Dialog2.Close, {
4063
+ asChild: true
4064
+ })
4065
+ }))))));
4066
+ };
4067
+
4068
+ // packages/sdk/shell/src/composites/StatusDialog/StatusDialog.tsx
4069
+ import React45 from "react";
4070
+ import { AlertDialog as AlertDialog2, useId as useId13 } from "@dxos/react-ui";
4071
+ var StatusDialog = () => {
4072
+ const titleId = useId13("statusDialog__title");
4073
+ return /* @__PURE__ */ React45.createElement(AlertDialog2.Root, {
4074
+ open: true
4075
+ }, /* @__PURE__ */ React45.createElement(AlertDialog2.Portal, null, /* @__PURE__ */ React45.createElement(AlertDialog2.Overlay, null, /* @__PURE__ */ React45.createElement(AlertDialog2.Content, {
4076
+ "aria-labelledby": titleId
4077
+ }, /* @__PURE__ */ React45.createElement(StatusPanel, {
4078
+ titleId
4079
+ })))));
4080
+ };
4081
+
4082
+ // packages/sdk/shell/src/composites/Shell/Shell.tsx
4083
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/composites/Shell/Shell.tsx";
4084
+ var blurActiveElement = () => document.activeElement?.blur?.();
4085
+ var Shell = ({ runtime }) => {
4086
+ const [{ layout, invitationCode, spaceKey, spaceId, target }, setLayout] = useState14({
4087
+ layout: runtime.layout,
4088
+ invitationCode: runtime.invitationCode,
4089
+ spaceKey: runtime.spaceKey,
4090
+ spaceId: runtime.spaceId,
4091
+ target: runtime.target
4092
+ });
4093
+ const [{ invitationUrl, deviceInvitationParam, spaceInvitationParam }, setInvitationUrl] = useState14({
4094
+ invitationUrl: runtime.invitationUrl,
4095
+ deviceInvitationParam: runtime.deviceInvitationParam,
4096
+ spaceInvitationParam: runtime.spaceInvitationParam
4097
+ });
4098
+ const client = useClient9();
4099
+ const space = useSpace2(spaceId ?? spaceKey);
4100
+ const createDeviceInvitationUrl = (invitationCode2) => {
4101
+ const baseUrl = new URL(invitationUrl);
4102
+ baseUrl.searchParams.set(deviceInvitationParam, invitationCode2);
4103
+ return baseUrl.toString();
4104
+ };
4105
+ const createSpaceInvitationUrl = (invitationCode2) => {
4106
+ const baseUrl = new URL(invitationUrl);
4107
+ baseUrl.searchParams.set(spaceInvitationParam, invitationCode2);
4108
+ return baseUrl.toString();
4109
+ };
4110
+ useEffect8(() => {
4111
+ const unsubscribeLayout = runtime.layoutUpdate.on((request) => setLayout(request));
4112
+ const unsubscribeInvitationUrl = runtime.invitationUrlUpdate.on((request) => setInvitationUrl(request));
4113
+ return () => {
4114
+ unsubscribeLayout();
4115
+ unsubscribeInvitationUrl();
4116
+ };
4117
+ }, [
4118
+ runtime
4119
+ ]);
4120
+ useEffect8(() => {
4121
+ if (layout === ShellLayout.SPACE && !space) {
4122
+ log14.warn("No space found for shell space invitations.", void 0, {
4123
+ F: __dxlog_file14,
4124
+ L: 67,
4125
+ S: void 0,
4126
+ C: (f, a) => f(...a)
4127
+ });
4128
+ const timeout = setTimeout(async () => {
4129
+ await runtime.setAppContext({
4130
+ display: ShellDisplay.NONE
4131
+ });
4132
+ runtime.setLayout({
4133
+ layout: ShellLayout.DEFAULT
4134
+ });
4135
+ });
4136
+ return () => clearTimeout(timeout);
4137
+ }
4138
+ }, [
4139
+ runtime,
4140
+ layout,
4141
+ space
4142
+ ]);
4143
+ switch (layout) {
4144
+ case ShellLayout.STATUS:
4145
+ return /* @__PURE__ */ React46.createElement(StatusDialog, null);
4146
+ case ShellLayout.INITIALIZE_IDENTITY:
4147
+ case ShellLayout.INITIALIZE_IDENTITY_FROM_INVITATION:
4148
+ case ShellLayout.INITIALIZE_IDENTITY_FROM_RECOVERY:
4149
+ return /* @__PURE__ */ React46.createElement(JoinDialog, {
4150
+ mode: "halo-only",
4151
+ initialDisposition: layout === ShellLayout.INITIALIZE_IDENTITY_FROM_RECOVERY ? "recover-identity" : layout === ShellLayout.INITIALIZE_IDENTITY_FROM_INVITATION ? "accept-halo-invitation" : "default",
4152
+ initialInvitationCode: invitationCode,
4153
+ onCancelResetStorage: () => runtime.setLayout({
4154
+ layout: ShellLayout.IDENTITY
4155
+ }),
4156
+ onDone: async () => {
4157
+ blurActiveElement();
4158
+ await runtime.setAppContext({
4159
+ display: ShellDisplay.NONE
4160
+ });
4161
+ runtime.setLayout({
4162
+ layout: ShellLayout.DEFAULT
4163
+ });
4164
+ }
4165
+ });
4166
+ case ShellLayout.IDENTITY:
4167
+ case ShellLayout.SHARE_IDENTITY:
4168
+ case ShellLayout.EDIT_PROFILE:
4169
+ return /* @__PURE__ */ React46.createElement(IdentityDialog, {
4170
+ createInvitationUrl: createDeviceInvitationUrl,
4171
+ onResetStorage: async () => {
4172
+ runtime.setLayout({
4173
+ layout: ShellLayout.STATUS
4174
+ });
4175
+ await client.reset();
4176
+ return runtime.setAppContext({
4177
+ display: ShellDisplay.NONE,
4178
+ reset: true
4179
+ });
4180
+ },
4181
+ onRecover: async () => {
4182
+ runtime.setLayout({
4183
+ layout: ShellLayout.STATUS
4184
+ });
4185
+ await client.reset();
4186
+ return runtime.setAppContext({
4187
+ display: ShellDisplay.NONE,
4188
+ reset: true,
4189
+ target: "recoverIdentity"
4190
+ });
4191
+ },
4192
+ onJoinNewIdentity: async () => {
4193
+ runtime.setLayout({
4194
+ layout: ShellLayout.STATUS
4195
+ });
4196
+ await client.reset();
4197
+ return runtime.setAppContext({
4198
+ display: ShellDisplay.NONE,
4199
+ reset: true,
4200
+ target: "deviceInvitation"
4201
+ });
4202
+ },
4203
+ onDone: async () => {
4204
+ blurActiveElement();
4205
+ await runtime.setAppContext({
4206
+ display: ShellDisplay.NONE
4207
+ });
4208
+ runtime.setLayout({
4209
+ layout: ShellLayout.DEFAULT
4210
+ });
4211
+ },
4212
+ initialDisposition: layout === ShellLayout.SHARE_IDENTITY ? "manage-device-invitation" : "default"
4213
+ });
4214
+ case ShellLayout.SPACE:
4215
+ return space ? /* @__PURE__ */ React46.createElement(SpaceDialog, {
4216
+ space,
4217
+ target,
4218
+ createInvitationUrl: createSpaceInvitationUrl,
4219
+ onDone: async () => {
4220
+ blurActiveElement();
4221
+ await runtime.setAppContext({
4222
+ display: ShellDisplay.NONE
4223
+ });
4224
+ runtime.setLayout({
4225
+ layout: ShellLayout.DEFAULT
4226
+ });
4227
+ }
4228
+ }) : null;
4229
+ case ShellLayout.JOIN_SPACE:
4230
+ return /* @__PURE__ */ React46.createElement(JoinDialog, {
4231
+ initialInvitationCode: invitationCode,
4232
+ onDone: async (result) => {
4233
+ blurActiveElement();
4234
+ const target2 = result?.target ?? void 0;
4235
+ await runtime.setAppContext({
4236
+ display: ShellDisplay.NONE,
4237
+ spaceKey: result?.spaceKey ?? void 0,
4238
+ target: target2
4239
+ });
4240
+ runtime.setLayout({
4241
+ layout: ShellLayout.DEFAULT
4242
+ });
4243
+ },
4244
+ onExit: async () => {
4245
+ blurActiveElement();
4246
+ await runtime.setAppContext({
4247
+ display: ShellDisplay.NONE
4248
+ });
4249
+ runtime.setLayout({
4250
+ layout: ShellLayout.DEFAULT
4251
+ });
4252
+ }
4253
+ });
4254
+ default:
4255
+ return null;
4256
+ }
4257
+ };
4258
+
4259
+ // packages/sdk/shell/src/composites/Shell/shell-runtime.ts
4260
+ import { Event } from "@dxos/async";
4261
+ import { appServiceBundle, shellServiceBundle } from "@dxos/client-protocol";
4262
+ import { invariant as invariant4 } from "@dxos/invariant";
4263
+ import { ShellLayout as ShellLayout2 } from "@dxos/protocols/proto/dxos/iframe";
4264
+ import { createProtoRpcPeer } from "@dxos/rpc";
4265
+ var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/sdk/shell/src/composites/Shell/shell-runtime.ts";
4266
+ var ShellRuntimeImpl = class {
4267
+ constructor(_port) {
4268
+ this._port = _port;
4269
+ this.layoutUpdate = new Event();
4270
+ this.invitationUrlUpdate = new Event();
4271
+ this._layout = ShellLayout2.DEFAULT;
4272
+ this._invitationUrl = typeof window !== "undefined" ? window.location.origin : void 0;
4273
+ this._deviceInvitationParam = "deviceInvitationCode";
4274
+ this._spaceInvitationParam = "spaceInvitationCode";
4275
+ }
4276
+ get layout() {
4277
+ return this._layout;
4278
+ }
4279
+ get invitationCode() {
4280
+ return this._invitationCode;
4281
+ }
4282
+ /** @deprecated Use spaceId. */
4283
+ get spaceKey() {
4284
+ return this._spaceKey;
4285
+ }
4286
+ get spaceId() {
4287
+ return this._spaceId;
4288
+ }
4289
+ get invitationUrl() {
4290
+ return this._invitationUrl;
4291
+ }
4292
+ get deviceInvitationParam() {
4293
+ return this._deviceInvitationParam;
4294
+ }
4295
+ get spaceInvitationParam() {
4296
+ return this._spaceInvitationParam;
4297
+ }
4298
+ setLayout({ layout, invitationCode, spaceKey, spaceId }) {
4299
+ this._layout = layout;
4300
+ this._invitationCode = invitationCode;
4301
+ this._spaceKey = spaceKey;
4302
+ this._spaceId = spaceId;
4303
+ this.layoutUpdate.emit({
4304
+ layout,
4305
+ invitationCode,
4306
+ spaceKey,
4307
+ spaceId
4308
+ });
4309
+ }
4310
+ setInvitationUrl({ invitationUrl, deviceInvitationParam, spaceInvitationParam }) {
4311
+ this._invitationUrl = invitationUrl;
4312
+ this._deviceInvitationParam = deviceInvitationParam;
4313
+ this._spaceInvitationParam = spaceInvitationParam;
4314
+ this.invitationUrlUpdate.emit({
4315
+ invitationUrl,
4316
+ deviceInvitationParam,
4317
+ spaceInvitationParam
4318
+ });
4319
+ }
4320
+ async setAppContext(context) {
4321
+ invariant4(this._appRpc, "runtime not open", {
4322
+ F: __dxlog_file15,
4323
+ L: 83,
4324
+ S: this,
4325
+ A: [
4326
+ "this._appRpc",
4327
+ "'runtime not open'"
4328
+ ]
4329
+ });
4330
+ await this._appRpc.rpc.AppService.setContext(context);
4331
+ }
4332
+ async open() {
4333
+ this._appRpc = createProtoRpcPeer({
4334
+ requested: appServiceBundle,
4335
+ exposed: shellServiceBundle,
4336
+ handlers: {
4337
+ ShellService: {
4338
+ setLayout: async (request) => {
4339
+ this._layout = request.layout;
4340
+ this._invitationCode = request.invitationCode;
4341
+ this._spaceKey = request.spaceKey;
4342
+ this._spaceId = request.spaceId;
4343
+ this.layoutUpdate.emit(request);
4344
+ },
4345
+ setInvitationUrl: async (request) => {
4346
+ this._invitationUrl = request.invitationUrl;
4347
+ this._deviceInvitationParam = request.deviceInvitationParam;
4348
+ this._spaceInvitationParam = request.spaceInvitationParam;
4349
+ this.invitationUrlUpdate.emit(request);
4350
+ }
4351
+ }
4352
+ },
4353
+ port: this._port
4354
+ });
4355
+ await this._appRpc.open();
4356
+ }
4357
+ async close() {
4358
+ await this._appRpc?.close();
4359
+ this._appRpc = void 0;
4360
+ }
4361
+ };
4362
+
4363
+ // packages/sdk/shell/src/translations/locales/en-US.ts
4364
+ var en_US_exports = {};
4365
+ __export(en_US_exports, {
4366
+ os: () => os
4367
+ });
4368
+ var os = {
4369
+ "manage profile label": "Manage identity",
4370
+ "identity offline description": "Offline",
4371
+ "sidebar label": "DXOS sidebar",
4372
+ "copy invitation code label": "Copy URL",
4373
+ "copy success label": "Copied",
4374
+ "copy self public key label": "Copy public key",
4375
+ "open share panel label": "View QR code",
4376
+ "joining space heading": "Joining space",
4377
+ "join space heading": "Enter space invitation",
4378
+ "halo heading": "Initialize device identity",
4379
+ "exit label": "Exit",
4380
+ "identity selector title": "Join as",
4381
+ "identity radio group title": "Select an identity you\u2019ve associated with this device already",
4382
+ "add identity label": "Add",
4383
+ "continue label": "Continue",
4384
+ "back label": "Back",
4385
+ "next label": "Next",
4386
+ "close label": "Close",
4387
+ "auth choices label": "Choose an identity",
4388
+ "create identity label": "Create an identity",
4389
+ "create identity description": "Create a new identity.",
4390
+ "recover identity label": "Use a seed phrase",
4391
+ "recover identity description": "Enter your seed phrase to log in manually.",
4392
+ "join identity label": "Use another device",
4393
+ "join identity description": "Add this device to an identity you\u2019re already logged into on another device.",
4394
+ "deselect identity label": "Back to identities",
4395
+ "addition method chooser title": "An identity is required to continue",
4396
+ "new identity input label": "Set display name",
4397
+ "recover identity input label": "Type or paste your seed phrase",
4398
+ "failed to create identity message": "Failed to create an identity.",
4399
+ "failed to recover identity message": "Failed to recover an identity from the provided seed phrase.",
4400
+ "failed to authenticate message": "Incorrect code.",
4401
+ "identity added label": "Created identity & added to this device:",
4402
+ "join space as identity heading": "<part>as </part><part><icon/><label>{{labelValue}}</label></part>",
4403
+ "error status label": "Failed",
4404
+ "timeout status label": "Timed out",
4405
+ "cancelled status label": "Cancelled",
4406
+ "init status label": "Ready to connect",
4407
+ "connecting status label": "Connecting\u2026",
4408
+ "connected status label": "Enter authentication code",
4409
+ "authenticating status label": "Enter authentication code",
4410
+ "success status label": "Success",
4411
+ "cancel label": "Cancel",
4412
+ "done label": "Done",
4413
+ "reset label": "Start over",
4414
+ "auth code input label": "Enter the verification code",
4415
+ "invitation input label": "Paste an invitation code or URL",
4416
+ "create device invitation label": "Add device",
4417
+ "create space invitation label": "Invite",
4418
+ "qr label": "Scan to accept invitation",
4419
+ "empty invitations message": "No pending invitations",
4420
+ "empty device list message": "No devices authenticated other than this device.",
4421
+ "show all spaces label": "All spaces",
4422
+ "all spaces label": "All spaces",
4423
+ "create space label": "Create a new space",
4424
+ "join space label": "Join a space",
4425
+ "show current space label": "Current space",
4426
+ "view space invitations label": "View space invitations",
4427
+ "toggle sidebar label": "Open/close sidebar",
4428
+ "open sidebar label": "Open sidebar",
4429
+ "close sidebar label": "Close sidebar",
4430
+ "welcome message": "Welcome",
4431
+ "selecting identity heading": "Selecting identity",
4432
+ "devices heading": "Devices",
4433
+ "identity heading": "Profile settings",
4434
+ "choose add device label": "Add device",
4435
+ "choose devices label": "Manage devices",
4436
+ "choose profile label": "Edit profile",
4437
+ "choose sign out label": "Reset storage",
4438
+ "choose join new identity label": "Join existing device",
4439
+ "empty space members message": "There is nobody in this space yet.",
4440
+ "back to all invitations label": "Back to all invitations",
4441
+ "invitation heading": "Invitation",
4442
+ "invitation list heading": "Invitations",
4443
+ "device invitation list heading": "Pending device invitations",
4444
+ "space invitation list heading": "Pending invitations",
4445
+ "device list heading": "Devices",
4446
+ "space member list heading": "Members",
4447
+ "space panel heading": "Space membership",
4448
+ "auth code message": "Enter the following auth code on the joining device.",
4449
+ "auth other device emoji message": "Be sure the other device shows the following symbol:",
4450
+ "display name input label": "Display name",
4451
+ "display name input placeholder": "Enter a display name",
4452
+ "invite one label": "Single-use invitation",
4453
+ "invite one description": "Only one user may join.",
4454
+ "invite one qr label": "Single-use invitation",
4455
+ "invite one list item label": "Active single-use invitation",
4456
+ "invite many label": "Multi-use invitation",
4457
+ "invite many description": "Anyone with the link can join.",
4458
+ "invite many qr label": "Multi-use invitation",
4459
+ "invite many list item label": "Active multi-use invitation",
4460
+ "invite options label": "Change the active invite option.",
4461
+ "reset in progress label": "Resetting...",
4462
+ "confirm label": "Confirm",
4463
+ "reset storage input label": "WARNING: This will delete all data on this device. Type {{confirmationValue}} to continue.",
4464
+ "join new identity input label": "WARNING: To join an existing device all data on this device will be deleted. Type {{confirmationValue}} to continue.",
4465
+ "failed to reset identity message": "Failed to reset identity.",
4466
+ "confirmation value": "RESET",
4467
+ "confirmation placeholder": "Enter {{confirmationValue}} to continue.",
4468
+ "sign out chooser title": "Danger zone",
4469
+ "sign out chooser message": "Proceeding with the action below will erase all data on this device.",
4470
+ "join new identity label": "Join a new identity",
4471
+ "reset device label": "Reset storage",
4472
+ "reset device confirm message": "Are you sure you want to reset your device? All data will be lost.",
4473
+ "emoji and color label": "Emoji and color",
4474
+ "clear label": "Use default",
4475
+ "select emoji label": "Avatar emoji",
4476
+ "select hue label": "Avatar background color",
4477
+ "resetting message": "One moment while the device is reset\u2026",
4478
+ "red label": "Red",
4479
+ "orange label": "Orange",
4480
+ "amber label": "Amber",
4481
+ "yellow label": "Yellow",
4482
+ "lime label": "Lime",
4483
+ "green label": "Green",
4484
+ "emerald label": "Emerald",
4485
+ "teal label": "Teal",
4486
+ "cyan label": "Cyan",
4487
+ "sky label": "Sky",
4488
+ "blue label": "Blue",
4489
+ "indigo label": "Indigo",
4490
+ "violet label": "Violet",
4491
+ "purple label": "Purple",
4492
+ "fuchsia label": "Fuchsia",
4493
+ "pink label": "Pink",
4494
+ "rose label": "Rose",
4495
+ "current device tag label": "This device",
4496
+ "device name placeholder": "{{platform}} on {{os}}",
4497
+ "more options label": "More options",
4498
+ "disconnect label": "Go offline.",
4499
+ "connect label": "Go online.",
4500
+ "edit device label": "Edit device name & avatar",
4501
+ "agent heading": "Agent",
4502
+ "create agent clickwrap": 'By creating an agent, you agree to our <tosLink href="https://dxos.org/tos">Terms of\xA0Service</tosLink>.',
4503
+ "create agent description": "Creating an agent helps keep your devices and peers in-sync. An agent is like any other device, but since it\u2019s always online your changes will sync even when all your physical devices are\xA0asleep.",
4504
+ "create agent label": "Start an agent",
4505
+ "destroy agent label": "Shut down agent",
4506
+ "agent device label": "Your agent",
4507
+ "shell fallback title": "Shell failed to connect",
4508
+ "creating agent label": "Starting agent\u2026",
4509
+ "destroying agent label": "Stopping agent\u2026",
4510
+ "getting agent label": "Checking agent status\u2026",
4511
+ "agent requested label": "Agent requested",
4512
+ "agent requested description": "The agent service is creating an agent for you. Once the agent connects, it will display in the devices list.",
4513
+ "authenticating label": "Authenticating\u2026",
4514
+ "create recovery code label": "Create recovery code",
4515
+ "choose recover identity label": "Recover identity"
4516
+ };
4517
+
4518
+ // packages/sdk/shell/src/translations/index.ts
4519
+ var osTranslations = {
4520
+ "en-US": en_US_exports
4521
+ };
4522
+
4523
+ // packages/sdk/shell/src/composites/Shell/run-shell.tsx
4524
+ var runShell = async (config = new Config()) => {
4525
+ const runtime = new ShellRuntimeImpl(createIFramePort({
4526
+ channel: DEFAULT_SHELL_CHANNEL
4527
+ }));
4528
+ await runtime.open();
4529
+ try {
4530
+ const services = new ClientServicesProxy(createIFramePort({
4531
+ channel: DEFAULT_CLIENT_CHANNEL
4532
+ }));
4533
+ createRoot(document.getElementById("root")).render(/* @__PURE__ */ React47.createElement(StrictMode, null, /* @__PURE__ */ React47.createElement(ThemeProvider, {
4534
+ tx: defaultTx,
4535
+ resourceExtensions: [
4536
+ osTranslations
4537
+ ]
4538
+ }, /* @__PURE__ */ React47.createElement(ClientProvider, {
4539
+ config,
4540
+ services,
4541
+ noBanner: true
4542
+ }, /* @__PURE__ */ React47.createElement(ClipboardProvider, null, /* @__PURE__ */ React47.createElement(Tooltip8.Provider, null, /* @__PURE__ */ React47.createElement(AgentHostingProvider, null, /* @__PURE__ */ React47.createElement(Shell, {
4543
+ runtime
4544
+ }))))))));
4545
+ } catch {
4546
+ createRoot(document.getElementById("root")).render(/* @__PURE__ */ React47.createElement(StrictMode, null, /* @__PURE__ */ React47.createElement(ThemeProvider, {
4547
+ tx: defaultTx,
4548
+ resourceExtensions: [
4549
+ osTranslations
4550
+ ]
4551
+ }, /* @__PURE__ */ React47.createElement(Fallback, {
4552
+ onClose: () => runtime.setAppContext({
4553
+ display: ShellDisplay2.NONE
4554
+ })
4555
+ }))));
4556
+ }
4557
+ };
4558
+ var Fallback = ({ onClose }) => {
4559
+ const { t } = useTranslation26("os");
4560
+ return /* @__PURE__ */ React47.createElement(Dialog3.Root, {
4561
+ modal: true,
4562
+ open: true,
4563
+ onOpenChange: () => onClose?.()
4564
+ }, /* @__PURE__ */ React47.createElement(Dialog3.Overlay, null, /* @__PURE__ */ React47.createElement(Dialog3.Content, null, /* @__PURE__ */ React47.createElement(Dialog3.Title, null, t("shell fallback title")), /* @__PURE__ */ React47.createElement(Dialog3.Close, {
4565
+ asChild: true,
4566
+ onClick: () => onClose?.()
4567
+ }, /* @__PURE__ */ React47.createElement(Button11, {
4568
+ variant: "primary"
4569
+ }, t("close label"))))));
4570
+ };
4571
+
4572
+ // packages/sdk/shell/src/composites/Shell/ShellContext.tsx
4573
+ import React48, { createContext as createContext2, useCallback as useCallback8, useContext as useContext2, useEffect as useEffect9, useMemo as useMemo6, useState as useState15 } from "react";
4574
+ import { useClient as useClient10, ShellDisplay as ShellDisplay3, ShellLayout as ShellLayout4 } from "@dxos/react-client";
4575
+ import { useIdentity as useIdentity3 } from "@dxos/react-client/halo";
4576
+ import { mx as mx25 } from "@dxos/react-ui-theme";
4577
+
4578
+ // packages/sdk/shell/src/composites/Shell/memory-shell-runtime.ts
4579
+ import { Event as Event2 } from "@dxos/async";
4580
+ import { ShellLayout as ShellLayout3 } from "@dxos/react-client";
4581
+ var MemoryShellRuntime = class {
4582
+ constructor({ layout, invitationCode, spaceKey, invitationUrl, deviceInvitationParam, spaceInvitationParam } = {}) {
4583
+ this.layoutUpdate = new Event2();
4584
+ this.invitationUrlUpdate = new Event2();
4585
+ this.contextUpdate = new Event2();
4586
+ this._layout = layout ?? ShellLayout3.DEFAULT;
4587
+ this._invitationCode = invitationCode;
4588
+ this._spaceKey = spaceKey;
4589
+ this._invitationUrl = invitationUrl ?? window.location.origin;
4590
+ this._deviceInvitationParam = deviceInvitationParam ?? "deviceInvitationCode";
4591
+ this._spaceInvitationParam = spaceInvitationParam ?? "spaceInvitationCode";
4592
+ }
4593
+ get layout() {
4594
+ return this._layout;
4595
+ }
4596
+ get invitationCode() {
4597
+ return this._invitationCode;
4598
+ }
4599
+ get spaceKey() {
4600
+ return this._spaceKey;
4601
+ }
4602
+ get invitationUrl() {
4603
+ return this._invitationUrl;
4604
+ }
4605
+ get deviceInvitationParam() {
4606
+ return this._deviceInvitationParam;
4607
+ }
4608
+ get spaceInvitationParam() {
4609
+ return this._spaceInvitationParam;
4610
+ }
4611
+ setLayout(request) {
4612
+ this._layout = request.layout;
4613
+ this._invitationCode = request.invitationCode;
4614
+ this._spaceKey = request.spaceKey;
4615
+ this.layoutUpdate.emit(request);
4616
+ }
4617
+ setInvitationUrl(request) {
4618
+ this._invitationUrl = request.invitationUrl;
4619
+ this._deviceInvitationParam = request.deviceInvitationParam;
4620
+ this._spaceInvitationParam = request.spaceInvitationParam;
4621
+ this.invitationUrlUpdate.emit(request);
4622
+ }
4623
+ async setAppContext(context) {
4624
+ this.contextUpdate.emit(context);
4625
+ }
4626
+ };
4627
+
4628
+ // packages/sdk/shell/src/composites/Shell/ShellContext.tsx
4629
+ var ShellContext = /* @__PURE__ */ createContext2({});
4630
+ var useShell = () => {
4631
+ const client = useClient10();
4632
+ const { runtime, setDisplay } = useContext2(ShellContext);
4633
+ const setLayout = async (layout, options) => {
4634
+ if (runtime) {
4635
+ if (layout === ShellLayout4.DEFAULT) {
4636
+ setDisplay?.(ShellDisplay3.NONE);
4637
+ } else {
4638
+ setDisplay?.(ShellDisplay3.FULLSCREEN);
4639
+ }
4640
+ runtime.setLayout({
4641
+ layout,
4642
+ ...options
4643
+ });
4644
+ }
4645
+ await client.shell.open(layout, options);
4646
+ };
4647
+ return {
4648
+ setLayout
4649
+ };
4650
+ };
4651
+ var ShellProvider = ({ space, deviceInvitationCode, spaceInvitationCode, onJoinedSpace, children }) => {
4652
+ const client = useClient10();
4653
+ const identity = useIdentity3();
4654
+ const [display, setDisplay] = useState15(!identity || spaceInvitationCode || deviceInvitationCode ? ShellDisplay3.FULLSCREEN : ShellDisplay3.NONE);
4655
+ const shellRuntime = useMemo6(() => {
4656
+ if (client.config.get("runtime.app.env.DX_VAULT") !== "false") {
4657
+ return;
4658
+ }
4659
+ if (spaceInvitationCode) {
4660
+ return new MemoryShellRuntime({
4661
+ layout: ShellLayout4.JOIN_SPACE,
4662
+ invitationCode: spaceInvitationCode
4663
+ });
4664
+ }
4665
+ if (deviceInvitationCode) {
4666
+ return new MemoryShellRuntime({
4667
+ layout: ShellLayout4.INITIALIZE_IDENTITY,
4668
+ invitationCode: deviceInvitationCode
4669
+ });
4670
+ }
4671
+ return new MemoryShellRuntime({
4672
+ layout: identity ? ShellLayout4.DEFAULT : ShellLayout4.INITIALIZE_IDENTITY
4673
+ });
4674
+ }, [
4675
+ client,
4676
+ identity,
4677
+ spaceInvitationCode,
4678
+ deviceInvitationCode
4679
+ ]);
4680
+ const handleKeyDown = useCallback8((event) => {
4681
+ if (!space || !shellRuntime) {
4682
+ return;
4683
+ }
4684
+ const modifier = event.ctrlKey || event.metaKey;
4685
+ if (event.key === ">" && event.shiftKey && modifier) {
4686
+ shellRuntime.setLayout({
4687
+ layout: ShellLayout4.SHARE_IDENTITY
4688
+ });
4689
+ setDisplay(ShellDisplay3.FULLSCREEN);
4690
+ } else if (event.key === "." && modifier) {
4691
+ shellRuntime.setLayout({
4692
+ layout: ShellLayout4.SPACE,
4693
+ spaceKey: space.key
4694
+ });
4695
+ setDisplay(ShellDisplay3.FULLSCREEN);
4696
+ }
4697
+ }, [
4698
+ space,
4699
+ shellRuntime
4700
+ ]);
4701
+ useEffect9(() => {
4702
+ if (!shellRuntime) {
4703
+ return;
4704
+ }
4705
+ window.addEventListener("keydown", handleKeyDown);
4706
+ return () => window.removeEventListener("keydown", handleKeyDown);
4707
+ }, [
4708
+ shellRuntime,
4709
+ handleKeyDown
4710
+ ]);
4711
+ useEffect9(() => {
4712
+ if (!shellRuntime) {
4713
+ return;
4714
+ }
4715
+ return shellRuntime.contextUpdate.on(({ display: display2, spaceKey }) => {
4716
+ if (display2) {
4717
+ setDisplay(display2);
4718
+ }
4719
+ onJoinedSpace?.(spaceKey);
4720
+ });
4721
+ }, [
4722
+ shellRuntime
4723
+ ]);
4724
+ return /* @__PURE__ */ React48.createElement(React48.Fragment, null, shellRuntime && /* @__PURE__ */ React48.createElement("div", {
4725
+ className: mx25(display === ShellDisplay3.NONE ? "hidden" : "")
4726
+ }, /* @__PURE__ */ React48.createElement(Shell, {
4727
+ runtime: shellRuntime
4728
+ })), /* @__PURE__ */ React48.createElement(ShellContext.Provider, {
4729
+ value: {
4730
+ runtime: shellRuntime,
4731
+ setDisplay
4732
+ }
4733
+ }, children));
4734
+ };
4735
+ export {
4736
+ Action,
4737
+ Actions,
4738
+ AgentConfig,
4739
+ AuthCode,
4740
+ BifurcatedAction,
4741
+ Centered,
4742
+ ClipboardContext,
4743
+ ClipboardProvider,
4744
+ CloseButton,
4745
+ CompoundButton,
4746
+ ConfirmReset,
4747
+ ConfirmResetImpl,
4748
+ CopyButton,
4749
+ CopyButtonIconOnly,
4750
+ DeviceList,
4751
+ Emoji,
4752
+ EmojiPickerBlock,
4753
+ EmojiPickerToolbarButton,
4754
+ Heading,
4755
+ HuePickerBlock,
4756
+ HuePickerToolbarButton,
4757
+ IdentityDialog,
4758
+ IdentityListItem,
4759
+ IdentityPanel,
4760
+ IdentityPanelImpl,
4761
+ Input,
4762
+ InvitationList,
4763
+ InvitationListContainer,
4764
+ InvitationListItem,
4765
+ InvitationListItemImpl,
4766
+ InvitationManager,
4767
+ JoinDialog,
4768
+ JoinPanel,
4769
+ JoinPanelImpl,
4770
+ Label,
4771
+ Shell,
4772
+ ShellContext,
4773
+ ShellProvider,
4774
+ SpaceDialog,
4775
+ SpaceListItem,
4776
+ SpaceMemberList,
4777
+ SpaceMemberListImpl,
4778
+ SpacePanel,
4779
+ SpacePanelImpl,
4780
+ StatusPanel,
4781
+ StepHeading,
4782
+ Viewport,
4783
+ activeBgColor,
4784
+ activeStrokeColor,
4785
+ activeTextColor,
4786
+ cancelledBgColor,
4787
+ cancelledStrokeColor,
4788
+ cancelledTextColor,
4789
+ createViewportScope,
4790
+ errorBgColor,
4791
+ errorStrokeColor,
4792
+ errorTextColor,
4793
+ inactiveBgColor,
4794
+ inactiveStrokeColor,
4795
+ inactiveTextColor,
4796
+ osTranslations,
4797
+ resolvedBgColor,
4798
+ resolvedStrokeColor,
4799
+ resolvedTextColor,
4800
+ runShell,
4801
+ stepStyles,
4802
+ successBgColor,
4803
+ successStrokeColor,
4804
+ successTextColor,
4805
+ useClipboardContext,
4806
+ useShell,
4807
+ useViewportContext
4808
+ };
4809
+ //# sourceMappingURL=react.mjs.map