@windrun-huaiin/third-ui 15.1.0 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/ai/ai-chat-composer.d.ts +2 -0
- package/dist/ai/ai-chat-composer.js +47 -0
- package/dist/ai/ai-chat-composer.mjs +45 -0
- package/dist/ai/ai-markdown.d.ts +2 -0
- package/dist/ai/ai-markdown.js +36 -0
- package/dist/ai/ai-markdown.mjs +34 -0
- package/dist/ai/ai-message-actions.d.ts +2 -0
- package/dist/ai/ai-message-actions.js +14 -0
- package/dist/ai/ai-message-actions.mjs +12 -0
- package/dist/ai/ai-message-bubble.d.ts +2 -0
- package/dist/ai/ai-message-bubble.js +66 -0
- package/dist/ai/ai-message-bubble.mjs +64 -0
- package/dist/ai/ai-message-content.d.ts +2 -0
- package/dist/ai/ai-message-content.js +63 -0
- package/dist/ai/ai-message-content.mjs +61 -0
- package/dist/ai/ai-message-list.d.ts +2 -0
- package/dist/ai/ai-message-list.js +24 -0
- package/dist/ai/ai-message-list.mjs +22 -0
- package/dist/ai/ai-message-meta.d.ts +2 -0
- package/dist/ai/ai-message-meta.js +38 -0
- package/dist/ai/ai-message-meta.mjs +36 -0
- package/dist/ai/ai-status-indicator.d.ts +2 -0
- package/dist/ai/ai-status-indicator.js +51 -0
- package/dist/ai/ai-status-indicator.mjs +49 -0
- package/dist/ai/index.d.ts +11 -0
- package/dist/ai/index.js +33 -0
- package/dist/ai/index.mjs +11 -0
- package/dist/ai/types.d.ts +110 -0
- package/dist/ai/use-ai-conversation.d.ts +13 -0
- package/dist/ai/use-ai-conversation.js +276 -0
- package/dist/ai/use-ai-conversation.mjs +274 -0
- package/dist/clerk/clerk-organization-client.js +2 -2
- package/dist/clerk/clerk-organization-client.mjs +2 -2
- package/dist/clerk/clerk-page-generator.d.ts +1 -1
- package/dist/clerk/clerk-user-client.js +2 -2
- package/dist/clerk/clerk-user-client.mjs +2 -2
- package/dist/clerk/fingerprint/fingerprint-provider.js +9 -9
- package/dist/clerk/fingerprint/fingerprint-provider.mjs +9 -9
- package/dist/fuma/base/custom-header.js +4 -4
- package/dist/fuma/base/custom-header.mjs +4 -4
- package/dist/fuma/mdx/banner.js +3 -3
- package/dist/fuma/mdx/banner.mjs +3 -3
- package/dist/fuma/mdx/fuma-github-info.js +3 -3
- package/dist/fuma/mdx/fuma-github-info.mjs +3 -3
- package/dist/fuma/mdx/gradient-button.js +3 -3
- package/dist/fuma/mdx/gradient-button.mjs +3 -3
- package/dist/fuma/mdx/index.d.ts +1 -0
- package/dist/fuma/mdx/index.js +2 -0
- package/dist/fuma/mdx/index.mjs +1 -0
- package/dist/fuma/mdx/markdown-component-map.d.ts +3 -0
- package/dist/fuma/mdx/markdown-component-map.js +73 -0
- package/dist/fuma/mdx/markdown-component-map.mjs +71 -0
- package/dist/fuma/mdx/mermaid.d.ts +2 -1
- package/dist/fuma/mdx/mermaid.js +130 -6
- package/dist/fuma/mdx/mermaid.mjs +130 -6
- package/dist/fuma/mdx/toc-base.js +4 -4
- package/dist/fuma/mdx/toc-base.mjs +4 -4
- package/dist/fuma/mdx/trophy-card.js +2 -2
- package/dist/fuma/mdx/trophy-card.mjs +2 -2
- package/dist/fuma/mdx/zia-card.js +3 -3
- package/dist/fuma/mdx/zia-card.mjs +3 -3
- package/dist/fuma/mdx/zia-file.js +3 -3
- package/dist/fuma/mdx/zia-file.mjs +3 -3
- package/dist/main/ads-alert-dialog.js +2 -2
- package/dist/main/ads-alert-dialog.mjs +2 -2
- package/dist/main/credit/credit-nav-button.js +2 -2
- package/dist/main/credit/credit-nav-button.mjs +2 -2
- package/dist/main/credit/credit-overview-client.js +4 -4
- package/dist/main/credit/credit-overview-client.mjs +4 -4
- package/dist/main/footer.js +2 -2
- package/dist/main/footer.mjs +2 -2
- package/dist/main/go-to-top.js +2 -2
- package/dist/main/go-to-top.mjs +2 -2
- package/dist/main/hero-media.d.ts +14 -0
- package/dist/main/hero-media.js +12 -0
- package/dist/main/hero-media.mjs +10 -0
- package/dist/main/hero-section.d.ts +10 -0
- package/dist/main/hero-section.js +11 -0
- package/dist/main/hero-section.mjs +9 -0
- package/dist/main/index.d.ts +3 -0
- package/dist/main/index.js +6 -0
- package/dist/main/index.mjs +3 -0
- package/dist/main/info-tooltip.d.ts +8 -0
- package/dist/main/info-tooltip.js +48 -0
- package/dist/main/info-tooltip.mjs +46 -0
- package/dist/main/pill-select/x-pill-select.js +2 -2
- package/dist/main/pill-select/x-pill-select.mjs +2 -2
- package/dist/main/pill-select/x-token-input.js +2 -2
- package/dist/main/pill-select/x-token-input.mjs +2 -2
- package/dist/main/x-button.js +3 -3
- package/dist/main/x-button.mjs +3 -3
- package/package.json +16 -3
- package/src/ai/ai-chat-composer.tsx +187 -0
- package/src/ai/ai-markdown.tsx +45 -0
- package/src/ai/ai-message-actions.tsx +16 -0
- package/src/ai/ai-message-bubble.tsx +138 -0
- package/src/ai/ai-message-content.tsx +149 -0
- package/src/ai/ai-message-list.tsx +59 -0
- package/src/ai/ai-message-meta.tsx +56 -0
- package/src/ai/ai-status-indicator.tsx +61 -0
- package/src/ai/index.ts +13 -0
- package/src/ai/types.ts +131 -0
- package/src/ai/use-ai-conversation.ts +422 -0
- package/src/clerk/clerk-organization-client.tsx +5 -5
- package/src/clerk/clerk-page-generator.tsx +1 -1
- package/src/clerk/clerk-user-client.tsx +4 -4
- package/src/clerk/fingerprint/fingerprint-provider.tsx +34 -22
- package/src/fuma/base/custom-header.tsx +5 -5
- package/src/fuma/mdx/banner.tsx +3 -3
- package/src/fuma/mdx/fuma-github-info.tsx +4 -4
- package/src/fuma/mdx/gradient-button.tsx +3 -3
- package/src/fuma/mdx/index.ts +2 -1
- package/src/fuma/mdx/markdown-component-map.tsx +174 -0
- package/src/fuma/mdx/mermaid.tsx +145 -10
- package/src/fuma/mdx/toc-base.tsx +5 -5
- package/src/fuma/mdx/trophy-card.tsx +2 -2
- package/src/fuma/mdx/zia-card.tsx +3 -3
- package/src/fuma/mdx/zia-file.tsx +3 -3
- package/src/main/ads-alert-dialog.tsx +5 -5
- package/src/main/credit/credit-nav-button.tsx +3 -3
- package/src/main/credit/credit-overview-client.tsx +15 -7
- package/src/main/features.tsx +5 -3
- package/src/main/footer.tsx +4 -5
- package/src/main/go-to-top.tsx +2 -2
- package/src/main/hero-media.tsx +53 -0
- package/src/main/hero-section.tsx +36 -0
- package/src/main/index.ts +5 -0
- package/src/main/info-tooltip.tsx +99 -0
- package/src/main/language-detector.tsx +4 -4
- package/src/main/pill-select/x-pill-select.tsx +2 -2
- package/src/main/pill-select/x-token-input.tsx +2 -2
- package/src/main/x-button.tsx +4 -4
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { MessagePart } from '@windrun-huaiin/contracts/ai';
|
|
4
|
+
import { themeIconColor } from '@windrun-huaiin/base-ui/lib';
|
|
5
|
+
import { cn } from '@windrun-huaiin/lib/utils';
|
|
6
|
+
import { TrophyCard } from '../fuma/mdx/trophy-card';
|
|
7
|
+
import { AIMarkdown } from './ai-markdown';
|
|
8
|
+
import type { AIMessageContentProps } from './types';
|
|
9
|
+
|
|
10
|
+
function hasRenderablePart(part: MessagePart) {
|
|
11
|
+
if (part.type === 'text') {
|
|
12
|
+
return part.text.trim().length > 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getEmptyAssistantFallback(message: AIMessageContentProps['message']) {
|
|
19
|
+
if (message.status === 'streaming') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (message.status === 'timeout') {
|
|
24
|
+
return 'No response received. The request timed out before the model returned any content.';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (message.status === 'request_aborted' || message.status === 'stopped') {
|
|
28
|
+
return 'Response stopped before any content was returned.';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (message.status === 'failed') {
|
|
32
|
+
return message.errorMessage || 'The model did not return any visible content.';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (message.errorMessage) {
|
|
36
|
+
return message.errorMessage;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return 'The model returned no visible content.';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function renderPart(
|
|
43
|
+
part: MessagePart,
|
|
44
|
+
index: number,
|
|
45
|
+
markdownComponents?: AIMessageContentProps['markdownComponents'],
|
|
46
|
+
) {
|
|
47
|
+
if (part.type === 'text') {
|
|
48
|
+
return (
|
|
49
|
+
<AIMarkdown
|
|
50
|
+
key={`text-${index}`}
|
|
51
|
+
content={part.text}
|
|
52
|
+
components={markdownComponents}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (part.type === 'image') {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
key={`image-${index}`}
|
|
61
|
+
className="rounded-2xl border border-dashed border-border/70 px-3 py-2 text-xs text-muted-foreground"
|
|
62
|
+
>
|
|
63
|
+
Image part reserved: {part.alt || part.url}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (part.type === 'trophy_card') {
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
key={`trophy-card-${index}`}
|
|
72
|
+
className="rounded-2xl bg-muted/35 p-1 text-foreground"
|
|
73
|
+
>
|
|
74
|
+
<TrophyCard title={part.title}>
|
|
75
|
+
{part.description ? (
|
|
76
|
+
<div className="mt-2">
|
|
77
|
+
<AIMarkdown
|
|
78
|
+
content={part.description}
|
|
79
|
+
components={markdownComponents}
|
|
80
|
+
className="space-y-3 text-sm text-inherit"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
) : null}
|
|
84
|
+
</TrophyCard>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
key={`file-${index}`}
|
|
92
|
+
className="rounded-2xl border border-dashed border-border/70 px-3 py-2 text-xs text-muted-foreground"
|
|
93
|
+
>
|
|
94
|
+
File part reserved: {part.name || part.url}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function AIMessageContent({
|
|
100
|
+
message,
|
|
101
|
+
className,
|
|
102
|
+
markdownComponents,
|
|
103
|
+
}: AIMessageContentProps) {
|
|
104
|
+
const parts = message.parts.filter(hasRenderablePart);
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
message.role === 'assistant' &&
|
|
108
|
+
message.status === 'streaming' &&
|
|
109
|
+
parts.length === 0
|
|
110
|
+
) {
|
|
111
|
+
return (
|
|
112
|
+
<div
|
|
113
|
+
className={cn(
|
|
114
|
+
'flex items-center gap-2 text-sm text-muted-foreground',
|
|
115
|
+
themeIconColor,
|
|
116
|
+
className,
|
|
117
|
+
)}
|
|
118
|
+
>
|
|
119
|
+
<span>AI is thinking</span>
|
|
120
|
+
<span className="inline-flex items-center gap-1" aria-hidden="true">
|
|
121
|
+
<span className="size-1.5 rounded-full bg-current animate-pulse [animation-delay:0ms]" />
|
|
122
|
+
<span className="size-1.5 rounded-full bg-current animate-pulse [animation-delay:180ms]" />
|
|
123
|
+
<span className="size-1.5 rounded-full bg-current animate-pulse [animation-delay:360ms]" />
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (parts.length === 0) {
|
|
130
|
+
const fallbackText = getEmptyAssistantFallback(message);
|
|
131
|
+
if (!fallbackText) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<AIMarkdown
|
|
137
|
+
content={fallbackText}
|
|
138
|
+
components={markdownComponents}
|
|
139
|
+
className={cn('space-y-3 text-sm text-inherit', className)}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div className={cn('space-y-3', className)}>
|
|
146
|
+
{parts.map((part, index) => renderPart(part, index, markdownComponents))}
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ConversationMessage } from '@windrun-huaiin/contracts/ai';
|
|
4
|
+
import { cn } from '@windrun-huaiin/lib/utils';
|
|
5
|
+
import { useEffect, useRef } from 'react';
|
|
6
|
+
import type { AIMessageListProps } from './types';
|
|
7
|
+
import { AIMessageBubble } from './ai-message-bubble';
|
|
8
|
+
|
|
9
|
+
export function AIMessageList({
|
|
10
|
+
messages,
|
|
11
|
+
className,
|
|
12
|
+
contentClassName,
|
|
13
|
+
emptyText = 'No messages yet.',
|
|
14
|
+
emptyState,
|
|
15
|
+
autoScroll = true,
|
|
16
|
+
scrollBehavior = 'smooth',
|
|
17
|
+
renderMessage,
|
|
18
|
+
}: AIMessageListProps) {
|
|
19
|
+
const bottomRef = useRef<HTMLDivElement | null>(null);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!autoScroll) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
bottomRef.current?.scrollIntoView({ behavior: scrollBehavior, block: 'end' });
|
|
27
|
+
}, [autoScroll, messages, scrollBehavior]);
|
|
28
|
+
|
|
29
|
+
const content = messages.length === 0
|
|
30
|
+
? (
|
|
31
|
+
emptyState ?? (
|
|
32
|
+
<div className="rounded-2xl border border-dashed border-border p-6 text-sm text-muted-foreground">
|
|
33
|
+
{emptyText}
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
: (
|
|
38
|
+
messages.map((message: ConversationMessage) => (
|
|
39
|
+
<div key={message.id}>
|
|
40
|
+
{renderMessage ? renderMessage(message) : <AIMessageBubble message={message} />}
|
|
41
|
+
</div>
|
|
42
|
+
))
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className={cn('min-h-0 flex-1 overflow-y-auto', className)}>
|
|
47
|
+
<div
|
|
48
|
+
className={cn(
|
|
49
|
+
'mx-auto flex min-h-full w-full max-w-5xl flex-col gap-5 px-1',
|
|
50
|
+
messages.length === 0 ? 'justify-center' : 'justify-end',
|
|
51
|
+
contentClassName,
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
{content}
|
|
55
|
+
<div ref={bottomRef} />
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
|
+
import { AIStatusIndicator } from './ai-status-indicator';
|
|
5
|
+
import type { AIMessageMetaProps, AIMessageRuntimeMetadata } from './types';
|
|
6
|
+
|
|
7
|
+
function getRuntimeMetadata(message: AIMessageMetaProps['message']): AIMessageRuntimeMetadata {
|
|
8
|
+
const metadata = message.metadata?.aiRuntime;
|
|
9
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return metadata as AIMessageRuntimeMetadata;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function formatDuration(durationMs?: number) {
|
|
17
|
+
if (durationMs === undefined || Number.isNaN(durationMs)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (durationMs < 1000) {
|
|
22
|
+
return `${durationMs}ms`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return `${(durationMs / 1000).toFixed(2)}s`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function formatTime(createdAt: number) {
|
|
29
|
+
return new Intl.DateTimeFormat(undefined, {
|
|
30
|
+
hour: '2-digit',
|
|
31
|
+
minute: '2-digit',
|
|
32
|
+
}).format(createdAt);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function AIMessageMeta({
|
|
36
|
+
message,
|
|
37
|
+
className,
|
|
38
|
+
showTime = true,
|
|
39
|
+
showStatus = true,
|
|
40
|
+
showRuntime = true,
|
|
41
|
+
showFailureReason = true,
|
|
42
|
+
}: AIMessageMetaProps) {
|
|
43
|
+
const runtime = getRuntimeMetadata(message);
|
|
44
|
+
const firstToken = formatDuration(runtime.firstTokenMs);
|
|
45
|
+
const total = formatDuration(runtime.totalMs);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className={cn('flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground', className)}>
|
|
49
|
+
{showTime ? <span>{formatTime(message.createdAt)}</span> : null}
|
|
50
|
+
{showStatus ? <AIStatusIndicator message={message} /> : null}
|
|
51
|
+
{showRuntime && firstToken ? <span>First Token {firstToken}</span> : null}
|
|
52
|
+
{showRuntime && total ? <span>Total {total}</span> : null}
|
|
53
|
+
{showFailureReason && message.failureReason ? <span>Reason {message.failureReason}</span> : null}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
|
+
import type { AIStatusIndicatorProps } from './types';
|
|
5
|
+
|
|
6
|
+
function getLabel(status?: string) {
|
|
7
|
+
switch (status) {
|
|
8
|
+
case 'streaming':
|
|
9
|
+
return 'Streaming';
|
|
10
|
+
case 'completed':
|
|
11
|
+
return 'Completed';
|
|
12
|
+
case 'stopped':
|
|
13
|
+
return 'Stopped';
|
|
14
|
+
case 'timeout':
|
|
15
|
+
return 'Timeout';
|
|
16
|
+
case 'request_aborted':
|
|
17
|
+
return 'Aborted';
|
|
18
|
+
case 'failed':
|
|
19
|
+
return 'Failed';
|
|
20
|
+
default:
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getStatusClassName(status?: string) {
|
|
26
|
+
switch (status) {
|
|
27
|
+
case 'completed':
|
|
28
|
+
return 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300';
|
|
29
|
+
case 'streaming':
|
|
30
|
+
return 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/60 dark:bg-sky-950/40 dark:text-sky-300';
|
|
31
|
+
case 'stopped':
|
|
32
|
+
return 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300';
|
|
33
|
+
case 'timeout':
|
|
34
|
+
return 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-300';
|
|
35
|
+
case 'request_aborted':
|
|
36
|
+
return 'border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900/60 dark:bg-orange-950/40 dark:text-orange-300';
|
|
37
|
+
case 'failed':
|
|
38
|
+
return 'border-red-200 bg-red-50 text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300';
|
|
39
|
+
default:
|
|
40
|
+
return 'border-border bg-background text-muted-foreground';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function AIStatusIndicator({ message, className }: AIStatusIndicatorProps) {
|
|
45
|
+
const label = getLabel(message.status);
|
|
46
|
+
if (!label) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<span
|
|
52
|
+
className={cn(
|
|
53
|
+
'inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] uppercase tracking-[0.12em]',
|
|
54
|
+
getStatusClassName(message.status),
|
|
55
|
+
className,
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{label}
|
|
59
|
+
</span>
|
|
60
|
+
);
|
|
61
|
+
}
|
package/src/ai/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
export { getMessageText, getTextParts, type ConversationMessage, type MessagePart } from '@windrun-huaiin/contracts/ai';
|
|
4
|
+
export * from './types';
|
|
5
|
+
export * from './use-ai-conversation';
|
|
6
|
+
export * from './ai-chat-composer';
|
|
7
|
+
export * from './ai-message-list';
|
|
8
|
+
export * from './ai-message-bubble';
|
|
9
|
+
export * from './ai-markdown';
|
|
10
|
+
export * from './ai-message-content';
|
|
11
|
+
export * from './ai-message-meta';
|
|
12
|
+
export * from './ai-message-actions';
|
|
13
|
+
export * from './ai-status-indicator';
|
package/src/ai/types.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AIRuntimeRequest,
|
|
3
|
+
AIStreamEvent,
|
|
4
|
+
ConversationMessage,
|
|
5
|
+
MessagePart,
|
|
6
|
+
} from '@windrun-huaiin/contracts/ai';
|
|
7
|
+
import type { ComponentType, ReactNode, RefObject } from 'react';
|
|
8
|
+
|
|
9
|
+
export type AIConversationTransport = (
|
|
10
|
+
input: AIRuntimeRequest,
|
|
11
|
+
signal: AbortSignal,
|
|
12
|
+
) => Promise<Response>;
|
|
13
|
+
|
|
14
|
+
export type AIConversationState = {
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
messages: ConversationMessage[];
|
|
17
|
+
isStreaming: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type AIConversationOptions = {
|
|
22
|
+
endpoint: string;
|
|
23
|
+
initialSessionId?: string;
|
|
24
|
+
initialMessages?: ConversationMessage[];
|
|
25
|
+
modelName?: string;
|
|
26
|
+
metadata?: Record<string, unknown>;
|
|
27
|
+
transport?: AIConversationTransport;
|
|
28
|
+
onEvent?: (event: AIStreamEvent) => void;
|
|
29
|
+
onError?: (error: Error) => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type SendMessageInput = {
|
|
33
|
+
text: string;
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type AIChatComposerProps = {
|
|
38
|
+
value: string;
|
|
39
|
+
onChange: (value: string) => void;
|
|
40
|
+
onSubmit: () => void;
|
|
41
|
+
onStop?: () => void;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
isStreaming?: boolean;
|
|
44
|
+
placeholder?: string;
|
|
45
|
+
className?: string;
|
|
46
|
+
leftSlot?: ReactNode;
|
|
47
|
+
attachments?: ReactNode;
|
|
48
|
+
helper?: ReactNode;
|
|
49
|
+
submitLabel?: string;
|
|
50
|
+
stopLabel?: string;
|
|
51
|
+
minHeight?: number;
|
|
52
|
+
maxHeight?: number;
|
|
53
|
+
submitOnEnter?: boolean;
|
|
54
|
+
shellClassName?: string;
|
|
55
|
+
textareaClassName?: string;
|
|
56
|
+
submitControl?: ReactNode;
|
|
57
|
+
stopControl?: ReactNode;
|
|
58
|
+
textareaRef?: RefObject<HTMLTextAreaElement | null>;
|
|
59
|
+
secondaryActions?: ReactNode;
|
|
60
|
+
actionLayout?: 'inline' | 'stacked';
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type AIMessageBubbleProps = {
|
|
64
|
+
message: ConversationMessage;
|
|
65
|
+
className?: string;
|
|
66
|
+
cardClassName?: string;
|
|
67
|
+
contentClassName?: string;
|
|
68
|
+
footerClassName?: string;
|
|
69
|
+
maxWidthClassName?: string;
|
|
70
|
+
showRoleLabel?: boolean;
|
|
71
|
+
markdownComponents?: AIMarkdownComponentMap;
|
|
72
|
+
showFooter?: boolean;
|
|
73
|
+
renderContent?: (message: ConversationMessage) => ReactNode;
|
|
74
|
+
renderMeta?: (message: ConversationMessage) => ReactNode;
|
|
75
|
+
renderActions?: (message: ConversationMessage) => ReactNode;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type AIMessageListProps = {
|
|
79
|
+
messages: ConversationMessage[];
|
|
80
|
+
className?: string;
|
|
81
|
+
contentClassName?: string;
|
|
82
|
+
emptyText?: string;
|
|
83
|
+
emptyState?: ReactNode;
|
|
84
|
+
autoScroll?: boolean;
|
|
85
|
+
scrollBehavior?: ScrollBehavior;
|
|
86
|
+
renderMessage?: (message: ConversationMessage) => ReactNode;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type AIStatusIndicatorProps = {
|
|
90
|
+
message: ConversationMessage;
|
|
91
|
+
className?: string;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export type AIMessageContentProps = {
|
|
95
|
+
message: ConversationMessage;
|
|
96
|
+
className?: string;
|
|
97
|
+
markdownComponents?: AIMarkdownComponentMap;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type AIMessageMetaProps = {
|
|
101
|
+
message: ConversationMessage;
|
|
102
|
+
className?: string;
|
|
103
|
+
showTime?: boolean;
|
|
104
|
+
showStatus?: boolean;
|
|
105
|
+
showRuntime?: boolean;
|
|
106
|
+
showFailureReason?: boolean;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type AIMessageActionsProps = {
|
|
110
|
+
className?: string;
|
|
111
|
+
children?: ReactNode;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type AIMarkdownComponentMap = Record<string, ComponentType<any>>;
|
|
115
|
+
|
|
116
|
+
export type AIMarkdownProps = {
|
|
117
|
+
content: string;
|
|
118
|
+
className?: string;
|
|
119
|
+
components?: AIMarkdownComponentMap;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type TextMessagePart = Extract<MessagePart, { type: 'text' }>;
|
|
123
|
+
|
|
124
|
+
export type AIMessageRuntimeMetadata = {
|
|
125
|
+
requestStartedAt?: number;
|
|
126
|
+
streamStartedAt?: number;
|
|
127
|
+
firstTokenAt?: number;
|
|
128
|
+
firstTokenMs?: number;
|
|
129
|
+
completedAt?: number;
|
|
130
|
+
totalMs?: number;
|
|
131
|
+
};
|