@anvilkit/puck-studio 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,267 +1,286 @@
1
1
  # @anvilkit/puck-studio
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@anvilkit/puck-studio)](https://www.npmjs.com/package/@anvilkit/puck-studio)
4
- [![license](https://img.shields.io/npm/l/@anvilkit/puck-studio)](./LICENSE)
4
+ [![license](https://img.shields.io/npm/l/@anvilkit/puck-studio)](https://www.npmjs.com/package/@anvilkit/puck-studio)
5
5
  [![react](https://img.shields.io/badge/react-19-blue)](https://react.dev)
6
6
  [![puck](https://img.shields.io/badge/puck-%5E0.21.1-purple)](https://puckeditor.com)
7
7
 
8
- Drop-in Shadcn UI overrides for all 15 Puck Editor surfaces enterprise-ready, TypeScript-first.
8
+ Opinionated Puck editor chrome and override pack built with React 19, `@base-ui/react`, and Tailwind v4.
9
9
 
10
- > 中文文档请见 [docs/README.zh.md](./docs/README.zh.md)
10
+ > Chinese reference: [docs/README.zh.md](./docs/README.zh.md). This README is the canonical, up-to-date guide for the current implementation.
11
11
 
12
- ---
12
+ ## What This Package Gives You
13
13
 
14
- ## Introduction
14
+ - `Studio`: a desktop editor shell around `<Puck />` with a custom header, left sidebar, preview area, and fields panel
15
+ - `puckOverrides`: a packaged set of Puck overrides for drawer, outline, preview, canvas, and fields surfaces
16
+ - Image and copy libraries with ghost-drag into the canvas
17
+ - Per-instance persisted UI state via `storeId`
18
+ - Persisted locale state plus message overrides
19
+ - Light/dark theme sync between the host document and the Puck iframe
20
+ - Optional AI copilot panel when `aiHost` is provided
21
+ - Exported CSS tokens in `@anvilkit/puck-studio/styles.css`
15
22
 
16
- Puck Editor ships with a minimal default UI that works for prototypes but falls short in enterprise products — no design system consistency, no accessible components, no Tailwind integration.
17
-
18
- `@anvilkit/puck-studio` solves this with a single drop-in `overrides` object built on Shadcn UI, @base-ui/react primitives, and Tailwind v4. All 15 override surfaces are covered. Components are **bundled** into the package — you do not run a Shadcn CLI to copy them.
19
-
20
- **Key value props:**
21
-
22
- - All 15 Puck override surfaces implemented
23
- - TypeScript-first with full type inference
24
- - Ships ESM + CJS + `.d.ts`
25
- - No Next.js at runtime — `next` is demo-only
26
- - @base-ui/react and lucide-react are bundled, not peer deps
27
-
28
- ---
29
-
30
- ## Architecture
31
-
32
- ```
33
- ┌─────────────────────────────────────────────────────────────────────┐
34
- │ <Studio /> │
35
- │ props: config, data, onPublish, images?, copywritings?, aiHost? │
36
- │ │
37
- │ ┌──────────────────────────────────────────────────────────────┐ │
38
- │ │ <Puck overrides={mergedOverrides} plugins={[aiPlugin]}> │ │
39
- │ │ │ │
40
- │ │ ┌────────────────────────────────────────────────────────┐ │ │
41
- │ │ │ <EditorLayout /> │ │ │
42
- │ │ │ │ │ │
43
- │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │
44
- │ │ │ │ <Header /> │ │ │ │
45
- │ │ │ │ back · title · undo/redo · collab · publish │ │ │ │
46
- │ │ │ └──────────────────────────────────────────────────┘ │ │ │
47
- │ │ │ │ │ │
48
- │ │ │ ┌──────┐ ┌────────────────┐ ┌────────┐ ┌───────┐ │ │ │
49
- │ │ │ │Aside │ │ Dynamic Panel │ │ Canvas │ │Fields │ │ │ │
50
- │ │ │ │ │ │ │ │ │ │ │ │ │ │
51
- │ │ │ │insert│ │ insert → │ │ Puck. │ │ Puck. │ │ │ │
52
- │ │ │ │layer │ │ Puck.Comps │ │Preview │ │Fields │ │ │ │
53
- │ │ │ │image │ │ layer → │ │ │ │ │ │ │ │
54
- │ │ │ │text │ │ Puck.Outline │ │ │ │ │ │ │ │
55
- │ │ │ │copil.│ │ image → │ │ │ │ │ │ │ │
56
- │ │ │ │ │ │ ImageLibrary │ │ │ │ │ │ │ │
57
- │ │ │ │ │ │ text → │ │ │ │ │ │ │ │
58
- │ │ │ │ │ │ CopyLibrary │ │ │ │ │ │ │ │
59
- │ │ │ │ │ │ copilot→ │ │ │ │ │ │ │ │
60
- │ │ │ │ │ │ aiPanel │ │ │ │ │ │ │ │
61
- │ │ │ └──────┘ └────────────────┘ └────────┘ └───────┘ │ │ │
62
- │ │ └────────────────────────────────────────────────────────┘ │ │
63
- │ └──────────────────────────────────────────────────────────────┘ │
64
- └─────────────────────────────────────────────────────────────────────┘
65
-
66
- Puck Override Surfaces (15 total)
67
- ──────────────────────────────────
68
- Layout: header · headerActions · drawer · components · outline
69
- Canvas: iframe · preview · componentOverlay · actionBar · drawerItem · componentItem
70
- Fields: fields · fieldLabel · fieldTypes (11 renderers) · puck
71
-
72
- Custom drag-drop events (window):
73
- anvilkit:librarydragstart → fired on pointer-down in either library
74
- anvilkit:imagedrop → fired on pointer-up with { src, clientX, clientY }
75
- anvilkit:textdrop → fired on pointer-up with { text, clientX, clientY }
76
- ```
77
-
78
- ---
79
-
80
- ## Installation
81
-
82
- ```bash
83
- pnpm add @anvilkit/puck-studio
84
- ```
85
-
86
- > `next` is a devDependency used only for the local demo app. It is not included in the published bundle.
87
-
88
- The package ships both ESM and CJS builds. No additional bundler configuration is required.
89
-
90
- ---
91
-
92
- ## Peer Dependencies
23
+ ## Requirements
93
24
 
94
25
  | Package | Version |
95
26
  |---|---|
96
- | `react` | `>=19` |
97
- | `react-dom` | `>=19` |
27
+ | `react` | `^19.2.4` |
28
+ | `react-dom` | `^19.2.4` |
98
29
  | `@puckeditor/core` | `^0.21.1` |
99
30
  | `@base-ui/react` | `^1.2.0` |
100
- | `tailwindcss` | `^4` |
101
-
102
- > `lucide-react`, `@dnd-kit/*`, `motion`, and `zustand` are **bundled** — do not install them separately.
31
+ | `tailwindcss` | `^4.2.1` |
103
32
 
104
- ---
33
+ `next` is used only by the local demo app in [`app/`](./app) and is not imported from the publishable library code in [`src/`](./src).
105
34
 
106
- ## Quick Start
35
+ Tailwind note:
36
+ This package exports design tokens and class names, not a standalone compiled utility bundle. Your app still needs Tailwind v4 configured. If your Tailwind setup does not scan dependency code automatically, include this package in the sources Tailwind reads.
107
37
 
108
- ### Import the CSS
38
+ ## Installation
109
39
 
110
- ```ts
111
- import "@anvilkit/puck-studio/styles.css";
40
+ ```bash
41
+ pnpm add @anvilkit/puck-studio
112
42
  ```
113
43
 
114
- ### Pattern 1 — Raw overrides object
44
+ The package publishes:
115
45
 
116
- ```tsx
117
- import { puckOverrides } from "@anvilkit/puck-studio";
118
- import { Puck } from "@puckeditor/core";
46
+ - root exports from `@anvilkit/puck-studio`
47
+ - overrides-only exports from `@anvilkit/puck-studio/overrides`
48
+ - deprecated compatibility exports from `@anvilkit/puck-studio/legacy`
49
+ - design tokens from `@anvilkit/puck-studio/styles.css`
119
50
 
120
- export default function Editor({ config, data, onPublish }) {
121
- return (
122
- <Puck
123
- config={config}
124
- data={data}
125
- onPublish={onPublish}
126
- overrides={puckOverrides}
127
- />
128
- );
129
- }
130
- ```
51
+ ## Recommended Usage: `Studio`
131
52
 
132
- ### Pattern 2 Studio convenience wrapper
53
+ `Studio` is the higher-level entry point. It mounts `Puck`, injects Puck base CSS, creates the UI and i18n stores, merges the default overrides, and renders the custom shell.
133
54
 
134
55
  ```tsx
56
+ import "@anvilkit/puck-studio/styles.css";
135
57
  import { Studio } from "@anvilkit/puck-studio";
136
58
 
137
- export default function Editor({ config, data, onPublish }) {
59
+ export function Editor({ config, data, setData, save }) {
138
60
  return (
139
61
  <Studio
140
62
  config={config}
141
63
  data={data}
142
- onPublish={onPublish}
64
+ onChange={setData}
65
+ onPublish={save}
66
+ className="h-screen"
67
+ storeId="marketing-home"
143
68
  />
144
69
  );
145
70
  }
146
71
  ```
147
72
 
148
- `Studio` renders `<Puck overrides={puckOverrides} />` with the correct CSS applied and the full editor shell (header, sidebar, canvas, fields panel).
73
+ ### `Studio` Props That Matter Most
149
74
 
150
- ---
75
+ | Prop | What it does |
76
+ |---|---|
77
+ | `config`, `data`, `onPublish` | Standard Puck inputs |
78
+ | `onChange` | Optional Puck `onChange` passthrough |
79
+ | `overrideExtensions` | Merged last, so your overrides win over the packaged defaults |
80
+ | `images` | Seeds or fixed items for the image library |
81
+ | `copywritings` | Fixed snippets for the copy library |
82
+ | `aiHost` | Lazily loads `@puckeditor/plugin-ai` and renders its panel in the copilot tab |
83
+ | `storeId` | Namespaces persisted UI state under `anvilkit-ui-${storeId}` |
84
+ | `locale` | Current locale string; defaults to `"zh"` |
85
+ | `messages` | Plain key/value message map used by the shell |
86
+ | `className` | Applied to the outer wrapper around `Puck` |
87
+
88
+ Merge order for overrides is:
151
89
 
152
- ## Library Customization
90
+ ```ts
91
+ { ...(aiPlugin?.overrides ?? {}), ...puckOverrides, ...overrideExtensions }
92
+ ```
153
93
 
154
- `Studio` accepts optional props to populate the Image Library and Copy Library panels with your own content.
94
+ That means consumer overrides take precedence.
95
+
96
+ ## Library Customization
155
97
 
156
98
  ### Image Library
157
99
 
158
100
  ```tsx
159
- import type { ImagesProps, ImageItem } from "@anvilkit/puck-studio";
101
+ import type { ImageItem } from "@anvilkit/puck-studio";
102
+
103
+ const brandImages: ImageItem[] = [
104
+ { id: "hero-1", src: "https://cdn.example.com/hero.jpg", alt: "Hero" },
105
+ { id: "logo-1", src: "https://cdn.example.com/logo.png", alt: "Logo" },
106
+ ];
160
107
 
161
- // Option A — replace the default picsum seed list
162
108
  <Studio
163
109
  config={config}
164
110
  data={data}
165
111
  onPublish={save}
166
- images={{ seeds: ["brand", "product", "team", "office"] }}
112
+ images={{ items: brandImages }}
167
113
  />
114
+ ```
168
115
 
169
- // Option B provide a fully custom image list
170
- const myImages: ImageItem[] = [
171
- { id: "hero-1", src: "https://cdn.example.com/hero.jpg", alt: "Hero" },
172
- { id: "logo-1", src: "https://cdn.example.com/logo.png", alt: "Logo" },
173
- ];
116
+ If you only want placeholder images, provide `seeds` instead of `items`:
174
117
 
118
+ ```tsx
175
119
  <Studio
176
120
  config={config}
177
121
  data={data}
178
122
  onPublish={save}
179
- images={{ items: myImages }}
123
+ images={{ seeds: ["brand", "product", "team", "office"] }}
180
124
  />
181
125
  ```
182
126
 
183
- When `items` is provided the search input is hidden (external data is static). When only `seeds` is provided, search still generates picsum results from the query string.
184
-
185
127
  ### Copy Library
186
128
 
187
129
  ```tsx
188
- import type { CopywritingProps, CopywritingItem } from "@anvilkit/puck-studio";
130
+ import type { CopywritingItem } from "@anvilkit/puck-studio";
189
131
 
190
- const brandCopy: CopywritingItem[] = [
191
- { category: "Headlines", label: "Brand promise", text: "Built for builders." },
192
- { category: "CTAs", label: "Primary", text: "Start for free" },
132
+ const snippets: CopywritingItem[] = [
133
+ { category: "Headlines", label: "Promise", text: "Built for builders." },
134
+ { category: "CTAs", label: "Primary", text: "Start for free" },
193
135
  ];
194
136
 
195
137
  <Studio
196
138
  config={config}
197
139
  data={data}
198
140
  onPublish={save}
199
- copywritings={{ items: brandCopy }}
141
+ copywritings={{ items: snippets }}
142
+ />
143
+ ```
144
+
145
+ If you omit `copywritings`, the built-in snippet library is used.
146
+
147
+ ## Localization
148
+
149
+ `Studio` defaults to Chinese UI messages because [`defaultMessages`](./src/store/i18n-defaults.ts) re-exports the Chinese catalog.
150
+
151
+ You can switch locale and provide your own message map:
152
+
153
+ ```tsx
154
+ <Studio
155
+ config={config}
156
+ data={data}
157
+ onPublish={save}
158
+ locale="en"
159
+ messages={{
160
+ "header.publish": "Publish",
161
+ "header.undo": "Undo",
162
+ "header.redo": "Redo",
163
+ }}
200
164
  />
201
165
  ```
202
166
 
203
- Omit the prop entirely to use the built-in 14-snippet default library.
167
+ Any missing message falls back to its key string at runtime.
204
168
 
205
- ---
169
+ ## Using the Raw Overrides
206
170
 
207
- ## Override Surfaces
171
+ Use `puckOverrides` when you want the styled Puck surfaces without the full `Studio` shell.
208
172
 
209
- ### Layout
173
+ ```tsx
174
+ import "@puckeditor/core/puck.css";
175
+ import "@anvilkit/puck-studio/styles.css";
210
176
 
211
- | Key | Component | Description |
212
- |---|---|---|
213
- | `header` | `EditorHeader` | Top toolbar shell |
214
- | `headerActions` | `EditorHeader` | Action buttons injected into header; **must render `children`** |
215
- | `drawer` | `EditorDrawer` | Left panel component list container |
216
- | `components` | `EditorDrawer` | Component group list inside drawer |
217
- | `outline` | `EditorOutline` | Layer tree / page outline panel |
177
+ import { Puck } from "@puckeditor/core";
178
+ import { puckOverrides } from "@anvilkit/puck-studio";
218
179
 
219
- ### Canvas
180
+ export function Editor({ config, data, onPublish }) {
181
+ return (
182
+ <Puck
183
+ config={config}
184
+ data={data}
185
+ onPublish={onPublish}
186
+ overrides={puckOverrides}
187
+ />
188
+ );
189
+ }
190
+ ```
220
191
 
221
- | Key | Component | Description |
222
- |---|---|---|
223
- | `iframe` | `CanvasIframe` | Canvas iframe wrapper; injects Tailwind + Shadcn CSS vars |
224
- | `preview` | `CanvasPreview` | Drag preview ghost |
225
- | `componentOverlay` | `ComponentOverlay` | Selection/hover highlight overlay; must be `pointer-events-none` |
226
- | `actionBar` | `ActionBar` | Per-component floating action bar (remove, duplicate, move) |
227
- | `drawerItem` | `DrawerItem` | Draggable component chip in drawer; **must forward all drag refs and event props to outermost DOM element** |
228
- | `componentItem` | `DrawerItem` | Alias surface for component items |
192
+ The packaged override object currently wires these Puck keys:
193
+
194
+ - `drawer`
195
+ - `components`
196
+ - `drawerItem`
197
+ - `componentItem`
198
+ - `outline`
199
+ - `iframe`
200
+ - `preview`
201
+ - `componentOverlay`
202
+ - `actionBar`
203
+ - `fields`
204
+ - `fieldTypes`
205
+ - `puck`
206
+
207
+ `Studio` adds the surrounding header, sidebar, image library, copy library, and fields layout on top of that.
208
+
209
+ ## Architecture At A Glance
210
+
211
+ ```text
212
+ Studio
213
+ -> EditorUiStoreProvider
214
+ -> EditorI18nStoreProvider
215
+ -> Puck
216
+ -> merged overrides
217
+ -> optional AI plugin
218
+ -> EditorLayout
219
+ -> Header
220
+ -> Aside
221
+ -> Puck.Components / Puck.Outline / ImageLibrary / CopyLibrary / aiPanel
222
+ -> Puck.Preview
223
+ -> Puck.Fields
224
+ ```
229
225
 
230
- ### Inspector
226
+ The drag-drop libraries communicate with the canvas through typed `window` events defined in [`src/features/library-dnd/drop-contract.ts`](./src/features/library-dnd/drop-contract.ts). The iframe bridge listens for those events, hit-tests the hovered component, and dispatches Puck `replace` actions with updated props.
231
227
 
232
- | Key | Component | Description |
233
- |---|---|---|
234
- | `fields` | `FieldWrapper` | Props panel field list wrapper |
235
- | `fieldLabel` | `FieldWrapper` | Individual field label + tooltip |
236
- | `fieldTypes` | `FieldTypesRegistry` | Map of all 11 field type renderers |
237
- | `puck` | `EditorHeader` | Puck logo / menu slot in header |
228
+ ## Public API Summary
238
229
 
239
- > **`headerActions` callout:** This surface receives Puck's built-in publish button as `children`. You must render `{children}` inside your implementation or the publish button disappears (regression introduced in Puck 0.15+).
230
+ ### Runtime exports
240
231
 
241
- > **`drawerItem` callout:** Puck passes drag refs and pointer event handlers as props. Wrapping the root element in an extra `<div>` silently breaks drag-and-drop. Spread all drag props directly onto the outermost DOM element.
232
+ - `Studio`
233
+ - `puckOverrides`
234
+ - `createEditorUiStore`
235
+ - `createEditorI18nStore`
236
+ - `EditorUiStoreProvider`
237
+ - `EditorI18nStoreProvider`
238
+ - `useEditorUiStoreApi`
239
+ - `useEditorI18nStoreApi`
240
+ - `defaultMessages`
242
241
 
243
- ---
242
+ ### Public types
244
243
 
245
- ## Contributing & Build
244
+ - `StudioProps`
245
+ - `ImagesProps`
246
+ - `ImageItem`
247
+ - `CopywritingProps`
248
+ - `CopywritingItem`
249
+ - `EditorUiStore`
250
+ - `EditorUiStoreApi`
251
+ - `ActiveTab`
252
+ - `EditorI18nStoreApi`
253
+ - `Locale`
254
+ - `Messages`
246
255
 
247
- ```bash
248
- pnpm install # install all dependencies
249
- pnpm dev # start Next.js demo app
250
- pnpm build # run tsup → dist/
251
- ```
256
+ ### Deprecated compatibility exports
252
257
 
253
- Build output in `dist/`:
258
+ `@anvilkit/puck-studio/legacy` exists only for the deprecated singleton `uiStore` and `UIStore` type alias.
254
259
 
255
- | File | Format |
256
- |---|---|
257
- | `index.js` | CJS |
258
- | `index.mjs` | ESM |
259
- | `index.d.ts` | TypeScript declarations |
260
+ ## Current Scope
261
+
262
+ - `Studio` is desktop-first today; [`EditorLayout`](./src/core/studio/layout/Layout.tsx) is hidden on very small screens
263
+ - The image and copy libraries are local editor tooling, not asset management backends
264
+ - The share and collaborators popovers in the header are presentational shell UI, not real-time collaboration features
265
+ - If you need fully custom publish, share, or shell-level workflows, build on `puckOverrides` directly or fork `Studio`
266
+
267
+ ## Development
260
268
 
261
- `next` is a devDependency for the demo app only. It does not appear in `src/` and is not included in the bundle.
269
+ ```bash
270
+ pnpm install
271
+ pnpm dev
272
+ pnpm lint
273
+ pnpm test
274
+ pnpm test:types
275
+ pnpm build
276
+ ```
262
277
 
263
- ---
278
+ Useful directories:
264
279
 
265
- ## License
280
+ - [`src/core/studio`](./src/core/studio): `Studio` and the shell layout
281
+ - [`src/core/overrides`](./src/core/overrides): packaged Puck overrides
282
+ - [`src/features/library-dnd`](./src/features/library-dnd): drag/drop protocol and prop replacement helpers
283
+ - [`src/store`](./src/store): UI and i18n stores, providers, hooks
284
+ - [`app`](./app): local Next.js demo only
266
285
 
267
- MIT
286
+ Build output is generated in [`dist/`](./dist) as ESM `.js`, CJS `.cjs`, CSS, and type declaration files.