@scaleflex/template-builder 0.4.1 → 0.4.2

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.
@@ -12,7 +12,7 @@ metadata:
12
12
  - design-templates
13
13
  - web-component
14
14
  status: ready
15
- version: 1
15
+ version: 2
16
16
  ---
17
17
 
18
18
  # Scaleflex Template Builder Integration Skill
@@ -24,6 +24,10 @@ metadata:
24
24
  - User asks how to use `@scaleflex/template-builder` in their project
25
25
  - User wants a banner / creative editor backed by Scaleflex rendering
26
26
 
27
+ A complete working React host (gallery, save round trip, live render
28
+ preview) is published at <https://stackblitz.com/edit/sotacrwx> — read its
29
+ `App.tsx` for the whole contract in one file.
30
+
27
31
  ## Step 1 — Decide who stores the template
28
32
 
29
33
  This is the first question, and it changes everything downstream. Ask if it is
@@ -43,7 +47,7 @@ storage and does not want to map them onto Scaleflex tenants. Pick
43
47
  Statelessness applies to the **document only**. The editor still needs an
44
48
  authenticated Scaleflex tenant for server-side text rendering, custom fonts,
45
49
  asset browsing, and metadata variables — so a stateless embed still needs a
46
- credential (Step 7).
50
+ credential (Step 8).
47
51
 
48
52
  ## Step 2 — Detect the target framework
49
53
 
@@ -213,7 +217,46 @@ try {
213
217
 
214
218
  The React wrapper does this from what `onSave` returns or throws.
215
219
 
216
- ## Step 6 — Guard against losing edits
220
+ ## Step 6 — Render links and previews (optional): `dam-store`
221
+
222
+ The CDN renders only files it stores, so a stateless host that wants image
223
+ URLs out of its templates (previews, production banners) needs a copy in
224
+ Filerobot too. Set `dam-store` and the element uploads that copy itself on
225
+ every save, before the `save` event fires — the host writes no upload code:
226
+
227
+ ```html
228
+ <sfx-template-builder stateless dam-store store-folder="/my-app" …>
229
+ ```
230
+
231
+ ```js
232
+ builder.addEventListener('save', (e) => {
233
+ const { content, templateQuery, stored, storeError } = e.detail
234
+ // stored = { uuid, url } — append templateQuery to url and it IS an image:
235
+ // <img src=`${stored.url}&${templateQuery}&force_format=png`>
236
+ // storeError replaces stored when the copy failed; the raw content
237
+ // arrives either way — whether that fails the save is your confirmSave call.
238
+ })
239
+ ```
240
+
241
+ Wiring rules that matter:
242
+
243
+ - **Branch on the detail** (`'stored' in e.detail || 'storeError' in e.detail`),
244
+ never on the live `damStore` property — saves arrive from a serialized
245
+ upload queue and the property can change before a queued event lands.
246
+ - **Persist `stored.uuid` and pass it back as `stored-uuid` / `storedUuid`
247
+ (or the `storedUuid` field of `load()`) when reopening the template.** The
248
+ element's memory of its copy dies with the page; the seed is what lets a
249
+ re-save version the existing file in place instead of erroring on
250
+ unchanged content. Do NOT echo the uuid into `template-id` — an id change
251
+ deliberately reloads the editor.
252
+ - **A save can arrive after `close`** when its upload is still in flight —
253
+ keep the `save` listener attached until it does. Removing the element from
254
+ the DOM flushes pending saves to still-attached listeners immediately.
255
+ - The credential needs **upload rights**, or every save reports `storeError`.
256
+ - `store-folder` names where templates with no existing DAM file land; an
257
+ existing file's own folder always wins.
258
+
259
+ ## Step 7 — Guard against losing edits
217
260
 
218
261
  Assigning a new `content` / `templateId` / `templateName` / `templateQuery`
219
262
  reloads the editor and
@@ -229,7 +272,7 @@ builder.addEventListener('dirtychange', (e) => {
229
272
  Re-assigning an identical template is a no-op, so an unrelated re-render cannot
230
273
  destroy work by accident.
231
274
 
232
- ## Step 7 — Authentication
275
+ ## Step 8 — Authentication
233
276
 
234
277
  Two credentials work. Both are minted **server-side** and injected into the
235
278
  page; neither belongs in a public bundle.
@@ -278,7 +321,7 @@ server-side, so it authenticates on the handed-over credential and returns
278
321
  template is what authorizes it when there is no Hub session. Full explanation:
279
322
  *Why the editor still calls a server* in the README.
280
323
 
281
- ## Step 8 — Register the embedding origin (required)
324
+ ## Step 9 — Register the embedding origin (required)
282
325
 
283
326
  The host page's origin must be in the deployment's embedding allowlist, via
284
327
  `NEXT_PUBLIC_TRUSTED_HUB_ORIGINS` on the `design-templates-app` deployment.
@@ -288,7 +331,7 @@ This is baked in at build time, so it needs a redeploy.
288
331
  and the widget reports `error` with code `handshake-timeout`. That is the
289
332
  single most common first-integration failure — check it before anything else.
290
333
 
291
- ## Step 9 — Theming (optional)
334
+ ## Step 10 — Theming (optional)
292
335
 
293
336
  ```html
294
337
  <sfx-template-builder brand-color="#FF6600" theme="dark" ...>
@@ -312,7 +355,7 @@ template document.
312
355
  | `token` | yes | Scaleflex token (`ftoken`) |
313
356
  | `sass-key` / `sassKey` | session auth | Project sass key |
314
357
  | `session-uuid` / `sessionUuid` | session auth | Hub session uuid |
315
- | `sec-template` / `secTemplate` | guest auth | Security-template key, instead of sass key + session uuid. Stateless only (Step 7) |
358
+ | `sec-template` / `secTemplate` | guest auth | Security-template key, instead of sass key + session uuid. Stateless only (Step 8) |
316
359
  | `company-uuid`, `project-uuid` | no | Company / project scoping (session auth only) |
317
360
  | `template-id` / `templateId` | no | DAM: the file uuid. Stateless: opaque host id |
318
361
  | `mode` | no | `inline` (default) or `modal` (starts closed — call `open()`) |
@@ -321,7 +364,12 @@ template document.
321
364
  | `new-template` / `newTemplate` | no | Stateless: open on a new, empty template — the widget supplies the blank document. Ignored when `content` is set |
322
365
  | `template-name` / `templateName` | no | Stateless: header title |
323
366
  | `template-query` / `templateQuery` | no | Stateless: the render to open on — the `templateQuery` from the last save. Empty uses the XML's `default=` values. |
324
- | `brand-color`, `theme` | no | See Step 9 |
367
+ | `dam-store` / `damStore` | no | Stateless: store each save in Filerobot too; the `save` detail gains `stored: { uuid, url }` or `storeError` (Step 6) |
368
+ | `store-folder` / `storeFolder` | no | `dam-store`: folder for templates with no existing DAM file (default `/`) |
369
+ | `stored-uuid` / `storedUuid` | no | `dam-store`: the persisted `stored.uuid`, passed back so re-saves version the existing copy across reloads (Step 6) |
370
+ | `custom-metadata` / `customMetadata` | no | Metadata model offered as the **Custom metadata** value source: `[{ key, title?, group? }]` — names only, values go into the render query as `$slug=value` |
371
+ | `custom-metadata-label` / `customMetadataLabel` | no | Renames that value source in the editor's UI (wording only) |
372
+ | `brand-color`, `theme` | no | See Step 10 |
325
373
  | `ready-timeout` / `readyTimeout` | no | Ms before `handshake-timeout` (default 20000) |
326
374
 
327
375
  ## Events Reference
@@ -330,26 +378,27 @@ template document.
330
378
  |---|---|
331
379
  | `ready` | — (editor mounted, auth valid) |
332
380
  | `open` | — |
333
- | `save` | `{ uuid, name }` (DAM) or `{ templateId, content, name, templateQuery }` (stateless) |
381
+ | `save` | `{ uuid, name }` (DAM) or `{ templateId, content, name, templateQuery }` (stateless; under `dam-store` also `stored: { uuid, url }` or `storeError`) |
334
382
  | `dirtychange` | `{ isDirty }` (stateless) |
335
- | `close` | — (user left the editor, or it unmounted) |
383
+ | `close` | — (user left the editor, or it unmounted). A `dam-store` save whose upload is still in flight emits **after** `close` — keep listening |
336
384
  | `error` | `{ code, message? }` |
337
385
 
338
386
  `error` codes: `auth`, `invalid-content`, `invalid-config`,
339
387
  `handshake-timeout`, `invalid-base-url`, `unknown`.
340
388
 
341
- Post-0.3.0 additions (check the CHANGELOG against your pinned bundle):
342
- `custom-metadata-label` renames the "Custom metadata" value source in the
343
- editor's UI (wording only); `dam-store` makes the element store each stateless
344
- save in Filerobot and adds `stored: { uuid, url }` (or `storeError`) to the
345
- `save` detail — `url` + `templateQuery` is a render URL, and `store-folder`
346
- names where templates with no existing DAM file land.
389
+ `dam-store`, `stored-uuid` and `custom-metadata-label` need bundle/package
390
+ **0.4.0+** an older pinned bundle silently ignores the attributes. Check
391
+ the CHANGELOG against what the host pins.
347
392
 
348
393
  ## Public Methods
349
394
 
350
- `open(templateId?)`, `close()`, `load({ content, templateId?, name? })`,
351
- `createNew({ templateId?, name? })`, `confirmSave(ok, message?)`. Read-only:
352
- `status`, `isDirty`.
395
+ `open(templateId?)`, `close()`,
396
+ `load({ content, templateId?, name?, templateQuery?, storedUuid? })`
397
+ (omitting `storedUuid` clears the seed — it belongs to the document),
398
+ `createNew({ templateId?, name? })`, `confirmSave(ok, message?)`, and
399
+ `flushPendingSaves(reason?)` (only for framework wrappers that detach
400
+ listeners without removing the element; the React wrapper calls it itself).
401
+ Read-only: `status`, `isDirty`.
353
402
 
354
403
  In React these are reached through a forwarded ref — **required for
355
404
  `mode="modal"`**, which renders nothing until `open()` is called:
@@ -393,11 +442,11 @@ ignored; and `layout` / `locale` / `force_format` are reserved slugs.
393
442
 
394
443
  | Symptom | Cause |
395
444
  |---|---|
396
- | `error` code `handshake-timeout`, nothing rendered | Host origin not in the deployment's embedding allowlist (Step 8), or third-party cookies blocked |
445
+ | `error` code `handshake-timeout`, nothing rendered | Host origin not in the deployment's embedding allowlist (Step 9), or third-party cookies blocked |
397
446
  | `error` code `auth` | Bad/expired session credentials, a rejected security template, or cookies blocked |
398
447
  | `error` code `invalid-content` | The `content` handed over is not parseable `.fdt` XML |
399
- | `error` code `invalid-config` | `sec-template` without `stateless` (Step 7) |
400
- | Metadata / regional / branding panels empty | Expected under `sec-template` — those come from the Hub project (Step 7) |
448
+ | `error` code `invalid-config` | `sec-template` without `stateless` (Step 8) |
449
+ | Metadata / regional / branding panels empty | Expected under `sec-template` — those come from the Hub project (Step 8) |
401
450
  | Nothing renders, no events | `inline` mode inside a zero-height parent — size the element |
402
451
  | Modal never appears | `open()` never called; in React that needs a ref |
403
452
  | Saves look successful but nothing is stored | `confirmSave(false)` not wired (Step 5) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scaleflex/template-builder",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Embeddable web component (<sfx-template-builder>) for the Filerobot design-templates builder",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",