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,4 +1,4 @@
|
|
|
1
|
-
.empty-state {
|
|
1
|
+
.oas-empty-state {
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
4
|
align-items: center;
|
|
@@ -8,23 +8,23 @@
|
|
|
8
8
|
color: var(--text-muted);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
.empty-
|
|
11
|
+
.oas-empty-icon {
|
|
12
12
|
font-size: var(--font-ui-larger, 2rem);
|
|
13
13
|
line-height: 1;
|
|
14
14
|
color: var(--text-faint);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
.empty-
|
|
17
|
+
.oas-empty-title {
|
|
18
18
|
font-size: var(--font-ui-medium);
|
|
19
19
|
font-weight: var(--font-semibold);
|
|
20
20
|
color: var(--text-normal);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
.empty-
|
|
23
|
+
.oas-empty-desc {
|
|
24
24
|
font-size: var(--font-ui-small);
|
|
25
25
|
max-width: 280px;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
.empty-
|
|
28
|
+
.oas-empty-action {
|
|
29
29
|
margin-top: var(--size-4-2);
|
|
30
30
|
}
|
|
@@ -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}
|
|
@@ -52,7 +52,56 @@
|
|
|
52
52
|
flex-shrink: 0;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/*
|
|
55
|
+
/*
|
|
56
|
+
* Spacing — two Obsidian token families:
|
|
57
|
+
*
|
|
58
|
+
* 4-px scale oas-gap-N / oas-p-N / oas-px-N / oas-py-N / oas-mt-N / oas-mb-N
|
|
59
|
+
* N=1 → --size-4-1 (4px) N=2 → --size-4-2 (8px) N=3 → --size-4-3 (12px)
|
|
60
|
+
* N=4 → --size-4-4 (16px) N=5 → --size-4-5 (20px) N=6 → --size-4-6 (24px)
|
|
61
|
+
*
|
|
62
|
+
* 2-px sub-scale oas-gap-2-1 / oas-p-2-1 … (mirrors Obsidian token name)
|
|
63
|
+
* 2-1 → --size-2-1 (2px) 2-3 → --size-2-3 (6px)
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/* 2-px sub-scale ── */
|
|
67
|
+
.oas-gap-2-1 {
|
|
68
|
+
gap: var(--size-2-1);
|
|
69
|
+
}
|
|
70
|
+
.oas-p-2-1 {
|
|
71
|
+
padding: var(--size-2-1);
|
|
72
|
+
}
|
|
73
|
+
.oas-px-2-1 {
|
|
74
|
+
padding-inline: var(--size-2-1);
|
|
75
|
+
}
|
|
76
|
+
.oas-py-2-1 {
|
|
77
|
+
padding-block: var(--size-2-1);
|
|
78
|
+
}
|
|
79
|
+
.oas-mt-2-1 {
|
|
80
|
+
margin-top: var(--size-2-1);
|
|
81
|
+
}
|
|
82
|
+
.oas-mb-2-1 {
|
|
83
|
+
margin-bottom: var(--size-2-1);
|
|
84
|
+
}
|
|
85
|
+
.oas-gap-2-3 {
|
|
86
|
+
gap: var(--size-2-3);
|
|
87
|
+
}
|
|
88
|
+
.oas-p-2-3 {
|
|
89
|
+
padding: var(--size-2-3);
|
|
90
|
+
}
|
|
91
|
+
.oas-px-2-3 {
|
|
92
|
+
padding-inline: var(--size-2-3);
|
|
93
|
+
}
|
|
94
|
+
.oas-py-2-3 {
|
|
95
|
+
padding-block: var(--size-2-3);
|
|
96
|
+
}
|
|
97
|
+
.oas-mt-2-3 {
|
|
98
|
+
margin-top: var(--size-2-3);
|
|
99
|
+
}
|
|
100
|
+
.oas-mb-2-3 {
|
|
101
|
+
margin-bottom: var(--size-2-3);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* 4-px scale ── */
|
|
56
105
|
.oas-gap-1 {
|
|
57
106
|
gap: var(--size-4-1);
|
|
58
107
|
}
|
|
@@ -65,6 +114,12 @@
|
|
|
65
114
|
.oas-gap-4 {
|
|
66
115
|
gap: var(--size-4-4);
|
|
67
116
|
}
|
|
117
|
+
.oas-gap-5 {
|
|
118
|
+
gap: var(--size-4-5);
|
|
119
|
+
}
|
|
120
|
+
.oas-gap-6 {
|
|
121
|
+
gap: var(--size-4-6);
|
|
122
|
+
}
|
|
68
123
|
.oas-p-1 {
|
|
69
124
|
padding: var(--size-4-1);
|
|
70
125
|
}
|
|
@@ -77,12 +132,27 @@
|
|
|
77
132
|
.oas-p-4 {
|
|
78
133
|
padding: var(--size-4-4);
|
|
79
134
|
}
|
|
135
|
+
.oas-p-5 {
|
|
136
|
+
padding: var(--size-4-5);
|
|
137
|
+
}
|
|
138
|
+
.oas-p-6 {
|
|
139
|
+
padding: var(--size-4-6);
|
|
140
|
+
}
|
|
80
141
|
.oas-px-2 {
|
|
81
142
|
padding-inline: var(--size-4-2);
|
|
82
143
|
}
|
|
83
144
|
.oas-px-3 {
|
|
84
145
|
padding-inline: var(--size-4-3);
|
|
85
146
|
}
|
|
147
|
+
.oas-px-4 {
|
|
148
|
+
padding-inline: var(--size-4-4);
|
|
149
|
+
}
|
|
150
|
+
.oas-px-5 {
|
|
151
|
+
padding-inline: var(--size-4-5);
|
|
152
|
+
}
|
|
153
|
+
.oas-px-6 {
|
|
154
|
+
padding-inline: var(--size-4-6);
|
|
155
|
+
}
|
|
86
156
|
.oas-py-1 {
|
|
87
157
|
padding-block: var(--size-4-1);
|
|
88
158
|
}
|
|
@@ -92,6 +162,15 @@
|
|
|
92
162
|
.oas-py-3 {
|
|
93
163
|
padding-block: var(--size-4-3);
|
|
94
164
|
}
|
|
165
|
+
.oas-py-4 {
|
|
166
|
+
padding-block: var(--size-4-4);
|
|
167
|
+
}
|
|
168
|
+
.oas-py-5 {
|
|
169
|
+
padding-block: var(--size-4-5);
|
|
170
|
+
}
|
|
171
|
+
.oas-py-6 {
|
|
172
|
+
padding-block: var(--size-4-6);
|
|
173
|
+
}
|
|
95
174
|
.oas-mt-1 {
|
|
96
175
|
margin-top: var(--size-4-1);
|
|
97
176
|
}
|
|
@@ -101,6 +180,15 @@
|
|
|
101
180
|
.oas-mt-3 {
|
|
102
181
|
margin-top: var(--size-4-3);
|
|
103
182
|
}
|
|
183
|
+
.oas-mt-4 {
|
|
184
|
+
margin-top: var(--size-4-4);
|
|
185
|
+
}
|
|
186
|
+
.oas-mt-5 {
|
|
187
|
+
margin-top: var(--size-4-5);
|
|
188
|
+
}
|
|
189
|
+
.oas-mt-6 {
|
|
190
|
+
margin-top: var(--size-4-6);
|
|
191
|
+
}
|
|
104
192
|
.oas-mb-1 {
|
|
105
193
|
margin-bottom: var(--size-4-1);
|
|
106
194
|
}
|
|
@@ -110,6 +198,15 @@
|
|
|
110
198
|
.oas-mb-3 {
|
|
111
199
|
margin-bottom: var(--size-4-3);
|
|
112
200
|
}
|
|
201
|
+
.oas-mb-4 {
|
|
202
|
+
margin-bottom: var(--size-4-4);
|
|
203
|
+
}
|
|
204
|
+
.oas-mb-5 {
|
|
205
|
+
margin-bottom: var(--size-4-5);
|
|
206
|
+
}
|
|
207
|
+
.oas-mb-6 {
|
|
208
|
+
margin-bottom: var(--size-4-6);
|
|
209
|
+
}
|
|
113
210
|
.oas-ml-auto {
|
|
114
211
|
margin-left: auto;
|
|
115
212
|
}
|
|
@@ -195,6 +292,9 @@
|
|
|
195
292
|
.oas-rounded-m {
|
|
196
293
|
border-radius: var(--radius-m);
|
|
197
294
|
}
|
|
295
|
+
.oas-rounded-l {
|
|
296
|
+
border-radius: var(--radius-l);
|
|
297
|
+
}
|
|
198
298
|
|
|
199
299
|
/* ── Interaction ─────────────────────────────────────────────── */
|
|
200
300
|
.oas-cursor-pointer {
|
|
@@ -203,3 +303,161 @@
|
|
|
203
303
|
.oas-select-none {
|
|
204
304
|
user-select: none;
|
|
205
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
|
+
});
|