# Changelog — Neon Design System

All notable changes to this project are documented here.

---

## [v0.5.0] — 2026-05 — NeonImageSidebar (SVG-icon vertical tabs)

### Added

- **`NeonImageSidebar` + `NavItemImage` struct** (`ui/components/nav.slint`) — Counterpart to `NeonSidebar` that takes `image` icons (`@image-url("…")`) instead of `string` icons. Built for apps that ship SVG asset packs (theme-aware colorization, pixel-perfect rendering at small sizes). The image's `colorize` property is driven by selected/hover state, so a single `currentColor` SVG renders in `Theme.neon-cyan` when active and `Theme.text-muted` when idle. Same collapsed/expanded behavior + badge support as `NeonSidebar`.

### Why a separate component (and not just a richer NavItem)

Slint can't express `icon: image | string` in a struct field — each type is locked. Apps with mixed needs (some menus emoji, others SVG) instantiate both `NeonSidebar` and `NeonImageSidebar` side by side. The components are otherwise identical.

### Use case

Originally added so `ttrust` (a downstream consumer with extensive SVG icon packs for IFC / GIS workflows) could replace its hand-rolled 4-tab horizontal bars on the left/right side panels with a unified vertical sidebar without losing the SVG icons that drive its visual identity.

### Migration

`NeonSidebar` (string icons) is unchanged — pure addition. Consumers with image icons drop in `NeonImageSidebar` with the same `selected-index <=> ...` + `item-clicked(int)` API.

---

## [v0.4.0] — 2026-05 — Producción-grade rewrite + 11 nuevos componentes

> Esta versión cierra dos frentes en paralelo: (a) la lib se vuelve realmente
> consumible desde otra crate (Cargo path / git / crates.io), y (b) suma 11
> componentes nuevos para apps "data-heavy". Sigue siendo Slint puro, sin
> deps nativas extras.

### Added — arquitectura para uso como librería pública

- **`SLINT_LIBRARY_PATH`** (`src/lib.rs`) — `const &str` con la ruta absoluta a `ui/`. Los consumers la leen desde su `build.rs` para wirear el `library_paths` de Slint, lo que les permite escribir `import { ... } from "@neon";` en sus propios `.slint`.
- **`theme::ThemeAccess`** + macro **`impl_theme_access!(Window, Theme)`** (`src/theme.rs`) — el consumer hace una sola línea de boilerplate y obtiene `theme::set_dark_mode`, `theme::is_dark_mode`, `theme::toggle` sobre su `Window`. Sin acoplamiento al `DemoWindow` de la lib.
- **`neon_ui_setup!(Window, Theme, dark = true)`** (`src/bridge.rs`) — macro one-shot que hace `Window::new()?` + `set_dark_mode` en una expresión.
- **`color::*`** (`src/color.rs`) — parser/formatter de hex con Result<>, plus `to_slint_color`. Soporta `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`. 10 unit tests.
- **Cargo metadata profesional** — versión `0.4.0`, `[lib]` + `[[bin]]` separados, `[workspace] members = ["examples/simple"]`, `include = […]` que ship .slint + íconos + docs, `[package.metadata.docs.rs]` para builds de docs.rs, `LICENSE-MIT` + `LICENSE-APACHE` agregados.
- **`examples/simple/`** — consumer template real con su propio `Cargo.toml`, `build.rs`, `ui/app.slint`, `src/main.rs`. Demuestra el patrón de integración canónico.

### Added — nuevos componentes

- **NeonFloatingPanel** (`ui/components/floating_panel.slint`) — Panel flotante draggable + resizable + close X. Posición + tamaño expuestos como `in-out` para persistencia entre sesiones. Accent color configurable, badge opcional, signals `position-changed` / `size-changed` para auto-save.
- **NeonContextMenu + ContextMenuItem** (`ui/components/context_menu.slint`) — Menú flotante contextual con click-outside scrim + Escape-to-dismiss. Soporta separadores, items disabled, danger highlight, shortcut hints. Auto-flip cuando el ancla empuja el menú fuera del bound.
- **NeonToastQueue + ToastEntry** (`ui/components/notification.slint`) — Stack manager de toasts con cuatro kinds (info / success / warning / danger), accent bar + glyph, posicionable en cualquiera de las 4 esquinas. Host maneja la cola + el timer de auto-dismiss; Slint queda stateless.
- **NeonSplitterHandle + NeonVSplitterHandle** (`ui/components/splitview.slint`) — Primitivas de divider arrastrable (col-resize / row-resize). Drag con grab-on-down (sin jitter), bound-min/max dinámicos. Reemplazan el patrón roto del `NeonSplitView` legacy.
- **NeonVirtualTable** (`ui/components/virtual_table.slint`) — Tabla con `ListView` (virtualizada). Drop-in replacement para `NeonDataTable` cuando hay >200 filas. Empty state, sticky header, column-widths opcionales, double-click signal.
- **NeonFormField, NeonFormGroup, NeonFormSummary, NeonFormSubmit + FormErrorRow** (`ui/components/form_validation.slint`) — Wrappers visuales para forms. Required marker, touched gating de errores, error summary banner, submit button gateado por `is-valid` + busy spinner. Lógica de validators en Rust; Slint solo render.
- **NeonSparkline, NeonAreaChart, NeonScatterChart, NeonPieChart, NeonStackedBar + ScatterPoint, PieSlice** (`ui/components/charts_extra.slint`) — Cinco tipos extra de chart sin deps externas. Pie usa `Path` + `ArcTo` con `donut-fraction` configurable. StackedBar es la "linear pie" alternativa más legible a tamaños chicos.
- **NeonCombobox** (`ui/components/combobox.slint`) — Select con filtro inline (case-insensitive `contains`). Opciones que no matchean se quitan del layout vía `if`. Indice retornado en el callback es del array original (mapeo visible→original automático). "Limpiar selección" opcional.
- **NeonDateRangePicker** (`ui/components/date_range_picker.slint`) — Range picker con calendar grid 6×7. Click 1 = start, click 2 = end, click 3 = reinicia. Swap automático cuando end < start. Output `is-valid: bool`. Clamp defensivo de month/year (resuelve el crash del NeonDatePicker legacy).
- **NeonSkeleton (rewrite), NeonSkeletonText, NeonSkeletonGate** (`ui/components/skeleton.slint`) — Shimmer animado con Timer + linear-gradient sweep. Variants `rect / line / circle`. `NeonSkeletonText` para párrafos (línea final al 75%). `NeonSkeletonGate` es el wrapper conectado: muestra placeholder mientras `loading`, sino renderiza `@children` (siempre montado para no perder estado).

### Changed — mejoras a componentes existentes

- **NeonColorPicker** (`ui/components/color-picker.slint`) — Outputs ahora legibles desde Rust: `actual-color`, `red`, `green`, `blue`, `hex` como `out property`. Soporte bidireccional vía `apply-rgb` flag (caller setea r/g/b → flag, picker reverse-computa HSV). Signals `changed` (drag) + `committed` (pointer-up).
- **NeonNotification** (single) marcado como legacy; preferir `NeonToastQueue` en código nuevo.
- **NeonSplitView** marcado como compat — reemplazado por las primitivas `NeonSplitterHandle` / `NeonVSplitterHandle`.
- **NeonSkeleton** (advanced.slint) removido — la versión animada vive ahora en `skeleton.slint`. Re-exportado vía `index.slint`.

### Architecture decisions

- **Splitter pattern**: Slint permite `@children` una sola vez por componente — un "two-slot split view" requería un patrón complejo. Nuevo enfoque: el divider es un primitive standalone (`NeonSplitterHandle`), el caller arma el HBox/VBox manualmente. Más flexible, drop-in con `HorizontalLayout` / `VerticalLayout` estándar.
- **Toast queue**: Slint no puede mutar arrays propias en respuesta a un timer interno. El host (Rust) es el dueño del `Vec<ToastEntry>` y del timer de auto-dismiss; el componente solo render + emite `dismiss(id)`.
- **Form validation**: validators con acceso a otras fields (password == confirm) o a estado externo (email taken via API) no caben en expresiones Slint. Lógica en Rust, wrappers visuales en Slint.
- **SkeletonGate**: `@children` no puede aparecer dentro de un `if`. Solución: render @children siempre con `visible: !loading` (no pierde focus/scroll en flips) + overlay condicional encima.

### Fixed

- `NeonSplitView` (legacy) tenía un upper-bound de drag hardcodeado a 600px — el divider no podía pasar de ahí en parents más anchos.
- `NeonSplitView` (legacy) reseteaba el grab en cada `moved` event causando jitter cuando el divider se movía bajo el cursor entre eventos. Las primitivas nuevas guardan `start-pos` en `pointer-event(down)` y aplican delta acumulado.
- `NeonSplitView` (legacy) pasaba `@children` SOLO al panel izquierdo; el derecho era un filler stretchy vacío. El nuevo patrón con primitives elimina la confusión.
- `NeonColorPicker` exponía `actual-color` solo como `property` (privada) — Rust no podía leerlo. Promovido a `out property` junto con r/g/b/hex.
- `NeonDatePicker` original sin clamp de `month` / `year` — `month: 0` o `month: 13` causaban out-of-bounds en `month-names[]`. El nuevo `NeonDateRangePicker` aplica clamp defensivo.

### Build / docs

- **build.rs** detecta automáticamente los archivos nuevos vía glob — no requirió cambios.
- **docs/component-api.md** extendido con secciones para los 11 nuevos componentes.
- **docs/index.html** sidebar reorganizada: badges `v0.4` en los items nuevos, sección "Skeleton" reescrita para reflejar el shimmer + variants. Visual previews agregados para cada componente nuevo.
- **docs/MIGRATION.md, docs/neon-design-system-roadmap.md, docs/performance-theming.md**: removidos (info ya integrada al CHANGELOG y al README).
- **CI**: `.github/workflows/ci.yml` con `cargo build --workspace`, `cargo test --workspace`, `cargo doc --no-deps`, `cargo clippy --workspace -- -D warnings`.
- **`LICENSE-MIT` + `LICENSE-APACHE`** agregados en root para cumplir SPDX `MIT OR Apache-2.0` declarado en `Cargo.toml`.

### Breaking changes (v0.3 → v0.4)

| Antes (v0.3) | Ahora (v0.4) | Migración |
|---|---|---|
| `slint_ui_system::DemoWindow` (re-exportado por `lib.rs`) | No existe en la API pública | Los consumers no deberían depender de `DemoWindow`. La lib expone solo helpers Rust + el path a los `.slint`. |
| `slint_ui_system::bridge::UiBridge::new(window)` | Reemplazado por `theme::set_dark_mode(&win, …)` + `impl_theme_access!` macro | Llamá `set_dark_mode` directo, sin wrapper struct. El old `UiBridge` estaba acoplado a `DemoWindow` y a un set fijo de propiedades del demo. |
| `bridge.set_status(…)`, `show_modal()`, `show_drawer()`, `show_toast()`, `switch_tab()`, `collapse_left_panel()`, etc. | Removidos | Eran helpers específicos del demo. Los consumers tienen sus propias propiedades en su Slint root y las setean directo. |
| Imports relativos: `import { NeonButton } from "../slint-ui-system/ui/components/button.slint";` | `import { NeonButton } from "@neon";` | Agregar `slint-ui-system` a `[build-dependencies]` del consumer + wirear `library_paths` en su `build.rs` con `SLINT_LIBRARY_PATH`. Ver README. |
| `theme.slint` con valores hardcoded (dark/light no funcionaba en runtime) | Cada token usa `dark-mode ? X : ThemeLight.X` | El switch ahora SÍ funciona en runtime. `theme::set_dark_mode(&win, false)` re-tinta toda la UI. |
| `Theme` global no expuesto al Rust API por default | `demo.slint` tiene `export { Theme, AnimationPresets } from "theme.slint";` y los consumers deben hacer lo mismo en su entry .slint | Agregar la línea de re-export al root .slint del consumer. Slint solo expone globals al Rust API si están exportados desde el archivo root. |

---

## [v0.3.0] — 2026-05-02 — Animations & Layout System (inspirado en adabraka-ui)

### Added
- **Animation duration tokens**: `duration-fastest` (50ms) → `duration-slowest` (500ms), `transition-fast/base/slow` en theme.slint
- **AnimationPresets global**: `fade-in` (200ms), `slide-up` (300ms), `scale-in` (250ms), `panel-slide` (150ms), `hover-glow` (150ms), `tab-switch` (100ms) con duraciones estandarizadas
- **AnimationPresetsLight**: Contraparte light para consistencia cross-theme
- **Fade-in animations**: NeonModal, NeonConfirmDialog, NeonTooltip, NeonNotification, NeonDropdown popup — `animate opacity` con AnimationPresets
- **Slide-in drawer**: NeonDrawer — `animate width` con `clip: true` para slide lateral suave
- **NeonVStack / NeonHStack**: Layout wrappers con spacing/padding/alignment/justify conectados a Theme tokens
- **NeonContainer**: Centered container con presets sm (640px), md (768px), lg (1024px), xl (1280px)
- **NeonSpacer**: Spacer flexible (flex: 1) para layouts que necesitan empujar elementos
- **NeonDivider**: Separador horizontal/vertical con `color: Theme.border` y thickness configurable
- **Tipografía semántica**: NeonH1..NeonH6 (títulos), NeonBody/NeonBodySmall (cuerpo), NeonCaption, NeonCode, NeonMuted — cada uno con font-size/weight/color del tema
- **index.slint**: Single import point — `import { Theme, NeonButton } from "../index.slint"` en lugar de imports individuales

### Changed
- **modal.slint**: NeonModal backdrop y panel usan `animate opacity`; NeonDrawer usa `animate width` con `clip: true`
- **tooltip.slint**: Importa AnimationPresets, `animate opacity` con duración scale-in
- **notification.slint**: `shown` cambiado a `in-out property` para auto-cierre; `animate opacity` con fade-in
- **dropdown.slint**: Popup menu con `animate opacity` y AnimationPresets.scale-in
- **theme_light.slint**: Añadido AnimationPresetsLight con mismas duraciones
- **theme.slint**: Nuevos tokens de animación y transition durations

### Architecture
- Inspirado en adabraka-ui (https://github.com/augani/adabraka-ui): filosofía shadcn/ui, layout abstractions, animation presets, theme tokens extendidos

### Added
- **Disabled states**: `enabled` property añadido a todos los componentes interactivos (button, input, toggle, slider, select, datepicker, radiogroup) con visual opacity 0.4
- **Focus rings**: `has-focus` property + `border-focus` (neon-cyan) en todos los componentes interactivos
- **Keyboard navigation**: FocusScope en demo con Escape (cierra overlays), Left/Right (cambia tabs)
- **NeonTooltip**: Nuevo componente de ayuda contextual overlay (`ui/components/tooltip.slint`)
- **NeonDropdown**: Nuevo menú desplegable contextual (`ui/components/dropdown.slint`)
- **NeonPagination**: Nueva navegación de páginas con 5 slots y chevrons (`ui/components/pagination.slint`)
- **NeonBreadcrumbs**: Nueva navegación de migas de pan (`ui/components/pagination.slint`)
- **NeonSplitView**: Nuevo divider horizontal arrastrable (`ui/components/splitview.slint`)
- **NeonNotification**: Nuevo toast notification en bottom-right (`ui/components/notification.slint`)
- **Theme tokens**: `font-sans`, `font-mono`, `elevation-*` (flattened simulated), z-index guidance documentado
- **build.rs**: Migrado a `glob` automático para `rerun-if-changed`

### Changed
- **theme.slint**: Ahora incluye font-mono, font-sans, elevation-flat/low/md/high, z-index doc
- **demo.slint**: Nuevo tab "Utilities" con pagination, breadcrumbs, tooltip, toast, dropdown; toggle 🔓/🔒 para disabled states
- **All buttons**: Press effect via padding-left/right en `touch.pressed`
- **NeonModal**: Nuevas props `modal-width`, `closable`
- **NeonDrawer**: Nueva prop `side` ("left" | "right")
- **Cargo.toml**: Añadida dependencia `glob = "0.3"` para build.rs

### Fixed
- Right Activity Bar toggle verificado y consistente con left bar
- FocusScope keyboard handling en demo

---

## [v0.1.0] — 2026-04 — Initial Foundation

### Added
- `src/main.rs` — Binary entry point con `DemoWindow`
- `src/lib.rs` — Crate root con re-exports
- `src/bridge.rs` — UiBridge (Rust ↔ Slint glue)
- **Theme system**: Dark + Light con 7 surfaces, 4 borders, 4 text, 6 accents, 10 spacings, 5 radius, 7 font sizes
- **14 component modules**: button, input, card, modal, nav, badge, toggle, slider, list, chart, form, tree, color-picker, icons
- **57 SVG icons** portados de Territorial Twin
- **VSCode layout**: 5-zone shell con activity bars, side panels, tabs, status bar
- **Interactions**: Collapse/expand 150ms, resize dividers 4px, toggle toggle
- `docs/index.html` — Documentación interactiva completa
- `ui/layouts/app_layout.slint` — App shell reutilizable
- `ui/layouts/page_layout.slint` — PageHeader + Toolbar
- `README.md` con quick start y estructura
