mirror of
https://github.com/fancive/obsidian-parallel-reader.git
synced 2026-07-22 06:53:43 +00:00
refactor: restore strict lint hygiene
Change-Id: I26917328ec55e529242dfd8e4c42af0b3fcd58b7
This commit is contained in:
parent
89e1ab9d5c
commit
9ed36570b5
8 changed files with 89 additions and 52 deletions
46
main.js
46
main.js
File diff suppressed because one or more lines are too long
54
main.ts
54
main.ts
|
|
@ -73,12 +73,10 @@ async function summarizeDocument(
|
|||
break;
|
||||
default: {
|
||||
const useStreaming = supportsStreaming(settings);
|
||||
const abortController = useStreaming ? new AbortController() : null;
|
||||
if (abortController) {
|
||||
job.onCancel(() => abortController.abort());
|
||||
}
|
||||
if (useStreaming) {
|
||||
cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController!.signal);
|
||||
const abortController = new AbortController();
|
||||
job.onCancel(() => abortController.abort());
|
||||
cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController.signal);
|
||||
} else {
|
||||
cards = await summarizeViaApi(requestUrl, system, user, settings);
|
||||
}
|
||||
|
|
@ -132,7 +130,8 @@ class ParallelReaderPlugin extends Plugin {
|
|||
|
||||
this.addRibbonIcon('book-open', this.t('ribbonOpen'), async () => {
|
||||
const active = this.getActiveView();
|
||||
await this.ensureView();
|
||||
const view = await this.ensureView();
|
||||
if (!view) return;
|
||||
if (active?.file) await this.syncViewToFile(active.file);
|
||||
});
|
||||
|
||||
|
|
@ -153,7 +152,8 @@ class ParallelReaderPlugin extends Plugin {
|
|||
name: this.t('cmdOpenView'),
|
||||
callback: async () => {
|
||||
const active = this.getActiveView();
|
||||
await this.ensureView();
|
||||
const view = await this.ensureView();
|
||||
if (!view) return;
|
||||
if (active?.file) void this.syncViewToFile(active.file);
|
||||
},
|
||||
});
|
||||
|
|
@ -221,12 +221,16 @@ class ParallelReaderPlugin extends Plugin {
|
|||
}),
|
||||
);
|
||||
this.registerEvent(this.app.workspace.on('file-menu', (menu, file) => this.addFileMenuItems(menu, file)));
|
||||
this.registerEvent(this.app.vault.on('rename', (file, oldPath) => {
|
||||
if (file instanceof TFile) void this.handleFileRename(file, oldPath);
|
||||
}));
|
||||
this.registerEvent(this.app.vault.on('delete', (file) => {
|
||||
if (file instanceof TFile) void this.handleFileDelete(file);
|
||||
}));
|
||||
this.registerEvent(
|
||||
this.app.vault.on('rename', (file, oldPath) => {
|
||||
if (file instanceof TFile) void this.handleFileRename(file, oldPath);
|
||||
}),
|
||||
);
|
||||
this.registerEvent(
|
||||
this.app.vault.on('delete', (file) => {
|
||||
if (file instanceof TFile) void this.handleFileDelete(file);
|
||||
}),
|
||||
);
|
||||
this.bindScrollSync();
|
||||
}
|
||||
|
||||
|
|
@ -296,11 +300,16 @@ class ParallelReaderPlugin extends Plugin {
|
|||
|
||||
/* ---------- View management ---------- */
|
||||
|
||||
async ensureView() {
|
||||
async ensureView(): Promise<ParallelReaderView | null> {
|
||||
const { workspace } = this.app;
|
||||
let leaf = workspace.getLeavesOfType(VIEW_TYPE_PARALLEL)[0];
|
||||
if (!leaf) {
|
||||
leaf = workspace.getRightLeaf(false)!;
|
||||
const newLeaf = workspace.getRightLeaf(false);
|
||||
if (!newLeaf) {
|
||||
new Notice(this.t('viewOpenFailed'));
|
||||
return null;
|
||||
}
|
||||
leaf = newLeaf;
|
||||
await leaf.setViewState({ type: VIEW_TYPE_PARALLEL, active: true });
|
||||
}
|
||||
await workspace.revealLeaf(leaf);
|
||||
|
|
@ -446,6 +455,7 @@ class ParallelReaderPlugin extends Plugin {
|
|||
async exportCurrentView() {
|
||||
const active = this.getActiveView();
|
||||
const view = await this.ensureView();
|
||||
if (!view) return;
|
||||
if (!view.sourceFile || !view.sections.length) {
|
||||
if (active?.file) await this.syncViewToFile(active.file);
|
||||
}
|
||||
|
|
@ -459,6 +469,7 @@ class ParallelReaderPlugin extends Plugin {
|
|||
async copyCurrentViewMarkdown() {
|
||||
const active = this.getActiveView();
|
||||
const view = await this.ensureView();
|
||||
if (!view) return;
|
||||
if (!view.sourceFile || !view.sections.length) {
|
||||
if (active?.file) await this.syncViewToFile(active.file);
|
||||
}
|
||||
|
|
@ -492,7 +503,10 @@ class ParallelReaderPlugin extends Plugin {
|
|||
new Notice(this.t('alreadyGenerating'));
|
||||
return;
|
||||
}
|
||||
if (shouldConfirmRegenerate(this.cacheManager.get(file.path), force) && !(await this.confirmRegenerateEditedCards())) {
|
||||
if (
|
||||
shouldConfirmRegenerate(this.cacheManager.get(file.path), force) &&
|
||||
!(await this.confirmRegenerateEditedCards())
|
||||
) {
|
||||
new Notice(this.t('regenerateCancelled'));
|
||||
return;
|
||||
}
|
||||
|
|
@ -509,6 +523,7 @@ class ParallelReaderPlugin extends Plugin {
|
|||
}
|
||||
|
||||
view = await this.ensureView();
|
||||
if (!view) return;
|
||||
job.throwIfCancelled();
|
||||
|
||||
job.setPhase('cache-check');
|
||||
|
|
@ -528,10 +543,11 @@ class ParallelReaderPlugin extends Plugin {
|
|||
new Notice(this.t('generatingNotice'));
|
||||
|
||||
job.setPhase('generating');
|
||||
const streamProgress = view
|
||||
const streamingView = view;
|
||||
const streamProgress = streamingView
|
||||
? (progress: StreamProgress) => {
|
||||
if (!progress.done && this.viewIsShowingFile(view, file)) {
|
||||
view!.renderStreamingPreview(file, progress.accumulated);
|
||||
if (!progress.done && this.viewIsShowingFile(streamingView, file)) {
|
||||
streamingView.renderStreamingPreview(file, progress.accumulated);
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
|
|
|||
19
src/cli.ts
19
src/cli.ts
|
|
@ -115,10 +115,19 @@ export function runCli(
|
|||
});
|
||||
}
|
||||
|
||||
child.stdout!.on('data', (d) => {
|
||||
if (!child.stdout || !child.stderr || !child.stdin) {
|
||||
fail(new Error('CLI process streams are unavailable'));
|
||||
return;
|
||||
}
|
||||
|
||||
const childStdout = child.stdout;
|
||||
const childStderr = child.stderr;
|
||||
const childStdin = child.stdin;
|
||||
|
||||
childStdout.on('data', (d) => {
|
||||
stdout += d.toString('utf8');
|
||||
});
|
||||
child.stderr!.on('data', (d) => {
|
||||
childStderr.on('data', (d) => {
|
||||
stderr += d.toString('utf8');
|
||||
});
|
||||
child.on('error', (e) => {
|
||||
|
|
@ -134,14 +143,14 @@ export function runCli(
|
|||
|
||||
if (stdinText) {
|
||||
try {
|
||||
child.stdin!.write(stdinText);
|
||||
child.stdin!.end();
|
||||
childStdin.write(stdinText);
|
||||
childStdin.end();
|
||||
} catch (_e) {
|
||||
// Child may have exited before stdin was written.
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
child.stdin!.end();
|
||||
childStdin.end();
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,9 @@ export class GenerationJob {
|
|||
for (const handler of this._cancelHandlers.splice(0)) {
|
||||
try {
|
||||
handler();
|
||||
} catch { /* handler error ignored */ }
|
||||
} catch {
|
||||
/* handler error ignored */
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ export const STRINGS: Record<string, Record<string, string>> = {
|
|||
noExportContent: '当前没有可导出的对照笔记',
|
||||
noCopyContent: '当前没有可复制的对照笔记',
|
||||
noActiveCard: '当前没有可跳转的摘要卡片',
|
||||
viewOpenFailed: '无法打开对照笔记面板',
|
||||
confirmRegenerateEditedCards: '这篇笔记的对照卡片已被手动编辑。重新生成会覆盖这些修改,是否继续?',
|
||||
regenerateCancelled: '已取消重新生成',
|
||||
cardDeleted: '已删除此卡片',
|
||||
|
|
@ -208,6 +209,7 @@ export const STRINGS: Record<string, Record<string, string>> = {
|
|||
noExportContent: 'No parallel notes to export',
|
||||
noCopyContent: 'No parallel notes to copy',
|
||||
noActiveCard: 'No active summary card to jump',
|
||||
viewOpenFailed: 'Could not open the Parallel Reader pane',
|
||||
confirmRegenerateEditedCards:
|
||||
'These parallel-reader cards were edited manually. Regenerating will overwrite those edits. Continue?',
|
||||
regenerateCancelled: 'Regeneration cancelled',
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ type RequestUrlFunction = (params: {
|
|||
throw?: boolean;
|
||||
}) => Promise<{ status: number; json: unknown; text: string }>;
|
||||
|
||||
function requiredDeltaExtractor(format: string) {
|
||||
const extractor = deltaExtractorForFormat(format);
|
||||
if (!extractor) throw new Error(`Streaming not supported for format: ${format}`);
|
||||
return extractor;
|
||||
}
|
||||
|
||||
interface AnthropicMessagesBody {
|
||||
model: string;
|
||||
max_tokens: number;
|
||||
|
|
@ -506,7 +512,7 @@ async function streamSummarizeViaOpenAiChat(
|
|||
const headers = buildApiHeaders(settings);
|
||||
const body = buildOpenAiChatBody(system, user, settings, { structured: false });
|
||||
body.stream = true;
|
||||
const extractor = deltaExtractorForFormat('openai-chat')!;
|
||||
const extractor = requiredDeltaExtractor('openai-chat');
|
||||
const text = await streamingFetch(url, headers, body, extractor, onProgress, signal, settings);
|
||||
return parseCardsJson(text.trim(), settings);
|
||||
}
|
||||
|
|
@ -522,7 +528,7 @@ async function streamSummarizeViaAnthropicMessages(
|
|||
const headers = buildApiHeaders(settings, { 'anthropic-version': '2023-06-01' });
|
||||
const body = buildAnthropicMessagesBody(system, user, settings, { structured: false });
|
||||
body.stream = true;
|
||||
const extractor = deltaExtractorForFormat('anthropic-messages')!;
|
||||
const extractor = requiredDeltaExtractor('anthropic-messages');
|
||||
const text = await streamingFetch(url, headers, body, extractor, onProgress, signal, settings);
|
||||
return parseCardsJson(text.trim(), settings);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -347,7 +347,9 @@ export class ParallelReaderSettingTab extends PluginSettingTab {
|
|||
this.display();
|
||||
}
|
||||
};
|
||||
t.inputEl.addEventListener('change', () => { void commit(); });
|
||||
t.inputEl.addEventListener('change', () => {
|
||||
void commit();
|
||||
});
|
||||
t.inputEl.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') t.inputEl.blur();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export function deltaExtractorForFormat(format: string): DeltaExtractor | null {
|
|||
export function parseSseBuffer(buffer: string, extractDelta: DeltaExtractor): { deltas: string[]; rest: string } {
|
||||
const deltas: string[] = [];
|
||||
const lines = buffer.split('\n');
|
||||
const rest = lines.pop()!; // keep incomplete line
|
||||
const rest = lines.pop() ?? ''; // keep incomplete line
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
|
|
|||
Loading…
Reference in a new issue