refactor: migrate frontend from shadcn/Tailwind to Mantine v9

Reescribe todos los componentes UI para usar Mantine v9 en lugar de shadcn/Tailwind.
Elimina cn(), CVA, components.json, theme_provider custom y globals.css con Tailwind.
Añade 25+ componentes nuevos (AppShell, AuthForm, DatePickerInput, Dropzone, etc.)
y MantineProvider como wrapper estándar del sistema de temas.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 23:46:44 +02:00
parent 4b2bb6998a
commit 97a3c84625
163 changed files with 6008 additions and 6310 deletions
+6 -6
View File
@@ -1,11 +1,11 @@
export const chartColors = [
'hsl(var(--chart-1, 220 70% 50%))',
'hsl(var(--chart-2, 160 60% 45%))',
'hsl(var(--chart-3, 30 80% 55%))',
'hsl(var(--chart-4, 280 65% 60%))',
'hsl(var(--chart-5, 340 75% 55%))',
'#3b82f6',
'#10b981',
'#f59e0b',
'#8b5cf6',
'#ef4444',
]
export function getChartColor(index: number): string {
return chartColors[index % chartColors.length]
return chartColors[index % chartColors.length]!
}
-40
View File
@@ -1,40 +0,0 @@
---
name: cn
kind: function
lang: ts
domain: core
version: "1.0.0"
purity: pure
signature: "cn(...inputs: ClassValue[]): string"
description: "Combina clases CSS con clsx y resuelve conflictos Tailwind con tailwind-merge. Utilidad fundamental para composición de estilos."
tags: [css, tailwind, classname, merge, utility]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [clsx, tailwind-merge]
params:
- name: inputs
desc: "Clases CSS en cualquier formato: strings, arrays, objetos con condiciones booleanas"
output: "String con clases CSS combinadas y mergeadas, sin duplicados y conflictos Tailwind resueltos"
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/core/cn.ts"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/Frontend_Library"
source_license: "MIT"
source_file: "frontend/src/lib/utils.ts"
---
## Ejemplo
```typescript
cn("px-4 py-2", "px-6") // "px-6 py-2" (tailwind-merge resuelve conflicto)
cn("text-red-500", false && "hidden") // "text-red-500" (clsx filtra falsy)
cn("rounded-lg", className) // composición con className externo
```
## Notas
Base de todo el sistema de estilos. Todos los componentes la usan para componer className.
-6
View File
@@ -1,6 +0,0 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs))
}
@@ -1,69 +0,0 @@
---
name: generate_theme_css
kind: function
lang: ts
domain: core
version: "1.0.0"
purity: pure
signature: "generateThemeCss(colors: Record<string, string>, selector?: string): string"
description: "Genera un bloque CSS con variables de tema a partir de un objeto de tokens. Convierte claves camelCase a kebab-case automaticamente. Pura — solo transforma datos, no accede al DOM."
tags: [theme, css, generator, pure]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
- name: colors
desc: "Objeto con pares clave-valor de nombre variable CSS a valor de color"
- name: selector
desc: "Selector CSS donde inyectar variables (':root' por defecto)"
output: "String con bloque CSS completo conteniendo definiciones de variables de tema"
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/core/generate_theme_css.ts"
---
## Ejemplo
```typescript
import { generateThemeCss } from './generate_theme_css'
import { themeConfigToColors } from './theme_config_to_colors'
import { darkTheme } from '../ui/themes'
// Generar CSS para inyectar en <style>
const colors = themeConfigToColors(darkTheme)
const css = generateThemeCss(colors)
// Output:
// :root {
// --background: oklch(8% 0.015 260);
// --foreground: oklch(95% 0.01 260);
// --card: oklch(12% 0.015 260);
// --card-foreground: oklch(95% 0.01 260);
// ...
// }
// Inyectar en el documento
const style = document.createElement('style')
style.textContent = css
document.head.appendChild(style)
// Generar para selector especifico (dark mode)
const darkCss = generateThemeCss(colors, '.dark')
// Output: .dark { --background: oklch(...); ... }
// Generar para multiples selectores
const lightCss = generateThemeCss(lightColors, ':root')
const darkCss2 = generateThemeCss(darkColors, ':root.dark')
const combined = [lightCss, darkCss2].join('\n\n')
```
## Notas
Funcion pura — sin acceso al DOM, sin side effects. Util para SSR, generacion de archivos CSS estaticos, o pre-generar temas en build time.
La conversion camelCase → kebab-case es simple (reemplaza mayusculas con `-` + minuscula). No maneja casos especiales como `backgroundColor``background-color`; los tokens del registry ya usan nombres semanticos directos (`background`, `cardForeground`, etc.).
Compone naturalmente con `themeConfigToColors` del registry: `generateThemeCss(themeConfigToColors(config))`.
@@ -1,23 +0,0 @@
/**
* Genera un bloque CSS con variables de tema para inyectar como &lt;style&gt; o
* escribir en un archivo .css.
*
* Convierte claves camelCase a kebab-case automaticamente:
* `cardForeground` → `--card-foreground`
*
* @param colors - Objeto con tokens de tema. Claves en camelCase, valores CSS.
* @param selector - Selector CSS donde aplicar las variables. Por defecto `:root`.
* @returns String CSS con el bloque completo.
*/
export function generateThemeCss(
colors: Record<string, string>,
selector: string = ':root',
): string {
const lines = Object.entries(colors)
.map(([key, value]) => {
const cssName = key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)
return ` --${cssName}: ${value};`
})
.join('\n')
return `${selector} {\n${lines}\n}`
}
+1 -1
View File
@@ -1,7 +1,7 @@
const defaultColors = ['#3b82f6', '#22c55e', '#f59e0b', '#8b5cf6', '#ec4899']
export function getSeriesColor(index: number, color?: string): string {
return color || defaultColors[index % defaultColors.length]
return color ?? defaultColors[index % defaultColors.length]!
}
export { defaultColors }
@@ -1,50 +0,0 @@
---
name: get_theme_tokens
kind: function
lang: ts
domain: core
version: "1.0.0"
purity: impure
signature: "getThemeTokens(): ThemeTokens"
description: "Lee todas las CSS variables de tema del documento y devuelve un objeto tipado con los valores computados desde :root. Util para pasar colores a APIs que no entienden CSS variables (canvas, sigma.js, D3)."
tags: [theme, css, tokens, runtime, dom]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
output: "Objeto ThemeTokens con todas las variables CSS de tema resueltas (colores, tipografía, espaciado)"
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/core/get_theme_tokens.ts"
---
## Ejemplo
```typescript
import { getThemeTokens } from './get_theme_tokens'
const tokens = getThemeTokens()
// Pasar colores a sigma.js (que no soporta CSS variables)
const sigmaSettings = {
defaultNodeColor: tokens.primary,
defaultEdgeColor: tokens.muted,
labelColor: { color: tokens.foreground },
}
// Pasar colores a un canvas 2D
const ctx = canvas.getContext('2d')
ctx.fillStyle = tokens.background
ctx.strokeStyle = tokens.border
```
## Notas
Impura — accede a `document.documentElement` y `getComputedStyle`. Solo disponible en browser.
Los valores retornados son los valores sin procesar de las CSS variables (ej: `oklch(8% 0.015 260)`). Para obtener valores RGB computed (necesarios para algunas APIs), usar `getComputedColor`.
Funciona con cualquier tema activo: el resultado cambia automaticamente cuando se cambia el tema via `applyTheme`.
@@ -1,59 +0,0 @@
/** Tokens de tema leidos de las CSS variables activas en :root. */
export interface ThemeTokens {
background: string
foreground: string
card: string
cardForeground: string
popover: string
popoverForeground: string
primary: string
primaryForeground: string
secondary: string
secondaryForeground: string
muted: string
mutedForeground: string
accent: string
accentForeground: string
destructive: string
destructiveForeground: string
success: string
successForeground: string
border: string
input: string
ring: string
}
/**
* Lee todas las CSS variables de tema del documento y devuelve un objeto
* tipado con los valores computados desde :root.
*
* Util para pasar colores a APIs que no entienden CSS variables
* (canvas, sigma.js, D3, etc.).
*/
export function getThemeTokens(): ThemeTokens {
const style = getComputedStyle(document.documentElement)
const get = (name: string) => style.getPropertyValue(`--${name}`).trim()
return {
background: get('background'),
foreground: get('foreground'),
card: get('card'),
cardForeground: get('card-foreground'),
popover: get('popover'),
popoverForeground: get('popover-foreground'),
primary: get('primary'),
primaryForeground: get('primary-foreground'),
secondary: get('secondary'),
secondaryForeground: get('secondary-foreground'),
muted: get('muted'),
mutedForeground: get('muted-foreground'),
accent: get('accent'),
accentForeground: get('accent-foreground'),
destructive: get('destructive'),
destructiveForeground: get('destructive-foreground'),
success: get('success'),
successForeground: get('success-foreground'),
border: get('border'),
input: get('input'),
ring: get('ring'),
}
}
@@ -1,41 +0,0 @@
---
name: theme_config_to_colors
kind: function
lang: ts
domain: core
version: "1.0.0"
purity: pure
signature: "themeConfigToColors(config: ThemeConfig): ThemeColors"
description: "Convierte un ThemeConfig completo a ThemeColors plano para inyectar como CSS variables. Mapea tokens semánticos a variables CSS."
tags: [theme, colors, css-variables, conversion]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
- name: config
desc: "Configuración de tema con propiedades semánticas de color"
output: "Objeto ThemeColors con variables CSS estandarizadas mapeadas de la config"
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/core/theme_config_to_colors.ts"
source_repo: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/Frontend_Library"
source_license: "MIT"
source_file: "frontend/src/themes/types.ts"
---
## Ejemplo
```typescript
const colors = themeConfigToColors(darkThemeConfig)
// { background: '...', foreground: '...', primary: '...', ... }
```
## Notas
Puente entre el sistema de temas estructurado (ThemeConfig) y el sistema plano de CSS variables que consumen los componentes.
Depende de los tipos ThemeConfig y ThemeColors definidos en `frontend/types/ui/theme_config.ts`. El tipo aún no está indexado en la BD (pendiente añadir theme_config.md para que fn index lo registre).
@@ -1,49 +0,0 @@
import type { ThemeConfig, ThemeColors } from "../../types/ui/theme_config"
export function themeConfigToColors(config: ThemeConfig): ThemeColors {
const { colors } = config
return {
background: colors.background.default,
foreground: colors.foreground.default,
card: colors.surface.raised,
cardForeground: colors.foreground.default,
popover: colors.surface.overlay,
popoverForeground: colors.foreground.default,
primary: colors.brand.primary,
primaryForeground: colors.brand.primaryForeground,
secondary: colors.brand.secondary,
secondaryForeground: colors.brand.secondaryForeground,
muted: colors.background.muted,
mutedForeground: colors.foreground.muted,
accent: colors.brand.accent,
accentForeground: colors.brand.accentForeground,
destructive: colors.status.error,
destructiveForeground: colors.status.errorForeground,
success: colors.status.success,
successForeground: colors.status.successForeground,
warning: colors.status.warning,
warningForeground: colors.status.warningForeground,
info: colors.status.info,
infoForeground: colors.status.infoForeground,
surface: colors.surface.raised,
surfaceHover: colors.background.subtle,
overlay: colors.surface.overlay,
border: colors.border.default,
input: colors.border.default,
ring: colors.ring,
chart1: colors.chart[1],
chart2: colors.chart[2],
chart3: colors.chart[3],
chart4: colors.chart[4],
chart5: colors.chart[5],
sidebar: colors.sidebar.background,
sidebarForeground: colors.sidebar.foreground,
sidebarPrimary: colors.brand.primary,
sidebarPrimaryForeground: colors.brand.primaryForeground,
sidebarAccent: colors.sidebar.accent,
sidebarAccentForeground: colors.sidebar.accentForeground,
sidebarBorder: colors.sidebar.border,
sidebarRing: colors.sidebar.ring,
}
}