@nextclaw/ui 0.11.9 → 0.11.10

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/assets/{ChannelsList-XGMfinnc.js → ChannelsList-C63gOoYI.js} +3 -3
  3. package/dist/assets/ChatPage-Ci3Gz0qh.js +37 -0
  4. package/dist/assets/{DocBrowser-DTRCNsSM.js → DocBrowser-CI4jOzJY.js} +1 -1
  5. package/dist/assets/{LogoBadge-CPMOwWdA.js → LogoBadge-DImV63-L.js} +1 -1
  6. package/dist/assets/{MarketplacePage-De2qZ9C0.js → MarketplacePage-B360oSAV.js} +1 -1
  7. package/dist/assets/{McpMarketplacePage-cjVKSQ2f.js → McpMarketplacePage-KIQgx_7h.js} +2 -2
  8. package/dist/assets/{ModelConfig-CMn3-VZk.js → ModelConfig-Ben3tQoX.js} +1 -1
  9. package/dist/assets/{ProvidersList-CArDOswN.js → ProvidersList-DE-S9mq0.js} +1 -1
  10. package/dist/assets/RemoteAccessPage-DxUia6R-.js +1 -0
  11. package/dist/assets/RuntimeConfig-CQcGfNZT.js +1 -0
  12. package/dist/assets/{SearchConfig-a38m8Ynx.js → SearchConfig-DeOa-M6j.js} +1 -1
  13. package/dist/assets/{SecretsConfig-B6mf4JY9.js → SecretsConfig-Ci8pJmzd.js} +2 -2
  14. package/dist/assets/{SessionsConfig-B_WQ1lVd.js → SessionsConfig-B6zq55yu.js} +2 -2
  15. package/dist/assets/chat-session-display--oo5yuIw.js +1 -0
  16. package/dist/assets/{index-D-wEIgPn.js → index-LhlkB00c.js} +4 -4
  17. package/dist/assets/{label-usOOP7mv.js → label-3TKt0PoZ.js} +1 -1
  18. package/dist/assets/{page-layout-CuIf20mx.js → page-layout-CopkIM3Q.js} +1 -1
  19. package/dist/assets/{popover-CTtTCP5d.js → popover-CUx8uRJw.js} +1 -1
  20. package/dist/assets/security-config-BL29kTzz.js +1 -0
  21. package/dist/assets/{skeleton-BNUaFYE7.js → skeleton-Bs4zvcql.js} +1 -1
  22. package/dist/assets/{status-dot-BeHTBy9k.js → status-dot-D6vJMwD7.js} +1 -1
  23. package/dist/assets/{switch-CtNnWZpa.js → switch-A3-ClT1P.js} +1 -1
  24. package/dist/assets/{tabs-custom-Dz_4tV62.js → tabs-custom-BVSd5urq.js} +1 -1
  25. package/dist/assets/{useConfirmDialog-C_n_JIEq.js → useConfirmDialog-ChPriea6.js} +1 -1
  26. package/dist/index.html +1 -1
  27. package/package.json +5 -5
  28. package/src/api/ncp-session-query-cache.test.ts +89 -0
  29. package/src/api/ncp-session-query-cache.ts +85 -0
  30. package/src/api/types.ts +2 -0
  31. package/src/components/chat/ChatConversationPanel.test.tsx +1 -1
  32. package/src/components/chat/ChatConversationPanel.tsx +6 -6
  33. package/src/components/chat/ChatSidebar.test.tsx +87 -92
  34. package/src/components/chat/ChatSidebar.tsx +21 -36
  35. package/src/components/chat/chat-session-label.service.ts +3 -3
  36. package/src/components/chat/containers/chat-message-list.container.test.tsx +53 -8
  37. package/src/components/chat/containers/chat-message-list.container.tsx +15 -14
  38. package/src/components/chat/managers/chat-session-list.manager.ts +0 -18
  39. package/src/components/chat/ncp/NcpChatPage.tsx +4 -52
  40. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +4 -18
  41. package/src/components/chat/ncp/ncp-chat.presenter.ts +0 -2
  42. package/src/components/chat/ncp/ncp-session-adapter.test.ts +0 -23
  43. package/src/components/chat/ncp/ncp-session-adapter.ts +0 -19
  44. package/src/components/chat/ncp/use-ncp-session-list-view.ts +42 -0
  45. package/src/components/chat/presenter/chat-presenter-context.tsx +0 -3
  46. package/src/components/chat/stores/chat-session-list.store.ts +1 -7
  47. package/src/components/chat/stores/chat-thread.store.ts +3 -3
  48. package/src/hooks/use-realtime-query-bridge.ts +14 -19
  49. package/src/hooks/useConfig.ts +10 -11
  50. package/dist/assets/ChatPage-DYTcCRPp.js +0 -37
  51. package/dist/assets/RemoteAccessPage-C0I4tHey.js +0 -1
  52. package/dist/assets/RuntimeConfig-B4o6uJq9.js +0 -1
  53. package/dist/assets/ncp-session-adapter-DSacECph.js +0 -1
  54. package/dist/assets/security-config-Bxrrv8Ac.js +0 -1
  55. package/src/components/chat/managers/chat-run-status.manager.ts +0 -32
  56. package/src/components/chat/stores/chat-run-status.store.ts +0 -30
@@ -1 +1 @@
1
- import{r as s,j as o}from"./vendor-BEQcLDx6.js";import{c as t}from"./index-D-wEIgPn.js";const l=s.forwardRef(({className:e,...a},r)=>o.jsx("label",{ref:r,className:t("text-sm font-medium leading-none text-gray-700 peer-disabled:cursor-not-allowed peer-disabled:opacity-70",e),...a}));l.displayName="Label";export{l as L};
1
+ import{r as s,j as o}from"./vendor-BEQcLDx6.js";import{c as t}from"./index-LhlkB00c.js";const l=s.forwardRef(({className:e,...a},r)=>o.jsx("label",{ref:r,className:t("text-sm font-medium leading-none text-gray-700 peer-disabled:cursor-not-allowed peer-disabled:opacity-70",e),...a}));l.displayName="Label";export{l as L};
@@ -1 +1 @@
1
- import{j as e}from"./vendor-BEQcLDx6.js";import{c as l}from"./index-D-wEIgPn.js";function x({children:t,fullHeight:s=!1,className:a}){return e.jsx("div",{className:l("animate-fade-in",s?"h-[calc(100vh-80px)] w-full flex flex-col":"pb-16",a),children:t})}function c({title:t,description:s,actions:a,className:r}){return e.jsxs("div",{className:l("flex items-center justify-between mb-6 shrink-0",r),children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:t}),s&&e.jsx("p",{className:"text-sm text-gray-500 mt-1",children:s})]}),a&&e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:a})]})}export{x as P,c as a};
1
+ import{j as e}from"./vendor-BEQcLDx6.js";import{c as l}from"./index-LhlkB00c.js";function x({children:t,fullHeight:s=!1,className:a}){return e.jsx("div",{className:l("animate-fade-in",s?"h-[calc(100vh-80px)] w-full flex flex-col":"pb-16",a),children:t})}function c({title:t,description:s,actions:a,className:r}){return e.jsxs("div",{className:l("flex items-center justify-between mb-6 shrink-0",r),children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:t}),s&&e.jsx("p",{className:"text-sm text-gray-500 mt-1",children:s})]}),a&&e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:a})]})}export{x as P,c as a};
@@ -1 +1 @@
1
- import{r as l,aI as $,j as c,aJ as z,aK as G,aL as A,aM as O,aN as P,aO as _,aP as w,aQ as b,aR as H,aS as K,aT as U,aU as V,aV as W,aW as Z,aX as J,aY as Q,aZ as X,a_ as Y}from"./vendor-BEQcLDx6.js";import{c as q}from"./index-D-wEIgPn.js";var m="Popover",[E]=K(m,[w]),g=w(),[B,d]=E(m),j=e=>{const{__scopePopover:a,children:t,open:n,defaultOpen:o,onOpenChange:r,modal:s=!1}=e,i=g(a),p=l.useRef(null),[u,h]=l.useState(!1),[C,f]=$({prop:n,defaultProp:o??!1,onChange:r,caller:m});return c.jsx(z,{...i,children:c.jsx(B,{scope:a,contentId:G(),triggerRef:p,open:C,onOpenChange:f,onOpenToggle:l.useCallback(()=>f(x=>!x),[f]),hasCustomAnchor:u,onCustomAnchorAdd:l.useCallback(()=>h(!0),[]),onCustomAnchorRemove:l.useCallback(()=>h(!1),[]),modal:s,children:t})})};j.displayName=m;var N="PopoverAnchor",y=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(N,t),r=g(t),{onCustomAnchorAdd:s,onCustomAnchorRemove:i}=o;return l.useEffect(()=>(s(),()=>i()),[s,i]),c.jsx(_,{...r,...n,ref:a})});y.displayName=N;var F="PopoverTrigger",S=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(F,t),r=g(t),s=A(a,o.triggerRef),i=c.jsx(O.button,{type:"button","aria-haspopup":"dialog","aria-expanded":o.open,"aria-controls":o.contentId,"data-state":k(o.open),...n,ref:s,onClick:P(e.onClick,o.onOpenToggle)});return o.hasCustomAnchor?i:c.jsx(_,{asChild:!0,...r,children:i})});S.displayName=F;var R="PopoverPortal",[ee,oe]=E(R,{forceMount:void 0}),M=e=>{const{__scopePopover:a,forceMount:t,children:n,container:o}=e,r=d(R,a);return c.jsx(ee,{scope:a,forceMount:t,children:c.jsx(b,{present:t||r.open,children:c.jsx(H,{asChild:!0,container:o,children:n})})})};M.displayName=R;var v="PopoverContent",T=l.forwardRef((e,a)=>{const t=oe(v,e.__scopePopover),{forceMount:n=t.forceMount,...o}=e,r=d(v,e.__scopePopover);return c.jsx(b,{present:n||r.open,children:r.modal?c.jsx(re,{...o,ref:a}):c.jsx(ae,{...o,ref:a})})});T.displayName=v;var te=W("PopoverContent.RemoveScroll"),re=l.forwardRef((e,a)=>{const t=d(v,e.__scopePopover),n=l.useRef(null),o=A(a,n),r=l.useRef(!1);return l.useEffect(()=>{const s=n.current;if(s)return U(s)},[]),c.jsx(V,{as:te,allowPinchZoom:!0,children:c.jsx(D,{...e,ref:o,trapFocus:t.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:P(e.onCloseAutoFocus,s=>{var i;s.preventDefault(),r.current||(i=t.triggerRef.current)==null||i.focus()}),onPointerDownOutside:P(e.onPointerDownOutside,s=>{const i=s.detail.originalEvent,p=i.button===0&&i.ctrlKey===!0,u=i.button===2||p;r.current=u},{checkForDefaultPrevented:!1}),onFocusOutside:P(e.onFocusOutside,s=>s.preventDefault(),{checkForDefaultPrevented:!1})})})}),ae=l.forwardRef((e,a)=>{const t=d(v,e.__scopePopover),n=l.useRef(!1),o=l.useRef(!1);return c.jsx(D,{...e,ref:a,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:r=>{var s,i;(s=e.onCloseAutoFocus)==null||s.call(e,r),r.defaultPrevented||(n.current||(i=t.triggerRef.current)==null||i.focus(),r.preventDefault()),n.current=!1,o.current=!1},onInteractOutside:r=>{var p,u;(p=e.onInteractOutside)==null||p.call(e,r),r.defaultPrevented||(n.current=!0,r.detail.originalEvent.type==="pointerdown"&&(o.current=!0));const s=r.target;((u=t.triggerRef.current)==null?void 0:u.contains(s))&&r.preventDefault(),r.detail.originalEvent.type==="focusin"&&o.current&&r.preventDefault()}})}),D=l.forwardRef((e,a)=>{const{__scopePopover:t,trapFocus:n,onOpenAutoFocus:o,onCloseAutoFocus:r,disableOutsidePointerEvents:s,onEscapeKeyDown:i,onPointerDownOutside:p,onFocusOutside:u,onInteractOutside:h,...C}=e,f=d(v,t),x=g(t);return Z(),c.jsx(J,{asChild:!0,loop:!0,trapped:n,onMountAutoFocus:o,onUnmountAutoFocus:r,children:c.jsx(Q,{asChild:!0,disableOutsidePointerEvents:s,onInteractOutside:h,onEscapeKeyDown:i,onPointerDownOutside:p,onFocusOutside:u,onDismiss:()=>f.onOpenChange(!1),children:c.jsx(X,{"data-state":k(f.open),role:"dialog",id:f.contentId,...x,...C,ref:a,style:{...C.style,"--radix-popover-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-popover-content-available-width":"var(--radix-popper-available-width)","--radix-popover-content-available-height":"var(--radix-popper-available-height)","--radix-popover-trigger-width":"var(--radix-popper-anchor-width)","--radix-popover-trigger-height":"var(--radix-popper-anchor-height)"}})})})}),I="PopoverClose",ne=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(I,t);return c.jsx(O.button,{type:"button",...n,ref:a,onClick:P(e.onClick,()=>o.onOpenChange(!1))})});ne.displayName=I;var se="PopoverArrow",ce=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=g(t);return c.jsx(Y,{...o,...n,ref:a})});ce.displayName=se;function k(e){return e?"open":"closed"}var ie=j,ve=y,le=S,pe=M,L=T;const Pe=ie,ge=le,ue=l.forwardRef(({className:e,sideOffset:a=8,align:t="start",...n},o)=>c.jsx(pe,{children:c.jsx(L,{ref:o,sideOffset:a,align:t,className:q("z-[var(--z-popover,50)] w-72 overflow-hidden rounded-2xl border border-gray-200/50 bg-white p-4 shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})}));ue.displayName=L.displayName;export{ve as A,L as C,pe as P,ie as R,le as T,Pe as a,ge as b,ue as c};
1
+ import{r as l,aI as $,j as c,aJ as z,aK as G,aL as A,aM as O,aN as P,aO as _,aP as w,aQ as b,aR as H,aS as K,aT as U,aU as V,aV as W,aW as Z,aX as J,aY as Q,aZ as X,a_ as Y}from"./vendor-BEQcLDx6.js";import{c as q}from"./index-LhlkB00c.js";var m="Popover",[E]=K(m,[w]),g=w(),[B,d]=E(m),j=e=>{const{__scopePopover:a,children:t,open:n,defaultOpen:o,onOpenChange:r,modal:s=!1}=e,i=g(a),p=l.useRef(null),[u,h]=l.useState(!1),[C,f]=$({prop:n,defaultProp:o??!1,onChange:r,caller:m});return c.jsx(z,{...i,children:c.jsx(B,{scope:a,contentId:G(),triggerRef:p,open:C,onOpenChange:f,onOpenToggle:l.useCallback(()=>f(x=>!x),[f]),hasCustomAnchor:u,onCustomAnchorAdd:l.useCallback(()=>h(!0),[]),onCustomAnchorRemove:l.useCallback(()=>h(!1),[]),modal:s,children:t})})};j.displayName=m;var N="PopoverAnchor",y=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(N,t),r=g(t),{onCustomAnchorAdd:s,onCustomAnchorRemove:i}=o;return l.useEffect(()=>(s(),()=>i()),[s,i]),c.jsx(_,{...r,...n,ref:a})});y.displayName=N;var F="PopoverTrigger",S=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(F,t),r=g(t),s=A(a,o.triggerRef),i=c.jsx(O.button,{type:"button","aria-haspopup":"dialog","aria-expanded":o.open,"aria-controls":o.contentId,"data-state":k(o.open),...n,ref:s,onClick:P(e.onClick,o.onOpenToggle)});return o.hasCustomAnchor?i:c.jsx(_,{asChild:!0,...r,children:i})});S.displayName=F;var R="PopoverPortal",[ee,oe]=E(R,{forceMount:void 0}),M=e=>{const{__scopePopover:a,forceMount:t,children:n,container:o}=e,r=d(R,a);return c.jsx(ee,{scope:a,forceMount:t,children:c.jsx(b,{present:t||r.open,children:c.jsx(H,{asChild:!0,container:o,children:n})})})};M.displayName=R;var v="PopoverContent",T=l.forwardRef((e,a)=>{const t=oe(v,e.__scopePopover),{forceMount:n=t.forceMount,...o}=e,r=d(v,e.__scopePopover);return c.jsx(b,{present:n||r.open,children:r.modal?c.jsx(re,{...o,ref:a}):c.jsx(ae,{...o,ref:a})})});T.displayName=v;var te=W("PopoverContent.RemoveScroll"),re=l.forwardRef((e,a)=>{const t=d(v,e.__scopePopover),n=l.useRef(null),o=A(a,n),r=l.useRef(!1);return l.useEffect(()=>{const s=n.current;if(s)return U(s)},[]),c.jsx(V,{as:te,allowPinchZoom:!0,children:c.jsx(D,{...e,ref:o,trapFocus:t.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:P(e.onCloseAutoFocus,s=>{var i;s.preventDefault(),r.current||(i=t.triggerRef.current)==null||i.focus()}),onPointerDownOutside:P(e.onPointerDownOutside,s=>{const i=s.detail.originalEvent,p=i.button===0&&i.ctrlKey===!0,u=i.button===2||p;r.current=u},{checkForDefaultPrevented:!1}),onFocusOutside:P(e.onFocusOutside,s=>s.preventDefault(),{checkForDefaultPrevented:!1})})})}),ae=l.forwardRef((e,a)=>{const t=d(v,e.__scopePopover),n=l.useRef(!1),o=l.useRef(!1);return c.jsx(D,{...e,ref:a,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:r=>{var s,i;(s=e.onCloseAutoFocus)==null||s.call(e,r),r.defaultPrevented||(n.current||(i=t.triggerRef.current)==null||i.focus(),r.preventDefault()),n.current=!1,o.current=!1},onInteractOutside:r=>{var p,u;(p=e.onInteractOutside)==null||p.call(e,r),r.defaultPrevented||(n.current=!0,r.detail.originalEvent.type==="pointerdown"&&(o.current=!0));const s=r.target;((u=t.triggerRef.current)==null?void 0:u.contains(s))&&r.preventDefault(),r.detail.originalEvent.type==="focusin"&&o.current&&r.preventDefault()}})}),D=l.forwardRef((e,a)=>{const{__scopePopover:t,trapFocus:n,onOpenAutoFocus:o,onCloseAutoFocus:r,disableOutsidePointerEvents:s,onEscapeKeyDown:i,onPointerDownOutside:p,onFocusOutside:u,onInteractOutside:h,...C}=e,f=d(v,t),x=g(t);return Z(),c.jsx(J,{asChild:!0,loop:!0,trapped:n,onMountAutoFocus:o,onUnmountAutoFocus:r,children:c.jsx(Q,{asChild:!0,disableOutsidePointerEvents:s,onInteractOutside:h,onEscapeKeyDown:i,onPointerDownOutside:p,onFocusOutside:u,onDismiss:()=>f.onOpenChange(!1),children:c.jsx(X,{"data-state":k(f.open),role:"dialog",id:f.contentId,...x,...C,ref:a,style:{...C.style,"--radix-popover-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-popover-content-available-width":"var(--radix-popper-available-width)","--radix-popover-content-available-height":"var(--radix-popper-available-height)","--radix-popover-trigger-width":"var(--radix-popper-anchor-width)","--radix-popover-trigger-height":"var(--radix-popper-anchor-height)"}})})})}),I="PopoverClose",ne=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(I,t);return c.jsx(O.button,{type:"button",...n,ref:a,onClick:P(e.onClick,()=>o.onOpenChange(!1))})});ne.displayName=I;var se="PopoverArrow",ce=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=g(t);return c.jsx(Y,{...o,...n,ref:a})});ce.displayName=se;function k(e){return e?"open":"closed"}var ie=j,ve=y,le=S,pe=M,L=T;const Pe=ie,ge=le,ue=l.forwardRef(({className:e,sideOffset:a=8,align:t="start",...n},o)=>c.jsx(pe,{children:c.jsx(L,{ref:o,sideOffset:a,align:t,className:q("z-[var(--z-popover,50)] w-72 overflow-hidden rounded-2xl border border-gray-200/50 bg-white p-4 shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})}));ue.displayName=L.displayName;export{ve as A,L as C,pe as P,ie as R,le as T,Pe as a,ge as b,ue as c};
@@ -0,0 +1 @@
1
+ import{r as c,j as e,e as M}from"./vendor-BEQcLDx6.js";import{a2 as O,a3 as R,a4 as I,a5 as B,a6 as _,C as u,$ as h,a0 as x,t as s,a1 as m,E as p,B as j,I as l}from"./index-LhlkB00c.js";import{L as o}from"./label-3TKt0PoZ.js";import{S as G}from"./switch-A3-ClT1P.js";import{P as V,a as W}from"./page-layout-CopkIM3Q.js";const $=8;function A(r){return r.trim().length>=$}function L(r,n){return r!==n?(M.error(s("authPasswordMismatch")),!1):!0}function q(){const r=O(),n=R(),S=I(),g=B(),y=_(),[f,U]=c.useState(""),[i,N]=c.useState(""),[w,b]=c.useState(""),[d,v]=c.useState(""),[P,C]=c.useState(""),t=r.data,E=f.trim().length>0&&A(i)&&i===w&&!n.isPending,D=A(d)&&d===P&&!g.isPending,T=async()=>{if(L(i,w))try{await n.mutateAsync({username:f.trim(),password:i}),N(""),b("")}catch{}},F=async()=>{if(L(d,P))try{await g.mutateAsync({password:d}),v(""),C("")}catch{}},H=async a=>{try{await S.mutateAsync({enabled:a})}catch{}},k=async()=>{try{await y.mutateAsync()}catch{}};return r.isLoading&&!t?e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsx(p,{className:"text-sm text-gray-500",children:s("loading")})]}):r.isError||!t?e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsxs(p,{className:"space-y-4",children:[e.jsx("p",{className:"text-sm text-gray-500",children:s("authStatusLoadFailed")}),e.jsx(j,{variant:"outline",onClick:()=>{r.refetch()},children:s("authRetryStatus")})]})]}):t.configured?e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsxs(p,{className:"space-y-6",children:[e.jsxs("div",{className:"rounded-xl border border-gray-200 p-4",children:[e.jsxs("div",{className:"flex flex-col gap-4 md:flex-row md:items-start md:justify-between",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authStatusLabel")}),e.jsx("p",{className:"text-sm text-gray-600",children:s("authStatusConfiguredUser").replace("{username}",t.username??"")}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authUsernameFixedHelp")})]}),e.jsx("span",{className:"inline-flex items-center rounded-full bg-gray-100 px-3 py-1 text-xs font-medium text-gray-700",children:t.enabled?s("enabled"):s("disabled")})]}),e.jsxs("div",{className:"mt-4 flex flex-col gap-4 border-t border-gray-200 pt-4 md:flex-row md:items-center md:justify-between",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authEnableLabel")}),e.jsx("p",{className:"text-xs text-gray-500",children:t.enabled?s("authEnableOnHelp"):s("authEnableOffHelp")})]}),e.jsx(G,{checked:t.enabled,disabled:S.isPending,onCheckedChange:a=>{H(a)}})]})]}),e.jsxs("div",{className:"rounded-xl border border-gray-200 p-4 space-y-4",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authPasswordSectionTitle")}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authPasswordSectionDescription")})]}),e.jsxs("div",{className:"grid gap-4 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-password-next",children:s("authPassword")}),e.jsx(l,{id:"auth-password-next",type:"password",value:d,onChange:a=>v(a.target.value),placeholder:s("authPasswordPlaceholder")})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-password-confirm",children:s("authConfirmPassword")}),e.jsx(l,{id:"auth-password-confirm",type:"password",value:P,onChange:a=>C(a.target.value),placeholder:s("authConfirmPasswordPlaceholder")})]})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx(j,{type:"button",disabled:!D,onClick:()=>void F(),children:g.isPending?s("authPasswordUpdating"):s("authPasswordAction")}),t.enabled&&t.authenticated?e.jsx(j,{type:"button",variant:"outline",disabled:y.isPending,onClick:()=>void k(),children:y.isPending?s("authLoggingOut"):s("authLogoutAction")}):null]}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authSessionMemoryNotice")})]})]})]}):e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsxs(p,{className:"space-y-5",children:[e.jsxs("div",{className:"rounded-xl border border-dashed border-gray-200 bg-gray-50/70 p-4",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authSetupTitle")}),e.jsx("p",{className:"mt-1 text-sm text-gray-500",children:s("authSetupDescription")})]}),e.jsxs("div",{className:"grid gap-4 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-setup-username",children:s("authUsername")}),e.jsx(l,{id:"auth-setup-username",value:f,onChange:a=>U(a.target.value),placeholder:s("authUsernamePlaceholder")})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-setup-password",children:s("authPassword")}),e.jsx(l,{id:"auth-setup-password",type:"password",value:i,onChange:a=>N(a.target.value),placeholder:s("authPasswordPlaceholder")})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-setup-confirm",children:s("authConfirmPassword")}),e.jsx(l,{id:"auth-setup-confirm",type:"password",value:w,onChange:a=>b(a.target.value),placeholder:s("authConfirmPasswordPlaceholder")}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authPasswordMinLengthHint")})]}),e.jsx(j,{type:"button",disabled:!E,onClick:()=>void T(),children:n.isPending?s("authSettingUp"):s("authSetupAction")})]})]})}function Y(){return e.jsxs(V,{className:"space-y-6",children:[e.jsx(W,{title:s("authSecurityTitle"),description:s("authSecurityDescription")}),e.jsx(q,{})]})}export{Y as SecurityConfig};
@@ -1 +1 @@
1
- import{j as t}from"./vendor-BEQcLDx6.js";import{c as o}from"./index-D-wEIgPn.js";function m({className:e,...s}){return t.jsx("div",{className:o("animate-pulse rounded-md bg-slate-200",e),...s})}export{m as S};
1
+ import{j as t}from"./vendor-BEQcLDx6.js";import{c as o}from"./index-LhlkB00c.js";function m({className:e,...s}){return t.jsx("div",{className:o("animate-pulse rounded-md bg-slate-200",e),...s})}export{m as S};
@@ -1 +1 @@
1
- import{j as e}from"./vendor-BEQcLDx6.js";import{c as a}from"./index-D-wEIgPn.js";const n={active:{dot:"bg-emerald-500",text:"text-emerald-600",bg:"bg-emerald-50"},ready:{dot:"bg-emerald-500",text:"text-emerald-600",bg:"bg-emerald-50"},inactive:{dot:"bg-gray-300",text:"text-gray-400",bg:"bg-gray-100/80"},setup:{dot:"bg-gray-300",text:"text-gray-400",bg:"bg-gray-100/80"},warning:{dot:"bg-amber-400",text:"text-amber-600",bg:"bg-amber-50"}};function m({status:r,label:s,className:g}){const t=n[r];return e.jsxs("div",{className:a("inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-2 py-0.5",t.bg,g),children:[e.jsx("span",{className:a("h-1.5 w-1.5 rounded-full",t.dot)}),e.jsx("span",{className:a("text-[11px] font-medium",t.text),children:s})]})}export{m as S};
1
+ import{j as e}from"./vendor-BEQcLDx6.js";import{c as a}from"./index-LhlkB00c.js";const n={active:{dot:"bg-emerald-500",text:"text-emerald-600",bg:"bg-emerald-50"},ready:{dot:"bg-emerald-500",text:"text-emerald-600",bg:"bg-emerald-50"},inactive:{dot:"bg-gray-300",text:"text-gray-400",bg:"bg-gray-100/80"},setup:{dot:"bg-gray-300",text:"text-gray-400",bg:"bg-gray-100/80"},warning:{dot:"bg-amber-400",text:"text-amber-600",bg:"bg-amber-50"}};function m({status:r,label:s,className:g}){const t=n[r];return e.jsxs("div",{className:a("inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-2 py-0.5",t.bg,g),children:[e.jsx("span",{className:a("h-1.5 w-1.5 rounded-full",t.dot)}),e.jsx("span",{className:a("text-[11px] font-medium",t.text),children:s})]})}export{m as S};
@@ -1 +1 @@
1
- import{r as e,j as i}from"./vendor-BEQcLDx6.js";import{c as t}from"./index-D-wEIgPn.js";const l=e.forwardRef(({className:o,checked:r=!1,onCheckedChange:s,...a},n)=>i.jsx("button",{type:"button",role:"switch","aria-checked":r,ref:n,className:t("peer inline-flex h-[22px] w-10 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50",r?"bg-primary":"bg-gray-200/80 hover:bg-gray-300/80",o),onClick:()=>s==null?void 0:s(!r),...a,children:i.jsx("span",{className:t("pointer-events-none block h-5 w-5 rounded-full bg-white shadow-md ring-0 transition-transform duration-fast",r?"translate-x-5":"translate-x-0")})}));l.displayName="Switch";export{l as S};
1
+ import{r as e,j as i}from"./vendor-BEQcLDx6.js";import{c as t}from"./index-LhlkB00c.js";const l=e.forwardRef(({className:o,checked:r=!1,onCheckedChange:s,...a},n)=>i.jsx("button",{type:"button",role:"switch","aria-checked":r,ref:n,className:t("peer inline-flex h-[22px] w-10 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50",r?"bg-primary":"bg-gray-200/80 hover:bg-gray-300/80",o),onClick:()=>s==null?void 0:s(!r),...a,children:i.jsx("span",{className:t("pointer-events-none block h-5 w-5 rounded-full bg-white shadow-md ring-0 transition-transform duration-fast",r?"translate-x-5":"translate-x-0")})}));l.displayName="Switch";export{l as S};
@@ -1 +1 @@
1
- import{j as e}from"./vendor-BEQcLDx6.js";import{aj as m,c as a}from"./index-D-wEIgPn.js";function c({tabs:s,activeTab:i,onChange:o,className:n}){return e.jsx("div",{className:a("flex items-center gap-6 border-b border-gray-200/60 mb-6",n),children:s.map(t=>{const r=i===t.id;return e.jsxs("button",{onClick:()=>o(t.id),className:a("relative pb-3 text-[14px] font-medium transition-all duration-fast flex items-center gap-1.5",r?"text-gray-900":"text-gray-600 hover:text-gray-900"),children:[t.label,t.count!==void 0&&e.jsx("span",{className:a("text-[11px] font-medium","text-gray-500"),children:m(t.count)}),r&&e.jsx("div",{className:"absolute bottom-0 left-0 right-0 h-[2px] bg-primary rounded-full"})]},t.id)})})}export{c as T};
1
+ import{j as e}from"./vendor-BEQcLDx6.js";import{am as m,c as a}from"./index-LhlkB00c.js";function c({tabs:s,activeTab:i,onChange:o,className:n}){return e.jsx("div",{className:a("flex items-center gap-6 border-b border-gray-200/60 mb-6",n),children:s.map(t=>{const r=i===t.id;return e.jsxs("button",{onClick:()=>o(t.id),className:a("relative pb-3 text-[14px] font-medium transition-all duration-fast flex items-center gap-1.5",r?"text-gray-900":"text-gray-600 hover:text-gray-900"),children:[t.label,t.count!==void 0&&e.jsx("span",{className:a("text-[11px] font-medium","text-gray-500"),children:m(t.count)}),r&&e.jsx("div",{className:"absolute bottom-0 left-0 right-0 h-[2px] bg-primary rounded-full"})]},t.id)})})}export{c as T};
@@ -1 +1 @@
1
- import{j as a,r as t}from"./vendor-BEQcLDx6.js";import{ad as C,ae as h,af as p,ag as g,ah as x,ai as D,B as d,t as r}from"./index-D-wEIgPn.js";const j=({open:l,onOpenChange:i,title:c,description:o,confirmLabel:s=r("confirm"),cancelLabel:e=r("cancel"),variant:n="default",onConfirm:f,onCancel:u})=>{const m=()=>{f(),i(!1)},v=()=>{u(),i(!1)};return a.jsx(C,{open:l,onOpenChange:i,children:a.jsxs(h,{className:"[&>:last-child]:hidden",onCloseAutoFocus:b=>b.preventDefault(),children:[a.jsxs(p,{children:[a.jsx(g,{children:c}),o?a.jsx(x,{children:o}):null]}),a.jsxs(D,{className:"gap-2 sm:gap-0",children:[a.jsx(d,{type:"button",variant:"outline",onClick:v,children:e}),a.jsx(d,{type:"button",variant:n==="destructive"?"destructive":"default",onClick:m,children:s})]})]})})},L={open:!1,title:"",description:"",confirmLabel:r("confirm"),cancelLabel:r("cancel"),variant:"default",resolve:null};function y(){const[l,i]=t.useState(L),c=t.useCallback(e=>new Promise(n=>{i({open:!0,title:e.title,description:e.description??"",confirmLabel:e.confirmLabel??r("confirm"),cancelLabel:e.cancelLabel??r("cancel"),variant:e.variant??"default",resolve:f=>{n(f),i(u=>({...u,open:!1,resolve:null}))}})}),[]),o=t.useCallback(e=>{i(n=>(!e&&n.resolve&&n.resolve(!1),{...n,open:e,resolve:e?n.resolve:null}))},[]),s=t.useCallback(()=>a.jsx(j,{open:l.open,onOpenChange:o,title:l.title,description:l.description||void 0,confirmLabel:l.confirmLabel,cancelLabel:l.cancelLabel,variant:l.variant,onConfirm:()=>{var e;return(e=l.resolve)==null?void 0:e.call(l,!0)},onCancel:()=>{var e;return(e=l.resolve)==null?void 0:e.call(l,!1)}}),[l,o]);return{confirm:c,ConfirmDialog:s}}export{y as u};
1
+ import{j as a,r as t}from"./vendor-BEQcLDx6.js";import{ag as C,ah as h,ai as p,aj as g,ak as x,al as j,B as d,t as r}from"./index-LhlkB00c.js";const D=({open:l,onOpenChange:i,title:c,description:o,confirmLabel:s=r("confirm"),cancelLabel:e=r("cancel"),variant:n="default",onConfirm:u,onCancel:f})=>{const m=()=>{u(),i(!1)},v=()=>{f(),i(!1)};return a.jsx(C,{open:l,onOpenChange:i,children:a.jsxs(h,{className:"[&>:last-child]:hidden",onCloseAutoFocus:b=>b.preventDefault(),children:[a.jsxs(p,{children:[a.jsx(g,{children:c}),o?a.jsx(x,{children:o}):null]}),a.jsxs(j,{className:"gap-2 sm:gap-0",children:[a.jsx(d,{type:"button",variant:"outline",onClick:v,children:e}),a.jsx(d,{type:"button",variant:n==="destructive"?"destructive":"default",onClick:m,children:s})]})]})})},L={open:!1,title:"",description:"",confirmLabel:r("confirm"),cancelLabel:r("cancel"),variant:"default",resolve:null};function y(){const[l,i]=t.useState(L),c=t.useCallback(e=>new Promise(n=>{i({open:!0,title:e.title,description:e.description??"",confirmLabel:e.confirmLabel??r("confirm"),cancelLabel:e.cancelLabel??r("cancel"),variant:e.variant??"default",resolve:u=>{n(u),i(f=>({...f,open:!1,resolve:null}))}})}),[]),o=t.useCallback(e=>{i(n=>(!e&&n.resolve&&n.resolve(!1),{...n,open:e,resolve:e?n.resolve:null}))},[]),s=t.useCallback(()=>a.jsx(D,{open:l.open,onOpenChange:o,title:l.title,description:l.description||void 0,confirmLabel:l.confirmLabel,cancelLabel:l.cancelLabel,variant:l.variant,onConfirm:()=>{var e;return(e=l.resolve)==null?void 0:e.call(l,!0)},onCancel:()=>{var e;return(e=l.resolve)==null?void 0:e.call(l,!1)}}),[l,o]);return{confirm:c,ConfirmDialog:s}}export{y as u};
package/dist/index.html CHANGED
@@ -6,7 +6,7 @@
6
6
  <link rel="icon" type="image/svg+xml" href="/logo.svg" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
8
  <title>NextClaw</title>
9
- <script type="module" crossorigin src="/assets/index-D-wEIgPn.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-LhlkB00c.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-BEQcLDx6.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-Bro-iRcb.css">
12
12
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.11.9",
3
+ "version": "0.11.10",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,11 +28,11 @@
28
28
  "tailwind-merge": "^2.5.4",
29
29
  "zod": "^3.23.8",
30
30
  "zustand": "^5.0.2",
31
- "@nextclaw/agent-chat-ui": "0.2.12",
32
- "@nextclaw/ncp-react": "0.4.2",
33
- "@nextclaw/ncp": "0.4.0",
31
+ "@nextclaw/ncp-react": "0.4.3",
32
+ "@nextclaw/ncp-http-agent-client": "0.3.4",
33
+ "@nextclaw/agent-chat-ui": "0.2.13",
34
34
  "@nextclaw/agent-chat": "0.1.3",
35
- "@nextclaw/ncp-http-agent-client": "0.3.4"
35
+ "@nextclaw/ncp": "0.4.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@testing-library/react": "^16.3.0",
@@ -0,0 +1,89 @@
1
+ import { QueryClient } from '@tanstack/react-query';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {
4
+ applyNcpSessionRealtimeEvent,
5
+ deleteNcpSessionSummaryList,
6
+ upsertNcpSessionSummaryList
7
+ } from '@/api/ncp-session-query-cache';
8
+ import type { NcpSessionsListView } from '@/api/types';
9
+
10
+ function createSessionsList(): NcpSessionsListView {
11
+ return {
12
+ sessions: [
13
+ {
14
+ sessionId: 'session-1',
15
+ messageCount: 1,
16
+ updatedAt: '2026-03-29T10:00:00.000Z',
17
+ status: 'idle',
18
+ metadata: {}
19
+ },
20
+ {
21
+ sessionId: 'session-2',
22
+ messageCount: 2,
23
+ updatedAt: '2026-03-29T09:00:00.000Z',
24
+ status: 'idle',
25
+ metadata: {}
26
+ }
27
+ ],
28
+ total: 2
29
+ };
30
+ }
31
+
32
+ describe('ncp-session-query-cache', () => {
33
+ it('upserts summaries and keeps the list sorted by updatedAt descending', () => {
34
+ const updated = upsertNcpSessionSummaryList(createSessionsList(), {
35
+ sessionId: 'session-2',
36
+ messageCount: 3,
37
+ updatedAt: '2026-03-29T11:00:00.000Z',
38
+ status: 'running',
39
+ metadata: { label: 'Latest' }
40
+ });
41
+
42
+ expect(updated?.sessions.map((session) => session.sessionId)).toEqual(['session-2', 'session-1']);
43
+ expect(updated?.sessions[0]).toMatchObject({
44
+ sessionId: 'session-2',
45
+ messageCount: 3,
46
+ status: 'running'
47
+ });
48
+ });
49
+
50
+ it('deletes summaries without mutating unrelated entries', () => {
51
+ const updated = deleteNcpSessionSummaryList(createSessionsList(), 'session-1');
52
+
53
+ expect(updated?.sessions.map((session) => session.sessionId)).toEqual(['session-2']);
54
+ expect(updated?.total).toBe(1);
55
+ });
56
+
57
+ it('applies realtime upsert/delete events to every ncp-sessions query cache entry', () => {
58
+ const queryClient = new QueryClient();
59
+ queryClient.setQueryData(['ncp-sessions', 200], createSessionsList());
60
+
61
+ applyNcpSessionRealtimeEvent(queryClient, {
62
+ type: 'session.summary.upsert',
63
+ payload: {
64
+ summary: {
65
+ sessionId: 'session-3',
66
+ messageCount: 1,
67
+ updatedAt: '2026-03-29T12:00:00.000Z',
68
+ status: 'running',
69
+ metadata: {}
70
+ }
71
+ }
72
+ });
73
+
74
+ expect(
75
+ queryClient.getQueryData<NcpSessionsListView>(['ncp-sessions', 200])?.sessions.map((session) => session.sessionId)
76
+ ).toEqual(['session-3', 'session-1', 'session-2']);
77
+
78
+ applyNcpSessionRealtimeEvent(queryClient, {
79
+ type: 'session.summary.delete',
80
+ payload: {
81
+ sessionKey: 'session-1'
82
+ }
83
+ });
84
+
85
+ expect(
86
+ queryClient.getQueryData<NcpSessionsListView>(['ncp-sessions', 200])?.sessions.map((session) => session.sessionId)
87
+ ).toEqual(['session-3', 'session-2']);
88
+ });
89
+ });
@@ -0,0 +1,85 @@
1
+ import type { QueryClient } from '@tanstack/react-query';
2
+ import type { NcpSessionSummaryView, NcpSessionsListView, WsEvent } from '@/api/types';
3
+
4
+ function sortSessionSummaries(summaries: readonly NcpSessionSummaryView[]): NcpSessionSummaryView[] {
5
+ return [...summaries].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
6
+ }
7
+
8
+ export function upsertNcpSessionSummaryList(
9
+ current: NcpSessionsListView | undefined,
10
+ summary: NcpSessionSummaryView
11
+ ): NcpSessionsListView | undefined {
12
+ if (!current) {
13
+ return current;
14
+ }
15
+
16
+ const existingIndex = current.sessions.findIndex((session) => session.sessionId === summary.sessionId);
17
+ const nextSessions =
18
+ existingIndex >= 0
19
+ ? current.sessions.map((session, index) => (index === existingIndex ? summary : session))
20
+ : [...current.sessions, summary];
21
+ const sortedSessions = sortSessionSummaries(nextSessions);
22
+
23
+ return {
24
+ ...current,
25
+ sessions: sortedSessions,
26
+ total: sortedSessions.length
27
+ };
28
+ }
29
+
30
+ export function deleteNcpSessionSummaryList(
31
+ current: NcpSessionsListView | undefined,
32
+ sessionKey: string
33
+ ): NcpSessionsListView | undefined {
34
+ if (!current) {
35
+ return current;
36
+ }
37
+
38
+ const normalizedSessionKey = sessionKey.trim();
39
+ if (!normalizedSessionKey) {
40
+ return current;
41
+ }
42
+
43
+ const nextSessions = current.sessions.filter((session) => session.sessionId !== normalizedSessionKey);
44
+ if (nextSessions.length === current.sessions.length) {
45
+ return current;
46
+ }
47
+
48
+ return {
49
+ ...current,
50
+ sessions: nextSessions,
51
+ total: nextSessions.length
52
+ };
53
+ }
54
+
55
+ export function upsertNcpSessionSummaryInQueryClient(
56
+ queryClient: QueryClient | undefined,
57
+ summary: NcpSessionSummaryView
58
+ ): void {
59
+ queryClient?.setQueriesData<NcpSessionsListView>(
60
+ { queryKey: ['ncp-sessions'] },
61
+ (current) => upsertNcpSessionSummaryList(current, summary)
62
+ );
63
+ }
64
+
65
+ export function deleteNcpSessionSummaryInQueryClient(
66
+ queryClient: QueryClient | undefined,
67
+ sessionKey: string
68
+ ): void {
69
+ queryClient?.setQueriesData<NcpSessionsListView>(
70
+ { queryKey: ['ncp-sessions'] },
71
+ (current) => deleteNcpSessionSummaryList(current, sessionKey)
72
+ );
73
+ }
74
+
75
+ export function applyNcpSessionRealtimeEvent(
76
+ queryClient: QueryClient | undefined,
77
+ event: Extract<WsEvent, { type: 'session.summary.upsert' | 'session.summary.delete' }>
78
+ ): void {
79
+ if (event.type === 'session.summary.upsert') {
80
+ upsertNcpSessionSummaryInQueryClient(queryClient, event.payload.summary);
81
+ return;
82
+ }
83
+
84
+ deleteNcpSessionSummaryInQueryClient(queryClient, event.payload.sessionKey);
85
+ }
package/src/api/types.ts CHANGED
@@ -569,6 +569,8 @@ export type ConfigActionExecuteResult = {
569
569
  export type WsEvent =
570
570
  | { type: 'config.updated'; payload: { path: string } }
571
571
  | { type: 'session.updated'; payload: { sessionKey: string } }
572
+ | { type: 'session.summary.upsert'; payload: { summary: NcpSessionSummaryView } }
573
+ | { type: 'session.summary.delete'; payload: { sessionKey: string } }
572
574
  | { type: 'config.reload.started'; payload?: Record<string, unknown> }
573
575
  | { type: 'config.reload.finished'; payload?: Record<string, unknown> }
574
576
  | { type: 'error'; payload: { message: string; code?: string } }
@@ -48,7 +48,7 @@ describe('ChatConversationPanel', () => {
48
48
  canDeleteSession: false,
49
49
  isDeletePending: false,
50
50
  isHistoryLoading: false,
51
- uiMessages: [],
51
+ messages: [],
52
52
  isSending: false,
53
53
  isAwaitingAssistantOutput: false
54
54
  }
@@ -55,14 +55,14 @@ export function ChatConversationPanel() {
55
55
 
56
56
  const showWelcome =
57
57
  !snapshot.selectedSessionKey &&
58
- snapshot.uiMessages.length === 0 &&
58
+ snapshot.messages.length === 0 &&
59
59
  !snapshot.isSending;
60
60
  const hasConfiguredModel = snapshot.modelOptions.length > 0;
61
61
  const shouldShowProviderHint =
62
62
  snapshot.isProviderStateResolved && !hasConfiguredModel;
63
63
  const hideEmptyHint =
64
64
  snapshot.isHistoryLoading &&
65
- snapshot.uiMessages.length === 0 &&
65
+ snapshot.messages.length === 0 &&
66
66
  !snapshot.isSending &&
67
67
  !snapshot.isAwaitingAssistantOutput;
68
68
 
@@ -70,8 +70,8 @@ export function ChatConversationPanel() {
70
70
  scrollRef: threadRef,
71
71
  resetKey: snapshot.selectedSessionKey,
72
72
  isLoading: snapshot.isHistoryLoading,
73
- hasContent: snapshot.uiMessages.length > 0,
74
- contentVersion: snapshot.uiMessages[snapshot.uiMessages.length - 1] ?? null,
73
+ hasContent: snapshot.messages.length > 0,
74
+ contentVersion: snapshot.messages[snapshot.messages.length - 1] ?? null,
75
75
  });
76
76
 
77
77
  if (!snapshot.isProviderStateResolved) {
@@ -146,14 +146,14 @@ export function ChatConversationPanel() {
146
146
  />
147
147
  ) : hideEmptyHint ? (
148
148
  <div className="h-full" />
149
- ) : snapshot.uiMessages.length === 0 ? (
149
+ ) : snapshot.messages.length === 0 ? (
150
150
  <div className="px-5 py-5 text-sm text-gray-500">
151
151
  {t("chatNoMessages")}
152
152
  </div>
153
153
  ) : (
154
154
  <div className="mx-auto w-full max-w-[min(1120px,100%)] px-6 py-5">
155
155
  <ChatMessageListContainer
156
- uiMessages={snapshot.uiMessages}
156
+ messages={snapshot.messages}
157
157
  isSending={
158
158
  snapshot.isSending && snapshot.isAwaitingAssistantOutput
159
159
  }
@@ -2,8 +2,8 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
2
2
  import { MemoryRouter } from 'react-router-dom';
3
3
  import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import { ChatSidebar } from '@/components/chat/ChatSidebar';
5
+ import type { NcpSessionListItemView } from '@/components/chat/ncp/use-ncp-session-list-view';
5
6
  import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
6
- import { useChatRunStatusStore } from '@/components/chat/stores/chat-run-status.store';
7
7
  import { useChatSessionListStore } from '@/components/chat/stores/chat-session-list.store';
8
8
 
9
9
  const mocks = vi.hoisted(() => ({
@@ -11,9 +11,15 @@ const mocks = vi.hoisted(() => ({
11
11
  setQuery: vi.fn(),
12
12
  selectSession: vi.fn(),
13
13
  docOpen: vi.fn(),
14
- updateNcpSession: vi.fn()
14
+ updateNcpSession: vi.fn(),
15
+ sessionItems: [] as NcpSessionListItemView[],
16
+ isLoading: false
15
17
  }));
16
18
 
19
+ function createSessionItem(session: NcpSessionListItemView['session']): NcpSessionListItemView {
20
+ return { session };
21
+ }
22
+
17
23
  vi.mock('@/components/chat/presenter/chat-presenter-context', () => ({
18
24
  usePresenter: () => ({
19
25
  chatSessionListManager: {
@@ -34,7 +40,27 @@ vi.mock('@/components/chat/chat-session-label.service', () => ({
34
40
  useChatSessionLabelService: () => async (params: {
35
41
  sessionKey: string;
36
42
  label: string | null;
37
- }) => mocks.updateNcpSession(params.sessionKey, { label: params.label })
43
+ }) => {
44
+ mocks.sessionItems = mocks.sessionItems.map((item) =>
45
+ item.session.key === params.sessionKey
46
+ ? {
47
+ ...item,
48
+ session: {
49
+ ...item.session,
50
+ ...(params.label ? { label: params.label } : { label: undefined })
51
+ }
52
+ }
53
+ : item
54
+ );
55
+ return mocks.updateNcpSession(params.sessionKey, { label: params.label });
56
+ }
57
+ }));
58
+
59
+ vi.mock('@/components/chat/ncp/use-ncp-session-list-view', () => ({
60
+ useNcpSessionListView: () => ({
61
+ isLoading: mocks.isLoading,
62
+ items: mocks.sessionItems
63
+ })
38
64
  }));
39
65
 
40
66
  vi.mock('@/components/common/BrandHeader', () => ({
@@ -72,6 +98,8 @@ describe('ChatSidebar', () => {
72
98
  mocks.docOpen.mockReset();
73
99
  mocks.updateNcpSession.mockReset();
74
100
  mocks.updateNcpSession.mockResolvedValue({});
101
+ mocks.sessionItems = [];
102
+ mocks.isLoading = false;
75
103
 
76
104
  useChatInputStore.setState({
77
105
  snapshot: {
@@ -86,15 +114,7 @@ describe('ChatSidebar', () => {
86
114
  useChatSessionListStore.setState({
87
115
  snapshot: {
88
116
  ...useChatSessionListStore.getState().snapshot,
89
- sessions: [],
90
- query: '',
91
- isLoading: false
92
- }
93
- });
94
- useChatRunStatusStore.setState({
95
- snapshot: {
96
- ...useChatRunStatusStore.getState().snapshot,
97
- sessionRunStatusByKey: new Map()
117
+ query: ''
98
118
  }
99
119
  });
100
120
  });
@@ -145,22 +165,17 @@ describe('ChatSidebar', () => {
145
165
  });
146
166
 
147
167
  it('shows a session type badge for non-native sessions in the list', () => {
148
- useChatSessionListStore.setState({
149
- snapshot: {
150
- ...useChatSessionListStore.getState().snapshot,
151
- sessions: [
152
- {
153
- key: 'session:codex-1',
154
- createdAt: '2026-03-19T09:00:00.000Z',
155
- updatedAt: '2026-03-19T09:05:00.000Z',
156
- label: 'Codex Task',
157
- sessionType: 'codex',
158
- sessionTypeMutable: false,
159
- messageCount: 2
160
- }
161
- ]
162
- }
163
- });
168
+ mocks.sessionItems = [
169
+ createSessionItem({
170
+ key: 'session:codex-1',
171
+ createdAt: '2026-03-19T09:00:00.000Z',
172
+ updatedAt: '2026-03-19T09:05:00.000Z',
173
+ label: 'Codex Task',
174
+ sessionType: 'codex',
175
+ sessionTypeMutable: false,
176
+ messageCount: 2
177
+ })
178
+ ];
164
179
 
165
180
  render(
166
181
  <MemoryRouter>
@@ -180,22 +195,17 @@ describe('ChatSidebar', () => {
180
195
  sessionTypeOptions: [{ value: 'native', label: 'Native' }]
181
196
  }
182
197
  });
183
- useChatSessionListStore.setState({
184
- snapshot: {
185
- ...useChatSessionListStore.getState().snapshot,
186
- sessions: [
187
- {
188
- key: 'session:workspace-agent-1',
189
- createdAt: '2026-03-19T09:00:00.000Z',
190
- updatedAt: '2026-03-19T09:05:00.000Z',
191
- label: 'Workspace Task',
192
- sessionType: 'workspace-agent',
193
- sessionTypeMutable: false,
194
- messageCount: 2
195
- }
196
- ]
197
- }
198
- });
198
+ mocks.sessionItems = [
199
+ createSessionItem({
200
+ key: 'session:workspace-agent-1',
201
+ createdAt: '2026-03-19T09:00:00.000Z',
202
+ updatedAt: '2026-03-19T09:05:00.000Z',
203
+ label: 'Workspace Task',
204
+ sessionType: 'workspace-agent',
205
+ sessionTypeMutable: false,
206
+ messageCount: 2
207
+ })
208
+ ];
199
209
 
200
210
  render(
201
211
  <MemoryRouter>
@@ -214,22 +224,17 @@ describe('ChatSidebar', () => {
214
224
  sessionTypeOptions: [{ value: 'native', label: 'Native' }]
215
225
  }
216
226
  });
217
- useChatSessionListStore.setState({
218
- snapshot: {
219
- ...useChatSessionListStore.getState().snapshot,
220
- sessions: [
221
- {
222
- key: 'session:native-1',
223
- createdAt: '2026-03-19T09:00:00.000Z',
224
- updatedAt: '2026-03-19T09:05:00.000Z',
225
- label: 'Native Task',
226
- sessionType: 'native',
227
- sessionTypeMutable: false,
228
- messageCount: 1
229
- }
230
- ]
231
- }
232
- });
227
+ mocks.sessionItems = [
228
+ createSessionItem({
229
+ key: 'session:native-1',
230
+ createdAt: '2026-03-19T09:00:00.000Z',
231
+ updatedAt: '2026-03-19T09:05:00.000Z',
232
+ label: 'Native Task',
233
+ sessionType: 'native',
234
+ sessionTypeMutable: false,
235
+ messageCount: 1
236
+ })
237
+ ];
233
238
 
234
239
  render(
235
240
  <MemoryRouter>
@@ -242,22 +247,17 @@ describe('ChatSidebar', () => {
242
247
  });
243
248
 
244
249
  it('edits the session label inline and saves through the ncp session api by default', async () => {
245
- useChatSessionListStore.setState({
246
- snapshot: {
247
- ...useChatSessionListStore.getState().snapshot,
248
- sessions: [
249
- {
250
- key: 'session:ncp-1',
251
- createdAt: '2026-03-19T09:00:00.000Z',
252
- updatedAt: '2026-03-19T09:05:00.000Z',
253
- label: 'Initial Label',
254
- sessionType: 'native',
255
- sessionTypeMutable: false,
256
- messageCount: 1
257
- }
258
- ]
259
- }
260
- });
250
+ mocks.sessionItems = [
251
+ createSessionItem({
252
+ key: 'session:ncp-1',
253
+ createdAt: '2026-03-19T09:00:00.000Z',
254
+ updatedAt: '2026-03-19T09:05:00.000Z',
255
+ label: 'Initial Label',
256
+ sessionType: 'native',
257
+ sessionTypeMutable: false,
258
+ messageCount: 1
259
+ })
260
+ ];
261
261
 
262
262
  render(
263
263
  <MemoryRouter>
@@ -280,22 +280,17 @@ describe('ChatSidebar', () => {
280
280
  });
281
281
 
282
282
  it('cancels inline session label editing without saving', () => {
283
- useChatSessionListStore.setState({
284
- snapshot: {
285
- ...useChatSessionListStore.getState().snapshot,
286
- sessions: [
287
- {
288
- key: 'session:ncp-2',
289
- createdAt: '2026-03-19T09:00:00.000Z',
290
- updatedAt: '2026-03-19T09:05:00.000Z',
291
- label: 'Cancelable Label',
292
- sessionType: 'native',
293
- sessionTypeMutable: false,
294
- messageCount: 1
295
- }
296
- ]
297
- }
298
- });
283
+ mocks.sessionItems = [
284
+ createSessionItem({
285
+ key: 'session:ncp-2',
286
+ createdAt: '2026-03-19T09:00:00.000Z',
287
+ updatedAt: '2026-03-19T09:05:00.000Z',
288
+ label: 'Cancelable Label',
289
+ sessionType: 'native',
290
+ sessionTypeMutable: false,
291
+ messageCount: 1
292
+ })
293
+ ];
299
294
 
300
295
  render(
301
296
  <MemoryRouter>