| 1 | /* Ported from DeepSeek Harness c291e7961a (MIT). */ |
| 2 | .dsh-TerminalBlock-block { |
| 3 | --dsl-terminal-radius: 12px; |
| 4 | --dsl-terminal-line-height: 22px; |
| 5 | /* Rebindable by consumers (CodeBlock's --dsl-code-block-content-font |
| 6 | pattern): a surface wanting the smaller code size rebinds this together |
| 7 | with --dsl-terminal-line-height on its own container. */ |
| 8 | --dsl-terminal-font: var(--dsw-font-markdown-code-block); |
| 9 | /* The card's own left inset, holding the run-state dot in a column of its own |
| 10 | so it never competes with the commands for horizontal space. */ |
| 11 | --dsl-terminal-gutter: 30px; |
| 12 | |
| 13 | position: relative; |
| 14 | margin: 16px 0; |
| 15 | /* The gutter is the card's OWN padding, not a margin: every consumer rewrites |
| 16 | `margin` wholesale (each render site sets its own indent), which silently |
| 17 | cancelled the reservation and let the dot fall outside the card into a |
| 18 | container that clips it. Owning the reservation here keeps the invariant |
| 19 | with the component that depends on it. */ |
| 20 | padding-left: var(--dsl-terminal-gutter); |
| 21 | color: var(--dsw-alias-label-primary); |
| 22 | background: var(--dsw-alias-markdown-code-block); |
| 23 | border-radius: var(--dsl-terminal-radius); |
| 24 | /* Clip the banner to the card's own radius: when a consumer adds a border, |
| 25 | the banner's equal corner radius no longer nests inside it and leaves a |
| 26 | notch at the corner. Nothing inside renders out of the box. */ |
| 27 | overflow: hidden; |
| 28 | } |
| 29 | |
| 30 | /* The status pill and copy control top-align to the FIRST prompt row (their |
| 31 | heights are capped to the prompt line, so on a multi-line command they sit |
| 32 | with the first command instead of floating mid-banner). */ |
| 33 | .dsh-TerminalBlock-header { |
| 34 | display: flex; |
| 35 | align-items: flex-start; |
| 36 | gap: 12px; |
| 37 | /* Pulled back across the card's gutter padding so the banner spans the FULL |
| 38 | surface, then re-inset by the same amount so the prompt text and the dot |
| 39 | keep their positions. The banner shares the card's own surface (no banner |
| 40 | token): the l2 divider below is the section boundary. */ |
| 41 | margin-left: calc(-1 * var(--dsl-terminal-gutter)); |
| 42 | padding: 9px 14px 9px var(--dsl-terminal-gutter); |
| 43 | border-top-left-radius: var(--dsl-terminal-radius); |
| 44 | border-top-right-radius: var(--dsl-terminal-radius); |
| 45 | /* A long multi-line command scrolls inside the banner (same cap as the |
| 46 | IN/OUT card's sections) instead of pushing the output off screen. */ |
| 47 | max-height: 150px; |
| 48 | overflow-y: auto; |
| 49 | } |
| 50 | |
| 51 | /* Banner scrollbar floats off the card edge like the output's. */ |
| 52 | .dsh-TerminalBlock-header::-webkit-scrollbar-thumb { |
| 53 | border: 2px solid transparent; |
| 54 | background-clip: padding-box; |
| 55 | border-radius: 6px; |
| 56 | } |
| 57 | |
| 58 | .dsh-TerminalBlock-header::-webkit-scrollbar-track { |
| 59 | margin: 6px; |
| 60 | } |
| 61 | |
| 62 | /* Full-width l2 hairline between the command banner and the body — the same |
| 63 | divider the IN/OUT card draws between its sections. A running card is |
| 64 | banner-only, so it draws none. */ |
| 65 | .dsh-TerminalBlock-block:not([data-running]) .dsh-TerminalBlock-header { |
| 66 | border-bottom: 0.5px solid var(--dsw-alias-border-l2); |
| 67 | } |
| 68 | |
| 69 | /* One row per command line. The prompt column is the only element allowed to |
| 70 | shrink; the status pill and the copy control keep their intrinsic width. */ |
| 71 | .dsh-TerminalBlock-prompt { |
| 72 | display: flex; |
| 73 | flex-direction: column; |
| 74 | min-width: 0; |
| 75 | flex: 1; |
| 76 | font: var(--dsl-terminal-font); |
| 77 | } |
| 78 | |
| 79 | .dsh-TerminalBlock-promptLine { |
| 80 | position: relative; |
| 81 | display: flex; |
| 82 | align-items: baseline; |
| 83 | gap: 8px; |
| 84 | min-width: 0; |
| 85 | line-height: var(--dsl-terminal-line-height); |
| 86 | } |
| 87 | |
| 88 | /* Out of flow inside the card's own gutter padding, so the reservation and the |
| 89 | dot move together and no consumer margin can pull them apart; the dot neither |
| 90 | indents its command nor depends on the command's text metrics to line up. |
| 91 | Centered against the row's line box, not the code font's baseline. */ |
| 92 | .dsh-TerminalBlock-runState { |
| 93 | position: absolute; |
| 94 | left: calc(-1 * var(--dsl-terminal-gutter) + 8px); |
| 95 | top: 50%; |
| 96 | transform: translateY(-50%); |
| 97 | } |
| 98 | |
| 99 | /* The dot is aria-hidden; this is its text label for assistive technology. */ |
| 100 | .dsh-TerminalBlock-runStateLabel { |
| 101 | position: absolute; |
| 102 | width: 1px; |
| 103 | height: 1px; |
| 104 | overflow: hidden; |
| 105 | clip-path: inset(50%); |
| 106 | white-space: nowrap; |
| 107 | } |
| 108 | |
| 109 | .dsh-TerminalBlock-cwd { |
| 110 | flex: none; |
| 111 | color: var(--dsw-alias-label-tertiary); |
| 112 | } |
| 113 | |
| 114 | /* `pre`, not `nowrap`: the prompt row renders the command verbatim, and |
| 115 | `nowrap` collapses the repeated spaces, tabs, and alignment of an indented |
| 116 | continuation. Both hold the single row and the ellipsis. */ |
| 117 | .dsh-TerminalBlock-command { |
| 118 | min-width: 0; |
| 119 | color: var(--dsw-alias-label-primary); |
| 120 | overflow: hidden; |
| 121 | text-overflow: ellipsis; |
| 122 | white-space: pre; |
| 123 | } |
| 124 | |
| 125 | /* Capped to the prompt's line height (Pill's own 24px height would exceed a |
| 126 | smaller-font prompt row and stretch the banner). Sticky against the |
| 127 | banner's own scroll so the pill and the copy control stay in reach while a |
| 128 | long command scrolls underneath. */ |
| 129 | .dsh-TerminalBlock-status { |
| 130 | flex: none; |
| 131 | position: sticky; |
| 132 | top: 0; |
| 133 | height: var(--dsl-terminal-line-height); |
| 134 | color: var(--dsw-alias-state-error-primary); |
| 135 | } |
| 136 | |
| 137 | .dsh-TerminalBlock-copyButton { |
| 138 | flex: none; |
| 139 | position: sticky; |
| 140 | top: 0; |
| 141 | /* Card surface, not transparent: the control is sticky over the banner's |
| 142 | own scroll, so scrolled command text must not bleed through it. */ |
| 143 | background-color: var(--dsw-alias-markdown-code-block); |
| 144 | border: none; |
| 145 | padding: 0; |
| 146 | margin: 0; |
| 147 | color: var(--dsw-alias-label-secondary); |
| 148 | cursor: pointer; |
| 149 | font: var(--dsw-font-xs-13); |
| 150 | line-height: var(--dsl-terminal-line-height); |
| 151 | } |
| 152 | |
| 153 | /* Vertical scrolling lives on the OUTPUT, not the card root: a root scroller |
| 154 | would run its scrollbar over the banner (and the copy control), while here |
| 155 | the banner stays pinned and the bar sits inside the output's right padding. |
| 156 | Unset, the max-height is none and the auto overflow never engages. */ |
| 157 | .dsh-TerminalBlock-output { |
| 158 | max-height: var(--dsl-terminal-output-max-height, none); |
| 159 | padding: 12px 14px 12px 0; |
| 160 | font: var(--dsl-terminal-font); |
| 161 | overflow-x: auto; |
| 162 | overflow-y: auto; |
| 163 | } |
| 164 | |
| 165 | /* Both output scrollbars (vertical cap, horizontal pre overflow) float 2px |
| 166 | off the card edge: a transparent border clips the thumb inward so it never |
| 167 | hugs the rounded corner. */ |
| 168 | .dsh-TerminalBlock-output::-webkit-scrollbar-thumb { |
| 169 | border: 2px solid transparent; |
| 170 | background-clip: padding-box; |
| 171 | border-radius: 6px; |
| 172 | } |
| 173 | |
| 174 | /* Track end-margins keep the thumb's travel out of the card's rounded |
| 175 | corners in both directions. */ |
| 176 | .dsh-TerminalBlock-output::-webkit-scrollbar-track { |
| 177 | margin: 6px; |
| 178 | } |
| 179 | |
| 180 | /* No wrapping, no word-break: alignment is the payload of terminal output. */ |
| 181 | .dsh-TerminalBlock-line { |
| 182 | min-height: var(--dsl-terminal-line-height); |
| 183 | white-space: pre; |
| 184 | } |
| 185 | |
| 186 | .dsh-TerminalBlock-expand { |
| 187 | display: block; |
| 188 | width: 100%; |
| 189 | padding: 0; |
| 190 | border: none; |
| 191 | background-color: transparent; |
| 192 | color: var(--dsw-alias-label-tertiary); |
| 193 | cursor: pointer; |
| 194 | font: inherit; |
| 195 | text-align: left; |
| 196 | } |
| 197 | |
| 198 | .dsh-TerminalBlock-expand:hover { |
| 199 | color: var(--dsw-alias-label-secondary); |
| 200 | } |
| 201 | |
| 202 | .dsh-TerminalBlock-empty { |
| 203 | padding: 12px 14px 12px 0; |
| 204 | font: var(--dsl-terminal-font); |
| 205 | color: var(--dsw-alias-label-tertiary); |
| 206 | } |
| 207 |