claude-artifact-framework 0.6.0 → 0.7.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/README.md +20 -5
- package/dist/index.esm.js +16 -4
- package/dist/index.esm.js.map +3 -3
- package/dist/index.global.js +7 -7
- package/dist/index.global.js.map +3 -3
- package/package.json +1 -1
- package/src/app.js +5 -0
- package/src/blocks.js +13 -4
- package/src/records.js +2 -0
- package/src/steps.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-artifact-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Utilities for building apps inside Claude Artifacts: platform storage, chat tool-calling loops, and other primitives verified against the real runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.esm.js",
|
package/src/app.js
CHANGED
|
@@ -36,6 +36,11 @@ function checkFields(fields, where) {
|
|
|
36
36
|
`claude-artifact-framework: field "${f.key}" in ${where} has unknown type "${f.type}". Valid types: ${FIELD_TYPES.join(", ")}.`
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
|
+
if (f.type === "select" && f.options !== undefined && !Array.isArray(f.options) && typeof f.options !== "function") {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`claude-artifact-framework: field "${f.key}" in ${where}: \`options\` must be an array or a function of data returning one.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
|
package/src/blocks.js
CHANGED
|
@@ -49,7 +49,7 @@ export function validateField(field, value) {
|
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export function Field({ field, value, onChange }) {
|
|
52
|
+
export function Field({ field, value, onChange, data }) {
|
|
53
53
|
const error = validateField(field, value);
|
|
54
54
|
const id = `caf-f-${field.key}`;
|
|
55
55
|
const numeric = field.type === "number" || field.type === "money" || field.type === "percent";
|
|
@@ -79,11 +79,14 @@ export function Field({ field, value, onChange }) {
|
|
|
79
79
|
if (field.type === "textarea") {
|
|
80
80
|
control = h("textarea", { ...common, rows: field.rows || 3 });
|
|
81
81
|
} else if (field.type === "select") {
|
|
82
|
+
// Options may be declared as a function of the app data, for selects
|
|
83
|
+
// whose choices come from what the user has entered elsewhere.
|
|
84
|
+
const options = typeof field.options === "function" ? field.options(data) || [] : field.options || [];
|
|
82
85
|
control = h(
|
|
83
86
|
"select",
|
|
84
87
|
common,
|
|
85
88
|
h("option", { value: "" }, field.placeholder || "Select…"),
|
|
86
|
-
|
|
89
|
+
options.map((opt) => {
|
|
87
90
|
const val = typeof opt === "string" ? opt : opt.value;
|
|
88
91
|
const label = typeof opt === "string" ? opt : opt.label;
|
|
89
92
|
return h("option", { key: val, value: val }, label);
|
|
@@ -126,6 +129,7 @@ export function FieldsBlock({ spec, ctx }) {
|
|
|
126
129
|
h(Field, {
|
|
127
130
|
key: field.key,
|
|
128
131
|
field,
|
|
132
|
+
data: ctx.data,
|
|
129
133
|
value: ctx.data[field.key],
|
|
130
134
|
onChange: (v) => ctx.update((d) => { d[field.key] = v; }),
|
|
131
135
|
})
|
|
@@ -170,13 +174,18 @@ export function TextBlock({ spec, ctx }) {
|
|
|
170
174
|
|
|
171
175
|
export function ListBlock({ spec, ctx }) {
|
|
172
176
|
const rows = (typeof spec.list === "function" ? spec.list(ctx.data) : spec.list) || [];
|
|
177
|
+
// `title` follows the same convention as every other block: a string is the
|
|
178
|
+
// block heading. A function is the per-row title. Both are supported —
|
|
179
|
+
// making list the odd one out was an API inconsistency a blind test caught.
|
|
180
|
+
const heading = typeof spec.title === "string" ? spec.title : null;
|
|
181
|
+
const rowTitle = typeof spec.title === "function" ? spec.title : (r) => r.title ?? r.name ?? r.label ?? r.text ?? String(r);
|
|
173
182
|
if (!rows.length) {
|
|
174
183
|
return h("div", { className: "caf-block caf-empty" }, spec.empty || "Nothing here yet.");
|
|
175
184
|
}
|
|
176
|
-
const title = spec.title || ((r) => r.title ?? r.name ?? r.label ?? String(r));
|
|
177
185
|
return h(
|
|
178
186
|
"div",
|
|
179
187
|
{ className: "caf-block caf-list" },
|
|
188
|
+
heading ? h("h2", { className: "caf-block-title" }, heading) : null,
|
|
180
189
|
rows.map((row, i) =>
|
|
181
190
|
h(
|
|
182
191
|
"button",
|
|
@@ -186,7 +195,7 @@ export function ListBlock({ spec, ctx }) {
|
|
|
186
195
|
type: "button",
|
|
187
196
|
onClick: spec.onSelect ? () => spec.onSelect(row, ctx) : undefined,
|
|
188
197
|
},
|
|
189
|
-
h("span", { className: "caf-row-title" },
|
|
198
|
+
h("span", { className: "caf-row-title" }, rowTitle(row)),
|
|
190
199
|
spec.subtitle ? h("span", { className: "caf-row-sub" }, spec.subtitle(row)) : null
|
|
191
200
|
)
|
|
192
201
|
)
|
package/src/records.js
CHANGED
|
@@ -199,6 +199,7 @@ function DetailScreen({ spec, ctx, record }) {
|
|
|
199
199
|
h(Field, {
|
|
200
200
|
key: field.key,
|
|
201
201
|
field,
|
|
202
|
+
data: ctx.data,
|
|
202
203
|
value: record[field.key],
|
|
203
204
|
onChange: (v) =>
|
|
204
205
|
ctx.update((d) => {
|
|
@@ -272,6 +273,7 @@ function ItemsEditor({ spec, ctx, record }) {
|
|
|
272
273
|
h(Field, {
|
|
273
274
|
key: field.key,
|
|
274
275
|
field,
|
|
276
|
+
data: ctx.data,
|
|
275
277
|
value: item[field.key],
|
|
276
278
|
onChange: (v) =>
|
|
277
279
|
mutate((rec) => {
|