@nextclaw/ui 0.6.8 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/assets/{ChannelsList-DH5fzlPu.js → ChannelsList-TyMb5Mgz.js} +1 -1
  3. package/dist/assets/{ChatPage-BrLCnJSb.js → ChatPage-CQerYqvy.js} +12 -12
  4. package/dist/assets/{DocBrowser-DPQHJVsZ.js → DocBrowser-CNtrA0ps.js} +1 -1
  5. package/dist/assets/{LogoBadge-FEb4_vSq.js → LogoBadge-BLqiOM5D.js} +1 -1
  6. package/dist/assets/{MarketplacePage-BAVXYeZA.js → MarketplacePage-CotZxxNe.js} +3 -3
  7. package/dist/assets/{ModelConfig-BqPXe7nw.js → ModelConfig-CCsQ8KFq.js} +1 -1
  8. package/dist/assets/ProvidersList-BYYX5K_g.js +1 -0
  9. package/dist/assets/{RuntimeConfig-DTYSU4_d.js → RuntimeConfig-BO6s-ls-.js} +1 -1
  10. package/dist/assets/{SecretsConfig-nNzs3YDm.js → SecretsConfig-mayFdxpM.js} +1 -1
  11. package/dist/assets/{SessionsConfig-CHjeyqEQ.js → SessionsConfig-DAIczdBj.js} +1 -1
  12. package/dist/assets/{card-73MmEZi7.js → card-BP5YnL-G.js} +1 -1
  13. package/dist/assets/{index-DI6BuShn.css → index-BUiahmWm.css} +1 -1
  14. package/dist/assets/index-D6_5HaDl.js +7 -0
  15. package/dist/assets/{input-1MCMs6Yf.js → input-B1D2QX0O.js} +1 -1
  16. package/dist/assets/{label-C4Q8RlBJ.js → label-DW0j-fXA.js} +1 -1
  17. package/dist/assets/{page-layout-CK0vcVmV.js → page-layout-Ch-H9gD-.js} +1 -1
  18. package/dist/assets/{session-run-status-BaNlKvi6.js → session-run-status-BUYsQeWs.js} +1 -1
  19. package/dist/assets/{switch-Bf8w_cF1.js → switch-_cZHlGKB.js} +1 -1
  20. package/dist/assets/{tabs-custom-B6Gw8gax.js → tabs-custom-ARxqYYjG.js} +1 -1
  21. package/dist/assets/{useConfirmDialog-B5CZ4EDN.js → useConfirmDialog-BaU7nIat.js} +1 -1
  22. package/dist/index.html +2 -2
  23. package/package.json +1 -1
  24. package/src/api/config.ts +6 -2
  25. package/src/api/types.ts +40 -4
  26. package/src/components/chat/useChatStreamController.ts +18 -3
  27. package/src/components/config/ProviderForm.tsx +221 -14
  28. package/src/components/marketplace/MarketplacePage.tsx +48 -44
  29. package/src/hooks/useConfig.ts +2 -1
  30. package/src/hooks/useWebSocket.ts +23 -1
  31. package/src/lib/i18n.ts +2 -0
  32. package/dist/assets/ProvidersList-vpKPuIxV.js +0 -1
  33. package/dist/assets/index-CTLvVlk8.js +0 -7
@@ -1,4 +1,4 @@
1
- import{r as i,aj as b,aU as I,ad as ie,j as s,af as j,ai as le,ak as h,al as D,am as ce,an as de,ao as ue,aq as fe,ar as ge,as as me,ap as pe,aV as ve,ax as xe}from"./vendor-C--HHaLf.js";import{c as N,t as x}from"./index-CTLvVlk8.js";import{B as w}from"./page-layout-CK0vcVmV.js";function Ne(e,t){return i.useReducer((o,a)=>t[o][a]??o,e)}var E=e=>{const{present:t,children:o}=e,a=De(t),r=typeof o=="function"?o({present:a.isPresent}):i.Children.only(o),n=b(a.ref,he(r));return typeof o=="function"||a.isPresent?i.cloneElement(r,{ref:n}):null};E.displayName="Presence";function De(e){const[t,o]=i.useState(),a=i.useRef(null),r=i.useRef(e),n=i.useRef("none"),l=e?"mounted":"unmounted",[c,u]=Ne(l,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return i.useEffect(()=>{const d=y(a.current);n.current=c==="mounted"?d:"none"},[c]),I(()=>{const d=a.current,f=r.current;if(f!==e){const C=n.current,p=y(d);e?u("MOUNT"):p==="none"||(d==null?void 0:d.display)==="none"?u("UNMOUNT"):u(f&&C!==p?"ANIMATION_OUT":"UNMOUNT"),r.current=e}},[e,u]),I(()=>{if(t){let d;const f=t.ownerDocument.defaultView??window,m=p=>{const re=y(a.current).includes(CSS.escape(p.animationName));if(p.target===t&&re&&(u("ANIMATION_END"),!r.current)){const se=t.style.animationFillMode;t.style.animationFillMode="forwards",d=f.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=se)})}},C=p=>{p.target===t&&(n.current=y(a.current))};return t.addEventListener("animationstart",C),t.addEventListener("animationcancel",m),t.addEventListener("animationend",m),()=>{f.clearTimeout(d),t.removeEventListener("animationstart",C),t.removeEventListener("animationcancel",m),t.removeEventListener("animationend",m)}}else u("ANIMATION_END")},[t,u]),{isPresent:["mounted","unmountSuspended"].includes(c),ref:i.useCallback(d=>{a.current=d?getComputedStyle(d):null,o(d)},[])}}function y(e){return(e==null?void 0:e.animationName)||"none"}function he(e){var a,r;let t=(a=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:a.get,o=t&&"isReactWarning"in t&&t.isReactWarning;return o?e.ref:(t=(r=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:r.get,o=t&&"isReactWarning"in t&&t.isReactWarning,o?e.props.ref:e.props.ref||e.ref)}var O="Dialog",[M]=ce(O),[Ce,g]=M(O),T=e=>{const{__scopeDialog:t,children:o,open:a,defaultOpen:r,onOpenChange:n,modal:l=!0}=e,c=i.useRef(null),u=i.useRef(null),[d,f]=ie({prop:a,defaultProp:r??!1,onChange:n,caller:O});return s.jsx(Ce,{scope:t,triggerRef:c,contentRef:u,contentId:j(),titleId:j(),descriptionId:j(),open:d,onOpenChange:f,onOpenToggle:i.useCallback(()=>f(m=>!m),[f]),modal:l,children:o})};T.displayName=O;var S="DialogTrigger",ye=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(S,o),n=b(t,r.triggerRef);return s.jsx(h.button,{type:"button","aria-haspopup":"dialog","aria-expanded":r.open,"aria-controls":r.contentId,"data-state":_(r.open),...a,ref:n,onClick:D(e.onClick,r.onOpenToggle)})});ye.displayName=S;var A="DialogPortal",[Re,F]=M(A,{forceMount:void 0}),L=e=>{const{__scopeDialog:t,forceMount:o,children:a,container:r}=e,n=g(A,t);return s.jsx(Re,{scope:t,forceMount:o,children:i.Children.map(a,l=>s.jsx(E,{present:o||n.open,children:s.jsx(le,{asChild:!0,container:r,children:l})}))})};L.displayName=A;var R="DialogOverlay",k=i.forwardRef((e,t)=>{const o=F(R,e.__scopeDialog),{forceMount:a=o.forceMount,...r}=e,n=g(R,e.__scopeDialog);return n.modal?s.jsx(E,{present:a||n.open,children:s.jsx(Ee,{...r,ref:t})}):null});k.displayName=R;var be=pe("DialogOverlay.RemoveScroll"),Ee=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(R,o);return s.jsx(ue,{as:be,allowPinchZoom:!0,shards:[r.contentRef],children:s.jsx(h.div,{"data-state":_(r.open),...a,ref:t,style:{pointerEvents:"auto",...a.style}})})}),v="DialogContent",W=i.forwardRef((e,t)=>{const o=F(v,e.__scopeDialog),{forceMount:a=o.forceMount,...r}=e,n=g(v,e.__scopeDialog);return s.jsx(E,{present:a||n.open,children:n.modal?s.jsx(Oe,{...r,ref:t}):s.jsx(je,{...r,ref:t})})});W.displayName=v;var Oe=i.forwardRef((e,t)=>{const o=g(v,e.__scopeDialog),a=i.useRef(null),r=b(t,o.contentRef,a);return i.useEffect(()=>{const n=a.current;if(n)return de(n)},[]),s.jsx(U,{...e,ref:r,trapFocus:o.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:D(e.onCloseAutoFocus,n=>{var l;n.preventDefault(),(l=o.triggerRef.current)==null||l.focus()}),onPointerDownOutside:D(e.onPointerDownOutside,n=>{const l=n.detail.originalEvent,c=l.button===0&&l.ctrlKey===!0;(l.button===2||c)&&n.preventDefault()}),onFocusOutside:D(e.onFocusOutside,n=>n.preventDefault())})}),je=i.forwardRef((e,t)=>{const o=g(v,e.__scopeDialog),a=i.useRef(!1),r=i.useRef(!1);return s.jsx(U,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:n=>{var l,c;(l=e.onCloseAutoFocus)==null||l.call(e,n),n.defaultPrevented||(a.current||(c=o.triggerRef.current)==null||c.focus(),n.preventDefault()),a.current=!1,r.current=!1},onInteractOutside:n=>{var u,d;(u=e.onInteractOutside)==null||u.call(e,n),n.defaultPrevented||(a.current=!0,n.detail.originalEvent.type==="pointerdown"&&(r.current=!0));const l=n.target;((d=o.triggerRef.current)==null?void 0:d.contains(l))&&n.preventDefault(),n.detail.originalEvent.type==="focusin"&&r.current&&n.preventDefault()}})}),U=i.forwardRef((e,t)=>{const{__scopeDialog:o,trapFocus:a,onOpenAutoFocus:r,onCloseAutoFocus:n,...l}=e,c=g(v,o),u=i.useRef(null),d=b(t,u);return fe(),s.jsxs(s.Fragment,{children:[s.jsx(ge,{asChild:!0,loop:!0,trapped:a,onMountAutoFocus:r,onUnmountAutoFocus:n,children:s.jsx(me,{role:"dialog",id:c.contentId,"aria-describedby":c.descriptionId,"aria-labelledby":c.titleId,"data-state":_(c.open),...l,ref:d,onDismiss:()=>c.onOpenChange(!1)})}),s.jsxs(s.Fragment,{children:[s.jsx(Ae,{titleId:c.titleId}),s.jsx(_e,{contentRef:u,descriptionId:c.descriptionId})]})]})}),P="DialogTitle",$=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(P,o);return s.jsx(h.h2,{id:r.titleId,...a,ref:t})});$.displayName=P;var G="DialogDescription",B=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(G,o);return s.jsx(h.p,{id:r.descriptionId,...a,ref:t})});B.displayName=G;var z="DialogClose",H=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(z,o);return s.jsx(h.button,{type:"button",...a,ref:t,onClick:D(e.onClick,()=>r.onOpenChange(!1))})});H.displayName=z;function _(e){return e?"open":"closed"}var V="DialogTitleWarning",[$e,q]=ve(V,{contentName:v,titleName:P,docsSlug:"dialog"}),Ae=({titleId:e})=>{const t=q(V),o=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
1
+ import{r as i,aj as b,aU as I,ad as ie,j as s,af as j,ai as le,ak as h,al as D,am as ce,an as de,ao as ue,aq as fe,ar as ge,as as me,ap as pe,aV as ve,ax as xe}from"./vendor-C--HHaLf.js";import{c as N,t as x}from"./index-D6_5HaDl.js";import{B as w}from"./page-layout-Ch-H9gD-.js";function Ne(e,t){return i.useReducer((o,a)=>t[o][a]??o,e)}var E=e=>{const{present:t,children:o}=e,a=De(t),r=typeof o=="function"?o({present:a.isPresent}):i.Children.only(o),n=b(a.ref,he(r));return typeof o=="function"||a.isPresent?i.cloneElement(r,{ref:n}):null};E.displayName="Presence";function De(e){const[t,o]=i.useState(),a=i.useRef(null),r=i.useRef(e),n=i.useRef("none"),l=e?"mounted":"unmounted",[c,u]=Ne(l,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return i.useEffect(()=>{const d=y(a.current);n.current=c==="mounted"?d:"none"},[c]),I(()=>{const d=a.current,f=r.current;if(f!==e){const C=n.current,p=y(d);e?u("MOUNT"):p==="none"||(d==null?void 0:d.display)==="none"?u("UNMOUNT"):u(f&&C!==p?"ANIMATION_OUT":"UNMOUNT"),r.current=e}},[e,u]),I(()=>{if(t){let d;const f=t.ownerDocument.defaultView??window,m=p=>{const re=y(a.current).includes(CSS.escape(p.animationName));if(p.target===t&&re&&(u("ANIMATION_END"),!r.current)){const se=t.style.animationFillMode;t.style.animationFillMode="forwards",d=f.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=se)})}},C=p=>{p.target===t&&(n.current=y(a.current))};return t.addEventListener("animationstart",C),t.addEventListener("animationcancel",m),t.addEventListener("animationend",m),()=>{f.clearTimeout(d),t.removeEventListener("animationstart",C),t.removeEventListener("animationcancel",m),t.removeEventListener("animationend",m)}}else u("ANIMATION_END")},[t,u]),{isPresent:["mounted","unmountSuspended"].includes(c),ref:i.useCallback(d=>{a.current=d?getComputedStyle(d):null,o(d)},[])}}function y(e){return(e==null?void 0:e.animationName)||"none"}function he(e){var a,r;let t=(a=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:a.get,o=t&&"isReactWarning"in t&&t.isReactWarning;return o?e.ref:(t=(r=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:r.get,o=t&&"isReactWarning"in t&&t.isReactWarning,o?e.props.ref:e.props.ref||e.ref)}var O="Dialog",[M]=ce(O),[Ce,g]=M(O),T=e=>{const{__scopeDialog:t,children:o,open:a,defaultOpen:r,onOpenChange:n,modal:l=!0}=e,c=i.useRef(null),u=i.useRef(null),[d,f]=ie({prop:a,defaultProp:r??!1,onChange:n,caller:O});return s.jsx(Ce,{scope:t,triggerRef:c,contentRef:u,contentId:j(),titleId:j(),descriptionId:j(),open:d,onOpenChange:f,onOpenToggle:i.useCallback(()=>f(m=>!m),[f]),modal:l,children:o})};T.displayName=O;var S="DialogTrigger",ye=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(S,o),n=b(t,r.triggerRef);return s.jsx(h.button,{type:"button","aria-haspopup":"dialog","aria-expanded":r.open,"aria-controls":r.contentId,"data-state":_(r.open),...a,ref:n,onClick:D(e.onClick,r.onOpenToggle)})});ye.displayName=S;var A="DialogPortal",[Re,F]=M(A,{forceMount:void 0}),L=e=>{const{__scopeDialog:t,forceMount:o,children:a,container:r}=e,n=g(A,t);return s.jsx(Re,{scope:t,forceMount:o,children:i.Children.map(a,l=>s.jsx(E,{present:o||n.open,children:s.jsx(le,{asChild:!0,container:r,children:l})}))})};L.displayName=A;var R="DialogOverlay",k=i.forwardRef((e,t)=>{const o=F(R,e.__scopeDialog),{forceMount:a=o.forceMount,...r}=e,n=g(R,e.__scopeDialog);return n.modal?s.jsx(E,{present:a||n.open,children:s.jsx(Ee,{...r,ref:t})}):null});k.displayName=R;var be=pe("DialogOverlay.RemoveScroll"),Ee=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(R,o);return s.jsx(ue,{as:be,allowPinchZoom:!0,shards:[r.contentRef],children:s.jsx(h.div,{"data-state":_(r.open),...a,ref:t,style:{pointerEvents:"auto",...a.style}})})}),v="DialogContent",W=i.forwardRef((e,t)=>{const o=F(v,e.__scopeDialog),{forceMount:a=o.forceMount,...r}=e,n=g(v,e.__scopeDialog);return s.jsx(E,{present:a||n.open,children:n.modal?s.jsx(Oe,{...r,ref:t}):s.jsx(je,{...r,ref:t})})});W.displayName=v;var Oe=i.forwardRef((e,t)=>{const o=g(v,e.__scopeDialog),a=i.useRef(null),r=b(t,o.contentRef,a);return i.useEffect(()=>{const n=a.current;if(n)return de(n)},[]),s.jsx(U,{...e,ref:r,trapFocus:o.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:D(e.onCloseAutoFocus,n=>{var l;n.preventDefault(),(l=o.triggerRef.current)==null||l.focus()}),onPointerDownOutside:D(e.onPointerDownOutside,n=>{const l=n.detail.originalEvent,c=l.button===0&&l.ctrlKey===!0;(l.button===2||c)&&n.preventDefault()}),onFocusOutside:D(e.onFocusOutside,n=>n.preventDefault())})}),je=i.forwardRef((e,t)=>{const o=g(v,e.__scopeDialog),a=i.useRef(!1),r=i.useRef(!1);return s.jsx(U,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:n=>{var l,c;(l=e.onCloseAutoFocus)==null||l.call(e,n),n.defaultPrevented||(a.current||(c=o.triggerRef.current)==null||c.focus(),n.preventDefault()),a.current=!1,r.current=!1},onInteractOutside:n=>{var u,d;(u=e.onInteractOutside)==null||u.call(e,n),n.defaultPrevented||(a.current=!0,n.detail.originalEvent.type==="pointerdown"&&(r.current=!0));const l=n.target;((d=o.triggerRef.current)==null?void 0:d.contains(l))&&n.preventDefault(),n.detail.originalEvent.type==="focusin"&&r.current&&n.preventDefault()}})}),U=i.forwardRef((e,t)=>{const{__scopeDialog:o,trapFocus:a,onOpenAutoFocus:r,onCloseAutoFocus:n,...l}=e,c=g(v,o),u=i.useRef(null),d=b(t,u);return fe(),s.jsxs(s.Fragment,{children:[s.jsx(ge,{asChild:!0,loop:!0,trapped:a,onMountAutoFocus:r,onUnmountAutoFocus:n,children:s.jsx(me,{role:"dialog",id:c.contentId,"aria-describedby":c.descriptionId,"aria-labelledby":c.titleId,"data-state":_(c.open),...l,ref:d,onDismiss:()=>c.onOpenChange(!1)})}),s.jsxs(s.Fragment,{children:[s.jsx(Ae,{titleId:c.titleId}),s.jsx(_e,{contentRef:u,descriptionId:c.descriptionId})]})]})}),P="DialogTitle",$=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(P,o);return s.jsx(h.h2,{id:r.titleId,...a,ref:t})});$.displayName=P;var G="DialogDescription",B=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(G,o);return s.jsx(h.p,{id:r.descriptionId,...a,ref:t})});B.displayName=G;var z="DialogClose",H=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(z,o);return s.jsx(h.button,{type:"button",...a,ref:t,onClick:D(e.onClick,()=>r.onOpenChange(!1))})});H.displayName=z;function _(e){return e?"open":"closed"}var V="DialogTitleWarning",[$e,q]=ve(V,{contentName:v,titleName:P,docsSlug:"dialog"}),Ae=({titleId:e})=>{const t=q(V),o=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
2
2
 
3
3
  If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component.
4
4
 
package/dist/index.html CHANGED
@@ -6,9 +6,9 @@
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-CTLvVlk8.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-D6_5HaDl.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-C--HHaLf.js">
11
- <link rel="stylesheet" crossorigin href="/assets/index-DI6BuShn.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-BUiahmWm.css">
12
12
  </head>
13
13
 
14
14
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/api/config.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  ProviderConfigUpdate,
10
10
  ProviderConnectionTestRequest,
11
11
  ProviderConnectionTestResult,
12
+ ProviderAuthStartRequest,
12
13
  ProviderAuthStartResult,
13
14
  ProviderAuthPollRequest,
14
15
  ProviderAuthPollResult,
@@ -144,10 +145,13 @@ export async function testProviderConnection(
144
145
  }
145
146
 
146
147
  // POST /api/config/providers/:provider/auth/start
147
- export async function startProviderAuth(provider: string): Promise<ProviderAuthStartResult> {
148
+ export async function startProviderAuth(
149
+ provider: string,
150
+ data: ProviderAuthStartRequest = {}
151
+ ): Promise<ProviderAuthStartResult> {
148
152
  const response = await api.post<ProviderAuthStartResult>(
149
153
  `/api/config/providers/${provider}/auth/start`,
150
- {}
154
+ data
151
155
  );
152
156
  if (!response.ok) {
153
157
  throw new Error(response.error.message);
package/src/api/types.ts CHANGED
@@ -77,6 +77,7 @@ export type ProviderConnectionTestResult = {
77
77
  export type ProviderAuthStartResult = {
78
78
  provider: string;
79
79
  kind: "device_code";
80
+ methodId?: string;
80
81
  sessionId: string;
81
82
  verificationUri: string;
82
83
  userCode: string;
@@ -85,6 +86,10 @@ export type ProviderAuthStartResult = {
85
86
  note?: string;
86
87
  };
87
88
 
89
+ export type ProviderAuthStartRequest = {
90
+ methodId?: string;
91
+ };
92
+
88
93
  export type ProviderAuthPollRequest = {
89
94
  sessionId: string;
90
95
  };
@@ -208,6 +213,24 @@ export type ChatCapabilitiesView = {
208
213
  stopReason?: string;
209
214
  };
210
215
 
216
+ export type ChatCommandOptionView = {
217
+ name: string;
218
+ description: string;
219
+ type: 'string' | 'boolean' | 'number';
220
+ required?: boolean;
221
+ };
222
+
223
+ export type ChatCommandView = {
224
+ name: string;
225
+ description: string;
226
+ options?: ChatCommandOptionView[];
227
+ };
228
+
229
+ export type ChatCommandsView = {
230
+ commands: ChatCommandView[];
231
+ total: number;
232
+ };
233
+
211
234
  export type ChatTurnStopRequest = {
212
235
  runId: string;
213
236
  sessionKey?: string;
@@ -440,6 +463,18 @@ export type ProviderSpecView = {
440
463
  en?: string;
441
464
  zh?: string;
442
465
  };
466
+ methods?: Array<{
467
+ id: string;
468
+ label?: {
469
+ en?: string;
470
+ zh?: string;
471
+ };
472
+ hint?: {
473
+ en?: string;
474
+ zh?: string;
475
+ };
476
+ }>;
477
+ defaultMethodId?: string;
443
478
  supportsCliImport?: boolean;
444
479
  };
445
480
  defaultModels?: string[];
@@ -541,6 +576,7 @@ export type ConfigActionExecuteResult = {
541
576
  export type WsEvent =
542
577
  | { type: 'config.updated'; payload: { path: string } }
543
578
  | { type: 'run.updated'; payload: { run: ChatRunView } }
579
+ | { type: 'session.updated'; payload: { sessionKey: string } }
544
580
  | { type: 'config.reload.started'; payload?: Record<string, unknown> }
545
581
  | { type: 'config.reload.finished'; payload?: Record<string, unknown> }
546
582
  | { type: 'error'; payload: { message: string; code?: string } }
@@ -550,7 +586,9 @@ export type MarketplaceItemType = 'plugin' | 'skill';
550
586
 
551
587
  export type MarketplaceSort = 'relevance' | 'updated';
552
588
 
553
- export type MarketplaceInstallKind = 'npm' | 'clawhub' | 'git' | 'builtin';
589
+ export type MarketplacePluginInstallKind = 'npm';
590
+ export type MarketplaceSkillInstallKind = 'builtin' | 'marketplace';
591
+ export type MarketplaceInstallKind = MarketplacePluginInstallKind | MarketplaceSkillInstallKind;
554
592
 
555
593
  export type MarketplaceInstallSpec = {
556
594
  kind: MarketplaceInstallKind;
@@ -586,7 +624,7 @@ export type MarketplaceSkillContentView = {
586
624
  slug: string;
587
625
  name: string;
588
626
  install: MarketplaceInstallSpec;
589
- source: 'workspace' | 'builtin' | 'git' | 'remote';
627
+ source: 'builtin' | 'marketplace' | 'remote';
590
628
  raw: string;
591
629
  metadataRaw?: string;
592
630
  bodyRaw: string;
@@ -652,8 +690,6 @@ export type MarketplaceInstallRequest = {
652
690
  kind?: MarketplaceInstallKind;
653
691
  skill?: string;
654
692
  installPath?: string;
655
- version?: string;
656
- registry?: string;
657
693
  force?: boolean;
658
694
  };
659
695
 
@@ -178,7 +178,7 @@ type ExecuteStreamRunParams = {
178
178
  onReady: (event: { runId?: string; stopSupported?: boolean; stopReason?: string; sessionKey: string }) => void;
179
179
  onDelta: (event: { delta: string }) => void;
180
180
  onSessionEvent: (event: { data: SessionEventView }) => void;
181
- }) => Promise<{ sessionKey: string }>;
181
+ }) => Promise<{ sessionKey: string; reply: string }>;
182
182
  setters: StreamSetters;
183
183
  };
184
184
 
@@ -226,6 +226,7 @@ async function executeStreamRun(params: ExecuteStreamRunParams): Promise<void> {
226
226
  let streamText = '';
227
227
  try {
228
228
  let hasAssistantSessionEvent = false;
229
+ let hasUserSessionEvent = false;
229
230
  const streamTimestamp = new Date().toISOString();
230
231
  setters.setStreamingAssistantTimestamp(streamTimestamp);
231
232
 
@@ -269,6 +270,7 @@ async function executeStreamRun(params: ExecuteStreamRunParams): Promise<void> {
269
270
  return;
270
271
  }
271
272
  if (event.data.message?.role === 'user') {
273
+ hasUserSessionEvent = true;
272
274
  setters.setOptimisticUserEvent(null);
273
275
  }
274
276
  upsertStreamingEvent(setters.setStreamingSessionEvents, event.data);
@@ -288,7 +290,13 @@ async function executeStreamRun(params: ExecuteStreamRunParams): Promise<void> {
288
290
  setSelectedSessionKey(result.sessionKey);
289
291
  }
290
292
 
291
- const localAssistantText = !hasAssistantSessionEvent ? streamText.trim() : '';
293
+ const finalReply = typeof result.reply === 'string' ? result.reply.trim() : '';
294
+ const localAssistantText = !hasAssistantSessionEvent ? (streamText.trim() || finalReply) : '';
295
+ const isSlashCommandMessage = typeof sourceMessage === 'string' && sourceMessage.trim().startsWith('/');
296
+ const shouldKeepLocalUserCommand =
297
+ !hasUserSessionEvent &&
298
+ optimisticUserEvent?.message?.role === 'user' &&
299
+ isSlashCommandMessage;
292
300
  await refetchIfSessionVisible({
293
301
  selectedSessionKeyRef,
294
302
  currentSessionKey: sourceSessionKey,
@@ -297,7 +305,14 @@ async function executeStreamRun(params: ExecuteStreamRunParams): Promise<void> {
297
305
  refetchHistory
298
306
  });
299
307
 
300
- setters.setStreamingSessionEvents(localAssistantText ? [buildLocalAssistantEvent(localAssistantText)] : []);
308
+ const localEvents: SessionEventView[] = [];
309
+ if (shouldKeepLocalUserCommand && optimisticUserEvent) {
310
+ localEvents.push(optimisticUserEvent);
311
+ }
312
+ if (localAssistantText) {
313
+ localEvents.push(buildLocalAssistantEvent(localAssistantText));
314
+ }
315
+ setters.setStreamingSessionEvents(localEvents);
301
316
 
302
317
  setters.setStreamingAssistantText('');
303
318
  setters.setStreamingAssistantTimestamp(null);
@@ -32,6 +32,15 @@ type ProviderFormProps = {
32
32
  onProviderDeleted?: (providerName: string) => void;
33
33
  };
34
34
 
35
+ type ProviderAuthMethodOption = {
36
+ id: string;
37
+ };
38
+
39
+ type PillSelectOption = {
40
+ value: string;
41
+ label: string;
42
+ };
43
+
35
44
  const EMPTY_PROVIDER_CONFIG: ProviderConfigView = {
36
45
  displayName: '',
37
46
  apiKeySet: false,
@@ -151,6 +160,101 @@ function serializeModelsForSave(models: string[], defaultModels: string[]): stri
151
160
  return models;
152
161
  }
153
162
 
163
+ function resolvePreferredAuthMethodId(params: {
164
+ providerName?: string;
165
+ methods: ProviderAuthMethodOption[];
166
+ defaultMethodId?: string;
167
+ language: 'zh' | 'en';
168
+ }): string {
169
+ const { providerName, methods, defaultMethodId, language } = params;
170
+ if (methods.length === 0) {
171
+ return '';
172
+ }
173
+
174
+ const methodIdMap = new Map<string, string>();
175
+ for (const method of methods) {
176
+ const methodId = method.id.trim();
177
+ if (methodId) {
178
+ methodIdMap.set(methodId.toLowerCase(), methodId);
179
+ }
180
+ }
181
+
182
+ const pick = (...candidates: string[]): string | undefined => {
183
+ for (const candidate of candidates) {
184
+ const resolved = methodIdMap.get(candidate.toLowerCase());
185
+ if (resolved) {
186
+ return resolved;
187
+ }
188
+ }
189
+ return undefined;
190
+ };
191
+
192
+ const normalizedDefault = defaultMethodId?.trim();
193
+ if (providerName === 'minimax-portal') {
194
+ if (language === 'zh') {
195
+ return pick('cn', 'china-mainland') ?? pick(normalizedDefault ?? '') ?? methods[0]?.id ?? '';
196
+ }
197
+ if (language === 'en') {
198
+ return pick('global', 'intl', 'international') ?? pick(normalizedDefault ?? '') ?? methods[0]?.id ?? '';
199
+ }
200
+ }
201
+
202
+ if (normalizedDefault) {
203
+ const matchedDefault = pick(normalizedDefault);
204
+ if (matchedDefault) {
205
+ return matchedDefault;
206
+ }
207
+ }
208
+
209
+ if (language === 'zh') {
210
+ return pick('cn') ?? methods[0]?.id ?? '';
211
+ }
212
+ if (language === 'en') {
213
+ return pick('global') ?? methods[0]?.id ?? '';
214
+ }
215
+
216
+ return methods[0]?.id ?? '';
217
+ }
218
+
219
+ function shouldUsePillSelector(params: {
220
+ required: boolean;
221
+ hasDefault: boolean;
222
+ optionCount: number;
223
+ }): boolean {
224
+ return params.required && params.hasDefault && params.optionCount > 1 && params.optionCount <= 3;
225
+ }
226
+
227
+ function PillSelector(props: {
228
+ value: string;
229
+ onChange: (value: string) => void;
230
+ options: PillSelectOption[];
231
+ }) {
232
+ const { value, onChange, options } = props;
233
+
234
+ return (
235
+ <div className="flex flex-wrap gap-2">
236
+ {options.map((option) => {
237
+ const selected = option.value === value;
238
+ return (
239
+ <button
240
+ key={option.value}
241
+ type="button"
242
+ onClick={() => onChange(option.value)}
243
+ aria-pressed={selected}
244
+ className={`rounded-full border px-3 py-1.5 text-xs font-medium transition-colors ${
245
+ selected
246
+ ? 'border-primary bg-primary text-white shadow-sm'
247
+ : 'border-gray-200 bg-white text-gray-700 hover:border-primary/40 hover:text-primary'
248
+ }`}
249
+ >
250
+ {option.label}
251
+ </button>
252
+ );
253
+ })}
254
+ </div>
255
+ );
256
+ }
257
+
154
258
  export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormProps) {
155
259
  const queryClient = useQueryClient();
156
260
  const { data: config } = useConfig();
@@ -174,6 +278,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
174
278
  const [showModelInput, setShowModelInput] = useState(false);
175
279
  const [authSessionId, setAuthSessionId] = useState<string | null>(null);
176
280
  const [authStatusMessage, setAuthStatusMessage] = useState('');
281
+ const [authMethodId, setAuthMethodId] = useState('');
177
282
  const authPollTimerRef = useRef<number | null>(null);
178
283
 
179
284
  const providerSpec = meta?.providers.find((p) => p.name === providerName);
@@ -225,11 +330,63 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
225
330
  apiBaseHint?.help ||
226
331
  t('providerApiBaseHelp');
227
332
  const providerAuth = providerSpec?.auth;
333
+ const providerAuthMethods = useMemo(
334
+ () => providerAuth?.methods ?? [],
335
+ [providerAuth?.methods]
336
+ );
337
+ const providerAuthMethodOptions = useMemo(
338
+ () =>
339
+ providerAuthMethods.map((method) => ({
340
+ value: method.id,
341
+ label: method.label?.[language] || method.label?.en || method.id
342
+ })),
343
+ [providerAuthMethods, language]
344
+ );
345
+ const preferredAuthMethodId = useMemo(
346
+ () => resolvePreferredAuthMethodId({
347
+ providerName,
348
+ methods: providerAuthMethods,
349
+ defaultMethodId: providerAuth?.defaultMethodId,
350
+ language
351
+ }),
352
+ [providerName, providerAuth?.defaultMethodId, providerAuthMethods, language]
353
+ );
354
+ const resolvedAuthMethodId = useMemo(() => {
355
+ if (!providerAuthMethods.length) {
356
+ return '';
357
+ }
358
+ const normalizedCurrent = authMethodId.trim();
359
+ if (normalizedCurrent && providerAuthMethods.some((method) => method.id === normalizedCurrent)) {
360
+ return normalizedCurrent;
361
+ }
362
+ return preferredAuthMethodId || providerAuthMethods[0]?.id || '';
363
+ }, [authMethodId, preferredAuthMethodId, providerAuthMethods]);
364
+ const selectedAuthMethod = useMemo(
365
+ () => providerAuthMethods.find((method) => method.id === resolvedAuthMethodId),
366
+ [providerAuthMethods, resolvedAuthMethodId]
367
+ );
368
+ const selectedAuthMethodHint =
369
+ selectedAuthMethod?.hint?.[language] || selectedAuthMethod?.hint?.en || '';
370
+ const shouldUseAuthMethodPills = shouldUsePillSelector({
371
+ required: providerAuth?.kind === 'device_code',
372
+ hasDefault: Boolean(providerAuth?.defaultMethodId?.trim()),
373
+ optionCount: providerAuthMethods.length
374
+ });
228
375
  const providerAuthNote =
229
376
  providerAuth?.note?.[language] ||
230
377
  providerAuth?.note?.en ||
231
378
  providerAuth?.displayName ||
232
379
  '';
380
+ const wireApiOptions = providerSpec?.wireApiOptions || ['auto', 'chat', 'responses'];
381
+ const wireApiSelectOptions: PillSelectOption[] = wireApiOptions.map((option) => ({
382
+ value: option,
383
+ label: option === 'chat' ? t('wireApiChat') : option === 'responses' ? t('wireApiResponses') : t('wireApiAuto')
384
+ }));
385
+ const shouldUseWireApiPills = shouldUsePillSelector({
386
+ required: Boolean(providerSpec?.supportsWireApi),
387
+ hasDefault: typeof providerSpec?.defaultWireApi === 'string' && providerSpec.defaultWireApi.length > 0,
388
+ optionCount: wireApiSelectOptions.length
389
+ });
233
390
 
234
391
  const clearAuthPollTimer = useCallback(() => {
235
392
  if (authPollTimerRef.current !== null) {
@@ -290,6 +447,7 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
290
447
  setProviderDisplayName('');
291
448
  setAuthSessionId(null);
292
449
  setAuthStatusMessage('');
450
+ setAuthMethodId('');
293
451
  clearAuthPollTimer();
294
452
  return;
295
453
  }
@@ -303,8 +461,18 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
303
461
  setProviderDisplayName(effectiveDisplayName);
304
462
  setAuthSessionId(null);
305
463
  setAuthStatusMessage('');
464
+ setAuthMethodId(preferredAuthMethodId);
306
465
  clearAuthPollTimer();
307
- }, [providerName, currentApiBase, resolvedProviderConfig.extraHeaders, currentWireApi, currentEditableModels, effectiveDisplayName, clearAuthPollTimer]);
466
+ }, [
467
+ providerName,
468
+ currentApiBase,
469
+ resolvedProviderConfig.extraHeaders,
470
+ currentWireApi,
471
+ currentEditableModels,
472
+ effectiveDisplayName,
473
+ preferredAuthMethodId,
474
+ clearAuthPollTimer
475
+ ]);
308
476
 
309
477
  useEffect(() => () => clearAuthPollTimer(), [clearAuthPollTimer]);
310
478
 
@@ -453,7 +621,10 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
453
621
 
454
622
  try {
455
623
  setAuthStatusMessage('');
456
- const result = await startProviderAuth.mutateAsync({ provider: providerName });
624
+ const result = await startProviderAuth.mutateAsync({
625
+ provider: providerName,
626
+ data: resolvedAuthMethodId ? { methodId: resolvedAuthMethodId } : {}
627
+ });
457
628
  if (!result.sessionId || !result.verificationUri) {
458
629
  throw new Error(t('providerAuthStartFailed'));
459
630
  }
@@ -567,6 +738,34 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
567
738
  {providerAuthNote ? (
568
739
  <p className="text-xs text-gray-600">{providerAuthNote}</p>
569
740
  ) : null}
741
+ {providerAuthMethods.length > 1 ? (
742
+ <div className="space-y-2">
743
+ <Label className="text-xs font-medium text-gray-700">{t('providerAuthMethodLabel')}</Label>
744
+ {shouldUseAuthMethodPills ? (
745
+ <PillSelector
746
+ value={resolvedAuthMethodId}
747
+ onChange={setAuthMethodId}
748
+ options={providerAuthMethodOptions}
749
+ />
750
+ ) : (
751
+ <Select value={resolvedAuthMethodId} onValueChange={setAuthMethodId}>
752
+ <SelectTrigger className="h-8 rounded-lg bg-white">
753
+ <SelectValue placeholder={t('providerAuthMethodPlaceholder')} />
754
+ </SelectTrigger>
755
+ <SelectContent>
756
+ {providerAuthMethodOptions.map((method) => (
757
+ <SelectItem key={method.value} value={method.value}>
758
+ {method.label}
759
+ </SelectItem>
760
+ ))}
761
+ </SelectContent>
762
+ </Select>
763
+ )}
764
+ {selectedAuthMethodHint ? (
765
+ <p className="text-xs text-gray-500">{selectedAuthMethodHint}</p>
766
+ ) : null}
767
+ </div>
768
+ ) : null}
570
769
  <div className="flex flex-wrap items-center gap-2">
571
770
  <Button
572
771
  type="button"
@@ -718,18 +917,26 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
718
917
  <Label htmlFor="wireApi" className="text-sm font-medium text-gray-900">
719
918
  {wireApiHint?.label ?? t('wireApi')}
720
919
  </Label>
721
- <Select value={wireApi} onValueChange={(v) => setWireApi(v as WireApiType)}>
722
- <SelectTrigger className="rounded-xl">
723
- <SelectValue />
724
- </SelectTrigger>
725
- <SelectContent>
726
- {(providerSpec.wireApiOptions || ['auto', 'chat', 'responses']).map((option) => (
727
- <SelectItem key={option} value={option}>
728
- {option === 'chat' ? t('wireApiChat') : option === 'responses' ? t('wireApiResponses') : t('wireApiAuto')}
729
- </SelectItem>
730
- ))}
731
- </SelectContent>
732
- </Select>
920
+ {shouldUseWireApiPills ? (
921
+ <PillSelector
922
+ value={wireApi}
923
+ onChange={(v) => setWireApi(v as WireApiType)}
924
+ options={wireApiSelectOptions}
925
+ />
926
+ ) : (
927
+ <Select value={wireApi} onValueChange={(v) => setWireApi(v as WireApiType)}>
928
+ <SelectTrigger className="rounded-xl">
929
+ <SelectValue />
930
+ </SelectTrigger>
931
+ <SelectContent>
932
+ {wireApiSelectOptions.map((option) => (
933
+ <SelectItem key={option.value} value={option.value}>
934
+ {option.label}
935
+ </SelectItem>
936
+ ))}
937
+ </SelectContent>
938
+ </Select>
939
+ )}
733
940
  </div>
734
941
  )}
735
942
 
@@ -824,7 +824,7 @@ export function MarketplacePage(props: MarketplacePageProps = {}) {
824
824
  };
825
825
 
826
826
  return (
827
- <PageLayout>
827
+ <PageLayout className="flex h-full min-h-0 flex-col pb-0">
828
828
  <PageHeader title={t(copyKeys.pageTitle)} description={t(copyKeys.pageDescription)} />
829
829
 
830
830
  <Tabs
@@ -849,7 +849,7 @@ export function MarketplacePage(props: MarketplacePageProps = {}) {
849
849
  }}
850
850
  />
851
851
 
852
- <section>
852
+ <section className="flex min-h-0 flex-1 flex-col">
853
853
  <div className="flex items-center justify-between mb-3">
854
854
  <h3 className="text-[14px] font-semibold text-gray-900">
855
855
  {scope === 'installed' ? t(copyKeys.sectionInstalled) : t(copyKeys.sectionCatalog)}
@@ -868,52 +868,56 @@ export function MarketplacePage(props: MarketplacePageProps = {}) {
868
868
  </div>
869
869
  )}
870
870
 
871
- <div className="grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-3">
872
- {scope === 'all' && allItems.map((item) => (
873
- <MarketplaceListCard
874
- key={item.id}
875
- item={item}
876
- record={findInstalledRecordForItem(item, installedRecordLookup)}
877
- localeFallbacks={localeFallbacks}
878
- installState={installState}
879
- manageState={manageState}
880
- onOpen={() => void openItemDetail(item, findInstalledRecordForItem(item, installedRecordLookup))}
881
- onInstall={handleInstall}
882
- onManage={handleManage}
883
- />
884
- ))}
885
-
886
- {scope === 'installed' && installedEntries.map((entry) => (
887
- <MarketplaceListCard
888
- key={entry.key}
889
- item={entry.item}
890
- record={entry.record}
891
- localeFallbacks={localeFallbacks}
892
- installState={installState}
893
- manageState={manageState}
894
- onOpen={() => void openItemDetail(entry.item, entry.record)}
895
- onInstall={handleInstall}
896
- onManage={handleManage}
897
- />
898
- ))}
899
- </div>
871
+ <div className="min-h-0 flex-1 overflow-y-auto custom-scrollbar pr-1">
872
+ <div className="grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-3">
873
+ {scope === 'all' && allItems.map((item) => (
874
+ <MarketplaceListCard
875
+ key={item.id}
876
+ item={item}
877
+ record={findInstalledRecordForItem(item, installedRecordLookup)}
878
+ localeFallbacks={localeFallbacks}
879
+ installState={installState}
880
+ manageState={manageState}
881
+ onOpen={() => void openItemDetail(item, findInstalledRecordForItem(item, installedRecordLookup))}
882
+ onInstall={handleInstall}
883
+ onManage={handleManage}
884
+ />
885
+ ))}
886
+
887
+ {scope === 'installed' && installedEntries.map((entry) => (
888
+ <MarketplaceListCard
889
+ key={entry.key}
890
+ item={entry.item}
891
+ record={entry.record}
892
+ localeFallbacks={localeFallbacks}
893
+ installState={installState}
894
+ manageState={manageState}
895
+ onOpen={() => void openItemDetail(entry.item, entry.record)}
896
+ onInstall={handleInstall}
897
+ onManage={handleManage}
898
+ />
899
+ ))}
900
+ </div>
900
901
 
901
- {scope === 'all' && !itemsQuery.isLoading && !itemsQuery.isError && allItems.length === 0 && (
902
- <div className="text-[13px] text-gray-500 py-8 text-center">{t(copyKeys.emptyData)}</div>
903
- )}
904
- {scope === 'installed' && !installedQuery.isLoading && !installedQuery.isError && installedEntries.length === 0 && (
905
- <div className="text-[13px] text-gray-500 py-8 text-center">{t(copyKeys.emptyInstalled)}</div>
906
- )}
902
+ {scope === 'all' && !itemsQuery.isLoading && !itemsQuery.isError && allItems.length === 0 && (
903
+ <div className="text-[13px] text-gray-500 py-8 text-center">{t(copyKeys.emptyData)}</div>
904
+ )}
905
+ {scope === 'installed' && !installedQuery.isLoading && !installedQuery.isError && installedEntries.length === 0 && (
906
+ <div className="text-[13px] text-gray-500 py-8 text-center">{t(copyKeys.emptyInstalled)}</div>
907
+ )}
908
+ </div>
907
909
  </section>
908
910
 
909
911
  {scope === 'all' && (
910
- <PaginationBar
911
- page={page}
912
- totalPages={totalPages}
913
- busy={itemsQuery.isFetching}
914
- onPrev={() => setPage((current) => Math.max(1, current - 1))}
915
- onNext={() => setPage((current) => (totalPages > 0 ? Math.min(totalPages, current + 1) : current + 1))}
916
- />
912
+ <div className="shrink-0">
913
+ <PaginationBar
914
+ page={page}
915
+ totalPages={totalPages}
916
+ busy={itemsQuery.isFetching}
917
+ onPrev={() => setPage((current) => Math.max(1, current - 1))}
918
+ onNext={() => setPage((current) => (totalPages > 0 ? Math.min(totalPages, current + 1) : current + 1))}
919
+ />
920
+ </div>
917
921
  )}
918
922
  <ConfirmDialog />
919
923
  </PageLayout>
@@ -139,7 +139,8 @@ export function useTestProviderConnection() {
139
139
 
140
140
  export function useStartProviderAuth() {
141
141
  return useMutation({
142
- mutationFn: ({ provider }: { provider: string }) => startProviderAuth(provider)
142
+ mutationFn: ({ provider, data }: { provider: string; data?: unknown }) =>
143
+ startProviderAuth(provider, data as Parameters<typeof startProviderAuth>[1])
143
144
  });
144
145
  }
145
146