@flamingo-stack/openframe-frontend-core 0.0.200 → 0.0.201-snapshot.20260521220457
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-V2FNIPZJ.cjs → chunk-3B43AHYE.cjs} +2 -2
- package/dist/{chunk-TMD44IKJ.js.map → chunk-3B43AHYE.cjs.map} +1 -1
- package/dist/{chunk-TMD44IKJ.js → chunk-55HF462A.js} +2 -2
- package/dist/chunk-55HF462A.js.map +1 -0
- package/dist/{chunk-ZOM75JOY.js → chunk-IDULPYOU.js} +4458 -4245
- package/dist/chunk-IDULPYOU.js.map +1 -0
- package/dist/{chunk-C3M6R6JH.cjs → chunk-JIKTMXTZ.cjs} +1277 -1064
- package/dist/chunk-JIKTMXTZ.cjs.map +1 -0
- package/dist/components/chat/approval-request-message.d.ts.map +1 -1
- package/dist/components/chat/chat-container.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/types/message.types.d.ts +34 -0
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/features/index.cjs +13 -3
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.d.ts +1 -1
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/components/features/index.js +16 -6
- package/dist/components/index.cjs +15 -3
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +16 -4
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/providers/theme-provider.d.ts +69 -0
- package/dist/components/providers/theme-provider.d.ts.map +1 -0
- package/dist/components/ui/entity-image.d.ts +9 -0
- package/dist/components/ui/entity-image.d.ts.map +1 -0
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +5 -3
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +4 -2
- package/dist/components/ui/organization-card.d.ts.map +1 -1
- package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
- package/dist/index.cjs +15 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -4
- package/package.json +2 -1
- package/src/components/chat/approval-request-message.tsx +106 -92
- package/src/components/chat/chat-container.tsx +8 -4
- package/src/components/chat/chat-message-enhanced.tsx +51 -9
- package/src/components/chat/chat-message-list.tsx +27 -19
- package/src/components/chat/types/message.types.ts +35 -0
- package/src/components/features/index.ts +15 -1
- package/src/components/layout/title-block.tsx +6 -30
- package/src/components/providers/theme-provider.tsx +130 -0
- package/src/components/ui/button/button.tsx +1 -1
- package/src/components/ui/checkbox-block.tsx +13 -13
- package/src/components/ui/entity-image.tsx +56 -0
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/organization-card.tsx +4 -8
- package/src/components/ui/simple-markdown-renderer.tsx +248 -2
- package/src/stories/CheckboxBlock.stories.tsx +1 -3
- package/src/stories/OrganizationCard.stories.tsx +14 -0
- package/src/stories/Theme.stories.tsx +350 -0
- package/src/styles/README.md +271 -174
- package/src/styles/dark_theme.tokens.json +982 -0
- package/src/styles/light_theme.tokens.json +982 -0
- package/src/styles/ods-colors.css +225 -146
- package/src/styles/ods_color_tokens.json +1 -300
- package/dist/chunk-C3M6R6JH.cjs.map +0 -1
- package/dist/chunk-V2FNIPZJ.cjs.map +0 -1
- package/dist/chunk-ZOM75JOY.js.map +0 -1
- package/dist/components/features/organization-icon.d.ts +0 -80
- package/dist/components/features/organization-icon.d.ts.map +0 -1
- package/src/components/features/organization-icon.tsx +0 -175
package/dist/index.js
CHANGED
|
@@ -100,6 +100,7 @@ import {
|
|
|
100
100
|
CursorPaginationSimple,
|
|
101
101
|
CveLink,
|
|
102
102
|
DEFAULT_OS_PLATFORM,
|
|
103
|
+
DEFAULT_THEME,
|
|
103
104
|
DashboardInfoCard,
|
|
104
105
|
DataTable,
|
|
105
106
|
DataTableBody,
|
|
@@ -142,6 +143,7 @@ import {
|
|
|
142
143
|
DrawerTrigger,
|
|
143
144
|
DropdownButton,
|
|
144
145
|
DynamicThemeProvider,
|
|
146
|
+
EntityImage,
|
|
145
147
|
EntitySummaryEditor,
|
|
146
148
|
EntityVideoSection,
|
|
147
149
|
ErrorBoundary,
|
|
@@ -258,7 +260,6 @@ import {
|
|
|
258
260
|
OnboardingStepCard,
|
|
259
261
|
OnboardingWalkthrough,
|
|
260
262
|
OrganizationCard,
|
|
261
|
-
OrganizationIcon,
|
|
262
263
|
PageActions,
|
|
263
264
|
PageContainer,
|
|
264
265
|
PageError,
|
|
@@ -337,6 +338,8 @@ import {
|
|
|
337
338
|
StatusIndicator,
|
|
338
339
|
StickySectionNav,
|
|
339
340
|
Switch,
|
|
341
|
+
THEME_ATTRIBUTE,
|
|
342
|
+
THEME_STORAGE_KEY,
|
|
340
343
|
TMCG_ROLES,
|
|
341
344
|
TMCG_ROLE_DISPLAY_NAMES,
|
|
342
345
|
TMCG_SOCIAL_PLATFORMS,
|
|
@@ -362,6 +365,7 @@ import {
|
|
|
362
365
|
TagsManager,
|
|
363
366
|
TagsSelector,
|
|
364
367
|
Textarea,
|
|
368
|
+
ThemeProvider,
|
|
365
369
|
ThinkingDisplay,
|
|
366
370
|
TicketAttachmentsList,
|
|
367
371
|
TicketCard,
|
|
@@ -503,12 +507,14 @@ import {
|
|
|
503
507
|
usePageActionsBottomPadding,
|
|
504
508
|
useRealtimeChunkProcessor,
|
|
505
509
|
useSectionNavigation,
|
|
510
|
+
useTheme,
|
|
511
|
+
useThemeToggle,
|
|
506
512
|
validateAccessCode,
|
|
507
513
|
validateAndConsumeAccessCode,
|
|
508
514
|
validateEmailDomain,
|
|
509
515
|
validateEmailDomainList,
|
|
510
516
|
validatePhoneNumber
|
|
511
|
-
} from "./chunk-
|
|
517
|
+
} from "./chunk-IDULPYOU.js";
|
|
512
518
|
import {
|
|
513
519
|
CommandApprovalToast,
|
|
514
520
|
GraphQLIntrospector,
|
|
@@ -622,7 +628,7 @@ import {
|
|
|
622
628
|
SkeletonText,
|
|
623
629
|
SplitButton,
|
|
624
630
|
buttonVariants
|
|
625
|
-
} from "./chunk-
|
|
631
|
+
} from "./chunk-55HF462A.js";
|
|
626
632
|
import {
|
|
627
633
|
AboutIcon,
|
|
628
634
|
AiRobotIcon,
|
|
@@ -924,6 +930,7 @@ export {
|
|
|
924
930
|
CutVendorCostsIcon,
|
|
925
931
|
CveLink,
|
|
926
932
|
DEFAULT_OS_PLATFORM,
|
|
933
|
+
DEFAULT_THEME,
|
|
927
934
|
DashboardIcon,
|
|
928
935
|
DashboardInfoCard,
|
|
929
936
|
DataTable,
|
|
@@ -991,6 +998,7 @@ export {
|
|
|
991
998
|
EditProfileIcon,
|
|
992
999
|
ElestioLogo,
|
|
993
1000
|
EmptyVendorIcon,
|
|
1001
|
+
EntityImage,
|
|
994
1002
|
EntitySummaryEditor,
|
|
995
1003
|
EntityVideoSection,
|
|
996
1004
|
ErrorBoundary,
|
|
@@ -1170,7 +1178,6 @@ export {
|
|
|
1170
1178
|
OpenSourceIcon,
|
|
1171
1179
|
OpenmspLogo,
|
|
1172
1180
|
OrganizationCard,
|
|
1173
|
-
OrganizationIcon,
|
|
1174
1181
|
OrganizationsIcon,
|
|
1175
1182
|
PLAY_ICON_PATH,
|
|
1176
1183
|
PackageIcon,
|
|
@@ -1293,6 +1300,8 @@ export {
|
|
|
1293
1300
|
StickySectionNav,
|
|
1294
1301
|
SunIcon,
|
|
1295
1302
|
Switch,
|
|
1303
|
+
THEME_ATTRIBUTE,
|
|
1304
|
+
THEME_STORAGE_KEY,
|
|
1296
1305
|
TMCG_ROLES,
|
|
1297
1306
|
TMCG_ROLE_DISPLAY_NAMES,
|
|
1298
1307
|
TMCG_SOCIAL_PLATFORMS,
|
|
@@ -1320,6 +1329,7 @@ export {
|
|
|
1320
1329
|
TagsSelector,
|
|
1321
1330
|
TelegramIcon,
|
|
1322
1331
|
Textarea,
|
|
1332
|
+
ThemeProvider,
|
|
1323
1333
|
ThinkingDisplay,
|
|
1324
1334
|
ThumbsDownIcon,
|
|
1325
1335
|
ThumbsUpIcon,
|
|
@@ -1558,6 +1568,8 @@ export {
|
|
|
1558
1568
|
useSectionNavigation,
|
|
1559
1569
|
useSmUp,
|
|
1560
1570
|
useTablePagination,
|
|
1571
|
+
useTheme,
|
|
1572
|
+
useThemeToggle,
|
|
1561
1573
|
useThrottle,
|
|
1562
1574
|
useToast,
|
|
1563
1575
|
useValidatePlatform,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flamingo-stack/openframe-frontend-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.201-snapshot.20260521220457",
|
|
4
4
|
"description": "Shared design system and components for all Flamingo platforms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -211,6 +211,7 @@
|
|
|
211
211
|
"recharts": "^3.1.0",
|
|
212
212
|
"rehype-highlight": "^7.0.2",
|
|
213
213
|
"rehype-raw": "^7.0.0",
|
|
214
|
+
"rehype-sanitize": "^6.0.0",
|
|
214
215
|
"remark": "^15.0.1",
|
|
215
216
|
"remark-breaks": "^4.0.0",
|
|
216
217
|
"remark-gfm": "^4.0.1",
|
|
@@ -6,11 +6,77 @@ import { Button } from "../ui/button"
|
|
|
6
6
|
import { Tag } from "../ui/tag"
|
|
7
7
|
import { CheckCircle, XCircle } from "lucide-react"
|
|
8
8
|
import type { ApprovalRequestMessageProps } from "./types"
|
|
9
|
+
import type { ApprovalRequestField } from "./types/message.types"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Stacked label/value rows for the approval card's structured field
|
|
13
|
+
* list. Labels are tiny uppercase muted text; values render as primary
|
|
14
|
+
* text with `whitespace-pre-wrap` so multi-line descriptions
|
|
15
|
+
* (`content`, `resolution`, etc.) keep their structure. Mirrored across
|
|
16
|
+
* the pending + resolved branches so an approved ticket reads the same
|
|
17
|
+
* way it did at decision time.
|
|
18
|
+
*/
|
|
19
|
+
function ApprovalFieldList({ fields }: { fields: ApprovalRequestField[] }) {
|
|
20
|
+
return (
|
|
21
|
+
<dl className="flex flex-col gap-2.5 mt-1">
|
|
22
|
+
{fields.map((f, i) => (
|
|
23
|
+
<div key={i} className="flex flex-col gap-0.5">
|
|
24
|
+
<dt className="font-['DM_Sans'] font-semibold text-[11px] uppercase tracking-wide text-ods-text-tertiary leading-4">
|
|
25
|
+
{f.label}
|
|
26
|
+
</dt>
|
|
27
|
+
<dd className="font-['DM_Sans'] text-sm text-ods-text-primary leading-5 whitespace-pre-wrap break-words">
|
|
28
|
+
{f.value}
|
|
29
|
+
</dd>
|
|
30
|
+
</div>
|
|
31
|
+
))}
|
|
32
|
+
</dl>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Shared body for both pending and resolved branches of
|
|
38
|
+
* `<ApprovalRequestMessage>`. The pending card adds Approve/Reject
|
|
39
|
+
* buttons below; the resolved card adds an Approved/Rejected `<Tag>`.
|
|
40
|
+
* Everything ABOVE the footer — command bar, icon, structured-fields
|
|
41
|
+
* stack, explanation paragraph — is identical, so the body lives here
|
|
42
|
+
* to prevent silent drift between the two render paths (a prior
|
|
43
|
+
* version already had a `break-words` vs `break-all` mismatch on the
|
|
44
|
+
* `<code>` element from an out-of-sync copy-paste edit).
|
|
45
|
+
*/
|
|
46
|
+
function ApprovalCardBody({
|
|
47
|
+
data,
|
|
48
|
+
}: {
|
|
49
|
+
data: ApprovalRequestMessageProps['data']
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<div className="flex flex-col gap-1">
|
|
53
|
+
<div className="bg-ods-bg border border-ods-border rounded-md p-3 flex gap-2 items-start max-h-32 overflow-y-auto">
|
|
54
|
+
<code className="font-['DM_Sans'] font-medium text-sm text-ods-text-primary flex-1 leading-5 whitespace-pre-wrap break-words">
|
|
55
|
+
{data.command}
|
|
56
|
+
</code>
|
|
57
|
+
{data.icon && (
|
|
58
|
+
<div className="w-4 h-4 shrink-0 text-ods-text-tertiary">
|
|
59
|
+
{data.icon}
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
{data.fields && data.fields.length > 0 ? (
|
|
64
|
+
<ApprovalFieldList fields={data.fields} />
|
|
65
|
+
) : (
|
|
66
|
+
data.explanation && (
|
|
67
|
+
<p className="font-['DM_Sans'] font-medium text-sm text-ods-text-secondary leading-5 whitespace-pre-line break-words">
|
|
68
|
+
{data.explanation}
|
|
69
|
+
</p>
|
|
70
|
+
)
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
9
75
|
|
|
10
76
|
const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessageProps>(
|
|
11
77
|
({ className, data, onApprove, onReject, status = 'pending', ...props }, ref) => {
|
|
12
78
|
const [isProcessing, setIsProcessing] = useState(false)
|
|
13
|
-
|
|
79
|
+
|
|
14
80
|
const handleApprove = async () => {
|
|
15
81
|
setIsProcessing(true)
|
|
16
82
|
try {
|
|
@@ -28,49 +94,7 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
28
94
|
setIsProcessing(false)
|
|
29
95
|
}
|
|
30
96
|
}
|
|
31
|
-
|
|
32
|
-
if (status !== 'pending') {
|
|
33
|
-
return (
|
|
34
|
-
<div
|
|
35
|
-
ref={ref}
|
|
36
|
-
className={cn(
|
|
37
|
-
"bg-ods-card border border-ods-border rounded-md p-4 mb-2 flex flex-col gap-4",
|
|
38
|
-
className
|
|
39
|
-
)}
|
|
40
|
-
{...props}
|
|
41
|
-
>
|
|
42
|
-
{/* Command and icon section */}
|
|
43
|
-
<div className="flex flex-col gap-1">
|
|
44
|
-
<div className="bg-ods-bg border border-ods-border rounded-md p-3 flex gap-2 items-start max-h-32 overflow-y-auto">
|
|
45
|
-
<code className="font-['DM_Sans'] font-medium text-sm text-ods-text-primary flex-1 leading-5 whitespace-pre-wrap break-words">
|
|
46
|
-
{data.command}
|
|
47
|
-
</code>
|
|
48
|
-
{data.icon && (
|
|
49
|
-
<div className="w-4 h-4 shrink-0 text-ods-text-tertiary">
|
|
50
|
-
{data.icon}
|
|
51
|
-
</div>
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
{data.explanation && (
|
|
56
|
-
<p className="font-['DM_Sans'] font-medium text-sm text-ods-text-secondary leading-5 whitespace-pre-line break-words">
|
|
57
|
-
{data.explanation}
|
|
58
|
-
</p>
|
|
59
|
-
)}
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
{/* Status indicator */}
|
|
63
|
-
<div className="flex">
|
|
64
|
-
<Tag
|
|
65
|
-
label={status === 'approved' ? 'Approved' : 'Rejected'}
|
|
66
|
-
variant={status === 'approved' ? 'success' : 'error'}
|
|
67
|
-
icon={status === 'approved' ? <CheckCircle className="w-4 h-4" /> : <XCircle className="w-4 h-4" />}
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
97
|
+
|
|
74
98
|
return (
|
|
75
99
|
<div
|
|
76
100
|
ref={ref}
|
|
@@ -80,55 +104,45 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
80
104
|
)}
|
|
81
105
|
{...props}
|
|
82
106
|
>
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
<div className="
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
<ApprovalCardBody data={data} />
|
|
108
|
+
{status === 'pending' ? (
|
|
109
|
+
<div className="flex gap-4 items-center">
|
|
110
|
+
<Button
|
|
111
|
+
size="small-legacy"
|
|
112
|
+
variant="accent"
|
|
113
|
+
onClick={handleApprove}
|
|
114
|
+
disabled={isProcessing}
|
|
115
|
+
className={cn(
|
|
116
|
+
"bg-ods-accent hover:bg-ods-accent/90",
|
|
117
|
+
"font-mono font-medium md:!text-sm text-ods-bg uppercase tracking-[-0.28px]",
|
|
118
|
+
"px-2 py-1 h-auto"
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
Approve
|
|
122
|
+
</Button>
|
|
123
|
+
<Button
|
|
124
|
+
size="small-legacy"
|
|
125
|
+
variant="outline"
|
|
126
|
+
onClick={handleReject}
|
|
127
|
+
disabled={isProcessing}
|
|
128
|
+
className={cn(
|
|
129
|
+
"bg-ods-card border-ods-border",
|
|
130
|
+
"font-mono font-medium md:!text-sm text-ods-text-primary uppercase tracking-[-0.28px]",
|
|
131
|
+
"hover:bg-ods-bg px-2 py-1 h-auto"
|
|
132
|
+
)}
|
|
133
|
+
>
|
|
134
|
+
Reject
|
|
135
|
+
</Button>
|
|
94
136
|
</div>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<div className="flex gap-4 items-center">
|
|
105
|
-
<Button
|
|
106
|
-
size="small-legacy"
|
|
107
|
-
variant="accent"
|
|
108
|
-
onClick={handleApprove}
|
|
109
|
-
disabled={isProcessing}
|
|
110
|
-
className={cn(
|
|
111
|
-
"bg-ods-accent hover:bg-ods-accent/90",
|
|
112
|
-
"font-mono font-medium md:!text-sm text-ods-bg uppercase tracking-[-0.28px]",
|
|
113
|
-
"px-2 py-1 h-auto"
|
|
114
|
-
)}
|
|
115
|
-
>
|
|
116
|
-
Approve
|
|
117
|
-
</Button>
|
|
118
|
-
<Button
|
|
119
|
-
size="small-legacy"
|
|
120
|
-
variant="outline"
|
|
121
|
-
onClick={handleReject}
|
|
122
|
-
disabled={isProcessing}
|
|
123
|
-
className={cn(
|
|
124
|
-
"bg-ods-card border-ods-border",
|
|
125
|
-
"font-mono font-medium md:!text-sm text-ods-text-primary uppercase tracking-[-0.28px]",
|
|
126
|
-
"hover:bg-ods-bg px-2 py-1 h-auto"
|
|
127
|
-
)}
|
|
128
|
-
>
|
|
129
|
-
Reject
|
|
130
|
-
</Button>
|
|
131
|
-
</div>
|
|
137
|
+
) : (
|
|
138
|
+
<div className="flex">
|
|
139
|
+
<Tag
|
|
140
|
+
label={status === 'approved' ? 'Approved' : 'Rejected'}
|
|
141
|
+
variant={status === 'approved' ? 'success' : 'error'}
|
|
142
|
+
icon={status === 'approved' ? <CheckCircle className="w-4 h-4" /> : <XCircle className="w-4 h-4" />}
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
132
146
|
</div>
|
|
133
147
|
)
|
|
134
148
|
}
|
|
@@ -11,14 +11,18 @@ import type { ConnectionIndicatorProps, ChatContainerProps, ChatHeaderProps } fr
|
|
|
11
11
|
const ConnectionIndicator: React.FC<ConnectionIndicatorProps> = ({ status }) => {
|
|
12
12
|
const getStatusStyles = () => {
|
|
13
13
|
switch (status) {
|
|
14
|
+
// ODS attention tokens — same scheme used by the rest of the chat
|
|
15
|
+
// shell (StatusBadge, error toast, etc.). Hex Tailwind palette
|
|
16
|
+
// (`bg-green-500` / `bg-red-500`) would diverge from the theme and
|
|
17
|
+
// is forbidden by the host's design-token policy.
|
|
14
18
|
case 'connected':
|
|
15
|
-
return 'bg-green-
|
|
19
|
+
return 'bg-ods-attention-green-success'
|
|
16
20
|
case 'connecting':
|
|
17
|
-
return 'bg-yellow-
|
|
21
|
+
return 'bg-ods-attention-yellow-warning animate-pulse'
|
|
18
22
|
case 'disconnected':
|
|
19
|
-
return 'bg-red-
|
|
23
|
+
return 'bg-ods-attention-red-error'
|
|
20
24
|
default:
|
|
21
|
-
return 'bg-
|
|
25
|
+
return 'bg-ods-text-tertiary'
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -111,8 +111,32 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
111
111
|
// returns the cached node so React reuses the same instance.
|
|
112
112
|
let rendered = seenRendered.get(key)
|
|
113
113
|
if (!seenRendered.has(key)) {
|
|
114
|
+
// Always invoke render() — even when the metadata map has
|
|
115
|
+
// no entry for this marker. Fetch-mode card types
|
|
116
|
+
// (delivery_item, roadmap_item, internal_task, etc.) don't
|
|
117
|
+
// ship metadata in the SSE frame; they self-fetch by `id`
|
|
118
|
+
// via the host's list-API hook, so a minimal {type,id}
|
|
119
|
+
// ChatRef is all the renderer needs to mount the loader.
|
|
120
|
+
// For no-fetch types (hubspot_ticket_self, slack_message,
|
|
121
|
+
// …) without a refMatch the host's render() returns null
|
|
122
|
+
// and we fall through to the bare-cardId fallback in the
|
|
123
|
+
// `<a card://…>` override below.
|
|
124
|
+
//
|
|
125
|
+
// SYNTHETIC REF DEFAULTS: `ChatRef.title` and `ChatRef.url`
|
|
126
|
+
// are non-optional in the type; a bare `{type, id}` cast
|
|
127
|
+
// would lie to consumers that read those fields. Default
|
|
128
|
+
// `title` to the cardId (so any host renderer that prints
|
|
129
|
+
// `ref.title` shows the id rather than `undefined`) and
|
|
130
|
+
// `url` to null (matches the no-link semantics fetch-mode
|
|
131
|
+
// cards rely on — they resolve their own URL after fetch).
|
|
114
132
|
const refMatch = refs[key]
|
|
115
|
-
|
|
133
|
+
const refForRender: ChatRef = refMatch ?? {
|
|
134
|
+
type: cardType,
|
|
135
|
+
id: cardId,
|
|
136
|
+
title: cardId,
|
|
137
|
+
url: null,
|
|
138
|
+
}
|
|
139
|
+
rendered = render(refForRender)
|
|
116
140
|
seenRendered.set(key, rendered)
|
|
117
141
|
}
|
|
118
142
|
if (React.isValidElement(rendered) && rendered.type === BlockCard) {
|
|
@@ -171,15 +195,33 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
171
195
|
const key = `${cardType}:${cardId}`
|
|
172
196
|
const inline = inlineByKey?.get(key)
|
|
173
197
|
if (inline != null) return inline
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
198
|
+
// Three fallback cases — keep them DISTINCT, never blur them
|
|
199
|
+
// together. Mixing them up (which the old code did by reaching
|
|
200
|
+
// for "any same-type ref's title") makes LLM hallucinations
|
|
201
|
+
// LOOK like real cards, which the user can't tell apart from
|
|
202
|
+
// genuine references.
|
|
203
|
+
//
|
|
204
|
+
// (1) Exact ref present, renderer returned null:
|
|
205
|
+
// The marker is legit (server confirmed the row exists)
|
|
206
|
+
// but no compact-card type is registered for `cardType`.
|
|
207
|
+
// Render the ref's REAL title as plain text — accurate,
|
|
208
|
+
// just no rich UI.
|
|
209
|
+
//
|
|
210
|
+
// (2) Exact ref absent (refs map has no `${cardType}:${cardId}`):
|
|
211
|
+
// The LLM emitted a marker for an ID the server did NOT
|
|
212
|
+
// surface. Either the LLM hallucinated the id, or the
|
|
213
|
+
// refs map and the snapshot drifted (server-side bug
|
|
214
|
+
// worth fixing — see `MAX_ROWS_PER_ENTITY_GROUP` in
|
|
215
|
+
// `doc-chat-utils.ts:buildSourcesMeta`). Render the raw
|
|
216
|
+
// `cardId` so the breakage is VISIBLE; never borrow a
|
|
217
|
+
// title from an unrelated ref — that hides the bug and
|
|
218
|
+
// deceives the reader into thinking they're looking at
|
|
219
|
+
// a real card.
|
|
178
220
|
const refMatch: ChatRef | undefined = refs[key]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return <span className="text-ods-text-
|
|
221
|
+
if (refMatch) {
|
|
222
|
+
return <span className="text-ods-text-primary">{refMatch.title}</span>
|
|
223
|
+
}
|
|
224
|
+
return <span className="text-ods-text-secondary opacity-60">{cardId}</span>
|
|
183
225
|
}
|
|
184
226
|
}
|
|
185
227
|
// Unified click rule — delegated to the host's `NavLinkAnchor`
|
|
@@ -441,25 +441,33 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
441
441
|
<div ref={sentinelRef} className="h-px" />
|
|
442
442
|
)}
|
|
443
443
|
<div className="flex-1" />
|
|
444
|
-
{messages.map((message, index) =>
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
444
|
+
{messages.map((message, index) => {
|
|
445
|
+
// Hidden messages (synthetic continuation prompts the host
|
|
446
|
+
// injects after an approval card) are part of the API
|
|
447
|
+
// conversation history but never render. Skipping here
|
|
448
|
+
// keeps the visible thread coherent — see
|
|
449
|
+
// `Message.hidden` doc-comment in message.types.ts.
|
|
450
|
+
if (message.hidden) return null
|
|
451
|
+
return (
|
|
452
|
+
<ChatMessageEnhanced
|
|
453
|
+
key={message.id}
|
|
454
|
+
ref={getRegisterMessageEl(message.id)}
|
|
455
|
+
role={message.role}
|
|
456
|
+
name={message.name}
|
|
457
|
+
content={message.content}
|
|
458
|
+
timestamp={message.timestamp}
|
|
459
|
+
isTyping={index === messages.length - 1 && isTyping && message.role === 'assistant'}
|
|
460
|
+
avatar={showAvatars ? message.avatar : null}
|
|
461
|
+
showAvatar={showAvatars}
|
|
462
|
+
assistantType={message.assistantType || assistantType}
|
|
463
|
+
authorType={message.authorType}
|
|
464
|
+
assistantIcon={message.role !== 'user' ? assistantIcon : undefined}
|
|
465
|
+
chatRefs={message.chatRefs}
|
|
466
|
+
renderEntityCard={renderEntityCard}
|
|
467
|
+
NavLinkAnchor={NavLinkAnchor}
|
|
468
|
+
/>
|
|
469
|
+
)
|
|
470
|
+
})}
|
|
463
471
|
</div>
|
|
464
472
|
</div>
|
|
465
473
|
|
|
@@ -78,8 +78,24 @@ export interface ExecutingToolState {
|
|
|
78
78
|
|
|
79
79
|
// ========== Approval Request Types ==========
|
|
80
80
|
|
|
81
|
+
export interface ApprovalRequestField {
|
|
82
|
+
/** Short label — e.g. "Subject", "Priority". Rendered in a muted
|
|
83
|
+
* caps style above the value. */
|
|
84
|
+
label: string
|
|
85
|
+
/** Free-text value. Wraps and line-breaks are preserved
|
|
86
|
+
* (`whitespace-pre-wrap`). */
|
|
87
|
+
value: string
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
export interface ApprovalRequestData {
|
|
82
91
|
command: string
|
|
92
|
+
/** Structured field list — preferred over `explanation`. When set,
|
|
93
|
+
* the approval card renders a vertical label/value stack with
|
|
94
|
+
* proper spacing. Falls back to `explanation` (a single paragraph)
|
|
95
|
+
* when omitted. Keep BOTH when you want hosts on older lib
|
|
96
|
+
* versions to still see the prose; new hosts should send only
|
|
97
|
+
* `fields`. */
|
|
98
|
+
fields?: ApprovalRequestField[]
|
|
83
99
|
explanation?: string
|
|
84
100
|
icon?: React.ReactNode
|
|
85
101
|
requestId?: string
|
|
@@ -333,4 +349,23 @@ export interface Message {
|
|
|
333
349
|
* whose body is a long article). The server is the sole decision-
|
|
334
350
|
* maker — set on the metadata leading frame. */
|
|
335
351
|
scrollAnchor?: ScrollAnchor
|
|
352
|
+
/** When true the message is part of the API conversation history (sent
|
|
353
|
+
* to the LLM so it has context) but is NOT rendered in the chat UI.
|
|
354
|
+
*
|
|
355
|
+
* Used for "synthetic continuation" turns: when the user clicks Approve
|
|
356
|
+
* on a tool proposal, the host auto-fires a follow-up `sendMessage`
|
|
357
|
+
* with `hidden: true` carrying a directive like "the user just
|
|
358
|
+
* approved <tool>; ask follow-up questions per protocol". The LLM's
|
|
359
|
+
* response IS rendered (as a normal assistant message); only the
|
|
360
|
+
* trigger prompt is suppressed so the chat reads naturally:
|
|
361
|
+
*
|
|
362
|
+
* user: "open a ticket"
|
|
363
|
+
* assistant: preamble + approval card
|
|
364
|
+
* [user clicks Approve]
|
|
365
|
+
* assistant: "Now to triage faster, can you share..." ← auto-fires
|
|
366
|
+
*
|
|
367
|
+
* Without this flag the trigger prompt would surface as a confusing
|
|
368
|
+
* bubble like "(continue per protocol)" between the approval card
|
|
369
|
+
* and the AI's follow-up. */
|
|
370
|
+
hidden?: boolean
|
|
336
371
|
}
|
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
// Feature Components exports
|
|
4
4
|
export { DynamicThemeProvider, useDynamicTheme } from '../providers/dynamic-theme-provider'
|
|
5
|
+
// Canonical ODS theme system — thin wrapper over `next-themes`
|
|
6
|
+
// (manual light/dark, default dark, no-flash handled by next-themes).
|
|
7
|
+
// Headless by design: apps build their own toggle button via the lib's
|
|
8
|
+
// existing <Button> + `useThemeToggle()`.
|
|
9
|
+
export {
|
|
10
|
+
ThemeProvider,
|
|
11
|
+
useTheme,
|
|
12
|
+
useThemeToggle,
|
|
13
|
+
THEME_STORAGE_KEY,
|
|
14
|
+
THEME_ATTRIBUTE,
|
|
15
|
+
DEFAULT_THEME,
|
|
16
|
+
type Theme,
|
|
17
|
+
type ThemeProviderProps,
|
|
18
|
+
type UseThemeToggleResult,
|
|
19
|
+
} from '../providers/theme-provider'
|
|
5
20
|
export * from './array-entry-manager'
|
|
6
21
|
export * from './auth-providers-list'
|
|
7
22
|
export * from './changelog-manager'
|
|
@@ -16,7 +31,6 @@ export * from './knowledge-base-links-manager'
|
|
|
16
31
|
export * from './loading-provider'
|
|
17
32
|
export * from './media-gallery-manager'
|
|
18
33
|
export * from './more-about-button'
|
|
19
|
-
export * from './organization-icon'
|
|
20
34
|
export * from './os-type-badge'
|
|
21
35
|
export * from './os-type-badge-group'
|
|
22
36
|
export * from './parallax-image-showcase'
|
|
@@ -3,16 +3,10 @@
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import { cn } from '../../utils/cn'
|
|
5
5
|
import type { ActionsMenuGroup } from '../ui/actions-menu'
|
|
6
|
+
import { EntityImage } from '../ui/entity-image'
|
|
6
7
|
import { PageActions, type PageActionButton } from '../ui/page-actions'
|
|
7
8
|
import { BackButton } from './back-button'
|
|
8
9
|
|
|
9
|
-
function getInitials(name?: string): string {
|
|
10
|
-
if (!name) return ''
|
|
11
|
-
const words = name.trim().split(/\s+/)
|
|
12
|
-
if (words.length === 1) return words[0].charAt(0).toUpperCase()
|
|
13
|
-
return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase()
|
|
14
|
-
}
|
|
15
|
-
|
|
16
10
|
export interface TitleBlockProps {
|
|
17
11
|
title?: string
|
|
18
12
|
subtitle?: string
|
|
@@ -45,14 +39,6 @@ export function TitleBlock({
|
|
|
45
39
|
className,
|
|
46
40
|
}: TitleBlockProps) {
|
|
47
41
|
const hasActions = actions && actions.length > 0
|
|
48
|
-
const [imageFailed, setImageFailed] = React.useState(false)
|
|
49
|
-
|
|
50
|
-
React.useEffect(() => {
|
|
51
|
-
setImageFailed(false)
|
|
52
|
-
}, [image?.src])
|
|
53
|
-
|
|
54
|
-
const showImageFallback = !!image && (imageFailed || !image.src)
|
|
55
|
-
const initials = getInitials(image?.alt || title)
|
|
56
42
|
|
|
57
43
|
return (
|
|
58
44
|
<div
|
|
@@ -83,21 +69,11 @@ export function TitleBlock({
|
|
|
83
69
|
{(image || subtitle) ? (
|
|
84
70
|
<div className="flex items-center gap-[var(--spacing-system-m)] min-w-0 w-full">
|
|
85
71
|
{image && (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{initials || '?'}
|
|
92
|
-
</div>
|
|
93
|
-
) : (
|
|
94
|
-
<img
|
|
95
|
-
src={image.src}
|
|
96
|
-
alt={image.alt ?? ''}
|
|
97
|
-
onError={() => setImageFailed(true)}
|
|
98
|
-
className="size-[52px] md:size-[60px] shrink-0 rounded-md border border-ods-border object-cover"
|
|
99
|
-
/>
|
|
100
|
-
)
|
|
72
|
+
<EntityImage
|
|
73
|
+
src={image.src}
|
|
74
|
+
alt={image.alt}
|
|
75
|
+
fallbackText={image.alt || title}
|
|
76
|
+
/>
|
|
101
77
|
)}
|
|
102
78
|
<div className="flex flex-col justify-center min-w-0 flex-1">
|
|
103
79
|
{title && (
|