feat: add new session button and integrate worktree session creation logic
This commit is contained in:
+12
-6
@@ -4,12 +4,18 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- Multi Run / Agent Manager: Select the Agent you want to run for the Worktree sessions (thanks to @wienans).
|
- Agent Manager / Multi Run: select agent per worktree session (thanks to @wienans).
|
||||||
- Agent Manager now submits the prompt if valid, similar to the Chat interfaces.
|
- Agent Manager / Multi Run: worktree actions to delete group or individual worktrees, or keep only selected one (thanks to @wienans).
|
||||||
- Added worktree management actions in Agent Manager: delete the group or individual worktrees or keep only selected one (thanks to @wienans).
|
- Agent Manager: added "Copy Worktree Path" action in the more menu (thanks to @wienans).
|
||||||
- Fixed IME (Input Method Editor) composition handling for CJK input methods, preventing accidental message send during character conversion (thanks to @madebyjun).
|
- Worktrees: added session creation flow with loading screen, auto-create worktree setting, and setup commands management.
|
||||||
- Added project management with multi-project support and per-project settings for agents/commands/skills.
|
- Session sidebar: refactoring with unified view for sessions in worktrees.
|
||||||
- Enhanced SSE event stream with heartbeat management, permission bootstrap on connect, and improved reconnection logic.
|
- Settings: added ability to create new session in worktree by default
|
||||||
|
- Git view: added branch rename for worktree.
|
||||||
|
- Chat: fixed IME composition for CJK input to prevent accidental send (thanks to @madebyjun).
|
||||||
|
- Projects: added multi-project support with per-project settings for agents/commands/skills.
|
||||||
|
- Event stream: improved SSE with heartbeat management, permission bootstrap on connect, and reconnection logic.
|
||||||
|
- Tunnel: added QR code and password URL for Cloudflare tunnel (thanks to @martindonadieu).
|
||||||
|
- Model selector: fixed dropdowns not responding to viewport size.
|
||||||
|
|
||||||
|
|
||||||
## [1.4.3] - 2026-01-04
|
## [1.4.3] - 2026-01-04
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
RiDeleteBinLine,
|
RiDeleteBinLine,
|
||||||
RiErrorWarningLine,
|
RiErrorWarningLine,
|
||||||
RiFileCopyLine,
|
RiFileCopyLine,
|
||||||
|
RiFolderAddLine,
|
||||||
RiGitBranchLine,
|
RiGitBranchLine,
|
||||||
RiLinkUnlinkM,
|
RiLinkUnlinkM,
|
||||||
RiMore2Line,
|
RiMore2Line,
|
||||||
@@ -50,10 +51,12 @@ import { useSessionStore } from '@/stores/useSessionStore';
|
|||||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
|
import { useConfigStore } from '@/stores/useConfigStore';
|
||||||
import type { WorktreeMetadata } from '@/types/worktree';
|
import type { WorktreeMetadata } from '@/types/worktree';
|
||||||
import { opencodeClient } from '@/lib/opencode/client';
|
import { opencodeClient } from '@/lib/opencode/client';
|
||||||
import { checkIsGitRepository } from '@/lib/gitApi';
|
import { checkIsGitRepository } from '@/lib/gitApi';
|
||||||
import { getSafeStorage } from '@/stores/utils/safeStorage';
|
import { getSafeStorage } from '@/stores/utils/safeStorage';
|
||||||
|
import { createWorktreeSession } from '@/lib/worktreeSessionCreator';
|
||||||
|
|
||||||
const PROJECT_COLLAPSE_STORAGE_KEY = 'oc.sessions.projectCollapse';
|
const PROJECT_COLLAPSE_STORAGE_KEY = 'oc.sessions.projectCollapse';
|
||||||
const SESSION_EXPANDED_STORAGE_KEY = 'oc.sessions.expandedParents';
|
const SESSION_EXPANDED_STORAGE_KEY = 'oc.sessions.expandedParents';
|
||||||
@@ -232,10 +235,6 @@ const SortableProjectItem: React.FC<SortableProjectItemProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="min-w-[180px]">
|
<DropdownMenuContent align="end" className="min-w-[180px]">
|
||||||
<DropdownMenuItem onClick={onNewSession}>
|
|
||||||
<RiAddLine className="mr-1.5 h-4 w-4" />
|
|
||||||
New Session
|
|
||||||
</DropdownMenuItem>
|
|
||||||
{isRepo && !hideDirectoryControls && (
|
{isRepo && !hideDirectoryControls && (
|
||||||
<DropdownMenuItem onClick={onOpenMultiRunLauncher}>
|
<DropdownMenuItem onClick={onOpenMultiRunLauncher}>
|
||||||
<ArrowsMerge className="mr-1.5 h-4 w-4" />
|
<ArrowsMerge className="mr-1.5 h-4 w-4" />
|
||||||
@@ -252,18 +251,17 @@ const SortableProjectItem: React.FC<SortableProjectItemProps> = ({
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
{/* Collapse arrow */}
|
{/* New session button */}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onToggle}
|
onClick={(e) => {
|
||||||
className="inline-flex h-6 w-6 items-center justify-center text-muted-foreground hover:text-foreground flex-shrink-0"
|
e.stopPropagation();
|
||||||
aria-label={isCollapsed ? 'Expand project' : 'Collapse project'}
|
onNewSession();
|
||||||
|
}}
|
||||||
|
className="inline-flex h-6 w-6 items-center justify-center text-muted-foreground hover:text-foreground flex-shrink-0 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
|
||||||
|
aria-label="New session"
|
||||||
>
|
>
|
||||||
{isCollapsed ? (
|
<RiAddLine className="h-4 w-4" />
|
||||||
<RiArrowRightSLine className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<RiArrowDownSLine className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -278,13 +276,11 @@ const SortableProjectItem: React.FC<SortableProjectItemProps> = ({
|
|||||||
interface ProjectDragOverlayProps {
|
interface ProjectDragOverlayProps {
|
||||||
projectLabel: string;
|
projectLabel: string;
|
||||||
isActiveProject: boolean;
|
isActiveProject: boolean;
|
||||||
isCollapsed: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectDragOverlay: React.FC<ProjectDragOverlayProps> = ({
|
const ProjectDragOverlay: React.FC<ProjectDragOverlayProps> = ({
|
||||||
projectLabel,
|
projectLabel,
|
||||||
isActiveProject,
|
isActiveProject,
|
||||||
isCollapsed,
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -299,11 +295,7 @@ const ProjectDragOverlay: React.FC<ProjectDragOverlayProps> = ({
|
|||||||
{projectLabel}
|
{projectLabel}
|
||||||
</span>
|
</span>
|
||||||
<span className="inline-flex h-6 w-6 items-center justify-center text-muted-foreground ml-auto">
|
<span className="inline-flex h-6 w-6 items-center justify-center text-muted-foreground ml-auto">
|
||||||
{isCollapsed ? (
|
<RiAddLine className="h-4 w-4" />
|
||||||
<RiArrowRightSLine className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<RiArrowDownSLine className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -361,6 +353,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen);
|
const setSessionSwitcherOpen = useUIStore((state) => state.setSessionSwitcherOpen);
|
||||||
const openMultiRunLauncher = useUIStore((state) => state.openMultiRunLauncher);
|
const openMultiRunLauncher = useUIStore((state) => state.openMultiRunLauncher);
|
||||||
|
|
||||||
|
const settingsAutoCreateWorktree = useConfigStore((state) => state.settingsAutoCreateWorktree);
|
||||||
|
|
||||||
const sessions = useSessionStore((state) => state.sessions);
|
const sessions = useSessionStore((state) => state.sessions);
|
||||||
const sessionsByDirectory = useSessionStore((state) => state.sessionsByDirectory);
|
const sessionsByDirectory = useSessionStore((state) => state.sessionsByDirectory);
|
||||||
const currentSessionId = useSessionStore((state) => state.currentSessionId);
|
const currentSessionId = useSessionStore((state) => state.currentSessionId);
|
||||||
@@ -1420,9 +1414,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
|| project.normalizedPath
|
|| project.normalizedPath
|
||||||
),
|
),
|
||||||
isActive: project.id === activeProjectId,
|
isActive: project.id === activeProjectId,
|
||||||
isCollapsed: collapsedProjects.has(project.id),
|
|
||||||
};
|
};
|
||||||
}, [activeDragId, projectSections, homeDirectory, activeProjectId, collapsedProjects]);
|
}, [activeDragId, projectSections, homeDirectory, activeProjectId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -1450,7 +1443,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
aria-label="Add project"
|
aria-label="Add project"
|
||||||
title="Add project"
|
title="Add project"
|
||||||
>
|
>
|
||||||
<RiAddLine className="h-5 w-5" />
|
<RiFolderAddLine className="h-4.5 w-4.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1538,7 +1531,11 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
if (mobileVariant) {
|
if (mobileVariant) {
|
||||||
setSessionSwitcherOpen(false);
|
setSessionSwitcherOpen(false);
|
||||||
}
|
}
|
||||||
openNewSessionDraft({ directoryOverride: project.normalizedPath });
|
if (settingsAutoCreateWorktree && isRepo) {
|
||||||
|
createWorktreeSession();
|
||||||
|
} else {
|
||||||
|
openNewSessionDraft({ directoryOverride: project.normalizedPath });
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onOpenMultiRunLauncher={() => {
|
onOpenMultiRunLauncher={() => {
|
||||||
if (projectKey !== activeProjectId) {
|
if (projectKey !== activeProjectId) {
|
||||||
@@ -1567,7 +1564,6 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
|||||||
<ProjectDragOverlay
|
<ProjectDragOverlay
|
||||||
projectLabel={activeDragProject.label}
|
projectLabel={activeDragProject.label}
|
||||||
isActiveProject={activeDragProject.isActive}
|
isActiveProject={activeDragProject.isActive}
|
||||||
isCollapsed={activeDragProject.isCollapsed}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
|
|||||||
Reference in New Issue
Block a user