openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
176 lines (173 loc) • 20.4 kB
JavaScript
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./mcp-app-view-registration-f68t3LcT.js","./gateway-runtime-Cr51Pi7N.js","./session-snapshot-database-BurLa_ut.js","./control-ui-boot-shared-Dfs-gZII.js","./control-ui-boot-shared-Sj2zqjOr.js","./control-ui-boot-shared-em9-jnbz.js","./plugin-contracts-runtime-CNCaYops.js","./control-ui-boot-shared-D4XEBdOG.js","./config-runtime-BecGLziM.js","./markdown-runtime-ORQpioyT.js","./control-ui-boot-shared-xfDhIVun.js","./control-ui-boot-shared-styles-BaQ2sAfZ.js","./sidebar-update-runtime-DonYLwN5.js","./mcp-app-routing-BHjVXpEf.js","./control-ui-boot-chat-C8hdsB2M.js","./control-ui-boot-chat-VeeaXyJz.js","./control-ui-boot-chat-Ck7W_FPO.js","./control-ui-boot-chat-DxoNF0Ad.js","./chat-transcript-styles-Dzr3t9kA.js","./control-ui-boot-chat-styles-BRrYkFSc.js","./mcp-app-confirm-C5ZrOS-h.js","./control-ui-core-yIvVj14Y.css","./control-ui-boot-shared-styles-DKyMAG_6.css","./control-ui-boot-shared-CXHNz0Bh.css","./sidebar-update-runtime-ah7rgQE7.css","./chat-transcript-styles-B_dd5Ueo.css","./control-ui-boot-chat-styles-C_Ls1c0m.css","./control-ui-boot-chat-mLz7NUiY.css"])))=>i.map(i=>d[i]);
import{n as e}from"./rolldown-runtime-8BhlS34s.js";import{Ac as t,Ci as n,Cu as r,Eu as i,Tu as a,W as o,du as s,ju as c,kc as l,m as u,p as d,pu as f,z as p}from"./control-ui-core-BncvAZjR.js";import{Ti as m,cr as h,na as g,ra as _,sr as v,wi as y}from"./control-ui-core-9qYjK5B2.js";import{$ as b,C as x,T as S,X as C,Y as w,ct as T,nt as E,ut as D}from"./lit-runtime-B8uOr6OX.js";import{Qn as O}from"./control-ui-boot-shared-umGE-sEP.js";import{gn as ee,hn as te,mn as k}from"./control-ui-boot-shared-DJkUdZmL.js";import{Xr as A,Zr as j,ii as M,oa as N,sa as P}from"./ui-DuRgK6kL.js";import{$a as F,Ar as I,Er as L,Ja as R,Qa as z,Xa as B,kr as V}from"./control-ui-boot-shared-D3oo_6Fq.js";import{ar as H,cr as U,lr as W,or as G,sr as K,ur as q}from"./control-ui-boot-chat-XcDaYmck.js";var J;function Y(){return(Y=e((()=>{j(),w(),E(),x(),m(),h(),i(),f(),u(),a(),t(),V(),P(),J=class extends r{constructor(){super(),this.requestId=``,this.sessionKey=``,this.selected=new Set,this.disabled=!1,this.busy=!1,this.error=``,this.uploaded=[],this.preview=null,this.generation=0,this.identity=``,new l(this).watchStore(()=>this.context?.gateway)}key(){return JSON.stringify([this.requestId,this.question?.questionId,this.question?.resource?.viewId,this.sessionKey,this.agentId,this.context?v(this.context.gateway).key:-1])}willUpdate(){let e=this.key();(e!==this.identity||this.disabled)&&(this.operationAbort?.abort(),this.generation++,e!==this.identity&&(this.uploaded=[]),this.identity=e,this.busy=!1,this.error=``,this.preview=null)}disconnectedCallback(){this.operationAbort?.abort(),this.generation++,super.disconnectedCallback()}async operation(e){let t=this.context,n=t?.gateway.snapshot.client,r=this.question;if(!t||t.gateway.snapshot.phase!==`connected`||!n||!r?.resource?.viewId||this.disabled||this.busy||!this.sessionKey)return;let i=++this.generation,a=new AbortController;this.operationAbort=a;let o=v(t.gateway).key,c=this.key(),l=()=>this.isConnected&&!this.disabled&&i===this.generation&&c===this.key()&&t.gateway.snapshot.client===n&&v(t.gateway).key===o;this.busy=!0,this.error=``;try{let t=await e(l);if(!l())return;let i=await n.request(`mcp.app.formResource`,{sessionKey:this.sessionKey,agentId:this.agentId,viewId:r.resource.viewId,requestId:this.requestId,questionId:r.questionId,...t},{signal:a.signal});if(!l())return;if(`resources`in i){if(!Array.isArray(i.resources)||i.resources.length>64||!i.resources.every(e=>typeof e.uri==`string`&&e.uri.length<=2048&&typeof e.name==`string`))throw Error(`Invalid resource upload result`);let e=new Map([...this.uploaded,...i.resources].map(e=>[e.uri,e]));if(e.size>64||!r.multiSelect&&i.resources.length!==1)throw Error(`Invalid resource upload count`);this.uploaded=[...e.values()];let t=r.multiSelect?new Set(this.selected):new Set;for(let e of i.resources)t.add(e.uri);this.dispatchEvent(new CustomEvent(`resource-selection`,{detail:{values:[...t]},bubbles:!0,composed:!0}))}else if(`preview`in i){if(i.preview.viewId){if(await N(()=>import(`./mcp-app-view-registration-f68t3LcT.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]),import.meta.url),!l())return;this.preview={viewId:i.preview.viewId}}else{let e=i.preview.contents?.map(e=>e.text??``).join(`
`)??``,t=i.preview.contents?.flatMap(e=>{let t=e.blob&&e.mimeType?`data:${e.mimeType};base64,${e.blob}`:void 0;return O(t)?[t]:[]}).slice(0,4);this.preview={text:e.slice(0,65536),images:t}}}}catch(e){l()&&(this.error=s(e))}finally{l()&&(this.busy=!1,this.operationAbort=void 0)}}async upload(e){let t=Array.from(e.files??[]);e.value=``,t.length&&await this.operation(async e=>{d(this.context?.config);let r=I(this.context?.gateway.snapshot.hello?.policy);if(!r||t.length+this.uploaded.length>64||L(t,r,0).length!==t.length)throw Error(c(`chat.questions.resourceUploadTooLarge`));let i=[];for(let r of t){let t=new Uint8Array(await r.arrayBuffer());if(!e())throw Error(`Resource upload cancelled`);d(this.context?.config),i.push({name:r.name,mimeType:r.type||`application/octet-stream`,content:n(t),...r.webkitRelativePath?{relativePath:r.webkitRelativePath}:{}})}return{action:`upload`,files:i}})}render(){let e=this.question,t=e?.resource;return!e||!t?.viewId?C:b`
<div class="chat-question-panel__resource-actions">
${e.options.map((e,n)=>e.preview&&(t.selection!==`implicit`||this.selected.has(e.value??e.label))?b`<button class="btn btn--sm" type="button" ?disabled=${this.disabled||this.busy} =${()=>this.operation(async()=>({action:`preview`,optionIndex:n}))}>${c(`chat.questions.resourcePreview`)}: ${e.label}</button>`:C)}
${t.userOptions?b`<label class="field"
><span>${e.header}</span
><input
type="file"
aria-label=${e.header}
?multiple=${e.multiSelect||t.userOptions.kind===`directory`}
accept=${S(t.userOptions.accept?.join(`,`))}
?webkitdirectory=${t.userOptions.kind===`directory`}
?disabled=${this.disabled||this.busy}
=${e=>{e.currentTarget instanceof HTMLInputElement&&this.upload(e.currentTarget)}}
/></label>`:C}
${this.uploaded.filter(e=>this.selected.has(e.uri)).map(e=>b`<div>${e.name}<button class="btn btn--sm" type="button" ?disabled=${this.disabled||this.busy} =${()=>this.dispatchEvent(new CustomEvent(`resource-selection`,{detail:{values:[...this.selected].filter(t=>t!==e.uri)},bubbles:!0,composed:!0}))}>${c(`common.remove`)}</button></div>`)}
${this.busy?b`<div role="status">${c(`common.loading`)}</div>`:C}
${this.error?b`<div role="alert">${this.error}</div>`:C}
${this.preview?.viewId?b`<mcp-app-view
.sessionKey=${this.sessionKey}
.viewId=${this.preview.viewId}
></mcp-app-view>`:this.preview?b`${this.preview.images?.map(t=>b`<img class="chat-question-panel__resource-image" src=${t} alt=${e.header} />`)}
<pre class="chat-question-panel__resource-preview">${this.preview.text}</pre>`:C}
</div>
`}},M([A({context:y,subscribe:!0})],J.prototype,`context`,void 0),M([D({attribute:!1})],J.prototype,`question`,void 0),M([D({attribute:!1})],J.prototype,`requestId`,void 0),M([D({attribute:!1})],J.prototype,`sessionKey`,void 0),M([D({attribute:!1})],J.prototype,`agentId`,void 0),M([D({attribute:!1})],J.prototype,`selected`,void 0),M([D({type:Boolean})],J.prototype,`disabled`,void 0),M([T()],J.prototype,`busy`,void 0),M([T()],J.prototype,`error`,void 0),M([T()],J.prototype,`uploaded`,void 0),M([T()],J.prototype,`preview`,void 0),customElements.get(`openclaw-chat-question-resource`)||customElements.define(`openclaw-chat-question-resource`,J)})))()}function X(e){return e?b`<div class="chat-question-panel__external">
<a
class="btn btn--sm"
href=${e}
target=${k}
rel=${te()}
>
${g.externalLink} ${c(`chat.questions.openLink`)}
</a>
<span class="muted">${c(`chat.questions.externalStepHint`)}</span>
</div>`:C}function Z(){return(Z=e((()=>{w(),_(),i(),ee()})))()}var Q;function $(){return($=e((()=>{w(),E(),_(),i(),o(),a(),z(),H(),Y(),Z(),Q=class extends r{constructor(...e){super(...e),this.currentQuestionIndex=0,this.pendingAction=null,this.requestKey=null,this.collapsed=!1,this.focusAfterUpdate=!1,this.answerTextarea=null,this.measuredAnswer=null}setCollapsed(e){if(this.props?.onCollapsedChange){this.props.onCollapsedChange(e);return}this.collapsed=e,this.focusAfterUpdate=!e,this.requestUpdate()}willUpdate(){let e=this.props?.model,t=e?.requestKey??null,n=e?.collapsed??!1;t===this.requestKey?this.props?.onCollapsedChange&&(this.collapsed&&!n&&(this.focusAfterUpdate=!0),this.collapsed=n):(this.requestKey=t,e&&G(e.questions,e.drafts),this.currentQuestionIndex=0,this.pendingAction=null,this.collapsed=n,this.focusAfterUpdate=!n&&e?.autoFocus!==!1)}disconnectedCallback(){super.disconnectedCallback(),this.answerTextarea&&=(B(this.answerTextarea),null)}updated(){let e=this.querySelector(`.chat-question-panel__textarea`);this.answerTextarea!==e&&(this.answerTextarea&&B(this.answerTextarea),this.answerTextarea=e,this.measuredAnswer=null,e&&F(e)),e&&this.measuredAnswer!==e.value&&(R(e),this.measuredAnswer=e.value),this.focusAfterUpdate&&!this.collapsed&&(this.focusAfterUpdate=!1,this.querySelector(`.chat-question-panel`)?.focus({preventScroll:!0}))}answerValues(e,t){return K(e.drafts.get(t.questionId),t)}buildAnswers(e){return Object.fromEntries(e.questions.map(t=>[t.questionId,this.answerValues(e,t)]))}updateDraft(e,t,n){e.drafts.set(t.questionId,n),this.requestUpdate(),this.props?.onChange?.()}focusPanel(){this.updateComplete.then(()=>this.querySelector(`.chat-question-panel`)?.focus({preventScroll:!0}))}toggleOption(e,t,n,r=!0){let i=e.drafts.get(t.questionId),a=new Set(t.multiSelect?i?.selected:[]);t.multiSelect&&a.has(n)?a.delete(n):a.add(n),this.updateDraft(e,t,{selected:a,freeText:t.multiSelect?i?.freeText??``:``}),r&&!t.multiSelect&&this.currentQuestionIndex<e.questions.length-1&&(this.currentQuestionIndex+=1,this.focusPanel())}setFreeText(e,t,n){let r=e.drafts.get(t.questionId);this.updateDraft(e,t,{selected:!t.multiSelect&&(U(t)?n:n.trim())?new Set:r?.selected??new Set,freeText:n})}async resolve(e,t){if(e.disabled||e.submitting||this.pendingAction)return;let n;if(t===`submit`){let t=this.props?.onSubmit;if(!t||!e.questions.every(t=>t.allowEmpty||this.answerValues(e,t).length>0))return;n=()=>t(this.buildAnswers(e))}else{let e=this.props?.onSkip;if(!e)return;n=e}let r={kind:t};this.pendingAction=r;try{await n()}catch{}finally{this.pendingAction===r&&(this.pendingAction=null)}}advanceOrSubmit(e,t){if(t.allowEmpty||this.answerValues(e,t).length!==0){if(this.currentQuestionIndex<e.questions.length-1){this.currentQuestionIndex+=1,this.focusPanel();return}this.resolve(e,`submit`)}}goBack(){this.currentQuestionIndex!==0&&(--this.currentQuestionIndex,this.focusPanel())}handleKeyDown(e,t,n,r){if(r||e.isComposing||e.keyCode===229)return;if(e.target instanceof HTMLTextAreaElement){e.key===`Enter`&&(e.metaKey||e.ctrlKey)&&!e.altKey&&!e.shiftKey&&(n.allowEmpty||this.answerValues(t,n).length>0)&&(e.preventDefault(),this.advanceOrSubmit(t,n));return}if(e.metaKey||e.ctrlKey||e.altKey||e.target instanceof HTMLAnchorElement)return;if(e.target instanceof HTMLInputElement){e.key===`Enter`&&(n.allowEmpty||this.answerValues(t,n).length>0)&&(e.preventDefault(),this.advanceOrSubmit(t,n));return}if(e.target instanceof HTMLButtonElement&&e.target.getAttribute(`role`)===`radio`&&[`ArrowDown`,`ArrowLeft`,`ArrowRight`,`ArrowUp`,`End`,`Home`].includes(e.key)){e.preventDefault();let r=Number(e.target.dataset.optionIndex??`0`),i=n.options.length-1,a=e.key===`Home`?0:e.key===`End`?i:e.key===`ArrowLeft`||e.key===`ArrowUp`?(r-1+n.options.length)%n.options.length:(r+1)%n.options.length,o=n.options[a];if(!o)return;let s=this.currentQuestionIndex;this.toggleOption(t,n,o.value??o.label,!1),this.currentQuestionIndex===s&&this.updateComplete.then(()=>this.querySelector(`.chat-question-panel__option[data-option-index="${a}"]`)?.focus({preventScroll:!0}));return}let i=Number(e.key)-1,a=this.querySelector(`.chat-question-panel__option[data-option-index="${i}"]`);if(i>=0&&i<9&&a){e.preventDefault(),a.click();return}if(n.options.length>0&&n.options.length<9&&!n.resource&&n.isOther&&i===n.options.length){e.preventDefault(),this.querySelector(`.chat-question-panel__other`)?.focus({preventScroll:!0});return}e.key===`Enter`&&!(e.target instanceof HTMLButtonElement)&&(n.allowEmpty||this.answerValues(t,n).length>0)&&(e.preventDefault(),this.advanceOrSubmit(t,n))}render(){let e=this.props;if(!e)return C;let{model:t}=e,n=t.questions[this.currentQuestionIndex];if(!n)return C;let r=t.disabled||t.submitting||this.pendingAction!==null,i=this.currentQuestionIndex===t.questions.length-1,a=n.allowEmpty||this.answerValues(t,n).length>0,o=t.drafts.get(n.questionId),s=`${this.currentQuestionIndex+1}/${t.questions.length}`,l=t.requestPosition?`${t.requestPosition.current}/${t.requestPosition.total}`:null,u=l?b`<div class="chat-question-panel__request-nav">
<button
type="button"
aria-label=${c(`common.previous`)}
=${e.onPreviousRequest}
>
${g.chevronLeft}
</button>
<span>${l}</span>
<button type="button" aria-label=${c(`common.next`)} =${e.onNextRequest}>
${g.chevronRight}
</button>
</div>`:C;return this.collapsed?b`
<section
class="chat-question-panel chat-question-panel--collapsed"
aria-label=${t.title}
>
<button
class="chat-question-panel__collapsed-button"
type="button"
=${()=>this.setCollapsed(!1)}
aria-label=${c(`chat.questions.expand`)}
aria-expanded="false"
>
<span
><strong>${t.title}</strong> ·
${t.collapsedLabel?b`${t.collapsedLabel} · ${n.question}`:n.header}</span
>
${t.collapsedLabel?C:b`<span class="chat-question-panel__progress">${s}</span>`}
<span class="chat-question-panel__chevron">${g.chevronDown}</span>
</button>
${u}
</section>
`:b`
<section
class="chat-question-panel"
role="group"
aria-label=${t.title}
tabindex="0"
=${e=>this.handleKeyDown(e,t,n,r)}
>
<div class="chat-question-panel__topline">
<div class="chat-question-panel__title">${t.title}</div>
${u}
<span class="chat-question-panel__progress">${s}</span>
<button
class="chat-question-panel__collapse"
type="button"
=${()=>this.setCollapsed(!0)}
aria-label=${c(`chat.questions.collapse`)}
aria-expanded="true"
>
${g.chevronDown}
</button>
</div>
<div class="chat-question-panel__heading">
<span class="chat-question-panel__prompt">${n.question}</span>
</div>
${X(n.url)}
${q({question:n,selected:o?.selected??new Set,disabled:r,onSelect:e=>this.toggleOption(t,n,e)})}
${n.secretStore?b`
<div class="chat-question-panel__store">
<div class="chat-question-panel__store-requester">
${c(`chat.questions.storeRequestedBy`,{agent:t.agentId??c(`common.unknown`),session:t.sessionKey??c(`common.unknown`)})}
</div>
<div class="chat-question-panel__store-entry">
${c(`chat.questions.storeEntry`,{name:n.secretStore.name,kind:n.secretStore.kind===`secret`?c(`secretsStore.protectedSecret`):c(`secretsStore.agentReadable`)})}
</div>
${n.secretStore.reason?b`<div class="chat-question-panel__store-reason">
${n.secretStore.reason}
</div>`:C}
${n.secretStoreExisting?b`<div class="chat-question-panel__store-replacement">
${n.secretStoreExisting.updatedBy?c(`chat.questions.storeReplacementBy`,{name:n.secretStore.name,updated:p(n.secretStoreExisting.updatedAtMs),updatedBy:n.secretStoreExisting.updatedBy}):c(`chat.questions.storeReplacement`,{name:n.secretStore.name,updated:p(n.secretStoreExisting.updatedAtMs)})}
</div>`:C}
${n.secretStore.kind===`secret`?b`<label class="chat-question-panel__store-hosts">
<span>${c(`secretsStore.allowedHosts`)}</span>
<input
class="chat-question-panel__other chat-question-panel__hosts"
type="text"
autocomplete="off"
placeholder=${c(`secretsStore.allowedHostsPlaceholder`)}
.value=${t.secretStoreAllowedHostsDraft??n.secretStore.allowedHosts?.join(`, `)??``}
?disabled=${r}
=${t=>{let n=t.target;n instanceof HTMLInputElement&&e.onSecretStoreAllowedHostsChange?.(n.value)}}
/>
</label>`:C}
</div>
`:C}
${W({question:n,value:o?.freeText??``,selected:!!(U(n)?o?.freeText:o?.freeText.trim()),disabled:r,onInput:e=>this.setFreeText(t,n,e)})}
${n.resource?b`<openclaw-chat-question-resource
.question=${n}
.requestId=${t.requestKey}
.sessionKey=${t.sessionKey??``}
.agentId=${t.agentId}
.selected=${o?.selected??new Set}
.disabled=${r}
-selection=${e=>this.updateDraft(t,n,{selected:new Set(e.detail.values),freeText:``})}
></openclaw-chat-question-resource>`:C}
<div class="chat-question-panel__footer">
${t.notice?b`<span class="chat-question-panel__error" role="status">${t.notice}</span>`:C}
${t.error?b`<span class="chat-question-panel__error" role="status">
${c(`chat.questions.submitFailed`,{error:t.error})}
${e.onDismissError?b`<button
type="button"
class="chat-question-panel__error-dismiss"
aria-label=${c(`chat.actions.dismissError`)}
=${e.onDismissError}
>
${g.x}
</button>`:C}
</span>`:C}
${this.currentQuestionIndex>0?b`<button
class="btn btn--sm chat-question-panel__back"
type="button"
?disabled=${r}
=${()=>this.goBack()}
>
${c(`chat.questions.back`)}
</button>`:C}
${e.onSkip?b`<button
class="btn btn--sm chat-question-panel__skip"
type="button"
?disabled=${r}
=${()=>void this.resolve(t,`skip`)}
>
${this.pendingAction?.kind===`skip`?c(t.nonBlocking?`chat.asyncQuestions.dismissing`:`chat.questions.skipping`):c(t.nonBlocking?`chat.asyncQuestions.dismiss`:`chat.questions.skip`)}
</button>`:C}
<button
class="btn btn--sm primary chat-question-panel__advance"
type="button"
?disabled=${r||!a||!e.onSubmit}
=${()=>this.advanceOrSubmit(t,n)}
>
${this.pendingAction?.kind===`submit`||t.submitting?c(`chat.questions.submitting`):c(i?`chat.questions.submit`:`chat.questions.next`)}
</button>
</div>
</section>
`}},M([D({attribute:!1})],Q.prototype,`props`,void 0),M([T()],Q.prototype,`currentQuestionIndex`,void 0),M([T()],Q.prototype,`pendingAction`,void 0),customElements.get(`openclaw-chat-question-panel`)||customElements.define(`openclaw-chat-question-panel`,Q)})))()}$();export{Q as ChatQuestionPanel};