@primer/brand-mcp 0.69.0 → 0.70.0-rc.9ef718be

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.
Files changed (50) hide show
  1. package/README.md +10 -9
  2. package/content/page-design.md +76 -0
  3. package/dist/brand/detect-asset-generator.d.ts +6 -0
  4. package/dist/brand/detect-asset-generator.js +21 -0
  5. package/dist/brand/detect-asset-generator.js.map +1 -0
  6. package/dist/brand/detect-framework.d.ts +3 -4
  7. package/dist/brand/detect-framework.js +18 -21
  8. package/dist/brand/detect-framework.js.map +1 -1
  9. package/dist/brand/docs-source.d.ts +8 -20
  10. package/dist/brand/docs-source.js +38 -41
  11. package/dist/brand/docs-source.js.map +1 -1
  12. package/dist/brand/resolve-assets.d.ts +4 -5
  13. package/dist/brand/resolve-assets.js +60 -68
  14. package/dist/brand/resolve-assets.js.map +1 -1
  15. package/dist/brand/resolve-install.d.ts +2 -4
  16. package/dist/brand/resolve-install.js +10 -10
  17. package/dist/brand/resolve-install.js.map +1 -1
  18. package/dist/catalog/load.js +0 -1
  19. package/dist/catalog/load.js.map +1 -1
  20. package/dist/catalog/types.d.ts +24 -14
  21. package/dist/catalog/types.js +2 -2
  22. package/dist/catalog/types.js.map +1 -1
  23. package/dist/catalog.json +105 -2
  24. package/dist/logger.d.ts +2 -2
  25. package/dist/review/rules.js +27 -0
  26. package/dist/review/rules.js.map +1 -1
  27. package/dist/review/types.d.ts +4 -4
  28. package/dist/server.js +13 -2
  29. package/dist/server.js.map +1 -1
  30. package/dist/tools/primer-brand-asset/primer-brand-asset.js +12 -3
  31. package/dist/tools/primer-brand-asset/primer-brand-asset.js.map +1 -1
  32. package/dist/tools/primer-brand-examples/primer-brand-examples.js +23 -4
  33. package/dist/tools/primer-brand-examples/primer-brand-examples.js.map +1 -1
  34. package/dist/tools/primer-brand-page-design/index.d.ts +1 -0
  35. package/dist/tools/primer-brand-page-design/index.js +2 -0
  36. package/dist/tools/primer-brand-page-design/index.js.map +1 -0
  37. package/dist/tools/primer-brand-page-design/primer-brand-page-design.d.ts +6 -0
  38. package/dist/tools/primer-brand-page-design/primer-brand-page-design.js +19 -0
  39. package/dist/tools/primer-brand-page-design/primer-brand-page-design.js.map +1 -0
  40. package/dist/tools/primer-brand-review/primer-brand-review.js +23 -19
  41. package/dist/tools/primer-brand-review/primer-brand-review.js.map +1 -1
  42. package/dist/tools/primer-brand-setup/primer-brand-setup.js +1 -1
  43. package/dist/tools/primer-brand-setup/primer-brand-setup.js.map +1 -1
  44. package/dist/tools/primer-brand-tokens/primer-brand-tokens.js +38 -29
  45. package/dist/tools/primer-brand-tokens/primer-brand-tokens.js.map +1 -1
  46. package/dist/tools/register.js +3 -2
  47. package/dist/tools/register.js.map +1 -1
  48. package/dist/tools/types.d.ts +11 -12
  49. package/dist/util/text.d.ts +2 -2
  50. package/package.json +2 -1
@@ -14,6 +14,16 @@ const notFound = {
14
14
  */
15
15
  export function resolveBrandInstall(fromDir = process.cwd()) {
16
16
  let dir = fromDir;
17
+ const readVersion = (packageJsonPath) => {
18
+ try {
19
+ const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
20
+ return parsed.version ?? null;
21
+ }
22
+ catch {
23
+ // Unreadable manifest -> treat the version as unknown rather than failing discovery.
24
+ return null;
25
+ }
26
+ };
17
27
  for (;;) {
18
28
  const packageDir = join(dir, 'node_modules', '@primer', 'react-brand');
19
29
  if (existsSync(join(packageDir, 'package.json'))) {
@@ -50,14 +60,4 @@ export function looksLikeProductProject(fromDir = process.cwd()) {
50
60
  dir = parent;
51
61
  }
52
62
  }
53
- function readVersion(packageJsonPath) {
54
- try {
55
- const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
56
- return parsed.version ?? null;
57
- }
58
- catch {
59
- // Unreadable manifest -> treat the version as unknown rather than failing discovery.
60
- return null;
61
- }
62
- }
63
63
  //# sourceMappingURL=resolve-install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-install.js","sourceRoot":"","sources":["../../src/brand/resolve-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAChD,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AAiBvC,MAAM,QAAQ,GAAiB;IAC7B,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,IAAI;CACf,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IACjE,IAAI,GAAG,GAAG,OAAO,CAAA;IACjB,SAAS,CAAC;QACR,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,CAAC,CAAA;QACtE,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;YAC7C,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;gBACtD,UAAU;gBACV,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAC7C,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;aACjD,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAA;QACnC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IACrE,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACpD,IAAI,GAAG,GAAG,OAAO,CAAA;IACjB,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,KAAK,CAAA;QAChC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,eAAuB;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAuB,CAAA;QACtF,OAAO,MAAM,CAAC,OAAO,IAAI,IAAI,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;QACrF,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"resolve-install.js","sourceRoot":"","sources":["../../src/brand/resolve-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAChD,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,WAAW,CAAA;AAevC,MAAM,QAAQ,GAAiB;IAC7B,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,IAAI;CACf,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IACjE,IAAI,GAAG,GAAG,OAAO,CAAA;IAEjB,MAAM,WAAW,GAAG,CAAC,eAAuB,EAAiB,EAAE;QAC7D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAuB,CAAA;YACtF,OAAO,MAAM,CAAC,OAAO,IAAI,IAAI,CAAA;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,qFAAqF;YACrF,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,CAAA;IAED,SAAS,CAAC;QACR,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,CAAC,CAAA;QACtE,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;YAC7C,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;gBACtD,UAAU;gBACV,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAC7C,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;aACjD,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,QAAQ,CAAA;QACnC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB,OAAO,CAAC,GAAG,EAAE;IACrE,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACpD,IAAI,GAAG,GAAG,OAAO,CAAA;IACjB,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,KAAK,CAAA;QAChC,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC"}
@@ -2,7 +2,6 @@ import { readFileSync } from 'node:fs';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { emptyCatalog } from './types.js';
4
4
  export function loadCatalog(logger) {
5
- // Output next to the compiled output (`dist/catalog.json`) by the build-time generator.
6
5
  const path = fileURLToPath(new URL('../catalog.json', import.meta.url));
7
6
  try {
8
7
  const parsed = JSON.parse(readFileSync(path, 'utf8'));
@@ -1 +1 @@
1
- {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/catalog/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAGtC,OAAO,EAAe,YAAY,EAAC,MAAM,YAAY,CAAA;AAErD,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,wFAAwF;IACxF,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAA;QAChE,MAAM,CAAC,KAAK,CACV,mBAAmB,MAAM,CAAC,UAAU,CAAC,MAAM,mDAAmD,MAAM,CAAC,oBAAoB,GAAG,CAC7H,CAAA;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,6BAA6B,IAAI,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAA;QAC7E,iDAAiD;QACjD,OAAO,YAAY,EAAE,CAAA;IACvB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/catalog/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAGtC,OAAO,EAAe,YAAY,EAAC,MAAM,YAAY,CAAA;AAErD,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACvE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAA;QAChE,MAAM,CAAC,KAAK,CACV,mBAAmB,MAAM,CAAC,UAAU,CAAC,MAAM,mDAAmD,MAAM,CAAC,oBAAoB,GAAG,CAC7H,CAAA;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,6BAA6B,IAAI,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAA;QAC7E,iDAAiD;QACjD,OAAO,YAAY,EAAE,CAAA;IACvB,CAAC;AACH,CAAC"}
@@ -1,8 +1,7 @@
1
1
  /**
2
- * All types associated with the build-generated catalog that ships inside the package.
3
- * It is the structured, offline source of truth the tools reason over so they never depend on the network for version-sensitive facts.
2
+ * All types relating to the Catalog
4
3
  */
5
- export interface CatalogProp {
4
+ export type CatalogProp = {
6
5
  name: string;
7
6
  type?: string;
8
7
  /** Allowed string-literal values, when the prop is an enum. */
@@ -11,8 +10,8 @@ export interface CatalogProp {
11
10
  default?: string;
12
11
  /** One-line prop description from its JSDoc, when documented in source. */
13
12
  description?: string;
14
- }
15
- export interface CatalogExample {
13
+ };
14
+ export type CatalogExample = {
16
15
  title: string;
17
16
  /** Source snippet (JSX), when one could be extracted. */
18
17
  code?: string;
@@ -20,8 +19,8 @@ export interface CatalogExample {
20
19
  source: 'jsdoc' | 'story' | 'docs';
21
20
  /** Storybook story id, when the example comes from a story. */
22
21
  storyId?: string;
23
- }
24
- export interface CatalogComponent {
22
+ };
23
+ export type CatalogComponent = {
25
24
  /** Public name, e.g. `Hero`. */
26
25
  name: string;
27
26
  /** Package the component is imported from. */
@@ -36,18 +35,28 @@ export interface CatalogComponent {
36
35
  keywords?: string[];
37
36
  /** A usage caveat from the component's JSDoc `@remarks` (e.g. a non-obvious prop interaction). */
38
37
  note?: string;
39
- }
40
- export interface CatalogAsset {
38
+ };
39
+ export type CatalogAsset = {
41
40
  name: string;
42
41
  module: string;
43
42
  kind: 'icon' | 'illustration';
44
- }
45
- export interface CatalogToken {
43
+ };
44
+ export type CatalogToken = {
46
45
  name: string;
47
46
  value: string;
48
47
  group: string;
49
- }
50
- export interface Catalog {
48
+ };
49
+ export type CatalogRecipe = {
50
+ /** The export/file name, e.g. `FlexSuiteAIOverview`. */
51
+ name: string;
52
+ /** A human-readable label for ranking and display, e.g. `Overview / landing page`. */
53
+ title: string;
54
+ /** Page-level keywords used to match a build goal. */
55
+ keywords: string[];
56
+ /** The recipe's full source `.tsx`. */
57
+ source: string;
58
+ };
59
+ export type Catalog = {
51
60
  /** Package whose API this catalog describes. */
52
61
  brandPackage: string;
53
62
  /** Version of `@primer/react-brand` the catalog was generated from. */
@@ -56,5 +65,6 @@ export interface Catalog {
56
65
  components: CatalogComponent[];
57
66
  assets: CatalogAsset[];
58
67
  tokens: CatalogToken[];
59
- }
68
+ recipes: CatalogRecipe[];
69
+ };
60
70
  export declare function emptyCatalog(): Catalog;
@@ -1,6 +1,5 @@
1
1
  /**
2
- * All types associated with the build-generated catalog that ships inside the package.
3
- * It is the structured, offline source of truth the tools reason over so they never depend on the network for version-sensitive facts.
2
+ * All types relating to the Catalog
4
3
  */
5
4
  export function emptyCatalog() {
6
5
  return {
@@ -10,6 +9,7 @@ export function emptyCatalog() {
10
9
  components: [],
11
10
  assets: [],
12
11
  tokens: [],
12
+ recipes: [],
13
13
  };
14
14
  }
15
15
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/catalog/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+DH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,YAAY,EAAE,qBAAqB;QACnC,oBAAoB,EAAE,SAAS;QAC/B,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QACtC,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;KACX,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/catalog/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4EH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,YAAY,EAAE,qBAAqB;QACnC,oBAAoB,EAAE,SAAS;QAC/B,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QACtC,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;KACZ,CAAA;AACH,CAAC"}
package/dist/catalog.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "brandPackage": "@primer/react-brand",
3
- "generatedFromVersion": "0.69.0",
4
- "generatedAt": "2026-06-26T11:48:44.672Z",
3
+ "generatedFromVersion": "0.70.0-rc.9ef718be",
4
+ "generatedAt": "2026-07-03T08:59:04.357Z",
5
5
  "components": [
6
6
  {
7
7
  "name": "Hero",
@@ -2663,6 +2663,49 @@
2663
2663
  "second level"
2664
2664
  ]
2665
2665
  },
2666
+ {
2667
+ "name": "NavList",
2668
+ "module": "@primer/react-brand",
2669
+ "subcomponents": [
2670
+ "NavList.Item",
2671
+ "NavList.SubNav",
2672
+ "NavList.Group",
2673
+ "NavList.testIds"
2674
+ ],
2675
+ "props": [
2676
+ {
2677
+ "name": "aria-label",
2678
+ "type": "string",
2679
+ "description": "Accessible label for the navigation landmark.",
2680
+ "required": true
2681
+ },
2682
+ {
2683
+ "name": "aria-labelledby",
2684
+ "type": "string",
2685
+ "description": "ID of an element that labels the navigation landmark.",
2686
+ "required": false
2687
+ },
2688
+ {
2689
+ "name": "children",
2690
+ "type": "ReactNode",
2691
+ "required": true
2692
+ }
2693
+ ],
2694
+ "examples": [
2695
+ {
2696
+ "title": "NavList example",
2697
+ "code": "<NavList aria-label={String(t('multipleLevelNavigation'))}>\n <NavList.Item defaultExpanded>\n {t('level1Group')}\n <NavList.SubNav>\n <NavList.Item href=\"#\">{t('level2Leaf')}</NavList.Item>\n <NavList.Item defaultExpanded>\n {t('level2Group')}\n <NavList.SubNav>\n <NavList.Item href=\"#\">{t('level3Leaf')}</NavList.Item>\n <NavList.Item defaultExpanded>\n {t('level3Group')}\n <NavList.SubNav>\n <NavList.Item href=\"#\">{t('level4Leaf')}</NavList.Item>\n <NavList.Item defaultExpanded>\n {t('level4Group')}\n <NavList.SubNav>\n <NavList.Item href=\"#\" aria-current=\"page\">\n {t('level5CurrentLeaf')}\n </NavList.Item>\n <NavList.Item href=\"#\">{t('level5SiblingLeaf')}</NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n </NavList.SubNav>\n </NavList.Item>\n</NavList>",
2698
+ "source": "story"
2699
+ }
2700
+ ],
2701
+ "description": "Use NavList to render vertical navigation links with groups and expandable nested lists.",
2702
+ "keywords": [
2703
+ "navigation",
2704
+ "vertical navigation",
2705
+ "sidebar",
2706
+ "nested navigation"
2707
+ ]
2708
+ },
2666
2709
  {
2667
2710
  "name": "IDE",
2668
2711
  "module": "@primer/react-brand",
@@ -8085,6 +8128,11 @@
8085
8128
  "value": "linear-gradient(180deg, rgba(242, 245, 243, 0.5) 0%, rgba(242, 245, 243, 0) 100%), var(--brand-color-canvas-default)",
8086
8129
  "group": "functional"
8087
8130
  },
8131
+ {
8132
+ "name": "--brand-NavList-activeIndicator-color",
8133
+ "value": "var(--brand-color-text-emphasized)",
8134
+ "group": "functional"
8135
+ },
8088
8136
  {
8089
8137
  "name": "--brand-PricingOptions-borderMask",
8090
8138
  "value": "linear-gradient(180deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.3))",
@@ -9960,5 +10008,60 @@
9960
10008
  "value": "var(--brand-text-display-weight) var(--brand-text-display-size)/var(--brand-text-display-lineHeight) var(--brand-fontStack-sansSerifDisplay)",
9961
10009
  "group": "functional"
9962
10010
  }
10011
+ ],
10012
+ "recipes": [
10013
+ {
10014
+ "name": "FlexSuiteAIDetailsPlaylist",
10015
+ "title": "Product feature details landing page",
10016
+ "keywords": [
10017
+ "product feature",
10018
+ "feature",
10019
+ "details",
10020
+ "detail",
10021
+ "landing page",
10022
+ "media",
10023
+ "video",
10024
+ "playlist",
10025
+ "watch",
10026
+ "full page",
10027
+ "template"
10028
+ ],
10029
+ "source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon, LogoGithubIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\nimport type {CSSProperties} from 'react'\n\nimport {\n Box,\n Button,\n Card,\n CTABanner,\n Grid,\n Heading,\n Hero,\n Image,\n InlineLink,\n MediaPlaylist,\n Section,\n SectionIntro,\n SubNav,\n Text,\n Token,\n} from '../../../..'\nimport {RedlineBackground} from '../../../../component-helpers'\n\nimport {\n defaultFlexSuiteAIDetailsPlaylistContent,\n type FlexSuiteAIDetailsPlaylistContent,\n} from './FlexSuiteAIDetailsPlaylist.content'\nimport placeholderImage from '../../../../fixtures/images/placeholder.png'\nimport overviewStyles from '../../Overview/FlexSuiteAIOverview.module.css'\n\nexport type FlexSuiteAIDetailsPlaylistTemplateProps = {\n content: FlexSuiteAIDetailsPlaylistContent\n}\n\nconst cardIcons = [CpuIcon, CodeOfConductIcon, LightBulbIcon] as const\nconst videoEmbedStyles: CSSProperties = {\n aspectRatio: '16 / 9',\n border: 0,\n display: 'block',\n width: '100%',\n}\n\nexport function FlexSuiteAIDetailsPlaylist() {\n return <FlexSuiteAIDetailsPlaylistTemplate content={defaultFlexSuiteAIDetailsPlaylistContent} />\n}\n\nexport function FlexSuiteAIDetailsPlaylistTemplate({content}: FlexSuiteAIDetailsPlaylistTemplateProps) {\n return (\n <Box className={overviewStyles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <Box style={{minHeight: '56px', position: 'relative'}}>\n <SubNav>\n <SubNav.Heading href=\"#\">{content.subNav.heading}</SubNav.Heading>\n\n {content.subNav.links.map((link, index) => (\n <SubNav.Link key={link} href=\"#\" aria-current={index === 4 ? 'page' : undefined}>\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n </Box>\n\n <Box\n backgroundColor=\"subtle\"\n borderInlineStartWidth=\"thin\"\n borderInlineEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n style={{maxInlineSize: 'var(--brand-breakpoint-xlarge)', marginInline: 'auto'}}\n >\n <Hero\n align=\"center\"\n trailingComponent={() => (\n <Box marginBlockStart={12}>\n <Text size=\"100\">\n {content.hero.secondaryText} <InlineLink href=\"#\">{content.hero.secondaryLinkText}</InlineLink>\n </Text>\n </Box>\n )}\n >\n <Hero.Heading>\n {content.hero.headingPrefix}\n <br /> <b>{content.hero.headingEmphasis}</b>\n </Hero.Heading>\n <Hero.Description>{content.hero.description}</Hero.Description>\n <Hero.PrimaryAction href=\"#\">{content.hero.primaryAction}</Hero.PrimaryAction>\n </Hero>\n </Box>\n\n {content.playlists.map((playlist, index) => (\n <Box\n key={playlist.heading}\n borderColor=\"muted\"\n borderStyle=\"solid\"\n borderBlockStartWidth={index === 0 ? 'thin' : 'none'}\n >\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\" fullWidth>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading size=\"3\">{playlist.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n <MediaPlaylist>\n <MediaPlaylist.Heading>{playlist.playlistHeading}</MediaPlaylist.Heading>\n\n {playlist.videos.map(video => (\n <MediaPlaylist.Item\n key={video.id}\n thumbnail={\n <Image\n src={\n process.env.NODE_ENV === 'test'\n ? placeholderImage\n : `https://i.ytimg.com/vi/${video.id}/hqdefault.jpg`\n }\n alt=\"\"\n aspectRatio=\"16:9\"\n loading=\"lazy\"\n />\n }\n >\n <MediaPlaylist.ItemHeading title={video.title} description={video.duration} />\n <MediaPlaylist.ItemContent>\n <Heading as=\"h3\" size=\"subhead-large\">\n {video.title}\n </Heading>\n <Text variant=\"muted\">{video.summary}</Text>\n </MediaPlaylist.ItemContent>\n <MediaPlaylist.ItemMedia>\n <iframe\n style={videoEmbedStyles}\n src={`https://www.youtube-nocookie.com/embed/${video.id}`}\n title={video.title}\n loading=\"lazy\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n referrerPolicy=\"strict-origin-when-cross-origin\"\n />\n </MediaPlaylist.ItemMedia>\n </MediaPlaylist.Item>\n ))}\n </MediaPlaylist>\n </Box>\n ))}\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\" fullWidth>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading size=\"3\">{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={overviewStyles.cardGridFrame}>\n <Box className={overviewStyles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(overviewStyles.cardGridColumn, overviewStyles.cardGridColumnArrowHover)}\n >\n <Box className={overviewStyles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={overviewStyles.resourceCard}\n >\n <Card.Icon icon={cardIcons[index]} color=\"green\" hasBackground size=\"medium\" />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box paddingBlockStart={80} paddingBlockEnd={80}>\n <CTABanner align=\"center\" hasGridLines>\n <CTABanner.Logo>\n <LogoGithubIcon size={64} />\n </CTABanner.Logo>\n <CTABanner.Heading>\n {content.cta.headingPrefix} <br /> <b>{content.cta.headingEmphasis}</b>\n </CTABanner.Heading>\n <CTABanner.Description>{content.cta.description}</CTABanner.Description>\n <CTABanner.ButtonGroup>\n <Button>{content.cta.primaryAction}</Button>\n </CTABanner.ButtonGroup>\n </CTABanner>\n </Box>\n\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n )\n}"
10030
+ },
10031
+ {
10032
+ "name": "FlexSuiteAIOverview",
10033
+ "title": "Product feature overview landing page",
10034
+ "keywords": [
10035
+ "product feature",
10036
+ "feature",
10037
+ "overview",
10038
+ "landing",
10039
+ "landing page",
10040
+ "homepage",
10041
+ "home",
10042
+ "product page",
10043
+ "marketing page",
10044
+ "full page",
10045
+ "template"
10046
+ ],
10047
+ "source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon, LogoGithubIcon, SparkleFillIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\nimport React, {useEffect} from 'react'\n\nimport {\n Box,\n Card,\n ColorModesEnum,\n CTABanner,\n FAQ,\n FAQGroup,\n Grid,\n Hero,\n Heading,\n InlineLink,\n LogoSuite,\n Section,\n SectionIntro,\n SubNav,\n Text,\n River,\n EyebrowText,\n Link,\n Stack,\n Token,\n PricingOptions,\n Tabs,\n Tiles,\n Image,\n useTheme,\n} from '../../..'\nimport {RedlineBackground} from '../../../component-helpers'\nimport placeholderBg from '../../../fixtures/images/dither-bg-landscape-purple.png'\nimport brandDividerWide from '../../../fixtures/images/brand-divider-copilot-wide.png'\nimport mercardo from '../../../fixtures/images/bento/mercado.png'\nimport copilotHeroHead from '../../../fixtures/images/copilot-mascot-head-peeking.png'\nimport renderUI3 from '../../../fixtures/images/copilot-vscode-agent-mode-3.png'\nimport renderUI3Dark from '../../../fixtures/images/copilot-vscode-agent-mode-3-dark.png'\nimport {logos} from '../../../LogoSuite/LogoSuite.fixtures'\nimport {tileItems} from '../../../Tiles/Tiles.fixtures'\n\nimport {defaultFlexSuiteAIOverviewContent, type FlexSuiteAIOverviewContent} from './FlexSuiteAIOverview.content'\nimport styles from './FlexSuiteAIOverview.module.css'\n\nexport type FlexSuiteAIOverviewTemplateProps = {\n content: FlexSuiteAIOverviewContent\n}\n\nconst cardIcons = [CpuIcon, LightBulbIcon, CodeOfConductIcon] as const\n\nexport function FlexSuiteAIOverview() {\n return <FlexSuiteAIOverviewTemplate content={defaultFlexSuiteAIOverviewContent} />\n}\n\nexport function FlexSuiteAIOverviewTemplate({content}: FlexSuiteAIOverviewTemplateProps) {\n const heroImageRef = React.useRef<HTMLDivElement>(null)\n const riverImageRefs = React.useRef<Array<HTMLDivElement | null>>([])\n const {colorMode} = useTheme()\n\n useEffect(() => {\n const cleanups: Array<() => void> = []\n\n if (heroImageRef.current) {\n cleanups.push(TempFadeInBackgroundEffect(heroImageRef.current, placeholderBg, 500))\n }\n\n for (const element of riverImageRefs.current) {\n if (element) {\n cleanups.push(TempFadeInBackgroundEffect(element, placeholderBg, 500))\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup()\n }\n }\n }, [])\n\n return (\n <>\n <Box className={styles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <SubNav>\n <SubNav.Heading href=\"#\">{content.subNav.heading}</SubNav.Heading>\n\n {content.subNav.links.map(link => (\n <SubNav.Link key={link} href=\"#\">\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n <Box paddingBlockStart={64}></Box>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\" fullWidth>\n <Hero\n imageContainerRef={heroImageRef}\n enableAnimation\n variant=\"gridline-expressive\"\n trailingComponent={() => (\n <Box className={styles.heroTrailingPeek}>\n <Text variant=\"muted\">\n {content.hero.trailingText} <InlineLink href=\"#\">{content.hero.trailingLinkText}</InlineLink>\n </Text>\n <Image\n className={styles.heroTrailingPeekImage}\n style={\n {\n ['--hero-peek-left' as string]: '-310px',\n ['--hero-peek-bottom' as string]: '-65px',\n ['--hero-peek-width' as string]: '190px',\n } as React.CSSProperties\n }\n src={copilotHeroHead}\n alt={content.hero.peekAlt}\n />\n </Box>\n )}\n >\n <Hero.Label>{content.hero.label}</Hero.Label>\n <Hero.Heading>\n {content.hero.headingLine1} <br /> {content.hero.headingLine2}\n </Hero.Heading>\n <Hero.Description>{content.hero.description}</Hero.Description>\n <Hero.PrimaryAction href=\"#\">{content.hero.primaryAction}</Hero.PrimaryAction>\n <Hero.SecondaryAction href=\"#\">{content.hero.secondaryAction}</Hero.SecondaryAction>\n <Hero.Image\n enableBorder={false}\n position=\"block-end\"\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.hero.imageAlt}\n />\n </Hero>\n </Section>\n\n <LogoSuite variant=\"gridline-expressive\">\n <LogoSuite.Heading visuallyHidden>{content.logoSuiteHeading}</LogoSuite.Heading>\n <LogoSuite.Logobar marquee={process.env.NODE_ENV !== 'test'}>{logos.slice(0, 8)}</LogoSuite.Logobar>\n </LogoSuite>\n\n <Box className={clsx(styles.cardGridFrame, styles.cardGridFrameNoTopBorder)}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resourceCards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem} padding={24}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.workflow.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.workflow.heading}</SectionIntro.Heading>\n <SectionIntro.Description>{content.workflow.description}</SectionIntro.Description>\n </SectionIntro>\n </Section>\n <Section paddingBlockEnd=\"none\">\n <Stack direction=\"vertical\" padding=\"none\" gap=\"spacious\">\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[0] = element\n }}\n imageBackgroundColor=\"subtle\"\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[0].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[0].eyebrow}</EyebrowText>\n <Heading>{content.rivers[0].heading}</Heading>\n <Text>{content.rivers[0].description}</Text>\n <Link href=\"#\">{content.rivers[0].linkText}</Link>\n </River.Content>\n </River>\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[1] = element\n }}\n imageBackgroundColor=\"subtle\"\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[1].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[1].eyebrow}</EyebrowText>\n <Heading>{content.rivers[1].heading}</Heading>\n <Text>{content.rivers[1].description}</Text>\n <Link href=\"#\">{content.rivers[1].linkText}</Link>\n </River.Content>\n </River>\n <River variant=\"gridline\">\n <River.Visual\n ref={element => {\n riverImageRefs.current[2] = element\n }}\n imageBackgroundColor=\"subtle\"\n >\n <picture>\n <source\n srcSet={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n media=\"(max-width: 47.99rem)\"\n />\n <img\n src={colorMode === ColorModesEnum.DARK ? renderUI3Dark : renderUI3}\n alt={content.rivers[2].imageAlt}\n />\n </picture>\n </River.Visual>\n <River.Content>\n <EyebrowText>{content.rivers[2].eyebrow}</EyebrowText>\n <Heading>{content.rivers[2].heading}</Heading>\n <Text>{content.rivers[2].description}</Text>\n <Link href=\"#\">{content.rivers[2].linkText}</Link>\n </River.Content>\n </River>\n </Stack>\n </Section>\n <Box marginBlockStart={48}>\n <CTABanner variant=\"balanced\" hasGridLines>\n <CTABanner.Logo>{<LogoGithubIcon size={64} />}</CTABanner.Logo>\n <CTABanner.Heading size=\"5\">\n {content.bento.headingPrefix} <b>{content.bento.headingEmphasis}</b>\n </CTABanner.Heading>\n <CTABanner.Link href=\"#\">{content.bento.linkText}</CTABanner.Link>\n <CTABanner.Image src={mercardo} alt={content.bento.imageAlt} />\n </CTABanner>\n </Box>\n <Stack justifyContent=\"center\" padding=\"none\">\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={brandDividerWide} alt={content.common.brandDividerAlt} style={{display: 'block'}} />\n </Box>\n </Stack>\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={48} paddingBlockStart={48}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.organization.heading}</SectionIntro.Heading>\n <SectionIntro.Description>{content.organization.description}</SectionIntro.Description>\n </SectionIntro>\n </Box>\n </Section>\n\n <Box className={styles.cardGridFrame}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.organization.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={4} paddingBlockStart={60}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.plans.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.plans.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Box>\n </Section>\n <Tabs aria-label={content.plans.tabsAriaLabel} variant=\"accent\" className={styles.fullWidthTabsPanel}>\n {content.plans.tabs.map(tab => (\n <Tabs.Item key={tab}>{tab}</Tabs.Item>\n ))}\n {content.plans.tabs.map(tab => (\n <Tabs.Panel key={tab}>\n <Box className={styles.cardGridFrame} backgroundColor=\"default\">\n <Grid>\n <Grid.Column span={12}>\n <PricingOptions\n style={{marginBlock: 'calc(var(--brand-borderWidth-thin) * -1)'}}\n variant=\"default-gradient\"\n >\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.free.heading}</PricingOptions.Heading>\n <PricingOptions.Description>{content.pricing.free.description}</PricingOptions.Description>\n <PricingOptions.Price>{content.pricing.free.price}</PricingOptions.Price>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListItem>\n {content.pricing.free.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.free.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem\n leadingVisual={SparkleFillIcon}\n leadingVisualFill=\"var(--base-color-scale-purple-5)\"\n >\n {content.pricing.free.features[2]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.free.primaryAction}\n </PricingOptions.PrimaryAction>\n {content.pricing.free.secondaryAction ? (\n <PricingOptions.SecondaryAction as=\"a\" href=\"#\" variant=\"subtle\">\n {content.pricing.free.secondaryAction}\n </PricingOptions.SecondaryAction>\n ) : null}\n </PricingOptions.Item>\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.pro.heading}</PricingOptions.Heading>\n {content.pricing.pro.label ? (\n <PricingOptions.Label>{content.pricing.pro.label}</PricingOptions.Label>\n ) : null}\n <PricingOptions.Description>{content.pricing.pro.description}</PricingOptions.Description>\n <PricingOptions.Price trailingText={content.pricing.pro.trailingText}>\n {content.pricing.pro.price}\n </PricingOptions.Price>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.pro.primaryAction}\n </PricingOptions.PrimaryAction>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListHeading>\n {content.pricing.pro.featureListHeading}\n </PricingOptions.FeatureListHeading>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[2]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.pro.features[3]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n {content.pricing.pro.footnote ? (\n <PricingOptions.Footnote>\n {content.pricing.pro.footnote}{' '}\n {content.pricing.pro.footnoteLinkText ? (\n <InlineLink href=\"#\">{content.pricing.pro.footnoteLinkText}</InlineLink>\n ) : null}\n </PricingOptions.Footnote>\n ) : null}\n </PricingOptions.Item>\n\n <PricingOptions.Item>\n <PricingOptions.Heading>{content.pricing.proPlus.heading}</PricingOptions.Heading>\n <PricingOptions.Description>{content.pricing.proPlus.description}</PricingOptions.Description>\n <PricingOptions.Price trailingText={content.pricing.proPlus.trailingText}>\n {content.pricing.proPlus.price}\n </PricingOptions.Price>\n <PricingOptions.PrimaryAction as=\"a\" href=\"#\" variant=\"primary\">\n {content.pricing.proPlus.primaryAction}\n </PricingOptions.PrimaryAction>\n <PricingOptions.FeatureList>\n <PricingOptions.FeatureListHeading>\n {content.pricing.proPlus.featureListHeading}\n </PricingOptions.FeatureListHeading>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[0]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[1]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem\n leadingVisual={SparkleFillIcon}\n leadingVisualFill=\"var(--base-color-scale-purple-5)\"\n >\n {content.pricing.proPlus.features[2]}\n </PricingOptions.FeatureListItem>\n <PricingOptions.FeatureListItem>\n {content.pricing.proPlus.features[3]}\n </PricingOptions.FeatureListItem>\n </PricingOptions.FeatureList>\n </PricingOptions.Item>\n </PricingOptions>\n </Grid.Column>\n </Grid>\n </Box>\n </Tabs.Panel>\n ))}\n </Tabs>\n\n <Section paddingBlockEnd=\"none\">\n <Tiles variant=\"gridlines\" layout=\"compact\">\n {tileItems.slice(0, 6).map(item => (\n <Tiles.Item key={item.name} name={item.name} href={item.href}>\n {item.icon}\n </Tiles.Item>\n ))}\n </Tiles>\n </Section>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Box paddingBlockEnd={32} paddingBlockStart={80}>\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Box>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockEnd={80}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon\n icon={cardIcons[index]}\n color=\"green\"\n hasBackground\n size=\"medium\"\n className={styles.resourceCardIcon}\n />\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n <Box paddingBlockEnd={64}>\n <FAQGroup variant=\"gridline\" defaultSelectedIndex={1}>\n <FAQGroup.Heading>\n {content.faq.headingLine1}\n <br />\n {content.faq.headingLine2}\n </FAQGroup.Heading>\n {content.faq.groups.map(group => (\n <FAQ key={group.heading} variant=\"gridline\">\n <FAQ.Heading>{group.heading}</FAQ.Heading>\n {group.items.map((item, index) => (\n <FAQ.Item key={item.question} name={`faq-${group.heading}`} open={index === 0}>\n <FAQ.Question>{item.question}</FAQ.Question>\n <FAQ.Answer>\n <Text as=\"p\" variant=\"muted\">\n {item.answer}\n </Text>\n </FAQ.Answer>\n </FAQ.Item>\n ))}\n </FAQ>\n ))}\n </FAQGroup>\n </Box>\n\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n </>\n )\n}\n\nfunction TempFadeInBackgroundEffect(element: HTMLDivElement, backgroundImageUrl: string, delay = 100) {\n element.style.position = 'relative'\n\n const bgDiv = document.createElement('div')\n bgDiv.style.cssText = `\n position: absolute;\n inset: 0;\n background-image: url(${backgroundImageUrl});\n background-size: cover;\n background-position: center;\n opacity: 0;\n transition: opacity 1.5s ease-out;\n z-index: 0;\n `\n element.insertBefore(bgDiv, element.firstChild)\n\n const children = element.children\n for (let index = 1; index < children.length; index += 1) {\n ;(children[index] as HTMLElement).style.position = 'relative'\n ;(children[index] as HTMLElement).style.zIndex = '1'\n }\n\n const timer = setTimeout(() => {\n bgDiv.style.opacity = '1'\n }, delay)\n\n return () => {\n clearTimeout(timer)\n bgDiv.remove()\n }\n}"
10048
+ },
10049
+ {
10050
+ "name": "FlexSuiteSecurityCategory",
10051
+ "title": "Product feature category landing page",
10052
+ "keywords": [
10053
+ "product feature",
10054
+ "feature",
10055
+ "category",
10056
+ "landing page",
10057
+ "solution",
10058
+ "solutions",
10059
+ "use case",
10060
+ "topic",
10061
+ "full page",
10062
+ "template"
10063
+ ],
10064
+ "source": "import {CodeOfConductIcon, CpuIcon, LightBulbIcon} from '@primer/octicons-react'\nimport {clsx} from 'clsx'\n\nimport {\n Box,\n Card,\n Grid,\n Hero,\n Pillar,\n LogoSuite,\n Section,\n SectionIntro,\n Stack,\n SubNav,\n Image,\n Token,\n Testimonial,\n CTABanner,\n Button,\n Text,\n} from '../../../..'\nimport {logos} from '../../../../LogoSuite/LogoSuite.fixtures'\nimport placeholderBg from '../../../../fixtures/images/security-banner-wide.png'\nimport placeholderImage from '../../../../fixtures/images/placeholder.png'\nimport monaAvatar from '../../../../fixtures/images/avatar-mona.png'\nimport brandDividerWide from '../../../../fixtures/images/brand-divider-security-wide.png'\nimport riverFootstepsChat from './fixtures/images/river-footsteps-chat.png'\nimport riverFootstepsChatMobile from './fixtures/images/river-footsteps-chat-mobile.png'\nimport riverFootstepsReview from './fixtures/images/river-footsteps-review.png'\nimport riverFootstepsReviewMobile from './fixtures/images/river-footsteps-review-mobile.png'\nimport riverFootstepsWorkflow from './fixtures/images/river-footsteps-workflow.png'\nimport riverFootstepsWorkflowMobile from './fixtures/images/river-footsteps-workflow-mobile.png'\n\nimport {\n defaultFlexSuiteSecurityCategoryContent,\n type FlexSuiteSecurityCategoryContent,\n} from './FlexSuiteSecurityCategory.content'\nimport styles from '../../Overview/FlexSuiteAIOverview.module.css'\nimport {RedlineBackground} from '../../../../component-helpers'\nimport {LockIcon} from '@primer/octovisuals-react'\n\nexport type FlexSuiteSecurityCategoryTemplateProps = {\n content: FlexSuiteSecurityCategoryContent\n}\n\nconst cardIcons = [CpuIcon, LightBulbIcon, CodeOfConductIcon] as const\nconst riverFootstepsImages = [riverFootstepsWorkflow, riverFootstepsReview, riverFootstepsChat] as const\nconst riverFootstepsMobileImages = [\n riverFootstepsWorkflowMobile,\n riverFootstepsReviewMobile,\n riverFootstepsChatMobile,\n] as const\n\nexport function FlexSuiteSecurityCategory() {\n return <FlexSuiteSecurityCategoryTemplate content={defaultFlexSuiteSecurityCategoryContent} />\n}\n\nexport function FlexSuiteSecurityCategoryTemplate({content}: FlexSuiteSecurityCategoryTemplateProps) {\n return (\n <Box className={styles.page} backgroundColor=\"default\">\n <Box backgroundColor=\"subtle\" style={{height: 72}} />\n\n <SubNav>\n <SubNav.Heading href=\"#\" aria-current=\"page\">\n {content.subNav.heading}\n </SubNav.Heading>\n\n {content.subNav.links.map(link => (\n <SubNav.Link key={link} href=\"#\">\n {link}\n </SubNav.Link>\n ))}\n </SubNav>\n\n <Section paddingBlockStart=\"none\" paddingBlockEnd=\"none\">\n <Hero align=\"center\">\n <Hero.Label>{content.hero.label}</Hero.Label>\n <Hero.Heading>{content.hero.heading}</Hero.Heading>\n <Hero.PrimaryAction href=\"#\">{content.hero.primaryAction}</Hero.PrimaryAction>\n <Hero.SecondaryAction href=\"#\">{content.hero.secondaryAction}</Hero.SecondaryAction>\n </Hero>\n </Section>\n <Stack justifyContent=\"center\" padding=\"none\" style={{marginTop: 'calc(var(--base-size-64) * -1)'}}>\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={placeholderBg} alt={content.common.heroImageAlt} style={{display: 'block', width: '100%'}} />\n </Box>\n </Stack>\n <Box\n className={clsx(styles.cardGridFrame)}\n marginBlockStart={{narrow: 24, wide: 80}}\n marginBlockEnd={{narrow: 24, wide: 64}}\n >\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.highlights.map((card, index) => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Icon icon={cardIcons[index]} color=\"green\" hasBackground size=\"medium\" />\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading as=\"h2\">{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box borderBlockStartWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <LogoSuite variant=\"gridline-expressive\">\n <LogoSuite.Heading visuallyHidden>{content.logoSuiteHeading}</LogoSuite.Heading>\n <LogoSuite.Logobar marquee={process.env.NODE_ENV !== 'test'}>{logos.slice(0, 8)}</LogoSuite.Logobar>\n </LogoSuite>\n </Box>\n\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"condensed\">\n <SectionIntro align=\"center\">\n <SectionIntro.Label>{content.workflow.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.workflow.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockStart={48}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.capabilities.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 6}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n <Card.Image aspectRatio=\"16:10\" src={placeholderImage} alt=\"\" />\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"none\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Label>{content.customerStories.label}</SectionIntro.Label>\n <SectionIntro.Heading>{content.customerStories.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockStart={48}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.customerStories.cards.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card href=\"#\" fullWidth ctaVariant=\"arrow\" className={styles.resourceCard}>\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box\n paddingInlineStart={48}\n paddingInlineEnd={48}\n borderBlockEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n >\n <Section>\n <Testimonial variant=\"expressive\" size=\"large\">\n <Testimonial.Quote>\n {content.testimonial.quoteLead} <em>{content.testimonial.quoteEmphasis}</em>{' '}\n {content.testimonial.quoteTrailing}\n </Testimonial.Quote>\n <Testimonial.Link href=\"#\">{content.testimonial.linkText}</Testimonial.Link>\n <Testimonial.Avatar src={monaAvatar} alt={content.testimonial.avatarAlt} />\n <Testimonial.Name position={content.testimonial.position}>{content.testimonial.name}</Testimonial.Name>\n </Testimonial>\n </Section>\n </Box>\n\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"none\">\n <Box\n borderInlineStartWidth=\"thin\"\n borderInlineEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n padding={{narrow: 24, regular: 48, wide: 64}}\n >\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n <Grid.Column span={{xsmall: 12, large: 5}}>\n <SectionIntro>\n <SectionIntro.Heading>{content.riverFootstepsSection.heading}</SectionIntro.Heading>\n <SectionIntro.Link href=\"#\">{content.riverFootstepsSection.linkText}</SectionIntro.Link>\n </SectionIntro>\n </Grid.Column>\n <Grid.Column span={{xsmall: 12, large: 7}}>\n <Grid className={styles.riverFootstepsGrid}>\n {content.riverFootstepsSection.pillars.map((pillar, index) => (\n <Grid.Column key={pillar.heading} span={{xsmall: 12, medium: 6}}>\n <Pillar fullWidth>\n <Pillar.Image\n as=\"picture\"\n src={riverFootstepsMobileImages[index]}\n alt={pillar.imageAlt}\n sources={[{srcset: riverFootstepsImages[index], media: '(min-width: 48rem)'}]}\n className={styles.riverFootstepsImage}\n />\n <Pillar.Heading>{pillar.heading}</Pillar.Heading>\n <Pillar.Link href=\"#\">{pillar.linkText}</Pillar.Link>\n </Pillar>\n </Grid.Column>\n ))}\n </Grid>\n </Grid.Column>\n </Grid>\n </Box>\n </Section>\n </Box>\n\n <Section paddingBlockStart=\"condensed\" paddingBlockEnd=\"condensed\">\n <SectionIntro align=\"center\" fullWidth>\n <SectionIntro.Heading>{content.resources.heading}</SectionIntro.Heading>\n </SectionIntro>\n </Section>\n\n <Box className={styles.cardGridFrame} marginBlockEnd={80}>\n <Box className={styles.cardGridContent}>\n <Grid columnGap=\"none\" rowGap=\"none\" enableGutters={false}>\n {content.resources.cards.map(card => (\n <Grid.Column\n key={card.heading}\n span={{xsmall: 12, large: 4}}\n className={clsx(styles.cardGridColumn, styles.cardGridColumnArrowHover)}\n >\n <Box className={styles.cardGridItem}>\n <Card\n href=\"#\"\n fullWidth\n ctaVariant=\"arrow\"\n ctaText={content.common.learnMore}\n className={styles.resourceCard}\n >\n {card.tag ? (\n <Card.Tokens>\n <Token>{card.tag}</Token>\n </Card.Tokens>\n ) : null}\n <Card.Heading>{card.heading}</Card.Heading>\n <Card.Description>{card.description}</Card.Description>\n </Card>\n </Box>\n </Grid.Column>\n ))}\n </Grid>\n </Box>\n </Box>\n\n <Box\n borderBlockStartWidth=\"thin\"\n borderBlockEndWidth=\"thin\"\n borderColor=\"muted\"\n borderStyle=\"solid\"\n marginBlockEnd=\"spacious\"\n >\n <Section paddingBlockEnd=\"none\" paddingBlockStart=\"none\">\n <CTABanner\n leadingComponent={() => (\n <Text>\n <LockIcon size={64} />\n </Text>\n )}\n >\n <CTABanner.Heading>{content.cta.heading}</CTABanner.Heading>\n\n <CTABanner.ButtonGroup>\n <Button>{content.cta.primaryAction}</Button>\n <Button>{content.cta.secondaryAction}</Button>\n </CTABanner.ButtonGroup>\n </CTABanner>\n </Section>\n </Box>\n <Stack justifyContent=\"center\" padding=\"none\">\n <Box borderBlockEndWidth=\"thin\" borderColor=\"muted\" borderStyle=\"solid\">\n <Image src={brandDividerWide} alt=\"\" style={{display: 'block'}} />\n </Box>\n </Stack>\n <RedlineBackground style={{height: 600, display: 'flex', flexDirection: 'column', gap: 'var(--base-size-32)'}}>\n <RedlineBackground\n style={{height: 84, marginTop: 'auto', backgroundColor: 'var(--brand-color-border-default)'}}\n />\n </RedlineBackground>\n </Box>\n )\n}"
10065
+ }
9963
10066
  ]
9964
10067
  }
package/dist/logger.d.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  * protocol stream (JSON-RPC over stdio), so an accidental `console.log` there would corrupt
4
4
  * the transport. Keep this the only logging surface in the server.
5
5
  */
6
- export interface Logger {
6
+ export type Logger = {
7
7
  debug(message: string): void;
8
8
  info(message: string): void;
9
9
  warn(message: string): void;
10
10
  error(message: string): void;
11
- }
11
+ };
12
12
  export declare function createLogger(enabled?: boolean): Logger;
@@ -71,6 +71,32 @@ const invalidPropValue = {
71
71
  return findings;
72
72
  },
73
73
  };
74
+ const invalidPropComboMap = [
75
+ // prevent align="center" + variant="gridline-expressive" beig used in Hero
76
+ {
77
+ component: 'Hero',
78
+ when: { prop: 'variant', value: 'gridline-expressive' },
79
+ disallow: { prop: 'align', value: 'center' },
80
+ message: 'The `Hero` `gridline-expressive` variant is always start-aligned; `align="center"` is unsupported and will be ignored — remove it (the default `align="start"` is correct).',
81
+ },
82
+ ];
83
+ const invalidPropCombination = {
84
+ id: 'invalid-prop-combination',
85
+ run(code) {
86
+ const hasAttribute = (tag, prop, value) => new RegExp(`\\b${escapeRegExp(prop)}=["']${escapeRegExp(value)}["']`).test(tag);
87
+ const findings = [];
88
+ for (const constraint of invalidPropComboMap) {
89
+ for (const usage of componentUsage(code, constraint.component)) {
90
+ const tag = usage[0];
91
+ if (hasAttribute(tag, constraint.when.prop, constraint.when.value) &&
92
+ hasAttribute(tag, constraint.disallow.prop, constraint.disallow.value)) {
93
+ findings.push({ severity: 'error', rule: this.id, message: constraint.message, evidence: evidence(tag) });
94
+ }
95
+ }
96
+ }
97
+ return findings;
98
+ },
99
+ };
74
100
  const RAW_HTML_PATTERNS = [
75
101
  {
76
102
  id: 'raw-form-elements',
@@ -227,6 +253,7 @@ export function brandComponentsUsed(code, catalog) {
227
253
  export const allRules = [
228
254
  unknownSubcomponents,
229
255
  invalidPropValue,
256
+ invalidPropCombination,
230
257
  rawHtml,
231
258
  hardcodedValues,
232
259
  offBrandTells,
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/review/rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/review/rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAA0B,QAAQ,EAAC,MAAM,YAAY,CAAA;AAE5D;;;;;;GAMG;AAEH,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,IAAY,EAAsB,EAAE,CAAC;IACzE,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;CACrE,CAAA;AAED,mDAAmD;AACnD,MAAM,oBAAoB,GAAS;IACjC,EAAE,EAAE,sBAAsB;IAC1B,GAAG,CAAC,IAAI,EAAE,OAAO;QACf,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;QACxF,MAAM,QAAQ,GAAc,EAAE,CAAA;QAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;QAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAAE,CAAC;YAChF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACrB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACpB,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG;gBAAE,SAAQ;YAC3B,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAClC,IAAI,CAAC,SAAS;gBAAE,SAAQ;YACxB,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,GAAG,EAAE,CAAA;YAClC,IAAI,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,SAAQ;YAChF,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YACnB,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,MAAM;gBAC1C,CAAC,CAAC,0BAA0B,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACjE,CAAC,CAAC,EAAE,CAAA;YACN,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,IAAI,CAAC,EAAE;gBACb,OAAO,EAAE,KAAK,SAAS,kCAAkC,IAAI,MAAM,KAAK,EAAE;gBAC1E,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;aAC7B,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED,iFAAiF;AACjF,MAAM,gBAAgB,GAAS;IAC7B,EAAE,EAAE,oBAAoB;IACxB,GAAG,CAAC,IAAI,EAAE,OAAO;QACf,MAAM,QAAQ,GAAc,EAAE,CAAA;QAC9B,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YAC/E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAQ;YAChC,KAAK,MAAM,KAAK,IAAI,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAA;oBAC5E,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;wBACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;wBACrB,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;4BACzC,QAAQ,CAAC,IAAI,CAAC;gCACZ,QAAQ,EAAE,OAAO;gCACjB,IAAI,EAAE,IAAI,CAAC,EAAE;gCACb,OAAO,EAAE,KAAK,SAAS,CAAC,IAAI,aAC1B,IAAI,CAAC,IACP,yBAAyB,KAAK,iBAAiB,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gCAC5F,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;6BAC7B,CAAC,CAAA;wBACJ,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAeD,MAAM,mBAAmB,GAAgC;IACvD,2EAA2E;IAC3E;QACE,SAAS,EAAE,MAAM;QACjB,IAAI,EAAE,EAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,qBAAqB,EAAC;QACrD,QAAQ,EAAE,EAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAC;QAC1C,OAAO,EACL,6KAA6K;KAChL;CACF,CAAA;AAED,MAAM,sBAAsB,GAAS;IACnC,EAAE,EAAE,0BAA0B;IAC9B,GAAG,CAAC,IAAI;QACN,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,IAAY,EAAE,KAAa,EAAW,EAAE,CACzE,IAAI,MAAM,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEjF,MAAM,QAAQ,GAAc,EAAE,CAAA;QAC9B,KAAK,MAAM,UAAU,IAAI,mBAAmB,EAAE,CAAC;YAC7C,KAAK,MAAM,KAAK,IAAI,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;gBACpB,IACE,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;oBAC9D,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,EACtE,CAAC;oBACD,QAAQ,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAC,CAAC,CAAA;gBACzG,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAQD,MAAM,iBAAiB,GAAiB;IACtC;QACE,EAAE,EAAE,mBAAmB;QACvB,IAAI,EAAE,kCAAkC;QACxC,OAAO,EACL,2GAA2G;KAC9G;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,iGAAiG;KAC3G;IACD;QACE,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,+CAA+C;QACrD,OAAO,EAAE,kGAAkG;KAC5G;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,2CAA2C;QACjD,OAAO,EAAE,qGAAqG;KAC/G;CACF,CAAA;AAED,MAAM,OAAO,GAAS;IACpB,EAAE,EAAE,oBAAoB;IACxB,GAAG,CAAC,IAAI;QACN,MAAM,QAAQ,GAAc,EAAE,CAAA;QAC9B,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACrC,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAC,CAAC,CAAA;YAChH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED,MAAM,eAAe,GAAS;IAC5B,EAAE,EAAE,aAAa;IACjB,GAAG,CAAC,IAAI;QACN,MAAM,QAAQ,GAAc,EAAE,CAAA;QAC9B,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC3F,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,SAAS;gBACnB,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,+BAA+B,GAAG;qBACxC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;qBACX,IAAI,CAAC,IAAI,CAAC,iEAAiE;aAC/E,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;QACnH,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,SAAS;gBACnB,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,gCAAgC,EAAE;qBACxC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;qBACX,IAAI,CAAC,IAAI,CAAC,6EAA6E;aAC3F,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED,0FAA0F;AAC1F,MAAM,eAAe,GAAiB;IACpC;QACE,EAAE,EAAE,oBAAoB;QACxB,IAAI,EAAE,6EAA6E;QACnF,OAAO,EACL,6IAA6I;KAChJ;IACD;QACE,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,+CAA+C;QACrD,OAAO,EACL,uHAAuH;KAC1H;IACD;QACE,EAAE,EAAE,qBAAqB;QACzB,IAAI,EAAE,0EAA0E;QAChF,OAAO,EACL,6GAA6G;KAChH;IACD;QACE,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,kCAAkC;QACxC,OAAO,EAAE,+FAA+F;KACzG;IACD;QACE,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,8CAA8C;QACpD,OAAO,EAAE,6EAA6E;KACvF;IACD;QACE,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,qCAAqC;QAC3C,OAAO,EAAE,iFAAiF;KAC3F;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,IAAI,EAAE,oCAAoC;QAC1C,OAAO,EAAE,qGAAqG;KAC/G;CACF,CAAA;AAED,MAAM,aAAa,GAAS;IAC1B,EAAE,EAAE,iBAAiB;IACrB,GAAG,CAAC,IAAI;QACN,MAAM,QAAQ,GAAc,EAAE,CAAA;QAC9B,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAClC,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAC,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED;;;;GAIG;AACH,MAAM,mBAAmB,GAAS;IAChC,EAAE,EAAE,uBAAuB;IAC3B,GAAG,CAAC,IAAI;QACN,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YACvD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;YACpB,yDAAyD;YACzD,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,SAAQ;YACzD,OAAO;gBACL;oBACE,QAAQ,EAAE,SAAS;oBACnB,IAAI,EAAE,IAAI,CAAC,EAAE;oBACb,OAAO,EACL,sQAAsQ;oBACxQ,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC;iBACxB;aACF,CAAA;QACH,CAAC;QACD,OAAO,EAAE,CAAA;IACX,CAAC;CACF,CAAA;AAED,qGAAqG;AACrG,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,OAAgB;IAChE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAC/B,sFAAsF,CACvF,EAAE,CAAC;QACF,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,IAAI;iBACd,IAAI,EAAE;iBACN,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACrB,EAAE,IAAI,EAAE,CAAA;YACV,IAAI,IAAI;gBAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7E,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAW;IAC9B,oBAAoB;IACpB,gBAAgB;IAChB,sBAAsB;IACtB,OAAO;IACP,eAAe;IACf,aAAa;IACb,mBAAmB;CACpB,CAAA"}
@@ -1,15 +1,15 @@
1
1
  import type { Catalog } from '../catalog/types.js';
2
2
  export type Severity = 'error' | 'warning';
3
- export interface Finding {
3
+ export type Finding = {
4
4
  severity: Severity;
5
5
  rule: string;
6
6
  message: string;
7
7
  /** A short snippet of the offending source, for context. */
8
8
  evidence?: string;
9
- }
10
- export interface Rule {
9
+ };
10
+ export type Rule = {
11
11
  id: string;
12
12
  run(code: string, catalog: Catalog): Finding[];
13
- }
13
+ };
14
14
  /** Trim a matched snippet so findings stay compact in the agent's context. */
15
15
  export declare function evidence(snippet: string, max?: number): string;
package/dist/server.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { detectAssetGenerator } from './brand/detect-asset-generator.js';
4
5
  import { createDocsSource } from './brand/docs-source.js';
5
6
  import { resolveInstalledAssets } from './brand/resolve-assets.js';
6
7
  import { looksLikeProductProject, resolveBrandInstall } from './brand/resolve-install.js';
7
8
  import { loadCatalog } from './catalog/load.js';
8
9
  import { createLogger } from './logger.js';
9
10
  import { registerTools } from './tools/register.js';
11
+ const assetGenerator = detectAssetGenerator();
10
12
  const INSTRUCTIONS = `Primer Brand design system tools for building GitHub web-based marketing experiences and landing pages with @primer/react-brand.
11
13
 
12
14
  Use these tools whenever a project builds marketing/landing pages, or depends on the @primer/react-brand design system.
@@ -16,7 +18,13 @@ use the Primer (product) tools instead.
16
18
  Typical flow: primer_brand_setup once at the start of a new page or project, primer_brand_examples to start
17
19
  from correct patterns, primer_brand_component for exact props, primer_brand_tokens / primer_brand_asset for tokens and
18
20
  icons, primer_brand_docs for guidance, and primer_brand_review as the final gate over your complete output (JSX and
19
- CSS together) before you finish.`;
21
+ CSS together) before you finish.
22
+
23
+ ${assetGenerator.available
24
+ ? `Use the GitHub Asset Generator MCP tools (\`asset-generator\`) instead of stock photos or AI art. Use Asset Generator
25
+ for wallpapers, hero or river background images, and product shots.`
26
+ : ''}
27
+ `;
20
28
  export function createServer() {
21
29
  const logger = createLogger();
22
30
  let version = '0.0.0';
@@ -46,8 +54,11 @@ export function createServer() {
46
54
  const versions = installedAssets.sources.map(source => `${source.module}@${source.version ?? '?'}`).join(', ');
47
55
  logger.info(`resolved icons/illustrations from installed packages: ${versions}`);
48
56
  }
57
+ if (detectAssetGenerator().available) {
58
+ logger.info('GitHub Asset Generator MCP server detected; surfacing on-brand imagery guidance');
59
+ }
49
60
  const docs = createDocsSource(brand, logger);
50
- registerTools(server, { catalog, brand, docs, logger, assets, assetsOrigin });
61
+ registerTools(server, { catalog, brand, docs, logger, assets, assetsOrigin, assetGenerator: detectAssetGenerator() });
51
62
  return server;
52
63
  }
53
64
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAEtC,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAA;AAEjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAC,sBAAsB,EAAC,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAC,uBAAuB,EAAE,mBAAmB,EAAC,MAAM,4BAA4B,CAAA;AACvF,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAA;AACxC,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAA;AAEjD,MAAM,YAAY,GAAG;;;;;;;;;iCASY,CAAA;AAEjC,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;IAE7B,IAAI,OAAO,GAAG,OAAO,CAAA;IACrB,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAC9E,OAAO,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAwB,CAAC,OAAO,IAAI,OAAO,CAAA;IACpG,CAAC;IAAC,MAAM,CAAC;QACP,oFAAoF;IACtF,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAC,EAAE,EAAC,YAAY,EAAE,YAAY,EAAC,CAAC,CAAA;IAE3F,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAA;IACnC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,6BAA6B,KAAK,CAAC,OAAO,IAAI,SAAS,SAAS,KAAK,CAAC,UAAU,EAAE,CAAC,CAAA;IACjG,CAAC;SAAM,IAAI,uBAAuB,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,qGAAqG,CAAC,CAAA;IACpH,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAA;IACnF,CAAC;IAED,MAAM,eAAe,GAAG,sBAAsB,EAAE,CAAA;IAChD,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAA;IACjF,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAA;IAC1F,IAAI,YAAY,KAAK,WAAW,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9G,MAAM,CAAC,IAAI,CAAC,yDAAyD,QAAQ,EAAE,CAAC,CAAA;IAClF,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC5C,aAAa,CAAC,MAAM,EAAE,EAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAC,CAAC,CAAA;IAC3E,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ const inputSchema = z.object({
8
8
  .describe('Restrict to Octicons ("icon") or Octovisuals ("illustration").'),
9
9
  limit: z.number().int().min(1).max(50).optional().default(12).describe('Max results.'),
10
10
  });
11
- const description = `Find approved Primer Brand visuals as code imports: Octicons (@primer/octicons-react) and Octovisuals (@primer/octovisuals-react). Use these instead of emoji, clip art, or random icon sets — mismatched or emoji icons are off-brand.`;
11
+ const description = `Find approved Primer Brand visuals as code imports: Octicons (@primer/octicons-react) and Octovisuals (@primer/octovisuals-react). Use these instead of emoji, clip art, or random icon sets - mismatched or emoji icons are off-brand.`;
12
12
  export const primerBrandAssetTool = {
13
13
  name: 'primer_brand_asset',
14
14
  title: 'Primer Brand icons & illustrations',
@@ -26,10 +26,19 @@ export const primerBrandAssetTool = {
26
26
  if (input.kind) {
27
27
  assets = assets.filter(asset => asset.kind === input.kind);
28
28
  }
29
+ const brandImages = ctx.assetGenerator.available
30
+ ? `Use the GitHub Asset Generator MCP tools (\`asset-generator\`) instead of stock photos or AI art. Check images suit usage first. `
31
+ : '';
29
32
  const ranked = rank(input.query, assets, asset => [asset.name]).map(entry => entry.item);
30
33
  if (ranked.length === 0) {
31
34
  return {
32
- text: `No ${input.kind ?? 'asset'} matched "${input.query}". Try a simpler term (e.g. "arrow", "check", "shield").\n\n${origin}`,
35
+ text: [
36
+ `No ${input.kind ?? 'asset'} matched "${input.query}". Try a simpler term (e.g. "arrow", "check", "shield").`,
37
+ origin,
38
+ brandImages,
39
+ ]
40
+ .filter(Boolean)
41
+ .join('\n\n'),
33
42
  };
34
43
  }
35
44
  const shown = ranked.slice(0, input.limit);
@@ -37,7 +46,7 @@ export const primerBrandAssetTool = {
37
46
  const lines = shown
38
47
  .map(asset => `- \`${asset.name}\` _(${asset.kind})_ — \`import {${asset.name}} from '${asset.module}'\``)
39
48
  .join('\n');
40
- return { text: `${lines}${more}\n\n${origin}` };
49
+ return { text: [`${lines}${more}`, origin, brandImages].filter(Boolean).join('\n\n') };
41
50
  },
42
51
  };
43
52
  //# sourceMappingURL=primer-brand-asset.js.map