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
26 changes: 0 additions & 26 deletions scripts/i18n-catalog-english-value-baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -241,21 +241,16 @@ templates/clips/app/i18n/es-ES|chapters.empty|No chapters yet.
templates/clips/app/i18n/es-ES|chapters.remove|Remove chapter
templates/clips/app/i18n/es-ES|chapters.saveFailed|Failed to save chapters
templates/clips/app/i18n/es-ES|dictateRoute.browserDictationDescriptionDesktop|Use the button below to capture a note right here on this page. It does not paste into other apps — for that, use the desktop shortcut on the right.
templates/clips/app/i18n/es-ES|dictateRoute.browserDictationDescription|Use the button on this page, or press the shortcut while this tab is focused. Browser dictation saves here for copy and cleanup.
templates/clips/app/i18n/es-ES|dictateRoute.browserDictation|Browser dictation
templates/clips/app/i18n/es-ES|dictateRoute.browserUnavailableShort|Browser speech recognition is unavailable here
templates/clips/app/i18n/es-ES|dictateRoute.browserUnavailable|Browser speech recognition is unavailable here. Use Chrome or the desktop app for global dictation.
templates/clips/app/i18n/es-ES|dictateRoute.cleanupHint|Click "Cleanup with AI" to fix punctuation, casing, and filler words.
templates/clips/app/i18n/es-ES|dictateRoute.cleanupWithAi|Cleanup with AI
templates/clips/app/i18n/es-ES|dictateRoute.desktopCtaTitle|Dictate from anywhere with the desktop app
templates/clips/app/i18n/es-ES|dictateRoute.desktopShortcutsDescriptionSuffix|, in the desktop app.
templates/clips/app/i18n/es-ES|dictateRoute.desktopShortcuts|Desktop shortcuts
templates/clips/app/i18n/es-ES|dictateRoute.dictationSaved|Dictation saved
templates/clips/app/i18n/es-ES|dictateRoute.dictionaryDescription|Terms here bias speech recognition toward your preferred spellings — auto-learned from corrections, or add your own.
templates/clips/app/i18n/es-ES|dictateRoute.dictionaryEmpty|No learned terms yet.
templates/clips/app/i18n/es-ES|dictateRoute.dictionaryLoading|Loading dictionary...
templates/clips/app/i18n/es-ES|dictateRoute.downloadDesktopApp|Download Clips desktop app
templates/clips/app/i18n/es-ES|dictateRoute.emptyTranscript|Empty transcript
templates/clips/app/i18n/es-ES|dictateRoute.emptyWebDescription|Dictation runs through the desktop app for global shortcuts that work in any app — Slack, your editor, anywhere.
templates/clips/app/i18n/es-ES|dictateRoute.holdFn|Hold Fn
templates/clips/app/i18n/es-ES|dictateRoute.howToUse|How to use Dictate
Expand All @@ -266,9 +261,6 @@ templates/clips/app/i18n/es-ES|dictateRoute.noText|No text
templates/clips/app/i18n/es-ES|dictateRoute.pageTitle|Dictate · Clips
templates/clips/app/i18n/es-ES|dictateRoute.quickNoteHint|Captures here without leaving this page — it does not paste into other apps. Use the button to start and stop.
templates/clips/app/i18n/es-ES|dictateRoute.quickNoteTitle|Quick dictation note
templates/clips/app/i18n/es-ES|dictateRoute.replaceOriginal|Replace original with cleaned
templates/clips/app/i18n/es-ES|dictateRoute.replacedOriginal|Replaced original with cleaned text
templates/clips/app/i18n/es-ES|dictateRoute.startFirst|Start your first dictation
templates/clips/app/i18n/es-ES|dictateRoute.startSpeaking|Start speaking...
templates/clips/app/i18n/es-ES|dictateRoute.vocabularyAddFailed|Couldn't add term
templates/clips/app/i18n/es-ES|dictateRoute.vocabularyRemoveFailed|Couldn't remove term
Expand Down Expand Up @@ -324,8 +316,6 @@ templates/clips/app/i18n/es-ES|meetingsRoute.calendarNeedsReconnect|Google Calen
templates/clips/app/i18n/es-ES|meetingsRoute.calendarSettings|Calendar settings
templates/clips/app/i18n/es-ES|meetingsRoute.clearSearch|Clear search
templates/clips/app/i18n/es-ES|meetingsRoute.connectCalendarReminder|Connect Google Calendar for meeting reminders.
templates/clips/app/i18n/es-ES|meetingsRoute.connectGoogleCalendar|Connect Google Calendar
templates/clips/app/i18n/es-ES|meetingsRoute.desktopReminder|Connect Google Calendar, keep Clips Desktop open, then click Start notes from the reminder or the menu bar when your meeting begins.
templates/clips/app/i18n/es-ES|meetingsRoute.disconnectGoogleCalendarTitle|Disconnect Google Calendar?
templates/clips/app/i18n/es-ES|meetingsRoute.getDesktopApp|Get desktop app
templates/clips/app/i18n/es-ES|meetingsRoute.guideCalendarDescription|Meetings are pulled from your calendar so Clips knows when to remind you.
Expand All @@ -335,9 +325,6 @@ templates/clips/app/i18n/es-ES|meetingsRoute.guideDesktopTitle|Keep Clips Deskto
templates/clips/app/i18n/es-ES|meetingsRoute.guideStartDescription|Use the desktop reminder or the menu-bar Start Meeting Notes item when the call begins.
templates/clips/app/i18n/es-ES|meetingsRoute.guideStartTitle|Click Start notes
templates/clips/app/i18n/es-ES|meetingsRoute.howToTriggerTitle|How to trigger meeting notes
templates/clips/app/i18n/es-ES|meetingsRoute.intro|Upcoming calendar meetings and your recorded notes. Start live notes from Clips Desktop at meeting time.
templates/clips/app/i18n/es-ES|meetingsRoute.noMeetingsDescription|Connect your calendar and keep Clips Desktop open. When a meeting starts, use Start notes from the reminder or menu bar.
templates/clips/app/i18n/es-ES|meetingsRoute.noMeetingsYet|No meetings yet
templates/clips/app/i18n/es-ES|meetingsRoute.notesPending|Notes pending
templates/clips/app/i18n/es-ES|meetingsRoute.pageTitle|Meetings · Clips
templates/clips/app/i18n/es-ES|meetingsRoute.pastRecordings|Past recordings
Expand Down Expand Up @@ -431,21 +418,16 @@ templates/clips/app/i18n/pt-BR|chapters.empty|No chapters yet.
templates/clips/app/i18n/pt-BR|chapters.remove|Remove chapter
templates/clips/app/i18n/pt-BR|chapters.saveFailed|Failed to save chapters
templates/clips/app/i18n/pt-BR|dictateRoute.browserDictationDescriptionDesktop|Use the button below to capture a note right here on this page. It does not paste into other apps — for that, use the desktop shortcut on the right.
templates/clips/app/i18n/pt-BR|dictateRoute.browserDictationDescription|Use the button on this page, or press the shortcut while this tab is focused. Browser dictation saves here for copy and cleanup.
templates/clips/app/i18n/pt-BR|dictateRoute.browserDictation|Browser dictation
templates/clips/app/i18n/pt-BR|dictateRoute.browserUnavailableShort|Browser speech recognition is unavailable here
templates/clips/app/i18n/pt-BR|dictateRoute.browserUnavailable|Browser speech recognition is unavailable here. Use Chrome or the desktop app for global dictation.
templates/clips/app/i18n/pt-BR|dictateRoute.cleanupHint|Click "Cleanup with AI" to fix punctuation, casing, and filler words.
templates/clips/app/i18n/pt-BR|dictateRoute.cleanupWithAi|Cleanup with AI
templates/clips/app/i18n/pt-BR|dictateRoute.desktopCtaTitle|Dictate from anywhere with the desktop app
templates/clips/app/i18n/pt-BR|dictateRoute.desktopShortcutsDescriptionSuffix|, in the desktop app.
templates/clips/app/i18n/pt-BR|dictateRoute.desktopShortcuts|Desktop shortcuts
templates/clips/app/i18n/pt-BR|dictateRoute.dictationSaved|Dictation saved
templates/clips/app/i18n/pt-BR|dictateRoute.dictionaryDescription|Terms here bias speech recognition toward your preferred spellings — auto-learned from corrections, or add your own.
templates/clips/app/i18n/pt-BR|dictateRoute.dictionaryEmpty|No learned terms yet.
templates/clips/app/i18n/pt-BR|dictateRoute.dictionaryLoading|Loading dictionary...
templates/clips/app/i18n/pt-BR|dictateRoute.downloadDesktopApp|Download Clips desktop app
templates/clips/app/i18n/pt-BR|dictateRoute.emptyTranscript|Empty transcript
templates/clips/app/i18n/pt-BR|dictateRoute.emptyWebDescription|Dictation runs through the desktop app for global shortcuts that work in any app — Slack, your editor, anywhere.
templates/clips/app/i18n/pt-BR|dictateRoute.holdFn|Hold Fn
templates/clips/app/i18n/pt-BR|dictateRoute.howToUse|How to use Dictate
Expand All @@ -456,9 +438,6 @@ templates/clips/app/i18n/pt-BR|dictateRoute.noText|No text
templates/clips/app/i18n/pt-BR|dictateRoute.pageTitle|Dictate · Clips
templates/clips/app/i18n/pt-BR|dictateRoute.quickNoteHint|Captures here without leaving this page — it does not paste into other apps. Use the button to start and stop.
templates/clips/app/i18n/pt-BR|dictateRoute.quickNoteTitle|Quick dictation note
templates/clips/app/i18n/pt-BR|dictateRoute.replaceOriginal|Replace original with cleaned
templates/clips/app/i18n/pt-BR|dictateRoute.replacedOriginal|Replaced original with cleaned text
templates/clips/app/i18n/pt-BR|dictateRoute.startFirst|Start your first dictation
templates/clips/app/i18n/pt-BR|dictateRoute.startSpeaking|Start speaking...
templates/clips/app/i18n/pt-BR|dictateRoute.vocabularyAddFailed|Couldn't add term
templates/clips/app/i18n/pt-BR|dictateRoute.vocabularyRemoveFailed|Couldn't remove term
Expand Down Expand Up @@ -514,8 +493,6 @@ templates/clips/app/i18n/pt-BR|meetingsRoute.calendarNeedsReconnect|Google Calen
templates/clips/app/i18n/pt-BR|meetingsRoute.calendarSettings|Calendar settings
templates/clips/app/i18n/pt-BR|meetingsRoute.clearSearch|Clear search
templates/clips/app/i18n/pt-BR|meetingsRoute.connectCalendarReminder|Connect Google Calendar for meeting reminders.
templates/clips/app/i18n/pt-BR|meetingsRoute.connectGoogleCalendar|Connect Google Calendar
templates/clips/app/i18n/pt-BR|meetingsRoute.desktopReminder|Connect Google Calendar, keep Clips Desktop open, then click Start notes from the reminder or the menu bar when your meeting begins.
templates/clips/app/i18n/pt-BR|meetingsRoute.disconnectGoogleCalendarTitle|Disconnect Google Calendar?
templates/clips/app/i18n/pt-BR|meetingsRoute.getDesktopApp|Get desktop app
templates/clips/app/i18n/pt-BR|meetingsRoute.guideCalendarDescription|Meetings are pulled from your calendar so Clips knows when to remind you.
Expand All @@ -525,9 +502,6 @@ templates/clips/app/i18n/pt-BR|meetingsRoute.guideDesktopTitle|Keep Clips Deskto
templates/clips/app/i18n/pt-BR|meetingsRoute.guideStartDescription|Use the desktop reminder or the menu-bar Start Meeting Notes item when the call begins.
templates/clips/app/i18n/pt-BR|meetingsRoute.guideStartTitle|Click Start notes
templates/clips/app/i18n/pt-BR|meetingsRoute.howToTriggerTitle|How to trigger meeting notes
templates/clips/app/i18n/pt-BR|meetingsRoute.intro|Upcoming calendar meetings and your recorded notes. Start live notes from Clips Desktop at meeting time.
templates/clips/app/i18n/pt-BR|meetingsRoute.noMeetingsDescription|Connect your calendar and keep Clips Desktop open. When a meeting starts, use Start notes from the reminder or menu bar.
templates/clips/app/i18n/pt-BR|meetingsRoute.noMeetingsYet|No meetings yet
templates/clips/app/i18n/pt-BR|meetingsRoute.notesPending|Notes pending
templates/clips/app/i18n/pt-BR|meetingsRoute.pageTitle|Meetings · Clips
templates/clips/app/i18n/pt-BR|meetingsRoute.pastRecordings|Past recordings
Expand Down
63 changes: 63 additions & 0 deletions templates/clips/actions/delete-dictation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { beforeEach, describe, expect, it, vi } from "vitest";

const mockSelectWhere = vi.hoisted(() => vi.fn());
const mockDeleteWhere = vi.hoisted(() => vi.fn(async () => undefined));
const mockDbDelete = vi.hoisted(() =>
vi.fn(() => ({ where: mockDeleteWhere })),
);
const mockDb = vi.hoisted(() => ({
select: vi.fn(() => ({
from: vi.fn(() => ({ where: mockSelectWhere })),
})),
delete: mockDbDelete,
transaction: vi.fn(async (fn: (tx: typeof mockDb) => Promise<unknown>) =>
fn(mockDb),
),
}));
const mockWriteAppState = vi.hoisted(() => vi.fn(async () => undefined));

vi.mock("@agent-native/core/action", () => ({
defineAction: (options: unknown) => options,
}));

vi.mock("@agent-native/core/application-state", () => ({
writeAppState: (...args: unknown[]) => mockWriteAppState(...args),
}));

vi.mock("@agent-native/core/sharing", () => ({
assertAccess: vi.fn(async () => undefined),
}));

vi.mock("drizzle-orm", () => ({
eq: (column: unknown, value: unknown) => ({ column, value }),
}));

vi.mock("../server/db/index.js", () => ({
getDb: () => mockDb,
schema: {
dictations: { id: "dictations.id" },
dictationShares: { resourceId: "dictationShares.resourceId" },
},
}));

import deleteDictation from "./delete-dictation";

describe("delete-dictation", () => {
beforeEach(() => {
vi.clearAllMocks();
mockSelectWhere.mockResolvedValue([{ id: "dictation_1" }]);
});

it("deletes share grants with the dictation in one transaction", async () => {
await deleteDictation.run({ id: "dictation_1" });

expect(mockDb.transaction).toHaveBeenCalledOnce();
expect(mockDbDelete).toHaveBeenNthCalledWith(1, {
resourceId: "dictationShares.resourceId",
});
expect(mockDbDelete).toHaveBeenNthCalledWith(2, {
id: "dictations.id",
});
expect(mockWriteAppState).toHaveBeenCalledOnce();
});
});
37 changes: 37 additions & 0 deletions templates/clips/actions/delete-dictation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/** Delete a dictation history row after the UI confirms the destructive action. */

import { defineAction } from "@agent-native/core/action";
import { writeAppState } from "@agent-native/core/application-state";
import { assertAccess } from "@agent-native/core/sharing";
import { eq } from "drizzle-orm";
import { z } from "zod";

import { getDb, schema } from "../server/db/index.js";

export default defineAction({
description: "Permanently delete a dictation history row.",
schema: z.object({
id: z.string().describe("Dictation ID"),
}),
run: async (args) => {
await assertAccess("dictation", args.id, "editor");

const db = getDb();
const [existing] = await db
.select({ id: schema.dictations.id })
.from(schema.dictations)
.where(eq(schema.dictations.id, args.id));
if (!existing) throw new Error(`Dictation not found: ${args.id}`);

await db.transaction(async (tx) => {
await tx
.delete(schema.dictationShares)
.where(eq(schema.dictationShares.resourceId, args.id));
await tx
.delete(schema.dictations)
.where(eq(schema.dictations.id, args.id));
});
await writeAppState("refresh-signal", { ts: Date.now() });
return { id: args.id };
},
});
8 changes: 5 additions & 3 deletions templates/clips/app/components/library/empty-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function AppEmptyState({
}: AppEmptyStateProps) {
return (
<Empty className="min-h-64 px-6 py-12 md:p-12">
<EmptyHeader>
<EmptyHeader className="w-full max-w-xl">
<EmptyMedia variant="icon">
<Icon />
</EmptyMedia>
Expand All @@ -69,7 +69,9 @@ export function AppEmptyState({
<EmptyDescription>{description}</EmptyDescription>
) : null}
</EmptyHeader>
{content ? <EmptyContent>{content}</EmptyContent> : null}
{content ? (
<EmptyContent className="gap-2">{content}</EmptyContent>
) : null}
</Empty>
);
}
Expand Down Expand Up @@ -109,7 +111,7 @@ export function EmptyState({
{t(`empty.${kind}.cta`)}
</Button>
) : BACK_TO_LIBRARY_KINDS.has(kind) ? (
<Button asChild size="sm" variant="outline">
<Button asChild size="sm">
<Link to="/library">{t("recordingPage.backToLibrary")}</Link>
</Button>
) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ describe("selected library actions layout", () => {
expect(primaryActionsSource).toContain('triggerIcon="chevron"');
expect(gridSource).toContain('import { FolderCard } from "./folder-card"');
expect(gridSource).toContain("visibleFolders");
expect(gridSource).toContain("const isEmptyState =");
expect(gridSource).toContain("!isEmptyState && extraActions");
expect(gridSource).toContain("!isEmptyState && (\n <SortMenu");
expect(gridSource).toContain("organizationId: currentOrganizationId");
expect(folderRouteSource).toContain("useOrganizations()");
expect(folderRouteSource).toContain(
Expand Down Expand Up @@ -184,9 +187,8 @@ describe("selected library actions layout", () => {
const dictateSource = readSource("../../routes/_app.dictate.tsx");
const trashSource = readSource("../../routes/_app.trash.tsx");

expect(layoutSource).toContain(
"to: getMeetingsSidebarHref(meetingsLabEnabled, CLIPS_MEETINGS.key)",
);
expect(layoutSource).toContain("...(meetingsLabEnabled");
expect(layoutSource).toContain('to: "/meetings"');
expect(meetingsSource).toContain("<PageBreadcrumb");
expect(dictateSource).toContain("<PageBreadcrumb");
expect(trashSource).toContain("<PageBreadcrumb");
Expand All @@ -208,7 +210,7 @@ describe("selected library actions layout", () => {
it("keeps library search before the primary recording action", () => {
const gridSource = readSource("./library-grid.tsx");
const searchIndex = gridSource.indexOf("<SearchBar");
const actionIndex = gridSource.indexOf("{extraActions}");
const actionIndex = gridSource.indexOf("!isEmptyState && extraActions");

expect(searchIndex).toBeGreaterThan(-1);
expect(actionIndex).toBeGreaterThan(searchIndex);
Expand Down
22 changes: 16 additions & 6 deletions templates/clips/app/components/library/library-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ export function LibraryGrid({
: [],
[folderId, scopedFolders, view],
);
const isEmptyState =
!isLoading &&
!(view !== "shared" && isFoldersLoading) &&
!isError &&
recordings.length === 0 &&
visibleFolders.length === 0;
const selectedIds = useMemo(() => Array.from(selected), [selected]);
const moveTargets = useMemo(
() =>
Expand Down Expand Up @@ -539,13 +545,17 @@ export function LibraryGrid({
<PageBreadcrumb items={pageBreadcrumbItems} />
) : null}
</div>
<SearchBar
side="bottom"
className="hidden min-w-0 max-w-80 flex-1 md:block lg:w-full lg:max-w-none"
/>
{!isEmptyState && (
<SearchBar
side="bottom"
className="hidden min-w-0 max-w-80 flex-1 md:block lg:w-full lg:max-w-none"
/>
)}
<div className="ms-auto flex shrink-0 items-center gap-2 lg:col-start-3 lg:ms-0 lg:justify-self-end">
{extraActions}
<SortMenu value={sort} onChange={handleSortChange} />
{!isEmptyState && extraActions}
{!isEmptyState && (
<SortMenu value={sort} onChange={handleSortChange} />
)}
</div>
</div>
</PageHeader>
Expand Down
17 changes: 10 additions & 7 deletions templates/clips/app/components/library/library-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
import { FolderTree, type FolderNode } from "./folder-tree";
import { PageHeaderSlotProvider } from "./page-header";
import { SidebarFeedbackButton } from "./sidebar-feedback-button";
import { getMeetingsSidebarHref } from "./sidebar-nav-hrefs";
import { SpaceDialogs } from "./space-dialogs";

interface LibraryLayoutProps {
Expand Down Expand Up @@ -162,7 +161,7 @@
<Icon className="size-4 shrink-0 text-primary" />
<span className="flex-1 truncate text-primary">{label}</span>
{count !== undefined && count > 0 && (
<span className="shrink-0 tabular-nums text-[11px] text-primary/80">

Check warning on line 164 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"text-[11px]" hardcodes an off-token value. Nearest on the scale: text-xs (12px), text-sm (14px). Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
{count}
</span>
)}
Expand All @@ -182,7 +181,7 @@
</button>
</CollapsibleTrigger>
</div>
<CollapsibleContent className="clips-collapsible-content">

Check warning on line 184 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-restyle)

"clips-collapsible-content" is not allowed on <CollapsibleContent>: the grammar does not recognize it. Fix the spelling, or use a class Tailwind generates. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
<div className="ms-3.5 border-s border-border/70 ps-2">{children}</div>
</CollapsibleContent>
</Collapsible>
Expand Down Expand Up @@ -394,7 +393,7 @@
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
(previouslyFocused ?? mobileMenuTriggerRef.current)?.focus();

Check warning on line 396 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

The ref's value `.current` is accessed directly in the effect cleanup function.
};
}, [isMobile, sidebarOpen]);
useEffect(() => {
Expand Down Expand Up @@ -440,12 +439,16 @@
icon: IconUsersGroup,
match: (p) => p === "/spaces" || p.startsWith("/spaces/"),
},
{
to: getMeetingsSidebarHref(meetingsLabEnabled, CLIPS_MEETINGS.key),
label: t("navigation.meetings"),
icon: IconCalendar,
match: (p) => p.startsWith("/meetings"),
},
...(meetingsLabEnabled
? [
{
to: "/meetings",
label: t("navigation.meetings"),
icon: IconCalendar,
match: (p: string) => p.startsWith("/meetings"),
},
]
: []),
...(wisprFlowLabEnabled
? [
{
Expand Down Expand Up @@ -505,7 +508,7 @@
{count !== undefined && count > 0 && (
<span
className={cn(
"shrink-0 tabular-nums text-[11px]",

Check warning on line 511 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"text-[11px]" hardcodes an off-token value. Nearest on the scale: text-xs (12px), text-sm (14px). Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
"text-primary/80",
)}
>
Expand Down Expand Up @@ -603,7 +606,7 @@
aria-hidden={isMobile && !sidebarOpen ? true : undefined}
inert={isMobile && !sidebarOpen ? true : undefined}
className={cn(
"agent-layout-left-drawer fixed inset-y-0 start-0 z-50 flex h-full w-[260px] flex-col overflow-hidden border-e border-border bg-sidebar transition-[width,transform] duration-200 ease-out md:static md:z-auto",

Check warning on line 609 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"transition-[width,transform]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.

Check warning on line 609 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"w-[260px]" hardcodes an off-token value. Use "w-65" instead (same value, on the scale). Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
showCollapsedSidebar && "md:w-14",
sidebarOpen
? "translate-x-0"
Expand Down Expand Up @@ -670,10 +673,10 @@
className="flex min-w-0 flex-1 items-center gap-2 text-primary"
>
<div
className="flex size-4 shrink-0 items-center justify-center rounded text-[10px]"

Check warning on line 676 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"text-[10px]" hardcodes an off-token value. Nearest on the scale: text-xs (12px), text-sm (14px). Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
style={{
background:
s.color ?? "hsl(var(--primary))",

Check warning on line 679 in templates/clips/app/components/library/library-layout.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-inline-styles)

Inline style sets background. Style through classes; use CSS custom properties for dynamic values. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
color: "hsl(var(--primary-foreground))",
}}
>
Expand Down
17 changes: 0 additions & 17 deletions templates/clips/app/components/library/sidebar-nav-hrefs.test.ts

This file was deleted.

Loading
Loading