@abraca/nuxt 2.0.0 → 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 (135) 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/AEditor.vue +5 -0
  15. package/dist/runtime/components/AEmailVerifyConfirmModal.d.vue.ts +30 -0
  16. package/dist/runtime/components/AEmailVerifyConfirmModal.vue +100 -0
  17. package/dist/runtime/components/AEmailVerifyConfirmModal.vue.d.ts +30 -0
  18. package/dist/runtime/components/AEmailVerifyRequestCard.d.vue.ts +22 -0
  19. package/dist/runtime/components/AEmailVerifyRequestCard.vue +65 -0
  20. package/dist/runtime/components/AEmailVerifyRequestCard.vue.d.ts +22 -0
  21. package/dist/runtime/components/AMnemonicLoginModal.d.vue.ts +1 -1
  22. package/dist/runtime/components/AMnemonicLoginModal.vue.d.ts +1 -1
  23. package/dist/runtime/components/ANodePanel.vue +2 -0
  24. package/dist/runtime/components/ANotificationBell.d.vue.ts +2 -2
  25. package/dist/runtime/components/ANotificationBell.vue.d.ts +2 -2
  26. package/dist/runtime/components/APasswordChangeModal.d.vue.ts +28 -0
  27. package/dist/runtime/components/APasswordChangeModal.vue +178 -0
  28. package/dist/runtime/components/APasswordChangeModal.vue.d.ts +28 -0
  29. package/dist/runtime/components/APasswordLoginModal.d.vue.ts +42 -0
  30. package/dist/runtime/components/APasswordLoginModal.vue +177 -0
  31. package/dist/runtime/components/APasswordLoginModal.vue.d.ts +42 -0
  32. package/dist/runtime/components/APasswordRegisterModal.d.vue.ts +49 -0
  33. package/dist/runtime/components/APasswordRegisterModal.vue +262 -0
  34. package/dist/runtime/components/APasswordRegisterModal.vue.d.ts +49 -0
  35. package/dist/runtime/components/APasswordResetConfirmModal.d.vue.ts +31 -0
  36. package/dist/runtime/components/APasswordResetConfirmModal.vue +154 -0
  37. package/dist/runtime/components/APasswordResetConfirmModal.vue.d.ts +31 -0
  38. package/dist/runtime/components/APasswordResetRequestModal.d.vue.ts +35 -0
  39. package/dist/runtime/components/APasswordResetRequestModal.vue +113 -0
  40. package/dist/runtime/components/APasswordResetRequestModal.vue.d.ts +35 -0
  41. package/dist/runtime/components/ASetPasswordCard.d.vue.ts +26 -0
  42. package/dist/runtime/components/ASetPasswordCard.vue +139 -0
  43. package/dist/runtime/components/ASetPasswordCard.vue.d.ts +26 -0
  44. package/dist/runtime/components/ASubPageList.d.vue.ts +66 -0
  45. package/dist/runtime/components/ASubPageList.vue +147 -0
  46. package/dist/runtime/components/ASubPageList.vue.d.ts +66 -0
  47. package/dist/runtime/components/aware/AAccordion.d.vue.ts +2 -0
  48. package/dist/runtime/components/aware/AAccordion.vue +11 -1
  49. package/dist/runtime/components/aware/AAccordion.vue.d.ts +2 -0
  50. package/dist/runtime/components/aware/AButton.vue +3 -3
  51. package/dist/runtime/components/aware/ACollapsible.d.vue.ts +2 -0
  52. package/dist/runtime/components/aware/ACollapsible.vue +9 -1
  53. package/dist/runtime/components/aware/ACollapsible.vue.d.ts +2 -0
  54. package/dist/runtime/components/aware/AGlobalFocusLayer.vue +1 -1
  55. package/dist/runtime/components/aware/AHoverItem.vue +28 -3
  56. package/dist/runtime/components/aware/AMedia.d.vue.ts +1 -1
  57. package/dist/runtime/components/aware/AMedia.vue.d.ts +1 -1
  58. package/dist/runtime/components/aware/AModal.d.vue.ts +2 -0
  59. package/dist/runtime/components/aware/AModal.vue +9 -1
  60. package/dist/runtime/components/aware/AModal.vue.d.ts +2 -0
  61. package/dist/runtime/components/aware/APresenceBlobs.vue +1 -1
  62. package/dist/runtime/components/aware/APresenceCursors.vue +1 -1
  63. package/dist/runtime/components/aware/AScroll.d.vue.ts +2 -0
  64. package/dist/runtime/components/aware/AScroll.vue +13 -3
  65. package/dist/runtime/components/aware/AScroll.vue.d.ts +2 -0
  66. package/dist/runtime/components/aware/ASlideover.d.vue.ts +2 -0
  67. package/dist/runtime/components/aware/ASlideover.vue +9 -1
  68. package/dist/runtime/components/aware/ASlideover.vue.d.ts +2 -0
  69. package/dist/runtime/components/aware/ASlider.vue +1 -0
  70. package/dist/runtime/components/aware/ATabs.d.vue.ts +2 -0
  71. package/dist/runtime/components/aware/ATabs.vue +9 -1
  72. package/dist/runtime/components/aware/ATabs.vue.d.ts +2 -0
  73. package/dist/runtime/components/chat/ANodeChatPanel.vue +1 -0
  74. package/dist/runtime/components/editor/AEditorRedoButton.d.vue.ts +2 -2
  75. package/dist/runtime/components/editor/AEditorRedoButton.vue.d.ts +2 -2
  76. package/dist/runtime/components/editor/AEditorUndoButton.d.vue.ts +2 -2
  77. package/dist/runtime/components/editor/AEditorUndoButton.vue.d.ts +2 -2
  78. package/dist/runtime/components/renderers/calendar/ACalendarToolbar.d.vue.ts +4 -4
  79. package/dist/runtime/components/renderers/calendar/ACalendarToolbar.vue.d.ts +4 -4
  80. package/dist/runtime/components/renderers/media/MediaTransportBar.d.vue.ts +2 -2
  81. package/dist/runtime/components/renderers/media/MediaTransportBar.vue.d.ts +2 -2
  82. package/dist/runtime/components/shell/AUserProfilePopover.d.vue.ts +1 -1
  83. package/dist/runtime/components/shell/AUserProfilePopover.vue.d.ts +1 -1
  84. package/dist/runtime/composables/useAAField.js +7 -4
  85. package/dist/runtime/composables/useAAFocus.js +10 -5
  86. package/dist/runtime/composables/useAAFollowAnchor.js +68 -34
  87. package/dist/runtime/composables/useAAFollowPeer.d.ts +7 -4
  88. package/dist/runtime/composables/useAAFollowPeer.js +60 -11
  89. package/dist/runtime/composables/useAAViewport.d.ts +1 -1
  90. package/dist/runtime/composables/useAbracadabraAuth.d.ts +2 -0
  91. package/dist/runtime/composables/useAbracadabraAuth.js +2 -0
  92. package/dist/runtime/composables/useEditorSuggestions.js +2 -1
  93. package/dist/runtime/composables/useEmailVerification.d.ts +40 -26
  94. package/dist/runtime/composables/useEmailVerification.js +95 -43
  95. package/dist/runtime/composables/usePasswordAuth.d.ts +64 -0
  96. package/dist/runtime/composables/usePasswordAuth.js +126 -0
  97. package/dist/runtime/composables/useTiptapHistory.d.ts +2 -2
  98. package/dist/runtime/composables/useTiptapHistory.js +5 -5
  99. package/dist/runtime/extensions/svg-embed.d.ts +23 -0
  100. package/dist/runtime/extensions/svg-embed.js +33 -0
  101. package/dist/runtime/extensions/views/MetaFieldView.vue +23 -6
  102. package/dist/runtime/extensions/views/SvgEmbedView.d.vue.ts +4 -0
  103. package/dist/runtime/extensions/views/SvgEmbedView.vue +120 -0
  104. package/dist/runtime/extensions/views/SvgEmbedView.vue.d.ts +4 -0
  105. package/dist/runtime/plugin-abracadabra.client.js +58 -9
  106. package/dist/runtime/plugin-abracadabra.server.js +2 -0
  107. package/dist/runtime/plugins/core.plugin.js +8 -4
  108. package/dist/runtime/server/plugins/abracadabra-service.js +102 -13
  109. package/dist/runtime/types.d.ts +11 -0
  110. package/dist/runtime/utils/awareRingStyle.js +1 -1
  111. package/dist/runtime/utils/sanitizeSvg.d.ts +19 -0
  112. package/dist/runtime/utils/sanitizeSvg.js +87 -0
  113. package/package.json +7 -8
  114. package/dist/runtime/components/renderers/ASpatialRenderer.d.vue.ts +0 -19
  115. package/dist/runtime/components/renderers/ASpatialRenderer.vue +0 -459
  116. package/dist/runtime/components/renderers/ASpatialRenderer.vue.d.ts +0 -19
  117. package/dist/runtime/components/renderers/spatial/SpatialGround.d.vue.ts +0 -20
  118. package/dist/runtime/components/renderers/spatial/SpatialGround.vue +0 -26
  119. package/dist/runtime/components/renderers/spatial/SpatialGround.vue.d.ts +0 -20
  120. package/dist/runtime/components/renderers/spatial/SpatialObject.d.vue.ts +0 -17
  121. package/dist/runtime/components/renderers/spatial/SpatialObject.vue +0 -257
  122. package/dist/runtime/components/renderers/spatial/SpatialObject.vue.d.ts +0 -17
  123. package/dist/runtime/components/renderers/spatial/SpatialSceneBridge.d.vue.ts +0 -15
  124. package/dist/runtime/components/renderers/spatial/SpatialSceneBridge.vue +0 -18
  125. package/dist/runtime/components/renderers/spatial/SpatialSceneBridge.vue.d.ts +0 -15
  126. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.d.vue.ts +0 -16
  127. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue +0 -66
  128. package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue.d.ts +0 -16
  129. package/dist/runtime/components/renderers/spatial/SpatialUserAvatar.d.vue.ts +0 -8
  130. package/dist/runtime/components/renderers/spatial/SpatialUserAvatar.vue +0 -53
  131. package/dist/runtime/components/renderers/spatial/SpatialUserAvatar.vue.d.ts +0 -8
  132. package/dist/runtime/composables/useSpatialCamera.d.ts +0 -16
  133. package/dist/runtime/composables/useSpatialCamera.js +0 -175
  134. package/dist/runtime/composables/useSpatialDrag.d.ts +0 -14
  135. package/dist/runtime/composables/useSpatialDrag.js +0 -137
@@ -20,16 +20,16 @@ type __VLS_Props = {
20
20
  };
21
21
  };
22
22
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
- next: () => any;
24
- prev: () => any;
25
23
  "update:viewMode": (mode: CalendarViewMode) => any;
24
+ prev: () => any;
25
+ next: () => any;
26
26
  today: () => any;
27
27
  "add-event": () => any;
28
28
  "navigate-to-month": (year: number, month: number) => any;
29
29
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
- onNext?: (() => any) | undefined;
31
- onPrev?: (() => any) | undefined;
32
30
  "onUpdate:viewMode"?: ((mode: CalendarViewMode) => any) | undefined;
31
+ onPrev?: (() => any) | undefined;
32
+ onNext?: (() => any) | undefined;
33
33
  onToday?: (() => any) | undefined;
34
34
  "onAdd-event"?: (() => any) | undefined;
35
35
  "onNavigate-to-month"?: ((year: number, month: number) => any) | undefined;
@@ -8,13 +8,13 @@ type __VLS_Props = {
8
8
  isLoading: boolean;
9
9
  };
10
10
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
- next: () => any;
12
11
  prev: () => any;
12
+ next: () => any;
13
13
  togglePlay: () => any;
14
14
  seek: (position: number) => any;
15
15
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
- onNext?: (() => any) | undefined;
17
16
  onPrev?: (() => any) | undefined;
17
+ onNext?: (() => any) | undefined;
18
18
  onTogglePlay?: (() => any) | undefined;
19
19
  onSeek?: ((position: number) => any) | undefined;
20
20
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -8,13 +8,13 @@ type __VLS_Props = {
8
8
  isLoading: boolean;
9
9
  };
10
10
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
- next: () => any;
12
11
  prev: () => any;
12
+ next: () => any;
13
13
  togglePlay: () => any;
14
14
  seek: (position: number) => any;
15
15
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
- onNext?: (() => any) | undefined;
17
16
  onPrev?: (() => any) | undefined;
17
+ onNext?: (() => any) | undefined;
18
18
  onTogglePlay?: (() => any) | undefined;
19
19
  onSeek?: ((position: number) => any) | undefined;
20
20
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -39,8 +39,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
39
39
  currentDocId: string | null;
40
40
  avatarStyle: string;
41
41
  isSelf: boolean;
42
- showFollow: boolean;
43
42
  isFollowing: boolean;
43
+ showFollow: boolean;
44
44
  currentDocLabel: string | null;
45
45
  currentDocIcon: string;
46
46
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -39,8 +39,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
39
39
  currentDocId: string | null;
40
40
  avatarStyle: string;
41
41
  isSelf: boolean;
42
- showFollow: boolean;
43
42
  isFollowing: boolean;
43
+ showFollow: boolean;
44
44
  currentDocLabel: string | null;
45
45
  currentDocIcon: string;
46
46
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -2,7 +2,7 @@ import { computed, isRef, onScopeDispose, ref } from "vue";
2
2
  import { useAwareness } from "./useAwareness.js";
3
3
  import { useAwarenessPeers } from "./useAwarenessPeers.js";
4
4
  export function useAAField(key) {
5
- const { setField } = useAwareness();
5
+ const { setField, localState } = useAwareness();
6
6
  const { peers } = useAwarenessPeers();
7
7
  const fieldKey = computed(
8
8
  () => typeof key === "function" ? key() : isRef(key) ? key.value : key
@@ -38,7 +38,8 @@ export function useAAField(key) {
38
38
  setField("hover", fieldKey.value);
39
39
  }
40
40
  function onMouseleave() {
41
- setField("hover", null);
41
+ const current = localState.value?.hover;
42
+ if (current === fieldKey.value) setField("hover", null);
42
43
  }
43
44
  function onMousedown() {
44
45
  localPress.value = true;
@@ -46,13 +47,15 @@ export function useAAField(key) {
46
47
  }
47
48
  function onMouseup() {
48
49
  localPress.value = false;
49
- setField("press", null);
50
+ const current = localState.value?.press;
51
+ if (current === fieldKey.value) setField("press", null);
50
52
  }
51
53
  function onFocus() {
52
54
  setField("focus", fieldKey.value);
53
55
  }
54
56
  function onBlur() {
55
- setField("focus", null);
57
+ const current = localState.value?.focus;
58
+ if (current === fieldKey.value) setField("focus", null);
56
59
  }
57
60
  const isPressed = computed(() => localPress.value || pressers.value.length > 0);
58
61
  const handlers = {
@@ -97,12 +97,17 @@ export function useAAFocus() {
97
97
  const candidates = document.elementsFromPoint(clientX, clientY);
98
98
  for (const el of candidates) {
99
99
  if (!isAnchorable(el)) continue;
100
- const path = pathFromElement(el);
101
- if (!path) continue;
102
- const r = el.getBoundingClientRect();
103
- return { elementPath: path, dx: clientX - r.left, dy: clientY - r.top };
100
+ const path2 = pathFromElement(el);
101
+ if (!path2) continue;
102
+ const r2 = el.getBoundingClientRect();
103
+ return { elementPath: path2, dx: clientX - r2.left, dy: clientY - r2.top };
104
104
  }
105
- return null;
105
+ const picked = pickAnchorElement();
106
+ if (!picked) return null;
107
+ const path = pathFromElement(picked.el);
108
+ if (!path) return null;
109
+ const r = picked.el.getBoundingClientRect();
110
+ return { elementPath: path, dx: clientX - r.left, dy: clientY - r.top };
106
111
  }
107
112
  function snapshot() {
108
113
  cancelAnimationFrame(raf);
@@ -1,4 +1,4 @@
1
- import { computed, watch } from "vue";
1
+ import { computed, onScopeDispose, watch } from "vue";
2
2
  import { useRoute, useRouter } from "#imports";
3
3
  import { useAAEphemeral } from "./useAAEphemeral.js";
4
4
  import { useAwarenessPeers } from "./useAwarenessPeers.js";
@@ -8,8 +8,11 @@ export function useAAFollowAnchor(leaderId) {
8
8
  const { peers: allPeers } = useAwarenessPeers();
9
9
  const route = useRoute();
10
10
  const router = useRouter();
11
+ const scrollState = /* @__PURE__ */ new Map();
11
12
  let scrollRaf = 0;
12
- let lastTargetKey = "";
13
+ const LERP = 0.18;
14
+ const SETTLE_PX = 0.5;
15
+ const MIN_DELTA_PX = 4;
13
16
  const leaderPayload = computed(() => {
14
17
  const id = leaderId.value;
15
18
  if (id == null) return null;
@@ -23,43 +26,74 @@ export function useAAFollowAnchor(leaderId) {
23
26
  });
24
27
  }
25
28
  }, { immediate: true });
26
- function scrollToLeader() {
27
- cancelAnimationFrame(scrollRaf);
28
- scrollRaf = requestAnimationFrame(() => {
29
- const payload = leaderPayload.value;
30
- if (!payload) return;
31
- if (payload.route !== route.path) return;
32
- const instructions = [];
33
- if (payload.scrollers && payload.scrollers.length) {
34
- for (const s of payload.scrollers) {
35
- instructions.push({ container: s.containerPath, element: s.elementPath, ratio: s.ratio });
29
+ function ensureRaf() {
30
+ if (scrollRaf) return;
31
+ const tick = () => {
32
+ let stillMoving = false;
33
+ for (const [scroller, s] of scrollState) {
34
+ const delta = s.target - s.current;
35
+ if (Math.abs(delta) < SETTLE_PX) {
36
+ if (scroller === window) window.scrollTo({ top: s.target, behavior: "auto" });
37
+ else scroller.scrollTo({ top: s.target, behavior: "auto" });
38
+ scrollState.delete(scroller);
39
+ continue;
36
40
  }
37
- } else if (payload.kind === "anchor" && payload.anchor) {
38
- instructions.push({ element: payload.anchor.elementPath, ratio: payload.anchorRatio ?? 0.05 });
39
- } else if (payload.kind === "selection" && payload.start) {
40
- instructions.push({ element: payload.start.elementPath, ratio: 0.2 });
41
+ stillMoving = true;
42
+ s.current += delta * LERP;
43
+ if (scroller === window) window.scrollTo({ top: s.current, behavior: "auto" });
44
+ else scroller.scrollTo({ top: s.current, behavior: "auto" });
41
45
  }
42
- const fingerprint = [];
43
- for (const ins of instructions) {
44
- const target = elementFromPath(ins.element);
45
- if (!target || !target.isConnected) continue;
46
- const scroller = ins.container ? elementFromPath(ins.container) ?? window : window;
47
- const isWindow = scroller === window;
48
- const scrollerRect = isWindow ? { top: 0, height: window.innerHeight || document.documentElement.clientHeight } : scroller.getBoundingClientRect();
49
- const desiredTop = scrollerRect.top + scrollerRect.height * Math.max(0, Math.min(0.9, ins.ratio));
50
- const rect = target.getBoundingClientRect();
51
- const delta = rect.top - desiredTop;
52
- fingerprint.push(`${isWindow ? "w" : ins.container?.join(",")}-${Math.round(rect.top)}-${ins.ratio.toFixed(3)}`);
53
- if (Math.abs(delta) < 8) continue;
54
- if (isWindow) window.scrollBy({ top: delta, behavior: "smooth" });
55
- else scroller.scrollBy({ top: delta, behavior: "smooth" });
46
+ scrollRaf = stillMoving ? requestAnimationFrame(tick) : 0;
47
+ };
48
+ scrollRaf = requestAnimationFrame(tick);
49
+ }
50
+ function scrollToLeader() {
51
+ const payload = leaderPayload.value;
52
+ if (!payload) return;
53
+ if (payload.route !== route.path) return;
54
+ const instructions = [];
55
+ if (payload.scrollers && payload.scrollers.length) {
56
+ for (const s of payload.scrollers) {
57
+ instructions.push({ container: s.containerPath, element: s.elementPath, ratio: s.ratio });
56
58
  }
57
- const key = fingerprint.join("|");
58
- if (key === lastTargetKey) return;
59
- lastTargetKey = key;
60
- });
59
+ } else if (payload.kind === "anchor" && payload.anchor) {
60
+ instructions.push({ element: payload.anchor.elementPath, ratio: payload.anchorRatio ?? 0.05 });
61
+ } else if (payload.kind === "selection" && payload.start) {
62
+ instructions.push({ element: payload.start.elementPath, ratio: 0.2 });
63
+ }
64
+ let targetsChanged = false;
65
+ for (const ins of instructions) {
66
+ const target = elementFromPath(ins.element);
67
+ if (!target || !target.isConnected) continue;
68
+ const scroller = ins.container ? elementFromPath(ins.container) ?? window : window;
69
+ const isWindow = scroller === window;
70
+ const scrollerRect = isWindow ? { top: 0, height: window.innerHeight || document.documentElement.clientHeight } : scroller.getBoundingClientRect();
71
+ const desiredTop = scrollerRect.top + scrollerRect.height * Math.max(0, Math.min(0.9, ins.ratio));
72
+ const rect = target.getBoundingClientRect();
73
+ const delta = rect.top - desiredTop;
74
+ if (Math.abs(delta) < MIN_DELTA_PX) continue;
75
+ const currentScroll = isWindow ? window.scrollY : scroller.scrollTop;
76
+ const targetScroll = currentScroll + delta;
77
+ const existing = scrollState.get(scroller);
78
+ if (existing) {
79
+ existing.current = currentScroll;
80
+ if (Math.abs(existing.target - targetScroll) >= 0.5) {
81
+ existing.target = targetScroll;
82
+ targetsChanged = true;
83
+ }
84
+ } else {
85
+ scrollState.set(scroller, { current: currentScroll, target: targetScroll });
86
+ targetsChanged = true;
87
+ }
88
+ }
89
+ if (targetsChanged) ensureRaf();
61
90
  }
62
91
  watch(leaderPayload, scrollToLeader, { deep: true, flush: "post" });
92
+ onScopeDispose(() => {
93
+ if (scrollRaf) cancelAnimationFrame(scrollRaf);
94
+ scrollRaf = 0;
95
+ scrollState.clear();
96
+ });
63
97
  const leader = computed(() => {
64
98
  const id = leaderId.value;
65
99
  if (id == null) return null;
@@ -19,11 +19,14 @@ export interface UseAAFollowPeerOptions {
19
19
  onBreak?: () => void;
20
20
  }
21
21
  export declare function useAAFollowPeer(options?: UseAAFollowPeerOptions): {
22
- follow: (clientId: number) => void;
22
+ follow: (clientIdOrPublicKey: number | string) => void;
23
23
  unfollow: () => void;
24
- followingClientId: import("vue").Ref<number | null, number | null>;
24
+ followingClientId: import("vue").ComputedRef<number | null>;
25
+ followingPublicKey: import("vue").ComputedRef<string | null>;
25
26
  followingUser: import("vue").ComputedRef<{
26
- name?: string;
27
- color?: string;
27
+ name?: string | undefined;
28
+ color?: string | undefined;
29
+ publicKey?: string | undefined;
28
30
  } | null>;
31
+ isFollowing: import("vue").ComputedRef<boolean>;
29
32
  };
@@ -2,17 +2,42 @@ import { computed, onScopeDispose, ref, watch } from "vue";
2
2
  import { useAbracadabra } from "./useAbracadabra.js";
3
3
  import { useSyncedMap } from "./useYDoc.js";
4
4
  import { useAwareness } from "./useAwareness.js";
5
- const _followingClientId = ref(null);
5
+ const _followingPublicKey = ref(null);
6
+ const _followingClientIdFallback = ref(null);
6
7
  export function useAAFollowPeer(options = {}) {
7
8
  const { doc, provider } = useAbracadabra();
8
9
  const { data, set } = useSyncedMap(doc, "aware-ui");
9
10
  const { states } = useAwareness();
10
- const followingClientId = _followingClientId;
11
11
  let unsubInteraction = null;
12
+ const followingClientId = computed(() => {
13
+ const pk = _followingPublicKey.value;
14
+ if (pk) {
15
+ for (const [cid, st] of states.value.entries()) {
16
+ const peerPk = st?.user?.publicKey;
17
+ if (peerPk === pk) return cid;
18
+ }
19
+ return null;
20
+ }
21
+ return _followingClientIdFallback.value;
22
+ });
23
+ const isFollowing = computed(
24
+ () => _followingPublicKey.value != null || _followingClientIdFallback.value != null
25
+ );
26
+ const followingPublicKey = computed(() => _followingPublicKey.value);
27
+ const followingUserCache = ref(null);
12
28
  const followingUser = computed(() => {
13
- if (followingClientId.value == null) return null;
14
- const state = states.value.get(followingClientId.value);
15
- return state?.user ?? null;
29
+ const cid = followingClientId.value;
30
+ if (cid != null) {
31
+ const state = states.value.get(cid);
32
+ if (state?.user) return state.user;
33
+ }
34
+ return followingUserCache.value;
35
+ });
36
+ watch(followingUser, (user) => {
37
+ if (user) followingUserCache.value = { ...user };
38
+ }, { flush: "post" });
39
+ watch(isFollowing, (active) => {
40
+ if (!active) followingUserCache.value = null;
16
41
  });
17
42
  function detachInteraction() {
18
43
  unsubInteraction?.();
@@ -31,14 +56,31 @@ export function useAAFollowPeer(options = {}) {
31
56
  window.removeEventListener("wheel", onBreak);
32
57
  };
33
58
  }
34
- function follow(clientId) {
35
- if (followingClientId.value === clientId) return;
36
- followingClientId.value = clientId;
59
+ function follow(clientIdOrPublicKey) {
60
+ if (typeof clientIdOrPublicKey === "string") {
61
+ if (_followingPublicKey.value === clientIdOrPublicKey) return;
62
+ _followingPublicKey.value = clientIdOrPublicKey;
63
+ _followingClientIdFallback.value = null;
64
+ } else {
65
+ const cid = clientIdOrPublicKey;
66
+ const state = states.value.get(cid);
67
+ const pk = state?.user?.publicKey;
68
+ if (pk) {
69
+ if (_followingPublicKey.value === pk) return;
70
+ _followingPublicKey.value = pk;
71
+ _followingClientIdFallback.value = null;
72
+ } else {
73
+ if (_followingClientIdFallback.value === cid) return;
74
+ _followingPublicKey.value = null;
75
+ _followingClientIdFallback.value = cid;
76
+ }
77
+ }
37
78
  attachInteractionBreaker();
38
79
  }
39
80
  function unfollow() {
40
- if (followingClientId.value == null) return;
41
- followingClientId.value = null;
81
+ if (_followingPublicKey.value == null && _followingClientIdFallback.value == null) return;
82
+ _followingPublicKey.value = null;
83
+ _followingClientIdFallback.value = null;
42
84
  detachInteraction();
43
85
  options.onBreak?.();
44
86
  }
@@ -71,5 +113,12 @@ export function useAAFollowPeer(options = {}) {
71
113
  }
72
114
  });
73
115
  onScopeDispose(detachInteraction);
74
- return { follow, unfollow, followingClientId, followingUser };
116
+ return {
117
+ follow,
118
+ unfollow,
119
+ followingClientId,
120
+ followingPublicKey,
121
+ followingUser,
122
+ isFollowing
123
+ };
75
124
  }
@@ -6,7 +6,7 @@ import type { MaybeRefOrGetter } from 'vue';
6
6
  * scoped container as `peer:viewport:<scope>` ephemeral state.
7
7
  *
8
8
  * Use case: render "Janis is reading the API section" overlays in long docs.
9
- * Pair with `<APresenceCursors>` for a complete spatial-presence layer.
9
+ * Pair with `<APresenceCursors>` for a complete presence layer.
10
10
  */
11
11
  export interface PeerViewport {
12
12
  clientId: number;
@@ -21,6 +21,8 @@ export declare function useAbracadabraAuth(): {
21
21
  confirmPasswordReset: any;
22
22
  changePassword: any;
23
23
  setPassword: any;
24
+ hasPassword: any;
25
+ refreshHasPassword: any;
24
26
  recoverIdentity: any;
25
27
  unblockConnection: any;
26
28
  setUserName: any;
@@ -24,6 +24,8 @@ export function useAbracadabraAuth() {
24
24
  confirmPasswordReset: abra.confirmPasswordReset,
25
25
  changePassword: abra.changePassword,
26
26
  setPassword: abra.setPassword,
27
+ hasPassword: abra.hasPassword,
28
+ refreshHasPassword: abra.refreshHasPassword,
27
29
  recoverIdentity: abra.recoverIdentity,
28
30
  unblockConnection: abra.unblockConnection,
29
31
  setUserName: abra.setUserName,
@@ -72,7 +72,8 @@ export function useEditorSuggestions(options = {}) {
72
72
  ...extEnabled("colorSwatch") ? [{ kind: "colorSwatch", label: "Color Swatch", icon: "i-lucide-palette", description: "Inline color chip with picker", keywords: ["color", "swatch", "token", "palette"] }] : [],
73
73
  ...extEnabled("mathBlock") ? [{ kind: "mathBlock", label: "Math (block)", icon: "i-lucide-square-sigma", description: "Display-mode LaTeX equation", keywords: ["katex", "latex", "equation", "formula", "math"] }] : [],
74
74
  ...extEnabled("mathInline") ? [{ kind: "mathInline", label: "Math (inline)", icon: "i-lucide-sigma", description: "Inline LaTeX expression", keywords: ["katex", "latex", "inline math"] }] : [],
75
- ...extEnabled("diff") ? [{ kind: "diff", label: "Diff", icon: "i-lucide-git-compare", description: "Side-by-side text diff", keywords: ["compare", "changes", "git"] }] : []
75
+ ...extEnabled("diff") ? [{ kind: "diff", label: "Diff", icon: "i-lucide-git-compare", description: "Side-by-side text diff", keywords: ["compare", "changes", "git"] }] : [],
76
+ ...extEnabled("svgEmbed") ? [{ kind: "svgEmbed", label: "SVG embed", icon: "i-lucide-image", description: "Inline SVG (sanitized)", keywords: ["svg", "vector", "icon", "diagram"] }] : []
76
77
  ]
77
78
  ];
78
79
  try {
@@ -1,33 +1,47 @@
1
- /**
2
- * useEmailVerification
3
- *
4
- * Wraps the email-verification half of the auth lifecycle.
5
- * - `request()` triggers a verification email (rate-limited 1/min, 10/day).
6
- * - `confirm(token)` consumes the token from the email link; no auth needed.
7
- *
8
- * `verified` mirrors the most recent local result of `confirm()` — the
9
- * server doesn't currently expose `email_verified_at` on `getMe()`, so
10
- * the canonical signal is "did `confirm()` succeed in this session".
11
- *
12
- * Usage:
13
- * const { verified, isRequesting, isConfirming, error, request, confirm } = useEmailVerification()
14
- */
15
- declare function request(): Promise<void>;
16
- declare function confirm(token: string): Promise<void>;
1
+ /** Known error codes raised by `useEmailVerification()` actions. */
2
+ export type EmailVerificationErrorCode = 'rate_limited' | 'token_expired' | 'token_invalid' | 'verification_disabled' | 'already_verified' | 'email_not_set' | 'unauthorized' | 'forbidden' | 'network_error' | 'server_error' | 'unknown';
3
+ export interface EmailVerificationError {
4
+ code: EmailVerificationErrorCode;
5
+ message: string;
6
+ status?: number;
7
+ retryAfterSeconds?: number;
8
+ }
9
+ export type EmailVerificationAction = 'request' | 'confirm';
10
+ export interface EmailVerificationResult {
11
+ ok: boolean;
12
+ error?: EmailVerificationError;
13
+ }
14
+ declare function classifyError(err: unknown): EmailVerificationError;
17
15
  export declare function useEmailVerification(): {
18
- /** True after a successful `confirm()` in the current session. */
16
+ /** True after a successful `confirm()` in this session. */
19
17
  verified: import("vue").Ref<boolean, boolean>;
20
- /** Whether a `request()` is in flight. */
21
- isRequesting: import("vue").Ref<boolean, boolean>;
22
- /** Whether a `confirm()` is in flight. */
23
- isConfirming: import("vue").Ref<boolean, boolean>;
24
- /** Last error message, or null. */
25
- error: import("vue").Ref<string | null, string | null>;
26
18
  /** Epoch ms of the last successful `request()`, or null. */
27
19
  lastSentAt: import("vue").Ref<number | null, number | null>;
28
- /** Send a verification email to the current user. Requires auth. */
29
- request: typeof request;
20
+ /** Currently-running action, or null. */
21
+ busy: import("vue").Ref<EmailVerificationAction | null, EmailVerificationAction | null>;
22
+ /** Most recent typed error, or null. Cleared at the start of every action. */
23
+ error: import("vue").Ref<{
24
+ code: EmailVerificationErrorCode;
25
+ message: string;
26
+ status?: number | undefined;
27
+ retryAfterSeconds?: number | undefined;
28
+ } | null, EmailVerificationError | {
29
+ code: EmailVerificationErrorCode;
30
+ message: string;
31
+ status?: number | undefined;
32
+ retryAfterSeconds?: number | undefined;
33
+ } | null>;
34
+ /** Most recent successful action, or null. */
35
+ lastSuccess: import("vue").Ref<EmailVerificationAction | null, EmailVerificationAction | null>;
36
+ /** Wipe `error` without re-running anything. */
37
+ clearError: () => void;
38
+ /** Send a verification email to the current user. Auth required. */
39
+ request: () => Promise<EmailVerificationResult>;
30
40
  /** Confirm a verification token from the email link. No auth required. */
31
- confirm: typeof confirm;
41
+ confirm: (opts: {
42
+ token: string;
43
+ }) => Promise<EmailVerificationResult>;
44
+ /** Internal helper exposed for testing. */
45
+ _classifyError: typeof classifyError;
32
46
  };
33
47
  export {};