@carto/meridian-ds 4.1.1-alpha.e6399e0.293 → 4.2.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/CHANGELOG.md CHANGED
@@ -2,10 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- - feat(ai-metadata): serve custom-icon and theme-token knowledge to AI agents — new `meridian_list_icons` and `meridian_get_tokens` MCP tools (icons searchable by curated keywords; tokens projected straight from the composed theme: palette / typography / spacing / shape / breakpoints / shadows), plus surfaced List `direction`/`spacing` props [#463](https://github.com/CartoDB/meridian-ds/pull/463) [sc-559510] [sc-559550]
6
-
7
5
  ## 4.0
8
6
 
7
+ ### 4.2.0
8
+
9
+ - feat(ai-metadata): serve custom-icon and theme-token knowledge to AI agents — new `meridian_list_icons`, `meridian_get_tokens`, and `meridian_get_screenshot` MCP tools (icons searchable by curated keywords + a labeled all-icons reference image an agent can Read to match a design by sight; tokens projected straight from the composed theme: palette / typography / spacing / shape / breakpoints / shadows), plus surfaced List `direction`/`spacing` props [#463](https://github.com/CartoDB/meridian-ds/pull/463) [sc-559510] [sc-559550] [sc-561408]
10
+ - feat(ci): add Claude AI PR review workflow (sc-561354) [#471](https://github.com/CartoDB/meridian-ds/pull/471)
11
+ - chore(ci): disable gemini-code-assist code review [#479](https://github.com/CartoDB/meridian-ds/pull/479)
12
+
9
13
  ### 4.1.0
10
14
 
11
15
  - Storybook: replace Chromatic visual regression with a mandatory self-hosted Playwright harness (in-repo baselines, PR diff report, label-driven baseline updates); Chromatic is now deploy-only for the MCP integration [#412](https://github.com/CartoDB/meridian-ds/pull/412)
@@ -34,7 +34,7 @@ var DOCS = [];
34
34
  var KNOWN = [];
35
35
  var ICONS = [];
36
36
  var TOKENS;
37
- var ICONS_SCREENSHOT;
37
+ var SCREENSHOTS = {};
38
38
  var CONVENTIONS = "";
39
39
  var VERSION = "0.0.0";
40
40
  var TOOLS = [
@@ -103,11 +103,16 @@ var TOOLS = [
103
103
  }
104
104
  },
105
105
  {
106
- name: "meridian_get_icons_screenshot",
107
- description: "Returns the filesystem path to a labeled screenshot of EVERY custom icon (each glyph captioned with its import name, in a grid). Read that image to visually confirm an icon you shortlisted from meridian_list_icons, or to match an icon in a design you're implementing, then use its exact name. Deliberately returns a path (not an inline image) \u2014 portable, cheap per call, and reusable by a consumer app's icon picker. Requires a runtime that can read images (e.g. Claude Code); headless/non-multimodal callers can skip it and match on name + keywords alone.",
106
+ name: "meridian_get_screenshot",
107
+ description: "Returns the filesystem path to a labeled reference screenshot. name='icons' is a grid of EVERY custom icon (each glyph captioned with its import name). Read that image to visually confirm an icon you shortlisted from meridian_list_icons, or to match one in a design you're implementing, then use its exact name. Deliberately returns a path (not an inline image) \u2014 portable, cheap per call, and reusable by a consumer app's picker. Requires a runtime that can read images (e.g. Claude Code); headless/non-multimodal callers can skip it. Call with no name to list the available screenshots.",
108
108
  inputSchema: {
109
109
  type: "object",
110
- properties: {},
110
+ properties: {
111
+ name: {
112
+ type: "string",
113
+ description: "Which screenshot to return (e.g. 'icons'). Omit to list the available ones."
114
+ }
115
+ },
111
116
  additionalProperties: false
112
117
  }
113
118
  }
@@ -280,20 +285,18 @@ Fetch any with meridian_get_examples(storyId):
280
285
  }
281
286
  return md;
282
287
  }
283
- function getIconsScreenshot() {
284
- if (!ICONS_SCREENSHOT || !fs.existsSync(ICONS_SCREENSHOT.path))
285
- return {
286
- error: "No icon screenshot in this bundle \u2014 run `yarn metadata:icons-screenshot` and rebuild."
287
- };
288
- return {
289
- path: ICONS_SCREENSHOT.path,
290
- iconCount: ICONS_SCREENSHOT.iconCount,
291
- hint: "Read this image; each glyph is labeled with its import name. Use it to confirm an icon shortlisted via meridian_list_icons, or to match one from a design."
292
- };
288
+ function getScreenshot(name) {
289
+ const available = Object.keys(SCREENSHOTS);
290
+ const shot = name ? SCREENSHOTS[name] : void 0;
291
+ if (!shot || !fs.existsSync(shot.path))
292
+ return name ? {
293
+ error: `No screenshot named "${name}". Available: ${available.join(", ") || "(none \u2014 run `yarn metadata:screenshots` and rebuild)"}.`
294
+ } : { available };
295
+ return { path: shot.path, count: shot.count, hint: shot.hint };
293
296
  }
294
297
  function listIcons() {
295
298
  return {
296
- note: "Import from '@carto/meridian-ds/custom-icons' and render as <Name />. Match by name or keywords \u2014 there's no search tool. To see the glyphs, call meridian_get_icons_screenshot and Read the returned image path.",
299
+ note: "Import from '@carto/meridian-ds/custom-icons' and render as <Name />. Match by name or keywords \u2014 there's no search tool. To see the glyphs, call meridian_get_screenshot({name:'icons'}) and Read the returned image path.",
297
300
  props: "Every icon is an identical forwardRef component sharing one prop type, CustomIconProps: MUI SvgIconProps \u2014 color, fontSize ('small'|'medium'|'large'|'inherit'), sx, htmlColor, titleAccess \u2014 plus width/height (number|string) to override the fontSize-based size. Prefer color/fontSize for theming/sizing; width/height only for a specific px size. The exact type ships at node_modules/@carto/meridian-ds/dist/types/custom-icons/types.d.ts (the per-icon <Name>.d.ts is identical boilerplate \u2014 no icon-specific props).",
298
301
  count: ICONS.length,
299
302
  icons: ICONS
@@ -307,8 +310,8 @@ function callTool(name, args) {
307
310
  return listIcons();
308
311
  case "meridian_get_tokens":
309
312
  return TOKENS ?? { error: "No tokens in this bundle." };
310
- case "meridian_get_icons_screenshot":
311
- return getIconsScreenshot();
313
+ case "meridian_get_screenshot":
314
+ return getScreenshot(String(args.name ?? ""));
312
315
  case "meridian_get_component":
313
316
  return getComponent(String(args.name ?? ""));
314
317
  case "meridian_get_examples":
@@ -333,7 +336,7 @@ Flow:
333
336
  4. meridian_get_examples(name[, storyId]) \u2014 real, copy-able example code. With no storyId: the lead usage story and the notable stories in full plus a list of the rest; pass a storyId for one story's full code.
334
337
  5. meridian_list_icons \u2014 when you need an icon, list the custom icons and match by name/keyword; import from '@carto/meridian-ds/custom-icons' and render as <Name />.
335
338
  6. meridian_get_tokens \u2014 exact palette/typography/spacing/shape/breakpoint/shadow values to use instead of hardcoding (the conventions say use tokens; this gives the values).
336
- 7. meridian_get_icons_screenshot \u2014 a labeled image of every icon; Read the returned path to visually confirm a shortlisted icon or match one from a design (image-capable runtimes only).
339
+ 7. meridian_get_screenshot \u2014 a labeled reference image (name='icons' \u2192 every icon); Read the returned path to visually confirm a shortlisted icon or match one from a design (image-capable runtimes only).
337
340
 
338
341
  Three tiers of components, by how much each carries (all are real and importable \u2014 always prefer one over a bare MUI equivalent): CURATED (most) have full when-to-use / decisionTree / promoted-prop guidance; LISTED (flagged "documented": false, with example stories) carry no curation yet \u2014 lean on get_examples + the .d.ts; KNOWN (flagged "documented": false, no stories) are real components not yet documented \u2014 get_component/get_examples point you at their type declarations (.d.ts).
339
342
 
@@ -394,10 +397,16 @@ function load() {
394
397
  KNOWN = bundle.knownComponents ?? [];
395
398
  ICONS = bundle.icons ?? [];
396
399
  TOKENS = bundle.tokens;
397
- ICONS_SCREENSHOT = bundle.iconsScreenshot && {
398
- path: path.join(path.dirname(bundlePath), bundle.iconsScreenshot.file),
399
- iconCount: bundle.iconsScreenshot.iconCount
400
- };
400
+ SCREENSHOTS = Object.fromEntries(
401
+ Object.entries(bundle.screenshots ?? {}).map(([key, s]) => [
402
+ key,
403
+ {
404
+ path: path.join(path.dirname(bundlePath), s.file),
405
+ count: s.count,
406
+ hint: s.hint
407
+ }
408
+ ])
409
+ );
401
410
  CONVENTIONS = bundle.conventions;
402
411
  VERSION = bundle.version;
403
412
  process.stderr.write(
@@ -22,7 +22,7 @@ Meridian is the source of truth: when an existing codebase pattern contradicts M
22
22
  add a local SVG only when neither has an equivalent. Don't duplicate an icon
23
23
  the design system already ships.
24
24
  - Find the right custom icon with `meridian_list_icons` — match by name or keyword (e.g. "warehouse" → `Bigquery`), import it from `@carto/meridian-ds/custom-icons`, and render `<Name />`. Every icon shares one prop type, `CustomIconProps` (MUI `SvgIconProps` — `color`/`fontSize`/`sx` — plus `width`/`height`).
25
- - To confirm a shortlisted icon by sight (or match an icon in a design you're implementing), call `meridian_get_icons_screenshot` and Read the returned image path — a labeled grid of every icon.
25
+ - To confirm a shortlisted icon by sight (or match an icon in a design you're implementing), call `meridian_get_screenshot` with `name: 'icons'` and Read the returned image path — a labeled grid of every icon.
26
26
  - Use the standard icon sizes — small 12px, medium (default) 18px, large 24px;
27
27
  a value off this scale (e.g. 16, 20) needs design sign-off. Prefer the
28
28
  outlined variants.
Binary file
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "4.1.1-alpha.e6399e0.293",
3
- "conventions": "# Meridian usage conventions (for code-generating agents)\n\nSystem-level rules an agent should apply across all components.\n\nMeridian is the source of truth: when an existing codebase pattern contradicts Meridian, follow Meridian — don't replicate the contradicting local pattern.\n\n## Choosing a component\n\n- Don't hand-roll what the design system already provides. Before writing a\n custom component, a styled wrapper, or a block of primitives (a modal,\n chip/tag, card, empty state, dropdown, tooltip, copy-to-clipboard input,\n etc.), confirm no Meridian component covers it — reuse or extend the existing one instead of rebuilding it.\n- Prefer a Meridian component over a bare MUI one whenever a Meridian wrapper\n exists — the wrappers carry CARTO theming, a11y defaults, and narrowed APIs. MUI is correct for layout primitives (`Box`/`Grid`/`Stack`/`Container`) and for gaps Meridian doesn't fill (e.g. date pickers); \"prefer Meridian\" is not \"never MUI\".\n- Resolve \"which component\" by reading each one's `description` + `keywords`,\n then confirm with its `decisionTree` (the conditions that point elsewhere) and `limitations` (what it lacks — check before you downgrade to it).\n Don't infer selection from prop lists.\n\n## Icons\n\n- Source icons from Meridian's custom-icons first, then `@mui/icons-material`;\n add a local SVG only when neither has an equivalent. Don't duplicate an icon\n the design system already ships.\n- Find the right custom icon with `meridian_list_icons` — match by name or keyword (e.g. \"warehouse\" → `Bigquery`), import it from `@carto/meridian-ds/custom-icons`, and render `<Name />`. Every icon shares one prop type, `CustomIconProps` (MUI `SvgIconProps` — `color`/`fontSize`/`sx` — plus `width`/`height`).\n- To confirm a shortlisted icon by sight (or match an icon in a design you're implementing), call `meridian_get_icons_screenshot` and Read the returned image path — a labeled grid of every icon.\n- Use the standard icon sizes — small 12px, medium (default) 18px, large 24px;\n a value off this scale (e.g. 16, 20) needs design sign-off. Prefer the\n outlined variants.\n- A new SVG must inherit, not bake in: set `fill='currentColor'` so it takes\n the parent's color, and use the standard size — don't hardcode\n color/opacity/dimensions.\n- For an icon color outside the palette `color` set, pass a theme token via\n `htmlColor` (e.g. `htmlColor={theme.palette.text.secondary}`) — never a\n hardcoded hex.\n\n## Accessibility\n\n- Any icon-only control needs an accessible name: `tooltip` or `aria-label`.\n- Don't disable a control without telling the user why — pair a disabled action with a tooltip explaining the requirement.\n- An `aria-label` must be human-readable text describing the control, not a test id in disguise — use a separate test hook for that.\n- User-facing strings (labels, `aria-label`, placeholders, messages) go through the app's i18n layer, not hardcoded literals.\n\n## Props\n\n- Two sources, both pinned to the installed `@carto/meridian-ds`: **exact props / types / JSDoc** come from the package's **type declarations** — read the `<Name>.d.ts` directly — while the **`meridian-design-system` MCP** (`meridian_*` tools) covers what types can't: which component, when, gotchas, and real examples.\n- A component's curation lists the high-value inherited MUI props worth knowing (`curation.mui`, e.g. `color`/`variant`/`size`); the rest of MUI's surface is available but rarely needed.\n- Don't use `sx`/inline styles to override a Meridian component's visual design (colors, typography, borders, sizing) — use its documented props and the theme tokens. (`sx` for layout/spacing on primitives is fine — see Styling.)\n- If no prop covers the visual change you need, treat it as a design-system gap: request the variant/behavior upstream in Meridian rather than permanently overriding the component locally — a local `styled()` of a Meridian component is a smell, not a fix.\n\n## Styling\n\n- Use theme tokens, never hardcoded values: colors from the palette (`text.*`, `background.*`, `divider`, semantic `*.main`) and spacing from the theme spacing scale — no raw hex/rgb or pixel literals for color or spacing. Use integer steps on the spacing scale (`theme.spacing(n)`, or `sx` numeric spacing like `px: 2` / `mt: 3` which resolve to it) — not fractional steps or raw px. Get the exact values — palette keys, spacing base, type ramp, `shape.borderRadius`, breakpoints, shadows — from `meridian_get_tokens`.\n- Text uses `<Typography variant=…>` (with the `weight` prop for emphasis), never hardcoded `fontSize`/`fontWeight`/`lineHeight`/`fontFamily`.\n- Meridian's type scale is custom and does not match MUI defaults (e.g. `body2`/`caption` differ from MUI's sizes, and there are Meridian-only variants like `code1`/`code2`/`code3` and `overlineDelicate`). Pick the variant by role from Typography's variant list (`meridian_get_examples('Typography')`) — don't assume a px size or map px → variant yourself.\n- `sx` is the right tool for one-off layout/spacing on primitives (`Box`/`Grid`/`Stack`), not the deprecated MUI system props\n (`<Box px={2}>` → `<Box sx={{ px: 2 }}>`). This is layout, distinct from restyling a Meridian component (see Props).\n\n## Examples\n\n- Never invent example code when a story exists — use the component's real story source (`meridian_get_examples`). The lead Usage story is the copy-paste starting point; the later stories are illustrative (variants, edge cases) and may carry story-arg shells, so don't copy one wholesale as your baseline.\n",
2
+ "version": "4.2.0",
3
+ "conventions": "# Meridian usage conventions (for code-generating agents)\n\nSystem-level rules an agent should apply across all components.\n\nMeridian is the source of truth: when an existing codebase pattern contradicts Meridian, follow Meridian — don't replicate the contradicting local pattern.\n\n## Choosing a component\n\n- Don't hand-roll what the design system already provides. Before writing a\n custom component, a styled wrapper, or a block of primitives (a modal,\n chip/tag, card, empty state, dropdown, tooltip, copy-to-clipboard input,\n etc.), confirm no Meridian component covers it — reuse or extend the existing one instead of rebuilding it.\n- Prefer a Meridian component over a bare MUI one whenever a Meridian wrapper\n exists — the wrappers carry CARTO theming, a11y defaults, and narrowed APIs. MUI is correct for layout primitives (`Box`/`Grid`/`Stack`/`Container`) and for gaps Meridian doesn't fill (e.g. date pickers); \"prefer Meridian\" is not \"never MUI\".\n- Resolve \"which component\" by reading each one's `description` + `keywords`,\n then confirm with its `decisionTree` (the conditions that point elsewhere) and `limitations` (what it lacks — check before you downgrade to it).\n Don't infer selection from prop lists.\n\n## Icons\n\n- Source icons from Meridian's custom-icons first, then `@mui/icons-material`;\n add a local SVG only when neither has an equivalent. Don't duplicate an icon\n the design system already ships.\n- Find the right custom icon with `meridian_list_icons` — match by name or keyword (e.g. \"warehouse\" → `Bigquery`), import it from `@carto/meridian-ds/custom-icons`, and render `<Name />`. Every icon shares one prop type, `CustomIconProps` (MUI `SvgIconProps` — `color`/`fontSize`/`sx` — plus `width`/`height`).\n- To confirm a shortlisted icon by sight (or match an icon in a design you're implementing), call `meridian_get_screenshot` with `name: 'icons'` and Read the returned image path — a labeled grid of every icon.\n- Use the standard icon sizes — small 12px, medium (default) 18px, large 24px;\n a value off this scale (e.g. 16, 20) needs design sign-off. Prefer the\n outlined variants.\n- A new SVG must inherit, not bake in: set `fill='currentColor'` so it takes\n the parent's color, and use the standard size — don't hardcode\n color/opacity/dimensions.\n- For an icon color outside the palette `color` set, pass a theme token via\n `htmlColor` (e.g. `htmlColor={theme.palette.text.secondary}`) — never a\n hardcoded hex.\n\n## Accessibility\n\n- Any icon-only control needs an accessible name: `tooltip` or `aria-label`.\n- Don't disable a control without telling the user why — pair a disabled action with a tooltip explaining the requirement.\n- An `aria-label` must be human-readable text describing the control, not a test id in disguise — use a separate test hook for that.\n- User-facing strings (labels, `aria-label`, placeholders, messages) go through the app's i18n layer, not hardcoded literals.\n\n## Props\n\n- Two sources, both pinned to the installed `@carto/meridian-ds`: **exact props / types / JSDoc** come from the package's **type declarations** — read the `<Name>.d.ts` directly — while the **`meridian-design-system` MCP** (`meridian_*` tools) covers what types can't: which component, when, gotchas, and real examples.\n- A component's curation lists the high-value inherited MUI props worth knowing (`curation.mui`, e.g. `color`/`variant`/`size`); the rest of MUI's surface is available but rarely needed.\n- Don't use `sx`/inline styles to override a Meridian component's visual design (colors, typography, borders, sizing) — use its documented props and the theme tokens. (`sx` for layout/spacing on primitives is fine — see Styling.)\n- If no prop covers the visual change you need, treat it as a design-system gap: request the variant/behavior upstream in Meridian rather than permanently overriding the component locally — a local `styled()` of a Meridian component is a smell, not a fix.\n\n## Styling\n\n- Use theme tokens, never hardcoded values: colors from the palette (`text.*`, `background.*`, `divider`, semantic `*.main`) and spacing from the theme spacing scale — no raw hex/rgb or pixel literals for color or spacing. Use integer steps on the spacing scale (`theme.spacing(n)`, or `sx` numeric spacing like `px: 2` / `mt: 3` which resolve to it) — not fractional steps or raw px. Get the exact values — palette keys, spacing base, type ramp, `shape.borderRadius`, breakpoints, shadows — from `meridian_get_tokens`.\n- Text uses `<Typography variant=…>` (with the `weight` prop for emphasis), never hardcoded `fontSize`/`fontWeight`/`lineHeight`/`fontFamily`.\n- Meridian's type scale is custom and does not match MUI defaults (e.g. `body2`/`caption` differ from MUI's sizes, and there are Meridian-only variants like `code1`/`code2`/`code3` and `overlineDelicate`). Pick the variant by role from Typography's variant list (`meridian_get_examples('Typography')`) — don't assume a px size or map px → variant yourself.\n- `sx` is the right tool for one-off layout/spacing on primitives (`Box`/`Grid`/`Stack`), not the deprecated MUI system props\n (`<Box px={2}>` → `<Box sx={{ px: 2 }}>`). This is layout, distinct from restyling a Meridian component (see Props).\n\n## Examples\n\n- Never invent example code when a story exists — use the component's real story source (`meridian_get_examples`). The lead Usage story is the copy-paste starting point; the later stories are illustrative (variants, edge cases) and may carry story-arg shells, so don't copy one wholesale as your baseline.\n",
4
4
  "components": [
5
5
  {
6
6
  "name": "AccordionGroup",
@@ -6683,8 +6683,11 @@
6683
6683
  "usage": "Elevation 0–24; use the component elevation prop or theme.shadows[n], not custom box-shadows."
6684
6684
  }
6685
6685
  },
6686
- "iconsScreenshot": {
6687
- "file": "icons-screenshot.png",
6688
- "iconCount": 270
6686
+ "screenshots": {
6687
+ "icons": {
6688
+ "file": "icons-screenshot.webp",
6689
+ "count": 270,
6690
+ "hint": "Read this image; each glyph is labeled with its import name. Use it to confirm an icon shortlisted via meridian_list_icons, or to match one from a design."
6691
+ }
6689
6692
  }
6690
6693
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carto/meridian-ds",
3
- "version": "4.1.1-alpha.e6399e0.293",
3
+ "version": "4.2.0",
4
4
  "description": "CARTO Meridian Design System",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -16,12 +16,12 @@
16
16
  "test:visual": "bash scripts/test-visual.sh",
17
17
  "test:visual:update": "bash scripts/test-visual.sh --update-snapshots && yarn test:visual:check-orphans --delete",
18
18
  "test:visual:check-orphans": "tsx scripts/check-orphan-visual-baselines.ts",
19
- "coverage": "LC_ALL=\"en_US.UTF-8\" vitest --coverage",
20
- "coverage:ui": "LC_ALL=\"en_US.UTF-8\" vitest --coverage --ui",
19
+ "test:coverage": "LC_ALL=\"en_US.UTF-8\" vitest --coverage",
20
+ "test:coverage:ui": "LC_ALL=\"en_US.UTF-8\" vitest --coverage --ui",
21
21
  "metadata:build": "tsx ai-metadata/pipeline/build-orchestrator.ts",
22
22
  "metadata:validate": "tsx ai-metadata/pipeline/validate-metadata.ts",
23
23
  "metadata:validate-mcp": "tsx ai-metadata/mcp/validate-mcp.ts",
24
- "metadata:icons-screenshot": "tsx scripts/render-icons-screenshot.ts",
24
+ "metadata:screenshots": "tsx scripts/render-screenshots.ts",
25
25
  "changelog:detect": "node scripts/changelog-autodetect.cjs",
26
26
  "mcp:list_components": "node ai-metadata/mcp/call-tool.mjs list_components",
27
27
  "mcp:list_icons": "node ai-metadata/mcp/call-tool.mjs list_icons",
@@ -29,6 +29,7 @@
29
29
  "mcp:get_examples": "node ai-metadata/mcp/call-tool.mjs get_examples",
30
30
  "mcp:get_conventions": "node ai-metadata/mcp/call-tool.mjs get_conventions",
31
31
  "mcp:get_tokens": "node ai-metadata/mcp/call-tool.mjs get_tokens",
32
+ "mcp:get_screenshot": "node ai-metadata/mcp/call-tool.mjs get_screenshot icons",
32
33
  "lint": "eslint . --ext .ts,.tsx",
33
34
  "format": "prettier --write .",
34
35
  "ts-check": "tsc -p tsconfig.json --noEmit",
Binary file