diff --git a/apps/code/src/renderer/components/TourHighlight.tsx b/apps/code/src/renderer/components/TourHighlight.tsx
deleted file mode 100644
index 79311aae1..000000000
--- a/apps/code/src/renderer/components/TourHighlight.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { motion } from "framer-motion";
-
-interface TourHighlightProps {
- active: boolean;
- children: React.ReactNode;
- borderRadius?: string;
- /** Set true for elements that should stretch to fill their container (e.g. editor) */
- fullWidth?: boolean;
- /** When true and not active, dim to show it's not the focus of attention */
- dimWhenInactive?: boolean;
- /**
- * Keep opacity at 1 without triggering the glow — use when a child component
- * is highlighted so this wrapper doesn't dim it via CSS opacity inheritance.
- */
- opaque?: boolean;
-}
-
-export function TourHighlight({
- active,
- children,
- borderRadius = "var(--radius-2)",
- fullWidth,
- dimWhenInactive,
- opaque,
-}: TourHighlightProps) {
- const targetOpacity = active || opaque ? 1 : dimWhenInactive ? 0.35 : 1;
-
- return (
-
- {children}
-
- );
-}
diff --git a/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx b/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx
index 2cb8a7d10..1d64fc693 100644
--- a/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx
+++ b/apps/code/src/renderer/features/onboarding/components/OnboardingFlow.tsx
@@ -12,7 +12,6 @@ import { GitIntegrationStep } from "./GitIntegrationStep";
import { OrgBillingStep } from "./OrgBillingStep";
import { SignalsStep } from "./SignalsStep";
import { StepIndicator } from "./StepIndicator";
-import { TutorialStep } from "./TutorialStep";
import { WelcomeStep } from "./WelcomeStep";
export function OnboardingFlow() {
@@ -26,8 +25,6 @@ export function OnboardingFlow() {
completeOnboarding();
};
- const isTutorial = currentStep === "tutorial";
-
return (
@@ -38,157 +35,151 @@ export function OnboardingFlow() {
>
- {isTutorial ? (
-
- ) : (
- <>
- {/* Background */}
-
-
- {/* Right panel — zen hedgehog */}
-
-
-
-
- {/* Content */}
-
-
-
- {currentStep === "welcome" && (
-
-
-
- )}
+ {/* Background */}
+
- {currentStep === "billing" && (
-
-
-
- )}
+ {/* Right panel — zen hedgehog */}
+
+
+
- {currentStep === "org-billing" && (
-
-
-
- )}
+ {/* Content */}
+
+
+
+ {currentStep === "welcome" && (
+
+
+
+ )}
- {currentStep === "git-integration" && (
-
-
-
- )}
+ {currentStep === "billing" && (
+
+
+
+ )}
- {currentStep === "signals" && (
-
-
-
- )}
-
-
+ {currentStep === "org-billing" && (
+
+
+
+ )}
-
-
-
-
+
+ )}
+
+ {currentStep === "signals" && (
+
- Skip setup
-
-
-
- >
- )}
+
+
+ )}
+
+
+
+
+
+
+
+
+
diff --git a/apps/code/src/renderer/features/onboarding/components/TutorialHedgehog.tsx b/apps/code/src/renderer/features/onboarding/components/TutorialHedgehog.tsx
deleted file mode 100644
index 3403f11d4..000000000
--- a/apps/code/src/renderer/features/onboarding/components/TutorialHedgehog.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { ArrowRight } from "@phosphor-icons/react";
-import { Button, Flex, Text } from "@radix-ui/themes";
-import zenHedgehog from "@renderer/assets/images/zen.png";
-import { AnimatePresence, motion } from "framer-motion";
-
-interface TutorialHedgehogProps {
- message: string;
- onNext?: () => void;
- stepNumber: number;
- totalSteps: number;
-}
-
-export function TutorialHedgehog({
- message,
- onNext,
- stepNumber,
- totalSteps,
-}: TutorialHedgehogProps) {
- return (
-
- {/* Speech bubble — to the left of the hedgehog */}
-
-
-
-
-
- {message}
-
-
-
- {stepNumber}/{totalSteps}
-
- {onNext && (
-
- )}
-
-
-
- {/* Tail pointing right toward hedgehog */}
-
-
-
-
-
-
- {/* Hedgehog image — layoutId matches ZenHedgehog for seamless transition */}
-
-
-
-
- );
-}
diff --git a/apps/code/src/renderer/features/onboarding/components/TutorialStep.tsx b/apps/code/src/renderer/features/onboarding/components/TutorialStep.tsx
deleted file mode 100644
index 03c473876..000000000
--- a/apps/code/src/renderer/features/onboarding/components/TutorialStep.tsx
+++ /dev/null
@@ -1,469 +0,0 @@
-import { TourHighlight } from "@components/TourHighlight";
-import { FolderPicker } from "@features/folder-picker/components/FolderPicker";
-import { GitHubRepoPicker } from "@features/folder-picker/components/GitHubRepoPicker";
-import { BranchSelector } from "@features/git-interaction/components/BranchSelector";
-import type { MessageEditorHandle } from "@features/message-editor/components/MessageEditor";
-import { ModeIndicatorInput } from "@features/message-editor/components/ModeIndicatorInput";
-import { useDraftStore } from "@features/message-editor/stores/draftStore";
-import { useOnboardingStore } from "@features/onboarding/stores/onboardingStore";
-import {
- cycleModeOption,
- getCurrentModeFromConfigOptions,
-} from "@features/sessions/stores/sessionStore";
-import { useSettingsStore } from "@features/settings/stores/settingsStore";
-import { TaskInputEditor } from "@features/task-detail/components/TaskInputEditor";
-import { WorkspaceModeSelect } from "@features/task-detail/components/WorkspaceModeSelect";
-import { usePreviewConfig } from "@features/task-detail/hooks/usePreviewConfig";
-import { useTaskCreation } from "@features/task-detail/hooks/useTaskCreation";
-import {
- useGithubBranches,
- useRepositoryIntegration,
-} from "@hooks/useIntegrations";
-import { ArrowLeft } from "@phosphor-icons/react";
-import { Button, Flex } from "@radix-ui/themes";
-import { motion } from "framer-motion";
-import {
- useCallback,
- useEffect,
- useLayoutEffect,
- useRef,
- useState,
-} from "react";
-import { useHotkeys } from "react-hotkeys-hook";
-import { useTutorialTour } from "../hooks/useTutorialTour";
-import { TutorialHedgehog } from "./TutorialHedgehog";
-
-const DOT_FILL = "var(--gray-6)";
-
-const HEDGEHOG_MESSAGES: Record = {
- "select-repo":
- "Pick a repo to get started — I'll help you set up PostHog instrumentation for it!",
- "select-worktree":
- "Great choice! Now pick Worktree from the workspace mode dropdown — it creates a copy of your project to work in parallel.",
- "select-model":
- "Now pick your AI model — try selecting Claude Opus 4.6 for the most capable option!",
- "explain-mode":
- "Press Shift+Tab to cycle through execution modes like Plan, Code, and more.",
- "auto-fill-prompt":
- "I've written your first task prompt — it'll set up PostHog based on the signals you enabled. Press Next when you're ready!",
- "submit-task":
- "You're ready! Hit the arrow button to launch your first task.",
- navigating: "Launching your task...",
-};
-
-const TOTAL_TOUR_STEPS = Object.keys(HEDGEHOG_MESSAGES).length - 1; // exclude "navigating"
-
-interface TutorialStepProps {
- onComplete: () => void;
- onBack: () => void;
-}
-
-export function TutorialStep({ onComplete, onBack }: TutorialStepProps) {
- const { allowBypassPermissions } = useSettingsStore();
- const completeOnboarding = useOnboardingStore(
- (state) => state.completeOnboarding,
- );
-
- // Tour state machine
- const {
- subStep,
- advance,
- isEnabled,
- isHighlighted,
- generatedPrompt,
- hasNextButton,
- } = useTutorialTour();
-
- const editorRef = useRef(null);
-
- // Clear any leftover draft and delay content until the hedgehog has animated in
- const [contentVisible, setContentVisible] = useState(false);
- useLayoutEffect(() => {
- useDraftStore.getState().actions.setDraft("tutorial-input", null);
- const timer = setTimeout(() => setContentVisible(true), 1000);
- return () => clearTimeout(timer);
- }, []);
-
- // GitHub repos
- const { repositories, getIntegrationIdForRepo, isLoadingRepos } =
- useRepositoryIntegration();
- const [selectedRepository, setSelectedRepository] = useState(
- null,
- );
- const [selectedDirectory, setSelectedDirectory] = useState("");
- const [selectedBranch, setSelectedBranch] = useState(null);
- const [editorIsEmpty, setEditorIsEmpty] = useState(true);
- const [workspaceMode, setWorkspaceMode] = useState<
- "local" | "worktree" | "cloud"
- >("local");
- const [selectedModel, setSelectedModel] = useState(null);
-
- const selectedIntegrationId = selectedRepository
- ? getIntegrationIdForRepo(selectedRepository)
- : undefined;
-
- const {
- data: cloudBranchData,
- isPending: cloudBranchesLoading,
- isFetchingMore: cloudBranchesFetchingMore,
- pauseLoadingMore: pauseCloudBranchesLoading,
- resumeLoadingMore: resumeCloudBranchesLoading,
- } = useGithubBranches(selectedIntegrationId, selectedRepository);
- const cloudBranches = cloudBranchData?.branches;
- const cloudDefaultBranch = cloudBranchData?.defaultBranch ?? null;
-
- // Preview config options — always claude
- const {
- modeOption,
- modelOption,
- thoughtOption,
- isLoading: isPreviewLoading,
- setConfigOption,
- } = usePreviewConfig("claude");
-
- const currentExecutionMode =
- getCurrentModeFromConfigOptions(modeOption ? [modeOption] : undefined) ??
- "plan";
- const currentReasoningLevel =
- thoughtOption?.type === "select" ? thoughtOption.currentValue : undefined;
-
- // Task creation — use whatever model the user picked
- const { isCreatingTask, canSubmit, handleSubmit } = useTaskCreation({
- editorRef,
- selectedDirectory,
- selectedRepository,
- githubIntegrationId: selectedIntegrationId,
- workspaceMode,
- branch: selectedBranch,
- editorIsEmpty,
- adapter: "claude",
- executionMode: currentExecutionMode,
- model: selectedModel ?? "claude-sonnet-4-6",
- reasoningLevel: currentReasoningLevel,
- });
-
- // Editor wrapper is interactive when user needs to interact with model selector, editor text, or submit button
- const editorInteractive =
- subStep === "select-model" ||
- subStep === "submit-task" ||
- subStep === "navigating" ||
- isCreatingTask;
-
- const isTourActive = subStep !== "navigating";
-
- // Advance tour when user selects a repo or folder
- useEffect(() => {
- if (
- subStep === "select-repo" &&
- (selectedRepository || selectedDirectory)
- ) {
- advance();
- }
- }, [subStep, selectedRepository, selectedDirectory, advance]);
-
- // Auto-fill prompt with typing animation — waits for user to click Next first
- const [autoFillTriggered, setAutoFillTriggered] = useState(false);
- useEffect(() => {
- if (subStep !== "auto-fill-prompt" || !editorRef.current) return;
- if (!autoFillTriggered) return;
-
- let index = 0;
- const interval = setInterval(() => {
- index += 4;
- editorRef.current?.setContent(generatedPrompt.slice(0, index));
- if (index >= generatedPrompt.length) {
- clearInterval(interval);
- advance();
- }
- }, 15);
-
- return () => clearInterval(interval);
- }, [subStep, generatedPrompt, advance, autoFillTriggered]);
-
- // Track mode selection; advance only when worktree is picked during select-worktree step
- const handleWorkspaceModeChange = useCallback(
- (mode: "local" | "worktree" | "cloud") => {
- setWorkspaceMode(mode);
- if (mode === "worktree" && subStep === "select-worktree") {
- advance();
- }
- },
- [subStep, advance],
- );
-
- // Track model selection; advance when any model is picked during select-model step
- const handleModelChange = useCallback(
- (model: string) => {
- setSelectedModel(model);
- if (subStep === "select-model") {
- advance();
- }
- },
- [subStep, advance],
- );
-
- // Shift+tab mode cycling (only active during explain-mode step)
- const handleCycleMode = useCallback(() => {
- const nextValue = cycleModeOption(modeOption, allowBypassPermissions);
- if (nextValue && modeOption) {
- setConfigOption(modeOption.id, nextValue);
- }
- }, [modeOption, allowBypassPermissions, setConfigOption]);
-
- useHotkeys(
- "shift+tab",
- (e) => {
- e.preventDefault();
- handleCycleMode();
- },
- {
- enableOnFormTags: true,
- enableOnContentEditable: true,
- enabled: !!modeOption && subStep === "explain-mode",
- },
- [handleCycleMode, modeOption, subStep],
- );
-
- // Submit and complete onboarding
- const handleTutorialSubmit = useCallback(async () => {
- await handleSubmit();
- completeOnboarding();
- }, [handleSubmit, completeOnboarding]);
-
- // Handle Next button — for auto-fill step, trigger the typing animation
- const handleNextClick = useCallback(() => {
- if (subStep === "auto-fill-prompt" && !autoFillTriggered) {
- setAutoFillTriggered(true);
- } else {
- advance();
- }
- }, [subStep, autoFillTriggered, advance]);
-
- const stepNumber = Math.max(
- 1,
- Object.keys(HEDGEHOG_MESSAGES).indexOf(subStep) + 1,
- );
- const hedgehogMessage = HEDGEHOG_MESSAGES[subStep] ?? "";
-
- return (
-
- {/* Main content area — mirrors TaskInput layout */}
-
- {/* Dot pattern background */}
-
-
- {contentVisible && (
-
- {/* Row 1: Repo picker + Workspace mode + Branch */}
-
-
- {workspaceMode === "cloud" ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
- {/* Row 2: Editor — opaque when a child (model/submit) is the active highlight */}
-
-
- {
- setConfigOption(configId, value);
- if (configId === modelOption?.id) {
- handleModelChange(value);
- }
- }}
- onAdapterChange={() => {}}
- isLoading={isPreviewLoading}
- autoFocus={false}
- tourHighlight={
- isHighlighted("model-selector")
- ? "model-selector"
- : isHighlighted("submit-button")
- ? "submit-button"
- : null
- }
- />
-
-
-
- {/* Row 3: Mode indicator */}
-
-
-
-
-
-
- )}
-
-
- {/* Hedgehog guide */}
-
-
- {/* Bottom controls */}
-
-
-
-
-
- );
-}
diff --git a/apps/code/src/renderer/features/onboarding/hooks/useTutorialTour.ts b/apps/code/src/renderer/features/onboarding/hooks/useTutorialTour.ts
deleted file mode 100644
index 8bd42efbf..000000000
--- a/apps/code/src/renderer/features/onboarding/hooks/useTutorialTour.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import type { SignalSourceValues } from "@features/inbox/components/SignalSourceToggles";
-import { useSignalSourceConfigs } from "@features/inbox/hooks/useSignalSourceConfigs";
-import { useCallback, useMemo, useState } from "react";
-import { generateInstrumentationPrompt } from "../utils/generateInstrumentationPrompt";
-
-export type TutorialSubStep =
- | "select-repo"
- | "select-worktree"
- | "select-model"
- | "explain-mode"
- | "auto-fill-prompt"
- | "submit-task"
- | "navigating";
-
-type TutorialComponent =
- | "repo-picker"
- | "workspace-mode"
- | "branch-selector"
- | "editor"
- | "model-selector"
- | "mode-indicator"
- | "submit-button";
-
-const SUB_STEP_ORDER: TutorialSubStep[] = [
- "select-repo",
- "select-worktree",
- "select-model",
- "explain-mode",
- "auto-fill-prompt",
- "submit-task",
- "navigating",
-];
-
-/**
- * The step at which each component becomes unlocked.
- * Once unlocked, it stays interactive for all subsequent steps.
- */
-const UNLOCK_AT: Record = {
- "repo-picker": "select-repo",
- "workspace-mode": "select-worktree",
- "branch-selector": "select-worktree",
- editor: "submit-task",
- "model-selector": "select-model",
- "mode-indicator": "explain-mode",
- "submit-button": "submit-task",
-};
-
-/** Which component is highlighted (has spotlight) at each sub-step */
-const HIGHLIGHTED_MAP: Record = {
- "select-repo": "repo-picker",
- "select-worktree": "workspace-mode",
- "select-model": "model-selector",
- "explain-mode": "mode-indicator",
- "auto-fill-prompt": "editor",
- "submit-task": "submit-button",
- navigating: null,
-};
-
-export function useTutorialTour() {
- const [subStep, setSubStep] = useState("select-repo");
- const { data: configs } = useSignalSourceConfigs();
-
- const signals: SignalSourceValues = useMemo(
- () => ({
- session_replay:
- configs?.some(
- (c) => c.source_product === "session_replay" && c.enabled,
- ) ?? true,
- github:
- configs?.some((c) => c.source_product === "github" && c.enabled) ??
- false,
- linear:
- configs?.some((c) => c.source_product === "linear" && c.enabled) ??
- false,
- zendesk:
- configs?.some((c) => c.source_product === "zendesk" && c.enabled) ??
- false,
- error_tracking:
- configs?.some(
- (c) => c.source_product === "error_tracking" && c.enabled,
- ) ?? false,
- }),
- [configs],
- );
-
- const generatedPrompt = useMemo(
- () => generateInstrumentationPrompt(signals),
- [signals],
- );
-
- const currentIndex = SUB_STEP_ORDER.indexOf(subStep);
-
- const advance = useCallback(() => {
- setSubStep((current) => {
- const idx = SUB_STEP_ORDER.indexOf(current);
- if (idx < SUB_STEP_ORDER.length - 1) {
- return SUB_STEP_ORDER[idx + 1];
- }
- return current;
- });
- }, []);
-
- const isEnabled = useCallback(
- (component: TutorialComponent): boolean => {
- const unlockStep = UNLOCK_AT[component];
- const unlockIndex = SUB_STEP_ORDER.indexOf(unlockStep);
- return currentIndex >= unlockIndex;
- },
- [currentIndex],
- );
-
- const isHighlighted = useCallback(
- (component: TutorialComponent): boolean => {
- return HIGHLIGHTED_MAP[subStep] === component;
- },
- [subStep],
- );
-
- /** Whether the tooltip for this step has a "Next" button (vs being advanced by user action) */
- const hasNextButton =
- subStep === "explain-mode" || subStep === "auto-fill-prompt";
-
- return {
- subStep,
- advance,
- isEnabled,
- isHighlighted,
- generatedPrompt,
- hasNextButton,
- };
-}
diff --git a/apps/code/src/renderer/features/onboarding/types.ts b/apps/code/src/renderer/features/onboarding/types.ts
index 91160eefe..9b702fb63 100644
--- a/apps/code/src/renderer/features/onboarding/types.ts
+++ b/apps/code/src/renderer/features/onboarding/types.ts
@@ -3,8 +3,7 @@ export type OnboardingStep =
| "billing"
| "org-billing"
| "git-integration"
- | "signals"
- | "tutorial";
+ | "signals";
export const ONBOARDING_STEPS: OnboardingStep[] = [
"welcome",
@@ -12,5 +11,4 @@ export const ONBOARDING_STEPS: OnboardingStep[] = [
"org-billing",
"git-integration",
"signals",
- "tutorial",
];
diff --git a/apps/code/src/renderer/features/onboarding/utils/generateInstrumentationPrompt.ts b/apps/code/src/renderer/features/onboarding/utils/generateInstrumentationPrompt.ts
deleted file mode 100644
index 406b5cab6..000000000
--- a/apps/code/src/renderer/features/onboarding/utils/generateInstrumentationPrompt.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { SignalSourceValues } from "@features/inbox/components/SignalSourceToggles";
-
-export function generateInstrumentationPrompt(
- signals: SignalSourceValues,
-): string {
- const parts: string[] = [
- "Set up PostHog instrumentation for this repository.",
- ];
-
- if (signals.session_replay) {
- parts.push(
- "Install the PostHog SDK if not already present and configure session recording. Initialize with `enable_recording_console_log: true` and ensure session replay is enabled.",
- );
- }
-
- if (!signals.session_replay) {
- parts.push(
- "Check if the PostHog SDK is installed. If not, install it and initialize it with the project's API key. Set up basic event tracking.",
- );
- }
-
- return parts.join("\n\n");
-}
diff --git a/apps/code/src/renderer/features/task-detail/components/TaskInputEditor.tsx b/apps/code/src/renderer/features/task-detail/components/TaskInputEditor.tsx
index 87bbac10e..f8c16718a 100644
--- a/apps/code/src/renderer/features/task-detail/components/TaskInputEditor.tsx
+++ b/apps/code/src/renderer/features/task-detail/components/TaskInputEditor.tsx
@@ -1,6 +1,5 @@
import "@features/message-editor/components/message-editor.css";
import type { SessionConfigOption } from "@agentclientprotocol/sdk";
-import { TourHighlight } from "@components/TourHighlight";
import { AttachmentsBar } from "@features/message-editor/components/AttachmentsBar";
import { EditorToolbar } from "@features/message-editor/components/EditorToolbar";
import type { MessageEditorHandle } from "@features/message-editor/components/MessageEditor";
@@ -34,7 +33,6 @@ interface TaskInputEditorProps {
onAdapterChange?: (adapter: AgentAdapter) => void;
isLoading?: boolean;
autoFocus?: boolean;
- tourHighlight?: "model-selector" | "submit-button" | null;
}
export const TaskInputEditor = forwardRef<
@@ -58,7 +56,6 @@ export const TaskInputEditor = forwardRef<
onAdapterChange,
isLoading,
autoFocus = true,
- tourHighlight,
},
ref,
) => {
@@ -257,16 +254,14 @@ export const TaskInputEditor = forwardRef<
iconSize={16}
hideSelectors
/>
-
- {})}
- disabled={isCreatingTask}
- isConnecting={isLoading}
- onModelChange={handleModelChange}
- />
-
+ {})}
+ disabled={isCreatingTask}
+ isConnecting={isLoading}
+ onModelChange={handleModelChange}
+ />
{!isLoading && (
-
- {
- e.stopPropagation();
- onSubmit();
- }}
- disabled={!canSubmit || isSubmitDisabled}
- loading={isCreatingTask}
- style={{
- backgroundColor:
- !canSubmit || isSubmitDisabled
- ? "var(--accent-a4)"
- : undefined,
- color:
- !canSubmit || isSubmitDisabled
- ? "var(--accent-8)"
- : undefined,
- }}
- >
-
-
-
+ {
+ e.stopPropagation();
+ onSubmit();
+ }}
+ disabled={!canSubmit || isSubmitDisabled}
+ loading={isCreatingTask}
+ style={{
+ backgroundColor:
+ !canSubmit || isSubmitDisabled
+ ? "var(--accent-a4)"
+ : undefined,
+ color:
+ !canSubmit || isSubmitDisabled
+ ? "var(--accent-8)"
+ : undefined,
+ }}
+ >
+
+