mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-06 22:55:56 +08:00
refactor/code-cli
16 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1c6ff30a18 |
refactor(shared): dissolve config/ and move logic out of types/ into utils/
Dissolve the by-kind @shared/config junk drawer per shared-layer governance: route each member by shape and actual consumer process — cross-process slices into types//utils//ai/, single-process code back into main/renderer (Invariant 1.1). Confirm each item's real consumer process rather than trusting the directional plan (API_SERVER_DEFAULTS is renderer-only, MIN_WINDOW_* is cross-process, providers.ts is renderer-only), and drop dead consts (ZOOM_LEVELS/ZOOM_OPTIONS, bookExts, thirdPartyApplicationExts). Purge runtime logic from types/ so the bucket holds only declarations: move serializeError + AI-SDK error guards to utils/error.ts, the FileHandle factories/guards to utils/file/handle.ts, isSerializable + SerializableSchema to utils/serializable.ts, and the tab-instance guard/normalizer to utils/tabInstanceMetadata.ts. Tests follow the logic to utils/__tests__; the type-level ipc contract test is retired (its invariants kept as a breadcrumb for the future IpcApi Zod schema). types/ is now logic-free and test-free. Also: remove the dead @shared mock from the packages/ui code-editor test so packages/ui no longer references production code; fix the data-classify preference generator prompts import and the update-languages output path to the relocated modules. Update shared-layer-architecture (3.1 type/util test rule, 5/6 config dissolution) and renderer-architecture cross-references. |
||
|
|
f6c74a40d1 |
chore(data-classify): retire non-generate scripts and commands
The data-classification extract/validate/check workflow has completed its staged mission; only the code-generation pipeline remains in use. - package.json: replace extract/validate/validate:gen/check:duplicates/all with an echo notice; keep generate and generate:* intact - rename the four retired scripts with a DO-NOT-USE- prefix (git mv) - README: document the deprecation via a global notice, a status column on the command table, renamed file references, and historical-section markers |
||
|
|
53a3577389 |
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.
|
||
|
|
c514dcc049 |
refactor(shared): move packages/shared to src/shared
packages/shared was never a real pnpm workspace package (no package.json); it was referenced only through the @shared TypeScript path alias. Relocate it under src/ via git mv (143 files, detected as pure renames).
Repoint the @shared alias and include globs to src/shared across electron.vite.config.ts, tsconfig.{json,node,web}.json and vitest.config.ts; update scripts/check-custom-exts.ts, scripts/update-languages.ts, the eslint.config.mjs generated-file globs, the data-classify generator output targets, .github/CODEOWNERS path rules, and CLAUDE.md/docs/source-comment references.
The @shared alias name is unchanged, so all 1403 @shared/* import sites resolve without modification. Verified with typecheck:node, typecheck:web and the full test suite (700 files, 9739 tests passing).
|
||
|
|
3a508c987b |
refactor(miniapp): migrate to v2 data layer (#14049)
### What this PR does
Migrates the MiniApp feature from v1 (Redux + sidecar
`custom-minapps.json`) to the v2 data architecture (DataApi + Preference
+ Cache), and integrates it into the v2 AppShell tab system.
**Before this PR**
- App lists lived in three Redux arrays (`enabled` / `disabled` /
`pinned`); custom-app logos were stripped before persistence and
recovered at runtime from `{userData}/Data/Files/custom-minapps.json`.
- Settings (`region`, `max_keep_alive`, `open_link_external`,
`show_opened_in_sidebar`) lived in legacy redux/electron-store.
- Runtime keep-alive used a module-level `lru-cache` singleton, mirrored
into v2 cache via `onInsert` / `disposeAfter` (two sources of truth —
already a known race).
- Routes were `/app/minapp/*`; sidebar icon literal was `'minapp'`.
- Sidebar mode used the legacy popup container; top-navbar mode was
non-functional.
**After this PR**
- A single `mini_app` SQLite table owns every row (preset + custom).
Preset rows are seeded by `MiniAppSeeder` from `PRESETS_MINI_APPS` on
every boot; custom rows come in via `POST /mini-apps`. The seeder uses
`setWhere isNotNull(presetMiniappId)` so refreshing preset display
fields can never overwrite a custom row whose `appId` happens to collide
with a preset.
- `MiniAppMigrator` imports v1 Redux state and reads
`custom-minapps.json` (path resolved through
`MigrationPaths.customMiniAppsFile`) to recover stripped logos.
- Settings live under typed Preference keys
(`feature.mini_app.{region,max_keep_alive,open_link_external}`); sidebar
icon literal renamed `'minapp'` → `'mini_app'` with a complex preference
transform that rewrites existing user arrays in-place.
- API: `GET/POST/PATCH/DELETE /mini-apps` + `POST
/mini-apps/order:batch`, Zod-validated, fractional-indexing ordering
scoped by `status` (cross-status batches are rejected with
`VALIDATION_ERROR` per the data-ordering-guide contract). Status
transitions reassign `orderKey` to the tail of the target partition
inside a transaction.
- Renderer hook `useMiniApps` exposes **command-style** writes only:
`updateAppStatus(id, status)` and `setAppStatusBulk([{id, status}])`.
The legacy declarative `updateMiniApps(list)` /
`updateDisabledMiniApps(list)` / `updatePinnedMiniApps(list)` are gone —
they took region-filtered subsets and silently disabled rows the caller
never saw.
- Keep-alive list is stored solely in
`useCache('mini_app.opened_keep_alive')`. Cap eviction respects AppShell
pin status: `useMiniAppPopup` reads pinned mini-app routes from
`useTabs` and skips them in eviction. `MiniAppTabsPool` renders webviews
in a stable `appId`-sorted order so LRU reorders never move `<webview>`
DOM nodes (Electron `<webview>` loses its guest WebContents on
detach/reattach).
- **Unified launch path**: clicking any miniapp (from the launcher grid
or a top tab bar entry) calls `openTab('/app/mini-app/<id>', { title,
icon: app.logo })`. A globally-mounted `<MiniAppTabsPool>` in `AppShell`
keeps a `<webview>` alive per opened app, regardless of sidebar vs
top-navbar layout.
- Settings UI rewritten as a `PageSidePanel` drawer composed of
`MiniAppListPair` (visible / hidden columns with drag-drop) and
`MiniAppDisplaySettings` (region / cache slider). New custom-app form is
a separate `NewMiniAppPanel` drawer.
- Sidebar's running-mini-apps strip removed — opened apps live
exclusively in the top tab bar (per #3198804265). Companion preference
`feature.mini_app.show_opened_in_sidebar` deleted from the schema.
### Why we need it and why it was done in this way
Part of the broader v2 data-layer migration (Redux/Dexie/ElectronStore →
DataApi + Preference + Cache).
**Architecture**
- DataApi for entity rows (preserves user content); Preference for
atomic settings; Cache (Memory tier) for runtime ephemera.
- Layered preset pattern (`best-practice-layered-preset-pattern.md`):
preset and custom rows share the same table, discriminated by
`presetMiniappId`. Seeder refreshes preset display fields on re-run;
custom rows are immutable to the seeder.
- Region filtering is a **view-only** concern (read path); the write
path is command-style and never references region. This eliminated a
class of bugs where editing the visible (filtered) list caused
region-hidden rows to drift.
- AppShell tab pinning is the canonical "keep this loaded" signal. The
keep-alive cap respects it; pinned mini-app tabs never get evicted
regardless of cap. Render-order independence in `MiniAppTabsPool`
ensures LRU touches don't move `<webview>` nodes around.
- Per-app icon resolution: `app.logo` is a `CompoundIcon` id (e.g.
`"Moonshot"`) for presets and a URL for custom apps. UI consumers (tab
bar, sidebar entry, settings list) call `getMiniAppsLogo` to resolve the
id to a `CompoundIcon` before rendering, with `<img>` fallback for URL
strings.
- Per-entity tab icons are cleared on internal navigation, sidebar
reuse, and the top-bar settings button — three call sites that all flip
the active tab's URL now consistently reset `icon: undefined` so a
mini-app logo never sticks onto an unrelated route.
**Tradeoffs**
- `useMiniApps` still exposes `miniapps` (region-filtered
enabled+pinned) and `disabled` (region-filtered). These are display-only
views. Renamed/typed wrappers were considered but deferred — the
refactor to command-style writes already eliminated the bug class that
motivated the rename.
- The `applyReorderedList` integration test for
`reorderMiniAppsByStatus` was dropped — `MockUseDataApiUtils` doesn't
fill the SWR cache that `useReorder.readCurrent` reads. Splice logic is
straightforward and the server-side `applyScopedMoves` test covers the
contract.
- Sidebar primitives in `@cherrystudio/ui`-adjacent layout still accept
`miniAppTabs` / `onMiniAppTabClick` props (defensive defaults — render
nothing without a producer). Removing these from the primitive's API is
a separate refactor not in scope.
### Breaking changes
User-visible changes are auto-migrated by the v2 migration framework —
no manual user action required:
- Sidebar icon literal `'minapp'` → `'mini_app'` (rewritten by the
`sidebar_icons_rename` complex preference transform)
- Preference key rename `feature.minapp.*` → `feature.mini_app.*`
(auto-migrated via `classification.json`)
- Custom-app logos stripped from v1 Redux are recovered from
`custom-minapps.json` during migration
One product-shape change is documented under
`v2-refactor-temp/docs/breaking-changes/`:
- `2026-05-07-miniapp-sidebar-running-list-removed.md` — the sidebar no
longer surfaces opened mini-apps under the mini-app entry. Open apps are
accessed exclusively via the top tab bar; pin a tab to keep its state
across switches.
The legacy v1 preference `showOpenedMinappsInSidebar` is reclassified as
`status: deleted` in the migration pipeline; v1 values are dropped
during v1→v2 migration with no v2 destination.
### Special notes for your reviewer
**Verified end-to-end on a real dev profile**: v1 Redux state +
`custom-minapps.json` → v2 SQLite, including pinned-app cross-group
dedup (a v1 pinned app appears in both `pinned` and `enabled` Redux
arrays; the migrator counts duplicates as skipped so the engine's
`targetCount >= sourceCount - skippedCount` invariant holds — without
this, any user with pinned miniapps was blocked from migrating).
**Drizzle migrations** are throwaway in dev per `CLAUDE.md`.
`migrations/sqlite-drizzle/0020_even_hulk.sql` is the single regenerated
migration; it will be wiped to a clean initial migration before release.
**Review history**: 28 line-comments across multiple formal review
rounds. All resolved. The most consequential fixes:
- `applyScopedMoves` in `MiniAppService.reorder` — rejects cross-status
batches with `VALIDATION_ERROR` instead of silently splitting them.
- `update()` reassigns `orderKey` to a fresh tail in the target
partition on status change.
- Empty-string substitution in migrator mappings is now caught by the
post-transform validity check; bad rows are skipped + warned, never
inserted.
- Migrator validation switched from `limit(5)` sample to full `count(*)
WHERE empty-fields` — bad rows can no longer pass validation by virtue
of being beyond the sample window.
- Keep-alive cap exempts pinned tabs (#3198809321 + the kangfenmao
keepalive review); render order in `MiniAppTabsPool` is `appId`-stable
so LRU touches don't move `<webview>` nodes (this was the root cause of
"switching tabs reloads the webview").
**Out of scope**:
- The remaining `@renderer/store/tabs` import in
`PaintingsRoutePage.tsx` is pre-existing v1 residual (not introduced or
touched by this PR).
### Checklist
- [x] PR: description rewritten to reflect the final architecture +
integration with the AppShell tab system
- [x] Code: command-style writes (`updateAppStatus` /
`setAppStatusBulk`); see `useMiniApps`, `MiniAppService`,
`MiniAppMigrator`, `MiniAppTabsPool`, `useMiniAppPopup` for the main
entry points
- [x] Refactor: ~1500 lines of dead/legacy code removed
(`Tab/TabContainer`, `TabsService`, `MiniAppPopupContainer`,
`TopViewMiniAppContainer`, legacy LRU singleton, `PinnedMiniApps`, dead
`userOverrides` / `MiniAppRegistryService`, unused `Signal.ts`)
- [x] Upgrade: v1 → v2 migration verified end-to-end on a real dev
instance
- [x] Documentation: architecture covered by `docs/references/data/`;
one user-visible behavior change documented in
`v2-refactor-temp/docs/breaking-changes/`
- [x] Self-review: multi-agent review via `/gh-pr-review` (twice); all
28 review comments resolved
### Release note
```release-note
NONE - Internal v2 data refactor. User-facing renames (route, sidebar icon, preference keys) are auto-migrated. The sidebar no longer shows a running-mini-apps strip; opened apps live in the top tab bar.
```
---------
Signed-off-by: suyao <sy20010504@gmail.com>
Signed-off-by: chengcheng84 <hello_world0000@outlook.com>
Co-authored-by: suyao <sy20010504@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
|
||
|
|
a4309aad61 |
feat(migration): migrate llm model preferences to v2 with UniqueModelId format (#14229)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com> |
||
|
|
ea99c82115 |
refactor: remove memory module from v2 (#14251)
Co-authored-by: SuYao <sy20010504@gmail.com> Fixes #14250 |
||
|
|
44b5edce54 |
feat(preboot): add core/preboot subsystem for userData location
Introduce a new `src/main/core/preboot/` subsystem that owns the synchronous setup happening before `application.bootstrap()` runs. This is the follow-up promised by |
||
|
|
c17dd033fc |
feat(migration): migrate v1 ~/.cherrystudio/config/config.json into BootConfig
Add a 'configfile' source kind to BootConfigMigrator so the v1 home config
file's `appDataPath` is migrated into a new BootConfig key
`app.user_data_path: Record<string, string>`, keyed by executable path.
v1's `~/.cherrystudio/config/config.json` is conceptually "early-boot
configuration that must be readable before userData is decided" — the same
category as BootConfig. v1 `config/config.json` will eventually be replaced
entirely by BootConfig; this PR establishes the data migration path, and a
follow-up will rewire `initAppDataDir()` to read from BootConfig instead.
Key pieces:
- New `LegacyHomeConfigReader` — sync fs read, normalizes both the legacy
string and the array-of-{executablePath,dataPath} v1 formats into a
`Record<string, string>`. Returns `null` (not `{}`) for "no data" so the
migrator's null-skip guard distinguishes "missing" from "empty record"
and doesn't write a spurious empty entry.
- BootConfigMigrator: new 'configfile' branch in `prepare()`; inline
`configFileMappings` local const with `targetKey: BootConfigKey` type
annotation (regen safety net — schema loses `app.user_data_path` →
compile error at the declaration site). `defaultValue: null` on
config-file items so "no v1 file" skips the item rather than falling
back to the schema default `{}`.
- Type tightening: `MigrationItem.targetKey` and `PreparedData.targetKey`
narrowed from `string` to `BootConfigKey`; all 6 `as BootConfigKey`
assertions deleted. This removes a silent "regen drops the key" failure
mode where the assertions previously suppressed compile errors.
- Generator updates: `generate-boot-config.js` gains a
`MANUAL_BOOT_CONFIG_ITEMS` list that flows through the same sort/emit
pipeline as classification-derived items, keeping `bootConfigSchemas.ts`
as a fully auto-generated single-interface file (no manual sections, no
declaration merging, no preservation logic). `generate-migration.js`
tightens the 3 BootConfig mapping arrays to `targetKey: BootConfigKey`.
- Tests: new `LegacyHomeConfigReader.test.ts` covers 9 scenarios (7 null
returns + 2 populated records); new `BootConfigMigrator.test.ts` covers
the configfile source end-to-end plus a redux-source regression.
- Docs: per-migrator `README-BootConfigMigrator.md` created per the
convention in `v2-migration-guide.md`. `v2-migration-guide.md`,
`boot-config-overview.md`, `boot-config-schema-guide.md`, and
`src/main/data/migration/v2/README.md` updated to mention
BootConfigMigrator, `LegacyHomeConfigReader`, and the 'configfile'
source kind. The AppImage/Windows-portable `executablePath`
normalization gap is documented as a known limitation that must be
addressed in the follow-up read-side PR.
Explicitly out of scope:
- Rewiring `initAppDataDir()` / `updateAppDataConfig()` to read/write
BootConfig — the v1 home config file keeps working unchanged.
- Adding a 'configfile' category to classification.json — the
data-classify toolchain isn't modelled for file sources yet.
- Deleting the v1 home config file post-migration — left intact.
Verified end-to-end on a real v1 config.json: the migrator produced
`"app.user_data_path": { [executablePath]: dataPath }` in
`~/.cherrystudio/boot-config.json`, verbatim-preserving the v1 data.
Signed-off-by: fullex <0xfullex@gmail.com>
|
||
|
|
c11967166a |
docs: update CLAUDE.md and data-classify toolchain for BootConfig
- CLAUDE.md: add Data Classification Toolchain section with generate command - README.md: add bootConfig category, generate-boot-config.js script, update directory structure, dependency graph, script tables, and decision flowchart - generate-all.js: include boot config generator as step 2/3 - package.json: add generate:boot-config script Signed-off-by: fullex <0xfullex@gmail.com> |
||
|
|
c5544c8a6e |
feat: wire BootConfig across renderer, preload, migration and UI
Complete the BootConfig integration across all layers: - Preload: update IPC bridge types to UnifiedPreferenceType - Renderer PreferenceService: use getDefaultValue for BootConfig fallback - usePreference hook: unified default value lookup - GeneralSettings: consume hardware acceleration via usePreference - BootConfigMigrator: migrate legacy Redux/ElectronStore values - BootConfigMappings: define source-to-target field mappings - Migration i18n: add migrated_boot_config progress text - Code generator: add generate-boot-config.js script - Update classification.json and preference schemas Signed-off-by: fullex <0xfullex@gmail.com> |
||
|
|
3c90efcc56 |
refactor(data): migrate translate data to v2 cache/preference/dataapi architecture (#13264)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: suyao <sy20010504@gmail.com> |
||
|
|
2e71e93b6c |
feat(migration): add Dexie settings unified data source and auto-generation (#13269)
### What this PR does Before this PR: The v2 migration system only supports ElectronStore and Redux as data sources for preference migration. Dexie IndexedDB `settings` table (a simple KV store used by the renderer for translate settings, pinned models, etc.) has no migration path. After this PR: - Adds `DexieSettingsReader` that pre-loads the Dexie `settings` table into memory for synchronous KV access, consistent with `ElectronStoreReader` and `ReduxStateReader` - Extends `MigrationContext` with a `dexieSettings` data source (makes `createMigrationContext` async to pre-load the table) - `PreferencesMigrator` now reads from `'dexie-settings'` source for both simple and complex mappings - `DEXIE_SETTINGS_MAPPINGS` is auto-generated into `PreferencesMappings.ts` from `classification.json`'s new `dexieSettings` section - All generator scripts (`generate-migration.js`, `generate-preferences.js`, `classificationUtils.js`) support `dexieSettings` as a data source with priority-based deduplication ### Why we need it and why it was done in this way The following tradeoffs were made: - `DEXIE_SETTINGS_MAPPINGS` is integrated into `PreferencesMappings.ts` (not a separate file) to keep all preference mapping exports in one auto-generated file and simplify imports. - `dexieSettings` priority is set to 2 (same as `localStorage`, below `redux` at 3). If the same `targetKey` exists in both `redux` and `dexieSettings`, redux wins — this is intentional since redux data is typically more up-to-date. - Dynamic keys (`image://`, `mcp:provider:*`) are explicitly excluded from this path. They will be handled by dedicated migrators in future PRs. The following alternatives were considered: - Generating a separate `DexieSettingsMappings.ts` file — rejected in favor of consolidating all mappings into one file for simpler imports and unified statistics. Links to places where the discussion took place: https://github.com/CherryHQ/cherry-studio/issues/10162 ### Breaking changes None. ### Special notes for your reviewer - The `classification.json` `dexieSettings.settings` array is currently empty (skeleton only). Actual key classifications will be added in follow-up PRs. - The architecture supports dynamic keys via `DexieSettingsReader.keys()` prefix filtering, but dynamic key migrators are out of scope for this PR. - Source identifier in `PreferencesMigrator` uses `'dexie-settings'` (with hyphen) to distinguish from the Dexie export reader (`dexieExport`) used by `ChatMigrator`. ### Checklist - [x] PR: The PR description is expressive enough and will help future contributors - [x] Code: [Write code that humans can understand](https://en.wikiquote.org/wiki/Martin_Fowler#code-for-humans) and [Keep it simple](https://en.wikipedia.org/wiki/KISS_principle) - [x] Refactor: You have [left the code cleaner than you found it (Boy Scout Rule)](https://learning.oreilly.com/library/view/97-things-every/9780596809515/ch08.html) - [ ] Upgrade: Impact of this change on upgrade flows was considered and addressed if required - [ ] Documentation: A [user-guide update](https://docs.cherry-ai.com) was considered and is present (link) or not required. Check this only when the PR introduces or changes a user-facing feature or behavior. - [ ] Self-review: I have reviewed my own code (e.g., via [`/gh-pr-review`](/.claude/skills/gh-pr-review/SKILL.md), `gh pr diff`, or GitHub UI) before requesting review from others ### Release note ```release-note NONE ``` --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
3165663340 |
feat(preferences): add 'useSystemTitleBar' setting and update generated files
- Introduced 'app.use_system_title_bar' preference in preferenceSchemas and DefaultPreferences. - Updated PreferenceMappings to include mapping for 'useSystemTitleBar'. - Adjusted classification.json and inventory.json to reflect new preference and metadata changes. - Regenerated auto-generated files with updated timestamps and statistics. |
||
|
|
73d45553be |
feat(migration): add complex preference transformation support
Add infrastructure for complex preference migrations that support: - Object splitting (1→N): One source object splits into multiple keys - Multi-source merging (N→1): Multiple sources merge into targets - Value calculation/transformation with pure functions - Conditional mapping based on source values New files: - ComplexPreferenceMappings.ts: Mapping definitions and interfaces - PreferenceTransformers.ts: Utility functions for transformations - target-key-definitions.json: Target key override definitions - Tests for both modules (43 tests total) Modified: - PreferencesMigrator.ts: Integrated complex mapping processing with strict conflict detection - generate-preferences.js: Merge classification.json with target-key-definitions.json - README.md: Updated documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
||
|
|
806a294508 |
feat: add v2-refactor-temp directory for V2 refactoring tools
- Add data-classify tools for data inventory extraction and code generation - Include consolidated Chinese documentation (README.md) - Update generated file path references This temporary directory will be removed after V2 refactor is complete. |