Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Lint (eslint-plugin-obsidianmd)
run: npm run lint

- name: Type-check & build (tsc --noEmit + esbuild)
run: npm run build

Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ jobs:
release:
name: release
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # mint the provenance signing certificate
attestations: write # write the artifact attestation
steps:
- name: Checkout
uses: actions/checkout@v7
Expand All @@ -37,6 +41,9 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Lint (eslint-plugin-obsidianmd)
run: npm run lint

- name: Type-check & build (tsc --noEmit + esbuild)
run: npm run build

Expand Down Expand Up @@ -71,6 +78,16 @@ jobs:
git tag "$V"
git push origin "$V"

# Cryptographically attest that these assets were built from this repo by
# this workflow, so users can verify provenance with `gh attestation verify`.
- name: Attest build provenance
if: steps.guard.outputs.exists == 'false'
uses: actions/attest-build-provenance@v2
with:
subject-path: |
main.js
styles.css

- name: Create GitHub release
if: steps.guard.outputs.exists == 'false'
env:
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

_Nothing yet._

## [1.1.5] - 2026-06-24

### Changed

- Brought the source into line with the Obsidian plugin guidelines (no
user-visible change): `setCssStyles`/`setCssProps` instead of direct `.style`
assignment, `activeDocument`/`activeWindow` for popout-window safety,
`.instanceOf()` for cross-window type checks, window-scoped `requestAnimation
Frame`, and auto-bound arrow-function event handlers.
- Full-screen tables get their `.markdown-rendered` styling context from the
scroll container directly, dropping the partially-supported `display: contents`.
- esbuild marks Node built-ins external via `node:module` `builtinModules`,
removing the `builtin-modules` dependency.

### Internal — recurrence prevention

- Added **`eslint-plugin-obsidianmd`** (the Obsidian reviewer's ruleset) with a
flat, type-aware `eslint.config.mjs`; `npm run lint` now gates CI and the
release workflow, so guideline violations fail the build instead of surfacing
at submission.
- Release assets `main.js` and `styles.css` are now published with **GitHub
build-provenance attestations** (`actions/attest-build-provenance`).

## [1.1.4] - 2026-06-24

### Fixed
Expand Down
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,24 @@ Lookout is an **Obsidian plugin** for surveying wide content: pan/zoom Mermaid d
npm ci # install dev deps (first time / CI)
npm run dev # esbuild watch: rebuild main.js on save (local dev)
npm run build # tsc --noEmit (type-check) + esbuild production bundle
npm run lint # eslint-plugin-obsidianmd (Obsidian compliance)
npm run validate # manifest/versions consistency + required files
```

The full CI gate (run before every PR), on Node 20:

```bash
npm ci
npm run lint # Obsidian plugin-guideline lint (eslint-plugin-obsidianmd)
npm run build # type-check + bundle -> main.js
node --check main.js
node scripts/validate.mjs
```

There are no unit tests — behavioural verification is manual in a real vault (see below). `tsconfig.json` is `strict` with `strictPropertyInitialization` off (fields init in `_build()`); see `docs/DEVELOPMENT.md`.

**Obsidian compliance is enforced by lint, not memory.** `eslint-plugin-obsidianmd` (the same ruleset the Obsidian reviewer runs) gates CI, so write code that passes it: no `innerHTML`; `activeDocument`/`activeWindow` not `document`/`window`; `setCssStyles`/`setCssProps` (available on both `HTMLElement` and `SVGElement`) instead of direct `.style.x =`; `.instanceOf(HTMLElement)` not `instanceof`; auto-bound arrow-function fields for event handlers. Releases attest provenance for `main.js`/`styles.css` via `actions/attest-build-provenance`.

## Local development against a vault

Obsidian runs the plugin from `<vault>/.obsidian/plugins/lookout/`. Symlink the repo there and run the watch build; reload the plugin (toggle off/on, or the *Reload app without saving* command) after each rebuild:
Expand Down
14 changes: 14 additions & 0 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ historically diverged (see the table-processing guards in `src/main.ts`).
npm run dev # esbuild watch build (development)
npm run build # tsc --noEmit (type-check) + esbuild production bundle
npm run typecheck # tsc --noEmit only
npm run lint # eslint-plugin-obsidianmd (Obsidian compliance) — see below
npm run validate # manifest/versions consistency + required-files check
npm run test:e2e # headless browser E2E (see below) — run after `npm run build`
```
Expand All @@ -69,11 +70,24 @@ CI (and a pre-PR check) runs, on Node 20:

```bash
npm ci
npm run lint # Obsidian plugin-guideline lint
npm run build # type-check + bundle -> main.js
node --check main.js
node scripts/validate.mjs
```

### Linting — Obsidian compliance (recurrence prevention)

The Obsidian plugin reviewer runs `eslint-plugin-obsidianmd` (no `innerHTML`,
`activeDocument`/`activeWindow` for popout windows, `setCssStyles`/`setCssProps`
instead of direct `.style` assignment, `.instanceOf()` for cross-window checks,
window-scoped timers, etc.). We run that **same ruleset locally and in CI**
(`eslint.config.mjs`, flat config with typed linting), so a violation fails the
build here instead of surfacing at submission/review time. `npm run lint` must
be green before opening a PR; the CI `validate` job enforces it. When a rule is
deliberately not applicable (e.g. `ui/sentence-case` for Korean UI text), it is
turned off in `eslint.config.mjs` with a comment rather than ignored ad hoc.

There are no unit tests, but there is a headless-browser **E2E** check (see
below). Beyond that, behavioural verification is manual in a vault.

Expand Down
4 changes: 2 additions & 2 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// `node esbuild.config.mjs production` does a one-shot minified build.
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { builtinModules } from "node:module";

const banner =
"/*\n * Lookout — bundled output. Do not edit; edit the TypeScript source in\n * src/ and rebuild (see docs/DEVELOPMENT.md).\n */";
Expand All @@ -15,7 +15,7 @@ const context = await esbuild.context({
entryPoints: ["src/main.ts"],
bundle: true,
// Obsidian and Electron are provided by the host; never bundle them.
external: ["obsidian", "electron", ...builtins],
external: ["obsidian", "electron", ...builtinModules],
format: "cjs",
target: "es2018",
logLevel: "info",
Expand Down
27 changes: 27 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Lint config — the recurrence-prevention gate. `eslint-plugin-obsidianmd`
// encodes the same rules the Obsidian plugin reviewer runs, so violations are
// caught locally and in CI instead of at submission/review time.
import tseslint from "typescript-eslint";
import obsidianmd from "eslint-plugin-obsidianmd";

export default tseslint.config(
{
ignores: ["main.js", "main.js.map", "node_modules/**", "tests/**", "*.mjs"],
},
...obsidianmd.configs.recommended,
{
files: ["src/**/*.ts"],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// User-facing strings are Korean by project convention (and the rule also
// mis-fires on key names like "Esc"), so English sentence-case doesn't apply.
"obsidianmd/ui/sentence-case": "off",
},
}
);
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "lookout",
"name": "Lookout",
"version": "1.1.4",
"version": "1.1.5",
"minAppVersion": "1.0.0",
"description": "Survey wide content instead of scrolling sideways. Pan and zoom Mermaid diagrams (wheel, Ctrl+wheel, or buttons), fit them to the frame, and open diagrams or wide tables full-screen.",
"author": "Post-Math",
Expand Down
Loading