@testgorilla/tgo-ui 8.10.0 → 8.11.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/mcp/server.mjs CHANGED
@@ -2230,8 +2230,8 @@ var require_resolve = __commonJS({
2230
2230
  }
2231
2231
  return count;
2232
2232
  }
2233
- function getFullPath(resolver, id = "", normalize) {
2234
- if (normalize !== false)
2233
+ function getFullPath(resolver, id = "", normalize2) {
2234
+ if (normalize2 !== false)
2235
2235
  id = normalizeId(id);
2236
2236
  const p = resolver.parse(id);
2237
2237
  return _getFullPath(resolver, p);
@@ -3627,7 +3627,7 @@ var require_fast_uri = __commonJS({
3627
3627
  "use strict";
3628
3628
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3629
3629
  var { SCHEMES, getSchemeHandler } = require_schemes();
3630
- function normalize(uri, options) {
3630
+ function normalize2(uri, options) {
3631
3631
  if (typeof uri === "string") {
3632
3632
  uri = /** @type {T} */
3633
3633
  normalizeString(uri, options);
@@ -3894,7 +3894,7 @@ var require_fast_uri = __commonJS({
3894
3894
  }
3895
3895
  var fastUri = {
3896
3896
  SCHEMES,
3897
- normalize,
3897
+ normalize: normalize2,
3898
3898
  resolve,
3899
3899
  resolveComponent,
3900
3900
  equal,
@@ -21581,6 +21581,202 @@ function registerGetQueryGuideTool(server, store) {
21581
21581
  );
21582
21582
  }
21583
21583
 
21584
+ // src/tools/list-stories-with-design-links.ts
21585
+ function handleListStoriesWithDesignLinks(store) {
21586
+ const rows = [];
21587
+ for (const c of store.getAll()) {
21588
+ const examplesWithUrl = c.examples.filter((e) => e.designUrl);
21589
+ const metaUrl = c.designUrl ?? "";
21590
+ if (examplesWithUrl.length > 0) {
21591
+ for (const ex of c.examples) {
21592
+ const url = ex.designUrl ?? metaUrl;
21593
+ if (!url) continue;
21594
+ rows.push({
21595
+ component: deriveComponentDir(c.importPath),
21596
+ componentName: c.name,
21597
+ selector: c.selector,
21598
+ storyName: ex.name,
21599
+ designUrl: url
21600
+ });
21601
+ }
21602
+ } else if (metaUrl) {
21603
+ rows.push({
21604
+ component: deriveComponentDir(c.importPath),
21605
+ componentName: c.name,
21606
+ selector: c.selector,
21607
+ storyName: "",
21608
+ designUrl: metaUrl
21609
+ });
21610
+ }
21611
+ }
21612
+ rows.sort((a, b) => {
21613
+ const byComp = a.component.localeCompare(b.component);
21614
+ return byComp !== 0 ? byComp : a.storyName.localeCompare(b.storyName);
21615
+ });
21616
+ return rows;
21617
+ }
21618
+ function deriveComponentDir(importPath) {
21619
+ const idx = importPath.lastIndexOf("/");
21620
+ return idx === -1 ? importPath : importPath.slice(idx + 1);
21621
+ }
21622
+ function registerListStoriesWithDesignLinksTool(server, store) {
21623
+ server.tool(
21624
+ "list_stories_with_design_links",
21625
+ "List every Storybook story that has a Figma design URL (from `parameters.design.url`). Returns flat rows of {component, componentName, selector, storyName, designUrl}. When a story has no per-story URL but the component has a meta-level URL, the meta URL applies. Components with no design URL at any level are omitted. `storyName` is empty when only a meta-level URL is known (i.e. applies to all stories).",
21626
+ {},
21627
+ async () => ({
21628
+ content: [{ type: "text", text: JSON.stringify(handleListStoriesWithDesignLinks(store), null, 2) }]
21629
+ })
21630
+ );
21631
+ }
21632
+
21633
+ // src/tools/get-icon-for-figma-name.ts
21634
+ function registerGetIconForFigmaNameTool(server, resolver) {
21635
+ server.tool(
21636
+ "get_icon_for_figma_name",
21637
+ 'Resolve a Figma icon name (as exported by Figma Dev Mode) to a Canopy `<ui-icon>` name + svg path. Returns null when no match is found at any level. The `match` field tells you how the resolution happened: "exact" (after case/punctuation normalization), "alias" (manual entry in mcp/icon-aliases.json), or "fuzzy" (substring match \u2014 verify visually before relying on it).',
21638
+ {
21639
+ figmaIconName: external_exports.string().describe('Icon name from Figma (e.g., "checkmark", "Arrow-down", "Eye Open"). Case- and punctuation-insensitive.')
21640
+ },
21641
+ async ({ figmaIconName }) => {
21642
+ const result = resolver.resolve(figmaIconName);
21643
+ return {
21644
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
21645
+ };
21646
+ }
21647
+ );
21648
+ }
21649
+
21650
+ // src/tools/get-design-tokens.ts
21651
+ function handleGetDesignTokens(tokens, params) {
21652
+ if (!params.category) return tokens;
21653
+ const wanted = params.category.toLowerCase();
21654
+ return tokens.filter((t) => t.category === wanted);
21655
+ }
21656
+ function registerGetDesignTokensTool(server, tokens) {
21657
+ server.tool(
21658
+ "get_design_tokens",
21659
+ "Return Canopy design tokens parsed from `theme/_variables.scss` (colors, breakpoints, shadows, etc.). Each token is `{ name, value, category }`. Call without `category` first to discover the available buckets, then re-call with a filter to narrow.",
21660
+ {
21661
+ category: external_exports.string().optional().describe('Optional category filter (e.g., "color", "breakpoint", "shadow", "font", "size"). Case-insensitive.')
21662
+ },
21663
+ async ({ category }) => ({
21664
+ content: [{ type: "text", text: JSON.stringify(handleGetDesignTokens(tokens, { category }), null, 2) }]
21665
+ })
21666
+ );
21667
+ }
21668
+
21669
+ // src/tools/list-typography-classes.ts
21670
+ function handleListTypographyClasses(classes) {
21671
+ return classes;
21672
+ }
21673
+ function registerListTypographyClassesTool(server, classes) {
21674
+ server.tool(
21675
+ "list_typography_classes",
21676
+ 'List Canopy typography styles available app-wide WITHOUT requiring a component (h1\u2013h6, .body-large, .body-small-regular, .featured, .caption, .hyperlink, .button-label, etc.). Use this BEFORE flagging text spans as missing components \u2014 a `<h1>` or `<p class="body-large">` is part of the design system, not a gap. Each entry returns `{ name, selector, source, properties }`; `properties` has font-size, line-height, font-weight, font-family, text-transform, color when present.',
21677
+ {},
21678
+ async () => ({
21679
+ content: [{ type: "text", text: JSON.stringify(handleListTypographyClasses(classes), null, 2) }]
21680
+ })
21681
+ );
21682
+ }
21683
+
21684
+ // src/tools/list-color-palettes.ts
21685
+ function handleListColorPalettes(palettes, params) {
21686
+ if (!params.palette) return palettes;
21687
+ const wanted = params.palette.toLowerCase();
21688
+ return palettes.filter(
21689
+ (p) => p.name.toLowerCase() === wanted || p.name.toLowerCase().includes(wanted)
21690
+ );
21691
+ }
21692
+ function registerListColorPalettesTool(server, palettes) {
21693
+ server.tool(
21694
+ "list_color_palettes",
21695
+ "List semantic color palettes from Canopy Foundations (BrandColors, RebrandInformativeColors, RebrandSuccessColors, RebrandGrayscaleColors, AiColors, etc.). Use this when you need to know which Canopy color is intended for a given role (brand / informative / success / error / warning / neutral / AI). Each palette returns `{ name, displayName, colors: [{ variable, cssClass, hex }, ...] }`. For raw SCSS variables without grouping, use `get_design_tokens` instead.",
21696
+ {
21697
+ palette: external_exports.string().optional().describe('Optional palette filter (case-insensitive, substring match). Example: "brand", "success", "rebrand".')
21698
+ },
21699
+ async ({ palette }) => ({
21700
+ content: [{ type: "text", text: JSON.stringify(handleListColorPalettes(palettes, { palette }), null, 2) }]
21701
+ })
21702
+ );
21703
+ }
21704
+
21705
+ // src/icons/icon-resolver.ts
21706
+ var MIN_FUZZY_QUERY_LENGTH = 4;
21707
+ var IconResolver = class {
21708
+ byNormalized = /* @__PURE__ */ new Map();
21709
+ aliasMap;
21710
+ unmapped;
21711
+ icons;
21712
+ constructor(icons, aliases = {}, unmapped = []) {
21713
+ this.icons = icons;
21714
+ for (const icon of icons) {
21715
+ this.byNormalized.set(normalize(icon.name), icon);
21716
+ }
21717
+ this.aliasMap = /* @__PURE__ */ new Map();
21718
+ for (const [from, to] of Object.entries(aliases)) {
21719
+ this.aliasMap.set(normalize(from), to);
21720
+ }
21721
+ this.unmapped = new Set(unmapped.map(normalize));
21722
+ }
21723
+ resolve(figmaName) {
21724
+ const normalized = normalize(figmaName);
21725
+ if (!normalized) return null;
21726
+ const exact = this.byNormalized.get(normalized);
21727
+ if (exact) {
21728
+ return { canopyIconName: exact.name, svgPath: exact.svgPath, match: "exact" };
21729
+ }
21730
+ const aliasTarget = this.aliasMap.get(normalized);
21731
+ if (aliasTarget) {
21732
+ const target = this.byNormalized.get(normalize(aliasTarget));
21733
+ if (target) {
21734
+ return { canopyIconName: target.name, svgPath: target.svgPath, match: "alias" };
21735
+ }
21736
+ }
21737
+ if (this.unmapped.has(normalized)) return null;
21738
+ if (normalized.length >= MIN_FUZZY_QUERY_LENGTH) {
21739
+ const best = this.bestFuzzyMatch(normalized);
21740
+ if (best) {
21741
+ return { canopyIconName: best.name, svgPath: best.svgPath, match: "fuzzy" };
21742
+ }
21743
+ }
21744
+ return null;
21745
+ }
21746
+ /**
21747
+ * Pick the best fuzzy candidate rather than the first one in iteration order.
21748
+ * Among icons whose normalized name contains (or is contained by) the query,
21749
+ * rank by: prefix match over mid-substring, then smallest length delta, then
21750
+ * alphabetical for stable results as the icon set grows. This stops `arrow`
21751
+ * from always landing on whichever icon happens to sort first.
21752
+ */
21753
+ bestFuzzyMatch(query) {
21754
+ let best = null;
21755
+ for (const icon of this.icons) {
21756
+ const candidate = normalize(icon.name);
21757
+ if (candidate === query) continue;
21758
+ if (!candidate.includes(query) && !query.includes(candidate)) continue;
21759
+ const isPrefix = candidate.startsWith(query) || query.startsWith(candidate) ? 0 : 1;
21760
+ const rank = [isPrefix, Math.abs(candidate.length - query.length), candidate];
21761
+ if (best === null || compareFuzzyRank(rank, best.rank) < 0) {
21762
+ best = { icon, rank };
21763
+ }
21764
+ }
21765
+ return best?.icon ?? null;
21766
+ }
21767
+ };
21768
+ function compareFuzzyRank(a, b) {
21769
+ if (a[0] !== b[0]) return a[0] - b[0];
21770
+ if (a[1] !== b[1]) return a[1] - b[1];
21771
+ return a[2].localeCompare(b[2]);
21772
+ }
21773
+ function stripSvgExtension(name) {
21774
+ return name.toLowerCase().endsWith(".svg") ? name.slice(0, -".svg".length) : name;
21775
+ }
21776
+ function normalize(name) {
21777
+ return stripSvgExtension(name).toLowerCase().replace(/[^a-z0-9]/g, "");
21778
+ }
21779
+
21584
21780
  // src/server-lite.ts
21585
21781
  var CatalogSchema = external_exports.object({
21586
21782
  version: external_exports.string(),
@@ -21595,7 +21791,24 @@ var CatalogSchema = external_exports.object({
21595
21791
  outputs: external_exports.array(external_exports.any()),
21596
21792
  types: external_exports.any(),
21597
21793
  queryGuide: external_exports.any()
21598
- }).passthrough())
21794
+ }).passthrough()),
21795
+ // Phase G additions. Defaulted so catalogs published before v8.1 still parse cleanly.
21796
+ icons: external_exports.array(external_exports.object({ name: external_exports.string(), svgPath: external_exports.string() })).optional().default([]),
21797
+ iconAliases: external_exports.record(external_exports.string()).optional().default({}),
21798
+ iconUnmapped: external_exports.array(external_exports.string()).optional().default([]),
21799
+ tokens: external_exports.array(external_exports.object({ name: external_exports.string(), value: external_exports.string(), category: external_exports.string() })).optional().default([]),
21800
+ // Foundations additions (Phase G.1.d). Same default-to-empty pattern.
21801
+ typography: external_exports.array(external_exports.object({
21802
+ name: external_exports.string(),
21803
+ selector: external_exports.string(),
21804
+ source: external_exports.string(),
21805
+ properties: external_exports.record(external_exports.string())
21806
+ })).optional().default([]),
21807
+ colorPalettes: external_exports.array(external_exports.object({
21808
+ name: external_exports.string(),
21809
+ displayName: external_exports.string(),
21810
+ colors: external_exports.array(external_exports.object({ variable: external_exports.string(), cssClass: external_exports.string(), hex: external_exports.string() }))
21811
+ })).optional().default([])
21599
21812
  });
21600
21813
  function loadCatalog(catalog) {
21601
21814
  const store = new CatalogStore();
@@ -21616,6 +21829,12 @@ function createStaticMcpServer(catalog) {
21616
21829
  registerGetUsageGuideTool(server, store);
21617
21830
  registerGetComponentTypesTool(server, store);
21618
21831
  registerGetQueryGuideTool(server, store);
21832
+ registerListStoriesWithDesignLinksTool(server, store);
21833
+ const iconResolver = new IconResolver(catalog.icons ?? [], catalog.iconAliases ?? {}, catalog.iconUnmapped ?? []);
21834
+ registerGetIconForFigmaNameTool(server, iconResolver);
21835
+ registerGetDesignTokensTool(server, catalog.tokens ?? []);
21836
+ registerListTypographyClassesTool(server, catalog.typography ?? []);
21837
+ registerListColorPalettesTool(server, catalog.colorPalettes ?? []);
21619
21838
  return server;
21620
21839
  }
21621
21840
  var isMainScript = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("server-lite.js") || process.argv[1]?.endsWith("server.mjs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testgorilla/tgo-ui",
3
- "version": "8.10.0",
3
+ "version": "8.11.1",
4
4
  "license": "proprietary-license",
5
5
  "lint-staged": {
6
6
  "{projects,components}/**/*.ts": [
@@ -64,7 +64,8 @@
64
64
  "postcss": "8.5.12",
65
65
  "uuid": "^11.1.1",
66
66
  "fast-uri": "^3.1.2",
67
- "webpack-dev-server": "^5.2.4"
67
+ "webpack-dev-server": "^5.2.4",
68
+ "shell-quote": "^1.8.4"
68
69
  },
69
70
  "module": "fesm2022/testgorilla-tgo-ui.mjs",
70
71
  "typings": "index.d.ts",
@@ -87,7 +87,7 @@ p,
87
87
  .caption {
88
88
  .semibold {
89
89
  font-weight: 600;
90
- color: #46a997;
90
+ color: $brand-50;
91
91
  }
92
92
  }
93
93