Commit Graph

16 Commits

Author SHA1 Message Date
fullex
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.
2026-06-19 20:41:18 -07:00
fullex
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
2026-06-16 05:15:47 -07:00
fullex
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.
2026-05-28 21:40:20 -07:00
fullex
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).
2026-05-28 21:02:49 -07:00
hello_world
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>
2026-05-07 20:45:20 +08:00
SuYao
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>
2026-04-17 18:07:15 +08:00
亢奋猫
ea99c82115 refactor: remove memory module from v2 (#14251)
Co-authored-by: SuYao <sy20010504@gmail.com>
Fixes #14250
2026-04-16 13:22:30 +08:00
fullex
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 c17dd033f to rewire v1's `initAppDataDir()`
to read from BootConfig instead of `~/.cherrystudio/config/config.json`.

## Startup phase vocabulary

The v2 main process now has three clearly-named startup phases, with the
definitions written into `src/main/core/README.md` as the authoritative
source:

  - preboot  — synchronous setup before the lifecycle container is
               built. BootConfig load, userData resolution, top-level
               Electron APIs. No DI, no services. Owned by core/preboot/.
  - bootstrap — the `application.bootstrap()` orchestration function.
                Freezes the path registry, runs Background/BeforeReady/
                WhenReady stages. NestJS/Spring-style term, the only
                meaning of "bootstrap" in v2 from this PR onward.
  - running  — steady state after bootstrap() returns.

v1's `src/main/bootstrap.ts` used "bootstrap" in the OS "early-boot
loader" sense, which clashes with the NestJS-style meaning v2's lifecycle
code has been using. "preboot" disambiguates without touching v2's
bootstrap terminology.

## userData location resolution

`resolveUserDataLocation()` is a single entry point that:

  1. Applies any pending relocation (see below).
  2. Reads `BootConfig.app.user_data_path[exe]` and calls setPath if
     valid.
  3. Falls back to portable-mode `<PORTABLE_EXECUTABLE_DIR>/data`.
  4. Otherwise leaves Electron's default userData.

Step 2 mirrors v1's `getAppDataPathFromConfig()` path-validity check
(existsSync + accessSync W_OK). Portable fallback mirrors v1's branch
character-for-character. Only the data source changes: BootConfig, not
v1 config.json. v1→v2 data migration is handled separately by
BootConfigMigrator (from c17dd033f).

## Pending relocation (new)

A new BootConfig field `temp.user_data_relocation` under a new top-level
`temp.*` namespace holds in-flight userData relocation state:

    { status: 'pending', from, to }
    { status: 'failed',  from, to, error, failedAt }
    null

The `temp.*` namespace is reserved for ephemeral runtime state that
should never be backed up or synced (restoring a stale temp.* entry
could re-execute a relocation that already happened).

When a pending request is present, `executePendingRelocation()`
synchronously copies the entire `from` tree to `to` with
`fs.cpSync({ recursive: true, force: true, verbatimSymlinks: true })`,
then atomically commits the new location to BootConfig
(`app.user_data_path[exe] = to` + clear `temp.user_data_relocation` +
flush via temp file + rename).

Pre-flight checks fail fast before any bytes are copied:
  - from !== to
  - to is not inside from (with path.sep guard against sibling-prefix
    false positives like /a vs /ab)
  - from exists
  - path.dirname(to) exists and is writable

On failure, the error is recorded as a `failed` state in BootConfig and
the function returns normally, letting Step 2 fall through to the
existing user_data_path. The `failed` record is the entry point for a
future renderer recovery UI (retry/abandon/investigate); this PR does
not implement the UI.

`failed` states are intentionally NOT auto-retried — that would turn a
one-off environmental failure into an infinite loop.

## v1 two-phase copy abandoned

v1's `copyOccupiedDirsInMainProcess()` worked around Windows file locks
on `occupiedDirs` (logs, Network, Partitions/webview/Network) by
splitting the copy into two phases (renderer copies the bulk, main
copies the locked dirs during the narrow "no renderer yet" window).

v2 abandons that distinction entirely: the previous process has fully
exited before preboot runs, so nothing is locked, and the entire
userData tree is copied as a single opaque unit. `occupiedDirs` has no
meaning in v2 and is deprecated in `packages/shared/config/constant.ts`
— it is kept on disk only because two v1-era call sites (bootstrap.ts
and BasicDataSettings.tsx) still reference it during the transition.

## Deprecations (code unchanged)

Two v1 files have their file-header @deprecated comments strengthened
but their code bodies untouched:

  - src/main/bootstrap.ts — no longer imported anywhere; the file stays
    on disk as reference for the follow-up cleanup PR.
  - src/main/utils/init.ts — initAppDataDir is dead code;
    updateAppDataConfig is still called by the v1 IPC handler at
    src/main/ipc.ts:300 (out of scope for this PR).

## Known limitation (pre-existing)

LoggerService opens its winston file transport at module-load time
pointing at `app.getPath('logs')`, which on Windows/Linux resolves to
`<userData>/logs`. This happens BEFORE preboot runs, so the winston
file handle is bound to the pre-setPath path. After preboot calls
setPath('userData', <new>), winston keeps writing to the old handle.
Post-relocation logs therefore appear at the old location until the
next restart. This is a pre-existing v2 issue (LoggerService doesn't
respond to setPath) and is documented in executePendingRelocation's
doc comment; a follow-up should either lazy-open winston transports
or teach LoggerService to rotate on userData change.

## Out of scope (follow-up cleanup PR)

  - App_SetAppDataPath IPC handler migration to write BootConfig
    pending_relocation instead of v1 argv protocol
  - BasicDataSettings.tsx renderer flow migration
  - src/main/bootstrap.ts and src/main/utils/init.ts deletion
  - occupiedDirs constant deletion
  - LoggerService setPath responsiveness

## Tests

25 unit tests in userDataLocation.test.ts covering:
  - getNormalizedExecutablePath (5 platform branches)
  - resolveUserDataLocation normal resolution (9 scenarios including
    AppImage/portable normalized key matching)
  - resolveUserDataLocation pending relocation (11 scenarios including
    pre-flight failure cases and the sibling-prefix regression guard)

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-08 01:03:45 -07:00
fullex
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>
2026-04-07 09:20:23 -07:00
fullex
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>
2026-03-25 09:50:13 -07:00
fullex
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>
2026-03-25 09:36:03 -07:00
Phantom
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>
2026-03-23 12:24:29 +08:00
Phantom
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>
2026-03-13 15:20:34 +08:00
fullex
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.
2026-01-23 18:32:56 +08:00
fullex
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>
2026-01-18 23:10:52 +08:00
fullex
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.
2025-11-29 11:55:45 +08:00