You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/temas/estudio/+page.svelte

161 lines
5.5 KiB

<script lang="ts">
/**
* Estudio — the Eidos theme builder. A route-tool for theme authors that
* dogfoods the framework and previews REAL components under the drafted theme,
* applied GLOBALLY at :root (so real components re-skin) while the control
* chrome (literal colours) stays put.
*/
import { onDestroy } from 'svelte'
import { createActiveUix, setActiveUix } from '$active-uix'
import { Soma } from '$soma/core/soma.svelte'
import { siumLangs } from '$sium/langs/langs'
import { secsLangs } from '$libs/secs'
import { ActiveEidos } from '$uix/eidos'
import type { ScalingKey } from '$uix/eidos/lib/config-types'
import '@/uix/eidos/index.css'
import '@/uix/eidos/generated/palette.css'
import './_lib/estudio.css'
import { ThemeDraftStore, createDraftStore } from './_lib/store.svelte'
import { applyDraft, draftToCode, draftToJson } from './_lib/apply'
import ColorPanel from './panels/ColorPanel.svelte'
import TypographyPanel from './panels/TypographyPanel.svelte'
import ShapePanel from './panels/ShapePanel.svelte'
import SurfacesPanel from './panels/SurfacesPanel.svelte'
import SpacePanel from './panels/SpacePanel.svelte'
import GradientsPanel from './panels/GradientsPanel.svelte'
import AdvancedPanel from './panels/AdvancedPanel.svelte'
import Preview from './panels/Preview.svelte'
let store = $state(createDraftStore())
let themeName = $state('mi-tema')
// ── Runtime boot (uix + soma + eidos), mode/density/scaling from the store ──
const uix = createActiveUix({
langs: { schema: { sium: siumLangs, secs: secsLangs }, defaultLocale: 'es' as const },
events: { sound: false, haptic: false }
})
setActiveUix(uix)
Soma.create()
// One shared registry; the structural sources all re-read from the store on
// notify, so the value arg is ignored (eidos re-reads `get()`).
const listeners = new Set<() => void>()
function onChange<T>(handler: (value: T) => void): () => void {
const fn = () => handler(undefined as T)
listeners.add(fn)
return () => listeners.delete(fn)
}
const eidos = ActiveEidos.create({
applyDom: true,
modeSource: { get: () => store.mode, onChange },
densitySource: { get: () => store.density, onChange },
scalingSource: { get: () => String(store.scaling) as ScalingKey, onChange }
})
// Notify eidos sources when the structural axes change.
$effect(() => {
void store.mode
void store.density
void store.scaling
for (const l of listeners) l()
})
onDestroy(() => {
eidos.dispose()
uix.dispose()
})
// ── The single reactive application ──
$effect(() => {
applyDraft(uix, eidos, store)
})
// ── Tabs ──
type Tab = { id: string; label: string }
const TABS: Tab[] = [
{ id: 'color', label: 'Color' },
{ id: 'type', label: 'Tipografía' },
{ id: 'shape', label: 'Forma' },
{ id: 'surfaces', label: 'Superficies' },
{ id: 'space', label: 'Espacio' },
{ id: 'gradients', label: 'Gradientes' },
{ id: 'advanced', label: 'Avanzado' }
]
let tab = $state('color')
let showExport = $state(false)
let exportKind = $state<'code' | 'json'>('code')
const exportText = $derived(exportKind === 'code' ? draftToCode(store) : draftToJson(store))
function reset(): void {
store = createDraftStore()
}
</script>
<svelte:head><title>Estudio · Eidos theme builder</title></svelte:head>
<div class="studio">
<header class="topbar">
<span class="brand">Estudio <small>eidos theme builder</small></span>
<div class="top-group">
<span class="top-label">tema</span>
<input class="field-select" style="inline-size: 8rem;" bind:value={themeName} spellcheck="false" />
</div>
<div class="top-group">
<span class="top-label">apariencia</span>
<div class="seg">
<button class="seg-btn" data-active={store.mode === 'light'} onclick={() => (store.mode = 'light')}>◐ claro</button>
<button class="seg-btn" data-active={store.mode === 'dark'} onclick={() => (store.mode = 'dark')}>◑ oscuro</button>
</div>
</div>
<button class="btn-ghost" onclick={reset}>restablecer</button>
<button class="btn-primary" onclick={() => (showExport = !showExport)}>Exportar</button>
</header>
<div class="lab">
<aside class="controls">
<nav class="tabbar">
{#each TABS as t}
<button class="tab" data-active={tab === t.id} onclick={() => (tab = t.id)}>{t.label}</button>
{/each}
</nav>
{#if tab === 'color'}
<ColorPanel {store} />
{:else if tab === 'type'}
<TypographyPanel {store} />
{:else if tab === 'shape'}
<ShapePanel {store} />
{:else if tab === 'surfaces'}
<SurfacesPanel {store} />
{:else if tab === 'space'}
<SpacePanel {store} />
{:else if tab === 'gradients'}
<GradientsPanel {store} />
{:else if tab === 'advanced'}
<AdvancedPanel {store} />
{/if}
</aside>
<main>
{#if showExport}
<div class="panel" style="background:#14141f;border:1px solid #26263a;border-radius:16px;margin-block-end:1.25rem;">
<div style="display:flex;align-items:center;gap:0.5rem;">
<div class="seg">
<button class="seg-btn" data-active={exportKind === 'code'} onclick={() => (exportKind = 'code')}>código</button>
<button class="seg-btn" data-active={exportKind === 'json'} onclick={() => (exportKind = 'json')}>JSON</button>
</div>
<button class="btn-ghost" style="margin-inline-start:auto;" onclick={() => navigator.clipboard?.writeText(exportText)}>copiar</button>
<button class="btn-ghost" onclick={() => (showExport = false)}>cerrar</button>
</div>
<code class="export-code">{exportText}</code>
</div>
{/if}
<Preview {store} />
</main>
</div>
</div>

Powered by TurnKey Linux.