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.
- package/CATALOG.md +1237 -0
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/THIRD-PARTY.md +4 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow_pressed.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/list.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/space.png +0 -0
- package/assets/pixel/kenney-pixel-ui/9-slice/space_inlay.png +0 -0
- package/assets/pixel/kenney-pixel-ui/License.txt +23 -0
- package/assets/pixel/kenney-pixel-ui/spritesheet/sheet.png +0 -0
- package/bin/trama-ui.mjs +27 -0
- package/dist/components/ui/Accordion.d.ts +12 -0
- package/dist/components/ui/Accordion.js +12 -0
- package/dist/components/ui/Alert.d.ts +15 -0
- package/dist/components/ui/Alert.js +15 -0
- package/dist/components/ui/ArticlesSection.d.ts +30 -0
- package/dist/components/ui/ArticlesSection.js +18 -0
- package/dist/components/ui/AsciiBackground.d.ts +58 -0
- package/dist/components/ui/AsciiBackground.js +173 -0
- package/dist/components/ui/AsciiCard.d.ts +78 -0
- package/dist/components/ui/AsciiCard.js +253 -0
- package/dist/components/ui/AsciiChart.d.ts +18 -0
- package/dist/components/ui/AsciiChart.js +47 -0
- package/dist/components/ui/Badge.d.ts +15 -0
- package/dist/components/ui/Badge.js +8 -0
- package/dist/components/ui/BitmapText.d.ts +25 -0
- package/dist/components/ui/BitmapText.js +21 -0
- package/dist/components/ui/BlogCard.d.ts +33 -0
- package/dist/components/ui/BlogCard.js +13 -0
- package/dist/components/ui/Breadcrumbs.d.ts +9 -0
- package/dist/components/ui/Breadcrumbs.js +7 -0
- package/dist/components/ui/Button.d.ts +74 -0
- package/dist/components/ui/Button.js +48 -0
- package/dist/components/ui/CTASection.d.ts +13 -0
- package/dist/components/ui/CTASection.js +7 -0
- package/dist/components/ui/Carousel.d.ts +30 -0
- package/dist/components/ui/Carousel.js +80 -0
- package/dist/components/ui/CheckboxGroup.d.ts +11 -0
- package/dist/components/ui/CheckboxGroup.js +20 -0
- package/dist/components/ui/CodeBlock.d.ts +11 -0
- package/dist/components/ui/CodeBlock.js +20 -0
- package/dist/components/ui/ContactForm.d.ts +25 -0
- package/dist/components/ui/ContactForm.js +26 -0
- package/dist/components/ui/CrtOverlay.d.ts +10 -0
- package/dist/components/ui/CrtOverlay.js +5 -0
- package/dist/components/ui/Divider.d.ts +12 -0
- package/dist/components/ui/Divider.js +7 -0
- package/dist/components/ui/Drawer.d.ts +16 -0
- package/dist/components/ui/Drawer.js +25 -0
- package/dist/components/ui/Dropdown.d.ts +10 -0
- package/dist/components/ui/Dropdown.js +21 -0
- package/dist/components/ui/FAQSection.d.ts +14 -0
- package/dist/components/ui/FAQSection.js +7 -0
- package/dist/components/ui/FeatureGrid.d.ts +18 -0
- package/dist/components/ui/FeatureGrid.js +14 -0
- package/dist/components/ui/Footer.d.ts +14 -0
- package/dist/components/ui/Footer.js +16 -0
- package/dist/components/ui/GlyphCursor.d.ts +22 -0
- package/dist/components/ui/GlyphCursor.js +50 -0
- package/dist/components/ui/GridBackground.d.ts +15 -0
- package/dist/components/ui/GridBackground.js +6 -0
- package/dist/components/ui/Hero.d.ts +21 -0
- package/dist/components/ui/Hero.js +12 -0
- package/dist/components/ui/HoverFX.d.ts +29 -0
- package/dist/components/ui/HoverFX.js +75 -0
- package/dist/components/ui/Icon.d.ts +33 -0
- package/dist/components/ui/Icon.js +29 -0
- package/dist/components/ui/ImageGallery.d.ts +31 -0
- package/dist/components/ui/ImageGallery.js +79 -0
- package/dist/components/ui/LogoCloud.d.ts +12 -0
- package/dist/components/ui/LogoCloud.js +9 -0
- package/dist/components/ui/MagneticButton.d.ts +12 -0
- package/dist/components/ui/MagneticButton.js +49 -0
- package/dist/components/ui/Marquee.d.ts +29 -0
- package/dist/components/ui/Marquee.js +15 -0
- package/dist/components/ui/Modal.d.ts +40 -0
- package/dist/components/ui/Modal.js +51 -0
- package/dist/components/ui/NavBar.d.ts +14 -0
- package/dist/components/ui/NavBar.js +12 -0
- package/dist/components/ui/NeonSign.d.ts +14 -0
- package/dist/components/ui/NeonSign.js +7 -0
- package/dist/components/ui/OtpInput.d.ts +9 -0
- package/dist/components/ui/OtpInput.js +27 -0
- package/dist/components/ui/Pagination.d.ts +10 -0
- package/dist/components/ui/Pagination.js +24 -0
- package/dist/components/ui/Panel.d.ts +21 -0
- package/dist/components/ui/Panel.js +8 -0
- package/dist/components/ui/PixelFrame.d.ts +61 -0
- package/dist/components/ui/PixelFrame.js +29 -0
- package/dist/components/ui/Presence.d.ts +51 -0
- package/dist/components/ui/Presence.js +343 -0
- package/dist/components/ui/PricingCard.d.ts +30 -0
- package/dist/components/ui/PricingCard.js +21 -0
- package/dist/components/ui/PricingSection.d.ts +35 -0
- package/dist/components/ui/PricingSection.js +21 -0
- package/dist/components/ui/Progress.d.ts +17 -0
- package/dist/components/ui/Progress.js +9 -0
- package/dist/components/ui/RadioGroup.d.ts +10 -0
- package/dist/components/ui/RadioGroup.js +11 -0
- package/dist/components/ui/RangeSlider.d.ts +14 -0
- package/dist/components/ui/RangeSlider.js +11 -0
- package/dist/components/ui/Reveal.d.ts +48 -0
- package/dist/components/ui/Reveal.js +181 -0
- package/dist/components/ui/SceneFlash.d.ts +14 -0
- package/dist/components/ui/SceneFlash.js +51 -0
- package/dist/components/ui/ScrambleText.d.ts +31 -0
- package/dist/components/ui/ScrambleText.js +51 -0
- package/dist/components/ui/ScrollArea.d.ts +15 -0
- package/dist/components/ui/ScrollArea.js +18 -0
- package/dist/components/ui/SectionHeader.d.ts +12 -0
- package/dist/components/ui/SectionHeader.js +6 -0
- package/dist/components/ui/Select.d.ts +17 -0
- package/dist/components/ui/Select.js +31 -0
- package/dist/components/ui/Skeleton.d.ts +12 -0
- package/dist/components/ui/Skeleton.js +18 -0
- package/dist/components/ui/Spinner.d.ts +15 -0
- package/dist/components/ui/Spinner.js +24 -0
- package/dist/components/ui/Spotlight.d.ts +16 -0
- package/dist/components/ui/Spotlight.js +16 -0
- package/dist/components/ui/Sprite.d.ts +27 -0
- package/dist/components/ui/Sprite.js +19 -0
- package/dist/components/ui/StatCounter.d.ts +24 -0
- package/dist/components/ui/StatCounter.js +70 -0
- package/dist/components/ui/StatsSection.d.ts +14 -0
- package/dist/components/ui/StatsSection.js +13 -0
- package/dist/components/ui/Stepper.d.ts +10 -0
- package/dist/components/ui/Stepper.js +7 -0
- package/dist/components/ui/Table.d.ts +10 -0
- package/dist/components/ui/Table.js +8 -0
- package/dist/components/ui/Tabs.d.ts +12 -0
- package/dist/components/ui/Tabs.js +18 -0
- package/dist/components/ui/TeamSection.d.ts +18 -0
- package/dist/components/ui/TeamSection.js +19 -0
- package/dist/components/ui/TerminalTyper.d.ts +20 -0
- package/dist/components/ui/TerminalTyper.js +45 -0
- package/dist/components/ui/Testimonial.d.ts +19 -0
- package/dist/components/ui/Testimonial.js +9 -0
- package/dist/components/ui/TestimonialSection.d.ts +20 -0
- package/dist/components/ui/TestimonialSection.js +15 -0
- package/dist/components/ui/TextField.d.ts +26 -0
- package/dist/components/ui/TextField.js +13 -0
- package/dist/components/ui/TextmodeBackground.d.ts +52 -0
- package/dist/components/ui/TextmodeBackground.js +83 -0
- package/dist/components/ui/Timeline.d.ts +13 -0
- package/dist/components/ui/Timeline.js +8 -0
- package/dist/components/ui/Toast.d.ts +30 -0
- package/dist/components/ui/Toast.js +47 -0
- package/dist/components/ui/Toggle.d.ts +13 -0
- package/dist/components/ui/Toggle.js +10 -0
- package/dist/components/ui/Tooltip.d.ts +20 -0
- package/dist/components/ui/Tooltip.js +20 -0
- package/dist/components/ui/Typewriter.d.ts +20 -0
- package/dist/components/ui/Typewriter.js +46 -0
- package/dist/components/ui/VideoPlayer.d.ts +81 -0
- package/dist/components/ui/VideoPlayer.js +379 -0
- package/dist/components/ui/fill.d.ts +32 -0
- package/dist/components/ui/fill.js +27 -0
- package/dist/components/ui/intent.d.ts +20 -0
- package/dist/components/ui/intent.js +23 -0
- package/dist/components/ui/styles/kit.css +31 -0
- package/dist/components/ui/styles/ui-carousel.css +113 -0
- package/dist/components/ui/styles/ui-kit.css +1135 -0
- package/dist/components/ui/styles/ui-video.css +75 -0
- package/dist/components/ui/swiperCss.d.ts +15 -0
- package/dist/components/ui/swiperCss.js +15 -0
- package/dist/components/ui/variants.d.ts +12 -0
- package/dist/components/ui/variants.js +10 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +77 -0
- package/dist/lib/asciify/painters.d.ts +25 -0
- package/dist/lib/asciify/painters.js +604 -0
- package/dist/lib/asciify/pointer.d.ts +12 -0
- package/dist/lib/asciify/pointer.js +54 -0
- package/dist/lib/asciify/shape-painters.d.ts +44 -0
- package/dist/lib/asciify/shape-painters.js +335 -0
- package/dist/lib/asciify/theme-painters.d.ts +8 -0
- package/dist/lib/asciify/theme-painters.js +437 -0
- package/dist/lib/cells.d.ts +46 -0
- package/dist/lib/cells.js +114 -0
- package/dist/lib/scroll/store.d.ts +21 -0
- package/dist/lib/scroll/store.js +25 -0
- package/dist/lib/sketches/aurora.d.ts +3 -0
- package/dist/lib/sketches/aurora.js +42 -0
- package/dist/lib/sketches/fire.d.ts +3 -0
- package/dist/lib/sketches/fire.js +80 -0
- package/dist/lib/sketches/flow.d.ts +3 -0
- package/dist/lib/sketches/flow.js +70 -0
- package/dist/lib/sketches/life.d.ts +3 -0
- package/dist/lib/sketches/life.js +107 -0
- package/dist/lib/sketches/matrix.d.ts +3 -0
- package/dist/lib/sketches/matrix.js +64 -0
- package/dist/lib/sketches/ocean.d.ts +3 -0
- package/dist/lib/sketches/ocean.js +56 -0
- package/dist/lib/sketches/plasma.d.ts +3 -0
- package/dist/lib/sketches/plasma.js +29 -0
- package/dist/lib/sketches/registry.d.ts +6 -0
- package/dist/lib/sketches/registry.js +60 -0
- package/dist/lib/sketches/ripples.d.ts +3 -0
- package/dist/lib/sketches/ripples.js +84 -0
- package/dist/lib/sketches/scroll-assemble.d.ts +8 -0
- package/dist/lib/sketches/scroll-assemble.js +90 -0
- package/dist/lib/sketches/scroll-parallax.d.ts +6 -0
- package/dist/lib/sketches/scroll-parallax.js +76 -0
- package/dist/lib/sketches/scroll-rain.d.ts +6 -0
- package/dist/lib/sketches/scroll-rain.js +82 -0
- package/dist/lib/sketches/scroll-stats.d.ts +7 -0
- package/dist/lib/sketches/scroll-stats.js +81 -0
- package/dist/lib/sketches/scroll-terminal.d.ts +6 -0
- package/dist/lib/sketches/scroll-terminal.js +87 -0
- package/dist/lib/sketches/scroll-zoom.d.ts +7 -0
- package/dist/lib/sketches/scroll-zoom.js +49 -0
- package/dist/lib/sketches/starfield.d.ts +3 -0
- package/dist/lib/sketches/starfield.js +54 -0
- package/dist/lib/sketches/text-3d.d.ts +6 -0
- package/dist/lib/sketches/text-3d.js +62 -0
- package/dist/lib/sketches/text-combo-figlet.d.ts +6 -0
- package/dist/lib/sketches/text-combo-figlet.js +91 -0
- package/dist/lib/sketches/text-glitch.d.ts +3 -0
- package/dist/lib/sketches/text-glitch.js +57 -0
- package/dist/lib/sketches/text-marquee.d.ts +3 -0
- package/dist/lib/sketches/text-marquee.js +35 -0
- package/dist/lib/sketches/text-particles.d.ts +9 -0
- package/dist/lib/sketches/text-particles.js +91 -0
- package/dist/lib/sketches/text-rain.d.ts +3 -0
- package/dist/lib/sketches/text-rain.js +73 -0
- package/dist/lib/sketches/text-scramble.d.ts +6 -0
- package/dist/lib/sketches/text-scramble.js +49 -0
- package/dist/lib/sketches/text-wave.d.ts +3 -0
- package/dist/lib/sketches/text-wave.js +43 -0
- package/dist/lib/sketches/theme-orbit.d.ts +6 -0
- package/dist/lib/sketches/theme-orbit.js +119 -0
- package/dist/lib/sketches/theme-pulse.d.ts +7 -0
- package/dist/lib/sketches/theme-pulse.js +84 -0
- package/dist/lib/sketches/tunnel.d.ts +3 -0
- package/dist/lib/sketches/tunnel.js +28 -0
- package/dist/lib/text/banner.d.ts +5 -0
- package/dist/lib/text/banner.js +35 -0
- package/dist/lib/text/mask.d.ts +40 -0
- package/dist/lib/text/mask.js +143 -0
- package/dist/lib/text/store.d.ts +23 -0
- package/dist/lib/text/store.js +86 -0
- package/dist/lib/ui/card-fields.d.ts +52 -0
- package/dist/lib/ui/card-fields.js +193 -0
- package/dist/lib/ui/fonts.d.ts +56 -0
- package/dist/lib/ui/fonts.js +94 -0
- package/dist/lib/ui/fx.d.ts +42 -0
- package/dist/lib/ui/fx.js +162 -0
- package/dist/lib/ui/icons.d.ts +437 -0
- package/dist/lib/ui/icons.js +314 -0
- package/dist/lib/ui/placeholder.d.ts +11 -0
- package/dist/lib/ui/placeholder.js +65 -0
- package/dist/lib/ui/tokens.d.ts +49 -0
- package/dist/lib/ui/tokens.js +129 -0
- package/dist/lib/ui/useTokens.d.ts +10 -0
- package/dist/lib/ui/useTokens.js +32 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Martínez Sebastián
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Trama
|
|
2
|
+
|
|
3
|
+
Componentes React / Next.js de **ASCII, textmode y pixel art**: fondos de caracteres animados, tarjetas con capas
|
|
4
|
+
ASCII, secciones de landing completas (hero, precios, FAQ, blog…), formularios, overlays y más. Siete estilos
|
|
5
|
+
(`glass solid outline neon retro terminal minimal`) y tema por tokens CSS.
|
|
6
|
+
|
|
7
|
+
## Instalación
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i trama-ui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requiere React 19. Pensado para **Next.js (App Router)**, sin configuración extra (probado con Turbopack y con
|
|
14
|
+
webpack); los componentes interactivos ya llevan `"use client"`.
|
|
15
|
+
|
|
16
|
+
## Uso
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// app/layout.tsx
|
|
20
|
+
import "trama-ui/styles.css";
|
|
21
|
+
import { fontVariables } from "trama-ui/fonts"; // opcional: las fuentes del kit, autoalojadas con next/font
|
|
22
|
+
|
|
23
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
24
|
+
return (
|
|
25
|
+
<html lang="es" className={fontVariables}>
|
|
26
|
+
<body>{children}</body>
|
|
27
|
+
</html>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// cualquier página
|
|
34
|
+
import { Hero, PricingSection, Button } from "trama-ui";
|
|
35
|
+
// o, un componente por archivo:
|
|
36
|
+
import FAQSection from "trama-ui/FAQSection";
|
|
37
|
+
|
|
38
|
+
export default function Page() {
|
|
39
|
+
return (
|
|
40
|
+
<main style={{ "--acc": "#ff4fa3" } as React.CSSProperties}>
|
|
41
|
+
<Hero title="Mi producto" subtitle="Lo que hace, en una línea." primaryCta="Empezar" variant="glass" />
|
|
42
|
+
<PricingSection highlight="invert" />
|
|
43
|
+
<FAQSection variant="outline" />
|
|
44
|
+
<Button label="Eliminar" intent="danger" emphasis="outline" />
|
|
45
|
+
</main>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Pasa siempre las props de texto**: los valores por defecto son textos de demostración.
|
|
51
|
+
|
|
52
|
+
## Tema
|
|
53
|
+
|
|
54
|
+
Define estos tokens en `:root` o en cualquier contenedor (sin ellos se usa un tema neutro oscuro):
|
|
55
|
+
|
|
56
|
+
```css
|
|
57
|
+
--bg --fg --mut --acc --acc2 --card --ln --r
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Vocabulario común de props: `variant` = estilo visual · `intent` = color semántico (`accent neutral success info
|
|
61
|
+
warning danger`) · `emphasis` = énfasis de un botón · `tone` = token de color · `fill` = fondo de una tarjeta.
|
|
62
|
+
|
|
63
|
+
## Recursos pixel art
|
|
64
|
+
|
|
65
|
+
`PixelFrame` y `Sprite` usan imágenes (Kenney «Pixel UI», CC0) que tienen que servirse desde tu `public/`:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx trama-ui assets # copia a ./public/pixel/kenney-pixel-ui
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Referencia completa
|
|
72
|
+
|
|
73
|
+
`CATALOG.md` (incluido en el paquete; `npx trama-ui catalog` imprime su ruta) lista cada componente con su import, sus
|
|
74
|
+
props, tipos y valores por defecto. Es la referencia pensada para agentes de IA.
|
|
75
|
+
|
|
76
|
+
## Licencia
|
|
77
|
+
|
|
78
|
+
MIT. Recursos de terceros en `THIRD-PARTY.md`.
|
package/THIRD-PARTY.md
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
###############################################################################
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Pixel UI pack
|
|
6
|
+
by Kenney Vleugels for Kenney (www.kenney.nl)
|
|
7
|
+
with help by Lynn Evers (Twitter: @EversLynn)
|
|
8
|
+
|
|
9
|
+
------------------------------
|
|
10
|
+
|
|
11
|
+
License (Creative Commons Zero, CC0)
|
|
12
|
+
http://creativecommons.org/publicdomain/zero/1.0/
|
|
13
|
+
|
|
14
|
+
You may use these graphics in personal and commercial projects.
|
|
15
|
+
Credit (Kenney or www.kenney.nl) would be nice but is not mandatory.
|
|
16
|
+
|
|
17
|
+
------------------------------
|
|
18
|
+
|
|
19
|
+
Donate: http://donate.kenney.nl/
|
|
20
|
+
Request: http://request.kenney.nl/
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
###############################################################################
|
|
Binary file
|
package/bin/trama-ui.mjs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI de Trama (se publica como `bin/trama-ui.mjs`).
|
|
4
|
+
*
|
|
5
|
+
* npx trama-ui assets [carpeta-public] copia los recursos pixel art (PixelFrame, Sprite) a public/pixel/…
|
|
6
|
+
* npx trama-ui catalog imprime la ruta de CATALOG.md (referencia de props para agentes)
|
|
7
|
+
*/
|
|
8
|
+
import { cpSync, existsSync } from "node:fs";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
+
const [cmd, arg] = process.argv.slice(2);
|
|
14
|
+
|
|
15
|
+
if (cmd === "assets") {
|
|
16
|
+
const pub = resolve(arg ?? "public");
|
|
17
|
+
const from = join(pkgRoot, "assets/pixel/kenney-pixel-ui");
|
|
18
|
+
const to = join(pub, "pixel/kenney-pixel-ui");
|
|
19
|
+
cpSync(from, to, { recursive: true });
|
|
20
|
+
console.log(`✓ recursos pixel art copiados a ${to} (los usan PixelFrame y Sprite; licencia CC0)`);
|
|
21
|
+
} else if (cmd === "catalog") {
|
|
22
|
+
const p = join(pkgRoot, "CATALOG.md");
|
|
23
|
+
console.log(existsSync(p) ? p : "CATALOG.md no encontrado en el paquete");
|
|
24
|
+
} else {
|
|
25
|
+
console.log("Uso:\n npx trama-ui assets [carpeta-public] copia los recursos de PixelFrame/Sprite a public/pixel/\n npx trama-ui catalog ruta de CATALOG.md (props de todos los componentes)");
|
|
26
|
+
process.exit(cmd ? 1 : 0);
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Variant } from "./variants.js";
|
|
2
|
+
export type AccordionProps = {
|
|
3
|
+
/** una entrada por línea con el formato «Pregunta|Respuesta» */
|
|
4
|
+
items?: string;
|
|
5
|
+
/** permite tener varios paneles abiertos a la vez */
|
|
6
|
+
multiple?: boolean;
|
|
7
|
+
defaultOpen?: number;
|
|
8
|
+
variant?: Variant;
|
|
9
|
+
className?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Acordeón (preguntas frecuentes). El panel se anima con `grid-template-rows`, sin medir alturas en JS. */
|
|
12
|
+
export default function Accordion({ items, multiple, defaultOpen, variant, className, }: AccordionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { vcls } from "./variants.js";
|
|
5
|
+
/** Acordeón (preguntas frecuentes). El panel se anima con `grid-template-rows`, sin medir alturas en JS. */
|
|
6
|
+
export default function Accordion({ items = "¿Necesito experiencia?|Ninguna. Empezamos desde cero con material blando.\n¿Qué incluye el precio?|Neopreno, tabla, monitor titulado y seguro.\n¿Puedo cancelar?|Hasta 24 horas antes, sin coste.", multiple = false, defaultOpen = 0, variant = "glass", className = "", }) {
|
|
7
|
+
const rows = useMemo(() => items.split("\n").map((l) => l.split("|")).filter((p) => p[0]?.trim()), [items]);
|
|
8
|
+
const [open, setOpen] = useState([defaultOpen]);
|
|
9
|
+
useEffect(() => setOpen([defaultOpen]), [defaultOpen, multiple]);
|
|
10
|
+
const toggle = (i) => setOpen((o) => (o.includes(i) ? o.filter((x) => x !== i) : multiple ? [...o, i] : [i]));
|
|
11
|
+
return (_jsx("div", { className: `ui-acc ${vcls(variant)} ${className}`, children: rows.map(([q, a], i) => (_jsxs("div", { className: `ui-acc__item ${open.includes(i) ? "is-open" : ""}`, children: [_jsxs("button", { className: "ui-acc__head", "aria-expanded": open.includes(i), onClick: () => toggle(i), children: [q, _jsx("span", { className: "ui-acc__mark", "aria-hidden": true, children: "+" })] }), _jsx("div", { className: "ui-acc__panel", children: _jsx("div", { children: _jsx("p", { children: a }) }) })] }, q + i))) }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Intent, type LegacyIntent } from "./intent.js";
|
|
2
|
+
import { type Variant } from "./variants.js";
|
|
3
|
+
export type AlertProps = {
|
|
4
|
+
title?: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
/** color semántico, el mismo vocabulario que Button, Badge, TextField y Toast (ver intent.ts) */
|
|
7
|
+
intent?: Intent | LegacyIntent;
|
|
8
|
+
/** @deprecated nombre antiguo de `intent` (aceptaba `error`); se mantiene por compatibilidad */
|
|
9
|
+
kind?: Intent | LegacyIntent;
|
|
10
|
+
variant?: Variant;
|
|
11
|
+
dismissible?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
/** Aviso o toast con icono según el tipo. Se puede cerrar y vuelve a aparecer si cambian las props. */
|
|
15
|
+
export default function Alert({ title, message, intent, kind, variant, dismissible, className }: AlertProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { intentCls, toIntent } from "./intent.js";
|
|
5
|
+
import { vcls } from "./variants.js";
|
|
6
|
+
const ICONS = { accent: "*", neutral: "·", info: "i", success: "ok", warning: "!", danger: "x" };
|
|
7
|
+
/** Aviso o toast con icono según el tipo. Se puede cerrar y vuelve a aparecer si cambian las props. */
|
|
8
|
+
export default function Alert({ title = "Cambios guardados", message = "Tu perfil se actualizó correctamente.", intent, kind, variant = "glass", dismissible = true, className = "" }) {
|
|
9
|
+
const i = toIntent(intent ?? kind, "success");
|
|
10
|
+
const [open, setOpen] = useState(true);
|
|
11
|
+
useEffect(() => setOpen(true), [title, message, i, variant]);
|
|
12
|
+
if (!open)
|
|
13
|
+
return _jsx("button", { className: "ui-hint", style: { background: "none", border: 0, cursor: "pointer" }, onClick: () => setOpen(true), children: "Mostrar de nuevo el aviso" });
|
|
14
|
+
return (_jsxs("div", { role: i === "danger" ? "alert" : "status", className: `ui-alert ${intentCls(i)} ui-surface ${vcls(variant)} ${className}`, children: [_jsx("span", { className: "ui-alert__icon", "aria-hidden": true, children: ICONS[i] }), _jsxs("div", { className: "ui-alert__body", children: [title && _jsx("strong", { children: title }), message && _jsx("span", { children: message })] }), dismissible && (_jsx("button", { className: "ui-alert__close", "aria-label": "Cerrar", onClick: () => setOpen(false), children: "\u00D7" }))] }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type BlogCardProps } from "./BlogCard.js";
|
|
2
|
+
import { type CardFill } from "./fill.js";
|
|
3
|
+
import type { Variant } from "./variants.js";
|
|
4
|
+
export type ArticlesSectionProps = {
|
|
5
|
+
kicker?: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
/** un artículo por línea: «categoría|título|extracto|fecha|min de lectura|imagen (opcional: URL o gen:N)|enlace (opcional)» */
|
|
9
|
+
articles?: string;
|
|
10
|
+
/**
|
|
11
|
+
* grid: rejilla de tarjetas · featured: el primero grande (imagen de fondo) y el resto en columna ·
|
|
12
|
+
* list: una columna de tarjetas horizontales · carousel: carrusel de Swiper
|
|
13
|
+
*/
|
|
14
|
+
layout?: "grid" | "featured" | "list" | "carousel";
|
|
15
|
+
/** maquetación de cada tarjeta en `grid` y `carousel` */
|
|
16
|
+
cardLayout?: BlogCardProps["layout"];
|
|
17
|
+
/** fondo de las tarjetas (ver fill.ts) */
|
|
18
|
+
fill?: CardFill;
|
|
19
|
+
/** varía trama y acento de tarjeta en tarjeta, para que una rejilla no sean tres copias */
|
|
20
|
+
vary?: boolean;
|
|
21
|
+
/** con layout="carousel": tarjetas visibles en pantallas anchas */
|
|
22
|
+
perView?: number;
|
|
23
|
+
/** con layout="carousel": segundos entre tarjetas (0 = manual) */
|
|
24
|
+
autoplay?: number;
|
|
25
|
+
variant?: Variant;
|
|
26
|
+
className?: string;
|
|
27
|
+
};
|
|
28
|
+
export declare const DEFAULT_ARTICLES: string;
|
|
29
|
+
/** Cabecera de sección + artículos (`BlogCard`) en rejilla, destacado, lista o carrusel: un blog listo para pegar. */
|
|
30
|
+
export default function ArticlesSection({ kicker, title, subtitle, articles, layout, cardLayout, fill, vary, perView, autoplay, variant, className, }: ArticlesSectionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import BlogCard from "./BlogCard.js";
|
|
3
|
+
import Carousel from "./Carousel.js";
|
|
4
|
+
import { CARD_PATTERNS, pick } from "./fill.js";
|
|
5
|
+
import SectionHeader from "./SectionHeader.js";
|
|
6
|
+
export const DEFAULT_ARTICLES = "Técnica|Cinco errores al remar que te frenan la primera ola|El remo cuenta más que la fuerza: la postura y el timing son lo que de verdad te sube a la tabla.|12 mar 2026|4 min|gen:1\n" +
|
|
7
|
+
"Equipo|Cómo elegir tu primera tabla de espuma|Volumen, largo y flotabilidad explicados sin tecnicismos, para acertar a la primera.|2 mar 2026|6 min|gen:4\n" +
|
|
8
|
+
"Comunidad|Un fin de semana de surf trip por la costa norte|Crónica de nuestra última salida, con el mapa de las playas que visitamos.|21 feb 2026|5 min|gen:2";
|
|
9
|
+
/** Cabecera de sección + artículos (`BlogCard`) en rejilla, destacado, lista o carrusel: un blog listo para pegar. */
|
|
10
|
+
export default function ArticlesSection({ kicker = "El blog", title = "Últimas entradas", subtitle = "", articles = DEFAULT_ARTICLES, layout = "grid", cardLayout = "stacked", fill = "surface", vary = true, perView = 3, autoplay = 0, variant = "glass", className = "", }) {
|
|
11
|
+
const rows = articles.split("\n").map((l) => l.split("|").map((x) => x.trim())).filter((r) => r[0] || r[1]);
|
|
12
|
+
const cards = rows.map(([category, t, excerpt, date, readTime, image, href], i) => {
|
|
13
|
+
const cardLayout_ = layout === "featured" ? (i === 0 ? "overlay" : "horizontal") : layout === "list" ? "horizontal" : cardLayout;
|
|
14
|
+
return (_jsx(BlogCard, { category: category, title: t, excerpt: layout === "featured" && i > 0 ? "" : excerpt, date: date, readTime: readTime, image: image, href: href || undefined, layout: cardLayout_, fill: fill, pattern: vary ? pick(CARD_PATTERNS, i) : "dots", tone: vary && i % 2 === 1 ? "acc2" : "acc", variant: variant }, t + i));
|
|
15
|
+
});
|
|
16
|
+
const featuredRows = Math.max(1, rows.length - 1);
|
|
17
|
+
return (_jsxs("section", { className: `ui-articles ${className}`, children: [_jsx(SectionHeader, { kicker: kicker, title: title, subtitle: subtitle, align: "center", variant: "minimal" }), layout === "carousel" ? (_jsx(Carousel, { perView: perView, autoplay: autoplay, variant: variant, label: title, children: cards })) : (_jsx("div", { className: `ui-articles__grid ui-articles__grid--${layout}`, style: layout === "featured" ? { "--feat-rows": featuredRows } : undefined, children: cards }))] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type SourceKind } from "../../lib/asciify/painters.js";
|
|
2
|
+
/** Escenas disponibles. `progress: true` = la escena cambia con la prop `progress`. */
|
|
3
|
+
export declare const ASCII_SCENES: {
|
|
4
|
+
id: SourceKind;
|
|
5
|
+
label: string;
|
|
6
|
+
progress?: boolean;
|
|
7
|
+
}[];
|
|
8
|
+
export declare const ASCII_STYLES: readonly ["ascii", "braille", "dots", "lines", "blocks", "cross", "diagonal", "diamond", "mixed", "pixel", "mosaic", "lego", "voxel", "disco", "dither"];
|
|
9
|
+
export declare const ASCII_CHARSETS: {
|
|
10
|
+
readonly detailed: " .,:;i1tfLCG08@";
|
|
11
|
+
readonly standard: " .:-=+*#%@";
|
|
12
|
+
readonly blocks: " ░▒▓█";
|
|
13
|
+
readonly braille: " ⠁⠃⠇⡇⣇⣧⣷⣿";
|
|
14
|
+
readonly technical: " .:;>_/[{}]=+*#@$";
|
|
15
|
+
};
|
|
16
|
+
/** original = colores de la escena · tint = tiñe con --acc · duotone = --bg/--acc · gradient = --bg/--acc→--acc2 */
|
|
17
|
+
export type Palette = "original" | "tint" | "duotone" | "gradient";
|
|
18
|
+
export type AsciiBackgroundProps = {
|
|
19
|
+
scene?: SourceKind;
|
|
20
|
+
/** estilo de render ASCII (no confundir con el `style` CSS de React) */
|
|
21
|
+
asciiStyle?: (typeof ASCII_STYLES)[number];
|
|
22
|
+
/** @deprecated usa `asciiStyle` */
|
|
23
|
+
style?: (typeof ASCII_STYLES)[number];
|
|
24
|
+
/** tamaño de celda en px (más pequeño = más detalle y más coste) */
|
|
25
|
+
cellSize?: number;
|
|
26
|
+
colorMode?: "source" | "accent" | "gray";
|
|
27
|
+
charset?: keyof typeof ASCII_CHARSETS;
|
|
28
|
+
/** cómo se reinterpretan los colores con los tokens del tema */
|
|
29
|
+
palette?: Palette;
|
|
30
|
+
/** 0–1: fuerza de la paleta */
|
|
31
|
+
tintAmount?: number;
|
|
32
|
+
/** efecto de hover del motor Studio (mismo nombre que en VideoPlayer) */
|
|
33
|
+
asciiHover?: "none" | "trail" | "water" | "contour" | "dissolve" | "silk" | "vortex";
|
|
34
|
+
/** @deprecated usa `asciiHover` */
|
|
35
|
+
hover?: "none" | "trail" | "water" | "contour" | "dissolve" | "silk" | "vortex";
|
|
36
|
+
hoverStrength?: number;
|
|
37
|
+
hoverRadius?: number;
|
|
38
|
+
bloom?: number;
|
|
39
|
+
scanlines?: number;
|
|
40
|
+
vignette?: number;
|
|
41
|
+
grain?: number;
|
|
42
|
+
glitch?: number;
|
|
43
|
+
/** multiplicador de velocidad de la escena */
|
|
44
|
+
speed?: number;
|
|
45
|
+
/** máximo de fotogramas por segundo */
|
|
46
|
+
fps?: number;
|
|
47
|
+
opacity?: number;
|
|
48
|
+
/** 0–1; solo lo usan las escenas con `progress: true` */
|
|
49
|
+
progress?: number;
|
|
50
|
+
className?: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Fondo de caracteres generado con asciify-engine Studio a partir de una escena procedural.
|
|
54
|
+
* Rellena su contenedor (que debe tener `position: relative` y tamaño) y reacciona al puntero.
|
|
55
|
+
* Los cambios de props se aplican en caliente, sin remontar. La prop `palette` reinterpreta los
|
|
56
|
+
* colores con los tokens del tema (CSS), así que responde al cambio de tema sin coste.
|
|
57
|
+
*/
|
|
58
|
+
export default function AsciiBackground({ scene, asciiStyle, style: legacyStyle, cellSize, colorMode, charset, palette, tintAmount, asciiHover, hover: legacyHover, hoverStrength, hoverRadius, bloom, scanlines, vignette, grain, glitch, speed, fps, opacity, progress, className, }: AsciiBackgroundProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { painters } from "../../lib/asciify/painters.js";
|
|
5
|
+
import { createPointerTracker } from "../../lib/asciify/pointer.js";
|
|
6
|
+
/** Escenas disponibles. `progress: true` = la escena cambia con la prop `progress`. */
|
|
7
|
+
export const ASCII_SCENES = [
|
|
8
|
+
{ id: "lava", label: "Lava" },
|
|
9
|
+
{ id: "waves", label: "Olas de color" },
|
|
10
|
+
{ id: "plasma", label: "Plasma" },
|
|
11
|
+
{ id: "synthwave", label: "Synthwave" },
|
|
12
|
+
{ id: "galaxy", label: "Galaxia" },
|
|
13
|
+
{ id: "rings", label: "Anillos" },
|
|
14
|
+
{ id: "kaleido", label: "Caleidoscopio" },
|
|
15
|
+
{ id: "torus", label: "Toro" },
|
|
16
|
+
{ id: "cube", label: "Cubo" },
|
|
17
|
+
{ id: "pyramid", label: "Pirámide" },
|
|
18
|
+
{ id: "sphere", label: "Esfera" },
|
|
19
|
+
{ id: "cylinder", label: "Cilindro" },
|
|
20
|
+
{ id: "cone", label: "Cono" },
|
|
21
|
+
{ id: "octahedron", label: "Octaedro" },
|
|
22
|
+
{ id: "tetrahedron", label: "Tetraedro" },
|
|
23
|
+
{ id: "icosahedron", label: "Icosaedro" },
|
|
24
|
+
{ id: "hexprism", label: "Prisma hexagonal" },
|
|
25
|
+
{ id: "diamond", label: "Diamante" },
|
|
26
|
+
{ id: "torusknot", label: "Nudo toroidal" },
|
|
27
|
+
{ id: "orbs", label: "Orbes" },
|
|
28
|
+
{ id: "tide", label: "Surf (amanecer→noche)", progress: true },
|
|
29
|
+
{ id: "wave", label: "Ola de surf (mar→serie grande)", progress: true },
|
|
30
|
+
{ id: "brew", label: "Café (grano→taza)", progress: true },
|
|
31
|
+
{ id: "bloom", label: "Plantas (brote→flor)", progress: true },
|
|
32
|
+
{ id: "arcade", label: "Arcade (bloques→coin)", progress: true },
|
|
33
|
+
];
|
|
34
|
+
export const ASCII_STYLES = ["ascii", "braille", "dots", "lines", "blocks", "cross", "diagonal", "diamond", "mixed", "pixel", "mosaic", "lego", "voxel", "disco", "dither"];
|
|
35
|
+
export const ASCII_CHARSETS = {
|
|
36
|
+
detailed: " .,:;i1tfLCG08@",
|
|
37
|
+
standard: " .:-=+*#%@",
|
|
38
|
+
blocks: " ░▒▓█",
|
|
39
|
+
braille: " ⠁⠃⠇⡇⣇⣧⣷⣿",
|
|
40
|
+
technical: " .:;>_/[{}]=+*#@$",
|
|
41
|
+
};
|
|
42
|
+
function toSettings(p, accent) {
|
|
43
|
+
return {
|
|
44
|
+
style: p.style,
|
|
45
|
+
cellSize: p.cellSize,
|
|
46
|
+
colorMode: p.colorMode,
|
|
47
|
+
ink: accent,
|
|
48
|
+
charset: ASCII_CHARSETS[p.charset],
|
|
49
|
+
effects: { bloom: p.bloom, scanlines: p.scanlines, vignette: p.vignette, grain: p.grain, glitch: p.glitch },
|
|
50
|
+
hover: { effect: p.hover, strength: p.hoverStrength, radius: p.hoverRadius },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fondo de caracteres generado con asciify-engine Studio a partir de una escena procedural.
|
|
55
|
+
* Rellena su contenedor (que debe tener `position: relative` y tamaño) y reacciona al puntero.
|
|
56
|
+
* Los cambios de props se aplican en caliente, sin remontar. La prop `palette` reinterpreta los
|
|
57
|
+
* colores con los tokens del tema (CSS), así que responde al cambio de tema sin coste.
|
|
58
|
+
*/
|
|
59
|
+
export default function AsciiBackground({ scene = "lava", asciiStyle, style: legacyStyle, cellSize = 8, colorMode = "source", charset = "detailed", palette = "tint", tintAmount = 0.6, asciiHover, hover: legacyHover, hoverStrength = 0.7, hoverRadius = 0.28, bloom = 0.2, scanlines = 0, vignette = 0.3, grain = 0, glitch = 0, speed = 1, fps = 60, opacity = 1, progress, className = "", }) {
|
|
60
|
+
const hover = asciiHover ?? legacyHover ?? "water";
|
|
61
|
+
const style = asciiStyle ?? legacyStyle ?? "ascii";
|
|
62
|
+
const host = useRef(null);
|
|
63
|
+
const paint = useRef(null);
|
|
64
|
+
const player = useRef(null);
|
|
65
|
+
const speedRef = useRef(speed);
|
|
66
|
+
speedRef.current = speed;
|
|
67
|
+
const latest = useRef({ style, cellSize, colorMode, charset, hover, hoverStrength, hoverRadius, bloom, scanlines, vignette, grain, glitch });
|
|
68
|
+
latest.current = { style, cellSize, colorMode, charset, hover, hoverStrength, hoverRadius, bloom, scanlines, vignette, grain, glitch };
|
|
69
|
+
const fpsRef = useRef(fps);
|
|
70
|
+
fpsRef.current = fps;
|
|
71
|
+
// La escena se cambia sin remontar el motor
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
paint.current = painters[scene]();
|
|
74
|
+
}, [scene]);
|
|
75
|
+
// Ajustes en caliente
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const accent = host.current ? getComputedStyle(host.current).getPropertyValue("--acc").trim() || "#7cc4ff" : "#7cc4ff";
|
|
78
|
+
player.current?.update(toSettings(latest.current, accent));
|
|
79
|
+
}, [style, cellSize, colorMode, charset, hover, hoverStrength, hoverRadius, bloom, scanlines, vignette, grain, glitch]);
|
|
80
|
+
// Progreso propio de esta instancia para las escenas temáticas: se pasa al painter en cada fotograma,
|
|
81
|
+
// así dos fondos en la misma página no se pisan. Sin `progress`, la escena lee el `scrollState` global
|
|
82
|
+
// (el de las demos de scroll), que es el comportamiento de siempre.
|
|
83
|
+
const progressRef = useRef(undefined);
|
|
84
|
+
progressRef.current = progress === undefined ? undefined : { p: progress, v: 0 };
|
|
85
|
+
// Montaje único
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const el = host.current;
|
|
88
|
+
if (!el)
|
|
89
|
+
return;
|
|
90
|
+
let disposed = false;
|
|
91
|
+
const cleanups = [];
|
|
92
|
+
const canvas = document.createElement("canvas");
|
|
93
|
+
canvas.className = "ui-fill-canvas";
|
|
94
|
+
el.appendChild(canvas);
|
|
95
|
+
cleanups.push(() => canvas.remove());
|
|
96
|
+
const src = document.createElement("canvas");
|
|
97
|
+
const sctx = src.getContext("2d");
|
|
98
|
+
const tracker = createPointerTracker(canvas);
|
|
99
|
+
cleanups.push(() => tracker.destroy());
|
|
100
|
+
paint.current ??= painters[scene]();
|
|
101
|
+
let started = false;
|
|
102
|
+
let lastFrame = 0;
|
|
103
|
+
const fit = (w, h) => {
|
|
104
|
+
src.width = 480;
|
|
105
|
+
src.height = Math.max(2, Math.round((480 * h) / w));
|
|
106
|
+
};
|
|
107
|
+
const start = async (w, h) => {
|
|
108
|
+
started = true;
|
|
109
|
+
const { mountStudioMedia, normalizeStudioSettings } = await import("asciify-engine/studio");
|
|
110
|
+
if (disposed)
|
|
111
|
+
return;
|
|
112
|
+
fit(w, h);
|
|
113
|
+
const media = {
|
|
114
|
+
source: src,
|
|
115
|
+
width: src.width,
|
|
116
|
+
height: src.height,
|
|
117
|
+
duration: 0,
|
|
118
|
+
animated: true,
|
|
119
|
+
seek: async () => { },
|
|
120
|
+
frame: (time) => {
|
|
121
|
+
// Límite de fps: si es pronto, se reutiliza el fotograma anterior
|
|
122
|
+
if (fpsRef.current < 60 && time - lastFrame < 1 / fpsRef.current)
|
|
123
|
+
return src;
|
|
124
|
+
lastFrame = time;
|
|
125
|
+
tracker.update(time);
|
|
126
|
+
paint.current?.(sctx, src.width, src.height, time * speedRef.current, tracker.pointer, progressRef.current);
|
|
127
|
+
return src;
|
|
128
|
+
},
|
|
129
|
+
destroy: () => { },
|
|
130
|
+
};
|
|
131
|
+
const accent = getComputedStyle(el).getPropertyValue("--acc").trim() || "#7cc4ff";
|
|
132
|
+
const p = mountStudioMedia(canvas, media, {
|
|
133
|
+
settings: normalizeStudioSettings(toSettings(latest.current, accent)),
|
|
134
|
+
width: w,
|
|
135
|
+
height: h,
|
|
136
|
+
fps: 60,
|
|
137
|
+
onError: (e) => console.error("[AsciiBackground]", e),
|
|
138
|
+
});
|
|
139
|
+
p.resize(w, h, Math.min(window.devicePixelRatio || 1, 2));
|
|
140
|
+
player.current = p;
|
|
141
|
+
cleanups.push(() => {
|
|
142
|
+
player.current = null;
|
|
143
|
+
p.destroy();
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
147
|
+
const { width, height } = entry.contentRect;
|
|
148
|
+
if (width < 4 || height < 4)
|
|
149
|
+
return;
|
|
150
|
+
if (!started)
|
|
151
|
+
void start(Math.round(width), Math.round(height));
|
|
152
|
+
else if (player.current) {
|
|
153
|
+
fit(width, height);
|
|
154
|
+
player.current.resize(Math.round(width), Math.round(height), Math.min(window.devicePixelRatio || 1, 2));
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
ro.observe(el);
|
|
158
|
+
cleanups.push(() => ro.disconnect());
|
|
159
|
+
return () => {
|
|
160
|
+
disposed = true;
|
|
161
|
+
cleanups.reverse().forEach((f) => {
|
|
162
|
+
try {
|
|
163
|
+
f();
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
/* ya liberado */
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
171
|
+
}, []);
|
|
172
|
+
return (_jsxs("div", { className: `ui-fill ui-pal ui-pal--${palette} ${className}`, style: { "--tint": tintAmount, "--op": opacity }, "aria-hidden": true, children: [_jsx("div", { ref: host, className: "ui-fill__media" }), _jsx("i", { className: "ui-pal__a" }), _jsx("i", { className: "ui-pal__b" })] }));
|
|
173
|
+
}
|