@nextclaw/ui 0.9.3 → 0.9.4

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 (36) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/assets/{ChannelsList-ZBPiF0y2.js → ChannelsList-DDfZIiJa.js} +1 -1
  3. package/dist/assets/{ChatPage-BOgoolWK.js → ChatPage-FpRraTxm.js} +1 -1
  4. package/dist/assets/{DocBrowser-BUYNHg0Y.js → DocBrowser-Kndx8OJj.js} +1 -1
  5. package/dist/assets/{LogoBadge-DXPq99LJ.js → LogoBadge-hKHoLH9n.js} +1 -1
  6. package/dist/assets/{MarketplacePage-Dx7nexYN.js → MarketplacePage-CZIJyfjK.js} +1 -1
  7. package/dist/assets/{McpMarketplacePage-064wdotP.js → McpMarketplacePage-BGrAMA37.js} +1 -1
  8. package/dist/assets/{ModelConfig-BDIfLesG.js → ModelConfig-BpKQeGfb.js} +1 -1
  9. package/dist/assets/{ProvidersList-DrlIr46m.js → ProvidersList-qfUL6mrW.js} +1 -1
  10. package/dist/assets/RemoteAccessPage-BQuMsngI.js +1 -0
  11. package/dist/assets/{RuntimeConfig-BPxXEGzM.js → RuntimeConfig-CVlqNWKO.js} +1 -1
  12. package/dist/assets/{SearchConfig-BIqnlpne.js → SearchConfig-DXFV6Mvx.js} +1 -1
  13. package/dist/assets/{SecretsConfig-jKZEVF2q.js → SecretsConfig-BGW9aUqv.js} +1 -1
  14. package/dist/assets/{SessionsConfig-C_FXgVe1.js → SessionsConfig-BByfa1ke.js} +1 -1
  15. package/dist/assets/{chat-message-DmzpZJc_.js → chat-message-ZwnDwDuQ.js} +1 -1
  16. package/dist/assets/index-BWvap_iq.js +8 -0
  17. package/dist/assets/index-COrhpAdh.css +1 -0
  18. package/dist/assets/{label-B1MloEtn.js → label-Bklr3fXc.js} +1 -1
  19. package/dist/assets/{page-layout-BGg1EhM5.js → page-layout-sNhcbwtm.js} +1 -1
  20. package/dist/assets/{popover-jJMv74Fp.js → popover-C3rJrJJG.js} +1 -1
  21. package/dist/assets/{security-config-Boh9NIMz.js → security-config-BueosYw1.js} +1 -1
  22. package/dist/assets/{skeleton-CmATs_b3.js → skeleton-CiG6msbm.js} +1 -1
  23. package/dist/assets/{status-dot-DNyCdxPZ.js → status-dot-CsIV5YrS.js} +1 -1
  24. package/dist/assets/{switch-DE_MYk7x.js → switch-DSdHSIsC.js} +1 -1
  25. package/dist/assets/{tabs-custom-B-zErYPr.js → tabs-custom-BB-VjdL2.js} +1 -1
  26. package/dist/assets/{useConfirmDialog-BqQ6QfhB.js → useConfirmDialog-BL5s8KDC.js} +1 -1
  27. package/dist/index.html +2 -2
  28. package/package.json +4 -4
  29. package/src/api/remote.ts +20 -0
  30. package/src/api/remote.types.ts +24 -0
  31. package/src/components/remote/RemoteAccessPage.tsx +120 -44
  32. package/src/hooks/useRemoteAccess.ts +28 -0
  33. package/src/lib/i18n.remote.ts +29 -2
  34. package/dist/assets/RemoteAccessPage-ZkUBA-Av.js +0 -1
  35. package/dist/assets/index-Byfw276e.js +0 -8
  36. package/dist/assets/index-bhNuQis7.css +0 -1
@@ -1 +1 @@
1
- import{r as c,j as e,e as M}from"./vendor-CwsIoNvJ.js";import{a6 as O,a7 as R,a8 as I,a9 as B,aa as _,C as u,a3 as h,a4 as x,t as s,a5 as m,z as p,B as j,I as l}from"./index-Byfw276e.js";import{L as o}from"./label-B1MloEtn.js";import{S as z}from"./switch-DE_MYk7x.js";import{P as G,a as V}from"./page-layout-BGg1EhM5.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-CwsIoNvJ.js";import{a6 as O,a7 as R,a8 as I,a9 as B,aa as _,C as u,a3 as h,a4 as x,t as s,a5 as m,z as p,B as j,I as l}from"./index-BWvap_iq.js";import{L as o}from"./label-Bklr3fXc.js";import{S as z}from"./switch-DSdHSIsC.js";import{P as G,a as V}from"./page-layout-sNhcbwtm.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-CwsIoNvJ.js";import{c as o}from"./index-Byfw276e.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-CwsIoNvJ.js";import{c as o}from"./index-BWvap_iq.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-CwsIoNvJ.js";import{c as a}from"./index-Byfw276e.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-CwsIoNvJ.js";import{c as a}from"./index-BWvap_iq.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-CwsIoNvJ.js";import{c as t}from"./index-Byfw276e.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-CwsIoNvJ.js";import{c as t}from"./index-BWvap_iq.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-CwsIoNvJ.js";import{ag as m,c as a}from"./index-Byfw276e.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-CwsIoNvJ.js";import{ag as m,c as a}from"./index-BWvap_iq.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,4 +1,4 @@
1
- import{r as i,aA as ee,j as a,aC as b,aI as te,aE as D,aF as v,aJ as oe,aD as N,aK as ae,aL as ne,aN as re,aO as se,aP as ie,aM as le,b5 as ce,ax as de}from"./vendor-CwsIoNvJ.js";import{P as R}from"./index-Ct7FQpxN.js";import{c as m,B as P,t as p}from"./index-Byfw276e.js";var C="Dialog",[O]=oe(C),[ue,u]=O(C),I=e=>{const{__scopeDialog:o,children:n,open:r,defaultOpen:s,onOpenChange:t,modal:l=!0}=e,c=i.useRef(null),d=i.useRef(null),[f,x]=ee({prop:r,defaultProp:s??!1,onChange:t,caller:C});return a.jsx(ue,{scope:o,triggerRef:c,contentRef:d,contentId:b(),titleId:b(),descriptionId:b(),open:f,onOpenChange:x,onOpenToggle:i.useCallback(()=>x(y=>!y),[x]),modal:l,children:n})};I.displayName=C;var w="DialogTrigger",fe=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(w,n),t=N(o,s.triggerRef);return a.jsx(D.button,{type:"button","aria-haspopup":"dialog","aria-expanded":s.open,"aria-controls":s.contentId,"data-state":E(s.open),...r,ref:t,onClick:v(e.onClick,s.onOpenToggle)})});fe.displayName=w;var j="DialogPortal",[ge,A]=O(j,{forceMount:void 0}),T=e=>{const{__scopeDialog:o,forceMount:n,children:r,container:s}=e,t=u(j,o);return a.jsx(ge,{scope:o,forceMount:n,children:i.Children.map(r,l=>a.jsx(R,{present:n||t.open,children:a.jsx(te,{asChild:!0,container:s,children:l})}))})};T.displayName=j;var h="DialogOverlay",M=i.forwardRef((e,o)=>{const n=A(h,e.__scopeDialog),{forceMount:r=n.forceMount,...s}=e,t=u(h,e.__scopeDialog);return t.modal?a.jsx(R,{present:r||t.open,children:a.jsx(me,{...s,ref:o})}):null});M.displayName=h;var pe=le("DialogOverlay.RemoveScroll"),me=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(h,n);return a.jsx(ne,{as:pe,allowPinchZoom:!0,shards:[s.contentRef],children:a.jsx(D.div,{"data-state":E(s.open),...r,ref:o,style:{pointerEvents:"auto",...r.style}})})}),g="DialogContent",F=i.forwardRef((e,o)=>{const n=A(g,e.__scopeDialog),{forceMount:r=n.forceMount,...s}=e,t=u(g,e.__scopeDialog);return a.jsx(R,{present:r||t.open,children:t.modal?a.jsx(xe,{...s,ref:o}):a.jsx(ve,{...s,ref:o})})});F.displayName=g;var xe=i.forwardRef((e,o)=>{const n=u(g,e.__scopeDialog),r=i.useRef(null),s=N(o,n.contentRef,r);return i.useEffect(()=>{const t=r.current;if(t)return ae(t)},[]),a.jsx(L,{...e,ref:s,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:v(e.onCloseAutoFocus,t=>{var l;t.preventDefault(),(l=n.triggerRef.current)==null||l.focus()}),onPointerDownOutside:v(e.onPointerDownOutside,t=>{const l=t.detail.originalEvent,c=l.button===0&&l.ctrlKey===!0;(l.button===2||c)&&t.preventDefault()}),onFocusOutside:v(e.onFocusOutside,t=>t.preventDefault())})}),ve=i.forwardRef((e,o)=>{const n=u(g,e.__scopeDialog),r=i.useRef(!1),s=i.useRef(!1);return a.jsx(L,{...e,ref:o,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{var l,c;(l=e.onCloseAutoFocus)==null||l.call(e,t),t.defaultPrevented||(r.current||(c=n.triggerRef.current)==null||c.focus(),t.preventDefault()),r.current=!1,s.current=!1},onInteractOutside:t=>{var d,f;(d=e.onInteractOutside)==null||d.call(e,t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type==="pointerdown"&&(s.current=!0));const l=t.target;((f=n.triggerRef.current)==null?void 0:f.contains(l))&&t.preventDefault(),t.detail.originalEvent.type==="focusin"&&s.current&&t.preventDefault()}})}),L=i.forwardRef((e,o)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:s,onCloseAutoFocus:t,...l}=e,c=u(g,n),d=i.useRef(null),f=N(o,d);return re(),a.jsxs(a.Fragment,{children:[a.jsx(se,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:s,onUnmountAutoFocus:t,children:a.jsx(ie,{role:"dialog",id:c.contentId,"aria-describedby":c.descriptionId,"aria-labelledby":c.titleId,"data-state":E(c.open),...l,ref:f,onDismiss:()=>c.onOpenChange(!1)})}),a.jsxs(a.Fragment,{children:[a.jsx(De,{titleId:c.titleId}),a.jsx(Ce,{contentRef:d,descriptionId:c.descriptionId})]})]})}),_="DialogTitle",S=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(_,n);return a.jsx(D.h2,{id:s.titleId,...r,ref:o})});S.displayName=_;var k="DialogDescription",W=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(k,n);return a.jsx(D.p,{id:s.descriptionId,...r,ref:o})});W.displayName=k;var $="DialogClose",G=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u($,n);return a.jsx(D.button,{type:"button",...r,ref:o,onClick:v(e.onClick,()=>s.onOpenChange(!1))})});G.displayName=$;function E(e){return e?"open":"closed"}var B="DialogTitleWarning",[we,z]=ce(B,{contentName:g,titleName:_,docsSlug:"dialog"}),De=({titleId:e})=>{const o=z(B),n=`\`${o.contentName}\` requires a \`${o.titleName}\` for the component to be accessible for screen reader users.
1
+ import{r as i,aA as ee,j as a,aC as b,aI as te,aE as D,aF as v,aJ as oe,aD as N,aK as ae,aL as ne,aN as re,aO as se,aP as ie,aM as le,b5 as ce,ax as de}from"./vendor-CwsIoNvJ.js";import{P as R}from"./index-Ct7FQpxN.js";import{c as m,B as P,t as p}from"./index-BWvap_iq.js";var C="Dialog",[O]=oe(C),[ue,u]=O(C),I=e=>{const{__scopeDialog:o,children:n,open:r,defaultOpen:s,onOpenChange:t,modal:l=!0}=e,c=i.useRef(null),d=i.useRef(null),[f,x]=ee({prop:r,defaultProp:s??!1,onChange:t,caller:C});return a.jsx(ue,{scope:o,triggerRef:c,contentRef:d,contentId:b(),titleId:b(),descriptionId:b(),open:f,onOpenChange:x,onOpenToggle:i.useCallback(()=>x(y=>!y),[x]),modal:l,children:n})};I.displayName=C;var w="DialogTrigger",fe=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(w,n),t=N(o,s.triggerRef);return a.jsx(D.button,{type:"button","aria-haspopup":"dialog","aria-expanded":s.open,"aria-controls":s.contentId,"data-state":E(s.open),...r,ref:t,onClick:v(e.onClick,s.onOpenToggle)})});fe.displayName=w;var j="DialogPortal",[ge,A]=O(j,{forceMount:void 0}),T=e=>{const{__scopeDialog:o,forceMount:n,children:r,container:s}=e,t=u(j,o);return a.jsx(ge,{scope:o,forceMount:n,children:i.Children.map(r,l=>a.jsx(R,{present:n||t.open,children:a.jsx(te,{asChild:!0,container:s,children:l})}))})};T.displayName=j;var h="DialogOverlay",M=i.forwardRef((e,o)=>{const n=A(h,e.__scopeDialog),{forceMount:r=n.forceMount,...s}=e,t=u(h,e.__scopeDialog);return t.modal?a.jsx(R,{present:r||t.open,children:a.jsx(me,{...s,ref:o})}):null});M.displayName=h;var pe=le("DialogOverlay.RemoveScroll"),me=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(h,n);return a.jsx(ne,{as:pe,allowPinchZoom:!0,shards:[s.contentRef],children:a.jsx(D.div,{"data-state":E(s.open),...r,ref:o,style:{pointerEvents:"auto",...r.style}})})}),g="DialogContent",F=i.forwardRef((e,o)=>{const n=A(g,e.__scopeDialog),{forceMount:r=n.forceMount,...s}=e,t=u(g,e.__scopeDialog);return a.jsx(R,{present:r||t.open,children:t.modal?a.jsx(xe,{...s,ref:o}):a.jsx(ve,{...s,ref:o})})});F.displayName=g;var xe=i.forwardRef((e,o)=>{const n=u(g,e.__scopeDialog),r=i.useRef(null),s=N(o,n.contentRef,r);return i.useEffect(()=>{const t=r.current;if(t)return ae(t)},[]),a.jsx(L,{...e,ref:s,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:v(e.onCloseAutoFocus,t=>{var l;t.preventDefault(),(l=n.triggerRef.current)==null||l.focus()}),onPointerDownOutside:v(e.onPointerDownOutside,t=>{const l=t.detail.originalEvent,c=l.button===0&&l.ctrlKey===!0;(l.button===2||c)&&t.preventDefault()}),onFocusOutside:v(e.onFocusOutside,t=>t.preventDefault())})}),ve=i.forwardRef((e,o)=>{const n=u(g,e.__scopeDialog),r=i.useRef(!1),s=i.useRef(!1);return a.jsx(L,{...e,ref:o,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{var l,c;(l=e.onCloseAutoFocus)==null||l.call(e,t),t.defaultPrevented||(r.current||(c=n.triggerRef.current)==null||c.focus(),t.preventDefault()),r.current=!1,s.current=!1},onInteractOutside:t=>{var d,f;(d=e.onInteractOutside)==null||d.call(e,t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type==="pointerdown"&&(s.current=!0));const l=t.target;((f=n.triggerRef.current)==null?void 0:f.contains(l))&&t.preventDefault(),t.detail.originalEvent.type==="focusin"&&s.current&&t.preventDefault()}})}),L=i.forwardRef((e,o)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:s,onCloseAutoFocus:t,...l}=e,c=u(g,n),d=i.useRef(null),f=N(o,d);return re(),a.jsxs(a.Fragment,{children:[a.jsx(se,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:s,onUnmountAutoFocus:t,children:a.jsx(ie,{role:"dialog",id:c.contentId,"aria-describedby":c.descriptionId,"aria-labelledby":c.titleId,"data-state":E(c.open),...l,ref:f,onDismiss:()=>c.onOpenChange(!1)})}),a.jsxs(a.Fragment,{children:[a.jsx(De,{titleId:c.titleId}),a.jsx(Ce,{contentRef:d,descriptionId:c.descriptionId})]})]})}),_="DialogTitle",S=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(_,n);return a.jsx(D.h2,{id:s.titleId,...r,ref:o})});S.displayName=_;var k="DialogDescription",W=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u(k,n);return a.jsx(D.p,{id:s.descriptionId,...r,ref:o})});W.displayName=k;var $="DialogClose",G=i.forwardRef((e,o)=>{const{__scopeDialog:n,...r}=e,s=u($,n);return a.jsx(D.button,{type:"button",...r,ref:o,onClick:v(e.onClick,()=>s.onOpenChange(!1))})});G.displayName=$;function E(e){return e?"open":"closed"}var B="DialogTitleWarning",[we,z]=ce(B,{contentName:g,titleName:_,docsSlug:"dialog"}),De=({titleId:e})=>{const o=z(B),n=`\`${o.contentName}\` requires a \`${o.titleName}\` for the component to be accessible for screen reader users.
2
2
 
3
3
  If you want to hide the \`${o.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-Byfw276e.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-BWvap_iq.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-CwsIoNvJ.js">
11
- <link rel="stylesheet" crossorigin href="/assets/index-bhNuQis7.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-COrhpAdh.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.9.3",
3
+ "version": "0.9.4",
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/ncp-react": "0.3.2",
31
30
  "@nextclaw/agent-chat-ui": "0.2.1",
32
- "@nextclaw/agent-chat": "0.1.1",
33
31
  "@nextclaw/ncp-http-agent-client": "0.3.1",
34
- "@nextclaw/ncp": "0.3.1"
32
+ "@nextclaw/ncp": "0.3.1",
33
+ "@nextclaw/ncp-react": "0.3.2",
34
+ "@nextclaw/agent-chat": "0.1.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@testing-library/react": "^16.3.0",
package/src/api/remote.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { api } from './client';
2
2
  import type {
3
3
  RemoteAccessView,
4
+ RemoteBrowserAuthPollRequest,
5
+ RemoteBrowserAuthPollResult,
6
+ RemoteBrowserAuthStartRequest,
7
+ RemoteBrowserAuthStartResult,
4
8
  RemoteDoctorView,
5
9
  RemoteLoginRequest,
6
10
  RemoteServiceAction,
@@ -32,6 +36,22 @@ export async function loginRemote(data: RemoteLoginRequest): Promise<RemoteAcces
32
36
  return response.data;
33
37
  }
34
38
 
39
+ export async function startRemoteBrowserAuth(data: RemoteBrowserAuthStartRequest): Promise<RemoteBrowserAuthStartResult> {
40
+ const response = await api.post<RemoteBrowserAuthStartResult>('/api/remote/auth/start', data);
41
+ if (!response.ok) {
42
+ throw new Error(response.error.message);
43
+ }
44
+ return response.data;
45
+ }
46
+
47
+ export async function pollRemoteBrowserAuth(data: RemoteBrowserAuthPollRequest): Promise<RemoteBrowserAuthPollResult> {
48
+ const response = await api.post<RemoteBrowserAuthPollResult>('/api/remote/auth/poll', data);
49
+ if (!response.ok) {
50
+ throw new Error(response.error.message);
51
+ }
52
+ return response.data;
53
+ }
54
+
35
55
  export async function logoutRemote(): Promise<RemoteAccessView> {
36
56
  const response = await api.post<RemoteAccessView>('/api/remote/logout', {});
37
57
  if (!response.ok) {
@@ -65,6 +65,30 @@ export type RemoteLoginRequest = {
65
65
  register?: boolean;
66
66
  };
67
67
 
68
+ export type RemoteBrowserAuthStartRequest = {
69
+ apiBase?: string;
70
+ };
71
+
72
+ export type RemoteBrowserAuthStartResult = {
73
+ sessionId: string;
74
+ verificationUri: string;
75
+ expiresAt: string;
76
+ intervalMs: number;
77
+ };
78
+
79
+ export type RemoteBrowserAuthPollRequest = {
80
+ sessionId: string;
81
+ apiBase?: string;
82
+ };
83
+
84
+ export type RemoteBrowserAuthPollResult = {
85
+ status: "pending" | "authorized" | "expired";
86
+ message?: string;
87
+ nextPollMs?: number;
88
+ email?: string;
89
+ role?: string;
90
+ };
91
+
68
92
  export type RemoteSettingsUpdateRequest = {
69
93
  enabled?: boolean;
70
94
  deviceName?: string;
@@ -2,7 +2,8 @@ import { useEffect, useMemo, useState } from 'react';
2
2
  import type { RemoteRuntimeView, RemoteServiceView } from '@/api/types';
3
3
  import {
4
4
  useRemoteDoctor,
5
- useRemoteLogin,
5
+ useRemoteBrowserAuthPoll,
6
+ useRemoteBrowserAuthStart,
6
7
  useRemoteLogout,
7
8
  useRemoteServiceControl,
8
9
  useRemoteSettings,
@@ -58,7 +59,8 @@ function KeyValueRow(props: { label: string; value?: string | number | null; mut
58
59
 
59
60
  export function RemoteAccessPage() {
60
61
  const remoteStatus = useRemoteStatus();
61
- const loginMutation = useRemoteLogin();
62
+ const browserAuthStartMutation = useRemoteBrowserAuthStart();
63
+ const browserAuthPollMutation = useRemoteBrowserAuthPoll();
62
64
  const logoutMutation = useRemoteLogout();
63
65
  const settingsMutation = useRemoteSettings();
64
66
  const doctorMutation = useRemoteDoctor();
@@ -68,13 +70,14 @@ export function RemoteAccessPage() {
68
70
  const runtimeStatus = useMemo(() => getRuntimeStatus(status?.runtime ?? null), [status?.runtime]);
69
71
  const serviceStatus = useMemo(() => getServiceStatus(status?.service ?? { running: false, currentProcess: false }), [status?.service]);
70
72
 
71
- const [email, setEmail] = useState('');
72
- const [password, setPassword] = useState('');
73
- const [loginApiBase, setLoginApiBase] = useState('');
74
- const [register, setRegister] = useState(false);
75
73
  const [enabled, setEnabled] = useState(false);
76
74
  const [deviceName, setDeviceName] = useState('');
77
75
  const [platformApiBase, setPlatformApiBase] = useState('');
76
+ const [authSessionId, setAuthSessionId] = useState<string | null>(null);
77
+ const [authVerificationUri, setAuthVerificationUri] = useState<string | null>(null);
78
+ const [authStatusMessage, setAuthStatusMessage] = useState('');
79
+ const [authExpiresAt, setAuthExpiresAt] = useState<string | null>(null);
80
+ const [authPollIntervalMs, setAuthPollIntervalMs] = useState(1500);
78
81
 
79
82
  useEffect(() => {
80
83
  if (!status) {
@@ -83,10 +86,91 @@ export function RemoteAccessPage() {
83
86
  setEnabled(status.settings.enabled);
84
87
  setDeviceName(status.settings.deviceName);
85
88
  setPlatformApiBase(status.settings.platformApiBase);
86
- if (!loginApiBase) {
87
- setLoginApiBase(status.account.apiBase ?? status.settings.platformApiBase ?? '');
89
+ }, [status]);
90
+
91
+ useEffect(() => {
92
+ if (!status?.account.loggedIn) {
93
+ return;
94
+ }
95
+ setAuthSessionId(null);
96
+ setAuthVerificationUri(null);
97
+ setAuthExpiresAt(null);
98
+ setAuthStatusMessage('');
99
+ setAuthPollIntervalMs(1500);
100
+ }, [status?.account.loggedIn]);
101
+
102
+ useEffect(() => {
103
+ if (!authSessionId || status?.account.loggedIn) {
104
+ return;
105
+ }
106
+
107
+ let cancelled = false;
108
+ const timerId = window.setTimeout(async () => {
109
+ try {
110
+ const result = await browserAuthPollMutation.mutateAsync({
111
+ sessionId: authSessionId,
112
+ apiBase: platformApiBase.trim() || status?.settings.platformApiBase || status?.account.apiBase || undefined
113
+ });
114
+ if (cancelled) {
115
+ return;
116
+ }
117
+ if (result.status === 'pending') {
118
+ setAuthStatusMessage(t('remoteBrowserAuthWaiting'));
119
+ setAuthPollIntervalMs(result.nextPollMs ?? 1500);
120
+ return;
121
+ }
122
+ if (result.status === 'authorized') {
123
+ setAuthStatusMessage(t('remoteBrowserAuthCompleted'));
124
+ setAuthSessionId(null);
125
+ setAuthVerificationUri(null);
126
+ return;
127
+ }
128
+ setAuthStatusMessage(result.message || t('remoteBrowserAuthExpired'));
129
+ setAuthSessionId(null);
130
+ setAuthVerificationUri(null);
131
+ } catch {
132
+ if (cancelled) {
133
+ return;
134
+ }
135
+ setAuthSessionId(null);
136
+ setAuthVerificationUri(null);
137
+ }
138
+ }, authPollIntervalMs);
139
+
140
+ return () => {
141
+ cancelled = true;
142
+ window.clearTimeout(timerId);
143
+ };
144
+ }, [
145
+ authPollIntervalMs,
146
+ authSessionId,
147
+ browserAuthPollMutation,
148
+ platformApiBase,
149
+ status?.account.apiBase,
150
+ status?.account.loggedIn,
151
+ status?.settings.platformApiBase
152
+ ]);
153
+
154
+ const handleStartBrowserAuth = async () => {
155
+ const apiBase = platformApiBase.trim() || status?.settings.platformApiBase || status?.account.apiBase || undefined;
156
+ const result = await browserAuthStartMutation.mutateAsync({ apiBase });
157
+ setAuthSessionId(result.sessionId);
158
+ setAuthVerificationUri(result.verificationUri);
159
+ setAuthExpiresAt(result.expiresAt);
160
+ setAuthPollIntervalMs(result.intervalMs);
161
+ setAuthStatusMessage(t('remoteBrowserAuthWaiting'));
162
+ const opened = window.open(result.verificationUri, '_blank', 'noopener,noreferrer');
163
+ if (!opened) {
164
+ setAuthStatusMessage(t('remoteBrowserAuthPopupBlocked'));
88
165
  }
89
- }, [loginApiBase, status]);
166
+ };
167
+
168
+ const handleResumeBrowserAuth = () => {
169
+ if (!authVerificationUri) {
170
+ return;
171
+ }
172
+ window.open(authVerificationUri, '_blank', 'noopener,noreferrer');
173
+ };
90
174
 
91
175
  if (remoteStatus.isLoading && !status) {
92
176
  return <div className="p-8 text-gray-400">{t('remoteLoading')}</div>;
@@ -207,43 +291,35 @@ export function RemoteAccessPage() {
207
291
  </>
208
292
  ) : (
209
293
  <>
210
- <div className="space-y-2">
211
- <Label htmlFor="remote-email">{t('remoteEmail')}</Label>
212
- <Input id="remote-email" value={email} onChange={(event) => setEmail(event.target.value)} placeholder="name@example.com" />
213
- </div>
214
- <div className="space-y-2">
215
- <Label htmlFor="remote-password">{t('remotePassword')}</Label>
216
- <Input id="remote-password" type="password" value={password} onChange={(event) => setPassword(event.target.value)} placeholder={t('remotePasswordPlaceholder')} />
217
- </div>
218
- <div className="space-y-2">
219
- <Label htmlFor="remote-login-api-base">{t('remoteApiBase')}</Label>
220
- <Input
221
- id="remote-login-api-base"
222
- value={loginApiBase}
223
- onChange={(event) => setLoginApiBase(event.target.value)}
224
- placeholder="https://ai-gateway-api.nextclaw.io/v1"
225
- />
226
- </div>
227
- <div className="flex items-center justify-between rounded-2xl border border-gray-200/70 px-4 py-3">
228
- <div>
229
- <p className="text-sm font-medium text-gray-900">{t('remoteRegisterIfNeeded')}</p>
230
- <p className="mt-1 text-xs text-gray-500">{t('remoteRegisterIfNeededHelp')}</p>
294
+ <div className="rounded-2xl border border-gray-200/70 bg-gray-50/70 px-4 py-3">
295
+ <p className="text-sm font-medium text-gray-900">{t('remoteBrowserAuthTitle')}</p>
296
+ <p className="mt-1 text-sm text-gray-600">{t('remoteBrowserAuthDescription')}</p>
297
+ <div className="mt-3 border-t border-white/80 pt-3">
298
+ <KeyValueRow label={t('remoteApiBase')} value={platformApiBase || status?.settings.platformApiBase || status?.account.apiBase} muted />
299
+ <KeyValueRow label={t('remoteBrowserAuthSession')} value={authSessionId} muted />
300
+ <KeyValueRow
301
+ label={t('remoteBrowserAuthExpiresAt')}
302
+ value={authExpiresAt ? formatDateTime(authExpiresAt) : '-'}
303
+ muted
304
+ />
231
305
  </div>
232
- <Switch checked={register} onCheckedChange={setRegister} />
233
306
  </div>
234
- <Button
235
- onClick={() =>
236
- loginMutation.mutate({
237
- email,
238
- password,
239
- apiBase: loginApiBase,
240
- register
241
- })
242
- }
243
- disabled={loginMutation.isPending || !email.trim() || !password}
244
- >
245
- {loginMutation.isPending ? t('remoteLoggingIn') : register ? t('remoteCreateAccount') : t('remoteLogin')}
246
- </Button>
307
+ {authStatusMessage ? <p className="text-sm text-gray-600">{authStatusMessage}</p> : null}
308
+ <div className="flex flex-wrap gap-3">
309
+ <Button onClick={handleStartBrowserAuth} disabled={browserAuthStartMutation.isPending || !!authSessionId}>
310
+ {browserAuthStartMutation.isPending
311
+ ? t('remoteBrowserAuthStarting')
312
+ : authSessionId
313
+ ? t('remoteBrowserAuthAuthorizing')
314
+ : t('remoteBrowserAuthAction')}
315
+ </Button>
316
+ {authVerificationUri ? (
317
+ <Button variant="outline" onClick={handleResumeBrowserAuth}>
318
+ {t('remoteBrowserAuthResume')}
319
+ </Button>
320
+ ) : null}
321
+ </div>
322
+ <p className="text-xs text-gray-500">{t('remoteBrowserAuthHint')}</p>
247
323
  </>
248
324
  )}
249
325
  </CardContent>
@@ -5,6 +5,8 @@ import {
5
5
  fetchRemoteDoctor,
6
6
  loginRemote,
7
7
  logoutRemote,
8
+ pollRemoteBrowserAuth,
9
+ startRemoteBrowserAuth,
8
10
  updateRemoteSettings
9
11
  } from '@/api/remote';
10
12
  import { t } from '@/lib/i18n';
@@ -34,6 +36,32 @@ export function useRemoteLogin() {
34
36
  });
35
37
  }
36
38
 
39
+ export function useRemoteBrowserAuthStart() {
40
+ return useMutation({
41
+ mutationFn: startRemoteBrowserAuth,
42
+ onError: (error: Error) => {
43
+ toast.error(`${t('remoteBrowserAuthStartFailed')}: ${error.message}`);
44
+ }
45
+ });
46
+ }
47
+
48
+ export function useRemoteBrowserAuthPoll() {
49
+ const queryClient = useQueryClient();
50
+
51
+ return useMutation({
52
+ mutationFn: pollRemoteBrowserAuth,
53
+ onSuccess: (result) => {
54
+ if (result.status === 'authorized') {
55
+ queryClient.invalidateQueries({ queryKey: ['remote-status'] });
56
+ toast.success(t('remoteLoginSuccess'));
57
+ }
58
+ },
59
+ onError: (error: Error) => {
60
+ toast.error(`${t('remoteBrowserAuthPollFailed')}: ${error.message}`);
61
+ }
62
+ });
63
+ }
64
+
37
65
  export function useRemoteLogout() {
38
66
  const queryClient = useQueryClient();
39
67
 
@@ -49,12 +49,39 @@ export const REMOTE_LABELS: Record<string, { zh: string; en: string }> = {
49
49
  },
50
50
  remoteAccountTitle: { zh: '平台账号', en: 'Platform Account' },
51
51
  remoteAccountDescription: {
52
- zh: '在界面内登录或注册 NextClaw 平台账号。',
53
- en: 'Log in or register your NextClaw platform account inside the UI.'
52
+ zh: '通过浏览器授权把当前设备安全连接到 NextClaw 平台。',
53
+ en: 'Authorize this device in your browser and connect it to the NextClaw platform.'
54
54
  },
55
55
  remoteAccountEmail: { zh: '邮箱', en: 'Email' },
56
56
  remoteAccountRole: { zh: '角色', en: 'Role' },
57
57
  remoteApiBase: { zh: 'API Base', en: 'API Base' },
58
+ remoteBrowserAuthTitle: { zh: '浏览器授权登录', en: 'Browser Authorization' },
59
+ remoteBrowserAuthDescription: {
60
+ zh: '点击后会打开平台授权页,在浏览器内登录或注册并授权当前设备。',
61
+ en: 'Open the platform authorization page in your browser, then sign in or create an account there.'
62
+ },
63
+ remoteBrowserAuthAction: { zh: '前往浏览器授权', en: 'Continue in Browser' },
64
+ remoteBrowserAuthResume: { zh: '重新打开授权页', en: 'Reopen Authorization Page' },
65
+ remoteBrowserAuthStarting: { zh: '正在创建授权会话...', en: 'Starting authorization...' },
66
+ remoteBrowserAuthAuthorizing: { zh: '等待浏览器完成授权...', en: 'Waiting for browser authorization...' },
67
+ remoteBrowserAuthWaiting: {
68
+ zh: '浏览器授权页已打开。请在网页中完成登录或注册,然后此页面会自动接入。',
69
+ en: 'The authorization page is open. Complete sign in or registration there and this page will connect automatically.'
70
+ },
71
+ remoteBrowserAuthCompleted: { zh: '浏览器授权完成,正在刷新登录状态。', en: 'Authorization completed. Refreshing account status.' },
72
+ remoteBrowserAuthExpired: { zh: '授权会话已过期,请重新发起。', en: 'Authorization session expired. Start again.' },
73
+ remoteBrowserAuthPopupBlocked: {
74
+ zh: '浏览器没有自动打开,请点击“重新打开授权页”。',
75
+ en: 'Your browser did not open automatically. Use "Reopen Authorization Page".'
76
+ },
77
+ remoteBrowserAuthSession: { zh: '授权会话', en: 'Auth Session' },
78
+ remoteBrowserAuthExpiresAt: { zh: '授权过期时间', en: 'Auth Expires At' },
79
+ remoteBrowserAuthHint: {
80
+ zh: '如果你刚修改了上方 Platform API Base,建议先保存设置;未保存时当前页面也会沿用你输入的新地址发起授权。',
81
+ en: 'If you just changed the Platform API Base above, saving settings is recommended. This page will still use the current value for browser authorization.'
82
+ },
83
+ remoteBrowserAuthStartFailed: { zh: '启动浏览器授权失败', en: 'Failed to start browser authorization' },
84
+ remoteBrowserAuthPollFailed: { zh: '浏览器授权状态检查失败', en: 'Failed to check browser authorization status' },
58
85
  remoteEmail: { zh: '邮箱', en: 'Email' },
59
86
  remotePassword: { zh: '密码', en: 'Password' },
60
87
  remotePasswordPlaceholder: { zh: '请输入你的平台密码', en: 'Enter your platform password' },
@@ -1 +0,0 @@
1
- import{u as z,b as N,d as f,e as c,r as o,j as e,aY as G,aZ as V,a_ as W,K as Y,a$ as Z,b0 as J,b1 as X}from"./vendor-CwsIoNvJ.js";import{af as u,t,C as x,a3 as g,a4 as p,a5 as h,z as v,s as H,I as j,B as m}from"./index-Byfw276e.js";import{P as ee,a as te}from"./page-layout-BGg1EhM5.js";import{L as y}from"./label-B1MloEtn.js";import{S}from"./status-dot-DNyCdxPZ.js";import{S as O}from"./switch-DE_MYk7x.js";async function re(){const s=await u.get("/api/remote/status");if(!s.ok)throw new Error(s.error.message);return s.data}async function se(){const s=await u.get("/api/remote/doctor");if(!s.ok)throw new Error(s.error.message);return s.data}async function ae(s){const a=await u.post("/api/remote/login",s);if(!a.ok)throw new Error(a.error.message);return a.data}async function ie(){const s=await u.post("/api/remote/logout",{});if(!s.ok)throw new Error(s.error.message);return s.data}async function ne(s){const a=await u.put("/api/remote/settings",s);if(!a.ok)throw new Error(a.error.message);return a.data}async function oe(s){const a=await u.post(`/api/remote/service/${s}`,{});if(!a.ok)throw new Error(a.error.message);return a.data}function ce(){return z({queryKey:["remote-status"],queryFn:re,staleTime:5e3,refetchOnWindowFocus:!0})}function le(){const s=N();return f({mutationFn:ae,onSuccess:()=>{s.invalidateQueries({queryKey:["remote-status"]}),c.success(t("remoteLoginSuccess"))},onError:a=>{c.error(`${t("remoteLoginFailed")}: ${a.message}`)}})}function me(){const s=N();return f({mutationFn:ie,onSuccess:()=>{s.invalidateQueries({queryKey:["remote-status"]}),c.success(t("remoteLogoutSuccess"))},onError:a=>{c.error(`${t("remoteLogoutFailed")}: ${a.message}`)}})}function de(){const s=N();return f({mutationFn:ne,onSuccess:()=>{s.invalidateQueries({queryKey:["remote-status"]}),c.success(t("remoteSettingsSaved"))},onError:a=>{c.error(`${t("remoteSettingsSaveFailed")}: ${a.message}`)}})}function ue(){return f({mutationFn:se,onSuccess:()=>{c.success(t("remoteDoctorCompleted"))},onError:s=>{c.error(`${t("remoteDoctorFailed")}: ${s.message}`)}})}function xe(){const s=N();return f({mutationFn:oe,onSuccess:a=>{s.invalidateQueries({queryKey:["remote-status"]}),c.success(a.message)},onError:a=>{c.error(`${t("remoteServiceActionFailed")}: ${a.message}`)}})}function ge(s){return s?s.state==="connected"?{status:"ready",label:t("remoteStateConnected")}:s.state==="connecting"?{status:"warning",label:t("remoteStateConnecting")}:s.state==="error"?{status:"warning",label:t("remoteStateError")}:s.state==="disconnected"?{status:"warning",label:t("remoteStateDisconnected")}:{status:"inactive",label:t("remoteStateDisabled")}:{status:"inactive",label:t("remoteRuntimeMissing")}}function pe(s){return s.running?s.currentProcess?{status:"ready",label:t("remoteServiceManagedRunning")}:{status:"active",label:t("remoteServiceRunning")}:{status:"inactive",label:t("remoteServiceStopped")}}function n(s){const a=s.value===void 0||s.value===null||s.value===""?"-":String(s.value);return e.jsxs("div",{className:"flex items-start justify-between gap-4 py-2 text-sm",children:[e.jsx("span",{className:"text-gray-500",children:s.label}),e.jsx("span",{className:s.muted?"text-right text-gray-500":"text-right text-gray-800",children:a})]})}function Se(){var M,T,K;const s=ce(),a=le(),w=me(),C=de(),d=ue(),l=xe(),r=s.data,A=o.useMemo(()=>ge((r==null?void 0:r.runtime)??null),[r==null?void 0:r.runtime]),E=o.useMemo(()=>pe((r==null?void 0:r.service)??{running:!1,currentProcess:!1}),[r==null?void 0:r.service]),[P,Q]=o.useState(""),[R,U]=o.useState(""),[b,k]=o.useState(""),[D,_]=o.useState(!1),[F,L]=o.useState(!1),[B,$]=o.useState(""),[q,I]=o.useState("");return o.useEffect(()=>{r&&(L(r.settings.enabled),$(r.settings.deviceName),I(r.settings.platformApiBase),b||k(r.account.apiBase??r.settings.platformApiBase??""))},[b,r]),s.isLoading&&!r?e.jsx("div",{className:"p-8 text-gray-400",children:t("remoteLoading")}):e.jsxs(ee,{className:"space-y-6",children:[e.jsx(te,{title:t("remotePageTitle"),description:t("remotePageDescription")}),e.jsxs("div",{className:"grid gap-6 xl:grid-cols-[1.35fr_0.95fr]",children:[e.jsxs(x,{children:[e.jsxs(g,{children:[e.jsxs(p,{className:"flex items-center gap-2",children:[e.jsx(G,{className:"h-4 w-4 text-primary"}),t("remoteOverviewTitle")]}),e.jsx(h,{children:t("remoteOverviewDescription")})]}),e.jsxs(v,{className:"space-y-5",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(S,{status:r!=null&&r.account.loggedIn?"ready":"inactive",label:r!=null&&r.account.loggedIn?t("remoteAccountConnected"):t("remoteAccountNotConnected")}),e.jsx(S,{status:E.status,label:E.label}),e.jsx(S,{status:A.status,label:A.label})]}),e.jsxs("div",{className:"rounded-2xl border border-gray-200/70 bg-gray-50/70 px-4 py-3",children:[e.jsx(n,{label:t("remoteLocalOrigin"),value:r==null?void 0:r.localOrigin}),e.jsx(n,{label:t("remotePublicPlatform"),value:(r==null?void 0:r.platformBase)??(r==null?void 0:r.account.platformBase)}),e.jsx(n,{label:t("remoteDeviceId"),value:(M=r==null?void 0:r.runtime)==null?void 0:M.deviceId,muted:!0}),e.jsx(n,{label:t("remoteLastConnectedAt"),value:(T=r==null?void 0:r.runtime)!=null&&T.lastConnectedAt?H(r.runtime.lastConnectedAt):"-",muted:!0}),e.jsx(n,{label:t("remoteLastError"),value:(K=r==null?void 0:r.runtime)==null?void 0:K.lastError,muted:!0})]})]})]}),e.jsxs(x,{children:[e.jsxs(g,{children:[e.jsxs(p,{className:"flex items-center gap-2",children:[e.jsx(V,{className:"h-4 w-4 text-primary"}),t("remoteDeviceTitle")]}),e.jsx(h,{children:t("remoteDeviceDescription")})]}),e.jsxs(v,{className:"space-y-4",children:[e.jsx("div",{className:"space-y-2",children:e.jsxs("div",{className:"flex items-center justify-between rounded-2xl border border-gray-200/70 px-4 py-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:t("remoteEnabled")}),e.jsx("p",{className:"mt-1 text-xs text-gray-500",children:t("remoteEnabledHelp")})]}),e.jsx(O,{checked:F,onCheckedChange:L})]})}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(y,{htmlFor:"remote-device-name",children:t("remoteDeviceName")}),e.jsx(j,{id:"remote-device-name",value:B,onChange:i=>$(i.target.value),placeholder:t("remoteDeviceNamePlaceholder")})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(y,{htmlFor:"remote-platform-api-base",children:t("remotePlatformApiBase")}),e.jsx(j,{id:"remote-platform-api-base",value:q,onChange:i=>I(i.target.value),placeholder:"https://ai-gateway-api.nextclaw.io/v1"}),e.jsx("p",{className:"text-xs text-gray-500",children:t("remotePlatformApiBaseHelp")})]}),e.jsxs("div",{className:"flex flex-wrap gap-3",children:[e.jsx(m,{onClick:()=>C.mutate({enabled:F,deviceName:B,platformApiBase:q}),disabled:C.isPending,children:C.isPending?t("saving"):t("remoteSaveSettings")}),e.jsxs(m,{variant:"outline",onClick:()=>l.mutate("restart"),disabled:l.isPending,children:[e.jsx(W,{className:"mr-2 h-4 w-4"}),t("remoteRestartService")]})]}),e.jsx("p",{className:"text-xs text-gray-500",children:t("remoteSaveHint")})]})]})]}),e.jsxs("div",{className:"grid gap-6 xl:grid-cols-[1fr_1fr]",children:[e.jsxs(x,{children:[e.jsxs(g,{children:[e.jsxs(p,{className:"flex items-center gap-2",children:[e.jsx(Y,{className:"h-4 w-4 text-primary"}),t("remoteAccountTitle")]}),e.jsx(h,{children:t("remoteAccountDescription")})]}),e.jsx(v,{className:"space-y-4",children:r!=null&&r.account.loggedIn?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rounded-2xl border border-gray-200/70 bg-gray-50/70 px-4 py-3",children:[e.jsx(n,{label:t("remoteAccountEmail"),value:r.account.email}),e.jsx(n,{label:t("remoteAccountRole"),value:r.account.role}),e.jsx(n,{label:t("remoteApiBase"),value:r.account.apiBase})]}),e.jsx(m,{variant:"outline",onClick:()=>w.mutate(),disabled:w.isPending,children:w.isPending?t("remoteLoggingOut"):t("remoteLogout")})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(y,{htmlFor:"remote-email",children:t("remoteEmail")}),e.jsx(j,{id:"remote-email",value:P,onChange:i=>Q(i.target.value),placeholder:"name@example.com"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(y,{htmlFor:"remote-password",children:t("remotePassword")}),e.jsx(j,{id:"remote-password",type:"password",value:R,onChange:i=>U(i.target.value),placeholder:t("remotePasswordPlaceholder")})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(y,{htmlFor:"remote-login-api-base",children:t("remoteApiBase")}),e.jsx(j,{id:"remote-login-api-base",value:b,onChange:i=>k(i.target.value),placeholder:"https://ai-gateway-api.nextclaw.io/v1"})]}),e.jsxs("div",{className:"flex items-center justify-between rounded-2xl border border-gray-200/70 px-4 py-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:t("remoteRegisterIfNeeded")}),e.jsx("p",{className:"mt-1 text-xs text-gray-500",children:t("remoteRegisterIfNeededHelp")})]}),e.jsx(O,{checked:D,onCheckedChange:_})]}),e.jsx(m,{onClick:()=>a.mutate({email:P,password:R,apiBase:b,register:D}),disabled:a.isPending||!P.trim()||!R,children:a.isPending?t("remoteLoggingIn"):D?t("remoteCreateAccount"):t("remoteLogin")})]})})]}),e.jsxs(x,{children:[e.jsxs(g,{children:[e.jsxs(p,{className:"flex items-center gap-2",children:[e.jsx(Z,{className:"h-4 w-4 text-primary"}),t("remoteServiceTitle")]}),e.jsx(h,{children:t("remoteServiceDescription")})]}),e.jsxs(v,{className:"space-y-4",children:[e.jsxs("div",{className:"rounded-2xl border border-gray-200/70 bg-gray-50/70 px-4 py-3",children:[e.jsx(n,{label:t("remoteServicePid"),value:r==null?void 0:r.service.pid}),e.jsx(n,{label:t("remoteServiceUiUrl"),value:r==null?void 0:r.service.uiUrl}),e.jsx(n,{label:t("remoteServiceCurrentProcess"),value:r!=null&&r.service.currentProcess?t("yes"):t("no")})]}),e.jsxs("div",{className:"flex flex-wrap gap-3",children:[e.jsx(m,{variant:"primary",onClick:()=>l.mutate("start"),disabled:l.isPending,children:t("remoteStartService")}),e.jsx(m,{variant:"outline",onClick:()=>l.mutate("restart"),disabled:l.isPending,children:t("remoteRestartService")}),e.jsx(m,{variant:"outline",onClick:()=>l.mutate("stop"),disabled:l.isPending,children:t("remoteStopService")})]}),e.jsx("p",{className:"text-xs text-gray-500",children:t("remoteServiceHint")})]})]})]}),e.jsxs(x,{children:[e.jsxs(g,{children:[e.jsxs(p,{className:"flex items-center gap-2",children:[e.jsx(J,{className:"h-4 w-4 text-primary"}),t("remoteDoctorTitle")]}),e.jsx(h,{children:t("remoteDoctorDescription")})]}),e.jsxs(v,{className:"space-y-4",children:[e.jsx("div",{className:"flex flex-wrap gap-3",children:e.jsxs(m,{variant:"outline",onClick:()=>d.mutate(),disabled:d.isPending,children:[e.jsx(X,{className:"mr-2 h-4 w-4"}),d.isPending?t("remoteDoctorRunning"):t("remoteRunDoctor")]})}),d.data?e.jsxs("div",{className:"rounded-2xl border border-gray-200/70 bg-gray-50/70 px-4 py-3",children:[e.jsx(n,{label:t("remoteDoctorGeneratedAt"),value:H(d.data.generatedAt),muted:!0}),e.jsx("div",{className:"mt-3 space-y-2",children:d.data.checks.map(i=>e.jsxs("div",{className:"rounded-xl border border-white bg-white px-3 py-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:i.name}),e.jsx(S,{status:i.ok?"ready":"warning",label:i.ok?t("remoteCheckPassed"):t("remoteCheckFailed")})]}),e.jsx("p",{className:"mt-2 text-sm text-gray-600",children:i.detail})]},i.name))})]}):e.jsx("p",{className:"text-sm text-gray-500",children:t("remoteDoctorEmpty")})]})]})]})}export{Se as RemoteAccessPage};