@abraca/nuxt 2.0.4 → 2.0.5

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.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.0.0"
6
6
  },
7
- "version": "2.0.4",
7
+ "version": "2.0.5",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -15,12 +15,16 @@ type __VLS_Props = {
15
15
  * the consumer can opt into the look without affecting other call
16
16
  * sites (e.g. show on the local user's own avatar but not on peers). */
17
17
  useStatusAsAvatar?: boolean;
18
+ /** Free-form icon override. Takes precedence over `useStatusAsAvatar`.
19
+ * Accepts a Lucide icon name (`smile`) or a full Iconify name (`i-lucide-smile`). */
20
+ icon?: string;
18
21
  };
19
22
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
23
  follow: (user: AwarenessUser) => any;
21
24
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
25
  onFollow?: ((user: AwarenessUser) => any) | undefined;
23
26
  }>, {
27
+ icon: string;
24
28
  size: "2xs" | "xs" | "sm" | "md" | "lg";
25
29
  showStatus: boolean;
26
30
  showTooltip: boolean;
@@ -10,7 +10,8 @@ const props = defineProps({
10
10
  showTooltip: { type: Boolean, required: false, default: true },
11
11
  showPopover: { type: Boolean, required: false, default: false },
12
12
  currentDocId: { type: String, required: false },
13
- useStatusAsAvatar: { type: Boolean, required: false, default: false }
13
+ useStatusAsAvatar: { type: Boolean, required: false, default: false },
14
+ icon: { type: String, required: false, default: "" }
14
15
  });
15
16
  const emit = defineEmits(["follow"]);
16
17
  const appConfig = useAppConfig();
@@ -22,7 +23,14 @@ const statusIcon = computed(
22
23
  () => statusIconRaw.value ? statusIconRaw.value.startsWith("i-") ? statusIconRaw.value : `i-lucide-${statusIconRaw.value}` : ""
23
24
  );
24
25
  const statusText = computed(() => props.user.user?.statusText ?? "");
25
- const showStatusAsAvatar = computed(() => props.useStatusAsAvatar && !!statusIcon.value);
26
+ const explicitIcon = computed(
27
+ () => props.icon ? props.icon.startsWith("i-") ? props.icon : `i-lucide-${props.icon}` : ""
28
+ );
29
+ const avatarIcon = computed(() => {
30
+ if (explicitIcon.value) return explicitIcon.value;
31
+ if (props.useStatusAsAvatar && statusIcon.value) return statusIcon.value;
32
+ return void 0;
33
+ });
26
34
  const avatarStyle = computed(
27
35
  () => color.value ? avatarBorderStyle(color.value, neutral.value) : ""
28
36
  );
@@ -77,6 +85,7 @@ const popoverOpen = ref(false);
77
85
  :alt="name"
78
86
  :size="size"
79
87
  :style="avatarStyle"
88
+ :icon="avatarIcon"
80
89
  />
81
90
  </UChip>
82
91
  <UAvatar
@@ -84,6 +93,7 @@ const popoverOpen = ref(false);
84
93
  :alt="name"
85
94
  :size="size"
86
95
  :style="avatarStyle"
96
+ :icon="avatarIcon"
87
97
  />
88
98
  </UTooltip>
89
99
  <UAvatar
@@ -91,6 +101,7 @@ const popoverOpen = ref(false);
91
101
  :alt="name"
92
102
  :size="size"
93
103
  :style="avatarStyle"
104
+ :icon="avatarIcon"
94
105
  />
95
106
 
96
107
  <template #content>
@@ -100,6 +111,7 @@ const popoverOpen = ref(false);
100
111
  :alt="name"
101
112
  size="md"
102
113
  :style="avatarStyle"
114
+ :icon="avatarIcon"
103
115
  />
104
116
  <div class="flex-1 min-w-0">
105
117
  <p class="text-sm font-semibold text-default truncate">
@@ -151,7 +163,7 @@ const popoverOpen = ref(false);
151
163
  :alt="name"
152
164
  :size="size"
153
165
  :style="avatarStyle"
154
- :icon="showStatusAsAvatar ? statusIcon : void 0"
166
+ :icon="avatarIcon"
155
167
  />
156
168
  </UChip>
157
169
  <UAvatar
@@ -174,7 +186,7 @@ const popoverOpen = ref(false);
174
186
  :alt="name"
175
187
  :size="size"
176
188
  :style="avatarStyle"
177
- :icon="showStatusAsAvatar ? statusIcon : void 0"
189
+ :icon="avatarIcon"
178
190
  />
179
191
  </UChip>
180
192
  <UAvatar
@@ -182,6 +194,7 @@ const popoverOpen = ref(false);
182
194
  :alt="name"
183
195
  :size="size"
184
196
  :style="avatarStyle"
197
+ :icon="avatarIcon"
185
198
  />
186
199
  </template>
187
200
  </template>
@@ -15,12 +15,16 @@ type __VLS_Props = {
15
15
  * the consumer can opt into the look without affecting other call
16
16
  * sites (e.g. show on the local user's own avatar but not on peers). */
17
17
  useStatusAsAvatar?: boolean;
18
+ /** Free-form icon override. Takes precedence over `useStatusAsAvatar`.
19
+ * Accepts a Lucide icon name (`smile`) or a full Iconify name (`i-lucide-smile`). */
20
+ icon?: string;
18
21
  };
19
22
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
23
  follow: (user: AwarenessUser) => any;
21
24
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
25
  onFollow?: ((user: AwarenessUser) => any) | undefined;
23
26
  }>, {
27
+ icon: string;
24
28
  size: "2xs" | "xs" | "sm" | "md" | "lg";
25
29
  showStatus: boolean;
26
30
  showTooltip: boolean;
@@ -30,6 +30,12 @@ const enableLive = computed(() => props.live || props.total);
30
30
  const attrs = useAttrs();
31
31
  const { hoverers, focusers, pressers, isPressed, handlers } = useAAField(() => props.fieldKey);
32
32
  const synced = useAAFieldValue(() => props.fieldKey);
33
+ const syncedModel = computed({
34
+ get: () => synced.value,
35
+ set: (v) => {
36
+ synced.value = v;
37
+ }
38
+ });
33
39
  const liveOpen = useAAUIState(() => `${props.fieldKey}:open`, { defaultValue: false });
34
40
  const liveQuery = useAAUIState(() => `${props.fieldKey}:query`, { defaultValue: "" });
35
41
  const hasExternalModel = computed(() => "modelValue" in attrs || "onUpdate:modelValue" in attrs);
@@ -81,7 +87,7 @@ const eventHandlers = computed(() => enableAwareness.value ? handlers : {});
81
87
  >
82
88
  <UInputMenu
83
89
  v-if="enableSync && !hasExternalModel && enableLive && !hasExternalOpen"
84
- v-model="synced"
90
+ v-model="syncedModel"
85
91
  v-model:open="localOpen"
86
92
  v-model:search-term="localQuery"
87
93
  v-bind="attrs"
@@ -94,7 +100,7 @@ const eventHandlers = computed(() => enableAwareness.value ? handlers : {});
94
100
  </UInputMenu>
95
101
  <UInputMenu
96
102
  v-else-if="enableSync && !hasExternalModel"
97
- v-model="synced"
103
+ v-model="syncedModel"
98
104
  v-bind="attrs"
99
105
  >
100
106
  <template #item="{ item }">
@@ -12,8 +12,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
12
12
  awareness: boolean;
13
13
  tag: "video" | "audio";
14
14
  live: boolean;
15
- controls: boolean;
16
15
  total: boolean;
16
+ controls: boolean;
17
17
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
18
  declare const _default: typeof __VLS_export;
19
19
  export default _default;
@@ -12,8 +12,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
12
12
  awareness: boolean;
13
13
  tag: "video" | "audio";
14
14
  live: boolean;
15
- controls: boolean;
16
15
  total: boolean;
16
+ controls: boolean;
17
17
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
18
  declare const _default: typeof __VLS_export;
19
19
  export default _default;
@@ -30,6 +30,12 @@ const enableLive = computed(() => props.live || props.total);
30
30
  const attrs = useAttrs();
31
31
  const { hoverers, focusers, pressers, isPressed, handlers } = useAAField(() => props.fieldKey);
32
32
  const synced = useAAFieldValue(() => props.fieldKey);
33
+ const syncedModel = computed({
34
+ get: () => synced.value,
35
+ set: (v) => {
36
+ synced.value = v;
37
+ }
38
+ });
33
39
  const liveOpen = useAAUIState(() => `${props.fieldKey}:open`, { defaultValue: false });
34
40
  const liveQuery = useAAUIState(() => `${props.fieldKey}:query`, { defaultValue: "" });
35
41
  const hasExternalModel = computed(() => "modelValue" in attrs || "onUpdate:modelValue" in attrs);
@@ -82,7 +88,7 @@ const eventHandlers = computed(() => enableAwareness.value ? handlers : {});
82
88
  >
83
89
  <USelectMenu
84
90
  v-if="enableSync && !hasExternalModel && enableLive && !hasExternalOpen"
85
- v-model="synced"
91
+ v-model="syncedModel"
86
92
  v-model:open="localOpen"
87
93
  v-model:search-term="localQuery"
88
94
  v-bind="attrs"
@@ -95,7 +101,7 @@ const eventHandlers = computed(() => enableAwareness.value ? handlers : {});
95
101
  </USelectMenu>
96
102
  <USelectMenu
97
103
  v-else-if="enableSync && !hasExternalModel"
98
- v-model="synced"
104
+ v-model="syncedModel"
99
105
  v-bind="attrs"
100
106
  >
101
107
  <template #item="{ item }">
@@ -238,8 +238,8 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<import
238
238
  transition: boolean;
239
239
  autofocus: boolean;
240
240
  loading: boolean;
241
- colorMode: boolean;
242
241
  overlay: boolean;
242
+ colorMode: boolean;
243
243
  dismissible: boolean;
244
244
  fullscreen: boolean;
245
245
  modal: boolean;
@@ -238,8 +238,8 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<import
238
238
  transition: boolean;
239
239
  autofocus: boolean;
240
240
  loading: boolean;
241
- colorMode: boolean;
242
241
  overlay: boolean;
242
+ colorMode: boolean;
243
243
  dismissible: boolean;
244
244
  fullscreen: boolean;
245
245
  modal: boolean;
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
2
  import { ref, computed, watch, shallowRef, onMounted, onBeforeUnmount, onUnmounted, defineAsyncComponent } from "vue";
3
- import { useRuntimeConfig, useColorMode } from "#imports";
3
+ import { useRuntimeConfig, useColorMode, useAppConfig } from "#imports";
4
+ import { avatarBorderStyle } from "../../utils/avatarStyle";
4
5
  import { useAbracadabra } from "../../composables/useAbracadabra";
5
6
  import { useRendererBase } from "../../composables/useRendererBase";
6
7
  import { useNodePanel } from "../../composables/useNodePanel";
@@ -144,6 +145,8 @@ const mapReady = ref(false);
144
145
  const mapFadedIn = ref(false);
145
146
  const colorMode = useColorMode();
146
147
  const isDark = computed(() => colorMode.value === "dark");
148
+ const mapAppConfig = useAppConfig();
149
+ const mapNeutralColor = computed(() => mapAppConfig.ui?.colors?.neutral ?? "zinc");
147
150
  const activeMode = ref("pan");
148
151
  const showLabels = ref(true);
149
152
  const popupProvider = shallowRef(null);
@@ -360,7 +363,15 @@ function createPresenceEl(name, colorHex) {
360
363
  const el = document.createElement("div");
361
364
  el.className = "presence-marker";
362
365
  const initials = name.split(" ").map((w) => w[0] ?? "").join("").substring(0, 2).toUpperCase();
363
- el.innerHTML = `<div class="presence-avatar" style="background:${colorHex}">${initials}</div><div class="presence-label">${name}</div>`;
366
+ const avatar = document.createElement("div");
367
+ avatar.className = "presence-avatar";
368
+ avatar.style.cssText = avatarBorderStyle(colorHex, mapNeutralColor.value);
369
+ avatar.textContent = initials;
370
+ const label = document.createElement("div");
371
+ label.className = "presence-label";
372
+ label.textContent = name;
373
+ el.appendChild(avatar);
374
+ el.appendChild(label);
364
375
  return el;
365
376
  }
366
377
  function tickPresenceMarkers() {
@@ -1620,5 +1631,5 @@ defineExpose({ connectedUsers });
1620
1631
  </style>
1621
1632
 
1622
1633
  <style>
1623
- .mapboxgl-map{height:100%!important;width:100%!important}.collab-marker-container{align-items:center;cursor:pointer;display:flex;flex-direction:column}.collab-marker-pin{align-items:center;border-radius:50%;display:flex;height:28px;justify-content:center;transition:transform .2s ease;width:28px;z-index:30}.collab-marker-pin:hover{transform:scale(1.15)}.collab-marker-label{background:rgba(0,0,0,.5);border-radius:3px;color:#fff;font-size:11px;font-weight:700;margin-top:3px;padding:1px 5px;pointer-events:none;text-shadow:0 1px 4px rgba(0,0,0,.7);white-space:nowrap}.collab-line-point{border:2px solid #fff;border-radius:50%;box-shadow:0 1px 4px rgba(0,0,0,.4);cursor:grab;height:10px;width:10px}.collab-line-point:hover{height:14px;width:14px}.presence-marker{align-items:center;display:flex;flex-direction:column;gap:2px;pointer-events:none}.presence-avatar{align-items:center;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.4);color:#fff;display:flex;font-size:.65rem;font-weight:700;height:1.75rem;justify-content:center;width:1.75rem}.presence-label{color:#fff;font-size:.6rem;font-weight:700;text-shadow:0 1px 4px rgba(0,0,0,.8);white-space:nowrap}.map-cursor-el{left:0;pointer-events:none;position:absolute;top:0;will-change:transform;z-index:50}.map-cursor-name{font-size:.6rem;font-weight:600;left:14px;top:8px}.map-cursor-name,.measure-label-el{border-radius:3px;color:#000;padding:1px 5px;position:absolute;white-space:nowrap}.measure-label-el{background:rgba(250,204,21,.92);box-shadow:0 1px 4px rgba(0,0,0,.3);font-size:.62rem;font-weight:700;left:0;pointer-events:none;top:0;transform:translate(-50%,calc(-100% - 5px));z-index:35}@keyframes ping-ring{0%{opacity:1;transform:scale(0)}to{opacity:0;transform:scale(3)}}.map-ping-root{height:12px;left:0;pointer-events:none;position:absolute;top:0;transform:translate(-50%,-50%);width:12px;z-index:45}.map-ping-ring{animation:ping-ring 1.5s ease-out forwards;border:3px solid var(--ping-color);border-radius:50%;inset:-16px;position:absolute}.map-ping-dot{background:var(--ping-color);border:2px solid #fff;border-radius:50%;height:12px;left:0;position:absolute;top:0;width:12px}
1634
+ .mapboxgl-map{height:100%!important;width:100%!important}.collab-marker-container{align-items:center;cursor:pointer;display:flex;flex-direction:column}.collab-marker-pin{align-items:center;border-radius:50%;display:flex;height:28px;justify-content:center;transition:transform .2s ease;width:28px;z-index:30}.collab-marker-pin:hover{transform:scale(1.15)}.collab-marker-label{background:rgba(0,0,0,.5);border-radius:3px;color:#fff;font-size:11px;font-weight:700;margin-top:3px;padding:1px 5px;pointer-events:none;text-shadow:0 1px 4px rgba(0,0,0,.7);white-space:nowrap}.collab-line-point{border:2px solid #fff;border-radius:50%;box-shadow:0 1px 4px rgba(0,0,0,.4);cursor:grab;height:10px;width:10px}.collab-line-point:hover{height:14px;width:14px}.presence-marker{align-items:center;display:flex;flex-direction:column;gap:2px;pointer-events:none}.presence-avatar{align-items:center;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.4);display:flex;font-size:.65rem;font-weight:700;height:1.75rem;justify-content:center;width:1.75rem}.presence-label{color:#fff;font-size:.6rem;font-weight:700;text-shadow:0 1px 4px rgba(0,0,0,.8);white-space:nowrap}.map-cursor-el{left:0;pointer-events:none;position:absolute;top:0;will-change:transform;z-index:50}.map-cursor-name{font-size:.6rem;font-weight:600;left:14px;top:8px}.map-cursor-name,.measure-label-el{border-radius:3px;color:#000;padding:1px 5px;position:absolute;white-space:nowrap}.measure-label-el{background:rgba(250,204,21,.92);box-shadow:0 1px 4px rgba(0,0,0,.3);font-size:.62rem;font-weight:700;left:0;pointer-events:none;top:0;transform:translate(-50%,calc(-100% - 5px));z-index:35}@keyframes ping-ring{0%{opacity:1;transform:scale(0)}to{opacity:0;transform:scale(3)}}.map-ping-root{height:12px;left:0;pointer-events:none;position:absolute;top:0;transform:translate(-50%,-50%);width:12px;z-index:45}.map-ping-ring{animation:ping-ring 1.5s ease-out forwards;border:3px solid var(--ping-color);border-radius:50%;inset:-16px;position:absolute}.map-ping-dot{background:var(--ping-color);border:2px solid #fff;border-radius:50%;height:12px;left:0;position:absolute;top:0;width:12px}
1624
1635
  </style>
@@ -1,8 +1,3 @@
1
- /**
2
- * Track list grouped by parent label.
3
- * Shows equalizer animation for current track, remote listener avatars.
4
- * Ported from cou-sh/app/components/doc/media/MediaPlaylist.vue
5
- */
6
1
  import type { MediaTrack, MediaGroup } from '../../../composables/useMediaExtractor.js';
7
2
  type __VLS_Props = {
8
3
  groups: MediaGroup[];
@@ -1,10 +1,15 @@
1
1
  <script setup>
2
+ import { computed } from "vue";
3
+ import { useAppConfig } from "#imports";
4
+ import { avatarBorderStyle } from "../../../utils/avatarStyle";
2
5
  const props = defineProps({
3
6
  groups: { type: Array, required: true },
4
7
  currentTrackId: { type: [String, null], required: true },
5
8
  remoteListeners: { type: Array, required: true }
6
9
  });
7
10
  const emit = defineEmits(["playTrack", "openTrack"]);
11
+ const appConfig = useAppConfig();
12
+ const neutral = computed(() => appConfig.ui?.colors?.neutral ?? "zinc");
8
13
  function formatDuration(seconds) {
9
14
  if (!seconds || !Number.isFinite(seconds)) return "";
10
15
  const m = Math.floor(seconds / 60);
@@ -89,15 +94,14 @@ function trackSubtitle(track) {
89
94
  v-if="listenersForTrack(track.id).length"
90
95
  class="flex -space-x-1.5 shrink-0 ml-1"
91
96
  >
92
- <span
97
+ <UAvatar
93
98
  v-for="listener in listenersForTrack(track.id).slice(0, 3)"
94
99
  :key="listener.clientId"
95
- class="size-5 rounded-full flex items-center justify-center text-[9px] font-medium text-white ring-1 ring-(--ui-bg)"
96
- :style="{ backgroundColor: listener.color }"
100
+ :alt="listener.name"
101
+ size="2xs"
97
102
  :title="listener.name"
98
- >
99
- {{ listener.name?.charAt(0)?.toUpperCase() ?? "?" }}
100
- </span>
103
+ :style="avatarBorderStyle(listener.color, neutral)"
104
+ />
101
105
  <span
102
106
  v-if="listenersForTrack(track.id).length > 3"
103
107
  class="size-5 rounded-full flex items-center justify-center text-[9px] font-medium text-(--ui-text-dimmed) bg-(--ui-bg-accented) ring-1 ring-(--ui-bg)"
@@ -1,8 +1,3 @@
1
- /**
2
- * Track list grouped by parent label.
3
- * Shows equalizer animation for current track, remote listener avatars.
4
- * Ported from cou-sh/app/components/doc/media/MediaPlaylist.vue
5
- */
6
1
  import type { MediaTrack, MediaGroup } from '../../../composables/useMediaExtractor.js';
7
2
  type __VLS_Props = {
8
3
  groups: MediaGroup[];
@@ -1,7 +1,3 @@
1
- /**
2
- * Sync session bar — join/leave coordinated listening.
3
- * Ported from cou-sh/app/components/doc/media/MediaSyncBar.vue
4
- */
5
1
  type __VLS_Props = {
6
2
  isSynced: boolean;
7
3
  syncedUsers: Array<{
@@ -1,9 +1,14 @@
1
1
  <script setup>
2
+ import { computed } from "vue";
3
+ import { useAppConfig } from "#imports";
4
+ import { avatarBorderStyle } from "../../../utils/avatarStyle";
2
5
  defineProps({
3
6
  isSynced: { type: Boolean, required: true },
4
7
  syncedUsers: { type: Array, required: true }
5
8
  });
6
9
  const emit = defineEmits(["joinSync", "leaveSync"]);
10
+ const appConfig = useAppConfig();
11
+ const neutral = computed(() => appConfig.ui?.colors?.neutral ?? "zinc");
7
12
  </script>
8
13
 
9
14
  <template>
@@ -28,15 +33,14 @@ const emit = defineEmits(["joinSync", "leaveSync"]);
28
33
  v-if="syncedUsers.length > 0"
29
34
  class="flex -space-x-1.5 shrink-0 mr-2"
30
35
  >
31
- <span
36
+ <UAvatar
32
37
  v-for="user in syncedUsers.slice(0, 5)"
33
38
  :key="user.clientId"
34
- class="size-5 rounded-full flex items-center justify-center text-[9px] font-medium text-white ring-1 ring-(--ui-bg)"
35
- :style="{ backgroundColor: user.color }"
39
+ :alt="user.name"
40
+ size="2xs"
36
41
  :title="user.name"
37
- >
38
- {{ user.name?.charAt(0)?.toUpperCase() ?? "?" }}
39
- </span>
42
+ :style="avatarBorderStyle(user.color, neutral)"
43
+ />
40
44
  <span
41
45
  v-if="syncedUsers.length > 5"
42
46
  class="size-5 rounded-full flex items-center justify-center text-[9px] font-medium text-(--ui-text-dimmed) bg-(--ui-bg-accented) ring-1 ring-(--ui-bg)"
@@ -1,7 +1,3 @@
1
- /**
2
- * Sync session bar — join/leave coordinated listening.
3
- * Ported from cou-sh/app/components/doc/media/MediaSyncBar.vue
4
- */
5
1
  type __VLS_Props = {
6
2
  isSynced: boolean;
7
3
  syncedUsers: Array<{
@@ -1,10 +1,12 @@
1
1
  /**
2
- * Split-border avatar: inner filled with primaryColor, border = left half primary / right half neutral.
2
+ * Identity avatar style: neutral background fill, primary foreground (text + icons),
3
+ * split conic border (left half primary, right half neutral) as the identity signal,
4
+ * subtle drop shadow on text/icons for legibility on the neutral fill.
3
5
  *
4
6
  * CSS rules:
5
7
  * - Colors cannot appear in non-final background layers; use linear-gradient(c,c) as a solid-color image instead.
6
8
  * - UAvatar has overflow:hidden which clips border-area backgrounds; override with overflow:visible.
7
- * Border-radius still clips the background shape, and text initials don't need overflow clipping.
9
+ * - `color` cascades to UAvatar's text fallback and to nested UIcon glyphs (UIcon uses currentColor).
8
10
  */
9
11
  export declare function avatarBorderStyle(primaryColor: string, neutralColorName: string): string;
10
12
  /**
@@ -2,7 +2,9 @@ export function avatarBorderStyle(primaryColor, neutralColorName) {
2
2
  const neutral = `var(--color-${neutralColorName}-400, #888)`;
3
3
  return [
4
4
  "border: 2px solid transparent",
5
- `background: linear-gradient(${primaryColor}, ${primaryColor}) padding-box, conic-gradient(${primaryColor} 180deg, ${neutral} 180deg) border-box`,
5
+ `background: linear-gradient(${neutral}, ${neutral}) padding-box, conic-gradient(${primaryColor} 180deg, ${neutral} 180deg) border-box`,
6
+ `color: ${primaryColor} !important`,
7
+ "text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35)",
6
8
  "overflow: visible"
7
9
  ].join("; ");
8
10
  }
@@ -11,7 +13,9 @@ export function avatarStyleFromName(primaryColorName, neutralColorName) {
11
13
  const neutral = `var(--color-${neutralColorName}-400, #888)`;
12
14
  return [
13
15
  "border: 2px solid transparent",
14
- `background: linear-gradient(${primary}, ${primary}) padding-box, conic-gradient(${primary} 180deg, ${neutral} 180deg) border-box`,
16
+ `background: linear-gradient(${neutral}, ${neutral}) padding-box, conic-gradient(${primary} 180deg, ${neutral} 180deg) border-box`,
17
+ `color: ${primary} !important`,
18
+ "text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35)",
15
19
  "overflow: visible"
16
20
  ].join("; ");
17
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/nuxt",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "First-class Nuxt module for the Abracadabra CRDT collaboration platform",
5
5
  "repository": "abracadabra/abracadabra-nuxt",
6
6
  "license": "MIT",
@@ -122,7 +122,7 @@
122
122
  "tslib": "^2.8.1",
123
123
  "typescript": "~6.0.3",
124
124
  "vitest": "^4.1.5",
125
- "vue": "^3.5.33",
125
+ "vue": "^3.5.34",
126
126
  "vue-tsc": "^3.2.8",
127
127
  "ws": "^8.20.0",
128
128
  "yjs": "^13.6.30"