@runfusion/fusion 0.14.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/bin.js +277 -105
  2. package/dist/client/assets/{AgentDetailView-CBFUveyO.js → AgentDetailView-C2Iik3Qf.js} +1 -1
  3. package/dist/client/assets/{AgentsView-DPezXQ-U.js → AgentsView-DkX0tzrN.js} +3 -3
  4. package/dist/client/assets/ChatView-CEm2Hw6m.js +1 -0
  5. package/dist/client/assets/{DevServerView-Daft4YFc.js → DevServerView-Bumvo_ge.js} +1 -1
  6. package/dist/client/assets/{DirectoryPicker-rew1y6qO.js → DirectoryPicker-CXN11cBp.js} +1 -1
  7. package/dist/client/assets/{DocumentsView-i72qJzwd.js → DocumentsView-B71IqAxA.js} +1 -1
  8. package/dist/client/assets/{InsightsView-BL5eZJ0a.js → InsightsView-Bs4Rldu6.js} +1 -1
  9. package/dist/client/assets/{MemoryView-pl8Cdg_p.js → MemoryView-Bs7b_L2Q.js} +1 -1
  10. package/dist/client/assets/{NodesView-D6eJ15zc.js → NodesView-BvAGTXbO.js} +1 -1
  11. package/dist/client/assets/{PiExtensionsManager-ExInwXWP.js → PiExtensionsManager-3Kcc4uhA.js} +2 -2
  12. package/dist/client/assets/PluginManager-Ch-Xynlm.js +1 -0
  13. package/dist/client/assets/PluginManager-DA_T0GHn.css +1 -0
  14. package/dist/client/assets/{ResearchView-B_QPUEjB.js → ResearchView-Bj6Saqf6.js} +1 -1
  15. package/dist/client/assets/{RoadmapsView-DBNLaEsK.js → RoadmapsView-9qT8Vwd0.js} +1 -1
  16. package/dist/client/assets/{SettingsModal-CL_gWmOj.js → SettingsModal-D4ERGQNQ.js} +1 -1
  17. package/dist/client/assets/SettingsModal-Zo5qDGOq.js +31 -0
  18. package/dist/client/assets/{SetupWizardModal-CLkY9HFL.js → SetupWizardModal-Dv0rX2_o.js} +1 -1
  19. package/dist/client/assets/{SkillMultiselect-B0qi32SQ.js → SkillMultiselect-CSkXQzdv.js} +1 -1
  20. package/dist/client/assets/{SkillsView-umVjRq6o.js → SkillsView-2srXMOzj.js} +1 -1
  21. package/dist/client/assets/TodoView-CxPPIvw2.js +6 -0
  22. package/dist/client/assets/{folder-open-nYPrL1W3.js → folder-open-FA1PwpXV.js} +1 -1
  23. package/dist/client/assets/{index-Bc8nfKeH.js → index-CEavim6l.js} +150 -149
  24. package/dist/client/assets/index-D1gTSlYB.css +1 -0
  25. package/dist/client/assets/{list-checks-sK8xJeH_.js → list-checks-6EktkUso.js} +1 -1
  26. package/dist/client/assets/{star-BRtXbYkB.js → star-B6Th07jw.js} +1 -1
  27. package/dist/client/assets/{upload-BP60eBwN.js → upload-BJwuErhV.js} +1 -1
  28. package/dist/client/assets/{users-qSGAX2Pf.js → users-BrnPTF8H.js} +1 -1
  29. package/dist/client/index.html +2 -2
  30. package/dist/client/version.json +1 -1
  31. package/dist/extension.js +273 -101
  32. package/dist/pi-claude-cli/package.json +1 -1
  33. package/dist/pi-claude-cli/src/__tests__/event-bridge.test.ts +107 -0
  34. package/dist/pi-claude-cli/src/event-bridge.ts +48 -4
  35. package/package.json +1 -1
  36. package/skill/fusion/references/engine-tools.md +0 -1
  37. package/dist/client/assets/ChatView-5N4-EuhD.js +0 -1
  38. package/dist/client/assets/PluginManager-CYhtxHun.js +0 -1
  39. package/dist/client/assets/PluginManager-jyNkJZSz.css +0 -1
  40. package/dist/client/assets/SettingsModal-1ET586M3.js +0 -31
  41. package/dist/client/assets/TodoView-CFifSvrD.js +0 -6
  42. package/dist/client/assets/index-C1prPuSl.css +0 -1
@@ -178,6 +178,89 @@ describe("createEventBridge", () => {
178
178
  expect(textEnd1.contentIndex).toBe(1);
179
179
  expect(textEnd1.content).toBe("Second");
180
180
  });
181
+
182
+ it("repairs a missing sentence boundary between consecutive text blocks", () => {
183
+ const bridge = createBridgeWithStart();
184
+
185
+ bridge.handleEvent({
186
+ type: "content_block_start",
187
+ index: 0,
188
+ content_block: { type: "text", text: "" },
189
+ });
190
+ bridge.handleEvent({
191
+ type: "content_block_delta",
192
+ index: 0,
193
+ delta: { type: "text_delta", text: "compare them." },
194
+ });
195
+ bridge.handleEvent({
196
+ type: "content_block_stop",
197
+ index: 0,
198
+ });
199
+
200
+ bridge.handleEvent({
201
+ type: "content_block_start",
202
+ index: 1,
203
+ content_block: { type: "text", text: "" },
204
+ });
205
+ bridge.handleEvent({
206
+ type: "content_block_delta",
207
+ index: 1,
208
+ delta: { type: "text_delta", text: "Good overview." },
209
+ });
210
+
211
+ const output = bridge.getOutput();
212
+ const combinedText = output.content
213
+ .filter((content): content is any => content.type === "text")
214
+ .map((content: any) => content.text)
215
+ .join("");
216
+
217
+ expect(combinedText).toBe("compare them. Good overview.");
218
+ expect(stream.events[4]).toEqual(
219
+ expect.objectContaining({
220
+ type: "text_delta",
221
+ contentIndex: 1,
222
+ delta: " Good overview.",
223
+ }),
224
+ );
225
+ });
226
+
227
+ it("does not insert spaces into lowercase continuations like property access", () => {
228
+ const bridge = createBridgeWithStart();
229
+
230
+ bridge.handleEvent({
231
+ type: "content_block_start",
232
+ index: 0,
233
+ content_block: { type: "text", text: "" },
234
+ });
235
+ bridge.handleEvent({
236
+ type: "content_block_delta",
237
+ index: 0,
238
+ delta: { type: "text_delta", text: "console." },
239
+ });
240
+ bridge.handleEvent({
241
+ type: "content_block_stop",
242
+ index: 0,
243
+ });
244
+
245
+ bridge.handleEvent({
246
+ type: "content_block_start",
247
+ index: 1,
248
+ content_block: { type: "text", text: "" },
249
+ });
250
+ bridge.handleEvent({
251
+ type: "content_block_delta",
252
+ index: 1,
253
+ delta: { type: "text_delta", text: "log('hi')" },
254
+ });
255
+
256
+ const output = bridge.getOutput();
257
+ const combinedText = output.content
258
+ .filter((content): content is any => content.type === "text")
259
+ .map((content: any) => content.text)
260
+ .join("");
261
+
262
+ expect(combinedText).toBe("console.log('hi')");
263
+ });
181
264
  });
182
265
 
183
266
  describe("message_start usage tracking", () => {
@@ -795,6 +878,30 @@ describe("createEventBridge", () => {
795
878
  expect(thinkingBlock.thinking).toBe("First thought. Second thought.");
796
879
  });
797
880
 
881
+ it("repairs a missing sentence boundary between thinking deltas", () => {
882
+ const bridge = createBridgeWithStart();
883
+
884
+ bridge.handleEvent({
885
+ type: "content_block_start",
886
+ index: 0,
887
+ content_block: { type: "thinking" },
888
+ });
889
+ bridge.handleEvent({
890
+ type: "content_block_delta",
891
+ index: 0,
892
+ delta: { type: "thinking_delta", thinking: "I will inspect the hook." },
893
+ });
894
+ bridge.handleEvent({
895
+ type: "content_block_delta",
896
+ index: 0,
897
+ delta: { type: "thinking_delta", thinking: "Good, now the component." },
898
+ });
899
+
900
+ const output = bridge.getOutput();
901
+ const thinkingBlock = output.content[0] as any;
902
+ expect(thinkingBlock.thinking).toBe("I will inspect the hook. Good, now the component.");
903
+ });
904
+
798
905
  it("emits thinking_end for thinking block stop", () => {
799
906
  const bridge = createBridgeWithStart();
800
907
 
@@ -60,6 +60,29 @@ function mapStopReason(
60
60
  }
61
61
  }
62
62
 
63
+ function normalizeStreamingDelta(previousText: string, nextDelta: string): string {
64
+ if (!previousText || !nextDelta) {
65
+ return nextDelta;
66
+ }
67
+
68
+ const previousChar = previousText.slice(-1);
69
+ const nextChar = nextDelta[0] ?? "";
70
+
71
+ if (/\s/.test(previousChar) || /\s/.test(nextChar)) {
72
+ return nextDelta;
73
+ }
74
+
75
+ // Claude sometimes splits adjacent sentences across separate deltas or text
76
+ // blocks without preserving the separating space. Only repair the specific
77
+ // "sentence punctuation + uppercase/quoted sentence start" case so code,
78
+ // domains, and lowercase continuations remain untouched.
79
+ if (/[.!?]/.test(previousChar) && /[A-Z0-9"'([]/.test(nextChar)) {
80
+ return ` ${nextDelta}`;
81
+ }
82
+
83
+ return nextDelta;
84
+ }
85
+
63
86
  /**
64
87
  * Create an event bridge that translates Claude API streaming events
65
88
  * into pi's AssistantMessageEventStream events.
@@ -98,6 +121,19 @@ export function createEventBridge(
98
121
 
99
122
  let started = false;
100
123
 
124
+ function getPreviousContentText(contentIndex: number, type: "text" | "thinking"): string {
125
+ for (let i = contentIndex - 1; i >= 0; i--) {
126
+ const contentBlock = output.content[i];
127
+ if (type === "text" && contentBlock?.type === "text") {
128
+ return contentBlock.text;
129
+ }
130
+ if (type === "thinking" && contentBlock?.type === "thinking") {
131
+ return contentBlock.thinking;
132
+ }
133
+ }
134
+ return "";
135
+ }
136
+
101
137
  function handleEvent(event: ClaudeApiEvent): void {
102
138
  // Emit start event on first message — tells pi to begin incremental rendering
103
139
  if (!started) {
@@ -226,14 +262,18 @@ export function createEventBridge(
226
262
 
227
263
  const block = blocks[idx];
228
264
  if (block.type === "text") {
229
- block.text += event.delta!.text;
265
+ const delta = normalizeStreamingDelta(
266
+ block.text || getPreviousContentText(idx, "text"),
267
+ event.delta!.text,
268
+ );
269
+ block.text += delta;
230
270
  const contentBlock = output.content[idx] as TextContent;
231
271
  contentBlock.text = block.text;
232
272
 
233
273
  stream.push({
234
274
  type: "text_delta",
235
275
  contentIndex: idx,
236
- delta: event.delta!.text,
276
+ delta,
237
277
  partial: output,
238
278
  });
239
279
  }
@@ -246,14 +286,18 @@ export function createEventBridge(
246
286
 
247
287
  const block = blocks[idx];
248
288
  if (block.type === "thinking") {
249
- block.text += event.delta!.thinking;
289
+ const delta = normalizeStreamingDelta(
290
+ block.text || getPreviousContentText(idx, "thinking"),
291
+ event.delta!.thinking,
292
+ );
293
+ block.text += delta;
250
294
  const contentBlock = output.content[idx] as ThinkingContent;
251
295
  contentBlock.thinking = block.text;
252
296
 
253
297
  stream.push({
254
298
  type: "thinking_delta",
255
299
  contentIndex: idx,
256
- delta: event.delta!.thinking,
300
+ delta,
257
301
  partial: output,
258
302
  });
259
303
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runfusion/fusion",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "license": "MIT",
5
5
  "description": "Fusion CLI: HTTP API server, daemon, dashboard launcher, and task tooling for the Fusion AI coding agent.",
6
6
  "homepage": "https://github.com/Runfusion/Fusion#readme",
@@ -55,5 +55,4 @@ These tools are **not** part of the pi extension's user-invokable `extension.ts`
55
55
 
56
56
  | Tool | Purpose | Parameters |
57
57
  |---|---|---|
58
- | `fn_identity` | Return loaded soul/instructions/memory summary for this heartbeat tick (must be called first) | none |
59
58
  | `fn_heartbeat_done` | Signal end of heartbeat run with optional summary | `summary?` (string) |
@@ -1 +0,0 @@
1
- import{r as s,j as t}from"./vendor-react-K0fH_qHe.js";import{i as He,aL as Pt,g as Et,aM as Ft,a as Lt,aN as _t,aO as It,aP as Ot,aQ as Ut,aR as zt,aS as Ht,s as Vt,aT as Bt,aU as qt,aV as Gt,aW as Wt,ab as rt,ac as lt,S as Kt,V as Ze,J as et,ao as Jt,aX as Yt,B as Pe,a8 as ot,a9 as ct,aY as Qt,aZ as Xt,a_ as Zt,a$ as es,b0 as ts,b1 as ss,b2 as ns,b3 as tt,h as as,u as st,k as is}from"./index-Bc8nfKeH.js";import"./vendor-xterm-DzcZoU0P.js";const ze="kb-chat-active-session";function rs(a){const i=a?.toolCalls;if(!Array.isArray(i))return;const r=i.map(l=>{if(!l||typeof l!="object")return null;const c=l,N=typeof c.toolName=="string"?c.toolName:"";if(!N)return null;const j=c.args;return{toolName:N,...j&&typeof j=="object"?{args:j}:{},isError:!!c.isError,result:c.result,status:"completed"}}).filter(l=>l!==null);return r.length>0?r:void 0}function nt(a){return{id:a.id,sessionId:a.sessionId,role:a.role,content:a.content,thinkingOutput:a.thinkingOutput,toolCalls:rs(a.metadata),attachments:a.attachments,createdAt:a.createdAt}}function ls(a){const[i,r]=s.useState([]),[l,c]=s.useState(null),[N,j]=s.useState(!0),[R,k]=s.useState([]),[P,I]=s.useState(!1),[x,F]=s.useState(!1),[T,$]=s.useState(""),[g,v]=s.useState(""),[C,y]=s.useState([]),[B,E]=s.useState(""),[X,W]=s.useState(""),[L,Y]=s.useState(!0),[h,M]=s.useState(new Map),w=s.useRef(null),_=s.useRef(!1),z=s.useRef(""),te=s.useRef(null),he=s.useRef(i),xe=s.useRef(l),ve=s.useRef(x);he.current=i,xe.current=l,ve.current=x,s.useEffect(()=>{z.current=B},[B]);const we=s.useRef(new Set),me=s.useRef(0),ke=s.useRef(a);ke.current!==a&&(ke.current=a,me.current++),s.useEffect(()=>{const d=me.current;He(void 0,a).then(f=>{if(me.current!==d)return;const p=new Map;for(const S of f)p.set(S.id,S);M(p)}).catch(()=>{})},[a]);const K=s.useCallback(async()=>{j(!0);try{const f=[...(await Pt(a)).sessions].sort((p,S)=>new Date(S.updatedAt).getTime()-new Date(p.updatedAt).getTime());r(f)}catch{}finally{j(!1)}},[a]);s.useEffect(()=>{K()},[K]);const se=s.useRef(()=>{});s.useEffect(()=>{if(N)return;const d=Et(ze,a);d&&i.find(p=>p.id===d)&&se.current(d)},[N,i,a]);const oe=s.useCallback(async(d,f)=>{I(!0);try{const p=await Ft(d,{limit:50,...f},a),S=p.messages.map(nt);f?.offset&&f.offset>0?k(q=>[...S,...q]):k(S),Y(p.messages.length>=50)}catch{}finally{I(!1)}},[a]),ne=s.useCallback(()=>{te.current?.(),te.current=null,z.current="",E(""),$(""),v(""),y([]),F(!1)},[]),ae=s.useCallback((d,f)=>{w.current&&(w.current.close(),w.current=null);const p=f??i.find(S=>S.id===d);c(p||null),ne(),Y(!0),d?oe(d):k([]),d?Lt(ze,d,a):_t(ze,a)},[i,oe,a,ne]);se.current=ae;const fe=s.useCallback(async d=>{const f=await It(d,a);w.current&&(w.current.close(),w.current=null);const p={id:f.session.id,title:f.session.title,agentId:f.session.agentId,status:f.session.status,modelProvider:f.session.modelProvider,modelId:f.session.modelId,createdAt:f.session.createdAt,updatedAt:f.session.updatedAt};return r(S=>S.some(q=>q.id===p.id)?S:[p,...S]),ne(),ae(p.id,p),k([]),p},[a,ne,ae]),Se=s.useCallback(async d=>{await Ot(d,{status:"archived"},a),r(f=>f.filter(p=>p.id!==d)),l?.id===d&&(c(null),k([]))},[l,a]),ce=s.useCallback(async d=>{l?.id===d&&w.current&&(w.current.close(),w.current=null),await Ut(d,a),r(f=>f.filter(p=>p.id!==d)),l?.id===d&&(c(null),k([]))},[l,a]),Z=s.useCallback(async()=>{!l||!L||await oe(l.id,{offset:R.length})},[l,L,oe,R.length]),de=s.useCallback(()=>{l&&(_.current=!0,te.current?.(),te.current=null,w.current?.close(),w.current=null,zt(l.id,a).catch(()=>{}),F(!1),$(""),v(""),y([]))},[l,a]),ye=s.useCallback(()=>{z.current="",E("")},[]),ue=s.useCallback((d,f)=>{if(!l)return;if(x){z.current=d,E(d);return}_.current=!1,w.current&&(w.current.close(),w.current=null);const p=`temp-${Date.now()}`,S={id:p,sessionId:l.id,role:"user",content:d,createdAt:new Date().toISOString()};k(m=>[...m,S]),$(""),v(""),y([]),F(!0);let q="",ge="",O=[],H=null,ie=null;const G=()=>{H=null,$(q)},u=()=>{ie=null,v(ge)},A=()=>{H!==null&&(cancelAnimationFrame(H),H=null),ie!==null&&(cancelAnimationFrame(ie),ie=null)};te.current=A;const V={onThinking:m=>{ge+=m,ie===null&&(ie=requestAnimationFrame(u))},onText:m=>{q+=m,H===null&&(H=requestAnimationFrame(G))},onToolStart:m=>{O=[...O,{toolName:m.toolName,args:m.args,isError:!1,status:"running"}],y(O)},onToolEnd:m=>{const D=[...O];for(let b=D.length-1;b>=0;b--){const ee=D[b];if(ee?.toolName===m.toolName&&ee.status==="running"){D[b]={...ee,status:"completed",isError:m.isError,result:m.result},O=D,y(D);return}}O=[...D,{toolName:m.toolName,isError:m.isError,result:m.result,status:"completed"}],y(O)},onDone:m=>{A();const D={id:m.messageId||`msg-${Date.now()}`,sessionId:l.id,role:"assistant",content:q,thinkingOutput:ge,toolCalls:O.length>0?O:void 0,createdAt:new Date().toISOString()};we.current.add(D.id),k(ee=>[...ee,D]),$(""),v(""),y([]),F(!1),w.current=null,setTimeout(()=>{we.current.delete(D.id)},1e3),K();const b=z.current.trim();b&&(z.current="",E(""),ue(b))},onError:m=>{if(A(),k(D=>D.filter(b=>b.id!==p)),$(""),v(""),y([]),F(!1),w.current=null,console.error("[useChat] Stream error:",m),!_.current){const D=z.current.trim();D&&(z.current="",E(""),ue(D))}}};w.current=Ht(l.id,d,V,f,a)},[l,x,a,K]),Ne=X?i.filter(d=>d.title?.toLowerCase().includes(X.toLowerCase())||d.agentId.toLowerCase().includes(X.toLowerCase())):i;return s.useEffect(()=>{const d=me.current,f=a?`?projectId=${encodeURIComponent(a)}`:"",p=()=>me.current!==d,S=G=>{if(p())return;const u=JSON.parse(G.data);r(A=>A.some(V=>V.id===u.id)?A:[u,...A])},q=G=>{if(p())return;const u=JSON.parse(G.data);r(A=>[...A.map(m=>m.id===u.id?u:m)]),xe.current?.id===u.id&&c(u)},ge=G=>{if(p())return;const{id:u}=JSON.parse(G.data);r(A=>A.filter(V=>V.id!==u)),xe.current?.id===u&&(c(null),k([]))},O=G=>{if(p())return;const u=JSON.parse(G.data),A=nt(u);we.current.has(A.id)||xe.current?.id===A.sessionId&&!ve.current&&k(V=>V.some(m=>m.id===A.id)?V:[...V,A])},H=G=>{if(p())return;const{id:u}=JSON.parse(G.data);k(A=>A.filter(V=>V.id!==u))};return Vt(`/api/events${f}`,{events:{"chat:session:created":S,"chat:session:updated":q,"chat:session:deleted":ge,"chat:message:added":O,"chat:message:deleted":H}})},[a]),s.useEffect(()=>()=>{w.current&&(w.current.close(),w.current=null)},[]),{sessions:i,activeSession:l,sessionsLoading:N,messages:R,messagesLoading:P,isStreaming:x,streamingText:T,streamingThinking:g,streamingToolCalls:C,pendingMessage:B,selectSession:ae,createSession:fe,archiveSession:Se,deleteSession:ce,sendMessage:ue,stopStreaming:de,clearPendingMessage:ye,loadMoreMessages:Z,hasMoreMessages:L,searchQuery:X,setSearchQuery:W,filteredSessions:Ne,refreshSessions:K,agentsMap:h}}function dt(a){const i=new Date(a),l=new Date().getTime()-i.getTime(),c=Math.floor(l/1e3),N=Math.floor(c/60),j=Math.floor(N/60),R=Math.floor(j/24);return c<60?"just now":N<60?`${N}m ago`:j<24?`${j}h ago`:R<7?`${R}d ago`:i.toLocaleDateString()}function at(a,i){if(!a||!i)return null;const r=i.toLowerCase();if(r.includes("claude")){let c=i.replace(/^claude[- ]/i,"Claude ").replace(/sonnet[- ](\d+)[- ](\d+)/i,"Sonnet $1.$2").replace(/sonnet[- ](\d+)/i,"Sonnet $1").replace(/haiku[- ](\d+)/i,"Haiku $1").replace(/opus[- ](\d+)/i,"Opus $1").replace(/sonnet/i,"Sonnet").replace(/haiku/i,"Haiku").replace(/opus/i,"Opus").replace(/-/g," ").trim();return c=c.replace(/\s+/g," "),c.length>30?c.slice(0,30)+"…":c}if(r.includes("gpt")||r.includes("openai")){const c=i.replace(/^gpt-4-turbo$/i,"GPT-4 Turbo").replace(/^gpt-4o-mini$/i,"GPT-4o Mini").replace(/^gpt-4o$/i,"GPT-4o").replace(/^gpt-4$/i,"GPT-4").replace(/^gpt-o1-preview$/i,"GPT-o1 Preview").replace(/^gpt-o1-mini$/i,"GPT-o1 Mini").replace(/^gpt-o1$/i,"GPT-o1").replace(/^gpt/i,"GPT").trim();return c.length>30?c.slice(0,30)+"…":c}if(r.includes("gemini")){const c=i.replace(/^gemini[- ]/i,"Gemini ").replace(/pro[- ](\d+)[- ](\d+)/i,"Pro $1.$2").replace(/pro[- ](\d+)/i,"Pro $1").replace(/-/g," ").replace(/\s+/g," ").trim();return c.length>30?c.slice(0,30)+"…":c}const l=i.replace(/-/g," ").replace(/^\w/,c=>c.toUpperCase()).replace(/\s+/g," ").trim();return l.length>30?l.slice(0,30)+"…":l}function Re(a,i){return a.length<=i?a:`${a.slice(0,i)}…`}function os(a){if(!a)return null;const i=Object.entries(a);return i.length===0?null:i.map(([r,l])=>{const c=typeof l=="string"?l:(()=>{try{return JSON.stringify(l)}catch{return String(l)}})();return`${r}=${Re(c,50)}`}).join(", ")}function cs(a){if(a===void 0)return null;if(typeof a=="string")return Re(a,200);try{return Re(JSON.stringify(a),200)}catch{return Re(String(a),200)}}function ut(a){if(!a||a.length===0)return null;const i=(x,F)=>{const T=x.status==="running",$=x.status==="completed"&&x.isError,g=os(x.args),v=cs(x.result),C=T?g:v?`result: ${v}`:g?`args: ${g}`:null,y=T?"running":$?"error":"completed";return t.jsxs("details",{className:`chat-tool-call${T?" chat-tool-call--running":""}${$?" chat-tool-call--error":""}`,open:T,children:[t.jsxs("summary",{children:[t.jsx("span",{className:"chat-tool-call-status-dot","aria-hidden":"true"}),t.jsx("span",{className:"chat-tool-call-name",children:x.toolName}),C&&t.jsx("span",{className:"chat-tool-call-preview",title:C,children:C}),t.jsx("span",{className:"chat-tool-call-status-text",children:y})]}),t.jsxs("div",{className:"chat-tool-call-content",children:[g&&t.jsxs("div",{className:"chat-tool-call-row",children:[t.jsx("span",{className:"chat-tool-call-label",children:"args"}),t.jsx("span",{className:"chat-tool-call-value",children:g})]}),v&&t.jsxs("div",{className:`chat-tool-call-row${$?" chat-tool-call-row--error":""}`,children:[t.jsx("span",{className:"chat-tool-call-label",children:"result"}),t.jsx("span",{className:"chat-tool-call-value",children:v})]})]})]},`${x.toolName}-${F}`)},r="chat-tool-calls";if(a.length===1)return t.jsxs("div",{className:r,"data-testid":"chat-tool-calls",children:[t.jsxs("div",{className:"chat-tool-calls-header",children:[t.jsx(tt,{size:12,"aria-hidden":"true"}),t.jsx("span",{children:"Tool calls"})]}),i(a[0],0)]});const l=a.filter(x=>x.status==="running").length,c=a.filter(x=>x.status==="completed"&&x.isError).length,N=l>0,j=Array.from(new Set(a.map(x=>x.toolName))),R=j.slice(0,5),k=Math.max(0,j.length-R.length),P=k>0?`${R.join(", ")}, +${k} more`:R.join(", "),I=N?`(${l} running)`:c>0?`(${c} ${c===1?"error":"errors"})`:null;return t.jsx("div",{className:r,"data-testid":"chat-tool-calls",children:t.jsxs("details",{className:"chat-tool-calls-group","data-testid":"chat-tool-calls-group",open:N,children:[t.jsxs("summary",{className:"chat-tool-calls-group-summary",children:[t.jsx(tt,{size:12,"aria-hidden":"true"}),t.jsxs("span",{children:[a.length," tool calls"]}),t.jsx("span",{className:"chat-tool-calls-names",title:P,children:P}),I&&t.jsx("span",{className:"chat-tool-calls-group-status",children:I})]}),a.map((x,F)=>i(x,F))]})})}const ht={pre:({children:a,...i})=>t.jsx("pre",{...i,className:"chat-markdown-pre",children:a}),table:({children:a,...i})=>t.jsx("table",{...i,className:"chat-markdown-table",children:a})},De="__fn_agent__",ds=["image/png","image/jpeg","image/gif","image/webp","text/plain","application/json","text/yaml","text/markdown","text/csv","application/xml","text/x-log"];function it(a){const i=/(^|[\s])\/([^\s]*)$/.exec(a);if(!i)return null;const r=i[1]??"",l=i[2]??"",c=i.index+r.length;return{filter:l,start:c,end:a.length}}function us(a,i){const r=a.slice(0,i),l=/(^|[\s\n])@([\w-]*)$/.exec(r);if(!l)return null;const c=l[2]??"",N=r.length-c.length-1;return{filter:c,start:N,end:i}}function hs({projectId:a,onClose:i,onCreate:r}){const[l,c]=s.useState("agent"),[N,j]=s.useState([]),[R,k]=s.useState(!0),[P,I]=s.useState(""),[x,F]=s.useState([]),[T,$]=s.useState(!0),[g,v]=s.useState(""),[C,y]=s.useState([]),[B,E]=s.useState([]);s.useEffect(()=>{let h=!1;return k(!0),He(void 0,a).then(M=>{h||j(M)}).catch(()=>{h||j([])}).finally(()=>{h||k(!1)}),()=>{h=!0}},[a]),s.useEffect(()=>{$(!0),as().then(h=>{F(h.models),y(h.favoriteProviders),E(h.favoriteModels)}).catch(()=>{F([]),y([]),E([])}).finally(()=>{$(!1)})},[]);const X=s.useCallback(async h=>{const M=C,_=M.includes(h)?M.filter(z=>z!==h):[h,...M];y(_);try{await st({favoriteProviders:_,favoriteModels:B})}catch{y(M)}},[C,B]),W=s.useCallback(async h=>{const M=B,_=M.includes(h)?M.filter(z=>z!==h):[h,...M];E(_);try{await st({favoriteProviders:C,favoriteModels:_})}catch{E(M)}},[B,C]),L=h=>{if(h.preventDefault(),l==="agent"){if(!P)return;r({agentId:P});return}if(!g)return;const M=g.indexOf("/");if(M<=0)return;const w=g.slice(0,M),_=g.slice(M+1);r({agentId:De,modelProvider:w,modelId:_})},Y=l==="agent"?!P:!g;return t.jsx("div",{className:"chat-new-dialog-backdrop",onClick:i,role:"dialog","aria-modal":"true",children:t.jsxs("div",{className:"chat-new-dialog",onClick:h=>h.stopPropagation(),children:[t.jsx("h3",{children:"New Chat"}),t.jsxs("div",{className:"chat-new-dialog-mode-toggle","data-testid":"chat-new-dialog-mode-toggle",children:[t.jsx("button",{type:"button",className:`chat-new-dialog-mode-btn${l==="agent"?" chat-new-dialog-mode-btn--active":""}`,"data-testid":"chat-new-dialog-mode-agent",onClick:()=>{c("agent"),v("")},children:"Agent"}),t.jsx("button",{type:"button",className:`chat-new-dialog-mode-btn${l==="model"?" chat-new-dialog-mode-btn--active":""}`,"data-testid":"chat-new-dialog-mode-model",onClick:()=>{c("model"),I("")},children:"Model"})]}),t.jsxs("form",{onSubmit:L,children:[l==="agent"&&t.jsxs("label",{className:"chat-new-dialog-model-label",children:["Agent",R?t.jsx("div",{className:"chat-new-dialog-loading",children:"Loading agents..."}):N.length===0?t.jsx("div",{className:"chat-new-dialog-empty",children:"No agents available"}):t.jsx("div",{className:"chat-new-dialog-agent-list",children:N.map(h=>t.jsxs("button",{type:"button",className:`chat-new-dialog-agent-item${P===h.id?" chat-new-dialog-agent-item--selected":""}`,onClick:()=>I(h.id),"data-testid":`agent-option-${h.id}`,children:[t.jsx(Pe,{size:16}),t.jsx("span",{className:"chat-new-dialog-agent-name",children:h.name}),t.jsx("span",{className:"chat-new-dialog-agent-role",children:h.role})]},h.id))})]}),l==="model"&&t.jsx("div",{className:"chat-new-dialog-model-dropdown","data-testid":"chat-new-dialog-model-section",children:T?t.jsx("div",{className:"chat-new-dialog-loading",children:"Loading models..."}):t.jsx(is,{models:x,value:g,onChange:v,label:"Model",placeholder:"Select a model",favoriteProviders:C,onToggleFavorite:X,favoriteModels:B,onToggleModelFavorite:W})}),t.jsxs("div",{className:"chat-new-dialog-actions",children:[t.jsx("button",{type:"button",className:"btn btn-sm",onClick:i,children:"Cancel"}),t.jsx("button",{type:"submit",className:"btn btn-sm btn-primary",disabled:Y,children:"Create"})]})]})]})})}const ms=s.memo(function({message:i,forcePlain:r,agentName:l,showAssistantModelTag:c,activeModelTag:N,activeSessionId:j,mentionAgentsByName:R,onToggleRender:k}){const P=i.role==="assistant",I=s.useMemo(()=>{if(P)return null;const T=i.content,$=/@([\w-]+)/g,g=[];let v=0,C=$.exec(T);for(;C;){const[y,B=""]=C,E=C.index;E>v&&g.push(T.slice(v,E));const X=B.replace(/_/g," ").toLowerCase(),W=R.get(X);W?g.push(t.jsxs("span",{className:"chat-mention-chip",children:["@",W.name.replace(/\s+/g,"_")]},`${W.id}-${E}`)):g.push(y),v=E+y.length,C=$.exec(T)}return v<T.length&&g.push(T.slice(v)),g.length===0?T:g},[P,i.content,R]),x=s.useMemo(()=>{const T=i.attachments;if(!T||T.length===0||!j)return null;const $=`/api/chat/sessions/${encodeURIComponent(j)}/attachments/`;return t.jsx("div",{className:"chat-message-attachments",children:T.map(g=>{const v=g.mimeType.startsWith("image/"),C=g.id||g.filename,y=`${$}${encodeURIComponent(g.filename)}`;return v?t.jsx("a",{className:"chat-message-attachment-link","data-testid":"chat-message-attachment",href:y,target:"_blank",rel:"noopener noreferrer",children:t.jsx("img",{className:"chat-message-attachment",src:y,alt:g.originalName})},C):t.jsxs("a",{className:"chat-message-attachment-file","data-testid":"chat-message-attachment",href:y,target:"_blank",rel:"noopener noreferrer",children:[t.jsx(ns,{size:14}),t.jsx("span",{children:g.originalName})]},C)})})},[i.attachments,j]),F=s.useMemo(()=>P?r?t.jsx("div",{className:"chat-message-content chat-message-content--plain",children:i.content}):t.jsx("div",{className:"chat-message-content chat-message-content--markdown",children:t.jsx(rt,{remarkPlugins:[lt],components:ht,children:i.content})}):null,[P,r,i.content]);return t.jsxs("div",{className:`chat-message chat-message--${i.role}`,"data-testid":`chat-message-${i.id}`,children:[P&&t.jsxs("div",{className:"chat-message-avatar",children:[t.jsx(Pe,{size:14}),t.jsx("span",{children:l}),c&&N&&t.jsx("span",{className:"chat-model-tag",children:N}),t.jsx("button",{type:"button",className:`chat-message-render-toggle${r?" chat-message-render-toggle--plain":""}`,"data-testid":"chat-message-render-toggle","aria-label":r?"Show rendered markdown":"Show plain text",onClick:()=>k(i.id),children:r?t.jsx(ot,{size:14}):t.jsx(ct,{size:14})})]}),P?F:t.jsx("div",{className:"chat-message-content",children:I}),ut(i.toolCalls),i.thinkingOutput&&t.jsxs("details",{className:"chat-message-thinking",children:[t.jsx("summary",{children:"Thinking"}),t.jsx("pre",{className:"chat-message-thinking-content",children:i.thinkingOutput})]}),x,t.jsx("div",{className:"chat-message-time",children:dt(i.createdAt)})]})});function ws({projectId:a,addToast:i}){const{activeSession:r,sessionsLoading:l,messages:c,messagesLoading:N,isStreaming:j,streamingText:R,streamingThinking:k,streamingToolCalls:P,selectSession:I,createSession:x,archiveSession:F,deleteSession:T,sendMessage:$,stopStreaming:g,pendingMessage:v,clearPendingMessage:C,searchQuery:y,setSearchQuery:B,filteredSessions:E}=ls(a),[X,W]=s.useState(!1),[L,Y]=s.useState(""),[h,M]=s.useState(null),[w,_]=s.useState(null),[z,te]=s.useState(!0),[he,xe]=s.useState(new Map),[ve,we]=s.useState([]),[me,ke]=s.useState(!0),[K,se]=s.useState(!1),[oe,ne]=s.useState(""),[ae,fe]=s.useState(0),[Se,ce]=s.useState(""),[Z,de]=s.useState(!1),[ye,ue]=s.useState(0),[Ne,d]=s.useState(-1),[f,p]=s.useState(()=>new Set),[S,q]=s.useState([]),[ge,O]=s.useState(!1),[,H]=s.useState(!1),[ie,G]=s.useState({top:0,left:0}),u=Bt({projectId:a}),A=s.useCallback(e=>{if(!e||!u.mentionActive)return;const n=e.getBoundingClientRect();G({top:n.top-260,left:n.left+8})},[u.mentionActive]),V=s.useRef(null),m=s.useRef(null),D=s.useRef(null),b=s.useRef(null),ee=s.useRef(!1),Me=s.useRef(!1),Ve=s.useRef(null),Be=s.useRef([]),je=s.useRef(0),re=qt()==="mobile",{keyboardOverlap:Ee,viewportHeight:qe,viewportOffsetTop:mt,keyboardOpen:Fe}=Gt({enabled:re&&!!r}),ft=Fe?{"--keyboard-overlap":`${Ee}px`,"--vv-offset-top":`${mt}px`,...qe!==null?{"--vv-height":`${qe}px`}:{}}:{},Q=s.useMemo(()=>{const e=oe.trim().toLowerCase();return(e?ve.filter(o=>o.name.toLowerCase().includes(e)):ve).slice(0,10)},[ve,oe]),be=s.useMemo(()=>Array.from(he.values()),[he]),pe=s.useMemo(()=>{const e=Se.trim().toLowerCase();return e?be.filter(n=>n.name.toLowerCase().includes(e)):be},[be,Se]),gt=s.useMemo(()=>{const e=new Map;for(const n of be)e.set(n.name.toLowerCase(),n);return e},[be]);s.useEffect(()=>{fe(0)},[Q]),s.useEffect(()=>{ue(0)},[Se,Z]),s.useEffect(()=>()=>{m.current!==null&&window.clearTimeout(m.current)},[]),s.useEffect(()=>{const e=D.current;e&&(e.scrollTop=e.scrollHeight)},[c,R]),s.useEffect(()=>{if(Ee<=0)return;const e=D.current;e&&(e.scrollTop=e.scrollHeight)},[Ee]),s.useEffect(()=>{if(!re||!Fe)return;const e=document.documentElement,n=document.body,o={htmlOverflow:e.style.overflow,bodyOverflow:n.style.overflow};return e.style.overflow="hidden",n.style.overflow="hidden",()=>{e.style.overflow=o.htmlOverflow,n.style.overflow=o.bodyOverflow}},[re,Fe]),s.useEffect(()=>{const e=()=>M(null);if(h)return document.addEventListener("click",e),()=>document.removeEventListener("click",e)},[h]),s.useEffect(()=>{let e=!1;const n=a;return He(void 0,a).then(o=>{if(e||n!==a)return;const U=new Map;for(const J of o)U.set(J.id,J);xe(U)}).catch(()=>{}),()=>{e=!0}},[a]),s.useEffect(()=>{let e=!1;return ke(!0),Wt(a).then(n=>{e||we(n)}).catch(()=>{e||we([])}).finally(()=>{e||ke(!1)}),()=>{e=!0}},[a]),s.useEffect(()=>{Be.current=S},[S]),s.useEffect(()=>()=>{for(const e of Be.current)e.previewUrl&&URL.revokeObjectURL(e.previewUrl)},[]);const Te=s.useCallback(e=>{if(!e||e.length===0)return;const n=[];for(const o of Array.from(e)){if(!ds.includes(o.type))continue;const U=o.type.startsWith("image/");n.push({file:o,previewUrl:U?URL.createObjectURL(o):""})}n.length>0&&q(o=>[...o,...n])},[]),pt=s.useCallback(e=>{q(n=>{const o=n[e];return o?.previewUrl&&URL.revokeObjectURL(o.previewUrl),n.filter((U,J)=>J!==e)})},[]),xt=s.useCallback(e=>{const n=e.clipboardData?.files;if(!n||n.length===0)return;const o=Array.from(n).filter(U=>U.type.startsWith("image/"));o.length!==0&&Te(o)},[Te]),vt=s.useCallback(async e=>{try{await x(e),W(!1),re&&te(!1)}catch{i("Failed to create chat session","error")}},[x,i,re]),Le=s.useCallback(()=>{Y(""),se(!1),ne(""),de(!1),ce(""),d(-1),q(e=>{for(const n of e)n.previewUrl&&URL.revokeObjectURL(n.previewUrl);return[]})},[]),Ce=s.useCallback(()=>{const e=L.trim(),n=S.map(o=>o.file);if(!(!e&&n.length===0||!r)){if(e==="/clear"){Le(),g(),C(),x({agentId:r.agentId,modelProvider:r.modelProvider??void 0,modelId:r.modelId??void 0}).catch(()=>{i("Failed to clear conversation","error")});return}Le(),$(e,n)}},[L,S,r,Le,g,C,x,i,$]),Ae=s.useCallback(()=>{if(typeof window>"u"||window.innerWidth>768)return;const e=b.current;if(!e||e.disabled)return;const n=window.scrollX,o=window.scrollY;e.focus({preventScroll:!0}),window.requestAnimationFrame(()=>{(window.scrollX!==n||window.scrollY!==o)&&window.scrollTo(n,o)})},[]),Ge=s.useCallback(()=>{typeof window>"u"||window.innerWidth>768||(ee.current=!0)},[]),_e=s.useCallback(e=>{Y(n=>{const o=it(n);if(!o)return n;const U=`/skill:${e.name} `,J=n.slice(0,o.start)+U+n.slice(o.end);return window.requestAnimationFrame(()=>{b.current&&(b.current.style.height="auto",b.current.style.height=`${Math.min(b.current.scrollHeight,120)}px`,b.current.focus())}),J}),se(!1),ne(""),fe(0)},[]),Ie=s.useCallback(e=>{const n=b.current;if(!n||Ne<0)return;const o=n.selectionStart??je.current,U=n.selectionEnd??o,J=Math.max(o,U),Ye=Math.min(Ne,J),Qe=`${`@${e.name.replace(/\s+/g,"_")}`} `,Dt=L.slice(0,Ye)+Qe+L.slice(J),Xe=Ye+Qe.length;Y(Dt),de(!1),ce(""),ue(0),d(-1),window.requestAnimationFrame(()=>{b.current&&(b.current.style.height="auto",b.current.style.height=`${Math.min(b.current.scrollHeight,120)}px`,b.current.focus(),b.current.setSelectionRange(Xe,Xe))})},[Ne,L]),wt=s.useCallback(e=>{if(je.current=e.currentTarget.selectionStart??je.current,u.mentionActive&&u.files.length>0){if(u.handleKeyDown(e,L),e.key==="Enter"||e.key==="Tab"){const n=u.files[u.selectedIndex];if(n){const o=u.selectFile(n,L);Y(o),u.dismissMention(),H(!1)}}return}if(Z&&e.key==="ArrowDown"){e.preventDefault(),pe.length>0&&ue(n=>(n+1)%pe.length);return}if(Z&&e.key==="ArrowUp"){e.preventDefault(),pe.length>0&&ue(n=>n===0?pe.length-1:n-1);return}if(Z&&e.key==="Enter"){e.preventDefault();const n=pe[ye]??pe[0];n&&Ie(n);return}if(Z&&e.key==="Escape"){e.preventDefault(),de(!1),ce(""),d(-1);return}if(K&&e.key==="ArrowDown"){e.preventDefault(),Q.length>0&&fe(n=>(n+1)%Q.length);return}if(K&&e.key==="ArrowUp"){e.preventDefault(),Q.length>0&&fe(n=>n===0?Q.length-1:n-1);return}if(K&&(e.key==="Enter"||e.key==="Tab")&&Q.length>0){e.preventDefault();const n=Q[ae]??Q[0];n&&_e(n);return}if(K&&e.key==="Escape"){e.preventDefault(),se(!1);return}e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Ce())},[Z,pe,ye,Ie,K,Q,ae,_e,Ce,u,L]),$e=s.useCallback((e,n)=>{const o=us(e,n);if(o){de(!0),ce(o.filter),d(o.start);return}de(!1),ce(""),d(-1)},[]),St=s.useCallback(e=>{const n=e.target,o=n.value,U=n.selectionStart??o.length;je.current=U,Y(o);const J=it(o);J?(se(!0),ne(J.filter)):(se(!1),ne("")),$e(o,U),u.detectMention(o,U),H(u.mentionActive),u.mentionActive&&A(n),n.style.height="auto",n.style.height=`${Math.min(n.scrollHeight,120)}px`},[$e]),Oe=s.useCallback(e=>{const n=e.currentTarget,o=n.selectionStart??n.value.length;je.current=o,$e(n.value,o),u.detectMention(n.value,o),H(u.mentionActive),u.mentionActive&&A(n)},[$e,u,A]),bt=s.useCallback(e=>{e.key!=="Escape"&&Oe(e)},[Oe]),kt=s.useCallback(()=>{if(ee.current){window.requestAnimationFrame(()=>{Ae()});return}m.current!==null&&window.clearTimeout(m.current),m.current=window.setTimeout(()=>{se(!1),de(!1),ce(""),d(-1),H(!1),u.dismissMention(),m.current=null},120)},[u,Ae]),yt=s.useCallback(()=>{m.current!==null&&(window.clearTimeout(m.current),m.current=null)},[]),Nt=s.useCallback(async e=>{M(null);try{await F(e),i("Conversation archived","success")}catch{i("Failed to archive conversation","error")}},[F,i]),jt=s.useCallback(async e=>{_(null),M(null);try{await T(e),i("Conversation deleted","success")}catch{i("Failed to delete conversation","error")}},[T,i]),Ct=s.useCallback(e=>{I(e),re&&te(!1)},[I,re]),Mt=s.useCallback(()=>{I(""),te(!0)},[I]),Tt=()=>t.jsxs("div",{className:"chat-empty-state",children:[t.jsx(ss,{size:48,strokeWidth:1.5}),t.jsx("h2",{children:"Start a new conversation"}),t.jsxs("button",{className:"btn btn-primary",onClick:()=>W(!0),children:[t.jsx(et,{size:16}),"New Chat"]})]}),le=at(r?.modelProvider,r?.modelId),We=r?.agentId===De?le??"Fusion":r?.title||he.get(r?.agentId??"")?.name||r?.agentId||"Chat",At=!!(le&&le!==We),Ue=he.get(r?.agentId??"")?.name||(r?.agentId===De?le??"Fusion":r?.agentId?.slice(0,30)??"Fusion"),Ke=!!(le&&le!==Ue),$t=v.length>50?`${v.slice(0,50)}…`:v,Je=s.useCallback(e=>{p(n=>{const o=new Set(n);return o.has(e)?o.delete(e):o.add(e),o})},[]),Rt=s.useCallback((e,n=!1)=>n?t.jsx("div",{className:"chat-message-content chat-message-content--plain",children:e}):t.jsx("div",{className:"chat-message-content chat-message-content--markdown",children:t.jsx(rt,{remarkPlugins:[lt],components:ht,children:e})}),[]);return t.jsxs("div",{className:"chat-view",children:[t.jsxs("div",{className:`chat-sidebar${z?"":" chat-sidebar--hidden"}`,children:[t.jsx("div",{className:"chat-sidebar-search",children:t.jsxs("div",{className:"chat-sidebar-search-wrapper",children:[t.jsx(Kt,{size:14,className:"chat-sidebar-search-icon"}),t.jsx("input",{type:"text",className:"chat-sidebar-search",placeholder:"Search conversations...",value:y,onChange:e=>B(e.target.value),"data-testid":"chat-search-input"})]})}),t.jsx("div",{className:"chat-session-list chat-sidebar-list",children:l?t.jsx("div",{style:{padding:"12px",color:"var(--text-secondary)",fontSize:"13px"},children:"Loading..."}):E.length===0?t.jsx("div",{style:{padding:"12px",color:"var(--text-secondary)",fontSize:"13px"},children:"No conversations yet"}):E.map(e=>t.jsxs("div",{className:`chat-session-item${r?.id===e.id?" chat-session-item--active":""}`,onClick:()=>Ct(e.id),onContextMenu:n=>{n.preventDefault(),M({sessionId:e.id,x:n.clientX,y:n.clientY})},"data-testid":`chat-session-${e.id}`,children:[t.jsx("button",{className:"chat-session-delete-btn",onClick:n=>{n.stopPropagation(),_(e.id)},"data-testid":"chat-session-delete-btn","aria-label":"Delete conversation",children:t.jsx(Ze,{size:14})}),t.jsx("div",{className:"chat-session-title",children:e.title||"Untitled"}),t.jsx("div",{className:"chat-session-preview",children:e.lastMessagePreview||"No messages"}),t.jsxs("div",{className:"chat-session-meta",children:[t.jsx("span",{children:he.get(e.agentId)?.name||(e.agentId===De?at(e.modelProvider,e.modelId)??"Fusion":e.agentId.slice(0,30))}),t.jsx("span",{children:e.updatedAt?dt(e.updatedAt):""})]})]},e.id))}),t.jsx("div",{className:"chat-sidebar-footer",children:t.jsxs("button",{className:"btn btn-sm btn-primary chat-sidebar-footer-btn",onClick:()=>W(!0),"data-testid":"chat-new-btn",children:[t.jsx(et,{size:14}),"New Chat"]})})]}),h&&t.jsxs("div",{className:"chat-session-context-menu",style:{top:h.y,left:h.x},onClick:e=>e.stopPropagation(),children:[t.jsxs("button",{onClick:()=>Nt(h.sessionId),"data-testid":"chat-context-archive",children:[t.jsx(Jt,{size:14}),"Archive"]}),t.jsxs("button",{onClick:()=>{M(null),_(h.sessionId)},"data-testid":"chat-context-delete",children:[t.jsx(Ze,{size:14}),"Delete"]})]}),w&&t.jsx("div",{className:"chat-new-dialog-backdrop",onClick:()=>_(null),children:t.jsxs("div",{className:"chat-new-dialog",onClick:e=>e.stopPropagation(),children:[t.jsx("h3",{children:"Delete Conversation?"}),t.jsx("p",{style:{fontSize:"14px",color:"var(--text-secondary)",marginBottom:"16px"},children:"This action cannot be undone. All messages in this conversation will be permanently deleted."}),t.jsxs("div",{className:"chat-new-dialog-actions",children:[t.jsx("button",{className:"btn btn-sm",onClick:()=>_(null),children:"Cancel"}),t.jsx("button",{className:"btn btn-sm btn-danger",onClick:()=>void jt(w),children:"Delete"})]})]})}),t.jsxs("div",{className:"chat-thread",style:ft,children:[(r||!re)&&t.jsxs("div",{className:"chat-thread-header",children:[re&&r&&t.jsx("button",{className:"btn-icon",onClick:Mt,"data-testid":"chat-back-btn",children:t.jsx(Yt,{size:16})}),t.jsx(Pe,{size:16}),t.jsx("span",{className:"chat-thread-header-title",children:We}),At&&t.jsx("span",{className:"chat-model-tag",children:le})]}),t.jsxs("div",{className:"chat-messages",ref:D,children:[N?t.jsx("div",{style:{color:"var(--text-secondary)",fontSize:"13px"},children:"Loading messages..."}):c.length===0&&!r?Tt():c.length===0&&r?t.jsx("div",{style:{color:"var(--text-secondary)",fontSize:"13px"},children:"No messages yet. Start the conversation!"}):t.jsxs(t.Fragment,{children:[c.map(e=>t.jsx(ms,{message:e,forcePlain:f.has(e.id),agentName:Ue,showAssistantModelTag:Ke,activeModelTag:le,activeSessionId:r?.id??null,mentionAgentsByName:gt,onToggleRender:Je},e.id)),j&&t.jsxs("div",{className:"chat-message chat-message--assistant chat-message--streaming",children:[t.jsxs("div",{className:"chat-message-avatar",children:[t.jsx(Pe,{size:14}),t.jsx("span",{children:Ue}),Ke&&t.jsx("span",{className:"chat-model-tag",children:le}),t.jsx("button",{type:"button",className:`chat-message-render-toggle${f.has("__streaming__")?" chat-message-render-toggle--plain":""}`,"data-testid":"chat-message-render-toggle","aria-label":f.has("__streaming__")?"Show rendered markdown":"Show plain text",onClick:()=>Je("__streaming__"),children:f.has("__streaming__")?t.jsx(ot,{size:14}):t.jsx(ct,{size:14})})]}),R?Rt(R,f.has("__streaming__")):t.jsx("div",{className:"chat-message-content chat-message-content--waiting",children:k?"Thinking…":"Connecting…"}),ut(P),k&&t.jsxs("details",{className:"chat-message-thinking",children:[t.jsx("summary",{children:"Thinking"}),t.jsx("pre",{className:"chat-message-thinking-content",children:k})]}),t.jsxs("div",{className:"chat-typing-indicator",children:[t.jsx("span",{}),t.jsx("span",{}),t.jsx("span",{})]})]})]}),t.jsx("div",{ref:V})]}),r&&t.jsxs("div",{className:"chat-input-area",children:[t.jsx("input",{ref:Ve,type:"file",accept:"image/*,.txt,.json,.yaml,.yml,.log,.csv,.xml,.md",multiple:!0,style:{display:"none"},onChange:e=>{Te(e.target.files),e.target.value=""}}),K&&t.jsx("div",{className:"chat-skill-menu","data-testid":"chat-skill-menu",role:"listbox","aria-label":"Skill suggestions",children:me?t.jsx("div",{className:"chat-skill-menu-empty",children:"Loading skills…"}):Q.length===0?t.jsx("div",{className:"chat-skill-menu-empty",children:oe?"No skills found":"No skills available"}):Q.map((e,n)=>t.jsxs("button",{type:"button",role:"option","aria-selected":n===ae,className:`chat-skill-menu-item${n===ae?" chat-skill-menu-item--highlighted":""}`,onMouseDown:o=>o.preventDefault(),onMouseEnter:()=>fe(n),onClick:()=>_e(e),children:[t.jsx("span",{className:"chat-skill-menu-item-name",children:e.name}),t.jsx("span",{className:"chat-skill-menu-item-description",title:e.relativePath,children:e.relativePath})]},e.id))}),S.length>0&&t.jsx("div",{className:"chat-attachment-previews","data-testid":"chat-attachment-previews",children:S.map((e,n)=>t.jsxs("div",{className:"chat-attachment-preview","data-testid":`chat-attachment-preview-${n}`,children:[e.previewUrl?t.jsx("img",{src:e.previewUrl,alt:e.file.name}):t.jsx("span",{className:"chat-attachment-preview-name",children:e.file.name}),t.jsx("button",{type:"button",className:"chat-attachment-remove",onClick:()=>pt(n),"data-testid":`chat-attachment-remove-${n}`,"aria-label":`Remove ${e.file.name}`,children:"×"})]},e.previewUrl||`${e.file.name}-${n}`))}),t.jsxs("div",{className:"chat-input-row",children:[t.jsx("button",{type:"button",className:"btn-icon chat-attach-btn","data-testid":"chat-attach-btn","aria-label":"Attach files",onClick:()=>Ve.current?.click(),children:t.jsx(Qt,{size:16})}),t.jsxs("div",{className:`chat-input-wrapper${ge?" chat-input-wrapper--dragover":""}`,onDragOver:e=>{e.preventDefault(),O(!0)},onDragLeave:()=>O(!1),onDrop:e=>{e.preventDefault(),O(!1),Te(e.dataTransfer.files)},children:[t.jsx("textarea",{ref:b,className:"chat-input-textarea",placeholder:"Type a message...",value:L,onChange:St,onKeyDown:wt,onKeyUp:bt,onClick:Oe,onBlur:kt,onFocus:yt,onPaste:xt,onTouchStart:e=>{typeof window>"u"||window.innerWidth>768||document.activeElement!==e.currentTarget&&(e.preventDefault(),e.currentTarget.focus({preventScroll:!0}))},rows:1,"data-testid":"chat-input"}),t.jsx(Xt,{agents:be,filter:Se,highlightedIndex:ye,visible:Z,onSelect:Ie,position:"below"}),t.jsx(Zt,{visible:u.mentionActive&&!Z,position:ie,files:u.files,selectedIndex:u.selectedIndex,onSelect:e=>{const n=u.selectFile(e,L);Y(n),u.dismissMention(),H(!1),b.current?.focus()},loading:u.loading}),v&&t.jsxs("div",{className:"chat-pending-message","data-testid":"chat-pending-indicator",children:[t.jsx("span",{children:`Queued: ${$t}`}),t.jsx("button",{type:"button",className:"chat-pending-message-dismiss","aria-label":"Dismiss queued message","data-testid":"chat-pending-dismiss",onClick:C,children:"×"})]})]}),j?t.jsx("button",{className:"chat-input-stop",onClick:g,"aria-label":"Stop generation","data-testid":"chat-stop-btn",children:t.jsx(es,{size:14})}):t.jsx("button",{type:"button",className:"chat-input-send",onPointerDown:e=>{typeof window>"u"||window.innerWidth>768||(e.preventDefault(),e.pointerType&&e.pointerType!=="mouse"&&(Me.current=!0,Ge(),Ae(),Ce(),window.setTimeout(()=>{ee.current=!1},1500)))},onTouchStart:e=>{typeof window>"u"||window.innerWidth>768||(e.preventDefault(),Me.current=!0,Ge(),Ae(),Ce(),window.setTimeout(()=>{ee.current=!1},1500))},onMouseDown:e=>{typeof window>"u"||window.innerWidth>768||e.preventDefault()},onClick:()=>{if(Me.current){Me.current=!1;return}Ce()},disabled:!L.trim()&&S.length===0,"data-testid":"chat-send-btn",children:t.jsx(ts,{size:16})})]})]})]}),X&&t.jsx(hs,{projectId:a,onClose:()=>W(!1),onCreate:vt})]})}export{ws as ChatView};
@@ -1 +0,0 @@
1
- import{r as u,j as s}from"./vendor-react-K0fH_qHe.js";import{w as ee,dP as se,s as ne,X as k,an as te,J as O,dQ as A,V,R as ie,dR as ae,aF as le,ch as re,dS as ce,dT as de,dU as ue,dV as oe,dW as _,ce as ge}from"./index-Bc8nfKeH.js";import{D as me}from"./DirectoryPicker-rew1y6qO.js";import"./vendor-xterm-DzcZoU0P.js";import"./folder-open-nYPrL1W3.js";const H=[{id:"fusion-plugin-hermes-runtime",name:"Hermes Runtime",path:"./plugins/fusion-plugin-hermes-runtime",experimental:!0},{id:"fusion-plugin-paperclip-runtime",name:"Paperclip Runtime",path:"./plugins/fusion-plugin-paperclip-runtime"},{id:"fusion-plugin-openclaw-runtime",name:"OpenClaw Runtime",path:"./plugins/fusion-plugin-openclaw-runtime",experimental:!0}],j={started:"var(--color-success)",loaded:"var(--color-warning)",error:"var(--color-error)",stopped:"var(--color-muted)",installed:"var(--color-info)"};function fe({addToast:l,projectId:c}){const[N,x]=u.useState([]),[S,w]=u.useState(!0),[J,f]=u.useState(!1),[b,v]=u.useState(""),[C,P]=u.useState(!1),[h,I]=u.useState(null),[i,y]=u.useState(null),[a,p]=u.useState({}),[M,R]=u.useState(!1),[$,E]=u.useState(null),{confirm:G}=ee(),o=u.useCallback(async()=>{try{w(!0);const e=await se(c);x(e)}catch(e){l(`Failed to load plugins: ${e instanceof Error?e.message:String(e)}`,"error")}finally{w(!1)}},[c,l]);u.useEffect(()=>{o()},[o]);const K=u.useRef([]);K.current=N,u.useEffect(()=>{const e=c?`?projectId=${encodeURIComponent(c)}`:"",n=g=>{try{const t=JSON.parse(g.data);if(c&&t.projectId&&t.projectId!==c)return;switch(t.transition){case"installing":case"enabled":case"disabled":case"settings-updated":x(d=>{const m=d.findIndex(r=>r.id===t.pluginId);if(m>=0){const r=[...d];return r[m]={...r[m],enabled:t.enabled,state:t.state,settings:t.settings,error:t.error},r}else return o(),d});break;case"uninstalled":x(d=>d.filter(m=>m.id!==t.pluginId));break;case"error":x(d=>{const m=d.findIndex(r=>r.id===t.pluginId);if(m>=0){const r=[...d];return r[m]={...r[m],state:t.state,error:t.error},r}return d});break}}catch{}};return ne(`/api/events${e}`,{events:{"plugin:lifecycle":n},onReconnect:()=>{o()}})},[c,o]);const z=async()=>{if(!b.trim()){l("Please enter a plugin path","error");return}try{P(!0),await _({path:b},c),l("Plugin installed successfully","success"),f(!1),v(""),await o()}catch(e){l(`Failed to install plugin: ${e instanceof Error?e.message:String(e)}`,"error")}finally{P(!1)}},Q=async e=>{try{E(e.id),await _({path:e.path},c),l(`${e.name} installed successfully`,"success"),await o()}catch(n){l(`Failed to install ${e.name}: ${n instanceof Error?n.message:String(n)}`,"error")}finally{E(null)}},F=async e=>{try{await ue(e.id,c),l(`${e.name} enabled`,"success"),await o()}catch(n){l(`Failed to enable plugin: ${n instanceof Error?n.message:String(n)}`,"error")}},U=async e=>{try{await de(e.id,c),l(`${e.name} disabled`,"success"),await o()}catch(n){l(`Failed to disable plugin: ${n instanceof Error?n.message:String(n)}`,"error")}},B=async e=>{try{I(e.id),await ce(e.id,c),l(`${e.name} reloaded`,"success"),await o()}catch(n){l(`Failed to reload plugin: ${n instanceof Error?n.message:String(n)}`,"error")}finally{I(null)}},L=async e=>{if(await G({title:"Uninstall Plugin",message:`Are you sure you want to uninstall "${e.name}"?`,danger:!0}))try{await oe(e.id,c),l(`${e.name} uninstalled`,"success"),await o(),y(null)}catch(g){l(`Failed to uninstall plugin: ${g instanceof Error?g.message:String(g)}`,"error")}},W=async e=>{y(e);try{R(!0);const n=await ge(e.id,c);p(n)}catch{p({})}finally{R(!1)}},X=async()=>{if(i)try{await re(i.id,a,c),l("Settings saved","success")}catch(e){l(`Failed to save settings: ${e instanceof Error?e.message:String(e)}`,"error")}};if(i)return s.jsxs("div",{className:"plugin-manager-detail","data-testid":"plugin-manager-detail",children:[s.jsxs("div",{className:"plugin-manager-detail-header",children:[s.jsx("button",{className:"btn-icon",onClick:()=>y(null),"aria-label":"Back to plugin list",children:s.jsx(k,{size:16})}),s.jsxs("div",{className:"plugin-detail-title",children:[s.jsx("h4",{className:"plugin-detail-name",children:i.name}),s.jsx("span",{className:"plugin-state-badge",style:{color:j[i.state]||j.installed},children:i.state})]})]}),s.jsxs("div",{className:"plugin-detail-content",children:[s.jsxs("div",{className:"plugin-detail-card",children:[i.description&&s.jsx("p",{className:"plugin-description",children:i.description}),i.author&&s.jsxs("p",{className:"plugin-detail-meta-row",children:[s.jsx("span",{className:"text-muted",children:"Author:"}),i.author]}),i.homepage&&s.jsxs("p",{className:"plugin-detail-meta-row plugin-homepage",children:[s.jsx("span",{className:"text-muted",children:"Homepage:"}),s.jsxs("a",{href:i.homepage,target:"_blank",rel:"noopener noreferrer",children:[i.homepage,s.jsx(te,{size:12})]})]}),s.jsxs("p",{className:"plugin-detail-meta-row",children:[s.jsx("span",{className:"text-muted",children:"Version:"}),i.version]})]}),s.jsxs("div",{className:"plugin-detail-card",children:[s.jsx("h5",{className:"plugin-detail-section-heading",children:"Settings"}),M?s.jsx("p",{className:"text-muted",children:"Loading..."}):i.settingsSchema&&Object.keys(i.settingsSchema).length>0?s.jsxs("div",{className:"plugin-settings-form",children:[Object.entries(i.settingsSchema).map(([e,n])=>{const g=`setting-${e}-help`;return s.jsxs("div",{className:"form-group",children:[s.jsxs("label",{htmlFor:`setting-${e}`,children:[n.label||e,n.required&&" *"]}),n.type==="string"&&!n.multiline&&s.jsx("input",{type:"text",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?g:void 0}),n.type==="string"&&n.multiline&&s.jsx("textarea",{id:`setting-${e}`,rows:4,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?g:void 0}),n.type==="password"&&s.jsx("input",{type:"password",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?g:void 0}),n.type==="number"&&s.jsx("input",{type:"number",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:Number(t.target.value)}),"aria-describedby":n.description&&!n.required?g:void 0}),n.type==="boolean"&&s.jsxs("label",{className:"checkbox-label",children:[s.jsx("input",{type:"checkbox",checked:a[e]??!1,onChange:t=>p({...a,[e]:t.target.checked})}),n.description]}),n.type==="enum"&&s.jsxs("select",{id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),"aria-describedby":n.description&&!n.required?g:void 0,children:[s.jsx("option",{value:"",children:"Select..."}),n.enumValues?.map(t=>s.jsx("option",{value:t,children:t},t))]}),n.type==="array"&&s.jsxs("div",{className:"plugin-settings-array",children:[a[e]?.map((t,d)=>s.jsxs("div",{className:"plugin-settings-array-item",children:[s.jsx("input",{type:n.itemType==="number"?"number":"text",value:t??"",onChange:m=>{const r=m.target.value,D=[...a[e]||[]];D[d]=n.itemType==="number"?Number(r):r,p({...a,[e]:D})}}),s.jsx("button",{className:"btn-icon",onClick:()=>{const r=[...a[e]||[]];r.splice(d,1),p({...a,[e]:r})},"aria-label":"Remove item",children:s.jsx(k,{size:14})})]},d)),s.jsxs("button",{className:"btn btn-secondary",onClick:()=>{const t=a[e]||[],d=n.itemType==="number"?0:"";p({...a,[e]:[...t,d]})},children:[s.jsx(O,{size:14})," Add Item"]})]}),n.description&&!n.required&&!n.multiline&&s.jsx("span",{id:g,className:"form-help",children:n.description})]},e)}),s.jsx("button",{className:"btn btn-primary",onClick:X,children:"Save Settings"})]}):s.jsx("p",{className:"text-muted",children:"No configurable settings."})]}),s.jsxs("div",{className:"plugin-detail-actions",children:[i.state==="started"&&s.jsxs("button",{className:"btn btn-secondary",onClick:()=>B(i),disabled:h===i.id,children:[s.jsx(A,{size:14,className:h===i.id?"spin":""}),h===i.id?"Reloading...":"Reload"]}),i.enabled?s.jsx("button",{className:"btn btn-secondary",onClick:()=>U(i),children:"Disable"}):s.jsx("button",{className:"btn btn-primary",onClick:()=>F(i),children:"Enable"}),s.jsxs("button",{className:"btn btn-danger",onClick:()=>L(i),children:[s.jsx(V,{size:14})," Uninstall"]})]})]})]});const Y=new Set(N.map(e=>e.id)),Z=new Set(H.map(e=>e.id)),q=N.filter(e=>!Z.has(e.id)),T=()=>s.jsxs("section",{className:"plugin-bundled-runtime-section","aria-label":"Bundled Runtime Plugins",children:[s.jsxs("div",{className:"plugin-bundled-runtime-header",children:[s.jsx("h4",{className:"plugin-bundled-runtime-heading",children:"Bundled Runtime Plugins"}),s.jsx("p",{className:"plugin-bundled-runtime-description",children:"Install Fusion's bundled runtimes directly from this screen."})]}),s.jsx("div",{className:"plugin-bundled-runtime-list","aria-label":"Bundled runtime plugin recommendations",children:H.map(e=>{const n=Y.has(e.id);return s.jsxs("div",{className:"plugin-bundled-runtime-item",children:[s.jsxs("div",{className:"plugin-bundled-runtime-meta",children:[s.jsx("span",{className:"plugin-bundled-runtime-name",children:e.name}),e.experimental&&s.jsx("span",{className:"plugin-bundled-runtime-badge",children:"Experimental"}),s.jsx("span",{className:`plugin-bundled-runtime-status ${n?"plugin-bundled-runtime-status--installed":"plugin-bundled-runtime-status--available"}`,children:n?"Installed":"Not installed"})]}),s.jsx("button",{className:`btn ${n?"btn-secondary":"btn-primary"} btn-sm`,onClick:()=>Q(e),disabled:n||$===e.id,children:n?"Installed":$===e.id?"Installing...":`Install ${e.name}`})]},e.id)})})]});return s.jsxs("div",{className:"plugin-manager","data-testid":"plugin-manager",children:[s.jsxs("div",{className:"plugin-manager-header",children:[s.jsx("span",{className:"plugin-manager-header-title",children:"Installed Plugins"}),s.jsxs("div",{className:"plugin-manager-actions",children:[s.jsxs("button",{className:"btn btn-sm btn-ghost",onClick:o,title:"Refresh","aria-label":"Refresh plugin list",children:[s.jsx(ie,{size:14,className:S?"spin":""}),"Refresh"]}),s.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>f(!0),children:[s.jsx(O,{size:14})," Install"]})]})]}),J&&s.jsxs("div",{className:"plugin-install-form",children:[s.jsxs("p",{className:"plugin-install-hint",children:["Browse to a plugin package root (contains ",s.jsx("code",{children:"manifest.json"}),") or a built ",s.jsx("code",{children:"dist"})," directory."]}),s.jsx(me,{value:b,onChange:v,placeholder:"Absolute path to plugin directory or dist folder",onInputKeyDown:e=>{e.key==="Enter"&&(e.preventDefault(),z())}}),s.jsxs("div",{className:"plugin-install-actions",children:[s.jsx("button",{className:"btn btn-primary",onClick:z,disabled:C||!b.trim(),children:C?"Installing...":"Install Plugin"}),s.jsx("button",{className:"btn btn-secondary",onClick:()=>{f(!1),v("")},children:"Cancel"})]})]}),S?s.jsx("div",{className:"settings-empty-state",children:"Loading plugins..."}):s.jsxs(s.Fragment,{children:[q.length===0?s.jsxs("div",{className:"settings-empty-state",children:[s.jsx(ae,{size:32,className:"text-muted"}),s.jsx("p",{children:"No plugins installed."}),s.jsx("p",{className:"text-muted",children:"Install a plugin to get started, or use a bundled runtime below."})]}):s.jsx("div",{className:"plugin-list",children:q.map(e=>s.jsxs("div",{className:"plugin-item",children:[s.jsxs("div",{className:"plugin-info",children:[s.jsx("span",{className:"plugin-name",children:e.name}),s.jsxs("span",{className:"plugin-version text-muted",children:["v",e.version]}),s.jsx("span",{className:"plugin-state-badge",style:{color:j[e.state]||j.installed},children:e.state})]}),s.jsxs("div",{className:"plugin-actions",children:[e.state==="started"&&s.jsx("button",{className:"btn-icon",onClick:()=>B(e),disabled:h===e.id,title:"Reload",children:s.jsx(A,{size:14,className:h===e.id?"spin":""})}),s.jsxs("label",{className:"toggle-switch",children:[s.jsx("input",{type:"checkbox",checked:e.enabled,onChange:()=>e.enabled?U(e):F(e)}),s.jsx("span",{className:"toggle-slider"})]}),s.jsx("button",{className:"btn-icon",onClick:()=>W(e),title:"Settings",children:s.jsx(le,{size:14})}),s.jsx("button",{className:"btn-icon",onClick:()=>L(e),title:"Uninstall",children:s.jsx(V,{size:14})})]})]},e.id))}),T()]})]})}export{fe as PluginManager,j as STATE_COLORS};
@@ -1 +0,0 @@
1
- .plugin-manager,.plugin-manager-detail{display:flex;flex-direction:column;gap:var(--space-lg);padding-inline:var(--space-xl);padding-block:var(--space-md)}.plugin-manager-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--space-sm);border-bottom:var(--btn-border-width) solid var(--border);gap:var(--space-sm)}.plugin-manager-header-title{font-size:13px;font-weight:600;color:var(--text);flex:1}.plugin-manager-actions{display:flex;gap:var(--space-sm);align-items:center}.plugin-install-form{display:flex;flex-direction:column;gap:var(--space-sm);padding:var(--space-lg);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-install-hint{margin:0;font-size:.85rem;color:var(--text-secondary, var(--text-muted));line-height:1.45}.plugin-install-hint code{padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--text-muted) 12%,transparent);font-size:.85em}.plugin-install-actions{display:flex;gap:var(--space-sm);justify-content:flex-end}.plugin-list{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-item{display:flex;align-items:center;justify-content:space-between;padding:var(--space-md) var(--space-lg);background:var(--surface);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);transition:border-color var(--transition-fast)}.plugin-item:hover{border-color:var(--text-dim)}.plugin-info{display:flex;align-items:center;gap:var(--space-sm);min-width:0}.plugin-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.plugin-version{font-size:.85rem}.plugin-state-badge{display:inline-flex;align-items:center;padding:var(--space-xs) var(--space-sm);border-radius:var(--radius-pill);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;background:color-mix(in srgb,currentColor 12%,transparent)}.plugin-actions{display:flex;align-items:center;gap:var(--space-xs);flex-shrink:0}.plugin-manager-detail-header,.plugin-detail-title{display:flex;align-items:center;gap:var(--space-md);flex-wrap:wrap}.plugin-detail-name{margin:0}.plugin-detail-content{display:flex;flex-direction:column;gap:var(--space-lg)}.plugin-detail-card{background:var(--surface);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);padding:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-md)}.plugin-description{font-size:.95rem;color:var(--text-secondary, var(--text-muted));line-height:1.5}.plugin-detail-meta-row{display:flex;align-items:center;gap:var(--space-xs);font-size:.9rem;color:var(--text-muted)}.plugin-homepage a{display:inline-flex;align-items:center;gap:var(--space-xs);color:var(--color-info);font-size:.85rem}.plugin-detail-section-heading{margin:0;padding:0;border:0;font-size:.95rem}.plugin-settings-form{display:flex;flex-direction:column;gap:var(--space-lg);margin-top:var(--space-xs)}.plugin-settings-form .form-group{padding:0;margin:0}.plugin-settings-array{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-settings-array-item{display:flex;align-items:center;gap:var(--space-sm)}.plugin-settings-array-item input{flex:1}.plugin-detail-actions{display:flex;gap:var(--space-sm);padding-top:var(--space-md);border-top:var(--btn-border-width) solid var(--border);justify-content:flex-end}.plugin-manager .empty-state,.plugin-manager .loading-state,.plugin-manager .settings-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-sm);padding:var(--space-2xl);text-align:center;color:var(--text-muted)}.plugin-bundled-runtime-list{width:100%;display:flex;flex-direction:column;gap:var(--space-sm);margin-top:var(--space-sm)}.plugin-bundled-runtime-item{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-bundled-runtime-meta{display:flex;align-items:center;gap:var(--space-sm);min-width:0}.plugin-bundled-runtime-name{color:var(--text);font-size:.9rem;font-weight:500}.plugin-bundled-runtime-badge{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);background:var(--status-in-review-bg);color:var(--in-review);font-size:.75rem;font-weight:600;text-transform:uppercase}.plugin-bundled-runtime-section{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-bundled-runtime-header{display:flex;flex-direction:column;gap:var(--space-xs)}.plugin-bundled-runtime-heading{margin:0;font-size:.95rem}.plugin-bundled-runtime-description{margin:0;font-size:.85rem;color:var(--text-muted)}.plugin-bundled-runtime-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:.75rem;font-weight:600;text-transform:uppercase}.plugin-bundled-runtime-status--installed{background:var(--status-done-bg);color:var(--done)}.plugin-bundled-runtime-status--available{background:var(--status-todo-bg);color:var(--todo)}@media(max-width:768px){.plugin-manager-detail-header{gap:var(--space-sm)}.plugin-detail-title{gap:var(--space-xs)}.plugin-detail-card{padding:var(--space-md);gap:var(--space-sm)}.plugin-list{gap:var(--space-xs)}.plugin-item{padding:var(--space-md);flex-direction:column;align-items:stretch;gap:var(--space-sm)}.plugin-info{width:100%;flex-wrap:wrap;row-gap:var(--space-xs)}.plugin-name{flex:1 1 100%;white-space:normal}.plugin-actions{width:100%;justify-content:flex-end;flex-wrap:wrap;gap:var(--space-sm)}.plugin-actions .btn-icon,.plugin-actions .toggle-switch{min-width:36px;min-height:36px}.plugin-actions .toggle-switch{display:inline-flex;align-items:center;justify-content:center}.plugin-detail-actions{flex-wrap:wrap;justify-content:flex-start}.plugin-detail-actions button{flex:1 1 auto;min-height:36px}.plugin-bundled-runtime-item{flex-direction:column;align-items:stretch}.plugin-bundled-runtime-item .btn{min-height:36px}}