154 lines
9.1 KiB
Markdown
154 lines
9.1 KiB
Markdown
# Editor Design System
|
||
|
||
Blacksite's editor uses the Penpot **Inspector Material Slot** as its visual source of truth. This
|
||
guide defines the reusable visual and interaction language; domain behavior remains in its owning
|
||
editor subsystem. See [ADR 0049](../adr/0049-penpot-led-editor-visual-system.md).
|
||
|
||
## Semantic palette
|
||
|
||
The default theme mirrors Blacksite's established palette. These values are published under
|
||
Penpot Assets / Colors and represented by `EditorVisualPalette`; domain widgets consume semantic
|
||
roles rather than literals. Theme selection and persistence are intentionally deferred to Editor
|
||
Settings, but the renderer-facing UI seam already accepts a different palette.
|
||
|
||
| Role | Default |
|
||
|------|-------|
|
||
| Canvas / panel / recessed | `#07080A` / `#0D0F12` / `#090B0D` |
|
||
| Control / elevated | `#15181C` / `#1F2328` |
|
||
| Brand accent / hover | `#E5A433` / `#F9C14F` |
|
||
| Selection and valid drop | `#41C6CF` |
|
||
| Border / strong border | `#262B31` / `#40474F` |
|
||
| Primary / secondary / muted text | `#DCE1E5` / `#9BA3AB` / `#606972` |
|
||
| Healthy / warning / error | `#54BE7F` / `#EBB448` / `#E25E5E` |
|
||
|
||
Source Sans Pro Regular and Bold are bundled in `crates/editor/assets/fonts/source-sans/`. Type
|
||
roles use the Penpot 9–16 px scale; weight is selected through separate egui font families rather
|
||
than simulated styling. Spacing follows 4/8/12 px steps. Common controls are 22–31 px tall with
|
||
4/5/6/7/8 px radii.
|
||
|
||
Use semantic tokens from `ui/design_system`; do not copy literal colors into a domain card. The
|
||
selection color is reserved for focus, valid drag targets, selection, and active values. Health,
|
||
unsaved, processing, failed, read-only, inherited, and built-in states keep the same geometry.
|
||
|
||
## Composition contract
|
||
|
||
Reusable controls render a supplied view model and return actions. They do not query unrelated
|
||
Bevy resources, save files, schedule processing, or directly mutate scene/world state. The owning
|
||
domain resolves actions after rendering. This keeps the visual system reusable in the Inspector,
|
||
Content Browser, Material Library, and future editor panels.
|
||
|
||
The material implementation is split as follows:
|
||
|
||
- `ui/design_system` owns tokens, typography, control chrome, property-grid geometry, and the color
|
||
popup.
|
||
- `ui/materials/panel.rs` owns the material-slot view model and action contract, while
|
||
`ui/materials/pickers.rs` owns the compact current/recent Material menu and the richer Texture
|
||
asset-picker presentation.
|
||
- `ui/materials/inputs.rs` renders schema-declared material controls.
|
||
- Inspector and asset editors provide state and apply returned actions through authored documents.
|
||
|
||
## Reference layout
|
||
|
||
At the current 620 px wide reference, 12 px outer padding produces a 596 px slot and a 569 px parameter
|
||
body. A 32 px **Materials** heading precedes repeated slots. Expanded headers are 82 px with a
|
||
64 px preview; collapsed headers are 52 px with a 32 px preview. Surface is 46 px, Inputs is
|
||
216 px (24 px header plus six 32 px rows), UV is 54 px, and Advanced is 30 px collapsed or 178 px
|
||
expanded. An expanded wide row keeps its label/value/channel geometry fixed while only the texture
|
||
field grows above the reference width.
|
||
|
||
| Column | Width |
|
||
|--------|-------|
|
||
| Parameter label | 82 px |
|
||
| Value | 132 px |
|
||
| Value/channel gap | 6 px |
|
||
| Channel | 48 px |
|
||
| Channel/texture gap | 16.5 px |
|
||
| Texture field | 198.5 px at the reference width |
|
||
| Locate / Clear | 24 px each, separated by 6 px |
|
||
|
||
At the 420 px Inspector floor, the slot is 396 px and parameter sections are 369 px. Surface remains
|
||
46 px and Inputs is 372 px: each of the six rows uses a 58 px two-line layout with parameter,
|
||
value, and channel above a 345 px asset picker. UV reflows from 54 px to 94 px. The layout must not introduce nested or horizontal
|
||
scrolling, clipped buttons, or height-dependent width changes.
|
||
|
||
These reference dimensions are implemented as owning rectangles, not nested `horizontal` flow.
|
||
At 569 px, each input row begins its value at x96, channel at x234, and texture group at x298.5.
|
||
At 369 px, the parameter line is `x12/y5/w345` and the texture line is
|
||
`x12/y31/w345`. Texture names are clipped and ellipsized inside the field, so content cannot move
|
||
the fixed Locate/Clear anchors. A transient width below 372 px retains finite clipped rectangles
|
||
and replaces the two low-frequency texture actions with one overflow control until the Inspector
|
||
minimum is restored.
|
||
|
||
## Material controls
|
||
|
||
Primitive, static-mesh, and skinned-mesh surfaces use the same ordered `MaterialsSection` and
|
||
slot-keyed `MaterialSlotPanel` actions. Static/skinned geometry cards do not embed duplicate
|
||
material presentations. The slot header and
|
||
asset zone include the material sphere, bounded name and project-relative path, shader, health,
|
||
assignment interaction, and responsive actions. The entire zone is the drop target; there is no
|
||
second “Drop material asset” box. Preview/name/path and Browse open the picker, while Shader,
|
||
Locate, Clear, and overflow remain isolated. Direct assignments do not show an “Explicit” badge.
|
||
Inherited identities are limited to Model Source, Model Default, Project Default, and Default Grid.
|
||
|
||
Expanded content shows supported controls only:
|
||
|
||
- Surface: Opaque/Cutout, cutoff when Cutout, Separate/ORM, and Double Sided.
|
||
- Inputs: six Standard Lit rows. Emissive color owns the independently stored intensity companion
|
||
and edits/restores both through the Emissive popup.
|
||
- UV: two equal Offset/Tiling groups with fixed X/Y controls and independent resets; Material
|
||
Instances reset to inheritance.
|
||
- Advanced Inputs: only evaluator-supported schema declarations.
|
||
|
||
Imported source content is read-only with Extract Editable in overflow. DefaultGrid is immutable.
|
||
Broken explicit references keep their failed identity and diagnostics rather than exposing a weaker
|
||
layer. Stable IDs, fingerprints, orphan state, and provenance live in overflow diagnostics, not in
|
||
the parameter grid.
|
||
|
||
## Pickers and direct manipulation
|
||
|
||
The compact Material menu shows the current assignment and editor-local recent choices, then offers
|
||
Browse Asset Library and Clear. The richer Texture picker uses bounded thumbnail rows for current,
|
||
recent, and project textures. A popup dismissal or invalid choice does not assign anything. Material and
|
||
Instance drops use the active theme's selection highlight; Texture drops show an explicit invalid state.
|
||
|
||
Handoff v2.3.1 has one Shader selector in the asset identity block. Surface contains the blend mode
|
||
and Double Sided without a visible `Blend` label; it does not mirror Shader as a second "Shading
|
||
Model" control. UV is followed by a
|
||
collapsed Advanced row. Its culling, render queue, depth-write, and receive-shadows mock controls are
|
||
explicitly painted muted previews until those renderer contracts exist, and therefore never return
|
||
actions. Standard egui disabled opacity is not compounded over these controls.
|
||
|
||
The Inspector actor identity remains fixed. The remaining dock-tab body is one actor-salted
|
||
component scroll region whose clip is intersected with the dock body, so scrolled Transform or
|
||
component cards cannot paint or receive input beneath the actor header. Component-local children
|
||
must preserve that inherited clip rather than replacing it with their own maximum rectangle. The
|
||
vertical scrollbar floats over a stable four-pixel interaction rail, so crossing the height overflow
|
||
threshold cannot change the content width or trigger a responsive-layout flip.
|
||
|
||
Texture fields open their picker when the field itself is clicked. They show a thumbnail or typed
|
||
fallback icon plus Locate and Clear actions. A ready typed thumbnail replaces the generic Texture
|
||
glyph rather than appearing beside a redundant icon; a redundant folder button is not used.
|
||
|
||
The color popup is a fixed 420 x 350 px modal centered over the owning Inspector region, with a
|
||
dimmed Inspector-local scrim. Its exported geometry includes a 38 px header, 250 x 26 mode tabs,
|
||
178 x 202 wheel editor, 198 x 186 values region, byte RGBA fields, degree/percentage HSV fields,
|
||
functional HEX copy, a 156 x 14 checkerboard alpha control, 396 x 28 recents row, and 396 x 26
|
||
footer. Wheel, Sliders, and Presets share that fixed frame. The exported eyedropper lane is a muted,
|
||
non-interactive preview labelled `Planned — screen sampling pending`.
|
||
|
||
Every preview updates the live material document. Apply keeps the dirty in-memory value; Cancel,
|
||
Escape, Close, and outside dismissal restore the exact value captured on open. No picker action
|
||
writes source or queues derived processing. Material scalar inputs use the reusable 130 x 22
|
||
Penpot control: a 70 x 14 track, 10 px thumb, 8 px separation, and 48 x 22 numeric field. Expanding
|
||
Advanced scrolls its complete 178 px disabled-preview body into view rather than revealing a clipped
|
||
partial section.
|
||
|
||
## Review checklist
|
||
|
||
- Verify the 620 px geometry numerically and in a native capture.
|
||
- Verify the narrow reflow, long and Unicode names, invalid drops, read-only/imported/built-in
|
||
states, and popup cancellation.
|
||
- Verify handle identity and dirty-document behavior during live interaction.
|
||
- Run `python scripts/codex/architecture_audit.py check`; new design-system work does not earn an
|
||
exception from the module-size ratchet.
|