Skip to content

Conversation

@MyPrototypeWhat
Copy link
Collaborator

@MyPrototypeWhat MyPrototypeWhat commented Nov 26, 2025

Summary

This PR implements the core routing architecture for Cherry Studio, with comprehensive research on Tab System + Router integration patterns.

Architecture Research

We identified a fundamental contradiction between URL Router design (single active view) and Tab system requirements (multiple views coexisting with state preservation). Three architecture approaches were documented:

Approach Document Status
Single HashRouter + Outlet + KeepAlive architecture-single-outlet.md Research Complete
Multi MemoryRouter Instances architecture-multi-memory-router.md Recommended & Implemented
MPA + Multi WebContents architecture-mpa-webcontents.md Research Complete

Key Requirements

Requirement Priority Description
No flicker on switch P0 UI responds instantly on Tab switch
State preservation P0 Scroll position, input content, etc.
Tab detach to window P1 Similar to Chrome/VS Code
Tab attach (drag back) P1 Drag window back to main tab bar
Memory control P1 Support LRU eviction for inactive Tabs

Current Implementation

  1. TanStack Router Integration

    • File-based routing structure (routes/__root.tsx, routes/index.tsx, routes/settings.tsx)
    • Auto-generated route tree (routeTree.gen.ts)
    • Fixed path configuration using resolve() for absolute paths
  2. Multi MemoryRouter Architecture

    • TabRouter component with React 19 <Activity> for KeepAlive
    • Each Tab has independent MemoryRouter instance
    • State fully preserved on Tab switch
  3. Tab State Management

    • useTabs hook with openTab API for Tab-level navigation
    • useNavigate for in-Tab navigation
    • Persisted via localStorage cache
  4. Developer Documentation

    • routes/README.md - English developer guide
    • routes/README.zh-CN.md - Chinese developer guide

Files Changed

src/renderer/src/
├── routeTree.gen.ts                 # Auto-generated route tree
├── routes/
│   ├── __root.tsx                   # Root route (renders Outlet)
│   ├── index.tsx                    # Home page placeholder
│   ├── settings.tsx                 # Settings page placeholder
│   ├── README.md                    # Developer guide (EN)
│   └── README.zh-CN.md              # Developer guide (CN)
├── hooks/
│   └── useTabs.ts                   # Tab state management with openTab API
└── components/layout/
    ├── AppShell.tsx                 # Main layout (Sidebar + TabBar + Content)
    ├── TabRouter.tsx                # Tab router container (MemoryRouter + Activity)
    ├── architecture-single-outlet.md
    ├── architecture-multi-memory-router.md
    ├── architecture-mpa-webcontents.md
    └── router-planning.md

TODO List

Feature 1: Multi-Tab Core ✅

  • TabRouter component with React 19.2 <Activity> for visibility control
  • createMemoryHistory + createRouter per Tab instance
  • useTabs hook enhancement with openTab API (add/remove/switch/update)
  • Tab state types definition (Tab, TabType, OpenTabOptions)
  • Developer documentation (README.md, README.zh-CN.md)

Feature 2: Route Pages (In Progress)

  • Chat routes (/chat/$assistantId/$topicId)
  • Settings routes placeholder (/settings)
  • Knowledge base pages
  • Notes pages
  • Other feature pages

Feature 3: Tab Detach (Drag Out to Window)

  • URL Query state serialization (path + scroll + form state)
  • DetachedTabWindow component (read URL Query, init MemoryRouter)
  • Main process window creation (BrowserWindow with query params)
  • Drag event handling and window positioning

Feature 4: Tab Attach (Drag Back to Main)

  • Drop zone detection on main window tab bar
  • IPC state transfer protocol (tab:attach-request / tab:attach-response)
  • Window close coordination after successful attach
  • Tab insertion position handling

Feature 5: Memory Management

  • LRU eviction strategy for inactive tabs
  • State persistence before eviction (localStorage/IndexedDB)
  • Lazy recovery on tab reactivation
  • Memory usage monitoring

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

- Added AppStateService for managing application state in the database, including methods for getting, setting, and deleting state.
- Introduced new API endpoint for app state storage with GET and PUT methods.
- Integrated TanStack Router for routing, enabling a tabbed interface with dynamic routing and state synchronization.
- Created AppShell component to manage layout and routing, including a sidebar and tab management.
- Developed useTabs hook for handling tab state, including adding, closing, and switching tabs.
- Updated package.json and yarn.lock to include necessary dependencies for routing and state management.

This commit enhances the application's architecture by providing a robust state management system and a flexible routing mechanism, improving user experience and maintainability.
@MyPrototypeWhat MyPrototypeWhat marked this pull request as draft November 26, 2025 02:45
@0xfullex
Copy link
Collaborator

0xfullex commented Nov 26, 2025

Note

This issue/comment/review was translated by Claude.

URL → DB: Listens to location.pathname and updates active tab's URL in SQLite

This is a typical Cache behavior, not a DB behavior. Interacting with the DB brings unnecessary complexity.


Original Content

URL → DB: Listens to location.pathname and updates active tab's URL in SQLite

这是个典型的 Cache 行为,而不是 db行为,和db交互,带来了没必要的复杂化

@0xfullex 0xfullex added the v2 label Nov 26, 2025
@0xfullex 0xfullex added this to the v2.0.0 milestone Nov 26, 2025
@MyPrototypeWhat
Copy link
Collaborator Author

MyPrototypeWhat commented Nov 26, 2025

URL → DB: Listens to location.pathname and updates active tab's URL in SQLite

This is a typical Cache behavior, not a DB behavior. Interacting with the DB brings unnecessary complexity.

Note

This issue/comment/review was translated by Claude.

The top tabs need to be stored so that when users reopen the application, these tabs are retained, similar to browser tabs. I was struggling between using db or cache... I'll change it.


Original Content

顶部的Tab是需要做存储的,这样用户重新打开应用会保留这些Tab,行为类似浏览器tab
在用db还是cache纠结了...我改一下

@0xfullex
Copy link
Collaborator

0xfullex commented Nov 26, 2025

URL → DB: Listens to location.pathname and updates active tab's URL in SQLite\r\n> \r\n> > This is a typical Cache behavior, not a DB behavior. Interacting with the DB brings unnecessary complexity.\r\n> \r\n> > [!NOTE]\r\n> > This issue/comment/review was translated by Claude.\r\n> \r\n> > The top tabs need to be stored so that when users reopen the application, these tabs are retained, similar to browser tabs. I was struggling between using db or cache... I'll change it.\r\n> \r\n> > ---\r\n> >

\r\n> > Original Content\r\n> > 顶部的Tab是需要做存储的,这样用户重新打开应用会保留这些Tab,行为类似浏览器tab\r\n> > 在用db还是cache纠结了...我改一下\r\n> >
\r\n\r\n> [!NOTE]\r\n> This issue/comment/review was translated by Claude.\r\n\r\nYou can use cache with persist. This is a typical use case for cache data:\r\n- Active tab inherently doesn't need synchronization (each terminal may have its own active tab),\r\n- It doesn't matter if this state is reset or lost (users don't care or can easily reset it)\r\n\r\n---\r\n
\r\nOriginal Content\r\n\r\n用带persist的cache就行,这个是cache数据的典型用法之处:\r\n- active tab本来就是个无需同步(各个终端可能有用户自己的active),\r\n- 该状态重置或丢失了没有关系(用户不在意也可以easily重置的)\r\n\r\n

- Remove /app/state/:key API endpoint from apiSchemas
- Remove appStateService handler from API handlers
- Add Tab/TabsState types to persist cache schema
- Refactor useTabs to use usePersistCache instead of useQuery/useMutation
- Delete unused routeTree.gen.ts

This change improves performance by avoiding unnecessary IPC + DB roundtrips
for UI state that can be safely stored in localStorage with 200ms debounce.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@0xfullex
Copy link
Collaborator

0xfullex commented Nov 26, 2025

Note

This issue/comment/review was translated by Claude.

There are naming conventions for cache keys. I've updated ESLint, you can adjust it further.


Original Content

cache key的命名有规范,我更新了ESLint,可以再调整下

@MyPrototypeWhat
Copy link
Collaborator Author

MyPrototypeWhat commented Nov 26, 2025

Note

This issue/comment/review was translated by Claude.

There are naming conventions for cache keys. I've updated ESLint, you can adjust it further.

ok


Original Content

cache key的命名有规范,我更新了ESLint,可以再调整下

ok

MyPrototypeWhat and others added 3 commits November 26, 2025 15:03
- Remove redundant 'ui.activeTabId' from UseCacheSchema (already in TabsState)
- Rename 'tabs_state' to 'ui.tab.state' to follow ESLint naming rule
- Update useTabs hook to use new cache key

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Introduced new markdown files detailing the architecture for Multi Page Application (MPA) with process isolation and Multi MemoryRouter instances.
- The MPA document outlines the concept, configuration, implementation, and performance analysis, emphasizing faster initial load and crash isolation.
- The Multi MemoryRouter document presents a recommended architecture for state preservation and tab management, addressing key requirements and providing a migration strategy.
- The Single Router + Outlet document describes a hybrid approach for tab management, focusing on state synchronization and webview handling.

This addition enhances the project's documentation, providing clear guidelines for future development and architectural decisions.
…r enhancements

- Changed code snippets in the architecture documentation to use the `text` syntax for better clarity.
- Replaced CSS `display:none` with React 19.2 `<Activity>` component for improved effect management and memory optimization.
- Documented the design decision to use a Pure MemoryRouter over HashRouter for detached windows, emphasizing simplicity and consistency.
- Enhanced the Tab Detach implementation with detailed explanations of state passing via URL query and IPC, along with architecture flow diagrams.
- Added new components and functions to support detached tab functionality, including state restoration and memory management strategies.

These updates improve the documentation and functionality of the tab management system, ensuring better performance and user experience.
…eTree.gen.ts

- Added src/renderer/src/routeTree.gen.ts to the ESLint includes for linting.
- Updated biome.jsonc to exclude src/renderer/src/routeTree.gen.ts from processing.
- Ensured consistent formatting in routeTree.gen.ts by adding missing commas and disabling ESLint for the file.
- Introduced a new TabRouter component to manage routing for each tab independently, enhancing the tab management system.
- Updated the AppShell component to utilize the new TabRouter, allowing for true KeepAlive behavior with isolated history.
- Refactored the Sidebar component for improved navigation and tab creation, now supporting internal app routes.
- Enhanced the useTabs hook to ensure at least one default tab exists, improving user experience on initial load.
- Updated cacheSchemas to reflect changes in tab types and metadata structure.

These changes significantly improve the architecture and functionality of the tab system, providing a more robust user experience.
@MyPrototypeWhat
Copy link
Collaborator Author

MyPrototypeWhat commented Dec 5, 2025

Note

This issue/comment/review was translated by Claude.

Testing TSR + Tab + Multi MemoryRouter + Keepalive

20251205-170641.mp4

Original Content

测试 TSR + Tab + Multi MemoryRouter + Keepalive

20251205-170641.mp4

MyPrototypeWhat and others added 2 commits December 5, 2025 17:53
…docs

- Fix TanStack Router path config using resolve() for absolute paths
- Add openTab API to useTabs hook for Tab-level navigation
- Remove AppRouter.tsx (merged into AppShell)
- Add developer documentation (README.md, README.zh-CN.md)
- Add settings route placeholder
- Update App.tsx with TODO comments for migration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants