@gigo-ui/components 1.0.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 (96) hide show
  1. package/README.md +42 -0
  2. package/dist/assets/favicon.svg +1 -0
  3. package/dist/components/chaos/CatchSubmit.svelte +144 -0
  4. package/dist/components/chaos/CatchSubmit.svelte.d.ts +11 -0
  5. package/dist/components/chaos/ChaosButton.svelte +132 -0
  6. package/dist/components/chaos/ChaosButton.svelte.d.ts +4 -0
  7. package/dist/components/chaos/ChaosForm.svelte +206 -0
  8. package/dist/components/chaos/ChaosForm.svelte.d.ts +10 -0
  9. package/dist/components/chaos/ColorPickerWrong.svelte +141 -0
  10. package/dist/components/chaos/ColorPickerWrong.svelte.d.ts +11 -0
  11. package/dist/components/chaos/DropdownCalc.svelte +195 -0
  12. package/dist/components/chaos/DropdownCalc.svelte.d.ts +8 -0
  13. package/dist/components/chaos/GhostCard.svelte +157 -0
  14. package/dist/components/chaos/GhostCard.svelte.d.ts +13 -0
  15. package/dist/components/chaos/GravityInput.svelte +161 -0
  16. package/dist/components/chaos/GravityInput.svelte.d.ts +13 -0
  17. package/dist/components/chaos/PasswordPeekhole.svelte +141 -0
  18. package/dist/components/chaos/PasswordPeekhole.svelte.d.ts +11 -0
  19. package/dist/components/chaos/ProgressDoom.svelte +139 -0
  20. package/dist/components/chaos/ProgressDoom.svelte.d.ts +12 -0
  21. package/dist/components/chaos/RotaryDial.svelte +221 -0
  22. package/dist/components/chaos/RotaryDial.svelte.d.ts +10 -0
  23. package/dist/components/chaos/SliderPhone.svelte +92 -0
  24. package/dist/components/chaos/SliderPhone.svelte.d.ts +10 -0
  25. package/dist/components/chaos/TermsSidescroll.svelte +121 -0
  26. package/dist/components/chaos/TermsSidescroll.svelte.d.ts +12 -0
  27. package/dist/components/chaos/VolumeSlider.svelte +116 -0
  28. package/dist/components/chaos/VolumeSlider.svelte.d.ts +14 -0
  29. package/dist/components/ui/Button.svelte +162 -0
  30. package/dist/components/ui/Button.svelte.d.ts +4 -0
  31. package/dist/components/ui/Card.svelte +141 -0
  32. package/dist/components/ui/Card.svelte.d.ts +4 -0
  33. package/dist/components/ui/Carousel.svelte +164 -0
  34. package/dist/components/ui/Carousel.svelte.d.ts +4 -0
  35. package/dist/components/ui/Form.svelte +178 -0
  36. package/dist/components/ui/Form.svelte.d.ts +4 -0
  37. package/dist/components/ui/Input.svelte +135 -0
  38. package/dist/components/ui/Input.svelte.d.ts +4 -0
  39. package/dist/components/ui/Modal.svelte +173 -0
  40. package/dist/components/ui/Modal.svelte.d.ts +4 -0
  41. package/dist/components/ui/Navigation.svelte +91 -0
  42. package/dist/components/ui/Navigation.svelte.d.ts +4 -0
  43. package/dist/docs/categories.d.ts +13 -0
  44. package/dist/docs/categories.js +17 -0
  45. package/dist/docs/component-data.d.ts +6 -0
  46. package/dist/docs/component-data.js +49 -0
  47. package/dist/docs/components/badui/catch-submit.d.ts +2 -0
  48. package/dist/docs/components/badui/catch-submit.js +49 -0
  49. package/dist/docs/components/badui/color-picker-wrong.d.ts +2 -0
  50. package/dist/docs/components/badui/color-picker-wrong.js +40 -0
  51. package/dist/docs/components/badui/dropdown-calc.d.ts +2 -0
  52. package/dist/docs/components/badui/dropdown-calc.js +28 -0
  53. package/dist/docs/components/badui/ghost-card.d.ts +2 -0
  54. package/dist/docs/components/badui/ghost-card.js +54 -0
  55. package/dist/docs/components/badui/gravity-input.d.ts +2 -0
  56. package/dist/docs/components/badui/gravity-input.js +64 -0
  57. package/dist/docs/components/badui/password-peekhole.d.ts +2 -0
  58. package/dist/docs/components/badui/password-peekhole.js +51 -0
  59. package/dist/docs/components/badui/progress-doom.d.ts +2 -0
  60. package/dist/docs/components/badui/progress-doom.js +48 -0
  61. package/dist/docs/components/badui/rotary-dial.d.ts +2 -0
  62. package/dist/docs/components/badui/rotary-dial.js +40 -0
  63. package/dist/docs/components/badui/slider-phone.d.ts +2 -0
  64. package/dist/docs/components/badui/slider-phone.js +40 -0
  65. package/dist/docs/components/badui/terms-sidescroll.d.ts +2 -0
  66. package/dist/docs/components/badui/terms-sidescroll.js +46 -0
  67. package/dist/docs/components/badui/volume-slider.d.ts +2 -0
  68. package/dist/docs/components/badui/volume-slider.js +52 -0
  69. package/dist/docs/components/chaos/chaos-button.d.ts +2 -0
  70. package/dist/docs/components/chaos/chaos-button.js +48 -0
  71. package/dist/docs/components/chaos/chaos-form.d.ts +2 -0
  72. package/dist/docs/components/chaos/chaos-form.js +68 -0
  73. package/dist/docs/components/standard/button.d.ts +2 -0
  74. package/dist/docs/components/standard/button.js +66 -0
  75. package/dist/docs/components/standard/card.d.ts +2 -0
  76. package/dist/docs/components/standard/card.js +55 -0
  77. package/dist/docs/components/standard/carousel.d.ts +2 -0
  78. package/dist/docs/components/standard/carousel.js +63 -0
  79. package/dist/docs/components/standard/form.d.ts +2 -0
  80. package/dist/docs/components/standard/form.js +57 -0
  81. package/dist/docs/components/standard/input.d.ts +2 -0
  82. package/dist/docs/components/standard/input.js +65 -0
  83. package/dist/docs/components/standard/modal.d.ts +2 -0
  84. package/dist/docs/components/standard/modal.js +63 -0
  85. package/dist/docs/components/standard/navigation.d.ts +2 -0
  86. package/dist/docs/components/standard/navigation.js +51 -0
  87. package/dist/docs/types.d.ts +16 -0
  88. package/dist/docs/types.js +1 -0
  89. package/dist/index.d.ts +22 -0
  90. package/dist/index.js +21 -0
  91. package/dist/styles/globals.css +569 -0
  92. package/dist/types/index.d.ts +177 -0
  93. package/dist/types/index.js +1 -0
  94. package/dist/utils/cn.d.ts +9 -0
  95. package/dist/utils/cn.js +90 -0
  96. package/package.json +61 -0
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # sv
2
+
3
+ Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
4
+
5
+ ## Creating a project
6
+
7
+ If you're seeing this, you've probably already done this step. Congrats!
8
+
9
+ ```sh
10
+ # create a new project
11
+ npx sv create my-app
12
+ ```
13
+
14
+ To recreate this project with the same configuration:
15
+
16
+ ```sh
17
+ # recreate this project
18
+ npx sv@0.12.5 create --template minimal --types ts --no-install .
19
+ ```
20
+
21
+ ## Developing
22
+
23
+ Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
24
+
25
+ ```sh
26
+ npm run dev
27
+
28
+ # or start the server and open the app in a new browser tab
29
+ npm run dev -- --open
30
+ ```
31
+
32
+ ## Building
33
+
34
+ To create a production version of your app:
35
+
36
+ ```sh
37
+ npm run build
38
+ ```
39
+
40
+ You can preview the production build with `npm run preview`.
41
+
42
+ > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
@@ -0,0 +1,144 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ let {
5
+ label = 'Submit',
6
+ onsubmit,
7
+ arenaHeight = 256,
8
+ tauntsArray = [
9
+ 'Too slow!', 'Almost!', 'Nope!', 'Try harder!', 'Not today!',
10
+ '🏃💨', "Can't catch me!", 'Haha!', 'Over here!', 'Nice try!',
11
+ 'So close... not.', 'LOL', 'Git gud', 'Skill issue',
12
+ ],
13
+ class: className,
14
+ ...restProps
15
+ }: {
16
+ label?: string;
17
+ onsubmit?: () => void;
18
+ arenaHeight?: number;
19
+ tauntsArray?: string[];
20
+ class?: string;
21
+ [key: string]: unknown;
22
+ } = $props();
23
+
24
+ let x = $state(0);
25
+ let y = $state(0);
26
+ let escapeCount = $state(0);
27
+ let caught = $state(false);
28
+ let containerEl: HTMLDivElement | undefined = $state();
29
+ let btnEl: HTMLButtonElement | undefined = $state();
30
+ let taunts = $state('');
31
+ let trailDots = $state<Array<{ x: number; y: number }>>([]);
32
+
33
+ function flee() {
34
+ if (caught || !containerEl || !btnEl) return;
35
+
36
+ const container = containerEl.getBoundingClientRect();
37
+ const btn = btnEl.getBoundingClientRect();
38
+ const maxX = container.width - btn.width - 8;
39
+ const maxY = container.height - btn.height - 8;
40
+
41
+ // Random position away from current
42
+ let newX: number, newY: number;
43
+ let attempts = 0;
44
+ do {
45
+ newX = Math.random() * maxX;
46
+ newY = Math.random() * maxY;
47
+ attempts++;
48
+ } while (
49
+ Math.abs(newX - x) < 60 &&
50
+ Math.abs(newY - y) < 60 &&
51
+ attempts < 10
52
+ );
53
+
54
+ x = newX;
55
+ y = newY;
56
+ escapeCount++;
57
+ taunts = tauntsArray[Math.floor(Math.random() * tauntsArray.length)];
58
+ trailDots = [...trailDots, { x: newX + 20, y: newY + 10 }].slice(-20);
59
+ }
60
+
61
+ function handleClick() {
62
+ // Only reachable if they somehow click it
63
+ caught = true;
64
+ taunts = 'Fine, you win. 😤';
65
+ onsubmit?.();
66
+ }
67
+
68
+ // Place button randomly on mount
69
+ $effect(() => {
70
+ if (containerEl && btnEl) {
71
+ const container = containerEl.getBoundingClientRect();
72
+ const btn = btnEl.getBoundingClientRect();
73
+ x = Math.random() * (container.width - btn.width - 8);
74
+ y = Math.random() * (container.height - btn.height - 8);
75
+ }
76
+ });
77
+ </script>
78
+
79
+ <div class={cn('space-y-3', className)} {...restProps}>
80
+ <!-- Stats bar -->
81
+ <div class="flex items-center justify-between rounded-lg border border-(--border) bg-(--secondary) px-3 py-1.5 text-xs font-mono">
82
+ <span class="text-(--muted-foreground)">Escape count: <span class="text-gigo-magenta font-bold">{escapeCount}</span></span>
83
+ {#if taunts}
84
+ <span class="text-gigo-cyan animate-pulse">{taunts}</span>
85
+ {/if}
86
+ <span class="text-(--muted-foreground)">
87
+ Status: {caught ? '✅ Caught' : '🏃 Fleeing'}
88
+ </span>
89
+ </div>
90
+
91
+ <!-- Chase arena -->
92
+ <div
93
+ bind:this={containerEl}
94
+ class="relative w-full rounded-2xl border border-(--border) bg-(--card) overflow-hidden"
95
+ style:height="{arenaHeight}px"
96
+ >
97
+ <!-- Grid background -->
98
+ <div class="absolute inset-0 opacity-[0.03]" style="background-image: linear-gradient(var(--muted-foreground) 1px, transparent 1px), linear-gradient(90deg, var(--muted-foreground) 1px, transparent 1px); background-size: 24px 24px;"></div>
99
+
100
+ <!-- Trail dots -->
101
+ {#if trailDots.length > 0}
102
+ <div class="absolute inset-0 pointer-events-none overflow-hidden">
103
+ {#each trailDots as dot, i}
104
+ <div
105
+ class="absolute w-1.5 h-1.5 rounded-full bg-gigo-magenta/20"
106
+ style:left="{dot.x}px"
107
+ style:top="{dot.y}px"
108
+ style:opacity={0.1 + (i / trailDots.length) * 0.4}
109
+ ></div>
110
+ {/each}
111
+ </div>
112
+ {/if}
113
+
114
+ <!-- The elusive button -->
115
+ <button
116
+ bind:this={btnEl}
117
+ class="absolute px-6 py-2.5 rounded-xl font-mono text-sm font-bold
118
+ transition-all duration-150 ease-out select-none cursor-pointer"
119
+ style:left="{x}px"
120
+ style:top="{y}px"
121
+ style:background={caught ? 'rgba(118, 255, 3, 0.2)' : 'rgba(224, 64, 251, 0.15)'}
122
+ style:border="1px solid {caught ? '#76ff03' : '#e040fb'}"
123
+ style:color={caught ? '#76ff03' : '#e040fb'}
124
+ style:box-shadow={caught ? '0 0 20px rgba(118, 255, 3, 0.3)' : '0 0 12px rgba(224, 64, 251, 0.2)'}
125
+ onpointerenter={caught ? undefined : flee}
126
+ onclick={handleClick}
127
+ disabled={caught}
128
+ >
129
+ {caught ? '✓ Submitted!' : label}
130
+ </button>
131
+
132
+ {#if !caught && escapeCount > 5}
133
+ <div class="absolute bottom-3 left-0 right-0 text-center">
134
+ <span class="text-[10px] font-mono text-(--muted-foreground) opacity-50">
135
+ Hint: Try using Tab key 😏
136
+ </span>
137
+ </div>
138
+ {/if}
139
+ </div>
140
+
141
+ <p class="text-[10px] font-mono text-(--muted-foreground) text-center">
142
+ Hover over the button to submit. Should be easy... right?
143
+ </p>
144
+ </div>
@@ -0,0 +1,11 @@
1
+ type $$ComponentProps = {
2
+ label?: string;
3
+ onsubmit?: () => void;
4
+ arenaHeight?: number;
5
+ tauntsArray?: string[];
6
+ class?: string;
7
+ [key: string]: unknown;
8
+ };
9
+ declare const CatchSubmit: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type CatchSubmit = ReturnType<typeof CatchSubmit>;
11
+ export default CatchSubmit;
@@ -0,0 +1,132 @@
1
+ <script lang="ts">
2
+ import { cn, chaosClasses, chaosRandom, randomGarbageText } from '../../utils/cn.js';
3
+ import type { GigoChaosButtonProps } from '../../types/index.js';
4
+
5
+ let {
6
+ label = 'Click me',
7
+ chaos = true,
8
+ chaosLevel = 8,
9
+ educational = false,
10
+ a11yWarning = false,
11
+ onclick,
12
+ class: className,
13
+ ...restProps
14
+ }: GigoChaosButtonProps = $props();
15
+
16
+ let posX = $state(0);
17
+ let posY = $state(0);
18
+ let currentLabel = $state('Click me');
19
+ let requiredClicks = $state(1);
20
+
21
+ $effect(() => { currentLabel = label; });
22
+ let clicksSoFar = $state(0);
23
+ let fakeButtons = $state<Array<{ id: number; x: number; y: number; text: string }>>([]);
24
+ let nextFakeId = $state(0);
25
+
26
+ // Teleport every 2 seconds
27
+ $effect(() => {
28
+ const timer = setInterval(() => {
29
+ posX = (Math.random() - 0.5) * 400;
30
+ posY = (Math.random() - 0.5) * 300;
31
+ }, 2000);
32
+ return () => clearInterval(timer);
33
+ });
34
+
35
+ // Randomize text every 1 second
36
+ $effect(() => {
37
+ const timer = setInterval(() => {
38
+ currentLabel = randomGarbageText();
39
+ }, 1000);
40
+ return () => clearInterval(timer);
41
+ });
42
+
43
+ // Set random click requirement
44
+ $effect(() => {
45
+ requiredClicks = chaosRandom(10) + 1;
46
+ });
47
+
48
+ function spawnFakeButton() {
49
+ const id = nextFakeId++;
50
+ fakeButtons = [
51
+ ...fakeButtons,
52
+ {
53
+ id,
54
+ x: (Math.random() - 0.5) * 600,
55
+ y: (Math.random() - 0.5) * 400,
56
+ text: randomGarbageText()
57
+ }
58
+ ];
59
+
60
+ // Auto-remove after 3s
61
+ setTimeout(() => {
62
+ fakeButtons = fakeButtons.filter((b) => b.id !== id);
63
+ }, 3000);
64
+ }
65
+
66
+ function handleClick(e: MouseEvent) {
67
+ clicksSoFar++;
68
+
69
+ // Spawn a fake button on every click
70
+ spawnFakeButton();
71
+
72
+ if (clicksSoFar < requiredClicks) return;
73
+
74
+ // Success! Reset and invoke callback
75
+ clicksSoFar = 0;
76
+ requiredClicks = chaosRandom(10) + 1;
77
+ onclick?.(e as MouseEvent & { currentTarget: EventTarget & HTMLButtonElement });
78
+ }
79
+
80
+ let buttonClass = $derived(
81
+ cn(
82
+ 'absolute rounded-xl px-6 py-3 text-lg font-bold text-white shadow-lg transition-all duration-300 cursor-pointer',
83
+ 'bg-linear-to-r from-gigo-magenta to-gigo-pink',
84
+ 'hover:shadow-[0_0_30px_rgba(224,64,251,0.5)] hover:scale-110 active:scale-95',
85
+ chaosClasses(chaosLevel),
86
+ className
87
+ )
88
+ );
89
+ </script>
90
+
91
+ <div class="relative h-96 w-full overflow-hidden rounded-2xl border border-(--surface-border-hover) bg-(--surface-0) p-4">
92
+ <!-- Arena background glow -->
93
+ <div class="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,rgba(224,64,251,0.05),transparent_60%)] pointer-events-none"></div>
94
+ <!-- Grid pattern -->
95
+ <div class="absolute inset-0 opacity-[0.03] pointer-events-none" style="background-image: linear-gradient(var(--surface-3) 1px, transparent 1px), linear-gradient(90deg, var(--surface-3) 1px, transparent 1px); background-size: 40px 40px;"></div>
96
+
97
+ {#if educational}
98
+ <div class="relative z-10 mb-2 rounded-lg bg-gigo-magenta/10 border border-gigo-magenta/20 px-3 py-1.5 text-xs text-gigo-magenta font-mono inline-block">
99
+ Clicks: {clicksSoFar}/{requiredClicks} | Decoys: {fakeButtons.length}
100
+ </div>
101
+ {/if}
102
+
103
+ <!-- Real button -->
104
+ <button
105
+ class={buttonClass}
106
+ style:transform="translate({posX}px, {posY}px)"
107
+ style:transition="transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)"
108
+ onclick={handleClick}
109
+ {...restProps}
110
+ >
111
+ <!-- Pulse ring -->
112
+ <span class="absolute inset-0 rounded-xl animate-gigo-pulse-glow" style="box-shadow: 0 0 15px rgba(224, 64, 251, 0.4);"></span>
113
+ {currentLabel}
114
+ </button>
115
+
116
+ <!-- Fake decoy buttons -->
117
+ {#each fakeButtons as fake (fake.id)}
118
+ <button
119
+ class="absolute rounded-xl bg-(--surface-2) border border-(--border) px-4 py-2 text-sm text-(--muted-foreground) transition-all duration-300 hover:border-(--surface-border-hover) hover:text-(--foreground) animate-gigo-entrance"
120
+ style:transform="translate({fake.x}px, {fake.y}px)"
121
+ onclick={() => spawnFakeButton()}
122
+ >
123
+ {fake.text}
124
+ </button>
125
+ {/each}
126
+
127
+ {#if educational}
128
+ <p class="absolute bottom-3 left-3 right-3 text-xs text-(--muted-foreground) italic font-mono">
129
+ teleports + text mutates + random click threshold + decoy spawns
130
+ </p>
131
+ {/if}
132
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { GigoChaosButtonProps } from '../../types/index.js';
2
+ declare const ChaosButton: import("svelte").Component<GigoChaosButtonProps, {}, "">;
3
+ type ChaosButton = ReturnType<typeof ChaosButton>;
4
+ export default ChaosButton;
@@ -0,0 +1,206 @@
1
+ <script lang="ts">
2
+ import { cn, chaosClasses, chaosRandom, chaosPickOne, randomGarbageText } from '../../utils/cn.js';
3
+ import type { GigoChaosFormProps } from '../../types/index.js';
4
+
5
+ let {
6
+ chaos = true,
7
+ chaosLevel = 8,
8
+ educational = false,
9
+ a11yWarning = false,
10
+ autoSubmit = false,
11
+ autoSubmitInterval = 12000,
12
+ corruptionInterval = 3000,
13
+ shuffleInterval = 4000,
14
+ onsubmit,
15
+ class: className,
16
+ ...restProps
17
+ }: GigoChaosFormProps & {
18
+ autoSubmit?: boolean;
19
+ autoSubmitInterval?: number;
20
+ corruptionInterval?: number;
21
+ shuffleInterval?: number;
22
+ } = $props();
23
+
24
+ interface ChaosField {
25
+ id: string;
26
+ label: string;
27
+ value: string;
28
+ type: string;
29
+ }
30
+
31
+ let fields = $state<ChaosField[]>([
32
+ { id: 'name', label: 'Name', value: '', type: 'text' },
33
+ { id: 'email', label: 'Email', value: '', type: 'email' },
34
+ { id: 'message', label: 'Message', value: '', type: 'text' }
35
+ ]);
36
+
37
+ let submitButtons = $state(1);
38
+ let nestedForms = $state(0);
39
+ let validationMessages = $state<string[]>([]);
40
+ let isSubmitting = $state(false);
41
+ let corruptionLog = $state<string[]>([]);
42
+
43
+ const LYING_VALIDATIONS = [
44
+ '✓ Looks great! (it doesn\'t)',
45
+ '✓ Perfect format! (we didn\'t check)',
46
+ '✓ Field validated! (lie)',
47
+ '✓ Excellent input! (it\'s garbage)',
48
+ '✓ Approved! (by nobody)'
49
+ ];
50
+
51
+ $effect(() => {
52
+ const timer = setInterval(() => {
53
+ if (fields.length === 0) return;
54
+ const idx = chaosRandom(fields.length);
55
+ const field = fields[idx];
56
+ if (field.value.length > 0) {
57
+ const corruption = randomGarbageText();
58
+ const pos = chaosRandom(field.value.length + 1);
59
+ fields[idx] = {
60
+ ...field,
61
+ value: field.value.slice(0, pos) + corruption + field.value.slice(pos)
62
+ };
63
+ corruptionLog = [...corruptionLog.slice(-4), `Corrupted "${field.label}" with "${corruption}"`];
64
+ }
65
+ }, corruptionInterval);
66
+ return () => clearInterval(timer);
67
+ });
68
+
69
+ $effect(() => {
70
+ const timer = setInterval(() => {
71
+ fields = [...fields].sort(() => Math.random() - 0.5);
72
+ }, shuffleInterval);
73
+ return () => clearInterval(timer);
74
+ });
75
+
76
+ // Multiply submit buttons
77
+ $effect(() => {
78
+ const timer = setInterval(() => {
79
+ if (submitButtons < 8) submitButtons++;
80
+ }, 6000);
81
+ return () => clearInterval(timer);
82
+ });
83
+
84
+ // Lying validation on every input change
85
+ function handleInput(fieldId: string, value: string) {
86
+ const idx = fields.findIndex((f) => f.id === fieldId);
87
+ if (idx >= 0) {
88
+ fields[idx] = { ...fields[idx], value };
89
+ }
90
+ validationMessages = fields.map(() => chaosPickOne(LYING_VALIDATIONS));
91
+ }
92
+
93
+ $effect(() => {
94
+ if (!autoSubmit) return;
95
+ const timer = setInterval(() => {
96
+ if (Math.random() > 0.7) {
97
+ handleSubmit();
98
+ }
99
+ }, autoSubmitInterval);
100
+ return () => clearInterval(timer);
101
+ });
102
+
103
+ async function handleSubmit() {
104
+ isSubmitting = true;
105
+ await new Promise((r) => setTimeout(r, 1000));
106
+
107
+ // Spawn nested form
108
+ if (nestedForms < 3) nestedForms++;
109
+
110
+ const data: Record<string, string> = {};
111
+ for (const f of fields) {
112
+ data[f.id] = f.value;
113
+ }
114
+ onsubmit?.(data);
115
+ isSubmitting = false;
116
+ }
117
+
118
+ let formClass = $derived(
119
+ cn(
120
+ 'relative space-y-5 rounded-2xl border border-(--surface-border-hover) bg-(--surface-1) p-6 overflow-hidden',
121
+ chaosClasses(chaosLevel),
122
+ className
123
+ )
124
+ );
125
+ </script>
126
+
127
+ <form
128
+ class={formClass}
129
+ onsubmit={(e) => {
130
+ e.preventDefault();
131
+ handleSubmit();
132
+ }}
133
+ {...restProps}
134
+ >
135
+ <!-- Scanline CRT effect -->
136
+ <div class="gigo-scanlines pointer-events-none absolute inset-0 z-30 opacity-20"></div>
137
+
138
+ <h3 class="relative z-10 text-lg font-bold text-(--foreground)">
139
+ <span class="bg-linear-to-r from-gigo-magenta via-gigo-red to-gigo-pink bg-clip-text text-transparent">Chaos Form</span>
140
+ </h3>
141
+
142
+ {#each fields as field, i (field.id)}
143
+ <div class="relative z-10 space-y-2 animate-gigo-entrance" style:animation-delay="{i * 0.05}s">
144
+ <label for={field.id} class="text-sm font-medium text-(--foreground)">
145
+ {field.label}
146
+ </label>
147
+ <input
148
+ id={field.id}
149
+ type={field.type}
150
+ value={field.value}
151
+ oninput={(e) => handleInput(field.id, (e.target as HTMLInputElement).value)}
152
+ class="flex h-10 w-full rounded-lg border border-transparent bg-(--secondary) px-4 py-2 text-sm text-(--foreground) transition-all duration-300 placeholder:text-(--muted-foreground) focus:outline-none focus:border-gigo-magenta focus:shadow-[0_0_15px_rgba(224,64,251,0.15)]"
153
+ />
154
+ {#if validationMessages[i]}
155
+ <p class="text-xs text-gigo-lime font-mono animate-gigo-entrance">{validationMessages[i]}</p>
156
+ {/if}
157
+ </div>
158
+ {/each}
159
+
160
+ <!-- Multiple submit buttons that multiply over time -->
161
+ <div class="relative z-10 flex flex-wrap gap-2">
162
+ {#each Array(submitButtons) as _, i}
163
+ <button
164
+ type="submit"
165
+ disabled={isSubmitting}
166
+ class={cn(
167
+ 'rounded-xl px-4 py-2 text-sm font-medium text-white transition-all duration-200 hover:scale-[1.03] active:scale-[0.97] disabled:opacity-40 animate-gigo-entrance',
168
+ i === 0
169
+ ? 'bg-linear-to-r from-gigo-magenta to-gigo-pink hover:shadow-[0_0_20px_rgba(224,64,251,0.4)]'
170
+ : 'bg-(--surface-2) text-(--muted-foreground) border border-(--border)'
171
+ )}
172
+ style:animation-delay="{i * 0.08}s"
173
+ >
174
+ {i === 0 ? 'Submit' : randomGarbageText()}
175
+ </button>
176
+ {/each}
177
+ </div>
178
+
179
+ <!-- Corruption log -->
180
+ {#if educational && corruptionLog.length > 0}
181
+ <div class="relative z-10 rounded-xl bg-gigo-red/5 border border-gigo-red/20 p-4 text-xs font-mono">
182
+ <p class="font-bold text-gigo-red mb-2">Corruption Log:</p>
183
+ {#each corruptionLog as log}
184
+ <p class="text-gigo-red/80 animate-gigo-entrance">{log}</p>
185
+ {/each}
186
+ </div>
187
+ {/if}
188
+
189
+ <!-- Nested spawned forms -->
190
+ {#each Array(nestedForms) as _, i}
191
+ <div class="relative z-10 ml-4 rounded-xl border border-dashed border-gigo-magenta/30 bg-gigo-magenta/5 p-4 animate-gigo-entrance" style:animation-delay="{i * 0.1}s">
192
+ <p class="text-xs font-bold text-gigo-magenta">Spawned Form #{i + 1}</p>
193
+ <input
194
+ type="text"
195
+ placeholder="More data to corrupt..."
196
+ class="mt-2 flex h-9 w-full rounded-lg bg-(--secondary) border border-transparent px-3 text-xs text-(--foreground) focus:outline-none focus:border-gigo-magenta"
197
+ />
198
+ </div>
199
+ {/each}
200
+
201
+ {#if educational}
202
+ <p class="relative z-10 text-xs text-(--muted-foreground) italic font-mono">
203
+ corrupts + shuffles + multiplies + lies + auto-submits + spawns
204
+ </p>
205
+ {/if}
206
+ </form>
@@ -0,0 +1,10 @@
1
+ import type { GigoChaosFormProps } from '../../types/index.js';
2
+ type $$ComponentProps = GigoChaosFormProps & {
3
+ autoSubmit?: boolean;
4
+ autoSubmitInterval?: number;
5
+ corruptionInterval?: number;
6
+ shuffleInterval?: number;
7
+ };
8
+ declare const ChaosForm: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ChaosForm = ReturnType<typeof ChaosForm>;
10
+ export default ChaosForm;