@nextclaw/ui 0.11.14 → 0.11.16

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 (39) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/assets/{ChannelsList-CvK4qHfg.js → ChannelsList-UKA-5t02.js} +1 -1
  3. package/dist/assets/ChatPage-xdBp-ddG.js +37 -0
  4. package/dist/assets/{DocBrowser-BFmW6e-4.js → DocBrowser-DBUIWJer.js} +1 -1
  5. package/dist/assets/{LogoBadge-DZL-zQTr.js → LogoBadge-CsSBHZeV.js} +1 -1
  6. package/dist/assets/{MarketplacePage-B__MZRrD.js → MarketplacePage-BSH836_G.js} +1 -1
  7. package/dist/assets/{McpMarketplacePage-C_VKm1uq.js → McpMarketplacePage-B9_kPnnM.js} +1 -1
  8. package/dist/assets/{ModelConfig-CqJubuwU.js → ModelConfig-DbEKVVg4.js} +1 -1
  9. package/dist/assets/{ProvidersList-BoSsFBk5.js → ProvidersList-Ck5PgTF2.js} +1 -1
  10. package/dist/assets/{RemoteAccessPage-S1ChRWMX.js → RemoteAccessPage-4JED9IcK.js} +1 -1
  11. package/dist/assets/{RuntimeConfig-WnFUsayT.js → RuntimeConfig-CB04ug9v.js} +1 -1
  12. package/dist/assets/{SearchConfig-D9V07oqj.js → SearchConfig-DmuvL9Pn.js} +1 -1
  13. package/dist/assets/{SecretsConfig-Ci8sEzaV.js → SecretsConfig-Dw2sRiSs.js} +1 -1
  14. package/dist/assets/{SessionsConfig-5Nznhx9P.js → SessionsConfig-c-Z9X3xH.js} +1 -1
  15. package/dist/assets/{chat-session-display-D0ZcEkUq.js → chat-session-display-z9RvX-D3.js} +1 -1
  16. package/dist/assets/{index-BvCYcN48.js → index-DqzLj8Sw.js} +3 -3
  17. package/dist/assets/{label-AurG3ZpO.js → label-DPGDZvhm.js} +1 -1
  18. package/dist/assets/{page-layout-Q2hHkfJy.js → page-layout-DMA_ZiHj.js} +1 -1
  19. package/dist/assets/{popover-BKInm43u.js → popover-BBLXwfva.js} +1 -1
  20. package/dist/assets/{security-config-BbPGNJAB.js → security-config-FYNEE2eR.js} +1 -1
  21. package/dist/assets/{skeleton-CuKw6-Ww.js → skeleton-DLM_39_P.js} +1 -1
  22. package/dist/assets/{status-dot-DLk8UxLB.js → status-dot-BOzEprxw.js} +1 -1
  23. package/dist/assets/{switch-BxMSKsQS.js → switch-rE_Ew8fl.js} +1 -1
  24. package/dist/assets/{tabs-custom-B6gK-RY6.js → tabs-custom-BkfMUTHE.js} +1 -1
  25. package/dist/assets/{useConfirmDialog-Dth62a0a.js → useConfirmDialog-0owrqZcT.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 +64 -0
  29. package/src/api/ncp-session-query-cache.ts +72 -2
  30. package/src/api/types.ts +1 -0
  31. package/src/components/chat/adapters/chat-message.adapter.test.ts +40 -0
  32. package/src/components/chat/adapters/chat-message.adapter.ts +11 -0
  33. package/src/components/chat/adapters/chat-message.subagent-tool-card.ts +125 -0
  34. package/src/components/chat/ncp/NcpChatPage.tsx +19 -35
  35. package/src/components/config/README.md +1 -1
  36. package/src/hooks/use-realtime-query-bridge.ts +5 -1
  37. package/dist/assets/ChatPage-Co3GqIVP.js +0 -37
  38. package/src/components/chat/ncp/ncp-chat-realtime-reload.test.ts +0 -44
  39. package/src/components/chat/ncp/ncp-chat-realtime-reload.ts +0 -20
@@ -1 +1 @@
1
- import{r as s,j as o}from"./vendor-MCpnpiKt.js";import{c as t}from"./index-BvCYcN48.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-MCpnpiKt.js";import{c as t}from"./index-DqzLj8Sw.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-MCpnpiKt.js";import{c as l}from"./index-BvCYcN48.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-MCpnpiKt.js";import{c as l}from"./index-DqzLj8Sw.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,aJ as L,j as c,aK as z,aL as G,aM as A,aN as O,aO as P,aP as _,aQ as w,aR as b,aS as H,aT as K,aU as U,aV as V,aW as W,aX as Z,aY as J,aZ as Q,a_ as X,a$ as Y}from"./vendor-MCpnpiKt.js";import{c as q}from"./index-BvCYcN48.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]=L({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":I(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":I(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)"}})})})}),k="PopoverClose",ne=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(k,t);return c.jsx(O.button,{type:"button",...n,ref:a,onClick:P(e.onClick,()=>o.onOpenChange(!1))})});ne.displayName=k;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 I(e){return e?"open":"closed"}var ie=j,ve=y,le=S,pe=M,$=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($,{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=$.displayName;export{ve as A,$ 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,aJ as L,j as c,aK as z,aL as G,aM as A,aN as O,aO as P,aP as _,aQ as w,aR as b,aS as H,aT as K,aU as U,aV as V,aW as W,aX as Z,aY as J,aZ as Q,a_ as X,a$ as Y}from"./vendor-MCpnpiKt.js";import{c as q}from"./index-DqzLj8Sw.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]=L({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":I(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":I(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)"}})})})}),k="PopoverClose",ne=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(k,t);return c.jsx(O.button,{type:"button",...n,ref:a,onClick:P(e.onClick,()=>o.onOpenChange(!1))})});ne.displayName=k;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 I(e){return e?"open":"closed"}var ie=j,ve=y,le=S,pe=M,$=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($,{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=$.displayName;export{ve as A,$ as C,pe as P,ie as R,le as T,Pe as a,ge as b,ue as c};
@@ -1 +1 @@
1
- import{r as c,j as e,e as M}from"./vendor-MCpnpiKt.js";import{a3 as O,a4 as R,a5 as I,a6 as B,a7 as _,C as u,a0 as h,a1 as x,t as s,a2 as m,E as p,B as j,I as l}from"./index-BvCYcN48.js";import{L as o}from"./label-AurG3ZpO.js";import{S as G}from"./switch-BxMSKsQS.js";import{P as V,a as W}from"./page-layout-Q2hHkfJy.js";const q=8;function A(r){return r.trim().length>=q}function L(r,n){return r!==n?(M.error(s("authPasswordMismatch")),!1):!0}function z(){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 Z(){return e.jsxs(V,{className:"space-y-6",children:[e.jsx(W,{title:s("authSecurityTitle"),description:s("authSecurityDescription")}),e.jsx(z,{})]})}export{Z as SecurityConfig};
1
+ import{r as c,j as e,e as M}from"./vendor-MCpnpiKt.js";import{a3 as O,a4 as R,a5 as I,a6 as B,a7 as _,C as u,a0 as h,a1 as x,t as s,a2 as m,E as p,B as j,I as l}from"./index-DqzLj8Sw.js";import{L as o}from"./label-DPGDZvhm.js";import{S as G}from"./switch-rE_Ew8fl.js";import{P as V,a as W}from"./page-layout-DMA_ZiHj.js";const q=8;function A(r){return r.trim().length>=q}function L(r,n){return r!==n?(M.error(s("authPasswordMismatch")),!1):!0}function z(){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 Z(){return e.jsxs(V,{className:"space-y-6",children:[e.jsx(W,{title:s("authSecurityTitle"),description:s("authSecurityDescription")}),e.jsx(z,{})]})}export{Z as SecurityConfig};
@@ -1 +1 @@
1
- import{j as t}from"./vendor-MCpnpiKt.js";import{c as o}from"./index-BvCYcN48.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-MCpnpiKt.js";import{c as o}from"./index-DqzLj8Sw.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-MCpnpiKt.js";import{c as a}from"./index-BvCYcN48.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-MCpnpiKt.js";import{c as a}from"./index-DqzLj8Sw.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-MCpnpiKt.js";import{c as t}from"./index-BvCYcN48.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-MCpnpiKt.js";import{c as t}from"./index-DqzLj8Sw.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-MCpnpiKt.js";import{an as m,c as a}from"./index-BvCYcN48.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-MCpnpiKt.js";import{an as m,c as a}from"./index-DqzLj8Sw.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-MCpnpiKt.js";import{ah as C,ai as h,aj as p,ak as x,al as g,am as j,B as d,t as r}from"./index-BvCYcN48.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(x,{children:c}),o?a.jsx(g,{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};
1
+ import{j as a,r as t}from"./vendor-MCpnpiKt.js";import{ah as C,ai as h,aj as p,ak as x,al as g,am as j,B as d,t as r}from"./index-DqzLj8Sw.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(x,{children:c}),o?a.jsx(g,{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-BvCYcN48.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-DqzLj8Sw.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-MCpnpiKt.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-CfVmBgkf.css">
12
12
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.11.14",
3
+ "version": "0.11.16",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,10 +29,10 @@
29
29
  "zod": "^3.23.8",
30
30
  "zustand": "^5.0.2",
31
31
  "@nextclaw/agent-chat": "0.1.4",
32
- "@nextclaw/agent-chat-ui": "0.2.14",
33
- "@nextclaw/ncp": "0.4.1",
34
- "@nextclaw/ncp-react": "0.4.5",
35
- "@nextclaw/ncp-http-agent-client": "0.3.5"
32
+ "@nextclaw/ncp": "0.4.2",
33
+ "@nextclaw/ncp-http-agent-client": "0.3.6",
34
+ "@nextclaw/ncp-react": "0.4.7",
35
+ "@nextclaw/agent-chat-ui": "0.2.14"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@testing-library/react": "^16.3.0",
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest';
3
3
  import {
4
4
  applyNcpSessionRealtimeEvent,
5
5
  deleteNcpSessionSummaryList,
6
+ updateNcpSessionRunStatusList,
6
7
  upsertNcpSessionSummaryList
7
8
  } from '@/api/ncp-session-query-cache';
8
9
  import type { NcpSessionsListView } from '@/api/types';
@@ -47,6 +48,45 @@ describe('ncp-session-query-cache', () => {
47
48
  });
48
49
  });
49
50
 
51
+ it('ignores stale summaries that would move a session back to an older running state', () => {
52
+ const updated = upsertNcpSessionSummaryList(createSessionsList(), {
53
+ sessionId: 'session-1',
54
+ messageCount: 9,
55
+ updatedAt: '2026-03-29T09:00:00.000Z',
56
+ status: 'running',
57
+ metadata: { label: 'Stale running' }
58
+ });
59
+
60
+ expect(updated?.sessions[0]).toMatchObject({
61
+ sessionId: 'session-1',
62
+ messageCount: 1,
63
+ status: 'idle'
64
+ });
65
+ });
66
+
67
+ it('prefers idle over running when summaries arrive with the same timestamp', () => {
68
+ const current = createSessionsList();
69
+ current.sessions[0] = {
70
+ ...current.sessions[0],
71
+ updatedAt: '2026-03-29T10:00:00.000Z',
72
+ status: 'idle'
73
+ };
74
+
75
+ const updated = upsertNcpSessionSummaryList(current, {
76
+ sessionId: 'session-1',
77
+ messageCount: 5,
78
+ updatedAt: '2026-03-29T10:00:00.000Z',
79
+ status: 'running',
80
+ metadata: {}
81
+ });
82
+
83
+ expect(updated?.sessions[0]).toMatchObject({
84
+ sessionId: 'session-1',
85
+ messageCount: 1,
86
+ status: 'idle'
87
+ });
88
+ });
89
+
50
90
  it('deletes summaries without mutating unrelated entries', () => {
51
91
  const updated = deleteNcpSessionSummaryList(createSessionsList(), 'session-1');
52
92
 
@@ -54,10 +94,34 @@ describe('ncp-session-query-cache', () => {
54
94
  expect(updated?.total).toBe(1);
55
95
  });
56
96
 
97
+ it('updates run status without changing message count or sort order', () => {
98
+ const updated = updateNcpSessionRunStatusList(createSessionsList(), {
99
+ sessionKey: 'session-2',
100
+ status: 'running'
101
+ });
102
+
103
+ expect(updated?.sessions.map((session) => session.sessionId)).toEqual(['session-1', 'session-2']);
104
+ expect(updated?.sessions[1]).toMatchObject({
105
+ sessionId: 'session-2',
106
+ messageCount: 2,
107
+ status: 'running'
108
+ });
109
+ });
110
+
57
111
  it('applies realtime upsert/delete events to every ncp-sessions query cache entry', () => {
58
112
  const queryClient = new QueryClient();
59
113
  queryClient.setQueryData(['ncp-sessions', 200], createSessionsList());
60
114
 
115
+ applyNcpSessionRealtimeEvent(queryClient, {
116
+ type: 'session.run-status',
117
+ payload: {
118
+ sessionKey: 'session-1',
119
+ status: 'running'
120
+ }
121
+ });
122
+
123
+ expect(queryClient.getQueryData<NcpSessionsListView>(['ncp-sessions', 200])?.sessions[0]?.status).toBe('running');
124
+
61
125
  applyNcpSessionRealtimeEvent(queryClient, {
62
126
  type: 'session.summary.upsert',
63
127
  payload: {
@@ -5,6 +5,25 @@ function sortSessionSummaries(summaries: readonly NcpSessionSummaryView[]): NcpS
5
5
  return [...summaries].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
6
6
  }
7
7
 
8
+ function shouldReplaceSessionSummary(
9
+ current: NcpSessionSummaryView,
10
+ next: NcpSessionSummaryView
11
+ ): boolean {
12
+ const timeOrder = next.updatedAt.localeCompare(current.updatedAt);
13
+ if (timeOrder > 0) {
14
+ return true;
15
+ }
16
+ if (timeOrder < 0) {
17
+ return false;
18
+ }
19
+
20
+ if (current.status === next.status) {
21
+ return true;
22
+ }
23
+
24
+ return next.status === 'idle';
25
+ }
26
+
8
27
  export function upsertNcpSessionSummaryList(
9
28
  current: NcpSessionsListView | undefined,
10
29
  summary: NcpSessionSummaryView
@@ -16,7 +35,9 @@ export function upsertNcpSessionSummaryList(
16
35
  const existingIndex = current.sessions.findIndex((session) => session.sessionId === summary.sessionId);
17
36
  const nextSessions =
18
37
  existingIndex >= 0
19
- ? current.sessions.map((session, index) => (index === existingIndex ? summary : session))
38
+ ? current.sessions.map((session, index) =>
39
+ index === existingIndex && shouldReplaceSessionSummary(session, summary) ? summary : session
40
+ )
20
41
  : [...current.sessions, summary];
21
42
  const sortedSessions = sortSessionSummaries(nextSessions);
22
43
 
@@ -62,6 +83,51 @@ export function upsertNcpSessionSummaryInQueryClient(
62
83
  );
63
84
  }
64
85
 
86
+ export function updateNcpSessionRunStatusList(
87
+ current: NcpSessionsListView | undefined,
88
+ payload: { sessionKey: string; status: 'running' | 'idle' }
89
+ ): NcpSessionsListView | undefined {
90
+ if (!current) {
91
+ return current;
92
+ }
93
+
94
+ const normalizedSessionKey = payload.sessionKey.trim();
95
+ if (!normalizedSessionKey) {
96
+ return current;
97
+ }
98
+
99
+ let changed = false;
100
+ const nextSessions = current.sessions.map((session) => {
101
+ if (session.sessionId !== normalizedSessionKey || session.status === payload.status) {
102
+ return session;
103
+ }
104
+ changed = true;
105
+ return {
106
+ ...session,
107
+ status: payload.status
108
+ };
109
+ });
110
+
111
+ if (!changed) {
112
+ return current;
113
+ }
114
+
115
+ return {
116
+ ...current,
117
+ sessions: nextSessions
118
+ };
119
+ }
120
+
121
+ export function updateNcpSessionRunStatusInQueryClient(
122
+ queryClient: QueryClient | undefined,
123
+ payload: { sessionKey: string; status: 'running' | 'idle' }
124
+ ): void {
125
+ queryClient?.setQueriesData<NcpSessionsListView>(
126
+ { queryKey: ['ncp-sessions'] },
127
+ (current) => updateNcpSessionRunStatusList(current, payload)
128
+ );
129
+ }
130
+
65
131
  export function deleteNcpSessionSummaryInQueryClient(
66
132
  queryClient: QueryClient | undefined,
67
133
  sessionKey: string
@@ -74,8 +140,12 @@ export function deleteNcpSessionSummaryInQueryClient(
74
140
 
75
141
  export function applyNcpSessionRealtimeEvent(
76
142
  queryClient: QueryClient | undefined,
77
- event: Extract<WsEvent, { type: 'session.summary.upsert' | 'session.summary.delete' }>
143
+ event: Extract<WsEvent, { type: 'session.run-status' | 'session.summary.upsert' | 'session.summary.delete' }>
78
144
  ): void {
145
+ if (event.type === 'session.run-status') {
146
+ updateNcpSessionRunStatusInQueryClient(queryClient, event.payload);
147
+ return;
148
+ }
79
149
  if (event.type === 'session.summary.upsert') {
80
150
  upsertNcpSessionSummaryInQueryClient(queryClient, event.payload.summary);
81
151
  return;
package/src/api/types.ts CHANGED
@@ -569,6 +569,7 @@ 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.run-status'; payload: { sessionKey: string; status: 'running' | 'idle' } }
572
573
  | { type: 'session.summary.upsert'; payload: { summary: NcpSessionSummaryView } }
573
574
  | { type: 'session.summary.delete'; payload: { sessionKey: string } }
574
575
  | { type: 'config.reload.started'; payload?: Record<string, unknown> }
@@ -141,6 +141,46 @@ it("maps tool lifecycle statuses into visible card state feedback", () => {
141
141
  });
142
142
  });
143
143
 
144
+ it("renders spawn tool cards from structured subagent status updates", () => {
145
+ const adapted = adapt([
146
+ {
147
+ id: "assistant-subagent",
148
+ role: "assistant",
149
+ parts: [
150
+ {
151
+ type: "tool-invocation",
152
+ toolInvocation: {
153
+ status: ToolInvocationStatus.RESULT,
154
+ toolCallId: "spawn-call-1",
155
+ toolName: "spawn",
156
+ args: '{"label":"Verifier","task":"Verify 1+1=2"}',
157
+ result: {
158
+ kind: "nextclaw.subagent_run",
159
+ runId: "subagent-1",
160
+ label: "Verifier",
161
+ task: "Verify 1+1=2",
162
+ status: "completed",
163
+ result: "Verified 1+1=2.",
164
+ },
165
+ },
166
+ },
167
+ ],
168
+ },
169
+ ] as unknown as ChatMessageSource[]);
170
+
171
+ expect(adapted[0]?.parts[0]).toMatchObject({
172
+ type: "tool-card",
173
+ card: {
174
+ toolName: "spawn",
175
+ summary: "label: Verifier · task: Verify 1+1=2",
176
+ output: "Verified 1+1=2.",
177
+ statusTone: "success",
178
+ statusLabel: "Completed",
179
+ titleLabel: "Tool Result",
180
+ },
181
+ });
182
+ });
183
+
144
184
  it("maps non-standard roles back to the generic message role", () => {
145
185
  const adapted = adapt([
146
186
  {
@@ -3,6 +3,7 @@ import {
3
3
  summarizeToolArgs,
4
4
  type ToolCard,
5
5
  } from "@/lib/chat-message";
6
+ import { buildSubagentToolCard } from "@/components/chat/adapters/chat-message.subagent-tool-card";
6
7
  import type {
7
8
  ChatMessageRole,
8
9
  ChatMessageViewModel,
@@ -386,6 +387,16 @@ export function adaptChatMessage(
386
387
  if (assetFileView) {
387
388
  return assetFileView;
388
389
  }
390
+ const subagentToolCard = buildSubagentToolCard({
391
+ invocation,
392
+ texts: params.texts,
393
+ });
394
+ if (subagentToolCard) {
395
+ return {
396
+ type: "tool-card" as const,
397
+ card: buildToolCard(subagentToolCard, params.texts),
398
+ };
399
+ }
389
400
  const statusView = resolveToolCardStatus({
390
401
  status: invocation.status,
391
402
  error: invocation.error,
@@ -0,0 +1,125 @@
1
+ import {
2
+ stringifyUnknown,
3
+ summarizeToolArgs,
4
+ type ToolCard,
5
+ } from "@/lib/chat-message";
6
+ import type { ChatToolPartViewModel } from "@nextclaw/agent-chat-ui";
7
+
8
+ type ToolCardViewSource = ToolCard & {
9
+ statusTone: ChatToolPartViewModel["statusTone"];
10
+ statusLabel: string;
11
+ };
12
+
13
+ type SpawnToolInvocation = {
14
+ toolName: string;
15
+ toolCallId?: string;
16
+ args?: unknown;
17
+ result?: unknown;
18
+ };
19
+
20
+ type SubagentToolCardTexts = {
21
+ toolStatusRunningLabel: string;
22
+ toolStatusCompletedLabel: string;
23
+ toolStatusFailedLabel: string;
24
+ };
25
+
26
+ type SubagentRunResult = {
27
+ runId?: string;
28
+ label?: string;
29
+ task?: string;
30
+ status?: string;
31
+ result?: unknown;
32
+ message?: string;
33
+ };
34
+
35
+ function isRecord(value: unknown): value is Record<string, unknown> {
36
+ return typeof value === "object" && value !== null;
37
+ }
38
+
39
+ function readOptionalString(value: unknown): string | null {
40
+ if (typeof value !== "string") {
41
+ return null;
42
+ }
43
+ const trimmed = value.trim();
44
+ return trimmed.length > 0 ? trimmed : null;
45
+ }
46
+
47
+ function readSubagentRunResult(value: unknown): SubagentRunResult | null {
48
+ if (!isRecord(value)) {
49
+ return null;
50
+ }
51
+ if (value.kind === "nextclaw.subagent_run") {
52
+ return value;
53
+ }
54
+ if (typeof value.runId === "string" && typeof value.status === "string") {
55
+ return value;
56
+ }
57
+ return null;
58
+ }
59
+
60
+ export function buildSubagentToolCard(params: {
61
+ invocation: SpawnToolInvocation;
62
+ texts: SubagentToolCardTexts;
63
+ }): ToolCardViewSource | null {
64
+ if (params.invocation.toolName !== "spawn") {
65
+ return null;
66
+ }
67
+
68
+ const subagentRun = readSubagentRunResult(params.invocation.result);
69
+ if (!subagentRun) {
70
+ return null;
71
+ }
72
+
73
+ const detailParts = [
74
+ readOptionalString(subagentRun.label)
75
+ ? `label: ${subagentRun.label?.trim()}`
76
+ : null,
77
+ readOptionalString(subagentRun.task)
78
+ ? `task: ${subagentRun.task?.trim()}`
79
+ : null,
80
+ ].filter((value): value is string => Boolean(value));
81
+ const normalizedStatus = readOptionalString(subagentRun.status)?.toLowerCase();
82
+ const output =
83
+ (typeof subagentRun.result !== "undefined"
84
+ ? stringifyUnknown(subagentRun.result).trim()
85
+ : "") ||
86
+ readOptionalString(subagentRun.message) ||
87
+ undefined;
88
+
89
+ if (normalizedStatus === "failed") {
90
+ return {
91
+ kind: "result",
92
+ name: params.invocation.toolName,
93
+ detail: detailParts.join(" · ") || summarizeToolArgs(params.invocation.args),
94
+ text: output,
95
+ callId: params.invocation.toolCallId || undefined,
96
+ hasResult: Boolean(output),
97
+ statusTone: "error",
98
+ statusLabel: params.texts.toolStatusFailedLabel,
99
+ };
100
+ }
101
+
102
+ if (normalizedStatus === "completed") {
103
+ return {
104
+ kind: "result",
105
+ name: params.invocation.toolName,
106
+ detail: detailParts.join(" · ") || summarizeToolArgs(params.invocation.args),
107
+ text: output,
108
+ callId: params.invocation.toolCallId || undefined,
109
+ hasResult: Boolean(output),
110
+ statusTone: "success",
111
+ statusLabel: params.texts.toolStatusCompletedLabel,
112
+ };
113
+ }
114
+
115
+ return {
116
+ kind: "result",
117
+ name: params.invocation.toolName,
118
+ detail: detailParts.join(" · ") || summarizeToolArgs(params.invocation.args),
119
+ text: output,
120
+ callId: params.invocation.toolCallId || undefined,
121
+ hasResult: Boolean(output),
122
+ statusTone: "running",
123
+ statusLabel: params.texts.toolStatusRunningLabel,
124
+ };
125
+ }
@@ -23,7 +23,6 @@ import { resolveSessionTypeLabel } from '@/components/chat/useChatSessionTypeSta
23
23
  import { useConfirmDialog } from '@/hooks/useConfirmDialog';
24
24
  import { normalizeRequestedSkills } from '@/lib/chat-runtime-utils';
25
25
  import { appClient } from '@/transport';
26
- import { resolveNcpChatRealtimeReloadAction } from '@/components/chat/ncp/ncp-chat-realtime-reload';
27
26
 
28
27
  function buildNcpSendMetadata(payload: {
29
28
  model?: string;
@@ -71,7 +70,7 @@ export function NcpChatPage({ view }: ChatPageProps) {
71
70
  const { sessionId: routeSessionIdParam } = useParams<{ sessionId?: string }>();
72
71
  const threadRef = useRef<HTMLDivElement | null>(null);
73
72
  const selectedSessionKeyRef = useRef<string | null>(selectedSessionKey);
74
- const pendingRealtimeReloadRef = useRef(false);
73
+ const sessionStreamAttachInFlightRef = useRef(false);
75
74
  const routeSessionKey = useMemo(
76
75
  () => parseSessionKeyFromRoute(routeSessionIdParam),
77
76
  [routeSessionIdParam]
@@ -158,48 +157,33 @@ export function NcpChatPage({ view }: ChatPageProps) {
158
157
  const lastSendError = agent.hydrateError?.message ?? agent.snapshot.error?.message ?? null;
159
158
 
160
159
  useEffect(() => {
161
- const flushRealtimeReload = () => {
162
- const action = resolveNcpChatRealtimeReloadAction({
163
- isHydrating: agent.isHydrating,
164
- isRunning: agent.isRunning,
165
- isSending: agent.isSending,
166
- });
167
- if (action === 'defer') {
168
- pendingRealtimeReloadRef.current = true;
160
+ const attachRealtimeSessionStream = () => {
161
+ if (sessionStreamAttachInFlightRef.current) {
169
162
  return;
170
163
  }
171
- if (action === 'skip') {
172
- pendingRealtimeReloadRef.current = false;
164
+ if (agent.isHydrating || agent.isRunning || agent.isSending) {
173
165
  return;
174
166
  }
175
- pendingRealtimeReloadRef.current = false;
176
- void agent.reloadSeed();
167
+
168
+ sessionStreamAttachInFlightRef.current = true;
169
+ void ncpClient
170
+ .stream({ sessionId: activeSessionId })
171
+ .catch(() => undefined)
172
+ .finally(() => {
173
+ sessionStreamAttachInFlightRef.current = false;
174
+ });
177
175
  };
178
176
 
179
177
  return appClient.subscribe((event) => {
180
- if (event.type === 'session.summary.upsert') {
181
- if (event.payload.summary.sessionId !== activeSessionId) {
182
- return;
183
- }
184
- flushRealtimeReload();
185
- return;
186
- }
187
- if (event.type === 'session.updated' && event.payload.sessionKey === activeSessionId) {
188
- flushRealtimeReload();
178
+ if (
179
+ event.type === 'session.run-status' &&
180
+ event.payload.sessionKey === activeSessionId &&
181
+ event.payload.status === 'running'
182
+ ) {
183
+ attachRealtimeSessionStream();
189
184
  }
190
185
  });
191
- }, [activeSessionId, agent.isHydrating, agent.isRunning, agent.isSending, agent.reloadSeed]);
192
-
193
- useEffect(() => {
194
- if (!pendingRealtimeReloadRef.current) {
195
- return;
196
- }
197
- if (agent.isHydrating || agent.isRunning || agent.isSending) {
198
- return;
199
- }
200
- pendingRealtimeReloadRef.current = false;
201
- void agent.reloadSeed();
202
- }, [agent.isHydrating, agent.isRunning, agent.isSending, agent.reloadSeed]);
186
+ }, [activeSessionId, agent.isHydrating, agent.isRunning, agent.isSending, ncpClient]);
203
187
 
204
188
  useEffect(() => {
205
189
  presenter.chatStreamActionsManager.bind({
@@ -1,2 +1,2 @@
1
1
  ## 目录预算豁免
2
- - 原因:配置中心目录按配置面板维度组织,每个面板都需要独立入口、局部测试与装配文件;当前结构受 UI 信息架构约束,需要保留超过 `20` 个直接代码文件的扁平集合。
2
+ - 原因:配置中心目录按配置面板维度组织,每个面板都需要独立入口、局部测试与装配文件;当前结构受 UI 信息架构约束,需要保留达到或超过 `12` 个直接代码文件的扁平集合。
@@ -62,7 +62,11 @@ function handleRealtimeEvent(
62
62
  handleConfigUpdatedEvent(queryClient, configPath);
63
63
  return;
64
64
  }
65
- if (event.type === 'session.summary.upsert' || event.type === 'session.summary.delete') {
65
+ if (
66
+ event.type === 'session.run-status' ||
67
+ event.type === 'session.summary.upsert' ||
68
+ event.type === 'session.summary.delete'
69
+ ) {
66
70
  applyNcpSessionRealtimeEvent(queryClient, event);
67
71
  return;
68
72
  }