ocwatch 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.
Files changed (41) hide show
  1. package/README.md +22 -3
  2. package/package.json +4 -4
  3. package/src/client/dist/assets/GraphView-BZV40eAE.css +1 -0
  4. package/src/client/dist/assets/GraphView-KWCCGYb2.js +9 -0
  5. package/src/client/dist/assets/graph-Cw_XSlvx.js +7 -0
  6. package/src/client/dist/assets/index-CbgYG3pJ.js +23 -0
  7. package/src/client/dist/assets/index-CgDCc8Mm.css +1 -0
  8. package/src/client/dist/assets/motion-CGUGF2CN.js +9 -0
  9. package/src/client/dist/index.html +4 -2
  10. package/src/server/__tests__/helpers/testDb.ts +220 -0
  11. package/src/server/index.ts +27 -27
  12. package/src/server/logic/activityLogic.ts +260 -0
  13. package/src/server/logic/index.ts +2 -0
  14. package/src/server/logic/sessionLogic.ts +107 -0
  15. package/src/server/routes/parts.ts +9 -7
  16. package/src/server/routes/poll.ts +32 -46
  17. package/src/server/routes/projects.ts +10 -27
  18. package/src/server/routes/sessions.ts +159 -68
  19. package/src/server/routes/sse.ts +10 -4
  20. package/src/server/services/parsing.ts +211 -0
  21. package/src/server/services/pollService.ts +400 -116
  22. package/src/server/services/recentSessions.ts +14 -0
  23. package/src/server/services/sessionContext.ts +97 -0
  24. package/src/server/services/sessionService.ts +97 -193
  25. package/src/server/services/sessionTree.ts +92 -0
  26. package/src/server/storage/db.ts +63 -0
  27. package/src/server/storage/index.ts +28 -0
  28. package/src/server/storage/queries.ts +528 -0
  29. package/src/server/utils/projectResolver.ts +9 -3
  30. package/src/server/utils/sessionStatus.ts +5 -89
  31. package/src/server/validation.ts +2 -4
  32. package/src/server/watcher.ts +225 -82
  33. package/src/shared/constants.ts +8 -3
  34. package/src/shared/index.ts +3 -0
  35. package/src/shared/types/index.ts +48 -53
  36. package/src/shared/utils/activityUtils.ts +3 -2
  37. package/src/client/dist/assets/index-BIu7r5_5.css +0 -1
  38. package/src/client/dist/assets/index-BYMVif3u.js +0 -50
  39. package/src/server/storage/messageParser.ts +0 -169
  40. package/src/server/storage/partParser.ts +0 -532
  41. package/src/server/storage/sessionParser.ts +0 -180
@@ -0,0 +1 @@
1
+ @keyframes pulse-bg{0%{background-color:transparent}50%{background-color:#58a6ff1a}to{background-color:transparent}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.animate-pulse-bg{animation:pulse-bg .3s ease-out forwards}.animate-fade-in{animation:fade-in .2s ease-out forwards}@keyframes badge-glow{0%,to{box-shadow:inset 0 1px #ffffff26,0 1px 2px #0003,0 0 4px var(--badge-color, rgba(88, 166, 255, .3))}50%{box-shadow:inset 0 1px #ffffff26,0 1px 2px #0003,0 0 16px var(--badge-color, rgba(88, 166, 255, .6))}}.animate-badge-glow{animation:badge-glow 2s ease-in-out infinite}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.animate-shimmer{background:linear-gradient(90deg,#161b22 25%,#30363d,#161b22 75%);background-size:200% 100%;animation:shimmer 1.5s infinite}@keyframes session-update-pulse{0%{background-color:#58a6ff14}to{background-color:transparent}}.animate-session-update{animation:session-update-pulse .4s ease-out forwards}@keyframes attention-glow{0%,to{box-shadow:inset 2px 0 #d2992299}50%{box-shadow:inset 2px 0 #d29922}}.animate-attention{animation:attention-glow 2s ease-in-out infinite}@keyframes waiting-user-row-pulse{0%,to{background-color:#d299220f}50%{background-color:#d299221f}}.animate-waiting-user-row{animation:waiting-user-row-pulse 2s ease-in-out infinite}@keyframes waiting-user-icon-pulse{0%,to{transform:scale(1);opacity:.8}50%{transform:scale(1.15);opacity:1}}.animate-waiting-user-icon{animation:waiting-user-icon-pulse 1.5s ease-in-out infinite}@keyframes node-pulse{0%,to{box-shadow:0 0 #58a6ff00;border-color:#58a6ff4d}50%{box-shadow:0 0 8px #58a6ff33;border-color:#58a6ff99}}.animate-node-pulse{animation:node-pulse 2s ease-in-out infinite}.edge-particle{filter:drop-shadow(0 0 2px #58a6ff)}.edge-particle-return{filter:drop-shadow(0 0 2px #22c55e)}@keyframes graph-edge-flow{0%{stroke-dashoffset:0}to{stroke-dashoffset:-36}}@keyframes graph-edge-return{0%{stroke-dashoffset:0;opacity:1}to{stroke-dashoffset:18;opacity:.8}}.graph-edge-flow{animation:graph-edge-flow 1.2s linear infinite}.graph-edge-return{animation:graph-edge-return 1.1s ease-out infinite}@media(prefers-reduced-motion:reduce){.animate-badge-glow,.animate-node-pulse,.graph-edge-flow,.graph-edge-return{animation:none!important}}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.static{position:static}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.bottom-4{bottom:1rem}.left-0{left:0}.right-0{right:0}.right-4{right:1rem}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.\!h-2{height:.5rem!important}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-\[200px\]{max-height:200px}.max-h-\[30vh\]{max-height:30vh}.max-h-\[320px\]{max-height:320px}.max-h-\[45\%\]{max-height:45%}.min-h-0{min-height:0px}.min-h-\[1\.5em\]{min-height:1.5em}.min-h-\[60px\]{min-height:60px}.\!w-2{width:.5rem!important}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-1\/4{width:25%}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\/3{width:66.666667%}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-8{width:2rem}.w-\[280px\]{width:280px}.w-\[300px\]{width:300px}.w-\[320px\]{width:320px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[300px\]{min-width:300px}.max-w-2xl{max-width:42rem}.max-w-\[80px\]{max-width:80px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.origin-top-right{transform-origin:top right}.-translate-y-1{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-\[0\.98\]{--tw-scale-x: .98;--tw-scale-y: .98;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-r-full{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-accent{--tw-border-opacity: 1;border-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.border-border{--tw-border-opacity: 1;border-color:rgb(48 54 61 / var(--tw-border-opacity, 1))}.border-border\/30{border-color:#30363d4d}.border-border\/50{border-color:#30363d80}.border-error\/20{border-color:#f8514933}.border-error\/50{border-color:#f8514980}.border-warning\/20{border-color:#d2992233}.border-l-accent{--tw-border-opacity: 1;border-left-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.border-l-success{--tw-border-opacity: 1;border-left-color:rgb(35 134 54 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.border-l-warning{--tw-border-opacity: 1;border-left-color:rgb(210 153 34 / var(--tw-border-opacity, 1))}.\!bg-border{--tw-bg-opacity: 1 !important;background-color:rgb(48 54 61 / var(--tw-bg-opacity, 1))!important}.bg-\[\#0d1117\]{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-\[\#0d1117\]\/30{background-color:#0d11174d}.bg-accent{--tw-bg-opacity: 1;background-color:rgb(88 166 255 / var(--tw-bg-opacity, 1))}.bg-accent\/10{background-color:#58a6ff1a}.bg-accent\/20{background-color:#58a6ff33}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-background{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-background\/80{background-color:#0d1117cc}.bg-black\/10{background-color:#0000001a}.bg-black\/20{background-color:#0003}.bg-black\/30{background-color:#0000004d}.bg-border{--tw-bg-opacity: 1;background-color:rgb(48 54 61 / var(--tw-bg-opacity, 1))}.bg-error\/10{background-color:#f851491a}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-purple-400\/50{background-color:#c084fc80}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:rgb(35 134 54 / var(--tw-bg-opacity, 1))}.bg-surface{--tw-bg-opacity: 1;background-color:rgb(22 27 34 / var(--tw-bg-opacity, 1))}.bg-surface\/50{background-color:#161b2280}.bg-surface\/95{background-color:#161b22f2}.bg-warning\/10{background-color:#d299221a}.bg-white\/5{background-color:#ffffff0d}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pl-6{padding-left:1.5rem}.pr-1{padding-right:.25rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-1\.5{padding-top:.375rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#58a6ff\],.text-accent{--tw-text-opacity: 1;color:rgb(88 166 255 / var(--tw-text-opacity, 1))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-error{--tw-text-opacity: 1;color:rgb(248 81 73 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:rgb(35 134 54 / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:rgb(139 148 158 / var(--tw-text-opacity, 1))}.text-warning{--tw-text-opacity: 1;color:rgb(210 153 34 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.line-through{text-decoration-line:line-through}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-35{opacity:.35}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_18px_rgba\(88\,166\,255\,0\.18\)\]{--tw-shadow: 0 0 18px rgba(88,166,255,.18);--tw-shadow-colored: 0 0 18px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-accent{--tw-ring-opacity: 1;--tw-ring-color: rgb(88 166 255 / var(--tw-ring-opacity, 1))}.drop-shadow-\[0_0_6px_\#58a6ff\]{--tw-drop-shadow: drop-shadow(0 0 6px #58a6ff);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[opacity\,border-color\,box-shadow\,transform\]{transition-property:opacity,border-color,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:border-accent:hover{--tw-border-opacity: 1;border-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.hover\:bg-accent\/90:hover{background-color:#58a6ffe6}.hover\:bg-background:hover{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.hover\:bg-surface:hover{--tw-bg-opacity: 1;background-color:rgb(22 27 34 / var(--tw-bg-opacity, 1))}.hover\:bg-white\/5:hover{background-color:#ffffff0d}.hover\:bg-white\/\[0\.02\]:hover{background-color:#ffffff05}.hover\:text-text-primary:hover{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}.focus\:bg-white\/5:focus{background-color:#ffffff0d}.group:hover .group-hover\:bg-purple-400{--tw-bg-opacity: 1;background-color:rgb(192 132 252 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-text-primary{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}@media(min-width:640px){.sm\:inline-block{display:inline-block}}
@@ -0,0 +1,9 @@
1
+ function ct(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}var B={exports:{}},D={};var Z;function lt(){if(Z)return D;Z=1;var n=Symbol.for("react.transitional.element"),p=Symbol.for("react.fragment");function i(E,a,l){var c=null;if(l!==void 0&&(c=""+l),a.key!==void 0&&(c=""+a.key),"key"in a){l={};for(var w in a)w!=="key"&&(l[w]=a[w])}else l=a;return a=l.ref,{$$typeof:n,type:E,key:c,ref:a!==void 0?a:null,props:l}}return D.Fragment=p,D.jsx=i,D.jsxs=i,D}var K;function pt(){return K||(K=1,B.exports=lt()),B.exports}var q=pt(),X={exports:{}},r={};var F;function at(){if(F)return r;F=1;var n=Symbol.for("react.transitional.element"),p=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),E=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),c=Symbol.for("react.context"),w=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),k=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),M=Symbol.for("react.activity"),R=Symbol.iterator;function T(t){return t===null||typeof t!="object"?null:(t=R&&t[R]||t["@@iterator"],typeof t=="function"?t:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},A=Object.assign,S={};function P(t,e,u){this.props=t,this.context=e,this.refs=S,this.updater=u||x}P.prototype.isReactComponent={},P.prototype.setState=function(t,e){if(typeof t!="object"&&typeof t!="function"&&t!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,t,e,"setState")},P.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")};function L(){}L.prototype=P.prototype;function j(t,e,u){this.props=t,this.context=e,this.refs=S,this.updater=u||x}var C=j.prototype=new L;C.constructor=j,A(C,P.prototype),C.isPureReactComponent=!0;var U=Array.isArray;function I(){}var h={H:null,A:null,T:null,S:null},g=Object.prototype.hasOwnProperty;function y(t,e,u){var o=u.ref;return{$$typeof:n,type:t,key:e,ref:o!==void 0?o:null,props:u}}function O(t,e){return y(t.type,e,t.props)}function Y(t){return typeof t=="object"&&t!==null&&t.$$typeof===n}function b(t){var e={"=":"=0",":":"=2"};return"$"+t.replace(/[=:]/g,function(u){return e[u]})}var z=/\/+/g;function W(t,e){return typeof t=="object"&&t!==null&&t.key!=null?b(""+t.key):e.toString(36)}function ut(t){switch(t.status){case"fulfilled":return t.value;case"rejected":throw t.reason;default:switch(typeof t.status=="string"?t.then(I,I):(t.status="pending",t.then(function(e){t.status==="pending"&&(t.status="fulfilled",t.value=e)},function(e){t.status==="pending"&&(t.status="rejected",t.reason=e)})),t.status){case"fulfilled":return t.value;case"rejected":throw t.reason}}throw t}function N(t,e,u,o,f){var d=typeof t;(d==="undefined"||d==="boolean")&&(t=null);var _=!1;if(t===null)_=!0;else switch(d){case"bigint":case"string":case"number":_=!0;break;case"object":switch(t.$$typeof){case n:case p:_=!0;break;case m:return _=t._init,N(_(t._payload),e,u,o,f)}}if(_)return f=f(t),_=o===""?"."+W(t,0):o,U(f)?(u="",_!=null&&(u=_.replace(z,"$&/")+"/"),N(f,e,u,"",function(ft){return ft})):f!=null&&(Y(f)&&(f=O(f,u+(f.key==null||t&&t.key===f.key?"":(""+f.key).replace(z,"$&/")+"/")+_)),e.push(f)),1;_=0;var $=o===""?".":o+":";if(U(t))for(var v=0;v<t.length;v++)o=t[v],d=$+W(o,v),_+=N(o,e,u,d,f);else if(v=T(t),typeof v=="function")for(t=v.call(t),v=0;!(o=t.next()).done;)o=o.value,d=$+W(o,v++),_+=N(o,e,u,d,f);else if(d==="object"){if(typeof t.then=="function")return N(ut(t),e,u,o,f);throw e=String(t),Error("Objects are not valid as a React child (found: "+(e==="[object Object]"?"object with keys {"+Object.keys(t).join(", ")+"}":e)+"). If you meant to render a collection of children, use an array instead.")}return _}function G(t,e,u){if(t==null)return t;var o=[],f=0;return N(t,o,"","",function(d){return e.call(u,d,f++)}),o}function st(t){if(t._status===-1){var e=t._result;e=e(),e.then(function(u){(t._status===0||t._status===-1)&&(t._status=1,t._result=u)},function(u){(t._status===0||t._status===-1)&&(t._status=2,t._result=u)}),t._status===-1&&(t._status=0,t._result=e)}if(t._status===1)return t._result.default;throw t._result}var Q=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)},it={map:G,forEach:function(t,e,u){G(t,function(){e.apply(this,arguments)},u)},count:function(t){var e=0;return G(t,function(){e++}),e},toArray:function(t){return G(t,function(e){return e})||[]},only:function(t){if(!Y(t))throw Error("React.Children.only expected to receive a single React element child.");return t}};return r.Activity=M,r.Children=it,r.Component=P,r.Fragment=i,r.Profiler=a,r.PureComponent=j,r.StrictMode=E,r.Suspense=H,r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=h,r.__COMPILER_RUNTIME={__proto__:null,c:function(t){return h.H.useMemoCache(t)}},r.cache=function(t){return function(){return t.apply(null,arguments)}},r.cacheSignal=function(){return null},r.cloneElement=function(t,e,u){if(t==null)throw Error("The argument must be a React element, but you passed "+t+".");var o=A({},t.props),f=t.key;if(e!=null)for(d in e.key!==void 0&&(f=""+e.key),e)!g.call(e,d)||d==="key"||d==="__self"||d==="__source"||d==="ref"&&e.ref===void 0||(o[d]=e[d]);var d=arguments.length-2;if(d===1)o.children=u;else if(1<d){for(var _=Array(d),$=0;$<d;$++)_[$]=arguments[$+2];o.children=_}return y(t.type,f,o)},r.createContext=function(t){return t={$$typeof:c,_currentValue:t,_currentValue2:t,_threadCount:0,Provider:null,Consumer:null},t.Provider=t,t.Consumer={$$typeof:l,_context:t},t},r.createElement=function(t,e,u){var o,f={},d=null;if(e!=null)for(o in e.key!==void 0&&(d=""+e.key),e)g.call(e,o)&&o!=="key"&&o!=="__self"&&o!=="__source"&&(f[o]=e[o]);var _=arguments.length-2;if(_===1)f.children=u;else if(1<_){for(var $=Array(_),v=0;v<_;v++)$[v]=arguments[v+2];f.children=$}if(t&&t.defaultProps)for(o in _=t.defaultProps,_)f[o]===void 0&&(f[o]=_[o]);return y(t,d,f)},r.createRef=function(){return{current:null}},r.forwardRef=function(t){return{$$typeof:w,render:t}},r.isValidElement=Y,r.lazy=function(t){return{$$typeof:m,_payload:{_status:-1,_result:t},_init:st}},r.memo=function(t,e){return{$$typeof:k,type:t,compare:e===void 0?null:e}},r.startTransition=function(t){var e=h.T,u={};h.T=u;try{var o=t(),f=h.S;f!==null&&f(u,o),typeof o=="object"&&o!==null&&typeof o.then=="function"&&o.then(I,Q)}catch(d){Q(d)}finally{e!==null&&u.types!==null&&(e.types=u.types),h.T=e}},r.unstable_useCacheRefresh=function(){return h.H.useCacheRefresh()},r.use=function(t){return h.H.use(t)},r.useActionState=function(t,e,u){return h.H.useActionState(t,e,u)},r.useCallback=function(t,e){return h.H.useCallback(t,e)},r.useContext=function(t){return h.H.useContext(t)},r.useDebugValue=function(){},r.useDeferredValue=function(t,e){return h.H.useDeferredValue(t,e)},r.useEffect=function(t,e){return h.H.useEffect(t,e)},r.useEffectEvent=function(t){return h.H.useEffectEvent(t)},r.useId=function(){return h.H.useId()},r.useImperativeHandle=function(t,e,u){return h.H.useImperativeHandle(t,e,u)},r.useInsertionEffect=function(t,e){return h.H.useInsertionEffect(t,e)},r.useLayoutEffect=function(t,e){return h.H.useLayoutEffect(t,e)},r.useMemo=function(t,e){return h.H.useMemo(t,e)},r.useOptimistic=function(t,e){return h.H.useOptimistic(t,e)},r.useReducer=function(t,e,u){return h.H.useReducer(t,e,u)},r.useRef=function(t){return h.H.useRef(t)},r.useState=function(t){return h.H.useState(t)},r.useSyncExternalStore=function(t,e,u){return h.H.useSyncExternalStore(t,e,u)},r.useTransition=function(){return h.H.useTransition()},r.version="19.2.4",r}var V;function dt(){return V||(V=1,X.exports=at()),X.exports}var s=dt();const Pt=ct(s),Et=s.createContext({});function rt(n){const p=s.useRef(null);return p.current===null&&(p.current=n()),p.current}const ht=typeof window<"u",_t=ht?s.useLayoutEffect:s.useEffect,ot=s.createContext(null);function yt(n){return typeof n=="object"&&n!==null}function tt(n){return yt(n)&&"offsetHeight"in n}const Rt=s.createContext({transformPagePoint:n=>n,isStatic:!1,reducedMotion:"never"});function et(n,p){if(typeof n=="function")return n(p);n!=null&&(n.current=p)}function mt(...n){return p=>{let i=!1;const E=n.map(a=>{const l=et(a,p);return!i&&typeof l=="function"&&(i=!0),l});if(i)return()=>{for(let a=0;a<E.length;a++){const l=E[a];typeof l=="function"?l():et(n[a],null)}}}}function Ct(...n){return s.useCallback(mt(...n),n)}class vt extends s.Component{getSnapshotBeforeUpdate(p){const i=this.props.childRef.current;if(i&&p.isPresent&&!this.props.isPresent){const E=i.offsetParent,a=tt(E)&&E.offsetWidth||0,l=tt(E)&&E.offsetHeight||0,c=this.props.sizeRef.current;c.height=i.offsetHeight||0,c.width=i.offsetWidth||0,c.top=i.offsetTop,c.left=i.offsetLeft,c.right=a-c.width-c.left,c.bottom=l-c.height-c.top}return null}componentDidUpdate(){}render(){return this.props.children}}function Tt({children:n,isPresent:p,anchorX:i,anchorY:E,root:a}){const l=s.useId(),c=s.useRef(null),w=s.useRef({width:0,height:0,top:0,left:0,right:0,bottom:0}),{nonce:H}=s.useContext(Rt),k=n.props?.ref??n?.ref,m=Ct(c,k);return s.useInsertionEffect(()=>{const{width:M,height:R,top:T,left:x,right:A,bottom:S}=w.current;if(p||!c.current||!M||!R)return;const P=i==="left"?`left: ${x}`:`right: ${A}`,L=E==="bottom"?`bottom: ${S}`:`top: ${T}`;c.current.dataset.motionPopId=l;const j=document.createElement("style");H&&(j.nonce=H);const C=a??document.head;return C.appendChild(j),j.sheet&&j.sheet.insertRule(`
2
+ [data-motion-pop-id="${l}"] {
3
+ position: absolute !important;
4
+ width: ${M}px !important;
5
+ height: ${R}px !important;
6
+ ${P}px !important;
7
+ ${L}px !important;
8
+ }
9
+ `),()=>{C.contains(j)&&C.removeChild(j)}},[p]),q.jsx(vt,{isPresent:p,childRef:c,sizeRef:w,children:s.cloneElement(n,{ref:m})})}const xt=({children:n,initial:p,isPresent:i,onExitComplete:E,custom:a,presenceAffectsLayout:l,mode:c,anchorX:w,anchorY:H,root:k})=>{const m=rt(gt),M=s.useId();let R=!0,T=s.useMemo(()=>(R=!1,{id:M,initial:p,isPresent:i,custom:a,onExitComplete:x=>{m.set(x,!0);for(const A of m.values())if(!A)return;E&&E()},register:x=>(m.set(x,!1),()=>m.delete(x))}),[i,m,E]);return l&&R&&(T={...T}),s.useMemo(()=>{m.forEach((x,A)=>m.set(A,!1))},[i]),s.useEffect(()=>{!i&&!m.size&&E&&E()},[i]),c==="popLayout"&&(n=q.jsx(Tt,{isPresent:i,anchorX:w,anchorY:H,root:k,children:n})),q.jsx(ot.Provider,{value:T,children:n})};function gt(){return new Map}function wt(n=!0){const p=s.useContext(ot);if(p===null)return[!0,null];const{isPresent:i,onExitComplete:E,register:a}=p,l=s.useId();s.useEffect(()=>{if(n)return a(l)},[n]);const c=s.useCallback(()=>n&&E&&E(l),[l,E,n]);return!i&&E?[!1,c]:[!0]}const J=n=>n.key||"";function nt(n){const p=[];return s.Children.forEach(n,i=>{s.isValidElement(i)&&p.push(i)}),p}const jt=({children:n,custom:p,initial:i=!0,onExitComplete:E,presenceAffectsLayout:a=!0,mode:l="sync",propagate:c=!1,anchorX:w="left",anchorY:H="top",root:k})=>{const[m,M]=wt(c),R=s.useMemo(()=>nt(n),[n]),T=c&&!m?[]:R.map(J),x=s.useRef(!0),A=s.useRef(R),S=rt(()=>new Map),P=s.useRef(new Set),[L,j]=s.useState(R),[C,U]=s.useState(R);_t(()=>{x.current=!1,A.current=R;for(let g=0;g<C.length;g++){const y=J(C[g]);T.includes(y)?(S.delete(y),P.current.delete(y)):S.get(y)!==!0&&S.set(y,!1)}},[C,T.length,T.join("-")]);const I=[];if(R!==L){let g=[...R];for(let y=0;y<C.length;y++){const O=C[y],Y=J(O);T.includes(Y)||(g.splice(y,0,O),I.push(O))}return l==="wait"&&I.length&&(g=I),U(nt(g)),j(R),null}const{forceRender:h}=s.useContext(Et);return q.jsx(q.Fragment,{children:C.map(g=>{const y=J(g),O=c&&!m?!1:R===C||T.includes(y),Y=()=>{if(P.current.has(y))return;if(P.current.add(y),S.has(y))S.set(y,!0);else return;let b=!0;S.forEach(z=>{z||(b=!1)}),b&&(h?.(),U(A.current),c&&M?.(),E&&E())};return q.jsx(xt,{isPresent:O,initial:!x.current||i?void 0:!1,custom:p,presenceAffectsLayout:a,mode:l,root:k,onExitComplete:O?void 0:Y,anchorX:w,anchorY:H,children:g},y)})})};export{jt as A,Pt as R,s as a,ct as g,q as j,dt as r};
@@ -5,8 +5,10 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>client</title>
8
- <script type="module" crossorigin src="/assets/index-BYMVif3u.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BIu7r5_5.css">
8
+ <script type="module" crossorigin src="/assets/index-CbgYG3pJ.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/motion-CGUGF2CN.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/graph-Cw_XSlvx.js">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-CgDCc8Mm.css">
10
12
  </head>
11
13
  <body>
12
14
  <div id="root"></div>
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Test helper for creating SQLite fixture databases.
3
+ *
4
+ * Creates a real SQLite DB at the expected XDG path so that the
5
+ * db.ts singleton picks it up via `XDG_DATA_HOME`.
6
+ *
7
+ * Usage:
8
+ * 1. Call `setupTestDb(testDir)` in beforeEach — sets XDG_DATA_HOME, creates DB, resets singleton
9
+ * 2. Use `insertSession()` / `insertMessage()` etc. to populate fixtures
10
+ * 3. Call `teardownTestDb(originalXdg)` in afterEach — closes DB, resets singleton, restores env
11
+ */
12
+ import { Database } from "bun:sqlite";
13
+ import { mkdir, rm, writeFile } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { closeDb } from "../../storage/db";
16
+ import { invalidatePollCache } from "../../services/pollService";
17
+
18
+ const SCHEMA_SQL = `
19
+ CREATE TABLE IF NOT EXISTS project (
20
+ id TEXT PRIMARY KEY,
21
+ name TEXT,
22
+ worktree TEXT NOT NULL DEFAULT '',
23
+ vcs TEXT,
24
+ commands TEXT,
25
+ sandboxes TEXT,
26
+ time_created INTEGER NOT NULL DEFAULT 0,
27
+ time_updated INTEGER NOT NULL DEFAULT 0
28
+ );
29
+
30
+ CREATE TABLE IF NOT EXISTS session (
31
+ id TEXT PRIMARY KEY,
32
+ project_id TEXT NOT NULL,
33
+ parent_id TEXT,
34
+ slug TEXT,
35
+ directory TEXT NOT NULL DEFAULT '',
36
+ title TEXT NOT NULL DEFAULT '',
37
+ version TEXT,
38
+ time_created INTEGER NOT NULL DEFAULT 0,
39
+ time_updated INTEGER NOT NULL DEFAULT 0
40
+ );
41
+
42
+ CREATE TABLE IF NOT EXISTS message (
43
+ id TEXT PRIMARY KEY,
44
+ session_id TEXT NOT NULL,
45
+ time_created INTEGER NOT NULL DEFAULT 0,
46
+ time_updated INTEGER NOT NULL DEFAULT 0,
47
+ data TEXT NOT NULL DEFAULT '{}'
48
+ );
49
+
50
+ CREATE TABLE IF NOT EXISTS part (
51
+ id TEXT PRIMARY KEY,
52
+ message_id TEXT NOT NULL,
53
+ session_id TEXT NOT NULL,
54
+ time_created INTEGER NOT NULL DEFAULT 0,
55
+ time_updated INTEGER NOT NULL DEFAULT 0,
56
+ data TEXT NOT NULL DEFAULT '{}'
57
+ );
58
+
59
+ CREATE TABLE IF NOT EXISTS todo (
60
+ session_id TEXT NOT NULL,
61
+ content TEXT NOT NULL DEFAULT '',
62
+ status TEXT NOT NULL DEFAULT 'pending',
63
+ priority TEXT NOT NULL DEFAULT 'medium',
64
+ position INTEGER NOT NULL DEFAULT 0,
65
+ time_created INTEGER NOT NULL DEFAULT 0,
66
+ time_updated INTEGER NOT NULL DEFAULT 0
67
+ );
68
+ `;
69
+
70
+ export interface TestSessionFixture {
71
+ id: string;
72
+ projectId: string;
73
+ directory: string;
74
+ title?: string;
75
+ parentId?: string | null;
76
+ timeCreated?: number;
77
+ timeUpdated?: number;
78
+ }
79
+
80
+ export interface TestMessageFixture {
81
+ id: string;
82
+ sessionId: string;
83
+ role?: string;
84
+ agent?: string;
85
+ timeCreated?: number;
86
+ timeUpdated?: number;
87
+ }
88
+
89
+ /**
90
+ * Returns the expected DB file path for a given test directory.
91
+ */
92
+ export function getTestDbPath(testDir: string): string {
93
+ return join(testDir, "opencode", "opencode.db");
94
+ }
95
+
96
+ /**
97
+ * Creates a test SQLite database with the full schema at the XDG-expected path.
98
+ * Returns the Database instance (writable) for inserting fixtures.
99
+ */
100
+ export async function createTestDatabase(testDir: string): Promise<Database> {
101
+ const dbDir = join(testDir, "opencode");
102
+ await mkdir(dbDir, { recursive: true });
103
+
104
+ const dbPath = getTestDbPath(testDir);
105
+ const db = new Database(dbPath);
106
+ db.exec(SCHEMA_SQL);
107
+ return db;
108
+ }
109
+
110
+ /**
111
+ * Full setup: sets XDG_DATA_HOME, resets db singleton, creates test DB.
112
+ * Returns the writable Database instance for inserting fixtures.
113
+ */
114
+ export async function setupTestDb(testDir: string): Promise<Database> {
115
+ await rm(testDir, { recursive: true, force: true });
116
+ await mkdir(testDir, { recursive: true });
117
+
118
+ // Reset singleton so it picks up the new XDG path
119
+ closeDb();
120
+ process.env.XDG_DATA_HOME = testDir;
121
+ invalidatePollCache();
122
+
123
+ return createTestDatabase(testDir);
124
+ }
125
+
126
+ /**
127
+ * Full teardown: closes writable DB, resets singleton, cleans up, restores env.
128
+ */
129
+ export async function teardownTestDb(
130
+ testDb: Database | null,
131
+ testDir: string,
132
+ originalXdg: string | undefined,
133
+ ): Promise<void> {
134
+ if (testDb) {
135
+ try { testDb.close(); } catch { /* already closed */ }
136
+ }
137
+
138
+ closeDb();
139
+ invalidatePollCache();
140
+
141
+ await rm(testDir, { recursive: true, force: true });
142
+
143
+ if (originalXdg === undefined) {
144
+ delete process.env.XDG_DATA_HOME;
145
+ } else {
146
+ process.env.XDG_DATA_HOME = originalXdg;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Insert a session fixture into the test database.
152
+ */
153
+ export function insertSession(db: Database, fixture: TestSessionFixture): void {
154
+ const now = Date.now();
155
+ db.run(
156
+ `INSERT INTO session (id, project_id, parent_id, slug, directory, title, time_created, time_updated)
157
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
158
+ [
159
+ fixture.id,
160
+ fixture.projectId,
161
+ fixture.parentId ?? null,
162
+ fixture.id,
163
+ fixture.directory,
164
+ fixture.title ?? `Session ${fixture.id}`,
165
+ fixture.timeCreated ?? now,
166
+ fixture.timeUpdated ?? now,
167
+ ],
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Insert a message fixture into the test database.
173
+ */
174
+ export function insertMessage(db: Database, fixture: TestMessageFixture): void {
175
+ const now = Date.now();
176
+ const data = JSON.stringify({
177
+ role: fixture.role ?? "assistant",
178
+ agent: fixture.agent ?? null,
179
+ });
180
+ db.run(
181
+ `INSERT INTO message (id, session_id, time_created, time_updated, data)
182
+ VALUES (?, ?, ?, ?, ?)`,
183
+ [
184
+ fixture.id,
185
+ fixture.sessionId,
186
+ fixture.timeCreated ?? now,
187
+ fixture.timeUpdated ?? now,
188
+ data,
189
+ ],
190
+ );
191
+ }
192
+
193
+ /**
194
+ * Write a boulder.json + plan markdown in a project directory.
195
+ * (Boulder is filesystem-based, not in SQLite.)
196
+ */
197
+ export async function writeBoulderFixture(
198
+ projectDirectory: string,
199
+ planName: string,
200
+ ): Promise<void> {
201
+ const sisyphusDir = join(projectDirectory, ".sisyphus");
202
+ const plansDir = join(sisyphusDir, "plans");
203
+ await mkdir(plansDir, { recursive: true });
204
+
205
+ await writeFile(
206
+ join(plansDir, `${planName}.md`),
207
+ ["# Test Plan", "", "- [x] Complete fixture setup", "- [ ] Keep testing"].join("\n"),
208
+ );
209
+
210
+ await writeFile(
211
+ join(sisyphusDir, "boulder.json"),
212
+ JSON.stringify({
213
+ active_plan: `.sisyphus/plans/${planName}.md`,
214
+ session_ids: [],
215
+ status: "in-progress",
216
+ started_at: new Date().toISOString(),
217
+ plan_name: planName,
218
+ }),
219
+ );
220
+ }
@@ -8,7 +8,7 @@ import { errorHandler, notFoundHandler } from "./middleware/error";
8
8
  import { registerRoutes } from "./routes";
9
9
  import { parseArgs, printHelp, openBrowser } from "./cli";
10
10
  import { getGlobalWatcher, closeAllSSEConnections } from "./routes/sse";
11
- import { listAllSessions } from "./storage/sessionParser";
11
+ import { queryProjectByWorktree, closeDb } from "./storage";
12
12
 
13
13
  const clientDistPath = join(import.meta.dir, "..", "client", "dist");
14
14
  const flags = parseArgs();
@@ -18,32 +18,17 @@ if (flags.showHelp) {
18
18
  process.exit(0);
19
19
  }
20
20
 
21
+ // Start watcher eagerly so fs events are captured before the first request
22
+ getGlobalWatcher();
23
+
21
24
  function normalizeDirectoryPath(pathValue: string): string {
22
25
  return normalize(resolve(pathValue));
23
26
  }
24
27
 
25
- async function resolveDefaultProjectId(projectPath: string): Promise<string | undefined> {
26
- const knownSessions = await listAllSessions();
27
- const requestedPath = normalizeDirectoryPath(projectPath);
28
- const seenProjectDirectories = new Map<string, string>();
29
-
30
- for (const session of knownSessions) {
31
- if (!session.directory || seenProjectDirectories.has(session.projectID)) {
32
- continue;
33
- }
34
- seenProjectDirectories.set(
35
- session.projectID,
36
- normalizeDirectoryPath(session.directory)
37
- );
38
- }
39
-
40
- for (const [projectID, directory] of seenProjectDirectories) {
41
- if (directory === requestedPath) {
42
- return projectID;
43
- }
44
- }
45
-
46
- return undefined;
28
+ function resolveDefaultProjectId(projectPath: string): string | undefined {
29
+ const normalizedPath = normalizeDirectoryPath(projectPath);
30
+ const project = queryProjectByWorktree(normalizedPath);
31
+ return project?.id;
47
32
  }
48
33
 
49
34
  async function getDefaultProjectIdFromFlag(projectPath: string | null): Promise<string | undefined> {
@@ -52,7 +37,7 @@ async function getDefaultProjectIdFromFlag(projectPath: string | null): Promise<
52
37
  }
53
38
 
54
39
  try {
55
- const defaultProjectId = await resolveDefaultProjectId(projectPath);
40
+ const defaultProjectId = resolveDefaultProjectId(projectPath);
56
41
  if (!defaultProjectId) {
57
42
  console.warn(
58
43
  `[ocwatch] --project path did not match any known project directory: ${projectPath}`
@@ -112,8 +97,21 @@ export default {
112
97
 
113
98
  function shutdown() {
114
99
  console.log("\n🛑 Shutting down gracefully...");
115
- try { getGlobalWatcher().stop(); } catch {}
116
- try { closeAllSSEConnections(); } catch {}
100
+ try {
101
+ getGlobalWatcher().stop();
102
+ } catch (error) {
103
+ console.warn('[shutdown] Failed to stop watcher:', error instanceof Error ? error.message : error);
104
+ }
105
+ try {
106
+ closeAllSSEConnections();
107
+ } catch (error) {
108
+ console.warn('[shutdown] Failed to close SSE connections:', error instanceof Error ? error.message : error);
109
+ }
110
+ try {
111
+ closeDb();
112
+ } catch (error) {
113
+ console.warn('[shutdown] Failed to close database:', error instanceof Error ? error.message : error);
114
+ }
117
115
  process.exit(0);
118
116
  }
119
117
 
@@ -125,5 +123,7 @@ if (flags.noBrowser) {
125
123
  console.log(`📡 API ready for Vite dev server`);
126
124
  } else {
127
125
  console.log(`📋 Press Ctrl+C to stop`);
128
- openBrowser(url).catch(() => {});
126
+ openBrowser(url).catch((error) => {
127
+ console.warn('[ocwatch] Failed to open browser:', error instanceof Error ? error.message : error);
128
+ });
129
129
  }
@@ -0,0 +1,260 @@
1
+ import type { PartMeta, SessionActivityType, SessionStatus } from "../../shared/types";
2
+
3
+ const MAX_PATH_LENGTH = 40;
4
+
5
+ function truncatePath(path: string): string {
6
+ if (path.length <= MAX_PATH_LENGTH) {
7
+ return path;
8
+ }
9
+ return "..." + path.slice(-MAX_PATH_LENGTH + 3);
10
+ }
11
+
12
+ export const TOOL_DISPLAY_NAMES: Record<string, string> = {
13
+ read: "Reading",
14
+ write: "Writing",
15
+ edit: "Editing",
16
+ bash: "Running",
17
+ grep: "Searching",
18
+ glob: "Finding",
19
+ task: "Delegating",
20
+ webfetch: "Fetching",
21
+ agent: "Agent",
22
+ subtask: "Subtask",
23
+ compaction: "Context Compaction",
24
+ file: "File",
25
+ };
26
+
27
+ function getToolDisplayName(tool: string): string {
28
+ const normalized = tool.replace(/^mcp_/, "").toLowerCase();
29
+ return TOOL_DISPLAY_NAMES[normalized] || tool;
30
+ }
31
+
32
+ export function formatCurrentAction(part: PartMeta): string | null {
33
+ if (!part.tool) {
34
+ return null;
35
+ }
36
+
37
+ const toolName = getToolDisplayName(part.tool);
38
+
39
+ if (part.tool === "task" || part.tool === "delegate_task") {
40
+ const input = part.input as { description?: string; subagent_type?: string } | undefined;
41
+ const desc = input?.description;
42
+ const agentType = input?.subagent_type;
43
+ if (desc && agentType) return `${desc} (${agentType})`;
44
+ if (desc) return desc;
45
+ if (agentType) return `Delegating (${agentType})`;
46
+ return "Delegating task";
47
+ }
48
+
49
+ if (part.tool === "agent" || part.tool === "subtask") {
50
+ const input = part.input as { description?: string; subagent_type?: string; name?: string } | undefined;
51
+ const desc = input?.description;
52
+ const name = input?.name;
53
+ const agentType = input?.subagent_type;
54
+ if (desc) return desc;
55
+ if (name) return `${toolName}: ${name}`;
56
+ if (agentType) return `${toolName} (${agentType})`;
57
+ return toolName;
58
+ }
59
+
60
+ if (part.tool === "compaction") {
61
+ return "Compacting context";
62
+ }
63
+
64
+ if (part.tool === "todowrite") {
65
+ const input = part.input as { todos?: Array<{ content?: string }> } | undefined;
66
+ const todos = input?.todos;
67
+ if (!todos || todos.length === 0) return "Cleared todos";
68
+ const preview = todos
69
+ .slice(0, 2)
70
+ .map(t => (t.content || "").slice(0, 30))
71
+ .filter(Boolean)
72
+ .join(", ");
73
+ return `Updated ${todos.length} todo${todos.length !== 1 ? "s" : ""}: ${preview}${todos.length > 2 ? "..." : ""}`;
74
+ }
75
+
76
+ if (part.tool === "todoread") {
77
+ return "Reading todos";
78
+ }
79
+
80
+ if (part.input) {
81
+ if (part.input.filePath) {
82
+ return `${toolName} ${truncatePath(part.input.filePath)}`;
83
+ }
84
+ if (part.input.command) {
85
+ const cmd = part.input.command.length > 30
86
+ ? part.input.command.slice(0, 27) + "..."
87
+ : part.input.command;
88
+ return `${toolName} ${cmd}`;
89
+ }
90
+ if (part.input.pattern) {
91
+ return `${toolName} for "${part.input.pattern}"`;
92
+ }
93
+ if (part.input.url) {
94
+ return `${toolName} ${truncatePath(part.input.url)}`;
95
+ }
96
+ if (part.input.query) {
97
+ return `${toolName} "${part.input.query}"`;
98
+ }
99
+ }
100
+
101
+ if (part.title) {
102
+ return part.title;
103
+ }
104
+
105
+ return toolName;
106
+ }
107
+
108
+ const ACTIVE_TOOL_STATES = ["pending", "running", "in_progress"];
109
+
110
+ export function isPendingToolCall(part: PartMeta): boolean {
111
+ if (!part.tool || part.type !== "tool") {
112
+ return false;
113
+ }
114
+
115
+ if (!part.state) {
116
+ return false;
117
+ }
118
+
119
+ return ACTIVE_TOOL_STATES.includes(part.state);
120
+ }
121
+
122
+ export interface SessionActivityState {
123
+ hasPendingToolCall: boolean;
124
+ pendingCount: number;
125
+ completedCount: number;
126
+ lastToolCompletedAt: Date | null;
127
+ isReasoning: boolean;
128
+ reasoningPreview: string | null;
129
+ patchFilesCount: number;
130
+ stepFinishReason: "stop" | "tool-calls" | null;
131
+ activeToolNames: string[];
132
+ }
133
+
134
+ export function getSessionActivityState(parts: PartMeta[]): SessionActivityState {
135
+ let pendingCount = 0;
136
+ let completedCount = 0;
137
+ let lastToolCompletedAt: Date | null = null;
138
+ let isReasoning = false;
139
+ let reasoningPreview: string | null = null;
140
+ let patchFilesCount = 0;
141
+ let stepFinishReason: "stop" | "tool-calls" | null = null;
142
+ const activeToolNames: string[] = [];
143
+
144
+ const sortedParts = [...parts].sort((a, b) => {
145
+ const timeA = a.startedAt?.getTime() || 0;
146
+ const timeB = b.startedAt?.getTime() || 0;
147
+ return timeB - timeA;
148
+ });
149
+
150
+ for (const part of sortedParts) {
151
+ if (part.type === "tool" && part.tool) {
152
+ if (isPendingToolCall(part)) {
153
+ pendingCount++;
154
+ activeToolNames.push(part.tool.replace(/^mcp_/, ""));
155
+ } else if (part.state === "completed") {
156
+ completedCount++;
157
+ if (part.completedAt && (!lastToolCompletedAt || part.completedAt > lastToolCompletedAt)) {
158
+ lastToolCompletedAt = part.completedAt;
159
+ }
160
+ }
161
+ }
162
+
163
+ if (part.type === "reasoning" && part.reasoningText && !isReasoning) {
164
+ isReasoning = true;
165
+ const text = part.reasoningText.trim();
166
+ reasoningPreview = text.length > 40 ? text.slice(0, 37) + "..." : text;
167
+ }
168
+
169
+ if (part.type === "patch" && part.patchFiles && !part.completedAt) {
170
+ patchFilesCount += part.patchFiles.length;
171
+ }
172
+
173
+ if (part.type === "step-finish" && part.stepFinishReason && !stepFinishReason) {
174
+ stepFinishReason = part.stepFinishReason;
175
+ }
176
+ }
177
+
178
+ return {
179
+ hasPendingToolCall: pendingCount > 0,
180
+ pendingCount,
181
+ completedCount,
182
+ lastToolCompletedAt,
183
+ isReasoning,
184
+ reasoningPreview,
185
+ patchFilesCount,
186
+ stepFinishReason,
187
+ activeToolNames,
188
+ };
189
+ }
190
+
191
+ export function deriveActivityType(
192
+ activityState: SessionActivityState,
193
+ lastAssistantFinished: boolean,
194
+ isSubagent: boolean,
195
+ status: SessionStatus,
196
+ waitingReason?: "user" | "children"
197
+ ): SessionActivityType {
198
+ if (status === "completed") {
199
+ return "idle";
200
+ }
201
+ if ((waitingReason === "user" || (!waitingReason && lastAssistantFinished)) && !isSubagent && status === "waiting") {
202
+ return "waiting-user";
203
+ }
204
+ if (activityState.pendingCount > 0) {
205
+ return "tool";
206
+ }
207
+ if (activityState.isReasoning) {
208
+ return "reasoning";
209
+ }
210
+ if (activityState.patchFilesCount > 0) {
211
+ return "patch";
212
+ }
213
+ if (activityState.stepFinishReason === "tool-calls") {
214
+ return "waiting-tools";
215
+ }
216
+ return "idle";
217
+ }
218
+
219
+ export function generateActivityMessage(
220
+ activityState: SessionActivityState,
221
+ lastAssistantFinished: boolean,
222
+ isSubagent: boolean,
223
+ status: SessionStatus,
224
+ pendingPart?: PartMeta,
225
+ waitingReason?: "user" | "children"
226
+ ): string | null {
227
+ if (status === "completed") {
228
+ return null;
229
+ }
230
+ if ((waitingReason === "user" || (!waitingReason && lastAssistantFinished)) && !isSubagent && status === "waiting") {
231
+ return "Waiting for user input";
232
+ }
233
+
234
+ if (activityState.pendingCount > 1) {
235
+ const toolNames = activityState.activeToolNames.slice(0, 3).join(", ");
236
+ const firstToolAction = pendingPart ? formatCurrentAction(pendingPart) : null;
237
+ if (firstToolAction) {
238
+ return `Running ${activityState.pendingCount} tools (${firstToolAction})`;
239
+ }
240
+ return `Running ${activityState.pendingCount} tools: ${toolNames}${activityState.activeToolNames.length > 3 ? "..." : ""}`;
241
+ }
242
+
243
+ if (activityState.pendingCount === 1 && pendingPart) {
244
+ return formatCurrentAction(pendingPart);
245
+ }
246
+
247
+ if (activityState.isReasoning && activityState.reasoningPreview) {
248
+ return `Analyzing: ${activityState.reasoningPreview}`;
249
+ }
250
+
251
+ if (activityState.patchFilesCount > 0) {
252
+ return `Writing ${activityState.patchFilesCount} file${activityState.patchFilesCount !== 1 ? "s" : ""}...`;
253
+ }
254
+
255
+ if (activityState.stepFinishReason === "tool-calls") {
256
+ return "Waiting for tool results";
257
+ }
258
+
259
+ return null;
260
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./activityLogic";
2
+ export * from "./sessionLogic";