@stonedeck/core 0.7.2 → 0.7.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.
@@ -0,0 +1,348 @@
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Variant Verification</title>
8
+ <style>
9
+ :root {
10
+ --canvas-width: 720pt;
11
+ --canvas-height: 405pt;
12
+ --accent-color: #3b82f6;
13
+ --scale-factor: 1;
14
+ }
15
+
16
+ html, body { height: 100%; }
17
+
18
+ body {
19
+ margin: 0;
20
+ padding: 0;
21
+ background-color: #1a1a1a;
22
+ min-height: 100vh;
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-items: center;
26
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
27
+ transition: background-color 0.3s;
28
+ overflow-x: hidden;
29
+ }
30
+
31
+ /* Presentation Mode */
32
+ body.presenting {
33
+ background-color: black;
34
+ overflow: hidden;
35
+ }
36
+
37
+ #presentation-container {
38
+ flex-grow: 1;
39
+ display: flex;
40
+ flex-direction: column;
41
+ justify-content: center;
42
+ align-items: center;
43
+ width: 100%;
44
+ padding: 20px 0;
45
+ box-sizing: border-box;
46
+ }
47
+
48
+ body.presenting #presentation-container {
49
+ padding: 0;
50
+ }
51
+
52
+ .slides-inner {
53
+ display: flex;
54
+ flex-direction: column;
55
+ gap: 20px;
56
+ align-items: center;
57
+ width: 100%;
58
+ }
59
+
60
+ body.presenting .slides-inner {
61
+ gap: 0;
62
+ width: auto;
63
+ height: 100%;
64
+ justify-content: center;
65
+ }
66
+
67
+ .slide-wrapper {
68
+ position: relative;
69
+ width: var(--canvas-width);
70
+ height: var(--canvas-height);
71
+ transform-origin: top left;
72
+ transform: scale(var(--scale-factor));
73
+ box-shadow: 0 5px 15px rgba(0,0,0,0.3);
74
+ background-color: white;
75
+ overflow: hidden;
76
+ flex-shrink: 0;
77
+ }
78
+
79
+ body.presenting .slide-wrapper {
80
+ box-shadow: none;
81
+ display: none; /* Hide inactive */
82
+ /* Do NOT override transform or width/height. Keep them logical 720x405 and let scale work. */
83
+ position: absolute;
84
+ top: 50%;
85
+ left: 50%;
86
+ transform-origin: center center;
87
+ transform: translate(-50%, -50%) scale(var(--scale-factor));
88
+ }
89
+
90
+ body.presenting .slide-wrapper.active {
91
+ display: block;
92
+ }
93
+
94
+ .slide {
95
+ position: absolute;
96
+ top: 0;
97
+ left: 0;
98
+ width: 100%;
99
+ height: 100%;
100
+ box-sizing: border-box;
101
+ background-color: white;
102
+ overflow: hidden;
103
+ }
104
+
105
+ /* Slots */
106
+ .slot {
107
+ position: absolute;
108
+ display: flex;
109
+ flex-direction: column;
110
+ overflow: hidden;
111
+ box-sizing: border-box;
112
+ }
113
+ .slot-title { font-weight: bold; }
114
+
115
+ h1 { font-size: 1.6em; margin-bottom: 0.1em; margin-top: 0; line-height: 1.1; }
116
+ h2 { font-size: 1.3em; margin-bottom: 0.15em; margin-top: 0; line-height: 1.2; }
117
+ h3 { font-size: 1.1em; margin-bottom: 0.1em; margin-top: 0; line-height: 1.2; }
118
+
119
+ ul { padding-left: 1.1em; margin: 0; list-style-type: disc; }
120
+ li { margin-bottom: 0.15em; line-height: 1.25; }
121
+ li::marker { color: var(--accent-color); }
122
+
123
+ table { border-collapse: collapse; width: 100%; margin-top: 0.4em; }
124
+ td, th { border: 1pt solid #ccc; padding: 3pt 5pt; text-align: left; }
125
+ th { background: rgba(0,0,0,0.05); font-weight: bold; }
126
+
127
+ img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; margin: auto; flex-shrink: 0; }
128
+
129
+ .markdown-line { margin-bottom: 2pt; line-height: 1.35; }
130
+ p { margin: 0 0 3pt 0; }
131
+
132
+ /* Navigation */
133
+ #nav-bar {
134
+ position: fixed;
135
+ bottom: 20px;
136
+ left: 50%;
137
+ transform: translateX(-50%);
138
+ background: rgba(0, 0, 0, 0.8);
139
+ color: white;
140
+ padding: 10px 20px;
141
+ border-radius: 30px;
142
+ display: flex;
143
+ gap: 15px;
144
+ align-items: center;
145
+ z-index: 1000;
146
+ backdrop-filter: blur(10px);
147
+ opacity: 1;
148
+ transition: opacity 0.3s;
149
+ }
150
+
151
+ body.presenting #nav-bar {
152
+ opacity: 0;
153
+ pointer-events: none;
154
+ }
155
+
156
+ body.presenting #nav-bar:hover {
157
+ opacity: 1;
158
+ pointer-events: auto;
159
+ }
160
+
161
+ button {
162
+ background: transparent;
163
+ border: 1px solid rgba(255,255,255,0.3);
164
+ color: white;
165
+ padding: 5px 12px;
166
+ border-radius: 15px;
167
+ cursor: pointer;
168
+ font-size: 14px;
169
+ }
170
+ button:hover { background: var(--accent-color); border-color: var(--accent-color); }
171
+
172
+ #slide-index { min-width: 60px; text-align: center; }
173
+
174
+ @media print {
175
+ @page {
176
+ size: 720pt 405pt;
177
+ margin: 0;
178
+ }
179
+ body {
180
+ background: white;
181
+ display: block;
182
+ overflow: visible;
183
+ -webkit-print-color-adjust: exact;
184
+ print-color-adjust: exact;
185
+ }
186
+ #nav-bar {
187
+ display: none !important;
188
+ }
189
+ #presentation-container {
190
+ padding: 0 !important;
191
+ display: block !important;
192
+ }
193
+ .slides-inner {
194
+ display: block !important;
195
+ gap: 0 !important;
196
+ }
197
+ .slide-wrapper {
198
+ display: block !important;
199
+ margin: 0 !important;
200
+ padding: 0 !important;
201
+ box-shadow: none !important;
202
+ page-break-after: always !important;
203
+ break-after: page !important;
204
+ transform: none !important;
205
+ width: 720pt !important;
206
+ height: 405pt !important;
207
+ position: relative !important;
208
+ left: 0 !important;
209
+ top: 0 !important;
210
+ overflow: hidden !important;
211
+ }
212
+ .slide {
213
+ position: absolute !important;
214
+ width: 100% !important;
215
+ height: 100% !important;
216
+ }
217
+ }
218
+ </style>
219
+ </head>
220
+ <body id="body">
221
+ <div id="presentation-container">
222
+ <div class="slides-inner">
223
+
224
+ <div class="slide-wrapper active" id="wrapper-0">
225
+ <div class="slide" id="slide-0" style="background: #E8F4F8; color: #007BFF; font-family: Roboto Slab;">
226
+
227
+ </div>
228
+ </div>
229
+
230
+ <div class="slide-wrapper " id="wrapper-1">
231
+ <div class="slide" id="slide-1" style="background: #E8F4F8; color: #007BFF; font-family: Roboto Slab;">
232
+
233
+ </div>
234
+ </div>
235
+
236
+ <div class="slide-wrapper " id="wrapper-2">
237
+ <div class="slide" id="slide-2" style="background: #FFF5E6; color: #000000; font-family: Merriweather;">
238
+
239
+ </div>
240
+ </div>
241
+
242
+ <div class="slide-wrapper " id="wrapper-3">
243
+ <div class="slide" id="slide-3" style="background: linear-gradient(to bottom, #1a2a6c, #b21f1f, #fdbb2d); color: #FFFFFF; font-family: #000000;">
244
+
245
+ </div>
246
+ </div>
247
+
248
+ </div>
249
+ </div>
250
+
251
+ <div id="nav-bar">
252
+ <button onclick="togglePresent()">Present</button>
253
+ <button onclick="prevSlide()">Prev</button>
254
+ <span id="slide-index">1 / 4</span>
255
+ <button onclick="nextSlide()">Next</button>
256
+ <button onclick="toggleFS()">FS</button>
257
+ </div>
258
+
259
+ <script>
260
+ const totalSlides = 4;
261
+ const body = document.getElementById('body');
262
+ const wrappers = document.querySelectorAll('.slide-wrapper');
263
+ const indexSpan = document.getElementById('slide-index');
264
+ let currentIdx = 0;
265
+
266
+ function resize() {
267
+ const baseWidth = 960;
268
+ const baseHeight = 540;
269
+ const isPresenting = body.classList.contains('presenting');
270
+
271
+ const winW = window.innerWidth || document.documentElement.clientWidth || 1024;
272
+ const winH = window.innerHeight || document.documentElement.clientHeight || 768;
273
+
274
+ const targetWidth = isPresenting ? winW : winW * 0.95;
275
+ const targetHeight = isPresenting ? winH : winH * 0.9;
276
+
277
+ let scale = Math.min(targetWidth / baseWidth, targetHeight / baseHeight);
278
+
279
+ if (!isPresenting) {
280
+ scale = Math.min(scale, 1);
281
+ }
282
+ if (scale <= 0.01) scale = 1;
283
+
284
+ document.documentElement.style.setProperty('--scale-factor', scale);
285
+
286
+ wrappers.forEach(w => {
287
+ if (isPresenting) {
288
+ w.style.height = ''; // Reset to CSS default (var(--canvas-height)) for proper aspect ratio scaling
289
+ } else {
290
+ w.style.height = (baseHeight * scale) + 'px';
291
+ }
292
+ });
293
+ }
294
+
295
+ window.addEventListener('resize', resize);
296
+ window.addEventListener('DOMContentLoaded', resize);
297
+ window.addEventListener('load', resize);
298
+ resize();
299
+
300
+ function updateUI() {
301
+ indexSpan.innerText = (currentIdx + 1) + ' / ' + totalSlides;
302
+ wrappers.forEach((w, i) => {
303
+ w.classList.toggle('active', i === currentIdx);
304
+ });
305
+
306
+ if (!body.classList.contains('presenting')) {
307
+ wrappers[currentIdx].scrollIntoView({ behavior: 'auto', block: 'center' });
308
+ }
309
+ }
310
+
311
+ function nextSlide() {
312
+ if (currentIdx < totalSlides - 1) {
313
+ currentIdx++;
314
+ updateUI();
315
+ }
316
+ }
317
+
318
+ function prevSlide() {
319
+ if (currentIdx > 0) {
320
+ currentIdx--;
321
+ updateUI();
322
+ }
323
+ }
324
+
325
+ function togglePresent() {
326
+ body.classList.toggle('presenting');
327
+ setTimeout(() => { resize(); updateUI(); }, 50);
328
+ }
329
+
330
+ function toggleFS() {
331
+ if (!document.fullscreenElement) {
332
+ body.requestFullscreen().catch(e => console.warn(e));
333
+ } else {
334
+ document.exitFullscreen();
335
+ }
336
+ }
337
+
338
+ window.addEventListener('keydown', (e) => {
339
+ if (['ArrowRight', ' ', 'PageDown', 'Enter'].includes(e.key)) { e.preventDefault(); nextSlide(); }
340
+ if (['ArrowLeft', 'PageUp', 'Backspace'].includes(e.key)) { e.preventDefault(); prevSlide(); }
341
+ if (e.key === 'p') togglePresent();
342
+ if (e.key === 'f') toggleFS();
343
+ if (e.key === 'Escape' && body.classList.contains('presenting')) togglePresent();
344
+ });
345
+ </script>
346
+
347
+ </body>
348
+ </html>
@@ -0,0 +1,40 @@
1
+ ---
2
+ StoneDeck: true
3
+ title: "Variant Verification"
4
+ theme: "../src/themes/default_v2.yaml"
5
+ ---
6
+
7
+ :::slide
8
+ ---
9
+ layout: title
10
+ ---
11
+ # Slide 1 (Dynamic)
12
+ Should pick a random variant.
13
+ :::
14
+
15
+ :::slide
16
+ ---
17
+ layout: title
18
+ layout_style: kind_1
19
+ ---
20
+ # Slide 2 (Kind 1)
21
+ Should use kind_1 variant.
22
+ :::
23
+
24
+ :::slide
25
+ ---
26
+ layout: title
27
+ layout_style: kind_2
28
+ ---
29
+ # Slide 3 (Kind 2)
30
+ Should use kind_2 variant.
31
+ :::
32
+
33
+ :::slide
34
+ ---
35
+ layout: title
36
+ layout_style: kind_3
37
+ ---
38
+ # Slide 4 (Kind 3)
39
+ Should use kind_3 variant.
40
+ :::
@@ -1,27 +0,0 @@
1
- import { SlideStyle } from '../models/ir.js';
2
- export interface LayoutSlot {
3
- id: string;
4
- x: number;
5
- y: number;
6
- width: number;
7
- height: number;
8
- }
9
- export interface LayoutDefinition {
10
- id: string;
11
- slots: LayoutSlot[];
12
- }
13
- export declare class MetricsCalculator {
14
- private doc;
15
- constructor();
16
- /**
17
- * Calculates the height of a string given a font and width.
18
- */
19
- calculateTextHeight(text: string, style: SlideStyle, maxWidth: number): number;
20
- /**
21
- * Estimates height for Markdown content.
22
- * For now, this is a simplified version that handles basic text and lists.
23
- */
24
- estimateMarkdownHeight(markdown: string, style: SlideStyle, maxWidth: number): number;
25
- }
26
- export declare const metricsCalculator: MetricsCalculator;
27
- //# sourceMappingURL=metrics.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/resolver/metrics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,GAAG,CAAqB;;IAOhC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAc9E;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAgBxF;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
@@ -1,43 +0,0 @@
1
- import PDFDocument from 'pdfkit';
2
- export class MetricsCalculator {
3
- constructor() {
4
- // Create a headless PDF document for measurements
5
- this.doc = new PDFDocument({ size: [720, 405] });
6
- }
7
- /**
8
- * Calculates the height of a string given a font and width.
9
- */
10
- calculateTextHeight(text, style, maxWidth) {
11
- const fontSize = style.font_size || 18; // Default font size
12
- const font = style.font_family || 'Helvetica'; // Default font
13
- try {
14
- this.doc.font(font).fontSize(fontSize);
15
- return this.doc.heightOfString(text, { width: maxWidth });
16
- }
17
- catch (_e) {
18
- // Fallback font if the requested one fails
19
- this.doc.font('Helvetica').fontSize(fontSize);
20
- return this.doc.heightOfString(text, { width: maxWidth });
21
- }
22
- }
23
- /**
24
- * Estimates height for Markdown content.
25
- * For now, this is a simplified version that handles basic text and lists.
26
- */
27
- estimateMarkdownHeight(markdown, style, maxWidth) {
28
- // Simple heuristic: split by lines and sum heights
29
- // In a real implementation, this would handle actual markdown parsing.
30
- const lines = markdown.split('\n');
31
- let totalHeight = 0;
32
- for (const line of lines) {
33
- if (!line.trim()) {
34
- totalHeight += (style.font_size || 18) * 0.5; // Half line height for empty lines
35
- continue;
36
- }
37
- totalHeight += this.calculateTextHeight(line, style, maxWidth);
38
- }
39
- return totalHeight;
40
- }
41
- }
42
- export const metricsCalculator = new MetricsCalculator();
43
- //# sourceMappingURL=metrics.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/resolver/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,QAAQ,CAAC;AAgBjC,MAAM,OAAO,iBAAiB;IAG1B;QACI,kDAAkD;QAClD,IAAI,CAAC,GAAG,GAAG,IAAI,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,IAAY,EAAE,KAAiB,EAAE,QAAgB;QACjE,MAAM,QAAQ,GAAI,KAAK,CAAC,SAAoB,IAAI,EAAE,CAAC,CAAC,oBAAoB;QACxE,MAAM,IAAI,GAAI,KAAK,CAAC,WAAsB,IAAI,WAAW,CAAC,CAAC,eAAe;QAE1E,IAAI,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACvC,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACV,2CAA2C;YAC3C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9D,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,sBAAsB,CAAC,QAAgB,EAAE,KAAiB,EAAE,QAAgB;QACxE,mDAAmD;QACnD,uEAAuE;QACvE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBACf,WAAW,IAAI,CAAE,KAAK,CAAC,SAAoB,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,mCAAmC;gBAC7F,SAAS;YACb,CAAC;YACD,WAAW,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,WAAW,CAAC;IACvB,CAAC;CACJ;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC"}
@@ -1,64 +0,0 @@
1
- import PDFDocument from 'pdfkit';
2
- import { SlideStyle } from '../models/ir.js';
3
-
4
- export interface LayoutSlot {
5
- id: string;
6
- x: number;
7
- y: number;
8
- width: number;
9
- height: number;
10
- }
11
-
12
- export interface LayoutDefinition {
13
- id: string;
14
- slots: LayoutSlot[];
15
- }
16
-
17
- export class MetricsCalculator {
18
- private doc: PDFKit.PDFDocument;
19
-
20
- constructor() {
21
- // Create a headless PDF document for measurements
22
- this.doc = new PDFDocument({ size: [720, 405] });
23
- }
24
-
25
- /**
26
- * Calculates the height of a string given a font and width.
27
- */
28
- calculateTextHeight(text: string, style: SlideStyle, maxWidth: number): number {
29
- const fontSize = (style.font_size as number) || 18; // Default font size
30
- const font = (style.font_family as string) || 'Helvetica'; // Default font
31
-
32
- try {
33
- this.doc.font(font).fontSize(fontSize);
34
- return this.doc.heightOfString(text, { width: maxWidth });
35
- } catch (_e) {
36
- // Fallback font if the requested one fails
37
- this.doc.font('Helvetica').fontSize(fontSize);
38
- return this.doc.heightOfString(text, { width: maxWidth });
39
- }
40
- }
41
-
42
- /**
43
- * Estimates height for Markdown content.
44
- * For now, this is a simplified version that handles basic text and lists.
45
- */
46
- estimateMarkdownHeight(markdown: string, style: SlideStyle, maxWidth: number): number {
47
- // Simple heuristic: split by lines and sum heights
48
- // In a real implementation, this would handle actual markdown parsing.
49
- const lines = markdown.split('\n');
50
- let totalHeight = 0;
51
-
52
- for (const line of lines) {
53
- if (!line.trim()) {
54
- totalHeight += ((style.font_size as number) || 18) * 0.5; // Half line height for empty lines
55
- continue;
56
- }
57
- totalHeight += this.calculateTextHeight(line, style, maxWidth);
58
- }
59
-
60
- return totalHeight;
61
- }
62
- }
63
-
64
- export const metricsCalculator = new MetricsCalculator();