mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
feat: add new design primitives
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# AGENTS.md
|
||||
|
||||
Инструкции для агентов (и IDE-ассистентов) по работе с этим репозиторием.
|
||||
|
||||
## Стек
|
||||
|
||||
- **SvelteKit** (`web/`) — основное приложение, Svelte 5 (runes).
|
||||
- Менеджер пакетов: **pnpm**. Запускать команды из `web/`, либо через
|
||||
обёртки в корне: `pnpm build`, `pnpm dev` (это `pnpm --dir web ...`).
|
||||
Не использовать npm.
|
||||
- Сборка: `pnpm --dir web build`, проверка типов: `pnpm --dir web exec svelte-check --tsconfig ./tsconfig.json`,
|
||||
тесты: `pnpm --dir web test`.
|
||||
|
||||
## Правила кода
|
||||
|
||||
### Svelte 5: типизация props через `interface Props`
|
||||
|
||||
Все типизированные пропсы компонентов описываются через локальный
|
||||
`interface Props`, а деструктуризация идёт через аннотацию типа при `$props()`:
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
label: string;
|
||||
accent?: boolean;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
let { label, accent = false, children }: Props = $props();
|
||||
</script>
|
||||
```
|
||||
|
||||
Не использовать инлайн-дженерик `$props<{ ... }>()` — он тяжело читается и
|
||||
разносит тип и деструктуризацию по разным местам.
|
||||
|
||||
> Позже планируется линтер/правило (eslint-plugin-svelte / custom rule),
|
||||
> запрещающее `$props<...>()` и требующее `interface Props`.
|
||||
|
||||
### Дизайн: новый визуальный язык
|
||||
|
||||
Переезд на новый дизайн описан в `docs/plan-redesign.md`. Ключевые
|
||||
ограничения при работе с веткой `feat/redesign`:
|
||||
|
||||
- Глобальные стили старого и нового дизайна разнесены по layout без
|
||||
наследования: старое в `web/src/app.css` (группа `(old)/`), новое в
|
||||
`web/src/lib/styles/design2.css` (ветка `preview/*`). Префикс `.ds2` не
|
||||
используется.
|
||||
- Все повторяющиеся визуальные элементы — отдельные компоненты в
|
||||
`web/src/lib/components/kit/`, даже «просто div с двумя стилями».
|
||||
- Токены нового дизайна живут в `design2.css`; `refs/` и `refs-html/` —
|
||||
одноразовый источник, не поддерживаемый (правки дизайна — сразу в код).
|
||||
- Коммиты мелкие (< ~500 строк), атомарные
|
||||
@@ -304,7 +304,7 @@ src/routes/preview/tools/[id]/+page.svelte
|
||||
подключить шрифты в `design2.css` (self-host, offline-first).
|
||||
- [x] **[C4]** _(слит в C3: шрифты нельзя подключить до создания `design2.css`)_
|
||||
`[data-theme='dark']` (таблица §2). Без утилитарных классов.
|
||||
- [ ] **[C5]** Примитивы batch 1: `AppShell`, `BlueprintGrid`, `CheckerCanvas`,
|
||||
- [x] **[C5]** Примитивы batch 1: `AppShell`, `BlueprintGrid`, `CheckerCanvas`,
|
||||
`MonoLabel`, `StatusDot`/`StatusLine`.
|
||||
- [ ] **[C6]** Примитивы batch 2: `Panel`, `PanelHeading`, `SettingGroup`,
|
||||
`SettingsFooter`, `MetaRow`/`MetaList`, `Segmented`, `Badge`, `StepCard`,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import BlueprintGrid from './BlueprintGrid.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="app-shell">
|
||||
<BlueprintGrid />
|
||||
<div class="app-content">
|
||||
{@render children()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.app-shell {
|
||||
position: relative;
|
||||
min-height: 100dvh;
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
.app-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="blueprint-grid" aria-hidden="true"></div>
|
||||
|
||||
<style>
|
||||
.blueprint-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
linear-gradient(to right, color-mix(in srgb, var(--line) 45%, transparent) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, color-mix(in srgb, var(--line) 45%, transparent) 1px, transparent 1px);
|
||||
background-size: 32px 32px;
|
||||
opacity: 0.55;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet;
|
||||
size?: 'sm' | 'large';
|
||||
}
|
||||
let { children, size = 'large' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="checker-canvas checker-canvas--{size}">
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.checker-canvas {
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
background-color: #0b0f14;
|
||||
background-image: linear-gradient(45deg, #1a2129 25%, transparent 25%),
|
||||
linear-gradient(-45deg, #1a2129 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #1a2129 75%),
|
||||
linear-gradient(-45deg, transparent 75%, #1a2129 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position:
|
||||
0 0,
|
||||
0 10px,
|
||||
10px -10px,
|
||||
-10px 0;
|
||||
}
|
||||
|
||||
.checker-canvas--sm {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.checker-canvas--large {
|
||||
min-height: 360px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
accent?: boolean;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { accent = false, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<span class="mono-label" class:accent={accent}>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<style>
|
||||
.mono-label {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.accent {
|
||||
color: var(--blue);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
<span class="status-dot" aria-hidden="true"></span>
|
||||
|
||||
<style>
|
||||
.status-dot {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--success);
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--success) 25%, transparent);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import StatusDot from './StatusDot.svelte';
|
||||
import MonoLabel from './MonoLabel.svelte';
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
}
|
||||
|
||||
let { label }: Props = $props();
|
||||
</script>
|
||||
|
||||
<span class="status-line">
|
||||
<StatusDot />
|
||||
<MonoLabel>{label}</MonoLabel>
|
||||
</span>
|
||||
|
||||
<style>
|
||||
.status-line {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user