@aivorynet/slaide 1.0.3 → 1.0.5
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 +28 -1
- package/README.md +1 -1
- package/dist/assets.d.ts +1 -0
- package/dist/assets.js +3 -0
- package/dist/assets.js.map +1 -1
- package/dist/compiler/compile.d.ts +1 -0
- package/dist/compiler/compile.js +3 -1
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/markdown.d.ts +1 -0
- package/dist/compiler/markdown.js +2 -1
- package/dist/compiler/markdown.js.map +1 -1
- package/dist/desktop/bootstrap.js +19 -5
- package/dist/desktop/bootstrap.js.map +1 -1
- package/dist/desktop/verify.d.ts +5 -4
- package/dist/desktop/verify.js +5 -4
- package/dist/desktop/verify.js.map +1 -1
- package/dist/index.js +4 -7
- package/dist/index.js.map +1 -1
- package/dist/master-io.js +12 -0
- package/dist/master-io.js.map +1 -1
- package/dist/parser/parse.d.ts +1 -0
- package/dist/parser/parse.js +3 -2
- package/dist/parser/parse.js.map +1 -1
- package/dist/render/charts.js +11 -6
- package/dist/render/charts.js.map +1 -1
- package/dist/render/css.js +5 -2
- package/dist/render/css.js.map +1 -1
- package/dist/render/runtime.js +70 -23
- package/dist/render/runtime.js.map +1 -1
- package/dist/vocab.d.ts +36 -0
- package/dist/vocab.js +77 -0
- package/dist/vocab.js.map +1 -0
- package/package.json +1 -1
- package/skills/slaide/SKILL.md +2 -2
- package/skills/slaide/grammar.md +183 -0
- package/skills/slaide/reference.md +58 -2
- package/skills/slaide/themes.md +23 -8
package/dist/render/charts.js
CHANGED
|
@@ -36,13 +36,17 @@ export const CHARTS_BOOT_JS = String.raw `
|
|
|
36
36
|
// deck's CSS custom properties so charts come out on-brand automatically.
|
|
37
37
|
function ensure(engine){
|
|
38
38
|
if(booted[engine]) return booted[engine];
|
|
39
|
-
var
|
|
40
|
-
if(!
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
var global = engine==='echart' ? window.echarts : engine==='mermaid' ? window.mermaid : null;
|
|
40
|
+
if(!global){
|
|
41
|
+
var tag = document.getElementById('sl-'+engine+'-lib');
|
|
42
|
+
if(!tag) return null;
|
|
43
|
+
try{ (0,eval)(b64decode(tag.textContent)); }
|
|
44
|
+
catch(e){ console.error('slaide: failed to boot '+engine, e); return null; }
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
if(engine==='mermaid' && window.mermaid){
|
|
45
|
-
var
|
|
48
|
+
var mSlide = document.querySelector('.sl-slide');
|
|
49
|
+
var font = cssVar('--font-sans','') || (mSlide ? getComputedStyle(mSlide).fontFamily : '') || 'system-ui, sans-serif';
|
|
46
50
|
window.mermaid.initialize({
|
|
47
51
|
startOnLoad:false, securityLevel:'strict', theme:'base', fontFamily:font,
|
|
48
52
|
themeVariables:{
|
|
@@ -66,7 +70,8 @@ export const CHARTS_BOOT_JS = String.raw `
|
|
|
66
70
|
if(accent && colors.indexOf(accent)<0) colors = [accent].concat(colors);
|
|
67
71
|
var text = cssVar('--color-text','#ffffff');
|
|
68
72
|
var muted = cssVar('--color-muted','#8B93A7');
|
|
69
|
-
var
|
|
73
|
+
var slideEl = document.querySelector('.sl-slide');
|
|
74
|
+
var font = cssVar('--font-sans','') || (slideEl ? getComputedStyle(slideEl).fontFamily : '') || 'system-ui, sans-serif';
|
|
70
75
|
var axis = {
|
|
71
76
|
axisLine:{lineStyle:{color:muted}},
|
|
72
77
|
axisTick:{lineStyle:{color:muted}},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"charts.js","sourceRoot":"","sources":["../../src/render/charts.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,sCAAsC;AACtC,kFAAkF;AAClF,EAAE;AACF,yFAAyF;AACzF,yFAAyF;AACzF,0FAA0F;AAC1F,wFAAwF;AACxF,yFAAyF;AACzF,oDAAoD;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,oFAAoF;AACpF,mEAAmE;AACnE,uFAAuF;AACvF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"charts.js","sourceRoot":"","sources":["../../src/render/charts.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,sCAAsC;AACtC,kFAAkF;AAClF,EAAE;AACF,yFAAyF;AACzF,yFAAyF;AACzF,0FAA0F;AAC1F,wFAAwF;AACxF,yFAAyF;AACzF,oDAAoD;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,oFAAoF;AACpF,mEAAmE;AACnE,uFAAuF;AACvF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8JvC,CAAC"}
|
package/dist/render/css.js
CHANGED
|
@@ -161,7 +161,10 @@ body{
|
|
|
161
161
|
background:rgba(0,0,0,.3);color:#fff;opacity:.26;cursor:pointer;font:16px/1 var(--font-sans,system-ui);
|
|
162
162
|
display:grid;place-items:center;backdrop-filter:blur(6px);transition:opacity .2s;}
|
|
163
163
|
.sl-present-toggle:hover{opacity:.85;}
|
|
164
|
-
body.sl-presenting .sl-present-toggle,body.
|
|
164
|
+
body.sl-presenting .sl-present-toggle,body.slv-presenting .sl-present-toggle,
|
|
165
|
+
body.sl-presenting .sl-counter,body.slv-presenting .sl-counter,
|
|
166
|
+
body.sl-presenting .sl-progress,body.slv-presenting .sl-progress{opacity:0;pointer-events:none;}
|
|
167
|
+
body.sl-presenting .sl-stage,body.slv-presenting .sl-stage{box-shadow:none;}
|
|
165
168
|
@media print{.sl-present-toggle{display:none;}}
|
|
166
169
|
|
|
167
170
|
/* ---- builds + transitions live in anim.ts (appended to BASE_CSS below) ---- */
|
|
@@ -170,7 +173,7 @@ body.sl-presenting .sl-present-toggle,body.sl-presenting .sl-counter{opacity:0;p
|
|
|
170
173
|
.sl-progress{position:fixed;left:0;bottom:0;height:3px;background:var(--color-accent,#6cf);width:0;z-index:50;transition:width .3s ease;}
|
|
171
174
|
.sl-counter{position:fixed;right:14px;bottom:12px;font:600 13px/1 var(--font-sans,system-ui);color:#fff;opacity:.35;z-index:50;background:rgba(0,0,0,.3);padding:5px 9px;border-radius:20px;backdrop-filter:blur(6px);}
|
|
172
175
|
.sl-counter:hover{opacity:.8;}
|
|
173
|
-
.sl-notes{position:fixed;left:
|
|
176
|
+
.sl-notes{position:fixed;left:var(--sl-dock-left,0px);right:var(--sl-dock-right,0px);bottom:var(--sl-dock-bottom,0px);height:180px;overflow:auto;background:rgba(10,10,16,.94);color:#ddd;padding:16px 22px;font:15px/1.6 var(--font-sans,system-ui);z-index:60;border-top:2px solid var(--color-accent,#6cf);display:none;box-sizing:border-box;}
|
|
174
177
|
.sl-notes.sl-open{display:block;}
|
|
175
178
|
.sl-help{position:fixed;inset:0;display:none;place-items:center;background:rgba(5,5,10,.82);z-index:70;}
|
|
176
179
|
.sl-help.sl-open{display:grid;}
|
package/dist/render/css.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/render/css.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzE,OAAO,WAAW,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,MAAM,CAAC,KAAK,qBAAqB,EAAE,CAAC,MAAM,CAAC,MAAM,0BAA0B,EAAE,CAAC,WAAW,CAAC,QAAQ,QAAQ,CAAC;AACpK,CAAC;AAED,MAAM,UAAU,GAAG
|
|
1
|
+
{"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/render/css.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzE,OAAO,WAAW,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,MAAM,CAAC,KAAK,qBAAqB,EAAE,CAAC,MAAM,CAAC,MAAM,0BAA0B,EAAE,CAAC,WAAW,CAAC,QAAQ,QAAQ,CAAC;AACpK,CAAC;AAED,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgLlB,CAAC;AAEF,qFAAqF;AACrF,gFAAgF;AAChF,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,YAAY,EAAE,GAAG,IAAI,GAAG,WAAW,EAAE,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAAC"}
|
package/dist/render/runtime.js
CHANGED
|
@@ -7,10 +7,12 @@ export const RUNTIME_JS = String.raw `
|
|
|
7
7
|
var stage = document.querySelector('.sl-stage');
|
|
8
8
|
if(!stage) return;
|
|
9
9
|
var vp = document.querySelector('.sl-viewport');
|
|
10
|
-
|
|
10
|
+
// Scope to the stage: a host (web editor) may clone .sl-slide into a filmstrip elsewhere in the
|
|
11
|
+
// document; collecting from the whole document would pollute the count/index and navigation.
|
|
12
|
+
var slides = Array.prototype.slice.call(stage.querySelectorAll('.sl-slide'));
|
|
11
13
|
var CW = parseFloat(stage.dataset.w)||1280, CH = parseFloat(stage.dataset.h)||720;
|
|
12
14
|
var notes = (window.__SLAIDE_NOTES__)||[];
|
|
13
|
-
var cur = 0, step = 0, busy = false;
|
|
15
|
+
var cur = 0, step = 0, busy = false, pendingNav = null;
|
|
14
16
|
var zoomFactor = 1, panX = 0, panY = 0;
|
|
15
17
|
// When false, taps don't advance and drag-pan is off (for interactive embeds,
|
|
16
18
|
// kiosk mode, or a host that drives navigation itself). Default: on.
|
|
@@ -40,20 +42,20 @@ export const RUNTIME_JS = String.raw `
|
|
|
40
42
|
panX = Math.max(-ox, Math.min(ox, panX));
|
|
41
43
|
panY = Math.max(-oy, Math.min(oy, panY));
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
// via the --sl-dock-left / --sl-dock-right CSS vars; the stage centers in the *remaining*
|
|
45
|
-
// width and shifts right by the left gutter. 0 (unset) means full-bleed, as before.
|
|
46
|
-
function dockVar(name){
|
|
47
|
-
var v = getComputedStyle(document.documentElement).getPropertyValue(name);
|
|
48
|
-
var n = parseFloat(v); return isFinite(n) ? n : 0;
|
|
49
|
-
}
|
|
45
|
+
function cssVar(cs, name){ var n = parseFloat(cs.getPropertyValue(name)); return isFinite(n) ? n : 0; }
|
|
50
46
|
function scale(){
|
|
51
|
-
var
|
|
52
|
-
var
|
|
47
|
+
var pres = isPresenting();
|
|
48
|
+
var cs = getComputedStyle(document.documentElement);
|
|
49
|
+
var dl = pres ? 0 : cssVar(cs,'--sl-dock-left'), dr = pres ? 0 : cssVar(cs,'--sl-dock-right');
|
|
50
|
+
var dt = pres ? 0 : cssVar(cs,'--sl-dock-top');
|
|
51
|
+
// Bottom dock = chrome dock (bstrip/filmstrip, absolute) + notes reservation (independent), so
|
|
52
|
+
// the two writers never clobber each other. .sl-notes sits at --sl-dock-bottom (above the chrome).
|
|
53
|
+
var db = pres ? 0 : cssVar(cs,'--sl-dock-bottom') + cssVar(cs,'--sl-dock-notes');
|
|
54
|
+
var vw = window.innerWidth - dl - dr, vh = window.innerHeight - dt - db;
|
|
53
55
|
var fit = Math.min(vw/CW, vh/CH);
|
|
54
56
|
var s = fit * zoomFactor;
|
|
55
57
|
if(zoomFactor<=1){ panX = panY = 0; } else { clampPan(s, vw, vh); }
|
|
56
|
-
var tx = dl + (vw - CW*s)/2 + panX, ty = (vh - CH*s)/2 + panY;
|
|
58
|
+
var tx = dl + (vw - CW*s)/2 + panX, ty = dt + (vh - CH*s)/2 + panY;
|
|
57
59
|
stage.style.transform = 'translate('+tx+'px,'+ty+'px) scale('+s+')';
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -118,30 +120,55 @@ export const RUNTIME_JS = String.raw `
|
|
|
118
120
|
if(dir<0){ newSlide.classList.add('sl-anim-rev'); oldSlide.classList.add('sl-anim-rev'); }
|
|
119
121
|
step = dir<0 ? buildCount(i) : 0; applyBuilds(newSlide, step);
|
|
120
122
|
var done=0;
|
|
121
|
-
function endOld(){ oldSlide.classList.remove('sl-active', outC, 'sl-anim-rev'); oldSlide.removeEventListener('animationend', endOld);
|
|
122
|
-
function endNew(){ newSlide.classList.remove(inC, 'sl-anim-rev'); newSlide.removeEventListener('animationend', endNew);
|
|
123
|
-
function
|
|
123
|
+
function endOld(){ if(done&1) return; done|=1; oldSlide.classList.remove('sl-active', outC, 'sl-anim-rev'); oldSlide.removeEventListener('animationend', endOld); fin(); }
|
|
124
|
+
function endNew(){ if(done&2) return; done|=2; newSlide.classList.remove(inC, 'sl-anim-rev'); newSlide.removeEventListener('animationend', endNew); fin(); }
|
|
125
|
+
function fin(){ if((done&3)===3){ busy=false; if(pendingNav){ var p=pendingNav; pendingNav=null; if(p==='fwd') forward(); else backward(); } } }
|
|
124
126
|
oldSlide.addEventListener('animationend', endOld);
|
|
125
127
|
newSlide.addEventListener('animationend', endNew);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
requestAnimationFrame(function(){
|
|
129
|
+
var cs1=getComputedStyle(oldSlide),cs2=getComputedStyle(newSlide);
|
|
130
|
+
if(!cs1.animationName||cs1.animationName==='none'||cs1.animationDuration==='0s') endOld();
|
|
131
|
+
if(!cs2.animationName||cs2.animationName==='none'||cs2.animationDuration==='0s') endNew();
|
|
132
|
+
});
|
|
133
|
+
setTimeout(function(){ if(busy){ endOld(); endNew(); } }, 800);
|
|
128
134
|
cur=i; updateChrome(); sync();
|
|
129
135
|
}
|
|
130
136
|
|
|
131
137
|
function forward(){
|
|
132
|
-
if(busy) return;
|
|
138
|
+
if(busy){ pendingNav='fwd'; return; }
|
|
133
139
|
if(blanked) blank('');
|
|
134
140
|
if(step < buildCount(cur)){ step++; applyBuilds(slides[cur], step); updateChrome(); sync(); return; }
|
|
135
141
|
goTo(cur+1, 1);
|
|
136
142
|
}
|
|
137
143
|
function backward(){
|
|
138
|
-
if(busy) return;
|
|
144
|
+
if(busy){ pendingNav='bwd'; return; }
|
|
139
145
|
if(blanked) blank('');
|
|
140
146
|
if(step > 0){ step--; applyBuilds(slides[cur], step); updateChrome(); sync(); return; }
|
|
141
147
|
goTo(cur-1, -1);
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
function toggle(sel){ var e=document.querySelector(sel); if(e) e.classList.toggle('sl-open'); updateChrome(); }
|
|
151
|
+
var NOTES_H = 180;
|
|
152
|
+
// Dock the notes panel BENEATH the slide (not floating over it). The panel is a fixed sibling;
|
|
153
|
+
// it reserves its own space via the INDEPENDENT --sl-dock-notes var (summed into the stage fit by
|
|
154
|
+
// scale()), and anchors at --sl-dock-bottom so it sits flush above the chrome dock (the editor
|
|
155
|
+
// filmstrip, or nothing). Keeping notes' reservation separate from --sl-dock-bottom means the
|
|
156
|
+
// bstrip's absolute writes and the notes' toggle never clobber each other (no overlap on toggle).
|
|
157
|
+
function setNotesOpen(open){
|
|
158
|
+
var np = document.querySelector('.sl-notes');
|
|
159
|
+
if(!np) return;
|
|
160
|
+
if(open === np.classList.contains('sl-open')) return; // already in the requested state
|
|
161
|
+
var ds = document.documentElement.style;
|
|
162
|
+
if(open){ np.classList.add('sl-open'); ds.setProperty('--sl-dock-notes', NOTES_H + 'px'); }
|
|
163
|
+
else { np.classList.remove('sl-open'); ds.setProperty('--sl-dock-notes', '0px'); }
|
|
164
|
+
// innerHTML is populated by the updateChrome() call below (it fills .sl-notes when open).
|
|
165
|
+
scale();
|
|
166
|
+
updateChrome();
|
|
167
|
+
}
|
|
168
|
+
function toggleNotes(){
|
|
169
|
+
var np = document.querySelector('.sl-notes');
|
|
170
|
+
if(np) setNotesOpen(!np.classList.contains('sl-open'));
|
|
171
|
+
}
|
|
145
172
|
|
|
146
173
|
// ---- present extras: screen blank (b/w), presenter view, cross-window sync ----------------
|
|
147
174
|
var blanked = '';
|
|
@@ -292,13 +319,18 @@ export const RUNTIME_JS = String.raw `
|
|
|
292
319
|
case 'ArrowLeft': case 'ArrowDown': case 'PageUp': backward(); e.preventDefault(); break;
|
|
293
320
|
case 'Home': goTo(0,1); break;
|
|
294
321
|
case 'End': goTo(slides.length-1,1); break;
|
|
295
|
-
case 'n': case 'N':
|
|
322
|
+
case 'n': case 'N': toggleNotes(); break;
|
|
296
323
|
case 'f': case 'F': setPresenting(!isPresenting(), true); break;
|
|
297
324
|
case 'p': case 'P': presenter(); break;
|
|
298
325
|
case 'b': case 'B': blank(blanked==='b'?'':'b'); e.preventDefault(); break;
|
|
299
326
|
case 'w': case 'W': blank(blanked==='w'?'':'w'); e.preventDefault(); break;
|
|
300
327
|
case '?': case 'h': toggle('.sl-help'); break;
|
|
301
|
-
case 'Escape':
|
|
328
|
+
case 'Escape':
|
|
329
|
+
// Close any open overlay (help, etc.) directly; notes go through setNotesOpen so the
|
|
330
|
+
// reserved bottom dock unwinds too. (Loop param is el; the outer e is the KeyboardEvent.)
|
|
331
|
+
document.querySelectorAll('.sl-open').forEach(function(el){ if(!el.classList.contains('sl-notes')) el.classList.remove('sl-open'); });
|
|
332
|
+
setNotesOpen(false);
|
|
333
|
+
break;
|
|
302
334
|
}
|
|
303
335
|
});
|
|
304
336
|
// ---- drag-to-pan (only when zoomed in past fit) -------------------------
|
|
@@ -347,6 +379,7 @@ export const RUNTIME_JS = String.raw `
|
|
|
347
379
|
}
|
|
348
380
|
document.addEventListener('fullscreenchange', function(){
|
|
349
381
|
document.body.classList.toggle('sl-presenting', !!document.fullscreenElement);
|
|
382
|
+
scale();
|
|
350
383
|
});
|
|
351
384
|
var presentBtn = document.querySelector('.sl-present-toggle');
|
|
352
385
|
if(presentBtn) presentBtn.addEventListener('click', function(){ setPresenting(!isPresenting(), true); });
|
|
@@ -357,9 +390,22 @@ export const RUNTIME_JS = String.raw `
|
|
|
357
390
|
prev: backward,
|
|
358
391
|
goTo: function(i){ goTo(i, i>cur?1:-1); },
|
|
359
392
|
relayout: function(){ scale(); }, // recompute the stage fit (e.g. after the dock opens/closes)
|
|
360
|
-
show: function(i){ if(
|
|
393
|
+
show: function(i){ if(!slides.length) return; i=Math.max(0,Math.min(slides.length-1,i)); activate(i, true); cur=i; updateChrome(); },
|
|
394
|
+
// Re-collect slides after the host appended/replaced .sl-slide nodes in the stage (live agent
|
|
395
|
+
// build: new slides stream in one by one). Preserve the current index; re-assert active + builds
|
|
396
|
+
// + chrome. Returns the new slide count.
|
|
397
|
+
rescan: function(){
|
|
398
|
+
slides = Array.prototype.slice.call(stage.querySelectorAll(':scope > .sl-slide'));
|
|
399
|
+
if(!slides.length) return 0;
|
|
400
|
+
cur = Math.max(0, Math.min(cur, slides.length-1));
|
|
401
|
+
slides.forEach(function(s,idx){ s.classList.toggle('sl-active', idx===cur); });
|
|
402
|
+
if(step > buildCount(cur)) step = buildCount(cur);
|
|
403
|
+
applyBuilds(slides[cur], step);
|
|
404
|
+
scale(); updateChrome();
|
|
405
|
+
return slides.length;
|
|
406
|
+
},
|
|
361
407
|
setInteractive: function(on){ navEnabled = on!==false; },
|
|
362
|
-
toggleNotes: function(){
|
|
408
|
+
toggleNotes: function(){ toggleNotes(); },
|
|
363
409
|
toggleHelp: function(){ toggle('.sl-help'); },
|
|
364
410
|
zoom: function(f){
|
|
365
411
|
zoomFactor = (f==='fit'||!f) ? 1 : Math.max(0.25, Math.min(8, f));
|
|
@@ -388,6 +434,7 @@ export const RUNTIME_JS = String.raw `
|
|
|
388
434
|
scale(); updateChrome();
|
|
389
435
|
if(AM_PRESENTER) buildPresenter(); // this window is the presenter view
|
|
390
436
|
if(AM_AUDIENCE){ document.body.classList.add('slv-presenting'); navEnabled = false; } // clean mirror, host-driven
|
|
437
|
+
if(document.body.classList.contains('sl-editing')) navEnabled = false;
|
|
391
438
|
})();
|
|
392
439
|
`;
|
|
393
440
|
//# sourceMappingURL=runtime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/render/runtime.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,sCAAsC;AACtC,8EAA8E;AAC9E,uFAAuF;AACvF,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/render/runtime.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,sCAAsC;AACtC,8EAA8E;AAC9E,uFAAuF;AACvF,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkbnC,CAAC"}
|
package/dist/vocab.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { DEFAULT_ENTRANCE } from './render/anim.js';
|
|
2
|
+
/** Named slide transitions (frontmatter `transition:`). Canonical: render/anim.ts. */
|
|
3
|
+
export declare const TRANSITIONS: readonly string[];
|
|
4
|
+
/** Element/build entrances (`>>> <entrance>`), default `fade-up`. Canonical: render/anim.ts. */
|
|
5
|
+
export declare const ENTRANCES: readonly string[];
|
|
6
|
+
export { DEFAULT_ENTRANCE };
|
|
7
|
+
/** Per-slide frontmatter keys the parser recognizes (metadata keys included — they also
|
|
8
|
+
* become `{{placeholders}}`). Canonical: parser/parse.ts `KNOWN_SLIDE_KEYS`. */
|
|
9
|
+
export declare const FRONTMATTER_KEYS: readonly string[];
|
|
10
|
+
/** The config (non-metadata) subset — the keys that change how a slide renders. Used by
|
|
11
|
+
* the docs lint to check the frontmatter-key table; metadata keys (title/author/…) are
|
|
12
|
+
* documented as placeholders/headmatter instead. */
|
|
13
|
+
export declare const FRONTMATTER_CONFIG_KEYS: readonly string[];
|
|
14
|
+
/** Deck headmatter keys with defined meaning (plus any custom scalar → placeholder). */
|
|
15
|
+
export declare const HEADMATTER_KEYS: readonly string[];
|
|
16
|
+
/** Inline span size classes `[x]{.xs}`. Canonical: compiler/markdown.ts `SIZE_CLASS`. */
|
|
17
|
+
export declare const SPAN_SIZE_CLASSES: readonly string[];
|
|
18
|
+
/** Inline span utility classes (besides `.grad`/`.grad-<name>` and colour names). */
|
|
19
|
+
export declare const SPAN_UTIL_CLASSES: readonly string[];
|
|
20
|
+
/** Image `{...}` utility classes `{.round}`. Canonical: render/css.ts `.sl-img.*`. */
|
|
21
|
+
export declare const IMAGE_UTIL_CLASSES: readonly string[];
|
|
22
|
+
/** Renderable code-fence info-strings (rendered, not shown as code). Canonical: compiler/markdown.ts. */
|
|
23
|
+
export declare const RENDERABLE_FENCES: readonly string[];
|
|
24
|
+
/** Placeholder built-ins `{{page}}` (plus any scalar headmatter/frontmatter key). Canonical: compiler/chrome.ts `placeholderCtx`. */
|
|
25
|
+
export declare const PLACEHOLDER_BUILTINS: readonly string[];
|
|
26
|
+
/** Master layout slot `style:` keys. Canonical: compiler/compile.ts `STYLE_MAP` + the two
|
|
27
|
+
* keys handled before the map (`anchor`, `box`). */
|
|
28
|
+
export declare const SLOT_STYLE_KEYS: readonly string[];
|
|
29
|
+
/** Master layout slot `type:`s that get first-class styling. Canonical: render/css.ts `.sl-slot-*`. */
|
|
30
|
+
export declare const SLOT_TYPES: readonly string[];
|
|
31
|
+
/** Every diagnostic (`validate`) code the engine can emit. Canonical: emitted as `code:`
|
|
32
|
+
* literals across the parser/compiler; the lint scrapes src to keep this exhaustive. */
|
|
33
|
+
export declare const DIAGNOSTIC_CODES: readonly string[];
|
|
34
|
+
/** Diagnostics that are hard errors (`ok:false`); every other code is a warning.
|
|
35
|
+
* Canonical here so `index.ts` and the docs share one definition. */
|
|
36
|
+
export declare const ERROR_SEVERITY_CODES: ReadonlySet<string>;
|
package/dist/vocab.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Copyright 2026 AIVory, Inc.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
//
|
|
4
|
+
// THE canonical registry of the slaide language's author-facing vocabulary.
|
|
5
|
+
//
|
|
6
|
+
// Why this file exists: the token sets (transitions, frontmatter keys, span/slot
|
|
7
|
+
// classes, placeholders, fence types, diagnostic codes) used to live scattered and
|
|
8
|
+
// mostly un-exported across parser/compiler/render modules, and the human docs
|
|
9
|
+
// (docs/spec.md, docs/grammar.md, docs/themes.md) were hand-kept in parallel — so they
|
|
10
|
+
// drifted (e.g. docs listed ~5 transitions while the engine shipped 13). Everything the
|
|
11
|
+
// docs must stay complete about is now imported/derived here from the real code, and
|
|
12
|
+
// `test/docs-sync.test.ts` fails CI if a token is missing from the docs or if the code
|
|
13
|
+
// grows a token this registry doesn't know about. Add a token to the engine → add it
|
|
14
|
+
// here → the lint tells you which doc to update. One source, no drift.
|
|
15
|
+
//
|
|
16
|
+
// This module is a LEAF: it only pulls the already-exported catalogs it can reuse
|
|
17
|
+
// verbatim; anything without a single code-level const is listed here as the canonical
|
|
18
|
+
// definition and cross-checked against source text by the lint.
|
|
19
|
+
import { SLIDE_TRANSITION_NAMES, ENTRANCE_NAMES, DEFAULT_ENTRANCE } from './render/anim.js';
|
|
20
|
+
import { KNOWN_SLIDE_KEYS } from './parser/parse.js';
|
|
21
|
+
import { SIZE_CLASS } from './compiler/markdown.js';
|
|
22
|
+
import { STYLE_MAP } from './compiler/compile.js';
|
|
23
|
+
/** Named slide transitions (frontmatter `transition:`). Canonical: render/anim.ts. */
|
|
24
|
+
export const TRANSITIONS = SLIDE_TRANSITION_NAMES;
|
|
25
|
+
/** Element/build entrances (`>>> <entrance>`), default `fade-up`. Canonical: render/anim.ts. */
|
|
26
|
+
export const ENTRANCES = ENTRANCE_NAMES;
|
|
27
|
+
export { DEFAULT_ENTRANCE };
|
|
28
|
+
/** Per-slide frontmatter keys the parser recognizes (metadata keys included — they also
|
|
29
|
+
* become `{{placeholders}}`). Canonical: parser/parse.ts `KNOWN_SLIDE_KEYS`. */
|
|
30
|
+
export const FRONTMATTER_KEYS = [...KNOWN_SLIDE_KEYS];
|
|
31
|
+
/** The config (non-metadata) subset — the keys that change how a slide renders. Used by
|
|
32
|
+
* the docs lint to check the frontmatter-key table; metadata keys (title/author/…) are
|
|
33
|
+
* documented as placeholders/headmatter instead. */
|
|
34
|
+
export const FRONTMATTER_CONFIG_KEYS = [
|
|
35
|
+
'layout', 'transition', 'transition-ms', 'transition-ease',
|
|
36
|
+
'background', 'variant', 'morph', 'chrome', 'logo', 'footer', 'notes',
|
|
37
|
+
];
|
|
38
|
+
/** Deck headmatter keys with defined meaning (plus any custom scalar → placeholder). */
|
|
39
|
+
export const HEADMATTER_KEYS = ['master', 'title', 'author', 'date', 'company', 'subtitle', 'progress'];
|
|
40
|
+
/** Inline span size classes `[x]{.xs}`. Canonical: compiler/markdown.ts `SIZE_CLASS`. */
|
|
41
|
+
export const SPAN_SIZE_CLASSES = Object.keys(SIZE_CLASS);
|
|
42
|
+
/** Inline span utility classes (besides `.grad`/`.grad-<name>` and colour names). */
|
|
43
|
+
export const SPAN_UTIL_CLASSES = ['grad', 'bold', 'muted'];
|
|
44
|
+
/** Image `{...}` utility classes `{.round}`. Canonical: render/css.ts `.sl-img.*`. */
|
|
45
|
+
export const IMAGE_UTIL_CLASSES = ['round', 'cover', 'shadow'];
|
|
46
|
+
/** Renderable code-fence info-strings (rendered, not shown as code). Canonical: compiler/markdown.ts. */
|
|
47
|
+
export const RENDERABLE_FENCES = ['svg', 'embed', 'widget', 'mermaid', 'echart'];
|
|
48
|
+
/** Placeholder built-ins `{{page}}` (plus any scalar headmatter/frontmatter key). Canonical: compiler/chrome.ts `placeholderCtx`. */
|
|
49
|
+
export const PLACEHOLDER_BUILTINS = [
|
|
50
|
+
'page', 'total', 'pagePadded', 'totalPadded', 'date', 'title', 'author', 'slideTitle', 'footer',
|
|
51
|
+
];
|
|
52
|
+
/** Master layout slot `style:` keys. Canonical: compiler/compile.ts `STYLE_MAP` + the two
|
|
53
|
+
* keys handled before the map (`anchor`, `box`). */
|
|
54
|
+
export const SLOT_STYLE_KEYS = [...Object.keys(STYLE_MAP), 'anchor', 'box'];
|
|
55
|
+
/** Master layout slot `type:`s that get first-class styling. Canonical: render/css.ts `.sl-slot-*`. */
|
|
56
|
+
export const SLOT_TYPES = ['title', 'subtitle', 'body', 'image', 'media', 'quote', 'caption'];
|
|
57
|
+
/** Every diagnostic (`validate`) code the engine can emit. Canonical: emitted as `code:`
|
|
58
|
+
* literals across the parser/compiler; the lint scrapes src to keep this exhaustive. */
|
|
59
|
+
export const DIAGNOSTIC_CODES = [
|
|
60
|
+
// parser/parse.ts
|
|
61
|
+
'bad-config', 'no-headmatter', 'ambiguous-frontmatter', 'empty-deck',
|
|
62
|
+
// compiler/compile.ts
|
|
63
|
+
'unknown-color', 'unknown-gradient', 'bad-animation', 'unknown-layout',
|
|
64
|
+
'unknown-transition', 'unknown-background', 'unknown-slot', 'low-contrast',
|
|
65
|
+
// compiler/markdown.ts
|
|
66
|
+
'unknown-class', 'unknown-entrance', 'stray-build', 'bad-chart',
|
|
67
|
+
// compiler/tokens.ts
|
|
68
|
+
'unknown-token', 'token-cycle', 'non-embeddable-font', 'unknown-variant',
|
|
69
|
+
// compiler/chrome.ts
|
|
70
|
+
'unknown-placeholder',
|
|
71
|
+
// index.ts (validation entry)
|
|
72
|
+
'no-master', 'parse-error',
|
|
73
|
+
];
|
|
74
|
+
/** Diagnostics that are hard errors (`ok:false`); every other code is a warning.
|
|
75
|
+
* Canonical here so `index.ts` and the docs share one definition. */
|
|
76
|
+
export const ERROR_SEVERITY_CODES = new Set(['empty-deck', 'no-master', 'unknown-layout']);
|
|
77
|
+
//# sourceMappingURL=vocab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vocab.js","sourceRoot":"","sources":["../src/vocab.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skills/slaide/SKILL.md
CHANGED
|
@@ -16,7 +16,7 @@ Run as `npx @aivorynet/slaide <cmd>` (or `npm run slaide -- <cmd>` where a scrip
|
|
|
16
16
|
|
|
17
17
|
## Workflow
|
|
18
18
|
1. **Brand first — check, then ask.** Scan the project/context for brand assets: colours, fonts, a logo, an existing `*.slaide.yaml` master, a brand doc. Found a master → use it. Found brand → build the master around it. Found nothing → **ask** the user: brand colours? logo? an existing theme, or author one from scratch? Prefer authoring a **new master** over copying an example.
|
|
19
|
-
2. **Read** `reference.md` (language) and `themes.md` (the master format + footguns) before authoring a master. Worked example decks: https://github.com/aivorynet/slaide/tree/main/examples
|
|
19
|
+
2. **Read** `reference.md` (language) and `themes.md` (the master format + footguns) before authoring a master; `grammar.md` is the precise formal reference (frontmatter detection, attribute braces, transitions/entrances, master value forms) when you need it. Worked example decks: https://github.com/aivorynet/slaide/tree/main/examples
|
|
20
20
|
3. **`slaide slots <deck>`** — prints the master's real layouts, slots, colours, gradients, sizes, transitions. Reference these **by name**. (Run on the deck, not the master.)
|
|
21
21
|
4. **Write** the `.slaide` — and the master, if authoring one.
|
|
22
22
|
5. **`slaide validate <deck> --strict`** — a **gate**: must be clean. Every `unknown-*` / `low-contrast` / `non-embeddable-font` means something renders wrong even when it "looks valid".
|
|
@@ -48,7 +48,7 @@ layout: title-content
|
|
|
48
48
|
- Charts: ` ```echart ` (data viz, ECharts option as JSON) and ` ```mermaid ` (diagrams) → theme-coloured SVG. Put a chart in a slot with height (a `1fr` row).
|
|
49
49
|
|
|
50
50
|
## The master (theme)
|
|
51
|
-
Use the bundled theme — omit `master:` or set `master: aurora` — or author your own (preferred for branded decks). **Read `themes.md` for the format before writing one**: fonts, a type scale, two-tier colour (palette + roles), gradients, backgrounds, grid layouts, chrome — all data. Reference **roles, not hex**, so a reskin is a palette swap. Past "tidy", give the deck **one signature** (a
|
|
51
|
+
Use the bundled theme — omit `master:` or set `master: aurora` — or author your own (preferred for branded decks). **Read `themes.md` for the format before writing one**: fonts, a type scale, two-tier colour (palette + roles), gradients, backgrounds, grid layouts, chrome — all data. Reference **roles, not hex**, so a reskin is a palette swap. Past "tidy", give the deck **one signature** (a recurring accent panel, a distinctive layout rhythm, or a colour-block motif) and vary composition slide to slide. Chrome `logo:` is opt-in — only add it when the user supplies a real brand logo; do NOT invent a generic icon. The load-bearing footguns — dark-on-dark text, hollow `box:` cards, chrome-corner collisions — are flagged in `themes.md`; heed them. A deck about slaide itself: use the shipped brand in the sibling `slaide-hugo` repo, don't invent one.
|
|
52
52
|
|
|
53
53
|
## Commands (prefix `npx @aivorynet/slaide`)
|
|
54
54
|
| Command | Does |
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# slaide grammar (v1)
|
|
2
|
+
|
|
3
|
+
A formal grammar for the `.slaide` document layer. Notation: EBNF — `=` defines, `|` alternation, `{ }` zero-or-more, `[ ]` optional, `( )` grouping, `"x"` literal, `…` charset prose. The Markdown *inside* slot content is CommonMark and is not re-specified here; this grammar defines slaide' structural envelope, sigils, and the inline/attribute extensions, plus the master value forms.
|
|
4
|
+
|
|
5
|
+
This file is the **precise structural reference**. For what each construct *does* — usage, examples, the full transition/entrance catalogs, chart/image/media options, and the `validate` diagnostic codes — see [spec.md](spec.md); for master semantics (colours, type scale, layouts, slot styles) see [themes.md](themes.md). The token sets are generated from the engine (`src/vocab.ts`) and kept in sync by `test/docs-sync.test.ts`.
|
|
6
|
+
|
|
7
|
+
## 1. Document
|
|
8
|
+
|
|
9
|
+
```ebnf
|
|
10
|
+
deck = [ headmatter ] , slide , { separator , slide } , [ NL ] ;
|
|
11
|
+
headmatter = fence , config-block , fence ;
|
|
12
|
+
slide = [ frontmatter ] , body ;
|
|
13
|
+
frontmatter = config-block , fence ; (* see §2 detection rule *)
|
|
14
|
+
separator = NL , fence ; (* a line that is exactly "---" *)
|
|
15
|
+
fence = "---" , EOL ;
|
|
16
|
+
NL = newline ;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A deck is an optional headmatter block, then one or more slides separated by a bare `---` line. `headmatter` is the first fenced config block. Each slide is an optional fenced frontmatter block followed by a body.
|
|
20
|
+
|
|
21
|
+
## 2. Frontmatter-vs-body detection (the load-bearing rule)
|
|
22
|
+
|
|
23
|
+
A block immediately after a separator (or the leading fence) is **frontmatter** iff **both**:
|
|
24
|
+
1. every non-blank, non-`#comment` line matches `config-line` (a `key:`/`~key:`/list-continuation), **and**
|
|
25
|
+
2. it is terminated by a `fence` before the body begins.
|
|
26
|
+
|
|
27
|
+
Otherwise the block is `body`. Consequences the parser guarantees:
|
|
28
|
+
- The **first** config-like fenced block in the file is `headmatter`.
|
|
29
|
+
- A body whose first line merely *contains* a colon (e.g. `We offer: Tooling`) is **not** frontmatter (it is not `key:`-only and has no trailing fence).
|
|
30
|
+
- A config-shaped block whose keys are **none of** the known slide keys (`layout`, `background`, `transition`, `variant`, `chrome`, `footer`, `logo`, `morph`, …) is still read as frontmatter, **but** the compiler emits an `ambiguous-frontmatter` warning — so a spec-sheet body (`Name: …` / `Founded: …`) eaten as config is no longer silent.
|
|
31
|
+
- To force a body that would otherwise look config-like, escape its first line with a leading backslash (`\Name: Acme`) or precede the slide with an empty frontmatter (`---` `---`).
|
|
32
|
+
- Malformed YAML in a config block emits a `bad-config` warning (with the parser message) instead of silently rendering with defaults.
|
|
33
|
+
|
|
34
|
+
```ebnf
|
|
35
|
+
config-block = { config-line | comment | blank } ;
|
|
36
|
+
config-line = [ "~" ] , key , ":" , [ value ] , EOL ;
|
|
37
|
+
key = ident ;
|
|
38
|
+
comment = "#" , … , EOL ;
|
|
39
|
+
ident = letter , { letter | digit | "-" | "_" } ;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`value` and nested structures follow YAML. A leading `~` on a key marks a **cascading** default (applies to this slide and all following until overridden); a bare key is **scoped** to its slide.
|
|
43
|
+
|
|
44
|
+
## 3. Fences vs code
|
|
45
|
+
|
|
46
|
+
`---` is a separator **only outside** a fenced code region. Inside ```` ``` ```` / `~~~` fences it is literal text.
|
|
47
|
+
|
|
48
|
+
```ebnf
|
|
49
|
+
code-fence = ("```" | "~~~") , [ info-string ] , EOL , { any-line } , ("```" | "~~~") , EOL ;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
A code fence whose `info-string` is a renderable type is rendered, not listed as code: `svg` (inline vector), `embed` (its body is a URL → sandboxed `<iframe>`), `widget` (its body is HTML/JS → `sandbox="allow-scripts"` srcdoc iframe, theme tokens injected), `mermaid` (diagram DSL → inline SVG), `echart` (ECharts `option` as JSON/YAML → inline SVG). Other info-strings render as code. CommonMark GFM pipe tables are supported as ordinary body content.
|
|
53
|
+
|
|
54
|
+
Media uses the image syntax with a media extension: `` / `` → `<video>`/`<audio>` (see §6).
|
|
55
|
+
|
|
56
|
+
## 4. Body
|
|
57
|
+
|
|
58
|
+
```ebnf
|
|
59
|
+
body = { region-marker | note | content-line } ;
|
|
60
|
+
region-marker = "::" , WS , slot-name , WS , "::" , EOL ;
|
|
61
|
+
slot-name = ident ; (* valid names are per-layout *)
|
|
62
|
+
note = "???" , [ WS ] , inline-text , EOL , { non-blank-line } ;
|
|
63
|
+
content-line = markdown-line ; (* CommonMark, with §5–§7 extensions *)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `region-marker` routes following content into the named slot until the next marker or separator. Content before any marker → the layout's main slot.
|
|
67
|
+
- `note` (a `???` line and its continuation until a blank line) is a speaker note: shown in the presenter overlay, omitted from audience view and PDF.
|
|
68
|
+
|
|
69
|
+
### 4.1 Build sigil
|
|
70
|
+
|
|
71
|
+
```ebnf
|
|
72
|
+
build = content , WS , ">>>" , EOL ;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
A list item or block ending with `>>>` becomes an incremental build step. Steps auto-number in document order (a shared per-slide counter); identical effective step → simultaneous. In PDF all builds are settled (shown).
|
|
76
|
+
|
|
77
|
+
### 4.2 Escaping sigils
|
|
78
|
+
|
|
79
|
+
A leading backslash turns a slaide sigil into literal content (the backslash is removed):
|
|
80
|
+
|
|
81
|
+
| Write | Renders as | Instead of |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `\:: word ::` | literal `:: word ::` | a region marker |
|
|
84
|
+
| `\??? text` | literal `??? text` body | a speaker note |
|
|
85
|
+
| `… \>>>` | literal trailing `>>>` | a build step |
|
|
86
|
+
| `\[text]{.cls}` | literal `[text]{.cls}` | a styled span (and skips class validation) |
|
|
87
|
+
| `\Name: Acme` (first body line) | body line | frontmatter (see §2) |
|
|
88
|
+
|
|
89
|
+
## 5. Inline styled spans
|
|
90
|
+
|
|
91
|
+
```ebnf
|
|
92
|
+
span = "[" , inline-text , "]" , "{" , class , { class } , "}" ;
|
|
93
|
+
class = "." , class-name ;
|
|
94
|
+
class-name = ident ; (* resolved against the master *)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Classes chain (`[40-80%]{.grad-purple .huge}`). Resolution (see themes.md → *Marks / utility classes*):
|
|
98
|
+
- `.grad` → brand gradient text; `.grad-<name>` → named gradient text.
|
|
99
|
+
- `.<color>` → text color, where `<color>` is a `palette` key, `roles` name, or a literal CSS colour.
|
|
100
|
+
- `.xs .sm .md .lg .xl .xxl .huge` → font-size (type-scale steps `small`…`stat`).
|
|
101
|
+
- `.bold`, `.muted`, and image utilities `.round`, `.cover`, `.shadow`.
|
|
102
|
+
|
|
103
|
+
A class that is **none** of the above (e.g. a typo like `.xxlarge` or `.grad-teel`) emits an `unknown-class` / `unknown-gradient` warning rather than silently degrading to inert/invisible CSS. Run `slaide slots <deck>` to print the legal slot, colour, gradient and size names for a deck's master; `slaide validate <deck> [--strict]` surfaces all diagnostics (`--strict` makes warnings fail).
|
|
104
|
+
|
|
105
|
+
## 6. Image & attribute brace
|
|
106
|
+
|
|
107
|
+
```ebnf
|
|
108
|
+
image = "" , [ attr-brace ] ;
|
|
109
|
+
attr-brace = "{" , WS , { attr , WS } , "}" ;
|
|
110
|
+
attr = id-attr | class | kv-attr | anchor-attr ;
|
|
111
|
+
id-attr = "#" , ident ;
|
|
112
|
+
kv-attr = key , "=" , value-token ; (* e.g. width=170px — unquoted, units ok *)
|
|
113
|
+
anchor-attr = "anchor" , ":" , WS , '"' , pct , WS , pct , WS , pct , WS , pct , '"' ;
|
|
114
|
+
value-token = { non-space-non-brace } ;
|
|
115
|
+
pct = number , "%" ;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
One brace may mix `#id`, `.class`, `key=value`, and `anchor:"…"` in any order. `#id` enables shared-element **morph** to a same-id element on the next slide (`transition: morph`).
|
|
119
|
+
|
|
120
|
+
## 7. Placeholders
|
|
121
|
+
|
|
122
|
+
```ebnf
|
|
123
|
+
placeholder = "{{" , WS , ph-name , WS , "}}" ;
|
|
124
|
+
ph-name = ident ;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Substituted at compile time inside chrome bands and master text. Names: `page`, `total`, `pagePadded`, `totalPadded`, `date`, `title`, `author`, `slideTitle`, `footer`, plus any scalar headmatter/frontmatter key. Unknown names resolve to empty (with a warning).
|
|
128
|
+
|
|
129
|
+
## 8. Master (`*.slaide.yaml`) value forms
|
|
130
|
+
|
|
131
|
+
The master is YAML; this section pins the slaide-specific value vocabulary (see themes.md for full semantics).
|
|
132
|
+
|
|
133
|
+
```ebnf
|
|
134
|
+
master = mapping of:
|
|
135
|
+
"schema" , "name" , [ "description" ] ,
|
|
136
|
+
[ "canvas" ] , [ "fonts" ] , [ "typeScale" ] , [ "colors" ] ,
|
|
137
|
+
[ "gradients" ] , [ "tokens" ] , [ "backgrounds" ] ,
|
|
138
|
+
[ "variants" ] , [ "transitions" ] , [ "chrome" ] , [ "ui" ] , [ "layouts" ] ;
|
|
139
|
+
ui = { [ "progress": boolean ] } ; (* web position indicator; default true *)
|
|
140
|
+
|
|
141
|
+
typeScale = { "base": dim , "ratio": number , "steps": { step-name : (integer | dim) } } ;
|
|
142
|
+
dim = number , ("px"|"em"|"rem"|"%"|"vw"|"vh") ;
|
|
143
|
+
color-ref = "{palette." , ident , "}" | "{" , ident , "}" | css-color ;
|
|
144
|
+
gradient-ref = "{gradients." , ident , "}" | gradient-name ;
|
|
145
|
+
|
|
146
|
+
layout = { "areas": [ area-row , { area-row } ] ,
|
|
147
|
+
[ "rows" ] , [ "cols" ] , [ "gap" ] , [ "padding" ] ,
|
|
148
|
+
[ "align": ("start"|"center"|"end") ] ,
|
|
149
|
+
[ "background": bg-name ] ,
|
|
150
|
+
[ "chrome": ("both"|"header"|"footer"|"false") ] ,
|
|
151
|
+
[ "logo": false ] ,
|
|
152
|
+
"slots": { slot-name : slot } } ;
|
|
153
|
+
area-row = string ; (* space-separated slot names; rectangular *)
|
|
154
|
+
slot = { "type": slot-type , [ "style": style-map ] } ;
|
|
155
|
+
slot-type = "title"|"subtitle"|"body"|"image"|"media"|"quote"|"caption"| ident ;
|
|
156
|
+
style-map = { style-key : style-val } ;
|
|
157
|
+
style-key = "font"|"size"|"color"|"fill"|"align"|"valign"|"justify"
|
|
158
|
+
| "weight"|"leading"|"transform"|"italic"|"maxw"|"box" ;
|
|
159
|
+
|
|
160
|
+
chrome = { [ "header": band ] , [ "footer": band ] ,
|
|
161
|
+
[ "logo": svg-string ] , [ "logoPos": corner ] } ;
|
|
162
|
+
band = { [ "left": tmpl ] , [ "center": tmpl ] , [ "right": tmpl ] } ;
|
|
163
|
+
tmpl = string ; (* Markdown-inline + placeholders *)
|
|
164
|
+
corner = "top-left"|"top-right"|"bottom-left"|"bottom-right" ;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## 9. Reserved tokens (summary)
|
|
168
|
+
|
|
169
|
+
| Token | Context | Meaning |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `---` | line | slide separator / config fence (outside code fences) |
|
|
172
|
+
| `:: name ::` | line | region/slot marker |
|
|
173
|
+
| `>>>` | end of line/item | build step |
|
|
174
|
+
| `???` | line start | speaker note |
|
|
175
|
+
| `~key:` | frontmatter | cascading default |
|
|
176
|
+
| `[t]{.c}` | inline | styled span |
|
|
177
|
+
| `{#id .c k=v anchor:"…"}` | after image | attributes |
|
|
178
|
+
| `{{name}}` | chrome/master text | placeholder |
|
|
179
|
+
| `` | inline | video / audio |
|
|
180
|
+
| ```` ```svg ```` | fence | inline vector embed |
|
|
181
|
+
| ```` ```embed ```` / ```` ```widget ```` | fence | sandboxed iframe (URL / inline JS) |
|
|
182
|
+
| ```` ```mermaid ```` / ```` ```echart ```` | fence | chart → inline SVG (diagram / data viz) |
|
|
183
|
+
| `\` (leading) | before a sigil | escape to literal (`\::`, `\???`, `\>>>`, `\[t]{.c}`) |
|