From 9e369ae1fa9683006da6c5c79f5b1e77408ff3aa Mon Sep 17 00:00:00 2001 From: wujunchen Date: Sun, 26 Apr 2026 09:57:30 +0800 Subject: [PATCH] refactor: remove CLI backends, add Chinese README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove Claude Code CLI and Codex CLI backends — they depend on unstable CLI flags and macOS Keychain ACLs. API backend is now the only path, default provider is Anthropic. Replace English README with Chinese version focused on API usage. Add .agent/ to .gitignore. Change-Id: I7b2488d9bca23e753cc3d461f5f7bc39052dcbef --- .gitignore | 3 + README.md | 228 +++++++++++++++++--------------------------- main.js | 44 ++++----- main.ts | 23 ++--- src/settings-tab.ts | 74 ++------------ src/settings.ts | 2 +- 6 files changed, 119 insertions(+), 255 deletions(-) diff --git a/.gitignore b/.gitignore index 06114e9..00673e8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ cache.json node_modules/ dist/ *.log + +# Agent state +.agent/ diff --git a/README.md b/README.md index fb16ffd..a6825ef 100644 --- a/README.md +++ b/README.md @@ -1,183 +1,125 @@ # Obsidian Parallel Reader -An Obsidian plugin that splits any long note into a left/right parallel-reading layout: the original content stays on the left; LLM-generated section summaries appear on the right. Scrolling the source highlights the matching summary card; clicking a summary jumps the source editor. +一个 Obsidian 插件:左边原文、右边 AI 摘要卡片,滚动联动、点击跳转。 -Inspired by the reading workflow demo in [this Bilibili video](https://www.bilibili.com/video/BV1FxoGBVETm/). +灵感来自 [这个 B 站视频](https://www.bilibili.com/video/BV1FxoGBVETm/) 的阅读工作流演示。 -## Features +## 功能 -- **Self-adaptive segmentation** — the LLM decides natural topic boundaries, no need to match markdown headings. Short adjacent sections merge; long ones split. -- **Anchor-based line resolution** — summaries carry a verbatim quote from the source; the plugin finds the line number by `indexOf` (with graceful fallbacks). -- **Scroll-sync highlighting** — scrolling the source editor updates the active card in the summary pane. -- **Right-click → copy** — Markdown / plain text / anchor quote / jump to source, as a native Obsidian context menu. -- **Markdown rendering** — summaries render through Obsidian's official `MarkdownRenderer`, so tables, bold, code, wikilinks all work. -- **Persistent bounded cache** — generated summaries are cached by SHA-1 of source content plus generation settings in a separate `cache.json`. Reopening a note shows the cached view instantly; edits or model changes invalidate the cache (shows a stale banner). The cache has an LRU-style entry limit. -- **Panel actions** — regenerate, copy all Markdown, or export the current comparison note directly from the right pane. -- **Vault lifecycle aware** — cached summaries follow Markdown file renames and are removed when the source note is deleted. -- **Configurable input limit** — default 20,000 characters, adjustable for long-context models and included in the cache fingerprint. -- **Prompt controls** — choose summary language, card count range, and an optional custom system prompt. Prompt settings are included in the cache fingerprint. -- **Localized UI** — plugin commands, settings, pane text, and notices support Chinese and English. -- **Multiple model access paths** — Claude Code CLI, Codex CLI, direct provider APIs, and OpenAI/Anthropic-compatible proxies. The API backend follows the same lightweight `provider/model + base URL + protocol` idea as OpenClaw. +- **自适应切段** — LLM 自行判断主题边界,不依赖 markdown 标题。短段合并、长段拆分。 +- **Anchor 定位** — 每张摘要卡片携带原文逐字引用,插件通过 `indexOf` 定位行号(含多级容错回退)。 +- **滚动联动** — 滚动编辑器时,右侧对应卡片自动高亮。 +- **右键菜单** — 复制 Markdown / 纯文本 / anchor 引用 / 跳转原文 / 编辑 / 删除卡片。 +- **Markdown 渲染** — 摘要通过 Obsidian 的 `MarkdownRenderer` 渲染,表格、加粗、代码、wikilink 均正常显示。 +- **流式输出** — 支持 OpenAI Chat 和 Anthropic API 的 SSE 流式响应,生成时实时显示进度。 +- **持久化缓存** — 生成结果按源文件 SHA-1 + 生成配置缓存在 `cache.json`,重新打开秒出;内容或配置变更后显示"已过期"提示。 +- **面板操作** — 重新生成、复制全部 Markdown、导出到 Vault。 +- **文件感知** — 缓存跟随文件重命名,源文件删除时自动清理。 +- **Prompt 控制** — 可选输出语言、卡片数量范围、自定义 system prompt。 +- **中英双语 UI** — 命令、设置、面板文案全部支持中文和英文。 +- **多 Provider 支持** — Anthropic、OpenAI、Gemini、OpenRouter、Groq、DeepSeek、Moonshot、千帆、MiniMax、xAI、Mistral、Cerebras、智谱、Ollama、LM Studio 及自定义兼容端点。 -## Installation +## 安装 -### Manual install (until this lands in Community Plugins) +### 手动安装 -1. Clone this repo into `.obsidian/plugins/parallel-reader/` in your vault, or symlink it there: - ```bash - ln -s /path/to/obsidian-parallel-reader /path/to/YourVault/.obsidian/plugins/parallel-reader - ``` -2. In Obsidian: **Settings → Community plugins → Installed plugins** → enable **Parallel Reader**. +1. 下载 [最新 Release](https://github.com/fancive/obsidian-parallel-reader/releases) 中的 `main.js`、`manifest.json`、`styles.css` +2. 在 Vault 的 `.obsidian/plugins/` 目录下创建 `parallel-reader` 文件夹 +3. 将三个文件放入该文件夹 +4. Obsidian → **设置 → 第三方插件 → 已安装插件** → 启用 **Parallel Reader** -### Pick a backend +### 配置 Provider -| Backend | Auth | Cost | Notes | -|---------|------|------|-------| -| **Claude Code CLI** | OAuth in macOS Keychain | Included in Claude subscription | ⚠️ Keychain ACL may block Obsidian subprocess reads — may not work from GUI | -| **Codex CLI** | File-based token (`~/.codex/auth.json`) | Included in ChatGPT Plus | ✅ Works from Obsidian GUI; recommended | -| **API / Provider** | API key or local server | Provider billing / local | Supports Anthropic, OpenAI, Gemini, OpenRouter, Groq, DeepSeek, Moonshot, Qianfan, MiniMax, xAI, Mistral, Cerebras, Z.AI, Ollama, LM Studio, and custom compatible endpoints | +在插件设置中选择一个 Provider preset,填入 API Key 和模型 ID 即可。 -### API provider mode +| Provider | API 格式 | 说明 | +|----------|----------|------| +| Anthropic | `anthropic-messages` | 默认 preset,推荐 | +| OpenAI | `openai-chat` | Chat Completions | +| Google Gemini | `google-generative-ai` | generateContent | +| OpenRouter / Groq / DeepSeek / Moonshot 等 | `openai-chat` | OpenAI 兼容格式 | +| Ollama / LM Studio | `openai-chat` | 本地模型,无需 API Key | +| 自定义端点 | 任意 | 填写 Base URL 即可 | -In plugin settings, choose **Backend → API / Provider**, then select a provider preset. Presets fill the protocol, base URL, auth header, and environment variable name; you still control the model ID. +Model ID 支持 `provider/model` 写法(如 `anthropic/claude-sonnet-4-6`),匹配当前 preset 时自动剥离前缀。 -Supported protocol formats: +## 命令 -| Format | Use it for | -|--------|------------| -| `anthropic-messages` | Anthropic API and Anthropic-compatible proxies | -| `openai-chat` | OpenAI-compatible `/chat/completions` providers and local proxies | -| `openai-responses` | OpenAI `/responses` endpoint | -| `google-generative-ai` | Gemini `generateContent` API | +| 命令 | 说明 | +|------|------| +| 为当前笔记生成对照笔记(缓存优先) | 有缓存直接显示,否则调用 LLM | +| 强制重新生成(绕过缓存) | 忽略缓存重新调用 LLM | +| 打开对照笔记面板 | 打开右侧面板 | +| 导出当前对照笔记到 Vault | 保存为 Markdown 文件 | +| 复制当前对照笔记 Markdown | 复制到剪贴板 | +| 取消当前对照笔记生成 | 取消正在进行的生成 | +| `Alt+↑` / `Alt+↓` | 在摘要卡片间切换 | +| `Enter`(在摘要面板内) | 跳转到当前卡片对应的原文位置 | -Model IDs accept OpenClaw-style `provider/model` values. If the prefix matches the selected preset, the plugin strips it before the request. For example: +## 交互 -- Preset `anthropic`, model `anthropic/claude-sonnet-4-6` → sends `claude-sonnet-4-6` -- Preset `openrouter`, model `openrouter/anthropic/claude-sonnet-4-5` → sends `anthropic/claude-sonnet-4-5` -- Preset `ollama`, model `llama3.3` → sends `llama3.3` +| 操作 | 效果 | +|------|------| +| 点击卡片 | 跳转到原文对应位置 | +| 右键卡片 | 上下文菜单 | +| 滚动编辑器 | 右侧卡片自动高亮 | +| 拖选文字 | 正常选择文本(不触发跳转) | +| 文件右键菜单 | 生成 / 重新生成 / 清除缓存 | +| Ribbon 图标 | 打开对照面板 | -For Cloudflare AI Gateway or other proxies, use **额外 headers** with either JSON: - -```json -{"cf-aig-authorization":"Bearer ..."} -``` - -or one header per line: - -```text -cf-aig-authorization: Bearer ... -``` - -### Configure the CLI path - -Obsidian launched from Finder does **not** inherit your shell `PATH`. Create a stable symlink in `~/bin/` and point the plugin setting at it: - -```bash -mkdir -p ~/bin -# Claude Code (Mach-O binary, no Node needed) -ln -sf "$(readlink -f "$(which claude)")" ~/bin/claude -# Codex (Node script — use the wrapper pattern below instead) -cat > ~/bin/codex <<'EOF' -#!/bin/bash -NODE="/path/to/your/node" -CODEX_JS="/path/to/@openai/codex/bin/codex.js" -exec "$NODE" "$CODEX_JS" "$@" -EOF -chmod +x ~/bin/codex -``` - -Then in the plugin settings, paste `/Users/you/bin/codex` (or `~/bin/claude`) into **CLI 路径**, and click **Test** to verify. - -## Commands - -| Command | What it does | -|---------|--------------| -| `为当前笔记生成对照笔记(缓存优先)` | Show cached summary if present, otherwise call LLM | -| `强制重新生成(绕过缓存)` | Re-run the LLM even if cache matches | -| `打开对照笔记面板` | Open right-pane view (loads cache if present) | -| `导出当前对照笔记到 Vault` | Save the current comparison note under the configured export folder | -| `复制当前对照笔记 Markdown` | Copy the current comparison note as Markdown | -| `取消当前对照笔记生成` | Mark the active generation job as cancelled | -| `清除当前笔记的缓存` | Drop the cache entry for the active note | -| `清除所有缓存` | Wipe all cached summaries | -| `聚焦上一张摘要卡片` / `Focus previous summary card` | Move the active summary card upward | -| `聚焦下一张摘要卡片` / `Focus next summary card` | Move the active summary card downward | -| `跳转到当前摘要卡片原文` / `Jump current summary card to source` | Jump the source editor to the active card | - -## Interaction - -| Action | Effect | -|--------|--------| -| Left-click a card body | Scroll source editor to that section | -| Right-click a card | Context menu: Copy Markdown / Copy plain text / Copy anchor / Jump / Edit / Delete card | -| Header icon buttons | Regenerate or cancel / copy all Markdown / export to Vault | -| File context menu | Generate / force regenerate / clear cache for a Markdown file | -| Ribbon icon | Open the comparison pane for the active note | -| `Alt+↑` / `Alt+↓` | Move between summary cards | -| `Enter` in the summary pane | Jump to the active card's source line | -| Drag to select text | Normal text selection (does not trigger jump) | -| Scroll source editor | Active card gets highlighted on the right | - -## Development +## 开发 ```bash npm install -npm run dev # watch main.ts + src/**/*.ts and rebuild main.js -npm run build # production bundle for Obsidian -npm run typecheck -npm test -node --check main.js +npm run dev # 监听 main.ts + src/**/*.ts,自动重新构建 +npm run build # 生产构建 +npm run typecheck # TypeScript 类型检查(strict 模式) +npm run lint # Biome lint +npm test # 构建 + 类型检查 + 测试 ``` -### Code layout +### 项目结构 -The plugin keeps `main.js` as the generated Obsidian runtime bundle. Edit `main.ts` and `src/**/*.ts`; esbuild bundles them back into root `main.js`. +| 文件 | 职责 | +|------|------| +| `main.ts` | 插件生命周期、命令注册、缓存管理、滚动联动 | +| `src/view.ts` | 右侧面板视图、卡片渲染、键盘导航、导出 | +| `src/modal.ts` | 卡片编辑弹窗 | +| `src/settings-tab.ts` | 设置面板 | +| `src/providers.ts` | API 请求/响应适配器(Anthropic、OpenAI、Gemini) | +| `src/streaming.ts` | SSE 流式解析 | +| `src/prompt.ts` | Prompt 构建、语言控制、自定义 prompt 模板 | +| `src/schema.ts` | JSON 提取、卡片数据归一化、结构化输出 schema | +| `src/anchor.ts` | Anchor 到行号的匹配(含容错) | +| `src/settings.ts` | 默认值、Provider preset、缓存指纹 | +| `src/i18n.ts` | 中英文 UI 翻译 | +| `src/cache.ts` / `src/cards.ts` / `src/navigation.ts` / `src/scroll.ts` / `src/vault.ts` / `src/markdown.ts` | 各种纯函数工具模块 | -| File | Responsibility | -|------|----------------| -| `main.ts` | Obsidian lifecycle, commands, right-pane view, settings tab orchestration | -| `src/anchor.ts` | Anchor-to-line matching and whitespace-normalized fallback | -| `src/cache.ts` | Cache entry touch semantics and compact cache-file serialization | -| `src/cards.ts` | Card list edit/delete helpers | -| `src/i18n.ts` | Chinese/English UI strings and translation helper | -| `src/navigation.ts` | Summary-card keyboard navigation helpers | -| `src/prompt.ts` | Prompt construction, language controls, and custom prompt templating | -| `src/settings.ts` | Defaults, provider presets, settings normalization, cache fingerprinting and pruning | -| `src/vault.ts` | Vault path normalization and recursive folder creation | -| `src/schema.ts` | JSON extraction, card payload normalization, structured-output schemas | -| `src/scroll.ts` | Scroll-sync requestAnimationFrame throttling helper | -| `src/providers.ts` | API provider request/response adapters | -| `src/generation-job-manager.ts` | Per-file generation state, cancellation, and error classification | -| `src/markdown.ts` | Card-to-Markdown/plain-text serialization | +## 设计 -## Design notes - -The LLM is asked to return JSON with this shape: +LLM 返回如下 JSON: ```json { "cards": [ { - "title": "3-10 character short heading", - "anchor": "40-80 characters, verbatim from source, used for line resolution", - "gist": "20-40 character one-line summary serving as a lead-in", - "bullets": ["3-6 supporting details, 20-50 chars each"] + "title": "3-10 字短标题", + "anchor": "40-80 字,从原文逐字复制,用于定位", + "gist": "20-40 字一句话领读", + "bullets": ["3-6 条支撑要点,每条 20-50 字"] } ] } ``` -- The **anchor** is the key mechanism that keeps scroll-sync working without relying on markdown headings. It's copied verbatim so `content.indexOf(anchor)` finds the origin line; graceful fallbacks (prefix trimming, whitespace normalization) handle minor LLM drift. -- The **gist + bullets** dual structure was chosen after several iterations — pure prose felt wall-of-text, pure bullets felt fragmented. A one-line lead-in before bullets gives both overview and scannable detail. -- Output is rendered via `MarkdownRenderer.render()` so any tables / bold / code / wikilinks in the LLM response render natively. +- **anchor** 是滚动联动的核心机制,通过 `content.indexOf(anchor)` 定位行号,不依赖 markdown 标题。 +- **gist + bullets** 双层结构在多次迭代后确定 — 纯散文太密、纯列表太碎,一句话导读 + bullet 兼顾概览和细节。 -## Known limitations +## 已知限制 -- **Document size cap**: defaults to 20,000 characters. Longer notes are truncated unless you raise **最大输入字符数** in settings. -- **Claude Code CLI auth**: Obsidian subprocess reads of the macOS Keychain `Claude Code-credentials` service may fail due to code-signing ACL. Codex backend doesn't have this issue. -- **No streaming**: generation is a single batch call; expect ~5-15s wait depending on document length and backend. -- **Provider feature gaps**: some OpenAI-compatible providers reject optional fields or use provider-specific model IDs. If a request fails, verify the model ID and switch between `openai-chat`, `openai-responses`, or the provider's native format. -- **Preview mode**: scroll-sync uses Obsidian's `setEphemeralState` which works in reading mode too, but accuracy depends on how CodeMirror resolves line positions. +- **文档长度**:默认 20,000 字符,超长笔记会截断(可在设置中调大)。 +- **Provider 差异**:部分 OpenAI 兼容 provider 可能不支持某些可选字段,请根据报错切换 API 格式。 +- **Preview 模式**:滚动联动在阅读模式下也能工作,但精度取决于 CodeMirror 的行位置计算。 ## License diff --git a/main.js b/main.js index db3b966..3902a38 100644 --- a/main.js +++ b/main.js @@ -1,29 +1,21 @@ 'use strict'; -"use strict";var dt=Object.create;var Z=Object.defineProperty;var pt=Object.getOwnPropertyDescriptor;var ht=Object.getOwnPropertyNames;var mt=Object.getPrototypeOf,gt=Object.prototype.hasOwnProperty;var ft=(t,n)=>{for(var e in n)Z(t,e,{get:n[e],enumerable:!0})},Ke=(t,n,e,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let a of ht(n))!gt.call(t,a)&&a!==e&&Z(t,a,{get:()=>n[a],enumerable:!(r=pt(n,a))||r.enumerable});return t};var Q=(t,n,e)=>(e=t!=null?dt(mt(t)):{},Ke(n||!t||!t.__esModule?Z(e,"default",{value:t,enumerable:!0}):e,t)),yt=t=>Ke(Z({},"__esModule",{value:!0}),t);var $t={};ft($t,{__test:()=>Kt,default:()=>Jt});module.exports=yt($t);var m=require("obsidian");function ee(t,n){if(!n)return-1;let e=p=>p.replace(/\s+/g," ").trim(),r=p=>{let h=[],f=[],u=!1;for(let C=0;C0;continue}u&&(h.push(" "),f.push(C),u=!1),h.push(Je),f.push(C)}return{text:h.join(""),map:f}},a=p=>{if(!p)return-1;let h=t.indexOf(p);if(h===-1)return-1;let f=0;for(let u=0;u=0||(i=a(n.trim()),i>=0))return i;for(let p of[60,40,25,15]){let h=n.trim().slice(0,p);if(i=a(h),i>=0)return i}let s=r(t),o=e(n).slice(0,30);if(!o)return-1;let l=s.text.indexOf(o);if(l===-1)return-1;let d=s.map[l];if(d==null)return-1;let c=0;for(let p=0;p0}function te(t,n){let e=Array.isArray(t)?t.slice():[];return!Number.isInteger(n)||n<0||n>=e.length||e.splice(n,1),e}function re(t,n,e){if(!Number.isInteger(t)||!Number.isInteger(n)||n<=0||!Number.isInteger(e)||e<0||t<0||t>=n)return e;let r=n-1;return r<=0?-1:t=r.length||(r[n]=Object.assign({},r[n],e||{})),r}var We=require("child_process"),Ye=Q(require("fs")),se=Q(require("os")),T=Q(require("path"));var _=class extends Error{constructor(n){super("\u8BE5\u7B14\u8BB0\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0"),this.name="GenerationJobAlreadyRunningError",this.code="already-running",this.key=n}},x=class extends Error{constructor(n){super("\u751F\u6210\u5DF2\u53D6\u6D88"),this.name="GenerationJobCancelledError",this.code="cancelled",this.key=n}},ve=class{constructor(n){this.key=n,this.phase="queued",this.cancelled=!1,this.startedAt=new Date().toISOString(),this.updatedAt=this.startedAt,this._cancelHandlers=[]}setPhase(n){this.phase=n,this.updatedAt=new Date().toISOString()}onCancel(n){if(typeof n=="function"){if(this.cancelled){n();return}this._cancelHandlers.push(n)}}cancel(){if(this.cancelled)return!1;this.cancelled=!0,this.setPhase("cancelled");for(let n of this._cancelHandlers.splice(0))try{n()}catch{}return!0}throwIfCancelled(){if(this.cancelled)throw new x(this.key)}},H=class{constructor(){this.jobs=new Map}get(n){return this.jobs.get(n)||null}isRunning(n){return this.jobs.has(n)}async start(n,e){if(this.jobs.has(n))throw new _(n);let r=new ve(n);this.jobs.set(n,r);try{r.setPhase("running");let a=await e(r);return r.throwIfCancelled(),a}catch(a){throw r.cancelled&&!(a instanceof x)?new x(n):a}finally{this.jobs.delete(n)}}cancel(n){let e=this.jobs.get(n);return e?e.cancel():!1}};function Ae(t){if(t instanceof x)return"cancelled";let n=t;if(n?.code==="cancelled")return"cancelled";let e=String(n?.message||t);return/api key|unauthorized|401|403|认证|权限/i.test(e)?"auth":/timeout|超时|timed out/i.test(e)?"timeout":/429|rate limit|too many requests/i.test(e)?"rate-limit":/非 JSON|json_schema|schema|structured/i.test(e)?"schema":/model 未设置|base url|配置|config/i.test(e)?"config":"unknown"}var ae={zh:{appTitle:"\u5BF9\u7167\u9605\u8BFB\u7B14\u8BB0",settingsTitle:"Parallel Reader \u8BBE\u7F6E",emptyOpenNote:"\u6253\u5F00\u4E00\u7BC7\u7B14\u8BB0\uFF0C\u7136\u540E\u8FD0\u884C\u547D\u4EE4\uFF1A",emptyNoCache:"\u8BE5\u7B14\u8BB0\u5C1A\u65E0\u5BF9\u7167\u7B14\u8BB0\u7F13\u5B58\u3002\u8FD0\u884C\u547D\u4EE4\uFF1A",commandGenerate:"Parallel Reader: \u4E3A\u5F53\u524D\u7B14\u8BB0\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",displayName:"\u5BF9\u7167\u9605\u8BFB\u7B14\u8BB0",ribbonOpen:"\u6253\u5F00\u5BF9\u7167\u7B14\u8BB0\u9762\u677F",cmdRun:"\u4E3A\u5F53\u524D\u7B14\u8BB0\u751F\u6210\u5BF9\u7167\u7B14\u8BB0\uFF08\u7F13\u5B58\u4F18\u5148\uFF09",cmdRegen:"\u5F3A\u5236\u91CD\u65B0\u751F\u6210\uFF08\u7ED5\u8FC7\u7F13\u5B58\uFF09",cmdOpenView:"\u6253\u5F00\u5BF9\u7167\u7B14\u8BB0\u9762\u677F",cmdExport:"\u5BFC\u51FA\u5F53\u524D\u5BF9\u7167\u7B14\u8BB0\u5230 Vault",cmdCopyMarkdown:"\u590D\u5236\u5F53\u524D\u5BF9\u7167\u7B14\u8BB0 Markdown",cmdCancel:"\u53D6\u6D88\u5F53\u524D\u5BF9\u7167\u7B14\u8BB0\u751F\u6210",cmdClearCurrent:"\u6E05\u9664\u5F53\u524D\u7B14\u8BB0\u7684\u7F13\u5B58",cmdClearAll:"\u6E05\u9664\u6240\u6709\u7F13\u5B58",cmdCardPrev:"\u805A\u7126\u4E0A\u4E00\u5F20\u6458\u8981\u5361\u7247",cmdCardNext:"\u805A\u7126\u4E0B\u4E00\u5F20\u6458\u8981\u5361\u7247",cmdCardJump:"\u8DF3\u8F6C\u5230\u5F53\u524D\u6458\u8981\u5361\u7247\u539F\u6587",actionCancel:"\u53D6\u6D88\u751F\u6210",actionRegenerate:"\u91CD\u65B0\u751F\u6210",actionCopyAll:"\u590D\u5236\u5168\u90E8 Markdown",actionExport:"\u5BFC\u51FA\u5230 Vault",staleBanner:"\u6E90\u7B14\u8BB0\u6216\u751F\u6210\u914D\u7F6E\u5DF2\u4FEE\u6539\uFF0C\u5F53\u524D\u662F\u65E7\u7F13\u5B58\u3002",loadingDefault:"\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0...",loadingGenerating:"\u5BF9\u7167\u9605\u8BFB\uFF1A\u8BA9 LLM \u8BFB\u5168\u6587\u5E76\u81EA\u9002\u5E94\u5207\u6BB5...",loadingSubtitle:"\u53EF\u4EE5\u7EE7\u7EED\u9605\u8BFB\u539F\u6587\uFF0C\u751F\u6210\u5B8C\u6210\u540E\u4F1A\u81EA\u52A8\u5237\u65B0\u53F3\u4FA7\u5361\u7247\u3002",errorTitle:"\u751F\u6210\u5931\u8D25",emptyCard:"\uFF08\u672A\u751F\u6210\uFF09",anchorMismatch:"anchor \u5339\u914D\u5931\u8D25\uFF0C\u65E0\u6CD5\u6EDA\u52A8\u8054\u52A8",menuCopyMarkdown:"\u590D\u5236 Markdown",menuCopyPlain:"\u590D\u5236\u7EAF\u6587\u672C",menuCopyAnchor:"\u590D\u5236 anchor \u5F15\u7528",menuJumpSource:"\u8DF3\u8F6C\u5230\u539F\u6587",menuEditCard:"\u7F16\u8F91\u6B64\u5361\u7247",menuDeleteCard:"\u5220\u9664\u6B64\u5361\u7247",copiedMarkdown:"\u5DF2\u590D\u5236 Markdown",copiedPlain:"\u5DF2\u590D\u5236\u7EAF\u6587\u672C",copiedAnchor:"\u5DF2\u590D\u5236\u5F15\u7528\u539F\u6587",copiedAllMarkdown:"\u5DF2\u590D\u5236\u5168\u90E8 Markdown",copyFailed:"\u590D\u5236\u5931\u8D25\uFF1A{error}",actionFailed:"{label}\u5931\u8D25\uFF1A{error}",exported:"\u5DF2\u5BFC\u51FA \u2192 {path}",noCurrentNote:"\u6CA1\u6709\u5F53\u524D\u7B14\u8BB0",cacheClearedFile:"\u5DF2\u6E05\u9664\u7F13\u5B58\uFF1A{name}",cacheClearedAll:"\u5DF2\u6E05\u9664 {count} \u6761\u7F13\u5B58",noCancelableJob:"\u5F53\u524D\u6CA1\u6709\u53EF\u53D6\u6D88\u7684\u751F\u6210\u4EFB\u52A1",cancelRequested:"\u5DF2\u8BF7\u6C42\u53D6\u6D88\u751F\u6210",cancelRequestedApiInFlight:"\u5DF2\u8BF7\u6C42\u53D6\u6D88\u751F\u6210\uFF1B\u5F53\u524D API \u8BF7\u6C42\u65E0\u6CD5\u7ACB\u5373\u4E2D\u65AD\uFF0C\u8FD4\u56DE\u540E\u4F1A\u4E22\u5F03\u7ED3\u679C\u3002",fileMenuGenerate:"\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",fileMenuRegen:"\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",fileMenuClear:"\u6E05\u9664\u5BF9\u7167\u7B14\u8BB0\u7F13\u5B58",noExportContent:"\u5F53\u524D\u6CA1\u6709\u53EF\u5BFC\u51FA\u7684\u5BF9\u7167\u7B14\u8BB0",noCopyContent:"\u5F53\u524D\u6CA1\u6709\u53EF\u590D\u5236\u7684\u5BF9\u7167\u7B14\u8BB0",noActiveCard:"\u5F53\u524D\u6CA1\u6709\u53EF\u8DF3\u8F6C\u7684\u6458\u8981\u5361\u7247",confirmRegenerateEditedCards:"\u8FD9\u7BC7\u7B14\u8BB0\u7684\u5BF9\u7167\u5361\u7247\u5DF2\u88AB\u624B\u52A8\u7F16\u8F91\u3002\u91CD\u65B0\u751F\u6210\u4F1A\u8986\u76D6\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u662F\u5426\u7EE7\u7EED\uFF1F",regenerateCancelled:"\u5DF2\u53D6\u6D88\u91CD\u65B0\u751F\u6210",cardDeleted:"\u5DF2\u5220\u9664\u6B64\u5361\u7247",cardSaved:"\u5DF2\u4FDD\u5B58\u6B64\u5361\u7247",editCardTitle:"\u7F16\u8F91\u6458\u8981\u5361\u7247",editCardTitleField:"\u6807\u9898",editCardGistField:"\u9886\u8BFB",editCardBulletsField:"\u8981\u70B9\uFF08\u6BCF\u884C\u4E00\u6761\uFF09",editCardCancel:"\u53D6\u6D88",editCardSave:"\u4FDD\u5B58",openNoteFirst:"\u5148\u6253\u5F00\u4E00\u7BC7\u7B14\u8BB0",alreadyGenerating:"\u8BE5\u7B14\u8BB0\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",emptyNote:"\u7B14\u8BB0\u4E3A\u7A7A",longNoteTruncated:"\u7B14\u8BB0\u8F83\u957F\uFF1A\u4EC5\u53D1\u9001\u524D {count} \u4E2A\u5B57\u7B26\u7ED9\u6A21\u578B",generatingNotice:"\u5BF9\u7167\u9605\u8BFB\uFF1A\u8BA9 LLM \u8BFB\u5168\u6587\u5E76\u81EA\u9002\u5E94\u5207\u6BB5\u2026",noCardsReturned:"LLM \u672A\u8FD4\u56DE\u4EFB\u4F55 card",generationDone:"\u5BF9\u7167\u7B14\u8BB0\u751F\u6210\u5B8C\u6210\uFF1A{count} \u6BB5{suffix}",unanchoredSuffix:"\uFF08\u26A0 {count} \u6BB5 anchor \u672A\u5339\u914D\uFF09",cancelled:"\u5DF2\u53D6\u6D88\u751F\u6210",cancelledError:"\u751F\u6210\u5DF2\u53D6\u6D88",generationFailed:"\u751F\u6210\u5931\u8D25{kind}\uFF1A{error}",errorCustomProviderBaseUrlRequired:"\u81EA\u5B9A\u4E49 provider \u9700\u8981\u586B\u5199 API Base URL\u3002",errorApiBaseUrlMissing:"API Base URL \u672A\u8BBE\u7F6E\u3002\u8BF7\u5728\u8BBE\u7F6E\u91CC\u9009\u62E9 provider \u6216\u586B\u5199\u81EA\u5B9A\u4E49 base URL\u3002",errorModelMissing:"Model \u672A\u8BBE\u7F6E\u3002\u8BF7\u5728\u8BBE\u7F6E\u91CC\u586B\u5199\u6A21\u578B ID\u3002",errorApiKeyMissing:"API key \u672A\u8BBE\u7F6E\u3002\u8BF7\u5728\u8BBE\u7F6E\u91CC\u586B\u5199 API Key{hint}\u3002",errorApiKeyEnvHint:" \u6216\u73AF\u5883\u53D8\u91CF {envVar}",errorLlmNonJson:`LLM \u8FD4\u56DE\u975E JSON\uFF1A +"use strict";var ot=Object.create;var W=Object.defineProperty;var lt=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var ut=Object.getPrototypeOf,pt=Object.prototype.hasOwnProperty;var dt=(t,n)=>{for(var e in n)W(t,e,{get:n[e],enumerable:!0})},je=(t,n,e,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let a of ct(n))!pt.call(t,a)&&a!==e&&W(t,a,{get:()=>n[a],enumerable:!(r=lt(n,a))||r.enumerable});return t};var Y=(t,n,e)=>(e=t!=null?ot(ut(t)):{},je(n||!t||!t.__esModule?W(e,"default",{value:t,enumerable:!0}):e,t)),ht=t=>je(W({},"__esModule",{value:!0}),t);var Ht={};dt(Ht,{__test:()=>Gt,default:()=>jt});module.exports=ht(Ht);var d=require("obsidian");function X(t,n){if(!n)return-1;let e=u=>u.replace(/\s+/g," ").trim(),r=u=>{let h=[],C=[],m=!1;for(let f=0;f0;continue}m&&(h.push(" "),C.push(f),m=!1),h.push(Ue),C.push(f)}return{text:h.join(""),map:C}},a=u=>{if(!u)return-1;let h=t.indexOf(u);if(h===-1)return-1;let C=0;for(let m=0;m=0||(i=a(n.trim()),i>=0))return i;for(let u of[60,40,25,15]){let h=n.trim().slice(0,u);if(i=a(h),i>=0)return i}let s=r(t),o=e(n).slice(0,30);if(!o)return-1;let l=s.text.indexOf(o);if(l===-1)return-1;let p=s.map[l];if(p==null)return-1;let c=0;for(let u=0;u0}function Z(t,n){let e=Array.isArray(t)?t.slice():[];return!Number.isInteger(n)||n<0||n>=e.length||e.splice(n,1),e}function Q(t,n,e){if(!Number.isInteger(t)||!Number.isInteger(n)||n<=0||!Number.isInteger(e)||e<0||t<0||t>=n)return e;let r=n-1;return r<=0?-1:t=r.length||(r[n]=Object.assign({},r[n],e||{})),r}var ft=require("child_process"),Ke=Y(require("fs")),$e=Y(require("os")),F=Y(require("path"));var L=class extends Error{constructor(n){super("\u8BE5\u7B14\u8BB0\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0"),this.name="GenerationJobAlreadyRunningError",this.code="already-running",this.key=n}},P=class extends Error{constructor(n){super("\u751F\u6210\u5DF2\u53D6\u6D88"),this.name="GenerationJobCancelledError",this.code="cancelled",this.key=n}},ye=class{constructor(n){this.key=n,this.phase="queued",this.cancelled=!1,this.startedAt=new Date().toISOString(),this.updatedAt=this.startedAt,this._cancelHandlers=[]}setPhase(n){this.phase=n,this.updatedAt=new Date().toISOString()}onCancel(n){if(typeof n=="function"){if(this.cancelled){n();return}this._cancelHandlers.push(n)}}cancel(){if(this.cancelled)return!1;this.cancelled=!0,this.setPhase("cancelled");for(let n of this._cancelHandlers.splice(0))try{n()}catch{}return!0}throwIfCancelled(){if(this.cancelled)throw new P(this.key)}},U=class{constructor(){this.jobs=new Map}get(n){return this.jobs.get(n)||null}isRunning(n){return this.jobs.has(n)}async start(n,e){if(this.jobs.has(n))throw new L(n);let r=new ye(n);this.jobs.set(n,r);try{r.setPhase("running");let a=await e(r);return r.throwIfCancelled(),a}catch(a){throw r.cancelled&&!(a instanceof P)?new P(n):a}finally{this.jobs.delete(n)}}cancel(n){let e=this.jobs.get(n);return e?e.cancel():!1}};function Ce(t){if(t instanceof P)return"cancelled";let n=t;if(n?.code==="cancelled")return"cancelled";let e=String(n?.message||t);return/api key|unauthorized|401|403|认证|权限/i.test(e)?"auth":/timeout|超时|timed out/i.test(e)?"timeout":/429|rate limit|too many requests/i.test(e)?"rate-limit":/非 JSON|json_schema|schema|structured/i.test(e)?"schema":/model 未设置|base url|配置|config/i.test(e)?"config":"unknown"}var te={zh:{appTitle:"\u5BF9\u7167\u9605\u8BFB\u7B14\u8BB0",settingsTitle:"Parallel Reader \u8BBE\u7F6E",emptyOpenNote:"\u6253\u5F00\u4E00\u7BC7\u7B14\u8BB0\uFF0C\u7136\u540E\u8FD0\u884C\u547D\u4EE4\uFF1A",emptyNoCache:"\u8BE5\u7B14\u8BB0\u5C1A\u65E0\u5BF9\u7167\u7B14\u8BB0\u7F13\u5B58\u3002\u8FD0\u884C\u547D\u4EE4\uFF1A",commandGenerate:"Parallel Reader: \u4E3A\u5F53\u524D\u7B14\u8BB0\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",displayName:"\u5BF9\u7167\u9605\u8BFB\u7B14\u8BB0",ribbonOpen:"\u6253\u5F00\u5BF9\u7167\u7B14\u8BB0\u9762\u677F",cmdRun:"\u4E3A\u5F53\u524D\u7B14\u8BB0\u751F\u6210\u5BF9\u7167\u7B14\u8BB0\uFF08\u7F13\u5B58\u4F18\u5148\uFF09",cmdRegen:"\u5F3A\u5236\u91CD\u65B0\u751F\u6210\uFF08\u7ED5\u8FC7\u7F13\u5B58\uFF09",cmdOpenView:"\u6253\u5F00\u5BF9\u7167\u7B14\u8BB0\u9762\u677F",cmdExport:"\u5BFC\u51FA\u5F53\u524D\u5BF9\u7167\u7B14\u8BB0\u5230 Vault",cmdCopyMarkdown:"\u590D\u5236\u5F53\u524D\u5BF9\u7167\u7B14\u8BB0 Markdown",cmdCancel:"\u53D6\u6D88\u5F53\u524D\u5BF9\u7167\u7B14\u8BB0\u751F\u6210",cmdClearCurrent:"\u6E05\u9664\u5F53\u524D\u7B14\u8BB0\u7684\u7F13\u5B58",cmdClearAll:"\u6E05\u9664\u6240\u6709\u7F13\u5B58",cmdCardPrev:"\u805A\u7126\u4E0A\u4E00\u5F20\u6458\u8981\u5361\u7247",cmdCardNext:"\u805A\u7126\u4E0B\u4E00\u5F20\u6458\u8981\u5361\u7247",cmdCardJump:"\u8DF3\u8F6C\u5230\u5F53\u524D\u6458\u8981\u5361\u7247\u539F\u6587",actionCancel:"\u53D6\u6D88\u751F\u6210",actionRegenerate:"\u91CD\u65B0\u751F\u6210",actionCopyAll:"\u590D\u5236\u5168\u90E8 Markdown",actionExport:"\u5BFC\u51FA\u5230 Vault",staleBanner:"\u6E90\u7B14\u8BB0\u6216\u751F\u6210\u914D\u7F6E\u5DF2\u4FEE\u6539\uFF0C\u5F53\u524D\u662F\u65E7\u7F13\u5B58\u3002",loadingDefault:"\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0...",loadingGenerating:"\u5BF9\u7167\u9605\u8BFB\uFF1A\u8BA9 LLM \u8BFB\u5168\u6587\u5E76\u81EA\u9002\u5E94\u5207\u6BB5...",loadingSubtitle:"\u53EF\u4EE5\u7EE7\u7EED\u9605\u8BFB\u539F\u6587\uFF0C\u751F\u6210\u5B8C\u6210\u540E\u4F1A\u81EA\u52A8\u5237\u65B0\u53F3\u4FA7\u5361\u7247\u3002",errorTitle:"\u751F\u6210\u5931\u8D25",emptyCard:"\uFF08\u672A\u751F\u6210\uFF09",anchorMismatch:"anchor \u5339\u914D\u5931\u8D25\uFF0C\u65E0\u6CD5\u6EDA\u52A8\u8054\u52A8",menuCopyMarkdown:"\u590D\u5236 Markdown",menuCopyPlain:"\u590D\u5236\u7EAF\u6587\u672C",menuCopyAnchor:"\u590D\u5236 anchor \u5F15\u7528",menuJumpSource:"\u8DF3\u8F6C\u5230\u539F\u6587",menuEditCard:"\u7F16\u8F91\u6B64\u5361\u7247",menuDeleteCard:"\u5220\u9664\u6B64\u5361\u7247",copiedMarkdown:"\u5DF2\u590D\u5236 Markdown",copiedPlain:"\u5DF2\u590D\u5236\u7EAF\u6587\u672C",copiedAnchor:"\u5DF2\u590D\u5236\u5F15\u7528\u539F\u6587",copiedAllMarkdown:"\u5DF2\u590D\u5236\u5168\u90E8 Markdown",copyFailed:"\u590D\u5236\u5931\u8D25\uFF1A{error}",actionFailed:"{label}\u5931\u8D25\uFF1A{error}",exported:"\u5DF2\u5BFC\u51FA \u2192 {path}",noCurrentNote:"\u6CA1\u6709\u5F53\u524D\u7B14\u8BB0",cacheClearedFile:"\u5DF2\u6E05\u9664\u7F13\u5B58\uFF1A{name}",cacheClearedAll:"\u5DF2\u6E05\u9664 {count} \u6761\u7F13\u5B58",noCancelableJob:"\u5F53\u524D\u6CA1\u6709\u53EF\u53D6\u6D88\u7684\u751F\u6210\u4EFB\u52A1",cancelRequested:"\u5DF2\u8BF7\u6C42\u53D6\u6D88\u751F\u6210",cancelRequestedApiInFlight:"\u5DF2\u8BF7\u6C42\u53D6\u6D88\u751F\u6210\uFF1B\u5F53\u524D API \u8BF7\u6C42\u65E0\u6CD5\u7ACB\u5373\u4E2D\u65AD\uFF0C\u8FD4\u56DE\u540E\u4F1A\u4E22\u5F03\u7ED3\u679C\u3002",fileMenuGenerate:"\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",fileMenuRegen:"\u5F3A\u5236\u91CD\u65B0\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",fileMenuClear:"\u6E05\u9664\u5BF9\u7167\u7B14\u8BB0\u7F13\u5B58",noExportContent:"\u5F53\u524D\u6CA1\u6709\u53EF\u5BFC\u51FA\u7684\u5BF9\u7167\u7B14\u8BB0",noCopyContent:"\u5F53\u524D\u6CA1\u6709\u53EF\u590D\u5236\u7684\u5BF9\u7167\u7B14\u8BB0",noActiveCard:"\u5F53\u524D\u6CA1\u6709\u53EF\u8DF3\u8F6C\u7684\u6458\u8981\u5361\u7247",confirmRegenerateEditedCards:"\u8FD9\u7BC7\u7B14\u8BB0\u7684\u5BF9\u7167\u5361\u7247\u5DF2\u88AB\u624B\u52A8\u7F16\u8F91\u3002\u91CD\u65B0\u751F\u6210\u4F1A\u8986\u76D6\u8FD9\u4E9B\u4FEE\u6539\uFF0C\u662F\u5426\u7EE7\u7EED\uFF1F",regenerateCancelled:"\u5DF2\u53D6\u6D88\u91CD\u65B0\u751F\u6210",cardDeleted:"\u5DF2\u5220\u9664\u6B64\u5361\u7247",cardSaved:"\u5DF2\u4FDD\u5B58\u6B64\u5361\u7247",editCardTitle:"\u7F16\u8F91\u6458\u8981\u5361\u7247",editCardTitleField:"\u6807\u9898",editCardGistField:"\u9886\u8BFB",editCardBulletsField:"\u8981\u70B9\uFF08\u6BCF\u884C\u4E00\u6761\uFF09",editCardCancel:"\u53D6\u6D88",editCardSave:"\u4FDD\u5B58",openNoteFirst:"\u5148\u6253\u5F00\u4E00\u7BC7\u7B14\u8BB0",alreadyGenerating:"\u8BE5\u7B14\u8BB0\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0",emptyNote:"\u7B14\u8BB0\u4E3A\u7A7A",longNoteTruncated:"\u7B14\u8BB0\u8F83\u957F\uFF1A\u4EC5\u53D1\u9001\u524D {count} \u4E2A\u5B57\u7B26\u7ED9\u6A21\u578B",generatingNotice:"\u5BF9\u7167\u9605\u8BFB\uFF1A\u8BA9 LLM \u8BFB\u5168\u6587\u5E76\u81EA\u9002\u5E94\u5207\u6BB5\u2026",noCardsReturned:"LLM \u672A\u8FD4\u56DE\u4EFB\u4F55 card",generationDone:"\u5BF9\u7167\u7B14\u8BB0\u751F\u6210\u5B8C\u6210\uFF1A{count} \u6BB5{suffix}",unanchoredSuffix:"\uFF08\u26A0 {count} \u6BB5 anchor \u672A\u5339\u914D\uFF09",cancelled:"\u5DF2\u53D6\u6D88\u751F\u6210",cancelledError:"\u751F\u6210\u5DF2\u53D6\u6D88",generationFailed:"\u751F\u6210\u5931\u8D25{kind}\uFF1A{error}",errorCustomProviderBaseUrlRequired:"\u81EA\u5B9A\u4E49 provider \u9700\u8981\u586B\u5199 API Base URL\u3002",errorApiBaseUrlMissing:"API Base URL \u672A\u8BBE\u7F6E\u3002\u8BF7\u5728\u8BBE\u7F6E\u91CC\u9009\u62E9 provider \u6216\u586B\u5199\u81EA\u5B9A\u4E49 base URL\u3002",errorModelMissing:"Model \u672A\u8BBE\u7F6E\u3002\u8BF7\u5728\u8BBE\u7F6E\u91CC\u586B\u5199\u6A21\u578B ID\u3002",errorApiKeyMissing:"API key \u672A\u8BBE\u7F6E\u3002\u8BF7\u5728\u8BBE\u7F6E\u91CC\u586B\u5199 API Key{hint}\u3002",errorApiKeyEnvHint:" \u6216\u73AF\u5883\u53D8\u91CF {envVar}",errorLlmNonJson:`LLM \u8FD4\u56DE\u975E JSON\uFF1A {excerpt}`,errorCustomHeadersJsonParse:"\u81EA\u5B9A\u4E49 headers JSON \u89E3\u6790\u5931\u8D25\uFF1A{error}",errorCustomHeadersJsonObject:"\u81EA\u5B9A\u4E49 headers JSON \u5FC5\u987B\u662F\u5BF9\u8C61",errorCustomHeadersLineFormat:"\u81EA\u5B9A\u4E49 headers \u6BCF\u884C\u683C\u5F0F\u5E94\u4E3A `Header-Name: value`",errorProviderNonJson:`{label} \u8FD4\u56DE\u975E JSON\uFF1A {excerpt}`,errorProviderRequestFailed:"{label} \u8BF7\u6C42\u5931\u8D25\uFF1A{error}",errorProviderApiStatus:"{label} API \u8FD4\u56DE HTTP {status}\uFF1A{excerpt}",noEditor:"\u627E\u4E0D\u5230\u6E90\u7B14\u8BB0\u5BF9\u5E94\u7684\u7F16\u8F91\u5668\u7A97\u53E3",settingUiLanguageName:"\u754C\u9762\u8BED\u8A00",settingUiLanguageDesc:"\u63A7\u5236\u63D2\u4EF6\u754C\u9762\u3001\u547D\u4EE4\u548C\u63D0\u793A\u6587\u6848\uFF1BAuto \u8DDF\u968F Obsidian/\u7CFB\u7EDF\u8BED\u8A00",settingBackendName:"Backend",settingBackendDesc:"\u751F\u6210 bullet \u7684\u540E\u7AEF\uFF1ACLI \u590D\u7528\u672C\u673A\u767B\u5F55\uFF1BAPI \u652F\u6301 OpenAI/Anthropic/Gemini \u53CA\u517C\u5BB9\u4EE3\u7406",settingCliPathName:"CLI \u8DEF\u5F84\uFF08\u53EF\u9009\uFF09",settingCliPathDesc:"\u7559\u7A7A\u5219\u81EA\u52A8\u63A2\u6D4B\u5E38\u89C1\u4F4D\u7F6E\uFF1BObsidian GUI \u542F\u52A8\u65F6\u4E0D\u7EE7\u627F shell PATH\uFF0C\u5FC5\u8981\u65F6\u586B\u7EDD\u5BF9\u8DEF\u5F84",settingCliPathPlaceholder:"\u4F8B\uFF1A/Users/you/bin/codex",settingCliTimeoutName:"CLI \u8D85\u65F6 (ms)",apiProviderHeader:"API Provider",settingProviderPresetName:"Provider preset",settingProviderPresetDesc:"\u53C2\u8003 OpenClaw \u7684 provider/model \u601D\u8DEF\uFF1Apreset \u53EA\u8D1F\u8D23\u534F\u8BAE\u3001base URL \u548C\u8BA4\u8BC1\u9ED8\u8BA4\u503C",settingApiFormatName:"API format",settingApiFormatDesc:"\u4E0D\u540C provider \u7684 wire protocol\uFF1BOpenAI-compatible \u4EE3\u7406\u901A\u5E38\u9009 Chat Completions",settingBaseUrlName:"Base URL",settingBaseUrlDesc:"\u586B provider \u6839\u5730\u5740\uFF0C\u4E0D\u8981\u9644\u52A0 /chat/completions\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528 preset \u9ED8\u8BA4\u503C",settingApiKeyName:"API Key",settingApiKeyDesc:"\u672C\u5730 Ollama/LM Studio \u53EF\u7559\u7A7A\uFF1B\u5176\u4ED6 provider \u901A\u5E38\u9700\u8981 key",settingApiKeyEnvName:"API Key \u73AF\u5883\u53D8\u91CF",settingApiKeyEnvDesc:"\u53EF\u9009\uFF1BObsidian GUI \u4E0D\u4E00\u5B9A\u7EE7\u627F shell \u73AF\u5883\uFF0C\u76F4\u63A5\u586B API Key \u66F4\u7A33\u5B9A",settingAuthTypeName:"\u8BA4\u8BC1\u65B9\u5F0F",settingAuthTypeDesc:"Auto \u4F7F\u7528 provider preset\uFF1B\u81EA\u5B9A\u4E49\u4EE3\u7406\u53EF\u6309\u9700\u8981\u6539\u6210 Bearer\u3001x-api-key \u6216 none",settingHeadersName:"\u989D\u5916 headers",settingHeadersDesc:"\u53EF\u9009\u3002\u652F\u6301 JSON \u5BF9\u8C61\u6216\u6BCF\u884C `Header: value`\uFF0C\u7528\u4E8E Cloudflare AI Gateway \u7B49\u4EE3\u7406",settingMaxTokensName:"\u6700\u5927\u8F93\u51FA tokens",settingModelName:"Model",settingModelDescApi:"API \u8C03\u7528\u7684\u6A21\u578B ID\uFF1B\u652F\u6301 OpenClaw \u98CE\u683C provider/model\uFF0C\u82E5 provider \u524D\u7F00\u5339\u914D\u5F53\u524D preset \u4F1A\u81EA\u52A8\u5265\u79BB",settingModelDescCli:"Claude Code \u4E0B\u4F1A\u4F20 --model\uFF1BCodex \u4E0B\u901A\u5E38\u5FFD\u7565\uFF08\u7528 Codex \u9ED8\u8BA4\u914D\u7F6E\uFF09",settingMaxInputName:"\u6700\u5927\u8F93\u5165\u5B57\u7B26\u6570",settingMaxInputDesc:"\u8D85\u8FC7\u8BE5\u957F\u5EA6\u4F1A\u622A\u65AD\u540E\u518D\u53D1\u9001\u7ED9\u6A21\u578B\uFF1B\u957F\u4E0A\u4E0B\u6587\u6A21\u578B\u53EF\u9002\u5F53\u8C03\u5927",promptHeader:"Prompt",settingPromptLanguageName:"\u8F93\u51FA\u8BED\u8A00",settingPromptLanguageDesc:"\u63A7\u5236 title/gist/bullets \u7684\u8BED\u8A00\uFF1Banchor \u59CB\u7EC8\u9010\u5B57\u590D\u5236\u539F\u6587",settingCardRangeName:"\u5361\u7247\u6570\u91CF\u8303\u56F4",settingCardRangeDesc:"\u6A21\u578B\u4F1A\u5728\u8FD9\u4E2A\u8303\u56F4\u5185\u81EA\u9002\u5E94\u5207\u6BB5",settingCustomPromptName:"\u81EA\u5B9A\u4E49 system prompt",settingCustomPromptDesc:"\u53EF\u9009\u3002\u652F\u6301\u53D8\u91CF\uFF1A{minCards}\u3001{maxCards}\u3001{languageInstruction}\u3001{schema}\u3001{example}",settingCustomPromptPlaceholder:"\u7559\u7A7A\u4F7F\u7528\u5185\u7F6E prompt",settingTestBackendName:"\u6D4B\u8BD5\u5F53\u524D\u540E\u7AEF",settingTestBackendDescApi:"\u4F1A\u53D1\u8D77\u4E00\u6B21\u6700\u5C0F LLM \u8BF7\u6C42\u9A8C\u8BC1 API \u8BBE\u7F6E",settingTestBackendDescCli:"\u8C03\u7528 ` --version` \u9A8C\u8BC1 spawn \u80FD\u627E\u5230\u4E8C\u8FDB\u5236",backendTestFailed:"\u2717 \u540E\u7AEF\u6D4B\u8BD5\u5931\u8D25\uFF1A{error}",settingExportFolderName:"\u5BFC\u51FA\u6587\u4EF6\u5939",settingExportFolderDesc:"\u5BF9\u7167\u7B14\u8BB0\u751F\u6210\u4F4D\u7F6E\uFF08\u76F8\u5BF9 Vault \u6839\uFF09",cacheHeader:"\u7F13\u5B58",settingMaxCacheName:"\u6700\u5927\u7F13\u5B58\u7BC7\u6570",settingMaxCacheDesc:"\u8D85\u8FC7\u4E0A\u9650\u540E\u6309\u6700\u8FD1\u8BBF\u95EE\u65F6\u95F4\u6DD8\u6C70\u6700\u65E7\u7684\u7B14\u8BB0\u7F13\u5B58\uFF1B\u7F13\u5B58\u4FDD\u5B58\u5728\u63D2\u4EF6\u76EE\u5F55\u7684 cache.json",cachePruned:"\u5DF2\u6DD8\u6C70 {count} \u6761\u65E7\u7F13\u5B58",cachedNotesName:"\u5DF2\u7F13\u5B58\u7B14\u8BB0\uFF1A{count} \u7BC7",cachedNotesDesc:"\u7F13\u5B58\u4EE5\u6E90\u7B14\u8BB0 SHA1 + \u751F\u6210\u914D\u7F6E\u6307\u7EB9\u4F5C\u4E3A\u5931\u6548\u952E\uFF0C\u6E90\u7B14\u8BB0\u6216\u6A21\u578B\u914D\u7F6E\u4FEE\u6539\u540E\u4F1A\u663E\u793A stale \u63D0\u793A",clearAllCacheButton:"\u6E05\u9664\u6240\u6709\u7F13\u5B58",settingStreamingName:"\u6D41\u5F0F\u8F93\u51FA",settingStreamingDesc:"\u542F\u7528\u540E\u751F\u6210\u65F6\u5B9E\u65F6\u663E\u793A LLM \u8F93\u51FA\u8FDB\u5EA6\uFF08\u4EC5 OpenAI Chat \u548C Anthropic \u683C\u5F0F\u652F\u6301\uFF09"},en:{appTitle:"Parallel Reader",settingsTitle:"Parallel Reader Settings",emptyOpenNote:"Open a note, then run:",emptyNoCache:"This note has no cached parallel notes. Run:",commandGenerate:"Parallel Reader: Generate notes for current note",displayName:"Parallel Reader",ribbonOpen:"Open Parallel Reader pane",cmdRun:"Generate parallel notes for current note (cache first)",cmdRegen:"Regenerate parallel notes (bypass cache)",cmdOpenView:"Open Parallel Reader pane",cmdExport:"Export current parallel notes to Vault",cmdCopyMarkdown:"Copy current parallel notes as Markdown",cmdCancel:"Cancel current parallel-note generation",cmdClearCurrent:"Clear cache for current note",cmdClearAll:"Clear all caches",cmdCardPrev:"Focus previous summary card",cmdCardNext:"Focus next summary card",cmdCardJump:"Jump current summary card to source",actionCancel:"Cancel generation",actionRegenerate:"Regenerate",actionCopyAll:"Copy all Markdown",actionExport:"Export to Vault",staleBanner:"The source note or generation settings changed. This is stale cache.",loadingDefault:"Generating parallel notes...",loadingGenerating:"Parallel Reader: asking the LLM to read and segment the full note...",loadingSubtitle:"You can keep reading. Cards will refresh when generation finishes.",errorTitle:"Generation failed",emptyCard:"(not generated)",anchorMismatch:"Anchor did not match; scroll sync is unavailable",menuCopyMarkdown:"Copy Markdown",menuCopyPlain:"Copy plain text",menuCopyAnchor:"Copy anchor quote",menuJumpSource:"Jump to source",menuEditCard:"Edit this card",menuDeleteCard:"Delete this card",copiedMarkdown:"Copied Markdown",copiedPlain:"Copied plain text",copiedAnchor:"Copied anchor quote",copiedAllMarkdown:"Copied all Markdown",copyFailed:"Copy failed: {error}",actionFailed:"{label} failed: {error}",exported:"Exported \u2192 {path}",noCurrentNote:"No current note",cacheClearedFile:"Cleared cache: {name}",cacheClearedAll:"Cleared {count} cache entries",noCancelableJob:"No cancellable generation job",cancelRequested:"Cancel requested",cancelRequestedApiInFlight:"Cancel requested. The in-flight API request cannot be aborted immediately; its result will be ignored.",fileMenuGenerate:"Generate parallel notes",fileMenuRegen:"Regenerate parallel notes",fileMenuClear:"Clear parallel-note cache",noExportContent:"No parallel notes to export",noCopyContent:"No parallel notes to copy",noActiveCard:"No active summary card to jump",confirmRegenerateEditedCards:"These parallel-reader cards were edited manually. Regenerating will overwrite those edits. Continue?",regenerateCancelled:"Regeneration cancelled",cardDeleted:"Deleted this card",cardSaved:"Saved this card",editCardTitle:"Edit summary card",editCardTitleField:"Title",editCardGistField:"Gist",editCardBulletsField:"Bullets (one per line)",editCardCancel:"Cancel",editCardSave:"Save",openNoteFirst:"Open a note first",alreadyGenerating:"This note is already being generated",emptyNote:"The note is empty",longNoteTruncated:"Long note: only the first {count} characters will be sent to the model",generatingNotice:"Parallel Reader: asking the LLM to read and segment the full note...",noCardsReturned:"LLM returned no cards",generationDone:"Generated {count} sections{suffix}",unanchoredSuffix:" (\u26A0 {count} anchors unmatched)",cancelled:"Generation cancelled",cancelledError:"Generation cancelled",generationFailed:"Generation failed{kind}: {error}",errorCustomProviderBaseUrlRequired:"Custom provider requires an API Base URL.",errorApiBaseUrlMissing:"API Base URL is not set. Choose a provider or enter a custom base URL in settings.",errorModelMissing:"Model is not set. Enter a model ID in settings.",errorApiKeyMissing:"API key is not set. Enter an API Key in settings{hint}.",errorApiKeyEnvHint:" or set environment variable {envVar}",errorLlmNonJson:`LLM returned non-JSON: {excerpt}`,errorCustomHeadersJsonParse:"Custom headers JSON parse failed: {error}",errorCustomHeadersJsonObject:"Custom headers JSON must be an object",errorCustomHeadersLineFormat:"Custom headers lines must use `Header-Name: value`",errorProviderNonJson:`{label} returned non-JSON: -{excerpt}`,errorProviderRequestFailed:"{label} request failed: {error}",errorProviderApiStatus:"{label} API returned HTTP {status}: {excerpt}",noEditor:"Could not find the source note editor",settingUiLanguageName:"UI language",settingUiLanguageDesc:"Controls plugin UI, commands, and notices. Auto follows Obsidian/system language.",settingBackendName:"Backend",settingBackendDesc:"Backend for generating bullets: CLI reuses local login; API supports OpenAI, Anthropic, Gemini, and compatible proxies.",settingCliPathName:"CLI path (optional)",settingCliPathDesc:"Leave blank to auto-detect common paths. Obsidian launched from the GUI may not inherit shell PATH.",settingCliPathPlaceholder:"Example: /Users/you/bin/codex",settingCliTimeoutName:"CLI timeout (ms)",apiProviderHeader:"API Provider",settingProviderPresetName:"Provider preset",settingProviderPresetDesc:"OpenClaw-style provider/model setup: presets define protocol, base URL, and auth defaults.",settingApiFormatName:"API format",settingApiFormatDesc:"Wire protocol for the provider. OpenAI-compatible proxies usually use Chat Completions.",settingBaseUrlName:"Base URL",settingBaseUrlDesc:"Provider root URL, without /chat/completions. Blank uses the preset default.",settingApiKeyName:"API Key",settingApiKeyDesc:"Can be blank for local Ollama/LM Studio; most hosted providers require a key.",settingApiKeyEnvName:"API key env var",settingApiKeyEnvDesc:"Optional. Obsidian GUI may not inherit shell env; direct API Key is usually more reliable.",settingAuthTypeName:"Auth type",settingAuthTypeDesc:"Auto uses provider preset. Custom proxies can use Bearer, x-api-key, or none.",settingHeadersName:"Extra headers",settingHeadersDesc:"Optional. JSON object or one `Header: value` per line, useful for Cloudflare AI Gateway.",settingMaxTokensName:"Max output tokens",settingModelName:"Model",settingModelDescApi:"Model ID for API calls. Supports OpenClaw-style provider/model; matching provider prefixes are stripped.",settingModelDescCli:"Passed as --model for Claude Code. Usually ignored by Codex, which uses its default config.",settingMaxInputName:"Max input characters",settingMaxInputDesc:"Longer notes are truncated before sending to the model. Raise this for long-context models.",promptHeader:"Prompt",settingPromptLanguageName:"Output language",settingPromptLanguageDesc:"Controls title/gist/bullets language. Anchor is always copied verbatim from source.",settingCardRangeName:"Card count range",settingCardRangeDesc:"The model adapts segmentation within this range.",settingCustomPromptName:"Custom system prompt",settingCustomPromptDesc:"Optional. Variables: {minCards}, {maxCards}, {languageInstruction}, {schema}, {example}",settingCustomPromptPlaceholder:"Leave blank to use built-in prompt",settingTestBackendName:"Test current backend",settingTestBackendDescApi:"Sends a minimal LLM request to validate API settings.",settingTestBackendDescCli:"Runs ` --version` to verify the binary can be spawned.",backendTestFailed:"\u2717 Backend test failed: {error}",settingExportFolderName:"Export folder",settingExportFolderDesc:"Parallel-note output location, relative to the Vault root.",cacheHeader:"Cache",settingMaxCacheName:"Max cached notes",settingMaxCacheDesc:"Prunes least-recently accessed note caches above this limit. Cache is stored in plugin cache.json.",cachePruned:"Pruned {count} old cache entries",cachedNotesName:"Cached notes: {count}",cachedNotesDesc:"Cache is invalidated by source SHA1 and generation settings fingerprint.",clearAllCacheButton:"Clear all caches",settingStreamingName:"Streaming output",settingStreamingDesc:"Show real-time LLM output progress during generation (OpenAI Chat and Anthropic formats only)"}};function Ct(t){let n=t?.uiLanguage;return n==="zh"||n==="en"?n:String((typeof navigator<"u"?navigator:null)?.language||"").toLowerCase().startsWith("zh")?"zh":"en"}function b(t,n,e){let r=Ct(t),a=ae[r]||ae.en,i=ae.en[n]||ae.zh[n]||n,s=a[n]||i;return String(s).replace(/\{([a-zA-Z0-9_]+)\}/g,(o,l)=>e&&Object.hasOwn(e,l)?String(e[l]):o)}var ie="record_parallel_reader_cards";function bt(t){let n=[],e=-1,r=0,a=!1,i=!1;for(let s=0;s0&&r--,r===0&&e>=0&&(n.push(t.slice(e,s+1)),e=-1))}return n}function xe(t){let n=(t||"").trim();if(!n)return n;try{return JSON.parse(n),n}catch{}let e=n.match(/```(?:json)?\s*([\s\S]*?)```/);if(e){let a=e[1].trim();try{return JSON.parse(a),a}catch{}}let r=bt(n);r.sort((a,i)=>i.length-a.length);for(let a of r)try{return JSON.parse(a),a}catch{}return n}function P(t,n){let e=xe(t),r;try{r=JSON.parse(e)}catch{throw new Error(b(n||null,"errorLlmNonJson",{excerpt:(t||"").slice(0,500)}))}return j(r)}function j(t){return(t&&Array.isArray(t.cards)?t.cards:[]).filter(e=>!!e&&typeof e=="object").map(e=>({title:typeof e.title=="string"?e.title:"(\u65E0\u6807\u9898)",anchor:typeof e.anchor=="string"?e.anchor:"",gist:typeof e.gist=="string"?e.gist:"",bullets:Array.isArray(e.bullets)?e.bullets.filter(r=>typeof r=="string"):[]}))}function J(t){let n={type:"object",properties:{title:{type:"string"},anchor:{type:"string"},gist:{type:"string"},bullets:{type:"array",items:{type:"string"}}},required:["title","anchor","gist","bullets"]},e={type:"object",properties:{cards:{type:"array",items:n}},required:["cards"]};return t&&(n.additionalProperties=!1,e.additionalProperties=!1),e}function $e(){return{type:"json_schema",json_schema:{name:"parallel_reader_cards",strict:!0,schema:J(!0)}}}function ze(){return{format:{type:"json_schema",name:"parallel_reader_cards",strict:!0,schema:J(!0)}}}function qe(){return{name:ie,description:"Return the generated Parallel Reader summary cards.",input_schema:J(!0)}}function M(t,n){if(n?.trim())return n.trim();let e=se.default.homedir(),r=[T.default.join(e,"bin",t),T.default.join(e,".local/bin",t),T.default.join(e,".claude/local",t),T.default.join(e,".codex/bin",t),T.default.join(e,".bun/bin",t),T.default.join(e,".npm-global/bin",t),T.default.join(e,".cargo/bin",t),"/opt/homebrew/bin/"+t,"/usr/local/bin/"+t];for(let a of r)try{if(Ye.default.existsSync(a))return a}catch{}return t}function K(t,n,e,r,a){return new Promise((i,s)=>{let o,l=!1,d=null,c=u=>{l||(l=!0,d&&clearTimeout(d),s(u))},p=u=>{l||(l=!0,d&&clearTimeout(d),i(u))};try{o=(0,We.spawn)(t,n,{stdio:["pipe","pipe","pipe"],env:{...process.env,PATH:[process.env.PATH||"","/usr/local/bin","/opt/homebrew/bin",T.default.join(se.default.homedir(),".local/bin"),T.default.join(se.default.homedir(),".claude/local")].filter(Boolean).join(":")}})}catch(u){return s(new Error(`Failed to start ${t}: ${u.message}`))}let h="",f="";if(d=setTimeout(()=>{try{o.kill("SIGKILL")}catch{}c(new Error(`CLI timed out (${r}ms)`))},r),a&&a.onCancel(()=>{try{o.kill("SIGKILL")}catch{}c(new x(a.key))}),o.stdout.on("data",u=>{h+=u.toString("utf8")}),o.stderr.on("data",u=>{f+=u.toString("utf8")}),o.on("error",u=>{c(new Error(`CLI startup error: ${u.message}. Try setting an absolute CLI path.`))}),o.on("close",u=>{if(!l){if(u!==0)return c(new Error(`CLI exited with code ${u} -stderr: -${f.slice(0,1e3)}`));p({stdout:h,stderr:f})}}),e)try{o.stdin.write(e),o.stdin.end()}catch{}else try{o.stdin.end()}catch{}})}async function Xe(t,n,e,r){let a=M("claude",e.cliPath),i=["-p","--output-format","json","--append-system-prompt",t,"--disallowed-tools","Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch,TodoWrite,Task"];e.model&&i.push("--model",e.model);let{stdout:s}=await K(a,i,n,e.cliTimeoutMs,r),o;try{o=JSON.parse(s)}catch{throw new Error(`claude CLI returned a non-JSON envelope: -`+s.slice(0,500))}let l=o.result||o.content||"";return P(l,e)}async function Ze(t,n,e,r){let a=M("codex",e.cliPath),i=`<> -${t} -<> -${n} - -Output JSON directly with no explanation.`,s=["exec","--skip-git-repo-check","-"],{stdout:o}=await K(a,s,i,e.cliTimeoutMs,r);return P(o,e)}function Pe(t){let n=[`## ${t.title}`];if(t.anchor){let e=t.anchor.replace(/\s+/g," ").trim();n.push(`> ${e}`)}return t.gist&&n.push(t.gist),t.bullets&&t.bullets.length>0&&n.push(t.bullets.map(e=>`- ${e}`).join(` +{excerpt}`,errorProviderRequestFailed:"{label} request failed: {error}",errorProviderApiStatus:"{label} API returned HTTP {status}: {excerpt}",noEditor:"Could not find the source note editor",settingUiLanguageName:"UI language",settingUiLanguageDesc:"Controls plugin UI, commands, and notices. Auto follows Obsidian/system language.",settingBackendName:"Backend",settingBackendDesc:"Backend for generating bullets: CLI reuses local login; API supports OpenAI, Anthropic, Gemini, and compatible proxies.",settingCliPathName:"CLI path (optional)",settingCliPathDesc:"Leave blank to auto-detect common paths. Obsidian launched from the GUI may not inherit shell PATH.",settingCliPathPlaceholder:"Example: /Users/you/bin/codex",settingCliTimeoutName:"CLI timeout (ms)",apiProviderHeader:"API Provider",settingProviderPresetName:"Provider preset",settingProviderPresetDesc:"OpenClaw-style provider/model setup: presets define protocol, base URL, and auth defaults.",settingApiFormatName:"API format",settingApiFormatDesc:"Wire protocol for the provider. OpenAI-compatible proxies usually use Chat Completions.",settingBaseUrlName:"Base URL",settingBaseUrlDesc:"Provider root URL, without /chat/completions. Blank uses the preset default.",settingApiKeyName:"API Key",settingApiKeyDesc:"Can be blank for local Ollama/LM Studio; most hosted providers require a key.",settingApiKeyEnvName:"API key env var",settingApiKeyEnvDesc:"Optional. Obsidian GUI may not inherit shell env; direct API Key is usually more reliable.",settingAuthTypeName:"Auth type",settingAuthTypeDesc:"Auto uses provider preset. Custom proxies can use Bearer, x-api-key, or none.",settingHeadersName:"Extra headers",settingHeadersDesc:"Optional. JSON object or one `Header: value` per line, useful for Cloudflare AI Gateway.",settingMaxTokensName:"Max output tokens",settingModelName:"Model",settingModelDescApi:"Model ID for API calls. Supports OpenClaw-style provider/model; matching provider prefixes are stripped.",settingModelDescCli:"Passed as --model for Claude Code. Usually ignored by Codex, which uses its default config.",settingMaxInputName:"Max input characters",settingMaxInputDesc:"Longer notes are truncated before sending to the model. Raise this for long-context models.",promptHeader:"Prompt",settingPromptLanguageName:"Output language",settingPromptLanguageDesc:"Controls title/gist/bullets language. Anchor is always copied verbatim from source.",settingCardRangeName:"Card count range",settingCardRangeDesc:"The model adapts segmentation within this range.",settingCustomPromptName:"Custom system prompt",settingCustomPromptDesc:"Optional. Variables: {minCards}, {maxCards}, {languageInstruction}, {schema}, {example}",settingCustomPromptPlaceholder:"Leave blank to use built-in prompt",settingTestBackendName:"Test current backend",settingTestBackendDescApi:"Sends a minimal LLM request to validate API settings.",settingTestBackendDescCli:"Runs ` --version` to verify the binary can be spawned.",backendTestFailed:"\u2717 Backend test failed: {error}",settingExportFolderName:"Export folder",settingExportFolderDesc:"Parallel-note output location, relative to the Vault root.",cacheHeader:"Cache",settingMaxCacheName:"Max cached notes",settingMaxCacheDesc:"Prunes least-recently accessed note caches above this limit. Cache is stored in plugin cache.json.",cachePruned:"Pruned {count} old cache entries",cachedNotesName:"Cached notes: {count}",cachedNotesDesc:"Cache is invalidated by source SHA1 and generation settings fingerprint.",clearAllCacheButton:"Clear all caches",settingStreamingName:"Streaming output",settingStreamingDesc:"Show real-time LLM output progress during generation (OpenAI Chat and Anthropic formats only)"}};function mt(t){let n=t?.uiLanguage;return n==="zh"||n==="en"?n:String((typeof navigator<"u"?navigator:null)?.language||"").toLowerCase().startsWith("zh")?"zh":"en"}function b(t,n,e){let r=mt(t),a=te[r]||te.en,i=te.en[n]||te.zh[n]||n,s=a[n]||i;return String(s).replace(/\{([a-zA-Z0-9_]+)\}/g,(o,l)=>e&&Object.hasOwn(e,l)?String(e[l]):o)}var re="record_parallel_reader_cards";function gt(t){let n=[],e=-1,r=0,a=!1,i=!1;for(let s=0;s0&&r--,r===0&&e>=0&&(n.push(t.slice(e,s+1)),e=-1))}return n}function be(t){let n=(t||"").trim();if(!n)return n;try{return JSON.parse(n),n}catch{}let e=n.match(/```(?:json)?\s*([\s\S]*?)```/);if(e){let a=e[1].trim();try{return JSON.parse(a),a}catch{}}let r=gt(n);r.sort((a,i)=>i.length-a.length);for(let a of r)try{return JSON.parse(a),a}catch{}return n}function T(t,n){let e=be(t),r;try{r=JSON.parse(e)}catch{throw new Error(b(n||null,"errorLlmNonJson",{excerpt:(t||"").slice(0,500)}))}return j(r)}function j(t){return(t&&Array.isArray(t.cards)?t.cards:[]).filter(e=>!!e&&typeof e=="object").map(e=>({title:typeof e.title=="string"?e.title:"(\u65E0\u6807\u9898)",anchor:typeof e.anchor=="string"?e.anchor:"",gist:typeof e.gist=="string"?e.gist:"",bullets:Array.isArray(e.bullets)?e.bullets.filter(r=>typeof r=="string"):[]}))}function G(t){let n={type:"object",properties:{title:{type:"string"},anchor:{type:"string"},gist:{type:"string"},bullets:{type:"array",items:{type:"string"}}},required:["title","anchor","gist","bullets"]},e={type:"object",properties:{cards:{type:"array",items:n}},required:["cards"]};return t&&(n.additionalProperties=!1,e.additionalProperties=!1),e}function Ge(){return{type:"json_schema",json_schema:{name:"parallel_reader_cards",strict:!0,schema:G(!0)}}}function He(){return{format:{type:"json_schema",name:"parallel_reader_cards",strict:!0,schema:G(!0)}}}function Je(){return{name:re,description:"Return the generated Parallel Reader summary cards.",input_schema:G(!0)}}function ze(t,n){if(n?.trim())return n.trim();let e=$e.default.homedir(),r=[F.default.join(e,"bin",t),F.default.join(e,".local/bin",t),F.default.join(e,".claude/local",t),F.default.join(e,".codex/bin",t),F.default.join(e,".bun/bin",t),F.default.join(e,".npm-global/bin",t),F.default.join(e,".cargo/bin",t),"/opt/homebrew/bin/"+t,"/usr/local/bin/"+t];for(let a of r)try{if(Ke.default.existsSync(a))return a}catch{}return t}function we(t){let n=[`## ${t.title}`];if(t.anchor){let e=t.anchor.replace(/\s+/g," ").trim();n.push(`> ${e}`)}return t.gist&&n.push(t.gist),t.bullets&&t.bullets.length>0&&n.push(t.bullets.map(e=>`- ${e}`).join(` `)),n.join(` -`)}function Qe(t){return[t.title,t.gist||"",...(t.bullets||[]).map(n=>"\u2022 "+n)].filter(Boolean).join(` -`)}function $(t,n){let e=[`# ${t||"\u5BF9\u7167\u7B14\u8BB0"}`];for(let r of n||[])e.push(Pe(r));return e.join(` +`)}function qe(t){return[t.title,t.gist||"",...(t.bullets||[]).map(n=>"\u2022 "+n)].filter(Boolean).join(` +`)}function H(t,n){let e=[`# ${t||"\u5BF9\u7167\u7B14\u8BB0"}`];for(let r of n||[])e.push(we(r));return e.join(` -`)}function oe(t,n,e){if(!Number.isFinite(n)||n<=0)return-1;let r=Math.floor(n),a=e<0?-1:1;return t<0||t>=r?a<0?r-1:0:Math.min(r-1,Math.max(0,t+a))}function le(t,n){if(!Array.isArray(t)||n<0||n>=t.length)return-1;let e=Number(t[n]?.startLine);return Number.isFinite(e)&&e>=0?e:-1}var rt=Q(require("crypto"));var Te=2e4,wt=2,z=2,B=100,q={zh:"\u4E2D\u6587",en:"English",auto:"Auto-detect"},Ee={auto:"Auto",zh:"\u4E2D\u6587",en:"English"},y={uiLanguage:"auto",backend:"claude-code",cliPath:"",apiProvider:"anthropic",apiFormat:"anthropic-messages",apiBaseUrl:"",apiKey:"",apiKeyEnvVar:"",apiAuthType:"auto",apiHeaders:"",apiMaxTokens:4096,maxDocChars:Te,maxCacheEntries:B,promptLanguage:"zh",minCards:5,maxCards:15,customSystemPrompt:"",model:"claude-sonnet-4-6",exportFolder:"Reading/Articles",cliTimeoutMs:12e4,streaming:!0},S={"anthropic-messages":{label:"Anthropic Messages",defaultBaseUrl:"https://api.anthropic.com/v1",defaultAuthType:"x-api-key"},"openai-chat":{label:"OpenAI Chat Completions",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer",tokenLimitField:"max_tokens"},"openai-responses":{label:"OpenAI Responses",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer"},"google-generative-ai":{label:"Google Gemini generateContent",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",defaultAuthType:"x-goog-api-key"}},ke={auto:"Auto",bearer:"Authorization: Bearer","x-api-key":"x-api-key","x-goog-api-key":"x-goog-api-key","api-key":"api-key",none:"None"},F={anthropic:{label:"Anthropic",format:"anthropic-messages",baseUrl:"https://api.anthropic.com/v1",authType:"x-api-key",envVar:"ANTHROPIC_API_KEY",model:"claude-sonnet-4-6"},openai:{label:"OpenAI",format:"openai-chat",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",tokenLimitField:"max_completion_tokens",model:"gpt-5.1"},"openai-responses":{label:"OpenAI Responses",format:"openai-responses",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",modelPrefix:"openai",model:"gpt-5.1"},google:{label:"Google Gemini",format:"google-generative-ai",baseUrl:"https://generativelanguage.googleapis.com/v1beta",authType:"x-goog-api-key",envVar:"GEMINI_API_KEY",model:"gemini-3-pro-preview"},openrouter:{label:"OpenRouter",format:"openai-chat",baseUrl:"https://openrouter.ai/api/v1",authType:"bearer",envVar:"OPENROUTER_API_KEY",model:""},groq:{label:"Groq",format:"openai-chat",baseUrl:"https://api.groq.com/openai/v1",authType:"bearer",envVar:"GROQ_API_KEY",model:""},deepseek:{label:"DeepSeek",format:"openai-chat",baseUrl:"https://api.deepseek.com",authType:"bearer",envVar:"DEEPSEEK_API_KEY",model:"deepseek-chat"},moonshot:{label:"Moonshot / Kimi",format:"openai-chat",baseUrl:"https://api.moonshot.ai/v1",authType:"bearer",envVar:"MOONSHOT_API_KEY",model:"kimi-k2.5"},qianfan:{label:"Baidu Qianfan",format:"openai-chat",baseUrl:"https://qianfan.baidubce.com/v2",authType:"bearer",envVar:"QIANFAN_API_KEY",model:"deepseek-v3.2"},minimax:{label:"MiniMax (Anthropic-compatible)",format:"anthropic-messages",baseUrl:"https://api.minimax.io/anthropic",authType:"bearer",envVar:"MINIMAX_API_KEY",model:"MiniMax-M2.1"},xai:{label:"xAI",format:"openai-chat",baseUrl:"https://api.x.ai/v1",authType:"bearer",envVar:"XAI_API_KEY",model:""},mistral:{label:"Mistral",format:"openai-chat",baseUrl:"https://api.mistral.ai/v1",authType:"bearer",envVar:"MISTRAL_API_KEY",model:""},cerebras:{label:"Cerebras",format:"openai-chat",baseUrl:"https://api.cerebras.ai/v1",authType:"bearer",envVar:"CEREBRAS_API_KEY",model:""},zai:{label:"Z.AI / GLM",format:"openai-chat",baseUrl:"https://api.z.ai/api/paas/v4",authType:"bearer",envVar:"ZAI_API_KEY",model:""},ollama:{label:"Ollama (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:11434/v1",authType:"none",envVar:"",model:""},lmstudio:{label:"LM Studio (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:1234/v1",authType:"none",envVar:"",model:""},"custom-openai":{label:"Custom OpenAI-compatible",format:"openai-chat",baseUrl:"",authType:"bearer",envVar:"",model:""},"custom-anthropic":{label:"Custom Anthropic-compatible",format:"anthropic-messages",baseUrl:"",authType:"x-api-key",envVar:"",model:""}};function V(t){return rt.default.createHash("sha1").update(t,"utf8").digest("hex")}function Se(t){return Array.isArray(t)?"["+t.map(Se).join(",")+"]":t&&typeof t=="object"?"{"+Object.keys(t).sort().map(n=>JSON.stringify(n)+":"+Se(t[n])).join(",")+"}":JSON.stringify(t)}function I(t){return t==="api"||t==="anthropic-api"}function w(t){return F[t.apiProvider]||F.anthropic}function A(t){let n=w(t),e=(t.apiFormat||n.format||"").trim();return S[e]?e:n.format}function k(t){let n=A(t),e=w(t),r=(t.apiBaseUrl||"").trim();if(r)return r.replace(/\/+$/,"");if((t.apiProvider||"").startsWith("custom-"))throw new Error(b(t,"errorCustomProviderBaseUrlRequired"));let a=(e.baseUrl||S[n].defaultBaseUrl||"").trim();if(!a)throw new Error(b(t,"errorApiBaseUrlMissing"));return a.replace(/\/+$/,"")}function Fe(t){let n=(t.apiAuthType||"auto").trim();if(n&&n!=="auto")return n;let e=w(t),r=A(t);return e.authType||S[r].defaultAuthType||"bearer"}function nt(t){let n=(t.apiKey||"").trim();if(n)return n;let e=(t.apiKeyEnvVar||w(t).envVar||"").trim();return e&&process.env?.[e]?String(process.env[e]).trim():""}function L(t){let n=(t.model||"").trim();if(!n)throw new Error(b(t,"errorModelMissing"));let e=w(t),r=[t.apiProvider,e.modelPrefix].map(i=>(i||"").trim()).filter(Boolean),a=n.toLowerCase();for(let i of r){let s=i.toLowerCase();if(a.startsWith(s+"/"))return n.slice(i.length+1).trim()}return n}function Ne(t,n){let e=w(t),r=F[n]||F.anthropic,a=(t.model||"").trim(),i=!a||a===y.model||!!e.model&&a===e.model;t.apiProvider=n,t.apiFormat=r.format,t.apiBaseUrl=r.baseUrl,t.apiAuthType=r.authType||"auto",t.apiKeyEnvVar=r.envVar||"",i&&(t.model=r.model||"")}function De(t){Ee[t.uiLanguage]||(t.uiLanguage=y.uiLanguage),(!t.apiProvider||!F[t.apiProvider])&&(t.apiProvider="anthropic");let n=w(t);(!t.apiFormat||!S[t.apiFormat])&&(t.apiFormat=n.format),(!t.apiAuthType||!ke[t.apiAuthType])&&(t.apiAuthType="auto"),t.backend==="anthropic-api"&&(t.apiProvider=t.apiProvider||"anthropic",t.apiFormat=t.apiFormat||"anthropic-messages",t.apiBaseUrl=t.apiBaseUrl||F.anthropic.baseUrl,t.apiAuthType=t.apiAuthType||"x-api-key",t.apiKeyEnvVar=t.apiKeyEnvVar||"ANTHROPIC_API_KEY");let e=Number(t.apiMaxTokens);(!Number.isFinite(e)||e<=0)&&(t.apiMaxTokens=y.apiMaxTokens);let r=Number(t.maxDocChars);return(!Number.isFinite(r)||r<1e3)&&(t.maxDocChars=y.maxDocChars),t.maxCacheEntries=at(t.maxCacheEntries),q[t.promptLanguage]||(t.promptLanguage=y.promptLanguage),t.minCards=et(t.minCards,y.minCards),t.maxCards=et(t.maxCards,y.maxCards),t.maxCardstt(s)-tt(l)||i.localeCompare(o)).slice(0,r.length-e).map(([i])=>i);for(let i of a)delete t[i];return a}function ce(t){let n=De(Object.assign({},y,t||{})),e=I(n.backend),r=w(n),a=A(n),i=e?(n.apiBaseUrl||r.baseUrl||S[a]?.defaultBaseUrl||"").trim().replace(/\/+$/,""):"";return V(Se({cacheSchemaVersion:z,promptVersion:wt,maxDocChars:Number(n.maxDocChars)||y.maxDocChars,promptLanguage:n.promptLanguage,minCards:n.minCards,maxCards:n.maxCards,customSystemPromptHash:V(n.customSystemPrompt||""),backend:n.backend,model:n.model,apiProvider:e?n.apiProvider:"",apiFormat:e?a:"",apiBaseUrl:i,apiAuthType:e?Fe(n):"",apiHeadersHash:e?V(n.apiHeaders||""):"",apiMaxTokens:e?Number(n.apiMaxTokens)||y.apiMaxTokens:0,structuredOutputVersion:1}))}function ue(t,n,e){return!!t&&t.schemaVersion===z&&t.contentHash===V(n)&&t.settingsHash===ce(e)}function vt(t){return t==="en"?"Write title, gist, and bullets in English.":t==="auto"?"Write title, gist, and bullets in the main language of the source document.":"\u7528\u4E2D\u6587\u8F93\u51FA title\u3001gist \u548C bullets\u3002"}function At(t){return t==="en"?`{"cards":[ +`)}function ne(t,n,e){if(!Number.isFinite(n)||n<=0)return-1;let r=Math.floor(n),a=e<0?-1:1;return t<0||t>=r?a<0?r-1:0:Math.min(r-1,Math.max(0,t+a))}function ae(t,n){if(!Array.isArray(t)||n<0||n>=t.length)return-1;let e=Number(t[n]?.startLine);return Number.isFinite(e)&&e>=0?e:-1}var Xe=Y(require("crypto"));var Ae=2e4,yt=2,J=2,_=100,K={zh:"\u4E2D\u6587",en:"English",auto:"Auto-detect"},xe={auto:"Auto",zh:"\u4E2D\u6587",en:"English"},y={uiLanguage:"auto",backend:"api",cliPath:"",apiProvider:"anthropic",apiFormat:"anthropic-messages",apiBaseUrl:"",apiKey:"",apiKeyEnvVar:"",apiAuthType:"auto",apiHeaders:"",apiMaxTokens:4096,maxDocChars:Ae,maxCacheEntries:_,promptLanguage:"zh",minCards:5,maxCards:15,customSystemPrompt:"",model:"claude-sonnet-4-6",exportFolder:"Reading/Articles",cliTimeoutMs:12e4,streaming:!0},x={"anthropic-messages":{label:"Anthropic Messages",defaultBaseUrl:"https://api.anthropic.com/v1",defaultAuthType:"x-api-key"},"openai-chat":{label:"OpenAI Chat Completions",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer",tokenLimitField:"max_tokens"},"openai-responses":{label:"OpenAI Responses",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer"},"google-generative-ai":{label:"Google Gemini generateContent",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",defaultAuthType:"x-goog-api-key"}},Pe={auto:"Auto",bearer:"Authorization: Bearer","x-api-key":"x-api-key","x-goog-api-key":"x-goog-api-key","api-key":"api-key",none:"None"},k={anthropic:{label:"Anthropic",format:"anthropic-messages",baseUrl:"https://api.anthropic.com/v1",authType:"x-api-key",envVar:"ANTHROPIC_API_KEY",model:"claude-sonnet-4-6"},openai:{label:"OpenAI",format:"openai-chat",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",tokenLimitField:"max_completion_tokens",model:"gpt-5.1"},"openai-responses":{label:"OpenAI Responses",format:"openai-responses",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",modelPrefix:"openai",model:"gpt-5.1"},google:{label:"Google Gemini",format:"google-generative-ai",baseUrl:"https://generativelanguage.googleapis.com/v1beta",authType:"x-goog-api-key",envVar:"GEMINI_API_KEY",model:"gemini-3-pro-preview"},openrouter:{label:"OpenRouter",format:"openai-chat",baseUrl:"https://openrouter.ai/api/v1",authType:"bearer",envVar:"OPENROUTER_API_KEY",model:""},groq:{label:"Groq",format:"openai-chat",baseUrl:"https://api.groq.com/openai/v1",authType:"bearer",envVar:"GROQ_API_KEY",model:""},deepseek:{label:"DeepSeek",format:"openai-chat",baseUrl:"https://api.deepseek.com",authType:"bearer",envVar:"DEEPSEEK_API_KEY",model:"deepseek-chat"},moonshot:{label:"Moonshot / Kimi",format:"openai-chat",baseUrl:"https://api.moonshot.ai/v1",authType:"bearer",envVar:"MOONSHOT_API_KEY",model:"kimi-k2.5"},qianfan:{label:"Baidu Qianfan",format:"openai-chat",baseUrl:"https://qianfan.baidubce.com/v2",authType:"bearer",envVar:"QIANFAN_API_KEY",model:"deepseek-v3.2"},minimax:{label:"MiniMax (Anthropic-compatible)",format:"anthropic-messages",baseUrl:"https://api.minimax.io/anthropic",authType:"bearer",envVar:"MINIMAX_API_KEY",model:"MiniMax-M2.1"},xai:{label:"xAI",format:"openai-chat",baseUrl:"https://api.x.ai/v1",authType:"bearer",envVar:"XAI_API_KEY",model:""},mistral:{label:"Mistral",format:"openai-chat",baseUrl:"https://api.mistral.ai/v1",authType:"bearer",envVar:"MISTRAL_API_KEY",model:""},cerebras:{label:"Cerebras",format:"openai-chat",baseUrl:"https://api.cerebras.ai/v1",authType:"bearer",envVar:"CEREBRAS_API_KEY",model:""},zai:{label:"Z.AI / GLM",format:"openai-chat",baseUrl:"https://api.z.ai/api/paas/v4",authType:"bearer",envVar:"ZAI_API_KEY",model:""},ollama:{label:"Ollama (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:11434/v1",authType:"none",envVar:"",model:""},lmstudio:{label:"LM Studio (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:1234/v1",authType:"none",envVar:"",model:""},"custom-openai":{label:"Custom OpenAI-compatible",format:"openai-chat",baseUrl:"",authType:"bearer",envVar:"",model:""},"custom-anthropic":{label:"Custom Anthropic-compatible",format:"anthropic-messages",baseUrl:"",authType:"x-api-key",envVar:"",model:""}};function O(t){return Xe.default.createHash("sha1").update(t,"utf8").digest("hex")}function ve(t){return Array.isArray(t)?"["+t.map(ve).join(",")+"]":t&&typeof t=="object"?"{"+Object.keys(t).sort().map(n=>JSON.stringify(n)+":"+ve(t[n])).join(",")+"}":JSON.stringify(t)}function Se(t){return t==="api"||t==="anthropic-api"}function w(t){return k[t.apiProvider]||k.anthropic}function A(t){let n=w(t),e=(t.apiFormat||n.format||"").trim();return x[e]?e:n.format}function E(t){let n=A(t),e=w(t),r=(t.apiBaseUrl||"").trim();if(r)return r.replace(/\/+$/,"");if((t.apiProvider||"").startsWith("custom-"))throw new Error(b(t,"errorCustomProviderBaseUrlRequired"));let a=(e.baseUrl||x[n].defaultBaseUrl||"").trim();if(!a)throw new Error(b(t,"errorApiBaseUrlMissing"));return a.replace(/\/+$/,"")}function Te(t){let n=(t.apiAuthType||"auto").trim();if(n&&n!=="auto")return n;let e=w(t),r=A(t);return e.authType||x[r].defaultAuthType||"bearer"}function Ze(t){let n=(t.apiKey||"").trim();if(n)return n;let e=(t.apiKeyEnvVar||w(t).envVar||"").trim();return e&&process.env?.[e]?String(process.env[e]).trim():""}function M(t){let n=(t.model||"").trim();if(!n)throw new Error(b(t,"errorModelMissing"));let e=w(t),r=[t.apiProvider,e.modelPrefix].map(i=>(i||"").trim()).filter(Boolean),a=n.toLowerCase();for(let i of r){let s=i.toLowerCase();if(a.startsWith(s+"/"))return n.slice(i.length+1).trim()}return n}function Qe(t,n){let e=w(t),r=k[n]||k.anthropic,a=(t.model||"").trim(),i=!a||a===y.model||!!e.model&&a===e.model;t.apiProvider=n,t.apiFormat=r.format,t.apiBaseUrl=r.baseUrl,t.apiAuthType=r.authType||"auto",t.apiKeyEnvVar=r.envVar||"",i&&(t.model=r.model||"")}function Ee(t){xe[t.uiLanguage]||(t.uiLanguage=y.uiLanguage),(!t.apiProvider||!k[t.apiProvider])&&(t.apiProvider="anthropic");let n=w(t);(!t.apiFormat||!x[t.apiFormat])&&(t.apiFormat=n.format),(!t.apiAuthType||!Pe[t.apiAuthType])&&(t.apiAuthType="auto"),t.backend==="anthropic-api"&&(t.apiProvider=t.apiProvider||"anthropic",t.apiFormat=t.apiFormat||"anthropic-messages",t.apiBaseUrl=t.apiBaseUrl||k.anthropic.baseUrl,t.apiAuthType=t.apiAuthType||"x-api-key",t.apiKeyEnvVar=t.apiKeyEnvVar||"ANTHROPIC_API_KEY");let e=Number(t.apiMaxTokens);(!Number.isFinite(e)||e<=0)&&(t.apiMaxTokens=y.apiMaxTokens);let r=Number(t.maxDocChars);return(!Number.isFinite(r)||r<1e3)&&(t.maxDocChars=y.maxDocChars),t.maxCacheEntries=et(t.maxCacheEntries),K[t.promptLanguage]||(t.promptLanguage=y.promptLanguage),t.minCards=We(t.minCards,y.minCards),t.maxCards=We(t.maxCards,y.maxCards),t.maxCardsYe(s)-Ye(l)||i.localeCompare(o)).slice(0,r.length-e).map(([i])=>i);for(let i of a)delete t[i];return a}function ie(t){let n=Ee(Object.assign({},y,t||{})),e=Se(n.backend),r=w(n),a=A(n),i=e?(n.apiBaseUrl||r.baseUrl||x[a]?.defaultBaseUrl||"").trim().replace(/\/+$/,""):"";return O(ve({cacheSchemaVersion:J,promptVersion:yt,maxDocChars:Number(n.maxDocChars)||y.maxDocChars,promptLanguage:n.promptLanguage,minCards:n.minCards,maxCards:n.maxCards,customSystemPromptHash:O(n.customSystemPrompt||""),backend:n.backend,model:n.model,apiProvider:e?n.apiProvider:"",apiFormat:e?a:"",apiBaseUrl:i,apiAuthType:e?Te(n):"",apiHeadersHash:e?O(n.apiHeaders||""):"",apiMaxTokens:e?Number(n.apiMaxTokens)||y.apiMaxTokens:0,structuredOutputVersion:1}))}function se(t,n,e){return!!t&&t.schemaVersion===J&&t.contentHash===O(n)&&t.settingsHash===ie(e)}function Ct(t){return t==="en"?"Write title, gist, and bullets in English.":t==="auto"?"Write title, gist, and bullets in the main language of the source document.":"\u7528\u4E2D\u6587\u8F93\u51FA title\u3001gist \u548C bullets\u3002"}function bt(t){return t==="en"?`{"cards":[ {"title":"U-shaped gains","anchor":"Who benefits from AI? Overall, it shifts the score from one to seven","gist":"AI productivity gains form a U shape, with both ends benefiting most","bullets":["Top-paid software managers benefit by accelerating existing work","Low-paid workers use AI to create new side income","Middle-layer specialists gain less because prompt precision is hard to trust","Average reported benefit is 5.1/7, with 42% describing gains as unclear"]} ]}`:`{"cards":[ {"title":"U \u578B\u6536\u76CA\u66F2\u7EBF","anchor":"\u90A3\u8C01\u53C8\u4F1A\u88AB AI \u6240\u53D7\u76CA\uFF1F\u6574\u4F53\u6765\u770B\uFF0C\u5B83\u628A\u6574\u4E2A\u5206\u6570\u53D8\u6210\u4E86\u4E00\u5206\u5230\u4E03\u5206","gist":"AI \u751F\u4EA7\u529B\u6536\u76CA\u5448 U \u578B\uFF0C\u4E24\u7AEF\u53D7\u76CA\u6700\u5927\u3001\u4E2D\u95F4\u5C42\u584C\u9677","bullets":["\u6700\u9AD8\u85AA\u5C97\u4F4D\uFF08\u8F6F\u4EF6\u7BA1\u7406\uFF09\u901A\u8FC7\u52A0\u901F\u65E2\u6709\u5DE5\u4F5C\u53D7\u76CA\u6700\u5927","\u6700\u4F4E\u85AA\u5C97\u4F4D\uFF08\u5916\u5356\u5458\u3001\u56ED\u827A\u5DE5\uFF09\u7528 AI \u5F00\u526F\u4E1A\u521B\u9020\u65B0\u6536\u5165","\u4E2D\u95F4\u5C42\u79D1\u5B66\u5BB6\u3001\u5F8B\u5E08\u6536\u76CA\u6700\u5C11\uFF0C\u90E8\u5206\u56E0\u5BF9 prompt \u7CBE\u5EA6\u4FE1\u4EFB\u4E0D\u8DB3","\u5168\u4F53\u5747\u5206 5.1/7\uFF0C42% \u62A5\u544A\u6536\u76CA\u6A21\u7CCA"]} -]}`}function xt(t,n){return String(t||"").replace(/\{([a-zA-Z0-9_]+)\}/g,(e,r)=>Object.hasOwn(n,r)?String(n[r]):e)}function Pt(t,n,e,r,a,i){return t==="en"?`You are a long-form reading summary assistant. After reading the full document, split it into ${n}-${e} natural topic units. They do not need to match markdown headings; use a complete argument or topic as the unit, merging short sections and splitting long ones when needed. +]}`}function wt(t,n){return String(t||"").replace(/\{([a-zA-Z0-9_]+)\}/g,(e,r)=>Object.hasOwn(n,r)?String(n[r]):e)}function vt(t,n,e,r,a,i){return t==="en"?`You are a long-form reading summary assistant. After reading the full document, split it into ${n}-${e} natural topic units. They do not need to match markdown headings; use a complete argument or topic as the unit, merging short sections and splitting long ones when needed. Each card has one guiding sentence plus several bullets. Bullets carry details; gist is the lead-in. @@ -73,7 +65,7 @@ ${i}`:`\u4F60\u662F\u4E00\u4E2A\u957F\u6587\u9605\u8BFB\u6458\u8981\u52A9\u624B\ ${a} \u793A\u4F8B\uFF1A -${i}`}function St(t,n,e,r,a){return t==="en"?`Non-overridable output contract: +${i}`}function At(t,n,e,r,a){return t==="en"?`Non-overridable output contract: - Must output ${n}-${e} cards. - ${r} - anchor must be copied verbatim from the source and exact-substring-match the source. @@ -83,20 +75,18 @@ ${i}`}function St(t,n,e,r,a){return t==="en"?`Non-overridable output contract: - ${r} - anchor \u5FC5\u987B\u4ECE\u539F\u6587\u9010\u5B57\u590D\u5236\uFF0C\u5FC5\u987B\u80FD\u5728\u539F\u6587 exact substring match \u627E\u5230\u3002 - \u4E25\u683C\u53EA\u8F93\u51FA JSON\uFF0C\u65E0 markdown fence\u3001\u65E0\u89E3\u91CA\u3001\u65E0 tool call\u3002 -- JSON shape: ${a}`}function Me(t,n){let e=Number(n.maxDocChars)||Te,r=q[n.promptLanguage]?n.promptLanguage:y.promptLanguage,a=Math.max(1,Number(n.minCards)||y.minCards),i=Math.max(a,Number(n.maxCards)||y.maxCards),s=vt(r),o=t.length>e?t.slice(0,e)+(r==="en"?` +- JSON shape: ${a}`}function ke(t,n){let e=Number(n.maxDocChars)||Ae,r=K[n.promptLanguage]?n.promptLanguage:y.promptLanguage,a=Math.max(1,Number(n.minCards)||y.minCards),i=Math.max(a,Number(n.maxCards)||y.maxCards),s=Ct(r),o=t.length>e?t.slice(0,e)+(r==="en"?` [Document truncated]`:` -[\u6587\u6863\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD]`):t,l='{"cards":[{"title":"...","anchor":"...","gist":"...","bullets":["...","..."]}]}',d=At(r),c={minCards:a,maxCards:i,languageInstruction:s,schema:l,example:d},p=xt(n.customSystemPrompt,c).trim(),h=St(r,a,i,s,l),f=Pt(r,a,i,s,l,d),u=p?`${p} +[\u6587\u6863\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD]`):t,l='{"cards":[{"title":"...","anchor":"...","gist":"...","bullets":["...","..."]}]}',p=bt(r),c={minCards:a,maxCards:i,languageInstruction:s,schema:l,example:p},u=wt(n.customSystemPrompt,c).trim(),h=At(r,a,i,s,l),C=vt(r,a,i,s,l,p),m=u?`${u} -${h}`:f,C=r==="en"?`Source document: +${h}`:C,f=r==="en"?`Source document: ${o}`:`\u4EE5\u4E0B\u662F\u9700\u8981\u5904\u7406\u7684\u6587\u6863\u5168\u6587\uFF1A -${o}`;return{system:u,user:C}}function Tt(t){return t.choices?.[0]?.delta?.content||""}function Et(t){return t.type==="content_block_delta"&&t.delta?.text||""}function U(t){switch(t){case"openai-chat":return Tt;case"anthropic-messages":return Et;default:return null}}function Ie(t,n){let e=[],r=t.split(` -`),a=r.pop();for(let i of r){let s=i.trim();if(!s||s==="data: [DONE]"||s.startsWith("event:")||!s.startsWith("data:"))continue;let o=s.slice(s.startsWith("data: ")?6:5);try{let l=JSON.parse(o),d=n(l);d&&e.push(d)}catch{}}return{deltas:e,rest:a}}async function Le(t,n,e,r,a,i,s){if(typeof globalThis.fetch!="function")throw new Error("Streaming requires fetch API");let o=await globalThis.fetch(t,{method:"POST",headers:n,body:JSON.stringify(e),signal:i});if(!o.ok){let h=await o.text();throw new Error(b(s||null,"errorProviderApiStatus",{label:"Streaming",status:o.status,excerpt:h.slice(0,500)}))}if(!o.body)throw new Error("Response has no body for streaming");let l=o.body.getReader(),d=new TextDecoder,c="",p="";try{for(;;){let{done:h,value:f}=await l.read();if(h)break;p+=d.decode(f,{stream:!0});let u=Ie(p,r);p=u.rest;for(let C of u.deltas)c+=C;u.deltas.length>0&&a?.({accumulated:c,done:!1})}}finally{l.releaseLock()}return a?.({accumulated:c,done:!0}),c}function X(t,n){let e=t.replace(/\/+$/,"");for(let r of n)if(e.endsWith(r))return e;return e+n[0]}function kt(t,n){let e=(t||"").trim();if(!e)return{};if(e.startsWith("{")){let a;try{a=JSON.parse(e)}catch(s){throw new Error(b(n||null,"errorCustomHeadersJsonParse",{error:s.message}))}if(!a||typeof a!="object"||Array.isArray(a))throw new Error(b(n||null,"errorCustomHeadersJsonObject"));let i={};for(let[s,o]of Object.entries(a))typeof o=="string"&&s.trim()&&(i[s.trim()]=o);return i}let r={};for(let a of e.split(/\r?\n/)){let i=a.trim();if(!i||i.startsWith("#"))continue;let s=i.indexOf(":");if(s<=0)throw new Error(b(n||null,"errorCustomHeadersLineFormat"));let o=i.slice(0,s).trim(),l=i.slice(s+1).trim();o&&(r[o]=l)}return r}function Ft(t){let n=Fe(t);if(n==="none")return{};let e=nt(t);if(!e){let r=(t.apiKeyEnvVar||w(t).envVar||"").trim(),a=r?b(t,"errorApiKeyEnvHint",{envVar:r}):"";throw new Error(b(t,"errorApiKeyMissing",{hint:a}))}return n==="bearer"?{authorization:`Bearer ${e}`}:n==="x-api-key"?{"x-api-key":e}:n==="x-goog-api-key"?{"x-goog-api-key":e}:n==="api-key"?{"api-key":e}:{authorization:`Bearer ${e}`}}function G(t,n){return{"content-type":"application/json",...Ft(t),...n||{},...kt(t.apiHeaders,t)}}function Nt(t,n,e){if(t.json&&typeof t.json=="object")return t.json;try{return JSON.parse(t.text||"{}")}catch{throw new Error(b(e||null,"errorProviderNonJson",{label:n,excerpt:(t.text||"").slice(0,500)}))}}async function it(t,n,e,r,a,i){let s;try{s=await t({url:e,method:"POST",headers:r,body:JSON.stringify(a),throw:!1})}catch(o){throw new Error(b(i||null,"errorProviderRequestFailed",{label:n,error:o.message||o}))}if(s.status>=400)throw new Error(b(i||null,"errorProviderApiStatus",{label:n,status:s.status,excerpt:(s.text||"").slice(0,500)}));return Nt(s,n,i)}function Dt(t){let n=String(t?.message?t.message:t);return/(?:API (?:400|404|422):|API returned HTTP (?:400|404|422)|API 返回 HTTP (?:400|404|422))/.test(n)?/response_format|json_schema|responseJsonSchema|responseMimeType|tools?|tool_choice|unsupported|unrecognized|unknown|schema/i.test(n):!1}async function he(t,n,e,r,a,i,s){try{return await it(t,n,e,r,a,s)}catch(o){if(!i||!Dt(o))throw o;return console.warn(`[parallel-reader] ${n} structured output rejected; retrying without structured output`,o),it(t,n+" fallback",e,r,i,s)}}function Oe(t){return typeof t=="string"?t:Array.isArray(t)?t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&(n.text||n.output_text)||"").join(""):t&&typeof t=="object"&&(t.text||t.output_text)||""}function Rt(t){if(typeof t.output_text=="string")return t.output_text;let n=[],e=r=>{if(r&&typeof r!="string"){if(Array.isArray(r)){r.forEach(e);return}typeof r=="object"&&(typeof r.text=="string"&&n.push(r.text),typeof r.output_text=="string"&&n.push(r.output_text),r.type==="output_text"&&typeof r.content=="string"&&n.push(r.content),r.content&&e(r.content),r.output&&e(r.output))}};return e(t.output),n.join("")}function _e(t){let n=w(t),e=S[A(t)];return n.tokenLimitField||e?.tokenLimitField||"max_tokens"}function W(t,n,e,r){let a=!r||r.structured!==!1,i={model:L(e),max_tokens:Number(e.apiMaxTokens)||4096,system:t,messages:[{role:"user",content:n}]};return a&&(i.tools=[qe()],i.tool_choice={type:"tool",name:ie}),i}function Y(t,n,e,r){let a=!r||r.structured!==!1,i={model:L(e),messages:[{role:"system",content:t},{role:"user",content:n}]};return i[_e(e)]=Number(e.apiMaxTokens)||4096,a&&(i.response_format=$e()),i}function de(t,n,e,r){let a=!r||r.structured!==!1,i={model:L(e),instructions:t,input:n,max_output_tokens:Number(e.apiMaxTokens)||4096};return a&&(i.text=ze()),i}function pe(t,n,e,r){let a=!r||r.structured!==!1,i={temperature:0,maxOutputTokens:Number(e.apiMaxTokens)||4096};return a&&(i.responseMimeType="application/json",i.responseJsonSchema=J(!1)),{systemInstruction:{parts:[{text:t}]},contents:[{role:"user",parts:[{text:n}]}],generationConfig:i}}function Mt(t,n){let r=(Array.isArray(t?.content)?t.content:[]).find(a=>a&&a.type==="tool_use"&&a.name===ie);return r?typeof r.input=="string"?P(r.input,n):r.input&&typeof r.input=="object"?j(r.input):[]:null}async function It(t,n,e,r){let a=X(k(r),["/messages"]),i=await he(t,"Anthropic-compatible",a,G(r,{"anthropic-version":"2023-06-01"}),W(n,e,r),W(n,e,r,{structured:!1}),r),s=Mt(i,r);if(s)return s;let o=(i.content||[]).map(l=>Oe(l)).join("").trim();return P(o,r)}async function Lt(t,n,e,r){let a=X(k(r),["/chat/completions"]),s=((await he(t,"OpenAI-compatible Chat",a,G(r),Y(n,e,r),Y(n,e,r,{structured:!1}),r)).choices||[])[0]||{},o=Oe(s.message?.content||s.text||"").trim();return P(o,r)}async function Ot(t,n,e,r){let a=X(k(r),["/responses"]),i=await he(t,"OpenAI Responses",a,G(r),de(n,e,r),de(n,e,r,{structured:!1}),r);return P(Rt(i).trim(),r)}async function _t(t,n,e,r){let a=encodeURIComponent(L(r)),i=k(r);/:generateContent(?:\?|$)/.test(i)||(i=`${i.replace(/\/+$/,"")}/models/${a}:generateContent`);let s=G(r),c=((((await he(t,"Google Gemini",i,s,pe(n,e,r),pe(n,e,r,{structured:!1}),r)).candidates||[])[0]||{}).content?.parts||[]).map(p=>Oe(p)).join("").trim();return P(c,r)}async function me(t,n,e,r){switch(A(r)){case"openai-chat":return Lt(t,n,e,r);case"openai-responses":return Ot(t,n,e,r);case"google-generative-ai":return _t(t,n,e,r);default:return It(t,n,e,r)}}function Ve(t){if(!t.streaming)return!1;let n=A(t);return!!U(n)}async function Vt(t,n,e,r,a){let i=X(k(e),["/chat/completions"]),s=G(e),o=Y(t,n,e,{structured:!1});o.stream=!0;let l=U("openai-chat"),d=await Le(i,s,o,l,r,a,e);return P(d.trim(),e)}async function Bt(t,n,e,r,a){let i=X(k(e),["/messages"]),s=G(e,{"anthropic-version":"2023-06-01"}),o=W(t,n,e,{structured:!1});o.stream=!0;let l=U("anthropic-messages"),d=await Le(i,s,o,l,r,a,e);return P(d.trim(),e)}async function st(t,n,e,r,a){let i=A(e);switch(i){case"openai-chat":return Vt(t,n,e,r,a);case"anthropic-messages":return Bt(t,n,e,r,a);default:throw new Error(`Streaming not supported for format: ${i}`)}}async function ot(t,n){await me(t,'\u53EA\u8F93\u51FA JSON\uFF1A{"cards":[]}','\u8FDE\u901A\u6027\u6D4B\u8BD5\uFF1A\u8BF7\u539F\u6837\u8F93\u51FA {"cards":[]}',n);let e=A(n);return`${w(n).label} / ${S[e].label}`}function Be(t,n=80,e=.1){let r=Number(t?.top)||0,a=Math.max(0,Number(t?.height)||0),i=Math.min(Number(n)||0,a*e);return r+Math.max(0,i)}function Ut(t){return typeof requestAnimationFrame=="function"?requestAnimationFrame(t):setTimeout(()=>t(Date.now()),16)}function Gt(t){if(typeof cancelAnimationFrame=="function"){cancelAnimationFrame(t);return}clearTimeout(t)}function Ue(t,n=Ut,e=Gt){let r=null,a=()=>{r===null&&(r=n(()=>{r=null,t()}))};return a.cancel=()=>{r!==null&&(e(r),r=null)},a}var g=require("obsidian");async function Ht(t){if(t.backend==="codex"){let n=M("codex",t.cliPath),{stdout:e}=await K(n,["--version"],"",1e4);return`codex @ ${n} -${e.trim()}`}if(t.backend==="claude-code"){let n=M("claude",t.cliPath),{stdout:e}=await K(n,["--version"],"",1e4);return`claude @ ${n} -${e.trim()}`}if(I(t.backend))return ot(g.requestUrl,t);throw new Error("Unknown backend: "+t.backend)}var ge=class extends g.PluginSettingTab{constructor(n,e){super(n,e),this.plugin=e}display(){let{containerEl:n}=this,e=(i,s)=>this.plugin.t(i,s);n.empty(),n.createEl("h2",{text:e("settingsTitle")}),new g.Setting(n).setName(e("settingUiLanguageName")).setDesc(e("settingUiLanguageDesc")).addDropdown(i=>{for(let[s,o]of Object.entries(Ee))i.addOption(s,o);return i.setValue(this.plugin.settings.uiLanguage||y.uiLanguage).onChange(async s=>{this.plugin.settings.uiLanguage=s,await this.plugin.saveSettings(),this.display()})}),new g.Setting(n).setName(e("settingBackendName")).setDesc(e("settingBackendDesc")).addDropdown(i=>i.addOption("claude-code","Claude Code CLI").addOption("codex","Codex CLI").addOption("api","API / Provider").addOption("anthropic-api","Anthropic API (legacy)").setValue(this.plugin.settings.backend).onChange(async s=>{this.plugin.settings.backend=s,s==="api"&&!this.plugin.settings.apiBaseUrl&&Ne(this.plugin.settings,this.plugin.settings.apiProvider||"anthropic"),await this.plugin.saveSettings(),this.display()}));let r=I(this.plugin.settings.backend);if(!r)new g.Setting(n).setName(e("settingCliPathName")).setDesc(e("settingCliPathDesc")).addText(i=>i.setPlaceholder(e("settingCliPathPlaceholder")).setValue(this.plugin.settings.cliPath).onChange(async s=>{this.plugin.settings.cliPath=s.trim(),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingCliTimeoutName")).addText(i=>i.setValue(String(this.plugin.settings.cliTimeoutMs)).onChange(async s=>{let o=parseInt(s,10);!Number.isNaN(o)&&o>0&&(this.plugin.settings.cliTimeoutMs=o,this.plugin.saveSettingsDebounced())}));else{n.createEl("h3",{text:e("apiProviderHeader")});let i=w(this.plugin.settings);new g.Setting(n).setName(e("settingProviderPresetName")).setDesc(e("settingProviderPresetDesc")).addDropdown(s=>{for(let[o,l]of Object.entries(F))s.addOption(o,l.label);return s.setValue(this.plugin.settings.apiProvider).onChange(async o=>{Ne(this.plugin.settings,o),await this.plugin.saveSettings(),this.display()})}),new g.Setting(n).setName(e("settingApiFormatName")).setDesc(e("settingApiFormatDesc")).addDropdown(s=>{for(let[o,l]of Object.entries(S))s.addOption(o,l.label);return s.setValue(A(this.plugin.settings)).onChange(async o=>{this.plugin.settings.apiFormat=o,await this.plugin.saveSettings(),this.display()})}),new g.Setting(n).setName(e("settingBaseUrlName")).setDesc(e("settingBaseUrlDesc")).addText(s=>s.setPlaceholder((this.plugin.settings.apiProvider||"").startsWith("custom-")?"https://your-provider.example/v1":i.baseUrl||S[A(this.plugin.settings)].defaultBaseUrl).setValue(this.plugin.settings.apiBaseUrl).onChange(async o=>{this.plugin.settings.apiBaseUrl=o.trim(),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingApiKeyName")).setDesc(e("settingApiKeyDesc")).addText(s=>(s.inputEl.type="password",s.setPlaceholder("sk-...").setValue(this.plugin.settings.apiKey).onChange(async o=>{this.plugin.settings.apiKey=o.trim(),this.plugin.saveSettingsDebounced()}))),new g.Setting(n).setName(e("settingApiKeyEnvName")).setDesc(e("settingApiKeyEnvDesc")).addText(s=>s.setPlaceholder(i.envVar||"OPENAI_API_KEY").setValue(this.plugin.settings.apiKeyEnvVar).onChange(async o=>{this.plugin.settings.apiKeyEnvVar=o.trim(),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingAuthTypeName")).setDesc(e("settingAuthTypeDesc")).addDropdown(s=>{for(let[o,l]of Object.entries(ke))s.addOption(o,l);return s.setValue(this.plugin.settings.apiAuthType||"auto").onChange(async o=>{this.plugin.settings.apiAuthType=o,await this.plugin.saveSettings()})}),new g.Setting(n).setName(e("settingHeadersName")).setDesc(e("settingHeadersDesc")).addTextArea(s=>s.setPlaceholder("cf-aig-authorization: Bearer ...").setValue(this.plugin.settings.apiHeaders).onChange(async o=>{this.plugin.settings.apiHeaders=o,this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingMaxTokensName")).addText(s=>s.setValue(String(this.plugin.settings.apiMaxTokens)).onChange(async o=>{let l=parseInt(o,10);!Number.isNaN(l)&&l>0&&(this.plugin.settings.apiMaxTokens=l,this.plugin.saveSettingsDebounced())})),new g.Setting(n).setName(e("settingStreamingName")).setDesc(e("settingStreamingDesc")).addToggle(s=>s.setValue(this.plugin.settings.streaming??!0).onChange(async o=>{this.plugin.settings.streaming=o,await this.plugin.saveSettings()}))}new g.Setting(n).setName(e("settingModelName")).setDesc(e(r?"settingModelDescApi":"settingModelDescCli")).addText(i=>i.setPlaceholder(r?w(this.plugin.settings).model||"model-id":y.model).setValue(this.plugin.settings.model).onChange(async s=>{this.plugin.settings.model=s.trim()||(r?"":y.model),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingMaxInputName")).setDesc(e("settingMaxInputDesc")).addText(i=>i.setValue(String(this.plugin.settings.maxDocChars||y.maxDocChars)).onChange(async s=>{let o=parseInt(s,10);!Number.isNaN(o)&&o>=1e3&&(this.plugin.settings.maxDocChars=o,this.plugin.saveSettingsDebounced())})),n.createEl("h3",{text:e("promptHeader")}),new g.Setting(n).setName(e("settingPromptLanguageName")).setDesc(e("settingPromptLanguageDesc")).addDropdown(i=>{for(let[s,o]of Object.entries(q))i.addOption(s,o);return i.setValue(this.plugin.settings.promptLanguage||y.promptLanguage).onChange(async s=>{this.plugin.settings.promptLanguage=s,await this.plugin.saveSettings()})}),new g.Setting(n).setName(e("settingCardRangeName")).setDesc(e("settingCardRangeDesc")).addText(i=>i.setPlaceholder("min").setValue(String(this.plugin.settings.minCards||y.minCards)).onChange(async s=>{let o=parseInt(s,10);!Number.isNaN(o)&&o>0&&(this.plugin.settings.minCards=o,this.plugin.settings.maxCardsi.setPlaceholder("max").setValue(String(this.plugin.settings.maxCards||y.maxCards)).onChange(async s=>{let o=parseInt(s,10);!Number.isNaN(o)&&o>0&&(this.plugin.settings.maxCards=Math.max(o,this.plugin.settings.minCards||y.minCards),this.plugin.saveSettingsDebounced())})),new g.Setting(n).setName(e("settingCustomPromptName")).setDesc(e("settingCustomPromptDesc")).addTextArea(i=>(i.inputEl.rows=8,i.setPlaceholder(e("settingCustomPromptPlaceholder")).setValue(this.plugin.settings.customSystemPrompt||"").onChange(async s=>{this.plugin.settings.customSystemPrompt=s,this.plugin.saveSettingsDebounced()}))),new g.Setting(n).setName(e("settingTestBackendName")).setDesc(e(r?"settingTestBackendDescApi":"settingTestBackendDescCli")).addButton(i=>i.setButtonText("Test").onClick(async()=>{try{let s=await Ht(this.plugin.settings);new g.Notice(`\u2713 ${s.slice(0,180)}`,8e3)}catch(s){new g.Notice(e("backendTestFailed",{error:s.message}),1e4)}})),new g.Setting(n).setName(e("settingExportFolderName")).setDesc(e("settingExportFolderDesc")).addText(i=>i.setValue(this.plugin.settings.exportFolder).onChange(async s=>{this.plugin.settings.exportFolder=s.trim()||y.exportFolder,this.plugin.saveSettingsDebounced()})),n.createEl("h3",{text:e("cacheHeader")}),new g.Setting(n).setName(e("settingMaxCacheName")).setDesc(e("settingMaxCacheDesc")).addText(i=>{i.setValue(String(this.plugin.settings.maxCacheEntries||B));let s=async()=>{let o=parseInt(i.getValue(),10);if(Number.isFinite(o)&&o>0){this.plugin.settings.maxCacheEntries=o,await this.plugin.saveSettings();let l=await this.plugin.pruneCacheIfNeeded();l.length>0&&new g.Notice(e("cachePruned",{count:l.length})),this.display()}};return i.inputEl.addEventListener("change",s),i.inputEl.addEventListener("keydown",o=>{o.key==="Enter"&&i.inputEl.blur()}),i});let a=Object.keys(this.plugin.cache).length;new g.Setting(n).setName(e("cachedNotesName",{count:a})).setDesc(e("cachedNotesDesc")).addButton(i=>i.setButtonText(e("clearAllCacheButton")).setWarning().onClick(async()=>{let s=Object.keys(this.plugin.cache).length;await this.plugin.cacheClear(),new g.Notice(e("cacheClearedAll",{count:s})),this.display()}))}};var E=require("obsidian");function N(t,n,e,r){let a=t.createEl("button",{cls:"parallel-reader-icon-button",attr:{type:"button","aria-label":e}});return a.title=e,typeof E.setIcon=="function"?(0,E.setIcon)(a,n):a.textContent=e,a.addEventListener("click",async i=>{i.preventDefault(),i.stopPropagation();try{await r()}catch(s){console.error(s),new E.Notice(`${e} failed: `+(s.message||s))}}),a}function D(t,n,e,r,a){let i=t.createEl("button",{cls:a||"parallel-reader-text-button",attr:{type:"button"}});return n&&typeof E.setIcon=="function"&&(0,E.setIcon)(i,n),i.createSpan({text:e}),i.addEventListener("click",async s=>{s.preventDefault(),s.stopPropagation();try{await r()}catch(o){console.error(o),new E.Notice(`${e} failed: `+(o.message||o))}}),i}async function O(t,n){try{await navigator.clipboard.writeText(t),new E.Notice(n)}catch(e){new E.Notice("Copy failed: "+(e.message||e))}}function Ge(t){return String(t||"").split("/").map(n=>n.trim()).filter(n=>!!n&&n!==".."&&n!==".").join("/")}function He(t){let n=Ge(t);if(!n)return[];let e=n.split("/");return e.map((r,a)=>e.slice(0,a+1).join("/"))}async function lt(t,n){for(let e of He(n))if(!t.vault.getAbstractFileByPath(e))try{await t.vault.createFolder(e)}catch(r){if(!t.vault.getAbstractFileByPath(e))throw r}}var v=require("obsidian");var ct=require("obsidian");var fe=class extends ct.Modal{constructor(n,e,r,a){super(n),this.plugin=e,this.card=r||{},this.onSave=a}onOpen(){let{contentEl:n}=this;n.empty(),n.createEl("h2",{text:this.plugin.t("editCardTitle")});let e=this.createLabeledInput(n,this.plugin.t("editCardTitleField"),this.card.title||""),r=this.createLabeledTextarea(n,this.plugin.t("editCardGistField"),this.card.gist||"",3),a=this.createLabeledTextarea(n,this.plugin.t("editCardBulletsField"),(this.card.bullets||[]).join(` -`),8),i=n.createDiv({cls:"parallel-reader-modal-actions"});D(i,null,this.plugin.t("editCardCancel"),()=>this.close(),"parallel-reader-text-button"),D(i,null,this.plugin.t("editCardSave"),async()=>{await this.onSave({title:e.value.trim()||this.card.title||"",gist:r.value.trim(),bullets:a.value.split(/\r?\n/).map(s=>s.trim()).filter(Boolean)}),this.close()},"parallel-reader-text-button")}createLabeledInput(n,e,r){let a=n.createDiv({cls:"parallel-reader-modal-field"});a.createEl("label",{text:e});let i=a.createEl("input",{attr:{type:"text"}});return i.value=r,i}createLabeledTextarea(n,e,r,a){let i=n.createDiv({cls:"parallel-reader-modal-field"});i.createEl("label",{text:e});let s=i.createEl("textarea");return s.rows=a,s.value=r,s}};var R="parallel-reader-view",ye=class extends v.ItemView{constructor(e,r){super(e);this.stale=!1;this.loadingMessage="";this.errorMessage="";this.plugin=r,this.sections=[],this.sourceFile=null,this.cards=[],this.activeIdx=-1}getViewType(){return R}getDisplayText(){return this.plugin.t("displayName")}getIcon(){return"book-open"}onOpen(){let e=this.containerEl.children[1];return e.empty(),e.addClass("parallel-reader-container"),e.setAttr("tabindex","0"),e.addEventListener("keydown",r=>this.handleKeydown(r)),this.renderEmpty(),this.focusSummaryPane(),Promise.resolve()}onClose(){return Promise.resolve()}renderEmpty(){this.sourceFile=null,this.sections=[],this.stale=!1,this.loadingMessage="",this.errorMessage="";let e=this.containerEl.children[1];e.empty();let r=e.createDiv({cls:"parallel-reader-empty"});r.createEl("h3",{text:this.plugin.t("appTitle")}),r.createEl("p",{text:this.plugin.t("emptyOpenNote")}),r.createEl("code",{text:this.plugin.t("commandGenerate")})}focusSummaryPane(){let e=this.containerEl.children[1];return!e||typeof e.focus!="function"?!1:(e.focus({preventScroll:!0}),!0)}async loadFor(e,r,a){this.sourceFile=e,this.sections=r,this.stale=!!a,this.loadingMessage="",this.errorMessage="",this.render()}async renderLoading(e,r){this.sourceFile=e,this.sections=[],this.stale=!1,this.loadingMessage=r||this.plugin.t("loadingDefault"),this.errorMessage="",this.render()}renderStreamingPreview(e,r){this.sourceFile?.path!==e.path&&(this.sourceFile=e);let a=this.containerEl.children[1],i=a.querySelector(".parallel-reader-streaming-preview");if(i){let h=i.querySelector("pre");h&&(h.textContent=r.slice(-2e3));let f=i.querySelector(".parallel-reader-stream-counter");f&&(f.textContent=`${r.length} chars`);return}a.empty();let o=a.createDiv({cls:"parallel-reader-header"}).createDiv({cls:"parallel-reader-header-row"});o.createEl("div",{text:e.basename,cls:"parallel-reader-title"});let l=o.createDiv({cls:"parallel-reader-actions"});N(l,"square",this.plugin.t("actionCancel"),()=>{this.plugin.cancelGenerationForFile(e)});let d=a.createDiv({cls:"parallel-reader-state parallel-reader-loading parallel-reader-streaming-preview"});d.createDiv({cls:"parallel-reader-spinner"});let c=d.createEl("div",{cls:"parallel-reader-state-title"});c.createSpan({text:this.plugin.t("loadingGenerating")+" "}),c.createSpan({cls:"parallel-reader-stream-counter",text:`${r.length} chars`});let p=d.createEl("pre",{cls:"parallel-reader-stream-text"});p.textContent=r.slice(-2e3)}async renderError(e,r){this.sourceFile=e,this.sections=[],this.stale=!1,this.loadingMessage="",this.errorMessage=r||this.plugin.t("errorTitle"),this.render()}renderEmptyWithHint(e){this.sourceFile=e,this.sections=[],this.stale=!1,this.loadingMessage="",this.errorMessage="";let r=this.containerEl.children[1];r.empty();let a=r.createDiv({cls:"parallel-reader-empty"});a.createEl("h3",{text:e.basename}),a.createEl("p",{text:this.plugin.t("emptyNoCache")}),a.createEl("code",{text:this.plugin.t("commandGenerate")})}render(){let e=this.containerEl.children[1];e.empty();let a=e.createDiv({cls:"parallel-reader-header"}).createDiv({cls:"parallel-reader-header-row"});a.createEl("div",{text:this.sourceFile?.basename||"",cls:"parallel-reader-title"});let i=a.createDiv({cls:"parallel-reader-actions"});if(this.sourceFile&&(this.plugin.isGeneratingFile(this.sourceFile)?N(i,"square",this.plugin.t("actionCancel"),()=>{this.plugin.cancelGenerationForFile(this.sourceFile)}):N(i,"refresh-cw",this.plugin.t("actionRegenerate"),()=>this.plugin.runForFile(this.sourceFile,!0)),N(i,"copy",this.plugin.t("actionCopyAll"),()=>this.plugin.copyCurrentViewMarkdown()),N(i,"download",this.plugin.t("actionExport"),()=>this.exportToVault())),this.stale){let l=e.createDiv({cls:"parallel-reader-stale-banner"});l.createSpan({text:this.plugin.t("staleBanner")}),D(l,"refresh-cw",this.plugin.t("actionRegenerate"),()=>this.plugin.runForFile(this.sourceFile,!0),"parallel-reader-stale-button")}if(this.loadingMessage){let l=e.createDiv({cls:"parallel-reader-state parallel-reader-loading"});l.createDiv({cls:"parallel-reader-spinner"}),l.createEl("div",{text:this.loadingMessage,cls:"parallel-reader-state-title"}),l.createEl("div",{text:this.plugin.t("loadingSubtitle"),cls:"parallel-reader-state-subtitle"});return}if(this.errorMessage){let l=e.createDiv({cls:"parallel-reader-state parallel-reader-error"});l.createEl("div",{text:this.plugin.t("errorTitle"),cls:"parallel-reader-state-title"}),l.createEl("div",{text:this.errorMessage,cls:"parallel-reader-state-subtitle"}),D(l,"refresh-cw",this.plugin.t("actionRegenerate"),()=>this.plugin.runForFile(this.sourceFile,!0),"parallel-reader-text-button");return}let s=e.createDiv({cls:"parallel-reader-cards"});this.cards=[];let o=this.sourceFile?.path||"";this.sections.forEach((l,d)=>{let c=s.createDiv({cls:"parallel-reader-card"});c.dataset.idx=String(d),l.startLine<0&&c.addClass("parallel-reader-card-unanchored");let p=c.createEl("div",{cls:"parallel-reader-card-title"});if(p.createSpan({text:l.title}),l.startLine<0&&p.createEl("span",{text:" \u26A0",cls:"parallel-reader-warn",title:this.plugin.t("anchorMismatch")}),l.gist){let f=c.createEl("div",{cls:"parallel-reader-gist"});v.MarkdownRenderer.render(this.app,l.gist,f,o,this).catch(()=>{f.setText(l.gist)})}let h=l.bullets||[];if(h.length>0){let f=c.createEl("div",{cls:"parallel-reader-bullets-md"}),u=h.map(C=>`- ${C}`).join(` -`);v.MarkdownRenderer.render(this.app,u,f,o,this).catch(()=>{f.setText(u)})}else l.gist||c.createEl("div",{cls:"parallel-reader-empty-li",text:this.plugin.t("emptyCard")});c.addEventListener("click",f=>{let u=window.getSelection();if(u&&u.toString().length>0)return;let C=f.target;C&&C.tagName==="A"||l.startLine>=0&&this.plugin.scrollEditorToLine(l.startLine,this.sourceFile)}),c.addEventListener("contextmenu",f=>{f.preventDefault();let u=new v.Menu;u.addItem(C=>C.setTitle(this.plugin.t("menuCopyMarkdown")).setIcon("copy").onClick(()=>O(Pe(l),this.plugin.t("copiedMarkdown")))),u.addItem(C=>C.setTitle(this.plugin.t("menuCopyPlain")).setIcon("clipboard-copy").onClick(()=>O(Qe(l),this.plugin.t("copiedPlain")))),l.anchor&&u.addItem(C=>C.setTitle(this.plugin.t("menuCopyAnchor")).setIcon("quote-glyph").onClick(()=>O(l.anchor,this.plugin.t("copiedAnchor")))),u.addSeparator(),l.startLine>=0&&u.addItem(C=>C.setTitle(this.plugin.t("menuJumpSource")).setIcon("arrow-right").onClick(()=>this.plugin.scrollEditorToLine(l.startLine,this.sourceFile))),u.addSeparator(),u.addItem(C=>C.setTitle(this.plugin.t("menuEditCard")).setIcon("pencil").onClick(()=>this.openEditCardModal(d))),u.addItem(C=>C.setTitle(this.plugin.t("menuDeleteCard")).setIcon("trash").onClick(()=>this.deleteCard(d))),u.showAtMouseEvent(f)}),this.cards.push(c)}),this.activeIdx>=0&&this.cards[this.activeIdx]&&this.cards[this.activeIdx].addClass("is-active")}setActiveSection(e){e!==this.activeIdx&&(this.activeIdx>=0&&this.cards[this.activeIdx]&&this.cards[this.activeIdx].removeClass("is-active"),this.activeIdx=e,e>=0&&this.cards[e]&&(this.cards[e].addClass("is-active"),this.cards[e].scrollIntoView({block:"nearest",behavior:"smooth"})))}moveActiveSection(e){let r=oe(this.activeIdx,this.sections.length,e);return this.setActiveSection(r),this.focusSummaryPane(),r}jumpToActiveSection(){let e=le(this.sections,this.activeIdx);return e<0||!this.sourceFile?-1:(this.plugin.scrollEditorToLine(e,this.sourceFile),e)}handleKeydown(e){if(e.altKey&&e.key==="ArrowUp"){e.preventDefault(),this.moveActiveSection(-1);return}if(e.altKey&&e.key==="ArrowDown"){e.preventDefault(),this.moveActiveSection(1);return}!e.altKey&&!e.metaKey&&!e.ctrlKey&&!e.shiftKey&&e.key==="Enter"&&this.jumpToActiveSection()>=0&&e.preventDefault()}async deleteCard(e){if(!this.sourceFile)return!1;let r=te(this.sections,e);if(r.length===this.sections.length)return!1;let a=this.sections.length;return this.sections=r,this.activeIdx=re(e,a,this.activeIdx),await this.plugin.cacheReplaceCards(this.sourceFile.path,r),this.render(),new v.Notice(this.plugin.t("cardDeleted")),!0}openEditCardModal(e){return!this.sourceFile||!this.sections[e]?!1:(new fe(this.app,this.plugin,this.sections[e],async r=>{await this.updateCard(e,r)}).open(),!0)}async updateCard(e,r){if(!this.sourceFile)return!1;let a=ne(this.sections,e,r);return a.length!==this.sections.length?!1:(this.sections=a,await this.plugin.cacheReplaceCards(this.sourceFile.path,a),this.render(),new v.Notice(this.plugin.t("cardSaved")),!0)}async exportToVault(){if(!this.sourceFile)return;let e=Ge(this.plugin.settings.exportFolder),r=`${this.sourceFile.basename} - ${this.plugin.t("displayName")}.md`,a=`${e}/${r}`,i=["---",`source: [[${this.sourceFile.basename}]]`,`generated: ${new Date().toISOString().slice(0,10)}`,"tool: parallel-reader","---","",$(`${this.sourceFile.basename} \xB7 ${this.plugin.t("displayName")}`,this.sections),""].join(` -`),s=this.plugin.app;await lt(s,e);let o=s.vault.getAbstractFileByPath(a);o instanceof v.TFile?await s.vault.modify(o,i):await s.vault.create(a,i),new v.Notice(this.plugin.t("exported",{path:a}))}};async function jt(t,n,e,r){let{system:a,user:i}=Me(t,n),s,o=I(n.backend)&&Ve(n),l=o?new AbortController:null;switch(l&&e.onCancel(()=>l.abort()),n.backend){case"codex":s=await Ze(a,i,n,e);break;case"api":case"anthropic-api":o?s=await st(a,i,n,r,l.signal):s=await me(m.requestUrl,a,i,n);break;default:s=await Xe(a,i,n,e);break}let d=s.map(c=>({title:c.title,level:2,anchor:c.anchor,gist:c.gist,startLine:ee(t,c.anchor),bullets:c.bullets}));return d.sort((c,p)=>c.startLine<0&&p.startLine<0?0:c.startLine<0?1:p.startLine<0?-1:c.startLine-p.startLine),d}function ut(t,n){return n?.phase==="generating"&&t&&I(t.backend)?"cancelRequestedApiInFlight":"cancelRequested"}var je=class extends m.Plugin{constructor(){super(...arguments);this._scrollDispose=null;this._settingsSaveTimer=null;this._cacheSaveTimer=null;this._cacheDirty=!1}t(e,r){return b(this.settings||y,e,r)}async onload(){await this.loadSettings(),this.jobs=new H,this._cacheSaveTimer=null,this._cacheDirty=!1,this.addRibbonIcon("book-open",this.t("ribbonOpen"),async()=>{let e=this.getActiveView();await this.ensureView(),e?.file&&await this.syncViewToFile(e.file)}),this.registerView(R,e=>new ye(e,this)),this.addCommand({id:"parallel-reader-run",name:this.t("cmdRun"),callback:()=>this.runForActiveFile(!1)}),this.addCommand({id:"parallel-reader-regen",name:this.t("cmdRegen"),callback:()=>this.runForActiveFile(!0)}),this.addCommand({id:"parallel-reader-open-view",name:this.t("cmdOpenView"),callback:async()=>{let e=this.getActiveView();await this.ensureView(),e?.file&&this.syncViewToFile(e.file)}}),this.addCommand({id:"parallel-reader-export-current",name:this.t("cmdExport"),callback:()=>this.exportCurrentView()}),this.addCommand({id:"parallel-reader-copy-current-markdown",name:this.t("cmdCopyMarkdown"),callback:()=>this.copyCurrentViewMarkdown()}),this.addCommand({id:"parallel-reader-cancel-current",name:this.t("cmdCancel"),callback:()=>this.cancelActiveGeneration()}),this.addCommand({id:"parallel-reader-clear-current",name:this.t("cmdClearCurrent"),callback:async()=>{let e=this.getActiveView();if(!e?.file)return new m.Notice(this.t("noCurrentNote"));await this.cacheDelete(e.file.path),new m.Notice(this.t("cacheClearedFile",{name:e.file.basename}))}}),this.addCommand({id:"parallel-reader-clear-all",name:this.t("cmdClearAll"),callback:async()=>{let e=Object.keys(this.cache).length;await this.cacheClear(),new m.Notice(this.t("cacheClearedAll",{count:e}))}}),this.addCommand({id:"parallel-reader-card-prev",name:this.t("cmdCardPrev"),hotkeys:[{modifiers:["Alt"],key:"ArrowUp"}],callback:()=>this.moveActiveCard(-1)}),this.addCommand({id:"parallel-reader-card-next",name:this.t("cmdCardNext"),hotkeys:[{modifiers:["Alt"],key:"ArrowDown"}],callback:()=>this.moveActiveCard(1)}),this.addCommand({id:"parallel-reader-card-jump",name:this.t("cmdCardJump"),callback:()=>this.jumpActiveCard()}),this.addSettingTab(new ge(this.app,this)),this.registerEvent(this.app.workspace.on("active-leaf-change",()=>this.bindScrollSync())),this.registerEvent(this.app.workspace.on("file-open",e=>{e&&this.syncViewToFile(e)})),this.registerEvent(this.app.workspace.on("file-menu",(e,r)=>this.addFileMenuItems(e,r))),this.registerEvent(this.app.vault.on("rename",(e,r)=>this.handleFileRename(e,r))),this.registerEvent(this.app.vault.on("delete",e=>this.handleFileDelete(e))),this.bindScrollSync()}async onunload(){await this.flushSettingsSave(),await this.flushCacheSave(),this.app.workspace.detachLeavesOfType(R)}async loadSettings(){let r=(await this.loadData()||{}).settings||{};this.settings=De(Object.assign({},y,r)),await this.loadCache()}async saveSettings(){this._settingsSaveTimer&&(clearTimeout(this._settingsSaveTimer),this._settingsSaveTimer=null),await this.saveData({settings:this.settings})}saveSettingsDebounced(e=400){this._settingsSaveTimer&&clearTimeout(this._settingsSaveTimer),this._settingsSaveTimer=setTimeout(()=>{this._settingsSaveTimer=null,this.saveSettings().catch(r=>console.error("[parallel-reader] failed to save settings",r))},e)}async flushSettingsSave(){this._settingsSaveTimer&&(clearTimeout(this._settingsSaveTimer),this._settingsSaveTimer=null,await this.saveSettings())}async saveCache(){this._cacheSaveTimer&&(clearTimeout(this._cacheSaveTimer),this._cacheSaveTimer=null),this.pruneCache(),await this.writeCacheFile(),this._cacheDirty=!1}scheduleCacheSave(e=5e3){this._cacheDirty=!0,!this._cacheSaveTimer&&(this._cacheSaveTimer=setTimeout(()=>{this._cacheSaveTimer=null,this._cacheDirty&&this.saveCache().catch(r=>console.error("[parallel-reader] failed to save cache",r))},e))}async flushCacheSave(){this._cacheSaveTimer&&(clearTimeout(this._cacheSaveTimer),this._cacheSaveTimer=null),this._cacheDirty&&await this.saveCache()}cacheFilePath(){let e=this.app.vault.configDir||".obsidian",r=this.manifest?.id||"parallel-reader";return`${e}/plugins/${r}/cache.json`}async ensurePluginDataDir(){let e=this.app.vault.adapter,r=this.app.vault.configDir||".obsidian",a=this.manifest?.id||"parallel-reader",i=`${r}/plugins/${a}`;try{if(typeof e.exists=="function"&&await e.exists(i))return;await e.mkdir(i)}catch{}}async readCacheFile(){let e=this.app.vault.adapter;try{let r=await e.read(this.cacheFilePath()),a=JSON.parse(r);if(a&&typeof a=="object"&&a.entries&&typeof a.entries=="object")return a.entries}catch(r){let a=String(r?.message||r||"");/not found|does not exist|ENOENT/i.test(a)||console.warn("[parallel-reader] failed to read cache.json",r)}return{}}async writeCacheFile(){await this.ensurePluginDataDir(),await this.app.vault.adapter.write(this.cacheFilePath(),be(this.cache))}async loadCache(){this.cache=await this.readCacheFile(),this.pruneCache().length>0&&await this.writeCacheFile()}pruneCache(){return Re(this.cache,this.settings?.maxCacheEntries||B)}async pruneCacheIfNeeded(){let e=this.pruneCache();return e.length>0&&await this.saveCache(),e}cacheGet(e){return this.cache[e]||null}async cacheTouch(e){let r=Ce(this.cache[e]||null);return r?(this.scheduleCacheSave(),r):null}async cachePut(e,r,a,i){let s=new Date().toISOString();this.cache[e]={schemaVersion:z,contentHash:V(r),settingsHash:ce(i||this.settings),cards:a,generatedAt:s,lastAccessedAt:s},await this.saveCache()}async cacheReplaceCards(e,r){let a=this.cache[e];if(!a)return!1;let i=new Date().toISOString();return a.cards=(r||[]).map(s=>({title:s.title,anchor:s.anchor,gist:s.gist,bullets:s.bullets||[]})),a.updatedAt=i,a.lastAccessedAt=i,await this.saveCache(),!0}async cacheDelete(e){this.cache[e]&&(delete this.cache[e],await this.saveCache())}async cacheClear(){this.cache={},await this.saveCache()}async ensureView(){let{workspace:e}=this.app,r=e.getLeavesOfType(R)[0];return r||(r=e.getRightLeaf(!1),await r.setViewState({type:R,active:!0})),e.revealLeaf(r),r.view}getActiveView(){return this.app.workspace.getActiveViewOfType(m.MarkdownView)}getParallelView(){return this.app.workspace.getLeavesOfType(R)[0]?.view}moveActiveCard(e){let r=this.getParallelView();return r?.sections.length?r.moveActiveSection(e):(new m.Notice(this.t("noActiveCard")),-1)}jumpActiveCard(){let e=this.getParallelView();return!e||e.jumpToActiveSection()<0?(new m.Notice(this.t("noActiveCard")),!1):!0}isGeneratingFile(e){return!!e&&!!e.path&&this.jobs.isRunning(e.path)}cancelGenerationForFile(e){if(!e?.path)return new m.Notice(this.t("noCancelableJob")),!1;let r=this.jobs.get(e.path),a=ut(this.settings,r),i=this.jobs.cancel(e.path);return new m.Notice(i?this.t(a):this.t("noCancelableJob")),i}viewIsShowingFile(e,r){return!!e&&!!r&&e.sourceFile?.path===r.path}activeFileStillMatches(e){let r=this.getActiveView();return!r?.file||r.file.path===e.path}cancelActiveGeneration(){let e=this.getActiveView();if(e?.file&&this.cancelGenerationForFile(e.file))return;let r=this.getParallelView();r?.sourceFile?this.cancelGenerationForFile(r.sourceFile):new m.Notice(this.t("noCancelableJob"))}confirmRegenerateEditedCards(){let e=this.t("confirmRegenerateEditedCards");return typeof window<"u"&&typeof window.confirm=="function"?window.confirm(e):!0}addFileMenuItems(e,r){!(r instanceof m.TFile)||!r.path.endsWith(".md")||(e.addSeparator(),e.addItem(a=>a.setTitle(this.t("fileMenuGenerate")).setIcon("book-open").onClick(()=>this.runForFile(r,!1))),e.addItem(a=>a.setTitle(this.t("fileMenuRegen")).setIcon("refresh-cw").onClick(()=>this.runForFile(r,!0))),this.cacheGet(r.path)&&e.addItem(a=>a.setTitle(this.t("fileMenuClear")).setIcon("trash").onClick(async()=>{await this.cacheDelete(r.path),new m.Notice(this.t("cacheClearedFile",{name:r.basename}))})))}async handleFileRename(e,r){if(!(e instanceof m.TFile)||!r)return;let a=r.endsWith(".md"),i=e.path.endsWith(".md");if(!a&&!i)return;if(a&&!i){this.cache[r]&&(delete this.cache[r],await this.saveCache());let o=this.getParallelView();o&&o.sourceFile?.path===r&&o.renderEmpty();return}if(!a)return;this.cache[r]&&(this.cache[e.path]=this.cache[r],delete this.cache[r],await this.saveCache());let s=this.getParallelView();s?.sourceFile&&(s.sourceFile.path===r||s.sourceFile.path===e.path)&&(s.sourceFile=e,await this.syncViewToFile(e))}async handleFileDelete(e){if(!(e instanceof m.TFile))return;this.cache[e.path]&&(delete this.cache[e.path],await this.saveCache());let r=this.getParallelView();r?.sourceFile?.path===e.path&&r.renderEmpty()}async exportCurrentView(){let e=this.getActiveView(),r=await this.ensureView();if((!r.sourceFile||!r.sections.length)&&e?.file&&await this.syncViewToFile(e.file),!r.sourceFile||!r.sections.length){new m.Notice(this.t("noExportContent"));return}await r.exportToVault()}async copyCurrentViewMarkdown(){let e=this.getActiveView(),r=await this.ensureView();if((!r.sourceFile||!r.sections.length)&&e?.file&&await this.syncViewToFile(e.file),!r.sourceFile||!r.sections.length){new m.Notice(this.t("noCopyContent"));return}await O($(`${r.sourceFile.basename} \xB7 ${this.t("displayName")}`,r.sections),this.t("copiedAllMarkdown"))}async runForActiveFile(e){let r=this.getActiveView();if(!r?.file){new m.Notice(this.t("openNoteFirst"));return}return this.runForFile(r.file,e)}async runForFile(e,r){if(!e){new m.Notice(this.t("openNoteFirst"));return}if(this.jobs.isRunning(e.path)){new m.Notice(this.t("alreadyGenerating"));return}if(we(this.cacheGet(e.path),r)&&!this.confirmRegenerateEditedCards()){new m.Notice(this.t("regenerateCancelled"));return}let a=null;return this.jobs.start(e.path,async i=>{i.setPhase("reading");let s=await this.app.vault.read(e);if(i.throwIfCancelled(),!s.trim()){new m.Notice(this.t("emptyNote"));return}if(a=await this.ensureView(),i.throwIfCancelled(),i.setPhase("cache-check"),!r){let h=this.cacheGet(e.path);if(ue(h,s,this.settings)){await this.cacheTouch(e.path),this.activeFileStillMatches(e)&&await a.loadFor(e,this.resolveCardAnchors(s,h.cards),!1);return}}await a.renderLoading(e,this.t("loadingGenerating"));let o=Number(this.settings.maxDocChars)||y.maxDocChars;s.length>o&&new m.Notice(this.t("longNoteTruncated",{count:o})),new m.Notice(this.t("generatingNotice")),i.setPhase("generating");let l=a?h=>{!h.done&&this.viewIsShowingFile(a,e)&&a.renderStreamingPreview(e,h.accumulated)}:void 0,d=await jt(s,this.settings,i,l);if(i.throwIfCancelled(),d.length===0){new m.Notice(this.t("noCardsReturned"));return}let c=d.map(h=>({title:h.title,anchor:h.anchor,gist:h.gist,bullets:h.bullets}));i.setPhase("saving"),await this.cachePut(e.path,s,c,this.settings),i.throwIfCancelled(),this.viewIsShowingFile(a,e)&&await a.loadFor(e,d,!1);let p=d.filter(h=>h.startLine<0).length;new m.Notice(this.t("generationDone",{count:d.length,suffix:p?this.t("unanchoredSuffix",{count:p}):""}))}).catch(async i=>{if(i instanceof _){new m.Notice(i.message);return}if(i instanceof x){a&&this.viewIsShowingFile(a,e)&&await a.renderError(e,this.t("cancelledError")),new m.Notice(this.t("cancelled"));return}let s=Ae(i);console.error(i),a&&this.viewIsShowingFile(a,e)&&await a.renderError(e,i.message||String(i)),new m.Notice(this.t("generationFailed",{kind:s==="unknown"?"":` (${s})`,error:i.message||i}))})}resolveCardAnchors(e,r){let a=(r||[]).map(i=>({title:i.title,level:2,anchor:i.anchor,gist:i.gist,startLine:ee(e,i.anchor),bullets:i.bullets||[]}));return a.sort((i,s)=>i.startLine<0&&s.startLine<0?0:i.startLine<0?1:s.startLine<0?-1:i.startLine-s.startLine),a}async syncViewToFile(e){if(!e?.path?.endsWith(".md"))return;let r=this.app.workspace.getLeavesOfType(R);if(r.length===0)return;let a=r[0].view;if(!a||!this.activeFileStillMatches(e))return;let i=this.cacheGet(e.path);if(!i){await a.loadFor(e,[],!1),a.renderEmptyWithHint(e);return}let s=await this.app.vault.read(e);if(!this.activeFileStillMatches(e))return;let o=!ue(i,s,this.settings);await this.cacheTouch(e.path),await a.loadFor(e,this.resolveCardAnchors(s,i.cards),o)}bindScrollSync(){this._scrollDispose&&(this._scrollDispose(),this._scrollDispose=null);let e=this.getActiveView();if(!e)return;let r=e.editor,a=r&&r.cm,i=a?.scrollDOM?a.scrollDOM:e.contentEl.querySelector(".cm-scroller");if(!i)return;let s=Ue(()=>this.handleEditorScroll(e));i.addEventListener("scroll",s,{passive:!0}),this._scrollDispose=()=>{s.cancel(),i.removeEventListener("scroll",s)}}handleEditorScroll(e){let r=this.getParallelView();if(!r||!e.file||r.sourceFile?.path!==e.file.path)return;let a=e.editor,i=a&&a.cm;if(!i?.scrollDOM)return;let s=i.scrollDOM.getBoundingClientRect(),o=Be(s),l=0;try{let c=i.posAtCoords({x:s.left+20,y:o});c!=null&&(l=i.state.doc.lineAt(c).number-1)}catch{return}let d=-1;for(let c=0;c0&&a?.({accumulated:c,done:!1})}}finally{l.releaseLock()}return a?.({accumulated:c,done:!0}),c}function q(t,n){let e=t.replace(/\/+$/,"");for(let r of n)if(e.endsWith(r))return e;return e+n[0]}function St(t,n){let e=(t||"").trim();if(!e)return{};if(e.startsWith("{")){let a;try{a=JSON.parse(e)}catch(s){throw new Error(b(n||null,"errorCustomHeadersJsonParse",{error:s.message}))}if(!a||typeof a!="object"||Array.isArray(a))throw new Error(b(n||null,"errorCustomHeadersJsonObject"));let i={};for(let[s,o]of Object.entries(a))typeof o=="string"&&s.trim()&&(i[s.trim()]=o);return i}let r={};for(let a of e.split(/\r?\n/)){let i=a.trim();if(!i||i.startsWith("#"))continue;let s=i.indexOf(":");if(s<=0)throw new Error(b(n||null,"errorCustomHeadersLineFormat"));let o=i.slice(0,s).trim(),l=i.slice(s+1).trim();o&&(r[o]=l)}return r}function Tt(t){let n=Te(t);if(n==="none")return{};let e=Ze(t);if(!e){let r=(t.apiKeyEnvVar||w(t).envVar||"").trim(),a=r?b(t,"errorApiKeyEnvHint",{envVar:r}):"";throw new Error(b(t,"errorApiKeyMissing",{hint:a}))}return n==="bearer"?{authorization:`Bearer ${e}`}:n==="x-api-key"?{"x-api-key":e}:n==="x-goog-api-key"?{"x-goog-api-key":e}:n==="api-key"?{"api-key":e}:{authorization:`Bearer ${e}`}}function B(t,n){return{"content-type":"application/json",...Tt(t),...n||{},...St(t.apiHeaders,t)}}function Et(t,n,e){if(t.json&&typeof t.json=="object")return t.json;try{return JSON.parse(t.text||"{}")}catch{throw new Error(b(e||null,"errorProviderNonJson",{label:n,excerpt:(t.text||"").slice(0,500)}))}}async function tt(t,n,e,r,a,i){let s;try{s=await t({url:e,method:"POST",headers:r,body:JSON.stringify(a),throw:!1})}catch(o){throw new Error(b(i||null,"errorProviderRequestFailed",{label:n,error:o.message||o}))}if(s.status>=400)throw new Error(b(i||null,"errorProviderApiStatus",{label:n,status:s.status,excerpt:(s.text||"").slice(0,500)}));return Et(s,n,i)}function Ft(t){let n=String(t?.message?t.message:t);return/(?:API (?:400|404|422):|API returned HTTP (?:400|404|422)|API 返回 HTTP (?:400|404|422))/.test(n)?/response_format|json_schema|responseJsonSchema|responseMimeType|tools?|tool_choice|unsupported|unrecognized|unknown|schema/i.test(n):!1}async function ce(t,n,e,r,a,i,s){try{return await tt(t,n,e,r,a,s)}catch(o){if(!i||!Ft(o))throw o;return console.warn(`[parallel-reader] ${n} structured output rejected; retrying without structured output`,o),tt(t,n+" fallback",e,r,i,s)}}function Re(t){return typeof t=="string"?t:Array.isArray(t)?t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&(n.text||n.output_text)||"").join(""):t&&typeof t=="object"&&(t.text||t.output_text)||""}function kt(t){if(typeof t.output_text=="string")return t.output_text;let n=[],e=r=>{if(r&&typeof r!="string"){if(Array.isArray(r)){r.forEach(e);return}typeof r=="object"&&(typeof r.text=="string"&&n.push(r.text),typeof r.output_text=="string"&&n.push(r.output_text),r.type==="output_text"&&typeof r.content=="string"&&n.push(r.content),r.content&&e(r.content),r.output&&e(r.output))}};return e(t.output),n.join("")}function Me(t){let n=w(t),e=x[A(t)];return n.tokenLimitField||e?.tokenLimitField||"max_tokens"}function $(t,n,e,r){let a=!r||r.structured!==!1,i={model:M(e),max_tokens:Number(e.apiMaxTokens)||4096,system:t,messages:[{role:"user",content:n}]};return a&&(i.tools=[Je()],i.tool_choice={type:"tool",name:re}),i}function z(t,n,e,r){let a=!r||r.structured!==!1,i={model:M(e),messages:[{role:"system",content:t},{role:"user",content:n}]};return i[Me(e)]=Number(e.apiMaxTokens)||4096,a&&(i.response_format=Ge()),i}function oe(t,n,e,r){let a=!r||r.structured!==!1,i={model:M(e),instructions:t,input:n,max_output_tokens:Number(e.apiMaxTokens)||4096};return a&&(i.text=He()),i}function le(t,n,e,r){let a=!r||r.structured!==!1,i={temperature:0,maxOutputTokens:Number(e.apiMaxTokens)||4096};return a&&(i.responseMimeType="application/json",i.responseJsonSchema=G(!1)),{systemInstruction:{parts:[{text:t}]},contents:[{role:"user",parts:[{text:n}]}],generationConfig:i}}function Nt(t,n){let r=(Array.isArray(t?.content)?t.content:[]).find(a=>a&&a.type==="tool_use"&&a.name===re);return r?typeof r.input=="string"?T(r.input,n):r.input&&typeof r.input=="object"?j(r.input):[]:null}async function Dt(t,n,e,r){let a=q(E(r),["/messages"]),i=await ce(t,"Anthropic-compatible",a,B(r,{"anthropic-version":"2023-06-01"}),$(n,e,r),$(n,e,r,{structured:!1}),r),s=Nt(i,r);if(s)return s;let o=(i.content||[]).map(l=>Re(l)).join("").trim();return T(o,r)}async function Rt(t,n,e,r){let a=q(E(r),["/chat/completions"]),s=((await ce(t,"OpenAI-compatible Chat",a,B(r),z(n,e,r),z(n,e,r,{structured:!1}),r)).choices||[])[0]||{},o=Re(s.message?.content||s.text||"").trim();return T(o,r)}async function Mt(t,n,e,r){let a=q(E(r),["/responses"]),i=await ce(t,"OpenAI Responses",a,B(r),oe(n,e,r),oe(n,e,r,{structured:!1}),r);return T(kt(i).trim(),r)}async function It(t,n,e,r){let a=encodeURIComponent(M(r)),i=E(r);/:generateContent(?:\?|$)/.test(i)||(i=`${i.replace(/\/+$/,"")}/models/${a}:generateContent`);let s=B(r),c=((((await ce(t,"Google Gemini",i,s,le(n,e,r),le(n,e,r,{structured:!1}),r)).candidates||[])[0]||{}).content?.parts||[]).map(u=>Re(u)).join("").trim();return T(c,r)}async function ue(t,n,e,r){switch(A(r)){case"openai-chat":return Rt(t,n,e,r);case"openai-responses":return Mt(t,n,e,r);case"google-generative-ai":return It(t,n,e,r);default:return Dt(t,n,e,r)}}function Ie(t){if(!t.streaming)return!1;let n=A(t);return!!V(n)}async function Lt(t,n,e,r,a){let i=q(E(e),["/chat/completions"]),s=B(e),o=z(t,n,e,{structured:!1});o.stream=!0;let l=V("openai-chat"),p=await De(i,s,o,l,r,a,e);return T(p.trim(),e)}async function Ot(t,n,e,r,a){let i=q(E(e),["/messages"]),s=B(e,{"anthropic-version":"2023-06-01"}),o=$(t,n,e,{structured:!1});o.stream=!0;let l=V("anthropic-messages"),p=await De(i,s,o,l,r,a,e);return T(p.trim(),e)}async function rt(t,n,e,r,a){let i=A(e);switch(i){case"openai-chat":return Lt(t,n,e,r,a);case"anthropic-messages":return Ot(t,n,e,r,a);default:throw new Error(`Streaming not supported for format: ${i}`)}}async function nt(t,n){await ue(t,'\u53EA\u8F93\u51FA JSON\uFF1A{"cards":[]}','\u8FDE\u901A\u6027\u6D4B\u8BD5\uFF1A\u8BF7\u539F\u6837\u8F93\u51FA {"cards":[]}',n);let e=A(n);return`${w(n).label} / ${x[e].label}`}function Le(t,n=80,e=.1){let r=Number(t?.top)||0,a=Math.max(0,Number(t?.height)||0),i=Math.min(Number(n)||0,a*e);return r+Math.max(0,i)}function _t(t){return typeof requestAnimationFrame=="function"?requestAnimationFrame(t):setTimeout(()=>t(Date.now()),16)}function Vt(t){if(typeof cancelAnimationFrame=="function"){cancelAnimationFrame(t);return}clearTimeout(t)}function Oe(t,n=_t,e=Vt){let r=null,a=()=>{r===null&&(r=n(()=>{r=null,t()}))};return a.cancel=()=>{r!==null&&(e(r),r=null)},a}var g=require("obsidian");async function Bt(t){return nt(g.requestUrl,t)}var pe=class extends g.PluginSettingTab{constructor(n,e){super(n,e),this.plugin=e}display(){let{containerEl:n}=this,e=(a,i)=>this.plugin.t(a,i);n.empty(),n.createEl("h2",{text:e("settingsTitle")}),new g.Setting(n).setName(e("settingUiLanguageName")).setDesc(e("settingUiLanguageDesc")).addDropdown(a=>{for(let[i,s]of Object.entries(xe))a.addOption(i,s);return a.setValue(this.plugin.settings.uiLanguage||y.uiLanguage).onChange(async i=>{this.plugin.settings.uiLanguage=i,await this.plugin.saveSettings(),this.display()})}),n.createEl("h3",{text:e("apiProviderHeader")});{let a=w(this.plugin.settings);new g.Setting(n).setName(e("settingProviderPresetName")).setDesc(e("settingProviderPresetDesc")).addDropdown(i=>{for(let[s,o]of Object.entries(k))i.addOption(s,o.label);return i.setValue(this.plugin.settings.apiProvider).onChange(async s=>{Qe(this.plugin.settings,s),await this.plugin.saveSettings(),this.display()})}),new g.Setting(n).setName(e("settingApiFormatName")).setDesc(e("settingApiFormatDesc")).addDropdown(i=>{for(let[s,o]of Object.entries(x))i.addOption(s,o.label);return i.setValue(A(this.plugin.settings)).onChange(async s=>{this.plugin.settings.apiFormat=s,await this.plugin.saveSettings(),this.display()})}),new g.Setting(n).setName(e("settingBaseUrlName")).setDesc(e("settingBaseUrlDesc")).addText(i=>i.setPlaceholder((this.plugin.settings.apiProvider||"").startsWith("custom-")?"https://your-provider.example/v1":a.baseUrl||x[A(this.plugin.settings)].defaultBaseUrl).setValue(this.plugin.settings.apiBaseUrl).onChange(async s=>{this.plugin.settings.apiBaseUrl=s.trim(),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingApiKeyName")).setDesc(e("settingApiKeyDesc")).addText(i=>(i.inputEl.type="password",i.setPlaceholder("sk-...").setValue(this.plugin.settings.apiKey).onChange(async s=>{this.plugin.settings.apiKey=s.trim(),this.plugin.saveSettingsDebounced()}))),new g.Setting(n).setName(e("settingApiKeyEnvName")).setDesc(e("settingApiKeyEnvDesc")).addText(i=>i.setPlaceholder(a.envVar||"OPENAI_API_KEY").setValue(this.plugin.settings.apiKeyEnvVar).onChange(async s=>{this.plugin.settings.apiKeyEnvVar=s.trim(),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingAuthTypeName")).setDesc(e("settingAuthTypeDesc")).addDropdown(i=>{for(let[s,o]of Object.entries(Pe))i.addOption(s,o);return i.setValue(this.plugin.settings.apiAuthType||"auto").onChange(async s=>{this.plugin.settings.apiAuthType=s,await this.plugin.saveSettings()})}),new g.Setting(n).setName(e("settingHeadersName")).setDesc(e("settingHeadersDesc")).addTextArea(i=>i.setPlaceholder("cf-aig-authorization: Bearer ...").setValue(this.plugin.settings.apiHeaders).onChange(async s=>{this.plugin.settings.apiHeaders=s,this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingMaxTokensName")).addText(i=>i.setValue(String(this.plugin.settings.apiMaxTokens)).onChange(async s=>{let o=parseInt(s,10);!Number.isNaN(o)&&o>0&&(this.plugin.settings.apiMaxTokens=o,this.plugin.saveSettingsDebounced())})),new g.Setting(n).setName(e("settingStreamingName")).setDesc(e("settingStreamingDesc")).addToggle(i=>i.setValue(this.plugin.settings.streaming??!0).onChange(async s=>{this.plugin.settings.streaming=s,await this.plugin.saveSettings()}))}new g.Setting(n).setName(e("settingModelName")).setDesc(e("settingModelDescApi")).addText(a=>a.setPlaceholder(w(this.plugin.settings).model||"model-id").setValue(this.plugin.settings.model).onChange(async i=>{this.plugin.settings.model=i.trim(),this.plugin.saveSettingsDebounced()})),new g.Setting(n).setName(e("settingMaxInputName")).setDesc(e("settingMaxInputDesc")).addText(a=>a.setValue(String(this.plugin.settings.maxDocChars||y.maxDocChars)).onChange(async i=>{let s=parseInt(i,10);!Number.isNaN(s)&&s>=1e3&&(this.plugin.settings.maxDocChars=s,this.plugin.saveSettingsDebounced())})),n.createEl("h3",{text:e("promptHeader")}),new g.Setting(n).setName(e("settingPromptLanguageName")).setDesc(e("settingPromptLanguageDesc")).addDropdown(a=>{for(let[i,s]of Object.entries(K))a.addOption(i,s);return a.setValue(this.plugin.settings.promptLanguage||y.promptLanguage).onChange(async i=>{this.plugin.settings.promptLanguage=i,await this.plugin.saveSettings()})}),new g.Setting(n).setName(e("settingCardRangeName")).setDesc(e("settingCardRangeDesc")).addText(a=>a.setPlaceholder("min").setValue(String(this.plugin.settings.minCards||y.minCards)).onChange(async i=>{let s=parseInt(i,10);!Number.isNaN(s)&&s>0&&(this.plugin.settings.minCards=s,this.plugin.settings.maxCardsa.setPlaceholder("max").setValue(String(this.plugin.settings.maxCards||y.maxCards)).onChange(async i=>{let s=parseInt(i,10);!Number.isNaN(s)&&s>0&&(this.plugin.settings.maxCards=Math.max(s,this.plugin.settings.minCards||y.minCards),this.plugin.saveSettingsDebounced())})),new g.Setting(n).setName(e("settingCustomPromptName")).setDesc(e("settingCustomPromptDesc")).addTextArea(a=>(a.inputEl.rows=8,a.setPlaceholder(e("settingCustomPromptPlaceholder")).setValue(this.plugin.settings.customSystemPrompt||"").onChange(async i=>{this.plugin.settings.customSystemPrompt=i,this.plugin.saveSettingsDebounced()}))),new g.Setting(n).setName(e("settingTestBackendName")).setDesc(e("settingTestBackendDescApi")).addButton(a=>a.setButtonText("Test").onClick(async()=>{try{let i=await Bt(this.plugin.settings);new g.Notice(`\u2713 ${i.slice(0,180)}`,8e3)}catch(i){new g.Notice(e("backendTestFailed",{error:i.message}),1e4)}})),new g.Setting(n).setName(e("settingExportFolderName")).setDesc(e("settingExportFolderDesc")).addText(a=>a.setValue(this.plugin.settings.exportFolder).onChange(async i=>{this.plugin.settings.exportFolder=i.trim()||y.exportFolder,this.plugin.saveSettingsDebounced()})),n.createEl("h3",{text:e("cacheHeader")}),new g.Setting(n).setName(e("settingMaxCacheName")).setDesc(e("settingMaxCacheDesc")).addText(a=>{a.setValue(String(this.plugin.settings.maxCacheEntries||_));let i=async()=>{let s=parseInt(a.getValue(),10);if(Number.isFinite(s)&&s>0){this.plugin.settings.maxCacheEntries=s,await this.plugin.saveSettings();let o=await this.plugin.pruneCacheIfNeeded();o.length>0&&new g.Notice(e("cachePruned",{count:o.length})),this.display()}};return a.inputEl.addEventListener("change",i),a.inputEl.addEventListener("keydown",s=>{s.key==="Enter"&&a.inputEl.blur()}),a});let r=Object.keys(this.plugin.cache).length;new g.Setting(n).setName(e("cachedNotesName",{count:r})).setDesc(e("cachedNotesDesc")).addButton(a=>a.setButtonText(e("clearAllCacheButton")).setWarning().onClick(async()=>{let i=Object.keys(this.plugin.cache).length;await this.plugin.cacheClear(),new g.Notice(e("cacheClearedAll",{count:i})),this.display()}))}};var S=require("obsidian");function N(t,n,e,r){let a=t.createEl("button",{cls:"parallel-reader-icon-button",attr:{type:"button","aria-label":e}});return a.title=e,typeof S.setIcon=="function"?(0,S.setIcon)(a,n):a.textContent=e,a.addEventListener("click",async i=>{i.preventDefault(),i.stopPropagation();try{await r()}catch(s){console.error(s),new S.Notice(`${e} failed: `+(s.message||s))}}),a}function D(t,n,e,r,a){let i=t.createEl("button",{cls:a||"parallel-reader-text-button",attr:{type:"button"}});return n&&typeof S.setIcon=="function"&&(0,S.setIcon)(i,n),i.createSpan({text:e}),i.addEventListener("click",async s=>{s.preventDefault(),s.stopPropagation();try{await r()}catch(o){console.error(o),new S.Notice(`${e} failed: `+(o.message||o))}}),i}async function I(t,n){try{await navigator.clipboard.writeText(t),new S.Notice(n)}catch(e){new S.Notice("Copy failed: "+(e.message||e))}}function _e(t){return String(t||"").split("/").map(n=>n.trim()).filter(n=>!!n&&n!==".."&&n!==".").join("/")}function Ve(t){let n=_e(t);if(!n)return[];let e=n.split("/");return e.map((r,a)=>e.slice(0,a+1).join("/"))}async function at(t,n){for(let e of Ve(n))if(!t.vault.getAbstractFileByPath(e))try{await t.vault.createFolder(e)}catch(r){if(!t.vault.getAbstractFileByPath(e))throw r}}var v=require("obsidian");var it=require("obsidian");var de=class extends it.Modal{constructor(n,e,r,a){super(n),this.plugin=e,this.card=r||{},this.onSave=a}onOpen(){let{contentEl:n}=this;n.empty(),n.createEl("h2",{text:this.plugin.t("editCardTitle")});let e=this.createLabeledInput(n,this.plugin.t("editCardTitleField"),this.card.title||""),r=this.createLabeledTextarea(n,this.plugin.t("editCardGistField"),this.card.gist||"",3),a=this.createLabeledTextarea(n,this.plugin.t("editCardBulletsField"),(this.card.bullets||[]).join(` +`),8),i=n.createDiv({cls:"parallel-reader-modal-actions"});D(i,null,this.plugin.t("editCardCancel"),()=>this.close(),"parallel-reader-text-button"),D(i,null,this.plugin.t("editCardSave"),async()=>{await this.onSave({title:e.value.trim()||this.card.title||"",gist:r.value.trim(),bullets:a.value.split(/\r?\n/).map(s=>s.trim()).filter(Boolean)}),this.close()},"parallel-reader-text-button")}createLabeledInput(n,e,r){let a=n.createDiv({cls:"parallel-reader-modal-field"});a.createEl("label",{text:e});let i=a.createEl("input",{attr:{type:"text"}});return i.value=r,i}createLabeledTextarea(n,e,r,a){let i=n.createDiv({cls:"parallel-reader-modal-field"});i.createEl("label",{text:e});let s=i.createEl("textarea");return s.rows=a,s.value=r,s}};var R="parallel-reader-view",he=class extends v.ItemView{constructor(e,r){super(e);this.stale=!1;this.loadingMessage="";this.errorMessage="";this.plugin=r,this.sections=[],this.sourceFile=null,this.cards=[],this.activeIdx=-1}getViewType(){return R}getDisplayText(){return this.plugin.t("displayName")}getIcon(){return"book-open"}onOpen(){let e=this.containerEl.children[1];return e.empty(),e.addClass("parallel-reader-container"),e.setAttr("tabindex","0"),e.addEventListener("keydown",r=>this.handleKeydown(r)),this.renderEmpty(),this.focusSummaryPane(),Promise.resolve()}onClose(){return Promise.resolve()}renderEmpty(){this.sourceFile=null,this.sections=[],this.stale=!1,this.loadingMessage="",this.errorMessage="";let e=this.containerEl.children[1];e.empty();let r=e.createDiv({cls:"parallel-reader-empty"});r.createEl("h3",{text:this.plugin.t("appTitle")}),r.createEl("p",{text:this.plugin.t("emptyOpenNote")}),r.createEl("code",{text:this.plugin.t("commandGenerate")})}focusSummaryPane(){let e=this.containerEl.children[1];return!e||typeof e.focus!="function"?!1:(e.focus({preventScroll:!0}),!0)}async loadFor(e,r,a){this.sourceFile=e,this.sections=r,this.stale=!!a,this.loadingMessage="",this.errorMessage="",this.render()}async renderLoading(e,r){this.sourceFile=e,this.sections=[],this.stale=!1,this.loadingMessage=r||this.plugin.t("loadingDefault"),this.errorMessage="",this.render()}renderStreamingPreview(e,r){this.sourceFile?.path!==e.path&&(this.sourceFile=e);let a=this.containerEl.children[1],i=a.querySelector(".parallel-reader-streaming-preview");if(i){let h=i.querySelector("pre");h&&(h.textContent=r.slice(-2e3));let C=i.querySelector(".parallel-reader-stream-counter");C&&(C.textContent=`${r.length} chars`);return}a.empty();let o=a.createDiv({cls:"parallel-reader-header"}).createDiv({cls:"parallel-reader-header-row"});o.createEl("div",{text:e.basename,cls:"parallel-reader-title"});let l=o.createDiv({cls:"parallel-reader-actions"});N(l,"square",this.plugin.t("actionCancel"),()=>{this.plugin.cancelGenerationForFile(e)});let p=a.createDiv({cls:"parallel-reader-state parallel-reader-loading parallel-reader-streaming-preview"});p.createDiv({cls:"parallel-reader-spinner"});let c=p.createEl("div",{cls:"parallel-reader-state-title"});c.createSpan({text:this.plugin.t("loadingGenerating")+" "}),c.createSpan({cls:"parallel-reader-stream-counter",text:`${r.length} chars`});let u=p.createEl("pre",{cls:"parallel-reader-stream-text"});u.textContent=r.slice(-2e3)}async renderError(e,r){this.sourceFile=e,this.sections=[],this.stale=!1,this.loadingMessage="",this.errorMessage=r||this.plugin.t("errorTitle"),this.render()}renderEmptyWithHint(e){this.sourceFile=e,this.sections=[],this.stale=!1,this.loadingMessage="",this.errorMessage="";let r=this.containerEl.children[1];r.empty();let a=r.createDiv({cls:"parallel-reader-empty"});a.createEl("h3",{text:e.basename}),a.createEl("p",{text:this.plugin.t("emptyNoCache")}),a.createEl("code",{text:this.plugin.t("commandGenerate")})}render(){let e=this.containerEl.children[1];e.empty();let a=e.createDiv({cls:"parallel-reader-header"}).createDiv({cls:"parallel-reader-header-row"});a.createEl("div",{text:this.sourceFile?.basename||"",cls:"parallel-reader-title"});let i=a.createDiv({cls:"parallel-reader-actions"});if(this.sourceFile&&(this.plugin.isGeneratingFile(this.sourceFile)?N(i,"square",this.plugin.t("actionCancel"),()=>{this.plugin.cancelGenerationForFile(this.sourceFile)}):N(i,"refresh-cw",this.plugin.t("actionRegenerate"),()=>this.plugin.runForFile(this.sourceFile,!0)),N(i,"copy",this.plugin.t("actionCopyAll"),()=>this.plugin.copyCurrentViewMarkdown()),N(i,"download",this.plugin.t("actionExport"),()=>this.exportToVault())),this.stale){let l=e.createDiv({cls:"parallel-reader-stale-banner"});l.createSpan({text:this.plugin.t("staleBanner")}),D(l,"refresh-cw",this.plugin.t("actionRegenerate"),()=>this.plugin.runForFile(this.sourceFile,!0),"parallel-reader-stale-button")}if(this.loadingMessage){let l=e.createDiv({cls:"parallel-reader-state parallel-reader-loading"});l.createDiv({cls:"parallel-reader-spinner"}),l.createEl("div",{text:this.loadingMessage,cls:"parallel-reader-state-title"}),l.createEl("div",{text:this.plugin.t("loadingSubtitle"),cls:"parallel-reader-state-subtitle"});return}if(this.errorMessage){let l=e.createDiv({cls:"parallel-reader-state parallel-reader-error"});l.createEl("div",{text:this.plugin.t("errorTitle"),cls:"parallel-reader-state-title"}),l.createEl("div",{text:this.errorMessage,cls:"parallel-reader-state-subtitle"}),D(l,"refresh-cw",this.plugin.t("actionRegenerate"),()=>this.plugin.runForFile(this.sourceFile,!0),"parallel-reader-text-button");return}let s=e.createDiv({cls:"parallel-reader-cards"});this.cards=[];let o=this.sourceFile?.path||"";this.sections.forEach((l,p)=>{let c=s.createDiv({cls:"parallel-reader-card"});c.dataset.idx=String(p),l.startLine<0&&c.addClass("parallel-reader-card-unanchored");let u=c.createEl("div",{cls:"parallel-reader-card-title"});if(u.createSpan({text:l.title}),l.startLine<0&&u.createEl("span",{text:" \u26A0",cls:"parallel-reader-warn",title:this.plugin.t("anchorMismatch")}),l.gist){let C=c.createEl("div",{cls:"parallel-reader-gist"});v.MarkdownRenderer.render(this.app,l.gist,C,o,this).catch(()=>{C.setText(l.gist)})}let h=l.bullets||[];if(h.length>0){let C=c.createEl("div",{cls:"parallel-reader-bullets-md"}),m=h.map(f=>`- ${f}`).join(` +`);v.MarkdownRenderer.render(this.app,m,C,o,this).catch(()=>{C.setText(m)})}else l.gist||c.createEl("div",{cls:"parallel-reader-empty-li",text:this.plugin.t("emptyCard")});c.addEventListener("click",C=>{let m=window.getSelection();if(m&&m.toString().length>0)return;let f=C.target;f&&f.tagName==="A"||l.startLine>=0&&this.plugin.scrollEditorToLine(l.startLine,this.sourceFile)}),c.addEventListener("contextmenu",C=>{C.preventDefault();let m=new v.Menu;m.addItem(f=>f.setTitle(this.plugin.t("menuCopyMarkdown")).setIcon("copy").onClick(()=>I(we(l),this.plugin.t("copiedMarkdown")))),m.addItem(f=>f.setTitle(this.plugin.t("menuCopyPlain")).setIcon("clipboard-copy").onClick(()=>I(qe(l),this.plugin.t("copiedPlain")))),l.anchor&&m.addItem(f=>f.setTitle(this.plugin.t("menuCopyAnchor")).setIcon("quote-glyph").onClick(()=>I(l.anchor,this.plugin.t("copiedAnchor")))),m.addSeparator(),l.startLine>=0&&m.addItem(f=>f.setTitle(this.plugin.t("menuJumpSource")).setIcon("arrow-right").onClick(()=>this.plugin.scrollEditorToLine(l.startLine,this.sourceFile))),m.addSeparator(),m.addItem(f=>f.setTitle(this.plugin.t("menuEditCard")).setIcon("pencil").onClick(()=>this.openEditCardModal(p))),m.addItem(f=>f.setTitle(this.plugin.t("menuDeleteCard")).setIcon("trash").onClick(()=>this.deleteCard(p))),m.showAtMouseEvent(C)}),this.cards.push(c)}),this.activeIdx>=0&&this.cards[this.activeIdx]&&this.cards[this.activeIdx].addClass("is-active")}setActiveSection(e){e!==this.activeIdx&&(this.activeIdx>=0&&this.cards[this.activeIdx]&&this.cards[this.activeIdx].removeClass("is-active"),this.activeIdx=e,e>=0&&this.cards[e]&&(this.cards[e].addClass("is-active"),this.cards[e].scrollIntoView({block:"nearest",behavior:"smooth"})))}moveActiveSection(e){let r=ne(this.activeIdx,this.sections.length,e);return this.setActiveSection(r),this.focusSummaryPane(),r}jumpToActiveSection(){let e=ae(this.sections,this.activeIdx);return e<0||!this.sourceFile?-1:(this.plugin.scrollEditorToLine(e,this.sourceFile),e)}handleKeydown(e){if(e.altKey&&e.key==="ArrowUp"){e.preventDefault(),this.moveActiveSection(-1);return}if(e.altKey&&e.key==="ArrowDown"){e.preventDefault(),this.moveActiveSection(1);return}!e.altKey&&!e.metaKey&&!e.ctrlKey&&!e.shiftKey&&e.key==="Enter"&&this.jumpToActiveSection()>=0&&e.preventDefault()}async deleteCard(e){if(!this.sourceFile)return!1;let r=Z(this.sections,e);if(r.length===this.sections.length)return!1;let a=this.sections.length;return this.sections=r,this.activeIdx=Q(e,a,this.activeIdx),await this.plugin.cacheReplaceCards(this.sourceFile.path,r),this.render(),new v.Notice(this.plugin.t("cardDeleted")),!0}openEditCardModal(e){return!this.sourceFile||!this.sections[e]?!1:(new de(this.app,this.plugin,this.sections[e],async r=>{await this.updateCard(e,r)}).open(),!0)}async updateCard(e,r){if(!this.sourceFile)return!1;let a=ee(this.sections,e,r);return a.length!==this.sections.length?!1:(this.sections=a,await this.plugin.cacheReplaceCards(this.sourceFile.path,a),this.render(),new v.Notice(this.plugin.t("cardSaved")),!0)}async exportToVault(){if(!this.sourceFile)return;let e=_e(this.plugin.settings.exportFolder),r=`${this.sourceFile.basename} - ${this.plugin.t("displayName")}.md`,a=`${e}/${r}`,i=["---",`source: [[${this.sourceFile.basename}]]`,`generated: ${new Date().toISOString().slice(0,10)}`,"tool: parallel-reader","---","",H(`${this.sourceFile.basename} \xB7 ${this.plugin.t("displayName")}`,this.sections),""].join(` +`),s=this.plugin.app;await at(s,e);let o=s.vault.getAbstractFileByPath(a);o instanceof v.TFile?await s.vault.modify(o,i):await s.vault.create(a,i),new v.Notice(this.plugin.t("exported",{path:a}))}};async function Ut(t,n,e,r){let{system:a,user:i}=ke(t,n),s,o=Ie(n),l=o?new AbortController:null;l&&e.onCancel(()=>l.abort()),o?s=await rt(a,i,n,r,l.signal):s=await ue(d.requestUrl,a,i,n);let p=s.map(c=>({title:c.title,level:2,anchor:c.anchor,gist:c.gist,startLine:X(t,c.anchor),bullets:c.bullets}));return p.sort((c,u)=>c.startLine<0&&u.startLine<0?0:c.startLine<0?1:u.startLine<0?-1:c.startLine-u.startLine),p}function st(t,n){return n?.phase==="generating"&&t&&Se(t.backend)?"cancelRequestedApiInFlight":"cancelRequested"}var Be=class extends d.Plugin{constructor(){super(...arguments);this._scrollDispose=null;this._settingsSaveTimer=null;this._cacheSaveTimer=null;this._cacheDirty=!1}t(e,r){return b(this.settings||y,e,r)}async onload(){await this.loadSettings(),this.jobs=new U,this._cacheSaveTimer=null,this._cacheDirty=!1,this.addRibbonIcon("book-open",this.t("ribbonOpen"),async()=>{let e=this.getActiveView();await this.ensureView(),e?.file&&await this.syncViewToFile(e.file)}),this.registerView(R,e=>new he(e,this)),this.addCommand({id:"parallel-reader-run",name:this.t("cmdRun"),callback:()=>this.runForActiveFile(!1)}),this.addCommand({id:"parallel-reader-regen",name:this.t("cmdRegen"),callback:()=>this.runForActiveFile(!0)}),this.addCommand({id:"parallel-reader-open-view",name:this.t("cmdOpenView"),callback:async()=>{let e=this.getActiveView();await this.ensureView(),e?.file&&this.syncViewToFile(e.file)}}),this.addCommand({id:"parallel-reader-export-current",name:this.t("cmdExport"),callback:()=>this.exportCurrentView()}),this.addCommand({id:"parallel-reader-copy-current-markdown",name:this.t("cmdCopyMarkdown"),callback:()=>this.copyCurrentViewMarkdown()}),this.addCommand({id:"parallel-reader-cancel-current",name:this.t("cmdCancel"),callback:()=>this.cancelActiveGeneration()}),this.addCommand({id:"parallel-reader-clear-current",name:this.t("cmdClearCurrent"),callback:async()=>{let e=this.getActiveView();if(!e?.file)return new d.Notice(this.t("noCurrentNote"));await this.cacheDelete(e.file.path),new d.Notice(this.t("cacheClearedFile",{name:e.file.basename}))}}),this.addCommand({id:"parallel-reader-clear-all",name:this.t("cmdClearAll"),callback:async()=>{let e=Object.keys(this.cache).length;await this.cacheClear(),new d.Notice(this.t("cacheClearedAll",{count:e}))}}),this.addCommand({id:"parallel-reader-card-prev",name:this.t("cmdCardPrev"),hotkeys:[{modifiers:["Alt"],key:"ArrowUp"}],callback:()=>this.moveActiveCard(-1)}),this.addCommand({id:"parallel-reader-card-next",name:this.t("cmdCardNext"),hotkeys:[{modifiers:["Alt"],key:"ArrowDown"}],callback:()=>this.moveActiveCard(1)}),this.addCommand({id:"parallel-reader-card-jump",name:this.t("cmdCardJump"),callback:()=>this.jumpActiveCard()}),this.addSettingTab(new pe(this.app,this)),this.registerEvent(this.app.workspace.on("active-leaf-change",()=>this.bindScrollSync())),this.registerEvent(this.app.workspace.on("file-open",e=>{e&&this.syncViewToFile(e)})),this.registerEvent(this.app.workspace.on("file-menu",(e,r)=>this.addFileMenuItems(e,r))),this.registerEvent(this.app.vault.on("rename",(e,r)=>this.handleFileRename(e,r))),this.registerEvent(this.app.vault.on("delete",e=>this.handleFileDelete(e))),this.bindScrollSync()}async onunload(){await this.flushSettingsSave(),await this.flushCacheSave(),this.app.workspace.detachLeavesOfType(R)}async loadSettings(){let r=(await this.loadData()||{}).settings||{};this.settings=Ee(Object.assign({},y,r)),await this.loadCache()}async saveSettings(){this._settingsSaveTimer&&(clearTimeout(this._settingsSaveTimer),this._settingsSaveTimer=null),await this.saveData({settings:this.settings})}saveSettingsDebounced(e=400){this._settingsSaveTimer&&clearTimeout(this._settingsSaveTimer),this._settingsSaveTimer=setTimeout(()=>{this._settingsSaveTimer=null,this.saveSettings().catch(r=>console.error("[parallel-reader] failed to save settings",r))},e)}async flushSettingsSave(){this._settingsSaveTimer&&(clearTimeout(this._settingsSaveTimer),this._settingsSaveTimer=null,await this.saveSettings())}async saveCache(){this._cacheSaveTimer&&(clearTimeout(this._cacheSaveTimer),this._cacheSaveTimer=null),this.pruneCache(),await this.writeCacheFile(),this._cacheDirty=!1}scheduleCacheSave(e=5e3){this._cacheDirty=!0,!this._cacheSaveTimer&&(this._cacheSaveTimer=setTimeout(()=>{this._cacheSaveTimer=null,this._cacheDirty&&this.saveCache().catch(r=>console.error("[parallel-reader] failed to save cache",r))},e))}async flushCacheSave(){this._cacheSaveTimer&&(clearTimeout(this._cacheSaveTimer),this._cacheSaveTimer=null),this._cacheDirty&&await this.saveCache()}cacheFilePath(){let e=this.app.vault.configDir||".obsidian",r=this.manifest?.id||"parallel-reader";return`${e}/plugins/${r}/cache.json`}async ensurePluginDataDir(){let e=this.app.vault.adapter,r=this.app.vault.configDir||".obsidian",a=this.manifest?.id||"parallel-reader",i=`${r}/plugins/${a}`;try{if(typeof e.exists=="function"&&await e.exists(i))return;await e.mkdir(i)}catch{}}async readCacheFile(){let e=this.app.vault.adapter;try{let r=await e.read(this.cacheFilePath()),a=JSON.parse(r);if(a&&typeof a=="object"&&a.entries&&typeof a.entries=="object")return a.entries}catch(r){let a=String(r?.message||r||"");/not found|does not exist|ENOENT/i.test(a)||console.warn("[parallel-reader] failed to read cache.json",r)}return{}}async writeCacheFile(){await this.ensurePluginDataDir(),await this.app.vault.adapter.write(this.cacheFilePath(),ge(this.cache))}async loadCache(){this.cache=await this.readCacheFile(),this.pruneCache().length>0&&await this.writeCacheFile()}pruneCache(){return Fe(this.cache,this.settings?.maxCacheEntries||_)}async pruneCacheIfNeeded(){let e=this.pruneCache();return e.length>0&&await this.saveCache(),e}cacheGet(e){return this.cache[e]||null}async cacheTouch(e){let r=me(this.cache[e]||null);return r?(this.scheduleCacheSave(),r):null}async cachePut(e,r,a,i){let s=new Date().toISOString();this.cache[e]={schemaVersion:J,contentHash:O(r),settingsHash:ie(i||this.settings),cards:a,generatedAt:s,lastAccessedAt:s},await this.saveCache()}async cacheReplaceCards(e,r){let a=this.cache[e];if(!a)return!1;let i=new Date().toISOString();return a.cards=(r||[]).map(s=>({title:s.title,anchor:s.anchor,gist:s.gist,bullets:s.bullets||[]})),a.updatedAt=i,a.lastAccessedAt=i,await this.saveCache(),!0}async cacheDelete(e){this.cache[e]&&(delete this.cache[e],await this.saveCache())}async cacheClear(){this.cache={},await this.saveCache()}async ensureView(){let{workspace:e}=this.app,r=e.getLeavesOfType(R)[0];return r||(r=e.getRightLeaf(!1),await r.setViewState({type:R,active:!0})),e.revealLeaf(r),r.view}getActiveView(){return this.app.workspace.getActiveViewOfType(d.MarkdownView)}getParallelView(){return this.app.workspace.getLeavesOfType(R)[0]?.view}moveActiveCard(e){let r=this.getParallelView();return r?.sections.length?r.moveActiveSection(e):(new d.Notice(this.t("noActiveCard")),-1)}jumpActiveCard(){let e=this.getParallelView();return!e||e.jumpToActiveSection()<0?(new d.Notice(this.t("noActiveCard")),!1):!0}isGeneratingFile(e){return!!e&&!!e.path&&this.jobs.isRunning(e.path)}cancelGenerationForFile(e){if(!e?.path)return new d.Notice(this.t("noCancelableJob")),!1;let r=this.jobs.get(e.path),a=st(this.settings,r),i=this.jobs.cancel(e.path);return new d.Notice(i?this.t(a):this.t("noCancelableJob")),i}viewIsShowingFile(e,r){return!!e&&!!r&&e.sourceFile?.path===r.path}activeFileStillMatches(e){let r=this.getActiveView();return!r?.file||r.file.path===e.path}cancelActiveGeneration(){let e=this.getActiveView();if(e?.file&&this.cancelGenerationForFile(e.file))return;let r=this.getParallelView();r?.sourceFile?this.cancelGenerationForFile(r.sourceFile):new d.Notice(this.t("noCancelableJob"))}confirmRegenerateEditedCards(){let e=this.t("confirmRegenerateEditedCards");return typeof window<"u"&&typeof window.confirm=="function"?window.confirm(e):!0}addFileMenuItems(e,r){!(r instanceof d.TFile)||!r.path.endsWith(".md")||(e.addSeparator(),e.addItem(a=>a.setTitle(this.t("fileMenuGenerate")).setIcon("book-open").onClick(()=>this.runForFile(r,!1))),e.addItem(a=>a.setTitle(this.t("fileMenuRegen")).setIcon("refresh-cw").onClick(()=>this.runForFile(r,!0))),this.cacheGet(r.path)&&e.addItem(a=>a.setTitle(this.t("fileMenuClear")).setIcon("trash").onClick(async()=>{await this.cacheDelete(r.path),new d.Notice(this.t("cacheClearedFile",{name:r.basename}))})))}async handleFileRename(e,r){if(!(e instanceof d.TFile)||!r)return;let a=r.endsWith(".md"),i=e.path.endsWith(".md");if(!a&&!i)return;if(a&&!i){this.cache[r]&&(delete this.cache[r],await this.saveCache());let o=this.getParallelView();o&&o.sourceFile?.path===r&&o.renderEmpty();return}if(!a)return;this.cache[r]&&(this.cache[e.path]=this.cache[r],delete this.cache[r],await this.saveCache());let s=this.getParallelView();s?.sourceFile&&(s.sourceFile.path===r||s.sourceFile.path===e.path)&&(s.sourceFile=e,await this.syncViewToFile(e))}async handleFileDelete(e){if(!(e instanceof d.TFile))return;this.cache[e.path]&&(delete this.cache[e.path],await this.saveCache());let r=this.getParallelView();r?.sourceFile?.path===e.path&&r.renderEmpty()}async exportCurrentView(){let e=this.getActiveView(),r=await this.ensureView();if((!r.sourceFile||!r.sections.length)&&e?.file&&await this.syncViewToFile(e.file),!r.sourceFile||!r.sections.length){new d.Notice(this.t("noExportContent"));return}await r.exportToVault()}async copyCurrentViewMarkdown(){let e=this.getActiveView(),r=await this.ensureView();if((!r.sourceFile||!r.sections.length)&&e?.file&&await this.syncViewToFile(e.file),!r.sourceFile||!r.sections.length){new d.Notice(this.t("noCopyContent"));return}await I(H(`${r.sourceFile.basename} \xB7 ${this.t("displayName")}`,r.sections),this.t("copiedAllMarkdown"))}async runForActiveFile(e){let r=this.getActiveView();if(!r?.file){new d.Notice(this.t("openNoteFirst"));return}return this.runForFile(r.file,e)}async runForFile(e,r){if(!e){new d.Notice(this.t("openNoteFirst"));return}if(this.jobs.isRunning(e.path)){new d.Notice(this.t("alreadyGenerating"));return}if(fe(this.cacheGet(e.path),r)&&!this.confirmRegenerateEditedCards()){new d.Notice(this.t("regenerateCancelled"));return}let a=null;return this.jobs.start(e.path,async i=>{i.setPhase("reading");let s=await this.app.vault.read(e);if(i.throwIfCancelled(),!s.trim()){new d.Notice(this.t("emptyNote"));return}if(a=await this.ensureView(),i.throwIfCancelled(),i.setPhase("cache-check"),!r){let h=this.cacheGet(e.path);if(se(h,s,this.settings)){await this.cacheTouch(e.path),this.activeFileStillMatches(e)&&await a.loadFor(e,this.resolveCardAnchors(s,h.cards),!1);return}}await a.renderLoading(e,this.t("loadingGenerating"));let o=Number(this.settings.maxDocChars)||y.maxDocChars;s.length>o&&new d.Notice(this.t("longNoteTruncated",{count:o})),new d.Notice(this.t("generatingNotice")),i.setPhase("generating");let l=a?h=>{!h.done&&this.viewIsShowingFile(a,e)&&a.renderStreamingPreview(e,h.accumulated)}:void 0,p=await Ut(s,this.settings,i,l);if(i.throwIfCancelled(),p.length===0){new d.Notice(this.t("noCardsReturned"));return}let c=p.map(h=>({title:h.title,anchor:h.anchor,gist:h.gist,bullets:h.bullets}));i.setPhase("saving"),await this.cachePut(e.path,s,c,this.settings),i.throwIfCancelled(),this.viewIsShowingFile(a,e)&&await a.loadFor(e,p,!1);let u=p.filter(h=>h.startLine<0).length;new d.Notice(this.t("generationDone",{count:p.length,suffix:u?this.t("unanchoredSuffix",{count:u}):""}))}).catch(async i=>{if(i instanceof L){new d.Notice(i.message);return}if(i instanceof P){a&&this.viewIsShowingFile(a,e)&&await a.renderError(e,this.t("cancelledError")),new d.Notice(this.t("cancelled"));return}let s=Ce(i);console.error(i),a&&this.viewIsShowingFile(a,e)&&await a.renderError(e,i.message||String(i)),new d.Notice(this.t("generationFailed",{kind:s==="unknown"?"":` (${s})`,error:i.message||i}))})}resolveCardAnchors(e,r){let a=(r||[]).map(i=>({title:i.title,level:2,anchor:i.anchor,gist:i.gist,startLine:X(e,i.anchor),bullets:i.bullets||[]}));return a.sort((i,s)=>i.startLine<0&&s.startLine<0?0:i.startLine<0?1:s.startLine<0?-1:i.startLine-s.startLine),a}async syncViewToFile(e){if(!e?.path?.endsWith(".md"))return;let r=this.app.workspace.getLeavesOfType(R);if(r.length===0)return;let a=r[0].view;if(!a||!this.activeFileStillMatches(e))return;let i=this.cacheGet(e.path);if(!i){await a.loadFor(e,[],!1),a.renderEmptyWithHint(e);return}let s=await this.app.vault.read(e);if(!this.activeFileStillMatches(e))return;let o=!se(i,s,this.settings);await this.cacheTouch(e.path),await a.loadFor(e,this.resolveCardAnchors(s,i.cards),o)}bindScrollSync(){this._scrollDispose&&(this._scrollDispose(),this._scrollDispose=null);let e=this.getActiveView();if(!e)return;let r=e.editor,a=r&&r.cm,i=a?.scrollDOM?a.scrollDOM:e.contentEl.querySelector(".cm-scroller");if(!i)return;let s=Oe(()=>this.handleEditorScroll(e));i.addEventListener("scroll",s,{passive:!0}),this._scrollDispose=()=>{s.cancel(),i.removeEventListener("scroll",s)}}handleEditorScroll(e){let r=this.getParallelView();if(!r||!e.file||r.sourceFile?.path!==e.file.path)return;let a=e.editor,i=a&&a.cm;if(!i?.scrollDOM)return;let s=i.scrollDOM.getBoundingClientRect(),o=Le(s),l=0;try{let c=i.posAtCoords({x:s.left+20,y:o});c!=null&&(l=i.state.doc.lineAt(c).number-1)}catch{return}let p=-1;for(let c=0;c abortController.abort()); } - switch (settings.backend) { - case 'codex': - cards = await summarizeViaCodex(system, user, settings, job); - break; - case 'api': - case 'anthropic-api': - if (useStreaming) { - cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController!.signal); - } else { - cards = await summarizeViaApi(requestUrl, system, user, settings); - } - break; - default: - cards = await summarizeViaClaudeCode(system, user, settings, job); - break; + if (useStreaming) { + cards = await summarizeViaApiStreaming(system, user, settings, onStreamProgress, abortController!.signal); + } else { + cards = await summarizeViaApi(requestUrl, system, user, settings); } const resolved: ResolvedCard[] = cards.map((c) => ({ title: c.title, diff --git a/src/settings-tab.ts b/src/settings-tab.ts index dc7cc2c..e160f52 100644 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -1,7 +1,6 @@ 'use strict'; import { type App, Notice, type Plugin, PluginSettingTab, requestUrl, Setting } from 'obsidian'; -import { resolveCliPath, runCli } from './cli'; import { testApiBackend } from './providers'; import { API_AUTH_TYPES, @@ -12,27 +11,13 @@ import { DEFAULT_SETTINGS, getApiFormat, getApiPreset, - isApiBackend, PROMPT_LANGUAGES, UI_LANGUAGES, } from './settings'; import type { PluginHost, PluginSettings } from './types'; async function testBackend(settings: PluginSettings) { - if (settings.backend === 'codex') { - const cmd = resolveCliPath('codex', settings.cliPath); - const { stdout } = await runCli(cmd, ['--version'], '', 10000); - return `codex @ ${cmd}\n${stdout.trim()}`; - } - if (settings.backend === 'claude-code') { - const cmd = resolveCliPath('claude', settings.cliPath); - const { stdout } = await runCli(cmd, ['--version'], '', 10000); - return `claude @ ${cmd}\n${stdout.trim()}`; - } - if (isApiBackend(settings.backend)) { - return testApiBackend(requestUrl, settings); - } - throw new Error('Unknown backend: ' + settings.backend); + return testApiBackend(requestUrl, settings); } export class ParallelReaderSettingTab extends PluginSettingTab { @@ -63,54 +48,9 @@ export class ParallelReaderSettingTab extends PluginSettingTab { }); }); - new Setting(containerEl) - .setName(tr('settingBackendName')) - .setDesc(tr('settingBackendDesc')) - .addDropdown((d) => - d - .addOption('claude-code', 'Claude Code CLI') - .addOption('codex', 'Codex CLI') - .addOption('api', 'API / Provider') - .addOption('anthropic-api', 'Anthropic API (legacy)') - .setValue(this.plugin.settings.backend) - .onChange(async (v) => { - this.plugin.settings.backend = v; - if (v === 'api' && !this.plugin.settings.apiBaseUrl) { - applyApiProviderPreset(this.plugin.settings, this.plugin.settings.apiProvider || 'anthropic'); - } - await this.plugin.saveSettings(); - this.display(); - }), - ); - - const apiBackend = isApiBackend(this.plugin.settings.backend); - - if (!apiBackend) { - new Setting(containerEl) - .setName(tr('settingCliPathName')) - .setDesc(tr('settingCliPathDesc')) - .addText((t) => - t - .setPlaceholder(tr('settingCliPathPlaceholder')) - .setValue(this.plugin.settings.cliPath) - .onChange(async (v) => { - this.plugin.settings.cliPath = v.trim(); - this.plugin.saveSettingsDebounced(); - }), - ); - - new Setting(containerEl).setName(tr('settingCliTimeoutName')).addText((t) => - t.setValue(String(this.plugin.settings.cliTimeoutMs)).onChange(async (v) => { - const n = parseInt(v, 10); - if (!Number.isNaN(n) && n > 0) { - this.plugin.settings.cliTimeoutMs = n; - this.plugin.saveSettingsDebounced(); - } - }), - ); - } else { - containerEl.createEl('h3', { text: tr('apiProviderHeader') }); + containerEl.createEl('h3', { text: tr('apiProviderHeader') }); + { const preset = getApiPreset(this.plugin.settings); new Setting(containerEl) .setName(tr('settingProviderPresetName')) @@ -233,13 +173,13 @@ export class ParallelReaderSettingTab extends PluginSettingTab { new Setting(containerEl) .setName(tr('settingModelName')) - .setDesc(apiBackend ? tr('settingModelDescApi') : tr('settingModelDescCli')) + .setDesc(tr('settingModelDescApi')) .addText((t) => t - .setPlaceholder(apiBackend ? getApiPreset(this.plugin.settings).model || 'model-id' : DEFAULT_SETTINGS.model) + .setPlaceholder(getApiPreset(this.plugin.settings).model || 'model-id') .setValue(this.plugin.settings.model) .onChange(async (v) => { - this.plugin.settings.model = v.trim() || (apiBackend ? '' : DEFAULT_SETTINGS.model); + this.plugin.settings.model = v.trim(); this.plugin.saveSettingsDebounced(); }), ); @@ -319,7 +259,7 @@ export class ParallelReaderSettingTab extends PluginSettingTab { new Setting(containerEl) .setName(tr('settingTestBackendName')) - .setDesc(apiBackend ? tr('settingTestBackendDescApi') : tr('settingTestBackendDescCli')) + .setDesc(tr('settingTestBackendDescApi')) .addButton((b) => b.setButtonText('Test').onClick(async () => { try { diff --git a/src/settings.ts b/src/settings.ts index 04694da..00dd453 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -21,7 +21,7 @@ export const UI_LANGUAGES = { export const DEFAULT_SETTINGS: PluginSettings = { uiLanguage: 'auto', - backend: 'claude-code', + backend: 'api', cliPath: '', apiProvider: 'anthropic', apiFormat: 'anthropic-messages',