qwen-code/packages/web-shell/client/components/toolbarDropdown.ts
ytahdn f88a8aa6fc
feat(web-shell): use popovers for composer controls (#6877)
* feat(web-shell): use popovers for composer controls

* fix(web-shell): address popover review feedback

* fix(web-shell): update popover regression coverage

* fix(web-shell): address popover review feedback

* fix(web-shell): stabilize toolbar label collapse

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-15 04:54:47 +00:00

117 lines
2.7 KiB
TypeScript

export interface ToolbarDropdownItem {
id: string;
label: string;
searchText?: string;
}
export function filterToolbarDropdownItems<T extends ToolbarDropdownItem>(
items: readonly T[],
query: string,
): T[] {
const normalized = query.trim().toLocaleLowerCase();
if (!normalized) return [...items];
return items.filter((item) =>
`${item.label}\n${item.searchText ?? ''}`
.toLocaleLowerCase()
.includes(normalized),
);
}
export function getToolbarItemVisibility({
availableWidth,
items,
}: {
availableWidth: number;
items: ReadonlyArray<{
id: string;
expansionWidth: number;
ready?: boolean;
}>;
}): Record<string, boolean> {
const visibility = Object.fromEntries(
items.map((item) => [item.id, item.ready !== false]),
);
let usedWidth = items.reduce(
(total, item) => total + (visibility[item.id] ? item.expansionWidth : 0),
0,
);
for (const item of items) {
if (usedWidth <= availableWidth) break;
if (!visibility[item.id]) continue;
visibility[item.id] = false;
usedWidth -= item.expansionWidth;
}
return visibility;
}
export function getToolbarItemVisibilityWithHysteresis({
availableWidth,
items,
currentVisibility,
expansionMargin,
}: {
availableWidth: number;
items: ReadonlyArray<{
id: string;
expansionWidth: number;
ready?: boolean;
}>;
currentVisibility: Readonly<Record<string, boolean>>;
expansionMargin: number;
}): Record<string, boolean> {
const collapseVisibility = getToolbarItemVisibility({
availableWidth,
items,
});
const expansionVisibility = getToolbarItemVisibility({
availableWidth: Math.max(0, availableWidth - expansionMargin),
items,
});
return Object.fromEntries(
items.map((item) => [
item.id,
currentVisibility[item.id]
? collapseVisibility[item.id]
: expansionVisibility[item.id],
]),
);
}
export function getToolbarExpansionBudget({
toolbarWidth,
leadingWidth,
rightWidth,
currentExpansionWidth,
gap,
}: {
toolbarWidth: number;
leadingWidth: number;
rightWidth: number;
currentExpansionWidth: number;
gap: number;
}): number {
const fixedLeadingWidth = Math.max(0, leadingWidth - currentExpansionWidth);
return Math.max(0, toolbarWidth - rightWidth - fixedLeadingWidth - gap);
}
export function resolveToolbarModelLabel({
currentModelLabel,
lastConfirmedModelLabel,
}: {
currentModelLabel: string;
lastConfirmedModelLabel: string;
}): {
modelLabel: string;
modelLabelReady: boolean;
nextConfirmedModelLabel: string;
} {
const nextConfirmedModelLabel = currentModelLabel || lastConfirmedModelLabel;
return {
modelLabel: nextConfirmedModelLabel,
modelLabelReady: Boolean(nextConfirmedModelLabel),
nextConfirmedModelLabel,
};
}