@alikhalilll/a-skeleton 1.0.0 → 1.1.0

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/styles.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}:root,.light{--ak-ui-background:0 0% 100%;--ak-ui-foreground:240 10% 3.9%;--ak-ui-card:0 0% 100%;--ak-ui-card-foreground:240 10% 3.9%;--ak-ui-popover:0 0% 100%;--ak-ui-popover-foreground:240 10% 3.9%;--ak-ui-primary:240 5.9% 10%;--ak-ui-primary-foreground:0 0% 98%;--ak-ui-secondary:240 4.8% 95.9%;--ak-ui-secondary-foreground:240 5.9% 10%;--ak-ui-muted:240 4.8% 95.9%;--ak-ui-muted-foreground:240 3.8% 46.1%;--ak-ui-accent:240 4.8% 95.9%;--ak-ui-accent-foreground:240 5.9% 10%;--ak-ui-destructive:0 84.2% 60.2%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 5.9% 90%;--ak-ui-input:240 5.9% 90%;--ak-ui-ring:240 5.9% 10%;--ak-ui-radius:.5rem}.dark{--ak-ui-background:240 10% 3.9%;--ak-ui-foreground:0 0% 98%;--ak-ui-card:240 10% 3.9%;--ak-ui-card-foreground:0 0% 98%;--ak-ui-popover:240 10% 3.9%;--ak-ui-popover-foreground:0 0% 98%;--ak-ui-primary:0 0% 98%;--ak-ui-primary-foreground:240 5.9% 10%;--ak-ui-secondary:240 3.7% 15.9%;--ak-ui-secondary-foreground:0 0% 98%;--ak-ui-muted:240 3.7% 15.9%;--ak-ui-muted-foreground:240 5% 64.9%;--ak-ui-accent:240 3.7% 15.9%;--ak-ui-accent-foreground:0 0% 98%;--ak-ui-destructive:0 62.8% 30.6%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 3.7% 15.9%;--ak-ui-input:240 3.7% 15.9%;--ak-ui-ring:240 4.9% 83.9%}:root,:host{--spacing:.25rem}.relative{position:relative}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.\!mt-3{margin-top:calc(var(--spacing) * 3)!important}.block{display:block}.flex{display:flex}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.size-16{width:calc(var(--spacing) * 16);height:calc(var(--spacing) * 16)}.flex-1{flex:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.resize{resize:both}.items-start{align-items:flex-start}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.p-4{padding:calc(var(--spacing) * 4)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:root{--ak-skeleton-block:hsl(var(--ak-ui-muted) / .55);--ak-skeleton-block-soft:hsl(var(--ak-ui-muted) / .32);--ak-skeleton-shimmer:hsl(var(--ak-ui-foreground) / .08);--ak-skeleton-radius:.375rem;--ak-skeleton-duration:1.6s;--ak-skeleton-pulse-opacity:.48;--ak-skeleton-shimmer-angle:110deg;--ak-skeleton-ring:hsl(var(--ak-ui-foreground) / .04)}.light{--ak-skeleton-shimmer:#ffffffd9;--ak-skeleton-ring:#0000000d}.a-skel-block{background-image:linear-gradient(180deg, var(--ak-skeleton-block) 0%, var(--ak-skeleton-block-soft) 100%);background-color:var(--ak-skeleton-block);border-radius:var(--ak-skeleton-radius);box-shadow:inset 0 0 0 1px var(--ak-skeleton-ring);isolation:isolate;position:relative;overflow:hidden}.a-skel-block--anim-shimmer:after{content:"";background-image:linear-gradient(var(--ak-skeleton-shimmer-angle), transparent 30%, var(--ak-skeleton-shimmer) 50%, transparent 70%);will-change:transform;animation:a-skel-shimmer var(--ak-skeleton-duration) cubic-bezier(.42, 0, .58, 1) infinite;z-index:1;position:absolute;inset:0 -25%;transform:translate(-110%)}.a-skel-block--anim-pulse{animation:a-skel-pulse var(--ak-skeleton-duration) cubic-bezier(.42, 0, .58, 1) infinite;will-change:opacity}@keyframes a-skel-shimmer{0%{transform:translate(-110%)}to{transform:translate(110%)}}@keyframes a-skel-pulse{0%,to{opacity:1}50%{opacity:var(--ak-skeleton-pulse-opacity)}}@media (prefers-reduced-motion:reduce){.a-skel-block--anim-shimmer:after,.a-skel-block--anim-pulse{will-change:auto;animation:none}.a-skel-block--anim-shimmer:after{opacity:.5;transform:translate(0)}}.a-skeleton__layer{contain:layout style paint;content-visibility:auto;position:relative}.a-skeleton__layer>.a-skel-block{contain:strict;position:absolute}.a-skel-block--text{border-radius:calc(var(--ak-skeleton-radius) * .6)}.a-skel-block--circle{background-image:radial-gradient(circle at 35% 30%, var(--ak-skeleton-block) 0%, var(--ak-skeleton-block-soft) 100%)}.a-skel-block--image{background-image:linear-gradient(160deg, var(--ak-skeleton-block) 0%, var(--ak-skeleton-block-soft) 60%, var(--ak-skeleton-block) 100%)}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}:root,.light{--ak-ui-background:0 0% 100%;--ak-ui-foreground:240 10% 3.9%;--ak-ui-card:0 0% 100%;--ak-ui-card-foreground:240 10% 3.9%;--ak-ui-popover:0 0% 100%;--ak-ui-popover-foreground:240 10% 3.9%;--ak-ui-primary:240 5.9% 10%;--ak-ui-primary-foreground:0 0% 98%;--ak-ui-secondary:240 4.8% 95.9%;--ak-ui-secondary-foreground:240 5.9% 10%;--ak-ui-muted:240 4.8% 95.9%;--ak-ui-muted-foreground:240 3.8% 46.1%;--ak-ui-accent:240 4.8% 95.9%;--ak-ui-accent-foreground:240 5.9% 10%;--ak-ui-destructive:0 84.2% 60.2%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 5.9% 90%;--ak-ui-input:240 5.9% 90%;--ak-ui-ring:240 5.9% 10%;--ak-ui-radius:.5rem}.dark{--ak-ui-background:240 10% 3.9%;--ak-ui-foreground:0 0% 98%;--ak-ui-card:240 10% 3.9%;--ak-ui-card-foreground:0 0% 98%;--ak-ui-popover:240 10% 3.9%;--ak-ui-popover-foreground:0 0% 98%;--ak-ui-primary:0 0% 98%;--ak-ui-primary-foreground:240 5.9% 10%;--ak-ui-secondary:240 3.7% 15.9%;--ak-ui-secondary-foreground:0 0% 98%;--ak-ui-muted:240 3.7% 15.9%;--ak-ui-muted-foreground:240 5% 64.9%;--ak-ui-accent:240 3.7% 15.9%;--ak-ui-accent-foreground:0 0% 98%;--ak-ui-destructive:0 62.8% 30.6%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 3.7% 15.9%;--ak-ui-input:240 3.7% 15.9%;--ak-ui-ring:240 4.9% 83.9%}:root,:host{--spacing:.25rem}.collapse{visibility:collapse}.relative{position:relative}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.\!mt-3{margin-top:calc(var(--spacing) * 3)!important}.block{display:block}.flex{display:flex}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.size-16{width:calc(var(--spacing) * 16);height:calc(var(--spacing) * 16)}.flex-1{flex:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.resize{resize:both}.items-start{align-items:flex-start}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.p-4{padding:calc(var(--spacing) * 4)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}:root{--ak-skeleton-block:hsl(var(--ak-ui-muted) / .55);--ak-skeleton-block-soft:hsl(var(--ak-ui-muted) / .32);--ak-skeleton-shimmer:hsl(var(--ak-ui-foreground) / .08);--ak-skeleton-radius:.375rem;--ak-skeleton-duration:1.6s;--ak-skeleton-pulse-opacity:.48;--ak-skeleton-shimmer-angle:110deg;--ak-skeleton-ring:hsl(var(--ak-ui-foreground) / .04)}.light{--ak-skeleton-shimmer:#ffffffd9;--ak-skeleton-ring:#0000000d}.a-skel-block{background-image:linear-gradient(180deg, var(--ak-skeleton-block) 0%, var(--ak-skeleton-block-soft) 100%);background-color:var(--ak-skeleton-block);border-radius:var(--ak-skeleton-radius);box-shadow:inset 0 0 0 1px var(--ak-skeleton-ring);isolation:isolate;position:relative;overflow:hidden}.a-skel-block--anim-shimmer:after{content:"";background-image:linear-gradient(var(--ak-skeleton-shimmer-angle), transparent 30%, var(--ak-skeleton-shimmer) 50%, transparent 70%);will-change:transform;animation:a-skel-shimmer var(--ak-skeleton-duration) cubic-bezier(.42, 0, .58, 1) infinite;z-index:1;position:absolute;inset:0 -25%;transform:translate(-110%)}.a-skel-block--anim-pulse{animation:a-skel-pulse var(--ak-skeleton-duration) cubic-bezier(.42, 0, .58, 1) infinite;will-change:opacity}@keyframes a-skel-shimmer{0%{transform:translate(-110%)}to{transform:translate(110%)}}@keyframes a-skel-pulse{0%,to{opacity:1}50%{opacity:var(--ak-skeleton-pulse-opacity)}}@media (prefers-reduced-motion:reduce){.a-skel-block--anim-shimmer:after,.a-skel-block--anim-pulse{will-change:auto;animation:none}.a-skel-block--anim-shimmer:after{opacity:.5;transform:translate(0)}}.a-skeleton__layer{contain:layout style paint;content-visibility:auto;position:relative}.a-skeleton__layer>.a-skel-block{contain:strict;position:absolute}.a-skel-block--text{border-radius:calc(var(--ak-skeleton-radius) * .6)}.a-skel-block--circle{background-image:radial-gradient(circle at 35% 30%, var(--ak-skeleton-block) 0%, var(--ak-skeleton-block-soft) 100%)}.a-skel-block--image{background-image:linear-gradient(160deg, var(--ak-skeleton-block) 0%, var(--ak-skeleton-block-soft) 60%, var(--ak-skeleton-block) 100%)}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
3
3
  /* --- bundled SFC styles --- */
4
4
 
5
5
  .a-skeleton[data-v-16717541] {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alikhalilll/a-skeleton",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Self-generating Vue 3 / Nuxt 3+ skeleton loader. First paint mirrors the slot's HTML structure; second load replays a pixel-aligned shape captured from the real DOM. Themeable via CSS vars. Part of the @alikhalilll/a-* toolkit.",
5
5
  "license": "MIT",
6
6
  "author": "alikhalilll",
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref, shallowRef, useSlots, watch, type CSSProperties } from 'vue';
2
+ import { computed, ref, shallowRef, useId, useSlots, watch, type CSSProperties } from 'vue';
3
3
  import { cn } from '@alikhalilll/a-ui-base';
4
4
  import type { ASkeletonProps, ASkeletonSlots, CachedShape, ShapeNodeType } from '../types';
5
5
  import { useShapeProbe } from '../composables/useShapeProbe';
@@ -19,7 +19,18 @@ defineSlots<ASkeletonSlots>();
19
19
 
20
20
  const slots = useSlots();
21
21
 
22
- const resolvedKey = computed(() => props.cacheKey ?? fingerprintSlot(slots.default?.()));
22
+ /* Per-instance suffix from Vue's useId() deterministic across SSR/hydration
23
+ * and stable across re-renders, but distinct per <ASkeleton> instance. Two
24
+ * <ASkeleton><UserCard/></ASkeleton> on the same page therefore never collide
25
+ * on the auto-generated key. Pass an explicit `cacheKey` to share a shape
26
+ * across instances on purpose. */
27
+ const instanceId = useId();
28
+
29
+ const resolvedKey = computed(
30
+ () => props.cacheKey ?? `${fingerprintSlot(slots.default?.())}:${instanceId}`
31
+ );
32
+
33
+ const warnedKeys = new Set<string>();
23
34
 
24
35
  const cached = shallowRef<CachedShape | undefined>(getCached(resolvedKey.value, props.persist));
25
36
 
@@ -38,6 +49,14 @@ useShapeProbe(() => (props.loading ? null : wrapperRef.value), {
38
49
  onCapture: (shape) => {
39
50
  setCached(resolvedKey.value, shape, props.persist);
40
51
  cached.value = shape;
52
+ if (shape.truncated && !warnedKeys.has(resolvedKey.value)) {
53
+ warnedKeys.add(resolvedKey.value);
54
+ console.warn(
55
+ `[ASkeleton] Capture truncated at maxNodes=${props.maxNodes} for cacheKey="${resolvedKey.value}". ` +
56
+ `The replayed skeleton will be missing nodes. Raise \`max-nodes\` or mark dense subtrees with ` +
57
+ `\`data-skeleton-stop\` to collapse them into a single block.`
58
+ );
59
+ }
41
60
  },
42
61
  });
43
62
 
@@ -4,21 +4,44 @@ import type { CachedShape, ShapeNode } from '../types';
4
4
  const memory = new Map<string, CachedShape>();
5
5
  const STORAGE_PREFIX = 'a-skeleton:';
6
6
 
7
+ /**
8
+ * Schema version for persisted entries. Bump whenever the `ShapeNode` /
9
+ * `CachedShape` field set changes so stale localStorage payloads from older
10
+ * releases get dropped on read instead of rehydrating into a wrong layout.
11
+ */
12
+ const SCHEMA_VERSION = 1;
13
+
14
+ interface PersistedShape {
15
+ v: number;
16
+ width: number;
17
+ height: number;
18
+ nodes: Partial<ShapeNode>[];
19
+ truncated?: boolean;
20
+ }
21
+
7
22
  /**
8
23
  * Lookup a captured shape by key. Reads in-memory first, then `localStorage` if
9
24
  * `persist` is enabled. SSR-safe — bypasses `window` access on the server.
10
25
  * Rehydrates pre-computed styles for shapes deserialized from older sessions
11
26
  * (Object.freeze + style/lineStyles don't survive `JSON.stringify` round-trip).
27
+ * Drops the entry if it was written by a different schema version.
12
28
  */
13
29
  export function getCached(key: string, persist: boolean): CachedShape | undefined {
14
30
  const hit = memory.get(key);
15
31
  if (hit) return hit;
16
32
  if (!persist || typeof window === 'undefined') return undefined;
17
33
  try {
18
- const raw = window.localStorage.getItem(STORAGE_PREFIX + key);
34
+ const storageKey = STORAGE_PREFIX + key;
35
+ const raw = window.localStorage.getItem(storageKey);
19
36
  if (!raw) return undefined;
20
- const parsed = JSON.parse(raw) as CachedShape;
21
- const rehydrated = rehydrateShape(parsed);
37
+ const parsed = JSON.parse(raw) as Partial<PersistedShape>;
38
+ if (parsed.v !== SCHEMA_VERSION) {
39
+ /* Stale payload from a previous release — purge so the next capture
40
+ * writes a fresh entry instead of rehydrating into a wrong layout. */
41
+ window.localStorage.removeItem(storageKey);
42
+ return undefined;
43
+ }
44
+ const rehydrated = rehydrateShape(parsed as PersistedShape);
22
45
  memory.set(key, rehydrated);
23
46
  return rehydrated;
24
47
  } catch {
@@ -32,7 +55,13 @@ export function setCached(key: string, value: CachedShape, persist: boolean): vo
32
55
  if (!persist || typeof window === 'undefined') return;
33
56
  try {
34
57
  /* Only the geometry survives the round-trip; styles get rebuilt on read. */
35
- const lean = { width: value.width, height: value.height, nodes: leanNodes(value.nodes) };
58
+ const lean: PersistedShape = {
59
+ v: SCHEMA_VERSION,
60
+ width: value.width,
61
+ height: value.height,
62
+ nodes: leanNodes(value.nodes),
63
+ truncated: value.truncated,
64
+ };
36
65
  window.localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(lean));
37
66
  } catch {
38
67
  /* quota exceeded / disabled storage — silently degrade to in-memory only. */
@@ -67,8 +96,8 @@ export function clearCached(key?: string): void {
67
96
  * Walks the array in-place where possible and freezes the result so the render
68
97
  * path stays allocation-free.
69
98
  */
70
- function rehydrateShape(shape: CachedShape): CachedShape {
71
- const nodes = shape.nodes.map((n) => (n.style ? n : freezeNodeStyles(n)));
99
+ function rehydrateShape(shape: PersistedShape): CachedShape {
100
+ const nodes = shape.nodes.map((n) => freezeNodeStyles(n as ShapeNode));
72
101
  return Object.freeze({
73
102
  nodes: Object.freeze(nodes),
74
103
  width: shape.width,
package/src/types.ts CHANGED
@@ -42,9 +42,12 @@ export interface ASkeletonProps {
42
42
  /** When true, render the captured skeleton (or `fallback` slot) instead of the default slot. */
43
43
  loading: boolean;
44
44
  /**
45
- * Identifier used to look up + persist the captured shape. Falls back to the
46
- * slot's first child component name, then `'anonymous'`. Pass explicitly when
47
- * the same component renders different shapes depending on props.
45
+ * Identifier used to look up + persist the captured shape. Defaults to
46
+ * `"<slot-fingerprint>:<useId()>"` so every `<ASkeleton>` instance gets its
47
+ * own cache slot automatically two `<ASkeleton><UserCard/></ASkeleton>` on
48
+ * the same page never collide. Pass explicitly when you *want* multiple
49
+ * instances to share a captured shape (e.g. a list of identical cards), or
50
+ * when one instance renders different shapes depending on a prop.
48
51
  */
49
52
  cacheKey?: string;
50
53
  /** Max recursion depth during shape capture. Default 6. */
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@alikhalilll/a-skeleton",
4
- "version": "1.0.0",
4
+ "version": "1.1.0",
5
5
  "js-types-syntax": "typescript",
6
6
  "description-markup": "markdown",
7
7
  "framework": "vue",
@@ -32,7 +32,7 @@
32
32
  "name": "cacheKey",
33
33
  "type": "string",
34
34
  "required": false,
35
- "description": "Identifier used to look up + persist the captured shape. Falls back to the\nslot's first child component name, then `'anonymous'`. Pass explicitly when\nthe same component renders different shapes depending on props."
35
+ "description": "Identifier used to look up + persist the captured shape. Defaults to\n`\"<slot-fingerprint>:<useId()>\"` so every `<ASkeleton>` instance gets its\nown cache slot automatically two `<ASkeleton><UserCard/></ASkeleton>` on\nthe same page never collide. Pass explicitly when you *want* multiple\ninstances to share a captured shape (e.g. a list of identical cards), or\nwhen one instance renders different shapes depending on a prop."
36
36
  },
37
37
  {
38
38
  "name": "class",