@shawnstack/quickforge 1.7.5 → 1.7.6

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 (66) hide show
  1. package/dist/assets/AgentProfilesPage-Dl5Peqov.js +1 -0
  2. package/dist/assets/ChatPanelHost-D3987TUJ.js +307 -0
  3. package/dist/assets/CloudAccountSettingsPage-BG5Nn3bq.js +1 -0
  4. package/dist/assets/PluginsPage-BDKYfLna.js +1 -0
  5. package/dist/assets/ScheduledTasksPage--GAki3lB.js +2 -0
  6. package/dist/assets/SettingsWorkspacePage-DSy66Tuw.js +1696 -0
  7. package/dist/assets/ShareLinksSettingsPage-BQQMsvJo.js +1 -0
  8. package/dist/assets/SharedConversationPage-B9BUVCri.js +1 -0
  9. package/dist/assets/TerminalDock-CT1nYo7P.js +2 -0
  10. package/dist/assets/{WorkspaceInspector-D9kdnLDd.js → WorkspaceInspector-BnfDLf9a.js} +5 -5
  11. package/dist/assets/{dist-DLKC_odd.js → dist-yDHbi5ux.js} +1 -1
  12. package/dist/assets/{esm-Df4edDpj.js → esm-sdsK6gaB.js} +2 -2
  13. package/dist/assets/{icons-CXPP1fFT.js → icons-2D18YbSI.js} +1 -1
  14. package/dist/assets/index-1ei6nhs-.css +3 -0
  15. package/dist/assets/index-BivLuZvX.js +65 -0
  16. package/dist/assets/{mcp-servers-dialog-BUSPvaqI.js → mcp-servers-dialog-BEV4_TXC.js} +2 -2
  17. package/dist/assets/{logger-8REuLPGK.js → model-display-label-C6-0eDxF.js} +1 -1
  18. package/dist/assets/{monaco-D6OhXgjg.js → monaco-DvXErxp2.js} +1 -1
  19. package/dist/assets/{react-vendor-1KC5PyRx.js → react-vendor-3UaA-C9W.js} +1 -1
  20. package/dist/assets/{skills-dialog-BThnIUBs.js → skills-dialog-BTJ7mzk_.js} +3 -3
  21. package/dist/assets/{web-DoV8MSMn.js → web-B5qgZJRv.js} +1 -1
  22. package/dist/index.html +8 -8
  23. package/dist/sw.js +17 -0
  24. package/package.json +2 -1
  25. package/runtime-assets/agent/darwin-arm64/qf-agent +0 -0
  26. package/runtime-assets/agent/darwin-x64/qf-agent +0 -0
  27. package/runtime-assets/agent/linux-arm64/qf-agent +0 -0
  28. package/runtime-assets/agent/linux-x64/qf-agent +0 -0
  29. package/runtime-assets/agent/win32-x64/qf-agent.exe +0 -0
  30. package/server/access-policy.mjs +11 -0
  31. package/server/agent-manager.mjs +349 -48
  32. package/server/agent-profiles.mjs +27 -8
  33. package/server/auto-compaction.mjs +35 -8
  34. package/server/cloud/client.mjs +4 -7
  35. package/server/cloud/config.mjs +6 -5
  36. package/server/cloud/credential-store.mjs +10 -3
  37. package/server/cloud/identity.mjs +17 -60
  38. package/server/cloud/models.mjs +1 -1
  39. package/server/cloud/qf-agent-process.mjs +571 -0
  40. package/server/cloud/runtime.mjs +11 -5
  41. package/server/cloud/service-config.mjs +8 -4
  42. package/server/index.mjs +96 -37
  43. package/server/mcp/registry.mjs +8 -7
  44. package/server/message-converters.mjs +40 -19
  45. package/server/model-catalog.mjs +11 -4
  46. package/server/opencode-acp-agent.mjs +1151 -0
  47. package/server/public-api.mjs +11 -5
  48. package/server/routes/agent-profiles.mjs +6 -3
  49. package/server/routes/agent.mjs +43 -3
  50. package/server/routes/backup.mjs +5 -4
  51. package/server/routes/cloud.mjs +54 -35
  52. package/server/routes/shares.mjs +7 -8
  53. package/server/routes/storage.mjs +9 -24
  54. package/server/routes/system.mjs +7 -4
  55. package/server/utils/process-tree.mjs +78 -0
  56. package/dist/assets/AgentProfilesPage-XNjuqJs7.js +0 -1
  57. package/dist/assets/ChatPanelHost-CuF-PZpc.js +0 -292
  58. package/dist/assets/CloudAccountSettingsPage-GJSoi6sO.js +0 -1
  59. package/dist/assets/PluginsPage-BQgp0qN-.js +0 -1
  60. package/dist/assets/ScheduledTasksPage-DdekATzq.js +0 -2
  61. package/dist/assets/SettingsWorkspacePage-Cb54Ic91.js +0 -1639
  62. package/dist/assets/ShareLinksSettingsPage-C75WJpaF.js +0 -1
  63. package/dist/assets/SharedConversationPage-CjMtSQTW.js +0 -1
  64. package/dist/assets/TerminalDock-D4zJBJc4.js +0 -2
  65. package/dist/assets/index-_ZhK6bvp.js +0 -65
  66. package/dist/assets/index-iVV2bP_f.css +0 -3
@@ -0,0 +1,1696 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AgentProfilesPage-Dl5Peqov.js","assets/rolldown-runtime-DWdDZTNf.js","assets/index-BivLuZvX.js","assets/pi-ai-Cr2ee4a0.js","assets/dist-yDHbi5ux.js","assets/pi-web-ui-DV5jKjSg.js","assets/lit-vendor-DvaQX_UQ.js","assets/css-utils-31qzCXsk.js","assets/icons-2D18YbSI.js","assets/react-vendor-3UaA-C9W.js","assets/skills-dialog-BTJ7mzk_.js","assets/model-display-label-C6-0eDxF.js","assets/model-reference-o41fJ6jQ.js","assets/index-1ei6nhs-.css","assets/model-identity-CgA6_Pbm.js","assets/mcp-servers-dialog-BEV4_TXC.js","assets/PluginsPage-BDKYfLna.js","assets/plugin-api-Uu2xAB4d.js","assets/ScheduledTasksPage--GAki3lB.js","assets/ShareLinksSettingsPage-BQQMsvJo.js","assets/CloudAccountSettingsPage-BG5Nn3bq.js"])))=>i.map(i=>d[i]);
2
+ import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{Ct as t,Gt as n,It as r,Lt as i,P as a,Q as o,Rt as s,Vt as c,Wt as ee,Y as l,_ as u,_t as d,f as te,gt as f,jt as ne,m as p,qt as m,u as h,w as g,xt as _,y as re,zt as v}from"./icons-2D18YbSI.js";import{n as y,r as ie}from"./react-vendor-3UaA-C9W.js";import{c as ae,d as oe,f as se,l as ce,p as b,u as le}from"./lit-vendor-DvaQX_UQ.js";import{m as x,x as S}from"./pi-web-ui-DV5jKjSg.js";import{J as C}from"./pi-ai-Cr2ee4a0.js";import{n as w,t as ue}from"./model-display-label-C6-0eDxF.js";import{t as de}from"./model-reference-o41fJ6jQ.js";import{Ct as fe,J as pe,K as me,Pt as he,Qt as ge,St as _e,Tt as T,V as ve,Zt as E,_n as ye,a as be,an as xe,bt as Se,c as Ce,ct as we,dt as Te,en as Ee,fn as De,ft as D,gn as Oe,hn as O,ht as ke,i as Ae,in as je,l as Me,lt as Ne,mn as Pe,nn as Fe,on as Ie,ot as Le,pt as k,rn as Re,s as ze,sn as Be,st as Ve,un as He,ut as Ue,vn as A,vt as We,wt as j,yn as M,yt as Ge}from"./index-BivLuZvX.js";var N=e(m(),1),Ke={openai:{name:`OpenAI`,baseUrl:`https://api.openai.com/v1`,protocol:`openai-completions`,modelId:`gpt-4o`,supportsImages:!0},deepseek:{name:`DeepSeek`,baseUrl:`https://api.deepseek.com/v1`,protocol:`openai-completions`,modelId:`deepseek-chat`},glm:{name:`Zhipu GLM`,baseUrl:`https://open.bigmodel.cn/api/paas/v4`,protocol:`openai-completions`,modelId:`glm-4-plus`},ollama:{name:`Ollama`,baseUrl:`http://localhost:11434/v1`,protocol:`openai-completions`,modelId:`llama3.1`},litellm:{name:E.name,baseUrl:E.baseUrl,protocol:`openai-completions`,modelId:E.modelId,supportsImages:E.supportsImages}},qe=[{key:`openai`,label:M(`presetOpenai`)},{key:`deepseek`,label:M(`presetDeepseek`)},{key:`glm`,label:M(`presetGlm`)},{key:`ollama`,label:M(`presetOllama`)},{key:`litellm`,label:M(`presetLitellm`)},{key:`custom`,label:M(`presetCustom`)}],P=()=>({modelId:``,contextWindow:E.contextWindow,maxTokens:E.maxTokens,reasoning:!0,supportsImages:!1,visibleInSelectors:!0,open:!1}),F=()=>({name:E.name,baseUrl:E.baseUrl,apiKey:``,headerRows:[],protocol:`openai-completions`,models:[P()]}),Je=class extends x{providers=[];form=F();editingProviderId;formOpen=!1;loading=!0;apiKeyVisible=!1;advancedOpen=!1;activePreset=``;testing=!1;testResult=`idle`;testError=``;autoEditProviderName=null;async connectedCallback(){if(super.connectedCallback(),await this.loadProviders(),this.autoEditProviderName){let e=this.providers.find(e=>e.name===this.autoEditProviderName);e&&await this.openEditForm(e),this.autoEditProviderName=null}}getTabName(){return M(`customModels`)}async loadProviders(){this.loading=!0,this.requestUpdate();try{this.providers=await S().customProviders.getAll()}catch(e){w.error(`Failed to load custom providers:`,e),this.providers=[]}finally{this.loading=!1,this.requestUpdate()}}resetTestState(){this.testing=!1,this.testResult=`idle`,this.testError=``}openAddForm(){this.editingProviderId=void 0,this.form=F(),this.apiKeyVisible=!1,this.advancedOpen=!1,this.activePreset=``,this.resetTestState(),this.formOpen=!0,this.requestUpdate()}async openEditForm(e){let t=await S().providerKeys.get(e.name)??e.apiKey??``,n=e.models??[],r=n.length>0?n.map(e=>({modelId:e.id,contextWindow:e.contextWindow??E.contextWindow,maxTokens:e.maxTokens??E.maxTokens,reasoning:e.reasoning===!0,supportsImages:e.input?.includes(`image`)===!0,visibleInSelectors:e.quickforgeHidden!==!0,open:!1})):[P()],i=n[0]?.headers??{},a=Object.entries(i).map(([e,t])=>({key:e,value:String(t)}));this.editingProviderId=e.id,this.form={providerId:e.id,id:e.id,name:e.name,baseUrl:e.baseUrl,apiKey:t,headerRows:a,protocol:e.type===`anthropic-messages`?`anthropic-messages`:`openai-completions`,models:r},this.apiKeyVisible=!1,this.advancedOpen=a.length>0||e.type===`anthropic-messages`,this.activePreset=``,this.resetTestState(),this.formOpen=!0,this.requestUpdate()}closeForm(){this.formOpen=!1,this.editingProviderId=void 0,this.form=F(),this.apiKeyVisible=!1,this.advancedOpen=!1,this.activePreset=``,this.resetTestState(),this.requestUpdate()}toggleApiKeyVisibility(){this.apiKeyVisible=!this.apiKeyVisible,this.requestUpdate()}updateForm(e,t){this.form={...this.form,[e]:t},(e===`name`||e===`baseUrl`)&&(this.activePreset=``),this.resetTestState(),this.requestUpdate()}updateModelField(e,t,n){let r=this.form.models.map((r,i)=>i===e?{...r,[t]:n}:r);this.form={...this.form,models:r},t===`modelId`&&(this.activePreset=``,this.resetTestState()),this.requestUpdate()}addModelRow(){this.form={...this.form,models:[...this.form.models,P()]},this.requestUpdate()}removeModelRow(e){let t=this.form.models.filter((t,n)=>n!==e);this.form={...this.form,models:t},this.requestUpdate()}toggleModelExpanded(e){let t=this.form.models.map((t,n)=>n===e?{...t,open:!t.open}:t);this.form={...this.form,models:t},this.requestUpdate()}addHeaderRow(){this.form={...this.form,headerRows:[...this.form.headerRows,{key:``,value:``}]},this.requestUpdate()}updateHeaderRow(e,t,n){let r=this.form.headerRows.map((r,i)=>i===e?{...r,[t]:n}:r);this.form={...this.form,headerRows:r},this.requestUpdate()}removeHeaderRow(e){let t=this.form.headerRows.filter((t,n)=>n!==e);this.form={...this.form,headerRows:t},this.requestUpdate()}applyPreset(e){if(this.activePreset=e,e===`custom`)this.form={...this.form,name:``,baseUrl:``,protocol:`openai-completions`,models:[P()]};else{let t=Ke[e];this.form={...this.form,name:t.name,baseUrl:t.baseUrl,protocol:t.protocol,models:[{...P(),modelId:t.modelId,supportsImages:t.supportsImages===!0,open:!1}]}}this.resetTestState(),this.requestUpdate()}buildHeadersFromRows(){let e={};for(let t of this.form.headerRows){let n=t.key.trim(),r=t.value.trim();n&&r&&(e[n]=r)}return e}buildModel(e,t){let n=this.form.name.trim(),r=this.form.baseUrl.trim(),i=e.reasoning===!0,a=e.supportsImages===!0?[`text`,`image`]:[`text`],o=r.includes(`api.deepseek.com`);return je({id:e.modelId,name:`${e.modelId} (${n})`,api:this.form.protocol,provider:n,baseUrl:r.replace(/\/$/,``),reasoning:i,input:a,quickforgeHidden:e.visibleInSelectors?void 0:!0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:Number(e.contextWindow)||E.contextWindow,maxTokens:Number(e.maxTokens)||E.maxTokens,headers:Object.keys(t).length>0?t:void 0,thinkingLevelMap:o&&i?{low:`high`,medium:`high`,high:`high`,xhigh:`max`}:void 0,compat:this.form.protocol===`openai-completions`?{supportsStore:!1,supportsDeveloperRole:!1,supportsReasoningEffort:i,supportsUsageInStreaming:!1,supportsStrictMode:!1,maxTokensField:`max_tokens`,...o&&i?{requiresReasoningContentOnAssistantMessages:!0,thinkingFormat:`deepseek`}:{}}:void 0})}async testConnection(){let e=this.form.baseUrl.trim(),t=this.form.models.find(e=>e.modelId.trim());if(!e||!t){this.testing=!1,this.testResult=`fail`,this.testError=M(`testRequiresFields`),this.requestUpdate();return}let n=this.buildHeadersFromRows(),r=this.buildModel(t,n);this.testing=!0,this.testResult=`idle`,this.testError=``,this.requestUpdate();try{let e=await(await fetch(`/api/models/test-connection`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({model:r,apiKey:this.form.apiKey.trim()})})).json().catch(()=>({}));e?.ok?(this.testResult=`ok`,this.testError=``):(this.testResult=`fail`,this.testError=e?.error||M(`connectionFailed`))}catch(e){this.testResult=`fail`,this.testError=e?.message||M(`connectionFailed`)}finally{this.testing=!1,this.requestUpdate()}}async saveModel(){let e=this.form.name.trim(),t=this.form.baseUrl.trim();if(!e||!t){j(M(`fillProviderBaseUrlModel`));return}let n=this.form.models.filter(e=>e.modelId.trim());if(n.length===0){j(M(`atLeastOneModel`));return}let r=n.map(e=>e.modelId.trim());if(new Set(r).size!==r.length){j(M(`duplicateModelId`));return}let i=this.buildHeadersFromRows(),a=n.map(e=>this.buildModel(e,i)),o=this.form.apiKey.trim(),s=this.editingProviderId?this.providers.find(e=>e.id===this.editingProviderId):void 0,c={id:this.editingProviderId??Be(),name:e,type:this.form.protocol,baseUrl:a[0].baseUrl,apiKey:o||void 0,models:a};try{let e=S();await e.customProviders.set(c),s&&s.name!==c.name&&await e.providerKeys.delete(s.name),o?await e.providerKeys.set(c.name,o):await e.providerKeys.delete(c.name),He(),this.closeForm(),await this.loadProviders()}catch(e){w.error(`Failed to save custom model:`,e),j(M(`saveCustomModelFailed`))}}async deleteProvider(e){if(await T({description:M(`confirmDeleteProvider`,{name:e.name}),confirmLabel:M(`confirmDelete`),cancelLabel:M(`cancel`),variant:`destructive`}))try{let t=S();await t.customProviders.delete(e.id),await t.providerKeys.delete(e.name),He(),await this.loadProviders()}catch(e){w.error(`Failed to delete custom provider:`,e),j(M(`deleteFailed`))}}renderProvider(e){let t=e.models??[],n=t.length,r=e.type===`anthropic-messages`?`Anthropic Messages`:`OpenAI Compatible`;return b`
3
+ <div class="quickforge-settings-list-item">
4
+ <div class="quickforge-settings-list-item-main">
5
+ <div class="flex min-w-0 items-start gap-3">
6
+ <span class="quickforge-settings-avatar" aria-hidden="true">${e.name.trim().charAt(0).toUpperCase()||`M`}</span>
7
+ <div class="min-w-0 flex-1">
8
+ <div class="quickforge-settings-row-title">${e.name}</div>
9
+ <div class="quickforge-settings-row-description break-all">${e.baseUrl}</div>
10
+ <div class="quickforge-settings-meta">
11
+ <span class="quickforge-settings-badge quickforge-settings-badge-muted">${M(`providerProtocol`)}: ${r}</span>
12
+ <span class="quickforge-settings-badge quickforge-settings-badge-info">${M(`modelsCount`,{count:n})}</span>
13
+ ${n===0?b`<span class="quickforge-settings-badge quickforge-settings-badge-warning">${M(`noModelAdded`)}</span>`:null}
14
+ </div>
15
+ ${n>0?b`
16
+ <div class="quickforge-settings-meta">
17
+ ${t.slice(0,6).map(e=>b`
18
+ <code class="quickforge-settings-command-name">${e.id}</code>
19
+ `)}
20
+ ${t.length>6?b`<span class="quickforge-settings-badge quickforge-settings-badge-muted">+${t.length-6}</span>`:null}
21
+ </div>
22
+ `:null}
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <div class="quickforge-settings-list-item-actions">
27
+ <button
28
+ class="quickforge-settings-button quickforge-settings-button-secondary quickforge-settings-button-compact"
29
+ type="button"
30
+ @click=${()=>this.openEditForm(e)}
31
+ >
32
+ ${M(`editModel`)}
33
+ </button>
34
+ <button
35
+ class="quickforge-settings-button quickforge-settings-button-danger quickforge-settings-button-compact"
36
+ type="button"
37
+ @click=${()=>this.deleteProvider(e)}
38
+ >
39
+ ${M(`delete`)}
40
+ </button>
41
+ </div>
42
+ </div>
43
+ `}renderPresetChips(){return b`
44
+ <div class="quickforge-settings-form-row">
45
+ <span class="quickforge-settings-form-label">${M(`presets`)}</span>
46
+ <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap">
47
+ ${qe.map(e=>b`
48
+ <button
49
+ class="quickforge-settings-segmented-option ${this.activePreset===e.key?`quickforge-settings-segmented-option-active`:``}"
50
+ type="button"
51
+ @click=${()=>this.applyPreset(e.key)}
52
+ >
53
+ ${e.label}
54
+ </button>
55
+ `)}
56
+ </div>
57
+ </div>
58
+ `}renderModelRow(e,t){let n=e.open===!0;return b`
59
+ <div class="quickforge-settings-subrow quickforge-settings-row-align-start">
60
+ <button
61
+ class="quickforge-settings-icon-action"
62
+ type="button"
63
+ title=${M(`expandModel`)}
64
+ aria-expanded=${n?`true`:`false`}
65
+ @click=${()=>this.toggleModelExpanded(t)}
66
+ >
67
+ ${n?`▾`:`▸`}
68
+ </button>
69
+ <div class="quickforge-settings-list-item-main">
70
+ <input
71
+ class="quickforge-settings-input quickforge-settings-mono"
72
+ .value=${e.modelId}
73
+ @input=${e=>this.updateModelField(t,`modelId`,e.target.value)}
74
+ placeholder=${M(`modelIdPlaceholder`)}
75
+ />
76
+ ${n?b`
77
+ <div class="quickforge-settings-model-grid mt-3">
78
+ <label class="quickforge-settings-form-row">
79
+ <span class="quickforge-settings-form-label">${M(`contextWindow`)}</span>
80
+ <input
81
+ class="quickforge-settings-input"
82
+ .value=${String(e.contextWindow)}
83
+ type="number"
84
+ @input=${e=>this.updateModelField(t,`contextWindow`,Number(e.target.value))}
85
+ />
86
+ </label>
87
+ <label class="quickforge-settings-form-row">
88
+ <span class="quickforge-settings-form-label">${M(`maxTokens`)}</span>
89
+ <input
90
+ class="quickforge-settings-input"
91
+ .value=${String(e.maxTokens)}
92
+ type="number"
93
+ @input=${e=>this.updateModelField(t,`maxTokens`,Number(e.target.value))}
94
+ />
95
+ </label>
96
+ </div>
97
+ <label class="mt-3 flex items-center gap-2 text-sm text-muted-foreground">
98
+ <input
99
+ type="checkbox"
100
+ .checked=${e.reasoning}
101
+ @change=${e=>this.updateModelField(t,`reasoning`,e.target.checked)}
102
+ />
103
+ <span>${M(`reasoningModel`)}</span>
104
+ </label>
105
+ <label class="mt-2 flex items-center gap-2 text-sm text-muted-foreground">
106
+ <input
107
+ type="checkbox"
108
+ .checked=${e.supportsImages}
109
+ @change=${e=>this.updateModelField(t,`supportsImages`,e.target.checked)}
110
+ />
111
+ <span>
112
+ ${M(`imageInputModel`)}
113
+ <quickforge-info-tip .label=${M(`imageInputModelHelp`)}></quickforge-info-tip>
114
+ </span>
115
+ </label>
116
+ <div class="mt-3 flex items-center justify-between gap-3 rounded-lg border border-border px-3 py-2">
117
+ <span class="text-sm text-muted-foreground">${M(`showModelInSelectors`)}</span>
118
+ <label class="quickforge-settings-switch" title=${M(`showModelInSelectorsHelp`)}>
119
+ <input
120
+ type="checkbox"
121
+ .checked=${e.visibleInSelectors}
122
+ @change=${e=>this.updateModelField(t,`visibleInSelectors`,e.target.checked)}
123
+ />
124
+ <span aria-hidden="true"></span>
125
+ </label>
126
+ </div>
127
+ `:null}
128
+ </div>
129
+ ${this.form.models.length>1?b`
130
+ <button
131
+ class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
132
+ type="button"
133
+ title=${M(`delete`)}
134
+ @click=${()=>this.removeModelRow(t)}
135
+ >
136
+
137
+ </button>
138
+ `:null}
139
+ </div>
140
+ `}renderHeadersEditor(){return b`
141
+ <div class="quickforge-settings-form-row">
142
+ <span class="quickforge-settings-form-label">
143
+ ${M(`customHeaders`)}
144
+ <quickforge-info-tip .label=${M(`customHeadersHelp`)}></quickforge-info-tip>
145
+ </span>
146
+ ${this.form.headerRows.length===0?b``:b`
147
+ <div class="quickforge-settings-nested-list">
148
+ ${this.form.headerRows.map((e,t)=>b`
149
+ <div class="quickforge-settings-subrow">
150
+ <input
151
+ class="quickforge-settings-input"
152
+ .value=${e.key}
153
+ @input=${e=>this.updateHeaderRow(t,`key`,e.target.value)}
154
+ placeholder=${M(`headerName`)}
155
+ />
156
+ <input
157
+ class="quickforge-settings-input"
158
+ .value=${e.value}
159
+ @input=${e=>this.updateHeaderRow(t,`value`,e.target.value)}
160
+ placeholder=${M(`headerValue`)}
161
+ />
162
+ <button
163
+ class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
164
+ type="button"
165
+ title=${M(`removeHeader`)}
166
+ @click=${()=>this.removeHeaderRow(t)}
167
+ >
168
+
169
+ </button>
170
+ </div>
171
+ `)}
172
+ </div>
173
+ `}
174
+ <button
175
+ class="quickforge-settings-button quickforge-settings-button-secondary quickforge-settings-button-compact"
176
+ type="button"
177
+ @click=${()=>this.addHeaderRow()}
178
+ >
179
+ + ${M(`addHeader`)}
180
+ </button>
181
+ </div>
182
+ `}renderTestStatus(){return this.testResult===`idle`&&!this.testing?b``:this.testing?b`<span class="quickforge-settings-status">${M(`testingConnection`)}</span>`:this.testResult===`ok`?b`<span class="quickforge-settings-badge quickforge-settings-badge-success">✓ ${M(`connectionOk`)}</span>`:b`<span class="quickforge-settings-alert">✗ ${this.testError||M(`connectionFailed`)}</span>`}renderForm(){return b`
183
+ <section class="quickforge-settings-section" aria-label=${this.editingProviderId?M(`editCustomModel`):M(`addCustomModel`)}>
184
+ <div class="quickforge-settings-toolbar">
185
+ <button
186
+ class="quickforge-settings-button quickforge-settings-button-secondary"
187
+ type="button"
188
+ @click=${()=>this.closeForm()}
189
+ >
190
+ <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
191
+ ${M(`back`)}
192
+ </button>
193
+ <div class="quickforge-settings-row-main">
194
+ <div class="quickforge-settings-row-title">${this.editingProviderId?M(`editCustomModel`):M(`addCustomModel`)}</div>
195
+ <div class="quickforge-settings-row-description">${M(`customModelsDescription`)}</div>
196
+ </div>
197
+ </div>
198
+
199
+ <div class="quickforge-settings-form-grid">
200
+ ${this.renderPresetChips()}
201
+
202
+ <label class="quickforge-settings-form-row">
203
+ <span class="quickforge-settings-form-label">${M(`providerName`)}</span>
204
+ <input
205
+ class="quickforge-settings-input"
206
+ .value=${this.form.name}
207
+ @input=${e=>this.updateForm(`name`,e.target.value)}
208
+ placeholder=${M(`providerNamePlaceholder`)}
209
+ />
210
+ </label>
211
+
212
+ <label class="quickforge-settings-form-row">
213
+ <span class="quickforge-settings-form-label">Base URL</span>
214
+ <input
215
+ class="quickforge-settings-input"
216
+ .value=${this.form.baseUrl}
217
+ @input=${e=>this.updateForm(`baseUrl`,e.target.value)}
218
+ placeholder=${this.form.protocol===`anthropic-messages`?`e.g., https://api.anthropic.com`:`e.g., http://localhost:4000/v1`}
219
+ />
220
+ </label>
221
+
222
+ <label class="quickforge-settings-form-row">
223
+ <span class="quickforge-settings-form-label">${M(`apiKey`)}</span>
224
+ <div class="quickforge-settings-inline-field">
225
+ <input
226
+ class="quickforge-settings-input pr-10"
227
+ .value=${this.form.apiKey}
228
+ type=${this.apiKeyVisible?`text`:`password`}
229
+ @input=${e=>this.updateForm(`apiKey`,e.target.value)}
230
+ placeholder=${M(`apiKeyPlaceholder`)}
231
+ />
232
+ <button
233
+ class="quickforge-settings-inline-field-button"
234
+ type="button"
235
+ title=${this.apiKeyVisible?M(`hideApiKey`):M(`showApiKey`)}
236
+ aria-label=${this.apiKeyVisible?M(`hideApiKey`):M(`showApiKey`)}
237
+ aria-pressed=${this.apiKeyVisible?`true`:`false`}
238
+ @click=${()=>this.toggleApiKeyVisibility()}
239
+ >
240
+ ${this.apiKeyVisible?b`<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" y1="2" x2="22" y2="22"/><path d="M8.53 8.53A5 5 0 0 0 12 17a5 5 0 0 0 3.47-8.53"/></svg>`:b`<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>`}
241
+ </button>
242
+ </div>
243
+ </label>
244
+
245
+ <div class="quickforge-settings-form-row">
246
+ <div class="flex items-center justify-between gap-3">
247
+ <span class="quickforge-settings-form-label">${M(`modelsList`)}</span>
248
+ <button
249
+ class="quickforge-settings-button quickforge-settings-button-secondary quickforge-settings-button-compact"
250
+ type="button"
251
+ @click=${()=>this.addModelRow()}
252
+ >
253
+ + ${M(`addModel`)}
254
+ </button>
255
+ </div>
256
+ <div class="quickforge-settings-nested-list">
257
+ ${this.form.models.map((e,t)=>this.renderModelRow(e,t))}
258
+ </div>
259
+ </div>
260
+
261
+ <button
262
+ class="quickforge-settings-details-toggle"
263
+ type="button"
264
+ aria-expanded=${this.advancedOpen?`true`:`false`}
265
+ @click=${()=>{this.advancedOpen=!this.advancedOpen,this.requestUpdate()}}
266
+ >
267
+ <span>${this.advancedOpen?`▾`:`▸`}</span>
268
+ ${M(`providerAdvanced`)}
269
+ </button>
270
+
271
+ ${this.advancedOpen?b`
272
+ <div class="quickforge-settings-form-grid rounded-lg border border-border p-3">
273
+ <label class="quickforge-settings-form-row">
274
+ <span class="quickforge-settings-form-label">
275
+ ${M(`protocolType`)}
276
+ <quickforge-info-tip .label=${M(`protocolHelp`)}></quickforge-info-tip>
277
+ </span>
278
+ <select
279
+ class="quickforge-settings-select"
280
+ .value=${this.form.protocol}
281
+ @change=${e=>this.updateForm(`protocol`,e.target.value)}
282
+ >
283
+ <option value="openai-completions">OpenAI Compatible / Chat Completions</option>
284
+ <option value="anthropic-messages">Anthropic Messages</option>
285
+ </select>
286
+ </label>
287
+ ${this.renderHeadersEditor()}
288
+ </div>
289
+ `:null}
290
+ </div>
291
+
292
+ <div class="quickforge-settings-row">
293
+ <div class="quickforge-settings-row-main">
294
+ ${this.renderTestStatus()}
295
+ </div>
296
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
297
+ <button
298
+ class="quickforge-settings-button quickforge-settings-button-secondary"
299
+ type="button"
300
+ @click=${()=>this.closeForm()}
301
+ >
302
+ ${M(`cancel`)}
303
+ </button>
304
+ <button
305
+ class="quickforge-settings-button quickforge-settings-button-secondary"
306
+ type="button"
307
+ ?disabled=${this.testing}
308
+ @click=${()=>this.testConnection()}
309
+ >
310
+ ${this.testing?M(`testingConnection`):M(`testConnection`)}
311
+ </button>
312
+ <button
313
+ class="quickforge-settings-button quickforge-settings-button-primary"
314
+ type="button"
315
+ @click=${()=>this.saveModel()}
316
+ >
317
+ ${M(`save`)}
318
+ </button>
319
+ </div>
320
+ </div>
321
+ </section>
322
+ `}render(){return this.formOpen?b`
323
+ <div class="quickforge-settings-stack">
324
+ <div class="quickforge-settings-heading">
325
+ <h3 class="quickforge-settings-title">
326
+ ${this.editingProviderId?M(`editCustomModel`):M(`addCustomModel`)}
327
+ <quickforge-info-tip .label=${M(`customModelsDescription`)}></quickforge-info-tip>
328
+ </h3>
329
+ </div>
330
+ ${this.renderForm()}
331
+ </div>
332
+ `:b`
333
+ <div class="quickforge-settings-stack">
334
+ <section class="quickforge-settings-section" aria-label=${M(`customModelsTitle`)}>
335
+ <div class="quickforge-settings-toolbar">
336
+ <div>
337
+ <div class="quickforge-settings-row-title">${M(`customModelsTitle`)}</div>
338
+ <div class="quickforge-settings-row-description">${M(`customModelsDescription`)}</div>
339
+ </div>
340
+ <button
341
+ class="quickforge-settings-button quickforge-settings-button-primary"
342
+ type="button"
343
+ @click=${()=>this.openAddForm()}
344
+ >
345
+ ${M(`addModel`)}
346
+ </button>
347
+ </div>
348
+
349
+ ${this.loading?b`<div class="quickforge-settings-empty-row">${M(`loading`)}</div>`:this.providers.length===0?b`<div class="quickforge-settings-empty-row">${M(`noCustomModels`)}</div>`:this.providers.map(e=>this.renderProvider(e))}
350
+ </section>
351
+ </div>
352
+ `}},I=`quickforge-custom-providers-only-tab`;customElements.get(I)||customElements.define(I,Je);function Ye(e){let t=document.createElement(I);return e&&(t.autoEditProviderName=e),t}var Xe=[{value:`light`,label:()=>M(`lightTheme`)},{value:`dark`,label:()=>M(`darkTheme`)}],Ze=class extends x{theme=Ne();interfaceFontSizePx=D.interfaceFontSizePx;messageFontSizePx=D.messageFontSizePx;loading=!0;error=``;getTabName(){return M(`appearance`)}async connectedCallback(){super.connectedCallback(),await this.loadSettings()}async loadSettings(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=S(),[t,n]=await Promise.all([Ue(e),We(e)]);this.theme=t.theme,this.interfaceFontSizePx=n.interfaceFontSizePx,this.messageFontSizePx=n.messageFontSizePx}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async selectTheme(e){if(this.theme!==e){this.theme=e,this.requestUpdate();try{await Te(S(),{theme:e}),this.error=``,this.requestUpdate()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}}currentFontSizeSettings(){return Ge({interfaceFontSizePx:this.interfaceFontSizePx,messageFontSizePx:this.messageFontSizePx})}previewFontSize(e){let t=Ge(e);this.interfaceFontSizePx=t.interfaceFontSizePx,this.messageFontSizePx=t.messageFontSizePx,ke(t),this.requestUpdate()}updateInterfaceFontSize(e){this.previewFontSize({...this.currentFontSizeSettings(),interfaceFontSizePx:Number(e)||D.interfaceFontSizePx})}updateMessageFontSize(e){this.previewFontSize({...this.currentFontSizeSettings(),messageFontSizePx:Number(e)||D.messageFontSizePx})}async saveFontSize(){try{await Se(S(),this.currentFontSizeSettings()),this.error=``,this.requestUpdate()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}renderThemeOption(e){let t=this.theme===e.value;return b`
353
+ <button
354
+ type="button"
355
+ class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
356
+ aria-pressed=${t?`true`:`false`}
357
+ @click=${()=>this.selectTheme(e.value)}
358
+ >
359
+ ${e.label()}
360
+ </button>
361
+ `}getFontSizeRangeProgress(e){return(e-k.min)/(k.max-k.min)*100}renderFontSizeSlider(e,t,n,r){return b`
362
+ <div class="quickforge-settings-row">
363
+ <div class="quickforge-settings-row-main">
364
+ <div class="quickforge-settings-row-title">
365
+ ${e}
366
+ ${t?b`<quickforge-info-tip .label=${t}></quickforge-info-tip>`:null}
367
+ </div>
368
+ <div class="quickforge-settings-row-description">${M(`fontSizeRangeDescription`)}</div>
369
+ </div>
370
+ <div class="quickforge-settings-row-control quickforge-settings-slider-control">
371
+ <span class="quickforge-settings-value-badge">${n}px</span>
372
+ <input
373
+ class="quickforge-font-size-slider"
374
+ style=${`--quickforge-font-size-slider-progress: ${this.getFontSizeRangeProgress(n)}%`}
375
+ type="range"
376
+ min=${String(k.min)}
377
+ max=${String(k.max)}
378
+ step="1"
379
+ .value=${String(n)}
380
+ @input=${e=>r(e.target.value)}
381
+ @change=${()=>this.saveFontSize()}
382
+ />
383
+ </div>
384
+ </div>
385
+ `}render(){return this.loading?b`<div class="text-sm text-muted-foreground">${M(`loading`)}</div>`:b`
386
+ <div class="quickforge-settings-stack">
387
+ <section class="quickforge-settings-section" aria-label=${M(`appearance`)}>
388
+ <div class="quickforge-settings-row">
389
+ <div class="quickforge-settings-row-main">
390
+ <div class="quickforge-settings-row-title">
391
+ ${M(`theme`)}
392
+ <quickforge-info-tip .label=${M(`themeDescription`)}></quickforge-info-tip>
393
+ </div>
394
+ <div class="quickforge-settings-row-description">${M(`themeDescription`)}</div>
395
+ </div>
396
+ <div class="quickforge-settings-row-control">
397
+ <div class="quickforge-settings-segmented" role="group" aria-label=${M(`theme`)}>
398
+ ${Xe.map(e=>this.renderThemeOption(e))}
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ ${this.renderFontSizeSlider(M(`interfaceFontSize`),null,this.interfaceFontSizePx,e=>this.updateInterfaceFontSize(e))}
404
+
405
+ ${this.renderFontSizeSlider(M(`messageFontSize`),M(`messageFontSizeNote`),this.messageFontSizePx,e=>this.updateMessageFontSize(e))}
406
+ </section>
407
+
408
+ ${this.error?b`<span class="quickforge-settings-error">${this.error}</span>`:null}
409
+ </div>
410
+ `}},L=`quickforge-appearance-settings-tab`;customElements.get(L)||customElements.define(L,Ze);function Qe(){return document.createElement(L)}var $e=`auto-compact-settings`,R={enabled:!0,thresholdPercent:80,keepRecentTurns:3,minSourceChars:1600,requireConfirmation:!0};function z(e,t,n,r){let i=Number(e);return Number.isFinite(i)?Math.min(r,Math.max(n,Math.round(i))):t}function et(e){if(!e||typeof e!=`object`)return{...R};let t=e;return{enabled:t.enabled!==!1,thresholdPercent:z(t.thresholdPercent,R.thresholdPercent,50,95),keepRecentTurns:z(t.keepRecentTurns,R.keepRecentTurns,1,20),minSourceChars:z(t.minSourceChars,R.minSourceChars,0,2e5),requireConfirmation:t.requireConfirmation!==!1}}async function tt(e){return et(await e.settings.get($e))}async function nt(e,t){await e.settings.set($e,et(t))}var rt=`auto-archive-settings`,it={enabled:!1};function at(e){return!e||typeof e!=`object`?{...it}:{enabled:e.enabled===!0}}async function ot(e){return at(await e.settings.get(rt))}async function st(e,t){await e.settings.set(rt,at(t))}var ct=0,lt=class extends le{static properties={value:{type:String},placeholder:{type:String},options:{type:Array},disabled:{type:Boolean},label:{type:String},searchable:{type:Boolean},searchPlaceholder:{type:String},noResultsLabel:{type:String}};value=``;placeholder=``;options=[];disabled=!1;label=``;searchable=!1;searchPlaceholder=``;noResultsLabel=``;triggerRef=ae();searchRef=ae();_menu=null;_menuId=`quickforge-settings-select-menu-${++ct}`;_listboxId=`quickforge-settings-select-listbox-${++ct}`;_open=!1;_searchQuery=``;focusedIndex=-1;createRenderRoot(){return this}render(){let e=this.options.find(e=>e.value===this.value);return b`
411
+ <button
412
+ ${ce(this.triggerRef)}
413
+ type="button"
414
+ class="quickforge-settings-select quickforge-settings-select-button quickforge-settings-select-trigger"
415
+ role="combobox"
416
+ aria-haspopup="listbox"
417
+ aria-expanded=${this._open?`true`:`false`}
418
+ aria-controls=${this._listboxId}
419
+ aria-activedescendant=${this._open?this._focusedOptionId():void 0}
420
+ aria-label=${this.label}
421
+ ?disabled=${this.disabled}
422
+ @click=${this._toggle}
423
+ @keydown=${this._handleTriggerKeyDown}
424
+ >
425
+ <span class="quickforge-settings-select-trigger-label">${e?e.label:this.placeholder}</span>
426
+ <span class="quickforge-settings-select-chevron" aria-hidden="true">▾</span>
427
+ </button>
428
+ `}updated(){this._refreshMenu()}disconnectedCallback(){super.disconnectedCallback(),this._close()}_toggle=()=>{this.disabled||(this._open?this._close(!0):this._openMenu())};_handleTriggerKeyDown=e=>{if(this.disabled||this._open)return;let t;switch(e.key){case`ArrowDown`:case`Home`:t=this._selectableIndexes()[0]??-1;break;case`ArrowUp`:case`End`:{let e=this._selectableIndexes();t=e[e.length-1]??-1;break}default:return}e.preventDefault(),this._openMenu(t)};_openMenu(e){if(this.disabled||this._open||!this.triggerRef.value)return;let t=this._selectableIndexes(),n=this.options.findIndex(e=>e.value===this.value);this.focusedIndex=e===void 0?n>=0&&!this.options[n].disabled?n:t[0]??-1:e,this._open=!0,this._attachMenu(),document.addEventListener(`pointerdown`,this._handlePointerDown,!0),document.addEventListener(`keydown`,this._handleKeyDown),window.addEventListener(`scroll`,this._reposition,!0),window.addEventListener(`resize`,this._reposition),this.requestUpdate(),this.searchable&&this.searchRef.value?.focus(),this._scrollFocusedIntoView()}_close(e=!1){this._open&&(this._open=!1,this.focusedIndex=-1,this._searchQuery=``,this._removeMenu(),document.removeEventListener(`pointerdown`,this._handlePointerDown,!0),document.removeEventListener(`keydown`,this._handleKeyDown),window.removeEventListener(`scroll`,this._reposition,!0),window.removeEventListener(`resize`,this._reposition),e&&this.triggerRef.value?.focus(),this.requestUpdate())}_select(e){this._close(!0),this.dispatchEvent(new CustomEvent(`change`,{detail:e,bubbles:!0,composed:!0}))}_selectFocused(){let e=this.options[this.focusedIndex];e&&!e.disabled&&this._select(e.value)}_isVisible(e){if(!this.searchable)return!0;let t=this._searchQuery.trim().toLowerCase();return t?e.label.toLowerCase().includes(t):!0}_selectableIndexes(){return this.options.map((e,t)=>!e.disabled&&this._isVisible(e)?t:-1).filter(e=>e>=0)}_moveFocus(e){let t=this._selectableIndexes();if(t.length===0)return;let n=t.indexOf(this.focusedIndex),r=n>=0?n:e>0?-1:0,i=Math.max(0,Math.min(t.length-1,r+e));this.focusedIndex=t[i],this._scrollFocusedIntoView()}_attachMenu(){if(this._removeMenu(),!this.triggerRef.value)return;let e=document.createElement(`div`);e.className=`quickforge-settings-select-menu`,e.id=this._menuId,document.body.appendChild(e),this._menu=e,this._renderMenuItems(),this._reposition()}_removeMenu(){this._menu&&=(this._menu.remove(),null)}_renderMenuItems(){this._menu&&se(this._menuTemplate(),this._menu)}_rerenderMenuItems(){this._open&&this._menu&&this._renderMenuItems()}_refreshMenu(){if(!this._open||!this._menu)return;let e=this._selectableIndexes();e.includes(this.focusedIndex)||(this.focusedIndex=e[0]??-1),this._renderMenuItems(),this._reposition()}_reposition=()=>{let e=this._menu,t=this.triggerRef.value;if(!e||!t)return;let n=t.getBoundingClientRect(),r=Math.min(300,e.offsetHeight||300),i=window.innerHeight-n.bottom,a=n.top,o=i-6<r&&a>i,s=Math.max(0,(o?a:i)-6-8);e.style.top=o?`auto`:`${Math.round(n.bottom+6)}px`,e.style.bottom=o?`${Math.round(window.innerHeight-n.top+6)}px`:`auto`,e.style.maxHeight=`${Math.max(120,Math.min(300,s))}px`,e.style.minWidth=`max(12rem, ${Math.round(n.width)}px)`;let c=e.offsetWidth,ee=Math.max(8,Math.min(n.left,window.innerWidth-c-8));e.style.left=`${Math.round(ee)}px`};_optionId(e){return`${this._listboxId}-option-${e}`}_focusedOptionId(){return this.focusedIndex>=0?this._optionId(this.focusedIndex):void 0}_isSearchFocused(){return this.searchable&&document.activeElement===this.searchRef.value}_scrollFocusedIntoView(){let e=this._menu;if(!e||this.focusedIndex<0)return;let t=e.querySelector(`.quickforge-settings-select-listbox`),n=e.querySelector(`[id="${this._optionId(this.focusedIndex)}"]`);if(!t||!n)return;let r=t.getBoundingClientRect(),i=n.getBoundingClientRect();i.top<r.top?t.scrollTop-=r.top-i.top:i.bottom>r.bottom&&(t.scrollTop+=i.bottom-r.bottom)}_menuTemplate(){let e=this._focusedOptionId(),t=this.options.filter(e=>this._isVisible(e)).length;return b`
429
+ ${this.searchable?b`
430
+ <div class="quickforge-settings-select-search">
431
+ <input
432
+ ${ce(this.searchRef)}
433
+ class="quickforge-settings-select-search-input"
434
+ type="text"
435
+ role="searchbox"
436
+ placeholder=${this.searchPlaceholder}
437
+ aria-label=${this.label}
438
+ aria-controls=${this._listboxId}
439
+ aria-activedescendant=${e}
440
+ .value=${this._searchQuery}
441
+ @input=${this._handleSearchInput}
442
+ >
443
+ </div>
444
+ `:``}
445
+ <div
446
+ id=${this._listboxId}
447
+ class="quickforge-settings-select-listbox"
448
+ role="listbox"
449
+ aria-label=${this.label}
450
+ >
451
+ ${this.options.map((e,t)=>{if(!this._isVisible(e))return oe;let n=e.value===this.value,r=this.focusedIndex===t;return b`
452
+ <div
453
+ id=${this._optionId(t)}
454
+ role="option"
455
+ aria-selected=${n?`true`:`false`}
456
+ aria-disabled=${e.disabled?`true`:`false`}
457
+ class="quickforge-settings-select-option${r?` quickforge-settings-select-option-focused`:``}${n?` quickforge-settings-select-option-selected`:``}${e.disabled?` quickforge-settings-select-option-disabled`:``}"
458
+ @mousemove=${()=>{!e.disabled&&this.focusedIndex!==t&&(this.focusedIndex=t,this._rerenderMenuItems())}}
459
+ @click=${()=>{e.disabled||this._select(e.value)}}
460
+ >
461
+ <span class="quickforge-settings-select-option-label">${e.label}</span>
462
+ ${n?b`
463
+ <svg class="quickforge-settings-select-option-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"></polyline></svg>
464
+ `:``}
465
+ </div>
466
+ `})}
467
+ </div>
468
+ ${this.searchable&&t===0?b`
469
+ <div class="quickforge-settings-select-no-results" role="status">${this.noResultsLabel}</div>
470
+ `:``}
471
+ `}_handleSearchInput=e=>{let t=e.target;this._searchQuery=t.value,this._rerenderMenuItems();let n=this._selectableIndexes();n.includes(this.focusedIndex)||(this.focusedIndex=n[0]??-1),this._reposition(),this._scrollFocusedIntoView()};_handlePointerDown=e=>{let t=e.target;t&&(this.contains(t)||this._menu?.contains(t)||this._close(!0))};_handleKeyDown=e=>{if(!(!this._open||e.isComposing))switch(e.key){case`ArrowDown`:e.preventDefault(),this._moveFocus(1),this._rerenderMenuItems();break;case`ArrowUp`:e.preventDefault(),this._moveFocus(-1),this._rerenderMenuItems();break;case`Home`:{e.preventDefault();let t=this._selectableIndexes();this.focusedIndex=t[0]??-1,this._rerenderMenuItems(),this._scrollFocusedIntoView();break}case`End`:{e.preventDefault();let t=this._selectableIndexes();this.focusedIndex=t[t.length-1]??-1,this._rerenderMenuItems(),this._scrollFocusedIntoView();break}case`Enter`:e.preventDefault(),this._selectFocused();break;case` `:if(this._isSearchFocused())break;e.preventDefault(),this._selectFocused();break;case`Escape`:e.preventDefault(),this._close(!0);break;case`Tab`:this._close(),this.triggerRef.value?.focus();break}}},ut=`quickforge-settings-select`;customElements.get(ut)||customElements.define(ut,lt);var dt=[{value:`off`,label:()=>M(`thinkingOff`)},{value:`low`,label:()=>M(`thinkingLow`)},{value:`medium`,label:()=>M(`thinkingMedium`)},{value:`high`,label:()=>M(`thinkingHigh`)},{value:`xhigh`,label:()=>M(`thinkingXHigh`)}],ft=[{value:`compact`,label:()=>M(`toolDisplayCompact`)},{value:`detailed`,label:()=>M(`toolDisplayDetailed`)}],B=`__custom__`;function pt(e){return(e??``).trim().replace(/\/$/,``)}function V(e){return JSON.stringify([e.provider,e.id,e.api,pt(e.baseUrl)])}function mt(){return`custom_${globalThis.crypto?.randomUUID?.().slice(0,8)||Date.now().toString(36)}`}function ht(e){let t=e.trim(),n=t.split(/[\\/]/).pop()?.replace(/^"|"$/g,``)||t;return/^bash(\.exe)?$/i.test(n)?`Bash`:/^zsh$/i.test(n)?`Zsh`:/^fish$/i.test(n)?`Fish`:/^cmd(\.exe)?$/i.test(n)?`Command Prompt`:/^powershell(\.exe)?$/i.test(n)?`Windows PowerShell`:/^pwsh(\.exe)?$/i.test(n)?`PowerShell 7+`:n||`Custom Shell`}var gt=b`
472
+ <svg class="size-3.5" viewBox="0 0 16 16" fill="none" aria-hidden="true">
473
+ <path d="M4.5 4.5 11.5 11.5M11.5 4.5 4.5 11.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" />
474
+ </svg>
475
+ `,_t=class extends x{models=[];selectedModel;harness=`quickforge`;savedHarness=`quickforge`;defaultOptionsSavePromise=Promise.resolve();thinkingLevel=`off`;toolDisplayMode=`compact`;showContextUsage=!1;autoCompactEnabled=!0;autoCompactRequireConfirmation=!0;autoCompactThresholdPercent=80;autoCompactThresholdPercentInput=`80`;autoCompactKeepRecentTurns=3;autoArchiveEnabled=!1;systemNotificationsEnabled=!1;systemNotificationPermission=`unsupported`;systemNotificationBusy=!1;selectedLanguage=ye();loading=!0;saved=!1;savedMessage=``;error=``;terminalShellConfig={terminalShell:`auto`,defaultProfileId:`auto`,profiles:[]};customShellCommand=``;customShellEditorOpen=!1;networkProxyMode=`direct`;networkProxyUrl=``;savedNetworkProxyConfig={mode:`direct`,proxyUrl:``};networkProxyStatus=null;networkProxySaving=!1;networkProxyLoaded=!1;getTabName(){return M(`defaultOptions`)}connectedCallback(){super.connectedCallback(),this.loadSettings(),window.addEventListener(ve,this.handleCloudStateChanged)}disconnectedCallback(){window.removeEventListener(ve,this.handleCloudStateChanged),super.disconnectedCallback?.()}handleCloudStateChanged=()=>{this.loadSettings()};async loadCloudModels(){try{let e=await pe();return!e.configured||e.enabled===!1||e.mode===`local`||!e.hasSession?[]:await me()}catch(e){return w.warn(`Failed to load QuickForge Cloud models for default options:`,e),[]}}async loadSettings(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=S(),[t,n,r,i,a,o,s]=await Promise.all([Ee(e),de().catch(()=>[]),this.loadCloudModels(),Fe(e),_e(e),tt(e),ot(e)]),c=Re(n.length?n:t,r);this.models=c,this.selectedModel=i.model?c.find(e=>V(e)===V(i.model))??c[0]:c[0],this.thinkingLevel=i.thinkingLevel??ge(this.selectedModel),this.harness=i.harness??`quickforge`,this.savedHarness=this.harness,this.toolDisplayMode=a.toolDisplayMode,this.showContextUsage=a.showContextUsage,this.autoCompactEnabled=o.enabled,this.autoCompactRequireConfirmation=o.requireConfirmation,this.autoCompactThresholdPercent=o.thresholdPercent,this.autoCompactThresholdPercentInput=String(o.thresholdPercent),this.autoCompactKeepRecentTurns=o.keepRecentTurns,this.autoArchiveEnabled=s.enabled,this.systemNotificationsEnabled=be(),this.systemNotificationPermission=await Ae(),this.systemNotificationsEnabled&&this.systemNotificationPermission!==`granted`&&(this.systemNotificationsEnabled=!1,Ce(!1)),await Promise.all([this.loadTerminalShell(),this.loadNetworkProxy()])}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}updateModel(e){let t=this.models.find(t=>V(t)===e);this.selectedModel=t,this.thinkingLevel=ge(t),this.saved=!1,this.requestUpdate(),this.saveDefaultModelOptions()}updateHarness(e){this.harness=e===`opencode`?`opencode`:`quickforge`,this.saved=!1,this.requestUpdate(),this.saveDefaultModelOptions()}updateThinkingLevel(e){this.thinkingLevel=dt.some(t=>t.value===e)?e:`off`,this.saved=!1,this.requestUpdate(),this.saveDefaultModelOptions()}updateLanguage(e){this.selectedLanguage=e===`zh`?`zh`:`en`,this.requestUpdate(),this.applyLanguage()}async applyLanguage(){await Oe(S(),this.selectedLanguage)}updateToolDisplayMode(e){this.toolDisplayMode!==e&&(this.toolDisplayMode=e,this.saved=!1,this.requestUpdate(),this.saveToolDisplayOptions())}updateShowContextUsage(e){this.showContextUsage=e,this.saved=!1,this.requestUpdate(),this.saveToolDisplayOptions()}updateAutoCompactEnabled(e){this.autoCompactEnabled=e,this.saved=!1,this.requestUpdate(),this.saveAutoCompactOptions()}updateAutoCompactRequireConfirmation(e){this.autoCompactRequireConfirmation=e,this.saved=!1,this.requestUpdate(),this.saveAutoCompactOptions()}updateAutoArchiveEnabled(e){this.autoArchiveEnabled=e,this.saved=!1,this.requestUpdate(),this.saveAutoArchiveOptions()}async updateSystemNotifications(e){this.systemNotificationBusy=!0,this.saved=!1,this.error=``,this.requestUpdate();try{if(!e){Ce(!1),this.systemNotificationsEnabled=!1,this.markSaved(M(`systemNotificationsDisabled`));return}let t=await ze();this.systemNotificationPermission=t,this.systemNotificationsEnabled=t===`granted`,t===`granted`?this.markSaved(M(`systemNotificationsEnabled`)):t===`denied`?this.error=M(`systemNotificationsDeniedHelp`):t===`unsupported`&&(this.error=M(`systemNotificationsUnsupported`))}catch(e){this.systemNotificationsEnabled=!1,Ce(!1),this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.systemNotificationBusy=!1,this.requestUpdate()}}async sendTestSystemNotification(){this.systemNotificationBusy=!0,this.error=``,this.requestUpdate();try{await Me({key:`test:${Date.now()}`,title:M(`systemNotificationTestTitle`),status:`idle`,force:!0})||(this.error=M(`systemNotificationTestFailed`))}catch(e){this.error=e instanceof Error?e.message:M(`systemNotificationTestFailed`)}finally{this.systemNotificationBusy=!1,this.requestUpdate()}}systemNotificationStatusText(){return this.systemNotificationPermission===`unsupported`?M(`systemNotificationsUnsupported`):this.systemNotificationPermission===`denied`?M(`systemNotificationsPermissionDenied`):this.systemNotificationPermission===`granted`?M(`systemNotificationsPermissionGranted`):M(`systemNotificationsPermissionPrompt`)}updateAutoCompactThresholdPercent(e){this.autoCompactThresholdPercentInput=e;let t=Number(e);e!==``&&Number.isFinite(t)&&(this.autoCompactThresholdPercent=t),this.saved=!1,this.requestUpdate()}updateAutoCompactKeepRecentTurns(e){this.autoCompactKeepRecentTurns=Number(e)||3,this.saved=!1,this.requestUpdate()}commitAutoCompactThresholdPercent(){let e=Number(this.autoCompactThresholdPercentInput);if(!Number.isFinite(e)){this.autoCompactThresholdPercentInput=String(this.autoCompactThresholdPercent),this.requestUpdate();return}let t=Math.max(50,Math.min(95,Math.round(e)));this.autoCompactThresholdPercent=t,this.autoCompactThresholdPercentInput=String(t),this.requestUpdate(),this.saveAutoCompactOptions()}commitAutoCompactKeepRecentTurns(){let e=Math.max(1,Math.min(20,Math.round(this.autoCompactKeepRecentTurns||3)));this.autoCompactKeepRecentTurns=e,this.requestUpdate(),this.saveAutoCompactOptions()}markSaved(e=M(`defaultOptionsSaved`)){this.saved=!0,this.savedMessage=e,this.error=``,this.requestUpdate()}customShellProfiles(){return this.terminalShellConfig.profiles.filter(e=>!e.builtin)}selectedTerminalShellProfileId(){let e=this.terminalShellConfig.profiles;return e.some(e=>e.id===this.terminalShellConfig.defaultProfileId)?this.terminalShellConfig.defaultProfileId:e[0]?.id||B}updateTerminalShellSelection(e){if(e===B){this.customShellEditorOpen=!0,this.requestUpdate();return}this.customShellEditorOpen=!1,this.requestUpdate(),this.saveTerminalShellConfig(e)}async loadNetworkProxy(){try{let e=await fetch(`/api/system/network-proxy`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`requestFailed`));this.networkProxyMode=t?.config?.mode||`direct`,this.networkProxyUrl=t?.config?.proxyUrl||``,this.savedNetworkProxyConfig={mode:this.networkProxyMode,proxyUrl:this.networkProxyUrl},this.networkProxyStatus=t?.status||null,this.networkProxyLoaded=!0}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}}updateNetworkProxyMode(e){this.networkProxyMode===e||this.networkProxySaving||!this.networkProxyLoaded||e===`pac`&&this.networkProxyStatus?.features?.pacUrl!==!0||(this.networkProxyMode=e,this.saved=!1,this.error=``,this.requestUpdate(),!(e===`manual`||e===`pac`)&&this.saveNetworkProxy())}updateNetworkProxyUrl(e){this.networkProxyUrl=e,this.saved=!1,this.requestUpdate()}networkProxyValidationError(){let e=this.networkProxyUrl.trim(),t=this.networkProxyMode===`pac`;if(!e)return M(t?`networkProxyPacUrlRequired`:`networkProxyAddressRequired`);try{let n=new URL(e);return[`http:`,`https:`].includes(n.protocol)?n.hostname?!t&&!n.port?M(`networkProxyAddressPortError`):n.username||n.password?M(t?`networkProxyPacUrlCredentialsError`:`networkProxyAddressCredentialsError`):t&&n.hash?M(`networkProxyPacUrlFragmentError`):!t&&(n.pathname&&n.pathname!==`/`||n.search||n.hash)?M(`networkProxyAddressPathError`):``:M(t?`networkProxyPacUrlHostError`:`networkProxyAddressPortError`):M(t?`networkProxyPacUrlProtocolError`:`networkProxyAddressProtocolError`)}catch{return M(t?`networkProxyPacUrlInvalid`:`networkProxyAddressInvalid`)}}async saveNetworkProxy(){if(!(this.networkProxySaving||!this.networkProxyLoaded)){if(this.networkProxyMode===`pac`&&this.networkProxyStatus?.features?.pacUrl!==!0){this.saved=!1,this.error=M(`networkProxyPacUnsupported`),this.requestUpdate();return}if(this.networkProxyMode===`manual`||this.networkProxyMode===`pac`){let e=this.networkProxyValidationError();if(e){this.saved=!1,this.error=e,this.requestUpdate();return}}this.networkProxySaving=!0,this.saved=!1,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/network-proxy`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({mode:this.networkProxyMode,proxyUrl:this.networkProxyUrl})}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`requestFailed`));this.networkProxyMode=t?.config?.mode||this.networkProxyMode,this.networkProxyUrl=t?.config?.proxyUrl||``,this.savedNetworkProxyConfig={mode:this.networkProxyMode,proxyUrl:this.networkProxyUrl},this.networkProxyStatus=t?.status||null,this.markSaved(M(`networkProxySaved`))}catch(e){this.networkProxyMode=this.savedNetworkProxyConfig.mode,this.networkProxyUrl=this.savedNetworkProxyConfig.proxyUrl,this.saved=!1,this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.networkProxySaving=!1,this.requestUpdate()}}}async refreshNetworkProxy(){if(!(this.networkProxySaving||!this.networkProxyLoaded)){this.networkProxySaving=!0,this.saved=!1,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/network-proxy/refresh`,{method:`POST`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`requestFailed`));this.networkProxyStatus=t?.status||null,this.markSaved(M(`networkProxyRefreshed`))}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.networkProxySaving=!1,this.requestUpdate()}}}renderNetworkProxyModeOption(e,t){let n=this.networkProxyMode===e,r=e===`pac`&&this.networkProxyStatus?.features?.pacUrl!==!0;return b`
476
+ <button
477
+ type="button"
478
+ class="quickforge-settings-segmented-option ${n?`quickforge-settings-segmented-option-active`:``}"
479
+ aria-pressed=${n?`true`:`false`}
480
+ title=${r?M(`networkProxyPacUnsupported`):``}
481
+ ?disabled=${this.networkProxySaving||!this.networkProxyLoaded||r}
482
+ @click=${()=>this.updateNetworkProxyMode(e)}
483
+ >
484
+ ${t}
485
+ </button>
486
+ `}networkProxySettings(){let e=this.networkProxyMode===`manual`||this.networkProxyMode===`pac`,t=this.networkProxyMode===`pac`&&this.networkProxyStatus?.features?.pacUrl!==!0,n=e?this.networkProxyValidationError():``,r=e&&(this.networkProxyUrl!==this.savedNetworkProxyConfig.proxyUrl||this.savedNetworkProxyConfig.mode!==this.networkProxyMode),i=this.networkProxyLoaded?t?M(`networkProxyPacUnsupported`):this.networkProxyStatus?.supported===!1?this.networkProxyStatus.error||M(`networkProxyUnsupported`):M(`networkProxyStatus`,{source:this.networkProxyStatus?.source||M(`unknown`),runtime:this.networkProxyStatus?.runtimeKind||M(`unknown`)}):M(`networkProxyLoadFailed`);return b`
487
+ <section class="quickforge-settings-section" aria-label=${M(`networkConnection`)}>
488
+ <div class="quickforge-settings-row">
489
+ <div class="quickforge-settings-row-main">
490
+ <div class="quickforge-settings-row-title">
491
+ ${M(`networkProxyMode`)}
492
+ <quickforge-info-tip .label=${M(`networkProxyDescription`)}></quickforge-info-tip>
493
+ </div>
494
+ <div class="quickforge-settings-row-description">${i}</div>
495
+ </div>
496
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
497
+ <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap" role="group" aria-label=${M(`networkProxyMode`)}>
498
+ ${this.renderNetworkProxyModeOption(`direct`,M(`networkProxyDirect`))}
499
+ ${this.renderNetworkProxyModeOption(`system`,M(`networkProxySystem`))}
500
+ ${this.renderNetworkProxyModeOption(`manual`,M(`networkProxyManual`))}
501
+ ${this.renderNetworkProxyModeOption(`pac`,M(`networkProxyPac`))}
502
+ </div>
503
+ </div>
504
+ </div>
505
+
506
+ ${e?b`
507
+ <div class="quickforge-settings-row">
508
+ <div class="quickforge-settings-row-main">
509
+ <div class="quickforge-settings-row-title">
510
+ ${M(this.networkProxyMode===`pac`?`networkProxyPacUrl`:`networkProxyAddress`)}
511
+ </div>
512
+ <div class="quickforge-settings-row-description">
513
+ ${M(this.networkProxyMode===`pac`?`networkProxyPacUrlDescription`:`networkProxyAddressDescription`)}
514
+ </div>
515
+ </div>
516
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-network-proxy-control">
517
+ <input
518
+ id="quickforge-network-proxy-url"
519
+ class="quickforge-settings-input quickforge-settings-mono"
520
+ type="url"
521
+ aria-label=${M(this.networkProxyMode===`pac`?`networkProxyPacUrl`:`networkProxyAddress`)}
522
+ aria-invalid=${r&&n?`true`:`false`}
523
+ .value=${this.networkProxyUrl}
524
+ placeholder=${this.networkProxyMode===`pac`?`https://example.com/proxy.pac`:`http://127.0.0.1:7890`}
525
+ ?disabled=${this.networkProxySaving||!this.networkProxyLoaded||t}
526
+ @input=${e=>this.updateNetworkProxyUrl(e.target.value)}
527
+ @keydown=${e=>{e.key===`Enter`&&this.networkProxyUrl.trim()&&(e.preventDefault(),this.saveNetworkProxy())}}
528
+ />
529
+ <button
530
+ class="quickforge-settings-button quickforge-settings-button-primary"
531
+ type="button"
532
+ ?disabled=${this.networkProxySaving||!this.networkProxyLoaded||t||!!n||!r}
533
+ @click=${()=>this.saveNetworkProxy()}
534
+ >
535
+ ${this.networkProxySaving?M(`saving`):M(`networkProxySave`)}
536
+ </button>
537
+ </div>
538
+ </div>
539
+ `:null}
540
+
541
+ ${this.networkProxyMode===`system`?b`
542
+ <div class="quickforge-settings-row">
543
+ <div class="quickforge-settings-row-main">
544
+ <div class="quickforge-settings-row-title">${M(`networkProxyRefresh`)}</div>
545
+ <div class="quickforge-settings-row-description">${M(`networkProxyRefreshDescription`)}</div>
546
+ </div>
547
+ <div class="quickforge-settings-row-control">
548
+ <button
549
+ class="quickforge-settings-button"
550
+ type="button"
551
+ ?disabled=${this.networkProxySaving||!this.networkProxyLoaded}
552
+ @click=${()=>this.refreshNetworkProxy()}
553
+ >
554
+ ${this.networkProxySaving?M(`saving`):M(`networkProxyRefresh`)}
555
+ </button>
556
+ </div>
557
+ </div>
558
+ `:null}
559
+ </section>
560
+ `}async loadTerminalShell(){try{let e=await fetch(`/api/system/terminal-shell`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`requestFailed`));this.terminalShellConfig={terminalShell:t?.terminalShell||`auto`,defaultProfileId:t?.defaultProfileId||`auto`,profiles:Array.isArray(t?.profiles)?t.profiles:[]}}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}}async saveTerminalShellConfig(e,t=this.customShellProfiles(),n=M(`terminalShellSaved`)){try{let r=await fetch(`/api/system/terminal-shell`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({defaultProfileId:e,profiles:t})}),i=await r.json().catch(()=>null);if(!r.ok)throw Error(i?.error||M(`requestFailed`));this.terminalShellConfig=i,this.markSaved(n)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}async addCustomTerminalShell(){let e=this.customShellCommand.trim();if(!e){this.error=M(`terminalShellProfileRequired`),this.requestUpdate();return}let t={id:mt(),name:ht(e),command:e,builtin:!1},n=[...this.customShellProfiles(),t];this.customShellCommand=``,this.customShellEditorOpen=!1,await this.saveTerminalShellConfig(t.id,n,M(`terminalShellProfilesSaved`))}async deleteCustomTerminalShell(e){let t=this.terminalShellConfig.profiles.find(t=>t.id===e);if(!t||t.builtin||!await T({description:M(`terminalShellDeleteConfirm`,{name:t.name}),confirmLabel:M(`confirmDelete`),cancelLabel:M(`cancel`),variant:`destructive`}))return;let n=this.customShellProfiles().filter(t=>t.id!==e),r=this.terminalShellConfig.defaultProfileId===e?`auto`:this.terminalShellConfig.defaultProfileId;await this.saveTerminalShellConfig(r,n,M(`terminalShellProfilesSaved`))}async saveDefaultModelOptions(){let e=this.selectedModel,t=e?.reasoning?this.thinkingLevel:`off`,n=this.harness,r=async()=>{try{await xe(S(),{model:e,thinkingLevel:t,harness:n}),this.savedHarness!==n&&(he(n),this.savedHarness=n),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}};this.defaultOptionsSavePromise=this.defaultOptionsSavePromise.then(r,r),await this.defaultOptionsSavePromise}async saveToolDisplayOptions(){try{await fe(S(),{toolDisplayMode:this.toolDisplayMode,showContextUsage:this.showContextUsage}),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}async saveAutoCompactOptions(){try{await nt(S(),{enabled:this.autoCompactEnabled,thresholdPercent:this.autoCompactThresholdPercent,keepRecentTurns:this.autoCompactKeepRecentTurns,minSourceChars:1600,requireConfirmation:this.autoCompactRequireConfirmation}),this.markSaved()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}async saveAutoArchiveOptions(){try{if(await st(S(),{enabled:this.autoArchiveEnabled}),this.markSaved(M(`autoArchiveSaved`)),typeof BroadcastChannel<`u`)try{let e=new BroadcastChannel(`quickforge-sync`);e.postMessage({type:`settings-changed`,sourceTabId:`default-options-settings-tab`,timestamp:Date.now()}),e.close()}catch{}}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}modelOptions(){return this.models}languageOptions(){return[{value:`zh`,label:M(`simplifiedChinese`)},{value:`en`,label:M(`english`)}]}harnessOptions(){return[{value:`quickforge`,label:`QuickForge`},{value:`claude-code`,label:M(`claudeCodeHarnessUnavailable`),disabled:!0},{value:`opencode`,label:`OpenCode`}]}modelSelectOptions(){return this.modelOptions().map(e=>({value:V(e),label:ue(e)}))}thinkingOptions(){return dt.map(e=>({value:e.value,label:e.label()}))}terminalShellOptions(){return[...this.terminalShellConfig.profiles.map(e=>({value:e.id,label:e.name})),{value:B,label:M(`terminalShellCustomOption`)}]}terminalShellSettings(){let e=this.terminalShellConfig.profiles,t=this.selectedTerminalShellProfileId(),n=e.find(e=>e.id===t),r=this.customShellEditorOpen||e.length===0;return b`
561
+ <section class="quickforge-settings-section" aria-label=${M(`terminalShell`)}>
562
+ <div class="quickforge-settings-row">
563
+ <div class="quickforge-settings-row-main">
564
+ <div class="quickforge-settings-row-title">
565
+ ${M(`terminalShellDefault`)}
566
+ <quickforge-info-tip .label=${M(`terminalShellDescription`)}></quickforge-info-tip>
567
+ </div>
568
+ <div class="quickforge-settings-row-description quickforge-settings-mono">
569
+ ${n?.command||M(`terminalShellNoDetected`)}
570
+ </div>
571
+ </div>
572
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
573
+ <quickforge-settings-select
574
+ .value=${r?B:t}
575
+ .options=${this.terminalShellOptions()}
576
+ label=${M(`terminalShellDefault`)}
577
+ @change=${e=>this.updateTerminalShellSelection(e.detail)}
578
+ ></quickforge-settings-select>
579
+ ${n&&!n.builtin&&!r?b`
580
+ <button
581
+ class="quickforge-settings-icon-action quickforge-settings-icon-action-danger"
582
+ type="button"
583
+ title=${M(`delete`)}
584
+ aria-label=${M(`delete`)}
585
+ @click=${()=>this.deleteCustomTerminalShell(n.id)}
586
+ >
587
+ ${gt}
588
+ </button>
589
+ `:null}
590
+ </div>
591
+ </div>
592
+
593
+ ${r?b`
594
+ <div class="quickforge-settings-row">
595
+ <div class="quickforge-settings-row-main">
596
+ <div class="quickforge-settings-row-title">${M(`terminalShellCommand`)}</div>
597
+ <div class="quickforge-settings-row-description">${M(`terminalShellCustomDescription`)}</div>
598
+ </div>
599
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-terminal-shell-command-control">
600
+ <input
601
+ class="quickforge-settings-input quickforge-settings-mono"
602
+ type="text"
603
+ .value=${this.customShellCommand}
604
+ placeholder=${M(`terminalShellCommandPlaceholder`)}
605
+ @input=${e=>{this.customShellCommand=e.target.value}}
606
+ />
607
+ <button
608
+ class="quickforge-settings-button quickforge-settings-button-primary"
609
+ type="button"
610
+ title=${M(`terminalShellAdd`)}
611
+ aria-label=${M(`terminalShellAdd`)}
612
+ @click=${()=>this.addCustomTerminalShell()}
613
+ >
614
+ ${M(`terminalShellAdd`)}
615
+ </button>
616
+ </div>
617
+ </div>
618
+ `:null}
619
+ </section>
620
+ `}renderToolDisplayModeOption(e){let t=this.toolDisplayMode===e.value;return b`
621
+ <button
622
+ type="button"
623
+ class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
624
+ aria-pressed=${t?`true`:`false`}
625
+ @click=${()=>this.updateToolDisplayMode(e.value)}
626
+ >
627
+ ${e.label()}
628
+ </button>
629
+ `}renderSwitch(e,t,n=!1){return b`
630
+ <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
631
+ <input
632
+ type="checkbox"
633
+ .checked=${e}
634
+ ?disabled=${n}
635
+ @change=${e=>t(e.target.checked)}
636
+ />
637
+ <span aria-hidden="true"></span>
638
+ </label>
639
+ `}render(){return this.loading?b`<div class="text-sm text-muted-foreground">${M(`loading`)}</div>`:b`
640
+ <div class="quickforge-settings-stack">
641
+ <section class="quickforge-settings-section" aria-label=${M(`defaultOptions`)}>
642
+ <div class="quickforge-settings-row">
643
+ <div class="quickforge-settings-row-main">
644
+ <div class="quickforge-settings-row-title">
645
+ ${M(`language`)}
646
+ <quickforge-info-tip .label=${M(`languageDescription`)}></quickforge-info-tip>
647
+ </div>
648
+ <div class="quickforge-settings-row-description">${M(`displayLanguage`)}</div>
649
+ </div>
650
+ <div class="quickforge-settings-row-control">
651
+ <quickforge-settings-select
652
+ .value=${this.selectedLanguage}
653
+ .options=${this.languageOptions()}
654
+ label=${M(`language`)}
655
+ @change=${e=>this.updateLanguage(e.detail)}
656
+ ></quickforge-settings-select>
657
+ </div>
658
+ </div>
659
+
660
+ <div class="quickforge-settings-row">
661
+ <div class="quickforge-settings-row-main">
662
+ <div class="quickforge-settings-row-title">${M(`defaultHarness`)}</div>
663
+ <div class="quickforge-settings-row-description">${M(`defaultHarnessDescription`)}</div>
664
+ </div>
665
+ <div class="quickforge-settings-row-control">
666
+ <quickforge-settings-select
667
+ .value=${this.harness}
668
+ .options=${this.harnessOptions()}
669
+ label=${M(`defaultHarness`)}
670
+ @change=${e=>this.updateHarness(e.detail)}
671
+ ></quickforge-settings-select>
672
+ </div>
673
+ </div>
674
+
675
+ <div class="quickforge-settings-row">
676
+ <div class="quickforge-settings-row-main">
677
+ <div class="quickforge-settings-row-title">${M(`defaultModel`)}</div>
678
+ <div class="quickforge-settings-row-description">${M(`defaultModelDescription`)}</div>
679
+ </div>
680
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
681
+ <quickforge-settings-select
682
+ .value=${this.selectedModel?V(this.selectedModel):``}
683
+ .options=${this.modelSelectOptions()}
684
+ .disabled=${this.models.length===0}
685
+ searchable
686
+ searchPlaceholder=${M(`search`)}
687
+ noResultsLabel=${M(`noMatchingOptions`)}
688
+ placeholder=${M(`noModelAvailable`)}
689
+ label=${M(`defaultModel`)}
690
+ @change=${e=>this.updateModel(e.detail)}
691
+ ></quickforge-settings-select>
692
+ </div>
693
+ </div>
694
+
695
+ <div class="quickforge-settings-row">
696
+ <div class="quickforge-settings-row-main">
697
+ <div class="quickforge-settings-row-title">${M(`defaultThinkingLevel`)}</div>
698
+ <div class="quickforge-settings-row-description">
699
+ ${this.selectedModel?.reasoning?M(`defaultThinkingLevelDescription`):M(`thinkingRequiresReasoningModel`)}
700
+ </div>
701
+ </div>
702
+ <div class="quickforge-settings-row-control">
703
+ <quickforge-settings-select
704
+ .value=${this.thinkingLevel}
705
+ .options=${this.thinkingOptions()}
706
+ .disabled=${!this.selectedModel?.reasoning}
707
+ label=${M(`defaultThinkingLevel`)}
708
+ @change=${e=>this.updateThinkingLevel(e.detail)}
709
+ ></quickforge-settings-select>
710
+ </div>
711
+ </div>
712
+
713
+ <div class="quickforge-settings-row">
714
+ <div class="quickforge-settings-row-main">
715
+ <div class="quickforge-settings-row-title">
716
+ ${M(`toolDisplay`)}
717
+ <quickforge-info-tip .label=${M(`toolDisplayModeDescription`)}></quickforge-info-tip>
718
+ </div>
719
+ <div class="quickforge-settings-row-description">${M(`toolDisplayModeDescription`)}</div>
720
+ </div>
721
+ <div class="quickforge-settings-row-control">
722
+ <div class="quickforge-settings-segmented" role="group" aria-label=${M(`toolDisplay`)}>
723
+ ${ft.map(e=>this.renderToolDisplayModeOption(e))}
724
+ </div>
725
+ </div>
726
+ </div>
727
+
728
+ <div class="quickforge-settings-row">
729
+ <div class="quickforge-settings-row-main">
730
+ <div class="quickforge-settings-row-title">
731
+ ${M(`systemNotifications`)}
732
+ <quickforge-info-tip .label=${M(`systemNotificationsDescription`)}></quickforge-info-tip>
733
+ </div>
734
+ <div class="quickforge-settings-row-description">${this.systemNotificationStatusText()}</div>
735
+ </div>
736
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
737
+ ${this.systemNotificationsEnabled?b`
738
+ <button
739
+ class="quickforge-settings-button quickforge-settings-button-secondary"
740
+ type="button"
741
+ ?disabled=${this.systemNotificationBusy}
742
+ @click=${()=>this.sendTestSystemNotification()}
743
+ >${M(`systemNotificationsTest`)}</button>
744
+ `:null}
745
+ ${this.renderSwitch(this.systemNotificationsEnabled,e=>{this.updateSystemNotifications(e)},this.systemNotificationBusy||this.systemNotificationPermission===`unsupported`)}
746
+ </div>
747
+ </div>
748
+
749
+ <div class="quickforge-settings-row">
750
+ <div class="quickforge-settings-row-main">
751
+ <div class="quickforge-settings-row-title">
752
+ ${M(`showContextUsage`)}
753
+ <quickforge-info-tip .label=${M(`showContextUsageDescription`)}></quickforge-info-tip>
754
+ </div>
755
+ <div class="quickforge-settings-row-description">${M(`showContextUsageDescription`)}</div>
756
+ </div>
757
+ <div class="quickforge-settings-row-control">
758
+ ${this.renderSwitch(this.showContextUsage,e=>this.updateShowContextUsage(e))}
759
+ </div>
760
+ </div>
761
+
762
+ <div class="quickforge-settings-row">
763
+ <div class="quickforge-settings-row-main">
764
+ <div class="quickforge-settings-row-title">
765
+ ${M(`autoArchiveEnabled`)}
766
+ <quickforge-info-tip .label=${M(`autoArchiveDescription`)}></quickforge-info-tip>
767
+ </div>
768
+ <div class="quickforge-settings-row-description">${M(`autoArchiveTriggerNote`)}</div>
769
+ </div>
770
+ <div class="quickforge-settings-row-control">
771
+ ${this.renderSwitch(this.autoArchiveEnabled,e=>this.updateAutoArchiveEnabled(e))}
772
+ </div>
773
+ </div>
774
+
775
+ <div class="quickforge-settings-row">
776
+ <div class="quickforge-settings-row-main">
777
+ <div class="quickforge-settings-row-title">
778
+ ${M(`autoCompactEnabled`)}
779
+ <quickforge-info-tip .label=${M(`autoCompactDescription`)}></quickforge-info-tip>
780
+ </div>
781
+ <div class="quickforge-settings-row-description">${M(`autoCompactTriggerNote`)}</div>
782
+ </div>
783
+ <div class="quickforge-settings-row-control">
784
+ ${this.renderSwitch(this.autoCompactEnabled,e=>this.updateAutoCompactEnabled(e))}
785
+ </div>
786
+ </div>
787
+
788
+ <div class="quickforge-settings-row">
789
+ <div class="quickforge-settings-row-main">
790
+ <div class="quickforge-settings-row-title">${M(`autoCompactRequireConfirmation`)}</div>
791
+ <div class="quickforge-settings-row-description">${M(`autoCompactRequireConfirmationDescription`)}</div>
792
+ </div>
793
+ <div class="quickforge-settings-row-control">
794
+ ${this.renderSwitch(this.autoCompactRequireConfirmation,e=>this.updateAutoCompactRequireConfirmation(e),!this.autoCompactEnabled)}
795
+ </div>
796
+ </div>
797
+
798
+ <div class="quickforge-settings-row">
799
+ <div class="quickforge-settings-row-main">
800
+ <div class="quickforge-settings-row-title">${M(`autoCompactThresholdPercent`)}</div>
801
+ <div class="quickforge-settings-row-description">${M(`autoCompactThresholdDescription`)}</div>
802
+ </div>
803
+ <div class="quickforge-settings-row-control">
804
+ <input
805
+ class="quickforge-settings-input quickforge-settings-number-input"
806
+ type="number"
807
+ min="50"
808
+ max="95"
809
+ step="1"
810
+ .value=${this.autoCompactThresholdPercentInput}
811
+ ?disabled=${!this.autoCompactEnabled}
812
+ @input=${e=>this.updateAutoCompactThresholdPercent(e.target.value)}
813
+ @change=${()=>this.commitAutoCompactThresholdPercent()}
814
+ @blur=${()=>this.commitAutoCompactThresholdPercent()}
815
+ />
816
+ </div>
817
+ </div>
818
+
819
+ <div class="quickforge-settings-row">
820
+ <div class="quickforge-settings-row-main">
821
+ <div class="quickforge-settings-row-title">
822
+ ${M(`autoCompactKeepRecentTurns`)}
823
+ <quickforge-info-tip .label=${M(`autoCompactHistoryPreserved`)}></quickforge-info-tip>
824
+ </div>
825
+ <div class="quickforge-settings-row-description">${M(`autoCompactKeepRecentTurnsDescription`)}</div>
826
+ </div>
827
+ <div class="quickforge-settings-row-control">
828
+ <input
829
+ class="quickforge-settings-input quickforge-settings-number-input"
830
+ type="number"
831
+ min="1"
832
+ max="20"
833
+ step="1"
834
+ .value=${String(this.autoCompactKeepRecentTurns)}
835
+ ?disabled=${!this.autoCompactEnabled}
836
+ @input=${e=>this.updateAutoCompactKeepRecentTurns(e.target.value)}
837
+ @change=${()=>this.commitAutoCompactKeepRecentTurns()}
838
+ @blur=${()=>this.commitAutoCompactKeepRecentTurns()}
839
+ />
840
+ </div>
841
+ </div>
842
+ </section>
843
+
844
+ ${this.networkProxySettings()}
845
+
846
+ ${this.terminalShellSettings()}
847
+
848
+ ${this.saved?b`<div class="quickforge-settings-message" role="status">${this.savedMessage||M(`defaultOptionsSaved`)}</div>`:null}
849
+ ${this.error?b`<div class="quickforge-settings-alert" role="alert">${this.error}</div>`:null}
850
+ </div>
851
+ `}},H=`quickforge-default-options-settings-tab`;customElements.get(H)||customElements.define(H,_t);function vt(){return document.createElement(H)}var yt=`memory-settings`,bt={enabled:!0};function xt(e){return!e||typeof e!=`object`?{...bt}:{enabled:e.enabled!==!1}}async function St(e){return xt(await e.settings.get(yt))}async function Ct(e,t){await e.settings.set(yt,xt(t))}var wt=class extends x{enabled=!0;loading=!0;saving=!1;loadingDocument=!1;markdown=``;savedMarkdown=``;memoryPath=`~/.quickforge/MEMORY.md`;message=``;error=``;getTabName(){return M(`memory`)}async connectedCallback(){super.connectedCallback(),await this.loadSettings()}async loadSettings(){this.loading=!0,this.error=``,this.requestUpdate();try{let[e]=await Promise.all([St(S()),this.loadDocument()]);this.enabled=e.enabled}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async loadDocument(e=!1){this.loadingDocument=!0,this.error=``,e&&(this.message=``),this.requestUpdate();try{let t=await fetch(`/api/memory`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(!t.ok||!n)throw Error(n?.error||M(`requestFailed`));this.markdown=n.markdown,this.savedMarkdown=n.markdown,this.memoryPath=n.path||this.memoryPath,e&&(this.message=M(`memoryReloaded`))}finally{this.loadingDocument=!1,this.requestUpdate()}}async updateEnabled(e){let t=this.enabled;this.enabled=e,this.saving=!0,this.message=``,this.error=``,this.requestUpdate();try{await Ct(S(),{enabled:e}),this.message=M(e?`memoryEnabledSaved`:`memoryDisabledSaved`)}catch(e){this.enabled=t,this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}async reloadDocument(){try{await this.loadDocument(!0)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}async saveDocument(){if(!(!this.enabled||this.saving)){this.saving=!0,this.message=``,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/memory`,{method:`PUT`,headers:{"content-type":`application/json`},body:JSON.stringify({markdown:this.markdown})}),t=await e.json().catch(()=>null);if(!e.ok||!t)throw Error(t?.error||M(`requestFailed`));this.markdown=t.markdown,this.savedMarkdown=t.markdown,this.memoryPath=t.path||this.memoryPath,this.message=M(`memoryContentSaved`)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}renderSwitch(){return b`
852
+ <label class="quickforge-settings-switch" aria-disabled=${this.saving?`true`:`false`}>
853
+ <input
854
+ type="checkbox"
855
+ .checked=${this.enabled}
856
+ ?disabled=${this.saving}
857
+ @change=${e=>void this.updateEnabled(e.target.checked)}
858
+ />
859
+ <span aria-hidden="true"></span>
860
+ </label>
861
+ `}render(){if(this.loading)return b`<div class="text-sm text-muted-foreground">${M(`loading`)}</div>`;let e=this.markdown!==this.savedMarkdown,t=this.saving||this.loadingDocument;return b`
862
+ <div class="quickforge-settings-stack">
863
+ <section class="quickforge-settings-section" aria-label=${M(`memory`)}>
864
+ <div class="quickforge-settings-row">
865
+ <div class="quickforge-settings-row-main">
866
+ <div class="quickforge-settings-row-title">
867
+ ${M(`globalMemory`)}
868
+ <quickforge-info-tip .label=${M(`globalMemoryInfo`)}></quickforge-info-tip>
869
+ </div>
870
+ <div class="quickforge-settings-row-description">${M(`globalMemoryDescription`)}</div>
871
+ </div>
872
+ <div class="quickforge-settings-row-control">${this.renderSwitch()}</div>
873
+ </div>
874
+
875
+ <div class="quickforge-settings-row">
876
+ <div class="quickforge-settings-row-main">
877
+ <div class="quickforge-settings-row-title">${M(`memoryFile`)}</div>
878
+ <div class="quickforge-settings-row-description">${M(`memoryFileDescription`)}</div>
879
+ </div>
880
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
881
+ <code class="text-xs text-muted-foreground">${this.memoryPath}</code>
882
+ </div>
883
+ </div>
884
+
885
+ <div class="quickforge-settings-row quickforge-settings-row-align-start quickforge-memory-content-row">
886
+ <div class="quickforge-settings-row-main">
887
+ <div class="quickforge-settings-row-title">${M(`memoryContent`)}</div>
888
+ <div class="quickforge-settings-row-description">${this.enabled?M(`memoryContentDescription`):M(`memoryContentDisabledDescription`)}</div>
889
+ </div>
890
+ <div class="quickforge-memory-editor-control">
891
+ <textarea
892
+ class="quickforge-settings-textarea quickforge-settings-mono quickforge-memory-editor"
893
+ .value=${this.markdown}
894
+ ?disabled=${!this.enabled||t}
895
+ spellcheck="false"
896
+ @input=${e=>{this.markdown=e.target.value,this.message=``,this.requestUpdate()}}
897
+ ></textarea>
898
+ <div class="quickforge-memory-editor-actions">
899
+ <button
900
+ class="quickforge-settings-button quickforge-settings-button-secondary"
901
+ type="button"
902
+ ?disabled=${t}
903
+ @click=${()=>void this.reloadDocument()}
904
+ >${this.loadingDocument?M(`loading`):M(`memoryReload`)}</button>
905
+ <button
906
+ class="quickforge-settings-button quickforge-settings-button-primary"
907
+ type="button"
908
+ ?disabled=${!this.enabled||t||!e}
909
+ @click=${()=>void this.saveDocument()}
910
+ >${this.saving?M(`saving`):M(`memorySave`)}</button>
911
+ </div>
912
+ </div>
913
+ </div>
914
+
915
+ <div class="quickforge-settings-note">${M(`memoryCurrentSessionNote`)}</div>
916
+ </section>
917
+
918
+ ${this.message?b`<div class="quickforge-settings-message">${this.message}</div>`:null}
919
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
920
+ </div>
921
+ `}},U=`quickforge-memory-settings-tab`;customElements.get(U)||customElements.define(U,wt);function Tt(){return document.createElement(U)}var Et=`quickforge-backup`,W=[{id:`settings`,countKey:`settings`,label:()=>M(`restoreSettings`),description:()=>M(`restoreSettingsDescription`)},{id:`mcp`,countKey:`mcp`,label:()=>M(`restoreMcp`),description:()=>M(`restoreMcpDescription`)},{id:`providerKeys`,countKey:`providerKeys`,label:()=>M(`restoreProviderKeys`),description:()=>M(`restoreProviderKeysDescription`)},{id:`customProviders`,countKey:`customProviders`,label:()=>M(`restoreCustomProviders`),description:()=>M(`restoreCustomProvidersDescription`)},{id:`scheduledTasks`,countKey:`scheduledTasks`,label:()=>M(`restoreScheduledTasks`),description:()=>M(`restoreScheduledTasksDescription`)}],Dt=W;function Ot(e,t){let n=new Blob([`${JSON.stringify(t,null,2)}\n`],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=e,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(r)}function kt(){return new Date().toISOString().replace(/[:.]/g,`-`)}function At(e){return e?Object.entries(e).map(([e,t])=>`${e}: ${t}`).join(`, `):``}function jt(e){return Dt.filter(t=>(e.sections?.[t.countKey]??0)>0)}function Mt(e){return jt(e).map(e=>e.id)}function Nt(e){return e instanceof RangeError||e instanceof Error&&e.message.includes(`Invalid string length`)}function G(e,t){return Nt(e)?M(`backupStringTooLarge`):e instanceof Error?e.message:t}var Pt=class extends x{exportSections=new Set(W.map(e=>e.id));busy=!1;message=``;error=``;safetyBackupPath=``;pendingImport=null;getTabName(){return M(`backupRestore`)}toggleExportSection(e,t){t?this.exportSections.add(e):this.exportSections.delete(e),this.clearStatus(),this.requestUpdate()}clearStatus(){this.message=``,this.error=``,this.safetyBackupPath=``}async exportBackup(){if(this.exportSections.size===0){this.error=M(`selectAtLeastOneExportSection`),this.requestUpdate();return}this.busy=!0,this.clearStatus(),this.requestUpdate();try{let e=new URLSearchParams({sections:[...this.exportSections].join(`,`)}),t=await fetch(`/api/backup/export?${e.toString()}`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||M(`backupExportFailed`));Ot(`${Et}-settings-${kt()}.json`,n),this.message=M(`backupExported`)}catch(e){this.error=G(e,M(`backupExportFailed`))}finally{this.busy=!1,this.requestUpdate()}}async inspectBackupFile(e){let t=await fetch(`/api/backup/inspect-file`,{method:`POST`,headers:{"content-type":`application/json`},body:e}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||M(`backupInspectFailed`));if(!n)throw Error(M(`backupInspectFailed`));return n}async importBackupFromFile(e){this.busy=!0,this.clearStatus(),this.pendingImport=null,this.requestUpdate();try{let t=await this.inspectBackupFile(e),n=new Set(Mt(t));this.pendingImport={backup:null,importToken:t.importToken,inspect:t,selectedSections:n,mode:`replace`},this.message=t.ignoredConversations?M(`backupInspectedWithIgnoredConversations`):M(`backupInspected`)}catch(e){this.error=G(e,M(`backupImportFailed`))}finally{this.busy=!1,this.requestUpdate()}}togglePendingSection(e,t){this.pendingImport&&(t?this.pendingImport.selectedSections.add(e):this.pendingImport.selectedSections.delete(e),this.message=``,this.error=``,this.requestUpdate())}setRestoreMode(e){this.pendingImport&&(this.pendingImport.mode=e,this.message=``,this.error=``,this.requestUpdate())}cancelPendingImport(){this.pendingImport=null,this.message=``,this.error=``,this.requestUpdate()}async confirmPendingImport(){if(this.pendingImport){if(this.pendingImport.selectedSections.size===0){this.error=M(`selectAtLeastOneRestoreSection`),this.requestUpdate();return}if(await T({description:this.pendingImport.mode===`replace`?M(`backupImportReplaceConfirm`):M(`backupImportMergeConfirm`),confirmLabel:M(`confirmImportSelected`),cancelLabel:M(`cancel`)})){this.busy=!0,this.clearStatus(),this.requestUpdate();try{let e=this.pendingImport.importToken?{importToken:this.pendingImport.importToken,sections:[...this.pendingImport.selectedSections],mode:this.pendingImport.mode}:{backup:this.pendingImport.backup,sections:[...this.pendingImport.selectedSections],mode:this.pendingImport.mode},t=await fetch(`/api/backup/import`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||M(`backupImportFailed`));let r=At(n?.summary);this.safetyBackupPath=n?.safetyBackupPath||``,this.pendingImport=null,this.message=r?`${M(`backupImported`)} ${r}`:M(`backupImported`),window.setTimeout(()=>window.location.reload(),1500)}catch(e){this.error=G(e,M(`backupImportFailed`))}finally{this.busy=!1,this.requestUpdate()}}}}handleFileChange(e){let t=e.target,n=t.files?.[0];t.value=``,n&&this.importBackupFromFile(n)}renderSwitch(e,t,n=!1){return b`
922
+ <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
923
+ <input
924
+ type="checkbox"
925
+ .checked=${e}
926
+ ?disabled=${n}
927
+ @change=${e=>t(e.target.checked)}
928
+ />
929
+ <span aria-hidden="true"></span>
930
+ </label>
931
+ `}renderPendingImport(){if(!this.pendingImport)return null;let{inspect:e,selectedSections:t}=this.pendingImport,n=jt(e);return b`
932
+ <section class="quickforge-settings-section" aria-label=${M(`backupInspectTitle`)}>
933
+ <div class="quickforge-settings-row quickforge-settings-row-top">
934
+ <div class="quickforge-settings-row-main">
935
+ <div class="quickforge-settings-row-title">${M(`backupInspectTitle`)}</div>
936
+ <div class="quickforge-settings-row-description">${M(`backupInspectDescription`)}</div>
937
+ </div>
938
+ </div>
939
+
940
+ <div class="quickforge-settings-nested-list">
941
+ <div class="quickforge-settings-subrow">
942
+ <div class="quickforge-settings-row-main">
943
+ <div class="quickforge-settings-row-title">${M(`backupInspectExportedAt`)}</div>
944
+ </div>
945
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.exportedAt||`-`}</div>
946
+ </div>
947
+ <div class="quickforge-settings-subrow">
948
+ <div class="quickforge-settings-row-main">
949
+ <div class="quickforge-settings-row-title">${M(`backupInspectVersion`)}</div>
950
+ </div>
951
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.version??`-`}</div>
952
+ </div>
953
+ <div class="quickforge-settings-subrow">
954
+ <div class="quickforge-settings-row-main">
955
+ <div class="quickforge-settings-row-title">${M(`backupInspectScope`)}</div>
956
+ </div>
957
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.scope||`-`}</div>
958
+ </div>
959
+ <div class="quickforge-settings-subrow">
960
+ <div class="quickforge-settings-row-main">
961
+ <div class="quickforge-settings-row-title">${M(`backupInspectSecrets`)}</div>
962
+ </div>
963
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.includeSecrets?M(`yes`):M(`no`)}</div>
964
+ </div>
965
+ </div>
966
+
967
+ ${e.warnings?.length?b`
968
+ <div class="quickforge-settings-warning quickforge-settings-warning-attached">
969
+ ${e.warnings.map(e=>b`<div>⚠ ${e}</div>`)}
970
+ </div>
971
+ `:null}
972
+
973
+ ${e.invalidSections&&Object.keys(e.invalidSections).length?b`
974
+ <div class="quickforge-settings-warning quickforge-settings-warning-attached">
975
+ <div>${M(`backupInvalidSections`)}</div>
976
+ ${Object.entries(e.invalidSections).map(([e,t])=>b`<div>${e}: ${t}</div>`)}
977
+ </div>
978
+ `:null}
979
+
980
+ <div class="quickforge-settings-row">
981
+ <div class="quickforge-settings-row-main">
982
+ <div class="quickforge-settings-row-title">${M(`restoreMode`)}</div>
983
+ <div class="quickforge-settings-row-description">${M(`restoreModeDescription`)}</div>
984
+ </div>
985
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
986
+ <div class="quickforge-settings-segmented" role="group" aria-label=${M(`restoreMode`)}>
987
+ <button
988
+ type="button"
989
+ class="quickforge-settings-segmented-option ${this.pendingImport.mode===`replace`?`quickforge-settings-segmented-option-active`:``}"
990
+ ?disabled=${this.busy}
991
+ @click=${()=>this.setRestoreMode(`replace`)}
992
+ >
993
+ ${M(`restoreModeReplace`)}
994
+ </button>
995
+ <button
996
+ type="button"
997
+ class="quickforge-settings-segmented-option ${this.pendingImport.mode===`merge`?`quickforge-settings-segmented-option-active`:``}"
998
+ ?disabled=${this.busy}
999
+ @click=${()=>this.setRestoreMode(`merge`)}
1000
+ >
1001
+ ${M(`restoreModeMerge`)}
1002
+ </button>
1003
+ </div>
1004
+ </div>
1005
+ </div>
1006
+
1007
+ <div class="quickforge-settings-row quickforge-settings-row-top">
1008
+ <div class="quickforge-settings-row-main">
1009
+ <div class="quickforge-settings-row-title">${M(`selectRestoreSections`)}</div>
1010
+ <div class="quickforge-settings-row-description">
1011
+ ${this.pendingImport.mode===`replace`?M(`restoreModeReplaceDescription`):M(`restoreModeMergeDescription`)}
1012
+ </div>
1013
+ </div>
1014
+ </div>
1015
+ <div class="quickforge-settings-nested-list">
1016
+ ${n.map(n=>b`
1017
+ <div class="quickforge-settings-subrow">
1018
+ <div class="quickforge-settings-row-main">
1019
+ <div class="quickforge-settings-row-title">${n.label()} (${e.sections?.[n.countKey]??0})</div>
1020
+ <div class="quickforge-settings-row-description">${n.description()}</div>
1021
+ </div>
1022
+ <div class="quickforge-settings-row-control">
1023
+ ${this.renderSwitch(t.has(n.id),e=>this.togglePendingSection(n.id,e),this.busy)}
1024
+ </div>
1025
+ </div>
1026
+ `)}
1027
+ </div>
1028
+
1029
+ <div class="quickforge-settings-row">
1030
+ <div class="quickforge-settings-row-main">
1031
+ <div class="quickforge-settings-row-title">${M(`backupImportActions`)}</div>
1032
+ <div class="quickforge-settings-row-description">${M(`backupImportActionsDescription`)}</div>
1033
+ </div>
1034
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1035
+ <button
1036
+ class="quickforge-settings-button quickforge-settings-button-primary"
1037
+ type="button"
1038
+ ?disabled=${this.busy||t.size===0}
1039
+ @click=${()=>this.confirmPendingImport()}
1040
+ >
1041
+ ${this.busy?M(`loading`):M(`confirmImportSelected`)}
1042
+ </button>
1043
+ <button
1044
+ class="quickforge-settings-button quickforge-settings-button-secondary"
1045
+ type="button"
1046
+ ?disabled=${this.busy}
1047
+ @click=${()=>this.cancelPendingImport()}
1048
+ >
1049
+ ${M(`cancel`)}
1050
+ </button>
1051
+ </div>
1052
+ </div>
1053
+ </section>
1054
+ `}render(){return b`
1055
+ <div class="quickforge-settings-stack">
1056
+ <section class="quickforge-settings-section" aria-label=${M(`exportData`)}>
1057
+ <div class="quickforge-settings-row quickforge-settings-row-top">
1058
+ <div class="quickforge-settings-row-main">
1059
+ <div class="quickforge-settings-row-title">
1060
+ ${M(`selectExportSections`)}
1061
+ <quickforge-info-tip .label=${M(`exportDataDescription`)}></quickforge-info-tip>
1062
+ </div>
1063
+ <div class="quickforge-settings-row-description">${M(`selectExportSectionsDescription`)}</div>
1064
+ </div>
1065
+ </div>
1066
+
1067
+ <div class="quickforge-settings-nested-list">
1068
+ ${W.map(e=>b`
1069
+ <div class="quickforge-settings-subrow">
1070
+ <div class="quickforge-settings-row-main">
1071
+ <div class="quickforge-settings-row-title">${e.label()}</div>
1072
+ <div class="quickforge-settings-row-description">${e.description()}</div>
1073
+ </div>
1074
+ <div class="quickforge-settings-row-control">
1075
+ ${this.renderSwitch(this.exportSections.has(e.id),t=>this.toggleExportSection(e.id,t),this.busy)}
1076
+ </div>
1077
+ </div>
1078
+ `)}
1079
+ </div>
1080
+
1081
+ <div class="quickforge-settings-note">${M(`conversationBackupExcluded`)}</div>
1082
+
1083
+ <div class="quickforge-settings-row">
1084
+ <div class="quickforge-settings-row-main">
1085
+ <div class="quickforge-settings-row-title">${M(`exportBackup`)}</div>
1086
+ <div class="quickforge-settings-row-description">${M(`exportBackupDescription`)}</div>
1087
+ </div>
1088
+ <div class="quickforge-settings-row-control">
1089
+ <button
1090
+ class="quickforge-settings-button quickforge-settings-button-primary"
1091
+ type="button"
1092
+ ?disabled=${this.busy||this.exportSections.size===0}
1093
+ @click=${()=>this.exportBackup()}
1094
+ >
1095
+ ${this.busy?M(`loading`):M(`exportBackup`)}
1096
+ </button>
1097
+ </div>
1098
+ </div>
1099
+ </section>
1100
+
1101
+ <section class="quickforge-settings-section" aria-label=${M(`importData`)}>
1102
+ <div class="quickforge-settings-row">
1103
+ <div class="quickforge-settings-row-main">
1104
+ <div class="quickforge-settings-row-title">
1105
+ ${M(`importBackup`)}
1106
+ <quickforge-info-tip .label=${M(`importDataDescription`)}></quickforge-info-tip>
1107
+ </div>
1108
+ <div class="quickforge-settings-row-description">${M(`importDataDescription`)}</div>
1109
+ </div>
1110
+ <div class="quickforge-settings-row-control">
1111
+ <label class="quickforge-settings-button quickforge-settings-button-secondary ${this.busy?`pointer-events-none opacity-60`:``}">
1112
+ <input
1113
+ class="hidden"
1114
+ type="file"
1115
+ accept="application/json,.json"
1116
+ ?disabled=${this.busy}
1117
+ @change=${e=>this.handleFileChange(e)}
1118
+ />
1119
+ ${M(`importBackup`)}
1120
+ </label>
1121
+ </div>
1122
+ </div>
1123
+ </section>
1124
+
1125
+ ${this.renderPendingImport()}
1126
+ ${this.message?b`<div class="quickforge-settings-message">${this.message}</div>`:null}
1127
+ ${this.safetyBackupPath?b`<div class="quickforge-settings-note">${M(`backupSafetyBackupPath`)}: <code>${this.safetyBackupPath}</code></div>`:null}
1128
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1129
+ </div>
1130
+ `}},K=`quickforge-backup-settings-tab`;customElements.get(K)||customElements.define(K,Pt);function Ft(){return document.createElement(K)}function It(e){if(!e)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString(A())}function Lt(e){let t={...e};return delete t.archivedAt,t}function q(){if(!(typeof BroadcastChannel>`u`))try{let e=new BroadcastChannel(`quickforge-sync`);e.postMessage({type:`sessions-changed`,sourceTabId:`archived-conversations-settings-tab`,timestamp:Date.now()}),e.close()}catch{}}var Rt=class extends x{sessions=[];projects=[];loading=!0;busySessionId=``;query=``;projectFilter=`all`;message=``;error=``;getTabName(){return M(`archivedConversations`)}async connectedCallback(){super.connectedCallback(),await this.loadData()}get projectNameById(){return new Map(this.projects.map(e=>[e.id,e.name]))}async loadData(){this.loading=!0,this.error=``,this.requestUpdate();try{let[e,t]=await Promise.all([fetch(`/api/storage/sessions-metadata/index/lastModified?direction=desc&limit=1000&offset=0&archived=only`,{cache:`no-store`}),fetch(`/api/project`,{cache:`no-store`}).catch(()=>null)]),n=await e.json().catch(()=>null);if(!e.ok)throw Error(n?.error||M(`requestFailed`));if(this.sessions=Array.isArray(n?.values)?n.values:[],t?.ok){let e=await t.json().catch(()=>null);this.projects=Array.isArray(e?.projects)?e.projects:[]}}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}filteredSessions(){let e=this.query.trim().toLowerCase();return this.sessions.filter(t=>{if(this.projectFilter!==`all`&&(this.projectFilter===`global`&&t.scope===`project`||this.projectFilter!==`global`&&t.projectId!==this.projectFilter))return!1;if(!e)return!0;let n=t.projectId&&this.projectNameById.get(t.projectId)||``;return`${Ie(t.title,t.channelName)} ${n}`.toLowerCase().includes(e)})}groupedSessions(){let e=new Map;for(let t of this.filteredSessions()){let n=t.scope===`project`&&t.projectId?t.projectId:`global`;e.set(n,[...e.get(n)??[],t])}return[...e.entries()]}projectLabel(e){return e===`global`?M(`normalChat`):this.projectNameById.get(e)||M(`unknownProject`)}async restoreSession(e){if(!this.busySessionId){this.busySessionId=e,this.message=``,this.error=``,this.requestUpdate();try{let t=S(),n=await t.sessions.get(e),r=await t.sessions.getMetadata(e);if(!n||!r)throw Error(M(`sessionNotFound`));await t.sessions.save(Lt(n),Lt(r)),this.sessions=this.sessions.filter(t=>t.id!==e),this.message=M(`sessionRestored`),q()}catch(e){this.error=e instanceof Error?e.message:M(`restoreSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}async deleteSession(e){if(!this.busySessionId&&await T({description:M(`deleteArchivedSessionConfirm`,{title:Ie(e.title)}),confirmLabel:M(`confirmDelete`),cancelLabel:M(`cancel`),variant:`destructive`})){this.busySessionId=e.id,this.message=``,this.error=``,this.requestUpdate();try{await S().sessions.delete(e.id),this.sessions=this.sessions.filter(t=>t.id!==e.id),this.message=M(`archivedSessionDeleted`),q()}catch(e){this.error=e instanceof Error?e.message:M(`deleteSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}async deleteAllArchivedSessions(){if(!(this.busySessionId||this.sessions.length===0)&&await T({description:M(`deleteAllArchivedSessionsConfirm`),confirmLabel:M(`confirmDelete`),cancelLabel:M(`cancel`),variant:`destructive`})){this.busySessionId=`__all__`,this.message=``,this.error=``,this.requestUpdate();try{let e=S();await Promise.all(this.sessions.map(t=>e.sessions.delete(t.id))),this.sessions=[],this.message=M(`archivedSessionsDeleted`),q()}catch(e){this.error=e instanceof Error?e.message:M(`deleteSessionFailed`)}finally{this.busySessionId=``,this.requestUpdate()}}}renderSession(e){let t=this.busySessionId===e.id||this.busySessionId===`__all__`;return b`
1131
+ <div class="border-t border-border px-4 py-3 first:border-t-0">
1132
+ <div class="flex flex-wrap items-start justify-between gap-x-3 gap-y-2">
1133
+ <div class="min-w-0 flex-1">
1134
+ <div class="truncate text-sm font-medium text-foreground">${Ie(e.title,e.channelName)}</div>
1135
+ <div class="mt-1 text-xs text-muted-foreground">
1136
+ ${M(`lastModified`)}: ${It(e.lastModified)} · ${M(`archivedAt`)}: ${It(e.archivedAt)}
1137
+ </div>
1138
+ </div>
1139
+ <div class="flex w-full shrink-0 items-center justify-end gap-2 sm:w-auto">
1140
+ <button
1141
+ class="rounded-md border border-input px-2.5 py-1 text-xs hover:bg-muted/60 disabled:opacity-60"
1142
+ type="button"
1143
+ ?disabled=${t}
1144
+ @click=${()=>this.restoreSession(e.id)}
1145
+ >
1146
+ ${M(`restoreSession`)}
1147
+ </button>
1148
+ <button
1149
+ class="rounded-md px-2.5 py-1 text-xs text-destructive hover:bg-destructive/10 disabled:opacity-60"
1150
+ type="button"
1151
+ ?disabled=${t}
1152
+ @click=${()=>this.deleteSession(e)}
1153
+ >
1154
+ ${M(`deletePermanently`)}
1155
+ </button>
1156
+ </div>
1157
+ </div>
1158
+ </div>
1159
+ `}render(){let e=this.groupedSessions();return b`
1160
+ <div class="flex flex-col gap-4">
1161
+ <div class="flex justify-end">
1162
+ <button
1163
+ class="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive hover:bg-destructive/15 disabled:opacity-60"
1164
+ type="button"
1165
+ ?disabled=${!!this.busySessionId||this.sessions.length===0}
1166
+ @click=${()=>this.deleteAllArchivedSessions()}
1167
+ >
1168
+ ${M(`deleteAll`)}
1169
+ </button>
1170
+ </div>
1171
+
1172
+ <section class="rounded-lg border border-border">
1173
+ <div class="grid gap-2 border-b border-border p-3 sm:grid-cols-[minmax(0,1fr)_12rem]">
1174
+ <input
1175
+ class="rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring/30"
1176
+ type="search"
1177
+ .value=${this.query}
1178
+ placeholder=${M(`searchArchivedConversations`)}
1179
+ @input=${e=>{this.query=e.target.value,this.requestUpdate()}}
1180
+ />
1181
+ <select
1182
+ class="rounded-md border border-input bg-background px-3 py-2 text-sm"
1183
+ .value=${this.projectFilter}
1184
+ @change=${e=>{this.projectFilter=e.target.value,this.requestUpdate()}}
1185
+ >
1186
+ <option value="all">${M(`allProjects`)}</option>
1187
+ <option value="global">${M(`normalChat`)}</option>
1188
+ ${this.projects.map(e=>b`<option value=${e.id}>${e.name}</option>`)}
1189
+ </select>
1190
+ </div>
1191
+
1192
+ ${this.loading?b`
1193
+ <div class="px-4 py-8 text-center text-sm text-muted-foreground">${M(`loading`)}</div>
1194
+ `:e.length===0?b`
1195
+ <div class="px-4 py-8 text-center text-sm text-muted-foreground">${M(`noArchivedConversations`)}</div>
1196
+ `:b`
1197
+ <div>
1198
+ ${e.map(([e,t])=>b`
1199
+ <div class="border-b border-border last:border-b-0">
1200
+ <div class="flex items-center justify-between gap-3 bg-muted/20 px-4 py-2 text-sm text-muted-foreground">
1201
+ <div class="truncate">${this.projectLabel(e)}</div>
1202
+ <div class="shrink-0">${M(`conversationCount`,{count:t.length})}</div>
1203
+ </div>
1204
+ ${t.map(e=>this.renderSession(e))}
1205
+ </div>
1206
+ `)}
1207
+ </div>
1208
+ `}
1209
+ </section>
1210
+
1211
+ ${this.message?b`<div class="rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-sm text-emerald-700 dark:text-emerald-300">${this.message}</div>`:null}
1212
+ ${this.error?b`<div class="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">${this.error}</div>`:null}
1213
+ </div>
1214
+ `}},zt=`quickforge-archived-conversations-settings-tab`;customElements.get(zt)||customElements.define(zt,Rt);function Bt(){return document.createElement(zt)}function Vt(){return Array.from({length:16},()=>`ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%^&*`[Math.floor(Math.random()*65)]).join(``)}function Ht(e){if(!e)return M(`lanAccessUnknownDevice`);let t=e.includes(`Edg/`)?`Edge`:e.includes(`Firefox/`)?`Firefox`:e.includes(`Chrome/`)?`Chrome`:e.includes(`Safari/`)?`Safari`:M(`lanAccessBrowser`),n=/Android/i.test(e)?`Android`:/iPhone|iPad|iPod/i.test(e)?`iOS`:/Windows/i.test(e)?`Windows`:/Macintosh|Mac OS X/i.test(e)?`macOS`:/Linux/i.test(e)?`Linux`:``;return n?`${t} · ${n}`:t}function Ut(e){if(!e)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?`-`:new Intl.DateTimeFormat(A(),{dateStyle:`medium`,timeStyle:`short`}).format(t)}var Wt=class extends x{loading=!0;saving=!1;enabled=!1;hasPassword=!1;password=``;passwordVisible=!1;sessionTtlHours=12;activeTokenCount=0;activeDevices=[];lanUrls=[];error=``;message=``;getTabName(){return M(`lanAccess`)}async connectedCallback(){super.connectedCallback(),await this.loadStatus()}async request(e,t){let n=await fetch(e,{...t,cache:`no-store`,headers:{...t?.body?{"content-type":`application/json`}:void 0,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||M(`requestFailed`));return r}applyStatus(e){this.enabled=!!e.enabled,this.hasPassword=!!e.hasPassword,this.sessionTtlHours=Number(e.sessionTtlHours||12),this.activeTokenCount=Number(e.activeTokenCount||0),this.activeDevices=Array.isArray(e.activeDevices)?e.activeDevices:[],this.lanUrls=Array.isArray(e.lanUrls)?e.lanUrls:[]}async loadStatus(){this.loading=!0,this.error=``,this.requestUpdate();try{this.applyStatus(await this.request(`/api/lan-access/status`))}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}updatePassword(e){this.password=e,this.requestUpdate()}togglePasswordVisibility(){this.passwordVisible=!this.passwordVisible,this.requestUpdate()}updateEnabled(e){this.enabled=e,this.requestUpdate()}updateTtl(e){this.sessionTtlHours=Number(e)||12,this.requestUpdate()}async saveSettings(){if(!this.saving){if(this.enabled&&!this.hasPassword&&!this.password.trim()){this.error=M(`lanAccessPasswordRequired`),this.requestUpdate();return}if(this.password.trim()&&this.password.trim().length<8){this.error=M(`lanAccessPasswordTooShort`),this.requestUpdate();return}if(!(this.enabled&&!await T({description:M(`lanAccessEnableConfirm`),confirmLabel:M(`enabled`),cancelLabel:M(`cancel`)}))){this.saving=!0,this.error=``,this.message=``,this.requestUpdate();try{let e=await this.request(`/api/lan-access/settings`,{method:`PUT`,body:JSON.stringify({enabled:this.enabled,password:this.password.trim()||void 0,sessionTtlHours:this.sessionTtlHours})});this.applyStatus(e),this.password=``,this.passwordVisible=!1,this.message=M(`lanAccessSaved`)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}}async revokeDevice(e){if(await T({description:M(`lanAccessRevokeDeviceConfirm`,{device:Ht(e.userAgent)}),confirmLabel:M(`lanAccessRevokeDevice`),cancelLabel:M(`cancel`),variant:`destructive`})){this.saving=!0,this.error=``,this.message=``,this.requestUpdate();try{let t=await this.request(`/api/lan-access/revoke`,{method:`POST`,body:JSON.stringify({id:e.id})});this.applyStatus(t),this.message=M(`lanAccessDeviceRevoked`)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}async revokeAll(){if(await T({description:M(`lanAccessRevokeAllConfirm`),confirmLabel:M(`lanAccessRevokeAll`),cancelLabel:M(`cancel`),variant:`destructive`})){this.saving=!0,this.error=``,this.message=``,this.requestUpdate();try{let e=await this.request(`/api/lan-access/revoke-all`,{method:`POST`});this.applyStatus(e),this.message=M(`lanAccessRevoked`)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.saving=!1,this.requestUpdate()}}}renderSwitch(e,t,n=!1){return b`
1215
+ <label class="quickforge-settings-switch" aria-disabled=${n?`true`:`false`}>
1216
+ <input
1217
+ type="checkbox"
1218
+ .checked=${e}
1219
+ ?disabled=${n}
1220
+ @change=${e=>t(e.target.checked)}
1221
+ />
1222
+ <span aria-hidden="true"></span>
1223
+ </label>
1224
+ `}render(){return this.loading?b`<div class="text-sm text-muted-foreground">${M(`loading`)}</div>`:b`
1225
+ <div class="quickforge-settings-stack">
1226
+ <div class="quickforge-settings-warning">${M(`lanAccessRiskWarning`)}</div>
1227
+
1228
+ <section class="quickforge-settings-section" aria-label=${M(`lanAccessStatus`)}>
1229
+ <div class="quickforge-settings-row">
1230
+ <div class="quickforge-settings-row-main">
1231
+ <div class="quickforge-settings-row-title">${M(`lanAccessEnabled`)}</div>
1232
+ <div class="quickforge-settings-row-description">${M(`lanAccessEnabledDescription`)}</div>
1233
+ </div>
1234
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.enabled?M(`enabled`):M(`disabled`)}</div>
1235
+ </div>
1236
+ <div class="quickforge-settings-row">
1237
+ <div class="quickforge-settings-row-main">
1238
+ <div class="quickforge-settings-row-title">${M(`lanAccessPassword`)}</div>
1239
+ <div class="quickforge-settings-row-description">${M(`lanAccessPasswordStatusDescription`)}</div>
1240
+ </div>
1241
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.hasPassword?M(`configured`):M(`notConfigured`)}</div>
1242
+ </div>
1243
+ <div class="quickforge-settings-row quickforge-settings-row-top">
1244
+ <div class="quickforge-settings-row-main">
1245
+ <div class="quickforge-settings-row-title">${M(`lanAccessActiveDevices`,{count:this.activeTokenCount})}</div>
1246
+ <div class="quickforge-settings-row-description">${M(`lanAccessActiveDevicesDescription`)}</div>
1247
+ </div>
1248
+ </div>
1249
+ <div class="quickforge-settings-nested-list quickforge-lan-device-list">
1250
+ ${this.activeDevices.length?this.activeDevices.map(e=>b`
1251
+ <div class="quickforge-settings-subrow">
1252
+ <div class="quickforge-settings-list-item-main">
1253
+ <div class="quickforge-settings-row-title">${Ht(e.userAgent)}</div>
1254
+ <div class="quickforge-settings-row-description quickforge-lan-device-meta">
1255
+ <span>${e.address||M(`lanAccessUnknownAddress`)}</span>
1256
+ <span>${M(`lanAccessSignedInAt`,{time:Ut(e.issuedAt)})}</span>
1257
+ <span>${M(`lanAccessExpiresAt`,{time:Ut(e.expiresAt)})}</span>
1258
+ </div>
1259
+ </div>
1260
+ <div class="quickforge-settings-list-item-actions">
1261
+ <button
1262
+ class="quickforge-settings-button quickforge-settings-button-danger quickforge-settings-button-compact"
1263
+ type="button"
1264
+ ?disabled=${this.saving}
1265
+ @click=${()=>this.revokeDevice(e)}
1266
+ >
1267
+ ${M(`lanAccessRevokeDevice`)}
1268
+ </button>
1269
+ </div>
1270
+ </div>
1271
+ `):b`<div class="quickforge-settings-empty-row">${M(`lanAccessNoActiveDevices`)}</div>`}
1272
+ </div>
1273
+ <div class="quickforge-settings-row">
1274
+ <div class="quickforge-settings-row-main">
1275
+ <div class="quickforge-settings-row-title">${M(`lanAccessUrls`)}</div>
1276
+ <div class="quickforge-settings-row-description">${M(`lanAccessUrlsDescription`)}</div>
1277
+ </div>
1278
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1279
+ ${this.lanUrls.length?this.lanUrls.map(e=>b`<div>${e}</div>`):`-`}
1280
+ </div>
1281
+ </div>
1282
+ </section>
1283
+
1284
+ <section class="quickforge-settings-section" aria-label=${M(`lanAccess`)}>
1285
+ <div class="quickforge-settings-row">
1286
+ <div class="quickforge-settings-row-main">
1287
+ <div class="quickforge-settings-row-title">${M(`lanAccessAllowFull`)}</div>
1288
+ <div class="quickforge-settings-row-description">${M(`lanAccessAllowFullDescription`)}</div>
1289
+ </div>
1290
+ <div class="quickforge-settings-row-control">
1291
+ ${this.renderSwitch(this.enabled,e=>this.updateEnabled(e),this.saving)}
1292
+ </div>
1293
+ </div>
1294
+
1295
+ <div class="quickforge-settings-row">
1296
+ <div class="quickforge-settings-row-main">
1297
+ <div class="quickforge-settings-row-title">${M(`lanAccessPassword`)}</div>
1298
+ <div class="quickforge-settings-row-description">${this.hasPassword?M(`lanAccessPasswordPlaceholderConfigured`):M(`lanAccessPasswordPlaceholder`)}</div>
1299
+ </div>
1300
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-lan-password-control">
1301
+ <div class="quickforge-lan-password-input-wrap">
1302
+ <input
1303
+ class="quickforge-settings-input"
1304
+ type=${this.passwordVisible?`text`:`password`}
1305
+ .value=${this.password}
1306
+ @input=${e=>this.updatePassword(e.target.value)}
1307
+ placeholder=${this.hasPassword?M(`lanAccessPasswordPlaceholderConfigured`):M(`lanAccessPasswordPlaceholder`)}
1308
+ />
1309
+ <button
1310
+ class="quickforge-lan-password-toggle"
1311
+ type="button"
1312
+ aria-label=${this.passwordVisible?M(`hidePassword`):M(`showPassword`)}
1313
+ title=${this.passwordVisible?M(`hidePassword`):M(`showPassword`)}
1314
+ aria-pressed=${this.passwordVisible?`true`:`false`}
1315
+ @click=${()=>this.togglePasswordVisibility()}
1316
+ >
1317
+ ${this.passwordVisible?b`
1318
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
1319
+ <path d="m3 3 18 18" stroke-linecap="round"></path>
1320
+ <path d="M10.6 10.7a2 2 0 0 0 2.7 2.7"></path>
1321
+ <path d="M9.9 4.3A10.9 10.9 0 0 1 12 4c5.5 0 9 5.3 9 8a7.3 7.3 0 0 1-1.5 3.2"></path>
1322
+ <path d="M6.6 6.6C4.3 8.1 3 10.4 3 12c0 2.7 3.5 8 9 8 1.5 0 2.8-.4 4-1"></path>
1323
+ </svg>
1324
+ `:b`
1325
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
1326
+ <path d="M3 12c0-2.7 3.5-8 9-8s9 5.3 9 8-3.5 8-9 8-9-5.3-9-8Z"></path>
1327
+ <circle cx="12" cy="12" r="2.5"></circle>
1328
+ </svg>
1329
+ `}
1330
+ </button>
1331
+ </div>
1332
+ <button
1333
+ class="quickforge-settings-button quickforge-settings-button-secondary"
1334
+ type="button"
1335
+ @click=${()=>this.updatePassword(Vt())}
1336
+ >
1337
+ ${M(`generatePassword`)}
1338
+ </button>
1339
+ </div>
1340
+ </div>
1341
+
1342
+ <div class="quickforge-settings-row">
1343
+ <div class="quickforge-settings-row-main">
1344
+ <div class="quickforge-settings-row-title">${M(`lanAccessSessionTtl`)}</div>
1345
+ <div class="quickforge-settings-row-description">${M(`lanAccessSessionTtlDescription`)}</div>
1346
+ </div>
1347
+ <div class="quickforge-settings-row-control">
1348
+ <select class="quickforge-settings-select" .value=${String(this.sessionTtlHours)} @change=${e=>this.updateTtl(e.target.value)}>
1349
+ <option value="1">1 ${M(`hour`)}</option>
1350
+ <option value="12">12 ${M(`hours`)}</option>
1351
+ <option value="24">24 ${M(`hours`)}</option>
1352
+ <option value="168">7 ${M(`days`)}</option>
1353
+ </select>
1354
+ </div>
1355
+ </div>
1356
+
1357
+ <div class="quickforge-settings-row">
1358
+ <div class="quickforge-settings-row-main">
1359
+ <div class="quickforge-settings-row-title">${M(`lanAccessActions`)}</div>
1360
+ <div class="quickforge-settings-row-description">${M(`lanAccessActionsDescription`)}</div>
1361
+ </div>
1362
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1363
+ <button class="quickforge-settings-button quickforge-settings-button-primary" type="button" ?disabled=${this.saving} @click=${()=>this.saveSettings()}>
1364
+ ${this.saving?M(`saving`):M(`save`)}
1365
+ </button>
1366
+ <button class="quickforge-settings-button quickforge-settings-button-danger" type="button" ?disabled=${this.saving} @click=${()=>this.revokeAll()}>
1367
+ ${M(`lanAccessRevokeAll`)}
1368
+ </button>
1369
+ </div>
1370
+ </div>
1371
+ </section>
1372
+
1373
+ ${this.message?b`<div class="quickforge-settings-message">${this.message}</div>`:null}
1374
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1375
+ </div>
1376
+ `}},Gt=`quickforge-lan-access-settings-tab`;customElements.get(Gt)||customElements.define(Gt,Wt);function Kt(){return document.createElement(Gt)}var qt=18e4,Jt=3e4,Yt=1e3,Xt=`https://github.com/shawnstack/quickforge/releases/latest`;function Zt(){if(typeof document>`u`)return!1;let e=window;return document.body.classList.contains(`quickforge-desktop-app`)||e.__quickforgeDesktopApp===!0}function Qt(e){return new Promise(t=>window.setTimeout(t,e))}function $t(e,t){return e?.code===`system_restart_requires_auth`?M(`systemRestartRequiresAuth`):e?.code===`system_update_requires_auth`?M(`systemUpdateRequiresAuth`):e?.error||t}var en=[{value:`startup`,label:()=>M(`frequencyStartup`)},{value:`daily`,label:()=>M(`frequencyDaily`)},{value:`weekly`,label:()=>M(`frequencyWeekly`)},{value:`off`,label:()=>M(`frequencyOff`)}],tn=class extends x{about;updateInfo;loading=!0;checking=!1;updating=!1;restarting=!1;message=``;error=``;frequency=Le.frequency;lastCheckAt=null;serviceStatus;getTabName(){return M(`about`)}async connectedCallback(){super.connectedCallback(),await this.loadAbout()}async loadAbout(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/about`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`requestFailed`));this.about=t;try{let[e,t]=await Promise.all([Ve(S()),this.loadServiceStatus()]);this.frequency=e.frequency,this.lastCheckAt=e.lastCheckAt,this.serviceStatus=t}catch{}}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async loadServiceStatus(){let e=await fetch(`/api/health`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`requestFailed`));return t}async checkUpdate(){if(!(this.checking||this.updating)){if(Zt()){window.open(Xt,`_blank`,`noopener,noreferrer`),this.message=M(`desktopUpdateHint`),this.error=``,this.requestUpdate();return}this.checking=!0,this.message=``,this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/update/check`,{cache:`no-store`}),t=await e.json().catch(()=>null);if(!e.ok)throw Error(t?.error||M(`updateCheckFailed`));this.updateInfo=t,this.about=t,this.updateInfo.updateAvailable?this.message=M(`updateAvailableMessage`,{current:this.updateInfo.currentVersion,latest:this.updateInfo.latestVersion}):this.updateInfo.localVersionIsNewer?this.message=M(`localVersionNewerMessage`,{current:this.updateInfo.currentVersion,latest:this.updateInfo.latestVersion}):this.message=M(`alreadyLatestVersion`,{version:this.updateInfo.currentVersion})}catch(e){this.error=e instanceof Error?e.message:M(`updateCheckFailed`)}finally{this.checking=!1,this.requestUpdate()}}}async pollUntilUpdated(e){let t=Date.now();for(;Date.now()-t<qt;){await Qt(Yt);try{let t=await fetch(`/api/health?updatePoll=${Date.now()}`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(t.ok&&n?.ok&&n.bootId&&n.bootId!==e){this.message=M(`updateRestarted`),this.requestUpdate(),window.setTimeout(()=>window.location.reload(),300);return}}catch{}}throw Error(M(`updateRestartTimeout`))}async pollUntilRestarted(e){let t=Date.now();for(;Date.now()-t<Jt;){await Qt(Yt);try{let t=await fetch(`/api/health?restartPoll=${Date.now()}`,{cache:`no-store`}),n=await t.json().catch(()=>null);if(t.ok&&n?.ok&&n.bootId&&n.bootId!==e){this.message=M(`backendRestarted`),this.requestUpdate(),window.setTimeout(()=>window.location.reload(),300);return}}catch{}}throw Error(M(`backendRestartTimeout`))}async restartService(){if(!this.serviceStatus||this.restarting||!await T({description:M(`restartBackendConfirm`),confirmLabel:M(`restartBackendService`),cancelLabel:M(`cancel`)}))return;this.restarting=!0,this.message=M(`backendRestarting`),this.error=``,this.requestUpdate();let e=this.serviceStatus.bootId;try{let t=await fetch(`/api/system/restart`,{method:`POST`,headers:{"x-quickforge-action":`restart`}}),n=await t.json().catch(()=>null);if(!t.ok)throw Error($t(n,M(`backendRestartFailed`)));await this.pollUntilRestarted(e)}catch(e){this.error=e instanceof Error?e.message:M(`backendRestartFailed`),this.message=``,this.restarting=!1,this.requestUpdate()}}async updateQuickForge(){if(Zt()){window.open(Xt,`_blank`,`noopener,noreferrer`),this.message=M(`desktopUpdateHint`),this.error=``,this.requestUpdate();return}if(!(!this.updateInfo?.updateAvailable||this.updating)&&await T({description:M(`updateConfirm`,{command:this.updateInfo.installCommand}),confirmLabel:M(`updateRuntimeNow`),cancelLabel:M(`cancel`)})){this.updating=!0,this.message=M(`updatingQuickForge`),this.error=``,this.requestUpdate();try{let e=await fetch(`/api/system/update`,{method:`POST`,headers:{"x-quickforge-action":`update`}}),t=await e.json().catch(()=>null);if(!e.ok)throw Error($t(t,M(`updateFailed`)));if(this.updateInfo=t,t?.updateStarted){let e=t.logFile?` ${M(`updateLogFile`,{path:t.logFile})}`:``;this.message=`${M(`updateStarted`)}${e}`,this.requestUpdate(),await this.pollUntilUpdated(t.bootId)}else this.message=t?.updated?M(`updateCompleted`):M(`alreadyLatestVersion`,{version:t?.currentVersion||this.about?.version||`-`}),this.updating=!1}catch(e){this.error=e instanceof Error?e.message:M(`updateFailed`),this.message=``,this.updating=!1}finally{this.requestUpdate()}}}async selectFrequency(e){if(this.frequency!==e){this.frequency=e,this.requestUpdate();try{let t=S();await we(t,{...await Ve(t),frequency:e}),this.error=``}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.requestUpdate()}}}renderFrequencyOption(e){let t=this.frequency===e.value;return b`
1377
+ <button
1378
+ type="button"
1379
+ class="quickforge-settings-segmented-option ${t?`quickforge-settings-segmented-option-active`:``}"
1380
+ aria-pressed=${t?`true`:`false`}
1381
+ @click=${()=>this.selectFrequency(e.value)}
1382
+ >
1383
+ ${e.label()}
1384
+ </button>
1385
+ `}infoRows(){let e=this.about;return e?b`
1386
+ <div class="quickforge-settings-row">
1387
+ <div class="quickforge-settings-row-main">
1388
+ <div class="quickforge-settings-row-title">${M(`packageName`)}</div>
1389
+ <div class="quickforge-settings-row-description">${M(`packageNameDescription`)}</div>
1390
+ </div>
1391
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.name}</div>
1392
+ </div>
1393
+ <div class="quickforge-settings-row">
1394
+ <div class="quickforge-settings-row-main">
1395
+ <div class="quickforge-settings-row-title">${M(`currentVersion`)}</div>
1396
+ <div class="quickforge-settings-row-description">${M(`currentVersionDescription`)}</div>
1397
+ </div>
1398
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${e.version}</div>
1399
+ </div>
1400
+ <div class="quickforge-settings-row">
1401
+ <div class="quickforge-settings-row-main">
1402
+ <div class="quickforge-settings-row-title">${M(`github`)}</div>
1403
+ <div class="quickforge-settings-row-description">${M(`githubDescription`)}</div>
1404
+ </div>
1405
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1406
+ <a class="quickforge-settings-link" href=${e.repositoryUrl} target="_blank" rel="noreferrer">
1407
+ ${e.repositoryUrl}
1408
+ </a>
1409
+ </div>
1410
+ </div>
1411
+ `:null}updateStatus(){return this.updateInfo?b`
1412
+ <div class="quickforge-settings-row">
1413
+ <div class="quickforge-settings-row-main">
1414
+ <div class="quickforge-settings-row-title">${M(`latestVersion`)}</div>
1415
+ <div class="quickforge-settings-row-description">${M(`latestVersionDescription`)}</div>
1416
+ </div>
1417
+ <div class="quickforge-settings-row-control quickforge-settings-readonly-value">${this.updateInfo.latestVersion}</div>
1418
+ </div>
1419
+ ${this.updateInfo.logFile?b`
1420
+ <div class="quickforge-settings-row">
1421
+ <div class="quickforge-settings-row-main">
1422
+ <div class="quickforge-settings-row-title">${M(`updateLog`)}</div>
1423
+ <div class="quickforge-settings-row-description">${M(`updateLogDescription`)}</div>
1424
+ </div>
1425
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">
1426
+ <code>${this.updateInfo.logFile}</code>
1427
+ </div>
1428
+ </div>
1429
+ `:null}
1430
+ `:null}restartSection(){let e=this.serviceStatus?.restartUnsupportedReason||M(`backendRestartUnsupported`),t=this.restarting||!this.serviceStatus?.restartSupported;return b`
1431
+ <section class="quickforge-settings-section" aria-label=${M(`restartBackendService`)}>
1432
+ <div class="quickforge-settings-row">
1433
+ <div class="quickforge-settings-row-main">
1434
+ <div class="quickforge-settings-row-title">
1435
+ ${M(`restartBackendService`)}
1436
+ <quickforge-info-tip .label=${M(`restartBackendServiceDescription`)}></quickforge-info-tip>
1437
+ </div>
1438
+ <div class="quickforge-settings-row-description">
1439
+ ${this.serviceStatus?.restartSupported?M(`restartBackendServiceDescription`):e}
1440
+ </div>
1441
+ </div>
1442
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1443
+ <button
1444
+ class="quickforge-settings-button quickforge-settings-button-danger"
1445
+ type="button"
1446
+ ?disabled=${t}
1447
+ @click=${()=>this.restartService()}
1448
+ >
1449
+ ${this.restarting?M(`backendRestarting`):M(`restartBackendService`)}
1450
+ </button>
1451
+ </div>
1452
+ </div>
1453
+ </section>
1454
+ `}render(){if(this.loading)return b`<div class="text-sm text-muted-foreground">${M(`loading`)}</div>`;let e=this.checking||this.updating||!this.updateInfo?.updateAvailable,t=Zt();return b`
1455
+ <div class="quickforge-settings-stack">
1456
+ <section class="quickforge-settings-section" aria-label=${M(`projectInfo`)}>
1457
+ ${this.infoRows()}
1458
+ </section>
1459
+
1460
+ <section class="quickforge-settings-section" aria-label=${M(t?`desktopUpdates`:`runtimeUpdates`)}>
1461
+ ${t?null:b`
1462
+ <div class="quickforge-settings-row">
1463
+ <div class="quickforge-settings-row-main">
1464
+ <div class="quickforge-settings-row-title">
1465
+ ${M(`updateFrequencySection`)}
1466
+ <quickforge-info-tip .label=${M(`updateFrequencyDescription`)}></quickforge-info-tip>
1467
+ </div>
1468
+ <div class="quickforge-settings-row-description">
1469
+ ${this.lastCheckAt?M(`lastCheckedAt`,{time:new Date(this.lastCheckAt).toLocaleString(A())}):M(`lastCheckedNever`)}
1470
+ </div>
1471
+ </div>
1472
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1473
+ <div class="quickforge-settings-segmented quickforge-settings-segmented-wrap" role="group" aria-label=${M(`updateFrequencySection`)}>
1474
+ ${en.map(e=>this.renderFrequencyOption(e))}
1475
+ </div>
1476
+ </div>
1477
+ </div>
1478
+
1479
+ ${this.updateStatus()}
1480
+ `}
1481
+
1482
+ <div class="quickforge-settings-row">
1483
+ <div class="quickforge-settings-row-main">
1484
+ <div class="quickforge-settings-row-title">
1485
+ ${M(t?`desktopUpdates`:`runtimeUpdates`)}
1486
+ <quickforge-info-tip .label=${M(t?`desktopUpdatesDescription`:`runtimeUpdatesDescription`)}></quickforge-info-tip>
1487
+ </div>
1488
+ <div class="quickforge-settings-row-description">
1489
+ ${M(t?`desktopUpdatesDescription`:`runtimeUpdatesDescription`)}
1490
+ </div>
1491
+ </div>
1492
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1493
+ <button
1494
+ class="quickforge-settings-button quickforge-settings-button-secondary"
1495
+ type="button"
1496
+ ?disabled=${this.checking||this.updating}
1497
+ @click=${()=>this.checkUpdate()}
1498
+ >
1499
+ ${this.checking?M(`checkingUpdate`):M(t?`openDesktopReleases`:`checkRuntimeUpdate`)}
1500
+ </button>
1501
+ ${t?null:b`
1502
+ <button
1503
+ class="quickforge-settings-button quickforge-settings-button-primary"
1504
+ type="button"
1505
+ ?disabled=${e}
1506
+ @click=${()=>this.updateQuickForge()}
1507
+ >
1508
+ ${this.updating?M(`updatingQuickForge`):M(`updateRuntimeNow`)}
1509
+ </button>
1510
+ `}
1511
+ </div>
1512
+ </div>
1513
+ </section>
1514
+
1515
+ ${this.restartSection()}
1516
+
1517
+ ${this.message?b`<div class="quickforge-settings-message">${this.message}</div>`:null}
1518
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1519
+ </div>
1520
+ `}},J=`quickforge-about-settings-tab`;customElements.get(J)||customElements.define(J,tn);function nn(){return document.createElement(J)}var rn=class extends x{loading=!0;saving=!1;saved=!1;error=``;message=``;projects=[];project;commandDir=``;commands=[];loadingCommands=!1;saveTimer=null;lastSavedCommandDir=``;pendingSaveAfterCurrent=!1;getTabName(){return M(`projectCommands`)}async connectedCallback(){super.connectedCallback(),await this.loadProjects()}async request(e,t){let n=await fetch(e,{...t,cache:`no-store`,headers:{...t?.body?{"content-type":`application/json`}:void 0,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||M(`requestFailed`));return r}async loadProjects(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=await this.request(`/api/project`);this.projects=e?.projects??(e?.project?[e.project]:[]),this.project=e?.project??this.projects[0],this.commandDir=typeof this.project?.commandDir==`string`?this.project.commandDir:``,this.lastSavedCommandDir=this.commandDir,await this.loadCommands()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}selectProject(e){this.clearSaveTimer();let t=e.target,n=this.projects.find(e=>e.id===t.value);n&&(this.project=n,this.commandDir=typeof n.commandDir==`string`?n.commandDir:``,this.lastSavedCommandDir=this.commandDir,this.saved=!1,this.message=``,this.error=``,this.commands=[],this.requestUpdate(),this.loadCommands())}async loadCommands(){if(this.project?.id){this.loadingCommands=!0,this.requestUpdate();try{let e=await this.request(`/api/project/commands?projectId=${encodeURIComponent(this.project.id)}`);this.commands=e?.commands??[]}catch{this.commands=[]}finally{this.loadingCommands=!1,this.requestUpdate()}}}updateCommandDir(e){this.commandDir=e,this.saved=!1,this.message=``,this.scheduleSave(),this.requestUpdate()}clearSaveTimer(){this.saveTimer!==null&&(window.clearTimeout(this.saveTimer),this.saveTimer=null)}scheduleSave(){this.clearSaveTimer(),this.saveTimer=window.setTimeout(()=>{this.saveTimer=null,this.save()},800)}disconnectedCallback(){this.clearSaveTimer(),super.disconnectedCallback()}async save(){if(!this.project)return;if(this.saving){this.pendingSaveAfterCurrent=!0;return}let e=this.project.id,t=this.commandDir;if(t!==this.lastSavedCommandDir){this.saving=!0,this.saved=!1,this.error=``,this.message=``,this.requestUpdate();try{let n=await this.request(`/api/project/${encodeURIComponent(e)}/command-dir`,{method:`PUT`,body:JSON.stringify({commandDir:t})});if(this.project?.id===e){if(this.project=n?.project??this.project,this.project){let e=this.projects.findIndex(e=>e.id===this.project.id);e>=0&&(this.projects[e]=this.project)}this.commandDir=typeof this.project?.commandDir==`string`?this.project.commandDir:t,this.lastSavedCommandDir=this.commandDir,this.saved=!0,await this.loadCommands()}}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.saving=!1,this.pendingSaveAfterCurrent&&(this.pendingSaveAfterCurrent=!1,this.scheduleSave()),this.requestUpdate()}}}async openCommandDir(){if(this.project){this.error=``,this.message=``,this.requestUpdate();try{await this.request(`/api/project/open-path`,{method:`POST`,body:JSON.stringify({path:`.ai/commands`,projectId:this.project.id})})}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`),this.requestUpdate()}}}async createCommand(){if(!this.project)return;let e=window.prompt(M(`newCommandPrompt`));if(e?.trim()){this.error=``,this.message=``,this.requestUpdate();try{let t=await this.request(`/api/project/command`,{method:`POST`,body:JSON.stringify({name:e.trim(),projectId:this.project.id})});t.ok?(this.message=M(`commandCreated`,{name:t.name??e.trim()}),await this.loadCommands()):t.reason===`exists`?this.error=M(`commandAlreadyExists`,{name:t.name??e.trim()}):this.error=M(`invalidCommandName`)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.requestUpdate()}}}render(){return this.loading?b`<div class="quickforge-settings-note">${M(`loading`)}</div>`:this.projects.length===0?b`
1521
+ <div class="quickforge-settings-stack">
1522
+ <div class="quickforge-settings-note">${M(`selectProjectForCommands`)}</div>
1523
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1524
+ </div>
1525
+ `:b`
1526
+ <div class="quickforge-settings-stack">
1527
+ <section class="quickforge-settings-section" aria-label=${M(`projectCommands`)}>
1528
+ <div class="quickforge-settings-row">
1529
+ <div class="quickforge-settings-row-main">
1530
+ <div class="quickforge-settings-row-title">${M(`project`)}</div>
1531
+ <div class="quickforge-settings-row-description">
1532
+ ${this.project?.path?this.project.path:M(`projectCommandsProjectDescription`)}
1533
+ </div>
1534
+ </div>
1535
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1536
+ <select
1537
+ class="quickforge-settings-select"
1538
+ .value=${this.project?.id??``}
1539
+ @change=${e=>this.selectProject(e)}
1540
+ >
1541
+ ${this.projects.map(e=>b`<option value=${e.id} ?selected=${e.id===this.project?.id}>${e.name}</option>`)}
1542
+ </select>
1543
+ </div>
1544
+ </div>
1545
+
1546
+ <div class="quickforge-settings-row quickforge-settings-row-align-start">
1547
+ <div class="quickforge-settings-row-main">
1548
+ <div class="quickforge-settings-row-title">
1549
+ ${M(`commandDirectories`)}
1550
+ <quickforge-info-tip .label=${M(`commandDirectoryHelp`)}></quickforge-info-tip>
1551
+ </div>
1552
+ <div class="quickforge-settings-row-description">${M(`commandDirectoriesDescription`)}</div>
1553
+ </div>
1554
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1555
+ <textarea
1556
+ class="quickforge-settings-textarea quickforge-settings-mono"
1557
+ .value=${this.commandDir}
1558
+ placeholder=${M(`commandDirectoryPlaceholder`)}
1559
+ @input=${e=>this.updateCommandDir(e.target.value)}
1560
+ ></textarea>
1561
+ </div>
1562
+ </div>
1563
+
1564
+ <div class="quickforge-settings-row quickforge-settings-row-align-start">
1565
+ <div class="quickforge-settings-row-main">
1566
+ <div class="quickforge-settings-row-title">${M(`commandDirectoryExamples`)}</div>
1567
+ <div class="quickforge-settings-row-description">${M(`commandDirectoryExamplesDescription`)}</div>
1568
+ </div>
1569
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-code-list">
1570
+ <code>.ai/commands</code>
1571
+ <code>.claude/commands</code>
1572
+ <code>.opencode/commands</code>
1573
+ <code>D:\shared\ai-commands</code>
1574
+ </div>
1575
+ </div>
1576
+ </section>
1577
+
1578
+ <section class="quickforge-settings-section" aria-label=${M(`loadedCommands`,{count:this.commands.length})}>
1579
+ <div class="quickforge-settings-row">
1580
+ <div class="quickforge-settings-row-main">
1581
+ <div class="quickforge-settings-row-title">${M(`loadedCommands`,{count:this.commands.length})}</div>
1582
+ <div class="quickforge-settings-row-description">${M(`loadedCommandsDescription`)}</div>
1583
+ </div>
1584
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1585
+ <button
1586
+ class="quickforge-settings-button quickforge-settings-button-secondary"
1587
+ type="button"
1588
+ ?disabled=${this.loadingCommands}
1589
+ @click=${()=>this.openCommandDir()}
1590
+ >
1591
+ ${M(`openCommandDir`)}
1592
+ </button>
1593
+ <button
1594
+ class="quickforge-settings-button quickforge-settings-button-primary"
1595
+ type="button"
1596
+ ?disabled=${this.loadingCommands}
1597
+ @click=${()=>this.createCommand()}
1598
+ >
1599
+ ${M(`createCommand`)}
1600
+ </button>
1601
+ </div>
1602
+ </div>
1603
+
1604
+ <div class="quickforge-settings-nested-list">
1605
+ ${this.loadingCommands?b`<div class="quickforge-settings-empty-row">${M(`loading`)}</div>`:this.commands.length===0?b`<div class="quickforge-settings-empty-row">${M(`noCommandsLoaded`)}</div>`:this.commands.map(e=>{let t=e.argumentHint?` ${e.argumentHint}`:``;return b`
1606
+ <div class="quickforge-settings-subrow">
1607
+ <div class="quickforge-settings-row-main">
1608
+ <div class="quickforge-settings-row-title">
1609
+ <code class="quickforge-settings-command-name">/${e.name}${t}</code>
1610
+ </div>
1611
+ <div class="quickforge-settings-row-description">
1612
+ ${e.description||M(`noDescription`)}
1613
+ </div>
1614
+ </div>
1615
+ ${e.relativePath?b`<div class="quickforge-settings-row-control quickforge-settings-row-control-wide quickforge-settings-readonly-value">${e.relativePath}</div>`:null}
1616
+ </div>
1617
+ `})}
1618
+ </div>
1619
+ </section>
1620
+
1621
+ ${this.saving?b`<div class="quickforge-settings-note">${M(`saving`)}</div>`:null}
1622
+ ${this.saved?b`<div class="quickforge-settings-message">${M(`projectCommandsSaved`)}</div>`:null}
1623
+ ${this.message?b`<div class="quickforge-settings-message">${this.message}</div>`:null}
1624
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1625
+ </div>
1626
+ `}},an=`quickforge-project-commands-settings-tab`;customElements.get(an)||customElements.define(an,rn);function on(){return document.createElement(an)}var Y={"x-quickforge-action":`channel-action`};function sn(e){switch(e){case`running`:return`quickforge-settings-badge-success`;case`waiting_scan`:case`starting`:case`stopping`:return`quickforge-settings-badge-warning`;case`error`:return`quickforge-settings-badge-danger`;default:return`quickforge-settings-badge-muted`}}function cn(e){switch(e){case`starting`:return M(`channelStatusStarting`);case`waiting_scan`:return M(`channelStatusWaitingScan`);case`running`:return M(`channelStatusRunning`);case`stopping`:return M(`channelStatusStopping`);case`error`:return M(`channelStatusError`);default:return M(`channelStatusStopped`)}}var ln=class extends x{loading=!0;channels=[];workspaces=[];selectedWorkspaceIdByChannel={};error=``;message=``;eventSource;busyChannelId=``;openingLogsChannelId=``;getTabName(){return M(`channels`)}async connectedCallback(){super.connectedCallback(),await Promise.all([this.loadChannels(),this.loadWorkspaces()]),this.connectEvents()}disconnectedCallback(){this.eventSource?.close(),this.eventSource=void 0,super.disconnectedCallback()}async request(e,t){let n=await fetch(e,{...t,cache:`no-store`,headers:{...t?.headers||{}}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||M(`requestFailed`));return r}async loadChannels(){this.loading=!0,this.error=``,this.requestUpdate();try{let e=await this.request(`/api/channels`);this.channels=Array.isArray(e.channels)?e.channels:[],this.ensureWorkspaceSelections()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.loading=!1,this.requestUpdate()}}async loadWorkspaces(){try{let e=await this.request(`/api/project`),t=e.defaultWorkspaceRoot||``,n=t?[{id:`default`,name:M(`channelDefaultWorkspace`),path:t,kind:`default`}]:[],r=(Array.isArray(e.projects)?e.projects:[]).map(e=>({id:e.id,name:e.name,path:e.path,kind:`project`}));this.workspaces=[...n,...r],this.ensureWorkspaceSelections()}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.requestUpdate()}}ensureWorkspaceSelections(){let e=new Set(this.workspaces.map(e=>e.id)),t={...this.selectedWorkspaceIdByChannel};for(let n of this.channels){if(!n.supportsWorkspaceSelection)continue;let r=t[n.id],i=n.launchWorkspace?.id,a=n.status!==`stopped`&&n.status!==`error`;if(i&&e.has(i)&&a){t[n.id]=i;continue}r&&e.has(r)||(t[n.id]=i&&e.has(i)?i:`default`)}this.selectedWorkspaceIdByChannel=t}connectEvents(){this.eventSource?.close();let e=new EventSource(`/api/channels/events`);this.eventSource=e;let t=e=>{try{let t=JSON.parse(e.data);this.applyChannelEvent(t)}catch{}};e.addEventListener(`snapshot`,t),e.addEventListener(`status`,t),e.addEventListener(`log`,t),e.addEventListener(`qrcode`,t),e.addEventListener(`error`,()=>{})}applyChannelEvent(e){if(Array.isArray(e.channels)){this.channels=e.channels,this.ensureWorkspaceSelections(),this.requestUpdate();return}if(e.snapshot){this.upsertChannel(e.snapshot);return}}upsertChannel(e){let t=this.channels.findIndex(t=>t.id===e.id);t>=0?this.channels=[...this.channels.slice(0,t),e,...this.channels.slice(t+1)]:this.channels=[...this.channels,e],this.ensureWorkspaceSelections(),this.requestUpdate()}selectedWorkspaceId(e){return this.selectedWorkspaceIdByChannel[e.id]||e.launchWorkspace?.id||`default`}handleWorkspaceChange(e,t){let n=t.currentTarget;n&&(this.selectedWorkspaceIdByChannel={...this.selectedWorkspaceIdByChannel,[e.id]:n.value||`default`},this.requestUpdate())}startOptions(e){return e.supportsWorkspaceSelection?{projectId:this.selectedWorkspaceId(e)}:void 0}async invoke(e,t){if(!this.busyChannelId&&!(t===`restart`&&!await T({description:M(`channelRestartConfirm`,{name:e.name}),confirmLabel:M(`restart`),cancelLabel:M(`cancel`)}))){this.busyChannelId=e.id,this.error=``,this.message=``,this.requestUpdate();try{let n=t===`start`||t===`restart`?this.startOptions(e):void 0,r=await this.request(`/api/channels/${encodeURIComponent(e.id)}/${t}`,{method:`POST`,headers:n?{...Y,"content-type":`application/json`}:Y,body:n?JSON.stringify(n):void 0});this.upsertChannel(r)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.busyChannelId=``,this.requestUpdate()}}}async invokeAction(e,t){if(!this.busyChannelId&&!((t.destructive||t.id===`relogin`)&&!await T({description:t.id===`relogin`?M(`channelReloginConfirm`,{name:e.name}):M(`channelLogoutConfirm`,{name:e.name}),confirmLabel:t.label,cancelLabel:M(`cancel`),variant:t.destructive?`destructive`:void 0}))){this.busyChannelId=e.id,this.error=``,this.message=``,this.requestUpdate();try{let n=t.id===`relogin`?this.startOptions(e):void 0,r=await this.request(`/api/channels/${encodeURIComponent(e.id)}/actions/${encodeURIComponent(t.id)}`,{method:`POST`,headers:n?{...Y,"content-type":`application/json`}:Y,body:n?JSON.stringify(n):void 0});this.upsertChannel(r)}catch(e){this.error=e instanceof Error?e.message:M(`requestFailed`)}finally{this.busyChannelId=``,this.requestUpdate()}}}async openLogs(e){if(!this.openingLogsChannelId){this.openingLogsChannelId=e.id,this.error=``,this.message=``,this.requestUpdate();try{await this.request(`/api/channels/${encodeURIComponent(e.id)}/open-logs`,{method:`POST`,headers:Y})}catch(e){this.error=e instanceof Error?e.message:M(`channelOpenLogsFailed`)}finally{this.openingLogsChannelId=``,this.requestUpdate()}}}workspaceSection(e,t){if(!e.supportsWorkspaceSelection)return null;let n=this.selectedWorkspaceId(e);return b`
1627
+ <div class="quickforge-settings-row quickforge-settings-row-align-start">
1628
+ <div class="quickforge-settings-row-main">
1629
+ <div class="quickforge-settings-row-title">
1630
+ ${M(`channelWorkspace`)}
1631
+ <quickforge-info-tip .label=${M(`channelWorkspaceDescription`)}></quickforge-info-tip>
1632
+ </div>
1633
+ <div class="quickforge-settings-row-description">${M(`channelWorkspaceDescription`)}</div>
1634
+ </div>
1635
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1636
+ <select
1637
+ class="quickforge-settings-select"
1638
+ ?disabled=${t}
1639
+ .value=${n}
1640
+ @change=${t=>this.handleWorkspaceChange(e,t)}
1641
+ >
1642
+ ${this.workspaces.map(e=>b`
1643
+ <option value=${e.id}>${e.kind===`default`?M(`channelDefaultWorkspace`):e.name}</option>
1644
+ `)}
1645
+ </select>
1646
+ </div>
1647
+ </div>
1648
+ `}qrSection(e){return e.qrCodeText?b`
1649
+ <div class="quickforge-settings-row quickforge-settings-row-align-start">
1650
+ <div class="quickforge-settings-row-main">
1651
+ <div class="quickforge-settings-row-title">
1652
+ ${M(`channelQrTitle`)}
1653
+ <quickforge-info-tip .label=${M(`channelQrDescription`)}></quickforge-info-tip>
1654
+ </div>
1655
+ <div class="quickforge-settings-row-description">${M(`channelQrDescription`)}</div>
1656
+ </div>
1657
+ <pre class="quickforge-channel-qr-text">${e.qrCodeText}</pre>
1658
+ </div>
1659
+ `:null}channelCard(e){let t=this.busyChannelId===e.id||!!e.activeAction,n=this.openingLogsChannelId===e.id,r=e.status===`running`||e.status===`waiting_scan`||e.status===`starting`,i=e.status===`stopping`,a=!!(e.supportsWorkspaceSelection&&this.workspaces.length===0);return b`
1660
+ <section class="quickforge-settings-section" aria-label=${e.name}>
1661
+ <div class="quickforge-settings-row quickforge-settings-row-top">
1662
+ <div class="quickforge-settings-row-main">
1663
+ <div class="quickforge-settings-row-title">
1664
+ ${e.name}
1665
+ <span class="quickforge-settings-badge ${sn(e.status)}">${cn(e.status)}</span>
1666
+ </div>
1667
+ <div class="quickforge-settings-row-description">${e.description}</div>
1668
+ </div>
1669
+ <div class="quickforge-settings-row-control quickforge-settings-row-control-wide">
1670
+ <button class="quickforge-settings-button quickforge-settings-button-primary" type="button" ?disabled=${t||r||i||a} @click=${()=>this.invoke(e,`start`)}>${M(`start`)}</button>
1671
+ <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${t||!r||i} @click=${()=>this.invoke(e,`stop`)}>${M(`stop`)}</button>
1672
+ <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${t||i||a} @click=${()=>this.invoke(e,`restart`)}>${M(`restart`)}</button>
1673
+ ${e.actions?.map(n=>b`
1674
+ <button class="quickforge-settings-button ${n.destructive?`quickforge-settings-button-danger`:`quickforge-settings-button-secondary`}" type="button" ?disabled=${t} @click=${()=>this.invokeAction(e,n)}>${n.label}</button>
1675
+ `)}
1676
+ <button class="quickforge-settings-button quickforge-settings-button-secondary" type="button" ?disabled=${!!this.openingLogsChannelId} @click=${()=>this.openLogs(e)}>${M(n?`channelOpeningLogs`:`channelOpenLogs`)}</button>
1677
+ </div>
1678
+ </div>
1679
+
1680
+ ${this.workspaceSection(e,t||r||i)}
1681
+ ${a?b`<div class="quickforge-settings-alert quickforge-settings-warning-attached">${M(`channelNoWorkspaces`)}</div>`:null}
1682
+ ${e.error?b`<div class="quickforge-settings-alert quickforge-settings-warning-attached">${e.error}</div>`:null}
1683
+ ${this.qrSection(e)}
1684
+ </section>
1685
+ `}render(){return this.loading?b`<div class="quickforge-settings-note">${M(`loading`)}</div>`:b`
1686
+ <div class="quickforge-settings-stack">
1687
+ <div class="quickforge-settings-note">
1688
+ ${M(`channelsSecurityWarning`)}
1689
+ </div>
1690
+
1691
+ ${this.channels.length?b`${this.channels.map(e=>this.channelCard(e))}`:b`<div class="quickforge-settings-note">${M(`channelsEmpty`)}</div>`}
1692
+
1693
+ ${this.message?b`<div class="quickforge-settings-message">${this.message}</div>`:null}
1694
+ ${this.error?b`<div class="quickforge-settings-alert">${this.error}</div>`:null}
1695
+ </div>
1696
+ `}},un=`quickforge-channels-settings-tab`;customElements.get(un)||customElements.define(un,ln);function dn(){return document.createElement(un)}var fn=ie(),X=y(),pn=(0,N.lazy)(()=>C(()=>import(`./AgentProfilesPage-Dl5Peqov.js`).then(e=>({default:e.AgentProfilesPage})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]))),mn=(0,N.lazy)(()=>C(()=>import(`./mcp-servers-dialog-BEV4_TXC.js`).then(e=>({default:e.McpServersPanel})),__vite__mapDeps([15,1,2,3,4,5,6,7,8,9,10,11,12,13]))),hn=(0,N.lazy)(()=>C(()=>import(`./skills-dialog-BTJ7mzk_.js`).then(e=>e.n).then(e=>({default:e.SkillsManagerPanel})),__vite__mapDeps([10,1,2,3,4,5,6,7,8,9,11,12,13]))),gn=(0,N.lazy)(()=>C(()=>import(`./PluginsPage-BDKYfLna.js`).then(e=>({default:e.PluginsPage})),__vite__mapDeps([16,1,2,3,4,5,6,7,8,9,10,11,12,13,17]))),_n=(0,N.lazy)(()=>C(()=>import(`./ScheduledTasksPage--GAki3lB.js`).then(e=>({default:e.ScheduledTasksPage})),__vite__mapDeps([18,1,2,3,4,5,6,7,8,9,10,11,12,13,14]))),vn=(0,N.lazy)(()=>C(()=>import(`./ShareLinksSettingsPage-BQQMsvJo.js`).then(e=>({default:e.ShareLinksSettingsPage})),__vite__mapDeps([19,1,2,3,4,5,6,7,8,9,10,11,12,13]))),yn=(0,N.lazy)(()=>C(()=>import(`./CloudAccountSettingsPage-BG5Nn3bq.js`).then(e=>({default:e.CloudAccountSettingsPage})),__vite__mapDeps([20,1,2,3,4,5,6,7,8,9,10,11,12,13]))),bn=class extends x{tabName=``;renderReact;root;getTabName(){return this.tabName}connectedCallback(){super.connectedCallback(),this.updateComplete.then(()=>this.mountReact())}disconnectedCallback(){this.root?.unmount(),this.root=void 0,super.disconnectedCallback()}render(){return b`<div class="quickforge-react-settings-tab h-full min-h-0"></div>`}mountReact(){let e=this.querySelector(`.quickforge-react-settings-tab`);!e||!this.renderReact||(this.root??=(0,fn.createRoot)(e),this.root.render(this.renderReact()))}},xn=`quickforge-react-settings-tab`;customElements.get(xn)||customElements.define(xn,bn);function Z(e,t){let n=document.createElement(xn);return n.tabName=e,n.renderReact=t,n}function Q({children:e}){return(0,X.jsx)(`div`,{className:`quickforge-settings-stack h-full min-h-[30rem]`,children:(0,X.jsx)(N.Suspense,{fallback:(0,X.jsx)(`div`,{className:`quickforge-settings-note`,children:M(`loading`)}),children:e})})}function Sn(e){window.dispatchEvent(new CustomEvent(`quickforge:open-session-from-settings`,{detail:{sessionId:e}}))}function Cn(){return Z(M(`agentsTab`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(pn,{})}))}function wn(){return Z(M(`skills`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(hn,{active:!0,scope:`global`,embedded:!0,onSaved:()=>void 0})}))}function Tn(){return Z(M(`mcpServers`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(mn,{active:!0})}))}function En(){return Z(M(`plugins`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(gn,{})}))}function Dn(){return Z(M(`scheduledTasks`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(_n,{onOpenSession:Sn})}))}function On(){return Z(M(`cloudAccount`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(yn,{})}))}function kn(){return Z(M(`shareLinks`),()=>(0,X.jsx)(Q,{children:(0,X.jsx)(vn,{})}))}function An(e){let t=[{key:`appearance`,tab:Qe(),getDescription:()=>M(`appearanceDescription`)},{key:`cloudAccount`,tab:On(),getDescription:()=>M(`cloudAccountDescription`)},{key:`defaults`,tab:vt(),getDescription:()=>M(`defaultOptionsDescription`)},{key:`memory`,tab:Tt(),getDescription:()=>M(`memoryDescription`)},{key:`customModels`,tab:Ye(e),getDescription:()=>M(`customModelsDescription`)},{key:`agents`,tab:Cn(),getDescription:()=>M(`agentsDescription`)},{key:`skills`,tab:wn(),getDescription:()=>M(`globalSkillsDescription`)},{key:`mcp`,tab:Tn(),getDescription:void 0},{key:`plugins`,tab:En(),getDescription:()=>M(`pluginsDescription`)},{key:`scheduledTasks`,tab:Dn(),getDescription:()=>M(`scheduledTasksDescription`)},{key:`projectCommands`,tab:on(),getDescription:()=>M(`projectCommandsDescription`)},{key:`backup`,tab:Ft(),getDescription:()=>M(`backupRestoreDescription`)},{key:`archivedConversations`,tab:Bt(),getDescription:()=>M(`archivedConversationsDescription`)},{key:`shareLinks`,tab:kn(),getDescription:()=>M(`shareLinksDescription`)},{key:`channels`,tab:dn(),getDescription:()=>M(`channelsDescription`)},{key:`lanAccess`,tab:Kt(),getDescription:()=>M(`lanAccessDescription`)},{key:`about`,tab:nn(),getDescription:()=>M(`aboutQuickForgeDescription`)}];return{items:[...t],tabs:t.map(e=>e.tab),indexOf:e=>t.findIndex(t=>t.key===e)}}var $={appearance:a,cloudAccount:_,defaults:te,memory:i,customModels:d,agents:s,skills:v,mcp:u,plugins:g,scheduledTasks:r,projectCommands:h,backup:t,archivedConversations:n,shareLinks:l,channels:p,lanAccess:f,about:o};function jn({tab:e}){let t=(0,N.useRef)(null);return(0,N.useEffect)(()=>{let n=t.current;if(n)return n.replaceChildren(e),()=>{e.parentNode===n&&n.removeChild(e)}},[e]),(0,X.jsx)(`div`,{ref:t,className:`quickforge-settings-tab-host min-h-0 flex-1`})}function Mn({initialTab:e,customProvider:t,onBack:n}){let r=(0,N.useMemo)(()=>An(t),[t]),i=Math.max(0,r.indexOf(e)),[a,o]=(0,N.useState)(),[s,l]=(0,N.useState)(null),u=a??i,[d,f]=(0,N.useState)(``),p=d.trim().toLowerCase(),m=(0,N.useMemo)(()=>r.items.map((e,t)=>({item:e,index:t})).filter(({item:e})=>p?[e.key,e.tab.getTabName(),e.getDescription?.()??``].join(` `).toLowerCase().includes(p):!0),[r.items,p]),h=(0,N.useMemo)(()=>{if(!p)return u;let e=m[0];return e?m.some(({index:e})=>e===u)?u:e.index:u},[u,m,p]),g=r.items[h]??r.items[0],_=g?.getDescription?.(),v=g?$[g.key]:void 0,y=(0,N.useRef)(null),[ie,ae]=(0,N.useState)(!1);return(0,N.useEffect)(()=>{y.current?.scrollTo({top:0})},[h,s]),(0,X.jsxs)(`div`,{className:`flex h-screen min-h-0 supports-[height:100dvh]:h-dvh bg-[var(--quickforge-sidebar-bg)] text-foreground`,children:[(0,X.jsxs)(`aside`,{className:`relative z-10 hidden w-80 shrink-0 overflow-hidden bg-[var(--quickforge-sidebar-bg)] md:flex md:min-h-0 md:flex-col`,children:[(0,X.jsxs)(`div`,{className:`shrink-0 px-3 pb-2 pt-3`,children:[(0,X.jsxs)(`button`,{type:`button`,className:`group relative flex w-full items-center gap-2 overflow-hidden rounded-lg px-2 py-1.5 text-left text-muted-foreground/72 transition-[background-color,color,box-shadow] duration-160 ease-out hover:bg-[var(--quickforge-sidebar-hover-bg)] hover:text-foreground/86 hover:shadow-[0_8px_20px_-18px_rgb(15_23_42_/_0.35)]`,onClick:n,"aria-label":`返回工作区`,children:[(0,X.jsx)(`span`,{className:`inline-flex size-6 shrink-0 items-center justify-center rounded-full text-muted-foreground/55 transition-colors group-hover:text-foreground/70`,children:(0,X.jsx)(ee,{className:`size-4`})}),(0,X.jsx)(`span`,{className:`truncate text-sm leading-5`,children:`返回工作区`})]}),(0,X.jsxs)(`div`,{className:`quickforge-settings-inline-field mt-3`,children:[(0,X.jsx)(re,{className:`pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground/60`,"aria-hidden":`true`}),(0,X.jsx)(`input`,{value:d,onChange:e=>f(e.target.value),placeholder:M(`searchSettings`),"aria-label":M(`searchSettings`),className:`quickforge-settings-input`})]})]}),(0,X.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-3 pb-4 pt-4`,children:(0,X.jsxs)(`nav`,{className:`space-y-1`,"aria-label":M(`settings`),children:[m.map(({item:e,index:t})=>{let n=t===h,r=$[e.key];return(0,X.jsxs)(`button`,{type:`button`,className:O(`group relative flex w-full items-center gap-2.5 overflow-hidden rounded-lg px-2 py-1.5 text-left text-sm leading-5 transition-[background-color,color,box-shadow] duration-160 ease-out`,n?`bg-[var(--quickforge-sidebar-active-bg)] font-medium text-foreground/92 shadow-[0_8px_22px_-20px_rgb(15_23_42_/_0.32)]`:`text-muted-foreground/76 hover:bg-[var(--quickforge-sidebar-hover-bg)] hover:text-foreground/90 hover:shadow-[0_8px_20px_-18px_rgb(15_23_42_/_0.35)]`),onClick:()=>o(t),"aria-current":n?`page`:void 0,children:[(0,X.jsx)(`span`,{className:O(`inline-flex size-5 shrink-0 items-center justify-center transition-colors`,n?`text-foreground/72`:`text-muted-foreground/52 group-hover:text-foreground/68`),children:(0,X.jsx)(r,{className:`size-4`,"aria-hidden":`true`})}),(0,X.jsx)(`span`,{className:`truncate`,children:e.tab.getTabName()})]},e.key)}),m.length===0?(0,X.jsx)(`div`,{className:`px-2 py-3 text-sm text-muted-foreground/70`,children:M(`noSettingsResults`)}):null]})})]}),(0,X.jsxs)(`main`,{className:`relative flex min-w-0 flex-1 flex-col bg-[var(--quickforge-main-bg)] md:overflow-hidden md:rounded-tl-2xl`,children:[(0,X.jsxs)(`header`,{className:`flex h-14 shrink-0 items-center gap-2 border-b-[0.5px] border-[color-mix(in_oklab,var(--border)_34%,transparent)] px-3 pr-4 md:px-5`,children:[(0,X.jsx)(Pe,{variant:`ghost`,size:`icon`,className:`md:hidden`,onClick:s===null?n:()=>l(null),"aria-label":s===null?`返回工作区`:`返回设置`,children:(0,X.jsx)(ee,{className:`size-4`})}),(0,X.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,X.jsxs)(`div`,{className:O(`flex min-w-0 items-center gap-2`,s===null?`md:hidden`:`hidden`),children:[(0,X.jsx)(te,{className:`size-4 shrink-0 text-muted-foreground/65`,"aria-hidden":`true`}),(0,X.jsx)(`div`,{className:`min-w-0 truncate text-sm font-medium text-foreground/90`,children:M(`settings`)})]}),(0,X.jsxs)(`div`,{className:O(`flex min-w-0 items-center gap-2`,s===null&&`hidden md:flex`),children:[v?(0,X.jsx)(v,{className:`size-4 shrink-0 text-muted-foreground/65`,"aria-hidden":`true`}):null,(0,X.jsx)(`div`,{className:`min-w-0 truncate text-sm font-medium text-foreground/90`,children:g?.tab.getTabName()}),_?(0,X.jsx)(De,{label:_}):null]})]})]}),(0,X.jsxs)(`div`,{className:O(`flex min-h-0 flex-1 flex-col md:hidden`,s!==null&&`hidden`),children:[(0,X.jsx)(`div`,{className:`shrink-0 border-b-[0.5px] border-[color-mix(in_oklab,var(--border)_30%,transparent)] px-3 py-2`,children:(0,X.jsxs)(`div`,{className:`quickforge-settings-inline-field`,children:[(0,X.jsx)(re,{className:`pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground/60`,"aria-hidden":`true`}),(0,X.jsx)(`input`,{value:d,onChange:e=>f(e.target.value),placeholder:M(`searchSettings`),"aria-label":M(`searchSettings`),className:`quickforge-settings-input`})]})}),(0,X.jsx)(`nav`,{className:`quickforge-settings-mobile-list`,"aria-label":M(`settings`),children:(0,X.jsxs)(`div`,{className:`quickforge-settings-mobile-list-card`,children:[m.map(({item:e,index:t})=>{let n=$[e.key];return(0,X.jsxs)(`button`,{type:`button`,className:`quickforge-settings-mobile-list-item`,onClick:()=>{o(t),l(t)},children:[(0,X.jsx)(`span`,{className:`quickforge-settings-mobile-list-icon`,children:(0,X.jsx)(n,{className:`size-4 shrink-0`,"aria-hidden":`true`})}),(0,X.jsx)(`span`,{className:`quickforge-settings-mobile-list-label`,children:e.tab.getTabName()}),(0,X.jsx)(ne,{className:`quickforge-settings-mobile-list-chevron size-4 shrink-0`,"aria-hidden":`true`})]},e.key)}),m.length===0?(0,X.jsx)(`div`,{className:`px-2 py-8 text-center text-sm text-muted-foreground/70`,children:M(`noSettingsResults`)}):null]})})]}),(0,X.jsx)(`section`,{ref:y,onScroll:()=>ae((y.current?.scrollTop??0)>160),className:O(`min-h-0 flex-1 overflow-y-auto px-4 py-5 md:px-8 md:py-7`,s===null&&`hidden md:block`),children:(0,X.jsx)(`div`,{className:`mx-auto flex min-h-full w-full max-w-5xl flex-col`,children:m.length===0?(0,X.jsx)(`div`,{className:`rounded-xl border border-dashed border-[color-mix(in_oklab,var(--border)_70%,transparent)] p-8 text-center text-sm text-muted-foreground/70`,children:M(`noSettingsResults`)}):g?(0,X.jsx)(jn,{tab:g.tab}):null})}),(0,X.jsx)(`button`,{type:`button`,onClick:()=>y.current?.scrollTo({top:0,behavior:`smooth`}),"aria-label":`返回顶部`,className:O(`absolute bottom-5 right-4 z-20 flex size-9 items-center justify-center rounded-[10px] border-[0.5px] border-[color-mix(in_oklab,var(--border)_65%,transparent)] bg-[var(--background)] text-muted-foreground/85 shadow-[0_10px_24px_-16px_rgb(15_23_42_/_0.55)] transition-opacity duration-160 md:hidden`,ie?`opacity-100`:`pointer-events-none opacity-0`,s===null&&`hidden`),children:(0,X.jsx)(c,{className:`size-4`})})]})]})}export{Mn as SettingsWorkspacePage};