Commit graph

69 commits

Author SHA1 Message Date
wujunchen
df48dc2cc4 fix: remove unsupported --max-tokens flag from claude CLI args
The Claude Code CLI doesn't support --max-tokens. This caused the CLI
backend to always fail with exit code 1. Also adds args validation tests
for both claude and codex CLI backends.

Change-Id: I57c5cc672ed08de291e3941511ce68ba56e5b4ff
2026-04-27 19:07:53 +08:00
wujunchen
1fff3cb343 fix: address CR findings — test runner, CI dedup, error cause
- Create scripts/run-tests.mjs to auto-discover test files, replacing
  the 900-char inline test command in package.json (P3-1)
- Add test:only script for CI to avoid duplicate build+typecheck (P2-1)
- Preserve original SyntaxError as cause in responseJson (P2-3)

Change-Id: I51d0c8c51d3b0b648ed5ff4ea282bda070b0d457
2026-04-27 16:04:03 +08:00
wujunchen
986143b05b refactor: split settings-tab renderBackendSection into CLI/API methods
Extracts renderCliBackendSettings() and renderApiBackendSettings() from
the 144-line renderBackendSection(). The parent method is now a 5-line
dispatcher. CLI settings (14 lines) and API settings (128 lines) are
cleanly separated.

Change-Id: Ic866ba78d3b572f5f564760b248460584b0819c5
2026-04-27 15:45:25 +08:00
wujunchen
4b7b8cccfc refactor: create test-exports.ts barrel to clean up main.ts
Replaces the 64-line __test manual export block in main.ts with a
single import from src/test-exports.ts. Also removes 30+ imports that
were only needed for __test re-export. main.ts reduced from 788 to
693 lines.

Change-Id: I029f127faf053135d04dba17b8c39c8c39ca8540
2026-04-27 15:43:58 +08:00
wujunchen
60e9ad6bb2 refactor: extract provider HTTP helpers to provider-request.ts
Moves RequestUrlFunction type, endpointUrl, responseJson, requestJsonBody,
shouldRetryWithoutStructuredOutput, and requestJsonBodyWithStructuredFallback
to a dedicated provider-request.ts module (103 lines). providers.ts reduced
from 377 to 277 lines.

Change-Id: I013d6a0b9565770c00e48eb416541fcf08d1c5e8
2026-04-27 15:41:28 +08:00
wujunchen
ed93e25cc9 refactor: split view.ts render() into 5 focused helper methods
Decomposes the 76-line render() method into renderHeader(),
renderStaleBanner(), renderLoadingState(), renderErrorState(), and
renderCardList(). Each helper is under 22 lines. render() is now a
15-line orchestrator that delegates to the appropriate helper.

Change-Id: Iffcf31131fcf0bce882c1e9ee35aaf58d002b5fa
2026-04-27 15:39:12 +08:00
wujunchen
62f99718f6 refactor: extract API_PROVIDER_PRESETS to provider-presets.ts
Moves 18 provider preset definitions (148 lines) from settings.ts into
a dedicated provider-presets.ts file. settings.ts reduced from 443 to
297 lines, now contains only utility functions and non-preset constants.

Change-Id: Ia9a1ef48854ab79cb405df9cd1f88d19826317d6
2026-04-27 15:15:23 +08:00
wujunchen
15b471efad refactor: extract i18n translation strings to dedicated i18n-strings.ts
Moves the STRINGS object (330 lines of zh/en translations) from i18n.ts
into a separate i18n-strings.ts file. i18n.ts now contains only the
resolveUiLanguage and translate logic (28 lines).

Change-Id: I9ddea79bddb4e8313dc16aae2a7c8d4eeff96fe1
2026-04-27 15:13:31 +08:00
fancivez
f4e0d84a9c refactor: replace magic numbers in scroll.ts with named constants
Add DEFAULT_MAX_OFFSET_PX (80), DEFAULT_PROBE_RATIO (0.1), and
FALLBACK_FRAME_MS (16) to document the purpose of each value.

Change-Id: Ie34d61c290384de81ae001f292e89f17b410e701
2026-04-27 14:11:37 +08:00
fancivez
bda21a0fb1 fix: replace unsafe (e as Error).message casts with instanceof guards
Fix 4 locations in providers.ts, cli.ts, and settings-tab.ts where error
objects were cast to Error without checking. Now uses proper instanceof
narrowing with String fallback for non-Error throwables.

Change-Id: Ib324380f5d56945672e32b9d3621855f6179186a
2026-04-27 14:11:37 +08:00
fancivez
5eb005f293 refactor: extract body builders and types to src/provider-bodies.ts
Move buildAnthropicMessagesBody, buildOpenAiChatBody, buildOpenAiResponsesBody,
buildGeminiBody, tokenLimitFieldForOpenAiChat and their type interfaces to a
new module. Reduces providers.ts from 494 to 367 lines (under 400 target).

Change-Id: Ib124d5cf5f2f66bac03acbf83e1a224cb6325d12
2026-04-27 14:11:37 +08:00
fancivez
0ef6395ed7 test: add parseApiHeaders unit tests covering all input formats
Export parseApiHeaders for testing. Cover: JSON object input, empty/whitespace,
line-based input, comments/blank lines, malformed JSON, array input fallback,
missing colon, non-string JSON values filtered, colons in values preserved.

Change-Id: Ia00898888c6a0b78a284b43f29e382cc4080ba88
2026-04-27 14:11:37 +08:00
fancivez
ff08dbe3c2 fix: eliminate unsafe as-unknown-as casts in scroll.ts
Replace fragile double-casts with a branded ScheduleId wrapper that
unifies RAF (returns number) and setTimeout (returns Timeout) IDs
without losing type safety. The wrapper is opaque to callers.

Change-Id: I304d68b562ff803d0b20ebcbfb71a3c3d9e1d881
2026-04-27 14:11:37 +08:00
fancivez
5f8b3cfd86 chore: upgrade biome noImplicitAnyLet to error, fix parsed type in schema
Set noImplicitAnyLet to error in biome.json. Fix the violation in
parseCardsJson by typing the parsed variable as unknown. Widen
normalizeCardsPayload to accept unknown (it already handles non-object
input defensively). Remove now-unnecessary cast in provider-parsers.ts.

Change-Id: I57ee46d3179775d5d7c34f35803a44c45061b0ec
2026-04-27 14:11:37 +08:00
fancivez
4f5e9e9923 refactor: extract card rendering and context menu from view.ts render()
Split the 160-line render() method into three focused methods:
- render(): orchestrates header, state, and card list (now ~60 lines)
- renderCard(): handles single card DOM creation with content and events
- showCardContextMenu(): builds the right-click context menu

Change-Id: I46f94e75d6aef1043ac905f1dc33c13cf4ae8447
2026-04-27 14:11:36 +08:00
fancivez
4c4468dd85 fix: add logging to silent catch blocks in CLI, job manager, and main
Replace silent error swallowing with console.warn in:
- GenerationJobManager cancel handler errors
- CLI process kill failures (timeout and cancellation)
- CLI stdin close failures
Add descriptive comments to intentionally silent scroll-related catches.

Change-Id: I2ec8fda6ca92186ed2d4fc1560efffd0677f3bcd
2026-04-27 14:11:36 +08:00
fancivez
ba6e44194a refactor: extract batch folder prompt dialog to src/batch.ts
Move the inline Modal dialog for batch folder selection into a reusable
promptForBatchFolder function. Removes Modal import from main.ts.
main.ts now 775 lines.

Change-Id: I7a5c62b7df95635dcbe0ac814007838ff83eded4
2026-04-27 14:11:36 +08:00
fancivez
50a3c4bec3 refactor: extract resolveCardAnchors and confirmRegenerateEditedCards from main.ts
Move resolveCardAnchors to src/cards.ts and confirmRegenerateEditedCards
to src/modal.ts to reduce main.ts from 835 to 796 lines (under 800 target).

Change-Id: If38ed4e45c61658b7cc52a6283f8e0e24458761e
2026-04-27 14:11:36 +08:00
wujunchen
bc1de37868 fix: remove unsafe fallback cast in CardEditModal constructor
The constructor always receives a validated ResolvedCard from the call
site (which guards with !this.sections[index]). The fallback to an
empty object cast as ResolvedCard was dead code that hid type safety.

Change-Id: I35c9731d9079b9aeea5eb0fda7eda46a740bc964
2026-04-27 12:41:13 +08:00
wujunchen
f635823499 fix: add error handling to fire-and-forget async patterns
Replace void-prefixed async calls with .catch() error logging in:
- onunload flush operations (settings + cache)
- file-open, rename, and delete event handlers
- settings-tab cache commit handler

Also await syncViewToFile in the open-view command where it was
unnecessarily fire-and-forget inside an already-async callback.

Change-Id: Ied1e88035b77a92a59c50ab6caef539a254c7787
2026-04-27 12:40:29 +08:00
wujunchen
1d3aa81678 fix: close abort signal race window in streaming fetch
The previous code checked signal.aborted before adding the listener,
creating a timing window where the signal could abort between the check
and addEventListener. Now the listener is always registered first, then
signal.aborted is checked after, ensuring no abort event is missed.

Change-Id: I1abc3ad21e5bb93559d66922edf7c325a977bc65
2026-04-27 12:22:43 +08:00
wujunchen
09f39dde8d fix: make normalizeSettings return a new object instead of mutating input
normalizeSettings previously modified its input parameter in-place.
While callers already created copies before calling it, the function
signature was misleading. Now it creates a shallow copy internally and
operates on that, with a Readonly<PluginSettings> parameter type to
signal intent. Added a test verifying the original object is unchanged.

Change-Id: I7600ac37930bbcebb02aa7c84d8988cb7aa71196
2026-04-27 12:20:56 +08:00
wujunchen
ff1116d7f4 fix: pass --max-tokens to Claude Code CLI to prevent output truncation
The CLI path was not forwarding settings.apiMaxTokens, causing the
model to use its own default which is too small for long documents
with many CJK cards. This was the root cause of repeated "LLM 返回
非 JSON" errors — the JSON was being cut off mid-card.

Change-Id: Ia738418e2e6cf7f919752cb3128a82768d95cedf
2026-04-27 11:58:04 +08:00
wujunchen
abce923f14 fix: salvage truncated LLM JSON and make errors copyable
- Add repairTruncatedCardsJson to recover complete cards when output
  is cut off mid-card (e.g. token limit reached)
- Log raw LLM response to console on parse failure for debugging
- Make error panel text selectable and add "copy error" button

Change-Id: I4e76121138888234d42f84f3695cbbfd6beba886
2026-04-27 11:56:40 +08:00
wujunchen
5f12113fcd chore: improve engineering quality and hardening
- Fix CI to run all 4 test files (was missing direct-modules.test.js)
- Remove unused ESLint dependencies (eslint, @typescript-eslint/*, eslint-plugin-obsidianmd)
- Enable noImplicitReturns in tsconfig.json for stricter type safety
- Make applyApiProviderPreset return new object instead of mutating input
- Add per-file error handling in batch processing loop with error count in summary
- Narrow all catch clause types to unknown with proper instanceof narrowing

Change-Id: I76975dbd5ebdb5645d18c5858c8a52fd6da3405e
2026-04-27 11:15:35 +08:00
wujunchen
6e83d216c2 feat: add generate button to empty state panel
When a note has no cached parallel notes, the empty state now shows a
clickable "Generate" button so users can trigger generation directly
without going through the command palette. Closes #2.

Change-Id: I879897168ef77c44e0ab570e9c094fa9af9f66d4
2026-04-27 10:24:05 +08:00
wujunchen
da535cb064 fix: address remaining Obsidian review bot required issues
- Remove unnecessary TFile cast (instanceof already narrows)
- Remove await on non-Promise view methods (loadFor, renderLoading, renderError)
- Remove async from CacheManager.touch() (no await expression)
- Replace this-aliasing with captured locals in batch modal
- Use CSS class instead of inline style for modal input width
- Fix sentence case in settings UI text

Change-Id: I2a222c6429eb7e4d69761cca8369956fef510054
2026-04-26 19:34:45 +08:00
wujunchen
3d8d72b66e refactor: split settings tab renderers
Change-Id: I456ddd16868150b2e22f5592c97d579a3540e48d
2026-04-26 19:17:12 +08:00
wujunchen
557908ca44 fix: clean up streaming abort listener
Change-Id: I9fa8ace7bc0fa723466ef851f7ec2960c6638eba
2026-04-26 18:29:48 +08:00
wujunchen
bc51acd00e fix: parse multiline sse events
Change-Id: I9c013dbe6840a0892836eb5d71206c6fcde5ec78
2026-04-26 18:27:28 +08:00
wujunchen
70f9a39cd7 refactor: extract provider response parsers
Change-Id: I6884999f34594faec349378deb59e68972e07355
2026-04-26 18:20:49 +08:00
wujunchen
6dd7ef15a4 test: cover cli edge cases
Change-Id: Ia6114ab7ba24e32f7f86ff76710a596b0c24682c
2026-04-26 18:16:38 +08:00
wujunchen
174fa7295d feat: expose streaming timeout setting
Change-Id: Ide69c0f0fe208a557024f9f77d83c9d1215391cf
2026-04-26 18:14:43 +08:00
wujunchen
9098de6884 feat: validate batch folder input
Change-Id: I137fa046e77781e063ee782af4321b7277d64d51
2026-04-26 18:12:38 +08:00
wujunchen
d8dc82362b feat: support cancellable batch generation
Change-Id: I89302f58995e4e15fbf77c20c87a94832e572d20
2026-04-26 18:10:17 +08:00
wujunchen
ebc882c2f1 refactor: encapsulate cache rename
Change-Id: I89ee56c071c8e5404f8eb9ea744172957811d714
2026-04-26 18:05:38 +08:00
wujunchen
2d4bef9539 refactor: extract batch generation helpers
Change-Id: I16ad657b783b65cc5b3e36a6ea163cc19740b651
2026-04-26 17:54:35 +08:00
wujunchen
b199f56a35 refactor: extract generation orchestration
Change-Id: I17002ff04b0d79d8ec27da3dbb4b08df9bf3659e
2026-04-26 17:45:43 +08:00
wujunchen
9ed36570b5 refactor: restore strict lint hygiene
Change-Id: I26917328ec55e529242dfd8e4c42af0b3fcd58b7
2026-04-26 17:41:28 +08:00
wujunchen
89d28d1113 fix: address all required issues from Obsidian plugin review bot
- Remove `parallel-reader-` prefix from command IDs (Obsidian auto-namespaces)
- Remove default hotkeys to avoid conflicts with user keybindings
- Replace `as TFile` casts with `instanceof TFile` checks
- Make onunload sync, remove detachLeavesOfType (preserves leaf positions)
- Use `vault.configDir` instead of hardcoded `.obsidian`
- Replace `window.confirm` with Obsidian Modal dialog
- Fix unhandled promises with `void` or `await`
- Remove unnecessary `async` from methods without `await`
- Use `new Setting().setHeading()` instead of raw HTML headings
- Fix promise-returning callbacks in event listeners
- Fix error type narrowing with `instanceof Error`
- Apply sentence case to UI text labels

Change-Id: I6e850d8a13b458d5cf1ad91227bf1772636b56b9
2026-04-26 15:57:16 +08:00
Claude
02fc5678ae
feat: add folder-level batch summarization command
Register parallel-reader-batch-generate command. It prompts the user for a
folder path via a Modal, finds all .md files in that folder (non-recursive),
skips files whose cache is still valid, and runs runForFile() sequentially
on the rest, showing per-file progress notices. Add i18n keys for the new
UI strings in both zh and en.

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:18:58 +00:00
Claude
10d811f197
refactor: upgrade Biome lint rules
Promote noExplicitAny from warn to error and enable noNonNullAssertion
at warn level. Fix the two resulting errors in src/schema.ts by introducing
a JsonSchema interface to replace the any-typed local variables.

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:16:36 +00:00
Claude
e7384fd875
feat: add streaming timeout protection
Add streamingTimeoutMs field to PluginSettings (default 120000ms). In
streamingFetch(), use Promise.race between the stream reader and a timeout
promise that aborts the AbortController and throws a clear timeout error.
External cancellation signals are forwarded to the same controller so only
one abort path is needed.

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:13:52 +00:00
Claude
dbb8ea4a5b
refactor: make cache entry updates immutable
touchCacheEntry() now returns a new object via spread instead of mutating
the entry in-place. CacheManager.replaceCards() likewise creates a new entry
object instead of mutating existing fields. Update tests to assert the new
immutable semantics (returned value differs from input by reference).

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:12:55 +00:00
Claude
64ebbaef36
refactor: replace any types in providers.ts with proper interfaces
Define RequestUrlFunction, AnthropicMessagesBody, OpenAiChatBody,
OpenAiResponsesBody, GeminiBody/GeminiGenerationConfig typed interfaces.
Replace all ~14 any usages with unknown + type narrowing or concrete types.
Drop the eslint-disable comments that were papering over the any usages.

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:10:28 +00:00
Claude
57d25501d6
refactor: add typed wrappers for Obsidian API boundaries
Add CmEditorView, ObsidianEditorWithCm, ObsidianMenuItem, and ObsidianMenu
interfaces to src/types.ts. Replace (editor as any).cm and (it: any) casts
in main.ts with these typed wrappers, eliminating all any casts in the
scroll-sync and file-menu code paths.

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:08:46 +00:00
Claude
87904811e2
refactor: extract CacheManager from main.ts
Move all cache persistence methods and state (_cacheSaveTimer, _cacheDirty)
into a new CacheManager class in src/cache-manager.ts. Plugin delegates to
it via thin wrappers that preserve the existing method names tested by the
test suite. main.ts reduced from 826 to 713 lines.

https://claude.ai/code/session_016QvEfqw6YZ3RjwBHrJ4w8S
2026-04-26 06:07:03 +00:00
wujunchen
38ba0acdb2 fix: parse Claude Code JSON array output and drop --model flag
Claude Code `--output-format json` returns a JSON array of event
objects, not a single object. Parse the array and extract the
result event. Also stop passing --model to let Claude Code use
its own default model. Revert timeout to 120s.

Change-Id: I5b5d4aa589ff78c0a2806a3e700bac0676a9b354
2026-04-26 11:09:47 +08:00
wujunchen
625ac4e5fe fix: raise CLI timeout from 120s to 300s
Change-Id: I1c6a66ed23c6908d6045042e57e50efce1910f9e
2026-04-26 11:08:13 +08:00
wujunchen
d60999d8df feat: raise default max input from 20K to 100K characters
Change-Id: Ie5b895eb1a4dba02bb84fd83adf1ef0216af067b
2026-04-26 10:23:42 +08:00