@ai-matrx/print 0.3.1 → 0.4.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/CHANGELOG.md +44 -0
- package/dist/certificate.cjs +746 -0
- package/dist/certificate.cjs.map +1 -0
- package/dist/certificate.d.cts +101 -0
- package/dist/certificate.d.ts +101 -0
- package/dist/certificate.js +723 -0
- package/dist/certificate.js.map +1 -0
- package/dist/education.cjs +928 -0
- package/dist/education.cjs.map +1 -0
- package/dist/education.d.cts +79 -0
- package/dist/education.d.ts +79 -0
- package/dist/education.js +905 -0
- package/dist/education.js.map +1 -0
- package/dist/exam.cjs +648 -0
- package/dist/exam.cjs.map +1 -0
- package/dist/exam.d.cts +69 -0
- package/dist/exam.d.ts +69 -0
- package/dist/exam.js +625 -0
- package/dist/exam.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/zpl.cjs +1039 -0
- package/dist/zpl.cjs.map +1 -0
- package/dist/zpl.d.cts +160 -0
- package/dist/zpl.d.ts +160 -0
- package/dist/zpl.js +1006 -0
- package/dist/zpl.js.map +1 -0
- package/package.json +41 -1
|
@@ -0,0 +1,905 @@
|
|
|
1
|
+
// src/core.ts
|
|
2
|
+
var PRINT_STYLES = `
|
|
3
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
4
|
+
|
|
5
|
+
body {
|
|
6
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
7
|
+
font-size: 11pt;
|
|
8
|
+
line-height: 1.6;
|
|
9
|
+
color: #1a1a1a;
|
|
10
|
+
background: #fff;
|
|
11
|
+
max-width: 860px;
|
|
12
|
+
margin: 0 auto;
|
|
13
|
+
padding: 28px 36px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.print-actions {
|
|
17
|
+
display: flex;
|
|
18
|
+
gap: 10px;
|
|
19
|
+
margin-bottom: 24px;
|
|
20
|
+
padding-bottom: 18px;
|
|
21
|
+
border-bottom: 2px solid #e5e7eb;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.print-btn {
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: 6px;
|
|
28
|
+
padding: 8px 20px;
|
|
29
|
+
border-radius: 8px;
|
|
30
|
+
font-size: 13px;
|
|
31
|
+
font-family: inherit;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
border: none;
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.print-btn-primary { background: #2563eb; color: #fff; }
|
|
38
|
+
.print-btn-secondary { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }
|
|
39
|
+
|
|
40
|
+
h1 { font-size: 22pt; font-weight: 700; margin-bottom: 0.5em; }
|
|
41
|
+
h2 { font-size: 16pt; font-weight: 700; margin: 1.2em 0 0.4em; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.2em; }
|
|
42
|
+
h3 { font-size: 13pt; font-weight: 600; margin: 1em 0 0.3em; }
|
|
43
|
+
p { margin-bottom: 0.7em; }
|
|
44
|
+
|
|
45
|
+
table {
|
|
46
|
+
width: 100%;
|
|
47
|
+
border-collapse: collapse;
|
|
48
|
+
margin: 1em 0;
|
|
49
|
+
font-size: 10pt;
|
|
50
|
+
}
|
|
51
|
+
th {
|
|
52
|
+
background: #1e293b;
|
|
53
|
+
color: #f8fafc;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
text-align: left;
|
|
56
|
+
padding: 7px 12px;
|
|
57
|
+
border: 1px solid #334155;
|
|
58
|
+
}
|
|
59
|
+
td {
|
|
60
|
+
padding: 6px 12px;
|
|
61
|
+
border: 1px solid #e2e8f0;
|
|
62
|
+
vertical-align: top;
|
|
63
|
+
}
|
|
64
|
+
tr:nth-child(even) td { background: #f8fafc; }
|
|
65
|
+
|
|
66
|
+
@media print {
|
|
67
|
+
body { max-width: 100%; padding: 0; }
|
|
68
|
+
.print-actions { display: none !important; }
|
|
69
|
+
th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
function escapeHtml(str) {
|
|
73
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
74
|
+
}
|
|
75
|
+
function buildPrintDocument(bodyHtml, title = "AI Response", extraStyles = "") {
|
|
76
|
+
return `<!DOCTYPE html>
|
|
77
|
+
<html lang="en">
|
|
78
|
+
<head>
|
|
79
|
+
<meta charset="UTF-8">
|
|
80
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
81
|
+
<title>${escapeHtml(title)}</title>
|
|
82
|
+
<style>
|
|
83
|
+
${PRINT_STYLES}
|
|
84
|
+
${extraStyles}
|
|
85
|
+
</style>
|
|
86
|
+
</head>
|
|
87
|
+
<body>
|
|
88
|
+
<div class="print-actions">
|
|
89
|
+
<button class="print-btn print-btn-primary" onclick="window.print()">Print / Save as PDF</button>
|
|
90
|
+
<button class="print-btn print-btn-secondary" onclick="window.close()">Close</button>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="content">
|
|
93
|
+
${bodyHtml}
|
|
94
|
+
</div>
|
|
95
|
+
</body>
|
|
96
|
+
</html>`;
|
|
97
|
+
}
|
|
98
|
+
function openPrintWindow(htmlDocument, filename = "print", windowFeatures = "width=920,height=720,scrollbars=yes") {
|
|
99
|
+
const win = window.open("", "_blank", windowFeatures);
|
|
100
|
+
if (!win) {
|
|
101
|
+
const blob = new Blob([htmlDocument], { type: "text/html;charset=utf-8" });
|
|
102
|
+
const url = URL.createObjectURL(blob);
|
|
103
|
+
const a = document.createElement("a");
|
|
104
|
+
a.href = url;
|
|
105
|
+
a.download = `${filename.replace(/\s+/g, "-").toLowerCase()}.html`;
|
|
106
|
+
document.body.appendChild(a);
|
|
107
|
+
a.click();
|
|
108
|
+
document.body.removeChild(a);
|
|
109
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
110
|
+
return "downloaded";
|
|
111
|
+
}
|
|
112
|
+
win.document.open();
|
|
113
|
+
win.document.write(htmlDocument);
|
|
114
|
+
win.document.close();
|
|
115
|
+
return "opened";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/education.ts
|
|
119
|
+
function isRecord(value) {
|
|
120
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
121
|
+
}
|
|
122
|
+
function readString(source, key) {
|
|
123
|
+
if (!isRecord(source)) return "";
|
|
124
|
+
const value = source[key];
|
|
125
|
+
return typeof value === "string" ? value : "";
|
|
126
|
+
}
|
|
127
|
+
function readArray(source, key) {
|
|
128
|
+
if (!isRecord(source)) return [];
|
|
129
|
+
const value = source[key];
|
|
130
|
+
return Array.isArray(value) ? value : [];
|
|
131
|
+
}
|
|
132
|
+
function readTitle(data, fallback) {
|
|
133
|
+
const title = readString(data, "title").trim();
|
|
134
|
+
return title.length > 0 ? title : fallback;
|
|
135
|
+
}
|
|
136
|
+
function readStringList(source, key) {
|
|
137
|
+
return readArray(source, key).filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
138
|
+
}
|
|
139
|
+
function extractCheatSheetSections(data) {
|
|
140
|
+
return readArray(data, "sections").map((raw) => {
|
|
141
|
+
const items = readArray(raw, "items").filter(isRecord).map(
|
|
142
|
+
(rawItem) => ({
|
|
143
|
+
term: readString(rawItem, "term"),
|
|
144
|
+
definition: readString(rawItem, "definition"),
|
|
145
|
+
formula: readString(rawItem, "formula"),
|
|
146
|
+
note: readString(rawItem, "note")
|
|
147
|
+
})
|
|
148
|
+
).filter(
|
|
149
|
+
(item) => item.term.trim().length > 0 || (item.definition ?? "").trim().length > 0 || (item.formula ?? "").trim().length > 0
|
|
150
|
+
);
|
|
151
|
+
return { heading: readString(raw, "heading"), items };
|
|
152
|
+
}).filter((section) => section.items.length > 0);
|
|
153
|
+
}
|
|
154
|
+
function extractGlossaryEntries(data) {
|
|
155
|
+
return readArray(data, "entries").filter(isRecord).map(
|
|
156
|
+
(raw) => ({
|
|
157
|
+
term: readString(raw, "term"),
|
|
158
|
+
definition: readString(raw, "definition")
|
|
159
|
+
})
|
|
160
|
+
).filter(
|
|
161
|
+
(entry) => entry.term.trim().length > 0 || entry.definition.trim().length > 0
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function extractStudyWeeks(data) {
|
|
165
|
+
return readArray(data, "weeks").map((rawWeek) => {
|
|
166
|
+
const days = readArray(rawWeek, "days").filter(isRecord).map((rawDay) => {
|
|
167
|
+
const milestone = readString(rawDay, "milestone");
|
|
168
|
+
return {
|
|
169
|
+
label: readString(rawDay, "label"),
|
|
170
|
+
tasks: readStringList(rawDay, "tasks"),
|
|
171
|
+
milestone
|
|
172
|
+
};
|
|
173
|
+
}).filter(
|
|
174
|
+
(day) => day.label.trim().length > 0 || day.tasks.length > 0 || (day.milestone ?? "").trim().length > 0
|
|
175
|
+
);
|
|
176
|
+
return { label: readString(rawWeek, "label"), days };
|
|
177
|
+
}).filter((week) => week.days.length > 0);
|
|
178
|
+
}
|
|
179
|
+
function boolSetting(raw, key, fallback) {
|
|
180
|
+
const value = raw?.[key];
|
|
181
|
+
return value === void 0 ? fallback : Boolean(value);
|
|
182
|
+
}
|
|
183
|
+
function numberSetting(raw, key) {
|
|
184
|
+
const value = raw?.[key];
|
|
185
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
186
|
+
}
|
|
187
|
+
function resolveRange(raw, fromKey, toKey, total) {
|
|
188
|
+
const rawFrom = numberSetting(raw, fromKey);
|
|
189
|
+
const rawTo = numberSetting(raw, toKey);
|
|
190
|
+
const start = rawFrom > 0 ? Math.min(rawFrom, total) : 1;
|
|
191
|
+
const end = rawTo > 0 ? Math.min(rawTo, total) : total;
|
|
192
|
+
const [from, to] = start <= end ? [start, end] : [end, start];
|
|
193
|
+
return { startIndex: from - 1, endIndex: to };
|
|
194
|
+
}
|
|
195
|
+
function printEmptyDocument(message, title, styles, filename) {
|
|
196
|
+
return openPrintWindow(
|
|
197
|
+
buildPrintDocument(`<p>${escapeHtml(message)}</p>`, title, styles),
|
|
198
|
+
filename
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
var CHECKBOX_HTML = `<span class="edu-check" aria-hidden="true"></span>`;
|
|
202
|
+
var EDU_BASE_STYLES = `
|
|
203
|
+
h1.edu-title {
|
|
204
|
+
font-size: 15pt !important;
|
|
205
|
+
font-weight: 700;
|
|
206
|
+
color: #0f172a;
|
|
207
|
+
margin: 0 0 3px 0;
|
|
208
|
+
letter-spacing: -0.01em;
|
|
209
|
+
border-bottom: none !important;
|
|
210
|
+
}
|
|
211
|
+
.edu-subtitle {
|
|
212
|
+
font-size: 9pt;
|
|
213
|
+
color: #475569;
|
|
214
|
+
margin: 0;
|
|
215
|
+
}
|
|
216
|
+
.edu-meta {
|
|
217
|
+
font-size: 8pt;
|
|
218
|
+
color: #64748b;
|
|
219
|
+
margin-top: 2px;
|
|
220
|
+
}
|
|
221
|
+
.edu-header {
|
|
222
|
+
border-bottom: 1.5px solid #cbd5e1;
|
|
223
|
+
padding-bottom: 8px;
|
|
224
|
+
margin-bottom: 12px;
|
|
225
|
+
}
|
|
226
|
+
.edu-hint {
|
|
227
|
+
font-size: 8pt;
|
|
228
|
+
color: #475569;
|
|
229
|
+
background: #f1f5f9;
|
|
230
|
+
border: 1px solid #cbd5e1;
|
|
231
|
+
border-radius: 6px;
|
|
232
|
+
padding: 6px 10px;
|
|
233
|
+
margin-bottom: 12px;
|
|
234
|
+
line-height: 1.45;
|
|
235
|
+
}
|
|
236
|
+
.edu-check {
|
|
237
|
+
display: inline-block;
|
|
238
|
+
width: 9pt;
|
|
239
|
+
height: 9pt;
|
|
240
|
+
border: 1px solid #64748b;
|
|
241
|
+
border-radius: 2px;
|
|
242
|
+
background: #fff;
|
|
243
|
+
vertical-align: -1px;
|
|
244
|
+
margin-right: 6px;
|
|
245
|
+
flex: 0 0 auto;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@media print {
|
|
249
|
+
@page { margin: 12mm 10mm; }
|
|
250
|
+
.edu-hint { display: none !important; }
|
|
251
|
+
.edu-check {
|
|
252
|
+
-webkit-print-color-adjust: exact;
|
|
253
|
+
print-color-adjust: exact;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
`;
|
|
257
|
+
var CHEAT_SHEET_STYLES = `
|
|
258
|
+
${EDU_BASE_STYLES}
|
|
259
|
+
|
|
260
|
+
.cs-sheet {
|
|
261
|
+
column-gap: 14px;
|
|
262
|
+
column-fill: auto;
|
|
263
|
+
}
|
|
264
|
+
.cs-cols-2 { column-count: 2; font-size: 8.5pt; }
|
|
265
|
+
.cs-cols-3 { column-count: 3; font-size: 7.5pt; }
|
|
266
|
+
.cs-cols-1 { column-count: 1; font-size: 11.5pt; }
|
|
267
|
+
|
|
268
|
+
.cs-section {
|
|
269
|
+
break-inside: auto;
|
|
270
|
+
page-break-inside: auto;
|
|
271
|
+
margin-bottom: 10px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Section headings are dense BARS, not airy titles \u2014 the hierarchy that
|
|
275
|
+
keeps a one-pager scannable instead of a wall of text. */
|
|
276
|
+
.cs-section-bar {
|
|
277
|
+
display: block;
|
|
278
|
+
background: #1e293b;
|
|
279
|
+
color: #f8fafc;
|
|
280
|
+
font-size: 8pt !important;
|
|
281
|
+
font-weight: 700;
|
|
282
|
+
letter-spacing: 0.07em;
|
|
283
|
+
text-transform: uppercase;
|
|
284
|
+
padding: 3px 7px;
|
|
285
|
+
margin: 0 0 5px 0 !important;
|
|
286
|
+
border-radius: 3px;
|
|
287
|
+
border-bottom: none !important;
|
|
288
|
+
break-after: avoid;
|
|
289
|
+
page-break-after: avoid;
|
|
290
|
+
}
|
|
291
|
+
.cs-cols-1 .cs-section-bar { font-size: 11pt !important; padding: 5px 10px; }
|
|
292
|
+
|
|
293
|
+
.cs-section-rule {
|
|
294
|
+
height: 0;
|
|
295
|
+
border-bottom: 1px solid #cbd5e1;
|
|
296
|
+
margin: 0 0 6px 0;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.cs-item {
|
|
300
|
+
break-inside: avoid;
|
|
301
|
+
page-break-inside: avoid;
|
|
302
|
+
margin-bottom: 6px;
|
|
303
|
+
padding-left: 5px;
|
|
304
|
+
border-left: 2px solid #e2e8f0;
|
|
305
|
+
line-height: 1.4;
|
|
306
|
+
}
|
|
307
|
+
.cs-term {
|
|
308
|
+
font-weight: 700;
|
|
309
|
+
color: #0f172a;
|
|
310
|
+
}
|
|
311
|
+
.cs-def {
|
|
312
|
+
color: #334155;
|
|
313
|
+
}
|
|
314
|
+
.cs-note {
|
|
315
|
+
color: #64748b;
|
|
316
|
+
font-style: italic;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Boxed formula \u2014 mono + border is how every leader prints an equation. */
|
|
320
|
+
code.cs-formula {
|
|
321
|
+
display: block;
|
|
322
|
+
font-family: "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
|
|
323
|
+
font-size: 0.95em;
|
|
324
|
+
background: #f8fafc;
|
|
325
|
+
border: 1px solid #94a3b8;
|
|
326
|
+
border-radius: 3px;
|
|
327
|
+
padding: 2px 5px;
|
|
328
|
+
margin: 3px 0;
|
|
329
|
+
white-space: pre-wrap;
|
|
330
|
+
word-break: break-word;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Compact: tighter leading and a smaller floor for a true single sheet. */
|
|
334
|
+
.cs-compact.cs-cols-2 { font-size: 7.5pt; }
|
|
335
|
+
.cs-compact.cs-cols-3 { font-size: 6.5pt; }
|
|
336
|
+
.cs-compact.cs-cols-1 { font-size: 10pt; }
|
|
337
|
+
.cs-compact .cs-item { margin-bottom: 3px; line-height: 1.25; }
|
|
338
|
+
.cs-compact .cs-section { margin-bottom: 6px; }
|
|
339
|
+
.cs-compact .cs-section-bar { padding: 2px 6px; margin-bottom: 3px !important; }
|
|
340
|
+
|
|
341
|
+
@media print {
|
|
342
|
+
.cs-section-bar {
|
|
343
|
+
-webkit-print-color-adjust: exact;
|
|
344
|
+
print-color-adjust: exact;
|
|
345
|
+
}
|
|
346
|
+
code.cs-formula {
|
|
347
|
+
-webkit-print-color-adjust: exact;
|
|
348
|
+
print-color-adjust: exact;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
`;
|
|
352
|
+
var CHEAT_SHEET_COLUMN_CLASS = {
|
|
353
|
+
"two-column": "cs-cols-2",
|
|
354
|
+
"three-column": "cs-cols-3",
|
|
355
|
+
"one-column": "cs-cols-1"
|
|
356
|
+
};
|
|
357
|
+
function renderCheatSheetItem(item) {
|
|
358
|
+
const parts = [];
|
|
359
|
+
const term = item.term.trim();
|
|
360
|
+
const definition = (item.definition ?? "").trim();
|
|
361
|
+
const formula = (item.formula ?? "").trim();
|
|
362
|
+
const note = (item.note ?? "").trim();
|
|
363
|
+
if (term) parts.push(`<span class="cs-term">${escapeHtml(term)}</span>`);
|
|
364
|
+
if (definition) {
|
|
365
|
+
parts.push(
|
|
366
|
+
`${term ? " " : ""}<span class="cs-def">${escapeHtml(definition)}</span>`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
if (formula) {
|
|
370
|
+
parts.push(`<code class="cs-formula">${escapeHtml(formula)}</code>`);
|
|
371
|
+
}
|
|
372
|
+
if (note) parts.push(`<div class="cs-note">${escapeHtml(note)}</div>`);
|
|
373
|
+
return `<div class="cs-item">${parts.join("")}</div>`;
|
|
374
|
+
}
|
|
375
|
+
function renderCheatSheet(data, variant, opts) {
|
|
376
|
+
const itemCount = data.sections.reduce(
|
|
377
|
+
(total, section) => total + section.items.length,
|
|
378
|
+
0
|
|
379
|
+
);
|
|
380
|
+
const subtitle = (data.subtitle ?? "").trim();
|
|
381
|
+
const sections = data.sections.map((section) => {
|
|
382
|
+
const heading = section.heading.trim();
|
|
383
|
+
const bar = heading ? `<h2 class="cs-section-bar">${escapeHtml(heading)}</h2>` : "";
|
|
384
|
+
const rule = opts.showSectionRules ? `<div class="cs-section-rule"></div>` : "";
|
|
385
|
+
const items = section.items.map(renderCheatSheetItem).join("\n");
|
|
386
|
+
return `<section class="cs-section">
|
|
387
|
+
${bar}${rule}
|
|
388
|
+
${items}
|
|
389
|
+
</section>`;
|
|
390
|
+
}).join("\n");
|
|
391
|
+
const hint = opts.duplexHint ? `<div class="edu-hint">Two-sided sheet: print double-sided, flip on <strong>long edge</strong>, 100% scale, no margins \u2014 then trim or slip it into a sleeve.</div>` : "";
|
|
392
|
+
const columnClass = CHEAT_SHEET_COLUMN_CLASS[variant];
|
|
393
|
+
const compactClass = opts.compact ? " cs-compact" : "";
|
|
394
|
+
return `<div class="edu-header">
|
|
395
|
+
<h1 class="edu-title">${escapeHtml(data.title)}</h1>
|
|
396
|
+
${subtitle ? `<p class="edu-subtitle">${escapeHtml(subtitle)}</p>` : ""}
|
|
397
|
+
<div class="edu-meta">${data.sections.length} section${data.sections.length !== 1 ? "s" : ""} \xB7 ${itemCount} item${itemCount !== 1 ? "s" : ""}</div>
|
|
398
|
+
</div>
|
|
399
|
+
${hint}
|
|
400
|
+
<div class="cs-sheet ${columnClass}${compactClass}">
|
|
401
|
+
${sections}
|
|
402
|
+
</div>`;
|
|
403
|
+
}
|
|
404
|
+
var cheatSheetPrinter = {
|
|
405
|
+
label: "Print cheat sheet",
|
|
406
|
+
variants: [
|
|
407
|
+
{
|
|
408
|
+
id: "two-column",
|
|
409
|
+
label: "Two column (recommended)",
|
|
410
|
+
description: "The classic dense one-pager \u2014 two columns, boxed formulas, section bars. Fits a surprising amount on a single sheet and still reads at arm's length"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: "three-column",
|
|
414
|
+
label: "Three column (ultra-dense)",
|
|
415
|
+
description: "Maximum content per sheet \u2014 three narrow columns at a smaller type size. Best for a reference you scan rather than read"
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: "one-column",
|
|
419
|
+
label: "Single column (large print)",
|
|
420
|
+
description: "One wide column at a comfortable reading size \u2014 for studying, for a wall, or for anyone who needs bigger type"
|
|
421
|
+
}
|
|
422
|
+
],
|
|
423
|
+
settings: [
|
|
424
|
+
{
|
|
425
|
+
type: "boolean",
|
|
426
|
+
id: "showSectionRules",
|
|
427
|
+
label: "Rule under each section",
|
|
428
|
+
description: "Draws a hairline under every section heading \u2014 extra separation when sections run together",
|
|
429
|
+
defaultValue: true
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
type: "boolean",
|
|
433
|
+
id: "compact",
|
|
434
|
+
label: "Compact",
|
|
435
|
+
description: "Tighter leading and a smaller type floor \u2014 use when the sheet spills onto a second page",
|
|
436
|
+
defaultValue: false
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
type: "boolean",
|
|
440
|
+
id: "duplexHint",
|
|
441
|
+
label: "Show double-sided printing tip",
|
|
442
|
+
description: "Adds an on-screen note with the duplex settings for a two-sided sheet. Never printed",
|
|
443
|
+
defaultValue: false
|
|
444
|
+
}
|
|
445
|
+
],
|
|
446
|
+
print(data, variantId = "two-column", rawSettings) {
|
|
447
|
+
const title = readTitle(data, "Cheat Sheet");
|
|
448
|
+
const sections = extractCheatSheetSections(data);
|
|
449
|
+
if (sections.length === 0) {
|
|
450
|
+
return printEmptyDocument(
|
|
451
|
+
"No cheat sheet data available to print.",
|
|
452
|
+
title,
|
|
453
|
+
CHEAT_SHEET_STYLES,
|
|
454
|
+
"cheat-sheet"
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const variant = variantId === "three-column" || variantId === "one-column" ? variantId : "two-column";
|
|
458
|
+
const subtitle = readString(data, "subtitle");
|
|
459
|
+
const body = renderCheatSheet(
|
|
460
|
+
{ title, subtitle, sections },
|
|
461
|
+
variant,
|
|
462
|
+
{
|
|
463
|
+
showSectionRules: boolSetting(rawSettings, "showSectionRules", true),
|
|
464
|
+
compact: boolSetting(rawSettings, "compact", false),
|
|
465
|
+
duplexHint: boolSetting(rawSettings, "duplexHint", false)
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
return openPrintWindow(
|
|
469
|
+
buildPrintDocument(body, title, CHEAT_SHEET_STYLES),
|
|
470
|
+
"cheat-sheet"
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
var GLOSSARY_STYLES = `
|
|
475
|
+
${EDU_BASE_STYLES}
|
|
476
|
+
|
|
477
|
+
table.gl-table {
|
|
478
|
+
width: 100%;
|
|
479
|
+
border-collapse: collapse;
|
|
480
|
+
margin-top: 4px;
|
|
481
|
+
font-size: 9.5pt;
|
|
482
|
+
}
|
|
483
|
+
table.gl-table thead th {
|
|
484
|
+
background: #1e293b;
|
|
485
|
+
color: #f8fafc;
|
|
486
|
+
text-align: left;
|
|
487
|
+
font-size: 8pt;
|
|
488
|
+
font-weight: 700;
|
|
489
|
+
letter-spacing: 0.06em;
|
|
490
|
+
text-transform: uppercase;
|
|
491
|
+
padding: 5px 9px;
|
|
492
|
+
border: none;
|
|
493
|
+
}
|
|
494
|
+
table.gl-table th.gl-th-check { width: 22px; }
|
|
495
|
+
table.gl-table th.gl-th-term { width: 32%; }
|
|
496
|
+
|
|
497
|
+
table.gl-table tbody tr.gl-row {
|
|
498
|
+
break-inside: avoid;
|
|
499
|
+
page-break-inside: avoid;
|
|
500
|
+
}
|
|
501
|
+
table.gl-table tbody td {
|
|
502
|
+
padding: 4px 9px;
|
|
503
|
+
border-bottom: 1px solid #e2e8f0;
|
|
504
|
+
vertical-align: top;
|
|
505
|
+
line-height: 1.4;
|
|
506
|
+
background: transparent;
|
|
507
|
+
}
|
|
508
|
+
table.gl-table tbody tr:nth-child(even) td { background: #f8fafc; }
|
|
509
|
+
td.gl-check-cell { width: 22px; text-align: center; }
|
|
510
|
+
td.gl-term-cell { font-weight: 700; color: #0f172a; }
|
|
511
|
+
.gl-index {
|
|
512
|
+
display: inline-block;
|
|
513
|
+
min-width: 20px;
|
|
514
|
+
font-size: 8pt;
|
|
515
|
+
font-weight: 400;
|
|
516
|
+
color: #94a3b8;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
ul.gl-list {
|
|
520
|
+
column-count: 2;
|
|
521
|
+
column-gap: 18px;
|
|
522
|
+
list-style: none;
|
|
523
|
+
margin: 4px 0 0 0;
|
|
524
|
+
padding: 0;
|
|
525
|
+
font-size: 8.5pt;
|
|
526
|
+
}
|
|
527
|
+
ul.gl-list li.gl-entry {
|
|
528
|
+
break-inside: avoid;
|
|
529
|
+
page-break-inside: avoid;
|
|
530
|
+
margin-bottom: 5px;
|
|
531
|
+
padding-left: 4px;
|
|
532
|
+
border-left: 2px solid #e2e8f0;
|
|
533
|
+
line-height: 1.35;
|
|
534
|
+
}
|
|
535
|
+
.gl-term { font-weight: 700; color: #0f172a; }
|
|
536
|
+
.gl-def { color: #334155; }
|
|
537
|
+
|
|
538
|
+
@media print {
|
|
539
|
+
table.gl-table thead th {
|
|
540
|
+
-webkit-print-color-adjust: exact;
|
|
541
|
+
print-color-adjust: exact;
|
|
542
|
+
}
|
|
543
|
+
table.gl-table tbody tr:nth-child(even) td {
|
|
544
|
+
-webkit-print-color-adjust: exact;
|
|
545
|
+
print-color-adjust: exact;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
`;
|
|
549
|
+
function orderGlossaryEntries(entries, alphabetize) {
|
|
550
|
+
if (!alphabetize) return entries;
|
|
551
|
+
return [...entries].sort(
|
|
552
|
+
(a, b) => a.term.localeCompare(b.term, void 0, { sensitivity: "base" })
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
function renderGlossaryTable(entries, opts) {
|
|
556
|
+
const rows = entries.map((entry, i) => {
|
|
557
|
+
const check = opts.masteredCheckbox ? `<td class="gl-check-cell">${CHECKBOX_HTML}</td>` : "";
|
|
558
|
+
const index = opts.showIndex ? `<span class="gl-index">${opts.startIndex + i + 1}.</span>` : "";
|
|
559
|
+
return ` <tr class="gl-row">
|
|
560
|
+
${check ? ` ${check}
|
|
561
|
+
` : ""} <td class="gl-term-cell">${index}${escapeHtml(entry.term)}</td>
|
|
562
|
+
<td class="gl-def">${escapeHtml(entry.definition)}</td>
|
|
563
|
+
</tr>`;
|
|
564
|
+
}).join("\n");
|
|
565
|
+
const checkHead = opts.masteredCheckbox ? `<th class="gl-th-check">\u2713</th>` : "";
|
|
566
|
+
return `<table class="gl-table">
|
|
567
|
+
<thead>
|
|
568
|
+
<tr>
|
|
569
|
+
${checkHead}<th class="gl-th-term">Term</th>
|
|
570
|
+
<th>Definition</th>
|
|
571
|
+
</tr>
|
|
572
|
+
</thead>
|
|
573
|
+
<tbody>
|
|
574
|
+
${rows}
|
|
575
|
+
</tbody>
|
|
576
|
+
</table>`;
|
|
577
|
+
}
|
|
578
|
+
function renderGlossaryList(entries, opts) {
|
|
579
|
+
const items = entries.map((entry, i) => {
|
|
580
|
+
const check = opts.masteredCheckbox ? CHECKBOX_HTML : "";
|
|
581
|
+
const index = opts.showIndex ? `<span class="gl-index">${opts.startIndex + i + 1}.</span>` : "";
|
|
582
|
+
return ` <li class="gl-entry">${check}${index}<span class="gl-term">${escapeHtml(entry.term)}</span> \u2014 <span class="gl-def">${escapeHtml(entry.definition)}</span></li>`;
|
|
583
|
+
}).join("\n");
|
|
584
|
+
return `<ul class="gl-list">
|
|
585
|
+
${items}
|
|
586
|
+
</ul>`;
|
|
587
|
+
}
|
|
588
|
+
var glossaryPrinter = {
|
|
589
|
+
label: "Print glossary",
|
|
590
|
+
variants: [
|
|
591
|
+
{
|
|
592
|
+
id: "table",
|
|
593
|
+
label: "Term / definition table (recommended)",
|
|
594
|
+
description: "Two-column table with a mastered checkbox per row \u2014 the Kaplan/Quizlet vocabulary sheet"
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
id: "compact-list",
|
|
598
|
+
label: "Compact list",
|
|
599
|
+
description: "Two columns of one-line entries \u2014 many more terms per sheet, for a final review pass"
|
|
600
|
+
}
|
|
601
|
+
],
|
|
602
|
+
settings: [
|
|
603
|
+
{
|
|
604
|
+
type: "range",
|
|
605
|
+
id: "entryRange",
|
|
606
|
+
label: "Entries to print",
|
|
607
|
+
description: "Leave blank to print the whole list",
|
|
608
|
+
fromId: "fromEntry",
|
|
609
|
+
toId: "toEntry",
|
|
610
|
+
defaultFrom: 0,
|
|
611
|
+
defaultTo: 0,
|
|
612
|
+
min: 1,
|
|
613
|
+
fromPlaceholder: "1",
|
|
614
|
+
toPlaceholder: "end"
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
type: "boolean",
|
|
618
|
+
id: "alphabetize",
|
|
619
|
+
label: "Alphabetize",
|
|
620
|
+
description: "Sorts by term. Off prints the entries in the order they were authored",
|
|
621
|
+
defaultValue: false
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
type: "boolean",
|
|
625
|
+
id: "masteredCheckbox",
|
|
626
|
+
label: "Mastered checkbox",
|
|
627
|
+
description: "An empty box on every row to tick off as each term is learned",
|
|
628
|
+
defaultValue: true
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
type: "boolean",
|
|
632
|
+
id: "showIndex",
|
|
633
|
+
label: "Number the entries",
|
|
634
|
+
description: "Prints 1., 2., 3. \u2026 so a study partner can call out terms by number",
|
|
635
|
+
defaultValue: false
|
|
636
|
+
}
|
|
637
|
+
],
|
|
638
|
+
print(data, variantId = "table", rawSettings) {
|
|
639
|
+
const title = readTitle(data, "Glossary");
|
|
640
|
+
const allEntries = extractGlossaryEntries(data);
|
|
641
|
+
if (allEntries.length === 0) {
|
|
642
|
+
return printEmptyDocument(
|
|
643
|
+
"No glossary data available to print.",
|
|
644
|
+
title,
|
|
645
|
+
GLOSSARY_STYLES,
|
|
646
|
+
"glossary"
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
const ordered = orderGlossaryEntries(
|
|
650
|
+
allEntries,
|
|
651
|
+
boolSetting(rawSettings, "alphabetize", false)
|
|
652
|
+
);
|
|
653
|
+
const { startIndex, endIndex } = resolveRange(
|
|
654
|
+
rawSettings,
|
|
655
|
+
"fromEntry",
|
|
656
|
+
"toEntry",
|
|
657
|
+
ordered.length
|
|
658
|
+
);
|
|
659
|
+
const entries = ordered.slice(startIndex, endIndex);
|
|
660
|
+
const opts = {
|
|
661
|
+
masteredCheckbox: boolSetting(rawSettings, "masteredCheckbox", true),
|
|
662
|
+
showIndex: boolSetting(rawSettings, "showIndex", false),
|
|
663
|
+
startIndex
|
|
664
|
+
};
|
|
665
|
+
const variant = variantId === "compact-list" ? "compact-list" : "table";
|
|
666
|
+
const body = variant === "compact-list" ? renderGlossaryList(entries, opts) : renderGlossaryTable(entries, opts);
|
|
667
|
+
const header = `<div class="edu-header">
|
|
668
|
+
<h1 class="edu-title">${escapeHtml(title)}</h1>
|
|
669
|
+
<div class="edu-meta">${entries.length} term${entries.length !== 1 ? "s" : ""}${entries.length !== allEntries.length ? ` of ${allEntries.length}` : ""}</div>
|
|
670
|
+
</div>`;
|
|
671
|
+
return openPrintWindow(
|
|
672
|
+
buildPrintDocument(`${header}
|
|
673
|
+
${body}`, title, GLOSSARY_STYLES),
|
|
674
|
+
"glossary"
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
var STUDY_CALENDAR_STYLES = `
|
|
679
|
+
${EDU_BASE_STYLES}
|
|
680
|
+
|
|
681
|
+
/* A week is the unit that must never split across pages when it fits. */
|
|
682
|
+
.sc-week {
|
|
683
|
+
break-inside: avoid;
|
|
684
|
+
page-break-inside: avoid;
|
|
685
|
+
margin-bottom: 12px;
|
|
686
|
+
border: 1px solid #cbd5e1;
|
|
687
|
+
border-radius: 5px;
|
|
688
|
+
overflow: hidden;
|
|
689
|
+
}
|
|
690
|
+
.sc-week-label {
|
|
691
|
+
background: #1e293b;
|
|
692
|
+
color: #f8fafc;
|
|
693
|
+
font-size: 9pt !important;
|
|
694
|
+
font-weight: 700;
|
|
695
|
+
letter-spacing: 0.06em;
|
|
696
|
+
text-transform: uppercase;
|
|
697
|
+
padding: 4px 9px;
|
|
698
|
+
margin: 0 !important;
|
|
699
|
+
border-bottom: none !important;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.sc-days-rows .sc-day {
|
|
703
|
+
display: block;
|
|
704
|
+
border-top: 1px solid #e2e8f0;
|
|
705
|
+
padding: 5px 9px;
|
|
706
|
+
}
|
|
707
|
+
.sc-days-rows .sc-day:first-child { border-top: none; }
|
|
708
|
+
|
|
709
|
+
.sc-days-grid {
|
|
710
|
+
display: grid;
|
|
711
|
+
grid-auto-flow: column;
|
|
712
|
+
grid-auto-columns: 1fr;
|
|
713
|
+
}
|
|
714
|
+
.sc-days-grid .sc-day {
|
|
715
|
+
border-left: 1px solid #e2e8f0;
|
|
716
|
+
padding: 5px 7px;
|
|
717
|
+
min-height: 1.1in;
|
|
718
|
+
}
|
|
719
|
+
.sc-days-grid .sc-day:first-child { border-left: none; }
|
|
720
|
+
|
|
721
|
+
.sc-day-label {
|
|
722
|
+
font-size: 8pt;
|
|
723
|
+
font-weight: 700;
|
|
724
|
+
color: #0f172a;
|
|
725
|
+
letter-spacing: 0.04em;
|
|
726
|
+
text-transform: uppercase;
|
|
727
|
+
margin-bottom: 3px;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
ul.sc-tasks {
|
|
731
|
+
list-style: none;
|
|
732
|
+
margin: 0;
|
|
733
|
+
padding: 0;
|
|
734
|
+
font-size: 8.5pt;
|
|
735
|
+
}
|
|
736
|
+
li.sc-task {
|
|
737
|
+
display: flex;
|
|
738
|
+
align-items: flex-start;
|
|
739
|
+
line-height: 1.35;
|
|
740
|
+
margin-bottom: 2px;
|
|
741
|
+
break-inside: avoid;
|
|
742
|
+
page-break-inside: avoid;
|
|
743
|
+
color: #334155;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/* Milestones are the thing a student actually plans around \u2014 they are
|
|
747
|
+
visually LOUD and survive a black-and-white printer. */
|
|
748
|
+
.sc-milestone {
|
|
749
|
+
margin-top: 4px;
|
|
750
|
+
border: 1.5px solid #b45309;
|
|
751
|
+
border-left-width: 5px;
|
|
752
|
+
background: #fef3c7;
|
|
753
|
+
color: #78350f;
|
|
754
|
+
font-size: 8pt;
|
|
755
|
+
font-weight: 700;
|
|
756
|
+
padding: 3px 7px;
|
|
757
|
+
border-radius: 3px;
|
|
758
|
+
line-height: 1.3;
|
|
759
|
+
}
|
|
760
|
+
.sc-milestone-flag {
|
|
761
|
+
letter-spacing: 0.08em;
|
|
762
|
+
text-transform: uppercase;
|
|
763
|
+
margin-right: 5px;
|
|
764
|
+
font-size: 7pt;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.sc-empty-day { font-size: 8pt; color: #94a3b8; font-style: italic; }
|
|
768
|
+
|
|
769
|
+
@media print {
|
|
770
|
+
.sc-week-label,
|
|
771
|
+
.sc-milestone {
|
|
772
|
+
-webkit-print-color-adjust: exact;
|
|
773
|
+
print-color-adjust: exact;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
`;
|
|
777
|
+
var STUDY_CALENDAR_LANDSCAPE_STYLES = `
|
|
778
|
+
${STUDY_CALENDAR_STYLES}
|
|
779
|
+
|
|
780
|
+
@media print {
|
|
781
|
+
@page { size: landscape; margin: 10mm; }
|
|
782
|
+
}
|
|
783
|
+
`;
|
|
784
|
+
function renderStudyDay(day, opts) {
|
|
785
|
+
const label = day.label.trim();
|
|
786
|
+
const milestone = (day.milestone ?? "").trim();
|
|
787
|
+
const tasks = day.tasks.length > 0 ? `<ul class="sc-tasks">
|
|
788
|
+
${day.tasks.map(
|
|
789
|
+
(task) => ` <li class="sc-task">${opts.taskCheckbox ? CHECKBOX_HTML : ""}<span class="sc-task-text">${escapeHtml(task)}</span></li>`
|
|
790
|
+
).join("\n")}
|
|
791
|
+
</ul>` : `<div class="sc-empty-day">Rest / catch-up</div>`;
|
|
792
|
+
const milestoneHtml = opts.showMilestones && milestone ? `
|
|
793
|
+
<div class="sc-milestone"><span class="sc-milestone-flag">Milestone</span>${escapeHtml(milestone)}</div>` : "";
|
|
794
|
+
return ` <div class="sc-day">
|
|
795
|
+
${label ? `<div class="sc-day-label">${escapeHtml(label)}</div>` : ""}
|
|
796
|
+
${tasks}${milestoneHtml}
|
|
797
|
+
</div>`;
|
|
798
|
+
}
|
|
799
|
+
function renderStudyCalendar(data, variant, opts) {
|
|
800
|
+
const daysClass = variant === "landscape-grid" ? "sc-days sc-days-grid" : "sc-days sc-days-rows";
|
|
801
|
+
const subtitle = (data.subtitle ?? "").trim();
|
|
802
|
+
const taskCount = data.weeks.reduce(
|
|
803
|
+
(total, week) => total + week.days.reduce((sum, day) => sum + day.tasks.length, 0),
|
|
804
|
+
0
|
|
805
|
+
);
|
|
806
|
+
const weeks = data.weeks.map((week) => {
|
|
807
|
+
const label = week.label.trim();
|
|
808
|
+
return `<section class="sc-week">
|
|
809
|
+
${label ? `<h2 class="sc-week-label">${escapeHtml(label)}</h2>` : ""}
|
|
810
|
+
<div class="${daysClass}">
|
|
811
|
+
${week.days.map((day) => renderStudyDay(day, opts)).join("\n")}
|
|
812
|
+
</div>
|
|
813
|
+
</section>`;
|
|
814
|
+
}).join("\n");
|
|
815
|
+
const hint = variant === "landscape-grid" ? `<div class="edu-hint">Set your printer to <strong>Landscape</strong> \u2014 each week prints as a row of day columns.</div>` : "";
|
|
816
|
+
return `<div class="edu-header">
|
|
817
|
+
<h1 class="edu-title">${escapeHtml(data.title)}</h1>
|
|
818
|
+
${subtitle ? `<p class="edu-subtitle">${escapeHtml(subtitle)}</p>` : ""}
|
|
819
|
+
<div class="edu-meta">${data.weeks.length} week${data.weeks.length !== 1 ? "s" : ""} \xB7 ${taskCount} task${taskCount !== 1 ? "s" : ""}</div>
|
|
820
|
+
</div>
|
|
821
|
+
${hint}
|
|
822
|
+
<div class="sc-plan">
|
|
823
|
+
${weeks}
|
|
824
|
+
</div>`;
|
|
825
|
+
}
|
|
826
|
+
var studyCalendarPrinter = {
|
|
827
|
+
label: "Print study calendar",
|
|
828
|
+
variants: [
|
|
829
|
+
{
|
|
830
|
+
id: "week-rows",
|
|
831
|
+
label: "Week per block (portrait, recommended)",
|
|
832
|
+
description: "Each week is one block with its days stacked as rows \u2014 the most room for tasks, and a week never splits across pages when it fits"
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
id: "landscape-grid",
|
|
836
|
+
label: "Calendar grid (landscape)",
|
|
837
|
+
description: "Each week is a row of day columns, calendar-style \u2014 best for a wall or a binder pocket"
|
|
838
|
+
}
|
|
839
|
+
],
|
|
840
|
+
settings: [
|
|
841
|
+
{
|
|
842
|
+
type: "range",
|
|
843
|
+
id: "weekRange",
|
|
844
|
+
label: "Weeks to print",
|
|
845
|
+
description: "Leave blank to print the whole plan",
|
|
846
|
+
fromId: "fromWeek",
|
|
847
|
+
toId: "toWeek",
|
|
848
|
+
defaultFrom: 0,
|
|
849
|
+
defaultTo: 0,
|
|
850
|
+
min: 1,
|
|
851
|
+
fromPlaceholder: "1",
|
|
852
|
+
toPlaceholder: "end"
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
type: "boolean",
|
|
856
|
+
id: "taskCheckbox",
|
|
857
|
+
label: "Checkbox per task",
|
|
858
|
+
description: "An empty box in front of every task to tick off as it is done",
|
|
859
|
+
defaultValue: true
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
type: "boolean",
|
|
863
|
+
id: "showMilestones",
|
|
864
|
+
label: "Show milestones",
|
|
865
|
+
description: "Prints each milestone as a bordered banner on its day \u2014 practice tests, deadlines, review checkpoints",
|
|
866
|
+
defaultValue: true
|
|
867
|
+
}
|
|
868
|
+
],
|
|
869
|
+
print(data, variantId = "week-rows", rawSettings) {
|
|
870
|
+
const title = readTitle(data, "Study Calendar");
|
|
871
|
+
const allWeeks = extractStudyWeeks(data);
|
|
872
|
+
const variant = variantId === "landscape-grid" ? "landscape-grid" : "week-rows";
|
|
873
|
+
const styles = variant === "landscape-grid" ? STUDY_CALENDAR_LANDSCAPE_STYLES : STUDY_CALENDAR_STYLES;
|
|
874
|
+
if (allWeeks.length === 0) {
|
|
875
|
+
return printEmptyDocument(
|
|
876
|
+
"No study calendar data available to print.",
|
|
877
|
+
title,
|
|
878
|
+
styles,
|
|
879
|
+
"study-calendar"
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
const { startIndex, endIndex } = resolveRange(
|
|
883
|
+
rawSettings,
|
|
884
|
+
"fromWeek",
|
|
885
|
+
"toWeek",
|
|
886
|
+
allWeeks.length
|
|
887
|
+
);
|
|
888
|
+
const weeks = allWeeks.slice(startIndex, endIndex);
|
|
889
|
+
const subtitle = readString(data, "subtitle");
|
|
890
|
+
const body = renderStudyCalendar({ title, subtitle, weeks }, variant, {
|
|
891
|
+
taskCheckbox: boolSetting(rawSettings, "taskCheckbox", true),
|
|
892
|
+
showMilestones: boolSetting(rawSettings, "showMilestones", true)
|
|
893
|
+
});
|
|
894
|
+
return openPrintWindow(
|
|
895
|
+
buildPrintDocument(body, title, styles),
|
|
896
|
+
"study-calendar"
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
export {
|
|
901
|
+
cheatSheetPrinter,
|
|
902
|
+
glossaryPrinter,
|
|
903
|
+
studyCalendarPrinter
|
|
904
|
+
};
|
|
905
|
+
//# sourceMappingURL=education.js.map
|