trama-ui 0.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.
Files changed (285) hide show
  1. package/CATALOG.md +1237 -0
  2. package/LICENSE +21 -0
  3. package/README.md +78 -0
  4. package/THIRD-PARTY.md +4 -0
  5. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown.png +0 -0
  6. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_inlay.png +0 -0
  7. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_pressed.png +0 -0
  8. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey.png +0 -0
  9. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_inlay.png +0 -0
  10. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_pressed.png +0 -0
  11. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan.png +0 -0
  12. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_inlay.png +0 -0
  13. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_pressed.png +0 -0
  14. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white.png +0 -0
  15. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_inlay.png +0 -0
  16. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_pressed.png +0 -0
  17. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue.png +0 -0
  18. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue_pressed.png +0 -0
  19. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green.png +0 -0
  20. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green_pressed.png +0 -0
  21. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey.png +0 -0
  22. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey_pressed.png +0 -0
  23. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red.png +0 -0
  24. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red_pressed.png +0 -0
  25. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow.png +0 -0
  26. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow_pressed.png +0 -0
  27. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue.png +0 -0
  28. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue_pressed.png +0 -0
  29. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green.png +0 -0
  30. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green_pressed.png +0 -0
  31. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red.png +0 -0
  32. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red_pressed.png +0 -0
  33. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow.png +0 -0
  34. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow_pressed.png +0 -0
  35. package/assets/pixel/kenney-pixel-ui/9-slice/list.png +0 -0
  36. package/assets/pixel/kenney-pixel-ui/9-slice/space.png +0 -0
  37. package/assets/pixel/kenney-pixel-ui/9-slice/space_inlay.png +0 -0
  38. package/assets/pixel/kenney-pixel-ui/License.txt +23 -0
  39. package/assets/pixel/kenney-pixel-ui/spritesheet/sheet.png +0 -0
  40. package/bin/trama-ui.mjs +27 -0
  41. package/dist/components/ui/Accordion.d.ts +12 -0
  42. package/dist/components/ui/Accordion.js +12 -0
  43. package/dist/components/ui/Alert.d.ts +15 -0
  44. package/dist/components/ui/Alert.js +15 -0
  45. package/dist/components/ui/ArticlesSection.d.ts +30 -0
  46. package/dist/components/ui/ArticlesSection.js +18 -0
  47. package/dist/components/ui/AsciiBackground.d.ts +58 -0
  48. package/dist/components/ui/AsciiBackground.js +173 -0
  49. package/dist/components/ui/AsciiCard.d.ts +78 -0
  50. package/dist/components/ui/AsciiCard.js +253 -0
  51. package/dist/components/ui/AsciiChart.d.ts +18 -0
  52. package/dist/components/ui/AsciiChart.js +47 -0
  53. package/dist/components/ui/Badge.d.ts +15 -0
  54. package/dist/components/ui/Badge.js +8 -0
  55. package/dist/components/ui/BitmapText.d.ts +25 -0
  56. package/dist/components/ui/BitmapText.js +21 -0
  57. package/dist/components/ui/BlogCard.d.ts +33 -0
  58. package/dist/components/ui/BlogCard.js +13 -0
  59. package/dist/components/ui/Breadcrumbs.d.ts +9 -0
  60. package/dist/components/ui/Breadcrumbs.js +7 -0
  61. package/dist/components/ui/Button.d.ts +74 -0
  62. package/dist/components/ui/Button.js +48 -0
  63. package/dist/components/ui/CTASection.d.ts +13 -0
  64. package/dist/components/ui/CTASection.js +7 -0
  65. package/dist/components/ui/Carousel.d.ts +30 -0
  66. package/dist/components/ui/Carousel.js +80 -0
  67. package/dist/components/ui/CheckboxGroup.d.ts +11 -0
  68. package/dist/components/ui/CheckboxGroup.js +20 -0
  69. package/dist/components/ui/CodeBlock.d.ts +11 -0
  70. package/dist/components/ui/CodeBlock.js +20 -0
  71. package/dist/components/ui/ContactForm.d.ts +25 -0
  72. package/dist/components/ui/ContactForm.js +26 -0
  73. package/dist/components/ui/CrtOverlay.d.ts +10 -0
  74. package/dist/components/ui/CrtOverlay.js +5 -0
  75. package/dist/components/ui/Divider.d.ts +12 -0
  76. package/dist/components/ui/Divider.js +7 -0
  77. package/dist/components/ui/Drawer.d.ts +16 -0
  78. package/dist/components/ui/Drawer.js +25 -0
  79. package/dist/components/ui/Dropdown.d.ts +10 -0
  80. package/dist/components/ui/Dropdown.js +21 -0
  81. package/dist/components/ui/FAQSection.d.ts +14 -0
  82. package/dist/components/ui/FAQSection.js +7 -0
  83. package/dist/components/ui/FeatureGrid.d.ts +18 -0
  84. package/dist/components/ui/FeatureGrid.js +14 -0
  85. package/dist/components/ui/Footer.d.ts +14 -0
  86. package/dist/components/ui/Footer.js +16 -0
  87. package/dist/components/ui/GlyphCursor.d.ts +22 -0
  88. package/dist/components/ui/GlyphCursor.js +50 -0
  89. package/dist/components/ui/GridBackground.d.ts +15 -0
  90. package/dist/components/ui/GridBackground.js +6 -0
  91. package/dist/components/ui/Hero.d.ts +21 -0
  92. package/dist/components/ui/Hero.js +12 -0
  93. package/dist/components/ui/HoverFX.d.ts +29 -0
  94. package/dist/components/ui/HoverFX.js +75 -0
  95. package/dist/components/ui/Icon.d.ts +33 -0
  96. package/dist/components/ui/Icon.js +29 -0
  97. package/dist/components/ui/ImageGallery.d.ts +31 -0
  98. package/dist/components/ui/ImageGallery.js +79 -0
  99. package/dist/components/ui/LogoCloud.d.ts +12 -0
  100. package/dist/components/ui/LogoCloud.js +9 -0
  101. package/dist/components/ui/MagneticButton.d.ts +12 -0
  102. package/dist/components/ui/MagneticButton.js +49 -0
  103. package/dist/components/ui/Marquee.d.ts +29 -0
  104. package/dist/components/ui/Marquee.js +15 -0
  105. package/dist/components/ui/Modal.d.ts +40 -0
  106. package/dist/components/ui/Modal.js +51 -0
  107. package/dist/components/ui/NavBar.d.ts +14 -0
  108. package/dist/components/ui/NavBar.js +12 -0
  109. package/dist/components/ui/NeonSign.d.ts +14 -0
  110. package/dist/components/ui/NeonSign.js +7 -0
  111. package/dist/components/ui/OtpInput.d.ts +9 -0
  112. package/dist/components/ui/OtpInput.js +27 -0
  113. package/dist/components/ui/Pagination.d.ts +10 -0
  114. package/dist/components/ui/Pagination.js +24 -0
  115. package/dist/components/ui/Panel.d.ts +21 -0
  116. package/dist/components/ui/Panel.js +8 -0
  117. package/dist/components/ui/PixelFrame.d.ts +61 -0
  118. package/dist/components/ui/PixelFrame.js +29 -0
  119. package/dist/components/ui/Presence.d.ts +51 -0
  120. package/dist/components/ui/Presence.js +343 -0
  121. package/dist/components/ui/PricingCard.d.ts +30 -0
  122. package/dist/components/ui/PricingCard.js +21 -0
  123. package/dist/components/ui/PricingSection.d.ts +35 -0
  124. package/dist/components/ui/PricingSection.js +21 -0
  125. package/dist/components/ui/Progress.d.ts +17 -0
  126. package/dist/components/ui/Progress.js +9 -0
  127. package/dist/components/ui/RadioGroup.d.ts +10 -0
  128. package/dist/components/ui/RadioGroup.js +11 -0
  129. package/dist/components/ui/RangeSlider.d.ts +14 -0
  130. package/dist/components/ui/RangeSlider.js +11 -0
  131. package/dist/components/ui/Reveal.d.ts +48 -0
  132. package/dist/components/ui/Reveal.js +181 -0
  133. package/dist/components/ui/SceneFlash.d.ts +14 -0
  134. package/dist/components/ui/SceneFlash.js +51 -0
  135. package/dist/components/ui/ScrambleText.d.ts +31 -0
  136. package/dist/components/ui/ScrambleText.js +51 -0
  137. package/dist/components/ui/ScrollArea.d.ts +15 -0
  138. package/dist/components/ui/ScrollArea.js +18 -0
  139. package/dist/components/ui/SectionHeader.d.ts +12 -0
  140. package/dist/components/ui/SectionHeader.js +6 -0
  141. package/dist/components/ui/Select.d.ts +17 -0
  142. package/dist/components/ui/Select.js +31 -0
  143. package/dist/components/ui/Skeleton.d.ts +12 -0
  144. package/dist/components/ui/Skeleton.js +18 -0
  145. package/dist/components/ui/Spinner.d.ts +15 -0
  146. package/dist/components/ui/Spinner.js +24 -0
  147. package/dist/components/ui/Spotlight.d.ts +16 -0
  148. package/dist/components/ui/Spotlight.js +16 -0
  149. package/dist/components/ui/Sprite.d.ts +27 -0
  150. package/dist/components/ui/Sprite.js +19 -0
  151. package/dist/components/ui/StatCounter.d.ts +24 -0
  152. package/dist/components/ui/StatCounter.js +70 -0
  153. package/dist/components/ui/StatsSection.d.ts +14 -0
  154. package/dist/components/ui/StatsSection.js +13 -0
  155. package/dist/components/ui/Stepper.d.ts +10 -0
  156. package/dist/components/ui/Stepper.js +7 -0
  157. package/dist/components/ui/Table.d.ts +10 -0
  158. package/dist/components/ui/Table.js +8 -0
  159. package/dist/components/ui/Tabs.d.ts +12 -0
  160. package/dist/components/ui/Tabs.js +18 -0
  161. package/dist/components/ui/TeamSection.d.ts +18 -0
  162. package/dist/components/ui/TeamSection.js +19 -0
  163. package/dist/components/ui/TerminalTyper.d.ts +20 -0
  164. package/dist/components/ui/TerminalTyper.js +45 -0
  165. package/dist/components/ui/Testimonial.d.ts +19 -0
  166. package/dist/components/ui/Testimonial.js +9 -0
  167. package/dist/components/ui/TestimonialSection.d.ts +20 -0
  168. package/dist/components/ui/TestimonialSection.js +15 -0
  169. package/dist/components/ui/TextField.d.ts +26 -0
  170. package/dist/components/ui/TextField.js +13 -0
  171. package/dist/components/ui/TextmodeBackground.d.ts +52 -0
  172. package/dist/components/ui/TextmodeBackground.js +83 -0
  173. package/dist/components/ui/Timeline.d.ts +13 -0
  174. package/dist/components/ui/Timeline.js +8 -0
  175. package/dist/components/ui/Toast.d.ts +30 -0
  176. package/dist/components/ui/Toast.js +47 -0
  177. package/dist/components/ui/Toggle.d.ts +13 -0
  178. package/dist/components/ui/Toggle.js +10 -0
  179. package/dist/components/ui/Tooltip.d.ts +20 -0
  180. package/dist/components/ui/Tooltip.js +20 -0
  181. package/dist/components/ui/Typewriter.d.ts +20 -0
  182. package/dist/components/ui/Typewriter.js +46 -0
  183. package/dist/components/ui/VideoPlayer.d.ts +81 -0
  184. package/dist/components/ui/VideoPlayer.js +379 -0
  185. package/dist/components/ui/fill.d.ts +32 -0
  186. package/dist/components/ui/fill.js +27 -0
  187. package/dist/components/ui/intent.d.ts +20 -0
  188. package/dist/components/ui/intent.js +23 -0
  189. package/dist/components/ui/styles/kit.css +31 -0
  190. package/dist/components/ui/styles/ui-carousel.css +113 -0
  191. package/dist/components/ui/styles/ui-kit.css +1135 -0
  192. package/dist/components/ui/styles/ui-video.css +75 -0
  193. package/dist/components/ui/swiperCss.d.ts +15 -0
  194. package/dist/components/ui/swiperCss.js +15 -0
  195. package/dist/components/ui/variants.d.ts +12 -0
  196. package/dist/components/ui/variants.js +10 -0
  197. package/dist/index.d.ts +77 -0
  198. package/dist/index.js +77 -0
  199. package/dist/lib/asciify/painters.d.ts +25 -0
  200. package/dist/lib/asciify/painters.js +604 -0
  201. package/dist/lib/asciify/pointer.d.ts +12 -0
  202. package/dist/lib/asciify/pointer.js +54 -0
  203. package/dist/lib/asciify/shape-painters.d.ts +44 -0
  204. package/dist/lib/asciify/shape-painters.js +335 -0
  205. package/dist/lib/asciify/theme-painters.d.ts +8 -0
  206. package/dist/lib/asciify/theme-painters.js +437 -0
  207. package/dist/lib/cells.d.ts +46 -0
  208. package/dist/lib/cells.js +114 -0
  209. package/dist/lib/scroll/store.d.ts +21 -0
  210. package/dist/lib/scroll/store.js +25 -0
  211. package/dist/lib/sketches/aurora.d.ts +3 -0
  212. package/dist/lib/sketches/aurora.js +42 -0
  213. package/dist/lib/sketches/fire.d.ts +3 -0
  214. package/dist/lib/sketches/fire.js +80 -0
  215. package/dist/lib/sketches/flow.d.ts +3 -0
  216. package/dist/lib/sketches/flow.js +70 -0
  217. package/dist/lib/sketches/life.d.ts +3 -0
  218. package/dist/lib/sketches/life.js +107 -0
  219. package/dist/lib/sketches/matrix.d.ts +3 -0
  220. package/dist/lib/sketches/matrix.js +64 -0
  221. package/dist/lib/sketches/ocean.d.ts +3 -0
  222. package/dist/lib/sketches/ocean.js +56 -0
  223. package/dist/lib/sketches/plasma.d.ts +3 -0
  224. package/dist/lib/sketches/plasma.js +29 -0
  225. package/dist/lib/sketches/registry.d.ts +6 -0
  226. package/dist/lib/sketches/registry.js +60 -0
  227. package/dist/lib/sketches/ripples.d.ts +3 -0
  228. package/dist/lib/sketches/ripples.js +84 -0
  229. package/dist/lib/sketches/scroll-assemble.d.ts +8 -0
  230. package/dist/lib/sketches/scroll-assemble.js +90 -0
  231. package/dist/lib/sketches/scroll-parallax.d.ts +6 -0
  232. package/dist/lib/sketches/scroll-parallax.js +76 -0
  233. package/dist/lib/sketches/scroll-rain.d.ts +6 -0
  234. package/dist/lib/sketches/scroll-rain.js +82 -0
  235. package/dist/lib/sketches/scroll-stats.d.ts +7 -0
  236. package/dist/lib/sketches/scroll-stats.js +81 -0
  237. package/dist/lib/sketches/scroll-terminal.d.ts +6 -0
  238. package/dist/lib/sketches/scroll-terminal.js +87 -0
  239. package/dist/lib/sketches/scroll-zoom.d.ts +7 -0
  240. package/dist/lib/sketches/scroll-zoom.js +49 -0
  241. package/dist/lib/sketches/starfield.d.ts +3 -0
  242. package/dist/lib/sketches/starfield.js +54 -0
  243. package/dist/lib/sketches/text-3d.d.ts +6 -0
  244. package/dist/lib/sketches/text-3d.js +62 -0
  245. package/dist/lib/sketches/text-combo-figlet.d.ts +6 -0
  246. package/dist/lib/sketches/text-combo-figlet.js +91 -0
  247. package/dist/lib/sketches/text-glitch.d.ts +3 -0
  248. package/dist/lib/sketches/text-glitch.js +57 -0
  249. package/dist/lib/sketches/text-marquee.d.ts +3 -0
  250. package/dist/lib/sketches/text-marquee.js +35 -0
  251. package/dist/lib/sketches/text-particles.d.ts +9 -0
  252. package/dist/lib/sketches/text-particles.js +91 -0
  253. package/dist/lib/sketches/text-rain.d.ts +3 -0
  254. package/dist/lib/sketches/text-rain.js +73 -0
  255. package/dist/lib/sketches/text-scramble.d.ts +6 -0
  256. package/dist/lib/sketches/text-scramble.js +49 -0
  257. package/dist/lib/sketches/text-wave.d.ts +3 -0
  258. package/dist/lib/sketches/text-wave.js +43 -0
  259. package/dist/lib/sketches/theme-orbit.d.ts +6 -0
  260. package/dist/lib/sketches/theme-orbit.js +119 -0
  261. package/dist/lib/sketches/theme-pulse.d.ts +7 -0
  262. package/dist/lib/sketches/theme-pulse.js +84 -0
  263. package/dist/lib/sketches/tunnel.d.ts +3 -0
  264. package/dist/lib/sketches/tunnel.js +28 -0
  265. package/dist/lib/text/banner.d.ts +5 -0
  266. package/dist/lib/text/banner.js +35 -0
  267. package/dist/lib/text/mask.d.ts +40 -0
  268. package/dist/lib/text/mask.js +143 -0
  269. package/dist/lib/text/store.d.ts +23 -0
  270. package/dist/lib/text/store.js +86 -0
  271. package/dist/lib/ui/card-fields.d.ts +52 -0
  272. package/dist/lib/ui/card-fields.js +193 -0
  273. package/dist/lib/ui/fonts.d.ts +56 -0
  274. package/dist/lib/ui/fonts.js +94 -0
  275. package/dist/lib/ui/fx.d.ts +42 -0
  276. package/dist/lib/ui/fx.js +162 -0
  277. package/dist/lib/ui/icons.d.ts +437 -0
  278. package/dist/lib/ui/icons.js +314 -0
  279. package/dist/lib/ui/placeholder.d.ts +11 -0
  280. package/dist/lib/ui/placeholder.js +65 -0
  281. package/dist/lib/ui/tokens.d.ts +49 -0
  282. package/dist/lib/ui/tokens.js +129 -0
  283. package/dist/lib/ui/useTokens.d.ts +10 -0
  284. package/dist/lib/ui/useTokens.js +32 -0
  285. package/package.json +80 -0
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useRef, useState } from "react";
4
+ import { vcls } from "./variants.js";
5
+ /** Casillas de código de verificación: el foco avanza solo al escribir y retrocede con Retroceso. */
6
+ export default function OtpInput({ length = 6, size = "md", variant = "glass", className = "" }) {
7
+ const n = Math.max(2, Math.min(8, length));
8
+ const [values, setValues] = useState(() => Array(n).fill(""));
9
+ const refs = useRef([]);
10
+ const setAt = (i, raw) => {
11
+ const c = raw.slice(-1).replace(/[^a-zA-Z0-9]/g, "");
12
+ setValues((prev) => {
13
+ const next = [...prev];
14
+ next[i] = c;
15
+ return next;
16
+ });
17
+ if (c && i < n - 1)
18
+ refs.current[i + 1]?.focus();
19
+ };
20
+ const onKeyDown = (i, e) => {
21
+ if (e.key === "Backspace" && !values[i] && i > 0)
22
+ refs.current[i - 1]?.focus();
23
+ };
24
+ return (_jsx("div", { className: `ui-otp ui-otp--${size} ${className}`, children: Array.from({ length: n }, (_, i) => (_jsx("input", { ref: (el) => {
25
+ refs.current[i] = el;
26
+ }, className: `ui-otp__box ui-surface ${vcls(variant)}`, inputMode: "text", maxLength: 1, value: values[i], onChange: (e) => setAt(i, e.target.value), onKeyDown: (e) => onKeyDown(i, e), "aria-label": `Dígito ${i + 1} de ${n}` }, i))) }));
27
+ }
@@ -0,0 +1,10 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type PaginationProps = {
3
+ total?: number;
4
+ defaultPage?: number;
5
+ siblingCount?: number;
6
+ variant?: Variant;
7
+ className?: string;
8
+ };
9
+ /** Paginación numérica con elipsis cuando hay muchas páginas. Gestiona su propia página activa. */
10
+ export default function Pagination({ total, defaultPage, siblingCount, variant, className }: PaginationProps): import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { vcls } from "./variants.js";
5
+ /** Paginación numérica con elipsis cuando hay muchas páginas. Gestiona su propia página activa. */
6
+ export default function Pagination({ total = 9, defaultPage = 1, siblingCount = 1, variant = "glass", className = "" }) {
7
+ const [page, setPage] = useState(() => Math.min(Math.max(1, defaultPage), Math.max(1, total)));
8
+ useEffect(() => setPage(Math.min(Math.max(1, defaultPage), Math.max(1, total))), [defaultPage, total]);
9
+ const pages = new Set([1, total, page]);
10
+ for (let d = 1; d <= siblingCount; d++) {
11
+ pages.add(page - d);
12
+ pages.add(page + d);
13
+ }
14
+ const list = Array.from(pages)
15
+ .filter((p) => p >= 1 && p <= total)
16
+ .sort((a, b) => a - b);
17
+ const withGaps = [];
18
+ list.forEach((p, i) => {
19
+ if (i > 0 && p - list[i - 1] > 1)
20
+ withGaps.push("gap");
21
+ withGaps.push(p);
22
+ });
23
+ return (_jsxs("nav", { "aria-label": "Paginaci\u00F3n", className: `ui-page ${vcls(variant)} ${className}`, children: [_jsx("button", { type: "button", className: "ui-page__nav", disabled: page <= 1, onClick: () => setPage((p) => p - 1), "aria-label": "P\u00E1gina anterior", children: "\u2039" }), withGaps.map((p, i) => p === "gap" ? (_jsx("span", { className: "ui-page__gap", children: "\u2026" }, `gap-${i}`)) : (_jsx("button", { type: "button", className: `ui-page__num ${p === page ? "is-on" : ""}`, "aria-current": p === page || undefined, onClick: () => setPage(p), children: p }, p))), _jsx("button", { type: "button", className: "ui-page__nav", disabled: page >= total, onClick: () => setPage((p) => p + 1), "aria-label": "P\u00E1gina siguiente", children: "\u203A" })] }));
24
+ }
@@ -0,0 +1,21 @@
1
+ import { type CardFill, type CardPattern, type CardTone } from "./fill.js";
2
+ import { type Variant } from "./variants.js";
3
+ export type PanelProps = {
4
+ title?: string;
5
+ body?: string;
6
+ footer?: string;
7
+ /** texto de la barra superior; vacío = sin barra */
8
+ bar?: string;
9
+ variant?: Variant;
10
+ /** dibuja un carácter en dos esquinas */
11
+ cornerGlyph?: string;
12
+ /** fondo de la tarjeta: surface · tint · gradient · accent (invertida) · pattern · image (ver fill.ts) */
13
+ fill?: CardFill;
14
+ pattern?: CardPattern;
15
+ tone?: CardTone;
16
+ /** con fill="image": URL/ruta o `gen:N` */
17
+ image?: string;
18
+ className?: string;
19
+ };
20
+ /** Superficie genérica del sistema: barra opcional, título, cuerpo y pie. Base de cualquier tarjeta simple. */
21
+ export default function Panel({ title, body, footer, bar, variant, cornerGlyph, fill, pattern, tone, image, className }: PanelProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { fillProps } from "./fill.js";
3
+ import { vcls } from "./variants.js";
4
+ /** Superficie genérica del sistema: barra opcional, título, cuerpo y pie. Base de cualquier tarjeta simple. */
5
+ export default function Panel({ title = "Resumen de reserva", body = "Dos clases de iniciación el sábado por la mañana, con neopreno y tabla incluidos.", footer = "Cancelación gratuita hasta 24 h antes", bar = "reserva.txt", variant = "glass", cornerGlyph = "", fill, pattern, tone, image, className = "" }) {
6
+ const f = fillProps({ fill, pattern, tone, image });
7
+ return (_jsxs("section", { className: `ui-panel ui-surface ${f.className} ${vcls(variant)} ${className}`, style: f.style, "data-corners": cornerGlyph || undefined, children: [bar && (_jsxs("div", { className: "ui-panel__bar", children: [_jsx("i", { style: { background: "var(--acc)" } }), _jsx("i", { style: { background: "var(--acc2)" } }), _jsx("i", { style: { background: "var(--mut)" } }), _jsx("span", { children: bar })] })), _jsxs("div", { className: "ui-panel__body", children: [title && _jsx("h4", { children: title }), body && _jsx("p", { children: body })] }), footer && _jsx("div", { className: "ui-panel__foot", children: footer })] }));
8
+ }
@@ -0,0 +1,61 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Pieles del pack Kenney "Pixel UI" (CC0), con el color de relleno (medido en el centro del PNG) de cada una:
4
+ * se pinta DETRÁS del `border-image` para tapar las rendijas de un píxel que el navegador deja entre las 9
5
+ * regiones cuando la escala de pantalla es fraccionaria (110 %, 125 %, 150 %…).
6
+ */
7
+ export declare const PIXEL_FRAME_SKINS: {
8
+ readonly colored: {
9
+ readonly dir: "Colored";
10
+ readonly fills: {
11
+ readonly blue: "#1ea7e1";
12
+ readonly green: "#73cd4b";
13
+ readonly grey: "#eeeeee";
14
+ readonly red: "#e86a17";
15
+ readonly yellow: "#ffcc00";
16
+ };
17
+ };
18
+ readonly outline: {
19
+ readonly dir: "Outline";
20
+ readonly fills: {
21
+ readonly blue: "#eeeeee";
22
+ readonly green: "#eeeeee";
23
+ readonly red: "#eeeeee";
24
+ readonly yellow: "#eeeeee";
25
+ };
26
+ };
27
+ readonly ancient: {
28
+ readonly dir: "Ancient";
29
+ readonly fills: {
30
+ readonly brown: "#97714a";
31
+ readonly grey: "#838796";
32
+ readonly tan: "#d3bf8f";
33
+ readonly white: "#e5e5e5";
34
+ };
35
+ };
36
+ };
37
+ export type PixelFrameSkin = keyof typeof PIXEL_FRAME_SKINS;
38
+ export type PixelFrameProps = {
39
+ skin?: PixelFrameSkin;
40
+ /** color de la piel; si no existe en esa piel se usa el primero que tenga (ver `PIXEL_FRAME_SKINS`) */
41
+ color?: string;
42
+ /** versión "hundida" del marco (estado pulsado) */
43
+ pressed?: boolean;
44
+ /** píxeles CSS por píxel del sprite; el marco ocupa 16 × escala px por cada lado */
45
+ scale?: 1 | 2 | 3 | 4;
46
+ children?: ReactNode;
47
+ className?: string;
48
+ };
49
+ /** Resuelve piel + color (con el fallback al primero de la piel) a la ruta del PNG y a su color de relleno. */
50
+ export declare function pixelFrameAsset(skin: PixelFrameSkin, color: string, pressed: boolean): {
51
+ src: string;
52
+ fill: string;
53
+ };
54
+ /**
55
+ * Marco pixel art de 9 cortes (`border-image`) con las piezas de Kenney "Pixel UI" — CC0, sin atribución
56
+ * obligatoria (`public/pixel/kenney-pixel-ui/License.txt`). El marco trae su PROPIO fondo, siempre opaco y de tono
57
+ * medio o claro (comprobado en las 13 combinaciones piel/color: negro sobre ellas da ≥ 4,5:1), y fija una paleta
58
+ * local de tinta (`.ui-pframe` en ui-kit.css) para que el texto y los componentes del kit que pongas dentro se
59
+ * lean igual con cualquier tema de la página: aquí el contraste no depende de lo que haya detrás.
60
+ */
61
+ export default function PixelFrame({ skin, color, pressed, scale, children, className }: PixelFrameProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Pieles del pack Kenney "Pixel UI" (CC0), con el color de relleno (medido en el centro del PNG) de cada una:
4
+ * se pinta DETRÁS del `border-image` para tapar las rendijas de un píxel que el navegador deja entre las 9
5
+ * regiones cuando la escala de pantalla es fraccionaria (110 %, 125 %, 150 %…).
6
+ */
7
+ export const PIXEL_FRAME_SKINS = {
8
+ colored: { dir: "Colored", fills: { blue: "#1ea7e1", green: "#73cd4b", grey: "#eeeeee", red: "#e86a17", yellow: "#ffcc00" } },
9
+ outline: { dir: "Outline", fills: { blue: "#eeeeee", green: "#eeeeee", red: "#eeeeee", yellow: "#eeeeee" } },
10
+ ancient: { dir: "Ancient", fills: { brown: "#97714a", grey: "#838796", tan: "#d3bf8f", white: "#e5e5e5" } },
11
+ };
12
+ /** Resuelve piel + color (con el fallback al primero de la piel) a la ruta del PNG y a su color de relleno. */
13
+ export function pixelFrameAsset(skin, color, pressed) {
14
+ const { dir, fills } = PIXEL_FRAME_SKINS[skin];
15
+ const names = Object.keys(fills);
16
+ const c = names.includes(color) ? color : names[0];
17
+ return { src: `/pixel/kenney-pixel-ui/9-slice/${dir}/${c}${pressed ? "_pressed" : ""}.png`, fill: fills[c] };
18
+ }
19
+ /**
20
+ * Marco pixel art de 9 cortes (`border-image`) con las piezas de Kenney "Pixel UI" — CC0, sin atribución
21
+ * obligatoria (`public/pixel/kenney-pixel-ui/License.txt`). El marco trae su PROPIO fondo, siempre opaco y de tono
22
+ * medio o claro (comprobado en las 13 combinaciones piel/color: negro sobre ellas da ≥ 4,5:1), y fija una paleta
23
+ * local de tinta (`.ui-pframe` en ui-kit.css) para que el texto y los componentes del kit que pongas dentro se
24
+ * lean igual con cualquier tema de la página: aquí el contraste no depende de lo que haya detrás.
25
+ */
26
+ export default function PixelFrame({ skin = "colored", color = "blue", pressed = false, scale = 2, children, className = "" }) {
27
+ const { src, fill } = pixelFrameAsset(skin, color, pressed);
28
+ return (_jsx("div", { className: `ui-pframe ${className}`.trim(), style: { "--pf-img": `url(${src})`, "--pf-bg": fill, "--pf-s": scale }, children: children }));
29
+ }
@@ -0,0 +1,51 @@
1
+ import { type ReactNode } from "react";
2
+ import { type CharsetId } from "../../lib/ui/fx.js";
3
+ export type PresenceEffect = "ascii-random" | "ascii-sweep" | "ascii-rain" | "ascii-radial" | "ascii-rows" | "glitch" | "slices" | "shatter" | "crt" | "flicker" | "static" | "redact" | "boot";
4
+ export declare const PRESENCE_EFFECTS: {
5
+ id: PresenceEffect;
6
+ label: string;
7
+ }[];
8
+ export type PresenceProps = {
9
+ effect?: PresenceEffect;
10
+ /** visible o no: al cambiarlo se reproduce la aparición o la desaparición */
11
+ show?: boolean;
12
+ duration?: number;
13
+ /** 0–1: fuerza del glitch, la estática y el desgarro */
14
+ intensity?: number;
15
+ /** glifos de las capas ASCII */
16
+ chars?: CharsetId;
17
+ /** tamaño de celda de las capas ASCII, en px */
18
+ cell?: number;
19
+ /** rastro tenue tras el borde de descubrimiento */
20
+ edge?: boolean;
21
+ tone?: "acc" | "acc2" | "fg";
22
+ /** columnas de fragmentos (shatter) */
23
+ tiles?: number;
24
+ /** bandas horizontales (slices) */
25
+ bands?: number;
26
+ /** altura de cada barra de censura en px */
27
+ rowHeight?: number;
28
+ redactLabel?: string;
29
+ /** líneas del arranque; separa las de entrada y las de salida con una línea «---» */
30
+ bootLines?: string;
31
+ /** alterna aparición y desaparición sin parar */
32
+ loop?: boolean;
33
+ /** pausa entre cambios en modo bucle, en segundos */
34
+ hold?: number;
35
+ /** reproduce la aparición inicial al montar (si es false, el contenido arranca ya visible) */
36
+ autoplay?: boolean;
37
+ /** ocupa el 100% del contenedor en vez de ajustarse al contenido (para envolver fondos o secciones) */
38
+ fill?: boolean;
39
+ playKey?: number;
40
+ children: ReactNode;
41
+ className?: string;
42
+ };
43
+ /**
44
+ * Envoltorio de aparición y desaparición cinematográficas, con estética hacker/glitch: capas de
45
+ * glifos que se descifran, glitch con separación RGB y corrupción, datamosh, fragmentos, encendido
46
+ * CRT, estática, censura o arranque de sistema. Todos usan --bg --fg --acc --acc2 del tema.
47
+ *
48
+ * El contenido está siempre en el DOM (y accesible); con movimiento reducido se muestra o se oculta
49
+ * sin animar. Ocupa el ancho de su contenido (`display: inline-block`); se puede cambiar con `className`.
50
+ */
51
+ export default function Presence({ effect, show, duration, intensity, chars, cell, edge, tone, tiles, bands, rowHeight, redactLabel, bootLines, loop, hold, autoplay, fill, playKey, children, className, }: PresenceProps): import("react").JSX.Element;
@@ -0,0 +1,343 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { flushSync } from "react-dom";
5
+ import { CHARSETS, drawAsciiCover, drawCorruption, drawRedact, drawStatic, hash } from "../../lib/ui/fx.js";
6
+ export const PRESENCE_EFFECTS = [
7
+ { id: "ascii-rain", label: "Lluvia de glifos" },
8
+ { id: "ascii-random", label: "Descifrado aleatorio" },
9
+ { id: "ascii-sweep", label: "Barrido binario" },
10
+ { id: "ascii-radial", label: "Onda radial" },
11
+ { id: "ascii-rows", label: "Filas de datos" },
12
+ { id: "glitch", label: "Glitch profundo" },
13
+ { id: "slices", label: "Datamosh (bandas)" },
14
+ { id: "shatter", label: "Fragmentos" },
15
+ { id: "crt", label: "Encendido CRT" },
16
+ { id: "flicker", label: "Parpadeo de neón" },
17
+ { id: "static", label: "Estática de TV" },
18
+ { id: "redact", label: "Censura" },
19
+ { id: "boot", label: "Arranque de sistema" },
20
+ ];
21
+ const DEFAULT_BOOT = "[ OK ] montando sistema de archivos\n[ OK ] iniciando servicios\n[ .. ] descifrando interfaz\n[ OK ] acceso concedido\n---\n[ .. ] cerrando sesión\n[ OK ] volcando memoria\n[ OK ] apagado";
22
+ /**
23
+ * Envoltorio de aparición y desaparición cinematográficas, con estética hacker/glitch: capas de
24
+ * glifos que se descifran, glitch con separación RGB y corrupción, datamosh, fragmentos, encendido
25
+ * CRT, estática, censura o arranque de sistema. Todos usan --bg --fg --acc --acc2 del tema.
26
+ *
27
+ * El contenido está siempre en el DOM (y accesible); con movimiento reducido se muestra o se oculta
28
+ * sin animar. Ocupa el ancho de su contenido (`display: inline-block`); se puede cambiar con `className`.
29
+ */
30
+ export default function Presence({ effect = "ascii-rain", show = true, duration = 1.2, intensity = 0.7, chars = "symbols", cell = 14, edge = true, tone = "acc", tiles = 6, bands = 10, rowHeight = 22, redactLabel = "REDACTED", bootLines = DEFAULT_BOOT, loop = false, hold = 1.2, autoplay = true, fill = false, playKey = 0, children, className = "", }) {
31
+ const root = useRef(null);
32
+ const content = useRef(null);
33
+ const cv = useRef(null);
34
+ const boot = useRef(null);
35
+ const layer = useRef(null);
36
+ const first = useRef(true);
37
+ const [auto, setAuto] = useState(true);
38
+ const [clone, setClone] = useState(null);
39
+ const visible = loop ? auto : show;
40
+ useEffect(() => {
41
+ if (!loop)
42
+ return;
43
+ setAuto(true);
44
+ const id = setInterval(() => setAuto((v) => !v), (duration + hold) * 1000);
45
+ return () => clearInterval(id);
46
+ }, [loop, duration, hold]);
47
+ useEffect(() => {
48
+ const el = root.current;
49
+ const c = content.current;
50
+ const canvas = cv.current;
51
+ const bootEl = boot.current;
52
+ if (!el || !c || !canvas || !bootEl)
53
+ return;
54
+ let disposed = false;
55
+ const cleanups = [];
56
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
57
+ const skip = first.current && !autoplay;
58
+ first.current = false;
59
+ (async () => {
60
+ const { gsap } = await import("gsap");
61
+ if (disposed)
62
+ return;
63
+ const ctx = canvas.getContext("2d");
64
+ const w = Math.max(2, el.clientWidth);
65
+ const h = Math.max(2, el.clientHeight);
66
+ canvas.width = w;
67
+ canvas.height = h;
68
+ const cs = getComputedStyle(el);
69
+ const T = (n, f) => cs.getPropertyValue(n).trim() || f;
70
+ const bg = T("--bg", "#000");
71
+ const fg = T("--fg", "#fff");
72
+ const acc = T("--acc", "#7cc4ff");
73
+ const acc2 = T("--acc2", "#c084fc");
74
+ const c1 = tone === "acc2" ? acc2 : tone === "fg" ? fg : acc;
75
+ const c2 = tone === "acc" ? fg : acc;
76
+ const dirIn = visible;
77
+ const clear = () => ctx.clearRect(0, 0, w, h);
78
+ const showC = () => void gsap.set(c, { autoAlpha: 1 });
79
+ const hideC = () => void gsap.set(c, { autoAlpha: 0 });
80
+ const reset = () => {
81
+ gsap.killTweensOf([c, canvas, bootEl]);
82
+ gsap.set(c, { clearProps: "transform,filter,clipPath,opacity,visibility" });
83
+ gsap.set(canvas, { autoAlpha: 1 });
84
+ gsap.set(bootEl, { autoAlpha: 0 });
85
+ clear();
86
+ if (layer.current)
87
+ flushSync(() => setClone(null));
88
+ };
89
+ const finish = () => {
90
+ reset();
91
+ if (!dirIn)
92
+ hideC();
93
+ };
94
+ cleanups.push(() => {
95
+ gsap.killTweensOf([c, canvas, bootEl]);
96
+ clear();
97
+ });
98
+ if (reduce || skip) {
99
+ finish();
100
+ return;
101
+ }
102
+ reset();
103
+ // ------------------------------------------------ capas de glifos
104
+ if (effect.startsWith("ascii-")) {
105
+ const pattern = effect.slice(6);
106
+ const k = { v: dirIn ? 0 : 1 };
107
+ let time = 0;
108
+ const draw = () => drawAsciiCover(ctx, w, h, k.v, { pattern, cell, chars: CHARSETS[chars], bg, c1, c2, time: (time += 0.03), seed: 11, edge });
109
+ showC();
110
+ draw();
111
+ gsap.to(k, {
112
+ v: dirIn ? 1 : 0,
113
+ duration,
114
+ ease: "power1.inOut",
115
+ onUpdate: draw,
116
+ onComplete: () => {
117
+ if (dirIn)
118
+ clear();
119
+ else {
120
+ hideC();
121
+ gsap.to(canvas, { autoAlpha: 0, duration: 0.3, onComplete: clear });
122
+ }
123
+ },
124
+ });
125
+ return;
126
+ }
127
+ switch (effect) {
128
+ // ---------------------------------------------- glitch profundo
129
+ case "glitch": {
130
+ const steps = Math.max(6, Math.round(duration * 26));
131
+ const p = { v: 0 };
132
+ let last = -1;
133
+ showC();
134
+ gsap.to(p, {
135
+ v: 1,
136
+ duration,
137
+ ease: "none",
138
+ onUpdate: () => {
139
+ const step = Math.floor(p.v * steps);
140
+ if (step === last)
141
+ return;
142
+ last = step;
143
+ const env = dirIn ? Math.pow(1 - p.v, 1.3) : Math.pow(p.v, 1.3);
144
+ const a = env * intensity;
145
+ const r = (n) => hash(step * 13.7 + n) * 2 - 1;
146
+ const gone = dirIn ? hash(step * 3.1) < a * 0.6 : hash(step * 3.1) < a * 0.5 + p.v * 0.35;
147
+ const band = hash(step * 5.3) < a ? `inset(${(hash(step * 2.2) * 80).toFixed(0)}% 0 ${(hash(step * 4.4) * 60).toFixed(0)}% 0)` : "none";
148
+ c.style.opacity = gone ? (hash(step) > 0.5 ? "0" : "0.25") : "1";
149
+ c.style.transform = `translate(${r(1) * a * 34}px, ${r(2) * a * 8}px) skewX(${r(3) * a * 28}deg) scale(${1 + r(4) * a * 0.05})`;
150
+ c.style.clipPath = band;
151
+ c.style.filter = `drop-shadow(${a * 16 * r(5)}px 0 0 ${acc}) drop-shadow(${-a * 16 * r(5)}px 0 0 ${acc2}) hue-rotate(${r(6) * a * 90}deg) contrast(${1 + a * 0.9})`;
152
+ drawCorruption(ctx, w, h, a, step, acc, acc2);
153
+ },
154
+ onComplete: () => {
155
+ if (dirIn)
156
+ reset();
157
+ else {
158
+ reset();
159
+ hideC();
160
+ }
161
+ },
162
+ });
163
+ break;
164
+ }
165
+ // ---------------------------------------------- datamosh y fragmentos (clones)
166
+ case "slices":
167
+ case "shatter": {
168
+ flushSync(() => setClone(effect === "slices" ? "bands" : "tiles"));
169
+ const els = Array.from(layer.current?.children ?? []);
170
+ hideC();
171
+ if (effect === "slices") {
172
+ const p = { v: 0 };
173
+ let last = -1;
174
+ const steps = Math.max(6, Math.round(duration * 22));
175
+ gsap.to(p, {
176
+ v: 1,
177
+ duration,
178
+ ease: "none",
179
+ onUpdate: () => {
180
+ const step = Math.floor(p.v * steps);
181
+ if (step === last)
182
+ return;
183
+ last = step;
184
+ const env = (dirIn ? Math.pow(1 - p.v, 1.2) : Math.pow(p.v, 1.2)) * intensity;
185
+ els.forEach((b, i) => {
186
+ const r = hash(step * 7 + i * 3) * 2 - 1;
187
+ const miss = dirIn ? hash(step * 5 + i) < env * 0.55 : p.v > 0.93 || hash(step * 5 + i) < env * 0.6;
188
+ gsap.set(b, { x: r * env * 70, opacity: miss ? 0 : 1, filter: `drop-shadow(${r * env * 10}px 0 0 ${acc}) drop-shadow(${-r * env * 10}px 0 0 ${acc2})` });
189
+ });
190
+ },
191
+ onComplete: () => {
192
+ reset();
193
+ if (!dirIn)
194
+ hideC();
195
+ },
196
+ });
197
+ }
198
+ else {
199
+ const cols = Math.max(2, Math.round(tiles));
200
+ const rows = Math.max(2, Math.round(cols * 0.6));
201
+ const scatter = (i) => ({
202
+ x: (hash(i * 3.1) * 2 - 1) * 260 * (0.4 + intensity),
203
+ y: (hash(i * 5.7) * 2 - 1) * 200 * (0.4 + intensity) + (dirIn ? 0 : 160),
204
+ rotation: (hash(i * 7.3) * 2 - 1) * 70,
205
+ scale: 0.4 + hash(i * 2.9) * 0.5,
206
+ });
207
+ const stagger = { amount: duration * 0.45, from: "center", grid: [cols, rows] };
208
+ if (dirIn) {
209
+ els.forEach((b, i) => gsap.set(b, { ...scatter(i), opacity: 0 }));
210
+ gsap.to(els, { x: 0, y: 0, rotation: 0, scale: 1, opacity: 1, duration: duration * 0.55, ease: "power3.out", stagger, onComplete: () => reset() });
211
+ }
212
+ else {
213
+ gsap.to(els, { x: (i) => scatter(i).x, y: (i) => scatter(i).y, rotation: (i) => scatter(i).rotation, scale: (i) => scatter(i).scale, opacity: 0, duration: duration * 0.55, ease: "power2.in", stagger, onComplete: () => { reset(); hideC(); } });
214
+ }
215
+ }
216
+ break;
217
+ }
218
+ // ---------------------------------------------- CRT
219
+ case "crt": {
220
+ showC();
221
+ gsap.set(c, { transformOrigin: "50% 50%" });
222
+ if (dirIn) {
223
+ gsap.set(c, { scaleY: 0.004, filter: "brightness(8) saturate(0)" });
224
+ gsap
225
+ .timeline({ onComplete: () => reset() })
226
+ .to(c, { scaleY: 1.05, filter: "brightness(2.2) saturate(1)", duration: duration * 0.55, ease: "power4.out" })
227
+ .to(c, { scaleY: 1, filter: "brightness(1) saturate(1)", duration: duration * 0.45, ease: "elastic.out(1,0.6)" });
228
+ }
229
+ else {
230
+ gsap
231
+ .timeline({ onComplete: () => { reset(); hideC(); } })
232
+ .to(c, { scaleY: 0.004, filter: "brightness(6) saturate(0)", duration: duration * 0.55, ease: "power4.in" })
233
+ .to(c, { scaleX: 0, duration: duration * 0.3, ease: "power2.in" })
234
+ .to(c, { autoAlpha: 0, duration: 0.05 });
235
+ }
236
+ break;
237
+ }
238
+ // ---------------------------------------------- neón que parpadea
239
+ case "flicker": {
240
+ const seq = dirIn ? [0, 1, 0, 0.3, 1, 0.15, 1, 0.6, 0.2, 1, 0.8, 1] : [1, 0.3, 1, 0, 0.7, 0, 0.35, 0.05, 0.4, 0];
241
+ const tl = gsap.timeline({ onComplete: () => { reset(); if (!dirIn)
242
+ hideC(); } });
243
+ const dt = duration / seq.length;
244
+ showC();
245
+ seq.forEach((v, i) => tl.set(c, { opacity: v, filter: `brightness(${1 + v * 0.7 * intensity}) saturate(${1 + v * 0.4})` }, i * dt));
246
+ tl.to({}, { duration: dt });
247
+ break;
248
+ }
249
+ // ---------------------------------------------- estática de TV
250
+ case "static": {
251
+ const p = { v: 0 };
252
+ showC();
253
+ gsap.to(p, {
254
+ v: 1,
255
+ duration,
256
+ ease: "none",
257
+ onUpdate: () => {
258
+ const env = dirIn ? 1 - p.v : p.v;
259
+ drawStatic(ctx, w, h, env * intensity * 1.15, acc);
260
+ const flick = hash(Math.floor(p.v * duration * 30)) < 0.5;
261
+ c.style.opacity = String(dirIn ? (p.v > 0.7 ? 1 : flick ? p.v : 0) : p.v > 0.6 ? (flick ? 1 - p.v : 0) : 1);
262
+ },
263
+ onComplete: () => {
264
+ reset();
265
+ if (!dirIn)
266
+ hideC();
267
+ },
268
+ });
269
+ break;
270
+ }
271
+ // ---------------------------------------------- censura
272
+ case "redact": {
273
+ const k = { v: dirIn ? 0 : 1 };
274
+ showC();
275
+ const draw = () => drawRedact(ctx, w, h, k.v, { rowH: rowHeight, color: fg, textColor: bg, seed: 5, label: redactLabel });
276
+ draw();
277
+ gsap.to(k, {
278
+ v: dirIn ? 1 : 0,
279
+ duration,
280
+ ease: "power1.inOut",
281
+ onUpdate: draw,
282
+ onComplete: () => {
283
+ if (dirIn)
284
+ clear();
285
+ else {
286
+ hideC();
287
+ gsap.to(canvas, { autoAlpha: 0, duration: 0.3, onComplete: clear });
288
+ }
289
+ },
290
+ });
291
+ break;
292
+ }
293
+ // ---------------------------------------------- arranque de sistema
294
+ case "boot": {
295
+ const [inPart, outPart] = bootLines.split(/\n---\n/);
296
+ const lines = (dirIn ? inPart : (outPart ?? "[ .. ] cerrando\n[ OK ] apagado")).split("\n").filter(Boolean);
297
+ const total = lines.reduce((n, l) => n + l.length + 1, 0);
298
+ const p = { v: 0 };
299
+ showC();
300
+ if (!dirIn)
301
+ hideC();
302
+ gsap.set(bootEl, { autoAlpha: 1 });
303
+ bootEl.style.color = c1;
304
+ gsap.to(p, {
305
+ v: 1,
306
+ duration: duration * 0.7,
307
+ ease: "none",
308
+ onUpdate: () => {
309
+ let n = Math.floor(p.v * total);
310
+ bootEl.textContent = lines
311
+ .map((l) => {
312
+ const part = l.slice(0, Math.max(0, n));
313
+ n -= l.length + 1;
314
+ return part;
315
+ })
316
+ .join("\n");
317
+ },
318
+ onComplete: () => {
319
+ gsap.to(bootEl, { autoAlpha: 0, duration: duration * 0.3, onComplete: () => { reset(); if (!dirIn)
320
+ hideC(); } });
321
+ },
322
+ });
323
+ break;
324
+ }
325
+ }
326
+ })();
327
+ return () => {
328
+ disposed = true;
329
+ cleanups.reverse().forEach((f) => f());
330
+ };
331
+ }, [visible, effect, duration, intensity, chars, cell, edge, tone, tiles, bands, rowHeight, redactLabel, bootLines, playKey, autoplay]);
332
+ const cols = Math.max(2, Math.round(tiles));
333
+ const rows = Math.max(2, Math.round(cols * 0.6));
334
+ const n = Math.max(2, Math.round(bands));
335
+ return (_jsxs("div", { ref: root, className: `ui-tx ${fill ? "ui-tx--fill" : ""} ${className}`, children: [_jsx("div", { ref: content, className: "ui-tx__content", children: children }), clone && (_jsx("div", { ref: layer, className: "ui-tx__clones", "aria-hidden": true, children: clone === "bands"
336
+ ? Array.from({ length: n }, (_, i) => (_jsx("div", { className: "ui-tx__clone", style: { clipPath: `inset(${((i / n) * 100).toFixed(2)}% 0 ${(100 - ((i + 1) / n) * 100).toFixed(2)}% 0)` }, children: children }, i)))
337
+ : Array.from({ length: cols * rows }, (_, i) => {
338
+ const cx = i % cols;
339
+ const cy = Math.floor(i / cols);
340
+ const inset = `inset(${((cy / rows) * 100).toFixed(2)}% ${(100 - ((cx + 1) / cols) * 100).toFixed(2)}% ${(100 - ((cy + 1) / rows) * 100).toFixed(2)}% ${((cx / cols) * 100).toFixed(2)}%)`;
341
+ return (_jsx("div", { className: "ui-tx__clone", style: { clipPath: inset }, children: children }, i));
342
+ }) })), _jsx("canvas", { ref: cv, className: "ui-tx__fx", "aria-hidden": true }), _jsx("pre", { ref: boot, className: "ui-tx__boot", "aria-hidden": true })] }));
343
+ }
@@ -0,0 +1,30 @@
1
+ import { type CardFill, type CardPattern, type CardTone } from "./fill.js";
2
+ import { type Variant } from "./variants.js";
3
+ export type PricingCardProps = {
4
+ plan?: string;
5
+ /** una línea bajo el nombre: para quién es el plan */
6
+ description?: string;
7
+ price?: string;
8
+ /** precio anterior, tachado junto al actual (ofertas, descuento anual) */
9
+ originalPrice?: string;
10
+ period?: string;
11
+ /** texto pequeño bajo el precio («facturado anualmente», «IVA incluido»…) */
12
+ note?: string;
13
+ /** una característica por línea; empezar la línea con «-» la marca como no incluida */
14
+ features?: string;
15
+ cta?: string;
16
+ highlighted?: boolean;
17
+ /** cómo se destaca: glow = borde y halo de acento · invert = fondo de acento (paleta invertida) · border = solo borde grueso */
18
+ highlight?: "glow" | "invert" | "border";
19
+ badge?: string;
20
+ /** classic: vertical · compact: sin lista (características en una línea) · horizontal: datos a la izquierda, precio y botón a la derecha */
21
+ layout?: "classic" | "compact" | "horizontal";
22
+ /** fondo de la tarjeta (ver fill.ts); `highlight="invert"` lo sustituye por el acento cuando está destacada */
23
+ fill?: CardFill;
24
+ pattern?: CardPattern;
25
+ tone?: CardTone;
26
+ variant?: Variant;
27
+ className?: string;
28
+ };
29
+ /** Tarjeta de plan de precios. `highlighted` la destaca (con halo, invertida o con borde) y la marca con una insignia. */
30
+ export default function PricingCard({ plan, description, price, originalPrice, period, note, features, cta, highlighted, highlight, badge, layout, fill, pattern, tone, variant, className, }: PricingCardProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import Badge from "./Badge.js";
3
+ import Button from "./Button.js";
4
+ import { fillProps } from "./fill.js";
5
+ import { vcls } from "./variants.js";
6
+ /** Tarjeta de plan de precios. `highlighted` la destaca (con halo, invertida o con borde) y la marca con una insignia. */
7
+ export default function PricingCard({ plan = "Pro", description = "", price = "29 €", originalPrice = "", period = "/ mes", note = "", features = "Proyectos ilimitados\nSoporte prioritario\nExportación a PNG y SVG", cta = "Empezar ahora", highlighted = true, highlight = "glow", badge = "Más popular", layout = "classic", fill = "surface", pattern, tone, variant = "glass", className = "", }) {
8
+ const inverted = highlighted && highlight === "invert";
9
+ const f = fillProps({ fill: inverted ? "accent" : fill, pattern, tone });
10
+ const items = features
11
+ .split("\n")
12
+ .map((l) => l.trim())
13
+ .filter(Boolean)
14
+ .map((l) => ({ text: l.replace(/^[-+]\s*/, ""), off: l.startsWith("-") }));
15
+ const hl = highlighted ? `ui-price--hl ui-price--hl-${highlight}` : "";
16
+ const head = (_jsxs("div", { className: "ui-price__head", children: [_jsx("span", { className: "ui-price__name", children: plan.toUpperCase() }), description && _jsx("p", { className: "ui-price__desc", children: description })] }));
17
+ const amount = (_jsxs("div", { className: "ui-price__cost", children: [_jsxs("div", { className: "ui-price__amount", children: [originalPrice && _jsx("s", { className: "ui-price__was", children: originalPrice }), price, _jsx("small", { children: period })] }), note && _jsx("span", { className: "ui-price__note", children: note })] }));
18
+ const list = layout === "compact" ? (_jsx("p", { className: "ui-price__inline", children: items.filter((i) => !i.off).map((i) => i.text).join(" · ") })) : (_jsx("ul", { children: items.map((i) => (_jsxs("li", { className: i.off ? "ui-price__off" : undefined, children: [i.off && _jsx("span", { className: "ui-sr", children: "No incluido: " }), i.text] }, i.text))) }));
19
+ const button = _jsx(Button, { label: cta, variant: variant, emphasis: highlighted ? "primary" : "secondary", fullWidth: true });
20
+ return (_jsxs("article", { className: `ui-price ui-price--${layout} ${hl} ui-surface ${f.className} ${vcls(variant)} ${className}`, style: f.style, children: [highlighted && badge && (_jsx("span", { className: "ui-price__badge", children: _jsx(Badge, { text: badge, variant: variant, intent: "accent" }) })), layout === "horizontal" ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "ui-price__main", children: [head, list] }), _jsxs("div", { className: "ui-price__side", children: [amount, button] })] })) : (_jsxs(_Fragment, { children: [head, amount, list, button] }))] }));
21
+ }