feat: isolate Claude and Codex session sources
Persist platform_source across session creation, transcript ingestion, API query paths, and viewer state so Claude and Codex data can coexist without bleeding into each other. - add platform-source normalization helpers and persist platform_source in sdk_sessions via migration 24 with backfill and indexing - thread platformSource through CLI hooks, transcript processing, context generation, pagination, search routes, SSE payloads, and session management - expose source-aware project catalogs, viewer tabs, context preview selectors, and source badges for observations, prompts, and summaries - start the transcript watcher from the worker for transcript-based clients and preserve platform source during Codex ingestion - auto-start the worker from the MCP server for MCP-only clients and tighten stdio-driven cleanup during shutdown - keep createSDKSession backward compatible with existing custom-title callers while allowing explicit platform source forwarding
This commit is contained in:
@@ -7,8 +7,11 @@ import { useSpinningFavicon } from '../hooks/useSpinningFavicon';
|
||||
interface HeaderProps {
|
||||
isConnected: boolean;
|
||||
projects: string[];
|
||||
sources: string[];
|
||||
currentFilter: string;
|
||||
currentSource: string;
|
||||
onFilterChange: (filter: string) => void;
|
||||
onSourceChange: (source: string) => void;
|
||||
isProcessing: boolean;
|
||||
queueDepth: number;
|
||||
themePreference: ThemePreference;
|
||||
@@ -16,11 +19,26 @@ interface HeaderProps {
|
||||
onContextPreviewToggle: () => void;
|
||||
}
|
||||
|
||||
function formatSourceLabel(source: string): string {
|
||||
if (source === 'all') return 'All';
|
||||
if (source === 'claude') return 'Claude';
|
||||
if (source === 'codex') return 'Codex';
|
||||
return source.charAt(0).toUpperCase() + source.slice(1);
|
||||
}
|
||||
|
||||
function buildSourceTabs(sources: string[]): string[] {
|
||||
const merged = ['all', 'claude', 'codex', ...sources];
|
||||
return Array.from(new Set(merged.filter(Boolean)));
|
||||
}
|
||||
|
||||
export function Header({
|
||||
isConnected,
|
||||
projects,
|
||||
sources,
|
||||
currentFilter,
|
||||
currentSource,
|
||||
onFilterChange,
|
||||
onSourceChange,
|
||||
isProcessing,
|
||||
queueDepth,
|
||||
themePreference,
|
||||
@@ -28,20 +46,36 @@ export function Header({
|
||||
onContextPreviewToggle
|
||||
}: HeaderProps) {
|
||||
useSpinningFavicon(isProcessing);
|
||||
const availableSources = buildSourceTabs(sources);
|
||||
|
||||
return (
|
||||
<div className="header">
|
||||
<h1>
|
||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||
<img src="claude-mem-logomark.webp" alt="" className={`logomark ${isProcessing ? 'spinning' : ''}`} />
|
||||
{queueDepth > 0 && (
|
||||
<div className="queue-bubble">
|
||||
{queueDepth}
|
||||
</div>
|
||||
)}
|
||||
<div className="header-main">
|
||||
<h1>
|
||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||
<img src="claude-mem-logomark.webp" alt="" className={`logomark ${isProcessing ? 'spinning' : ''}`} />
|
||||
{queueDepth > 0 && (
|
||||
<div className="queue-bubble">
|
||||
{queueDepth}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span className="logo-text">claude-mem</span>
|
||||
</h1>
|
||||
<div className="source-tabs" role="tablist" aria-label="Context source tabs">
|
||||
{availableSources.map(source => (
|
||||
<button
|
||||
key={source}
|
||||
type="button"
|
||||
className={`source-tab ${currentSource === source ? 'active' : ''}`}
|
||||
onClick={() => onSourceChange(source)}
|
||||
aria-pressed={currentSource === source}
|
||||
>
|
||||
{formatSourceLabel(source)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<span className="logo-text">claude-mem</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="status">
|
||||
<a
|
||||
href="https://docs.claude-mem.ai"
|
||||
|
||||
Reference in New Issue
Block a user