@kamishibai/sdk 0.1.1
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/README.md +192 -0
- package/package.json +54 -0
- package/src/blocks/board.js +210 -0
- package/src/blocks/callout.js +63 -0
- package/src/blocks/code.js +28 -0
- package/src/blocks/deck.js +76 -0
- package/src/blocks/diagram.js +265 -0
- package/src/blocks/element.js +51 -0
- package/src/blocks/graph.js +264 -0
- package/src/blocks/grid.js +156 -0
- package/src/blocks/index.js +106 -0
- package/src/blocks/list.js +50 -0
- package/src/blocks/placement.js +119 -0
- package/src/blocks/prose.js +28 -0
- package/src/blocks/quote.js +25 -0
- package/src/blocks/raw.js +47 -0
- package/src/blocks/registry.js +158 -0
- package/src/blocks/schema-parts.js +19 -0
- package/src/blocks/section.js +53 -0
- package/src/blocks/slide.js +104 -0
- package/src/blocks/stat.js +83 -0
- package/src/blocks/table.js +50 -0
- package/src/blocks/timeline.js +80 -0
- package/src/cli/commands/close.js +51 -0
- package/src/cli/commands/comments.js +73 -0
- package/src/cli/commands/debug.js +34 -0
- package/src/cli/commands/example.js +22 -0
- package/src/cli/commands/export.js +10 -0
- package/src/cli/commands/init.js +53 -0
- package/src/cli/commands/lint.js +93 -0
- package/src/cli/commands/list.js +54 -0
- package/src/cli/commands/open.js +31 -0
- package/src/cli/commands/promote.js +38 -0
- package/src/cli/commands/render.js +31 -0
- package/src/cli/commands/replay.js +49 -0
- package/src/cli/commands/schema.js +10 -0
- package/src/cli/commands/serve.js +178 -0
- package/src/cli/commands/setup.js +64 -0
- package/src/cli/commands/snapshot.js +29 -0
- package/src/cli/commands/templates.js +75 -0
- package/src/cli/deliver.js +54 -0
- package/src/cli/emit.js +29 -0
- package/src/cli/format.js +153 -0
- package/src/cli/index.js +365 -0
- package/src/cli/registry.js +18 -0
- package/src/core/blocks.js +147 -0
- package/src/core/diagram.js +282 -0
- package/src/core/errors.js +156 -0
- package/src/core/example.js +109 -0
- package/src/core/ir.js +62 -0
- package/src/core/lint-gates.js +427 -0
- package/src/core/lint.js +281 -0
- package/src/core/scan.js +84 -0
- package/src/core/schema.js +88 -0
- package/src/core/spec-check.js +33 -0
- package/src/core/validate.js +44 -0
- package/src/core/version.js +18 -0
- package/src/core/vocabulary.js +140 -0
- package/src/delivery/atomic.js +71 -0
- package/src/delivery/comments.js +180 -0
- package/src/delivery/home.js +70 -0
- package/src/delivery/open.js +31 -0
- package/src/delivery/project.js +141 -0
- package/src/delivery/read.js +109 -0
- package/src/delivery/run.js +95 -0
- package/src/delivery/scaffold-blueprints.js +728 -0
- package/src/delivery/store.js +219 -0
- package/src/delivery/template-extensions.js +183 -0
- package/src/delivery/template-format.js +112 -0
- package/src/delivery/template-package.js +376 -0
- package/src/delivery/template-promote.js +240 -0
- package/src/delivery/template-scaffold.js +181 -0
- package/src/delivery/templates.js +192 -0
- package/src/delivery/toml.js +195 -0
- package/src/delivery/write.js +35 -0
- package/src/export/browser.js +130 -0
- package/src/export/index.js +96 -0
- package/src/export/pdf.js +25 -0
- package/src/export/png.js +40 -0
- package/src/export/pptx.js +48 -0
- package/src/export/slides.js +33 -0
- package/src/export/snapshot.js +33 -0
- package/src/layouts/article.js +103 -0
- package/src/layouts/canvas.js +144 -0
- package/src/layouts/card.js +128 -0
- package/src/layouts/deck.js +88 -0
- package/src/layouts/index.js +90 -0
- package/src/layouts/one-page.js +161 -0
- package/src/layouts/registry.js +251 -0
- package/src/layouts/resume.js +172 -0
- package/src/layouts/template-index.js +78 -0
- package/src/parser/artifact.js +38 -0
- package/src/parser/container.js +103 -0
- package/src/parser/index.js +223 -0
- package/src/parser/tokens.js +265 -0
- package/src/render/board-filter.client.js +80 -0
- package/src/render/compile.js +29 -0
- package/src/render/context.js +98 -0
- package/src/render/element.js +32 -0
- package/src/render/fonts.js +129 -0
- package/src/render/graph-hover.client.js +148 -0
- package/src/render/html.js +52 -0
- package/src/render/index.js +241 -0
- package/src/render/measure.js +60 -0
- package/src/render/placement.js +136 -0
- package/src/render/playback.client.js +74 -0
- package/src/render/scale-to-fit.client.js +136 -0
- package/src/render/scale.js +41 -0
- package/src/render/skeleton.js +131 -0
- package/src/render/ssr.js +24 -0
- package/src/render/styles.js +56 -0
- package/src/render/templates.js +191 -0
- package/src/serve/daemon.js +117 -0
- package/src/serve/overlay.js +213 -0
- package/src/serve/protocol.js +36 -0
- package/src/serve/server.js +264 -0
- package/templates/kami/cards/components.js +40 -0
- package/templates/kami/cards/index.js +25 -0
- package/templates/kami/cards/manifest.js +67 -0
- package/templates/kami/cards/styles.css +389 -0
- package/templates/kami/long-form/components.js +102 -0
- package/templates/kami/long-form/index.js +25 -0
- package/templates/kami/long-form/manifest.js +87 -0
- package/templates/kami/long-form/styles.css +481 -0
- package/templates/kami/one-page/components.js +48 -0
- package/templates/kami/one-page/index.js +27 -0
- package/templates/kami/one-page/manifest.js +65 -0
- package/templates/kami/one-page/styles.css +375 -0
- package/templates/kami/resume/components.js +51 -0
- package/templates/kami/resume/index.js +27 -0
- package/templates/kami/resume/manifest.js +65 -0
- package/templates/kami/resume/styles.css +424 -0
- package/templates/kami/slides/components.js +41 -0
- package/templates/kami/slides/index.js +26 -0
- package/templates/kami/slides/manifest.js +64 -0
- package/templates/kami/slides/styles.css +406 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--backdrop: #2b2f3a;
|
|
3
|
+
--surface: #ffffff;
|
|
4
|
+
--accent: #1B365D;
|
|
5
|
+
--accent-soft: #dbe2ec;
|
|
6
|
+
--rule: #d9d5cb;
|
|
7
|
+
--text-primary: #23201a;
|
|
8
|
+
--text-secondary: #55503f;
|
|
9
|
+
--text-muted: #8a8371;
|
|
10
|
+
--code-bg: #f1eee5;
|
|
11
|
+
--warn: #8a5a12;
|
|
12
|
+
/* Sheet geometry is the `resume` layout's A4 logical canvas, shipped on the
|
|
13
|
+
markup as data-ksb-canvas (src/layouts/resume.js). Not restated here: a
|
|
14
|
+
second copy of a size is a copy free to drift from the one that decides
|
|
15
|
+
anything. Comments in this file are ASCII on purpose -- every distinct
|
|
16
|
+
character in a stylesheet pulls a font subset into every artifact. */
|
|
17
|
+
/* ksb:maxWidth --max-width */
|
|
18
|
+
/* ksb:measure --measure */
|
|
19
|
+
/* Declared means embedded (CONTRACT A4): only families that really ship with
|
|
20
|
+
the artifact are named here; the rest fall back to generic keywords. */
|
|
21
|
+
--serif: 'Noto Serif TC', serif;
|
|
22
|
+
--mono: monospace;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
* {
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
html,
|
|
30
|
+
body {
|
|
31
|
+
margin: 0;
|
|
32
|
+
padding: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
background: var(--backdrop);
|
|
37
|
+
color: var(--text-primary);
|
|
38
|
+
font-family: var(--serif);
|
|
39
|
+
font-size: 15px;
|
|
40
|
+
line-height: 1.55;
|
|
41
|
+
-webkit-font-smoothing: antialiased;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* The page box belongs to this layout rather than to the printer defaults.
|
|
45
|
+
This SDK's own PDF export prints A4 with 14mm margins (src/export/pdf.js),
|
|
46
|
+
which leaves a printable box narrower and shorter than one sheet -- so a
|
|
47
|
+
sheet drawn at its natural size would lose a strip off the right edge and
|
|
48
|
+
another off the bottom of every page, silently. Claiming the whole page box
|
|
49
|
+
back is what makes one sheet exactly one printed page. The sheet's own
|
|
50
|
+
padding is the margin a reader sees. */
|
|
51
|
+
@page {
|
|
52
|
+
size: A4;
|
|
53
|
+
margin: 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* The document: a stack of sheets. Its width is not capped here -- a sheet
|
|
57
|
+
carries its own fixed logical width inline, so a narrower window scrolls
|
|
58
|
+
sideways rather than squeezing a canvas that is not allowed to reflow. */
|
|
59
|
+
.resume {
|
|
60
|
+
margin: 0 auto;
|
|
61
|
+
overflow-x: auto;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* One sheet *is* the bounded canvas: its size arrives inline and the skeleton's
|
|
65
|
+
scale-to-fit asset scales it (src/render/scale-to-fit.client.js). Only the
|
|
66
|
+
skin lives here. Content starts at the top rather than being centred: a page
|
|
67
|
+
of a resume that is half full should look half full, not float.
|
|
68
|
+
|
|
69
|
+
No horizontal margin, and that is an assertion rather than an omission: the
|
|
70
|
+
skeleton already translates every canvas by its own offsetX, so a skin that
|
|
71
|
+
also centres the box centres it twice and the sheet drifts off to one side by
|
|
72
|
+
half the leftover width. Measured at 1920 wide: sheet centre landed at 1523
|
|
73
|
+
against a window centre of 960. Horizontal placement belongs to the skeleton,
|
|
74
|
+
which is the only party that knows the scale. */
|
|
75
|
+
.sheet {
|
|
76
|
+
padding: 72px 67px;
|
|
77
|
+
background: var(--surface);
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
box-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Content region: capped by the sheet width minus the printer-safe margin the
|
|
85
|
+
padding above already reserves. */
|
|
86
|
+
.sheet-inner {
|
|
87
|
+
width: 100%;
|
|
88
|
+
max-width: var(--max-width);
|
|
89
|
+
margin: 0 auto;
|
|
90
|
+
overflow-y: auto;
|
|
91
|
+
max-height: 100%;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Running text keeps the measure: on paper the page and the column are two
|
|
95
|
+
numbers just as they are on a screen (CONTRACT C3). */
|
|
96
|
+
.prose,
|
|
97
|
+
.list,
|
|
98
|
+
.quote,
|
|
99
|
+
.callout {
|
|
100
|
+
max-width: var(--measure);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.masthead {
|
|
104
|
+
border-bottom: 2px solid var(--accent);
|
|
105
|
+
padding-bottom: 18px;
|
|
106
|
+
margin-bottom: 26px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.masthead .doc-title {
|
|
110
|
+
font-size: 34px;
|
|
111
|
+
line-height: 1.15;
|
|
112
|
+
font-weight: 700;
|
|
113
|
+
margin: 0;
|
|
114
|
+
color: var(--text-primary);
|
|
115
|
+
letter-spacing: 0.02em;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.masthead .kicker {
|
|
119
|
+
font-size: 13px;
|
|
120
|
+
letter-spacing: 0.16em;
|
|
121
|
+
text-transform: uppercase;
|
|
122
|
+
color: var(--accent);
|
|
123
|
+
margin: 10px 0 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.masthead .byline {
|
|
127
|
+
margin: 10px 0 0;
|
|
128
|
+
font-size: 13px;
|
|
129
|
+
color: var(--text-secondary);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* A sheet holds one chapter, so the section gives up its outer margins and
|
|
133
|
+
keeps only its inner rhythm. */
|
|
134
|
+
.sheet-inner > .section {
|
|
135
|
+
margin: 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.section {
|
|
139
|
+
margin: 0 0 18px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.section > .section-title {
|
|
143
|
+
font-weight: 700;
|
|
144
|
+
color: var(--accent);
|
|
145
|
+
line-height: 1.3;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* The band heading of a resume: small, ruled, and unmistakably a divider
|
|
149
|
+
between one part of a career and the next. */
|
|
150
|
+
.section-l1 > .section-title,
|
|
151
|
+
.section-l2 > .section-title {
|
|
152
|
+
font-size: 17px;
|
|
153
|
+
letter-spacing: 0.12em;
|
|
154
|
+
text-transform: uppercase;
|
|
155
|
+
margin: 0 0 14px;
|
|
156
|
+
padding-bottom: 6px;
|
|
157
|
+
border-bottom: 1px solid var(--rule);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.section .section-l2 > .section-title,
|
|
161
|
+
.section .section-l3 > .section-title {
|
|
162
|
+
font-size: 15px;
|
|
163
|
+
letter-spacing: 0;
|
|
164
|
+
text-transform: none;
|
|
165
|
+
border-bottom: 0;
|
|
166
|
+
padding-bottom: 0;
|
|
167
|
+
margin: 16px 0 6px;
|
|
168
|
+
color: var(--text-primary);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.section-l4 > .section-title,
|
|
172
|
+
.section-l5 > .section-title,
|
|
173
|
+
.section-l6 > .section-title {
|
|
174
|
+
font-size: 14px;
|
|
175
|
+
margin: 12px 0 6px;
|
|
176
|
+
color: var(--text-secondary);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.prose {
|
|
180
|
+
margin: 0 0 10px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.prose:last-child {
|
|
184
|
+
margin-bottom: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.prose a {
|
|
188
|
+
color: var(--accent);
|
|
189
|
+
text-underline-offset: 3px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Lists arrive two ways -- a list block, and list markup pushed into prose by a
|
|
193
|
+
raw island -- and both get the same rules, or the same source would look
|
|
194
|
+
different depending on which door it came through. On this layout the list is
|
|
195
|
+
the workhorse: a career is a list. */
|
|
196
|
+
.prose ul,
|
|
197
|
+
.prose ol,
|
|
198
|
+
.list {
|
|
199
|
+
margin: 0 0 10px;
|
|
200
|
+
padding-left: 1.3em;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.prose li,
|
|
204
|
+
.list li {
|
|
205
|
+
margin: 0 0 5px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.prose li:last-child,
|
|
209
|
+
.list li:last-child {
|
|
210
|
+
margin-bottom: 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.prose li::marker,
|
|
214
|
+
.list li::marker {
|
|
215
|
+
color: var(--text-muted);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.list .list {
|
|
219
|
+
margin: 5px 0 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.list li > .prose {
|
|
223
|
+
margin: 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.list li > .prose + * {
|
|
227
|
+
margin-top: 5px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.prose code,
|
|
231
|
+
.callout code,
|
|
232
|
+
.quote code,
|
|
233
|
+
.table code {
|
|
234
|
+
font-family: var(--mono);
|
|
235
|
+
font-size: 0.9em;
|
|
236
|
+
background: var(--code-bg);
|
|
237
|
+
border-radius: 3px;
|
|
238
|
+
padding: 1px 4px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.quote {
|
|
242
|
+
margin: 0 0 12px;
|
|
243
|
+
padding: 2px 0 2px 14px;
|
|
244
|
+
border-left: 3px solid var(--accent-soft);
|
|
245
|
+
color: var(--text-secondary);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.quote > .prose:last-child {
|
|
249
|
+
margin-bottom: 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.callout {
|
|
253
|
+
margin: 0 0 12px;
|
|
254
|
+
padding: 12px 14px;
|
|
255
|
+
background: var(--code-bg);
|
|
256
|
+
border-left: 4px solid var(--accent);
|
|
257
|
+
border-radius: 2px;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.callout-warn {
|
|
261
|
+
border-left-color: var(--warn);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.callout .callout-label {
|
|
265
|
+
display: block;
|
|
266
|
+
font-size: 10px;
|
|
267
|
+
letter-spacing: 0.18em;
|
|
268
|
+
text-transform: uppercase;
|
|
269
|
+
color: var(--accent);
|
|
270
|
+
margin-bottom: 5px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.callout-warn .callout-label {
|
|
274
|
+
color: var(--warn);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.callout > .prose:last-child {
|
|
278
|
+
margin-bottom: 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.code {
|
|
282
|
+
margin: 0 0 12px;
|
|
283
|
+
padding: 12px 14px;
|
|
284
|
+
background: var(--code-bg);
|
|
285
|
+
border: 1px solid var(--rule);
|
|
286
|
+
border-radius: 4px;
|
|
287
|
+
overflow-x: auto;
|
|
288
|
+
font-family: var(--mono);
|
|
289
|
+
font-size: 13px;
|
|
290
|
+
line-height: 1.45;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.code code {
|
|
294
|
+
font-family: inherit;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.table-wrap {
|
|
298
|
+
margin: 0 0 12px;
|
|
299
|
+
overflow-x: auto;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.table {
|
|
303
|
+
width: 100%;
|
|
304
|
+
border-collapse: collapse;
|
|
305
|
+
font-size: 13px;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.table th,
|
|
309
|
+
.table td {
|
|
310
|
+
border-bottom: 1px solid var(--rule);
|
|
311
|
+
padding: 6px 8px;
|
|
312
|
+
text-align: left;
|
|
313
|
+
vertical-align: top;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.table th {
|
|
317
|
+
color: var(--accent);
|
|
318
|
+
font-weight: 700;
|
|
319
|
+
border-bottom: 2px solid var(--accent-soft);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.island {
|
|
323
|
+
margin: 0 0 12px;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* diagram (CONTRACT D1) -- the geometry is computed by core/diagram.js and the
|
|
327
|
+
skin only colours it, with variables every Kami skin agrees on, so one spec
|
|
328
|
+
draws one picture rather than several that look alike. */
|
|
329
|
+
.diagram {
|
|
330
|
+
margin: 0 0 12px;
|
|
331
|
+
overflow-x: auto;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.diagram-svg {
|
|
335
|
+
display: block;
|
|
336
|
+
max-width: 100%;
|
|
337
|
+
height: auto;
|
|
338
|
+
font-family: var(--serif);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.diagram-node-box {
|
|
342
|
+
fill: var(--accent-soft);
|
|
343
|
+
stroke: var(--accent);
|
|
344
|
+
stroke-width: 1.5;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.diagram-node-label {
|
|
348
|
+
fill: var(--text-primary);
|
|
349
|
+
font-size: 13px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.diagram-edge-line {
|
|
353
|
+
fill: none;
|
|
354
|
+
stroke: var(--accent);
|
|
355
|
+
stroke-width: 1.5;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.diagram-arrow {
|
|
359
|
+
fill: var(--accent);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.diagram-edge-label {
|
|
363
|
+
fill: var(--text-secondary);
|
|
364
|
+
font-size: 11px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* The foot of the last sheet, inside the canvas: on paper anything outside a
|
|
368
|
+
sheet is laid onto a page of its own. */
|
|
369
|
+
.colophon {
|
|
370
|
+
margin-top: auto;
|
|
371
|
+
padding-top: 18px;
|
|
372
|
+
font-size: 10px;
|
|
373
|
+
color: var(--text-muted);
|
|
374
|
+
letter-spacing: 0.06em;
|
|
375
|
+
text-align: right;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@media print {
|
|
379
|
+
body {
|
|
380
|
+
background: #fff;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* scale-to-fit is a screen affordance. On paper (and in `export`, which asks
|
|
384
|
+
for the print form) the inline transform would shrink every sheet into a
|
|
385
|
+
corner of its page. The stacking shadow goes too: it is a screen cue that
|
|
386
|
+
these are sheets of paper, and on paper they already are.
|
|
387
|
+
|
|
388
|
+
The other three declarations undo what makes a sheet a *fixed frame* on
|
|
389
|
+
screen, and they are the difference between a resume and a truncated one.
|
|
390
|
+
On screen a sheet is exactly one canvas high and its inner region scrolls;
|
|
391
|
+
on paper there is no scrollbar to reach for, so the same three rules mean
|
|
392
|
+
that anything past the first page is painted nowhere and reported by
|
|
393
|
+
nothing. Measured before this was fixed: a one-sheet resume of 60 entries
|
|
394
|
+
exported to PDF as ONE page, the last line sitting 744px below the bottom
|
|
395
|
+
of the paper, exit 0, no message anywhere -- while the same artifact
|
|
396
|
+
scrolled to the end perfectly well in a browser.
|
|
397
|
+
|
|
398
|
+
`height: auto` is the load-bearing one of the three, and it needs
|
|
399
|
+
`!important` because the height arrives inline with the canvas. Control
|
|
400
|
+
measurement: keeping the fixed height and relaxing only the clipping still
|
|
401
|
+
printed one page -- a fixed-height box does not hand its overflow to the
|
|
402
|
+
next page, it just stops. `break-inside: avoid` is gone for the same
|
|
403
|
+
reason: a sheet with more than a page of content has to be allowed to
|
|
404
|
+
split, and a rule that says otherwise is a rule the browser ignores while
|
|
405
|
+
the reader believes it. */
|
|
406
|
+
.sheet {
|
|
407
|
+
transform: none !important;
|
|
408
|
+
box-shadow: none;
|
|
409
|
+
display: block;
|
|
410
|
+
overflow: visible;
|
|
411
|
+
height: auto !important;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.sheet-inner {
|
|
415
|
+
overflow: visible;
|
|
416
|
+
max-height: none;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* One sheet *starts* one printed page -- and runs on to the next when it has
|
|
420
|
+
more than a page of content in it. */
|
|
421
|
+
.sheet + .sheet {
|
|
422
|
+
break-before: page;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { el } from '../../../src/blocks/element.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Chrome of the Kami slides template.
|
|
5
|
+
*
|
|
6
|
+
* The deck frame and the slide frame belong to the `deck` and `slide` block
|
|
7
|
+
* modules; the *shape of the artifact* — that a deck-rooted document draws its
|
|
8
|
+
* deck and nothing else — belongs to the `deck` layout since F2a. What is left
|
|
9
|
+
* here is what only this template can decide: the wording of the deck chrome,
|
|
10
|
+
* and that the opening slide carries the document's own identity.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The opening slide carries the document's own identity (frontmatter title and
|
|
15
|
+
* kicker); every later slide carries only its compiled blocks (CONTRACT C4).
|
|
16
|
+
*/
|
|
17
|
+
const slideMasthead = (meta) =>
|
|
18
|
+
el('header', { class: 'slide-masthead' }, [
|
|
19
|
+
meta.kicker ? el('p', { class: 'kicker' }, [meta.kicker]) : null,
|
|
20
|
+
el('h1', { class: 'deck-title' }, [meta.title]),
|
|
21
|
+
meta.author || meta.date
|
|
22
|
+
? el('p', { class: 'byline' }, [[meta.author, meta.date].filter(Boolean).join(' · ')])
|
|
23
|
+
: null,
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
export const chrome = Object.freeze({
|
|
27
|
+
/** Only the first slide is led by the document masthead. */
|
|
28
|
+
'slide-lead': (position, ctx) => (position.index === 0 ? [slideMasthead(ctx.meta)] : []),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The progress readout starts at `1 / n` and is rewritten by the skeleton's
|
|
32
|
+
* playback script; the hint is this template's wording, in this template's
|
|
33
|
+
* language, and is the reason the deck frame itself carries no text.
|
|
34
|
+
*/
|
|
35
|
+
'deck-chrome': ({ total }) => [
|
|
36
|
+
el('footer', { class: 'deck-chrome' }, [
|
|
37
|
+
el('span', { class: 'deck-progress' }, [`1 / ${total}`]),
|
|
38
|
+
el('span', { class: 'deck-hint' }, ['方向鍵/空白鍵翻頁 · F 全螢幕 · Esc 離開']),
|
|
39
|
+
]),
|
|
40
|
+
],
|
|
41
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { manifest, templateKey } from './manifest.js'
|
|
4
|
+
import { chrome } from './components.js'
|
|
5
|
+
|
|
6
|
+
const STYLES_PATH = fileURLToPath(new URL('./styles.css', import.meta.url))
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Template package as consumed by the render layer.
|
|
10
|
+
*
|
|
11
|
+
* No `script`: playback is a skeleton asset supplied to every deck-rooted
|
|
12
|
+
* layout by `src/render/skeleton.js`, with no opt-out. No `root` either: the
|
|
13
|
+
* shape of a deck artifact is the `deck` 文體's. What is left here is paint and
|
|
14
|
+
* wording — which is the whole claim of wayfinder issue 13.
|
|
15
|
+
*/
|
|
16
|
+
export default Object.freeze({
|
|
17
|
+
manifest,
|
|
18
|
+
key: templateKey,
|
|
19
|
+
chrome,
|
|
20
|
+
language: manifest.language,
|
|
21
|
+
/** Fonts subset into the artifact (SPEC §7.2 出廠字體堆疊). */
|
|
22
|
+
fonts: Object.freeze([
|
|
23
|
+
Object.freeze({ package: '@fontsource/noto-serif-tc', weights: Object.freeze(['400', '700']) }),
|
|
24
|
+
]),
|
|
25
|
+
styles: () => readFileSync(STYLES_PATH, 'utf8'),
|
|
26
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template package descriptor (SPEC §5.2 `[template]` section) for the deck
|
|
3
|
+
* family. `---` only becomes a page break under this key (CONTRACT C4).
|
|
4
|
+
*/
|
|
5
|
+
export const manifest = Object.freeze({
|
|
6
|
+
namespace: 'kami',
|
|
7
|
+
name: 'slides',
|
|
8
|
+
version: '0.1.0',
|
|
9
|
+
description: 'Kami 簡報模板 — 16:9 單張版面,內建離線播放',
|
|
10
|
+
language: 'zh-TW',
|
|
11
|
+
/**
|
|
12
|
+
* The 文體 this skin is a skin *of* (CONTRACT C1) — and the reason the root
|
|
13
|
+
* form below is inherited rather than invented: every template on the `deck`
|
|
14
|
+
* layout requires a deck root, whether or not it remembers to say so.
|
|
15
|
+
*/
|
|
16
|
+
layout: 'deck',
|
|
17
|
+
/**
|
|
18
|
+
* Required root form (CONTRACT C4). `blocks` cannot express this: every
|
|
19
|
+
* long-form type below is legal *inside* a slide, so a flat document tree
|
|
20
|
+
* passes the type check while this template draws only `deck.slides` — the
|
|
21
|
+
* whole document silently disappears. The root form is a separate claim.
|
|
22
|
+
*/
|
|
23
|
+
root: 'deck',
|
|
24
|
+
blocks: Object.freeze([
|
|
25
|
+
'doc',
|
|
26
|
+
'deck',
|
|
27
|
+
'slide',
|
|
28
|
+
'section',
|
|
29
|
+
'prose',
|
|
30
|
+
'list',
|
|
31
|
+
'quote',
|
|
32
|
+
'callout',
|
|
33
|
+
'code',
|
|
34
|
+
'table',
|
|
35
|
+
'raw',
|
|
36
|
+
'diagram',
|
|
37
|
+
// F6a 四型資料 block(11 號票 P1「資料」)。五張 Kami 皮一起宣告:
|
|
38
|
+
// 詞彙表是准入規則,一張皮少宣告一種,同一份文件在它底下就會被
|
|
39
|
+
// KSB_TEMPLATE_BLOCK_UNSUPPORTED 擋掉——而那不是守門,是缺角。
|
|
40
|
+
'stat',
|
|
41
|
+
'timeline',
|
|
42
|
+
'board',
|
|
43
|
+
'graph',
|
|
44
|
+
]),
|
|
45
|
+
/** 轉子/<型別>/config — see the long-form manifest for why wording lives here. */
|
|
46
|
+
blockConfig: Object.freeze({
|
|
47
|
+
callout: Object.freeze({ labels: Object.freeze({ note: 'NOTE', warn: 'WARNING' }) }),
|
|
48
|
+
}),
|
|
49
|
+
/**
|
|
50
|
+
* The applied state of the `deck` layout's geometry knobs, in CSS pixels.
|
|
51
|
+
*
|
|
52
|
+
* A deck is the case that makes the numbers obviously distinct. Since F2f
|
|
53
|
+
* there are three of them and each has a different job: the logical page is
|
|
54
|
+
* 1280×720 and belongs to the 文體 (it arrives with the layout, not from
|
|
55
|
+
* here); `maxWidth` 1120 is how wide the content region inside that page may
|
|
56
|
+
* grow (`--slide-measure` in styles.css); and a line of text still wants the
|
|
57
|
+
* same 740px ruler everything else uses. Collapsing any two of them is
|
|
58
|
+
* exactly the mistake CONTRACT C3 separates out.
|
|
59
|
+
*/
|
|
60
|
+
maxWidth: 1120,
|
|
61
|
+
measure: 740,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const templateKey = `${manifest.namespace}/${manifest.name}`
|