mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-17 22:01:57 +08:00
fix: normalize project session payloads
The sidebar had to understand cursorSessions, codexSessions, and other provider buckets because /api/projects exposed provider-shaped arrays. That leaked backend adapter storage into project state and made frontend behavior drift each time a provider needed another bucket or exception. Return one sessions list with provider metadata instead. Project state, search, and running-session filtering now share one contract, while provider-specific storage remains behind the backend boundary.
This commit is contained in:
@@ -594,16 +594,7 @@ export function useSidebarController({
|
||||
|
||||
return sortedProjects.reduce<Project[]>((acc, project) => {
|
||||
const sessions = (project.sessions ?? []).filter((session) => activeSessionIds.has(String(session.id)));
|
||||
const cursorSessions = (project.cursorSessions ?? []).filter((session) => activeSessionIds.has(String(session.id)));
|
||||
const codexSessions = (project.codexSessions ?? []).filter((session) => activeSessionIds.has(String(session.id)));
|
||||
const geminiSessions = (project.geminiSessions ?? []).filter((session) => activeSessionIds.has(String(session.id)));
|
||||
const opencodeSessions = (project.opencodeSessions ?? []).filter((session) => activeSessionIds.has(String(session.id)));
|
||||
const runningCount =
|
||||
sessions.length
|
||||
+ cursorSessions.length
|
||||
+ codexSessions.length
|
||||
+ geminiSessions.length
|
||||
+ opencodeSessions.length;
|
||||
const runningCount = sessions.length;
|
||||
|
||||
if (runningCount === 0) {
|
||||
return acc;
|
||||
@@ -612,10 +603,6 @@ export function useSidebarController({
|
||||
acc.push({
|
||||
...project,
|
||||
sessions,
|
||||
cursorSessions,
|
||||
codexSessions,
|
||||
geminiSessions,
|
||||
opencodeSessions,
|
||||
sessionMeta: {
|
||||
...project.sessionMeta,
|
||||
total: runningCount,
|
||||
|
||||
@@ -61,10 +61,6 @@ export type SidebarProps = {
|
||||
};
|
||||
|
||||
export type SessionViewModel = {
|
||||
isCursorSession: boolean;
|
||||
isCodexSession: boolean;
|
||||
isGeminiSession: boolean;
|
||||
isOpenCodeSession: boolean;
|
||||
isActive: boolean;
|
||||
sessionName: string;
|
||||
sessionTime: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TFunction } from 'i18next';
|
||||
|
||||
import type { Project } from '../../../types/app';
|
||||
import type { LLMProvider, Project, ProjectSession } from '../../../types/app';
|
||||
import type { ProjectSortOrder, SettingsProject, SessionViewModel, SessionWithProvider } from '../types/types';
|
||||
|
||||
export const readProjectSortOrder = (): ProjectSortOrder => {
|
||||
@@ -61,6 +61,13 @@ const getUpdatedTimestamp = (session: SessionWithProvider): string => {
|
||||
return String(session.lastActivity || '');
|
||||
};
|
||||
|
||||
const getSessionProvider = (session: ProjectSession): LLMProvider => {
|
||||
const provider = session.__provider ?? session.provider;
|
||||
return typeof provider === 'string' && provider.trim()
|
||||
? provider as LLMProvider
|
||||
: 'claude';
|
||||
};
|
||||
|
||||
export const getSessionDate = (session: SessionWithProvider): Date => {
|
||||
return new Date(getUpdatedTimestamp(session) || getCreatedTimestamp(session) || 0);
|
||||
};
|
||||
@@ -82,10 +89,6 @@ export const createSessionViewModel = (
|
||||
const diffInMinutes = Math.floor((currentTime.getTime() - sessionDate.getTime()) / (1000 * 60));
|
||||
|
||||
return {
|
||||
isCursorSession: session.__provider === 'cursor',
|
||||
isCodexSession: session.__provider === 'codex',
|
||||
isGeminiSession: session.__provider === 'gemini',
|
||||
isOpenCodeSession: session.__provider === 'opencode',
|
||||
isActive: diffInMinutes < 10,
|
||||
sessionName: getSessionName(session, t),
|
||||
sessionTime: getSessionTime(session),
|
||||
@@ -94,32 +97,10 @@ export const createSessionViewModel = (
|
||||
};
|
||||
|
||||
export const getAllSessions = (project: Project): SessionWithProvider[] => {
|
||||
const claudeSessions = [...(project.sessions || [])].map((session) => ({
|
||||
return (project.sessions || []).map((session) => ({
|
||||
...session,
|
||||
__provider: 'claude' as const,
|
||||
}));
|
||||
|
||||
const cursorSessions = (project.cursorSessions || []).map((session) => ({
|
||||
...session,
|
||||
__provider: 'cursor' as const,
|
||||
}));
|
||||
|
||||
const codexSessions = (project.codexSessions || []).map((session) => ({
|
||||
...session,
|
||||
__provider: 'codex' as const,
|
||||
}));
|
||||
|
||||
const geminiSessions = (project.geminiSessions || []).map((session) => ({
|
||||
...session,
|
||||
__provider: 'gemini' as const,
|
||||
}));
|
||||
|
||||
const opencodeSessions = (project.opencodeSessions || []).map((session) => ({
|
||||
...session,
|
||||
__provider: 'opencode' as const,
|
||||
}));
|
||||
|
||||
return [...claudeSessions, ...cursorSessions, ...codexSessions, ...geminiSessions, ...opencodeSessions].sort(
|
||||
__provider: getSessionProvider(session),
|
||||
})).sort(
|
||||
(a, b) => getSessionDate(b).getTime() - getSessionDate(a).getTime(),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ export default function SidebarSessionItem({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isProcessing && !sessionView.isCursorSession && (
|
||||
{!isProcessing && (
|
||||
<button
|
||||
className="ml-1 flex h-5 w-5 items-center justify-center rounded-md bg-red-50 opacity-70 transition-transform active:scale-95 dark:bg-red-900/20"
|
||||
onClick={(event) => {
|
||||
@@ -309,7 +309,7 @@ export default function SidebarSessionItem({
|
||||
>
|
||||
<Edit2 className="h-3 w-3 text-gray-600 dark:text-gray-400" />
|
||||
</button>
|
||||
{!isProcessing && !sessionView.isCursorSession && (
|
||||
{!isProcessing && (
|
||||
<button
|
||||
className="flex h-6 w-6 items-center justify-center rounded bg-red-50 hover:bg-red-100 dark:bg-red-900/20 dark:hover:bg-red-900/40"
|
||||
onClick={(event) => {
|
||||
|
||||
Reference in New Issue
Block a user