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,32 +1,43 @@
|
|
|
1
1
|
import { html } from "@arrow-js/core";
|
|
2
2
|
import type { ArrowTemplate } from "@arrow-js/core";
|
|
3
|
-
import { MIN_WIDTH, WIDTH_PRESETS, layoutState, setWidth } from "./layout";
|
|
3
|
+
import { MIN_WIDTH, WIDTH_PRESETS, layoutState, pageState, setWidth } from "./layout";
|
|
4
|
+
import { themeState, toggleTheme } from "./theme";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* Sandbox toolbar
|
|
7
|
-
*
|
|
8
|
-
* not part of the component under test.
|
|
7
|
+
* Sandbox toolbar: home, breadcrumb, panel-width controls, and theme toggle.
|
|
8
|
+
* All sandbox-global chrome lives here so the story panels stay content-only.
|
|
9
9
|
*/
|
|
10
10
|
const onRangeInput = (event: Event): void => {
|
|
11
11
|
setWidth(Number((event.target as HTMLInputElement).value));
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export const Toolbar = (): ArrowTemplate => html`
|
|
15
|
-
<div class="
|
|
16
|
-
<
|
|
15
|
+
<div class="oasbox-toolbar">
|
|
16
|
+
<a class="clickable-icon oasbox-home" href="/" aria-label="Home">⌂</a>
|
|
17
|
+
${() =>
|
|
18
|
+
pageState.breadcrumb
|
|
19
|
+
? html`<span class="oasbox-breadcrumb">${() => pageState.breadcrumb}</span>`
|
|
20
|
+
: ""}
|
|
21
|
+
<div class="oasbox-toolbar-divider"></div>
|
|
22
|
+
<span class="oasbox-toolbar-label">Panel width</span>
|
|
17
23
|
<input
|
|
18
|
-
class="
|
|
24
|
+
class="oasbox-width-range"
|
|
19
25
|
type="range"
|
|
20
26
|
min="${MIN_WIDTH}"
|
|
21
27
|
max="${() => window.innerWidth}"
|
|
22
28
|
.value="${() => String(layoutState.width)}"
|
|
23
29
|
@input="${onRangeInput}"
|
|
24
30
|
/>
|
|
25
|
-
<span class="
|
|
31
|
+
<span class="oasbox-width-readout">${() => `${layoutState.width}px`}</span>
|
|
26
32
|
${WIDTH_PRESETS.map((width) =>
|
|
27
|
-
html`<button class="
|
|
33
|
+
html`<button class="oasbox-preset" @click="${() => setWidth(width)}">${width}</button>`.key(
|
|
28
34
|
width
|
|
29
35
|
)
|
|
30
36
|
)}
|
|
37
|
+
<button
|
|
38
|
+
class="clickable-icon oasbox-theme-toggle"
|
|
39
|
+
aria-label="Toggle theme"
|
|
40
|
+
@click="${toggleTheme}"
|
|
41
|
+
>${() => (themeState.theme === "theme-dark" ? "☾" : "☀")}</button>
|
|
31
42
|
</div>
|
|
32
43
|
`;
|
|
@@ -4,7 +4,7 @@ import { classGroups } from "./obsidian-classes";
|
|
|
4
4
|
|
|
5
5
|
export const ClassesPage = component(() => {
|
|
6
6
|
return html`
|
|
7
|
-
<div class="
|
|
7
|
+
<div class="oasbox-reference">
|
|
8
8
|
<div class="setting-item setting-item-heading">
|
|
9
9
|
<div class="setting-item-info">
|
|
10
10
|
<div class="setting-item-name">Obsidian classes</div>
|
|
@@ -15,17 +15,17 @@ export const ClassesPage = component(() => {
|
|
|
15
15
|
</div>
|
|
16
16
|
${classGroups.map(
|
|
17
17
|
(group) => html`
|
|
18
|
-
<div class="
|
|
18
|
+
<div class="oasbox-class-group">
|
|
19
19
|
<div class="vertical-tab-header-group-title">${group.label}</div>
|
|
20
20
|
${group.entries.map(
|
|
21
21
|
(entry) => html`
|
|
22
|
-
<div class="
|
|
23
|
-
<div class="
|
|
22
|
+
<div class="oasbox-class-entry">
|
|
23
|
+
<div class="oasbox-class-head">
|
|
24
24
|
<code>${entry.className}</code>
|
|
25
|
-
<button class="
|
|
25
|
+
<button class="oasbox-copy" @click="${() => copyText(entry.className)}">Copy</button>
|
|
26
26
|
</div>
|
|
27
|
-
<div class="
|
|
28
|
-
<div class="
|
|
27
|
+
<div class="oasbox-class-when">${entry.whenToUse}</div>
|
|
28
|
+
<div class="oasbox-class-preview">${entry.preview()}</div>
|
|
29
29
|
</div>
|
|
30
30
|
`
|
|
31
31
|
)}
|
|
@@ -5,7 +5,7 @@ import { stories } from "./discovery";
|
|
|
5
5
|
export function ComponentsIndex(): ArrowExpression {
|
|
6
6
|
if (stories.length === 0) {
|
|
7
7
|
return html`
|
|
8
|
-
<div class="
|
|
8
|
+
<div class="oasbox-settings">
|
|
9
9
|
<div class="setting-item setting-item-heading">
|
|
10
10
|
<div class="setting-item-info">
|
|
11
11
|
<div class="setting-item-name">Components</div>
|
|
@@ -18,7 +18,7 @@ export function ComponentsIndex(): ArrowExpression {
|
|
|
18
18
|
`;
|
|
19
19
|
}
|
|
20
20
|
return html`
|
|
21
|
-
<div class="
|
|
21
|
+
<div class="oasbox-settings">
|
|
22
22
|
<div class="setting-item setting-item-heading">
|
|
23
23
|
<div class="setting-item-info">
|
|
24
24
|
<div class="setting-item-name">Components</div>
|
|
@@ -46,7 +46,7 @@ export function ComponentsIndex(): ArrowExpression {
|
|
|
46
46
|
}
|
|
47
47
|
</div>
|
|
48
48
|
<div class="setting-item-control">
|
|
49
|
-
<a class="mod-cta
|
|
49
|
+
<a class="mod-cta oasbox-open-link" href="${path}">Open →</a>
|
|
50
50
|
</div>
|
|
51
51
|
</div>
|
|
52
52
|
`.key(story.slug);
|
|
@@ -9,59 +9,61 @@ export function copyText(text: string): void {
|
|
|
9
9
|
|
|
10
10
|
function pathRow(label: string, path: string): ArrowExpression {
|
|
11
11
|
return html`
|
|
12
|
-
<div class="
|
|
13
|
-
<span class="
|
|
14
|
-
<code>${path}</code>
|
|
15
|
-
<button class="
|
|
12
|
+
<div class="oasbox-story-path">
|
|
13
|
+
<span class="oasbox-path-label">${label}</span>
|
|
14
|
+
<code class="oasbox-path-code" title="${path}">${path}</code>
|
|
15
|
+
<button class="oasbox-copy" @click="${() => copyText(path)}">Copy</button>
|
|
16
16
|
</div>
|
|
17
17
|
`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/** Metadata panel:
|
|
21
|
-
*
|
|
20
|
+
/** Metadata panel: story info, references, variants, notes, children.
|
|
21
|
+
* Sections are visually separated with labels. Used as the left panel for
|
|
22
|
+
* both component and view stories. */
|
|
22
23
|
export function StoryPageDetails(story: DiscoveredStory, variantName: string): ArrowExpression {
|
|
23
24
|
const variantNames = Object.keys(story.variants);
|
|
24
25
|
const variant = story.variants[variantName];
|
|
26
|
+
const badge =
|
|
27
|
+
story.status === "live"
|
|
28
|
+
? html`<span class="oas-badge is-live">live</span>`
|
|
29
|
+
: html`<span class="oas-badge is-draft">draft</span>`;
|
|
30
|
+
|
|
25
31
|
return html`
|
|
26
|
-
<div class="
|
|
27
|
-
<div class="
|
|
28
|
-
<div class="
|
|
29
|
-
|
|
30
|
-
${story.title}
|
|
31
|
-
${
|
|
32
|
-
story.status === "live"
|
|
33
|
-
? html`<span class="oas-badge is-live">live</span>`
|
|
34
|
-
: html`<span class="oas-badge is-draft">draft</span>`
|
|
35
|
-
}
|
|
36
|
-
</div>
|
|
37
|
-
${
|
|
38
|
-
story.description
|
|
39
|
-
? html`<div class="setting-item-description">${story.description}</div>`
|
|
40
|
-
: ""
|
|
41
|
-
}
|
|
42
|
-
</div>
|
|
32
|
+
<div class="oasbox-story">
|
|
33
|
+
<div class="oasbox-story-header">
|
|
34
|
+
<div class="oasbox-story-title">${story.title} ${badge}</div>
|
|
35
|
+
${story.description ? html`<div class="oasbox-story-desc">${story.description}</div>` : ""}
|
|
43
36
|
</div>
|
|
44
|
-
<div class="
|
|
37
|
+
<div class="oasbox-story-section">
|
|
38
|
+
<div class="oasbox-section-label">References</div>
|
|
45
39
|
${pathRow("component", story.componentPath)}
|
|
46
40
|
${pathRow("stories", story.storiesPath)}
|
|
47
41
|
</div>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
${
|
|
43
|
+
variantNames.length > 1
|
|
44
|
+
? html`<div class="oasbox-story-section">
|
|
45
|
+
<div class="oasbox-section-label">Variants</div>
|
|
46
|
+
<div class="oasbox-variants">
|
|
47
|
+
${variantNames.map((name) => {
|
|
48
|
+
const cls = name === variantName ? "oasbox-variant is-active" : "oasbox-variant";
|
|
49
|
+
const href = `/components/${story.slug}?variant=${encodeURIComponent(name)}`;
|
|
50
|
+
return html`<a class="${cls}" href="${href}">${name}</a>`;
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
</div>`
|
|
54
|
+
: ""
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
${variant?.notes ? html`<div class="oasbox-story-notes">${variant.notes}</div>` : ""}
|
|
56
58
|
${
|
|
57
59
|
story.children.length > 0
|
|
58
|
-
? html`<div class="
|
|
60
|
+
? html`<div class="oasbox-story-children">
|
|
59
61
|
${story.children.map((slug) => {
|
|
60
62
|
const child = findStory(slug);
|
|
61
63
|
const childHref = `/components/${slug}`;
|
|
62
64
|
return child
|
|
63
|
-
? html`<a class="
|
|
64
|
-
: html`<span class="
|
|
65
|
+
? html`<a class="oasbox-child" href="${childHref}">${child.title} →</a>`
|
|
66
|
+
: html`<span class="oasbox-child-missing">${slug} (missing story)</span>`;
|
|
65
67
|
})}
|
|
66
68
|
</div>`
|
|
67
69
|
: ""
|
|
@@ -76,16 +78,27 @@ export function StoryPageCanvas(story: DiscoveredStory, variantName: string): Ar
|
|
|
76
78
|
const variant = story.variants[variantName];
|
|
77
79
|
const rendered = variant
|
|
78
80
|
? variant.render()
|
|
79
|
-
: html`<div class="
|
|
81
|
+
: html`<div class="oasbox-story-missing">No variant "${variantName}" — pick one above.</div>`;
|
|
80
82
|
return story.decorator ? story.decorator(rendered) : rendered;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
/**
|
|
84
|
-
* Used for kind: "view" — StoryPageDetails provides the .oas-story wrapper. */
|
|
85
|
+
/** @deprecated Use StoryPageDetails + StoryPageCanvas via the router's viewCanvas flag instead. */
|
|
85
86
|
export function StoryPageView(story: DiscoveredStory, variantName: string): ArrowExpression {
|
|
87
|
+
const variantNames = Object.keys(story.variants);
|
|
88
|
+
const hasMultiple = variantNames.length > 1;
|
|
86
89
|
return html`
|
|
87
|
-
${
|
|
88
|
-
|
|
90
|
+
${
|
|
91
|
+
hasMultiple
|
|
92
|
+
? html`<div class="oasbox-view-variant-bar">
|
|
93
|
+
${variantNames.map((name) => {
|
|
94
|
+
const cls = name === variantName ? "oasbox-variant is-active" : "oasbox-variant";
|
|
95
|
+
const href = `/components/${story.slug}?variant=${encodeURIComponent(name)}`;
|
|
96
|
+
return html`<a class="${cls}" href="${href}">${name}</a>`;
|
|
97
|
+
})}
|
|
98
|
+
</div>`
|
|
99
|
+
: ""
|
|
100
|
+
}
|
|
101
|
+
<div class="oasbox-story-view">
|
|
89
102
|
${StoryPageCanvas(story, variantName)}
|
|
90
103
|
</div>
|
|
91
104
|
`;
|
|
@@ -14,20 +14,20 @@ function tokenRow(decl: TokenDecl) {
|
|
|
14
14
|
return resolveToken(decl.name) || decl.value;
|
|
15
15
|
};
|
|
16
16
|
return html`
|
|
17
|
-
<div class="
|
|
18
|
-
<code class="
|
|
19
|
-
<span class="
|
|
17
|
+
<div class="oasbox-token-row">
|
|
18
|
+
<code class="oasbox-token-name">${decl.name}</code>
|
|
19
|
+
<span class="oasbox-token-value">${() => resolved()}</span>
|
|
20
20
|
${() => {
|
|
21
21
|
const kind = classifyValue(resolved());
|
|
22
22
|
if (kind === "color") {
|
|
23
|
-
return html`<span class="
|
|
23
|
+
return html`<span class="oasbox-swatch" style="${() => `background: ${resolved()};`}"></span>`;
|
|
24
24
|
}
|
|
25
25
|
if (kind === "length") {
|
|
26
|
-
return html`<span class="
|
|
26
|
+
return html`<span class="oasbox-sizebar" style="${() => `width: ${resolved()};`}"></span>`;
|
|
27
27
|
}
|
|
28
|
-
return html`<span class="
|
|
28
|
+
return html`<span class="oasbox-swatch-none"></span>`;
|
|
29
29
|
}}
|
|
30
|
-
<button class="
|
|
30
|
+
<button class="oasbox-copy" @click="${() => copyText(`var(${decl.name})`)}">Copy</button>
|
|
31
31
|
</div>
|
|
32
32
|
`;
|
|
33
33
|
}
|
|
@@ -35,7 +35,7 @@ function tokenRow(decl: TokenDecl) {
|
|
|
35
35
|
export const TokensPage = component(() => {
|
|
36
36
|
const decls = collectTokenDecls();
|
|
37
37
|
return html`
|
|
38
|
-
<div class="
|
|
38
|
+
<div class="oasbox-reference">
|
|
39
39
|
<div class="setting-item setting-item-heading">
|
|
40
40
|
<div class="setting-item-info">
|
|
41
41
|
<div class="setting-item-name">Obsidian tokens (${String(decls.length)})</div>
|
|
@@ -58,7 +58,7 @@ export const TokensPage = component(() => {
|
|
|
58
58
|
: ""
|
|
59
59
|
}
|
|
60
60
|
<input
|
|
61
|
-
class="
|
|
61
|
+
class="oasbox-token-filter"
|
|
62
62
|
type="search"
|
|
63
63
|
placeholder="Filter tokens…"
|
|
64
64
|
.value="${() => state.query}"
|
|
@@ -69,7 +69,7 @@ export const TokensPage = component(() => {
|
|
|
69
69
|
${() =>
|
|
70
70
|
groupTokens(filterTokens(decls, state.query)).map((group) =>
|
|
71
71
|
html`
|
|
72
|
-
<div class="
|
|
72
|
+
<div class="oasbox-token-group">
|
|
73
73
|
<div class="vertical-tab-header-group-title">
|
|
74
74
|
${group.label} (${String(group.tokens.length)})
|
|
75
75
|
</div>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { html } from "@arrow-js/core";
|
|
2
|
+
import type { ArrowExpression } from "@arrow-js/core";
|
|
3
|
+
import { stories } from "./discovery";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Index page for all kind: "view" stories. Mirrors ComponentsIndex for views.
|
|
7
|
+
*/
|
|
8
|
+
export function ViewsIndex(): ArrowExpression {
|
|
9
|
+
const views = stories.filter((s) => s.kind === "view");
|
|
10
|
+
|
|
11
|
+
if (views.length === 0) {
|
|
12
|
+
return html`
|
|
13
|
+
<div class="oasbox-settings">
|
|
14
|
+
<div class="setting-item setting-item-heading">
|
|
15
|
+
<div class="setting-item-info">
|
|
16
|
+
<div class="setting-item-name">Views</div>
|
|
17
|
+
<div class="setting-item-description">
|
|
18
|
+
No view stories found. Create a
|
|
19
|
+
<code>*.stories.ts</code>
|
|
20
|
+
file under
|
|
21
|
+
<code>stories/views/</code>
|
|
22
|
+
to register a view.
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return html`
|
|
31
|
+
<div class="oasbox-settings">
|
|
32
|
+
<div class="setting-item setting-item-heading">
|
|
33
|
+
<div class="setting-item-info">
|
|
34
|
+
<div class="setting-item-name">Views</div>
|
|
35
|
+
<div class="setting-item-description">
|
|
36
|
+
${views.length} ${views.length === 1 ? "view" : "views"} — click to open.
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
${views.map((story) => {
|
|
41
|
+
const path = `/components/${story.slug}`;
|
|
42
|
+
const badge =
|
|
43
|
+
story.status === "live"
|
|
44
|
+
? html`<span class="oas-badge is-live">live</span>`
|
|
45
|
+
: html`<span class="oas-badge is-draft">draft</span>`;
|
|
46
|
+
return html`
|
|
47
|
+
<div class="setting-item">
|
|
48
|
+
<div class="setting-item-info">
|
|
49
|
+
<div class="setting-item-name">
|
|
50
|
+
<a href="${path}">${story.title}</a> ${badge}
|
|
51
|
+
</div>
|
|
52
|
+
${
|
|
53
|
+
story.description
|
|
54
|
+
? html`<div class="setting-item-description">${story.description}</div>`
|
|
55
|
+
: ""
|
|
56
|
+
}
|
|
57
|
+
</div>
|
|
58
|
+
<div class="setting-item-control">
|
|
59
|
+
<a class="mod-cta oasbox-open-link" href="${path}">Open →</a>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
`.key(story.slug);
|
|
63
|
+
})}
|
|
64
|
+
</div>
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
@@ -21,6 +21,7 @@ export interface DiscoveredStory {
|
|
|
21
21
|
children: string[];
|
|
22
22
|
status: "live" | "draft";
|
|
23
23
|
kind: "view" | "component";
|
|
24
|
+
surface: "panel" | "editor";
|
|
24
25
|
decorator?: (content: ArrowExpression) => ArrowExpression;
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -57,6 +58,7 @@ for (const [globKey, mod] of Object.entries(modules)) {
|
|
|
57
58
|
children: def.children ?? [],
|
|
58
59
|
status: def.status ?? "draft",
|
|
59
60
|
kind: def.kind ?? autoKind,
|
|
61
|
+
surface: def.surface ?? "panel",
|
|
60
62
|
decorator: def.decorator,
|
|
61
63
|
});
|
|
62
64
|
}
|
|
@@ -101,7 +101,7 @@ export const classGroups: ClassGroup[] = [
|
|
|
101
101
|
whenToUse:
|
|
102
102
|
"Dialog card (.modal-title + .modal-content). Preview is contained by a transformed wrapper.",
|
|
103
103
|
preview: () => html`
|
|
104
|
-
<div class="modal
|
|
104
|
+
<div class="modal oasbox-modal-preview">
|
|
105
105
|
<div class="modal-title">Modal title</div>
|
|
106
106
|
<div class="modal-content">Modal content goes here.</div>
|
|
107
107
|
</div>
|
|
@@ -1,54 +1,43 @@
|
|
|
1
|
-
import { html } from "@arrow-js/core";
|
|
2
|
-
import type { ArrowTemplate } from "@arrow-js/core";
|
|
1
|
+
import { html, reactive } from "@arrow-js/core";
|
|
3
2
|
import type { ArrowExpression } from "@arrow-js/core";
|
|
4
|
-
import type { TreeNode } from "./derive";
|
|
5
|
-
import { findStory, invalidStories, storyTree } from "./discovery";
|
|
6
3
|
|
|
7
4
|
/**
|
|
8
|
-
* Viewer navigation
|
|
9
|
-
* Reference section. Rendered OUTSIDE the pane, as the first child of the
|
|
10
|
-
* stage. Sandbox chrome — never ports to a plugin.
|
|
5
|
+
* Viewer navigation sidebar. Sandbox chrome — never ports to a plugin.
|
|
11
6
|
*/
|
|
12
7
|
|
|
8
|
+
const sidebarState = reactive({ collapsed: false });
|
|
9
|
+
|
|
13
10
|
function navClass(active: boolean): string {
|
|
14
11
|
return active
|
|
15
|
-
? "vertical-tab-nav-item
|
|
16
|
-
: "vertical-tab-nav-item
|
|
12
|
+
? "vertical-tab-nav-item oasbox-nav-item is-active"
|
|
13
|
+
: "vertical-tab-nav-item oasbox-nav-item";
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
if (!story) {
|
|
22
|
-
return "";
|
|
23
|
-
}
|
|
24
|
-
const href = `/components/${node.slug}`;
|
|
25
|
-
return html`
|
|
26
|
-
<a
|
|
27
|
-
class="${navClass(activePath === href)}"
|
|
28
|
-
style="${`padding-left: calc(var(--size-4-3) * ${depth + 1});`}"
|
|
29
|
-
href="${href}"
|
|
30
|
-
>${story.title}</a>
|
|
31
|
-
${node.children.map((child) => nodeRows(child, activePath, depth + 1))}
|
|
32
|
-
`;
|
|
16
|
+
function toggleSidebar(): void {
|
|
17
|
+
sidebarState.collapsed = !sidebarState.collapsed;
|
|
33
18
|
}
|
|
34
19
|
|
|
35
20
|
export function ViewerSidebar(activePath: string): ArrowExpression {
|
|
21
|
+
const onComponents = activePath.startsWith("/components");
|
|
22
|
+
const onViews = activePath.startsWith("/views");
|
|
36
23
|
return html`
|
|
37
|
-
<nav class="
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
${
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<div class="vertical-tab-header-group
|
|
50
|
-
|
|
51
|
-
|
|
24
|
+
<nav class="${() => `oasbox-sidebar${sidebarState.collapsed ? " is-collapsed" : ""}`}">
|
|
25
|
+
<button
|
|
26
|
+
class="oasbox-sidebar-toggle clickable-icon"
|
|
27
|
+
aria-label="Toggle sidebar"
|
|
28
|
+
@click="${toggleSidebar}"
|
|
29
|
+
>${() => (sidebarState.collapsed ? "›" : "‹")}</button>
|
|
30
|
+
<div class="oasbox-sidebar-content">
|
|
31
|
+
<div class="vertical-tab-header-group">
|
|
32
|
+
<div class="vertical-tab-header-group-title">Sandbox</div>
|
|
33
|
+
<a class="${navClass(onComponents)}" href="/components">Components</a>
|
|
34
|
+
<a class="${navClass(onViews)}" href="/views">Views</a>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="vertical-tab-header-group">
|
|
37
|
+
<div class="vertical-tab-header-group-title">Reference</div>
|
|
38
|
+
<a class="${navClass(activePath === "/reference")}" href="/reference">Tokens</a>
|
|
39
|
+
<a class="${navClass(activePath === "/reference/classes")}" href="/reference/classes">Classes</a>
|
|
40
|
+
</div>
|
|
52
41
|
</div>
|
|
53
42
|
</nav>
|
|
54
43
|
`;
|
|
@@ -18,12 +18,21 @@ export interface StoryDef {
|
|
|
18
18
|
title?: string;
|
|
19
19
|
/** One-line description shown in the tree and header. */
|
|
20
20
|
description?: string;
|
|
21
|
-
/** Repo-relative override for where the component lives
|
|
22
|
-
*
|
|
21
|
+
/** Repo-relative override for where the component lives. Defaults to mirroring
|
|
22
|
+
* the stories/ subtree into src/components/ (stories/Foo.stories.ts →
|
|
23
|
+
* src/components/Foo.ts), so set this for views (src/views/…) and for a
|
|
24
|
+
* subcomponent defined inside its parent's file. */
|
|
23
25
|
componentPath?: string;
|
|
24
26
|
/** "view" = full pane frame. "component" = centered canvas with separate details panel.
|
|
25
27
|
* Auto-detected from stories/ path if omitted. */
|
|
26
28
|
kind?: "view" | "component";
|
|
29
|
+
/** Obsidian surface a view replicates — orthogonal to `kind`.
|
|
30
|
+
* "panel" (default) = full-bleed custom view (workspace leaf).
|
|
31
|
+
* "editor" = readable line width (note/document style): the sandbox widens
|
|
32
|
+
* the pane so the centering is visible. The width itself comes from the
|
|
33
|
+
* component wrapping its content in the portable `oas-readable-width` utility
|
|
34
|
+
* (scaffolded by `create:view --editor`), so it ports 1:1 into the plugin. */
|
|
35
|
+
surface?: "panel" | "editor";
|
|
27
36
|
/** Wraps the rendered variant — use for ancestor class context.
|
|
28
37
|
* Example: (content) => html`<div class="my-shell">${content}</div>` */
|
|
29
38
|
decorator?: (content: ArrowExpression) => ArrowExpression;
|
|
@@ -80,6 +89,11 @@ export function validateStoryDef(def: unknown): ValidationResult {
|
|
|
80
89
|
return { ok: false, reason: '"kind" must be "view" or "component"' };
|
|
81
90
|
}
|
|
82
91
|
}
|
|
92
|
+
if ("surface" in def) {
|
|
93
|
+
if (def.surface !== "panel" && def.surface !== "editor") {
|
|
94
|
+
return { ok: false, reason: '"surface" must be "panel" or "editor"' };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
83
97
|
if ("decorator" in def) {
|
|
84
98
|
if (typeof def.decorator !== "function") {
|
|
85
99
|
return { ok: false, reason: '"decorator" must be a function' };
|
|
@@ -1,36 +0,0 @@
|
|
|
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
|