diff --git a/plugins/_office/extensions/webui/get_tool_message_handler/document-artifact-handler.js b/plugins/_office/extensions/webui/get_tool_message_handler/document-artifact-handler.js index 3ee0f595d..37ed3a599 100644 --- a/plugins/_office/extensions/webui/get_tool_message_handler/document-artifact-handler.js +++ b/plugins/_office/extensions/webui/get_tool_message_handler/document-artifact-handler.js @@ -10,9 +10,6 @@ import { drawProcessStep, } from "/js/messages.js"; -const AUTO_OPEN_WINDOW_MS = 10 * 60 * 1000; -const autoOpenedDocuments = new Set(); - export default async function registerDocumentArtifactHandler(extData) { if (extData?.tool_name === "document_artifact") { extData.handler = drawDocumentArtifactTool; @@ -25,6 +22,7 @@ async function openOfficeCanvas(kvps = {}) { await canvas?.open?.("office", { path: kvps.path || "", file_id: kvps.file_id || "", + refresh: true, source: "tool", }); } @@ -50,50 +48,10 @@ function documentFromArgs(args, result = {}) { title: kvps.title || kvps.basename || document.basename || "", format: kvps.format || kvps.extension || document.extension || "", version: kvps.version || document.version || "", + last_modified: kvps.last_modified || document.last_modified || "", }; } -function shouldAutoOpenDocument(args, document) { - const kvps = args?.kvps || {}; - if (kvps.canvas_surface && kvps.canvas_surface !== "office") return false; - if (!document?.path) return false; - const action = String(kvps.action || "").trim().toLowerCase(); - if (["status", "version_history", "inspect", "read", "extract"].includes(action)) return false; - return isFreshToolMessage(args?.timestamp); -} - -function isFreshToolMessage(timestamp) { - const value = Number(timestamp); - if (!Number.isFinite(value) || value <= 0) return true; - const messageMs = value > 10_000_000_000 ? value : value * 1000; - return Math.abs(Date.now() - messageMs) <= AUTO_OPEN_WINDOW_MS; -} - -function autoOpenOfficeCanvas(args) { - const document = documentFromArgs(args, parseDocumentResult(args?.content)); - if (!shouldAutoOpenDocument(args, document)) return; - const key = `${args.id || ""}:${document.file_id || ""}:${document.path || ""}:${document.version || ""}`; - const persistedKey = `a0.office.autoOpened.${key}`; - if (hasOpenedDocument(key, persistedKey)) return; - requestAnimationFrame(() => { - void openOfficeCanvas(document); - }); -} - -function hasOpenedDocument(key, persistedKey) { - if (autoOpenedDocuments.has(key)) return true; - autoOpenedDocuments.add(key); - - try { - if (sessionStorage.getItem(persistedKey)) return true; - sessionStorage.setItem(persistedKey, "1"); - } catch { - // Best-effort persistence; the in-memory guard still prevents repeat opens. - } - - return false; -} - function drawDocumentArtifactTool({ id, type, @@ -116,7 +74,7 @@ function drawDocumentArtifactTool({ ].filter(Boolean); const actionButtons = [ - createActionButton("description", "Office", () => openOfficeCanvas(document)), + createActionButton("desktop_windows", "Desktop", () => openOfficeCanvas(document)), ]; if (document?.path) { @@ -145,6 +103,5 @@ function drawDocumentArtifactTool({ actionButtons: actionButtons.filter(Boolean), log: args, }); - autoOpenOfficeCanvas(args); return result; } diff --git a/plugins/_office/extensions/webui/right_canvas_register_surfaces/register-office.js b/plugins/_office/extensions/webui/right_canvas_register_surfaces/register-office.js index 746384b2f..65d436c8d 100644 --- a/plugins/_office/extensions/webui/right_canvas_register_surfaces/register-office.js +++ b/plugins/_office/extensions/webui/right_canvas_register_surfaces/register-office.js @@ -1,3 +1,7 @@ +import { store as officeStore } from "/plugins/_office/webui/office-store.js"; + +void officeStore; + function waitForElement(selector, timeoutMs = 3000) { const found = document.querySelector(selector); if (found) return Promise.resolve(found); @@ -20,8 +24,8 @@ function waitForElement(selector, timeoutMs = 3000) { export default async function registerOfficeSurface(canvas) { canvas.registerSurface({ id: "office", - title: "Office", - icon: "description", + title: "Desktop", + icon: "desktop_windows", order: 20, modalPath: "/plugins/_office/webui/main.html", async open(payload = {}) { @@ -30,9 +34,9 @@ export default async function registerOfficeSurface(canvas) { await office?.onMount?.(panel, { mode: "canvas" }); await office?.onOpen?.(payload); }, - async close() { + async close(payload = {}) { const office = globalThis.Alpine?.store?.("office"); - office?.beforeHostHidden?.(); + office?.beforeHostHidden?.({ unloadDesktop: payload?.reason === "mobile" }); }, }); } diff --git a/plugins/_office/extensions/webui/set_messages_after_loop/auto-open-document-results.js b/plugins/_office/extensions/webui/set_messages_after_loop/auto-open-document-results.js index 27c77ff02..af8ce63ee 100644 --- a/plugins/_office/extensions/webui/set_messages_after_loop/auto-open-document-results.js +++ b/plugins/_office/extensions/webui/set_messages_after_loop/auto-open-document-results.js @@ -1,145 +1,3 @@ -const AUTO_OPEN_WINDOW_MS = 10 * 60 * 1000; -const autoOpenedDocuments = new Set(); - -export default async function autoOpenDocumentResults(context) { - if (!context?.results?.length || context.historyEmpty) return; - - for (const { args } of context.results) { - const payload = getToolResultPayload(args); - if (getToolName(payload) !== "document_artifact") continue; - - const document = getDocumentPayload(payload); - if (!document?.path) continue; - if (payload.canvas_surface && payload.canvas_surface !== "office") continue; - if (isReadOnlyAction(payload)) continue; - if (!isFresh(args?.timestamp, document.last_modified)) continue; - - const key = [ - args?.id || "", - document.file_id || "", - document.path, - document.version || "", - ].join(":"); - const persistedKey = `a0.office.autoOpened.${key}`; - if (hasOpened(key, persistedKey)) continue; - - requestAnimationFrame(() => { - void openOfficeCanvas(document); - }); - } -} - -function getToolResultPayload(args = {}) { - const topLevelPayload = pickPayloadFields(args); - const contentPayload = parseMaybeJson(args.content); - const kvpsPayload = parseMaybeJson(args.kvps); - return { - ...topLevelPayload, - ...(contentPayload || {}), - ...(kvpsPayload || {}), - }; -} - -function pickPayloadFields(args = {}) { - const payload = {}; - for (const key of [ - "_tool_name", - "tool_name", - "tool_result", - "canvas_surface", - "action", - "file_id", - "path", - "title", - "basename", - "format", - "extension", - "version", - "last_modified", - ]) { - if (args[key] != null && args[key] !== "") payload[key] = args[key]; - } - return payload; -} - -function getToolName(payload = {}) { - return String(payload._tool_name || payload.tool_name || "").trim(); -} - -function getDocumentPayload(payload = {}) { - const result = parseMaybeJson(payload.tool_result) || {}; - const document = result.document && typeof result.document === "object" - ? result.document - : {}; - - return { - file_id: payload.file_id || document.file_id || "", - path: payload.path || document.path || "", - title: payload.title || payload.basename || document.basename || "", - format: payload.format || payload.extension || document.extension || "", - version: payload.version || document.version || "", - last_modified: payload.last_modified || document.last_modified || "", - }; -} - -function isReadOnlyAction(payload = {}) { - const action = String(payload.action || "").trim().toLowerCase(); - return ["status", "version_history", "inspect", "read", "extract"].includes(action); -} - -function parseMaybeJson(value) { - if (!value) return null; - if (typeof value === "object") return value; - if (typeof value !== "string") return null; - - const trimmed = value.trim(); - if (!trimmed.startsWith("{")) return null; - try { - const parsed = JSON.parse(trimmed); - return parsed && typeof parsed === "object" ? parsed : null; - } catch { - return null; - } -} - -function isFresh(timestamp, fallbackTimestamp) { - const messageMs = toMs(timestamp) || toMs(fallbackTimestamp); - if (!messageMs) return true; - return Math.abs(Date.now() - messageMs) <= AUTO_OPEN_WINDOW_MS; -} - -function toMs(value) { - if (value == null || value === "") return 0; - - const numeric = Number(value); - if (Number.isFinite(numeric) && numeric > 0) { - return numeric > 10_000_000_000 ? numeric : numeric * 1000; - } - - const parsed = Date.parse(String(value)); - return Number.isFinite(parsed) ? parsed : 0; -} - -function hasOpened(key, persistedKey) { - if (autoOpenedDocuments.has(key)) return true; - autoOpenedDocuments.add(key); - - try { - if (sessionStorage.getItem(persistedKey)) return true; - sessionStorage.setItem(persistedKey, "1"); - } catch { - // Best-effort persistence; the in-memory guard still prevents repeat opens. - } - - return false; -} - -async function openOfficeCanvas(document) { - const canvas = globalThis.Alpine?.store?.("rightCanvas") - || (await import("/components/canvas/right-canvas-store.js")).store; - await canvas?.open?.("office", { - path: document.path || "", - file_id: document.file_id || "", - source: "tool-result", - }); +export default async function autoOpenDocumentResults(_context) { + return; } diff --git a/plugins/_office/webui/main.html b/plugins/_office/webui/main.html index d9f9701d1..8248c6d03 100644 --- a/plugins/_office/webui/main.html +++ b/plugins/_office/webui/main.html @@ -2,11 +2,14 @@ class="office-modal modal-no-backdrop" data-canvas-surface="office" data-canvas-modal-path="/plugins/_office/webui/main.html" - data-canvas-dock-title="Open Office in canvas" + data-canvas-dock-title="Open Desktop in canvas" data-canvas-dock-icon="dock_to_right" > - Office + Desktop + diff --git a/plugins/_office/webui/office-panel.html b/plugins/_office/webui/office-panel.html index 5d98a9e17..5bca54b13 100644 --- a/plugins/_office/webui/office-panel.html +++ b/plugins/_office/webui/office-panel.html @@ -9,131 +9,153 @@