Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b151f8b
feat: show feature splash before login for new users
adboio Mar 17, 2026
d97bd71
Fix wordmark import to use existing SVG asset
charlesvien Apr 12, 2026
33d971f
Migrate onboarding components from stale useAuthStore to query-based …
charlesvien Apr 12, 2026
27e52ef
Replace folder icon with git branch icon for repo groups
charlesvien Apr 14, 2026
bcd92da
Unify welcome screen into the onboarding wizard flow
charlesvien Apr 14, 2026
6c9be32
Add support link and icons to onboarding footer buttons
charlesvien Apr 14, 2026
3738aa5
Extract external links into shared constants
charlesvien Apr 14, 2026
00e3107
Add org switcher to project select and standardize step buttons
charlesvien Apr 14, 2026
5f9e5e0
Add hover animation to welcome screen feature list items
charlesvien Apr 14, 2026
aa83a6b
Revamp welcome screen feature list and bold step titles
charlesvien Apr 14, 2026
7a1e3bb
Add product workbench subtitle to welcome screen
charlesvien Apr 14, 2026
54c6fe8
Add auto-cycling highlight and staggered entrance to feature list
charlesvien Apr 14, 2026
456290c
Remove duplicate and unused image assets
charlesvien Apr 14, 2026
a664f44
Extract StepActions and bake animation into OnboardingHogTip
charlesvien Apr 14, 2026
56cdf67
Add talking wobble animation to hedgehog tips
charlesvien Apr 14, 2026
1384709
Reorder welcome screen feature list
charlesvien Apr 14, 2026
447e18b
Prefer user's current team when auto-selecting project
charlesvien Apr 14, 2026
56e5247
Rework project select step for unauthenticated state
charlesvien Apr 14, 2026
5ddff3d
Fix repos not iterable when API returns non-array data
charlesvien Apr 14, 2026
6ac90bb
Respect dark mode in onboarding and auth screens
charlesvien Apr 14, 2026
9612f1a
Use happy-hog in project select and tweak GitHub copy
charlesvien Apr 14, 2026
64c7446
Fix org switch loading flash with bridging state flag
charlesvien Apr 14, 2026
253a1bb
Drop Oxford comma from repo summary text
charlesvien Apr 14, 2026
b74d197
Adopt upstream SignalSourceToggles for signals step
charlesvien Apr 14, 2026
f0fb0d8
Remove billing onboarding steps
charlesvien Apr 14, 2026
da6852b
Update TaskInput.tsx
charlesvien Apr 14, 2026
e5df46e
Fix crash on logout during onboarding
charlesvien Apr 14, 2026
ca14bc9
Use optional auth client in useIntegrations
charlesvien Apr 14, 2026
4bede02
Extract shared DotPatternBackground component
charlesvien Apr 14, 2026
8b79bef
Remove session replay and evaluations from signal sources
charlesvien Apr 14, 2026
6c39ac8
Polish onboarding copy and consolidate feature list to 4 items
charlesvien Apr 14, 2026
2e156cc
Pluralize agent references across onboarding
charlesvien Apr 14, 2026
1a3187e
Rework welcome screen feature list and signals copy
charlesvien Apr 14, 2026
0ab6b02
Bold arrow icons on onboarding buttons
charlesvien Apr 14, 2026
b6fde6c
Move context collection from onboarding to sidebar setup tab
charlesvien Apr 14, 2026
69d30a2
Remove task examples from task input
charlesvien Apr 14, 2026
5dcd916
Add CLI tools install step to onboarding
charlesvien Apr 14, 2026
73ade3a
Improve signals step hog tip copy
charlesvien Apr 14, 2026
c67e13b
lint
charlesvien Apr 15, 2026
b78492e
Add keyboard navigation to onboarding flow
charlesvien Apr 15, 2026
fc06eeb
Fix smoke test to recognize onboarding screen as valid boot state
charlesvien Apr 15, 2026
2b3fe8d
Fix white wordmark snoot fill color
charlesvien Apr 15, 2026
4aea548
Update ProjectSwitcher.tsx
charlesvien Apr 15, 2026
30876a3
Remove unused signal source references after rebase
charlesvien Apr 16, 2026
152836e
rebase fixes
charlesvien Apr 17, 2026
8977ece
Remove redundant org selection onboarding step
charlesvien Apr 17, 2026
dd9bbac
Add missing SignalSourceConfig import
charlesvien Apr 17, 2026
baaeba4
Add subtitles to all onboarding steps
charlesvien Apr 17, 2026
5c27117
keyboard shortcuts improvements
charlesvien Apr 18, 2026
b7d6858
Move invite code gate into onboarding flow
charlesvien Apr 18, 2026
7aa4c93
Extract shared FullScreenLayout and restyle auth pages
charlesvien Apr 18, 2026
bc5346b
Extract shared SignInCard for sign in / sign up text
charlesvien Apr 19, 2026
74cd7d9
Update heading to "Pick your PostHog home base"
charlesvien Apr 19, 2026
d679c50
Polish onboarding step copy and layout
charlesvien Apr 19, 2026
c5a6362
Update Signals Inbox copy and center hog tip tail
charlesvien Apr 19, 2026
4cb9a43
Fix invite-code race gate and remove dead auth code
charlesvien Apr 20, 2026
bed4486
Fix onboarding review issues
charlesvien Apr 20, 2026
b48b59b
Remove context collection and setup view
charlesvien Apr 20, 2026
a89e442
Fix onboarding review issues
charlesvien Apr 20, 2026
6198b37
Restore upstream signal sources and project switch logic
charlesvien Apr 20, 2026
c13ef61
Fix onboarding step layout and spacing consistency
charlesvien Apr 20, 2026
76511f6
Two-column layout for signal source toggles
charlesvien Apr 20, 2026
c5c83d2
Show spinner while loading CLI tool status
charlesvien Apr 21, 2026
b5de151
Fix scroll clipping and remove responsive feature hiding
charlesvien Apr 21, 2026
b03b6db
Show scrollbar on signals onboarding step
charlesvien Apr 21, 2026
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 apps/code/src/main/services/git/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ export const commitInput = z.object({

export type CommitInput = z.infer<typeof commitInput>;

// Git CLI status
export const gitStatusOutput = z.object({
installed: z.boolean(),
version: z.string().nullable(),
});

export type GitStatusOutput = z.infer<typeof gitStatusOutput>;

// GitHub CLI status
export const ghStatusOutput = z.object({
installed: z.boolean(),
Expand Down
20 changes: 19 additions & 1 deletion apps/code/src/main/services/git/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { execFile } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);

import { execGh } from "@posthog/git/gh";
import {
getAllBranches,
Expand Down Expand Up @@ -53,6 +58,7 @@ import type {
GitHubIssue,
GitRepoInfo,
GitStateSnapshot,
GitStatusOutput,
GitSyncStatus,
OpenPrOutput,
PrActionType,
Expand Down Expand Up @@ -683,6 +689,16 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
};
}

public async getGitStatus(): Promise<GitStatusOutput> {
try {
const { stdout } = await execFileAsync("git", ["--version"]);
const version = stdout.trim().replace("git version ", "");
return { installed: true, version };
} catch {
return { installed: false, version: null };
}
}

public async getGhStatus(): Promise<GhStatusOutput> {
const versionResult = await execGh(["--version"]);
if (versionResult.exitCode !== 0) {
Expand All @@ -699,7 +715,9 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
const authResult = await execGh(["auth", "status"]);
const authenticated = authResult.exitCode === 0;
const authOutput = `${authResult.stdout}\n${authResult.stderr}`;
const usernameMatch = authOutput.match(/Logged in to github.com as (\S+)/);
const usernameMatch = authOutput.match(
/Logged in to github.com (?:as |account )(\S+)/,
);

return {
installed: true,
Expand Down
5 changes: 5 additions & 0 deletions apps/code/src/main/trpc/routers/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
ghAuthTokenOutput,
ghStatusOutput,
gitStateSnapshotSchema,
gitStatusOutput,
openPrInput,
openPrOutput,
prStatusInput,
Expand Down Expand Up @@ -269,6 +270,10 @@ export const gitRouter = router({
getService().sync(input.directoryPath, input.remote),
),

getGitStatus: publicProcedure
.output(gitStatusOutput)
.query(() => getService().getGitStatus()),

getGhStatus: publicProcedure
.output(ghStatusOutput)
.query(() => getService().getGhStatus()),
Expand Down
33 changes: 18 additions & 15 deletions apps/code/src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ function App() {
}),
);

const needsInviteCode =
isAuthenticated && hasCodeAccess === false && hasCompletedOnboarding;
const isCheckingAccess =
isAuthenticated && hasCodeAccess === null && hasCompletedOnboarding;

// Handle transition into main app — only show the dark overlay if dark mode is active
useEffect(() => {
const isInMainApp = isAuthenticated && hasCompletedOnboarding;
Expand Down Expand Up @@ -164,8 +169,16 @@ function App() {
);
}

// Four-phase rendering: auth → access gate → onboarding → main app
// Rendering: onboarding (includes auth + invite code gate) → main app
const renderContent = () => {
if (!hasCompletedOnboarding) {
return (
<motion.div key="onboarding" initial={{ opacity: 1 }}>
<OnboardingFlow />
</motion.div>
);
}

if (!isAuthenticated) {
return (
<motion.div key="auth" initial={{ opacity: 1 }}>
Expand All @@ -174,10 +187,9 @@ function App() {
);
}

// Access check loading state
if (hasCodeAccess === null) {
if (isCheckingAccess) {
return (
<motion.div key="access-check">
<motion.div key="access-check" initial={{ opacity: 1 }}>
<Flex align="center" justify="center" minHeight="100vh">
<Flex align="center" gap="3">
<Spinner size="3" />
Expand All @@ -188,23 +200,14 @@ function App() {
);
}

// Access gate: show invite code screen if flag is not enabled
if (!hasCodeAccess) {
if (needsInviteCode) {
return (
<motion.div key="invite-code">
<motion.div key="invite-code" initial={{ opacity: 1 }}>
<InviteCodeScreen />
</motion.div>
);
}

if (!hasCompletedOnboarding) {
return (
<motion.div key="onboarding">
<OnboardingFlow />
</motion.div>
);
}

return (
<motion.div
key="main"
Expand Down
3 changes: 2 additions & 1 deletion apps/code/src/renderer/api/posthogClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,8 @@ export class PostHogAPIClient {

const data = await response.json();

const repos = data.repositories ?? data.results ?? data ?? [];
const repos =
data.repositories ?? data.results ?? (Array.isArray(data) ? data : []);
return repos.map((repo: string | { full_name?: string; name?: string }) => {
if (typeof repo === "string") return repo;
return (repo.full_name ?? repo.name ?? "").toLowerCase();
Expand Down
Binary file removed apps/code/src/renderer/assets/images/bw-logo.png
Binary file not shown.
Binary file removed apps/code/src/renderer/assets/images/cave-hero.jpg
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 0 additions & 15 deletions apps/code/src/renderer/assets/images/logomark.svg

This file was deleted.

6 changes: 0 additions & 6 deletions apps/code/src/renderer/assets/images/tree-bg.svg

This file was deleted.

22 changes: 22 additions & 0 deletions apps/code/src/renderer/assets/images/wordmark-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions apps/code/src/renderer/components/DotPatternBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useId } from "react";

const DOT_FILL = "var(--gray-6)";

interface DotPatternBackgroundProps {
style?: React.CSSProperties;
}

export function DotPatternBackground({ style }: DotPatternBackgroundProps) {
const patternId = useId();

return (
<svg
aria-hidden="true"
style={{
position: "absolute",
bottom: 0,
left: 0,
width: "100%",
height: "100%",
pointerEvents: "none",
opacity: 0.4,
maskImage: "linear-gradient(to top, black 0%, transparent 100%)",
WebkitMaskImage: "linear-gradient(to top, black 0%, transparent 100%)",
...style,
}}
>
<defs>
<pattern
id={patternId}
patternUnits="userSpaceOnUse"
width="8"
height="8"
>
<circle cx="0" cy="0" r="1" fill={DOT_FILL} />
<circle cx="0" cy="8" r="1" fill={DOT_FILL} />
<circle cx="8" cy="8" r="1" fill={DOT_FILL} />
<circle cx="8" cy="0" r="1" fill={DOT_FILL} />
<circle cx="4" cy="4" r="1" fill={DOT_FILL} />
</pattern>
</defs>
<rect width="100%" height="100%" fill={`url(#${patternId})`} />
</svg>
);
}
111 changes: 111 additions & 0 deletions apps/code/src/renderer/components/FullScreenLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Lifebuoy } from "@phosphor-icons/react";
import { Button, Flex, Theme } from "@radix-ui/themes";
import phWordmark from "@renderer/assets/images/wordmark.svg";
import phWordmarkWhite from "@renderer/assets/images/wordmark-white.svg";
import { trpcClient } from "@renderer/trpc/client";
import { useThemeStore } from "@stores/themeStore";
import { EXTERNAL_LINKS } from "@utils/links";
import type { ReactNode } from "react";
import { DotPatternBackground } from "./DotPatternBackground";
import { DraggableTitleBar } from "./DraggableTitleBar";

interface FullScreenLayoutProps {
children: ReactNode;
footerLeft?: ReactNode;
footerRight?: ReactNode;
}

export function FullScreenLayout({
children,
footerLeft,
footerRight,
}: FullScreenLayoutProps) {
const isDarkMode = useThemeStore((state) => state.isDarkMode);

return (
<Theme
appearance={isDarkMode ? "dark" : "light"}
accentColor={isDarkMode ? "yellow" : "orange"}
radius="medium"
>
<Flex
direction="column"
height="100vh"
style={{ position: "relative", overflow: "hidden" }}
>
<DraggableTitleBar />

<div
style={{
position: "absolute",
inset: 0,
backgroundColor: "var(--color-background)",
}}
/>
<DotPatternBackground />

<Flex
direction="column"
flexGrow="1"
style={{
position: "relative",
zIndex: 1,
minHeight: 0,
width: "100%",
}}
>
<img
src={isDarkMode ? phWordmarkWhite : phWordmark}
alt="PostHog"
style={{
height: "40px",
objectFit: "contain",
alignSelf: "flex-start",
marginLeft: 32,
marginTop: "clamp(24px, 6vh, 80px)",
flexShrink: 0,
}}
/>

<Flex
direction="column"
flexGrow="1"
overflow="hidden"
style={{ minHeight: 0 }}
>
{children}
</Flex>

<Flex
justify="between"
style={{
position: "absolute",
bottom: 20,
left: 32,
right: 32,
zIndex: 2,
}}
>
{footerLeft ?? (
<Button
size="1"
variant="ghost"
color="gray"
onClick={() =>
trpcClient.os.openExternal.mutate({
url: EXTERNAL_LINKS.discord,
})
}
style={{ opacity: 0.5 }}
>
<Lifebuoy size={14} />
Get support
</Button>
)}
{footerRight ?? <div />}
</Flex>
</Flex>
</Flex>
</Theme>
);
}
Loading
Loading