mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-20 16:23:02 +01:00
Improve border radius of input stack (#330852)
* Improve border radius of input stack * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI
parent
d144109c7f
commit
edf4ef0416
@@ -1001,6 +1001,8 @@
|
||||
"--prompt-timeline-gutter-dot-gap",
|
||||
"--prompt-timeline-gutter-more-height",
|
||||
"--chat-input-notice-severity",
|
||||
"--chat-input-notice-radius",
|
||||
"--chat-input-own-radius",
|
||||
"--chat-input-notification-line-height",
|
||||
"--chat-editing-last-edit-shift",
|
||||
"--chat-voice-icon-glow-color",
|
||||
@@ -1148,6 +1150,7 @@
|
||||
"--chat-input-anim-angle",
|
||||
"--chat-input-anim-duration",
|
||||
"--chat-input-radius",
|
||||
"--chat-input-stack-radius-outer",
|
||||
"--chat-input-stack-radius-top",
|
||||
"--chat-input-working-border-color1",
|
||||
"--session-input-banner-anim-angle",
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--vscode-input-border, transparent);
|
||||
border-bottom: none;
|
||||
border-top-left-radius: var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-small));
|
||||
border-top-right-radius: var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-small));
|
||||
/* Follows the run it docks into; the fallback is its own tier outside one. */
|
||||
border-top-left-radius: var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-large));
|
||||
border-top-right-radius: var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-large));
|
||||
background-color: color-mix(in srgb, var(--vscode-focusBorder) 6%, var(--vscode-editorWidget-background));
|
||||
color: var(--vscode-foreground);
|
||||
font-size: var(--vscode-agents-fontSize-label1);
|
||||
|
||||
@@ -80,10 +80,12 @@
|
||||
}
|
||||
|
||||
/* A card closes its own box and sits clear of the input, so it reports itself
|
||||
standalone and the input below keeps its own frame. */
|
||||
standalone and the input below keeps its own frame. Its bottom corners are its
|
||||
own, so they take the run's radius directly rather than through the stack. */
|
||||
.chat-input-notice.chat-input-notice-onboarding {
|
||||
--chat-input-notice-radius: var(--chat-input-stack-radius-outer, var(--vscode-cornerRadius-large));
|
||||
border-bottom: var(--vscode-strokeThickness) solid var(--vscode-input-border, var(--vscode-widget-border, transparent));
|
||||
border-radius: var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-large)) var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-large)) var(--vscode-cornerRadius-large) var(--vscode-cornerRadius-large);
|
||||
border-radius: var(--chat-input-stack-radius-top, var(--chat-input-notice-radius)) var(--chat-input-stack-radius-top, var(--chat-input-notice-radius)) var(--chat-input-notice-radius) var(--chat-input-notice-radius);
|
||||
margin-bottom: var(--vscode-spacing-size40);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,36 @@
|
||||
|
||||
.chat-input-stack {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* Reset first, so a nested stack does not inherit its container's squaring. */
|
||||
.chat-input-stack > * {
|
||||
--chat-input-stack-radius-top: initial;
|
||||
/*
|
||||
* The outer radius of the run: how round the joined surface is on the
|
||||
* corners it does not share with a neighbour.
|
||||
*
|
||||
* The input owns this value - a denser host rounds it tighter - but a custom
|
||||
* property cannot travel from the input back up to the members docked above
|
||||
* it, so the input part declares `--chat-input-radius` (see `chat.css`) and
|
||||
* the stack republishes it here for the whole run to read. The fallback
|
||||
* covers a stack used outside a chat input part.
|
||||
*/
|
||||
--chat-input-stack-radius-outer: var(--chat-input-radius, var(--vscode-cornerRadius-large));
|
||||
}
|
||||
|
||||
/*
|
||||
* Members read `--chat-input-stack-radius-top` for their top corners. It is
|
||||
* unset outside a stack, so a notice used elsewhere keeps its own rounding.
|
||||
* What the stack dictates for a member's top corners: the run's outer radius for
|
||||
* the member that opens the run, and a squared edge for every member that
|
||||
* continues one. Reset here so a nested stack does not inherit its container's
|
||||
* squaring; the rule below re-squares the ones that continue.
|
||||
*
|
||||
* Members already read this with their own value as the fallback, so a member
|
||||
* used outside a stack keeps its own rounding and one inside a run follows the
|
||||
* run - no member has to know which input it is docked to.
|
||||
*/
|
||||
.chat-input-stack > * {
|
||||
--chat-input-stack-radius-top: var(--chat-input-stack-radius-outer);
|
||||
}
|
||||
|
||||
/*
|
||||
* A member that continues the run meets the one above it, so it squares the
|
||||
* shared edge.
|
||||
*/
|
||||
.chat-input-stack > .chat-input-stack-continues {
|
||||
--chat-input-stack-radius-top: 0px;
|
||||
|
||||
@@ -1078,18 +1078,45 @@ have to be updated for changes to the rules above, or to support more deeply nes
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/*
|
||||
* How round the chat input run is.
|
||||
*
|
||||
* Declared on the part that wraps the whole stack rather than on the input
|
||||
* itself. Everything docked above the input joins it into a single surface, so
|
||||
* those members have to round their outer corners to the same value - and a
|
||||
* custom property only reaches them from a shared ancestor. `chatInputStack.css`
|
||||
* republishes this as the run's outer radius; members read it from there.
|
||||
*
|
||||
* The three rules below are ordered, not just specific: `.compact` and the
|
||||
* quick input scope both match at the same specificity and a compact input
|
||||
* inside the quick input widget must round like a compact one, so `.compact`
|
||||
* comes last. Keep them adjacent and in this order.
|
||||
*/
|
||||
.interactive-session .interactive-input-part {
|
||||
--chat-input-radius: var(--vscode-cornerRadius-large);
|
||||
}
|
||||
|
||||
.quick-input-widget .interactive-session .interactive-input-part {
|
||||
--chat-input-radius: var(--vscode-cornerRadius-xSmall);
|
||||
}
|
||||
|
||||
.interactive-session .interactive-input-part.compact {
|
||||
--chat-input-radius: var(--vscode-cornerRadius-small);
|
||||
}
|
||||
|
||||
.monaco-workbench .interactive-session .chat-input-container {
|
||||
box-sizing: border-box;
|
||||
cursor: text;
|
||||
background-color: var(--vscode-input-background);
|
||||
border: 1px solid var(--vscode-input-border, transparent);
|
||||
/* Top corners follow the stack, so a docked widget can square them. */
|
||||
--chat-input-radius: var(--vscode-cornerRadius-large);
|
||||
/* Top corners follow the stack, so a docked widget can square them. The
|
||||
fallback keeps an input rendered outside a chat input part rounded. */
|
||||
--chat-input-own-radius: var(--chat-input-radius, var(--vscode-cornerRadius-large));
|
||||
border-radius:
|
||||
var(--chat-input-stack-radius-top, var(--chat-input-radius))
|
||||
var(--chat-input-stack-radius-top, var(--chat-input-radius))
|
||||
var(--chat-input-radius)
|
||||
var(--chat-input-radius);
|
||||
var(--chat-input-stack-radius-top, var(--chat-input-own-radius))
|
||||
var(--chat-input-stack-radius-top, var(--chat-input-own-radius))
|
||||
var(--chat-input-own-radius)
|
||||
var(--chat-input-own-radius);
|
||||
padding: 0 6px 6px 6px;
|
||||
/* top padding is inside the editor widget */
|
||||
width: 100%;
|
||||
@@ -1769,7 +1796,6 @@ have to be updated for changes to the rules above, or to support more deeply nes
|
||||
padding-bottom: 0;
|
||||
/* no scrollbar */
|
||||
padding-right: 6px;
|
||||
--chat-input-radius: var(--vscode-cornerRadius-small);
|
||||
}
|
||||
|
||||
.interactive-session .interactive-input-and-side-toolbar {
|
||||
@@ -2585,7 +2611,6 @@ have to be updated for changes to the rules above, or to support more deeply nes
|
||||
|
||||
.quick-input-widget .interactive-session .chat-input-container {
|
||||
margin: 0;
|
||||
--chat-input-radius: var(--vscode-cornerRadius-xSmall);
|
||||
padding: 0 4px 0 6px;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
}
|
||||
|
||||
.chat-input-stack > .chat-input-notification-container.chat-input-stack-docked ~ .interactive-input-and-side-toolbar {
|
||||
--chat-input-stack-radius-top: var(--vscode-cornerRadius-large);
|
||||
--chat-input-stack-radius-top: var(--chat-input-stack-radius-outer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,52 @@ import { chatInputStackClass, chatInputStackSlotClass, ChatInputStackSlot, setCh
|
||||
import { ComponentFixtureContext, defineComponentFixture, defineThemedFixtureGroup } from '../fixtureUtils.js';
|
||||
|
||||
/**
|
||||
* The three notice variants side by side. Five producers share one frame, and
|
||||
* the only way to see that they still agree - and that severity only tints what
|
||||
* it is meant to - is to put the variants next to each other at one width.
|
||||
* Mounts one notice above a stand-in chat input, inside a real stack.
|
||||
*
|
||||
* The notice is wrapped in a slot that reports its state, so the squared join
|
||||
* between the two - and the rounding they share - is produced by the stack
|
||||
* rather than hardcoded here, and this breaks if that mechanism does.
|
||||
*/
|
||||
function renderNotices(context: ComponentFixtureContext): void {
|
||||
function addNoticeStack(context: ComponentFixtureContext, variant: ChatInputNoticeVariant, className: string, severity?: string): ChatInputNoticeWidget {
|
||||
const { container, disposableStore } = context;
|
||||
const stack = dom.append(container, dom.$(`.${chatInputStackClass}`));
|
||||
stack.style.display = 'flex';
|
||||
stack.style.flexDirection = 'column';
|
||||
|
||||
const slot = dom.append(stack, dom.$(`.${chatInputStackSlotClass}`));
|
||||
const notice = disposableStore.add(new ChatInputNoticeWidget({
|
||||
container: slot,
|
||||
variant,
|
||||
className,
|
||||
ariaLabel: className,
|
||||
}));
|
||||
if (severity) {
|
||||
notice.domNode.classList.add(severity);
|
||||
}
|
||||
|
||||
const input = dom.append(stack, dom.$('div'));
|
||||
input.textContent = 'Chat input';
|
||||
// Match the real input's host radius and square its top corners when docked.
|
||||
input.style.cssText = 'padding:10px;color:var(--vscode-descriptionForeground);'
|
||||
+ 'background:var(--vscode-agentsChatInput-background, var(--vscode-input-background));'
|
||||
+ 'border:var(--vscode-strokeThickness) solid var(--vscode-input-border);'
|
||||
+ 'border-radius:'
|
||||
+ 'var(--chat-input-stack-radius-top, var(--chat-input-radius, var(--vscode-cornerRadius-large)))'
|
||||
+ ' var(--chat-input-stack-radius-top, var(--chat-input-radius, var(--vscode-cornerRadius-large)))'
|
||||
+ ' var(--chat-input-radius, var(--vscode-cornerRadius-large))'
|
||||
+ ' var(--chat-input-radius, var(--vscode-cornerRadius-large));';
|
||||
|
||||
// Reported last, once the input is in the stack: reporting a slot rescans the
|
||||
// stack's children, so doing it before the input exists would leave the input
|
||||
// unmarked and its top corners rounded under a docked notice.
|
||||
setChatInputStackSlot(slot, variant === ChatInputNoticeVariant.Onboarding
|
||||
? ChatInputStackSlot.Standalone
|
||||
: ChatInputStackSlot.Docked);
|
||||
|
||||
return notice;
|
||||
}
|
||||
|
||||
function prepareContainer(container: HTMLElement): void {
|
||||
container.classList.add('monaco-workbench');
|
||||
container.style.width = '320px';
|
||||
container.style.padding = '24px';
|
||||
@@ -24,65 +64,28 @@ function renderNotices(context: ComponentFixtureContext): void {
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
container.style.background = 'var(--vscode-editor-background)';
|
||||
}
|
||||
|
||||
const addNotice = (variant: ChatInputNoticeVariant, className: string, severity?: string) => {
|
||||
// Each notice sits in a real stack above a stand-in for the chat input, and
|
||||
// the notice is wrapped in a slot that reports its state. The squared top
|
||||
// corner on the input below is therefore produced by the stack rather than
|
||||
// hardcoded here, so this breaks if the mechanism does.
|
||||
const stack = dom.append(container, dom.$(`.${chatInputStackClass}`));
|
||||
stack.style.display = 'flex';
|
||||
stack.style.flexDirection = 'column';
|
||||
|
||||
const slot = dom.append(stack, dom.$(`.${chatInputStackSlotClass}`));
|
||||
const notice = disposableStore.add(new ChatInputNoticeWidget({
|
||||
container: slot,
|
||||
variant,
|
||||
className,
|
||||
ariaLabel: className,
|
||||
}));
|
||||
if (severity) {
|
||||
notice.domNode.classList.add(severity);
|
||||
}
|
||||
setChatInputStackSlot(slot, variant === ChatInputNoticeVariant.Onboarding
|
||||
? ChatInputStackSlot.Standalone
|
||||
: ChatInputStackSlot.Docked);
|
||||
|
||||
const input = dom.append(stack, dom.$('div'));
|
||||
input.textContent = 'Chat input';
|
||||
// Reads the stack's radius the way a real input does: rounded on its own,
|
||||
// squared on top while something is docked above it.
|
||||
input.style.cssText = 'padding:10px;color:var(--vscode-descriptionForeground);'
|
||||
+ 'background:var(--vscode-agentsChatInput-background, var(--vscode-input-background));'
|
||||
+ 'border:var(--vscode-strokeThickness) solid var(--vscode-input-border);'
|
||||
+ 'border-radius:'
|
||||
+ 'var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-large))'
|
||||
+ ' var(--chat-input-stack-radius-top, var(--vscode-cornerRadius-large))'
|
||||
+ ' var(--vscode-cornerRadius-large) var(--vscode-cornerRadius-large);';
|
||||
return notice;
|
||||
};
|
||||
|
||||
// A tip: one row of prose, an icon, and a dismiss that sits in the flow.
|
||||
const tip = addNotice(ChatInputNoticeVariant.Tip, 'fixture-tip');
|
||||
/** A tip: one row of prose, an icon, and a dismiss that sits in the flow. */
|
||||
function addTip(context: ComponentFixtureContext): void {
|
||||
const tip = addNoticeStack(context, ChatInputNoticeVariant.Tip, 'fixture-tip');
|
||||
dom.append(tip.domNode, dom.$(ThemeIcon.asCSSSelector(Codicon.lightbulb)));
|
||||
dom.append(tip.domNode, dom.$('span')).textContent = 'Start a parallel conversation to build on all the changes made in this session.';
|
||||
tip.addDismissAction({ onActivate: () => { } });
|
||||
}
|
||||
|
||||
// A notification, at each severity: same frame, only the tint changes.
|
||||
for (const [severity, icon, message] of [
|
||||
['severity-info', Codicon.info, 'You are approaching your monthly limit.'],
|
||||
['severity-warning', Codicon.warning, 'This model is temporarily unavailable.'],
|
||||
['severity-error', Codicon.error, 'Sign in to keep using chat.'],
|
||||
] as const) {
|
||||
const notification = addNotice(ChatInputNoticeVariant.Notification, 'chat-input-notification-widget', severity);
|
||||
const header = dom.append(notification.domNode, dom.$('.chat-input-notification-header'));
|
||||
dom.append(dom.append(header, dom.$('.chat-input-notification-icon')), dom.$(ThemeIcon.asCSSSelector(icon)));
|
||||
dom.append(header, dom.$('.chat-input-notification-title')).textContent = message;
|
||||
notification.addDismissAction({ parent: header, onActivate: () => { } });
|
||||
}
|
||||
/** A notification: the same frame at every severity, only the tint changes. */
|
||||
function addNotification(context: ComponentFixtureContext, severity: string, icon: ThemeIcon, message: string): void {
|
||||
const notification = addNoticeStack(context, ChatInputNoticeVariant.Notification, 'chat-input-notification-widget', severity);
|
||||
const header = dom.append(notification.domNode, dom.$('.chat-input-notification-header'));
|
||||
dom.append(dom.append(header, dom.$('.chat-input-notification-icon')), dom.$(ThemeIcon.asCSSSelector(icon)));
|
||||
dom.append(header, dom.$('.chat-input-notification-title')).textContent = message;
|
||||
notification.addDismissAction({ parent: header, onActivate: () => { } });
|
||||
}
|
||||
|
||||
// An onboarding card: a stack, with its close pinned to the corner.
|
||||
const card = addNotice(ChatInputNoticeVariant.Onboarding, 'fixture-card');
|
||||
/** An onboarding card: a stack, with its close pinned to the corner. */
|
||||
function addOnboardingCard(context: ComponentFixtureContext): void {
|
||||
const card = addNoticeStack(context, ChatInputNoticeVariant.Onboarding, 'fixture-card');
|
||||
const copy = dom.append(card.domNode, dom.$('div'));
|
||||
copy.style.paddingRight = 'var(--vscode-spacing-size240)';
|
||||
dom.append(copy, dom.$('div')).textContent = 'Welcome to Voice Mode';
|
||||
@@ -90,6 +93,36 @@ function renderNotices(context: ComponentFixtureContext): void {
|
||||
card.addDismissAction({ onActivate: () => { } });
|
||||
}
|
||||
|
||||
/**
|
||||
* The three notice variants side by side. Five producers share one frame, and
|
||||
* the only way to see that they still agree - and that severity only tints what
|
||||
* it is meant to - is to put the variants next to each other at one width.
|
||||
*/
|
||||
function renderNotices(context: ComponentFixtureContext): void {
|
||||
prepareContainer(context.container);
|
||||
|
||||
addTip(context);
|
||||
for (const [severity, icon, message] of [
|
||||
['severity-info', Codicon.info, 'You are approaching your monthly limit.'],
|
||||
['severity-warning', Codicon.warning, 'This model is temporarily unavailable.'],
|
||||
['severity-error', Codicon.error, 'Sign in to keep using chat.'],
|
||||
] as const) {
|
||||
addNotification(context, severity, icon, message);
|
||||
}
|
||||
addOnboardingCard(context);
|
||||
}
|
||||
|
||||
/** Renders notices at the compact input radius, declared inline because this fixture does not load `chat.css`. */
|
||||
function renderNoticesInCompactInput(context: ComponentFixtureContext): void {
|
||||
prepareContainer(context.container);
|
||||
context.container.style.setProperty('--chat-input-radius', 'var(--vscode-cornerRadius-small)');
|
||||
|
||||
addTip(context);
|
||||
addNotification(context, 'severity-info', Codicon.info, 'You are approaching your monthly limit.');
|
||||
addOnboardingCard(context);
|
||||
}
|
||||
|
||||
export default defineThemedFixtureGroup({ path: 'chat/input/' }, {
|
||||
'Chat input notices': defineComponentFixture({ render: renderNotices }),
|
||||
'Chat input notices in a compact input': defineComponentFixture({ render: renderNoticesInCompactInput }),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user