@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
@@ -0,0 +1,113 @@
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
+ sent: { type: Boolean, required: false, default: false },
7
+ error: { type: [String, null], required: false },
8
+ title: { type: String, required: false, default: "Reset your password" },
9
+ subtitle: { type: String, required: false, default: "Enter your username or email and we'll send you a reset link." },
10
+ showSignInLink: { type: Boolean, required: false, default: true }
11
+ });
12
+ const emit = defineEmits(["update:open", "submit", "signin"]);
13
+ const open = computed({
14
+ get: () => !!props.open,
15
+ set: (v) => emit("update:open", v)
16
+ });
17
+ const identifier = ref("");
18
+ watch(() => props.open, (v) => {
19
+ if (!v) return;
20
+ identifier.value = "";
21
+ });
22
+ const canSubmit = computed(
23
+ () => !!identifier.value.trim() && !props.loading && !props.sent
24
+ );
25
+ function submit() {
26
+ if (!canSubmit.value) return;
27
+ emit("submit", { identifier: identifier.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="sent"
41
+ color="success"
42
+ variant="soft"
43
+ icon="i-lucide-mail-check"
44
+ title="Check your inbox"
45
+ description="If an account matches that identifier, we've sent a password reset link. The link expires shortly — use it soon."
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)">Username or email</label>
63
+ <UInput
64
+ v-model="identifier"
65
+ size="md"
66
+ autofocus
67
+ autocomplete="username"
68
+ placeholder="your-handle or you@example.com"
69
+ icon="i-lucide-mail-question"
70
+ @keydown.enter="submit"
71
+ />
72
+ </div>
73
+
74
+ <div
75
+ v-if="showSignInLink"
76
+ class="-mt-1 text-xs"
77
+ >
78
+ <UButton
79
+ variant="link"
80
+ color="neutral"
81
+ size="xs"
82
+ label="Back to sign in"
83
+ @click="emit('signin')"
84
+ />
85
+ </div>
86
+ </template>
87
+ </div>
88
+ </template>
89
+
90
+ <template #footer>
91
+ <div class="flex items-center justify-end gap-2 w-full">
92
+ <UButton
93
+ variant="ghost"
94
+ color="neutral"
95
+ size="sm"
96
+ :label="sent ? 'Close' : 'Cancel'"
97
+ :disabled="loading"
98
+ @click="open = false"
99
+ />
100
+ <UButton
101
+ v-if="!sent"
102
+ color="primary"
103
+ size="sm"
104
+ icon="i-lucide-mail"
105
+ label="Send reset link"
106
+ :loading="loading"
107
+ :disabled="!canSubmit"
108
+ @click="submit"
109
+ />
110
+ </div>
111
+ </template>
112
+ </UModal>
113
+ </template>
@@ -0,0 +1,35 @@
1
+ type __VLS_Props = {
2
+ open?: boolean;
3
+ loading?: boolean;
4
+ /**
5
+ * Flip to true after the consumer's submit handler resolves; flips the modal to the
6
+ * "we sent an email if that account exists" confirmation copy.
7
+ */
8
+ sent?: boolean;
9
+ error?: string | null;
10
+ title?: string;
11
+ subtitle?: string;
12
+ /** Show the "Back to sign in" link in the footer. */
13
+ showSignInLink?: boolean;
14
+ };
15
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ submit: (payload: {
17
+ identifier: string;
18
+ }) => any;
19
+ "update:open": (v: boolean) => any;
20
+ signin: () => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onSubmit?: ((payload: {
23
+ identifier: string;
24
+ }) => any) | undefined;
25
+ "onUpdate:open"?: ((v: boolean) => any) | undefined;
26
+ onSignin?: (() => any) | undefined;
27
+ }>, {
28
+ subtitle: string;
29
+ title: string;
30
+ loading: boolean;
31
+ sent: boolean;
32
+ showSignInLink: boolean;
33
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
+ declare const _default: typeof __VLS_export;
35
+ export default _default;
@@ -0,0 +1,26 @@
1
+ type __VLS_Props = {
2
+ loading?: boolean;
3
+ error?: string | null;
4
+ /** Flip to true after a successful set — card shows a success state. */
5
+ success?: boolean;
6
+ title?: string;
7
+ subtitle?: string;
8
+ minLength?: number;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ submit: (payload: {
12
+ password: string;
13
+ }) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onSubmit?: ((payload: {
16
+ password: string;
17
+ }) => any) | undefined;
18
+ }>, {
19
+ subtitle: string;
20
+ title: string;
21
+ success: boolean;
22
+ minLength: number;
23
+ loading: boolean;
24
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;
@@ -0,0 +1,139 @@
1
+ <script setup>
2
+ import { computed, ref, watch } from "vue";
3
+ const props = defineProps({
4
+ loading: { type: Boolean, required: false, default: false },
5
+ error: { type: [String, null], required: false },
6
+ success: { type: Boolean, required: false, default: false },
7
+ title: { type: String, required: false, default: "Add a password" },
8
+ subtitle: { type: String, required: false, default: "Your account currently relies on this device's passkey. Adding a password lets you sign in from a second device or recover access if this device is lost." },
9
+ minLength: { type: Number, required: false, default: 8 }
10
+ });
11
+ const emit = defineEmits(["submit"]);
12
+ const password = ref("");
13
+ const confirm = ref("");
14
+ const showPassword = ref(false);
15
+ watch(() => props.success, (v) => {
16
+ if (!v) return;
17
+ password.value = "";
18
+ confirm.value = "";
19
+ showPassword.value = false;
20
+ });
21
+ const passwordTooShort = computed(
22
+ () => password.value.length > 0 && password.value.length < props.minLength
23
+ );
24
+ const confirmMismatch = computed(
25
+ () => confirm.value.length > 0 && confirm.value !== password.value
26
+ );
27
+ const canSubmit = computed(
28
+ () => password.value.length >= props.minLength && confirm.value === password.value && !props.loading && !props.success
29
+ );
30
+ function submit() {
31
+ if (!canSubmit.value) return;
32
+ emit("submit", { password: password.value });
33
+ }
34
+ </script>
35
+
36
+ <template>
37
+ <div class="set-pw-card">
38
+ <div class="set-pw-head">
39
+ <UIcon
40
+ name="i-lucide-key-square"
41
+ class="size-5 text-(--ui-primary) shrink-0"
42
+ />
43
+ <div class="flex-1">
44
+ <h4 class="set-pw-title">
45
+ {{ title }}
46
+ </h4>
47
+ <p class="set-pw-sub">
48
+ {{ subtitle }}
49
+ </p>
50
+ </div>
51
+ </div>
52
+
53
+ <UAlert
54
+ v-if="success"
55
+ color="success"
56
+ variant="soft"
57
+ icon="i-lucide-check"
58
+ title="Password set"
59
+ description="You can now sign in with your username and password from another device."
60
+ />
61
+
62
+ <UAlert
63
+ v-else-if="error"
64
+ color="error"
65
+ variant="soft"
66
+ icon="i-lucide-triangle-alert"
67
+ :description="error"
68
+ />
69
+
70
+ <template v-if="!success">
71
+ <div class="set-pw-field">
72
+ <label class="set-pw-label">New password</label>
73
+ <UInput
74
+ v-model="password"
75
+ :type="showPassword ? 'text' : 'password'"
76
+ size="md"
77
+ autocomplete="new-password"
78
+ :placeholder="`${minLength}+ characters`"
79
+ icon="i-lucide-lock"
80
+ :ui="{ trailing: 'pe-1' }"
81
+ @keydown.enter="submit"
82
+ >
83
+ <template #trailing>
84
+ <UButton
85
+ tabindex="-1"
86
+ variant="ghost"
87
+ color="neutral"
88
+ size="xs"
89
+ :icon="showPassword ? 'i-lucide-eye-off' : 'i-lucide-eye'"
90
+ :aria-label="showPassword ? 'Hide password' : 'Show password'"
91
+ @click="showPassword = !showPassword"
92
+ />
93
+ </template>
94
+ </UInput>
95
+ <p
96
+ v-if="passwordTooShort"
97
+ class="set-pw-err"
98
+ >
99
+ At least {{ minLength }} characters.
100
+ </p>
101
+ </div>
102
+
103
+ <div class="set-pw-field">
104
+ <label class="set-pw-label">Confirm</label>
105
+ <UInput
106
+ v-model="confirm"
107
+ :type="showPassword ? 'text' : 'password'"
108
+ size="md"
109
+ autocomplete="new-password"
110
+ placeholder="Type it again"
111
+ icon="i-lucide-shield-check"
112
+ @keydown.enter="submit"
113
+ />
114
+ <p
115
+ v-if="confirmMismatch"
116
+ class="set-pw-err"
117
+ >
118
+ Passwords don't match.
119
+ </p>
120
+ </div>
121
+
122
+ <div class="set-pw-actions">
123
+ <UButton
124
+ color="primary"
125
+ size="sm"
126
+ icon="i-lucide-key-round"
127
+ label="Set password"
128
+ :loading="loading"
129
+ :disabled="!canSubmit"
130
+ @click="submit"
131
+ />
132
+ </div>
133
+ </template>
134
+ </div>
135
+ </template>
136
+
137
+ <style scoped>
138
+ .set-pw-card{background:var(--ui-bg);border:1px solid var(--ui-border);border-radius:.6rem;display:flex;flex-direction:column;gap:.75rem;padding:1rem}.set-pw-head{align-items:flex-start;display:flex;gap:.6rem}.set-pw-title{color:var(--ui-text-highlighted);font-size:.875rem;font-weight:600;margin:0}.set-pw-sub{color:var(--ui-text-muted);font-size:.8125rem;line-height:1.5;margin:.2rem 0 0}.set-pw-field{display:flex;flex-direction:column;gap:.25rem}.set-pw-label{color:var(--ui-text-muted);font-size:.7rem;font-weight:500}.set-pw-err{color:var(--ui-color-error-500);font-size:.7rem;margin:0}.set-pw-actions{display:flex;justify-content:flex-end;padding-top:.25rem}
139
+ </style>
@@ -0,0 +1,26 @@
1
+ type __VLS_Props = {
2
+ loading?: boolean;
3
+ error?: string | null;
4
+ /** Flip to true after a successful set — card shows a success state. */
5
+ success?: boolean;
6
+ title?: string;
7
+ subtitle?: string;
8
+ minLength?: number;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ submit: (payload: {
12
+ password: string;
13
+ }) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onSubmit?: ((payload: {
16
+ password: string;
17
+ }) => any) | undefined;
18
+ }>, {
19
+ subtitle: string;
20
+ title: string;
21
+ success: boolean;
22
+ minLength: number;
23
+ loading: boolean;
24
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;
@@ -0,0 +1,66 @@
1
+ export interface SubPageListEntry {
2
+ id: string;
3
+ label: string;
4
+ type?: string;
5
+ meta?: {
6
+ icon?: string;
7
+ color?: string;
8
+ [k: string]: unknown;
9
+ };
10
+ }
11
+ type __VLS_Props = {
12
+ /** Parent document whose direct children are listed. */
13
+ parentDocId: string;
14
+ /** Layout. `list` = vertical rows, `grid` = card grid, `compact` = minimal pills. Default: 'list'. */
15
+ layout?: 'list' | 'grid' | 'compact';
16
+ /** Maximum number of items shown. Omit for all. */
17
+ limit?: number;
18
+ /** Empty-state text. */
19
+ emptyText?: string;
20
+ /** Show a "New …" button at the bottom of the list. Emits `create`. */
21
+ showCreate?: boolean;
22
+ /** Label for the create button. */
23
+ createLabel?: string;
24
+ };
25
+ declare var __VLS_1: {}, __VLS_8: {
26
+ entry: SubPageListEntry;
27
+ }, __VLS_15: {
28
+ entry: SubPageListEntry;
29
+ }, __VLS_17: {
30
+ key: string;
31
+ entry: SubPageListEntry;
32
+ }, __VLS_24: {
33
+ key: string;
34
+ entry: SubPageListEntry;
35
+ };
36
+ type __VLS_Slots = {} & {
37
+ empty?: (props: typeof __VLS_1) => any;
38
+ } & {
39
+ item?: (props: typeof __VLS_8) => any;
40
+ } & {
41
+ 'item-trailing'?: (props: typeof __VLS_15) => any;
42
+ } & {
43
+ item?: (props: typeof __VLS_17) => any;
44
+ } & {
45
+ item?: (props: typeof __VLS_24) => any;
46
+ };
47
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
48
+ open: (id: string, label: string) => any;
49
+ create: () => any;
50
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
51
+ onOpen?: ((id: string, label: string) => any) | undefined;
52
+ onCreate?: (() => any) | undefined;
53
+ }>, {
54
+ layout: "list" | "grid" | "compact";
55
+ emptyText: string;
56
+ showCreate: boolean;
57
+ createLabel: string;
58
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
59
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
60
+ declare const _default: typeof __VLS_export;
61
+ export default _default;
62
+ type __VLS_WithSlots<T, S> = T & {
63
+ new (): {
64
+ $slots: S;
65
+ };
66
+ };
@@ -0,0 +1,147 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { useAbracadabra } from "../composables/useAbracadabra";
4
+ import { useChildTree } from "../composables/useChildTree";
5
+ const props = defineProps({
6
+ parentDocId: { type: String, required: true },
7
+ layout: { type: String, required: false, default: "list" },
8
+ limit: { type: Number, required: false },
9
+ emptyText: { type: String, required: false, default: "No child documents yet" },
10
+ showCreate: { type: Boolean, required: false, default: false },
11
+ createLabel: { type: String, required: false, default: "New page" }
12
+ });
13
+ const emit = defineEmits(["open", "create"]);
14
+ const { doc: rootDoc } = useAbracadabra();
15
+ const tree = useChildTree(rootDoc, props.parentDocId);
16
+ const entries = computed(() => {
17
+ const all = tree.childrenOf(null);
18
+ return props.limit !== void 0 ? all.slice(0, props.limit) : all;
19
+ });
20
+ function defaultIcon(entry) {
21
+ return entry.meta?.icon ?? "i-lucide-file-text";
22
+ }
23
+ </script>
24
+
25
+ <template>
26
+ <div class="a-subpage-list">
27
+ <!-- Empty state -->
28
+ <div
29
+ v-if="!entries.length"
30
+ class="a-subpage-list__empty"
31
+ >
32
+ <slot name="empty">
33
+ <UIcon
34
+ name="i-lucide-folder-open"
35
+ class="size-6 text-(--ui-text-dimmed) mb-2"
36
+ />
37
+ <p class="text-sm text-(--ui-text-muted)">
38
+ {{ emptyText }}
39
+ </p>
40
+ </slot>
41
+ </div>
42
+
43
+ <!-- list -->
44
+ <ul
45
+ v-else-if="layout === 'list'"
46
+ class="flex flex-col gap-0.5"
47
+ >
48
+ <li
49
+ v-for="entry in entries"
50
+ :key="entry.id"
51
+ >
52
+ <slot
53
+ name="item"
54
+ :entry="entry"
55
+ >
56
+ <button
57
+ class="w-full flex items-center gap-2 px-2.5 py-1.5 rounded-md text-sm text-(--ui-text) hover:bg-(--ui-bg-elevated) transition-colors text-left"
58
+ type="button"
59
+ @click="emit('open', entry.id, entry.label)"
60
+ >
61
+ <UIcon
62
+ :name="defaultIcon(entry)"
63
+ class="size-4 shrink-0"
64
+ :style="entry.meta?.color ? `color: ${entry.meta.color}` : ''"
65
+ :class="entry.meta?.color ? '' : 'text-(--ui-text-muted)'"
66
+ />
67
+ <span class="flex-1 truncate">{{ entry.label || "Untitled" }}</span>
68
+ <slot
69
+ name="item-trailing"
70
+ :entry="entry"
71
+ />
72
+ </button>
73
+ </slot>
74
+ </li>
75
+ </ul>
76
+
77
+ <!-- grid -->
78
+ <div
79
+ v-else-if="layout === 'grid'"
80
+ class="grid grid-cols-2 sm:grid-cols-3 gap-2"
81
+ >
82
+ <slot
83
+ v-for="entry in entries"
84
+ :key="entry.id"
85
+ name="item"
86
+ :entry="entry"
87
+ >
88
+ <button
89
+ class="flex flex-col gap-2 p-3 rounded-md border border-(--ui-border) bg-(--ui-bg) hover:border-(--ui-border-accented) hover:bg-(--ui-bg-elevated) transition-colors text-left"
90
+ type="button"
91
+ @click="emit('open', entry.id, entry.label)"
92
+ >
93
+ <UIcon
94
+ :name="defaultIcon(entry)"
95
+ class="size-5"
96
+ :style="entry.meta?.color ? `color: ${entry.meta.color}` : ''"
97
+ :class="entry.meta?.color ? '' : 'text-(--ui-text-muted)'"
98
+ />
99
+ <span class="text-sm font-medium text-(--ui-text-highlighted) line-clamp-2">{{ entry.label || "Untitled" }}</span>
100
+ </button>
101
+ </slot>
102
+ </div>
103
+
104
+ <!-- compact -->
105
+ <div
106
+ v-else-if="layout === 'compact'"
107
+ class="flex flex-wrap gap-1.5"
108
+ >
109
+ <slot
110
+ v-for="entry in entries"
111
+ :key="entry.id"
112
+ name="item"
113
+ :entry="entry"
114
+ >
115
+ <button
116
+ class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full border border-(--ui-border) bg-(--ui-bg) text-xs hover:border-(--ui-border-accented) hover:bg-(--ui-bg-elevated) transition-colors"
117
+ type="button"
118
+ @click="emit('open', entry.id, entry.label)"
119
+ >
120
+ <UIcon
121
+ :name="defaultIcon(entry)"
122
+ class="size-3"
123
+ :style="entry.meta?.color ? `color: ${entry.meta.color}` : ''"
124
+ :class="entry.meta?.color ? '' : 'text-(--ui-text-muted)'"
125
+ />
126
+ <span class="text-(--ui-text)">{{ entry.label || "Untitled" }}</span>
127
+ </button>
128
+ </slot>
129
+ </div>
130
+
131
+ <!-- create -->
132
+ <div
133
+ v-if="showCreate"
134
+ class="mt-2"
135
+ >
136
+ <UButton
137
+ :icon="layout === 'grid' ? 'i-lucide-plus-square' : 'i-lucide-plus'"
138
+ :label="createLabel"
139
+ size="sm"
140
+ variant="ghost"
141
+ color="neutral"
142
+ :block="layout === 'grid'"
143
+ @click="emit('create')"
144
+ />
145
+ </div>
146
+ </div>
147
+ </template>
@@ -0,0 +1,66 @@
1
+ export interface SubPageListEntry {
2
+ id: string;
3
+ label: string;
4
+ type?: string;
5
+ meta?: {
6
+ icon?: string;
7
+ color?: string;
8
+ [k: string]: unknown;
9
+ };
10
+ }
11
+ type __VLS_Props = {
12
+ /** Parent document whose direct children are listed. */
13
+ parentDocId: string;
14
+ /** Layout. `list` = vertical rows, `grid` = card grid, `compact` = minimal pills. Default: 'list'. */
15
+ layout?: 'list' | 'grid' | 'compact';
16
+ /** Maximum number of items shown. Omit for all. */
17
+ limit?: number;
18
+ /** Empty-state text. */
19
+ emptyText?: string;
20
+ /** Show a "New …" button at the bottom of the list. Emits `create`. */
21
+ showCreate?: boolean;
22
+ /** Label for the create button. */
23
+ createLabel?: string;
24
+ };
25
+ declare var __VLS_1: {}, __VLS_8: {
26
+ entry: SubPageListEntry;
27
+ }, __VLS_15: {
28
+ entry: SubPageListEntry;
29
+ }, __VLS_17: {
30
+ key: string;
31
+ entry: SubPageListEntry;
32
+ }, __VLS_24: {
33
+ key: string;
34
+ entry: SubPageListEntry;
35
+ };
36
+ type __VLS_Slots = {} & {
37
+ empty?: (props: typeof __VLS_1) => any;
38
+ } & {
39
+ item?: (props: typeof __VLS_8) => any;
40
+ } & {
41
+ 'item-trailing'?: (props: typeof __VLS_15) => any;
42
+ } & {
43
+ item?: (props: typeof __VLS_17) => any;
44
+ } & {
45
+ item?: (props: typeof __VLS_24) => any;
46
+ };
47
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
48
+ open: (id: string, label: string) => any;
49
+ create: () => any;
50
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
51
+ onOpen?: ((id: string, label: string) => any) | undefined;
52
+ onCreate?: (() => any) | undefined;
53
+ }>, {
54
+ layout: "list" | "grid" | "compact";
55
+ emptyText: string;
56
+ showCreate: boolean;
57
+ createLabel: string;
58
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
59
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
60
+ declare const _default: typeof __VLS_export;
61
+ export default _default;
62
+ type __VLS_WithSlots<T, S> = T & {
63
+ new (): {
64
+ $slots: S;
65
+ };
66
+ };
@@ -2,6 +2,7 @@ type __VLS_Props = {
2
2
  fieldKey: string;
3
3
  awareness?: boolean;
4
4
  live?: boolean;
5
+ followOnly?: boolean;
5
6
  total?: boolean;
6
7
  };
7
8
  declare var __VLS_9: string, __VLS_10: any, __VLS_27: string, __VLS_28: any;
@@ -13,6 +14,7 @@ type __VLS_Slots = {} & {
13
14
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
14
15
  awareness: boolean;
15
16
  live: boolean;
17
+ followOnly: boolean;
16
18
  total: boolean;
17
19
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
20
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -2,6 +2,7 @@
2
2
  import { computed, ref, useAttrs, watch } from "vue";
3
3
  import { useAAField } from "../../composables/useAAField";
4
4
  import { useAAUIState } from "../../composables/useAAUIState";
5
+ import { useAAFollowPeer } from "../../composables/useAAFollowPeer";
5
6
  import AHoverItem from "./AHoverItem.vue";
6
7
  function itemId(item) {
7
8
  if (item == null) return "";
@@ -18,10 +19,12 @@ const props = defineProps({
18
19
  fieldKey: { type: String, required: true },
19
20
  awareness: { type: Boolean, required: false, default: true },
20
21
  live: { type: Boolean, required: false, default: false },
22
+ followOnly: { type: Boolean, required: false, default: false },
21
23
  total: { type: Boolean, required: false, default: false }
22
24
  });
25
+ const { isFollowing } = useAAFollowPeer();
23
26
  const enableAwareness = computed(() => props.awareness || props.total);
24
- const enableLive = computed(() => props.live || props.total);
27
+ const enableLive = computed(() => props.live || props.total || props.followOnly && isFollowing.value);
25
28
  const attrs = useAttrs();
26
29
  const { hoverers, focusers, hoverHandlers } = useAAField(() => props.fieldKey);
27
30
  const liveExpanded = useAAUIState(() => `${props.fieldKey}:expanded`, { defaultValue: "" });
@@ -37,6 +40,13 @@ watch(local, (val) => {
37
40
  if (!enableLive.value) return;
38
41
  if (JSON.stringify(liveExpanded.value) !== JSON.stringify(val)) liveExpanded.value = val;
39
42
  });
43
+ watch(enableLive, (now, prev) => {
44
+ if (!now || prev) return;
45
+ const incoming = liveExpanded.value;
46
+ if (incoming != null && JSON.stringify(local.value) !== JSON.stringify(incoming)) {
47
+ local.value = incoming;
48
+ }
49
+ });
40
50
  const ringStyle = computed(() => {
41
51
  if (!enableAwareness.value) return void 0;
42
52
  const focusColor = focusers.value[0]?.user?.color;