diff --git a/packages/app/package.json b/packages/app/package.json
index c18727b8..3ccaf90b 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -31,6 +31,7 @@
"typecheck:test": "tsc -p tsconfig.test.json --noEmit"
},
"dependencies": {
+ "@base-ui/react": "^1.7.0",
"@codemirror/commands": "6.10.3",
"@codemirror/lang-json": "6.0.2",
"@codemirror/lang-markdown": "6.5.0",
diff --git a/packages/app/src/components/AppMenuBar.tsx b/packages/app/src/components/AppMenuBar.tsx
new file mode 100644
index 00000000..f963d386
--- /dev/null
+++ b/packages/app/src/components/AppMenuBar.tsx
@@ -0,0 +1,121 @@
+import { Menu } from "@base-ui/react/menu";
+import { Menubar } from "@base-ui/react/menubar";
+import { ChevronRight } from "lucide-react";
+import type { ContextMenuEntry, ContextMenuItem, ContextMenuSubmenu } from "./ContextMenu";
+
+export type AppMenuConfig = {
+ id: string;
+ label: string;
+ /** Extra classes for this menu's trigger, e.g. app-name weight or per-menu color. */
+ triggerClassName?: string;
+};
+
+export type AppMenuBarProps = {
+ menus: AppMenuConfig[];
+ getEntries: (menuId: string) => ContextMenuEntry[];
+ triggerClassName?: string;
+};
+
+const positionerClassName = "z-[2147483647]";
+
+const popupClassName =
+ "min-w-[216px] max-w-[min(280px,calc(100vw-16px))] rounded-lg border border-(--border-default) bg-(--bg-primary) p-1 text-[13px] leading-5 font-[520] text-(--text-primary) shadow-[0_14px_36px_color-mix(in_srgb,var(--text-heading)_16%,transparent)] outline-none select-none";
+
+const itemClassName =
+ "flex h-7 w-full items-center justify-between gap-4 rounded-md border-0 bg-transparent px-2 text-left font-inherit text-(--text-primary) outline-none transition-[background-color,color,opacity] duration-150 ease-out cursor-pointer hover:bg-(--bg-hover) hover:text-(--text-heading) focus-visible:bg-(--bg-hover) focus-visible:text-(--text-heading) focus-visible:outline-none data-disabled:opacity-45 data-disabled:cursor-default data-disabled:pointer-events-none";
+
+const separatorClassName = "my-1 h-px bg-(--border-default)";
+
+const acceleratorClassName =
+ "shrink-0 text-[12px] font-[520] text-(--text-secondary)";
+
+const submenuTriggerClassName =
+ "flex h-7 w-full items-center justify-between gap-4 rounded-md border-0 bg-transparent px-2 text-left font-inherit text-(--text-primary) outline-none transition-[background-color,color,opacity] duration-150 ease-out cursor-pointer hover:bg-(--bg-hover) hover:text-(--text-heading) focus-visible:bg-(--bg-hover) focus-visible:text-(--text-heading) focus-visible:outline-none data-disabled:opacity-45 data-disabled:cursor-default data-disabled:pointer-events-none";
+
+function formatAccelerator(accelerator: string) {
+ return accelerator.replace(/CmdOrCtrl/gu, "Cmd/Ctrl");
+}
+
+export function AppMenuBar({ menus, getEntries, triggerClassName }: AppMenuBarProps) {
+ return (
+
+ {menus.map((menu) => (
+
+
+ {menu.label}
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+}
+
+function AppMenuEntries({ entries }: { entries: ContextMenuEntry[] }) {
+ return entries.map((entry, index) => {
+ if (entry.kind === "separator") {
+ return
;
+ }
+
+ if (entry.kind === "submenu") {
+ return ;
+ }
+
+ return ;
+ });
+}
+
+function AppMenuItem({ entry }: { entry: ContextMenuItem }) {
+ const disabled = Boolean(entry.disabled);
+ const acceleratorLabel = entry.accelerator ? formatAccelerator(entry.accelerator) : null;
+ const accessibleLabel = acceleratorLabel ? `${entry.label} ${acceleratorLabel}` : entry.label;
+
+ return (
+ {
+ if (disabled) return;
+ Promise.resolve(entry.onSelect?.()).catch(() => {});
+ }}
+ >
+
+ {entry.icon ? {entry.icon} : null}
+ {entry.label}
+
+ {entry.accelerator ? (
+ {acceleratorLabel}
+ ) : null}
+
+ );
+}
+
+function AppSubmenu({ entry }: { entry: ContextMenuSubmenu }) {
+ const disabled = Boolean(entry.disabled);
+
+ return (
+
+
+ {entry.label}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/app/src/components/NativeTitleBar.test.tsx b/packages/app/src/components/NativeTitleBar.test.tsx
index 58b94f76..f2c92f26 100644
--- a/packages/app/src/components/NativeTitleBar.test.tsx
+++ b/packages/app/src/components/NativeTitleBar.test.tsx
@@ -638,74 +638,74 @@ describe("NativeTitleBar", () => {
/>
);
- fireEvent.click(screen.getByRole("button", { name: "Markra" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Markra" }));
expect(screen.getByRole("menu", { name: "Markra" })).toBeInTheDocument();
fireEvent.click(screen.getByRole("menuitem", { name: "About Markra" }));
expect(showAbout).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "Markra" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Markra" }));
fireEvent.click(screen.getByRole("menuitem", { name: "Settings... Ctrl+," }));
expect(openSettings).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "Markra" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Markra" }));
fireEvent.click(screen.getByRole("menuitem", { name: "Check for updates" }));
expect(checkForUpdates).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "Markra" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Markra" }));
fireEvent.click(screen.getByRole("menuitem", { name: "Quit Markra" }));
expect(exitApp).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
expect(screen.getByRole("menu", { name: "File" })).toBeInTheDocument();
fireEvent.click(screen.getByRole("menuitem", { name: "New Ctrl+N" }));
expect(openBlankEditorWindow).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
fireEvent.click(screen.getByRole("menuitem", { name: "Open... Ctrl+O" }));
expect(openMarkdown).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
fireEvent.click(screen.getByRole("menuitem", { name: "Save Ctrl+S" }));
expect(saveMarkdown).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
const saveAsItem = screen.getByRole("menuitem", { name: "Save As... Ctrl+Shift+S" });
expect(saveAsItem).not.toBeDisabled();
fireEvent.click(saveAsItem);
expect(saveMarkdownAs).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
const syncItem = screen.getByRole("menuitem", { name: "Sync now Ctrl+Alt+R" });
expect(syncItem).not.toBeDisabled();
fireEvent.click(syncItem);
expect(syncNow).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
const exportMenuItem = screen.getByRole("menuitem", { name: "Export" });
expect(exportMenuItem).not.toBeDisabled();
- fireEvent.pointerEnter(exportMenuItem);
+ fireEvent.click(exportMenuItem);
const exportPdfItem = screen.getByRole("menuitem", { name: "Export PDF Ctrl+Alt+P" });
expect(exportPdfItem).not.toBeDisabled();
fireEvent.click(exportPdfItem);
expect(exportPdf).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "File" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
const reopenedExportMenuItem = screen.getByRole("menuitem", { name: "Export" });
- fireEvent.pointerEnter(reopenedExportMenuItem);
+ fireEvent.click(reopenedExportMenuItem);
const exportMarkdownItem = screen.getByRole("menuitem", { name: "Export Markdown with attachments" });
expect(exportMarkdownItem).not.toBeDisabled();
fireEvent.click(exportMarkdownItem);
expect(exportMarkdown).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "Edit" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Edit" }));
expect(screen.getByRole("menu", { name: "Edit" })).toBeInTheDocument();
expect(screen.getByRole("menuitem", { name: "Undo Ctrl+Z" })).toBeInTheDocument();
expect(screen.getByRole("menuitem", { name: "Select All Ctrl+A" })).toBeInTheDocument();
- fireEvent.click(screen.getByRole("button", { name: "Format" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Format" }));
expect(screen.getByRole("menu", { name: "Format" })).toBeInTheDocument();
const boldItem = screen.getByRole("menuitem", { name: "Bold Ctrl+B" });
@@ -713,10 +713,10 @@ describe("NativeTitleBar", () => {
fireEvent.click(boldItem);
expect(formatBold).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "Format" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "Format" }));
expect(screen.getByRole("menuitem", { name: "Heading 1 Ctrl+Alt+1" })).toBeInTheDocument();
- fireEvent.click(screen.getByRole("button", { name: "View" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "View" }));
expect(screen.getByRole("menu", { name: "View" })).toBeInTheDocument();
expect(screen.queryByRole("menuitem", { name: "Enter Full Screen" })).not.toBeInTheDocument();
@@ -728,23 +728,91 @@ describe("NativeTitleBar", () => {
fireEvent.click(screen.getByRole("button", { name: "Maximize or restore window" }));
expect(toggleWindowMaximized).toHaveBeenCalledTimes(2);
- fireEvent.click(screen.getByRole("button", { name: "View" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "View" }));
const aiCommandItem = screen.getByRole("menuitem", { name: "AI writing command Ctrl+Shift+J" });
expect(aiCommandItem).not.toBeDisabled();
fireEvent.click(aiCommandItem);
expect(toggleAiCommand).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "View" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "View" }));
const foldsItem = screen.getByRole("menuitem", { name: "Toggle all folds Ctrl+Alt+T" });
expect(foldsItem).not.toBeDisabled();
fireEvent.click(foldsItem);
expect(toggleAllFolds).toHaveBeenCalledTimes(1);
- fireEvent.click(screen.getByRole("button", { name: "View" }));
+ fireEvent.click(screen.getByRole("menuitem", { name: "View" }));
fireEvent.click(screen.getByRole("menuitem", { name: "Toggle file list Ctrl+Shift+M" }));
expect(toggleMarkdownFiles).toHaveBeenCalledTimes(1);
});
+ it("marks disabled Windows app menu items with data-disabled styling hooks", () => {
+ const onOpenMarkdown = vi.fn();
+ render(
+ {}}
+ onOpenBlankEditorWindow={vi.fn()}
+ onOpenMarkdown={onOpenMarkdown}
+ onSaveMarkdown={() => {}}
+ onToggleMarkdownFiles={() => {}}
+ onToggleTheme={() => {}}
+ />
+ );
+
+ fireEvent.click(screen.getByRole("menuitem", { name: "Markra" }));
+
+ const hiddenItem = screen.getByRole("menuitem", { name: "Hide Markra" });
+ expect(hiddenItem).toHaveAttribute("data-disabled");
+ expect(hiddenItem.className).toContain("data-disabled:opacity-45");
+ expect(hiddenItem.className).toContain("data-disabled:pointer-events-none");
+
+ fireEvent.click(screen.getByRole("menuitem", { name: "File" }));
+
+ const saveItem = screen.getByRole("menuitem", { name: "Save Ctrl+S" });
+ expect(saveItem).toHaveAttribute("data-disabled");
+ expect(saveItem.className).toContain("data-disabled:opacity-45");
+
+ const newItem = screen.getByRole("menuitem", { name: "New Ctrl+N" });
+ expect(newItem).not.toHaveAttribute("data-disabled");
+ expect(newItem.className).toContain("hover:bg-(--bg-hover)");
+ });
+
+ it("switches the open Windows app menu when hovering another menubar trigger", () => {
+ render(
+ {}}
+ onOpenBlankEditorWindow={() => {}}
+ onOpenMarkdown={() => {}}
+ onSaveMarkdown={() => {}}
+ onToggleMarkdownFiles={() => {}}
+ onToggleTheme={() => {}}
+ />
+ );
+
+ fireEvent.click(screen.getByRole("menuitem", { name: "Markra" }));
+ expect(screen.getByRole("menu", { name: "Markra" })).toBeInTheDocument();
+
+ fireEvent.mouseEnter(screen.getByRole("menuitem", { name: "File" }), {
+ clientX: 80,
+ clientY: 10
+ });
+
+ expect(screen.getByRole("menu", { name: "File" })).toBeInTheDocument();
+ expect(screen.queryByRole("menu", { name: "Markra" })).not.toBeInTheDocument();
+ });
+
it("toggles the Windows window state from self-drawn titlebar double-click mouse downs", () => {
const toggleWindowMaximized = vi.fn();
const { container } = render(
diff --git a/packages/app/src/components/WindowsNativeTitleBar.tsx b/packages/app/src/components/WindowsNativeTitleBar.tsx
index 1f8ddf36..1bbb1aef 100644
--- a/packages/app/src/components/WindowsNativeTitleBar.tsx
+++ b/packages/app/src/components/WindowsNativeTitleBar.tsx
@@ -14,19 +14,14 @@ import {
contextMenuItem,
contextMenuSeparator,
contextMenuSubmenu,
- showContextMenu,
type ContextMenuEntry
} from "./ContextMenu";
import { WindowsWindowControls } from "./WindowsWindowControls";
import { IconButton, Tooltip } from "@markra/ui";
+import { AppMenuBar, type AppMenuConfig } from "./AppMenuBar";
type WindowsAppMenuId = "app" | "file" | "edit" | "format" | "view";
-type WindowsAppMenuConfig = {
- id: WindowsAppMenuId;
- label: string;
-};
-
type WindowsNativeTitleBarProps = {
aiAgentOpen: boolean;
aiAgentResizing: boolean;
@@ -183,16 +178,21 @@ export function WindowsNativeTitleBar({
) : null;
- const windowsAppMenus: WindowsAppMenuConfig[] = [
- { id: "file", label: label("menu.file") },
- { id: "edit", label: label("menu.edit") },
- { id: "format", label: label("menu.format") },
- { id: "view", label: label("menu.view") }
+ const windowsAppMenus: AppMenuConfig[] = [
+ {
+ id: "app",
+ label: "Markra",
+ triggerClassName: "font-[620] text-(--text-heading)"
+ },
+ { id: "file", label: label("menu.file"), triggerClassName: "text-(--text-primary)" },
+ { id: "edit", label: label("menu.edit"), triggerClassName: "text-(--text-primary)" },
+ { id: "format", label: label("menu.format"), triggerClassName: "text-(--text-primary)" },
+ { id: "view", label: label("menu.view"), triggerClassName: "text-(--text-primary)" }
];
const runToggleWindowMaximized = () => {
runOptionalWindowsAction(onToggleWindowMaximized);
};
- const windowsAppMenuEntries = (menuId: WindowsAppMenuId): ContextMenuEntry[] => {
+ const windowsAppMenuEntries = (menuId: string): ContextMenuEntry[] => {
if (menuId === "app") {
return [
windowsMenuItem("showAbout", label("menu.aboutMarkra"), undefined, onShowAbout),
@@ -303,20 +303,6 @@ export function WindowsNativeTitleBar({
contextMenuItem("toggleTheme", themeActionLabel, undefined, onToggleTheme)
];
};
- const showWindowsAppMenu = (menu: WindowsAppMenuConfig, event: ReactMouseEvent) => {
- event.preventDefault();
- event.stopPropagation();
-
- const rect = event.currentTarget.getBoundingClientRect();
- showContextMenu(event.currentTarget.ownerDocument, {
- ariaLabel: menu.label,
- entries: windowsAppMenuEntries(menu.id),
- position: {
- x: rect.left,
- y: rect.bottom
- }
- });
- };
const handleWindowsTitlebarMouseDownCapture = (event: ReactMouseEvent) => {
if (!nativeWindowChrome || !onToggleWindowMaximized || isWindowsTitlebarInteractiveTarget(event.target)) return;
if (event.button !== 0 || event.detail !== 2) return;
@@ -364,27 +350,13 @@ export function WindowsNativeTitleBar({
) : null}
-
- {windowsAppMenus.map((menu) => (
-
- ))}
+ event.stopPropagation()}>
+
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5abfe581..805a8b0b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -239,6 +239,9 @@ importers:
packages/app:
dependencies:
+ '@base-ui/react':
+ specifier: ^1.7.0
+ version: 1.7.0(@types/react@19.2.7)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@codemirror/commands':
specifier: 6.10.3
version: 6.10.3
@@ -856,6 +859,33 @@ packages:
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
engines: {node: '>=6.9.0'}
+ '@base-ui/react@1.7.0':
+ resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@date-fns/tz': ^1.2.0
+ '@types/react': ^17 || ^18 || ^19
+ date-fns: ^4.0.0
+ react: ^17 || ^18 || ^19
+ react-dom: ^17 || ^18 || ^19
+ peerDependenciesMeta:
+ '@date-fns/tz':
+ optional: true
+ '@types/react':
+ optional: true
+ date-fns:
+ optional: true
+
+ '@base-ui/utils@0.3.2':
+ resolution: {integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==}
+ peerDependencies:
+ '@types/react': ^17 || ^18 || ^19
+ react: ^17 || ^18 || ^19
+ react-dom: ^17 || ^18 || ^19
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@braintree/sanitize-url@7.1.2':
resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==}
@@ -1003,6 +1033,21 @@ packages:
'@noble/hashes':
optional: true
+ '@floating-ui/core@1.8.0':
+ resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
+
+ '@floating-ui/dom@1.8.0':
+ resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
+
+ '@floating-ui/react-dom@2.1.9':
+ resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.12':
+ resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
+
'@fontsource-variable/noto-sans-sc@5.3.0':
resolution: {integrity: sha512-lNar1dF7Ik/lHNPo/7JWG0TolXY29LtsqYgMvEysooZ5bsO9uH4shJmRrwyJ3PjyTPljhpMJEK0jDuLSU4vJ1w==}
@@ -1174,36 +1219,42 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.17':
resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17':
resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17':
resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.17':
resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-rc.17':
resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-rc.17':
resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==}
@@ -1327,24 +1378,28 @@ packages:
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.2.4':
resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.2.4':
resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.2.4':
resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.2.4':
resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==}
@@ -1414,30 +1469,35 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.11.0':
resolution: {integrity: sha512-DtSE8ZBlB9H+L+eHkfZ3myt00EVEyAB3e41juEHoE2qT88fgVlJvyrwa9SZYc/xTwCS9TnmK+R84tpg+ZsAg7Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.11.0':
resolution: {integrity: sha512-5QdgS4LD+kntClI1aj2JmwjW38LosNXxwCe8viIHEwqYIWuMPdNEIau6/cLogI38Yzx9DnfCPRfEWLyI+5li8Q==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.11.0':
resolution: {integrity: sha512-5UynPXo3Zq9khjVdAbD+YogeLltdVUeOah2ioSIM3tu6H7wY9vMy6rgGJhv9r5R8ZXmk9GttMippdqYJWrnLnA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.11.0':
resolution: {integrity: sha512-CNz7fHbApz1Zyhhq73jtGn9JqgNEV/lIWnTnUo6h6ujw+mHsTmkLszvJSM8W6JBaDjNpTTFr/RSNoVL5FMwcTg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.11.0':
resolution: {integrity: sha512-K+br+VXZ+Xx0n/9FdWohpW5Ugq+2FQUpJScqcPl1hTxXfh3fgjYgt4qA2NgrjlJo+zZPNrmUMl+NLvm0ufEqBQ==}
@@ -2350,24 +2410,28 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [musl]
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
@@ -2743,6 +2807,9 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ reselect@5.3.0:
+ resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==}
+
retry@0.13.1:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
@@ -2950,6 +3017,11 @@ packages:
unist-util-visit@5.1.0:
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
uuid@14.0.0:
resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==}
hasBin: true
@@ -3495,6 +3567,29 @@ snapshots:
'@babel/runtime@7.29.2': {}
+ '@base-ui/react@1.7.0(@types/react@19.2.7)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@babel/runtime': 7.29.2
+ '@base-ui/utils': 0.3.2(@types/react@19.2.7)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ '@floating-ui/react-dom': 2.1.9(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ '@floating-ui/utils': 0.2.12
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+ use-sync-external-store: 1.6.0(react@19.2.5)
+ optionalDependencies:
+ '@types/react': 19.2.7
+
+ '@base-ui/utils@0.3.2(@types/react@19.2.7)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@babel/runtime': 7.29.2
+ '@floating-ui/utils': 0.2.12
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+ reselect: 5.3.0
+ use-sync-external-store: 1.6.0(react@19.2.5)
+ optionalDependencies:
+ '@types/react': 19.2.7
+
'@braintree/sanitize-url@7.1.2': {}
'@bramus/specificity@2.4.2':
@@ -3708,6 +3803,23 @@ snapshots:
optionalDependencies:
'@noble/hashes': 2.2.0
+ '@floating-ui/core@1.8.0':
+ dependencies:
+ '@floating-ui/utils': 0.2.12
+
+ '@floating-ui/dom@1.8.0':
+ dependencies:
+ '@floating-ui/core': 1.8.0
+ '@floating-ui/utils': 0.2.12
+
+ '@floating-ui/react-dom@2.1.9(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+
+ '@floating-ui/utils@0.2.12': {}
+
'@fontsource-variable/noto-sans-sc@5.3.0': {}
'@google/genai@1.52.0':
@@ -5729,6 +5841,8 @@ snapshots:
require-from-string@2.0.2: {}
+ reselect@5.3.0: {}
+
retry@0.13.1: {}
robust-predicates@3.0.3: {}
@@ -5940,6 +6054,10 @@ snapshots:
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
+ use-sync-external-store@1.6.0(react@19.2.5):
+ dependencies:
+ react: 19.2.5
+
uuid@14.0.0: {}
validate-npm-package-license@3.0.4: