ArticleWriter

by 蓓蕾耿
5
4
3
2
1
Score: 50/100

Description

obsidian的写书插件,支持多种AI, 支持书籍管理,章节管理,卷管理,实时字数统计

Reviews

No reviews yet.

Stats

0
stars
245
downloads
0
forks
36
days
6
days
6
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
0
total issues
0
open issues
0
closed issues
72
commits

Latest Version

7 days ago

Changelog

v0.2.4

Built automatically from source commit 0b30056fc24387d8c6848dbcbf22b20e1f72253f.

README file from

Github

ArticleWriter for Obsidian

Chinese version: README_ZN.md

⚠️ Disclosures & Security Statement

Network requests. This plugin performs no network activity except LLM chat. All outbound traffic goes exclusively through the official openai SDK to the OpenAI-compatible endpoint that you configure yourself in settings (a local server such as Ollama / LM Studio / llama.cpp, or a provider API like DeepSeek or Qwen DashScope). At runtime the plugin makes exactly three kinds of calls — non-streaming and streaming chat completions, plus one optional GET /models issued only when you run the LLM connection test to confirm your endpoint works; any additional request-related call sites flagged by scanners belong to the bundled SDK's generic HTTP layer and are not used elsewhere by this plugin. There is no telemetry, no analytics, no update checking, and no connection to any author-operated server. Your API key is entered by you in the settings panel, stored locally inside your vault with the rest of the plugin settings, and transmitted only as an authentication header to the endpoint you chose; when left empty (the normal case for local servers) a fixed placeholder value (local-no-auth) stands in its place, so no real secret ever leaves your machine.

Base64 usage (atob / btoa). The plugin's own source code contains zero base64 encode/decode calls. Every occurrence present in the shipped bundle originates from third-party libraries that esbuild bundles: the yaml parser (its standard handler for YAML !!binary tags) and the openai SDK (generic base64 ↔ binary-buffer conversion helpers). They perform ordinary data-format conversions and are not used to obfuscate API keys, hide URLs, or mask code payloads. Nothing in the build is minified or obfuscated — the complete human-readable source is published in the repository; please audit it directly if you have concerns.

Vault Enumeration Disclosure

This plugin inspects file structures inside your vault through Obsidian's standard app.vault APIs, in exactly two scopes:

  • Purpose — vault-wide path listing (two small UI pickers): (a) the folder picker shown on first use / when switching the working directory (work_dir) lists existing folders so you can fuzzy-search where your manuscripts live; this walk starts at the vault root but is deliberately bounded — it descends only 3 levels deep, stops after 500 entries, and skips hidden directories such as .obsidian/; (b) the dockable chat panel's @reference candidate list calls vault.getFiles() when rendering so that any document in your vault can be quoted into an LLM prompt with one keystroke. Both list paths only — no content is read at either point. A file's text is loaded only after you act on it: picking a folder merely sets the scope for later local operations, and referencing a file adds its content to the explicit request sent to the model endpoint you configured yourself (see the Network-requests note above).
  • Purpose — working-directory-scoped operations: Everything else (building the story status tree, listing volumes/chapters, counting words per chapter/book, and loading outline / world-building / character / scene documents into writing prompts) operates exclusively inside the work folder you select on first use (work_dir). Files outside that folder are never opened or scanned by these features.
  • Privacy Assurance: All enumeration runs entirely locally and in-memory on your machine. File paths and contents are processed strictly on-device and are never uploaded, indexed remotely, leaked, or shared with any external server — except for the deliberate LLM requests described in the Network-requests note that you trigger yourself.
  • Summary for reviewers: Vault-wide listing happens in exactly two UI pickers and exposes paths only (the folder picker even caps at 3 levels deep / 500 entries); all other file access stays inside the user-selected working directory, everything runs locally in-memory, and nothing leaves the machine except LLM requests you explicitly send to your own configured endpoint.

Overview

ArticleWriter turns your vault into an AI-assisted novel workshop. It organizes each story as a folder of plain Markdown files (state doc, outline, world-building, characters, scenes, foreshadowing notes and one folder per chapter) and adds LLM-powered writing commands on top: write / continue / rewrite / polish chapters, strip AI-sounding phrasing, review from a global perspective, plus a dockable chat panel that can quote any vault file via @references. All data stays local; no external services are required beyond the OpenAI-compatible model endpoint you configure yourself.

Nothing depends on external services — every operation works on the Markdown documents inside your vault through Obsidian's built-in Vault / Workspace APIs.

Data Layout

<novel root>/<book title>/
├── 故事状态.md              # Obsidian "file properties" style: YAML frontmatter holds the version-2 runtime state (title/genre/current volume·scene·chapter/per-chapter metadata); body is free-form notes; legacy story_state.json is auto-migrated with a backup in _backup/ on first save
├── WRITING_GUIDE.md         # Story-level writing guide (the user-level one of the same name lives under work_dir)
├── 写作指南汇总.md           # Per-book merged guide (auto-generated, change-detected via hash); the only writing-guide content injected into LLM prompts
├── 大纲.md                  # Master outline
├── 世界观.md                # World-building template
├── 卷.md                    # Volumes (grouping container)
├── 伏笔.md                  # Foreshadowing log
├── 笔记.md                  # Notes
└── 第NN章-<标题>/           # One folder per chapter
    ├── NN-<标题>.md          # Chapter body (file prefix is the numeric chapter number)
    ├── NN-<标题>-大纲.md      # Chapter outline
    ├── NN-<标题>-人物.md      # Characters
    ├── NN-<标题>-人物关系.md  # Character relations
    ├── NN-<标题>-场景.md      # Scenes
    └── NN-<标题>-信息.md      # Chapter info

Volume-level template docs are named after the volume too (e.g. <卷名>-大纲.md, <卷名>-人物.md, <卷名>-人物关系.md, <卷名>-场景.md).

Missing documents are auto-created as "HTML comment example" templates when creating a new book/chapter; existing files are skipped and never overwrite user content. All file names are normalized through safeFilename(). Legacy documents (bare names like 章节.md/卷大纲.md, or older prefix forms like 初见-章节.md) are auto-detected when switching books and can be batch-migrated to the new names (rename only, content untouched).

Commands

Search the command palette for "ArticleWriter" or the Chinese description.

Command Implementation notes
Create new story vault.createFolder + vault.create build the book folder and all template docs, then write the state doc
New chapter Creates the 第NN章-title/ directory with its 6 documents (named NN-title.md, NN-title-大纲.md, …) and updates the current chapter
Chapter list Scans chapter folders; picking one opens its body via workspace.getLeaf("tab").openFile and sets it as the current chapter
Open outline / world-building / foreshadowing / notes Created from template first if missing, then opened
Next / previous chapter With no current chapter: next → first, prev → last; at a boundary a notice is shown without switching
Word count of current chapter / whole book Counts pure text characters only (excludes punctuation/symbols/whitespace)
Save current chapter Reads the focused editor's content and force-flushes it to disk via vault.modify
Story status Shows title / genre / current chapter / chapter count / total word count
View / edit writing guide Three tiers: story-level <book>/WRITING_GUIDE.md > user-level file of the same name under work_dir > system level = a fixed plugin-data-dir file .obsidian/plugins/articlewriter/WRITING_GUIDE.md (seeded on first run from the built-in default; no longer user-configurable — the old system_guide_path setting was removed). Each tier opens its own file directly; multiple tiers open a picker. Editing saves the full text for the chosen tier and refreshes that book's merged《写作指南汇总》
Generate empty writing-guide templates Creates a same-format skeleton (section headings kept, body cleared) for both the user-level work_dir/WRITING_GUIDE.md and the current book's WRITING_GUIDE.md; skips any target that already has non-empty content and reports it. Does not touch the system tier
Regenerate system writing guide After a confirmation dialog, overwrites the system-tier .obsidian/plugins/articlewriter/WRITING_GUIDE.md with the built-in default, then best-effort refreshes the current book's merged guide
Generate usage guide Writes 《使用说明.md》(built-in user manual of this plugin) at the work_dir root. Also auto-created and opened when missing at plugin start-up or whenever the work directory is set or switched (only when missing or empty); an existing non-empty file is skipped and never overwritten
LLM connection test Uses the openai SDK against any OpenAI-compatible endpoint (DeepSeek/DashScope/Ollama/LM Studio/llama.cpp…); verifies the active config via GET /models
LLM chat window Persistent dockable panel (custom view, draggable into any workspace area, position survives reloads; message icon in the sidebar as quick entry): multi-turn streaming chat, Enter sends / Shift+Enter newline, dropdown on top switches saved model configs, "Stop generating" only interrupts the current turn; every turn automatically carries a chat-specific prompt (friendly-assistant identity + writing guide + snapshot of the current story context), the current story·chapter is shown on top, history is not persisted
Writing desk Persistent dockable panel (book icon in the sidebar as quick entry): work directory, list of all stories (click to switch the current book), genre / writing type / total word count / update time of the current story, chapter list (activating a chapter also syncs its volume), global docs and per-chapter files (click opens them in the editor); story/chapter/file lists all support folder-like expand/collapse (title row or the arrow before a chapter); right-clicking a story/chapter/file row shows a shortcut menu: create/delete story, create/delete chapter, create an article .md at the book root or inside a chapter dir / delete file (all destructive actions require a second confirmation; deletions go to the Obsidian trash so they are recoverable); manual reload via "Refresh" at the top right
Relationship panel Persistent dockable panel (people icon in the sidebar as quick entry): aggregates the current story's relationship cards from the three tiers of 《人物关系.md》 (book / volume / chapter) — character pair, type badge (icon picked by relationship type), status dot (active green / pending yellow / ended grey) and description; click a card (or "Open document" on a group header) to open that tier's source md; the top offers a "Cards / Graph" toggle (kept for the session): Cards = grouped relationship cards, Graph = a circular node-link network (edge colour = relationship type with an in-graph legend, line style = active solid / pending dashed / ended dotted, thicker line = same pair registered in several places; hover a character to highlight its neighbours, hover an edge to highlight just that one, click a node or edge to open the source doc; a zoom bar above the graph (zoom out / zoom in / reset view) scales the whole drawing including character names, and scrolling the mouse wheel over the graph also zooms (the point under the mouse stays fixed across zoom; scroll down = zoom out, scroll up = zoom in). The part cropped by the pane can be reached by holding and dragging the graph itself — no scrollbars appear, dragging is clamped so the graph can never be lost off-view, and "Reset view" returns it to the initial fit-to-pane position), plus the filter box (character/type/status) and a "Refresh" button — no work-directory row and no story list (the panel shows relationships only; switch books via the "Switch current story" command or the Writing desk), groups collapse in card mode, and edits to relationship docs auto-refresh the panel
Timeline panel Persistent dockable panel (history icon): merges the current story's three tiers of hand-written 《时间线.md》 docs (book root / volume dir / chapter dirs) into a single top-to-bottom vertical timeline — every entry carries its source-tier label (book level / Volume · X / Chapter N Y), sorted ascending by time point (year / year-month / year-month-day, e.g. -129-06-15; negatives/decimals/scientific notation also allowed); a summary line shows totals per tier. The filter box on top matches character/event keywords; double-clicking an entry opens that doc in the editor and jumps to its ## <time-point> heading row. Docs are seeded from templates when creating books/volumes/chapters (and backfilled via the status view's complete-docs actions) but remain opt-in reference material only — never injected into writing prompts: place them at <book>/时间线.md, <volDir>/<卷名>-时间线.md (bare 时间线.md also works) or <chDir>/NN-<标题>-时间线.md (bare name also works); format = one time point per ## <time-point> heading + - 人物:A、B field line for involved characters and - 事件:<text> for the event (free text or ```text fenced blocks may follow); headings that aren't valid time points skip their whole block; edits to the docs auto-refresh the panel
LLM model configuration Obsidian Settings → ArticleWriter, stored in the plugin data directory .obsidian/plugins/articlewriter/data.json (first run presets three standard templates local/deepseek/qwen-dashscope awaiting api_key/model name)

Workflow (work_dir)

  1. The first time you use any ArticleWriter command, a work directory picker pops up automatically — choose an existing vault folder as your work_dir for novel writing. A 《使用说明.md》 usage guide is then dropped into that folder automatically (editable; re-created via the "Generate usage guide" command only when missing or empty).
  2. From then on, creating stories, creating chapters, opening documents, switching chapters, word counts — everything operates under work_dir; each story is one subfolder of work_dir.
  3. To change directories, use the "Select work directory" command or the "Re-select…" button on the settings page (switching clears the remembered last story).
  4. With multiple books, commands show a story picker and remember your last choice.

Settings

  • Work directory (work_dir): the folder where novels live; initialized by the automatic picker on first use, can be changed/re-selected manually.
  • Auto-open document after creation: opens 大纲.md when a book is created, 章节.md when a chapter is created.

Build & Install

npm install
npm run build        # outputs main.js + manifest.json + styles.css into release/

Development mode (watch): npm run dev.

Manual install: copy main.js, manifest.json, styles.css from release/ to <your vault>/.obsidian/plugins/articlewriter/, then enable the plugin in Obsidian under "Settings → Community plugins".