Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .design-sync/NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,46 @@ unusual shape.
- **Synth-entry `.d.ts` are weaker** than a real library's. cva components
(Button, Tabs) may need `dtsPropsFor` overrides — check the emitted
`.d.ts` on every re-sync.

---

# PixelForge kit (second design system)

A SECOND, separate design system for the image editor's Photoshop-style web UI,
uploaded to the **"PixelForge — Image Editor UI Kit"** Claude Design project
(projectId in `pixelforge.config.json`). Built from the SAME repo with a parallel
config; the base shadcn kit is unaffected.

- **Config:** `pixelforge.config.json` (globalName `PixelForge`, out dir `pf-bundle`,
entry `pixelforge-entry.ts`, conventions `pixelforge-conventions.md`).
- **Shape:** synth-entry, same as base. `srcDir = src/components/image-editor`.
- **cssEntry:** `cat dist/assets/index-*.css dist/assets/ImageEditor-*.css >
.design-sync/.cache/pixelforge.css` — the editor's `pixelforge.css` (`.pf-*`
chrome) lives in the ImageEditor route chunk, so BOTH app CSS chunks are needed.
Re-sync must rebuild (`pnpm build`) then re-cat before the converter.
- **i18n:** editor components use `react-i18next` `t()`. `pixelforge-entry.ts`
self-initializes the global i18n instance in English (no provider needed) so
components render real copy. To add zh-CN, merge that resource in the entry.
- **Dark:** previews set `html.dark` (Frame `useEffect`) so portaled dialogs are
dark too — the editor is dark-only.
- **dtsPropsFor: intentionally NOT written** for this kit — 30 app-internal
components with editor-shaped props (EditorState/LayerEffect[]/handlers) would
be very costly + error-prone to hand-model. The `.d.ts` ship as loose index
signatures; usage is carried by each `<Name>.prompt.md` (embeds the authored
preview) + the conventions header. Add dtsPropsFor in a later re-sync if needed.
- **Scope:** 28 audited SYNC + 2 smoke = 30 synced. SKIPPED (need live canvas/
pixels/state): Canvas, Workspace, RightSidebar, ChannelsPanel, ReplaceColorDialog,
ColorRangeDialog. **SaveForWebDialog** ships as a FLOOR CARD (its live <canvas>
preview can't paint without the running editor; chrome is real).
- **Shared dirs:** previews live in the shared `.design-sync/previews/` (disjoint
names from the base kit — safe; the other kit's build only warns "stale preview"
and never deletes). Always pass `--components` on pf captures so an unscoped run
can't prune the base kit's grades.
- **Re-sync:** `node .ds-sync/resync.mjs --config .design-sync/pixelforge.config.json
--node-modules ./node_modules --out ./pf-bundle` (first sync omits --remote), with
`DS_CHROMIUM_PATH=/usr/bin/google-chrome`.

## PixelForge re-sync risks
- Source bug noticed during the sync: a `Duplicate key "toolHint"` esbuild warning
(a duplicate object key somewhere in the editor source) — non-fatal, but worth
fixing in the app.
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/ActionsPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/AdjustPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/AdjustmentDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/BrushesPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/CanvasSizeDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/ColorPickerDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/ContextMenu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Widgets
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/CurvesEditor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Widgets
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/DropZone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Widgets
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/FillDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/FilterDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/HistoryPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/ImageSizeDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/LayerCompsPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/LayerStyleDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/LayersPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/MenuBar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Chrome
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/NewDocumentDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/OptionsBar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Chrome
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/PathsPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/PropertiesPanel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Panels
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/RotateArbitraryDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/SaveForWebDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/SelectModifyDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/ShortcutsDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/Slider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Widgets
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/StatusBar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Chrome
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/StrokeDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/ToolsPalette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Chrome
---
3 changes: 3 additions & 0 deletions .design-sync/pf-groups/WarpTextDialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
category: Dialogs
---
88 changes: 88 additions & 0 deletions .design-sync/pixelforge-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# PixelForge — Photoshop-style web UI kit

The toolbox image editor's **pro-tool UI**, as a design system: menu/options/tool
bars, dockable panels (Layers, Properties, History…), and the full set of CC-style
modal dialogs. It is a **separate kit from the base Toolbox primitives** but built
on top of them (it composes Button/Dialog/Label internally) and shares the same
zinc/dark tokens, plus a dedicated **`.pf-*` chrome layer**. Every component is a
real compiled export on `window.PixelForge`.

## Setup (do this first)

- **Dark only.** PixelForge mirrors the editor, which runs in permanent dark mode.
Put `class="dark"` on the design root (or `html`). There is **no light variant**
and **no React provider** to wire.
- **i18n is pre-wired.** Components are localized with react-i18next; the bundle
self-initializes English on load, so `t()` renders real copy with zero setup.
Switch languages with `i18n.changeLanguage('zh-CN')` after adding that resource.
- Modal dialogs portal to `<body>`; the root `.dark` makes them dark automatically.

## The `.pf-*` chrome vocabulary

Tokens/utilities are the **same semantic set as the base Toolbox kit** (`bg-card`,
`text-muted-foreground`, `border-input`, `rounded-*`, the `var(--*)` tokens — and
the same build-snapshot caveat: use `var(--token)` / inline styles for any utility
not already in `styles.css`). On top of that, PixelForge adds these chrome classes
(all defined in `styles.css`'s `@import`ed `_ds_bundle.css`):

- **Shell / layout:** `pf-root`, `pf-shell`, `pf-canvas-area`, `pf-canvas-wrap`, `pf-right`
- **Menu bar:** `pf-menubar`, `pf-menubar-name`, `pf-menu-item`, `pf-menu-dd`, `pf-menu-backdrop`
- **Options bar:** `pf-options`, `pf-opt-group`, `pf-opt-label`, `pf-opt-input`, `pf-opt-select`, `pf-opt-btn`
- **Tools rail:** `pf-tools`, `pf-tool-group`, `pf-tool-btn`, `pf-tool-colors`, `pf-tool-sep`
- **Panels:** `pf-panel-group`, `pf-panel-tabs`, `pf-panel-tab`, `pf-panel-body`, `pf-scroll-y`
- **Layer rows:** `pf-layer-row`, `pf-layer-selected`, color tags `pf-tone-blue|neutral|warm`
- **Status bar:** `pf-statusbar` · **Context menu:** `pf-context-menu`, `pf-context-menu-item`, `pf-context-menu-sep`, `pf-context-menu-header`, `pf-context-menu-shortcut`, `pf-context-menu-danger`
- **Misc:** `pf-kbd`, `pf-active`, `pf-disabled`, `pf-open`, `pf-sep`

## Props are editor-shaped — read the per-component docs

Unlike the base primitives, these are app-internal components: their props are the
editor's own data (an `EditorState`, a `layers` array, a `handlers` object of
callbacks, an `open` flag for dialogs). The emitted `<Name>.d.ts` are therefore
**intentionally loose** (an index signature, not a hand-modelled interface) — so
**read each component's `<Name>.prompt.md`, which embeds a working preview with
realistic props**, rather than trusting the `.d.ts` shape. Dialogs render via an
`open` prop (or an open-key string); panels take plain data objects + noop-able
callbacks.

## A typical editor layout

```
pf-root
├── MenuBar (handlers={…}) — File/Edit/Image/Select/Filter/Layer/View
├── OptionsBar (tool="brush" …) — context-sensitive tool options
└── pf-shell
├── ToolsPalette (tool, setTool, fg/bg) — vertical tool rail
├── pf-canvas-area (your <canvas>) — NOT in this kit (app-owned)
└── pf-right
├── LayersPanel (state, selectedId, …)
├── PropertiesPanel · HistoryPanel · ActionsPanel · PathsPanel · …
StatusBar (dimensions, zoom, tool…) — bottom bar
```

Modal dialogs (New Document, Image/Canvas Size, Fill, Stroke, Adjustment, Filter,
Layer Style, Warp Text, Rotate, Select Modify, Color Picker, Shortcuts) mount over
that shell when opened. `SaveForWebDialog` ships as a floor card — its chrome is
real but its live `<canvas>` preview needs the running editor.

## Idiomatic snippet

```tsx
import { LayersPanel } from 'toolbox' // resolves to window.PixelForge

export default function Panels() {
return (
<div className="dark" style={{ background: 'var(--background)', color: 'var(--foreground)', padding: 16, width: 280 }}>
<LayersPanel
state={{ imageLayer: { kind: 'image', id: 'image', name: 'Background', visible: true, opacity: 100, blend: 'normal' },
layers: [{ kind: 'annotation', id: 'l1', name: 'Headline', visible: true, opacity: 100, blend: 'normal', colorTag: 'blue', shape: { kind: 'text', x: 40, y: 60, text: 'Summer Sale', color: '#fff', fontSize: 48 } }],
transforms: { rotation: 0, flipH: false, flipV: false },
adjust: { brightness: 100, contrast: 100, saturation: 100, grayscale: 0, blur: 0, hue: 0, sepia: 0, invert: 0 } }}
selectedId="l1" onSelect={() => {}} setLayers={() => {}} patchLayer={() => {}}
patchImageLayer={() => {}} deleteLayer={() => {}} onOpenStyle={() => {}}
renamingId={null} onStartRename={() => {}} onCommitRename={() => {}} onSetColorTag={() => {}}
/>
</div>
)
}
```
44 changes: 44 additions & 0 deletions .design-sync/pixelforge-entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// design-sync PixelForge bundle entry (committed, durable). Auto-covers the
// editor's PS-style web UI kit. See pixelforge-conventions.md.
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import en from '@/i18n/en.json'

if (!i18n.isInitialized) {
void i18n.use(initReactI18next).init({
resources: { en: { translation: en } },
lng: 'en', fallbackLng: 'en', interpolation: { escapeValue: false },
})
}

// Keep in sync with componentSrcMap in pixelforge.config.json.
export * from '@/components/image-editor/NewDocumentDialog'
export * from '@/components/image-editor/ImageSizeDialog'
export * from '@/components/image-editor/CanvasSizeDialog'
export * from '@/components/image-editor/SaveForWebDialog'
export * from '@/components/image-editor/FillDialog'
export * from '@/components/image-editor/StrokeDialog'
export * from '@/components/image-editor/AdjustmentDialog'
export * from '@/components/image-editor/FilterDialog'
export * from '@/components/image-editor/LayerStyleDialog'
export * from '@/components/image-editor/WarpTextDialog'
export * from '@/components/image-editor/RotateArbitraryDialog'
export * from '@/components/image-editor/SelectModifyDialog'
export * from '@/components/image-editor/ColorPickerDialog'
export * from '@/components/image-editor/ShortcutsDialog'
export * from '@/components/image-editor/MenuBar'
export * from '@/components/image-editor/OptionsBar'
export * from '@/components/image-editor/ToolsPalette'
export * from '@/components/image-editor/StatusBar'
export * from '@/components/image-editor/LayersPanel'
export * from '@/components/image-editor/PropertiesPanel'
export * from '@/components/image-editor/HistoryPanel'
export * from '@/components/image-editor/ActionsPanel'
export * from '@/components/image-editor/PathsPanel'
export * from '@/components/image-editor/LayerCompsPanel'
export * from '@/components/image-editor/AdjustPanel'
export * from '@/components/image-editor/BrushesPanel'
export * from '@/components/image-editor/Slider'
export * from '@/components/image-editor/CurvesEditor'
export * from '@/components/image-editor/ContextMenu'
export * from '@/components/image-editor/DropZone'
Loading
Loading