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
12 changes: 6 additions & 6 deletions client/src/components/SidePanel/Agents/Tools/CategoryFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ export default function CategoryFilter({ options, value, onChange }: Props) {
<Button
variant="outline"
size="default"
className={cn(
'h-10 gap-1.5 px-3 text-sm font-normal',
isFiltered && 'border-emerald-500/50 text-text-primary',
)}
className="h-10 gap-1.5 px-3 text-sm font-normal"
aria-label={localize('com_ui_category')}
>
<Filter className="size-4 text-text-tertiary" aria-hidden="true" />
<Filter
className={cn('size-4', isFiltered ? 'text-accent-primary' : 'text-text-tertiary')}
aria-hidden="true"
/>
<span className="max-w-[10ch] truncate">{label}</span>
<ChevronDown className="size-3.5 text-text-tertiary" aria-hidden="true" />
<ChevronDown className="text-text-tertiary size-3.5" aria-hidden="true" />
</Button>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function HeaderIcon({ item }: { item: AgentItem }) {
if (iconUrl && !imgError) {
return (
<span
className="flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-2xl bg-white"
className="bg-surface-fixed flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-2xl"
aria-hidden="true"
>
<img
Expand Down Expand Up @@ -51,13 +51,13 @@ export default function ItemDialogHeader({ item }: { item: AgentItem }) {
const kindLabel = localize(KIND_LABEL_KEYS[item.kind]);

return (
<OGDialogHeader className="flex flex-row items-center gap-3 space-y-0 px-6 pb-4 pt-5 text-left">
<OGDialogHeader className="flex flex-row items-center gap-3 space-y-0 px-6 pt-5 pb-4 text-left">
<HeaderIcon item={item} />
<div className="min-w-0 flex-1">
<OGDialogTitle className="truncate text-base font-semibold text-text-primary">
<OGDialogTitle className="text-text-primary truncate text-base font-semibold">
{displayName}
</OGDialogTitle>
<OGDialogDescription className="m-0 text-[11px] uppercase tracking-wide text-text-secondary">
<OGDialogDescription className="text-text-secondary m-0 text-[11px] tracking-wide uppercase">
{kindLabel}
</OGDialogDescription>
</div>
Expand Down
22 changes: 11 additions & 11 deletions client/src/components/Skills/tree/SkillFilePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,24 @@ export default function SkillFilePreview({ skillId, nodeId, fileName }: SkillFil

if (isLoading) {
return (
<div className="flex h-full items-center justify-center bg-surface-primary">
<div className="bg-surface-primary flex h-full items-center justify-center">
<Spinner className="text-text-tertiary" />
</div>
);
}

return (
<div className="flex h-full flex-col bg-surface-primary">
<div className="flex items-center gap-2 border-b border-border-light px-4 py-2">
<div className="bg-surface-primary flex h-full flex-col">
<div className="border-border-light flex items-center gap-2 border-b px-4 py-2">
<div className="flex min-w-0 flex-1 items-center gap-2">
{isImage ? (
<FileImage className="size-4 shrink-0 text-pink-400" aria-hidden="true" />
<FileImage className="text-series-5 size-4 shrink-0" aria-hidden="true" />
) : (
<File className="size-4 shrink-0 text-text-tertiary" aria-hidden="true" />
<File className="text-text-tertiary size-4 shrink-0" aria-hidden="true" />
)}
<span className="truncate text-sm font-medium text-text-primary">{fileName}</span>
<span className="text-text-primary truncate text-sm font-medium">{fileName}</span>
{ext && (
<span className="rounded bg-surface-tertiary px-1.5 py-0.5 text-[11px] font-medium uppercase tracking-wide text-text-tertiary">
<span className="bg-surface-tertiary text-text-tertiary rounded px-1.5 py-0.5 text-[11px] font-medium tracking-wide uppercase">
{ext}
</span>
)}
Expand Down Expand Up @@ -76,12 +76,12 @@ export default function SkillFilePreview({ skillId, nodeId, fileName }: SkillFil
/>
) : (
<div className="flex flex-col items-center gap-3 text-center">
<div className="flex size-14 items-center justify-center rounded-2xl bg-surface-tertiary">
<File className="size-7 text-text-tertiary" aria-hidden="true" />
<div className="bg-surface-tertiary flex size-14 items-center justify-center rounded-2xl">
<File className="text-text-tertiary size-7" aria-hidden="true" />
</div>
<div>
<p className="text-sm font-medium text-text-primary">{fileName}</p>
<p className="mt-0.5 text-xs text-text-tertiary">
<p className="text-text-primary text-sm font-medium">{fileName}</p>
<p className="text-text-tertiary mt-0.5 text-xs">
{data?.mimeType ?? localize('com_ui_unknown_file_type')}
</p>
</div>
Expand Down
74 changes: 74 additions & 0 deletions e2e/specs/mock/scenarios/category-filter-state.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { expect, test } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { openAgentBuilder } from '../agents.helpers';
import { getAccessToken, requestJson } from '../helpers';

/**
* Picking a category in the Skills dialog marks the filter as active on its icon, drawn in the
* theme's accent role; the resting filter keeps the tertiary icon. The state used to be a raw
* emerald border no theme could set.
*/
type SkillSummary = { _id: string; name: string };

async function resolveRole(page: Page, role: string): Promise<string> {
return page.evaluate((name) => {
const probe = document.createElement('div');
probe.style.color = `rgb(var(--${name}))`;
document.body.appendChild(probe);
const color = getComputedStyle(probe).color;
probe.remove();
return color;
}, role);
}

async function openSkillsPicker(page: Page): Promise<Locator> {
const form = await openAgentBuilder(page);
await form.getByRole('radio', { name: 'Selected', exact: true }).click();
const addSkill = form.getByRole('button', { name: /Add skill/ }).first();
await expect(addSkill).toBeVisible();
await addSkill.click();
const dialog = page.getByRole('dialog', { name: 'Skills', exact: true });
await expect(dialog).toBeVisible();
return dialog;
}

const filterIconColor = (trigger: Locator) =>
trigger
.locator('svg')
.first()
.evaluate((icon) => getComputedStyle(icon).color);

test('an active skills category shows on the filter icon in the accent role @scenario:skills-category-filter-shows-active-state', async ({
page,
}) => {
test.setTimeout(120_000);
await page.goto('/c/new', { timeout: 15_000 });
const token = await getAccessToken(page);
const suffix = `${Date.now().toString(36)}-${Math.floor(Math.random() * 1e4)}`;
const category = `theme-${suffix}`;
const skill = await requestJson<SkillSummary>(page, {
path: '/api/skills',
token,
method: 'POST',
body: {
name: `filter-state-${suffix}`,
description: 'Carries a category so the Skills dialog offers a category filter.',
body: '# Category filter\n\nUsed by the category filter end-to-end test.',
category,
},
});

try {
const dialog = await openSkillsPicker(page);
const trigger = dialog.getByRole('button', { name: 'Category', exact: true });
await expect(trigger).toBeVisible();
expect(await filterIconColor(trigger)).toBe(await resolveRole(page, 'text-tertiary'));

await trigger.click();
await page.getByRole('menuitem', { name: category }).click();
await expect(trigger).toContainText(category);
expect(await filterIconColor(trigger)).toBe(await resolveRole(page, 'accent-primary'));
} finally {
await requestJson(page, { path: `/api/skills/${skill._id}`, token, method: 'DELETE' });
}
});
8 changes: 1 addition & 7 deletions eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1730,11 +1730,8 @@
}
},
"client/src/components/SidePanel/Agents/Tools/CategoryFilter.tsx": {
"shadcn/no-raw-colors": {
"count": 1
},
"shadcn/no-restyle": {
"count": 6
"count": 4
}
},
"client/src/components/SidePanel/Agents/Tools/ItemDialog/ItemDialog.tsx": {
Expand Down Expand Up @@ -2086,9 +2083,6 @@
"shadcn/no-arbitrary-values": {
"count": 3
},
"shadcn/no-raw-colors": {
"count": 1
},
"shadcn/no-restyle": {
"count": 5
}
Expand Down
7 changes: 7 additions & 0 deletions packages/client/src/theme/allowlist.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,10 @@ is a role, not an exception.
| `client/src/utils/mermaid.ts` | The mermaid artifact document and its zoom controls render in a sandboxed iframe without theme variables; the token reads' fallbacks and the luminance repair ink are literals by necessity. |
| `client/src/utils/richtext.ts` | Inline styles on HTML copied to the clipboard for pasting into other apps, which must not carry the sender's theme. |
| `client/src/utils/officePreview.ts`, `client/src/hooks/ScreenshotContext.tsx` | Fallbacks for a theme role read at runtime, used only when the variable is unset. |

## Panels: media and third-party surfaces

| File | Why |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `client/src/components/Nav/SettingsTabs/Account/Avatar.tsx` | The move hint drawn over the user's photo in the avatar cropper: a media scrim with white ink, independent of the theme around the image. |
| `client/src/hooks/Files/useSharePointPicker.ts` | Background of Microsoft's SharePoint file picker iframe, matched to that third party's own surface. |
Loading