Files
openidea/src/workbench/toolwindows/ToolWindows.module.css
T
l0ng-ai 08fd134bdb fix(workbench): Keep tool window resizing smooth with a long agent conversation (#133)
Dragging a tool window edge set an inherited custom property on the tool window
grid, so every pointer move restyled the whole workbench below it. With a long
agent conversation open each move cost 150-400 ms. The grid track variables are
now registered as non-inherited, and the side splitter sets flex-grow on its two
areas instead of inherited variables on the side.
2026-10-28 17:59:09 +08:00

458 lines
9.6 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
.frame {
--stripe-button-radius: var(--radius-popup);
flex: 1;
display: flex;
min-height: 0;
min-width: 0;
}
/* No right stripe: keep the window-edge gap the stripe would have provided. */
.frameNoRight {
padding-right: var(--island-gap);
}
.frameNoLeft {
padding-left: var(--island-gap);
}
/*
* Only the grid reads its track variables. Not inherited, so dragging a tool window
* edge restyles the grid alone instead of every node in the workbench below it.
*/
@property --tw-left {
syntax: "*";
inherits: false;
}
@property --tw-right {
syntax: "*";
inherits: false;
}
@property --tw-bottom {
syntax: "*";
inherits: false;
}
@property --gap-left {
syntax: "*";
inherits: false;
}
@property --gap-right {
syntax: "*";
inherits: false;
}
@property --gap-bottom {
syntax: "*";
inherits: false;
}
.grid {
flex: 1;
display: grid;
min-width: 0;
min-height: 0;
/* Track sizes come from ToolWindowLayout; the editor keeps 200 × 60 px and side tracks give way first. */
grid-template-columns:
minmax(0, var(--tw-left, 0px)) var(--gap-left, 0px) minmax(200px, 1fr) var(--gap-right, 0px)
minmax(0, var(--tw-right, 0px));
grid-template-rows: minmax(60px, 1fr) var(--gap-bottom, 0px) minmax(0, var(--tw-bottom, 0px));
grid-template-areas:
"left gap-left editor gap-right right"
"gap-bottom gap-bottom gap-bottom gap-bottom gap-bottom"
"bottom bottom bottom bottom bottom";
}
.widescreen {
grid-template-areas:
"left gap-left editor gap-right right"
"left gap-left gap-bottom gap-right right"
"left gap-left bottom gap-right right";
}
.grid > [data-anchor="left"] {
grid-area: left;
}
.grid > [data-anchor="right"] {
grid-area: right;
}
.grid > [data-anchor="bottom"] {
grid-area: bottom;
}
.editorIsland {
grid-area: editor;
}
/* A side: primary and split areas, each an island; stacked on the left and right, side by side at the bottom. */
.side {
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
}
.sideRow {
flex-direction: row;
}
.island[hidden] {
display: none;
}
.side > .splitGap {
flex: none;
height: var(--island-gap);
}
.sideRow > .splitGap {
height: auto;
width: var(--island-gap);
}
/* Islands: rounded surfaces on the frame background, separated by gaps. */
.island {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
overflow: hidden;
background: var(--bg-island);
border-radius: var(--island-radius);
}
/* Resize handles live in the gaps; a hidden neighbour takes no gap. */
.gap {
position: relative;
outline: none;
}
.gap[data-gap="left"] {
grid-area: gap-left;
}
.gap[data-gap="right"] {
grid-area: gap-right;
}
.gap[data-gap="bottom"] {
grid-area: gap-bottom;
}
.gap[aria-orientation="vertical"] {
cursor: col-resize;
}
.gap[aria-orientation="horizontal"] {
cursor: row-resize;
}
/* A slightly wider hit area than the gap itself. */
.gap::before {
content: "";
position: absolute;
z-index: var(--z-panel);
}
.gap[aria-orientation="vertical"]::before {
inset: 0 -2px;
}
.gap[aria-orientation="horizontal"]::before {
inset: -2px 0;
}
/* Keyboard focus only: a mouse drag never focuses the handle. */
.gap:focus-visible::after {
content: "";
position: absolute;
background: color-mix(in srgb, var(--accent) 45%, transparent);
border-radius: 2px;
}
.gap[aria-orientation="vertical"]::after {
inset: 12px 2px;
}
.gap[aria-orientation="horizontal"]::after {
inset: 2px 12px;
}
/* Stripes */
.stripe {
/* --stripe-drag: the width under the pointer while the stripe edge is dragged. */
--stripe-button: var(--stripe-drag, 32px);
/* Outer padding: icons only sit centred between equal margins. */
--stripe-pad: var(--island-gap);
width: calc(var(--stripe-button) + var(--stripe-pad) + var(--island-gap));
container-type: inline-size;
flex: none;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: 0 0 6px;
/* Transparent: stripes sit on the window frame so the project-colour wash flows from the title bar. */
background: transparent;
/* Short windows: scroll the buttons instead of clipping the last ones. */
overflow-x: hidden;
overflow-y: auto;
scrollbar-width: none;
}
.stripe::-webkit-scrollbar {
display: none;
}
.stripeWithNames {
--stripe-button: var(--stripe-drag, var(--stripe-button-w, 56px));
--stripe-pad: 4px;
gap: 4px;
}
.stripeSideLeft {
padding-left: var(--stripe-pad);
padding-right: var(--island-gap);
}
.stripeSideRight {
padding-left: var(--island-gap);
padding-right: var(--stripe-pad);
}
/* Drag the stripe's inner edge: wider shows names, narrow shows icons only. Hit area covers the stripe's gap. */
.stripeHandle {
position: relative;
flex: none;
width: 0;
outline: none;
cursor: col-resize;
}
.stripeHandle::before {
content: "";
position: absolute;
inset: 0 0 0 calc(-1 * var(--island-gap));
z-index: var(--z-panel);
}
.stripeHandleRight::before {
inset: 0 calc(-1 * var(--island-gap)) 0 0;
}
.stripeHandle:focus-visible::before {
inset-block: 12px;
margin-inline: 2px;
background: color-mix(in srgb, var(--accent) 45%, transparent);
border-radius: 2px;
}
.stripeSpacer {
flex: 1;
}
.stripeGroup {
flex: none;
display: flex;
flex-direction: column;
align-items: center;
gap: inherit;
}
/* Between a stripe group's primary and split windows. */
.stripeSeparator {
flex: none;
width: 16px;
height: 1px;
margin: 2px 0;
background: var(--border);
}
.stripeButton {
position: relative;
flex: none;
width: 32px;
height: 32px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
border-radius: var(--stripe-button-radius);
color: var(--fg-muted);
transition:
background var(--dur-fast) var(--ease),
color var(--dur-fast) var(--ease);
}
.stripeNamed {
width: var(--stripe-button);
height: auto;
min-height: 44px;
padding: 6px 2px 5px;
}
.stripeMore {
height: 30px;
}
.stripeWithNames .stripeMore {
width: var(--stripe-button);
}
.stripeButton:hover,
.stripeButton[aria-expanded="true"] {
background: var(--bg-stripe-hover);
color: var(--fg-secondary);
}
.stripeActive,
.stripeActive:hover {
background: var(--bg-stripe-selected);
color: var(--fg-emphasis);
}
.stripeFocused,
.stripeFocused:hover {
background: var(--bg-button-primary);
color: var(--fg-button-primary);
--icon-color: currentColor;
}
.stripeButton:focus-visible {
outline-offset: -2px;
}
.stripeIcon {
position: relative;
display: flex;
}
.stripeLabel {
max-width: 100%;
font-size: var(--fs-stripe-label);
line-height: 1.2;
color: var(--fg-stripe-label);
text-align: center;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow-wrap: normal;
word-break: keep-all;
}
/* A single long word can't wrap: end it with an ellipsis instead of clipping mid-letter. */
.stripeLabelWord {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
}
.stripeActive .stripeLabel {
color: inherit;
}
/* Narrow stripe: one line, truncated. */
@container (max-width: 62px) {
.stripeLabel {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.badge {
position: absolute;
top: -5px;
left: 10px;
min-width: 13px;
height: 13px;
padding: 0 3px;
border-radius: 7px;
background: var(--bg-active);
color: var(--fg-emphasis);
font-family: var(--font-ui);
font-size: 9px;
font-weight: 600;
line-height: 13px;
text-align: center;
pointer-events: none;
}
/* Anchored panels */
.window {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
outline: none;
}
.window[hidden] {
display: none;
}
.header {
height: var(--h-header);
flex: none;
display: flex;
align-items: center;
gap: 6px;
padding: 0 6px 0 8px;
color: var(--fg-emphasis);
}
.headerTabbed {
border-bottom: 1px solid var(--border);
}
.title {
flex: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 600;
color: var(--fg-strong);
}
.titlePlain {
padding: 0 4px;
}
.titleButton {
flex: none;
height: 24px;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 0 4px;
border-radius: var(--radius-small);
transition: background var(--dur-fast) var(--ease);
}
.titleButton:hover,
.titleButton[aria-expanded="true"] {
background: var(--bg-toolbar-hover);
}
.titleChevron {
flex: none;
color: var(--fg-muted);
}
.headerContent {
min-width: 0;
display: flex;
align-items: center;
margin-left: 6px;
overflow: hidden;
}
.headerContentOnly {
margin-left: 0;
}
.headerSpacer {
flex: 1;
}
.headerActions {
display: flex;
align-items: center;
gap: 2px;
opacity: 0;
transition: opacity var(--dur) var(--ease);
}
.window:hover .headerActions,
.window:focus-within .headerActions,
.headerActions:has([aria-expanded="true"]) {
opacity: 1;
}
.body {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
min-width: 0;
}
/* stripe drag & drop */
.stripeSlot {
position: relative;
flex: none;
display: flex;
flex-direction: column;
}
.stripeDrop {
position: absolute;
top: -3px;
left: 6px;
right: 6px;
height: 2px;
border-radius: 1px;
background: var(--accent);
pointer-events: none;
}
.stripeDragged {
opacity: 0.4;
}
.stripeGhost {
position: fixed;
z-index: 1000;
margin: 0;
pointer-events: none;
background: var(--bg-popup);
border-radius: var(--radius-toolbar-button);
box-shadow: var(--shadow-popup);
opacity: 0.9;
}
/* Settings › Appearance › "Different tool window background". */
:global(:root[data-tool-window-bg="different"]) .anchor {
background: var(--bg-elev);
}