@scaleflex/template-builder 0.1.1 → 0.4.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/.claude/skills/integrate-template-builder/SKILL.md +79 -24
- package/CHANGELOG.md +190 -4
- package/README.md +452 -66
- package/dist/dam-store.d.ts +92 -0
- package/dist/define.cjs +1 -1
- package/dist/define.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -14
- package/dist/protocol.d.ts +84 -2
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +41 -1
- package/dist/react.js +45 -29
- package/dist/react.js.map +1 -1
- package/dist/template-builder-B9Cwo_Q-.js +651 -0
- package/dist/template-builder-B9Cwo_Q-.js.map +1 -0
- package/dist/template-builder-Byqg1q93.cjs +53 -0
- package/dist/template-builder-Byqg1q93.cjs.map +1 -0
- package/dist/template-builder.d.ts +201 -6
- package/package.json +3 -1
- package/src/dam-store.ts +388 -0
- package/src/index.ts +2 -0
- package/src/protocol.ts +92 -2
- package/src/react.ts +120 -27
- package/src/template-builder.ts +467 -11
- package/dist/template-builder-CSyPZni9.cjs +0 -52
- package/dist/template-builder-CSyPZni9.cjs.map +0 -1
- package/dist/template-builder-S33H_d5T.js +0 -354
- package/dist/template-builder-S33H_d5T.js.map +0 -1
|
@@ -8,7 +8,6 @@ metadata:
|
|
|
8
8
|
category: integration
|
|
9
9
|
tags:
|
|
10
10
|
- scaleflex
|
|
11
|
-
- filerobot
|
|
12
11
|
- template-builder
|
|
13
12
|
- design-templates
|
|
14
13
|
- web-component
|
|
@@ -23,7 +22,7 @@ metadata:
|
|
|
23
22
|
- User says "add the template builder", "embed the design template editor",
|
|
24
23
|
"let users edit templates in our app"
|
|
25
24
|
- User asks how to use `@scaleflex/template-builder` in their project
|
|
26
|
-
- User wants a banner / creative editor backed by
|
|
25
|
+
- User wants a banner / creative editor backed by Scaleflex rendering
|
|
27
26
|
|
|
28
27
|
## Step 1 — Decide who stores the template
|
|
29
28
|
|
|
@@ -32,18 +31,19 @@ not already clear from the project.
|
|
|
32
31
|
|
|
33
32
|
| | **DAM-backed** (default) | **Stateless** |
|
|
34
33
|
|---|---|---|
|
|
35
|
-
| Template lives in |
|
|
36
|
-
| `template-id` is | a
|
|
34
|
+
| Template lives in | Scaleflex, as a `.fdt` file | The host's own database |
|
|
35
|
+
| `template-id` is | a DAM file uuid | any string the host chooses |
|
|
37
36
|
| Save does | uploads a new version, returns `{ uuid }` | hands back `{ content }` for the host to store |
|
|
38
37
|
| Needs per-user Scaleflex identities | yes | no — one service tenant is enough |
|
|
39
38
|
|
|
40
39
|
Pick **stateless** when the host already has its own users, permissions and
|
|
41
40
|
storage and does not want to map them onto Scaleflex tenants. Pick
|
|
42
|
-
**DAM-backed** when templates are a
|
|
41
|
+
**DAM-backed** when templates are a Scaleflex asset like any other.
|
|
43
42
|
|
|
44
43
|
Statelessness applies to the **document only**. The editor still needs an
|
|
45
|
-
authenticated
|
|
46
|
-
asset browsing, and metadata variables
|
|
44
|
+
authenticated Scaleflex tenant for server-side text rendering, custom fonts,
|
|
45
|
+
asset browsing, and metadata variables — so a stateless embed still needs a
|
|
46
|
+
credential (Step 7).
|
|
47
47
|
|
|
48
48
|
## Step 2 — Detect the target framework
|
|
49
49
|
|
|
@@ -61,7 +61,7 @@ npm i @scaleflex/template-builder
|
|
|
61
61
|
Or via CDN (self-registering, Lit bundled in — pin the major):
|
|
62
62
|
|
|
63
63
|
```html
|
|
64
|
-
<script type="module" src="https://cdn.
|
|
64
|
+
<script type="module" src="https://cdn.cloudimage.io/template-builder/0.4.0/template-builder.min.js"></script>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
**SSR caution:** the element extends `HTMLElement`, so importing `.`,
|
|
@@ -82,7 +82,7 @@ in, take the edited XML back out.**
|
|
|
82
82
|
<sfx-template-builder
|
|
83
83
|
stateless
|
|
84
84
|
base-url="https://<deployment>"
|
|
85
|
-
token="
|
|
85
|
+
token="PROJECT_TOKEN"
|
|
86
86
|
sass-key="SASS_KEY"
|
|
87
87
|
session-uuid="SESSION_UUID"
|
|
88
88
|
style="display:block;height:800px"
|
|
@@ -144,15 +144,48 @@ import { TemplateBuilder } from '@scaleflex/template-builder/react'
|
|
|
144
144
|
/>
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
+
### Stateless — a template that does not exist yet
|
|
148
|
+
|
|
149
|
+
A host whose user is creating their first template has no XML to pass in, and
|
|
150
|
+
should not have to author any. Set `new-template` instead of `content` and the
|
|
151
|
+
widget supplies the empty document:
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<sfx-template-builder stateless new-template template-name="Untitled" …>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
builder.createNew({ templateId: 'your-own-id-43', name: 'Untitled' })
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
<TemplateBuilder stateless newTemplate templateName="Untitled" … />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The editor opens on its empty state, the user adds the first layout (canvas
|
|
166
|
+
size, background, preset), and the first `save` hands back a complete `.fdt`
|
|
167
|
+
document — store that and every later open is the ordinary load flow. Save is
|
|
168
|
+
refused until a layout exists.
|
|
169
|
+
|
|
170
|
+
- `templateId` is optional; without one the `save` payload just arrives without
|
|
171
|
+
an id, and the host allocates one when storing.
|
|
172
|
+
- `templateQuery` stays empty — a new document has no layouts or variables for
|
|
173
|
+
a query to select. It comes back on the first save.
|
|
174
|
+
- `content` wins when both are set, so one element can serve both cases.
|
|
175
|
+
- Empty `content` alone does **not** start a blank template: it means the host
|
|
176
|
+
is still fetching, and the editor keeps waiting. Only the flag changes that.
|
|
177
|
+
- For a house-style starting point (standard canvas, locked logo layer), pass
|
|
178
|
+
it as ordinary `content` — a starter template is just a template.
|
|
179
|
+
|
|
147
180
|
### DAM-backed
|
|
148
181
|
|
|
149
|
-
`template-id` is a
|
|
182
|
+
`template-id` is a DAM file uuid; omit it to open the new-template flow.
|
|
150
183
|
The app loads and saves the file itself.
|
|
151
184
|
|
|
152
185
|
```html
|
|
153
186
|
<sfx-template-builder
|
|
154
187
|
base-url="https://<deployment>"
|
|
155
|
-
token="
|
|
188
|
+
token="PROJECT_TOKEN"
|
|
156
189
|
sass-key="SASS_KEY"
|
|
157
190
|
session-uuid="SESSION_UUID"
|
|
158
191
|
template-id="TEMPLATE_UUID"
|
|
@@ -164,8 +197,8 @@ The app loads and saves the file itself.
|
|
|
164
197
|
|
|
165
198
|
## Step 5 — Handle the save outcome (stateless only)
|
|
166
199
|
|
|
167
|
-
The editor clears its unsaved-changes state as soon as it
|
|
168
|
-
|
|
200
|
+
The editor clears its unsaved-changes state as soon as it emits `save`, because
|
|
201
|
+
receiving the event says nothing about whether the host stored anything.
|
|
169
202
|
**Call `confirmSave(false)` when your write fails**, or a failed save silently
|
|
170
203
|
looks successful and the user loses work:
|
|
171
204
|
|
|
@@ -211,7 +244,7 @@ it when the host has no Scaleflex identity to hand over per user:
|
|
|
211
244
|
```html
|
|
212
245
|
<sfx-template-builder
|
|
213
246
|
base-url="https://<deployment>"
|
|
214
|
-
token="
|
|
247
|
+
token="PROJECT_TOKEN"
|
|
215
248
|
sec-template="SEC_TEMPLATE_KEY"
|
|
216
249
|
stateless
|
|
217
250
|
></sfx-template-builder>
|
|
@@ -232,15 +265,28 @@ What it costs:
|
|
|
232
265
|
In stateless mode a single service tenant is usually right either way: one
|
|
233
266
|
machine credential, with all per-user permission logic staying in the host app.
|
|
234
267
|
|
|
268
|
+
**If asked why a stateless embed needs a credential at all** — because the
|
|
269
|
+
preview is not drawn by the browser. A `.fdt` is rendered server-side with
|
|
270
|
+
ImageMagick when its CDN URL is requested, and browser text APIs cannot
|
|
271
|
+
reproduce that layout (kerning, letter spacing, wrapping, baselines,
|
|
272
|
+
antialiasing differ per browser and from the export), so the editor rasterizes
|
|
273
|
+
every text and shape layer through the same engine via
|
|
274
|
+
`POST /api/render-layers` on the `base-url` deployment — batched, debounced,
|
|
275
|
+
supersampled 3×, composited with CSS. That endpoint fetches fonts and images
|
|
276
|
+
server-side, so it authenticates on the handed-over credential and returns
|
|
277
|
+
`401` without one: no credential, no visible text or shape layers. The security
|
|
278
|
+
template is what authorizes it when there is no Hub session. Full explanation:
|
|
279
|
+
*Why the editor still calls a server* in the README.
|
|
280
|
+
|
|
235
281
|
## Step 8 — Register the embedding origin (required)
|
|
236
282
|
|
|
237
|
-
The host page's origin must be in the deployment's
|
|
238
|
-
|
|
283
|
+
The host page's origin must be in the deployment's embedding allowlist, via
|
|
284
|
+
`NEXT_PUBLIC_TRUSTED_HUB_ORIGINS` on the `design-templates-app` deployment.
|
|
239
285
|
This is baked in at build time, so it needs a redeploy.
|
|
240
286
|
|
|
241
|
-
**If this is missed**, the browser refuses to
|
|
242
|
-
reports `error` with code `handshake-timeout`. That is the
|
|
243
|
-
first-integration failure — check it before anything else.
|
|
287
|
+
**If this is missed**, the browser refuses to load the editor on the host page
|
|
288
|
+
and the widget reports `error` with code `handshake-timeout`. That is the
|
|
289
|
+
single most common first-integration failure — check it before anything else.
|
|
244
290
|
|
|
245
291
|
## Step 9 — Theming (optional)
|
|
246
292
|
|
|
@@ -263,15 +309,16 @@ template document.
|
|
|
263
309
|
| Attribute / property | Required | Description |
|
|
264
310
|
|---|---|---|
|
|
265
311
|
| `base-url` / `baseUrl` | yes | Origin of the design-templates-app deployment |
|
|
266
|
-
| `token` | yes |
|
|
312
|
+
| `token` | yes | Scaleflex token (`ftoken`) |
|
|
267
313
|
| `sass-key` / `sassKey` | session auth | Project sass key |
|
|
268
314
|
| `session-uuid` / `sessionUuid` | session auth | Hub session uuid |
|
|
269
315
|
| `sec-template` / `secTemplate` | guest auth | Security-template key, instead of sass key + session uuid. Stateless only (Step 7) |
|
|
270
316
|
| `company-uuid`, `project-uuid` | no | Company / project scoping (session auth only) |
|
|
271
|
-
| `template-id` / `templateId` | no | DAM:
|
|
317
|
+
| `template-id` / `templateId` | no | DAM: the file uuid. Stateless: opaque host id |
|
|
272
318
|
| `mode` | no | `inline` (default) or `modal` (starts closed — call `open()`) |
|
|
273
|
-
| `stateless` | no | Host owns the document; requires `content` |
|
|
319
|
+
| `stateless` | no | Host owns the document; requires `content`, or `new-template` |
|
|
274
320
|
| `content` (property only) | stateless | Template as `.fdt` XML |
|
|
321
|
+
| `new-template` / `newTemplate` | no | Stateless: open on a new, empty template — the widget supplies the blank document. Ignored when `content` is set |
|
|
275
322
|
| `template-name` / `templateName` | no | Stateless: header title |
|
|
276
323
|
| `template-query` / `templateQuery` | no | Stateless: the render to open on — the `templateQuery` from the last save. Empty uses the XML's `default=` values. |
|
|
277
324
|
| `brand-color`, `theme` | no | See Step 9 |
|
|
@@ -291,10 +338,18 @@ template document.
|
|
|
291
338
|
`error` codes: `auth`, `invalid-content`, `invalid-config`,
|
|
292
339
|
`handshake-timeout`, `invalid-base-url`, `unknown`.
|
|
293
340
|
|
|
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.
|
|
347
|
+
|
|
294
348
|
## Public Methods
|
|
295
349
|
|
|
296
350
|
`open(templateId?)`, `close()`, `load({ content, templateId?, name? })`,
|
|
297
|
-
`confirmSave(ok, message?)`. Read-only:
|
|
351
|
+
`createNew({ templateId?, name? })`, `confirmSave(ok, message?)`. Read-only:
|
|
352
|
+
`status`, `isDirty`.
|
|
298
353
|
|
|
299
354
|
In React these are reached through a forwarded ref — **required for
|
|
300
355
|
`mode="modal"`**, which renders nothing until `open()` is called:
|
|
@@ -338,7 +393,7 @@ ignored; and `layout` / `locale` / `force_format` are reserved slugs.
|
|
|
338
393
|
|
|
339
394
|
| Symptom | Cause |
|
|
340
395
|
|---|---|
|
|
341
|
-
| `error` code `handshake-timeout`,
|
|
396
|
+
| `error` code `handshake-timeout`, nothing rendered | Host origin not in the deployment's embedding allowlist (Step 8), or third-party cookies blocked |
|
|
342
397
|
| `error` code `auth` | Bad/expired session credentials, a rejected security template, or cookies blocked |
|
|
343
398
|
| `error` code `invalid-content` | The `content` handed over is not parseable `.fdt` XML |
|
|
344
399
|
| `error` code `invalid-config` | `sec-template` without `stateless` (Step 7) |
|
package/CHANGELOG.md
CHANGED
|
@@ -8,15 +8,201 @@ Protocol message *values* are wire format: an existing string is never changed,
|
|
|
8
8
|
only new messages added, so an older widget keeps working against a newer app
|
|
9
9
|
deployment and vice versa.
|
|
10
10
|
|
|
11
|
+
## [Unreleased]
|
|
12
|
+
|
|
13
|
+
## [0.4.0] - 2026-08-26
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `stored-uuid` attribute / `storedUuid` prop (and a `storedUuid` field on
|
|
18
|
+
`load()`) — seeds the element's `dam-store` memory with the copy a document
|
|
19
|
+
already has, so re-saves after a page reload resolve to (and version) the
|
|
20
|
+
existing file instead of reporting a spurious `storeError` on unchanged
|
|
21
|
+
content or starting a fresh file. Per-document: `load()` without it clears
|
|
22
|
+
the seed, `createNew()` always does.
|
|
23
|
+
- `flushPendingSaves()` public method — emits any `dam-store` saves still
|
|
24
|
+
waiting on their upload, raw content with `storeError` in place of the
|
|
25
|
+
links. For framework wrappers that unsubscribe their listeners before the
|
|
26
|
+
element leaves the DOM; the React wrapper calls it from its cleanup, so a
|
|
27
|
+
host unmounting `<TemplateBuilder>` mid-upload no longer loses the save.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Closing the editor no longer flushes a `dam-store` upload still in flight
|
|
32
|
+
into a `storeError`: the element outlives a close (inline keeps rendering,
|
|
33
|
+
modal drops only its overlay), so the save emits its real outcome when the
|
|
34
|
+
copy lands — possibly after the `close` event. Element removal still
|
|
35
|
+
flushes immediately.
|
|
36
|
+
- `dam-store` filenames: the element reuses its known copy's own filename so
|
|
37
|
+
versioning-in-place keeps matching; otherwise the name carries a
|
|
38
|
+
deterministic hash of the host's template id — the same document lands on
|
|
39
|
+
the same DAM file across page reloads (no fork per session, no stored-uuid
|
|
40
|
+
seed required), while two documents sharing a display name ("Untitled")
|
|
41
|
+
never collide. Only an id-less document falls back to a per-page unique
|
|
42
|
+
suffix.
|
|
43
|
+
- `dam-store` document memory is now epoch-based and validated at read time:
|
|
44
|
+
the document epoch advances only when a genuinely different document ships,
|
|
45
|
+
never on iframe changes — a modal close or theme swap mid-save no longer
|
|
46
|
+
forgets the stored copy, a host echo followed by an in-place remount no
|
|
47
|
+
longer orphans it, and a late-landing upload is recorded under its own
|
|
48
|
+
document's epoch so it can never masquerade as another document's copy.
|
|
49
|
+
- `dam-store` no longer looks up opaque (non-uuid-shaped) template ids
|
|
50
|
+
against the files API — one round trip saved per save, and the store no
|
|
51
|
+
longer depends on how the API answers a malformed id. The transient-failure
|
|
52
|
+
rule is role-aware: the required folder anchor still fails the copy, but a
|
|
53
|
+
transient failure of the optional freshness lookup no longer fails a save
|
|
54
|
+
the other anchor can carry.
|
|
55
|
+
- An unchanged-content conflict with no anchor in hand (first save after a
|
|
56
|
+
reload without the stored-uuid seed) now resolves itself by finding the
|
|
57
|
+
document's own earlier copy by its deterministic filename; a genuinely
|
|
58
|
+
foreign duplicate reports an actionable error naming `stored-uuid` as the
|
|
59
|
+
fix.
|
|
60
|
+
- Removing the element flushes pending saves one task later, skipped if the
|
|
61
|
+
element is reconnected — reparenting the mounted widget (e.g. a fullscreen
|
|
62
|
+
toggle) no longer reports a false `storeError` for an upload that lands
|
|
63
|
+
fine.
|
|
64
|
+
- Demo page: the Filerobot API helpers (key exchange, scope headers, file
|
|
65
|
+
lookup) are imported from `dam-store.ts` instead of being hand copies.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- A host echoing the full save detail back into the props (content + name +
|
|
70
|
+
`templateQuery` — the documented controlled pattern) no longer triggers a
|
|
71
|
+
HOST_LOAD that reloads the editor and wipes undo history: the element now
|
|
72
|
+
also accepts the save's own identity, whose query nearly always differs
|
|
73
|
+
from the one the template was loaded with.
|
|
74
|
+
- A transient Filerobot failure (5xx, 429, auth, network) during the
|
|
75
|
+
folder lookup no longer reads as "file does not exist" — which silently
|
|
76
|
+
re-homed an existing template into the fallback folder as a brand-new
|
|
77
|
+
file, or failed an unchanged re-save with a spurious `storeError`. Only a
|
|
78
|
+
clean 404 means missing; everything else fails the copy explicitly, and
|
|
79
|
+
the post-upload record read-back stays best-effort.
|
|
80
|
+
- `dam-store` uploads now declare the template MIME type
|
|
81
|
+
(`text/xml+sfxtemplate`) the app's own uploads use, instead of plain
|
|
82
|
+
`text/xml`.
|
|
83
|
+
- A queued `dam-store` upload captures `store-folder` and the credentials at
|
|
84
|
+
save time — an in-place document swap while a save is still queued no
|
|
85
|
+
longer re-targets that save's upload to the new document's folder or
|
|
86
|
+
tenant.
|
|
87
|
+
- A `dam-store` upload finishing after the host swapped documents in place
|
|
88
|
+
(new `content`/`templateId` on the mounted element) no longer records the
|
|
89
|
+
old document's uuid into the new document's session memory — a follow-up
|
|
90
|
+
save can no longer version the wrong DAM file.
|
|
91
|
+
- React wrapper: event listeners are now attached once per element and read
|
|
92
|
+
the latest callbacks at event time, instead of being re-bound on every
|
|
93
|
+
handler identity change. The unmount-time flush is guarded, so a page
|
|
94
|
+
whose older CDN bundle registered the element first degrades silently
|
|
95
|
+
instead of throwing from the effect cleanup.
|
|
96
|
+
- Demo page: the save handler now detects a `dam-store` save from the event
|
|
97
|
+
detail itself (`stored`/`storeError` present) rather than the live
|
|
98
|
+
`damStore` property, which a "Load template" inside the upload window can
|
|
99
|
+
rewrite before the serialized save event arrives.
|
|
100
|
+
- Demo sample template: the `$headline$` placeholder now renders — the
|
|
101
|
+
variable holds its value in the `default=` attribute as
|
|
102
|
+
`type="text_placeholder"`, the shape the app's parser actually reads
|
|
103
|
+
(CDATA text on a `type="text"` variable is ignored, leaving the layer
|
|
104
|
+
blank).
|
|
105
|
+
|
|
106
|
+
- `dam-store` attribute / `damStore` prop (stateless only) — the element
|
|
107
|
+
stores each save in Filerobot itself, with the same multipart upload the
|
|
108
|
+
DAM-backed editor makes, before emitting `save`. The detail then carries
|
|
109
|
+
`stored: { uuid, url }` (the DAM copy's uuid and its CDN URL with a fresh
|
|
110
|
+
`?vh=`) next to the raw `content` — append the `templateQuery` to `url` and
|
|
111
|
+
it is a render. A failed copy emits `storeError` instead; the raw data
|
|
112
|
+
arrives either way, and the host decides via the save ack whether that
|
|
113
|
+
fails the save. `store-folder` / `storeFolder` names the folder for
|
|
114
|
+
templates whose id matches no existing DAM file (an existing file's own
|
|
115
|
+
folder always wins, so same-name saves version in place). The stored
|
|
116
|
+
`template_query` metadata has custom-metadata-bound values stripped —
|
|
117
|
+
record-agnostic, like the editor's own stores. Rapid saves are serialized
|
|
118
|
+
(events and acks keep post order); element removal mid-upload flushes the
|
|
119
|
+
pending save immediately — raw content with `storeError`, never a dropped
|
|
120
|
+
save (a mere close lets the upload finish and the save emit its real
|
|
121
|
+
outcome); and the element remembers the copy it last made, so unchanged
|
|
122
|
+
re-saves resolve to it instead of erroring even when the host's own
|
|
123
|
+
template id is opaque.
|
|
124
|
+
- Demo page: the store checkbox now drives `dam-store` (on by default) — the
|
|
125
|
+
page writes no upload code; it reads `stored` off the save event, logs the
|
|
126
|
+
links, and the Template id / .fdt URL fields follow the stored file so the
|
|
127
|
+
preview always renders what was just saved. A failed copy fails the save
|
|
128
|
+
(`confirmSave(false)`).
|
|
129
|
+
- Demo page: a **Show preview** button under the template-query field. It
|
|
130
|
+
renders the stored `.fdt` with the query applied and the host's
|
|
131
|
+
custom-metadata values substituted — the same image URL a host page would
|
|
132
|
+
embed. The file's CDN URL is filled in by a library pick, resolved from the
|
|
133
|
+
Template id via the API when the field is left empty (dam-store saves also
|
|
134
|
+
fill it), or pasted by hand. Renders the stored file, not unsaved local
|
|
135
|
+
edits.
|
|
136
|
+
|
|
137
|
+
- `custom-metadata-label` attribute / `customMetadataLabel` prop — renames the
|
|
138
|
+
**Custom metadata** value source in the editor's UI (source dropdowns and the
|
|
139
|
+
properties-panel section), e.g. to `External metadata`. Wording only: the
|
|
140
|
+
saved template carries the same `custom_ckey` binding either way, and an
|
|
141
|
+
empty value keeps the editor's default label. Requires an app deployment
|
|
142
|
+
that understands `customMetadataLabel` in `HOST_CONFIG`; an older app
|
|
143
|
+
ignores it.
|
|
144
|
+
|
|
145
|
+
## [0.3.0] - 2026-08-18
|
|
146
|
+
|
|
147
|
+
### Added
|
|
148
|
+
|
|
149
|
+
- `custom-metadata` attribute / `customMetadata` prop — a metadata model
|
|
150
|
+
(`[{ key, title?, group? }]`) offered in the editor as a third **Custom
|
|
151
|
+
metadata** value source, so authors bind a text variable to one of your field
|
|
152
|
+
names instead of to a bare slug. Names only: no values travel with the model
|
|
153
|
+
and the editor resolves nothing against it. The binding is saved on the
|
|
154
|
+
variable as `custom_ckey` while the variable stays `source="URL"`, so the
|
|
155
|
+
render pipeline is unchanged — you substitute the value with `$slug=value` as
|
|
156
|
+
you would for free text, and read `custom_ckey` back from the stored `.fdt` to
|
|
157
|
+
know which field each variable wants. Set no model and the source is not
|
|
158
|
+
offered. Its main use is `sec-template` / stateless embeds, where the Hub
|
|
159
|
+
project model — and with it the *File metadata* source — is unavailable.
|
|
160
|
+
Fields the editor cannot use are dropped rather than breaking the source: no
|
|
161
|
+
`key`, a duplicate `key`, or a `key` holding a character XML cannot represent
|
|
162
|
+
(a C0 control, an unpaired surrogate) — the last of which would otherwise make
|
|
163
|
+
the saved `.fdt` unparseable in its entirety.
|
|
164
|
+
- The demo page plays the host half of it: a switch, an editable JSON model to
|
|
165
|
+
start from, and an optional `value` per field that the page substitutes into
|
|
166
|
+
the render query for any variable bound to that field — on load and on save,
|
|
167
|
+
with each substitution logged. Values never reach the editor.
|
|
168
|
+
- `HOST_CONFIG` message and `CustomMetadataField` / `HostConfigData` protocol
|
|
169
|
+
types; `PROTOCOL_VERSION` is now 3. Additive: an app deployment that predates
|
|
170
|
+
it ignores the message, and a host that sends no model gets the previous
|
|
171
|
+
behaviour exactly. The widget delivers config on the editor's ready signal
|
|
172
|
+
(so it reaches DAM-backed embeds, which exchange no content messages) and
|
|
173
|
+
whenever the model changes.
|
|
174
|
+
|
|
175
|
+
## [0.2.0] - 2026-08-11
|
|
176
|
+
|
|
177
|
+
### Added
|
|
178
|
+
|
|
179
|
+
- `new-template` attribute / `newTemplate` prop and `createNew({ templateId?,
|
|
180
|
+
name? })` — start a template from scratch in stateless mode. The widget
|
|
181
|
+
supplies the empty document (`BLANK_TEMPLATE_XML`, exported from
|
|
182
|
+
`./protocol`), so a host with nothing stored yet needs no knowledge of the
|
|
183
|
+
`.fdt` format: the editor opens on its empty state, the user adds the first
|
|
184
|
+
layout, and the first `save` hands back a complete document to store.
|
|
185
|
+
`content` wins when both are set. Empty `content` still means "the host is
|
|
186
|
+
still fetching" and keeps the editor waiting — only the flag turns that into
|
|
187
|
+
a blank document.
|
|
188
|
+
|
|
189
|
+
### Changed
|
|
190
|
+
|
|
191
|
+
- The demo's Base URL defaults to `https://design-templates.scaleflex.com`
|
|
192
|
+
instead of `http://localhost:3000`, so the published page is usable without
|
|
193
|
+
filling a field first. It also logs the full `.fdt` and `templateQuery` to the
|
|
194
|
+
console at both crossings — going into the editor, and coming back on save —
|
|
195
|
+
while the on-page panel keeps summarizing.
|
|
196
|
+
|
|
11
197
|
## [0.1.1] - 2026-08-10
|
|
12
198
|
|
|
13
199
|
First published release. `<sfx-template-builder>` custom element, `./react`
|
|
14
|
-
wrapper, `./define` registration, and the v2
|
|
15
|
-
|
|
200
|
+
wrapper, `./define` registration, and the v2 host protocol (DAM-backed and
|
|
201
|
+
stateless modes). 0.1.0 was never published.
|
|
16
202
|
|
|
17
203
|
### Added
|
|
18
204
|
|
|
19
|
-
- `sec-template` attribute — authenticate with a
|
|
205
|
+
- `sec-template` attribute — authenticate with a Scaleflex security template
|
|
20
206
|
instead of a Hub session. The app exchanges the key for an access key itself
|
|
21
207
|
and renews it on expiry. Stateless mode only, and Hub-project features
|
|
22
208
|
(metadata fields, regional variants, project branding) come back empty; see
|
|
@@ -36,7 +222,7 @@ and stateless modes). 0.1.0 was never published.
|
|
|
36
222
|
parse, rather than leaving the host on a loading state indefinitely.
|
|
37
223
|
|
|
38
224
|
- Buildable demo (`build:demo` → `demo-dist/`) and a `release` script wrapping
|
|
39
|
-
the shared
|
|
225
|
+
the shared Scaleflex CDN release pipeline. The demo fetches its template over
|
|
40
226
|
HTTP from a bundled sample, so the "XML comes from your API, goes in, comes
|
|
41
227
|
back out" round trip is visible end to end with no backend.
|
|
42
228
|
- `LICENSE` (proprietary), shipped in the npm tarball.
|