@illuma-ai/code-sandbox 1.2.1 → 1.3.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.
@@ -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: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:Menlo,Monaco,Consolas,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}.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}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.right-4{right:1rem}.top-0{top:0}.top-4{top:1rem}.z-0{z-index:0}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-mb-\[1px\]{margin-bottom:-1px}.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}.mb-8{margin-bottom:2rem}.ml-0\.5{margin-left:.125rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.line-clamp-1{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-0\.5{height:.125rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-48{height:12rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0px}.min-h-\[36px\]{min-height:36px}.min-h-\[calc\(100vh-56px\)\]{min-height:calc(100vh - 56px)}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-full{width:100%}.w-screen{width:100vw}.min-w-0{min-width:0px}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-\[120px\]{max-width:120px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.transform{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 bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s 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}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,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-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-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-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))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overscroll-contain{overscroll-behavior:contain}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-2{border-top-width:2px}.border-none{border-style:none}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-sb-accent{border-color:var(--sb-accent, #007acc)}.border-sb-border{border-color:var(--sb-border, #3c3c3c)}.border-t-sb-accent{border-top-color:var(--sb-accent, #007acc)}.border-t-transparent{border-top-color:transparent}.bg-amber-100{--tw-bg-opacity: 1;background-color:rgb(254 243 199 / var(--tw-bg-opacity, 1))}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-amber-700{--tw-bg-opacity: 1;background-color:rgb(180 83 9 / var(--tw-bg-opacity, 1))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-700{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.bg-emerald-100{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity, 1))}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.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-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-300{--tw-bg-opacity: 1;background-color:rgb(209 213 219 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-purple-100{--tw-bg-opacity: 1;background-color:rgb(243 232 255 / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-sb-accent{background-color:var(--sb-accent, #007acc)}.bg-sb-bg{background-color:var(--sb-bg, #1e1e1e)}.bg-sb-bg-active{background-color:var(--sb-bg-active, #37373d)}.bg-sb-bg-alt{background-color:var(--sb-bg-alt, #252526)}.bg-sb-editor{background-color:var(--sb-editor, #1e1e1e)}.bg-sb-sidebar{background-color:var(--sb-sidebar, #252526)}.bg-sb-success{background-color:var(--sb-success, #4ec9b0)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-purple-500{--tw-gradient-to: #a855f7 var(--tw-gradient-to-position)}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.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-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-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-20{padding-top:5rem;padding-bottom:5rem}.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}.py-\[3px\]{padding-top:3px;padding-bottom:3px}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[9px\]{font-size:9px}.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}.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-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity, 1))}.text-emerald-700{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / 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-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-purple-700{--tw-text-opacity: 1;color:rgb(126 34 206 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.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-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-sb-text{color:var(--sb-text, #cccccc)}.text-sb-text-active{color:var(--sb-text-active, #ffffff)}.text-sb-text-muted{color:var(--sb-text-muted, #858585)}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.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)}.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{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-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-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:root{--sb-bg: #1e1e1e;--sb-bg-alt: #252526;--sb-bg-hover: #2a2d2e;--sb-bg-active: #37373d;--sb-sidebar: #252526;--sb-editor: #1e1e1e;--sb-terminal: #0d1117;--sb-terminal-header: #161b22;--sb-preview: #ffffff;--sb-border: #3c3c3c;--sb-text: #cccccc;--sb-text-muted: #858585;--sb-text-active: #ffffff;--sb-accent: #007acc;--sb-accent-hover: #1a8ad4;--sb-success: #3fb950;--sb-warning: #d29922;--sb-error: #f85149;--sb-info: #58a6ff;--sb-cyan: #56d4dd;--sb-magenta: #bc8cff;--sb-yellow: #e3b341;--sb-scrollbar-thumb: rgba(255, 255, 255, .12);--sb-scrollbar-thumb-hover: rgba(255, 255, 255, .25);--sb-scrollbar-track: transparent;--sb-scrollbar-width: 6px}.sb-root ::-webkit-scrollbar{width:var(--sb-scrollbar-width);height:var(--sb-scrollbar-width)}.sb-root ::-webkit-scrollbar-track{background:var(--sb-scrollbar-track)}.sb-root ::-webkit-scrollbar-thumb{background:var(--sb-scrollbar-thumb);border-radius:999px}.sb-root ::-webkit-scrollbar-thumb:hover{background:var(--sb-scrollbar-thumb-hover)}.sb-root ::-webkit-scrollbar-corner{background:transparent}.sb-root *{scrollbar-width:thin;scrollbar-color:var(--sb-scrollbar-thumb) var(--sb-scrollbar-track)}.sb-terminal-output{font-family:Cascadia Code,JetBrains Mono,Fira Code,Consolas,Monaco,Courier New,monospace;font-size:12.5px;line-height:1.6;letter-spacing:.01em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--text-primary: #1a1a1a;--text-secondary: #565869;--text-tertiary: #8e8ea0;--surface-primary: #ffffff;--surface-secondary: #f9fafb;--surface-tertiary: #f3f4f6;--border-light: #e5e7eb;--border-medium: #d1d5db;--border-heavy: #9ca3af;--background: 0 0% 100%;--foreground: 0 0% 3.9%;--card: 0 0% 100%;--card-foreground: 0 0% 3.9%;--primary: 0 0% 9%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96.1%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96.1%;--muted-foreground: 0 0% 45.1%;--accent: 0 0% 96.1%;--accent-foreground: 0 0% 9%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 98%;--border: 0 0% 89.8%;--input: 0 0% 89.8%;--ring: 0 0% 3.9%;--radius: .5rem;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%}.dark{--text-primary: #f3f4f6;--text-secondary: #d1d5db;--text-tertiary: #6b7280;--surface-primary: #111827;--surface-secondary: #1f2937;--surface-tertiary: #374151;--border-light: #3a3a3b;--border-medium: #4b5563;--border-heavy: #6b7280;--background: 0 0% 7%;--foreground: 0 0% 98%;--card: 0 0% 3.9%;--card-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 9%;--secondary: 0 0% 14.9%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 14.9%;--muted-foreground: 0 0% 63.9%;--accent: 0 0% 14.9%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 40.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14.9%;--input: 0 0% 14.9%;--ring: 0 0% 83.1%;--chart-1: 220 70% 50%;--chart-2: 160 60% 45%;--chart-3: 30 80% 55%;--chart-4: 280 65% 60%;--chart-5: 340 75% 55%}.hover\:bg-amber-600:hover{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-600:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.hover\:bg-sb-bg-hover:hover{background-color:var(--sb-bg-hover, #2a2d2e)}.hover\:text-blue-700:hover{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.hover\:text-red-600:hover{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.hover\:text-sb-text:hover{color:var(--sb-text, #cccccc)}.hover\:text-sb-text-active:hover{color:var(--sb-text-active, #ffffff)}.focus\:border-transparent:focus{border-color:transparent}.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)}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}@media(min-width:640px){.sm\:block{display:block}.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:text-5xl{font-size:3rem;line-height:1}}@media(min-width:1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}:root{--separator-border: rgba(128, 128, 128, .35);--sash-hover-transition-duration: .1s}.allotment-module_splitView__L-yRc{height:100%;overflow:hidden;position:relative;width:100%}.allotment-module_splitView__L-yRc>.allotment-module_sashContainer__fzwJF{height:100%;pointer-events:none;position:absolute;width:100%}.allotment-module_splitView__L-yRc>.allotment-module_sashContainer__fzwJF>.allotment-module_sash__QA-2t{pointer-events:auto}.allotment-module_splitView__L-yRc>.allotment-module_splitViewContainer__rQnVa{height:100%;position:relative;white-space:nowrap;width:100%}.allotment-module_splitView__L-yRc>.allotment-module_splitViewContainer__rQnVa>.allotment-module_splitViewView__MGZ6O{overflow:hidden;position:absolute;white-space:initial}.allotment-module_splitView__L-yRc.allotment-module_vertical__WSwwa>.allotment-module_splitViewContainer__rQnVa>.allotment-module_splitViewView__MGZ6O{width:100%}.allotment-module_splitView__L-yRc.allotment-module_horizontal__7doS8>.allotment-module_splitViewContainer__rQnVa>.allotment-module_splitViewView__MGZ6O{height:100%}.allotment-module_splitView__L-yRc.allotment-module_separatorBorder__x-rDS>.allotment-module_splitViewContainer__rQnVa>.allotment-module_splitViewView__MGZ6O:not(:first-child):before{background-color:var(--separator-border);content:" ";left:0;pointer-events:none;position:absolute;top:0;z-index:5}.allotment-module_splitView__L-yRc.allotment-module_separatorBorder__x-rDS.allotment-module_vertical__WSwwa>.allotment-module_splitViewContainer__rQnVa>.allotment-module_splitViewView__MGZ6O:not(:first-child):before{height:1px;width:100%}.allotment-module_splitView__L-yRc.allotment-module_separatorBorder__x-rDS.allotment-module_horizontal__7doS8>.allotment-module_splitViewContainer__rQnVa>.allotment-module_splitViewView__MGZ6O:not(:first-child):before{height:100%;width:1px}:root{--focus-border: #007fd4;--sash-size: 8px;--sash-hover-size: 4px}.sash-module_sash__K-9lB{position:absolute;z-index:35;touch-action:none;pointer-events:auto;text-align:initial}.sash-module_sash__K-9lB.sash-module_disabled__Hm-wx{pointer-events:none}.sash-module_sash__K-9lB.sash-module_mac__Jf6OJ.sash-module_vertical__pB-rs{cursor:col-resize}.sash-module_sash__K-9lB.sash-module_vertical__pB-rs.sash-module_minimum__-UKxp{cursor:e-resize}.sash-module_sash__K-9lB.sash-module_vertical__pB-rs.sash-module_maximum__TCWxD{cursor:w-resize}.sash-module_sash__K-9lB.sash-module_mac__Jf6OJ.sash-module_horizontal__kFbiw{cursor:row-resize}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw.sash-module_minimum__-UKxp{cursor:s-resize}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw.sash-module_maximum__TCWxD{cursor:n-resize}.sash-module_sash__K-9lB.sash-module_disabled__Hm-wx{cursor:default!important;pointer-events:none!important}.sash-module_sash__K-9lB.sash-module_vertical__pB-rs{cursor:ew-resize;top:0;width:var(--sash-size);height:100%}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw{cursor:ns-resize;left:0;width:100%;height:var(--sash-size)}.sash-module_sash__K-9lB:not(.sash-module_disabled__Hm-wx)>.sash-module_orthogonal-drag-handle__Yii2-{content:" ";height:calc(var(--sash-size) * 2);width:calc(var(--sash-size) * 2);z-index:100;display:block;cursor:all-scroll;position:absolute}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw.sash-module_orthogonal-edge-north__f7Noe:not(.sash-module_disabled__Hm-wx)>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_start__uZEDk,.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw.sash-module_orthogonal-edge-south__6ZrFC:not(.sash-module_disabled__Hm-wx)>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_end__0TP-R{cursor:nwse-resize}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw.sash-module_orthogonal-edge-north__f7Noe:not(.sash-module_disabled__Hm-wx)>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_end__0TP-R,.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw.sash-module_orthogonal-edge-south__6ZrFC:not(.sash-module_disabled__Hm-wx)>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_start__uZEDk{cursor:nesw-resize}.sash-module_sash__K-9lB.sash-module_vertical__pB-rs>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_start__uZEDk{left:calc(var(--sash-size) * -.5);top:calc(var(--sash-size) * -1)}.sash-module_sash__K-9lB.sash-module_vertical__pB-rs>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_end__0TP-R{left:calc(var(--sash-size) * -.5);bottom:calc(var(--sash-size) * -1)}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_start__uZEDk{top:calc(var(--sash-size) * -.5);left:calc(var(--sash-size) * -1)}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw>.sash-module_orthogonal-drag-handle__Yii2-.sash-module_end__0TP-R{top:calc(var(--sash-size) * -.5);right:calc(var(--sash-size) * -1)}.sash-module_sash__K-9lB:before{content:"";pointer-events:none;position:absolute;width:100%;height:100%;transition:background-color var(--sash-hover-transition-duration) ease-out;background:transparent}.sash-module_sash__K-9lB.sash-module_vertical__pB-rs:before{width:var(--sash-hover-size);left:calc(50% - (var(--sash-hover-size) / 2))}.sash-module_sash__K-9lB.sash-module_horizontal__kFbiw:before{height:var(--sash-hover-size);top:calc(50% - (var(--sash-hover-size) / 2))}.sash-module_sash__K-9lB.sash-module_hover__80W6I:before,.sash-module_sash__K-9lB.sash-module_active__bJspD:before{background:var(--focus-border)}
package/dist/types.d.ts CHANGED
@@ -282,6 +282,10 @@ export interface CodeEditorProps {
282
282
  export interface TerminalProps {
283
283
  output: string[];
284
284
  className?: string;
285
+ /** Whether the terminal is collapsed to just its header bar */
286
+ minimized?: boolean;
287
+ /** Called when the user clicks the minimize/expand toggle */
288
+ onToggleMinimize?: () => void;
285
289
  }
286
290
  export interface PreviewProps {
287
291
  url: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@illuma-ai/code-sandbox",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Illuma AI (https://github.com/illuma-ai)",
@@ -163,59 +163,389 @@ function FolderIcon({ open }: { open: boolean }) {
163
163
  );
164
164
  }
165
165
 
166
- /** Get file icon color based on extension */
167
- function getFileIconColor(name: string): string {
168
- const ext = name.split(".").pop()?.toLowerCase() || "";
169
- const colorMap: Record<string, string> = {
170
- js: "#f0db4f",
171
- jsx: "#61dafb",
172
- ts: "#3178c6",
173
- tsx: "#61dafb",
174
- json: "#a8b065",
175
- html: "#e44d26",
176
- css: "#264de4",
177
- scss: "#cd6799",
178
- md: "#ffffff",
179
- py: "#3776ab",
180
- rb: "#cc342d",
181
- go: "#00add8",
182
- rs: "#dea584",
183
- yml: "#cb171e",
184
- yaml: "#cb171e",
185
- env: "#ecd53f",
186
- sh: "#89e051",
187
- sql: "#e38c00",
188
- svg: "#ffb13b",
189
- };
190
- return colorMap[ext] || "#8c8c8c";
191
- }
166
+ // ---------------------------------------------------------------------------
167
+ // Per-extension file icons — compact 16x16 SVGs with distinct shapes/labels.
168
+ // Inspired by VS Code's Seti/Material icon themes.
169
+ // ---------------------------------------------------------------------------
192
170
 
193
- /** Generic file icon with extension-based color */
194
- function FileIcon({ name }: { name: string }) {
195
- const color = getFileIconColor(name);
171
+ /** SVG wrapper common props for all file icons */
172
+ function IconSvg({
173
+ children,
174
+ className = "",
175
+ }: {
176
+ children: React.ReactNode;
177
+ className?: string;
178
+ }) {
196
179
  return (
197
180
  <svg
198
181
  width="16"
199
182
  height="16"
200
183
  viewBox="0 0 16 16"
201
184
  fill="none"
202
- className="shrink-0"
185
+ className={`shrink-0 ${className}`}
203
186
  >
187
+ {children}
188
+ </svg>
189
+ );
190
+ }
191
+
192
+ /** Base document shape used by most file icons */
193
+ function DocShape({ color }: { color: string }) {
194
+ return (
195
+ <>
204
196
  <path
205
197
  d="M4 1.5h5.5L13 5v9.5a1 1 0 01-1 1H4a1 1 0 01-1-1v-13a1 1 0 011-1z"
206
198
  fill={color}
207
- opacity="0.2"
199
+ opacity="0.15"
208
200
  />
209
201
  <path
210
202
  d="M4 1.5h5.5L13 5v9.5a1 1 0 01-1 1H4a1 1 0 01-1-1v-13a1 1 0 011-1z"
211
203
  stroke={color}
212
- strokeWidth="1"
204
+ strokeWidth="0.8"
213
205
  />
214
- <path d="M9.5 1.5V5H13" stroke={color} strokeWidth="1" />
215
- </svg>
206
+ <path d="M9.5 1.5V5H13" stroke={color} strokeWidth="0.8" />
207
+ </>
208
+ );
209
+ }
210
+
211
+ /** Label text centered on the document icon */
212
+ function DocLabel({
213
+ label,
214
+ color,
215
+ y = "11.5",
216
+ fontSize = "5",
217
+ }: {
218
+ label: string;
219
+ color: string;
220
+ y?: string;
221
+ fontSize?: string;
222
+ }) {
223
+ return (
224
+ <text
225
+ x="8"
226
+ y={y}
227
+ textAnchor="middle"
228
+ fill={color}
229
+ fontSize={fontSize}
230
+ fontWeight="700"
231
+ fontFamily="Arial, sans-serif"
232
+ >
233
+ {label}
234
+ </text>
216
235
  );
217
236
  }
218
237
 
238
+ /** JavaScript (.js) */
239
+ function JsIcon() {
240
+ return (
241
+ <IconSvg>
242
+ <DocShape color="#f0db4f" />
243
+ <DocLabel label="JS" color="#f0db4f" />
244
+ </IconSvg>
245
+ );
246
+ }
247
+
248
+ /** JSX (.jsx) */
249
+ function JsxIcon() {
250
+ return (
251
+ <IconSvg>
252
+ <DocShape color="#61dafb" />
253
+ <DocLabel label="JSX" color="#61dafb" fontSize="4.5" />
254
+ </IconSvg>
255
+ );
256
+ }
257
+
258
+ /** TypeScript (.ts) */
259
+ function TsIcon() {
260
+ return (
261
+ <IconSvg>
262
+ <DocShape color="#3178c6" />
263
+ <DocLabel label="TS" color="#3178c6" />
264
+ </IconSvg>
265
+ );
266
+ }
267
+
268
+ /** TSX (.tsx) */
269
+ function TsxIcon() {
270
+ return (
271
+ <IconSvg>
272
+ <DocShape color="#3178c6" />
273
+ <DocLabel label="TSX" color="#61dafb" fontSize="4.5" />
274
+ </IconSvg>
275
+ );
276
+ }
277
+
278
+ /** JSON (.json) */
279
+ function JsonIcon() {
280
+ return (
281
+ <IconSvg>
282
+ <DocShape color="#a8b065" />
283
+ <text
284
+ x="8"
285
+ y="11"
286
+ textAnchor="middle"
287
+ fill="#a8b065"
288
+ fontSize="7"
289
+ fontWeight="700"
290
+ fontFamily="Arial, sans-serif"
291
+ >
292
+ {"{}"}
293
+ </text>
294
+ </IconSvg>
295
+ );
296
+ }
297
+
298
+ /** HTML (.html) */
299
+ function HtmlIcon() {
300
+ return (
301
+ <IconSvg>
302
+ <DocShape color="#e44d26" />
303
+ <text
304
+ x="8"
305
+ y="11.5"
306
+ textAnchor="middle"
307
+ fill="#e44d26"
308
+ fontSize="4.5"
309
+ fontWeight="700"
310
+ fontFamily="Arial, sans-serif"
311
+ >
312
+ {"</>"}
313
+ </text>
314
+ </IconSvg>
315
+ );
316
+ }
317
+
318
+ /** CSS (.css) */
319
+ function CssIcon() {
320
+ return (
321
+ <IconSvg>
322
+ <DocShape color="#264de4" />
323
+ <DocLabel label="#" color="#264de4" fontSize="7" />
324
+ </IconSvg>
325
+ );
326
+ }
327
+
328
+ /** SCSS (.scss) */
329
+ function ScssIcon() {
330
+ return (
331
+ <IconSvg>
332
+ <DocShape color="#cd6799" />
333
+ <DocLabel label="S" color="#cd6799" fontSize="7" />
334
+ </IconSvg>
335
+ );
336
+ }
337
+
338
+ /** Markdown (.md) */
339
+ function MdIcon() {
340
+ return (
341
+ <IconSvg>
342
+ <DocShape color="#9da5b4" />
343
+ <DocLabel label="M↓" color="#9da5b4" fontSize="4.5" />
344
+ </IconSvg>
345
+ );
346
+ }
347
+
348
+ /** Python (.py) */
349
+ function PyIcon() {
350
+ return (
351
+ <IconSvg>
352
+ <DocShape color="#3776ab" />
353
+ <DocLabel label="PY" color="#3776ab" />
354
+ </IconSvg>
355
+ );
356
+ }
357
+
358
+ /** Ruby (.rb) */
359
+ function RbIcon() {
360
+ return (
361
+ <IconSvg>
362
+ <DocShape color="#cc342d" />
363
+ <text
364
+ x="8"
365
+ y="11.5"
366
+ textAnchor="middle"
367
+ fill="#cc342d"
368
+ fontSize="6"
369
+ fontWeight="700"
370
+ fontFamily="Arial, sans-serif"
371
+ >
372
+ {"◆"}
373
+ </text>
374
+ </IconSvg>
375
+ );
376
+ }
377
+
378
+ /** Go (.go) */
379
+ function GoIcon() {
380
+ return (
381
+ <IconSvg>
382
+ <DocShape color="#00add8" />
383
+ <DocLabel label="GO" color="#00add8" />
384
+ </IconSvg>
385
+ );
386
+ }
387
+
388
+ /** Rust (.rs) */
389
+ function RsIcon() {
390
+ return (
391
+ <IconSvg>
392
+ <DocShape color="#dea584" />
393
+ <DocLabel label="RS" color="#dea584" />
394
+ </IconSvg>
395
+ );
396
+ }
397
+
398
+ /** YAML (.yml, .yaml) */
399
+ function YmlIcon() {
400
+ return (
401
+ <IconSvg>
402
+ <DocShape color="#cb171e" />
403
+ <DocLabel label="YML" color="#cb171e" fontSize="4" />
404
+ </IconSvg>
405
+ );
406
+ }
407
+
408
+ /** Env (.env) */
409
+ function EnvIcon() {
410
+ return (
411
+ <IconSvg>
412
+ <DocShape color="#ecd53f" />
413
+ <text
414
+ x="8"
415
+ y="11.5"
416
+ textAnchor="middle"
417
+ fill="#ecd53f"
418
+ fontSize="6"
419
+ fontWeight="700"
420
+ fontFamily="Arial, sans-serif"
421
+ >
422
+ {"⚙"}
423
+ </text>
424
+ </IconSvg>
425
+ );
426
+ }
427
+
428
+ /** Shell (.sh, .bash) */
429
+ function ShIcon() {
430
+ return (
431
+ <IconSvg>
432
+ <DocShape color="#89e051" />
433
+ <DocLabel label="$_" color="#89e051" fontSize="5" />
434
+ </IconSvg>
435
+ );
436
+ }
437
+
438
+ /** SQL (.sql) */
439
+ function SqlIcon() {
440
+ return (
441
+ <IconSvg>
442
+ <DocShape color="#e38c00" />
443
+ <DocLabel label="SQL" color="#e38c00" fontSize="4" />
444
+ </IconSvg>
445
+ );
446
+ }
447
+
448
+ /** SVG (.svg) */
449
+ function SvgIcon() {
450
+ return (
451
+ <IconSvg>
452
+ <DocShape color="#ffb13b" />
453
+ <DocLabel label="SVG" color="#ffb13b" fontSize="4" />
454
+ </IconSvg>
455
+ );
456
+ }
457
+
458
+ /** Lock files (package-lock.json, yarn.lock, etc.) */
459
+ function LockIcon() {
460
+ return (
461
+ <IconSvg>
462
+ <DocShape color="#6b7280" />
463
+ <text
464
+ x="8"
465
+ y="11.5"
466
+ textAnchor="middle"
467
+ fill="#6b7280"
468
+ fontSize="6"
469
+ fontWeight="700"
470
+ fontFamily="Arial, sans-serif"
471
+ >
472
+ {"🔒"}
473
+ </text>
474
+ </IconSvg>
475
+ );
476
+ }
477
+
478
+ /** Generic fallback file icon */
479
+ function DefaultFileIcon() {
480
+ return (
481
+ <IconSvg>
482
+ <DocShape color="#8c8c8c" />
483
+ </IconSvg>
484
+ );
485
+ }
486
+
487
+ /**
488
+ * Map of file extensions to their icon components.
489
+ * Falls back to DefaultFileIcon for unrecognized extensions.
490
+ */
491
+ const ICON_BY_EXT: Record<string, React.FC> = {
492
+ js: JsIcon,
493
+ mjs: JsIcon,
494
+ cjs: JsIcon,
495
+ jsx: JsxIcon,
496
+ ts: TsIcon,
497
+ mts: TsIcon,
498
+ cts: TsIcon,
499
+ tsx: TsxIcon,
500
+ json: JsonIcon,
501
+ html: HtmlIcon,
502
+ htm: HtmlIcon,
503
+ css: CssIcon,
504
+ scss: ScssIcon,
505
+ sass: ScssIcon,
506
+ less: ScssIcon,
507
+ md: MdIcon,
508
+ mdx: MdIcon,
509
+ py: PyIcon,
510
+ rb: RbIcon,
511
+ go: GoIcon,
512
+ rs: RsIcon,
513
+ yml: YmlIcon,
514
+ yaml: YmlIcon,
515
+ env: EnvIcon,
516
+ sh: ShIcon,
517
+ bash: ShIcon,
518
+ zsh: ShIcon,
519
+ sql: SqlIcon,
520
+ svg: SvgIcon,
521
+ lock: LockIcon,
522
+ };
523
+
524
+ /**
525
+ * Special filename overrides — some files are identified by their full name
526
+ * rather than extension (e.g., Dockerfile, .gitignore).
527
+ */
528
+ const ICON_BY_NAME: Record<string, React.FC> = {
529
+ "package-lock.json": LockIcon,
530
+ "yarn.lock": LockIcon,
531
+ "pnpm-lock.yaml": LockIcon,
532
+ };
533
+
534
+ /** Resolve the correct icon component for a filename */
535
+ function FileIcon({ name }: { name: string }) {
536
+ // Check full filename first (for special cases)
537
+ const lowerName = name.toLowerCase();
538
+ const ByName = ICON_BY_NAME[lowerName];
539
+ if (ByName) return <ByName />;
540
+
541
+ // Then check by extension
542
+ const ext = lowerName.split(".").pop() || "";
543
+ // Handle dotfiles like .env, .gitignore — use the part after the dot
544
+ const dotfileExt = lowerName.startsWith(".") ? lowerName.slice(1) : "";
545
+ const Icon = ICON_BY_EXT[ext] || ICON_BY_EXT[dotfileExt] || DefaultFileIcon;
546
+ return <Icon />;
547
+ }
548
+
219
549
  // ---------------------------------------------------------------------------
220
550
  // Components
221
551
  // ---------------------------------------------------------------------------
@@ -231,7 +561,7 @@ export function FileTree({
231
561
  fileChanges,
232
562
  }: FileTreeProps) {
233
563
  return (
234
- <div className="h-full overflow-auto bg-sb-sidebar text-sm select-none">
564
+ <div className="h-full overflow-auto bg-sb-sidebar text-sm select-none overscroll-contain">
235
565
  <div className="px-3 py-2 text-[11px] font-semibold text-sb-text-muted uppercase tracking-wider border-b border-sb-border">
236
566
  Explorer
237
567
  </div>
@@ -1,68 +1,111 @@
1
1
  /**
2
- * Terminal — Display terminal output from Nodepod processes.
2
+ * Terminal — Clean monochrome terminal output panel with minimize toggle.
3
3
  *
4
- * Simple scrolling output panel styled like a terminal.
5
- * Auto-scrolls to the bottom as new output arrives.
4
+ * Uses a simple div-based approach (not xterm.js) to keep the bundle lean.
5
+ * Auto-scrolls to bottom as new output arrives.
6
+ * Minimize/expand state is controlled by the parent via props.
6
7
  */
7
8
 
8
9
  import React, { useEffect, useRef } from "react";
9
10
  import type { TerminalProps } from "../types";
10
11
 
11
12
  /**
12
- * Terminal component — renders process stdout/stderr in a terminal-style panel.
13
+ * Terminal component — renders process output in a clean monochrome panel.
13
14
  *
14
- * Uses a simple div-based approach (not xterm.js) for the initial version.
15
- * This keeps the bundle small and avoids the xterm peer dependency
16
- * for consumers who don't need full terminal emulation.
15
+ * Features:
16
+ * - Monospace font with proper line spacing
17
+ * - Auto-scroll to bottom on new output
18
+ * - Minimize/expand toggle via chevron in header (parent-controlled)
19
+ * - Modern thin scrollbar matching the sandbox theme
17
20
  */
18
- export function Terminal({ output, className = "" }: TerminalProps) {
21
+ export function Terminal({
22
+ output,
23
+ className = "",
24
+ minimized = false,
25
+ onToggleMinimize,
26
+ }: TerminalProps) {
19
27
  const containerRef = useRef<HTMLDivElement>(null);
20
28
 
21
29
  // Auto-scroll to bottom when new output arrives
22
30
  useEffect(() => {
23
31
  const el = containerRef.current;
24
- if (el) {
32
+ if (el && !minimized) {
25
33
  el.scrollTop = el.scrollHeight;
26
34
  }
27
- }, [output.length]);
35
+ }, [output.length, minimized]);
28
36
 
29
37
  return (
30
- <div className={`flex flex-col h-full bg-sb-terminal ${className}`}>
38
+ <div
39
+ className={`flex flex-col ${minimized ? "" : "h-full"} ${className}`}
40
+ style={{ background: "var(--sb-terminal)" }}
41
+ >
31
42
  {/* Header */}
32
- <div className="flex items-center px-3 py-1 bg-sb-bg-alt border-b border-sb-border">
33
- <span className="text-xs font-medium text-sb-text-muted uppercase tracking-wider">
34
- Terminal
35
- </span>
36
- </div>
37
-
38
- {/* Output */}
39
43
  <div
40
- ref={containerRef}
41
- className="flex-1 overflow-auto p-3 font-mono text-xs leading-relaxed"
44
+ className="flex items-center gap-2 px-3 py-1 border-t border-sb-border shrink-0 select-none cursor-pointer"
45
+ style={{ background: "var(--sb-terminal-header)" }}
46
+ onClick={onToggleMinimize}
42
47
  >
43
- {output.length === 0 ? (
44
- <span className="text-sb-text-muted">Waiting for output...</span>
45
- ) : (
46
- output.map((line, i) => (
47
- <div
48
- key={i}
49
- className={`whitespace-pre-wrap ${
50
- line.startsWith("[stderr]")
51
- ? "text-sb-error"
52
- : line.startsWith("$")
53
- ? "text-sb-success"
54
- : line.startsWith(" ✓")
55
- ? "text-sb-success"
56
- : line.startsWith(" ✗")
57
- ? "text-sb-error"
58
- : "text-sb-text"
59
- }`}
60
- >
61
- {line}
62
- </div>
63
- ))
48
+ {/* Chevron toggle */}
49
+ <svg
50
+ width="14"
51
+ height="14"
52
+ viewBox="0 0 16 16"
53
+ fill="none"
54
+ className="shrink-0 transition-transform duration-150"
55
+ style={{
56
+ transform: minimized ? "rotate(-90deg)" : "rotate(0deg)",
57
+ color: "var(--sb-text-muted)",
58
+ }}
59
+ >
60
+ <path
61
+ d="M4 6l4 4 4-4"
62
+ stroke="currentColor"
63
+ strokeWidth="1.5"
64
+ strokeLinecap="round"
65
+ strokeLinejoin="round"
66
+ />
67
+ </svg>
68
+ <span
69
+ className="text-[11px] font-medium tracking-wider"
70
+ style={{ color: "var(--sb-text-muted)" }}
71
+ >
72
+ TERMINAL
73
+ </span>
74
+ {/* Line count badge */}
75
+ {output.length > 0 && (
76
+ <span
77
+ className="text-[10px] ml-auto tabular-nums"
78
+ style={{ color: "var(--sb-text-muted)", opacity: 0.6 }}
79
+ >
80
+ {output.length} lines
81
+ </span>
64
82
  )}
65
83
  </div>
84
+
85
+ {/* Output area — hidden when minimized */}
86
+ {!minimized && (
87
+ <div
88
+ ref={containerRef}
89
+ className="flex-1 overflow-auto px-3.5 py-2 sb-terminal-output overscroll-contain min-h-0"
90
+ >
91
+ {output.length === 0 ? (
92
+ <div style={{ color: "var(--sb-text-muted)" }}>
93
+ <span>$ </span>
94
+ <span className="animate-pulse">_</span>
95
+ </div>
96
+ ) : (
97
+ output.map((line, i) => (
98
+ <div
99
+ key={i}
100
+ className="whitespace-pre-wrap"
101
+ style={{ color: "var(--sb-text)" }}
102
+ >
103
+ {line}
104
+ </div>
105
+ ))
106
+ )}
107
+ </div>
108
+ )}
66
109
  </div>
67
110
  );
68
111
  }