sdocs-dev 1.15.0 → 1.18.0
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-dev.js +35 -11
- package/lib/agent-block.js +253 -50
- package/lib/agent-files.js +308 -74
- package/lib/cells-verify.js +1 -0
- package/lib/cloud-bindings.js +85 -0
- package/lib/cloud-commands.js +741 -0
- package/lib/cloud-credentials.js +380 -0
- package/lib/commands.js +44 -4
- package/lib/help-text.js +272 -70
- package/lib/io.js +44 -3
- package/lib/library-commands.js +6 -15
- package/lib/library-scan.js +16 -6
- package/lib/library-server.js +4 -12
- package/lib/setup.js +145 -189
- package/lib/slides-verify.js +109 -0
- package/package.json +1 -1
- package/shared/sdocs-cells-formula.js +547 -73
- package/shared/sdocs-cells.js +164 -18
- package/shared/sdocs-shapes.js +1044 -0
- package/shared/sdocs-slide-resolve.js +258 -0
- package/shared/sdocs-slide-stdlib.js +180 -0
- package/shared/sdocs-styles.js +1 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// sdocs-slide-resolve.js — template resolution for slide blocks (UMD).
|
|
2
|
+
// Shared by the browser slide pipeline and Node unit tests.
|
|
3
|
+
//
|
|
4
|
+
// Input: an array of raw slide DSL strings (the text between ~~~slide
|
|
5
|
+
// fences), in document order.
|
|
6
|
+
// Output: an array of { dsl, skip, errors } in the same order, where:
|
|
7
|
+
// - dsl: the DSL text to render (merged for consumers; unchanged
|
|
8
|
+
// for plain slides; the template body for template slides)
|
|
9
|
+
// - skip: true for @template slides (they register but don't render)
|
|
10
|
+
// - errors: [{ message }] of resolve-time problems (unknown
|
|
11
|
+
// template, malformed directive, etc.). Empty array when clean.
|
|
12
|
+
//
|
|
13
|
+
// Directives (must be the first non-blank line inside the fence):
|
|
14
|
+
// @template NAME — register this slide's DSL under NAME; don't render it.
|
|
15
|
+
// @extends NAME — replace slot contents in template NAME with the
|
|
16
|
+
// `#id: value` blocks that follow.
|
|
17
|
+
//
|
|
18
|
+
// Slot block syntax inside @extends bodies:
|
|
19
|
+
// #id: inline value (single-line)
|
|
20
|
+
// #id: (multi-line; content on following
|
|
21
|
+
// content line one lines, until the next `#id:` or EOF)
|
|
22
|
+
// content line two
|
|
23
|
+
//
|
|
24
|
+
// The resolver is deliberately pure — it takes strings, returns strings,
|
|
25
|
+
// and does not touch the DOM, parse shape geometry, or know anything
|
|
26
|
+
// about CSS. All heavy lifting (DSL parsing, rendering) happens downstream
|
|
27
|
+
// on the resolved text, so templates re-use every existing code path.
|
|
28
|
+
|
|
29
|
+
(function (exports) {
|
|
30
|
+
'use strict';
|
|
31
|
+
|
|
32
|
+
var DIRECTIVE_RE = /^\s*@(template|extends)\s+([A-Za-z][\w-]*)\s*$/;
|
|
33
|
+
// Consumer slot lines optionally accept a trailing `!` after the id - the
|
|
34
|
+
// required marker belongs in the template, but accepting it here means a
|
|
35
|
+
// copy-paste from a template definition doesn't silently break.
|
|
36
|
+
var SLOT_RE = /^#([A-Za-z][\w-]*)!?\s*:\s?(.*)$/;
|
|
37
|
+
|
|
38
|
+
function splitDirective(raw) {
|
|
39
|
+
var lines = String(raw == null ? '' : raw).split('\n');
|
|
40
|
+
var i = 0;
|
|
41
|
+
while (i < lines.length && lines[i].trim() === '') i++;
|
|
42
|
+
if (i >= lines.length) return { kind: null };
|
|
43
|
+
var m = lines[i].match(DIRECTIVE_RE);
|
|
44
|
+
if (!m) return { kind: null };
|
|
45
|
+
return {
|
|
46
|
+
kind: m[1],
|
|
47
|
+
name: m[2],
|
|
48
|
+
body: lines.slice(i + 1).join('\n'),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseSlots(body) {
|
|
53
|
+
var lines = body.split('\n');
|
|
54
|
+
var slots = {};
|
|
55
|
+
var currentId = null;
|
|
56
|
+
var buf = [];
|
|
57
|
+
var inline = false; // inline-only slot (body on the directive line)
|
|
58
|
+
for (var i = 0; i < lines.length; i++) {
|
|
59
|
+
var ln = lines[i];
|
|
60
|
+
var m = ln.match(SLOT_RE);
|
|
61
|
+
if (m) {
|
|
62
|
+
if (currentId !== null) slots[currentId] = finalizeBuf(buf, inline);
|
|
63
|
+
currentId = m[1];
|
|
64
|
+
// A bare `|` after the colon is YAML-style block-scalar sugar: the
|
|
65
|
+
// user signalled "multi-line body follows", same as an empty inline.
|
|
66
|
+
// Dropping it prevents the literal `|` from leaking into the rendered
|
|
67
|
+
// markdown as a stray paragraph.
|
|
68
|
+
var inlineVal = m[2] === '|' ? '' : m[2];
|
|
69
|
+
inline = inlineVal.length > 0;
|
|
70
|
+
buf = inlineVal ? [inlineVal] : [];
|
|
71
|
+
} else if (currentId !== null) {
|
|
72
|
+
buf.push(ln);
|
|
73
|
+
inline = false; // as soon as we collect body lines, treat as block
|
|
74
|
+
}
|
|
75
|
+
// Lines before the first #id: are ignored (allows authors to leave
|
|
76
|
+
// notes or blank space between @extends and the first slot).
|
|
77
|
+
}
|
|
78
|
+
if (currentId !== null) slots[currentId] = finalizeBuf(buf, inline);
|
|
79
|
+
return slots;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Inline slots (single-line `#id: value`) round-trip as-is. Block slots get
|
|
83
|
+
// trailing blank lines trimmed and common leading indent stripped, so an
|
|
84
|
+
// author who wrote:
|
|
85
|
+
//
|
|
86
|
+
// #body:
|
|
87
|
+
// - one
|
|
88
|
+
// - two
|
|
89
|
+
//
|
|
90
|
+
// gets `- one\n- two` in their shape content (not ` - one\n - two`, which
|
|
91
|
+
// markdown may render with an extra indent level or misread as code).
|
|
92
|
+
function finalizeBuf(lines, isInline) {
|
|
93
|
+
if (isInline) return lines.join('\n');
|
|
94
|
+
return dedent(trimTrailingBlank(lines)).join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function trimTrailingBlank(lines) {
|
|
98
|
+
var end = lines.length;
|
|
99
|
+
while (end > 0 && lines[end - 1].trim() === '') end--;
|
|
100
|
+
return lines.slice(0, end);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function dedent(lines) {
|
|
104
|
+
var min = Infinity;
|
|
105
|
+
for (var i = 0; i < lines.length; i++) {
|
|
106
|
+
if (lines[i].trim() === '') continue;
|
|
107
|
+
var lead = /^ */.exec(lines[i])[0].length;
|
|
108
|
+
if (lead < min) min = lead;
|
|
109
|
+
}
|
|
110
|
+
if (!isFinite(min) || min === 0) return lines;
|
|
111
|
+
return lines.map(function (l) {
|
|
112
|
+
return l.length >= min ? l.slice(min) : l;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Merge the template's shapes with the consumer's slots by #id match.
|
|
117
|
+
// Relies on the SDocShapes parse/serialize pair, so any DSL the parser
|
|
118
|
+
// accepts round-trips correctly. Shapes whose id doesn't appear in the
|
|
119
|
+
// consumer's slots keep their template-provided content — that's how
|
|
120
|
+
// "partial fills render the layout's placeholder" works.
|
|
121
|
+
function mergeTemplate(templateDsl, slots, SDocShapes) {
|
|
122
|
+
var parsed = SDocShapes.parse(templateDsl);
|
|
123
|
+
var shapes = parsed.shapes.map(function (s) {
|
|
124
|
+
if (s.id && Object.prototype.hasOwnProperty.call(slots, s.id)) {
|
|
125
|
+
var copy = {};
|
|
126
|
+
for (var k in s) if (Object.prototype.hasOwnProperty.call(s, k)) copy[k] = s[k];
|
|
127
|
+
copy.content = slots[s.id];
|
|
128
|
+
return copy;
|
|
129
|
+
}
|
|
130
|
+
return s;
|
|
131
|
+
});
|
|
132
|
+
return SDocShapes.serialize(shapes, parsed.grid);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function resolveSlides(rawDsls, SDocShapes, opts) {
|
|
136
|
+
if (!SDocShapes) {
|
|
137
|
+
throw new Error('resolveSlides: SDocShapes is required');
|
|
138
|
+
}
|
|
139
|
+
opts = opts || {};
|
|
140
|
+
var stdlib = opts.stdlib || {};
|
|
141
|
+
|
|
142
|
+
var templates = {};
|
|
143
|
+
var shadowSlideByName = {};
|
|
144
|
+
var parsed = rawDsls.map(splitDirective);
|
|
145
|
+
|
|
146
|
+
// Pass 1: register every user @template. Doing this ahead of pass 2
|
|
147
|
+
// means author ordering in the document is unconstrained - a consumer
|
|
148
|
+
// can appear before its template. A user template shadowing a stdlib
|
|
149
|
+
// name is allowed but flagged on the template's own slide entry, so
|
|
150
|
+
// accidental shadowing is visible at render time.
|
|
151
|
+
for (var i = 0; i < parsed.length; i++) {
|
|
152
|
+
var p = parsed[i];
|
|
153
|
+
if (p.kind === 'template') {
|
|
154
|
+
templates[p.name] = p.body;
|
|
155
|
+
if (Object.prototype.hasOwnProperty.call(stdlib, p.name)) {
|
|
156
|
+
shadowSlideByName[p.name] = i;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Pass 2: build a result per slide.
|
|
162
|
+
return parsed.map(function (p, idx) {
|
|
163
|
+
var raw = rawDsls[idx];
|
|
164
|
+
if (p.kind === 'template') {
|
|
165
|
+
var tErrs = [];
|
|
166
|
+
if (shadowSlideByName[p.name] === idx) {
|
|
167
|
+
tErrs.push({
|
|
168
|
+
line: 1,
|
|
169
|
+
message: 'template "' + p.name + '" shadows the stdlib template of the same name',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return { dsl: p.body, skip: true, errors: tErrs };
|
|
173
|
+
}
|
|
174
|
+
if (p.kind === 'extends') {
|
|
175
|
+
var tplBody = Object.prototype.hasOwnProperty.call(templates, p.name)
|
|
176
|
+
? templates[p.name]
|
|
177
|
+
: (Object.prototype.hasOwnProperty.call(stdlib, p.name) ? stdlib[p.name] : null);
|
|
178
|
+
if (!tplBody) {
|
|
179
|
+
return {
|
|
180
|
+
dsl: raw,
|
|
181
|
+
skip: false,
|
|
182
|
+
errors: [{ line: 1, message: 'unknown template "' + p.name + '"' }],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
var slots = parseSlots(p.body);
|
|
187
|
+
var errors = [];
|
|
188
|
+
|
|
189
|
+
// Parse the template once so we can introspect shape ids + required
|
|
190
|
+
// markers. The same parse work happens inside mergeTemplate; we eat
|
|
191
|
+
// the duplication for clarity (resolver stays pure DSL-string-in,
|
|
192
|
+
// DSL-string-out at the boundary).
|
|
193
|
+
var parsedTpl;
|
|
194
|
+
try {
|
|
195
|
+
parsedTpl = SDocShapes.parse(tplBody);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return {
|
|
198
|
+
dsl: raw,
|
|
199
|
+
skip: false,
|
|
200
|
+
errors: [{ line: 1, message: 'template parse failed: ' + e.message }],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
var templateIds = {};
|
|
205
|
+
for (var s = 0; s < parsedTpl.shapes.length; s++) {
|
|
206
|
+
var sid = parsedTpl.shapes[s].id;
|
|
207
|
+
if (sid) templateIds[sid] = parsedTpl.shapes[s];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Unknown-slot check: consumer named a slot the template doesn't
|
|
211
|
+
// define. Silently no-op was the #1 cause of "template feels
|
|
212
|
+
// broken" confusion - surface it as an error per the DSL-design
|
|
213
|
+
// review.
|
|
214
|
+
for (var slotName in slots) {
|
|
215
|
+
if (!Object.prototype.hasOwnProperty.call(slots, slotName)) continue;
|
|
216
|
+
if (!Object.prototype.hasOwnProperty.call(templateIds, slotName)) {
|
|
217
|
+
errors.push({
|
|
218
|
+
line: 1,
|
|
219
|
+
message: 'unknown slot "#' + slotName + '" (template "' + p.name + '" has no shape with that id)',
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Required-slot check: template marked the shape with #id! but
|
|
225
|
+
// the consumer didn't pass that slot.
|
|
226
|
+
for (var tid in templateIds) {
|
|
227
|
+
if (!Object.prototype.hasOwnProperty.call(templateIds, tid)) continue;
|
|
228
|
+
var shp = templateIds[tid];
|
|
229
|
+
if (shp.required && !Object.prototype.hasOwnProperty.call(slots, tid)) {
|
|
230
|
+
errors.push({
|
|
231
|
+
line: 1,
|
|
232
|
+
message: 'missing required slot "#' + tid + '" for template "' + p.name + '"',
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
var merged;
|
|
238
|
+
try {
|
|
239
|
+
merged = mergeTemplate(tplBody, slots, SDocShapes);
|
|
240
|
+
} catch (e) {
|
|
241
|
+
return {
|
|
242
|
+
dsl: raw,
|
|
243
|
+
skip: false,
|
|
244
|
+
errors: [{ line: 1, message: 'template merge failed: ' + e.message }],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return { dsl: merged, skip: false, errors: errors };
|
|
248
|
+
}
|
|
249
|
+
// Plain slide - untouched.
|
|
250
|
+
return { dsl: raw, skip: false, errors: [] };
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
exports.resolveSlides = resolveSlides;
|
|
255
|
+
exports.splitDirective = splitDirective;
|
|
256
|
+
exports.parseSlots = parseSlots;
|
|
257
|
+
|
|
258
|
+
})(typeof module !== 'undefined' && module.exports ? module.exports : (window.SDocSlideResolve = {}));
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// sdocs-slide-stdlib.js - built-in slide templates (UMD).
|
|
2
|
+
//
|
|
3
|
+
// Each entry is a DSL string the resolver registers under the given name,
|
|
4
|
+
// alongside user `@template` definitions. User-defined templates shadow
|
|
5
|
+
// stdlib names (with a warning surfaced through the slide error badge),
|
|
6
|
+
// so authors can override any built-in without touching this file.
|
|
7
|
+
//
|
|
8
|
+
// Design constants applied across every template:
|
|
9
|
+
// - 1-unit safe margin on all four sides of a 16x9 grid (roughly 6%
|
|
10
|
+
// horizontal, 5-7% vertical, matching the standard slide safe area).
|
|
11
|
+
// - No shape fill colours for shapes containing title/body text. The
|
|
12
|
+
// section divider uses `grid bg=` for full-bleed contrast instead.
|
|
13
|
+
// - Title role (64px) reserved for cover + quote + section. In-deck
|
|
14
|
+
// content slides use subtitle role (40px) so action titles can wrap
|
|
15
|
+
// to two lines without filling half the page.
|
|
16
|
+
// - Caption role (14px) reserved for eyebrows / footers / attributions
|
|
17
|
+
// - never for load-bearing content (it renders as ~3px in a 240px
|
|
18
|
+
// thumbnail, illegible).
|
|
19
|
+
// - Optional slots default to empty content, so omitting them renders
|
|
20
|
+
// nothing visible. Required slots (#name!) emit a resolver error
|
|
21
|
+
// when the consumer doesn't fill them.
|
|
22
|
+
//
|
|
23
|
+
// All templates assume a 16x9 grid. Authors who need a different aspect
|
|
24
|
+
// ratio should copy a template into a user `@template` and edit the grid.
|
|
25
|
+
|
|
26
|
+
(function (exports) {
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
var TEMPLATES = {
|
|
30
|
+
|
|
31
|
+
// Opening slide. Once per deck, sets the tone before anything else.
|
|
32
|
+
cover: [
|
|
33
|
+
'grid 16 9',
|
|
34
|
+
'r 1 1 14 0.7 #eyebrow text=caption color=$color opacity=0.6 align=left |',
|
|
35
|
+
'r 1 3.2 14 2.2 #title! text=title align=left | (required: cover title)',
|
|
36
|
+
'r 1 5.8 14 1.0 #subtitle text=subtitle color=$color opacity=0.75 align=left |',
|
|
37
|
+
'r 1 7.7 14 0.6 #meta text=caption color=$color opacity=0.6 align=left |',
|
|
38
|
+
].join('\n'),
|
|
39
|
+
|
|
40
|
+
// The workhorse. 60-70% of body slides. Title at top, body filling the
|
|
41
|
+
// safe area below, optional footer for source / page / context. Title
|
|
42
|
+
// uses subtitle role so an action title can wrap to two lines without
|
|
43
|
+
// crowding the body.
|
|
44
|
+
'title-body': [
|
|
45
|
+
'grid 16 9',
|
|
46
|
+
'r 1 0.7 14 1.1 #title! text=subtitle align=left | (required: slide title)',
|
|
47
|
+
'r 1 2.2 14 5.7 #body! text=body align=left valign=top | (required: slide body)',
|
|
48
|
+
'r 1 8.1 14 0.4 #footer text=caption color=$color opacity=0.55 align=left |',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
|
|
51
|
+
// Two equal columns with a 1-unit gutter, both bodies anchored top so
|
|
52
|
+
// matched-length content reads as parallel. Optional column headers in
|
|
53
|
+
// caption role above each body.
|
|
54
|
+
'two-column': [
|
|
55
|
+
'grid 16 9',
|
|
56
|
+
'r 1 0.7 14 1.1 #title! text=subtitle align=left | (required: slide title)',
|
|
57
|
+
'r 1 2.4 6.5 0.5 #left-header text=caption color=$color opacity=0.75 align=left |',
|
|
58
|
+
'r 1 3.0 6.5 5.0 #left! text=body align=left valign=top | (required: left column)',
|
|
59
|
+
'r 8.5 2.4 6.5 0.5 #right-header text=caption color=$color opacity=0.75 align=left |',
|
|
60
|
+
'r 8.5 3.0 6.5 5.0 #right! text=body align=left valign=top | (required: right column)',
|
|
61
|
+
].join('\n'),
|
|
62
|
+
|
|
63
|
+
// Three-way compare. A/B/C variants, before/during/after, three
|
|
64
|
+
// perspectives on the same question. Three equal columns (4.3 wide
|
|
65
|
+
// each) separated by 0.55-unit gutters, optional headers above each.
|
|
66
|
+
// Bias to keeping all three columns roughly the same length - if one
|
|
67
|
+
// is half-empty, drop it and use two-column.
|
|
68
|
+
'three-column': [
|
|
69
|
+
'grid 16 9',
|
|
70
|
+
'r 1 0.7 14 1.1 #title! text=subtitle align=left | (required: slide title)',
|
|
71
|
+
'r 1 2.4 4.3 0.5 #left-header text=caption color=$color opacity=0.75 align=left |',
|
|
72
|
+
'r 1 3.0 4.3 5.0 #left! text=body align=left valign=top | (required: left column)',
|
|
73
|
+
'r 5.85 2.4 4.3 0.5 #mid-header text=caption color=$color opacity=0.75 align=left |',
|
|
74
|
+
'r 5.85 3.0 4.3 5.0 #mid! text=body align=left valign=top | (required: middle column)',
|
|
75
|
+
'r 10.7 2.4 4.3 0.5 #right-header text=caption color=$color opacity=0.75 align=left |',
|
|
76
|
+
'r 10.7 3.0 4.3 5.0 #right! text=body align=left valign=top | (required: right column)',
|
|
77
|
+
].join('\n'),
|
|
78
|
+
|
|
79
|
+
// Exhibit. Chart on the left (~56% wide), takeaway column on the
|
|
80
|
+
// right (~28% wide). The chart is the evidence; the takeaway tells
|
|
81
|
+
// the audience what to see. Optional source caption at the bottom.
|
|
82
|
+
// Reserve for business decks where the audience needs a verbal
|
|
83
|
+
// handle on the chart under time pressure - if your audience can
|
|
84
|
+
// read the chart themselves in 5 seconds, use figure-hero instead.
|
|
85
|
+
exhibit: [
|
|
86
|
+
'grid 16 9',
|
|
87
|
+
'r 1 0.7 14 1.1 #title! text=subtitle align=left | (required: action title)',
|
|
88
|
+
'r 1 2 9 6 #chart! align=center valign=center | (required:  or chart)',
|
|
89
|
+
'r 10.5 2 4.5 6 #takeaway! text=body align=left valign=center | (required: takeaway)',
|
|
90
|
+
'r 1 8.1 14 0.4 #source text=caption color=$color opacity=0.55 align=left |',
|
|
91
|
+
].join('\n'),
|
|
92
|
+
|
|
93
|
+
// Image-and-text. Image fills the left half of the safe area, body
|
|
94
|
+
// sits on the right. The image slot accepts markdown image syntax:
|
|
95
|
+
// `#image: `. Optional small title above both columns.
|
|
96
|
+
'image-and-text': [
|
|
97
|
+
'grid 16 9',
|
|
98
|
+
'r 1 0.7 14 0.9 #title text=caption color=$color opacity=0.75 align=left |',
|
|
99
|
+
'r 1 1.9 7.5 6.2 #image! align=center valign=center | (required: )',
|
|
100
|
+
'r 9 1.9 6 6.2 #body! text=body align=left valign=top | (required: body)',
|
|
101
|
+
].join('\n'),
|
|
102
|
+
|
|
103
|
+
// Image-dominant slide. The figure carries the slide; a small
|
|
104
|
+
// caption sits below. Use when the image IS the argument (a chart,
|
|
105
|
+
// a screenshot, a product shot, a photo). If you want supporting
|
|
106
|
+
// body text alongside the image, use `image-and-text` instead.
|
|
107
|
+
'figure-hero': [
|
|
108
|
+
'grid 16 9',
|
|
109
|
+
'r 1 0.6 14 7.0 #image! align=center valign=center | (required: )',
|
|
110
|
+
'r 1 7.9 14 0.6 #caption text=caption color=$color opacity=0.75 align=center valign=top |',
|
|
111
|
+
].join('\n'),
|
|
112
|
+
|
|
113
|
+
// Single big idea. Quote, customer voice, or a callout sentence that
|
|
114
|
+
// deserves the whole slide. Lead is centered both ways so short content
|
|
115
|
+
// sits visually balanced rather than top-left adrift.
|
|
116
|
+
quote: [
|
|
117
|
+
'grid 16 9',
|
|
118
|
+
'r 2 2 12 4 #lead! text=title align=center valign=center | (required: the lead)',
|
|
119
|
+
'r 2 6.5 12 0.7 #attribution text=caption color=$color opacity=0.6 align=center valign=top |',
|
|
120
|
+
].join('\n'),
|
|
121
|
+
|
|
122
|
+
// One big number + a line of context. The hero metric slide.
|
|
123
|
+
// `size=fit` lets the number balloon to fill its shape, so a 3-char
|
|
124
|
+
// value (`87%`) lands much larger than a long one (`$4,231,889`).
|
|
125
|
+
// `maxfont=300px` lifts the default 12%-of-stage cap so the number
|
|
126
|
+
// can actually feel hero-sized; without it, autofit would cap at
|
|
127
|
+
// ~86px on a 720-tall stage and the slide reads as "a small number
|
|
128
|
+
// floating in a big box" rather than "stop the room".
|
|
129
|
+
// Use sparingly - one metric slide per deck is the rule, not three.
|
|
130
|
+
metric: [
|
|
131
|
+
'grid 16 9',
|
|
132
|
+
'r 1 1.5 14 4.8 #metric! size=fit maxfont=300px align=center valign=center | (required: the number)',
|
|
133
|
+
'r 1 6.7 14 1.4 #context text=body color=$color opacity=0.75 align=center valign=top |',
|
|
134
|
+
].join('\n'),
|
|
135
|
+
|
|
136
|
+
// Section divider. The one template where the slide background gets a
|
|
137
|
+
// saturated fill - the contrast against content slides is the point.
|
|
138
|
+
// Text shapes themselves have no fill (Consultant-2 rule preserved);
|
|
139
|
+
// the grid's bg= provides the colour underneath.
|
|
140
|
+
section: [
|
|
141
|
+
'grid 16 9 bg=#0f172a',
|
|
142
|
+
'r 1 1 14 0.7 #kicker text=caption color=#94a3b8 align=left |',
|
|
143
|
+
'r 1 3 14 3 #title! text=title color=#f8fafc align=left valign=center | (required: section title)',
|
|
144
|
+
'r 1 6 14 1 #subtitle text=subtitle color=#cbd5e1 align=left valign=center |',
|
|
145
|
+
].join('\n'),
|
|
146
|
+
|
|
147
|
+
// Closing slide. Symmetric bookend with `cover`. Center-aligned and
|
|
148
|
+
// minimal - one quiet message + optional contact line. Don't write
|
|
149
|
+
// "Thanks for listening" here; pick something the audience will
|
|
150
|
+
// remember instead.
|
|
151
|
+
closing: [
|
|
152
|
+
'grid 16 9',
|
|
153
|
+
'r 1 3.5 14 2.0 #lead! text=subtitle align=center valign=center | (required: closing line)',
|
|
154
|
+
'r 1 6 14 0.6 #contact text=caption color=$color opacity=0.6 align=center valign=center |',
|
|
155
|
+
].join('\n'),
|
|
156
|
+
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Per-template slot summary for `sdoc slides list` and the resolver's
|
|
160
|
+
// unknown-slot check. Required slots end with `!` here too, matching the
|
|
161
|
+
// DSL marker. Order matters - it controls how the listing reads.
|
|
162
|
+
var SLOT_DOCS = {
|
|
163
|
+
cover: ['eyebrow', 'title!', 'subtitle', 'meta'],
|
|
164
|
+
'title-body': ['title!', 'body!', 'footer'],
|
|
165
|
+
'two-column': ['title!', 'left-header', 'left!', 'right-header', 'right!'],
|
|
166
|
+
'three-column': ['title!', 'left-header', 'left!', 'mid-header', 'mid!', 'right-header', 'right!'],
|
|
167
|
+
exhibit: ['title!', 'chart!', 'takeaway!', 'source'],
|
|
168
|
+
'image-and-text':['title', 'image!', 'body!'],
|
|
169
|
+
'figure-hero': ['image!', 'caption'],
|
|
170
|
+
quote: ['lead!', 'attribution'],
|
|
171
|
+
metric: ['metric!', 'context'],
|
|
172
|
+
section: ['kicker', 'title!', 'subtitle'],
|
|
173
|
+
closing: ['lead!', 'contact'],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
exports.templates = TEMPLATES;
|
|
177
|
+
exports.slots = SLOT_DOCS;
|
|
178
|
+
exports.names = Object.keys(TEMPLATES);
|
|
179
|
+
|
|
180
|
+
})(typeof module !== 'undefined' && module.exports ? module.exports : (window.SDocSlideStdlib = {}));
|
package/shared/sdocs-styles.js
CHANGED
|
@@ -536,7 +536,7 @@ var STYLE_DEFAULTS = {
|
|
|
536
536
|
p: { lineHeight: 1.75, marginBottom: 1.1 },
|
|
537
537
|
link: { color: '#2563eb', decoration: 'underline' },
|
|
538
538
|
code: { font: 'JetBrains Mono' },
|
|
539
|
-
blockquote: { borderColor: '#2563eb', borderWidth: 3, fontSize: 1.
|
|
539
|
+
blockquote: { borderColor: '#2563eb', borderWidth: 3, fontSize: 1.05 },
|
|
540
540
|
list: { spacing: 0.3, indent: 1.6 },
|
|
541
541
|
};
|
|
542
542
|
|