fancy-ui-svelte 0.8.0 → 0.9.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.
Files changed (39) hide show
  1. package/README.md +107 -89
  2. package/dist/fancy-ui/animated-testimonials/README.md +44 -0
  3. package/dist/fancy-ui/animated-tooltip/README.md +40 -0
  4. package/dist/fancy-ui/apple-card-carousel/README.md +46 -0
  5. package/dist/fancy-ui/bento-grid/BentoGrid.test.d.ts +1 -0
  6. package/dist/fancy-ui/bento-grid/BentoGrid.test.js +109 -0
  7. package/dist/fancy-ui/border-beam/README.md +37 -0
  8. package/dist/fancy-ui/card-3d/CardContainer.test.d.ts +1 -0
  9. package/dist/fancy-ui/card-3d/CardContainer.test.js +118 -0
  10. package/dist/fancy-ui/card-spotlight/CardSpotlight.test.d.ts +1 -0
  11. package/dist/fancy-ui/card-spotlight/CardSpotlight.test.js +58 -0
  12. package/dist/fancy-ui/displacement-text/README.md +39 -0
  13. package/dist/fancy-ui/dock/README.md +1 -1
  14. package/dist/fancy-ui/editorial-engine/EditorialEngine.test.d.ts +1 -0
  15. package/dist/fancy-ui/editorial-engine/EditorialEngine.test.js +54 -0
  16. package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte +130 -9
  17. package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte.d.ts +9 -1
  18. package/dist/fancy-ui/fluid-cursor/FluidCursor.test.js +19 -0
  19. package/dist/fancy-ui/fluid-cursor/fluid-shared.d.ts +32 -0
  20. package/dist/fancy-ui/fluid-cursor/index.d.ts +1 -0
  21. package/dist/fancy-ui/fluid-cursor/webgpu-engine.d.ts +11 -4
  22. package/dist/fancy-ui/fluid-cursor/webgpu-engine.js +113 -34
  23. package/dist/fancy-ui/line-hover-link/README.md +37 -0
  24. package/dist/fancy-ui/line-reveal/LineReveal.test.d.ts +1 -0
  25. package/dist/fancy-ui/line-reveal/LineReveal.test.js +77 -0
  26. package/dist/fancy-ui/marquee/README.md +57 -0
  27. package/dist/fancy-ui/matrix-rain/MatrixRain.test.d.ts +1 -0
  28. package/dist/fancy-ui/matrix-rain/MatrixRain.test.js +30 -0
  29. package/dist/fancy-ui/matrix-rain/README.md +42 -0
  30. package/dist/fancy-ui/noise-reveal/README.md +52 -0
  31. package/dist/fancy-ui/rainbow-button/README.md +36 -0
  32. package/dist/fancy-ui/registry.js +20 -20
  33. package/dist/fancy-ui/ripple-button/README.md +36 -0
  34. package/dist/fancy-ui/smooth-cursor/SmoothCursor.test.d.ts +1 -0
  35. package/dist/fancy-ui/smooth-cursor/SmoothCursor.test.js +45 -0
  36. package/dist/fancy-ui/terminal-text/README.md +48 -0
  37. package/dist/fancy-ui/terminal-text/TerminalText.test.d.ts +1 -0
  38. package/dist/fancy-ui/terminal-text/TerminalText.test.js +88 -0
  39. package/package.json +15 -2
@@ -68,7 +68,7 @@ export const registry = {
68
68
  "apple-card-carousel": {
69
69
  name: "AppleCardCarousel",
70
70
  slug: "apple-card-carousel",
71
- description: "Horizontal card carousel with spring-animated full-screen expansion, inspired by Apple's App Store UI",
71
+ description: "Horizontal card carousel where the focused card expands into a spring-animated full-screen view",
72
72
  category: "cards",
73
73
  status: "done",
74
74
  credits: [
@@ -246,7 +246,7 @@ export const registry = {
246
246
  "blur-reveal": {
247
247
  name: "BlurReveal",
248
248
  slug: "blur-reveal",
249
- description: "Scroll-triggered blur-to-clear reveal animation with staggered children",
249
+ description: "Scroll-triggered reveal that unblurs and slides each direct child into place as the container enters the viewport, staggering up to 10 children via CSS nth-child delays and respecting reduced motion",
250
250
  category: "text",
251
251
  status: "done",
252
252
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/blur-fade" }],
@@ -284,7 +284,7 @@ export const registry = {
284
284
  "border-beam": {
285
285
  name: "BorderBeam",
286
286
  slug: "border-beam",
287
- description: "Animated beam effect that travels around borders",
287
+ description: "Gradient beam that races around a container's border using a CSS offset-path animation, masked so only the border ring is painted, with configurable size, speed, anchor position, and gradient colors",
288
288
  category: "effects",
289
289
  status: "done",
290
290
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/border-beam" }],
@@ -405,7 +405,7 @@ export const registry = {
405
405
  "image-trail-cursor": {
406
406
  name: "ImageTrailCursor",
407
407
  slug: "image-trail-cursor",
408
- description: "Cursor-following image trail with 8 animation variants",
408
+ description: "Trail of images that spawn and animate along the cursor's path, powered by GSAP timelines, with 8 selectable variants spanning simple fades, momentum drift, rotation flings, and 3D perspective tilt",
409
409
  category: "effects",
410
410
  status: "done",
411
411
  tags: ["cursor", "animation", "images", "trail", "interactive"],
@@ -643,7 +643,7 @@ export const registry = {
643
643
  "ripple-button": {
644
644
  name: "RippleButton",
645
645
  slug: "ripple-button",
646
- description: "Button with ripple click effect",
646
+ description: "Button that spawns an expanding, fading ripple circle centered on the click point, with configurable color and duration",
647
647
  category: "buttons",
648
648
  status: "done",
649
649
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/ripple-button" }],
@@ -734,7 +734,7 @@ export const registry = {
734
734
  "bg-stars": {
735
735
  name: "StarsBackground",
736
736
  slug: "bg-stars",
737
- description: "Animated starfield background with parallax mouse tracking",
737
+ description: "Three-layer starfield background that scrolls continuously while parallax-shifting in response to cursor movement via spring physics, with stars rendered as box-shadow dots across near, mid, and far depth layers",
738
738
  category: "backgrounds",
739
739
  status: "done",
740
740
  credits: [
@@ -773,11 +773,11 @@ export const registry = {
773
773
  dock: {
774
774
  name: "Dock",
775
775
  slug: "dock",
776
- description: "macOS-style dock with icon magnification on hover",
776
+ description: "Icon dock where each item magnifies smoothly as the cursor approaches, sharing pointer position via Svelte context so every DockIcon scales by proximity within a configurable magnification and distance range",
777
777
  category: "navigation",
778
778
  status: "done",
779
779
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/dock" }],
780
- tags: ["dock", "navigation", "macos", "magnification", "hover"],
780
+ tags: ["dock", "navigation", "launcher", "magnification", "hover"],
781
781
  props: [
782
782
  {
783
783
  name: "magnification",
@@ -809,7 +809,7 @@ export const registry = {
809
809
  "fluid-cursor": {
810
810
  name: "FluidCursor",
811
811
  slug: "fluid-cursor",
812
- description: "WebGL fluid simulation that follows cursor movement",
812
+ description: "Full Navier-Stokes fluid simulation that swirls color in response to cursor and touch movement, running on WebGL2 with a WebGL1 fallback and an optional WebGPU HDR path for wide-gamut, brighter-than-white glow",
813
813
  category: "effects",
814
814
  status: "done",
815
815
  credits: [
@@ -952,7 +952,7 @@ export const registry = {
952
952
  "glow-border": {
953
953
  name: "GlowBorder",
954
954
  slug: "glow-border",
955
- description: "Animated glowing border effect with gradient support",
955
+ description: "Glowing ring that animates around a container's edge by sweeping a radial gradient's position, masked with CSS mask-composite so only the border ring is painted, supporting single or multi-color gradients",
956
956
  category: "effects",
957
957
  status: "done",
958
958
  credits: [
@@ -1100,7 +1100,7 @@ export const registry = {
1100
1100
  "flickering-grid": {
1101
1101
  name: "FlickeringGrid",
1102
1102
  slug: "flickering-grid",
1103
- description: "Canvas-based grid of squares with flickering opacity",
1103
+ description: "Canvas grid of squares that flicker in and out of opacity at random each frame, pausing automatically via IntersectionObserver when scrolled off-screen and resizing to fit its container via ResizeObserver",
1104
1104
  category: "backgrounds",
1105
1105
  status: "done",
1106
1106
  credits: [
@@ -1183,7 +1183,7 @@ export const registry = {
1183
1183
  "colourful-text": {
1184
1184
  name: "ColourfulText",
1185
1185
  slug: "colourful-text",
1186
- description: "Per-character color animation with shuffling colors",
1186
+ description: "Per-character text animation where each glyph transitions to a new color from a shuffled palette, staggered across characters and reshuffling automatically every 5 seconds using pure CSS transitions",
1187
1187
  category: "text",
1188
1188
  status: "done",
1189
1189
  credits: [
@@ -1238,7 +1238,7 @@ export const registry = {
1238
1238
  "hyper-text": {
1239
1239
  name: "HyperText",
1240
1240
  slug: "hyper-text",
1241
- description: "Character scramble effect that activates on hover",
1241
+ description: "Text that scrambles into random uppercase letters and resolves character-by-character back to the original on hover, with a staggered per-letter reveal and an optional animate-on-load mode",
1242
1242
  category: "text",
1243
1243
  status: "done",
1244
1244
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/hyper-text" }],
@@ -1321,7 +1321,7 @@ export const registry = {
1321
1321
  "sparkles-text": {
1322
1322
  name: "SparklesText",
1323
1323
  slug: "sparkles-text",
1324
- description: "Text with animated SVG sparkle stars overlay",
1324
+ description: "Text overlaid with small SVG star sparkles that scale, rotate, and fade in a continuous loop, each one regenerating at a random position once its lifespan expires, cycling between two configurable colors",
1325
1325
  category: "text",
1326
1326
  status: "done",
1327
1327
  credits: [{ source: "Aceternity UI", url: "https://ui.aceternity.com/components/sparkles" }],
@@ -1345,7 +1345,7 @@ export const registry = {
1345
1345
  "box-reveal": {
1346
1346
  name: "BoxReveal",
1347
1347
  slug: "box-reveal",
1348
- description: "Content reveal with sliding colored box animation",
1348
+ description: "Reveal animation where a solid color box slides left to right across content on viewport entry, unveiling it as it fades up into place underneath, triggered once via IntersectionObserver",
1349
1349
  category: "text",
1350
1350
  status: "done",
1351
1351
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/box-reveal" }],
@@ -1395,7 +1395,7 @@ export const registry = {
1395
1395
  "card-spotlight": {
1396
1396
  name: "CardSpotlight",
1397
1397
  slug: "card-spotlight",
1398
- description: "Card with mouse-following radial gradient spotlight overlay",
1398
+ description: "Card whose radial-gradient spotlight tracks the cursor as it moves inside, fading in on hover and parking itself off-canvas when the pointer leaves, with configurable gradient size, color, and opacity",
1399
1399
  category: "cards",
1400
1400
  status: "done",
1401
1401
  credits: [
@@ -1967,7 +1967,7 @@ export const registry = {
1967
1967
  ripple: {
1968
1968
  name: "Ripple",
1969
1969
  slug: "ripple",
1970
- description: "Concentric pulsing circles with ripple wave animation",
1970
+ description: "Set of concentric circles that pulse in a staggered wave, each ring larger, more transparent, and more delayed than the last, with the outermost ring rendered dashed to suggest a fading signal",
1971
1971
  category: "effects",
1972
1972
  status: "done",
1973
1973
  credits: [{ source: "Magic UI", url: "https://magicui.design/docs/components/ripple" }],
@@ -2125,7 +2125,7 @@ export const registry = {
2125
2125
  "matrix-rain": {
2126
2126
  name: "MatrixRain",
2127
2127
  slug: "matrix-rain",
2128
- description: "Canvas-based Matrix-style falling glyph rain with configurable color, speed, and density",
2128
+ description: "Canvas-based falling glyph rain with configurable color, speed, and density",
2129
2129
  category: "backgrounds",
2130
2130
  status: "done",
2131
2131
  tags: ["background", "matrix", "canvas", "animation", "glyphs", "cyberpunk"],
@@ -2134,7 +2134,7 @@ export const registry = {
2134
2134
  name: "color",
2135
2135
  type: "string",
2136
2136
  default: '"#00ff41"',
2137
- description: "Glyph color (the classic Matrix green)",
2137
+ description: "Glyph color (classic terminal green)",
2138
2138
  },
2139
2139
  {
2140
2140
  name: "speed",
@@ -2206,7 +2206,7 @@ export const registry = {
2206
2206
  "noise-reveal": {
2207
2207
  name: "NoiseReveal",
2208
2208
  slug: "noise-reveal",
2209
- description: "WebGL image reveal with a Perlin-noise dissolve mask, contracting radial gradient, and wave displacement, inspired by a Codrops shader effect",
2209
+ description: "WebGL image reveal with a Perlin-noise dissolve mask, contracting radial gradient, and wave displacement",
2210
2210
  category: "media",
2211
2211
  status: "done",
2212
2212
  credits: [
@@ -0,0 +1,36 @@
1
+ # RippleButton
2
+
3
+ A button that spawns a Material-style expanding ripple circle from the exact click position on every click, layered behind the button's content.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script lang="ts">
9
+ import { RippleButton } from "fancy-ui-svelte";
10
+ </script>
11
+
12
+ <RippleButton onclick={() => console.log("clicked")}>Click me</RippleButton>
13
+
14
+ <RippleButton rippleColor="#f472b6" duration={800} class="bg-primary text-primary-foreground">
15
+ Custom ripple
16
+ </RippleButton>
17
+ ```
18
+
19
+ ## Props
20
+
21
+ | Prop | Type | Default | Description |
22
+ | ------------- | --------- | ----------- | ---------------------------------- |
23
+ | `rippleColor` | `string` | `"#ADD8E6"` | Color of the ripple effect |
24
+ | `duration` | `number` | `600` | Animation duration in milliseconds |
25
+ | `class` | `string` | — | Additional CSS classes |
26
+ | `children` | `Snippet` | — | Button content |
27
+
28
+ `RippleButtonProps` also extends `HTMLButtonAttributes` (minus `class`), and any attributes not explicitly listed above (e.g. `type`, `aria-*`, `data-*`) are spread onto the rendered `<button>` via `...restProps`.
29
+
30
+ ## Implementation notes
31
+
32
+ - `onclick` is intercepted, not overwritten: the component's own `handleClick` always creates the ripple first, then calls the caller's `onclick` (if provided) with the original event — passing `onclick` works exactly as it would on a plain `<button>`.
33
+ - Ripple size is `Math.max(width, height)` of the button so a single circle always covers the whole button regardless of click position; position is computed from `event.clientX/Y` relative to the button's `getBoundingClientRect()`, centered on the click point.
34
+ - Each ripple is tracked in a `ripples` array keyed by `Date.now()` and removed via `setTimeout(duration)` after the animation finishes — rapid repeated clicks stack multiple concurrent ripple elements rather than restarting a single one.
35
+ - `duration` is threaded into the CSS animation via the `--ripple-duration` custom property, so the visual animation and the removal timeout always stay in sync even when `duration` changes between renders.
36
+ - No `prefers-reduced-motion` handling — the ripple scale/opacity animation plays on every click regardless of user preference.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import { render, cleanup, fireEvent } from "@testing-library/svelte";
2
+ import { createRawSnippet } from "svelte";
3
+ import { afterEach, describe, it, expect } from "vitest";
4
+ import SmoothCursor from "./SmoothCursor.svelte";
5
+ function textSnippet(html) {
6
+ return createRawSnippet(() => ({ render: () => html }));
7
+ }
8
+ describe("SmoothCursor", () => {
9
+ afterEach(cleanup);
10
+ it("renders hidden (opacity-0) before any pointer movement", () => {
11
+ const { container } = render(SmoothCursor);
12
+ const cursor = container.firstElementChild;
13
+ expect(cursor.className).toContain("opacity-0");
14
+ expect(cursor.className).not.toContain("opacity-100");
15
+ });
16
+ it("applies custom class names", () => {
17
+ const { container } = render(SmoothCursor, { props: { class: "my-cursor" } });
18
+ const cursor = container.firstElementChild;
19
+ expect(cursor.className).toContain("my-cursor");
20
+ });
21
+ it("renders the default arrow svg when no cursor snippet is provided", () => {
22
+ const { container } = render(SmoothCursor);
23
+ expect(container.querySelector("svg")).toBeTruthy();
24
+ });
25
+ it("renders a custom cursor snippet instead of the default arrow", () => {
26
+ const { container } = render(SmoothCursor, {
27
+ props: { cursor: textSnippet("<div data-testid='custom-cursor'></div>") },
28
+ });
29
+ expect(container.querySelector("svg")).toBeFalsy();
30
+ expect(container.querySelector("[data-testid='custom-cursor']")).toBeTruthy();
31
+ });
32
+ it("hides the native cursor on mount and restores it on unmount", () => {
33
+ const { unmount } = render(SmoothCursor);
34
+ expect(document.body.style.cursor).toBe("none");
35
+ unmount();
36
+ expect(document.body.style.cursor).toBe("");
37
+ });
38
+ it("becomes visible once the pointer moves, and unmount does not throw once the animation loop is running", async () => {
39
+ const { container, unmount } = render(SmoothCursor);
40
+ const cursor = container.firstElementChild;
41
+ await fireEvent.mouseMove(document, { clientX: 120, clientY: 80 });
42
+ expect(cursor.className).toContain("opacity-100");
43
+ expect(() => unmount()).not.toThrow();
44
+ });
45
+ });
@@ -0,0 +1,48 @@
1
+ # TerminalText
2
+
3
+ Streams an array of lines onto the screen character by character, terminal-style, with a blinking block cursor and an optional post-stream "glitch" effect that randomly swaps a visible character for a glyph for a moment.
4
+
5
+ ## Usage
6
+
7
+ ```svelte
8
+ <script lang="ts">
9
+ import { TerminalText } from "fancy-ui-svelte";
10
+ </script>
11
+
12
+ <div class="rounded-lg border bg-black p-5 text-green-400">
13
+ <TerminalText
14
+ lines={[
15
+ "[INFO] boot sequence complete",
16
+ "[WARN] memory fragmentation at sector 0x4A2F",
17
+ "[OK] all systems nominal",
18
+ ]}
19
+ speed={25}
20
+ />
21
+ </div>
22
+ ```
23
+
24
+ ```svelte
25
+ <!-- Glitch effect after streaming finishes, and a completion callback -->
26
+ <TerminalText lines={["Connection established."]} glitch onComplete={() => console.log("done")} />
27
+ ```
28
+
29
+ ## Props
30
+
31
+ | Prop | Type | Default | Description |
32
+ | ------------ | ------------ | ------- | ---------------------------------------------------------- |
33
+ | `lines` | `string[]` | — | Array of lines to stream character by character (required) |
34
+ | `speed` | `number` | `40` | Delay between each character in ms |
35
+ | `delay` | `number` | `0` | Initial delay before streaming starts, in ms |
36
+ | `cursor` | `boolean` | `true` | Show the blinking cursor |
37
+ | `cursorChar` | `string` | `"█"` | Character used as the cursor |
38
+ | `glitch` | `boolean` | `false` | Enable random character glitching after streaming |
39
+ | `class` | `string` | — | Additional CSS classes |
40
+ | `onComplete` | `() => void` | — | Called once, after the last line finishes streaming |
41
+
42
+ ## Implementation notes
43
+
44
+ - Streaming is scheduled with a flat list of `setTimeout` calls computed up front from a running `totalDelay` counter (one timeout per character, plus a `speed * 3` pause between lines), not a single interval — every timeout is tracked and cleared together (`clearAllTimeouts`) so restarting the stream never leaves stray callbacks running.
45
+ - Streaming and glitching are two independent `$effect`s. The streaming effect depends on `lines`, `speed`, and `delay` only (`void`-read to declare the dependency without using the value), so toggling `glitch` mid-stream does not restart the text. The glitch effect depends only on `glitch` and starts/stops its own self-rescheduling loop (`scheduleGlitch`, random 2–4s interval).
46
+ - The glitch loop picks a random visible character from a random non-empty line, swaps in a random glyph from a fixed set, and restores the original character after 100ms — it checks that the same `(lineIdx, charIdx)` is still the active glitch before restoring, so it can't clobber a different glitch that started in between.
47
+ - The blinking cursor is CSS-only (`animation: blink 1s step-end infinite`) and is rendered after whichever line is currently last — while streaming that's the in-progress line, once `done` it moves to its own trailing line.
48
+ - No `prefers-reduced-motion` handling — lines always stream character-by-character and the cursor always blinks, regardless of user preference.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,88 @@
1
+ import { render, cleanup } from "@testing-library/svelte";
2
+ import { afterEach, describe, it, expect, vi } from "vitest";
3
+ import TerminalText from "./TerminalText.svelte";
4
+ // The template has static whitespace between its {#each} and {#if} blocks
5
+ // that shows up as stray spaces in textContent depending on which branch is
6
+ // active. Stripping all whitespace isolates the actual streamed characters.
7
+ function text(el) {
8
+ return (el.textContent ?? "").replace(/\s+/g, "");
9
+ }
10
+ describe("TerminalText", () => {
11
+ afterEach(() => {
12
+ cleanup();
13
+ vi.useRealTimers();
14
+ });
15
+ it("renders nothing until the first scheduled tick, then streams characters over time and completes", async () => {
16
+ vi.useFakeTimers();
17
+ const onComplete = vi.fn();
18
+ const { container } = render(TerminalText, {
19
+ props: { lines: ["hi"], speed: 10, onComplete },
20
+ });
21
+ const wrapper = container.firstElementChild;
22
+ // The effect only schedules timeouts synchronously; none have fired yet.
23
+ expect(text(wrapper)).toBe("");
24
+ // t=0: empty line placeholder + blinking cursor appear.
25
+ await vi.advanceTimersByTimeAsync(1);
26
+ expect(text(wrapper)).toBe("█");
27
+ expect(onComplete).not.toHaveBeenCalled();
28
+ // t=10: first character streamed in.
29
+ await vi.advanceTimersByTimeAsync(9);
30
+ expect(text(wrapper)).toBe("h█");
31
+ // t=20: second character streamed in.
32
+ await vi.advanceTimersByTimeAsync(10);
33
+ expect(text(wrapper)).toBe("hi█");
34
+ expect(onComplete).not.toHaveBeenCalled();
35
+ // t=50: stream marked done, onComplete fires exactly once.
36
+ await vi.advanceTimersByTimeAsync(30);
37
+ expect(text(wrapper)).toBe("hi█");
38
+ expect(onComplete).toHaveBeenCalledTimes(1);
39
+ });
40
+ it("renders no cursor glyph anywhere when cursor is false", async () => {
41
+ vi.useFakeTimers();
42
+ const { container } = render(TerminalText, {
43
+ props: { lines: ["ok"], speed: 5, cursor: false },
44
+ });
45
+ await vi.advanceTimersByTimeAsync(25);
46
+ const wrapper = container.firstElementChild;
47
+ expect(text(wrapper)).toBe("ok");
48
+ expect(container.querySelector(".cursor-blink")).toBeFalsy();
49
+ });
50
+ it("streams multiple lines in order, one div per line", async () => {
51
+ vi.useFakeTimers();
52
+ const { container } = render(TerminalText, {
53
+ props: { lines: ["a", "bb"], speed: 10, cursor: false },
54
+ });
55
+ await vi.advanceTimersByTimeAsync(90);
56
+ const wrapper = container.firstElementChild;
57
+ const lineDivs = Array.from(wrapper.children);
58
+ expect(lineDivs).toHaveLength(2);
59
+ expect(lineDivs[0].textContent).toBe("a");
60
+ expect(lineDivs[1].textContent).toBe("bb");
61
+ });
62
+ it("applies custom class names alongside the base classes", () => {
63
+ const { container } = render(TerminalText, {
64
+ props: { lines: ["x"], class: "my-terminal" },
65
+ });
66
+ const wrapper = container.firstElementChild;
67
+ expect(wrapper.className).toContain("my-terminal");
68
+ expect(wrapper.className).toContain("font-mono");
69
+ });
70
+ it("uses a custom cursor character once the stream is done", async () => {
71
+ vi.useFakeTimers();
72
+ const { container } = render(TerminalText, {
73
+ props: { lines: ["a"], speed: 5, cursorChar: "_" },
74
+ });
75
+ // push@0, char@5, done@20 (5 + speed*3=15)
76
+ await vi.advanceTimersByTimeAsync(20);
77
+ const wrapper = container.firstElementChild;
78
+ expect(text(wrapper)).toBe("a_");
79
+ });
80
+ it("does not throw or leave dangling timers when glitch is enabled", async () => {
81
+ vi.useFakeTimers();
82
+ const { unmount } = render(TerminalText, {
83
+ props: { lines: ["glitchy line"], speed: 5, glitch: true },
84
+ });
85
+ await vi.advanceTimersByTimeAsync(5000);
86
+ expect(() => unmount()).not.toThrow();
87
+ });
88
+ });
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "fancy-ui-svelte",
3
3
  "private": false,
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
+ "description": "61 animated UI components for Svelte 5 — WebGL, GSAP and CSS motion, styled with Tailwind CSS v4.",
6
+ "author": "Rama Herbin",
7
+ "funding": "https://github.com/sponsors/RamaHerbin",
5
8
  "type": "module",
6
9
  "license": "MIT",
7
10
  "repository": {
@@ -15,10 +18,18 @@
15
18
  "keywords": [
16
19
  "svelte",
17
20
  "svelte5",
21
+ "sveltekit",
18
22
  "components",
23
+ "component-library",
19
24
  "animations",
25
+ "animated-components",
26
+ "motion",
20
27
  "ui",
21
- "tailwind"
28
+ "ui-library",
29
+ "tailwind",
30
+ "tailwindcss",
31
+ "webgl",
32
+ "typescript"
22
33
  ],
23
34
  "sideEffects": [
24
35
  "**/*.css",
@@ -110,6 +121,8 @@
110
121
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
111
122
  "check:registry": "node scripts/check-registry.mjs",
112
123
  "check:i18n": "node scripts/check-i18n.mjs",
124
+ "build:assets": "node scripts/build-synthwave-assets.mjs",
125
+ "build:og": "node scripts/build-og-images.mjs",
113
126
  "test": "vitest run",
114
127
  "test:watch": "vitest",
115
128
  "test:e2e": "playwright test",