Align application surfaces with Launcher and SEO

This commit is contained in:
DCCONSTRUCTIONS
2026-07-10 21:35:06 +03:00
parent 0e030136bb
commit 41e491191c
7 changed files with 102 additions and 160 deletions
+37 -57
View File
@@ -56,16 +56,16 @@ type MaterialDraft = {
const materialDefaults: Record<NodedcTheme, MaterialDraft> = {
dark: {
panelHex: "#16161a",
panelOpacity: 68,
fieldHex: "#101016",
fieldOpacity: 78,
panelHex: "#151517",
panelOpacity: 100,
fieldHex: "#2a2a2c",
fieldOpacity: 100,
},
light: {
panelHex: "#ffffff",
panelOpacity: 62,
panelOpacity: 100,
fieldHex: "#ffffff",
fieldOpacity: 72,
fieldOpacity: 100,
},
};
@@ -87,7 +87,7 @@ const sectionDefinitions: Record<CatalogSection, { eyebrow: string; title: strin
foundation: {
eyebrow: "01 / FOUNDATION",
title: "Основа",
description: "Тема, акцент, стекло и неизменяемая геометрия приложения.",
description: "Тема, нейтральные поверхности и неизменяемая геометрия приложения.",
icon: "panel",
},
controls: {
@@ -234,7 +234,7 @@ export function CatalogApp() {
dark: { ...materialDefaults.dark },
light: { ...materialDefaults.light },
}));
const [savedEnvironmentControl, setSavedEnvironmentControl] = useState<string | null>(null);
const [environmentLayoutSaved, setEnvironmentLayoutSaved] = useState(false);
const workspace = useApplicationWorkspace<CatalogSection>();
const { navigationOpen: guidelineOpen, activeView: activeSection, contentExpanded: panelExpanded } = workspace;
const [selectedStatus, setSelectedStatus] = useState<(typeof selectOptions)[number]["value"]>("active");
@@ -344,7 +344,7 @@ export function CatalogApp() {
}));
};
const saveEnvironmentLayout = (control: string) => {
const saveEnvironmentLayout = () => {
const draft = {
theme,
accentHex,
@@ -367,15 +367,9 @@ export function CatalogApp() {
} catch {
// The preview remains useful in restricted browser contexts even without local persistence.
}
setSavedEnvironmentControl(control);
setEnvironmentLayoutSaved(true);
};
const savedAction = (control: string) => (
<IconButton label={`Сохранить «${control}» в локальный layout`} onClick={() => saveEnvironmentLayout(control)}>
<Icon name="save" />
</IconButton>
);
const environmentSections = [
{
id: "application-material",
@@ -384,24 +378,21 @@ export function CatalogApp() {
tone: "accent" as const,
content: (
<>
<ControlRow label="Акцент" action={savedAction("Акцент")}>
<ControlRow label="Акцент">
<ColorField label="Акцент приложения" value={accentHex} onChange={setAccentHex} />
</ControlRow>
<ControlRow label="Плашки" action={savedAction("Плашки")}>
<ControlRow label="Плашки">
<ColorField label="Цвет плашек" value={material.panelHex} onChange={(panelHex) => updateMaterial({ panelHex })} />
</ControlRow>
<ControlRow label="Прозрачность плашки" action={savedAction("Прозрачность плашки")}>
<ControlRow label="Прозрачность плашки">
<RangeControl label="Прозрачность плашки" showLabel={false} value={material.panelOpacity} min={18} max={96} formatValue={(value) => `${value}%`} onChange={(panelOpacity) => updateMaterial({ panelOpacity })} />
</ControlRow>
<ControlRow label="Поля ввода" action={savedAction("Поля ввода")}>
<ControlRow label="Поля ввода">
<ColorField label="Цвет полей ввода" value={material.fieldHex} onChange={(fieldHex) => updateMaterial({ fieldHex })} />
</ControlRow>
<ControlRow label="Прозрачность полей" action={savedAction("Прозрачность полей")}>
<ControlRow label="Прозрачность полей">
<RangeControl label="Прозрачность полей" showLabel={false} value={material.fieldOpacity} min={18} max={100} formatValue={(value) => `${value}%`} onChange={(fieldOpacity) => updateMaterial({ fieldOpacity })} />
</ControlRow>
<p className="catalog-environment-draft" aria-live="polite">
{savedEnvironmentControl ? `«${savedEnvironmentControl}» сохранён в локальный layout. Сервер и MCP не используются.` : "Дискета сохраняет текущий параметр в локальный layout браузера."}
</p>
</>
),
},
@@ -411,15 +402,9 @@ export function CatalogApp() {
tone: "accent" as const,
content: (
<>
<ControlRow label="Цвет" action={savedAction("Цвет подсветки")}><ColorField value={lightColor} onChange={setLightColor} /></ControlRow>
<div className="catalog-environment-with-action">
<RangeControl label="Яркость" value={brightness} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setBrightness} />
{savedAction("Яркость")}
</div>
<div className="catalog-environment-with-action">
<RangeControl label="Дистанция свечения" value={glowDistance} min={0} max={200} formatValue={(value) => `${value}%`} onChange={setGlowDistance} />
{savedAction("Дистанция свечения")}
</div>
<ControlRow label="Цвет"><ColorField value={lightColor} onChange={setLightColor} /></ControlRow>
<RangeControl label="Яркость" value={brightness} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setBrightness} />
<RangeControl label="Дистанция свечения" value={glowDistance} min={0} max={200} formatValue={(value) => `${value}%`} onChange={setGlowDistance} />
</>
),
},
@@ -429,7 +414,7 @@ export function CatalogApp() {
tone: "accent" as const,
content: (
<>
<ControlRow label="Связи по умолчанию" action={savedAction("Связи по умолчанию")}>
<ControlRow label="Связи по умолчанию">
<Select
variant="split"
label="Тип связи"
@@ -442,11 +427,8 @@ export function CatalogApp() {
onChange={setConnectionType}
/>
</ControlRow>
<div className="catalog-environment-with-action">
<Checker checked={usePortColors} label="Использовать цвет портов" onChange={setUsePortColors} />
{savedAction("Использовать цвет портов")}
</div>
<ControlRow label="Цвет связей по умолчанию" action={savedAction("Цвет связей")}><ColorField value={connectionColor} onChange={setConnectionColor} /></ControlRow>
<Checker checked={usePortColors} label="Использовать цвет портов" onChange={setUsePortColors} />
<ControlRow label="Цвет связей по умолчанию"><ColorField value={connectionColor} onChange={setConnectionColor} /></ControlRow>
<ControlRow label="Применить ко всем"><Button variant="primary" shape="pill" width="full">Применить</Button></ControlRow>
</>
),
@@ -457,16 +439,10 @@ export function CatalogApp() {
tone: "accent" as const,
content: (
<>
<ControlRow label="Заливка" action={savedAction("Заливка портов")}><ColorField value={fillColor} onChange={setFillColor} /></ControlRow>
<div className="catalog-environment-with-action">
<RangeControl label="Прозрачность заливки" value={fillOpacity} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setFillOpacity} />
{savedAction("Прозрачность заливки")}
</div>
<ControlRow label="Обводка" action={savedAction("Обводка портов")}><ColorField value={strokeColor} onChange={setStrokeColor} /></ControlRow>
<div className="catalog-environment-with-action">
<RangeControl label="Прозрачность обводки" value={strokeOpacity} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setStrokeOpacity} />
{savedAction("Прозрачность обводки")}
</div>
<ControlRow label="Заливка"><ColorField value={fillColor} onChange={setFillColor} /></ControlRow>
<RangeControl label="Прозрачность заливки" value={fillOpacity} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setFillOpacity} />
<ControlRow label="Обводка"><ColorField value={strokeColor} onChange={setStrokeColor} /></ControlRow>
<RangeControl label="Прозрачность обводки" value={strokeOpacity} min={0} max={100} formatValue={(value) => `${value}%`} onChange={setStrokeOpacity} />
</>
),
},
@@ -477,18 +453,18 @@ export function CatalogApp() {
case "foundation":
return (
<div className="catalog-grid catalog-grid--foundation">
<Preview title="Тема приложения" note={theme === "dark" ? "dark glass" : "light glass"}>
<Preview title="Тема приложения" note={theme === "dark" ? "Launcher / dark" : "SEO / light"}>
<div className="catalog-theme-row">
<Button variant={theme === "dark" ? "primary" : "secondary"} shape="pill" onClick={() => setTheme("dark")}>Dark</Button>
<Button variant={theme === "light" ? "primary" : "secondary"} shape="pill" onClick={() => setTheme("light")}>Light</Button>
</div>
<p className="catalog-preview__explanation">Тема задаёт нейтральный каркас. Акцент, плашки, поля и их прозрачность настраиваются живым Environment control там же сохраняется локальный draft.</p>
<p className="catalog-preview__explanation">Две базовые темы фиксированы по эталонам: чёрная Launcher/Hub, белая SEO. Стекло используется только в модальных окнах.</p>
</Preview>
<Preview title="Иерархия стекла" note="default / strong / soft">
<Preview title="Поверхности приложения" note="Launcher / SEO">
<div className="catalog-surface-stack">
<GlassSurface padding="md">Default surface</GlassSurface>
<GlassSurface tone="strong" padding="md">Strong floating surface</GlassSurface>
<GlassSurface tone="soft" padding="md">Soft nested surface</GlassSurface>
<GlassSurface padding="md">Основная поверхность</GlassSurface>
<GlassSurface tone="strong" padding="md">Контентное окно</GlassSurface>
<GlassSurface tone="soft" padding="md">Вложенная область</GlassSurface>
</div>
</Preview>
<Preview title="Каркас Launcher" note="fixed header / stage / panels" className="catalog-preview--wide">
@@ -555,10 +531,14 @@ export function CatalogApp() {
<Preview title="Environment controls" note="Engine / select + accordion" className="catalog-preview--wide">
<div className="catalog-environment-demo">
<GlassSurface tone="strong" radius="panel" className="catalog-environment-card">
<div className="catalog-environment-title"><strong>Настройки окружения</strong><span>ENGINE / новый канон</span></div>
<div className="catalog-environment-title">
<div><strong>Настройки окружения</strong><span>ENGINE / новый канон</span></div>
<IconButton label="Сохранить текущий layout" onClick={saveEnvironmentLayout}><Icon name="save" /></IconButton>
</div>
{environmentLayoutSaved ? <p className="catalog-environment-draft" aria-live="polite">Layout сохранён локально. Сервер и MCP не используются.</p> : null}
<Inspector sections={environmentSections} defaultOpen={["application-material"]} />
</GlassSurface>
<p className="catalog-preview__explanation">Environment controls единый референс Engine: palette, range, checker, split-select и раскрывающиеся sections. Верхняя секция меняет настоящий material приложения; отдельной вкладки Inspector больше нет.</p>
<p className="catalog-preview__explanation">Environment controls единый референс Engine: palette, range, checker, split-select и раскрывающиеся sections. Верхняя секция меняет цвет плашек и полей приложения; сохраняется весь текущий layout одной кнопкой сверху.</p>
</div>
</Preview>
<Preview title="Статусы операций" note="не цвет приложения" className="catalog-preview--wide">
+13 -16
View File
@@ -281,7 +281,7 @@ textarea {
grid-template-rows: 3.2rem 1fr;
gap: 0.6rem;
border-radius: 1.1rem;
background: color-mix(in srgb, var(--nodedc-canvas-soft) 76%, transparent);
background: var(--nodedc-canvas-soft);
padding: 0.6rem;
}
@@ -300,11 +300,11 @@ textarea {
}
.catalog-shell-diagram__nav {
background: color-mix(in srgb, var(--nodedc-glass-control-hover) 90%, transparent) !important;
background: var(--nodedc-glass-control-hover) !important;
}
.catalog-shell-diagram__content {
background: color-mix(in srgb, var(--nodedc-glass-control-hover) 70%, transparent) !important;
background: var(--nodedc-glass-control-bg) !important;
}
.catalog-environment-demo {
@@ -314,9 +314,7 @@ textarea {
align-items: start;
gap: clamp(1rem, 4vw, 4rem);
border-radius: var(--nodedc-radius-card);
background:
radial-gradient(circle at 18% 24%, rgb(var(--nodedc-accent-rgb) / 0.13), transparent 22rem),
color-mix(in srgb, var(--nodedc-canvas-soft) 86%, transparent);
background: var(--nodedc-canvas-soft);
padding: clamp(1rem, 3vw, 2rem);
}
@@ -326,9 +324,16 @@ textarea {
}
.catalog-environment-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0 0.875rem 0.875rem;
}
.catalog-environment-title > div {
display: grid;
gap: 0.3rem;
padding: 0 0.875rem 0.875rem;
}
.catalog-environment-title span {
@@ -337,20 +342,12 @@ textarea {
}
.catalog-environment-draft {
margin: 0;
margin: -0.25rem 0.875rem 0.75rem;
color: var(--nodedc-text-muted);
font-size: 0.68rem;
line-height: 1.4;
}
.catalog-environment-with-action {
display: grid;
width: min(var(--nodedc-inspector-inner-width), 100%);
grid-template-columns: minmax(0, 1fr) var(--nodedc-control-height);
align-items: center;
gap: var(--nodedc-space-2);
}
.catalog-icon-catalog {
display: grid;
gap: 1rem;