Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion design-system/packages/ui/src/components/Menu/Menu.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
display: flex;
min-inline-size: 0;
flex-direction: column;
gap: var(--bf-overlay-menu-section-gap);
gap: calc(var(--bf-space-1) / 2);
/* Keep item focus rings inside the scroll viewport on every edge. */
padding: var(--bf-focus-width);
}
Expand All @@ -56,6 +56,14 @@
flex-direction: column;
}

.section {
gap: var(--bf-overlay-menu-section-gap);
}

.items {
gap: calc(var(--bf-space-1) / 2);
}

.heading {
display: flex;
align-items: center;
Expand Down Expand Up @@ -115,6 +123,10 @@
flex: 0 0 auto;
inline-size: 100%;
block-size: var(--bf-border-width-default);
margin-block: calc(
var(--bf-overlay-menu-section-gap)
- calc(var(--bf-space-1) / 2)
);
background: var(--bf-color-border-subtle);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
gap: var(--bf-layout-navigation-panel-section-gap);
}

.items {
gap: calc(var(--bf-space-1) / 2);
}

.heading {
display: flex;
align-items: center;
Expand Down
3 changes: 3 additions & 0 deletions design-system/packages/ui/tests/menu.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ test("Menu styling uses only public surface, geometry, action, and scrollbar tok

assert.match(styles, /--bf-overlay-menu-inline-size/);
assert.match(styles, /--bf-overlay-menu-item-height/);
assert.match(styles, /\.list\s*\{[^}]*gap: calc\(var\(--bf-space-1\) \/ 2\)/);
assert.match(styles, /\.items\s*\{[^}]*gap: calc\(var\(--bf-space-1\) \/ 2\)/);
assert.match(styles, /\.separator\s*\{[^}]*margin-block: calc\(/);
assert.match(styles, /--bf-color-surface-panel/);
assert.match(styles, /--bf-shadow-menu/);
assert.match(styles, /--bf-overlay-menu-scrollbar-gap/);
Expand Down
1 change: 1 addition & 0 deletions design-system/packages/ui/tests/navigation-panel.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ test("NavigationPanel styling reuses shared action and scrollbar contracts", asy

assert.match(styles, /--bf-layout-navigation-panel-inline-size/);
assert.match(styles, /--bf-layout-navigation-panel-footer-height/);
assert.match(styles, /\.items\s*\{[^}]*gap: calc\(var\(--bf-space-1\) \/ 2\)/);
assert.match(styles, /--bf-color-surface-subtle/);
assert.match(styles, /--bf-color-action-neutral-surface-pressed/);
assert.match(styles, /aria-current/);
Expand Down
16 changes: 8 additions & 8 deletions src/web-ui/src/app/components/NavPanel/NavPanel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,7 @@ $_section-header-height: 22px;
min-width: 0;
max-width: 100%;
padding: 2px $size-gap-1;
gap: 2px;
gap: calc(var(--bf-space-1) / 2);

// Multiple SessionsSection siblings: match vertical rhythm of __workspace-list (gap 0 + 2px row padding).
&--session-blocks {
Expand Down Expand Up @@ -2373,7 +2373,7 @@ $_section-header-height: 22px;
position: relative;
display: flex;
flex-direction: column;
gap: 1px;
gap: calc(var(--bf-space-1) / 2);
padding: $size-gap-2 $size-gap-2 calc(#{$size-gap-4} * 0.75);
flex-shrink: 0;

Expand All @@ -2392,15 +2392,15 @@ $_section-header-height: 22px;
.bitfun-nav-panel__top-action-expand {
display: flex;
flex-direction: column;
gap: 1px;
gap: calc(var(--bf-space-1) / 2);
width: 100%;
min-width: 0;
}

.bitfun-nav-panel__top-action-sublist {
display: flex;
flex-direction: column;
gap: 1px;
gap: calc(var(--bf-space-1) / 2);
overflow: hidden;
max-height: 0;
opacity: 0;
Expand Down Expand Up @@ -2596,7 +2596,7 @@ $_section-header-height: 22px;
}

&__top-actions {
gap: 2px;
gap: calc(var(--bf-space-1) / 2);
padding: 5px 6px $size-gap-3;
margin-bottom: $size-gap-3;
}
Expand Down Expand Up @@ -2648,11 +2648,11 @@ $_section-header-height: 22px;
}

&__top-action-expand {
gap: 0;
gap: calc(var(--bf-space-1) / 2);
}

&__top-action-sublist {
gap: 0;
gap: calc(var(--bf-space-1) / 2);
transition: none;
}

Expand Down Expand Up @@ -2772,7 +2772,7 @@ $_section-header-height: 22px;
}

&__items {
gap: 0;
gap: calc(var(--bf-space-1) / 2);
padding: 2px 6px;
}

Expand Down
5 changes: 5 additions & 0 deletions src/web-ui/src/app/components/NavPanel/NavPanelLayout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ describe('NavPanel layout styles', () => {
const stylesheet = readNavPanelStylesheet();
const sectionHeaderBlock = extractBlock(stylesheet, '&__section-header');
const itemsBlock = extractBlock(stylesheet, '&__items');
const topActionExpandBlock = extractBlock(stylesheet, '&__top-action-expand');
const topActionSublistBlock = extractBlock(stylesheet, '&__top-action-sublist');

expect(itemsBlock).toContain('padding: 2px $size-gap-1;');
expect(itemsBlock).toContain('gap: calc(var(--bf-space-1) / 2);');
expect(topActionExpandBlock).toContain('gap: calc(var(--bf-space-1) / 2);');
expect(topActionSublistBlock).toContain('gap: calc(var(--bf-space-1) / 2);');
expect(sectionHeaderBlock).toContain('margin: 0 $size-gap-1;');
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@
min-width: 0;
max-width: 100%;
padding: 2px $size-gap-1 2px;
gap: 0;
// No vertical margin: spacing between assistant blocks comes from 2px top/bottom padding only
// (see .bitfun-nav-panel__items--session-blocks gap: 0), aligned with __workspace-item padding.
gap: calc(var(--bf-space-1) / 2);
// No vertical margin: the list owns the separation between adjacent row surfaces.
margin: 0 $size-gap-1 0 calc(#{$size-gap-1} + 4px);

&.is-flat-workspace-view {
Expand Down Expand Up @@ -147,7 +146,7 @@
}

&.is-child {
margin-top: -2px;
margin-top: 0;
min-height: 24px;
font-size: var(--bf-font-size-xs);
padding-left: calc(var(--bf-nav-session-rail) + 14px);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ describe('SessionsSection layout styles', () => {

expect(inlineListBlock).toContain('padding: 2px $size-gap-1 2px;');
expect(inlineListBlock).toContain('margin: 0 $size-gap-1 0 calc(#{$size-gap-1} + 4px);');
expect(inlineListBlock).toContain('gap: 0;');
expect(inlineListBlock).toContain('gap: calc(var(--bf-space-1) / 2);');
expect(inlineItemBlock).toContain('height: 26px;');
expect(stylesheet).toContain('margin-top: -2px;');
expect(stylesheet).toContain('margin-top: 0;');
});

it('keeps hidden session row actions from reserving title width', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
min-width: 0;
max-width: 100%;
box-sizing: border-box;
gap: 2px;
gap: calc(var(--bf-space-1) / 2);
min-width: 0;
max-width: 100%;
padding: 2px $size-gap-1;
Expand Down Expand Up @@ -1074,7 +1074,7 @@
min-width: 0;
max-width: 100%;
box-sizing: border-box;
gap: 2px;
gap: calc(var(--bf-space-1) / 2);
min-width: 0;
max-width: 100%;
padding: $size-gap-1;
Expand Down Expand Up @@ -1373,7 +1373,7 @@
}

&__workspace-group {
gap: 0;
gap: calc(var(--bf-space-1) / 2);
}

&__workspace-group-title {
Expand All @@ -1397,7 +1397,7 @@

&__workspace-item,
&__assistant-item {
gap: 0;
gap: calc(var(--bf-space-1) / 2);
padding: 0;
border-radius: $size-radius-base;
transition:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe('WorkspaceListSection layout styles', () => {
expect(workspaceGroup).toContain('min-width: 0;');
expect(workspaceItem).toContain('min-width: 0;');
expect(workspaceItem).toContain('max-width: 100%;');
expect(workspaceItem).toContain('gap: calc(var(--bf-space-1) / 2);');
expect(workspaceCard).toContain('max-width: 100%;');
expect(workspaceCard).toContain('overflow: hidden;');
expect(workspaceIcon).toContain('width: 16px;');
Expand All @@ -88,6 +89,7 @@ describe('WorkspaceListSection layout styles', () => {

expect(assistantItem).toContain('min-width: 0;');
expect(assistantItem).toContain('max-width: 100%;');
expect(assistantItem).toContain('gap: calc(var(--bf-space-1) / 2);');
expect(assistantCard).toContain('max-width: 100%;');
expect(assistantCard).toContain('min-height: 30px;');
expect(assistantCard).toContain('overflow: hidden;');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export const harnessProfileSelectorAppearanceDescriptor: AppearanceSurfaceDescri
{ id: 'menu' },
{ id: 'profile' },
{ id: 'agent' },
{ id: 'newSessionNotice' },
],
facets: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@
color: currentColor;
}

// One surface swaps between the fixed-Session summary, Harness profiles, and
// the second-level Agent list.
// One surface swaps between Harness profiles and the second-level Agent list.
.bitfun-harness-selector__menu {
position: fixed;
z-index: tokens.$z-popover;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ vi.mock('@/infrastructure/appearance/runtime/AppearanceOverlayHost', () => ({
getAppearanceOverlayHost: () => document.body,
}));

const confirmation = vi.hoisted(() => ({ dialog: vi.fn(async () => true) }));

vi.mock('@/infrastructure/confirm-dialog', () => ({
confirmDialog: confirmation.dialog,
}));

const notify = vi.hoisted(() => ({ info: vi.fn() }));

vi.mock('@/shared/notification-system', () => ({
Expand Down Expand Up @@ -369,7 +375,7 @@ describe('HarnessProfileSelector', () => {
expect(document.querySelector('.bitfun-harness-selector__menu')).toBeNull();
});

it('opens new-Session profile choices directly for a started Session', async () => {
it('confirms a new Session after a profile choice in a started Session', async () => {
const onSelectProfile = vi.fn();
const onStartNewSession = vi.fn();
await act(async () => {
Expand Down Expand Up @@ -398,9 +404,7 @@ describe('HarnessProfileSelector', () => {
const menu = document.querySelector<HTMLElement>('.bitfun-harness-selector__menu');
expect(menu).not.toBeNull();
expect(menu?.dataset.bfPage).toBe('profiles');
expect(
menu?.querySelector('[data-testid="harness-new-session-notice"]')?.textContent,
).toBe('chatInput.harness.selectionCreatesNewSession');
expect(menu?.querySelector('[data-testid="harness-new-session-notice"]')).toBeNull();
expect(menu?.querySelector('[data-testid="harness-session-summary"]')).toBeNull();
expect(menu?.querySelector('[data-testid="harness-start-new-session"]')).toBeNull();
expect(
Expand All @@ -417,6 +421,11 @@ describe('HarnessProfileSelector', () => {
?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
expect(onSelectProfile).not.toHaveBeenCalled();
expect(confirmation.dialog).toHaveBeenCalledWith({
title: 'chatInput.harness.newSessionConfirmation.title',
message: 'chatInput.harness.newSessionConfirmation.message',
confirmText: 'chatInput.harness.newSessionConfirmation.confirm',
});
expect(onStartNewSession).toHaveBeenCalledWith(
{ kind: 'profile', id: 'minimal' },
);
Expand Down Expand Up @@ -464,11 +473,45 @@ describe('HarnessProfileSelector', () => {
?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
expect(onSelectAgent).not.toHaveBeenCalled();
expect(confirmation.dialog).toHaveBeenCalledWith({
title: 'chatInput.harness.newSessionConfirmation.title',
message: 'chatInput.harness.newSessionConfirmation.message',
confirmText: 'chatInput.harness.newSessionConfirmation.confirm',
});
expect(onStartNewSession).toHaveBeenCalledWith(
{ kind: 'agent', id: 'Cowork' },
);
});

it('keeps the current Session unchanged when new-Session confirmation is cancelled', async () => {
confirmation.dialog.mockResolvedValueOnce(false);
const onStartNewSession = vi.fn();
await act(async () => {
root.render(
<HarnessProfileSelector
sessionStarted
selectedProfile="balanced"
onSelectProfile={vi.fn()}
onStartNewSession={onStartNewSession}
/>,
);
});

await act(async () => {
container.querySelector<HTMLButtonElement>('[data-testid="harness-profile-selector"]')
?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
await act(async () => {
document.querySelector<HTMLButtonElement>('[data-testid="harness-profile-ultimate"]')
?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
await Promise.resolve();
});

expect(confirmation.dialog).toHaveBeenCalledTimes(1);
expect(onStartNewSession).not.toHaveBeenCalled();
expect(document.querySelector('.bitfun-harness-selector__menu')).toBeNull();
});

it.each(['creative'] as const)(
'presents a persisted %s profile as active',
async (profileId) => {
Expand Down
Loading
Loading