claude-artifact-framework 0.2.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/README.md +144 -3
- package/dist/index.esm.js +1086 -1
- package/dist/index.esm.js.map +4 -4
- package/dist/index.global.js +120 -1
- package/dist/index.global.js.map +4 -4
- package/package.json +1 -1
- package/src/app.js +337 -0
- package/src/appState.js +127 -0
- package/src/blocks.js +389 -0
- package/src/index.js +3 -0
- package/src/react-runtime.js +80 -0
- package/src/records.js +158 -0
- package/src/steps.js +106 -0
- package/src/theme.js +157 -0
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.
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.4.0/dist/index.global.js"></script>
|
|
16
16
|
<script>
|
|
17
17
|
const { storage, createStore, createPersistedStore, createSharedStore } = ArtifactKit;
|
|
18
18
|
</script>
|
|
@@ -27,13 +27,154 @@ mirrors npm packages automatically.
|
|
|
27
27
|
createStore,
|
|
28
28
|
createPersistedStore,
|
|
29
29
|
createSharedStore,
|
|
30
|
-
} from "https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.
|
|
30
|
+
} from "https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.4.0/dist/index.esm.js";
|
|
31
31
|
</script>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Pin a version (`@0.
|
|
34
|
+
Pin a version (`@0.4.0`) for reproducible artifacts, or drop the version
|
|
35
35
|
(`claude-artifact-framework/dist/...`) to always get the latest release.
|
|
36
36
|
|
|
37
|
+
### Inside a React artifact
|
|
38
|
+
|
|
39
|
+
A React artifact loads external code the same way any artifact does — by
|
|
40
|
+
injecting a classic `<script>` tag, not by importing a module URL. Hand the
|
|
41
|
+
framework the React the artifact already imported, so there is only ever one
|
|
42
|
+
React on the page:
|
|
43
|
+
|
|
44
|
+
```jsx
|
|
45
|
+
import React, { useState, useEffect } from "react";
|
|
46
|
+
|
|
47
|
+
const KIT = "https://cdn.jsdelivr.net/npm/claude-artifact-framework/dist/index.global.js";
|
|
48
|
+
|
|
49
|
+
function load(src) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
if (document.querySelector(`script[src="${src}"]`)) return resolve();
|
|
52
|
+
const tag = document.createElement("script");
|
|
53
|
+
tag.src = src;
|
|
54
|
+
tag.onload = resolve;
|
|
55
|
+
tag.onerror = reject;
|
|
56
|
+
document.head.appendChild(tag);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default function Artifact() {
|
|
61
|
+
const [App, setApp] = useState(null);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
load(KIT).then(() => {
|
|
65
|
+
ArtifactKit.useReact(React);
|
|
66
|
+
setApp(() => ArtifactKit.createApp({
|
|
67
|
+
title: "Mortgage calculator",
|
|
68
|
+
theme: { seed: "#0c8b8d" },
|
|
69
|
+
blocks: [
|
|
70
|
+
{ fields: [
|
|
71
|
+
{ key: "amount", label: "Amount", type: "money", value: 300000 },
|
|
72
|
+
{ key: "rate", label: "Rate", type: "percent", value: 5.5 },
|
|
73
|
+
{ key: "years", label: "Term", type: "number", value: 30, min: 1, max: 40 },
|
|
74
|
+
]},
|
|
75
|
+
{ output: (d) => {
|
|
76
|
+
const i = d.rate / 100 / 12, n = d.years * 12;
|
|
77
|
+
const payment = d.amount * i / (1 - Math.pow(1 + i, -n));
|
|
78
|
+
return [{ label: "Monthly payment", value: payment, format: "money", big: true }];
|
|
79
|
+
}},
|
|
80
|
+
],
|
|
81
|
+
}));
|
|
82
|
+
});
|
|
83
|
+
}, []);
|
|
84
|
+
|
|
85
|
+
return App ? <App /> : null;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`useReact` is the one thing the framework asks for, and only because a runtime
|
|
90
|
+
may keep React in a private module registry the bundle can't reach. If React is
|
|
91
|
+
already on `globalThis`, it is picked up automatically and the call is a no-op.
|
|
92
|
+
|
|
93
|
+
### The `records` layout — a full CRUD app from a declaration
|
|
94
|
+
|
|
95
|
+
For "list of things" apps (expenses, habits, notes, todos), declare what a
|
|
96
|
+
record is and the whole lifecycle — list, add, edit, delete, empty state,
|
|
97
|
+
selection, persistence — is framework-owned:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
ArtifactKit.createApp({
|
|
101
|
+
title: "Gastos",
|
|
102
|
+
theme: { seed: "#c2410c" },
|
|
103
|
+
layout: "records",
|
|
104
|
+
records: {
|
|
105
|
+
label: "expense",
|
|
106
|
+
fields: [
|
|
107
|
+
{ key: "concepto", label: "Concepto", type: "text", required: true },
|
|
108
|
+
{ key: "monto", label: "Monto", type: "money", value: 0, min: 0 },
|
|
109
|
+
{ key: "categoria", label: "Categoría", type: "select", options: ["comida", "transporte", "otros"] },
|
|
110
|
+
],
|
|
111
|
+
subtitle: (r) => r.categoria || "sin categoría",
|
|
112
|
+
summary: (all) => [
|
|
113
|
+
{ label: "Total", value: all.reduce((s, r) => s + (Number(r.monto) || 0), 0), format: "money", big: true },
|
|
114
|
+
{ label: "Gastos", value: all.length, format: "number" },
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
"Add" creates the record from field defaults and opens it (no draft object to
|
|
121
|
+
lose); detail fields edit the record in place through the same debounced
|
|
122
|
+
persistence path as everything else. `id` is assigned by the framework and
|
|
123
|
+
reserved. Optional: `title(r)` / `subtitle(r)` for rows, `sort(a, b)`,
|
|
124
|
+
`summary(records)` for stats above the list, `empty` for the empty-state text.
|
|
125
|
+
|
|
126
|
+
### The `steps` layout — wizards, quizzes, staged calculators
|
|
127
|
+
|
|
128
|
+
Declare the sequence; next/back/progress/result are framework-owned. Step
|
|
129
|
+
fields write into the same flat `data` pool, so the result step is just a
|
|
130
|
+
function of data. Next is gated on the current step's validation; the current
|
|
131
|
+
step survives a reload.
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
ArtifactKit.createApp({
|
|
135
|
+
title: "Quiz de riesgo",
|
|
136
|
+
layout: "steps",
|
|
137
|
+
steps: [
|
|
138
|
+
{ title: "Tu edad", fields: [{ key: "edad", type: "number", required: true, min: 18 }] },
|
|
139
|
+
{ title: "Tu perfil", fields: [{ key: "perfil", type: "select", required: true, options: ["conservador", "moderado", "agresivo"] }] },
|
|
140
|
+
{ title: "Resultado", result: (d) => [{ label: "Puntaje", value: score(d), big: true }] },
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Field keys must be unique across all steps (they share one data pool); a
|
|
146
|
+
duplicate throws at creation, as does an unknown step key.
|
|
147
|
+
|
|
148
|
+
### The `chart` block — bar, line, donut
|
|
149
|
+
|
|
150
|
+
Same vocabulary as `output` (items of `{ label, value }` plus an optional
|
|
151
|
+
`format`), so declaring one teaches nothing new. Colors derive from the theme
|
|
152
|
+
seed by golden-angle rotation — a chart can't clash with the app it lives in.
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
blocks: [
|
|
156
|
+
{ chart: (d) => ({ type: "bar", format: "money", items: monthlyTotals(d) }), title: "Ventas", wide: true },
|
|
157
|
+
{ chart: { type: "donut", items: [{ label: "Comida", value: 45 }, { label: "Otros", value: 55 }] } },
|
|
158
|
+
]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### The `timer` block — countdowns that persist their effects
|
|
162
|
+
|
|
163
|
+
The interval is framework-owned and cleaned up on unmount (a leaked
|
|
164
|
+
`setInterval` is a classic half-built artifact bug). The countdown itself is
|
|
165
|
+
deliberately ephemeral; what you declare is what happens when it finishes —
|
|
166
|
+
`onDone` runs through `update()`, so its effects persist like any other change.
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
blocks: [
|
|
170
|
+
{ timer: { minutes: 25, label: "Focus", onDone: (d) => { d.completados = (d.completados || 0) + 1; } } },
|
|
171
|
+
{ output: (d) => [{ label: "Pomodoros", value: d.completados || 0, big: true }] },
|
|
172
|
+
]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`text` blocks also accept a function of data — `{ text: (d) => "Hola " + d.nombre }` —
|
|
176
|
+
with the same contract as `output`.
|
|
177
|
+
|
|
37
178
|
## `storage`
|
|
38
179
|
|
|
39
180
|
Thin wrapper over `window.storage`, the persistence API Claude injects into a
|