@nextclaw/ui 0.3.12 → 0.3.14

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:hsl(var(--gray-200))}: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:var(--font-sans);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:var(--font-mono);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:hsl(var(--gray-400))}input::placeholder,textarea::placeholder{opacity:1;color:hsl(var(--gray-400))}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}:root{--brand-50: 217 100% 97%;--brand-100: 217 100% 94%;--brand-200: 217 95% 87%;--brand-300: 217 90% 77%;--brand-400: 217 85% 65%;--brand-500: 217 80% 55%;--brand-600: 217 75% 48%;--brand-700: 217 70% 40%;--brand-800: 217 65% 33%;--brand-900: 217 60% 25%;--gray-50: 210 20% 98%;--gray-100: 220 14% 96%;--gray-200: 220 13% 91%;--gray-300: 216 12% 84%;--gray-400: 218 11% 65%;--gray-500: 220 9% 46%;--gray-600: 215 14% 34%;--gray-700: 217 19% 27%;--gray-800: 215 28% 17%;--gray-900: 221 39% 11%;--background-secondary: 220 14% 96%;--background-tertiary: 220 13% 91%;--foreground-secondary: 215 28% 17%;--foreground-tertiary: 220 9% 46%;--foreground-muted: 218 11% 65%;--primary-hover: 217 75% 48%;--primary-active: 217 70% 40%;--secondary-hover: 220 13% 91%;--success: 142 76% 36%;--success-foreground: 0 0% 100%;--warning: 38 92% 50%;--warning-foreground: 0 0% 100%;--card-border: 220 13% 91%;--border-hover: 216 12% 84%;--border-active: 217 80% 55%;--input: 0 0% 100%;--input-border: 220 13% 91%;--input-focus: 217 80% 55%;--ring-offset: 0 0% 100%;--space-0: 0px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--space-10: 40px;--space-12: 48px;--space-16: 64px;--space-20: 80px;--space-24: 96px;--radius-sm: 6px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--radius-2xl: 20px;--radius-3xl: 24px;--radius-full: 9999px;--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / .05);--shadow-sm: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / .25);--shadow-card: 0 1px 3px 0 rgb(0 0 0 / .05), 0 1px 2px -1px rgb(0 0 0 / .05);--shadow-card-hover: 0 10px 15px -3px rgb(0 0 0 / .08), 0 4px 6px -4px rgb(0 0 0 / .05);--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;--text-xs: 12px;--text-sm: 14px;--text-base: 16px;--text-lg: 18px;--text-xl: 20px;--text-2xl: 24px;--text-3xl: 30px;--text-4xl: 36px;--text-5xl: 48px;--font-normal: 400;--font-medium: 500;--font-semibold: 600;--font-bold: 700;--leading-tight: 1.25;--leading-snug: 1.375;--leading-normal: 1.5;--leading-relaxed: 1.625;--tracking-tight: -.025em;--tracking-normal: 0;--tracking-wide: .025em;--transition-fast: .15s ease;--transition-base: .2s ease;--transition-slow: .3s ease;--z-dropdown: 100;--z-sticky: 200;--z-fixed: 300;--z-modal-backdrop: 400;--z-modal: 500;--z-popover: 600;--z-tooltip: 700;--background: 210 20% 98%;--foreground: 221 39% 11%;--card: 0 0% 100%;--card-foreground: 221 39% 11%;--popover: 0 0% 100%;--popover-foreground: 221 39% 11%;--primary: 217 80% 55%;--primary-foreground: 0 0% 100%;--secondary: 220 14% 96%;--secondary-foreground: 215 28% 17%;--muted: 220 14% 96%;--muted-foreground: 220 9% 46%;--accent: 217 100% 97%;--accent-foreground: 217 70% 40%;--destructive: 0 84% 60%;--destructive-foreground: 0 0% 98%;--border: 220 13% 91%;--input: 220 13% 91%;--ring: 217 80% 55%;--radius: .75rem;--milk-50: 210 20% 98%;--milk-100: 220 14% 96%;--milk-200: 220 13% 91%;--milk-300: 216 12% 84%;--milk-400: 218 11% 65%;--milk-500: 220 9% 46%;--milk-600: 215 14% 34%;--milk-700: 217 19% 27%;--milk-800: 215 28% 17%;--milk-900: 221 39% 11%}*{border-color:hsl(var(--border))}html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background-color:hsl(var(--background));color:hsl(var(--foreground));font-family:var(--font-sans);overflow:hidden}*{scrollbar-width:thin;scrollbar-color:hsl(var(--gray-300)) transparent}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-0{bottom:0}.left-0{left:0}.left-3{left:.75rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-1\.5{right:.375rem}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-3{top:.75rem}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-10{z-index:10}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-0\.5{height:.125rem}.h-1{height:.25rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[180px\]{height:180px}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[480px\]{max-height:480px}.max-h-\[60vh\]{max-height:60vh}.max-h-\[85vh\]{max-height:85vh}.min-h-\[120px\]{min-height:120px}.min-h-\[42px\]{min-height:42px}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-24{width:6rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[160px\]{width:160px}.w-\[240px\]{width:240px}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[100px\]{min-width:100px}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-\[200px\]{max-width:200px}.max-w-lg{max-width:32rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50%;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-x-0{--tw-translate-x: 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))}.translate-x-5{--tw-translate-x: 1.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-x-\[-50\%\]{--tw-translate-x: -50%;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-\[-50\%\]{--tw-translate-y: -50%;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 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}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.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-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * 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))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.625rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem * 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))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:calc(var(--radius) + 8px)}.rounded-\[2rem\]{border-radius:2rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-\[hsl\(40\,10\%\,94\%\)\]{--tw-border-opacity: 1;border-color:hsl(40 10% 94% / var(--tw-border-opacity, 1))}.border-gray-200{border-color:hsl(var(--gray-200))}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.border-transparent{border-color:transparent}.bg-\[hsl\(40\,20\%\,98\%\)\]{--tw-bg-opacity: 1;background-color:hsl(40 20% 98% / var(--tw-bg-opacity, 1))}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-amber-50{--tw-bg-opacity: 1;background-color:rgb(255 251 235 / var(--tw-bg-opacity, 1))}.bg-background{background-color:hsl(var(--background))}.bg-black\/40{background-color:#0006}.bg-destructive{background-color:hsl(var(--destructive))}.bg-emerald-50{--tw-bg-opacity: 1;background-color:rgb(236 253 245 / var(--tw-bg-opacity, 1))}.bg-emerald-500{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.bg-gray-200{background-color:hsl(var(--gray-200))}.bg-gray-400{background-color:hsl(var(--gray-400))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary-100{background-color:hsl(var(--brand-100))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-slate-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\/80{background-color:#fffc}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-l{background-image:linear-gradient(to left,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-primary{--tw-gradient-from: hsl(var(--primary)) var(--tw-gradient-from-position);--tw-gradient-to: hsl(var(--primary) / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-purple-400{--tw-gradient-from: #c084fc var(--tw-gradient-from-position);--tw-gradient-to: rgb(192 132 252 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-rose-400{--tw-gradient-from: #fb7185 var(--tw-gradient-from-position);--tw-gradient-to: rgb(251 113 133 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-sky-400{--tw-gradient-from: #38bdf8 var(--tw-gradient-from-position);--tw-gradient-to: rgb(56 189 248 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-400{--tw-gradient-from: #94a3b8 var(--tw-gradient-from-position);--tw-gradient-to: rgb(148 163 184 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-transparent{--tw-gradient-from: transparent var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-blue-500{--tw-gradient-to: #3b82f6 var(--tw-gradient-to-position)}.to-gray-500{--tw-gradient-to: hsl(var(--gray-500)) var(--tw-gradient-to-position)}.to-indigo-500{--tw-gradient-to: #6366f1 var(--tw-gradient-to-position)}.to-pink-500{--tw-gradient-to: #ec4899 var(--tw-gradient-to-position)}.to-primary-600{--tw-gradient-to: hsl(var(--brand-600)) var(--tw-gradient-to-position)}.to-white\/10{--tw-gradient-to: rgb(255 255 255 / .1) var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.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}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pl-9{padding-left:2.25rem}.pr-1{padding-right:.25rem}.pr-2{padding-right:.5rem}.pr-20{padding-right:5rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[15px\]{font-size:15px}.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-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-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-\[-0\.02em\]{letter-spacing:-.02em}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-600{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-gray-400{color:hsl(var(--gray-400))}.text-gray-700{color:hsl(var(--gray-700))}.text-gray-800{color:hsl(var(--gray-800))}.text-primary{color:hsl(var(--primary))}.text-primary-700{color:hsl(var(--brand-700))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-transparent{color:transparent}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.accent-primary{accent-color:hsl(var(--primary))}.opacity-0{opacity:0}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.mix-blend-multiply{mix-blend-mode:multiply}.shadow-card{--tw-shadow: var(--shadow-card);--tw-shadow-colored: var(--shadow-card);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px 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)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px 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-card{--tw-shadow-color: hsl(var(--card));--tw-shadow: var(--tw-shadow-colored)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--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(0px + 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-offset-background{--tw-ring-offset-color: hsl(var(--background))}.ring-offset-white{--tw-ring-offset-color: #fff}.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-md{--tw-backdrop-blur: blur(12px);-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)}.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}.transition-opacity{transition-property:opacity;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-300{transition-duration:.3s}.duration-base{transition-duration:.2s}.duration-fast{transition-duration:.15s}.duration-slow{transition-duration:.3s}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.fade-in{--tw-enter-opacity: 0}.slide-in-from-left-2{--tw-enter-translate-x: -.5rem}.duration-300{animation-duration:.3s}.duration-base{animation-duration:.2s}.duration-fast{animation-duration:.15s}.duration-slow{animation-duration:.3s}.bg-gray-50{background-color:hsl(var(--gray-50))}.bg-gray-100{background-color:hsl(var(--gray-100))}.text-gray-500{color:hsl(var(--gray-500))}.text-gray-600{color:hsl(var(--gray-600))}.text-gray-900{color:hsl(var(--gray-900))}.shadow-card{box-shadow:var(--shadow-card)}.custom-scrollbar::-webkit-scrollbar{width:6px;height:6px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background:hsl(var(--gray-300));border-radius:6px}.custom-scrollbar::-webkit-scrollbar-thumb:hover{background:hsl(var(--gray-400))}.shadow-card{box-shadow:0 1px 3px #0000000d,0 1px 2px -1px #0000000d}@keyframes fadeIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes slideIn{0%{opacity:0;transform:translate(-12px)}to{opacity:1;transform:translate(0)}}@keyframes scaleIn{0%{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes pulse-soft{0%,to{opacity:1}50%{opacity:.8}}.animate-fade-in{animation:fadeIn .4s cubic-bezier(.16,1,.3,1) forwards}.animate-slide-in{animation:slideIn .35s cubic-bezier(.16,1,.3,1) forwards}.animate-scale-in{animation:scaleIn .35s cubic-bezier(.16,1,.3,1) forwards}.animate-pulse-soft{animation:pulse-soft 3s ease-in-out infinite}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.placeholder\:text-gray-400::-moz-placeholder{color:hsl(var(--gray-400))}.placeholder\:text-gray-400::placeholder{color:hsl(var(--gray-400))}.hover\:-translate-y-0\.5:hover{--tw-translate-y: -.125rem;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))}.hover\:border-gray-300:hover{border-color:hsl(var(--gray-300))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-gray-200:hover{background-color:hsl(var(--gray-200))}.hover\:bg-gray-300:hover{background-color:hsl(var(--gray-300))}.hover\:bg-primary:hover{background-color:hsl(var(--primary))}.hover\:bg-primary-600:hover{background-color:hsl(var(--brand-600))}.hover\:bg-secondary:hover{background-color:hsl(var(--secondary))}.hover\:bg-white:hover{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.hover\:bg-white\/50:hover{background-color:#ffffff80}.hover\:text-gray-700:hover{color:hsl(var(--gray-700))}.hover\:text-primary-foreground:hover{color:hsl(var(--primary-foreground))}.hover\:text-red-300:hover{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.hover\:text-secondary-foreground:hover{color:hsl(var(--secondary-foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-card-hover:hover{--tw-shadow: var(--shadow-card-hover);--tw-shadow-colored: var(--shadow-card-hover);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:bg-gray-100:hover{background-color:hsl(var(--gray-100))}.hover\:text-gray-600:hover{color:hsl(var(--gray-600))}.hover\:text-gray-900:hover{color:hsl(var(--gray-900))}.hover\:shadow-card-hover:hover{box-shadow:var(--shadow-card-hover);box-shadow:0 10px 15px -3px #00000014,0 4px 6px -4px #0000000d}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--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-visible\:ring-primary:focus-visible{--tw-ring-color: hsl(var(--primary))}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-white:focus-visible{--tw-ring-offset-color: #fff}.active\:bg-primary-700:active{background-color:hsl(var(--brand-700))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:translate-x-0\.5{--tw-translate-x: .125rem;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))}.group:hover .group-hover\: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))}.group:hover .group-hover\:border-gray-300{border-color:hsl(var(--gray-300))}.group:hover .group-hover\:text-primary{color:hsl(var(--primary))}.group:hover .group-hover\:opacity-100{opacity:1}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[state\=checked\]\:bg-emerald-500[data-state=checked]{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}@media(min-width:640px){.sm\:max-w-\[500px\]{max-width:500px}.sm\:max-w-\[550px\]{max-width:550px}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:gap-3{gap:.75rem}.sm\:text-left{text-align:left}}@media(min-width:768px){.md\:col-span-2{grid-column:span 2 / span 2}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}
package/dist/index.html CHANGED
@@ -6,8 +6,8 @@
6
6
  <link rel="icon" type="image/svg+xml" href="/logo.svg" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
8
  <title>NextClaw - 系统配置</title>
9
- <script type="module" crossorigin src="/assets/index-DTd23uLj.js"></script>
10
- <link rel="stylesheet" crossorigin href="/assets/index-D3arfjLX.css">
9
+ <script type="module" crossorigin src="/assets/index-DMFVPFFm.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/assets/index-uqpsLADF.css">
11
11
  </head>
12
12
 
13
13
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/App.tsx CHANGED
@@ -5,6 +5,7 @@ import { ModelConfig } from '@/components/config/ModelConfig';
5
5
  import { ProvidersList } from '@/components/config/ProvidersList';
6
6
  import { ChannelsList } from '@/components/config/ChannelsList';
7
7
  import { RuntimeConfig } from '@/components/config/RuntimeConfig';
8
+ import { SessionsConfig } from '@/components/config/SessionsConfig';
8
9
  import { useWebSocket } from '@/hooks/useWebSocket';
9
10
  import { Toaster } from 'sonner';
10
11
 
@@ -31,6 +32,8 @@ function AppContent() {
31
32
  return <ChannelsList />;
32
33
  case 'runtime':
33
34
  return <RuntimeConfig />;
35
+ case 'sessions':
36
+ return <SessionsConfig />;
34
37
  default:
35
38
  return <ModelConfig />;
36
39
  }
package/src/api/client.ts CHANGED
@@ -66,5 +66,9 @@ export const api = {
66
66
  apiRequest<T>(path, {
67
67
  method: 'POST',
68
68
  body: JSON.stringify(body)
69
+ }),
70
+ delete: <T>(path: string) =>
71
+ apiRequest<T>(path, {
72
+ method: 'DELETE'
69
73
  })
70
74
  };
package/src/api/config.ts CHANGED
@@ -8,7 +8,10 @@ import type {
8
8
  ProviderConfigUpdate,
9
9
  RuntimeConfigUpdate,
10
10
  ConfigActionExecuteRequest,
11
- ConfigActionExecuteResult
11
+ ConfigActionExecuteResult,
12
+ SessionsListView,
13
+ SessionHistoryView,
14
+ SessionPatchUpdate
12
15
  } from './types';
13
16
 
14
17
  // GET /api/config
@@ -107,3 +110,54 @@ export async function executeConfigAction(
107
110
  }
108
111
  return response.data;
109
112
  }
113
+
114
+
115
+ // GET /api/sessions
116
+ export async function fetchSessions(params?: { q?: string; limit?: number; activeMinutes?: number }): Promise<SessionsListView> {
117
+ const query = new URLSearchParams();
118
+ if (params?.q?.trim()) {
119
+ query.set('q', params.q.trim());
120
+ }
121
+ if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
122
+ query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
123
+ }
124
+ if (typeof params?.activeMinutes === 'number' && Number.isFinite(params.activeMinutes)) {
125
+ query.set('activeMinutes', String(Math.max(0, Math.trunc(params.activeMinutes))));
126
+ }
127
+ const suffix = query.toString();
128
+ const response = await api.get<SessionsListView>(suffix ? '/api/sessions?' + suffix : '/api/sessions');
129
+ if (!response.ok) {
130
+ throw new Error(response.error.message);
131
+ }
132
+ return response.data;
133
+ }
134
+
135
+ // GET /api/sessions/:key/history
136
+ export async function fetchSessionHistory(key: string, limit = 200): Promise<SessionHistoryView> {
137
+ const response = await api.get<SessionHistoryView>(`/api/sessions/${encodeURIComponent(key)}/history?limit=${Math.max(1, Math.trunc(limit))}`);
138
+ if (!response.ok) {
139
+ throw new Error(response.error.message);
140
+ }
141
+ return response.data;
142
+ }
143
+
144
+ // PUT /api/sessions/:key
145
+ export async function updateSession(
146
+ key: string,
147
+ data: SessionPatchUpdate
148
+ ): Promise<SessionHistoryView> {
149
+ const response = await api.put<SessionHistoryView>(`/api/sessions/${encodeURIComponent(key)}`, data);
150
+ if (!response.ok) {
151
+ throw new Error(response.error.message);
152
+ }
153
+ return response.data;
154
+ }
155
+
156
+ // DELETE /api/sessions/:key
157
+ export async function deleteSession(key: string): Promise<{ deleted: boolean }> {
158
+ const response = await api.delete<{ deleted: boolean }>(`/api/sessions/${encodeURIComponent(key)}`);
159
+ if (!response.ok) {
160
+ throw new Error(response.error.message);
161
+ }
162
+ return response.data;
163
+ }
package/src/api/types.ts CHANGED
@@ -30,6 +30,7 @@ export type AgentProfileView = {
30
30
  workspace?: string;
31
31
  model?: string;
32
32
  maxTokens?: number;
33
+ contextTokens?: number;
33
34
  maxToolIterations?: number;
34
35
  };
35
36
 
@@ -54,8 +55,48 @@ export type SessionConfigView = {
54
55
  };
55
56
  };
56
57
 
58
+ export type SessionEntryView = {
59
+ key: string;
60
+ createdAt: string;
61
+ updatedAt: string;
62
+ label?: string;
63
+ preferredModel?: string;
64
+ messageCount: number;
65
+ lastRole?: string;
66
+ lastTimestamp?: string;
67
+ };
68
+
69
+ export type SessionsListView = {
70
+ sessions: SessionEntryView[];
71
+ total: number;
72
+ };
73
+
74
+ export type SessionMessageView = {
75
+ role: string;
76
+ content: string;
77
+ timestamp: string;
78
+ name?: string;
79
+ tool_call_id?: string;
80
+ };
81
+
82
+ export type SessionHistoryView = {
83
+ key: string;
84
+ totalMessages: number;
85
+ metadata: Record<string, unknown>;
86
+ messages: SessionMessageView[];
87
+ };
88
+
89
+ export type SessionPatchUpdate = {
90
+ label?: string | null;
91
+ preferredModel?: string | null;
92
+ clearHistory?: boolean;
93
+ };
94
+
57
95
  export type RuntimeConfigUpdate = {
58
96
  agents?: {
97
+ defaults?: {
98
+ contextTokens?: number;
99
+ };
59
100
  list?: AgentProfileView[];
60
101
  };
61
102
  bindings?: AgentBindingView[];
@@ -70,6 +111,7 @@ export type ConfigView = {
70
111
  model: string;
71
112
  workspace?: string;
72
113
  maxTokens?: number;
114
+ contextTokens?: number;
73
115
  maxToolIterations?: number;
74
116
  };
75
117
  list?: AgentProfileView[];
@@ -26,6 +26,7 @@ function createEmptyAgent(): AgentProfileView {
26
26
  workspace: '',
27
27
  model: '',
28
28
  maxTokens: undefined,
29
+ contextTokens: undefined,
29
30
  maxToolIterations: undefined
30
31
  };
31
32
  }
@@ -58,6 +59,7 @@ export function RuntimeConfig() {
58
59
  const [bindings, setBindings] = useState<AgentBindingView[]>([]);
59
60
  const [dmScope, setDmScope] = useState<DmScope>('per-channel-peer');
60
61
  const [maxPingPongTurns, setMaxPingPongTurns] = useState(0);
62
+ const [defaultContextTokens, setDefaultContextTokens] = useState(200000);
61
63
 
62
64
  useEffect(() => {
63
65
  if (!config) {
@@ -70,6 +72,7 @@ export function RuntimeConfig() {
70
72
  workspace: agent.workspace ?? '',
71
73
  model: agent.model ?? '',
72
74
  maxTokens: agent.maxTokens,
75
+ contextTokens: agent.contextTokens,
73
76
  maxToolIterations: agent.maxToolIterations
74
77
  }))
75
78
  );
@@ -90,11 +93,14 @@ export function RuntimeConfig() {
90
93
  );
91
94
  setDmScope((config.session?.dmScope as DmScope) ?? 'per-channel-peer');
92
95
  setMaxPingPongTurns(config.session?.agentToAgent?.maxPingPongTurns ?? 0);
96
+ setDefaultContextTokens(config.agents.defaults.contextTokens ?? 200000);
93
97
  }, [config]);
94
98
 
95
99
  const uiHints = schema?.uiHints;
96
100
  const dmScopeHint = hintForPath('session.dmScope', uiHints);
97
101
  const maxPingHint = hintForPath('session.agentToAgent.maxPingPongTurns', uiHints);
102
+ const defaultContextTokensHint = hintForPath('agents.defaults.contextTokens', uiHints);
103
+ const agentContextTokensHint = hintForPath('agents.list.*.contextTokens', uiHints);
98
104
  const agentsHint = hintForPath('agents.list', uiHints);
99
105
  const bindingsHint = hintForPath('bindings', uiHints);
100
106
 
@@ -137,6 +143,9 @@ export function RuntimeConfig() {
137
143
  if (typeof agent.maxTokens === 'number') {
138
144
  normalized.maxTokens = agent.maxTokens;
139
145
  }
146
+ if (typeof agent.contextTokens === 'number') {
147
+ normalized.contextTokens = Math.max(1000, agent.contextTokens);
148
+ }
140
149
  if (typeof agent.maxToolIterations === 'number') {
141
150
  normalized.maxToolIterations = agent.maxToolIterations;
142
151
  }
@@ -194,7 +203,12 @@ export function RuntimeConfig() {
194
203
 
195
204
  updateRuntime.mutate({
196
205
  data: {
197
- agents: { list: normalizedAgents },
206
+ agents: {
207
+ defaults: {
208
+ contextTokens: Math.max(1000, defaultContextTokens)
209
+ },
210
+ list: normalizedAgents
211
+ },
198
212
  bindings: normalizedBindings,
199
213
  session: {
200
214
  dmScope,
@@ -229,6 +243,21 @@ export function RuntimeConfig() {
229
243
  <CardDescription>{dmScopeHint?.help ?? 'Control how direct-message sessions are isolated.'}</CardDescription>
230
244
  </CardHeader>
231
245
  <CardContent className="space-y-4">
246
+ <div className="space-y-2">
247
+ <label className="text-sm font-medium text-gray-800">
248
+ {defaultContextTokensHint?.label ?? 'Default Context Tokens'}
249
+ </label>
250
+ <Input
251
+ type="number"
252
+ min={1000}
253
+ step={1000}
254
+ value={defaultContextTokens}
255
+ onChange={(event) => setDefaultContextTokens(Math.max(1000, Number.parseInt(event.target.value, 10) || 1000))}
256
+ />
257
+ <p className="text-xs text-gray-500">
258
+ {defaultContextTokensHint?.help ?? 'Input context budget for agents when no per-agent override is set.'}
259
+ </p>
260
+ </div>
232
261
  <div className="space-y-2">
233
262
  <label className="text-sm font-medium text-gray-800">{dmScopeHint?.label ?? 'DM Scope'}</label>
234
263
  <select
@@ -285,7 +314,7 @@ export function RuntimeConfig() {
285
314
  onChange={(event) => updateAgent(index, { model: event.target.value })}
286
315
  placeholder="Model override (optional)"
287
316
  />
288
- <div className="grid grid-cols-2 gap-2">
317
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-2">
289
318
  <Input
290
319
  type="number"
291
320
  min={1}
@@ -297,6 +326,18 @@ export function RuntimeConfig() {
297
326
  }
298
327
  placeholder="Max tokens"
299
328
  />
329
+ <Input
330
+ type="number"
331
+ min={1000}
332
+ step={1000}
333
+ value={agent.contextTokens ?? ''}
334
+ onChange={(event) =>
335
+ updateAgent(index, {
336
+ contextTokens: parseOptionalInt(event.target.value)
337
+ })
338
+ }
339
+ placeholder={agentContextTokensHint?.label ?? 'Context tokens'}
340
+ />
300
341
  <Input
301
342
  type="number"
302
343
  min={1}
@@ -0,0 +1,246 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import type { SessionEntryView, SessionMessageView } from '@/api/types';
3
+ import { useDeleteSession, useSessionHistory, useSessions, useUpdateSession } from '@/hooks/useConfig';
4
+ import { Button } from '@/components/ui/button';
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
6
+ import { Input } from '@/components/ui/input';
7
+ import { RefreshCw, Save, Search, Trash2 } from 'lucide-react';
8
+
9
+ function formatDate(value?: string): string {
10
+ if (!value) {
11
+ return '-';
12
+ }
13
+ const date = new Date(value);
14
+ if (Number.isNaN(date.getTime())) {
15
+ return value;
16
+ }
17
+ return date.toLocaleString();
18
+ }
19
+
20
+ type SessionRowProps = {
21
+ session: SessionEntryView;
22
+ isSelected: boolean;
23
+ labelValue: string;
24
+ modelValue: string;
25
+ onToggleHistory: () => void;
26
+ onLabelChange: (value: string) => void;
27
+ onModelChange: (value: string) => void;
28
+ onSave: () => void;
29
+ onClear: () => void;
30
+ onDelete: () => void;
31
+ };
32
+
33
+ function SessionRow(props: SessionRowProps) {
34
+ const { session } = props;
35
+ return (
36
+ <div className="rounded-xl border border-gray-200 p-3 space-y-3">
37
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 text-xs text-gray-600">
38
+ <div>
39
+ <span className="font-semibold text-gray-800">Key:</span> {session.key}
40
+ </div>
41
+ <div>
42
+ <span className="font-semibold text-gray-800">Messages:</span> {session.messageCount}
43
+ </div>
44
+ <div>
45
+ <span className="font-semibold text-gray-800">Updated:</span> {formatDate(session.updatedAt)}
46
+ </div>
47
+ <div>
48
+ <span className="font-semibold text-gray-800">Last Role:</span> {session.lastRole ?? '-'}
49
+ </div>
50
+ </div>
51
+
52
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
53
+ <Input value={props.labelValue} onChange={(event) => props.onLabelChange(event.target.value)} placeholder="会话标签(可选)" />
54
+ <Input
55
+ value={props.modelValue}
56
+ onChange={(event) => props.onModelChange(event.target.value)}
57
+ placeholder="Preferred model(可选)"
58
+ />
59
+ </div>
60
+
61
+ <div className="flex flex-wrap gap-2">
62
+ <Button type="button" variant={props.isSelected ? 'default' : 'outline'} size="sm" onClick={props.onToggleHistory}>
63
+ {props.isSelected ? '隐藏历史' : '查看历史'}
64
+ </Button>
65
+ <Button type="button" variant="outline" size="sm" onClick={props.onSave}>
66
+ <Save className="h-4 w-4 mr-1" />
67
+ 保存元信息
68
+ </Button>
69
+ <Button type="button" variant="outline" size="sm" onClick={props.onClear}>
70
+ 清空历史
71
+ </Button>
72
+ <Button type="button" variant="outline" size="sm" onClick={props.onDelete}>
73
+ <Trash2 className="h-4 w-4 mr-1" />
74
+ 删除
75
+ </Button>
76
+ </div>
77
+ </div>
78
+ );
79
+ }
80
+
81
+ function SessionMessageItem({ message, index }: { message: SessionMessageView; index: number }) {
82
+ return (
83
+ <div key={`${message.timestamp}-${index}`} className="rounded-lg border border-gray-200 p-2">
84
+ <div className="text-xs text-gray-500">
85
+ <span className="font-semibold text-gray-700">{message.role}</span> · {formatDate(message.timestamp)}
86
+ </div>
87
+ <div className="text-sm whitespace-pre-wrap break-words mt-1">{message.content}</div>
88
+ </div>
89
+ );
90
+ }
91
+
92
+ export function SessionsConfig() {
93
+ const [query, setQuery] = useState('');
94
+ const [limit, setLimit] = useState(100);
95
+ const [activeMinutes, setActiveMinutes] = useState(0);
96
+ const [selectedKey, setSelectedKey] = useState<string | null>(null);
97
+ const [labelDraft, setLabelDraft] = useState<Record<string, string>>({});
98
+ const [modelDraft, setModelDraft] = useState<Record<string, string>>({});
99
+
100
+ const sessionsParams = useMemo(() => ({ q: query.trim() || undefined, limit, activeMinutes }), [query, limit, activeMinutes]);
101
+ const sessionsQuery = useSessions(sessionsParams);
102
+ const historyQuery = useSessionHistory(selectedKey, 200);
103
+ const updateSession = useUpdateSession();
104
+ const deleteSession = useDeleteSession();
105
+
106
+ useEffect(() => {
107
+ const sessions = sessionsQuery.data?.sessions ?? [];
108
+ if (!sessions.length) {
109
+ return;
110
+ }
111
+ setLabelDraft((prev) => {
112
+ const next = { ...prev };
113
+ for (const session of sessions) {
114
+ if (!(session.key in next)) {
115
+ next[session.key] = session.label ?? '';
116
+ }
117
+ }
118
+ return next;
119
+ });
120
+ setModelDraft((prev) => {
121
+ const next = { ...prev };
122
+ for (const session of sessions) {
123
+ if (!(session.key in next)) {
124
+ next[session.key] = session.preferredModel ?? '';
125
+ }
126
+ }
127
+ return next;
128
+ });
129
+ }, [sessionsQuery.data]);
130
+
131
+ const sessions = sessionsQuery.data?.sessions ?? [];
132
+
133
+ const saveSessionMeta = (key: string) => {
134
+ updateSession.mutate({
135
+ key,
136
+ data: {
137
+ label: (labelDraft[key] ?? '').trim() || null,
138
+ preferredModel: (modelDraft[key] ?? '').trim() || null
139
+ }
140
+ });
141
+ };
142
+
143
+ const clearSessionHistory = (key: string) => {
144
+ updateSession.mutate({ key, data: { clearHistory: true } });
145
+ if (selectedKey === key) {
146
+ setSelectedKey(null);
147
+ }
148
+ };
149
+
150
+ const deleteSessionByKey = (key: string) => {
151
+ const confirmed = window.confirm(`确认删除会话 ${key} ?`);
152
+ if (!confirmed) {
153
+ return;
154
+ }
155
+ deleteSession.mutate(
156
+ { key },
157
+ {
158
+ onSuccess: () => {
159
+ if (selectedKey === key) {
160
+ setSelectedKey(null);
161
+ }
162
+ }
163
+ }
164
+ );
165
+ };
166
+
167
+ return (
168
+ <div className="space-y-6 pb-20 animate-fade-in">
169
+ <div>
170
+ <h2 className="text-2xl font-bold text-gray-900">Sessions</h2>
171
+ <p className="text-sm text-gray-500 mt-1">管理会话:筛选、查看历史、改标签/偏好模型、清空和删除。</p>
172
+ </div>
173
+
174
+ <Card>
175
+ <CardHeader>
176
+ <CardTitle>Filters</CardTitle>
177
+ <CardDescription>按关键词与活跃窗口筛选会话。</CardDescription>
178
+ </CardHeader>
179
+ <CardContent className="grid grid-cols-1 md:grid-cols-4 gap-3">
180
+ <div className="md:col-span-2 relative">
181
+ <Search className="h-4 w-4 absolute left-3 top-3 text-gray-400" />
182
+ <Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="搜索 key 或标签" className="pl-9" />
183
+ </div>
184
+ <Input type="number" min={0} value={activeMinutes} onChange={(event) => setActiveMinutes(Math.max(0, Number.parseInt(event.target.value, 10) || 0))} placeholder="活跃分钟(0=不限)" />
185
+ <div className="flex gap-2">
186
+ <Input type="number" min={1} value={limit} onChange={(event) => setLimit(Math.max(1, Number.parseInt(event.target.value, 10) || 1))} placeholder="Limit" />
187
+ <Button type="button" variant="outline" onClick={() => sessionsQuery.refetch()}>
188
+ <RefreshCw className="h-4 w-4" />
189
+ </Button>
190
+ </div>
191
+ </CardContent>
192
+ </Card>
193
+
194
+ <Card>
195
+ <CardHeader>
196
+ <CardTitle>Session List</CardTitle>
197
+ <CardDescription>共 {sessionsQuery.data?.total ?? 0} 条;当前展示 {sessions.length} 条。</CardDescription>
198
+ </CardHeader>
199
+ <CardContent className="space-y-3">
200
+ {sessionsQuery.isLoading ? <div className="text-sm text-gray-500">Loading sessions...</div> : null}
201
+ {sessionsQuery.error ? <div className="text-sm text-red-600">{(sessionsQuery.error as Error).message}</div> : null}
202
+ {!sessionsQuery.isLoading && !sessions.length ? <div className="text-sm text-gray-500">暂无会话。</div> : null}
203
+
204
+ {sessions.map((session) => (
205
+ <SessionRow
206
+ key={session.key}
207
+ session={session}
208
+ isSelected={selectedKey === session.key}
209
+ labelValue={labelDraft[session.key] ?? ''}
210
+ modelValue={modelDraft[session.key] ?? ''}
211
+ onToggleHistory={() => setSelectedKey((prev) => (prev === session.key ? null : session.key))}
212
+ onLabelChange={(value) => setLabelDraft((prev) => ({ ...prev, [session.key]: value }))}
213
+ onModelChange={(value) => setModelDraft((prev) => ({ ...prev, [session.key]: value }))}
214
+ onSave={() => saveSessionMeta(session.key)}
215
+ onClear={() => clearSessionHistory(session.key)}
216
+ onDelete={() => deleteSessionByKey(session.key)}
217
+ />
218
+ ))}
219
+ </CardContent>
220
+ </Card>
221
+
222
+ {selectedKey ? (
223
+ <Card>
224
+ <CardHeader>
225
+ <CardTitle>History: {selectedKey}</CardTitle>
226
+ <CardDescription>最近 200 条消息(展示窗口)。</CardDescription>
227
+ </CardHeader>
228
+ <CardContent className="space-y-2">
229
+ {historyQuery.isLoading ? <div className="text-sm text-gray-500">Loading history...</div> : null}
230
+ {historyQuery.error ? <div className="text-sm text-red-600">{(historyQuery.error as Error).message}</div> : null}
231
+ {historyQuery.data ? (
232
+ <div className="text-xs text-gray-500">Total: {historyQuery.data.totalMessages} · metadata: {JSON.stringify(historyQuery.data.metadata ?? {})}</div>
233
+ ) : null}
234
+ <div className="max-h-[480px] overflow-auto space-y-2">
235
+ {(historyQuery.data?.messages ?? []).map((message, index) => (
236
+ <SessionMessageItem key={`${message.timestamp}-${index}`} message={message} index={index} />
237
+ ))}
238
+ </div>
239
+ </CardContent>
240
+ </Card>
241
+ ) : null}
242
+
243
+ {(updateSession.isPending || deleteSession.isPending) && <div className="text-xs text-gray-500">Applying session changes...</div>}
244
+ </div>
245
+ );
246
+ }
@@ -1,6 +1,6 @@
1
1
  import { useUiStore } from '@/stores/ui.store';
2
2
  import { cn } from '@/lib/utils';
3
- import { Cpu, GitBranch, MessageSquare, Sparkles } from 'lucide-react';
3
+ import { Cpu, GitBranch, History, MessageSquare, Sparkles } from 'lucide-react';
4
4
 
5
5
  const navItems = [
6
6
  {
@@ -22,6 +22,11 @@ const navItems = [
22
22
  id: 'runtime' as const,
23
23
  label: 'Routing & Runtime',
24
24
  icon: GitBranch,
25
+ },
26
+ {
27
+ id: 'sessions' as const,
28
+ label: 'Sessions',
29
+ icon: History,
25
30
  }
26
31
  ];
27
32