Skip to content

Latest commit

 

History

History
265 lines (212 loc) · 21 KB

File metadata and controls

265 lines (212 loc) · 21 KB

Harper Docs Map for Agents

This repository’s documentation site is powered by Vite + SvelteKit + SveltePress.

Use packages/web/vite.config.ts as the source of truth for documentation scope:

  • Sidebar and important doc routes are defined in packages/web/vite.config.ts.
  • Route /docs/... maps to packages/web/src/routes/docs/....
  • Most docs are in +page.md; some are +page.svelte or route helpers.

If you are working on the Harper repository itself, please pay special attention to the contributors/* pages. Importantly, all the tools available in this repository are available via just. To learn more, run just --list. Whenever agents are done making changes, they should run just format before handing work back to the user. If just format fails, you can use just setup to prepare the relevant dependencies.

Human coders should periodically review this file and manually migrate stable, human-facing guidance into the real documentation website under packages/web so it is available outside agent workflows. Agents should remind humans of this when possible.

Read First

  1. packages/web/vite.config.ts: Sidebar source of truth and canonical map of important docs routes.
  2. packages/web/src/routes/docs/about/+page.md: High-level product overview, privacy model, versioning policy, and ecosystem context.
  3. packages/web/src/routes/docs/weir/+page.md: Weir rule language reference with syntax, expression types, and examples.
  4. packages/web/src/routes/docs/rules/+page.svelte: Live/generated rule catalog (rule names, defaults, and descriptions).
  5. packages/web/src/routes/docs/contributors/introduction/+page.md: Entry point for contributors and links to deeper contributor docs.
  6. ./AGENT_POLICY.md: Harper's policy on code generated by large language models. Agents should read this before making ANY changes.

Core Documentation Directories

  • packages/web/src/routes/docs/about
  • packages/web/src/routes/docs/weir
  • packages/web/src/routes/docs/rules
  • packages/web/src/routes/docs/integrations
  • packages/web/src/routes/docs/harperjs
  • packages/web/src/routes/docs/contributors

Route Prefix to File Prefix

  • /docs/about -> packages/web/src/routes/docs/about/+page.md
  • /docs/weir -> packages/web/src/routes/docs/weir/+page.md
  • /docs/rules -> packages/web/src/routes/docs/rules/+page.svelte
  • /docs/integrations/* -> packages/web/src/routes/docs/integrations/*/+page.md
  • /docs/harperjs/* -> packages/web/src/routes/docs/harperjs/*/+page.md
  • /docs/contributors/* -> packages/web/src/routes/docs/contributors/*/+page.md

Files Listed in the Sidebar (Local)

  • packages/web/src/routes/docs/about/+page.md: High-level product overview, privacy model, versioning policy, and ecosystem context.
  • packages/web/src/routes/docs/weir/+page.md: Weir rule language reference with syntax, expression types, and examples. Very important if you're asked to write a Weir rule.
  • packages/web/src/routes/docs/rules/+page.svelte: Live/generated rule catalog (rule names, defaults, and descriptions).
  • packages/web/src/routes/docs/integrations/obsidian/+page.md: Obsidian plugin overview, privacy/value comparison, installation, and support links.
  • packages/web/src/routes/docs/integrations/chrome-extension/+page.md: End-user Chrome extension overview and install link.
  • packages/web/src/routes/docs/integrations/firefox-extension/+page.md: End-user Firefox extension overview and install link.
  • packages/web/src/routes/docs/integrations/wordpress/+page.md: Current WordPress guidance, including migration recommendation to Chrome extension and legacy plugin status.
  • packages/web/src/routes/docs/integrations/language-server/+page.md: harper-ls install methods, dictionaries, code actions, ignore comments, and full configuration reference.
  • packages/web/src/routes/docs/integrations/visual-studio-code/+page.md: VS Code extension install, command list, and settings reference.
  • packages/web/src/routes/docs/integrations/neovim/+page.md: Neovim setup using harper-ls, plus optional and common config tweaks.
  • packages/web/src/routes/docs/integrations/helix/+page.md: Helix setup using harper-ls, plus optional and common config tweaks.
  • packages/web/src/routes/docs/integrations/emacs/+page.md: Emacs setup using harper-ls, plus optional and common config tweaks.
  • packages/web/src/routes/docs/integrations/zed/+page.md: Zed extension entry point and link to canonical extension README.
  • packages/web/src/routes/docs/integrations/sublime-text/+page.md: Sublime Text setup with harper-ls and LSP package configuration.
  • packages/web/src/routes/docs/harperjs/introduction/+page.md: harper.js mission, package overview, and installation starting point.
  • packages/web/src/routes/docs/harperjs/linting/+page.md: Core harper.js lint workflow and linter usage patterns.
  • packages/web/src/routes/docs/harperjs/spans/+page.md: Explains span objects and how to use them to locate/handle lint ranges.
  • packages/web/src/routes/docs/harperjs/configurerules/+page.md: How to programmatically read and set LintConfig to enable/disable rules.
  • packages/web/src/routes/docs/harperjs/node/+page.md: Node.js-specific usage notes, especially LocalLinter vs WorkerLinter.
  • packages/web/src/routes/docs/harperjs/CDN/+page.md: Browser/CDN usage via unpkg and ESM import patterns.
  • packages/web/src/routes/docs/contributors/introduction/+page.md: Contributor onboarding overview and links to architecture/testing/rule-authoring docs.
  • packages/web/src/routes/docs/contributors/environment/+page.md: Local development environment setup across Rust, Node/pnpm, and optional Nix shell.
  • packages/web/src/routes/docs/contributors/committing/+page.md: Commit message conventions and commit hygiene requirements.
  • packages/web/src/routes/docs/contributors/architecture/+page.md: System architecture and roles of core components like harper-core, harper-ls, and harper.js.
  • packages/web/src/routes/docs/contributors/dictionary/+page.md: Process for adding or updating curated dictionary entries.
  • packages/web/src/routes/docs/contributors/testing-strategy/+page.md: Harper's risk-driven testing strategy, covering automated checks, production feedback, manual testing, and testability.
  • packages/web/src/routes/docs/contributors/author-a-rule/+page.md: Step-by-step workflow for implementing and testing new grammar rules.
  • packages/web/src/routes/docs/contributors/visual-studio-code/+page.md: How to run, debug, test, and package the VS Code extension locally.
  • packages/web/src/routes/docs/contributors/chrome-extension/+page.md: Internal architecture and local development notes for the browser extensions.
  • packages/web/src/routes/docs/contributors/desktop/+page.md: Placeholder for Harper Desktop contributor documentation.
  • packages/web/src/routes/docs/contributors/website/+page.md: Local development, preview, and validation for writewithharper.com (SvelteKit/SveltePress).
  • packages/web/src/routes/docs/contributors/wordpress/+page.md: How to build and run the WordPress plugin locally.
  • packages/web/src/routes/docs/contributors/obsidian/+page.md: Obsidian-plugin contributor workflow and plugin-specific constraints.
  • packages/web/src/routes/docs/contributors/review/+page.md: PR reviewer playbook, including ways to fetch artifacts and test patches locally.
  • packages/web/src/routes/docs/contributors/local-stats/+page.md: Local stats logging model, stats.txt format, locations, and privacy behavior.
  • packages/web/src/routes/docs/contributors/brill/+page.md: Brief explanation of Harper’s Brill-tagging approach and further reading link.
  • packages/web/src/routes/docs/contributors/faq/+page.md: Contributor FAQ for conceptual distinctions (for example Linter vs PatternLinter).

Documentation Route Helpers (Non-+page.md)

  • packages/web/src/routes/docs/about/+page.ts: Route behavior helper (ssr = false) for the About page.
  • packages/web/src/routes/docs/harperjs/CDN/example/+server.ts: Serves the HTML example used by the harper.js CDN documentation page.

External Sidebar Targets (No Local Source File)

  • https://docs.rs/harper-core/latest/harper_core/
  • /docs/harperjs/ref/index.html (generated API reference target)

Projects Contained in This Repository

  • harper-core: The core grammar checking engine. This is a dependency to pretty much everything related to Harper.
  • harper-ls: A Language Server compatible with a number of text editors, including Neovim, Zed, and Helix. See above linked documentation for more details.
  • harper-cli: A command-line binary for debugging Harper's core engine and markup language support.
  • harper-comments: Provides parsers for a number of programming languages to support linting their comments.
  • harper-wasm: The WebAssembly build target that powers browser and JavaScript integrations such as harper.js.
  • packages/lint-framework: A package containing the tooling necessary to read/write/highlight text on the web for the purpose of linting.
  • packages/components: Shared Svelte component package used by web-facing packages.
  • packages/harper-editor: Shared Svelte editor package used by web-facing packages and Harper Desktop.
  • packages/web: The Harper website, including documentation and a live demo that uses the lint-framework.
  • packages/harper.js: The JavaScript package that uses harper-wasm to lint text from websites or Node.js processes.
  • packages/chrome-plugin: The Harper Chrome Extension - uses the lint-framework. Also support Firefox.
  • packages/obsidian-plugin
  • packages/wordpress-plugin
  • packages/vscode-plugin: The Harper Visual Studio Code plugin. Uses harper-ls.
  • harper-desktop: The Harper Desktop app. It is a Tauri v2 + SvelteKit SPA with an offline editor, settings UI, tray/service control, and native overlay highlighter.

There are of course projects in this repository not listed above. If relevant, feel free to poke around.

Harper Desktop

Commands

  • Use pnpm for frontend packages; harper-desktop/package.json pins pnpm@10.10.0.
  • Dev app: just dev-desktop from repo root. It builds shared web packages, runs cargo tauri dev, and Tauri then runs pnpm dev on port 1420.
  • Start the highlighter process directly: just dev-desktop-highlighter from repo root.
  • Frontend checks: pnpm check from harper-desktop.
  • Frontend build only: pnpm build from harper-desktop.
  • Full desktop checks: just check-desktop from repo root.
  • Rust checks: cargo check -p harper-desktop --all-targets from repo root.
  • Rust formatting/fix loop from repo root: cargo fmt && cargo check -p harper-desktop --all-targets.
  • Bundle builds match CI: just build-desktop-linux or just build-desktop-macos.
  • just build-desktop-linux builds deb/rpm/appimage bundles.
  • just build-desktop-macos builds app/dmg bundles.

Architecture

  • Harper Desktop lives under harper-desktop and is part of the root Cargo and pnpm workspaces.
  • It is a SvelteKit SPA inside Tauri v2.
  • SSR is disabled in harper-desktop/src/routes/+layout.ts.
  • harper-desktop/svelte.config.js uses adapter-static with fallback: "index.html".
  • Main Rust entrypoint is harper-desktop/src-tauri/src/main.rs, which calls harper_desktop_lib::run().
  • CLI behavior is in harper-desktop/src-tauri/src/lib.rs: no subcommand runs the normal Tauri app; highlighter runs the native overlay highlighter.
  • Tauri config lives in harper-desktop/src-tauri/tauri.conf.json.
  • Tauri capabilities live in harper-desktop/src-tauri/capabilities/default.json.
  • The main Svelte route is harper-desktop/src/routes/+page.svelte; it switches between the settings UI and editor UI.
  • The editor UI lives in harper-desktop/src/lib/EditorView.svelte and uses workspace dependencies on harper.js and harper-editor.
  • The settings UI lives under harper-desktop/src/lib/settings/.
  • Rust uses local workspace dependencies from harper-desktop/src-tauri/Cargo.toml, including harper-core and harper-dictionary-wordlist.
  • The Tauri app starts a tray/menu-bar controlled highlighter service on launch and can open editor/settings windows from the tray.
  • Editor and settings windows hide on close instead of exiting; quit is handled through the tray menu.

Highlighter Architecture

  • The highlighter is Rust/egui/winit code under harper-desktop/src-tauri/src/highlighter/.
  • Highlighter is the public entry point for the overlay system.
  • Window owns native window/GPU plumbing.
  • WindowManager owns the winit event loop, monitor windows, cursor hit-testing, and popup selection.
  • RenderState owns highlight rendering, popup drawing, markdown rendering cache, and popup action dispatch.
  • RenderState uses ActionableLint values from harper-desktop/src-tauri/src/rect.rs.
  • ActionableLint stores lint geometry, the Harper Lint, and source text needed for suggestion application and popup actions.
  • Suggestion popup actions currently include close, apply suggestion, ignore lint, add to dictionary, disable rule, and refresh config.
  • Popup hover text is implemented in RenderState button helpers.
  • The highlighter read interval is set in run_highlighter() with with_read_interval(Duration::from_millis(16)).

OS Integration

  • OS integration is behind harper-desktop/src-tauri/src/os_broker.rs.
  • macOS uses harper-desktop/src-tauri/src/mac_broker.rs.
  • non-macOS currently uses NoopBroker.
  • macOS highlighter focus handling depends on MacBroker.last_focused; clicking the overlay can make the highlighter process focused, so accessibility reads fall back to the last non-highlighter PID.
  • macOS integrations are bundle-ID based and controlled by persisted Integration entries.
  • Highlighter stdout is reserved for JSON-line IPC. Diagnostics in highlighter paths should use eprintln!, not println!.

IPC And App State

  • Tauri app state lives in harper-desktop/src-tauri/src/config.rs.
  • Config currently stores mutable_dictionary: MutableDictionary, dialect: Dialect, ignored_lints: IgnoredLints, lint_config: FlatConfig, and integrations: Vec<Integration>.
  • Config::new() uses MutableDictionary::new(), Dialect::American, IgnoredLints::new(), FlatConfig::new_curated(), and curated default integrations.
  • Config is persisted under the system config directory in a harper-desktop folder. Main settings are serialized to config.json; the mutable dictionary is stored separately as dictionary.txt.
  • Config::load_from_system() loads the serialized config and then hydrates the mutable dictionary with the selected dialect.
  • The Tauri app owns shared config as Arc<Mutex<Config>>.
  • run_tauri() loads persisted config, creates HighlighterService, starts the highlighter service, and creates the tray/menu-bar UI.
  • The highlighter process is spawned through harper-desktop/src-tauri/src/highlighter_process.rs.
  • IPC is implemented under harper-desktop/src-tauri/src/communication/.
  • IPC uses newline-delimited JSON over child stdin/stdout.
  • The Tauri app is the protocol server.
  • The highlighter process is the protocol client.
  • Protocol messages live in harper-desktop/src-tauri/src/communication/message.rs.
  • Supported requests include GetLintConfig, GetDictionary, GetDialect, GetIgnoredLints, GetIntegrations, SetLintConfig, IgnoreLint, AddToDictionary, AddIntegration, RemoveIntegration, and SetIntegrationEnabled.
  • Supported responses include GetLintConfig, GetDictionary, GetDialect, GetIgnoredLints, GetIntegrations, and Ack.
  • IgnoredLints is transferred as whole serialized state and merged server-side.
  • AddToDictionary sends only the word; Rust appends it with DictWordMetadata::default().
  • The highlighter uses a Tokio current-thread runtime to call async IPC client methods from synchronous UI callbacks.
  • That runtime is bridge plumbing only; it is not used for linting or UI rendering.

Dictionary And Linting Gotchas

  • Keep dictionary construction centralized through Config::dictionary_from_user_dictionary(user_dictionary: MutableDictionary) -> Arc<MergedDictionary> and Config::create_dictionary().
  • Keep linter construction centralized through Config::create_linter().
  • The same merged dictionary source must be used for both Document::new_markdown_default(text, &dictionary) and LintGroup::new_curated(dictionary, dialect).
  • Do not use Document::new_markdown_default_curated(text) in the main highlighter lint callback when user dictionary words should suppress spelling lints.
  • Harper spelling lint behavior depends on document token metadata, so a linter with the updated dictionary is not enough if the Document was built with the curated dictionary only.
  • RenderState may still use curated document construction for localized source extraction where dictionary membership does not matter.

Frontend Integration

  • Tauri commands in harper-desktop/src-tauri/src/lib.rs include get_lint_config, get_dialect, set_dialect, set_lint_config, get_dictionary, set_dictionary, ignore_lint, add_to_dictionary, get_integrations, add_integration, remove_integration, and set_integration_enabled.
  • JS helper lives in harper-desktop/src/lib/client.ts.
  • JS helper class is named Client and exposes static methods for lint config, dialect, dictionary, ignored lints, dictionary additions, and integrations.
  • Client.ignoreLint(...) uses Harper JS ignored-lints export data and invokes the Rust command.
  • Client.addToDictionary(word) sends only the word to Rust.
  • Vite's dev server must stay on port 1420; harper-desktop/vite.config.js uses strictPort: true because Tauri expects that port.
  • Vite intentionally ignores src-tauri/** during frontend file watching.

Repo-Specific Gotchas

  • CI installs wasm-pack, tauri-cli, and cargo-hack with cargo binstall, then runs the relevant root just task.
  • Avoid mixing behavior fixes with structural refactors in the highlighter; popup/UI, IPC, dictionary behavior, config persistence, settings UI, and OS-coordinate fixes are easier to review separately.

Known Review Findings

  • Secondary-monitor highlighter coordinates may be wrong. Accessibility rectangles appear to be global screen coordinates, while each overlay window's egui origin is local to that monitor/window. Translate by monitor/window origin or render through one virtual-desktop-sized overlay.
  • macOS text range lookup may be wrong after emoji or other non-BMP characters. Harper spans are char-based, while macOS accessibility ranges are NSString/UTF-16 based. Convert spans before calling AXBoundsForRangeParameterizedAttribute.

On Writing New Rules

When asked to write a new rule, keep these guidelines in mind:

  • The user is almost always expecting you to write it to a file. Which file and where is up to you to find out.
  • You should include at least 15 total tests, covering a wide variety of cases. Cover false-positives, false negatives, true positives, and if relevant, true negatives.
  • You should run any and all tests to ensure that you do no break existing behavior and that your new rule runs the way you expect. At minimum, that means just test-rust. Make sure to check the snapshots for changes.
  • After adding a new rule, update harper-core/default_config.json so it is enabled in the curated default config.
  • Built-in Weir rules can be grouped by putting related .weir files in a directory under harper-core/src/linting/weir_rules; the directory name becomes the single public rule name.
  • If the rule is related to a closed compound noun, see if you can just add an entry to the existing closed compound linter.

Unless you are specifically requested to write the rule in a specific way, choose the language (Rust or Weir) and methodology that fits the task.

ALWAYS run extensive bullet tests with cargo run --bin harper-cli --release -- lint <TEXT> to make sure the new rule isn't already covered by Harper.

Workflow for Writing Weir Rules

  1. Draft the core expression
  • Encode the match with expr main using words, sequences, alternatives, filters, exceptions, POS tags, wildcards, or punctuation.
  • Keep the expression minimal but precise; avoid overmatching.
  • If a wordlist is needed, include it as its own expression, used with an expression reference.
  1. Add rule metadata
  • let message, let description, let kind, let becomes (and let strategy if needed).
  • Use strategy "Exact" when casing must be normalized; otherwise default behavior or MatchCase as appropriate.
  1. Add tests (required)
  • Include at least 15 tests.
  • Tests must cover: true positives, false positives, false negatives, and (if relevant) true negatives.
  • Prefer a mix of casing, punctuation, whitespace, and nearby-token variations.
  1. Sanity-check edge cases
  • Ensure exceptions do not block valid matches.
  • Ensure replacements are correct and not destructive.
  1. Run the tests.
  • Fix any issues that arise.

Output Format

Write a Weir rule to a new file with a name of your choosing, including expr main, let fields, and tests. Make sure it has the extension .weir.

Tips

Before finishing your turn, run just format or cargo fmt if you have made any changes. This can help diffs if a human decides to make a commit.