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.
- 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 +57 -12
- package/template/README.md +66 -31
- package/template/_gitignore +4 -1
- package/template/biome.json +7 -1
- package/template/docs/prompts/agent-setup.md +22 -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 +158 -0
- 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
|
@@ -12,18 +12,14 @@ export interface EmptyStateOptions {
|
|
|
12
12
|
|
|
13
13
|
export function EmptyState(options: EmptyStateOptions): ArrowExpression {
|
|
14
14
|
return html`
|
|
15
|
-
<div class="empty-state">
|
|
16
|
-
${options.icon ? html`<div class="empty-
|
|
17
|
-
<div class="empty-
|
|
18
|
-
${
|
|
19
|
-
options.description
|
|
20
|
-
? html`<div class="empty-state-description">${options.description}</div>`
|
|
21
|
-
: ""
|
|
22
|
-
}
|
|
15
|
+
<div class="oas-empty-state">
|
|
16
|
+
${options.icon ? html`<div class="oas-empty-icon">${icon(options.icon)}</div>` : ""}
|
|
17
|
+
<div class="oas-empty-title">${options.title}</div>
|
|
18
|
+
${options.description ? html`<div class="oas-empty-desc">${options.description}</div>` : ""}
|
|
23
19
|
${
|
|
24
20
|
options.action
|
|
25
21
|
? html`<button
|
|
26
|
-
class="mod-cta empty-
|
|
22
|
+
class="mod-cta oas-empty-action"
|
|
27
23
|
@click="${options.action.onClick}"
|
|
28
24
|
>
|
|
29
25
|
${options.action.label}
|
|
@@ -303,3 +303,161 @@
|
|
|
303
303
|
.oas-select-none {
|
|
304
304
|
user-select: none;
|
|
305
305
|
}
|
|
306
|
+
|
|
307
|
+
/* ── Badges ──────────────────────────────────────────────────── */
|
|
308
|
+
/* Status and label badges. Portable — copy with components into plugin. */
|
|
309
|
+
.oas-badge {
|
|
310
|
+
display: inline-flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
padding: 1px var(--size-4-1);
|
|
313
|
+
border-radius: var(--radius-s);
|
|
314
|
+
font-size: var(--font-ui-smaller);
|
|
315
|
+
font-weight: var(--font-medium);
|
|
316
|
+
vertical-align: middle;
|
|
317
|
+
margin-left: var(--size-4-1);
|
|
318
|
+
}
|
|
319
|
+
.oas-badge.is-live {
|
|
320
|
+
background: color-mix(in srgb, var(--text-success) 15%, var(--background-primary));
|
|
321
|
+
color: var(--text-success);
|
|
322
|
+
}
|
|
323
|
+
.oas-badge.is-draft {
|
|
324
|
+
background: var(--background-modifier-hover);
|
|
325
|
+
color: var(--text-faint);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* ── Collapsible card ────────────────────────────────────────── */
|
|
329
|
+
/* Expandable section with left-border accent. Portable — copy with components. */
|
|
330
|
+
.oas-card {
|
|
331
|
+
border-bottom: 1px solid var(--background-modifier-border);
|
|
332
|
+
border-left: 3px solid var(--background-modifier-border-hover);
|
|
333
|
+
}
|
|
334
|
+
.oas-card.is-expanded {
|
|
335
|
+
border-left-color: var(--interactive-accent);
|
|
336
|
+
}
|
|
337
|
+
.oas-card-header {
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: space-between;
|
|
341
|
+
gap: var(--size-4-2);
|
|
342
|
+
padding: var(--size-4-3);
|
|
343
|
+
cursor: pointer;
|
|
344
|
+
font-size: var(--font-ui-small);
|
|
345
|
+
font-weight: var(--font-semibold);
|
|
346
|
+
color: var(--text-normal);
|
|
347
|
+
user-select: none;
|
|
348
|
+
}
|
|
349
|
+
.oas-card-header:hover {
|
|
350
|
+
background: var(--background-modifier-hover);
|
|
351
|
+
}
|
|
352
|
+
.oas-card-title {
|
|
353
|
+
flex: 1;
|
|
354
|
+
min-width: 0;
|
|
355
|
+
overflow: hidden;
|
|
356
|
+
text-overflow: ellipsis;
|
|
357
|
+
white-space: nowrap;
|
|
358
|
+
}
|
|
359
|
+
.oas-card-chevron {
|
|
360
|
+
flex-shrink: 0;
|
|
361
|
+
color: var(--text-faint);
|
|
362
|
+
transition: transform 0.15s;
|
|
363
|
+
font-size: var(--font-ui-medium);
|
|
364
|
+
line-height: 1;
|
|
365
|
+
}
|
|
366
|
+
.oas-card.is-expanded .oas-card-chevron {
|
|
367
|
+
transform: rotate(90deg);
|
|
368
|
+
}
|
|
369
|
+
.oas-card-body {
|
|
370
|
+
display: none;
|
|
371
|
+
border-top: 1px solid var(--background-modifier-border);
|
|
372
|
+
}
|
|
373
|
+
.oas-card.is-expanded .oas-card-body {
|
|
374
|
+
display: block;
|
|
375
|
+
}
|
|
376
|
+
.oas-card-desc {
|
|
377
|
+
margin: 0;
|
|
378
|
+
padding: var(--size-4-2) var(--size-4-3);
|
|
379
|
+
color: var(--text-muted);
|
|
380
|
+
font-size: var(--font-ui-smaller);
|
|
381
|
+
}
|
|
382
|
+
.oas-card-note {
|
|
383
|
+
margin: 0;
|
|
384
|
+
padding: 0 var(--size-4-3) var(--size-4-2);
|
|
385
|
+
color: var(--text-muted);
|
|
386
|
+
font-size: var(--font-ui-smaller);
|
|
387
|
+
}
|
|
388
|
+
.oas-card-children {
|
|
389
|
+
display: flex;
|
|
390
|
+
align-items: baseline;
|
|
391
|
+
flex-wrap: wrap;
|
|
392
|
+
gap: var(--size-4-1);
|
|
393
|
+
padding: var(--size-4-1) var(--size-4-3);
|
|
394
|
+
}
|
|
395
|
+
.oas-card-children-label {
|
|
396
|
+
color: var(--text-faint);
|
|
397
|
+
font-size: var(--font-ui-smaller);
|
|
398
|
+
}
|
|
399
|
+
.oas-card-actions {
|
|
400
|
+
padding: var(--size-4-2) var(--size-4-3);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* ── View & component shells ─────────────────────────────────── */
|
|
404
|
+
/*
|
|
405
|
+
* Portable structural shells. Copy these with your view/component into the
|
|
406
|
+
* plugin — they rely only on Obsidian tokens and need no sandbox-specific CSS.
|
|
407
|
+
*
|
|
408
|
+
* View shell: apply oas-shell-view to the root element your view returns.
|
|
409
|
+
* The view-content div (Obsidian) is the scroll container; oas-shell-view
|
|
410
|
+
* fills it as a flex column so the body can grow and the footer stays pinned.
|
|
411
|
+
*
|
|
412
|
+
* Component shell: apply oas-shell-panel to a standalone component root for a
|
|
413
|
+
* card-style container with border, radius, and padding.
|
|
414
|
+
*/
|
|
415
|
+
.oas-shell-view {
|
|
416
|
+
display: flex;
|
|
417
|
+
flex-direction: column;
|
|
418
|
+
height: 100%;
|
|
419
|
+
min-height: 0;
|
|
420
|
+
}
|
|
421
|
+
/* Header: pinned to the top, never shrinks, has a min-height floor but grows
|
|
422
|
+
* to fit taller content (e.g. a wrapping toolbar). */
|
|
423
|
+
.oas-shell-view-header {
|
|
424
|
+
flex: 0 0 auto;
|
|
425
|
+
min-height: var(--header-height);
|
|
426
|
+
}
|
|
427
|
+
/* Body: fills all space between header and footer; scrolls its own overflow. */
|
|
428
|
+
.oas-shell-view-body {
|
|
429
|
+
flex: 1 1 auto;
|
|
430
|
+
min-height: 0;
|
|
431
|
+
overflow-y: auto;
|
|
432
|
+
}
|
|
433
|
+
/* Footer: pinned to the bottom, never shrinks, has a min-height floor but
|
|
434
|
+
* expands to fit contained components (e.g. a composer that grows with input). */
|
|
435
|
+
.oas-shell-view-footer {
|
|
436
|
+
flex: 0 0 auto;
|
|
437
|
+
min-height: var(--header-height);
|
|
438
|
+
border-top: 1px solid var(--background-modifier-border);
|
|
439
|
+
}
|
|
440
|
+
.oas-shell-panel {
|
|
441
|
+
display: flex;
|
|
442
|
+
flex-direction: column;
|
|
443
|
+
gap: var(--size-4-2);
|
|
444
|
+
padding: var(--size-4-3);
|
|
445
|
+
background: var(--background-primary);
|
|
446
|
+
border: 1px solid var(--background-modifier-border);
|
|
447
|
+
border-radius: var(--radius-m);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* ── Readable width (editor-style views) ─────────────────────────
|
|
451
|
+
* Reproduces Obsidian's "readable line length" for a *custom* view: caps
|
|
452
|
+
* content at the same --file-line-width token the markdown editor uses and
|
|
453
|
+
* centers it, with the editor's horizontal --file-margins. A custom ItemView
|
|
454
|
+
* does not inherit Obsidian's markdown editor chrome, so an editor-style
|
|
455
|
+
* (note/document) view wraps its content in this class to match. Portable: the
|
|
456
|
+
* tokens resolve against the active theme in both the sandbox and the plugin.
|
|
457
|
+
*/
|
|
458
|
+
.oas-readable-width {
|
|
459
|
+
width: 100%;
|
|
460
|
+
max-width: var(--file-line-width);
|
|
461
|
+
margin-inline: auto;
|
|
462
|
+
padding-inline: var(--file-margins-x);
|
|
463
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* DiffViewer as a full-pane editor view. */
|
|
2
|
+
|
|
3
|
+
/* Label row above each editor pane. */
|
|
4
|
+
.oas-diff-view-labels {
|
|
5
|
+
display: flex;
|
|
6
|
+
border-bottom: 1px solid var(--background-modifier-border);
|
|
7
|
+
flex-shrink: 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.oas-diff-view-label {
|
|
11
|
+
flex: 1;
|
|
12
|
+
padding: var(--size-2-1) var(--size-4-3);
|
|
13
|
+
font-size: var(--font-ui-smaller);
|
|
14
|
+
color: var(--text-muted);
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
text-overflow: ellipsis;
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
border-right: 1px solid var(--background-modifier-border);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.oas-diff-view-label:last-child {
|
|
22
|
+
border-right: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Body: fills remaining height. position: relative lets the diff viewer use inset: 0. */
|
|
26
|
+
.oas-diff-view-body {
|
|
27
|
+
flex: 1;
|
|
28
|
+
min-height: 0;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
position: relative;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Absolute positioning gives CM6 an explicit pixel height so height: 100% resolves
|
|
34
|
+
correctly on .cm-mergeView. Flex-resolved heights (flex: 1 + height: auto) don't
|
|
35
|
+
propagate to percentage-height grandchildren in all browsers. */
|
|
36
|
+
.oas-diff-view-body .oas-diff-viewer {
|
|
37
|
+
position: absolute;
|
|
38
|
+
inset: 0;
|
|
39
|
+
height: auto;
|
|
40
|
+
border: none;
|
|
41
|
+
border-radius: 0;
|
|
42
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiffViewerView — full-pane editor view wrapping the DiffViewer component.
|
|
3
|
+
*
|
|
4
|
+
* Shell anatomy:
|
|
5
|
+
* oas-shell-view-header pinned header: file labels + accept/reject actions
|
|
6
|
+
* oas-diff-view-labels per-pane filename labels (original | modified)
|
|
7
|
+
* oas-diff-view-body fills remaining height; CM6 handles internal scroll
|
|
8
|
+
*
|
|
9
|
+
* Duplicate this folder, rename DiffViewerView → YourDiffView, and wire in
|
|
10
|
+
* your real file content. The oas-shell-* classes handle the flex layout.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { html } from "@arrow-js/core";
|
|
14
|
+
import type { ArrowExpression } from "@arrow-js/core";
|
|
15
|
+
import { DiffViewer } from "../../components/DiffViewer/DiffViewer";
|
|
16
|
+
import "./DiffViewerView.css";
|
|
17
|
+
|
|
18
|
+
export interface DiffViewerViewOptions {
|
|
19
|
+
original: string;
|
|
20
|
+
modified: string;
|
|
21
|
+
originalLabel?: string;
|
|
22
|
+
modifiedLabel?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function DiffViewerView({
|
|
26
|
+
original,
|
|
27
|
+
modified,
|
|
28
|
+
originalLabel = "Original",
|
|
29
|
+
modifiedLabel = "Modified",
|
|
30
|
+
}: DiffViewerViewOptions): ArrowExpression {
|
|
31
|
+
return html`
|
|
32
|
+
<div class="oas-shell-view">
|
|
33
|
+
<div class="oas-shell-view-header">
|
|
34
|
+
<div class="oas-flex oas-items-center oas-justify-between oas-px-3 oas-py-2-1">
|
|
35
|
+
<span class="oas-font-semibold oas-text-sm">Changes</span>
|
|
36
|
+
<div class="oas-flex oas-gap-2">
|
|
37
|
+
<button class="mod-cta" style="font-size: var(--font-ui-smaller);">Accept all</button>
|
|
38
|
+
<button class="mod-destructive" style="font-size: var(--font-ui-smaller);">Reject all</button>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="oas-diff-view-labels">
|
|
44
|
+
<div class="oas-diff-view-label">${originalLabel}</div>
|
|
45
|
+
<div class="oas-diff-view-label">${modifiedLabel}</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="oas-diff-view-body">
|
|
49
|
+
${DiffViewer({ original, modified })}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExampleView — a fuller reference for a new Obsidian plugin view.
|
|
3
|
+
*
|
|
4
|
+
* Shows header + scrollable list body + pinned footer input, matching the
|
|
5
|
+
* ChatView shell pattern from pi-vault-mind. Duplicate this folder, rename
|
|
6
|
+
* ExampleView → YourView everywhere, and replace the stub content.
|
|
7
|
+
*
|
|
8
|
+
* Shell anatomy (oas-shell-* classes handle all layout — no flex CSS to write):
|
|
9
|
+
* oas-shell-view-header pinned top bar: title + actions
|
|
10
|
+
* oas-shell-view-body scrollable content area
|
|
11
|
+
* oas-shell-view-footer pinned bottom bar: input + action button
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { html } from "@arrow-js/core";
|
|
15
|
+
import type { ArrowExpression, ArrowTemplate } from "@arrow-js/core";
|
|
16
|
+
import { EmptyState } from "../../components/EmptyState/EmptyState";
|
|
17
|
+
|
|
18
|
+
/** Stub item type — replace with your real data model. */
|
|
19
|
+
interface ExampleItem {
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
note?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const STUB_ITEMS: ExampleItem[] = [
|
|
26
|
+
{ id: "1", label: "First item", note: "This is an example note." },
|
|
27
|
+
{ id: "2", label: "Second item" },
|
|
28
|
+
{ id: "3", label: "Third item", note: "Another note." },
|
|
29
|
+
{ id: "4", label: "Fourth item" },
|
|
30
|
+
{ id: "5", label: "Fifth item", note: "More content here." },
|
|
31
|
+
{ id: "6", label: "Sixth item" },
|
|
32
|
+
{ id: "7", label: "Seventh item", note: "Scroll to see this." },
|
|
33
|
+
{ id: "8", label: "Eighth item" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
function ItemRow(item: ExampleItem): ArrowTemplate {
|
|
37
|
+
return html`
|
|
38
|
+
<div class="setting-item">
|
|
39
|
+
<div class="setting-item-info">
|
|
40
|
+
<div class="setting-item-name">${item.label}</div>
|
|
41
|
+
${item.note ? html`<div class="setting-item-description">${item.note}</div>` : ""}
|
|
42
|
+
</div>
|
|
43
|
+
<div class="setting-item-control">
|
|
44
|
+
<button class="clickable-icon" aria-label="Options">⋯</button>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function ExampleView(items: ExampleItem[] = STUB_ITEMS): ArrowExpression {
|
|
51
|
+
return html`
|
|
52
|
+
<div class="oas-shell-view">
|
|
53
|
+
|
|
54
|
+
<div class="oas-shell-view-header">
|
|
55
|
+
<div class="oas-flex oas-items-center oas-justify-between oas-px-3 oas-py-2-1">
|
|
56
|
+
<span class="oas-font-semibold oas-text-sm">Example View</span>
|
|
57
|
+
<div class="oas-flex oas-gap-1">
|
|
58
|
+
<button class="clickable-icon" aria-label="Filter">⊟</button>
|
|
59
|
+
<button class="mod-cta" style="font-size: var(--font-ui-smaller);">New item</button>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="oas-shell-view-body">
|
|
65
|
+
${
|
|
66
|
+
items.length === 0
|
|
67
|
+
? EmptyState({
|
|
68
|
+
icon: "file",
|
|
69
|
+
title: "No items yet",
|
|
70
|
+
description: "Create your first item to get started.",
|
|
71
|
+
action: { label: "New item", onClick: () => {} },
|
|
72
|
+
})
|
|
73
|
+
: ""
|
|
74
|
+
}
|
|
75
|
+
${items.map((item) => ItemRow(item))}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="oas-shell-view-footer">
|
|
79
|
+
<div class="oas-flex oas-items-center oas-gap-2 oas-px-3 oas-py-2">
|
|
80
|
+
<input
|
|
81
|
+
type="text"
|
|
82
|
+
class="search-input"
|
|
83
|
+
placeholder="Quick add…"
|
|
84
|
+
style="flex: 1;"
|
|
85
|
+
/>
|
|
86
|
+
<button class="mod-cta" aria-label="Add">+</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
</div>
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { html } from "@arrow-js/core";
|
|
2
|
+
import { defineStories } from "../../tools/viewer/stories";
|
|
3
|
+
|
|
4
|
+
export default defineStories({
|
|
5
|
+
title: "Structural / Card",
|
|
6
|
+
description:
|
|
7
|
+
"Standard panel container for standalone components. Use oas-shell-panel as the root wrapper when a component needs its own card frame.",
|
|
8
|
+
status: "live",
|
|
9
|
+
componentPath: "src/utilities.css",
|
|
10
|
+
variants: {
|
|
11
|
+
default: () => html`
|
|
12
|
+
<div class="oas-shell-panel" style="max-width: 320px;">
|
|
13
|
+
<div class="setting-item-name">Panel heading</div>
|
|
14
|
+
<div class="setting-item-description">Panel description or sub-content goes here.</div>
|
|
15
|
+
<div><button class="mod-cta">Primary action</button></div>
|
|
16
|
+
</div>
|
|
17
|
+
`,
|
|
18
|
+
with_badge: () => html`
|
|
19
|
+
<div class="oas-shell-panel" style="max-width: 320px;">
|
|
20
|
+
<div class="oas-flex oas-items-center oas-justify-between">
|
|
21
|
+
<div class="setting-item-name">Panel with badge</div>
|
|
22
|
+
<span class="oas-badge is-live">live</span>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="setting-item-description">Panel using oas-badge alongside shell.</div>
|
|
25
|
+
</div>
|
|
26
|
+
`,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -4,6 +4,7 @@ import { defineStories } from "../../tools/viewer/stories";
|
|
|
4
4
|
export default defineStories({
|
|
5
5
|
description: "Reusable empty state for any view — icon, title, description, optional action.",
|
|
6
6
|
status: "live",
|
|
7
|
+
componentPath: "src/components/EmptyState/EmptyState.ts",
|
|
7
8
|
variants: {
|
|
8
9
|
default: () => EmptyState({ title: "Nothing here yet" }),
|
|
9
10
|
"with description": () => {
|
|
@@ -4,6 +4,7 @@ import { defineStories } from "../../tools/viewer/stories";
|
|
|
4
4
|
export default defineStories({
|
|
5
5
|
description: "Loading indicator for async view content.",
|
|
6
6
|
status: "live",
|
|
7
|
+
componentPath: "src/components/LoadingState.ts",
|
|
7
8
|
variants: {
|
|
8
9
|
default: () => LoadingState(),
|
|
9
10
|
"with message": () => LoadingState("Loading sessions…"),
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { html, 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
|
+
title: "Settings / Toggle",
|
|
7
|
+
description:
|
|
8
|
+
"Obsidian-native checkbox toggle used in settings rows. Pass a reactive getter and an onToggle handler.",
|
|
9
|
+
status: "live",
|
|
10
|
+
componentPath: "src/components/SettingsPanel.ts",
|
|
11
|
+
variants: {
|
|
12
|
+
on: () => {
|
|
13
|
+
const state = reactive({ enabled: true });
|
|
14
|
+
return Toggle(
|
|
15
|
+
() => state.enabled,
|
|
16
|
+
() => {
|
|
17
|
+
state.enabled = !state.enabled;
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
off: () => {
|
|
22
|
+
const state = reactive({ enabled: false });
|
|
23
|
+
return Toggle(
|
|
24
|
+
() => state.enabled,
|
|
25
|
+
() => {
|
|
26
|
+
state.enabled = !state.enabled;
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
},
|
|
30
|
+
in_setting_row: () => {
|
|
31
|
+
const state = reactive({ enabled: true });
|
|
32
|
+
return html`
|
|
33
|
+
<div class="setting-item">
|
|
34
|
+
<div class="setting-item-info">
|
|
35
|
+
<div class="setting-item-name">Enable feature</div>
|
|
36
|
+
<div class="setting-item-description">Toggles the feature on or off.</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="setting-item-control">
|
|
39
|
+
${Toggle(
|
|
40
|
+
() => state.enabled,
|
|
41
|
+
() => {
|
|
42
|
+
state.enabled = !state.enabled;
|
|
43
|
+
}
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
`;
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { DiffViewerView } from "../../../src/views/DiffViewer/DiffViewerView";
|
|
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-04
|
|
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 view 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 CODE_ORIGINAL = `function greet(name: string): string {
|
|
56
|
+
\treturn "Hello, " + name;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = greet("world");
|
|
60
|
+
console.log(result);
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const CODE_MODIFIED = `function greet(name: string, greeting = "Hello"): string {
|
|
64
|
+
\treturn \`\${greeting}, \${name}!\`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const result = greet("world", "Hi");
|
|
68
|
+
console.log(result);
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
export default defineStories({
|
|
72
|
+
title: "Editor / DiffViewer",
|
|
73
|
+
description:
|
|
74
|
+
"Full-pane diff editor using CodeMirror 6 MergeView. Header shows file labels and accept/reject actions. Body fills the remaining height — CM6 manages internal scrolling.",
|
|
75
|
+
status: "live",
|
|
76
|
+
kind: "view",
|
|
77
|
+
componentPath: "src/views/DiffViewer/DiffViewerView.ts",
|
|
78
|
+
variants: {
|
|
79
|
+
markdown: () =>
|
|
80
|
+
DiffViewerView({
|
|
81
|
+
original: ORIGINAL,
|
|
82
|
+
modified: MODIFIED,
|
|
83
|
+
originalLabel: "notes-draft.md",
|
|
84
|
+
modifiedLabel: "notes-revised.md",
|
|
85
|
+
}),
|
|
86
|
+
code: () =>
|
|
87
|
+
DiffViewerView({
|
|
88
|
+
original: CODE_ORIGINAL,
|
|
89
|
+
modified: CODE_MODIFIED,
|
|
90
|
+
originalLabel: "utils.ts (original)",
|
|
91
|
+
modifiedLabel: "utils.ts (modified)",
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { html } from "@arrow-js/core";
|
|
2
|
+
import { defineStories } from "../../tools/viewer/stories";
|
|
3
|
+
|
|
4
|
+
export default defineStories({
|
|
5
|
+
title: "Views / Editor View",
|
|
6
|
+
description:
|
|
7
|
+
"Note/document view at readable line width. Full-bleed header + footer with the body content capped at --file-line-width via oas-readable-width. Copy this for any editor- or reader-style view; scaffold with `pnpm create:view <Name> --editor`.",
|
|
8
|
+
status: "live",
|
|
9
|
+
kind: "view",
|
|
10
|
+
surface: "editor",
|
|
11
|
+
componentPath: "src/utilities.css",
|
|
12
|
+
variants: {
|
|
13
|
+
default: () => html`
|
|
14
|
+
<div class="oas-shell-view" style="height: 420px; border: 1px dashed var(--background-modifier-border);">
|
|
15
|
+
<div class="oas-shell-view-header" style="padding: var(--size-4-2) var(--size-4-3); background: var(--background-secondary); border-bottom: 1px solid var(--background-modifier-border);">
|
|
16
|
+
Editor header (title, actions) — spans the full pane
|
|
17
|
+
</div>
|
|
18
|
+
<div class="oas-shell-view-body">
|
|
19
|
+
<div class="oas-readable-width">
|
|
20
|
+
<h1>Document title</h1>
|
|
21
|
+
<p>
|
|
22
|
+
The body content is capped at Obsidian's readable line width
|
|
23
|
+
(<code>--file-line-width</code>, 700px) and centered — the same
|
|
24
|
+
measure the markdown editor uses. Header and footer stay full-bleed.
|
|
25
|
+
</p>
|
|
26
|
+
${Array.from({ length: 4 }, (_, i) =>
|
|
27
|
+
html`<p>
|
|
28
|
+
Paragraph ${i + 1}. Long-form prose stays comfortable to read
|
|
29
|
+
because the line length never exceeds the readable measure, no
|
|
30
|
+
matter how wide the pane is dragged. Resize the panel to see the
|
|
31
|
+
column stay put while the chrome fills the width.
|
|
32
|
+
</p>`.key(i)
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="oas-shell-view-footer" style="padding: var(--size-4-2) var(--size-4-3);">
|
|
37
|
+
Editor footer (word count, status) — spans the full pane
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
`,
|
|
41
|
+
body_only: () => html`
|
|
42
|
+
<div class="oas-shell-view" style="height: 420px; border: 1px dashed var(--background-modifier-border);">
|
|
43
|
+
<div class="oas-shell-view-body">
|
|
44
|
+
<div class="oas-readable-width">
|
|
45
|
+
<h1>Reader view</h1>
|
|
46
|
+
<p>
|
|
47
|
+
Body only — no header or footer. Content is centered at readable
|
|
48
|
+
line width, ideal for a distraction-free reader or preview pane.
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
`,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ExampleView } from "../../../src/views/ExampleView/ExampleView";
|
|
2
|
+
import { defineStories } from "../../../tools/viewer/stories";
|
|
3
|
+
|
|
4
|
+
export default defineStories({
|
|
5
|
+
title: "Example / ExampleView",
|
|
6
|
+
description:
|
|
7
|
+
"Reference shell for a new view. Duplicate this folder, rename ExampleView → YourView, and replace the stub content. The oas-shell-* classes handle all layout.",
|
|
8
|
+
status: "live",
|
|
9
|
+
kind: "view",
|
|
10
|
+
componentPath: "src/views/ExampleView/ExampleView.ts",
|
|
11
|
+
variants: {
|
|
12
|
+
with_items: () => ExampleView(),
|
|
13
|
+
empty: () => ExampleView([]),
|
|
14
|
+
},
|
|
15
|
+
});
|