@scaleflex/template-builder 0.1.1
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 +356 -0
- package/CHANGELOG.md +66 -0
- package/LICENSE +50 -0
- package/README.md +775 -0
- package/dist/define.cjs +2 -0
- package/dist/define.cjs.map +1 -0
- package/dist/define.d.ts +2 -0
- package/dist/define.js +6 -0
- package/dist/define.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +237 -0
- package/dist/react.cjs +2 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.ts +95 -0
- package/dist/react.js +59 -0
- package/dist/react.js.map +1 -0
- package/dist/template-builder-CSyPZni9.cjs +52 -0
- package/dist/template-builder-CSyPZni9.cjs.map +1 -0
- package/dist/template-builder-S33H_d5T.js +354 -0
- package/dist/template-builder-S33H_d5T.js.map +1 -0
- package/dist/template-builder.d.ts +206 -0
- package/package.json +75 -0
- package/src/define.ts +10 -0
- package/src/index.ts +9 -0
- package/src/protocol.ts +298 -0
- package/src/react.ts +223 -0
- package/src/template-builder.ts +599 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: integrate-template-builder
|
|
3
|
+
description: Integrate @scaleflex/template-builder into any project — install, choose
|
|
4
|
+
DAM-backed or stateless storage, wire the save round trip, theme, and register the
|
|
5
|
+
embedding origin. Works with vanilla JS, React, Vue, Angular, Svelte.
|
|
6
|
+
user_invocable: true
|
|
7
|
+
metadata:
|
|
8
|
+
category: integration
|
|
9
|
+
tags:
|
|
10
|
+
- scaleflex
|
|
11
|
+
- filerobot
|
|
12
|
+
- template-builder
|
|
13
|
+
- design-templates
|
|
14
|
+
- web-component
|
|
15
|
+
status: ready
|
|
16
|
+
version: 1
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Scaleflex Template Builder Integration Skill
|
|
20
|
+
|
|
21
|
+
## When to Use
|
|
22
|
+
|
|
23
|
+
- User says "add the template builder", "embed the design template editor",
|
|
24
|
+
"let users edit templates in our app"
|
|
25
|
+
- User asks how to use `@scaleflex/template-builder` in their project
|
|
26
|
+
- User wants a banner / creative editor backed by Filerobot rendering
|
|
27
|
+
|
|
28
|
+
## Step 1 — Decide who stores the template
|
|
29
|
+
|
|
30
|
+
This is the first question, and it changes everything downstream. Ask if it is
|
|
31
|
+
not already clear from the project.
|
|
32
|
+
|
|
33
|
+
| | **DAM-backed** (default) | **Stateless** |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Template lives in | Filerobot, as a `.fdt` file | The host's own database |
|
|
36
|
+
| `template-id` is | a Filerobot file uuid | any string the host chooses |
|
|
37
|
+
| Save does | uploads a new version, returns `{ uuid }` | hands back `{ content }` for the host to store |
|
|
38
|
+
| Needs per-user Scaleflex identities | yes | no — one service tenant is enough |
|
|
39
|
+
|
|
40
|
+
Pick **stateless** when the host already has its own users, permissions and
|
|
41
|
+
storage and does not want to map them onto Scaleflex tenants. Pick
|
|
42
|
+
**DAM-backed** when templates are a Filerobot asset like any other.
|
|
43
|
+
|
|
44
|
+
Statelessness applies to the **document only**. The editor still needs an
|
|
45
|
+
authenticated Filerobot tenant for server-side text rendering, custom fonts,
|
|
46
|
+
asset browsing, and metadata variables.
|
|
47
|
+
|
|
48
|
+
## Step 2 — Detect the target framework
|
|
49
|
+
|
|
50
|
+
Read the project's `package.json`:
|
|
51
|
+
|
|
52
|
+
- **React** (18+): use `@scaleflex/template-builder/react`
|
|
53
|
+
- **Vue / Angular / Svelte / vanilla JS**: use `@scaleflex/template-builder/define`
|
|
54
|
+
|
|
55
|
+
## Step 3 — Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm i @scaleflex/template-builder
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or via CDN (self-registering, Lit bundled in — pin the major):
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<script type="module" src="https://cdn.scaleflex.com/plugins/scaleflex/template-builder/0.1.0/template-builder.min.js"></script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**SSR caution:** the element extends `HTMLElement`, so importing `.`,
|
|
68
|
+
`./define` or `./react` from a server-rendered module throws. In Next.js use
|
|
69
|
+
`dynamic(() => import(...), { ssr: false })`; in Nuxt use `<client-only>`.
|
|
70
|
+
|
|
71
|
+
## Step 4 — Add the builder
|
|
72
|
+
|
|
73
|
+
Size the element — in `inline` mode it fills the box it is given, and a
|
|
74
|
+
zero-height parent renders nothing.
|
|
75
|
+
|
|
76
|
+
### Stateless (host owns the template)
|
|
77
|
+
|
|
78
|
+
The flow is always these three steps: **fetch the XML from your API, pass it
|
|
79
|
+
in, take the edited XML back out.**
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<sfx-template-builder
|
|
83
|
+
stateless
|
|
84
|
+
base-url="https://<deployment>"
|
|
85
|
+
token="FILEROBOT_TOKEN"
|
|
86
|
+
sass-key="SASS_KEY"
|
|
87
|
+
session-uuid="SESSION_UUID"
|
|
88
|
+
style="display:block;height:800px"
|
|
89
|
+
></sfx-template-builder>
|
|
90
|
+
|
|
91
|
+
<script type="module">
|
|
92
|
+
import '@scaleflex/template-builder/define'
|
|
93
|
+
|
|
94
|
+
const builder = document.querySelector('sfx-template-builder')
|
|
95
|
+
const id = 'your-own-id-42'
|
|
96
|
+
|
|
97
|
+
// 1 — get the template XML from your API
|
|
98
|
+
const { content, name } = await fetch(`/api/templates/${id}`).then((r) => r.json())
|
|
99
|
+
|
|
100
|
+
// 2 — pass it in. `content` is a property, never an attribute: templates
|
|
101
|
+
// routinely exceed what fits in markup or a URL.
|
|
102
|
+
builder.load({ templateId: id, name, content })
|
|
103
|
+
|
|
104
|
+
// 3 — take the edited template back out and store it
|
|
105
|
+
builder.addEventListener('save', async (e) => {
|
|
106
|
+
const { templateId, content, name, templateQuery } = e.detail
|
|
107
|
+
const ok = await fetch(`/api/templates/${templateId}`, {
|
|
108
|
+
method: 'PUT',
|
|
109
|
+
headers: { 'content-type': 'application/json' },
|
|
110
|
+
body: JSON.stringify({ content, name, templateQuery }),
|
|
111
|
+
}).then((r) => r.ok)
|
|
112
|
+
|
|
113
|
+
// Reporting the outcome is not optional in practice — see Step 5.
|
|
114
|
+
builder.confirmSave(ok)
|
|
115
|
+
})
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Stateless, React
|
|
120
|
+
|
|
121
|
+
`content` is a prop, and the outcome of `onSave` is acked automatically:
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { TemplateBuilder } from '@scaleflex/template-builder/react'
|
|
125
|
+
|
|
126
|
+
<TemplateBuilder
|
|
127
|
+
stateless
|
|
128
|
+
baseUrl="https://<deployment>"
|
|
129
|
+
token={token}
|
|
130
|
+
sassKey={sassKey}
|
|
131
|
+
sessionUuid={sessionUuid}
|
|
132
|
+
templateId={id}
|
|
133
|
+
content={xml}
|
|
134
|
+
templateName={name}
|
|
135
|
+
style={{ height: 800 }}
|
|
136
|
+
onSave={async (data) => {
|
|
137
|
+
const res = await fetch(`/api/templates/${data.templateId}`, {
|
|
138
|
+
method: 'PUT',
|
|
139
|
+
headers: { 'content-type': 'application/json' },
|
|
140
|
+
body: JSON.stringify(data),
|
|
141
|
+
})
|
|
142
|
+
return res.ok // false → the editor restores its unsaved-changes flag
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### DAM-backed
|
|
148
|
+
|
|
149
|
+
`template-id` is a Filerobot file uuid; omit it to open the new-template flow.
|
|
150
|
+
The app loads and saves the file itself.
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<sfx-template-builder
|
|
154
|
+
base-url="https://<deployment>"
|
|
155
|
+
token="FILEROBOT_TOKEN"
|
|
156
|
+
sass-key="SASS_KEY"
|
|
157
|
+
session-uuid="SESSION_UUID"
|
|
158
|
+
template-id="TEMPLATE_UUID"
|
|
159
|
+
style="display:block;height:800px"
|
|
160
|
+
></sfx-template-builder>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`save` then carries `{ uuid, name }` — the uuid of the version just written.
|
|
164
|
+
|
|
165
|
+
## Step 5 — Handle the save outcome (stateless only)
|
|
166
|
+
|
|
167
|
+
The editor clears its unsaved-changes state as soon as it posts `save`, because
|
|
168
|
+
a delivered postMessage says nothing about whether the host stored anything.
|
|
169
|
+
**Call `confirmSave(false)` when your write fails**, or a failed save silently
|
|
170
|
+
looks successful and the user loses work:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
try {
|
|
174
|
+
await yourApi.save(...)
|
|
175
|
+
builder.confirmSave(true)
|
|
176
|
+
} catch {
|
|
177
|
+
builder.confirmSave(false, 'Could not save — please try again.')
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The React wrapper does this from what `onSave` returns or throws.
|
|
182
|
+
|
|
183
|
+
## Step 6 — Guard against losing edits
|
|
184
|
+
|
|
185
|
+
Assigning a new `content` / `templateId` / `templateName` / `templateQuery`
|
|
186
|
+
reloads the editor and
|
|
187
|
+
**discards unsaved edits without prompting** — the host is authoritative. Watch
|
|
188
|
+
`dirtychange` (or read `isDirty`) before swapping:
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
builder.addEventListener('dirtychange', (e) => {
|
|
192
|
+
hasUnsavedEdits = e.detail.isDirty
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Re-assigning an identical template is a no-op, so an unrelated re-render cannot
|
|
197
|
+
destroy work by accident.
|
|
198
|
+
|
|
199
|
+
## Step 7 — Authentication
|
|
200
|
+
|
|
201
|
+
Two credentials work. Both are minted **server-side** and injected into the
|
|
202
|
+
page; neither belongs in a public bundle.
|
|
203
|
+
|
|
204
|
+
**Hub session** — `token` + `sass-key` + `session-uuid`. Full features, both
|
|
205
|
+
storage modes. Issue short-lived per-user sessions; never put a long-lived
|
|
206
|
+
master credential in client-side code.
|
|
207
|
+
|
|
208
|
+
**Security template** — `token` + `sec-template`, no Hub account involved. Use
|
|
209
|
+
it when the host has no Scaleflex identity to hand over per user:
|
|
210
|
+
|
|
211
|
+
```html
|
|
212
|
+
<sfx-template-builder
|
|
213
|
+
base-url="https://<deployment>"
|
|
214
|
+
token="FILEROBOT_TOKEN"
|
|
215
|
+
sec-template="SEC_TEMPLATE_KEY"
|
|
216
|
+
stateless
|
|
217
|
+
></sfx-template-builder>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The app exchanges the key for a short-lived access key and renews it on expiry.
|
|
221
|
+
What it costs:
|
|
222
|
+
|
|
223
|
+
- **`stateless` is required.** Without it the widget reports `error` with code
|
|
224
|
+
`invalid-config` and never mounts. The DAM-backed editor keeps needing a
|
|
225
|
+
session.
|
|
226
|
+
- **Hub-project features come back empty** — metadata fields, regional variants,
|
|
227
|
+
dynamic fields, project branding. Theme with `brand-color` / `theme` instead.
|
|
228
|
+
- **Scope the template to what the editor needs**: `LIST` on browsable folders,
|
|
229
|
+
plus `LIST` + `UPLOAD` on `/.studio/fonts*` for custom fonts. Prefer short
|
|
230
|
+
TTLs — expiry is handled, over-permission is not.
|
|
231
|
+
|
|
232
|
+
In stateless mode a single service tenant is usually right either way: one
|
|
233
|
+
machine credential, with all per-user permission logic staying in the host app.
|
|
234
|
+
|
|
235
|
+
## Step 8 — Register the embedding origin (required)
|
|
236
|
+
|
|
237
|
+
The host page's origin must be in the deployment's `frame-ancestors` allowlist,
|
|
238
|
+
via `NEXT_PUBLIC_TRUSTED_HUB_ORIGINS` on the `design-templates-app` deployment.
|
|
239
|
+
This is baked in at build time, so it needs a redeploy.
|
|
240
|
+
|
|
241
|
+
**If this is missed**, the browser refuses to render the iframe and the widget
|
|
242
|
+
reports `error` with code `handshake-timeout`. That is the single most common
|
|
243
|
+
first-integration failure — check it before anything else.
|
|
244
|
+
|
|
245
|
+
## Step 9 — Theming (optional)
|
|
246
|
+
|
|
247
|
+
```html
|
|
248
|
+
<sfx-template-builder brand-color="#FF6600" theme="dark" ...>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- `brand-color` must be `#rgb` / `#rrggbb`; anything else is ignored and the
|
|
252
|
+
Scaleflex accent is kept.
|
|
253
|
+
- Label colour on top of the brand is chosen automatically by contrast.
|
|
254
|
+
- Pick a colour readable on white: one accent token serves both filled surfaces
|
|
255
|
+
and link text, so a very pale brand gives good buttons and weak links.
|
|
256
|
+
- `theme` is `light` / `dark` / `auto`.
|
|
257
|
+
|
|
258
|
+
Themes the **editor chrome only** — colours inside the design live in the
|
|
259
|
+
template document.
|
|
260
|
+
|
|
261
|
+
## Attributes Reference
|
|
262
|
+
|
|
263
|
+
| Attribute / property | Required | Description |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| `base-url` / `baseUrl` | yes | Origin of the design-templates-app deployment |
|
|
266
|
+
| `token` | yes | Filerobot token (`ftoken`) |
|
|
267
|
+
| `sass-key` / `sassKey` | session auth | Project sass key |
|
|
268
|
+
| `session-uuid` / `sessionUuid` | session auth | Hub session uuid |
|
|
269
|
+
| `sec-template` / `secTemplate` | guest auth | Security-template key, instead of sass key + session uuid. Stateless only (Step 7) |
|
|
270
|
+
| `company-uuid`, `project-uuid` | no | Company / project scoping (session auth only) |
|
|
271
|
+
| `template-id` / `templateId` | no | DAM: Filerobot uuid. Stateless: opaque host id |
|
|
272
|
+
| `mode` | no | `inline` (default) or `modal` (starts closed — call `open()`) |
|
|
273
|
+
| `stateless` | no | Host owns the document; requires `content` |
|
|
274
|
+
| `content` (property only) | stateless | Template as `.fdt` XML |
|
|
275
|
+
| `template-name` / `templateName` | no | Stateless: header title |
|
|
276
|
+
| `template-query` / `templateQuery` | no | Stateless: the render to open on — the `templateQuery` from the last save. Empty uses the XML's `default=` values. |
|
|
277
|
+
| `brand-color`, `theme` | no | See Step 9 |
|
|
278
|
+
| `ready-timeout` / `readyTimeout` | no | Ms before `handshake-timeout` (default 20000) |
|
|
279
|
+
|
|
280
|
+
## Events Reference
|
|
281
|
+
|
|
282
|
+
| Event | `detail` |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `ready` | — (editor mounted, auth valid) |
|
|
285
|
+
| `open` | — |
|
|
286
|
+
| `save` | `{ uuid, name }` (DAM) or `{ templateId, content, name, templateQuery }` (stateless) |
|
|
287
|
+
| `dirtychange` | `{ isDirty }` (stateless) |
|
|
288
|
+
| `close` | — (user left the editor, or it unmounted) |
|
|
289
|
+
| `error` | `{ code, message? }` |
|
|
290
|
+
|
|
291
|
+
`error` codes: `auth`, `invalid-content`, `invalid-config`,
|
|
292
|
+
`handshake-timeout`, `invalid-base-url`, `unknown`.
|
|
293
|
+
|
|
294
|
+
## Public Methods
|
|
295
|
+
|
|
296
|
+
`open(templateId?)`, `close()`, `load({ content, templateId?, name? })`,
|
|
297
|
+
`confirmSave(ok, message?)`. Read-only: `status`, `isDirty`.
|
|
298
|
+
|
|
299
|
+
In React these are reached through a forwarded ref — **required for
|
|
300
|
+
`mode="modal"`**, which renders nothing until `open()` is called:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
const builder = useRef<SfxTemplateBuilder>(null)
|
|
304
|
+
<TemplateBuilder ref={builder} mode="modal" ... />
|
|
305
|
+
<button onClick={() => builder.current?.open()}>Edit</button>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## About `templateQuery`
|
|
309
|
+
|
|
310
|
+
`save` returns a `templateQuery` next to the content — the query string that
|
|
311
|
+
renders the template at its defaults (layout, variable values, locale):
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
https://<tenant>.filerobot.com/<path>/<template>.fdt?<templateQuery>&force_format=png
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Persist it alongside `content`, then pass it back on the next load:
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
builder.load({ templateId, name, content, templateQuery })
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Omitting it opens the template on the `default=` values in the XML, which is a
|
|
324
|
+
different render whenever the query overrode any of them. It is applied as
|
|
325
|
+
display state — layout and variable values — and does not mark the document
|
|
326
|
+
dirty. Entries naming a variable or layout the document no longer defines are
|
|
327
|
+
ignored, so a stale query still opens.
|
|
328
|
+
|
|
329
|
+
Inbound limits, where the preview can differ from the CDN render of the same
|
|
330
|
+
query: `$locale` is not applied (seeded from Hub project info, which guest
|
|
331
|
+
embeds lack); an explicitly empty value (`$headline=`) falls back to the XML
|
|
332
|
+
default instead of clearing — omit the key rather than sending it blank;
|
|
333
|
+
metadata-sourced variables keep the query's value under a guest session but are
|
|
334
|
+
re-resolved from the linked asset under a Hub session; `$layout_color` is
|
|
335
|
+
ignored; and `layout` / `locale` / `force_format` are reserved slugs.
|
|
336
|
+
|
|
337
|
+
## Troubleshooting
|
|
338
|
+
|
|
339
|
+
| Symptom | Cause |
|
|
340
|
+
|---|---|
|
|
341
|
+
| `error` code `handshake-timeout`, blank frame | Host origin not in `frame-ancestors` (Step 8), or third-party cookies blocked |
|
|
342
|
+
| `error` code `auth` | Bad/expired session credentials, a rejected security template, or cookies blocked |
|
|
343
|
+
| `error` code `invalid-content` | The `content` handed over is not parseable `.fdt` XML |
|
|
344
|
+
| `error` code `invalid-config` | `sec-template` without `stateless` (Step 7) |
|
|
345
|
+
| Metadata / regional / branding panels empty | Expected under `sec-template` — those come from the Hub project (Step 7) |
|
|
346
|
+
| Nothing renders, no events | `inline` mode inside a zero-height parent — size the element |
|
|
347
|
+
| Modal never appears | `open()` never called; in React that needs a ref |
|
|
348
|
+
| Saves look successful but nothing is stored | `confirmSave(false)` not wired (Step 5) |
|
|
349
|
+
| Images in the template fail to render | Their host is not in the deployment's `RENDER_ALLOWED_HOSTS_EXTRA` |
|
|
350
|
+
|
|
351
|
+
## Browser Support
|
|
352
|
+
|
|
353
|
+
Chrome/Edge 114+, Firefox 131+, Safari 18.4+. The floors come from partitioned
|
|
354
|
+
cookies (CHIPS), which cross-site embedding depends on — not from web
|
|
355
|
+
components. Older browsers that block third-party cookies fail with `auth` /
|
|
356
|
+
`handshake-timeout`.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@scaleflex/template-builder` are documented here. The
|
|
4
|
+
format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
5
|
+
the package follows [semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
Protocol message *values* are wire format: an existing string is never changed,
|
|
8
|
+
only new messages added, so an older widget keeps working against a newer app
|
|
9
|
+
deployment and vice versa.
|
|
10
|
+
|
|
11
|
+
## [0.1.1] - 2026-08-10
|
|
12
|
+
|
|
13
|
+
First published release. `<sfx-template-builder>` custom element, `./react`
|
|
14
|
+
wrapper, `./define` registration, and the v2 postMessage protocol (DAM-backed
|
|
15
|
+
and stateless modes). 0.1.0 was never published.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `sec-template` attribute — authenticate with a Filerobot security template
|
|
20
|
+
instead of a Hub session. The app exchanges the key for an access key itself
|
|
21
|
+
and renews it on expiry. Stateless mode only, and Hub-project features
|
|
22
|
+
(metadata fields, regional variants, project branding) come back empty; see
|
|
23
|
+
*Authentication* in the README. In React the credential props are a
|
|
24
|
+
discriminated union, so mixing the two modes is a type error.
|
|
25
|
+
- `invalid-config` error code — reported when attributes contradict each other,
|
|
26
|
+
currently `sec-template` without `stateless`.
|
|
27
|
+
- `AUTH_MODES` / `AuthMode` protocol exports, and `EMBED_PARAMS.SEC_TEMPLATE`.
|
|
28
|
+
- `brand-color` and `theme` attributes — restyle the editor chrome from a single
|
|
29
|
+
accent colour and pick the colour scheme. Themes the editor UI only; template
|
|
30
|
+
colours live in the document.
|
|
31
|
+
- `confirmSave(ok, message?)` and the `HOST_SAVED` protocol message. Stateless
|
|
32
|
+
hosts can report a failed write, and the editor restores its unsaved-changes
|
|
33
|
+
flag instead of showing the template as saved. The React wrapper reports it
|
|
34
|
+
automatically from what `onSave` returns or throws.
|
|
35
|
+
- `invalid-content` error code — the app now reports a template it could not
|
|
36
|
+
parse, rather than leaving the host on a loading state indefinitely.
|
|
37
|
+
|
|
38
|
+
- Buildable demo (`build:demo` → `demo-dist/`) and a `release` script wrapping
|
|
39
|
+
the shared Filerobot CDN release pipeline. The demo fetches its template over
|
|
40
|
+
HTTP from a bundled sample, so the "XML comes from your API, goes in, comes
|
|
41
|
+
back out" round trip is visible end to end with no backend.
|
|
42
|
+
- `LICENSE` (proprietary), shipped in the npm tarball.
|
|
43
|
+
- `.claude/skills/integrate-template-builder` — an integration skill shipped in
|
|
44
|
+
the package, matching `@scaleflex/asset-picker`.
|
|
45
|
+
- End-to-end coverage of the embed boundary (`e2e/embed-widget.spec.ts` in the
|
|
46
|
+
app repo): a cross-origin host page loads the built CDN bundle, fetches
|
|
47
|
+
template XML from its own API, and gets the edited document back on save.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- CDN bundle moved from `dist/cdn/template-builder.js` to
|
|
52
|
+
`dist-cdn/template-builder.min.js`, matching `@scaleflex/asset-picker`. It no
|
|
53
|
+
longer ships inside the npm tarball, where it duplicated the library and Lit.
|
|
54
|
+
- `build` now produces only the npm artifact; use `build:cdn` / `build:all`.
|
|
55
|
+
- `license` corrected from `MIT` to `SEE LICENSE IN LICENSE`.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- The React wrapper kept its ref private, so `open()`, `close()`, `load()` and
|
|
60
|
+
`confirmSave()` were unreachable — which made `mode="modal"` unusable from
|
|
61
|
+
React, since nothing renders until `open()` is called. It now forwards a ref
|
|
62
|
+
to the element.
|
|
63
|
+
- An unparseable `base-url` emitted `error` on every render pass instead of once.
|
|
64
|
+
- The editor's close button navigated an embedded editor to the Scaleflex
|
|
65
|
+
dashboard inside the host page. It now posts `builder:close` and leaves the
|
|
66
|
+
decision to the embedder.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Scaleflex SAS. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
NOTICE: This software and associated documentation files (the "Software") are
|
|
6
|
+
the exclusive property of Scaleflex SAS. The Software is protected by copyright
|
|
7
|
+
laws, international treaties, and other intellectual property laws.
|
|
8
|
+
|
|
9
|
+
RESTRICTIONS
|
|
10
|
+
|
|
11
|
+
You may NOT, without prior written permission from Scaleflex SAS:
|
|
12
|
+
|
|
13
|
+
1. Copy, reproduce, or duplicate the Software, in whole or in part.
|
|
14
|
+
2. Modify, adapt, translate, reverse engineer, decompile, or disassemble the
|
|
15
|
+
Software, or create derivative works based on the Software.
|
|
16
|
+
3. Distribute, sublicense, lease, rent, loan, sell, or otherwise transfer the
|
|
17
|
+
Software or any rights therein to any third party.
|
|
18
|
+
4. Remove, alter, or obscure any proprietary notices, labels, or marks on the
|
|
19
|
+
Software.
|
|
20
|
+
5. Use the Software for any purpose other than as expressly authorised under
|
|
21
|
+
a separate written licence agreement with Scaleflex SAS.
|
|
22
|
+
|
|
23
|
+
GRANT OF LICENCE
|
|
24
|
+
|
|
25
|
+
Use of this Software is permitted only under a separate written licence
|
|
26
|
+
agreement between the licensee and Scaleflex SAS. Installing or downloading
|
|
27
|
+
this Software does not, by itself, grant any licence or right to use the
|
|
28
|
+
Software.
|
|
29
|
+
|
|
30
|
+
NO WARRANTY
|
|
31
|
+
|
|
32
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
33
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
34
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
35
|
+
SCALEFLEX SAS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
|
|
36
|
+
AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
|
|
37
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
38
|
+
|
|
39
|
+
ENFORCEMENT
|
|
40
|
+
|
|
41
|
+
Unauthorised use, reproduction, or distribution of this Software, or any
|
|
42
|
+
portion thereof, may result in severe civil and criminal penalties and will be
|
|
43
|
+
prosecuted to the maximum extent permitted by applicable law.
|
|
44
|
+
|
|
45
|
+
CONTACT
|
|
46
|
+
|
|
47
|
+
For licensing enquiries:
|
|
48
|
+
Scaleflex SAS
|
|
49
|
+
Email: sales@scaleflex.com
|
|
50
|
+
Web: https://www.scaleflex.com
|