create-obsidian-arrow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +47 -0
  2. package/index.mjs +76 -0
  3. package/package.json +40 -0
  4. package/template/.github/workflows/ci.yml +36 -0
  5. package/template/.husky/pre-commit +2 -0
  6. package/template/AGENTS.md +90 -0
  7. package/template/LICENSE +21 -0
  8. package/template/README.md +116 -0
  9. package/template/_gitignore +8 -0
  10. package/template/biome.json +30 -0
  11. package/template/docs/prompts/agent-setup.md +94 -0
  12. package/template/docs/superpowers/specs/2026-06-29-obsidian-arrow-sandbox-design.md +206 -0
  13. package/template/index.html +19 -0
  14. package/template/package.json +43 -0
  15. package/template/pnpm-lock.yaml +1408 -0
  16. package/template/scripts/install-skills.mjs +47 -0
  17. package/template/scripts/lib/extract-app-css.mjs +60 -0
  18. package/template/scripts/pull-app-css.mjs +90 -0
  19. package/template/skills/arrow-js-obsidian-patterns/SKILL.md +94 -0
  20. package/template/skills/arrow-js-obsidian-templates/SKILL.md +101 -0
  21. package/template/skills/obsidian-arrow-sandbox/SKILL.md +64 -0
  22. package/template/src/components/SettingsPanel.ts +232 -0
  23. package/template/src/data/loadStatus.ts +17 -0
  24. package/template/src/examples/ExamplesIndex.ts +36 -0
  25. package/template/src/examples/registry.ts +26 -0
  26. package/template/src/main.ts +18 -0
  27. package/template/src/router/client.ts +85 -0
  28. package/template/src/router/routeToPage.ts +57 -0
  29. package/template/src/sandbox/frame.ts +35 -0
  30. package/template/src/sandbox/layout.ts +40 -0
  31. package/template/src/sandbox/sandbox.css +125 -0
  32. package/template/src/sandbox/shell.ts +15 -0
  33. package/template/src/sandbox/theme.ts +22 -0
  34. package/template/src/sandbox/toolbar.ts +32 -0
  35. package/template/test/extract-app-css.test.mjs +70 -0
  36. package/template/test/template-footguns.test.mjs +58 -0
  37. package/template/tsconfig.json +13 -0
  38. package/template/vite.config.ts +15 -0
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # create-obsidian-arrow
2
+
3
+ Scaffold a new [Obsidian](https://obsidian.md/)-styled [Arrow.js](https://arrow-js.com/)
4
+ UI sandbox — a client-only Vite + TypeScript project that renders Arrow
5
+ components against Obsidian's real `app.css`, ready to port into a plugin.
6
+
7
+ ## Usage
8
+
9
+ ```sh
10
+ npm create obsidian-arrow@latest my-app
11
+ # or: pnpm create obsidian-arrow my-app
12
+ # or: npx create-obsidian-arrow my-app
13
+ ```
14
+
15
+ Then:
16
+
17
+ ```sh
18
+ cd my-app
19
+ pnpm install
20
+ pnpm pull-css # required — extract Obsidian's app.css from your local install (macOS auto-detect)
21
+ pnpm dev
22
+ ```
23
+
24
+ > `public/app.css` is git-ignored and never bundled — it's Obsidian's proprietary
25
+ > CSS, so each developer extracts it from their own install via `pnpm pull-css`.
26
+
27
+ Local dev of the initializer itself (from the sandbox repo, before publishing):
28
+
29
+ ```sh
30
+ node create-obsidian-arrow/index.mjs ../my-app
31
+ ```
32
+
33
+ ## What you get
34
+
35
+ A full sandbox: client-only Vite + TS, `@arrow-js/core` + `@arrow-js/framework`
36
+ (no SSR), `routeToPage` + Navigation-API router with an `/example` demo, Biome +
37
+ husky pre-commit + `node:test` + GitHub Actions CI, bundled agent skills, and the
38
+ `pull-css` script that extracts Obsidian's `app.css`.
39
+
40
+ ## Maintaining the template
41
+
42
+ `template/` is **generated** from the sandbox repo so it never drifts. After
43
+ changing the sandbox, regenerate it from the repo root:
44
+
45
+ ```sh
46
+ pnpm create:sync
47
+ ```
package/index.mjs ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-obsidian-arrow — scaffold a new Obsidian-styled Arrow.js UI sandbox.
4
+ *
5
+ * pnpm create obsidian-arrow my-app # once published
6
+ * node create-obsidian-arrow/index.mjs my-app # locally, before publishing
7
+ *
8
+ * Copies the vendored template/ into <dir>, restores .gitignore (npm strips it
9
+ * from packages, so it's vendored as _gitignore), rewrites the project name,
10
+ * and runs `git init`. The template is a full, verified sandbox — see template/.
11
+ */
12
+ import { spawnSync } from "node:child_process";
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ const here = path.dirname(fileURLToPath(import.meta.url));
18
+ const templateDir = path.join(here, "template");
19
+
20
+ function fail(message) {
21
+ console.error(`create-obsidian-arrow: ${message}`);
22
+ process.exit(1);
23
+ }
24
+
25
+ const targetArg = process.argv[2];
26
+ if (!targetArg) {
27
+ fail("usage: create-obsidian-arrow <directory>");
28
+ }
29
+
30
+ const destRoot = path.resolve(process.cwd(), targetArg);
31
+ const appName = path.basename(destRoot);
32
+
33
+ if (fs.existsSync(destRoot) && fs.readdirSync(destRoot).length > 0) {
34
+ fail(`target "${targetArg}" already exists and is not empty.`);
35
+ }
36
+ if (!fs.existsSync(templateDir)) {
37
+ fail("template/ is missing — run scripts/sync-template.mjs to build it.");
38
+ }
39
+
40
+ function copyDir(src, dest) {
41
+ fs.mkdirSync(dest, { recursive: true });
42
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
43
+ const srcPath = path.join(src, entry.name);
44
+ // Vendored as _gitignore because npm omits .gitignore from published tarballs.
45
+ const destName = entry.name === "_gitignore" ? ".gitignore" : entry.name;
46
+ const destPath = path.join(dest, destName);
47
+ if (entry.isDirectory()) {
48
+ copyDir(srcPath, destPath);
49
+ } else {
50
+ fs.copyFileSync(srcPath, destPath);
51
+ }
52
+ }
53
+ }
54
+
55
+ copyDir(templateDir, destRoot);
56
+
57
+ // Personalize the project name. Use a targeted replace (not JSON.parse +
58
+ // stringify) so the template's existing Biome formatting stays byte-identical
59
+ // and the fresh project passes `pnpm lint` out of the box.
60
+ const pkgPath = path.join(destRoot, "package.json");
61
+ const pkgText = fs.readFileSync(pkgPath, "utf8");
62
+ const renamed = pkgText.replace(
63
+ /("name":\s*)"[^"]*"/,
64
+ (_match, prefix) => `${prefix}${JSON.stringify(appName)}`
65
+ );
66
+ fs.writeFileSync(pkgPath, renamed);
67
+
68
+ // Initialize a git repo (best-effort; ignore if git is unavailable).
69
+ spawnSync("git", ["init", "-q"], { cwd: destRoot, stdio: "ignore" });
70
+
71
+ console.log(`\nScaffolded ${appName} in ${path.relative(process.cwd(), destRoot) || "."}\n`);
72
+ console.log("Next steps:");
73
+ console.log(` cd ${targetArg}`);
74
+ console.log(" pnpm install");
75
+ console.log(" pnpm pull-css # extract Obsidian's app.css (macOS auto-detect)");
76
+ console.log(" pnpm dev\n");
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "create-obsidian-arrow",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold an Obsidian-styled Arrow.js UI sandbox (pnpm create obsidian-arrow <dir>).",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-obsidian-arrow": "index.mjs"
8
+ },
9
+ "files": [
10
+ "index.mjs",
11
+ "template"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "keywords": [
20
+ "obsidian",
21
+ "obsidian-plugin",
22
+ "arrow-js",
23
+ "arrowjs",
24
+ "create",
25
+ "scaffold",
26
+ "template",
27
+ "ui",
28
+ "prototyping"
29
+ ],
30
+ "author": "Kyle Brodeur",
31
+ "license": "MIT",
32
+ "homepage": "https://github.com/kylebrodeur/obsidian-arrow-sandbox#readme",
33
+ "bugs": "https://github.com/kylebrodeur/obsidian-arrow-sandbox/issues",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/kylebrodeur/obsidian-arrow-sandbox.git",
37
+ "directory": "create-obsidian-arrow"
38
+ },
39
+ "scripts": {}
40
+ }
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ check:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: pnpm/action-setup@v4
15
+ with:
16
+ version: 10.14.0
17
+
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: 22
21
+ cache: pnpm
22
+
23
+ - name: Install
24
+ run: pnpm install --frozen-lockfile
25
+
26
+ - name: Lint + format check (Biome)
27
+ run: pnpm biome ci .
28
+
29
+ - name: Typecheck
30
+ run: pnpm typecheck
31
+
32
+ - name: Test
33
+ run: pnpm test
34
+
35
+ - name: Build
36
+ run: pnpm build
@@ -0,0 +1,2 @@
1
+ pnpm exec lint-staged
2
+ pnpm typecheck
@@ -0,0 +1,90 @@
1
+ # AGENTS.md
2
+
3
+ Operating guide for AI agents working in **obsidian-arrow-sandbox** — a
4
+ client-only environment for prototyping [Arrow.js](https://arrow-js.com/) UI that
5
+ ports into an Obsidian plugin with near-zero refactoring.
6
+
7
+ Full design + rationale: [`docs/superpowers/specs`](docs/superpowers/specs/2026-06-29-obsidian-arrow-sandbox-design.md).
8
+ Deeper how-to skills: [`skills/`](skills/) (install via `npx skills`).
9
+
10
+ ## What this is (and isn't)
11
+
12
+ - **Is:** a Vite + TypeScript sandbox. Components use `@arrow-js/core`
13
+ (`reactive`, `html`, `component`, `watch`) + `@arrow-js/framework` (`boundary`),
14
+ mounted with `template(container)` — the exact call an Obsidian
15
+ `ItemView.onOpen()` makes. Styling comes entirely from Obsidian's real
16
+ `app.css`, loaded under Obsidian body classes.
17
+ - **Isn't:** an SSR app. There is **no server and no hydration** — an Obsidian
18
+ plugin renders only in the Electron renderer, so `@arrow-js/ssr` and
19
+ `@arrow-js/hydrate` are intentionally absent. Do not add them.
20
+
21
+ ## Run it
22
+
23
+ ```sh
24
+ pnpm install
25
+ pnpm pull-css # macOS-only auto-detect; else --path <asar|css> / OBSIDIAN_ASAR=<path>
26
+ pnpm dev # Vite + HMR
27
+ ```
28
+
29
+ `public/app.css` is **git-ignored** (Obsidian's proprietary CSS — not
30
+ redistributed); run `pnpm pull-css` once before `pnpm dev`.
31
+
32
+ ## Arrow v1.0.6 footguns — READ BEFORE WRITING TEMPLATES
33
+
34
+ These are hard runtime errors, not style nits. They are encoded in CI
35
+ (`test/template-footguns.test.mjs`) where possible.
36
+
37
+ 1. **No literal HTML comments inside `html\`\`` templates.** Arrow uses HTML
38
+ comments as expression-slot markers; a literal `<!-- … -->` makes the slot
39
+ count mismatch and throws `Invalid HTML position`. Use JS `//` comments
40
+ *outside* the template literal.
41
+ 2. **An attribute expression must be the *entire* value.** Good:
42
+ `class="${() => '…'}"`. Broken: `class="static ${() => '…'}"` (partial values
43
+ aren't registered as placeholders → throws). Build the full string in one
44
+ expression.
45
+ 3. **Reactive vs static.** `${data.x}` renders once at mount; `${() => data.x}`
46
+ is tracked and updates only that slot. Forgetting the `() =>` is the #1
47
+ "why isn't it updating" bug. Returning `false` from an attribute expression
48
+ **removes** the attribute (vs `""` which keeps it empty).
49
+ 4. **`@event` handlers must type the param `Event`, not a narrowed subtype.**
50
+ `(e: MouseEvent) => …` fails to assign to Arrow's handler type (parameter
51
+ contravariance) → `TS2345`. Use `(e: Event) => …` and narrow inside; no-arg
52
+ handlers are fine. (Caught by `tsc`; footguns 1–2 are caught by
53
+ `test/template-footguns.test.mjs`, which also flags this for inline handlers.)
54
+
55
+ Other conventions: property binding via `.prop` (`.checked="${() => …}"`),
56
+ events via `@event` (`@click="${fn}"`), keyed lists via
57
+ `html\`…\`.key(id)`, async sections via `component(asyncFn, { fallback })` wrapped
58
+ in `boundary()`.
59
+
60
+ ## CSS scoping
61
+
62
+ - Use Obsidian's own classes (`.setting-item`, `.clickable-icon`,
63
+ `.workspace-leaf`, `.vertical-tab-*`) and `var(--…)` tokens **first**. Add
64
+ custom CSS only where Obsidian has no class.
65
+ - Scope any custom rule under a container class + element type (e.g.
66
+ `.oas-frame button.oas-theme-toggle`) so it beats Obsidian's global
67
+ `button:not(.clickable-icon)` rule and never leaks. Sandbox-only chrome lives
68
+ in `src/sandbox/sandbox.css`; component styling stays on Obsidian classes.
69
+
70
+ ## Verify before claiming done
71
+
72
+ ```sh
73
+ pnpm typecheck # tsc --noEmit
74
+ pnpm test # node:test
75
+ pnpm lint # biome
76
+ pnpm check # all of the above (format + typecheck + test)
77
+ ```
78
+
79
+ Then confirm the actual render in the browser at the `pnpm dev` URL — check the
80
+ console is clean and the component looks like a real Obsidian pane. Do not claim
81
+ a component works on typecheck alone; Arrow's footguns only surface at render.
82
+
83
+ ## Porting a component into the plugin
84
+
85
+ Components are framework-light and use only Obsidian classes/tokens, so porting
86
+ is mechanical: copy the component file into the plugin's view directory and
87
+ mount it from `ItemView.onOpen()` via `template(this.contentEl)`. If it uses
88
+ `boundary()`/async components, add `@arrow-js/framework` to the plugin and the
89
+ `import '@arrow-js/framework'` side-effect import. Strip any sandbox chrome
90
+ (`src/sandbox/*`) — that stays here.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kyle Brodeur
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,116 @@
1
+ # Obsidian Arrow Sandbox
2
+
3
+ A client-only prototyping environment for building [Arrow.js](https://arrow-js.com/)
4
+ UI that drops into an Obsidian plugin with near-zero refactoring. Components are
5
+ written with `@arrow-js/core` (+ `@arrow-js/framework` for async boundaries) and
6
+ styled entirely by Obsidian's real `app.css`, so what you see here is what you
7
+ get inside a plugin view.
8
+
9
+ See the design + decision record in
10
+ [`docs/superpowers/specs`](docs/superpowers/specs/2026-06-29-obsidian-arrow-sandbox-design.md).
11
+
12
+ ## Scaffold a new project
13
+
14
+ Scaffold a fresh sandbox with the published initializer
15
+ ([`create-obsidian-arrow`](create-obsidian-arrow/)):
16
+
17
+ ```sh
18
+ npm create obsidian-arrow@latest my-app
19
+ # or: pnpm create obsidian-arrow my-app
20
+ # or: npx create-obsidian-arrow my-app
21
+ ```
22
+
23
+ Then `cd my-app && pnpm install && pnpm pull-css && pnpm dev`. A freshly
24
+ scaffolded project passes `pnpm run ci` out of the box. The initializer's
25
+ template is generated from this repo (`pnpm create:sync`), so it never drifts.
26
+
27
+ > This repo (the full sandbox) is **not** published to npm — only the
28
+ > `create-obsidian-arrow/` initializer is. An agent-onboarding prompt lives in
29
+ > [`docs/prompts/agent-setup.md`](docs/prompts/agent-setup.md).
30
+
31
+ ## Quick start
32
+
33
+ ```sh
34
+ pnpm install
35
+ pnpm pull-css # extract Obsidian's app.css from your local install (required)
36
+ pnpm dev # Vite dev server with HMR
37
+ ```
38
+
39
+ `pull-css` reads `app.css` out of `Obsidian.app/.../obsidian.asar` (macOS) and
40
+ writes `public/app.css`. Override the location with `--path <obsidian.asar|app.css>`
41
+ or `OBSIDIAN_ASAR=<path>`. **Run it once before `pnpm dev`** — it needs a local
42
+ Obsidian install.
43
+
44
+ > **Why it isn't committed:** `public/app.css` is **git-ignored**. It's
45
+ > Obsidian's proprietary stylesheet, so we don't redistribute it — each developer
46
+ > extracts it from their own licensed Obsidian install via `pnpm pull-css`.
47
+
48
+ > **Platform note:** automatic Obsidian discovery is currently **macOS-only** —
49
+ > `pull-css` knows where `Obsidian.app` lives on macOS. Windows and WSL paths are
50
+ > not auto-detected yet (planned). On those platforms, point the script at the
51
+ > file explicitly via `--path <obsidian.asar|app.css>` or `OBSIDIAN_ASAR=<path>`.
52
+
53
+ ## Scripts
54
+
55
+ | Script | What it does |
56
+ |---|---|
57
+ | `pnpm dev` | Vite dev server (client-only, HMR) |
58
+ | `pnpm build` / `pnpm preview` | Production build / preview |
59
+ | `pnpm typecheck` | `tsc --noEmit` |
60
+ | `pnpm test` | Node built-in test runner (`test/*.test.mjs`) |
61
+ | `pnpm lint` / `pnpm format` | Biome check / check-and-write |
62
+ | `pnpm check` | format + typecheck + test (local pre-flight) |
63
+ | `pnpm ci` | Biome CI + typecheck + test + build (what CI runs) |
64
+ | `pnpm pull-css` | refresh `public/app.css` from the local Obsidian install |
65
+
66
+ A husky `pre-commit` hook runs `lint-staged` (Biome on staged files) + a full
67
+ typecheck. CI (`.github/workflows/ci.yml`) runs the `ci` script on push/PR.
68
+
69
+ ## Bundled agent skills
70
+
71
+ This repo ships [`skills`](https://github.com/vercel-labs/skills)-compatible skills
72
+ under [`skills/`](skills/) — it doubles as a local skill marketplace:
73
+
74
+ - `obsidian-arrow-sandbox` — running and using this sandbox, and porting to a plugin.
75
+ - `arrow-js-obsidian-templates` — Arrow v1.0.6 template rules + footguns.
76
+ - `arrow-js-obsidian-patterns` — integration patterns: icons (Lucide / data-icon
77
+ sweep), CSS scoping vs Obsidian globals, mount/unmount lifecycle, reactive state.
78
+
79
+ Install them into your agent via the interactive TUI:
80
+
81
+ ```sh
82
+ pnpm skills:install # = npx skills add . (pick skills in the TUI)
83
+ ```
84
+
85
+ `postinstall` offers this automatically after `pnpm install`, but only in an
86
+ interactive terminal — it skips in CI / non-TTY (set `SKIP_SKILLS_INSTALL=1` to
87
+ opt out entirely). You can also add this repo from anywhere with
88
+ `npx skills add <git-url-or-path>`.
89
+
90
+ ## Porting a component into the plugin
91
+
92
+ Components use only Obsidian classes + `var(--…)` tokens and mount via
93
+ `template(container)` — the same call an `ItemView.onOpen()` makes. To port:
94
+ copy the component file into the plugin's view directory and mount it from
95
+ `onOpen()`. If it uses `boundary()`/async components, add `@arrow-js/framework`
96
+ to the plugin and the `import '@arrow-js/framework'` side-effect import.
97
+
98
+ ## Arrow v1.0.6 footguns (learned the hard way)
99
+
100
+ These are enforced/encoded so they don't regress:
101
+
102
+ 1. **No literal HTML comments inside `html\`\`` templates.** Arrow uses HTML
103
+ comments as expression-slot markers; a literal `<!-- … -->` inflates the slot
104
+ count and throws `Invalid HTML position`. Use JS `//` comments outside the
105
+ template. Guarded by `test/template-footguns.test.mjs`.
106
+ 2. **An attribute expression must be the *entire* value.** `class="${() => '…'}"`
107
+ works; `class="static ${() => '…'}"` (partial) does not register as a
108
+ placeholder and throws. Build the full string inside one expression.
109
+ 3. **Reactive vs static:** `${data.x}` renders once; `${() => data.x}` is tracked
110
+ and updates only that slot. Returning `false` from an attribute expression
111
+ removes the attribute.
112
+ 4. **`@event` handlers must type the param `Event`, not a narrowed subtype**
113
+ (`MouseEvent`, …). Parameter contravariance makes `(e: MouseEvent) => void`
114
+ fail to assign to Arrow's handler type (`TS2345`); use `(e: Event) => …` and
115
+ narrow inside. Caught by `tsc`, and the inline form is also guarded by
116
+ `test/template-footguns.test.mjs`.
@@ -0,0 +1,8 @@
1
+ node_modules
2
+ dist
3
+ .DS_Store
4
+ *.tgz
5
+
6
+ # Obsidian's app.css is proprietary — extract it locally with `pnpm pull-css`,
7
+ # don't commit/redistribute it.
8
+ public/app.css
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3
+ "organizeImports": {
4
+ "enabled": true
5
+ },
6
+ "linter": {
7
+ "enabled": true,
8
+ "rules": {
9
+ "recommended": true,
10
+ "suspicious": {
11
+ "noExplicitAny": "off"
12
+ }
13
+ }
14
+ },
15
+ "formatter": {
16
+ "enabled": true,
17
+ "indentStyle": "tab",
18
+ "lineWidth": 100
19
+ },
20
+ "javascript": {
21
+ "formatter": {
22
+ "quoteStyle": "double",
23
+ "trailingCommas": "es5",
24
+ "semicolons": "always"
25
+ }
26
+ },
27
+ "files": {
28
+ "ignore": ["dist", "node_modules", "public", "pnpm-lock.yaml", "create-obsidian-arrow/template"]
29
+ }
30
+ }
@@ -0,0 +1,94 @@
1
+ # Agent setup prompt
2
+
3
+ Copy everything in the block below and give it to a coding agent (Claude Code or
4
+ similar) to scaffold a new Obsidian Arrow sandbox and orient itself.
5
+
6
+ ---
7
+
8
+ ```text
9
+ Scaffold and set up an Obsidian Arrow Sandbox, then orient yourself on it.
10
+
11
+ WHAT IT IS
12
+ A client-only Vite + TypeScript sandbox for prototyping Obsidian plugin UI with
13
+ Arrow.js (@arrow-js/core + @arrow-js/framework), rendered against Obsidian's real
14
+ app.css so components look exactly as they will inside a plugin. Components mount
15
+ via `template(container)` — the same call an Obsidian ItemView.onOpen() makes —
16
+ so a finished component copy-pastes into a plugin with near-zero refactoring.
17
+ There is NO SSR/hydration (an Obsidian plugin renders entirely client-side); do
18
+ not add @arrow-js/ssr or @arrow-js/hydrate.
19
+
20
+ SCAFFOLD IT (use the published tool — pick one)
21
+ npm create obsidian-arrow@latest my-app
22
+ pnpm create obsidian-arrow my-app
23
+ npx create-obsidian-arrow my-app
24
+
25
+ Then:
26
+ cd my-app
27
+ pnpm install
28
+ pnpm pull-css # REQUIRED before dev — extracts Obsidian's app.css from your
29
+ # LOCAL install into public/app.css (git-ignored, never
30
+ # committed; it's Obsidian's proprietary CSS). Auto-detect is
31
+ # macOS-only; on Windows/WSL pass --path <obsidian.asar|app.css>
32
+ # or set OBSIDIAN_ASAR=<path>.
33
+ pnpm dev # open the printed URL: / is the examples index, /example the demo.
34
+ # The toolbar slider/presets + edge drag handle resize the panel.
35
+ pnpm skills:install # install the bundled agent skills (npx skills add .) — pick
36
+ # them in the TUI; this is how you load the domain knowledge.
37
+
38
+ READ FIRST
39
+ - AGENTS.md (root) — operating guide: run, footguns, CSS scoping, verify, port.
40
+ - skills/*/SKILL.md — obsidian-arrow-sandbox (workflow), arrow-js-obsidian-
41
+ templates (template syntax + footguns), arrow-js-obsidian-patterns (icons via
42
+ Lucide/data-icon sweep, CSS scoping, mount/unmount lifecycle, reactive state).
43
+ - docs/superpowers/specs/ — design + decision record (why core+framework, no SSR).
44
+
45
+ ARROW v1.0.6 FOOTGUNS — do not relearn these the hard way:
46
+ 1. NO literal HTML comments inside html`` templates — Arrow treats HTML comments
47
+ as expression-slot markers, so `<!-- … -->` throws "Invalid HTML position" at
48
+ render. Use JS // comments outside the template.
49
+ 2. An attribute expression must be the ENTIRE value. `class="${() => '…'}"` works;
50
+ `class="static ${() => '…'}"` (partial) throws. Build the full string in one
51
+ expression. Returning `false` from a whole-value attribute expr removes the attr.
52
+ 3. Reactive vs static: `${x}` renders once; `${() => x}` is tracked and updates
53
+ only that slot. Forgetting the `() =>` is the #1 "not updating" bug.
54
+ 4. @event handlers must type the param as `Event`, not a narrowed subtype
55
+ (MouseEvent, …) — contravariance makes it fail to assign (TS2345). Use
56
+ `(e: Event) => …` and narrow inside; no-arg handlers are fine.
57
+ Footguns 1, 2, 4 are guarded by test/template-footguns.test.mjs + tsc.
58
+
59
+ CONVENTIONS
60
+ - Use Obsidian's own classes (.setting-item, .clickable-icon, .workspace-leaf,
61
+ .vertical-tab-*, .modal, .mod-cta) and var(--…) tokens first; add custom CSS
62
+ only when Obsidian has no class, scoped under a container class + element type
63
+ (e.g. `.my-panel button.my-action`) so it beats Obsidian's global button rule.
64
+ - Sandbox-only chrome lives in src/sandbox/* — it does NOT port to a plugin.
65
+ - Add a demo by exporting an Arrow component and registering it in
66
+ src/examples/registry.ts (it shows on the index and at its own route).
67
+
68
+ VERIFY BEFORE CLAIMING DONE
69
+ - `pnpm typecheck && pnpm test && pnpm lint` (or `pnpm run ci` for the full chain).
70
+ - Then open the `pnpm dev` URL and confirm the component renders like a real
71
+ Obsidian pane with a clean console — Arrow's footguns only surface at render,
72
+ so typecheck passing is not proof a component works.
73
+
74
+ PORTING TO A PLUGIN
75
+ Copy the component file into the plugin's view dir and mount from
76
+ ItemView.onOpen() via `template(this.contentEl)`. If it uses boundary()/async
77
+ components, add @arrow-js/framework to the plugin and the side-effect
78
+ `import '@arrow-js/framework'`. Leave src/sandbox/* behind.
79
+
80
+ Start by scaffolding, running setup steps, then read AGENTS.md and confirm
81
+ `pnpm dev` renders /example correctly. Report what you see.
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Notes
87
+
88
+ - The scaffolder is published to npm as **`create-obsidian-arrow`**, which is why
89
+ all three of `npm create obsidian-arrow`, `pnpm create obsidian-arrow`, and
90
+ `npx create-obsidian-arrow` work (the `create-` prefix is what `*/create`
91
+ resolves to).
92
+ - `pnpm pull-css` is the one step that won't "just work" on a fresh machine —
93
+ it needs a local Obsidian install (macOS auto-detected). That's intentional:
94
+ Obsidian's `app.css` is proprietary and never committed/redistributed.