Skip to content
Open
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
8 changes: 8 additions & 0 deletions electron/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ export function createEditorWindow(): BrowserWindow {

win.maximize();

if (process.platform !== "darwin") {
win.setAutoHideMenuBar(true);
}

// Show only once painted to avoid a white flash on cold Vite start.
win.once("ready-to-show", () => {
if (!HEADLESS) win.show();
Expand Down Expand Up @@ -345,6 +349,10 @@ export function createNotesWindow(): BrowserWindow {
},
});

if (process.platform !== "darwin") {
win.setAutoHideMenuBar(true);
}

win.setContentProtection(true);
win.once("ready-to-show", () => {
win.setContentProtection(true);
Expand Down
105 changes: 105 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,12 @@
"main": "dist-electron/main.js",
"lint-staged": {
"*.{ts,tsx,js,jsx,mts,cts,json}": "biome check --no-errors-on-unmatched"
},
"allowScripts": {
"electron@41.2.1": true,
"esbuild@0.27.7": true,
"sharp@0.32.6": true,
"protobufjs@6.11.6": true,
"electron-winstaller@5.4.0": true
}
}
152 changes: 139 additions & 13 deletions src/components/video-editor/VideoEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Span } from "dnd-timeline";
import { FolderOpen, Languages, Save, Video } from "lucide-react";
import { ChevronDown, FolderOpen, Languages, Save, Video } from "lucide-react";
import { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { toast } from "sonner";
Expand All @@ -12,6 +12,14 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Label } from "@/components/ui/label";
import {
Select,
Expand Down Expand Up @@ -198,6 +206,8 @@ export default function VideoEditor() {
undo,
redo,
resetState,
canUndo,
canRedo,
} = useEditorHistory(INITIAL_EDITOR_STATE);

const {
Expand Down Expand Up @@ -2731,17 +2741,132 @@ export default function VideoEditor() {
style={{ WebkitAppRegion: "drag" } as CSSProperties}
>
<div
className="flex-1 flex items-center gap-1"
className="flex-1 flex items-center gap-1.5"
style={{ WebkitAppRegion: "no-drag" } as CSSProperties}
>
<div
className={`flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-white/50 hover:text-white/90 hover:bg-white/[0.08] transition-all duration-150 ${isMac ? "ml-14" : "ml-2"}`}
>
<Languages size={14} />
{/* Custom Window Menus */}
<div className={`flex items-center gap-0.5 ${isMac ? "ml-14" : "ml-2"}`}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className="px-2.5 py-1.5 rounded-lg text-[13px] font-semibold text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 outline-none focus-visible:ring-1 focus-visible:ring-white/20 focus-visible:bg-white/[0.08]"
>
{rawT("common.actions.file" as never) || "File"}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="bg-[#09090b]/95 backdrop-blur-md border border-white/[0.08] text-slate-200 min-w-[170px]"
>
<DropdownMenuItem
onClick={handleNewProject}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between"
>
<span>{rawT("dialogs.unsavedChanges.newProject" as never) || "New Project"}</span>
<DropdownMenuShortcut className="ml-2">Ctrl+N</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator className="bg-white/[0.08]" />
<DropdownMenuItem
onClick={handleLoadProject}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between"
>
<span>
{rawT("dialogs.unsavedChanges.loadProject" as never) || "Load Project…"}
</span>
<DropdownMenuShortcut className="ml-2">Ctrl+O</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem
onClick={handleSaveProject}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between"
>
<span>
{rawT("dialogs.unsavedChanges.saveProject" as never) || "Save Project…"}
</span>
<DropdownMenuShortcut className="ml-2">Ctrl+S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem
onClick={handleSaveProjectAs}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between"
>
<span>
{rawT("dialogs.unsavedChanges.saveProjectAs" as never) || "Save Project As…"}
</span>
<DropdownMenuShortcut className="ml-2">Ctrl+Shift+S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator className="bg-white/[0.08]" />
<DropdownMenuItem
onClick={() => window.close()}
className="hover:bg-red-500/20 focus:bg-red-500/20 focus:text-red-400 text-red-400 cursor-pointer justify-between"
>
<span>{rawT("common.actions.quit" as never) || "Quit"}</span>
<DropdownMenuShortcut className="ml-2">Ctrl+Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className="px-2.5 py-1.5 rounded-lg text-[13px] font-semibold text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 outline-none focus-visible:ring-1 focus-visible:ring-white/20 focus-visible:bg-white/[0.08]"
>
{rawT("common.actions.edit" as never) || "Edit"}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="bg-[#09090b]/95 backdrop-blur-md border border-white/[0.08] text-slate-200 min-w-[130px]"
>
<DropdownMenuItem
onClick={undo}
disabled={!canUndo}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between disabled:opacity-40 disabled:pointer-events-none"
>
<span>{rawT("common.actions.undo" as never) || "Undo"}</span>
<DropdownMenuShortcut className="ml-2">Ctrl+Z</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem
onClick={redo}
disabled={!canRedo}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between disabled:opacity-40 disabled:pointer-events-none"
>
<span>{rawT("common.actions.redo" as never) || "Redo"}</span>
<DropdownMenuShortcut className="ml-2">Ctrl+Y</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className="px-2.5 py-1.5 rounded-lg text-[13px] font-semibold text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 outline-none focus-visible:ring-1 focus-visible:ring-white/20 focus-visible:bg-white/[0.08]"
>
{rawT("common.actions.view" as never) || "View"}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="bg-[#09090b]/95 backdrop-blur-md border border-white/[0.08] text-slate-200 min-w-[130px]"
>
<DropdownMenuItem
onClick={() => window.location.reload()}
className="hover:bg-white/[0.08] focus:bg-white/[0.08] focus:text-white cursor-pointer justify-between"
>
<span>{rawT("common.actions.reload" as never) || "Reload"}</span>
<DropdownMenuShortcut className="ml-2">Ctrl+R</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
Comment on lines +2748 to +2862

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keyboard shortcut labels should be platform-aware.

All shortcut hints are hardcoded as Ctrl+… (e.g., Ctrl+N, Ctrl+Z). On macOS, users expect (Cmd) as the modifier. The isMac variable is already available at line 326 from useShortcuts().

✨ Proposed fix
- // Near line 326, derive a display modifier:
- // const { shortcuts, isMac } = useShortcuts();
+ const { shortcuts, isMac } = useShortcuts();
+ const modKey = isMac ? "⌘" : "Ctrl";

 // Then in each DropdownMenuShortcut:
- <DropdownMenuShortcut className="ml-2">Ctrl+N</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+N</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+O</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+O</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+S</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+S</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+Shift+S</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{isMac ? "⌘⇧" : "Ctrl+Shift"}+S</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+Q</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+Q</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+Z</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+Z</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+Y</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+Y</DropdownMenuShortcut>

- <DropdownMenuShortcut className="ml-2">Ctrl+R</DropdownMenuShortcut>
+ <DropdownMenuShortcut className="ml-2">{modKey}+R</DropdownMenuShortcut>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/video-editor/VideoEditor.tsx` around lines 2748 - 2862, Update
the shortcut labels in the File and Edit menus to use the existing isMac value:
display ⌘ for macOS and Ctrl otherwise. Apply this to the New, Load, Save, Save
As, Quit, Undo, and Redo DropdownMenuShortcut elements while leaving the
shortcut behavior unchanged.


<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 ml-1">
<Languages size={15} />
<select
value={locale}
onChange={(e) => setLocale(e.target.value as Locale)}
className="bg-transparent text-[11px] font-medium outline-none cursor-pointer appearance-none pr-1"
className="bg-transparent text-[13px] font-semibold outline-none cursor-pointer appearance-none pr-1"
style={{ color: "inherit" }}
>
{availableLocales.map((loc) => (
Expand All @@ -2750,29 +2875,30 @@ export default function VideoEditor() {
</option>
))}
</select>
<ChevronDown size={12} className="opacity-70 ml-0.5 flex-shrink-0" />
</div>
<button
type="button"
onClick={() => setShowNewRecordingDialog(true)}
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-white/50 hover:text-white/90 hover:bg-white/[0.08] transition-all duration-150 text-[11px] font-medium"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 text-[13px] font-medium"
>
<Video size={14} />
<Video size={15} />
{t("newRecording.title")}
</button>
<button
type="button"
onClick={handleLoadProject}
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-white/50 hover:text-white/90 hover:bg-white/[0.08] transition-all duration-150 text-[11px] font-medium"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 text-[13px] font-medium"
>
<FolderOpen size={14} />
<FolderOpen size={15} />
{ts("project.load")}
</button>
<button
type="button"
onClick={handleSaveProject}
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-white/50 hover:text-white/90 hover:bg-white/[0.08] transition-all duration-150 text-[11px] font-medium"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-slate-300 hover:text-white hover:bg-white/[0.08] transition-all duration-150 text-[13px] font-medium"
>
<Save size={14} />
<Save size={15} />
{ts("project.save")}
</button>
</div>
Expand Down
Loading
Loading