Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ chats.](docs/images/command-palette.png)
- **Keyboard-first navigation.** A command palette (Ctrl/Cmd+K) reaches new
chat, settings, theme, model switching, and recent chats; `?` opens a
shortcuts reference. The sidebar also supports searching chats by title.
- **Transcript export.** Any conversation can be exported as Markdown or JSON
from the workspace header.
- **Durable context.** Threads live in SQLite; permanent memories use atomic
local JSON storage. Existing JSON chat history and Windows settings are read
additively during migration without rewriting the legacy source.
Expand Down
Binary file modified docs/images/workspace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 0 additions & 9 deletions frontend/src/features/chat/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,6 @@ export function ChatPage({

const currentChat = threadId !== null && chat?.id === threadId ? chat : null;

// Mirror the fully-loaded, currently-viewed chat into the global store so
// chrome outside ChatPage (the AppShell header's export control) can read
// it without prop-drilling. Cleared on unmount so a Settings round-trip
// doesn't leave a stale chat exported from the header.
useEffect(() => {
useChatStore.getState().setActiveChat(currentChat);
return () => useChatStore.getState().setActiveChat(null);
}, [currentChat]);

useEffect(() => {
if (isNearTranscriptEnd.current) {
messageListRef.current?.scrollToBottom();
Expand Down
28 changes: 0 additions & 28 deletions frontend/src/features/chat/ExportTranscriptMenu.tsx

This file was deleted.

73 changes: 0 additions & 73 deletions frontend/src/features/chat/exportTranscript.test.ts

This file was deleted.

34 changes: 0 additions & 34 deletions frontend/src/features/chat/exportTranscript.ts

This file was deleted.

17 changes: 0 additions & 17 deletions frontend/src/features/shell/AppShell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ChatGroup, ChatSummary, ModelResponse } from "../../../../contracts/cortex-api";
import { useChatStore } from "../../stores/useChatStore";
import { AppShell } from "./AppShell";

const CONNECTED = { success: true, status: "connected", message: "Connected." } satisfies NonNullable<ModelResponse["connection"]>;
Expand Down Expand Up @@ -117,20 +116,4 @@ describe("AppShell", () => {

expect(screen.getByText("No chats match your search.")).toBeInTheDocument();
});

it("shows an export control in the header once a full chat is the active chat in the store", () => {
useChatStore.getState().setActiveChat({
id: "chat-1",
title: "Quarterly planning",
timestamp: "2026-01-01T00:00:00Z",
revision: 1,
messages: [{ id: "m-1", role: "user", content: "hi" }],
});
const chat: ChatSummary = { id: "chat-1", title: "Quarterly planning", timestamp: "2026-01-01T00:00:00Z" };

renderShell({ chats: [chat], activeChatId: chat.id, onDeleteChat: vi.fn<(id: string) => Promise<void>>().mockResolvedValue(), onOpenSettings: vi.fn() });

expect(screen.getByRole("button", { name: "Export as Markdown" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Export as JSON" })).toBeInTheDocument();
});
});
4 changes: 0 additions & 4 deletions frontend/src/features/shell/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import { Menu, Plus, Search, Settings, Trash2 } from "lucide-react";
import type { ChatGroup, ChatSummary, CodeExecutionSourceResponse, ExecutionApprovalDecisionRequest, ExecutionTaskSummary, ModelResponse } from "../../../../contracts/cortex-api";
import { displayChatTitle } from "../../lib/chatTitle";
import { chatPath, parseAppRoute, useNavigate, usePathname } from "../../lib/navigation";
import { useChatStore } from "../../stores/useChatStore";
import { AlertDialog, Dialog, DialogContent } from "../../shared/ui/Dialog";
import { ExecutionTaskTray } from "./ExecutionTaskTray";
import { ChatLibrary } from "./ChatLibrary";
import { ExportTranscriptMenu } from "../chat/ExportTranscriptMenu";
import { NavigationLink } from "./NavigationLink";

type Props = {
Expand Down Expand Up @@ -53,7 +51,6 @@ export function AppShell({
}: Props) {
const navigate = useNavigate();
const pathname = usePathname();
const activeChat = useChatStore((state) => state.activeChat);
const [sidebarVisible, setSidebarVisible] = useState(() => !isCompactWindow());
const [renameTarget, setRenameTarget] = useState<ChatSummary | null>(null);
const [deleteTarget, setDeleteTarget] = useState<ChatSummary | null>(null);
Expand Down Expand Up @@ -110,7 +107,6 @@ export function AppShell({
</div>
</div>
<div className="window-actions">
{!isSettings && activeChat && <ExportTranscriptMenu chat={activeChat} />}
<NavigationLink
to="/settings"
className={`window-control settings-control ${isSettings ? "window-control-active" : ""}`}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/stores/useChatStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const idleGeneration = {

describe("useChatStore", () => {
beforeEach(() => {
useChatStore.setState({ chats: [], activeChat: null, generation: idleGeneration, generationOptionsByThread: {} });
useChatStore.setState({ chats: [], generation: idleGeneration, generationOptionsByThread: {} });
});

it("setChats accepts a plain array, mirroring useState", () => {
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/stores/useChatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ interface ChatStoreState {
chats: ChatSummary[];
/** User-created folders/projects, in their persisted display order. */
groups: ChatGroup[];
activeChat: ChatResponse | null;
generation: GenerationState;
generationOptionsByThread: Record<string, GenerationOptionsOverride>;

setChats: (next: ChatSummary[] | ((current: ChatSummary[]) => ChatSummary[])) => void;
upsertChatSummary: (chat: ChatResponse) => void;
setActiveChat: (chat: ChatResponse | null) => void;

setGroups: (next: ChatGroup[] | ((current: ChatGroup[]) => ChatGroup[])) => void;
/** Replace one group in place, keeping its position in the list. */
Expand Down Expand Up @@ -78,7 +76,6 @@ const idleGeneration: GenerationState = {
export const useChatStore = create<ChatStoreState>((set) => ({
chats: [],
groups: [],
activeChat: null,
generation: idleGeneration,
generationOptionsByThread: {},

Expand All @@ -99,7 +96,6 @@ export const useChatStore = create<ChatStoreState>((set) => ({
...state.chats.filter((item) => item.id !== chat.id),
],
})),
setActiveChat: (chat) => set({ activeChat: chat }),

setGroups: (next) =>
set((state) => ({ groups: typeof next === "function" ? (next as (current: ChatGroup[]) => ChatGroup[])(state.groups) : next })),
Expand Down
Loading