@xopcai/xopc 0.0.22 → 0.0.24
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/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-CiZMJZRp.js +216 -0
- package/dist/gateway/static/root/assets/agents-CiZMJZRp.js.map +1 -0
- package/dist/gateway/static/root/assets/apps-page-tZz69XM3.js +2 -0
- package/dist/gateway/static/root/assets/apps-page-tZz69XM3.js.map +1 -0
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js → attachment-preview-renderer-CxMJMbD2.js} +4 -4
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js.map → attachment-preview-renderer-CxMJMbD2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js → attachment-process-heavy-EFXPGfWk.js} +6 -6
- package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js.map → attachment-process-heavy-EFXPGfWk.js.map} +1 -1
- package/dist/gateway/static/root/assets/{attachment-utils-core-Dt6UxMPV.js → attachment-utils-core-ECbeoa9H.js} +1 -1
- package/dist/gateway/static/root/assets/attachment-utils-core-ECbeoa9H.js.map +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BAvk9-aK.js +9 -0
- package/dist/gateway/static/root/assets/{channels-settings-BGueHxMv.js.map → channels-settings-BAvk9-aK.js.map} +1 -1
- package/dist/gateway/static/root/assets/cn-BMCV0OMB.js +2 -0
- package/dist/gateway/static/root/assets/cn-BMCV0OMB.js.map +1 -0
- package/dist/gateway/static/root/assets/cron-page-CANqvhK7.js +2 -0
- package/dist/gateway/static/root/assets/{cron-page-DsVZzPqv.js.map → cron-page-CANqvhK7.js.map} +1 -1
- package/dist/gateway/static/root/assets/cron-utils-DyOO6TdN.js +3 -0
- package/dist/gateway/static/root/assets/{cron-utils-zbRs2yND.js.map → cron-utils-DyOO6TdN.js.map} +1 -1
- package/dist/gateway/static/root/assets/dist-Brod9LF3.js +2 -0
- package/dist/gateway/static/root/assets/{dist-CDA7gR_M.js.map → dist-Brod9LF3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js → docx-preview-F-jKDMNv.js} +2 -2
- package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js.map → docx-preview-F-jKDMNv.js.map} +1 -1
- package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js → excel-worksheet-utils-DPfAinZn.js} +1 -1
- package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js.map → excel-worksheet-utils-DPfAinZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-debug-page-CDD7ozsC.js +2 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-CDLp4DAs.js.map → extension-debug-page-CDD7ozsC.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-page-UUFMjoWf.js +2 -0
- package/dist/gateway/static/root/assets/{extension-page-DwSCjzHO.js.map → extension-page-UUFMjoWf.js.map} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CP9JNc4m.js +2 -0
- package/dist/gateway/static/root/assets/extension-settings-page-CP9JNc4m.js.map +1 -0
- package/dist/gateway/static/root/assets/index-BZvlG48D.js +150 -0
- package/dist/gateway/static/root/assets/index-BZvlG48D.js.map +1 -0
- package/dist/gateway/static/root/assets/index-DxkgyT8R.css +1 -0
- package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js → jszip.min-CL3dfyxs.js} +1 -1
- package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js.map → jszip.min-CL3dfyxs.js.map} +1 -1
- package/dist/gateway/static/root/assets/logs-page-Cr0eCGb4.js +2 -0
- package/dist/gateway/static/root/assets/{logs-page-ChJ0nsPh.js.map → logs-page-Cr0eCGb4.js.map} +1 -1
- package/dist/gateway/static/root/assets/{pdf--jE7rvON.js → pdf-CX6ji-QC.js} +1 -1
- package/dist/gateway/static/root/assets/{pdf--jE7rvON.js.map → pdf-CX6ji-QC.js.map} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DwLHN5GJ.js +2 -0
- package/dist/gateway/static/root/assets/{sessions-page-Cle4fPla.js.map → sessions-page-DwLHN5GJ.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-B3O3R0E4.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-B3O3R0E4.js.map +1 -0
- package/dist/gateway/static/root/assets/skills-page-DgBYvH6B.js +3 -0
- package/dist/gateway/static/root/assets/{skills-page-B-smhcB2.js.map → skills-page-DgBYvH6B.js.map} +1 -1
- package/dist/gateway/static/root/assets/vendor-swr-B5fPo7KK.js +2 -0
- package/dist/gateway/static/root/assets/{vendor-swr-Dp4nzp5h.js.map → vendor-swr-B5fPo7KK.js.map} +1 -1
- package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js → xlsx-CPtvfmVF.js} +1 -1
- package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js.map → xlsx-CPtvfmVF.js.map} +1 -1
- package/dist/gateway/static/root/index.html +5 -4
- package/dist/package.js +1 -1
- package/dist/src/agent/image/tool-model-config.js +2 -2
- package/dist/src/agent/image/tool-model-config.js.map +1 -1
- package/dist/src/agent/tools/execute-code-tool.js +1 -1
- package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
- package/dist/src/cli/commands/extension-dev.d.ts +2 -0
- package/dist/src/cli/commands/extension-dev.js +196 -0
- package/dist/src/cli/commands/extension-dev.js.map +1 -0
- package/dist/src/cli/commands/extension-marketplace.d.ts +4 -0
- package/dist/src/cli/commands/extension-marketplace.js +145 -0
- package/dist/src/cli/commands/extension-marketplace.js.map +1 -0
- package/dist/src/cli/commands/extension-pack.d.ts +2 -0
- package/dist/src/cli/commands/extension-pack.js +242 -0
- package/dist/src/cli/commands/extension-pack.js.map +1 -0
- package/dist/src/cli/commands/extension.js +13 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/index.js +5 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.js +1 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/api.d.ts +6 -1
- package/dist/src/extensions/api.js +30 -0
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/engine-check.d.ts +14 -0
- package/dist/src/extensions/engine-check.js +148 -0
- package/dist/src/extensions/engine-check.js.map +1 -0
- package/dist/src/extensions/loader.js +23 -0
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/marketplace.d.ts +24 -0
- package/dist/src/extensions/marketplace.js +98 -0
- package/dist/src/extensions/marketplace.js.map +1 -0
- package/dist/src/extensions/normalize-manifest.js +16 -4
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +2 -0
- package/dist/src/extensions/sdk/index.js +2 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/sdk/testing.d.ts +49 -3
- package/dist/src/extensions/sdk/testing.js +174 -5
- package/dist/src/extensions/sdk/testing.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +5 -0
- package/dist/src/extensions/types/manifest.d.ts +17 -0
- package/dist/src/extensions/when-context.d.ts +6 -0
- package/dist/src/extensions/when-context.js +28 -0
- package/dist/src/extensions/when-context.js.map +1 -0
- package/dist/src/extensions/when-expression.d.ts +11 -0
- package/dist/src/extensions/when-expression.js +215 -0
- package/dist/src/extensions/when-expression.js.map +1 -0
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +28 -0
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
- package/dist/src/providers/index.d.ts +6 -3
- package/dist/src/providers/index.js +12 -23
- package/dist/src/providers/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-BcLv59-r.js +0 -71
- package/dist/gateway/static/root/assets/agents-BcLv59-r.js.map +0 -1
- package/dist/gateway/static/root/assets/apps-page-Bl-yxbQo.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-Bl-yxbQo.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-utils-core-Dt6UxMPV.js.map +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BGueHxMv.js +0 -9
- package/dist/gateway/static/root/assets/cron-page-DsVZzPqv.js +0 -2
- package/dist/gateway/static/root/assets/cron-utils-zbRs2yND.js +0 -3
- package/dist/gateway/static/root/assets/dist-CDA7gR_M.js +0 -2
- package/dist/gateway/static/root/assets/extension-debug-page-CDLp4DAs.js +0 -2
- package/dist/gateway/static/root/assets/extension-page-DwSCjzHO.js +0 -2
- package/dist/gateway/static/root/assets/extension-settings-page-Rdmxe24_.js +0 -2
- package/dist/gateway/static/root/assets/extension-settings-page-Rdmxe24_.js.map +0 -1
- package/dist/gateway/static/root/assets/index-D9Wmfh2f.css +0 -1
- package/dist/gateway/static/root/assets/index-DG8WvMbu.js +0 -150
- package/dist/gateway/static/root/assets/index-DG8WvMbu.js.map +0 -1
- package/dist/gateway/static/root/assets/logs-page-ChJ0nsPh.js +0 -2
- package/dist/gateway/static/root/assets/sessions-page-Cle4fPla.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-Dyo2NYdy.js.map +0 -1
- package/dist/gateway/static/root/assets/skills-page-B-smhcB2.js +0 -3
- package/dist/gateway/static/root/assets/vendor-swr-Dp4nzp5h.js +0 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{i as t,t as n}from"./vendor-react-DbimaAId.js";import{a as r,i}from"./vendor-swr-B5fPo7KK.js";import{t as a}from"./cn-BMCV0OMB.js";import{Bn as o,Bt as s,Dr as c,En as l,Gt as u,Hr as d,Kn as f,Kt as p,Nr as m,Pn as h,Qt as g,Rr as _,Rt as v,Ut as y,Vt as b,ct as x,dn as S,dt as C,kn as w,lt as T,mr as E,on as D,ot as O,qt as k,st as A,ut as j,zt as M}from"./index-BZvlG48D.js";var N=e(t(),1),P=n();function F({className:e}){let t=k(e=>!!e.token),[n,r]=(0,N.useState)(``),[s,l]=(0,N.useState)(``),d=t?`marketplace-${s}`:null;(0,N.useEffect)(()=>{let e=window.setTimeout(()=>l(n.trim()),300);return()=>window.clearTimeout(e)},[n]);let{data:f,isLoading:p,error:m}=i(d,async()=>u(s.length>0?y(`/api/marketplace?q=${encodeURIComponent(s)}`):y(`/api/marketplace`)),{revalidateOnFocus:!1}),h=f?.extensions??[];return(0,P.jsxs)(`div`,{className:a(`flex flex-col gap-4`,e),children:[(0,P.jsxs)(`div`,{className:`relative`,children:[(0,P.jsx)(w,{className:`pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-muted`,strokeWidth:1.75,"aria-hidden":!0}),(0,P.jsx)(`input`,{type:`search`,value:n,onChange:e=>r(e.target.value),placeholder:`Search extensions…`,className:`ui-input h-10 w-full rounded-lg border border-edge bg-surface-base pl-9 pr-3 text-sm text-fg placeholder:text-fg-muted`})]}),m?(0,P.jsx)(`p`,{className:`text-sm text-fg-muted`,children:m instanceof Error?m.message:`Failed to load marketplace`}):null,p&&!f?(0,P.jsx)(`p`,{className:`text-sm text-fg-muted`,children:`Loading…`}):(0,P.jsx)(`ul`,{className:`flex flex-col gap-3`,children:h.length===0?(0,P.jsx)(`li`,{className:`text-sm text-fg-muted`,children:`No extensions in registry.`}):h.map(e=>(0,P.jsxs)(`li`,{className:`rounded-xl border border-edge bg-surface-base p-4 shadow-surface`,children:[(0,P.jsx)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:(0,P.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,P.jsx)(`h3`,{className:`font-semibold text-fg`,children:e.name}),e.verified?(0,P.jsx)(c,{className:`size-4 shrink-0 text-emerald-600 dark:text-emerald-400`,strokeWidth:1.75,"aria-label":`Verified`}):null,e.version?(0,P.jsx)(`span`,{className:`text-xs text-fg-muted`,children:e.version}):null]}),e.description?(0,P.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:e.description}):null,(0,P.jsx)(`div`,{className:`mt-2 flex flex-wrap gap-1.5`,children:(e.categories??[]).map(e=>(0,P.jsx)(`span`,{className:`rounded-md border border-edge bg-surface-panel px-2 py-0.5 text-[11px] text-fg-muted`,children:e},e))}),(0,P.jsxs)(`p`,{className:`mt-2 flex items-center gap-1.5 text-xs text-fg-muted`,children:[(0,P.jsx)(o,{className:`size-3.5 shrink-0`,strokeWidth:1.75,"aria-hidden":!0}),(0,P.jsx)(`code`,{className:`rounded bg-surface-panel px-1 py-0.5`,children:e.npmPackage})]}),e.homepage?(0,P.jsxs)(`a`,{href:e.homepage,target:`_blank`,rel:`noreferrer`,className:`mt-2 inline-flex items-center gap-1 text-xs text-accent hover:underline`,children:[(0,P.jsx)(E,{className:`size-3.5`,strokeWidth:1.75,"aria-hidden":!0}),`Homepage`]}):null]})}),(0,P.jsxs)(`p`,{className:`mt-3 text-[11px] text-fg-muted`,children:[`npm package:`,` `,(0,P.jsx)(`code`,{className:`rounded bg-surface-panel px-1 py-0.5`,children:e.npmPackage})]})]},e.id))})]})}function I(){let e=g(e=>e.language),t=D(e),n=p(e=>e.setPageHeader),i=p(e=>e.clearPageHeader),a=s(),o=b(),{mutate:c}=r(),[l,u]=(0,N.useState)(`all`),[d,f]=(0,N.useState)(``),[m,h]=(0,N.useState)(null),_=(0,N.useMemo)(()=>{let t=a;l===`ui`&&(t=t.filter(e=>e.hasUi)),l===`backend`&&(t=t.filter(e=>!e.hasUi));let n=d.trim().toLowerCase();return n&&(t=t.filter(e=>(e.name??``).toLowerCase().includes(n)||e.id.toLowerCase().includes(n)||(e.description??``).toLowerCase().includes(n))),[...t].sort((t,n)=>t.hasUi===n.hasUi?(t.name||t.id).localeCompare(n.name||n.id,e):t.hasUi?-1:1)},[a,l,d,e]);return(0,N.useEffect)(()=>{h(e=>{if(!e)return e;let t=a.find(t=>t.id===e.id);return t?R(e)===R(t)&&e.active===t.active?e:t:null})},[a]),(0,N.useLayoutEffect)(()=>(n({startExtra:null,main:(0,P.jsx)(`div`,{className:`w-full min-w-0 px-3 sm:px-5 xl:px-6`,children:(0,P.jsx)(`h1`,{className:`min-w-0 truncate text-base font-semibold tracking-tight text-fg`,children:t.appsPage.title})}),end:null}),()=>i()),[i,t.appsPage.title,n]),o&&l!==`marketplace`?(0,P.jsx)(W,{}):(0,P.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto bg-surface-panel`,children:[(0,P.jsxs)(`div`,{className:`mx-auto w-full max-w-app-main px-4 py-8`,children:[(0,P.jsxs)(`header`,{className:`mb-6`,children:[(0,P.jsx)(`h1`,{className:`text-xl font-semibold text-fg`,children:t.appsPage.title}),(0,P.jsx)(`p`,{className:`mt-1 text-sm text-fg-muted`,children:t.appsPage.subtitle})]}),(0,P.jsxs)(`div`,{className:`mb-5 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,P.jsx)(L,{active:l===`all`,onClick:()=>u(`all`),label:t.appsPage.tabAll}),(0,P.jsx)(L,{active:l===`ui`,onClick:()=>u(`ui`),label:t.appsPage.tabWithUi}),(0,P.jsx)(L,{active:l===`backend`,onClick:()=>u(`backend`),label:t.appsPage.tabBackend}),(0,P.jsx)(L,{active:l===`marketplace`,onClick:()=>u(`marketplace`),label:t.appsPage.tabMarketplace})]}),l!==`marketplace`&&a.length>0?(0,P.jsxs)(`div`,{className:`relative w-full sm:max-w-xs`,children:[(0,P.jsx)(w,{className:`pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-muted`,"aria-hidden":!0}),(0,P.jsx)(`input`,{type:`search`,value:d,onChange:e=>f(e.target.value),placeholder:t.appsPage.searchPlaceholder,className:`w-full rounded-lg border border-edge bg-surface-base py-2 pl-9 pr-3 text-sm text-fg placeholder:text-fg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,autoComplete:`off`})]}):null]}),l===`marketplace`?(0,P.jsx)(F,{}):a.length===0?(0,P.jsx)(G,{message:t.appsPage.empty}):_.length===0?(0,P.jsx)(`p`,{className:`rounded-xl border border-dashed border-edge-subtle bg-surface-hover/30 px-4 py-8 text-center text-sm text-fg-muted dark:bg-surface-hover/15`,children:t.appsPage.noSearchResults}):(0,P.jsx)(`div`,{className:`grid gap-4 sm:grid-cols-2 lg:grid-cols-3`,children:_.map(e=>(0,P.jsx)(V,{extension:e,copy:t.appsPage,onOpen:()=>h(e)},e.id))})]}),m?(0,P.jsx)(H,{extension:m,copy:t.appsPage,onClose:()=>h(null),onAfterToggle:async()=>{await c(`gateway-extensions-list`)}},m.id):null]})}function L({active:e,onClick:t,label:n}){return(0,P.jsx)(`button`,{type:`button`,onClick:t,className:a(`rounded-full border px-3 py-1.5 text-xs font-medium transition-colors`,`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,e?`border-accent/40 bg-accent-soft text-accent-fg`:`border-edge bg-surface-base text-fg-muted hover:bg-surface-hover hover:text-fg`),children:n})}function R(e){return e.activationEligible??e.active}function z(e,t){return e.source===`bundled`?t.providerBundled:e.source===`global`?t.providerGlobal:e.source===`workspace`?t.providerWorkspace:t.providerOther}function B(e,t){let n=R(e);return n&&e.active?t.runStateLive:n&&!e.active?t.runStatePendingOn:!n&&e.active?t.runStatePendingOff:t.runStateOff}function V({extension:e,copy:t,onOpen:n}){let r=R(e);return(0,P.jsx)(`button`,{type:`button`,onClick:n,className:a(`group flex w-full flex-col rounded-xl border border-edge bg-surface-base p-4 text-left shadow-sm transition-colors`,`hover:border-edge-subtle hover:bg-surface-hover/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,`dark:hover:bg-surface-hover/25`),children:(0,P.jsxs)(`div`,{className:`flex gap-3`,children:[(0,P.jsx)(`div`,{className:`flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent-soft text-base font-semibold text-accent-fg`,"aria-hidden":!0,children:(e.name||e.id).charAt(0).toUpperCase()}),(0,P.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,P.jsxs)(`div`,{className:`flex items-start justify-between gap-2`,children:[(0,P.jsxs)(`div`,{className:`min-w-0`,children:[(0,P.jsx)(`h2`,{className:`truncate text-sm font-semibold text-fg`,children:e.name}),e.version?(0,P.jsxs)(`span`,{className:`text-[11px] text-fg-muted`,children:[`v`,e.version]}):null]}),r?(0,P.jsxs)(`span`,{className:`inline-flex shrink-0 items-center gap-1 rounded-full bg-surface-hover px-2 py-0.5 text-[11px] font-medium text-fg-muted`,children:[(0,P.jsx)(m,{className:`size-3`,strokeWidth:2.5,"aria-hidden":!0}),t.statusEnabled]}):(0,P.jsx)(`span`,{className:`flex size-8 shrink-0 items-center justify-center rounded-full border-2 border-accent text-accent`,"aria-hidden":!0,children:(0,P.jsx)(h,{className:`size-4`,strokeWidth:2.5})})]}),(0,P.jsx)(`p`,{className:`mt-2 line-clamp-2 text-xs leading-relaxed text-fg-muted`,children:e.description?.trim()||t.cardNoDescription}),(0,P.jsxs)(`div`,{className:`mt-2 flex flex-wrap gap-1.5`,children:[(0,P.jsx)(`span`,{className:`rounded-md bg-surface-hover px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-fg-muted`,children:e.hasUi?t.badgeKindUi:t.badgeKindBackend}),e.source===`bundled`?(0,P.jsx)(`span`,{className:`rounded-md bg-surface-hover px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-fg-muted`,children:t.badgeBundled}):null]})]})]})})}function H({extension:e,copy:t,onClose:n,onAfterToggle:r}){let[i,o]=(0,N.useState)(!1),[s,c]=(0,N.useState)(null),p=e.ui?.contributions?.pages??[],m=e.ui?.contributions?.settingsPanels??[],h=e.ui?.contributions?.chatWidgets??[],g=e.ui?.contributions?.sidebarPanels??[],b=p.find(e=>e.showInNav)??p[0],w=m[0],D=b?v(e.id,b):null,k=w?`/settings/ext/${e.id}/${w.id}`:e.hasConfigSchema?`/settings/ext/${e.id}`:null,F=R(e),I=e.source===`bundled`,L=async n=>{c(null),o(!0);try{await u(y(`/api/extensions/bundled/activation`),{method:`POST`,body:JSON.stringify({extensionId:e.id,enabled:n})}),await r()}catch(e){c(e instanceof Error?e.message:t.toggleError)}finally{o(!1)}};return(0,P.jsx)(j,{defaultOpen:!0,onOpenChange:e=>!e&&n(),children:(0,P.jsxs)(T,{children:[(0,P.jsx)(x,{className:`xopc-dialog-overlay fixed inset-0 z-[130] bg-scrim`}),(0,P.jsxs)(O,{className:`fixed left-1/2 top-1/2 z-[131] flex max-h-[min(90vh,44rem)] w-[min(42rem,calc(100vw-1.5rem))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-edge bg-surface-panel shadow-elevated`,onOpenAutoFocus:e=>e.preventDefault(),children:[(0,P.jsxs)(`div`,{className:`flex shrink-0 items-center justify-between gap-2 border-b border-edge px-4 py-3`,children:[(0,P.jsx)(`button`,{type:`button`,onClick:n,className:`inline-flex size-9 items-center justify-center rounded-lg text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,"aria-label":t.detailBack,children:(0,P.jsx)(_,{className:`size-5`})}),(0,P.jsx)(C,{className:`min-w-0 flex-1 truncate text-center text-sm font-semibold text-fg`,children:t.detailTitle}),(0,P.jsxs)(A,{className:`sr-only`,children:[e.name,` (`,e.id,`)`]}),(0,P.jsx)(`button`,{type:`button`,onClick:n,className:`inline-flex size-9 items-center justify-center rounded-lg text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,"aria-label":t.detailClose,children:(0,P.jsx)(S,{className:`size-5`})})]}),(0,P.jsxs)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-5 py-5`,children:[(0,P.jsxs)(`div`,{className:`flex flex-wrap items-start gap-4`,children:[(0,P.jsx)(`div`,{className:`flex size-16 shrink-0 items-center justify-center rounded-2xl bg-accent-soft text-xl font-semibold text-accent-fg`,"aria-hidden":!0,children:(e.name||e.id).charAt(0).toUpperCase()}),(0,P.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,P.jsx)(`h3`,{className:`text-lg font-semibold text-fg`,children:e.name}),(0,P.jsxs)(`p`,{className:`mt-0.5 text-sm text-fg-muted`,children:[t.detailProviderPrefix,` `,z(e,t)]}),e.version?(0,P.jsxs)(`p`,{className:`mt-1 text-xs text-fg-muted`,children:[`v`,e.version]}):null]}),I?(0,P.jsx)(`div`,{className:`flex w-full flex-col items-stretch gap-2 sm:w-auto sm:items-end`,children:(0,P.jsxs)(`button`,{type:`button`,disabled:i,"aria-busy":i,onClick:()=>void L(!F),className:a(`relative inline-flex max-w-full rounded-full py-1.5 text-sm font-medium`,`transition-[color,background-color,border-color,opacity] duration-200 ease-out`,`focus-visible:outline-none focus-visible:ring-2 motion-reduce:transition-none`,F?`border-2 border-red-500/50 text-red-600 hover:bg-red-500/10 focus-visible:ring-red-500/40 dark:text-red-400`:`border-2 border-transparent bg-accent text-white hover:opacity-90 focus-visible:ring-accent`,i&&`cursor-wait opacity-80`),children:[(0,P.jsx)(`span`,{className:`block whitespace-nowrap px-4 text-center leading-none`,children:F?t.actionDisable:t.actionEnable}),(0,P.jsx)(f,{className:a(`pointer-events-none absolute right-2 top-1/2 size-3 -translate-y-1/2 text-current opacity-90`,i?`animate-spin`:`invisible`),"aria-hidden":!0})]})}):null]}),(0,P.jsx)(`p`,{className:`mt-5 text-sm leading-relaxed text-fg`,children:e.description?.trim()||t.detailNoDescription}),e.hasUi?null:(0,P.jsx)(`p`,{className:`mt-3 text-xs text-fg-muted`,children:t.backendOnlyHint}),(0,P.jsx)(`p`,{className:`mt-2 text-xs text-fg-muted`,children:B(e,t)}),s?(0,P.jsx)(`p`,{className:`mt-3 rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-xs text-fg`,role:`alert`,children:s}):null,I?null:(0,P.jsx)(`p`,{className:`mt-4 rounded-lg border border-edge-subtle bg-surface-hover/40 px-3 py-2 text-xs text-fg-muted dark:bg-surface-hover/20`,children:t.cliManageHint}),e.hasUi&&(p.length>0||m.length>0||h.length>0)?(0,P.jsxs)(`section`,{className:`mt-8`,children:[(0,P.jsx)(`h4`,{className:`mb-3 text-sm font-semibold text-fg`,children:t.detailSectionFeatures}),(0,P.jsxs)(`div`,{className:`flex flex-wrap gap-1.5`,children:[(0,P.jsx)(U,{template:t.badgePages,count:p.length,hidden:p.length===0}),(0,P.jsx)(U,{template:t.badgeSettings,count:m.length,hidden:m.length===0}),(0,P.jsx)(U,{template:t.badgeWidgets,count:h.length,hidden:h.length===0}),(0,P.jsx)(U,{template:t.badgeSidebar,count:g.length,hidden:g.length===0})]})]}):null,M(e)&&(D||k)?(0,P.jsxs)(`div`,{className:`mt-6 flex flex-wrap gap-2 border-t border-edge-subtle pt-4`,children:[D?(0,P.jsxs)(d,{to:D,onClick:n,className:a(`inline-flex items-center gap-1.5 rounded-lg border border-edge px-3 py-2 text-sm font-medium text-fg`,`hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`),children:[(0,P.jsx)(E,{className:`size-4 shrink-0 opacity-80`,"aria-hidden":!0}),t.open]}):null,k?(0,P.jsxs)(d,{to:k,onClick:n,className:a(`inline-flex items-center gap-1.5 rounded-lg border border-edge px-3 py-2 text-sm font-medium text-fg`,`hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`),children:[(0,P.jsx)(l,{className:`size-4 shrink-0 opacity-80`,"aria-hidden":!0}),t.openSettings]}):null]}):null,(0,P.jsx)(`p`,{className:`mt-6 text-[11px] leading-relaxed text-fg-muted`,children:t.restartNote})]})]})]})})}function U({template:e,count:t,hidden:n}){return n||t===0?null:(0,P.jsx)(`span`,{className:`inline-flex items-center rounded-md bg-surface-hover px-2 py-0.5 text-[11px] font-medium text-fg-muted`,children:e.replace(/\{\{count\}\}/g,String(t))})}function W(){return(0,P.jsx)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto bg-surface-panel`,children:(0,P.jsxs)(`div`,{className:`mx-auto w-full max-w-app-main px-4 py-8`,children:[(0,P.jsx)(`div`,{className:`mb-6 h-8 w-40 max-w-full animate-pulse rounded-md bg-surface-hover`}),(0,P.jsx)(`div`,{className:`mb-2 h-4 w-full max-w-md animate-pulse rounded bg-surface-hover`}),(0,P.jsx)(`div`,{className:`mb-5 h-9 w-full max-w-lg animate-pulse rounded-full bg-surface-hover`}),(0,P.jsx)(`div`,{className:`mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3`,children:[0,1,2,3,4,5].map(e=>(0,P.jsx)(`div`,{className:`h-36 animate-pulse rounded-xl border border-edge bg-surface-base`},e))})]})})}function G({message:e}){return(0,P.jsx)(`div`,{className:`flex min-h-[min(40vh,16rem)] flex-col items-center justify-center rounded-xl border border-dashed border-edge-subtle bg-surface-hover/40 px-4 py-12 text-center dark:bg-surface-hover/20`,children:(0,P.jsx)(`p`,{className:`text-sm text-fg-muted`,children:e})})}export{I as AppsPage};
|
|
2
|
+
//# sourceMappingURL=apps-page-tZz69XM3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps-page-tZz69XM3.js","names":[],"sources":["../../../../../web/src/features/extensions/extension-marketplace.tsx","../../../../../web/src/pages/apps-page.tsx"],"sourcesContent":["import { CheckCircle, ExternalLink, Package, Search } from 'lucide-react';\nimport { useEffect, useState } from 'react';\nimport useSWR from 'swr';\n\nimport { useGatewayStore } from '@/stores/gateway-store';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { cn } from '@/lib/cn';\n\ntype RegistryEntry = {\n id: string;\n name: string;\n description?: string;\n npmPackage: string;\n version?: string;\n categories?: string[];\n tags?: string[];\n verified?: boolean;\n homepage?: string;\n author?: string;\n};\n\ntype MarketplaceResponse = { ok: boolean; extensions: RegistryEntry[] };\n\nexport function ExtensionMarketplacePanel({ className }: { className?: string }) {\n const hasToken = useGatewayStore((s) => Boolean(s.token));\n const [q, setQ] = useState('');\n const [debounced, setDebounced] = useState('');\n const key = hasToken ? `marketplace-${debounced}` : null;\n\n useEffect(() => {\n const t = window.setTimeout(() => setDebounced(q.trim()), 300);\n return () => window.clearTimeout(t);\n }, [q]);\n\n const { data, isLoading, error } = useSWR(\n key,\n async () => {\n const url =\n debounced.length > 0\n ? apiUrl(`/api/marketplace?q=${encodeURIComponent(debounced)}`)\n : apiUrl('/api/marketplace');\n return fetchJson<MarketplaceResponse>(url);\n },\n { revalidateOnFocus: false },\n );\n\n const extensions = data?.extensions ?? [];\n\n return (\n <div className={cn('flex flex-col gap-4', className)}>\n <div className=\"relative\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-muted\"\n strokeWidth={1.75}\n aria-hidden\n />\n <input\n type=\"search\"\n value={q}\n onChange={(e) => setQ(e.target.value)}\n placeholder=\"Search extensions…\"\n className=\"ui-input h-10 w-full rounded-lg border border-edge bg-surface-base pl-9 pr-3 text-sm text-fg placeholder:text-fg-muted\"\n />\n </div>\n\n {error ? (\n <p className=\"text-sm text-fg-muted\">\n {error instanceof Error ? error.message : 'Failed to load marketplace'}\n </p>\n ) : null}\n\n {isLoading && !data ? (\n <p className=\"text-sm text-fg-muted\">Loading…</p>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {extensions.length === 0 ? (\n <li className=\"text-sm text-fg-muted\">No extensions in registry.</li>\n ) : (\n extensions.map((e) => (\n <li\n key={e.id}\n className=\"rounded-xl border border-edge bg-surface-base p-4 shadow-surface\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-fg\">{e.name}</h3>\n {e.verified ? (\n <CheckCircle\n className=\"size-4 shrink-0 text-emerald-600 dark:text-emerald-400\"\n strokeWidth={1.75}\n aria-label=\"Verified\"\n />\n ) : null}\n {e.version ? (\n <span className=\"text-xs text-fg-muted\">{e.version}</span>\n ) : null}\n </div>\n {e.description ? (\n <p className=\"mt-1 text-sm text-fg-muted\">{e.description}</p>\n ) : null}\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {(e.categories ?? []).map((c) => (\n <span\n key={c}\n className=\"rounded-md border border-edge bg-surface-panel px-2 py-0.5 text-[11px] text-fg-muted\"\n >\n {c}\n </span>\n ))}\n </div>\n <p className=\"mt-2 flex items-center gap-1.5 text-xs text-fg-muted\">\n <Package className=\"size-3.5 shrink-0\" strokeWidth={1.75} aria-hidden />\n <code className=\"rounded bg-surface-panel px-1 py-0.5\">{e.npmPackage}</code>\n </p>\n {e.homepage ? (\n <a\n href={e.homepage}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-2 inline-flex items-center gap-1 text-xs text-accent hover:underline\"\n >\n <ExternalLink className=\"size-3.5\" strokeWidth={1.75} aria-hidden />\n Homepage\n </a>\n ) : null}\n </div>\n </div>\n <p className=\"mt-3 text-[11px] text-fg-muted\">\n npm package:{' '}\n <code className=\"rounded bg-surface-panel px-1 py-0.5\">{e.npmPackage}</code>\n </p>\n </li>\n ))\n )}\n </ul>\n )}\n </div>\n );\n}\n","import * as Dialog from '@radix-ui/react-dialog';\nimport {\n ArrowLeft,\n Check,\n ExternalLink,\n Loader2,\n Plus,\n Search,\n Settings,\n X,\n} from 'lucide-react';\nimport { useEffect, useLayoutEffect, useMemo, useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport { useSWRConfig } from 'swr';\n\nimport {\n extensionExposesGatewayShellUi,\n useExtensions,\n useExtensionsLoading,\n} from '@/features/extensions/extension-provider';\nimport { ExtensionMarketplacePanel } from '@/features/extensions/extension-marketplace';\nimport { extensionPagePath } from '@/features/extensions/extension-paths';\nimport type { ExtensionApiRow, PageContribution } from '@/features/extensions/types';\nimport { messages } from '@/i18n/messages';\nimport type { MessageBundle } from '@/i18n/messages';\nimport { cn } from '@/lib/cn';\nimport { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\nimport { usePageHeaderStore } from '@/stores/page-header-store';\nimport { useLocaleStore } from '@/stores/locale-store';\n\ntype AppsPageCopy = MessageBundle['appsPage'];\ntype AppsTab = 'all' | 'ui' | 'backend' | 'marketplace';\n\nexport function AppsPage() {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const setPageHeader = usePageHeaderStore((s) => s.setPageHeader);\n const clearPageHeader = usePageHeaderStore((s) => s.clearPageHeader);\n const extensions = useExtensions();\n const loading = useExtensionsLoading();\n const { mutate } = useSWRConfig();\n const [tab, setTab] = useState<AppsTab>('all');\n const [search, setSearch] = useState('');\n const [detail, setDetail] = useState<ExtensionApiRow | null>(null);\n\n const filtered = useMemo(() => {\n let list = extensions;\n if (tab === 'ui') list = list.filter((e) => e.hasUi);\n if (tab === 'backend') list = list.filter((e) => !e.hasUi);\n const q = search.trim().toLowerCase();\n if (q) {\n list = list.filter(\n (e) =>\n (e.name ?? '').toLowerCase().includes(q) ||\n e.id.toLowerCase().includes(q) ||\n (e.description ?? '').toLowerCase().includes(q),\n );\n }\n return [...list].sort((a, b) => {\n if (a.hasUi !== b.hasUi) return a.hasUi ? -1 : 1;\n return (a.name || a.id).localeCompare(b.name || b.id, language);\n });\n }, [extensions, tab, search, language]);\n\n /** Keep dialog + cards in sync after SWR refetch (detail was a stale row reference). */\n useEffect(() => {\n setDetail((prev) => {\n if (!prev) return prev;\n const next = extensions.find((e) => e.id === prev.id);\n if (!next) return null;\n const eligPrev = activationEligibleFor(prev);\n const eligNext = activationEligibleFor(next);\n if (eligPrev === eligNext && prev.active === next.active) return prev;\n return next;\n });\n }, [extensions]);\n\n useLayoutEffect(() => {\n setPageHeader({\n startExtra: null,\n main: (\n <div className=\"w-full min-w-0 px-3 sm:px-5 xl:px-6\">\n <h1 className=\"min-w-0 truncate text-base font-semibold tracking-tight text-fg\">{m.appsPage.title}</h1>\n </div>\n ),\n end: null,\n });\n return () => clearPageHeader();\n }, [clearPageHeader, m.appsPage.title, setPageHeader]);\n\n if (loading && tab !== 'marketplace') {\n return <AppsPageSkeleton />;\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto bg-surface-panel\">\n <div className=\"mx-auto w-full max-w-app-main px-4 py-8\">\n <header className=\"mb-6\">\n <h1 className=\"text-xl font-semibold text-fg\">{m.appsPage.title}</h1>\n <p className=\"mt-1 text-sm text-fg-muted\">{m.appsPage.subtitle}</p>\n </header>\n\n <div className=\"mb-5 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap gap-2\">\n <TabChip active={tab === 'all'} onClick={() => setTab('all')} label={m.appsPage.tabAll} />\n <TabChip\n active={tab === 'ui'}\n onClick={() => setTab('ui')}\n label={m.appsPage.tabWithUi}\n />\n <TabChip\n active={tab === 'backend'}\n onClick={() => setTab('backend')}\n label={m.appsPage.tabBackend}\n />\n <TabChip\n active={tab === 'marketplace'}\n onClick={() => setTab('marketplace')}\n label={m.appsPage.tabMarketplace}\n />\n </div>\n {tab !== 'marketplace' && extensions.length > 0 ? (\n <div className=\"relative w-full sm:max-w-xs\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-fg-muted\"\n aria-hidden\n />\n <input\n type=\"search\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={m.appsPage.searchPlaceholder}\n className=\"w-full rounded-lg border border-edge bg-surface-base py-2 pl-9 pr-3 text-sm text-fg placeholder:text-fg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n autoComplete=\"off\"\n />\n </div>\n ) : null}\n </div>\n\n {tab === 'marketplace' ? (\n <ExtensionMarketplacePanel />\n ) : extensions.length === 0 ? (\n <EmptyAppsState message={m.appsPage.empty} />\n ) : filtered.length === 0 ? (\n <p className=\"rounded-xl border border-dashed border-edge-subtle bg-surface-hover/30 px-4 py-8 text-center text-sm text-fg-muted dark:bg-surface-hover/15\">\n {m.appsPage.noSearchResults}\n </p>\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((ext) => (\n <ExtensionAppCard\n key={ext.id}\n extension={ext}\n copy={m.appsPage}\n onOpen={() => setDetail(ext)}\n />\n ))}\n </div>\n )}\n </div>\n\n {detail ? (\n <ExtensionDetailDialog\n key={detail.id}\n extension={detail}\n copy={m.appsPage}\n onClose={() => setDetail(null)}\n onAfterToggle={async () => {\n await mutate('gateway-extensions-list');\n }}\n />\n ) : null}\n </div>\n );\n}\n\nfunction TabChip({\n active,\n onClick,\n label,\n}: {\n active: boolean;\n onClick: () => void;\n label: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n 'rounded-full border px-3 py-1.5 text-xs font-medium transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent',\n active\n ? 'border-accent/40 bg-accent-soft text-accent-fg'\n : 'border-edge bg-surface-base text-fg-muted hover:bg-surface-hover hover:text-fg',\n )}\n >\n {label}\n </button>\n );\n}\n\nfunction activationEligibleFor(ext: ExtensionApiRow): boolean {\n return ext.activationEligible ?? ext.active;\n}\n\nfunction providerLabel(ext: ExtensionApiRow, copy: AppsPageCopy): string {\n if (ext.source === 'bundled') return copy.providerBundled;\n if (ext.source === 'global') return copy.providerGlobal;\n if (ext.source === 'workspace') return copy.providerWorkspace;\n return copy.providerOther;\n}\n\nfunction bundledRunCaption(ext: ExtensionApiRow, copy: AppsPageCopy): string {\n const eligible = activationEligibleFor(ext);\n if (eligible && ext.active) return copy.runStateLive;\n if (eligible && !ext.active) return copy.runStatePendingOn;\n if (!eligible && ext.active) return copy.runStatePendingOff;\n return copy.runStateOff;\n}\n\nfunction ExtensionAppCard({\n extension: ext,\n copy,\n onOpen,\n}: {\n extension: ExtensionApiRow;\n copy: AppsPageCopy;\n onOpen: () => void;\n}) {\n const eligible = activationEligibleFor(ext);\n\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className={cn(\n 'group flex w-full flex-col rounded-xl border border-edge bg-surface-base p-4 text-left shadow-sm transition-colors',\n 'hover:border-edge-subtle hover:bg-surface-hover/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent',\n 'dark:hover:bg-surface-hover/25',\n )}\n >\n <div className=\"flex gap-3\">\n <div\n className=\"flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent-soft text-base font-semibold text-accent-fg\"\n aria-hidden\n >\n {(ext.name || ext.id).charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-sm font-semibold text-fg\">{ext.name}</h2>\n {ext.version ? (\n <span className=\"text-[11px] text-fg-muted\">v{ext.version}</span>\n ) : null}\n </div>\n {eligible ? (\n <span className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-surface-hover px-2 py-0.5 text-[11px] font-medium text-fg-muted\">\n <Check className=\"size-3\" strokeWidth={2.5} aria-hidden />\n {copy.statusEnabled}\n </span>\n ) : (\n <span\n className=\"flex size-8 shrink-0 items-center justify-center rounded-full border-2 border-accent text-accent\"\n aria-hidden\n >\n <Plus className=\"size-4\" strokeWidth={2.5} />\n </span>\n )}\n </div>\n <p className=\"mt-2 line-clamp-2 text-xs leading-relaxed text-fg-muted\">\n {ext.description?.trim() || copy.cardNoDescription}\n </p>\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n <span className=\"rounded-md bg-surface-hover px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-fg-muted\">\n {ext.hasUi ? copy.badgeKindUi : copy.badgeKindBackend}\n </span>\n {ext.source === 'bundled' ? (\n <span className=\"rounded-md bg-surface-hover px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-fg-muted\">\n {copy.badgeBundled}\n </span>\n ) : null}\n </div>\n </div>\n </div>\n </button>\n );\n}\n\nfunction ExtensionDetailDialog({\n extension: ext,\n copy,\n onClose,\n onAfterToggle,\n}: {\n extension: ExtensionApiRow;\n copy: AppsPageCopy;\n onClose: () => void;\n onAfterToggle: () => void | Promise<void>;\n}) {\n const [saving, setSaving] = useState(false);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const pages = ext.ui?.contributions?.pages ?? [];\n const settingsPanels = ext.ui?.contributions?.settingsPanels ?? [];\n const chatWidgets = ext.ui?.contributions?.chatWidgets ?? [];\n const sidebarPanels = ext.ui?.contributions?.sidebarPanels ?? [];\n const primaryPage: PageContribution | undefined = pages.find((p) => p.showInNav) ?? pages[0];\n const primarySettingsPanel = settingsPanels[0];\n const openPath = primaryPage ? extensionPagePath(ext.id, primaryPage) : null;\n const settingsPath = primarySettingsPanel\n ? `/settings/ext/${ext.id}/${primarySettingsPanel.id}`\n : ext.hasConfigSchema\n ? `/settings/ext/${ext.id}`\n : null;\n\n const eligible = activationEligibleFor(ext);\n const isBundled = ext.source === 'bundled';\n\n const toggleBundled = async (next: boolean) => {\n setErrorMessage(null);\n setSaving(true);\n try {\n await fetchJson<{ ok: true; payload: { requiresGatewayRestart: boolean } }>(\n apiUrl('/api/extensions/bundled/activation'),\n { method: 'POST', body: JSON.stringify({ extensionId: ext.id, enabled: next }) },\n );\n await onAfterToggle();\n } catch (e) {\n setErrorMessage(e instanceof Error ? e.message : copy.toggleError);\n } finally {\n setSaving(false);\n }\n };\n\n return (\n <Dialog.Root defaultOpen onOpenChange={(o) => !o && onClose()}>\n <Dialog.Portal>\n <Dialog.Overlay className=\"xopc-dialog-overlay fixed inset-0 z-[130] bg-scrim\" />\n <Dialog.Content\n className=\"fixed left-1/2 top-1/2 z-[131] flex max-h-[min(90vh,44rem)] w-[min(42rem,calc(100vw-1.5rem))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-edge bg-surface-panel shadow-elevated\"\n onOpenAutoFocus={(e) => e.preventDefault()}\n >\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b border-edge px-4 py-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"inline-flex size-9 items-center justify-center rounded-lg text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n aria-label={copy.detailBack}\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <Dialog.Title className=\"min-w-0 flex-1 truncate text-center text-sm font-semibold text-fg\">\n {copy.detailTitle}\n </Dialog.Title>\n <Dialog.Description className=\"sr-only\">\n {ext.name} ({ext.id})\n </Dialog.Description>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"inline-flex size-9 items-center justify-center rounded-lg text-fg-muted hover:bg-surface-hover hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n aria-label={copy.detailClose}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto px-5 py-5\">\n <div className=\"flex flex-wrap items-start gap-4\">\n <div\n className=\"flex size-16 shrink-0 items-center justify-center rounded-2xl bg-accent-soft text-xl font-semibold text-accent-fg\"\n aria-hidden\n >\n {(ext.name || ext.id).charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-lg font-semibold text-fg\">{ext.name}</h3>\n <p className=\"mt-0.5 text-sm text-fg-muted\">\n {copy.detailProviderPrefix} {providerLabel(ext, copy)}\n </p>\n {ext.version ? (\n <p className=\"mt-1 text-xs text-fg-muted\">v{ext.version}</p>\n ) : null}\n </div>\n {isBundled ? (\n <div className=\"flex w-full flex-col items-stretch gap-2 sm:w-auto sm:items-end\">\n <button\n type=\"button\"\n disabled={saving}\n aria-busy={saving}\n onClick={() => void toggleBundled(!eligible)}\n className={cn(\n 'relative inline-flex max-w-full rounded-full py-1.5 text-sm font-medium',\n 'transition-[color,background-color,border-color,opacity] duration-200 ease-out',\n 'focus-visible:outline-none focus-visible:ring-2 motion-reduce:transition-none',\n eligible\n ? 'border-2 border-red-500/50 text-red-600 hover:bg-red-500/10 focus-visible:ring-red-500/40 dark:text-red-400'\n : 'border-2 border-transparent bg-accent text-white hover:opacity-90 focus-visible:ring-accent',\n saving && 'cursor-wait opacity-80',\n )}\n >\n <span className=\"block whitespace-nowrap px-4 text-center leading-none\">\n {eligible ? copy.actionDisable : copy.actionEnable}\n </span>\n <Loader2\n className={cn(\n 'pointer-events-none absolute right-2 top-1/2 size-3 -translate-y-1/2 text-current opacity-90',\n saving ? 'animate-spin' : 'invisible',\n )}\n aria-hidden\n />\n </button>\n </div>\n ) : null}\n </div>\n\n <p className=\"mt-5 text-sm leading-relaxed text-fg\">\n {ext.description?.trim() || copy.detailNoDescription}\n </p>\n\n {!ext.hasUi ? (\n <p className=\"mt-3 text-xs text-fg-muted\">{copy.backendOnlyHint}</p>\n ) : null}\n\n <p className=\"mt-2 text-xs text-fg-muted\">{bundledRunCaption(ext, copy)}</p>\n\n {errorMessage ? (\n <p className=\"mt-3 rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-xs text-fg\" role=\"alert\">\n {errorMessage}\n </p>\n ) : null}\n\n {!isBundled ? (\n <p className=\"mt-4 rounded-lg border border-edge-subtle bg-surface-hover/40 px-3 py-2 text-xs text-fg-muted dark:bg-surface-hover/20\">\n {copy.cliManageHint}\n </p>\n ) : null}\n\n {ext.hasUi && (pages.length > 0 || settingsPanels.length > 0 || chatWidgets.length > 0) ? (\n <section className=\"mt-8\">\n <h4 className=\"mb-3 text-sm font-semibold text-fg\">{copy.detailSectionFeatures}</h4>\n <div className=\"flex flex-wrap gap-1.5\">\n <ContributionBadge\n template={copy.badgePages}\n count={pages.length}\n hidden={pages.length === 0}\n />\n <ContributionBadge\n template={copy.badgeSettings}\n count={settingsPanels.length}\n hidden={settingsPanels.length === 0}\n />\n <ContributionBadge\n template={copy.badgeWidgets}\n count={chatWidgets.length}\n hidden={chatWidgets.length === 0}\n />\n <ContributionBadge\n template={copy.badgeSidebar}\n count={sidebarPanels.length}\n hidden={sidebarPanels.length === 0}\n />\n </div>\n </section>\n ) : null}\n\n {extensionExposesGatewayShellUi(ext) && (openPath || settingsPath) ? (\n <div className=\"mt-6 flex flex-wrap gap-2 border-t border-edge-subtle pt-4\">\n {openPath ? (\n <Link\n to={openPath}\n onClick={onClose}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-lg border border-edge px-3 py-2 text-sm font-medium text-fg',\n 'hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent',\n )}\n >\n <ExternalLink className=\"size-4 shrink-0 opacity-80\" aria-hidden />\n {copy.open}\n </Link>\n ) : null}\n {settingsPath ? (\n <Link\n to={settingsPath}\n onClick={onClose}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-lg border border-edge px-3 py-2 text-sm font-medium text-fg',\n 'hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent',\n )}\n >\n <Settings className=\"size-4 shrink-0 opacity-80\" aria-hidden />\n {copy.openSettings}\n </Link>\n ) : null}\n </div>\n ) : null}\n\n <p className=\"mt-6 text-[11px] leading-relaxed text-fg-muted\">{copy.restartNote}</p>\n </div>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n\nfunction ContributionBadge({\n template,\n count,\n hidden,\n}: {\n template: string;\n count: number;\n hidden: boolean;\n}) {\n if (hidden || count === 0) return null;\n return (\n <span className=\"inline-flex items-center rounded-md bg-surface-hover px-2 py-0.5 text-[11px] font-medium text-fg-muted\">\n {template.replace(/\\{\\{count\\}\\}/g, String(count))}\n </span>\n );\n}\n\nfunction AppsPageSkeleton() {\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto bg-surface-panel\">\n <div className=\"mx-auto w-full max-w-app-main px-4 py-8\">\n <div className=\"mb-6 h-8 w-40 max-w-full animate-pulse rounded-md bg-surface-hover\" />\n <div className=\"mb-2 h-4 w-full max-w-md animate-pulse rounded bg-surface-hover\" />\n <div className=\"mb-5 h-9 w-full max-w-lg animate-pulse rounded-full bg-surface-hover\" />\n <div className=\"mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[0, 1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-36 animate-pulse rounded-xl border border-edge bg-surface-base\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nfunction EmptyAppsState({ message }: { message: string }) {\n return (\n <div className=\"flex min-h-[min(40vh,16rem)] flex-col items-center justify-center rounded-xl border border-dashed border-edge-subtle bg-surface-hover/40 px-4 py-12 text-center dark:bg-surface-hover/20\">\n <p className=\"text-sm text-fg-muted\">{message}</p>\n </div>\n );\n}\n"],"mappings":"2cAwBA,SAAgB,EAA0B,CAAE,aAAqC,CAC/E,IAAM,EAAW,EAAiB,GAAM,EAAQ,EAAE,MAAO,CACnD,CAAC,EAAG,IAAA,EAAA,EAAA,UAAiB,GAAG,CACxB,CAAC,EAAW,IAAA,EAAA,EAAA,UAAyB,GAAG,CACxC,EAAM,EAAW,eAAe,IAAc,MAEpD,EAAA,EAAA,eAAgB,CACd,IAAM,EAAI,OAAO,eAAiB,EAAa,EAAE,MAAM,CAAC,CAAE,IAAI,CAC9D,UAAa,OAAO,aAAa,EAAE,EAClC,CAAC,EAAE,CAAC,CAEP,GAAM,CAAE,OAAM,YAAW,SAAU,EACjC,EACA,SAKS,EAHL,EAAU,OAAS,EACf,EAAO,sBAAsB,mBAAmB,EAAU,GAAG,CAC7D,EAAO,mBAAmB,CACU,CAE5C,CAAE,kBAAmB,GAAO,CAC7B,CAEK,EAAa,GAAM,YAAc,EAAE,CAEzC,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAW,EAAG,sBAAuB,EAAU,UAApD,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oBAAf,EACE,EAAA,EAAA,KAAC,EAAD,CACE,UAAU,oFACV,YAAa,KACb,cAAA,GACA,CAAA,EACF,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,SACL,MAAO,EACP,SAAW,GAAM,EAAK,EAAE,OAAO,MAAM,CACrC,YAAY,qBACZ,UAAU,yHACV,CAAA,CACE,GAEL,GACC,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCACV,aAAiB,MAAQ,EAAM,QAAU,6BACxC,CAAA,CACF,KAEH,GAAa,CAAC,GACb,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAwB,WAAY,CAAA,EAEjD,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,+BACX,EAAW,SAAW,GACrB,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,iCAAwB,6BAA+B,CAAA,CAErE,EAAW,IAAK,IACd,EAAA,EAAA,MAAC,KAAD,CAEE,UAAU,4EAFZ,EAIE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,6DACb,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,6CAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,iCAAyB,EAAE,KAAU,CAAA,CAClD,EAAE,UACD,EAAA,EAAA,KAAC,EAAD,CACE,UAAU,yDACV,YAAa,KACb,aAAW,WACX,CAAA,CACA,KACH,EAAE,SACD,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iCAAyB,EAAE,QAAe,CAAA,CACxD,KACA,GACL,EAAE,aACD,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAE,YAAgB,CAAA,CAC3D,MACJ,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,wCACX,EAAE,YAAc,EAAE,EAAE,IAAK,IACzB,EAAA,EAAA,KAAC,OAAD,CAEE,UAAU,gGAET,EACI,CAJA,EAIA,CACP,CACE,CAAA,EACN,EAAA,EAAA,MAAC,IAAD,CAAG,UAAU,gEAAb,EACE,EAAA,EAAA,KAAC,EAAD,CAAS,UAAU,oBAAoB,YAAa,KAAM,cAAA,GAAc,CAAA,EACxE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,gDAAwC,EAAE,WAAkB,CAAA,CAC1E,GACH,EAAE,UACD,EAAA,EAAA,MAAC,IAAD,CACE,KAAM,EAAE,SACR,OAAO,SACP,IAAI,aACJ,UAAU,mFAJZ,EAME,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,WAAW,YAAa,KAAM,cAAA,GAAc,CAAA,CAAA,WAElE,GACF,KACA,GACF,CAAA,EACN,EAAA,EAAA,MAAC,IAAD,CAAG,UAAU,0CAAb,CAA8C,eAC/B,KACb,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,gDAAwC,EAAE,WAAkB,CAAA,CAC1E,GACD,EApDE,EAAE,GAoDJ,CACL,CAED,CAAA,CAEH,GCxGV,SAAgB,GAAW,CACzB,IAAM,EAAW,EAAgB,GAAM,EAAE,SAAS,CAC5C,EAAI,EAAS,EAAS,CACtB,EAAgB,EAAoB,GAAM,EAAE,cAAc,CAC1D,EAAkB,EAAoB,GAAM,EAAE,gBAAgB,CAC9D,EAAa,GAAe,CAC5B,EAAU,GAAsB,CAChC,CAAE,UAAW,GAAc,CAC3B,CAAC,EAAK,IAAA,EAAA,EAAA,UAA4B,MAAM,CACxC,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAsB,GAAG,CAClC,CAAC,EAAQ,IAAA,EAAA,EAAA,UAA8C,KAAK,CAE5D,GAAA,EAAA,EAAA,aAAyB,CAC7B,IAAI,EAAO,EACP,IAAQ,OAAM,EAAO,EAAK,OAAQ,GAAM,EAAE,MAAM,EAChD,IAAQ,YAAW,EAAO,EAAK,OAAQ,GAAM,CAAC,EAAE,MAAM,EAC1D,IAAM,EAAI,EAAO,MAAM,CAAC,aAAa,CASrC,OARI,IACF,EAAO,EAAK,OACT,IACE,EAAE,MAAQ,IAAI,aAAa,CAAC,SAAS,EAAE,EACxC,EAAE,GAAG,aAAa,CAAC,SAAS,EAAE,GAC7B,EAAE,aAAe,IAAI,aAAa,CAAC,SAAS,EAAE,CAClD,EAEI,CAAC,GAAG,EAAK,CAAC,MAAM,EAAG,IACpB,EAAE,QAAU,EAAE,OACV,EAAE,MAAQ,EAAE,IAAI,cAAc,EAAE,MAAQ,EAAE,GAAI,EAAS,CAD/B,EAAE,MAAQ,GAAK,EAE/C,EACD,CAAC,EAAY,EAAK,EAAQ,EAAS,CAAC,CAgCvC,OA7BA,EAAA,EAAA,eAAgB,CACd,EAAW,GAAS,CAClB,GAAI,CAAC,EAAM,OAAO,EAClB,IAAM,EAAO,EAAW,KAAM,GAAM,EAAE,KAAO,EAAK,GAAG,CAKrD,OAJK,EACY,EAAsB,EAEnC,GADa,EAAsB,EACtB,EAAY,EAAK,SAAW,EAAK,OAAe,EAC1D,EAJW,MAKlB,EACD,CAAC,EAAW,CAAC,EAEhB,EAAA,EAAA,sBACE,EAAc,CACZ,WAAY,KACZ,MACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,gDACb,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,2EAAmE,EAAE,SAAS,MAAW,CAAA,CACnG,CAAA,CAER,IAAK,KACN,CAAC,KACW,GAAiB,EAC7B,CAAC,EAAiB,EAAE,SAAS,MAAO,EAAc,CAAC,CAElD,GAAW,IAAQ,eACd,EAAA,EAAA,KAAC,EAAD,EAAoB,CAAA,EAI3B,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,yEAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mDAAf,EACE,EAAA,EAAA,MAAC,SAAD,CAAQ,UAAU,gBAAlB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAE,SAAS,MAAW,CAAA,EACrE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAE,SAAS,SAAa,CAAA,CAC5D,IAET,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mFAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,gCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAS,OAAQ,IAAQ,MAAO,YAAe,EAAO,MAAM,CAAE,MAAO,EAAE,SAAS,OAAU,CAAA,EAC1F,EAAA,EAAA,KAAC,EAAD,CACE,OAAQ,IAAQ,KAChB,YAAe,EAAO,KAAK,CAC3B,MAAO,EAAE,SAAS,UAClB,CAAA,EACF,EAAA,EAAA,KAAC,EAAD,CACE,OAAQ,IAAQ,UAChB,YAAe,EAAO,UAAU,CAChC,MAAO,EAAE,SAAS,WAClB,CAAA,EACF,EAAA,EAAA,KAAC,EAAD,CACE,OAAQ,IAAQ,cAChB,YAAe,EAAO,cAAc,CACpC,MAAO,EAAE,SAAS,eAClB,CAAA,CACE,GACL,IAAQ,eAAiB,EAAW,OAAS,GAC5C,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CACE,UAAU,oFACV,cAAA,GACA,CAAA,EACF,EAAA,EAAA,KAAC,QAAD,CACE,KAAK,SACL,MAAO,EACP,SAAW,GAAM,EAAU,EAAE,OAAO,MAAM,CAC1C,YAAa,EAAE,SAAS,kBACxB,UAAU,0LACV,aAAa,MACb,CAAA,CACE,GACJ,KACA,GAEL,IAAQ,eACP,EAAA,EAAA,KAAC,EAAD,EAA6B,CAAA,CAC3B,EAAW,SAAW,GACxB,EAAA,EAAA,KAAC,EAAD,CAAgB,QAAS,EAAE,SAAS,MAAS,CAAA,CAC3C,EAAS,SAAW,GACtB,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uJACV,EAAE,SAAS,gBACV,CAAA,EAEJ,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oDACZ,EAAS,IAAK,IACb,EAAA,EAAA,KAAC,EAAD,CAEE,UAAW,EACX,KAAM,EAAE,SACR,WAAc,EAAU,EAAI,CAC5B,CAJK,EAAI,GAIT,CACF,CACE,CAAA,CAEJ,GAEL,GACC,EAAA,EAAA,KAAC,EAAD,CAEE,UAAW,EACX,KAAM,EAAE,SACR,YAAe,EAAU,KAAK,CAC9B,cAAe,SAAY,CACzB,MAAM,EAAO,0BAA0B,EAEzC,CAPK,EAAO,GAOZ,CACA,KACA,GAIV,SAAS,EAAQ,CACf,SACA,UACA,SAKC,CACD,OACE,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACI,UACT,UAAW,EACT,wEACA,4EACA,EACI,iDACA,iFACL,UAEA,EACM,CAAA,CAIb,SAAS,EAAsB,EAA+B,CAC5D,OAAO,EAAI,oBAAsB,EAAI,OAGvC,SAAS,EAAc,EAAsB,EAA4B,CAIvE,OAHI,EAAI,SAAW,UAAkB,EAAK,gBACtC,EAAI,SAAW,SAAiB,EAAK,eACrC,EAAI,SAAW,YAAoB,EAAK,kBACrC,EAAK,cAGd,SAAS,EAAkB,EAAsB,EAA4B,CAC3E,IAAM,EAAW,EAAsB,EAAI,CAI3C,OAHI,GAAY,EAAI,OAAe,EAAK,aACpC,GAAY,CAAC,EAAI,OAAe,EAAK,kBACrC,CAAC,GAAY,EAAI,OAAe,EAAK,mBAClC,EAAK,YAGd,SAAS,EAAiB,CACxB,UAAW,EACX,OACA,UAKC,CACD,IAAM,EAAW,EAAsB,EAAI,CAE3C,OACE,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,UAAW,EACT,qHACA,+HACA,iCACD,WAED,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sBAAf,EACE,EAAA,EAAA,KAAC,MAAD,CACE,UAAU,qHACV,cAAA,aAEE,EAAI,MAAQ,EAAI,IAAI,OAAO,EAAE,CAAC,aAAa,CACzC,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kDAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mBAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,kDAA0C,EAAI,KAAU,CAAA,CACrE,EAAI,SACH,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,qCAAhB,CAA4C,IAAE,EAAI,QAAe,GAC/D,KACA,GACL,GACC,EAAA,EAAA,MAAC,OAAD,CAAM,UAAU,mIAAhB,EACE,EAAA,EAAA,KAAC,EAAD,CAAO,UAAU,SAAS,YAAa,IAAK,cAAA,GAAc,CAAA,CACzD,EAAK,cACD,IAEP,EAAA,EAAA,KAAC,OAAD,CACE,UAAU,mGACV,cAAA,aAEA,EAAA,EAAA,KAAC,EAAD,CAAM,UAAU,SAAS,YAAa,IAAO,CAAA,CACxC,CAAA,CAEL,IACN,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,mEACV,EAAI,aAAa,MAAM,EAAI,EAAK,kBAC/B,CAAA,EACJ,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uCAAf,EACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,mHACb,EAAI,MAAQ,EAAK,YAAc,EAAK,iBAChC,CAAA,CACN,EAAI,SAAW,WACd,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,mHACb,EAAK,aACD,CAAA,CACL,KACA,GACF,GACF,GACC,CAAA,CAIb,SAAS,EAAsB,CAC7B,UAAW,EACX,OACA,UACA,iBAMC,CACD,GAAM,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAsB,GAAM,CACrC,CAAC,EAAc,IAAA,EAAA,EAAA,UAA2C,KAAK,CAE/D,EAAQ,EAAI,IAAI,eAAe,OAAS,EAAE,CAC1C,EAAiB,EAAI,IAAI,eAAe,gBAAkB,EAAE,CAC5D,EAAc,EAAI,IAAI,eAAe,aAAe,EAAE,CACtD,EAAgB,EAAI,IAAI,eAAe,eAAiB,EAAE,CAC1D,EAA4C,EAAM,KAAM,GAAM,EAAE,UAAU,EAAI,EAAM,GACpF,EAAuB,EAAe,GACtC,EAAW,EAAc,EAAkB,EAAI,GAAI,EAAY,CAAG,KAClE,EAAe,EACjB,iBAAiB,EAAI,GAAG,GAAG,EAAqB,KAChD,EAAI,gBACF,iBAAiB,EAAI,KACrB,KAEA,EAAW,EAAsB,EAAI,CACrC,EAAY,EAAI,SAAW,UAE3B,EAAgB,KAAO,IAAkB,CAC7C,EAAgB,KAAK,CACrB,EAAU,GAAK,CACf,GAAI,CACF,MAAM,EACJ,EAAO,qCAAqC,CAC5C,CAAE,OAAQ,OAAQ,KAAM,KAAK,UAAU,CAAE,YAAa,EAAI,GAAI,QAAS,EAAM,CAAC,CAAE,CACjF,CACD,MAAM,GAAe,OACd,EAAG,CACV,EAAgB,aAAa,MAAQ,EAAE,QAAU,EAAK,YAAY,QAC1D,CACR,EAAU,GAAM,GAIpB,OACE,EAAA,EAAA,KAAC,EAAD,CAAa,YAAA,GAAY,aAAe,GAAM,CAAC,GAAK,GAAS,WAC3D,EAAA,EAAA,MAAC,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAgB,UAAU,qDAAuD,CAAA,EACjF,EAAA,EAAA,MAAC,EAAD,CACE,UAAU,0NACV,gBAAkB,GAAM,EAAE,gBAAgB,UAF5C,EAIE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,2FAAf,EACE,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,UAAU,yLACV,aAAY,EAAK,qBAEjB,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,SAAW,CAAA,CACzB,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,6EACrB,EAAK,YACO,CAAA,EACf,EAAA,EAAA,MAAC,EAAD,CAAoB,UAAU,mBAA9B,CACG,EAAI,KAAK,KAAG,EAAI,GAAG,IACD,IACrB,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,UAAU,yLACV,aAAY,EAAK,sBAEjB,EAAA,EAAA,KAAC,EAAD,CAAG,UAAU,SAAW,CAAA,CACjB,CAAA,CACL,IAEN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oDAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,4CAAf,EACE,EAAA,EAAA,KAAC,MAAD,CACE,UAAU,oHACV,cAAA,aAEE,EAAI,MAAQ,EAAI,IAAI,OAAO,EAAE,CAAC,aAAa,CACzC,CAAA,EACN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0BAAf,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,yCAAiC,EAAI,KAAU,CAAA,EAC7D,EAAA,EAAA,MAAC,IAAD,CAAG,UAAU,wCAAb,CACG,EAAK,qBAAqB,IAAE,EAAc,EAAK,EAAK,CACnD,GACH,EAAI,SACH,EAAA,EAAA,MAAC,IAAD,CAAG,UAAU,sCAAb,CAA0C,IAAE,EAAI,QAAY,GAC1D,KACA,GACL,GACC,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,4EACb,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,SAAU,EACV,YAAW,EACX,YAAe,KAAK,EAAc,CAAC,EAAS,CAC5C,UAAW,EACT,0EACA,iFACA,gFACA,EACI,8GACA,8FACJ,GAAU,yBACX,UAbH,EAeE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,iEACb,EAAW,EAAK,cAAgB,EAAK,aACjC,CAAA,EACP,EAAA,EAAA,KAAC,EAAD,CACE,UAAW,EACT,+FACA,EAAS,eAAiB,YAC3B,CACD,cAAA,GACA,CAAA,CACK,GACL,CAAA,CACJ,KACA,IAEN,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,gDACV,EAAI,aAAa,MAAM,EAAI,EAAK,oBAC/B,CAAA,CAEF,EAAI,MAEF,MADF,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAK,gBAAoB,CAAA,EAGtE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAkB,EAAK,EAAK,CAAK,CAAA,CAE3E,GACC,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,mFAAmF,KAAK,iBAClG,EACC,CAAA,CACF,KAEF,EAIE,MAHF,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,kIACV,EAAK,cACJ,CAAA,CAGL,EAAI,QAAU,EAAM,OAAS,GAAK,EAAe,OAAS,GAAK,EAAY,OAAS,IACnF,EAAA,EAAA,MAAC,UAAD,CAAS,UAAU,gBAAnB,EACE,EAAA,EAAA,KAAC,KAAD,CAAI,UAAU,8CAAsC,EAAK,sBAA2B,CAAA,EACpF,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kCAAf,EACE,EAAA,EAAA,KAAC,EAAD,CACE,SAAU,EAAK,WACf,MAAO,EAAM,OACb,OAAQ,EAAM,SAAW,EACzB,CAAA,EACF,EAAA,EAAA,KAAC,EAAD,CACE,SAAU,EAAK,cACf,MAAO,EAAe,OACtB,OAAQ,EAAe,SAAW,EAClC,CAAA,EACF,EAAA,EAAA,KAAC,EAAD,CACE,SAAU,EAAK,aACf,MAAO,EAAY,OACnB,OAAQ,EAAY,SAAW,EAC/B,CAAA,EACF,EAAA,EAAA,KAAC,EAAD,CACE,SAAU,EAAK,aACf,MAAO,EAAc,OACrB,OAAQ,EAAc,SAAW,EACjC,CAAA,CACE,GACE,GACR,KAEH,EAA+B,EAAI,GAAK,GAAY,IACnD,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,sEAAf,CACG,GACC,EAAA,EAAA,MAAC,EAAD,CACE,GAAI,EACJ,QAAS,EACT,UAAW,EACT,uGACA,mGACD,UANH,EAQE,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,6BAA6B,cAAA,GAAc,CAAA,CAClE,EAAK,KACD,GACL,KACH,GACC,EAAA,EAAA,MAAC,EAAD,CACE,GAAI,EACJ,QAAS,EACT,UAAW,EACT,uGACA,mGACD,UANH,EAQE,EAAA,EAAA,KAAC,EAAD,CAAU,UAAU,6BAA6B,cAAA,GAAc,CAAA,CAC9D,EAAK,aACD,GACL,KACA,GACJ,MAEJ,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,0DAAkD,EAAK,YAAgB,CAAA,CAChF,GACS,GACH,CAAA,CAAA,CACJ,CAAA,CAIlB,SAAS,EAAkB,CACzB,WACA,QACA,UAKC,CAED,OADI,GAAU,IAAU,EAAU,MAEhC,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,kHACb,EAAS,QAAQ,iBAAkB,OAAO,EAAM,CAAC,CAC7C,CAAA,CAIX,SAAS,GAAmB,CAC1B,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,0EACb,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mDAAf,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,qEAAuE,CAAA,EACtF,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,kEAAoE,CAAA,EACnF,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,uEAAyE,CAAA,EACxF,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,yDACZ,CAAC,EAAG,EAAG,EAAG,EAAG,EAAG,EAAE,CAAC,IAAK,IACvB,EAAA,EAAA,KAAC,MAAD,CAAa,UAAU,mEAAqE,CAAlF,EAAkF,CAC5F,CACE,CAAA,CACF,GACF,CAAA,CAIV,SAAS,EAAe,CAAE,WAAgC,CACxD,OACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,qMACb,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,iCAAyB,EAAY,CAAA,CAC9C,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/pdf
|
|
2
|
-
import{Cn as e}from"./vendor-codemirror-CXAvob9m.js";import{t}from"./excel-worksheet-utils-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/pdf-CX6ji-QC.js","assets/vendor-codemirror-CXAvob9m.js","assets/rolldown-runtime-DWdDZTNf.js","assets/docx-preview-F-jKDMNv.js","assets/jszip.min-CL3dfyxs.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{Cn as e}from"./vendor-codemirror-CXAvob9m.js";import{t}from"./excel-worksheet-utils-DPfAinZn.js";var n=!1,r=5,i=5;async function a(){let t=await e(()=>import(`./pdf-CX6ji-QC.js`),__vite__mapDeps([0,1,2]));return n||=(t.GlobalWorkerOptions.workerSrc=new URL(`/assets/pdf.worker.min-FHbmGBN0.mjs`,``+import.meta.url).toString(),!0),t}function o(){return new Promise(e=>requestAnimationFrame(()=>e()))}async function s(e,t,n){let s=(await a()).getDocument({data:t}),c=s;e.innerHTML=``;let l=document.createElement(`p`);l.className=`p-4 text-sm text-fg-muted`,l.textContent=n?.loadingText??`…`,e.appendChild(l);let u=null,d=null;try{u=await s.promise}catch(e){throw c?.destroy(),c=null,e}c=null,e.innerHTML=``;let f=document.createElement(`div`);e.appendChild(f);let p=document.createElement(`div`);f.appendChild(p);let m=async(e,t)=>{if(!u)return;let n=await u.getPage(e),r=document.createElement(`div`);r.className=`mb-4 last:mb-0`;let i=document.createElement(`canvas`),a=i.getContext(`2d`),o=n.getViewport({scale:1.5});if(i.height=o.height,i.width=o.width,i.className=`mx-auto block h-auto w-full max-w-full rounded border border-edge bg-white shadow-sm dark:border-edge`,a&&(a.fillStyle=`white`,a.fillRect(0,0,i.width,i.height)),await n.render({canvasContext:a,viewport:o,canvas:i}).promise,r.appendChild(i),e<t){let e=document.createElement(`div`);e.className=`my-4 h-px bg-edge`,r.appendChild(e)}p.appendChild(r)},h=u.numPages,g=Math.min(r,h);for(let e=1;e<=g;e++)await m(e,h),await o();let _=g+1;if(_<=h){let t=document.createElement(`p`);t.className=`py-2 text-center text-xs text-fg-muted`,t.textContent=n?.loadMoreHint??``,f.appendChild(t);let r=document.createElement(`div`);r.className=`pdf-lazy-sentinel h-12 w-full shrink-0`,r.setAttribute(`aria-hidden`,`true`),f.appendChild(r);let a=!1;d=new IntersectionObserver(async e=>{if(!(!e[0]?.isIntersecting||a||!u)){a=!0;try{let e=Math.min(_+i-1,h);for(let t=_;t<=e;t++)await m(t,h),await o();_=e+1,_>h&&(d?.disconnect(),d=null,t.remove(),r.remove())}finally{a=!1}}},{root:e,rootMargin:`320px`,threshold:0}),d.observe(r)}return{cleanup:()=>{if(d?.disconnect(),d=null,c){try{c.destroy()}catch{}c=null}u&&=(u.destroy(),null),e.innerHTML=``}}}async function c(t,n){let{renderAsync:r,defaultOptions:i}=await e(async()=>{let{renderAsync:e,defaultOptions:t}=await import(`./docx-preview-F-jKDMNv.js`);return{renderAsync:e,defaultOptions:t}},__vite__mapDeps([3,2,4]));t.innerHTML=``;let a=document.createElement(`div`);a.className=`docx-wrapper-custom max-w-full overflow-x-auto`,t.appendChild(a),await r(n,a,void 0,{...i,className:`docx`,inWrapper:!0,ignoreWidth:!0,ignoreHeight:!1,ignoreFonts:!0,breakPages:!0,ignoreLastRenderedPageBreak:!0,experimental:!1,trimXmlDeclaration:!0,useBase64URL:!1,renderHeaders:!0,renderFooters:!0,renderFootnotes:!0,renderEndnotes:!0});let o=document.createElement(`style`);return o.textContent=`
|
|
3
3
|
.docx-preview-host { padding: 0; }
|
|
4
4
|
.docx-preview-host .docx-wrapper-custom { max-width: 100%; overflow-x: auto; }
|
|
5
5
|
.docx-preview-host .docx-wrapper { max-width: 100% !important; margin: 0 !important; background: transparent !important; padding: 0em !important; }
|
|
@@ -8,5 +8,5 @@ import{Cn as e}from"./vendor-codemirror-CXAvob9m.js";import{t}from"./excel-works
|
|
|
8
8
|
.docx-preview-host img { max-width: 100% !important; height: auto !important; }
|
|
9
9
|
.docx-preview-host p, .docx-preview-host span, .docx-preview-host div, .docx-preview-host td, .docx-preview-host th { max-width: 100% !important; word-wrap: break-word !important; overflow-wrap: break-word !important; font-family: inherit !important; }
|
|
10
10
|
.docx-preview-host .docx-page-break { display: none !important; }
|
|
11
|
-
`,t.classList.add(`docx-preview-host`),t.appendChild(o),{cleanup:()=>{t.innerHTML=``,t.classList.remove(`docx-preview-host`)}}}async function l(t,n,r){let i=await e(()=>import(`./xlsx-
|
|
12
|
-
//# sourceMappingURL=attachment-preview-renderer-
|
|
11
|
+
`,t.classList.add(`docx-preview-host`),t.appendChild(o),{cleanup:()=>{t.innerHTML=``,t.classList.remove(`docx-preview-host`)}}}async function l(t,n,r){let i=await e(()=>import(`./xlsx-CPtvfmVF.js`),[]),a=i.read(n,{type:`array`});t.innerHTML=``;let o=document.createElement(`div`);o.className=`flex h-full min-h-0 flex-col overflow-auto`,t.appendChild(o);let s=a.SheetNames??[];if(s.length===0){let e=document.createElement(`p`);return e.className=`p-4 text-sm text-fg-muted`,e.textContent=`(No sheets in workbook.)`,o.appendChild(e),{truncated:!1,cleanup:()=>{t.innerHTML=``}}}let c=!1;if(s.length>1){let e=document.createElement(`div`);e.className=`sticky top-0 z-10 mb-4 flex gap-2 border-b border-edge bg-surface-panel dark:border-edge`;let t=[];s.forEach((n,o)=>{let s=document.createElement(`button`);s.type=`button`,s.textContent=n,s.className=o===0?`border-b-2 border-accent px-4 py-2 text-sm font-medium text-accent`:`border-b-2 border-transparent px-4 py-2 text-sm font-medium text-fg-muted hover:border-edge hover:text-fg`;let l=document.createElement(`div`);l.style.display=o===0?`flex`:`none`,l.className=`min-h-0 flex-1 overflow-auto`;let d=u(i,a.Sheets[n],n,r?.truncationNotice);d.truncated&&(c=!0),l.appendChild(d.element),t.push(l),s.onclick=()=>{e.querySelectorAll(`button`).forEach((e,t)=>{t===o?e.className=`border-b-2 border-accent px-4 py-2 text-sm font-medium text-accent`:e.className=`border-b-2 border-transparent px-4 py-2 text-sm font-medium text-fg-muted hover:border-edge hover:text-fg`}),t.forEach((e,t)=>{e.style.display=t===o?`flex`:`none`})},e.appendChild(s)}),o.appendChild(e),t.forEach(e=>{o.appendChild(e)})}else{let e=s[0],t=u(i,a.Sheets[e],e,r?.truncationNotice);c=t.truncated,o.appendChild(t.element)}return{truncated:c,cleanup:()=>{t.innerHTML=``}}}function u(e,n,r,i){let a=document.createElement(`div`);if(!n){let e=document.createElement(`p`);return e.className=`p-4 text-sm text-fg-muted`,e.textContent=`Sheet "${r}" is missing from the workbook.`,a.appendChild(e),{element:a,truncated:!1}}if(!t(n)){let e=document.createElement(`p`);return e.className=`p-4 text-sm text-fg-muted`,e.textContent=`(Empty sheet — no cell range.)`,a.appendChild(e),{element:a,truncated:!1}}try{let t=e.utils.sheet_to_json(n,{header:1,defval:``}),r=t.reduce((e,t)=>Math.max(e,t.length),0),o=t.length>500||r>64;if(o&&i){let e=document.createElement(`p`);e.className=`mb-2 border-b border-edge-subtle px-1 pb-2 text-xs text-fg-muted dark:border-edge`,e.textContent=i,a.appendChild(e)}let s=t.slice(0,500).map(e=>e.slice(0,64)),c=document.createElement(`table`);return c.className=`w-full border-collapse text-fg`,s.forEach((e,t)=>{let n=document.createElement(`tr`);t%2==1&&(n.className=`bg-surface-hover/40`),e.forEach(e=>{let t=document.createElement(`td`);t.className=`border border-edge px-3 py-2 text-left text-sm dark:border-edge`,t.textContent=e==null?``:String(e),n.appendChild(t)}),c.appendChild(n)}),a.appendChild(c),{element:a,truncated:o}}catch(e){let t=document.createElement(`p`);return t.className=`p-4 text-sm text-red-600 dark:text-red-400`,t.textContent=e instanceof Error?e.message:String(e),a.appendChild(t),{element:a,truncated:!1}}}export{c as renderDocxInContainer,l as renderExcelInContainer,s as renderPdfInContainer};
|
|
12
|
+
//# sourceMappingURL=attachment-preview-renderer-CxMJMbD2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../web/src/features/chat/attachment-preview-renderer.ts"],"sourcesContent":["/**\n * Full-document preview for the attachment dialog (PDF canvas / DOCX HTML / Excel table).\n * Kept separate from the dialog shell so Vite can split this into async chunks.\n */\n\nimport {\n EXCEL_PREVIEW_MAX_COLS,\n EXCEL_PREVIEW_MAX_ROWS,\n} from '@/features/chat/attachment-utils-core';\nimport { isRenderableWorksheet } from '@/features/chat/excel-worksheet-utils';\n\nlet pdfWorkerConfigured = false;\n\n/** First paint: render this many pages, then lazy-load the rest when the viewport nears the sentinel. */\nconst PDF_INITIAL_PAGE_COUNT = 5;\nconst PDF_LAZY_PAGE_BATCH = 5;\n\nasync function ensurePdfWorker(): Promise<typeof import('pdfjs-dist')> {\n const pdfjsLib = await import('pdfjs-dist');\n if (!pdfWorkerConfigured) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n ).toString();\n pdfWorkerConfigured = true;\n }\n return pdfjsLib;\n}\n\nfunction yieldToBrowser(): Promise<void> {\n return new Promise((resolve) => requestAnimationFrame(() => resolve()));\n}\n\nexport type RenderPdfInContainerOptions = {\n loadingText?: string;\n loadMoreHint?: string;\n};\n\nexport async function renderPdfInContainer(\n container: HTMLDivElement,\n arrayBuffer: ArrayBuffer,\n options?: RenderPdfInContainerOptions,\n): Promise<{ cleanup: () => void }> {\n const pdfjsLib = await ensurePdfWorker();\n const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });\n let pendingLoadTask: { destroy: () => void } | null = loadingTask;\n\n container.innerHTML = '';\n const loadingEl = document.createElement('p');\n loadingEl.className = 'p-4 text-sm text-fg-muted';\n loadingEl.textContent = options?.loadingText ?? '…';\n container.appendChild(loadingEl);\n\n let pdf: import('pdfjs-dist').PDFDocumentProxy | null = null;\n let observer: IntersectionObserver | null = null;\n\n try {\n pdf = await loadingTask.promise;\n } catch (e) {\n pendingLoadTask?.destroy();\n pendingLoadTask = null;\n throw e;\n }\n pendingLoadTask = null;\n\n container.innerHTML = '';\n const wrapper = document.createElement('div');\n container.appendChild(wrapper);\n const pagesHost = document.createElement('div');\n wrapper.appendChild(pagesHost);\n\n const renderPageToHost = async (pageNum: number, numPagesTotal: number) => {\n if (!pdf) return;\n const page = await pdf.getPage(pageNum);\n\n const pageContainer = document.createElement('div');\n pageContainer.className = 'mb-4 last:mb-0';\n\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n\n const viewport = page.getViewport({ scale: 1.5 });\n canvas.height = viewport.height;\n canvas.width = viewport.width;\n\n canvas.className =\n 'mx-auto block h-auto w-full max-w-full rounded border border-edge bg-white shadow-sm dark:border-edge';\n\n if (context) {\n context.fillStyle = 'white';\n context.fillRect(0, 0, canvas.width, canvas.height);\n }\n\n await page\n .render({\n canvasContext: context!,\n viewport,\n canvas: canvas,\n })\n .promise;\n\n pageContainer.appendChild(canvas);\n\n if (pageNum < numPagesTotal) {\n const separator = document.createElement('div');\n separator.className = 'my-4 h-px bg-edge';\n pageContainer.appendChild(separator);\n }\n\n pagesHost.appendChild(pageContainer);\n };\n\n const numPages = pdf.numPages;\n const initialEnd = Math.min(PDF_INITIAL_PAGE_COUNT, numPages);\n\n for (let pageNum = 1; pageNum <= initialEnd; pageNum++) {\n await renderPageToHost(pageNum, numPages);\n await yieldToBrowser();\n }\n\n let nextPage = initialEnd + 1;\n\n if (nextPage <= numPages) {\n const hint = document.createElement('p');\n hint.className = 'py-2 text-center text-xs text-fg-muted';\n hint.textContent = options?.loadMoreHint ?? '';\n wrapper.appendChild(hint);\n\n const sentinel = document.createElement('div');\n sentinel.className = 'pdf-lazy-sentinel h-12 w-full shrink-0';\n sentinel.setAttribute('aria-hidden', 'true');\n wrapper.appendChild(sentinel);\n\n let loadingMore = false;\n observer = new IntersectionObserver(\n async (entries) => {\n if (!entries[0]?.isIntersecting || loadingMore || !pdf) return;\n loadingMore = true;\n try {\n const batchEnd = Math.min(nextPage + PDF_LAZY_PAGE_BATCH - 1, numPages);\n for (let p = nextPage; p <= batchEnd; p++) {\n await renderPageToHost(p, numPages);\n await yieldToBrowser();\n }\n nextPage = batchEnd + 1;\n if (nextPage > numPages) {\n observer?.disconnect();\n observer = null;\n hint.remove();\n sentinel.remove();\n }\n } finally {\n loadingMore = false;\n }\n },\n { root: container, rootMargin: '320px', threshold: 0 },\n );\n observer.observe(sentinel);\n }\n\n return {\n cleanup: () => {\n observer?.disconnect();\n observer = null;\n if (pendingLoadTask) {\n try {\n pendingLoadTask.destroy();\n } catch {\n /* ignore */\n }\n pendingLoadTask = null;\n }\n if (pdf) {\n pdf.destroy();\n pdf = null;\n }\n container.innerHTML = '';\n },\n };\n}\n\nexport async function renderDocxInContainer(\n container: HTMLDivElement,\n arrayBuffer: ArrayBuffer,\n): Promise<{ cleanup: () => void }> {\n const { renderAsync, defaultOptions } = await import('docx-preview');\n\n container.innerHTML = '';\n const wrapper = document.createElement('div');\n wrapper.className = 'docx-wrapper-custom max-w-full overflow-x-auto';\n container.appendChild(wrapper);\n\n await renderAsync(arrayBuffer, wrapper, undefined, {\n ...defaultOptions,\n className: 'docx',\n inWrapper: true,\n ignoreWidth: true,\n ignoreHeight: false,\n // Embedded Word fonts often fail to load in the browser; ignoring them uses system fonts so CJK/Latin render correctly.\n ignoreFonts: true,\n breakPages: true,\n ignoreLastRenderedPageBreak: true,\n experimental: false,\n trimXmlDeclaration: true,\n useBase64URL: false,\n renderHeaders: true,\n renderFooters: true,\n renderFootnotes: true,\n renderEndnotes: true,\n });\n\n const style = document.createElement('style');\n style.textContent = `\n .docx-preview-host { padding: 0; }\n .docx-preview-host .docx-wrapper-custom { max-width: 100%; overflow-x: auto; }\n .docx-preview-host .docx-wrapper { max-width: 100% !important; margin: 0 !important; background: transparent !important; padding: 0em !important; }\n .docx-preview-host .docx-wrapper > section.docx { box-shadow: none !important; border: none !important; border-radius: 0 !important; margin: 0 !important; padding: 2em !important; background: white !important; color: black !important; max-width: 100% !important; width: 100% !important; min-width: 0 !important; overflow-x: auto !important; font-family: system-ui, -apple-system, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", \"Noto Sans CJK JP\", sans-serif !important; }\n .docx-preview-host table { max-width: 100% !important; width: auto !important; overflow-x: auto !important; display: block !important; font-family: inherit !important; }\n .docx-preview-host img { max-width: 100% !important; height: auto !important; }\n .docx-preview-host p, .docx-preview-host span, .docx-preview-host div, .docx-preview-host td, .docx-preview-host th { max-width: 100% !important; word-wrap: break-word !important; overflow-wrap: break-word !important; font-family: inherit !important; }\n .docx-preview-host .docx-page-break { display: none !important; }\n `;\n container.classList.add('docx-preview-host');\n container.appendChild(style);\n\n return {\n cleanup: () => {\n container.innerHTML = '';\n container.classList.remove('docx-preview-host');\n },\n };\n}\n\nexport type RenderExcelInContainerOptions = {\n truncationNotice?: string;\n};\n\nexport async function renderExcelInContainer(\n container: HTMLDivElement,\n arrayBuffer: ArrayBuffer,\n options?: RenderExcelInContainerOptions,\n): Promise<{ cleanup: () => void; truncated: boolean }> {\n const XLSX = await import('xlsx');\n const workbook = XLSX.read(arrayBuffer, { type: 'array' });\n\n container.innerHTML = '';\n const wrapper = document.createElement('div');\n wrapper.className = 'flex h-full min-h-0 flex-col overflow-auto';\n container.appendChild(wrapper);\n\n const names = workbook.SheetNames ?? [];\n if (names.length === 0) {\n const empty = document.createElement('p');\n empty.className = 'p-4 text-sm text-fg-muted';\n empty.textContent = '(No sheets in workbook.)';\n wrapper.appendChild(empty);\n return {\n truncated: false,\n cleanup: () => {\n container.innerHTML = '';\n },\n };\n }\n\n let anyTruncated = false;\n\n if (names.length > 1) {\n const tabContainer = document.createElement('div');\n tabContainer.className =\n 'sticky top-0 z-10 mb-4 flex gap-2 border-b border-edge bg-surface-panel dark:border-edge';\n\n const sheetContents: HTMLElement[] = [];\n\n names.forEach((sheetName, index) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.textContent = sheetName;\n tab.className =\n index === 0\n ? 'border-b-2 border-accent px-4 py-2 text-sm font-medium text-accent'\n : 'border-b-2 border-transparent px-4 py-2 text-sm font-medium text-fg-muted hover:border-edge hover:text-fg';\n\n const sheetDiv = document.createElement('div');\n sheetDiv.style.display = index === 0 ? 'flex' : 'none';\n sheetDiv.className = 'min-h-0 flex-1 overflow-auto';\n const built = buildExcelSheetDomWithXlsx(\n XLSX,\n workbook.Sheets[sheetName],\n sheetName,\n options?.truncationNotice,\n );\n if (built.truncated) anyTruncated = true;\n sheetDiv.appendChild(built.element);\n sheetContents.push(sheetDiv);\n\n tab.onclick = () => {\n tabContainer.querySelectorAll('button').forEach((btn, btnIndex) => {\n if (btnIndex === index) {\n btn.className =\n 'border-b-2 border-accent px-4 py-2 text-sm font-medium text-accent';\n } else {\n btn.className =\n 'border-b-2 border-transparent px-4 py-2 text-sm font-medium text-fg-muted hover:border-edge hover:text-fg';\n }\n });\n sheetContents.forEach((content, contentIndex) => {\n content.style.display = contentIndex === index ? 'flex' : 'none';\n });\n };\n\n tabContainer.appendChild(tab);\n });\n\n wrapper.appendChild(tabContainer);\n sheetContents.forEach((content) => {\n wrapper.appendChild(content);\n });\n } else {\n const sheetName = names[0];\n const built = buildExcelSheetDomWithXlsx(\n XLSX,\n workbook.Sheets[sheetName],\n sheetName,\n options?.truncationNotice,\n );\n anyTruncated = built.truncated;\n wrapper.appendChild(built.element);\n }\n\n return {\n truncated: anyTruncated,\n cleanup: () => {\n container.innerHTML = '';\n },\n };\n}\n\nfunction buildExcelSheetDomWithXlsx(\n XLSX: typeof import('xlsx'),\n worksheet: import('xlsx').WorkSheet | undefined,\n sheetName: string,\n truncationNotice?: string,\n): { element: HTMLElement; truncated: boolean } {\n const sheetDiv = document.createElement('div');\n\n if (!worksheet) {\n const p = document.createElement('p');\n p.className = 'p-4 text-sm text-fg-muted';\n p.textContent = `Sheet \"${sheetName}\" is missing from the workbook.`;\n sheetDiv.appendChild(p);\n return { element: sheetDiv, truncated: false };\n }\n\n if (!isRenderableWorksheet(worksheet)) {\n const p = document.createElement('p');\n p.className = 'p-4 text-sm text-fg-muted';\n p.textContent = '(Empty sheet — no cell range.)';\n sheetDiv.appendChild(p);\n return { element: sheetDiv, truncated: false };\n }\n\n try {\n const rows: string[][] = XLSX.utils.sheet_to_json(worksheet, {\n header: 1,\n defval: '',\n });\n\n const rawMaxCol = rows.reduce((m, r) => Math.max(m, r.length), 0);\n const truncated =\n rows.length > EXCEL_PREVIEW_MAX_ROWS || rawMaxCol > EXCEL_PREVIEW_MAX_COLS;\n\n if (truncated && truncationNotice) {\n const note = document.createElement('p');\n note.className = 'mb-2 border-b border-edge-subtle px-1 pb-2 text-xs text-fg-muted dark:border-edge';\n note.textContent = truncationNotice;\n sheetDiv.appendChild(note);\n }\n\n const sliced = rows\n .slice(0, EXCEL_PREVIEW_MAX_ROWS)\n .map((row) => row.slice(0, EXCEL_PREVIEW_MAX_COLS));\n\n const table = document.createElement('table');\n table.className = 'w-full border-collapse text-fg';\n\n sliced.forEach((row, ri) => {\n const tr = document.createElement('tr');\n if (ri % 2 === 1) {\n tr.className = 'bg-surface-hover/40';\n }\n row.forEach((cell) => {\n const td = document.createElement('td');\n td.className = 'border border-edge px-3 py-2 text-left text-sm dark:border-edge';\n td.textContent = cell === null || cell === undefined ? '' : String(cell);\n tr.appendChild(td);\n });\n table.appendChild(tr);\n });\n\n sheetDiv.appendChild(table);\n return { element: sheetDiv, truncated };\n } catch (e) {\n const p = document.createElement('p');\n p.className = 'p-4 text-sm text-red-600 dark:text-red-400';\n p.textContent = e instanceof Error ? e.message : String(e);\n sheetDiv.appendChild(p);\n return { element: sheetDiv, truncated: false };\n }\n}\n"],"file":"attachment-preview-renderer-CebH7fCz.js"}
|
|
1
|
+
{"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../web/src/features/chat/attachment-preview-renderer.ts"],"sourcesContent":["/**\n * Full-document preview for the attachment dialog (PDF canvas / DOCX HTML / Excel table).\n * Kept separate from the dialog shell so Vite can split this into async chunks.\n */\n\nimport {\n EXCEL_PREVIEW_MAX_COLS,\n EXCEL_PREVIEW_MAX_ROWS,\n} from '@/features/chat/attachment-utils-core';\nimport { isRenderableWorksheet } from '@/features/chat/excel-worksheet-utils';\n\nlet pdfWorkerConfigured = false;\n\n/** First paint: render this many pages, then lazy-load the rest when the viewport nears the sentinel. */\nconst PDF_INITIAL_PAGE_COUNT = 5;\nconst PDF_LAZY_PAGE_BATCH = 5;\n\nasync function ensurePdfWorker(): Promise<typeof import('pdfjs-dist')> {\n const pdfjsLib = await import('pdfjs-dist');\n if (!pdfWorkerConfigured) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n ).toString();\n pdfWorkerConfigured = true;\n }\n return pdfjsLib;\n}\n\nfunction yieldToBrowser(): Promise<void> {\n return new Promise((resolve) => requestAnimationFrame(() => resolve()));\n}\n\nexport type RenderPdfInContainerOptions = {\n loadingText?: string;\n loadMoreHint?: string;\n};\n\nexport async function renderPdfInContainer(\n container: HTMLDivElement,\n arrayBuffer: ArrayBuffer,\n options?: RenderPdfInContainerOptions,\n): Promise<{ cleanup: () => void }> {\n const pdfjsLib = await ensurePdfWorker();\n const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });\n let pendingLoadTask: { destroy: () => void } | null = loadingTask;\n\n container.innerHTML = '';\n const loadingEl = document.createElement('p');\n loadingEl.className = 'p-4 text-sm text-fg-muted';\n loadingEl.textContent = options?.loadingText ?? '…';\n container.appendChild(loadingEl);\n\n let pdf: import('pdfjs-dist').PDFDocumentProxy | null = null;\n let observer: IntersectionObserver | null = null;\n\n try {\n pdf = await loadingTask.promise;\n } catch (e) {\n pendingLoadTask?.destroy();\n pendingLoadTask = null;\n throw e;\n }\n pendingLoadTask = null;\n\n container.innerHTML = '';\n const wrapper = document.createElement('div');\n container.appendChild(wrapper);\n const pagesHost = document.createElement('div');\n wrapper.appendChild(pagesHost);\n\n const renderPageToHost = async (pageNum: number, numPagesTotal: number) => {\n if (!pdf) return;\n const page = await pdf.getPage(pageNum);\n\n const pageContainer = document.createElement('div');\n pageContainer.className = 'mb-4 last:mb-0';\n\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n\n const viewport = page.getViewport({ scale: 1.5 });\n canvas.height = viewport.height;\n canvas.width = viewport.width;\n\n canvas.className =\n 'mx-auto block h-auto w-full max-w-full rounded border border-edge bg-white shadow-sm dark:border-edge';\n\n if (context) {\n context.fillStyle = 'white';\n context.fillRect(0, 0, canvas.width, canvas.height);\n }\n\n await page\n .render({\n canvasContext: context!,\n viewport,\n canvas: canvas,\n })\n .promise;\n\n pageContainer.appendChild(canvas);\n\n if (pageNum < numPagesTotal) {\n const separator = document.createElement('div');\n separator.className = 'my-4 h-px bg-edge';\n pageContainer.appendChild(separator);\n }\n\n pagesHost.appendChild(pageContainer);\n };\n\n const numPages = pdf.numPages;\n const initialEnd = Math.min(PDF_INITIAL_PAGE_COUNT, numPages);\n\n for (let pageNum = 1; pageNum <= initialEnd; pageNum++) {\n await renderPageToHost(pageNum, numPages);\n await yieldToBrowser();\n }\n\n let nextPage = initialEnd + 1;\n\n if (nextPage <= numPages) {\n const hint = document.createElement('p');\n hint.className = 'py-2 text-center text-xs text-fg-muted';\n hint.textContent = options?.loadMoreHint ?? '';\n wrapper.appendChild(hint);\n\n const sentinel = document.createElement('div');\n sentinel.className = 'pdf-lazy-sentinel h-12 w-full shrink-0';\n sentinel.setAttribute('aria-hidden', 'true');\n wrapper.appendChild(sentinel);\n\n let loadingMore = false;\n observer = new IntersectionObserver(\n async (entries) => {\n if (!entries[0]?.isIntersecting || loadingMore || !pdf) return;\n loadingMore = true;\n try {\n const batchEnd = Math.min(nextPage + PDF_LAZY_PAGE_BATCH - 1, numPages);\n for (let p = nextPage; p <= batchEnd; p++) {\n await renderPageToHost(p, numPages);\n await yieldToBrowser();\n }\n nextPage = batchEnd + 1;\n if (nextPage > numPages) {\n observer?.disconnect();\n observer = null;\n hint.remove();\n sentinel.remove();\n }\n } finally {\n loadingMore = false;\n }\n },\n { root: container, rootMargin: '320px', threshold: 0 },\n );\n observer.observe(sentinel);\n }\n\n return {\n cleanup: () => {\n observer?.disconnect();\n observer = null;\n if (pendingLoadTask) {\n try {\n pendingLoadTask.destroy();\n } catch {\n /* ignore */\n }\n pendingLoadTask = null;\n }\n if (pdf) {\n pdf.destroy();\n pdf = null;\n }\n container.innerHTML = '';\n },\n };\n}\n\nexport async function renderDocxInContainer(\n container: HTMLDivElement,\n arrayBuffer: ArrayBuffer,\n): Promise<{ cleanup: () => void }> {\n const { renderAsync, defaultOptions } = await import('docx-preview');\n\n container.innerHTML = '';\n const wrapper = document.createElement('div');\n wrapper.className = 'docx-wrapper-custom max-w-full overflow-x-auto';\n container.appendChild(wrapper);\n\n await renderAsync(arrayBuffer, wrapper, undefined, {\n ...defaultOptions,\n className: 'docx',\n inWrapper: true,\n ignoreWidth: true,\n ignoreHeight: false,\n // Embedded Word fonts often fail to load in the browser; ignoring them uses system fonts so CJK/Latin render correctly.\n ignoreFonts: true,\n breakPages: true,\n ignoreLastRenderedPageBreak: true,\n experimental: false,\n trimXmlDeclaration: true,\n useBase64URL: false,\n renderHeaders: true,\n renderFooters: true,\n renderFootnotes: true,\n renderEndnotes: true,\n });\n\n const style = document.createElement('style');\n style.textContent = `\n .docx-preview-host { padding: 0; }\n .docx-preview-host .docx-wrapper-custom { max-width: 100%; overflow-x: auto; }\n .docx-preview-host .docx-wrapper { max-width: 100% !important; margin: 0 !important; background: transparent !important; padding: 0em !important; }\n .docx-preview-host .docx-wrapper > section.docx { box-shadow: none !important; border: none !important; border-radius: 0 !important; margin: 0 !important; padding: 2em !important; background: white !important; color: black !important; max-width: 100% !important; width: 100% !important; min-width: 0 !important; overflow-x: auto !important; font-family: system-ui, -apple-system, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", \"Noto Sans CJK JP\", sans-serif !important; }\n .docx-preview-host table { max-width: 100% !important; width: auto !important; overflow-x: auto !important; display: block !important; font-family: inherit !important; }\n .docx-preview-host img { max-width: 100% !important; height: auto !important; }\n .docx-preview-host p, .docx-preview-host span, .docx-preview-host div, .docx-preview-host td, .docx-preview-host th { max-width: 100% !important; word-wrap: break-word !important; overflow-wrap: break-word !important; font-family: inherit !important; }\n .docx-preview-host .docx-page-break { display: none !important; }\n `;\n container.classList.add('docx-preview-host');\n container.appendChild(style);\n\n return {\n cleanup: () => {\n container.innerHTML = '';\n container.classList.remove('docx-preview-host');\n },\n };\n}\n\nexport type RenderExcelInContainerOptions = {\n truncationNotice?: string;\n};\n\nexport async function renderExcelInContainer(\n container: HTMLDivElement,\n arrayBuffer: ArrayBuffer,\n options?: RenderExcelInContainerOptions,\n): Promise<{ cleanup: () => void; truncated: boolean }> {\n const XLSX = await import('xlsx');\n const workbook = XLSX.read(arrayBuffer, { type: 'array' });\n\n container.innerHTML = '';\n const wrapper = document.createElement('div');\n wrapper.className = 'flex h-full min-h-0 flex-col overflow-auto';\n container.appendChild(wrapper);\n\n const names = workbook.SheetNames ?? [];\n if (names.length === 0) {\n const empty = document.createElement('p');\n empty.className = 'p-4 text-sm text-fg-muted';\n empty.textContent = '(No sheets in workbook.)';\n wrapper.appendChild(empty);\n return {\n truncated: false,\n cleanup: () => {\n container.innerHTML = '';\n },\n };\n }\n\n let anyTruncated = false;\n\n if (names.length > 1) {\n const tabContainer = document.createElement('div');\n tabContainer.className =\n 'sticky top-0 z-10 mb-4 flex gap-2 border-b border-edge bg-surface-panel dark:border-edge';\n\n const sheetContents: HTMLElement[] = [];\n\n names.forEach((sheetName, index) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.textContent = sheetName;\n tab.className =\n index === 0\n ? 'border-b-2 border-accent px-4 py-2 text-sm font-medium text-accent'\n : 'border-b-2 border-transparent px-4 py-2 text-sm font-medium text-fg-muted hover:border-edge hover:text-fg';\n\n const sheetDiv = document.createElement('div');\n sheetDiv.style.display = index === 0 ? 'flex' : 'none';\n sheetDiv.className = 'min-h-0 flex-1 overflow-auto';\n const built = buildExcelSheetDomWithXlsx(\n XLSX,\n workbook.Sheets[sheetName],\n sheetName,\n options?.truncationNotice,\n );\n if (built.truncated) anyTruncated = true;\n sheetDiv.appendChild(built.element);\n sheetContents.push(sheetDiv);\n\n tab.onclick = () => {\n tabContainer.querySelectorAll('button').forEach((btn, btnIndex) => {\n if (btnIndex === index) {\n btn.className =\n 'border-b-2 border-accent px-4 py-2 text-sm font-medium text-accent';\n } else {\n btn.className =\n 'border-b-2 border-transparent px-4 py-2 text-sm font-medium text-fg-muted hover:border-edge hover:text-fg';\n }\n });\n sheetContents.forEach((content, contentIndex) => {\n content.style.display = contentIndex === index ? 'flex' : 'none';\n });\n };\n\n tabContainer.appendChild(tab);\n });\n\n wrapper.appendChild(tabContainer);\n sheetContents.forEach((content) => {\n wrapper.appendChild(content);\n });\n } else {\n const sheetName = names[0];\n const built = buildExcelSheetDomWithXlsx(\n XLSX,\n workbook.Sheets[sheetName],\n sheetName,\n options?.truncationNotice,\n );\n anyTruncated = built.truncated;\n wrapper.appendChild(built.element);\n }\n\n return {\n truncated: anyTruncated,\n cleanup: () => {\n container.innerHTML = '';\n },\n };\n}\n\nfunction buildExcelSheetDomWithXlsx(\n XLSX: typeof import('xlsx'),\n worksheet: import('xlsx').WorkSheet | undefined,\n sheetName: string,\n truncationNotice?: string,\n): { element: HTMLElement; truncated: boolean } {\n const sheetDiv = document.createElement('div');\n\n if (!worksheet) {\n const p = document.createElement('p');\n p.className = 'p-4 text-sm text-fg-muted';\n p.textContent = `Sheet \"${sheetName}\" is missing from the workbook.`;\n sheetDiv.appendChild(p);\n return { element: sheetDiv, truncated: false };\n }\n\n if (!isRenderableWorksheet(worksheet)) {\n const p = document.createElement('p');\n p.className = 'p-4 text-sm text-fg-muted';\n p.textContent = '(Empty sheet — no cell range.)';\n sheetDiv.appendChild(p);\n return { element: sheetDiv, truncated: false };\n }\n\n try {\n const rows: string[][] = XLSX.utils.sheet_to_json(worksheet, {\n header: 1,\n defval: '',\n });\n\n const rawMaxCol = rows.reduce((m, r) => Math.max(m, r.length), 0);\n const truncated =\n rows.length > EXCEL_PREVIEW_MAX_ROWS || rawMaxCol > EXCEL_PREVIEW_MAX_COLS;\n\n if (truncated && truncationNotice) {\n const note = document.createElement('p');\n note.className = 'mb-2 border-b border-edge-subtle px-1 pb-2 text-xs text-fg-muted dark:border-edge';\n note.textContent = truncationNotice;\n sheetDiv.appendChild(note);\n }\n\n const sliced = rows\n .slice(0, EXCEL_PREVIEW_MAX_ROWS)\n .map((row) => row.slice(0, EXCEL_PREVIEW_MAX_COLS));\n\n const table = document.createElement('table');\n table.className = 'w-full border-collapse text-fg';\n\n sliced.forEach((row, ri) => {\n const tr = document.createElement('tr');\n if (ri % 2 === 1) {\n tr.className = 'bg-surface-hover/40';\n }\n row.forEach((cell) => {\n const td = document.createElement('td');\n td.className = 'border border-edge px-3 py-2 text-left text-sm dark:border-edge';\n td.textContent = cell === null || cell === undefined ? '' : String(cell);\n tr.appendChild(td);\n });\n table.appendChild(tr);\n });\n\n sheetDiv.appendChild(table);\n return { element: sheetDiv, truncated };\n } catch (e) {\n const p = document.createElement('p');\n p.className = 'p-4 text-sm text-red-600 dark:text-red-400';\n p.textContent = e instanceof Error ? e.message : String(e);\n sheetDiv.appendChild(p);\n return { element: sheetDiv, truncated: false };\n }\n}\n"],"file":"attachment-preview-renderer-CxMJMbD2.js"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/pdf
|
|
2
|
-
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{Cn as t}from"./vendor-codemirror-CXAvob9m.js";import{n}from"./excel-worksheet-utils-
|
|
3
|
-
</pdf>`;let a=await o(r);return{extractedText:i,preview:a}}finally{r&&r.destroy()}}async function o(e){try{let t=await e.getPage(1),n=t.getViewport({scale:1}),r=Math.min(160/n.width,160/n.height),i=t.getViewport({scale:r}),a=document.createElement(`canvas`),o=a.getContext(`2d`);return o?(a.height=i.height,a.width=i.width,await t.render({canvasContext:o,viewport:i,canvas:a}).promise,a.toDataURL(`image/png`).split(`,`)[1]):void 0}catch{return}}async function s(e,n){let{parseAsync:r}=await t(async()=>{let{parseAsync:e}=await import(`./docx-preview-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/pdf-CX6ji-QC.js","assets/vendor-codemirror-CXAvob9m.js","assets/rolldown-runtime-DWdDZTNf.js","assets/docx-preview-F-jKDMNv.js","assets/jszip.min-CL3dfyxs.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{Cn as t}from"./vendor-codemirror-CXAvob9m.js";import{n}from"./excel-worksheet-utils-DPfAinZn.js";var r=!1;async function i(){let e=await t(()=>import(`./pdf-CX6ji-QC.js`),__vite__mapDeps([0,1,2]));return r||=(e.GlobalWorkerOptions.workerSrc=new URL(`/assets/pdf.worker.min-FHbmGBN0.mjs`,``+import.meta.url).toString(),!0),e}async function a(e,t){let n=await i(),r=null;try{r=await n.getDocument({data:e}).promise;let i=`<pdf filename="${t}">`;for(let e=1;e<=r.numPages;e++){let t=(await(await r.getPage(e)).getTextContent()).items.map(e=>`str`in e?e.str:``).filter(e=>e.trim()).join(` `);i+=`\n<page number="${e}">\n${t}\n</page>`}i+=`
|
|
3
|
+
</pdf>`;let a=await o(r);return{extractedText:i,preview:a}}finally{r&&r.destroy()}}async function o(e){try{let t=await e.getPage(1),n=t.getViewport({scale:1}),r=Math.min(160/n.width,160/n.height),i=t.getViewport({scale:r}),a=document.createElement(`canvas`),o=a.getContext(`2d`);return o?(a.height=i.height,a.width=i.width,await t.render({canvasContext:o,viewport:i,canvas:a}).promise,a.toDataURL(`image/png`).split(`,`)[1]):void 0}catch{return}}async function s(e,n){let{parseAsync:r}=await t(async()=>{let{parseAsync:e}=await import(`./docx-preview-F-jKDMNv.js`);return{parseAsync:e}},__vite__mapDeps([3,2,4]));try{let t=await r(e),i=`<docx filename="${n}">\n<page number="1">\n`,a=t.documentPart?.body;if(a?.children){let e=[];for(let t of a.children){let n=c(t);n&&e.push(n)}i+=e.join(`
|
|
4
4
|
`)}return i+=`
|
|
5
5
|
</page>
|
|
6
6
|
</docx>`,{extractedText:i}}catch(e){throw Error(`Failed to process DOCX: ${String(e)}`)}}function c(e){let t=e,n=``,r=t.type?.toLowerCase()||``;if(r===`paragraph`&&t.children)for(let e of t.children){let t=e;if((t.type?.toLowerCase()||``)===`run`&&t.children)for(let e of t.children){let t=e;(t.type?.toLowerCase()||``)===`text`&&(n+=t.text||``)}}else if(r===`table`){if(t.children){let e=[];for(let n of t.children){let t=n;if((t.type?.toLowerCase()||``)===`tablerow`&&t.children){let n=[];for(let e of t.children){let t=e;if((t.type?.toLowerCase()||``)===`tablecell`&&t.children){let e=[];for(let n of t.children){let t=c(n);t&&e.push(t)}e.length>0&&n.push(e.join(` `))}}n.length>0&&e.push(n.join(` | `))}}e.length>0&&(n=`\n[Table]\n${e.join(`
|
|
7
|
-
`)}\n[/Table]\n`)}}else if(t.children&&Array.isArray(t.children)){let e=[];for(let n of t.children){let t=c(n);t&&e.push(t)}n=e.join(` `)}return n.trim()}var l=120,u=12e5;function d(e){let t=[];try{if(typeof DOMParser>`u`){let n=/<a:t[^>]*>([^<]*)<\/a:t>/g,r;for(;(r=n.exec(e))!==null;){let e=r[1]?.trim();e&&t.push(e)}return t}let n=new DOMParser().parseFromString(e,`text/xml`);if(n.querySelector(`parsererror`))return t;let r=n.getElementsByTagNameNS(`http://schemas.openxmlformats.org/drawingml/2006/main`,`t`);if(r.length>0){for(let e=0;e<r.length;e++){let n=r[e]?.textContent?.trim();n&&t.push(n)}return t}let i=n.querySelectorAll(`*|t`);for(let e=0;e<i.length;e++){let n=i[e]?.textContent?.trim();n&&t.push(n)}}catch{}return t}async function f(n,r){let i=(await t(async()=>{let{default:t}=await import(`./jszip.min-
|
|
7
|
+
`)}\n[/Table]\n`)}}else if(t.children&&Array.isArray(t.children)){let e=[];for(let n of t.children){let t=c(n);t&&e.push(t)}n=e.join(` `)}return n.trim()}var l=120,u=12e5;function d(e){let t=[];try{if(typeof DOMParser>`u`){let n=/<a:t[^>]*>([^<]*)<\/a:t>/g,r;for(;(r=n.exec(e))!==null;){let e=r[1]?.trim();e&&t.push(e)}return t}let n=new DOMParser().parseFromString(e,`text/xml`);if(n.querySelector(`parsererror`))return t;let r=n.getElementsByTagNameNS(`http://schemas.openxmlformats.org/drawingml/2006/main`,`t`);if(r.length>0){for(let e=0;e<r.length;e++){let n=r[e]?.textContent?.trim();n&&t.push(n)}return t}let i=n.querySelectorAll(`*|t`);for(let e=0;e<i.length;e++){let n=i[e]?.textContent?.trim();n&&t.push(n)}}catch{}return t}async function f(n,r){let i=(await t(async()=>{let{default:t}=await import(`./jszip.min-CL3dfyxs.js`).then(t=>e(t.default,1));return{default:t}},__vite__mapDeps([4,2]))).default;try{let e=await i.loadAsync(n),t=`<pptx filename="${r}">`,a=Object.keys(e.files).filter(e=>e.match(/ppt\/slides\/slide\d+\.xml$/)).sort((e,t)=>Number.parseInt(e.match(/slide(\d+)\.xml$/)?.[1]||`0`,10)-Number.parseInt(t.match(/slide(\d+)\.xml$/)?.[1]||`0`,10)),o=Math.min(a.length,l);a.length>l&&(t+=`\n<!-- truncated: ${a.length} slides, processing first ${l} -->`);for(let n=0;n<o;n++){if(t.length>=u){t+=`\n<!-- extraction stopped: size limit ${u} chars -->`;break}let r=e.file(a[n]);if(r){let e=d(await r.async(`text`));if(e.length>0){t+=`\n<slide number="${n+1}">`;let r=e.join(`
|
|
8
8
|
`),i=u-t.length-32;t+=i>=r.length?`\n${r}`:`\n${r.slice(0,Math.max(0,i))}`,t+=`
|
|
9
9
|
</slide>`}}n%4==3&&await new Promise(e=>setTimeout(e,0))}return t+=`
|
|
10
|
-
</pptx>`,{extractedText:t}}catch(e){throw Error(`Failed to process PPTX: ${String(e)}`)}}async function p(e,r){let i=await t(()=>import(`./xlsx-
|
|
10
|
+
</pptx>`,{extractedText:t}}catch(e){throw Error(`Failed to process PPTX: ${String(e)}`)}}async function p(e,r){let i=await t(()=>import(`./xlsx-CPtvfmVF.js`),[]);try{let t=i.read(e,{type:`array`}),a=`<excel filename="${r}">`,o=t.SheetNames??[];for(let[e,r]of o.entries()){let o=t.Sheets[r],s=n(i,o,r);a+=`\n<sheet name="${r}" index="${e+1}">\n${s}\n</sheet>`}return a+=`
|
|
11
11
|
</excel>`,{extractedText:a}}catch(e){throw Error(`Failed to process Excel: ${String(e)}`)}}export{s as processDocx,p as processExcel,a as processPdf,f as processPptx};
|
|
12
|
-
//# sourceMappingURL=attachment-process-heavy-
|
|
12
|
+
//# sourceMappingURL=attachment-process-heavy-EFXPGfWk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../web/src/features/chat/attachment-process-heavy.ts"],"sourcesContent":["/**\n * Heavy parsers (pdfjs, docx-preview, jszip, xlsx) — imported dynamically from `loadAttachment`\n * and attachment preview dialog to keep the main bundle smaller.\n */\n\nimport { safeSheetToCsv } from '@/features/chat/excel-worksheet-utils';\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\nlet pdfWorkerConfigured = false;\n\nasync function ensurePdfWorker(): Promise<typeof import('pdfjs-dist')> {\n const pdfjsLib = await import('pdfjs-dist');\n if (!pdfWorkerConfigured) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n ).toString();\n pdfWorkerConfigured = true;\n }\n return pdfjsLib;\n}\n\nexport async function processPdf(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string; preview?: string }> {\n const pdfjsLib = await ensurePdfWorker();\n let pdf: PDFDocumentProxy | null = null;\n try {\n pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;\n\n let extractedText = `<pdf filename=\"${name}\">`;\n for (let i = 1; i <= pdf.numPages; i++) {\n const page = await pdf.getPage(i);\n const textContent = await page.getTextContent();\n const pageText = textContent.items\n .map((item) => ('str' in item ? item.str : ''))\n .filter((str) => str.trim())\n .join(' ');\n extractedText += `\\n<page number=\"${i}\">\\n${pageText}\\n</page>`;\n }\n extractedText += '\\n</pdf>';\n\n const preview = await generatePdfPreview(pdf);\n return { extractedText, preview };\n } finally {\n if (pdf) {\n pdf.destroy();\n }\n }\n}\n\nasync function generatePdfPreview(pdf: PDFDocumentProxy): Promise<string | undefined> {\n try {\n const page = await pdf.getPage(1);\n const viewport = page.getViewport({ scale: 1.0 });\n const scale = Math.min(160 / viewport.width, 160 / viewport.height);\n const scaledViewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return undefined;\n\n canvas.height = scaledViewport.height;\n canvas.width = scaledViewport.width;\n\n await page\n .render({\n canvasContext: context,\n viewport: scaledViewport,\n canvas: canvas,\n })\n .promise;\n\n return canvas.toDataURL('image/png').split(',')[1];\n } catch {\n return undefined;\n }\n}\n\nexport async function processDocx(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string }> {\n const { parseAsync } = await import('docx-preview');\n try {\n const wordDoc = await parseAsync(arrayBuffer);\n let extractedText = `<docx filename=\"${name}\">\\n<page number=\"1\">\\n`;\n\n const body = wordDoc.documentPart?.body;\n if (body?.children) {\n const texts: string[] = [];\n for (const element of body.children) {\n const text = extractTextFromElement(element);\n if (text) texts.push(text);\n }\n extractedText += texts.join('\\n');\n }\n\n extractedText += `\\n</page>\\n</docx>`;\n return { extractedText };\n } catch (error) {\n throw new Error(`Failed to process DOCX: ${String(error)}`);\n }\n}\n\nfunction extractTextFromElement(element: unknown): string {\n const el = element as {\n type?: string;\n children?: unknown[];\n text?: string;\n };\n let text = '';\n const elementType = el.type?.toLowerCase() || '';\n\n if (elementType === 'paragraph' && el.children) {\n for (const child of el.children) {\n const c = child as { type?: string; children?: unknown[] };\n const childType = c.type?.toLowerCase() || '';\n if (childType === 'run' && c.children) {\n for (const textChild of c.children) {\n const tc = textChild as { type?: string; text?: string };\n const textType = tc.type?.toLowerCase() || '';\n if (textType === 'text') {\n text += tc.text || '';\n }\n }\n }\n }\n } else if (elementType === 'table') {\n if (el.children) {\n const tableTexts: string[] = [];\n for (const row of el.children) {\n const r = row as { type?: string; children?: unknown[] };\n const rowType = r.type?.toLowerCase() || '';\n if (rowType === 'tablerow' && r.children) {\n const rowTexts: string[] = [];\n for (const cell of r.children) {\n const cellEl = cell as { type?: string; children?: unknown[] };\n const cellType = cellEl.type?.toLowerCase() || '';\n if (cellType === 'tablecell' && cellEl.children) {\n const cellTexts: string[] = [];\n for (const cellElement of cellEl.children) {\n const cellText = extractTextFromElement(cellElement);\n if (cellText) cellTexts.push(cellText);\n }\n if (cellTexts.length > 0) rowTexts.push(cellTexts.join(' '));\n }\n }\n if (rowTexts.length > 0) tableTexts.push(rowTexts.join(' | '));\n }\n }\n if (tableTexts.length > 0) {\n text = `\\n[Table]\\n${tableTexts.join('\\n')}\\n[/Table]\\n`;\n }\n }\n } else if (el.children && Array.isArray(el.children)) {\n const childTexts: string[] = [];\n for (const child of el.children) {\n const childText = extractTextFromElement(child);\n if (childText) childTexts.push(childText);\n }\n text = childTexts.join(' ');\n }\n\n return text.trim();\n}\n\n/** Bounds for PPTX text extraction (upload path) — avoids multi‑MB strings and long main-thread stalls. */\nconst PPTX_EXTRACT_MAX_SLIDES = 120;\nconst PPTX_EXTRACT_MAX_CHARS = 1_200_000;\n\nfunction extractTextNodesFromSlideXml(slideXml: string): string[] {\n const out: string[] = [];\n try {\n if (typeof DOMParser === 'undefined') {\n const re = /<a:t[^>]*>([^<]*)<\\/a:t>/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(slideXml)) !== null) {\n const t = m[1]?.trim();\n if (t) out.push(t);\n }\n return out;\n }\n const doc = new DOMParser().parseFromString(slideXml, 'text/xml');\n const parserErr = doc.querySelector('parsererror');\n if (parserErr) {\n return out;\n }\n const ns = 'http://schemas.openxmlformats.org/drawingml/2006/main';\n const primary = doc.getElementsByTagNameNS(ns, 't');\n if (primary.length > 0) {\n for (let i = 0; i < primary.length; i++) {\n const t = primary[i]?.textContent?.trim();\n if (t) out.push(t);\n }\n return out;\n }\n const anyT = doc.querySelectorAll('*|t');\n for (let i = 0; i < anyT.length; i++) {\n const t = anyT[i]?.textContent?.trim();\n if (t) out.push(t);\n }\n } catch {\n /* ignore */\n }\n return out;\n}\n\nexport async function processPptx(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string }> {\n const JSZip = (await import('jszip')).default;\n try {\n const zip = await JSZip.loadAsync(arrayBuffer);\n let extractedText = `<pptx filename=\"${name}\">`;\n\n const slideFiles = Object.keys(zip.files)\n .filter((n) => n.match(/ppt\\/slides\\/slide\\d+\\.xml$/))\n .sort((a, b) => {\n const numA = Number.parseInt(a.match(/slide(\\d+)\\.xml$/)?.[1] || '0', 10);\n const numB = Number.parseInt(b.match(/slide(\\d+)\\.xml$/)?.[1] || '0', 10);\n return numA - numB;\n });\n\n const maxSlides = Math.min(slideFiles.length, PPTX_EXTRACT_MAX_SLIDES);\n if (slideFiles.length > PPTX_EXTRACT_MAX_SLIDES) {\n extractedText += `\\n<!-- truncated: ${slideFiles.length} slides, processing first ${PPTX_EXTRACT_MAX_SLIDES} -->`;\n }\n\n for (let i = 0; i < maxSlides; i++) {\n if (extractedText.length >= PPTX_EXTRACT_MAX_CHARS) {\n extractedText += `\\n<!-- extraction stopped: size limit ${PPTX_EXTRACT_MAX_CHARS} chars -->`;\n break;\n }\n\n const slideFile = zip.file(slideFiles[i]);\n if (slideFile) {\n const slideXml = await slideFile.async('text');\n const slideTexts = extractTextNodesFromSlideXml(slideXml);\n\n if (slideTexts.length > 0) {\n extractedText += `\\n<slide number=\"${i + 1}\">`;\n const block = slideTexts.join('\\n');\n const room = PPTX_EXTRACT_MAX_CHARS - extractedText.length - 32;\n extractedText +=\n room >= block.length ? `\\n${block}` : `\\n${block.slice(0, Math.max(0, room))}`;\n extractedText += '\\n</slide>';\n }\n }\n\n if (i % 4 === 3) {\n await new Promise<void>((r) => setTimeout(r, 0));\n }\n }\n\n extractedText += '\\n</pptx>';\n return { extractedText };\n } catch (error) {\n throw new Error(`Failed to process PPTX: ${String(error)}`);\n }\n}\n\nexport async function processExcel(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string }> {\n const XLSX = await import('xlsx');\n try {\n const workbook = XLSX.read(arrayBuffer, { type: 'array' });\n let extractedText = `<excel filename=\"${name}\">`;\n\n const names = workbook.SheetNames ?? [];\n for (const [index, sheetName] of names.entries()) {\n const worksheet = workbook.Sheets[sheetName];\n const csvText = safeSheetToCsv(XLSX, worksheet, sheetName);\n extractedText += `\\n<sheet name=\"${sheetName}\" index=\"${index + 1}\">\\n${csvText}\\n</sheet>`;\n }\n\n extractedText += '\\n</excel>';\n return { extractedText };\n } catch (error) {\n throw new Error(`Failed to process Excel: ${String(error)}`);\n }\n}\n"],"file":"attachment-process-heavy-Dbf1--O6.js"}
|
|
1
|
+
{"version":3,"mappings":";2JAQA,IAAI,EAAsB,GAE1B,eAAe,GAAwD,CACrE,IAAM,EAAW,YAAM,OAAO,+CAQ9B,MAPA,CAKE,KAJA,EAAS,oBAAoB,UAAY,iEAGxC,CAAC,UAAU,CACU,IAEjB,EAGT,eAAsB,EACpB,EACA,EACsD,CACtD,IAAM,EAAW,MAAM,GAAiB,CACpC,EAA+B,KACnC,GAAI,CACF,EAAM,MAAM,EAAS,YAAY,CAAE,KAAM,EAAa,CAAC,CAAC,QAExD,IAAI,EAAgB,kBAAkB,EAAK,IAC3C,IAAK,IAAI,EAAI,EAAG,GAAK,EAAI,SAAU,IAAK,CAGtC,IAAM,GAAW,MADS,MADP,EAAI,QAAQ,EAAE,EACF,gBAAgB,EAClB,MAC1B,IAAK,GAAU,QAAS,EAAO,EAAK,IAAM,GAAI,CAC9C,OAAQ,GAAQ,EAAI,MAAM,CAAC,CAC3B,KAAK,IAAI,CACZ,GAAiB,mBAAmB,EAAE,MAAM,EAAS,WAEvD,GAAiB;QAEjB,IAAM,EAAU,MAAM,EAAmB,EAAI,CAC7C,MAAO,CAAE,gBAAe,UAAS,QACzB,CACJ,GACF,EAAI,SAAS,EAKnB,eAAe,EAAmB,EAAoD,CACpF,GAAI,CACF,IAAM,EAAO,MAAM,EAAI,QAAQ,EAAE,CAC3B,EAAW,EAAK,YAAY,CAAE,MAAO,EAAK,CAAC,CAC3C,EAAQ,KAAK,IAAI,IAAM,EAAS,MAAO,IAAM,EAAS,OAAO,CAC7D,EAAiB,EAAK,YAAY,CAAE,QAAO,CAAC,CAE5C,EAAS,SAAS,cAAc,SAAS,CACzC,EAAU,EAAO,WAAW,KAAK,CAcvC,OAbK,GAEL,EAAO,OAAS,EAAe,OAC/B,EAAO,MAAQ,EAAe,MAE9B,MAAM,EACH,OAAO,CACN,cAAe,EACf,SAAU,EACF,SACT,CAAC,CACD,QAEI,EAAO,UAAU,YAAY,CAAC,MAAM,IAAI,CAAC,IAblC,YAcR,CACN,QAIJ,eAAsB,EACpB,EACA,EACoC,CACpC,GAAM,CAAE,mCAAF,CAAE,cAAe,MAAM,OAAO,8EACpC,GAAI,CACF,IAAM,EAAU,MAAM,EAAW,EAAY,CACzC,EAAgB,mBAAmB,EAAK,yBAEtC,EAAO,EAAQ,cAAc,KACnC,GAAI,GAAM,SAAU,CAClB,IAAM,EAAkB,EAAE,CAC1B,IAAK,IAAM,KAAW,EAAK,SAAU,CACnC,IAAM,EAAO,EAAuB,EAAQ,CACxC,GAAM,EAAM,KAAK,EAAK,CAE5B,GAAiB,EAAM,KAAK;EAAK,CAInC,MADA,IAAiB;;SACV,CAAE,gBAAe,OACjB,EAAO,CACd,MAAU,MAAM,2BAA2B,OAAO,EAAM,GAAG,EAI/D,SAAS,EAAuB,EAA0B,CACxD,IAAM,EAAK,EAKP,EAAO,GACL,EAAc,EAAG,MAAM,aAAa,EAAI,GAE9C,GAAI,IAAgB,aAAe,EAAG,SACpC,IAAK,IAAM,KAAS,EAAG,SAAU,CAC/B,IAAM,EAAI,EAEV,IADkB,EAAE,MAAM,aAAa,EAAI,MACzB,OAAS,EAAE,SAC3B,IAAK,IAAM,KAAa,EAAE,SAAU,CAClC,IAAM,EAAK,GACM,EAAG,MAAM,aAAa,EAAI,MAC1B,SACf,GAAQ,EAAG,MAAQ,aAKlB,IAAgB,YACrB,EAAG,SAAU,CACf,IAAM,EAAuB,EAAE,CAC/B,IAAK,IAAM,KAAO,EAAG,SAAU,CAC7B,IAAM,EAAI,EAEV,IADgB,EAAE,MAAM,aAAa,EAAI,MACzB,YAAc,EAAE,SAAU,CACxC,IAAM,EAAqB,EAAE,CAC7B,IAAK,IAAM,KAAQ,EAAE,SAAU,CAC7B,IAAM,EAAS,EAEf,IADiB,EAAO,MAAM,aAAa,EAAI,MAC9B,aAAe,EAAO,SAAU,CAC/C,IAAM,EAAsB,EAAE,CAC9B,IAAK,IAAM,KAAe,EAAO,SAAU,CACzC,IAAM,EAAW,EAAuB,EAAY,CAChD,GAAU,EAAU,KAAK,EAAS,CAEpC,EAAU,OAAS,GAAG,EAAS,KAAK,EAAU,KAAK,IAAI,CAAC,EAG5D,EAAS,OAAS,GAAG,EAAW,KAAK,EAAS,KAAK,MAAM,CAAC,EAG9D,EAAW,OAAS,IACtB,EAAO,cAAc,EAAW,KAAK;EAAK,CAAC,wBAGtC,EAAG,UAAY,MAAM,QAAQ,EAAG,SAAS,CAAE,CACpD,IAAM,EAAuB,EAAE,CAC/B,IAAK,IAAM,KAAS,EAAG,SAAU,CAC/B,IAAM,EAAY,EAAuB,EAAM,CAC3C,GAAW,EAAW,KAAK,EAAU,CAE3C,EAAO,EAAW,KAAK,IAAI,CAG7B,OAAO,EAAK,MAAM,CAIpB,IAAM,EAA0B,IAC1B,EAAyB,KAE/B,SAAS,EAA6B,EAA4B,CAChE,IAAM,EAAgB,EAAE,CACxB,GAAI,CACF,GAAI,OAAO,UAAc,IAAa,CACpC,IAAM,EAAK,4BACP,EACJ,MAAQ,EAAI,EAAG,KAAK,EAAS,IAAM,MAAM,CACvC,IAAM,EAAI,EAAE,IAAI,MAAM,CAClB,GAAG,EAAI,KAAK,EAAE,CAEpB,OAAO,EAET,IAAM,EAAM,IAAI,WAAW,CAAC,gBAAgB,EAAU,WAAW,CAEjE,GADkB,EAAI,cAAc,cAChC,CACF,OAAO,EAGT,IAAM,EAAU,EAAI,uBAAuB,wDAAI,IAAI,CACnD,GAAI,EAAQ,OAAS,EAAG,CACtB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACvC,IAAM,EAAI,EAAQ,IAAI,aAAa,MAAM,CACrC,GAAG,EAAI,KAAK,EAAE,CAEpB,OAAO,EAET,IAAM,EAAO,EAAI,iBAAiB,MAAM,CACxC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAI,EAAK,IAAI,aAAa,MAAM,CAClC,GAAG,EAAI,KAAK,EAAE,OAEd,EAGR,OAAO,EAGT,eAAsB,EACpB,EACA,EACoC,CACpC,IAAM,oCAAS,MAAM,OAAO,+FAAU,QACtC,GAAI,CACF,IAAM,EAAM,MAAM,EAAM,UAAU,EAAY,CAC1C,EAAgB,mBAAmB,EAAK,IAEtC,EAAa,OAAO,KAAK,EAAI,MAAM,CACtC,OAAQ,GAAM,EAAE,MAAM,8BAA8B,CAAC,CACrD,MAAM,EAAG,IACK,OAAO,SAAS,EAAE,MAAM,mBAAmB,GAAG,IAAM,IAAK,GAE/D,CADM,OAAO,SAAS,EAAE,MAAM,mBAAmB,GAAG,IAAM,IAAK,GACxD,CACd,CAEE,EAAY,KAAK,IAAI,EAAW,OAAQ,EAAwB,CAClE,EAAW,OAAS,IACtB,GAAiB,qBAAqB,EAAW,OAAO,4BAA4B,EAAwB,OAG9G,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,IAAK,CAClC,GAAI,EAAc,QAAU,EAAwB,CAClD,GAAiB,yCAAyC,EAAuB,YACjF,MAGF,IAAM,EAAY,EAAI,KAAK,EAAW,GAAG,CACzC,GAAI,EAAW,CAEb,IAAM,EAAa,EAA6B,MADzB,EAAU,MAAM,OAAO,CACW,CAEzD,GAAI,EAAW,OAAS,EAAG,CACzB,GAAiB,oBAAoB,EAAI,EAAE,IAC3C,IAAM,EAAQ,EAAW,KAAK;EAAK,CAC7B,EAAO,EAAyB,EAAc,OAAS,GAC7D,GACE,GAAQ,EAAM,OAAS,KAAK,IAAU,KAAK,EAAM,MAAM,EAAG,KAAK,IAAI,EAAG,EAAK,CAAC,GAC9E,GAAiB;WAIjB,EAAI,GAAM,GACZ,MAAM,IAAI,QAAe,GAAM,WAAW,EAAG,EAAE,CAAC,CAKpD,MADA,IAAiB;SACV,CAAE,gBAAe,OACjB,EAAO,CACd,MAAU,MAAM,2BAA2B,OAAO,EAAM,GAAG,EAI/D,eAAsB,EACpB,EACA,EACoC,CACpC,IAAM,EAAO,YAAM,OAAO,0BAC1B,GAAI,CACF,IAAM,EAAW,EAAK,KAAK,EAAa,CAAE,KAAM,QAAS,CAAC,CACtD,EAAgB,oBAAoB,EAAK,IAEvC,EAAQ,EAAS,YAAc,EAAE,CACvC,IAAK,GAAM,CAAC,EAAO,KAAc,EAAM,SAAS,CAAE,CAChD,IAAM,EAAY,EAAS,OAAO,GAC5B,EAAU,EAAe,EAAM,EAAW,EAAU,CAC1D,GAAiB,kBAAkB,EAAU,WAAW,EAAQ,EAAE,MAAM,EAAQ,YAIlF,MADA,IAAiB;UACV,CAAE,gBAAe,OACjB,EAAO,CACd,MAAU,MAAM,4BAA4B,OAAO,EAAM,GAAG","names":[],"ignoreList":[],"sources":["../../../../../web/src/features/chat/attachment-process-heavy.ts"],"sourcesContent":["/**\n * Heavy parsers (pdfjs, docx-preview, jszip, xlsx) — imported dynamically from `loadAttachment`\n * and attachment preview dialog to keep the main bundle smaller.\n */\n\nimport { safeSheetToCsv } from '@/features/chat/excel-worksheet-utils';\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\nlet pdfWorkerConfigured = false;\n\nasync function ensurePdfWorker(): Promise<typeof import('pdfjs-dist')> {\n const pdfjsLib = await import('pdfjs-dist');\n if (!pdfWorkerConfigured) {\n pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(\n 'pdfjs-dist/build/pdf.worker.min.mjs',\n import.meta.url,\n ).toString();\n pdfWorkerConfigured = true;\n }\n return pdfjsLib;\n}\n\nexport async function processPdf(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string; preview?: string }> {\n const pdfjsLib = await ensurePdfWorker();\n let pdf: PDFDocumentProxy | null = null;\n try {\n pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;\n\n let extractedText = `<pdf filename=\"${name}\">`;\n for (let i = 1; i <= pdf.numPages; i++) {\n const page = await pdf.getPage(i);\n const textContent = await page.getTextContent();\n const pageText = textContent.items\n .map((item) => ('str' in item ? item.str : ''))\n .filter((str) => str.trim())\n .join(' ');\n extractedText += `\\n<page number=\"${i}\">\\n${pageText}\\n</page>`;\n }\n extractedText += '\\n</pdf>';\n\n const preview = await generatePdfPreview(pdf);\n return { extractedText, preview };\n } finally {\n if (pdf) {\n pdf.destroy();\n }\n }\n}\n\nasync function generatePdfPreview(pdf: PDFDocumentProxy): Promise<string | undefined> {\n try {\n const page = await pdf.getPage(1);\n const viewport = page.getViewport({ scale: 1.0 });\n const scale = Math.min(160 / viewport.width, 160 / viewport.height);\n const scaledViewport = page.getViewport({ scale });\n\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return undefined;\n\n canvas.height = scaledViewport.height;\n canvas.width = scaledViewport.width;\n\n await page\n .render({\n canvasContext: context,\n viewport: scaledViewport,\n canvas: canvas,\n })\n .promise;\n\n return canvas.toDataURL('image/png').split(',')[1];\n } catch {\n return undefined;\n }\n}\n\nexport async function processDocx(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string }> {\n const { parseAsync } = await import('docx-preview');\n try {\n const wordDoc = await parseAsync(arrayBuffer);\n let extractedText = `<docx filename=\"${name}\">\\n<page number=\"1\">\\n`;\n\n const body = wordDoc.documentPart?.body;\n if (body?.children) {\n const texts: string[] = [];\n for (const element of body.children) {\n const text = extractTextFromElement(element);\n if (text) texts.push(text);\n }\n extractedText += texts.join('\\n');\n }\n\n extractedText += `\\n</page>\\n</docx>`;\n return { extractedText };\n } catch (error) {\n throw new Error(`Failed to process DOCX: ${String(error)}`);\n }\n}\n\nfunction extractTextFromElement(element: unknown): string {\n const el = element as {\n type?: string;\n children?: unknown[];\n text?: string;\n };\n let text = '';\n const elementType = el.type?.toLowerCase() || '';\n\n if (elementType === 'paragraph' && el.children) {\n for (const child of el.children) {\n const c = child as { type?: string; children?: unknown[] };\n const childType = c.type?.toLowerCase() || '';\n if (childType === 'run' && c.children) {\n for (const textChild of c.children) {\n const tc = textChild as { type?: string; text?: string };\n const textType = tc.type?.toLowerCase() || '';\n if (textType === 'text') {\n text += tc.text || '';\n }\n }\n }\n }\n } else if (elementType === 'table') {\n if (el.children) {\n const tableTexts: string[] = [];\n for (const row of el.children) {\n const r = row as { type?: string; children?: unknown[] };\n const rowType = r.type?.toLowerCase() || '';\n if (rowType === 'tablerow' && r.children) {\n const rowTexts: string[] = [];\n for (const cell of r.children) {\n const cellEl = cell as { type?: string; children?: unknown[] };\n const cellType = cellEl.type?.toLowerCase() || '';\n if (cellType === 'tablecell' && cellEl.children) {\n const cellTexts: string[] = [];\n for (const cellElement of cellEl.children) {\n const cellText = extractTextFromElement(cellElement);\n if (cellText) cellTexts.push(cellText);\n }\n if (cellTexts.length > 0) rowTexts.push(cellTexts.join(' '));\n }\n }\n if (rowTexts.length > 0) tableTexts.push(rowTexts.join(' | '));\n }\n }\n if (tableTexts.length > 0) {\n text = `\\n[Table]\\n${tableTexts.join('\\n')}\\n[/Table]\\n`;\n }\n }\n } else if (el.children && Array.isArray(el.children)) {\n const childTexts: string[] = [];\n for (const child of el.children) {\n const childText = extractTextFromElement(child);\n if (childText) childTexts.push(childText);\n }\n text = childTexts.join(' ');\n }\n\n return text.trim();\n}\n\n/** Bounds for PPTX text extraction (upload path) — avoids multi‑MB strings and long main-thread stalls. */\nconst PPTX_EXTRACT_MAX_SLIDES = 120;\nconst PPTX_EXTRACT_MAX_CHARS = 1_200_000;\n\nfunction extractTextNodesFromSlideXml(slideXml: string): string[] {\n const out: string[] = [];\n try {\n if (typeof DOMParser === 'undefined') {\n const re = /<a:t[^>]*>([^<]*)<\\/a:t>/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(slideXml)) !== null) {\n const t = m[1]?.trim();\n if (t) out.push(t);\n }\n return out;\n }\n const doc = new DOMParser().parseFromString(slideXml, 'text/xml');\n const parserErr = doc.querySelector('parsererror');\n if (parserErr) {\n return out;\n }\n const ns = 'http://schemas.openxmlformats.org/drawingml/2006/main';\n const primary = doc.getElementsByTagNameNS(ns, 't');\n if (primary.length > 0) {\n for (let i = 0; i < primary.length; i++) {\n const t = primary[i]?.textContent?.trim();\n if (t) out.push(t);\n }\n return out;\n }\n const anyT = doc.querySelectorAll('*|t');\n for (let i = 0; i < anyT.length; i++) {\n const t = anyT[i]?.textContent?.trim();\n if (t) out.push(t);\n }\n } catch {\n /* ignore */\n }\n return out;\n}\n\nexport async function processPptx(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string }> {\n const JSZip = (await import('jszip')).default;\n try {\n const zip = await JSZip.loadAsync(arrayBuffer);\n let extractedText = `<pptx filename=\"${name}\">`;\n\n const slideFiles = Object.keys(zip.files)\n .filter((n) => n.match(/ppt\\/slides\\/slide\\d+\\.xml$/))\n .sort((a, b) => {\n const numA = Number.parseInt(a.match(/slide(\\d+)\\.xml$/)?.[1] || '0', 10);\n const numB = Number.parseInt(b.match(/slide(\\d+)\\.xml$/)?.[1] || '0', 10);\n return numA - numB;\n });\n\n const maxSlides = Math.min(slideFiles.length, PPTX_EXTRACT_MAX_SLIDES);\n if (slideFiles.length > PPTX_EXTRACT_MAX_SLIDES) {\n extractedText += `\\n<!-- truncated: ${slideFiles.length} slides, processing first ${PPTX_EXTRACT_MAX_SLIDES} -->`;\n }\n\n for (let i = 0; i < maxSlides; i++) {\n if (extractedText.length >= PPTX_EXTRACT_MAX_CHARS) {\n extractedText += `\\n<!-- extraction stopped: size limit ${PPTX_EXTRACT_MAX_CHARS} chars -->`;\n break;\n }\n\n const slideFile = zip.file(slideFiles[i]);\n if (slideFile) {\n const slideXml = await slideFile.async('text');\n const slideTexts = extractTextNodesFromSlideXml(slideXml);\n\n if (slideTexts.length > 0) {\n extractedText += `\\n<slide number=\"${i + 1}\">`;\n const block = slideTexts.join('\\n');\n const room = PPTX_EXTRACT_MAX_CHARS - extractedText.length - 32;\n extractedText +=\n room >= block.length ? `\\n${block}` : `\\n${block.slice(0, Math.max(0, room))}`;\n extractedText += '\\n</slide>';\n }\n }\n\n if (i % 4 === 3) {\n await new Promise<void>((r) => setTimeout(r, 0));\n }\n }\n\n extractedText += '\\n</pptx>';\n return { extractedText };\n } catch (error) {\n throw new Error(`Failed to process PPTX: ${String(error)}`);\n }\n}\n\nexport async function processExcel(\n arrayBuffer: ArrayBuffer,\n name: string,\n): Promise<{ extractedText: string }> {\n const XLSX = await import('xlsx');\n try {\n const workbook = XLSX.read(arrayBuffer, { type: 'array' });\n let extractedText = `<excel filename=\"${name}\">`;\n\n const names = workbook.SheetNames ?? [];\n for (const [index, sheetName] of names.entries()) {\n const worksheet = workbook.Sheets[sheetName];\n const csvText = safeSheetToCsv(XLSX, worksheet, sheetName);\n extractedText += `\\n<sheet name=\"${sheetName}\" index=\"${index + 1}\">\\n${csvText}\\n</sheet>`;\n }\n\n extractedText += '\\n</excel>';\n return { extractedText };\n } catch (error) {\n throw new Error(`Failed to process Excel: ${String(error)}`);\n }\n}\n"],"file":"attachment-process-heavy-EFXPGfWk.js"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var e=3e5;function t(e,t){let n=e.replace(/\\/g,`/`),r=encodeURIComponent(n),i=n.startsWith(`tts/`)?`/api/workspace/tts-file?rel=${r}`:`/api/workspace/inbound-file?rel=${r}`,a=t?.sessionKey?.trim();return a?`${i}&sessionKey=${encodeURIComponent(a)}`:i}function n(e){if(typeof e.content==`string`&&e.content.length>0)return e.content;if(typeof e.data==`string`&&e.data.length>0)return e.data}var r=[`.txt`,`.md`,`.json`,`.xml`,`.html`,`.css`,`.js`,`.ts`,`.jsx`,`.tsx`,`.yml`,`.yaml`];function i(e){let t=e.mimeType?.toLowerCase()??``;if(t.startsWith(`text/`)||t===`application/json`||t===`application/xml`||t===`application/javascript`||t===`application/typescript`)return!0;let n=e.name?.toLowerCase()??``;return r.some(e=>n.endsWith(e))}function a(e){if(e.extractedText!=null&&e.extractedText!==``)return e.extractedText;if(!i(e))return;let t=n(e);if(t)try{let e=c(t);return new TextDecoder(`utf-8`,{fatal:!1}).decode(new Uint8Array(e))}catch{return}}function o(e,t){let n=t.trim();if(n.startsWith(`data:`))return n;let r=n.replace(/\s/g,``);return`data:${e&&typeof e==`string`&&e.includes(`/`)?e:`application/octet-stream`};base64,${r}`}function s(e){let t=new Uint8Array(e),n=``,r=32768;for(let e=0;e<t.length;e+=r){let i=t.subarray(e,e+r);n+=String.fromCharCode(...i)}return btoa(n)}function c(e){if(e==null||e===``)throw Error(`Missing file data`);let t=e;if(e.startsWith(`data:`)){let n=e.match(/base64,(.+)/);n&&(t=n[1])}let n=atob(t),r=new Uint8Array(n.length);for(let e=0;e<n.length;e++)r[e]=n.charCodeAt(e);return r.buffer}function l(e){let t=[`B`,`KB`,`MB`,`GB`],n=0,r=e;for(;r>=1024&&n<t.length-1;)r/=1024,n++;return`${r.toFixed(1)} ${t[n]}`}function u(e){let t=(e.mimeType??``).toLowerCase(),n=t.split(`;`)[0]?.trim()??``,r=e.name?.toLowerCase()??``;return e.type===`image`||n.startsWith(`image/`)?`image`:n===`application/pdf`||t.includes(`application/pdf`)||r.endsWith(`.pdf`)?`pdf`:n===`application/vnd.openxmlformats-officedocument.wordprocessingml.document`||t.includes(`wordprocessingml`)||r.endsWith(`.docx`)?`docx`:n===`application/vnd.openxmlformats-officedocument.presentationml.presentation`||t.includes(`presentationml`)||r.endsWith(`.pptx`)?`pptx`:[`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/vnd.ms-excel`].includes(n)||t.includes(`spreadsheetml`)||t.includes(`ms-excel`)||r.endsWith(`.xlsx`)||r.endsWith(`.xls`)?`excel`:[`.jpg`,`.jpeg`,`.png`,`.gif`,`.webp`,`.bmp`,`.svg`].some(e=>r.endsWith(e))?`image`:`text`}function d(e){let t=e.toLowerCase();for(let[e,n]of[[`.pdf`,`application/pdf`],[`.docx`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`],[`.pptx`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`],[`.xlsx`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],[`.xls`,`application/vnd.ms-excel`],[`.png`,`image/png`],[`.jpg`,`image/jpeg`],[`.jpeg`,`image/jpeg`],[`.gif`,`image/gif`],[`.webp`,`image/webp`],[`.bmp`,`image/bmp`],[`.svg`,`image/svg+xml`],[`.svgz`,`image/svg+xml`],[`.ico`,`image/x-icon`],[`.tif`,`image/tiff`],[`.tiff`,`image/tiff`],[`.avif`,`image/avif`],[`.jxl`,`image/jxl`]])if(t.endsWith(e))return n}function f(e,t){return(t?.startsWith(`text/`)??!1)||r.some(t=>e.toLowerCase().endsWith(t))}export{l as a,d as c,t as d,a as i,f as l,s as n,n as o,c as r,u as s,e as t,o as u};
|
|
2
|
-
//# sourceMappingURL=attachment-utils-core-
|
|
2
|
+
//# sourceMappingURL=attachment-utils-core-ECbeoa9H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment-utils-core-ECbeoa9H.js","names":[],"sources":["../../../../../web/src/features/chat/attachment-utils-core.ts"],"sourcesContent":["/** Max files per chat message (keep in sync with `src/gateway/chat-limits.ts`). */\nexport const MAX_CHAT_ATTACHMENTS = 10;\n\n/** Excel preview row/column caps — keep in sync with `attachment-preview-renderer` table builder. */\nexport const EXCEL_PREVIEW_MAX_ROWS = 500;\nexport const EXCEL_PREVIEW_MAX_COLS = 64;\n\n/** PPTX: cap text shown in preview dialog to avoid freezing the browser on huge decks. */\nexport const PPTX_PREVIEW_MAX_CHARS = 300_000;\n\n/** Path for gateway `GET` (inbound vs TTS); `rel` is relative to agent home (`inbound/…`, `tts/…`). */\nexport function workspaceRelativePathToApiPath(\n rel: string,\n opts?: { sessionKey?: string | null },\n): string {\n const norm = rel.replace(/\\\\/g, '/');\n const q = encodeURIComponent(norm);\n const base = norm.startsWith('tts/')\n ? `/api/workspace/tts-file?rel=${q}`\n : `/api/workspace/inbound-file?rel=${q}`;\n const sk = opts?.sessionKey?.trim();\n if (!sk) return base;\n return `${base}&sessionKey=${encodeURIComponent(sk)}`;\n}\n\nexport interface Attachment {\n id?: string;\n type: 'image' | 'document' | 'voice';\n name: string;\n mimeType: string;\n size: number;\n content: string; // base64 encoded original data (without data URL prefix)\n /** Wire/API payloads may use `data` instead of `content` */\n data?: string;\n extractedText?: string; // For documents: extracted text content\n preview?: string; // base64 image preview (first page for PDFs, or same as content for images)\n /** Server-persisted path under agent home (`inbound/…` or `tts/…`; gateway with `?sessionKey=` when needed) */\n workspaceRelativePath?: string;\n durationSeconds?: number;\n}\n\n/** Prefer `content`, then `data` (gateway / webchat wire format). */\nexport function getAttachmentBinaryPayload(att: {\n content?: string;\n data?: string;\n}): string | undefined {\n if (typeof att.content === 'string' && att.content.length > 0) return att.content;\n if (typeof att.data === 'string' && att.data.length > 0) return att.data;\n return undefined;\n}\n\n/** Same list as `loadAttachment` text branch — keep in sync for preview decode. */\nexport const TEXT_FILE_EXTENSIONS = [\n '.txt',\n '.md',\n '.json',\n '.xml',\n '.html',\n '.css',\n '.js',\n '.ts',\n '.jsx',\n '.tsx',\n '.yml',\n '.yaml',\n] as const;\n\nfunction isLikelyTextLikeFile(att: { name?: string; mimeType?: string }): boolean {\n const mime = att.mimeType?.toLowerCase() ?? '';\n if (mime.startsWith('text/')) return true;\n if (\n mime === 'application/json' ||\n mime === 'application/xml' ||\n mime === 'application/javascript' ||\n mime === 'application/typescript'\n ) {\n return true;\n }\n const lower = att.name?.toLowerCase() ?? '';\n return TEXT_FILE_EXTENSIONS.some((ext) => lower.endsWith(ext));\n}\n\n/**\n * Text for overlay preview: prefers `extractedText`, otherwise decodes UTF-8 from base64\n * when the attachment is a text-like file (e.g. .md). Webchat only sends `data`, not\n * `extractedText`, so previews would otherwise show empty.\n */\nexport function extractTextForPreview(att: {\n name?: string;\n mimeType?: string;\n content?: string;\n data?: string;\n extractedText?: string;\n}): string | undefined {\n if (att.extractedText != null && att.extractedText !== '') {\n return att.extractedText;\n }\n if (!isLikelyTextLikeFile(att)) return undefined;\n const payload = getAttachmentBinaryPayload(att);\n if (!payload) return undefined;\n try {\n const buf = base64ToArrayBuffer(payload);\n return new TextDecoder('utf-8', { fatal: false }).decode(new Uint8Array(buf));\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build a valid `data:` URL for `<img src>` / preview.\n * If payload is already a data URL, returns it unchanged.\n * Otherwise strips whitespace from base64 and uses `mime` (falls back if invalid).\n */\nexport function resolveDataUrlForDisplay(mime: string, payload: string): string {\n const trimmed = payload.trim();\n if (trimmed.startsWith('data:')) {\n return trimmed;\n }\n const compact = trimmed.replace(/\\s/g, '');\n const mimeSafe =\n mime && typeof mime === 'string' && mime.includes('/') ? mime : 'application/octet-stream';\n return `data:${mimeSafe};base64,${compact}`;\n}\n\n/** Encode binary as base64 (chunked for large buffers). */\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n const chunkSize = 0x8000;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n const chunk = bytes.subarray(i, i + chunkSize);\n binary += String.fromCharCode(...chunk);\n }\n return btoa(binary);\n}\n\n/**\n * Convert base64 to ArrayBuffer\n */\nexport function base64ToArrayBuffer(base64: string | undefined | null): ArrayBuffer {\n if (base64 == null || base64 === '') {\n throw new Error('Missing file data');\n }\n // Remove data URL prefix if present\n let base64Data = base64;\n if (base64.startsWith('data:')) {\n const base64Match = base64.match(/base64,(.+)/);\n if (base64Match) {\n base64Data = base64Match[1];\n }\n }\n\n const binaryString = atob(base64Data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * Get file icon based on mime type\n */\nexport function getFileIcon(mimeType: string): string {\n if (!mimeType) return '📎';\n if (mimeType.includes('pdf')) return '📄';\n if (mimeType.includes('word') || mimeType.includes('document')) return '📝';\n if (mimeType.includes('sheet') || mimeType.includes('excel')) return '📊';\n if (mimeType.includes('presentation') || mimeType.includes('powerpoint')) return '📽️';\n if (mimeType.startsWith('image/')) return '🖼️';\n if (mimeType.startsWith('text/') || mimeType.includes('json')) return '📃';\n return '📎';\n}\n\n/**\n * Format file size\n */\nexport function formatFileSize(bytes: number): string {\n const units = ['B', 'KB', 'MB', 'GB'];\n let unitIndex = 0;\n let size = bytes;\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n return `${size.toFixed(1)} ${units[unitIndex]}`;\n}\n\n/**\n * Check if file is an image\n */\nexport function isImageFile(mimeType: string): boolean {\n return Boolean(mimeType?.startsWith('image/'));\n}\n\n/** View kind for the attachment preview dialog — mirrors `loadAttachment` detection (MIME + extension). */\nexport type AttachmentPreviewFileType =\n | 'image'\n | 'pdf'\n | 'docx'\n | 'pptx'\n | 'excel'\n | 'text';\n\n/**\n * Infer how to preview an attachment (wire payloads often use `application/octet-stream`).\n * Order matches [`loadAttachment`](./attachment-load.ts): office types before generic image extension fallback.\n */\nexport function inferAttachmentFileType(att: {\n name?: string;\n mimeType?: string;\n type?: string;\n}): AttachmentPreviewFileType {\n const rawMime = att.mimeType ?? '';\n const mime = rawMime.toLowerCase();\n const baseMime = mime.split(';')[0]?.trim() ?? '';\n const name = att.name?.toLowerCase() ?? '';\n\n if (att.type === 'image' || baseMime.startsWith('image/')) {\n return 'image';\n }\n\n if (baseMime === 'application/pdf' || mime.includes('application/pdf') || name.endsWith('.pdf')) {\n return 'pdf';\n }\n\n if (\n baseMime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||\n mime.includes('wordprocessingml') ||\n name.endsWith('.docx')\n ) {\n return 'docx';\n }\n\n if (\n baseMime === 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ||\n mime.includes('presentationml') ||\n name.endsWith('.pptx')\n ) {\n return 'pptx';\n }\n\n const excelMimeTypes = [\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-excel',\n ];\n if (\n excelMimeTypes.includes(baseMime) ||\n mime.includes('spreadsheetml') ||\n mime.includes('ms-excel') ||\n name.endsWith('.xlsx') ||\n name.endsWith('.xls')\n ) {\n return 'excel';\n }\n\n const imageExt = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];\n if (imageExt.some((ext) => name.endsWith(ext))) {\n return 'image';\n }\n\n return 'text';\n}\n\n/**\n * When MIME is missing or generic, infer a concrete type from the file name (for session wire normalization).\n */\nexport function inferMimeTypeFromFileName(fileName: string): string | undefined {\n const lower = fileName.toLowerCase();\n const map: Array<[string, string]> = [\n ['.pdf', 'application/pdf'],\n ['.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],\n ['.pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],\n ['.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],\n ['.xls', 'application/vnd.ms-excel'],\n ['.png', 'image/png'],\n ['.jpg', 'image/jpeg'],\n ['.jpeg', 'image/jpeg'],\n ['.gif', 'image/gif'],\n ['.webp', 'image/webp'],\n ['.bmp', 'image/bmp'],\n ['.svg', 'image/svg+xml'],\n ['.svgz', 'image/svg+xml'],\n ['.ico', 'image/x-icon'],\n ['.tif', 'image/tiff'],\n ['.tiff', 'image/tiff'],\n ['.avif', 'image/avif'],\n ['.jxl', 'image/jxl'],\n ];\n for (const [ext, mime] of map) {\n if (lower.endsWith(ext)) return mime;\n }\n return undefined;\n}\n\n/**\n * Check if file is a document that can be previewed\n */\nexport function isPreviewableDocument(mimeType: string, name?: string): boolean {\n const previewableTypes = [\n 'application/pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'text/plain',\n 'text/markdown',\n 'text/html',\n 'application/json',\n 'text/xml',\n ];\n\n if (previewableTypes.includes(mimeType)) return true;\n\n if (name) {\n const ext = name.toLowerCase().split('.').pop();\n const previewableExts = [\n 'pdf',\n 'docx',\n 'xlsx',\n 'xls',\n 'pptx',\n 'txt',\n 'md',\n 'json',\n 'xml',\n 'html',\n 'css',\n 'js',\n 'ts',\n ];\n if (ext && previewableExts.includes(ext)) return true;\n }\n\n return false;\n}\n\nexport function isTextLikeFileNameAndMime(name: string, mimeType: string): boolean {\n const isTextFile =\n (mimeType?.startsWith('text/') ?? false) ||\n TEXT_FILE_EXTENSIONS.some((ext) => name.toLowerCase().endsWith(ext));\n return isTextFile;\n}\n"],"mappings":"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"}
|