@abraca/nuxt 2.0.1 → 2.0.3

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 (116) hide show
  1. package/dist/module.d.mts +18 -7
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +21 -5
  4. package/dist/runtime/assets/aware-tokens.css +1 -0
  5. package/dist/runtime/components/AAccountSwitcherModal.d.vue.ts +16 -1
  6. package/dist/runtime/components/AAccountSwitcherModal.vue +33 -4
  7. package/dist/runtime/components/AAccountSwitcherModal.vue.d.ts +16 -1
  8. package/dist/runtime/components/AAuthLinkLanding.d.vue.ts +3 -0
  9. package/dist/runtime/components/AAuthLinkLanding.vue +85 -0
  10. package/dist/runtime/components/AAuthLinkLanding.vue.d.ts +3 -0
  11. package/dist/runtime/components/AClaimAccountModal.d.vue.ts +7 -1
  12. package/dist/runtime/components/AClaimAccountModal.vue +28 -13
  13. package/dist/runtime/components/AClaimAccountModal.vue.d.ts +7 -1
  14. package/dist/runtime/components/AEmailVerifyConfirmModal.d.vue.ts +30 -0
  15. package/dist/runtime/components/AEmailVerifyConfirmModal.vue +100 -0
  16. package/dist/runtime/components/AEmailVerifyConfirmModal.vue.d.ts +30 -0
  17. package/dist/runtime/components/AEmailVerifyRequestCard.d.vue.ts +22 -0
  18. package/dist/runtime/components/AEmailVerifyRequestCard.vue +65 -0
  19. package/dist/runtime/components/AEmailVerifyRequestCard.vue.d.ts +22 -0
  20. package/dist/runtime/components/AMnemonicLoginModal.d.vue.ts +1 -1
  21. package/dist/runtime/components/AMnemonicLoginModal.vue.d.ts +1 -1
  22. package/dist/runtime/components/ANodePanel.vue +2 -0
  23. package/dist/runtime/components/ANotificationBell.d.vue.ts +2 -2
  24. package/dist/runtime/components/ANotificationBell.vue.d.ts +2 -2
  25. package/dist/runtime/components/APasswordChangeModal.d.vue.ts +28 -0
  26. package/dist/runtime/components/APasswordChangeModal.vue +178 -0
  27. package/dist/runtime/components/APasswordChangeModal.vue.d.ts +28 -0
  28. package/dist/runtime/components/APasswordLoginModal.d.vue.ts +42 -0
  29. package/dist/runtime/components/APasswordLoginModal.vue +177 -0
  30. package/dist/runtime/components/APasswordLoginModal.vue.d.ts +42 -0
  31. package/dist/runtime/components/APasswordRegisterModal.d.vue.ts +49 -0
  32. package/dist/runtime/components/APasswordRegisterModal.vue +262 -0
  33. package/dist/runtime/components/APasswordRegisterModal.vue.d.ts +49 -0
  34. package/dist/runtime/components/APasswordResetConfirmModal.d.vue.ts +31 -0
  35. package/dist/runtime/components/APasswordResetConfirmModal.vue +154 -0
  36. package/dist/runtime/components/APasswordResetConfirmModal.vue.d.ts +31 -0
  37. package/dist/runtime/components/APasswordResetRequestModal.d.vue.ts +35 -0
  38. package/dist/runtime/components/APasswordResetRequestModal.vue +113 -0
  39. package/dist/runtime/components/APasswordResetRequestModal.vue.d.ts +35 -0
  40. package/dist/runtime/components/ASetPasswordCard.d.vue.ts +26 -0
  41. package/dist/runtime/components/ASetPasswordCard.vue +139 -0
  42. package/dist/runtime/components/ASetPasswordCard.vue.d.ts +26 -0
  43. package/dist/runtime/components/aware/AAccordion.d.vue.ts +2 -0
  44. package/dist/runtime/components/aware/AAccordion.vue +11 -1
  45. package/dist/runtime/components/aware/AAccordion.vue.d.ts +2 -0
  46. package/dist/runtime/components/aware/AButton.vue +3 -3
  47. package/dist/runtime/components/aware/ACollapsible.d.vue.ts +2 -0
  48. package/dist/runtime/components/aware/ACollapsible.vue +9 -1
  49. package/dist/runtime/components/aware/ACollapsible.vue.d.ts +2 -0
  50. package/dist/runtime/components/aware/AGlobalFocusLayer.vue +1 -1
  51. package/dist/runtime/components/aware/AHoverItem.vue +28 -3
  52. package/dist/runtime/components/aware/AModal.d.vue.ts +2 -0
  53. package/dist/runtime/components/aware/AModal.vue +9 -1
  54. package/dist/runtime/components/aware/AModal.vue.d.ts +2 -0
  55. package/dist/runtime/components/aware/APresenceBlobs.vue +1 -1
  56. package/dist/runtime/components/aware/APresenceCursors.vue +1 -1
  57. package/dist/runtime/components/aware/AScroll.d.vue.ts +2 -0
  58. package/dist/runtime/components/aware/AScroll.vue +13 -3
  59. package/dist/runtime/components/aware/AScroll.vue.d.ts +2 -0
  60. package/dist/runtime/components/aware/ASlideover.d.vue.ts +2 -0
  61. package/dist/runtime/components/aware/ASlideover.vue +9 -1
  62. package/dist/runtime/components/aware/ASlideover.vue.d.ts +2 -0
  63. package/dist/runtime/components/aware/ASlider.vue +1 -0
  64. package/dist/runtime/components/aware/ATabs.d.vue.ts +2 -0
  65. package/dist/runtime/components/aware/ATabs.vue +9 -1
  66. package/dist/runtime/components/aware/ATabs.vue.d.ts +2 -0
  67. package/dist/runtime/components/chat/ANodeChatPanel.vue +1 -0
  68. package/dist/runtime/components/editor/AEditorRedoButton.d.vue.ts +2 -2
  69. package/dist/runtime/components/editor/AEditorRedoButton.vue.d.ts +2 -2
  70. package/dist/runtime/components/editor/AEditorUndoButton.d.vue.ts +2 -2
  71. package/dist/runtime/components/editor/AEditorUndoButton.vue.d.ts +2 -2
  72. package/dist/runtime/components/shell/AUserProfilePopover.d.vue.ts +1 -1
  73. package/dist/runtime/components/shell/AUserProfilePopover.vue.d.ts +1 -1
  74. package/dist/runtime/composables/useAAField.js +7 -4
  75. package/dist/runtime/composables/useAAFocus.js +10 -5
  76. package/dist/runtime/composables/useAAFollowAnchor.js +68 -34
  77. package/dist/runtime/composables/useAAFollowPeer.d.ts +7 -4
  78. package/dist/runtime/composables/useAAFollowPeer.js +60 -11
  79. package/dist/runtime/composables/useAAViewport.d.ts +1 -1
  80. package/dist/runtime/composables/useAbracadabraAuth.d.ts +2 -0
  81. package/dist/runtime/composables/useAbracadabraAuth.js +2 -0
  82. package/dist/runtime/composables/useEmailVerification.d.ts +40 -26
  83. package/dist/runtime/composables/useEmailVerification.js +95 -43
  84. package/dist/runtime/composables/usePasswordAuth.d.ts +64 -0
  85. package/dist/runtime/composables/usePasswordAuth.js +126 -0
  86. package/dist/runtime/composables/useTiptapHistory.d.ts +2 -2
  87. package/dist/runtime/composables/useTiptapHistory.js +5 -5
  88. package/dist/runtime/extensions/views/MetaFieldView.vue +23 -6
  89. package/dist/runtime/plugin-abracadabra.client.js +57 -8
  90. package/dist/runtime/plugin-abracadabra.server.js +2 -0
  91. package/dist/runtime/server/plugins/abracadabra-service.js +20 -9
  92. package/dist/runtime/types.d.ts +11 -0
  93. package/dist/runtime/utils/awareRingStyle.js +1 -1
  94. package/package.json +7 -7
  95. package/dist/runtime/components/renderers/ASpatialRenderer.d.vue.ts +0 -19
  96. package/dist/runtime/components/renderers/ASpatialRenderer.vue +0 -459
  97. package/dist/runtime/components/renderers/ASpatialRenderer.vue.d.ts +0 -19
  98. package/dist/runtime/components/renderers/spatial/SpatialGround.d.vue.ts +0 -20
  99. package/dist/runtime/components/renderers/spatial/SpatialGround.vue +0 -26
  100. package/dist/runtime/components/renderers/spatial/SpatialGround.vue.d.ts +0 -20
  101. package/dist/runtime/components/renderers/spatial/SpatialObject.d.vue.ts +0 -17
  102. package/dist/runtime/components/renderers/spatial/SpatialObject.vue +0 -257
  103. package/dist/runtime/components/renderers/spatial/SpatialObject.vue.d.ts +0 -17
  104. package/dist/runtime/components/renderers/spatial/SpatialSceneBridge.d.vue.ts +0 -15
  105. package/dist/runtime/components/renderers/spatial/SpatialSceneBridge.vue +0 -18
  106. package/dist/runtime/components/renderers/spatial/SpatialSceneBridge.vue.d.ts +0 -15
  107. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.d.vue.ts +0 -16
  108. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue +0 -66
  109. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue.d.ts +0 -16
  110. package/dist/runtime/components/renderers/spatial/SpatialUserAvatar.d.vue.ts +0 -8
  111. package/dist/runtime/components/renderers/spatial/SpatialUserAvatar.vue +0 -53
  112. package/dist/runtime/components/renderers/spatial/SpatialUserAvatar.vue.d.ts +0 -8
  113. package/dist/runtime/composables/useSpatialCamera.d.ts +0 -16
  114. package/dist/runtime/composables/useSpatialCamera.js +0 -175
  115. package/dist/runtime/composables/useSpatialDrag.d.ts +0 -14
  116. package/dist/runtime/composables/useSpatialDrag.js +0 -137
package/dist/module.d.mts CHANGED
@@ -40,7 +40,6 @@ declare module '@nuxt/schema' {
40
40
  broadcastSync?: boolean;
41
41
  webrtc?: boolean;
42
42
  chat?: boolean;
43
- spatial?: boolean;
44
43
  media?: boolean;
45
44
  slides?: boolean;
46
45
  };
@@ -69,6 +68,10 @@ declare module '@nuxt/schema' {
69
68
  e2ee: boolean;
70
69
  fileTransfer: boolean;
71
70
  };
71
+ authQueryKeys: {
72
+ resetToken: string | null;
73
+ verifyToken: string | null;
74
+ };
72
75
  };
73
76
  }
74
77
  interface RuntimeConfig {
@@ -152,12 +155,6 @@ interface ModuleOptions {
152
155
  * Default: false.
153
156
  */
154
157
  chat?: boolean;
155
- /**
156
- * Register 3D spatial renderer and composables.
157
- * Requires @tresjs/core and @tresjs/cientos as optional peer dependencies.
158
- * Default: false.
159
- */
160
- spatial?: boolean;
161
158
  /**
162
159
  * Register media playlist renderer with synced listening sessions.
163
160
  * Default: false.
@@ -237,6 +234,20 @@ interface ModuleOptions {
237
234
  adjectives?: string[];
238
235
  nouns?: string[];
239
236
  };
237
+ /**
238
+ * Query-string keys read by `<AAuthLinkLanding>` to auto-open the matching
239
+ * confirm modal when an email link lands the user at an app route. Set
240
+ * any value to `null` to disable that landing (e.g. if your app handles
241
+ * the flow itself or never uses email links).
242
+ *
243
+ * Defaults: `{ resetToken: 'reset_token', verifyToken: 'verify_token' }`.
244
+ */
245
+ authQueryKeys?: {
246
+ /** Query param holding the password-reset token. Default: 'reset_token'. */
247
+ resetToken?: string | null;
248
+ /** Query param holding the email-verification token. Default: 'verify_token'. */
249
+ verifyToken?: string | null;
250
+ };
240
251
  /**
241
252
  * WebRTC P2P configuration. Only used when features.webrtc is true.
242
253
  */
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "2.0.1",
7
+ "version": "2.0.3",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -31,7 +31,6 @@ const module$1 = defineNuxtModule({
31
31
  broadcastSync: true,
32
32
  webrtc: false,
33
33
  chat: false,
34
- spatial: false,
35
34
  media: false,
36
35
  slides: true
37
36
  },
@@ -55,6 +54,10 @@ const module$1 = defineNuxtModule({
55
54
  e2ee: false,
56
55
  fileTransfer: false
57
56
  },
57
+ authQueryKeys: {
58
+ resetToken: "reset_token",
59
+ verifyToken: "verify_token"
60
+ },
58
61
  service: {
59
62
  disabled: false
60
63
  }
@@ -85,6 +88,10 @@ const module$1 = defineNuxtModule({
85
88
  iceServers: options.webrtc?.iceServers ?? [{ urls: "stun:stun.l.google.com:19302" }],
86
89
  e2ee: options.webrtc?.e2ee ?? false,
87
90
  fileTransfer: options.webrtc?.fileTransfer ?? false
91
+ },
92
+ authQueryKeys: {
93
+ resetToken: options.authQueryKeys?.resetToken ?? "reset_token",
94
+ verifyToken: options.authQueryKeys?.verifyToken ?? "verify_token"
88
95
  }
89
96
  }
90
97
  }
@@ -209,8 +216,10 @@ const module$1 = defineNuxtModule({
209
216
  // from internal modules (ChannelKeyResolver, decryptChatContent, foldRecords,
210
217
  // recordFromYAny, isEncryptedContent) get dropped — useChat.ts then fails to
211
218
  // import them. Excluding from optimizeDeps means Vite serves it native ESM.
212
- "@noble/ed25519",
213
- "@noble/hashes/sha512",
219
+ // Also: `@noble/ed25519` v3 exposes the runtime hash hook via the named
220
+ // export `hashes` (a const object). esbuild's pre-bundling tree-shakes
221
+ // that export from the dynamic-import namespace, leaving `ed.hashes`
222
+ // undefined at runtime. Serve native ESM to preserve the live binding.
214
223
  // Composable + util deps
215
224
  "@vueuse/core",
216
225
  "nanoevents"
@@ -225,8 +234,16 @@ const module$1 = defineNuxtModule({
225
234
  ];
226
235
  const existingExclude = nuxt.options.vite.optimizeDeps.exclude ?? [];
227
236
  nuxt.options.vite.optimizeDeps.exclude = [
228
- .../* @__PURE__ */ new Set([...existingExclude, "@abraca/dabra"])
237
+ .../* @__PURE__ */ new Set([
238
+ ...existingExclude,
239
+ "@abraca/dabra",
240
+ "@noble/ed25519",
241
+ "@noble/hashes/sha2.js",
242
+ "@noble/hashes/hkdf.js",
243
+ "@noble/curves/ed25519.js"
244
+ ])
229
245
  ];
246
+ nuxt.options.css.push(resolver.resolve("./runtime/assets/aware-tokens.css"));
230
247
  if (options.features?.editor !== false) {
231
248
  nuxt.options.css.push(resolver.resolve("./runtime/assets/editor.css"));
232
249
  }
@@ -290,7 +307,6 @@ const module$1 = defineNuxtModule({
290
307
  "**/table/**",
291
308
  "**/timeline/**",
292
309
  "**/media/**",
293
- "**/spatial/**",
294
310
  "**/gallery/**",
295
311
  "**/sheets/**",
296
312
  "**/slides/**",
@@ -0,0 +1 @@
1
+ :root{--aa-track-cursor-duration:60ms;--aa-track-cursor-easing:linear;--aa-track-blob-duration:200ms;--aa-track-blob-easing:ease-out;--aa-state-fade-shadow:100ms ease;--aa-state-fade-filter:120ms ease;--aa-pulse-duration:260ms;--aa-pulse-easing:ease-out;--aa-pulse-radius-button:12px;--aa-pulse-radius-item:14px;--aa-pulse-opacity-start-button:0.45;--aa-pulse-opacity-start-item:0.5;--aa-peer-fade-duration:150ms;--aa-peer-fade-easing:ease;--aa-flag-fade-duration:120ms;--aa-flag-fade-easing:ease}
@@ -1,10 +1,25 @@
1
1
  type __VLS_Props = {
2
2
  open: boolean;
3
+ /** Show the "Sign in with password" link in the footer. */
4
+ showPasswordSignIn?: boolean;
5
+ /**
6
+ * Show the "Add a password to this account" CTA when the signed-in user has no
7
+ * password set. Drives the flow described in `gaps/14-password-auth.md`
8
+ * (key-only soft identity opting into a recovery credential).
9
+ */
10
+ showAddPasswordCta?: boolean;
3
11
  };
4
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
13
  "update:open": (v: boolean) => any;
14
+ "password-signin": () => any;
15
+ "add-password": () => any;
6
16
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
7
17
  "onUpdate:open"?: ((v: boolean) => any) | undefined;
8
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ "onPassword-signin"?: (() => any) | undefined;
19
+ "onAdd-password"?: (() => any) | undefined;
20
+ }>, {
21
+ showPasswordSignIn: boolean;
22
+ showAddPasswordCta: boolean;
23
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
24
  declare const _default: typeof __VLS_export;
10
25
  export default _default;
@@ -1,13 +1,18 @@
1
1
  <script setup>
2
- import { ref, watch } from "vue";
2
+ import { computed, ref, watch } from "vue";
3
3
  import { useAbracadabra } from "../composables/useAbracadabra";
4
4
  import { usePasskeyAccounts } from "../composables/usePasskeyAccounts";
5
5
  const props = defineProps({
6
- open: { type: Boolean, required: true }
6
+ open: { type: Boolean, required: true },
7
+ showPasswordSignIn: { type: Boolean, required: false, default: true },
8
+ showAddPasswordCta: { type: Boolean, required: false, default: true }
7
9
  });
8
- const emit = defineEmits(["update:open"]);
9
- const { loginWithHardware, publicKeyB64 } = useAbracadabra();
10
+ const emit = defineEmits(["update:open", "password-signin", "add-password"]);
11
+ const { loginWithHardware, publicKeyB64, hasPassword, isClaimed } = useAbracadabra();
10
12
  const { accounts: passkeyAccounts } = usePasskeyAccounts();
13
+ const showAddPassword = computed(
14
+ () => props.showAddPasswordCta && isClaimed.value && !hasPassword.value
15
+ );
11
16
  const isLoading = ref(null);
12
17
  const error = ref(null);
13
18
  watch(() => props.open, (open) => {
@@ -152,6 +157,30 @@ async function loginAny() {
152
157
  >
153
158
  Use Any Passkey
154
159
  </UButton>
160
+
161
+ <UButton
162
+ v-if="showPasswordSignIn"
163
+ variant="ghost"
164
+ color="neutral"
165
+ icon="i-lucide-key-square"
166
+ class="w-full justify-start"
167
+ @click="emit('update:open', false);
168
+ emit('password-signin')"
169
+ >
170
+ Sign in with password
171
+ </UButton>
172
+
173
+ <UButton
174
+ v-if="showAddPassword"
175
+ variant="soft"
176
+ color="primary"
177
+ icon="i-lucide-shield-plus"
178
+ class="w-full justify-start"
179
+ @click="emit('update:open', false);
180
+ emit('add-password')"
181
+ >
182
+ Add a password to this account
183
+ </UButton>
155
184
  </div>
156
185
  </template>
157
186
  </UModal>
@@ -1,10 +1,25 @@
1
1
  type __VLS_Props = {
2
2
  open: boolean;
3
+ /** Show the "Sign in with password" link in the footer. */
4
+ showPasswordSignIn?: boolean;
5
+ /**
6
+ * Show the "Add a password to this account" CTA when the signed-in user has no
7
+ * password set. Drives the flow described in `gaps/14-password-auth.md`
8
+ * (key-only soft identity opting into a recovery credential).
9
+ */
10
+ showAddPasswordCta?: boolean;
3
11
  };
4
12
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
13
  "update:open": (v: boolean) => any;
14
+ "password-signin": () => any;
15
+ "add-password": () => any;
6
16
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
7
17
  "onUpdate:open"?: ((v: boolean) => any) | undefined;
8
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ "onPassword-signin"?: (() => any) | undefined;
19
+ "onAdd-password"?: (() => any) | undefined;
20
+ }>, {
21
+ showPasswordSignIn: boolean;
22
+ showAddPasswordCta: boolean;
23
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
24
  declare const _default: typeof __VLS_export;
10
25
  export default _default;
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,85 @@
1
+ <script setup>
2
+ import { computed, ref, watch } from "vue";
3
+ import { useRoute, useRouter, useRuntimeConfig } from "#imports";
4
+ import { usePasswordAuth } from "../composables/usePasswordAuth";
5
+ import { useEmailVerification } from "../composables/useEmailVerification";
6
+ const config = useRuntimeConfig();
7
+ const keys = config.public.abracadabra.authQueryKeys;
8
+ const route = useRoute();
9
+ const router = useRouter();
10
+ const pw = usePasswordAuth();
11
+ const ev = useEmailVerification();
12
+ const resetOpen = ref(false);
13
+ const resetInitialToken = ref("");
14
+ const verifyOpen = ref(false);
15
+ const verifyInitialToken = ref("");
16
+ function readQuery(key) {
17
+ if (!key) return "";
18
+ const v = route.query[key];
19
+ if (typeof v === "string") return v.trim();
20
+ if (Array.isArray(v) && typeof v[0] === "string") return v[0].trim();
21
+ return "";
22
+ }
23
+ function stripQueryKey(key) {
24
+ if (!key) return;
25
+ if (typeof window === "undefined") return;
26
+ const url = new URL(window.location.href);
27
+ if (!url.searchParams.has(key)) return;
28
+ url.searchParams.delete(key);
29
+ const next = `${url.pathname}${url.search ? `?${url.searchParams}` : ""}${url.hash}`;
30
+ router.replace({ query: { ...route.query, [key]: void 0 } }).catch(() => {
31
+ window.history.replaceState(window.history.state, "", next);
32
+ });
33
+ }
34
+ const resetTokenSeen = computed(() => readQuery(keys.resetToken));
35
+ watch(resetTokenSeen, (token) => {
36
+ if (!token) return;
37
+ resetInitialToken.value = token;
38
+ resetOpen.value = true;
39
+ }, { immediate: true });
40
+ async function onResetSubmit(p) {
41
+ const res = await pw.confirmReset(p);
42
+ if (res.ok) {
43
+ resetOpen.value = false;
44
+ stripQueryKey(keys.resetToken);
45
+ }
46
+ }
47
+ watch(resetOpen, (open) => {
48
+ if (!open) stripQueryKey(keys.resetToken);
49
+ });
50
+ const verifyTokenSeen = computed(() => readQuery(keys.verifyToken));
51
+ watch(verifyTokenSeen, (token) => {
52
+ if (!token) return;
53
+ verifyInitialToken.value = token;
54
+ verifyOpen.value = true;
55
+ }, { immediate: true });
56
+ async function onVerifySubmit(p) {
57
+ const res = await ev.confirm(p);
58
+ if (res.ok) {
59
+ stripQueryKey(keys.verifyToken);
60
+ }
61
+ }
62
+ watch(verifyOpen, (open) => {
63
+ if (!open) stripQueryKey(keys.verifyToken);
64
+ });
65
+ </script>
66
+
67
+ <template>
68
+ <APasswordResetConfirmModal
69
+ v-if="keys.resetToken"
70
+ v-model:open="resetOpen"
71
+ :loading="pw.busy.value === 'reset-confirm'"
72
+ :error="pw.busy.value === null && pw.error.value && pw.lastSuccess.value !== 'reset-confirm' ? pw.error.value.message : null"
73
+ :initial-token="resetInitialToken"
74
+ @submit="onResetSubmit"
75
+ />
76
+ <AEmailVerifyConfirmModal
77
+ v-if="keys.verifyToken"
78
+ v-model:open="verifyOpen"
79
+ :loading="ev.busy.value === 'confirm'"
80
+ :success="ev.verified.value"
81
+ :error="ev.busy.value === null && ev.error.value && ev.lastSuccess.value !== 'confirm' ? ev.error.value.message : null"
82
+ :initial-token="verifyInitialToken"
83
+ @submit="onVerifySubmit"
84
+ />
85
+ </template>
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -1,10 +1,16 @@
1
1
  type __VLS_Props = {
2
2
  open: boolean;
3
+ /** Show the "Use a password instead" link in the identity step. */
4
+ showPasswordOption?: boolean;
3
5
  };
4
6
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
7
  "update:open": (v: boolean) => any;
8
+ "password-signup": () => any;
6
9
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
7
10
  "onUpdate:open"?: ((v: boolean) => any) | undefined;
8
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
+ "onPassword-signup"?: (() => any) | undefined;
12
+ }>, {
13
+ showPasswordOption: boolean;
14
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
15
  declare const _default: typeof __VLS_export;
10
16
  export default _default;
@@ -3,9 +3,10 @@ import { ref, watch } from "vue";
3
3
  import { UI_COLORS } from "../types";
4
4
  import { useAbracadabra } from "../composables/useAbracadabra";
5
5
  const props = defineProps({
6
- open: { type: Boolean, required: true }
6
+ open: { type: Boolean, required: true },
7
+ showPasswordOption: { type: Boolean, required: false, default: true }
7
8
  });
8
- const emit = defineEmits(["update:open"]);
9
+ const emit = defineEmits(["update:open", "password-signup"]);
9
10
  const {
10
11
  userName,
11
12
  userColorName,
@@ -90,19 +91,19 @@ async function handleClaim() {
90
91
  <p class="text-xs font-medium text-(--ui-text-muted) uppercase tracking-wide">
91
92
  Identity Color
92
93
  </p>
93
- <div class="flex flex-wrap justify-center gap-1.5 max-w-78">
94
- <UTooltip
94
+ <div class="claim-swatches">
95
+ <button
95
96
  v-for="color in UI_COLORS"
96
97
  :key="color"
97
- :text="color"
98
- >
99
- <button
100
- class="w-5 h-5 rounded-full ring-2 ring-offset-1 ring-offset-(--ui-bg) transition-all duration-150 hover:scale-110 focus:outline-none"
101
- :class="userColorName === color ? 'ring-current scale-110' : 'ring-transparent'"
102
- :style="{ background: `var(--color-${color}-400)` }"
103
- @click="setUserColor(color)"
104
- />
105
- </UTooltip>
98
+ type="button"
99
+ :title="color"
100
+ :aria-label="color"
101
+ :aria-pressed="userColorName === color"
102
+ class="claim-swatch focus:outline-none"
103
+ :class="userColorName === color ? 'claim-swatch--selected' : ''"
104
+ :style="{ background: `var(--color-${color}-400)` }"
105
+ @click="setUserColor(color)"
106
+ />
106
107
  </div>
107
108
  </div>
108
109
 
@@ -124,6 +125,16 @@ async function handleClaim() {
124
125
  block
125
126
  @click="emit('update:open', false)"
126
127
  />
128
+
129
+ <button
130
+ v-if="showPasswordOption"
131
+ type="button"
132
+ class="text-xs text-(--ui-text-muted) hover:text-(--ui-text-highlighted) underline underline-offset-4 mx-auto -mt-2"
133
+ @click="emit('update:open', false);
134
+ emit('password-signup')"
135
+ >
136
+ Use a password instead
137
+ </button>
127
138
  </div>
128
139
 
129
140
  <!-- Step 2: Passkey ceremony -->
@@ -228,3 +239,7 @@ async function handleClaim() {
228
239
  </template>
229
240
  </UModal>
230
241
  </template>
242
+
243
+ <style scoped>
244
+ .claim-swatches{display:grid;gap:.45rem;grid-template-columns:repeat(auto-fit,1.25rem);justify-content:center;max-width:18rem}.claim-swatch{border:none;border-radius:999px;box-shadow:0 0 0 0 transparent;cursor:pointer;height:1.25rem;padding:0;transition:transform .15s ease,box-shadow .15s ease;width:1.25rem}.claim-swatch:hover{transform:scale(1.12)}.claim-swatch--selected{box-shadow:0 0 0 2px var(--ui-bg),0 0 0 4px var(--ui-text-highlighted);transform:scale(1.18)}
245
+ </style>
@@ -1,10 +1,16 @@
1
1
  type __VLS_Props = {
2
2
  open: boolean;
3
+ /** Show the "Use a password instead" link in the identity step. */
4
+ showPasswordOption?: boolean;
3
5
  };
4
6
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
7
  "update:open": (v: boolean) => any;
8
+ "password-signup": () => any;
6
9
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
7
10
  "onUpdate:open"?: ((v: boolean) => any) | undefined;
8
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
+ "onPassword-signup"?: (() => any) | undefined;
12
+ }>, {
13
+ showPasswordOption: boolean;
14
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
15
  declare const _default: typeof __VLS_export;
10
16
  export default _default;
@@ -0,0 +1,30 @@
1
+ type __VLS_Props = {
2
+ open?: boolean;
3
+ loading?: boolean;
4
+ error?: string | null;
5
+ /** Flip to true after a successful confirm — modal pivots to a success body. */
6
+ success?: boolean;
7
+ /** Pre-fill the token (e.g. from a query param). */
8
+ initialToken?: string;
9
+ title?: string;
10
+ subtitle?: string;
11
+ };
12
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ submit: (payload: {
14
+ token: string;
15
+ }) => any;
16
+ "update:open": (v: boolean) => any;
17
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
+ onSubmit?: ((payload: {
19
+ token: string;
20
+ }) => any) | undefined;
21
+ "onUpdate:open"?: ((v: boolean) => any) | undefined;
22
+ }>, {
23
+ subtitle: string;
24
+ title: string;
25
+ success: boolean;
26
+ loading: boolean;
27
+ initialToken: string;
28
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
29
+ declare const _default: typeof __VLS_export;
30
+ export default _default;
@@ -0,0 +1,100 @@
1
+ <script setup>
2
+ import { computed, ref, watch } from "vue";
3
+ const props = defineProps({
4
+ open: { type: Boolean, required: false },
5
+ loading: { type: Boolean, required: false, default: false },
6
+ error: { type: [String, null], required: false },
7
+ success: { type: Boolean, required: false, default: false },
8
+ initialToken: { type: String, required: false, default: "" },
9
+ title: { type: String, required: false, default: "Verify your email" },
10
+ subtitle: { type: String, required: false, default: "Paste the token from your verification email to confirm your address." }
11
+ });
12
+ const emit = defineEmits(["update:open", "submit"]);
13
+ const open = computed({
14
+ get: () => !!props.open,
15
+ set: (v) => emit("update:open", v)
16
+ });
17
+ const token = ref("");
18
+ watch(() => props.open, (v) => {
19
+ if (!v) return;
20
+ token.value = props.initialToken || "";
21
+ });
22
+ const canSubmit = computed(
23
+ () => !!token.value.trim() && !props.loading && !props.success
24
+ );
25
+ function submit() {
26
+ if (!canSubmit.value) return;
27
+ emit("submit", { token: token.value.trim() });
28
+ }
29
+ </script>
30
+
31
+ <template>
32
+ <UModal
33
+ v-model:open="open"
34
+ :title="title"
35
+ :ui="{ content: 'sm:max-w-md' }"
36
+ >
37
+ <template #body>
38
+ <div class="flex flex-col gap-4">
39
+ <UAlert
40
+ v-if="success"
41
+ color="success"
42
+ variant="soft"
43
+ icon="i-lucide-shield-check"
44
+ title="Email verified"
45
+ description="Your email address is now confirmed. You can close this dialog."
46
+ />
47
+
48
+ <template v-else>
49
+ <p class="text-sm text-(--ui-text-muted)">
50
+ {{ subtitle }}
51
+ </p>
52
+
53
+ <UAlert
54
+ v-if="error"
55
+ color="error"
56
+ variant="soft"
57
+ icon="i-lucide-triangle-alert"
58
+ :description="error"
59
+ />
60
+
61
+ <div class="flex flex-col gap-1">
62
+ <label class="text-xs font-medium text-(--ui-text-muted)">Verification token</label>
63
+ <UInput
64
+ v-model="token"
65
+ size="md"
66
+ autofocus
67
+ placeholder="paste from email"
68
+ icon="i-lucide-key"
69
+ :ui="{ base: 'font-mono' }"
70
+ @keydown.enter="submit"
71
+ />
72
+ </div>
73
+ </template>
74
+ </div>
75
+ </template>
76
+
77
+ <template #footer>
78
+ <div class="flex items-center justify-end gap-2 w-full">
79
+ <UButton
80
+ variant="ghost"
81
+ color="neutral"
82
+ size="sm"
83
+ :label="success ? 'Close' : 'Cancel'"
84
+ :disabled="loading"
85
+ @click="open = false"
86
+ />
87
+ <UButton
88
+ v-if="!success"
89
+ color="primary"
90
+ size="sm"
91
+ icon="i-lucide-check"
92
+ label="Confirm email"
93
+ :loading="loading"
94
+ :disabled="!canSubmit"
95
+ @click="submit"
96
+ />
97
+ </div>
98
+ </template>
99
+ </UModal>
100
+ </template>
@@ -0,0 +1,30 @@
1
+ type __VLS_Props = {
2
+ open?: boolean;
3
+ loading?: boolean;
4
+ error?: string | null;
5
+ /** Flip to true after a successful confirm — modal pivots to a success body. */
6
+ success?: boolean;
7
+ /** Pre-fill the token (e.g. from a query param). */
8
+ initialToken?: string;
9
+ title?: string;
10
+ subtitle?: string;
11
+ };
12
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ submit: (payload: {
14
+ token: string;
15
+ }) => any;
16
+ "update:open": (v: boolean) => any;
17
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
+ onSubmit?: ((payload: {
19
+ token: string;
20
+ }) => any) | undefined;
21
+ "onUpdate:open"?: ((v: boolean) => any) | undefined;
22
+ }>, {
23
+ subtitle: string;
24
+ title: string;
25
+ success: boolean;
26
+ loading: boolean;
27
+ initialToken: string;
28
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
29
+ declare const _default: typeof __VLS_export;
30
+ export default _default;
@@ -0,0 +1,22 @@
1
+ type __VLS_Props = {
2
+ loading?: boolean;
3
+ /** Flip to true after the consumer's request handler resolves. */
4
+ sent?: boolean;
5
+ error?: string | null;
6
+ /** Address shown in the cooldown copy ("we sent a link to <email>"). Optional. */
7
+ email?: string | null;
8
+ title?: string;
9
+ subtitle?: string;
10
+ };
11
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
12
+ request: () => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ onRequest?: (() => any) | undefined;
15
+ }>, {
16
+ subtitle: string;
17
+ title: string;
18
+ loading: boolean;
19
+ sent: boolean;
20
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;