sdocs 0.0.14 → 0.0.16
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/bin/sdocs.js +1 -1
- package/dist/client/App.svelte +16 -39
- package/dist/client/App.svelte.d.ts +1 -1
- package/dist/client/favicon.png +0 -0
- package/dist/client/tree-builder.js +57 -14
- package/dist/client/{CollapsiblePanel.svelte → views/CollapsiblePanel.svelte} +5 -5
- package/dist/client/{ComponentView.svelte → views/ComponentView.svelte} +39 -13
- package/dist/client/{ComponentView.svelte.d.ts → views/ComponentView.svelte.d.ts} +1 -1
- package/dist/client/{ControlsPanel.svelte → views/ControlsPanel.svelte} +10 -15
- package/dist/client/{ControlsPanel.svelte.d.ts → views/ControlsPanel.svelte.d.ts} +1 -1
- package/dist/client/{DataTable.svelte → views/DataTable.svelte} +8 -8
- package/dist/client/{HomePage.svelte → views/HomePage.svelte} +10 -8
- package/dist/client/{HomePage.svelte.d.ts → views/HomePage.svelte.d.ts} +1 -1
- package/dist/client/{LayoutView.svelte → views/LayoutView.svelte} +1 -1
- package/dist/client/{LayoutView.svelte.d.ts → views/LayoutView.svelte.d.ts} +1 -1
- package/dist/client/{PageView.svelte → views/PageView.svelte} +7 -7
- package/dist/client/{PageView.svelte.d.ts → views/PageView.svelte.d.ts} +1 -1
- package/dist/client/{PreviewFrame.svelte → views/PreviewFrame.svelte} +10 -3
- package/dist/client/views/Sidebar.svelte +369 -0
- package/dist/client/{Sidebar.svelte.d.ts → views/Sidebar.svelte.d.ts} +2 -2
- package/dist/commands/build.js +2 -2
- package/dist/commands/dev.js +2 -2
- package/dist/commands/init.js +4 -4
- package/dist/commands/preview.js +1 -1
- package/dist/{app-gen.d.ts → server/app-gen.d.ts} +1 -1
- package/dist/{app-gen.js → server/app-gen.js} +4 -4
- package/dist/{cli.js → server/cli.js} +4 -4
- package/dist/{config.d.ts → server/config.d.ts} +1 -1
- package/dist/{config.js → server/config.js} +7 -2
- package/dist/server/discovery.d.ts +1 -1
- package/dist/server/discovery.js +1 -1
- package/dist/server/meta-parser.js +1 -1
- package/dist/server/prop-parser.js +12 -4
- package/dist/server/snippet-compiler.js +8 -0
- package/dist/server/snippet-extractor.d.ts +1 -1
- package/dist/server/snippet-extractor.js +40 -5
- package/dist/server/toc-extractor.d.ts +0 -2
- package/dist/server/toc-extractor.js +0 -14
- package/dist/types.d.ts +3 -3
- package/dist/ui/About.page.sdoc +37 -0
- package/dist/ui/Button/Button.sdoc +51 -0
- package/dist/ui/Button/Button.svelte +90 -0
- package/dist/ui/Button/Button.svelte.d.ts +27 -0
- package/dist/ui/Button/index.d.ts +1 -0
- package/dist/ui/Button/index.js +1 -0
- package/dist/ui/Control/Checkbox.sdoc +17 -0
- package/dist/{client/controls/CheckboxControl.svelte → ui/Control/Checkbox.svelte} +1 -1
- package/dist/ui/Control/Checkbox.svelte.d.ts +8 -0
- package/dist/ui/Control/Color.sdoc +17 -0
- package/dist/{client/controls/ColorControl.svelte → ui/Control/Color.svelte} +4 -4
- package/dist/ui/Control/Color.svelte.d.ts +8 -0
- package/dist/ui/Control/Dimension.sdoc +17 -0
- package/dist/{client/controls/DimensionControl.svelte → ui/Control/Dimension.svelte} +5 -5
- package/dist/ui/Control/Dimension.svelte.d.ts +8 -0
- package/dist/ui/Control/Number.sdoc +17 -0
- package/dist/{client/controls/NumberControl.svelte → ui/Control/Number.svelte} +4 -4
- package/dist/ui/Control/Number.svelte.d.ts +8 -0
- package/dist/ui/Control/Select.sdoc +18 -0
- package/dist/{client/controls/SelectControl.svelte → ui/Control/Select.svelte} +4 -4
- package/dist/ui/Control/Select.svelte.d.ts +9 -0
- package/dist/ui/Control/Text.sdoc +17 -0
- package/dist/{client/controls/TextControl.svelte → ui/Control/Text.svelte} +4 -4
- package/dist/ui/Control/Text.svelte.d.ts +8 -0
- package/dist/ui/Control/index.d.ts +6 -0
- package/dist/ui/Control/index.js +6 -0
- package/dist/ui/Dashboard.layout.sdoc +60 -0
- package/dist/ui/Frame/Frame.sdoc +54 -0
- package/dist/ui/Frame/Frame.svelte +88 -0
- package/dist/ui/Frame/Frame.svelte.d.ts +15 -0
- package/dist/ui/Frame/index.d.ts +1 -0
- package/dist/ui/Frame/index.js +1 -0
- package/dist/ui/Icon/Icon.sdoc +45 -0
- package/dist/ui/Icon/Icon.svelte +47 -0
- package/dist/ui/Icon/Icon.svelte.d.ts +16 -0
- package/dist/ui/Icon/icons/bookmark.svg +1 -0
- package/dist/ui/Icon/icons/chevron-down.svg +1 -0
- package/dist/ui/Icon/icons/chevron-left.svg +1 -0
- package/dist/ui/Icon/icons/chevron-right.svg +1 -0
- package/dist/ui/Icon/icons/chevron-up.svg +1 -0
- package/dist/ui/Icon/icons/code.svg +1 -0
- package/dist/ui/Icon/icons/component.svg +1 -0
- package/dist/ui/Icon/icons/copy.svg +1 -0
- package/dist/ui/Icon/icons/diamond.svg +1 -0
- package/dist/ui/Icon/icons/file-code.svg +1 -0
- package/dist/ui/Icon/icons/file-text.svg +1 -0
- package/dist/ui/Icon/icons/folder-open.svg +1 -0
- package/dist/ui/Icon/icons/folder.svg +1 -0
- package/dist/ui/Icon/icons/panels-top-left.svg +1 -0
- package/dist/ui/Icon/icons/sdocs.svg +8 -0
- package/dist/ui/Icon/index.d.ts +1 -0
- package/dist/ui/Icon/index.js +1 -0
- package/dist/ui/NavTree/Group.sdoc +46 -0
- package/dist/ui/NavTree/Group.svelte +82 -0
- package/dist/ui/NavTree/Group.svelte.d.ts +11 -0
- package/dist/ui/NavTree/Item.sdoc +70 -0
- package/dist/ui/NavTree/Item.svelte +164 -0
- package/dist/ui/NavTree/Item.svelte.d.ts +33 -0
- package/dist/ui/NavTree/NavTree.sdoc +119 -0
- package/dist/ui/NavTree/NavTree.svelte +31 -0
- package/dist/ui/NavTree/NavTree.svelte.d.ts +8 -0
- package/dist/ui/NavTree/index.js +6 -0
- package/dist/ui/Stack/Stack.sdoc +44 -0
- package/dist/ui/Stack/Stack.svelte +41 -0
- package/dist/ui/Stack/Stack.svelte.d.ts +19 -0
- package/dist/ui/Stack/index.d.ts +1 -0
- package/dist/ui/Stack/index.js +1 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/styles/fonts/jetbrains-mono-italic-latin-ext.woff2 +0 -0
- package/dist/ui/styles/fonts/jetbrains-mono-italic-latin.woff2 +0 -0
- package/dist/ui/styles/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
- package/dist/ui/styles/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/dist/ui/styles/fonts.css +87 -0
- package/dist/ui/styles/theme.css +910 -0
- package/dist/vite.js +47 -51
- package/package.json +5 -2
- package/dist/client/Sidebar.svelte +0 -329
- package/dist/client/controls/CheckboxControl.svelte.d.ts +0 -8
- package/dist/client/controls/ColorControl.svelte.d.ts +0 -8
- package/dist/client/controls/DimensionControl.svelte.d.ts +0 -8
- package/dist/client/controls/NumberControl.svelte.d.ts +0 -8
- package/dist/client/controls/SelectControl.svelte.d.ts +0 -9
- package/dist/client/controls/TextControl.svelte.d.ts +0 -8
- package/dist/client/theme.css +0 -80
- package/dist/server/sdocx-parser.d.ts +0 -11
- package/dist/server/sdocx-parser.js +0 -197
- /package/dist/client/{CollapsiblePanel.svelte.d.ts → views/CollapsiblePanel.svelte.d.ts} +0 -0
- /package/dist/client/{DataTable.svelte.d.ts → views/DataTable.svelte.d.ts} +0 -0
- /package/dist/client/{PreviewFrame.svelte.d.ts → views/PreviewFrame.svelte.d.ts} +0 -0
- /package/dist/{cli.d.ts → server/cli.d.ts} +0 -0
- /package/dist/{client → ui/styles}/fonts/figtree-italic-latin-ext.woff2 +0 -0
- /package/dist/{client → ui/styles}/fonts/figtree-italic-latin.woff2 +0 -0
- /package/dist/{client → ui/styles}/fonts/figtree-latin-ext.woff2 +0 -0
- /package/dist/{client → ui/styles}/fonts/figtree-latin.woff2 +0 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
let { src, props = {}, cssVars = {}, activeStylesheet, fullHeight = false }: Props = $props();
|
|
13
13
|
let iframe: HTMLIFrameElement;
|
|
14
14
|
let ready = $state(false);
|
|
15
|
+
let contentHeight = $state(0);
|
|
15
16
|
|
|
16
17
|
onMount(() => {
|
|
17
18
|
function onMessage(e: MessageEvent) {
|
|
@@ -21,6 +22,9 @@
|
|
|
21
22
|
sendCss();
|
|
22
23
|
sendStylesheet();
|
|
23
24
|
}
|
|
25
|
+
if (e.data?.type === 'sdocs:resize' && e.source === iframe?.contentWindow) {
|
|
26
|
+
contentHeight = e.data.height;
|
|
27
|
+
}
|
|
24
28
|
}
|
|
25
29
|
window.addEventListener('message', onMessage);
|
|
26
30
|
return () => window.removeEventListener('message', onMessage);
|
|
@@ -72,15 +76,17 @@
|
|
|
72
76
|
{src}
|
|
73
77
|
title="Component preview"
|
|
74
78
|
class="sdocs-iframe"
|
|
79
|
+
scrolling="no"
|
|
80
|
+
style:height={contentHeight ? `${contentHeight}px` : undefined}
|
|
75
81
|
></iframe>
|
|
76
82
|
</div>
|
|
77
83
|
|
|
78
84
|
<style>
|
|
79
85
|
.sdocs-preview-frame {
|
|
80
|
-
border: 1px solid var(--
|
|
86
|
+
border: 1px solid var(--color-base-200);
|
|
81
87
|
border-radius: 6px;
|
|
82
88
|
overflow: hidden;
|
|
83
|
-
background: var(--
|
|
89
|
+
background: var(--color-base-0);
|
|
84
90
|
}
|
|
85
91
|
.sdocs-preview-frame.full-height {
|
|
86
92
|
flex: 1;
|
|
@@ -89,7 +95,8 @@
|
|
|
89
95
|
}
|
|
90
96
|
.sdocs-iframe {
|
|
91
97
|
width: 100%;
|
|
92
|
-
|
|
98
|
+
max-height: 800px;
|
|
99
|
+
overflow-y: auto;
|
|
93
100
|
border: none;
|
|
94
101
|
display: block;
|
|
95
102
|
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
3
|
+
import type { TreeNode } from '../tree-builder.js';
|
|
4
|
+
import { pathToHash } from '../router.svelte.js';
|
|
5
|
+
import { Icon } from '../../ui/Icon/index.js';
|
|
6
|
+
import { NavTree } from '../../ui/index.js';
|
|
7
|
+
|
|
8
|
+
type ThemeMode = 'light' | 'dark';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
tree: TreeNode[];
|
|
12
|
+
currentPath: string[];
|
|
13
|
+
logo: string;
|
|
14
|
+
cssNames?: string[];
|
|
15
|
+
activeStylesheet?: string;
|
|
16
|
+
theme?: ThemeMode;
|
|
17
|
+
onToggleFullscreen?: () => void;
|
|
18
|
+
onStylesheetChange?: (name: string) => void;
|
|
19
|
+
onThemeChange?: (theme: ThemeMode) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { tree, currentPath, logo, cssNames = [], activeStylesheet, theme = 'light', onToggleFullscreen, onStylesheetChange, onThemeChange }: Props = $props();
|
|
23
|
+
|
|
24
|
+
const themeIcons: Record<ThemeMode, string> = { light: '\u2600', dark: '\u263D' };
|
|
25
|
+
const themeLabels: Record<ThemeMode, string> = { light: 'Light', dark: 'Dark' };
|
|
26
|
+
|
|
27
|
+
function toggleTheme() {
|
|
28
|
+
onThemeChange?.(theme === 'light' ? 'dark' : 'light');
|
|
29
|
+
}
|
|
30
|
+
let searchQuery = $state('');
|
|
31
|
+
|
|
32
|
+
// Track expanded state by path key (SvelteSet for reactivity)
|
|
33
|
+
let expandedSet = new SvelteSet<string>();
|
|
34
|
+
let initialized = false;
|
|
35
|
+
|
|
36
|
+
// Initialize expanded state from tree defaults
|
|
37
|
+
$effect(() => {
|
|
38
|
+
if (!initialized && tree.length > 0) {
|
|
39
|
+
collectDefaults(tree, expandedSet);
|
|
40
|
+
initialized = true;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
function collectDefaults(nodes: TreeNode[], set: SvelteSet<string>) {
|
|
45
|
+
for (const node of nodes) {
|
|
46
|
+
if (node.defaultExpanded || node.type === 'group') {
|
|
47
|
+
set.add(node.path.join('/'));
|
|
48
|
+
}
|
|
49
|
+
if (node.children.length > 0) {
|
|
50
|
+
collectDefaults(node.children, set);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function toggleExpanded(pathKey: string) {
|
|
56
|
+
if (expandedSet.has(pathKey)) {
|
|
57
|
+
expandedSet.delete(pathKey);
|
|
58
|
+
} else {
|
|
59
|
+
expandedSet.add(pathKey);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isExpanded(pathKey: string): boolean {
|
|
64
|
+
if (searchQuery.trim()) return true; // Auto-expand when searching
|
|
65
|
+
return expandedSet.has(pathKey);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const filteredTree = $derived(
|
|
69
|
+
searchQuery.trim() ? filterTree(tree, searchQuery.trim().toLowerCase()) : tree,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
function filterTree(nodes: TreeNode[], query: string): TreeNode[] {
|
|
73
|
+
const result: TreeNode[] = [];
|
|
74
|
+
for (const node of nodes) {
|
|
75
|
+
if (node.name.toLowerCase().includes(query)) {
|
|
76
|
+
result.push(node);
|
|
77
|
+
} else if (node.children.length > 0) {
|
|
78
|
+
const filtered = filterTree(node.children, query);
|
|
79
|
+
if (filtered.length > 0) {
|
|
80
|
+
result.push({ ...node, children: filtered });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isActive(nodePath: string[]): boolean {
|
|
88
|
+
if (nodePath.length > currentPath.length) return false;
|
|
89
|
+
return nodePath.every((seg, i) => seg === currentPath[i]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isExactActive(nodePath: string[]): boolean {
|
|
93
|
+
return nodePath.length === currentPath.length && nodePath.every((seg, i) => seg === currentPath[i]);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function iconName(node: TreeNode, expanded: boolean): string {
|
|
97
|
+
switch (node.type) {
|
|
98
|
+
case 'folder': return expanded ? 'folder-open' : 'folder';
|
|
99
|
+
case 'component':
|
|
100
|
+
if (node.children.length > 0) {
|
|
101
|
+
const hasChildComponent = node.children.some(c => c.type === 'component' && c.children.length > 0);
|
|
102
|
+
return hasChildComponent ? 'component' : 'diamond';
|
|
103
|
+
}
|
|
104
|
+
if (node.name === 'Docs') return 'file-code';
|
|
105
|
+
return 'bookmark';
|
|
106
|
+
case 'page': return 'file-text';
|
|
107
|
+
case 'layout': return 'panels-top-left';
|
|
108
|
+
default: return 'file';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function iconColor(node: TreeNode): string {
|
|
113
|
+
switch (node.type) {
|
|
114
|
+
case 'folder': return 'var(--color-base-400)';
|
|
115
|
+
case 'component':
|
|
116
|
+
if (node.children.length > 0) return 'var(--color-component-500)';
|
|
117
|
+
if (node.name === 'Docs') return 'var(--color-docs-500)';
|
|
118
|
+
return 'var(--color-example-500)';
|
|
119
|
+
case 'page': return 'var(--color-page-550)';
|
|
120
|
+
case 'layout': return 'var(--color-layout-500)';
|
|
121
|
+
default: return 'inherit';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function expanderActiveColor(node: TreeNode): string {
|
|
126
|
+
switch (node.type) {
|
|
127
|
+
case 'component': return 'var(--color-component-400)';
|
|
128
|
+
case 'page': return 'var(--color-page-400)';
|
|
129
|
+
case 'layout': return 'var(--color-layout-400)';
|
|
130
|
+
default: return 'var(--color-base-400)';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function expanderHoverColor(node: TreeNode): string {
|
|
135
|
+
switch (node.type) {
|
|
136
|
+
case 'component': return 'var(--color-component-600)';
|
|
137
|
+
case 'page': return 'var(--color-page-600)';
|
|
138
|
+
case 'layout': return 'var(--color-layout-600)';
|
|
139
|
+
default: return 'var(--color-base-500)';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function hoverBg(node: TreeNode): string {
|
|
144
|
+
switch (node.type) {
|
|
145
|
+
case 'component':
|
|
146
|
+
if (node.children.length > 0) return 'var(--color-component-50)';
|
|
147
|
+
if (node.name === 'Docs') return 'var(--color-docs-50)';
|
|
148
|
+
return 'var(--color-example-50)';
|
|
149
|
+
case 'page': return 'var(--color-page-50)';
|
|
150
|
+
case 'layout': return 'var(--color-layout-50)';
|
|
151
|
+
default: return 'var(--color-base-100)';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function activeBg(node: TreeNode): string {
|
|
156
|
+
switch (node.type) {
|
|
157
|
+
case 'component':
|
|
158
|
+
if (node.children.length > 0) return 'var(--color-component-100)';
|
|
159
|
+
if (node.name === 'Docs') return 'var(--color-docs-100)';
|
|
160
|
+
return 'var(--color-example-100)';
|
|
161
|
+
case 'page': return 'var(--color-page-100)';
|
|
162
|
+
case 'layout': return 'var(--color-layout-100)';
|
|
163
|
+
default: return 'var(--color-base-100)';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function activeHoverBg(node: TreeNode): string {
|
|
168
|
+
switch (node.type) {
|
|
169
|
+
case 'component':
|
|
170
|
+
if (node.children.length > 0) return 'var(--color-component-150)';
|
|
171
|
+
if (node.name === 'Docs') return 'var(--color-docs-150)';
|
|
172
|
+
return 'var(--color-example-150)';
|
|
173
|
+
case 'page': return 'var(--color-page-150)';
|
|
174
|
+
case 'layout': return 'var(--color-layout-150)';
|
|
175
|
+
default: return 'var(--color-base-150)';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function leafWeight(node: TreeNode): string {
|
|
180
|
+
if (node.type === 'page' || node.type === 'layout') return '500';
|
|
181
|
+
return '400';
|
|
182
|
+
}
|
|
183
|
+
</script>
|
|
184
|
+
|
|
185
|
+
<aside class="sdocs-sidebar">
|
|
186
|
+
<div class="sdocs-sidebar-header">
|
|
187
|
+
<a href="#/" class="sdocs-logo">
|
|
188
|
+
<Icon name="sdocs" --w="22px" --h="22px" --fill="#FC1D29" />
|
|
189
|
+
{logo}
|
|
190
|
+
</a>
|
|
191
|
+
<div class="sdocs-header-actions">
|
|
192
|
+
{#if cssNames.length > 1}
|
|
193
|
+
<select
|
|
194
|
+
class="sdocs-css-picker"
|
|
195
|
+
value={activeStylesheet}
|
|
196
|
+
onchange={(e) => onStylesheetChange?.(e.currentTarget.value)}
|
|
197
|
+
>
|
|
198
|
+
{#each cssNames as name (name)}
|
|
199
|
+
<option value={name}>{name}</option>
|
|
200
|
+
{/each}
|
|
201
|
+
</select>
|
|
202
|
+
{/if}
|
|
203
|
+
<button class="sdocs-theme-btn" onclick={toggleTheme} title="{themeLabels[theme]} theme">
|
|
204
|
+
{themeIcons[theme]}
|
|
205
|
+
</button>
|
|
206
|
+
<button class="sdocs-fullscreen-btn" onclick={() => onToggleFullscreen?.()} title="Fullscreen">
|
|
207
|
+
⛶
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<div class="sdocs-sidebar-search">
|
|
213
|
+
<input
|
|
214
|
+
type="text"
|
|
215
|
+
placeholder="Search..."
|
|
216
|
+
bind:value={searchQuery}
|
|
217
|
+
class="sdocs-search-input"
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<nav class="sdocs-sidebar-tree">
|
|
222
|
+
{#each filteredTree as node (node.path.join('/'))}
|
|
223
|
+
{@render treeNode(node)}
|
|
224
|
+
{/each}
|
|
225
|
+
</nav>
|
|
226
|
+
</aside>
|
|
227
|
+
|
|
228
|
+
{#snippet treeNode(node: TreeNode)}
|
|
229
|
+
{@const pathKey = node.path.join('/')}
|
|
230
|
+
{@const expanded = isExpanded(pathKey)}
|
|
231
|
+
|
|
232
|
+
{#if node.type === 'group'}
|
|
233
|
+
<NavTree.Group label={node.name} {expanded} onclick={() => toggleExpanded(pathKey)}>
|
|
234
|
+
{#each node.children as child (child.path.join('/'))}
|
|
235
|
+
{@render treeNode(child)}
|
|
236
|
+
{/each}
|
|
237
|
+
</NavTree.Group>
|
|
238
|
+
{:else if node.children.length > 0}
|
|
239
|
+
<NavTree.Item
|
|
240
|
+
label={node.name}
|
|
241
|
+
{expanded}
|
|
242
|
+
active={isActive(node.path)}
|
|
243
|
+
onclick={() => {
|
|
244
|
+
const wasCollapsed = !expandedSet.has(pathKey);
|
|
245
|
+
toggleExpanded(pathKey);
|
|
246
|
+
if (wasCollapsed && node.type !== 'folder') {
|
|
247
|
+
window.location.hash = pathToHash(node.path);
|
|
248
|
+
}
|
|
249
|
+
}}
|
|
250
|
+
--font-weight="500"
|
|
251
|
+
--bg-hover={hoverBg(node)}
|
|
252
|
+
--bg-active={activeBg(node)}
|
|
253
|
+
--bg-active-hover={activeHoverBg(node)}
|
|
254
|
+
--r="4px"
|
|
255
|
+
--expander-color-active={expanderActiveColor(node)}
|
|
256
|
+
--expander-color-hover={expanderHoverColor(node)}
|
|
257
|
+
>
|
|
258
|
+
{#snippet left()}<Icon name={iconName(node, expanded)} --w="14px" --h="14px" --fill={iconColor(node)} />{/snippet}
|
|
259
|
+
{#each node.children as child (child.path.join('/'))}
|
|
260
|
+
{@render treeNode(child)}
|
|
261
|
+
{/each}
|
|
262
|
+
</NavTree.Item>
|
|
263
|
+
{:else}
|
|
264
|
+
<NavTree.Item
|
|
265
|
+
label={node.name}
|
|
266
|
+
href={pathToHash(node.path)}
|
|
267
|
+
active={isExactActive(node.path)}
|
|
268
|
+
--font-weight={leafWeight(node)}
|
|
269
|
+
--bg-hover={hoverBg(node)}
|
|
270
|
+
--bg-active={activeBg(node)}
|
|
271
|
+
--bg-active-hover={activeHoverBg(node)}
|
|
272
|
+
--r="4px"
|
|
273
|
+
>
|
|
274
|
+
{#snippet left()}<Icon name={iconName(node, false)} --w="14px" --h="14px" --fill={iconColor(node)} />{/snippet}
|
|
275
|
+
</NavTree.Item>
|
|
276
|
+
{/if}
|
|
277
|
+
{/snippet}
|
|
278
|
+
|
|
279
|
+
<style>
|
|
280
|
+
.sdocs-sidebar {
|
|
281
|
+
width: 260px;
|
|
282
|
+
height: 100vh;
|
|
283
|
+
overflow-y: auto;
|
|
284
|
+
border-right: 1px solid var(--color-base-200);
|
|
285
|
+
background: var(--color-base-0);
|
|
286
|
+
display: flex;
|
|
287
|
+
flex-direction: column;
|
|
288
|
+
font-family: var(--sans);
|
|
289
|
+
font-size: 13px;
|
|
290
|
+
}
|
|
291
|
+
.sdocs-sidebar-header {
|
|
292
|
+
padding: 12px 16px;
|
|
293
|
+
border-bottom: 1px solid var(--color-base-200);
|
|
294
|
+
display: flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
justify-content: space-between;
|
|
297
|
+
}
|
|
298
|
+
.sdocs-logo {
|
|
299
|
+
display: flex;
|
|
300
|
+
align-items: center;
|
|
301
|
+
gap: 6px;
|
|
302
|
+
font-weight: 700;
|
|
303
|
+
font-size: 20px;
|
|
304
|
+
color: var(--color-base-900);
|
|
305
|
+
text-decoration: none;
|
|
306
|
+
}
|
|
307
|
+
.sdocs-header-actions {
|
|
308
|
+
display: flex;
|
|
309
|
+
align-items: center;
|
|
310
|
+
gap: 6px;
|
|
311
|
+
}
|
|
312
|
+
.sdocs-css-picker {
|
|
313
|
+
font-size: 12px;
|
|
314
|
+
padding: 2px 4px;
|
|
315
|
+
border: 1px solid var(--color-base-200);
|
|
316
|
+
border-radius: 4px;
|
|
317
|
+
background: var(--color-base-0);
|
|
318
|
+
color: var(--color-base-600);
|
|
319
|
+
}
|
|
320
|
+
.sdocs-theme-btn {
|
|
321
|
+
padding: 2px 6px;
|
|
322
|
+
border: 1px solid var(--color-base-200);
|
|
323
|
+
border-radius: 4px;
|
|
324
|
+
background: var(--color-base-0);
|
|
325
|
+
color: var(--color-base-600);
|
|
326
|
+
cursor: pointer;
|
|
327
|
+
font-size: 14px;
|
|
328
|
+
line-height: 1;
|
|
329
|
+
}
|
|
330
|
+
.sdocs-theme-btn:hover {
|
|
331
|
+
background: var(--color-base-100);
|
|
332
|
+
}
|
|
333
|
+
.sdocs-fullscreen-btn {
|
|
334
|
+
padding: 2px 6px;
|
|
335
|
+
border: 1px solid var(--color-base-200);
|
|
336
|
+
border-radius: 4px;
|
|
337
|
+
background: var(--color-base-0);
|
|
338
|
+
color: var(--color-base-600);
|
|
339
|
+
cursor: pointer;
|
|
340
|
+
font-size: 14px;
|
|
341
|
+
line-height: 1;
|
|
342
|
+
}
|
|
343
|
+
.sdocs-fullscreen-btn:hover {
|
|
344
|
+
background: var(--color-base-100);
|
|
345
|
+
}
|
|
346
|
+
.sdocs-sidebar-search {
|
|
347
|
+
padding: 8px 16px;
|
|
348
|
+
}
|
|
349
|
+
.sdocs-search-input {
|
|
350
|
+
width: 100%;
|
|
351
|
+
padding: 6px 10px;
|
|
352
|
+
border: 1px solid var(--color-base-200);
|
|
353
|
+
border-radius: 6px;
|
|
354
|
+
font-size: 13px;
|
|
355
|
+
outline: none;
|
|
356
|
+
box-sizing: border-box;
|
|
357
|
+
background: var(--color-base-0);
|
|
358
|
+
color: var(--color-base-600);
|
|
359
|
+
}
|
|
360
|
+
.sdocs-search-input:focus {
|
|
361
|
+
border-color: var(--color-action-500);
|
|
362
|
+
box-shadow: 0 0 0 2px var(--color-action-100);
|
|
363
|
+
}
|
|
364
|
+
.sdocs-sidebar-tree {
|
|
365
|
+
flex: 1;
|
|
366
|
+
overflow-y: auto;
|
|
367
|
+
padding: 8px;
|
|
368
|
+
}
|
|
369
|
+
</style>
|
package/dist/commands/build.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { build } from 'vite';
|
|
3
3
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
4
|
+
import { loadConfig } from '../server/config.js';
|
|
5
5
|
import { sdocsPlugin } from '../vite.js';
|
|
6
|
-
import { generateBuildFiles, cleanBuildFiles } from '../app-gen.js';
|
|
6
|
+
import { generateBuildFiles, cleanBuildFiles } from '../server/app-gen.js';
|
|
7
7
|
export async function buildCommand() {
|
|
8
8
|
const cwd = process.cwd();
|
|
9
9
|
const config = await loadConfig(cwd);
|
package/dist/commands/dev.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { createServer } from 'vite';
|
|
3
3
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
4
|
+
import { loadConfig } from '../server/config.js';
|
|
5
5
|
import { sdocsPlugin } from '../vite.js';
|
|
6
|
-
import { generateDevFiles, cleanBuildFiles } from '../app-gen.js';
|
|
6
|
+
import { generateDevFiles, cleanBuildFiles } from '../server/app-gen.js';
|
|
7
7
|
export async function devCommand() {
|
|
8
8
|
const cwd = process.cwd();
|
|
9
9
|
const config = await loadConfig(cwd);
|
package/dist/commands/init.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { writeFile } from 'node:fs/promises';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { findConfigFile } from '../config.js';
|
|
4
|
+
import { findConfigFile } from '../server/config.js';
|
|
5
5
|
const DEFAULT_CONFIG = `/** @type {import('sdocs').SdocsConfig} */
|
|
6
6
|
export default {
|
|
7
7
|
// Glob pattern(s) to find sdoc files
|
|
8
|
-
// include: ['./src/**/*.sdoc'
|
|
8
|
+
// include: ['./src/**/*.sdoc'],
|
|
9
9
|
|
|
10
|
-
// Dev server port (default:
|
|
11
|
-
// port:
|
|
10
|
+
// Dev server port (default: 3000)
|
|
11
|
+
// port: 3000,
|
|
12
12
|
|
|
13
13
|
// Open browser on start (default: false)
|
|
14
14
|
// open: false,
|
package/dist/commands/preview.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { preview } from 'vite';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
4
|
+
import { loadConfig } from '../server/config.js';
|
|
5
5
|
export async function previewCommand() {
|
|
6
6
|
const cwd = process.cwd();
|
|
7
7
|
const config = await loadConfig(cwd);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResolvedSdocsConfig } from '
|
|
1
|
+
import type { ResolvedSdocsConfig } from '../types.js';
|
|
2
2
|
/** Generate .sdocs/ directory with entry files for dev mode */
|
|
3
3
|
export declare function generateDevFiles(config: ResolvedSdocsConfig, cwd: string): Promise<string>;
|
|
4
4
|
/** Generate .sdocs/ directory with entry + preview HTML files for build mode */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { mkdir, writeFile, rm, readFile, copyFile, readdir } from 'node:fs/promises';
|
|
2
2
|
import { dirname, resolve, join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { discoverDocFiles, getSdocKind } from './
|
|
5
|
-
import { extractSnippets, hasDefaultSnippet } from './
|
|
6
|
-
import { base64urlEncode } from './
|
|
4
|
+
import { discoverDocFiles, getSdocKind } from './discovery.js';
|
|
5
|
+
import { extractSnippets, hasDefaultSnippet } from './snippet-extractor.js';
|
|
6
|
+
import { base64urlEncode } from './snippet-compiler.js';
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
/** Source client directory in the installed package */
|
|
9
9
|
function getClientSourceDir() {
|
|
@@ -31,7 +31,7 @@ function generateIndexHtml(title) {
|
|
|
31
31
|
<head>
|
|
32
32
|
<meta charset="UTF-8">
|
|
33
33
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
34
|
-
<link rel="icon" type="image/png" href="./favicon.png">
|
|
34
|
+
<link rel="icon" type="image/png" href="./client/favicon.png">
|
|
35
35
|
<title>${title}</title>
|
|
36
36
|
</head>
|
|
37
37
|
<body>
|
|
@@ -40,22 +40,22 @@ async function main() {
|
|
|
40
40
|
}
|
|
41
41
|
switch (command) {
|
|
42
42
|
case 'dev': {
|
|
43
|
-
const { devCommand } = await import('
|
|
43
|
+
const { devCommand } = await import('../commands/dev.js');
|
|
44
44
|
await devCommand();
|
|
45
45
|
break;
|
|
46
46
|
}
|
|
47
47
|
case 'build': {
|
|
48
|
-
const { buildCommand } = await import('
|
|
48
|
+
const { buildCommand } = await import('../commands/build.js');
|
|
49
49
|
await buildCommand();
|
|
50
50
|
break;
|
|
51
51
|
}
|
|
52
52
|
case 'preview': {
|
|
53
|
-
const { previewCommand } = await import('
|
|
53
|
+
const { previewCommand } = await import('../commands/preview.js');
|
|
54
54
|
await previewCommand();
|
|
55
55
|
break;
|
|
56
56
|
}
|
|
57
57
|
case 'init': {
|
|
58
|
-
const { initCommand } = await import('
|
|
58
|
+
const { initCommand } = await import('../commands/init.js');
|
|
59
59
|
await initCommand();
|
|
60
60
|
break;
|
|
61
61
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SdocsConfig, ResolvedSdocsConfig } from '
|
|
1
|
+
import type { SdocsConfig, ResolvedSdocsConfig } from '../types.js';
|
|
2
2
|
/** Find the config file path in the given directory */
|
|
3
3
|
export declare function findConfigFile(root: string): string | null;
|
|
4
4
|
/** Load and resolve the sdocs config with defaults */
|
|
@@ -3,8 +3,8 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
const CONFIG_NAMES = ['sdocs.config.ts', 'sdocs.config.mjs', 'sdocs.config.js'];
|
|
5
5
|
const DEFAULTS = {
|
|
6
|
-
include: ['./src/**/*.sdoc'
|
|
7
|
-
port:
|
|
6
|
+
include: ['./src/**/*.sdoc'],
|
|
7
|
+
port: 3000,
|
|
8
8
|
open: false,
|
|
9
9
|
css: null,
|
|
10
10
|
logo: 'sdocs',
|
|
@@ -22,6 +22,10 @@ export function findConfigFile(root) {
|
|
|
22
22
|
}
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
|
+
/** Resolve include glob patterns to absolute paths */
|
|
26
|
+
function resolveIncludePatterns(patterns, root) {
|
|
27
|
+
return patterns.map((p) => (p.startsWith('/') ? p : resolve(root, p)));
|
|
28
|
+
}
|
|
25
29
|
/** Resolve CSS paths to absolute filesystem paths */
|
|
26
30
|
function resolveCssPaths(css, root) {
|
|
27
31
|
if (!css)
|
|
@@ -42,6 +46,7 @@ export async function loadConfig(root) {
|
|
|
42
46
|
return { ...DEFAULTS };
|
|
43
47
|
const userConfig = await importConfig(configPath);
|
|
44
48
|
const resolved = resolveConfig(userConfig);
|
|
49
|
+
resolved.include = resolveIncludePatterns(resolved.include, root);
|
|
45
50
|
resolved.css = resolveCssPaths(resolved.css, root);
|
|
46
51
|
return resolved;
|
|
47
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Discover all .sdoc
|
|
1
|
+
/** Discover all .sdoc files matching the include patterns */
|
|
2
2
|
export declare function discoverDocFiles(include: string[], root: string): Promise<string[]>;
|
|
3
3
|
/** Determine the sdoc kind from the file path */
|
|
4
4
|
export declare function getSdocKind(filePath: string): 'component' | 'page' | 'layout';
|
package/dist/server/discovery.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { glob } from 'tinyglobby';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
/** Discover all .sdoc
|
|
3
|
+
/** Discover all .sdoc files matching the include patterns */
|
|
4
4
|
export async function discoverDocFiles(include, root) {
|
|
5
5
|
const files = await glob(include, {
|
|
6
6
|
cwd: root,
|
|
@@ -105,5 +105,5 @@ function resolveComponentPath(meta, imports, docFilePath) {
|
|
|
105
105
|
/** Guess a title from the file path */
|
|
106
106
|
function guessTitle(filePath) {
|
|
107
107
|
const fileName = filePath.split('/').pop() ?? '';
|
|
108
|
-
return fileName.replace(/\.
|
|
108
|
+
return fileName.replace(/\.sdoc$/, '').replace(/\./g, ' ');
|
|
109
109
|
}
|
|
@@ -64,10 +64,17 @@ function parsePropsDestructuring(sourceFile) {
|
|
|
64
64
|
ts.isObjectBindingPattern(node.name)) {
|
|
65
65
|
for (const element of node.name.elements) {
|
|
66
66
|
if (ts.isBindingElement(element)) {
|
|
67
|
-
|
|
68
|
-
const
|
|
67
|
+
// Use propertyName when present (e.g. `class: className`)
|
|
68
|
+
const name = element.propertyName
|
|
69
|
+
? element.propertyName.getText(sourceFile)
|
|
70
|
+
: element.name.getText(sourceFile);
|
|
71
|
+
const rawDefault = element.initializer
|
|
69
72
|
? element.initializer.getText(sourceFile)
|
|
70
73
|
: null;
|
|
74
|
+
// Strip wrapping quotes from string literals
|
|
75
|
+
const defaultValue = rawDefault?.match(/^['"`](.*?)['"`]$/)
|
|
76
|
+
? rawDefault.slice(1, -1) || null
|
|
77
|
+
: rawDefault;
|
|
71
78
|
props.push({ name, default: defaultValue });
|
|
72
79
|
}
|
|
73
80
|
}
|
|
@@ -210,7 +217,8 @@ function parseExportedState(sourceFile) {
|
|
|
210
217
|
function parseCssProps(fullSource, styleContent) {
|
|
211
218
|
const propMap = new Map();
|
|
212
219
|
// Extract var(--name) and var(--name, default) from <style>
|
|
213
|
-
|
|
220
|
+
// Supports one level of nested parens: var(--x, var(--y)), var(--x, rgba(0,0,0,0.5))
|
|
221
|
+
const varRegex = /var\(\s*(--[\w-]+)(?:\s*,\s*((?:[^()]+|\([^)]*\))+))?\s*\)/g;
|
|
214
222
|
let match;
|
|
215
223
|
while ((match = varRegex.exec(styleContent)) !== null) {
|
|
216
224
|
const name = match[1];
|
|
@@ -241,7 +249,7 @@ function parseCssProps(fullSource, styleContent) {
|
|
|
241
249
|
propMap.set(name, { name, type, default: defaultVal, description });
|
|
242
250
|
}
|
|
243
251
|
}
|
|
244
|
-
return Array.from(propMap.values());
|
|
252
|
+
return Array.from(propMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
245
253
|
}
|
|
246
254
|
// ─── Helpers ───
|
|
247
255
|
function getJsdocComment(node, sourceFile) {
|
|
@@ -56,6 +56,14 @@ export function generateIframeComponent(absoluteImports, snippetBody) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
window.parent.postMessage({ type: 'sdocs:preview-ready' }, '*');
|
|
59
|
+
|
|
60
|
+
// Report content height to parent for auto-sizing
|
|
61
|
+
const ro = new ResizeObserver(() => {
|
|
62
|
+
const height = document.getElementById('sdocs-preview')?.scrollHeight ?? 0;
|
|
63
|
+
window.parent.postMessage({ type: 'sdocs:resize', height }, '*');
|
|
64
|
+
});
|
|
65
|
+
ro.observe(document.getElementById('sdocs-preview'));
|
|
66
|
+
return () => ro.disconnect();
|
|
59
67
|
});
|
|
60
68
|
</script>
|
|
61
69
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtractedSnippet } from '../types.js';
|
|
2
|
-
/** Extract {#snippet} blocks from .sdoc source */
|
|
2
|
+
/** Extract top-level {#snippet} blocks from .sdoc source, handling nested snippets */
|
|
3
3
|
export declare function extractSnippets(source: string): ExtractedSnippet[];
|
|
4
4
|
/** Check if a Default snippet exists or needs auto-generation */
|
|
5
5
|
export declare function hasDefaultSnippet(snippets: ExtractedSnippet[]): boolean;
|