@flamingo-stack/openframe-frontend-core 0.0.669 → 0.0.670-2401.7221.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.
Files changed (130) hide show
  1. package/dist/{chunk-QHL4GYYY.cjs → chunk-3BLN7YNM.cjs} +575 -452
  2. package/dist/chunk-3BLN7YNM.cjs.map +1 -0
  3. package/dist/{chunk-CFUUVLYQ.cjs → chunk-4TEV4KPV.cjs} +33 -33
  4. package/dist/{chunk-CFUUVLYQ.cjs.map → chunk-4TEV4KPV.cjs.map} +1 -1
  5. package/dist/{chunk-Z3HUP4G7.cjs → chunk-635CAJ36.cjs} +32 -32
  6. package/dist/{chunk-Z3HUP4G7.cjs.map → chunk-635CAJ36.cjs.map} +1 -1
  7. package/dist/{chunk-7UW2BBFI.cjs → chunk-7KZRU5Z2.cjs} +59 -59
  8. package/dist/{chunk-7UW2BBFI.cjs.map → chunk-7KZRU5Z2.cjs.map} +1 -1
  9. package/dist/{chunk-FJNX67W2.cjs → chunk-AGM3ZA43.cjs} +5 -5
  10. package/dist/{chunk-FJNX67W2.cjs.map → chunk-AGM3ZA43.cjs.map} +1 -1
  11. package/dist/{chunk-TFVUM6KK.cjs → chunk-ALKT3U5Y.cjs} +10 -10
  12. package/dist/{chunk-TFVUM6KK.cjs.map → chunk-ALKT3U5Y.cjs.map} +1 -1
  13. package/dist/{chunk-YH3BUB4W.js → chunk-B7EGFSTQ.js} +5 -5
  14. package/dist/{chunk-GF6WJ4MG.js → chunk-BP3OYBQV.js} +6 -6
  15. package/dist/{chunk-P6IJXAEE.js → chunk-DLJGCDHX.js} +2 -1
  16. package/dist/{chunk-P6IJXAEE.js.map → chunk-DLJGCDHX.js.map} +1 -1
  17. package/dist/{chunk-GO63IT6K.js → chunk-EJM2E6KF.js} +5 -5
  18. package/dist/{chunk-WVCDX7FE.cjs → chunk-GAQSQ7CH.cjs} +15 -15
  19. package/dist/{chunk-WVCDX7FE.cjs.map → chunk-GAQSQ7CH.cjs.map} +1 -1
  20. package/dist/{chunk-B5UP36KY.cjs → chunk-GN2TSC36.cjs} +12 -12
  21. package/dist/{chunk-B5UP36KY.cjs.map → chunk-GN2TSC36.cjs.map} +1 -1
  22. package/dist/{chunk-ZHRIFUN3.cjs → chunk-GP4SLGDO.cjs} +28 -28
  23. package/dist/{chunk-ZHRIFUN3.cjs.map → chunk-GP4SLGDO.cjs.map} +1 -1
  24. package/dist/{chunk-LJOELK7P.js → chunk-GUZG4MSV.js} +2 -2
  25. package/dist/{chunk-IBQT4RTS.js → chunk-HJXIIGLD.js} +3 -3
  26. package/dist/{chunk-E5HUHKHH.js → chunk-I3I3HPZA.js} +7 -7
  27. package/dist/{chunk-6KJV6GAJ.js → chunk-IA7AGOBD.js} +4 -4
  28. package/dist/{chunk-NNYND3I4.js → chunk-J4XQQ43Y.js} +3 -3
  29. package/dist/{chunk-IZ6F7LOE.js → chunk-JBH5YDPP.js} +3 -3
  30. package/dist/{chunk-Y7SFGCF4.cjs → chunk-LKRNXIUL.cjs} +20 -20
  31. package/dist/{chunk-Y7SFGCF4.cjs.map → chunk-LKRNXIUL.cjs.map} +1 -1
  32. package/dist/{chunk-EV2KJ4D2.cjs → chunk-MQCEDC6H.cjs} +7 -7
  33. package/dist/{chunk-EV2KJ4D2.cjs.map → chunk-MQCEDC6H.cjs.map} +1 -1
  34. package/dist/{chunk-PHLBRU5P.js → chunk-OCMJWI6L.js} +3 -3
  35. package/dist/{chunk-CWK4QHVF.js → chunk-PAJUQOWK.js} +3 -3
  36. package/dist/{chunk-IJPV7OMA.js → chunk-PEBQEALM.js} +589 -466
  37. package/dist/chunk-PEBQEALM.js.map +1 -0
  38. package/dist/{chunk-5CC33IJ7.js → chunk-PMCLM3KV.js} +6 -6
  39. package/dist/{chunk-6W4LUHMY.cjs → chunk-Q4SJJNP2.cjs} +206 -206
  40. package/dist/{chunk-6W4LUHMY.cjs.map → chunk-Q4SJJNP2.cjs.map} +1 -1
  41. package/dist/{chunk-37NYF4W2.cjs → chunk-QRUW4UXB.cjs} +2 -1
  42. package/dist/chunk-QRUW4UXB.cjs.map +1 -0
  43. package/dist/{chunk-Z3GN3MIW.cjs → chunk-QYUBHOIE.cjs} +81 -81
  44. package/dist/{chunk-Z3GN3MIW.cjs.map → chunk-QYUBHOIE.cjs.map} +1 -1
  45. package/dist/{chunk-VJ4XBTR5.cjs → chunk-S5UETFC4.cjs} +22 -22
  46. package/dist/{chunk-VJ4XBTR5.cjs.map → chunk-S5UETFC4.cjs.map} +1 -1
  47. package/dist/{chunk-5V3LERVC.js → chunk-SPYKIXKJ.js} +4 -4
  48. package/dist/{chunk-FOH4MNOZ.js → chunk-TQJN7EBG.js} +3 -3
  49. package/dist/{chunk-NZD2WTJ2.cjs → chunk-TYXXODCQ.cjs} +3 -3
  50. package/dist/{chunk-NZD2WTJ2.cjs.map → chunk-TYXXODCQ.cjs.map} +1 -1
  51. package/dist/{chunk-K4WYWTQV.cjs → chunk-UYZNNKPD.cjs} +6 -6
  52. package/dist/{chunk-K4WYWTQV.cjs.map → chunk-UYZNNKPD.cjs.map} +1 -1
  53. package/dist/{chunk-SHGMDYCR.js → chunk-VEAA5C5X.js} +2 -2
  54. package/dist/{chunk-VEQWH5KH.cjs → chunk-X4B3UY3L.cjs} +30 -30
  55. package/dist/{chunk-VEQWH5KH.cjs.map → chunk-X4B3UY3L.cjs.map} +1 -1
  56. package/dist/{chunk-DK3BHDUZ.js → chunk-Z72SBI2U.js} +3 -3
  57. package/dist/components/case-studies/index.cjs +16 -16
  58. package/dist/components/case-studies/index.js +4 -4
  59. package/dist/components/chat/entity-cards/design-doc-card.d.ts +32 -0
  60. package/dist/components/chat/entity-cards/design-doc-card.d.ts.map +1 -0
  61. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  62. package/dist/components/chat/entity-cards/index.d.ts +1 -0
  63. package/dist/components/chat/entity-cards/index.d.ts.map +1 -1
  64. package/dist/components/chat/index.cjs +15 -3
  65. package/dist/components/chat/index.cjs.map +1 -1
  66. package/dist/components/chat/index.js +14 -2
  67. package/dist/components/contact/index.cjs +5 -5
  68. package/dist/components/contact/index.js +4 -4
  69. package/dist/components/docs/index.cjs +8 -8
  70. package/dist/components/docs/index.js +7 -7
  71. package/dist/components/embeds/index.cjs +5 -5
  72. package/dist/components/embeds/index.js +4 -4
  73. package/dist/components/faq/index.cjs +5 -5
  74. package/dist/components/faq/index.js +4 -4
  75. package/dist/components/features/index.cjs +4 -4
  76. package/dist/components/features/index.js +3 -3
  77. package/dist/components/help-center-pages/index.cjs +91 -91
  78. package/dist/components/help-center-pages/index.js +15 -15
  79. package/dist/components/index.cjs +66 -54
  80. package/dist/components/index.cjs.map +1 -1
  81. package/dist/components/index.js +27 -15
  82. package/dist/components/index.js.map +1 -1
  83. package/dist/components/meeting-scheduler/index.cjs +40 -40
  84. package/dist/components/meeting-scheduler/index.js +3 -3
  85. package/dist/components/navigation/index.cjs +7 -7
  86. package/dist/components/navigation/index.js +6 -6
  87. package/dist/components/onboarding-guides/index.cjs +7 -7
  88. package/dist/components/onboarding-guides/index.js +6 -6
  89. package/dist/components/related-content/index.cjs +3 -3
  90. package/dist/components/related-content/index.js +2 -2
  91. package/dist/components/tickets/index.cjs +8 -8
  92. package/dist/components/tickets/index.js +7 -7
  93. package/dist/components/ui/index.cjs +16 -4
  94. package/dist/components/ui/index.cjs.map +1 -1
  95. package/dist/components/ui/index.js +15 -3
  96. package/dist/index.cjs +21 -9
  97. package/dist/index.cjs.map +1 -1
  98. package/dist/index.js +19 -7
  99. package/dist/index.js.map +1 -1
  100. package/dist/utils/index.cjs +7 -3
  101. package/dist/utils/index.cjs.map +1 -1
  102. package/dist/utils/index.js +7 -3
  103. package/dist/utils/index.js.map +1 -1
  104. package/dist/utils/list-url.d.ts.map +1 -1
  105. package/dist/utils/source-icons.d.ts.map +1 -1
  106. package/package.json +1 -1
  107. package/src/components/chat/entity-cards/design-doc-card.tsx +130 -0
  108. package/src/components/chat/entity-cards/dispatch.tsx +53 -1
  109. package/src/components/chat/entity-cards/index.ts +9 -0
  110. package/src/utils/list-url.ts +1 -0
  111. package/src/utils/source-icons.ts +3 -0
  112. package/dist/chunk-37NYF4W2.cjs.map +0 -1
  113. package/dist/chunk-IJPV7OMA.js.map +0 -1
  114. package/dist/chunk-QHL4GYYY.cjs.map +0 -1
  115. /package/dist/{chunk-YH3BUB4W.js.map → chunk-B7EGFSTQ.js.map} +0 -0
  116. /package/dist/{chunk-GF6WJ4MG.js.map → chunk-BP3OYBQV.js.map} +0 -0
  117. /package/dist/{chunk-GO63IT6K.js.map → chunk-EJM2E6KF.js.map} +0 -0
  118. /package/dist/{chunk-LJOELK7P.js.map → chunk-GUZG4MSV.js.map} +0 -0
  119. /package/dist/{chunk-IBQT4RTS.js.map → chunk-HJXIIGLD.js.map} +0 -0
  120. /package/dist/{chunk-E5HUHKHH.js.map → chunk-I3I3HPZA.js.map} +0 -0
  121. /package/dist/{chunk-6KJV6GAJ.js.map → chunk-IA7AGOBD.js.map} +0 -0
  122. /package/dist/{chunk-NNYND3I4.js.map → chunk-J4XQQ43Y.js.map} +0 -0
  123. /package/dist/{chunk-IZ6F7LOE.js.map → chunk-JBH5YDPP.js.map} +0 -0
  124. /package/dist/{chunk-PHLBRU5P.js.map → chunk-OCMJWI6L.js.map} +0 -0
  125. /package/dist/{chunk-CWK4QHVF.js.map → chunk-PAJUQOWK.js.map} +0 -0
  126. /package/dist/{chunk-5CC33IJ7.js.map → chunk-PMCLM3KV.js.map} +0 -0
  127. /package/dist/{chunk-5V3LERVC.js.map → chunk-SPYKIXKJ.js.map} +0 -0
  128. /package/dist/{chunk-FOH4MNOZ.js.map → chunk-TQJN7EBG.js.map} +0 -0
  129. /package/dist/{chunk-SHGMDYCR.js.map → chunk-VEAA5C5X.js.map} +0 -0
  130. /package/dist/{chunk-DK3BHDUZ.js.map → chunk-Z72SBI2U.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"list-url.d.ts","sourceRoot":"","sources":["../../src/utils/list-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAaH;;;;iEAIiE;AACjE,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAKtE;AAqFD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,IAAI,CAa5F"}
1
+ {"version":3,"file":"list-url.d.ts","sourceRoot":"","sources":["../../src/utils/list-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAaH;;;;iEAIiE;AACjE,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAKtE;AAsFD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,IAAI,CAa5F"}
@@ -1 +1 @@
1
- {"version":3,"file":"source-icons.d.ts","sourceRoot":"","sources":["../../src/utils/source-icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkEpD,CAAC;AAEF;;6DAE6D;AAC7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGxF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwEzD,CAAC;AAEF;;kEAEkE;AAClE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iCAAiC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkEpE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5E"}
1
+ {"version":3,"file":"source-icons.d.ts","sourceRoot":"","sources":["../../src/utils/source-icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmEpD,CAAC;AAEF;;6DAE6D;AAC7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGxF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyEzD,CAAC;AAEF;;kEAEkE;AAClE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iCAAiC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmEpE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.669",
3
+ "version": "0.0.670-2401.7221.1",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,130 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * DesignDocCard (pure presentation) — a design doc as the entity card every surface shows it with: the full card
5
+ * (`default`, the `RoadmapCard` layout: glyph slot, title, DRI and date, sign-off status, summary, sign-off count and
6
+ * the people) for admin surfaces, and the chat's compact card (`DesignDocChatCard` in `dispatch.tsx`, which reads
7
+ * the same status and meta through the helpers below). The card writes no click logic beyond its `href` anchor.
8
+ */
9
+
10
+ import type { DesignDoc, DesignDocCompletion } from '../../../types/design-doc';
11
+ import { formatDateShort } from '../../../utils/date-formatters';
12
+ import { FileContentIcon } from '../../icons-v2-generated/documents/file-content-icon';
13
+ import { AvatarStack, type AvatarStackPerson } from '../../ui/avatar-stack';
14
+ import { StatusBadge, type StatusBadgeProps } from '../../ui/status-badge';
15
+ import { safeHref } from '../utils/compact-card-classes';
16
+
17
+ /** A doc's sign-off state: ready (every section signed off), blocked (an open blocking comment or a blocked section), else in review. */
18
+ export function designDocSignOffStatus(completion: DesignDocCompletion): {
19
+ label: string;
20
+ scheme: NonNullable<StatusBadgeProps['colorScheme']>;
21
+ tag: 'success' | 'error' | 'warning';
22
+ } {
23
+ if (completion.isComplete) return { label: 'Ready', scheme: 'success', tag: 'success' };
24
+ if (completion.openBlocking > 0 || completion.blocked > 0) return { label: 'Blocked', scheme: 'error', tag: 'error' };
25
+ return { label: 'In review', scheme: 'warning', tag: 'warning' };
26
+ }
27
+
28
+ /** "3/4 reviews signed off" — the count the design-docs screen prints. */
29
+ export function designDocSignOffLabel(completion: DesignDocCompletion): string {
30
+ return `${completion.completed}/${completion.total} review${completion.total === 1 ? '' : 's'} signed off`;
31
+ }
32
+
33
+ /** The people behind a doc: the DRI first, then the implementation owners (display only). */
34
+ export function designDocPeople(doc: Pick<DesignDoc, 'author' | 'feature_leads'>): AvatarStackPerson[] {
35
+ const people: AvatarStackPerson[] = [];
36
+ if (doc.author?.full_name)
37
+ people.push({
38
+ key: `dri-${doc.author.id ?? doc.author.full_name}`,
39
+ name: doc.author.full_name,
40
+ avatarUrl: doc.author.avatar_url,
41
+ });
42
+ for (const p of doc.feature_leads ?? []) {
43
+ if (!p.full_name) continue;
44
+ people.push({ key: `lead-${p.id}`, name: p.full_name, avatarUrl: p.avatar_url });
45
+ }
46
+ return people;
47
+ }
48
+
49
+ /** "DRI Jane Doe · updated Sep 24, 2026" — the card's subtitle. */
50
+ export function designDocMetaLine(doc: Pick<DesignDoc, 'author' | 'updated_at'>): string {
51
+ return [doc.author?.full_name ? `DRI ${doc.author.full_name}` : null, `updated ${formatDateShort(doc.updated_at)}`]
52
+ .filter(Boolean)
53
+ .join(' · ');
54
+ }
55
+
56
+ export interface DesignDocCardProps {
57
+ doc: DesignDoc;
58
+ /** The doc's page. */
59
+ href?: string | null;
60
+ target?: '_blank';
61
+ rel?: 'noopener' | 'noopener noreferrer';
62
+ className?: string;
63
+ }
64
+
65
+ export function DesignDocCardSkeleton() {
66
+ return (
67
+ <div className="flex h-full animate-pulse flex-col gap-[16px] rounded-[6px] border border-ods-border bg-ods-card p-[24px]">
68
+ <div className="flex items-center gap-[16px]">
69
+ <div className="h-16 w-16 rounded-lg bg-ods-bg" />
70
+ <div className="flex flex-1 flex-col gap-2">
71
+ <div className="h-5 w-3/4 rounded bg-ods-bg" />
72
+ <div className="h-4 w-1/3 rounded bg-ods-bg/70" />
73
+ </div>
74
+ </div>
75
+ <div className="h-4 w-full rounded bg-ods-bg/60" />
76
+ <div className="h-4 w-5/6 rounded bg-ods-bg/60" />
77
+ </div>
78
+ );
79
+ }
80
+
81
+ export function DesignDocCard({ doc, href, target, rel, className }: DesignDocCardProps) {
82
+ const status = designDocSignOffStatus(doc.completion);
83
+ const people = designDocPeople(doc);
84
+ const link = safeHref(href ?? null);
85
+ const body = (
86
+ <>
87
+ <div className="flex w-full items-center gap-[16px]">
88
+ <div className="flex h-16 w-16 flex-shrink-0 items-center justify-center rounded-lg border border-ods-border bg-ods-bg text-ods-accent">
89
+ <FileContentIcon size={32} />
90
+ </div>
91
+ <div className="flex min-w-0 flex-1 flex-col">
92
+ <div className="flex min-h-[48px] items-center">
93
+ <h3 className="line-clamp-2 flex-1 text-ods-text-primary text-h3">{doc.title || 'Untitled design doc'}</h3>
94
+ </div>
95
+ <div className="flex min-h-[20px] items-center">
96
+ <p className="truncate text-ods-text-secondary text-h5">{designDocMetaLine(doc)}</p>
97
+ </div>
98
+ </div>
99
+ <StatusBadge
100
+ text={status.label.toUpperCase()}
101
+ colorScheme={status.scheme}
102
+ className="hidden border border-ods-border md:inline-flex"
103
+ />
104
+ </div>
105
+ <div className="md:hidden">
106
+ <StatusBadge
107
+ text={status.label.toUpperCase()}
108
+ colorScheme={status.scheme}
109
+ className="border border-ods-border"
110
+ />
111
+ </div>
112
+ <div className="flex min-h-[72px] items-center">
113
+ <p className="line-clamp-3 text-ods-text-secondary text-h4">{doc.summary || ''}</p>
114
+ </div>
115
+ <div className="flex-1" />
116
+ <div className="flex w-full items-center justify-between gap-2">
117
+ <span className="truncate text-ods-text-secondary text-h6">{designDocSignOffLabel(doc.completion)}</span>
118
+ {people.length > 0 ? <AvatarStack size="xs" people={people} className="shrink-0" /> : null}
119
+ </div>
120
+ </>
121
+ );
122
+ const outer = `flex h-full flex-col gap-[16px] rounded-[6px] border border-ods-border bg-ods-card p-[24px] transition-all hover:border-ods-accent ${className ?? ''}`;
123
+ return link ? (
124
+ <a href={link} target={target} rel={rel} className={outer}>
125
+ {body}
126
+ </a>
127
+ ) : (
128
+ <div className={outer}>{body}</div>
129
+ );
130
+ }
@@ -33,6 +33,7 @@ import React, { type ReactNode } from 'react';
33
33
  import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context';
34
34
  import Image from '../../../embed-shims/next-image';
35
35
  import { useRouter } from '../../../embed-shims/next-navigation';
36
+ import type { DesignDoc } from '../../../types/design-doc';
36
37
  import {
37
38
  TRUST_CENTER_DOCUMENT_TYPE,
38
39
  TRUST_CENTER_PAGE_PATH,
@@ -62,6 +63,7 @@ import { CodeIcon } from '../../icons-v2-generated/coding/code-icon';
62
63
  import { CodeSquareIcon } from '../../icons-v2-generated/coding/code-square-icon';
63
64
  import { CodingBranchIcon } from '../../icons-v2-generated/coding/coding-branch-icon';
64
65
  import { CodingCommitIcon } from '../../icons-v2-generated/coding/coding-commit-icon';
66
+ import { CodingMergeIcon } from '../../icons-v2-generated/coding/coding-merge-icon';
65
67
  import { CodingPullRequestIcon } from '../../icons-v2-generated/coding/coding-pull-request-icon';
66
68
  import { PackageIcon } from '../../icons-v2-generated/coding/package-icon';
67
69
  import { CallIcon } from '../../icons-v2-generated/communication/call-icon';
@@ -115,6 +117,7 @@ import { CaseStudyCardSkeleton } from './case-study-card';
115
117
  import { ChatVideoEntityCard } from './chat-video-entity-card';
116
118
  import { CustomerInterviewCardSkeleton } from './customer-interview-card';
117
119
  import { DeletedDataCard } from './deleted-data-card';
120
+ import { designDocMetaLine, designDocSignOffLabel, designDocSignOffStatus } from './design-doc-card';
118
121
  import {
119
122
  parseGithubTitle,
120
123
  formatActivityId,
@@ -1068,6 +1071,42 @@ function CustomerInterviewChatCard({
1068
1071
  );
1069
1072
  }
1070
1073
 
1074
+ /**
1075
+ * Design doc → the doc glyph, its sign-off state as the pill (Ready / In review / Blocked, `designDocSignOffStatus`)
1076
+ * and "DRI · updated · n/m reviews signed off" under the title. The hub's card route attaches the doc's list row as
1077
+ * `item.doc`; a row without it (an older hub) renders the ref's own title and preview.
1078
+ */
1079
+ function DesignDocChatCard({
1080
+ item,
1081
+ chatRef,
1082
+ isNewTab,
1083
+ discuss,
1084
+ }: {
1085
+ item: (ChatCardItem & { doc?: DesignDoc }) | undefined;
1086
+ chatRef: ChatRef;
1087
+ isNewTab: boolean;
1088
+ discuss?: CardDiscussAction;
1089
+ }) {
1090
+ const displayRef = fetchedItemDisplayRef(item, chatRef);
1091
+ const doc = item?.doc;
1092
+ const status = doc ? designDocSignOffStatus(doc.completion) : null;
1093
+ return (
1094
+ <MingoInfoCard
1095
+ title={doc?.title ?? displayRef.title}
1096
+ description={
1097
+ doc
1098
+ ? [designDocMetaLine(doc), designDocSignOffLabel(doc.completion)].join(' · ')
1099
+ : (displayRef.preview ?? undefined)
1100
+ }
1101
+ icon={<FileContentIcon size={24} />}
1102
+ status={status ? { label: status.label, variant: status.tag } : { label: 'Design doc', variant: 'grey' }}
1103
+ anchorProps={buildAnchorProps(displayRef.url, isNewTab)}
1104
+ menuGroups={cardMenuGroups(displayRef.url, discuss)}
1105
+ menuAriaLabel="Design doc actions"
1106
+ />
1107
+ );
1108
+ }
1109
+
1071
1110
  /** Investor update → presentation icon + "Investor update" pill. Title falls
1072
1111
  * back to "Update #N" when the row has no explicit title. */
1073
1112
  function InvestorUpdateChatCard({
@@ -1411,7 +1450,6 @@ interface GlyphCardConfig {
1411
1450
  /** The OpenFrame logo every OpenFrame surface uses (the `openframe` icon name). */
1412
1451
  const OpenFrameGlyph = resolveIcon('openframe');
1413
1452
  const REF_GLYPH_CARD_CONFIGS: Record<string, GlyphCardConfig> = {
1414
- design_doc: { label: 'Design doc', icon: () => <FileContentIcon size={24} /> },
1415
1453
  openframe_tenant: { label: 'OpenFrame tenant', icon: () => <OpenFrameGlyph size={24} /> },
1416
1454
  prospect_call: { label: 'Prospect call', icon: () => <CallIcon size={24} />, media: true },
1417
1455
  // Code intelligence (product-hub internal): the review rules a repository is
@@ -1429,6 +1467,9 @@ const REF_GLYPH_CARD_CONFIGS: Record<string, GlyphCardConfig> = {
1429
1467
  code_symbol: { label: 'Code symbol', icon: () => <CodeSquareIcon size={24} /> },
1430
1468
  code_duplicate: { label: 'Duplicate code', icon: () => <Copy01Icon size={24} /> },
1431
1469
  code_impact: { label: 'Change impact', icon: () => <CodingPullRequestIcon size={24} /> },
1470
+ // A change set: pull requests across repositories declared as one change, with the ClickUp tasks and design
1471
+ // docs its pull requests are attached to. ONE card for it wherever a set is shown (chat, a design doc's page).
1472
+ change_set: { label: 'Change set', icon: () => <CodingMergeIcon size={24} /> },
1432
1473
  };
1433
1474
  function refGlyphRegistryEntries(): Record<string, ChatCardRegistryEntry> {
1434
1475
  return registryEntries(REF_GLYPH_CARD_CONFIGS, (cfg, docType) =>
@@ -1698,6 +1739,17 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
1698
1739
  />
1699
1740
  ),
1700
1741
  },
1742
+ design_doc: {
1743
+ label: 'Design doc',
1744
+ contentRefType: 'design_doc',
1745
+ bareInline: true,
1746
+ noComposedHref: true,
1747
+ fallbackHref: (item: { url?: string | null }) => item?.url ?? null,
1748
+ skeleton: () => <MingoInfoCardSkeleton />,
1749
+ render: (item, chatRef, opts) => (
1750
+ <DesignDocChatCard item={item} chatRef={chatRef} isNewTab={opts.isNewTab} discuss={opts.discuss} />
1751
+ ),
1752
+ },
1701
1753
  customer_interview: {
1702
1754
  label: 'Customer interview',
1703
1755
  contentRefType: 'customer_interview',
@@ -69,6 +69,15 @@ export {
69
69
  type OnboardingGuideCardProps,
70
70
  } from './onboarding-guide-card';
71
71
  export { RoadmapCard, RoadmapCardSkeleton, type RoadmapCardProps, type VoteType } from './roadmap-card';
72
+ export {
73
+ DesignDocCard,
74
+ DesignDocCardSkeleton,
75
+ designDocMetaLine,
76
+ designDocPeople,
77
+ designDocSignOffLabel,
78
+ designDocSignOffStatus,
79
+ type DesignDocCardProps,
80
+ } from './design-doc-card';
72
81
  export { RoadmapVoteButton, type RoadmapVoteButtonProps } from './roadmap-vote-button';
73
82
  export { TaskTypeIcon, type TaskTypeIconProps } from './task-type-icon';
74
83
  export {
@@ -128,6 +128,7 @@ const BUILDERS: Record<string, (ids: string[], base: string) => string> = {
128
128
  code_symbol: (ids, b) => `${b}/api/code-graph/symbols?ids=${ids.join(',')}`,
129
129
  code_duplicate: (ids, b) => `${b}/api/code-graph/duplicates?ids=${ids.join(',')}`,
130
130
  code_impact: (ids, b) => `${b}/api/code-graph/impacts?ids=${ids.join(',')}`,
131
+ change_set: (ids, b) => `${b}/api/code-graph/change-sets?ids=${ids.join(',')}`,
131
132
  // Single-record live source: the route ignores `?ids=` and returns the ONE
132
133
  // `TrustCenterPublic` object (card id `TRUST_CENTER_CARD_ID`); see
133
134
  // `extractCardItems` for how the loader matches it back.
@@ -91,6 +91,7 @@ export const SOURCE_ICON_NAMES: Record<string, string> = {
91
91
  'code-symbols': 'box',
92
92
  'code-duplicates': 'search',
93
93
  'code-impact': 'activity',
94
+ 'change-sets': 'coding-branch',
94
95
  };
95
96
 
96
97
  /** Lookup an icon name by RagTableConfig.id. Returns undefined when
@@ -185,6 +186,7 @@ export const SOURCE_LABELS_BY_TABLE: Record<string, string> = {
185
186
  'code-symbols': 'Code Symbols',
186
187
  'code-duplicates': 'Duplicates',
187
188
  'code-impact': 'Change Impact',
189
+ 'change-sets': 'Change Sets',
188
190
  };
189
191
 
190
192
  /** Lookup a human-readable label by RagTableConfig.id. Falls back
@@ -281,6 +283,7 @@ export const DEFAULT_DOCUMENT_TYPE_TO_TABLE_ID: Record<string, string> = {
281
283
  code_symbol: 'code-symbols',
282
284
  code_duplicate: 'code-duplicates',
283
285
  code_impact: 'code-impact',
286
+ change_set: 'change-sets',
284
287
  };
285
288
 
286
289
  /**