@spencerboucher/switchyard 0.7.0 → 0.8.0

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 (60) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge/switchyard-terminal.js +96 -1
  3. package/dist/switchyard.mjs +463 -126
  4. package/package.json +1 -1
  5. package/web-dist/assets/{AutomationView-BIW_pO0M.js → AutomationView-GCthHkym.js} +2 -2
  6. package/web-dist/assets/{AutomationView-BIW_pO0M.js.map → AutomationView-GCthHkym.js.map} +1 -1
  7. package/web-dist/assets/{Badge-BiDp7NKI.js → Badge-CEMI3YyI.js} +2 -2
  8. package/web-dist/assets/{Badge-BiDp7NKI.js.map → Badge-CEMI3YyI.js.map} +1 -1
  9. package/web-dist/assets/{InteractionCard-CWxyDv81.js → InteractionCard-DaLqafmA.js} +2 -2
  10. package/web-dist/assets/{InteractionCard-CWxyDv81.js.map → InteractionCard-DaLqafmA.js.map} +1 -1
  11. package/web-dist/assets/{Menu-BDt1bxwz.js → Menu-BcKiEc6X.js} +2 -2
  12. package/web-dist/assets/{Menu-BDt1bxwz.js.map → Menu-BcKiEc6X.js.map} +1 -1
  13. package/web-dist/assets/{SegmentedControl-D-2BCZzd.js → SegmentedControl-BZPzNU08.js} +2 -2
  14. package/web-dist/assets/{SegmentedControl-D-2BCZzd.js.map → SegmentedControl-BZPzNU08.js.map} +1 -1
  15. package/web-dist/assets/{Tabs-ByiPklAw.js → Tabs-COH5EMql.js} +2 -2
  16. package/web-dist/assets/{Tabs-ByiPklAw.js.map → Tabs-COH5EMql.js.map} +1 -1
  17. package/web-dist/assets/{TextArea-BYr88SnV.js → TextArea-CeMEPZn4.js} +2 -2
  18. package/web-dist/assets/{TextArea-BYr88SnV.js.map → TextArea-CeMEPZn4.js.map} +1 -1
  19. package/web-dist/assets/{attention-Bya8XHKI.js → attention-BAdea2kA.js} +2 -2
  20. package/web-dist/assets/{attention-Bya8XHKI.js.map → attention-BAdea2kA.js.map} +1 -1
  21. package/web-dist/assets/{automations-DiXKRcnE.js → automations-DSFUzB0w.js} +2 -2
  22. package/web-dist/assets/{automations-DiXKRcnE.js.map → automations-DSFUzB0w.js.map} +1 -1
  23. package/web-dist/assets/{board-BrLaJwLJ.js → board-eQ3H8yCp.js} +2 -2
  24. package/web-dist/assets/{board-BrLaJwLJ.js.map → board-eQ3H8yCp.js.map} +1 -1
  25. package/web-dist/assets/code-D7JmYMKe.js +13 -0
  26. package/web-dist/assets/code-D7JmYMKe.js.map +1 -0
  27. package/web-dist/assets/{describe-DnvH6_fl.js → describe-C1YimBXJ.js} +2 -2
  28. package/web-dist/assets/{describe-DnvH6_fl.js.map → describe-C1YimBXJ.js.map} +1 -1
  29. package/web-dist/assets/dist-B_RpNOcK.js +56 -0
  30. package/web-dist/assets/dist-B_RpNOcK.js.map +1 -0
  31. package/web-dist/assets/{enroll-DUDpbJoB.js → enroll-1OCy5r50.js} +2 -2
  32. package/web-dist/assets/{enroll-DUDpbJoB.js.map → enroll-1OCy5r50.js.map} +1 -1
  33. package/web-dist/assets/{index-vIzH1m8u.js → index-BhpEXLav.js} +4 -4
  34. package/web-dist/assets/{index-vIzH1m8u.js.map → index-BhpEXLav.js.map} +1 -1
  35. package/web-dist/assets/index-DUeOwDZ6.css +2 -0
  36. package/web-dist/assets/{logic-xb-QF00E.js → logic-CBvFOyIo.js} +2 -2
  37. package/web-dist/assets/{logic-xb-QF00E.js.map → logic-CBvFOyIo.js.map} +1 -1
  38. package/web-dist/assets/{more-BP5x45cs.js → more-RjVyxPaz.js} +2 -2
  39. package/web-dist/assets/{more-BP5x45cs.js.map → more-RjVyxPaz.js.map} +1 -1
  40. package/web-dist/assets/{now--XjJC37b.js → now-CDjeFSLD.js} +2 -2
  41. package/web-dist/assets/{now--XjJC37b.js.map → now-CDjeFSLD.js.map} +1 -1
  42. package/web-dist/assets/{settings-D8T1esWq.js → settings-tH4UuIfJ.js} +2 -2
  43. package/web-dist/assets/{settings-D8T1esWq.js.map → settings-tH4UuIfJ.js.map} +1 -1
  44. package/web-dist/assets/{shared-CZ7fcu_B.js → shared-CRX6KSBm.js} +2 -2
  45. package/web-dist/assets/{shared-CZ7fcu_B.js.map → shared-CRX6KSBm.js.map} +1 -1
  46. package/web-dist/assets/states-DXozulGZ.js +2 -0
  47. package/web-dist/assets/states-DXozulGZ.js.map +1 -0
  48. package/web-dist/assets/{terminal-login-BJxV78au.js → terminal-login-CW2rE50W.js} +2 -2
  49. package/web-dist/assets/{terminal-login-BJxV78au.js.map → terminal-login-CW2rE50W.js.map} +1 -1
  50. package/web-dist/assets/{useNow-jxdoOC_1.js → useNow-C_B7JgMM.js} +2 -2
  51. package/web-dist/assets/{useNow-jxdoOC_1.js.map → useNow-C_B7JgMM.js.map} +1 -1
  52. package/web-dist/index.html +4 -4
  53. package/web-dist/sw.js +1 -1
  54. package/web-dist/assets/code-DKLO8bzz.js +0 -11
  55. package/web-dist/assets/code-DKLO8bzz.js.map +0 -1
  56. package/web-dist/assets/dist-BE_V9dwJ.js +0 -56
  57. package/web-dist/assets/dist-BE_V9dwJ.js.map +0 -1
  58. package/web-dist/assets/index-DvRcMGEe.css +0 -2
  59. package/web-dist/assets/states--geW2Ntl.js +0 -2
  60. package/web-dist/assets/states--geW2Ntl.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{_ as e,m as t,t as n}from"./jsx-runtime-D13aM9HY.js";import{i as r}from"./Badge-BiDp7NKI.js";import{h as i}from"./states--geW2Ntl.js";import{m as a}from"./index-vIzH1m8u.js";var o=r(),s=e(t(),1),c=n(),l=(0,s.forwardRef)(function({label:e,icon:t,variant:n=`ghost`,size:r=`md`,className:a,type:o,...s},l){return(0,c.jsx)(`button`,{ref:l,type:o??`button`,"aria-label":e,title:e,className:i(`tap inline-flex shrink-0 items-center justify-center rounded-md border transition-colors`,`disabled:opacity-55 disabled:pointer-events-none`,r===`sm`?`size-8`:`size-9`,n===`ghost`&&`border-transparent text-muted hover:bg-surface-2 hover:text-text`,n===`secondary`&&`border-border bg-surface text-text hover:bg-surface-2`,n===`danger`&&`border-transparent text-danger hover:bg-danger-soft`,a),...s,children:t})}),u=`a[href], area[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]`;function d(e){return[...e.querySelectorAll(u)].filter(e=>!e.hasAttribute(`inert`)&&e.getClientRects().length>0)}function f(e,t,n,r){let i=(0,s.useRef)(n);i.current=n;let a=(0,s.useRef)(null),o=(0,s.useRef)(!1);t&&!o.current&&typeof document<`u`&&(a.current=document.activeElement),o.current=t,(0,s.useEffect)(()=>{if(!t)return;let n=e.current;if(!n)return;let o=a.current,s=o&&!n.contains(o)?o:document.activeElement,c=r?.current??d(n)[0]??n,l=requestAnimationFrame(()=>{(r?.current||!n.contains(document.activeElement))&&c.focus({preventScroll:!0})}),u=e=>{if(e.key===`Escape`&&i.current){e.stopPropagation(),i.current();return}if(e.key!==`Tab`)return;let t=d(n);if(t.length===0){e.preventDefault(),n.focus();return}let r=t[0],a=t[t.length-1],o=document.activeElement;e.shiftKey&&(o===r||!n.contains(o))?(e.preventDefault(),a.focus()):e.shiftKey||o!==a&&n.contains(o)||(e.preventDefault(),r.focus())};return n.addEventListener(`keydown`,u),()=>{cancelAnimationFrame(l),n.removeEventListener(`keydown`,u),s&&document.contains(s)&&s.focus({preventScroll:!0})}},[t,e,r])}function p(e){(0,s.useEffect)(()=>{if(!e)return;let t=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{document.body.style.overflow=t}},[e])}function m({placement:e,open:t,onClose:n,title:r,description:u,children:d,footer:m,initialFocus:h,dismissOnBackdrop:g=!0,headerActions:_,className:v,style:y}){let b=(0,s.useRef)(null),x=(0,s.useId)(),S=(0,s.useId)();return f(b,t,n,h),p(t),t?(0,o.createPortal)((0,c.jsxs)(`div`,{className:i(`fixed inset-0 z-50 flex`,e===`right`&&`justify-end`,e===`bottom`&&`items-end`,e===`center`&&`items-center justify-center p-4`),children:[(0,c.jsx)(`div`,{className:`absolute inset-0 bg-overlay`,"aria-hidden":!0,onClick:g?n:void 0}),(0,c.jsxs)(`div`,{ref:b,role:`dialog`,"aria-modal":`true`,"aria-labelledby":x,"aria-describedby":u?S:void 0,tabIndex:-1,style:y,className:i(`relative flex flex-col bg-surface text-text shadow-pop outline-none`,e===`right`&&`h-full max-w-full border-l border-border safe-pt safe-pb`,e===`bottom`&&`max-h-[90dvh] w-full rounded-t-xl border-t border-border safe-pb`,e===`center`&&`max-h-[min(90dvh,720px)] w-full max-w-lg rounded-xl border border-border`,v),children:[e===`bottom`&&(0,c.jsx)(`div`,{className:`flex justify-center pt-2`,"aria-hidden":!0,children:(0,c.jsx)(`div`,{className:`h-1 w-10 rounded-full bg-border-strong`})}),(0,c.jsxs)(`div`,{className:`flex items-start gap-2 border-b border-border px-4 py-3`,children:[(0,c.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,c.jsx)(`h2`,{id:x,className:`text-base font-semibold leading-6`,children:r}),u&&(0,c.jsx)(`p`,{id:S,className:`text-sm text-muted`,children:u})]}),_,(0,c.jsx)(l,{label:`Close`,icon:(0,c.jsx)(a,{className:`size-4`}),onClick:n})]}),(0,c.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-3`,children:d}),m&&(0,c.jsx)(`div`,{className:`flex flex-wrap items-center justify-end gap-2 border-t border-border px-4 py-3`,children:m})]})]}),document.body):null}function h({width:e=520,...t}){return(0,c.jsx)(m,{...t,placement:`right`,style:{width:`min(${e}px, 100vw)`}})}function g(e){return(0,c.jsx)(m,{...e,placement:`bottom`})}function _(e){return(0,c.jsx)(m,{...e,placement:`center`})}var v=(0,s.forwardRef)(function({maxHeight:e=`33vh`,minRows:t=1,className:n,value:r,style:a,...o},l){let u=(0,s.useRef)(null),d=(0,s.useCallback)(e=>{u.current=e,typeof l==`function`?l(e):l&&(l.current=e)},[l]);return(0,s.useLayoutEffect)(()=>{let e=u.current;e&&(e.style.height=`auto`,e.style.height=`${e.scrollHeight+(e.offsetHeight-e.clientHeight)}px`)},[r]),(0,c.jsx)(`textarea`,{ref:d,rows:t,value:r,style:{maxHeight:e,...a},className:i(`block w-full resize-none overflow-y-auto rounded-md border border-border bg-surface px-3 py-2 text-base text-text placeholder:text-subtle md:text-sm`,`focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0`,n),...o})}),y=(0,s.forwardRef)(function({className:e,...t},n){return(0,c.jsx)(`input`,{ref:n,className:i(`tap block h-9 w-full rounded-md border border-border bg-surface px-3 text-base text-text placeholder:text-subtle md:text-sm`,`focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0`,e),...t})});export{g as a,h as i,y as n,f as o,_ as r,l as s,v as t};
2
- //# sourceMappingURL=TextArea-BYr88SnV.js.map
1
+ import{_ as e,m as t,t as n}from"./jsx-runtime-D13aM9HY.js";import{i as r}from"./Badge-CEMI3YyI.js";import{h as i}from"./states-DXozulGZ.js";import{m as a}from"./index-BhpEXLav.js";var o=r(),s=e(t(),1),c=n(),l=(0,s.forwardRef)(function({label:e,icon:t,variant:n=`ghost`,size:r=`md`,className:a,type:o,...s},l){return(0,c.jsx)(`button`,{ref:l,type:o??`button`,"aria-label":e,title:e,className:i(`tap inline-flex shrink-0 items-center justify-center rounded-md border transition-colors`,`disabled:opacity-55 disabled:pointer-events-none`,r===`sm`?`size-8`:`size-9`,n===`ghost`&&`border-transparent text-muted hover:bg-surface-2 hover:text-text`,n===`secondary`&&`border-border bg-surface text-text hover:bg-surface-2`,n===`danger`&&`border-transparent text-danger hover:bg-danger-soft`,a),...s,children:t})}),u=`a[href], area[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]`;function d(e){return[...e.querySelectorAll(u)].filter(e=>!e.hasAttribute(`inert`)&&e.getClientRects().length>0)}function f(e,t,n,r){let i=(0,s.useRef)(n);i.current=n;let a=(0,s.useRef)(null),o=(0,s.useRef)(!1);t&&!o.current&&typeof document<`u`&&(a.current=document.activeElement),o.current=t,(0,s.useEffect)(()=>{if(!t)return;let n=e.current;if(!n)return;let o=a.current,s=o&&!n.contains(o)?o:document.activeElement,c=r?.current??d(n)[0]??n,l=requestAnimationFrame(()=>{(r?.current||!n.contains(document.activeElement))&&c.focus({preventScroll:!0})}),u=e=>{if(e.key===`Escape`&&i.current){e.stopPropagation(),i.current();return}if(e.key!==`Tab`)return;let t=d(n);if(t.length===0){e.preventDefault(),n.focus();return}let r=t[0],a=t[t.length-1],o=document.activeElement;e.shiftKey&&(o===r||!n.contains(o))?(e.preventDefault(),a.focus()):e.shiftKey||o!==a&&n.contains(o)||(e.preventDefault(),r.focus())};return n.addEventListener(`keydown`,u),()=>{cancelAnimationFrame(l),n.removeEventListener(`keydown`,u),s&&document.contains(s)&&s.focus({preventScroll:!0})}},[t,e,r])}function p(e){(0,s.useEffect)(()=>{if(!e)return;let t=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{document.body.style.overflow=t}},[e])}function m({placement:e,open:t,onClose:n,title:r,description:u,children:d,footer:m,initialFocus:h,dismissOnBackdrop:g=!0,headerActions:_,className:v,style:y}){let b=(0,s.useRef)(null),x=(0,s.useId)(),S=(0,s.useId)();return f(b,t,n,h),p(t),t?(0,o.createPortal)((0,c.jsxs)(`div`,{className:i(`fixed inset-0 z-50 flex`,e===`right`&&`justify-end`,e===`bottom`&&`items-end`,e===`center`&&`items-center justify-center p-4`),children:[(0,c.jsx)(`div`,{className:`absolute inset-0 bg-overlay`,"aria-hidden":!0,onClick:g?n:void 0}),(0,c.jsxs)(`div`,{ref:b,role:`dialog`,"aria-modal":`true`,"aria-labelledby":x,"aria-describedby":u?S:void 0,tabIndex:-1,style:y,className:i(`relative flex flex-col bg-surface text-text shadow-pop outline-none`,e===`right`&&`h-full max-w-full border-l border-border safe-pt safe-pb`,e===`bottom`&&`max-h-[90dvh] w-full rounded-t-xl border-t border-border safe-pb`,e===`center`&&`max-h-[min(90dvh,720px)] w-full max-w-lg rounded-xl border border-border`,v),children:[e===`bottom`&&(0,c.jsx)(`div`,{className:`flex justify-center pt-2`,"aria-hidden":!0,children:(0,c.jsx)(`div`,{className:`h-1 w-10 rounded-full bg-border-strong`})}),(0,c.jsxs)(`div`,{className:`flex items-start gap-2 border-b border-border px-4 py-3`,children:[(0,c.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,c.jsx)(`h2`,{id:x,className:`text-base font-semibold leading-6`,children:r}),u&&(0,c.jsx)(`p`,{id:S,className:`text-sm text-muted`,children:u})]}),_,(0,c.jsx)(l,{label:`Close`,icon:(0,c.jsx)(a,{className:`size-4`}),onClick:n})]}),(0,c.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-3`,children:d}),m&&(0,c.jsx)(`div`,{className:`flex flex-wrap items-center justify-end gap-2 border-t border-border px-4 py-3`,children:m})]})]}),document.body):null}function h({width:e=520,...t}){return(0,c.jsx)(m,{...t,placement:`right`,style:{width:`min(${e}px, 100vw)`}})}function g(e){return(0,c.jsx)(m,{...e,placement:`bottom`})}function _(e){return(0,c.jsx)(m,{...e,placement:`center`})}var v=(0,s.forwardRef)(function({maxHeight:e=`33vh`,minRows:t=1,className:n,value:r,style:a,...o},l){let u=(0,s.useRef)(null),d=(0,s.useCallback)(e=>{u.current=e,typeof l==`function`?l(e):l&&(l.current=e)},[l]);return(0,s.useLayoutEffect)(()=>{let e=u.current;e&&(e.style.height=`auto`,e.style.height=`${e.scrollHeight+(e.offsetHeight-e.clientHeight)}px`)},[r]),(0,c.jsx)(`textarea`,{ref:d,rows:t,value:r,style:{maxHeight:e,...a},className:i(`block w-full resize-none overflow-y-auto rounded-md border border-border bg-surface px-3 py-2 text-base text-text placeholder:text-subtle md:text-sm`,`focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0`,n),...o})}),y=(0,s.forwardRef)(function({className:e,...t},n){return(0,c.jsx)(`input`,{ref:n,className:i(`tap block h-9 w-full rounded-md border border-border bg-surface px-3 text-base text-text placeholder:text-subtle md:text-sm`,`focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0`,e),...t})});export{g as a,h as i,y as n,f as o,_ as r,l as s,v as t};
2
+ //# sourceMappingURL=TextArea-CeMEPZn4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea-BYr88SnV.js","names":["forwardRef","useRef","useRef","useId","createPortal","forwardRef","useRef","useCallback","__toESM","require_react","require_jsx_runtime","require_react_dom","cn","X","import_react_dom","import_react","import_jsx_runtime","IconButton","rest","type","label","size","variant","className","icon","FOCUSABLE","focusableWithin","root","el","useFocusTrap","escRef","onEscape","triggerRef","wasActive","active","ref","captured","previous","target","initialFocus","raf","onKey","items","first","last","activeEl","useScrollLock","prev","Surface","titleId","descId","open","onClose","placement","dismissOnBackdrop","description","title","headerActions","footer","Drawer","props","width","Sheet","Dialog","TextArea","inner","setRef","value","minRows","style","TextInput"],"sources":["../../src/components/ui/IconButton.tsx","../../src/components/ui/useFocusTrap.ts","../../src/components/ui/Overlay.tsx","../../src/components/ui/TextArea.tsx"],"sourcesContent":["import { type ButtonHTMLAttributes, forwardRef, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface IconButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"aria-label\"> {\n /** Accessible name (also the tooltip). Required: icon-only controls need a label. */\n label: string;\n icon: ReactNode;\n variant?: \"ghost\" | \"secondary\" | \"danger\";\n size?: \"sm\" | \"md\";\n}\n\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n { label, icon, variant = \"ghost\", size = \"md\", className, type, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type={type ?? \"button\"}\n aria-label={label}\n title={label}\n className={cn(\n \"tap inline-flex shrink-0 items-center justify-center rounded-md border transition-colors\",\n \"disabled:opacity-55 disabled:pointer-events-none\",\n size === \"sm\" ? \"size-8\" : \"size-9\",\n variant === \"ghost\" && \"border-transparent text-muted hover:bg-surface-2 hover:text-text\",\n variant === \"secondary\" && \"border-border bg-surface text-text hover:bg-surface-2\",\n variant === \"danger\" && \"border-transparent text-danger hover:bg-danger-soft\",\n className,\n )}\n {...rest}\n >\n {icon}\n </button>\n );\n});\n","import { type RefObject, useEffect, useRef } from \"react\";\n\nconst FOCUSABLE =\n 'a[href], area[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]';\n\nexport function focusableWithin(root: HTMLElement): HTMLElement[] {\n return [...root.querySelectorAll<HTMLElement>(FOCUSABLE)].filter(\n (el) => !el.hasAttribute(\"inert\") && el.getClientRects().length > 0,\n );\n}\n\n/**\n * Modal focus management: moves focus into `ref` when `active`, keeps Tab\n * inside it, closes on Escape, and returns focus to the element that had it\n * (the trigger) when deactivated.\n */\nexport function useFocusTrap(\n ref: RefObject<HTMLElement | null>,\n active: boolean,\n onEscape?: () => void,\n initialFocus?: RefObject<HTMLElement | null>,\n): void {\n const escRef = useRef(onEscape);\n escRef.current = onEscape;\n // Remember the trigger while rendering the opening frame: by the time effects run, an\n // `autoFocus` field inside the surface has already taken focus.\n const triggerRef = useRef<HTMLElement | null>(null);\n const wasActive = useRef(false);\n if (active && !wasActive.current && typeof document !== \"undefined\") {\n triggerRef.current = document.activeElement as HTMLElement | null;\n }\n wasActive.current = active;\n useEffect(() => {\n if (!active) return;\n const root = ref.current;\n if (!root) return;\n const captured = triggerRef.current;\n const previous =\n captured && !root.contains(captured)\n ? captured\n : (document.activeElement as HTMLElement | null);\n const target = initialFocus?.current ?? focusableWithin(root)[0] ?? root;\n // Defer so the element is laid out (portals, transitions).\n // An `autoFocus` field inside keeps its focus unless an explicit initial focus was given.\n const raf = requestAnimationFrame(() => {\n if (!initialFocus?.current && root.contains(document.activeElement)) return;\n target.focus({ preventScroll: true });\n });\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && escRef.current) {\n e.stopPropagation();\n escRef.current();\n return;\n }\n if (e.key !== \"Tab\") return;\n const items = focusableWithin(root);\n if (items.length === 0) {\n e.preventDefault();\n root.focus();\n return;\n }\n const first = items[0]!;\n const last = items[items.length - 1]!;\n const activeEl = document.activeElement;\n if (e.shiftKey && (activeEl === first || !root.contains(activeEl))) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && (activeEl === last || !root.contains(activeEl))) {\n e.preventDefault();\n first.focus();\n }\n };\n root.addEventListener(\"keydown\", onKey);\n return () => {\n cancelAnimationFrame(raf);\n root.removeEventListener(\"keydown\", onKey);\n if (previous && document.contains(previous)) previous.focus({ preventScroll: true });\n };\n }, [active, ref, initialFocus]);\n}\n\n/** Lock body scroll while a modal surface is open. */\nexport function useScrollLock(active: boolean): void {\n useEffect(() => {\n if (!active) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = prev;\n };\n }, [active]);\n}\n","import { X } from \"lucide-react\";\nimport { type ReactNode, type RefObject, useId, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../lib/cn\";\nimport { IconButton } from \"./IconButton\";\nimport { useFocusTrap, useScrollLock } from \"./useFocusTrap\";\n\nexport interface OverlayProps {\n open: boolean;\n onClose: () => void;\n /** Visible heading; also the accessible name. */\n title: ReactNode;\n /** Accessible description shown under the title. */\n description?: ReactNode;\n children?: ReactNode;\n /** Sticky footer (actions). */\n footer?: ReactNode;\n /** Element to focus first (defaults to the first focusable). */\n initialFocus?: RefObject<HTMLElement | null>;\n /** Close when the backdrop is clicked (default true). */\n dismissOnBackdrop?: boolean;\n /** Extra header controls before the close button. */\n headerActions?: ReactNode;\n className?: string;\n}\n\ntype Placement = \"right\" | \"bottom\" | \"center\";\n\nfunction Surface({\n placement,\n open,\n onClose,\n title,\n description,\n children,\n footer,\n initialFocus,\n dismissOnBackdrop = true,\n headerActions,\n className,\n style,\n}: OverlayProps & { placement: Placement; style?: React.CSSProperties }) {\n const ref = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const descId = useId();\n useFocusTrap(ref, open, onClose, initialFocus);\n useScrollLock(open);\n if (!open) return null;\n return createPortal(\n <div\n className={cn(\n \"fixed inset-0 z-50 flex\",\n placement === \"right\" && \"justify-end\",\n placement === \"bottom\" && \"items-end\",\n placement === \"center\" && \"items-center justify-center p-4\",\n )}\n >\n <div\n className=\"absolute inset-0 bg-overlay\"\n aria-hidden\n onClick={dismissOnBackdrop ? onClose : undefined}\n />\n <div\n ref={ref}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n style={style}\n className={cn(\n \"relative flex flex-col bg-surface text-text shadow-pop outline-none\",\n placement === \"right\" && \"h-full max-w-full border-l border-border safe-pt safe-pb\",\n placement === \"bottom\" &&\n \"max-h-[90dvh] w-full rounded-t-xl border-t border-border safe-pb\",\n placement === \"center\" &&\n \"max-h-[min(90dvh,720px)] w-full max-w-lg rounded-xl border border-border\",\n className,\n )}\n >\n {placement === \"bottom\" && (\n <div className=\"flex justify-center pt-2\" aria-hidden>\n <div className=\"h-1 w-10 rounded-full bg-border-strong\" />\n </div>\n )}\n <div className=\"flex items-start gap-2 border-b border-border px-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <h2 id={titleId} className=\"text-base font-semibold leading-6\">\n {title}\n </h2>\n {description && (\n <p id={descId} className=\"text-sm text-muted\">\n {description}\n </p>\n )}\n </div>\n {headerActions}\n <IconButton label=\"Close\" icon={<X className=\"size-4\" />} onClick={onClose} />\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-3\">\n {children}\n </div>\n {footer && (\n <div className=\"flex flex-wrap items-center justify-end gap-2 border-t border-border px-4 py-3\">\n {footer}\n </div>\n )}\n </div>\n </div>,\n document.body,\n );\n}\n\nexport interface DrawerProps extends OverlayProps {\n /** Width in px (default 520, the task drawer width). Never wider than the viewport. */\n width?: number;\n}\n\n/** Right-side drawer with focus trap, Escape to close, and focus returned to the trigger. */\nexport function Drawer({ width = 520, ...props }: DrawerProps) {\n return <Surface {...props} placement=\"right\" style={{ width: `min(${width}px, 100vw)` }} />;\n}\n\n/** Bottom sheet for mobile; same accessibility behavior as Drawer. */\nexport function Sheet(props: OverlayProps) {\n return <Surface {...props} placement=\"bottom\" />;\n}\n\n/** Centered modal dialog. */\nexport function Dialog(props: OverlayProps) {\n return <Surface {...props} placement=\"center\" />;\n}\n","import {\n forwardRef,\n type TextareaHTMLAttributes,\n useCallback,\n useLayoutEffect,\n useRef,\n} from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface TextAreaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Max height before scrolling internally (CSS length; default \"33vh\"). */\n maxHeight?: string;\n /** Minimum rows (default 1). */\n minRows?: number;\n}\n\n/** Auto-growing textarea that scrolls internally past `maxHeight`. */\nexport const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function TextArea(\n { maxHeight = \"33vh\", minRows = 1, className, value, style, ...rest },\n ref,\n) {\n const inner = useRef<HTMLTextAreaElement | null>(null);\n const setRef = useCallback(\n (el: HTMLTextAreaElement | null) => {\n inner.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) ref.current = el;\n },\n [ref],\n );\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-measure when the value changes\n useLayoutEffect(() => {\n const el = inner.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight + (el.offsetHeight - el.clientHeight)}px`;\n }, [value]);\n return (\n <textarea\n ref={setRef}\n rows={minRows}\n value={value}\n style={{ maxHeight, ...style }}\n className={cn(\n \"block w-full resize-none overflow-y-auto rounded-md border border-border bg-surface px-3 py-2 text-base text-text placeholder:text-subtle md:text-sm\",\n \"focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0\",\n className,\n )}\n {...rest}\n />\n );\n});\n\n/** Single-line input with the same styling. */\nexport const TextInput = forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(\n function TextInput({ className, ...rest }, ref) {\n return (\n <input\n ref={ref}\n className={cn(\n \"tap block h-9 w-full rounded-md border border-border bg-surface px-3 text-base text-text placeholder:text-subtle md:text-sm\",\n \"focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0\",\n className,\n )}\n {...rest}\n />\n );\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"TextArea-CeMEPZn4.js","names":["forwardRef","useRef","useRef","useId","createPortal","forwardRef","useRef","useCallback","__toESM","require_react","require_jsx_runtime","require_react_dom","cn","X","import_react_dom","import_react","import_jsx_runtime","IconButton","rest","type","label","size","variant","className","icon","FOCUSABLE","focusableWithin","root","el","useFocusTrap","escRef","onEscape","triggerRef","wasActive","active","ref","captured","previous","target","initialFocus","raf","onKey","items","first","last","activeEl","useScrollLock","prev","Surface","titleId","descId","open","onClose","placement","dismissOnBackdrop","description","title","headerActions","footer","Drawer","props","width","Sheet","Dialog","TextArea","inner","setRef","value","minRows","style","TextInput"],"sources":["../../src/components/ui/IconButton.tsx","../../src/components/ui/useFocusTrap.ts","../../src/components/ui/Overlay.tsx","../../src/components/ui/TextArea.tsx"],"sourcesContent":["import { type ButtonHTMLAttributes, forwardRef, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface IconButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"aria-label\"> {\n /** Accessible name (also the tooltip). Required: icon-only controls need a label. */\n label: string;\n icon: ReactNode;\n variant?: \"ghost\" | \"secondary\" | \"danger\";\n size?: \"sm\" | \"md\";\n}\n\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n { label, icon, variant = \"ghost\", size = \"md\", className, type, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type={type ?? \"button\"}\n aria-label={label}\n title={label}\n className={cn(\n \"tap inline-flex shrink-0 items-center justify-center rounded-md border transition-colors\",\n \"disabled:opacity-55 disabled:pointer-events-none\",\n size === \"sm\" ? \"size-8\" : \"size-9\",\n variant === \"ghost\" && \"border-transparent text-muted hover:bg-surface-2 hover:text-text\",\n variant === \"secondary\" && \"border-border bg-surface text-text hover:bg-surface-2\",\n variant === \"danger\" && \"border-transparent text-danger hover:bg-danger-soft\",\n className,\n )}\n {...rest}\n >\n {icon}\n </button>\n );\n});\n","import { type RefObject, useEffect, useRef } from \"react\";\n\nconst FOCUSABLE =\n 'a[href], area[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]';\n\nexport function focusableWithin(root: HTMLElement): HTMLElement[] {\n return [...root.querySelectorAll<HTMLElement>(FOCUSABLE)].filter(\n (el) => !el.hasAttribute(\"inert\") && el.getClientRects().length > 0,\n );\n}\n\n/**\n * Modal focus management: moves focus into `ref` when `active`, keeps Tab\n * inside it, closes on Escape, and returns focus to the element that had it\n * (the trigger) when deactivated.\n */\nexport function useFocusTrap(\n ref: RefObject<HTMLElement | null>,\n active: boolean,\n onEscape?: () => void,\n initialFocus?: RefObject<HTMLElement | null>,\n): void {\n const escRef = useRef(onEscape);\n escRef.current = onEscape;\n // Remember the trigger while rendering the opening frame: by the time effects run, an\n // `autoFocus` field inside the surface has already taken focus.\n const triggerRef = useRef<HTMLElement | null>(null);\n const wasActive = useRef(false);\n if (active && !wasActive.current && typeof document !== \"undefined\") {\n triggerRef.current = document.activeElement as HTMLElement | null;\n }\n wasActive.current = active;\n useEffect(() => {\n if (!active) return;\n const root = ref.current;\n if (!root) return;\n const captured = triggerRef.current;\n const previous =\n captured && !root.contains(captured)\n ? captured\n : (document.activeElement as HTMLElement | null);\n const target = initialFocus?.current ?? focusableWithin(root)[0] ?? root;\n // Defer so the element is laid out (portals, transitions).\n // An `autoFocus` field inside keeps its focus unless an explicit initial focus was given.\n const raf = requestAnimationFrame(() => {\n if (!initialFocus?.current && root.contains(document.activeElement)) return;\n target.focus({ preventScroll: true });\n });\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && escRef.current) {\n e.stopPropagation();\n escRef.current();\n return;\n }\n if (e.key !== \"Tab\") return;\n const items = focusableWithin(root);\n if (items.length === 0) {\n e.preventDefault();\n root.focus();\n return;\n }\n const first = items[0]!;\n const last = items[items.length - 1]!;\n const activeEl = document.activeElement;\n if (e.shiftKey && (activeEl === first || !root.contains(activeEl))) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && (activeEl === last || !root.contains(activeEl))) {\n e.preventDefault();\n first.focus();\n }\n };\n root.addEventListener(\"keydown\", onKey);\n return () => {\n cancelAnimationFrame(raf);\n root.removeEventListener(\"keydown\", onKey);\n if (previous && document.contains(previous)) previous.focus({ preventScroll: true });\n };\n }, [active, ref, initialFocus]);\n}\n\n/** Lock body scroll while a modal surface is open. */\nexport function useScrollLock(active: boolean): void {\n useEffect(() => {\n if (!active) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = prev;\n };\n }, [active]);\n}\n","import { X } from \"lucide-react\";\nimport { type ReactNode, type RefObject, useId, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../lib/cn\";\nimport { IconButton } from \"./IconButton\";\nimport { useFocusTrap, useScrollLock } from \"./useFocusTrap\";\n\nexport interface OverlayProps {\n open: boolean;\n onClose: () => void;\n /** Visible heading; also the accessible name. */\n title: ReactNode;\n /** Accessible description shown under the title. */\n description?: ReactNode;\n children?: ReactNode;\n /** Sticky footer (actions). */\n footer?: ReactNode;\n /** Element to focus first (defaults to the first focusable). */\n initialFocus?: RefObject<HTMLElement | null>;\n /** Close when the backdrop is clicked (default true). */\n dismissOnBackdrop?: boolean;\n /** Extra header controls before the close button. */\n headerActions?: ReactNode;\n className?: string;\n}\n\ntype Placement = \"right\" | \"bottom\" | \"center\";\n\nfunction Surface({\n placement,\n open,\n onClose,\n title,\n description,\n children,\n footer,\n initialFocus,\n dismissOnBackdrop = true,\n headerActions,\n className,\n style,\n}: OverlayProps & { placement: Placement; style?: React.CSSProperties }) {\n const ref = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const descId = useId();\n useFocusTrap(ref, open, onClose, initialFocus);\n useScrollLock(open);\n if (!open) return null;\n return createPortal(\n <div\n className={cn(\n \"fixed inset-0 z-50 flex\",\n placement === \"right\" && \"justify-end\",\n placement === \"bottom\" && \"items-end\",\n placement === \"center\" && \"items-center justify-center p-4\",\n )}\n >\n <div\n className=\"absolute inset-0 bg-overlay\"\n aria-hidden\n onClick={dismissOnBackdrop ? onClose : undefined}\n />\n <div\n ref={ref}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n style={style}\n className={cn(\n \"relative flex flex-col bg-surface text-text shadow-pop outline-none\",\n placement === \"right\" && \"h-full max-w-full border-l border-border safe-pt safe-pb\",\n placement === \"bottom\" &&\n \"max-h-[90dvh] w-full rounded-t-xl border-t border-border safe-pb\",\n placement === \"center\" &&\n \"max-h-[min(90dvh,720px)] w-full max-w-lg rounded-xl border border-border\",\n className,\n )}\n >\n {placement === \"bottom\" && (\n <div className=\"flex justify-center pt-2\" aria-hidden>\n <div className=\"h-1 w-10 rounded-full bg-border-strong\" />\n </div>\n )}\n <div className=\"flex items-start gap-2 border-b border-border px-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <h2 id={titleId} className=\"text-base font-semibold leading-6\">\n {title}\n </h2>\n {description && (\n <p id={descId} className=\"text-sm text-muted\">\n {description}\n </p>\n )}\n </div>\n {headerActions}\n <IconButton label=\"Close\" icon={<X className=\"size-4\" />} onClick={onClose} />\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-3\">\n {children}\n </div>\n {footer && (\n <div className=\"flex flex-wrap items-center justify-end gap-2 border-t border-border px-4 py-3\">\n {footer}\n </div>\n )}\n </div>\n </div>,\n document.body,\n );\n}\n\nexport interface DrawerProps extends OverlayProps {\n /** Width in px (default 520, the task drawer width). Never wider than the viewport. */\n width?: number;\n}\n\n/** Right-side drawer with focus trap, Escape to close, and focus returned to the trigger. */\nexport function Drawer({ width = 520, ...props }: DrawerProps) {\n return <Surface {...props} placement=\"right\" style={{ width: `min(${width}px, 100vw)` }} />;\n}\n\n/** Bottom sheet for mobile; same accessibility behavior as Drawer. */\nexport function Sheet(props: OverlayProps) {\n return <Surface {...props} placement=\"bottom\" />;\n}\n\n/** Centered modal dialog. */\nexport function Dialog(props: OverlayProps) {\n return <Surface {...props} placement=\"center\" />;\n}\n","import {\n forwardRef,\n type TextareaHTMLAttributes,\n useCallback,\n useLayoutEffect,\n useRef,\n} from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface TextAreaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Max height before scrolling internally (CSS length; default \"33vh\"). */\n maxHeight?: string;\n /** Minimum rows (default 1). */\n minRows?: number;\n}\n\n/** Auto-growing textarea that scrolls internally past `maxHeight`. */\nexport const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function TextArea(\n { maxHeight = \"33vh\", minRows = 1, className, value, style, ...rest },\n ref,\n) {\n const inner = useRef<HTMLTextAreaElement | null>(null);\n const setRef = useCallback(\n (el: HTMLTextAreaElement | null) => {\n inner.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) ref.current = el;\n },\n [ref],\n );\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-measure when the value changes\n useLayoutEffect(() => {\n const el = inner.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight + (el.offsetHeight - el.clientHeight)}px`;\n }, [value]);\n return (\n <textarea\n ref={setRef}\n rows={minRows}\n value={value}\n style={{ maxHeight, ...style }}\n className={cn(\n \"block w-full resize-none overflow-y-auto rounded-md border border-border bg-surface px-3 py-2 text-base text-text placeholder:text-subtle md:text-sm\",\n \"focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0\",\n className,\n )}\n {...rest}\n />\n );\n});\n\n/** Single-line input with the same styling. */\nexport const TextInput = forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(\n function TextInput({ className, ...rest }, ref) {\n return (\n <input\n ref={ref}\n className={cn(\n \"tap block h-9 w-full rounded-md border border-border bg-surface px-3 text-base text-text placeholder:text-subtle md:text-sm\",\n \"focus:border-accent focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-0\",\n className,\n )}\n {...rest}\n />\n );\n },\n);\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{_ as e,i as t,m as n,n as r,t as i}from"./jsx-runtime-D13aM9HY.js";import{D as a,P as o,T as s,U as c,W as l,c as u,i as d,l as f,n as p,o as m,p as h,t as g,u as _}from"./states--geW2Ntl.js";import{t as v}from"./server-SxFxIScj.js";import{t as y}from"./external-link-3jjTxo92.js";import{n as b,t as x}from"./InteractionCard-CWxyDv81.js";import{F as S,P as C,f as w,l as T,s as E,t as D,u as O,w as k}from"./index-vIzH1m8u.js";import{t as A}from"./SegmentedControl-D-2BCZzd.js";var j={name:`list-todo`,size:24,node:[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`rect`,{x:`3`,y:`4`,width:`6`,height:`6`,rx:`1`,key:`cif1o7`}]]};j.node;var M=r(j),N=e(n(),1),P=i(),F=[`needs_input`,`failed`,`ready_for_review`,`unsupported_interaction`,`host_unreachable`],I={needs_input:`Answer here; the answer goes to the same runtime question.`,failed:`Runs that failed or were blocked. Open the task to see why.`,ready_for_review:`Finished runs waiting for your review.`,unsupported_interaction:`Questions this client cannot display. Answer them in a terminal attached to the session.`,host_unreachable:`Running work on hosts that stopped responding. Nothing is restarted elsewhere.`};function L(e){let t=Object.fromEntries(F.map(e=>[e,[]]));for(let n of e)t[n.reason]?.push(n);return t}function R(){let e=a(),t=o(e=>e.hydrated),n=s(),[r,i]=E(`attention`,`all`),c=(0,N.useMemo)(()=>r===`new`?e.filter(e=>!e.acknowledgedAt):e,[e,r]),l=(0,N.useMemo)(()=>L(c),[c]);return(0,P.jsxs)(`div`,{className:`flex w-full min-w-0 flex-col gap-4 px-4 py-4 safe-pt md:px-6`,children:[(0,P.jsxs)(`header`,{className:`flex min-w-0 flex-wrap items-center gap-3`,children:[(0,P.jsx)(`h1`,{className:`flex-1 text-lg font-semibold`,children:`Attention`}),(0,P.jsx)(A,{label:`Show`,className:`max-sm:w-full max-sm:basis-full`,value:r,onChange:i,options:[{value:`all`,label:`All open`},{value:`new`,label:`Not acknowledged`}]})]}),n.state!==`live`&&t&&(0,P.jsx)(`p`,{className:`text-sm text-muted`,role:`status`,children:`Showing the last known attention items; answers are sent once the connection is back.`}),t?c.length===0?(0,P.jsx)(g,{icon:(0,P.jsx)(S,{className:`size-8`}),title:e.length>0?`Nothing new`:`Nothing needs your attention`,description:e.length>0?`${e.length} acknowledged ${e.length===1?`item is`:`items are`} still open.`:`Questions, failures and finished work show up here.`,action:e.length>0?{label:`Show all open`,onClick:()=>i(`all`)}:{label:`Go to Board`,to:D.board}}):F.filter(e=>l[e].length>0).map(e=>{let t=O[e],n=t.icon;return(0,P.jsxs)(`section`,{"data-testid":`attention-group-${e}`,"aria-labelledby":`att-${e}`,className:`flex min-w-0 flex-col gap-2`,children:[(0,P.jsxs)(`div`,{children:[(0,P.jsxs)(`h2`,{id:`att-${e}`,className:`flex items-center gap-2 text-base font-semibold`,children:[(0,P.jsx)(n,{"aria-hidden":!0,className:`size-4 text-muted`}),t.label,(0,P.jsxs)(`span`,{className:`text-sm font-normal text-muted`,children:[`(`,l[e].length,`)`]})]}),(0,P.jsx)(`p`,{className:`text-sm text-muted`,children:I[e]})]}),(0,P.jsx)(`ul`,{className:`flex min-w-0 flex-col gap-2`,children:l[e].map(e=>(0,P.jsx)(`li`,{className:`min-w-0`,children:(0,P.jsx)(z,{item:e})},e.id))})]},e)}):(0,P.jsx)(m,{label:`Loading attention…`})]})}function z({item:e}){let n=o(t=>e.projectId?t.projects[e.projectId]:void 0),r=o(t=>e.taskId?t.tasks[e.taskId]:void 0),i=o(t=>e.sessionId?t.sessions[e.sessionId]:void 0),a=o(e=>i?e.hosts[i.hostId]:void 0),s=o(t=>e.interactionId?t.interactions[e.interactionId]:void 0),[m,g]=(0,N.useState)(!1),[S,E]=(0,N.useState)(null),A=async()=>{g(!0),E(null);try{let t=await c(`/api/attention/${encodeURIComponent(e.id)}/acknowledge`),n=t&&typeof t==`object`&&`id`in t?t:{...e,acknowledgedAt:new Date().toISOString()};o.setState(t=>t.attention[e.id]?{attention:{...t.attention,[e.id]:n}}:{}),T.success(`Acknowledged. It stays open until the underlying task or question is resolved.`)}catch(e){E(e)}finally{g(!1)}},j=e.sessionId?e.interactionId?D.interaction(e.sessionId,e.interactionId):D.session(e.sessionId):null,F=!!a&&a.connectivity!==`connected`,I=null;switch(e.reason){case`needs_input`:I=s&&s.state===`pending`?(0,P.jsx)(x,{flush:!0,interaction:s,readOnlyReason:F?`Host ${a?.name} is offline. You can answer once it reconnects.`:i?.readOnly?`This session is read-only.`:null}):s?(0,P.jsx)(x,{flush:!0,interaction:s}):j?(0,P.jsxs)(t,{to:j,className:_(`primary`,`sm`),children:[(0,P.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Answer in session`]}):null;break;case`failed`:I=e.taskId?(0,P.jsxs)(t,{to:D.task(e.taskId),className:_(`primary`,`sm`),children:[(0,P.jsx)(M,{"aria-hidden":!0,className:`size-3.5`}),`Open task`]}):null;break;case`ready_for_review`:I=e.sessionId?(0,P.jsxs)(t,{to:`${D.session(e.sessionId)}?tab=changes`,className:_(`primary`,`sm`),children:[(0,P.jsx)(k,{"aria-hidden":!0,className:`size-3.5`}),`Review changes`]}):e.taskId?(0,P.jsxs)(t,{to:D.task(e.taskId),className:_(`primary`,`sm`),children:[(0,P.jsx)(k,{"aria-hidden":!0,className:`size-3.5`}),`Open review`]}):null;break;case`unsupported_interaction`:I=s?(0,P.jsx)(x,{interaction:s,readOnlyReason:F?`Host ${a?.name} is offline. You can choose once it reconnects.`:i?.readOnly?`This session is read-only.`:null}):(0,P.jsx)(u,{capability:`This dialog`,reason:`An extension opened a terminal-only dialog. In RPC mode Pi gave it no answer, so the extension continued without one.`});break;case`host_unreachable`:I=a?(0,P.jsx)(d,{compact:!0,hostName:a.name,connectivity:a.connectivity===`reconnecting`?`reconnecting`:`unreachable`,lastSeenAt:a.lastSeenAt}):null}let L=[n?.name,r?.title,i&&i.title!==r?.title?i.title:null].filter(Boolean).join(` · `)||`Switchyard`;return e.reason===`needs_input`&&s?.state===`pending`&&b(s.presentation)?(0,P.jsxs)(`article`,{"aria-label":`Plan awaiting approval: ${L}`,className:`flex min-w-0 flex-col gap-2`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-sm text-muted`,children:[(0,P.jsx)(`span`,{className:`min-w-0 basis-full truncate sm:basis-auto sm:flex-1`,children:L}),j&&(0,P.jsxs)(t,{to:j,className:_(`ghost`,`sm`),children:[(0,P.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Open session`]}),(0,P.jsx)(`time`,{dateTime:e.createdAt,className:`shrink-0 text-xs text-subtle sm:ml-auto`,children:h(e.createdAt)}),!e.acknowledgedAt&&(0,P.jsx)(f,{size:`sm`,variant:`ghost`,busy:m,onClick:()=>void A(),children:`Acknowledge`})]}),I,S!==null&&(0,P.jsx)(p,{compact:!0,title:`Could not acknowledge`,error:l(S),onRetry:()=>void A()})]}):(0,P.jsxs)(`article`,{"aria-label":`${O[e.reason].label}: ${r?.title??i?.title??e.summary}`,className:`flex min-w-0 flex-col gap-2 rounded-lg border border-border bg-surface p-3`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,P.jsx)(w,{kind:`attention`,value:e.reason,size:`sm`}),(0,P.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-sm text-muted`,children:[n?.name,r?.title,i&&i.title!==r?.title?i.title:null].filter(Boolean).join(` · `)||`Switchyard`}),(0,P.jsx)(`time`,{dateTime:e.createdAt,className:`shrink-0 text-xs text-subtle`,children:h(e.createdAt)})]}),(0,P.jsx)(`p`,{className:`min-w-0 text-sm break-words`,children:e.summary}),I&&(0,P.jsx)(`div`,{className:`flex min-w-0 flex-col items-start`,children:I}),(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[e.reason!==`failed`&&e.taskId&&(0,P.jsxs)(t,{to:D.task(e.taskId),className:_(`ghost`,`sm`),children:[(0,P.jsx)(M,{"aria-hidden":!0,className:`size-3.5`}),`Task`]}),j&&(0,P.jsxs)(t,{to:j,className:_(`ghost`,`sm`),children:[(0,P.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Open session`]}),e.reason===`host_unreachable`&&a&&(0,P.jsxs)(t,{to:`${D.settings(`hosts`)}/${encodeURIComponent(a.id)}`,className:_(`ghost`,`sm`),children:[(0,P.jsx)(v,{"aria-hidden":!0,className:`size-3.5`}),`Host details`]}),(0,P.jsx)(`span`,{className:`ml-auto`}),e.acknowledgedAt?(0,P.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-xs text-muted`,children:[(0,P.jsx)(C,{"aria-hidden":!0,className:`size-3.5`}),`Acknowledged `,h(e.acknowledgedAt)]}):(0,P.jsx)(f,{size:`sm`,busy:m,onClick:()=>void A(),title:`Hide the alert without resolving the task`,children:`Acknowledge`})]}),S!==null&&(0,P.jsx)(p,{compact:!0,title:`Could not acknowledge`,error:l(S),onRetry:()=>void A()})]})}export{F as ATTENTION_GROUPS,R as default,L as groupAttention};
2
- //# sourceMappingURL=attention-Bya8XHKI.js.map
1
+ import{_ as e,i as t,m as n,n as r,t as i}from"./jsx-runtime-D13aM9HY.js";import{D as a,P as o,T as s,U as c,W as l,c as u,i as d,l as f,n as p,o as m,p as h,t as g,u as _}from"./states-DXozulGZ.js";import{t as v}from"./server-SxFxIScj.js";import{t as y}from"./external-link-3jjTxo92.js";import{n as b,t as x}from"./InteractionCard-DaLqafmA.js";import{F as S,P as C,f as w,l as T,s as E,t as D,u as O,w as k}from"./index-BhpEXLav.js";import{t as A}from"./SegmentedControl-BZPzNU08.js";var j={name:`list-todo`,size:24,node:[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`rect`,{x:`3`,y:`4`,width:`6`,height:`6`,rx:`1`,key:`cif1o7`}]]};j.node;var M=r(j),N=e(n(),1),P=i(),F=[`needs_input`,`failed`,`ready_for_review`,`unsupported_interaction`,`host_unreachable`],I={needs_input:`Answer here; the answer goes to the same runtime question.`,failed:`Runs that failed or were blocked. Open the task to see why.`,ready_for_review:`Finished runs waiting for your review.`,unsupported_interaction:`Questions this client cannot display. Answer them in a terminal attached to the session.`,host_unreachable:`Running work on hosts that stopped responding. Nothing is restarted elsewhere.`};function L(e){let t=Object.fromEntries(F.map(e=>[e,[]]));for(let n of e)t[n.reason]?.push(n);return t}function R(){let e=a(),t=o(e=>e.hydrated),n=s(),[r,i]=E(`attention`,`all`),c=(0,N.useMemo)(()=>r===`new`?e.filter(e=>!e.acknowledgedAt):e,[e,r]),l=(0,N.useMemo)(()=>L(c),[c]);return(0,P.jsxs)(`div`,{className:`flex w-full min-w-0 flex-col gap-4 px-4 py-4 safe-pt md:px-6`,children:[(0,P.jsxs)(`header`,{className:`flex min-w-0 flex-wrap items-center gap-3`,children:[(0,P.jsx)(`h1`,{className:`flex-1 text-lg font-semibold`,children:`Attention`}),(0,P.jsx)(A,{label:`Show`,className:`max-sm:w-full max-sm:basis-full`,value:r,onChange:i,options:[{value:`all`,label:`All open`},{value:`new`,label:`Not acknowledged`}]})]}),n.state!==`live`&&t&&(0,P.jsx)(`p`,{className:`text-sm text-muted`,role:`status`,children:`Showing the last known attention items; answers are sent once the connection is back.`}),t?c.length===0?(0,P.jsx)(g,{icon:(0,P.jsx)(S,{className:`size-8`}),title:e.length>0?`Nothing new`:`Nothing needs your attention`,description:e.length>0?`${e.length} acknowledged ${e.length===1?`item is`:`items are`} still open.`:`Questions, failures and finished work show up here.`,action:e.length>0?{label:`Show all open`,onClick:()=>i(`all`)}:{label:`Go to Board`,to:D.board}}):F.filter(e=>l[e].length>0).map(e=>{let t=O[e],n=t.icon;return(0,P.jsxs)(`section`,{"data-testid":`attention-group-${e}`,"aria-labelledby":`att-${e}`,className:`flex min-w-0 flex-col gap-2`,children:[(0,P.jsxs)(`div`,{children:[(0,P.jsxs)(`h2`,{id:`att-${e}`,className:`flex items-center gap-2 text-base font-semibold`,children:[(0,P.jsx)(n,{"aria-hidden":!0,className:`size-4 text-muted`}),t.label,(0,P.jsxs)(`span`,{className:`text-sm font-normal text-muted`,children:[`(`,l[e].length,`)`]})]}),(0,P.jsx)(`p`,{className:`text-sm text-muted`,children:I[e]})]}),(0,P.jsx)(`ul`,{className:`flex min-w-0 flex-col gap-2`,children:l[e].map(e=>(0,P.jsx)(`li`,{className:`min-w-0`,children:(0,P.jsx)(z,{item:e})},e.id))})]},e)}):(0,P.jsx)(m,{label:`Loading attention…`})]})}function z({item:e}){let n=o(t=>e.projectId?t.projects[e.projectId]:void 0),r=o(t=>e.taskId?t.tasks[e.taskId]:void 0),i=o(t=>e.sessionId?t.sessions[e.sessionId]:void 0),a=o(e=>i?e.hosts[i.hostId]:void 0),s=o(t=>e.interactionId?t.interactions[e.interactionId]:void 0),[m,g]=(0,N.useState)(!1),[S,E]=(0,N.useState)(null),A=async()=>{g(!0),E(null);try{let t=await c(`/api/attention/${encodeURIComponent(e.id)}/acknowledge`),n=t&&typeof t==`object`&&`id`in t?t:{...e,acknowledgedAt:new Date().toISOString()};o.setState(t=>t.attention[e.id]?{attention:{...t.attention,[e.id]:n}}:{}),T.success(`Acknowledged. It stays open until the underlying task or question is resolved.`)}catch(e){E(e)}finally{g(!1)}},j=e.sessionId?e.interactionId?D.interaction(e.sessionId,e.interactionId):D.session(e.sessionId):null,F=!!a&&a.connectivity!==`connected`,I=null;switch(e.reason){case`needs_input`:I=s&&s.state===`pending`?(0,P.jsx)(x,{flush:!0,interaction:s,readOnlyReason:F?`Host ${a?.name} is offline. You can answer once it reconnects.`:i?.readOnly?`This session is read-only.`:null}):s?(0,P.jsx)(x,{flush:!0,interaction:s}):j?(0,P.jsxs)(t,{to:j,className:_(`primary`,`sm`),children:[(0,P.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Answer in session`]}):null;break;case`failed`:I=e.taskId?(0,P.jsxs)(t,{to:D.task(e.taskId),className:_(`primary`,`sm`),children:[(0,P.jsx)(M,{"aria-hidden":!0,className:`size-3.5`}),`Open task`]}):null;break;case`ready_for_review`:I=e.sessionId?(0,P.jsxs)(t,{to:`${D.session(e.sessionId)}?tab=changes`,className:_(`primary`,`sm`),children:[(0,P.jsx)(k,{"aria-hidden":!0,className:`size-3.5`}),`Review changes`]}):e.taskId?(0,P.jsxs)(t,{to:D.task(e.taskId),className:_(`primary`,`sm`),children:[(0,P.jsx)(k,{"aria-hidden":!0,className:`size-3.5`}),`Open review`]}):null;break;case`unsupported_interaction`:I=s?(0,P.jsx)(x,{interaction:s,readOnlyReason:F?`Host ${a?.name} is offline. You can choose once it reconnects.`:i?.readOnly?`This session is read-only.`:null}):(0,P.jsx)(u,{capability:`This dialog`,reason:`An extension opened a terminal-only dialog. In RPC mode Pi gave it no answer, so the extension continued without one.`});break;case`host_unreachable`:I=a?(0,P.jsx)(d,{compact:!0,hostName:a.name,connectivity:a.connectivity===`reconnecting`?`reconnecting`:`unreachable`,lastSeenAt:a.lastSeenAt}):null}let L=[n?.name,r?.title,i&&i.title!==r?.title?i.title:null].filter(Boolean).join(` · `)||`Switchyard`;return e.reason===`needs_input`&&s?.state===`pending`&&b(s.presentation)?(0,P.jsxs)(`article`,{"aria-label":`Plan awaiting approval: ${L}`,className:`flex min-w-0 flex-col gap-2`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-sm text-muted`,children:[(0,P.jsx)(`span`,{className:`min-w-0 basis-full truncate sm:basis-auto sm:flex-1`,children:L}),j&&(0,P.jsxs)(t,{to:j,className:_(`ghost`,`sm`),children:[(0,P.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Open session`]}),(0,P.jsx)(`time`,{dateTime:e.createdAt,className:`shrink-0 text-xs text-subtle sm:ml-auto`,children:h(e.createdAt)}),!e.acknowledgedAt&&(0,P.jsx)(f,{size:`sm`,variant:`ghost`,busy:m,onClick:()=>void A(),children:`Acknowledge`})]}),I,S!==null&&(0,P.jsx)(p,{compact:!0,title:`Could not acknowledge`,error:l(S),onRetry:()=>void A()})]}):(0,P.jsxs)(`article`,{"aria-label":`${O[e.reason].label}: ${r?.title??i?.title??e.summary}`,className:`flex min-w-0 flex-col gap-2 rounded-lg border border-border bg-surface p-3`,children:[(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,P.jsx)(w,{kind:`attention`,value:e.reason,size:`sm`}),(0,P.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-sm text-muted`,children:[n?.name,r?.title,i&&i.title!==r?.title?i.title:null].filter(Boolean).join(` · `)||`Switchyard`}),(0,P.jsx)(`time`,{dateTime:e.createdAt,className:`shrink-0 text-xs text-subtle`,children:h(e.createdAt)})]}),(0,P.jsx)(`p`,{className:`min-w-0 text-sm break-words`,children:e.summary}),I&&(0,P.jsx)(`div`,{className:`flex min-w-0 flex-col items-start`,children:I}),(0,P.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[e.reason!==`failed`&&e.taskId&&(0,P.jsxs)(t,{to:D.task(e.taskId),className:_(`ghost`,`sm`),children:[(0,P.jsx)(M,{"aria-hidden":!0,className:`size-3.5`}),`Task`]}),j&&(0,P.jsxs)(t,{to:j,className:_(`ghost`,`sm`),children:[(0,P.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Open session`]}),e.reason===`host_unreachable`&&a&&(0,P.jsxs)(t,{to:`${D.settings(`hosts`)}/${encodeURIComponent(a.id)}`,className:_(`ghost`,`sm`),children:[(0,P.jsx)(v,{"aria-hidden":!0,className:`size-3.5`}),`Host details`]}),(0,P.jsx)(`span`,{className:`ml-auto`}),e.acknowledgedAt?(0,P.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-xs text-muted`,children:[(0,P.jsx)(C,{"aria-hidden":!0,className:`size-3.5`}),`Acknowledged `,h(e.acknowledgedAt)]}):(0,P.jsx)(f,{size:`sm`,busy:m,onClick:()=>void A(),title:`Hide the alert without resolving the task`,children:`Acknowledge`})]}),S!==null&&(0,P.jsx)(p,{compact:!0,title:`Could not acknowledge`,error:l(S),onRetry:()=>void A()})]})}export{F as ATTENTION_GROUPS,R as default,L as groupAttention};
2
+ //# sourceMappingURL=attention-BAdea2kA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"attention-Bya8XHKI.js","names":["useMemo","useState","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useOpenAttention","useSyncStore","useConnection","apiPost","errorMessage","UnsupportedCapability","HostUnavailable","Button","ErrorState","LoadingState","formatRelative","EmptyState","buttonClasses","Server","ExternalLink","isDocumentPresentation","InteractionCard","Bell","CircleCheck","StatusChip","toast","useFilter","ROUTES","ATTENTION_STATUS","Eye","SegmentedControl","__iconData","ListTodo","import_react","import_jsx_runtime","ATTENTION_GROUPS","GROUP_HINT","groupAttention","out","r","a","items","AttentionView","all","hydrated","s","connection","show","setShow","groups","info","reason","Icon","AttentionRow","project","item","task","session","host","interaction","busy","setBusy","ackError","setAckError","acknowledge","res","next","sessionHref","hostOffline","action","metaText"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/list-todo.mjs","../../src/features/attention/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"list-todo\",\n size: 24,\n node: [\n [\"path\", { d: \"M13 5h8\", key: \"a7qcls\" }],\n [\"path\", { d: \"M13 12h8\", key: \"h98zly\" }],\n [\"path\", { d: \"M13 19h8\", key: \"c3s6r1\" }],\n [\"path\", { d: \"m3 17 2 2 4-4\", key: \"1jhpwq\" }],\n [\"rect\", { x: \"3\", y: \"4\", width: \"6\", height: \"6\", rx: \"1\", key: \"cif1o7\" }]\n ]\n};\n__iconData.node;\nconst ListTodo = createLucideIcon(__iconData);\n\nexport { __iconData, ListTodo as default };\n//# sourceMappingURL=list-todo.mjs.map\n","import type { AttentionItem, AttentionReason } from \"@switchyard/protocol\";\nimport { Bell, CircleCheck, ExternalLink, Eye, ListTodo, Server } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { ROUTES } from \"../../app/routes\";\nimport { InteractionCard, isDocumentPresentation } from \"../../components/intent/InteractionCard\";\nimport { Button, buttonClasses } from \"../../components/ui/Button\";\nimport { SegmentedControl } from \"../../components/ui/SegmentedControl\";\nimport { ATTENTION_STATUS, StatusChip } from \"../../components/ui/StatusChip\";\nimport {\n EmptyState,\n ErrorState,\n HostUnavailable,\n LoadingState,\n UnsupportedCapability,\n} from \"../../components/ui/states\";\nimport { toast } from \"../../components/ui/Toast\";\nimport { formatRelative } from \"../../lib/time\";\nimport { useFilter } from \"../../prefs\";\nimport { apiPost, errorMessage } from \"../../sync/api\";\nimport { useConnection, useOpenAttention, useSyncStore } from \"../../sync/store\";\n\nexport const ATTENTION_GROUPS: AttentionReason[] = [\n \"needs_input\",\n \"failed\",\n \"ready_for_review\",\n \"unsupported_interaction\",\n \"host_unreachable\",\n];\n\nconst GROUP_HINT: Record<AttentionReason, string> = {\n needs_input: \"Answer here; the answer goes to the same runtime question.\",\n failed: \"Runs that failed or were blocked. Open the task to see why.\",\n ready_for_review: \"Finished runs waiting for your review.\",\n unsupported_interaction:\n \"Questions this client cannot display. Answer them in a terminal attached to the session.\",\n host_unreachable:\n \"Running work on hosts that stopped responding. Nothing is restarted elsewhere.\",\n};\n\n/** Group open attention by reason, keeping newest first within a group. */\nexport function groupAttention(items: AttentionItem[]): Record<AttentionReason, AttentionItem[]> {\n const out = Object.fromEntries(ATTENTION_GROUPS.map((r) => [r, [] as AttentionItem[]])) as Record<\n AttentionReason,\n AttentionItem[]\n >;\n for (const a of items) out[a.reason]?.push(a);\n return out;\n}\n\n/** /attention: Needs input, Failed, Ready for review (plus unsupported and unreachable). */\nexport default function AttentionView() {\n const all = useOpenAttention();\n const hydrated = useSyncStore((s) => s.hydrated);\n const connection = useConnection();\n const [show, setShow] = useFilter<\"all\" | \"new\">(\"attention\", \"all\");\n const items = useMemo(\n () => (show === \"new\" ? all.filter((a) => !a.acknowledgedAt) : all),\n [all, show],\n );\n const groups = useMemo(() => groupAttention(items), [items]);\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-4 px-4 py-4 safe-pt md:px-6\">\n <header className=\"flex min-w-0 flex-wrap items-center gap-3\">\n <h1 className=\"flex-1 text-lg font-semibold\">Attention</h1>\n <SegmentedControl\n label=\"Show\"\n className=\"max-sm:w-full max-sm:basis-full\"\n value={show}\n onChange={setShow}\n options={[\n { value: \"all\", label: \"All open\" },\n { value: \"new\", label: \"Not acknowledged\" },\n ]}\n />\n </header>\n {connection.state !== \"live\" && hydrated && (\n <p className=\"text-sm text-muted\" role=\"status\">\n Showing the last known attention items; answers are sent once the connection is back.\n </p>\n )}\n {!hydrated ? (\n <LoadingState label=\"Loading attention…\" />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Bell className=\"size-8\" />}\n title={all.length > 0 ? \"Nothing new\" : \"Nothing needs your attention\"}\n description={\n all.length > 0\n ? `${all.length} acknowledged ${all.length === 1 ? \"item is\" : \"items are\"} still open.`\n : \"Questions, failures and finished work show up here.\"\n }\n action={\n all.length > 0\n ? { label: \"Show all open\", onClick: () => setShow(\"all\") }\n : { label: \"Go to Board\", to: ROUTES.board }\n }\n />\n ) : (\n ATTENTION_GROUPS.filter((r) => groups[r].length > 0).map((reason) => {\n const info = ATTENTION_STATUS[reason];\n const Icon = info.icon;\n return (\n <section\n key={reason}\n data-testid={`attention-group-${reason}`}\n aria-labelledby={`att-${reason}`}\n className=\"flex min-w-0 flex-col gap-2\"\n >\n <div>\n <h2\n id={`att-${reason}`}\n className=\"flex items-center gap-2 text-base font-semibold\"\n >\n <Icon aria-hidden className=\"size-4 text-muted\" />\n {info.label}\n <span className=\"text-sm font-normal text-muted\">({groups[reason].length})</span>\n </h2>\n <p className=\"text-sm text-muted\">{GROUP_HINT[reason]}</p>\n </div>\n <ul className=\"flex min-w-0 flex-col gap-2\">\n {groups[reason].map((a) => (\n <li key={a.id} className=\"min-w-0\">\n <AttentionRow item={a} />\n </li>\n ))}\n </ul>\n </section>\n );\n })\n )}\n </div>\n );\n}\n\nfunction AttentionRow({ item }: { item: AttentionItem }) {\n const project = useSyncStore((s) => (item.projectId ? s.projects[item.projectId] : undefined));\n const task = useSyncStore((s) => (item.taskId ? s.tasks[item.taskId] : undefined));\n const session = useSyncStore((s) => (item.sessionId ? s.sessions[item.sessionId] : undefined));\n const host = useSyncStore((s) => (session ? s.hosts[session.hostId] : undefined));\n const interaction = useSyncStore((s) =>\n item.interactionId ? s.interactions[item.interactionId] : undefined,\n );\n const [busy, setBusy] = useState(false);\n const [ackError, setAckError] = useState<unknown>(null);\n\n const acknowledge = async () => {\n setBusy(true);\n setAckError(null);\n try {\n const res = await apiPost<AttentionItem>(\n `/api/attention/${encodeURIComponent(item.id)}/acknowledge`,\n );\n const next =\n res && typeof res === \"object\" && \"id\" in res\n ? res\n : { ...item, acknowledgedAt: new Date().toISOString() };\n useSyncStore.setState((s) =>\n s.attention[item.id] ? { attention: { ...s.attention, [item.id]: next } } : {},\n );\n toast.success(\n \"Acknowledged. It stays open until the underlying task or question is resolved.\",\n );\n } catch (e) {\n setAckError(e);\n } finally {\n setBusy(false);\n }\n };\n\n const sessionHref = item.sessionId\n ? item.interactionId\n ? ROUTES.interaction(item.sessionId, item.interactionId)\n : ROUTES.session(item.sessionId)\n : null;\n const hostOffline = !!host && host.connectivity !== \"connected\";\n\n let action: React.ReactNode = null;\n switch (item.reason) {\n case \"needs_input\":\n action =\n interaction && interaction.state === \"pending\" ? (\n <InteractionCard\n flush\n interaction={interaction}\n readOnlyReason={\n hostOffline\n ? `Host ${host?.name} is offline. You can answer once it reconnects.`\n : session?.readOnly\n ? \"This session is read-only.\"\n : null\n }\n />\n ) : interaction ? (\n <InteractionCard flush interaction={interaction} />\n ) : sessionHref ? (\n <Link to={sessionHref} className={buttonClasses(\"primary\", \"sm\")}>\n <ExternalLink aria-hidden className=\"size-3.5\" />\n Answer in session\n </Link>\n ) : null;\n break;\n case \"failed\":\n action = item.taskId ? (\n <Link to={ROUTES.task(item.taskId)} className={buttonClasses(\"primary\", \"sm\")}>\n <ListTodo aria-hidden className=\"size-3.5\" />\n Open task\n </Link>\n ) : null;\n break;\n case \"ready_for_review\":\n action = item.sessionId ? (\n <Link\n to={`${ROUTES.session(item.sessionId)}?tab=changes`}\n className={buttonClasses(\"primary\", \"sm\")}\n >\n <Eye aria-hidden className=\"size-3.5\" />\n Review changes\n </Link>\n ) : item.taskId ? (\n <Link to={ROUTES.task(item.taskId)} className={buttonClasses(\"primary\", \"sm\")}>\n <Eye aria-hidden className=\"size-3.5\" />\n Open review\n </Link>\n ) : null;\n break;\n case \"unsupported_interaction\":\n action = interaction ? (\n <InteractionCard\n interaction={interaction}\n readOnlyReason={\n hostOffline\n ? `Host ${host?.name} is offline. You can choose once it reconnects.`\n : session?.readOnly\n ? \"This session is read-only.\"\n : null\n }\n />\n ) : (\n <UnsupportedCapability\n capability=\"This dialog\"\n reason=\"An extension opened a terminal-only dialog. In RPC mode Pi gave it no answer, so the extension continued without one.\"\n />\n );\n break;\n case \"host_unreachable\":\n action = host ? (\n <HostUnavailable\n compact\n hostName={host.name}\n connectivity={host.connectivity === \"reconnecting\" ? \"reconnecting\" : \"unreachable\"}\n lastSeenAt={host.lastSeenAt}\n />\n ) : null;\n break;\n }\n\n const metaText =\n [project?.name, task?.title, session && session.title !== task?.title ? session.title : null]\n .filter(Boolean)\n .join(\" · \") || \"Switchyard\";\n\n // A plan awaiting approval gets the whole width: one meta line, no card around it.\n if (\n item.reason === \"needs_input\" &&\n interaction?.state === \"pending\" &&\n isDocumentPresentation(interaction.presentation)\n ) {\n return (\n <article\n aria-label={`Plan awaiting approval: ${metaText}`}\n className=\"flex min-w-0 flex-col gap-2\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-sm text-muted\">\n <span className=\"min-w-0 basis-full truncate sm:basis-auto sm:flex-1\">{metaText}</span>\n {sessionHref && (\n <Link to={sessionHref} className={buttonClasses(\"ghost\", \"sm\")}>\n <ExternalLink aria-hidden className=\"size-3.5\" />\n Open session\n </Link>\n )}\n <time dateTime={item.createdAt} className=\"shrink-0 text-xs text-subtle sm:ml-auto\">\n {formatRelative(item.createdAt)}\n </time>\n {!item.acknowledgedAt && (\n <Button size=\"sm\" variant=\"ghost\" busy={busy} onClick={() => void acknowledge()}>\n Acknowledge\n </Button>\n )}\n </div>\n {action}\n {ackError !== null && (\n <ErrorState\n compact\n title=\"Could not acknowledge\"\n error={errorMessage(ackError)}\n onRetry={() => void acknowledge()}\n />\n )}\n </article>\n );\n }\n\n return (\n <article\n aria-label={`${ATTENTION_STATUS[item.reason].label}: ${task?.title ?? session?.title ?? item.summary}`}\n className=\"flex min-w-0 flex-col gap-2 rounded-lg border border-border bg-surface p-3\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <StatusChip kind=\"attention\" value={item.reason} size=\"sm\" />\n <span className=\"min-w-0 flex-1 truncate text-sm text-muted\">\n {[\n project?.name,\n task?.title,\n session && session.title !== task?.title ? session.title : null,\n ]\n .filter(Boolean)\n .join(\" · \") || \"Switchyard\"}\n </span>\n <time dateTime={item.createdAt} className=\"shrink-0 text-xs text-subtle\">\n {formatRelative(item.createdAt)}\n </time>\n </div>\n <p className=\"min-w-0 text-sm break-words\">{item.summary}</p>\n {action && <div className=\"flex min-w-0 flex-col items-start\">{action}</div>}\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n {item.reason !== \"failed\" && item.taskId && (\n <Link to={ROUTES.task(item.taskId)} className={buttonClasses(\"ghost\", \"sm\")}>\n <ListTodo aria-hidden className=\"size-3.5\" />\n Task\n </Link>\n )}\n {sessionHref && (\n <Link to={sessionHref} className={buttonClasses(\"ghost\", \"sm\")}>\n <ExternalLink aria-hidden className=\"size-3.5\" />\n Open session\n </Link>\n )}\n {item.reason === \"host_unreachable\" && host && (\n <Link\n to={`${ROUTES.settings(\"hosts\")}/${encodeURIComponent(host.id)}`}\n className={buttonClasses(\"ghost\", \"sm\")}\n >\n <Server aria-hidden className=\"size-3.5\" />\n Host details\n </Link>\n )}\n <span className=\"ml-auto\" />\n {item.acknowledgedAt ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-muted\">\n <CircleCheck aria-hidden className=\"size-3.5\" />\n Acknowledged {formatRelative(item.acknowledgedAt)}\n </span>\n ) : (\n <Button\n size=\"sm\"\n busy={busy}\n onClick={() => void acknowledge()}\n title=\"Hide the alert without resolving the task\"\n >\n Acknowledge\n </Button>\n )}\n </div>\n {ackError !== null && (\n <ErrorState\n compact\n title=\"Could not acknowledge\"\n error={errorMessage(ackError)}\n onRetry={() => void acknowledge()}\n />\n )}\n </article>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}
1
+ {"version":3,"file":"attention-BAdea2kA.js","names":["useMemo","useState","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useOpenAttention","useSyncStore","useConnection","apiPost","errorMessage","UnsupportedCapability","HostUnavailable","Button","ErrorState","LoadingState","formatRelative","EmptyState","buttonClasses","Server","ExternalLink","isDocumentPresentation","InteractionCard","Bell","CircleCheck","StatusChip","toast","useFilter","ROUTES","ATTENTION_STATUS","Eye","SegmentedControl","__iconData","ListTodo","import_react","import_jsx_runtime","ATTENTION_GROUPS","GROUP_HINT","groupAttention","out","r","a","items","AttentionView","all","hydrated","s","connection","show","setShow","groups","info","reason","Icon","AttentionRow","project","item","task","session","host","interaction","busy","setBusy","ackError","setAckError","acknowledge","res","next","sessionHref","hostOffline","action","metaText"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/list-todo.mjs","../../src/features/attention/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"list-todo\",\n size: 24,\n node: [\n [\"path\", { d: \"M13 5h8\", key: \"a7qcls\" }],\n [\"path\", { d: \"M13 12h8\", key: \"h98zly\" }],\n [\"path\", { d: \"M13 19h8\", key: \"c3s6r1\" }],\n [\"path\", { d: \"m3 17 2 2 4-4\", key: \"1jhpwq\" }],\n [\"rect\", { x: \"3\", y: \"4\", width: \"6\", height: \"6\", rx: \"1\", key: \"cif1o7\" }]\n ]\n};\n__iconData.node;\nconst ListTodo = createLucideIcon(__iconData);\n\nexport { __iconData, ListTodo as default };\n//# sourceMappingURL=list-todo.mjs.map\n","import type { AttentionItem, AttentionReason } from \"@switchyard/protocol\";\nimport { Bell, CircleCheck, ExternalLink, Eye, ListTodo, Server } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { ROUTES } from \"../../app/routes\";\nimport { InteractionCard, isDocumentPresentation } from \"../../components/intent/InteractionCard\";\nimport { Button, buttonClasses } from \"../../components/ui/Button\";\nimport { SegmentedControl } from \"../../components/ui/SegmentedControl\";\nimport { ATTENTION_STATUS, StatusChip } from \"../../components/ui/StatusChip\";\nimport {\n EmptyState,\n ErrorState,\n HostUnavailable,\n LoadingState,\n UnsupportedCapability,\n} from \"../../components/ui/states\";\nimport { toast } from \"../../components/ui/Toast\";\nimport { formatRelative } from \"../../lib/time\";\nimport { useFilter } from \"../../prefs\";\nimport { apiPost, errorMessage } from \"../../sync/api\";\nimport { useConnection, useOpenAttention, useSyncStore } from \"../../sync/store\";\n\nexport const ATTENTION_GROUPS: AttentionReason[] = [\n \"needs_input\",\n \"failed\",\n \"ready_for_review\",\n \"unsupported_interaction\",\n \"host_unreachable\",\n];\n\nconst GROUP_HINT: Record<AttentionReason, string> = {\n needs_input: \"Answer here; the answer goes to the same runtime question.\",\n failed: \"Runs that failed or were blocked. Open the task to see why.\",\n ready_for_review: \"Finished runs waiting for your review.\",\n unsupported_interaction:\n \"Questions this client cannot display. Answer them in a terminal attached to the session.\",\n host_unreachable:\n \"Running work on hosts that stopped responding. Nothing is restarted elsewhere.\",\n};\n\n/** Group open attention by reason, keeping newest first within a group. */\nexport function groupAttention(items: AttentionItem[]): Record<AttentionReason, AttentionItem[]> {\n const out = Object.fromEntries(ATTENTION_GROUPS.map((r) => [r, [] as AttentionItem[]])) as Record<\n AttentionReason,\n AttentionItem[]\n >;\n for (const a of items) out[a.reason]?.push(a);\n return out;\n}\n\n/** /attention: Needs input, Failed, Ready for review (plus unsupported and unreachable). */\nexport default function AttentionView() {\n const all = useOpenAttention();\n const hydrated = useSyncStore((s) => s.hydrated);\n const connection = useConnection();\n const [show, setShow] = useFilter<\"all\" | \"new\">(\"attention\", \"all\");\n const items = useMemo(\n () => (show === \"new\" ? all.filter((a) => !a.acknowledgedAt) : all),\n [all, show],\n );\n const groups = useMemo(() => groupAttention(items), [items]);\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-4 px-4 py-4 safe-pt md:px-6\">\n <header className=\"flex min-w-0 flex-wrap items-center gap-3\">\n <h1 className=\"flex-1 text-lg font-semibold\">Attention</h1>\n <SegmentedControl\n label=\"Show\"\n className=\"max-sm:w-full max-sm:basis-full\"\n value={show}\n onChange={setShow}\n options={[\n { value: \"all\", label: \"All open\" },\n { value: \"new\", label: \"Not acknowledged\" },\n ]}\n />\n </header>\n {connection.state !== \"live\" && hydrated && (\n <p className=\"text-sm text-muted\" role=\"status\">\n Showing the last known attention items; answers are sent once the connection is back.\n </p>\n )}\n {!hydrated ? (\n <LoadingState label=\"Loading attention…\" />\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Bell className=\"size-8\" />}\n title={all.length > 0 ? \"Nothing new\" : \"Nothing needs your attention\"}\n description={\n all.length > 0\n ? `${all.length} acknowledged ${all.length === 1 ? \"item is\" : \"items are\"} still open.`\n : \"Questions, failures and finished work show up here.\"\n }\n action={\n all.length > 0\n ? { label: \"Show all open\", onClick: () => setShow(\"all\") }\n : { label: \"Go to Board\", to: ROUTES.board }\n }\n />\n ) : (\n ATTENTION_GROUPS.filter((r) => groups[r].length > 0).map((reason) => {\n const info = ATTENTION_STATUS[reason];\n const Icon = info.icon;\n return (\n <section\n key={reason}\n data-testid={`attention-group-${reason}`}\n aria-labelledby={`att-${reason}`}\n className=\"flex min-w-0 flex-col gap-2\"\n >\n <div>\n <h2\n id={`att-${reason}`}\n className=\"flex items-center gap-2 text-base font-semibold\"\n >\n <Icon aria-hidden className=\"size-4 text-muted\" />\n {info.label}\n <span className=\"text-sm font-normal text-muted\">({groups[reason].length})</span>\n </h2>\n <p className=\"text-sm text-muted\">{GROUP_HINT[reason]}</p>\n </div>\n <ul className=\"flex min-w-0 flex-col gap-2\">\n {groups[reason].map((a) => (\n <li key={a.id} className=\"min-w-0\">\n <AttentionRow item={a} />\n </li>\n ))}\n </ul>\n </section>\n );\n })\n )}\n </div>\n );\n}\n\nfunction AttentionRow({ item }: { item: AttentionItem }) {\n const project = useSyncStore((s) => (item.projectId ? s.projects[item.projectId] : undefined));\n const task = useSyncStore((s) => (item.taskId ? s.tasks[item.taskId] : undefined));\n const session = useSyncStore((s) => (item.sessionId ? s.sessions[item.sessionId] : undefined));\n const host = useSyncStore((s) => (session ? s.hosts[session.hostId] : undefined));\n const interaction = useSyncStore((s) =>\n item.interactionId ? s.interactions[item.interactionId] : undefined,\n );\n const [busy, setBusy] = useState(false);\n const [ackError, setAckError] = useState<unknown>(null);\n\n const acknowledge = async () => {\n setBusy(true);\n setAckError(null);\n try {\n const res = await apiPost<AttentionItem>(\n `/api/attention/${encodeURIComponent(item.id)}/acknowledge`,\n );\n const next =\n res && typeof res === \"object\" && \"id\" in res\n ? res\n : { ...item, acknowledgedAt: new Date().toISOString() };\n useSyncStore.setState((s) =>\n s.attention[item.id] ? { attention: { ...s.attention, [item.id]: next } } : {},\n );\n toast.success(\n \"Acknowledged. It stays open until the underlying task or question is resolved.\",\n );\n } catch (e) {\n setAckError(e);\n } finally {\n setBusy(false);\n }\n };\n\n const sessionHref = item.sessionId\n ? item.interactionId\n ? ROUTES.interaction(item.sessionId, item.interactionId)\n : ROUTES.session(item.sessionId)\n : null;\n const hostOffline = !!host && host.connectivity !== \"connected\";\n\n let action: React.ReactNode = null;\n switch (item.reason) {\n case \"needs_input\":\n action =\n interaction && interaction.state === \"pending\" ? (\n <InteractionCard\n flush\n interaction={interaction}\n readOnlyReason={\n hostOffline\n ? `Host ${host?.name} is offline. You can answer once it reconnects.`\n : session?.readOnly\n ? \"This session is read-only.\"\n : null\n }\n />\n ) : interaction ? (\n <InteractionCard flush interaction={interaction} />\n ) : sessionHref ? (\n <Link to={sessionHref} className={buttonClasses(\"primary\", \"sm\")}>\n <ExternalLink aria-hidden className=\"size-3.5\" />\n Answer in session\n </Link>\n ) : null;\n break;\n case \"failed\":\n action = item.taskId ? (\n <Link to={ROUTES.task(item.taskId)} className={buttonClasses(\"primary\", \"sm\")}>\n <ListTodo aria-hidden className=\"size-3.5\" />\n Open task\n </Link>\n ) : null;\n break;\n case \"ready_for_review\":\n action = item.sessionId ? (\n <Link\n to={`${ROUTES.session(item.sessionId)}?tab=changes`}\n className={buttonClasses(\"primary\", \"sm\")}\n >\n <Eye aria-hidden className=\"size-3.5\" />\n Review changes\n </Link>\n ) : item.taskId ? (\n <Link to={ROUTES.task(item.taskId)} className={buttonClasses(\"primary\", \"sm\")}>\n <Eye aria-hidden className=\"size-3.5\" />\n Open review\n </Link>\n ) : null;\n break;\n case \"unsupported_interaction\":\n action = interaction ? (\n <InteractionCard\n interaction={interaction}\n readOnlyReason={\n hostOffline\n ? `Host ${host?.name} is offline. You can choose once it reconnects.`\n : session?.readOnly\n ? \"This session is read-only.\"\n : null\n }\n />\n ) : (\n <UnsupportedCapability\n capability=\"This dialog\"\n reason=\"An extension opened a terminal-only dialog. In RPC mode Pi gave it no answer, so the extension continued without one.\"\n />\n );\n break;\n case \"host_unreachable\":\n action = host ? (\n <HostUnavailable\n compact\n hostName={host.name}\n connectivity={host.connectivity === \"reconnecting\" ? \"reconnecting\" : \"unreachable\"}\n lastSeenAt={host.lastSeenAt}\n />\n ) : null;\n break;\n }\n\n const metaText =\n [project?.name, task?.title, session && session.title !== task?.title ? session.title : null]\n .filter(Boolean)\n .join(\" · \") || \"Switchyard\";\n\n // A plan awaiting approval gets the whole width: one meta line, no card around it.\n if (\n item.reason === \"needs_input\" &&\n interaction?.state === \"pending\" &&\n isDocumentPresentation(interaction.presentation)\n ) {\n return (\n <article\n aria-label={`Plan awaiting approval: ${metaText}`}\n className=\"flex min-w-0 flex-col gap-2\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-sm text-muted\">\n <span className=\"min-w-0 basis-full truncate sm:basis-auto sm:flex-1\">{metaText}</span>\n {sessionHref && (\n <Link to={sessionHref} className={buttonClasses(\"ghost\", \"sm\")}>\n <ExternalLink aria-hidden className=\"size-3.5\" />\n Open session\n </Link>\n )}\n <time dateTime={item.createdAt} className=\"shrink-0 text-xs text-subtle sm:ml-auto\">\n {formatRelative(item.createdAt)}\n </time>\n {!item.acknowledgedAt && (\n <Button size=\"sm\" variant=\"ghost\" busy={busy} onClick={() => void acknowledge()}>\n Acknowledge\n </Button>\n )}\n </div>\n {action}\n {ackError !== null && (\n <ErrorState\n compact\n title=\"Could not acknowledge\"\n error={errorMessage(ackError)}\n onRetry={() => void acknowledge()}\n />\n )}\n </article>\n );\n }\n\n return (\n <article\n aria-label={`${ATTENTION_STATUS[item.reason].label}: ${task?.title ?? session?.title ?? item.summary}`}\n className=\"flex min-w-0 flex-col gap-2 rounded-lg border border-border bg-surface p-3\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <StatusChip kind=\"attention\" value={item.reason} size=\"sm\" />\n <span className=\"min-w-0 flex-1 truncate text-sm text-muted\">\n {[\n project?.name,\n task?.title,\n session && session.title !== task?.title ? session.title : null,\n ]\n .filter(Boolean)\n .join(\" · \") || \"Switchyard\"}\n </span>\n <time dateTime={item.createdAt} className=\"shrink-0 text-xs text-subtle\">\n {formatRelative(item.createdAt)}\n </time>\n </div>\n <p className=\"min-w-0 text-sm break-words\">{item.summary}</p>\n {action && <div className=\"flex min-w-0 flex-col items-start\">{action}</div>}\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n {item.reason !== \"failed\" && item.taskId && (\n <Link to={ROUTES.task(item.taskId)} className={buttonClasses(\"ghost\", \"sm\")}>\n <ListTodo aria-hidden className=\"size-3.5\" />\n Task\n </Link>\n )}\n {sessionHref && (\n <Link to={sessionHref} className={buttonClasses(\"ghost\", \"sm\")}>\n <ExternalLink aria-hidden className=\"size-3.5\" />\n Open session\n </Link>\n )}\n {item.reason === \"host_unreachable\" && host && (\n <Link\n to={`${ROUTES.settings(\"hosts\")}/${encodeURIComponent(host.id)}`}\n className={buttonClasses(\"ghost\", \"sm\")}\n >\n <Server aria-hidden className=\"size-3.5\" />\n Host details\n </Link>\n )}\n <span className=\"ml-auto\" />\n {item.acknowledgedAt ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-muted\">\n <CircleCheck aria-hidden className=\"size-3.5\" />\n Acknowledged {formatRelative(item.acknowledgedAt)}\n </span>\n ) : (\n <Button\n size=\"sm\"\n busy={busy}\n onClick={() => void acknowledge()}\n title=\"Hide the alert without resolving the task\"\n >\n Acknowledge\n </Button>\n )}\n </div>\n {ackError !== null && (\n <ErrorState\n compact\n title=\"Could not acknowledge\"\n error={errorMessage(ackError)}\n onRetry={() => void acknowledge()}\n />\n )}\n </article>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{_ as e,i as t,m as n,n as r,t as i}from"./jsx-runtime-D13aM9HY.js";import{P as a,V as o,h as s,l as c,n as l,o as u,p as d,r as f,t as p,u as m}from"./states--geW2Ntl.js";import{t as h}from"./calendar-clock-B8zE4JsX.js";import{t as g}from"./chevron-right-evVLS6hq.js";import{t as _}from"./hand-UPoHamrn.js";import{t as v}from"./link-2-CaMlUDRG.js";import{A as y,C as b,F as x,I as S,M as C,P as w,_ as T,c as E,p as D,w as O}from"./describe-DnvH6_fl.js";import{t as k}from"./play-CjoDg3ws.js";import{t as A}from"./plus-BLP-wIYD.js";import{D as j,g as M,h as N,r as P}from"./index-vIzH1m8u.js";import{a as F}from"./dist-BE_V9dwJ.js";var I={name:`webhook`,size:24,node:[[`path`,{d:`M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2`,key:`q3hayz`}],[`path`,{d:`m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06`,key:`1go1hn`}],[`path`,{d:`m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8`,key:`qlwsc0`}]]};I.node;var L=r(I),R=e(n(),1),z=i(),B=`/automations/new`,V=e=>`/automations/${encodeURIComponent(e)}`,H={manual:_,once:j,cron:h,webhook:L,dependency:v};function U(){let e=a(e=>e.hydrated),n=w(),r=a(e=>e.projects),i=a(e=>e.hosts),s=a(e=>e.tasks),c=P(),[d,h]=(0,R.useState)({status:`idle`}),g=(0,R.useCallback)(async()=>{h({status:`loading`});try{let e=await o(`/api/automations`),t=Array.isArray(e)?e:e&&typeof e==`object`&&Array.isArray(e.items)?e.items:null,n=t?F.array().safeParse(t):null;if(!n?.success){h({status:`fatal`,error:Error(`The coordinator returned automations in an unexpected format.`)});return}C.getState().putMany(n.data),h({status:`done`})}catch(e){h({status:`error`,error:e})}},[]);(0,R.useEffect)(()=>{e||g()},[e,g]);let _=(0,R.useMemo)(()=>Object.values(n).sort((e,t)=>e.name.localeCompare(t.name)),[n]),v=e||d.status===`done`,y;return y=v?_.length===0?(0,z.jsx)(p,{icon:(0,z.jsx)(N,{className:`size-8`}),title:`No automations yet`,description:`Automations create tasks on a schedule, from a webhook, or when another task is done.`,action:{label:`Create an automation`,to:B,icon:(0,z.jsx)(A,{className:`size-4`})}}):(0,z.jsx)(G,{automations:_,projects:r,hosts:i,tasks:s,table:c===`desktop`}):d.status===`fatal`?(0,z.jsx)(f,{error:d.error}):d.status===`error`?(0,z.jsx)(l,{title:`Could not load automations`,error:d.error,onRetry:()=>void g()}):(0,z.jsx)(u,{label:`Loading automations…`}),(0,z.jsxs)(`div`,{className:`flex w-full min-w-0 flex-col gap-4 px-4 py-4 md:px-6`,children:[(0,z.jsxs)(`header`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,z.jsxs)(`div`,{className:`min-w-0`,children:[(0,z.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Automations`}),(0,z.jsx)(`p`,{className:`text-sm text-muted`,children:`Scheduled, webhook and dependency automations. Routine successes stay in each automation's history.`})]}),(0,z.jsxs)(t,{to:B,className:m(`primary`),children:[(0,z.jsx)(A,{"aria-hidden":!0,className:`size-4`}),`New automation`]})]}),(0,z.jsx)(b,{}),y]})}var W=`grid grid-cols-[minmax(0,2.4fr)_minmax(0,1fr)_5.5rem_minmax(0,1.2fr)_minmax(0,1.4fr)_7.5rem_7rem_auto] items-center gap-3`;function G({automations:e,projects:t,hosts:n,tasks:r,table:i}){let{busy:a,toggle:o}=S(),{busy:c,run:l}=x(),u=(0,R.useCallback)(e=>r[e]?.title,[r]);return(0,z.jsxs)(`div`,{className:`min-w-0 overflow-hidden rounded-lg border border-border bg-surface`,children:[i&&(0,z.jsxs)(`div`,{"aria-hidden":!0,className:s(W,`border-b border-border bg-surface-2 px-4 py-2 text-xs font-medium text-muted`),children:[(0,z.jsx)(`span`,{children:`Automation`}),(0,z.jsx)(`span`,{children:`Project`}),(0,z.jsx)(`span`,{children:`Mode`}),(0,z.jsx)(`span`,{children:`Next occurrence`}),(0,z.jsx)(`span`,{children:`Last outcome`}),(0,z.jsx)(`span`,{children:`Failures`}),(0,z.jsx)(`span`,{children:`Enabled`}),(0,z.jsx)(`span`,{className:`sr-only`,children:`Actions`})]}),(0,z.jsx)(`ul`,{"data-testid":`automations-list`,className:`divide-y divide-border`,children:e.map(e=>{let r=t[e.projectId],s=e.hostId??r?.defaultHostId??null,d=s?n[s]:void 0,f={automation:e,projectName:r?.name??`Unknown project`,host:d,trigger:E(e.trigger,{taskTitle:u}),toggling:!!a[e.id],running:!!c[e.id],onToggle:t=>void o(e,t),onRun:()=>void l(e)};return(0,z.jsx)(`li`,{"data-testid":`automation-row`,"data-automation-id":e.id,children:i?(0,z.jsx)(X,{...f}):(0,z.jsx)(Z,{...f})},e.id)})})]})}function K({automation:e,trigger:t}){let n=H[e.trigger.kind];return(0,z.jsxs)(`span`,{className:`flex min-w-0 items-start gap-1.5 text-sm text-muted`,children:[(0,z.jsx)(n,{"aria-hidden":!0,className:`mt-0.5 size-3.5 shrink-0`}),(0,z.jsx)(`span`,{className:`min-w-0 break-words`,children:t})]})}function q({automation:e}){if(!e.enabled)return(0,z.jsx)(`span`,{className:`text-sm text-muted`,children:`Paused (disabled)`});if(!e.nextAt){let t=e.trigger.kind;return(0,z.jsx)(`span`,{className:`text-sm text-muted`,children:t===`manual`?`On demand`:t===`webhook`?`On webhook`:t===`dependency`?`When the task is done`:`None scheduled`})}return(0,z.jsxs)(`span`,{className:`flex min-w-0 flex-col text-sm`,children:[(0,z.jsx)(`span`,{children:d(e.nextAt)}),(0,z.jsx)(`time`,{dateTime:e.nextAt,className:`text-xs text-muted`,children:D(e.nextAt)})]})}function J({host:e}){return!e||e.connectivity===`connected`?null:(0,z.jsxs)(`span`,{className:`flex items-center gap-1 text-xs text-danger`,children:[(0,z.jsx)(M,{"aria-hidden":!0,className:`size-3.5 shrink-0`}),(0,z.jsxs)(`span`,{className:`min-w-0 break-words`,children:[`Host `,e.name,` `,e.connectivity===`reconnecting`?`reconnecting`:`unreachable`]})]})}function Y({running:e,onRun:t,name:n}){return(0,z.jsx)(c,{size:`sm`,busy:e,icon:(0,z.jsx)(k,{className:`size-3.5`}),onClick:t,"aria-label":`Run ${n} now`,children:`Run now`})}function X(e){let n=e.automation;return(0,z.jsxs)(`div`,{className:s(W,`px-4 py-3`),children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,z.jsx)(t,{to:V(n.id),className:`truncate font-medium text-text hover:underline focus-visible:underline`,children:n.name}),(0,z.jsx)(K,{automation:n,trigger:e.trigger})]}),(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Project: `}),(0,z.jsx)(`span`,{className:`truncate text-sm`,children:e.projectName}),(0,z.jsx)(J,{host:e.host})]}),(0,z.jsxs)(`span`,{className:`text-sm`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Mode: `}),T(n.mode)]}),(0,z.jsxs)(`div`,{className:`min-w-0`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Next occurrence: `}),(0,z.jsx)(q,{automation:n})]}),(0,z.jsxs)(`div`,{className:`min-w-0 text-sm break-words`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Last outcome: `}),n.lastOutcome??(0,z.jsx)(`span`,{className:`text-muted`,children:`No runs yet`})]}),(0,z.jsx)(O,{count:n.failureCount}),(0,z.jsx)(y,{checked:n.enabled,onChange:e.onToggle,busy:e.toggling,label:`Enable ${n.name}`,onText:`Enabled`,offText:`Disabled`}),(0,z.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,z.jsx)(Y,{running:e.running,onRun:e.onRun,name:n.name}),(0,z.jsx)(t,{to:V(n.id),"aria-label":`Edit ${n.name}`,title:`Edit`,className:`tap inline-flex size-9 items-center justify-center rounded-md text-muted hover:bg-surface-2 hover:text-text`,children:(0,z.jsx)(g,{"aria-hidden":!0,className:`size-4`})})]})]})}function Z(e){let n=e.automation;return(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-2 px-4 py-3`,children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 items-start justify-between gap-2`,children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,z.jsx)(t,{to:V(n.id),className:`font-medium break-words text-text hover:underline focus-visible:underline`,children:n.name}),(0,z.jsx)(K,{automation:n,trigger:e.trigger})]}),(0,z.jsx)(y,{checked:n.enabled,onChange:e.onToggle,busy:e.toggling,label:`Enable ${n.name}`,onText:`Enabled`,offText:`Disabled`})]}),(0,z.jsxs)(`dl`,{className:`grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm`,children:[(0,z.jsx)(`dt`,{className:`text-muted`,children:`Project`}),(0,z.jsxs)(`dd`,{className:`flex min-w-0 flex-col break-words`,children:[e.projectName,(0,z.jsx)(J,{host:e.host})]}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Mode`}),(0,z.jsx)(`dd`,{children:T(n.mode)}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Next`}),(0,z.jsx)(`dd`,{className:`min-w-0`,children:(0,z.jsx)(q,{automation:n})}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Last outcome`}),(0,z.jsx)(`dd`,{className:`min-w-0 break-words`,children:n.lastOutcome??(0,z.jsx)(`span`,{className:`text-muted`,children:`No runs yet`})}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Failures`}),(0,z.jsx)(`dd`,{children:(0,z.jsx)(O,{count:n.failureCount})})]}),(0,z.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,z.jsx)(Y,{running:e.running,onRun:e.onRun,name:n.name}),(0,z.jsxs)(t,{to:V(n.id),className:m(`ghost`,`sm`),children:[`Edit`,(0,z.jsx)(g,{"aria-hidden":!0,className:`size-3.5`})]})]})]})}export{U as default};
2
- //# sourceMappingURL=automations-DiXKRcnE.js.map
1
+ import{_ as e,i as t,m as n,n as r,t as i}from"./jsx-runtime-D13aM9HY.js";import{P as a,V as o,h as s,l as c,n as l,o as u,p as d,r as f,t as p,u as m}from"./states-DXozulGZ.js";import{t as h}from"./calendar-clock-B8zE4JsX.js";import{t as g}from"./chevron-right-evVLS6hq.js";import{t as _}from"./hand-UPoHamrn.js";import{t as v}from"./link-2-CaMlUDRG.js";import{A as y,C as b,F as x,I as S,M as C,P as w,_ as T,c as E,p as D,w as O}from"./describe-C1YimBXJ.js";import{t as k}from"./play-CjoDg3ws.js";import{t as A}from"./plus-BLP-wIYD.js";import{D as j,g as M,h as N,r as P}from"./index-BhpEXLav.js";import{a as F}from"./dist-B_RpNOcK.js";var I={name:`webhook`,size:24,node:[[`path`,{d:`M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2`,key:`q3hayz`}],[`path`,{d:`m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06`,key:`1go1hn`}],[`path`,{d:`m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8`,key:`qlwsc0`}]]};I.node;var L=r(I),R=e(n(),1),z=i(),B=`/automations/new`,V=e=>`/automations/${encodeURIComponent(e)}`,H={manual:_,once:j,cron:h,webhook:L,dependency:v};function U(){let e=a(e=>e.hydrated),n=w(),r=a(e=>e.projects),i=a(e=>e.hosts),s=a(e=>e.tasks),c=P(),[d,h]=(0,R.useState)({status:`idle`}),g=(0,R.useCallback)(async()=>{h({status:`loading`});try{let e=await o(`/api/automations`),t=Array.isArray(e)?e:e&&typeof e==`object`&&Array.isArray(e.items)?e.items:null,n=t?F.array().safeParse(t):null;if(!n?.success){h({status:`fatal`,error:Error(`The coordinator returned automations in an unexpected format.`)});return}C.getState().putMany(n.data),h({status:`done`})}catch(e){h({status:`error`,error:e})}},[]);(0,R.useEffect)(()=>{e||g()},[e,g]);let _=(0,R.useMemo)(()=>Object.values(n).sort((e,t)=>e.name.localeCompare(t.name)),[n]),v=e||d.status===`done`,y;return y=v?_.length===0?(0,z.jsx)(p,{icon:(0,z.jsx)(N,{className:`size-8`}),title:`No automations yet`,description:`Automations create tasks on a schedule, from a webhook, or when another task is done.`,action:{label:`Create an automation`,to:B,icon:(0,z.jsx)(A,{className:`size-4`})}}):(0,z.jsx)(G,{automations:_,projects:r,hosts:i,tasks:s,table:c===`desktop`}):d.status===`fatal`?(0,z.jsx)(f,{error:d.error}):d.status===`error`?(0,z.jsx)(l,{title:`Could not load automations`,error:d.error,onRetry:()=>void g()}):(0,z.jsx)(u,{label:`Loading automations…`}),(0,z.jsxs)(`div`,{className:`flex w-full min-w-0 flex-col gap-4 px-4 py-4 md:px-6`,children:[(0,z.jsxs)(`header`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,z.jsxs)(`div`,{className:`min-w-0`,children:[(0,z.jsx)(`h1`,{className:`text-lg font-semibold`,children:`Automations`}),(0,z.jsx)(`p`,{className:`text-sm text-muted`,children:`Scheduled, webhook and dependency automations. Routine successes stay in each automation's history.`})]}),(0,z.jsxs)(t,{to:B,className:m(`primary`),children:[(0,z.jsx)(A,{"aria-hidden":!0,className:`size-4`}),`New automation`]})]}),(0,z.jsx)(b,{}),y]})}var W=`grid grid-cols-[minmax(0,2.4fr)_minmax(0,1fr)_5.5rem_minmax(0,1.2fr)_minmax(0,1.4fr)_7.5rem_7rem_auto] items-center gap-3`;function G({automations:e,projects:t,hosts:n,tasks:r,table:i}){let{busy:a,toggle:o}=S(),{busy:c,run:l}=x(),u=(0,R.useCallback)(e=>r[e]?.title,[r]);return(0,z.jsxs)(`div`,{className:`min-w-0 overflow-hidden rounded-lg border border-border bg-surface`,children:[i&&(0,z.jsxs)(`div`,{"aria-hidden":!0,className:s(W,`border-b border-border bg-surface-2 px-4 py-2 text-xs font-medium text-muted`),children:[(0,z.jsx)(`span`,{children:`Automation`}),(0,z.jsx)(`span`,{children:`Project`}),(0,z.jsx)(`span`,{children:`Mode`}),(0,z.jsx)(`span`,{children:`Next occurrence`}),(0,z.jsx)(`span`,{children:`Last outcome`}),(0,z.jsx)(`span`,{children:`Failures`}),(0,z.jsx)(`span`,{children:`Enabled`}),(0,z.jsx)(`span`,{className:`sr-only`,children:`Actions`})]}),(0,z.jsx)(`ul`,{"data-testid":`automations-list`,className:`divide-y divide-border`,children:e.map(e=>{let r=t[e.projectId],s=e.hostId??r?.defaultHostId??null,d=s?n[s]:void 0,f={automation:e,projectName:r?.name??`Unknown project`,host:d,trigger:E(e.trigger,{taskTitle:u}),toggling:!!a[e.id],running:!!c[e.id],onToggle:t=>void o(e,t),onRun:()=>void l(e)};return(0,z.jsx)(`li`,{"data-testid":`automation-row`,"data-automation-id":e.id,children:i?(0,z.jsx)(X,{...f}):(0,z.jsx)(Z,{...f})},e.id)})})]})}function K({automation:e,trigger:t}){let n=H[e.trigger.kind];return(0,z.jsxs)(`span`,{className:`flex min-w-0 items-start gap-1.5 text-sm text-muted`,children:[(0,z.jsx)(n,{"aria-hidden":!0,className:`mt-0.5 size-3.5 shrink-0`}),(0,z.jsx)(`span`,{className:`min-w-0 break-words`,children:t})]})}function q({automation:e}){if(!e.enabled)return(0,z.jsx)(`span`,{className:`text-sm text-muted`,children:`Paused (disabled)`});if(!e.nextAt){let t=e.trigger.kind;return(0,z.jsx)(`span`,{className:`text-sm text-muted`,children:t===`manual`?`On demand`:t===`webhook`?`On webhook`:t===`dependency`?`When the task is done`:`None scheduled`})}return(0,z.jsxs)(`span`,{className:`flex min-w-0 flex-col text-sm`,children:[(0,z.jsx)(`span`,{children:d(e.nextAt)}),(0,z.jsx)(`time`,{dateTime:e.nextAt,className:`text-xs text-muted`,children:D(e.nextAt)})]})}function J({host:e}){return!e||e.connectivity===`connected`?null:(0,z.jsxs)(`span`,{className:`flex items-center gap-1 text-xs text-danger`,children:[(0,z.jsx)(M,{"aria-hidden":!0,className:`size-3.5 shrink-0`}),(0,z.jsxs)(`span`,{className:`min-w-0 break-words`,children:[`Host `,e.name,` `,e.connectivity===`reconnecting`?`reconnecting`:`unreachable`]})]})}function Y({running:e,onRun:t,name:n}){return(0,z.jsx)(c,{size:`sm`,busy:e,icon:(0,z.jsx)(k,{className:`size-3.5`}),onClick:t,"aria-label":`Run ${n} now`,children:`Run now`})}function X(e){let n=e.automation;return(0,z.jsxs)(`div`,{className:s(W,`px-4 py-3`),children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,z.jsx)(t,{to:V(n.id),className:`truncate font-medium text-text hover:underline focus-visible:underline`,children:n.name}),(0,z.jsx)(K,{automation:n,trigger:e.trigger})]}),(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Project: `}),(0,z.jsx)(`span`,{className:`truncate text-sm`,children:e.projectName}),(0,z.jsx)(J,{host:e.host})]}),(0,z.jsxs)(`span`,{className:`text-sm`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Mode: `}),T(n.mode)]}),(0,z.jsxs)(`div`,{className:`min-w-0`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Next occurrence: `}),(0,z.jsx)(q,{automation:n})]}),(0,z.jsxs)(`div`,{className:`min-w-0 text-sm break-words`,children:[(0,z.jsx)(`span`,{className:`sr-only`,children:`Last outcome: `}),n.lastOutcome??(0,z.jsx)(`span`,{className:`text-muted`,children:`No runs yet`})]}),(0,z.jsx)(O,{count:n.failureCount}),(0,z.jsx)(y,{checked:n.enabled,onChange:e.onToggle,busy:e.toggling,label:`Enable ${n.name}`,onText:`Enabled`,offText:`Disabled`}),(0,z.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,z.jsx)(Y,{running:e.running,onRun:e.onRun,name:n.name}),(0,z.jsx)(t,{to:V(n.id),"aria-label":`Edit ${n.name}`,title:`Edit`,className:`tap inline-flex size-9 items-center justify-center rounded-md text-muted hover:bg-surface-2 hover:text-text`,children:(0,z.jsx)(g,{"aria-hidden":!0,className:`size-4`})})]})]})}function Z(e){let n=e.automation;return(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-2 px-4 py-3`,children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 items-start justify-between gap-2`,children:[(0,z.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-0.5`,children:[(0,z.jsx)(t,{to:V(n.id),className:`font-medium break-words text-text hover:underline focus-visible:underline`,children:n.name}),(0,z.jsx)(K,{automation:n,trigger:e.trigger})]}),(0,z.jsx)(y,{checked:n.enabled,onChange:e.onToggle,busy:e.toggling,label:`Enable ${n.name}`,onText:`Enabled`,offText:`Disabled`})]}),(0,z.jsxs)(`dl`,{className:`grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm`,children:[(0,z.jsx)(`dt`,{className:`text-muted`,children:`Project`}),(0,z.jsxs)(`dd`,{className:`flex min-w-0 flex-col break-words`,children:[e.projectName,(0,z.jsx)(J,{host:e.host})]}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Mode`}),(0,z.jsx)(`dd`,{children:T(n.mode)}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Next`}),(0,z.jsx)(`dd`,{className:`min-w-0`,children:(0,z.jsx)(q,{automation:n})}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Last outcome`}),(0,z.jsx)(`dd`,{className:`min-w-0 break-words`,children:n.lastOutcome??(0,z.jsx)(`span`,{className:`text-muted`,children:`No runs yet`})}),(0,z.jsx)(`dt`,{className:`text-muted`,children:`Failures`}),(0,z.jsx)(`dd`,{children:(0,z.jsx)(O,{count:n.failureCount})})]}),(0,z.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,z.jsx)(Y,{running:e.running,onRun:e.onRun,name:n.name}),(0,z.jsxs)(t,{to:V(n.id),className:m(`ghost`,`sm`),children:[`Edit`,(0,z.jsx)(g,{"aria-hidden":!0,className:`size-3.5`})]})]})]})}export{U as default};
2
+ //# sourceMappingURL=automations-DSFUzB0w.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"automations-DiXKRcnE.js","names":["useState","useCallback","useMemo","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useSyncStore","apiGet","cn","Button","ErrorState","LoadingState","formatRelative","FatalError","EmptyState","buttonClasses","CalendarClock","ChevronRight","Hand","Link2","Switch","ConnectionNote","useRunNow","useToggleEnabled","useLocalAutomations","useMergedAutomations","modeLabel","describeTrigger","formatAbsolute","FailureCount","Play","Plus","Clock","WifiOff","Workflow","useBreakpoint","Automation","__iconData","Webhook","import_react","import_jsx_runtime","NEW_PATH","editPath","id","TRIGGER_ICONS","AutomationsView","hydrated","s","automations","projects","hosts","tasks","bp","fetchState","setFetchState","load","res","list","parsed","rows","a","b","haveData","body","AutomationRows","TABLE_COLS","toggling","running","taskTitle","table","project","hostId","host","props","toggle","next","run","TableRow","CardRow","TriggerText","Icon","automation","trigger","NextOccurrence","k","HostWarning","RunButton","onRun","name","p"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/webhook.mjs","../../src/features/automations/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"webhook\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2\",\n key: \"q3hayz\"\n }\n ],\n [\"path\", { d: \"m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06\", key: \"1go1hn\" }],\n [\"path\", { d: \"m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8\", key: \"qlwsc0\" }]\n ]\n};\n__iconData.node;\nconst Webhook = createLucideIcon(__iconData);\n\nexport { __iconData, Webhook as default };\n//# sourceMappingURL=webhook.mjs.map\n","import { Automation, type Host, type Project, type Task } from \"@switchyard/protocol\";\nimport {\n CalendarClock,\n ChevronRight,\n Clock,\n Hand,\n Link2,\n type LucideIcon,\n Play,\n Plus,\n Webhook,\n WifiOff,\n Workflow,\n} from \"lucide-react\";\nimport { type ReactNode, useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { useBreakpoint } from \"../../app/useBreakpoint\";\nimport { Button, buttonClasses } from \"../../components/ui/Button\";\nimport { EmptyState, ErrorState, FatalError, LoadingState } from \"../../components/ui/states\";\nimport { cn } from \"../../lib/cn\";\nimport { formatRelative } from \"../../lib/time\";\nimport { apiGet } from \"../../sync/api\";\nimport { useSyncStore } from \"../../sync/store\";\nimport {\n ConnectionNote,\n FailureCount,\n Switch,\n useLocalAutomations,\n useMergedAutomations,\n useRunNow,\n useToggleEnabled,\n} from \"./controls\";\nimport { describeTrigger, formatAbsolute, modeLabel, type TriggerKind } from \"./describe\";\n\nconst NEW_PATH = \"/automations/new\";\nconst editPath = (id: string) => `/automations/${encodeURIComponent(id)}`;\n\nconst TRIGGER_ICONS: Record<TriggerKind, LucideIcon> = {\n manual: Hand,\n once: Clock,\n cron: CalendarClock,\n webhook: Webhook,\n dependency: Link2,\n};\n\ntype FetchState =\n | { status: \"idle\" | \"loading\" | \"done\" }\n | { status: \"error\"; error: unknown }\n | { status: \"fatal\"; error: unknown };\n\n/** Automations list (spec 15): enabled state, trigger, next occurrence, last outcome, failures. */\nexport default function AutomationsView() {\n const hydrated = useSyncStore((s) => s.hydrated);\n const automations = useMergedAutomations();\n const projects = useSyncStore((s) => s.projects);\n const hosts = useSyncStore((s) => s.hosts);\n const tasks = useSyncStore((s) => s.tasks);\n const bp = useBreakpoint();\n const [fetchState, setFetchState] = useState<FetchState>({ status: \"idle\" });\n\n // REST fallback while the sync snapshot has not arrived (and for retry after a failure).\n const load = useCallback(async () => {\n setFetchState({ status: \"loading\" });\n try {\n const res = await apiGet<unknown>(\"/api/automations\");\n const list = Array.isArray(res)\n ? res\n : res && typeof res === \"object\" && Array.isArray((res as { items?: unknown }).items)\n ? (res as { items: unknown[] }).items\n : null;\n const parsed = list ? Automation.array().safeParse(list) : null;\n if (!parsed?.success) {\n setFetchState({\n status: \"fatal\",\n error: new Error(\"The coordinator returned automations in an unexpected format.\"),\n });\n return;\n }\n useLocalAutomations.getState().putMany(parsed.data);\n setFetchState({ status: \"done\" });\n } catch (e) {\n setFetchState({ status: \"error\", error: e });\n }\n }, []);\n\n useEffect(() => {\n if (!hydrated) void load();\n }, [hydrated, load]);\n\n const rows = useMemo(\n () => Object.values(automations).sort((a, b) => a.name.localeCompare(b.name)),\n [automations],\n );\n\n const haveData = hydrated || fetchState.status === \"done\";\n\n let body: ReactNode;\n if (!haveData) {\n if (fetchState.status === \"fatal\") body = <FatalError error={fetchState.error} />;\n else if (fetchState.status === \"error\")\n body = (\n <ErrorState\n title=\"Could not load automations\"\n error={fetchState.error}\n onRetry={() => void load()}\n />\n );\n else body = <LoadingState label=\"Loading automations…\" />;\n } else if (rows.length === 0) {\n body = (\n <EmptyState\n icon={<Workflow className=\"size-8\" />}\n title=\"No automations yet\"\n description=\"Automations create tasks on a schedule, from a webhook, or when another task is done.\"\n action={{ label: \"Create an automation\", to: NEW_PATH, icon: <Plus className=\"size-4\" /> }}\n />\n );\n } else {\n body = (\n <AutomationRows\n automations={rows}\n projects={projects}\n hosts={hosts}\n tasks={tasks}\n table={bp === \"desktop\"}\n />\n );\n }\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-4 px-4 py-4 md:px-6\">\n <header className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <h1 className=\"text-lg font-semibold\">Automations</h1>\n <p className=\"text-sm text-muted\">\n Scheduled, webhook and dependency automations. Routine successes stay in each\n automation's history.\n </p>\n </div>\n <Link to={NEW_PATH} className={buttonClasses(\"primary\")}>\n <Plus aria-hidden className=\"size-4\" />\n New automation\n </Link>\n </header>\n <ConnectionNote />\n {body}\n </div>\n );\n}\n\nconst TABLE_COLS =\n \"grid grid-cols-[minmax(0,2.4fr)_minmax(0,1fr)_5.5rem_minmax(0,1.2fr)_minmax(0,1.4fr)_7.5rem_7rem_auto] items-center gap-3\";\n\nfunction AutomationRows({\n automations,\n projects,\n hosts,\n tasks,\n table,\n}: {\n automations: Automation[];\n projects: Record<string, Project>;\n hosts: Record<string, Host>;\n tasks: Record<string, Task>;\n table: boolean;\n}) {\n const { busy: toggling, toggle } = useToggleEnabled();\n const { busy: running, run } = useRunNow();\n const taskTitle = useCallback((id: string) => tasks[id]?.title, [tasks]);\n\n return (\n <div className=\"min-w-0 overflow-hidden rounded-lg border border-border bg-surface\">\n {table && (\n <div\n aria-hidden\n className={cn(\n TABLE_COLS,\n \"border-b border-border bg-surface-2 px-4 py-2 text-xs font-medium text-muted\",\n )}\n >\n <span>Automation</span>\n <span>Project</span>\n <span>Mode</span>\n <span>Next occurrence</span>\n <span>Last outcome</span>\n <span>Failures</span>\n <span>Enabled</span>\n <span className=\"sr-only\">Actions</span>\n </div>\n )}\n <ul data-testid=\"automations-list\" className=\"divide-y divide-border\">\n {automations.map((a) => {\n const project = projects[a.projectId];\n const hostId = a.hostId ?? project?.defaultHostId ?? null;\n const host = hostId ? hosts[hostId] : undefined;\n const props: RowProps = {\n automation: a,\n projectName: project?.name ?? \"Unknown project\",\n host,\n trigger: describeTrigger(a.trigger, { taskTitle }),\n toggling: !!toggling[a.id],\n running: !!running[a.id],\n onToggle: (next) => void toggle(a, next),\n onRun: () => void run(a),\n };\n return (\n <li key={a.id} data-testid=\"automation-row\" data-automation-id={a.id}>\n {table ? <TableRow {...props} /> : <CardRow {...props} />}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\ninterface RowProps {\n automation: Automation;\n projectName: string;\n host: Host | undefined;\n trigger: string;\n toggling: boolean;\n running: boolean;\n onToggle: (next: boolean) => void;\n onRun: () => void;\n}\n\nfunction TriggerText({ automation, trigger }: { automation: Automation; trigger: string }) {\n const Icon = TRIGGER_ICONS[automation.trigger.kind];\n return (\n <span className=\"flex min-w-0 items-start gap-1.5 text-sm text-muted\">\n <Icon aria-hidden className=\"mt-0.5 size-3.5 shrink-0\" />\n <span className=\"min-w-0 break-words\">{trigger}</span>\n </span>\n );\n}\n\nfunction NextOccurrence({ automation }: { automation: Automation }) {\n if (!automation.enabled) return <span className=\"text-sm text-muted\">Paused (disabled)</span>;\n if (!automation.nextAt) {\n const k = automation.trigger.kind;\n const text =\n k === \"manual\"\n ? \"On demand\"\n : k === \"webhook\"\n ? \"On webhook\"\n : k === \"dependency\"\n ? \"When the task is done\"\n : \"None scheduled\";\n return <span className=\"text-sm text-muted\">{text}</span>;\n }\n return (\n <span className=\"flex min-w-0 flex-col text-sm\">\n <span>{formatRelative(automation.nextAt)}</span>\n <time dateTime={automation.nextAt} className=\"text-xs text-muted\">\n {formatAbsolute(automation.nextAt)}\n </time>\n </span>\n );\n}\n\nfunction HostWarning({ host }: { host: Host | undefined }) {\n if (!host || host.connectivity === \"connected\") return null;\n return (\n <span className=\"flex items-center gap-1 text-xs text-danger\">\n <WifiOff aria-hidden className=\"size-3.5 shrink-0\" />\n <span className=\"min-w-0 break-words\">\n Host {host.name} {host.connectivity === \"reconnecting\" ? \"reconnecting\" : \"unreachable\"}\n </span>\n </span>\n );\n}\n\nfunction RunButton({\n running,\n onRun,\n name,\n}: {\n running: boolean;\n onRun: () => void;\n name: string;\n}) {\n return (\n <Button\n size=\"sm\"\n busy={running}\n icon={<Play className=\"size-3.5\" />}\n onClick={onRun}\n aria-label={`Run ${name} now`}\n >\n Run now\n </Button>\n );\n}\n\nfunction TableRow(p: RowProps) {\n const a = p.automation;\n return (\n <div className={cn(TABLE_COLS, \"px-4 py-3\")}>\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <Link\n to={editPath(a.id)}\n className=\"truncate font-medium text-text hover:underline focus-visible:underline\"\n >\n {a.name}\n </Link>\n <TriggerText automation={a} trigger={p.trigger} />\n </div>\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"sr-only\">Project: </span>\n <span className=\"truncate text-sm\">{p.projectName}</span>\n <HostWarning host={p.host} />\n </div>\n <span className=\"text-sm\">\n <span className=\"sr-only\">Mode: </span>\n {modeLabel(a.mode)}\n </span>\n <div className=\"min-w-0\">\n <span className=\"sr-only\">Next occurrence: </span>\n <NextOccurrence automation={a} />\n </div>\n <div className=\"min-w-0 text-sm break-words\">\n <span className=\"sr-only\">Last outcome: </span>\n {a.lastOutcome ?? <span className=\"text-muted\">No runs yet</span>}\n </div>\n <FailureCount count={a.failureCount} />\n <Switch\n checked={a.enabled}\n onChange={p.onToggle}\n busy={p.toggling}\n label={`Enable ${a.name}`}\n onText=\"Enabled\"\n offText=\"Disabled\"\n />\n <div className=\"flex items-center gap-1\">\n <RunButton running={p.running} onRun={p.onRun} name={a.name} />\n <Link\n to={editPath(a.id)}\n aria-label={`Edit ${a.name}`}\n title=\"Edit\"\n className=\"tap inline-flex size-9 items-center justify-center rounded-md text-muted hover:bg-surface-2 hover:text-text\"\n >\n <ChevronRight aria-hidden className=\"size-4\" />\n </Link>\n </div>\n </div>\n );\n}\n\nfunction CardRow(p: RowProps) {\n const a = p.automation;\n return (\n <div className=\"flex min-w-0 flex-col gap-2 px-4 py-3\">\n <div className=\"flex min-w-0 items-start justify-between gap-2\">\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <Link\n to={editPath(a.id)}\n className=\"font-medium break-words text-text hover:underline focus-visible:underline\"\n >\n {a.name}\n </Link>\n <TriggerText automation={a} trigger={p.trigger} />\n </div>\n <Switch\n checked={a.enabled}\n onChange={p.onToggle}\n busy={p.toggling}\n label={`Enable ${a.name}`}\n onText=\"Enabled\"\n offText=\"Disabled\"\n />\n </div>\n <dl className=\"grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm\">\n <dt className=\"text-muted\">Project</dt>\n <dd className=\"flex min-w-0 flex-col break-words\">\n {p.projectName}\n <HostWarning host={p.host} />\n </dd>\n <dt className=\"text-muted\">Mode</dt>\n <dd>{modeLabel(a.mode)}</dd>\n <dt className=\"text-muted\">Next</dt>\n <dd className=\"min-w-0\">\n <NextOccurrence automation={a} />\n </dd>\n <dt className=\"text-muted\">Last outcome</dt>\n <dd className=\"min-w-0 break-words\">\n {a.lastOutcome ?? <span className=\"text-muted\">No runs yet</span>}\n </dd>\n <dt className=\"text-muted\">Failures</dt>\n <dd>\n <FailureCount count={a.failureCount} />\n </dd>\n </dl>\n <div className=\"flex flex-wrap gap-2\">\n <RunButton running={p.running} onRun={p.onRun} name={a.name} />\n <Link to={editPath(a.id)} className={buttonClasses(\"ghost\", \"sm\")}>\n Edit\n <ChevronRight aria-hidden className=\"size-3.5\" />\n </Link>\n </div>\n </div>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}
1
+ {"version":3,"file":"automations-DSFUzB0w.js","names":["useState","useCallback","useMemo","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useSyncStore","apiGet","cn","Button","ErrorState","LoadingState","formatRelative","FatalError","EmptyState","buttonClasses","CalendarClock","ChevronRight","Hand","Link2","Switch","ConnectionNote","useRunNow","useToggleEnabled","useLocalAutomations","useMergedAutomations","modeLabel","describeTrigger","formatAbsolute","FailureCount","Play","Plus","Clock","WifiOff","Workflow","useBreakpoint","Automation","__iconData","Webhook","import_react","import_jsx_runtime","NEW_PATH","editPath","id","TRIGGER_ICONS","AutomationsView","hydrated","s","automations","projects","hosts","tasks","bp","fetchState","setFetchState","load","res","list","parsed","rows","a","b","haveData","body","AutomationRows","TABLE_COLS","toggling","running","taskTitle","table","project","hostId","host","props","toggle","next","run","TableRow","CardRow","TriggerText","Icon","automation","trigger","NextOccurrence","k","HostWarning","RunButton","onRun","name","p"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/webhook.mjs","../../src/features/automations/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"webhook\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2\",\n key: \"q3hayz\"\n }\n ],\n [\"path\", { d: \"m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06\", key: \"1go1hn\" }],\n [\"path\", { d: \"m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8\", key: \"qlwsc0\" }]\n ]\n};\n__iconData.node;\nconst Webhook = createLucideIcon(__iconData);\n\nexport { __iconData, Webhook as default };\n//# sourceMappingURL=webhook.mjs.map\n","import { Automation, type Host, type Project, type Task } from \"@switchyard/protocol\";\nimport {\n CalendarClock,\n ChevronRight,\n Clock,\n Hand,\n Link2,\n type LucideIcon,\n Play,\n Plus,\n Webhook,\n WifiOff,\n Workflow,\n} from \"lucide-react\";\nimport { type ReactNode, useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { useBreakpoint } from \"../../app/useBreakpoint\";\nimport { Button, buttonClasses } from \"../../components/ui/Button\";\nimport { EmptyState, ErrorState, FatalError, LoadingState } from \"../../components/ui/states\";\nimport { cn } from \"../../lib/cn\";\nimport { formatRelative } from \"../../lib/time\";\nimport { apiGet } from \"../../sync/api\";\nimport { useSyncStore } from \"../../sync/store\";\nimport {\n ConnectionNote,\n FailureCount,\n Switch,\n useLocalAutomations,\n useMergedAutomations,\n useRunNow,\n useToggleEnabled,\n} from \"./controls\";\nimport { describeTrigger, formatAbsolute, modeLabel, type TriggerKind } from \"./describe\";\n\nconst NEW_PATH = \"/automations/new\";\nconst editPath = (id: string) => `/automations/${encodeURIComponent(id)}`;\n\nconst TRIGGER_ICONS: Record<TriggerKind, LucideIcon> = {\n manual: Hand,\n once: Clock,\n cron: CalendarClock,\n webhook: Webhook,\n dependency: Link2,\n};\n\ntype FetchState =\n | { status: \"idle\" | \"loading\" | \"done\" }\n | { status: \"error\"; error: unknown }\n | { status: \"fatal\"; error: unknown };\n\n/** Automations list (spec 15): enabled state, trigger, next occurrence, last outcome, failures. */\nexport default function AutomationsView() {\n const hydrated = useSyncStore((s) => s.hydrated);\n const automations = useMergedAutomations();\n const projects = useSyncStore((s) => s.projects);\n const hosts = useSyncStore((s) => s.hosts);\n const tasks = useSyncStore((s) => s.tasks);\n const bp = useBreakpoint();\n const [fetchState, setFetchState] = useState<FetchState>({ status: \"idle\" });\n\n // REST fallback while the sync snapshot has not arrived (and for retry after a failure).\n const load = useCallback(async () => {\n setFetchState({ status: \"loading\" });\n try {\n const res = await apiGet<unknown>(\"/api/automations\");\n const list = Array.isArray(res)\n ? res\n : res && typeof res === \"object\" && Array.isArray((res as { items?: unknown }).items)\n ? (res as { items: unknown[] }).items\n : null;\n const parsed = list ? Automation.array().safeParse(list) : null;\n if (!parsed?.success) {\n setFetchState({\n status: \"fatal\",\n error: new Error(\"The coordinator returned automations in an unexpected format.\"),\n });\n return;\n }\n useLocalAutomations.getState().putMany(parsed.data);\n setFetchState({ status: \"done\" });\n } catch (e) {\n setFetchState({ status: \"error\", error: e });\n }\n }, []);\n\n useEffect(() => {\n if (!hydrated) void load();\n }, [hydrated, load]);\n\n const rows = useMemo(\n () => Object.values(automations).sort((a, b) => a.name.localeCompare(b.name)),\n [automations],\n );\n\n const haveData = hydrated || fetchState.status === \"done\";\n\n let body: ReactNode;\n if (!haveData) {\n if (fetchState.status === \"fatal\") body = <FatalError error={fetchState.error} />;\n else if (fetchState.status === \"error\")\n body = (\n <ErrorState\n title=\"Could not load automations\"\n error={fetchState.error}\n onRetry={() => void load()}\n />\n );\n else body = <LoadingState label=\"Loading automations…\" />;\n } else if (rows.length === 0) {\n body = (\n <EmptyState\n icon={<Workflow className=\"size-8\" />}\n title=\"No automations yet\"\n description=\"Automations create tasks on a schedule, from a webhook, or when another task is done.\"\n action={{ label: \"Create an automation\", to: NEW_PATH, icon: <Plus className=\"size-4\" /> }}\n />\n );\n } else {\n body = (\n <AutomationRows\n automations={rows}\n projects={projects}\n hosts={hosts}\n tasks={tasks}\n table={bp === \"desktop\"}\n />\n );\n }\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-4 px-4 py-4 md:px-6\">\n <header className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <h1 className=\"text-lg font-semibold\">Automations</h1>\n <p className=\"text-sm text-muted\">\n Scheduled, webhook and dependency automations. Routine successes stay in each\n automation's history.\n </p>\n </div>\n <Link to={NEW_PATH} className={buttonClasses(\"primary\")}>\n <Plus aria-hidden className=\"size-4\" />\n New automation\n </Link>\n </header>\n <ConnectionNote />\n {body}\n </div>\n );\n}\n\nconst TABLE_COLS =\n \"grid grid-cols-[minmax(0,2.4fr)_minmax(0,1fr)_5.5rem_minmax(0,1.2fr)_minmax(0,1.4fr)_7.5rem_7rem_auto] items-center gap-3\";\n\nfunction AutomationRows({\n automations,\n projects,\n hosts,\n tasks,\n table,\n}: {\n automations: Automation[];\n projects: Record<string, Project>;\n hosts: Record<string, Host>;\n tasks: Record<string, Task>;\n table: boolean;\n}) {\n const { busy: toggling, toggle } = useToggleEnabled();\n const { busy: running, run } = useRunNow();\n const taskTitle = useCallback((id: string) => tasks[id]?.title, [tasks]);\n\n return (\n <div className=\"min-w-0 overflow-hidden rounded-lg border border-border bg-surface\">\n {table && (\n <div\n aria-hidden\n className={cn(\n TABLE_COLS,\n \"border-b border-border bg-surface-2 px-4 py-2 text-xs font-medium text-muted\",\n )}\n >\n <span>Automation</span>\n <span>Project</span>\n <span>Mode</span>\n <span>Next occurrence</span>\n <span>Last outcome</span>\n <span>Failures</span>\n <span>Enabled</span>\n <span className=\"sr-only\">Actions</span>\n </div>\n )}\n <ul data-testid=\"automations-list\" className=\"divide-y divide-border\">\n {automations.map((a) => {\n const project = projects[a.projectId];\n const hostId = a.hostId ?? project?.defaultHostId ?? null;\n const host = hostId ? hosts[hostId] : undefined;\n const props: RowProps = {\n automation: a,\n projectName: project?.name ?? \"Unknown project\",\n host,\n trigger: describeTrigger(a.trigger, { taskTitle }),\n toggling: !!toggling[a.id],\n running: !!running[a.id],\n onToggle: (next) => void toggle(a, next),\n onRun: () => void run(a),\n };\n return (\n <li key={a.id} data-testid=\"automation-row\" data-automation-id={a.id}>\n {table ? <TableRow {...props} /> : <CardRow {...props} />}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\ninterface RowProps {\n automation: Automation;\n projectName: string;\n host: Host | undefined;\n trigger: string;\n toggling: boolean;\n running: boolean;\n onToggle: (next: boolean) => void;\n onRun: () => void;\n}\n\nfunction TriggerText({ automation, trigger }: { automation: Automation; trigger: string }) {\n const Icon = TRIGGER_ICONS[automation.trigger.kind];\n return (\n <span className=\"flex min-w-0 items-start gap-1.5 text-sm text-muted\">\n <Icon aria-hidden className=\"mt-0.5 size-3.5 shrink-0\" />\n <span className=\"min-w-0 break-words\">{trigger}</span>\n </span>\n );\n}\n\nfunction NextOccurrence({ automation }: { automation: Automation }) {\n if (!automation.enabled) return <span className=\"text-sm text-muted\">Paused (disabled)</span>;\n if (!automation.nextAt) {\n const k = automation.trigger.kind;\n const text =\n k === \"manual\"\n ? \"On demand\"\n : k === \"webhook\"\n ? \"On webhook\"\n : k === \"dependency\"\n ? \"When the task is done\"\n : \"None scheduled\";\n return <span className=\"text-sm text-muted\">{text}</span>;\n }\n return (\n <span className=\"flex min-w-0 flex-col text-sm\">\n <span>{formatRelative(automation.nextAt)}</span>\n <time dateTime={automation.nextAt} className=\"text-xs text-muted\">\n {formatAbsolute(automation.nextAt)}\n </time>\n </span>\n );\n}\n\nfunction HostWarning({ host }: { host: Host | undefined }) {\n if (!host || host.connectivity === \"connected\") return null;\n return (\n <span className=\"flex items-center gap-1 text-xs text-danger\">\n <WifiOff aria-hidden className=\"size-3.5 shrink-0\" />\n <span className=\"min-w-0 break-words\">\n Host {host.name} {host.connectivity === \"reconnecting\" ? \"reconnecting\" : \"unreachable\"}\n </span>\n </span>\n );\n}\n\nfunction RunButton({\n running,\n onRun,\n name,\n}: {\n running: boolean;\n onRun: () => void;\n name: string;\n}) {\n return (\n <Button\n size=\"sm\"\n busy={running}\n icon={<Play className=\"size-3.5\" />}\n onClick={onRun}\n aria-label={`Run ${name} now`}\n >\n Run now\n </Button>\n );\n}\n\nfunction TableRow(p: RowProps) {\n const a = p.automation;\n return (\n <div className={cn(TABLE_COLS, \"px-4 py-3\")}>\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <Link\n to={editPath(a.id)}\n className=\"truncate font-medium text-text hover:underline focus-visible:underline\"\n >\n {a.name}\n </Link>\n <TriggerText automation={a} trigger={p.trigger} />\n </div>\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"sr-only\">Project: </span>\n <span className=\"truncate text-sm\">{p.projectName}</span>\n <HostWarning host={p.host} />\n </div>\n <span className=\"text-sm\">\n <span className=\"sr-only\">Mode: </span>\n {modeLabel(a.mode)}\n </span>\n <div className=\"min-w-0\">\n <span className=\"sr-only\">Next occurrence: </span>\n <NextOccurrence automation={a} />\n </div>\n <div className=\"min-w-0 text-sm break-words\">\n <span className=\"sr-only\">Last outcome: </span>\n {a.lastOutcome ?? <span className=\"text-muted\">No runs yet</span>}\n </div>\n <FailureCount count={a.failureCount} />\n <Switch\n checked={a.enabled}\n onChange={p.onToggle}\n busy={p.toggling}\n label={`Enable ${a.name}`}\n onText=\"Enabled\"\n offText=\"Disabled\"\n />\n <div className=\"flex items-center gap-1\">\n <RunButton running={p.running} onRun={p.onRun} name={a.name} />\n <Link\n to={editPath(a.id)}\n aria-label={`Edit ${a.name}`}\n title=\"Edit\"\n className=\"tap inline-flex size-9 items-center justify-center rounded-md text-muted hover:bg-surface-2 hover:text-text\"\n >\n <ChevronRight aria-hidden className=\"size-4\" />\n </Link>\n </div>\n </div>\n );\n}\n\nfunction CardRow(p: RowProps) {\n const a = p.automation;\n return (\n <div className=\"flex min-w-0 flex-col gap-2 px-4 py-3\">\n <div className=\"flex min-w-0 items-start justify-between gap-2\">\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <Link\n to={editPath(a.id)}\n className=\"font-medium break-words text-text hover:underline focus-visible:underline\"\n >\n {a.name}\n </Link>\n <TriggerText automation={a} trigger={p.trigger} />\n </div>\n <Switch\n checked={a.enabled}\n onChange={p.onToggle}\n busy={p.toggling}\n label={`Enable ${a.name}`}\n onText=\"Enabled\"\n offText=\"Disabled\"\n />\n </div>\n <dl className=\"grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm\">\n <dt className=\"text-muted\">Project</dt>\n <dd className=\"flex min-w-0 flex-col break-words\">\n {p.projectName}\n <HostWarning host={p.host} />\n </dd>\n <dt className=\"text-muted\">Mode</dt>\n <dd>{modeLabel(a.mode)}</dd>\n <dt className=\"text-muted\">Next</dt>\n <dd className=\"min-w-0\">\n <NextOccurrence automation={a} />\n </dd>\n <dt className=\"text-muted\">Last outcome</dt>\n <dd className=\"min-w-0 break-words\">\n {a.lastOutcome ?? <span className=\"text-muted\">No runs yet</span>}\n </dd>\n <dt className=\"text-muted\">Failures</dt>\n <dd>\n <FailureCount count={a.failureCount} />\n </dd>\n </dl>\n <div className=\"flex flex-wrap gap-2\">\n <RunButton running={p.running} onRun={p.onRun} name={a.name} />\n <Link to={editPath(a.id)} className={buttonClasses(\"ghost\", \"sm\")}>\n Edit\n <ChevronRight aria-hidden className=\"size-3.5\" />\n </Link>\n </div>\n </div>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}