claude-artifact-framework 0.18.0 → 0.19.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 +4 -3
- package/dist/index.esm.js +30 -13
- package/dist/index.esm.js.map +2 -2
- package/dist/index.global.js +28 -15
- package/dist/index.global.js.map +3 -3
- package/package.json +1 -1
- package/src/app.js +23 -8
- package/src/blocks.js +11 -4
- package/src/records.js +5 -1
- package/src/steps.js +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-artifact-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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
|
@@ -1170,13 +1170,15 @@ export function createApp(spec = {}) {
|
|
|
1170
1170
|
: h(
|
|
1171
1171
|
"span",
|
|
1172
1172
|
{ className: "caf-logo caf-logo-mono", "aria-hidden": true },
|
|
1173
|
+
// First letter/digit of each word — punctuation "words" like the
|
|
1174
|
+
// "-" in "Caja - Camión" must not become half the monogram.
|
|
1173
1175
|
(spec.title || "?")
|
|
1174
1176
|
.split(/\s+/)
|
|
1175
|
-
.map((w) => w[0])
|
|
1177
|
+
.map((w) => (w.match(/[\p{L}\p{N}]/u) || [])[0])
|
|
1176
1178
|
.filter(Boolean)
|
|
1177
1179
|
.slice(0, 2)
|
|
1178
1180
|
.join("")
|
|
1179
|
-
.toUpperCase()
|
|
1181
|
+
.toUpperCase() || "?"
|
|
1180
1182
|
);
|
|
1181
1183
|
|
|
1182
1184
|
return h(
|
|
@@ -1221,7 +1223,9 @@ const BASE_CSS = `
|
|
|
1221
1223
|
--caf-control-radius: 7px; /* one radius for every control: buttons, inputs, rows */
|
|
1222
1224
|
--caf-btn-pad-y: 0.45rem;
|
|
1223
1225
|
--caf-btn-pad-x: 0.8rem;
|
|
1224
|
-
--caf-num-primary: 2.
|
|
1226
|
+
--caf-num-primary: 2.6rem; /* the one size for a block's dominant number — the largest thing on screen, bigger than the H1 */
|
|
1227
|
+
--caf-accent-tint: color-mix(in srgb, var(--caf-accent) 7%, var(--caf-surface)); /* soft accent surface for the feature card */
|
|
1228
|
+
--caf-block-gap: 1.75rem; /* ONE value for air between blocks, every layout (audited: workspace used a smaller gap for the same visual role) */
|
|
1225
1229
|
box-sizing: border-box;
|
|
1226
1230
|
min-height: 100vh;
|
|
1227
1231
|
padding: 2rem 1.25rem 4rem;
|
|
@@ -1237,7 +1241,9 @@ const BASE_CSS = `
|
|
|
1237
1241
|
.caf-header h1 { margin: 0; font-size: 1.5rem; font-weight: 650; letter-spacing: -0.01em; text-wrap: balance; }
|
|
1238
1242
|
.caf-header p { margin: 0.4rem 0 0; color: var(--caf-muted); font-size: 0.95rem; line-height: 1.5; }
|
|
1239
1243
|
/* Between-card air must beat within-card padding (1.1rem), or groups dissolve. */
|
|
1240
|
-
|
|
1244
|
+
/* align-items: start — a short card must not stretch to its tall neighbor's
|
|
1245
|
+
height; the stretched version reads as dead air (audited). */
|
|
1246
|
+
.caf-panel { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--caf-block-gap) 1rem; align-items: start; }
|
|
1241
1247
|
.caf-slot { grid-column: span 2; min-width: 0; }
|
|
1242
1248
|
@media (min-width: 720px) { .caf-slot { grid-column: span 1; } .caf-slot-wide { grid-column: span 2; } }
|
|
1243
1249
|
.caf-block {
|
|
@@ -1331,7 +1337,9 @@ const BASE_CSS = `
|
|
|
1331
1337
|
.caf-timer-done { color: var(--caf-accent); }
|
|
1332
1338
|
.caf-timer-fill { transition: width 0.9s linear; }
|
|
1333
1339
|
.caf-timer-controls { display: flex; gap: 0.6rem; }
|
|
1334
|
-
|
|
1340
|
+
/* Disabled goes NEUTRAL, not pale-accent: a washed-out primary reads as
|
|
1341
|
+
broken (audited on the chat Send with an empty input). */
|
|
1342
|
+
.caf-btn:disabled { cursor: not-allowed; background: var(--caf-sunken); color: var(--caf-muted); border-color: var(--caf-border); opacity: 1; }
|
|
1335
1343
|
.caf-steps-progress { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.2rem; }
|
|
1336
1344
|
.caf-steps-result { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
|
|
1337
1345
|
.caf-steps-fill { transition: width 0.25s ease; }
|
|
@@ -1363,7 +1371,9 @@ const BASE_CSS = `
|
|
|
1363
1371
|
.caf-row-wrap { display: flex; align-items: center; gap: 0.4rem; }
|
|
1364
1372
|
.caf-row-wrap .caf-row { flex: 1; min-width: 0; }
|
|
1365
1373
|
.caf-row-actions { display: flex; gap: 0.35rem; flex: none; padding-right: 0.3rem; }
|
|
1366
|
-
|
|
1374
|
+
/* min-width keeps row actions' left edges aligned across rows even when
|
|
1375
|
+
labels differ ("Hecho" vs "Desmarcar" — audited misalignment). */
|
|
1376
|
+
.caf-btn-sm { font-size: 0.78rem; padding: calc(var(--caf-btn-pad-y) * 0.67) calc(var(--caf-btn-pad-x) * 0.75); min-width: 76px; text-align: center; }
|
|
1367
1377
|
.caf-items { display: flex; flex-direction: column; gap: 0.5rem; border-top: 1px solid var(--caf-border); padding-top: 0.7rem; }
|
|
1368
1378
|
.caf-item-row { display: flex; align-items: flex-end; gap: 0.5rem; }
|
|
1369
1379
|
.caf-item-row .caf-field { flex: 1; min-width: 0; }
|
|
@@ -1377,8 +1387,8 @@ const BASE_CSS = `
|
|
|
1377
1387
|
.caf-md-li { display: block; padding-left: 1rem; position: relative; }
|
|
1378
1388
|
.caf-md-li::before { content: "•"; position: absolute; left: 0.2rem; opacity: 0.6; }
|
|
1379
1389
|
.caf-workspace { max-width: 1200px; margin: 0 auto; display: flex; gap: 1rem; align-items: flex-start; }
|
|
1380
|
-
.caf-ws-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap:
|
|
1381
|
-
.caf-ws-side { width: 340px; flex: none; display: flex; flex-direction: column; gap:
|
|
1390
|
+
.caf-ws-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--caf-block-gap); }
|
|
1391
|
+
.caf-ws-side { width: 340px; flex: none; display: flex; flex-direction: column; gap: var(--caf-block-gap); position: sticky; top: 1rem; }
|
|
1382
1392
|
.caf-ws-slot { min-width: 0; }
|
|
1383
1393
|
.caf-chat-panel { display: flex; flex-direction: column; min-height: 0; }
|
|
1384
1394
|
.caf-ws-side .caf-chat-log { max-height: calc(100vh - 220px); }
|
|
@@ -1463,6 +1473,11 @@ const BASE_CSS = `
|
|
|
1463
1473
|
/* Never-clip guarantee: content inside a custom block that is wider than the
|
|
1464
1474
|
phone (column grids, week views) must scroll, not disappear off-screen. */
|
|
1465
1475
|
.caf-custom { overflow-x: auto; }
|
|
1476
|
+
/* The one differentiated surface per screen: accent top rule + tinted bg on
|
|
1477
|
+
the block holding the hero number. Everything-is-the-same-card was the
|
|
1478
|
+
audited root cause of the "generic template" look. (Late in the sheet on
|
|
1479
|
+
purpose — it must win over .caf-block's border/background.) */
|
|
1480
|
+
.caf-block-feature { border-top: 3px solid var(--caf-accent); background: var(--caf-accent-tint); }
|
|
1466
1481
|
/* mobile.nav: "bottom" — the app-shell tab bar pinned to the thumb zone.
|
|
1467
1482
|
Tabs share the width evenly so every section stays visible; the root
|
|
1468
1483
|
reserves space so the bar never covers the end of the content. */
|
package/src/blocks.js
CHANGED
|
@@ -178,9 +178,14 @@ export function OutputBlock({ spec, ctx }) {
|
|
|
178
178
|
if (!items.length) {
|
|
179
179
|
return h("div", { className: "caf-block caf-empty" }, spec.empty || "Nothing to show yet.");
|
|
180
180
|
}
|
|
181
|
+
// The block holding the screen's hero number (a `big` stat) gets the one
|
|
182
|
+
// differentiated surface — an accent-tinted feature card. Audited miss:
|
|
183
|
+
// with a single card chrome, the most important number on screen weighed
|
|
184
|
+
// exactly the same as everything else.
|
|
185
|
+
const hero = items.some((it) => it && it.big);
|
|
181
186
|
return h(
|
|
182
187
|
"div",
|
|
183
|
-
{ className: "caf-block caf-output" },
|
|
188
|
+
{ className: hero ? "caf-block caf-output caf-block-feature" : "caf-block caf-output" },
|
|
184
189
|
spec.title ? h("h2", { className: "caf-block-title" }, spec.title) : null,
|
|
185
190
|
h(
|
|
186
191
|
"div",
|
|
@@ -202,12 +207,14 @@ export function OutputBlock({ spec, ctx }) {
|
|
|
202
207
|
// with nothing uploaded (artifacts can't persist real images — the ~5MB
|
|
203
208
|
// pool). banner's default face is a gradient derived from the theme seed;
|
|
204
209
|
// image accepts a URL or an inline SVG string (LLMs generate SVG well).
|
|
205
|
-
export function BannerBlock({ spec
|
|
210
|
+
export function BannerBlock({ spec }) {
|
|
206
211
|
const b = typeof spec.banner === "string" ? { title: spec.banner } : spec.banner || {};
|
|
207
|
-
|
|
212
|
+
// Tonal gradient, one hue family: the golden-angle series palette is for
|
|
213
|
+
// CHARTS (categorical distinction) — on a brand surface its second hue
|
|
214
|
+
// clashes (audited: an orange seed produced an orange→green banner).
|
|
208
215
|
const background = b.image
|
|
209
216
|
? `linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)), url(${JSON.stringify(b.image)}) center / cover`
|
|
210
|
-
: `linear-gradient(135deg,
|
|
217
|
+
: `linear-gradient(135deg, color-mix(in srgb, var(--caf-accent) 82%, #000), var(--caf-accent) 55%, color-mix(in srgb, var(--caf-accent) 72%, #fff))`;
|
|
211
218
|
return h(
|
|
212
219
|
"div",
|
|
213
220
|
{ className: "caf-banner", style: { background } },
|
package/src/records.js
CHANGED
|
@@ -103,9 +103,13 @@ function Summary({ spec, records }) {
|
|
|
103
103
|
if (!spec.summary) return null;
|
|
104
104
|
const items = spec.summary(records) || [];
|
|
105
105
|
if (!items.length) return null;
|
|
106
|
+
// Same rule as OutputBlock: the hero (big) stat promotes its card to the
|
|
107
|
+
// screen's feature surface. Re-audit caught this second render path
|
|
108
|
+
// missing the treatment — the rule must hold wherever big stats render.
|
|
109
|
+
const hero = items.some((it) => it && it.big);
|
|
106
110
|
return h(
|
|
107
111
|
"div",
|
|
108
|
-
{ className: "caf-block caf-output" },
|
|
112
|
+
{ className: hero ? "caf-block caf-output caf-block-feature" : "caf-block caf-output" },
|
|
109
113
|
h(
|
|
110
114
|
"div",
|
|
111
115
|
{ className: "caf-output-grid" },
|
package/src/steps.js
CHANGED
|
@@ -28,7 +28,8 @@ function ResultScreen({ step, ctx }) {
|
|
|
28
28
|
{ className: "caf-steps-result" },
|
|
29
29
|
h(
|
|
30
30
|
"div",
|
|
31
|
-
|
|
31
|
+
// Same rule as OutputBlock: the hero (big) result gets the feature card.
|
|
32
|
+
{ className: items.some((it) => it && it.big) ? "caf-block caf-output caf-block-feature" : "caf-block caf-output" },
|
|
32
33
|
step.title ? h("h2", { className: "caf-block-title" }, step.title) : null,
|
|
33
34
|
h(
|
|
34
35
|
"div",
|