create-obsidian-arrow 0.3.0 → 0.4.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.
- package/README.md +6 -4
- package/index.mjs +29 -10
- package/package.json +1 -1
- package/template/AGENTS.md +8 -6
- package/template/README.md +13 -10
- package/template/docs/prompts/agent-setup.md +8 -6
- package/template/docs/prompts/update-existing.md +12 -8
- package/template/docs/workflow.md +5 -4
- package/template/package.json +5 -1
- package/template/pnpm-lock.yaml +197 -0
- package/template/src/components/DiffViewer.ts +42 -0
- package/template/src/main.ts +3 -3
- package/template/stories/DiffViewer.stories.ts +75 -0
- package/template/{src/components → stories}/SettingsPanel.stories.ts +3 -3
- package/template/{src/components → stories}/Toggle.stories.ts +3 -3
- package/template/test/token-utils.test.mjs +1 -1
- package/template/test/viewer-derive.test.mjs +5 -5
- package/template/test/viewer-stories.test.mjs +1 -1
- package/template/{src → tools}/sandbox/sandbox.css +42 -0
- package/template/{src → tools}/viewer/derive.ts +15 -5
- package/template/{src → tools}/viewer/discovery.ts +6 -5
- /package/template/{src → tools}/router/client.ts +0 -0
- /package/template/{src → tools}/router/routeToPage.ts +0 -0
- /package/template/{src → tools}/sandbox/frame.ts +0 -0
- /package/template/{src → tools}/sandbox/home.ts +0 -0
- /package/template/{src → tools}/sandbox/layout.ts +0 -0
- /package/template/{src → tools}/sandbox/shell.ts +0 -0
- /package/template/{src → tools}/sandbox/theme.ts +0 -0
- /package/template/{src → tools}/sandbox/toolbar.ts +0 -0
- /package/template/{src → tools}/viewer/ClassesPage.ts +0 -0
- /package/template/{src → tools}/viewer/ComponentsIndex.ts +0 -0
- /package/template/{src → tools}/viewer/StoryPage.ts +0 -0
- /package/template/{src → tools}/viewer/TokensPage.ts +0 -0
- /package/template/{src → tools}/viewer/obsidian-classes.ts +0 -0
- /package/template/{src → tools}/viewer/sidebar.ts +0 -0
- /package/template/{src → tools}/viewer/stories.ts +0 -0
- /package/template/{src → tools}/viewer/token-utils.ts +0 -0
- /package/template/{src → tools}/viewer/tokens.ts +0 -0
package/README.md
CHANGED
|
@@ -45,9 +45,11 @@ prints this hint when it detects nesting.
|
|
|
45
45
|
## Update an existing project
|
|
46
46
|
|
|
47
47
|
The scaffolder is create-only, but `update` refreshes an existing project's
|
|
48
|
-
**managed**
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
**managed** files (`scripts/`, `docs/`, `.github/`, `.husky/`, `biome.json`,
|
|
49
|
+
agent guides, `tools/viewer/`, `tools/router/`, `tools/sandbox/`, `src/main.ts`,
|
|
50
|
+
`src/utilities.css`, `test/`) and merges new `package.json` scripts/deps —
|
|
51
|
+
it never touches `src/components/`, `stories/`, `public/`, `index.html`, or
|
|
52
|
+
build configs:
|
|
51
53
|
|
|
52
54
|
```sh
|
|
53
55
|
npx create-obsidian-arrow update # in the project (or: update <dir>)
|
|
@@ -69,7 +71,7 @@ node create-obsidian-arrow/index.mjs update ../my-app # update
|
|
|
69
71
|
|
|
70
72
|
A full sandbox: client-only Vite + TS, `@arrow-js/core` + `@arrow-js/framework`
|
|
71
73
|
(no SSR), `routeToPage` + Navigation-API router, a Storybook-style component
|
|
72
|
-
viewer at `/components` (
|
|
74
|
+
viewer at `/components` (add `*.stories.ts` files in `stories/`), a live token
|
|
73
75
|
and class reference at `/reference`, Biome + husky pre-commit + `node:test` +
|
|
74
76
|
GitHub Actions CI, a `skills:install` that pulls the agent skills from the
|
|
75
77
|
published repo, and the `pull-css` script that extracts Obsidian's `app.css`.
|
package/index.mjs
CHANGED
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
* Scaffold copies the vendored template/, restores .gitignore (vendored as
|
|
14
14
|
* _gitignore), names the project, and runs `git init`.
|
|
15
15
|
*
|
|
16
|
-
* Update refreshes
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Update refreshes *managed* files from the template and merges package.json
|
|
17
|
+
* scripts + missing deps. Managed = scripts/, docs/, .github/, .husky/,
|
|
18
|
+
* biome.json, AGENTS.md, CLAUDE.md, tools/, src/main.ts, src/utilities.css,
|
|
19
|
+
* test/. Never touches src/components/, stories/, public/, index.html,
|
|
20
|
+
* vite.config.ts, tsconfig.json, or .gitignore. Use --dry-run first.
|
|
19
21
|
*/
|
|
20
22
|
import { spawnSync } from "node:child_process";
|
|
21
23
|
import fs from "node:fs";
|
|
@@ -25,12 +27,29 @@ import { fileURLToPath } from "node:url";
|
|
|
25
27
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
26
28
|
const templateDir = path.join(here, "template");
|
|
27
29
|
|
|
28
|
-
// Files/dirs the template owns and `update` may overwrite/merge.
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
30
|
+
// Files/dirs the template owns and `update` may overwrite/merge.
|
|
31
|
+
//
|
|
32
|
+
// src/components/ and stories/ are user-owned and never touched.
|
|
33
|
+
// tools/ contains all viewer/router/sandbox infrastructure and IS managed.
|
|
34
|
+
// Everything else (public/, index.html, vite.config.ts, tsconfig.json,
|
|
35
|
+
// lockfile, .gitignore, port-parity.json, …) is also user-owned. Skills aren't here — they're pulled from the published
|
|
36
|
+
// repo via `pnpm skills:update`, not vendored into the scaffold.
|
|
37
|
+
const MANAGED = [
|
|
38
|
+
// Tooling
|
|
39
|
+
"scripts",
|
|
40
|
+
"docs",
|
|
41
|
+
".github",
|
|
42
|
+
".husky",
|
|
43
|
+
"biome.json",
|
|
44
|
+
"AGENTS.md",
|
|
45
|
+
"CLAUDE.md",
|
|
46
|
+
// Viewer infrastructure — all managed; src/components/ and stories/ are user-owned
|
|
47
|
+
"tools",
|
|
48
|
+
"src/main.ts",
|
|
49
|
+
"src/utilities.css",
|
|
50
|
+
// Viewer tests
|
|
51
|
+
"test",
|
|
52
|
+
];
|
|
34
53
|
|
|
35
54
|
const argv = process.argv.slice(2);
|
|
36
55
|
const dryRun = argv.includes("--dry-run");
|
|
@@ -175,7 +194,7 @@ function update(targetArg) {
|
|
|
175
194
|
console.log(
|
|
176
195
|
dryRun
|
|
177
196
|
? "\n(dry run — nothing written.)"
|
|
178
|
-
: "\nLeft alone: src/, public/, index.html, vite.config.ts, tsconfig.json, .gitignore.\nRun `pnpm install` then `pnpm check`. Update skills separately with `pnpm skills:update`.\n"
|
|
197
|
+
: "\nLeft alone: src/components/, stories/, public/, index.html, vite.config.ts, tsconfig.json, .gitignore.\n(tools/, src/main.ts, src/utilities.css, and test/ were refreshed above.)\nRun `pnpm install` then `pnpm check`. Update skills separately with `pnpm skills:update`.\n"
|
|
179
198
|
);
|
|
180
199
|
}
|
|
181
200
|
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -91,10 +91,12 @@ in `boundary()`.
|
|
|
91
91
|
Prefer `class="oas-flex oas-gap-2"` over `style="display:flex;gap:8px"`.
|
|
92
92
|
- **Custom CSS last** — only when Obsidian has no class and utilities don't cover
|
|
93
93
|
it. Scope under container + element type, use `var(--…)` tokens for values.
|
|
94
|
-
- Add a story by creating
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
- Add a story by creating `stories/MyThing.stories.ts` — stories live in the
|
|
95
|
+
top-level `stories/` directory (not in `src/`). Import via
|
|
96
|
+
`"../tools/viewer/stories"` and `"../src/components/MyThing"`. See the
|
|
97
|
+
`obsidian-arrow-stories` skill for the full `defineStories` API (variants,
|
|
98
|
+
children, status, notes). Browse live tokens at `/reference`, curated classes
|
|
99
|
+
at `/reference/classes`.
|
|
98
100
|
|
|
99
101
|
## CSS scoping
|
|
100
102
|
|
|
@@ -104,7 +106,7 @@ in `boundary()`.
|
|
|
104
106
|
- Scope any custom rule under a container class + element type (e.g.
|
|
105
107
|
`.oas-frame button.oas-theme-toggle`) so it beats Obsidian's global
|
|
106
108
|
`button:not(.clickable-icon)` rule and never leaks. Sandbox-only chrome lives
|
|
107
|
-
in `
|
|
109
|
+
in `tools/sandbox/sandbox.css`; component styling stays on Obsidian classes.
|
|
108
110
|
|
|
109
111
|
## Verify before claiming done
|
|
110
112
|
|
|
@@ -126,7 +128,7 @@ is mechanical: copy the component file into the plugin's view directory and
|
|
|
126
128
|
mount it from `ItemView.onOpen()` via `template(this.contentEl)`. If it uses
|
|
127
129
|
`boundary()`/async components, add `@arrow-js/framework` to the plugin and the
|
|
128
130
|
`import '@arrow-js/framework'` side-effect import. Strip any sandbox chrome
|
|
129
|
-
(`
|
|
131
|
+
(`tools/sandbox/*`) — that stays here.
|
|
130
132
|
|
|
131
133
|
**Bring `src/utilities.css` along.** Components may use `oas-`-prefixed utility
|
|
132
134
|
classes (flex, gap, padding, typography, border — all built on Obsidian's token
|
package/template/README.md
CHANGED
|
@@ -24,9 +24,10 @@ Then `cd my-app && pnpm install && pnpm pull-css && pnpm dev`. A freshly
|
|
|
24
24
|
scaffolded project passes `pnpm run ci` out of the box. The initializer's
|
|
25
25
|
template is generated from this repo (`pnpm create:sync`), so it never drifts.
|
|
26
26
|
|
|
27
|
-
**Update an existing project's tooling** (refreshes
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
**Update an existing project's tooling** (refreshes managed files — scripts,
|
|
28
|
+
docs, CI, `biome.json`, agent guides, and the viewer/router/sandbox
|
|
29
|
+
infrastructure in `src/` — merges new `package.json` scripts/deps; never
|
|
30
|
+
touches `src/components/`, `stories/`, `public/`, `index.html`, or build configs):
|
|
30
31
|
|
|
31
32
|
```sh
|
|
32
33
|
npx create-obsidian-arrow update # in the project (or: update <dir>)
|
|
@@ -86,7 +87,7 @@ typecheck. CI (`.github/workflows/ci.yml`) runs the `ci` script on push/PR.
|
|
|
86
87
|
|
|
87
88
|
## Agent skills
|
|
88
89
|
|
|
89
|
-
This repo is the source of truth for
|
|
90
|
+
This repo is the source of truth for seven [`skills`](https://github.com/vercel-labs/skills)-compatible
|
|
90
91
|
skills under [`skills/`](skills/) — it's a skill marketplace. Scaffolds **don't
|
|
91
92
|
vendor copies**; they pull from this published repo, so installs are always
|
|
92
93
|
current.
|
|
@@ -143,21 +144,23 @@ with live previews.
|
|
|
143
144
|
|
|
144
145
|
### Add a story
|
|
145
146
|
|
|
146
|
-
Create `
|
|
147
|
+
Create `stories/MyThing.stories.ts` — stories live in the top-level `stories/`
|
|
148
|
+
directory, keeping `src/` purely for component code:
|
|
147
149
|
|
|
148
150
|
```ts
|
|
149
|
-
import { defineStories } from "../viewer/stories";
|
|
150
|
-
import { MyThing } from "
|
|
151
|
+
import { defineStories } from "../tools/viewer/stories";
|
|
152
|
+
import { MyThing } from "../src/components/MyThing";
|
|
151
153
|
|
|
152
154
|
export default defineStories({
|
|
153
155
|
description: "What it demonstrates.",
|
|
156
|
+
status: "draft",
|
|
154
157
|
variants: { default: () => MyThing() },
|
|
155
158
|
});
|
|
156
159
|
```
|
|
157
160
|
|
|
158
|
-
It appears in the sidebar and
|
|
159
|
-
viewer is derived from the
|
|
160
|
-
port to the plugin.
|
|
161
|
+
It appears in the sidebar and at `/components` automatically; the component path
|
|
162
|
+
shown in the viewer is derived from the filename. Stories are sandbox-only —
|
|
163
|
+
they never port to the plugin.
|
|
161
164
|
|
|
162
165
|
## Porting a component into the plugin
|
|
163
166
|
|
|
@@ -74,10 +74,11 @@ CONVENTIONS
|
|
|
74
74
|
.vertical-tab-*, .modal, .mod-cta) and var(--…) tokens first; add custom CSS
|
|
75
75
|
only when Obsidian has no class, scoped under a container class + element type
|
|
76
76
|
(e.g. `.my-panel button.my-action`) so it beats Obsidian's global button rule.
|
|
77
|
-
- Sandbox-only chrome lives in
|
|
78
|
-
- Add a
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
- Sandbox-only chrome lives in tools/sandbox/* — it does NOT port to a plugin.
|
|
78
|
+
- Add a story by creating `stories/MyThing.stories.ts` (top-level `stories/`
|
|
79
|
+
dir, NOT in `src/`). Import: `"../tools/viewer/stories"` and
|
|
80
|
+
`"../src/components/MyThing"`. It appears at `/components/<slug>` automatically.
|
|
81
|
+
Browse tokens at `/reference`, curated classes at `/reference/classes`.
|
|
81
82
|
|
|
82
83
|
VERIFY BEFORE CLAIMING DONE
|
|
83
84
|
- `pnpm typecheck && pnpm test && pnpm lint` (or `pnpm run ci` for the full chain).
|
|
@@ -92,11 +93,12 @@ components, add @arrow-js/framework to the plugin and the side-effect
|
|
|
92
93
|
`import '@arrow-js/framework'`. Also copy src/utilities.css into the plugin once
|
|
93
94
|
(all ported components share it) — components may use oas-* utility classes
|
|
94
95
|
(flex, gap, padding, text, border helpers built on Obsidian's token scale).
|
|
95
|
-
Leave
|
|
96
|
+
Leave tools/sandbox/* behind. Guard against drift with the porting-parity check
|
|
96
97
|
(see the arrow-js-obsidian-porting skill).
|
|
97
98
|
|
|
98
99
|
MAINTENANCE (existing project)
|
|
99
|
-
Refresh tooling
|
|
100
|
+
Refresh tooling with `npx create-obsidian-arrow update` (preserves src/components/
|
|
101
|
+
and stories/ — updates viewer, router, sandbox, utilities, and tooling files),
|
|
100
102
|
update skills with `pnpm skills:update`. See the obsidian-arrow-maintenance skill.
|
|
101
103
|
|
|
102
104
|
Start by scaffolding, running setup steps, then read AGENTS.md and confirm
|
|
@@ -11,10 +11,12 @@ Update this existing Obsidian Arrow project to the latest tooling and agent
|
|
|
11
11
|
skills. Do NOT change any of my source code.
|
|
12
12
|
|
|
13
13
|
HARD CONSTRAINTS
|
|
14
|
-
- Never modify src/, public/, index.html, vite.config.ts,
|
|
15
|
-
component/app code. Only managed tooling, skills,
|
|
14
|
+
- Never modify src/components/, stories/, public/, index.html, vite.config.ts,
|
|
15
|
+
tsconfig.json, or my component/app code. Only managed tooling, skills, docs,
|
|
16
|
+
and the viewer/router/sandbox infrastructure in src/ may change.
|
|
16
17
|
- Preview before applying anything destructive; don't commit unless I ask.
|
|
17
|
-
- At the end, prove src/
|
|
18
|
+
- At the end, prove src/components/ and stories/ were untouched
|
|
19
|
+
(`git diff --stat` shows no changes to those paths).
|
|
18
20
|
|
|
19
21
|
CONTEXT
|
|
20
22
|
Scaffolded from create-obsidian-arrow. Tooling + agent skills come from the
|
|
@@ -29,12 +31,14 @@ STEPS (in order)
|
|
|
29
31
|
project dir. If the OUTER root differs, that's where skills must be installed
|
|
30
32
|
so the session's skill:// registry (repo-root + global only) can find them.
|
|
31
33
|
|
|
32
|
-
2. Refresh tooling — preserves src/
|
|
34
|
+
2. Refresh tooling — preserves src/components/ only
|
|
33
35
|
- Preview: npx create-obsidian-arrow update --dry-run
|
|
34
36
|
- Apply: npx create-obsidian-arrow update
|
|
35
37
|
- Then: pnpm install
|
|
36
|
-
Refreshes scripts/, docs/, .github/, .husky/, biome.json, AGENTS.md, CLAUDE.md
|
|
37
|
-
|
|
38
|
+
Refreshes scripts/, docs/, .github/, .husky/, biome.json, AGENTS.md, CLAUDE.md,
|
|
39
|
+
tools/, src/main.ts, src/utilities.css, test/,
|
|
40
|
+
and merges package.json scripts/deps. It never touches src/components/, public/,
|
|
41
|
+
index.html, vite.config.ts, tsconfig.json, or .gitignore.
|
|
38
42
|
|
|
39
43
|
3. Reset agent skills to the current set
|
|
40
44
|
- Audit: npx skills list
|
|
@@ -73,6 +77,6 @@ REPORT
|
|
|
73
77
|
- Skills are pulled from the published repo, so step 3 is location-independent for
|
|
74
78
|
the *source* — only the *install location* matters (run it at the repo root the
|
|
75
79
|
agent uses; reload after).
|
|
76
|
-
- `npx create-obsidian-arrow update`
|
|
77
|
-
|
|
80
|
+
- `npx create-obsidian-arrow update` refreshes managed files and merges
|
|
81
|
+
`package.json`, but never overwrites `src/components/`, `stories/`, `public/`,
|
|
78
82
|
`index.html`, or build configs. See the `obsidian-arrow-maintenance` skill.
|
|
@@ -49,8 +49,9 @@ Then point the agent at [`AGENTS.md`](../AGENTS.md), or brief a fresh agent with
|
|
|
49
49
|
`skills` CLI is cwd-relative). To install them at the outer repo instead:
|
|
50
50
|
`pnpm skills:install --yes --project-dir=<outer-repo>` (or `--global`).
|
|
51
51
|
|
|
52
|
-
**Refresh an existing project's tooling** (scripts,
|
|
53
|
-
`src/`
|
|
52
|
+
**Refresh an existing project's tooling** (scripts, docs, CI, viewer/router/sandbox
|
|
53
|
+
in `src/` — never `src/components/` or `stories/`):
|
|
54
|
+
`npx create-obsidian-arrow update` (add `--dry-run` to preview).
|
|
54
55
|
|
|
55
56
|
## Build → verify → port loop
|
|
56
57
|
|
|
@@ -58,7 +59,7 @@ Then point the agent at [`AGENTS.md`](../AGENTS.md), or brief a fresh agent with
|
|
|
58
59
|
# 1. Check /reference/classes in the running sandbox — does Obsidian have a class
|
|
59
60
|
# for your pattern? Use it before writing custom CSS.
|
|
60
61
|
# 2. Add src/components/MyThing.ts (Arrow component)
|
|
61
|
-
# 3. Add
|
|
62
|
+
# 3. Add stories/MyThing.stories.ts (story file — lives in stories/, not src/)
|
|
62
63
|
pnpm dev # iterate with HMR
|
|
63
64
|
# /components → index of all discovered stories
|
|
64
65
|
# /components/my-thing → story for MyThing
|
|
@@ -77,7 +78,7 @@ status flag, DRY patterns) see the `obsidian-arrow-stories` skill.
|
|
|
77
78
|
**Port a component into a plugin:** copy the component file and `src/utilities.css`
|
|
78
79
|
(once, shared by all ports) into the plugin. Mount from `ItemView.onOpen()` via
|
|
79
80
|
`template(this.contentEl)`. If it uses `boundary()`/async, add
|
|
80
|
-
`@arrow-js/framework` and `import '@arrow-js/framework'`. Leave `
|
|
81
|
+
`@arrow-js/framework` and `import '@arrow-js/framework'`. Leave `tools/sandbox/*`
|
|
81
82
|
behind.
|
|
82
83
|
|
|
83
84
|
## Scaffold vs. clone
|
package/template/package.json
CHANGED
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@arrow-js/core": "^1.0.6",
|
|
30
|
-
"@arrow-js/framework": "^1.0.6"
|
|
30
|
+
"@arrow-js/framework": "^1.0.6",
|
|
31
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
32
|
+
"@codemirror/merge": "^6.12.2",
|
|
33
|
+
"@codemirror/state": "^6.7.0",
|
|
34
|
+
"@codemirror/view": "^6.43.4"
|
|
31
35
|
},
|
|
32
36
|
"devDependencies": {
|
|
33
37
|
"@biomejs/biome": "^1.9.4",
|
package/template/pnpm-lock.yaml
CHANGED
|
@@ -14,6 +14,18 @@ importers:
|
|
|
14
14
|
'@arrow-js/framework':
|
|
15
15
|
specifier: ^1.0.6
|
|
16
16
|
version: 1.0.6
|
|
17
|
+
'@codemirror/lang-markdown':
|
|
18
|
+
specifier: ^6.5.0
|
|
19
|
+
version: 6.5.0
|
|
20
|
+
'@codemirror/merge':
|
|
21
|
+
specifier: ^6.12.2
|
|
22
|
+
version: 6.12.2
|
|
23
|
+
'@codemirror/state':
|
|
24
|
+
specifier: ^6.7.0
|
|
25
|
+
version: 6.7.0
|
|
26
|
+
'@codemirror/view':
|
|
27
|
+
specifier: ^6.43.4
|
|
28
|
+
version: 6.43.4
|
|
17
29
|
devDependencies:
|
|
18
30
|
'@biomejs/biome':
|
|
19
31
|
specifier: ^1.9.4
|
|
@@ -99,6 +111,36 @@ packages:
|
|
|
99
111
|
cpu: [x64]
|
|
100
112
|
os: [win32]
|
|
101
113
|
|
|
114
|
+
'@codemirror/autocomplete@6.20.3':
|
|
115
|
+
resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==}
|
|
116
|
+
|
|
117
|
+
'@codemirror/lang-css@6.3.1':
|
|
118
|
+
resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
|
|
119
|
+
|
|
120
|
+
'@codemirror/lang-html@6.4.11':
|
|
121
|
+
resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==}
|
|
122
|
+
|
|
123
|
+
'@codemirror/lang-javascript@6.2.5':
|
|
124
|
+
resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==}
|
|
125
|
+
|
|
126
|
+
'@codemirror/lang-markdown@6.5.0':
|
|
127
|
+
resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==}
|
|
128
|
+
|
|
129
|
+
'@codemirror/language@6.12.4':
|
|
130
|
+
resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==}
|
|
131
|
+
|
|
132
|
+
'@codemirror/lint@6.9.7':
|
|
133
|
+
resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==}
|
|
134
|
+
|
|
135
|
+
'@codemirror/merge@6.12.2':
|
|
136
|
+
resolution: {integrity: sha512-V8JvyAPjHbPupqP7BeMcsdsYCbyPij74jxIbaIJDORI+VZzW44zFmon8bF+oxGWvOKhcRmkiUMXd8MxHr3YA2w==}
|
|
137
|
+
|
|
138
|
+
'@codemirror/state@6.7.0':
|
|
139
|
+
resolution: {integrity: sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==}
|
|
140
|
+
|
|
141
|
+
'@codemirror/view@6.43.4':
|
|
142
|
+
resolution: {integrity: sha512-YImu23iyKfncJzT7sRy+rEqEhSc8RhOHqDxwy4WzXRKJwYm6iwf/9OJk5ctCAdZ6yi2ZqaGEvmf55fSVqMDrgg==}
|
|
143
|
+
|
|
102
144
|
'@csstools/color-helpers@5.1.0':
|
|
103
145
|
resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
|
|
104
146
|
engines: {node: '>=18'}
|
|
@@ -136,6 +178,30 @@ packages:
|
|
|
136
178
|
'@emnapi/wasi-threads@1.2.2':
|
|
137
179
|
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
|
|
138
180
|
|
|
181
|
+
'@lezer/common@1.5.2':
|
|
182
|
+
resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==}
|
|
183
|
+
|
|
184
|
+
'@lezer/css@1.3.4':
|
|
185
|
+
resolution: {integrity: sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==}
|
|
186
|
+
|
|
187
|
+
'@lezer/highlight@1.2.3':
|
|
188
|
+
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
|
|
189
|
+
|
|
190
|
+
'@lezer/html@1.3.13':
|
|
191
|
+
resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==}
|
|
192
|
+
|
|
193
|
+
'@lezer/javascript@1.5.4':
|
|
194
|
+
resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==}
|
|
195
|
+
|
|
196
|
+
'@lezer/lr@1.4.10':
|
|
197
|
+
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
|
|
198
|
+
|
|
199
|
+
'@lezer/markdown@1.6.4':
|
|
200
|
+
resolution: {integrity: sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==}
|
|
201
|
+
|
|
202
|
+
'@marijn/find-cluster-break@1.0.3':
|
|
203
|
+
resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==}
|
|
204
|
+
|
|
139
205
|
'@napi-rs/wasm-runtime@1.1.6':
|
|
140
206
|
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
|
|
141
207
|
peerDependencies:
|
|
@@ -288,6 +354,9 @@ packages:
|
|
|
288
354
|
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
|
|
289
355
|
engines: {node: '>=18'}
|
|
290
356
|
|
|
357
|
+
crelt@1.0.7:
|
|
358
|
+
resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==}
|
|
359
|
+
|
|
291
360
|
cross-spawn@7.0.6:
|
|
292
361
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
|
293
362
|
engines: {node: '>= 8'}
|
|
@@ -648,6 +717,9 @@ packages:
|
|
|
648
717
|
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
|
|
649
718
|
engines: {node: '>=12'}
|
|
650
719
|
|
|
720
|
+
style-mod@4.1.3:
|
|
721
|
+
resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==}
|
|
722
|
+
|
|
651
723
|
symbol-tree@3.2.4:
|
|
652
724
|
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
|
653
725
|
|
|
@@ -731,6 +803,9 @@ packages:
|
|
|
731
803
|
yaml:
|
|
732
804
|
optional: true
|
|
733
805
|
|
|
806
|
+
w3c-keyname@2.2.8:
|
|
807
|
+
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
|
808
|
+
|
|
734
809
|
w3c-xmlserializer@5.0.0:
|
|
735
810
|
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
|
|
736
811
|
engines: {node: '>=18'}
|
|
@@ -843,6 +918,87 @@ snapshots:
|
|
|
843
918
|
'@biomejs/cli-win32-x64@1.9.4':
|
|
844
919
|
optional: true
|
|
845
920
|
|
|
921
|
+
'@codemirror/autocomplete@6.20.3':
|
|
922
|
+
dependencies:
|
|
923
|
+
'@codemirror/language': 6.12.4
|
|
924
|
+
'@codemirror/state': 6.7.0
|
|
925
|
+
'@codemirror/view': 6.43.4
|
|
926
|
+
'@lezer/common': 1.5.2
|
|
927
|
+
|
|
928
|
+
'@codemirror/lang-css@6.3.1':
|
|
929
|
+
dependencies:
|
|
930
|
+
'@codemirror/autocomplete': 6.20.3
|
|
931
|
+
'@codemirror/language': 6.12.4
|
|
932
|
+
'@codemirror/state': 6.7.0
|
|
933
|
+
'@lezer/common': 1.5.2
|
|
934
|
+
'@lezer/css': 1.3.4
|
|
935
|
+
|
|
936
|
+
'@codemirror/lang-html@6.4.11':
|
|
937
|
+
dependencies:
|
|
938
|
+
'@codemirror/autocomplete': 6.20.3
|
|
939
|
+
'@codemirror/lang-css': 6.3.1
|
|
940
|
+
'@codemirror/lang-javascript': 6.2.5
|
|
941
|
+
'@codemirror/language': 6.12.4
|
|
942
|
+
'@codemirror/state': 6.7.0
|
|
943
|
+
'@codemirror/view': 6.43.4
|
|
944
|
+
'@lezer/common': 1.5.2
|
|
945
|
+
'@lezer/css': 1.3.4
|
|
946
|
+
'@lezer/html': 1.3.13
|
|
947
|
+
|
|
948
|
+
'@codemirror/lang-javascript@6.2.5':
|
|
949
|
+
dependencies:
|
|
950
|
+
'@codemirror/autocomplete': 6.20.3
|
|
951
|
+
'@codemirror/language': 6.12.4
|
|
952
|
+
'@codemirror/lint': 6.9.7
|
|
953
|
+
'@codemirror/state': 6.7.0
|
|
954
|
+
'@codemirror/view': 6.43.4
|
|
955
|
+
'@lezer/common': 1.5.2
|
|
956
|
+
'@lezer/javascript': 1.5.4
|
|
957
|
+
|
|
958
|
+
'@codemirror/lang-markdown@6.5.0':
|
|
959
|
+
dependencies:
|
|
960
|
+
'@codemirror/autocomplete': 6.20.3
|
|
961
|
+
'@codemirror/lang-html': 6.4.11
|
|
962
|
+
'@codemirror/language': 6.12.4
|
|
963
|
+
'@codemirror/state': 6.7.0
|
|
964
|
+
'@codemirror/view': 6.43.4
|
|
965
|
+
'@lezer/common': 1.5.2
|
|
966
|
+
'@lezer/markdown': 1.6.4
|
|
967
|
+
|
|
968
|
+
'@codemirror/language@6.12.4':
|
|
969
|
+
dependencies:
|
|
970
|
+
'@codemirror/state': 6.7.0
|
|
971
|
+
'@codemirror/view': 6.43.4
|
|
972
|
+
'@lezer/common': 1.5.2
|
|
973
|
+
'@lezer/highlight': 1.2.3
|
|
974
|
+
'@lezer/lr': 1.4.10
|
|
975
|
+
style-mod: 4.1.3
|
|
976
|
+
|
|
977
|
+
'@codemirror/lint@6.9.7':
|
|
978
|
+
dependencies:
|
|
979
|
+
'@codemirror/state': 6.7.0
|
|
980
|
+
'@codemirror/view': 6.43.4
|
|
981
|
+
crelt: 1.0.7
|
|
982
|
+
|
|
983
|
+
'@codemirror/merge@6.12.2':
|
|
984
|
+
dependencies:
|
|
985
|
+
'@codemirror/language': 6.12.4
|
|
986
|
+
'@codemirror/state': 6.7.0
|
|
987
|
+
'@codemirror/view': 6.43.4
|
|
988
|
+
'@lezer/highlight': 1.2.3
|
|
989
|
+
style-mod: 4.1.3
|
|
990
|
+
|
|
991
|
+
'@codemirror/state@6.7.0':
|
|
992
|
+
dependencies:
|
|
993
|
+
'@marijn/find-cluster-break': 1.0.3
|
|
994
|
+
|
|
995
|
+
'@codemirror/view@6.43.4':
|
|
996
|
+
dependencies:
|
|
997
|
+
'@codemirror/state': 6.7.0
|
|
998
|
+
crelt: 1.0.7
|
|
999
|
+
style-mod: 4.1.3
|
|
1000
|
+
w3c-keyname: 2.2.8
|
|
1001
|
+
|
|
846
1002
|
'@csstools/color-helpers@5.1.0': {}
|
|
847
1003
|
|
|
848
1004
|
'@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
|
|
@@ -879,6 +1035,41 @@ snapshots:
|
|
|
879
1035
|
tslib: 2.8.1
|
|
880
1036
|
optional: true
|
|
881
1037
|
|
|
1038
|
+
'@lezer/common@1.5.2': {}
|
|
1039
|
+
|
|
1040
|
+
'@lezer/css@1.3.4':
|
|
1041
|
+
dependencies:
|
|
1042
|
+
'@lezer/common': 1.5.2
|
|
1043
|
+
'@lezer/highlight': 1.2.3
|
|
1044
|
+
'@lezer/lr': 1.4.10
|
|
1045
|
+
|
|
1046
|
+
'@lezer/highlight@1.2.3':
|
|
1047
|
+
dependencies:
|
|
1048
|
+
'@lezer/common': 1.5.2
|
|
1049
|
+
|
|
1050
|
+
'@lezer/html@1.3.13':
|
|
1051
|
+
dependencies:
|
|
1052
|
+
'@lezer/common': 1.5.2
|
|
1053
|
+
'@lezer/highlight': 1.2.3
|
|
1054
|
+
'@lezer/lr': 1.4.10
|
|
1055
|
+
|
|
1056
|
+
'@lezer/javascript@1.5.4':
|
|
1057
|
+
dependencies:
|
|
1058
|
+
'@lezer/common': 1.5.2
|
|
1059
|
+
'@lezer/highlight': 1.2.3
|
|
1060
|
+
'@lezer/lr': 1.4.10
|
|
1061
|
+
|
|
1062
|
+
'@lezer/lr@1.4.10':
|
|
1063
|
+
dependencies:
|
|
1064
|
+
'@lezer/common': 1.5.2
|
|
1065
|
+
|
|
1066
|
+
'@lezer/markdown@1.6.4':
|
|
1067
|
+
dependencies:
|
|
1068
|
+
'@lezer/common': 1.5.2
|
|
1069
|
+
'@lezer/highlight': 1.2.3
|
|
1070
|
+
|
|
1071
|
+
'@marijn/find-cluster-break@1.0.3': {}
|
|
1072
|
+
|
|
882
1073
|
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
|
|
883
1074
|
dependencies:
|
|
884
1075
|
'@emnapi/core': 1.11.1
|
|
@@ -986,6 +1177,8 @@ snapshots:
|
|
|
986
1177
|
|
|
987
1178
|
commander@13.1.0: {}
|
|
988
1179
|
|
|
1180
|
+
crelt@1.0.7: {}
|
|
1181
|
+
|
|
989
1182
|
cross-spawn@7.0.6:
|
|
990
1183
|
dependencies:
|
|
991
1184
|
path-key: 3.1.1
|
|
@@ -1326,6 +1519,8 @@ snapshots:
|
|
|
1326
1519
|
|
|
1327
1520
|
strip-final-newline@3.0.0: {}
|
|
1328
1521
|
|
|
1522
|
+
style-mod@4.1.3: {}
|
|
1523
|
+
|
|
1329
1524
|
symbol-tree@3.2.4: {}
|
|
1330
1525
|
|
|
1331
1526
|
tinyglobby@0.2.17:
|
|
@@ -1372,6 +1567,8 @@ snapshots:
|
|
|
1372
1567
|
fsevents: 2.3.3
|
|
1373
1568
|
yaml: 2.9.0
|
|
1374
1569
|
|
|
1570
|
+
w3c-keyname@2.2.8: {}
|
|
1571
|
+
|
|
1375
1572
|
w3c-xmlserializer@5.0.0:
|
|
1376
1573
|
dependencies:
|
|
1377
1574
|
xml-name-validator: 5.0.0
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ArrowExpression } from "@arrow-js/core";
|
|
2
|
+
import { markdown } from "@codemirror/lang-markdown";
|
|
3
|
+
import { MergeView } from "@codemirror/merge";
|
|
4
|
+
import { EditorState } from "@codemirror/state";
|
|
5
|
+
|
|
6
|
+
export interface DiffViewerOptions {
|
|
7
|
+
original: string;
|
|
8
|
+
modified: string;
|
|
9
|
+
/** Which side shows original (a) and which shows modified (b). Default: a-b */
|
|
10
|
+
orientation?: "a-b" | "b-a";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* CodeMirror 6 MergeView — side-by-side diff using the same CM6 engine as
|
|
15
|
+
* Obsidian's editor. Obsidian's app.css styles .cm-* classes automatically,
|
|
16
|
+
* so this looks native without additional theming.
|
|
17
|
+
*
|
|
18
|
+
* Arrow's ArrowExpression type doesn't include Node but accepts it at runtime.
|
|
19
|
+
* The cast is intentional — CM6 manages its own DOM and Arrow inserts it as-is.
|
|
20
|
+
*/
|
|
21
|
+
export function DiffViewer(options: DiffViewerOptions): ArrowExpression {
|
|
22
|
+
const el = document.createElement("div");
|
|
23
|
+
el.className = "oas-diff-viewer";
|
|
24
|
+
|
|
25
|
+
new MergeView({
|
|
26
|
+
a: {
|
|
27
|
+
doc: options.original,
|
|
28
|
+
extensions: [markdown(), EditorState.readOnly.of(true)],
|
|
29
|
+
},
|
|
30
|
+
b: {
|
|
31
|
+
doc: options.modified,
|
|
32
|
+
extensions: [markdown(), EditorState.readOnly.of(true)],
|
|
33
|
+
},
|
|
34
|
+
parent: el,
|
|
35
|
+
orientation: options.orientation ?? "a-b",
|
|
36
|
+
highlightChanges: true,
|
|
37
|
+
collapseUnchanged: { margin: 3, minSize: 4 },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// biome-ignore lint/suspicious/noExplicitAny: Arrow accepts Node at runtime; type doesn't include it
|
|
41
|
+
return el as unknown as ArrowExpression;
|
|
42
|
+
}
|
package/template/src/main.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// This is the one extra line a plugin would add to adopt @arrow-js/framework.
|
|
3
3
|
import "@arrow-js/framework";
|
|
4
4
|
|
|
5
|
-
import { startRouter } from "
|
|
6
|
-
import { applyTheme } from "
|
|
5
|
+
import { startRouter } from "../tools/router/client";
|
|
6
|
+
import { applyTheme } from "../tools/sandbox/theme";
|
|
7
7
|
import "./utilities.css";
|
|
8
|
-
import "
|
|
8
|
+
import "../tools/sandbox/sandbox.css";
|
|
9
9
|
|
|
10
10
|
applyTheme();
|
|
11
11
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DiffViewer } from "../src/components/DiffViewer";
|
|
2
|
+
import { defineStories } from "../tools/viewer/stories";
|
|
3
|
+
|
|
4
|
+
const ORIGINAL = `---
|
|
5
|
+
title: Meeting Notes
|
|
6
|
+
status: draft
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Team Standup
|
|
10
|
+
|
|
11
|
+
Quick notes from today's standup.
|
|
12
|
+
|
|
13
|
+
## Done
|
|
14
|
+
|
|
15
|
+
- Reviewed the PR for the search panel
|
|
16
|
+
- Fixed the token filter in the reference viewer
|
|
17
|
+
|
|
18
|
+
## In Progress
|
|
19
|
+
|
|
20
|
+
- Arrow component for the diff viewer
|
|
21
|
+
- Documentation updates
|
|
22
|
+
|
|
23
|
+
## Notes
|
|
24
|
+
|
|
25
|
+
See the project board for full task breakdown.
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const MODIFIED = `---
|
|
29
|
+
title: Meeting Notes
|
|
30
|
+
status: complete
|
|
31
|
+
tags: [standup, team]
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# Team Standup — 2026-07-02
|
|
35
|
+
|
|
36
|
+
Notes from today's standup.
|
|
37
|
+
|
|
38
|
+
## Done
|
|
39
|
+
|
|
40
|
+
- Reviewed and merged the PR for the search panel
|
|
41
|
+
- Fixed the token filter in the reference viewer
|
|
42
|
+
- Added DiffViewer component to the sandbox
|
|
43
|
+
|
|
44
|
+
## In Progress
|
|
45
|
+
|
|
46
|
+
- Documentation updates
|
|
47
|
+
- Editor pane integration
|
|
48
|
+
|
|
49
|
+
## Notes
|
|
50
|
+
|
|
51
|
+
See the project board for full task breakdown.
|
|
52
|
+
Next standup: Thursday.
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const SHORT_ORIGINAL = `function greet(name: string): string {
|
|
56
|
+
return "Hello, " + name;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const SHORT_MODIFIED = `function greet(name: string, greeting = "Hello"): string {
|
|
61
|
+
return \`\${greeting}, \${name}!\`;
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
export default defineStories({
|
|
66
|
+
description:
|
|
67
|
+
"CodeMirror 6 MergeView — side-by-side diff using the same engine as Obsidian's editor. Changed lines highlight inline; unchanged sections collapse.",
|
|
68
|
+
status: "draft",
|
|
69
|
+
variants: {
|
|
70
|
+
"markdown document": () => DiffViewer({ original: ORIGINAL, modified: MODIFIED }),
|
|
71
|
+
"code snippet": () => DiffViewer({ original: SHORT_ORIGINAL, modified: SHORT_MODIFIED }),
|
|
72
|
+
"reversed (b-a)": () =>
|
|
73
|
+
DiffViewer({ original: ORIGINAL, modified: MODIFIED, orientation: "b-a" }),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { SettingsPanel } from "../src/components/SettingsPanel";
|
|
2
|
+
import { defineStories } from "../tools/viewer/stories";
|
|
3
3
|
|
|
4
4
|
export default defineStories({
|
|
5
5
|
description: "Vertical tabs, toggles, a keyed list, and an async boundary() section.",
|
|
6
|
+
status: "live",
|
|
6
7
|
variants: {
|
|
7
8
|
default: () => SettingsPanel(),
|
|
8
9
|
},
|
|
9
10
|
children: ["toggle"],
|
|
10
|
-
status: "live",
|
|
11
11
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { reactive } from "@arrow-js/core";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Toggle } from "../src/components/SettingsPanel";
|
|
3
|
+
import { defineStories } from "../tools/viewer/stories";
|
|
4
4
|
|
|
5
5
|
export default defineStories({
|
|
6
6
|
description: "Obsidian checkbox-container toggle used by SettingsPanel.",
|
|
7
7
|
componentPath: "src/components/SettingsPanel.ts",
|
|
8
|
+
status: "live",
|
|
8
9
|
variants: {
|
|
9
10
|
interactive: () => {
|
|
10
11
|
const state = reactive({ on: true });
|
|
@@ -24,5 +25,4 @@ export default defineStories({
|
|
|
24
25
|
notes: "Static off state (click does nothing).",
|
|
25
26
|
},
|
|
26
27
|
},
|
|
27
|
-
status: "live",
|
|
28
28
|
});
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
filterTokens,
|
|
6
6
|
groupTokens,
|
|
7
7
|
parseCustomProps,
|
|
8
|
-
} from "../
|
|
8
|
+
} from "../tools/viewer/token-utils.ts";
|
|
9
9
|
|
|
10
10
|
test("parseCustomProps extracts custom property declarations from rule text", () => {
|
|
11
11
|
const css = "body.theme-dark { --text-accent: #a288ff; --size-4-2: 8px; color: red; }";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
kebabCase,
|
|
6
6
|
storyMetaFromGlobKey,
|
|
7
7
|
titleFromSlug,
|
|
8
|
-
} from "../
|
|
8
|
+
} from "../tools/viewer/derive.ts";
|
|
9
9
|
|
|
10
10
|
test("kebabCase converts PascalCase and spaces/underscores", () => {
|
|
11
11
|
assert.equal(kebabCase("SettingsPanel"), "settings-panel");
|
|
@@ -19,16 +19,16 @@ test("titleFromSlug start-cases", () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
test("storyMetaFromGlobKey derives slug + repo-relative paths", () => {
|
|
22
|
-
const meta = storyMetaFromGlobKey("
|
|
22
|
+
const meta = storyMetaFromGlobKey("../../stories/SettingsPanel.stories.ts");
|
|
23
23
|
assert.equal(meta.slug, "settings-panel");
|
|
24
|
-
assert.equal(meta.storiesPath, "
|
|
24
|
+
assert.equal(meta.storiesPath, "stories/SettingsPanel.stories.ts");
|
|
25
25
|
assert.equal(meta.componentPath, "src/components/SettingsPanel.ts");
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test("storyMetaFromGlobKey handles nested directories", () => {
|
|
29
|
-
const meta = storyMetaFromGlobKey("
|
|
29
|
+
const meta = storyMetaFromGlobKey("../../stories/chat/MessageFeed.stories.ts");
|
|
30
30
|
assert.equal(meta.slug, "message-feed");
|
|
31
|
-
assert.equal(meta.storiesPath, "
|
|
31
|
+
assert.equal(meta.storiesPath, "stories/chat/MessageFeed.stories.ts");
|
|
32
32
|
assert.equal(meta.componentPath, "src/components/chat/MessageFeed.ts");
|
|
33
33
|
});
|
|
34
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "node:test";
|
|
3
|
-
import { defineStories, normalizeVariants, validateStoryDef } from "../
|
|
3
|
+
import { defineStories, normalizeVariants, validateStoryDef } from "../tools/viewer/stories.ts";
|
|
4
4
|
|
|
5
5
|
test("defineStories is an identity that preserves the def", () => {
|
|
6
6
|
const def = { variants: { default: () => "x" } };
|
|
@@ -430,3 +430,45 @@ body {
|
|
|
430
430
|
width: auto;
|
|
431
431
|
max-width: 100%;
|
|
432
432
|
}
|
|
433
|
+
|
|
434
|
+
/* DiffViewer — CodeMirror 6 MergeView container */
|
|
435
|
+
.oas-diff-viewer {
|
|
436
|
+
height: 480px;
|
|
437
|
+
overflow: hidden;
|
|
438
|
+
border: 1px solid var(--background-modifier-border);
|
|
439
|
+
border-radius: var(--radius-m);
|
|
440
|
+
font-family: var(--font-monospace);
|
|
441
|
+
font-size: var(--font-ui-small);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.oas-diff-viewer .cm-mergeView {
|
|
445
|
+
height: 100%;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.oas-diff-viewer .cm-editor {
|
|
449
|
+
height: 100%;
|
|
450
|
+
background: var(--background-primary);
|
|
451
|
+
color: var(--text-normal);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.oas-diff-viewer .cm-scroller {
|
|
455
|
+
overflow: auto;
|
|
456
|
+
font-family: var(--font-monospace);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.oas-diff-viewer .cm-mergeViewEditor {
|
|
460
|
+
border-right: 1px solid var(--background-modifier-border);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.oas-diff-viewer .cm-mergeViewEditor:last-child {
|
|
464
|
+
border-right: none;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.oas-diff-viewer .cm-deletedChunk {
|
|
468
|
+
background: color-mix(in srgb, var(--color-red) 15%, transparent);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.oas-diff-viewer .cm-insertedChunk,
|
|
472
|
+
.oas-diff-viewer .cm-changedLine {
|
|
473
|
+
background: color-mix(in srgb, var(--color-green) 12%, transparent);
|
|
474
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Pure derivation helpers for the component viewer. DOM-free so node:test can
|
|
3
3
|
* cover them directly (via --experimental-strip-types).
|
|
4
4
|
*
|
|
5
|
-
* Glob keys come from import.meta.glob in src/viewer/discovery.ts,
|
|
6
|
-
*
|
|
5
|
+
* Glob keys come from import.meta.glob in src/viewer/discovery.ts, relative to
|
|
6
|
+
* src/viewer/ — e.g. "../../stories/SettingsPanel.stories.ts".
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export interface StoryPathMeta {
|
|
@@ -27,10 +27,20 @@ export function titleFromSlug(slug: string): string {
|
|
|
27
27
|
.join(" ");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* "../../stories/Foo.stories.ts" → project-relative paths + slug.
|
|
32
|
+
*
|
|
33
|
+
* Glob keys are relative to src/viewer/, so "../../" reaches the project root.
|
|
34
|
+
* Default componentPath mirrors the stories/ subtree into src/components/:
|
|
35
|
+
* stories/Foo.stories.ts → src/components/Foo.ts
|
|
36
|
+
* stories/chat/Foo.stories.ts → src/components/chat/Foo.ts
|
|
37
|
+
* Override with componentPath in the story def when the component lives elsewhere.
|
|
38
|
+
*/
|
|
31
39
|
export function storyMetaFromGlobKey(globKey: string): StoryPathMeta {
|
|
32
|
-
const storiesPath =
|
|
33
|
-
const componentPath = storiesPath
|
|
40
|
+
const storiesPath = globKey.replace(/^(\.\.\/)+/, "");
|
|
41
|
+
const componentPath = storiesPath
|
|
42
|
+
.replace(/^stories\//, "src/components/")
|
|
43
|
+
.replace(/\.stories\.ts$/, ".ts");
|
|
34
44
|
const base = storiesPath.split("/").pop() ?? "";
|
|
35
45
|
const slug = kebabCase(base.replace(/\.stories\.ts$/, ""));
|
|
36
46
|
return { slug, storiesPath, componentPath };
|
|
@@ -3,10 +3,11 @@ import type { StoryDef, StoryVariant } from "./stories";
|
|
|
3
3
|
import { normalizeVariants, validateStoryDef } from "./stories";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Discovers
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Discovers stories/*.stories.ts files at build time. Stories live in the
|
|
7
|
+
* top-level stories/ directory (not in src/), keeping src/ pure component code.
|
|
8
|
+
* The glob key IS the file path, so src locations shown in the viewer are
|
|
9
|
+
* derived, never hand-maintained. One malformed story file is skipped (with a
|
|
10
|
+
* console.warn and an entry in invalidStories) — it never blanks the viewer.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
export interface DiscoveredStory {
|
|
@@ -25,7 +26,7 @@ export interface InvalidStory {
|
|
|
25
26
|
reason: string;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const modules = import.meta.glob("
|
|
29
|
+
const modules = import.meta.glob("../../stories/**/*.stories.ts", { eager: true }) as Record<
|
|
29
30
|
string,
|
|
30
31
|
{ default?: unknown }
|
|
31
32
|
>;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|