From b317890b169a1f102748ac022117e860a8d3a2d5 Mon Sep 17 00:00:00 2001 From: Abian Suarez Date: Fri, 25 Sep 2026 11:39:54 +0100 Subject: [PATCH 1/2] fix(extension): treat custom text sizes as font sizes in cn --- apps/extension/lib/utils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/extension/lib/utils.ts b/apps/extension/lib/utils.ts index ab99730..e490954 100644 --- a/apps/extension/lib/utils.ts +++ b/apps/extension/lib/utils.ts @@ -1,5 +1,11 @@ import { type ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; +import { extendTailwindMerge } from "tailwind-merge"; + +// Our custom type scale (`text-2xs`, `text-ui`, see theme.css) must count as font +// sizes: otherwise tailwind-merge reads them as colors and drops the real text color. +const twMerge = extendTailwindMerge({ + extend: { classGroups: { "font-size": [{ text: ["2xs", "ui"] }] } }, +}); export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); From 03e19e62497eb0e14e46e3c508d9f36350c9105c Mon Sep 17 00:00:00 2001 From: Abian Suarez Date: Fri, 25 Sep 2026 11:39:55 +0100 Subject: [PATCH 2/2] feat(extension): redesign the toolbar popup --- apps/extension/entrypoints/popup/App.tsx | 254 ++++++++++++------ .../entrypoints/popup/components/RecDot.tsx | 10 + .../popup/components/RecentRecordings.tsx | 47 ++++ .../popup/components/RecordGlyph.tsx | 41 +++ .../entrypoints/popup/components/TabCard.tsx | 71 +++++ .../popup/core/__test__/unit/format.test.ts | 68 +++++ .../entrypoints/popup/core/format.ts | 52 ++++ .../entrypoints/popup/hooks/use-popup-data.ts | 116 ++++++++ apps/extension/entrypoints/popup/style.css | 46 ++++ apps/extension/lib/db.ts | 12 + 10 files changed, 638 insertions(+), 79 deletions(-) create mode 100644 apps/extension/entrypoints/popup/components/RecDot.tsx create mode 100644 apps/extension/entrypoints/popup/components/RecentRecordings.tsx create mode 100644 apps/extension/entrypoints/popup/components/RecordGlyph.tsx create mode 100644 apps/extension/entrypoints/popup/components/TabCard.tsx create mode 100644 apps/extension/entrypoints/popup/core/__test__/unit/format.test.ts create mode 100644 apps/extension/entrypoints/popup/core/format.ts create mode 100644 apps/extension/entrypoints/popup/hooks/use-popup-data.ts diff --git a/apps/extension/entrypoints/popup/App.tsx b/apps/extension/entrypoints/popup/App.tsx index a9f7988..f6c3b66 100644 --- a/apps/extension/entrypoints/popup/App.tsx +++ b/apps/extension/entrypoints/popup/App.tsx @@ -1,27 +1,36 @@ -import { ArrowUpRight, Circle, MousePointerClick, Square } from "lucide-react"; -import { useEffect, useState } from "react"; +import { + ArrowUpRight, + MousePointerClick, + Square, + TriangleAlert, +} from "lucide-react"; import { browser } from "wxt/browser"; import { MadeByRustrak } from "@/components/brand/made-by-rustrak"; import { OpenShowcaseWordmark } from "@/components/brand/openshowcase-wordmark"; import { Button } from "@/components/ui/button"; import { useColorScheme } from "@/hooks/use-color-scheme"; +import { sendMessage } from "@/lib/messaging"; +import { RecDot } from "./components/RecDot"; +import { RecentRecordings } from "./components/RecentRecordings"; +import { RecordGlyph } from "./components/RecordGlyph"; +import { TabCard } from "./components/TabCard"; +import { formatElapsed, isRecordableUrl } from "./core/format"; import { - defaultRecordingSessionState, - type RecordingSessionState, - sendMessage, -} from "@/lib/messaging"; -import { recordingSession } from "@/lib/recording-storage"; + useActiveTab, + useNow, + useRecentRecordings, + useRecordingSession, + useRecordingStart, + useTab, +} from "./hooks/use-popup-data"; + +const RECENT_LIMIT = 3; function App() { useColorScheme(); - const [state, setState] = useState( - defaultRecordingSessionState, - ); - - useEffect(() => { - recordingSession.getValue().then(setState); - return recordingSession.watch((newState) => setState(newState)); - }, []); + const session = useRecordingSession(); + const activeTab = useActiveTab(); + const recent = useRecentRecordings(RECENT_LIMIT); async function send(type: "startRecording" | "stopRecording") { await sendMessage(type, undefined).catch((error) => { @@ -35,79 +44,40 @@ function App() { window.close(); } - async function openEditor() { - if (!state.recordingId) return; + async function openEditor(recordingId: string) { await browser.tabs.create({ - url: browser.runtime.getURL( - `/editor.html?recordingId=${state.recordingId}`, - ), + url: browser.runtime.getURL(`/editor.html?recordingId=${recordingId}`), }); + window.close(); } - const hasFinishedRecording = !state.active && Boolean(state.recordingId); - return ( -
-
+
+
- {state.active && ( - - - - - - REC - - )} + {session.active && }
- {state.active ? ( -
-
- - - -
- - {state.clickCount ?? 0}{" "} - {state.clickCount === 1 ? "step" : "steps"} - - - Every click becomes a step - -
-
- -
- ) : ( -
-

- Use this tab normally. Clicks become steps, scrolling and typing - become short clips — the editor opens when you stop. -

- - {hasFinishedRecording && ( - - )} -
- )} +
+ {session.active ? ( + send("stopRecording")} + /> + ) : ( + + } + /> + )} +
-