@improba/page-builder 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Improba
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # @improba/page-builder
2
+
3
+ Bibliothèque Vue 3 pour construire et afficher des pages à partir d’un arbre JSON. Elle fournit un **mode lecture** (rendu statique, compatible SSR) et un **mode édition** (éditeur WYSIWYG avec palette de composants, panneau de propriétés, glisser-déposer, undo/redo). Le backend envoie un seul contrat JSON (`IPageData`) ; le frontend le rend et, en mode édition, permet de le modifier visuellement.
4
+
5
+ **En bref :** installez le plugin Vue, fournissez des données `IPageData`, et utilisez `<PageBuilder>` en `mode="read"` pour l’affichage ou `mode="edit"` pour l’édition. Vous pouvez enregistrer vos propres composants (hero, cartes, etc.) et les utiliser comme blocs dans l’arbre.
6
+
7
+ ## Aperçu
8
+
9
+ **Mode édition** — Éditeur WYSIWYG avec palette de composants, panneau de propriétés et prévisualisation responsive.
10
+
11
+ ![Mode édition — toolbar, palette, canvas, propriétés](./docs/images/edit-mode.png)
12
+
13
+ **Mode lecture** — Rendu de la page sans interface d’édition (compatible SSR).
14
+
15
+ ![Mode lecture — rendu de la page](./docs/images/read-mode.png)
16
+
17
+ *Pour régénérer les captures : `docker compose -f docker/docker-compose.yml run --rm e2e sh -lc "npm install && npm run docs:screenshots"`.*
18
+
19
+ ## Fonctionnalités
20
+
21
+ - **Mode lecture** — Rendu du contenu à partir d’un arbre JSON, compatible SSR. Intégrable dans Nuxt ou toute app Vue 3.
22
+ - **Mode édition** — Éditeur WYSIWYG avec palette de composants, panneau de propriétés, glisser-déposer, undo/redo et prévisualisation responsive (desktop / tablette / mobile).
23
+ - **Registre de composants** — Enregistrement de composants Vue personnalisés (props typées, slots, métadonnées d’édition). Livré avec des composants de mise en page et de contenu (PbColumn, PbRow, PbText, PbImage, etc.).
24
+ - **Contrat JSON unique** — Le backend envoie un seul payload `IPageData` ; le frontend le rend et l’édite. Séparation claire des responsabilités.
25
+
26
+ ## Démarrage rapide
27
+
28
+ Pour un guide pas à pas (installation, premier rendu, mode édition, composants personnalisés), voir **[Quick Start](./docs/quickstart.md)**.
29
+
30
+ Résumé minimal :
31
+
32
+ ### Installation
33
+
34
+ ```bash
35
+ npm install @improba/page-builder
36
+ ```
37
+
38
+ ### Setup
39
+
40
+ ```ts
41
+ import { createApp } from 'vue';
42
+ import { PageBuilderPlugin } from '@improba/page-builder';
43
+ import '@improba/page-builder/style.css';
44
+ import App from './App.vue';
45
+
46
+ const app = createApp(App);
47
+ app.use(PageBuilderPlugin);
48
+ app.mount('#app');
49
+ ```
50
+
51
+ ### Usage
52
+
53
+ ```vue
54
+ <script setup lang="ts">
55
+ import { PageBuilder } from '@improba/page-builder';
56
+ import type { IPageData } from '@improba/page-builder';
57
+
58
+ const pageData: IPageData = {
59
+ meta: { id: '1', name: 'Home', url: '/', status: 'published' },
60
+ content: {
61
+ id: 0,
62
+ name: 'PbColumn',
63
+ slot: null,
64
+ props: { gap: '16px' },
65
+ children: [
66
+ {
67
+ id: 1,
68
+ name: 'PbText',
69
+ slot: 'default',
70
+ props: { content: '<h1>Hello World</h1>' },
71
+ children: [],
72
+ },
73
+ ],
74
+ },
75
+ layout: { id: 100, name: 'PbContainer', slot: null, props: {}, children: [] },
76
+ maxId: 100,
77
+ variables: {},
78
+ };
79
+ </script>
80
+
81
+ <template>
82
+ <PageBuilder :page-data="pageData" mode="read" />
83
+ </template>
84
+ ```
85
+
86
+ ### Edit Mode
87
+
88
+ ```vue
89
+ <template>
90
+ <PageBuilder
91
+ :page-data="pageData"
92
+ mode="edit"
93
+ @save="handleSave"
94
+ @change="handleChange"
95
+ />
96
+ </template>
97
+ ```
98
+
99
+ ## Custom Components
100
+
101
+ Register your own components for the page builder:
102
+
103
+ ```ts
104
+ import { registerComponent } from '@improba/page-builder';
105
+ import type { IComponentDefinition } from '@improba/page-builder';
106
+ import MyHero from './MyHero.vue';
107
+
108
+ const myHero: IComponentDefinition = {
109
+ name: 'MyHero',
110
+ label: 'Hero Banner',
111
+ description: 'Full-width hero section with title and CTA.',
112
+ category: 'content',
113
+ component: MyHero,
114
+ slots: [{ name: 'default', label: 'Content' }],
115
+ editableProps: [
116
+ { key: 'title', label: 'Title', type: 'text', required: true },
117
+ { key: 'backgroundImage', label: 'Background', type: 'image' },
118
+ ],
119
+ defaultProps: { title: 'Hero Title' },
120
+ };
121
+
122
+ registerComponent(myHero);
123
+ ```
124
+
125
+ ## Built-in Components
126
+
127
+ | Component | Category | Description |
128
+ |-----------|----------|-------------|
129
+ | `PbColumn` | layout | Vertical flex container |
130
+ | `PbRow` | layout | Horizontal flex container |
131
+ | `PbSection` | layout | Full-width section with background |
132
+ | `PbContainer` | layout | Centered max-width container |
133
+ | `PbText` | content | Text/HTML block |
134
+ | `PbImage` | media | Image with sizing options |
135
+
136
+ ## JSON Format
137
+
138
+ The page builder consumes a single `IPageData` JSON:
139
+
140
+ ```ts
141
+ interface IPageData {
142
+ meta: { id: string; name: string; url: string; status: string };
143
+ content: INode; // The page content tree
144
+ layout: INode; // The page layout wrapper
145
+ maxId: number; // For generating unique IDs
146
+ variables: Record<string, string>; // Template variables
147
+ }
148
+
149
+ interface INode {
150
+ id: number;
151
+ name: string; // Must match a registered component
152
+ slot: string | null; // Target slot in parent
153
+ props: Record<string, unknown>;
154
+ children: INode[];
155
+ readonly?: boolean;
156
+ }
157
+ ```
158
+
159
+ Props support template variables: `{{ PAGE_NAME }}` is replaced at render time.
160
+
161
+ ## Development
162
+
163
+ **All commands run through Docker** (see [AGENTS.md](./AGENTS.md) for details):
164
+
165
+ ```bash
166
+ # Start dev server with hot reload
167
+ docker compose -f docker/docker-compose.yml up dev
168
+
169
+ # Run tests
170
+ docker compose -f docker/docker-compose.yml run --rm test
171
+
172
+ # Run Playwright end-to-end tests
173
+ docker compose -f docker/docker-compose.yml run --rm e2e sh -lc "npm install && npm run test:e2e"
174
+
175
+ # Build the library
176
+ docker compose -f docker/docker-compose.yml run --rm build
177
+
178
+ # Generate API reference docs (TypeDoc)
179
+ docker compose -f docker/docker-compose.yml run --rm dev npm run docs:api
180
+
181
+ # Install a new dependency
182
+ docker compose -f docker/docker-compose.yml run --rm dev npm install <package>
183
+ ```
184
+
185
+ The dev server starts a Vite playground at `http://localhost:5173` with a demo page for testing components.
186
+
187
+ ### End-to-End Tests (Playwright)
188
+
189
+ E2E tests live in `tests/e2e/` and run against the playground via Playwright's `webServer` integration.
190
+
191
+ ```bash
192
+ # Full E2E suite in Docker/CI
193
+ docker compose -f docker/docker-compose.yml run --rm e2e sh -lc "npm install && npm run test:e2e"
194
+
195
+ # Smoke workflow only (mode switch -> node selection -> prop edit -> save)
196
+ docker compose -f docker/docker-compose.yml run --rm e2e sh -lc "npm install && npm run test:e2e:smoke"
197
+ ```
198
+
199
+ The `e2e` Docker image already includes Playwright browsers. If you need to (re)install browser binaries explicitly, run:
200
+
201
+ ```bash
202
+ docker compose -f docker/docker-compose.yml run --rm e2e npm run e2e:install
203
+ ```
204
+
205
+ ## Documentation
206
+
207
+ Toute la documentation se trouve dans `docs/` :
208
+
209
+ | Document | Description |
210
+ |----------|-------------|
211
+ | **[Quick Start](./docs/quickstart.md)** | Démarrer rapidement : installation, configuration, premier rendu, mode édition, API |
212
+ | **[Intégration backend](./docs/backend-integration.md)** | Routes attendues, contrats (IPageData, IPageSavePayload), validation, médias, sécurité |
213
+ | **[Architecture](./docs/architecture/)** | Vue d’ensemble, schéma JSON, système de composants, pipeline de rendu, architecture du mode édition |
214
+ | **[Fonctionnalités](./docs/features/)** | Mode lecture, mode édition, registre de composants, format JSON |
215
+ | **[Conventions](./docs/conventions/)** | Style de code, workflow git |
216
+ | **[Roadmap](./docs/plans/roadmap.md)** | Phases et jalons |
217
+ | **[Référence API](./docs/api/)** | Sortie TypeDoc (types et fonctions publics) |
218
+
219
+ Pour régénérer la référence API :
220
+
221
+ ```bash
222
+ docker compose -f docker/docker-compose.yml run --rm dev npm run docs:api
223
+ ```
224
+
225
+ ## Releases
226
+
227
+ Releases are **tag-based**. Pushing a tag `release-vX.Y.Z` triggers the GitHub Actions workflow (quality gate + publish to npm).
228
+
229
+ ### Creating a release
230
+
231
+ From the repo root, run the release script with the desired bump (`patch` is the default):
232
+
233
+ ```bash
234
+ ./scripts/release.sh [major|minor|patch]
235
+ # Examples:
236
+ ./scripts/release.sh # 0.1.0 → 0.1.1 (patch)
237
+ ./scripts/release.sh minor # 0.1.1 → 0.2.0
238
+ ./scripts/release.sh major # 0.2.0 → 1.0.0
239
+ ```
240
+
241
+ The script bumps the version in `package.json`, commits, creates the tag `release-vX.Y.Z`, and pushes the branch and tag. The CI then runs the quality gate and publishes to npm. See [Git Workflow — Releases](./docs/conventions/git-workflow.md#releases) for details.
242
+
243
+ ### Required repository secrets
244
+
245
+ - `NPM_TOKEN` (npm automation token with publish permission on `@improba/page-builder`)
246
+
247
+ ### Local release verification and manual publish (Docker)
248
+
249
+ ```bash
250
+ # Full release safety gate (typecheck + tests + build + types + docs)
251
+ docker compose -f docker/docker-compose.yml run --rm dev npm run release:prepare
252
+
253
+ # Inspect package contents before publish
254
+ docker compose -f docker/docker-compose.yml run --rm dev npm run release:dry-run
255
+
256
+ # Publish to npm manually (requires NPM_TOKEN in .env at project root)
257
+ source .env && docker compose -f docker/docker-compose.yml run --rm \
258
+ -e NPM_TOKEN="$NPM_TOKEN" \
259
+ dev sh -lc 'printf "//registry.npmjs.org/:_authToken=%s\n" "$NPM_TOKEN" > /tmp/.npmrc && npm publish --userconfig /tmp/.npmrc --access public'
260
+ ```
261
+
262
+ See [Git Workflow — Releases](./docs/conventions/git-workflow.md#releases) for the full release process (tag-based CI and manual publish).
263
+
264
+ ## API Reference
265
+
266
+ ### Vue Plugin
267
+
268
+ ```ts
269
+ app.use(PageBuilderPlugin, {
270
+ components: [], // Additional IComponentDefinition[]
271
+ registerBuiltIn: true, // Register PbColumn, PbRow, etc.
272
+ globalName: 'PageBuilder', // Global component name (false to skip)
273
+ });
274
+ ```
275
+
276
+ ### Registry Functions
277
+
278
+ | Function | Description |
279
+ |----------|-------------|
280
+ | `registerComponent(def)` | Register a single component |
281
+ | `registerComponents(defs)` | Register multiple components |
282
+ | `replaceComponent(def)` | Override an existing registration |
283
+ | `unregisterComponent(name)` | Remove a registration |
284
+ | `getComponent(name)` | Get definition by name |
285
+ | `resolveComponent(name)` | Get Vue component (throws if missing) |
286
+ | `getRegisteredComponents()` | Get all definitions |
287
+ | `getComponentsByCategory()` | Get definitions grouped by category |
288
+ | `hasComponent(name)` | Check if registered |
289
+ | `clearRegistry()` | Remove all (testing) |
290
+
291
+ ### Tree Utilities
292
+
293
+ | Function | Description |
294
+ |----------|-------------|
295
+ | `findNodeById(root, id)` | Find node in tree |
296
+ | `findParent(root, childId)` | Find parent of node |
297
+ | `removeNode(root, id)` | Remove node from tree |
298
+ | `insertNode(root, parentId, node, index, slot)` | Insert node |
299
+ | `moveNode(root, nodeId, parentId, index, slot)` | Move node |
300
+ | `createNode(id, name, options)` | Create new node |
301
+ | `walkTree(root, visitor)` | Depth-first traversal |
302
+ | `cloneTree(node)` | Deep clone |
303
+ | `interpolateProps(props, vars)` | Replace template variables |
304
+
305
+ ### Composables
306
+
307
+ | Composable | Purpose |
308
+ |------------|---------|
309
+ | `usePageBuilder(options)` | Core state management (mode, content, history) |
310
+ | `useEditor()` | Editor UI state (selection, drawers, viewport) |
311
+ | `useNodeTree(options)` | Tree mutation operations |
312
+ | `useDragDrop()` | Drag-and-drop interaction state |
313
+
314
+ ## License
315
+
316
+ MIT