create-obsidian-arrow 0.5.1 → 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.
Files changed (61) hide show
  1. package/README.md +7 -7
  2. package/cli/create.mjs +65 -0
  3. package/cli/detect-pm.mjs +20 -0
  4. package/cli/lib.mjs +117 -0
  5. package/cli/refresh.mjs +65 -0
  6. package/index.mjs +47 -204
  7. package/package.json +11 -2
  8. package/template/.husky/pre-commit +3 -2
  9. package/template/AGENTS.md +57 -12
  10. package/template/README.md +66 -31
  11. package/template/_gitignore +4 -1
  12. package/template/biome.json +7 -1
  13. package/template/docs/prompts/agent-setup.md +22 -20
  14. package/template/docs/prompts/update-existing.md +3 -3
  15. package/template/docs/workflow.md +11 -7
  16. package/template/package.json +15 -14
  17. package/template/src/components/DiffViewer/DiffViewer.css +41 -0
  18. package/template/src/components/DiffViewer/DiffViewer.ts +55 -0
  19. package/template/src/components/EmptyState/EmptyState.css +5 -5
  20. package/template/src/components/EmptyState/EmptyState.ts +5 -9
  21. package/template/src/utilities.css +158 -0
  22. package/template/src/views/DiffViewer/DiffViewerView.css +42 -0
  23. package/template/src/views/DiffViewer/DiffViewerView.ts +53 -0
  24. package/template/src/views/ExampleView/ExampleView.ts +92 -0
  25. package/template/stories/components/ComponentShell.stories.ts +28 -0
  26. package/template/stories/components/EmptyState.stories.ts +1 -0
  27. package/template/stories/components/LoadingState.stories.ts +1 -0
  28. package/template/stories/components/Toggle.stories.ts +50 -0
  29. package/template/stories/views/DiffViewer/DiffViewer.stories.ts +94 -0
  30. package/template/stories/views/EditorView.stories.ts +55 -0
  31. package/template/stories/views/ExampleView/ExampleView.stories.ts +15 -0
  32. package/template/stories/views/PanelView.stories.ts +61 -0
  33. package/template/stories/views/SettingsPanel/SettingsPanel.stories.ts +14 -0
  34. package/template/test/css-structure.test.mjs +112 -0
  35. package/template/test/template-footguns.test.mjs +85 -6
  36. package/template/test/viewer-stories.test.mjs +12 -0
  37. package/template/tools/router/client.ts +26 -4
  38. package/template/tools/router/routeToPage.ts +29 -13
  39. package/template/tools/sandbox/frame.ts +7 -27
  40. package/template/tools/sandbox/home.ts +6 -11
  41. package/template/tools/sandbox/layout.ts +24 -2
  42. package/template/tools/sandbox/sandbox.css +188 -226
  43. package/template/tools/sandbox/shell.ts +2 -2
  44. package/template/tools/sandbox/toolbar.ts +20 -9
  45. package/template/tools/viewer/ClassesPage.ts +7 -7
  46. package/template/tools/viewer/ComponentsIndex.ts +3 -3
  47. package/template/tools/viewer/StoryPage.ts +53 -40
  48. package/template/tools/viewer/TokensPage.ts +10 -10
  49. package/template/tools/viewer/ViewsIndex.ts +66 -0
  50. package/template/tools/viewer/discovery.ts +2 -0
  51. package/template/tools/viewer/obsidian-classes.ts +1 -1
  52. package/template/tools/viewer/sidebar.ts +27 -38
  53. package/template/tools/viewer/stories.ts +16 -2
  54. package/template/.github/workflows/ci.yml +0 -36
  55. package/template/pnpm-lock.yaml +0 -1608
  56. package/template/scripts/create-component.mjs +0 -101
  57. package/template/scripts/create-view.mjs +0 -75
  58. package/template/src/components/DiffViewer.ts +0 -42
  59. package/template/stories/DiffViewer.stories.ts +0 -75
  60. package/template/stories/SettingsPanel.stories.ts +0 -11
  61. 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
- });