claude-artifact-framework 0.15.0 → 0.16.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/llms.txt CHANGED
@@ -64,6 +64,10 @@ https://cdn.jsdelivr.net/npm/claude-artifact-framework/dist/index.global.js
64
64
  13. Brand is zero-asset-first: monogram/gradient/emoji defaults before URLs;
65
65
  inline <svg> over remote images; one icon style per app (emoji-first);
66
66
  avatar: true instead of hand-built initials circles.
67
+ 14. Column grids in custom (kanban, week views) branch on ctx.viewport
68
+ ("mobile" < 720px | "desktop", reactive) — stack columns on mobile; half
69
+ the viewers are on a phone. The framework never clips (wide custom
70
+ content scrolls), but scrolling is the fallback, not the design.
67
71
 
68
72
  ## Canonical example
69
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-artifact-framework",
3
- "version": "0.15.0",
3
+ "version": "0.16.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
@@ -1029,6 +1029,17 @@ export function createApp(spec = {}) {
1029
1029
  );
1030
1030
  }
1031
1031
  const [, force] = useState(0);
1032
+ // ctx.viewport shares the 720px breakpoint the block grid already uses,
1033
+ // so a custom block that branches on it flips in sync with the layout.
1034
+ const [narrow, setNarrow] = useState(
1035
+ () => typeof window !== "undefined" && window.matchMedia("(max-width: 719px)").matches
1036
+ );
1037
+ useEffect(() => {
1038
+ const mq = window.matchMedia("(max-width: 719px)");
1039
+ const onChange = (e) => setNarrow(e.matches);
1040
+ mq.addEventListener("change", onChange);
1041
+ return () => mq.removeEventListener("change", onChange);
1042
+ }, []);
1032
1043
  const [libsState, setLibsState] = useState(spec.libs && spec.libs.length ? "loading" : "ready");
1033
1044
  const [libsError, setLibsError] = useState(null);
1034
1045
  useEffect(() => {
@@ -1081,6 +1092,7 @@ export function createApp(spec = {}) {
1081
1092
  setTab: (key, i) => state.setView({ tabs: { ...state.getView().tabs, [key]: i } }),
1082
1093
  toggleCollapsed: (key, val) => state.setView({ collapsed: { ...state.getView().collapsed, [key]: val } }),
1083
1094
  colors: (n) => seriesColors(spec.theme && spec.theme.seed, n),
1095
+ viewport: narrow ? "mobile" : "desktop",
1084
1096
  machine: spec.machine || null,
1085
1097
  send: spec.machine
1086
1098
  ? (eventName, payload) => runMachineEvent(spec.machine, state.update, eventName, payload)
@@ -1390,4 +1402,12 @@ const BASE_CSS = `
1390
1402
  .caf-block-error { border-color: var(--caf-danger); gap: 0.4rem; }
1391
1403
  .caf-block-error strong { font-size: 0.9rem; color: var(--caf-danger); }
1392
1404
  .caf-block-error code { font-size: 0.78rem; color: var(--caf-muted); word-break: break-word; }
1405
+ /* Never-clip guarantee: content inside a custom block that is wider than the
1406
+ phone (column grids, week views) must scroll, not disappear off-screen. */
1407
+ .caf-custom { overflow-x: auto; }
1408
+ /* Touch devices get tappable framework controls regardless of the spec. */
1409
+ @media (pointer: coarse) {
1410
+ .caf-btn, .caf-tab, .caf-field input, .caf-field select { min-height: 44px; }
1411
+ .caf-btn-sm, .caf-doc-close, .caf-item-remove { min-height: 40px; }
1412
+ }
1393
1413
  `;
package/src/blocks.js CHANGED
@@ -485,7 +485,10 @@ export function CustomBlock({ spec, ctx }) {
485
485
  const rendered = spec.custom(ctx);
486
486
  // `fill: true` frees the block from the card chrome so it can own a large
487
487
  // scrollable surface (a viewer, a canvas) — the workspace main-area case.
488
- const cls = spec.fill ? "caf-block caf-block-fill" : "caf-block";
488
+ // caf-custom guarantees a custom block can never CLIP its content on a
489
+ // narrow screen: wide hand-built grids become scrollable, not amputated
490
+ // (measured: a week grid cut off its last column at 390px with no way in).
491
+ const cls = spec.fill ? "caf-block caf-block-fill caf-custom" : "caf-block caf-custom";
489
492
  if (typeof rendered === "string") {
490
493
  return h("div", { className: cls, dangerouslySetInnerHTML: { __html: rendered } });
491
494
  }