mirror of
https://github.com/fancive/obsidian-parallel-reader.git
synced 2026-07-22 06:53:43 +00:00
Assessment-driven batch of 10 verified, low-risk improvements. All gates green
(typecheck, biome, obsidian lint + strict review, 28 test files, branch
coverage 100%, e2e gate).
Correctness:
- provider-request: decide structured-output fallback on HTTP status via a new
ProviderApiError (status+body) instead of pattern-matching the i18n-translated
message — previously only en/zh matched, so fr/de/es/ja/ko users hit silent
permanent failures when a provider rejected json_schema.
- streaming: surface in-stream provider error payloads ({type:'error'} /
{error:{}}) by throwing, instead of swallowing them and later misreporting a
transient overload/quota error as "non-JSON LLM output". Note: detection runs
outside the JSON.parse try/catch so the throw is not swallowed.
- main: throwIfCancelled before cacheManager.put so a cancelled job cannot
poison the cache.
- generation-job-manager: add cancelAll(); onunload now cancels in-flight jobs
(aborting streaming HTTP + SIGKILL-ing CLI children), not just queued waiters.
Onboarding / UX:
- settings: DEFAULT_SETTINGS.promptLanguage 'zh' -> 'auto' so new non-Chinese
users get source-language summaries by default (existing users unaffected).
- view/main/types: first-run "Set up AI provider" CTA in the empty state when no
credential is configured (PluginHost.openSettings + isCredentialConfigured).
- error-ui/generation-job-manager/types: new 'network' ErrorKind with an
actionable notice + Retry for offline/connection failures.
- i18n-strings: 4 new keys across all 7 locales (parity test enforced).
CI / docs:
- .c8rc.json + package.json: branch-coverage gate (check-coverage, branches=100,
degenerate metrics disabled) and preserve c8's exit code in the coverage script.
- ci.yml: run coverage gate and strict obsidian review in CI.
- README: fix Obsidian version badge 1.4.0 -> 1.8.7 (matches manifest).
- e2e product-shell DOM shim: add createSpan (was missing; real Obsidian has it).
Tests: cover the status-based fallback (incl. non-English locale), in-stream
error throwing, cancelAll, and network classification.
Change-Id: Ic619098aa7cdf3dc1c444be4bb8a445550eadf55
181 lines
4.7 KiB
TypeScript
181 lines
4.7 KiB
TypeScript
'use strict';
|
|
|
|
import type { App, PluginManifest, TFile } from 'obsidian';
|
|
|
|
/* ---------- Card types ---------- */
|
|
|
|
/** Raw card as returned by the LLM and stored in cache (no computed fields). */
|
|
export interface RawCard {
|
|
title: string;
|
|
anchor: string;
|
|
gist: string;
|
|
bullets: string[];
|
|
}
|
|
|
|
/** Card with the computed startLine from anchor resolution. */
|
|
export interface ResolvedCard extends RawCard {
|
|
level: number;
|
|
startLine: number;
|
|
}
|
|
|
|
/** Patch payload when editing a card via the modal. */
|
|
export interface CardPatch {
|
|
title?: string;
|
|
gist?: string;
|
|
bullets?: string[];
|
|
}
|
|
|
|
/* ---------- Cache types ---------- */
|
|
|
|
export interface CacheEntry {
|
|
schemaVersion: number;
|
|
contentHash: string;
|
|
settingsHash: string;
|
|
cards: RawCard[];
|
|
generatedAt: string;
|
|
lastAccessedAt?: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
/* ---------- Settings types ---------- */
|
|
|
|
export interface PluginSettings {
|
|
uiLanguage: string;
|
|
backend: string;
|
|
cliPath: string;
|
|
apiProvider: string;
|
|
apiFormat: string;
|
|
apiBaseUrl: string;
|
|
apiKey: string;
|
|
apiKeyEnvVar: string;
|
|
apiAuthType: string;
|
|
apiHeaders: string;
|
|
apiMaxTokens: number;
|
|
maxDocChars: number;
|
|
maxCacheEntries: number;
|
|
promptLanguage: string;
|
|
minCards: number;
|
|
maxCards: number;
|
|
customSystemPrompt: string;
|
|
model: string;
|
|
exportFolder: string;
|
|
cliTimeoutMs: number;
|
|
streaming: boolean;
|
|
streamingTimeoutMs: number;
|
|
}
|
|
|
|
/* ---------- Provider types ---------- */
|
|
|
|
export interface ApiProviderPreset {
|
|
label: string;
|
|
format: string;
|
|
baseUrl: string;
|
|
authType: string;
|
|
envVar: string;
|
|
model: string;
|
|
tokenLimitField?: string;
|
|
modelPrefix?: string;
|
|
}
|
|
|
|
export interface ApiFormat {
|
|
label: string;
|
|
defaultBaseUrl: string;
|
|
defaultAuthType: string;
|
|
tokenLimitField?: string;
|
|
}
|
|
|
|
/* ---------- Generation job types ---------- */
|
|
|
|
export type GenerationPhase =
|
|
| 'queued'
|
|
| 'running'
|
|
| 'reading'
|
|
| 'cache-check'
|
|
| 'generating'
|
|
| 'saving'
|
|
| 'done'
|
|
| 'cancelled';
|
|
|
|
export type ErrorKind = 'auth' | 'timeout' | 'rate-limit' | 'network' | 'schema' | 'config' | 'cancelled' | 'unknown';
|
|
|
|
export interface RunForFileOptions {
|
|
rethrowErrors?: boolean;
|
|
/** Skip ensureView+revealLeaf; only update view if it's already showing this file (used by batch). */
|
|
silentView?: boolean;
|
|
/** Skip the "you have edited cards" confirm dialog (used by unattended batch). */
|
|
skipEditConfirm?: boolean;
|
|
}
|
|
|
|
export type RunForFileResult = 'generated' | 'cached' | 'cancelled' | 'already-running' | 'empty' | 'error' | 'no-view';
|
|
|
|
/* ---------- Prompt types ---------- */
|
|
|
|
export interface PromptPair {
|
|
system: string;
|
|
user: string;
|
|
}
|
|
|
|
/* ---------- Obsidian internal API types ---------- */
|
|
|
|
/** Minimal CodeMirror 6 EditorView shape used for scroll synchronization. */
|
|
export interface CmEditorView {
|
|
scrollDOM: HTMLElement;
|
|
state: {
|
|
doc: {
|
|
lineAt(pos: number): { number: number };
|
|
};
|
|
};
|
|
posAtCoords(coords: { x: number; y: number }): number | null;
|
|
}
|
|
|
|
/** Obsidian Editor with optional CodeMirror 6 view attached at `.cm`. */
|
|
export interface ObsidianEditorWithCm {
|
|
cm?: CmEditorView;
|
|
}
|
|
|
|
/** Minimal Obsidian MenuItem builder API used in file-menu callbacks. */
|
|
export interface ObsidianMenuItem {
|
|
setTitle(title: string): this;
|
|
setIcon(icon: string): this;
|
|
onClick(callback: () => unknown): this;
|
|
}
|
|
|
|
/** Minimal Obsidian Menu API used to build context-menu entries. */
|
|
export interface ObsidianMenu {
|
|
addSeparator(): void;
|
|
addItem(cb: (item: ObsidianMenuItem) => void): void;
|
|
}
|
|
|
|
/* ---------- Plugin host interface ---------- */
|
|
|
|
/**
|
|
* Minimal interface that extracted UI classes (View, Modal, SettingsTab)
|
|
* use to call back into the plugin. Avoids circular imports between
|
|
* main.ts and the extracted modules.
|
|
*/
|
|
export interface PluginHost {
|
|
app: App;
|
|
settings: PluginSettings;
|
|
cache: Record<string, CacheEntry>;
|
|
manifest: PluginManifest;
|
|
t(key: string, vars?: Record<string, string | number>): string;
|
|
/** Open the plugin's settings tab (best-effort; no-op if the API is unavailable). */
|
|
openSettings(): void;
|
|
/** True if a usable credential is configured for the current backend (API key, env var, or keyless local provider). */
|
|
isCredentialConfigured(): boolean;
|
|
isGeneratingFile(file: TFile | null): boolean;
|
|
cancelGenerationForFile(file: TFile | null): boolean;
|
|
runForFile(
|
|
file: TFile | null,
|
|
force: boolean,
|
|
options?: RunForFileOptions,
|
|
preloadedContent?: string,
|
|
): Promise<RunForFileResult>;
|
|
copyCurrentViewMarkdown(): Promise<void>;
|
|
scrollEditorToLine(line: number, file: TFile | null): Promise<void>;
|
|
cacheReplaceCards(filePath: string, cards: ResolvedCard[]): Promise<boolean>;
|
|
saveSettings(): Promise<void>;
|
|
saveSettingsDebounced(delayMs?: number): void;
|
|
cacheClear(): Promise<void>;
|
|
pruneCacheIfNeeded(): Promise<string[]>;
|
|
}
|