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 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
- { key: i, className: block.wide ? "caf-slot caf-slot-wide" : "caf-slot" },
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
- { key: i, className: block.wide ? "caf-slot caf-slot-wide" : "caf-slot" },
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 = () => reject(new Error(`failed to load ${src}`));
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
- { className: "caf-root" },
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
- .caf-panel { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
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.95rem; font-weight: 650; }
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.78rem; font-weight: 600; color: var(--caf-muted); letter-spacing: 0.02em; }
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: 7px; border: 1px solid var(--caf-border);
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: flex; flex-direction: column; gap: 0.7rem; }
1880
- .caf-stat { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
1881
- .caf-stat-label { color: var(--caf-muted); font-size: 0.82rem; }
1882
- .caf-stat-value { font-size: 1rem; font-weight: 600; font-variant-numeric: tabular-nums; }
1883
- .caf-stat-big { flex-direction: column; align-items: flex-start; gap: 0.15rem; padding-bottom: 0.4rem; border-bottom: 1px solid var(--caf-border); }
1884
- .caf-stat-big .caf-stat-value { font-size: 1.9rem; font-weight: 680; letter-spacing: -0.02em; color: var(--caf-accent); }
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: 7px;
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: 0.45rem 0.8rem; border-radius: 7px;
1902
- border: 1px solid var(--caf-border); background: var(--caf-sunken); color: var(--caf-text);
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
- .caf-donut-legend { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; flex: 1; }
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: 2.6rem; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }
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 { display: flex; gap: 0.6rem; margin-top: 0.8rem; }
1948
- .caf-chat-input input { flex: 1; font: inherit; font-size: 0.95rem; padding: 0.55rem 0.7rem; border-radius: 8px; border: 1px solid var(--caf-border); background: var(--caf-surface); color: var(--caf-text); }
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: 0.3rem 0.6rem; border-radius: 6px; }
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
- .caf-tab-active { color: var(--caf-accent); border-bottom-color: var(--caf-accent); }
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
- font: inherit; font-size: 0.95rem; font-weight: 650; cursor: pointer;
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); color: var(--caf-text); width: 100%; text-align: left;
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); }