@nextclaw/ui 0.9.10 → 0.9.11

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 (33) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/assets/{ChannelsList-BgJbR6E9.js → ChannelsList-Brc1qLSU.js} +1 -1
  3. package/dist/assets/{ChatPage-Bv9UJPse.js → ChatPage-DmGI776q.js} +1 -1
  4. package/dist/assets/{DocBrowser-Dw9BGO1m.js → DocBrowser-xLVf1p4L.js} +1 -1
  5. package/dist/assets/{LogoBadge-CLc2B6st.js → LogoBadge-CcTyimdr.js} +1 -1
  6. package/dist/assets/{MarketplacePage-ChqCNL7k.js → MarketplacePage-Bk-qXxyh.js} +1 -1
  7. package/dist/assets/{McpMarketplacePage-B3PF-7ED.js → McpMarketplacePage-gFqAYekc.js} +1 -1
  8. package/dist/assets/{ModelConfig-Dqz_NOow.js → ModelConfig-DnKNTuw6.js} +1 -1
  9. package/dist/assets/{ProvidersList-D2WaZShJ.js → ProvidersList-Cjr8EFu_.js} +1 -1
  10. package/dist/assets/{RemoteAccessPage-D_l9irp4.js → RemoteAccessPage-Rzi5a6Gc.js} +1 -1
  11. package/dist/assets/{RuntimeConfig-TDxQLuGy.js → RuntimeConfig-CttN--Tv.js} +1 -1
  12. package/dist/assets/{SearchConfig-gba64nGn.js → SearchConfig-D-GzinsL.js} +1 -1
  13. package/dist/assets/{SecretsConfig-DpL8wgly.js → SecretsConfig-BvqQq4Ds.js} +1 -1
  14. package/dist/assets/{SessionsConfig-CAODVTNW.js → SessionsConfig-DbtnLmI6.js} +1 -1
  15. package/dist/assets/{chat-message-CSG50nNb.js → chat-message-DYQjL1tD.js} +1 -1
  16. package/dist/assets/{index-DaEflNCE.js → index-ClLy_7T2.js} +5 -5
  17. package/dist/assets/{label-3T28q3PJ.js → label-DBSKOMGE.js} +1 -1
  18. package/dist/assets/{page-layout-BrXOQeua.js → page-layout-B5th9UzR.js} +1 -1
  19. package/dist/assets/{popover-BrBJjElY.js → popover-BEIWRoeP.js} +1 -1
  20. package/dist/assets/{security-config-oGAhN4Zf.js → security-config-D72JskP5.js} +1 -1
  21. package/dist/assets/{skeleton-CIPQUKo2.js → skeleton-B_Pn9x0i.js} +1 -1
  22. package/dist/assets/{status-dot-QL3hmT1d.js → status-dot-CU5ZpOn1.js} +1 -1
  23. package/dist/assets/{switch-Dbt2kUg2.js → switch-BdaXEtXk.js} +1 -1
  24. package/dist/assets/{tabs-custom-y5hdkzXk.js → tabs-custom-BVhSoteN.js} +1 -1
  25. package/dist/assets/{useConfirmDialog-B4zwBVbl.js → useConfirmDialog-Dugi9V-Z.js} +1 -1
  26. package/dist/index.html +1 -1
  27. package/package.json +4 -4
  28. package/src/api/config.ts +1 -4
  29. package/src/hooks/use-realtime-query-bridge.ts +77 -71
  30. package/src/transport/local.transport.ts +5 -123
  31. package/src/transport/remote.transport.ts +39 -74
  32. package/src/transport/sse-stream.ts +114 -0
  33. package/src/transport/transport-websocket-url.ts +24 -0
@@ -1 +1 @@
1
- import{r as s,j as o}from"./vendor-CmQZsDAE.js";import{c as t}from"./index-DaEflNCE.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-CmQZsDAE.js";import{c as t}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{c as l}from"./index-DaEflNCE.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-CmQZsDAE.js";import{c as l}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{c as q}from"./index-DaEflNCE.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-CmQZsDAE.js";import{c as q}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{a5 as O,a6 as R,a7 as I,a8 as B,a9 as _,C as u,a2 as h,a3 as x,t as s,a4 as m,z as p,B as j,I as l}from"./index-DaEflNCE.js";import{L as o}from"./label-3T28q3PJ.js";import{S as z}from"./switch-Dbt2kUg2.js";import{P as G,a as V}from"./page-layout-BrXOQeua.js";const W=8;function A(r){return r.trim().length>=W}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(z,{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(G,{className:"space-y-6",children:[e.jsx(V,{title:s("authSecurityTitle"),description:s("authSecurityDescription")}),e.jsx(q,{})]})}export{Z as SecurityConfig};
1
+ import{r as c,j as e,e as M}from"./vendor-CmQZsDAE.js";import{a5 as O,a6 as R,a7 as I,a8 as B,a9 as _,C as u,a2 as h,a3 as x,t as s,a4 as m,z as p,B as j,I as l}from"./index-ClLy_7T2.js";import{L as o}from"./label-DBSKOMGE.js";import{S as z}from"./switch-BdaXEtXk.js";import{P as G,a as V}from"./page-layout-B5th9UzR.js";const W=8;function A(r){return r.trim().length>=W}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(z,{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(G,{className:"space-y-6",children:[e.jsx(V,{title:s("authSecurityTitle"),description:s("authSecurityDescription")}),e.jsx(q,{})]})}export{Z as SecurityConfig};
@@ -1 +1 @@
1
- import{j as t}from"./vendor-CmQZsDAE.js";import{c as o}from"./index-DaEflNCE.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-CmQZsDAE.js";import{c as o}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{c as a}from"./index-DaEflNCE.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-CmQZsDAE.js";import{c as a}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{c as t}from"./index-DaEflNCE.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-CmQZsDAE.js";import{c as t}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{ap as m,c as a}from"./index-DaEflNCE.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-CmQZsDAE.js";import{ap as m,c as a}from"./index-ClLy_7T2.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-CmQZsDAE.js";import{aj as C,ak as p,al as h,am as x,an as g,ao as j,B as d,t as r}from"./index-DaEflNCE.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(p,{className:"[&>:last-child]:hidden",onCloseAutoFocus:b=>b.preventDefault(),children:[a.jsxs(h,{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-CmQZsDAE.js";import{aj as C,ak as p,al as h,am as x,an as g,ao as j,B as d,t as r}from"./index-ClLy_7T2.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(p,{className:"[&>:last-child]:hidden",onCloseAutoFocus:b=>b.preventDefault(),children:[a.jsxs(h,{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-DaEflNCE.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-ClLy_7T2.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-CmQZsDAE.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-DfEAJJsA.css">
12
12
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.9.10",
3
+ "version": "0.9.11",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,11 +27,11 @@
27
27
  "tailwind-merge": "^2.5.4",
28
28
  "zod": "^3.23.8",
29
29
  "zustand": "^5.0.2",
30
- "@nextclaw/agent-chat": "0.1.1",
31
30
  "@nextclaw/ncp-http-agent-client": "0.3.1",
31
+ "@nextclaw/agent-chat": "0.1.1",
32
+ "@nextclaw/ncp": "0.3.1",
32
33
  "@nextclaw/agent-chat-ui": "0.2.1",
33
- "@nextclaw/ncp-react": "0.3.2",
34
- "@nextclaw/ncp": "0.3.1"
34
+ "@nextclaw/ncp-react": "0.3.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@testing-library/react": "^16.3.0",
package/src/api/config.ts CHANGED
@@ -142,10 +142,7 @@ export async function fetchConfigSchema(): Promise<ConfigSchemaResponse> {
142
142
  }
143
143
 
144
144
  // PUT /api/config/model
145
- export async function updateModel(data: {
146
- model: string;
147
- workspace?: string;
148
- }): Promise<{ model: string; workspace?: string }> {
145
+ export async function updateModel(data: { model: string; workspace?: string }): Promise<{ model: string; workspace?: string }> {
149
146
  const response = await api.put<{ model: string; workspace?: string }>('/api/config/model', data);
150
147
  if (!response.ok) {
151
148
  throw new Error(response.error.message);
@@ -3,6 +3,9 @@ import { appClient } from '@/transport';
3
3
  import { useUiStore } from '@/stores/ui.store';
4
4
  import type { QueryClient } from '@tanstack/react-query';
5
5
 
6
+ type ConnectionStatus = 'connected' | 'disconnected' | 'connecting';
7
+ type SetConnectionStatus = (status: ConnectionStatus) => void;
8
+
6
9
  function shouldInvalidateConfigQuery(configPath: string) {
7
10
  const normalized = configPath.trim().toLowerCase();
8
11
  if (!normalized) {
@@ -27,82 +30,85 @@ function invalidateMarketplaceQueries(queryClient: QueryClient | undefined, conf
27
30
  }
28
31
  }
29
32
 
30
- export function useRealtimeQueryBridge(queryClient?: QueryClient) {
31
- const { setConnectionStatus } = useUiStore();
32
-
33
- useEffect(() => {
34
- const invalidateSessionQueries = (sessionKey?: string) => {
35
- if (!queryClient) {
36
- return;
37
- }
38
- queryClient.invalidateQueries({ queryKey: ['sessions'] });
39
- queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
40
- if (sessionKey && sessionKey.trim().length > 0) {
41
- queryClient.invalidateQueries({ queryKey: ['session-history', sessionKey.trim()] });
42
- queryClient.invalidateQueries({ queryKey: ['ncp-session-messages', sessionKey.trim()] });
43
- return;
44
- }
45
- queryClient.invalidateQueries({ queryKey: ['session-history'] });
46
- queryClient.invalidateQueries({ queryKey: ['ncp-session-messages'] });
47
- };
48
-
49
- setConnectionStatus('connecting');
50
-
51
- return appClient.subscribe((event) => {
52
- if (event.type === 'connection.open') {
53
- setConnectionStatus('connected');
54
- return;
55
- }
33
+ function invalidateSessionQueries(queryClient: QueryClient | undefined, sessionKey?: string): void {
34
+ if (!queryClient) {
35
+ return;
36
+ }
37
+ queryClient.invalidateQueries({ queryKey: ['sessions'] });
38
+ queryClient.invalidateQueries({ queryKey: ['ncp-sessions'] });
39
+ if (sessionKey && sessionKey.trim().length > 0) {
40
+ queryClient.invalidateQueries({ queryKey: ['session-history', sessionKey.trim()] });
41
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-messages', sessionKey.trim()] });
42
+ return;
43
+ }
44
+ queryClient.invalidateQueries({ queryKey: ['session-history'] });
45
+ queryClient.invalidateQueries({ queryKey: ['ncp-session-messages'] });
46
+ }
56
47
 
57
- if (event.type === 'connection.close') {
58
- setConnectionStatus('disconnected');
59
- return;
60
- }
48
+ function handleConfigUpdatedEvent(queryClient: QueryClient | undefined, path: string): void {
49
+ if (queryClient && shouldInvalidateConfigQuery(path)) {
50
+ queryClient.invalidateQueries({ queryKey: ['config'] });
51
+ }
52
+ if (path.startsWith('session')) {
53
+ invalidateSessionQueries(queryClient);
54
+ }
55
+ invalidateMarketplaceQueries(queryClient, path);
56
+ }
61
57
 
62
- if (event.type === 'connection.error') {
63
- setConnectionStatus('disconnected');
64
- return;
65
- }
58
+ function handleRunUpdatedEvent(queryClient: QueryClient | undefined, payload: { run: { sessionKey?: string; runId?: string } }): void {
59
+ if (!queryClient) {
60
+ return;
61
+ }
62
+ const { sessionKey, runId } = payload.run;
63
+ queryClient.invalidateQueries({ queryKey: ['chat-runs'] });
64
+ if (sessionKey) {
65
+ queryClient.invalidateQueries({ queryKey: ['sessions'] });
66
+ queryClient.invalidateQueries({ queryKey: ['session-history', sessionKey] });
67
+ } else {
68
+ queryClient.invalidateQueries({ queryKey: ['session-history'] });
69
+ }
70
+ if (runId) {
71
+ queryClient.invalidateQueries({ queryKey: ['chat-run', runId] });
72
+ }
73
+ }
66
74
 
67
- if (event.type === 'config.updated') {
68
- const configPath = typeof event.payload?.path === 'string' ? event.payload.path : '';
69
- if (queryClient && shouldInvalidateConfigQuery(configPath)) {
70
- queryClient.invalidateQueries({ queryKey: ['config'] });
71
- }
72
- if (configPath.startsWith('session')) {
73
- invalidateSessionQueries();
74
- }
75
- invalidateMarketplaceQueries(queryClient, configPath);
76
- return;
77
- }
75
+ function handleRealtimeEvent(
76
+ queryClient: QueryClient | undefined,
77
+ setConnectionStatus: SetConnectionStatus,
78
+ event: Parameters<Parameters<typeof appClient.subscribe>[0]>[0]
79
+ ): void {
80
+ if (event.type === 'connection.open') {
81
+ setConnectionStatus('connected');
82
+ return;
83
+ }
84
+ if (event.type === 'connection.close' || event.type === 'connection.error') {
85
+ setConnectionStatus('disconnected');
86
+ return;
87
+ }
88
+ if (event.type === 'config.updated') {
89
+ const configPath = typeof event.payload?.path === 'string' ? event.payload.path : '';
90
+ handleConfigUpdatedEvent(queryClient, configPath);
91
+ return;
92
+ }
93
+ if (event.type === 'run.updated') {
94
+ handleRunUpdatedEvent(queryClient, event.payload);
95
+ return;
96
+ }
97
+ if (event.type === 'session.updated') {
98
+ invalidateSessionQueries(queryClient, event.payload.sessionKey);
99
+ return;
100
+ }
101
+ if (event.type === 'error') {
102
+ console.error('Realtime transport error:', event.payload.message);
103
+ }
104
+ }
78
105
 
79
- if (event.type === 'run.updated') {
80
- if (!queryClient) {
81
- return;
82
- }
83
- const sessionKey = event.payload.run.sessionKey;
84
- const runId = event.payload.run.runId;
85
- queryClient.invalidateQueries({ queryKey: ['chat-runs'] });
86
- if (sessionKey) {
87
- queryClient.invalidateQueries({ queryKey: ['sessions'] });
88
- queryClient.invalidateQueries({ queryKey: ['session-history', sessionKey] });
89
- } else {
90
- queryClient.invalidateQueries({ queryKey: ['session-history'] });
91
- }
92
- if (runId) {
93
- queryClient.invalidateQueries({ queryKey: ['chat-run', runId] });
94
- }
95
- return;
96
- }
106
+ export function useRealtimeQueryBridge(queryClient?: QueryClient) {
107
+ const { setConnectionStatus } = useUiStore();
97
108
 
98
- if (event.type === 'session.updated') {
99
- invalidateSessionQueries(event.payload.sessionKey);
100
- return;
101
- }
109
+ useEffect(() => {
110
+ setConnectionStatus('connecting');
102
111
 
103
- if (event.type === 'error') {
104
- console.error('Realtime transport error:', event.payload.message);
105
- }
106
- });
112
+ return appClient.subscribe((event) => handleRealtimeEvent(queryClient, setConnectionStatus, event));
107
113
  }, [queryClient, setConnectionStatus]);
108
114
  }
@@ -1,34 +1,11 @@
1
1
  import { API_BASE, requestApiResponse } from '@/api/client';
2
2
  import type { ApiResponse } from '@/api/types';
3
- import type { AppEvent, AppTransport, RequestInput, StreamEvent, StreamInput, StreamSession } from './transport.types';
3
+ import type { AppEvent, AppTransport, RequestInput, StreamInput, StreamSession } from './transport.types';
4
+ import { readSseStreamResult } from './sse-stream';
5
+ import { resolveTransportWebSocketUrl } from './transport-websocket-url';
4
6
 
5
7
  type EventHandler = (event: AppEvent) => void;
6
8
 
7
- function toWebSocketUrl(base: string, path: string): string {
8
- const normalizedBase = base.replace(/\/$/, '');
9
- try {
10
- const resolved = new URL(normalizedBase, window.location.origin);
11
- const protocol =
12
- resolved.protocol === 'https:'
13
- ? 'wss:'
14
- : resolved.protocol === 'http:'
15
- ? 'ws:'
16
- : resolved.protocol;
17
- return `${protocol}//${resolved.host}${path}`;
18
- } catch {
19
- if (normalizedBase.startsWith('wss://') || normalizedBase.startsWith('ws://')) {
20
- return `${normalizedBase}${path}`;
21
- }
22
- if (normalizedBase.startsWith('https://')) {
23
- return `${normalizedBase.replace(/^https:/, 'wss:')}${path}`;
24
- }
25
- if (normalizedBase.startsWith('http://')) {
26
- return `${normalizedBase.replace(/^http:/, 'ws:')}${path}`;
27
- }
28
- return `${normalizedBase}${path}`;
29
- }
30
- }
31
-
32
9
  function createTransportError(response: ApiResponse<unknown>, fallback: string): Error {
33
10
  if (!response.ok) {
34
11
  return new Error(response.error.message);
@@ -36,43 +13,6 @@ function createTransportError(response: ApiResponse<unknown>, fallback: string):
36
13
  return new Error(fallback);
37
14
  }
38
15
 
39
- function parseSseFrame(frame: string): StreamEvent | null {
40
- const lines = frame.split('\n');
41
- let name = '';
42
- const dataLines: string[] = [];
43
- for (const raw of lines) {
44
- const line = raw.trimEnd();
45
- if (!line || line.startsWith(':')) {
46
- continue;
47
- }
48
- if (line.startsWith('event:')) {
49
- name = line.slice(6).trim();
50
- continue;
51
- }
52
- if (line.startsWith('data:')) {
53
- dataLines.push(line.slice(5).trimStart());
54
- }
55
- }
56
- if (!name) {
57
- return null;
58
- }
59
-
60
- let payload: unknown = undefined;
61
- const data = dataLines.join('\n');
62
- if (data) {
63
- try {
64
- payload = JSON.parse(data);
65
- } catch {
66
- payload = data;
67
- }
68
- }
69
-
70
- return {
71
- name,
72
- payload
73
- };
74
- }
75
-
76
16
  class LocalRealtimeGateway {
77
17
  private socket: WebSocket | null = null;
78
18
  private reconnectTimer: number | null = null;
@@ -168,7 +108,7 @@ export class LocalAppTransport implements AppTransport {
168
108
  } = {}
169
109
  ) {
170
110
  const apiBase = options.apiBase ?? API_BASE;
171
- this.realtimeGateway = new LocalRealtimeGateway(toWebSocketUrl(apiBase, options.wsPath ?? '/ws'));
111
+ this.realtimeGateway = new LocalRealtimeGateway(resolveTransportWebSocketUrl(apiBase, options.wsPath ?? '/ws'));
172
112
  }
173
113
 
174
114
  async request<T>(input: RequestInput): Promise<T> {
@@ -209,69 +149,11 @@ export class LocalAppTransport implements AppTransport {
209
149
  const text = await response.text();
210
150
  throw new Error(text.trim() || `HTTP ${response.status}`);
211
151
  }
212
-
213
- const reader = response.body?.getReader();
214
- if (!reader) {
215
- throw new Error('SSE response body unavailable');
216
- }
217
-
218
- const decoder = new TextDecoder();
219
- let buffer = '';
220
- let finalResult: unknown;
221
-
222
152
  try {
223
- while (true) {
224
- const { value, done } = await reader.read();
225
- if (done) {
226
- break;
227
- }
228
- buffer += decoder.decode(value, { stream: true });
229
- let boundary = buffer.indexOf('\n\n');
230
- while (boundary !== -1) {
231
- const frame = parseSseFrame(buffer.slice(0, boundary));
232
- buffer = buffer.slice(boundary + 2);
233
- if (frame) {
234
- if (frame.name === 'final') {
235
- finalResult = frame.payload;
236
- } else if (frame.name === 'error') {
237
- const errorPayload = frame.payload as { message?: string } | string | undefined;
238
- const message = typeof errorPayload === 'string'
239
- ? errorPayload
240
- : errorPayload?.message ?? 'chat stream failed';
241
- throw new Error(message);
242
- } else {
243
- input.onEvent(frame);
244
- }
245
- }
246
- boundary = buffer.indexOf('\n\n');
247
- }
248
- }
249
-
250
- if (buffer.trim()) {
251
- const frame = parseSseFrame(buffer);
252
- if (frame) {
253
- if (frame.name === 'final') {
254
- finalResult = frame.payload;
255
- } else if (frame.name === 'error') {
256
- const errorPayload = frame.payload as { message?: string } | string | undefined;
257
- const message = typeof errorPayload === 'string'
258
- ? errorPayload
259
- : errorPayload?.message ?? 'chat stream failed';
260
- throw new Error(message);
261
- } else {
262
- input.onEvent(frame);
263
- }
264
- }
265
- }
153
+ return await readSseStreamResult<TFinal>(response, input.onEvent);
266
154
  } finally {
267
- reader.releaseLock();
268
155
  input.signal?.removeEventListener('abort', abort);
269
156
  }
270
-
271
- if (finalResult === undefined) {
272
- throw new Error('stream ended without final event');
273
- }
274
- return finalResult as TFinal;
275
157
  })();
276
158
 
277
159
  return {
@@ -1,6 +1,7 @@
1
1
  import { API_BASE } from '@/api/client';
2
2
  import type { ApiError } from '@/api/types';
3
3
  import type { AppEvent, AppTransport, RemoteRuntimeInfo, RequestInput, StreamInput, StreamSession } from './transport.types';
4
+ import { resolveTransportWebSocketUrl } from './transport-websocket-url';
4
5
 
5
6
  type RemoteTarget = {
6
7
  method: string;
@@ -34,31 +35,6 @@ type PendingStream = {
34
35
  reject: (error: Error) => void;
35
36
  };
36
37
 
37
- function createWsUrl(apiBase: string, wsPath: string): string {
38
- const normalizedBase = apiBase.replace(/\/$/, '');
39
- try {
40
- const resolved = new URL(normalizedBase, window.location.origin);
41
- const protocol =
42
- resolved.protocol === 'https:'
43
- ? 'wss:'
44
- : resolved.protocol === 'http:'
45
- ? 'ws:'
46
- : resolved.protocol;
47
- return `${protocol}//${resolved.host}${wsPath}`;
48
- } catch {
49
- if (normalizedBase.startsWith('wss://') || normalizedBase.startsWith('ws://')) {
50
- return `${normalizedBase}${wsPath}`;
51
- }
52
- if (normalizedBase.startsWith('https://')) {
53
- return `${normalizedBase.replace(/^https:/, 'wss:')}${wsPath}`;
54
- }
55
- if (normalizedBase.startsWith('http://')) {
56
- return `${normalizedBase.replace(/^http:/, 'ws:')}${wsPath}`;
57
- }
58
- return `${normalizedBase}${wsPath}`;
59
- }
60
- }
61
-
62
38
  function normalizeApiError(body: unknown, status: number, fallback: string): Error {
63
39
  if (typeof body === 'object' && body && 'ok' in body) {
64
40
  const typed = body as { ok?: boolean; error?: ApiError; data?: unknown };
@@ -243,7 +219,7 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
243
219
  return await this.connectPromise;
244
220
  }
245
221
 
246
- const wsUrl = createWsUrl(this.apiBase, this.runtime.wsPath);
222
+ const wsUrl = resolveTransportWebSocketUrl(this.apiBase, this.runtime.wsPath);
247
223
  this.manualClose = false;
248
224
  this.connectPromise = new Promise<void>((innerResolve, innerReject) => {
249
225
  const socket = new WebSocket(wsUrl);
@@ -330,69 +306,58 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
330
306
  }
331
307
 
332
308
  private handleFrame(frame: RemoteBrowserFrame): void {
333
- if (frame.type === 'response') {
334
- const pending = this.pendingRequests.get(frame.id);
335
- if (!pending) {
336
- return;
337
- }
338
- this.pendingRequests.delete(frame.id);
339
- if (frame.status >= 400) {
340
- pending.reject(normalizeApiError(frame.body, frame.status, 'Remote request failed.'));
341
- return;
342
- }
343
- try {
344
- pending.resolve(unwrapApiBody(frame.body));
345
- } catch (error) {
346
- pending.reject(error instanceof Error ? error : new Error(String(error)));
347
- }
309
+ if (frame.type === 'response' || frame.type === 'request.error') {
310
+ this.handleRequestFrame(frame);
348
311
  return;
349
312
  }
350
-
351
- if (frame.type === 'request.error') {
352
- const pending = this.pendingRequests.get(frame.id);
353
- if (!pending) {
354
- return;
355
- }
356
- this.pendingRequests.delete(frame.id);
357
- pending.reject(new Error(frame.message));
313
+ if (frame.type === 'stream.event' || frame.type === 'stream.end' || frame.type === 'stream.error') {
314
+ this.handleStreamFrame(frame);
358
315
  return;
359
316
  }
360
-
361
- if (frame.type === 'stream.event') {
362
- this.pendingStreams.get(frame.streamId)?.onEvent({
363
- name: frame.event,
364
- payload: frame.payload
365
- });
317
+ if (frame.type === 'event') {
318
+ this.emit(frame.event);
366
319
  return;
367
320
  }
321
+ if (frame.type === 'connection.error') {
322
+ this.emit({ type: 'connection.error', payload: { message: frame.message } });
323
+ }
324
+ }
368
325
 
369
- if (frame.type === 'stream.end') {
370
- const pending = this.pendingStreams.get(frame.streamId);
371
- if (!pending) {
372
- return;
373
- }
374
- this.pendingStreams.delete(frame.streamId);
375
- pending.resolve(frame.result);
326
+ private handleRequestFrame(frame: Extract<RemoteBrowserFrame, { type: 'response' | 'request.error' }>): void {
327
+ const pending = this.pendingRequests.get(frame.id);
328
+ if (!pending) {
376
329
  return;
377
330
  }
378
-
379
- if (frame.type === 'stream.error') {
380
- const pending = this.pendingStreams.get(frame.streamId);
381
- if (!pending) {
382
- return;
383
- }
384
- this.pendingStreams.delete(frame.streamId);
331
+ this.pendingRequests.delete(frame.id);
332
+ if (frame.type === 'request.error') {
385
333
  pending.reject(new Error(frame.message));
386
334
  return;
387
335
  }
388
-
389
- if (frame.type === 'event') {
390
- this.emit(frame.event);
336
+ if (frame.status >= 400) {
337
+ pending.reject(normalizeApiError(frame.body, frame.status, 'Remote request failed.'));
391
338
  return;
392
339
  }
340
+ try {
341
+ pending.resolve(unwrapApiBody(frame.body));
342
+ } catch (error) {
343
+ pending.reject(error instanceof Error ? error : new Error(String(error)));
344
+ }
345
+ }
393
346
 
394
- if (frame.type === 'connection.error') {
395
- this.emit({ type: 'connection.error', payload: { message: frame.message } });
347
+ private handleStreamFrame(frame: Extract<RemoteBrowserFrame, { type: 'stream.event' | 'stream.end' | 'stream.error' }>): void {
348
+ const pending = this.pendingStreams.get(frame.streamId);
349
+ if (!pending) {
350
+ return;
351
+ }
352
+ if (frame.type === 'stream.event') {
353
+ pending.onEvent({ name: frame.event, payload: frame.payload });
354
+ return;
355
+ }
356
+ this.pendingStreams.delete(frame.streamId);
357
+ if (frame.type === 'stream.end') {
358
+ pending.resolve(frame.result);
359
+ return;
396
360
  }
361
+ pending.reject(new Error(frame.message));
397
362
  }
398
363
  }