create-obsidian-arrow 0.5.0 → 0.5.2
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 +7 -7
- package/cli/create.mjs +65 -0
- package/cli/detect-pm.mjs +20 -0
- package/cli/lib.mjs +117 -0
- package/cli/refresh.mjs +65 -0
- package/index.mjs +47 -204
- package/package.json +11 -2
- package/template/.husky/pre-commit +3 -2
- package/template/AGENTS.md +58 -12
- package/template/README.md +67 -31
- package/template/_gitignore +4 -1
- package/template/biome.json +7 -1
- package/template/docs/prompts/agent-setup.md +24 -20
- package/template/docs/prompts/update-existing.md +3 -3
- package/template/docs/workflow.md +11 -7
- package/template/package.json +15 -14
- package/template/src/components/DiffViewer/DiffViewer.css +41 -0
- package/template/src/components/DiffViewer/DiffViewer.ts +55 -0
- package/template/src/components/EmptyState/EmptyState.css +5 -5
- package/template/src/components/EmptyState/EmptyState.ts +5 -9
- package/template/src/utilities.css +259 -1
- package/template/src/views/DiffViewer/DiffViewerView.css +42 -0
- package/template/src/views/DiffViewer/DiffViewerView.ts +53 -0
- package/template/src/views/ExampleView/ExampleView.ts +92 -0
- package/template/stories/components/ComponentShell.stories.ts +28 -0
- package/template/stories/components/EmptyState.stories.ts +1 -0
- package/template/stories/components/LoadingState.stories.ts +1 -0
- package/template/stories/components/Toggle.stories.ts +50 -0
- package/template/stories/views/DiffViewer/DiffViewer.stories.ts +94 -0
- package/template/stories/views/EditorView.stories.ts +55 -0
- package/template/stories/views/ExampleView/ExampleView.stories.ts +15 -0
- package/template/stories/views/PanelView.stories.ts +61 -0
- package/template/stories/views/SettingsPanel/SettingsPanel.stories.ts +14 -0
- package/template/test/css-structure.test.mjs +112 -0
- package/template/test/template-footguns.test.mjs +85 -6
- package/template/test/viewer-stories.test.mjs +12 -0
- package/template/tools/router/client.ts +26 -4
- package/template/tools/router/routeToPage.ts +29 -13
- package/template/tools/sandbox/frame.ts +7 -27
- package/template/tools/sandbox/home.ts +6 -11
- package/template/tools/sandbox/layout.ts +24 -2
- package/template/tools/sandbox/sandbox.css +188 -226
- package/template/tools/sandbox/shell.ts +2 -2
- package/template/tools/sandbox/toolbar.ts +20 -9
- package/template/tools/viewer/ClassesPage.ts +7 -7
- package/template/tools/viewer/ComponentsIndex.ts +3 -3
- package/template/tools/viewer/StoryPage.ts +53 -40
- package/template/tools/viewer/TokensPage.ts +10 -10
- package/template/tools/viewer/ViewsIndex.ts +66 -0
- package/template/tools/viewer/discovery.ts +2 -0
- package/template/tools/viewer/obsidian-classes.ts +1 -1
- package/template/tools/viewer/sidebar.ts +27 -38
- package/template/tools/viewer/stories.ts +16 -2
- package/template/.github/workflows/ci.yml +0 -36
- package/template/pnpm-lock.yaml +0 -1608
- package/template/scripts/create-component.mjs +0 -101
- package/template/scripts/create-view.mjs +0 -75
- package/template/src/components/DiffViewer.ts +0 -42
- package/template/stories/DiffViewer.stories.ts +0 -75
- package/template/stories/SettingsPanel.stories.ts +0 -11
- package/template/stories/Toggle.stories.ts +0 -28
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Usage:
|
|
4
|
-
* pnpm create:component Composer → src/components/Composer/ (with CSS)
|
|
5
|
-
* pnpm create:component icons → src/components/icons.ts (flat, no args)
|
|
6
|
-
* pnpm create:component ChatView/ChatComposer → src/views/ChatView/ChatComposer.ts (flat)
|
|
7
|
-
* pnpm create:component ChatView/Widget --css → src/views/ChatView/Widget/ (with CSS)
|
|
8
|
-
*/
|
|
9
|
-
import fs from "node:fs";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
|
|
13
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
|
-
const args = process.argv.slice(2).filter((a) => !a.startsWith("--"));
|
|
15
|
-
const flags = process.argv.slice(2).filter((a) => a.startsWith("--"));
|
|
16
|
-
const withCss = flags.includes("--css");
|
|
17
|
-
|
|
18
|
-
if (!args[0]) {
|
|
19
|
-
console.error("Usage: pnpm create:component <[ViewName/]ComponentName> [--css]");
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const parts = args[0].split("/");
|
|
24
|
-
const componentName = parts.pop();
|
|
25
|
-
const parentView = parts[0]; // e.g. "ChatView" or undefined
|
|
26
|
-
|
|
27
|
-
let srcDir;
|
|
28
|
-
let storyPath;
|
|
29
|
-
let isFolder;
|
|
30
|
-
|
|
31
|
-
if (parentView) {
|
|
32
|
-
// View-specific: flat by default, folder with --css
|
|
33
|
-
const viewDir = path.join(root, "src", "views", parentView);
|
|
34
|
-
isFolder = withCss;
|
|
35
|
-
srcDir = isFolder ? path.join(viewDir, componentName) : viewDir;
|
|
36
|
-
storyPath = path.join(root, "stories", "views", parentView, `${componentName}.stories.ts`);
|
|
37
|
-
} else {
|
|
38
|
-
// Primitive: folder with CSS by default
|
|
39
|
-
isFolder = true;
|
|
40
|
-
srcDir = path.join(root, "src", "components", componentName);
|
|
41
|
-
storyPath = path.join(root, "stories", "components", `${componentName}.stories.ts`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const tsFile = path.join(srcDir, `${componentName}.ts`);
|
|
45
|
-
const cssFile = isFolder ? path.join(srcDir, `${componentName}.css`) : null;
|
|
46
|
-
|
|
47
|
-
// Component .ts stub
|
|
48
|
-
const cssImport = cssFile ? `import "./${componentName}.css";\n` : "";
|
|
49
|
-
const tsContent = `${cssImport}import { html } from "@arrow-js/core";
|
|
50
|
-
import type { ArrowExpression } from "@arrow-js/core";
|
|
51
|
-
|
|
52
|
-
export function ${componentName}(): ArrowExpression {
|
|
53
|
-
return html\`<div class="${componentName
|
|
54
|
-
.replace(/([A-Z])/g, (m, l, i) => (i ? "-" : "") + l.toLowerCase())
|
|
55
|
-
.replace(/^-/, "")}">TODO</div>\`;
|
|
56
|
-
}
|
|
57
|
-
`;
|
|
58
|
-
|
|
59
|
-
// Story stub
|
|
60
|
-
const importPath = "../../tools/viewer/stories";
|
|
61
|
-
const componentImport = parentView
|
|
62
|
-
? `../../src/views/${parentView}/${componentName}`
|
|
63
|
-
: `../../src/components/${isFolder ? `${componentName}/` : ""}${componentName}`;
|
|
64
|
-
const kindLine = parentView ? `\tkind: "component",\n` : "";
|
|
65
|
-
|
|
66
|
-
const storyContent = `import { defineStories } from "${importPath}";
|
|
67
|
-
import { ${componentName} } from "${componentImport}";
|
|
68
|
-
|
|
69
|
-
export default defineStories({
|
|
70
|
-
${kindLine}\tdescription: "TODO: describe ${componentName}.",
|
|
71
|
-
\tstatus: "draft",
|
|
72
|
-
\tvariants: {
|
|
73
|
-
\t\tdefault: () => ${componentName}(),
|
|
74
|
-
\t},
|
|
75
|
-
});
|
|
76
|
-
`;
|
|
77
|
-
|
|
78
|
-
// Write files
|
|
79
|
-
if (isFolder) fs.mkdirSync(srcDir, { recursive: true });
|
|
80
|
-
else fs.mkdirSync(path.dirname(tsFile), { recursive: true });
|
|
81
|
-
|
|
82
|
-
if (fs.existsSync(tsFile)) {
|
|
83
|
-
console.error(`Already exists: ${tsFile}`);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
fs.writeFileSync(tsFile, tsContent);
|
|
88
|
-
console.log(`Created: ${path.relative(root, tsFile)}`);
|
|
89
|
-
|
|
90
|
-
if (cssFile) {
|
|
91
|
-
fs.writeFileSync(cssFile, `/* ${componentName} styles */\n`);
|
|
92
|
-
console.log(`Created: ${path.relative(root, cssFile)}`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
fs.mkdirSync(path.dirname(storyPath), { recursive: true });
|
|
96
|
-
if (!fs.existsSync(storyPath)) {
|
|
97
|
-
fs.writeFileSync(storyPath, storyContent);
|
|
98
|
-
console.log(`Created: ${path.relative(root, storyPath)}`);
|
|
99
|
-
} else {
|
|
100
|
-
console.log(`Story already exists, skipped: ${path.relative(root, storyPath)}`);
|
|
101
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Usage:
|
|
4
|
-
* pnpm create:view ChatView → src/views/ChatView/ + stories/views/ChatView.stories.ts
|
|
5
|
-
*/
|
|
6
|
-
import fs from "node:fs";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
|
|
10
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
-
const viewName = process.argv[2];
|
|
12
|
-
|
|
13
|
-
if (!viewName) {
|
|
14
|
-
console.error("Usage: pnpm create:view <ViewName>");
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const viewDir = path.join(root, "src", "views", viewName);
|
|
19
|
-
const storyDir = path.join(root, "stories", "views");
|
|
20
|
-
|
|
21
|
-
if (fs.existsSync(viewDir)) {
|
|
22
|
-
console.error(`Already exists: src/views/${viewName}/`);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
fs.mkdirSync(viewDir, { recursive: true });
|
|
27
|
-
fs.mkdirSync(storyDir, { recursive: true });
|
|
28
|
-
|
|
29
|
-
const tsContent = `import "./${viewName}.css";
|
|
30
|
-
import { component, html } from "@arrow-js/core";
|
|
31
|
-
import type { ArrowTemplate } from "@arrow-js/core";
|
|
32
|
-
|
|
33
|
-
export const ${viewName} = component((): ArrowTemplate => {
|
|
34
|
-
return html\`
|
|
35
|
-
<div class="oas-settings">
|
|
36
|
-
<div class="setting-item setting-item-heading">
|
|
37
|
-
<div class="setting-item-info">
|
|
38
|
-
<div class="setting-item-name">${viewName}</div>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
\`;
|
|
43
|
-
});
|
|
44
|
-
`;
|
|
45
|
-
|
|
46
|
-
const cssContent = `/* ${viewName} layout and chrome */\n`;
|
|
47
|
-
|
|
48
|
-
const stateContent = `import { reactive } from "@arrow-js/core";
|
|
49
|
-
|
|
50
|
-
export const state = reactive({
|
|
51
|
-
// TODO: add view state
|
|
52
|
-
});
|
|
53
|
-
`;
|
|
54
|
-
|
|
55
|
-
const storyContent = `import { defineStories } from "../../tools/viewer/stories";
|
|
56
|
-
import { ${viewName} } from "../../src/views/${viewName}/${viewName}";
|
|
57
|
-
|
|
58
|
-
export default defineStories({
|
|
59
|
-
kind: "view",
|
|
60
|
-
description: "TODO: describe ${viewName}.",
|
|
61
|
-
status: "draft",
|
|
62
|
-
variants: {
|
|
63
|
-
default: () => ${viewName}(),
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
`;
|
|
67
|
-
|
|
68
|
-
fs.writeFileSync(path.join(viewDir, `${viewName}.ts`), tsContent);
|
|
69
|
-
fs.writeFileSync(path.join(viewDir, `${viewName}.css`), cssContent);
|
|
70
|
-
fs.writeFileSync(path.join(viewDir, "state.ts"), stateContent);
|
|
71
|
-
fs.writeFileSync(path.join(storyDir, `${viewName}.stories.ts`), storyContent);
|
|
72
|
-
|
|
73
|
-
console.log(`Created src/views/${viewName}/`);
|
|
74
|
-
console.log(` ${viewName}.ts, ${viewName}.css, state.ts`);
|
|
75
|
-
console.log(`Created stories/views/${viewName}.stories.ts`);
|
|
@@ -1,42 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
import { SettingsPanel } from "../src/components/SettingsPanel";
|
|
2
|
-
import { defineStories } from "../tools/viewer/stories";
|
|
3
|
-
|
|
4
|
-
export default defineStories({
|
|
5
|
-
description: "Vertical tabs, toggles, a keyed list, and an async boundary() section.",
|
|
6
|
-
status: "live",
|
|
7
|
-
variants: {
|
|
8
|
-
default: () => SettingsPanel(),
|
|
9
|
-
},
|
|
10
|
-
children: ["toggle"],
|
|
11
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { reactive } from "@arrow-js/core";
|
|
2
|
-
import { Toggle } from "../src/components/SettingsPanel";
|
|
3
|
-
import { defineStories } from "../tools/viewer/stories";
|
|
4
|
-
|
|
5
|
-
export default defineStories({
|
|
6
|
-
description: "Obsidian checkbox-container toggle used by SettingsPanel.",
|
|
7
|
-
componentPath: "src/components/SettingsPanel.ts",
|
|
8
|
-
status: "live",
|
|
9
|
-
variants: {
|
|
10
|
-
interactive: () => {
|
|
11
|
-
const state = reactive({ on: true });
|
|
12
|
-
return Toggle(
|
|
13
|
-
() => state.on,
|
|
14
|
-
() => {
|
|
15
|
-
state.on = !state.on;
|
|
16
|
-
}
|
|
17
|
-
);
|
|
18
|
-
},
|
|
19
|
-
off: {
|
|
20
|
-
render: () =>
|
|
21
|
-
Toggle(
|
|
22
|
-
() => false,
|
|
23
|
-
() => {}
|
|
24
|
-
),
|
|
25
|
-
notes: "Static off state (click does nothing).",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
});
|