claude-artifact-framework 0.20.0 → 0.20.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/dist/index.esm.js +16 -8
- package/dist/index.esm.js.map +2 -2
- package/dist/index.global.js +11 -8
- package/dist/index.global.js.map +3 -3
- package/package.json +1 -1
- package/src/app.js +5 -2
- package/src/blocks.js +17 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-artifact-framework",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.1",
|
|
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
|
@@ -1477,7 +1477,10 @@ const BASE_CSS = `
|
|
|
1477
1477
|
.caf-doc-close:hover { color: var(--caf-danger); }
|
|
1478
1478
|
.caf-doc-add { font-size: 1rem; font-weight: 650; }
|
|
1479
1479
|
.caf-empty .caf-btn { margin-top: 0.6rem; }
|
|
1480
|
-
|
|
1480
|
+
/* Tight gap: the block title is a SECTION head for the cards below — it
|
|
1481
|
+
must hug its group, not float equidistant (audited on the retro board). */
|
|
1482
|
+
.caf-records-block { display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; }
|
|
1483
|
+
.caf-stat-neg, .caf-stat-big .caf-stat-value.caf-stat-neg { color: var(--caf-danger); }
|
|
1481
1484
|
.caf-doc-typemenu { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
|
1482
1485
|
.caf-header-brand { display: flex; align-items: center; gap: 0.8rem; }
|
|
1483
1486
|
/* Where the page has a gutter, the logo lives in it: the title keeps the
|
|
@@ -1489,7 +1492,7 @@ const BASE_CSS = `
|
|
|
1489
1492
|
.caf-header-brand .caf-logo { position: absolute; right: 100%; margin-right: 0.85rem; top: 0.1rem; }
|
|
1490
1493
|
}
|
|
1491
1494
|
.caf-header-text { min-width: 0; }
|
|
1492
|
-
.caf-logo { flex: none; width: 40px; height: 40px; border-radius: 9px; object-fit: contain; }
|
|
1495
|
+
.caf-logo { flex: none; width: 40px; height: 40px; border-radius: 9px; object-fit: contain; overflow: hidden; }
|
|
1493
1496
|
.caf-logo-emoji { display: flex; align-items: center; justify-content: center; font-size: 1.7rem; background: var(--caf-surface); border: 1px solid var(--caf-border); }
|
|
1494
1497
|
.caf-logo-mono {
|
|
1495
1498
|
display: flex; align-items: center; justify-content: center;
|
package/src/blocks.js
CHANGED
|
@@ -63,7 +63,13 @@ export function validateField(field, value) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export function Field({ field, value, onChange, data }) {
|
|
66
|
-
|
|
66
|
+
// A pristine empty field must not shout "Required" before the user ever
|
|
67
|
+
// touched it (audited on first load of a quick-add form). Range/format
|
|
68
|
+
// errors imply a non-empty value, so only the empty-required case waits.
|
|
69
|
+
const [touched, setTouched] = useState(false);
|
|
70
|
+
const rawError = validateField(field, value);
|
|
71
|
+
const pristineEmpty = !touched && (value === "" || value === null || value === undefined);
|
|
72
|
+
const error = pristineEmpty ? null : rawError;
|
|
67
73
|
const id = `caf-f-${field.key}`;
|
|
68
74
|
const numeric = field.type === "number" || field.type === "money" || field.type === "percent";
|
|
69
75
|
|
|
@@ -108,6 +114,7 @@ export function Field({ field, value, onChange, data }) {
|
|
|
108
114
|
id,
|
|
109
115
|
value: value === null || value === undefined ? "" : value,
|
|
110
116
|
"aria-invalid": error ? "true" : undefined,
|
|
117
|
+
onBlur: () => setTouched(true),
|
|
111
118
|
onChange: (e) => onChange(numeric ? toNumber(e.target.value) : e.target.value),
|
|
112
119
|
};
|
|
113
120
|
|
|
@@ -229,15 +236,19 @@ export function StatGrid({ items, title }) {
|
|
|
229
236
|
h(
|
|
230
237
|
"div",
|
|
231
238
|
{ className: "caf-output-grid" },
|
|
232
|
-
items.map((item, i) =>
|
|
233
|
-
|
|
239
|
+
items.map((item, i) => {
|
|
240
|
+
// A negative money/number stat reads as a loss everywhere else on
|
|
241
|
+
// screen (list rows already color by sign) — the hero must agree,
|
|
242
|
+
// not paint a deficit in cheerful brand accent (audited).
|
|
243
|
+
const neg = (item.format === "money" || item.format === "number") && Number(item.value) < 0;
|
|
244
|
+
return h(
|
|
234
245
|
"div",
|
|
235
246
|
{ key: item.label || i, className: item.big ? "caf-stat caf-stat-big" : "caf-stat" },
|
|
236
247
|
h("span", { className: "caf-stat-label" }, item.label),
|
|
237
|
-
h("span", { className: "caf-stat-value" }, formatValue(item.value, item.format)),
|
|
248
|
+
h("span", { className: neg ? "caf-stat-value caf-stat-neg" : "caf-stat-value" }, formatValue(item.value, item.format)),
|
|
238
249
|
item.hint ? h("small", { className: "caf-hint" }, item.hint) : null
|
|
239
|
-
)
|
|
240
|
-
)
|
|
250
|
+
);
|
|
251
|
+
})
|
|
241
252
|
)
|
|
242
253
|
);
|
|
243
254
|
}
|