@windrun-huaiin/third-ui 15.1.1 → 16.0.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 (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,51 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var utils = require('@windrun-huaiin/lib/utils');
6
+
7
+ function getLabel(status) {
8
+ switch (status) {
9
+ case 'streaming':
10
+ return 'Streaming';
11
+ case 'completed':
12
+ return 'Completed';
13
+ case 'stopped':
14
+ return 'Stopped';
15
+ case 'timeout':
16
+ return 'Timeout';
17
+ case 'request_aborted':
18
+ return 'Aborted';
19
+ case 'failed':
20
+ return 'Failed';
21
+ default:
22
+ return null;
23
+ }
24
+ }
25
+ function getStatusClassName(status) {
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
+ function AIStatusIndicator({ message, className }) {
44
+ const label = getLabel(message.status);
45
+ if (!label) {
46
+ return null;
47
+ }
48
+ return (jsxRuntime.jsx("span", { className: utils.cn('inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] uppercase tracking-[0.12em]', getStatusClassName(message.status), className), children: label }));
49
+ }
50
+
51
+ exports.AIStatusIndicator = AIStatusIndicator;
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { cn } from '@windrun-huaiin/lib/utils';
4
+
5
+ function getLabel(status) {
6
+ switch (status) {
7
+ case 'streaming':
8
+ return 'Streaming';
9
+ case 'completed':
10
+ return 'Completed';
11
+ case 'stopped':
12
+ return 'Stopped';
13
+ case 'timeout':
14
+ return 'Timeout';
15
+ case 'request_aborted':
16
+ return 'Aborted';
17
+ case 'failed':
18
+ return 'Failed';
19
+ default:
20
+ return null;
21
+ }
22
+ }
23
+ function getStatusClassName(status) {
24
+ switch (status) {
25
+ case 'completed':
26
+ return 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300';
27
+ case 'streaming':
28
+ return 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/60 dark:bg-sky-950/40 dark:text-sky-300';
29
+ case 'stopped':
30
+ return 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300';
31
+ case 'timeout':
32
+ return 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-300';
33
+ case 'request_aborted':
34
+ return 'border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900/60 dark:bg-orange-950/40 dark:text-orange-300';
35
+ case 'failed':
36
+ return 'border-red-200 bg-red-50 text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300';
37
+ default:
38
+ return 'border-border bg-background text-muted-foreground';
39
+ }
40
+ }
41
+ function AIStatusIndicator({ message, className }) {
42
+ const label = getLabel(message.status);
43
+ if (!label) {
44
+ return null;
45
+ }
46
+ return (jsx("span", { className: cn('inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] uppercase tracking-[0.12em]', getStatusClassName(message.status), className), children: label }));
47
+ }
48
+
49
+ export { AIStatusIndicator };
@@ -0,0 +1,11 @@
1
+ export { getMessageText, getTextParts, type ConversationMessage, type MessagePart } from '@windrun-huaiin/contracts/ai';
2
+ export * from './types';
3
+ export * from './use-ai-conversation';
4
+ export * from './ai-chat-composer';
5
+ export * from './ai-message-list';
6
+ export * from './ai-message-bubble';
7
+ export * from './ai-markdown';
8
+ export * from './ai-message-content';
9
+ export * from './ai-message-meta';
10
+ export * from './ai-message-actions';
11
+ export * from './ai-status-indicator';
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var ai = require('@windrun-huaiin/contracts/ai');
5
+ var useAiConversation = require('./use-ai-conversation.js');
6
+ var aiChatComposer = require('./ai-chat-composer.js');
7
+ var aiMessageList = require('./ai-message-list.js');
8
+ var aiMessageBubble = require('./ai-message-bubble.js');
9
+ var aiMarkdown = require('./ai-markdown.js');
10
+ var aiMessageContent = require('./ai-message-content.js');
11
+ var aiMessageMeta = require('./ai-message-meta.js');
12
+ var aiMessageActions = require('./ai-message-actions.js');
13
+ var aiStatusIndicator = require('./ai-status-indicator.js');
14
+
15
+
16
+
17
+ Object.defineProperty(exports, "getMessageText", {
18
+ enumerable: true,
19
+ get: function () { return ai.getMessageText; }
20
+ });
21
+ Object.defineProperty(exports, "getTextParts", {
22
+ enumerable: true,
23
+ get: function () { return ai.getTextParts; }
24
+ });
25
+ exports.useAIConversation = useAiConversation.useAIConversation;
26
+ exports.AIChatComposer = aiChatComposer.AIChatComposer;
27
+ exports.AIMessageList = aiMessageList.AIMessageList;
28
+ exports.AIMessageBubble = aiMessageBubble.AIMessageBubble;
29
+ exports.AIMarkdown = aiMarkdown.AIMarkdown;
30
+ exports.AIMessageContent = aiMessageContent.AIMessageContent;
31
+ exports.AIMessageMeta = aiMessageMeta.AIMessageMeta;
32
+ exports.AIMessageActions = aiMessageActions.AIMessageActions;
33
+ exports.AIStatusIndicator = aiStatusIndicator.AIStatusIndicator;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ export { getMessageText, getTextParts } from '@windrun-huaiin/contracts/ai';
3
+ export { useAIConversation } from './use-ai-conversation.mjs';
4
+ export { AIChatComposer } from './ai-chat-composer.mjs';
5
+ export { AIMessageList } from './ai-message-list.mjs';
6
+ export { AIMessageBubble } from './ai-message-bubble.mjs';
7
+ export { AIMarkdown } from './ai-markdown.mjs';
8
+ export { AIMessageContent } from './ai-message-content.mjs';
9
+ export { AIMessageMeta } from './ai-message-meta.mjs';
10
+ export { AIMessageActions } from './ai-message-actions.mjs';
11
+ export { AIStatusIndicator } from './ai-status-indicator.mjs';
@@ -0,0 +1,110 @@
1
+ import type { AIRuntimeRequest, AIStreamEvent, ConversationMessage, MessagePart } from '@windrun-huaiin/contracts/ai';
2
+ import type { ComponentType, ReactNode, RefObject } from 'react';
3
+ export type AIConversationTransport = (input: AIRuntimeRequest, signal: AbortSignal) => Promise<Response>;
4
+ export type AIConversationState = {
5
+ sessionId?: string;
6
+ messages: ConversationMessage[];
7
+ isStreaming: boolean;
8
+ error?: string;
9
+ };
10
+ export type AIConversationOptions = {
11
+ endpoint: string;
12
+ initialSessionId?: string;
13
+ initialMessages?: ConversationMessage[];
14
+ modelName?: string;
15
+ metadata?: Record<string, unknown>;
16
+ transport?: AIConversationTransport;
17
+ onEvent?: (event: AIStreamEvent) => void;
18
+ onError?: (error: Error) => void;
19
+ };
20
+ export type SendMessageInput = {
21
+ text: string;
22
+ metadata?: Record<string, unknown>;
23
+ };
24
+ export type AIChatComposerProps = {
25
+ value: string;
26
+ onChange: (value: string) => void;
27
+ onSubmit: () => void;
28
+ onStop?: () => void;
29
+ disabled?: boolean;
30
+ isStreaming?: boolean;
31
+ placeholder?: string;
32
+ className?: string;
33
+ leftSlot?: ReactNode;
34
+ attachments?: ReactNode;
35
+ helper?: ReactNode;
36
+ submitLabel?: string;
37
+ stopLabel?: string;
38
+ minHeight?: number;
39
+ maxHeight?: number;
40
+ submitOnEnter?: boolean;
41
+ shellClassName?: string;
42
+ textareaClassName?: string;
43
+ submitControl?: ReactNode;
44
+ stopControl?: ReactNode;
45
+ textareaRef?: RefObject<HTMLTextAreaElement | null>;
46
+ secondaryActions?: ReactNode;
47
+ actionLayout?: 'inline' | 'stacked';
48
+ };
49
+ export type AIMessageBubbleProps = {
50
+ message: ConversationMessage;
51
+ className?: string;
52
+ cardClassName?: string;
53
+ contentClassName?: string;
54
+ footerClassName?: string;
55
+ maxWidthClassName?: string;
56
+ showRoleLabel?: boolean;
57
+ markdownComponents?: AIMarkdownComponentMap;
58
+ showFooter?: boolean;
59
+ renderContent?: (message: ConversationMessage) => ReactNode;
60
+ renderMeta?: (message: ConversationMessage) => ReactNode;
61
+ renderActions?: (message: ConversationMessage) => ReactNode;
62
+ };
63
+ export type AIMessageListProps = {
64
+ messages: ConversationMessage[];
65
+ className?: string;
66
+ contentClassName?: string;
67
+ emptyText?: string;
68
+ emptyState?: ReactNode;
69
+ autoScroll?: boolean;
70
+ scrollBehavior?: ScrollBehavior;
71
+ renderMessage?: (message: ConversationMessage) => ReactNode;
72
+ };
73
+ export type AIStatusIndicatorProps = {
74
+ message: ConversationMessage;
75
+ className?: string;
76
+ };
77
+ export type AIMessageContentProps = {
78
+ message: ConversationMessage;
79
+ className?: string;
80
+ markdownComponents?: AIMarkdownComponentMap;
81
+ };
82
+ export type AIMessageMetaProps = {
83
+ message: ConversationMessage;
84
+ className?: string;
85
+ showTime?: boolean;
86
+ showStatus?: boolean;
87
+ showRuntime?: boolean;
88
+ showFailureReason?: boolean;
89
+ };
90
+ export type AIMessageActionsProps = {
91
+ className?: string;
92
+ children?: ReactNode;
93
+ };
94
+ export type AIMarkdownComponentMap = Record<string, ComponentType<any>>;
95
+ export type AIMarkdownProps = {
96
+ content: string;
97
+ className?: string;
98
+ components?: AIMarkdownComponentMap;
99
+ };
100
+ export type TextMessagePart = Extract<MessagePart, {
101
+ type: 'text';
102
+ }>;
103
+ export type AIMessageRuntimeMetadata = {
104
+ requestStartedAt?: number;
105
+ streamStartedAt?: number;
106
+ firstTokenAt?: number;
107
+ firstTokenMs?: number;
108
+ completedAt?: number;
109
+ totalMs?: number;
110
+ };
@@ -0,0 +1,13 @@
1
+ import { type ConversationMessage } from '@windrun-huaiin/contracts/ai';
2
+ import type { AIConversationOptions, SendMessageInput } from './types';
3
+ export declare function useAIConversation(options: AIConversationOptions): {
4
+ sendMessage: (input: SendMessageInput) => Promise<void>;
5
+ stopGeneration: () => void;
6
+ resetConversation: () => void;
7
+ removeMessage: (messageId: string) => void;
8
+ loadConversation: (messages: ConversationMessage[], sessionId?: string) => void;
9
+ sessionId?: string;
10
+ messages: ConversationMessage[];
11
+ isStreaming: boolean;
12
+ error?: string;
13
+ };
@@ -0,0 +1,276 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var tslib = require('tslib');
5
+ var React = require('react');
6
+
7
+ function createId(prefix) {
8
+ try {
9
+ return `${prefix}-${crypto.randomUUID()}`;
10
+ }
11
+ catch (_a) {
12
+ return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
13
+ }
14
+ }
15
+ function getAssistantMessageText(message) {
16
+ return message.parts
17
+ .flatMap((part) => (part.type === 'text' ? [part.text] : []))
18
+ .join('');
19
+ }
20
+ function createUserMessage(input) {
21
+ return {
22
+ id: createId('user'),
23
+ role: 'user',
24
+ parts: [{ type: 'text', text: input.text }],
25
+ createdAt: Date.now(),
26
+ metadata: input.metadata,
27
+ };
28
+ }
29
+ function getRuntimeMetadata(message) {
30
+ var _a;
31
+ const metadata = (_a = message.metadata) === null || _a === void 0 ? void 0 : _a.aiRuntime;
32
+ if (!metadata || typeof metadata !== 'object') {
33
+ return {};
34
+ }
35
+ return metadata;
36
+ }
37
+ function withRuntimeMetadata(message, nextMetadata) {
38
+ var _a;
39
+ return Object.assign(Object.assign({}, message), { metadata: Object.assign(Object.assign({}, ((_a = message.metadata) !== null && _a !== void 0 ? _a : {})), { aiRuntime: Object.assign(Object.assign({}, getRuntimeMetadata(message)), nextMetadata) }) });
40
+ }
41
+ function createAssistantPlaceholder(requestStartedAt) {
42
+ return {
43
+ id: createId('assistant'),
44
+ role: 'assistant',
45
+ parts: [{ type: 'text', text: '' }],
46
+ status: 'streaming',
47
+ createdAt: Date.now(),
48
+ metadata: {
49
+ aiRuntime: {
50
+ requestStartedAt,
51
+ },
52
+ },
53
+ };
54
+ }
55
+ function defaultTransport(endpoint) {
56
+ return (input, signal) => tslib.__awaiter(this, void 0, void 0, function* () {
57
+ return fetch(endpoint, {
58
+ method: 'POST',
59
+ signal,
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ body: JSON.stringify(input),
64
+ });
65
+ });
66
+ }
67
+ function updateAssistantText(messages, messageId, textDelta) {
68
+ return messages.map((message) => {
69
+ if (message.id !== messageId) {
70
+ return message;
71
+ }
72
+ const currentText = getAssistantMessageText(message);
73
+ const runtimeMetadata = getRuntimeMetadata(message);
74
+ const now = Date.now();
75
+ return withRuntimeMetadata(Object.assign(Object.assign({}, message), { parts: [{ type: 'text', text: currentText + textDelta }], status: 'streaming' }), runtimeMetadata.firstTokenAt
76
+ ? {}
77
+ : {
78
+ firstTokenAt: now,
79
+ firstTokenMs: runtimeMetadata.requestStartedAt
80
+ ? now - runtimeMetadata.requestStartedAt
81
+ : undefined,
82
+ });
83
+ });
84
+ }
85
+ function updateMessageStarted(messages, placeholderId, messageId) {
86
+ return messages.map((message) => {
87
+ if (message.id !== placeholderId) {
88
+ return message;
89
+ }
90
+ return withRuntimeMetadata(Object.assign(Object.assign({}, message), { id: messageId }), {
91
+ streamStartedAt: Date.now(),
92
+ });
93
+ });
94
+ }
95
+ function updateMessageStatus(messages, messageId, status) {
96
+ return messages.map((message) => {
97
+ if (message.id !== messageId) {
98
+ return message;
99
+ }
100
+ const now = Date.now();
101
+ const runtimeMetadata = getRuntimeMetadata(message);
102
+ return withRuntimeMetadata(Object.assign(Object.assign({}, message), { status }), {
103
+ completedAt: now,
104
+ totalMs: runtimeMetadata.requestStartedAt
105
+ ? now - runtimeMetadata.requestStartedAt
106
+ : undefined,
107
+ });
108
+ });
109
+ }
110
+ function applyErrorToLatestAssistant(messages, error) {
111
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
112
+ const message = messages[index];
113
+ if (message.role !== 'assistant') {
114
+ continue;
115
+ }
116
+ const nextMessages = [...messages];
117
+ const now = Date.now();
118
+ const runtimeMetadata = getRuntimeMetadata(message);
119
+ nextMessages[index] = withRuntimeMetadata(Object.assign(Object.assign({}, message), { status: error.status, failureReason: error.failureReason, errorMessage: error.error, upstreamStatusCode: error.upstreamStatusCode }), {
120
+ completedAt: now,
121
+ totalMs: runtimeMetadata.requestStartedAt
122
+ ? now - runtimeMetadata.requestStartedAt
123
+ : undefined,
124
+ });
125
+ return nextMessages;
126
+ }
127
+ return messages;
128
+ }
129
+ function consumeEventStream(response, onEvent) {
130
+ return tslib.__awaiter(this, void 0, void 0, function* () {
131
+ var _a;
132
+ if (!response.body) {
133
+ throw new Error('Missing response body');
134
+ }
135
+ const reader = response.body.getReader();
136
+ const decoder = new TextDecoder();
137
+ let buffer = '';
138
+ for (;;) {
139
+ const chunk = yield reader.read();
140
+ if (chunk.done) {
141
+ break;
142
+ }
143
+ buffer += decoder.decode(chunk.value, { stream: true });
144
+ const frames = buffer.split('\n\n');
145
+ buffer = (_a = frames.pop()) !== null && _a !== void 0 ? _a : '';
146
+ for (const frame of frames) {
147
+ const line = frame
148
+ .split('\n')
149
+ .find((item) => item.startsWith('data: '));
150
+ if (!line) {
151
+ continue;
152
+ }
153
+ onEvent(JSON.parse(line.slice(6)));
154
+ }
155
+ }
156
+ });
157
+ }
158
+ function useAIConversation(options) {
159
+ var _a, _b;
160
+ const transport = (_a = options.transport) !== null && _a !== void 0 ? _a : defaultTransport(options.endpoint);
161
+ const [state, setState] = React.useState({
162
+ sessionId: options.initialSessionId,
163
+ messages: (_b = options.initialMessages) !== null && _b !== void 0 ? _b : [],
164
+ isStreaming: false,
165
+ });
166
+ const abortRef = React.useRef(null);
167
+ const sendMessage = (input) => tslib.__awaiter(this, void 0, void 0, function* () {
168
+ var _a, _b, _c;
169
+ const text = input.text.trim();
170
+ if (!text || state.isStreaming) {
171
+ return;
172
+ }
173
+ const userMessage = createUserMessage(input);
174
+ const requestStartedAt = Date.now();
175
+ const assistantMessage = createAssistantPlaceholder(requestStartedAt);
176
+ React.startTransition(() => {
177
+ setState((current) => (Object.assign(Object.assign({}, current), { messages: [...current.messages, userMessage, assistantMessage], isStreaming: true, error: undefined })));
178
+ });
179
+ const controller = new AbortController();
180
+ abortRef.current = controller;
181
+ try {
182
+ const response = yield transport({
183
+ sessionId: state.sessionId,
184
+ messages: [...state.messages, userMessage],
185
+ modelName: options.modelName,
186
+ metadata: Object.assign(Object.assign({}, ((_a = options.metadata) !== null && _a !== void 0 ? _a : {})), ((_b = input.metadata) !== null && _b !== void 0 ? _b : {})),
187
+ }, controller.signal);
188
+ if (!response.ok) {
189
+ const payload = (yield response.json());
190
+ throw new Error(payload.error || 'AI request failed');
191
+ }
192
+ yield consumeEventStream(response, (event) => {
193
+ var _a;
194
+ (_a = options.onEvent) === null || _a === void 0 ? void 0 : _a.call(options, event);
195
+ React.startTransition(() => {
196
+ setState((current) => {
197
+ if (event.type === 'message_started') {
198
+ return Object.assign(Object.assign({}, current), { messages: updateMessageStarted(current.messages, assistantMessage.id, event.messageId) });
199
+ }
200
+ if (event.type === 'text_delta') {
201
+ return Object.assign(Object.assign({}, current), { messages: updateAssistantText(current.messages, event.messageId, event.text) });
202
+ }
203
+ if (event.type === 'part') {
204
+ return Object.assign(Object.assign({}, current), { messages: current.messages.map((message) => {
205
+ if (message.id !== event.messageId) {
206
+ return message;
207
+ }
208
+ return Object.assign(Object.assign({}, message), { parts: [...message.parts, event.part] });
209
+ }) });
210
+ }
211
+ if (event.type === 'message_completed') {
212
+ return Object.assign(Object.assign({}, current), { isStreaming: false, messages: updateMessageStatus(current.messages, event.messageId, 'completed') });
213
+ }
214
+ if (event.type === 'error') {
215
+ return Object.assign(Object.assign({}, current), { isStreaming: false, error: event.error.error, messages: applyErrorToLatestAssistant(current.messages, event.error) });
216
+ }
217
+ return current;
218
+ });
219
+ });
220
+ });
221
+ React.startTransition(() => {
222
+ setState((current) => (Object.assign(Object.assign({}, current), { isStreaming: false })));
223
+ });
224
+ }
225
+ catch (error) {
226
+ const nextError = error instanceof Error ? error : new Error('AI request failed');
227
+ (_c = options.onError) === null || _c === void 0 ? void 0 : _c.call(options, nextError);
228
+ React.startTransition(() => {
229
+ setState((current) => (Object.assign(Object.assign({}, current), { isStreaming: false, error: nextError.message, messages: applyErrorToLatestAssistant(current.messages, {
230
+ error: nextError.message,
231
+ status: controller.signal.aborted ? 'request_aborted' : 'failed',
232
+ failureReason: 'unknown',
233
+ upstreamStatusCode: controller.signal.aborted ? 499 : 500,
234
+ }) })));
235
+ });
236
+ }
237
+ finally {
238
+ abortRef.current = null;
239
+ }
240
+ });
241
+ const stopGeneration = () => {
242
+ var _a;
243
+ (_a = abortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
244
+ };
245
+ const resetConversation = () => {
246
+ React.startTransition(() => {
247
+ var _a;
248
+ setState({
249
+ sessionId: options.initialSessionId,
250
+ messages: (_a = options.initialMessages) !== null && _a !== void 0 ? _a : [],
251
+ isStreaming: false,
252
+ });
253
+ });
254
+ };
255
+ const removeMessage = (messageId) => {
256
+ React.startTransition(() => {
257
+ setState((current) => (Object.assign(Object.assign({}, current), { messages: current.messages.filter((message) => message.id !== messageId) })));
258
+ });
259
+ };
260
+ const loadConversation = (messages, sessionId) => {
261
+ React.startTransition(() => {
262
+ setState({
263
+ sessionId,
264
+ messages,
265
+ isStreaming: false,
266
+ });
267
+ });
268
+ };
269
+ return Object.assign(Object.assign({}, state), { sendMessage,
270
+ stopGeneration,
271
+ resetConversation,
272
+ removeMessage,
273
+ loadConversation });
274
+ }
275
+
276
+ exports.useAIConversation = useAIConversation;