claude-artifact-framework 0.8.1 → 0.9.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 +19 -2
- package/dist/index.esm.js +61 -26
- package/dist/index.esm.js.map +2 -2
- package/dist/index.global.js +52 -30
- package/dist/index.global.js.map +3 -3
- package/package.json +1 -1
- package/src/app.js +66 -26
package/README.md
CHANGED
|
@@ -323,7 +323,9 @@ blocks: [
|
|
|
323
323
|
|
|
324
324
|
Action keys: `label`, `run`, optional `when` (hides the button when false)
|
|
325
325
|
and `kind` (`"primary"` / `"danger"`). Row actions receive the record;
|
|
326
|
-
block actions receive data. Changes persist like any other update.
|
|
326
|
+
block actions receive data. Changes persist like any other update. Give the
|
|
327
|
+
constructive action `kind: "primary"` — a "Vote"/"Add"/"Done" left as the
|
|
328
|
+
neutral default reads weaker than a `"danger"` next to it.
|
|
327
329
|
|
|
328
330
|
Chat bubbles render the assistant's **markdown** (bold, code, lists,
|
|
329
331
|
headings) automatically — HTML is escaped first, so model output can't
|
|
@@ -409,6 +411,19 @@ ArtifactKit.createApp({
|
|
|
409
411
|
- **`libs: ["https://..."]`** loads external scripts before the app mounts,
|
|
410
412
|
deduped by URL, with a "Loading libraries…" state and a named error block
|
|
411
413
|
if one fails. No hand-written `loadScript` in custom code.
|
|
414
|
+
- **Call the library's method, not its global.** Modern CDN builds expose a
|
|
415
|
+
*namespace object*, and APIs you remember may have moved: `marked` v5+ is
|
|
416
|
+
an object (`marked.parse(text)` works, `marked(text)` throws "marked is
|
|
417
|
+
not a function"). When unsure, prefer the method form — `marked.parse(md)`,
|
|
418
|
+
`Papa.parse(csv)`, `QRCode.toCanvas(...)` or `new QRCode(el, ...)`
|
|
419
|
+
depending on the lib — and pin the version in the URL so the API can't
|
|
420
|
+
drift under you.
|
|
421
|
+
- **Unsure of the file path? Use the bare package URL.** jsDelivr serves the
|
|
422
|
+
package's default build at `https://cdn.jsdelivr.net/npm/<pkg>@<version>`
|
|
423
|
+
with no file path — guessed inner paths (`/marked.umd.js`,
|
|
424
|
+
`/dist/foo.min.js`) 404 more often than versions do. If a full path you
|
|
425
|
+
wrote does 404, the framework retries the bare package URL once before
|
|
426
|
+
showing the error.
|
|
412
427
|
- **`type: "file"`** fields hand the live `File` to your code at
|
|
413
428
|
`ctx.files[key]` (in memory only — the 5MB/key storage limit makes real
|
|
414
429
|
files unpersistable); `data[key]` gets serializable metadata
|
|
@@ -533,7 +548,9 @@ keys must be unique across steps.
|
|
|
533
548
|
|
|
534
549
|
**Formats** (`format:` on output/chart/compute items): `money`, `percent`,
|
|
535
550
|
`number`, `date`. `percent` expects 0–100 (pass `ratio * 100`, not the raw
|
|
536
|
-
ratio).
|
|
551
|
+
ratio). `big: true` renders an item at hero size — reserve it for the ONE
|
|
552
|
+
number that is the screen's main result (a total, a final score), not for
|
|
553
|
+
every stat; more than one hero per screen means no hero at all.
|
|
537
554
|
|
|
538
555
|
**Quick-add pattern** — a lightweight alternative to `records` when you just
|
|
539
556
|
need "type something, press a button, it lands in a list":
|
package/dist/index.esm.js
CHANGED
|
@@ -1717,7 +1717,9 @@ function Panel({ spec, ctx }) {
|
|
|
1717
1717
|
(spec.blocks || []).map(
|
|
1718
1718
|
(block, i) => createElement(
|
|
1719
1719
|
"section",
|
|
1720
|
-
|
|
1720
|
+
// A collapsible is always full-width: half-width accordions leave an
|
|
1721
|
+
// orphaned right edge on desktop and change width across breakpoints.
|
|
1722
|
+
{ key: i, className: block.wide || block.collapsible ? "caf-slot caf-slot-wide" : "caf-slot" },
|
|
1721
1723
|
createElement(Block, { block, ctx })
|
|
1722
1724
|
)
|
|
1723
1725
|
)
|
|
@@ -1735,7 +1737,9 @@ function RecordsWithBlocks({ spec, ctx }) {
|
|
|
1735
1737
|
spec.blocks.map(
|
|
1736
1738
|
(block, i) => createElement(
|
|
1737
1739
|
"section",
|
|
1738
|
-
|
|
1740
|
+
// A collapsible is always full-width: half-width accordions leave an
|
|
1741
|
+
// orphaned right edge on desktop and change width across breakpoints.
|
|
1742
|
+
{ key: i, className: block.wide || block.collapsible ? "caf-slot caf-slot-wide" : "caf-slot" },
|
|
1739
1743
|
createElement(Block, { block, ctx })
|
|
1740
1744
|
)
|
|
1741
1745
|
)
|
|
@@ -1767,7 +1771,14 @@ function loadScript(src) {
|
|
|
1767
1771
|
tag.dataset.cafReady = "1";
|
|
1768
1772
|
resolve();
|
|
1769
1773
|
};
|
|
1770
|
-
tag.onerror = () =>
|
|
1774
|
+
tag.onerror = () => {
|
|
1775
|
+
const m = src.match(/^(https:\/\/cdn\.jsdelivr\.net\/npm\/(?:@[^/]+\/)?[^/]+)\/.+/);
|
|
1776
|
+
if (m && m[1] !== src) {
|
|
1777
|
+
loadScript(m[1]).then(resolve, () => reject(new Error(`failed to load ${src} (also tried package default ${m[1]})`)));
|
|
1778
|
+
} else {
|
|
1779
|
+
reject(new Error(`failed to load ${src}`));
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1771
1782
|
document.head.appendChild(tag);
|
|
1772
1783
|
});
|
|
1773
1784
|
}
|
|
@@ -1817,7 +1828,9 @@ function createApp(spec = {}) {
|
|
|
1817
1828
|
const Layout = LAYOUT_COMPONENTS[layout];
|
|
1818
1829
|
return createElement(
|
|
1819
1830
|
"div",
|
|
1820
|
-
|
|
1831
|
+
// The layout class lets shared CSS follow the active container width —
|
|
1832
|
+
// e.g. the header must share the workspace's wider axis, not panel's.
|
|
1833
|
+
{ className: `caf-root caf-layout-${layout}` },
|
|
1821
1834
|
createElement("style", { dangerouslySetInnerHTML: { __html: css } }),
|
|
1822
1835
|
createElement(
|
|
1823
1836
|
"header",
|
|
@@ -1837,6 +1850,10 @@ function createApp(spec = {}) {
|
|
|
1837
1850
|
var BASE_CSS = `
|
|
1838
1851
|
.caf-root {
|
|
1839
1852
|
--caf-radius: 10px;
|
|
1853
|
+
--caf-control-radius: 7px; /* one radius for every control: buttons, inputs, rows */
|
|
1854
|
+
--caf-btn-pad-y: 0.45rem;
|
|
1855
|
+
--caf-btn-pad-x: 0.8rem;
|
|
1856
|
+
--caf-num-primary: 2.1rem; /* the one size for a block's dominant number */
|
|
1840
1857
|
box-sizing: border-box;
|
|
1841
1858
|
min-height: 100vh;
|
|
1842
1859
|
padding: 2rem 1.25rem 4rem;
|
|
@@ -1847,9 +1864,12 @@ var BASE_CSS = `
|
|
|
1847
1864
|
}
|
|
1848
1865
|
.caf-root *, .caf-root *::before, .caf-root *::after { box-sizing: inherit; }
|
|
1849
1866
|
.caf-header { max-width: 760px; margin: 0 auto 1.5rem; }
|
|
1867
|
+
/* One left axis per screen: the header adopts the workspace's wider container. */
|
|
1868
|
+
.caf-layout-workspace .caf-header { max-width: 1200px; }
|
|
1850
1869
|
.caf-header h1 { margin: 0; font-size: 1.5rem; font-weight: 650; letter-spacing: -0.01em; text-wrap: balance; }
|
|
1851
1870
|
.caf-header p { margin: 0.4rem 0 0; color: var(--caf-muted); font-size: 0.95rem; line-height: 1.5; }
|
|
1852
|
-
|
|
1871
|
+
/* Between-card air must beat within-card padding (1.1rem), or groups dissolve. */
|
|
1872
|
+
.caf-panel { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 1.75rem 1rem; }
|
|
1853
1873
|
.caf-slot { grid-column: span 2; min-width: 0; }
|
|
1854
1874
|
@media (min-width: 720px) { .caf-slot { grid-column: span 1; } .caf-slot-wide { grid-column: span 2; } }
|
|
1855
1875
|
.caf-block {
|
|
@@ -1862,12 +1882,12 @@ var BASE_CSS = `
|
|
|
1862
1882
|
gap: 0.8rem;
|
|
1863
1883
|
height: 100%;
|
|
1864
1884
|
}
|
|
1865
|
-
.caf-block-title { margin: 0; font-size: 0.
|
|
1885
|
+
.caf-block-title { margin: 0; font-size: 0.78rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--caf-muted); }
|
|
1866
1886
|
.caf-field { display: flex; flex-direction: column; gap: 0.3rem; }
|
|
1867
|
-
.caf-field label { font-size: 0.
|
|
1887
|
+
.caf-field label { font-size: 0.72rem; font-weight: 500; color: var(--caf-muted); letter-spacing: 0.02em; }
|
|
1868
1888
|
.caf-field input, .caf-field select, .caf-field textarea {
|
|
1869
1889
|
font: inherit; font-size: 0.95rem; padding: 0.5rem 0.6rem;
|
|
1870
|
-
border-radius:
|
|
1890
|
+
border-radius: var(--caf-control-radius); border: 1px solid var(--caf-border);
|
|
1871
1891
|
background: var(--caf-sunken); color: var(--caf-text); width: 100%;
|
|
1872
1892
|
}
|
|
1873
1893
|
.caf-field input:focus-visible, .caf-field select:focus-visible, .caf-field textarea:focus-visible,
|
|
@@ -1876,17 +1896,21 @@ var BASE_CSS = `
|
|
|
1876
1896
|
.caf-check { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; }
|
|
1877
1897
|
.caf-hint { color: var(--caf-muted); font-size: 0.75rem; }
|
|
1878
1898
|
.caf-error { color: var(--caf-danger); font-size: 0.75rem; font-weight: 600; }
|
|
1879
|
-
.caf-output-grid { display:
|
|
1880
|
-
|
|
1881
|
-
.caf-stat
|
|
1882
|
-
.caf-stat-
|
|
1883
|
-
.caf-stat-
|
|
1884
|
-
.caf-stat-big
|
|
1899
|
+
.caf-output-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.9rem 1.1rem; }
|
|
1900
|
+
/* Every stat shares one axis: label above value, both flush left. */
|
|
1901
|
+
.caf-stat { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }
|
|
1902
|
+
.caf-stat-label { color: var(--caf-muted); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.02em; }
|
|
1903
|
+
.caf-stat-value { font-size: 1.05rem; font-weight: 650; font-variant-numeric: tabular-nums; }
|
|
1904
|
+
.caf-stat-big { grid-column: 1 / -1; }
|
|
1905
|
+
/* The divider exists to separate the hero from stats below it \u2014 alone in the
|
|
1906
|
+
grid it would be an orphaned rule, so it only renders when followed. */
|
|
1907
|
+
.caf-stat-big:not(:last-child) { padding-bottom: 0.5rem; border-bottom: 1px solid var(--caf-border); }
|
|
1908
|
+
.caf-stat-big .caf-stat-value { font-size: var(--caf-num-primary); font-weight: 700; letter-spacing: -0.02em; color: var(--caf-accent); line-height: 1.15; }
|
|
1885
1909
|
.caf-list { padding: 0.4rem; gap: 0; }
|
|
1886
1910
|
.caf-row {
|
|
1887
1911
|
font: inherit; text-align: left; cursor: pointer;
|
|
1888
1912
|
display: flex; flex-direction: column; gap: 0.15rem;
|
|
1889
|
-
padding: 0.65rem 0.7rem; border: none; border-radius:
|
|
1913
|
+
padding: 0.65rem 0.7rem; border: none; border-radius: var(--caf-control-radius);
|
|
1890
1914
|
background: transparent; color: inherit; width: 100%;
|
|
1891
1915
|
}
|
|
1892
1916
|
.caf-row:hover { background: var(--caf-sunken); }
|
|
@@ -1898,8 +1922,9 @@ var BASE_CSS = `
|
|
|
1898
1922
|
.caf-count { font-size: 0.8rem; color: var(--caf-muted); font-variant-numeric: tabular-nums; }
|
|
1899
1923
|
.caf-btn {
|
|
1900
1924
|
font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer;
|
|
1901
|
-
padding:
|
|
1902
|
-
border: 1px solid var(--caf-border); background: var(--caf-
|
|
1925
|
+
padding: var(--caf-btn-pad-y) var(--caf-btn-pad-x); border-radius: var(--caf-control-radius);
|
|
1926
|
+
border: 1px solid var(--caf-border); background: var(--caf-surface); color: var(--caf-text);
|
|
1927
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
|
1903
1928
|
}
|
|
1904
1929
|
.caf-btn:hover { filter: brightness(0.97); }
|
|
1905
1930
|
.caf-btn:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
@@ -1920,14 +1945,16 @@ var BASE_CSS = `
|
|
|
1920
1945
|
.caf-donut { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; }
|
|
1921
1946
|
.caf-donut-svg { width: 110px; height: 110px; flex: none; transform: rotate(0.001deg); }
|
|
1922
1947
|
.caf-donut-seg { fill: none; stroke-width: 6; }
|
|
1923
|
-
|
|
1948
|
+
/* Capped width keeps each value NEXT TO its label in wide cards \u2014 the pair
|
|
1949
|
+
must read as a pair regardless of container width. */
|
|
1950
|
+
.caf-donut-legend { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; flex: 1; max-width: 260px; }
|
|
1924
1951
|
.caf-legend-row { display: flex; align-items: center; gap: 0.5rem; font-size: 0.84rem; }
|
|
1925
1952
|
.caf-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
|
|
1926
1953
|
.caf-legend-label { color: var(--caf-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1927
1954
|
.caf-legend-value { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
1928
1955
|
.caf-timer { align-items: center; text-align: center; }
|
|
1929
1956
|
.caf-timer .caf-bar-track { width: 100%; }
|
|
1930
|
-
.caf-timer-time { font-size:
|
|
1957
|
+
.caf-timer-time { font-size: var(--caf-num-primary); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; color: var(--caf-accent); }
|
|
1931
1958
|
.caf-timer-done { color: var(--caf-accent); }
|
|
1932
1959
|
.caf-timer-fill { transition: width 0.9s linear; }
|
|
1933
1960
|
.caf-timer-controls { display: flex; gap: 0.6rem; }
|
|
@@ -1944,8 +1971,11 @@ var BASE_CSS = `
|
|
|
1944
1971
|
.caf-msg-tools { align-self: flex-start; font-size: 0.74rem; color: var(--caf-muted); padding: 0.15rem 0.55rem; border: 1px solid var(--caf-border); border-radius: 999px; }
|
|
1945
1972
|
.caf-msg-error { align-self: stretch; max-width: none; display: flex; flex-direction: column; gap: 0.2rem; background: transparent; border: 1px solid var(--caf-danger); }
|
|
1946
1973
|
.caf-msg-error strong { color: var(--caf-danger); font-size: 0.85rem; }
|
|
1947
|
-
.caf-chat-input {
|
|
1948
|
-
|
|
1974
|
+
.caf-chat-input {
|
|
1975
|
+
display: flex; gap: 0.6rem; margin-top: 1rem; padding: 0.6rem;
|
|
1976
|
+
background: var(--caf-surface); border: 1px solid var(--caf-border); border-radius: var(--caf-radius);
|
|
1977
|
+
}
|
|
1978
|
+
.caf-chat-input input { flex: 1; font: inherit; font-size: 0.95rem; padding: 0.55rem 0.7rem; border-radius: var(--caf-control-radius); border: 1px solid var(--caf-border); background: var(--caf-sunken); color: var(--caf-text); }
|
|
1949
1979
|
.caf-chat-input input:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
1950
1980
|
.caf-search {
|
|
1951
1981
|
font: inherit; font-size: 0.9rem; width: 100%;
|
|
@@ -1959,7 +1989,7 @@ var BASE_CSS = `
|
|
|
1959
1989
|
.caf-row-wrap { display: flex; align-items: center; gap: 0.4rem; }
|
|
1960
1990
|
.caf-row-wrap .caf-row { flex: 1; min-width: 0; }
|
|
1961
1991
|
.caf-row-actions { display: flex; gap: 0.35rem; flex: none; padding-right: 0.3rem; }
|
|
1962
|
-
.caf-btn-sm { font-size: 0.78rem; padding:
|
|
1992
|
+
.caf-btn-sm { font-size: 0.78rem; padding: calc(var(--caf-btn-pad-y) * 0.67) calc(var(--caf-btn-pad-x) * 0.75); }
|
|
1963
1993
|
.caf-items { display: flex; flex-direction: column; gap: 0.5rem; border-top: 1px solid var(--caf-border); padding-top: 0.7rem; }
|
|
1964
1994
|
.caf-item-row { display: flex; align-items: flex-end; gap: 0.5rem; }
|
|
1965
1995
|
.caf-item-row .caf-field { flex: 1; min-width: 0; }
|
|
@@ -1991,15 +2021,20 @@ var BASE_CSS = `
|
|
|
1991
2021
|
}
|
|
1992
2022
|
.caf-tab:hover { color: var(--caf-text); }
|
|
1993
2023
|
.caf-tab:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: -2px; }
|
|
1994
|
-
|
|
2024
|
+
/* :hover on the active tab must not win the specificity race and mute it. */
|
|
2025
|
+
.caf-tab-active, .caf-tab-active:hover { color: var(--caf-accent); border-bottom-color: var(--caf-accent); }
|
|
1995
2026
|
.caf-tabs-body { display: flex; flex-direction: column; gap: 0.8rem; }
|
|
1996
2027
|
.caf-collapse { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
|
|
1997
2028
|
.caf-collapse-head {
|
|
1998
|
-
|
|
2029
|
+
/* Same treatment as .caf-block-title: a collapsible header IS a card title. */
|
|
2030
|
+
font: inherit; font-size: 0.78rem; font-weight: 650; text-transform: uppercase;
|
|
2031
|
+
letter-spacing: 0.06em; color: var(--caf-muted); cursor: pointer;
|
|
1999
2032
|
display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
|
|
2000
|
-
padding: 0.7rem 1rem; border: 1px solid var(--caf-border); border-radius: var(--caf-radius);
|
|
2001
|
-
background: var(--caf-surface);
|
|
2033
|
+
padding: 0.7rem 1.1rem; border: 1px solid var(--caf-border); border-radius: var(--caf-radius);
|
|
2034
|
+
background: var(--caf-surface); width: 100%; text-align: left;
|
|
2002
2035
|
}
|
|
2036
|
+
.caf-collapse-head { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); }
|
|
2037
|
+
.caf-collapse-head:hover { filter: brightness(0.97); }
|
|
2003
2038
|
.caf-collapse-head:focus-visible { outline: 2px solid var(--caf-accent); outline-offset: 1px; }
|
|
2004
2039
|
.caf-chevron { transition: transform 0.15s ease; color: var(--caf-muted); }
|
|
2005
2040
|
.caf-chevron-closed { transform: rotate(-90deg); }
|