Commit Graph

4 Commits

Author SHA1 Message Date
Phantom
4ef2889fd3 refactor(file-ref): split persistent ref ownership (#16532)
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
Signed-off-by: eurfelux <eurfelux@gmail.com>
2026-06-30 18:44:44 +08: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
SuYao
db8a1834c2 feat(file-manager): unified DirectoryTreeBuilder primitive for Notes (#15363)
### What this PR does

Before this PR:

- Directory loading is split between two pipelines:
`FileStorage.getDirectoryStructure` (Notes) and
`FileStorage.listDirectory` (everything else). Same workspace, two
scans, two watchers.
- `FileStorage.listDirectory` defaults `maxEntries: 20`, silently
truncating list-mode callers (workspace trees rendered `tests/__mocks__`
as a file because it sorted out of the first 20 results).
- Notes' manual chokidar plumbing (`startFileWatcher` / `onFileChange`)
lives on legacy `file-change` IPC and crashes with `EMFILE: too many
open files` on large repos because the watcher opens one FD per
directory in `node_modules`.

After this PR:

- One primitive — `DirectoryTreeBuilder`
(`src/main/file/tree/builder.ts`, RFC §12) — owns the in-memory tree and
the chokidar watcher. It is the only directory-walking code on the main
side.
- Builder dedupe lives behind `TreeRegistry` (lifecycle `WhenReady`):
identical `(rootPath, options)` requests share one builder, with a 500ms
grace window so a remount inside one React commit reuses the warm scan +
watcher instead of paying for a rescan.
- `.gitignore` parsing (via `ignore@7`) drives BOTH ripgrep's
`--ignore-file` AND chokidar's `ignored` predicate, so EMFILE on
`node_modules`-heavy workspaces is gone. `.git` is always excluded even
when `.gitignore` doesn't list it.
- `search.listDirectory` is the now-real Phase 2 entry point (was a
stub). `maxEntries` default raised to `Number.MAX_SAFE_INTEGER`;
truncation is a search-mode concern and callers that want a cap pass it
explicitly. The 20-entry default was the bug.
- Renderer-side `useDirectoryTree(rootPath, options)` hook applies
mutation pushes to a JSON-mirrored `TreeDirRoot`. The tree DTO and
`TreeNode` class live in `packages/shared/file/types/tree.ts` so main
and renderer share one shape (no parent cycles in JSON; WeakMap parent
ref recovered in `FromJSON`).
- Notes (`NotesPage`) migrated off `getDirectoryStructure` + manual
watcher onto `useDirectoryTree` + `useNote` join. The Redux
`starredPaths` / `expandedPaths` path stays untouched in this PR —
`useNote` already covers that surface via `noteTable`.

Fixes #

### Why we need it and why it was done in this way

The following tradeoffs were made:

- **Builder dedupe on main, not renderer.** A renderer-side cache would
still pay one IPC round-trip per remount because the expensive thing is
the FS scan + watcher install, which only the main side owns. Sharing on
the main means one ripgrep + one chokidar regardless of how many panes
mount the same root.
- **500ms grace window for builder teardown.** Long enough to cover
React's "deletions before insertions" effect ordering (sub-millisecond
in practice), short enough that closing a workspace doesn't keep watcher
FDs alive noticeably.
- **`Tree_*` IPC owned by `TreeRegistry.onInit`.** The lifetime of the
handlers must match the lifetime of the chokidar watchers they
reference. Putting them in `FileManager` would leak handlers across
re-init.
- **`TreeNode` class hierarchy (not plain DTOs)** lets `rename` mutate
`path` once at the subtree root and have `adjustChildrenPaths` cascade —
the alternative is rebuilding the subtree, which throws away every
consumer's identity-based caching.
- **Hard ESLint isolation:** `src/main/file/tree/**` does not import
`@main/data/**`. Tree is a runtime concern; persistence is an orthogonal
one (`noteTable` is a sparse state overlay on top of FS paths, not a
tree mirror). RFC §12.6.

The following alternatives were considered:

- Keep the renderer-side cache (rejected — covered above; main-side
dedupe is strictly stronger).
- Add an `excludeGlobs` array option (rejected — the right source of
truth is `.gitignore`, and users editing `.gitignore` get free updates).
- One DTO without classes (rejected — rename cascade is the load-bearing
case).

Links to places where the discussion took place: RFC §12
(`v2-refactor-temp/docs/file-manager/rfc-file-manager.md`)

### Breaking changes

- Removed legacy IPC channels: `File_GetDirectoryStructure`,
`File_StartWatcher`, `File_StopWatcher`, `File_PauseWatcher`,
`File_ResumeWatcher`. Only Notes used them; the migration is in this PR.
No renderer outside Notes touched them.
- `search.listDirectory` (renamed from `FileStorage.listDirectory`) now
defaults to unbounded `maxEntries`. Existing callers that relied on the
implicit 20-cap (none I could find) would need to pass `maxEntries: 20`
explicitly.

### Special notes for your reviewer

- `src/main/file/tree/__tests__/builder.test.ts` covers initial scan,
`.gitignore` honoring, chokidar fan-out, `dispose` cleanup, and JSON
round-trip (no parent cycles).
- `src/main/file/tree/__tests__/registry.test.ts` covers builder dedupe,
grace-window reuse, multi-consumer mutation fan-out, and
`webContents`-destroyed cascade cleanup.
- `src/main/utils/file/__tests__/search.test.ts` locks in the
`maxEntries` default fix and the `--hidden` flag wiring.
- ArtifactPane / chat-page integration of this primitive lives on
`feat/chat-page` and is not part of this PR (that branch has the Shell
forceMount + chat-page Tree IPC integration on top).

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: Write code that humans can understand and Keep it simple
- [x] Refactor: You have left the code cleaner than you found it (Boy
Scout Rule)
- [ ] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: A user-guide update was considered and is present
(link) or not required.
- [x] Self-review: I have reviewed my own code before requesting review
from others

### Release note

```release-note
NONE
```

---------

Signed-off-by: suyao <sy20010504@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:33:48 +08:00