refactor(renderer): flatten src/renderer/src to src/renderer

Move all renderer source from src/renderer/src/* up one level to
src/renderer/*, removing the redundant nested src directory.

- Update path aliases (@renderer, @types, @logger, @data) and TanStack
  Router paths in electron.vite.config.ts; update tsconfig.{json,web,node}
  path mappings and include globs.
- Fix Vite root-relative script paths in the 8 renderer HTML entries.
- Update cross-process relative imports in main/preload (language,
  apiServer models, preload index) to drop the /src segment.
- Switch renderer test imports of the logger mock to the @test-mocks alias.
- Update hardcoded renderer paths in scripts and their fixtures, lint
  configs (eslint/oxlint/biome), CODEOWNERS, docs, and the data-classify tool.
- Convert deep (../../+) relative imports within the renderer to the
  @renderer alias (69 files, 108 imports); keep single-level relatives.
- Fix doc links broken by the move and correct one pre-existing broken
  link in naming-conventions.md.
This commit is contained in:
fullex
2026-05-28 21:40:20 -07:00
parent c514dcc049
commit 53a3577389
1889 changed files with 825 additions and 832 deletions

8
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,5 @@
/src/renderer/src/store/ @0xfullex @DeJeune
/src/renderer/src/databases/ @0xfullex @DeJeune
/src/renderer/store/ @0xfullex @DeJeune
/src/renderer/databases/ @0xfullex @DeJeune
/src/main/services/ConfigManager.ts @0xfullex @DeJeune
/src/shared/IpcChannel.ts @0xfullex @DeJeune
/src/main/ipc.ts @0xfullex @DeJeune
@@ -8,8 +8,8 @@
/src/main/core/ @0xfullex
/src/shared/data/ @0xfullex
/src/main/data/ @0xfullex
/src/renderer/src/data/ @0xfullex
/src/renderer/src/core/ @0xfullex
/src/renderer/data/ @0xfullex
/src/renderer/core/ @0xfullex
/src/preload/ @0xfullex
/v2-refactor-temp/ @0xfullex
/docs/references/data/ @0xfullex

View File

@@ -18,7 +18,7 @@
"src/main/services/nutstore/sso/lib/**",
"src/main/integration/cherryai/index.js",
"src/main/services/nutstore/sso/lib/**",
"src/renderer/src/ui/**",
"src/renderer/ui/**",
"packages/**/dist",
"eslint.config.mjs",
"v2-refactor-temp/**"

View File

@@ -99,7 +99,7 @@ Use the `gh-create-issue` skill. Fallback: read `.agents/skills/gh-create-issue/
### TypeScript
- Place shared type definitions in `src/renderer/src/types/` or `src/shared/`.
- Place shared type definitions in `src/renderer/types/` or `src/shared/`.
### Naming Conventions
@@ -126,7 +126,7 @@ logger.error("message", error);
- All user-visible strings must use `i18next` — never hardcode UI strings
- Run `pnpm i18n:check` to validate; `pnpm i18n:sync` to add missing keys
- Locale files in `src/renderer/src/i18n/`
- Locale files in `src/renderer/i18n/`
### UI Design

View File

@@ -2,7 +2,7 @@
## 1. Visual Theme & Atmosphere
> **Source of truth:** token sources live in `packages/ui/src/styles/tokens/` and Tailwind-facing aliases are generated in `packages/ui/src/styles/theme.css`. Renderer-only bridge aliases live in `src/renderer/src/assets/styles/tailwind.css`. This document references public aliases only when they are actually exported; for actual values open the relevant token source or generated theme alias.
> **Source of truth:** token sources live in `packages/ui/src/styles/tokens/` and Tailwind-facing aliases are generated in `packages/ui/src/styles/theme.css`. Renderer-only bridge aliases live in `src/renderer/assets/styles/tailwind.css`. This document references public aliases only when they are actually exported; for actual values open the relevant token source or generated theme alias.
Cherry Studio is a shadcn/ui-based design system built for an AI conversation application. The design language follows a neutral-first approach — a restrained, systematic palette rooted in pure neutral grays where the interface itself recedes to let content take center stage. The aesthetic is utilitarian-modern: clean surfaces, subtle borders, and restrained use of the exported primary color for true primary actions, creating a tool that feels professional, focused, and endlessly customizable through its robust light/dark mode support.
@@ -487,11 +487,11 @@ When a search field needs an inline trailing button (e.g. add provider in `Provi
- Foreground: `var(--color-foreground)` at full opacity
- Disabled: `pointer-events-none opacity-30`
Canonical implementation: `providerListClasses.searchInlineAddButton` in `src/renderer/src/pages/settings/ProviderSettings/primitives/classNames.ts`. The search wrap itself stays the standard input surface (`bg-background`, hairline border, `rounded-xl`).
Canonical implementation: `providerListClasses.searchInlineAddButton` in `src/renderer/pages/settings/ProviderSettings/primitives/classNames.ts`. The search wrap itself stays the standard input surface (`bg-background`, hairline border, `rounded-xl`).
### Sidebar
Sidebar primitives currently live in `src/renderer/src/components/Sidebar`, not in `@cherrystudio/ui`. Treat this section as renderer sidebar guidance until a shared `@cherrystudio/ui` sidebar API exists.
Sidebar primitives currently live in `src/renderer/components/Sidebar`, not in `@cherrystudio/ui`. Treat this section as renderer sidebar guidance until a shared `@cherrystudio/ui` sidebar API exists.
The page owns the outer wrapper (width / Scrollbar / padding). Reusable sidebar internals should own spacing, sizing, and active state so individual pages do not hand-roll divergent menus.
@@ -526,7 +526,7 @@ The page owns the outer wrapper (width / Scrollbar / padding). Reusable sidebar
> If a sidebar elsewhere needs different spacing, propose a shared renderer variant before hard-coding page-local overrides.
>
> **Target rule:** once the `SidebarHeader / SidebarSection / SidebarSectionTitle / SidebarMenuItem` family lands in `@cherrystudio/ui`, hand-rolled sidebar menus will not be allowed. Until that family ships, compose with `MenuList` + `MenuItem` + project-level className tokens (see `src/renderer/src/pages/settings/index.tsx` for the canonical token pattern: `settingsSubmenuItemClassName`, `settingsSubmenuItemLabelClassName`, `settingsSubmenuSectionTitleClassName`, `settingsSubmenuDividerClassName`).
> **Target rule:** once the `SidebarHeader / SidebarSection / SidebarSectionTitle / SidebarMenuItem` family lands in `@cherrystudio/ui`, hand-rolled sidebar menus will not be allowed. Until that family ships, compose with `MenuList` + `MenuItem` + project-level className tokens (see `src/renderer/pages/settings/index.tsx` for the canonical token pattern: `settingsSubmenuItemClassName`, `settingsSubmenuItemLabelClassName`, `settingsSubmenuSectionTitleClassName`, `settingsSubmenuDividerClassName`).
### Page Header
@@ -612,7 +612,7 @@ Submenu composition rules:
- Use `PageHeader` from `@cherrystudio/ui` at the top — do not hand-roll a header.
- **Section-title-as-page-title exception**: when a page-level label is itself a *group name* that should match in-list group labels, keep using `PageHeader` and pass `titleClassName="font-normal text-foreground-muted text-xs leading-4"` so the heading swaps to section-title typography while preserving the same 16px line box. The PageHeader's `mt-3 + h-8 + mb-2` outer geometry is preserved, so the label baseline still aligns with the right column's PageHeader heading. See `page-header.stories.tsx` `SectionTitleStyle` for the canonical example.
- Wrap menu rows in `MenuList` with `gap-1`; group with `MenuDivider` + a section title `<div>` carrying `settingsSubmenuSectionTitleClassName`.
- Each row is a `MenuItem` styled by the canonical settings token pair: `settingsSubmenuItemClassName` on `className` (height / hover / active surface) and `settingsSubmenuItemLabelClassName` on `labelClassName` (`group-data-[active=true]:font-medium` for the bold-on-active label). Both tokens live in `src/renderer/src/pages/settings/index.tsx`.
- Each row is a `MenuItem` styled by the canonical settings token pair: `settingsSubmenuItemClassName` on `className` (height / hover / active surface) and `settingsSubmenuItemLabelClassName` on `labelClassName` (`group-data-[active=true]:font-medium` for the bold-on-active label). Both tokens live in `src/renderer/pages/settings/index.tsx`.
- Provider-style nested lists (`ProviderList`) follow the same shape: `PageHeader` + search field with trailing action + scroll body. They use their own scoped tokens in `ProviderSettings/primitives/classNames.ts` but keep the 200px column convention.
### Spacing System

View File

@@ -60,7 +60,7 @@
"!src/main/services/nutstore/sso/lib/**",
"!**/tailwind.css",
"!**/package.json",
"!src/renderer/src/routeTree.gen.ts",
"!src/renderer/routeTree.gen.ts",
"!.zed/**",
"!v2-refactor-temp/**"
],
@@ -94,7 +94,7 @@
"enabled": true,
"includes": [
"!**/tailwind.css",
"!src/renderer/src/routeTree.gen.ts",
"!src/renderer/routeTree.gen.ts",
"!v2-refactor-temp/**",
"src/renderer/**/*.{tsx,ts}"
],

View File

@@ -89,7 +89,7 @@ To avoid missing keys, all dynamically translated texts should first maintain a
For example:
```ts
// src/renderer/src/i18n/label.ts
// src/renderer/i18n/label.ts
const themeModeKeyMap = {
dark: "settings.theme.dark",
light: "settings.theme.light",

View File

@@ -145,7 +145,7 @@ For example, if the chain is `[AuthMiddleware, CacheMiddleware, LoggingMiddlewar
### Registering Middleware
Middleware is registered in `src/renderer/src/providers/middleware/register.ts` (or a similar configuration file).
Middleware is registered in `src/renderer/providers/middleware/register.ts` (or a similar configuration file).
```typescript
// register.ts

View File

@@ -39,7 +39,7 @@ Cherry Studio's AI calls follow a clear layered architecture:
┌─────────────────────────────────────────────────────────────┐
│ Service Layer │
│ src/renderer/src/services/ │
│ src/renderer/services/ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ApiService.ts │ │
│ │ - transformMessagesAndFetch() │ │
@@ -51,7 +51,7 @@ Cherry Studio's AI calls follow a clear layered architecture:
┌─────────────────────────────────────────────────────────────┐
│ AI Provider Layer │
│ src/renderer/src/aiCore/ │
│ src/renderer/aiCore/ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ AiProvider (AiProvider.ts) │ │
│ │ - completions() │ │
@@ -155,7 +155,7 @@ User Input (UI)
┌─────────────────────────────────────────────────────────────┐
│ 2. ApiService.transformMessagesAndFetch() │
│ Location: src/renderer/src/services/ApiService.ts:92 │
│ Location: src/renderer/services/ApiService.ts:92 │
│ │
│ Step 2.1: ConversationService.prepareMessagesForModel() │
│ ├─ Message format conversion (UI Message → Model Message) │
@@ -174,7 +174,7 @@ User Input (UI)
┌─────────────────────────────────────────────────────────────┐
│ 3. ApiService.fetchChatCompletion() │
│ Location: src/renderer/src/services/ApiService.ts:139 │
│ Location: src/renderer/services/ApiService.ts:139 │
│ │
│ Step 3.1: getProviderByModel() + API Key Rotation │
│ ├─ Get provider configuration │
@@ -199,7 +199,7 @@ User Input (UI)
┌─────────────────────────────────────────────────────────────┐
│ 4. AiProvider.completions() │
│ Location: src/renderer/src/aiCore/index_new.ts:116 │
│ Location: src/renderer/aiCore/index_new.ts:116 │
│ │
│ Step 4.1: providerToAiSdkConfig() │
│ ├─ Convert Cherry Provider → AI SDK Config │
@@ -214,7 +214,7 @@ User Input (UI)
┌─────────────────────────────────────────────────────────────┐
│ 5. AiProvider._completionsOrImageGeneration() │
│ Location: src/renderer/src/aiCore/index_new.ts:167 │
│ Location: src/renderer/aiCore/index_new.ts:167 │
│ │
│ Decision: │
│ ├─ Image generation endpoint → legacyProvider.completions()│
@@ -224,7 +224,7 @@ User Input (UI)
┌─────────────────────────────────────────────────────────────┐
│ 6. AiProvider.modernCompletions() │
│ Location: src/renderer/src/aiCore/index_new.ts:284 │
│ Location: src/renderer/aiCore/index_new.ts:284 │
│ │
│ Step 6.1: buildPlugins(config) │
│ └─ Build plugin array (Reasoning, ToolUse, WebSearch, etc.)│
@@ -291,7 +291,7 @@ User Input (UI)
┌─────────────────────────────────────────────────────────────┐
│ 10. Stream Data Processing │
│ Location: src/renderer/src/aiCore/chunk/ │
│ Location: src/renderer/aiCore/chunk/ │
│ │
│ Step 10.1: AiSdkToChunkAdapter.processStream() │
│ ├─ Listen to AI SDK's textStream │
@@ -377,7 +377,7 @@ plugins = [ReasoningPlugin, ToolUsePlugin, WebSearchPlugin]
#### File Location
`src/renderer/src/services/ApiService.ts`
`src/renderer/services/ApiService.ts`
#### Core Responsibilities
@@ -556,7 +556,7 @@ function getRotatedApiKey(provider: Provider): string {
#### File Location
`src/renderer/src/aiCore/index_new.ts`
`src/renderer/aiCore/index_new.ts`
#### Core Responsibilities
@@ -682,7 +682,7 @@ private async modernCompletions(
#### providerToAiSdkConfig() Details
**File**: `src/renderer/src/aiCore/provider/providerConfig.ts`
**File**: `src/renderer/aiCore/provider/providerConfig.ts`
```typescript
export function providerToAiSdkConfig(
@@ -1132,7 +1132,7 @@ Other built-in plugins remain unchanged. See:
### 6.1 Message Conversion
**File**: `src/renderer/src/services/ConversationService.ts`
**File**: `src/renderer/services/ConversationService.ts`
```typescript
export class ConversationService {
@@ -1190,7 +1190,7 @@ export class ConversationService {
### 6.2 Stream Data Adaptation
**File**: `src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts`
**File**: `src/renderer/aiCore/chunk/AiSdkToChunkAdapter.ts`
```typescript
export default class AiSdkToChunkAdapter {
@@ -1411,7 +1411,7 @@ const executor = await createExecutor("custom-provider", {
#### Span Creation
**File**: `src/renderer/src/services/SpanManagerService.ts`
**File**: `src/renderer/services/SpanManagerService.ts`
```typescript
export function addSpan(params: StartSpanParams): Span | null {
@@ -1671,16 +1671,16 @@ Current test coverage:
### Service Layer
- `src/renderer/src/services/ApiService.ts` - Main API service
- `src/renderer/src/services/ConversationService.ts` - Message preparation
- `src/renderer/src/services/SpanManagerService.ts` - Trace management
- `src/renderer/services/ApiService.ts` - Main API service
- `src/renderer/services/ConversationService.ts` - Message preparation
- `src/renderer/services/SpanManagerService.ts` - Trace management
### AI Provider Layer
- `src/renderer/src/aiCore/index_new.ts` - AiProvider
- `src/renderer/src/aiCore/provider/providerConfig.ts` - Provider configuration
- `src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts` - Stream adaptation
- `src/renderer/src/aiCore/plugins/PluginBuilder.ts` - Plugin building
- `src/renderer/aiCore/index_new.ts` - AiProvider
- `src/renderer/aiCore/provider/providerConfig.ts` - Provider configuration
- `src/renderer/aiCore/chunk/AiSdkToChunkAdapter.ts` - Stream adaptation
- `src/renderer/aiCore/plugins/PluginBuilder.ts` - Plugin building
### Core Package
@@ -1693,8 +1693,8 @@ Current test coverage:
### App-Level Extensions
- `src/renderer/src/aiCore/provider/extensions/index.ts` - App-level provider extensions
- `src/renderer/src/aiCore/types/merged.ts` - Merged types (core + app extensions)
- `src/renderer/aiCore/provider/extensions/index.ts` - App-level provider extensions
- `src/renderer/aiCore/types/merged.ts` - Merged types (core + app extensions)
### Test Utilities

View File

@@ -179,10 +179,10 @@ cherry-studio
|-----------|----------|---------------|
| Service Lifecycle | `src/main/core/lifecycle/` | [Lifecycle Reference](./lifecycle/README.md) |
| Data Layer | `src/main/data/` | [Data Reference](./data/README.md) |
| AI Core | `src/renderer/src/aiCore/` | [AI Core Architecture](./ai-core-architecture.md) |
| AI Core | `src/renderer/aiCore/` | [AI Core Architecture](./ai-core-architecture.md) |
| MCP (Tool Use) | `src/main/services/mcp/` | — |
| Knowledge (RAG) | `src/main/knowledge/` | [KnowledgeService](./knowledge/knowledge-service.md) |
| Message System | `src/renderer/src/store/` | [Message System](./messaging/message-system.md) |
| Message System | `src/renderer/store/` | [Message System](./messaging/message-system.md) |
| CherryClaw (Agent) | `src/main/services/agents/` | [CherryClaw Overview](./cherryclaw/overview.md) |
| API Server | `src/main/apiServer/` | [App Upgrade Config](./app-upgrade.md) |

View File

@@ -36,7 +36,7 @@ The user-facing code execution component is [CodeBlockView][codeblock-view-link]
- **State Management and Output Display**: Uses `executionResult` to manage all execution output; whenever there's any result (text or image), the [StatusBar][statusbar-link] component is rendered for unified display.
```typescript
// src/renderer/src/components/CodeBlockView/view.tsx
// src/renderer/components/CodeBlockView/view.tsx
const [executionResult, setExecutionResult] = useState<{ text: string; image?: string } | null>(null)
const handleRunScript = useCallback(() => {
@@ -119,7 +119,7 @@ The core Python execution happens inside the Web Worker defined in [pyodide.work
<!-- Link Definitions -->
[pyodide-link]: https://pyodide.org/
[codeblock-view-link]: /src/renderer/src/components/CodeBlockView/view.tsx
[pyodide-service-link]: /src/renderer/src/services/PyodideService.ts
[pyodide-worker-link]: /src/renderer/src/workers/pyodide.worker.ts
[statusbar-link]: /src/renderer/src/components/CodeBlockView/StatusBar.tsx
[codeblock-view-link]: /src/renderer/components/CodeBlockView/view.tsx
[pyodide-service-link]: /src/renderer/services/PyodideService.ts
[pyodide-worker-link]: /src/renderer/workers/pyodide.worker.ts
[statusbar-link]: /src/renderer/components/CodeBlockView/StatusBar.tsx

View File

@@ -268,8 +268,8 @@ See [App State Overview](./app-state-overview.md) for full rules and the key reg
- `src/main/data/db/` - Database schemas
### Renderer Process Implementation
- `src/renderer/src/data/DataApiService.ts` - API client
- `src/renderer/src/data/CacheService.ts` - Cache service
- `src/renderer/src/data/PreferenceService.ts` - Preference service
- `src/renderer/src/data/hooks/` - React hooks
- `src/renderer/data/DataApiService.ts` - API client
- `src/renderer/data/CacheService.ts` - Cache service
- `src/renderer/data/PreferenceService.ts` - Preference service
- `src/renderer/data/hooks/` - React hooks

View File

@@ -184,7 +184,7 @@ Create a custom hook that merges presets with user overrides:
> **Note:** The hook below is a basic example. Your actual implementation should be tailored to your specific data structure and usage patterns. Consider factors like: which fields are user-editable, how merging should work for nested objects, whether you need filtering/sorting, etc.
```typescript
// src/renderer/src/hooks/useProviders.ts
// src/renderer/hooks/useProviders.ts
import { useCallback, useMemo } from 'react'

View File

@@ -39,7 +39,7 @@ Non-obvious rules the code enforces; assume them when designing consumers.
1. **Same-value write is a no-op.** Equality via `lodash.isEqual`. No broadcast, no subscriber fire, no hook re-render. (`src/main/data/CacheService.ts` `isEqual` guards before `broadcastSync` / notifier)
2. **TTL-only refresh does not fire subscribers.** Updating `expireAt` on the same value is silent.
3. **Subscribers fire only on explicit writes.** Lazy TTL cleanup, the 10-min GC sweep, and `onStop` do not fire.
4. **Hooks + TTL is discouraged.** `useCache` / `useSharedCache` log a warn when the key has TTL (`src/renderer/src/data/hooks/useCache.ts:186-192,289-295`) — values can expire between renders.
4. **Hooks + TTL is discouraged.** `useCache` / `useSharedCache` log a warn when the key has TTL (`src/renderer/data/hooks/useCache.ts:186-192,289-295`) — values can expire between renders.
5. **Hooks pin cache entries.** `registerHook` / `unregisterHook` refcount keys; `delete` / `deleteShared` return `false` while any hook is active.
6. **Persist has no delete.** Persist keys are fixed by schema; the API exposes only `getPersist` / `setPersist` / `hasPersist`.
7. **TTL uses absolute `expireAt` (Unix ms).** Every process expires the same entry at the same instant, regardless of clock skew in IPC delivery.
@@ -107,4 +107,4 @@ Non-obvious rules the code enforces; assume them when designing consumers.
- [Cache Usage](./cache-usage.md) — React hooks, direct API, patterns
- [Cache Schema Guide](./cache-schema-guide.md) — Adding fixed and template keys
- Source: `src/main/data/CacheService.ts`, `src/renderer/src/data/CacheService.ts`, `src/renderer/src/data/hooks/useCache.ts`, `src/shared/data/cache/`
- Source: `src/main/data/CacheService.ts`, `src/renderer/data/CacheService.ts`, `src/renderer/data/hooks/useCache.ts`, `src/shared/data/cache/`

View File

@@ -75,7 +75,7 @@ Migrators (Redux/Dexie → SQLite) use the pure-function counterparts `assignOrd
### 4. Renderer — `useReorder` hook
File: `src/renderer/src/data/hooks/useReorder.ts`. One hook on top of `useMutation`; drop its `applyReorderedList` straight into a drag-and-drop callback.
File: `src/renderer/data/hooks/useReorder.ts`. One hook on top of `useMutation`; drop its `applyReorderedList` straight into a drag-and-drop callback.
```tsx
import { useQuery } from '@data/hooks/useDataApi'

View File

@@ -82,7 +82,7 @@ src/shared/file/types/tree.ts ← shared with renderer
├── CreateTreeIpcResult — { treeId, snapshot }
└── TreeMutationPushPayload — { treeId, event }
src/renderer/src/hooks/useDirectoryTree.ts ← renderer hook
src/renderer/hooks/useDirectoryTree.ts ← renderer hook
├── On mount → File_TreeCreate → rehydrate TreeNode class hierarchy
├── On File_TreeMutation (filtered by treeId) → applyMutation in place
├── Returns { root, isLoading, error, version, treeId, getNode }
@@ -363,7 +363,7 @@ Three suites under `src/main/services/file/tree/__tests__/`:
- **`TreeNode.test.ts`** — class invariants: rename cascade, identity preservation, JSON serialization shape.
- **`search.test.ts`** — `listDirectory` happy path + error branches (ripgrep unavailable, EACCES on root).
Renderer-side: `src/renderer/src/hooks/__tests__/useDirectoryTree.test.tsx` covers mount/unmount, mutation application, mid-flight cancel, StrictMode remount, post-unmount rejection, and treeId mismatch filtering.
Renderer-side: `src/renderer/hooks/__tests__/useDirectoryTree.test.tsx` covers mount/unmount, mutation application, mid-flight cancel, StrictMode remount, post-unmount rejection, and treeId mismatch filtering.
---

View File

@@ -42,7 +42,7 @@ The 90% case. See later sections for full rules and edge cases.
| Bucket directory (categorical container) | lowercase **plural** noun | `services/`, `utils/`, `hooks/` |
| Business / domain module directory | `camelCase` | `apiServer/`, `fileProcessing/` |
| `packages/ui/` directory | `kebab-case` | `primitives/`, `button-group/` |
| TanStack route file under `src/renderer/src/routes/` | `kebab-case.tsx` | `api-server.tsx`, `quick-assistant.tsx` |
| TanStack route file under `src/renderer/routes/` | `kebab-case.tsx` | `api-server.tsx`, `quick-assistant.tsx` |
> Stateful classes use only `Service` (default) or `Manager` (instance pool) — see §5.2. Files placed inside any `utils/` directory drop the `Utils` suffix — the directory already declares the role; see §3.2.
@@ -64,8 +64,8 @@ Three rules trump any specific table below when in conflict:
| Location | Convention | Rationale |
|---|---|---|
| `src/renderer/src/components/**` | `PascalCase.tsx` | Filename mirrors the exported component name. |
| `src/renderer/src/pages/**` | `PascalCase.tsx` | Filename mirrors the exported component name. |
| `src/renderer/components/**` | `PascalCase.tsx` | Filename mirrors the exported component name. |
| `src/renderer/pages/**` | `PascalCase.tsx` | Filename mirrors the exported component name. |
| `packages/ui/**` (shadcn-derived) | `kebab-case.tsx` | Required by shadcn CLI for cross-OS file resolution. |
The component's **exported identifier** is always `PascalCase`, regardless of filename style:
@@ -74,7 +74,7 @@ The component's **exported identifier** is always `PascalCase`, regardless of fi
// packages/ui/src/components/primitives/button.tsx
export function Button() { /* ... */ }
// src/renderer/src/components/Sidebar.tsx
// src/renderer/components/Sidebar.tsx
export function Sidebar() { /* ... */ }
```
@@ -102,7 +102,7 @@ utils/notesTree.ts ✅
A `*Utils` suffix is used only when the file lives outside any `utils/` directory.
**Hooks (`useXxx.ts`)** — live in `src/renderer/src/hooks/` (default, may group into sub-folders by feature) or co-located with the consuming feature.
**Hooks (`useXxx.ts`)** — live in `src/renderer/hooks/` (default, may group into sub-folders by feature) or co-located with the consuming feature.
**Renderer wrappers around `window.api.*`** — the renderer does not use `*Api`, `*Client`, or any other IPC-wrapper suffix. Categorize wrappers by module shape per §5.2.
@@ -158,9 +158,9 @@ packages/SomePkg/ ❌ (PascalCase not allowed)
When a directory **is** a component (i.e. contains `index.tsx` exporting the component, or groups files under one component name), use `PascalCase`.
```
src/renderer/src/components/Sidebar/ ✅
src/renderer/src/components/CodeEditor/ ✅
src/renderer/src/components/MarkdownEditor/ ✅
src/renderer/components/Sidebar/ ✅
src/renderer/components/CodeEditor/ ✅
src/renderer/components/MarkdownEditor/ ✅
```
### 4.3 Bucket Directories — `lowercase plural noun`
@@ -305,7 +305,7 @@ The `Service` suffix names a **role** (a stateful domain capability), not a **me
| Lifecycle service | `@Injectable('XxxService')` + `extends BaseService`, accessed via `application.get('XxxService')` | The service owns long-lived resources OR registers persistent side effects |
| Direct-import singleton service | `export const xxxService = new XxxService()` | No long-lived resources, no persistent side effects, but still has class-level state (e.g. cached SDK instances) |
The criteria for choosing between them are defined in [`docs/references/lifecycle/lifecycle-decision-guide.md`](../lifecycle/lifecycle-decision-guide.md).
The criteria for choosing between them are defined in [`docs/references/lifecycle/lifecycle-decision-guide.md`](lifecycle/lifecycle-decision-guide.md).
---
@@ -345,7 +345,7 @@ In `packages/*`, the directory name and `package.json#name` (after stripping sco
### 6.6 TanStack Router File-Based Routes
Files under `src/renderer/src/routes/` are **kebab-case** — TanStack Router maps filename directly to URL.
Files under `src/renderer/routes/` are **kebab-case** — TanStack Router maps filename directly to URL.
Reserved tokens (TanStack-defined):
@@ -374,9 +374,9 @@ Any of these signals warrants a consolidation review.
```
Naming a new FILE
├─ React component (.tsx)?
│ ├─ Under src/renderer/src/routes/? → kebab-case.tsx (api-server.tsx)
│ ├─ Under src/renderer/routes/? → kebab-case.tsx (api-server.tsx)
│ ├─ Under packages/ui/? → kebab-case.tsx (button.tsx)
│ └─ Under src/renderer/src/? → PascalCase.tsx (Sidebar.tsx)
│ └─ Under src/renderer/? → PascalCase.tsx (Sidebar.tsx)
├─ React hook? → useXxx.ts (useShortcuts.ts)
├─ Primary export is a class? → PascalCase.ts (KnowledgeService.ts)
├─ Primary export is function(s)? → camelCase.ts (markdownConverter.ts)

View File

@@ -144,5 +144,5 @@ Behavioral injection goes through **`onWindowCreated`** (or its type-filtered co
### Renderer Integration
- `src/renderer/src/core/hooks/useWindowInitData.ts` — Canonical hook for init data consumption
- `src/renderer/core/hooks/useWindowInitData.ts` — Canonical hook for init data consumption
- `src/shared/IpcChannel.ts``WindowManager_*` IPC channel constants

View File

@@ -226,7 +226,7 @@ openTopic(topicId: string): void {
## Renderer: `useWindowInitData` hook
`src/renderer/src/core/hooks/useWindowInitData.ts` provides the canonical way for any managed window to consume its init data across both creation paths:
`src/renderer/core/hooks/useWindowInitData.ts` provides the canonical way for any managed window to consume its init data across both creation paths:
```typescript
import { useWindowInitData } from '@renderer/core/hooks/useWindowInitData'

View File

@@ -35,7 +35,7 @@ export default defineConfig({
alias: {
'@main': resolve('src/main'),
'@application': resolve('src/main/core/application'),
'@types': resolve('src/renderer/src/types'),
'@types': resolve('src/renderer/types'),
'@data': resolve('src/main/data'),
'@shared': resolve('src/shared'),
'@logger': resolve('src/main/core/logger/LoggerService'),
@@ -101,8 +101,8 @@ export default defineConfig({
tanstackRouter({
target: 'react',
autoCodeSplitting: true,
routesDirectory: resolve('src/renderer/src/routes'),
generatedRouteTree: resolve('src/renderer/src/routeTree.gen.ts')
routesDirectory: resolve('src/renderer/routes'),
generatedRouteTree: resolve('src/renderer/routeTree.gen.ts')
}),
(async () => (await import('@tailwindcss/vite')).default())(),
react({
@@ -113,11 +113,11 @@ export default defineConfig({
],
resolve: {
alias: {
'@renderer': resolve('src/renderer/src'),
'@renderer': resolve('src/renderer'),
'@shared': resolve('src/shared'),
'@types': resolve('src/renderer/src/types'),
'@logger': resolve('src/renderer/src/services/LoggerService'),
'@data': resolve('src/renderer/src/data'),
'@types': resolve('src/renderer/types'),
'@logger': resolve('src/renderer/services/LoggerService'),
'@data': resolve('src/renderer/data'),
'@mcp-trace/trace-core': resolve('packages/mcp-trace/trace-core'),
'@mcp-trace/trace-web': resolve('packages/mcp-trace/trace-web'),
'@cherrystudio/ai-core/provider': resolve('packages/aiCore/src/core/providers'),

View File

@@ -125,8 +125,8 @@ export default defineConfig([
'src/main/services/nutstore/sso/lib/**',
'src/main/integration/cherryai/index.js',
'src/main/services/nutstore/sso/lib/**',
'src/renderer/src/ui/**',
'src/renderer/src/routeTree.gen.ts',
'src/renderer/ui/**',
'src/renderer/routeTree.gen.ts',
'packages/**/dist',
'v2-refactor-temp/**'
]
@@ -316,11 +316,11 @@ export default defineConfig([
},
// renderer legacy css var migration warnings
{
files: ['src/renderer/src/**/*.{ts,tsx,js,jsx}'],
files: ['src/renderer/**/*.{ts,tsx,js,jsx}'],
ignores: [
'src/renderer/src/**/*.test.*',
'src/renderer/src/**/__tests__/**',
'src/renderer/src/**/__mocks__/**'
'src/renderer/**/*.test.*',
'src/renderer/**/__tests__/**',
'src/renderer/**/__mocks__/**'
],
plugins: {
'renderer-styles': {

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/draggable-list/index.tsx
// Original path: src/renderer/components/draggable-list/index.tsx
export { default as DraggableList } from './list'
export { useDraggableReorder } from './use-draggable-reorder'
export {

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/draggable-list/list.tsx
// Original path: src/renderer/components/draggable-list/list.tsx
import type {
DroppableProps,
DropResult,

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/draggable-list/useDraggableReorder.ts
// Original path: src/renderer/components/draggable-list/useDraggableReorder.ts
import type { DropResult } from '@hello-pangea/dnd'
import type { Key } from 'react'
import { useCallback, useMemo } from 'react'

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/editable-number/index.tsx
// Original path: src/renderer/components/editable-number/index.tsx
import { cn } from '@cherrystudio/ui/lib/utils'
import * as React from 'react'

View File

@@ -1,4 +1,4 @@
// Original: src/renderer/src/components/ellipsis/index.tsx
// Original: src/renderer/components/ellipsis/index.tsx
import { cn } from '@cherrystudio/ui/lib/utils'
import type { HTMLAttributes } from 'react'

View File

@@ -1,4 +1,4 @@
// Original: src/renderer/src/components/horizontal-scroll-container/index.tsx
// Original: src/renderer/components/horizontal-scroll-container/index.tsx
import { cn } from '@cherrystudio/ui/lib/utils'
import { ChevronRight } from 'lucide-react'
import * as React from 'react'

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/Preview/ImageToolButton.tsx
// Original path: src/renderer/components/Preview/ImageToolButton.tsx
import { Button } from '@cherrystudio/ui/components/primitives/button'
import { Tooltip } from '@cherrystudio/ui/components/primitives/tooltip'
import { memo } from 'react'

View File

@@ -1,4 +1,4 @@
// Original: src/renderer/src/components/scrollbar/index.tsx
// Original: src/renderer/components/scrollbar/index.tsx
import { cn } from '@cherrystudio/ui/lib/utils'
import { throttle } from 'lodash'
import * as React from 'react'

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/CopyButton.tsx
// Original path: src/renderer/components/CopyButton.tsx
import { Copy } from 'lucide-react'
import type { FC } from 'react'

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/Tags/CustomTag.tsx
// Original path: src/renderer/components/Tags/CustomTag.tsx
import { X } from 'lucide-react'
import type { CSSProperties, FC, MouseEventHandler } from 'react'
import { memo, useMemo } from 'react'

View File

@@ -1,4 +1,4 @@
// Original: src/renderer/src/components/DividerWithText.tsx
// Original: src/renderer/components/DividerWithText.tsx
import type { CSSProperties } from 'react'
import React from 'react'

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/EmojiIcon.tsx
// Original path: src/renderer/components/EmojiIcon.tsx
import type { FC } from 'react'
interface EmojiIconProps {

View File

@@ -1,4 +1,4 @@
// Original path: src/renderer/src/components/ErrorBoundary.tsx
// Original path: src/renderer/components/ErrorBoundary.tsx
import { AlertTriangle } from 'lucide-react'
import type { ComponentType, ReactNode } from 'react'
import type { FallbackProps } from 'react-error-boundary'

View File

@@ -1,4 +1,4 @@
// Original: src/renderer/src/components/IndicatorLight.tsx
// Original: src/renderer/components/IndicatorLight.tsx
import React from 'react'
interface IndicatorLightProps {

View File

@@ -1,4 +1,4 @@
// Original: src/renderer/src/components/Spinner.tsx
// Original: src/renderer/components/Spinner.tsx
import { motion } from 'framer-motion'
import { Search } from 'lucide-react'

View File

@@ -11,5 +11,5 @@
* type UserWithName = RequireSome<User, 'name'>
* // Result: { name: string; age?: number; }
*/
// The type is copied from src/renderer/src/types/index.ts.
// The type is copied from src/renderer/types/index.ts.
export type RequireSome<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>

View File

@@ -45,7 +45,7 @@ function findTemplateLiteral(project: Project, code: string): Node | undefined {
vi.mock('fs')
describe('check-hardcoded-strings', () => {
const mockSrcDir = '/mock/src/renderer/src'
const mockSrcDir = '/mock/src/renderer'
beforeEach(() => {
vi.resetAllMocks()

View File

@@ -19,7 +19,7 @@ describe('check-legacy-css-vars', () => {
/* var(--color-text-2) */
`
expect(findLegacyVarHitsInContent(content, 'src/renderer/src/example.css')).toEqual([])
expect(findLegacyVarHitsInContent(content, 'src/renderer/example.css')).toEqual([])
})
it('reports real legacy variable usages', () => {
@@ -31,7 +31,7 @@ describe('check-legacy-css-vars', () => {
const node = '<div class="text-[var(--color-text-2)]" />';
`
const findings = findLegacyVarHitsInContent(content, 'src/renderer/src/example.tsx')
const findings = findLegacyVarHitsInContent(content, 'src/renderer/example.tsx')
expect(findings).toHaveLength(2)
expect(findings.map((finding) => finding.variable)).toEqual(['--color-text-1', '--color-text-2'])

View File

@@ -9,10 +9,10 @@ import {
describe('check-pr-style-reminders', () => {
it('reports legacy vars only from added lines', () => {
const diff = `
diff --git a/src/renderer/src/example.tsx b/src/renderer/src/example.tsx
diff --git a/src/renderer/example.tsx b/src/renderer/example.tsx
index 1111111..2222222 100644
--- a/src/renderer/src/example.tsx
+++ b/src/renderer/src/example.tsx
--- a/src/renderer/example.tsx
+++ b/src/renderer/example.tsx
@@ -10,0 +11,4 @@
+const css = 'color: var(--color-text-1);'
+// var(--color-text-2)
@@ -20,17 +20,17 @@ index 1111111..2222222 100644
-const removed = 'color: var(--color-text-3);'
`
const findings = parseAddedLegacyVarFindingsFromDiff(diff, 'src/renderer/src/example.tsx')
const findings = parseAddedLegacyVarFindingsFromDiff(diff, 'src/renderer/example.tsx')
expect(findings).toEqual([
{
file: 'src/renderer/src/example.tsx',
file: 'src/renderer/example.tsx',
line: 11,
variable: '--color-text-1',
lineText: "const css = 'color: var(--color-text-1);'"
},
{
file: 'src/renderer/src/example.tsx',
file: 'src/renderer/example.tsx',
line: 13,
variable: '--color-background-soft',
lineText: "const next = 'background: var(--color-background-soft);'"
@@ -40,10 +40,10 @@ index 1111111..2222222 100644
it('tracks added line numbers from a unified diff', () => {
const diff = `
diff --git a/src/renderer/src/example.tsx b/src/renderer/src/example.tsx
diff --git a/src/renderer/example.tsx b/src/renderer/example.tsx
index 1111111..2222222 100644
--- a/src/renderer/src/example.tsx
+++ b/src/renderer/src/example.tsx
--- a/src/renderer/example.tsx
+++ b/src/renderer/example.tsx
@@ -10,2 +10,4 @@
const old = true
+const added = 'w-[420px]'
@@ -59,7 +59,7 @@ index 1111111..2222222 100644
const body = buildPullRequestStyleRemindersComment(
[
{
file: 'src/renderer/src/example.tsx',
file: 'src/renderer/example.tsx',
line: 11,
variable: '--color-text-1',
lineText: "const css = 'color: var(--color-text-1);'"
@@ -67,7 +67,7 @@ index 1111111..2222222 100644
],
[
{
file: 'src/renderer/src/example.tsx',
file: 'src/renderer/example.tsx',
line: 12,
original: 'w-[420px]',
canonical: 'w-105',

View File

@@ -260,11 +260,11 @@ const countTranslatableStrings = (obj: I18N): number =>
const main = async () => {
validateConfig()
const localesDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
const translateDir = path.join(__dirname, '../src/renderer/src/i18n/translate')
const localesDir = path.join(__dirname, '../src/renderer/i18n/locales')
const translateDir = path.join(__dirname, '../src/renderer/i18n/translate')
const baseLocale = process.env.TRANSLATION_BASE_LOCALE ?? 'en-us'
const baseFileName = `${baseLocale}.json`
const baseLocalePath = path.join(__dirname, '../src/renderer/src/i18n/locales', baseFileName)
const baseLocalePath = path.join(__dirname, '../src/renderer/i18n/locales', baseFileName)
if (!fs.existsSync(baseLocalePath)) {
throw new Error(`${baseLocalePath} not found.`)
}

View File

@@ -7,7 +7,7 @@ import * as path from 'path'
import type { SourceFile } from 'ts-morph'
import { Node, Project } from 'ts-morph'
const RENDERER_DIR = path.join(__dirname, '../src/renderer/src')
const RENDERER_DIR = path.join(__dirname, '../src/renderer')
const MAIN_DIR = path.join(__dirname, '../src/main')
const EXTENSIONS = ['.tsx', '.ts']
const IGNORED_DIRS = ['__tests__', 'node_modules', 'i18n', 'locales', 'types', 'assets']

View File

@@ -3,7 +3,7 @@ import * as path from 'path'
import { sortedObjectByKeys } from './sort'
const translationsDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
const translationsDir = path.join(__dirname, '../src/renderer/i18n/locales')
const baseLocale = process.env.BASE_LOCALE ?? 'zh-cn'
const baseFileName = `${baseLocale}.json`
const baseFilePath = path.join(translationsDir, baseFileName)

View File

@@ -1,7 +1,7 @@
import * as fs from 'fs'
import * as path from 'path'
const RENDERER_DIR = path.join(__dirname, '../src/renderer/src')
const RENDERER_DIR = path.join(__dirname, '../src/renderer')
const CHECK_EXTENSIONS = new Set(['.css', '.ts', '.tsx'])
const IGNORED_DIR_NAMES = new Set(['node_modules', 'dist', 'out'])
const IGNORED_FILE_PATTERNS = [/\.test\.(ts|tsx)$/, /\.spec\.(ts|tsx)$/, /\.snap$/]

View File

@@ -71,19 +71,19 @@ function parseArgs(): { baseRef: string; headRef: string; markdownOutput?: strin
}
function isTrackedRendererFile(filePath: string): boolean {
if (!filePath.startsWith('src/renderer/src/')) return false
if (!filePath.startsWith('src/renderer/')) return false
if (!LEGACY_CHECK_EXTENSIONS.has(path.extname(filePath))) return false
return !shouldIgnoreFile(path.join(REPO_ROOT, filePath))
}
function isCanonicalClassCheckFile(filePath: string): boolean {
if (!filePath.startsWith('src/renderer/src/')) return false
if (!filePath.startsWith('src/renderer/')) return false
if (!CANONICAL_CLASS_CHECK_EXTENSIONS.has(path.extname(filePath))) return false
return !shouldIgnoreFile(path.join(REPO_ROOT, filePath))
}
function getChangedRendererFiles(baseRef: string, headRef: string): string[] {
const output = runGit(['diff', '--name-only', '--diff-filter=ACMR', baseRef, headRef, '--', 'src/renderer/src'])
const output = runGit(['diff', '--name-only', '--diff-filter=ACMR', baseRef, headRef, '--', 'src/renderer'])
return output
.split(/\r?\n/)
@@ -93,7 +93,7 @@ function getChangedRendererFiles(baseRef: string, headRef: string): string[] {
}
function getChangedCanonicalClassFiles(baseRef: string, headRef: string): string[] {
const output = runGit(['diff', '--name-only', '--diff-filter=ACMR', baseRef, headRef, '--', 'src/renderer/src'])
const output = runGit(['diff', '--name-only', '--diff-filter=ACMR', baseRef, headRef, '--', 'src/renderer'])
return output
.split(/\r?\n/)

View File

@@ -278,7 +278,7 @@ async function resolveStylesheet(id: string, base: string, cwd: string): Promise
}
export async function loadTailwindDesignSystem(cwd = process.cwd()): Promise<DesignSystem> {
const entryPath = path.join(cwd, 'src/renderer/src/assets/styles/tailwind.css')
const entryPath = path.join(cwd, 'src/renderer/assets/styles/tailwind.css')
const css = await fs.readFile(entryPath, 'utf8')
return __unstable__loadDesignSystem(css, {

View File

@@ -3,8 +3,8 @@ import * as path from 'path'
import { sortedObjectByKeys } from './sort'
const localesDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
const translateDir = path.join(__dirname, '../src/renderer/src/i18n/translate')
const localesDir = path.join(__dirname, '../src/renderer/i18n/locales')
const translateDir = path.join(__dirname, '../src/renderer/i18n/translate')
const baseLocale = process.env.TRANSLATION_BASE_LOCALE ?? 'en-us'
const baseFileName = `${baseLocale}.json`
const baseFilePath = path.join(localesDir, baseFileName)

View File

@@ -23,7 +23,7 @@ const INDEX = [
{ name: 'Greek', code: 'el-gr', model: MODEL }
]
const zh = JSON.parse(fs.readFileSync('src/renderer/src/i18n/locales/zh-cn.json', 'utf8')) as I18N
const zh = JSON.parse(fs.readFileSync('src/renderer/i18n/locales/zh-cn.json', 'utf8')) as I18N
const openai = new OpenAI({
apiKey: API_KEY,
@@ -134,11 +134,11 @@ void (async () => {
const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic)
bar.start(INDEX.length, 0)
for (const { name, code, model } of INDEX) {
const obj = fs.existsSync(`src/renderer/src/i18n/translate/${code}.json`)
? (JSON.parse(fs.readFileSync(`src/renderer/src/i18n/translate/${code}.json`, 'utf8')) as I18N)
const obj = fs.existsSync(`src/renderer/i18n/translate/${code}.json`)
? (JSON.parse(fs.readFileSync(`src/renderer/i18n/translate/${code}.json`, 'utf8')) as I18N)
: {}
await translate(zh, obj, name, model, () => {
fs.writeFileSync(`src/renderer/src/i18n/translate/${code}.json`, JSON.stringify(obj, null, 2), 'utf8')
fs.writeFileSync(`src/renderer/i18n/translate/${code}.json`, JSON.stringify(obj, null, 2), 'utf8')
})
count += 1
bar.update(count)

View File

@@ -3,7 +3,7 @@ import { formatApiHost, withoutTrailingSlash } from '@shared/utils'
import { trim } from 'lodash'
// NOTE: Since #13194, it's re-written with reduxService
// See: renderer/src/utils/api.ts: formatVertexApiHost
// See: renderer/utils/api.ts: formatVertexApiHost
export async function formatVertexApiHost(host: string): Promise<string> {
const { projectId: project, location } = await reduxService.select('llm.settings.vertexai')
const trimmedHost = withoutTrailingSlash(trim(host))

View File

@@ -1,7 +1,7 @@
import { loggerService } from '@logger'
import { isEmpty } from 'lodash'
import type { ApiModel, ApiModelsFilter, ApiModelsResponse } from '../../../renderer/src/types/apiModels'
import type { ApiModel, ApiModelsFilter, ApiModelsResponse } from '../../../renderer/types/apiModels'
import {
getAvailableProviders,
getProviderAnthropicModelChecker,

View File

@@ -65,7 +65,7 @@ export class MiniAppMigrator extends BaseMigrator {
this.originalSourceCount = groups.reduce((total, group) => total + group.data.length, 0)
// v1 strips `logo` to undefined before persisting custom apps to Redux state
// (see v1 src/renderer/src/store/minapps.ts reducers). The full custom-app
// (see v1 src/renderer/store/minapps.ts reducers). The full custom-app
// record — including logo — lives in `customMiniAppsFile` (resolved by
// MigrationPaths from {userData}/Data/Files/custom-minapps.json) and is
// reattached at runtime. Re-read it here so logos survive migration.

View File

@@ -31,7 +31,7 @@ import { legacyModelToUniqueId } from '../transformers/ModelTransformers'
/**
* Old Model type from Redux state
* Source: src/renderer/src/types/index.ts
* Source: src/renderer/types/index.ts
*/
/**
* Legacy data may have incomplete model objects (e.g. missing provider or group).
@@ -46,7 +46,7 @@ export interface OldModel {
/**
* Old AssistantSettings from Redux state
* Source: src/renderer/src/types/index.ts
* Source: src/renderer/types/index.ts
*/
export interface OldAssistantSettings {
maxTokens?: number
@@ -84,7 +84,7 @@ export interface OldMcpServer {
/**
* Old Assistant type from Redux state.
* Source: src/renderer/src/types/index.ts
* Source: src/renderer/types/index.ts
*
* Fields use nullable unions (`| null`) because legacy Redux data
* may store explicit nulls. All fields except `id` are optional

View File

@@ -69,7 +69,7 @@ import { legacyModelToUniqueId } from '../transformers/ModelTransformers'
/**
* Old Topic type from Redux assistants slice
* Source: src/renderer/src/types/index.ts
* Source: src/renderer/types/index.ts
*/
export interface OldTopic {
id: string
@@ -125,7 +125,7 @@ export interface OldModel {
/**
* Old Message type from Dexie topics table
* Source: src/renderer/src/types/newMessage.ts
* Source: src/renderer/types/newMessage.ts
*/
export interface OldMessage {
id: string

View File

@@ -49,7 +49,7 @@ export function transformMiniApp(
): Omit<MiniAppInsert, 'orderKey'> {
const appId = toRequired<string>(source.id, '')
// v1 stamps `type: 'Custom'` on apps loaded from custom-minapps.json
// (see v1 src/renderer/src/config/minapps.ts:loadCustomMiniApp). Preset rows
// (see v1 src/renderer/config/minapps.ts:loadCustomMiniApp). Preset rows
// in v1 leave `type` unset. Honor that explicit signal first so a user-created
// app whose id collides with a v2-only preset isn't misclassified as preset.
const isExplicitCustom = source.type === 'Custom'

View File

@@ -416,7 +416,7 @@ export async function registerIpc() {
// (read/write/delete/move on <userData>/Data/Files/<v1uuid>.<ext>, plus
// notes/watcher/directory utilities) backed by the FileStorage singleton.
// The Dexie `db.files` metadata + refcount layer lives entirely in the
// renderer (`src/renderer/src/services/FileManager.ts`); main never
// renderer (`src/renderer/services/FileManager.ts`); main never
// touches IndexedDB. Both sides stay live until Batch A-E migrates the
// renderer callers to the v2 surface (createInternalEntry /
// ensureExternalEntry / getPhysicalPath / permanentDelete / runSweep)

View File

@@ -15,7 +15,7 @@
* The former `ipcMain.handle(IpcChannel.ReduxStoreReady, ...)` handshake has
* been removed on both sides: the stub does not read from the renderer, so
* there is no readiness state to track, and the renderer's invoke of the
* channel (renderer/src/store/index.ts) has been commented out with the
* channel (renderer/store/index.ts) has been commented out with the
* existing `// [v2] Removed:` convention.
*
* Migrate each caller to the v2 data layer (Preference / DataApi / direct

View File

@@ -3,19 +3,19 @@ import { defaultLanguage } from '@shared/config/constant'
import type { LanguageVarious } from '@shared/data/preference/preferenceTypes'
import { app } from 'electron'
import EnUs from '../../renderer/src/i18n/locales/en-us.json'
import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json'
import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json'
import EnUs from '../../renderer/i18n/locales/en-us.json'
import ZhCn from '../../renderer/i18n/locales/zh-cn.json'
import ZhTw from '../../renderer/i18n/locales/zh-tw.json'
// Machine translation
import deDE from '../../renderer/src/i18n/translate/de-de.json'
import elGR from '../../renderer/src/i18n/translate/el-gr.json'
import esES from '../../renderer/src/i18n/translate/es-es.json'
import frFR from '../../renderer/src/i18n/translate/fr-fr.json'
import JaJP from '../../renderer/src/i18n/translate/ja-jp.json'
import ptPT from '../../renderer/src/i18n/translate/pt-pt.json'
import roRO from '../../renderer/src/i18n/translate/ro-ro.json'
import RuRu from '../../renderer/src/i18n/translate/ru-ru.json'
import viVN from '../../renderer/src/i18n/translate/vi-vn.json'
import deDE from '../../renderer/i18n/translate/de-de.json'
import elGR from '../../renderer/i18n/translate/el-gr.json'
import esES from '../../renderer/i18n/translate/es-es.json'
import frFR from '../../renderer/i18n/translate/fr-fr.json'
import JaJP from '../../renderer/i18n/translate/ja-jp.json'
import ptPT from '../../renderer/i18n/translate/pt-pt.json'
import roRO from '../../renderer/i18n/translate/ro-ro.json'
import RuRu from '../../renderer/i18n/translate/ru-ru.json'
import viVN from '../../renderer/i18n/translate/vi-vn.json'
export const locales = Object.fromEntries(
[

View File

@@ -89,7 +89,7 @@ import type {
SkillInstallOptions,
SkillResult,
SkillToggleOptions
} from '../renderer/src/types/skill'
} from '../renderer/types/skill'
// OpenClaw types
type OpenClawGatewayStatus = 'stopped' | 'starting' | 'running' | 'error'

View File

@@ -22,7 +22,7 @@
- **职责**:作为特定 AI Provider SDK 的纯粹适配层。
- **参数适配**:将应用内部统一的 `CoreRequest` 对象 (见下文) 转换为特定 SDK 所需的请求参数格式。
- **基础响应转换**:将 SDK 返回的原始数据块 (`RawSdkChunk`,例如 `OpenAI.Chat.Completions.ChatCompletionChunk`) 转换为一组最基础、最直接的应用层 `Chunk` 对象 (定义于 `src/renderer/src/types/chunk.ts`)。
- **基础响应转换**:将 SDK 返回的原始数据块 (`RawSdkChunk`,例如 `OpenAI.Chat.Completions.ChatCompletionChunk`) 转换为一组最基础、最直接的应用层 `Chunk` 对象 (定义于 `src/renderer/types/chunk.ts`)。
- 例如SDK 的 `delta.content` -> `TextDeltaChunk`SDK 的 `delta.reasoning_content` -> `ThinkingDeltaChunk`SDK 的 `delta.tool_calls` -> `RawToolCallChunk` (包含原始工具调用数据)。
- **关键**`XxxApiClient` **不处理**耦合在文本内容中的复杂结构,如 `<think>``<tool_use>` 标签。
- **特点**:极度轻量化,代码量少,易于实现和维护新的 Provider 适配。
@@ -155,7 +155,7 @@
## 4. 建议的文件/目录结构
```
src/renderer/src/
src/renderer/
└── aiCore/
├── clients/
│ ├── openai/
@@ -207,7 +207,7 @@ src/renderer/src/
1. **定义核心数据结构 (TypeScript 类型)**
- `CoreCompletionsRequest` (Type):定义应用内部统一的对话请求结构。
- `Chunk` (Type - 检查并按需扩展现有 `src/renderer/src/types/chunk.ts`)定义所有可能的通用Chunk类型。
- `Chunk` (Type - 检查并按需扩展现有 `src/renderer/types/chunk.ts`)定义所有可能的通用Chunk类型。
- 为其他API翻译、总结定义类似的 `CoreXxxRequest` (Type)。
2. **定义 `ApiClient` 接口:** 明确 `getRequestTransformer`, `getResponseChunkTransformer`, `getSdkInstance` 等核心方法。
3. **调整 `AiProviderMiddlewareContext`**

Some files were not shown because too many files have changed in this diff Show More