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
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { dirname } from "node:path";
import type { IStoragePaths } from "@posthog/platform/storage-paths";
import { app } from "electron";
import { injectable } from "inversify";
import { getLogFilePath } from "../utils/logger";

@injectable()
export class ElectronStoragePaths implements IStoragePaths {
Expand All @@ -11,4 +13,8 @@ export class ElectronStoragePaths implements IStoragePaths {
public get logsPath(): string {
return app.getPath("logs");
}

public get logFolderPath(): string {
return dirname(getLogFilePath());
}
}
4 changes: 4 additions & 0 deletions packages/host-router/src/routers/os.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export const osRouter = router({
ctx.container.get<OsService>(OS_SERVICE).openExternal(input.url),
),

showLogFolder: publicProcedure.mutation(({ ctx }) =>
ctx.container.get<OsService>(OS_SERVICE).showLogFolder(),
),

searchDirectories: publicProcedure
.input(searchDirectoriesInput)
.query(({ ctx, input }) =>
Expand Down
1 change: 1 addition & 0 deletions packages/platform/src/storage-paths.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface IStoragePaths {
readonly appDataPath: string;
readonly logsPath: string;
readonly logFolderPath: string;
}

export const STORAGE_PATHS_SERVICE = Symbol.for(
Expand Down
6 changes: 5 additions & 1 deletion packages/shared/src/analytics-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ export type CommandMenuAction =
| "toggle-theme"
| "toggle-left-sidebar"
| "open-review-panel"
| "go-back"
| "go-forward"
| "open-task"
| "open-channel";
| "open-channel"
| "reload-window"
| "show-log-folder";

// Event property interfaces
export interface TaskListViewProperties {
Expand Down
77 changes: 72 additions & 5 deletions packages/ui/src/features/command/CommandMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HashIcon } from "@phosphor-icons/react";
import { CaretLeftIcon, CaretRightIcon, HashIcon } from "@phosphor-icons/react";
import {
Autocomplete,
AutocompleteCollection,
Expand All @@ -10,6 +10,7 @@ import {
AutocompleteStatus,
Dialog,
DialogContent,
Kbd,
} from "@posthog/quill";
import { PROJECT_BLUEBIRD_FLAG } from "@posthog/shared";
import {
Expand All @@ -21,6 +22,10 @@ import { useChannels } from "@posthog/ui/features/canvas/hooks/useChannels";
import { useTaskChannelMap } from "@posthog/ui/features/canvas/hooks/useTaskChannelMap";
import { useReviewNavigationStore } from "@posthog/ui/features/code-review/reviewNavigationStore";
import { CommandKeyHints } from "@posthog/ui/features/command/CommandKeyHints";
import {
formatHotkeyParts,
SHORTCUTS,
} from "@posthog/ui/features/command/keyboard-shortcuts";
import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag";
import { useFolders } from "@posthog/ui/features/folders/useFolders";
import {
Expand All @@ -31,17 +36,23 @@ import { TaskIcon } from "@posthog/ui/features/sidebar/components/items/TaskIcon
import { useSidebarStore } from "@posthog/ui/features/sidebar/sidebarStore";
import { useTaskPrStatus } from "@posthog/ui/features/sidebar/useTaskPrStatus";
import { useTasks } from "@posthog/ui/features/tasks/useTasks";
import { navigateToChannel } from "@posthog/ui/router/navigationBridge";
import {
goBackInHistory,
goForwardInHistory,
navigateToChannel,
} from "@posthog/ui/router/navigationBridge";
import { useAppView } from "@posthog/ui/router/useAppView";
import { openTask, openTaskInput } from "@posthog/ui/router/useOpenTask";
import { track } from "@posthog/ui/shell/analytics";
import { showLogFolder } from "@posthog/ui/shell/openExternal";
import { useThemeStore } from "@posthog/ui/shell/themeStore";
import {
DesktopIcon,
FileTextIcon,
GearIcon,
HomeIcon,
MoonIcon,
ReloadIcon,
SunIcon,
ViewVerticalIcon,
} from "@radix-ui/react-icons";
Expand All @@ -62,6 +73,8 @@ type Command = {
action: CommandMenuAction;
/** Channel in scope for the bluebird open-channel / open-task actions. */
channelId?: string;
/** Hotkey string (e.g. "mod+b") shown right-aligned when present. */
shortcut?: string;
onRun: () => void;
};

Expand Down Expand Up @@ -208,8 +221,27 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
label: "Settings",
icon: <GearIcon className="h-3 w-3 text-gray-11" />,
action: "settings",
shortcut: SHORTCUTS.SETTINGS,
onRun: () => openSettingsDialog(),
},
{
id: "go-back",
label: "Go back",
keywords: "navigate history previous",
icon: <CaretLeftIcon size={12} className="text-gray-11" />,
action: "go-back",
shortcut: SHORTCUTS.GO_BACK,
onRun: goBackInHistory,
},
{
id: "go-forward",
label: "Go forward",
keywords: "navigate history next",
icon: <CaretRightIcon size={12} className="text-gray-11" />,
action: "go-forward",
shortcut: SHORTCUTS.GO_FORWARD,
onRun: goForwardInHistory,
},
];

const actions: Command[] = [
Expand All @@ -219,6 +251,7 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
label: "Toggle left sidebar",
icon: <ViewVerticalIcon className="h-3 w-3 text-gray-11" />,
action: "toggle-left-sidebar",
shortcut: SHORTCUTS.TOGGLE_LEFT_SIDEBAR,
onRun: toggleLeftSidebar,
},
...(reviewTaskId
Expand All @@ -230,6 +263,7 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
<ViewVerticalIcon className="h-3 w-3 rotate-180 text-gray-11" />
),
action: "open-review-panel" as CommandMenuAction,
shortcut: SHORTCUTS.TOGGLE_REVIEW_PANEL,
onRun: openReviewPanel,
},
]
Expand All @@ -240,16 +274,38 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
keywords: "create",
icon: <FileTextIcon className="h-3 w-3 text-gray-11" />,
action: "new-task",
shortcut: SHORTCUTS.NEW_TASK,
onRun: () => {
closeSettingsDialog();
openTaskInput();
},
},
];

const developer: Command[] = [
{
id: "show-log-folder",
label: "Show log folder",
keywords: "logs debug files finder",
icon: <FileTextIcon className="h-3 w-3 text-gray-11" />,
action: "show-log-folder",
onRun: showLogFolder,
},
{
id: "reload-window",
label: "Reload window",
keywords: "refresh restart",
icon: <ReloadIcon className="h-3 w-3 text-gray-11" />,
action: "reload-window",
shortcut: SHORTCUTS.RELOAD_WINDOW,
onRun: () => window.location.reload(),
},
];

const out: CommandSection[] = [
{ label: "Navigation", items: navigation },
{ label: "Actions", items: actions },
{ label: "Navigation", items: navigation },
{ label: "Developer", items: developer },
];

if (folders.length > 0) {
Expand Down Expand Up @@ -409,8 +465,12 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
value={cmd.id}
onClick={() => handleSelect(cmd.id)}
// Long task names wrap instead of truncating, so the
// item must grow: min-height, not a fixed height.
className="h-auto! min-h-7 py-1.5 text-left"
// item must grow: min-height, not a fixed height. Quill
// wraps our children in an inner content span; force it to
// fill the row (so a trailing shortcut can `ml-auto` to the
// end) and let it overflow visibly so the shortcut Kbd
// boxes aren't clipped by the wrapper's `truncate`.
className="flex h-auto! min-h-7 w-full items-center gap-2 py-1.5 pr-2 text-left [&>span]:w-full [&>span]:overflow-visible"
>
{cmd.icon}
<span className="wrap-break-word min-w-0 whitespace-normal">
Expand All @@ -421,6 +481,13 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
· #{cmd.detail}
</span>
)}
{cmd.shortcut && (
<span className="ml-auto flex shrink-0 items-center gap-2 pl-2">
{formatHotkeyParts(cmd.shortcut).map((part) => (
<Kbd key={part}>{part}</Kbd>
))}
</span>
)}
</AutocompleteItem>
)}
</AutocompleteCollection>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/features/command/keyboard-shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const SHORTCUTS = {
BLUR: "escape",
SUBMIT_BLUR: "mod+enter",
SWITCH_MESSAGING_MODE: "mod+s",
RELOAD_WINDOW: "mod+shift+r",
} as const;

export type ShortcutCategory = "general" | "navigation" | "panels" | "editor";
Expand Down
5 changes: 5 additions & 0 deletions packages/ui/src/shell/GlobalEventHandlers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ export function GlobalEventHandlers({
setReviewMode(currentTaskId, mode === "closed" ? "split" : "closed");
}, [currentTaskId, getReviewMode, setReviewMode]);

useHotkeys(
SHORTCUTS.RELOAD_WINDOW,
() => window.location.reload(),
globalOptions,
);
useHotkeys(SHORTCUTS.TOGGLE_LEFT_SIDEBAR, toggleLeftSidebar, globalOptions);
useHotkeys(SHORTCUTS.TOGGLE_REVIEW_PANEL, handleToggleReview, globalOptions);
useHotkeys(SHORTCUTS.SHORTCUTS_SHEET, onToggleShortcutsSheet, globalOptions);
Expand Down
6 changes: 6 additions & 0 deletions packages/ui/src/shell/openExternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ export function openExternalUrl(url: string): void {
url,
});
}

export function showLogFolder(): void {
void resolveService<HostTrpcClient>(
HOST_TRPC_CLIENT,
).os.showLogFolder.mutate();
}
15 changes: 15 additions & 0 deletions packages/workspace-server/src/services/os/os.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ function createService() {
getWorktreeLocation: vi.fn(() => "/tmp/worktrees"),
};

const storagePaths = {
appDataPath: "/data",
logsPath: "/logs",
logFolderPath: "/logs",
};

const service = new OsService(
dialog as never,
urlLauncher as never,
appMeta as never,
imageProcessor as never,
workspaceSettings as never,
storagePaths as never,
);

return { service, dialog, urlLauncher, appMeta, workspaceSettings };
Expand Down Expand Up @@ -152,6 +159,14 @@ describe("OsService simple delegations", () => {
await service.openExternal("https://posthog.com");
expect(urlLauncher.launch).toHaveBeenCalledWith("https://posthog.com");
});

it("opens the log folder as a file URL via the url launcher", async () => {
const { service, urlLauncher } = createService();
await service.showLogFolder();
expect(urlLauncher.launch).toHaveBeenCalledWith(
expect.stringMatching(/^file:\/\//),
);
});
});

describe("OsService.getClaudePermissions", () => {
Expand Down
13 changes: 13 additions & 0 deletions packages/workspace-server/src/services/os/os.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { APP_META_SERVICE, type IAppMeta } from "@posthog/platform/app-meta";
import {
DIALOG_SERVICE,
Expand All @@ -11,6 +12,10 @@ import {
type IImageProcessor,
IMAGE_PROCESSOR_SERVICE,
} from "@posthog/platform/image-processor";
import {
type IStoragePaths,
STORAGE_PATHS_SERVICE,
} from "@posthog/platform/storage-paths";
import {
type IUrlLauncher,
URL_LAUNCHER_SERVICE,
Expand Down Expand Up @@ -55,6 +60,8 @@ export class OsService {
private readonly imageProcessor: IImageProcessor,
@inject(WORKSPACE_SETTINGS_SERVICE)
private readonly workspaceSettings: IWorkspaceSettings,
@inject(STORAGE_PATHS_SERVICE)
private readonly storagePaths: IStoragePaths,
) {}

async getClaudePermissions(): Promise<ClaudePermissions> {
Expand Down Expand Up @@ -162,6 +169,12 @@ export class OsService {
await this.urlLauncher.launch(url);
}

async showLogFolder(): Promise<void> {
await this.urlLauncher.launch(
pathToFileURL(this.storagePaths.logFolderPath).href,
);
}

async searchDirectories(query: string): Promise<string[]> {
if (!query?.trim()) return [];

Expand Down
Loading