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
2 changes: 2 additions & 0 deletions apps/desktop/src/lib/trpc/routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { BrowserWindow } from "electron";
import { router } from "..";
import { createConfigRouter } from "./config";
import { createExternalRouter } from "./external";
import { createMenuRouter } from "./menu";
import { createNotificationsRouter } from "./notifications";
import { createProjectsRouter } from "./projects";
import { createSettingsRouter } from "./settings";
Expand All @@ -23,6 +24,7 @@ export const createAppRouter = (getWindow: () => BrowserWindow | null) => {
workspaces: createWorkspacesRouter(),
terminal: createTerminalRouter(),
notifications: createNotificationsRouter(),
menu: createMenuRouter(),
external: createExternalRouter(),
settings: createSettingsRouter(),
config: createConfigRouter(),
Expand Down
27 changes: 27 additions & 0 deletions apps/desktop/src/lib/trpc/routers/menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { observable } from "@trpc/server/observable";
import {
menuEmitter,
type OpenSettingsEvent,
type SettingsSection,
} from "main/lib/menu-events";
import { publicProcedure, router } from "..";

type MenuEvent = { type: "open-settings"; data: OpenSettingsEvent };

export const createMenuRouter = () => {
return router({
subscribe: publicProcedure.subscription(() => {
return observable<MenuEvent>((emit) => {
const onOpenSettings = (section?: SettingsSection) => {
emit.next({ type: "open-settings", data: { section } });
};

menuEmitter.on("open-settings", onOpenSettings);

return () => {
menuEmitter.off("open-settings", onOpenSettings);
};
});
}),
});
};
13 changes: 13 additions & 0 deletions apps/desktop/src/main/lib/menu-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { EventEmitter } from "node:events";

export type SettingsSection =
| "project"
| "workspace"
| "appearance"
| "keyboard";

export interface OpenSettingsEvent {
section?: SettingsSection;
}

export const menuEmitter = new EventEmitter();
37 changes: 31 additions & 6 deletions apps/desktop/src/main/lib/menu.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { app, type BrowserWindow, Menu } from "electron";
import { app, Menu, shell } from "electron";
import { HELP_MENU } from "shared/constants";
import { checkForUpdatesInteractive } from "./auto-updater";
import { menuEmitter } from "./menu-events";

export function createApplicationMenu(mainWindow: BrowserWindow) {
export function createApplicationMenu() {
const template: Electron.MenuItemConstructorOptions[] = [
{
label: "File",
Expand Down Expand Up @@ -47,18 +49,41 @@ export function createApplicationMenu(mainWindow: BrowserWindow) {
{ role: "minimize" },
{ role: "zoom" },
{ type: "separator" },
{ role: "close", accelerator: "CmdOrCtrl+Shift+W" },
],
},
{
label: "Help",
submenu: [
{
label: "Contact Us",
click: () => {
shell.openExternal(HELP_MENU.CONTACT_URL);
},
},
{
label: "Report Issue",
click: () => {
shell.openExternal(HELP_MENU.REPORT_ISSUE_URL);
},
},
{
label: "Join Discord",
click: () => {
shell.openExternal(HELP_MENU.DISCORD_URL);
},
},
{ type: "separator" },
{
label: "Close Window",
accelerator: "CmdOrCtrl+Shift+W",
label: "Keyboard Shortcuts",
click: () => {
mainWindow.close();
menuEmitter.emit("open-settings", "keyboard");
},
},
],
},
];

// Add About menu on macOS
if (process.platform === "darwin") {
template.unshift({
label: app.name,
Expand Down
8 changes: 1 addition & 7 deletions apps/desktop/src/main/windows/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,11 @@ export async function MainWindow() {
},
});

// Set main window for auto-updater dialogs
setMainWindow(window);
createApplicationMenu();

// Create application menu
createApplicationMenu(window);

// Update current window reference for router getter
currentWindow = window;

// Set up tRPC handler - reuse existing handler on macOS window reopen
// Router uses getWindow() to always access current window
if (ipcHandler) {
ipcHandler.attachWindow(window);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@superset/ui/dropdown-menu";
import { Kbd, KbdGroup } from "@superset/ui/kbd";
import { FaDiscord } from "react-icons/fa";
import {
HiOutlineBugAnt,
HiOutlineCommandLine,
HiOutlineEnvelope,
HiOutlineQuestionMarkCircle,
} from "react-icons/hi2";
import { useOpenSettings } from "renderer/stores";
import { HELP_MENU } from "shared/constants";
import { formatKeysForDisplay, HOTKEYS } from "shared/hotkeys";

export function HelpMenu() {
const openSettings = useOpenSettings();
const hotkeyKeys = formatKeysForDisplay(HOTKEYS.SHOW_HOTKEYS.keys);

const handleContactUs = () => {
window.open(HELP_MENU.CONTACT_URL, "_blank");
};
Comment on lines +24 to +26
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix incorrect mailto: link handling.

Using window.open with a mailto: link and "_blank" target is incorrect. Mailto links open the user's default email client and don't work with window targets.

Apply this diff to fix the handler:

 const handleContactUs = () => {
-	window.open(CONTACT_EMAIL, "_blank");
+	window.location.href = CONTACT_EMAIL;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleContactUs = () => {
window.open(CONTACT_EMAIL, "_blank");
};
const handleContactUs = () => {
window.location.href = CONTACT_EMAIL;
};
🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/screens/main/components/TopBar/HelpMenu/HelpMenu.tsx
around lines 27 to 29, the handler opens a mailto link with window.open(...,
"_blank") which is incorrect; change it to navigate the current window to a
properly formed mailto URL. Build the mailto string (prepend "mailto:" if
CONTACT_EMAIL doesn't already start with it) and assign it to
window.location.href (or use window.location.assign) so the user's default email
client is launched.


const handleReportIssue = () => {
window.open(HELP_MENU.REPORT_ISSUE_URL, "_blank");
};

const handleJoinDiscord = () => {
window.open(HELP_MENU.DISCORD_URL, "_blank");
};

const handleViewHotkeys = () => {
openSettings("keyboard");
};

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className="no-drag flex h-8 w-8 items-center justify-center rounded-md text-accent-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
aria-label="Help menu"
>
<HiOutlineQuestionMarkCircle className="h-4 w-4" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" side="bottom" className="w-64">
<DropdownMenuItem onClick={handleContactUs}>
<HiOutlineEnvelope className="h-4 w-4" />
Contact Us
</DropdownMenuItem>
<DropdownMenuItem onClick={handleReportIssue}>
<HiOutlineBugAnt className="h-4 w-4" />
Report Issue
</DropdownMenuItem>
<DropdownMenuItem onClick={handleJoinDiscord}>
<FaDiscord className="h-4 w-4" />
Join Discord
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleViewHotkeys}>
<HiOutlineCommandLine className="h-4 w-4" />
<span className="flex-1">Keyboard Shortcuts</span>
<KbdGroup>
{hotkeyKeys.map((key) => (
<Kbd key={key}>{key}</Kbd>
))}
</KbdGroup>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { HelpMenu } from "./HelpMenu";
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { trpc } from "renderer/lib/trpc";
import { WorkspaceHeader } from "../WorkspaceView/WorkspaceHeader";
import { HelpMenu } from "./HelpMenu";
import { SettingsButton } from "./SettingsButton";
import { SidebarControl } from "./SidebarControl";
import { WindowControls } from "./WindowControls";
Expand All @@ -25,6 +26,7 @@ export function TopBar() {
<div className="flex items-center gap-2 h-full pr-4">
<WorkspaceHeader worktreePath={activeWorkspace?.worktreePath} />
<SettingsButton />
<HelpMenu />
{!isMac && <WindowControls />}
</div>
</div>
Expand Down
14 changes: 8 additions & 6 deletions apps/desktop/src/renderer/screens/main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,16 @@ export function MainScreen() {
const focusedPaneIds = useWindowsStore((s) => s.focusedPaneIds);
const windows = useWindowsStore((s) => s.windows);

// Listen for agent completion hooks from main process
useAgentHookListener();

trpc.menu.subscribe.useSubscription(undefined, {
onData: (event) => {
if (event.type === "open-settings") {
openSettings(event.data.section);
}
},
});

const activeWorkspaceId = activeWorkspace?.id;
const activeWindowId = activeWorkspaceId
? activeWindowIds[activeWorkspaceId]
Expand All @@ -58,7 +65,6 @@ export function MainScreen() {
const activeWindow = windows.find((w) => w.id === activeWindowId);
const isWorkspaceView = currentView === "workspace";

// Register global shortcuts
useHotkeys(HOTKEYS.SHOW_HOTKEYS.keys, () => openSettings("keyboard"), [
openSettings,
]);
Expand Down Expand Up @@ -149,15 +155,13 @@ export function MainScreen() {
const showStartView =
!isLoading && !activeWorkspace && currentView !== "settings";

// Determine which content view to show
const renderContent = () => {
if (currentView === "settings") {
return <SettingsView />;
}
return <WorkspaceView />;
};

// Show loading spinner while query is in flight
if (isLoading) {
return (
<DndProvider manager={dragDropManager}>
Expand All @@ -171,8 +175,6 @@ export function MainScreen() {
);
}

// Show error state with retry option
// Note: failureCount resets automatically on successful query
if (isError) {
const hasRepeatedFailures = failureCount >= 5;

Expand Down
7 changes: 7 additions & 0 deletions apps/desktop/src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export const CONFIG_FILE_NAME = "config.json";
// Website URL - defaults to production, can be overridden via env var for local dev
export const WEBSITE_URL = process.env.WEBSITE_URL || "https://superset.sh";

// Help menu URLs
export const HELP_MENU = {
CONTACT_URL: "https://x.com/superset_sh",
REPORT_ISSUE_URL: "https://github.com/superset-sh/superset/issues/new",
DISCORD_URL: "https://discord.gg/cZeD9WYcV7",
} as const;

// Config file template
export const CONFIG_TEMPLATE = `{
"setup": [],
Expand Down