claude-artifact-framework 0.2.1 → 0.3.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 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.2.0/dist/index.global.js"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.3.0/dist/index.global.js"></script>
16
16
  <script>
17
17
  const { storage, createStore, createPersistedStore, createSharedStore } = ArtifactKit;
18
18
  </script>
@@ -27,13 +27,102 @@ mirrors npm packages automatically.
27
27
  createStore,
28
28
  createPersistedStore,
29
29
  createSharedStore,
30
- } from "https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.2.0/dist/index.esm.js";
30
+ } from "https://cdn.jsdelivr.net/npm/claude-artifact-framework@0.3.0/dist/index.esm.js";
31
31
  </script>
32
32
  ```
33
33
 
34
- Pin a version (`@0.2.0`) for reproducible artifacts, or drop the version
34
+ Pin a version (`@0.3.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
+
37
126
  ## `storage`
38
127
 
39
128
  Thin wrapper over `window.storage`, the persistence API Claude injects into a