@spencerboucher/switchyard 0.4.0 → 0.6.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.
- package/README.md +8 -5
- package/dist/bridge/pi-model.js +44 -0
- package/dist/bridge/switchyard-bridge.js +24 -2
- package/dist/bridge/switchyard-terminal.js +608 -0
- package/dist/switchyard.mjs +3811 -1811
- package/dist/terminal/holder.js +305 -0
- package/package.json +6 -3
- package/web-dist/assets/{AutomationView-BhEfjAtI.js → AutomationView-DgZn3ltn.js} +2 -2
- package/web-dist/assets/{AutomationView-BhEfjAtI.js.map → AutomationView-DgZn3ltn.js.map} +1 -1
- package/web-dist/assets/{InteractionCard-DxEZUNrD.js → InteractionCard-cvPWKSu1.js} +2 -2
- package/web-dist/assets/{InteractionCard-DxEZUNrD.js.map → InteractionCard-cvPWKSu1.js.map} +1 -1
- package/web-dist/assets/{Menu-C4GUY_ym.js → Menu-GymfuHGP.js} +2 -2
- package/web-dist/assets/Menu-GymfuHGP.js.map +1 -0
- package/web-dist/assets/{Overlay-C-GqmwUn.js → Overlay-oYnnwvFM.js} +2 -2
- package/web-dist/assets/{Overlay-C-GqmwUn.js.map → Overlay-oYnnwvFM.js.map} +1 -1
- package/web-dist/assets/{attention-Dzqdk9Gz.js → attention-Bxg9W15n.js} +2 -2
- package/web-dist/assets/{attention-Dzqdk9Gz.js.map → attention-Bxg9W15n.js.map} +1 -1
- package/web-dist/assets/{automations-DzTTwxBl.js → automations-DdB97dnL.js} +2 -2
- package/web-dist/assets/{automations-DzTTwxBl.js.map → automations-DdB97dnL.js.map} +1 -1
- package/web-dist/assets/{board-C5mG_8rg.js → board-s4LuDPcg.js} +2 -2
- package/web-dist/assets/{board-C5mG_8rg.js.map → board-s4LuDPcg.js.map} +1 -1
- package/web-dist/assets/code-B0KXn7lD.js +8 -0
- package/web-dist/assets/code-B0KXn7lD.js.map +1 -0
- package/web-dist/assets/{describe-BbCZVvJP.js → describe-C_3FJuSs.js} +2 -2
- package/web-dist/assets/{describe-BbCZVvJP.js.map → describe-C_3FJuSs.js.map} +1 -1
- package/web-dist/assets/dist-Bgkh2hc5.js +56 -0
- package/web-dist/assets/dist-Bgkh2hc5.js.map +1 -0
- package/web-dist/assets/{enroll-DWJANJtw.js → enroll-DijmoolY.js} +2 -2
- package/web-dist/assets/{enroll-DWJANJtw.js.map → enroll-DijmoolY.js.map} +1 -1
- package/web-dist/assets/index-C5n2QB-Y.css +2 -0
- package/web-dist/assets/{index-afOJ8NRR.js → index-Cf6ZUCfK.js} +3 -3
- package/web-dist/assets/{index-afOJ8NRR.js.map → index-Cf6ZUCfK.js.map} +1 -1
- package/web-dist/assets/{more-B6qxC6M-.js → more-CRGJsz6E.js} +2 -2
- package/web-dist/assets/{more-B6qxC6M-.js.map → more-CRGJsz6E.js.map} +1 -1
- package/web-dist/assets/{now-BOWxgbt3.js → now-D6lDofYy.js} +2 -2
- package/web-dist/assets/{now-BOWxgbt3.js.map → now-D6lDofYy.js.map} +1 -1
- package/web-dist/assets/{settings-BpkmIv_4.js → settings-Csal1F-W.js} +6 -6
- package/web-dist/assets/settings-Csal1F-W.js.map +1 -0
- package/web-dist/assets/{useNow-D9Zxr5Jb.js → useNow-Bc_NlDY3.js} +2 -2
- package/web-dist/assets/{useNow-D9Zxr5Jb.js.map → useNow-Bc_NlDY3.js.map} +1 -1
- package/web-dist/index.html +2 -2
- package/web-dist/sw.js +1 -1
- package/web-dist/assets/Menu-C4GUY_ym.js.map +0 -1
- package/web-dist/assets/code-9shyh1Gw.js +0 -8
- package/web-dist/assets/code-9shyh1Gw.js.map +0 -1
- package/web-dist/assets/dist-PaQekTP8.js +0 -56
- package/web-dist/assets/dist-PaQekTP8.js.map +0 -1
- package/web-dist/assets/index-ByiuDoG0.css +0 -2
- package/web-dist/assets/settings-BpkmIv_4.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Overlay-C-GqmwUn.js","names":["forwardRef","useRef","useRef","useId","createPortal","__toESM","require_react","require_jsx_runtime","cn","require_react_dom","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"],"sources":["../../src/components/ui/IconButton.tsx","../../src/components/ui/useFocusTrap.ts","../../src/components/ui/Overlay.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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Overlay-oYnnwvFM.js","names":["forwardRef","useRef","useRef","useId","createPortal","__toESM","require_react","require_jsx_runtime","cn","require_react_dom","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"],"sources":["../../src/components/ui/IconButton.tsx","../../src/components/ui/useFocusTrap.ts","../../src/components/ui/Overlay.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"],"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,d,i as f,l as p,n as m,o as h,p as g,t as _}from"./states-DrTQuYXw.js";import{t as v}from"./server-SxFxIScj.js";import{t as y}from"./external-link-3jjTxo92.js";import{t as b}from"./InteractionCard-
|
|
2
|
-
//# sourceMappingURL=attention-
|
|
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,d,i as f,l as p,n as m,o as h,p as g,t as _}from"./states-DrTQuYXw.js";import{t as v}from"./server-SxFxIScj.js";import{t as y}from"./external-link-3jjTxo92.js";import{t as b}from"./InteractionCard-cvPWKSu1.js";import{E as x,I as S,L as C,c as w,d as T,l as E,o as D,t as O}from"./index-Cf6ZUCfK.js";import{t as k}from"./SegmentedControl-ChdVilXb.js";var A={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`}]]};A.node;var j=r(A),M=e(n(),1),N=i(),P=[`needs_input`,`failed`,`ready_for_review`,`unsupported_interaction`,`host_unreachable`],F={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 I(e){let t=Object.fromEntries(P.map(e=>[e,[]]));for(let n of e)t[n.reason]?.push(n);return t}function L(){let e=a(),t=o(e=>e.hydrated),n=s(),[r,i]=D(`attention`,`all`),c=(0,M.useMemo)(()=>r===`new`?e.filter(e=>!e.acknowledgedAt):e,[e,r]),l=(0,M.useMemo)(()=>I(c),[c]);return(0,N.jsxs)(`div`,{className:`mx-auto flex w-full max-w-3xl min-w-0 flex-col gap-4 px-4 py-4 safe-pt`,children:[(0,N.jsxs)(`header`,{className:`flex min-w-0 flex-wrap items-center gap-3`,children:[(0,N.jsx)(`h1`,{className:`flex-1 text-lg font-semibold`,children:`Attention`}),(0,N.jsx)(k,{label:`Show`,value:r,onChange:i,options:[{value:`all`,label:`All open`},{value:`new`,label:`Not acknowledged`}]})]}),n.state!==`live`&&t&&(0,N.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,N.jsx)(_,{icon:(0,N.jsx)(C,{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:O.board}}):P.filter(e=>l[e].length>0).map(e=>{let t=E[e],n=t.icon;return(0,N.jsxs)(`section`,{"data-testid":`attention-group-${e}`,"aria-labelledby":`att-${e}`,className:`flex min-w-0 flex-col gap-2`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsxs)(`h2`,{id:`att-${e}`,className:`flex items-center gap-2 text-base font-semibold`,children:[(0,N.jsx)(n,{"aria-hidden":!0,className:`size-4 text-muted`}),t.label,(0,N.jsxs)(`span`,{className:`text-sm font-normal text-muted`,children:[`(`,l[e].length,`)`]})]}),(0,N.jsx)(`p`,{className:`text-sm text-muted`,children:F[e]})]}),(0,N.jsx)(`ul`,{className:`flex min-w-0 flex-col gap-2`,children:l[e].map(e=>(0,N.jsx)(`li`,{children:(0,N.jsx)(R,{item:e})},e.id))})]},e)}):(0,N.jsx)(h,{label:`Loading attention…`})]})}function R({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),[h,_]=(0,M.useState)(!1),[C,D]=(0,M.useState)(null),k=async()=>{_(!0),D(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}}:{}),w.success(`Acknowledged. It stays open until the underlying task or question is resolved.`)}catch(e){D(e)}finally{_(!1)}},A=e.sessionId?e.interactionId?O.interaction(e.sessionId,e.interactionId):O.session(e.sessionId):null,P=!!a&&a.connectivity!==`connected`,F=null;switch(e.reason){case`needs_input`:F=s&&s.state===`pending`?(0,N.jsx)(b,{interaction:s,readOnlyReason:P?`Host ${a?.name} is offline. You can answer once it reconnects.`:i?.readOnly?`This session is read-only.`:null}):s?(0,N.jsx)(b,{interaction:s}):A?(0,N.jsxs)(t,{to:A,className:d(`primary`,`sm`),children:[(0,N.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Answer in session`]}):null;break;case`failed`:F=e.taskId?(0,N.jsxs)(t,{to:O.task(e.taskId),className:d(`primary`,`sm`),children:[(0,N.jsx)(j,{"aria-hidden":!0,className:`size-3.5`}),`Open task`]}):null;break;case`ready_for_review`:F=e.sessionId?(0,N.jsxs)(t,{to:`${O.session(e.sessionId)}?tab=changes`,className:d(`primary`,`sm`),children:[(0,N.jsx)(x,{"aria-hidden":!0,className:`size-3.5`}),`Review changes`]}):e.taskId?(0,N.jsxs)(t,{to:O.task(e.taskId),className:d(`primary`,`sm`),children:[(0,N.jsx)(x,{"aria-hidden":!0,className:`size-3.5`}),`Open review`]}):null;break;case`unsupported_interaction`:F=s?(0,N.jsx)(b,{interaction:s,readOnlyReason:P?`Host ${a?.name} is offline. You can choose once it reconnects.`:i?.readOnly?`This session is read-only.`:null}):(0,N.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`:F=a?(0,N.jsx)(f,{compact:!0,hostName:a.name,connectivity:a.connectivity===`reconnecting`?`reconnecting`:`unreachable`,lastSeenAt:a.lastSeenAt}):null}return(0,N.jsxs)(`article`,{"aria-label":`${E[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,N.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,N.jsx)(T,{kind:`attention`,value:e.reason,size:`sm`}),(0,N.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,N.jsx)(`time`,{dateTime:e.createdAt,className:`shrink-0 text-xs text-subtle`,children:g(e.createdAt)})]}),(0,N.jsx)(`p`,{className:`min-w-0 text-sm break-words`,children:e.summary}),F&&(0,N.jsx)(`div`,{className:`flex min-w-0 flex-col items-start`,children:F}),(0,N.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[e.reason!==`failed`&&e.taskId&&(0,N.jsxs)(t,{to:O.task(e.taskId),className:d(`ghost`,`sm`),children:[(0,N.jsx)(j,{"aria-hidden":!0,className:`size-3.5`}),`Task`]}),A&&(0,N.jsxs)(t,{to:A,className:d(`ghost`,`sm`),children:[(0,N.jsx)(y,{"aria-hidden":!0,className:`size-3.5`}),`Open session`]}),e.reason===`host_unreachable`&&a&&(0,N.jsxs)(t,{to:`${O.settings(`hosts`)}/${encodeURIComponent(a.id)}`,className:d(`ghost`,`sm`),children:[(0,N.jsx)(v,{"aria-hidden":!0,className:`size-3.5`}),`Host details`]}),(0,N.jsx)(`span`,{className:`ml-auto`}),e.acknowledgedAt?(0,N.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-xs text-muted`,children:[(0,N.jsx)(S,{"aria-hidden":!0,className:`size-3.5`}),`Acknowledged `,g(e.acknowledgedAt)]}):(0,N.jsx)(p,{size:`sm`,busy:h,onClick:()=>void k(),title:`Hide the alert without resolving the task`,children:`Acknowledge`})]}),C!==null&&(0,N.jsx)(m,{compact:!0,title:`Could not acknowledge`,error:l(C),onRetry:()=>void k()})]})}export{P as ATTENTION_GROUPS,L as default,I as groupAttention};
|
|
2
|
+
//# sourceMappingURL=attention-Bxg9W15n.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attention-Dzqdk9Gz.js","names":["useMemo","useState","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useOpenAttention","useSyncStore","useConnection","apiPost","errorMessage","UnsupportedCapability","HostUnavailable","Button","ErrorState","LoadingState","formatRelative","EmptyState","Server","ExternalLink","InteractionCard","Eye","CircleCheck","Bell","toast","StatusChip","ATTENTION_STATUS","useFilter","ROUTES","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","buttonClasses"],"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 } 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=\"mx-auto flex w-full max-w-3xl min-w-0 flex-col gap-4 px-4 py-4 safe-pt\">\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 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}>\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 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 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 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-Bxg9W15n.js","names":["useMemo","useState","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useOpenAttention","useSyncStore","useConnection","apiPost","errorMessage","UnsupportedCapability","HostUnavailable","Button","ErrorState","LoadingState","formatRelative","EmptyState","Server","ExternalLink","InteractionCard","Eye","CircleCheck","Bell","toast","StatusChip","ATTENTION_STATUS","useFilter","ROUTES","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","buttonClasses"],"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 } 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=\"mx-auto flex w-full max-w-3xl min-w-0 flex-col gap-4 px-4 py-4 safe-pt\">\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 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}>\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 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 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 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,d as s,h as c,l,n as u,o as d,p as f,r as p,t as m}from"./states-DrTQuYXw.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-
|
|
2
|
-
//# sourceMappingURL=automations-
|
|
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,d as s,h as c,l,n as u,o as d,p as f,r as p,t as m}from"./states-DrTQuYXw.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-C_3FJuSs.js";import{t as k}from"./play-CjoDg3ws.js";import{t as A}from"./plus-BLP-wIYD.js";import{_ as j,k as M,n as N,v as P}from"./index-Cf6ZUCfK.js";import{a as F}from"./dist-Bgkh2hc5.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:M,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),c=a(e=>e.tasks),l=N(),[f,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||f.status===`done`,y;return y=v?_.length===0?(0,z.jsx)(m,{icon:(0,z.jsx)(j,{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:c,table:l===`desktop`}):f.status===`fatal`?(0,z.jsx)(p,{error:f.error}):f.status===`error`?(0,z.jsx)(u,{title:`Could not load automations`,error:f.error,onRetry:()=>void g()}):(0,z.jsx)(d,{label:`Loading automations…`}),(0,z.jsxs)(`div`,{className:`mx-auto flex w-full max-w-6xl 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:s(`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:s,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:c(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],c=e.hostId??r?.defaultHostId??null,d=c?n[c]:void 0,f={automation:e,projectName:r?.name??`Unknown project`,host:d,trigger:E(e.trigger,{taskTitle:u}),toggling:!!a[e.id],running:!!s[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:f(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)(P,{"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)(l,{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:c(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:s(`ghost`,`sm`),children:[`Edit`,(0,z.jsx)(g,{"aria-hidden":!0,className:`size-3.5`})]})]})]})}export{U as default};
|
|
2
|
+
//# sourceMappingURL=automations-DdB97dnL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automations-DzTTwxBl.js","names":["useState","useCallback","useMemo","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useSyncStore","apiGet","buttonClasses","cn","ErrorState","LoadingState","formatRelative","FatalError","EmptyState","CalendarClock","ChevronRight","Hand","Link2","Switch","ConnectionNote","useRunNow","useToggleEnabled","useLocalAutomations","useMergedAutomations","modeLabel","describeTrigger","formatAbsolute","FailureCount","Play","Plus","Workflow","Clock","useBreakpoint","WifiOff","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","Button","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=\"mx-auto flex w-full max-w-6xl 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":"A,0nBASA,IAAMsC,EAAa,CACjB,KAAM,UACN,KAAM,GACN,KAAM,CACJ,CACE,OACA,CACE,EAAG,2EACH,IAAK,QACP,CACF,EACA,CAAC,OAAQ,CAAE,EAAG,4DAA6D,IAAK,QAAS,CAAC,EAC1F,CAAC,OAAQ,CAAE,EAAG,yDAA0D,IAAK,QAAS,CAAC,CACzF,CACF,EACAA,EAAW,KACX,IAAMC,EAAUjC,EAAiBgC,CAAU,E,iBCSrCI,EAAW,mBACXC,EAAY,GAAe,gBAAgB,mBAAmBC,CAAE,IAEhEC,EAAiD,CACrD,OAAQ1B,EACR,KAAMe,EACN,KAAMjB,EACN,QAASsB,EACT,WAAYnB,CACd,EAQA,SAAwB0B,GAAkB,CACxC,IAAMC,EAAWvC,EAAc,GAAMwC,EAAE,QAAQ,EACzCC,EAAcvB,EAAqB,EACnCwB,EAAW1C,EAAc,GAAMwC,EAAE,QAAQ,EACzCG,EAAQ3C,EAAc,GAAMwC,EAAE,KAAK,EACnCI,EAAQ5C,EAAc,GAAMwC,EAAE,KAAK,EACnCK,EAAKlB,EAAc,EACnB,CAACmB,EAAYC,IAAA,EAAiBvD,EAAAA,SAAAA,CAAqB,CAAE,OAAQ,MAAO,CAAC,EAGrEwD,GAAA,EAAOvD,EAAAA,YAAAA,CAAY,SAAY,CACnCsD,EAAc,CAAE,OAAQ,SAAU,CAAC,EACnC,GAAI,CACF,IAAME,EAAM,MAAMhD,EAAgB,kBAAkB,EAC9CiD,EAAO,MAAM,QAAQD,CAAG,EAC1BA,EACAA,GAAO,OAAOA,GAAQ,UAAY,MAAM,QAASA,EAA4B,KAAK,EAC/EA,EAA6B,MAC9B,KACAE,EAASD,EAAOrB,EAAW,MAAM,CAAC,CAAC,UAAUqB,CAAI,EAAI,KAC3D,GAAI,CAACC,GAAQ,QAAS,CACpBJ,EAAc,CACZ,OAAQ,QACR,MAAW,MAAM,+DAA+D,CAClF,CAAC,EACD,MACF,CACA9B,EAAoB,SAAS,CAAC,CAAC,QAAQkC,EAAO,IAAI,EAClDJ,EAAc,CAAE,OAAQ,MAAO,CAAC,CAClC,OAAS,EAAG,CACVA,EAAc,CAAE,OAAQ,QAAS,MAAO,CAAE,CAAC,CAC7C,CACF,EAAG,CAAC,CAAC,GAEL,EAAAf,EAAA,UAAA,KAAgB,CACTO,GAAUS,EAAU,CAC3B,EAAG,CAACT,EAAUS,CAAI,CAAC,EAEnB,IAAMI,GAAA,EAAO1D,EAAAA,QAAAA,KACL,OAAO,OAAO+C,CAAW,CAAC,CAAC,MAAM,EAAG,IAAMY,EAAE,KAAK,cAAcC,EAAE,IAAI,CAAC,EAC5E,CAACb,CAAW,CACd,EAEMc,EAAWhB,GAAYO,EAAW,SAAW,OAE/CU,EAiCJ,MAhCA,CAUOA,EAVFD,EAWMH,EAAK,SAAW,GAEvB,EAAAnB,EAAA,IAAA,CAACzB,EAAD,CACE,MAAM,EAAAyB,EAAA,IAAA,CAACR,EAAD,CAAU,UAAU,QAAU,CAAA,EACpC,MAAM,qBACN,YAAY,wFACZ,OAAQ,CAAE,MAAO,uBAAwB,GAAIS,EAAU,MAAM,EAAAD,EAAA,IAAA,CAACT,EAAD,CAAM,UAAU,QAAU,CAAA,CAAE,CAC1F,CAAA,GAID,EAAAS,EAAA,IAAA,CAACwB,EAAD,CACE,YAAaL,EACH,WACH,QACA,QACP,MAAOP,IAAO,SACf,CAAA,EA3BCC,EAAW,SAAW,SAAgB,EAAAb,EAAA,IAAA,CAAC1B,EAAD,CAAY,MAAOuC,EAAW,KAAQ,CAAA,EACvEA,EAAW,SAAW,SAE3B,EAAAb,EAAA,IAAA,CAAC7B,EAAD,CACE,MAAM,6BACN,MAAO0C,EAAW,MAClB,YAAe,KAAKE,EAAK,CAC1B,CAAA,GAEO,EAAAf,EAAA,IAAA,CAAC5B,EAAD,CAAc,MAAM,sBAAwB,CAAA,GAuBxD,EAAA4B,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,yEAAf,SAAA,EACE,EAAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAU,oDAAlB,SAAA,EACE,EAAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,UAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,wBAAwB,SAAA,aAAe,CAAA,GACrD,EAAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAU,qBAAqB,SAAA,qGAG/B,CAAA,CACA,CACL,CAAA,GAAA,EAAAA,EAAA,KAAA,CAACrC,EAAD,CAAM,GAAIsC,EAAU,UAAWhC,EAAc,SAAS,EAAtD,SAAA,EACE,EAAA+B,EAAA,IAAA,CAACT,EAAD,CAAM,cAAA,GAAY,UAAU,QAAU,CAAA,EAAC,gBAEnC,CACA,CAAA,CAAA,C,IACR,EAAAS,EAAA,IAAA,CAACnB,EAAD,CAAiB,CAAA,EAChB0C,CACE,C,EAET,CAEA,IAAME,EACJ,4HAEF,SAASD,EAAe,CACtB,cACA,WACA,QACA,QACA,SAOC,CACD,GAAM,CAAE,KAAME,EAAU,UAAW3C,EAAiB,EAC9C,CAAE,KAAM4C,EAAS,OAAQ7C,EAAU,EACnC8C,GAAA,EAAYpE,EAAAA,YAAAA,CAAa,GAAemD,EAAMR,EAAG,EAAE,MAAO,CAACQ,CAAK,CAAC,EAEvE,OACE,EAAAX,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,qEAAf,SAAA,CACG6B,IACC,EAAA7B,EAAA,KAAA,CAAC,MAAD,CACE,cAAA,GACA,UAAW9B,EACTuD,EACA,8EACF,EALF,SAAA,EAOE,EAAAzB,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,YAAgB,CAAA,GACtB,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,SAAa,CAAA,GACnB,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,MAAU,CAAA,GAChB,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,iBAAqB,CAAA,GAC3B,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,cAAkB,CAAA,GACxB,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,UAAc,CAAA,GACpB,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAM,SAAa,CAAA,GACnB,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,UAAU,SAAA,SAAa,CAAA,CACpC,CAEP,CAAA,GAAA,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,cAAY,mBAAmB,UAAU,yBAC1C,SAAAQ,EAAY,IAAK,GAAM,CACtB,IAAMsB,EAAUrB,EAASW,EAAE,WACrBW,EAASX,EAAE,QAAUU,GAAS,eAAiB,KAC/CE,EAAOD,EAASrB,EAAMqB,GAAU,IAAA,GAChCE,EAAkB,CACtB,WAAYb,EACZ,YAAaU,GAAS,MAAQ,kBAC9B,OACA,QAAS3C,EAAgBiC,EAAE,QAAS,CAAE,WAAU,CAAC,EACjD,SAAU,CAAC,CAACM,EAASN,EAAE,IACvB,QAAS,CAAC,CAACO,EAAQP,EAAE,IACrB,SAAW,GAAS,KAAKc,EAAOd,EAAGe,CAAI,EACvC,UAAa,KAAKC,EAAIhB,CAAC,CACzB,EACA,OACE,EAAApB,EAAA,IAAA,CAAC,KAAD,CAAe,cAAY,iBAAiB,qBAAoBoB,EAAE,GAC/D,SAAAS,GAAQ,EAAA7B,EAAA,IAAA,CAACqC,EAAD,CAAU,GAAIJ,CAAQ,CAAA,GAAI,EAAAjC,EAAA,IAAA,CAACsC,EAAD,CAAS,GAAIL,CAAQ,CAAA,CACtD,EAFKb,EAAE,EAEP,CAER,CAAC,CACC,CAAA,CACD,C,EAET,CAaA,SAASmB,EAAY,CAAE,aAAY,WAAwD,CACzF,IAAMC,EAAOpC,EAAcqC,EAAW,QAAQ,MAC9C,OACE,EAAAzC,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,sDAAhB,SAAA,EACE,EAAAA,EAAA,IAAA,CAACwC,EAAD,CAAM,cAAA,GAAY,UAAU,0BAA4B,CAAA,GACxD,EAAAxC,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,sBAAuB,SAAA0C,CAAc,CAAA,CACjD,C,EAEV,CAEA,SAASC,EAAe,CAAE,cAA0C,CAClE,GAAI,CAACF,EAAW,QAAS,OAAO,EAAAzC,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,qBAAqB,SAAA,mBAAuB,CAAA,EAC5F,GAAI,CAACyC,EAAW,OAAQ,CACtB,IAAMG,EAAIH,EAAW,QAAQ,KAS7B,OAAO,EAAAzC,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,qBAAsB,SAP3C4C,IAAM,SACF,YACAA,IAAM,UACJ,aACAA,IAAM,aACJ,wBACA,gBAC8C,CAAA,CAC1D,CACA,OACE,EAAA5C,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,gCAAhB,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO3B,EAAeoE,EAAW,MAAM,CAAQ,CAAA,GAC/C,EAAAzC,EAAA,IAAA,CAAC,OAAD,CAAM,SAAUyC,EAAW,OAAQ,UAAU,qBAC1C,SAAArD,EAAeqD,EAAW,MAAM,CAC7B,CAAA,CACF,C,EAEV,CAEA,SAASI,EAAY,CAAE,QAAoC,CAEzD,MADI,CAACb,GAAQA,EAAK,eAAiB,YAAoB,MAErD,EAAAhC,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,8CAAhB,SAAA,EACE,EAAAA,EAAA,IAAA,CAACL,EAAD,CAAS,cAAA,GAAY,UAAU,mBAAqB,CAAA,GACpD,EAAAK,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,sBAAhB,SAAA,CAAsC,QAC9BgC,EAAK,KAAK,IAAEA,EAAK,eAAiB,eAAiB,eAAiB,aACtE,CACF,CAAA,CAAA,C,EAEV,CAEA,SAASc,EAAU,CACjB,UACA,QACA,QAKC,CACD,OACE,EAAA9C,EAAA,IAAA,CAAC+C,EAAD,CACE,KAAK,KACL,KAAMpB,EACN,MAAM,EAAA3B,EAAA,IAAA,CAACV,EAAD,CAAM,UAAU,UAAY,CAAA,EAClC,QAAS0D,EACT,aAAY,OAAOC,EAAK,MACzB,SAAA,SAEO,CAAA,CAEZ,CAEA,SAASZ,EAAS,EAAa,CAC7B,IAAMjB,EAAI8B,EAAE,WACZ,OACE,EAAAlD,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW9B,EAAGuD,EAAY,WAAW,EAA1C,SAAA,EACE,EAAAzB,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,gCAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAACrC,EAAD,CACE,GAAIuC,EAASkB,EAAE,EAAE,EACjB,UAAU,yEAET,SAAAA,EAAE,IACC,CAAA,GACN,EAAApB,EAAA,IAAA,CAACuC,EAAD,CAAa,WAAYnB,EAAG,QAAS8B,EAAE,OAAU,CAAA,CAC9C,C,IACL,EAAAlD,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,gCAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,UAAU,SAAA,WAAe,CAAA,GACzC,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,mBAAoB,SAAAkD,EAAE,WAAkB,CAAA,GACxD,EAAAlD,EAAA,IAAA,CAAC6C,EAAD,CAAa,KAAMK,EAAE,IAAO,CAAA,CACzB,C,IACL,EAAAlD,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,UAAhB,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,UAAU,SAAA,QAAY,CAAA,EACrCd,EAAUkC,EAAE,IAAI,CACb,C,IACN,EAAApB,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,UAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,UAAU,SAAA,mBAAuB,CAAA,GACjD,EAAAA,EAAA,IAAA,CAAC2C,EAAD,CAAgB,WAAYvB,CAAI,CAAA,CAC7B,C,IACL,EAAApB,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,8BAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,UAAU,SAAA,gBAAoB,CAAA,EAC7CoB,EAAE,cAAe,EAAApB,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,aAAa,SAAA,aAAiB,CAAA,CAC7D,C,IACL,EAAAA,EAAA,IAAA,CAACX,EAAD,CAAc,MAAO+B,EAAE,YAAe,CAAA,GACtC,EAAApB,EAAA,IAAA,CAACpB,EAAD,CACE,QAASwC,EAAE,QACX,SAAU8B,EAAE,SACZ,KAAMA,EAAE,SACR,MAAO,UAAU9B,EAAE,OACnB,OAAO,UACP,QAAQ,UACT,CAAA,GACD,EAAApB,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,0BAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC8C,EAAD,CAAW,QAASI,EAAE,QAAS,MAAOA,EAAE,MAAO,KAAM9B,EAAE,IAAO,CAAA,GAC9D,EAAApB,EAAA,IAAA,CAACrC,EAAD,CACE,GAAIuC,EAASkB,EAAE,EAAE,EACjB,aAAY,QAAQA,EAAE,OACtB,MAAM,OACN,UAAU,8GAEV,UAAA,EAAApB,EAAA,IAAA,CAACvB,EAAD,CAAc,cAAA,GAAY,UAAU,QAAU,CAAA,CAC1C,CAAA,CACH,C,EACF,C,EAET,CAEA,SAAS6D,EAAQ,EAAa,CAC5B,IAAMlB,EAAI8B,EAAE,WACZ,OACE,EAAAlD,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,wCAAf,SAAA,EACE,EAAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,iDAAf,SAAA,EACE,EAAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,gCAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAACrC,EAAD,CACE,GAAIuC,EAASkB,EAAE,EAAE,EACjB,UAAU,4EAET,SAAAA,EAAE,IACC,CAAA,GACN,EAAApB,EAAA,IAAA,CAACuC,EAAD,CAAa,WAAYnB,EAAG,QAAS8B,EAAE,OAAU,CAAA,CAC9C,CACL,CAAA,GAAA,EAAAlD,EAAA,IAAA,CAACpB,EAAD,CACE,QAASwC,EAAE,QACX,SAAU8B,EAAE,SACZ,KAAMA,EAAE,SACR,MAAO,UAAU9B,EAAE,OACnB,OAAO,UACP,QAAQ,UACT,CAAA,CACE,C,IACL,EAAApB,EAAA,KAAA,CAAC,KAAD,CAAI,UAAU,sEAAd,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,aAAa,SAAA,SAAW,CAAA,GACtC,EAAAA,EAAA,KAAA,CAAC,KAAD,CAAI,UAAU,oCAAd,SAAA,CACGkD,EAAE,aACH,EAAAlD,EAAA,IAAA,CAAC6C,EAAD,CAAa,KAAMK,EAAE,IAAO,CAAA,CAC1B,C,IACJ,EAAAlD,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,aAAa,SAAA,MAAQ,CAAA,GACnC,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAA,SAAKd,EAAUkC,EAAE,IAAI,CAAM,CAAA,GAC3B,EAAApB,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,aAAa,SAAA,MAAQ,CAAA,GACnC,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,UACZ,UAAA,EAAAA,EAAA,IAAA,CAAC2C,EAAD,CAAgB,WAAYvB,CAAI,CAAA,CAC9B,CAAA,GACJ,EAAApB,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,aAAa,SAAA,cAAgB,CAAA,GAC3C,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,sBACX,SAAAoB,EAAE,cAAe,EAAApB,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,aAAa,SAAA,aAAiB,CAAA,CAC9D,CAAA,GACJ,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,aAAa,SAAA,UAAY,CAAA,GACvC,EAAAA,EAAA,IAAA,CAAC,KAAD,CAAA,UACE,EAAAA,EAAA,IAAA,CAACX,EAAD,CAAc,MAAO+B,EAAE,YAAe,CAAA,CACpC,CAAA,CACF,C,IACJ,EAAApB,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,uBAAf,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC8C,EAAD,CAAW,QAASI,EAAE,QAAS,MAAOA,EAAE,MAAO,KAAM9B,EAAE,IAAO,CAAA,GAC9D,EAAApB,EAAA,KAAA,CAACrC,EAAD,CAAM,GAAIuC,EAASkB,EAAE,EAAE,EAAG,UAAWnD,EAAc,QAAS,IAAI,EAAhE,SAAA,CAAmE,QAEjE,EAAA+B,EAAA,IAAA,CAACvB,EAAD,CAAc,cAAA,GAAY,UAAU,UAAY,CAAA,CAC5C,CACH,CAAA,CAAA,C,EACF,C,EAET,C"}
|
|
1
|
+
{"version":3,"file":"automations-DdB97dnL.js","names":["useState","useCallback","useMemo","__toESM","Link","require_react","createLucideIcon","require_jsx_runtime","useSyncStore","apiGet","buttonClasses","cn","ErrorState","LoadingState","formatRelative","FatalError","EmptyState","CalendarClock","ChevronRight","Hand","Link2","Switch","ConnectionNote","useRunNow","useToggleEnabled","useLocalAutomations","useMergedAutomations","modeLabel","describeTrigger","formatAbsolute","FailureCount","Play","Plus","Workflow","Clock","useBreakpoint","WifiOff","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","Button","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=\"mx-auto flex w-full max-w-6xl 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,4 +1,4 @@
|
|
|
1
|
-
import{_ as e,d as t,f as n,i as r,m as i,n as a,t as o,u as s}from"./jsx-runtime-D13aM9HY.js";import{A as c,E as l,F as u,G as d,P as f,S as p,W as m,a as h,b as g,c as _,d as v,f as y,h as b,i as x,l as S,m as C,n as w,o as T,p as E,t as D,u as O,x as k,y as A}from"./states-DrTQuYXw.js";import{i as j,n as M,r as N,t as P}from"./Menu-
|
|
1
|
+
import{_ as e,d as t,f as n,i as r,m as i,n as a,t as o,u as s}from"./jsx-runtime-D13aM9HY.js";import{A as c,E as l,F as u,G as d,P as f,S as p,W as m,a as h,b as g,c as _,d as v,f as y,h as b,i as x,l as S,m as C,n as w,o as T,p as E,t as D,u as O,x as k,y as A}from"./states-DrTQuYXw.js";import{i as j,n as M,r as N,t as P}from"./Menu-GymfuHGP.js";import{t as F}from"./arrow-left-C6YWM6y6.js";import{t as I}from"./server-SxFxIScj.js";import{t as ee}from"./chevron-right-evVLS6hq.js";import{t as te}from"./external-link-3jjTxo92.js";import{A as ne,C as L,D as re,E as ie,M as ae,N as R,O as oe,P as se,S as ce,T as le,_ as ue,a as z,b as de,c as fe,d as pe,f as B,g as me,h as he,i as ge,j as _e,k as ve,l as ye,m as be,n as xe,o as Se,p as Ce,r as we,s as Te,t as Ee,u as De,v as Oe,w as ke,x as Ae,y as je}from"./useNow-Bc_NlDY3.js";import{n as Me,t as Ne}from"./pencil-DI8QbCN8.js";import{t as Pe}from"./link-2-CaMlUDRG.js";import{t as Fe}from"./play-CjoDg3ws.js";import{t as Ie}from"./plus-BLP-wIYD.js";import{t as Le}from"./trash-C6X2OLrI.js";import{D as Re,E as ze,I as Be,L as Ve,N as He,U as Ue,b as We,c as V,d as Ge,f as Ke,i as qe,k as Je,l as Ye,n as Xe,o as Ze,p as Qe,s as $e,t as H,u as et,x as tt}from"./index-Cf6ZUCfK.js";import{n as nt,t as rt}from"./TextArea-BsQHOQMe.js";import{i as it,r as at,t as ot}from"./Markdown-jXkvZeRp.js";import{c as st}from"./dist-Bgkh2hc5.js";import{t as ct}from"./SegmentedControl-ChdVilXb.js";import{a as lt,n as ut,r as dt,t as ft}from"./Overlay-oYnnwvFM.js";var pt={name:`arrow-right-left`,size:24,node:[[`path`,{d:`m16 3 4 4-4 4`,key:`1x1c3m`}],[`path`,{d:`M20 7H4`,key:`zbl0bi`}],[`path`,{d:`m8 21-4-4 4-4`,key:`h9nckh`}],[`path`,{d:`M4 17h16`,key:`g4d7ey`}]]};pt.node;var mt=a(pt),ht={name:`arrow-up`,size:24,node:[[`path`,{d:`m5 12 7-7 7 7`,key:`hav0vg`}],[`path`,{d:`M12 19V5`,key:`x0mq9r`}]]};ht.node;var gt=a(ht),_t={name:`chevrons-up`,size:24,node:[[`path`,{d:`m17 11-5-5-5 5`,key:`e8nh98`}],[`path`,{d:`m17 18-5-5-5 5`,key:`2avn1x`}]]};_t.node;var vt=a(_t),yt={name:`columns-3`,size:24,node:[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}]],aliases:[`panels-left-right`]};yt.node;var bt=a(yt),xt={name:`grip-vertical`,size:24,node:[[`circle`,{cx:`9`,cy:`12`,r:`1`,key:`1vctgf`}],[`circle`,{cx:`9`,cy:`5`,r:`1`,key:`hp0tcf`}],[`circle`,{cx:`9`,cy:`19`,r:`1`,key:`fkjjf6`}],[`circle`,{cx:`15`,cy:`12`,r:`1`,key:`1tmaij`}],[`circle`,{cx:`15`,cy:`5`,r:`1`,key:`19l28e`}],[`circle`,{cx:`15`,cy:`19`,r:`1`,key:`f4zoj3`}]]};xt.node;var St=a(xt),Ct={name:`list`,size:24,node:[[`path`,{d:`M3 5h.01`,key:`18ugdj`}],[`path`,{d:`M3 12h.01`,key:`nlz23k`}],[`path`,{d:`M3 19h.01`,key:`noohij`}],[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M8 12h13`,key:`1za7za`}],[`path`,{d:`M8 19h13`,key:`m83p4d`}]]};Ct.node;var wt=a(Ct),Tt={name:`message-square-warning`,size:24,node:[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}],[`path`,{d:`M12 15h.01`,key:`q59x07`}],[`path`,{d:`M12 7v4`,key:`xawao1`}]]};Tt.node;var Et=a(Tt),Dt={name:`minus`,size:24,node:[[`path`,{d:`M5 12h14`,key:`1ays0h`}]]};Dt.node;var Ot=a(Dt),kt={name:`rotate-ccw`,size:24,node:[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]};kt.node;var At=a(kt),jt=Ue(),U=e(i()),Mt=typeof window<`u`&&window.document!==void 0&&window.document.createElement!==void 0;function Nt(e){let t=Object.prototype.toString.call(e);return t===`[object Window]`||t===`[object global]`}function Pt(e){return`nodeType`in e}function W(e){return e?Nt(e)?e:Pt(e)?e.ownerDocument?.defaultView??window:window:window}function Ft(e){let{Document:t}=W(e);return e instanceof t}function It(e){return!Nt(e)&&e instanceof W(e).HTMLElement}function Lt(e){return e instanceof W(e).SVGElement}function Rt(e){return e?Nt(e)?e.document:Pt(e)?Ft(e)?e:It(e)||Lt(e)?e.ownerDocument:document:document:document}var G=Mt?U.useLayoutEffect:U.useEffect;function zt(e){let t=(0,U.useRef)(e);return G(()=>{t.current=e}),(0,U.useCallback)(function(){var e=[...arguments];return t.current==null?void 0:t.current(...e)},[])}function Bt(){let e=(0,U.useRef)(null);return[(0,U.useCallback)((t,n)=>{e.current=setInterval(t,n)},[]),(0,U.useCallback)(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[])]}function Vt(e,t){t===void 0&&(t=[e]);let n=(0,U.useRef)(e);return G(()=>{n.current!==e&&(n.current=e)},t),n}function Ht(e,t){let n=(0,U.useRef)();return(0,U.useMemo)(()=>{let t=e(n.current);return n.current=t,t},[...t])}function Ut(e){let t=zt(e),n=(0,U.useRef)(null);return[n,(0,U.useCallback)(e=>{e!==n.current&&t?.(e,n.current),n.current=e},[])]}function Wt(e){let t=(0,U.useRef)();return(0,U.useEffect)(()=>{t.current=e},[e]),t.current}var Gt={};function Kt(e,t){return(0,U.useMemo)(()=>{if(t)return t;let n=Gt[e]==null?0:Gt[e]+1;return Gt[e]=n,e+`-`+n},[e,t])}function qt(e){return function(t){return[...arguments].slice(1).reduce((t,n)=>{let r=Object.entries(n);for(let[n,i]of r){let r=t[n];r!=null&&(t[n]=r+e*i)}return t},{...t})}}var Jt=qt(1),Yt=qt(-1);function Xt(e){return`clientX`in e&&`clientY`in e}function Zt(e){if(!e)return!1;let{KeyboardEvent:t}=W(e.target);return t&&e instanceof t}function Qt(e){if(!e)return!1;let{TouchEvent:t}=W(e.target);return t&&e instanceof t}function $t(e){if(Qt(e)){if(e.touches&&e.touches.length){let{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}if(e.changedTouches&&e.changedTouches.length){let{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return Xt(e)?{x:e.clientX,y:e.clientY}:null}var en=Object.freeze({Translate:{toString(e){if(!e)return;let{x:t,y:n}=e;return`translate3d(`+(t?Math.round(t):0)+`px, `+(n?Math.round(n):0)+`px, 0)`}},Scale:{toString(e){if(!e)return;let{scaleX:t,scaleY:n}=e;return`scaleX(`+t+`) scaleY(`+n+`)`}},Transform:{toString(e){if(e)return[en.Translate.toString(e),en.Scale.toString(e)].join(` `)}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+` `+n+`ms `+r}}}),tn=`a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]`;function nn(e){return e.matches(tn)?e:e.querySelector(tn)}var rn={display:`none`};function an(e){let{id:t,value:n}=e;return U.createElement(`div`,{id:t,style:rn},n)}function on(e){let{id:t,announcement:n,ariaLiveType:r=`assertive`}=e;return U.createElement(`div`,{id:t,style:{position:`fixed`,top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:`hidden`,clip:`rect(0 0 0 0)`,clipPath:`inset(100%)`,whiteSpace:`nowrap`},role:`status`,"aria-live":r,"aria-atomic":!0},n)}function sn(){let[e,t]=(0,U.useState)(``);return{announce:(0,U.useCallback)(e=>{e!=null&&t(e)},[]),announcement:e}}var cn=(0,U.createContext)(null);function ln(e){let t=(0,U.useContext)(cn);(0,U.useEffect)(()=>{if(!t)throw Error(`useDndMonitor must be used within a children of <DndContext>`);return t(e)},[e,t])}function un(){let[e]=(0,U.useState)(()=>new Set),t=(0,U.useCallback)(t=>(e.add(t),()=>e.delete(t)),[e]);return[(0,U.useCallback)(t=>{let{type:n,event:r}=t;e.forEach(e=>e[n]?.call(e,r))},[e]),t]}var dn={draggable:`
|
|
2
2
|
To pick up a draggable item, press the space bar.
|
|
3
3
|
While dragging, use the arrow keys to move the item.
|
|
4
4
|
Press space again to drop the item in its new position, or press escape to cancel.
|
|
@@ -7,4 +7,4 @@ import{_ as e,d as t,f as n,i as r,m as i,n as a,t as o,u as s}from"./jsx-runtim
|
|
|
7
7
|
`)),[l,u]=(0,U.useState)(e.priority),[d,p]=(0,U.useState)(e.dependencies),[h,g]=(0,U.useState)(!1),[_,v]=(0,U.useState)(null),y=(0,U.useId)(),b=Object.values(n).filter(t=>t.projectId===e.projectId&&t.id!==e.id&&t.state!==`canceled`&&!t.hiddenFromBoard).sort((e,t)=>e.title.localeCompare(t.title));return(0,Z.jsxs)(`form`,{className:`flex flex-col gap-3`,onSubmit:async n=>{if(n.preventDefault(),!r.trim())v(`Title is required.`);else{g(!0),v(null);try{await se(e.id,{title:r.trim(),objective:a,acceptanceCriteria:s.split(`
|
|
8
8
|
`).map(e=>e.trim()).filter(Boolean),priority:l,dependencies:d}),V.success(`Task saved`),t()}catch(e){v(m(e))}finally{g(!1)}}},children:[(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${y}-title`,className:`text-sm font-medium`,children:`Title`}),(0,Z.jsx)(nt,{id:`${y}-title`,value:r,onChange:e=>i(e.target.value),required:!0})]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${y}-obj`,className:`text-sm font-medium`,children:`Objective`}),(0,Z.jsx)(rt,{id:`${y}-obj`,minRows:4,maxHeight:`40vh`,value:a,onChange:e=>o(e.target.value)})]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${y}-crit`,className:`text-sm font-medium`,children:`Acceptance criteria (one per line)`}),(0,Z.jsx)(rt,{id:`${y}-crit`,minRows:2,maxHeight:`30vh`,value:s,onChange:e=>c(e.target.value)})]}),(0,Z.jsx)(Qi,{id:`${y}-prio`,value:l,onChange:u}),(0,Z.jsx)($i,{candidates:b,value:d,onChange:p}),_&&(0,Z.jsx)(`p`,{role:`alert`,className:`text-sm text-danger`,children:_}),(0,Z.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Z.jsx)(S,{type:`submit`,variant:`primary`,busy:h,children:`Save`}),(0,Z.jsx)(S,{onClick:t,disabled:h,children:`Cancel`})]})]})}function Qi({id:e,value:t,onChange:n}){return(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:e,className:`text-sm font-medium`,children:`Priority`}),(0,Z.jsx)(`select`,{id:e,value:t,onChange:e=>n(Number(e.target.value)),className:`tap h-9 rounded-md border border-border bg-surface px-2 text-base md:text-sm`,children:st.map((e,t)=>(0,Z.jsx)(`option`,{value:t,children:e},e))})]})}function $i({candidates:e,value:t,onChange:n}){let[r,i]=(0,U.useState)(``),a=(0,U.useId)(),o=e.filter(e=>t.includes(e.id)||e.title.toLowerCase().includes(r.toLowerCase())).slice(0,50);return(0,Z.jsxs)(`fieldset`,{className:`flex min-w-0 flex-col gap-1`,children:[(0,Z.jsx)(`legend`,{className:`text-sm font-medium`,children:`Dependencies`}),e.length===0?(0,Z.jsx)(`p`,{className:`text-sm text-muted`,children:`No other tasks in this project.`}):(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(`label`,{htmlFor:a,className:`sr-only`,children:`Filter tasks`}),(0,Z.jsx)(nt,{id:a,placeholder:`Filter tasks`,value:r,onChange:e=>i(e.target.value)}),(0,Z.jsx)(`ul`,{className:`max-h-48 overflow-y-auto rounded-md border border-border`,children:o.map(e=>(0,Z.jsx)(`li`,{children:(0,Z.jsxs)(`label`,{className:`tap flex min-w-0 items-center gap-2 px-3 py-1.5 text-sm hover:bg-surface-2`,children:[(0,Z.jsx)(`input`,{type:`checkbox`,checked:t.includes(e.id),onChange:r=>n(r.target.checked?[...t,e.id]:t.filter(t=>t!==e.id))}),(0,Z.jsx)(`span`,{className:`min-w-0 flex-1 truncate`,children:e.title}),(0,Z.jsx)(`span`,{className:`shrink-0 text-xs text-muted`,children:B[e.state]})]})},e.id))}),t.length>0&&(0,Z.jsxs)(`p`,{className:`text-xs text-muted`,children:[t.length,` selected. The task waits until they are Done.`]})]})]})}var ea=`sy:new-task-draft`;function ta(e){return{title:``,objective:``,projectId:e,advanced:!1,priority:2,dependencies:[],hostId:``,profileId:``,branch:``,criteria:``,continuation:!1,contObjective:``,maxContinuations:`3`,maxMinutes:`60`,maxTokens:``,maxCost:``,checks:``,checksMode:`default`,humanReview:!0}}function na(e){try{let t=sessionStorage.getItem(ea);if(t)return{...ta(e),...JSON.parse(t)}}catch{}return ta(e)}var ra=e=>e.split(`
|
|
9
9
|
`).map(e=>e.trim()).filter(Boolean);function ia(e){let t={},n={};if(e.hostId&&(n.hostId=e.hostId),e.profileId&&(n.profileId=e.profileId),e.branch.trim()&&(n.workspace={mode:`new_worktree`,branch:e.branch.trim(),base:null}),e.continuation){let r=Number(e.maxContinuations),i=Number(e.maxMinutes),a=e.maxTokens.trim()?Number(e.maxTokens):null,o=e.maxCost.trim()?Number(e.maxCost):null;e.contObjective.trim()||(t.contObjective=`Continuation needs an objective.`),(!Number.isInteger(r)||r<1||r>50)&&(t.maxContinuations=`Use 1 to 50.`),(!Number.isInteger(i)||i<1||i>1440)&&(t.maxMinutes=`Use 1 to 1440 minutes.`),a!==null&&(!Number.isInteger(a)||a<=0)&&(t.maxTokens=`Use a positive whole number.`),o!==null&&(!Number.isFinite(o)||o<=0)&&(t.maxCost=`Use a positive amount.`),n.continuation={enabled:!0,objective:e.contObjective.trim(),maxContinuations:r,maxElapsedMinutes:i,maxTokens:a,maxCostUsd:o}}return e.checksMode!==`custom`&&e.humanReview||(n.completion={checks:e.checksMode===`custom`?ra(e.checks):null,requireHumanReview:e.humanReview}),{policy:n,errors:t}}function aa({defaultProjectId:e,onCreated:t,onCancel:n}){let r=c(),i=l(),a=f(e=>e.profiles),o=f(e=>e.tasks),s=f(e=>e.runs),u=f(e=>e.hosts),d=f(e=>e.dispatchPaused),[p,h]=(0,U.useState)(()=>na(e??r[0]?.id??``)),[_,v]=(0,U.useState)(null),[y,b]=(0,U.useState)({}),[x,C]=(0,U.useState)(null),w=(0,U.useId)(),T=(e,t)=>h(n=>({...n,[e]:t}));(0,U.useEffect)(()=>{!p.projectId&&r[0]&&h(e=>({...e,projectId:r[0].id}))},[p.projectId,r]),(0,U.useEffect)(()=>{sessionStorage.setItem(ea,JSON.stringify(p))},[p]);let E=r.find(e=>e.id===p.projectId),D=E?.defaultHostId?u[E.defaultHostId]:void 0,O=E?.defaultProfileId?a[E.defaultProfileId]:void 0,k=Object.values(o).filter(e=>e.projectId===p.projectId&&e.state!==`canceled`&&!e.hiddenFromBoard).sort((e,t)=>e.title.localeCompare(t.title)),A=(0,U.useMemo)(()=>{let e={};for(let t of Object.values(s)){if(!ue(t))continue;let n=o[t.taskId]?.projectId;n&&(e[n]=(e[n]??0)+1)}return L({project:E,hostId:p.hostId||null,dependencies:p.dependencies},{hosts:u,tasks:o,dispatchPaused:d,runningByProject:e})},[E,p.hostId,p.dependencies,u,o,s,d]),j=async e=>{let n={};p.title.trim()||(n.title=`Give the task a title.`),E||(n.projectId=`Choose a project.`);let{policy:r,errors:i}=ia(p);if(Object.assign(n,i),b(n),Object.keys(n).length>0)Object.keys(i).length>0&&T(`advanced`,!0);else{v(e),C(null);try{let n=await le({projectId:p.projectId,title:p.title.trim(),objective:p.objective,acceptanceCriteria:ra(p.criteria),dependencies:p.dependencies,priority:p.priority,state:e,...Object.keys(r).length>0?{policy:r}:{}});if(sessionStorage.removeItem(ea),e===`ready`){let e=me(A);try{e=me(await re(n.id))}catch{}V.success(`Created "${n.title}" in Ready. ${e}.`)}else V.success(`Created "${n.title}" in Backlog.`);t(n)}catch(e){C(m(e))}finally{v(null)}}},M=e=>y[e]?(0,Z.jsx)(`p`,{id:`${w}-${e}-err`,className:`text-xs text-danger`,children:y[e]}):null,P=`tap h-9 w-full rounded-md border border-border bg-surface px-2 text-base md:text-sm`;return(0,Z.jsxs)(`form`,{"data-testid":`new-task-form`,className:`flex min-w-0 flex-col gap-4`,onSubmit:e=>{e.preventDefault(),j(`backlog`)},children:[(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-title`,className:`text-sm font-medium`,children:`Title`}),(0,Z.jsx)(nt,{id:`${w}-title`,value:p.title,autoFocus:!0,"aria-invalid":!!y.title,"aria-describedby":y.title?`${w}-title-err`:void 0,onChange:e=>T(`title`,e.target.value),placeholder:`What should be done?`}),M(`title`)]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-objective`,className:`text-sm font-medium`,children:`Objective`}),(0,Z.jsx)(rt,{id:`${w}-objective`,minRows:3,maxHeight:`35vh`,value:p.objective,onChange:e=>T(`objective`,e.target.value),placeholder:`Context, constraints and what done looks like`})]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-project`,className:`text-sm font-medium`,children:`Project`}),(0,Z.jsxs)(`select`,{id:`${w}-project`,className:P,value:p.projectId,"aria-invalid":!!y.projectId,onChange:e=>h(t=>({...t,projectId:e.target.value,dependencies:[]})),children:[r.length===0&&(0,Z.jsx)(`option`,{value:``,children:`No projects yet`}),r.map(e=>(0,Z.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),M(`projectId`)]}),(0,Z.jsxs)(`div`,{children:[(0,Z.jsxs)(`button`,{type:`button`,className:`tap inline-flex items-center gap-1 rounded text-sm font-medium text-accent`,"aria-expanded":p.advanced,"aria-controls":`${w}-adv`,onClick:()=>T(`advanced`,!p.advanced),children:[p.advanced?(0,Z.jsx)(N,{"aria-hidden":!0,className:`size-4`}):(0,Z.jsx)(ee,{"aria-hidden":!0,className:`size-4`}),`Advanced options`]}),(0,Z.jsx)(`p`,{className:`text-xs text-muted`,children:`Unset options inherit the project defaults.`})]}),p.advanced&&(0,Z.jsxs)(`div`,{id:`${w}-adv`,className:`flex flex-col gap-4 rounded-lg border border-border p-3`,children:[(0,Z.jsx)(Qi,{id:`${w}-prio`,value:p.priority,onChange:e=>T(`priority`,e)}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-criteria`,className:`text-sm font-medium`,children:`Acceptance criteria (one per line)`}),(0,Z.jsx)(rt,{id:`${w}-criteria`,minRows:2,maxHeight:`25vh`,value:p.criteria,onChange:e=>T(`criteria`,e.target.value)})]}),(0,Z.jsx)($i,{candidates:k,value:p.dependencies,onChange:e=>T(`dependencies`,e)}),(0,Z.jsxs)(`div`,{className:`grid gap-4 sm:grid-cols-2`,children:[(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-host`,className:`text-sm font-medium`,children:`Host`}),(0,Z.jsxs)(`select`,{id:`${w}-host`,className:P,value:p.hostId,onChange:e=>T(`hostId`,e.target.value),children:[(0,Z.jsxs)(`option`,{value:``,children:[`Project default`,D?` (${D.name})`:``]}),i.map(e=>(0,Z.jsxs)(`option`,{value:e.id,children:[e.name,e.connectivity===`connected`?``:` (offline)`]},e.id))]})]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-profile`,className:`text-sm font-medium`,children:`Pi profile`}),(0,Z.jsxs)(`select`,{id:`${w}-profile`,className:P,value:p.profileId,onChange:e=>T(`profileId`,e.target.value),children:[(0,Z.jsxs)(`option`,{value:``,children:[`Project default`,O?` (${O.name})`:``]}),Object.values(a).map(e=>(0,Z.jsx)(`option`,{value:e.id,children:e.name},e.id))]})]})]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-branch`,className:`text-sm font-medium`,children:`Branch`}),(0,Z.jsx)(nt,{id:`${w}-branch`,value:p.branch,placeholder:`Generated from the title`,onChange:e=>T(`branch`,e.target.value)}),(0,Z.jsx)(`p`,{className:`text-xs text-muted`,children:`Work happens in a new worktree on this branch.`})]}),(0,Z.jsxs)(`fieldset`,{className:`flex flex-col gap-2`,children:[(0,Z.jsx)(`legend`,{className:`text-sm font-medium`,children:`Continuation limits`}),(0,Z.jsxs)(`label`,{className:`tap flex items-center gap-2 text-sm`,children:[(0,Z.jsx)(`input`,{type:`checkbox`,checked:p.continuation,onChange:e=>T(`continuation`,e.target.checked)}),`Continue automatically until done (off by default)`]}),p.continuation&&(0,Z.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1 sm:col-span-2`,children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-cobj`,className:`text-sm`,children:`Continuation objective`}),(0,Z.jsx)(nt,{id:`${w}-cobj`,value:p.contObjective,"aria-invalid":!!y.contObjective,onChange:e=>T(`contObjective`,e.target.value)}),M(`contObjective`)]}),(0,Z.jsx)(oa,{id:`${w}-cmax`,label:`Max continuations`,value:p.maxContinuations,onChange:e=>T(`maxContinuations`,e),error:M(`maxContinuations`)}),(0,Z.jsx)(oa,{id:`${w}-cmin`,label:`Max minutes`,value:p.maxMinutes,onChange:e=>T(`maxMinutes`,e),error:M(`maxMinutes`)}),(0,Z.jsx)(oa,{id:`${w}-ctok`,label:`Max tokens (optional)`,value:p.maxTokens,onChange:e=>T(`maxTokens`,e),error:M(`maxTokens`)}),(0,Z.jsx)(oa,{id:`${w}-ccost`,label:`Max cost USD (optional)`,value:p.maxCost,onChange:e=>T(`maxCost`,e),error:M(`maxCost`)}),(0,Z.jsx)(`p`,{className:`text-xs text-muted sm:col-span-2`,children:`Token and cost limits apply only when the Pi provider reports usage.`})]})]}),(0,Z.jsxs)(`fieldset`,{className:`flex flex-col gap-2`,children:[(0,Z.jsx)(`legend`,{className:`text-sm font-medium`,children:`Checks and review`}),(0,Z.jsxs)(`label`,{className:`tap flex items-center gap-2 text-sm`,children:[(0,Z.jsx)(`input`,{type:`checkbox`,checked:p.checksMode===`custom`,onChange:e=>T(`checksMode`,e.target.checked?`custom`:`default`)}),`Use custom checks instead of the project's`,E?.checks.length?` (${E.checks.join(`, `)})`:` (none)`]}),p.checksMode===`custom`&&(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(`label`,{htmlFor:`${w}-checks`,className:`text-sm`,children:`Check commands (one per line; empty means no checks)`}),(0,Z.jsx)(rt,{id:`${w}-checks`,minRows:2,className:`font-mono`,value:p.checks,onChange:e=>T(`checks`,e.target.value)})]}),(0,Z.jsxs)(`label`,{className:`tap flex items-center gap-2 text-sm`,children:[(0,Z.jsx)(`input`,{type:`checkbox`,checked:p.humanReview,onChange:e=>T(`humanReview`,e.target.checked)}),`Require human review before Done`]})]})]}),x&&(0,Z.jsxs)(`p`,{role:`alert`,className:`rounded-md bg-danger-soft px-3 py-2 text-sm`,children:[`Could not create the task: `,x]}),(0,Z.jsxs)(`div`,{className:`flex flex-col gap-2 border-t border-border pt-3`,children:[(0,Z.jsx)(Ki,{e:A,prefix:`Make ready: `}),(0,Z.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,Z.jsx)(S,{type:`submit`,icon:(0,Z.jsx)(g,{className:`size-4`}),busy:_===`backlog`,disabled:_!==null,children:`Save to Backlog`}),(0,Z.jsx)(S,{variant:`primary`,icon:(0,Z.jsx)(Fe,{className:`size-4`}),busy:_===`ready`,disabled:_!==null,onClick:()=>void j(`ready`),children:`Make ready`}),(0,Z.jsx)(S,{variant:`ghost`,onClick:n,disabled:_!==null,children:`Cancel`})]}),(0,Z.jsx)(`p`,{className:`text-xs text-muted`,children:`Make ready authorizes dispatch. Nothing starts until the coordinator confirms.`})]})]})}function oa({id:e,label:t,value:n,onChange:r,error:i}){return(0,Z.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,Z.jsx)(`label`,{htmlFor:e,className:`text-sm`,children:t}),(0,Z.jsx)(nt,{id:e,inputMode:`decimal`,value:n,onChange:e=>r(e.target.value)}),i]})}var sa={neutral:`text-muted`,info:`text-info`,success:`text-success`,warning:`text-warning`,danger:`text-danger`,accent:`text-accent`};function ca({task:e,showProject:t,wait:i,onOpen:a,drag:o,full:s,selected:c}){let l=zi(e),u=f(e=>e.tasks),d=Fi(),p=n(),m=d.isBusy(e.id),h=Ri[e.priority],g=h.icon,_=Bi(e,l.run,i),v=he(e,u),y=l.attention[0],x=[{id:`open`,label:`Open details`,icon:(0,Z.jsx)(M,{className:`size-4`}),onSelect:()=>a(e)}];if(e.sessionId){let t=e.sessionId;x.push({id:`session`,label:`Open session`,icon:(0,Z.jsx)(te,{className:`size-4`}),onSelect:()=>p(H.session(t))})}x.push(`separator`);for(let t of Oe(e))x.push({id:`move-${t}`,label:da(e.state,t),icon:(0,Z.jsx)(ua,{state:t}),onSelect:()=>void d.move(e,t)});return(0,Z.jsxs)(`article`,{ref:o?.setNodeRef,"data-testid":`task-card`,"data-task-id":e.id,"aria-label":e.title,"aria-busy":m||void 0,style:o?.style,...o?.cardProps,className:b(`group relative flex min-w-0 flex-col gap-1.5 rounded-lg border bg-surface p-3 text-sm shadow-sm`,c?`border-accent`:`border-border`,o?.dragging&&`opacity-60`,s?`w-full`:``),children:[(0,Z.jsxs)(`div`,{className:`flex min-w-0 items-start gap-1`,children:[o&&(0,Z.jsx)(`button`,{type:`button`,ref:o.setHandleRef,...o.handleProps,"aria-label":`Drag ${e.title}`,title:`Drag to move (Space to pick up, arrows to move, Space to drop)`,className:`tap -ml-1.5 inline-flex h-7 w-5 shrink-0 cursor-grab items-center justify-center rounded text-subtle hover:bg-surface-2 hover:text-text`,children:(0,Z.jsx)(St,{"aria-hidden":!0,className:`size-4`})}),(0,Z.jsx)(`button`,{type:`button`,"data-card-open":e.id,onClick:()=>a(e),className:`tap min-w-0 flex-1 rounded text-left font-medium leading-snug`,children:(0,Z.jsx)(`span`,{className:`line-clamp-2 break-words`,children:e.title})}),m&&(0,Z.jsx)(O,{label:`Updating`,className:`mt-0.5 shrink-0`}),(0,Z.jsx)(P,{label:`Actions for ${e.title}`,items:x,trigger:t=>(0,Z.jsx)(`button`,{type:`button`,...t,"aria-label":`Move to… and other actions for ${e.title}`,title:`Move to…`,className:`tap -mr-1.5 inline-flex size-7 shrink-0 items-center justify-center rounded text-muted hover:bg-surface-2 hover:text-text`,children:(0,Z.jsx)(Re,{"aria-hidden":!0,className:`size-4`})})})]}),(0,Z.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted`,children:[(0,Z.jsxs)(`span`,{className:b(`inline-flex items-center gap-1`,sa[h.tone]),children:[(0,Z.jsx)(g,{"aria-hidden":!0,className:`size-3.5`}),h.label]}),t&&l.projectName&&(0,Z.jsx)(`span`,{className:`min-w-0 truncate`,children:l.projectName})]}),_&&(0,Z.jsx)(la,{icon:(0,Z.jsx)(_.icon,{"aria-hidden":!0,className:b(`size-3.5`,_.spin&&`animate-spin`)}),className:sa[_.tone],children:_.text}),(l.hostName||l.branch)&&(0,Z.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted`,children:[l.hostName&&(0,Z.jsxs)(`span`,{className:`inline-flex min-w-0 items-center gap-1`,children:[(0,Z.jsx)(I,{"aria-hidden":!0,className:`size-3.5 shrink-0`}),(0,Z.jsxs)(`span`,{className:`truncate`,children:[l.hostName,l.hostOffline?` (offline)`:``]})]}),l.branch&&(0,Z.jsxs)(`span`,{className:`inline-flex min-w-0 items-center gap-1`,children:[(0,Z.jsx)(Me,{"aria-hidden":!0,className:`size-3.5 shrink-0`}),(0,Z.jsx)(`span`,{className:`truncate`,children:l.branch})]})]}),v&&(0,Z.jsx)(la,{icon:(0,Z.jsx)(Pe,{"aria-hidden":!0,className:`size-3.5`}),className:`text-muted`,children:v}),l.pendingInteraction&&e.sessionId?(0,Z.jsxs)(r,{to:H.interaction(e.sessionId,l.pendingInteraction.id),className:`tap inline-flex min-w-0 items-center gap-1 rounded text-xs font-medium text-warning hover:underline`,children:[(0,Z.jsx)(A,{"aria-hidden":!0,className:`size-3.5 shrink-0`}),(0,Z.jsxs)(`span`,{className:`truncate`,children:[`Needs input: `,l.pendingInteraction.title]})]}):y?(0,Z.jsxs)(la,{icon:(0,Z.jsx)(Ve,{"aria-hidden":!0,className:`size-3.5`}),className:sa[Ye[y.reason].tone],children:[Ye[y.reason].label,l.attention.length>1?` (+${l.attention.length-1})`:``]}):null,(0,Z.jsxs)(`div`,{className:`text-xs text-subtle`,children:[`Updated `,(0,Z.jsx)(`time`,{dateTime:e.lastActivityAt,children:E(e.lastActivityAt)})]})]})}function la({icon:e,children:t,className:n}){return(0,Z.jsxs)(`div`,{className:b(`flex min-w-0 items-start gap-1 text-xs`,n),children:[(0,Z.jsx)(`span`,{className:`mt-0.5 shrink-0`,children:e}),(0,Z.jsx)(`span`,{className:`min-w-0 break-words line-clamp-2`,children:t})]})}function ua({state:e,className:t}){let n=Ke[e].icon;return(0,Z.jsx)(n,{"aria-hidden":!0,className:b(`size-4`,t)})}function da(e,t){return e===`ready`&&t===`in_progress`?`Run now (In progress)`:e===`review`&&t===`in_progress`?`Start new attempt (In progress)`:e===`backlog`&&t===`ready`?`Make ready`:(e===`done`||e===`canceled`)&&t!==`canceled`?`Reopen to ${B[t]}`:t===`canceled`?`Cancel task`:t===`done`?`Accept (Done)`:`Move to ${B[t]}`}function fa(){let e=$e(e=>e.filters.board),t=$e(e=>e.setFilter),n=$e(e=>e.selectedProjectId),r=$e(e=>e.set),i=(0,U.useMemo)(()=>je(e),[e]),a=f(e=>e.tasks),o=f(e=>e.runs),s=f(e=>e.attention),c=f(e=>e.projects),l=Ee(),u=n&&c[n]?n:null,d=(0,U.useMemo)(()=>{let e=new Set(de(s).keys());return be(a,i,{projectId:u,runs:o,attentionTaskIds:e})},[a,i,u,o,s]),p=(0,U.useMemo)(()=>{let e=new Map;for(let t of l.data?.queued??[])e.set(t.taskId,t.eligibility);return e},[l.data]),m=(0,U.useMemo)(()=>l.data?l.data.queued.filter(e=>!u||a[e.taskId]?.projectId===u).length:Object.values(a).filter(e=>e.state===`ready`&&!e.hiddenFromBoard&&(!u||e.projectId===u)).length,[l.data,a,u]),h=ke(i),g=(0,U.useMemo)(()=>Object.values(a).filter(e=>!e.hiddenFromBoard&&(!u||e.projectId===u)).length,[a,u]),_=h.reduce((e,t)=>e+d[t].length,0);return{filters:i,setFilters:e=>t(`board`,e),projectId:u,setProjectId:e=>r(`selectedProjectId`,e),columns:d,states:h,waits:p,queueCount:m,now:l,showProject:!u&&Object.keys(c).length>1,totalTasks:g,visibleCount:_}}var $={mainScrollTop:0,boardScrollLeft:0,columnScroll:{},focusTaskId:null},pa=`col:`,ma=`card:`,ha=`task:`,ga=(e,{currentCoordinates:t,context:n})=>{if(e.code===`ArrowDown`)return{...t,y:t.y+72};if(e.code===`ArrowUp`)return{...t,y:t.y-72};if(e.code!==`ArrowRight`&&e.code!==`ArrowLeft`)return;e.preventDefault();let r=n.droppableContainers.toArray().filter(e=>String(e.id).startsWith(pa)).map(e=>({id:e.id,rect:n.droppableRects.get(e.id)})).filter(e=>!!e.rect).sort((e,t)=>e.rect.left-t.rect.left);if(r.length===0)return;let i=n.collisionRect?.width??0,a=t.x+i/2,o=r.findIndex(e=>a>=e.rect.left&&a<=e.rect.left+e.rect.width);o<0&&(o=0);let s=r[Math.max(0,Math.min(r.length-1,o+(e.code===`ArrowRight`?1:-1)))];return{x:s.rect.left+(s.rect.width-i)/2,y:t.y}},_a=e=>{let t=On(e);if(t.length>0){let e=t.find(e=>String(e.id).startsWith(ma));return e?[e]:t}return wn(e)};function va(e,t){if(e.startsWith(pa))return{state:e.slice(4),beforeId:null};if(e.startsWith(ma)){let n=t[e.slice(5)];return n?{state:n.state,beforeId:n.id}:null}return null}function ya({taskId:e}){let t=fa(),r=f(e=>e.hydrated),i=f(e=>e.projects),a=f(e=>e.tasks),o=Fi(),c=n(),l=s(),u=$e(e=>e.paneSizes.boardColumn),[d,p]=(0,U.useState)(null),[m,h]=(0,U.useState)(!1),g=gn(hn(gr,{activationConstraint:{distance:6}}),hn(dr,{coordinateGetter:ga,keyboardCodes:{start:[`Space`],cancel:[`Escape`],end:[`Space`,`Enter`]}})),_=(0,U.useCallback)(e=>{e&&(e.scrollLeft=$.boardScrollLeft)},[]);(0,U.useEffect)(()=>{if(e||!$.focusTaskId)return;let t=$.focusTaskId;$.focusTaskId=null,requestAnimationFrame(()=>document.querySelector(`[data-card-open="${CSS.escape(t)}"]`)?.focus({preventScroll:!1}))},[e]);let v=e=>{$.focusTaskId=e.id,c(H.task(e.id),{state:{fromBoard:!0}})},y=()=>{e&&($.focusTaskId=e),l.state?.fromBoard?c(-1):c(H.board)},b=e=>p(String(e.active.id).slice(5)),x=e=>{p(null);let n=String(e.active.id).slice(5),r=a[n];if(!r||!e.over)return;let i=va(String(e.over.id),a);if(!i||i.beforeId===n)return;let s=t.columns[i.state],c=ce(s,i.beforeId,n);i.state===r.state?t.filters.sort===`priority`&&o.reorder(r,c):o.move(r,i.state,{position:c})},C={onDragStart:({active:e})=>`Picked up ${xa(e.id,a)}.`,onDragOver:({active:e,over:t})=>t?`${xa(e.id,a)} is over ${Sa(String(t.id),a)}.`:void 0,onDragEnd:({active:e,over:t})=>t?`Dropped ${xa(e.id,a)} on ${Sa(String(t.id),a)}.`:`Dropped ${xa(e.id,a)}. It did not move.`,onDragCancel:({active:e})=>`Moving ${xa(e.id,a)} was canceled.`},w=d?a[d]:void 0,E=e?a[e]:void 0,O=r&&Object.keys(i).length===0;return(0,Z.jsxs)(`div`,{"data-testid":`board`,className:`absolute inset-0 flex min-w-0 flex-col`,children:[(0,Z.jsxs)(`header`,{className:`flex min-w-0 flex-wrap items-center gap-2 border-b border-border px-4 py-2.5`,children:[(0,Z.jsx)(`h1`,{className:`mr-2 text-lg font-semibold`,children:`Board`}),(0,Z.jsx)(fe,{board:t}),(0,Z.jsx)(ye,{board:t,className:`w-56 max-w-full`}),(0,Z.jsx)(Se,{board:t}),(0,Z.jsx)(`div`,{className:`ml-auto`,children:(0,Z.jsx)(S,{variant:`primary`,icon:(0,Z.jsx)(Ie,{className:`size-4`}),onClick:()=>h(!0),disabled:O,title:O?`Add a project first`:void 0,children:`New task`})})]}),(0,Z.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-5 gap-y-2 border-b border-border px-4 py-2`,children:[(0,Z.jsx)(z,{}),(0,Z.jsxs)(`span`,{className:`text-sm text-muted`,role:`status`,children:[t.queueCount,` queued`]}),(0,Z.jsx)(De,{board:t}),(0,Z.jsx)(ge,{board:t}),(0,Z.jsx)(Te,{board:t})]}),(0,Z.jsx)(we,{board:t}),r?O?(0,Z.jsx)(D,{icon:(0,Z.jsx)(tt,{className:`size-8`}),title:`No projects yet`,description:`Tasks belong to a project. Add a project from a host checkout to start planning work.`,action:{label:`Add a project`,to:H.settings(`projects`)}}):t.totalTasks===0?(0,Z.jsx)(D,{icon:(0,Z.jsx)(tt,{className:`size-8`}),title:`No tasks yet`,description:`Create a task in Backlog, or make it ready so the dispatcher starts it.`,action:{label:`New task`,onClick:()=>h(!0)}}):(0,Z.jsxs)(pi,{sensors:g,collisionDetection:_a,onDragStart:b,onDragEnd:x,onDragCancel:()=>p(null),accessibility:{announcements:C,screenReaderInstructions:{draggable:`To move a task, press Space to pick it up, use the arrow keys to move between columns, then press Space to drop or Escape to cancel. The task menu offers Move to… without dragging.`}},children:[t.visibleCount===0&&(0,Z.jsxs)(`p`,{role:`status`,className:`px-4 pt-3 text-sm text-muted`,children:[`No tasks match the filters.`,` `,(0,Z.jsx)(`button`,{type:`button`,className:`tap font-medium text-accent`,onClick:()=>t.setFilters({...pe,sort:t.filters.sort}),children:`Clear filters`})]}),(0,Z.jsx)(`section`,{ref:_,"aria-label":`Board columns`,onScroll:e=>{$.boardScrollLeft=e.currentTarget.scrollLeft},className:`min-h-0 flex-1 overflow-x-auto overflow-y-hidden overscroll-x-contain`,children:(0,Z.jsx)(`div`,{className:`flex h-full w-max gap-3 p-4`,children:t.states.map(n=>(0,Z.jsx)(Ca,{state:n,board:t,width:u,onOpen:v,selectedId:e,onNew:()=>h(!0)},n))})}),(0,Z.jsx)(Ni,{dropAnimation:null,children:w&&(0,Z.jsx)(`div`,{className:`rotate-1 shadow-pop`,style:{width:`clamp(272px, ${u-16}px, 344px)`},children:(0,Z.jsx)(ca,{task:w,showProject:t.showProject,wait:t.waits.get(w.id),onOpen:()=>void 0})})})]}):(0,Z.jsx)(T,{label:`Loading tasks…`}),(0,Z.jsx)(ut,{open:!!e,onClose:y,width:520,title:E?.title??(r?`Task not found`:`Loading task…`),description:E?B[E.state]:void 0,footer:E?(0,Z.jsx)(ba,{task:E}):void 0,children:(0,Z.jsx)(`div`,{"data-testid":`task-drawer`,className:`min-w-0`,children:E?(0,Z.jsx)(qi,{task:E,onDeleted:()=>c(H.board,{replace:!0})}):r?(0,Z.jsx)(D,{title:`Task not found`,description:`It may have been deleted, or this link is from another coordinator.`,action:{label:`Back to board`,to:H.board}}):(0,Z.jsx)(T,{label:`Loading task…`})})}),(0,Z.jsx)(ft,{open:m,onClose:()=>h(!1),title:`New task`,className:`max-w-xl`,children:(0,Z.jsx)(aa,{defaultProjectId:t.projectId,onCancel:()=>h(!1),onCreated:e=>{h(!1),$.focusTaskId=e.id,requestAnimationFrame(()=>document.querySelector(`[data-card-open="${CSS.escape(e.id)}"]`)?.focus())}})})]})}function ba({task:e}){let{eligibility:t}=Ui(e);return(0,Z.jsx)(`div`,{className:`w-full min-w-0`,children:(0,Z.jsx)(Gi,{task:e,eligibility:t})})}function xa(e,t){let n=t[String(e).replace(ha,``)];return n?`task ${n.title}`:`task`}function Sa(e,t){if(e.startsWith(pa))return`the ${B[e.slice(4)]} column`;let n=t[e.slice(5)];return n?`${n.title} in ${B[n.state]}`:`a task`}function Ca({state:e,board:t,width:n,onOpen:r,selectedId:i,onNew:a}){let{setNodeRef:o,isOver:s}=xi({id:`${pa}${e}`}),[c,l]=(0,U.useState)(!1),u=t.columns[e],d=Ke[e].icon,f=e===`done`&&!t.filters.showCompleted&&!c,p=`col-${e}-title`,m=(0,U.useCallback)(t=>{t&&(t.scrollTop=$.columnScroll[e]??0)},[e]);return(0,Z.jsx)(`section`,{ref:o,"data-testid":`column-${e}`,"aria-labelledby":p,style:{width:`clamp(288px, ${n}px, 360px)`},className:`flex h-full shrink-0 flex-col rounded-lg border bg-surface-2 ${s?`border-accent`:`border-border`}`,children:(0,Z.jsxs)(`div`,{ref:m,"data-column-scroll":e,onScroll:t=>{$.columnScroll[e]=t.currentTarget.scrollTop},className:`min-h-0 flex-1 overflow-y-auto overscroll-y-contain`,children:[(0,Z.jsxs)(`h2`,{id:p,className:`sticky top-0 z-10 flex items-center gap-2 rounded-t-lg border-b border-border bg-surface-2 px-3 py-2 text-sm font-semibold`,children:[(0,Z.jsx)(d,{"aria-hidden":!0,className:`size-4 text-muted`}),(0,Z.jsx)(`span`,{className:`flex-1`,children:B[e]}),(0,Z.jsxs)(`span`,{className:`rounded-full bg-surface px-2 text-xs font-medium text-muted tabular-nums`,children:[u.length,(0,Z.jsx)(`span`,{className:`sr-only`,children:` tasks`})]})]}),(0,Z.jsx)(`ul`,{className:`flex flex-col gap-2 p-2`,children:f?(0,Z.jsxs)(`li`,{className:`flex flex-col items-start gap-1 px-1 py-2 text-sm text-muted`,children:[u.length,` completed `,u.length===1?`task is`:`tasks are`,` hidden.`,u.length>0&&(0,Z.jsx)(`button`,{type:`button`,className:`tap font-medium text-accent`,onClick:()=>l(!0),children:`Show completed`})]}):u.length===0?(0,Z.jsx)(`li`,{className:`px-1 py-2 text-sm text-muted`,children:e===`backlog`?(0,Z.jsxs)(`button`,{type:`button`,className:`tap inline-flex items-center gap-1 font-medium text-accent`,onClick:a,children:[(0,Z.jsx)(Ie,{"aria-hidden":!0,className:`size-4`}),` New task`]}):`No tasks`}):u.map(e=>(0,Z.jsx)(`li`,{children:(0,Z.jsx)(wa,{task:e,board:t,onOpen:r,selected:e.id===i})},e.id))})]})})}function wa({task:e,board:t,onOpen:n,selected:r}){let i=_i({id:`${ha}${e.id}`}),a=xi({id:`${ma}${e.id}`,disabled:i.isDragging}),{onPointerDown:o,onKeyDown:s}=i.listeners??{},c={setNodeRef:e=>{i.setNodeRef(e),a.setNodeRef(e)},setHandleRef:i.setActivatorNodeRef,cardProps:{onPointerDown:o},handleProps:{...i.attributes,onKeyDown:s},dragging:i.isDragging,style:a.isOver?{boxShadow:`0 -3px 0 0 var(--sy-accent)`}:void 0};return(0,Z.jsx)(ca,{task:e,showProject:t.showProject,wait:t.waits.get(e.id),onOpen:n,drag:c,selected:r})}var Ta=()=>document.getElementById(`main`);function Ea(){let e=fa(),t=f(e=>e.hydrated),r=f(e=>e.projects),i=n(),[a,o]=Ze(`board-mobile`,{tab:`ready`,layout:`column`}),[s,c]=(0,U.useState)(!1),l=e.states.includes(a.tab)?a.tab:`ready`;(0,U.useLayoutEffect)(()=>{if(!t)return;let e=Ta(),n=$.mainScrollTop;e&&(e.scrollTop=n);let r=requestAnimationFrame(()=>{r=requestAnimationFrame(()=>{e&&e.scrollTop<n&&(e.scrollTop=n)})}),i=$.focusTaskId;return $.focusTaskId=null,i&&requestAnimationFrame(()=>document.querySelector(`[data-card-open="${CSS.escape(i)}"]`)?.focus({preventScroll:!0})),()=>cancelAnimationFrame(r)},[t]);let u=e=>{$.mainScrollTop=Ta()?.scrollTop??0,$.focusTaskId=e.id,i(H.task(e.id),{state:{fromBoard:!0}})},d=t&&Object.keys(r).length===0,p=(t,n)=>t.length===0?(0,Z.jsxs)(`p`,{className:`px-1 py-3 text-sm text-muted`,children:[`No `,B[n],` tasks.`]}):(0,Z.jsx)(`ul`,{className:`flex flex-col gap-2`,children:t.map(t=>(0,Z.jsx)(`li`,{children:(0,Z.jsx)(ca,{task:t,full:!0,showProject:e.showProject,wait:e.waits.get(t.id),onOpen:u})},t.id))});return(0,Z.jsxs)(`div`,{"data-testid":`board`,className:`flex min-w-0 flex-col`,children:[(0,Z.jsxs)(`header`,{className:`flex min-w-0 flex-col gap-2 border-b border-border bg-bg px-3 pt-3 pb-0 safe-pt`,children:[(0,Z.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Z.jsx)(`h1`,{className:`flex-1 text-lg font-semibold`,children:`Board`}),(0,Z.jsx)(S,{variant:`primary`,icon:(0,Z.jsx)(Ie,{className:`size-4`}),onClick:()=>c(!0),disabled:d,children:`New task`})]}),(0,Z.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,Z.jsx)(fe,{board:e,className:`min-w-0 flex-1`}),(0,Z.jsx)(Se,{board:e,mobile:!0})]}),(0,Z.jsx)(ye,{board:e}),(0,Z.jsx)(Te,{board:e}),(0,Z.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center justify-between gap-2`,children:[(0,Z.jsx)(z,{compact:!0}),(0,Z.jsxs)(`span`,{className:`text-sm text-muted`,children:[e.queueCount,` queued`]})]}),(0,Z.jsx)(ct,{label:`Board layout`,block:!0,value:a.layout,onChange:e=>o({...a,layout:e}),options:[{value:`column`,label:`Columns`,icon:(0,Z.jsx)(bt,{"aria-hidden":!0,className:`size-4`})},{value:`list`,label:`List`,icon:(0,Z.jsx)(wt,{"aria-hidden":!0,className:`size-4`})}]}),a.layout===`column`&&(0,Z.jsx)(`div`,{"data-testid":`status-tabs`,className:`-mx-3 px-1`,children:(0,Z.jsx)(it,{label:`Task status`,idPrefix:`board-tab`,scrollable:!0,className:`border-b-0`,value:l,onChange:e=>o({...a,tab:e}),tabs:e.states.map(t=>{let n=Ke[t].icon;return{id:t,label:(0,Z.jsxs)(Z.Fragment,{children:[(0,Z.jsx)(n,{"aria-hidden":!0,className:`size-4`}),B[t]]}),badge:(0,Z.jsx)(Qe,{label:`${e.columns[t].length} tasks`,children:e.columns[t].length})}})})})]}),(0,Z.jsx)(we,{board:e}),(0,Z.jsx)(`div`,{className:`flex min-w-0 flex-col gap-3 px-3 py-3`,children:t?d?(0,Z.jsx)(D,{icon:(0,Z.jsx)(tt,{className:`size-8`}),title:`No projects yet`,description:`Tasks belong to a project. Add a project from a host checkout first.`,action:{label:`Add a project`,to:H.settings(`projects`)}}):e.totalTasks===0?(0,Z.jsx)(D,{icon:(0,Z.jsx)(tt,{className:`size-8`}),title:`No tasks yet`,description:`Create a task in Backlog, or make it ready so the dispatcher starts it.`,action:{label:`New task`,onClick:()=>c(!0)}}):e.visibleCount===0?(0,Z.jsx)(D,{title:`No tasks match the filters`,action:{label:`Clear filters`,onClick:()=>e.setFilters({...pe,sort:e.filters.sort})}}):a.layout===`column`?(0,Z.jsx)(at,{idPrefix:`board-tab`,id:l,children:l===`done`&&!e.filters.showCompleted?(0,Z.jsxs)(`p`,{className:`text-sm text-muted`,children:[`Completed tasks are hidden.`,` `,(0,Z.jsx)(`button`,{type:`button`,className:`tap font-medium text-accent`,onClick:()=>e.setFilters({...e.filters,showCompleted:!0}),children:`Show completed`})]}):p(e.columns[l],l)}):e.states.filter(t=>t!==`done`||e.filters.showCompleted).map(t=>(0,Z.jsxs)(`section`,{"aria-labelledby":`grp-${t}`,"data-testid":`column-${t}`,className:`flex flex-col gap-2`,children:[(0,Z.jsxs)(`h2`,{id:`grp-${t}`,className:`flex items-center gap-2 pt-1 text-sm font-semibold`,children:[(0,Z.jsx)(ua,{state:t,className:`text-muted`}),B[t],(0,Z.jsxs)(`span`,{className:`text-muted`,children:[`(`,e.columns[t].length,`)`]})]}),p(e.columns[t],t)]},t)):(0,Z.jsx)(T,{label:`Loading tasks…`})}),(0,Z.jsx)(dt,{open:s,onClose:()=>c(!1),title:`New task`,children:(0,Z.jsx)(aa,{defaultProjectId:e.projectId,onCancel:()=>c(!1),onCreated:e=>{c(!1),o({...a,tab:e.state})}})})]})}function Da({taskId:e}){qe();let t=u(e),r=f(e=>e.hydrated),i=n(),a=s(),[o,c]=(0,U.useState)(!1),{eligibility:l}=Ui(t);return(0,U.useLayoutEffect)(()=>{let e=Ta();e&&(e.scrollTop=0)},[]),(0,Z.jsxs)(`div`,{className:`flex min-w-0 flex-col`,"data-testid":`task-page`,children:[(0,Z.jsxs)(`header`,{className:`sticky top-0 z-20 flex min-w-0 items-center gap-1 border-b border-border bg-surface px-1 py-1 safe-pt`,children:[(0,Z.jsx)(lt,{label:`Back to board`,icon:(0,Z.jsx)(F,{className:`size-5`}),onClick:()=>{a.state?.fromBoard?i(-1):i(H.board)}}),(0,Z.jsx)(`h1`,{className:`min-w-0 flex-1 truncate text-base font-semibold`,children:t?.title??`Task`}),t&&(0,Z.jsx)(S,{size:`sm`,icon:(0,Z.jsx)(mt,{className:`size-4`}),onClick:()=>c(!0),children:`Move task`})]}),t?(0,Z.jsxs)(`div`,{className:`flex min-w-0 flex-col px-4 pt-3 pb-8`,children:[(0,Z.jsx)(`div`,{children:(0,Z.jsx)(Gi,{task:t,eligibility:l,block:!0})}),(0,Z.jsx)(qi,{task:t,onDeleted:()=>i(H.board,{replace:!0})}),(0,Z.jsx)(Oa,{task:t,open:o,onClose:()=>c(!1)})]}):r?(0,Z.jsx)(D,{title:`Task not found`,description:`It may have been deleted.`,action:{label:`Back to board`,to:H.board}}):(0,Z.jsx)(T,{label:`Loading task…`})]})}function Oa({task:e,open:t,onClose:n}){let r=Fi(),i=f(e=>e.runs),a=e.currentRunId?i[e.currentRunId]:void 0,o=[`backlog`,`ready`,`in_progress`,`review`,`done`,`canceled`],s=new Set(Oe(e));return(0,Z.jsx)(dt,{open:t,onClose:n,title:`Move task`,description:`Currently ${B[e.state]}`,children:(0,Z.jsx)(`ul`,{"data-testid":`move-sheet`,className:`flex flex-col`,children:o.filter(t=>t!==e.state).map(t=>{let i=Ae(e,t,a),o=i.kind===`invalid`?i.reason:i.kind===`choose_running`?`Asks whether to pause automation or stop execution.`:i.kind===`confirm_done`?`Asks you to confirm acceptance.`:i.kind===`run_now`?`Starts now if the task is eligible.`:null;return(0,Z.jsx)(`li`,{children:(0,Z.jsxs)(`button`,{type:`button`,disabled:!s.has(t),onClick:()=>{n(),r.move(e,t)},className:`tap flex w-full items-start gap-3 rounded-md px-2 py-3 text-left hover:bg-surface-2 disabled:opacity-60`,children:[(0,Z.jsx)(ua,{state:t,className:`mt-0.5 text-muted`}),(0,Z.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,Z.jsx)(`span`,{className:`block font-medium`,children:da(e.state,t)}),o&&(0,Z.jsx)(`span`,{className:`block text-sm text-muted`,children:o})]})]})},t)})})})}function ka(){let e=Xe(),n=t(`/tasks/:taskId`)?.params.taskId;return(0,Z.jsx)(Li,{children:e===`mobile`?n?(0,Z.jsx)(Da,{taskId:n},n):(0,Z.jsx)(Ea,{}):(0,Z.jsx)(ya,{taskId:n})})}export{ka as default};
|
|
10
|
-
//# sourceMappingURL=board-
|
|
10
|
+
//# sourceMappingURL=board-s4LuDPcg.js.map
|