@polderlabs/bizar 5.5.0 → 5.5.1

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 (70) hide show
  1. package/bizar-dash/dist/assets/{EnvVarsSection-L413CQEI.js → EnvVarsSection-BUQnyVQD.js} +3 -3
  2. package/bizar-dash/dist/assets/{EnvVarsSection-L413CQEI.js.map → EnvVarsSection-BUQnyVQD.js.map} +1 -1
  3. package/bizar-dash/dist/assets/{MobileChat-0kGQb1S6.js → MobileChat-8FupFgTv.js} +1 -1
  4. package/bizar-dash/dist/assets/{MobileChat-0kGQb1S6.js.map → MobileChat-8FupFgTv.js.map} +1 -1
  5. package/bizar-dash/dist/assets/MobileSettings-BkXtglA6.js +1 -0
  6. package/bizar-dash/dist/assets/MobileSettings-BkXtglA6.js.map +1 -0
  7. package/bizar-dash/dist/assets/{Toast-C6f1zFJ2.js → Toast-CUwkLRms.js} +1 -1
  8. package/bizar-dash/dist/assets/{Toast-C6f1zFJ2.js.map → Toast-CUwkLRms.js.map} +1 -1
  9. package/bizar-dash/dist/assets/{icons-BsWXWkEF.js → icons-COpQr35l.js} +110 -95
  10. package/bizar-dash/dist/assets/icons-COpQr35l.js.map +1 -0
  11. package/bizar-dash/dist/assets/{main-BklYy02e.css → main-Bz_zWFCw.css} +1 -1
  12. package/bizar-dash/dist/assets/main-gqVwliHz.js +18 -0
  13. package/bizar-dash/dist/assets/main-gqVwliHz.js.map +1 -0
  14. package/bizar-dash/dist/assets/mobile-B7dAkmYd.js +1 -0
  15. package/bizar-dash/dist/assets/{mobile-CsaN4CxH.js.map → mobile-B7dAkmYd.js.map} +1 -1
  16. package/bizar-dash/dist/assets/{mobile-layout-iFBdjEE4.js → mobile-layout-ClC6Qa42.js} +2 -2
  17. package/bizar-dash/dist/assets/{mobile-layout-iFBdjEE4.js.map → mobile-layout-ClC6Qa42.js.map} +1 -1
  18. package/bizar-dash/dist/assets/{useSlashCommands-DGJcC9iQ.js → useSlashCommands-B3MZ1JXa.js} +2 -2
  19. package/bizar-dash/dist/assets/{useSlashCommands-DGJcC9iQ.js.map → useSlashCommands-B3MZ1JXa.js.map} +1 -1
  20. package/bizar-dash/dist/index.html +7 -7
  21. package/bizar-dash/dist/mobile.html +3 -3
  22. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  23. package/bizar-dash/src/server/bg-spawner.mjs +522 -305
  24. package/bizar-dash/src/server/headroom.mjs +3 -1
  25. package/bizar-dash/src/server/memory-lightrag.mjs +26 -2
  26. package/bizar-dash/src/server/opencode-sdk.mjs +63 -3
  27. package/bizar-dash/src/server/plugins/registry.mjs +1 -1
  28. package/bizar-dash/src/server/routes/activity.mjs +85 -0
  29. package/bizar-dash/src/server/routes/background.mjs +31 -13
  30. package/bizar-dash/src/server/routes/opencode-session-detail.mjs +1 -1
  31. package/bizar-dash/src/server/server.mjs +4 -1
  32. package/bizar-dash/src/web/App.tsx +43 -36
  33. package/bizar-dash/src/web/components/Sidebar.tsx +7 -27
  34. package/bizar-dash/src/web/components/Topbar.tsx +48 -11
  35. package/bizar-dash/src/web/styles/main.css +50 -26
  36. package/bizar-dash/src/web/styles/memory.css +17 -0
  37. package/bizar-dash/src/web/styles/settings.css +9 -171
  38. package/bizar-dash/src/web/views/Marketplace.tsx +192 -31
  39. package/bizar-dash/src/web/views/Settings.tsx +205 -184
  40. package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +157 -7
  41. package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +11 -0
  42. package/bizar-dash/src/web/views/settings/MemorySection.tsx +65 -11
  43. package/bizar-dash/tests/background-sdk-session.test.mjs +94 -0
  44. package/bizar-dash/tests/background-session-events.test.mjs +74 -0
  45. package/bizar-dash/tests/background-steer-sdk.test.mjs +71 -0
  46. package/bizar-dash/tests/background-steer.test.mjs +25 -7
  47. package/bizar-dash/tests/views/Marketplace.test.tsx +200 -0
  48. package/bizar-dash/tests/views/Memory.test.tsx +235 -0
  49. package/bizar-dash/tests/views/Settings.test.tsx +160 -0
  50. package/bizar-dash/tests/views/sidebar.test.tsx +116 -0
  51. package/bizar-dash/tests/views/topbar.test.tsx +102 -0
  52. package/cli/commands/marketplace.mjs +1 -1
  53. package/package.json +1 -1
  54. package/plugins/bizar/src/background-state.ts +11 -0
  55. package/plugins/bizar/src/background.ts +212 -34
  56. package/plugins/bizar/src/opencode-runner.ts +80 -353
  57. package/plugins/bizar/src/tools/bg-send-message.ts +160 -61
  58. package/plugins/bizar/src/tools/bg-spawn.ts +235 -156
  59. package/plugins/bizar/tests/tools/bg-send-message.test.ts +84 -11
  60. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +223 -0
  61. package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js +0 -1
  62. package/bizar-dash/dist/assets/MobileSettings-Cq1oFIdO.js.map +0 -1
  63. package/bizar-dash/dist/assets/icons-BsWXWkEF.js.map +0 -1
  64. package/bizar-dash/dist/assets/main-CBTgYSeG.js +0 -18
  65. package/bizar-dash/dist/assets/main-CBTgYSeG.js.map +0 -1
  66. package/bizar-dash/dist/assets/mobile-CsaN4CxH.js +0 -1
  67. package/bizar-dash/src/web/components/SettingsNav.tsx +0 -92
  68. package/bizar-dash/tests/settings-layout.test.tsx +0 -129
  69. package/bizar-dash/tests/settings-mode-wiring.test.tsx +0 -151
  70. package/bizar-dash/tests/settings-nav.test.tsx +0 -128
@@ -1 +1 @@
1
- import{j as n,r as t}from"./react-vendor-Dn4wqh4Z.js";import{C as N,u as T,a as k,b as R,c as E}from"./useSlashCommands-DGJcC9iQ.js";import{u as F}from"./Toast-C6f1zFJ2.js";import"./icons-BsWXWkEF.js";import"./mobile-layout-iFBdjEE4.js";import"./markdown-C6mXtQxD.js";import"./vendor-CeHGtduv.js";function $(o){const{activeSource:c}=o;return n.jsx("div",{className:`chat-composer-wrap chat-composer-source-${c??"none"} legacy`,children:n.jsx("div",{className:"chat-composer-pill",children:n.jsx(N,{...o})})})}function G({snapshot:o,settings:c,setActiveTab:g,initialTaskId:j,onClearTaskId:z}){const i=F(),s=T(o,c,j??"");t.useEffect(()=>{s.setToast({error:e=>i.error(e),success:e=>i.success(e),info:e=>i.info(e),warning:e=>i.warning(e)})},[s,i]);const[r,l]=t.useState(""),[f,x]=t.useState(c.defaultAgent||"odin"),[p,C]=t.useState(c.defaultModel||""),[h,v]=t.useState([]),u=t.useRef(null),{suggestions:y,setQuery:m}=k(o);t.useEffect(()=>{m(r)},[r,m]);const P=()=>{var e;return(e=u.current)==null?void 0:e.click()},w=e=>{const d=e.target.files;if(!d)return;const S=[];for(let a=0;a<d.length;a++)S.push(d[a].name);v(a=>{const O=S.filter(M=>!a.includes(M));return[...a,...O]}),u.current&&(u.current.value="")},I=async()=>{const e=r.trim();if(!e)return;l(""),m(""),(await s.onSend(e,f,p,h)).ok&&s.jumpToLatest()};return n.jsxs("div",{className:"chat-shell",children:[n.jsx(R,{activeProject:o.activeProject,sessionCount:s.sessions.length,sessionsOpen:!1,infoOpen:!1,onToggleSessions:()=>{},onToggleInfo:()=>{},onOpenOverview:()=>g==null?void 0:g("overview")}),n.jsx("div",{className:"chat-body",children:n.jsxs("main",{className:"chat-main",children:[n.jsx(E,{messages:s.activeSource==="opencode"?s.opencodeMessages:s.bizarMessages,loading:s.loading,activeProject:o.activeProject,sessionId:s.activeSource==="opencode"?s.activeOpencodeSessionId??s.sessionId:s.sessionId,pinned:s.pinned,activeSource:s.activeSource,onPickSuggestion:e=>l(e),onCopy:e=>s.copyMessage(e),onDelete:()=>{},onTogglePin:s.togglePin,onRegenerate:s.onRegenerate}),n.jsx($,{agent:f,setAgent:x,model:p,setModel:C,text:r,setText:l,sending:s.sending,activeSource:s.activeSource,onSend:I,attachments:h,setAttachments:v,suggestions:y,onPickSuggestion:e=>l(`${e.split(" ")[0]} `),agents:o.agents||[],onAttach:P,sessionsOpen:!1,infoOpen:!1}),n.jsx("input",{ref:u,type:"file",multiple:!0,style:{display:"none"},onChange:w})]})})]})}export{G as MobileChat};
1
+ import{j as n,r as t}from"./react-vendor-Dn4wqh4Z.js";import{C as N,u as T,a as k,b as R,c as E}from"./useSlashCommands-B3MZ1JXa.js";import{u as F}from"./Toast-CUwkLRms.js";import"./icons-COpQr35l.js";import"./mobile-layout-ClC6Qa42.js";import"./markdown-C6mXtQxD.js";import"./vendor-CeHGtduv.js";function $(o){const{activeSource:c}=o;return n.jsx("div",{className:`chat-composer-wrap chat-composer-source-${c??"none"} legacy`,children:n.jsx("div",{className:"chat-composer-pill",children:n.jsx(N,{...o})})})}function G({snapshot:o,settings:c,setActiveTab:g,initialTaskId:j,onClearTaskId:z}){const i=F(),s=T(o,c,j??"");t.useEffect(()=>{s.setToast({error:e=>i.error(e),success:e=>i.success(e),info:e=>i.info(e),warning:e=>i.warning(e)})},[s,i]);const[r,l]=t.useState(""),[f,x]=t.useState(c.defaultAgent||"odin"),[p,C]=t.useState(c.defaultModel||""),[h,v]=t.useState([]),u=t.useRef(null),{suggestions:y,setQuery:m}=k(o);t.useEffect(()=>{m(r)},[r,m]);const P=()=>{var e;return(e=u.current)==null?void 0:e.click()},w=e=>{const d=e.target.files;if(!d)return;const S=[];for(let a=0;a<d.length;a++)S.push(d[a].name);v(a=>{const O=S.filter(M=>!a.includes(M));return[...a,...O]}),u.current&&(u.current.value="")},I=async()=>{const e=r.trim();if(!e)return;l(""),m(""),(await s.onSend(e,f,p,h)).ok&&s.jumpToLatest()};return n.jsxs("div",{className:"chat-shell",children:[n.jsx(R,{activeProject:o.activeProject,sessionCount:s.sessions.length,sessionsOpen:!1,infoOpen:!1,onToggleSessions:()=>{},onToggleInfo:()=>{},onOpenOverview:()=>g==null?void 0:g("overview")}),n.jsx("div",{className:"chat-body",children:n.jsxs("main",{className:"chat-main",children:[n.jsx(E,{messages:s.activeSource==="opencode"?s.opencodeMessages:s.bizarMessages,loading:s.loading,activeProject:o.activeProject,sessionId:s.activeSource==="opencode"?s.activeOpencodeSessionId??s.sessionId:s.sessionId,pinned:s.pinned,activeSource:s.activeSource,onPickSuggestion:e=>l(e),onCopy:e=>s.copyMessage(e),onDelete:()=>{},onTogglePin:s.togglePin,onRegenerate:s.onRegenerate}),n.jsx($,{agent:f,setAgent:x,model:p,setModel:C,text:r,setText:l,sending:s.sending,activeSource:s.activeSource,onSend:I,attachments:h,setAttachments:v,suggestions:y,onPickSuggestion:e=>l(`${e.split(" ")[0]} `),agents:o.agents||[],onAttach:P,sessionsOpen:!1,infoOpen:!1}),n.jsx("input",{ref:u,type:"file",multiple:!0,style:{display:"none"},onChange:w})]})})]})}export{G as MobileChat};
@@ -1 +1 @@
1
- {"version":3,"file":"MobileChat-0kGQb1S6.js","sources":["../../src/web/components/chat/FloatingComposer.tsx","../../src/web/mobile/MobileChat.tsx"],"sourcesContent":["// src/components/chat/FloatingComposer.tsx — sticky bottom wrapper for the composer pill.\n\nimport { Composer } from './Composer';\n\ninterface Props {\n agent: string;\n setAgent: (a: string) => void;\n model: string;\n setModel: (m: string) => void;\n text: string;\n setText: (t: string) => void;\n sending: boolean;\n onSend: () => void;\n attachments: string[];\n setAttachments: (a: string[] | ((prev: string[]) => string[])) => void;\n suggestions: Array<{ cmd: string; desc: string }>;\n onPickSuggestion: (cmd: string) => void;\n agents: Array<{ name: string }>;\n onAttach: () => void;\n sessionsOpen: boolean;\n infoOpen: boolean;\n /** Which stream will receive the next message — propagates the\n * pill border color and a small \"→ opencode\" hint. */\n activeSource?: 'bizar' | 'opencode' | null;\n}\n\nexport function FloatingComposer(props: Props) {\n const { activeSource } = props;\n return (\n <div\n className={`chat-composer-wrap chat-composer-source-${activeSource ?? 'none'} legacy`}\n >\n <div className=\"chat-composer-pill\">\n <Composer {...props} />\n </div>\n </div>\n );\n}","// src/web/mobile/MobileChat.tsx — mobile chat UI without info/sessions sidebars.\n// No OCR capture, no clipper, no sessions panel — pure chat thread.\nimport { useEffect, useRef, useState } from 'react';\nimport { ChatTopBar } from '../components/chat/ChatTopBar';\nimport { ChatThread } from '../components/chat/ChatThread';\nimport { FloatingComposer } from '../components/chat/FloatingComposer';\nimport { useChat } from '../components/chat/useChat';\nimport { useSlashCommands } from '../components/chat/useSlashCommands';\nimport { useToast } from '../components/Toast';\nimport type { Snapshot, Settings } from '../lib/types';\n\ninterface Props {\n snapshot: Snapshot;\n settings: Settings;\n setActiveTab?: (id: string) => void;\n initialTaskId?: string | null;\n onClearTaskId?: () => void;\n}\n\nexport function MobileChat({\n snapshot,\n settings,\n setActiveTab,\n initialTaskId,\n onClearTaskId,\n}: Props) {\n const toast = useToast();\n const chat = useChat(snapshot, settings, initialTaskId ?? '');\n useEffect(() => {\n chat.setToast({\n error: (msg: string) => toast.error(msg),\n success: (msg: string) => toast.success(msg),\n info: (msg: string) => toast.info(msg),\n warning: (msg: string) => toast.warning(msg),\n });\n }, [chat, toast]);\n\n const [text, setText] = useState('');\n const [agent, setAgent] = useState(settings.defaultAgent || 'odin');\n const [model, setModel] = useState(settings.defaultModel || '');\n const [attachments, setAttachments] = useState<string[]>([]);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { suggestions, setQuery } = useSlashCommands(snapshot);\n useEffect(() => {\n setQuery(text);\n }, [text, setQuery]);\n\n const onAttach = () => fileInputRef.current?.click();\n const onFiles = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files) return;\n const next: string[] = [];\n for (let i = 0; i < files.length; i++) next.push(files[i].name);\n setAttachments((cur) => {\n const newNames = next.filter((n) => !cur.includes(n));\n return [...cur, ...newNames];\n });\n if (fileInputRef.current) fileInputRef.current.value = '';\n };\n\n const handleSend = async () => {\n const msg = text.trim();\n if (!msg) return;\n setText('');\n setQuery('');\n const result = await chat.onSend(msg, agent, model, attachments);\n if (result.ok) chat.jumpToLatest();\n };\n\n return (\n <div className=\"chat-shell\">\n <ChatTopBar\n activeProject={snapshot.activeProject}\n sessionCount={chat.sessions.length}\n sessionsOpen={false}\n infoOpen={false}\n onToggleSessions={() => undefined}\n onToggleInfo={() => undefined}\n onOpenOverview={() => setActiveTab?.('overview')}\n />\n <div className=\"chat-body\">\n <main className=\"chat-main\">\n <ChatThread\n messages={\n chat.activeSource === 'opencode'\n ? chat.opencodeMessages\n : chat.bizarMessages\n }\n loading={chat.loading}\n activeProject={snapshot.activeProject}\n sessionId={\n chat.activeSource === 'opencode'\n ? chat.activeOpencodeSessionId ?? chat.sessionId\n : chat.sessionId\n }\n pinned={chat.pinned}\n activeSource={chat.activeSource}\n onPickSuggestion={(t) => setText(t)}\n onCopy={(m) =>\n chat.copyMessage(m as Parameters<typeof chat.copyMessage>[0])\n }\n onDelete={() => undefined}\n onTogglePin={chat.togglePin}\n onRegenerate={chat.onRegenerate}\n />\n <FloatingComposer\n agent={agent}\n setAgent={setAgent}\n model={model}\n setModel={setModel}\n text={text}\n setText={setText}\n sending={chat.sending}\n activeSource={chat.activeSource}\n onSend={handleSend}\n attachments={attachments}\n setAttachments={setAttachments}\n suggestions={suggestions}\n onPickSuggestion={(cmd) => setText(`${cmd.split(' ')[0]} `)}\n agents={snapshot.agents || []}\n onAttach={onAttach}\n sessionsOpen={false}\n infoOpen={false}\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n style={{ display: 'none' }}\n onChange={onFiles}\n />\n </main>\n </div>\n </div>\n );\n}\n"],"names":["FloatingComposer","props","activeSource","jsx","Composer","MobileChat","snapshot","settings","setActiveTab","initialTaskId","onClearTaskId","toast","useToast","chat","useChat","useEffect","msg","text","setText","useState","agent","setAgent","model","setModel","attachments","setAttachments","fileInputRef","useRef","suggestions","setQuery","useSlashCommands","onAttach","_a","onFiles","files","next","i","cur","newNames","n","handleSend","jsxs","ChatTopBar","ChatThread","t","m","cmd"],"mappings":"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"}
1
+ {"version":3,"file":"MobileChat-8FupFgTv.js","sources":["../../src/web/components/chat/FloatingComposer.tsx","../../src/web/mobile/MobileChat.tsx"],"sourcesContent":["// src/components/chat/FloatingComposer.tsx — sticky bottom wrapper for the composer pill.\n\nimport { Composer } from './Composer';\n\ninterface Props {\n agent: string;\n setAgent: (a: string) => void;\n model: string;\n setModel: (m: string) => void;\n text: string;\n setText: (t: string) => void;\n sending: boolean;\n onSend: () => void;\n attachments: string[];\n setAttachments: (a: string[] | ((prev: string[]) => string[])) => void;\n suggestions: Array<{ cmd: string; desc: string }>;\n onPickSuggestion: (cmd: string) => void;\n agents: Array<{ name: string }>;\n onAttach: () => void;\n sessionsOpen: boolean;\n infoOpen: boolean;\n /** Which stream will receive the next message — propagates the\n * pill border color and a small \"→ opencode\" hint. */\n activeSource?: 'bizar' | 'opencode' | null;\n}\n\nexport function FloatingComposer(props: Props) {\n const { activeSource } = props;\n return (\n <div\n className={`chat-composer-wrap chat-composer-source-${activeSource ?? 'none'} legacy`}\n >\n <div className=\"chat-composer-pill\">\n <Composer {...props} />\n </div>\n </div>\n );\n}","// src/web/mobile/MobileChat.tsx — mobile chat UI without info/sessions sidebars.\n// No OCR capture, no clipper, no sessions panel — pure chat thread.\nimport { useEffect, useRef, useState } from 'react';\nimport { ChatTopBar } from '../components/chat/ChatTopBar';\nimport { ChatThread } from '../components/chat/ChatThread';\nimport { FloatingComposer } from '../components/chat/FloatingComposer';\nimport { useChat } from '../components/chat/useChat';\nimport { useSlashCommands } from '../components/chat/useSlashCommands';\nimport { useToast } from '../components/Toast';\nimport type { Snapshot, Settings } from '../lib/types';\n\ninterface Props {\n snapshot: Snapshot;\n settings: Settings;\n setActiveTab?: (id: string) => void;\n initialTaskId?: string | null;\n onClearTaskId?: () => void;\n}\n\nexport function MobileChat({\n snapshot,\n settings,\n setActiveTab,\n initialTaskId,\n onClearTaskId,\n}: Props) {\n const toast = useToast();\n const chat = useChat(snapshot, settings, initialTaskId ?? '');\n useEffect(() => {\n chat.setToast({\n error: (msg: string) => toast.error(msg),\n success: (msg: string) => toast.success(msg),\n info: (msg: string) => toast.info(msg),\n warning: (msg: string) => toast.warning(msg),\n });\n }, [chat, toast]);\n\n const [text, setText] = useState('');\n const [agent, setAgent] = useState(settings.defaultAgent || 'odin');\n const [model, setModel] = useState(settings.defaultModel || '');\n const [attachments, setAttachments] = useState<string[]>([]);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { suggestions, setQuery } = useSlashCommands(snapshot);\n useEffect(() => {\n setQuery(text);\n }, [text, setQuery]);\n\n const onAttach = () => fileInputRef.current?.click();\n const onFiles = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files) return;\n const next: string[] = [];\n for (let i = 0; i < files.length; i++) next.push(files[i].name);\n setAttachments((cur) => {\n const newNames = next.filter((n) => !cur.includes(n));\n return [...cur, ...newNames];\n });\n if (fileInputRef.current) fileInputRef.current.value = '';\n };\n\n const handleSend = async () => {\n const msg = text.trim();\n if (!msg) return;\n setText('');\n setQuery('');\n const result = await chat.onSend(msg, agent, model, attachments);\n if (result.ok) chat.jumpToLatest();\n };\n\n return (\n <div className=\"chat-shell\">\n <ChatTopBar\n activeProject={snapshot.activeProject}\n sessionCount={chat.sessions.length}\n sessionsOpen={false}\n infoOpen={false}\n onToggleSessions={() => undefined}\n onToggleInfo={() => undefined}\n onOpenOverview={() => setActiveTab?.('overview')}\n />\n <div className=\"chat-body\">\n <main className=\"chat-main\">\n <ChatThread\n messages={\n chat.activeSource === 'opencode'\n ? chat.opencodeMessages\n : chat.bizarMessages\n }\n loading={chat.loading}\n activeProject={snapshot.activeProject}\n sessionId={\n chat.activeSource === 'opencode'\n ? chat.activeOpencodeSessionId ?? chat.sessionId\n : chat.sessionId\n }\n pinned={chat.pinned}\n activeSource={chat.activeSource}\n onPickSuggestion={(t) => setText(t)}\n onCopy={(m) =>\n chat.copyMessage(m as Parameters<typeof chat.copyMessage>[0])\n }\n onDelete={() => undefined}\n onTogglePin={chat.togglePin}\n onRegenerate={chat.onRegenerate}\n />\n <FloatingComposer\n agent={agent}\n setAgent={setAgent}\n model={model}\n setModel={setModel}\n text={text}\n setText={setText}\n sending={chat.sending}\n activeSource={chat.activeSource}\n onSend={handleSend}\n attachments={attachments}\n setAttachments={setAttachments}\n suggestions={suggestions}\n onPickSuggestion={(cmd) => setText(`${cmd.split(' ')[0]} `)}\n agents={snapshot.agents || []}\n onAttach={onAttach}\n sessionsOpen={false}\n infoOpen={false}\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n style={{ display: 'none' }}\n onChange={onFiles}\n />\n </main>\n </div>\n </div>\n );\n}\n"],"names":["FloatingComposer","props","activeSource","jsx","Composer","MobileChat","snapshot","settings","setActiveTab","initialTaskId","onClearTaskId","toast","useToast","chat","useChat","useEffect","msg","text","setText","useState","agent","setAgent","model","setModel","attachments","setAttachments","fileInputRef","useRef","suggestions","setQuery","useSlashCommands","onAttach","_a","onFiles","files","next","i","cur","newNames","n","handleSend","jsxs","ChatTopBar","ChatThread","t","m","cmd"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import{r,j as e}from"./react-vendor-Dn4wqh4Z.js";import{a as y,S as E,c as R}from"./mobile-layout-ClC6Qa42.js";import{C as M,a as U,b as B,B as I,G,E as V,S as P,U as A,H as C,T as $,c as H}from"./EnvVarsSection-BUQnyVQD.js";import{u as D}from"./Toast-CUwkLRms.js";import{j as N,aZ as T,aT as k,N as F,a9 as K,q as O,aC as q,w,R as W,aF as Z,I as J,e as Q}from"./icons-COpQr35l.js";function X(){const s=D(),[i,t]=r.useState(!0),[c,a]=r.useState(!1),[o,d]=r.useState(!1),[u,g]=r.useState(""),[p,x]=r.useState(""),[f,h]=r.useState(!1),[j,n]=r.useState(0);r.useEffect(()=>{let l=!1;return(async()=>{var S,L;try{const[z,v]=await Promise.all([y.get("/memory/config/global").catch(()=>null),y.get("/memory/status").catch(()=>null)]);if(l)return;z&&g(((L=(S=z.config)==null?void 0:S.git)==null?void 0:L.remoteUrl)||""),v&&(x(v.vaultRoot||""),h(!!v.initialized))}finally{l||t(!1)}})(),()=>{l=!0}},[j]);const b=async()=>{a(!0);try{await y.put("/memory/config/global",{git:{remoteUrl:u}}),s.success("Memory config saved."),n(l=>l+1)}catch(l){s.error(`Save failed: ${l.message}`)}finally{a(!1)}},m=async()=>{d(!0);try{await y.post("/memory/init",{}),s.success("Vault initialised."),n(l=>l+1)}catch(l){s.error(`Initialise failed: ${l.message}`)}finally{d(!1)}};return e.jsxs(M,{id:"settings-memory","data-section":"memory",children:[e.jsxs(U,{children:[e.jsx(N,{size:14})," Memory vault"]}),e.jsx(B,{children:"The vault lives at a default location on disk. Only the git remote URL needs to be configured to enable sync across machines."}),i?e.jsx("div",{className:"flex-center",style:{padding:24},children:e.jsx(E,{size:"sm"})}):e.jsxs("div",{className:"settings-fields",children:[e.jsxs("div",{className:"field",children:[e.jsxs("label",{className:"field-label",children:[e.jsx(T,{size:12,style:{display:"inline",marginRight:4}}),"Vault path"]}),e.jsxs("div",{className:"field-readonly mono","data-testid":"settings-memory-vault-path",style:{fontSize:12,padding:"6px 0"},children:[p||e.jsx("span",{className:"muted",children:"not initialised"}),f?null:e.jsxs(I,{variant:"ghost",size:"sm",onClick:m,disabled:o,style:{marginLeft:12},children:[o?e.jsx(k,{size:12,className:"memory-spin"}):e.jsx(T,{size:12}),"Initialise"]})]})]}),e.jsxs("div",{className:"field",children:[e.jsxs("label",{className:"field-label",htmlFor:"set-memory-git-remote",children:[e.jsx(F,{size:12,style:{display:"inline",marginRight:4}}),"Git remote URL"]}),e.jsx("input",{id:"set-memory-git-remote",className:"input mono",type:"text",value:u,onChange:l=>g(l.target.value),placeholder:"git@github.com:org/repo.git"}),e.jsx("span",{className:"field-hint",children:"Leave empty to use local-only mode (no git sync)."})]}),e.jsx("div",{style:{marginTop:12},children:e.jsxs(I,{variant:"primary",size:"sm",onClick:b,disabled:c,children:[c?e.jsx(k,{size:12,className:"memory-spin"}):e.jsx(K,{size:12}),"Save memory config"]})})]})]})}function Y({settings:s,onSettingsChange:i,autoSave:t}){const c=(o,d)=>{i==null||i(o,d),t==null||t(o,d)},a=o=>{i==null||i("ui",{...s.ui,...o}),t==null||t("ui",{...s.ui,...o})};return e.jsx(G,{settings:s,patchUi:a,patchTop:c,autoSave:t})}function _({settings:s,onSettingsChange:i,autoSave:t}){return e.jsxs("div",{style:{fontSize:13,color:"var(--text-dim)",padding:"8px 0"},children:[e.jsx("p",{style:{marginBottom:8},children:"Provider management is available on desktop."}),e.jsx("a",{href:"/?desktop=1#settings-providers",className:"mobile-btn mobile-btn-secondary",style:{display:"inline-block",fontSize:12,padding:"6px 12px"},children:"Open desktop to manage providers"})]})}function ee(){return e.jsx(V,{})}function se(){return e.jsx(X,{})}function ie({settings:s,onSettingsChange:i,autoSave:t}){const c=(a,o)=>{i==null||i(a,o),t==null||t(a,o)};return e.jsx(P,{settings:s,patchTop:c})}function te(){return e.jsx(A,{})}function ne({settings:s}){const[i,t]=r.useState(s),[c,a]=r.useState(!1);return e.jsx(C,{settings:i,setSettings:t,setDirty:a})}function ae(){return e.jsx($,{})}function oe({settings:s}){const i=s.about||{version:"—",homepage:"https://github.com/DrB0rk/BizarHarness",license:"MIT"};return e.jsx(H,{about:i})}function pe({settings:s,onSettingsChange:i,autoSave:t,snapshot:c}){var x,f,h,j;const[a,o]=r.useState(""),[d,u]=r.useState(null),p=[{id:"general",title:"General",icon:O,description:"Theme, accent, layout",component:e.jsx(Y,{settings:s,onSettingsChange:i,autoSave:t})},{id:"providers",title:"AI Providers",icon:N,description:`${((x=c==null?void 0:c.providers)==null?void 0:x.length)??0} configured`,component:e.jsx(_,{settings:s,onSettingsChange:i,autoSave:t})},{id:"env-vars",title:"Environment Variables",icon:q,description:"API keys, secrets",component:e.jsx(ee,{})},{id:"memory",title:"Memory Vault",icon:N,description:"Git remote, LightRAG config",component:e.jsx(se,{})},{id:"system-llm",title:"System LLM",icon:w,description:((f=s.systemLlm)==null?void 0:f.model)||"not set",component:e.jsx(ie,{settings:s,onSettingsChange:i,autoSave:t})},{id:"updates",title:"Updates",icon:W,description:"Check for updates",component:e.jsx(te,{})},{id:"headroom",title:"Headroom",icon:w,description:(h=s.headroom)!=null&&h.enabled?"Enabled":"Disabled",component:e.jsx(ne,{settings:s})},{id:"tailscale",title:"Tailscale",icon:Z,description:"Tailscale serve & auth",component:e.jsx(ae,{})},{id:"about",title:"About",icon:J,description:`Bizar v${((j=s.about)==null?void 0:j.version)||"—"}`,component:e.jsx(oe,{settings:s})}].filter(n=>!a||n.title.toLowerCase().includes(a.toLowerCase())||n.description.toLowerCase().includes(a.toLowerCase()));return e.jsxs("div",{className:"mobile-settings",children:[e.jsxs("div",{className:"mobile-settings-search",children:[e.jsx(le,{}),e.jsx("input",{value:a,onChange:n=>o(n.target.value),placeholder:"Search settings...","aria-label":"Search settings"})]}),e.jsxs("div",{className:"mobile-settings-list",children:[p.map(n=>{const b=n.icon,m=d===n.id;return e.jsxs("div",{className:R("mobile-settings-item",m&&"is-expanded"),children:[e.jsxs("button",{className:"mobile-settings-item-head",onClick:()=>u(m?null:n.id),"aria-expanded":m,"aria-controls":`mobile-settings-body-${n.id}`,children:[e.jsx("div",{className:"mobile-settings-item-icon",children:e.jsx(b,{size:18})}),e.jsxs("div",{className:"mobile-settings-item-text",children:[e.jsx("div",{className:"mobile-settings-item-title",children:n.title}),e.jsx("div",{className:"mobile-settings-item-desc",children:n.description})]}),e.jsx(Q,{size:16,className:R("mobile-settings-item-chevron",m&&"is-rotated")})]}),m&&e.jsx("div",{id:`mobile-settings-body-${n.id}`,className:"mobile-settings-item-body",children:n.component})]},n.id)}),p.length===0&&e.jsxs("div",{className:"mobile-settings-empty",children:['No settings match "',a,'"']})]})]})}function le(){return e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("circle",{cx:"11",cy:"11",r:"8"}),e.jsx("path",{d:"m21 21-4.35-4.35"})]})}export{pe as MobileSettings};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileSettings-BkXtglA6.js","sources":["../../src/web/views/settings/MemorySection.tsx","../../src/web/mobile/MobileSettings.tsx"],"sourcesContent":["// src/web/views/settings/MemorySection.tsx — memory vault + git config for Settings.\n// v6.x — Vault path now loads from /api/memory/status (was hardcoded to '').\n// We also surface a small \"Initialise\" button when the vault does not exist.\nimport React, { useEffect, useState } from 'react';\nimport { Brain, GitBranch, Loader2, Save, FolderInput } from 'lucide-react';\nimport { Button } from '../../components/Button';\nimport { Card, CardTitle, CardMeta } from '../../components/Card';\nimport { Spinner } from '../../components/Spinner';\nimport { useToast } from '../../components/Toast';\nimport { api } from '../../lib/api';\n\ntype GitConfig = {\n remoteUrl?: string;\n};\n\ntype GlobalConfig = {\n git?: GitConfig;\n};\n\ntype MemoryStatus = {\n initialized: boolean;\n vaultRoot?: string | null;\n mode?: string | null;\n};\n\nexport function MemorySection() {\n const toast = useToast();\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [initialising, setInitialising] = useState(false);\n const [remoteUrl, setRemoteUrl] = useState('');\n const [vaultPath, setVaultPath] = useState<string>('');\n const [vaultInitialised, setVaultInitialised] = useState(false);\n const [refreshKey, setRefreshKey] = useState(0);\n\n useEffect(() => {\n let cancelled = false;\n (async () => {\n try {\n const [cfgRes, statusRes] = await Promise.all([\n api.get<{ config: GlobalConfig; path?: string }>('/memory/config/global').catch(() => null),\n api.get<MemoryStatus>('/memory/status').catch(() => null),\n ]);\n if (cancelled) return;\n if (cfgRes) setRemoteUrl(cfgRes.config?.git?.remoteUrl || '');\n if (statusRes) {\n setVaultPath(statusRes.vaultRoot || '');\n setVaultInitialised(!!statusRes.initialized);\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n return () => { cancelled = true; };\n }, [refreshKey]);\n\n const onSave = async () => {\n setSaving(true);\n try {\n await api.put('/memory/config/global', { git: { remoteUrl } });\n toast.success('Memory config saved.');\n setRefreshKey((k) => k + 1);\n } catch (err) {\n toast.error(`Save failed: ${(err as Error).message}`);\n } finally {\n setSaving(false);\n }\n };\n\n const onInitialise = async () => {\n setInitialising(true);\n try {\n await api.post('/memory/init', {});\n toast.success('Vault initialised.');\n setRefreshKey((k) => k + 1);\n } catch (err) {\n toast.error(`Initialise failed: ${(err as Error).message}`);\n } finally {\n setInitialising(false);\n }\n };\n\n return (\n <Card id=\"settings-memory\" data-section=\"memory\">\n <CardTitle><Brain size={14} /> Memory vault</CardTitle>\n <CardMeta>\n The vault lives at a default location on disk. Only the git remote URL\n needs to be configured to enable sync across machines.\n </CardMeta>\n\n {loading ? (\n <div className=\"flex-center\" style={{ padding: 24 }}>\n <Spinner size=\"sm\" />\n </div>\n ) : (\n <div className=\"settings-fields\">\n <div className=\"field\">\n <label className=\"field-label\">\n <FolderInput size={12} style={{ display: 'inline', marginRight: 4 }} />\n Vault path\n </label>\n <div\n className=\"field-readonly mono\"\n data-testid=\"settings-memory-vault-path\"\n style={{ fontSize: 12, padding: '6px 0' }}\n >\n {vaultPath || <span className=\"muted\">not initialised</span>}\n {vaultInitialised ? null : (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onInitialise}\n disabled={initialising}\n style={{ marginLeft: 12 }}\n >\n {initialising ? <Loader2 size={12} className=\"memory-spin\" /> : <FolderInput size={12} />}\n Initialise\n </Button>\n )}\n </div>\n </div>\n\n <div className=\"field\">\n <label className=\"field-label\" htmlFor=\"set-memory-git-remote\">\n <GitBranch size={12} style={{ display: 'inline', marginRight: 4 }} />\n Git remote URL\n </label>\n <input\n id=\"set-memory-git-remote\"\n className=\"input mono\"\n type=\"text\"\n value={remoteUrl}\n onChange={(e) => setRemoteUrl(e.target.value)}\n placeholder=\"git@github.com:org/repo.git\"\n />\n <span className=\"field-hint\">Leave empty to use local-only mode (no git sync).</span>\n </div>\n\n <div style={{ marginTop: 12 }}>\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={onSave}\n disabled={saving}\n >\n {saving ? <Loader2 size={12} className=\"memory-spin\" /> : <Save size={12} />}\n Save memory config\n </Button>\n </div>\n </div>\n )}\n </Card>\n );\n}\n","// src/web/mobile/MobileSettings.tsx — v5.4 mobile settings with full desktop parity.\n// All 9 sections in an accordion layout with search.\nimport { useState } from 'react';\nimport {\n Settings2, Brain, KeyRound, Cpu, RefreshCw, Globe, Info, ChevronRight,\n} from 'lucide-react';\nimport { cn } from '../lib/utils';\nimport type { Settings, Snapshot } from '../lib/types';\n\n// Sub-section components — each renders its section content inline.\nimport { GeneralSection } from '../views/settings/GeneralSection';\nimport { EnvVarsSection } from '../views/settings/EnvVarsSection';\nimport { MemorySection } from '../views/settings/MemorySection';\nimport { SystemLlmSection } from '../views/settings/SystemLlmSection';\nimport { UpdatesSection } from '../views/settings/UpdatesSection';\nimport { HeadroomSection } from '../views/settings/HeadroomSection';\nimport { TailscaleSettings } from '../components/TailscaleSettings';\nimport { ActivitySection } from '../views/settings/ActivitySection';\n\n// Lazy-load heavy sections that pull in large dependencies.\nimport React from 'react';\n\ntype Props = {\n settings: Settings;\n /** Called when the user changes any top-level setting */\n onSettingsChange?: <K extends keyof Settings>(key: K, value: Settings[K]) => void;\n /** Debounced auto-save to API (key, value) */\n autoSave?: (key: keyof Settings, value: Settings[keyof Settings]) => void;\n /** Optional snapshot for counts / agent list */\n snapshot?: Snapshot | null;\n /** v5.3 compatibility — called after settings are saved (MobileApp.tsx passes refreshSnapshot) */\n onRefresh?: () => Promise<void>;\n};\n\n// ─── Section definitions ─────────────────────────────────────────────────────\n\nfunction GeneralSettings({ settings, onSettingsChange, autoSave }: Props) {\n const patchTop = <K extends keyof Settings>(key: K, value: Settings[K]) => {\n onSettingsChange?.(key, value);\n autoSave?.(key, value);\n };\n const patchUi = (patch: Partial<Settings['ui']>) => {\n onSettingsChange?.('ui', { ...settings.ui, ...patch });\n autoSave?.('ui', { ...settings.ui, ...patch });\n };\n return (\n <GeneralSection\n settings={settings}\n patchUi={patchUi}\n patchTop={patchTop}\n autoSave={autoSave}\n />\n );\n}\n\nfunction ProvidersSettings({ settings, onSettingsChange, autoSave }: Props) {\n // Providers are managed server-side; show a link to the desktop providers view.\n // Mobile can't add/edit providers without more complex auth flow.\n void settings; void onSettingsChange; void autoSave;\n return (\n <div style={{ fontSize: 13, color: 'var(--text-dim)', padding: '8px 0' }}>\n <p style={{ marginBottom: 8 }}>\n Provider management is available on desktop.\n </p>\n <a href=\"/?desktop=1#settings-providers\" className=\"mobile-btn mobile-btn-secondary\" style={{ display: 'inline-block', fontSize: 12, padding: '6px 12px' }}>\n Open desktop to manage providers\n </a>\n </div>\n );\n}\n\nfunction EnvVarsSettings() {\n return <EnvVarsSection />;\n}\n\nfunction MemorySettings() {\n return <MemorySection />;\n}\n\nfunction SystemLlmSettings({ settings, onSettingsChange, autoSave }: Props) {\n const patchTop = <K extends keyof Settings>(key: K, value: Settings[K]) => {\n onSettingsChange?.(key, value);\n autoSave?.(key, value);\n };\n return <SystemLlmSection settings={settings} patchTop={patchTop} />;\n}\n\nfunction UpdatesSettings() {\n return <UpdatesSection />;\n}\n\nfunction HeadroomSettingsSection({ settings }: { settings: Settings }) {\n const [localSettings, setLocalSettings] = useState(settings);\n const [dirty, setDirty] = useState(false);\n return (\n <HeadroomSection\n settings={localSettings}\n setSettings={setLocalSettings as React.Dispatch<React.SetStateAction<Settings>>}\n setDirty={setDirty}\n />\n );\n}\n\nfunction TailscaleSettingsMobile() {\n return <TailscaleSettings />;\n}\n\nfunction AboutSection({ settings }: { settings: Settings }) {\n const about = settings.about || { version: '—', homepage: 'https://github.com/DrB0rk/BizarHarness', license: 'MIT' };\n return <ActivitySection about={about} />;\n}\n\n// ─── Main component ──────────────────────────────────────────────────────────\n\nexport function MobileSettings({ settings, onSettingsChange, autoSave, snapshot }: Props) {\n const [search, setSearch] = useState('');\n const [expandedSection, setExpandedSection] = useState<string | null>(null);\n\n const sections = [\n {\n id: 'general',\n title: 'General',\n icon: Settings2,\n description: 'Theme, accent, layout',\n component: <GeneralSettings settings={settings} onSettingsChange={onSettingsChange} autoSave={autoSave} />,\n },\n {\n id: 'providers',\n title: 'AI Providers',\n icon: Brain,\n description: `${snapshot?.providers?.length ?? 0} configured`,\n component: <ProvidersSettings settings={settings} onSettingsChange={onSettingsChange} autoSave={autoSave} />,\n },\n {\n id: 'env-vars',\n title: 'Environment Variables',\n icon: KeyRound,\n description: 'API keys, secrets',\n component: <EnvVarsSettings />,\n },\n {\n id: 'memory',\n title: 'Memory Vault',\n icon: Brain,\n description: 'Git remote, LightRAG config',\n component: <MemorySettings />,\n },\n {\n id: 'system-llm',\n title: 'System LLM',\n icon: Cpu,\n description: settings.systemLlm?.model || 'not set',\n component: <SystemLlmSettings settings={settings} onSettingsChange={onSettingsChange} autoSave={autoSave} />,\n },\n {\n id: 'updates',\n title: 'Updates',\n icon: RefreshCw,\n description: 'Check for updates',\n component: <UpdatesSettings />,\n },\n {\n id: 'headroom',\n title: 'Headroom',\n icon: Cpu,\n description: settings.headroom?.enabled ? 'Enabled' : 'Disabled',\n component: <HeadroomSettingsSection settings={settings} />,\n },\n {\n id: 'tailscale',\n title: 'Tailscale',\n icon: Globe,\n description: 'Tailscale serve & auth',\n component: <TailscaleSettingsMobile />,\n },\n {\n id: 'about',\n title: 'About',\n icon: Info,\n description: `Bizar v${settings.about?.version || '—'}`,\n component: <AboutSection settings={settings} />,\n },\n ];\n\n const filtered = sections.filter(\n (s) =>\n !search ||\n s.title.toLowerCase().includes(search.toLowerCase()) ||\n s.description.toLowerCase().includes(search.toLowerCase()),\n );\n\n return (\n <div className=\"mobile-settings\">\n <div className=\"mobile-settings-search\">\n <SearchIcon />\n <input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search settings...\"\n aria-label=\"Search settings\"\n />\n </div>\n\n <div className=\"mobile-settings-list\">\n {filtered.map((s) => {\n const Icon = s.icon;\n const isExpanded = expandedSection === s.id;\n return (\n <div key={s.id} className={cn('mobile-settings-item', isExpanded && 'is-expanded')}>\n <button\n className=\"mobile-settings-item-head\"\n onClick={() => setExpandedSection(isExpanded ? null : s.id)}\n aria-expanded={isExpanded}\n aria-controls={`mobile-settings-body-${s.id}`}\n >\n <div className=\"mobile-settings-item-icon\">\n <Icon size={18} />\n </div>\n <div className=\"mobile-settings-item-text\">\n <div className=\"mobile-settings-item-title\">{s.title}</div>\n <div className=\"mobile-settings-item-desc\">{s.description}</div>\n </div>\n <ChevronRight\n size={16}\n className={cn('mobile-settings-item-chevron', isExpanded && 'is-rotated')}\n />\n </button>\n {isExpanded && (\n <div id={`mobile-settings-body-${s.id}`} className=\"mobile-settings-item-body\">\n {s.component}\n </div>\n )}\n </div>\n );\n })}\n {filtered.length === 0 && (\n <div className=\"mobile-settings-empty\">No settings match \"{search}\"</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction SearchIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.35-4.35\" />\n </svg>\n );\n}\n"],"names":["MemorySection","toast","useToast","loading","setLoading","useState","saving","setSaving","initialising","setInitialising","remoteUrl","setRemoteUrl","vaultPath","setVaultPath","vaultInitialised","setVaultInitialised","refreshKey","setRefreshKey","useEffect","cancelled","cfgRes","statusRes","api","_b","_a","onSave","k","err","onInitialise","jsxs","Card","CardTitle","jsx","Brain","CardMeta","Spinner","FolderInput","Button","Loader2","GitBranch","e","Save","GeneralSettings","settings","onSettingsChange","autoSave","patchTop","key","value","patchUi","patch","GeneralSection","ProvidersSettings","EnvVarsSettings","EnvVarsSection","MemorySettings","SystemLlmSettings","SystemLlmSection","UpdatesSettings","UpdatesSection","HeadroomSettingsSection","localSettings","setLocalSettings","dirty","setDirty","HeadroomSection","TailscaleSettingsMobile","TailscaleSettings","AboutSection","about","ActivitySection","MobileSettings","snapshot","search","setSearch","expandedSection","setExpandedSection","filtered","Settings2","KeyRound","Cpu","RefreshCw","_c","Globe","Info","_d","s","SearchIcon","Icon","isExpanded","cn","ChevronRight"],"mappings":"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"}
@@ -1 +1 @@
1
- import{r,j as o}from"./react-vendor-Dn4wqh4Z.js";import{z as C,C as j,D as p,I as T,X as g}from"./icons-BsWXWkEF.js";const d=r.createContext(null);function I(){const t=r.useContext(d);return t||{show:()=>{},info:()=>{},success:()=>{},error:()=>{},warning:()=>{},dismiss:()=>{},toasts:[]}}const h={info:T,success:p,error:j,warning:C};function R({children:t}){const[i,n]=r.useState([]),x=r.useRef(1),c=r.useRef(new Map),u=r.useCallback(s=>{n(m=>m.filter(l=>l.id!==s));const e=c.current.get(s);e&&(clearTimeout(e),c.current.delete(s))},[]),a=r.useCallback((s,e="info",m=4e3)=>{const l=x.current++;if(n(f=>[...f,{id:l,kind:e,message:s}]),m>0){const f=setTimeout(()=>u(l),m);c.current.set(l,f)}},[u]);r.useEffect(()=>()=>{for(const s of c.current.values())clearTimeout(s);c.current.clear()},[]);const v=r.useMemo(()=>({show:a,info:(s,e)=>a(s,"info",e),success:(s,e)=>a(s,"success",e),error:(s,e)=>a(s,"error",e),warning:(s,e)=>a(s,"warning",e),dismiss:u,toasts:i}),[a,u,i]);return o.jsxs(d.Provider,{value:v,children:[t,o.jsx(k,{toasts:i,onDismiss:u})]})}function k({toasts:t,onDismiss:i}){return o.jsx("div",{className:"toast-stack","aria-live":"polite",children:t.map(n=>o.jsx(w,{toast:n,onDismiss:i},n.id))})}function w({toast:t,onDismiss:i}){const n=h[t.kind];return o.jsxs("div",{className:`toast toast-${t.kind}`,role:"alert","aria-live":"assertive","aria-atomic":"true",children:[o.jsx(n,{size:16,className:"toast-icon"}),o.jsx("span",{className:"toast-message",children:t.message}),o.jsx("button",{type:"button",className:"toast-close","aria-label":"Dismiss",onClick:()=>i(t.id),children:o.jsx(g,{size:14})})]})}export{R as T,I as u};
1
+ import{r,j as o}from"./react-vendor-Dn4wqh4Z.js";import{D as C,C as j,E as p,I as T,X as g}from"./icons-COpQr35l.js";const d=r.createContext(null);function I(){const t=r.useContext(d);return t||{show:()=>{},info:()=>{},success:()=>{},error:()=>{},warning:()=>{},dismiss:()=>{},toasts:[]}}const h={info:T,success:p,error:j,warning:C};function R({children:t}){const[i,n]=r.useState([]),x=r.useRef(1),c=r.useRef(new Map),u=r.useCallback(s=>{n(m=>m.filter(l=>l.id!==s));const e=c.current.get(s);e&&(clearTimeout(e),c.current.delete(s))},[]),a=r.useCallback((s,e="info",m=4e3)=>{const l=x.current++;if(n(f=>[...f,{id:l,kind:e,message:s}]),m>0){const f=setTimeout(()=>u(l),m);c.current.set(l,f)}},[u]);r.useEffect(()=>()=>{for(const s of c.current.values())clearTimeout(s);c.current.clear()},[]);const v=r.useMemo(()=>({show:a,info:(s,e)=>a(s,"info",e),success:(s,e)=>a(s,"success",e),error:(s,e)=>a(s,"error",e),warning:(s,e)=>a(s,"warning",e),dismiss:u,toasts:i}),[a,u,i]);return o.jsxs(d.Provider,{value:v,children:[t,o.jsx(k,{toasts:i,onDismiss:u})]})}function k({toasts:t,onDismiss:i}){return o.jsx("div",{className:"toast-stack","aria-live":"polite",children:t.map(n=>o.jsx(w,{toast:n,onDismiss:i},n.id))})}function w({toast:t,onDismiss:i}){const n=h[t.kind];return o.jsxs("div",{className:`toast toast-${t.kind}`,role:"alert","aria-live":"assertive","aria-atomic":"true",children:[o.jsx(n,{size:16,className:"toast-icon"}),o.jsx("span",{className:"toast-message",children:t.message}),o.jsx("button",{type:"button",className:"toast-close","aria-label":"Dismiss",onClick:()=>i(t.id),children:o.jsx(g,{size:14})})]})}export{R as T,I as u};
@@ -1 +1 @@
1
- {"version":3,"file":"Toast-C6f1zFJ2.js","sources":["../../src/web/components/Toast.tsx"],"sourcesContent":["// src/components/Toast.tsx — toast context, provider hook, container.\n\nimport {\n createContext,\n useEffect,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { CheckCircle2, AlertTriangle, AlertCircle, Info, X } from 'lucide-react';\n\nexport type ToastKind = 'info' | 'success' | 'error' | 'warning';\n\nexport type Toast = {\n id: number;\n kind: ToastKind;\n message: string;\n};\n\nexport type ToastApi = {\n show: (message: string, kind?: ToastKind, durationMs?: number) => void;\n info: (message: string, durationMs?: number) => void;\n success: (message: string, durationMs?: number) => void;\n error: (message: string, durationMs?: number) => void;\n warning: (message: string, durationMs?: number) => void;\n dismiss: (id: number) => void;\n toasts: Toast[];\n};\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) {\n // Fallback no-op so tests/Storybook don't crash\n return {\n show: () => undefined,\n info: () => undefined,\n success: () => undefined,\n error: () => undefined,\n warning: () => undefined,\n dismiss: () => undefined,\n toasts: [],\n };\n }\n return ctx;\n}\n\nconst ICONS: Record<ToastKind, typeof Info> = {\n info: Info,\n success: CheckCircle2,\n error: AlertCircle,\n warning: AlertTriangle,\n};\n\nexport function ToastProvider({ children }: { children: React.ReactNode }) {\n const [toasts, setToasts] = useState<Toast[]>([]);\n const idRef = useRef(1);\n const timersRef = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const dismiss = useCallback((id: number) => {\n setToasts((cur) => cur.filter((t) => t.id !== id));\n const timer = timersRef.current.get(id);\n if (timer) {\n clearTimeout(timer);\n timersRef.current.delete(id);\n }\n }, []);\n\n const show = useCallback<ToastApi['show']>(\n (message, kind = 'info', durationMs = 4000) => {\n const id = idRef.current++;\n setToasts((cur) => [...cur, { id, kind, message }]);\n if (durationMs > 0) {\n const timer = setTimeout(() => dismiss(id), durationMs);\n timersRef.current.set(id, timer);\n }\n },\n [dismiss],\n );\n\n useEffect(() => {\n return () => {\n for (const timer of timersRef.current.values()) clearTimeout(timer);\n timersRef.current.clear();\n };\n }, []);\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n info: (m, d) => show(m, 'info', d),\n success: (m, d) => show(m, 'success', d),\n error: (m, d) => show(m, 'error', d),\n warning: (m, d) => show(m, 'warning', d),\n dismiss,\n toasts,\n }),\n [show, dismiss, toasts],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} />\n </ToastContext.Provider>\n );\n}\n\nexport function ToastContainer({\n toasts,\n onDismiss,\n}: {\n toasts: Toast[];\n onDismiss: (id: number) => void;\n}) {\n return (\n <div className=\"toast-stack\" aria-live=\"polite\">\n {toasts.map((t) => (\n <ToastItem key={t.id} toast={t} onDismiss={onDismiss} />\n ))}\n </div>\n );\n}\n\nfunction ToastItem({\n toast,\n onDismiss,\n}: {\n toast: Toast;\n onDismiss: (id: number) => void;\n}) {\n const Icon = ICONS[toast.kind];\n return (\n <div className={`toast toast-${toast.kind}`} role=\"alert\" aria-live=\"assertive\" aria-atomic=\"true\">\n <Icon size={16} className=\"toast-icon\" />\n <span className=\"toast-message\">{toast.message}</span>\n <button\n type=\"button\"\n className=\"toast-close\"\n aria-label=\"Dismiss\"\n onClick={() => onDismiss(toast.id)}\n >\n <X size={14} />\n </button>\n </div>\n );\n}\n"],"names":["ToastContext","createContext","useToast","ctx","useContext","ICONS","Info","CheckCircle2","AlertCircle","AlertTriangle","ToastProvider","children","toasts","setToasts","useState","idRef","useRef","timersRef","dismiss","useCallback","id","cur","t","timer","show","message","kind","durationMs","useEffect","api","useMemo","m","d","jsxs","jsx","ToastContainer","onDismiss","ToastItem","toast","Icon","X"],"mappings":"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"}
1
+ {"version":3,"file":"Toast-CUwkLRms.js","sources":["../../src/web/components/Toast.tsx"],"sourcesContent":["// src/components/Toast.tsx — toast context, provider hook, container.\n\nimport {\n createContext,\n useEffect,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { CheckCircle2, AlertTriangle, AlertCircle, Info, X } from 'lucide-react';\n\nexport type ToastKind = 'info' | 'success' | 'error' | 'warning';\n\nexport type Toast = {\n id: number;\n kind: ToastKind;\n message: string;\n};\n\nexport type ToastApi = {\n show: (message: string, kind?: ToastKind, durationMs?: number) => void;\n info: (message: string, durationMs?: number) => void;\n success: (message: string, durationMs?: number) => void;\n error: (message: string, durationMs?: number) => void;\n warning: (message: string, durationMs?: number) => void;\n dismiss: (id: number) => void;\n toasts: Toast[];\n};\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) {\n // Fallback no-op so tests/Storybook don't crash\n return {\n show: () => undefined,\n info: () => undefined,\n success: () => undefined,\n error: () => undefined,\n warning: () => undefined,\n dismiss: () => undefined,\n toasts: [],\n };\n }\n return ctx;\n}\n\nconst ICONS: Record<ToastKind, typeof Info> = {\n info: Info,\n success: CheckCircle2,\n error: AlertCircle,\n warning: AlertTriangle,\n};\n\nexport function ToastProvider({ children }: { children: React.ReactNode }) {\n const [toasts, setToasts] = useState<Toast[]>([]);\n const idRef = useRef(1);\n const timersRef = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const dismiss = useCallback((id: number) => {\n setToasts((cur) => cur.filter((t) => t.id !== id));\n const timer = timersRef.current.get(id);\n if (timer) {\n clearTimeout(timer);\n timersRef.current.delete(id);\n }\n }, []);\n\n const show = useCallback<ToastApi['show']>(\n (message, kind = 'info', durationMs = 4000) => {\n const id = idRef.current++;\n setToasts((cur) => [...cur, { id, kind, message }]);\n if (durationMs > 0) {\n const timer = setTimeout(() => dismiss(id), durationMs);\n timersRef.current.set(id, timer);\n }\n },\n [dismiss],\n );\n\n useEffect(() => {\n return () => {\n for (const timer of timersRef.current.values()) clearTimeout(timer);\n timersRef.current.clear();\n };\n }, []);\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n info: (m, d) => show(m, 'info', d),\n success: (m, d) => show(m, 'success', d),\n error: (m, d) => show(m, 'error', d),\n warning: (m, d) => show(m, 'warning', d),\n dismiss,\n toasts,\n }),\n [show, dismiss, toasts],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} />\n </ToastContext.Provider>\n );\n}\n\nexport function ToastContainer({\n toasts,\n onDismiss,\n}: {\n toasts: Toast[];\n onDismiss: (id: number) => void;\n}) {\n return (\n <div className=\"toast-stack\" aria-live=\"polite\">\n {toasts.map((t) => (\n <ToastItem key={t.id} toast={t} onDismiss={onDismiss} />\n ))}\n </div>\n );\n}\n\nfunction ToastItem({\n toast,\n onDismiss,\n}: {\n toast: Toast;\n onDismiss: (id: number) => void;\n}) {\n const Icon = ICONS[toast.kind];\n return (\n <div className={`toast toast-${toast.kind}`} role=\"alert\" aria-live=\"assertive\" aria-atomic=\"true\">\n <Icon size={16} className=\"toast-icon\" />\n <span className=\"toast-message\">{toast.message}</span>\n <button\n type=\"button\"\n className=\"toast-close\"\n aria-label=\"Dismiss\"\n onClick={() => onDismiss(toast.id)}\n >\n <X size={14} />\n </button>\n </div>\n );\n}\n"],"names":["ToastContext","createContext","useToast","ctx","useContext","ICONS","Info","CheckCircle2","AlertCircle","AlertTriangle","ToastProvider","children","toasts","setToasts","useState","idRef","useRef","timersRef","dismiss","useCallback","id","cur","t","timer","show","message","kind","durationMs","useEffect","api","useMemo","m","d","jsxs","jsx","ToastContainer","onDismiss","ToastItem","toast","Icon","X"],"mappings":"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"}