ikoncomponents 1.6.2 → 1.6.4
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/README.md +36 -36
- package/dist/ikoncomponents/assistant-ui/Assistant.d.ts +14 -0
- package/dist/ikoncomponents/assistant-ui/Assistant.js +20 -0
- package/dist/ikoncomponents/assistant-ui/agentTextChatTransport.d.ts +28 -0
- package/dist/ikoncomponents/assistant-ui/agentTextChatTransport.js +198 -0
- package/dist/ikoncomponents/assistant-ui/attachment.d.ts +4 -0
- package/dist/ikoncomponents/assistant-ui/attachment.js +93 -0
- package/dist/ikoncomponents/assistant-ui/markdown-text.d.ts +2 -0
- package/dist/ikoncomponents/assistant-ui/markdown-text.js +126 -0
- package/dist/ikoncomponents/assistant-ui/thread.d.ts +5 -0
- package/dist/ikoncomponents/assistant-ui/thread.js +109 -0
- package/dist/ikoncomponents/assistant-ui/tool-fallback.d.ts +2 -0
- package/dist/ikoncomponents/assistant-ui/tool-fallback.js +18 -0
- package/dist/ikoncomponents/assistant-ui/tooltip-icon-button.d.ts +7 -0
- package/dist/ikoncomponents/assistant-ui/tooltip-icon-button.js +23 -0
- package/dist/ikoncomponents/main-layout/RefreshContext.js +0 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/styles.css +6866 -2
- package/dist/utils/userType.d.ts +13 -0
- package/dist/utils/userType.js +1 -0
- package/package.json +12 -15
package/README.md
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
-
|
|
3
|
-
## Getting Started
|
|
4
|
-
|
|
5
|
-
First, run the development server:
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm run dev
|
|
9
|
-
# or
|
|
10
|
-
yarn dev
|
|
11
|
-
# or
|
|
12
|
-
pnpm dev
|
|
13
|
-
# or
|
|
14
|
-
bun dev
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
-
|
|
19
|
-
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
-
|
|
21
|
-
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
-
|
|
23
|
-
## Learn More
|
|
24
|
-
|
|
25
|
-
To learn more about Next.js, take a look at the following resources:
|
|
26
|
-
|
|
27
|
-
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
-
|
|
30
|
-
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
-
|
|
32
|
-
## Deploy on Vercel
|
|
33
|
-
|
|
34
|
-
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
-
|
|
36
|
-
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { UserData } from "../../utils/userType";
|
|
2
|
+
interface AssistantComponentProps {
|
|
3
|
+
provider?: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
agentId?: string;
|
|
6
|
+
agentName?: string;
|
|
7
|
+
temperature?: number;
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
currentUserDetails: UserData;
|
|
12
|
+
}
|
|
13
|
+
export declare const AssistantComponent: ({ provider, model, agentId, agentName, temperature, maxTokens, className, baseUrl, currentUserDetails }: AssistantComponentProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Thread } from "./thread";
|
|
4
|
+
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
5
|
+
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
6
|
+
import { AgentTextChatTransport } from "./agentTextChatTransport";
|
|
7
|
+
export const AssistantComponent = ({ provider = "openai", model = "gpt-4o-mini", agentId = "default-agent", agentName = "Default Agent", temperature = 0.7, maxTokens = 2048, className, baseUrl = "http://localhost:3000", currentUserDetails }) => {
|
|
8
|
+
const runtime = useChatRuntime({
|
|
9
|
+
transport: new AgentTextChatTransport({
|
|
10
|
+
provider,
|
|
11
|
+
model,
|
|
12
|
+
agentId,
|
|
13
|
+
agentName,
|
|
14
|
+
temperature,
|
|
15
|
+
maxTokens,
|
|
16
|
+
baseUrl
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
return _jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx("div", { className: className, children: _jsx("div", { className: `flex flex-col h-full border rounded-lg overflow-hidden ${className}`, children: _jsx(Thread, { currentUserDetails: currentUserDetails }) }) }) });
|
|
20
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ChatTransport, UIMessage, UIMessageChunk, ChatRequestOptions } from "ai";
|
|
2
|
+
export interface AgentTextChatTransportOptions {
|
|
3
|
+
provider: string;
|
|
4
|
+
model: string;
|
|
5
|
+
agentId: string;
|
|
6
|
+
agentName?: string;
|
|
7
|
+
temperature?: number;
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class AgentTextChatTransport<UI_MESSAGE extends UIMessage = UIMessage> implements ChatTransport<UI_MESSAGE> {
|
|
12
|
+
private agentConfig;
|
|
13
|
+
private baseUrl;
|
|
14
|
+
private chatIdMap;
|
|
15
|
+
constructor(options: AgentTextChatTransportOptions);
|
|
16
|
+
private getChatId;
|
|
17
|
+
sendMessages(options: {
|
|
18
|
+
trigger: "submit-message" | "regenerate-message";
|
|
19
|
+
chatId: string;
|
|
20
|
+
messageId: string | undefined;
|
|
21
|
+
messages: UI_MESSAGE[];
|
|
22
|
+
abortSignal: AbortSignal | undefined;
|
|
23
|
+
} & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk>>;
|
|
24
|
+
reconnectToStream(options: {
|
|
25
|
+
chatId: string;
|
|
26
|
+
} & ChatRequestOptions): Promise<ReadableStream<UIMessageChunk> | null>;
|
|
27
|
+
private processResponseStream;
|
|
28
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
export class AgentTextChatTransport {
|
|
3
|
+
constructor(options) {
|
|
4
|
+
this.chatIdMap = new Map();
|
|
5
|
+
this.agentConfig = options;
|
|
6
|
+
this.baseUrl = options.baseUrl || "";
|
|
7
|
+
}
|
|
8
|
+
getChatId(chatId) {
|
|
9
|
+
if (this.chatIdMap.has(chatId)) {
|
|
10
|
+
return this.chatIdMap.get(chatId);
|
|
11
|
+
}
|
|
12
|
+
return uuidv4();
|
|
13
|
+
}
|
|
14
|
+
async sendMessages(options) {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
const { messages, chatId, abortSignal, headers, body } = options;
|
|
17
|
+
console.log("sendMessages called with chatId:", chatId, "trigger:", options.trigger);
|
|
18
|
+
const serverChatId = this.getChatId(chatId);
|
|
19
|
+
console.log("Using server chatId:", serverChatId);
|
|
20
|
+
// Convert UIMessage[] to LangChain AIMessage format
|
|
21
|
+
const convertedMessages = messages.map((msg) => {
|
|
22
|
+
const content = msg.parts
|
|
23
|
+
.filter((part) => part.type === "text")
|
|
24
|
+
.map((part) => part.text)
|
|
25
|
+
.join("");
|
|
26
|
+
return {
|
|
27
|
+
role: msg.role,
|
|
28
|
+
content: content,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
const requestBody = Object.assign({ messages: convertedMessages, provider: this.agentConfig.provider, model: this.agentConfig.model, agentId: this.agentConfig.agentId, agentName: this.agentConfig.agentName, temperature: (_a = this.agentConfig.temperature) !== null && _a !== void 0 ? _a : 0.7, maxTokens: (_b = this.agentConfig.maxTokens) !== null && _b !== void 0 ? _b : 2048, stream: true, useVoice: false }, body);
|
|
32
|
+
const mergedHeaders = Object.assign({ "Content-Type": "application/json" }, (headers instanceof Headers
|
|
33
|
+
? Object.fromEntries(headers.entries())
|
|
34
|
+
: headers || {}));
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`${this.baseUrl}/api/chat/completions/${serverChatId}`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: mergedHeaders,
|
|
39
|
+
body: JSON.stringify(requestBody),
|
|
40
|
+
signal: abortSignal,
|
|
41
|
+
cache: "no-store",
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
45
|
+
}
|
|
46
|
+
if (!response.body) {
|
|
47
|
+
throw new Error("Response body is null");
|
|
48
|
+
}
|
|
49
|
+
return this.processResponseStream(response.body);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error("Error in sendMessages:", error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async reconnectToStream(options) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
processResponseStream(stream) {
|
|
60
|
+
const decoder = new TextDecoder();
|
|
61
|
+
let buffer = "";
|
|
62
|
+
let reader = null;
|
|
63
|
+
let isClosed = false;
|
|
64
|
+
let pendingChunks = [];
|
|
65
|
+
let hasStarted = false;
|
|
66
|
+
let messageId = "";
|
|
67
|
+
let textStarted = false;
|
|
68
|
+
let textPartId = "";
|
|
69
|
+
return new ReadableStream({
|
|
70
|
+
async start(controller) {
|
|
71
|
+
reader = stream.getReader();
|
|
72
|
+
},
|
|
73
|
+
async pull(controller) {
|
|
74
|
+
var _a;
|
|
75
|
+
if (!reader || isClosed) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// First, deliver any pending chunks one at a time
|
|
79
|
+
if (pendingChunks.length > 0) {
|
|
80
|
+
controller.enqueue(pendingChunks.shift());
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const { done, value } = await reader.read();
|
|
85
|
+
if (done) {
|
|
86
|
+
// Send text-end and finish chunks before closing
|
|
87
|
+
if (hasStarted && messageId) {
|
|
88
|
+
if (textStarted && textPartId) {
|
|
89
|
+
pendingChunks.push({
|
|
90
|
+
type: "text-end",
|
|
91
|
+
id: textPartId,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
pendingChunks.push({
|
|
95
|
+
type: "finish",
|
|
96
|
+
finishReason: "stop",
|
|
97
|
+
});
|
|
98
|
+
// Enqueue pending chunks before closing
|
|
99
|
+
while (pendingChunks.length > 0) {
|
|
100
|
+
controller.enqueue(pendingChunks.shift());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!isClosed) {
|
|
104
|
+
isClosed = true;
|
|
105
|
+
controller.close();
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
buffer += decoder.decode(value, { stream: true });
|
|
110
|
+
const lines = buffer.split("\n");
|
|
111
|
+
buffer = lines.pop() || "";
|
|
112
|
+
for (const line of lines) {
|
|
113
|
+
const trimmedLine = line.trim();
|
|
114
|
+
if (!trimmedLine)
|
|
115
|
+
continue;
|
|
116
|
+
if (trimmedLine.startsWith("data:")) {
|
|
117
|
+
const data = trimmedLine.slice(5).trim();
|
|
118
|
+
if (!data)
|
|
119
|
+
continue;
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(data);
|
|
122
|
+
if (isClosed) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Track message ID and send start chunk once
|
|
126
|
+
if (parsed.id && !messageId) {
|
|
127
|
+
messageId = parsed.id;
|
|
128
|
+
hasStarted = true;
|
|
129
|
+
pendingChunks.push({
|
|
130
|
+
type: "start",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
let chunk = null;
|
|
134
|
+
if (parsed.type === "ai") {
|
|
135
|
+
const message = (_a = parsed.messages) === null || _a === void 0 ? void 0 : _a[0];
|
|
136
|
+
if (message === null || message === void 0 ? void 0 : message.content) {
|
|
137
|
+
// Generate text part ID once
|
|
138
|
+
if (!textPartId) {
|
|
139
|
+
textPartId = uuidv4();
|
|
140
|
+
}
|
|
141
|
+
// Send text-start before first delta
|
|
142
|
+
if (!textStarted) {
|
|
143
|
+
textStarted = true;
|
|
144
|
+
pendingChunks.push({
|
|
145
|
+
type: "text-start",
|
|
146
|
+
id: textPartId,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
chunk = {
|
|
150
|
+
type: "text-delta",
|
|
151
|
+
delta: message.content,
|
|
152
|
+
id: textPartId,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (parsed.type === "tool") {
|
|
157
|
+
const toolCall = parsed.toolCall;
|
|
158
|
+
if (toolCall) {
|
|
159
|
+
chunk = {
|
|
160
|
+
type: "tool-input-available",
|
|
161
|
+
toolCallId: toolCall.callId,
|
|
162
|
+
toolName: toolCall.name,
|
|
163
|
+
input: toolCall.result,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (chunk) {
|
|
168
|
+
pendingChunks.push(chunk);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
// Ignore parse errors
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Enqueue the first chunk if we have any
|
|
177
|
+
if (pendingChunks.length > 0) {
|
|
178
|
+
controller.enqueue(pendingChunks.shift());
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (!isClosed) {
|
|
183
|
+
isClosed = true;
|
|
184
|
+
controller.error(error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
cancel() {
|
|
189
|
+
isClosed = true;
|
|
190
|
+
pendingChunks = [];
|
|
191
|
+
if (reader) {
|
|
192
|
+
reader.releaseLock();
|
|
193
|
+
reader = null;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import Image from "next/image";
|
|
5
|
+
import { XIcon, PlusIcon, FileText } from "lucide-react";
|
|
6
|
+
import { AttachmentPrimitive, ComposerPrimitive, MessagePrimitive, useAssistantState, useAssistantApi, } from "@assistant-ui/react";
|
|
7
|
+
import { useShallow } from "zustand/shallow";
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../shadcn/tooltip";
|
|
9
|
+
import { Dialog, DialogTitle, DialogContent, DialogTrigger, } from "../../shadcn/dialog";
|
|
10
|
+
import { Avatar, AvatarImage, AvatarFallback } from "../../shadcn/avatar";
|
|
11
|
+
import { TooltipIconButton } from "./tooltip-icon-button";
|
|
12
|
+
import { cn } from "../../utils/cn";
|
|
13
|
+
const useFileSrc = (file) => {
|
|
14
|
+
const [src, setSrc] = useState(undefined);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!file) {
|
|
17
|
+
setSrc(undefined);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const objectUrl = URL.createObjectURL(file);
|
|
21
|
+
setSrc(objectUrl);
|
|
22
|
+
return () => {
|
|
23
|
+
URL.revokeObjectURL(objectUrl);
|
|
24
|
+
};
|
|
25
|
+
}, [file]);
|
|
26
|
+
return src;
|
|
27
|
+
};
|
|
28
|
+
const useAttachmentSrc = () => {
|
|
29
|
+
var _a;
|
|
30
|
+
const { file, src } = useAssistantState(useShallow(({ attachment }) => {
|
|
31
|
+
var _a, _b;
|
|
32
|
+
if (attachment.type !== "image")
|
|
33
|
+
return {};
|
|
34
|
+
if (attachment.file)
|
|
35
|
+
return { file: attachment.file };
|
|
36
|
+
const src = (_b = (_a = attachment.content) === null || _a === void 0 ? void 0 : _a.filter((c) => c.type === "image")[0]) === null || _b === void 0 ? void 0 : _b.image;
|
|
37
|
+
if (!src)
|
|
38
|
+
return {};
|
|
39
|
+
return { src };
|
|
40
|
+
}));
|
|
41
|
+
return (_a = useFileSrc(file)) !== null && _a !== void 0 ? _a : src;
|
|
42
|
+
};
|
|
43
|
+
const AttachmentPreview = ({ src }) => {
|
|
44
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
45
|
+
return (_jsx(Image, { src: src, alt: "Image Preview", width: 1, height: 1, className: isLoaded
|
|
46
|
+
? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
|
|
47
|
+
: "aui-attachment-preview-image-loading hidden", onLoadingComplete: () => setIsLoaded(true), priority: false }));
|
|
48
|
+
};
|
|
49
|
+
const AttachmentPreviewDialog = ({ children }) => {
|
|
50
|
+
const src = useAttachmentSrc();
|
|
51
|
+
if (!src)
|
|
52
|
+
return children;
|
|
53
|
+
return (_jsxs(Dialog, { children: [_jsx(DialogTrigger, { className: "aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50", asChild: true, children: children }), _jsxs(DialogContent, { className: "aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive", children: [_jsx(DialogTitle, { className: "aui-sr-only sr-only", children: "Image Attachment Preview" }), _jsx("div", { className: "aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background", children: _jsx(AttachmentPreview, { src: src }) })] })] }));
|
|
54
|
+
};
|
|
55
|
+
const AttachmentThumb = () => {
|
|
56
|
+
const isImage = useAssistantState(({ attachment }) => attachment.type === "image");
|
|
57
|
+
const src = useAttachmentSrc();
|
|
58
|
+
return (_jsxs(Avatar, { className: "aui-attachment-tile-avatar h-full w-full rounded-none", children: [_jsx(AvatarImage, { src: src, alt: "Attachment preview", className: "aui-attachment-tile-image object-cover" }), _jsx(AvatarFallback, { delayMs: isImage ? 200 : 0, children: _jsx(FileText, { className: "aui-attachment-tile-fallback-icon size-8 text-muted-foreground" }) })] }));
|
|
59
|
+
};
|
|
60
|
+
const AttachmentUI = () => {
|
|
61
|
+
const api = useAssistantApi();
|
|
62
|
+
const isComposer = api.attachment.source === "composer";
|
|
63
|
+
const isImage = useAssistantState(({ attachment }) => attachment.type === "image");
|
|
64
|
+
const typeLabel = useAssistantState(({ attachment }) => {
|
|
65
|
+
const type = attachment.type;
|
|
66
|
+
switch (type) {
|
|
67
|
+
case "image":
|
|
68
|
+
return "Image";
|
|
69
|
+
case "document":
|
|
70
|
+
return "Document";
|
|
71
|
+
case "file":
|
|
72
|
+
return "File";
|
|
73
|
+
default:
|
|
74
|
+
const _exhaustiveCheck = type;
|
|
75
|
+
throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return (_jsxs(Tooltip, { children: [_jsxs(AttachmentPrimitive.Root, { className: cn("aui-attachment-root relative", isImage &&
|
|
79
|
+
"aui-attachment-root-composer only:[&>#attachment-tile]:size-24"), children: [_jsx(AttachmentPreviewDialog, { children: _jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { className: cn("aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75", isComposer &&
|
|
80
|
+
"aui-attachment-tile-composer border-foreground/20"), role: "button", id: "attachment-tile", "aria-label": `${typeLabel} attachment`, children: _jsx(AttachmentThumb, {}) }) }) }), isComposer && _jsx(AttachmentRemove, {})] }), _jsx(TooltipContent, { side: "top", children: _jsx(AttachmentPrimitive.Name, {}) })] }));
|
|
81
|
+
};
|
|
82
|
+
const AttachmentRemove = () => {
|
|
83
|
+
return (_jsx(AttachmentPrimitive.Remove, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "Remove file", className: "aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive", side: "top", children: _jsx(XIcon, { className: "aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" }) }) }));
|
|
84
|
+
};
|
|
85
|
+
export const UserMessageAttachments = () => {
|
|
86
|
+
return (_jsx("div", { className: "aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2", children: _jsx(MessagePrimitive.Attachments, { components: { Attachment: AttachmentUI } }) }));
|
|
87
|
+
};
|
|
88
|
+
export const ComposerAttachments = () => {
|
|
89
|
+
return (_jsx("div", { className: "aui-composer-attachments mb-2 flex w-full flex-row items-center gap-2 overflow-x-auto px-1.5 pt-0.5 pb-1 empty:hidden", children: _jsx(ComposerPrimitive.Attachments, { components: { Attachment: AttachmentUI } }) }));
|
|
90
|
+
};
|
|
91
|
+
export const ComposerAddAttachment = () => {
|
|
92
|
+
return (_jsx(ComposerPrimitive.AddAttachment, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "Add Attachment", side: "bottom", variant: "ghost", size: "icon", className: "aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30", "aria-label": "Add Attachment", children: _jsx(PlusIcon, { className: "aui-attachment-add-icon size-5 stroke-[1.5px]" }) }) }));
|
|
93
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import "@assistant-ui/react-markdown";
|
|
15
|
+
import { MarkdownTextPrimitive, unstable_memoizeMarkdownComponents as memoizeMarkdownComponents, useIsMarkdownCodeBlock, } from "@assistant-ui/react-markdown";
|
|
16
|
+
import remarkGfm from "remark-gfm";
|
|
17
|
+
import { memo, useState } from "react";
|
|
18
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
19
|
+
import { TooltipIconButton } from "../assistant-ui/tooltip-icon-button";
|
|
20
|
+
import { cn } from "../../utils/cn";
|
|
21
|
+
const MarkdownTextImpl = () => {
|
|
22
|
+
return (_jsx(MarkdownTextPrimitive, { remarkPlugins: [remarkGfm], className: "aui-md", components: defaultComponents }));
|
|
23
|
+
};
|
|
24
|
+
export const MarkdownText = memo(MarkdownTextImpl);
|
|
25
|
+
const CodeHeader = ({ language, code }) => {
|
|
26
|
+
const { isCopied, copyToClipboard } = useCopyToClipboard();
|
|
27
|
+
const onCopy = () => {
|
|
28
|
+
if (!code || isCopied)
|
|
29
|
+
return;
|
|
30
|
+
copyToClipboard(code);
|
|
31
|
+
};
|
|
32
|
+
return (_jsxs("div", { className: "aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 font-semibold text-foreground text-sm dark:bg-muted-foreground/20", children: [_jsx("span", { className: "aui-code-header-language lowercase [&>span]:text-xs", children: language }), _jsxs(TooltipIconButton, { tooltip: "Copy", onClick: onCopy, children: [!isCopied && _jsx(CopyIcon, {}), isCopied && _jsx(CheckIcon, {})] })] }));
|
|
33
|
+
};
|
|
34
|
+
const useCopyToClipboard = ({ copiedDuration = 3000, } = {}) => {
|
|
35
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
36
|
+
const copyToClipboard = (value) => {
|
|
37
|
+
if (!value)
|
|
38
|
+
return;
|
|
39
|
+
navigator.clipboard.writeText(value).then(() => {
|
|
40
|
+
setIsCopied(true);
|
|
41
|
+
setTimeout(() => setIsCopied(false), copiedDuration);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
return { isCopied, copyToClipboard };
|
|
45
|
+
};
|
|
46
|
+
const defaultComponents = memoizeMarkdownComponents({
|
|
47
|
+
h1: (_a) => {
|
|
48
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
49
|
+
return (_jsx("h1", Object.assign({ className: cn("aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0", className) }, props)));
|
|
50
|
+
},
|
|
51
|
+
h2: (_a) => {
|
|
52
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
53
|
+
return (_jsx("h2", Object.assign({ className: cn("aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0", className) }, props)));
|
|
54
|
+
},
|
|
55
|
+
h3: (_a) => {
|
|
56
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
57
|
+
return (_jsx("h3", Object.assign({ className: cn("aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0", className) }, props)));
|
|
58
|
+
},
|
|
59
|
+
h4: (_a) => {
|
|
60
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
61
|
+
return (_jsx("h4", Object.assign({ className: cn("aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0", className) }, props)));
|
|
62
|
+
},
|
|
63
|
+
h5: (_a) => {
|
|
64
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
65
|
+
return (_jsx("h5", Object.assign({ className: cn("aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0", className) }, props)));
|
|
66
|
+
},
|
|
67
|
+
h6: (_a) => {
|
|
68
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
69
|
+
return (_jsx("h6", Object.assign({ className: cn("aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0", className) }, props)));
|
|
70
|
+
},
|
|
71
|
+
p: (_a) => {
|
|
72
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
73
|
+
return (_jsx("p", Object.assign({ className: cn("aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className) }, props)));
|
|
74
|
+
},
|
|
75
|
+
a: (_a) => {
|
|
76
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
77
|
+
return (_jsx("a", Object.assign({ className: cn("aui-md-a font-medium text-primary underline underline-offset-4", className) }, props)));
|
|
78
|
+
},
|
|
79
|
+
blockquote: (_a) => {
|
|
80
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
81
|
+
return (_jsx("blockquote", Object.assign({ className: cn("aui-md-blockquote border-l-2 pl-6 italic", className) }, props)));
|
|
82
|
+
},
|
|
83
|
+
ul: (_a) => {
|
|
84
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
85
|
+
return (_jsx("ul", Object.assign({ className: cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className) }, props)));
|
|
86
|
+
},
|
|
87
|
+
ol: (_a) => {
|
|
88
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
89
|
+
return (_jsx("ol", Object.assign({ className: cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className) }, props)));
|
|
90
|
+
},
|
|
91
|
+
hr: (_a) => {
|
|
92
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
93
|
+
return (_jsx("hr", Object.assign({ className: cn("aui-md-hr my-5 border-b", className) }, props)));
|
|
94
|
+
},
|
|
95
|
+
table: (_a) => {
|
|
96
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
97
|
+
return (_jsx("table", Object.assign({ className: cn("aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto", className) }, props)));
|
|
98
|
+
},
|
|
99
|
+
th: (_a) => {
|
|
100
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
101
|
+
return (_jsx("th", Object.assign({ className: cn("aui-md-th bg-muted px-4 py-2 text-left font-bold first:rounded-tl-lg last:rounded-tr-lg [[align=center]]:text-center [[align=right]]:text-right", className) }, props)));
|
|
102
|
+
},
|
|
103
|
+
td: (_a) => {
|
|
104
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
105
|
+
return (_jsx("td", Object.assign({ className: cn("aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right", className) }, props)));
|
|
106
|
+
},
|
|
107
|
+
tr: (_a) => {
|
|
108
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
109
|
+
return (_jsx("tr", Object.assign({ className: cn("aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg", className) }, props)));
|
|
110
|
+
},
|
|
111
|
+
sup: (_a) => {
|
|
112
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
113
|
+
return (_jsx("sup", Object.assign({ className: cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className) }, props)));
|
|
114
|
+
},
|
|
115
|
+
pre: (_a) => {
|
|
116
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
117
|
+
return (_jsx("pre", Object.assign({ className: cn("aui-md-pre overflow-x-auto rounded-t-none!rounded-b-lg bg-black p-4 text-white", className) }, props)));
|
|
118
|
+
},
|
|
119
|
+
code: function Code(_a) {
|
|
120
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
121
|
+
const isCodeBlock = useIsMarkdownCodeBlock();
|
|
122
|
+
return (_jsx("code", Object.assign({ className: cn(!isCodeBlock &&
|
|
123
|
+
"aui-md-inline-code rounded border bg-muted font-semibold", className) }, props)));
|
|
124
|
+
},
|
|
125
|
+
CodeHeader,
|
|
126
|
+
});
|