mirror of
https://github.com/ckelsoe/obsidian-plaud-importer.git
synced 2026-07-22 07:49:02 +00:00
Adds the ImportModal that the "Plaud Importer: Import recent recordings"
command opens. The modal walks through four states:
- loading: spinner while listRecordings is in flight
- empty: "no recordings found" when Plaud returns zero items
- error: category-specific message + Close (and Retry where it helps)
- list: newest-first checkbox list using the comfortable two-line
layout requested for long meeting titles
main.ts constructs a single ReverseEngineeredPlaudClient in
onLayoutReady with an obsidianFetcher adapter that wraps Obsidian's
requestUrl. safeJson narrows the catch to SyntaxError only so that
non-JSON errors from the requestUrl getter are not silently swallowed.
Error classification (classifyError) routes each error class to a
distinct category with a canRetry flag and user-facing message:
- PlaudAuthError.reason === 'not_configured' → tells the user to
open settings, no retry (modal is blocking anyway)
- PlaudAuthError.reason === 'token_rejected' → "your token was
rejected, re-enter it", no retry
- PlaudParseError → "unexpected shape, plugin may need update", no
retry (comes before the generic PlaudApiError branch because it
extends PlaudApiError)
- PlaudApiError status 429 → rate-limited, retry enabled
- PlaudApiError status 500+ → server error, retry enabled
- PlaudApiError status 400-499 (not 401/429) → api-error, no retry
("Plaud.AI returned HTTP X, the plugin may need an update")
- PlaudApiError no status (network wrap) → network-error, retry
- Anything else → unknown, retry DISABLED and asks for bug report
Review-agent findings applied before committing:
- PlaudAuthError gained a readonly `reason` field ('not_configured'
| 'token_rejected'). classifyError discriminates on err.reason
instead of substring-matching on err.message, which would have
silently misclassified the not-configured case on any future
message rewording.
- 4xx responses other than 401/429 now map to a dedicated api-error
category instead of being lied about as "Could not reach Plaud.AI"
(403/404/418 would reach Plaud fine and get rejected by Plaud).
Parametric test covers 400/403/404/418/451.
- Unknown-error fallback sets canRetry: false and asks the user to
report the issue. Retrying an unknown error almost never helps and
masks real bugs behind a false-hope Retry button.
- formatDuration guards against NaN/Infinity input to avoid rendering
"NaNh NaNm NaNs" in the UI. Parser rejects these upstream today but
the helper is exported and a future caller could slip by.
- Removed four <h2> createEl calls in favor of a single this.setTitle()
in onOpen, which puts the title in Obsidian's modal titleEl (separate
from contentEl) and survives state transitions automatically.
Satisfies the Plugin guidelines rule against raw heading elements in
plugin UI.
- refresh() now uses a monotonic fetchGeneration counter so that a
slow in-flight fetch can't overwrite the results of a newer Retry.
- onOpen's refresh() call has a defense-in-depth .catch() in case a
future render function throws outside the fetch try/catch.
- onImportClick stub says "NOT IMPLEMENTED" in the Notice so a future
refactor can't accidentally ship a silent no-op Import.
Tests:
- 68 tests pass (was 60). New: 5 parametric 4xx tests, 2 not-configured
reason-field tests (one pins that discrimination is via the enum,
not message text), 3 parametric NaN/Infinity tests for
formatDuration, updated class-hierarchy precedence tests.
- New __tests__/__mocks__/obsidian.ts provides an inert stub for the
obsidian peer dep so jest can resolve imports from source files that
use Modal/Notice/App. Wired in via jest.config.js moduleNameMapper.
- ESLint config already includes test files via the prior commit's
jest-globals block; lint script extended to cover the new source.
styles.css replaced the placeholder with ~85 lines for the comfortable
row layout, state screens, and button row. Uses Obsidian's CSS
variables (--text-muted, --text-error, --text-accent,
--background-modifier-border, --font-monospace) instead of hardcoded
colors — compliant with Plugin guidelines.
Build clean, lint clean (0 warnings), 68/68 tests pass.
19 lines
596 B
JavaScript
19 lines
596 B
JavaScript
/** @type {import('jest').Config} */
|
|
module.exports = {
|
|
testEnvironment: 'node',
|
|
testMatch: ['**/__tests__/**/*.test.ts'],
|
|
// `obsidian` is a peer dep provided at runtime by the Obsidian app —
|
|
// no real module exists for jest to resolve. Map it to an inert stub so
|
|
// source files that import from it can be loaded by the test harness.
|
|
moduleNameMapper: {
|
|
'^obsidian$': '<rootDir>/__tests__/__mocks__/obsidian.ts',
|
|
},
|
|
transform: {
|
|
'^.+\\.ts$': ['ts-jest', {
|
|
tsconfig: {
|
|
// Override ESNext module to CommonJS for Jest compatibility
|
|
module: 'CommonJS',
|
|
},
|
|
}],
|
|
},
|
|
};
|