@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
|
@@ -23,7 +23,7 @@ __export(new_chat_thread_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(new_chat_thread_exports);
|
|
24
24
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
25
|
var import_react = require("react");
|
|
26
|
-
var
|
|
26
|
+
var import_meshagent_agents = require("@meshagent/meshagent-agents");
|
|
27
27
|
var import_chat_input = require("./chat-input");
|
|
28
28
|
var import_file_attachment = require("./file-attachment");
|
|
29
29
|
var import_sonner = require("../components/ui/sonner");
|
|
@@ -38,7 +38,7 @@ function normalizeThreadPath(path) {
|
|
|
38
38
|
return normalizedPath ? normalizedPath : null;
|
|
39
39
|
}
|
|
40
40
|
function getParticipantName(participant) {
|
|
41
|
-
const name = participant
|
|
41
|
+
const name = participant?.getAttribute("name");
|
|
42
42
|
return typeof name === "string" ? name.trim() : "";
|
|
43
43
|
}
|
|
44
44
|
function displayParticipantName(name) {
|
|
@@ -69,53 +69,6 @@ function ensureOperationActive(operationId, activeOperationRef) {
|
|
|
69
69
|
throw new NewThreadCancelledError();
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
function delay(milliseconds) {
|
|
73
|
-
return new Promise((resolve) => {
|
|
74
|
-
window.setTimeout(resolve, milliseconds);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
async function waitForTargetAgent(params) {
|
|
78
|
-
const { room, agentName, operationId, activeOperationRef } = params;
|
|
79
|
-
while (true) {
|
|
80
|
-
ensureOperationActive(operationId, activeOperationRef);
|
|
81
|
-
const targetAgent = findTargetAgent(room, agentName);
|
|
82
|
-
if (targetAgent) {
|
|
83
|
-
return targetAgent;
|
|
84
|
-
}
|
|
85
|
-
await delay(250);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
async function waitForToolkitAvailable(params) {
|
|
89
|
-
const {
|
|
90
|
-
room,
|
|
91
|
-
participantId,
|
|
92
|
-
toolkit,
|
|
93
|
-
operationId,
|
|
94
|
-
activeOperationRef
|
|
95
|
-
} = params;
|
|
96
|
-
while (true) {
|
|
97
|
-
ensureOperationActive(operationId, activeOperationRef);
|
|
98
|
-
try {
|
|
99
|
-
const toolkits = await room.agents.listToolkits({ participantId, timeout: 1e3 });
|
|
100
|
-
if (toolkits.some((toolkitDescription) => toolkitDescription.name === toolkit)) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
} catch {
|
|
104
|
-
}
|
|
105
|
-
await delay(250);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function getStringField(record, key) {
|
|
109
|
-
const value = record[key];
|
|
110
|
-
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
111
|
-
}
|
|
112
|
-
function parseThreadToolResult(toolkit, tool, content) {
|
|
113
|
-
const path = getStringField(content.json, "path");
|
|
114
|
-
if (!path) {
|
|
115
|
-
throw new Error(`${toolkit}.${tool} response missing path`);
|
|
116
|
-
}
|
|
117
|
-
return [path, getStringField(content.json, "name")];
|
|
118
|
-
}
|
|
119
72
|
function describeError(error) {
|
|
120
73
|
if (error instanceof Error && error.message.trim() !== "") {
|
|
121
74
|
return error.message;
|
|
@@ -136,10 +89,10 @@ function ErrorBanner({ message }) {
|
|
|
136
89
|
}
|
|
137
90
|
function NewChatThread({
|
|
138
91
|
room,
|
|
92
|
+
chatClient,
|
|
93
|
+
disposeChatClient = false,
|
|
139
94
|
agentName,
|
|
140
95
|
builder,
|
|
141
|
-
toolkit = "chat",
|
|
142
|
-
tool = "new_thread",
|
|
143
96
|
selectedThreadPath,
|
|
144
97
|
onThreadPathChanged,
|
|
145
98
|
onThreadResolved,
|
|
@@ -156,11 +109,30 @@ function NewChatThread({
|
|
|
156
109
|
const activeOperationRef = (0, import_react.useRef)(0);
|
|
157
110
|
const controlledThreadPath = selectedThreadPath !== void 0 ? normalizeThreadPath(selectedThreadPath) : void 0;
|
|
158
111
|
const activePath = controlledThreadPath ?? internalThreadPath;
|
|
112
|
+
const ownsChatClient = chatClient == null;
|
|
113
|
+
const activeChatClient = (0, import_react.useMemo)(
|
|
114
|
+
() => chatClient ?? new import_meshagent_agents.MessagingChatClient({ room, agentName }),
|
|
115
|
+
[agentName, chatClient, room]
|
|
116
|
+
);
|
|
117
|
+
const [clientVersion, setClientVersion] = (0, import_react.useState)(0);
|
|
159
118
|
(0, import_react.useEffect)(() => {
|
|
160
119
|
return () => {
|
|
161
120
|
activeOperationRef.current += 1;
|
|
162
121
|
};
|
|
163
122
|
}, []);
|
|
123
|
+
(0, import_react.useEffect)(() => {
|
|
124
|
+
void activeChatClient.start();
|
|
125
|
+
const handleChange = () => {
|
|
126
|
+
setClientVersion((current) => current + 1);
|
|
127
|
+
};
|
|
128
|
+
activeChatClient.addListener(handleChange);
|
|
129
|
+
return () => {
|
|
130
|
+
activeChatClient.removeListener(handleChange);
|
|
131
|
+
if (ownsChatClient || disposeChatClient) {
|
|
132
|
+
void activeChatClient.stop();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}, [activeChatClient, disposeChatClient, ownsChatClient]);
|
|
164
136
|
(0, import_react.useEffect)(() => {
|
|
165
137
|
if (controlledThreadPath === void 0) {
|
|
166
138
|
return;
|
|
@@ -202,46 +174,30 @@ function NewChatThread({
|
|
|
202
174
|
}
|
|
203
175
|
const operationId = activeOperationRef.current + 1;
|
|
204
176
|
activeOperationRef.current = operationId;
|
|
205
|
-
const initialTargetAgent = findTargetAgent(room, agentName);
|
|
206
|
-
setWaitingForAgent(initialTargetAgent === null);
|
|
207
|
-
setCreatingNewThread(initialTargetAgent !== null);
|
|
177
|
+
const initialTargetAgent = activeChatClient.agentParticipant() ?? findTargetAgent(room, agentName);
|
|
178
|
+
setWaitingForAgent(initialTargetAgent === null && chatClient == null);
|
|
179
|
+
setCreatingNewThread(initialTargetAgent !== null || chatClient != null);
|
|
208
180
|
setNewThreadError(null);
|
|
209
181
|
try {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
182
|
+
if (initialTargetAgent === null && chatClient == null) {
|
|
183
|
+
if (!(activeChatClient instanceof import_meshagent_agents.MessagingChatClient)) {
|
|
184
|
+
throw new Error("No online agent supports agent messages.");
|
|
185
|
+
}
|
|
186
|
+
await activeChatClient.waitForAgentParticipant({ waitKey: String(operationId) });
|
|
187
|
+
}
|
|
216
188
|
ensureOperationActive(operationId, activeOperationRef);
|
|
217
|
-
if (initialTargetAgent === null) {
|
|
189
|
+
if (initialTargetAgent === null && chatClient == null) {
|
|
218
190
|
setWaitingForAgent(false);
|
|
219
191
|
setCreatingNewThread(true);
|
|
220
192
|
}
|
|
221
|
-
await
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
operationId,
|
|
226
|
-
activeOperationRef
|
|
227
|
-
});
|
|
228
|
-
ensureOperationActive(operationId, activeOperationRef);
|
|
229
|
-
const response = await room.invoke({
|
|
230
|
-
participantId: targetAgent.id,
|
|
231
|
-
toolkit,
|
|
232
|
-
tool,
|
|
233
|
-
arguments: {
|
|
234
|
-
message: {
|
|
235
|
-
text,
|
|
236
|
-
attachments: newThreadAttachments.map((attachment) => ({ path: attachment.path }))
|
|
237
|
-
}
|
|
238
|
-
}
|
|
193
|
+
const result = await activeChatClient.startThread({
|
|
194
|
+
message: text,
|
|
195
|
+
attachments: newThreadAttachments.map((attachment) => attachment.path),
|
|
196
|
+
senderName: getParticipantName(room.localParticipant) || void 0
|
|
239
197
|
});
|
|
240
198
|
ensureOperationActive(operationId, activeOperationRef);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
const [threadPath, displayName] = parseThreadToolResult(toolkit, tool, response);
|
|
199
|
+
const threadPath = result.threadPath;
|
|
200
|
+
const displayName = null;
|
|
245
201
|
const normalizedPath = normalizeThreadPath(threadPath);
|
|
246
202
|
if (controlledThreadPath === void 0) {
|
|
247
203
|
setInternalThreadPath(normalizedPath);
|
|
@@ -262,7 +218,9 @@ function NewChatThread({
|
|
|
262
218
|
setNewThreadError(describeError(error));
|
|
263
219
|
}
|
|
264
220
|
}, [
|
|
221
|
+
activeChatClient,
|
|
265
222
|
agentName,
|
|
223
|
+
chatClient,
|
|
266
224
|
controlledThreadPath,
|
|
267
225
|
creatingNewThread,
|
|
268
226
|
newThreadAttachments,
|
|
@@ -270,8 +228,6 @@ function NewChatThread({
|
|
|
270
228
|
onThreadPathChanged,
|
|
271
229
|
onThreadResolved,
|
|
272
230
|
room,
|
|
273
|
-
toolkit,
|
|
274
|
-
tool,
|
|
275
231
|
waitingForAgent
|
|
276
232
|
]);
|
|
277
233
|
const targetAgentLabel = (0, import_react.useMemo)(() => {
|
|
@@ -279,9 +235,9 @@ function NewChatThread({
|
|
|
279
235
|
if (knownAgentName) {
|
|
280
236
|
return displayParticipantName(knownAgentName);
|
|
281
237
|
}
|
|
282
|
-
const targetAgent = findTargetAgent(room);
|
|
238
|
+
const targetAgent = activeChatClient.agentParticipant() ?? findTargetAgent(room);
|
|
283
239
|
return displayParticipantName(targetAgent ? getParticipantName(targetAgent) : null);
|
|
284
|
-
}, [agentName, room]);
|
|
240
|
+
}, [activeChatClient, agentName, clientVersion, room]);
|
|
285
241
|
const pendingStatusText = waitingForAgent ? `Waiting for ${targetAgentLabel} to be ready.` : creatingNewThread ? `Starting a thread with ${targetAgentLabel}.` : null;
|
|
286
242
|
if (activePath !== null) {
|
|
287
243
|
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;
|
|
@@ -23,6 +23,7 @@ __export(file_preview_exports, {
|
|
|
23
23
|
FilePreviewDialog: () => FilePreviewDialog,
|
|
24
24
|
ImagePreview: () => ImagePreview,
|
|
25
25
|
PdfPreview: () => PdfPreview,
|
|
26
|
+
SourcePreview: () => SourcePreview,
|
|
26
27
|
VideoPreview: () => VideoPreview,
|
|
27
28
|
classifyFile: () => classifyFile,
|
|
28
29
|
filePreviewName: () => filePreviewName,
|
|
@@ -33,15 +34,20 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
33
34
|
var import_react = require("react");
|
|
34
35
|
var import_lucide_react = require("lucide-react");
|
|
35
36
|
var import_react_pdf = require("react-pdf");
|
|
37
|
+
var import_react_syntax_highlighter = require("react-syntax-highlighter");
|
|
38
|
+
var import_prism = require("react-syntax-highlighter/dist/esm/styles/prism");
|
|
36
39
|
var import_dialog = require("../components/ui/dialog");
|
|
37
40
|
var import_button = require("../components/ui/button");
|
|
38
41
|
var import_spinner = require("../components/ui/spinner");
|
|
39
42
|
var import_utils = require("../lib/utils");
|
|
43
|
+
var import_chat_thread = require("../chat/chat-thread");
|
|
40
44
|
import_react_pdf.pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${import_react_pdf.pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
41
45
|
var FileKind = /* @__PURE__ */ ((FileKind2) => {
|
|
42
46
|
FileKind2["Image"] = "image";
|
|
43
47
|
FileKind2["Video"] = "video";
|
|
44
48
|
FileKind2["Pdf"] = "pdf";
|
|
49
|
+
FileKind2["Source"] = "source";
|
|
50
|
+
FileKind2["Thread"] = "thread";
|
|
45
51
|
FileKind2["Unknown"] = "unknown";
|
|
46
52
|
return FileKind2;
|
|
47
53
|
})(FileKind || {});
|
|
@@ -63,6 +69,123 @@ const imageExtensions = /* @__PURE__ */ new Set([
|
|
|
63
69
|
]);
|
|
64
70
|
const videoExtensions = /* @__PURE__ */ new Set(["m4v", "mkv", "mov", "mp4", "webm"]);
|
|
65
71
|
const pdfExtensions = /* @__PURE__ */ new Set(["pdf"]);
|
|
72
|
+
const sourceExtensions = /* @__PURE__ */ new Set([
|
|
73
|
+
"bash",
|
|
74
|
+
"c",
|
|
75
|
+
"cc",
|
|
76
|
+
"cfg",
|
|
77
|
+
"cmake",
|
|
78
|
+
"cpp",
|
|
79
|
+
"cs",
|
|
80
|
+
"css",
|
|
81
|
+
"csv",
|
|
82
|
+
"dart",
|
|
83
|
+
"diff",
|
|
84
|
+
"dockerfile",
|
|
85
|
+
"env",
|
|
86
|
+
"fish",
|
|
87
|
+
"go",
|
|
88
|
+
"gradle",
|
|
89
|
+
"graphql",
|
|
90
|
+
"h",
|
|
91
|
+
"hpp",
|
|
92
|
+
"htm",
|
|
93
|
+
"html",
|
|
94
|
+
"ini",
|
|
95
|
+
"java",
|
|
96
|
+
"js",
|
|
97
|
+
"json",
|
|
98
|
+
"jsx",
|
|
99
|
+
"kt",
|
|
100
|
+
"kts",
|
|
101
|
+
"lua",
|
|
102
|
+
"md",
|
|
103
|
+
"mjs",
|
|
104
|
+
"patch",
|
|
105
|
+
"php",
|
|
106
|
+
"proto",
|
|
107
|
+
"py",
|
|
108
|
+
"rb",
|
|
109
|
+
"rs",
|
|
110
|
+
"scss",
|
|
111
|
+
"sh",
|
|
112
|
+
"sql",
|
|
113
|
+
"swift",
|
|
114
|
+
"toml",
|
|
115
|
+
"ts",
|
|
116
|
+
"tsx",
|
|
117
|
+
"txt",
|
|
118
|
+
"xml",
|
|
119
|
+
"yaml",
|
|
120
|
+
"yml",
|
|
121
|
+
"zsh"
|
|
122
|
+
]);
|
|
123
|
+
const sourceFilenames = /* @__PURE__ */ new Set([
|
|
124
|
+
".dockerignore",
|
|
125
|
+
".env",
|
|
126
|
+
".gitignore",
|
|
127
|
+
"dockerfile",
|
|
128
|
+
"makefile"
|
|
129
|
+
]);
|
|
130
|
+
const sourceLanguagesByExtension = /* @__PURE__ */ new Map([
|
|
131
|
+
["bash", "bash"],
|
|
132
|
+
["c", "c"],
|
|
133
|
+
["cc", "cpp"],
|
|
134
|
+
["cfg", "ini"],
|
|
135
|
+
["cmake", "cmake"],
|
|
136
|
+
["cpp", "cpp"],
|
|
137
|
+
["cs", "csharp"],
|
|
138
|
+
["css", "css"],
|
|
139
|
+
["csv", "csv"],
|
|
140
|
+
["dart", "dart"],
|
|
141
|
+
["diff", "diff"],
|
|
142
|
+
["dockerfile", "docker"],
|
|
143
|
+
["env", "ini"],
|
|
144
|
+
["fish", "fish"],
|
|
145
|
+
["go", "go"],
|
|
146
|
+
["gradle", "gradle"],
|
|
147
|
+
["graphql", "graphql"],
|
|
148
|
+
["h", "c"],
|
|
149
|
+
["hpp", "cpp"],
|
|
150
|
+
["htm", "html"],
|
|
151
|
+
["html", "html"],
|
|
152
|
+
["ini", "ini"],
|
|
153
|
+
["java", "java"],
|
|
154
|
+
["js", "javascript"],
|
|
155
|
+
["json", "json"],
|
|
156
|
+
["jsx", "jsx"],
|
|
157
|
+
["kt", "kotlin"],
|
|
158
|
+
["kts", "kotlin"],
|
|
159
|
+
["lua", "lua"],
|
|
160
|
+
["md", "markdown"],
|
|
161
|
+
["mjs", "javascript"],
|
|
162
|
+
["patch", "diff"],
|
|
163
|
+
["php", "php"],
|
|
164
|
+
["proto", "protobuf"],
|
|
165
|
+
["py", "python"],
|
|
166
|
+
["rb", "ruby"],
|
|
167
|
+
["rs", "rust"],
|
|
168
|
+
["scss", "scss"],
|
|
169
|
+
["sh", "bash"],
|
|
170
|
+
["sql", "sql"],
|
|
171
|
+
["swift", "swift"],
|
|
172
|
+
["toml", "toml"],
|
|
173
|
+
["ts", "typescript"],
|
|
174
|
+
["tsx", "tsx"],
|
|
175
|
+
["txt", "text"],
|
|
176
|
+
["xml", "xml"],
|
|
177
|
+
["yaml", "yaml"],
|
|
178
|
+
["yml", "yaml"],
|
|
179
|
+
["zsh", "bash"]
|
|
180
|
+
]);
|
|
181
|
+
const sourceLanguagesByFilename = /* @__PURE__ */ new Map([
|
|
182
|
+
[".dockerignore", "docker"],
|
|
183
|
+
[".env", "ini"],
|
|
184
|
+
[".gitignore", "git"],
|
|
185
|
+
["dockerfile", "docker"],
|
|
186
|
+
["makefile", "makefile"]
|
|
187
|
+
]);
|
|
188
|
+
const threadExtensions = /* @__PURE__ */ new Set(["thread"]);
|
|
66
189
|
function basename(path) {
|
|
67
190
|
const withoutQuery = path.split("?")[0] ?? path;
|
|
68
191
|
const withoutHash = withoutQuery.split("#")[0] ?? withoutQuery;
|
|
@@ -94,6 +217,12 @@ function classifyFile(path) {
|
|
|
94
217
|
if (pdfExtensions.has(ext)) {
|
|
95
218
|
return "pdf" /* Pdf */;
|
|
96
219
|
}
|
|
220
|
+
if (sourceExtensions.has(ext) || sourceFilenames.has(filePreviewName(path).toLowerCase())) {
|
|
221
|
+
return "source" /* Source */;
|
|
222
|
+
}
|
|
223
|
+
if (threadExtensions.has(ext)) {
|
|
224
|
+
return "thread" /* Thread */;
|
|
225
|
+
}
|
|
97
226
|
return "unknown" /* Unknown */;
|
|
98
227
|
}
|
|
99
228
|
function isImagePath(path) {
|
|
@@ -165,6 +294,55 @@ function usePdfUrl(room, path) {
|
|
|
165
294
|
}, [path, room]);
|
|
166
295
|
return { url, error };
|
|
167
296
|
}
|
|
297
|
+
function sourceLanguage(path) {
|
|
298
|
+
const name = filePreviewName(path).toLowerCase();
|
|
299
|
+
const byFilename = sourceLanguagesByFilename.get(name);
|
|
300
|
+
if (byFilename != null) {
|
|
301
|
+
return byFilename;
|
|
302
|
+
}
|
|
303
|
+
return sourceLanguagesByExtension.get(extension(path)) ?? "text";
|
|
304
|
+
}
|
|
305
|
+
function useSourceText(room, path) {
|
|
306
|
+
const [text, setText] = (0, import_react.useState)(null);
|
|
307
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
308
|
+
(0, import_react.useEffect)(() => {
|
|
309
|
+
let cancelled = false;
|
|
310
|
+
const controller = new AbortController();
|
|
311
|
+
const normalizedPath = path.trim();
|
|
312
|
+
setText(null);
|
|
313
|
+
setError(null);
|
|
314
|
+
if (normalizedPath === "") {
|
|
315
|
+
return () => {
|
|
316
|
+
controller.abort();
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const loadText = async () => {
|
|
320
|
+
if (isHttpUrl(normalizedPath)) {
|
|
321
|
+
const response = await fetch(normalizedPath, { signal: controller.signal });
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
throw new Error(`HTTP ${response.status}`);
|
|
324
|
+
}
|
|
325
|
+
return await response.text();
|
|
326
|
+
}
|
|
327
|
+
const content = await room.storage.download(normalizedPath);
|
|
328
|
+
return new TextDecoder().decode(content.data);
|
|
329
|
+
};
|
|
330
|
+
void loadText().then((nextText) => {
|
|
331
|
+
if (!cancelled) {
|
|
332
|
+
setText(nextText);
|
|
333
|
+
}
|
|
334
|
+
}).catch((nextError) => {
|
|
335
|
+
if (!cancelled) {
|
|
336
|
+
setError(nextError);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
return () => {
|
|
340
|
+
cancelled = true;
|
|
341
|
+
controller.abort();
|
|
342
|
+
};
|
|
343
|
+
}, [path, room]);
|
|
344
|
+
return { text, error };
|
|
345
|
+
}
|
|
168
346
|
function ErrorPreview({ message }) {
|
|
169
347
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex h-full min-h-48 items-center justify-center p-6 text-center text-sm text-destructive", children: message });
|
|
170
348
|
}
|
|
@@ -276,6 +454,44 @@ function PdfPreview({ room, path }) {
|
|
|
276
454
|
}
|
|
277
455
|
) });
|
|
278
456
|
}
|
|
457
|
+
function SourcePreview({ room, path }) {
|
|
458
|
+
const { text, error } = useSourceText(room, path);
|
|
459
|
+
const language = (0, import_react.useMemo)(() => sourceLanguage(path), [path]);
|
|
460
|
+
if (error != null) {
|
|
461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorPreview, { message: `Unable to load source preview: ${String(error)}` });
|
|
462
|
+
}
|
|
463
|
+
if (text == null) {
|
|
464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingPreview, {});
|
|
465
|
+
}
|
|
466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-full overflow-auto bg-[#fafafa] text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
467
|
+
import_react_syntax_highlighter.Prism,
|
|
468
|
+
{
|
|
469
|
+
language,
|
|
470
|
+
style: import_prism.oneLight,
|
|
471
|
+
showLineNumbers: true,
|
|
472
|
+
wrapLongLines: true,
|
|
473
|
+
customStyle: {
|
|
474
|
+
margin: 0,
|
|
475
|
+
minHeight: "100%",
|
|
476
|
+
background: "transparent",
|
|
477
|
+
padding: "1rem"
|
|
478
|
+
},
|
|
479
|
+
codeTagProps: {
|
|
480
|
+
style: {
|
|
481
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
lineNumberStyle: {
|
|
485
|
+
minWidth: "2.75em",
|
|
486
|
+
paddingRight: "1em",
|
|
487
|
+
color: "var(--muted-foreground)",
|
|
488
|
+
textAlign: "right",
|
|
489
|
+
userSelect: "none"
|
|
490
|
+
},
|
|
491
|
+
children: text
|
|
492
|
+
}
|
|
493
|
+
) });
|
|
494
|
+
}
|
|
279
495
|
function UnsupportedPreview({ room, path }) {
|
|
280
496
|
const filename = filePreviewName(path);
|
|
281
497
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex h-full min-h-64 flex-col items-center justify-center gap-4 p-8 text-center", children: [
|
|
@@ -296,6 +512,10 @@ function FilePreview({ room, path }) {
|
|
|
296
512
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPreview, { room, path });
|
|
297
513
|
case "pdf" /* Pdf */:
|
|
298
514
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PdfPreview, { room, path });
|
|
515
|
+
case "source" /* Source */:
|
|
516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SourcePreview, { room, path });
|
|
517
|
+
case "thread" /* Thread */:
|
|
518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_chat_thread.ChatThread, { room, path });
|
|
299
519
|
case "unknown" /* Unknown */:
|
|
300
520
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnsupportedPreview, { room, path });
|
|
301
521
|
}
|
|
@@ -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;
|