mirror of
https://github.com/fancive/obsidian-parallel-reader.git
synced 2026-07-22 17:20:24 +00:00
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
182 lines
4.6 KiB
TypeScript
182 lines
4.6 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;
|
|
}
|
|
|
|
export interface CacheFile {
|
|
version: number;
|
|
entries: Record<string, CacheEntry>;
|
|
}
|
|
|
|
/* ---------- 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' | '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;
|
|
isGeneratingFile(file: TFile | null): boolean;
|
|
cancelGenerationForFile(file: TFile | null): boolean;
|
|
runForFile(
|
|
file: TFile | null,
|
|
force: boolean,
|
|
options?: RunForFileOptions,
|
|
preloadedContent?: string,
|
|
): Promise<RunForFileResult | void>;
|
|
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[]>;
|
|
}
|