sdocs 0.0.53 → 0.0.54
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/CHANGELOG.md +14 -0
- package/dist/language/index.d.ts +1 -1
- package/dist/language/index.js +1 -1
- package/dist/language/parser.d.ts +15 -0
- package/dist/language/parser.js +10 -0
- package/dist/server/config.js +8 -1
- package/dist/server/snippet-compiler.d.ts +2 -0
- package/dist/server/snippet-compiler.js +39 -12
- package/dist/types.d.ts +11 -1
- package/dist/vite.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.0.54] - 2026-07-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Stage alignment on previews and examples.** Two direction-aware
|
|
15
|
+
attributes (also settable per-kind in `content.docs` and on `[DOCS]`):
|
|
16
|
+
`align` — horizontal (`left`/`center`/`right`/`justify`) — and `alignY` —
|
|
17
|
+
vertical (`top`/`middle`/`bottom`/`justify`). sdocs maps each to the right
|
|
18
|
+
flex property for the current `direction`, so `align="center"` centers
|
|
19
|
+
horizontally in a row or a column; `justify` spreads items along the flow.
|
|
20
|
+
- **`attributeRules(kind)` in `sdocs/language`** — the allowed attributes and
|
|
21
|
+
value shapes for a block kind, the single source of truth behind both
|
|
22
|
+
diagnostics and the VS Code extension's attribute completions.
|
|
23
|
+
|
|
10
24
|
## [0.0.53] - 2026-07-05
|
|
11
25
|
|
|
12
26
|
### Added
|
package/dist/language/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { scanSdoc, offsetToPosition, ENTITY_KINDS, SUB_BLOCK_KINDS, type Span, type EntityKind, type SubBlockKind, type AttrValue, type Attrs, type SubBlock, type Entity, type TagBlock, type ScanError, type SdocFile, } from './scanner.js';
|
|
2
2
|
export { projectSdoc, type SdocProjection, type ProjectedLineKind, } from './projection.js';
|
|
3
3
|
export { segmentPageBody, type PageSegment, } from './page-islands.js';
|
|
4
|
-
export { parseSdoc, parseArgsLiteral, slugifyTitle, normalizeBody, type ArgValue, type Sizing, type PreviewBlock, type ExampleBlock, type DocsEntity, type PageEntity, type LayoutEntity, type SdocEntity, type SdocDocument, } from './parser.js';
|
|
4
|
+
export { parseSdoc, parseArgsLiteral, slugifyTitle, normalizeBody, attributeRules, type AttrRule, type ArgValue, type Sizing, type PreviewBlock, type ExampleBlock, type DocsEntity, type PageEntity, type LayoutEntity, type SdocEntity, type SdocDocument, } from './parser.js';
|
package/dist/language/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { scanSdoc, offsetToPosition, ENTITY_KINDS, SUB_BLOCK_KINDS, } from './scanner.js';
|
|
2
2
|
export { projectSdoc, } from './projection.js';
|
|
3
3
|
export { segmentPageBody, } from './page-islands.js';
|
|
4
|
-
export { parseSdoc, parseArgsLiteral, slugifyTitle, normalizeBody, } from './parser.js';
|
|
4
|
+
export { parseSdoc, parseArgsLiteral, slugifyTitle, normalizeBody, attributeRules, } from './parser.js';
|
|
@@ -13,6 +13,10 @@ export interface Sizing {
|
|
|
13
13
|
direction: string | null;
|
|
14
14
|
/** gap of preview/example stages */
|
|
15
15
|
gap: string | null;
|
|
16
|
+
/** horizontal alignment of preview/example stage contents */
|
|
17
|
+
align: string | null;
|
|
18
|
+
/** vertical alignment of preview/example stage contents */
|
|
19
|
+
alignY: string | null;
|
|
16
20
|
/** table-of-contents visibility (PAGE) */
|
|
17
21
|
toc: boolean | null;
|
|
18
22
|
}
|
|
@@ -88,6 +92,17 @@ export interface SdocDocument {
|
|
|
88
92
|
export declare function normalizeBody(raw: string): string;
|
|
89
93
|
/** Slug used for entity addressing: relPath + '#' + slug. */
|
|
90
94
|
export declare function slugifyTitle(title: string): string;
|
|
95
|
+
export interface AttrRule {
|
|
96
|
+
/** required | optional */
|
|
97
|
+
required: boolean;
|
|
98
|
+
/** expected value kind */
|
|
99
|
+
kind: 'string' | 'expression';
|
|
100
|
+
hint: string;
|
|
101
|
+
}
|
|
102
|
+
/** Allowed attributes and their value shapes for a block, keyed by kind
|
|
103
|
+
* ('DOCS'|'PAGE'|'LAYOUT'|'preview'|'example'). Single source of truth for
|
|
104
|
+
* both diagnostics and editor attribute completions. */
|
|
105
|
+
export declare function attributeRules(kind: string): Record<string, AttrRule>;
|
|
91
106
|
/**
|
|
92
107
|
* Parse a preview args expression: a flat object literal whose values are
|
|
93
108
|
* plain literals (strings, numbers, booleans). Anything richer belongs in
|
package/dist/language/parser.js
CHANGED
|
@@ -50,6 +50,8 @@ const SIZING_ATTR_RULES = {
|
|
|
50
50
|
const STAGE_LAYOUT_ATTR_RULES = {
|
|
51
51
|
direction: { required: false, kind: 'string', hint: 'direction="column"' },
|
|
52
52
|
gap: { required: false, kind: 'string', hint: 'gap="16px"' },
|
|
53
|
+
align: { required: false, kind: 'string', hint: 'align="center"' },
|
|
54
|
+
alignY: { required: false, kind: 'string', hint: 'alignY="middle"' },
|
|
53
55
|
};
|
|
54
56
|
function sizingOf(attrs) {
|
|
55
57
|
const toc = stringAttr(attrs, 'toc');
|
|
@@ -58,6 +60,8 @@ function sizingOf(attrs) {
|
|
|
58
60
|
padding: stringAttr(attrs, 'padding'),
|
|
59
61
|
direction: stringAttr(attrs, 'direction'),
|
|
60
62
|
gap: stringAttr(attrs, 'gap'),
|
|
63
|
+
align: stringAttr(attrs, 'align'),
|
|
64
|
+
alignY: stringAttr(attrs, 'alignY'),
|
|
61
65
|
toc: toc === null ? null : toc === 'true',
|
|
62
66
|
};
|
|
63
67
|
}
|
|
@@ -92,6 +96,12 @@ const SUB_BLOCK_ATTR_RULES = {
|
|
|
92
96
|
...STAGE_LAYOUT_ATTR_RULES,
|
|
93
97
|
},
|
|
94
98
|
};
|
|
99
|
+
/** Allowed attributes and their value shapes for a block, keyed by kind
|
|
100
|
+
* ('DOCS'|'PAGE'|'LAYOUT'|'preview'|'example'). Single source of truth for
|
|
101
|
+
* both diagnostics and editor attribute completions. */
|
|
102
|
+
export function attributeRules(kind) {
|
|
103
|
+
return ENTITY_ATTR_RULES[kind] ?? SUB_BLOCK_ATTR_RULES[kind] ?? {};
|
|
104
|
+
}
|
|
95
105
|
function checkAttrs(owner, attrs, rules, ownerSpan, diagnostics) {
|
|
96
106
|
for (const [name, value] of Object.entries(attrs)) {
|
|
97
107
|
const rule = rules[name];
|
package/dist/server/config.js
CHANGED
|
@@ -15,7 +15,14 @@ const DEFAULTS = {
|
|
|
15
15
|
},
|
|
16
16
|
content: {
|
|
17
17
|
page: { maxWidth: '1200px', padding: '32px', toc: true },
|
|
18
|
-
docs: {
|
|
18
|
+
docs: {
|
|
19
|
+
maxWidth: '1200px',
|
|
20
|
+
padding: '16px',
|
|
21
|
+
direction: 'row',
|
|
22
|
+
gap: '16px',
|
|
23
|
+
align: 'left',
|
|
24
|
+
alignY: 'top',
|
|
25
|
+
},
|
|
19
26
|
layout: { maxWidth: '100%', padding: '0px' },
|
|
20
27
|
},
|
|
21
28
|
};
|
|
@@ -28,6 +28,8 @@ export declare function generateIframeComponent(scriptPrelude: string, snippetBo
|
|
|
28
28
|
padding: string;
|
|
29
29
|
direction?: string;
|
|
30
30
|
gap?: string;
|
|
31
|
+
align?: string;
|
|
32
|
+
alignY?: string;
|
|
31
33
|
}): string;
|
|
32
34
|
/** The JS that boots a preview page: mount the wrapper + parent-frame messaging. */
|
|
33
35
|
export declare function generateMountScript(iframeComponentId: string): string;
|
|
@@ -84,19 +84,46 @@ function injectRootRef(snippetBody, componentName) {
|
|
|
84
84
|
export function generateIframeComponent(scriptPrelude, snippetBody, stateNames = [], componentName, stage) {
|
|
85
85
|
// The stage layout (config -> entity -> block cascade) applies here, inside
|
|
86
86
|
// the iframe, so every consumer of the preview page gets it. Preview and
|
|
87
|
-
// example stages are flex containers (direction + gap); page and
|
|
88
|
-
// stages are flow-root blocks. Both contain child margins, so the
|
|
89
|
-
// reported for iframe auto-sizing is exact.
|
|
87
|
+
// example stages are flex containers (direction + gap + alignment); page and
|
|
88
|
+
// layout stages are flow-root blocks. Both contain child margins, so the
|
|
89
|
+
// height reported for iframe auto-sizing is exact.
|
|
90
|
+
//
|
|
91
|
+
// align/alignY are *physical* (horizontal/vertical); which flex property
|
|
92
|
+
// each drives depends on direction. The flow axis (main) takes
|
|
93
|
+
// justify-content, the other (cross) takes align-items — and align-items
|
|
94
|
+
// has no distribution value, so a 'justify' that lands on the cross axis
|
|
95
|
+
// falls back to stretch.
|
|
96
|
+
const H = {
|
|
97
|
+
left: 'flex-start',
|
|
98
|
+
center: 'center',
|
|
99
|
+
right: 'flex-end',
|
|
100
|
+
justify: 'space-between',
|
|
101
|
+
};
|
|
102
|
+
const V = {
|
|
103
|
+
top: 'flex-start',
|
|
104
|
+
middle: 'center',
|
|
105
|
+
bottom: 'flex-end',
|
|
106
|
+
justify: 'space-between',
|
|
107
|
+
};
|
|
108
|
+
const flexStage = () => {
|
|
109
|
+
const h = H[stage.align ?? 'left'] ?? 'flex-start';
|
|
110
|
+
const v = V[stage.alignY ?? 'top'] ?? 'flex-start';
|
|
111
|
+
const isColumn = String(stage.direction).startsWith('column');
|
|
112
|
+
const justify = isColumn ? v : h;
|
|
113
|
+
let alignItems = isColumn ? h : v;
|
|
114
|
+
if (alignItems === 'space-between')
|
|
115
|
+
alignItems = 'stretch';
|
|
116
|
+
return [
|
|
117
|
+
'display: flex',
|
|
118
|
+
`flex-direction: ${stage.direction}`,
|
|
119
|
+
'flex-wrap: wrap',
|
|
120
|
+
`justify-content: ${justify}`,
|
|
121
|
+
`align-items: ${alignItems}`,
|
|
122
|
+
`gap: ${stage.gap}`,
|
|
123
|
+
];
|
|
124
|
+
};
|
|
90
125
|
const stageStyle = [
|
|
91
|
-
...(stage?.direction
|
|
92
|
-
? [
|
|
93
|
-
'display: flex',
|
|
94
|
-
`flex-direction: ${stage.direction}`,
|
|
95
|
-
'flex-wrap: wrap',
|
|
96
|
-
'align-items: flex-start',
|
|
97
|
-
`gap: ${stage.gap}`,
|
|
98
|
-
]
|
|
99
|
-
: ['display: flow-root']),
|
|
126
|
+
...(stage?.direction ? flexStage() : ['display: flow-root']),
|
|
100
127
|
...(stage ? [`padding: ${stage.padding}`] : []),
|
|
101
128
|
...(stage && stage.maxWidth !== '100%'
|
|
102
129
|
? [`max-width: ${stage.maxWidth}`, 'margin-inline: auto']
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,10 @@ export interface SdocsConfig {
|
|
|
34
34
|
direction?: string;
|
|
35
35
|
/** Stage gap. Default: '16px' */
|
|
36
36
|
gap?: string;
|
|
37
|
+
/** Horizontal alignment of stage contents: 'left'|'center'|'right'|'justify'. Default: 'left' */
|
|
38
|
+
align?: string;
|
|
39
|
+
/** Vertical alignment of stage contents: 'top'|'middle'|'bottom'. Default: 'top' */
|
|
40
|
+
alignY?: string;
|
|
37
41
|
};
|
|
38
42
|
/** [LAYOUT] stages. Defaults: maxWidth '100%', padding '0px'. */
|
|
39
43
|
layout?: ContentSizing;
|
|
@@ -48,9 +52,13 @@ export interface ContentSizing {
|
|
|
48
52
|
export interface StageLayout {
|
|
49
53
|
maxWidth: string;
|
|
50
54
|
padding: string;
|
|
51
|
-
/** flex-direction + gap; set for preview/example stages only */
|
|
55
|
+
/** flex-direction + gap + align; set for preview/example stages only */
|
|
52
56
|
direction?: string;
|
|
53
57
|
gap?: string;
|
|
58
|
+
/** horizontal ('left'|'center'|'right'|'justify') — mapped by direction */
|
|
59
|
+
align?: string;
|
|
60
|
+
/** vertical ('top'|'middle'|'bottom') — mapped by direction */
|
|
61
|
+
alignY?: string;
|
|
54
62
|
}
|
|
55
63
|
/** Resolved config with all defaults applied */
|
|
56
64
|
export interface ResolvedSdocsConfig {
|
|
@@ -71,6 +79,8 @@ export interface ResolvedSdocsConfig {
|
|
|
71
79
|
docs: Required<ContentSizing> & {
|
|
72
80
|
direction: string;
|
|
73
81
|
gap: string;
|
|
82
|
+
align: string;
|
|
83
|
+
alignY: string;
|
|
74
84
|
};
|
|
75
85
|
layout: Required<ContentSizing>;
|
|
76
86
|
};
|
package/dist/vite.js
CHANGED
|
@@ -308,11 +308,13 @@ export function sdocsPlugin(userConfig) {
|
|
|
308
308
|
maxWidth: block?.maxWidth ??
|
|
309
309
|
(entity.kind === 'LAYOUT' ? (entity.sizing.maxWidth ?? kindDefaults.maxWidth) : '100%'),
|
|
310
310
|
padding: block?.padding ?? entity.sizing.padding ?? kindDefaults.padding,
|
|
311
|
-
// direction/gap flex the preview/example stages only
|
|
311
|
+
// direction/gap/align flex the preview/example stages only
|
|
312
312
|
...(entity.kind === 'DOCS' && block
|
|
313
313
|
? {
|
|
314
314
|
direction: block.direction ?? entity.sizing.direction ?? config.content.docs.direction,
|
|
315
315
|
gap: block.gap ?? entity.sizing.gap ?? config.content.docs.gap,
|
|
316
|
+
align: block.align ?? entity.sizing.align ?? config.content.docs.align,
|
|
317
|
+
alignY: block.alignY ?? entity.sizing.alignY ?? config.content.docs.alignY,
|
|
316
318
|
}
|
|
317
319
|
: {}),
|
|
318
320
|
});
|