@invergent/agent-chat-react 2.17.8 → 2.18.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.
- package/dist/index.cjs +228 -287
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +230 -287
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn, __publicField } from './chunk-47MF6JUW.js';
|
|
2
2
|
import { createContext, memo, useRef, useEffect, useCallback, useMemo, useState, useLayoutEffect, forwardRef, useId, lazy, useContext, useSyncExternalStore, Fragment as Fragment$1, Suspense, Children } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight as ChevronRight$1, RefreshCw, CreditCard, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, PlugZapIcon, ChevronRightIcon, ArrowLeftIcon, Loader2Icon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, Pen, Eraser, Type, MousePointer2, Hand, Maximize2, Undo2, Redo2, CheckIcon, CopyIcon, DownloadIcon, Maximize2Icon, LaptopIcon, GlobeIcon, FolderIcon, MessageSquareIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, ChevronLeftIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, TrashIcon,
|
|
4
|
+
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight as ChevronRight$1, RefreshCw, CreditCard, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, PlugZapIcon, ChevronRightIcon, ArrowLeftIcon, Loader2Icon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, Pen, Eraser, Type, MousePointer2, Hand, Maximize2, Undo2, Redo2, CheckIcon, CopyIcon, DownloadIcon, Maximize2Icon, LaptopIcon, GlobeIcon, FolderIcon, MessageSquareIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, ChevronLeftIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, TrashIcon, FileTextIcon, Minimize2Icon, Clock, ArrowRight, ListTreeIcon, PenLineIcon, TerminalIcon, SparklesIcon, ClockIcon, PaperclipIcon, CloudIcon, HardDriveIcon, IdCardIcon, PlusIcon, ShrinkIcon, FolderOpenIcon, FileIcon, ImageIcon, CircleDot, XCircle, CheckCircle2, SearchIcon, ZapIcon, ArrowUpIcon, CircleCheckIcon, ThumbsUpIcon, ThumbsDownIcon, BookOpenIcon, GitBranchIcon, ActivityIcon, EyeIcon, LibraryIcon, ListTodoIcon, BotIcon, ListIcon, FileEditIcon, WrenchIcon, FileCodeIcon, CodeIcon, FileArchiveIcon, FileVideoIcon, FileAudioIcon, FileImageIcon, FileChartLineIcon, FileSpreadsheetIcon, XCircleIcon, CheckCircle2Icon, Code, CircleXIcon, SkullIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
|
|
5
5
|
import { cva } from 'class-variance-authority';
|
|
6
6
|
import { Collapsible as Collapsible$1, Slot, Tooltip as Tooltip$1, Checkbox as Checkbox$1, Label as Label$1, Progress as Progress$1, Tabs as Tabs$1, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1 } from 'radix-ui';
|
|
7
7
|
import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
|
|
@@ -18,7 +18,7 @@ import AnsiImport from 'ansi-to-react';
|
|
|
18
18
|
import { Command as Command$1 } from 'cmdk';
|
|
19
19
|
import { nanoid } from 'nanoid';
|
|
20
20
|
import 'tokenlens';
|
|
21
|
-
import '
|
|
21
|
+
import { useTheme } from 'next-themes';
|
|
22
22
|
import { formatDistanceToNow } from 'date-fns';
|
|
23
23
|
|
|
24
24
|
var AgentChatAdapterContext = createContext(null);
|
|
@@ -12284,7 +12284,7 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
|
12284
12284
|
if (msg.systemKind === "worker" || msg.systemKind === "thread_proposal") {
|
|
12285
12285
|
return [{ kind: "thread_card", key: msg.id, message: msg }];
|
|
12286
12286
|
}
|
|
12287
|
-
if (msg.systemKind === "browser_marker" || msg.systemKind === "browser_marker_warning") {
|
|
12287
|
+
if (msg.systemKind === "browser_marker" || msg.systemKind === "browser_marker_warning" || msg.systemKind === "notice") {
|
|
12288
12288
|
return [{
|
|
12289
12289
|
kind: "browser_marker",
|
|
12290
12290
|
key: msg.id,
|
|
@@ -12495,7 +12495,7 @@ function OrphanSystemMarker({
|
|
|
12495
12495
|
}
|
|
12496
12496
|
);
|
|
12497
12497
|
}
|
|
12498
|
-
if (message.systemKind === "browser_marker" || message.systemKind === "browser_marker_warning") {
|
|
12498
|
+
if (message.systemKind === "browser_marker" || message.systemKind === "browser_marker_warning" || message.systemKind === "notice") {
|
|
12499
12499
|
const warning = message.systemKind === "browser_marker_warning";
|
|
12500
12500
|
return /* @__PURE__ */ jsxs(
|
|
12501
12501
|
"div",
|
|
@@ -13528,7 +13528,8 @@ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
13528
13528
|
"write_file",
|
|
13529
13529
|
"patch",
|
|
13530
13530
|
"execute_code",
|
|
13531
|
-
"browser_screenshot"
|
|
13531
|
+
"browser_screenshot",
|
|
13532
|
+
"office_edit"
|
|
13532
13533
|
]);
|
|
13533
13534
|
var AGENT_CHAT_LISTENED_EVENTS = [
|
|
13534
13535
|
"user.message",
|
|
@@ -13576,7 +13577,10 @@ var AGENT_CHAT_LISTENED_EVENTS = [
|
|
|
13576
13577
|
"worker.spawned",
|
|
13577
13578
|
"worker.complete",
|
|
13578
13579
|
"worker.failed",
|
|
13579
|
-
"thread.proposed"
|
|
13580
|
+
"thread.proposed",
|
|
13581
|
+
"office.conflict",
|
|
13582
|
+
"office.save_failed",
|
|
13583
|
+
"stream.live"
|
|
13580
13584
|
];
|
|
13581
13585
|
var AGENT_CHAT_LISTENED_EVENT_SET = new Set(
|
|
13582
13586
|
AGENT_CHAT_LISTENED_EVENTS
|
|
@@ -13821,7 +13825,10 @@ function createInitialAgentChatState(options = {}) {
|
|
|
13821
13825
|
browserTabs: [],
|
|
13822
13826
|
viewMode: options.viewMode ?? "simple",
|
|
13823
13827
|
researchSources: [],
|
|
13824
|
-
deviceWait: null
|
|
13828
|
+
deviceWait: null,
|
|
13829
|
+
officeEvents: {},
|
|
13830
|
+
live: false,
|
|
13831
|
+
officeAutoOpen: null
|
|
13825
13832
|
};
|
|
13826
13833
|
}
|
|
13827
13834
|
function applyAgentChatEvent(state, event) {
|
|
@@ -13839,6 +13846,9 @@ function applyAgentChatEvent(state, event) {
|
|
|
13839
13846
|
case "user.message":
|
|
13840
13847
|
// A project coordinator's follow-up to its thread: shown as the thread reads it, its header saying whose it is.
|
|
13841
13848
|
case "coordinator.message":
|
|
13849
|
+
if (event.type === "user.message" && PLATFORM_NOTES.has(stringValue2(event.data.synthetic))) {
|
|
13850
|
+
return nextState;
|
|
13851
|
+
}
|
|
13842
13852
|
return withMessages(
|
|
13843
13853
|
{ ...nextState, terminal: false, stopped: false, isRunning: true },
|
|
13844
13854
|
applyUserMessage(nextState.messages, event.eventId, event.data)
|
|
@@ -13981,6 +13991,21 @@ function applyAgentChatEvent(state, event) {
|
|
|
13981
13991
|
};
|
|
13982
13992
|
case "device.resumed":
|
|
13983
13993
|
return { ...nextState, deviceWait: null };
|
|
13994
|
+
case "office.conflict":
|
|
13995
|
+
case "office.save_failed": {
|
|
13996
|
+
const path = stringValue2(event.data.path);
|
|
13997
|
+
if (!path) return nextState;
|
|
13998
|
+
const conflict = event.type === "office.conflict";
|
|
13999
|
+
return {
|
|
14000
|
+
...nextState,
|
|
14001
|
+
officeEvents: {
|
|
14002
|
+
...nextState.officeEvents,
|
|
14003
|
+
[path]: conflict ? { type: event.type, eventId: event.eventId, conflictPath: stringValue2(event.data.conflict_path) } : { type: event.type, eventId: event.eventId }
|
|
14004
|
+
},
|
|
14005
|
+
// The copy the conflicting save landed as shows in the tree.
|
|
14006
|
+
workspaceRefreshKey: nextState.workspaceRefreshKey + (conflict ? 1 : 0)
|
|
14007
|
+
};
|
|
14008
|
+
}
|
|
13984
14009
|
case "worker.spawned":
|
|
13985
14010
|
return withMessages(nextState, applyWorkerSpawned(nextState.messages, event));
|
|
13986
14011
|
case "worker.complete":
|
|
@@ -13994,17 +14019,22 @@ function applyAgentChatEvent(state, event) {
|
|
|
13994
14019
|
return applyLlmResponse(nextState, event);
|
|
13995
14020
|
case "llm.thinking":
|
|
13996
14021
|
return applyLlmThinking(nextState, event);
|
|
14022
|
+
case "stream.live":
|
|
14023
|
+
return { ...nextState, live: true };
|
|
13997
14024
|
case "tool.call":
|
|
13998
14025
|
return applyToolCall(nextState, event);
|
|
13999
14026
|
case "tool.result": {
|
|
14000
14027
|
const toolCallId = stringValue2(event.data.tool_call_id);
|
|
14001
|
-
const toolName = findToolNameById(nextState.messages, toolCallId);
|
|
14002
14028
|
const messages = applyToolResult(nextState.messages, event.data);
|
|
14029
|
+
const call = findToolCallById(messages, toolCallId);
|
|
14030
|
+
const toolName = call?.toolName ?? null;
|
|
14003
14031
|
const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
|
|
14032
|
+
const officePath = nextState.live && call?.toolName === "office_edit" && !toolCallFailed(call) ? officeEditPath(call.args) : null;
|
|
14004
14033
|
const withResult = {
|
|
14005
14034
|
...nextState,
|
|
14006
14035
|
messages,
|
|
14007
|
-
workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
|
|
14036
|
+
workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey,
|
|
14037
|
+
officeAutoOpen: officePath ? { path: officePath, eventId: event.eventId } : nextState.officeAutoOpen
|
|
14008
14038
|
};
|
|
14009
14039
|
return collectResearchSource(withResult, toolName, event.data);
|
|
14010
14040
|
}
|
|
@@ -14141,6 +14171,7 @@ function typedBehindTheClear(messages, answers) {
|
|
|
14141
14171
|
}
|
|
14142
14172
|
return messages.slice(first);
|
|
14143
14173
|
}
|
|
14174
|
+
var PLATFORM_NOTES = /* @__PURE__ */ new Set(["checkin", "checkin_opener", "outreach_opener"]);
|
|
14144
14175
|
function applyUserMessage(messages, eventId, data) {
|
|
14145
14176
|
const content = stringValue2(data.content);
|
|
14146
14177
|
const images = parseUserMessageImages(data.images);
|
|
@@ -14206,6 +14237,22 @@ function parseUserMessageAttachments(raw) {
|
|
|
14206
14237
|
}
|
|
14207
14238
|
return out.length > 0 ? out : void 0;
|
|
14208
14239
|
}
|
|
14240
|
+
function officeEditPath(args) {
|
|
14241
|
+
try {
|
|
14242
|
+
const path = JSON.parse(args)?.path;
|
|
14243
|
+
return typeof path === "string" ? path.replace(/^__WORKSPACE__\//, "") || null : null;
|
|
14244
|
+
} catch {
|
|
14245
|
+
return null;
|
|
14246
|
+
}
|
|
14247
|
+
}
|
|
14248
|
+
function toolCallFailed(call) {
|
|
14249
|
+
if (call.status === "error") return true;
|
|
14250
|
+
try {
|
|
14251
|
+
return JSON.parse(call.result ?? "")?.error != null;
|
|
14252
|
+
} catch {
|
|
14253
|
+
return false;
|
|
14254
|
+
}
|
|
14255
|
+
}
|
|
14209
14256
|
function onComputer(event) {
|
|
14210
14257
|
return event.data.computer === true ? { computer: true } : {};
|
|
14211
14258
|
}
|
|
@@ -14321,6 +14368,21 @@ function applyLlmResponse(state, event) {
|
|
|
14321
14368
|
const messages = [...state.messages];
|
|
14322
14369
|
const message = objectValue(event.data.message);
|
|
14323
14370
|
const responseContent = stringValue2(message?.content) || stringValue2(event.data.content);
|
|
14371
|
+
if (event.data.notice === true) {
|
|
14372
|
+
return {
|
|
14373
|
+
...state,
|
|
14374
|
+
messages: [...messages, {
|
|
14375
|
+
id: `evt-${event.eventId}`,
|
|
14376
|
+
role: "system",
|
|
14377
|
+
systemKind: "notice",
|
|
14378
|
+
content: responseContent,
|
|
14379
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
14380
|
+
status: "complete"
|
|
14381
|
+
}],
|
|
14382
|
+
hadDeltas: false,
|
|
14383
|
+
isRunning: false
|
|
14384
|
+
};
|
|
14385
|
+
}
|
|
14324
14386
|
const toolCalls = Array.isArray(message?.tool_calls) ? message?.tool_calls : [];
|
|
14325
14387
|
const hasToolCalls = toolCalls.length > 0;
|
|
14326
14388
|
const responseToolCallIds = toolCallIdsFromResponse(toolCalls);
|
|
@@ -15045,11 +15107,11 @@ function hasUserAfterIndex(messages, idx) {
|
|
|
15045
15107
|
}
|
|
15046
15108
|
return false;
|
|
15047
15109
|
}
|
|
15048
|
-
function
|
|
15110
|
+
function findToolCallById(messages, toolCallId) {
|
|
15049
15111
|
if (!toolCallId) return null;
|
|
15050
15112
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
15051
15113
|
const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
|
|
15052
|
-
if (tc) return tc
|
|
15114
|
+
if (tc) return tc;
|
|
15053
15115
|
}
|
|
15054
15116
|
return null;
|
|
15055
15117
|
}
|
|
@@ -15230,7 +15292,7 @@ function useAgentChatRuntime({
|
|
|
15230
15292
|
stream.addEventListener(eventType, (messageEvent) => {
|
|
15231
15293
|
if (streamRef.current !== stream) return;
|
|
15232
15294
|
const data = parseEventData(messageEvent.data);
|
|
15233
|
-
const eventId = messageEvent.lastEventId ? Number(messageEvent.lastEventId) : 0;
|
|
15295
|
+
const eventId = eventType === "stream.live" ? 0 : messageEvent.lastEventId ? Number(messageEvent.lastEventId) : 0;
|
|
15234
15296
|
setState(
|
|
15235
15297
|
(prev) => applyRuntimeEvent(prev, {
|
|
15236
15298
|
type: eventType,
|
|
@@ -19027,11 +19089,41 @@ function chunkPairs(flat) {
|
|
|
19027
19089
|
for (let i = 0; i + 1 < flat.length; i += 2) pairs.push([flat[i], flat[i + 1]]);
|
|
19028
19090
|
return pairs;
|
|
19029
19091
|
}
|
|
19092
|
+
|
|
19093
|
+
// src/lib/office.ts
|
|
19094
|
+
var OFFICE_EXTENSIONS = /* @__PURE__ */ new Set(["docx", "xlsx", "pptx", "pdf", "doc", "odt", "rtf", "xls", "ods", "ppt", "odp"]);
|
|
19095
|
+
function isOfficeDocument(path) {
|
|
19096
|
+
const dot = path.lastIndexOf(".");
|
|
19097
|
+
return dot >= 0 && OFFICE_EXTENSIONS.has(path.slice(dot + 1).toLowerCase());
|
|
19098
|
+
}
|
|
19099
|
+
var loads = /* @__PURE__ */ new Map();
|
|
19100
|
+
var LOAD_TIMEOUT_MS = 3e4;
|
|
19101
|
+
function loadDocsApi(documentServerUrl) {
|
|
19102
|
+
const src = `${documentServerUrl.replace(/\/$/, "")}/web-apps/apps/api/documents/api.js`;
|
|
19103
|
+
let load = loads.get(src);
|
|
19104
|
+
if (!load) {
|
|
19105
|
+
load = new Promise((resolve, reject) => {
|
|
19106
|
+
const script = document.createElement("script");
|
|
19107
|
+
const settle = (loaded) => {
|
|
19108
|
+
clearTimeout(timer);
|
|
19109
|
+
script.onload = script.onerror = null;
|
|
19110
|
+
if (loaded) return resolve();
|
|
19111
|
+
loads.delete(src);
|
|
19112
|
+
script.remove();
|
|
19113
|
+
reject(new Error("The document editor could not be loaded."));
|
|
19114
|
+
};
|
|
19115
|
+
const timer = setTimeout(() => settle(false), LOAD_TIMEOUT_MS);
|
|
19116
|
+
script.src = src;
|
|
19117
|
+
script.async = true;
|
|
19118
|
+
script.onload = () => settle(Boolean(window.DocsAPI));
|
|
19119
|
+
script.onerror = () => settle(false);
|
|
19120
|
+
document.head.appendChild(script);
|
|
19121
|
+
});
|
|
19122
|
+
loads.set(src, load);
|
|
19123
|
+
}
|
|
19124
|
+
return load;
|
|
19125
|
+
}
|
|
19030
19126
|
var SKELETON_WIDTHS = [70, 85, 55, 90, 60, 78, 45, 82, 65, 72];
|
|
19031
|
-
var PDF_WORKER_SRC = new URL(
|
|
19032
|
-
"pdfjs-dist/legacy/build/pdf.worker.mjs",
|
|
19033
|
-
import.meta.url
|
|
19034
|
-
).toString();
|
|
19035
19127
|
function FileViewer({
|
|
19036
19128
|
file,
|
|
19037
19129
|
loading,
|
|
@@ -19046,8 +19138,8 @@ function FileViewer({
|
|
|
19046
19138
|
if (!visible) return null;
|
|
19047
19139
|
const fileName2 = file?.path.split("/").pop() ?? "File";
|
|
19048
19140
|
const lang = file ? getLanguageHint(file.path) : "";
|
|
19049
|
-
const
|
|
19050
|
-
const
|
|
19141
|
+
const isImage = file?.encoding === "base64" && (file.mime_type?.startsWith("image/") ?? false);
|
|
19142
|
+
const isBinary = file?.encoding === "base64" && !isImage;
|
|
19051
19143
|
const HeaderIcon = isImage ? ImageIcon : FileTextIcon;
|
|
19052
19144
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col border-t border-line", className), children: [
|
|
19053
19145
|
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2 border-b border-line px-3 py-2", children: [
|
|
@@ -19095,12 +19187,7 @@ function FileViewer({
|
|
|
19095
19187
|
}
|
|
19096
19188
|
)
|
|
19097
19189
|
] }),
|
|
19098
|
-
|
|
19099
|
-
// PDF has its own scrollable container — bypass ScrollArea so
|
|
19100
|
-
// it can grow with the parent's flex height (Radix Viewport's
|
|
19101
|
-
// internal table wrapper sizes to content and breaks h-full).
|
|
19102
|
-
/* @__PURE__ */ jsx(PdfPreview, { file, fileName: fileName2 })
|
|
19103
|
-
) : /* @__PURE__ */ jsxs(ScrollArea, { className: "min-h-0 flex-1", children: [
|
|
19190
|
+
/* @__PURE__ */ jsxs(ScrollArea, { className: "min-h-0 flex-1", children: [
|
|
19104
19191
|
loading && /* @__PURE__ */ jsx("div", { className: "space-y-1.5 p-3", children: Array.from({ length: 10 }).map((_, index) => /* @__PURE__ */ jsx(
|
|
19105
19192
|
Skeleton,
|
|
19106
19193
|
{
|
|
@@ -19115,7 +19202,7 @@ function FileViewer({
|
|
|
19115
19202
|
/* @__PURE__ */ jsx(AlertCircleIcon, { className: "mt-0.5 size-3.5 shrink-0" }),
|
|
19116
19203
|
/* @__PURE__ */ jsx("span", { children: error })
|
|
19117
19204
|
] }),
|
|
19118
|
-
file &&
|
|
19205
|
+
file && isBinary && /* @__PURE__ */ jsx(FileViewerError, { message: "Preview not available. Download the file to open it." }),
|
|
19119
19206
|
file && isImage && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-4", children: /* @__PURE__ */ jsx(
|
|
19120
19207
|
"img",
|
|
19121
19208
|
{
|
|
@@ -19124,7 +19211,7 @@ function FileViewer({
|
|
|
19124
19211
|
className: "max-h-[60vh] max-w-full rounded object-contain"
|
|
19125
19212
|
}
|
|
19126
19213
|
) }),
|
|
19127
|
-
file && !isImage && !
|
|
19214
|
+
file && !isImage && !isBinary && /* @__PURE__ */ jsx("pre", { className: "wrap-break-word whitespace-pre-wrap p-3 text-base leading-relaxed text-foreground", children: /* @__PURE__ */ jsx("code", { "data-language": lang, children: file.content }) })
|
|
19128
19215
|
] })
|
|
19129
19216
|
] });
|
|
19130
19217
|
}
|
|
@@ -19134,277 +19221,113 @@ function FileViewerError({ message }) {
|
|
|
19134
19221
|
/* @__PURE__ */ jsx("span", { children: message })
|
|
19135
19222
|
] });
|
|
19136
19223
|
}
|
|
19137
|
-
function
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
const containerRef = useRef(null);
|
|
19142
|
-
const viewerRef = useRef(null);
|
|
19143
|
-
const pdfViewerRef = useRef(null);
|
|
19144
|
-
const eventBusRef = useRef(null);
|
|
19145
|
-
const findControllerRef = useRef(null);
|
|
19224
|
+
function OfficeEditorPane({ adapter, sessionId, path, view, officeEvent, onClose }) {
|
|
19225
|
+
const { resolvedTheme } = useTheme();
|
|
19226
|
+
const theme = resolvedTheme === "dark" ? "dark" : "light";
|
|
19227
|
+
const placeholderId = `office-editor-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
19146
19228
|
const [error, setError] = useState(null);
|
|
19147
|
-
const [
|
|
19148
|
-
const [
|
|
19149
|
-
const
|
|
19150
|
-
const [
|
|
19229
|
+
const [loading, setLoading] = useState(true);
|
|
19230
|
+
const [openedAfter] = useState(() => officeEvent?.eventId ?? 0);
|
|
19231
|
+
const notice = officeEvent && officeEvent.eventId > openedAfter ? officeEvent : null;
|
|
19232
|
+
const [reopenedFor, setReopenedFor] = useState(0);
|
|
19233
|
+
if (notice?.type === "office.conflict" && notice.eventId !== reopenedFor) setReopenedFor(notice.eventId);
|
|
19234
|
+
const paneRef = useRef(null);
|
|
19235
|
+
const [fullScreen, setFullScreen] = useState(false);
|
|
19236
|
+
useEffect(() => {
|
|
19237
|
+
const follow = () => setFullScreen(document.fullscreenElement === paneRef.current);
|
|
19238
|
+
document.addEventListener("fullscreenchange", follow);
|
|
19239
|
+
return () => document.removeEventListener("fullscreenchange", follow);
|
|
19240
|
+
}, []);
|
|
19151
19241
|
useEffect(() => {
|
|
19152
19242
|
let cancelled = false;
|
|
19153
|
-
let
|
|
19154
|
-
|
|
19155
|
-
|
|
19156
|
-
|
|
19157
|
-
|
|
19158
|
-
|
|
19159
|
-
|
|
19160
|
-
|
|
19161
|
-
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
if (cancelled) return;
|
|
19169
|
-
const eventBus = new pdfViewerModule.EventBus();
|
|
19170
|
-
const linkService = new pdfViewerModule.PDFLinkService({ eventBus });
|
|
19171
|
-
const findController = new pdfViewerModule.PDFFindController({
|
|
19172
|
-
eventBus,
|
|
19173
|
-
linkService
|
|
19174
|
-
});
|
|
19175
|
-
const pdfViewer = new pdfViewerModule.PDFViewer({
|
|
19176
|
-
container,
|
|
19177
|
-
viewer: viewerElement,
|
|
19178
|
-
eventBus,
|
|
19179
|
-
linkService,
|
|
19180
|
-
findController,
|
|
19181
|
-
removePageBorders: true
|
|
19182
|
-
});
|
|
19183
|
-
linkService.setViewer(pdfViewer);
|
|
19184
|
-
linkService.setDocument(pdf);
|
|
19185
|
-
findController.setDocument(pdf);
|
|
19186
|
-
const onPagesInit = () => {
|
|
19187
|
-
pdfViewer.currentScaleValue = "page-width";
|
|
19188
|
-
setScale(pdfViewer.currentScale || 1);
|
|
19189
|
-
setPagesCount(pdfViewer.pagesCount);
|
|
19190
|
-
setPageNumber(pdfViewer.currentPageNumber);
|
|
19191
|
-
};
|
|
19192
|
-
const onPageChanging = (event) => {
|
|
19193
|
-
if (typeof event.pageNumber === "number") {
|
|
19194
|
-
setPageNumber(event.pageNumber);
|
|
19195
|
-
}
|
|
19196
|
-
};
|
|
19197
|
-
const onScaleChanging = (event) => {
|
|
19198
|
-
if (typeof event.scale === "number") {
|
|
19199
|
-
setScale(event.scale);
|
|
19200
|
-
}
|
|
19201
|
-
};
|
|
19202
|
-
eventBus.on("pagesinit", onPagesInit);
|
|
19203
|
-
eventBus.on("pagechanging", onPageChanging);
|
|
19204
|
-
eventBus.on("scalechanging", onScaleChanging);
|
|
19205
|
-
pdfViewerRef.current = pdfViewer;
|
|
19206
|
-
eventBusRef.current = eventBus;
|
|
19207
|
-
findControllerRef.current = findController;
|
|
19208
|
-
setPagesCount(pdf.numPages);
|
|
19209
|
-
pdfViewer.setDocument(pdf);
|
|
19210
|
-
};
|
|
19211
|
-
void renderPdf().catch((nextError) => {
|
|
19212
|
-
if (cancelled) return;
|
|
19213
|
-
if (nextError instanceof Error && nextError.name === "RenderingCancelledException") {
|
|
19214
|
-
return;
|
|
19243
|
+
let editor = null;
|
|
19244
|
+
setError(null);
|
|
19245
|
+
setLoading(true);
|
|
19246
|
+
void (async () => {
|
|
19247
|
+
try {
|
|
19248
|
+
if (!adapter.getWorkspaceEditorConfig) throw new Error("Preview not available. Download the file to open it.");
|
|
19249
|
+
const opened = await adapter.getWorkspaceEditorConfig({ sessionId, path, theme, view });
|
|
19250
|
+
await loadDocsApi(opened.document_server_url);
|
|
19251
|
+
if (cancelled) return;
|
|
19252
|
+
if (!window.DocsAPI) throw new Error("The document editor could not be loaded.");
|
|
19253
|
+
editor = new window.DocsAPI.DocEditor(placeholderId, opened.config);
|
|
19254
|
+
} catch (openError) {
|
|
19255
|
+
if (!cancelled) setError(openError.message);
|
|
19256
|
+
} finally {
|
|
19257
|
+
if (!cancelled) setLoading(false);
|
|
19215
19258
|
}
|
|
19216
|
-
|
|
19217
|
-
});
|
|
19259
|
+
})();
|
|
19218
19260
|
return () => {
|
|
19219
19261
|
cancelled = true;
|
|
19220
|
-
|
|
19221
|
-
pdfDocument?.destroy();
|
|
19222
|
-
pdfViewerRef.current?.cleanup();
|
|
19223
|
-
pdfViewerRef.current = null;
|
|
19224
|
-
eventBusRef.current = null;
|
|
19225
|
-
findControllerRef.current = null;
|
|
19262
|
+
editor?.destroyEditor();
|
|
19226
19263
|
};
|
|
19227
|
-
}, [
|
|
19228
|
-
const
|
|
19229
|
-
|
|
19230
|
-
|
|
19231
|
-
|
|
19232
|
-
|
|
19233
|
-
|
|
19234
|
-
|
|
19235
|
-
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
|
|
19240
|
-
|
|
19241
|
-
|
|
19242
|
-
|
|
19243
|
-
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19276
|
-
|
|
19277
|
-
{
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19281
|
-
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
),
|
|
19287
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
19288
|
-
"/ ",
|
|
19289
|
-
pagesCount || "-"
|
|
19290
|
-
] }),
|
|
19291
|
-
/* @__PURE__ */ jsx(
|
|
19292
|
-
Button,
|
|
19293
|
-
{
|
|
19294
|
-
type: "button",
|
|
19295
|
-
variant: "ghost",
|
|
19296
|
-
size: "icon-xs",
|
|
19297
|
-
onClick: () => setViewerPage(pageNumber + 1),
|
|
19298
|
-
disabled: pagesCount > 0 && pageNumber >= pagesCount,
|
|
19299
|
-
"aria-label": "Next PDF page",
|
|
19300
|
-
title: "Next page",
|
|
19301
|
-
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3.5" })
|
|
19302
|
-
}
|
|
19303
|
-
),
|
|
19304
|
-
/* @__PURE__ */ jsx("div", { className: "mx-1 h-5 w-px bg-line" }),
|
|
19305
|
-
/* @__PURE__ */ jsx(
|
|
19306
|
-
Button,
|
|
19307
|
-
{
|
|
19308
|
-
type: "button",
|
|
19309
|
-
variant: "ghost",
|
|
19310
|
-
size: "icon-xs",
|
|
19311
|
-
onClick: () => setViewerScale(scale - 0.1),
|
|
19312
|
-
disabled: scale <= 0.5,
|
|
19313
|
-
"aria-label": "Zoom out PDF",
|
|
19314
|
-
title: "Zoom out",
|
|
19315
|
-
children: /* @__PURE__ */ jsx(MinusIcon, { className: "size-3.5" })
|
|
19316
|
-
}
|
|
19317
|
-
),
|
|
19318
|
-
/* @__PURE__ */ jsxs(
|
|
19319
|
-
"button",
|
|
19320
|
-
{
|
|
19321
|
-
type: "button",
|
|
19322
|
-
className: "h-7 min-w-12 px-1 text-xs text-muted-foreground hover:text-foreground",
|
|
19323
|
-
onClick: () => {
|
|
19324
|
-
const viewer = pdfViewerRef.current;
|
|
19325
|
-
if (!viewer) return;
|
|
19326
|
-
viewer.currentScaleValue = "page-width";
|
|
19327
|
-
setScale(viewer.currentScale || 1);
|
|
19328
|
-
},
|
|
19329
|
-
"aria-label": "Fit PDF to width",
|
|
19330
|
-
title: "Fit width",
|
|
19331
|
-
children: [
|
|
19332
|
-
Math.round(scale * 100),
|
|
19333
|
-
"%"
|
|
19334
|
-
]
|
|
19335
|
-
}
|
|
19336
|
-
),
|
|
19337
|
-
/* @__PURE__ */ jsx(
|
|
19338
|
-
Button,
|
|
19339
|
-
{
|
|
19340
|
-
type: "button",
|
|
19341
|
-
variant: "ghost",
|
|
19342
|
-
size: "icon-xs",
|
|
19343
|
-
onClick: () => setViewerScale(scale + 0.1),
|
|
19344
|
-
disabled: scale >= 3,
|
|
19345
|
-
"aria-label": "Zoom in PDF",
|
|
19346
|
-
title: "Zoom in",
|
|
19347
|
-
children: /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" })
|
|
19348
|
-
}
|
|
19349
|
-
),
|
|
19350
|
-
/* @__PURE__ */ jsx("div", { className: "mx-1 h-5 w-px bg-line" }),
|
|
19351
|
-
/* @__PURE__ */ jsx(SearchIcon, { className: "size-3.5 text-muted-foreground" }),
|
|
19352
|
-
/* @__PURE__ */ jsx(
|
|
19353
|
-
Input,
|
|
19354
|
-
{
|
|
19355
|
-
type: "search",
|
|
19356
|
-
value: query,
|
|
19357
|
-
onChange: (event) => setQuery(event.target.value),
|
|
19358
|
-
onKeyDown: (event) => {
|
|
19359
|
-
if (event.key === "Enter") runFind(event.shiftKey);
|
|
19360
|
-
},
|
|
19361
|
-
placeholder: "Find",
|
|
19362
|
-
"aria-label": "Find in PDF",
|
|
19363
|
-
className: "h-7 w-28 border-input px-1 text-xs"
|
|
19364
|
-
}
|
|
19365
|
-
),
|
|
19366
|
-
/* @__PURE__ */ jsx(
|
|
19367
|
-
Button,
|
|
19368
|
-
{
|
|
19369
|
-
type: "button",
|
|
19370
|
-
variant: "ghost",
|
|
19371
|
-
size: "xs",
|
|
19372
|
-
onClick: () => runFind(false),
|
|
19373
|
-
disabled: !query.trim(),
|
|
19374
|
-
children: "Find"
|
|
19375
|
-
}
|
|
19376
|
-
)
|
|
19377
|
-
] }),
|
|
19378
|
-
error && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-start gap-2 p-3 text-xs text-destructive", children: [
|
|
19379
|
-
/* @__PURE__ */ jsx(AlertCircleIcon, { className: "mt-0.5 size-3.5 shrink-0" }),
|
|
19380
|
-
/* @__PURE__ */ jsx("span", { children: error })
|
|
19381
|
-
] }),
|
|
19382
|
-
!error && /* @__PURE__ */ jsx("div", { className: "relative min-h-105 flex-1 bg-muted/20", children: /* @__PURE__ */ jsx(
|
|
19383
|
-
"div",
|
|
19384
|
-
{
|
|
19385
|
-
ref: containerRef,
|
|
19386
|
-
"aria-label": `PDF viewer for ${fileName2}`,
|
|
19387
|
-
className: "absolute inset-0 overflow-auto",
|
|
19388
|
-
children: /* @__PURE__ */ jsx("div", { ref: viewerRef, className: "pdfViewer" })
|
|
19389
|
-
}
|
|
19390
|
-
) })
|
|
19391
|
-
] });
|
|
19392
|
-
}
|
|
19393
|
-
function decodeBase64(content) {
|
|
19394
|
-
const binary = globalThis.atob(content);
|
|
19395
|
-
const bytes = new Uint8Array(binary.length);
|
|
19396
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
19397
|
-
bytes[index] = binary.charCodeAt(index);
|
|
19398
|
-
}
|
|
19399
|
-
return bytes;
|
|
19264
|
+
}, [adapter, sessionId, path, theme, view, placeholderId, reopenedFor]);
|
|
19265
|
+
const fileName2 = path.split("/").pop() ?? path;
|
|
19266
|
+
return /* @__PURE__ */ jsxs(
|
|
19267
|
+
"aside",
|
|
19268
|
+
{
|
|
19269
|
+
ref: paneRef,
|
|
19270
|
+
"data-testid": "file-preview-panel",
|
|
19271
|
+
className: "flex h-full min-h-0 w-full flex-col overflow-hidden border-l border-line bg-card",
|
|
19272
|
+
children: [
|
|
19273
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2 border-b border-line px-3 py-2", children: [
|
|
19274
|
+
/* @__PURE__ */ jsx(FileTextIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
19275
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 truncate font-medium", children: fileName2 }),
|
|
19276
|
+
/* @__PURE__ */ jsx(
|
|
19277
|
+
"a",
|
|
19278
|
+
{
|
|
19279
|
+
href: adapter.getWorkspaceDownloadUrl({ sessionId, path }),
|
|
19280
|
+
download: fileName2,
|
|
19281
|
+
"aria-label": `Download ${fileName2}`,
|
|
19282
|
+
title: "Download",
|
|
19283
|
+
className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
19284
|
+
children: /* @__PURE__ */ jsx(DownloadIcon, { className: "size-4" })
|
|
19285
|
+
}
|
|
19286
|
+
),
|
|
19287
|
+
/* @__PURE__ */ jsx(
|
|
19288
|
+
"button",
|
|
19289
|
+
{
|
|
19290
|
+
type: "button",
|
|
19291
|
+
onClick: () => void (fullScreen ? document.exitFullscreen() : paneRef.current?.requestFullscreen()),
|
|
19292
|
+
"aria-label": fullScreen ? "Exit full screen" : "Full screen",
|
|
19293
|
+
title: fullScreen ? "Exit full screen" : "Full screen",
|
|
19294
|
+
className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
19295
|
+
children: fullScreen ? /* @__PURE__ */ jsx(Minimize2Icon, { className: "size-4" }) : /* @__PURE__ */ jsx(Maximize2Icon, { className: "size-4" })
|
|
19296
|
+
}
|
|
19297
|
+
),
|
|
19298
|
+
/* @__PURE__ */ jsx(
|
|
19299
|
+
"button",
|
|
19300
|
+
{
|
|
19301
|
+
type: "button",
|
|
19302
|
+
onClick: onClose,
|
|
19303
|
+
"aria-label": "Close file",
|
|
19304
|
+
className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
19305
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-4" })
|
|
19306
|
+
}
|
|
19307
|
+
)
|
|
19308
|
+
] }),
|
|
19309
|
+
error && /* @__PURE__ */ jsxs("div", { role: "alert", className: "flex items-start gap-2 p-3 text-xs text-destructive", children: [
|
|
19310
|
+
/* @__PURE__ */ jsx(AlertCircleIcon, { className: "mt-0.5 size-3.5 shrink-0" }),
|
|
19311
|
+
/* @__PURE__ */ jsx("span", { children: error })
|
|
19312
|
+
] }),
|
|
19313
|
+
notice && /* @__PURE__ */ jsx("div", { role: "status", className: "border-b border-line px-3 py-2 text-xs text-muted-foreground", children: notice.type === "office.conflict" ? `This document changed outside the editor. Your edits were saved to ${notice.conflictPath}.` : "Your latest changes could not be saved yet." }),
|
|
19314
|
+
loading && !error && /* @__PURE__ */ jsxs("div", { className: "p-3 text-sm text-muted-foreground", children: [
|
|
19315
|
+
"Opening ",
|
|
19316
|
+
fileName2,
|
|
19317
|
+
"\u2026"
|
|
19318
|
+
] }),
|
|
19319
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx("div", { id: placeholderId }) })
|
|
19320
|
+
]
|
|
19321
|
+
}
|
|
19322
|
+
);
|
|
19400
19323
|
}
|
|
19401
|
-
function
|
|
19402
|
-
if (
|
|
19403
|
-
return
|
|
19324
|
+
function WorkspaceFileDrawer({ adapter, sessionId, path, view = false, officeEvent, onClose }) {
|
|
19325
|
+
if (isOfficeDocument(path)) {
|
|
19326
|
+
return /* @__PURE__ */ jsx(OfficeEditorPane, { adapter, sessionId, path, view, officeEvent, onClose });
|
|
19404
19327
|
}
|
|
19405
|
-
return
|
|
19328
|
+
return /* @__PURE__ */ jsx(WorkspaceFilePreview, { adapter, sessionId, path, onClose });
|
|
19406
19329
|
}
|
|
19407
|
-
function
|
|
19330
|
+
function WorkspaceFilePreview({
|
|
19408
19331
|
adapter,
|
|
19409
19332
|
sessionId,
|
|
19410
19333
|
path,
|
|
@@ -20136,10 +20059,16 @@ function isSubAgentSession(session) {
|
|
|
20136
20059
|
if (session.channel && _SUB_AGENT_CHANNELS.has(session.channel)) return true;
|
|
20137
20060
|
return false;
|
|
20138
20061
|
}
|
|
20062
|
+
function isProjectThread(session) {
|
|
20063
|
+
return session?.config?.workstream_role === "thread";
|
|
20064
|
+
}
|
|
20139
20065
|
function readOnlyReasonForSession(session) {
|
|
20140
20066
|
if (isScheduledRunSession(session)) {
|
|
20141
20067
|
return { readOnly: true, reason: "Scheduled run is read-only" };
|
|
20142
20068
|
}
|
|
20069
|
+
if (isProjectThread(session)) {
|
|
20070
|
+
return { readOnly: false };
|
|
20071
|
+
}
|
|
20143
20072
|
if (isSubAgentSession(session)) {
|
|
20144
20073
|
return {
|
|
20145
20074
|
readOnly: true,
|
|
@@ -20209,11 +20138,13 @@ function AgentChat({
|
|
|
20209
20138
|
setOpenPane(null);
|
|
20210
20139
|
setFilesOpen(false);
|
|
20211
20140
|
}, [runtime.viewMode]);
|
|
20141
|
+
const officeAutoOpened = useRef(/* @__PURE__ */ new Set());
|
|
20212
20142
|
useEffect(() => {
|
|
20213
20143
|
setFilesOpen(false);
|
|
20214
20144
|
setWorkspacePath(null);
|
|
20215
20145
|
setPreviewPath(null);
|
|
20216
20146
|
setOpenPane(null);
|
|
20147
|
+
officeAutoOpened.current.clear();
|
|
20217
20148
|
}, [sessionId]);
|
|
20218
20149
|
const readOnly = readOnlyReasonForSession(runtime.session);
|
|
20219
20150
|
const boardAvailable = whiteboardEnabled && isBoardSession(runtime.session);
|
|
@@ -20296,6 +20227,16 @@ function AgentChat({
|
|
|
20296
20227
|
setOpenPane("file");
|
|
20297
20228
|
setMobileView("file");
|
|
20298
20229
|
}, []);
|
|
20230
|
+
const officeAutoOpen = runtime.state.officeAutoOpen;
|
|
20231
|
+
useEffect(() => {
|
|
20232
|
+
if (!officeAutoOpen || !isOfficeDocument(officeAutoOpen.path)) return;
|
|
20233
|
+
if (openPane === "file" && previewPath !== null && previewPath !== officeAutoOpen.path) return;
|
|
20234
|
+
if (officeAutoOpened.current.has(officeAutoOpen.path)) return;
|
|
20235
|
+
officeAutoOpened.current.add(officeAutoOpen.path);
|
|
20236
|
+
setWorkspacePath(officeAutoOpen.path);
|
|
20237
|
+
setPreviewPath(officeAutoOpen.path);
|
|
20238
|
+
setOpenPane("file");
|
|
20239
|
+
}, [officeAutoOpen?.eventId]);
|
|
20299
20240
|
const handleSelectedPathChange = useCallback((path) => {
|
|
20300
20241
|
setWorkspacePath(path);
|
|
20301
20242
|
if (path === null) setPreviewPath(null);
|
|
@@ -20525,6 +20466,8 @@ function AgentChat({
|
|
|
20525
20466
|
adapter,
|
|
20526
20467
|
sessionId,
|
|
20527
20468
|
path: previewPath,
|
|
20469
|
+
view: effectiveDisabled,
|
|
20470
|
+
officeEvent: runtime.state.officeEvents[previewPath],
|
|
20528
20471
|
onClose: () => setOpenPane(null)
|
|
20529
20472
|
}
|
|
20530
20473
|
)
|