@windrun-huaiin/third-ui 15.1.1 → 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.
Files changed (125) hide show
  1. package/LICENSE +1 -1
  2. package/dist/ai/ai-chat-composer.d.ts +2 -0
  3. package/dist/ai/ai-chat-composer.js +47 -0
  4. package/dist/ai/ai-chat-composer.mjs +45 -0
  5. package/dist/ai/ai-markdown.d.ts +2 -0
  6. package/dist/ai/ai-markdown.js +36 -0
  7. package/dist/ai/ai-markdown.mjs +34 -0
  8. package/dist/ai/ai-message-actions.d.ts +2 -0
  9. package/dist/ai/ai-message-actions.js +14 -0
  10. package/dist/ai/ai-message-actions.mjs +12 -0
  11. package/dist/ai/ai-message-bubble.d.ts +2 -0
  12. package/dist/ai/ai-message-bubble.js +66 -0
  13. package/dist/ai/ai-message-bubble.mjs +64 -0
  14. package/dist/ai/ai-message-content.d.ts +2 -0
  15. package/dist/ai/ai-message-content.js +63 -0
  16. package/dist/ai/ai-message-content.mjs +61 -0
  17. package/dist/ai/ai-message-list.d.ts +2 -0
  18. package/dist/ai/ai-message-list.js +24 -0
  19. package/dist/ai/ai-message-list.mjs +22 -0
  20. package/dist/ai/ai-message-meta.d.ts +2 -0
  21. package/dist/ai/ai-message-meta.js +38 -0
  22. package/dist/ai/ai-message-meta.mjs +36 -0
  23. package/dist/ai/ai-status-indicator.d.ts +2 -0
  24. package/dist/ai/ai-status-indicator.js +51 -0
  25. package/dist/ai/ai-status-indicator.mjs +49 -0
  26. package/dist/ai/index.d.ts +11 -0
  27. package/dist/ai/index.js +33 -0
  28. package/dist/ai/index.mjs +11 -0
  29. package/dist/ai/types.d.ts +110 -0
  30. package/dist/ai/use-ai-conversation.d.ts +13 -0
  31. package/dist/ai/use-ai-conversation.js +276 -0
  32. package/dist/ai/use-ai-conversation.mjs +274 -0
  33. package/dist/clerk/clerk-organization-client.js +2 -2
  34. package/dist/clerk/clerk-organization-client.mjs +2 -2
  35. package/dist/clerk/clerk-page-generator.d.ts +1 -1
  36. package/dist/clerk/clerk-user-client.js +2 -2
  37. package/dist/clerk/clerk-user-client.mjs +2 -2
  38. package/dist/clerk/fingerprint/fingerprint-provider.js +9 -9
  39. package/dist/clerk/fingerprint/fingerprint-provider.mjs +9 -9
  40. package/dist/fuma/base/custom-header.js +4 -4
  41. package/dist/fuma/base/custom-header.mjs +4 -4
  42. package/dist/fuma/mdx/banner.js +3 -3
  43. package/dist/fuma/mdx/banner.mjs +3 -3
  44. package/dist/fuma/mdx/fuma-github-info.js +3 -3
  45. package/dist/fuma/mdx/fuma-github-info.mjs +3 -3
  46. package/dist/fuma/mdx/gradient-button.js +3 -3
  47. package/dist/fuma/mdx/gradient-button.mjs +3 -3
  48. package/dist/fuma/mdx/index.d.ts +1 -0
  49. package/dist/fuma/mdx/index.js +2 -0
  50. package/dist/fuma/mdx/index.mjs +1 -0
  51. package/dist/fuma/mdx/markdown-component-map.d.ts +3 -0
  52. package/dist/fuma/mdx/markdown-component-map.js +73 -0
  53. package/dist/fuma/mdx/markdown-component-map.mjs +71 -0
  54. package/dist/fuma/mdx/mermaid.d.ts +2 -1
  55. package/dist/fuma/mdx/mermaid.js +130 -6
  56. package/dist/fuma/mdx/mermaid.mjs +130 -6
  57. package/dist/fuma/mdx/toc-base.js +4 -4
  58. package/dist/fuma/mdx/toc-base.mjs +4 -4
  59. package/dist/fuma/mdx/trophy-card.js +2 -2
  60. package/dist/fuma/mdx/trophy-card.mjs +2 -2
  61. package/dist/fuma/mdx/zia-card.js +3 -3
  62. package/dist/fuma/mdx/zia-card.mjs +3 -3
  63. package/dist/fuma/mdx/zia-file.js +3 -3
  64. package/dist/fuma/mdx/zia-file.mjs +3 -3
  65. package/dist/main/ads-alert-dialog.js +2 -2
  66. package/dist/main/ads-alert-dialog.mjs +2 -2
  67. package/dist/main/credit/credit-nav-button.js +2 -2
  68. package/dist/main/credit/credit-nav-button.mjs +2 -2
  69. package/dist/main/credit/credit-overview-client.js +4 -4
  70. package/dist/main/credit/credit-overview-client.mjs +4 -4
  71. package/dist/main/footer.js +2 -2
  72. package/dist/main/footer.mjs +2 -2
  73. package/dist/main/go-to-top.js +2 -2
  74. package/dist/main/go-to-top.mjs +2 -2
  75. package/dist/main/index.d.ts +1 -0
  76. package/dist/main/index.js +2 -0
  77. package/dist/main/index.mjs +1 -0
  78. package/dist/main/info-tooltip.d.ts +8 -0
  79. package/dist/main/info-tooltip.js +48 -0
  80. package/dist/main/info-tooltip.mjs +46 -0
  81. package/dist/main/pill-select/x-pill-select.js +2 -2
  82. package/dist/main/pill-select/x-pill-select.mjs +2 -2
  83. package/dist/main/pill-select/x-token-input.js +2 -2
  84. package/dist/main/pill-select/x-token-input.mjs +2 -2
  85. package/dist/main/x-button.js +3 -3
  86. package/dist/main/x-button.mjs +3 -3
  87. package/package.json +16 -3
  88. package/src/ai/ai-chat-composer.tsx +187 -0
  89. package/src/ai/ai-markdown.tsx +45 -0
  90. package/src/ai/ai-message-actions.tsx +16 -0
  91. package/src/ai/ai-message-bubble.tsx +138 -0
  92. package/src/ai/ai-message-content.tsx +149 -0
  93. package/src/ai/ai-message-list.tsx +59 -0
  94. package/src/ai/ai-message-meta.tsx +56 -0
  95. package/src/ai/ai-status-indicator.tsx +61 -0
  96. package/src/ai/index.ts +13 -0
  97. package/src/ai/types.ts +131 -0
  98. package/src/ai/use-ai-conversation.ts +422 -0
  99. package/src/clerk/clerk-organization-client.tsx +5 -5
  100. package/src/clerk/clerk-page-generator.tsx +1 -1
  101. package/src/clerk/clerk-user-client.tsx +4 -4
  102. package/src/clerk/fingerprint/fingerprint-provider.tsx +34 -22
  103. package/src/fuma/base/custom-header.tsx +5 -5
  104. package/src/fuma/mdx/banner.tsx +3 -3
  105. package/src/fuma/mdx/fuma-github-info.tsx +4 -4
  106. package/src/fuma/mdx/gradient-button.tsx +3 -3
  107. package/src/fuma/mdx/index.ts +2 -1
  108. package/src/fuma/mdx/markdown-component-map.tsx +174 -0
  109. package/src/fuma/mdx/mermaid.tsx +145 -10
  110. package/src/fuma/mdx/toc-base.tsx +5 -5
  111. package/src/fuma/mdx/trophy-card.tsx +2 -2
  112. package/src/fuma/mdx/zia-card.tsx +3 -3
  113. package/src/fuma/mdx/zia-file.tsx +3 -3
  114. package/src/main/ads-alert-dialog.tsx +5 -5
  115. package/src/main/credit/credit-nav-button.tsx +3 -3
  116. package/src/main/credit/credit-overview-client.tsx +15 -7
  117. package/src/main/features.tsx +5 -3
  118. package/src/main/footer.tsx +4 -5
  119. package/src/main/go-to-top.tsx +2 -2
  120. package/src/main/index.ts +2 -0
  121. package/src/main/info-tooltip.tsx +99 -0
  122. package/src/main/language-detector.tsx +4 -4
  123. package/src/main/pill-select/x-pill-select.tsx +2 -2
  124. package/src/main/pill-select/x-token-input.tsx +2 -2
  125. package/src/main/x-button.tsx +4 -4
@@ -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
+ }
@@ -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';
@@ -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
+ };