Commit graph

65 commits

Author SHA1 Message Date
wujunchen
33371cadd6 chore: integrate obsidianmd eslint plugin and fix recommended rules
Add eslint v9 + @typescript-eslint/parser + eslint-plugin-obsidianmd as
a dedicated `npm run lint:obsidian` track, separate from the existing
biome lint. Apply autofixes from the recommended config and resolve the
follow-on type/test issues.

- prefer-create-el: createEl('div'/'span') -> createDiv/createSpan
- prefer-active-window-timers: setTimeout/clearTimeout -> activeWindow.*
  + retype timer holders from ReturnType<typeof setTimeout> to number
- prefer-active-doc: drop globalThis.fetch in streaming
- no-useless-catch: drop no-op rethrow wrapper
- no-restricted-globals: streaming.ts is exempted because requestUrl
  does not support streaming responses
- @typescript-eslint/no-unsafe-*: disabled (out of scope for an Obsidian
  plugin lint pass; tsc + biome already cover type safety)
- tests: polyfill globalThis.activeWindow so unit tests still run in Node

Change-Id: I43cc652e29a66d490e2834e940843c39b211b80b
2026-05-03 10:45:10 +08:00
fancivez
a5d9d74b54 feat: toggle right sidebar instead of detaching tab + component tests
UX change:
- The "open Parallel Reader" command now toggles the right sidebar's
  collapsed state instead of detaching the leaf. The tab and its content
  are preserved across hide/show, matching standard Obsidian behaviour
  for File Explorer and friends. Detaching the tab on every second
  press destroyed cached cards and forced re-generation.

Behaviour:
- No leaf yet → create + reveal
- Leaf exists, right sidebar collapsed → reveal (expands sidebar)
- Leaf exists, right sidebar expanded → collapse(); tab is preserved
- No right sidebar (mobile / unusual layout) → reveal as fallback

Tests:
- New tests/view-render.test.js (18 cases) covering the component layer
  that was previously a gap. The 1.0.12 → 1.0.13 sidebar regression
  could not have been caught by any existing pure-function test; this
  file fills that gap by mocking app.workspace + view spies and running
  real runForFile / refreshViewAfterCache* / toggleParallelView.
- shouldRender matrix (4 cases): silentView × sourceFile state — the
  exact regression introduced in PR2 where guards were over-applied.
- toggleParallelView (4 cases): no-leaf opens, expanded → collapse,
  collapsed → reveal, no-rightSplit fallback.
- refreshViewAfterCacheDelete + Clear (4 cases): matching file vs
  different file vs no-view, plus clear-all always renderEmpty.
- runForFile outcome enum mapping (6 cases): already-running early
  return, already-running from catch, cancelled, generic error,
  regenerate-confirm cancellation, skipEditConfirm bypass — these
  drive batch statistics and were untested before.
- view.deleteCard / updateCard cardPersistFailed path (3 cases):
  exposes ParallelReaderView via test-exports for instantiation;
  mocks cacheReplaceCards returning false to verify the failure
  notice path lands and the public method returns false.

src/test-exports.ts: export ParallelReaderView for component tests.
tests/catalog.json: register view-render.test.js under component.

Change-Id: I439b5fca07b0928e6fe44d9025c2421c71c6cb28
2026-05-02 15:38:41 +08:00
fancivez
76d2e8050a fix: address 10 P2 behavioral and concurrency findings
Findings from the original review (P2):
- streaming: flush unterminated final SSE event at EOF (some providers
  close the stream without a trailing blank line, dropping the last delta)
- cache-manager: validate entry shape on load — drop entries where
  cards is not an array, anchor is not a string, or bullets is not an
  array. Tolerates missing optional fields (treated as cache miss).
- view: card edit/delete now check cacheReplaceCards return and surface
  failures via a localized Notice instead of pretending success
- main: clear-current / clear-all / file-menu-clear refresh open view
  via renderEmpty so stale UI does not display deleted data
- prompt + settings-tab: card count is normalized via a single helper
  used by buildPrompts and onChange. Prompt and fingerprint stay in sync.
  UI value is written back to the textbox after clamping.
- generation-job-manager: global concurrency limit (default 3) with a
  cancellable wait queue. Race-safe slot accounting via a reserved
  counter so resolved-but-not-yet-set waiters are visible to fast-path
  start() callers.
- runForFile: now returns RunForFileResult; accepts preloadedContent +
  silentView + skipEditConfirm options used by batch (and reflected on
  the PluginHost interface).
- batch: avoid double file read, do not steal UI focus, classify
  results correctly (generated / cached / already-running / empty / ...)

Follow-up from the 1.0.11 review:
- generationFingerprint: codex backend excludes settings.model from the
  hash. Codex ignores --model; previously editing model spuriously
  invalidated all codex cache.

Codex review pass:
- generation-job-manager: race fix — releaseSlot's resolve microtask
  and a synchronous start() fast-path could briefly overshoot
  maxConcurrent. Reserve the slot synchronously inside the wrapped
  resolve to close the window.
- cache-manager: anchor type validation in addition to bullets.

NOTE: Codex backend users will see a one-time "stale cache" banner on
existing notes due to the fingerprint change; regenerate to refresh.

Change-Id: I7721c7dfe51dea3f51b0215764f721523c2f6806
2026-05-02 13:52:49 +08:00
fancivez
01ec934d28 fix: address 13 review findings (security, UX, i18n)
P1:
- cli: pin codex --sandbox read-only (defeat prompt injection)
- settings: clear apiKey/apiHeaders on provider preset switch
- main/view: render error state when LLM returns empty cards
- schema/cli: do not log raw model output; UI errors expose length only

P2:
- cli: pass --model to Claude Code when settings.model set
- settings-tab: add CLI timeout input + normalizeCliTimeoutMs (min 1s)
- view: export source link uses [[path|basename]]
- view: export failures surface a localized Notice

P3:
- schema: cardUntitled fallback now respects uiLanguage
- generation-job-manager: error codes localized via main.ts
- batch: settled guard on promptForBatchFolder modal
- README: drop stale e2e validator install note

Codex review pass:
- provider-parsers: forward settings to normalizeCardsPayload
- generation-job-manager: classifyGenerationError regex covers new EN/zh
  schema-error messages
- cli: do NOT pass --model to codex (DEFAULT_SETTINGS.model is a Claude
  name and would break codex; codex relies on its own config)

Tests: cli (codex sandbox + claude --model + classify zh/en), settings
(applyApiProviderPreset isolation + normalizeCliTimeoutMs), schema
(language-aware fallback title), test-exports (new symbols).

Change-Id: I9f4c21f9299e1a9bf166f69f712859854482fe92
2026-05-02 12:50:35 +08:00
fancivez
418f733d14 ci(e2e): enforce self-contained release gate
Change-Id: I4a7385a861981223edf5b4a3f1861b349ef520bf
2026-04-30 14:45:56 +08:00
wujunchen
310c81a073 fix: add cancel action to batch prompt
Change-Id: Ic3922d55d776d369959eff4e5c811c5d3434db2b
2026-04-29 14:13:47 +08:00
wujunchen
3bdd6d67c6 fix: localize batch prompt confirmation
Change-Id: I13b546785c5984a9d0cb5abaffa843f9ad7655d0
2026-04-29 13:58:10 +08:00
wujunchen
2bf4e86451 fix: confirm before overwriting exports
Change-Id: I922fca90e46a8982686a3449b00ed793e924097b
2026-04-29 13:56:36 +08:00
wujunchen
5304ed3a48 fix: localize remaining action labels
Change-Id: I4ac6db14c4aede9a106e8023effac3c99d3c037a
2026-04-29 13:54:53 +08:00
wujunchen
2d39a91aed test: split architecture guard tests
Change-Id: Ibde4ae79f202ae0e26658ab47bbcf5b52ef9effe
2026-04-29 13:53:19 +08:00
wujunchen
39ddd4289e fix: count batch generation failures
Change-Id: Ie6b29cb3efc22fe72250e9c63c1e82c71b24a356
2026-04-29 13:51:33 +08:00
wujunchen
0801b644f6 fix: address CR findings — bump script, test runner, temp cleanup
- bump-version.mjs: reject duplicate versions (use --force to override),
  auto git-add manifest.json + versions.json for npm version hook
- run-tests.mjs: use execFileSync to handle paths with spaces
- test-setup.js: clean up esbuild temp directory on process exit

Change-Id: I44410b550d09487dd7f5e30a8f9385a2c500252a
2026-04-27 20:19:53 +08:00
wujunchen
199ab73188 refactor: split main.test.js into focused test files
Reduce main.test.js from 560 to 70 lines by keeping only architecture
guards and export smoke tests. Moved provider error tests, Anthropic
tool-use parsing, and cancellationNoticeKey tests to providers.test.js.
Settings, cache, and other tests were already covered in their
dedicated test files.

Change-Id: I8f0f3c576dec7fcf5a4c6e0c2b6071601940349d
2026-04-27 19:59:46 +08:00
wujunchen
f5f974fd74 build: remove __test barrel from production bundle
Tests now load src/test-exports.ts via esbuild buildSync instead of
importing __test from the production main.js. This removes ~60 internal
symbols and all test-only module code from the shipped plugin bundle.

Change-Id: I2caf5232f6894c7514215e1751f2e268b74d7dc4
2026-04-27 19:57:28 +08:00
wujunchen
0bf5ab12b1 lint: extend Biome coverage to tests/ and scripts/
Add tests/**/*.js and scripts/**/*.mjs to biome.json includes and
update npm lint scripts to cover these directories. Auto-fixed all
formatting issues in 26 files.

Change-Id: I8c2763cb9683616a8abc1484d91bf8af79bb590e
2026-04-27 19:53:35 +08:00
wujunchen
b26b241c7f refactor: centralize obsidian mock into tests/obsidian-mock.js
Extracts the duplicated Module._load obsidian stub from main.test.js,
generation-job-manager.test.js, and test-setup.js into a single shared
file. All three now require('./obsidian-mock') instead of defining
their own copy.

Change-Id: I8ac38f779299ac250963357eee9f7824e13c7d33
2026-04-27 19:52:34 +08:00
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
05ac2b923f test: add batch.ts comprehensive tests including isFileInBatchFolder
Tests cover isFileInBatchFolder (root, subfolder, nested), edge cases
for normalizeBatchFolderInput (null, undefined, only slashes),
hasUnsafeBatchFolderSegments (null, single dot, dotfile), selectBatchFiles
with complex folder structures, and stats immutability verification.

Change-Id: If4964355db427347060ea3f902e8f6ef71ea2471
2026-04-27 15:31:37 +08:00
wujunchen
741012e535 test: add settings.ts edge case tests for utility functions
Tests cover stableStringify (key sorting, nested, types), isApiBackend,
getApiFormat/getApiAuthType fallback behavior, normalizeMaxCacheEntries,
hashContent consistency, normalizeSettings validation (invalid uiLanguage,
apiProvider, apiFormat, apiAuthType, non-string customSystemPrompt),
and pruneCacheEntries with null/empty/non-object inputs.

Change-Id: I96269c69871e3c17ccb06a71ac9cfb9733a382f6
2026-04-27 15:30:33 +08:00
wujunchen
6f80d0de03 test: add generation.ts cancellationNoticeKey edge case tests
Tests cover all backend types (api, anthropic-api, claude-code, codex),
all generation phases (queued, reading, cache-check, generating, saving),
and null settings/job edge cases.

Change-Id: I722fb0cf1129c7cf45dfc5bf2544577652df843d
2026-04-27 15:29:29 +08:00
wujunchen
0f8625874e test: add comprehensive markdown.ts unit tests
Tests cover cardToMarkdown, cardToPlain, and cardsToMarkdown with
edge cases: empty/missing fields, anchor whitespace normalization,
null cards array, empty title fallback, plain text bullet formatting.

Change-Id: I1972705d385f0393c9fd1be7f80d6513f5328d06
2026-04-27 15:28:34 +08:00
wujunchen
1e2ccc0840 test: add comprehensive prompt.ts unit tests
Tests cover promptLanguageInstruction, promptSchemaExample,
renderPromptTemplate, and buildPrompts edge cases including
auto language, truncation markers, custom prompt variable
substitution, card count normalization, and fallback behavior.

Change-Id: I75d5b354df15456ddbd08d9d21846f74d6b6cea3
2026-04-27 15:27:19 +08:00
wujunchen
0264ef0b6c refactor: split direct-modules.test.js into 5 per-module test files
Replaces the monolithic direct-modules.test.js (780 lines) with focused
per-module test files sharing a common direct-test-setup.js. Each file
is under 165 lines. All existing test assertions preserved.

New files: direct-test-setup.js, direct-cache.test.js,
direct-providers.test.js, direct-settings.test.js, direct-i18n.test.js,
direct-streaming.test.js

Change-Id: Ie9cceaf3b9b71e618c0d7e524a5186b5c44079c8
2026-04-27 15:24:32 +08:00
wujunchen
7cb68658f5 refactor: split modules.test.js into 12 per-module test files
Replaces the monolithic modules.test.js (983 lines) with focused
per-module test files sharing a common test-setup.js. Each file is
under 225 lines. All existing test assertions preserved.

New files: test-setup.js, anchor.test.js, schema.test.js, cache.test.js,
cards-nav.test.js, markdown.test.js, vault-batch.test.js, i18n.test.js,
scroll.test.js, settings.test.js, providers.test.js, streaming.test.js,
cli.test.js

Change-Id: I37a3342a1588807277e679e6535cde361e650565
2026-04-27 15:21:03 +08:00
fancivez
b2716da4ca test: add getApiKey, cacheEntryMatches, normalizeCardCount edge case tests
Cover normalizeCardCount boundary values (0, negative, NaN, max 30, string).
Test getApiKey with direct key, env var, precedence, and trimming.
Test cacheEntryMatches for valid match, null entry, wrong hash, wrong schema.

Change-Id: I1113d923885e03f6b28d47721cc303bbbb6d70ed
2026-04-27 14:11:37 +08:00
fancivez
c111865fbd test: add resolveCardAnchors unit tests
Cover sorting by startLine, unanchored cards pushed to end, empty/null
input, all-unanchored relative order, and default level value.

Change-Id: I74c48041897e8e4fb44555aaa319e6ebde8f91b1
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
3a2c4e6a1c test: add vault.ts normalizeVaultPath and folderPathsForTarget tests
Cover normalizeVaultPath with clean paths, whitespace trimming, parent
traversal stripping, dot segments, null input, and empty segments.
Test folderPathsForTarget ancestor chain generation for various depths.

Change-Id: I4de1744301df1e42d35f0bd5702d5cc58fb9a277
2026-04-27 14:11:37 +08:00
fancivez
d6b89924f2 test: add i18n translate and resolveUiLanguage unit tests
Cover translate() with explicit languages, variable interpolation (single,
multiple, numeric), missing keys, null settings, unmatched placeholders.
Test resolveUiLanguage for explicit/auto/null settings.
Validate zh/en STRINGS parity (same key count, no missing keys).

Change-Id: I06c3bdc9d824eaa0e205b335b9856d872a1aefd1
2026-04-27 14:11:37 +08:00
fancivez
1efacf695c test: add schema.ts edge case coverage for repair and JSON extraction
Test repairTruncatedCardsJson with whitespace variations, escaped quotes,
braces in string values, all-truncated cards, and empty arrays.
Add collectJsonObjectCandidates tests for empty input, nested objects,
braces in strings, and unclosed braces.

Change-Id: I82392bff6eaf457934dbbe63e7c5788ea6a44e19
2026-04-27 14:11:37 +08:00
fancivez
be8ad72ba2 test: add comprehensive parseSseBuffer and deltaExtractor tests
Cover SSE parsing edge cases: single/multiple events, incomplete buffers,
CRLF line endings, [DONE] sentinel, multi-line data fields, malformed JSON
skipping, comment/event lines, data: without trailing space, empty buffer.
Test both OpenAI and Anthropic delta extractors with various input shapes.

Change-Id: I108352e07233490d23b86ed31b855692c360d4d1
2026-04-27 14:11:36 +08:00
fancivez
b3d774f4df test: add provider-parsers edge case coverage
Test all parser functions with empty responses, missing fields, null inputs,
legacy format fallbacks, multi-block concatenation, and tool-use rejection
for wrong tool names and missing content arrays.

Change-Id: Ie3c15aa636a6b421ca6bf4315313368c249f2782
2026-04-27 14:11:36 +08:00
fancivez
f7c4f42a5a test: add comprehensive CacheManager unit test coverage
Cover previously untested methods: get(), move() (success/failure/edge
cases), readFile() with corrupt JSON and missing entries, pruneIfNeeded(),
replaceCards() for missing entries, and scheduleSave()+flush() behavior.

Change-Id: Iee71e574d37925c2111eb70a292d5f59a80dc3c5
2026-04-27 14:11:36 +08:00
wujunchen
538d7bcedd test: add structured output fallback and non-retryable error tests
Cover the two key paths in requestJsonBodyWithStructuredFallback:
1. 400 with response_format mention → retry without structured output
2. 500 server error → no retry, error propagated

Change-Id: If8d0fc484e49238699444131cbadcf1b18bf992f
2026-04-27 12:44:42 +08:00
wujunchen
67a9ccdafb test: add applyApiProviderPreset model swap coverage
Cover the key scenarios for provider switching: model swapped when
matching previous preset, custom model preserved across switches,
empty model triggers swap, and input immutability verified.

Change-Id: I467604d51e8c11e990e54621347094cf914cb6b7
2026-04-27 12:43:29 +08:00
wujunchen
b02d0f4f30 test: add cache pruning interleaved with put test
Verify that when a new entry is added via put() and the cache exceeds
maxCacheEntries, the oldest entry (by timestamp) is pruned and the
pruned state is correctly persisted to disk.

Change-Id: Ia937800a97d7591877e1d2d1fe808b86858735e1
2026-04-27 12:30:56 +08:00
wujunchen
cfdd985f78 test: add batch state isolation and cancellation tests
Verify that two concurrent BatchRunState objects don't interfere with
each other (cancelling one doesn't affect the other), and that stats
accumulate correctly when processing is stopped mid-iteration.

Change-Id: Id32b27307beafe49594cfde6fa818b2f22b87c66
2026-04-27 12:29:42 +08:00
wujunchen
593817734b test: add streaming cancellation edge case tests
Cover pre-aborted signal (aborted before fetch starts) and mid-read
abort (signal aborted while reading response body). Both verify that
abort listeners are properly cleaned up after rejection.

Change-Id: I0760f6ed051de1504e9b7aeead01a10cfd12476e
2026-04-27 12:28:36 +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
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
486060bddf test: cover cache manager with fake adapter
Change-Id: I83f6b71a46af31f783522faa53bffbfc6c4bc54d
2026-04-26 18:31:17 +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
846e32301a test: import pure modules directly
Change-Id: Ia029de2ac4c3cb3ddb7ea638c868d6347af5c67e
2026-04-26 18:25:50 +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