@hadoku/task-ui-components 4.22.3 → 4.22.4

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.
@@ -1,8 +1,9 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useState, useRef, useCallback, useEffect } from "react";
2
+ import { useState, useRef, useId, useCallback, useEffect } from "react";
3
+ import { useSettingsTab } from "@wolffm/themes";
3
4
  import { SettingsIcon } from "./ThemeIcons.js";
4
5
  import { AvatarPicker } from "./AvatarPicker.js";
5
- import { prefetchSettings, setDisplayName, loadSignIn, switchKeyHref, loadSignInClient, passkeyHref, setContentLevel, passkeysSupported, deleteOwnAccount } from "../lib/settingsClient.js";
6
+ import { passkeysSupported, prefetchSettings, setDisplayName, loadSignIn, switchKeyHref, loadSignInClient, passkeyHref, setContentLevel, deleteOwnAccount } from "../lib/settingsClient.js";
6
7
  /* empty css */
7
8
  function untilAborted(signal) {
8
9
  return new Promise((_, reject) => {
@@ -26,7 +27,6 @@ const PASSKEY_DOT = {
26
27
  pointerEvents: "none"
27
28
  };
28
29
  const PASSKEY_PULSE_CLASS = "settings-popout__btn--pulse";
29
- const PASSKEY_PULSE_STYLE_ID = "settings-passkey-pulse";
30
30
  const PASSKEY_PULSE_CSS = `
31
31
  @keyframes settings-passkey-pulse {
32
32
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-primary) 70%, transparent); }
@@ -39,14 +39,67 @@ const PASSKEY_PULSE_CSS = `
39
39
  }
40
40
  }
41
41
  `;
42
- function usePasskeyPulseStyle(active) {
42
+ const TABS_CSS = `
43
+ .settings-popout__top {
44
+ position: sticky;
45
+ top: -16px;
46
+ z-index: 1;
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 10px;
50
+ margin: -16px -16px 0;
51
+ padding: 16px 16px 0;
52
+ background: var(--color-bg-card);
53
+ border-bottom: 1px solid var(--color-border);
54
+ }
55
+ .settings-popout__tabs {
56
+ display: flex;
57
+ gap: 2px;
58
+ margin: 0 -16px -1px;
59
+ padding: 0 16px;
60
+ overflow-x: auto;
61
+ scrollbar-width: thin;
62
+ }
63
+ .settings-popout__tab {
64
+ position: relative;
65
+ flex: none;
66
+ padding: 6px 10px;
67
+ border: 0;
68
+ border-bottom: 2px solid transparent;
69
+ background: transparent;
70
+ color: var(--color-text-secondary);
71
+ font: inherit;
72
+ font-size: 0.8rem;
73
+ font-weight: 600;
74
+ white-space: nowrap;
75
+ cursor: pointer;
76
+ }
77
+ .settings-popout__tab:hover {
78
+ color: var(--color-text);
79
+ }
80
+ .settings-popout__tab[aria-selected='true'] {
81
+ color: var(--color-text);
82
+ border-bottom-color: var(--color-primary);
83
+ }
84
+ .settings-popout__tab:focus-visible {
85
+ outline: 2px solid var(--color-primary);
86
+ outline-offset: -2px;
87
+ border-radius: 6px 6px 0 0;
88
+ }
89
+ .settings-popout__tabpanel {
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 14px;
93
+ }
94
+ `;
95
+ function useInjectedStyle(id, css, active) {
43
96
  useEffect(() => {
44
- if (!active || document.getElementById(PASSKEY_PULSE_STYLE_ID)) return;
97
+ if (!active || document.getElementById(id)) return;
45
98
  const el = document.createElement("style");
46
- el.id = PASSKEY_PULSE_STYLE_ID;
47
- el.textContent = PASSKEY_PULSE_CSS;
99
+ el.id = id;
100
+ el.textContent = css;
48
101
  document.head.appendChild(el);
49
- }, [active]);
102
+ }, [id, css, active]);
50
103
  }
51
104
  const TIER_LABEL = {
52
105
  admin: "Administrator",
@@ -60,10 +113,15 @@ function ConnectedSettings({
60
113
  userType: userTypeProp,
61
114
  name: nameProp,
62
115
  onNameChange,
63
- children
116
+ children,
117
+ appTabs
64
118
  }) {
65
119
  const [open, setOpen] = useState(false);
66
120
  const rootRef = useRef(null);
121
+ const tabIdBase = useId();
122
+ const tabRefs = useRef(/* @__PURE__ */ new Map());
123
+ const [savedTab, saveTab] = useSettingsTab();
124
+ const [pickedTab, setPickedTab] = useState(null);
67
125
  const [userType, setUserType] = useState(userTypeProp ?? "public");
68
126
  const [resolvedName, setResolvedName] = useState(nameProp ?? null);
69
127
  const [nameDraft, setNameDraft] = useState(resolvedName ?? "");
@@ -89,7 +147,42 @@ function ConnectedSettings({
89
147
  const [resolving, setResolving] = useState(true);
90
148
  const showContentPill = userType !== "public";
91
149
  const needsPasskey = !resolving && userType !== "public" && passkeyCount === 0 && !passkeySaved && passkeysSupported();
92
- usePasskeyPulseStyle(needsPasskey);
150
+ useInjectedStyle("settings-passkey-pulse", PASSKEY_PULSE_CSS, needsPasskey);
151
+ useInjectedStyle("settings-tabs", TABS_CSS, open);
152
+ const tabs = [
153
+ { id: "profile", label: "Profile" },
154
+ ...!resolving && showContentPill ? [{ id: "content", label: "Content" }] : [],
155
+ // The gear's dot asks for a passkey; this one says where Save passkey is.
156
+ { id: "account", label: "Account", dot: needsPasskey },
157
+ ...children ? [{ id: "app:", label: "App" }] : [],
158
+ ...(appTabs ?? []).map((t) => ({ id: `app:${t.id}`, label: t.label }))
159
+ ];
160
+ const tabIds = tabs.map((t) => t.id);
161
+ const defaultTab = tabIds.find((id) => id.startsWith("app:")) ?? "profile";
162
+ const activeTab = [pickedTab, savedTab].find((id) => id !== null && tabIds.includes(id)) ?? defaultTab;
163
+ const appTab = appTabs?.find((t) => `app:${t.id}` === activeTab);
164
+ const tabDomId = (id) => `${tabIdBase}tab-${id}`;
165
+ const panelDomId = `${tabIdBase}panel`;
166
+ const selectTab = useCallback(
167
+ (id) => {
168
+ setPickedTab(id);
169
+ saveTab(id);
170
+ },
171
+ [saveTab]
172
+ );
173
+ const onTabKey = (e) => {
174
+ const i = tabIds.indexOf(activeTab);
175
+ const n = tabIds.length;
176
+ const next = e.key === "ArrowRight" ? tabIds[(i + 1) % n] : e.key === "ArrowLeft" ? tabIds[(i - 1 + n) % n] : e.key === "Home" ? tabIds[0] : e.key === "End" ? tabIds[n - 1] : void 0;
177
+ if (next === void 0) return;
178
+ e.preventDefault();
179
+ selectTab(next);
180
+ tabRefs.current.get(next)?.focus();
181
+ };
182
+ useEffect(() => {
183
+ if (open)
184
+ tabRefs.current.get(activeTab)?.scrollIntoView({ block: "nearest", inline: "nearest" });
185
+ }, [open, activeTab]);
93
186
  useEffect(() => {
94
187
  if (userTypeProp !== void 0) setUserType(userTypeProp);
95
188
  }, [userTypeProp]);
@@ -262,295 +355,344 @@ function ConnectedSettings({
262
355
  ),
263
356
  open && /* @__PURE__ */ jsx("div", { className: "settings-popout__overlay", onClick: () => setOpen(false) }),
264
357
  open && /* @__PURE__ */ jsxs("div", { className: "settings-popout__panel", role: "dialog", "aria-label": "User settings", children: [
265
- /* @__PURE__ */ jsx("div", { className: "settings-popout__header", children: "Settings" }),
266
- /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
267
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Access tier" }),
268
- resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : /* @__PURE__ */ jsx("span", { className: `settings-popout__tier settings-popout__tier--${userType}`, children: TIER_LABEL[userType] })
269
- ] }),
270
- /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
271
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Display name" }),
272
- resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : editingName ? /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
273
- /* @__PURE__ */ jsx(
274
- "input",
275
- {
276
- className: "settings-popout__input",
277
- value: nameDraft,
278
- maxLength: 64,
279
- autoFocus: true,
280
- disabled: nameSaving,
281
- "aria-invalid": nameError !== null,
282
- "aria-describedby": nameError ? "settings-popout-name-error" : void 0,
283
- onChange: (e) => {
284
- setNameDraft(e.target.value);
285
- setNameError(null);
286
- },
287
- onKeyDown: (e) => {
288
- if (e.key === "Enter") {
289
- saveName().catch(reportErr);
290
- }
291
- if (e.key === "Escape") {
292
- e.stopPropagation();
293
- setEditingName(false);
294
- setNameError(null);
295
- setNameDraft(displayName ?? "");
296
- }
297
- }
298
- }
299
- ),
300
- /* @__PURE__ */ jsx(
301
- "button",
302
- {
303
- type: "button",
304
- className: "settings-popout__btn settings-popout__btn--primary",
305
- disabled: nameSaving,
306
- onClick: () => {
307
- saveName().catch(reportErr);
308
- },
309
- children: nameSaving ? "…" : "Save"
310
- }
311
- ),
312
- nameError && /* @__PURE__ */ jsx(
313
- "span",
314
- {
315
- id: "settings-popout-name-error",
316
- className: "settings-popout__error",
317
- role: "alert",
318
- children: nameError
319
- }
320
- )
321
- ] }) : /* @__PURE__ */ jsx(
322
- "button",
358
+ /* @__PURE__ */ jsxs("div", { className: "settings-popout__top", children: [
359
+ /* @__PURE__ */ jsx("div", { className: "settings-popout__header", children: "Settings" }),
360
+ /* @__PURE__ */ jsx(
361
+ "div",
323
362
  {
324
- type: "button",
325
- className: "settings-popout__value settings-popout__value--editable",
326
- "aria-label": "Edit display name",
327
- onClick: () => {
328
- setNameDraft(displayName ?? "");
329
- setEditingName(true);
330
- },
331
- children: displayName || "Unnamed"
332
- }
333
- )
334
- ] }),
335
- !resolving && userType !== "public" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
336
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
337
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Avatar" }),
338
- /* @__PURE__ */ jsx(
339
- "button",
340
- {
341
- type: "button",
342
- className: "settings-popout__btn",
343
- "aria-expanded": avatarOpen,
344
- onClick: () => setAvatarOpen((v) => !v),
345
- children: avatarOpen ? "Done" : "Change"
346
- }
347
- )
348
- ] }),
349
- avatarOpen && /* @__PURE__ */ jsx(AvatarPicker, {})
350
- ] }),
351
- !resolving && showContentPill && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
352
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Content visibility" }),
353
- content ? /* @__PURE__ */ jsxs(Fragment, { children: [
354
- /* @__PURE__ */ jsx(
355
- "div",
356
- {
357
- className: "settings-popout__pill",
358
- role: "radiogroup",
359
- "aria-label": "Content visibility level",
360
- children: Array.from({ length: content.maxLevel }, (_, i) => i + 1).map((lvl) => /* @__PURE__ */ jsx(
361
- "button",
362
- {
363
- type: "button",
364
- role: "radio",
365
- "aria-checked": lvl === content.level,
366
- "aria-label": `Level ${lvl}`,
367
- className: `settings-popout__seg${lvl <= content.level ? " settings-popout__seg--filled" : ""}`,
368
- disabled: levelSaving,
369
- onClick: () => {
370
- pickLevel(lvl).catch(reportErr);
371
- },
372
- children: lvl
363
+ className: "settings-popout__tabs",
364
+ role: "tablist",
365
+ "aria-label": "Settings sections",
366
+ onKeyDown: onTabKey,
367
+ children: tabs.map((t) => /* @__PURE__ */ jsxs(
368
+ "button",
369
+ {
370
+ ref: (el) => {
371
+ if (el) tabRefs.current.set(t.id, el);
372
+ else tabRefs.current.delete(t.id);
373
373
  },
374
- lvl
375
- ))
376
- }
377
- ),
378
- /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Higher levels reveal more mature content across apps. Level 1 is safe-for-everyone." })
379
- ] }) : (
380
- // Not "Loading…" — the fetch has already settled by the time
381
- // this section renders at all, so a null here means it failed.
382
- /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Currently unavailable." })
374
+ type: "button",
375
+ role: "tab",
376
+ id: tabDomId(t.id),
377
+ className: "settings-popout__tab",
378
+ "aria-selected": t.id === activeTab,
379
+ "aria-controls": panelDomId,
380
+ tabIndex: t.id === activeTab ? 0 : -1,
381
+ onClick: () => selectTab(t.id),
382
+ children: [
383
+ t.label,
384
+ t.dot && /* @__PURE__ */ jsx("span", { style: PASSKEY_DOT, "aria-hidden": "true" })
385
+ ]
386
+ },
387
+ t.id
388
+ ))
389
+ }
383
390
  )
384
391
  ] }),
385
- keyFormOpen ? /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children: /* @__PURE__ */ jsxs(
386
- "form",
392
+ /* @__PURE__ */ jsxs(
393
+ "div",
387
394
  {
388
- className: "settings-popout__key-form",
389
- onSubmit: (e) => {
390
- e.preventDefault();
391
- submitKey().catch(reportErr);
392
- },
395
+ className: "settings-popout__tabpanel",
396
+ role: "tabpanel",
397
+ id: panelDomId,
398
+ "aria-labelledby": tabDomId(activeTab),
393
399
  children: [
394
- /* @__PURE__ */ jsx(
395
- "input",
396
- {
397
- type: "text",
398
- name: "username",
399
- autoComplete: "username",
400
- value: "hadoku",
401
- readOnly: true,
402
- hidden: true
403
- }
404
- ),
405
- /* @__PURE__ */ jsx("label", { className: "settings-popout__label", htmlFor: "settings-popout-key", children: "Access key" }),
406
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
407
- /* @__PURE__ */ jsx(
408
- "input",
409
- {
410
- id: "settings-popout-key",
411
- className: "settings-popout__input",
412
- type: "password",
413
- name: "password",
414
- autoComplete: "current-password",
415
- autoCapitalize: "none",
416
- spellCheck: false,
417
- autoFocus: true,
418
- value: keyDraft,
419
- disabled: keyBusy,
420
- "aria-invalid": keyError !== null,
421
- "aria-describedby": keyError ? "settings-popout-key-error" : void 0,
422
- onChange: (e) => setKeyDraft(e.target.value)
423
- }
424
- ),
425
- /* @__PURE__ */ jsx(
426
- "button",
400
+ activeTab === "profile" && /* @__PURE__ */ jsxs(Fragment, { children: [
401
+ /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
402
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Access tier" }),
403
+ resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : /* @__PURE__ */ jsx("span", { className: `settings-popout__tier settings-popout__tier--${userType}`, children: TIER_LABEL[userType] })
404
+ ] }),
405
+ /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
406
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Display name" }),
407
+ resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : editingName ? /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
408
+ /* @__PURE__ */ jsx(
409
+ "input",
410
+ {
411
+ className: "settings-popout__input",
412
+ value: nameDraft,
413
+ maxLength: 64,
414
+ autoFocus: true,
415
+ disabled: nameSaving,
416
+ "aria-invalid": nameError !== null,
417
+ "aria-describedby": nameError ? "settings-popout-name-error" : void 0,
418
+ onChange: (e) => {
419
+ setNameDraft(e.target.value);
420
+ setNameError(null);
421
+ },
422
+ onKeyDown: (e) => {
423
+ if (e.key === "Enter") {
424
+ saveName().catch(reportErr);
425
+ }
426
+ if (e.key === "Escape") {
427
+ e.stopPropagation();
428
+ setEditingName(false);
429
+ setNameError(null);
430
+ setNameDraft(displayName ?? "");
431
+ }
432
+ }
433
+ }
434
+ ),
435
+ /* @__PURE__ */ jsx(
436
+ "button",
437
+ {
438
+ type: "button",
439
+ className: "settings-popout__btn settings-popout__btn--primary",
440
+ disabled: nameSaving,
441
+ onClick: () => {
442
+ saveName().catch(reportErr);
443
+ },
444
+ children: nameSaving ? "…" : "Save"
445
+ }
446
+ ),
447
+ nameError && /* @__PURE__ */ jsx(
448
+ "span",
449
+ {
450
+ id: "settings-popout-name-error",
451
+ className: "settings-popout__error",
452
+ role: "alert",
453
+ children: nameError
454
+ }
455
+ )
456
+ ] }) : /* @__PURE__ */ jsx(
457
+ "button",
458
+ {
459
+ type: "button",
460
+ className: "settings-popout__value settings-popout__value--editable",
461
+ "aria-label": "Edit display name",
462
+ onClick: () => {
463
+ setNameDraft(displayName ?? "");
464
+ setEditingName(true);
465
+ },
466
+ children: displayName || "Unnamed"
467
+ }
468
+ )
469
+ ] }),
470
+ !resolving && userType !== "public" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
471
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
472
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Avatar" }),
473
+ /* @__PURE__ */ jsx(
474
+ "button",
475
+ {
476
+ type: "button",
477
+ className: "settings-popout__btn",
478
+ "aria-expanded": avatarOpen,
479
+ onClick: () => setAvatarOpen((v) => !v),
480
+ children: avatarOpen ? "Done" : "Change"
481
+ }
482
+ )
483
+ ] }),
484
+ avatarOpen && /* @__PURE__ */ jsx(AvatarPicker, {})
485
+ ] })
486
+ ] }),
487
+ activeTab === "content" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
488
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Content visibility" }),
489
+ content ? /* @__PURE__ */ jsxs(Fragment, { children: [
490
+ /* @__PURE__ */ jsx(
491
+ "div",
492
+ {
493
+ className: "settings-popout__pill",
494
+ role: "radiogroup",
495
+ "aria-label": "Content visibility level",
496
+ children: Array.from({ length: content.maxLevel }, (_, i) => i + 1).map((lvl) => /* @__PURE__ */ jsx(
497
+ "button",
498
+ {
499
+ type: "button",
500
+ role: "radio",
501
+ "aria-checked": lvl === content.level,
502
+ "aria-label": `Level ${lvl}`,
503
+ className: `settings-popout__seg${lvl <= content.level ? " settings-popout__seg--filled" : ""}`,
504
+ disabled: levelSaving,
505
+ onClick: () => {
506
+ pickLevel(lvl).catch(reportErr);
507
+ },
508
+ children: lvl
509
+ },
510
+ lvl
511
+ ))
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Higher levels reveal more mature content across apps. Level 1 is safe-for-everyone." })
515
+ ] }) : (
516
+ // Not "Loading…" — the fetch has already settled by the time
517
+ // this section renders at all, so a null here means it failed.
518
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Currently unavailable." })
519
+ )
520
+ ] }),
521
+ activeTab === "account" && /* @__PURE__ */ jsxs(Fragment, { children: [
522
+ keyFormOpen ? /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children: /* @__PURE__ */ jsxs(
523
+ "form",
427
524
  {
428
- type: "submit",
429
- className: "settings-popout__btn settings-popout__btn--primary",
430
- disabled: keyBusy,
431
- children: keyBusy ? "Signing in…" : "Sign in"
525
+ className: "settings-popout__key-form",
526
+ onSubmit: (e) => {
527
+ e.preventDefault();
528
+ submitKey().catch(reportErr);
529
+ },
530
+ children: [
531
+ /* @__PURE__ */ jsx(
532
+ "input",
533
+ {
534
+ type: "text",
535
+ name: "username",
536
+ autoComplete: "username",
537
+ value: "hadoku",
538
+ readOnly: true,
539
+ hidden: true
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsx("label", { className: "settings-popout__label", htmlFor: "settings-popout-key", children: "Access key" }),
543
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
544
+ /* @__PURE__ */ jsx(
545
+ "input",
546
+ {
547
+ id: "settings-popout-key",
548
+ className: "settings-popout__input",
549
+ type: "password",
550
+ name: "password",
551
+ autoComplete: "current-password",
552
+ autoCapitalize: "none",
553
+ spellCheck: false,
554
+ autoFocus: true,
555
+ value: keyDraft,
556
+ disabled: keyBusy,
557
+ "aria-invalid": keyError !== null,
558
+ "aria-describedby": keyError ? "settings-popout-key-error" : void 0,
559
+ onChange: (e) => setKeyDraft(e.target.value)
560
+ }
561
+ ),
562
+ /* @__PURE__ */ jsx(
563
+ "button",
564
+ {
565
+ type: "submit",
566
+ className: "settings-popout__btn settings-popout__btn--primary",
567
+ disabled: keyBusy,
568
+ children: keyBusy ? "Signing in…" : "Sign in"
569
+ }
570
+ )
571
+ ] }),
572
+ passkeysSupported() && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
573
+ "button",
574
+ {
575
+ type: "button",
576
+ className: "settings-popout__btn",
577
+ disabled: keyBusy,
578
+ onClick: () => {
579
+ signInWithPasskey().catch(reportErr);
580
+ },
581
+ children: passkeyMode === "signin" ? "Waiting for passkey…" : "Use a passkey"
582
+ }
583
+ ) }),
584
+ passkeyMode === "signin" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
585
+ "button",
586
+ {
587
+ type: "button",
588
+ className: "settings-popout__btn",
589
+ onClick: () => cancelPasskey(),
590
+ children: "Cancel"
591
+ }
592
+ ) }),
593
+ keyError && /* @__PURE__ */ jsx(
594
+ "span",
595
+ {
596
+ id: "settings-popout-key-error",
597
+ className: "settings-popout__error",
598
+ role: "alert",
599
+ children: keyError
600
+ }
601
+ )
602
+ ]
432
603
  }
433
- )
604
+ ) }) : /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
605
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
606
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Access key" }),
607
+ !resolving && userType !== "public" && passkeysSupported() && /* @__PURE__ */ jsx(
608
+ "button",
609
+ {
610
+ type: "button",
611
+ className: `settings-popout__btn${needsPasskey ? ` settings-popout__btn--primary ${PASSKEY_PULSE_CLASS}` : ""}`,
612
+ disabled: passkeyMode !== null || passkeySaved,
613
+ onClick: () => {
614
+ savePasskey().catch(reportErr);
615
+ },
616
+ children: passkeySaved ? "Passkey saved" : passkeyMode === "save" ? "Saving…" : "Save passkey"
617
+ }
618
+ ),
619
+ /* @__PURE__ */ jsx(
620
+ "button",
621
+ {
622
+ type: "button",
623
+ className: "settings-popout__btn",
624
+ disabled: passkeyMode === "save",
625
+ onClick: openKeyForm,
626
+ children: "Switch key"
627
+ }
628
+ )
629
+ ] }),
630
+ passkeyMode === "save" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
631
+ "button",
632
+ {
633
+ type: "button",
634
+ className: "settings-popout__btn",
635
+ onClick: () => cancelPasskey(),
636
+ children: "Cancel"
637
+ }
638
+ ) }),
639
+ passkeyError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: passkeyError })
640
+ ] }),
641
+ !resolving && userType === "member" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
642
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
643
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Account" }),
644
+ deleteStage === "idle" ? /* @__PURE__ */ jsx(
645
+ "button",
646
+ {
647
+ type: "button",
648
+ className: "settings-popout__btn",
649
+ onClick: () => {
650
+ setDeleteError(null);
651
+ setDeleteStage("confirm");
652
+ },
653
+ children: "Delete account"
654
+ }
655
+ ) : /* @__PURE__ */ jsx(
656
+ "button",
657
+ {
658
+ type: "button",
659
+ className: "settings-popout__btn",
660
+ disabled: deleteStage === "busy",
661
+ onClick: () => setDeleteStage("idle"),
662
+ children: "Keep"
663
+ }
664
+ )
665
+ ] }),
666
+ deleteStage !== "idle" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
667
+ "button",
668
+ {
669
+ type: "button",
670
+ className: "settings-popout__btn settings-popout__btn--primary",
671
+ disabled: deleteStage === "busy",
672
+ onClick: () => {
673
+ setDeleteStage("busy");
674
+ deleteOwnAccount().then((deleted) => {
675
+ if (!deleted) {
676
+ setDeleteError("Account not deleted");
677
+ setDeleteStage("confirm");
678
+ return;
679
+ }
680
+ localStorage.removeItem("hadoku_session_id");
681
+ localStorage.removeItem("hadoku_user_type");
682
+ window.location.assign("/");
683
+ }).catch(reportErr);
684
+ },
685
+ children: deleteStage === "busy" ? "Deleting…" : "Delete for good"
686
+ }
687
+ ) }),
688
+ deleteError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: deleteError })
689
+ ] })
434
690
  ] }),
435
- passkeysSupported() && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
436
- "button",
437
- {
438
- type: "button",
439
- className: "settings-popout__btn",
440
- disabled: keyBusy,
441
- onClick: () => {
442
- signInWithPasskey().catch(reportErr);
443
- },
444
- children: passkeyMode === "signin" ? "Waiting for passkey…" : "Use a passkey"
445
- }
446
- ) }),
447
- passkeyMode === "signin" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
448
- "button",
449
- {
450
- type: "button",
451
- className: "settings-popout__btn",
452
- onClick: () => cancelPasskey(),
453
- children: "Cancel"
454
- }
455
- ) }),
456
- keyError && /* @__PURE__ */ jsx(
457
- "span",
458
- {
459
- id: "settings-popout-key-error",
460
- className: "settings-popout__error",
461
- role: "alert",
462
- children: keyError
463
- }
464
- )
691
+ activeTab === "app:" && /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children }),
692
+ appTab && /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children: appTab.render() })
465
693
  ]
466
694
  }
467
- ) }) : /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
468
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
469
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Access key" }),
470
- !resolving && userType !== "public" && passkeysSupported() && /* @__PURE__ */ jsx(
471
- "button",
472
- {
473
- type: "button",
474
- className: `settings-popout__btn${needsPasskey ? ` settings-popout__btn--primary ${PASSKEY_PULSE_CLASS}` : ""}`,
475
- disabled: passkeyMode !== null || passkeySaved,
476
- onClick: () => {
477
- savePasskey().catch(reportErr);
478
- },
479
- children: passkeySaved ? "Passkey saved" : passkeyMode === "save" ? "Saving…" : "Save passkey"
480
- }
481
- ),
482
- /* @__PURE__ */ jsx(
483
- "button",
484
- {
485
- type: "button",
486
- className: "settings-popout__btn",
487
- disabled: passkeyMode === "save",
488
- onClick: openKeyForm,
489
- children: "Switch key"
490
- }
491
- )
492
- ] }),
493
- passkeyMode === "save" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
494
- "button",
495
- {
496
- type: "button",
497
- className: "settings-popout__btn",
498
- onClick: () => cancelPasskey(),
499
- children: "Cancel"
500
- }
501
- ) }),
502
- passkeyError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: passkeyError })
503
- ] }),
504
- !resolving && userType === "member" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
505
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
506
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Account" }),
507
- deleteStage === "idle" ? /* @__PURE__ */ jsx(
508
- "button",
509
- {
510
- type: "button",
511
- className: "settings-popout__btn",
512
- onClick: () => {
513
- setDeleteError(null);
514
- setDeleteStage("confirm");
515
- },
516
- children: "Delete account"
517
- }
518
- ) : /* @__PURE__ */ jsx(
519
- "button",
520
- {
521
- type: "button",
522
- className: "settings-popout__btn",
523
- disabled: deleteStage === "busy",
524
- onClick: () => setDeleteStage("idle"),
525
- children: "Keep"
526
- }
527
- )
528
- ] }),
529
- deleteStage !== "idle" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
530
- "button",
531
- {
532
- type: "button",
533
- className: "settings-popout__btn settings-popout__btn--primary",
534
- disabled: deleteStage === "busy",
535
- onClick: () => {
536
- setDeleteStage("busy");
537
- deleteOwnAccount().then((deleted) => {
538
- if (!deleted) {
539
- setDeleteError("Account not deleted");
540
- setDeleteStage("confirm");
541
- return;
542
- }
543
- localStorage.removeItem("hadoku_session_id");
544
- localStorage.removeItem("hadoku_user_type");
545
- window.location.assign("/");
546
- }).catch(reportErr);
547
- },
548
- children: deleteStage === "busy" ? "Deleting…" : "Delete for good"
549
- }
550
- ) }),
551
- deleteError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: deleteError })
552
- ] }),
553
- children && /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack settings-popout__app", children })
695
+ )
554
696
  ] })
555
697
  ] });
556
698
  }