claude-artifact-framework 0.5.0 → 0.5.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/README.md +40 -3
- package/dist/index.esm.js +61 -26
- package/dist/index.esm.js.map +2 -2
- package/dist/index.global.js +10 -9
- package/dist/index.global.js.map +3 -3
- package/package.json +1 -1
- package/src/app.js +76 -29
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ mirrors npm packages automatically.
|
|
|
12
12
|
### As a global script (`<script>` tag)
|
|
13
13
|
|
|
14
14
|
```html
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.5.
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.5.1/dist/index.global.js"></script>
|
|
16
16
|
<script>
|
|
17
17
|
const { storage, createStore, createPersistedStore, createSharedStore } = ArtifactKit;
|
|
18
18
|
</script>
|
|
@@ -27,11 +27,11 @@ mirrors npm packages automatically.
|
|
|
27
27
|
createStore,
|
|
28
28
|
createPersistedStore,
|
|
29
29
|
createSharedStore,
|
|
30
|
-
} from "https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.5.
|
|
30
|
+
} from "https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.5.1/dist/index.esm.js";
|
|
31
31
|
</script>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Pin a version (`@0.5.
|
|
34
|
+
Pin a version (`@0.5.1`) for reproducible artifacts, or drop the version
|
|
35
35
|
(`claude-artifact-framework/dist/...`) to always get the latest release.
|
|
36
36
|
|
|
37
37
|
### Inside a React artifact
|
|
@@ -254,6 +254,43 @@ ArtifactKit.createApp({
|
|
|
254
254
|
that haven't been persisted yet. View state (which screen is open) stays
|
|
255
255
|
personal per viewer.
|
|
256
256
|
|
|
257
|
+
## API reference
|
|
258
|
+
|
|
259
|
+
Everything an app can declare, in one place.
|
|
260
|
+
|
|
261
|
+
**Top-level keys**: `title`, `subtitle`, `theme: { seed }`, `layout`, `blocks`,
|
|
262
|
+
`records`, `steps`, `chat`, `data`, `shared`. Anything else throws.
|
|
263
|
+
|
|
264
|
+
**Layouts** (`layout:`): `panel` (default — grid of blocks), `records` (CRUD
|
|
265
|
+
list), `steps` (wizard), `chat` (conversation with Claude). Each requires its
|
|
266
|
+
matching key (`blocks` / `records` / `steps` / `chat`).
|
|
267
|
+
|
|
268
|
+
**Block types** (entries of `blocks`): `fields`, `output`, `text`, `list`,
|
|
269
|
+
`chart`, `timer`, `custom`. One type per entry, plus optional `title`, `empty`,
|
|
270
|
+
`wide`. In the `records` layout, `blocks` render below the list (never on the
|
|
271
|
+
detail screen); their functions receive the same `data` object as everywhere
|
|
272
|
+
else — the collection lives at `d.records`.
|
|
273
|
+
|
|
274
|
+
**Field types** (`type:` on any field): `text` (default), `textarea`,
|
|
275
|
+
`number`, `money`, `percent`, `check` (checkbox), `date`, `select` (needs
|
|
276
|
+
`options`). Other field keys: `key` (required), `label`, `value` (initial),
|
|
277
|
+
`required`, `min`, `max`, `step`, `placeholder`, `hint`, `rows`, `options`.
|
|
278
|
+
An unknown `type` throws naming the valid ones.
|
|
279
|
+
|
|
280
|
+
**`records` keys**: `label`, `fields`, `title(r)`, `subtitle(r)`, `sort(a,b)`,
|
|
281
|
+
`summary(all)`, `empty`, `search`, `compute`. `id` is framework-assigned and
|
|
282
|
+
reserved.
|
|
283
|
+
|
|
284
|
+
**`steps` keys** (per step): `title`, `text`, `fields`, `result(d)`. Field
|
|
285
|
+
keys must be unique across steps.
|
|
286
|
+
|
|
287
|
+
**`chat` keys**: `system` (string or `(d) => string`), `greeting`,
|
|
288
|
+
`placeholder`, `tools`, `model`, `maxTokens`. Each tool:
|
|
289
|
+
`{ description, input, run(input, ctx) }`.
|
|
290
|
+
|
|
291
|
+
**Formats** (`format:` on output/chart/compute items): `money`, `percent`,
|
|
292
|
+
`number`, `date`.
|
|
293
|
+
|
|
257
294
|
## `storage`
|
|
258
295
|
|
|
259
296
|
Thin wrapper over `window.storage`, the persistence API Claude injects into a
|
package/dist/index.esm.js
CHANGED
|
@@ -373,6 +373,7 @@ function seriesColors(seed, n) {
|
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
// src/blocks.js
|
|
376
|
+
var FIELD_TYPES = ["text", "textarea", "number", "money", "percent", "check", "date", "select"];
|
|
376
377
|
function formatValue(value, format) {
|
|
377
378
|
if (value === null || value === void 0 || value === "") return "\u2014";
|
|
378
379
|
const n = Number(value);
|
|
@@ -1165,6 +1166,41 @@ Continue with this information. If you have everything you need, answer in natur
|
|
|
1165
1166
|
var LAYOUTS = ["panel", "records", "steps", "chat"];
|
|
1166
1167
|
var RESERVED = ["title", "empty", "wide", "onSelect", "subtitle"];
|
|
1167
1168
|
var TOP_KEYS = ["title", "subtitle", "theme", "layout", "blocks", "records", "steps", "chat", "data", "shared"];
|
|
1169
|
+
function checkFields(fields, where) {
|
|
1170
|
+
for (const f of fields || []) {
|
|
1171
|
+
if (!f || !f.key) throw new Error(`claude-artifact-framework: every field in ${where} needs a \`key\`.`);
|
|
1172
|
+
if (f.type !== void 0 && !FIELD_TYPES.includes(f.type)) {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
`claude-artifact-framework: field "${f.key}" in ${where} has unknown type "${f.type}". Valid types: ${FIELD_TYPES.join(", ")}.`
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
function checkBlocks(blocks) {
|
|
1180
|
+
if (!Array.isArray(blocks)) {
|
|
1181
|
+
throw new Error("claude-artifact-framework: `blocks` must be an array.");
|
|
1182
|
+
}
|
|
1183
|
+
blocks.forEach((block, i) => {
|
|
1184
|
+
if (!block || typeof block !== "object") {
|
|
1185
|
+
throw new Error(`claude-artifact-framework: blocks[${i}] must be an object like { fields: [...] }.`);
|
|
1186
|
+
}
|
|
1187
|
+
const keys = Object.keys(block).filter((k) => !RESERVED.includes(k));
|
|
1188
|
+
const known = keys.filter((k) => BLOCK_NAMES.includes(k));
|
|
1189
|
+
if (known.length === 0) {
|
|
1190
|
+
throw new Error(
|
|
1191
|
+
`claude-artifact-framework: blocks[${i}] has no recognised block type (found: ${keys.join(", ") || "nothing"}). Valid block types: ${BLOCK_NAMES.join(", ")}.`
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
if (known.length > 1) {
|
|
1195
|
+
throw new Error(
|
|
1196
|
+
`claude-artifact-framework: blocks[${i}] declares more than one block type (${known.join(
|
|
1197
|
+
", "
|
|
1198
|
+
)}). Split them into separate entries of \`blocks\`.`
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
checkFields(block.fields, `blocks[${i}]`);
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1168
1204
|
function validate(spec) {
|
|
1169
1205
|
const unknownTop = Object.keys(spec).filter((k) => !TOP_KEYS.includes(k));
|
|
1170
1206
|
if (unknownTop.length) {
|
|
@@ -1198,8 +1234,8 @@ function validate(spec) {
|
|
|
1198
1234
|
if (!st.fields && !st.text && !st.result) {
|
|
1199
1235
|
throw new Error(`claude-artifact-framework: steps[${i}] needs \`fields\`, \`text\`, or \`result\`.`);
|
|
1200
1236
|
}
|
|
1237
|
+
checkFields(st.fields, `steps[${i}]`);
|
|
1201
1238
|
for (const f of st.fields || []) {
|
|
1202
|
-
if (!f || !f.key) throw new Error(`claude-artifact-framework: every field in steps[${i}] needs a \`key\`.`);
|
|
1203
1239
|
if (seen.has(f.key)) {
|
|
1204
1240
|
throw new Error(
|
|
1205
1241
|
`claude-artifact-framework: field key "${f.key}" appears in more than one step \u2014 keys share one data pool and must be unique.`
|
|
@@ -1246,13 +1282,14 @@ function validate(spec) {
|
|
|
1246
1282
|
`claude-artifact-framework: records has unknown keys (${unknownR.join(", ")}). Valid keys: ${RECORDS_KEYS.join(", ")}.`
|
|
1247
1283
|
);
|
|
1248
1284
|
}
|
|
1285
|
+
checkFields(r.fields, "records");
|
|
1249
1286
|
const seen = /* @__PURE__ */ new Set();
|
|
1250
1287
|
for (const f of r.fields) {
|
|
1251
|
-
if (!f || !f.key) throw new Error("claude-artifact-framework: every records field needs a `key`.");
|
|
1252
1288
|
if (f.key === "id") throw new Error('claude-artifact-framework: "id" is reserved on records \u2014 the framework assigns it.');
|
|
1253
1289
|
if (seen.has(f.key)) throw new Error(`claude-artifact-framework: duplicate records field key "${f.key}".`);
|
|
1254
1290
|
seen.add(f.key);
|
|
1255
1291
|
}
|
|
1292
|
+
if (spec.blocks) checkBlocks(spec.blocks);
|
|
1256
1293
|
for (const [key, c] of Object.entries(r.compute || {})) {
|
|
1257
1294
|
if (seen.has(key) || key === "id") {
|
|
1258
1295
|
throw new Error(
|
|
@@ -1267,29 +1304,7 @@ function validate(spec) {
|
|
|
1267
1304
|
}
|
|
1268
1305
|
return layout;
|
|
1269
1306
|
}
|
|
1270
|
-
|
|
1271
|
-
if (!Array.isArray(blocks)) {
|
|
1272
|
-
throw new Error("claude-artifact-framework: `blocks` must be an array.");
|
|
1273
|
-
}
|
|
1274
|
-
blocks.forEach((block, i) => {
|
|
1275
|
-
if (!block || typeof block !== "object") {
|
|
1276
|
-
throw new Error(`claude-artifact-framework: blocks[${i}] must be an object like { fields: [...] }.`);
|
|
1277
|
-
}
|
|
1278
|
-
const keys = Object.keys(block).filter((k) => !RESERVED.includes(k));
|
|
1279
|
-
const known = keys.filter((k) => BLOCK_NAMES.includes(k));
|
|
1280
|
-
if (known.length === 0) {
|
|
1281
|
-
throw new Error(
|
|
1282
|
-
`claude-artifact-framework: blocks[${i}] has no recognised block type (found: ${keys.join(", ") || "nothing"}). Valid block types: ${BLOCK_NAMES.join(", ")}.`
|
|
1283
|
-
);
|
|
1284
|
-
}
|
|
1285
|
-
if (known.length > 1) {
|
|
1286
|
-
throw new Error(
|
|
1287
|
-
`claude-artifact-framework: blocks[${i}] declares more than one block type (${known.join(
|
|
1288
|
-
", "
|
|
1289
|
-
)}). Split them into separate entries of \`blocks\`.`
|
|
1290
|
-
);
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1307
|
+
checkBlocks(spec.blocks || []);
|
|
1293
1308
|
return layout;
|
|
1294
1309
|
}
|
|
1295
1310
|
function defaultsFrom(spec) {
|
|
@@ -1353,7 +1368,26 @@ function Panel({ spec, ctx }) {
|
|
|
1353
1368
|
)
|
|
1354
1369
|
);
|
|
1355
1370
|
}
|
|
1356
|
-
|
|
1371
|
+
function RecordsWithBlocks({ spec, ctx }) {
|
|
1372
|
+
const detailOpen = ctx.view.screen === "record" && (ctx.data.records || []).some((r) => r.id === ctx.view.arg);
|
|
1373
|
+
return createElement(
|
|
1374
|
+
"div",
|
|
1375
|
+
null,
|
|
1376
|
+
createElement(RecordsLayout, { spec, ctx }),
|
|
1377
|
+
!detailOpen && Array.isArray(spec.blocks) && spec.blocks.length ? createElement(
|
|
1378
|
+
"div",
|
|
1379
|
+
{ className: "caf-panel caf-records-blocks" },
|
|
1380
|
+
spec.blocks.map(
|
|
1381
|
+
(block, i) => createElement(
|
|
1382
|
+
"section",
|
|
1383
|
+
{ key: i, className: block.wide ? "caf-slot caf-slot-wide" : "caf-slot" },
|
|
1384
|
+
createElement(Block, { block, ctx })
|
|
1385
|
+
)
|
|
1386
|
+
)
|
|
1387
|
+
) : null
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
var LAYOUT_COMPONENTS = { panel: Panel, records: RecordsWithBlocks, steps: StepsLayout, chat: ChatLayout };
|
|
1357
1391
|
function createApp(spec = {}) {
|
|
1358
1392
|
const layout = validate(spec);
|
|
1359
1393
|
const state = createAppState(defaultsFrom(spec), { shared: Boolean(spec.shared) });
|
|
@@ -1516,6 +1550,7 @@ var BASE_CSS = `
|
|
|
1516
1550
|
}
|
|
1517
1551
|
.caf-search:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
1518
1552
|
.caf-computed { border-top: 1px solid var(--caf-border); padding-top: 0.7rem; }
|
|
1553
|
+
.caf-records-blocks { margin-top: 1rem; }
|
|
1519
1554
|
.caf-empty, .caf-loading { color: var(--caf-muted); font-size: 0.9rem; text-align: center; padding: 1.6rem 1rem; }
|
|
1520
1555
|
.caf-block-error { border-color: var(--caf-danger); gap: 0.4rem; }
|
|
1521
1556
|
.caf-block-error strong { font-size: 0.9rem; color: var(--caf-danger); }
|