@meshagent/meshagent-tailwind 0.41.4 → 0.41.6
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/CHANGELOG.md +10 -0
- package/README.md +33 -0
- package/dist/cjs/chat/chat-thread.js +15 -10
- package/dist/cjs/chat/dataset-chat-thread.d.ts +4 -1
- package/dist/cjs/chat/dataset-chat-thread.js +130 -157
- package/dist/cjs/chat/multi-thread-view.d.ts +4 -1
- package/dist/cjs/chat/multi-thread-view.js +4 -0
- package/dist/cjs/chat/new-chat-thread.d.ts +4 -1
- package/dist/cjs/chat/new-chat-thread.js +43 -87
- package/dist/cjs/file-preview/file-preview.d.ts +6 -0
- package/dist/cjs/file-preview/file-preview.js +220 -0
- package/dist/cjs/meetings/camera-grid.d.ts +46 -0
- package/dist/cjs/meetings/camera-grid.js +435 -0
- package/dist/cjs/meetings/controls.d.ts +4 -2
- package/dist/cjs/meetings/controls.js +9 -3
- package/dist/cjs/meetings/lobby.d.ts +17 -0
- package/dist/cjs/meetings/lobby.js +595 -0
- package/dist/cjs/meetings/meeting-scope.d.ts +7 -6
- package/dist/cjs/meetings/meeting-scope.js +64 -15
- package/dist/cjs/meetings/meeting-view.d.ts +6 -0
- package/dist/cjs/meetings/meeting-view.js +635 -0
- package/dist/cjs/meetings/meetings.d.ts +3 -0
- package/dist/cjs/meetings/meetings.js +3 -0
- package/dist/cjs/meetings/wake-lock.js +2 -2
- package/dist/esm/chat/chat-thread.js +15 -10
- package/dist/esm/chat/dataset-chat-thread.d.ts +4 -1
- package/dist/esm/chat/dataset-chat-thread.js +129 -133
- package/dist/esm/chat/multi-thread-view.d.ts +4 -1
- package/dist/esm/chat/multi-thread-view.js +4 -0
- package/dist/esm/chat/new-chat-thread.d.ts +4 -1
- package/dist/esm/chat/new-chat-thread.js +43 -87
- package/dist/esm/file-preview/file-preview.d.ts +6 -0
- package/dist/esm/file-preview/file-preview.js +220 -0
- package/dist/esm/meetings/camera-grid.d.ts +46 -0
- package/dist/esm/meetings/camera-grid.js +405 -0
- package/dist/esm/meetings/controls.d.ts +4 -2
- package/dist/esm/meetings/controls.js +9 -3
- package/dist/esm/meetings/lobby.d.ts +17 -0
- package/dist/esm/meetings/lobby.js +595 -0
- package/dist/esm/meetings/meeting-scope.d.ts +7 -6
- package/dist/esm/meetings/meeting-scope.js +71 -16
- package/dist/esm/meetings/meeting-view.d.ts +6 -0
- package/dist/esm/meetings/meeting-view.js +630 -0
- package/dist/esm/meetings/meetings.d.ts +3 -0
- package/dist/esm/meetings/meetings.js +3 -0
- package/dist/esm/meetings/wake-lock.js +2 -2
- package/dist/index.css +1 -1
- package/package.json +9 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { MessagingChatClient } from "@meshagent/meshagent-agents";
|
|
4
4
|
import { ChatInput } from "./chat-input";
|
|
5
5
|
import { MeshagentFileUpload, fileToAsyncIterable } from "./file-attachment";
|
|
6
6
|
import { Toaster } from "../components/ui/sonner";
|
|
@@ -15,7 +15,7 @@ function normalizeThreadPath(path) {
|
|
|
15
15
|
return normalizedPath ? normalizedPath : null;
|
|
16
16
|
}
|
|
17
17
|
function getParticipantName(participant) {
|
|
18
|
-
const name = participant
|
|
18
|
+
const name = participant?.getAttribute("name");
|
|
19
19
|
return typeof name === "string" ? name.trim() : "";
|
|
20
20
|
}
|
|
21
21
|
function displayParticipantName(name) {
|
|
@@ -46,53 +46,6 @@ function ensureOperationActive(operationId, activeOperationRef) {
|
|
|
46
46
|
throw new NewThreadCancelledError();
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
function delay(milliseconds) {
|
|
50
|
-
return new Promise((resolve) => {
|
|
51
|
-
window.setTimeout(resolve, milliseconds);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
async function waitForTargetAgent(params) {
|
|
55
|
-
const { room, agentName, operationId, activeOperationRef } = params;
|
|
56
|
-
while (true) {
|
|
57
|
-
ensureOperationActive(operationId, activeOperationRef);
|
|
58
|
-
const targetAgent = findTargetAgent(room, agentName);
|
|
59
|
-
if (targetAgent) {
|
|
60
|
-
return targetAgent;
|
|
61
|
-
}
|
|
62
|
-
await delay(250);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
async function waitForToolkitAvailable(params) {
|
|
66
|
-
const {
|
|
67
|
-
room,
|
|
68
|
-
participantId,
|
|
69
|
-
toolkit,
|
|
70
|
-
operationId,
|
|
71
|
-
activeOperationRef
|
|
72
|
-
} = params;
|
|
73
|
-
while (true) {
|
|
74
|
-
ensureOperationActive(operationId, activeOperationRef);
|
|
75
|
-
try {
|
|
76
|
-
const toolkits = await room.agents.listToolkits({ participantId, timeout: 1e3 });
|
|
77
|
-
if (toolkits.some((toolkitDescription) => toolkitDescription.name === toolkit)) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
82
|
-
await delay(250);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function getStringField(record, key) {
|
|
86
|
-
const value = record[key];
|
|
87
|
-
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
88
|
-
}
|
|
89
|
-
function parseThreadToolResult(toolkit, tool, content) {
|
|
90
|
-
const path = getStringField(content.json, "path");
|
|
91
|
-
if (!path) {
|
|
92
|
-
throw new Error(`${toolkit}.${tool} response missing path`);
|
|
93
|
-
}
|
|
94
|
-
return [path, getStringField(content.json, "name")];
|
|
95
|
-
}
|
|
96
49
|
function describeError(error) {
|
|
97
50
|
if (error instanceof Error && error.message.trim() !== "") {
|
|
98
51
|
return error.message;
|
|
@@ -113,10 +66,10 @@ function ErrorBanner({ message }) {
|
|
|
113
66
|
}
|
|
114
67
|
function NewChatThread({
|
|
115
68
|
room,
|
|
69
|
+
chatClient,
|
|
70
|
+
disposeChatClient = false,
|
|
116
71
|
agentName,
|
|
117
72
|
builder,
|
|
118
|
-
toolkit = "chat",
|
|
119
|
-
tool = "new_thread",
|
|
120
73
|
selectedThreadPath,
|
|
121
74
|
onThreadPathChanged,
|
|
122
75
|
onThreadResolved,
|
|
@@ -133,11 +86,30 @@ function NewChatThread({
|
|
|
133
86
|
const activeOperationRef = useRef(0);
|
|
134
87
|
const controlledThreadPath = selectedThreadPath !== void 0 ? normalizeThreadPath(selectedThreadPath) : void 0;
|
|
135
88
|
const activePath = controlledThreadPath ?? internalThreadPath;
|
|
89
|
+
const ownsChatClient = chatClient == null;
|
|
90
|
+
const activeChatClient = useMemo(
|
|
91
|
+
() => chatClient ?? new MessagingChatClient({ room, agentName }),
|
|
92
|
+
[agentName, chatClient, room]
|
|
93
|
+
);
|
|
94
|
+
const [clientVersion, setClientVersion] = useState(0);
|
|
136
95
|
useEffect(() => {
|
|
137
96
|
return () => {
|
|
138
97
|
activeOperationRef.current += 1;
|
|
139
98
|
};
|
|
140
99
|
}, []);
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
void activeChatClient.start();
|
|
102
|
+
const handleChange = () => {
|
|
103
|
+
setClientVersion((current) => current + 1);
|
|
104
|
+
};
|
|
105
|
+
activeChatClient.addListener(handleChange);
|
|
106
|
+
return () => {
|
|
107
|
+
activeChatClient.removeListener(handleChange);
|
|
108
|
+
if (ownsChatClient || disposeChatClient) {
|
|
109
|
+
void activeChatClient.stop();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}, [activeChatClient, disposeChatClient, ownsChatClient]);
|
|
141
113
|
useEffect(() => {
|
|
142
114
|
if (controlledThreadPath === void 0) {
|
|
143
115
|
return;
|
|
@@ -179,46 +151,30 @@ function NewChatThread({
|
|
|
179
151
|
}
|
|
180
152
|
const operationId = activeOperationRef.current + 1;
|
|
181
153
|
activeOperationRef.current = operationId;
|
|
182
|
-
const initialTargetAgent = findTargetAgent(room, agentName);
|
|
183
|
-
setWaitingForAgent(initialTargetAgent === null);
|
|
184
|
-
setCreatingNewThread(initialTargetAgent !== null);
|
|
154
|
+
const initialTargetAgent = activeChatClient.agentParticipant() ?? findTargetAgent(room, agentName);
|
|
155
|
+
setWaitingForAgent(initialTargetAgent === null && chatClient == null);
|
|
156
|
+
setCreatingNewThread(initialTargetAgent !== null || chatClient != null);
|
|
185
157
|
setNewThreadError(null);
|
|
186
158
|
try {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
159
|
+
if (initialTargetAgent === null && chatClient == null) {
|
|
160
|
+
if (!(activeChatClient instanceof MessagingChatClient)) {
|
|
161
|
+
throw new Error("No online agent supports agent messages.");
|
|
162
|
+
}
|
|
163
|
+
await activeChatClient.waitForAgentParticipant({ waitKey: String(operationId) });
|
|
164
|
+
}
|
|
193
165
|
ensureOperationActive(operationId, activeOperationRef);
|
|
194
|
-
if (initialTargetAgent === null) {
|
|
166
|
+
if (initialTargetAgent === null && chatClient == null) {
|
|
195
167
|
setWaitingForAgent(false);
|
|
196
168
|
setCreatingNewThread(true);
|
|
197
169
|
}
|
|
198
|
-
await
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
operationId,
|
|
203
|
-
activeOperationRef
|
|
204
|
-
});
|
|
205
|
-
ensureOperationActive(operationId, activeOperationRef);
|
|
206
|
-
const response = await room.invoke({
|
|
207
|
-
participantId: targetAgent.id,
|
|
208
|
-
toolkit,
|
|
209
|
-
tool,
|
|
210
|
-
arguments: {
|
|
211
|
-
message: {
|
|
212
|
-
text,
|
|
213
|
-
attachments: newThreadAttachments.map((attachment) => ({ path: attachment.path }))
|
|
214
|
-
}
|
|
215
|
-
}
|
|
170
|
+
const result = await activeChatClient.startThread({
|
|
171
|
+
message: text,
|
|
172
|
+
attachments: newThreadAttachments.map((attachment) => attachment.path),
|
|
173
|
+
senderName: getParticipantName(room.localParticipant) || void 0
|
|
216
174
|
});
|
|
217
175
|
ensureOperationActive(operationId, activeOperationRef);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
const [threadPath, displayName] = parseThreadToolResult(toolkit, tool, response);
|
|
176
|
+
const threadPath = result.threadPath;
|
|
177
|
+
const displayName = null;
|
|
222
178
|
const normalizedPath = normalizeThreadPath(threadPath);
|
|
223
179
|
if (controlledThreadPath === void 0) {
|
|
224
180
|
setInternalThreadPath(normalizedPath);
|
|
@@ -239,7 +195,9 @@ function NewChatThread({
|
|
|
239
195
|
setNewThreadError(describeError(error));
|
|
240
196
|
}
|
|
241
197
|
}, [
|
|
198
|
+
activeChatClient,
|
|
242
199
|
agentName,
|
|
200
|
+
chatClient,
|
|
243
201
|
controlledThreadPath,
|
|
244
202
|
creatingNewThread,
|
|
245
203
|
newThreadAttachments,
|
|
@@ -247,8 +205,6 @@ function NewChatThread({
|
|
|
247
205
|
onThreadPathChanged,
|
|
248
206
|
onThreadResolved,
|
|
249
207
|
room,
|
|
250
|
-
toolkit,
|
|
251
|
-
tool,
|
|
252
208
|
waitingForAgent
|
|
253
209
|
]);
|
|
254
210
|
const targetAgentLabel = useMemo(() => {
|
|
@@ -256,9 +212,9 @@ function NewChatThread({
|
|
|
256
212
|
if (knownAgentName) {
|
|
257
213
|
return displayParticipantName(knownAgentName);
|
|
258
214
|
}
|
|
259
|
-
const targetAgent = findTargetAgent(room);
|
|
215
|
+
const targetAgent = activeChatClient.agentParticipant() ?? findTargetAgent(room);
|
|
260
216
|
return displayParticipantName(targetAgent ? getParticipantName(targetAgent) : null);
|
|
261
|
-
}, [agentName, room]);
|
|
217
|
+
}, [activeChatClient, agentName, clientVersion, room]);
|
|
262
218
|
const pendingStatusText = waitingForAgent ? `Waiting for ${targetAgentLabel} to be ready.` : creatingNewThread ? `Starting a thread with ${targetAgentLabel}.` : null;
|
|
263
219
|
if (activePath !== null) {
|
|
264
220
|
return builder(activePath);
|
|
@@ -4,6 +4,8 @@ export declare enum FileKind {
|
|
|
4
4
|
Image = "image",
|
|
5
5
|
Video = "video",
|
|
6
6
|
Pdf = "pdf",
|
|
7
|
+
Source = "source",
|
|
8
|
+
Thread = "thread",
|
|
7
9
|
Unknown = "unknown"
|
|
8
10
|
}
|
|
9
11
|
export declare function filePreviewName(path: string): string;
|
|
@@ -22,6 +24,10 @@ export declare function PdfPreview({ room, path }: {
|
|
|
22
24
|
room: RoomClient;
|
|
23
25
|
path: string;
|
|
24
26
|
}): ReactElement;
|
|
27
|
+
export declare function SourcePreview({ room, path }: {
|
|
28
|
+
room: RoomClient;
|
|
29
|
+
path: string;
|
|
30
|
+
}): ReactElement;
|
|
25
31
|
export declare function FilePreview({ room, path }: {
|
|
26
32
|
room: RoomClient;
|
|
27
33
|
path: string;
|
|
@@ -2,6 +2,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Download, FileText } from "lucide-react";
|
|
4
4
|
import { Document, Page, pdfjs } from "react-pdf";
|
|
5
|
+
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
6
|
+
import { oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
5
7
|
import {
|
|
6
8
|
Dialog,
|
|
7
9
|
DialogContent,
|
|
@@ -13,11 +15,14 @@ import {
|
|
|
13
15
|
import { Button } from "../components/ui/button";
|
|
14
16
|
import { Spinner } from "../components/ui/spinner";
|
|
15
17
|
import { cn } from "../lib/utils";
|
|
18
|
+
import { ChatThread } from "../chat/chat-thread";
|
|
16
19
|
pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
17
20
|
var FileKind = /* @__PURE__ */ ((FileKind2) => {
|
|
18
21
|
FileKind2["Image"] = "image";
|
|
19
22
|
FileKind2["Video"] = "video";
|
|
20
23
|
FileKind2["Pdf"] = "pdf";
|
|
24
|
+
FileKind2["Source"] = "source";
|
|
25
|
+
FileKind2["Thread"] = "thread";
|
|
21
26
|
FileKind2["Unknown"] = "unknown";
|
|
22
27
|
return FileKind2;
|
|
23
28
|
})(FileKind || {});
|
|
@@ -39,6 +44,123 @@ const imageExtensions = /* @__PURE__ */ new Set([
|
|
|
39
44
|
]);
|
|
40
45
|
const videoExtensions = /* @__PURE__ */ new Set(["m4v", "mkv", "mov", "mp4", "webm"]);
|
|
41
46
|
const pdfExtensions = /* @__PURE__ */ new Set(["pdf"]);
|
|
47
|
+
const sourceExtensions = /* @__PURE__ */ new Set([
|
|
48
|
+
"bash",
|
|
49
|
+
"c",
|
|
50
|
+
"cc",
|
|
51
|
+
"cfg",
|
|
52
|
+
"cmake",
|
|
53
|
+
"cpp",
|
|
54
|
+
"cs",
|
|
55
|
+
"css",
|
|
56
|
+
"csv",
|
|
57
|
+
"dart",
|
|
58
|
+
"diff",
|
|
59
|
+
"dockerfile",
|
|
60
|
+
"env",
|
|
61
|
+
"fish",
|
|
62
|
+
"go",
|
|
63
|
+
"gradle",
|
|
64
|
+
"graphql",
|
|
65
|
+
"h",
|
|
66
|
+
"hpp",
|
|
67
|
+
"htm",
|
|
68
|
+
"html",
|
|
69
|
+
"ini",
|
|
70
|
+
"java",
|
|
71
|
+
"js",
|
|
72
|
+
"json",
|
|
73
|
+
"jsx",
|
|
74
|
+
"kt",
|
|
75
|
+
"kts",
|
|
76
|
+
"lua",
|
|
77
|
+
"md",
|
|
78
|
+
"mjs",
|
|
79
|
+
"patch",
|
|
80
|
+
"php",
|
|
81
|
+
"proto",
|
|
82
|
+
"py",
|
|
83
|
+
"rb",
|
|
84
|
+
"rs",
|
|
85
|
+
"scss",
|
|
86
|
+
"sh",
|
|
87
|
+
"sql",
|
|
88
|
+
"swift",
|
|
89
|
+
"toml",
|
|
90
|
+
"ts",
|
|
91
|
+
"tsx",
|
|
92
|
+
"txt",
|
|
93
|
+
"xml",
|
|
94
|
+
"yaml",
|
|
95
|
+
"yml",
|
|
96
|
+
"zsh"
|
|
97
|
+
]);
|
|
98
|
+
const sourceFilenames = /* @__PURE__ */ new Set([
|
|
99
|
+
".dockerignore",
|
|
100
|
+
".env",
|
|
101
|
+
".gitignore",
|
|
102
|
+
"dockerfile",
|
|
103
|
+
"makefile"
|
|
104
|
+
]);
|
|
105
|
+
const sourceLanguagesByExtension = /* @__PURE__ */ new Map([
|
|
106
|
+
["bash", "bash"],
|
|
107
|
+
["c", "c"],
|
|
108
|
+
["cc", "cpp"],
|
|
109
|
+
["cfg", "ini"],
|
|
110
|
+
["cmake", "cmake"],
|
|
111
|
+
["cpp", "cpp"],
|
|
112
|
+
["cs", "csharp"],
|
|
113
|
+
["css", "css"],
|
|
114
|
+
["csv", "csv"],
|
|
115
|
+
["dart", "dart"],
|
|
116
|
+
["diff", "diff"],
|
|
117
|
+
["dockerfile", "docker"],
|
|
118
|
+
["env", "ini"],
|
|
119
|
+
["fish", "fish"],
|
|
120
|
+
["go", "go"],
|
|
121
|
+
["gradle", "gradle"],
|
|
122
|
+
["graphql", "graphql"],
|
|
123
|
+
["h", "c"],
|
|
124
|
+
["hpp", "cpp"],
|
|
125
|
+
["htm", "html"],
|
|
126
|
+
["html", "html"],
|
|
127
|
+
["ini", "ini"],
|
|
128
|
+
["java", "java"],
|
|
129
|
+
["js", "javascript"],
|
|
130
|
+
["json", "json"],
|
|
131
|
+
["jsx", "jsx"],
|
|
132
|
+
["kt", "kotlin"],
|
|
133
|
+
["kts", "kotlin"],
|
|
134
|
+
["lua", "lua"],
|
|
135
|
+
["md", "markdown"],
|
|
136
|
+
["mjs", "javascript"],
|
|
137
|
+
["patch", "diff"],
|
|
138
|
+
["php", "php"],
|
|
139
|
+
["proto", "protobuf"],
|
|
140
|
+
["py", "python"],
|
|
141
|
+
["rb", "ruby"],
|
|
142
|
+
["rs", "rust"],
|
|
143
|
+
["scss", "scss"],
|
|
144
|
+
["sh", "bash"],
|
|
145
|
+
["sql", "sql"],
|
|
146
|
+
["swift", "swift"],
|
|
147
|
+
["toml", "toml"],
|
|
148
|
+
["ts", "typescript"],
|
|
149
|
+
["tsx", "tsx"],
|
|
150
|
+
["txt", "text"],
|
|
151
|
+
["xml", "xml"],
|
|
152
|
+
["yaml", "yaml"],
|
|
153
|
+
["yml", "yaml"],
|
|
154
|
+
["zsh", "bash"]
|
|
155
|
+
]);
|
|
156
|
+
const sourceLanguagesByFilename = /* @__PURE__ */ new Map([
|
|
157
|
+
[".dockerignore", "docker"],
|
|
158
|
+
[".env", "ini"],
|
|
159
|
+
[".gitignore", "git"],
|
|
160
|
+
["dockerfile", "docker"],
|
|
161
|
+
["makefile", "makefile"]
|
|
162
|
+
]);
|
|
163
|
+
const threadExtensions = /* @__PURE__ */ new Set(["thread"]);
|
|
42
164
|
function basename(path) {
|
|
43
165
|
const withoutQuery = path.split("?")[0] ?? path;
|
|
44
166
|
const withoutHash = withoutQuery.split("#")[0] ?? withoutQuery;
|
|
@@ -70,6 +192,12 @@ function classifyFile(path) {
|
|
|
70
192
|
if (pdfExtensions.has(ext)) {
|
|
71
193
|
return "pdf" /* Pdf */;
|
|
72
194
|
}
|
|
195
|
+
if (sourceExtensions.has(ext) || sourceFilenames.has(filePreviewName(path).toLowerCase())) {
|
|
196
|
+
return "source" /* Source */;
|
|
197
|
+
}
|
|
198
|
+
if (threadExtensions.has(ext)) {
|
|
199
|
+
return "thread" /* Thread */;
|
|
200
|
+
}
|
|
73
201
|
return "unknown" /* Unknown */;
|
|
74
202
|
}
|
|
75
203
|
function isImagePath(path) {
|
|
@@ -141,6 +269,55 @@ function usePdfUrl(room, path) {
|
|
|
141
269
|
}, [path, room]);
|
|
142
270
|
return { url, error };
|
|
143
271
|
}
|
|
272
|
+
function sourceLanguage(path) {
|
|
273
|
+
const name = filePreviewName(path).toLowerCase();
|
|
274
|
+
const byFilename = sourceLanguagesByFilename.get(name);
|
|
275
|
+
if (byFilename != null) {
|
|
276
|
+
return byFilename;
|
|
277
|
+
}
|
|
278
|
+
return sourceLanguagesByExtension.get(extension(path)) ?? "text";
|
|
279
|
+
}
|
|
280
|
+
function useSourceText(room, path) {
|
|
281
|
+
const [text, setText] = useState(null);
|
|
282
|
+
const [error, setError] = useState(null);
|
|
283
|
+
useEffect(() => {
|
|
284
|
+
let cancelled = false;
|
|
285
|
+
const controller = new AbortController();
|
|
286
|
+
const normalizedPath = path.trim();
|
|
287
|
+
setText(null);
|
|
288
|
+
setError(null);
|
|
289
|
+
if (normalizedPath === "") {
|
|
290
|
+
return () => {
|
|
291
|
+
controller.abort();
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const loadText = async () => {
|
|
295
|
+
if (isHttpUrl(normalizedPath)) {
|
|
296
|
+
const response = await fetch(normalizedPath, { signal: controller.signal });
|
|
297
|
+
if (!response.ok) {
|
|
298
|
+
throw new Error(`HTTP ${response.status}`);
|
|
299
|
+
}
|
|
300
|
+
return await response.text();
|
|
301
|
+
}
|
|
302
|
+
const content = await room.storage.download(normalizedPath);
|
|
303
|
+
return new TextDecoder().decode(content.data);
|
|
304
|
+
};
|
|
305
|
+
void loadText().then((nextText) => {
|
|
306
|
+
if (!cancelled) {
|
|
307
|
+
setText(nextText);
|
|
308
|
+
}
|
|
309
|
+
}).catch((nextError) => {
|
|
310
|
+
if (!cancelled) {
|
|
311
|
+
setError(nextError);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
return () => {
|
|
315
|
+
cancelled = true;
|
|
316
|
+
controller.abort();
|
|
317
|
+
};
|
|
318
|
+
}, [path, room]);
|
|
319
|
+
return { text, error };
|
|
320
|
+
}
|
|
144
321
|
function ErrorPreview({ message }) {
|
|
145
322
|
return /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-48 items-center justify-center p-6 text-center text-sm text-destructive", children: message });
|
|
146
323
|
}
|
|
@@ -252,6 +429,44 @@ function PdfPreview({ room, path }) {
|
|
|
252
429
|
}
|
|
253
430
|
) });
|
|
254
431
|
}
|
|
432
|
+
function SourcePreview({ room, path }) {
|
|
433
|
+
const { text, error } = useSourceText(room, path);
|
|
434
|
+
const language = useMemo(() => sourceLanguage(path), [path]);
|
|
435
|
+
if (error != null) {
|
|
436
|
+
return /* @__PURE__ */ jsx(ErrorPreview, { message: `Unable to load source preview: ${String(error)}` });
|
|
437
|
+
}
|
|
438
|
+
if (text == null) {
|
|
439
|
+
return /* @__PURE__ */ jsx(LoadingPreview, {});
|
|
440
|
+
}
|
|
441
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full overflow-auto bg-[#fafafa] text-sm", children: /* @__PURE__ */ jsx(
|
|
442
|
+
SyntaxHighlighter,
|
|
443
|
+
{
|
|
444
|
+
language,
|
|
445
|
+
style: oneLight,
|
|
446
|
+
showLineNumbers: true,
|
|
447
|
+
wrapLongLines: true,
|
|
448
|
+
customStyle: {
|
|
449
|
+
margin: 0,
|
|
450
|
+
minHeight: "100%",
|
|
451
|
+
background: "transparent",
|
|
452
|
+
padding: "1rem"
|
|
453
|
+
},
|
|
454
|
+
codeTagProps: {
|
|
455
|
+
style: {
|
|
456
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
lineNumberStyle: {
|
|
460
|
+
minWidth: "2.75em",
|
|
461
|
+
paddingRight: "1em",
|
|
462
|
+
color: "var(--muted-foreground)",
|
|
463
|
+
textAlign: "right",
|
|
464
|
+
userSelect: "none"
|
|
465
|
+
},
|
|
466
|
+
children: text
|
|
467
|
+
}
|
|
468
|
+
) });
|
|
469
|
+
}
|
|
255
470
|
function UnsupportedPreview({ room, path }) {
|
|
256
471
|
const filename = filePreviewName(path);
|
|
257
472
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-64 flex-col items-center justify-center gap-4 p-8 text-center", children: [
|
|
@@ -272,6 +487,10 @@ function FilePreview({ room, path }) {
|
|
|
272
487
|
return /* @__PURE__ */ jsx(VideoPreview, { room, path });
|
|
273
488
|
case "pdf" /* Pdf */:
|
|
274
489
|
return /* @__PURE__ */ jsx(PdfPreview, { room, path });
|
|
490
|
+
case "source" /* Source */:
|
|
491
|
+
return /* @__PURE__ */ jsx(SourcePreview, { room, path });
|
|
492
|
+
case "thread" /* Thread */:
|
|
493
|
+
return /* @__PURE__ */ jsx(ChatThread, { room, path });
|
|
275
494
|
case "unknown" /* Unknown */:
|
|
276
495
|
return /* @__PURE__ */ jsx(UnsupportedPreview, { room, path });
|
|
277
496
|
}
|
|
@@ -309,6 +528,7 @@ export {
|
|
|
309
528
|
FilePreviewDialog,
|
|
310
529
|
ImagePreview,
|
|
311
530
|
PdfPreview,
|
|
531
|
+
SourcePreview,
|
|
312
532
|
VideoPreview,
|
|
313
533
|
classifyFile,
|
|
314
534
|
filePreviewName,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { Track } from "livekit-client";
|
|
3
|
+
import type { Participant, TrackPublication, VideoTrack } from "livekit-client";
|
|
4
|
+
export declare const TrackSource: {
|
|
5
|
+
readonly Camera: Track.Source.Camera;
|
|
6
|
+
readonly ScreenShareVideo: Track.Source.ScreenShare;
|
|
7
|
+
};
|
|
8
|
+
export type TrackSource = (typeof TrackSource)[keyof typeof TrackSource];
|
|
9
|
+
export interface CameraGridFrameArgs {
|
|
10
|
+
participant: Participant;
|
|
11
|
+
publication: TrackPublication | null;
|
|
12
|
+
trackNode: ReactNode;
|
|
13
|
+
showName: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface CameraGridProps {
|
|
16
|
+
participants: Participant[];
|
|
17
|
+
room?: unknown | null;
|
|
18
|
+
spacing?: number;
|
|
19
|
+
showNames?: boolean;
|
|
20
|
+
showAllVideos?: boolean;
|
|
21
|
+
preferredSource?: TrackSource;
|
|
22
|
+
rowsDesired?: number;
|
|
23
|
+
columnsDesired?: number;
|
|
24
|
+
tryFill?: boolean;
|
|
25
|
+
/** Equivalent to activeVideoPublicationForSource(p, source). */
|
|
26
|
+
activeVideoPublicationForSource: (participant: Participant, source: TrackSource) => TrackPublication | null | undefined;
|
|
27
|
+
/** Equivalent to activeVideoPublications(p, { source? }). */
|
|
28
|
+
activeVideoPublications: (participant: Participant, options?: {
|
|
29
|
+
source?: TrackSource;
|
|
30
|
+
}) => TrackPublication[];
|
|
31
|
+
/** Equivalent to VideoTrackRenderer. */
|
|
32
|
+
renderVideoTrack: (args: {
|
|
33
|
+
track: VideoTrack;
|
|
34
|
+
fit: "contain" | "cover";
|
|
35
|
+
publication: TrackPublication;
|
|
36
|
+
participant: Participant;
|
|
37
|
+
}) => ReactNode;
|
|
38
|
+
/** Equivalent to AudioStats. Used only for `.agent` camera placeholders. */
|
|
39
|
+
renderAudioStats?: (args: {
|
|
40
|
+
room: unknown;
|
|
41
|
+
participant: Participant;
|
|
42
|
+
}) => ReactNode;
|
|
43
|
+
/** Equivalent to frameBuilder. */
|
|
44
|
+
frameBuilder: (args: CameraGridFrameArgs) => ReactNode;
|
|
45
|
+
}
|
|
46
|
+
export declare function CameraGrid({ participants, room, spacing, showNames, showAllVideos, preferredSource, rowsDesired, columnsDesired, tryFill, activeVideoPublicationForSource, activeVideoPublications, renderVideoTrack, renderAudioStats, frameBuilder, }: CameraGridProps): import("react/jsx-runtime").JSX.Element | null;
|