@nextclaw/ui 0.25.1 → 0.25.2
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.
- package/CHANGELOG.md +11 -0
- package/dist/.vite/manifest.json +190 -190
- package/dist/assets/{account-panel-QhOMbAQP.js → account-panel-BXTCFv7d.js} +1 -1
- package/dist/assets/{api-BWFOIoUm.js → api-DqzQ-_3w.js} +1 -1
- package/dist/assets/{app-data-BnrEdmg2.js → app-data-C54N2p8b.js} +1 -1
- package/dist/assets/app-presenter-provider-V0xnU_Wb.js +115 -0
- package/dist/assets/{appearance-settings-page-C5z5sOkC.js → appearance-settings-page-83T2AtVm.js} +1 -1
- package/dist/assets/{channels-list-page-CzXYZZtK.js → channels-list-page-Bv7CVSqV.js} +1 -1
- package/dist/assets/{config-split-page-CxLWGwdt.js → config-split-page-DqAXKLmG.js} +1 -1
- package/dist/assets/{confirm-dialog-BWS7gA_z.js → confirm-dialog-BTKCWMn6.js} +1 -1
- package/dist/assets/{desktop-capabilities-page-BJNGkq4q.js → desktop-capabilities-page-zLzCSB7A.js} +1 -1
- package/dist/assets/{desktop-update-config-7tBum3MA.js → desktop-update-config-PmofcFf6.js} +1 -1
- package/dist/assets/{doc-browser-BNiqa-zm.js → doc-browser-BJrre2S_.js} +1 -1
- package/dist/assets/doc-browser-BPqA-zQx.js +1 -0
- package/dist/assets/{doc-browser-context-Dp0dEMyT.js → doc-browser-context-TILuCY5c.js} +1 -1
- package/dist/assets/{extensions-WWEYpaUh.js → extensions-CVUFK6Jj.js} +1 -1
- package/dist/assets/{i18n-ClHx6jwj.js → i18n-20rSH40D.js} +2 -2
- package/dist/assets/{i18n-provider-C1NxogO4.js → i18n-provider-BPRq6S7O.js} +1 -1
- package/dist/assets/{keyboard-shortcuts-settings-page-CS9BFOBW.js → keyboard-shortcuts-settings-page-CkQbSKwI.js} +1 -1
- package/dist/assets/login-page-Dlyvp-81.js +1 -0
- package/dist/assets/{login-page-CL0SrBCf.js → login-page-f8qQ4xKY.js} +1 -1
- package/dist/assets/main-D2dWy8fM.css +1 -0
- package/dist/assets/main-i18n-catalog.config-DZ-5ollN.js +1 -0
- package/dist/assets/{main-CvVBCOlN.js → main-ky5Efhq1.js} +2 -2
- package/dist/assets/{mcp-marketplace-page-D75hPmzO.js → mcp-marketplace-page-FfIfEo6w.js} +1 -1
- package/dist/assets/mcp-marketplace-page-sH7VptN_.js +1 -0
- package/dist/assets/{model-config-page-DFfcV9lu.js → model-config-page-Bcm6-pCX.js} +1 -1
- package/dist/assets/panel-app-i18n-catalog.config-p3qAY_Eq.js +1 -0
- package/dist/assets/{panelStandalone-D2CqTyi_.js → panelStandalone-By-6dDXS.js} +2 -2
- package/dist/assets/{privacy-settings-page-CE-xF9bo.js → privacy-settings-page-B1sHLAc1.js} +1 -1
- package/dist/assets/{projects-BF0wpY4X.js → projects-O3GTERwP.js} +1 -1
- package/dist/assets/{provider-scoped-model-input-BvZitHkT.js → provider-scoped-model-input-BQsugx1v.js} +1 -1
- package/dist/assets/{providers-config-page-cDeyluVG.js → providers-config-page-BDEdHJbw.js} +1 -1
- package/dist/assets/{pwa-dXn0s_lD.js → pwa-D_nZUwG0.js} +1 -1
- package/dist/assets/remote-CFWp4wyH.js +1 -0
- package/dist/assets/{runtime-config-page-DNo4dzS1.js → runtime-config-page-BHTKwgDi.js} +1 -1
- package/dist/assets/{search-config-page-CA0HxVy0.js → search-config-page-DWKlMHgH.js} +1 -1
- package/dist/assets/{secrets-config-page-iT1wamQp.js → secrets-config-page-DiQL088Y.js} +1 -1
- package/dist/assets/security-config-DmTDKKY4.js +1 -0
- package/dist/assets/{service-apps-panel-CU0tFrqT.js → service-apps-panel-CEnyYsQp.js} +1 -1
- package/dist/assets/{tag-chip-CY0Hqk_l.js → tag-chip-B1AYPOKL.js} +1 -1
- package/dist/assets/{theme-provider-BvpXGXWH.js → theme-provider-DzvAi3pe.js} +1 -1
- package/dist/assets/{use-config-C8ZkQiTc.js → use-config-BEdbtpD4.js} +1 -1
- package/dist/assets/{use-desktop-capabilities-DR9snbeo.js → use-desktop-capabilities-BkuRd2cY.js} +1 -1
- package/dist/index.html +18 -18
- package/dist/panel-standalone.html +5 -5
- package/package.json +5 -5
- package/src/features/chat/features/conversation/components/__tests__/session-voice-input.test-utils.ts +18 -5
- package/src/features/chat/features/conversation/components/chat-voice-input-panel.tsx +37 -14
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +3 -3
- package/src/features/chat/features/conversation/hooks/__tests__/use-chat-voice-input.test.tsx +116 -10
- package/src/features/chat/features/conversation/hooks/use-chat-voice-input.ts +15 -2
- package/src/features/chat/managers/chat-voice-input.manager.ts +26 -4
- package/src/features/chat/utils/chat-voice-permissions.utils.test.ts +36 -0
- package/src/features/chat/utils/chat-voice-permissions.utils.ts +31 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +18 -3
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +18 -3
- package/dist/assets/app-presenter-provider-CDRBc_lQ.js +0 -115
- package/dist/assets/doc-browser-DZDqghNc.js +0 -1
- package/dist/assets/login-page-Dq5nPUCU.js +0 -1
- package/dist/assets/main-CLwCjK9U.css +0 -1
- package/dist/assets/main-i18n-catalog.config-D4qFo4OB.js +0 -1
- package/dist/assets/mcp-marketplace-page-CATnpZ2Q.js +0 -1
- package/dist/assets/panel-app-i18n-catalog.config-oUwmxoE7.js +0 -1
- package/dist/assets/remote-CV5wQVVj.js +0 -1
- package/dist/assets/security-config-B38PLI3M.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e}from"./chunk-DF37yw5_.js";import{A as t,M as n,i as r}from"./utils-CBZPzC_b.js";import{Lt as i,Yt as a,bn as o,hn as s,jn as c,vn as l,yn as u}from"./api-BWFOIoUm.js";import{s as d}from"./i18n-ClHx6jwj.js";import{t as f}from"./createLucideIcon-BNKf0_kc.js";import{t as ee}from"./boxes-D6uPnkGo.js";import{a as p,i as te,n as ne,o as re,r as ie,t as m}from"./dialog-Xe73hpQz.js";import{t as h}from"./use-panel-app-scroll-restoration-ZnicIfo3.js";var g=f(`RotateCcw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]),_=f(`ShieldAlert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]),v=[`panel-apps`],y=[`panel-app`];function b(e,t){return e.id===t||e.appId===t}function x(e,t,n){return e&&{...e,entries:e.entries.map(e=>b(e,t)?n(e):e)}}function S(e,t){return x(e,t.id,()=>t)}function C(e,t){let n=e?.entries.reduce((e,t)=>Math.max(e,t.mainSidebarOrder??-1),-1)??-1;return x(e,t.id,e=>({...e,...typeof t.preferences.favorite==`boolean`?{favorite:t.preferences.favorite}:{},...typeof t.preferences.mainSidebar==`boolean`?t.preferences.mainSidebar?{mainSidebar:!0,mainSidebarOrder:e.mainSidebar?e.mainSidebarOrder:n+1}:{mainSidebar:!1,mainSidebarOrder:void 0}:{}}))}function ae(e={}){return o({queryKey:v,queryFn:()=>i.panelApps.listPanelApps(),enabled:e.enabled??!0,staleTime:0})}function w(e,t={}){return o({queryKey:[...y,e],queryFn:()=>i.panelApps.getPanelApp(e),enabled:!!e&&(t.enabled??!0),staleTime:5e3})}function T(){let e=c();return u({mutationFn:({id:e,preferences:t})=>i.panelApps.updatePanelAppPreferences(e,t),onMutate:async t=>{await e.cancelQueries({queryKey:v});let n=e.getQueryData(v)?.entries.find(e=>b(e,t.id));return e.setQueryData(v,e=>C(e,t)),{previousEntry:n}},onSuccess:t=>{e.setQueryData(v,e=>S(e,t)),e.setQueryData([...y,t.appId],t)},onError:(t,n,r)=>{let i=r?.previousEntry;i&&e.setQueryData(v,e=>x(e,n.id,()=>i));let a=t instanceof Error?t.message:String(t);l.error(`${d(`panelAppsPreferenceUpdateFailed`)}: ${a}`)}})}function E(){let e=c();return u({mutationFn:e=>i.panelApps.recordPanelAppOpened(e),onSuccess:t=>{e.setQueryData(v,e=>S(e,t)),e.setQueryData([...y,t.appId],t)}})}function D(){let e=c();return u({mutationFn:e=>i.panelApps.grantClient(e),onSuccess:()=>{e.invalidateQueries({queryKey:v}),e.invalidateQueries({queryKey:y})}})}function O(){let e=c();return u({mutationFn:e=>i.panelApps.deletePanelApp(e),onSuccess:()=>{e.invalidateQueries({queryKey:v}),e.invalidateQueries({queryKey:y})}})}var k=class{constructor(e){this.authorizationManager=e,this.handleIframeMessage=({event:e,iframe:t})=>{this.isBridgeRequest(e.data)&&(!t?.contentWindow||e.source!==t.contentWindow||this.handleBridgeRequest({event:e,iframe:t},e.data))},this.handleBridgeRequest=async(e,t)=>{try{let n=this.getBridgeContext(t),r=await this.dispatchRequest(n,t);this.postResponse(e,{type:`nextclaw:panel-app-service-actions:response`,requestId:t.requestId,ok:!0,data:r})}catch(n){this.postResponse(e,{type:`nextclaw:panel-app-service-actions:response`,requestId:t.requestId,ok:!1,error:this.toBridgeError(n)})}},this.dispatchRequest=async(e,t)=>{switch(t.method){case`list`:return await i.serviceApps.listServiceActions({bridgeSessionToken:e.token});case`requestGrant`:return await this.confirmAndGrant(e,this.requireActionId(t));case`revokeGrant`:return await i.serviceApps.revokeServiceAction(this.requireActionId(t),{bridgeSessionToken:e.token});case`invoke`:return await this.invokeWithAuthorization(e,t);case`agent.send`:return await this.sendAgentMessageWithAuthorization(e,t);case`agent.generateObject`:return await this.generateAgentObjectWithAuthorization(e,t);case`verification.list`:return await i.serviceApps.listVerificationRecords({acceptanceId:t.payload?.acceptanceId,appId:t.payload?.appId,limit:t.payload?.limit,bridgeSessionToken:e.token});case`acceptance.status`:return await i.serviceApps.getPortableRuntimeAcceptanceStatus({appId:t.payload?.appId,locale:t.payload?.locale,bridgeSessionToken:e.token});case`acceptance.export`:return await i.serviceApps.exportPortableRuntimeAcceptance({appId:t.payload?.appId,locale:t.payload?.locale,bridgeSessionToken:e.token});default:throw Error(`Unsupported panel bridge method: ${String(t.method)}`)}},this.invokeWithAuthorization=async(e,t)=>{let n=this.requireActionId(t);try{return await i.serviceApps.invokeServiceAction(n,this.readOptionalRecord(t.payload?.input),{bridgeSessionToken:e.token})}catch(r){if(!(r instanceof s)||r.code!==`AUTHORIZATION_REQUIRED`)throw r;return await this.confirmAndGrant(e,n,this.readOptionalRecord(t.payload?.input)),await i.serviceApps.invokeServiceAction(n,this.readOptionalRecord(t.payload?.input),{bridgeSessionToken:e.token})}},this.sendAgentMessageWithAuthorization=async(e,t)=>{try{return await i.panelApps.sendAgentMessage(this.readAgentSendRequest(t),{bridgeSessionToken:e.token})}catch(n){if(!(n instanceof s)||n.code!==`AUTHORIZATION_REQUIRED`)throw n;return await this.confirmAndGrantAgentCapability(e,`agent:send`),await i.panelApps.sendAgentMessage(this.readAgentSendRequest(t),{bridgeSessionToken:e.token})}},this.generateAgentObjectWithAuthorization=async(e,t)=>{try{return await i.panelApps.generateAgentObject(this.readGenerateObjectRequest(t),{bridgeSessionToken:e.token})}catch(n){if(!(n instanceof s)||n.code!==`AUTHORIZATION_REQUIRED`)throw n;return await this.confirmAndGrantAgentCapability(e,`agent:generateObject`),await i.panelApps.generateAgentObject(this.readGenerateObjectRequest(t),{bridgeSessionToken:e.token})}},this.confirmAndGrantAgentCapability=async(e,t)=>{if(!await this.authorizationManager.requestAuthorization({panelAppId:e.appId,actions:[{actionId:t,actionTitle:t===`agent:send`?`Send agent message`:`Generate object`,actionDescription:t===`agent:send`?`Send a message to a NextClaw Agent session.`:`Send context to a NextClaw Agent session and receive a structured object.`,risk:`write`}]}))throw new s({code:`AUTHORIZATION_REJECTED`,message:`Permission rejected for ${t}.`});return await i.panelApps.grantAgentCapability(t,{bridgeSessionToken:e.token})},this.confirmAndGrant=async(e,t,n)=>{let r=await this.listGrantCandidateActions(e,t);if(!await this.authorizationManager.requestAuthorization({panelAppId:e.appId,actions:r.map(e=>({actionId:e.id,actionTitle:e.title,actionDescription:e.description,risk:e.risk})),inputPreview:this.createInputPreview(n)}))throw new s({code:`AUTHORIZATION_REJECTED`,message:`Permission rejected for ${t}.`});let a=await i.serviceApps.grantServiceActions(r.map(e=>e.id),{bridgeSessionToken:e.token}),o=a.grants.find(e=>e.actionId===t)??a.grants[0];if(!o)throw Error(`No permission grant returned for ${t}.`);return o},this.listGrantCandidateActions=async(e,t)=>{let n=await i.serviceApps.listServiceActions({bridgeSessionToken:e.token}),r=n.actions.find(e=>e.id===t);if(r&&r.grantState!==`not-granted`)return[r];let a=n.actions.filter(e=>e.grantState===`not-granted`),o=[...a.filter(e=>e.id===t),...a.filter(e=>e.id!==t)];return o.length>0?o:[{appId:r?.appId??t.split(`.`)[0]??t,description:r?.description,id:t,name:r?.name??t,risk:r?.risk??`dangerous`,title:r?.title}]},this.createInputPreview=e=>{if(!(!e||Object.keys(e).length===0))try{let t=JSON.stringify(e,null,2);return t.length>500?`${t.slice(0,500)}...`:t}catch{return`[unserializable input]`}},this.getBridgeContext=e=>({appId:e.appId,token:e.runtimeToken}),this.requireActionId=e=>{let t=e.payload?.actionId?.trim();if(!t)throw Error(`Panel bridge actionId is required.`);return t},this.readOptionalRecord=e=>{if(e!==void 0)return this.requireRecord(e,`input`)},this.readAgentSendRequest=e=>({payload:this.requireRecord(e.payload?.request,`agent.send request`)}),this.readGenerateObjectRequest=e=>({input:this.requireRecord(e.payload?.input,`generateObject input`)}),this.requireRecord=(e,t)=>{if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`${t} is required.`);return e},this.postResponse=(e,t)=>{e.iframe?.contentWindow?.postMessage(t,`*`)},this.toBridgeError=e=>e instanceof s?{code:e.code,message:e.message,details:e.details}:{message:e instanceof Error?e.message:String(e)},this.isBridgeRequest=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return t.type===`nextclaw:panel-app-service-actions:request`&&typeof t.requestId==`string`&&typeof t.runtimeToken==`string`&&typeof t.appId==`string`&&(t.method===`invoke`||t.method===`agent.send`||t.method===`agent.generateObject`||t.method===`acceptance.export`||t.method===`acceptance.status`||t.method===`list`||t.method===`requestGrant`||t.method===`revokeGrant`||t.method===`verification.list`)}}};function oe(){return`service-action-auth-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}var A=r((e,t)=>({pending:null,requestAuthorization:async n=>await new Promise(r=>{t().pending?.resolve(!1),e({pending:{id:oe(),...n,resolve:r}})}),resolveAuthorization:n=>{let{pending:r}=t();r&&(r.resolve(n),e({pending:null}))}})),se=class{constructor(){this.requestAuthorization=async e=>await A.getState().requestAuthorization(e)}},j=class{constructor(){this.serviceActionAuthorizationManager=new se,this.panelAppBridgeManager=new k(this.serviceActionAuthorizationManager)}},M=e(n(),1),N=t(),P=globalThis,F=P.__NEXTCLAW_PANEL_APP_HOST_CONTEXT__??(0,M.createContext)(null);P.__NEXTCLAW_PANEL_APP_HOST_CONTEXT__=F;function I({children:e,presenter:t}){let n=(0,M.useMemo)(()=>t??new j,[t]);return(0,N.jsx)(F.Provider,{value:n,children:e})}function L(){let e=(0,M.useContext)(F);if(!e)throw Error(`usePanelAppHostPresenter must be used inside PanelAppHostProvider`);return e}function R(){let e=L(),{isPending:t,mutateAsync:n}=D(),r=(0,M.useRef)(new Map);return{ensurePanelAppClientGrant:(0,M.useCallback)(async t=>{if(!t.clientDeclared||t.clientGranted)return!0;let i=r.current.get(t.appId);if(i)return await i;let a=(async()=>await e.serviceActionAuthorizationManager.requestAuthorization({panelAppId:t.appId,actions:[{actionId:`nextclaw.client`,actionTitle:d(`panelAppsClientGrantTitle`),actionDescription:d(`panelAppsClientGrantDescription`),risk:`dangerous`}]})?(await n(t.appId),!0):!1)();r.current.set(t.appId,a);try{return await a}finally{r.current.delete(t.appId)}},[n,e]),isPending:t}}function z(e){let t=c(),n=(0,M.useId)(),{ensurePanelAppClientGrant:r}=R(),a=!!(e?.clientDeclared&&!e.clientGranted),s=o({queryKey:[`panel-app-client-grant`,e?.appId??`none`,e?.clientGranted??!1],queryFn:async()=>await r(e),enabled:a,gcTime:0,retry:!1,staleTime:1/0}),l=e?a?s.isError?`error`:s.isPending?`checking`:s.data?`allowed`:`denied`:`allowed`:`idle`;return o({queryKey:[`panel-app-open`,n,e?.appId??`none`],queryFn:async()=>{let n=await i.panelApps.recordPanelAppOpened(e.id);return t.setQueryData(v,e=>S(e,n)),t.setQueryData([...y,n.appId],n),n},enabled:!!(e&&l===`allowed`),retry:!1,staleTime:1/0}),{retryClientGrant:s.refetch,state:l}}var B=`allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-downloads allow-pointer-lock allow-presentation`;function V(e){e?.focus({preventScroll:!0}),e?.contentWindow?.focus()}function H({appId:e,restorationScope:t,unavailableAction:n}){let r=L(),i=w(e),a=(0,M.useRef)(null),o=(0,M.useMemo)(()=>i.data,[i.data]),s=z(o),c=h({currentUrl:o?.contentPath??null,iframeRef:a,isEnabled:!!o,restorationKey:o?`panel-app:${t}:${o.appId}`:null});return(0,M.useEffect)(()=>{let e=e=>{r.panelAppBridgeManager.handleIframeMessage({event:e,iframe:a.current})};return window.addEventListener(`message`,e),()=>window.removeEventListener(`message`,e)},[r]),i.isLoading?(0,N.jsx)(U,{message:d(`panelAppsLoading`)}):i.isError?(0,N.jsx)(U,{message:i.error instanceof Error?i.error.message:d(`panelAppsLoadFailed`),actionLabel:d(`panelAppsTryAgain`),onAction:()=>void i.refetch()}):o?s.state===`checking`||s.state===`idle`?(0,N.jsx)(U,{message:d(`panelAppsCheckingPermission`)}):s.state===`denied`||s.state===`error`?(0,N.jsx)(U,{message:s.state===`denied`?d(`panelAppsPermissionDenied`):d(`panelAppsPermissionFailed`),actionLabel:d(`panelAppsTryAgain`),onAction:()=>void s.retryClientGrant()}):(0,N.jsx)(`div`,{className:`h-full min-h-0 bg-background`,children:(0,N.jsx)(`iframe`,{ref:a,src:o.contentPath,title:o.title,sandbox:B,className:`block h-full w-full border-0 bg-background`,onLoad:c,onPointerOver:e=>V(e.currentTarget)},o.appId)}):(0,N.jsx)(U,{message:d(`panelAppsMainUnavailable`),actionLabel:n?.label,onAction:n?.onAction})}function U({actionLabel:e,message:t,onAction:n}){return(0,N.jsx)(`div`,{className:`flex h-full min-h-0 items-center justify-center bg-background px-6 text-center`,children:(0,N.jsxs)(`div`,{className:`max-w-sm`,children:[(0,N.jsx)(`div`,{className:`mx-auto flex h-10 w-10 items-center justify-center rounded-lg bg-muted text-muted-foreground`,children:n?(0,N.jsx)(g,{className:`h-5 w-5`}):(0,N.jsx)(ee,{className:`h-5 w-5`})}),(0,N.jsx)(`p`,{className:`mt-3 text-sm leading-6 text-muted-foreground`,children:t}),e&&n?(0,N.jsx)(`button`,{type:`button`,className:`mt-4 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border`,onClick:n,children:e}):null]})})}function W({appId:e}){let t=w(e),n=(0,M.useMemo)(()=>t.data,[t.data]);return(0,M.useEffect)(()=>{let e=document.title;return document.title=`${n?.title??d(`panelAppsTitle`)} · NextClaw`,()=>{document.title=e}},[n?.title]),(0,N.jsx)(`main`,{className:`h-dvh min-h-0 w-full overflow-hidden bg-background`,children:(0,N.jsx)(H,{appId:e,restorationScope:`standalone`})})}function G(){let e=A(e=>e.pending),t=A(e=>e.resolveAuthorization);return(0,N.jsx)(m,{open:!!e,onOpenChange:e=>{e||t(!1)},children:(0,N.jsxs)(ne,{className:`[&>:last-child]:hidden`,children:[(0,N.jsxs)(p,{children:[(0,N.jsxs)(re,{className:`flex items-center gap-2`,children:[(0,N.jsx)(_,{className:`h-4 w-4 text-amber-600`}),d(`serviceActionAuthorizationTitle`)]}),(0,N.jsx)(ie,{children:d(`serviceActionAuthorizationDescription`)})]}),e?(0,N.jsxs)(`div`,{className:`space-y-3 rounded-md border border-border bg-muted/60 p-3 text-sm`,children:[(0,N.jsx)(K,{label:d(`serviceActionAuthorizationSource`),value:e.panelAppId}),(0,N.jsxs)(`div`,{className:`space-y-2`,children:[(0,N.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:d(`serviceActionAuthorizationActions`)}),(0,N.jsx)(`div`,{className:`max-h-64 space-y-2 overflow-auto`,children:e.actions.map(e=>(0,N.jsx)(ce,{action:e},e.actionId))})]}),e.inputPreview?(0,N.jsx)(K,{label:d(`serviceActionAuthorizationInput`),value:e.inputPreview}):null]}):null,(0,N.jsxs)(te,{className:`gap-2 sm:gap-0`,children:[(0,N.jsx)(a,{type:`button`,variant:`outline`,onClick:()=>t(!1),children:d(`serviceActionAuthorizationReject`)}),(0,N.jsx)(a,{type:`button`,onClick:()=>t(!0),children:d(`serviceActionAuthorizationAllow`)})]})]})})}function ce({action:e}){return(0,N.jsxs)(`div`,{className:`space-y-1 rounded border border-border bg-card px-3 py-2`,children:[(0,N.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,N.jsx)(`div`,{className:`min-w-0 break-words text-xs font-medium text-foreground`,children:e.actionTitle??e.actionId}),(0,N.jsx)(`div`,{className:`shrink-0 rounded bg-amber-50 px-1.5 py-0.5 text-[11px] font-medium text-amber-700`,children:e.risk??`dangerous`})]}),(0,N.jsx)(`div`,{className:`break-words text-[11px] text-muted-foreground`,children:e.actionId}),e.actionDescription?(0,N.jsx)(`div`,{className:`break-words text-xs text-muted-foreground`,children:e.actionDescription}):null]})}function K({label:e,value:t}){return(0,N.jsxs)(`div`,{className:`grid grid-cols-[5rem_minmax(0,1fr)] gap-3`,children:[(0,N.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:e}),(0,N.jsx)(`div`,{className:`min-w-0 break-words text-xs text-foreground`,children:t})]})}var q=`nextclaw.ui.theme`,J=`work`,Y=[{value:`work`,labelKey:`themeWork`,appearance:`light`},{value:`night`,labelKey:`themeNight`,appearance:`dark`},{value:`charcoal`,labelKey:`themeCharcoal`,appearance:`dark`},{value:`island`,labelKey:`themeIsland`,appearance:`light`},{value:`natural`,labelKey:`themeNatural`,appearance:`light`},{value:`minimal`,labelKey:`themeMinimal`,appearance:`light`},{value:`warm`,labelKey:`themeWarm`,appearance:`light`},{value:`cool`,labelKey:`themeCool`,appearance:`light`},{value:`dawn`,labelKey:`themeDawn`,appearance:`light`},{value:`graphite`,labelKey:`themeGraphite`,appearance:`light`},{value:`probe`,labelKey:`themeProbe`,appearance:`light`}],le=Y.map(({value:e})=>e),ue=Y.map(({value:e,labelKey:t})=>({value:e,labelKey:t}));function X(e){return typeof e==`string`?le.includes(e)?e:e===`leaf`?`warm`:null:null}function Z(e){return Y.find(t=>t.value===e)?.appearance??`light`}var Q=new class{constructor(){this.activeTheme=J,this.initialized=!1,this.listeners=new Set,this.resolveInitialTheme=()=>{if(typeof window>`u`)return J;try{let e=X(window.localStorage.getItem(q));if(e)return e}catch{}return J},this.initializeTheme=()=>(this.initialized||=(this.activeTheme=this.resolveInitialTheme(),this.applyThemeAttribute(this.activeTheme),!0),this.activeTheme),this.getTheme=()=>this.initialized?this.activeTheme:this.initializeTheme(),this.setTheme=e=>{this.initializeTheme(),e!==this.activeTheme&&(this.activeTheme=e,this.applyThemeAttribute(this.activeTheme),this.saveTheme(this.activeTheme),this.listeners.forEach(e=>e(this.activeTheme)))},this.subscribeThemeChange=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)}),this.applyThemeAttribute=e=>{if(typeof document>`u`)return;let t=Z(e);document.documentElement.setAttribute(`data-theme`,e),document.documentElement.setAttribute(`data-theme-appearance`,t),document.documentElement.style.colorScheme=t},this.saveTheme=e=>{if(!(typeof window>`u`))try{window.localStorage.setItem(q,e)}catch{}}}};function de(){return Q.initializeTheme()}function fe(){return Q.getTheme()}function pe(e){Q.setTheme(e)}function me(e){return Q.subscribeThemeChange(e)}var $=(0,M.createContext)(null);function he({children:e}){let[t,n]=(0,M.useState)(()=>de());(0,M.useEffect)(()=>me(e=>{n(e)}),[]);let r=(0,M.useCallback)(e=>{pe(e),n(fe())},[]),i=(0,M.useMemo)(()=>({theme:t,setTheme:r}),[t,r]);return(0,N.jsx)($.Provider,{value:i,children:e})}function ge(){let e=(0,M.useContext)($);if(!e)throw Error(`useTheme must be used within ThemeProvider`);return e}export{ae as _,Z as a,_ as b,W as c,V as d,R as f,D as g,O as h,ue as i,H as l,j as m,ge as n,X as o,I as p,J as r,G as s,he as t,B as u,E as v,g as x,T as y};
|
|
1
|
+
import{i as e}from"./chunk-DF37yw5_.js";import{A as t,M as n,i as r}from"./utils-CBZPzC_b.js";import{Lt as i,Yt as a,bn as o,hn as s,jn as c,vn as l,yn as u}from"./api-DqzQ-_3w.js";import{s as d}from"./i18n-20rSH40D.js";import{t as f}from"./createLucideIcon-BNKf0_kc.js";import{t as ee}from"./boxes-D6uPnkGo.js";import{a as p,i as te,n as ne,o as re,r as ie,t as m}from"./dialog-Xe73hpQz.js";import{t as h}from"./use-panel-app-scroll-restoration-ZnicIfo3.js";var g=f(`RotateCcw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]),_=f(`ShieldAlert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]),v=[`panel-apps`],y=[`panel-app`];function b(e,t){return e.id===t||e.appId===t}function x(e,t,n){return e&&{...e,entries:e.entries.map(e=>b(e,t)?n(e):e)}}function S(e,t){return x(e,t.id,()=>t)}function C(e,t){let n=e?.entries.reduce((e,t)=>Math.max(e,t.mainSidebarOrder??-1),-1)??-1;return x(e,t.id,e=>({...e,...typeof t.preferences.favorite==`boolean`?{favorite:t.preferences.favorite}:{},...typeof t.preferences.mainSidebar==`boolean`?t.preferences.mainSidebar?{mainSidebar:!0,mainSidebarOrder:e.mainSidebar?e.mainSidebarOrder:n+1}:{mainSidebar:!1,mainSidebarOrder:void 0}:{}}))}function ae(e={}){return o({queryKey:v,queryFn:()=>i.panelApps.listPanelApps(),enabled:e.enabled??!0,staleTime:0})}function w(e,t={}){return o({queryKey:[...y,e],queryFn:()=>i.panelApps.getPanelApp(e),enabled:!!e&&(t.enabled??!0),staleTime:5e3})}function T(){let e=c();return u({mutationFn:({id:e,preferences:t})=>i.panelApps.updatePanelAppPreferences(e,t),onMutate:async t=>{await e.cancelQueries({queryKey:v});let n=e.getQueryData(v)?.entries.find(e=>b(e,t.id));return e.setQueryData(v,e=>C(e,t)),{previousEntry:n}},onSuccess:t=>{e.setQueryData(v,e=>S(e,t)),e.setQueryData([...y,t.appId],t)},onError:(t,n,r)=>{let i=r?.previousEntry;i&&e.setQueryData(v,e=>x(e,n.id,()=>i));let a=t instanceof Error?t.message:String(t);l.error(`${d(`panelAppsPreferenceUpdateFailed`)}: ${a}`)}})}function E(){let e=c();return u({mutationFn:e=>i.panelApps.recordPanelAppOpened(e),onSuccess:t=>{e.setQueryData(v,e=>S(e,t)),e.setQueryData([...y,t.appId],t)}})}function D(){let e=c();return u({mutationFn:e=>i.panelApps.grantClient(e),onSuccess:()=>{e.invalidateQueries({queryKey:v}),e.invalidateQueries({queryKey:y})}})}function O(){let e=c();return u({mutationFn:e=>i.panelApps.deletePanelApp(e),onSuccess:()=>{e.invalidateQueries({queryKey:v}),e.invalidateQueries({queryKey:y})}})}var k=class{constructor(e){this.authorizationManager=e,this.handleIframeMessage=({event:e,iframe:t})=>{this.isBridgeRequest(e.data)&&(!t?.contentWindow||e.source!==t.contentWindow||this.handleBridgeRequest({event:e,iframe:t},e.data))},this.handleBridgeRequest=async(e,t)=>{try{let n=this.getBridgeContext(t),r=await this.dispatchRequest(n,t);this.postResponse(e,{type:`nextclaw:panel-app-service-actions:response`,requestId:t.requestId,ok:!0,data:r})}catch(n){this.postResponse(e,{type:`nextclaw:panel-app-service-actions:response`,requestId:t.requestId,ok:!1,error:this.toBridgeError(n)})}},this.dispatchRequest=async(e,t)=>{switch(t.method){case`list`:return await i.serviceApps.listServiceActions({bridgeSessionToken:e.token});case`requestGrant`:return await this.confirmAndGrant(e,this.requireActionId(t));case`revokeGrant`:return await i.serviceApps.revokeServiceAction(this.requireActionId(t),{bridgeSessionToken:e.token});case`invoke`:return await this.invokeWithAuthorization(e,t);case`agent.send`:return await this.sendAgentMessageWithAuthorization(e,t);case`agent.generateObject`:return await this.generateAgentObjectWithAuthorization(e,t);case`verification.list`:return await i.serviceApps.listVerificationRecords({acceptanceId:t.payload?.acceptanceId,appId:t.payload?.appId,limit:t.payload?.limit,bridgeSessionToken:e.token});case`acceptance.status`:return await i.serviceApps.getPortableRuntimeAcceptanceStatus({appId:t.payload?.appId,locale:t.payload?.locale,bridgeSessionToken:e.token});case`acceptance.export`:return await i.serviceApps.exportPortableRuntimeAcceptance({appId:t.payload?.appId,locale:t.payload?.locale,bridgeSessionToken:e.token});default:throw Error(`Unsupported panel bridge method: ${String(t.method)}`)}},this.invokeWithAuthorization=async(e,t)=>{let n=this.requireActionId(t);try{return await i.serviceApps.invokeServiceAction(n,this.readOptionalRecord(t.payload?.input),{bridgeSessionToken:e.token})}catch(r){if(!(r instanceof s)||r.code!==`AUTHORIZATION_REQUIRED`)throw r;return await this.confirmAndGrant(e,n,this.readOptionalRecord(t.payload?.input)),await i.serviceApps.invokeServiceAction(n,this.readOptionalRecord(t.payload?.input),{bridgeSessionToken:e.token})}},this.sendAgentMessageWithAuthorization=async(e,t)=>{try{return await i.panelApps.sendAgentMessage(this.readAgentSendRequest(t),{bridgeSessionToken:e.token})}catch(n){if(!(n instanceof s)||n.code!==`AUTHORIZATION_REQUIRED`)throw n;return await this.confirmAndGrantAgentCapability(e,`agent:send`),await i.panelApps.sendAgentMessage(this.readAgentSendRequest(t),{bridgeSessionToken:e.token})}},this.generateAgentObjectWithAuthorization=async(e,t)=>{try{return await i.panelApps.generateAgentObject(this.readGenerateObjectRequest(t),{bridgeSessionToken:e.token})}catch(n){if(!(n instanceof s)||n.code!==`AUTHORIZATION_REQUIRED`)throw n;return await this.confirmAndGrantAgentCapability(e,`agent:generateObject`),await i.panelApps.generateAgentObject(this.readGenerateObjectRequest(t),{bridgeSessionToken:e.token})}},this.confirmAndGrantAgentCapability=async(e,t)=>{if(!await this.authorizationManager.requestAuthorization({panelAppId:e.appId,actions:[{actionId:t,actionTitle:t===`agent:send`?`Send agent message`:`Generate object`,actionDescription:t===`agent:send`?`Send a message to a NextClaw Agent session.`:`Send context to a NextClaw Agent session and receive a structured object.`,risk:`write`}]}))throw new s({code:`AUTHORIZATION_REJECTED`,message:`Permission rejected for ${t}.`});return await i.panelApps.grantAgentCapability(t,{bridgeSessionToken:e.token})},this.confirmAndGrant=async(e,t,n)=>{let r=await this.listGrantCandidateActions(e,t);if(!await this.authorizationManager.requestAuthorization({panelAppId:e.appId,actions:r.map(e=>({actionId:e.id,actionTitle:e.title,actionDescription:e.description,risk:e.risk})),inputPreview:this.createInputPreview(n)}))throw new s({code:`AUTHORIZATION_REJECTED`,message:`Permission rejected for ${t}.`});let a=await i.serviceApps.grantServiceActions(r.map(e=>e.id),{bridgeSessionToken:e.token}),o=a.grants.find(e=>e.actionId===t)??a.grants[0];if(!o)throw Error(`No permission grant returned for ${t}.`);return o},this.listGrantCandidateActions=async(e,t)=>{let n=await i.serviceApps.listServiceActions({bridgeSessionToken:e.token}),r=n.actions.find(e=>e.id===t);if(r&&r.grantState!==`not-granted`)return[r];let a=n.actions.filter(e=>e.grantState===`not-granted`),o=[...a.filter(e=>e.id===t),...a.filter(e=>e.id!==t)];return o.length>0?o:[{appId:r?.appId??t.split(`.`)[0]??t,description:r?.description,id:t,name:r?.name??t,risk:r?.risk??`dangerous`,title:r?.title}]},this.createInputPreview=e=>{if(!(!e||Object.keys(e).length===0))try{let t=JSON.stringify(e,null,2);return t.length>500?`${t.slice(0,500)}...`:t}catch{return`[unserializable input]`}},this.getBridgeContext=e=>({appId:e.appId,token:e.runtimeToken}),this.requireActionId=e=>{let t=e.payload?.actionId?.trim();if(!t)throw Error(`Panel bridge actionId is required.`);return t},this.readOptionalRecord=e=>{if(e!==void 0)return this.requireRecord(e,`input`)},this.readAgentSendRequest=e=>({payload:this.requireRecord(e.payload?.request,`agent.send request`)}),this.readGenerateObjectRequest=e=>({input:this.requireRecord(e.payload?.input,`generateObject input`)}),this.requireRecord=(e,t)=>{if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`${t} is required.`);return e},this.postResponse=(e,t)=>{e.iframe?.contentWindow?.postMessage(t,`*`)},this.toBridgeError=e=>e instanceof s?{code:e.code,message:e.message,details:e.details}:{message:e instanceof Error?e.message:String(e)},this.isBridgeRequest=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return t.type===`nextclaw:panel-app-service-actions:request`&&typeof t.requestId==`string`&&typeof t.runtimeToken==`string`&&typeof t.appId==`string`&&(t.method===`invoke`||t.method===`agent.send`||t.method===`agent.generateObject`||t.method===`acceptance.export`||t.method===`acceptance.status`||t.method===`list`||t.method===`requestGrant`||t.method===`revokeGrant`||t.method===`verification.list`)}}};function oe(){return`service-action-auth-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}var A=r((e,t)=>({pending:null,requestAuthorization:async n=>await new Promise(r=>{t().pending?.resolve(!1),e({pending:{id:oe(),...n,resolve:r}})}),resolveAuthorization:n=>{let{pending:r}=t();r&&(r.resolve(n),e({pending:null}))}})),se=class{constructor(){this.requestAuthorization=async e=>await A.getState().requestAuthorization(e)}},j=class{constructor(){this.serviceActionAuthorizationManager=new se,this.panelAppBridgeManager=new k(this.serviceActionAuthorizationManager)}},M=e(n(),1),N=t(),P=globalThis,F=P.__NEXTCLAW_PANEL_APP_HOST_CONTEXT__??(0,M.createContext)(null);P.__NEXTCLAW_PANEL_APP_HOST_CONTEXT__=F;function I({children:e,presenter:t}){let n=(0,M.useMemo)(()=>t??new j,[t]);return(0,N.jsx)(F.Provider,{value:n,children:e})}function L(){let e=(0,M.useContext)(F);if(!e)throw Error(`usePanelAppHostPresenter must be used inside PanelAppHostProvider`);return e}function R(){let e=L(),{isPending:t,mutateAsync:n}=D(),r=(0,M.useRef)(new Map);return{ensurePanelAppClientGrant:(0,M.useCallback)(async t=>{if(!t.clientDeclared||t.clientGranted)return!0;let i=r.current.get(t.appId);if(i)return await i;let a=(async()=>await e.serviceActionAuthorizationManager.requestAuthorization({panelAppId:t.appId,actions:[{actionId:`nextclaw.client`,actionTitle:d(`panelAppsClientGrantTitle`),actionDescription:d(`panelAppsClientGrantDescription`),risk:`dangerous`}]})?(await n(t.appId),!0):!1)();r.current.set(t.appId,a);try{return await a}finally{r.current.delete(t.appId)}},[n,e]),isPending:t}}function z(e){let t=c(),n=(0,M.useId)(),{ensurePanelAppClientGrant:r}=R(),a=!!(e?.clientDeclared&&!e.clientGranted),s=o({queryKey:[`panel-app-client-grant`,e?.appId??`none`,e?.clientGranted??!1],queryFn:async()=>await r(e),enabled:a,gcTime:0,retry:!1,staleTime:1/0}),l=e?a?s.isError?`error`:s.isPending?`checking`:s.data?`allowed`:`denied`:`allowed`:`idle`;return o({queryKey:[`panel-app-open`,n,e?.appId??`none`],queryFn:async()=>{let n=await i.panelApps.recordPanelAppOpened(e.id);return t.setQueryData(v,e=>S(e,n)),t.setQueryData([...y,n.appId],n),n},enabled:!!(e&&l===`allowed`),retry:!1,staleTime:1/0}),{retryClientGrant:s.refetch,state:l}}var B=`allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-downloads allow-pointer-lock allow-presentation`;function V(e){e?.focus({preventScroll:!0}),e?.contentWindow?.focus()}function H({appId:e,restorationScope:t,unavailableAction:n}){let r=L(),i=w(e),a=(0,M.useRef)(null),o=(0,M.useMemo)(()=>i.data,[i.data]),s=z(o),c=h({currentUrl:o?.contentPath??null,iframeRef:a,isEnabled:!!o,restorationKey:o?`panel-app:${t}:${o.appId}`:null});return(0,M.useEffect)(()=>{let e=e=>{r.panelAppBridgeManager.handleIframeMessage({event:e,iframe:a.current})};return window.addEventListener(`message`,e),()=>window.removeEventListener(`message`,e)},[r]),i.isLoading?(0,N.jsx)(U,{message:d(`panelAppsLoading`)}):i.isError?(0,N.jsx)(U,{message:i.error instanceof Error?i.error.message:d(`panelAppsLoadFailed`),actionLabel:d(`panelAppsTryAgain`),onAction:()=>void i.refetch()}):o?s.state===`checking`||s.state===`idle`?(0,N.jsx)(U,{message:d(`panelAppsCheckingPermission`)}):s.state===`denied`||s.state===`error`?(0,N.jsx)(U,{message:s.state===`denied`?d(`panelAppsPermissionDenied`):d(`panelAppsPermissionFailed`),actionLabel:d(`panelAppsTryAgain`),onAction:()=>void s.retryClientGrant()}):(0,N.jsx)(`div`,{className:`h-full min-h-0 bg-background`,children:(0,N.jsx)(`iframe`,{ref:a,src:o.contentPath,title:o.title,sandbox:B,className:`block h-full w-full border-0 bg-background`,onLoad:c,onPointerOver:e=>V(e.currentTarget)},o.appId)}):(0,N.jsx)(U,{message:d(`panelAppsMainUnavailable`),actionLabel:n?.label,onAction:n?.onAction})}function U({actionLabel:e,message:t,onAction:n}){return(0,N.jsx)(`div`,{className:`flex h-full min-h-0 items-center justify-center bg-background px-6 text-center`,children:(0,N.jsxs)(`div`,{className:`max-w-sm`,children:[(0,N.jsx)(`div`,{className:`mx-auto flex h-10 w-10 items-center justify-center rounded-lg bg-muted text-muted-foreground`,children:n?(0,N.jsx)(g,{className:`h-5 w-5`}):(0,N.jsx)(ee,{className:`h-5 w-5`})}),(0,N.jsx)(`p`,{className:`mt-3 text-sm leading-6 text-muted-foreground`,children:t}),e&&n?(0,N.jsx)(`button`,{type:`button`,className:`mt-4 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border`,onClick:n,children:e}):null]})})}function W({appId:e}){let t=w(e),n=(0,M.useMemo)(()=>t.data,[t.data]);return(0,M.useEffect)(()=>{let e=document.title;return document.title=`${n?.title??d(`panelAppsTitle`)} · NextClaw`,()=>{document.title=e}},[n?.title]),(0,N.jsx)(`main`,{className:`h-dvh min-h-0 w-full overflow-hidden bg-background`,children:(0,N.jsx)(H,{appId:e,restorationScope:`standalone`})})}function G(){let e=A(e=>e.pending),t=A(e=>e.resolveAuthorization);return(0,N.jsx)(m,{open:!!e,onOpenChange:e=>{e||t(!1)},children:(0,N.jsxs)(ne,{className:`[&>:last-child]:hidden`,children:[(0,N.jsxs)(p,{children:[(0,N.jsxs)(re,{className:`flex items-center gap-2`,children:[(0,N.jsx)(_,{className:`h-4 w-4 text-amber-600`}),d(`serviceActionAuthorizationTitle`)]}),(0,N.jsx)(ie,{children:d(`serviceActionAuthorizationDescription`)})]}),e?(0,N.jsxs)(`div`,{className:`space-y-3 rounded-md border border-border bg-muted/60 p-3 text-sm`,children:[(0,N.jsx)(K,{label:d(`serviceActionAuthorizationSource`),value:e.panelAppId}),(0,N.jsxs)(`div`,{className:`space-y-2`,children:[(0,N.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:d(`serviceActionAuthorizationActions`)}),(0,N.jsx)(`div`,{className:`max-h-64 space-y-2 overflow-auto`,children:e.actions.map(e=>(0,N.jsx)(ce,{action:e},e.actionId))})]}),e.inputPreview?(0,N.jsx)(K,{label:d(`serviceActionAuthorizationInput`),value:e.inputPreview}):null]}):null,(0,N.jsxs)(te,{className:`gap-2 sm:gap-0`,children:[(0,N.jsx)(a,{type:`button`,variant:`outline`,onClick:()=>t(!1),children:d(`serviceActionAuthorizationReject`)}),(0,N.jsx)(a,{type:`button`,onClick:()=>t(!0),children:d(`serviceActionAuthorizationAllow`)})]})]})})}function ce({action:e}){return(0,N.jsxs)(`div`,{className:`space-y-1 rounded border border-border bg-card px-3 py-2`,children:[(0,N.jsxs)(`div`,{className:`flex items-start justify-between gap-3`,children:[(0,N.jsx)(`div`,{className:`min-w-0 break-words text-xs font-medium text-foreground`,children:e.actionTitle??e.actionId}),(0,N.jsx)(`div`,{className:`shrink-0 rounded bg-amber-50 px-1.5 py-0.5 text-[11px] font-medium text-amber-700`,children:e.risk??`dangerous`})]}),(0,N.jsx)(`div`,{className:`break-words text-[11px] text-muted-foreground`,children:e.actionId}),e.actionDescription?(0,N.jsx)(`div`,{className:`break-words text-xs text-muted-foreground`,children:e.actionDescription}):null]})}function K({label:e,value:t}){return(0,N.jsxs)(`div`,{className:`grid grid-cols-[5rem_minmax(0,1fr)] gap-3`,children:[(0,N.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:e}),(0,N.jsx)(`div`,{className:`min-w-0 break-words text-xs text-foreground`,children:t})]})}var q=`nextclaw.ui.theme`,J=`work`,Y=[{value:`work`,labelKey:`themeWork`,appearance:`light`},{value:`night`,labelKey:`themeNight`,appearance:`dark`},{value:`charcoal`,labelKey:`themeCharcoal`,appearance:`dark`},{value:`island`,labelKey:`themeIsland`,appearance:`light`},{value:`natural`,labelKey:`themeNatural`,appearance:`light`},{value:`minimal`,labelKey:`themeMinimal`,appearance:`light`},{value:`warm`,labelKey:`themeWarm`,appearance:`light`},{value:`cool`,labelKey:`themeCool`,appearance:`light`},{value:`dawn`,labelKey:`themeDawn`,appearance:`light`},{value:`graphite`,labelKey:`themeGraphite`,appearance:`light`},{value:`probe`,labelKey:`themeProbe`,appearance:`light`}],le=Y.map(({value:e})=>e),ue=Y.map(({value:e,labelKey:t})=>({value:e,labelKey:t}));function X(e){return typeof e==`string`?le.includes(e)?e:e===`leaf`?`warm`:null:null}function Z(e){return Y.find(t=>t.value===e)?.appearance??`light`}var Q=new class{constructor(){this.activeTheme=J,this.initialized=!1,this.listeners=new Set,this.resolveInitialTheme=()=>{if(typeof window>`u`)return J;try{let e=X(window.localStorage.getItem(q));if(e)return e}catch{}return J},this.initializeTheme=()=>(this.initialized||=(this.activeTheme=this.resolveInitialTheme(),this.applyThemeAttribute(this.activeTheme),!0),this.activeTheme),this.getTheme=()=>this.initialized?this.activeTheme:this.initializeTheme(),this.setTheme=e=>{this.initializeTheme(),e!==this.activeTheme&&(this.activeTheme=e,this.applyThemeAttribute(this.activeTheme),this.saveTheme(this.activeTheme),this.listeners.forEach(e=>e(this.activeTheme)))},this.subscribeThemeChange=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)}),this.applyThemeAttribute=e=>{if(typeof document>`u`)return;let t=Z(e);document.documentElement.setAttribute(`data-theme`,e),document.documentElement.setAttribute(`data-theme-appearance`,t),document.documentElement.style.colorScheme=t},this.saveTheme=e=>{if(!(typeof window>`u`))try{window.localStorage.setItem(q,e)}catch{}}}};function de(){return Q.initializeTheme()}function fe(){return Q.getTheme()}function pe(e){Q.setTheme(e)}function me(e){return Q.subscribeThemeChange(e)}var $=(0,M.createContext)(null);function he({children:e}){let[t,n]=(0,M.useState)(()=>de());(0,M.useEffect)(()=>me(e=>{n(e)}),[]);let r=(0,M.useCallback)(e=>{pe(e),n(fe())},[]),i=(0,M.useMemo)(()=>({theme:t,setTheme:r}),[t,r]);return(0,N.jsx)($.Provider,{value:i,children:e})}function ge(){let e=(0,M.useContext)($);if(!e)throw Error(`useTheme must be used within ThemeProvider`);return e}export{ae as _,Z as a,_ as b,W as c,V as d,R as f,D as g,O as h,ue as i,H as l,j as m,ge as n,X as o,I as p,J as r,G as s,he as t,B as u,E as v,g as x,T as y};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{At as e,Ct as t,Dt as n,Et as r,Ot as i,St as a,Tt as o,_t as s,at as c,bn as l,dt as u,gt as d,ht as f,jn as p,kt as m,lt as h,mt as g,ot as _,rt as v,st as y,ut as b,vn as x,vt as S,wt as C,yn as w,yt as T}from"./api-
|
|
1
|
+
import{At as e,Ct as t,Dt as n,Et as r,Ot as i,St as a,Tt as o,_t as s,at as c,bn as l,dt as u,gt as d,ht as f,jn as p,kt as m,lt as h,mt as g,ot as _,rt as v,st as y,ut as b,vn as x,vt as S,wt as C,yn as w,yt as T}from"./api-DqzQ-_3w.js";import{s as E}from"./i18n-20rSH40D.js";function D(){return l({queryKey:[`config`],queryFn:h,staleTime:3e4,refetchOnWindowFocus:!0})}function O(){return l({queryKey:[`config-meta`],queryFn:b,staleTime:1/0})}function k(){return l({queryKey:[`product-analytics-status`],queryFn:g,staleTime:15e3,refetchOnWindowFocus:!0})}function A(){return l({queryKey:[`providers`],queryFn:s,staleTime:3e4,refetchOnWindowFocus:!0})}function j(){return l({queryKey:[`provider-templates`],queryFn:d,staleTime:1/0})}function M(e={}){return l({queryKey:[`provider-model-catalog`],queryFn:f,staleTime:3e4,refetchInterval:t=>e.pollWhileRefreshing&&t.state.data?.refreshing?1e3:!1,refetchOnMount:e.refreshOnMount?`always`:!0,refetchOnWindowFocus:!0})}function N(){return l({queryKey:[`config-schema`],queryFn:u,staleTime:1/0})}function P(){let e=p();return w({mutationFn:o,onSuccess:()=>{e.invalidateQueries({queryKey:[`config`]}),x.success(E(`configSaved`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function F(){let e=p();return w({mutationFn:({data:e})=>r(e),onSuccess:t=>{e.setQueryData([`config`],e=>e&&{...e,productAnalytics:t}),e.invalidateQueries({queryKey:[`product-analytics-status`]}),x.success(E(`configSavedApplied`))},onError:e=>{x.error(`${E(`configSaveFailed`)}: ${e.message}`)}})}function I(){let e=p();return w({mutationFn:({data:e})=>m(e),onSuccess:()=>{e.invalidateQueries({queryKey:[`config`]}),e.invalidateQueries({queryKey:[`config-meta`]}),x.success(E(`configSavedApplied`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function L(){let e=p();return w({mutationFn:({provider:e,data:t})=>n(e,t),onSuccess:(t,n)=>{e.setQueryData([`providers`],e=>e&&{providers:{...e.providers,[n.provider]:t}}),e.invalidateQueries({queryKey:[`providers`]}),e.invalidateQueries({queryKey:[`config`]}),n.silentSuccess||x.success(E(`configSaved`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function R(){let e=p();return w({mutationFn:({data:e})=>v(e??{}),onSuccess:()=>{e.invalidateQueries({queryKey:[`providers`]}),e.invalidateQueries({queryKey:[`config`]}),x.success(E(`configSaved`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function z(){let e=p();return w({mutationFn:({provider:e})=>c(e),onSuccess:()=>{e.invalidateQueries({queryKey:[`providers`]}),e.invalidateQueries({queryKey:[`config`]}),x.success(E(`configSaved`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function B(){return w({mutationFn:({provider:e,data:n})=>t(e,n)})}function V(){return w({mutationFn:({provider:e,data:t})=>_(e,t)})}function H(){return w({mutationFn:({provider:e,data:t})=>a(e,t)})}function U(){return w({mutationFn:({provider:e,data:t})=>T(e,t)})}function W(){return w({mutationFn:({provider:e})=>S(e)})}function G(){let e=p();return w({mutationFn:({channel:e,data:t})=>C(e,t),onSuccess:()=>{e.invalidateQueries({queryKey:[`config`]}),e.invalidateQueries({queryKey:[`config-meta`]}),x.success(E(`configSavedApplying`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function K(){let e=p();return w({mutationFn:({data:e})=>i(e),onSuccess:()=>{e.invalidateQueries({queryKey:[`config`]}),x.success(E(`configSavedApplied`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function q(){let t=p();return w({mutationFn:({data:t})=>e(t),onSuccess:()=>{t.invalidateQueries({queryKey:[`config`]}),x.success(E(`configSavedApplied`))},onError:e=>{x.error(E(`configSaveFailed`)+`: `+e.message)}})}function J(){return w({mutationFn:({actionId:e,data:t})=>y(e,t),onError:e=>{x.error(E(`error`)+`: `+e.message)}})}export{q as S,P as _,z as a,K as b,W as c,M as d,j as f,G as g,B as h,R as i,U as l,H as m,O as n,V as o,A as p,N as r,J as s,D as t,k as u,F as v,I as x,L as y};
|
package/dist/assets/{use-desktop-capabilities-DR9snbeo.js → use-desktop-capabilities-BkuRd2cY.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Lt as e,bn as t,jn as n,yn as r}from"./api-
|
|
1
|
+
import{Lt as e,bn as t,jn as n,yn as r}from"./api-DqzQ-_3w.js";var i=[`feature-controls`];function a(){return t({queryKey:i,queryFn:e.featureControls.get})}var o=new class{constructor(){this.getStatus=async()=>await e.capabilityAccess.getDesktopStatus(),this.listGrants=async()=>await e.capabilityAccess.listDesktopGrants(),this.grantAccess=async t=>await e.capabilityAccess.grantDesktopAccess(t),this.requestSystemPermission=async()=>await e.capabilityAccess.requestDesktopPermissions(),this.openSystemSettings=async()=>await e.capabilityAccess.openDesktopPermissionSettings(),this.revokeGrant=async t=>await e.capabilityAccess.revokeDesktopAccess(t)}},s=[`desktop-capability`,`status`],c=[`desktop-capability`,`grants`];function l(){return t({queryKey:s,queryFn:o.getStatus})}function u(){return d(a().data)}function d(e){return e?.desktopAutomation.available===!0}function f(){return t({queryKey:c,queryFn:o.listGrants})}function p(){let e=n();return r({mutationFn:o.requestSystemPermission,onSuccess:async()=>{await e.invalidateQueries({queryKey:s})}})}function m(){return r({mutationFn:o.openSystemSettings})}function h(){let e=n();return r({mutationFn:async e=>await o.revokeGrant(e),onSuccess:async()=>{await e.invalidateQueries({queryKey:c})}})}export{p as a,m as i,f as n,h as o,l as r,o as s,u as t};
|
package/dist/index.html
CHANGED
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
})();
|
|
259
259
|
</script>
|
|
260
260
|
<title>NextClaw</title>
|
|
261
|
-
<script type="module" crossorigin src="/assets/main-
|
|
261
|
+
<script type="module" crossorigin src="/assets/main-ky5Efhq1.js"></script>
|
|
262
262
|
<link rel="modulepreload" crossorigin href="/assets/chunk-DF37yw5_.js">
|
|
263
263
|
<link rel="modulepreload" crossorigin href="/assets/utils-CBZPzC_b.js">
|
|
264
264
|
<link rel="modulepreload" crossorigin href="/assets/client-BtGBwLTs.js">
|
|
@@ -274,8 +274,8 @@
|
|
|
274
274
|
<link rel="modulepreload" crossorigin href="/assets/dialog-Xe73hpQz.js">
|
|
275
275
|
<link rel="modulepreload" crossorigin href="/assets/popover-NkrFlWHp.js">
|
|
276
276
|
<link rel="modulepreload" crossorigin href="/assets/tooltip-DYcuWhuP.js">
|
|
277
|
-
<link rel="modulepreload" crossorigin href="/assets/i18n-
|
|
278
|
-
<link rel="modulepreload" crossorigin href="/assets/api-
|
|
277
|
+
<link rel="modulepreload" crossorigin href="/assets/i18n-20rSH40D.js">
|
|
278
|
+
<link rel="modulepreload" crossorigin href="/assets/api-DqzQ-_3w.js">
|
|
279
279
|
<link rel="modulepreload" crossorigin href="/assets/search-CUe41wjm.js">
|
|
280
280
|
<link rel="modulepreload" crossorigin href="/assets/book-open-xqEj1OLE.js">
|
|
281
281
|
<link rel="modulepreload" crossorigin href="/assets/boxes-D6uPnkGo.js">
|
|
@@ -284,37 +284,37 @@
|
|
|
284
284
|
<link rel="modulepreload" crossorigin href="/assets/plus-LA8-jpxP.js">
|
|
285
285
|
<link rel="modulepreload" crossorigin href="/assets/refresh-cw-h9Xq3LY3.js">
|
|
286
286
|
<link rel="modulepreload" crossorigin href="/assets/middleware-0o3JF3tS.js">
|
|
287
|
-
<link rel="modulepreload" crossorigin href="/assets/doc-browser-context-
|
|
287
|
+
<link rel="modulepreload" crossorigin href="/assets/doc-browser-context-TILuCY5c.js">
|
|
288
288
|
<link rel="modulepreload" crossorigin href="/assets/use-panel-app-scroll-restoration-ZnicIfo3.js">
|
|
289
|
-
<link rel="modulepreload" crossorigin href="/assets/doc-browser-
|
|
289
|
+
<link rel="modulepreload" crossorigin href="/assets/doc-browser-BJrre2S_.js">
|
|
290
290
|
<link rel="modulepreload" crossorigin href="/assets/circle-alert-lSa3LRvS.js">
|
|
291
291
|
<link rel="modulepreload" crossorigin href="/assets/loader-circle-K1BDdHR5.js">
|
|
292
|
-
<link rel="modulepreload" crossorigin href="/assets/confirm-dialog-
|
|
293
|
-
<link rel="modulepreload" crossorigin href="/assets/i18n-provider-
|
|
294
|
-
<link rel="modulepreload" crossorigin href="/assets/tag-chip-
|
|
292
|
+
<link rel="modulepreload" crossorigin href="/assets/confirm-dialog-BTKCWMn6.js">
|
|
293
|
+
<link rel="modulepreload" crossorigin href="/assets/i18n-provider-BPRq6S7O.js">
|
|
294
|
+
<link rel="modulepreload" crossorigin href="/assets/tag-chip-B1AYPOKL.js">
|
|
295
295
|
<link rel="modulepreload" crossorigin href="/assets/skeleton-Drvn83YW.js">
|
|
296
|
-
<link rel="modulepreload" crossorigin href="/assets/mcp-marketplace-page-
|
|
296
|
+
<link rel="modulepreload" crossorigin href="/assets/mcp-marketplace-page-FfIfEo6w.js">
|
|
297
297
|
<link rel="modulepreload" crossorigin href="/assets/katex-B26hhTOT.js">
|
|
298
298
|
<link rel="modulepreload" crossorigin href="/assets/puzzle-D6gulz7u.js">
|
|
299
299
|
<link rel="modulepreload" crossorigin href="/assets/trash-2-BPPYt2NV.js">
|
|
300
|
-
<link rel="modulepreload" crossorigin href="/assets/app-data-
|
|
300
|
+
<link rel="modulepreload" crossorigin href="/assets/app-data-C54N2p8b.js">
|
|
301
301
|
<link rel="modulepreload" crossorigin href="/assets/ellipsis-C5YXdOUx.js">
|
|
302
302
|
<link rel="modulepreload" crossorigin href="/assets/eye-C84inPcV.js">
|
|
303
303
|
<link rel="modulepreload" crossorigin href="/assets/tabs-BuE0qKMh.js">
|
|
304
304
|
<link rel="modulepreload" crossorigin href="/assets/use-viewport-layout-BS18drMT.js">
|
|
305
|
-
<link rel="modulepreload" crossorigin href="/assets/theme-provider-
|
|
306
|
-
<link rel="modulepreload" crossorigin href="/assets/use-desktop-capabilities-
|
|
305
|
+
<link rel="modulepreload" crossorigin href="/assets/theme-provider-DzvAi3pe.js">
|
|
306
|
+
<link rel="modulepreload" crossorigin href="/assets/use-desktop-capabilities-BkuRd2cY.js">
|
|
307
307
|
<link rel="modulepreload" crossorigin href="/assets/logo-badge-C-5-zim6.js">
|
|
308
308
|
<link rel="modulepreload" crossorigin href="/assets/card-BhfAZv5L.js">
|
|
309
|
-
<link rel="modulepreload" crossorigin href="/assets/use-config-
|
|
309
|
+
<link rel="modulepreload" crossorigin href="/assets/use-config-BEdbtpD4.js">
|
|
310
310
|
<link rel="modulepreload" crossorigin href="/assets/provider-models-hrHMcHqg.js">
|
|
311
|
-
<link rel="modulepreload" crossorigin href="/assets/app-presenter-provider-
|
|
312
|
-
<link rel="modulepreload" crossorigin href="/assets/provider-scoped-model-input-
|
|
311
|
+
<link rel="modulepreload" crossorigin href="/assets/app-presenter-provider-V0xnU_Wb.js">
|
|
312
|
+
<link rel="modulepreload" crossorigin href="/assets/provider-scoped-model-input-BQsugx1v.js">
|
|
313
313
|
<link rel="modulepreload" crossorigin href="/assets/settings-2-CE3ELQyk.js">
|
|
314
|
-
<link rel="modulepreload" crossorigin href="/assets/pwa-
|
|
315
|
-
<link rel="modulepreload" crossorigin href="/assets/login-page-
|
|
314
|
+
<link rel="modulepreload" crossorigin href="/assets/pwa-D_nZUwG0.js">
|
|
315
|
+
<link rel="modulepreload" crossorigin href="/assets/login-page-f8qQ4xKY.js">
|
|
316
316
|
<link rel="stylesheet" crossorigin href="/assets/app-presenter-provider-jNGlmBLe.css">
|
|
317
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
317
|
+
<link rel="stylesheet" crossorigin href="/assets/main-D2dWy8fM.css">
|
|
318
318
|
</head>
|
|
319
319
|
|
|
320
320
|
<body>
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="theme-color" content="#ffffff" />
|
|
7
7
|
<title>Panel App · NextClaw</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/panelStandalone-
|
|
8
|
+
<script type="module" crossorigin src="/assets/panelStandalone-By-6dDXS.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-DF37yw5_.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/utils-CBZPzC_b.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/client-BtGBwLTs.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-ca-nBW7U.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/i18n-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/api-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/i18n-20rSH40D.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/api-DqzQ-_3w.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/dist-BiIcnf9j.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/es2015-cuwbYI-l.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/dist-czPa8xFO.js">
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/assets/dialog-Xe73hpQz.js">
|
|
21
21
|
<link rel="modulepreload" crossorigin href="/assets/boxes-D6uPnkGo.js">
|
|
22
22
|
<link rel="modulepreload" crossorigin href="/assets/use-panel-app-scroll-restoration-ZnicIfo3.js">
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/theme-provider-
|
|
24
|
-
<link rel="modulepreload" crossorigin href="/assets/i18n-provider-
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/theme-provider-DzvAi3pe.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/i18n-provider-BPRq6S7O.js">
|
|
25
25
|
<link rel="stylesheet" crossorigin href="/assets/panelStandalone-Y91OJkgV.css">
|
|
26
26
|
</head>
|
|
27
27
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"zod": "^3.23.8",
|
|
36
36
|
"zustand": "^5.0.2",
|
|
37
37
|
"@nextclaw/agent-chat": "0.3.14",
|
|
38
|
-
"@nextclaw/
|
|
39
|
-
"@nextclaw/
|
|
38
|
+
"@nextclaw/agent-chat-ui": "0.9.1",
|
|
39
|
+
"@nextclaw/client-sdk": "0.12.3",
|
|
40
40
|
"@nextclaw/ncp": "0.11.0",
|
|
41
|
-
"@nextclaw/ncp-react": "0.5.28",
|
|
42
41
|
"@nextclaw/ncp-http-agent-client": "0.4.23",
|
|
42
|
+
"@nextclaw/ncp-react": "0.5.28",
|
|
43
43
|
"@nextclaw/ncp-toolkit": "0.6.25",
|
|
44
|
-
"@nextclaw/shared": "0.
|
|
44
|
+
"@nextclaw/shared": "0.6.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@testing-library/react": "^16.3.0",
|
|
@@ -11,12 +11,22 @@ export async function verifyVoiceDraftLifecycle(
|
|
|
11
11
|
start: vi.fn(), stop: vi.fn(), abort: vi.fn(),
|
|
12
12
|
onstart: null, onend: null, onresult: null, onerror: null,
|
|
13
13
|
};
|
|
14
|
-
const manager = new ChatVoiceInputManager(() => recognition);
|
|
14
|
+
const manager = new ChatVoiceInputManager(() => recognition, async () => null);
|
|
15
15
|
const send = vi.fn();
|
|
16
16
|
const rendered = renderHarness(manager, send);
|
|
17
17
|
try {
|
|
18
18
|
fireEvent.click(screen.getByRole('button', { name: /^(语音输入|Voice input)$/ }));
|
|
19
|
-
|
|
19
|
+
const voicePanel = screen.getByRole('region', { name: /^(语音输入|Voice input)$/ });
|
|
20
|
+
expect({ rounded: voicePanel.classList.contains('rounded-full'), fit: voicePanel.classList.contains('w-fit'), fixed: voicePanel.classList.contains('w-80') })
|
|
21
|
+
.toEqual({ rounded: true, fit: true, fixed: false });
|
|
22
|
+
const compactClass = voicePanel.className;
|
|
23
|
+
expect({ esc: voicePanel.textContent?.includes('Esc'), phase: manager.getSnapshot().phase })
|
|
24
|
+
.toEqual({ esc: false, phase: 'starting' });
|
|
25
|
+
await act(async () => { await Promise.resolve(); recognition.onstart?.(); });
|
|
26
|
+
expect(screen.getByRole('region', { name: /^(语音输入|Voice input)$/ })).toBe(voicePanel);
|
|
27
|
+
expect(voicePanel.className).toBe(compactClass);
|
|
28
|
+
expect({ starts: vi.mocked(recognition.start).mock.calls.length, stops: vi.mocked(recognition.stop).mock.calls.length, phase: manager.getSnapshot().phase })
|
|
29
|
+
.toEqual({ starts: 1, stops: 0, phase: 'recording' });
|
|
20
30
|
const textbox = screen.getByRole('textbox');
|
|
21
31
|
const before = textbox.textContent;
|
|
22
32
|
act(() => { recognition.onresult?.({ results: [{ isFinal: false, 0: { transcript: 'speaking' } }] }); });
|
|
@@ -25,7 +35,10 @@ export async function verifyVoiceDraftLifecycle(
|
|
|
25
35
|
act(() => { recognition.onresult?.({ results: [{ isFinal: false, 0: { transcript: 'speak' } }] }); });
|
|
26
36
|
await waitFor(() => expect(textbox.textContent).not.toContain('speaking'));
|
|
27
37
|
act(() => { recognition.onresult?.({ results: [{ isFinal: true, 0: { transcript: 'spoken' } }] }); });
|
|
28
|
-
|
|
38
|
+
expect(screen.queryByRole('button', { name: /^(取消|Cancel)$/ })).toBeNull();
|
|
39
|
+
fireEvent.keyDown(textbox, { key: 'Escape', code: 'Escape' });
|
|
40
|
+
expect(recognition.stop).toHaveBeenCalledOnce();
|
|
41
|
+
expect(voicePanel.className).toBe(compactClass);
|
|
29
42
|
act(() => { recognition.onend?.(); });
|
|
30
43
|
await waitFor(() => expect(textbox.textContent).toContain('spoken'));
|
|
31
44
|
expect(textbox.textContent).toContain(before);
|
|
@@ -34,13 +47,13 @@ export async function verifyVoiceDraftLifecycle(
|
|
|
34
47
|
fireEvent.click(screen.getByRole('button', { name: /^(语音输入|Voice input)$/ }));
|
|
35
48
|
const lateResult = recognition.onresult;
|
|
36
49
|
const lateEnd = recognition.onend;
|
|
37
|
-
fireEvent.
|
|
50
|
+
fireEvent.keyDown(textbox, { key: 'Escape', code: 'Escape' });
|
|
38
51
|
act(() => {
|
|
39
52
|
lateResult?.({ results: [{ isFinal: true, 0: { transcript: 'discard' } }] }); lateEnd?.();
|
|
40
53
|
});
|
|
41
54
|
expect(textbox.textContent).not.toContain('discard');
|
|
42
55
|
fireEvent.click(screen.getByRole('button', { name: /^(语音输入|Voice input)$/ }));
|
|
43
|
-
act(() => { recognition.onstart?.(); });
|
|
56
|
+
await act(async () => { await Promise.resolve(); recognition.onstart?.(); });
|
|
44
57
|
fireEvent.click(screen.getByRole('button', { name: /^(语音快捷键|Voice shortcut)$/ }));
|
|
45
58
|
act(() => { recognition.onerror?.({ error: 'no-speech' }); });
|
|
46
59
|
await waitFor(() => expect(screen.getByRole('button', { name: /绑定按键|Bind a key/ })).toBeTruthy());
|
|
@@ -4,9 +4,11 @@ import { IconActionButton } from '@/shared/components/ui/actions/icon-action-but
|
|
|
4
4
|
import { t } from '@/shared/lib/i18n';
|
|
5
5
|
import type { ChatVoiceInputManager, VoiceError, VoiceSnapshot } from '@/features/chat/managers/chat-voice-input.manager';
|
|
6
6
|
import { ChatVoiceShortcutControl } from './chat-voice-shortcut-control';
|
|
7
|
+
import { voicePermissionInstructionKeys } from '@/features/chat/utils/chat-voice-permissions.utils';
|
|
7
8
|
|
|
8
9
|
const ERROR_KEYS: Record<VoiceError, string> = {
|
|
9
10
|
unsupported: 'chatInputVoiceUnsupported', permission: 'chatInputVoicePermissionDenied',
|
|
11
|
+
'service-denied': 'chatInputVoiceServiceDenied', 'no-device': 'chatInputVoiceNoDevice', 'device-busy': 'chatInputVoiceDeviceBusy',
|
|
10
12
|
network: 'chatInputVoiceNetwork', 'audio-capture': 'chatInputVoiceDevice',
|
|
11
13
|
'no-speech': 'chatInputVoiceNoSpeech', interrupted: 'chatInputVoiceInterrupted',
|
|
12
14
|
timeout: 'chatInputVoiceTimeout', failed: 'chatInputVoiceFailed',
|
|
@@ -21,39 +23,60 @@ export function ChatVoiceInputPanel({ manager, snapshot, onStart, desktop }: {
|
|
|
21
23
|
const { phase, error, seconds } = snapshot;
|
|
22
24
|
if (phase === 'ready') return desktop ? <VoiceShortcutPanel onClose={manager.cancel} /> : null;
|
|
23
25
|
const busy = phase === 'starting' || phase === 'stopping';
|
|
26
|
+
const microphoneError = ['permission', 'audio-capture', 'no-device', 'device-busy'].includes(error ?? '');
|
|
24
27
|
const status = {
|
|
25
28
|
idle: 'chatInputVoice', ready: 'chatInputVoiceShortcut', starting: 'chatInputVoiceStarting',
|
|
26
29
|
recording: 'chatInputVoiceRecording', stopping: 'chatInputVoiceFinishing', error: 'chatInputVoiceFailed',
|
|
27
30
|
}[phase];
|
|
28
31
|
return (
|
|
29
32
|
<div role='region' aria-label={t('chatInputVoice')}
|
|
30
|
-
className={'max-h-[var(--radix-popover-content-available-height,24rem)] max-w-full overflow-
|
|
31
|
-
(error ? 'w-80 rounded-xl p-3' : 'rounded-full px-3 py-1.5')}
|
|
33
|
+
className={'flex max-h-[var(--radix-popover-content-available-height,24rem)] max-w-full flex-col overflow-hidden border border-border bg-popover text-popover-foreground shadow-lg ' +
|
|
34
|
+
(phase === 'error' ? 'w-80 rounded-xl p-3' : 'w-fit rounded-full px-3 py-1.5')}
|
|
32
35
|
onKeyDown={(event) => {
|
|
33
36
|
if (event.key === 'Escape' && !event.nativeEvent.isComposing) {
|
|
34
|
-
event.stopPropagation(); manager.
|
|
37
|
+
event.stopPropagation(); manager.saveAndClose();
|
|
35
38
|
}
|
|
36
39
|
}}>
|
|
37
|
-
<div className='flex items-center gap-2'>
|
|
40
|
+
<div className='flex shrink-0 items-center gap-2'>
|
|
38
41
|
{busy ? <LoaderCircle className='h-4 w-4 shrink-0 motion-safe:animate-spin text-muted-foreground' />
|
|
39
42
|
: <Mic className={'h-4 w-4 shrink-0 ' + (phase === 'recording' ? 'text-destructive' : '')} />}
|
|
40
|
-
<span role='status' className='min-w-0 flex-1 text-xs font-medium'
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
<span role='status' className='grid min-w-0 flex-1 text-xs font-medium'
|
|
44
|
+
title={phase === 'starting' ? t('chatInputVoicePermissionPurpose') : undefined}>
|
|
45
|
+
<span className='col-start-1 row-start-1 truncate'>{t(status)}</span>
|
|
46
|
+
{phase !== 'error' ? ['chatInputVoiceStarting', 'chatInputVoiceRecording', 'chatInputVoiceFinishing'].map((key) =>
|
|
47
|
+
<span key={key} aria-hidden='true' className='invisible col-start-1 row-start-1'>{t(key)}</span>) : null}
|
|
48
|
+
</span>
|
|
49
|
+
{phase !== 'error' ? <span aria-hidden={phase !== 'recording'} data-recording={phase === 'recording'}
|
|
50
|
+
className='w-7 text-right text-xs tabular-nums text-muted-foreground data-[recording=false]:invisible'>{seconds}s</span> : null}
|
|
51
|
+
{phase !== 'error' ? <IconActionButton icon={<Square className='h-3 w-3' />}
|
|
52
|
+
label={t('chatInputVoiceSaveAndClose')} tooltip={t('chatInputVoiceEscapeHint')} disabled={phase === 'stopping'} onClick={manager.saveAndClose} /> : null}
|
|
45
53
|
{desktop ? <IconActionButton icon={<Settings className='h-4 w-4' />}
|
|
46
54
|
label={t('chatInputVoiceShortcut')} disabled={busy} onClick={manager.openSettings} /> : null}
|
|
47
55
|
</div>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
<div className='min-h-0 overflow-y-auto'>
|
|
57
|
+
{phase === 'starting' ? <p className='sr-only'>{t('chatInputVoicePermissionPurpose')}</p> : null}
|
|
58
|
+
{error ? <VoiceErrorDetails error={error} microphoneError={microphoneError} text={snapshot.text} /> : null}
|
|
59
|
+
</div>
|
|
60
|
+
{error ? <Button className='mt-2 shrink-0 self-start' size='sm' disabled={error === 'unsupported'} onClick={onStart}>
|
|
61
|
+
{t(microphoneError ? 'chatInputVoiceCheckAndRetry' : 'chatInputVoiceRetry')}
|
|
62
|
+
</Button> : null}
|
|
63
|
+
{phase === 'error' ? <Button className='mt-2 shrink-0 self-start' variant='ghost' size='sm'
|
|
64
|
+
title={t('chatInputVoiceEscapeHint')} onClick={manager.saveAndClose}>{t('chatInputVoiceSaveAndClose')}</Button> : null}
|
|
53
65
|
</div>
|
|
54
66
|
);
|
|
55
67
|
}
|
|
56
68
|
|
|
69
|
+
function VoiceErrorDetails({ error, microphoneError, text }: { error: VoiceError; microphoneError: boolean; text: string }) {
|
|
70
|
+
return <>
|
|
71
|
+
<p role='alert' className='mt-2 text-xs leading-5 text-destructive'>{t(ERROR_KEYS[error])}</p>
|
|
72
|
+
{microphoneError ? <ol className='mt-2 list-decimal space-y-1 pl-4 text-xs leading-5 text-muted-foreground'>
|
|
73
|
+
{voicePermissionInstructionKeys(navigator.userAgent).map((key) => <li key={key}>{t(key)}</li>)}
|
|
74
|
+
<li>{t('chatInputVoicePermissionRetryHint')}</li>
|
|
75
|
+
</ol> : null}
|
|
76
|
+
{text ? <p className='mt-1 text-xs text-muted-foreground'>{t('chatInputVoiceReviewHint')}</p> : null}
|
|
77
|
+
</>;
|
|
78
|
+
}
|
|
79
|
+
|
|
57
80
|
function VoiceShortcutPanel({ onClose }: { onClose: () => void }) {
|
|
58
81
|
return <div role='region' aria-label={t('chatInputVoiceShortcut')}
|
|
59
82
|
className='max-h-[var(--radix-popover-content-available-height,24rem)] w-80 max-w-full overflow-y-auto rounded-xl border border-border bg-popover p-3 text-popover-foreground shadow-lg'
|
|
@@ -458,12 +458,12 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
|
|
|
458
458
|
addMenuLabel: t('chatInputAdd'),
|
|
459
459
|
leadingSlot: <>
|
|
460
460
|
<IconActionButton icon={voice.phase === 'recording' ? <Square className='h-4 w-4' /> : <Mic className='h-4 w-4' />}
|
|
461
|
-
label={t(voice.phase === 'recording' ? '
|
|
461
|
+
label={t(voice.phase === 'recording' ? 'chatInputVoiceSaveAndClose' : 'chatInputVoice')}
|
|
462
|
+
tooltip={voice.phase === 'recording' ? t('chatInputVoiceEscapeHint') : undefined}
|
|
462
463
|
size='lg'
|
|
463
464
|
disabled={inputDisabled || voice.phase === 'stopping'}
|
|
464
465
|
aria-pressed={voice.phase === 'recording'}
|
|
465
|
-
onClick={() => voice.phase === 'recording' ? voiceManager.
|
|
466
|
-
: voice.phase === 'starting' ? voiceManager.cancel() : startVoice()} />
|
|
466
|
+
onClick={() => voice.phase === 'recording' || voice.phase === 'starting' ? voiceManager.saveAndClose() : startVoice()} />
|
|
467
467
|
</>,
|
|
468
468
|
selects: [],
|
|
469
469
|
trailingSelects: toolbarSelects,
|