@openchambery/web 2.1.9-beta.2 → 2.1.9-beta.3
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/assets/{AssistantView-CeP8V0X-.js → AssistantView-3Lz_SADb.js} +3 -3
- package/dist/assets/{DiagramView-DRCsjVx9.js → DiagramView-BZP5Aj4S.js} +1 -1
- package/dist/assets/{MarkdownRenderer-BvKaN2KX.js → MarkdownRenderer-DpB0hJ_E.js} +2 -2
- package/dist/assets/{MarkdownRendererImpl-D6iUJhtT.js → MarkdownRendererImpl-Hg1bbRlM.js} +1 -1
- package/dist/assets/{MarkstreamRendererImpl-B14yVm7-.js → MarkstreamRendererImpl-BfUZU3WC.js} +1 -1
- package/dist/assets/{MobileDetailNavigation-e4vO0lXT.js → MobileDetailNavigation-DqGKTFzT.js} +1 -1
- package/dist/assets/{MobileShareBridge-BnA02Mps.js → MobileShareBridge-DHHw00TT.js} +1 -1
- package/dist/assets/{MobileTabPageHeader-nApqJ-1X.js → MobileTabPageHeader-jFeAz_Z2.js} +1 -1
- package/dist/assets/{MultiRunWindow-C7Vgvpoz.js → MultiRunWindow-C92A1Qkc.js} +1 -1
- package/dist/assets/{SettingsView-Be7_mr8t.js → SettingsView-C2-Tug3M.js} +1 -1
- package/dist/assets/{SettingsWindow-ZnWK6Itb.js → SettingsWindow-D_j-lLiY.js} +1 -1
- package/dist/assets/{TerminalView-BluEtXH8.js → TerminalView-Veaa8xms.js} +1 -1
- package/dist/assets/{ToolOutputDialog-6gouT_GS.js → ToolOutputDialog-CI32t1nx.js} +1 -1
- package/dist/assets/{appThemeRegistry-D59Upe3t.js → appThemeRegistry-BhVqv80m.js} +1 -1
- package/dist/assets/{fileMentionTouchSelection-DbICTM0g.js → fileMentionTouchSelection-ytHOa8vD.js} +1 -1
- package/dist/assets/{gitApi-Bq2BWLAz.js → gitApi-DZSpp5_u.js} +1 -1
- package/dist/assets/{insertionBoundaries-n7dVxiuI.js → insertionBoundaries-B3F2vgJn.js} +1 -1
- package/dist/assets/{main-BlZ-gMwg.js → main-HsqXFi70.js} +2 -2
- package/dist/assets/{main-DRGmBqpD.js → main-mdIF-V3Y.js} +4 -4
- package/dist/assets/{mcpQueries-BxICKLst.js → mcpQueries-BAU9FYFF.js} +1 -1
- package/dist/assets/{miniChat-PxCQIMUm.js → miniChat-B7C-IBFC.js} +2 -2
- package/dist/assets/{mobile-Bs5d4s3_.js → mobile-CqWR9PBo.js} +2 -2
- package/dist/assets/{multirun-BxUSBUb_.js → multirun-D3iS9HjC.js} +1 -1
- package/dist/assets/{projectMeta-dZJ-hOil.js → projectMeta-DlHKE_8C.js} +1 -1
- package/dist/assets/{renderElectronMiniChatApp-CGWfxiDc.js → renderElectronMiniChatApp-CVT0WyUz.js} +2 -2
- package/dist/assets/{renderMobileApp-DJmWaXgS.js → renderMobileApp-hUJNFMKe.js} +4 -4
- package/dist/assets/{runtimeConfig-B-2tboUz.js → runtimeConfig-DAxmpV5p.js} +1 -1
- package/dist/assets/{runtimeEndpointReset-BbJRibgL.js → runtimeEndpointReset-BCf2ORY3.js} +1 -1
- package/dist/assets/{sessionChangeSummary-DTeJLaNT.js → sessionChangeSummary-BYoAdclB.js} +1 -1
- package/dist/assets/{sessionLookup-_U9nCuBN.js → sessionLookup-DxWtYNX-.js} +1 -1
- package/dist/assets/{speechSettingsQueries-CdlOc_sI.js → speechSettingsQueries-CBrLvvue.js} +1 -1
- package/dist/assets/{useAppFontEffects-YhyFj8z3.js → useAppFontEffects-BvJWL9Tq.js} +5 -5
- package/dist/assets/{useDesktopWindowControlsLayout-jdIXqoD8.js → useDesktopWindowControlsLayout-6B2M3hOM.js} +1 -1
- package/dist/assets/{useEffectiveDirectory-COgvBW0Q.js → useEffectiveDirectory-fANUMttq.js} +1 -1
- package/dist/assets/{useMobileNavigationStore-CuOiHjP1.js → useMobileNavigationStore-DRpR2Oyd.js} +10 -10
- package/dist/assets/{useWorkerHighlightedLines-DGH-O6ZJ.js → useWorkerHighlightedLines-D1io7Q4t.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/mini-chat.html +2 -2
- package/dist/mobile.html +2 -2
- package/package.json +1 -1
- package/server/lib/session-index/DOCUMENTATION.md +17 -0
- package/server/lib/session-index/sync-runtime.js +34 -14
- package/server/lib/session-index/sync-runtime.test.js +121 -0
package/dist/assets/{useWorkerHighlightedLines-DGH-O6ZJ.js → useWorkerHighlightedLines-D1io7Q4t.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t}from"./runtime-fetch-WgH0pROs.js";import{Mn as n,ln as r}from"./persistence-BKszKaoQ.js";import{it as i,rt as a}from"./vendor-base-ui-CdzJ6My_.js";import{n as o}from"./platform-CgwxwfA4.js";import{v as s}from"./useUIStore-CEqU9Ujv.js";import{n as c}from"./vendor-react-BP2cRVqX.js";import{a as l,n as u}from"./vendor-aparajita-capacitor-secure-storage-xol_6BsB.js";import{f as d,r as f,t as p}from"./button-CWSs9Baz.js";import{n as m}from"./useThemeSystem-8jhE4Zyk.js";import{n as h}from"./Icon-BCFeHDWx.js";import{i as g,n as _,t as v}from"./tooltip-CV6dvLBp.js";import{a as y,i as b,o as x,t as S}from"./image-loading-Bj11k4gM.js";import{n as C}from"./MarkdownRenderer-BvKaN2KX.js";import{n as w,o as T,s as E,t as D}from"./markdown-worker-DFNZ3KJ_.js";import{n as ee}from"./markdownTheme-CV7_0QAL.js";import{t as te}from"./vendor-tanstack-react-virtual-B_EO--9t.js";var O=e(i(),1),k=c(),A=a(),ne=`w-[min(30rem,calc(100cqw-1rem))] min-w-[280px] max-w-full overflow-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]`,j={maxWidth:`min(28rem, calc(100cqw - 4ch))`,backgroundColor:`var(--surface-elevated)`,color:`var(--surface-elevated-foreground)`},M=`flex items-center justify-between gap-3 border-b border-[var(--surface-subtle)] px-3 py-2.5`,N=`text-xs font-medium leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,re=`text-xs tabular-nums leading-4 text-muted-foreground md:text-[0.8125rem] md:leading-5`,P=`m-0 list-none divide-y divide-[var(--surface-subtle)] p-0`,F=`${P} max-h-[min(22rem,50vh)] overflow-y-auto`,ie=`${P} w-full`,ae={className:`p-0 rounded-none`,maxHeightClass:`max-h-[46vh]`,disableHorizontal:!0},I=`px-2.5 py-1.5 text-xs leading-4 md:py-2 md:text-[0.8125rem] md:leading-5`,oe=`flex min-w-0 items-center gap-1 text-xs leading-4 text-muted-foreground md:gap-1.5 md:text-[0.8125rem] md:leading-5`,se=`min-w-0 truncate text-xs leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,ce=`inline-flex shrink-0 items-center gap-1 text-xs leading-4 tabular-nums text-muted-foreground md:text-[0.8125rem] md:leading-5`,le=`h-3 w-3 shrink-0 md:h-3.5 md:w-3.5`,ue={in_progress:`text-foreground`,pending:`text-foreground`,completed:`text-muted-foreground line-through`,cancelled:`text-muted-foreground line-through`},L={in_progress:`chat.statusRow.todo.status.inProgress`,pending:`chat.statusRow.todo.status.pending`,completed:`chat.statusRow.todo.status.completed`,cancelled:`chat.statusRow.todo.status.cancelled`},R=e=>{let t=(0,k.c)(26),{todo:n,isActive:r,activeRef:i}=e,{t:a}=d(),o=z.has(n.status)?n.status:`pending`,s=L[o],c;t[0]!==a||t[1]!==s?(c=a(s),t[0]=a,t[1]=s,t[2]=c):c=t[2];let l=c,u;t[3]===o?u=t[4]:(u=o===`in_progress`?(0,A.jsx)(h,{name:`record-circle`,className:`h-3.5 w-3.5 text-[var(--status-info)]`,"aria-hidden":`true`}):o===`completed`?(0,A.jsx)(h,{name:`checkbox-circle`,className:`h-3.5 w-3.5 text-[var(--status-success)]`,"aria-hidden":`true`}):(0,A.jsx)(h,{name:`time`,className:`h-3.5 w-3.5 text-muted-foreground`,"aria-hidden":`true`}),t[3]=o,t[4]=u);let p=u,m=r?`step`:void 0,y=r&&`bg-[var(--surface-muted)] before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-[var(--status-info)]`,b;t[5]===y?b=t[6]:(b=f(`group relative grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] items-start gap-2.5`,I,y),t[5]=y,t[6]=b);let x;t[7]!==p||t[8]!==l?(x=(0,A.jsx)(g,{asChild:!0,children:(0,A.jsx)(`span`,{className:`flex size-4 items-center justify-center`,"aria-label":l,children:p})}),t[7]=p,t[8]=l,t[9]=x):x=t[9];let S;t[10]===l?S=t[11]:(S=(0,A.jsx)(_,{side:`left`,sideOffset:6,children:l}),t[10]=l,t[11]=S);let C;t[12]!==x||t[13]!==S?(C=(0,A.jsxs)(v,{children:[x,S]}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w=ue[o],T;t[15]===w?T=t[16]:(T=f(`block whitespace-normal break-words`,w),t[15]=w,t[16]=T);let E;t[17]!==T||t[18]!==n.content?(E=(0,A.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,A.jsx)(`span`,{className:T,children:n.content})}),t[17]=T,t[18]=n.content,t[19]=E):E=t[19];let D;return t[20]!==i||t[21]!==E||t[22]!==m||t[23]!==b||t[24]!==C?(D=(0,A.jsxs)(`li`,{ref:i,"aria-current":m,className:b,children:[C,E]}),t[20]=i,t[21]=E,t[22]=m,t[23]=b,t[24]=C,t[25]=D):D=t[25],D},z=new Set([`in_progress`,`pending`,`completed`,`cancelled`]),B=`image/png`,V=l(`OpenChamberMedia`),H=e=>{let t=e.toLowerCase().split(`;`)[0]?.trim()||B;if(t===`image/jpeg`||t===`image/jpg`)return`jpg`;if(t===`image/webp`)return`webp`;if(t===`image/gif`)return`gif`;if(t===`image/svg+xml`)return`svg`;if(t===`image/png`)return`png`;let n=t.split(`/`)[1];return n&&/^[a-z0-9.+-]+$/i.test(n)?n:`png`},U=(e,t)=>{let n=`image.${H(t)}`,r=(((e||``).trim()||n).split(/[\\/]/).pop()||n).replace(/[^\w.\- ()[\]]+/g,`_`).replace(/^\.+/,``);return r?/\.[a-z0-9]{1,8}$/i.test(r)?r:`${r}.${H(t)}`:n},W=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{if(typeof r.result==`string`){t(r.result);return}n(Error(`Failed to encode image`))},r.onerror=()=>n(r.error??Error(`Failed to encode image`)),r.readAsDataURL(e)}),de=async e=>{let t=await W(e),n=t.indexOf(`,`);return n>=0?t.slice(n+1):t},fe=(e,t)=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)},G=async(e,t)=>{let n=await fetch(e,{signal:t,cache:`no-store`});if(!n.ok)throw Error(`Image fetch failed (${n.status})`);return n.blob()},pe=async(e,t=B)=>{if(!e.naturalWidth||!e.naturalHeight)return null;let n=e.currentSrc||e.src;if(n&&(/^(?:blob:|data:|openchamber-asset:)/i.test(n)||/^https?:/i.test(n)||/^\/api(?=\/|[?#]|$)/.test(n)))try{let e=await G(n);if(e.size>0)return e}catch{}try{let n=document.createElement(`canvas`);n.width=e.naturalWidth,n.height=e.naturalHeight;let r=n.getContext(`2d`);if(!r)return null;r.drawImage(e,0,0);let i=t.startsWith(`image/`)?t:B,a=await new Promise(e=>{n.toBlob(t=>e(t),i===`image/jpg`?`image/jpeg`:i)});return a&&a.size>0?a:null}catch{return null}},me=async(e,t)=>{let n=``;try{return n=await S(e,t??new AbortController().signal),await G(n,t)}finally{n&&y(n)}},he=async(e,n)=>{let r=e.effectiveDirectory??``,i=null;e.prefetchedBlob&&e.prefetchedBlob.size>0&&(i=e.prefetchedBlob);let a=e.displayUrl?.trim();if(!i&&a)try{i=await G(a,n)}catch{i=null}if(!i){let a=e.sourceUrl.trim();if(!a)throw Error(`Image source is empty`);if(/^(?:data|blob):/i.test(a)||/^https?:/i.test(a)||/^\/api(?=\/|[?#]|$)/.test(a))i=await G(a,n);else{let e=x(a,r);if(b(e))try{i=await me(e.path,n)}catch{let r=await t(`/api/fs/raw`,{method:`GET`,cache:`no-store`,query:{path:e.path},signal:n});if(!r.ok)throw Error(`Image source request failed with status ${r.status}`);i=await r.blob()}else i=await G(e.source,n)}}if(!i||i.size<=0)throw Error(`Image is empty`);let o=(e.mimeType||(i.type&&i.type.startsWith(`image/`)?i.type:``)||B).split(`;`)[0].trim()||B;return{blob:i,filename:U(e.filename,o),mimeType:o}},ge=()=>!o()||u===void 0?!1:u.isPluginAvailable(`OpenChamberMedia`),_e=async(e,t)=>{let{blob:i,filename:a,mimeType:o}=await he(e,t);if(ge()){let e=await de(i);return await V.saveImage({dataBase64:e,mimeType:o,filename:a}),`saved`}if(r()){let e=n()?.vscode;if(e?.saveImage){let t=await W(i);return await e.saveImage({fileName:a,dataUrl:t}),`saved`}}return fe(i,a),`downloaded`},ve=e=>e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`),ye=e=>e?Object.entries(e).map(([e,t])=>t==null?``:`${e.startsWith(`--`)?e:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}:${typeof t==`number`?`${t}px`:t};`).join(``):``,K=(e,t,n)=>{let r=e.querySelector(`pre`);r&&(r.style.margin=`0`,r.style.background=`transparent`,r.style.whiteSpace=t?`pre-wrap`:`pre`,t&&(r.style.wordBreak=`break-word`,r.style.overflowWrap=`break-word`));let i=e.querySelector(`code`);if(i){let e=ye(n);e&&i.setAttribute(`style`,`${i.getAttribute(`style`)??``}${e}`)}},be=e=>`<pre><code>${ve(e)}</code></pre>`,xe=e=>{let t=(0,k.c)(21),{code:n,language:r,className:i,style:a,codeStyle:o,wrap:s}=e,c=s!==void 0&&s,{currentTheme:l}=m(),u=O.useRef(null),d;t[0]===l?d=t[1]:(d=ee(l),t[0]=l,t[1]=d);let p=d,h,g;t[2]!==n||t[3]!==o||t[4]!==c?(h=()=>{let e=u.current;e&&(e.innerHTML=be(n),K(e,c,o))},g=[n,c,o],t[2]=n,t[3]=o,t[4]=c,t[5]=h,t[6]=g):(h=t[5],g=t[6]),O.useLayoutEffect(h,g);let _,v;t[7]!==n||t[8]!==o||t[9]!==r||t[10]!==c?(_=()=>{let e=u.current;if(!e)return;let t=!0,i=new AbortController;return D(n,(r||`text`).toLowerCase(),{signal:i.signal,priority:`visible`}).then(n=>{!t||!e||!n||(e.innerHTML=n,K(e,c,o))}),()=>{t=!1,i.abort()}},v=[n,r,c,o],t[7]=n,t[8]=o,t[9]=r,t[10]=c,t[11]=_,t[12]=v):(_=t[11],v=t[12]),O.useEffect(_,v);let y;t[13]===i?y=t[14]:(y=f(`typography-code`,i),t[13]=i,t[14]=y);let b=p,x;t[15]!==a||t[16]!==b?(x={...b,...a},t[15]=a,t[16]=b,t[17]=x):x=t[17];let S;return t[18]!==y||t[19]!==x?(S=(0,A.jsx)(`div`,{ref:u,className:y,style:x}),t[18]=y,t[19]=x,t[20]=S):S=t[20],S},Se=e=>{let t=e.replace(/^<file>\s*\n?/,``).replace(/\n?<\/file>\s*$/,``);return t=t.replace(/^\s*\d{5}\|\s?/gm,``),t.trim()},Ce=(e,t=``)=>{if(typeof e==`string`)return e;if(e==null)return t;if(typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`)return String(e);try{return JSON.stringify(e)}catch{return t}},we=e=>e?e.includes(`<diagnostics`)||e.includes(`<file_diagnostics>`)||e.includes(`LSP errors detected`)||e.includes(`This file has errors`):!1,Te=e=>e?e.replace(/\n{0,2}LSP errors detected[\s\S]*?<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/\n{0,2}This file has errors[\s\S]*?<\/file_diagnostics>/g,``).replace(/<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/<file_diagnostics>[\s\S]*?<\/file_diagnostics>/g,``).trim():``,Ee=(e,t)=>!e||typeof e!=`object`?String(e):E(e,t||``),q=e=>{if(typeof e==`string`){let t=e.trim();return t.length>0?t:void 0}if(e&&typeof e==`object`){let t=e.patch;if(typeof t==`string`){let e=t.trim();return e.length>0?e:void 0}}},De=e=>{if(!e||typeof e!=`object`)return;let t=q(e.patch)??q(e.diff);if(t)return t;let n=Array.isArray(e.files)?e.files:[];for(let e of n){if(!e||typeof e!=`object`)continue;let t=q(e.patch)??q(e.diff);if(t)return t}},Oe=e=>{if(!e||typeof e!=`string`)return{data:null,isJson:!1};let t=e.trim();if(!t.startsWith(`{`)&&!t.startsWith(`[`)||!t.endsWith(`}`)&&!t.endsWith(`]`)||t.length<2)return{data:null,isJson:!1};try{let e=JSON.parse(t);return typeof e==`object`&&e?{data:e,isJson:!0}:{data:null,isJson:!1}}catch{return{data:null,isJson:!1}}},ke=(e,t,n)=>{let r=Se(e);if((t===`edit`||t===`multiedit`||t===`write`||t===`apply_patch`)&&we(r)&&(r=Te(r)),(t===`edit`||t===`multiedit`||t===`apply_patch`)&&r.trim().length===0){let e=De(n);if(e)return e}return r},Ae=e=>{let t=e.match(/<type>(file|directory)<\/type>/i)?.[1]?.toLowerCase()??`unknown`,n=(e.match(/<content>([\s\S]*?)<\/content>/i)?.[1]??e).replace(/\r\n/g,`
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t}from"./runtime-fetch-WgH0pROs.js";import{Mn as n,ln as r}from"./persistence-BKszKaoQ.js";import{it as i,rt as a}from"./vendor-base-ui-CdzJ6My_.js";import{n as o}from"./platform-CgwxwfA4.js";import{v as s}from"./useUIStore-CEqU9Ujv.js";import{n as c}from"./vendor-react-BP2cRVqX.js";import{a as l,n as u}from"./vendor-aparajita-capacitor-secure-storage-xol_6BsB.js";import{f as d,r as f,t as p}from"./button-CWSs9Baz.js";import{n as m}from"./useThemeSystem-8jhE4Zyk.js";import{n as h}from"./Icon-BCFeHDWx.js";import{i as g,n as _,t as v}from"./tooltip-CV6dvLBp.js";import{a as y,i as b,o as x,t as S}from"./image-loading-Bj11k4gM.js";import{n as C}from"./MarkdownRenderer-DpB0hJ_E.js";import{n as w,o as T,s as E,t as D}from"./markdown-worker-DFNZ3KJ_.js";import{n as ee}from"./markdownTheme-CV7_0QAL.js";import{t as te}from"./vendor-tanstack-react-virtual-B_EO--9t.js";var O=e(i(),1),k=c(),A=a(),ne=`w-[min(30rem,calc(100cqw-1rem))] min-w-[280px] max-w-full overflow-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]`,j={maxWidth:`min(28rem, calc(100cqw - 4ch))`,backgroundColor:`var(--surface-elevated)`,color:`var(--surface-elevated-foreground)`},M=`flex items-center justify-between gap-3 border-b border-[var(--surface-subtle)] px-3 py-2.5`,N=`text-xs font-medium leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,re=`text-xs tabular-nums leading-4 text-muted-foreground md:text-[0.8125rem] md:leading-5`,P=`m-0 list-none divide-y divide-[var(--surface-subtle)] p-0`,F=`${P} max-h-[min(22rem,50vh)] overflow-y-auto`,ie=`${P} w-full`,ae={className:`p-0 rounded-none`,maxHeightClass:`max-h-[46vh]`,disableHorizontal:!0},I=`px-2.5 py-1.5 text-xs leading-4 md:py-2 md:text-[0.8125rem] md:leading-5`,oe=`flex min-w-0 items-center gap-1 text-xs leading-4 text-muted-foreground md:gap-1.5 md:text-[0.8125rem] md:leading-5`,se=`min-w-0 truncate text-xs leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,ce=`inline-flex shrink-0 items-center gap-1 text-xs leading-4 tabular-nums text-muted-foreground md:text-[0.8125rem] md:leading-5`,le=`h-3 w-3 shrink-0 md:h-3.5 md:w-3.5`,ue={in_progress:`text-foreground`,pending:`text-foreground`,completed:`text-muted-foreground line-through`,cancelled:`text-muted-foreground line-through`},L={in_progress:`chat.statusRow.todo.status.inProgress`,pending:`chat.statusRow.todo.status.pending`,completed:`chat.statusRow.todo.status.completed`,cancelled:`chat.statusRow.todo.status.cancelled`},R=e=>{let t=(0,k.c)(26),{todo:n,isActive:r,activeRef:i}=e,{t:a}=d(),o=z.has(n.status)?n.status:`pending`,s=L[o],c;t[0]!==a||t[1]!==s?(c=a(s),t[0]=a,t[1]=s,t[2]=c):c=t[2];let l=c,u;t[3]===o?u=t[4]:(u=o===`in_progress`?(0,A.jsx)(h,{name:`record-circle`,className:`h-3.5 w-3.5 text-[var(--status-info)]`,"aria-hidden":`true`}):o===`completed`?(0,A.jsx)(h,{name:`checkbox-circle`,className:`h-3.5 w-3.5 text-[var(--status-success)]`,"aria-hidden":`true`}):(0,A.jsx)(h,{name:`time`,className:`h-3.5 w-3.5 text-muted-foreground`,"aria-hidden":`true`}),t[3]=o,t[4]=u);let p=u,m=r?`step`:void 0,y=r&&`bg-[var(--surface-muted)] before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-[var(--status-info)]`,b;t[5]===y?b=t[6]:(b=f(`group relative grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] items-start gap-2.5`,I,y),t[5]=y,t[6]=b);let x;t[7]!==p||t[8]!==l?(x=(0,A.jsx)(g,{asChild:!0,children:(0,A.jsx)(`span`,{className:`flex size-4 items-center justify-center`,"aria-label":l,children:p})}),t[7]=p,t[8]=l,t[9]=x):x=t[9];let S;t[10]===l?S=t[11]:(S=(0,A.jsx)(_,{side:`left`,sideOffset:6,children:l}),t[10]=l,t[11]=S);let C;t[12]!==x||t[13]!==S?(C=(0,A.jsxs)(v,{children:[x,S]}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w=ue[o],T;t[15]===w?T=t[16]:(T=f(`block whitespace-normal break-words`,w),t[15]=w,t[16]=T);let E;t[17]!==T||t[18]!==n.content?(E=(0,A.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,A.jsx)(`span`,{className:T,children:n.content})}),t[17]=T,t[18]=n.content,t[19]=E):E=t[19];let D;return t[20]!==i||t[21]!==E||t[22]!==m||t[23]!==b||t[24]!==C?(D=(0,A.jsxs)(`li`,{ref:i,"aria-current":m,className:b,children:[C,E]}),t[20]=i,t[21]=E,t[22]=m,t[23]=b,t[24]=C,t[25]=D):D=t[25],D},z=new Set([`in_progress`,`pending`,`completed`,`cancelled`]),B=`image/png`,V=l(`OpenChamberMedia`),H=e=>{let t=e.toLowerCase().split(`;`)[0]?.trim()||B;if(t===`image/jpeg`||t===`image/jpg`)return`jpg`;if(t===`image/webp`)return`webp`;if(t===`image/gif`)return`gif`;if(t===`image/svg+xml`)return`svg`;if(t===`image/png`)return`png`;let n=t.split(`/`)[1];return n&&/^[a-z0-9.+-]+$/i.test(n)?n:`png`},U=(e,t)=>{let n=`image.${H(t)}`,r=(((e||``).trim()||n).split(/[\\/]/).pop()||n).replace(/[^\w.\- ()[\]]+/g,`_`).replace(/^\.+/,``);return r?/\.[a-z0-9]{1,8}$/i.test(r)?r:`${r}.${H(t)}`:n},W=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{if(typeof r.result==`string`){t(r.result);return}n(Error(`Failed to encode image`))},r.onerror=()=>n(r.error??Error(`Failed to encode image`)),r.readAsDataURL(e)}),de=async e=>{let t=await W(e),n=t.indexOf(`,`);return n>=0?t.slice(n+1):t},fe=(e,t)=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)},G=async(e,t)=>{let n=await fetch(e,{signal:t,cache:`no-store`});if(!n.ok)throw Error(`Image fetch failed (${n.status})`);return n.blob()},pe=async(e,t=B)=>{if(!e.naturalWidth||!e.naturalHeight)return null;let n=e.currentSrc||e.src;if(n&&(/^(?:blob:|data:|openchamber-asset:)/i.test(n)||/^https?:/i.test(n)||/^\/api(?=\/|[?#]|$)/.test(n)))try{let e=await G(n);if(e.size>0)return e}catch{}try{let n=document.createElement(`canvas`);n.width=e.naturalWidth,n.height=e.naturalHeight;let r=n.getContext(`2d`);if(!r)return null;r.drawImage(e,0,0);let i=t.startsWith(`image/`)?t:B,a=await new Promise(e=>{n.toBlob(t=>e(t),i===`image/jpg`?`image/jpeg`:i)});return a&&a.size>0?a:null}catch{return null}},me=async(e,t)=>{let n=``;try{return n=await S(e,t??new AbortController().signal),await G(n,t)}finally{n&&y(n)}},he=async(e,n)=>{let r=e.effectiveDirectory??``,i=null;e.prefetchedBlob&&e.prefetchedBlob.size>0&&(i=e.prefetchedBlob);let a=e.displayUrl?.trim();if(!i&&a)try{i=await G(a,n)}catch{i=null}if(!i){let a=e.sourceUrl.trim();if(!a)throw Error(`Image source is empty`);if(/^(?:data|blob):/i.test(a)||/^https?:/i.test(a)||/^\/api(?=\/|[?#]|$)/.test(a))i=await G(a,n);else{let e=x(a,r);if(b(e))try{i=await me(e.path,n)}catch{let r=await t(`/api/fs/raw`,{method:`GET`,cache:`no-store`,query:{path:e.path},signal:n});if(!r.ok)throw Error(`Image source request failed with status ${r.status}`);i=await r.blob()}else i=await G(e.source,n)}}if(!i||i.size<=0)throw Error(`Image is empty`);let o=(e.mimeType||(i.type&&i.type.startsWith(`image/`)?i.type:``)||B).split(`;`)[0].trim()||B;return{blob:i,filename:U(e.filename,o),mimeType:o}},ge=()=>!o()||u===void 0?!1:u.isPluginAvailable(`OpenChamberMedia`),_e=async(e,t)=>{let{blob:i,filename:a,mimeType:o}=await he(e,t);if(ge()){let e=await de(i);return await V.saveImage({dataBase64:e,mimeType:o,filename:a}),`saved`}if(r()){let e=n()?.vscode;if(e?.saveImage){let t=await W(i);return await e.saveImage({fileName:a,dataUrl:t}),`saved`}}return fe(i,a),`downloaded`},ve=e=>e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`),ye=e=>e?Object.entries(e).map(([e,t])=>t==null?``:`${e.startsWith(`--`)?e:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}:${typeof t==`number`?`${t}px`:t};`).join(``):``,K=(e,t,n)=>{let r=e.querySelector(`pre`);r&&(r.style.margin=`0`,r.style.background=`transparent`,r.style.whiteSpace=t?`pre-wrap`:`pre`,t&&(r.style.wordBreak=`break-word`,r.style.overflowWrap=`break-word`));let i=e.querySelector(`code`);if(i){let e=ye(n);e&&i.setAttribute(`style`,`${i.getAttribute(`style`)??``}${e}`)}},be=e=>`<pre><code>${ve(e)}</code></pre>`,xe=e=>{let t=(0,k.c)(21),{code:n,language:r,className:i,style:a,codeStyle:o,wrap:s}=e,c=s!==void 0&&s,{currentTheme:l}=m(),u=O.useRef(null),d;t[0]===l?d=t[1]:(d=ee(l),t[0]=l,t[1]=d);let p=d,h,g;t[2]!==n||t[3]!==o||t[4]!==c?(h=()=>{let e=u.current;e&&(e.innerHTML=be(n),K(e,c,o))},g=[n,c,o],t[2]=n,t[3]=o,t[4]=c,t[5]=h,t[6]=g):(h=t[5],g=t[6]),O.useLayoutEffect(h,g);let _,v;t[7]!==n||t[8]!==o||t[9]!==r||t[10]!==c?(_=()=>{let e=u.current;if(!e)return;let t=!0,i=new AbortController;return D(n,(r||`text`).toLowerCase(),{signal:i.signal,priority:`visible`}).then(n=>{!t||!e||!n||(e.innerHTML=n,K(e,c,o))}),()=>{t=!1,i.abort()}},v=[n,r,c,o],t[7]=n,t[8]=o,t[9]=r,t[10]=c,t[11]=_,t[12]=v):(_=t[11],v=t[12]),O.useEffect(_,v);let y;t[13]===i?y=t[14]:(y=f(`typography-code`,i),t[13]=i,t[14]=y);let b=p,x;t[15]!==a||t[16]!==b?(x={...b,...a},t[15]=a,t[16]=b,t[17]=x):x=t[17];let S;return t[18]!==y||t[19]!==x?(S=(0,A.jsx)(`div`,{ref:u,className:y,style:x}),t[18]=y,t[19]=x,t[20]=S):S=t[20],S},Se=e=>{let t=e.replace(/^<file>\s*\n?/,``).replace(/\n?<\/file>\s*$/,``);return t=t.replace(/^\s*\d{5}\|\s?/gm,``),t.trim()},Ce=(e,t=``)=>{if(typeof e==`string`)return e;if(e==null)return t;if(typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`)return String(e);try{return JSON.stringify(e)}catch{return t}},we=e=>e?e.includes(`<diagnostics`)||e.includes(`<file_diagnostics>`)||e.includes(`LSP errors detected`)||e.includes(`This file has errors`):!1,Te=e=>e?e.replace(/\n{0,2}LSP errors detected[\s\S]*?<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/\n{0,2}This file has errors[\s\S]*?<\/file_diagnostics>/g,``).replace(/<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/<file_diagnostics>[\s\S]*?<\/file_diagnostics>/g,``).trim():``,Ee=(e,t)=>!e||typeof e!=`object`?String(e):E(e,t||``),q=e=>{if(typeof e==`string`){let t=e.trim();return t.length>0?t:void 0}if(e&&typeof e==`object`){let t=e.patch;if(typeof t==`string`){let e=t.trim();return e.length>0?e:void 0}}},De=e=>{if(!e||typeof e!=`object`)return;let t=q(e.patch)??q(e.diff);if(t)return t;let n=Array.isArray(e.files)?e.files:[];for(let e of n){if(!e||typeof e!=`object`)continue;let t=q(e.patch)??q(e.diff);if(t)return t}},Oe=e=>{if(!e||typeof e!=`string`)return{data:null,isJson:!1};let t=e.trim();if(!t.startsWith(`{`)&&!t.startsWith(`[`)||!t.endsWith(`}`)&&!t.endsWith(`]`)||t.length<2)return{data:null,isJson:!1};try{let e=JSON.parse(t);return typeof e==`object`&&e?{data:e,isJson:!0}:{data:null,isJson:!1}}catch{return{data:null,isJson:!1}}},ke=(e,t,n)=>{let r=Se(e);if((t===`edit`||t===`multiedit`||t===`write`||t===`apply_patch`)&&we(r)&&(r=Te(r)),(t===`edit`||t===`multiedit`||t===`apply_patch`)&&r.trim().length===0){let e=De(n);if(e)return e}return r},Ae=e=>{let t=e.match(/<type>(file|directory)<\/type>/i)?.[1]?.toLowerCase()??`unknown`,n=(e.match(/<content>([\s\S]*?)<\/content>/i)?.[1]??e).replace(/\r\n/g,`
|
|
2
2
|
`).split(`
|
|
3
3
|
`),r=e=>/\(\s*File has more lines\..*offset.*\)/i.test(e.trim());return{type:t,lines:n.map(e=>{let n=e.trim(),i=n.startsWith(`(`)&&n.endsWith(`)`)||r(n);if(t!==`directory`){let t=e.match(/^(\d+):\s?(.*)$/);if(t){let e=t[2],n=e.trim(),i=n.startsWith(`(`)&&n.endsWith(`)`)||r(n);return{lineNumber:i?null:Number(t[1]),text:e,isInfo:i}}}return{lineNumber:null,text:e,isInfo:i}}).filter((e,t,n)=>{if(e.text.trim().length>0)return!0;let r=n[t-1],i=n[t+1],a=!!(r?.isInfo||i?.isInfo),o=e.lineNumber!==null;return!(a&&o)})}},je=(e,t)=>{try{let n=e.trim().split(`
|
|
4
4
|
`).filter(Boolean);if(n.length===0)return null;let r=[];return n.forEach(e=>{let t=e.match(/^(\s*)(.+)$/);if(t){let[,e,n]=t,i=Math.floor(e.length/2),a=!n.endsWith(`/`);r.push({name:n.replace(/\/$/,``),depth:i,isFile:a})}}),(0,A.jsx)(`div`,{className:f(`w-full min-w-0 font-mono space-y-0.5`,t?.unstyled?null:`p-3 bg-muted/20 rounded-xl border border-border/30`),style:s.tool.popup,children:r.map((e,t)=>(0,A.jsx)(`div`,{className:`min-w-0`,style:{paddingLeft:`${e.depth*20}px`},children:e.isFile?(0,A.jsx)(`span`,{className:`text-foreground/90 block truncate`,children:e.name}):(0,A.jsxs)(`span`,{className:`font-semibold text-foreground block truncate`,children:[e.name,`/`]})},t))})}catch{return null}},Me={backgroundColor:`var(--status-info)`,opacity:.6},Ne=(e,t,n)=>{try{let r=e.trim().split(`
|
package/dist/index.html
CHANGED
|
@@ -552,7 +552,7 @@
|
|
|
552
552
|
opacity: 0.72;
|
|
553
553
|
}
|
|
554
554
|
</style>
|
|
555
|
-
<script type="module" crossorigin src="/assets/main-
|
|
555
|
+
<script type="module" crossorigin src="/assets/main-HsqXFi70.js"></script>
|
|
556
556
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
557
557
|
<link rel="modulepreload" crossorigin href="/assets/runtime-auth-BpCQ-dPr.js">
|
|
558
558
|
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-CMiMO5Yj.js">
|
|
@@ -572,7 +572,7 @@
|
|
|
572
572
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-rxsMWL0o.js">
|
|
573
573
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
574
574
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-KNLHDj7G.js">
|
|
575
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
575
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DAxmpV5p.js">
|
|
576
576
|
<link rel="stylesheet" crossorigin href="/assets/src-CdGit3gf.css">
|
|
577
577
|
</head>
|
|
578
578
|
<body class="h-full bg-background text-foreground">
|
package/dist/mini-chat.html
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
}
|
|
75
75
|
</style>
|
|
76
76
|
|
|
77
|
-
<script type="module" crossorigin src="/assets/miniChat-
|
|
77
|
+
<script type="module" crossorigin src="/assets/miniChat-B7C-IBFC.js"></script>
|
|
78
78
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
79
79
|
<link rel="modulepreload" crossorigin href="/assets/runtime-auth-BpCQ-dPr.js">
|
|
80
80
|
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-CMiMO5Yj.js">
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-rxsMWL0o.js">
|
|
95
95
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
96
96
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-KNLHDj7G.js">
|
|
97
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
97
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DAxmpV5p.js">
|
|
98
98
|
<link rel="stylesheet" crossorigin href="/assets/src-CdGit3gf.css">
|
|
99
99
|
</head>
|
|
100
100
|
<body class="h-full bg-background text-foreground">
|
package/dist/mobile.html
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
})();
|
|
64
64
|
</script>
|
|
65
65
|
|
|
66
|
-
<script type="module" crossorigin src="/assets/mobile-
|
|
66
|
+
<script type="module" crossorigin src="/assets/mobile-CqWR9PBo.js"></script>
|
|
67
67
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
68
68
|
<link rel="modulepreload" crossorigin href="/assets/runtime-auth-BpCQ-dPr.js">
|
|
69
69
|
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-CMiMO5Yj.js">
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-rxsMWL0o.js">
|
|
84
84
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
85
85
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-KNLHDj7G.js">
|
|
86
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
86
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DAxmpV5p.js">
|
|
87
87
|
<link rel="stylesheet" crossorigin href="/assets/src-CdGit3gf.css">
|
|
88
88
|
</head>
|
|
89
89
|
<body class="h-full bg-background text-foreground">
|
package/package.json
CHANGED
|
@@ -121,6 +121,23 @@ observer active after startup refresh work becomes idle, and successful
|
|
|
121
121
|
event-driven index writes with a semantic snapshot change publish a new
|
|
122
122
|
revision tip immediately.
|
|
123
123
|
|
|
124
|
+
Background directory and child-enrichment progress share a fixed 500ms trailing
|
|
125
|
+
tip window: the first pending change arms the injected timer, later changes do
|
|
126
|
+
not reset it, and delivery reads the latest revision and sync flags. New-batch
|
|
127
|
+
enqueue, idle (`active=false` and `enriching=false`), `publishChange()` (including
|
|
128
|
+
pin/rename and event-driven writes), and stop bypass the window and cancel its
|
|
129
|
+
pending delivery. These immediate edges are exceptions to the 500ms rate bound.
|
|
130
|
+
Stop publishes idle with interrupted directories marked failed; late worker
|
|
131
|
+
cleanup cannot emit another revision or leave a pending tip timer. Internal
|
|
132
|
+
revision increments and `waitForChange` delivery remain immediate, including the
|
|
133
|
+
final state. Full snapshots are materialized for waiting readers, not for each
|
|
134
|
+
tip. No persisted revision, failure, or runtime-isolation semantics change.
|
|
135
|
+
|
|
136
|
+
The shared renderer independently throttles busy tips to a fixed 1000ms window
|
|
137
|
+
for compatibility with older Hosts. Idle/legacy tips use 100ms debounce and
|
|
138
|
+
shorten a pending busy deadline without extending it. Stream-ready and safety
|
|
139
|
+
timeout repair retain their existing behavior; GET remains authoritative.
|
|
140
|
+
|
|
124
141
|
The OpenCode proxy calls `noteInteractiveRequest()` for selected-session reads
|
|
125
142
|
and mutations. That aborts the current background list, yields for one second,
|
|
126
143
|
then resumes the same directory. Consequently a startup index refresh cannot
|
|
@@ -7,6 +7,7 @@ const FULL_RECONCILE_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
|
7
7
|
const FETCH_TIMEOUT_MS = 6_000;
|
|
8
8
|
const INTERACTIVE_YIELD_MS = 1_000;
|
|
9
9
|
const LONG_POLL_MAX_MS = 25_000;
|
|
10
|
+
const REVISION_TIP_THROTTLE_MS = 500;
|
|
10
11
|
|
|
11
12
|
const normalizeDirectory = (value) => {
|
|
12
13
|
if (typeof value !== 'string') return null;
|
|
@@ -64,7 +65,7 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
64
65
|
buildOpenCodeUrl,
|
|
65
66
|
getOpenCodeAuthHeaders,
|
|
66
67
|
waitForOpenCodeReady,
|
|
67
|
-
/** Optional tip sink: ({ revision, sync })
|
|
68
|
+
/** Optional tip sink: ({ revision, sync }); background progress is coalesced. */
|
|
68
69
|
onRevisionTip = null,
|
|
69
70
|
/**
|
|
70
71
|
* Optional Host authority projection applied to each upstream list page
|
|
@@ -96,10 +97,11 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
96
97
|
const failedDirectories = new Set();
|
|
97
98
|
const waiters = new Set();
|
|
98
99
|
let batchDirectories = new Set();
|
|
100
|
+
let revisionTipTimer;
|
|
99
101
|
|
|
100
102
|
const syncState = () => ({
|
|
101
|
-
active: running || queue.length > 0,
|
|
102
|
-
enriching: enrichingChildFlags || childFlagQueue.length > 0,
|
|
103
|
+
active: !stopped && (running || queue.length > 0),
|
|
104
|
+
enriching: !stopped && (enrichingChildFlags || childFlagQueue.length > 0),
|
|
103
105
|
completed: completedDirectories.size,
|
|
104
106
|
total: batchDirectories.size,
|
|
105
107
|
pendingDirectories: [...queuedDirectories],
|
|
@@ -113,17 +115,17 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
113
115
|
...sessionIndexService.snapshot(),
|
|
114
116
|
});
|
|
115
117
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
const broadcastRevisionTip = () => {
|
|
119
|
+
if (revisionTipTimer !== undefined) clearTimer(revisionTipTimer);
|
|
120
|
+
revisionTipTimer = undefined;
|
|
121
|
+
const sync = syncState();
|
|
120
122
|
if (typeof onRevisionTip === 'function') {
|
|
121
123
|
try {
|
|
122
124
|
onRevisionTip({
|
|
123
|
-
revision
|
|
125
|
+
revision,
|
|
124
126
|
sync: {
|
|
125
|
-
active:
|
|
126
|
-
enriching:
|
|
127
|
+
active: sync.active,
|
|
128
|
+
enriching: sync.enriching,
|
|
127
129
|
},
|
|
128
130
|
occurredAt: now(),
|
|
129
131
|
});
|
|
@@ -133,6 +135,21 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
133
135
|
}
|
|
134
136
|
};
|
|
135
137
|
|
|
138
|
+
const notify = (immediate = false) => {
|
|
139
|
+
if (stopped && !immediate) return;
|
|
140
|
+
revision += 1;
|
|
141
|
+
if (waiters.size > 0) {
|
|
142
|
+
const value = snapshot();
|
|
143
|
+
for (const resolve of [...waiters]) resolve(value);
|
|
144
|
+
}
|
|
145
|
+
const sync = syncState();
|
|
146
|
+
if (immediate || (!sync.active && !sync.enriching)) {
|
|
147
|
+
broadcastRevisionTip();
|
|
148
|
+
} else if (revisionTipTimer === undefined && typeof onRevisionTip === 'function') {
|
|
149
|
+
revisionTipTimer = setTimer(broadcastRevisionTip, REVISION_TIP_THROTTLE_MS);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
136
153
|
const delay = (ms, signal) => new Promise((resolve) => {
|
|
137
154
|
if (ms <= 0 || signal?.aborted) {
|
|
138
155
|
resolve();
|
|
@@ -414,6 +431,7 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
414
431
|
};
|
|
415
432
|
|
|
416
433
|
const enqueue = (directories) => {
|
|
434
|
+
if (stopped) return snapshot();
|
|
417
435
|
const nextRuntimeKey = sessionIndexService.getRuntimeKey();
|
|
418
436
|
if (nextRuntimeKey !== runtimeKey) {
|
|
419
437
|
runtimeKey = nextRuntimeKey;
|
|
@@ -427,7 +445,8 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
427
445
|
failedDirectories.clear();
|
|
428
446
|
batchDirectories = new Set();
|
|
429
447
|
}
|
|
430
|
-
|
|
448
|
+
const newBatch = !running && queue.length === 0;
|
|
449
|
+
if (newBatch) {
|
|
431
450
|
completedDirectories.clear();
|
|
432
451
|
failedDirectories.clear();
|
|
433
452
|
batchDirectories = new Set();
|
|
@@ -443,7 +462,7 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
443
462
|
currentWasPreempted = true;
|
|
444
463
|
currentController.abort();
|
|
445
464
|
}
|
|
446
|
-
notify();
|
|
465
|
+
notify(newBatch);
|
|
447
466
|
void run();
|
|
448
467
|
return snapshot();
|
|
449
468
|
};
|
|
@@ -458,7 +477,7 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
458
477
|
|
|
459
478
|
const publishChange = () => {
|
|
460
479
|
if (stopped) return;
|
|
461
|
-
notify();
|
|
480
|
+
notify(true);
|
|
462
481
|
};
|
|
463
482
|
|
|
464
483
|
const waitForChange = (since, { signal, timeoutMs = LONG_POLL_MAX_MS } = {}) => {
|
|
@@ -481,6 +500,7 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
481
500
|
};
|
|
482
501
|
|
|
483
502
|
const stop = () => {
|
|
503
|
+
if (stopped) return;
|
|
484
504
|
stopped = true;
|
|
485
505
|
currentController?.abort();
|
|
486
506
|
const interruptedDirectories = [...queuedDirectories];
|
|
@@ -489,7 +509,7 @@ export const createSessionIndexSyncRuntime = ({
|
|
|
489
509
|
queuedDirectories.clear();
|
|
490
510
|
queuedChildFlagKeys.clear();
|
|
491
511
|
for (const directory of interruptedDirectories) failedDirectories.add(directory);
|
|
492
|
-
notify();
|
|
512
|
+
notify(true);
|
|
493
513
|
};
|
|
494
514
|
|
|
495
515
|
return { enqueue, noteInteractiveRequest, publishChange, snapshot, waitForChange, stop };
|
|
@@ -41,6 +41,127 @@ const isFullyIdle = (runtime) => (
|
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
describe('session index background sync runtime', () => {
|
|
44
|
+
it('coalesces hundreds of directory/child checks and immediately publishes the final revision', async () => {
|
|
45
|
+
vi.useFakeTimers();
|
|
46
|
+
try {
|
|
47
|
+
const service = createService();
|
|
48
|
+
const revisionsAtDelivery = [];
|
|
49
|
+
const tips = vi.fn(() => { revisionsAtDelivery.push(runtime.snapshot().revision); });
|
|
50
|
+
const runtime = createSessionIndexSyncRuntime({
|
|
51
|
+
sessionIndexService: service,
|
|
52
|
+
buildOpenCodeUrl: (route) => `http://opencode.test${route}`,
|
|
53
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
54
|
+
onRevisionTip: tips,
|
|
55
|
+
setTimer: setTimeout,
|
|
56
|
+
clearTimer: clearTimeout,
|
|
57
|
+
fetchFn: async (url) => {
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
59
|
+
const directory = url.searchParams.get('directory');
|
|
60
|
+
return new Response(JSON.stringify({ data: url.searchParams.has('parentID') ? []
|
|
61
|
+
: Array.from({ length: 20 }, (_, i) => session(`${directory}-${i}`, 100 - i, directory)) }));
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
runtime.enqueue(Array.from({ length: 15 }, (_, i) => `/repo/${i}`));
|
|
65
|
+
expect(tips).toHaveBeenCalledTimes(1);
|
|
66
|
+
expect(tips.mock.calls[0][0]).toMatchObject({ revision: 1, sync: { active: true } });
|
|
67
|
+
await vi.advanceTimersByTimeAsync(499);
|
|
68
|
+
expect(tips).toHaveBeenCalledTimes(1);
|
|
69
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
70
|
+
expect(tips).toHaveBeenCalledTimes(2);
|
|
71
|
+
const progress = runtime.waitForChange(runtime.snapshot().revision);
|
|
72
|
+
await vi.advanceTimersByTimeAsync(10);
|
|
73
|
+
expect((await progress).revision).toBe(runtime.snapshot().revision);
|
|
74
|
+
const final = (async () => {
|
|
75
|
+
let value = runtime.snapshot();
|
|
76
|
+
while (value.sync.active || value.sync.enriching) value = await runtime.waitForChange(value.revision);
|
|
77
|
+
return value;
|
|
78
|
+
})();
|
|
79
|
+
await vi.advanceTimersByTimeAsync(2640);
|
|
80
|
+
expect(service.replaceDirectory).toHaveBeenCalledTimes(15);
|
|
81
|
+
expect(service.replaceChildSessions).toHaveBeenCalledTimes(300);
|
|
82
|
+
expect(isFullyIdle(runtime)).toBe(true);
|
|
83
|
+
expect(tips).toHaveBeenCalledTimes(8);
|
|
84
|
+
expect(tips.mock.calls.map(([tip]) => tip.revision)).toEqual(revisionsAtDelivery);
|
|
85
|
+
expect(tips.mock.lastCall[0]).toMatchObject({ revision: runtime.snapshot().revision, sync: { active: false, enriching: false } });
|
|
86
|
+
expect((await final).revision).toBe(runtime.snapshot().revision);
|
|
87
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
88
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
89
|
+
expect(tips).toHaveBeenCalledTimes(8);
|
|
90
|
+
runtime.stop();
|
|
91
|
+
} finally {
|
|
92
|
+
vi.useRealTimers();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('publishes external changes immediately while busy and stop cancels the pending tip', async () => {
|
|
97
|
+
vi.useFakeTimers();
|
|
98
|
+
try {
|
|
99
|
+
const tips = vi.fn();
|
|
100
|
+
let releaseReady;
|
|
101
|
+
const runtime = createSessionIndexSyncRuntime({
|
|
102
|
+
sessionIndexService: createService(),
|
|
103
|
+
onRevisionTip: tips,
|
|
104
|
+
waitForOpenCodeReady: () => new Promise((resolve) => { releaseReady = resolve; }),
|
|
105
|
+
setTimer: setTimeout,
|
|
106
|
+
clearTimer: clearTimeout,
|
|
107
|
+
});
|
|
108
|
+
runtime.enqueue(['/repo']);
|
|
109
|
+
expect(vi.getTimerCount()).toBe(1);
|
|
110
|
+
runtime.publishChange();
|
|
111
|
+
expect(tips).toHaveBeenCalledTimes(2);
|
|
112
|
+
expect(tips.mock.lastCall[0].revision).toBe(runtime.snapshot().revision);
|
|
113
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
114
|
+
runtime.enqueue(['/repo/second']);
|
|
115
|
+
expect(vi.getTimerCount()).toBe(1);
|
|
116
|
+
const final = runtime.waitForChange(runtime.snapshot().revision);
|
|
117
|
+
runtime.stop();
|
|
118
|
+
const stoppedRevision = runtime.snapshot().revision;
|
|
119
|
+
expect(tips).toHaveBeenCalledTimes(3);
|
|
120
|
+
expect(tips.mock.lastCall[0]).toMatchObject({ revision: stoppedRevision, sync: { active: false, enriching: false } });
|
|
121
|
+
await expect(final).resolves.toMatchObject({ revision: stoppedRevision, sync: { active: false, failedDirectories: ['/repo', '/repo/second'] } });
|
|
122
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
123
|
+
releaseReady(true);
|
|
124
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
125
|
+
expect(runtime.snapshot().revision).toBe(stoppedRevision);
|
|
126
|
+
expect(tips).toHaveBeenCalledTimes(3);
|
|
127
|
+
} finally {
|
|
128
|
+
vi.useRealTimers();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('stopping during enrichment flushes idle and fences late worker notifications', async () => {
|
|
133
|
+
vi.useFakeTimers();
|
|
134
|
+
try {
|
|
135
|
+
const tips = vi.fn();
|
|
136
|
+
const runtime = createSessionIndexSyncRuntime({
|
|
137
|
+
sessionIndexService: createService(),
|
|
138
|
+
buildOpenCodeUrl: (route) => `http://opencode.test${route}`,
|
|
139
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
140
|
+
onRevisionTip: tips,
|
|
141
|
+
fetchFn: async (url, { signal }) => {
|
|
142
|
+
if (!url.searchParams.has('parentID')) return new Response(JSON.stringify([session('root', 1)]));
|
|
143
|
+
return new Promise((_resolve, reject) => {
|
|
144
|
+
signal.addEventListener('abort', () => reject(new DOMException('aborted', 'AbortError')), { once: true });
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
runtime.enqueue(['/repo']);
|
|
149
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
150
|
+
expect(runtime.snapshot().sync.enriching).toBe(true);
|
|
151
|
+
const final = runtime.waitForChange(runtime.snapshot().revision);
|
|
152
|
+
runtime.stop();
|
|
153
|
+
const revision = runtime.snapshot().revision;
|
|
154
|
+
await expect(final).resolves.toMatchObject({ revision, sync: { active: false, enriching: false } });
|
|
155
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
156
|
+
expect(tips).toHaveBeenCalledTimes(2);
|
|
157
|
+
expect(tips.mock.lastCall[0]).toMatchObject({ revision, sync: { active: false, enriching: false } });
|
|
158
|
+
expect(runtime.snapshot().revision).toBe(revision);
|
|
159
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
160
|
+
} finally {
|
|
161
|
+
vi.useRealTimers();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
44
165
|
it.each([1, 20, 21])('only advertises more when terminal pages contain over 20 visible roots (%i roots)', async (rootCount) => {
|
|
45
166
|
const service = createService();
|
|
46
167
|
const rows = Array.from({ length: Math.max(20, rootCount) }, (_, i) => ({
|