loki-mode 6.36.0 → 6.36.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ *,: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:Inter,system-ui,-apple-system,sans-serif;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:JetBrains Mono,Fira Code,Cascadia Code,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}html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.terminal-scroll::-webkit-scrollbar{width:6px}.terminal-scroll::-webkit-scrollbar-track{background:transparent}.terminal-scroll::-webkit-scrollbar-thumb{background:#3d52a033;border-radius:3px}.terminal-scroll::-webkit-scrollbar-thumb:hover{background:#3d52a059}.glass{background:#fff9;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgba(255,255,255,.3);border-radius:16px;box-shadow:0 8px 32px #3d52a014}.glass-subtle{background:#fff6;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.2);border-radius:12px;box-shadow:0 4px 16px #3d52a00f}.pattern-circles{position:absolute;top:0;right:0;bottom:0;left:0;opacity:.04;background-image:radial-gradient(circle at 20% 30%,transparent 30%,#6C63FF 30.5%,transparent 31%),radial-gradient(circle at 80% 70%,transparent 25%,#6C63FF 25.5%,transparent 26%),radial-gradient(circle at 50% 50%,transparent 40%,#6C63FF 40.5%,transparent 41%);pointer-events:none}@keyframes phase-pulse{0%,to{opacity:1}50%{opacity:.5}}.phase-active{animation:phase-pulse 2s ease-in-out infinite}@keyframes cursor-blink{0%,to{opacity:1}50%{opacity:0}}.terminal-cursor:after{content:"";display:inline-block;width:8px;height:16px;background:#7091e6;animation:cursor-blink 1s step-end infinite;margin-left:2px;vertical-align:text-bottom}.\!visible{visibility:visible!important}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.right-0{right:0}.top-0{top:0}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.col-span-3{grid-column:span 3 / span 3}.col-span-4{grid-column:span 4 / span 4}.col-span-5{grid-column:span 5 / span 5}.col-span-6{grid-column:span 6 / span 6}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-8{margin-bottom:2rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.grid{display:grid}.h-1\.5{height:.375rem}.h-14{height:3.5rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-28{height:7rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.max-h-32{max-height:8rem}.max-h-40{max-height:10rem}.max-h-64{max-height:16rem}.max-h-\[400px\]{max-height:400px}.min-h-0{min-height:0px}.min-h-\[280px\]{min-height:280px}.min-h-\[300px\]{min-height:300px}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-28{width:7rem}.w-3{width:.75rem}.w-4{width:1rem}.w-56{width:14rem}.w-8{width:2rem}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.max-w-3xl{max-width:48rem}.max-w-\[1920px\]{max-width:1920px}.max-w-\[220px\]{max-width:220px}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@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-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.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-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.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))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-accent-product{--tw-border-opacity: 1;border-color:rgb(108 99 255 / var(--tw-border-opacity, 1))}.border-accent-product\/20{border-color:#6c63ff33}.border-accent-product\/30{border-color:#6c63ff4d}.border-danger\/20{border-color:#c45b5b33}.border-primary-wash\/30{border-color:#8697c44d}.border-primary\/20{border-color:#3d52a033}.border-slate\/20{border-color:#4f5d7533}.border-success\/20{border-color:#7fb06933}.border-surface\/50{border-color:#adbbda80}.border-warning\/20{border-color:#d4a03c33}.border-warning\/30{border-color:#d4a03c4d}.border-warning\/40{border-color:#d4a03c66}.border-white\/10{border-color:#ffffff1a}.border-white\/20{border-color:#fff3}.border-white\/30{border-color:#ffffff4d}.border-t-transparent{border-top-color:transparent}.bg-accent-product{--tw-bg-opacity: 1;background-color:rgb(108 99 255 / var(--tw-bg-opacity, 1))}.bg-accent-product\/10{background-color:#6c63ff1a}.bg-background{--tw-bg-opacity: 1;background-color:rgb(237 232 245 / var(--tw-bg-opacity, 1))}.bg-black\/30{background-color:#0000004d}.bg-black\/5{background-color:#0000000d}.bg-charcoal\/10{background-color:#2d31421a}.bg-charcoal\/5{background-color:#2d31420d}.bg-charcoal\/\[0\.03\]{background-color:#2d314208}.bg-danger{--tw-bg-opacity: 1;background-color:rgb(196 91 91 / var(--tw-bg-opacity, 1))}.bg-danger\/10{background-color:#c45b5b1a}.bg-primary{--tw-bg-opacity: 1;background-color:rgb(61 82 160 / var(--tw-bg-opacity, 1))}.bg-primary-light{--tw-bg-opacity: 1;background-color:rgb(112 145 230 / var(--tw-bg-opacity, 1))}.bg-primary-wash\/20{background-color:#8697c433}.bg-primary\/10{background-color:#3d52a01a}.bg-slate{--tw-bg-opacity: 1;background-color:rgb(79 93 117 / var(--tw-bg-opacity, 1))}.bg-slate\/10{background-color:#4f5d751a}.bg-slate\/30{background-color:#4f5d754d}.bg-slate\/40{background-color:#4f5d7566}.bg-success{--tw-bg-opacity: 1;background-color:rgb(127 176 105 / var(--tw-bg-opacity, 1))}.bg-success\/10{background-color:#7fb0691a}.bg-surface{--tw-bg-opacity: 1;background-color:rgb(173 187 218 / var(--tw-bg-opacity, 1))}.bg-surface\/30{background-color:#adbbda4d}.bg-surface\/50{background-color:#adbbda80}.bg-warning{--tw-bg-opacity: 1;background-color:rgb(212 160 60 / var(--tw-bg-opacity, 1))}.bg-warning\/10{background-color:#d4a03c1a}.bg-warning\/5{background-color:#d4a03c0d}.bg-white\/30{background-color:#ffffff4d}.bg-white\/40{background-color:#fff6}.fill-charcoal{fill:#2d3142}.fill-primary{fill:#3d52a0}.p-0{padding:0}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.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-5{padding-left:1.25rem;padding-right:1.25rem}.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-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2\.5{padding-bottom:.625rem}.pr-2{padding-right:.5rem}.pt-0{padding-top:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,Fira Code,Cascadia Code,monospace}.font-sans{font-family:Inter,system-ui,-apple-system,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.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}.capitalize{text-transform:capitalize}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-accent-product{--tw-text-opacity: 1;color:rgb(108 99 255 / var(--tw-text-opacity, 1))}.text-charcoal{--tw-text-opacity: 1;color:rgb(45 49 66 / var(--tw-text-opacity, 1))}.text-charcoal\/70{color:#2d3142b3}.text-charcoal\/80{color:#2d3142cc}.text-danger{--tw-text-opacity: 1;color:rgb(196 91 91 / var(--tw-text-opacity, 1))}.text-primary{--tw-text-opacity: 1;color:rgb(61 82 160 / var(--tw-text-opacity, 1))}.text-primary-light{--tw-text-opacity: 1;color:rgb(112 145 230 / var(--tw-text-opacity, 1))}.text-primary-wash{--tw-text-opacity: 1;color:rgb(134 151 196 / var(--tw-text-opacity, 1))}.text-slate{--tw-text-opacity: 1;color:rgb(79 93 117 / var(--tw-text-opacity, 1))}.text-slate\/40{color:#4f5d7566}.text-slate\/50{color:#4f5d7580}.text-slate\/60{color:#4f5d7599}.text-success{--tw-text-opacity: 1;color:rgb(127 176 105 / var(--tw-text-opacity, 1))}.text-warning{--tw-text-opacity: 1;color:rgb(212 160 60 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-glass{--tw-shadow: 0 8px 32px rgba(61,82,160,.08);--tw-shadow-colored: 0 8px 32px 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-glass-subtle{--tw-shadow: 0 4px 16px rgba(61,82,160,.06);--tw-shadow-colored: 0 4px 16px 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)}.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-sm{--tw-backdrop-blur: blur(4px);-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-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}.duration-200{transition-duration:.2s}.duration-500{transition-duration:.5s}.placeholder\:text-primary-wash::-moz-placeholder{--tw-text-opacity: 1;color:rgb(134 151 196 / var(--tw-text-opacity, 1))}.placeholder\:text-primary-wash::placeholder{--tw-text-opacity: 1;color:rgb(134 151 196 / var(--tw-text-opacity, 1))}.placeholder\:text-primary-wash\/70::-moz-placeholder{color:#8697c4b3}.placeholder\:text-primary-wash\/70::placeholder{color:#8697c4b3}.hover\:bg-accent-product\/20:hover{background-color:#6c63ff33}.hover\:bg-accent-product\/5:hover{background-color:#6c63ff0d}.hover\:bg-accent-product\/90:hover{background-color:#6c63ffe6}.hover\:bg-danger\/20:hover{background-color:#c45b5b33}.hover\:bg-primary\/5:hover{background-color:#3d52a00d}.hover\:bg-warning\/10:hover{background-color:#d4a03c1a}.hover\:bg-white\/20:hover{background-color:#fff3}.hover\:bg-white\/30:hover{background-color:#ffffff4d}.hover\:bg-white\/40:hover{background-color:#fff6}.hover\:bg-white\/5:hover{background-color:#ffffff0d}.hover\:text-charcoal:hover{--tw-text-opacity: 1;color:rgb(45 49 66 / var(--tw-text-opacity, 1))}.hover\:text-primary-light:hover{--tw-text-opacity: 1;color:rgb(112 145 230 / var(--tw-text-opacity, 1))}.focus\:border-accent-product\/30:focus{border-color:#6c63ff4d}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--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(2px + 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)}.focus\:ring-accent-product\/20:focus{--tw-ring-color: rgb(108 99 255 / .2)}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-accent-product{--tw-text-opacity: 1;color:rgb(108 99 255 / var(--tw-text-opacity, 1))}
@@ -8,8 +8,8 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
11
- <script type="module" crossorigin src="/assets/index-CY21Z26x.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-DW1e50zX.css">
11
+ <script type="module" crossorigin src="/assets/index-Cm-odtQC.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-SenDDIyx.css">
13
13
  </head>
14
14
  <body class="bg-background text-charcoal font-sans antialiased">
15
15
  <div id="root"></div>
package/web-app/server.py CHANGED
@@ -830,14 +830,67 @@ async def get_metrics() -> JSONResponse:
830
830
  return JSONResponse(content=metrics)
831
831
 
832
832
 
833
+ def _infer_session_status(entry: Path) -> str:
834
+ """Infer session status from project directory contents."""
835
+ # 1. Check .loki/state/session.json for explicit phase
836
+ state_file = entry / ".loki" / "state" / "session.json"
837
+ if state_file.exists():
838
+ try:
839
+ with open(state_file) as f:
840
+ st = json.load(f)
841
+ phase = st.get("phase", "")
842
+ if phase and phase != "idle":
843
+ return phase
844
+ except (json.JSONDecodeError, OSError):
845
+ pass
846
+
847
+ # 2. Check .loki/autonomy-state.json (run.sh writes this)
848
+ for state_name in ("autonomy-state.json", ".loki/autonomy-state.json"):
849
+ sf = entry / state_name
850
+ if sf.exists():
851
+ try:
852
+ with open(sf) as f:
853
+ st = json.load(f)
854
+ if st.get("completed") or st.get("status") == "completed":
855
+ return "completed"
856
+ if st.get("status"):
857
+ return st["status"]
858
+ except (json.JSONDecodeError, OSError):
859
+ pass
860
+
861
+ # 3. Infer from file contents
862
+ files = set()
863
+ try:
864
+ files = {f.name for f in entry.iterdir() if not f.name.startswith(".")}
865
+ except OSError:
866
+ pass
867
+
868
+ source_extensions = {".js", ".ts", ".tsx", ".py", ".html", ".css", ".go", ".rs", ".java", ".rb"}
869
+ has_source = any(
870
+ (entry / f).suffix in source_extensions
871
+ for f in files
872
+ if (entry / f).is_file()
873
+ )
874
+ has_prd = "PRD.md" in files or "prd.md" in files
875
+
876
+ if has_source:
877
+ return "completed"
878
+ if has_prd and len(files) <= 2:
879
+ return "started"
880
+ if has_prd:
881
+ return "in_progress"
882
+
883
+ return "empty"
884
+
885
+
833
886
  @app.get("/api/sessions/history")
834
887
  async def get_sessions_history() -> JSONResponse:
835
- """Return list of past loki sessions from ~/.loki-sessions/ or ~/.loki/sessions/."""
888
+ """Return list of past loki sessions from ~/purple-lab-projects/ etc."""
836
889
  history: list[dict] = []
837
890
  search_dirs = [
891
+ Path.home() / "purple-lab-projects",
838
892
  Path.home() / ".loki-sessions",
839
893
  Path.home() / ".loki" / "sessions",
840
- Path.home() / "purple-lab-projects",
841
894
  ]
842
895
  for base_dir in search_dirs:
843
896
  if not base_dir.is_dir():
@@ -850,7 +903,7 @@ async def get_sessions_history() -> JSONResponse:
850
903
  "path": str(entry),
851
904
  "date": "",
852
905
  "prd_snippet": "",
853
- "status": "unknown",
906
+ "status": _infer_session_status(entry),
854
907
  }
855
908
  # Read timestamp from directory mtime
856
909
  try:
@@ -869,21 +922,83 @@ async def get_sessions_history() -> JSONResponse:
869
922
  except OSError:
870
923
  pass
871
924
  break
872
- # Try to read status
873
- state_file = entry / ".loki" / "state" / "session.json"
874
- if state_file.exists():
875
- try:
876
- with open(state_file) as f:
877
- st = json.load(f)
878
- session_info["status"] = st.get("phase", "unknown")
879
- except (json.JSONDecodeError, OSError):
880
- pass
925
+ # Count project files for progress indication
926
+ try:
927
+ file_count = sum(1 for f in entry.rglob("*") if f.is_file()
928
+ and ".git" not in f.parts and "node_modules" not in f.parts
929
+ and "__pycache__" not in f.parts)
930
+ session_info["file_count"] = file_count
931
+ except OSError:
932
+ session_info["file_count"] = 0
933
+
881
934
  history.append(session_info)
882
935
  if history:
883
936
  break # Use first directory that has entries
884
937
  return JSONResponse(content=history)
885
938
 
886
939
 
940
+ @app.get("/api/sessions/{session_id}")
941
+ async def get_session_detail(session_id: str) -> JSONResponse:
942
+ """Get details of a past session for read-only viewing."""
943
+ import re
944
+ # Validate session_id format (prevent path traversal)
945
+ if not re.match(r"^[a-zA-Z0-9_-]+$", session_id):
946
+ return JSONResponse(status_code=400, content={"error": "Invalid session ID"})
947
+
948
+ search_dirs = [
949
+ Path.home() / "purple-lab-projects",
950
+ Path.home() / ".loki-sessions",
951
+ Path.home() / ".loki" / "sessions",
952
+ ]
953
+ target: Optional[Path] = None
954
+ for base_dir in search_dirs:
955
+ candidate = base_dir / session_id
956
+ if candidate.is_dir():
957
+ target = candidate
958
+ break
959
+
960
+ if target is None:
961
+ return JSONResponse(status_code=404, content={"error": "Session not found"})
962
+
963
+ # Read PRD
964
+ prd_content = ""
965
+ for prd_name in ("PRD.md", "prd.md", ".loki/prd.md"):
966
+ prd_file = target / prd_name
967
+ if prd_file.exists():
968
+ try:
969
+ prd_content = prd_file.read_text(errors="replace")
970
+ except OSError:
971
+ pass
972
+ break
973
+
974
+ # Build file tree
975
+ files = _build_file_tree(target)
976
+
977
+ # Read logs if available
978
+ log_lines: list[str] = []
979
+ for log_name in (".loki/logs/session.log", ".loki/session.log", "loki.log"):
980
+ log_file = target / log_name
981
+ if log_file.exists():
982
+ try:
983
+ text = log_file.read_text(errors="replace")
984
+ log_lines = text.splitlines()[-200:]
985
+ except OSError:
986
+ pass
987
+ break
988
+
989
+ # Status
990
+ status = _infer_session_status(target)
991
+
992
+ return JSONResponse(content={
993
+ "id": session_id,
994
+ "path": str(target),
995
+ "status": status,
996
+ "prd": prd_content,
997
+ "files": files,
998
+ "logs": log_lines,
999
+ })
1000
+
1001
+
887
1002
  @app.post("/api/session/onboard")
888
1003
  async def onboard_session(req: OnboardRequest) -> JSONResponse:
889
1004
  """Run loki onboard on a path and return CLAUDE.md content."""