mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
* fix(web-shell): keep mobile composer controls reachable above a soft keyboard Real-device testing of the touch composer on Android Chrome with Gboard found three layout problems once the soft keyboard shrinks the viewport: - The workspace/Git row sits inside the composer's height cap, so with the keyboard open the attachments strip collapsed to 16px and attached files could be neither seen nor removed. The cap now includes that row's height. - History search opens upward from the composer with a fixed cap, so its search field and close button slid under the header. The panel is now also limited to the room above the composer, the same way the @ panel is. - The editing row overlapped the workspace/Git row by 8px, so tapping the bottom of a workspace or branch chip recalled a history entry instead. * fix(web-shell): keep mobile history search inside the visible chat area Bound the history-search panel by the real clipping edge above the composer (the chat pane under the header, or a host-declared --web-shell-popover-safe-top) instead of a 48px guess. When not even its 96px minimum fits, the panel moves down over the composer so the search field and close button stay below the header. Re-measurement is coalesced to one pass per animation frame. Measure the workspace/Git row instead of assuming 44px: with a second workspace or a long branch it wraps onto two lines. The cap also adds the 8px pull-up the editing row gives up below the row, and the editing row keeps that pull-up when no workspace row is shown, as on main. A declared zero safe top is now kept as zero; the @ reference panel reads the variable through the same helper. * refactor(web-shell): share the popover floor and fit the history panel imperatively Round-2 review follow-up: - Export POPOVER_MIN_HEIGHT from popoverRoom.ts and use it for both upward-opening popovers instead of two bare 96 literals. - Derive the history panel's room/shift from its rendered height (a no-match panel is just the search bar) and write the two custom properties straight to the panel node, so per-frame composer movement no longer re-renders the whole composer through state. - Observe the panel itself so a change in match count re-measures. - Name the mobile composer cap once as --chat-editor-composer-cap and derive the :has(.mobileContextRow) override from it. - Pin the ResizeObserver re-measure paths with target-tracking stubs, pin auto as a clipping overflow value, and add a 412x360 e2e asserting the workspace row never shrinks the attachments strip. - Qualify the soft-keyboard acceptance criterion with the verified height and document --web-shell-popover-safe-top in the design docs and the web-shell README. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): focus history search after input mounts * test(web-shell): cover history search in short desktop windows --------- Co-authored-by: probe <probe@local> Co-authored-by: Qwen-Coder <[email protected]>
2861 lines
56 KiB
CSS
2861 lines
56 KiB
CSS
.editorShell {
|
||
position: relative;
|
||
container-type: inline-size;
|
||
margin: 0 0 14px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] {
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.editorShellDropdownOpen {
|
||
z-index: 7;
|
||
}
|
||
|
||
.container {
|
||
position: relative;
|
||
container-type: inline-size;
|
||
border-radius: 12px;
|
||
margin: 0;
|
||
cursor: text;
|
||
--chat-editor-min-height: 116px;
|
||
--chat-editor-max-height: min(350px, 40vh);
|
||
--chat-editor-input-min-height: 44px;
|
||
--chat-editor-input-max-height: 300px;
|
||
--chat-editor-attachments-max-height: 136px;
|
||
--dac-glow-on: 0;
|
||
--dac-glow-pulse: 0;
|
||
--dac-g1: #6a78ff;
|
||
--dac-g2: #8a7bff;
|
||
--chat-send-button-disabled-foreground: var(--secondary-foreground);
|
||
--chat-send-button-foreground: #fafafa;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] .container {
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.content {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: flex;
|
||
box-sizing: border-box;
|
||
min-height: var(--chat-editor-min-height, 116px);
|
||
max-height: var(--chat-editor-max-height, min(350px, 40vh));
|
||
flex-direction: column;
|
||
overflow: visible;
|
||
border: 1.5px solid var(--agent-gray-200);
|
||
border-radius: inherit;
|
||
background: var(--chat-editor-bg-primary);
|
||
opacity: 1;
|
||
padding: 12px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] .content {
|
||
padding: 10px;
|
||
}
|
||
|
||
.container:focus-within .content,
|
||
.container[data-at-panel-open] .content {
|
||
border-color: var(--chat-editor-accent-color);
|
||
}
|
||
|
||
@supports (selector(:has(*))) {
|
||
.container:has([aria-expanded='true']) .content {
|
||
border-color: var(--chat-editor-accent-color);
|
||
}
|
||
}
|
||
|
||
.container[data-image-drag-active] .content {
|
||
border-color: var(--primary);
|
||
background: color-mix(
|
||
in srgb,
|
||
var(--primary) 6%,
|
||
var(--chat-editor-bg-primary)
|
||
);
|
||
}
|
||
|
||
.dacAura,
|
||
.dacHalo {
|
||
pointer-events: none;
|
||
}
|
||
|
||
.dacAura {
|
||
position: absolute;
|
||
inset: -50px;
|
||
z-index: 0;
|
||
opacity: calc(var(--dac-glow-on) * (0.33 + var(--dac-glow-pulse) * 0.17));
|
||
-webkit-mask: radial-gradient(
|
||
ellipse 56% 64% at center,
|
||
transparent 28%,
|
||
#000 48%,
|
||
transparent 94%
|
||
);
|
||
mask: radial-gradient(
|
||
ellipse 56% 64% at center,
|
||
transparent 28%,
|
||
#000 48%,
|
||
transparent 94%
|
||
);
|
||
will-change: opacity;
|
||
}
|
||
|
||
.dacAura::before,
|
||
.dacAura::after {
|
||
position: absolute;
|
||
inset: 0;
|
||
content: '';
|
||
will-change: opacity;
|
||
}
|
||
|
||
.dacAura::before {
|
||
background: color-mix(in srgb, var(--dac-g1) 58%, transparent);
|
||
-webkit-mask:
|
||
repeating-linear-gradient(90deg, #000 0 1px, transparent 1px 5px),
|
||
repeating-linear-gradient(0deg, #000 0 1px, transparent 1px 5px);
|
||
-webkit-mask-composite: source-in;
|
||
mask:
|
||
repeating-linear-gradient(90deg, #000 0 1px, transparent 1px 5px),
|
||
repeating-linear-gradient(0deg, #000 0 1px, transparent 1px 5px);
|
||
mask-composite: intersect;
|
||
opacity: 1;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.dacAura::after {
|
||
background: color-mix(in srgb, var(--dac-g1) 66%, transparent);
|
||
-webkit-mask:
|
||
repeating-linear-gradient(90deg, #000 0 1px, transparent 1px 5px),
|
||
repeating-linear-gradient(0deg, #000 0 1px, transparent 1px 5px),
|
||
repeating-linear-gradient(
|
||
90deg,
|
||
#000 0,
|
||
rgba(0, 0, 0, 0.08) 28px,
|
||
#000 56px
|
||
);
|
||
-webkit-mask-composite: source-in, source-in;
|
||
mask:
|
||
repeating-linear-gradient(90deg, #000 0 1px, transparent 1px 5px),
|
||
repeating-linear-gradient(0deg, #000 0 1px, transparent 1px 5px),
|
||
repeating-linear-gradient(
|
||
90deg,
|
||
#000 0,
|
||
rgba(0, 0, 0, 0.08) 28px,
|
||
#000 56px
|
||
);
|
||
mask-composite: intersect, intersect;
|
||
opacity: 0;
|
||
animation: dac-aura-cols 2.4s linear infinite paused;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.container[data-dac-typing] .dacAura::before {
|
||
opacity: 0;
|
||
}
|
||
|
||
.container[data-dac-typing] .dacAura::after {
|
||
opacity: 1;
|
||
animation-play-state: running;
|
||
}
|
||
|
||
.dacHalo {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1;
|
||
border-radius: inherit;
|
||
opacity: calc(var(--dac-glow-on) * 0.5);
|
||
box-shadow:
|
||
0 0 0 1px color-mix(in srgb, var(--dac-g2) 36%, transparent),
|
||
0 1px 5px color-mix(in srgb, var(--dac-g1) 18%, transparent),
|
||
0 6px 30px color-mix(in srgb, var(--dac-g1) 22%, transparent);
|
||
will-change: opacity;
|
||
}
|
||
|
||
.dacHalo::after {
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
box-shadow: 0 6px 40px color-mix(in srgb, var(--dac-g1) 14%, transparent);
|
||
content: '';
|
||
opacity: calc(var(--dac-glow-on) * var(--dac-glow-pulse) * 0.5);
|
||
will-change: opacity;
|
||
}
|
||
|
||
@keyframes dac-aura-cols {
|
||
from {
|
||
-webkit-mask-position:
|
||
0 0,
|
||
0 0,
|
||
0 0;
|
||
mask-position:
|
||
0 0,
|
||
0 0,
|
||
0 0;
|
||
}
|
||
|
||
to {
|
||
-webkit-mask-position:
|
||
0 0,
|
||
0 0,
|
||
56px 0;
|
||
mask-position:
|
||
0 0,
|
||
0 0,
|
||
56px 0;
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.dacAura::after,
|
||
.container[data-dac-typing] .dacAura::after {
|
||
opacity: 0;
|
||
animation: none;
|
||
}
|
||
|
||
.container[data-dac-typing] .dacAura::before {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.atPortalLayer {
|
||
display: contents;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu]) {
|
||
width: 620px;
|
||
max-width: min(
|
||
calc(var(--radix-popover-trigger-width) - 32px),
|
||
var(--radix-popover-content-available-width),
|
||
calc(100vw - 24px)
|
||
);
|
||
max-height: var(--radix-popover-content-available-height);
|
||
overflow: hidden;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu][data-web-shell-compact-overlay]) {
|
||
width: min(360px, calc(100vw - 16px));
|
||
max-width: min(
|
||
var(--radix-popover-content-available-width),
|
||
calc(100vw - 16px)
|
||
);
|
||
box-sizing: border-box;
|
||
min-width: 0;
|
||
padding: 6px;
|
||
}
|
||
|
||
:global([data-web-shell-slash-detail]) {
|
||
z-index: calc(var(--web-shell-popover-z-index, 1000) + 1);
|
||
width: min(320px, calc(100vw - 24px));
|
||
max-height: min(200px, var(--radix-popover-content-available-height));
|
||
overflow: hidden;
|
||
}
|
||
|
||
:global([data-web-shell-toolbar-popover]) {
|
||
width: min(max-content, var(--radix-popover-content-available-width));
|
||
max-height: var(--radix-popover-content-available-height);
|
||
overflow: hidden;
|
||
}
|
||
|
||
:global([data-web-shell-toolbar-popover][data-web-shell-compact-overlay]) {
|
||
width: min(360px, calc(100vw - 16px));
|
||
max-width: calc(100vw - 16px);
|
||
box-sizing: border-box;
|
||
padding: 6px;
|
||
}
|
||
|
||
:global([data-web-shell-toolbar-popover][data-web-shell-compact-overlay])
|
||
.dropdownItemDesc {
|
||
display: -webkit-box;
|
||
overflow-wrap: anywhere;
|
||
white-space: normal;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
}
|
||
|
||
:global([data-web-shell-reasoning-popover]) {
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
:global([data-web-shell-model-submenu]) {
|
||
z-index: calc(var(--web-shell-popover-z-index, 1000) + 1);
|
||
min-width: min(280px, var(--radix-popover-content-available-width));
|
||
}
|
||
|
||
.slashPanel {
|
||
max-height: min(
|
||
460px,
|
||
calc(var(--radix-popover-content-available-height) - 20px)
|
||
);
|
||
overflow: hidden;
|
||
cursor: default;
|
||
}
|
||
|
||
.slashPanelBody {
|
||
max-height: inherit;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.slashList {
|
||
display: flex;
|
||
width: 100%;
|
||
max-height: inherit;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
scroll-padding-bottom: 12px;
|
||
scrollbar-color: var(--chat-editor-border-color) transparent;
|
||
scrollbar-width: thin;
|
||
}
|
||
|
||
.slashList::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
.slashList::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.slashList::-webkit-scrollbar-thumb {
|
||
border-radius: 3px;
|
||
background: var(--chat-editor-border-color);
|
||
}
|
||
|
||
.slashEntry {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.slashSection {
|
||
height: 1px;
|
||
margin: 5px 8px;
|
||
background: var(--chat-editor-border-color);
|
||
}
|
||
|
||
.slashSectionHeader {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
padding: 4px 8px 2px;
|
||
color: var(--muted-foreground);
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.02em;
|
||
user-select: none;
|
||
}
|
||
|
||
.slashSectionCount {
|
||
flex: 0 0 auto;
|
||
color: var(--muted-foreground);
|
||
font-weight: 400;
|
||
font-variant-numeric: tabular-nums;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.slashItem {
|
||
position: relative;
|
||
display: grid;
|
||
width: 100%;
|
||
min-width: 0;
|
||
grid-template-columns:
|
||
minmax(0, 220px)
|
||
minmax(0, 1fr);
|
||
column-gap: 8px;
|
||
align-items: baseline;
|
||
padding: 5px 8px;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--foreground);
|
||
font: inherit;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.slashItem:not([data-has-description]) {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
column-gap: 0;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu][data-web-shell-compact-overlay])
|
||
.slashItem {
|
||
display: flex;
|
||
align-items: stretch;
|
||
flex-direction: column;
|
||
gap: 1px;
|
||
padding: 6px 8px;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu][data-web-shell-compact-overlay])
|
||
.slashDescription {
|
||
display: none;
|
||
overflow-wrap: anywhere;
|
||
text-align: left;
|
||
white-space: normal;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu][data-web-shell-compact-overlay])
|
||
.slashItemActive
|
||
.slashDescription {
|
||
display: -webkit-box;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu][data-web-shell-compact-overlay])
|
||
.slashCommand {
|
||
min-width: 0;
|
||
overflow-wrap: anywhere;
|
||
text-overflow: clip;
|
||
white-space: normal;
|
||
}
|
||
|
||
:global([data-web-shell-slash-menu][data-web-shell-compact-overlay])
|
||
.slashSectionCount {
|
||
display: none;
|
||
}
|
||
|
||
.slashItem:hover,
|
||
.slashItemActive {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.slashItem:hover .slashCommand,
|
||
.slashItemActive .slashCommand {
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.slashDetail {
|
||
min-height: 0;
|
||
max-height: min(
|
||
180px,
|
||
max(0px, calc(var(--radix-popover-content-available-height) - 20px))
|
||
);
|
||
overflow: auto;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
.slashDetailCommand {
|
||
margin-bottom: 5px;
|
||
color: var(--foreground);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 22px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.slashDetailText {
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
font-weight: 400;
|
||
line-height: 20px;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.slashCommand {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
color: var(--foreground);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 22px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.slashArgumentHint {
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.slashDescription {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
font-weight: 400;
|
||
line-height: 20px;
|
||
text-align: right;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.atPanel {
|
||
position: fixed;
|
||
z-index: var(--web-shell-popover-z-index, 1000);
|
||
--at-anchor-width: 620px;
|
||
display: flex;
|
||
width: min(360px, calc(var(--at-anchor-width) - 32px), calc(100vw - 24px));
|
||
max-height: min(var(--at-panel-max-height, 300px), 42vh);
|
||
box-sizing: border-box;
|
||
flex-direction: column;
|
||
padding: 6px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 8px;
|
||
background: var(--background);
|
||
box-shadow:
|
||
0 2px 4px -2px rgba(0, 0, 0, 0.1),
|
||
0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||
color: var(--chat-editor-text-primary);
|
||
cursor: default;
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 13px;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.atPanel[data-web-shell-compact-overlay] {
|
||
width: min(360px, calc(100vw - 16px));
|
||
padding: 4px;
|
||
}
|
||
|
||
.atPanel[data-web-shell-compact-overlay] .atItemDescription {
|
||
overflow-wrap: anywhere;
|
||
white-space: normal;
|
||
}
|
||
|
||
.atPanel[data-web-shell-compact-overlay] .atItemMain {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
row-gap: 1px;
|
||
}
|
||
|
||
.atPanel[data-web-shell-compact-overlay] .atItemSubtitle {
|
||
justify-self: start;
|
||
overflow-wrap: anywhere;
|
||
text-align: left;
|
||
text-overflow: clip;
|
||
white-space: normal;
|
||
}
|
||
|
||
.atPanelHeaderWrap {
|
||
flex: 0 0 auto;
|
||
padding: 3px 4px 7px;
|
||
margin-bottom: 4px;
|
||
border-bottom: 1px solid var(--chat-editor-border-color);
|
||
}
|
||
|
||
.atPanelHeader {
|
||
display: flex;
|
||
min-width: 0;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.atBackButton {
|
||
display: inline-flex;
|
||
width: 22px;
|
||
height: 22px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 5px;
|
||
background: transparent;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 18px;
|
||
line-height: 1;
|
||
}
|
||
|
||
.atBackButton:hover {
|
||
background: var(--accent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.atPanelTitle {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
color: var(--foreground);
|
||
font-size: 13px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.atTabsWrap {
|
||
display: grid;
|
||
min-width: 0;
|
||
grid-template-columns: 22px minmax(0, 1fr) 22px;
|
||
align-items: center;
|
||
gap: 4px;
|
||
margin-top: 4px;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.atTabs {
|
||
display: flex;
|
||
min-width: 0;
|
||
gap: 4px;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.atTabs::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.atTab,
|
||
.atTabScrollButton {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
height: 22px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 8px;
|
||
border: 0;
|
||
border-radius: 5px;
|
||
background: transparent;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.atTab {
|
||
flex: 0 0 auto;
|
||
max-width: 120px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.atTabScrollButton {
|
||
width: 22px;
|
||
padding: 0;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.atTab:hover,
|
||
.atTabActive,
|
||
.atTabScrollButton:hover {
|
||
background: var(--accent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.atTab:disabled {
|
||
opacity: 0.45;
|
||
cursor: default;
|
||
}
|
||
|
||
.atSearchInput {
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
padding: 5px 7px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 6px;
|
||
outline: none;
|
||
background: transparent;
|
||
color: var(--foreground);
|
||
font: inherit;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
}
|
||
|
||
.atSearchInput::placeholder {
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.atList {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
border-radius: 6px;
|
||
scroll-padding-bottom: 6px;
|
||
scrollbar-color: var(--chat-editor-border-color) transparent;
|
||
scrollbar-width: thin;
|
||
}
|
||
|
||
.atList::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
.atList::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.atList::-webkit-scrollbar-thumb {
|
||
border-radius: 3px;
|
||
background: var(--chat-editor-border-color);
|
||
}
|
||
|
||
.atItem {
|
||
display: grid;
|
||
width: 100%;
|
||
min-width: 0;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
grid-template-areas:
|
||
'label trailing'
|
||
'description trailing';
|
||
column-gap: 8px;
|
||
row-gap: 1px;
|
||
align-items: center;
|
||
padding: 6px 8px;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--foreground);
|
||
font: inherit;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.atItem:hover,
|
||
.atItemActive {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.atItemSingleLine {
|
||
grid-template-areas: 'label trailing';
|
||
padding-top: 5px;
|
||
padding-bottom: 5px;
|
||
}
|
||
|
||
.atItemMain {
|
||
grid-area: label;
|
||
display: grid;
|
||
width: 100%;
|
||
min-width: 0;
|
||
grid-template-columns: var(--at-item-main-template, minmax(0, 1fr));
|
||
column-gap: 8px;
|
||
align-items: end;
|
||
justify-self: stretch;
|
||
}
|
||
|
||
.atItemLeading {
|
||
display: grid;
|
||
width: 100%;
|
||
min-width: 0;
|
||
grid-template-columns: auto minmax(0, 1fr);
|
||
align-items: end;
|
||
gap: 6px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.atItemIcon {
|
||
display: inline-block;
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: 0 0 auto;
|
||
background: currentColor;
|
||
color: var(--muted-foreground);
|
||
mask: var(--at-item-icon-url) center / contain no-repeat;
|
||
-webkit-mask: var(--at-item-icon-url) center / contain no-repeat;
|
||
}
|
||
|
||
.atItemUploadIcon {
|
||
align-self: center;
|
||
width: 14px;
|
||
height: 14px;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.atItemImageIcon {
|
||
display: block;
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: 0 0 auto;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.atItemIconSpin {
|
||
animation: at-item-icon-spin 1.2s linear infinite;
|
||
}
|
||
|
||
.atItemLabel,
|
||
.atItemSubtitle {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
font-size: 13px;
|
||
line-height: 18px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.atItemLabel {
|
||
display: block;
|
||
width: 100%;
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.atItemSubtitle {
|
||
color: var(--muted-foreground);
|
||
justify-self: end;
|
||
text-align: right;
|
||
}
|
||
|
||
@keyframes at-item-icon-spin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.atItemDescription {
|
||
grid-area: description;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
line-height: 16px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.atItemTrailing {
|
||
grid-area: trailing;
|
||
color: var(--muted-foreground);
|
||
font-size: 16px;
|
||
line-height: 1;
|
||
}
|
||
|
||
.atEmpty {
|
||
padding: 10px 8px;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.attachments {
|
||
display: flex;
|
||
min-height: 0;
|
||
max-height: var(--chat-editor-attachments-max-height, 136px);
|
||
flex: 0 1 auto;
|
||
flex-direction: column;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
}
|
||
|
||
.tags {
|
||
display: flex;
|
||
flex: 0 0 auto;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
padding: 0 0 8px;
|
||
}
|
||
|
||
.tag {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
max-width: 100%;
|
||
min-height: 22px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 4px;
|
||
background: var(--chat-editor-bg-tertiary);
|
||
color: var(--chat-editor-text-primary);
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.tagContent {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
flex: 1 1 auto;
|
||
align-items: center;
|
||
}
|
||
|
||
.tagTooltip {
|
||
z-index: calc(var(--web-shell-tooltip-z-index, 1000) + 1);
|
||
box-sizing: border-box;
|
||
min-width: 160px;
|
||
max-width: min(320px, 80vw);
|
||
max-height: min(
|
||
calc(100vh - 16px),
|
||
var(--radix-tooltip-content-available-height, calc(100vh - 16px))
|
||
);
|
||
padding: 8px 10px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 6px;
|
||
background: var(--background);
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
||
color: var(--foreground);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
pointer-events: auto;
|
||
white-space: normal;
|
||
}
|
||
|
||
@supports (height: 100dvh) {
|
||
.tagTooltip {
|
||
max-height: min(
|
||
calc(100dvh - 16px),
|
||
var(--radix-tooltip-content-available-height, calc(100dvh - 16px))
|
||
);
|
||
}
|
||
}
|
||
|
||
.tagLabel,
|
||
.tagValue {
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.tagIcon {
|
||
display: block;
|
||
width: 12px;
|
||
height: 12px;
|
||
flex: 0 0 auto;
|
||
margin-left: 7px;
|
||
background: currentColor;
|
||
mask: var(--composer-tag-icon-url) center / contain no-repeat;
|
||
-webkit-mask: var(--composer-tag-icon-url) center / contain no-repeat;
|
||
}
|
||
|
||
.tagLabel,
|
||
.tagValue {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.tagLabel {
|
||
padding: 3px 0 3px 7px;
|
||
color: var(--chat-editor-accent-color);
|
||
}
|
||
|
||
.tagIcon + .tagLabel {
|
||
padding-left: 0.5ch;
|
||
}
|
||
|
||
.tagValue {
|
||
max-width: 32ch;
|
||
padding: 3px 0 3px 0.5ch;
|
||
color: var(--foreground, #e4e4e4);
|
||
opacity: 1;
|
||
}
|
||
|
||
.tagRemove {
|
||
flex: 0 0 auto;
|
||
width: 22px;
|
||
height: 22px;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--chat-editor-text-dimmed);
|
||
font: inherit;
|
||
line-height: 22px;
|
||
cursor: pointer;
|
||
border-radius: 0 4px 4px 0;
|
||
}
|
||
|
||
.tagRemove:hover,
|
||
.tagRemove:focus-visible {
|
||
color: var(--error-color);
|
||
outline: none;
|
||
}
|
||
|
||
.editorArea {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
align-items: stretch;
|
||
gap: 6px;
|
||
min-height: var(--chat-editor-input-min-height, 44px);
|
||
padding: 4px 0;
|
||
overflow: clip;
|
||
}
|
||
|
||
.editorArea > :last-child {
|
||
display: grid;
|
||
min-width: 0;
|
||
min-height: var(--chat-editor-input-min-height, 44px);
|
||
flex: 1;
|
||
overflow: clip;
|
||
}
|
||
|
||
.editorArea :global(.cm-editor) {
|
||
height: 100%;
|
||
min-height: 0;
|
||
}
|
||
|
||
.editorArea :global(.cm-scroller) {
|
||
height: 100%;
|
||
min-height: 0;
|
||
}
|
||
|
||
.shellPrefix {
|
||
flex: 0 0 auto;
|
||
align-self: flex-start;
|
||
padding-top: 1px;
|
||
color: var(--chat-editor-accent-color);
|
||
font-family: var(--font-mono);
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
line-height: 1.6;
|
||
user-select: none;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.editorArea .cm-content {
|
||
color: var(--foreground);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* Touch-device textarea backend (#5958). Mirrors the CodeMirror look; the
|
||
16px font size is mandatory: iOS Safari auto-zooms the page when focusing
|
||
any input with a smaller font. */
|
||
.mobileTextarea {
|
||
field-sizing: content;
|
||
width: 100%;
|
||
min-height: var(--chat-editor-input-min-height, 44px);
|
||
max-height: var(--chat-editor-input-max-height, 300px);
|
||
padding: 0;
|
||
border: none;
|
||
outline: none;
|
||
background: transparent;
|
||
resize: none;
|
||
color: var(--chat-editor-text-primary, #e0e0e0);
|
||
caret-color: var(--chat-editor-accent-color, #4a9eff);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 16px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.mobileTextarea::placeholder {
|
||
color: var(--chat-editor-text-dimmed, #666);
|
||
}
|
||
|
||
/* As .editorArea's last child the textarea would inherit `overflow: clip`
|
||
from the rule above, pinning scrollTop to 0 — content beyond the height
|
||
cap would become unreachable. The extra `textarea` type wins that
|
||
specificity. */
|
||
.editorArea > textarea.mobileTextarea {
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.editorArea .cm-scroller {
|
||
scrollbar-color: var(--chat-editor-border-color) transparent;
|
||
scrollbar-width: thin;
|
||
}
|
||
|
||
.editorArea .cm-scroller::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.editorArea .cm-scroller::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.editorArea .cm-scroller::-webkit-scrollbar-thumb {
|
||
border-radius: 3px;
|
||
background: var(--chat-editor-border-color);
|
||
}
|
||
|
||
.toolbar {
|
||
display: flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
min-width: 0;
|
||
padding: 4px 0 0;
|
||
gap: 8px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] .toolbar {
|
||
gap: 4px;
|
||
}
|
||
|
||
/* The workspace and git chips, moved out of the toolbar into their own row
|
||
under the composer box so the composer stays about the prompt itself. The
|
||
4px inset plus the workspace chip's 8px padding lines their content up with the
|
||
editor's text. */
|
||
.contextRow {
|
||
display: flex;
|
||
min-width: 0;
|
||
align-items: center;
|
||
gap: 4px;
|
||
margin-top: -12px;
|
||
padding: 18px 4px 6px;
|
||
border-radius: 0 0 12px 12px;
|
||
background: color-mix(in srgb, var(--muted) 70%, transparent);
|
||
}
|
||
|
||
.contextRow button[data-web-shell-git-branch] {
|
||
height: 28px;
|
||
padding: 0 12px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--agent-gray-500);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 13px;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.contextRow button[data-web-shell-git-branch]:hover,
|
||
.contextRow button[data-web-shell-git-branch]:focus-visible {
|
||
background: var(--chat-editor-bg-tertiary);
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.contextRow button[data-web-shell-git-branch] > svg {
|
||
display: none;
|
||
}
|
||
|
||
.toolbarLeading,
|
||
.toolbarStart,
|
||
.toolbarEnd,
|
||
.toolbarLeft,
|
||
.toolbarRight {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.toolbarLeading {
|
||
margin-left: -5px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] .toolbarLeading {
|
||
margin-left: 0;
|
||
}
|
||
|
||
.toolbar[data-mobile-voice-active='true'] .toolbarLeading,
|
||
.toolbar[data-mobile-voice-active='true'] [data-hide-during-mobile-voice] {
|
||
display: none;
|
||
}
|
||
|
||
.toolbar[data-mobile-voice-active='true'] .toolbarRight {
|
||
margin-left: auto;
|
||
}
|
||
|
||
.toolbarStart {
|
||
min-width: 0;
|
||
}
|
||
|
||
.toolbarEnd {
|
||
min-width: 0;
|
||
}
|
||
|
||
.toolbarLeft {
|
||
min-width: 0;
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
.gitBranchChip {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
max-width: 180px;
|
||
height: 28px;
|
||
padding: 0 8px;
|
||
align-items: center;
|
||
gap: 5px;
|
||
border-radius: 6px;
|
||
color: var(--agent-gray-500);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* When the chip opens the Changes dialog it renders as a <button>; reset the
|
||
UA button chrome so it looks identical to the read-only <output> chip. */
|
||
.gitBranchChipButton {
|
||
appearance: none;
|
||
border: 0;
|
||
background: transparent;
|
||
cursor: pointer;
|
||
margin: 0;
|
||
}
|
||
|
||
.gitBranchChipButton:hover {
|
||
background: var(--chat-editor-bg-tertiary);
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.gitBranchChipButton:focus-visible {
|
||
outline: 2px solid var(--chat-editor-accent-color);
|
||
outline-offset: -2px;
|
||
}
|
||
|
||
.gitBranchIcon {
|
||
display: inline-flex;
|
||
width: var(--git-branch-icon-size, 16px);
|
||
height: var(--git-branch-icon-size, 16px);
|
||
flex: 0 0 var(--git-branch-icon-size, 16px);
|
||
}
|
||
|
||
.gitBranchIcon svg {
|
||
width: var(--git-branch-icon-size, 16px);
|
||
height: var(--git-branch-icon-size, 16px);
|
||
}
|
||
|
||
.gitBranchText {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.gitBranchChip[data-detached='true'] .gitBranchText {
|
||
color: var(--warning-color);
|
||
}
|
||
|
||
.gitBranchChip[data-worktree='true'] {
|
||
color: var(--color-accent-fg, #8b5cf6);
|
||
}
|
||
|
||
.gitBranchIconWrap {
|
||
position: relative;
|
||
display: inline-flex;
|
||
flex: 0 0 var(--git-branch-icon-size, 16px);
|
||
}
|
||
|
||
/* Compact (icon-only) chip: a single severity dot on the branch icon so the
|
||
working-tree state is still glanceable without the text + inline indicators. */
|
||
.gitBranchBadgeDot {
|
||
position: absolute;
|
||
top: var(--git-branch-badge-offset, -2px);
|
||
right: var(--git-branch-badge-offset, -3px);
|
||
width: var(--git-branch-badge-size, 7px);
|
||
height: var(--git-branch-badge-size, 7px);
|
||
border-radius: 50%;
|
||
background: var(--chat-editor-accent-color);
|
||
box-shadow: 0 0 0 1.5px var(--chat-editor-bg-primary, transparent);
|
||
}
|
||
|
||
.gitBranchBadgeDot[data-tone='warning'] {
|
||
background: var(--warning-color);
|
||
}
|
||
|
||
.gitBranchBadgeDot[data-tone='error'] {
|
||
background: var(--error-color);
|
||
}
|
||
|
||
.gitBranchIndicators {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.gitBranchDirtyDot {
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
background: var(--chat-editor-accent-color);
|
||
display: inline-block;
|
||
}
|
||
|
||
.gitBranchAheadBehind {
|
||
color: var(--muted-foreground);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.gitBranchStash {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
color: var(--muted-foreground);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.gitBranchStash svg {
|
||
width: 13px;
|
||
height: 13px;
|
||
}
|
||
|
||
.gitBranchConflicted {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
color: var(--error-color);
|
||
font-weight: 600;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.gitBranchConflicted svg {
|
||
width: 13px;
|
||
height: 13px;
|
||
}
|
||
|
||
.gitBranchOperation {
|
||
color: var(--warning-color);
|
||
font-weight: 600;
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
.gitBranchTooltip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
|
||
.gitBranchTooltipTitle {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.gitBranchTooltipRow {
|
||
opacity: 0.85;
|
||
}
|
||
|
||
/* Which workspace a split-view pane's session lives in — parallel to the git
|
||
branch chip, shown only on a multi-workspace daemon. */
|
||
.workspaceChip {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
max-width: 160px;
|
||
height: 28px;
|
||
padding: 0 8px;
|
||
align-items: center;
|
||
gap: 5px;
|
||
border-radius: 6px;
|
||
color: var(--agent-gray-500);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* On a multi-workspace daemon the chip gets a stable per-workspace accent
|
||
(--ws-accent, set by a shared accent class): a faint tinted background plus a
|
||
colored folder, so panes stay distinguishable even when the chip collapses to
|
||
the icon-only compact state. */
|
||
.workspaceChipAccented {
|
||
background: color-mix(in srgb, var(--ws-accent) 12%, transparent);
|
||
}
|
||
|
||
.workspaceChipAccented .workspaceChipIcon {
|
||
color: var(--ws-accent);
|
||
}
|
||
|
||
.workspaceChipIcon {
|
||
display: inline-flex;
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: 0 0 16px;
|
||
}
|
||
|
||
.workspaceChipIcon svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.workspaceChipText {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.toolbarRight {
|
||
flex-shrink: 0;
|
||
margin-right: -5px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] .toolbarRight {
|
||
gap: 0;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer]
|
||
.toolbarRight
|
||
button[data-available] {
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-compact-composer] .toolbarRight button svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.toolbarRightCustom {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
align-items: center;
|
||
}
|
||
|
||
.toolbarMeasurements {
|
||
position: fixed;
|
||
top: -10000px;
|
||
left: -10000px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
visibility: hidden;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.dropdownWrapper {
|
||
position: relative;
|
||
}
|
||
|
||
.dropdownWrapperCompact {
|
||
width: 28px;
|
||
flex: 0 0 28px;
|
||
}
|
||
|
||
.dropdownList {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.dropdownListConstrained {
|
||
max-height: 250px;
|
||
}
|
||
|
||
.dropdownEmpty {
|
||
padding: 8px 9px;
|
||
color: var(--muted-foreground);
|
||
font-size: 13px;
|
||
line-height: 18px;
|
||
}
|
||
|
||
.dropdownItem {
|
||
display: block;
|
||
width: 100%;
|
||
padding: 6px 12px;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--foreground);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 22px;
|
||
text-align: left;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.dropdownRich .dropdownItem {
|
||
display: grid;
|
||
grid-template-columns: 24px minmax(0, 1fr) 18px;
|
||
gap: 10px;
|
||
align-items: center;
|
||
padding: 6px 9px;
|
||
white-space: normal;
|
||
}
|
||
|
||
.dropdownCheck .dropdownItem {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) 18px;
|
||
gap: 10px;
|
||
align-items: center;
|
||
padding: 6px 9px;
|
||
}
|
||
|
||
.dropdownItem:hover {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.dropdownItemActive {
|
||
color: var(--foreground);
|
||
background: var(--accent);
|
||
}
|
||
|
||
.dropdownItemIcon,
|
||
.dropdownItemCheck {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--secondary-foreground);
|
||
}
|
||
|
||
.dropdownItemIcon svg {
|
||
width: 19px;
|
||
height: 19px;
|
||
}
|
||
|
||
.dropdownItemCheck svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.dropdownItemContent {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
|
||
.dropdownItemLabel {
|
||
overflow: hidden;
|
||
color: var(--foreground);
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 22px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.dropdownItemDesc {
|
||
overflow: hidden;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
font-weight: 400;
|
||
line-height: 1.2;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.dropdownSubmenuTrigger {
|
||
display: flex;
|
||
min-height: 34px;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
}
|
||
|
||
.dropdownSubmenuTrigger span {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.dropdownSubmenuTrigger svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: 0 0 auto;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.dropdownSubmenuSection {
|
||
padding-top: 4px;
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
|
||
.reasoningOptions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-bottom: 4px;
|
||
}
|
||
|
||
.reasoningSectionTitle {
|
||
padding: 4px 9px;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.reasoningThinkingRow,
|
||
.reasoningEffortRow {
|
||
display: flex;
|
||
min-height: 34px;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
padding: 6px 9px;
|
||
}
|
||
|
||
.reasoningEffortRow {
|
||
width: 100%;
|
||
border: 0;
|
||
border-radius: 4px;
|
||
background: transparent;
|
||
color: var(--foreground);
|
||
font: inherit;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.reasoningEffortRow:hover:not(:disabled) {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.reasoningEffortRow:disabled {
|
||
cursor: default;
|
||
opacity: 0.45;
|
||
}
|
||
|
||
.reasoningDivider {
|
||
height: 1px;
|
||
margin: 4px 0;
|
||
background: var(--border);
|
||
}
|
||
|
||
.dropdownItemActive .dropdownItemIcon,
|
||
.dropdownItemActive .dropdownItemCheck {
|
||
color: var(--secondary-foreground);
|
||
}
|
||
|
||
.toolBtn {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
height: 28px;
|
||
padding: 0 8px;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--agent-gray-500);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 13px;
|
||
line-height: 1;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.modeToolBtn,
|
||
.modelToolBtn {
|
||
min-width: 0;
|
||
}
|
||
|
||
.toolBtn:not(:disabled):not([data-disabled]):hover {
|
||
background: var(--chat-editor-bg-tertiary);
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.planControl {
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.planControl[data-disabled] {
|
||
cursor: default;
|
||
}
|
||
|
||
/* Shown only while Plan is on: the state has to stay visible even though the
|
||
entry itself is tucked into the add menu. */
|
||
.planChip {
|
||
flex: 0 0 auto;
|
||
background: var(--subtle-bg-strong);
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
/* Not the shorter `.toolBtn.planChip:hover`: this ties the generic toolbar
|
||
hover above on specificity and wins by coming later, which the shorter
|
||
selector would not. The chip is never natively disabled, so the tint does
|
||
not drop under the pointer during the request the click itself starts. */
|
||
.toolBtn.planChip:not(:disabled):hover {
|
||
background: color-mix(
|
||
in srgb,
|
||
var(--chat-editor-text-primary) 12%,
|
||
transparent
|
||
);
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.planChip .toolBtnText {
|
||
color: inherit;
|
||
}
|
||
|
||
/* No dimming: like every other .toolBtn, including the permission button
|
||
beside it, the chip keeps its appearance while mode controls are busy. That
|
||
state is brief, and dimming one control of the pair would flicker. */
|
||
.planChip[aria-disabled='true'] {
|
||
cursor: default;
|
||
}
|
||
|
||
/* At rest the chip shows the Plan icon and no close mark. The mark shares the
|
||
icon slot and takes it over on hover or keyboard focus, so revealing it
|
||
never changes how wide the chip is. */
|
||
.planChipIcon {
|
||
position: relative;
|
||
}
|
||
|
||
.planChipIcon > .planChipClose {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: inline-flex;
|
||
width: auto;
|
||
height: auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 0;
|
||
}
|
||
|
||
.planChipIcon > .planChipClose svg {
|
||
width: 12px;
|
||
height: 12px;
|
||
}
|
||
|
||
/* None of the reveals is limited to an enabled chip: it is inert for the
|
||
length of the request its own click starts, and swapping back under the
|
||
pointer would read as the click having been refused. */
|
||
.planChip:focus-visible .planChipIcon > :not(.planChipClose) {
|
||
opacity: 0;
|
||
}
|
||
|
||
.planChip:focus-visible .planChipClose {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Only where the pointer really hovers. A touch browser keeps :hover on the
|
||
last thing tapped, which would leave the mark stuck on a chip whose tap did
|
||
nothing. */
|
||
@media (hover: hover) {
|
||
.planChip:hover .planChipIcon > :not(.planChipClose) {
|
||
opacity: 0;
|
||
}
|
||
|
||
.planChip:hover .planChipClose {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* Without hover only keyboard focus would ever reveal the mark, so it stays
|
||
shown — but only beside the label. A chip too narrow for its label keeps the Plan icon
|
||
instead: a bare × would name nothing, and a pressed button that a tap
|
||
releases needs no mark to work. Keyed on the primary pointer, so a
|
||
touchscreen laptop driven by a mouse still gets the quiet chip. */
|
||
@media (hover: none) {
|
||
.planChip[data-labelled] .planChipIcon > :not(.planChipClose) {
|
||
opacity: 0;
|
||
}
|
||
|
||
.planChip[data-labelled] .planChipClose {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* Forced colours repaint a masked background away, which would leave a chip
|
||
without its label blank at rest. Painting the mask with a system colour
|
||
keeps the icon. Every mode icon has the same gap; this covers only the one
|
||
this chip depends on. */
|
||
@media (forced-colors: active) {
|
||
.planChipIcon > span:not(.planChipClose) {
|
||
forced-color-adjust: none;
|
||
background: ButtonText;
|
||
}
|
||
}
|
||
|
||
.addMenuPlanIcon {
|
||
display: inline-flex;
|
||
width: 16px;
|
||
height: 16px;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.workspaceSelectTrigger {
|
||
min-width: 0;
|
||
max-width: 176px;
|
||
align-items: center;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.workspaceSelectTrigger[data-disabled] {
|
||
cursor: default;
|
||
opacity: 1;
|
||
}
|
||
|
||
.workspaceSelectTrigger > svg {
|
||
display: block;
|
||
flex-shrink: 0;
|
||
align-self: center;
|
||
}
|
||
|
||
.workspaceSelectTooltipTrigger {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
max-width: 176px;
|
||
}
|
||
|
||
.workspaceSelectTrigger [data-slot='select-value'] {
|
||
display: block;
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
align-self: center;
|
||
line-height: 1;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.gitBranchChipCompact,
|
||
.workspaceChipCompact,
|
||
.workspaceSelectTriggerCompact,
|
||
.toolBtnCompact {
|
||
width: 28px;
|
||
max-width: 28px;
|
||
flex: 0 0 28px;
|
||
gap: 0;
|
||
padding: 0 6px;
|
||
justify-content: center;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.workspaceSelectTooltipTriggerCompact {
|
||
width: 28px;
|
||
max-width: 28px;
|
||
flex: 0 0 28px;
|
||
}
|
||
|
||
.gitBranchChipCompact .gitBranchText,
|
||
.workspaceChipCompact .workspaceChipText,
|
||
.workspaceSelectTriggerCompact [data-slot='select-value'],
|
||
.workspaceSelectTriggerCompact > svg:last-child,
|
||
.workspaceSelectTriggerCompact .toolBtnText,
|
||
.workspaceSelectTriggerCompact .toolBtnArrow,
|
||
.toolBtnCompact .toolBtnText,
|
||
.toolBtnCompact .toolBtnArrow {
|
||
display: none;
|
||
}
|
||
|
||
.toolBtn[data-tooltip]:hover::after,
|
||
.toolBtn[data-tooltip]:focus-visible::after {
|
||
position: absolute;
|
||
right: 50%;
|
||
bottom: calc(100% + 8px);
|
||
z-index: 120;
|
||
padding: 4px 7px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 6px;
|
||
background: var(--background);
|
||
color: var(--chat-editor-text-primary);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);
|
||
content: attr(data-tooltip);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 12px;
|
||
line-height: 1.2;
|
||
pointer-events: none;
|
||
transform: translateX(50%);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.toolBtnIcon {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 20px;
|
||
height: 20px;
|
||
font-size: 16px;
|
||
line-height: 1;
|
||
}
|
||
|
||
.toolBtnIcon svg {
|
||
display: block;
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.widthModeBtn {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.addMenuBtn {
|
||
width: 32px;
|
||
height: 32px;
|
||
flex-shrink: 0;
|
||
justify-content: center;
|
||
padding: 0;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.addMenuBtn .toolBtnIcon svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
.contextUsageBtn {
|
||
flex-shrink: 0;
|
||
padding: 0 4px;
|
||
}
|
||
|
||
.contextUsagePercentage {
|
||
display: none;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.contextUsagePercentage[data-level='normal'] {
|
||
color: var(--chat-editor-text-secondary);
|
||
}
|
||
|
||
.contextUsagePercentage[data-level='warning'] {
|
||
color: var(--warning-color);
|
||
}
|
||
|
||
.contextUsagePercentage[data-level='error'] {
|
||
color: var(--error-color);
|
||
}
|
||
|
||
.contextTooltip {
|
||
--muted-foreground: var(--chat-editor-text-secondary);
|
||
--destructive: var(--error-color);
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 10px;
|
||
width: 280px;
|
||
max-width: calc(100vw - 24px);
|
||
padding: 12px;
|
||
white-space: normal;
|
||
}
|
||
|
||
.contextTooltipHeader {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.contextTooltipHeader strong {
|
||
font-size: 18px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.contextTooltipMeter {
|
||
height: 6px;
|
||
overflow: hidden;
|
||
border-radius: 3px;
|
||
background: var(--border);
|
||
}
|
||
|
||
.contextTooltipMeter > span {
|
||
display: block;
|
||
height: 100%;
|
||
background: var(--agent-blue-500);
|
||
}
|
||
|
||
.contextTooltipMeter > span[data-level='warning'] {
|
||
background: var(--warning-color);
|
||
}
|
||
|
||
.contextTooltipMeter > span[data-level='error'] {
|
||
background: var(--error-color);
|
||
}
|
||
|
||
.contextTooltipStats {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
gap: 6px 12px;
|
||
margin: 0;
|
||
}
|
||
|
||
.contextTooltipStats dt,
|
||
.contextTooltipHint {
|
||
color: var(--chat-editor-text-secondary);
|
||
}
|
||
|
||
.contextTooltipStats dd {
|
||
margin: 0;
|
||
text-align: right;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.contextTooltipHint {
|
||
padding-top: 8px;
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
|
||
@supports (container-type: inline-size) {
|
||
@container (min-width: 521px) {
|
||
.contextUsagePercentage {
|
||
display: inline;
|
||
}
|
||
}
|
||
}
|
||
|
||
.contextRingTrack {
|
||
stroke: var(--border);
|
||
}
|
||
|
||
.contextRingValue {
|
||
stroke: var(--agent-blue-500);
|
||
transition: stroke-dashoffset 0.25s ease;
|
||
}
|
||
|
||
.contextRingValueWarning {
|
||
stroke: var(--warning-color);
|
||
}
|
||
|
||
.contextRingValueError {
|
||
stroke: var(--error-color);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.contextRingValue {
|
||
transition: none;
|
||
}
|
||
}
|
||
|
||
.widthModeBtn .toolBtnIcon svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
.toolBtnModeIcon,
|
||
.toolBtnModelIcon {
|
||
display: inline-flex;
|
||
width: 18px;
|
||
height: 18px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.toolBtnModeIcon svg,
|
||
.toolBtnModelIcon svg {
|
||
display: block;
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.toolBtnModeIcon > span {
|
||
width: 14px;
|
||
height: 14px;
|
||
}
|
||
|
||
.toolBtnText {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
color: var(--agent-gray-500);
|
||
line-height: 1.4;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.toolBtnArrow {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 16px;
|
||
height: 16px;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.toolBtnArrow svg {
|
||
display: block;
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.sendBtn {
|
||
flex-shrink: 0;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 8px;
|
||
border: none;
|
||
background: var(--agent-gray-200);
|
||
color: var(--chat-send-button-disabled-foreground);
|
||
cursor: default;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
margin-left: 4px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.sendBtn:disabled {
|
||
opacity: 1;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.sendBtn:not(:disabled) {
|
||
border: none;
|
||
background: var(--agent-gray-800);
|
||
box-shadow: var(--agent-purple-shadow);
|
||
color: var(--chat-send-button-foreground);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.sendBtn:not(:disabled):hover {
|
||
filter: brightness(1.08);
|
||
}
|
||
|
||
.sendBtn:not(:disabled):active {
|
||
transform: scale(0.96);
|
||
}
|
||
|
||
.sendBtnRunning,
|
||
.sendBtnRunning:not(:disabled) {
|
||
border: none;
|
||
background: var(--agent-gray-800);
|
||
box-shadow: none;
|
||
color: var(--chat-send-button-foreground);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.sendBtnRunning:not(:disabled):hover {
|
||
filter: brightness(1.08);
|
||
}
|
||
|
||
.sendIcon {
|
||
display: inline-block;
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.stopIcon {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 3px;
|
||
background: currentColor;
|
||
}
|
||
|
||
.loadingIcon {
|
||
width: 14px;
|
||
height: 14px;
|
||
border: 2px solid currentColor;
|
||
border-top-color: transparent;
|
||
border-radius: 50%;
|
||
animation: sendBtnLoadingSpin 0.8s linear infinite;
|
||
}
|
||
|
||
/* Armed-to-cancel state: first Esc primes the stop, the button shows an "Esc"
|
||
hint, a subtle pulse, and a depleting ring counting down the confirm window
|
||
until the second press confirms or the window lapses. */
|
||
.sendBtnArmed,
|
||
.sendBtnArmed:not(:disabled) {
|
||
position: relative;
|
||
border-radius: 50%;
|
||
background: var(--error-color, #e06c75);
|
||
animation: sendBtnArmedPulse 1s ease-in-out infinite;
|
||
}
|
||
|
||
/* Registered so the conic angle can animate smoothly. */
|
||
@property --esc-countdown {
|
||
syntax: '<percentage>';
|
||
inherits: false;
|
||
initial-value: 100%;
|
||
}
|
||
|
||
/* Countdown ring around the armed button. Its duration matches
|
||
ESC_CANCEL_CONFIRM_WINDOW_MS in App.tsx — keep the two in sync. */
|
||
.sendBtnArmed::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -4px;
|
||
border-radius: 50%;
|
||
background: conic-gradient(
|
||
var(--error-color, #e06c75) var(--esc-countdown),
|
||
transparent 0
|
||
);
|
||
-webkit-mask: radial-gradient(
|
||
farthest-side,
|
||
transparent calc(100% - 2px),
|
||
#000 calc(100% - 2px)
|
||
);
|
||
mask: radial-gradient(
|
||
farthest-side,
|
||
transparent calc(100% - 2px),
|
||
#000 calc(100% - 2px)
|
||
);
|
||
animation: escCountdown 2000ms linear forwards;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@keyframes escCountdown {
|
||
to {
|
||
--esc-countdown: 0%;
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.loadingIcon {
|
||
animation: none;
|
||
}
|
||
|
||
.sendBtnArmed,
|
||
.sendBtnArmed:not(:disabled) {
|
||
animation: none;
|
||
}
|
||
|
||
.sendBtnArmed::after {
|
||
animation: none;
|
||
--esc-countdown: 100%;
|
||
}
|
||
}
|
||
|
||
@keyframes sendBtnLoadingSpin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.escLabel {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
/* Visually hidden but exposed to assistive tech (for aria-live announcements). */
|
||
.srOnly {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip: rect(0, 0, 0, 0);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
@keyframes sendBtnArmedPulse {
|
||
0%,
|
||
100% {
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
|
||
.images {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: flex;
|
||
flex: 0 0 auto;
|
||
gap: 6px;
|
||
padding: 0 0 8px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.imageThumb {
|
||
position: relative;
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
}
|
||
|
||
.imageThumb img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.imageRemove,
|
||
.fileChipRemove {
|
||
width: 16px;
|
||
height: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(0, 0, 0, 0.55);
|
||
color: #fff;
|
||
border: none;
|
||
cursor: pointer;
|
||
border-radius: 50%;
|
||
padding: 0;
|
||
opacity: 0;
|
||
transition:
|
||
opacity 0.15s ease,
|
||
background 0.15s ease;
|
||
}
|
||
|
||
.imageRemove {
|
||
position: absolute;
|
||
top: 2px;
|
||
right: 2px;
|
||
}
|
||
|
||
.imageRemove svg,
|
||
.fileChipRemove svg {
|
||
display: block;
|
||
}
|
||
|
||
.imageThumb:hover .imageRemove:not(:disabled),
|
||
.fileChip:hover .fileChipRemove:not(:disabled),
|
||
.imageRemove:focus-visible,
|
||
.fileChipRemove:focus-visible {
|
||
opacity: 1;
|
||
}
|
||
|
||
.imageRemove:not(:disabled):hover,
|
||
.fileChipRemove:not(:disabled):hover {
|
||
background: rgba(0, 0, 0, 0.8);
|
||
}
|
||
|
||
.imageRemove:disabled,
|
||
.fileChipRemove:disabled {
|
||
cursor: default;
|
||
opacity: 0;
|
||
}
|
||
|
||
.files {
|
||
display: flex;
|
||
flex: 0 0 auto;
|
||
gap: 6px;
|
||
padding: 4px 0 0;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
/* Two classes so this wins over `.fileChip`'s fixed width, which is declared
|
||
later in this file: a text card sizes to its own content. */
|
||
.fileChip.fileChipText {
|
||
width: auto;
|
||
min-width: min(264px, 100%);
|
||
max-width: min(100%, 360px);
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
}
|
||
|
||
.fileChipIcon {
|
||
display: flex;
|
||
flex: 0 0 48px;
|
||
width: 48px;
|
||
height: 48px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 12px;
|
||
background: var(--muted);
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.fileChipBody {
|
||
display: flex;
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
text-align: left;
|
||
}
|
||
|
||
.fileChipTitle {
|
||
overflow: hidden;
|
||
min-width: 0;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--foreground);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
line-height: 20px;
|
||
text-align: left;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.fileChipTitle:disabled {
|
||
cursor: default;
|
||
}
|
||
|
||
.fileChipTitle:not(:disabled):hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.fileChipTitle:focus-visible {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.fileChipMeta {
|
||
display: flex;
|
||
min-width: 0;
|
||
align-items: center;
|
||
gap: 6px;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
white-space: nowrap;
|
||
/* The row is nowrap, so at the narrowest panes let the size drop to its own
|
||
line rather than escaping the card. */
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.fileChipExpand {
|
||
display: inline-flex;
|
||
flex: 0 0 auto;
|
||
align-items: center;
|
||
gap: 2px;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
font: inherit;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
}
|
||
|
||
.fileChipExpand:not(:disabled):hover {
|
||
color: var(--foreground);
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.fileChipExpand:focus-visible {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.fileChipExpand:disabled {
|
||
cursor: default;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.fileChip {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
width: 272px;
|
||
max-width: 100%;
|
||
padding: 8px 32px 8px 8px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 16px;
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.fileChipPreviewable {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.fileChipPreview:focus-visible {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.fileChipPreview {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
width: 100%;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: inherit;
|
||
cursor: inherit;
|
||
font: inherit;
|
||
}
|
||
|
||
.fileChipPreview:disabled {
|
||
cursor: default;
|
||
}
|
||
|
||
.fileChipRemove {
|
||
position: absolute;
|
||
top: 8px;
|
||
right: 8px;
|
||
z-index: 1;
|
||
width: 20px;
|
||
height: 20px;
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.searchPanel {
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: calc(100% + 8px - var(--chat-editor-search-shift, 0px));
|
||
left: 0;
|
||
z-index: 20;
|
||
max-height: min(320px, 50vh, var(--chat-editor-search-room, 320px));
|
||
padding: 8px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 12px;
|
||
background: var(--background);
|
||
box-shadow: 0 8px 24px color-mix(in srgb, #000 12%, transparent);
|
||
cursor: default;
|
||
}
|
||
|
||
.searchBar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 2px 2px 6px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.searchLabel {
|
||
color: var(--chat-editor-text-dimmed);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.searchInput {
|
||
flex: 1;
|
||
background: transparent;
|
||
border: none;
|
||
outline: none;
|
||
color: var(--chat-editor-text-primary);
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.searchInput::placeholder {
|
||
color: var(--chat-editor-text-dimmed);
|
||
}
|
||
|
||
.searchResults {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
min-height: 0;
|
||
padding: 2px 0 0;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
}
|
||
|
||
.searchResult {
|
||
display: grid;
|
||
grid-template-columns: 12px minmax(0, 1fr);
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
flex: none;
|
||
height: 30px;
|
||
padding: 5px 8px;
|
||
overflow: hidden;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--chat-editor-text-secondary);
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
text-align: left;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.searchResult:hover,
|
||
.searchResultActive {
|
||
background: var(--chat-editor-bg-tertiary);
|
||
color: var(--chat-editor-accent-color);
|
||
}
|
||
|
||
.searchResultMarker {
|
||
color: var(--chat-editor-accent-color);
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.searchResultText {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.searchEmpty {
|
||
padding: 4px 2px 0;
|
||
color: var(--chat-editor-text-dimmed);
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* File upload ----------------------------------------------------------- */
|
||
|
||
.hiddenUploadInput {
|
||
display: none;
|
||
}
|
||
|
||
.uploadStrip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
padding: 0 0 10px;
|
||
}
|
||
|
||
.uploadRow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
max-width: 100%;
|
||
padding: 8px;
|
||
border: 1px solid var(--chat-editor-border-color);
|
||
border-radius: 10px;
|
||
background: var(--chat-editor-bg-primary);
|
||
color: var(--chat-editor-text-primary);
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.uploadRow[data-status='error'] {
|
||
border-color: var(--error-border);
|
||
background: var(--error-bg);
|
||
}
|
||
|
||
.uploadRow[data-previewable='true'] {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.uploadRowPreview:focus-visible {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.uploadRowPreview {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: inherit;
|
||
cursor: inherit;
|
||
font: inherit;
|
||
}
|
||
|
||
.uploadRowIcon {
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.uploadRowName {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
text-align: left;
|
||
}
|
||
|
||
.uploadRowPreview:disabled {
|
||
cursor: default;
|
||
}
|
||
|
||
.uploadRowAction svg,
|
||
.uploadRowSpinner {
|
||
flex: 0 0 auto;
|
||
width: 14px;
|
||
height: 14px;
|
||
}
|
||
|
||
.uploadRowSpinner {
|
||
color: var(--agent-blue-500);
|
||
animation: uploadRowSpin 1s linear infinite;
|
||
}
|
||
|
||
/* Must follow the base rule: equal-specificity cascade resolves to the
|
||
last declaration, so an earlier override would stay dead code. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.uploadRowSpinner {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
@keyframes uploadRowSpin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.uploadRowStatus {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
min-width: 0;
|
||
max-width: 40%;
|
||
flex: 0 0 auto;
|
||
overflow: hidden;
|
||
color: var(--chat-editor-text-dimmed);
|
||
}
|
||
|
||
.uploadRowStatusText {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.uploadRow[data-status='error'] .uploadRowStatus {
|
||
color: var(--error-color);
|
||
}
|
||
|
||
.uploadRowAction {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: 0 0 auto;
|
||
width: 20px;
|
||
height: 20px;
|
||
padding: 0;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--chat-editor-text-dimmed);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.uploadRowAction:hover {
|
||
background: var(--chat-editor-border-color);
|
||
color: var(--chat-editor-text-primary);
|
||
}
|
||
|
||
.uploadDropOverlay {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 5;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
border: 2px dashed var(--chat-editor-accent-color);
|
||
border-radius: 8px;
|
||
background: color-mix(
|
||
in srgb,
|
||
var(--chat-editor-accent-color) 12%,
|
||
transparent
|
||
);
|
||
color: var(--chat-editor-text-primary);
|
||
font-size: 13px;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.fileTag {
|
||
background: var(--chat-editor-bg-primary);
|
||
min-height: 28px;
|
||
border-radius: 8px;
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
}
|
||
|
||
.fileTagIcon {
|
||
flex: 0 0 auto;
|
||
margin-left: 8px;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .container {
|
||
--chat-editor-composer-cap: min(480px, 60dvh);
|
||
--chat-editor-max-height: var(--chat-editor-composer-cap);
|
||
}
|
||
|
||
/* The workspace/Git row lives inside the capped box; without its height added
|
||
back, a soft keyboard leaves the attachments strip almost no room. The
|
||
height is measured because the row wraps on narrow viewports, and the 8px
|
||
is the pull-up the editing row gives up below it. */
|
||
.editorShell[data-web-shell-mobile-composer] .container:has(.mobileContextRow) {
|
||
--chat-editor-max-height: calc(
|
||
var(--chat-editor-composer-cap) +
|
||
var(--chat-editor-context-row-height, 44px) + 8px
|
||
);
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .uploadRowAction,
|
||
.editorShell[data-web-shell-mobile-composer] .toolBtn,
|
||
.editorShell[data-web-shell-mobile-composer] .sendBtn,
|
||
.editorShell[data-web-shell-mobile-composer] .toolbarRight button,
|
||
.editorShell[data-web-shell-mobile-composer] .mobileContextRow button,
|
||
.editorShell[data-web-shell-mobile-composer] .dropdownWrapperCompact {
|
||
min-width: 44px;
|
||
height: 44px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .toolbar,
|
||
.editorShell[data-web-shell-mobile-composer] .toolbarLeft {
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .toolbarLeading {
|
||
max-width: 100%;
|
||
margin-left: 0;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .toolbarRight {
|
||
margin-left: auto;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.mobileEditingActions,
|
||
.mobileContextRow,
|
||
.mobileExpandedHeader {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.mobileContextRow {
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.mobileContextRow > *,
|
||
.mobileContextRow [data-web-shell-git-branch] {
|
||
max-width: 100%;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .mobileContextRow button {
|
||
max-width: 100%;
|
||
flex-shrink: 1;
|
||
}
|
||
|
||
.mobileEditingActions {
|
||
flex-wrap: wrap;
|
||
margin-top: -8px;
|
||
}
|
||
|
||
/* Pulled up under the context row it would cover the row's touch targets. */
|
||
.mobileContextRow + .mobileEditingActions {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.mobileEditingActions svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
.mobileEditingSpacer {
|
||
flex: 1;
|
||
}
|
||
|
||
.mobileExpandedEditor {
|
||
display: flex;
|
||
width: calc(100% - 16px);
|
||
max-width: 720px;
|
||
height: 85dvh;
|
||
max-height: 100%;
|
||
flex-direction: column;
|
||
padding: 12px;
|
||
gap: 8px;
|
||
}
|
||
|
||
.mobileExpandedHeader h2 {
|
||
flex: 1;
|
||
}
|
||
|
||
.mobileExpandedTextarea {
|
||
flex: 1;
|
||
min-height: 0;
|
||
width: 100%;
|
||
resize: none;
|
||
border: 0;
|
||
outline: none;
|
||
background: transparent;
|
||
color: inherit;
|
||
font: inherit;
|
||
font-size: 16px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .uploadRowPreview {
|
||
min-height: 44px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .searchInput {
|
||
min-height: 44px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.editorShell[data-web-shell-mobile-composer] .searchResult {
|
||
min-height: 44px;
|
||
}
|