@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,728 @@
|
|
|
1
|
+
import { NO_ROOT } from './template-format.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What the scaffold has to *say* to produce a working package, per 文體.
|
|
5
|
+
*
|
|
6
|
+
* `template-scaffold.js` owns the mechanism — validate, refuse an occupied
|
|
7
|
+
* directory, write three files atomically — and none of that differs between
|
|
8
|
+
* 文體. What differs is entirely content: which blocks the package admits, what
|
|
9
|
+
* root form it restates, how its skin paints the hooks that 文體 emits, and what
|
|
10
|
+
* its 預設 md looks like. Keeping the content here is what lets a new 文體 be
|
|
11
|
+
* supported by writing a blueprint rather than by editing the writer, and it is
|
|
12
|
+
* why `SCAFFOLD_SUPPORTED_LAYOUTS` is *derived* from this table: a 文體 the flag
|
|
13
|
+
* lets through but has no blueprint for would scaffold a package that cannot
|
|
14
|
+
* draw, which is the failure F3's 複驗 F-1 narrowed the flag to prevent.
|
|
15
|
+
*
|
|
16
|
+
* Every stylesheet here is deliberately small and deliberately **complete**:
|
|
17
|
+
* every hook the admitted blocks emit gets at least a rule, because a block
|
|
18
|
+
* nobody painted renders as invisible data loss with exit 0 (issue 13「這類系統
|
|
19
|
+
* 最惡毒的失敗模式」). The `ksb:` comment is the geometry marker
|
|
20
|
+
* `src/render/styles.js` fills from the manifest — the scaffold teaches the
|
|
21
|
+
* mechanism by using it.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** The applied state the article scaffold turns its 文體's knobs to, in CSS pixels. */
|
|
25
|
+
const ARTICLE_MAX_WIDTH = 740
|
|
26
|
+
const ARTICLE_MEASURE = 740
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The deck scaffold's applied state. The two numbers are *not* the logical
|
|
30
|
+
* canvas: the canvas (1280×720) belongs to the `deck` 文體 and arrives with it,
|
|
31
|
+
* while `maxWidth` is how wide the content region inside a slide may grow and
|
|
32
|
+
* `measure` is still the width of a line of text (CONTRACT C3).
|
|
33
|
+
*/
|
|
34
|
+
const DECK_MAX_WIDTH = 1120
|
|
35
|
+
const DECK_MEASURE = 740
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The card scaffold's applied state — the board and the column, at their
|
|
39
|
+
* furthest apart. `maxWidth` 1200 is how wide the board of cards may get;
|
|
40
|
+
* `measure` 740 is still the width of a line of text, and on this 文體 it is the
|
|
41
|
+
* lead-in paragraph rather than the cards that respects it.
|
|
42
|
+
*/
|
|
43
|
+
const CARD_MAX_WIDTH = 1200
|
|
44
|
+
const CARD_MEASURE = 740
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The one-page scaffold's applied state. Like the deck's, the two numbers are
|
|
48
|
+
* *not* the logical canvas: the canvas (1440×900, one screen) belongs to the
|
|
49
|
+
* `one-page` 文體 and arrives with it, while `maxWidth` is how wide the content
|
|
50
|
+
* region inside a screen may grow and `measure` is still the width of a line of
|
|
51
|
+
* text (CONTRACT C3).
|
|
52
|
+
*/
|
|
53
|
+
const ONE_PAGE_MAX_WIDTH = 1200
|
|
54
|
+
const ONE_PAGE_MEASURE = 740
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The resume scaffold's applied state. Again the two numbers are *not* the
|
|
58
|
+
* logical canvas — the canvas (794×1123, one sheet of A4) belongs to the
|
|
59
|
+
* `resume` 文體 and arrives with it. What is different here is that neither
|
|
60
|
+
* number is the usual one: on paper `measure` cannot be 740, because a 740px
|
|
61
|
+
* column plus any margin at all is wider than an A4 sheet.
|
|
62
|
+
*/
|
|
63
|
+
const RESUME_MAX_WIDTH = 660
|
|
64
|
+
const RESUME_MEASURE = 620
|
|
65
|
+
|
|
66
|
+
/** The block types the article scaffold admits — the core long-form vocabulary. */
|
|
67
|
+
const ARTICLE_BLOCKS = Object.freeze([
|
|
68
|
+
'doc',
|
|
69
|
+
'section',
|
|
70
|
+
'prose',
|
|
71
|
+
'list',
|
|
72
|
+
'quote',
|
|
73
|
+
'callout',
|
|
74
|
+
'code',
|
|
75
|
+
'table',
|
|
76
|
+
'raw',
|
|
77
|
+
'diagram',
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The deck scaffold admits the same vocabulary **plus the two container types a
|
|
82
|
+
* deck is made of**. `deck`/`slide` are not optional extras here: the 文體
|
|
83
|
+
* requires a `deck` root form, so a package that did not admit them would be
|
|
84
|
+
* refused by the vocabulary guard on its own 預設 md.
|
|
85
|
+
*/
|
|
86
|
+
const DECK_BLOCKS = Object.freeze(['doc', 'deck', 'slide', ...ARTICLE_BLOCKS.slice(1)])
|
|
87
|
+
|
|
88
|
+
/** Rules every 文體's skin needs, because every 文體 admits these blocks. */
|
|
89
|
+
const commonBlockRules = () => [
|
|
90
|
+
'.section-title { line-height: 1.3; }',
|
|
91
|
+
'.prose { margin: 0; }',
|
|
92
|
+
'.list { padding-left: 1.4rem; }',
|
|
93
|
+
'.quote { margin: 0; padding-left: 1rem; border-left: 3px solid var(--rule); }',
|
|
94
|
+
'.callout { padding: 1rem; border: 1px solid var(--rule); border-radius: 4px; }',
|
|
95
|
+
'.callout-label { font-size: 0.75rem; letter-spacing: 0.08em; }',
|
|
96
|
+
'.code { padding: 1rem; overflow-x: auto; background: #f2efe9; }',
|
|
97
|
+
'.table-wrap { overflow-x: auto; }',
|
|
98
|
+
'.table { border-collapse: collapse; width: 100%; }',
|
|
99
|
+
'.table th, .table td { border: 1px solid var(--rule); padding: 0.4rem 0.6rem; }',
|
|
100
|
+
'.island { margin: 0; }',
|
|
101
|
+
'.diagram { max-width: 100%; }',
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
const articleStylesheet = () =>
|
|
105
|
+
[
|
|
106
|
+
':root {',
|
|
107
|
+
' /* ksb:measure --measure */',
|
|
108
|
+
' --ink: #1a1a1a;',
|
|
109
|
+
' --paper: #fdfdfb;',
|
|
110
|
+
' --rule: #d8d4cc;',
|
|
111
|
+
'}',
|
|
112
|
+
'',
|
|
113
|
+
'body { margin: 0; background: var(--paper); color: var(--ink);',
|
|
114
|
+
' font-family: Georgia, "Noto Serif TC", serif; line-height: 1.75; }',
|
|
115
|
+
'.paper { max-width: var(--measure); margin: 0 auto; padding: 4rem 1.5rem; }',
|
|
116
|
+
'.doc-body > * + * { margin-top: 1.2rem; }',
|
|
117
|
+
...commonBlockRules(),
|
|
118
|
+
'',
|
|
119
|
+
].join('\n')
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The deck skin.
|
|
123
|
+
*
|
|
124
|
+
* Three rules here are not decoration and would each break 開箱即用 if dropped:
|
|
125
|
+
*
|
|
126
|
+
* - `.deck.is-live` / `.is-current` — the playback asset only adds classes.
|
|
127
|
+
* A skin that never acts on them ships a deck whose arrow keys do nothing,
|
|
128
|
+
* which looks like a broken artifact rather than an unpainted one.
|
|
129
|
+
* - `.slide-cell { min-width: 0 }` — a grid cell defaults to `min-width: auto`,
|
|
130
|
+
* so one long unbroken word widens its track and silently narrows its
|
|
131
|
+
* neighbours.
|
|
132
|
+
* - `transform: none !important` under `@media print` — scale-to-fit is a
|
|
133
|
+
* screen affordance; on paper (and in `export`, which asks for the print
|
|
134
|
+
* form) the inline transform would shrink every slide into a corner.
|
|
135
|
+
*/
|
|
136
|
+
const deckStylesheet = () =>
|
|
137
|
+
[
|
|
138
|
+
':root {',
|
|
139
|
+
' /* ksb:maxWidth --slide-measure */',
|
|
140
|
+
' --ink: #1a1a1a;',
|
|
141
|
+
' --paper: #14161c;',
|
|
142
|
+
' --surface: #fdfdfb;',
|
|
143
|
+
' --rule: #d8d4cc;',
|
|
144
|
+
'}',
|
|
145
|
+
'',
|
|
146
|
+
'* { box-sizing: border-box; }',
|
|
147
|
+
'body { margin: 0; background: var(--paper); color: var(--ink);',
|
|
148
|
+
' font-family: Georgia, "Noto Serif TC", serif; line-height: 1.6; }',
|
|
149
|
+
'',
|
|
150
|
+
'/* 每張投影片就是文體宣告的有界邏輯畫布;寬高隨標記出貨,縮放由骨架資產做。 */',
|
|
151
|
+
'.deck { margin: 0 auto; padding: 2rem 1.5rem; overflow-x: auto; }',
|
|
152
|
+
'.slide { margin: 0 auto 1.5rem; padding: 3rem 3.5rem; background: var(--surface);',
|
|
153
|
+
' border-radius: 4px; overflow: hidden; display: flex; flex-direction: column;',
|
|
154
|
+
' justify-content: center; }',
|
|
155
|
+
// 列距走**下**緣,不走上緣、也不走 row-gap。上緣會讓同一列的側欄比主欄低
|
|
156
|
+
// 一截;row-gap 更糟——格軌有 24 條顯式列,gap 連空列都算,於是每一張都被
|
|
157
|
+
// 撐掉半頁高。兩種錯都只是「看起來有點怪」,沒有人會去查。
|
|
158
|
+
'.slide-inner { width: 100%; max-width: var(--slide-measure); margin: 0 auto;',
|
|
159
|
+
' overflow-y: auto; max-height: 100%; }',
|
|
160
|
+
'.slide-cell { min-width: 0; margin-bottom: 1.2rem; }',
|
|
161
|
+
'.slide-cell:last-child { margin-bottom: 0; }',
|
|
162
|
+
'',
|
|
163
|
+
'/* 播放模式:沒有 JS 時每張照常可讀,整份產物退化成一份可捲動的文件。 */',
|
|
164
|
+
'.deck.is-live { position: fixed; inset: 0; padding: 0; overflow: hidden; }',
|
|
165
|
+
'.deck.is-live .slide { display: none; margin: 0; }',
|
|
166
|
+
'.deck.is-live .slide.is-current { display: flex; }',
|
|
167
|
+
'',
|
|
168
|
+
...commonBlockRules(),
|
|
169
|
+
'',
|
|
170
|
+
'@media print {',
|
|
171
|
+
' body { background: #fff; }',
|
|
172
|
+
' .deck.is-live { position: static; overflow: visible; padding: 2rem 1.5rem; }',
|
|
173
|
+
' .deck.is-live .slide { display: flex; margin: 0 auto 1.5rem; }',
|
|
174
|
+
' .slide { break-inside: avoid; transform: none !important; }',
|
|
175
|
+
'}',
|
|
176
|
+
'',
|
|
177
|
+
].join('\n')
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The card skin.
|
|
181
|
+
*
|
|
182
|
+
* Three rules here are load-bearing rather than decorative:
|
|
183
|
+
*
|
|
184
|
+
* - the 24 tracks plus `grid-column: span N` — this is the 文體's whole shape,
|
|
185
|
+
* and a scaffold that laid no tracks would produce a package whose cards
|
|
186
|
+
* stack in one column, i.e. an `article` wearing another name.
|
|
187
|
+
* - `.card { min-width: 0 }` — a grid item defaults to `min-width: auto`, so
|
|
188
|
+
* one long unbroken word widens its track and silently narrows the rest of
|
|
189
|
+
* the row.
|
|
190
|
+
* - the two breakpoints — 「寬視窗多欄、窄視窗單欄」 is part of what the 文體
|
|
191
|
+
* promises (CONTRACT F2c P1); without them the starter package is unreadable
|
|
192
|
+
* on the first phone that opens it.
|
|
193
|
+
*/
|
|
194
|
+
const cardStylesheet = () =>
|
|
195
|
+
[
|
|
196
|
+
':root {',
|
|
197
|
+
' /* ksb:measure --measure */',
|
|
198
|
+
' /* ksb:maxWidth --max-width */',
|
|
199
|
+
' --card-columns: 24;',
|
|
200
|
+
' --card-span: 8;',
|
|
201
|
+
' --card-gap: 24px;',
|
|
202
|
+
' --ink: #1a1a1a;',
|
|
203
|
+
' --paper: #fdfdfb;',
|
|
204
|
+
' --surface: #ffffff;',
|
|
205
|
+
' --rule: #d8d4cc;',
|
|
206
|
+
'}',
|
|
207
|
+
'',
|
|
208
|
+
'* { box-sizing: border-box; }',
|
|
209
|
+
'body { margin: 0; background: var(--paper); color: var(--ink);',
|
|
210
|
+
' font-family: Georgia, "Noto Serif TC", serif; line-height: 1.6; }',
|
|
211
|
+
'',
|
|
212
|
+
'.cards { max-width: var(--max-width); margin: 0 auto; padding: 3rem 1.5rem; }',
|
|
213
|
+
'.cards-intro { max-width: var(--measure); margin: 0 0 2rem; }',
|
|
214
|
+
'.cards-intro > * + * { margin-top: 1rem; }',
|
|
215
|
+
'',
|
|
216
|
+
'/* The board: 24 real tracks, and a card spans some of them. */',
|
|
217
|
+
'.card-grid { display: grid;',
|
|
218
|
+
' grid-template-columns: repeat(var(--card-columns), minmax(0, 1fr));',
|
|
219
|
+
' column-gap: var(--card-gap); row-gap: var(--card-gap); align-items: start; }',
|
|
220
|
+
'.card { grid-column: span var(--card-span); min-width: 0;',
|
|
221
|
+
' padding: 1.25rem 1.4rem; background: var(--surface);',
|
|
222
|
+
' border: 1px solid var(--rule); border-radius: 6px; }',
|
|
223
|
+
'.card > .section { margin: 0; }',
|
|
224
|
+
'.card > .section > .section-title { margin-top: 0; }',
|
|
225
|
+
...commonBlockRules(),
|
|
226
|
+
'',
|
|
227
|
+
'@media (max-width: 1024px) { .card { grid-column: span 12; } }',
|
|
228
|
+
'@media (max-width: 680px) { .card { grid-column: 1 / -1; } }',
|
|
229
|
+
'',
|
|
230
|
+
'@media print {',
|
|
231
|
+
' body { background: #fff; }',
|
|
232
|
+
' .card-grid { display: block; }',
|
|
233
|
+
' .card { break-inside: avoid; margin: 0 0 var(--card-gap); }',
|
|
234
|
+
'}',
|
|
235
|
+
'',
|
|
236
|
+
].join('\n')
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* The one-page skin.
|
|
240
|
+
*
|
|
241
|
+
* Three rules here are load-bearing rather than decorative:
|
|
242
|
+
*
|
|
243
|
+
* - `.screen { display: flex; justify-content: center }` plus `overflow:
|
|
244
|
+
* hidden` — a screen is a *fixed frame*, not a column that grows. Without
|
|
245
|
+
* them the content sits at the top of a 900px box and anything longer
|
|
246
|
+
* silently spills past the canvas the skeleton is scaling.
|
|
247
|
+
* - `.one-page { overflow-x: auto }` — the screen carries its logical width
|
|
248
|
+
* inline and is not allowed to reflow, so a narrower window has to scroll.
|
|
249
|
+
* Without this the starter package overflows the document instead.
|
|
250
|
+
* - `transform: none !important` under `@media print` — scale-to-fit is a
|
|
251
|
+
* screen affordance; on paper (and in `export`, which asks for the print
|
|
252
|
+
* form) the inline transform would shrink every screen into a corner.
|
|
253
|
+
*/
|
|
254
|
+
const onePageStylesheet = () =>
|
|
255
|
+
[
|
|
256
|
+
':root {',
|
|
257
|
+
' /* ksb:measure --measure */',
|
|
258
|
+
' /* ksb:maxWidth --max-width */',
|
|
259
|
+
' --ink: #1a1a1a;',
|
|
260
|
+
' --paper: #14161c;',
|
|
261
|
+
' --surface: #fdfdfb;',
|
|
262
|
+
' --rule: #d8d4cc;',
|
|
263
|
+
'}',
|
|
264
|
+
'',
|
|
265
|
+
'* { box-sizing: border-box; }',
|
|
266
|
+
'body { margin: 0; background: var(--paper); color: var(--ink);',
|
|
267
|
+
' font-family: Georgia, "Noto Serif TC", serif; line-height: 1.6; }',
|
|
268
|
+
'',
|
|
269
|
+
'/* The site is a scrollable sequence of screens; a screen is the bounded',
|
|
270
|
+
' logical canvas, and its size arrives inline on the markup. */',
|
|
271
|
+
'.one-page { margin: 0 auto; overflow-x: auto; }',
|
|
272
|
+
'.screen { margin: 0 auto; padding: 4rem 5rem; background: var(--surface);',
|
|
273
|
+
' overflow: hidden; display: flex; flex-direction: column;',
|
|
274
|
+
' justify-content: center; }',
|
|
275
|
+
'.screen-inner { width: 100%; max-width: var(--max-width); margin: 0 auto;',
|
|
276
|
+
' overflow-y: auto; max-height: 100%; }',
|
|
277
|
+
'.screen-inner > .section { margin: 0; }',
|
|
278
|
+
'.prose, .list, .quote, .callout { max-width: var(--measure); }',
|
|
279
|
+
'.colophon { padding: 1.5rem; text-align: center; font-size: 0.75rem;',
|
|
280
|
+
' color: #8a8371; }',
|
|
281
|
+
...commonBlockRules(),
|
|
282
|
+
'',
|
|
283
|
+
'@media print {',
|
|
284
|
+
' body { background: #fff; }',
|
|
285
|
+
' /* scale-to-fit is for screens; on paper the inline transform stands down. */',
|
|
286
|
+
' .screen { transform: none !important; break-inside: avoid; background: #fff; }',
|
|
287
|
+
'}',
|
|
288
|
+
'',
|
|
289
|
+
].join('\n')
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The resume skin.
|
|
293
|
+
*
|
|
294
|
+
* Three rules here are load-bearing rather than decorative:
|
|
295
|
+
*
|
|
296
|
+
* - `@page { margin: 0 }` — this SDK's PDF export prints A4 with 14mm margins
|
|
297
|
+
* (`src/export/pdf.js`), so the printable box is smaller than one sheet. A
|
|
298
|
+
* package that never claimed the page box back would lose a strip off the
|
|
299
|
+
* right edge and another off the bottom of *every* printed page, silently.
|
|
300
|
+
* - `.sheet + .sheet { break-before: page }` — one sheet *opens* one printed
|
|
301
|
+
* page, alone. `break-inside: avoid` is deliberately absent: a sheet with
|
|
302
|
+
* more than a page of content has to be allowed to split, and a rule that
|
|
303
|
+
* says otherwise is a rule the browser ignores while the reader believes it.
|
|
304
|
+
* - the print form hands back the three screen devices (`display: block;
|
|
305
|
+
* overflow: visible; height: auto !important`) — on screen a sheet is a
|
|
306
|
+
* fixed frame whose inner region scrolls; on paper there is no scrollbar,
|
|
307
|
+
* so a frame that kept its fixed height would paint everything past the
|
|
308
|
+
* first page nowhere and report it to nobody. `overflow: hidden` is a
|
|
309
|
+
* screen-only affordance.
|
|
310
|
+
*/
|
|
311
|
+
const resumeStylesheet = () =>
|
|
312
|
+
[
|
|
313
|
+
':root {',
|
|
314
|
+
' /* ksb:measure --measure */',
|
|
315
|
+
' /* ksb:maxWidth --max-width */',
|
|
316
|
+
' --ink: #1a1a1a;',
|
|
317
|
+
' --backdrop: #2b2f3a;',
|
|
318
|
+
' --surface: #ffffff;',
|
|
319
|
+
' --rule: #d8d4cc;',
|
|
320
|
+
'}',
|
|
321
|
+
'',
|
|
322
|
+
'* { box-sizing: border-box; }',
|
|
323
|
+
'body { margin: 0; background: var(--backdrop); color: var(--ink);',
|
|
324
|
+
' font-family: Georgia, "Noto Serif TC", serif; font-size: 15px; line-height: 1.55; }',
|
|
325
|
+
'',
|
|
326
|
+
'/* The page box belongs to this genre, not to the printer defaults: the',
|
|
327
|
+
' exporter prints A4 with margins, and a sheet drawn at its natural size',
|
|
328
|
+
' would be clipped by them. */',
|
|
329
|
+
'@page {',
|
|
330
|
+
' size: A4;',
|
|
331
|
+
' margin: 0;',
|
|
332
|
+
'}',
|
|
333
|
+
'',
|
|
334
|
+
'/* A resume is a stack of sheets; a sheet is the bounded logical canvas,',
|
|
335
|
+
' and its size arrives inline on the markup. */',
|
|
336
|
+
'.resume { margin: 0 auto; overflow-x: auto; }',
|
|
337
|
+
'/* No horizontal margin on the sheet: the skeleton already translates every',
|
|
338
|
+
' canvas by its own offsetX, so centring it here as well centres it twice',
|
|
339
|
+
' and the sheet drifts off to one side. */',
|
|
340
|
+
'.sheet { padding: 72px 67px; background: var(--surface);',
|
|
341
|
+
' overflow: hidden; display: flex; flex-direction: column;',
|
|
342
|
+
' box-shadow: 0 2px 18px rgba(0, 0, 0, 0.35); }',
|
|
343
|
+
'.sheet-inner { width: 100%; max-width: var(--max-width); margin: 0 auto;',
|
|
344
|
+
' overflow-y: auto; max-height: 100%; }',
|
|
345
|
+
'.sheet-inner > .section { margin: 0; }',
|
|
346
|
+
'.prose, .list, .quote, .callout { max-width: var(--measure); }',
|
|
347
|
+
'.colophon { margin-top: auto; padding-top: 1rem; text-align: right;',
|
|
348
|
+
' font-size: 0.7rem; color: #8a8371; }',
|
|
349
|
+
...commonBlockRules(),
|
|
350
|
+
'',
|
|
351
|
+
'@media print {',
|
|
352
|
+
' body { background: #fff; }',
|
|
353
|
+
' /* scale-to-fit is for screens; on paper the inline transform stands down.',
|
|
354
|
+
' The fixed height, the clip and the inner scroll region stand down with',
|
|
355
|
+
' it: on screen they make a sheet a fixed frame, on paper -- where there',
|
|
356
|
+
' is no scrollbar -- they mean anything past the first page is printed',
|
|
357
|
+
' nowhere and reported by nothing. `height: auto` is the load-bearing one',
|
|
358
|
+
' and needs !important, because the height arrives inline. */',
|
|
359
|
+
' .sheet { transform: none !important; box-shadow: none; display: block;',
|
|
360
|
+
' overflow: visible; height: auto !important; }',
|
|
361
|
+
' .sheet-inner { overflow: visible; max-height: none; }',
|
|
362
|
+
' /* One sheet starts one printed page, and runs on when it holds more. */',
|
|
363
|
+
' .sheet + .sheet { break-before: page; }',
|
|
364
|
+
'}',
|
|
365
|
+
'',
|
|
366
|
+
].join('\n')
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The 預設 md — 「起手內容文件,render 即得該模板預設樣貌」 (issue 14 補充裁定).
|
|
370
|
+
*
|
|
371
|
+
* The frontmatter names the package itself, so the file renders with the
|
|
372
|
+
* template it belongs to even without `-t`. That is the property D4 turns into
|
|
373
|
+
* a test: the scaffold is only 開箱即 render if this exact file, unedited, comes
|
|
374
|
+
* out the other side as a clean artifact.
|
|
375
|
+
*/
|
|
376
|
+
const articleDocument = ({ namespace, name }) =>
|
|
377
|
+
[
|
|
378
|
+
'---',
|
|
379
|
+
`template: ${namespace}/${name}`,
|
|
380
|
+
`title: ${namespace}/${name}`,
|
|
381
|
+
'kicker: kamishibai 模板包骨架',
|
|
382
|
+
'---',
|
|
383
|
+
'',
|
|
384
|
+
'# 這是預設 md',
|
|
385
|
+
'',
|
|
386
|
+
`這份文件跟著模板包 \`${namespace}/${name}\` 一起被建立。它的用途是「秀出這個模板`,
|
|
387
|
+
'預設長什麼樣」——改它的內容,或改隔壁的 `stylesheet.css`,兩者都不需要動 SDK。',
|
|
388
|
+
'',
|
|
389
|
+
':::note',
|
|
390
|
+
'manifest.toml 管宣告與套用態(所騎的 layout、准入的 block、measure 與 maxWidth);',
|
|
391
|
+
'stylesheet.css 管皮;這份 md 管預設樣貌。三者分工,缺一不可。',
|
|
392
|
+
':::',
|
|
393
|
+
'',
|
|
394
|
+
'## 想再往前一步',
|
|
395
|
+
'',
|
|
396
|
+
'- 純資料就夠用:多數模板只要改 `stylesheet.css` 與這份 md。',
|
|
397
|
+
'- 要換掉某個型別的轉換:放 `rotors/<型別>/override.js`。',
|
|
398
|
+
'- 要加新的 block 型別:放 `plugins/x-<名字>.js`,並把 `x-*` 寫進 manifest 的 blocks。',
|
|
399
|
+
'',
|
|
400
|
+
].join('\n')
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The deck 預設 md. Three slides, because the thing a deck scaffold has to
|
|
404
|
+
* demonstrate is the one thing a one-slide file cannot: that `---` is a page
|
|
405
|
+
* break under this package's own key, not decoration. `:::place` appears on the
|
|
406
|
+
* third slide for the same reason — the 文體 declares the whole page addressable
|
|
407
|
+
* by 24-grid coordinates, and a starter file that never uses them leaves the
|
|
408
|
+
* reader to discover the feature from a specification.
|
|
409
|
+
*
|
|
410
|
+
* Note that the `:::place` fences stand on their own, with no `::::grid` around
|
|
411
|
+
* them. That is the visible consequence of the 文體's `gridAddressed` claim: on
|
|
412
|
+
* a deck the *page* lays the tracks, so a coordinate resolves without anyone
|
|
413
|
+
* wrapping it — and on a flowing 文體 the same three lines are refused loudly
|
|
414
|
+
* rather than silently dropped (`src/render/placement.js`).
|
|
415
|
+
*/
|
|
416
|
+
const deckDocument = ({ namespace, name }) =>
|
|
417
|
+
[
|
|
418
|
+
'---',
|
|
419
|
+
`template: ${namespace}/${name}`,
|
|
420
|
+
`title: ${namespace}/${name}`,
|
|
421
|
+
'kicker: kamishibai 簡報骨架',
|
|
422
|
+
'---',
|
|
423
|
+
'',
|
|
424
|
+
'# 這是預設簡報',
|
|
425
|
+
'',
|
|
426
|
+
`這份文件跟著模板包 \`${namespace}/${name}\` 一起被建立。\`---\` 在這個包底下是切頁符——`,
|
|
427
|
+
'前後各留一個空行,否則 Markdown 會把它讀成 setext 標題。',
|
|
428
|
+
'',
|
|
429
|
+
'---',
|
|
430
|
+
'',
|
|
431
|
+
'# 三份分工',
|
|
432
|
+
'',
|
|
433
|
+
'- `manifest.toml` 管宣告與套用態(所騎的 layout、准入的 block、maxWidth 與 measure)',
|
|
434
|
+
'- `stylesheet.css` 管皮',
|
|
435
|
+
'- 這份 md 管預設樣貌',
|
|
436
|
+
'',
|
|
437
|
+
':::note',
|
|
438
|
+
'產物內建播放:方向鍵/空白鍵翻頁,f 全螢幕,Esc 離開。',
|
|
439
|
+
':::',
|
|
440
|
+
'',
|
|
441
|
+
'---',
|
|
442
|
+
'',
|
|
443
|
+
':::place col=1 colSpan=16',
|
|
444
|
+
'**24 格座標**:這一段占 24 格中的 16 格。這個文體宣告了整頁以 24 格軌定址,',
|
|
445
|
+
'所以座標不必再包一層 `grid`。',
|
|
446
|
+
':::',
|
|
447
|
+
'',
|
|
448
|
+
':::place col=17 colSpan=8',
|
|
449
|
+
'**側欄**:占右邊 8 格。',
|
|
450
|
+
':::',
|
|
451
|
+
'',
|
|
452
|
+
':::place col=1 colSpan=24',
|
|
453
|
+
'這一張沒有標題,是刻意的:`#` 標題會開一個 section,把後面的內容都收進去,',
|
|
454
|
+
'於是擺位是相對那個 section 而不是相對整張投影片的格軌。',
|
|
455
|
+
':::',
|
|
456
|
+
'',
|
|
457
|
+
].join('\n')
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The card 預設 md. **Three cards**, because the one thing a card board has to
|
|
461
|
+
* demonstrate is the thing a single-card file cannot: that a top-level heading
|
|
462
|
+
* is a card, that the cards sit side by side, and that they reflow. A starter
|
|
463
|
+
* file with one heading is indistinguishable from an `article`.
|
|
464
|
+
*
|
|
465
|
+
* It opens with a paragraph *before* the first heading, on purpose: that is the
|
|
466
|
+
* lead-in region, the other half of the 文體's cut, and a starter file that never
|
|
467
|
+
* produced one would leave the reader to discover it from a specification.
|
|
468
|
+
*
|
|
469
|
+
* No `---` anywhere. On this 文體 it is not a page break (that is a `deck` fact,
|
|
470
|
+
* `src/layouts/template-index.js`), so a stray one would come out as a thematic
|
|
471
|
+
* rule and teach exactly the wrong thing.
|
|
472
|
+
*/
|
|
473
|
+
const cardDocument = ({ namespace, name }) =>
|
|
474
|
+
[
|
|
475
|
+
'---',
|
|
476
|
+
`template: ${namespace}/${name}`,
|
|
477
|
+
`title: ${namespace}/${name}`,
|
|
478
|
+
'kicker: kamishibai 卡片骨架',
|
|
479
|
+
'---',
|
|
480
|
+
'',
|
|
481
|
+
`這份文件跟著模板包 \`${namespace}/${name}\` 一起被建立。第一個標題之前的段落`,
|
|
482
|
+
'不會變成卡片,而是卡群的前導語區——文體把頂層內容切成這兩塊。',
|
|
483
|
+
'',
|
|
484
|
+
'# 一張卡就是一個頂層標題',
|
|
485
|
+
'',
|
|
486
|
+
'標題底下的所有東西都留在同一張卡裡,子標題也是。卡片之間彼此獨立,',
|
|
487
|
+
'所以讀的人可以只挑一張看。',
|
|
488
|
+
'',
|
|
489
|
+
'## 子標題留在卡內',
|
|
490
|
+
'',
|
|
491
|
+
'- 卡內照常有清單',
|
|
492
|
+
'- 也照常有行內 `程式碼`',
|
|
493
|
+
'',
|
|
494
|
+
'# 三張卡才看得出鋪排',
|
|
495
|
+
'',
|
|
496
|
+
'一張卡看起來與長文沒有兩樣,兩張看不出換行,三張才畫得出「寬視窗多欄、',
|
|
497
|
+
'窄視窗單欄」這件事。',
|
|
498
|
+
'',
|
|
499
|
+
':::note',
|
|
500
|
+
'卡格是 24 條格軌,一張卡佔其中幾條由 `stylesheet.css` 的 `--card-span` 決定。',
|
|
501
|
+
'想要兩欄看板就把它改成 12。',
|
|
502
|
+
':::',
|
|
503
|
+
'',
|
|
504
|
+
'# 三份分工',
|
|
505
|
+
'',
|
|
506
|
+
'- `manifest.toml` 管宣告與套用態(所騎的 layout、准入的 block、maxWidth 與 measure)',
|
|
507
|
+
'- `stylesheet.css` 管皮,包含卡格與斷點',
|
|
508
|
+
'- 這份 md 管預設樣貌',
|
|
509
|
+
'',
|
|
510
|
+
].join('\n')
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* The one-page 預設 md. **Two screens plus an opening**, because the one thing a
|
|
514
|
+
* one-page site has to demonstrate is the thing a single-screen file cannot:
|
|
515
|
+
* that a top-level heading starts a new screen, and that scrolling is what moves
|
|
516
|
+
* between them. A starter file with one heading is indistinguishable from an
|
|
517
|
+
* `article` that happens to be scaled.
|
|
518
|
+
*
|
|
519
|
+
* It opens with a paragraph *before* the first heading, on purpose: together
|
|
520
|
+
* with the package's masthead that is the opening screen — the other half of the
|
|
521
|
+
* 文體's cut, and a starter file that never produced one would leave the reader
|
|
522
|
+
* to discover it from a specification.
|
|
523
|
+
*
|
|
524
|
+
* No `---` anywhere. On this 文體 it is not a page break (that is a `deck` fact,
|
|
525
|
+
* `src/layouts/template-index.js`), so a stray one would come out as a thematic
|
|
526
|
+
* rule *inside* a screen and teach exactly the wrong thing — the screen break is
|
|
527
|
+
* the heading.
|
|
528
|
+
*/
|
|
529
|
+
const onePageDocument = ({ namespace, name }) =>
|
|
530
|
+
[
|
|
531
|
+
'---',
|
|
532
|
+
`template: ${namespace}/${name}`,
|
|
533
|
+
`title: ${namespace}/${name}`,
|
|
534
|
+
'kicker: kamishibai 一頁式骨架',
|
|
535
|
+
'---',
|
|
536
|
+
'',
|
|
537
|
+
`這份文件跟著模板包 \`${namespace}/${name}\` 一起被建立。第一個標題之前的段落`,
|
|
538
|
+
'不會自成一屏,而是與抬頭一起組成開場屏——文體把頂層內容切成這兩塊。',
|
|
539
|
+
'',
|
|
540
|
+
'# 一屏就是一個頂層標題',
|
|
541
|
+
'',
|
|
542
|
+
'標題底下的所有東西都留在同一屏裡,子標題也是。一屏是一塊固定邏輯畫布,',
|
|
543
|
+
'視窗多大它就等比縮放到多大,所以在投影幕上與在筆電上看到的是同一個版面。',
|
|
544
|
+
'',
|
|
545
|
+
'## 子標題留在屏內',
|
|
546
|
+
'',
|
|
547
|
+
'- 屏內照常有清單',
|
|
548
|
+
'- 也照常有行內 `程式碼`',
|
|
549
|
+
'',
|
|
550
|
+
'# 兩屏才看得出捲動',
|
|
551
|
+
'',
|
|
552
|
+
'一屏看起來與一頁縮放過的長文沒有兩樣,兩屏才畫得出「往下捲,換一屏」這件事。',
|
|
553
|
+
'',
|
|
554
|
+
':::note',
|
|
555
|
+
'切屏的是頂層標題,不是切頁符:deck 用的那組三橫線在這個文體上只會畫出一條分隔線。',
|
|
556
|
+
':::',
|
|
557
|
+
'',
|
|
558
|
+
'# 三份分工',
|
|
559
|
+
'',
|
|
560
|
+
'- `manifest.toml` 管宣告與套用態(所騎的 layout、准入的 block、maxWidth 與 measure)',
|
|
561
|
+
'- `stylesheet.css` 管皮,包含屏的內距與列印形',
|
|
562
|
+
'- 這份 md 管預設樣貌',
|
|
563
|
+
'',
|
|
564
|
+
].join('\n')
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* The resume 預設 md. **Two sheets**, because the one thing a résumé scaffold has
|
|
568
|
+
* to demonstrate is the thing a single-sheet file cannot: that a top-level
|
|
569
|
+
* heading starts a **new sheet of paper**, and that a résumé section is `##`
|
|
570
|
+
* rather than `#`. A starter file with one heading would teach the opposite
|
|
571
|
+
* convention by omission, and the reader would find out at the printer.
|
|
572
|
+
*
|
|
573
|
+
* It opens with a line *before* the first heading, on purpose: together with the
|
|
574
|
+
* package's masthead that is the top of the first sheet — and, unlike every
|
|
575
|
+
* other 文體 here, it does **not** get a page of its own. An A4 sheet holding
|
|
576
|
+
* only a name is the failure this cut exists to avoid.
|
|
577
|
+
*
|
|
578
|
+
* No `---` anywhere. On this 文體 it is not a page break (that is a `deck` fact,
|
|
579
|
+
* `src/layouts/template-index.js`), so a stray one would come out as a thematic
|
|
580
|
+
* rule *inside* a sheet and teach exactly the wrong thing — the page break is
|
|
581
|
+
* the top-level heading.
|
|
582
|
+
*/
|
|
583
|
+
const resumeDocument = ({ namespace, name }) =>
|
|
584
|
+
[
|
|
585
|
+
'---',
|
|
586
|
+
`template: ${namespace}/${name}`,
|
|
587
|
+
`title: ${namespace}/${name}`,
|
|
588
|
+
'kicker: kamishibai 履歷骨架',
|
|
589
|
+
'author: you@example.org',
|
|
590
|
+
'---',
|
|
591
|
+
'',
|
|
592
|
+
`這份文件跟著模板包 \`${namespace}/${name}\` 一起被建立。第一個標題之前的段落`,
|
|
593
|
+
'不會自成一張紙,而是與抬頭一起排在**第一張紙的頂端**——紙上只印一個名字',
|
|
594
|
+
'是這個文體刻意避開的浪費。',
|
|
595
|
+
'',
|
|
596
|
+
'# 這一張紙',
|
|
597
|
+
'',
|
|
598
|
+
'一個頂層標題就是一張 A4。履歷的各段落請用 `##`:它們留在同一張紙裡,',
|
|
599
|
+
'而列印時一張紙就是一頁,不多也不少。',
|
|
600
|
+
'',
|
|
601
|
+
'## 經歷',
|
|
602
|
+
'',
|
|
603
|
+
'- **某某公司**(2023 - 至今)|職稱',
|
|
604
|
+
' - 一行寫清楚做了什麼、對誰有用',
|
|
605
|
+
'- **另一間公司**(2021 - 2023)|職稱',
|
|
606
|
+
'',
|
|
607
|
+
'## 技能',
|
|
608
|
+
'',
|
|
609
|
+
'- 一組工具、一組語言、一組領域知識',
|
|
610
|
+
'',
|
|
611
|
+
':::note',
|
|
612
|
+
'切紙的是頂層標題,不是切頁符:deck 用的那組三橫線在這個文體上只會畫出一條分隔線。',
|
|
613
|
+
':::',
|
|
614
|
+
'',
|
|
615
|
+
'# 第二張紙',
|
|
616
|
+
'',
|
|
617
|
+
'兩張紙才看得出換頁。第二張紙用來放作品、著作或推薦人——內容放不下的時候,',
|
|
618
|
+
'開一個新的頂層標題就是開一張新的紙。',
|
|
619
|
+
'',
|
|
620
|
+
'## 三份分工',
|
|
621
|
+
'',
|
|
622
|
+
'- `manifest.toml` 管宣告與套用態(所騎的 layout、准入的 block、maxWidth 與 measure)',
|
|
623
|
+
'- `stylesheet.css` 管皮,包含紙的內距、@page 邊界與列印形',
|
|
624
|
+
'- 這份 md 管預設樣貌',
|
|
625
|
+
'',
|
|
626
|
+
].join('\n')
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* The 文體 this scaffold can actually produce a **working** package for, and
|
|
630
|
+
* everything it needs in order to.
|
|
631
|
+
*
|
|
632
|
+
* 複驗 F-1 (F3): `-l` used to accept any string. `init x/y -l deck` therefore
|
|
633
|
+
* wrote a manifest declaring the `deck` 文體 next to an article-shaped 預設 md,
|
|
634
|
+
* and then printed 「開箱即用:kamishibai render …」 — a command that cannot
|
|
635
|
+
* work, because the deck layout requires a `deck` root form the scaffolded
|
|
636
|
+
* document does not have. Two harms, and the second is the worse one: a dead
|
|
637
|
+
* package is a file you can delete, while a wrong copy-paste command from the
|
|
638
|
+
* SDK's own mouth is something the user will trust and then blame themselves for.
|
|
639
|
+
*
|
|
640
|
+
* So the flag is narrowed to what this table has content for. `deck` joined it
|
|
641
|
+
* in F2f, together with the other half it could not work without — a store deck
|
|
642
|
+
* package's `---` actually cutting pages (`src/layouts/template-index.js`).
|
|
643
|
+
*/
|
|
644
|
+
export const SCAFFOLD_BLUEPRINTS = Object.freeze({
|
|
645
|
+
article: Object.freeze({
|
|
646
|
+
root: NO_ROOT,
|
|
647
|
+
blocks: ARTICLE_BLOCKS,
|
|
648
|
+
maxWidth: ARTICLE_MAX_WIDTH,
|
|
649
|
+
measure: ARTICLE_MEASURE,
|
|
650
|
+
stylesheet: articleStylesheet,
|
|
651
|
+
document: articleDocument,
|
|
652
|
+
}),
|
|
653
|
+
/**
|
|
654
|
+
* `card` joined the table in F2c, and the entry is the measurement that slice
|
|
655
|
+
* exists to take: supporting a third 文體 cost this object and nothing else —
|
|
656
|
+
* no edit to `template-scaffold.js`, no new branch anywhere in the writer.
|
|
657
|
+
*
|
|
658
|
+
* `root: NO_ROOT` restated for the same reason `article` restates it: the
|
|
659
|
+
* `card` 文體 requires no root form, and a manifest that said nothing would be
|
|
660
|
+
* a disagreement rather than a silence (`KSB_LAYOUT_ROOT_CONFLICT`).
|
|
661
|
+
*/
|
|
662
|
+
card: Object.freeze({
|
|
663
|
+
root: NO_ROOT,
|
|
664
|
+
blocks: ARTICLE_BLOCKS,
|
|
665
|
+
maxWidth: CARD_MAX_WIDTH,
|
|
666
|
+
measure: CARD_MEASURE,
|
|
667
|
+
stylesheet: cardStylesheet,
|
|
668
|
+
document: cardDocument,
|
|
669
|
+
}),
|
|
670
|
+
deck: Object.freeze({
|
|
671
|
+
/**
|
|
672
|
+
* Restated, never invented: the `deck` 文體 requires this root form, and a
|
|
673
|
+
* manifest that disagreed with its 文體 is refused outright
|
|
674
|
+
* (`KSB_LAYOUT_ROOT_CONFLICT`). Saying nothing would be a disagreement too.
|
|
675
|
+
*/
|
|
676
|
+
root: 'deck',
|
|
677
|
+
blocks: DECK_BLOCKS,
|
|
678
|
+
maxWidth: DECK_MAX_WIDTH,
|
|
679
|
+
measure: DECK_MEASURE,
|
|
680
|
+
stylesheet: deckStylesheet,
|
|
681
|
+
document: deckDocument,
|
|
682
|
+
}),
|
|
683
|
+
/**
|
|
684
|
+
* `one-page` joined the table in F2d — the first **bounded** 文體 the scaffold
|
|
685
|
+
* can produce a working package for. That matters here rather than being a
|
|
686
|
+
* label: the logical canvas is the 文體's and arrives on the markup, so this
|
|
687
|
+
* entry supplies no size at all, and the skin's whole job is to be a frame
|
|
688
|
+
* that does not grow (see `onePageStylesheet`). A blueprint that laid the
|
|
689
|
+
* screen out like an article would produce a package whose content overflows
|
|
690
|
+
* the very canvas the skeleton is scaling — a page that looks broken in a way
|
|
691
|
+
* no error ever mentions.
|
|
692
|
+
*
|
|
693
|
+
* `root: NO_ROOT` restated for the same reason `article` and `card` restate
|
|
694
|
+
* it: the 文體 requires no root form, and a manifest that said nothing would
|
|
695
|
+
* be a disagreement rather than a silence (`KSB_LAYOUT_ROOT_CONFLICT`).
|
|
696
|
+
*/
|
|
697
|
+
'one-page': Object.freeze({
|
|
698
|
+
root: NO_ROOT,
|
|
699
|
+
blocks: ARTICLE_BLOCKS,
|
|
700
|
+
maxWidth: ONE_PAGE_MAX_WIDTH,
|
|
701
|
+
measure: ONE_PAGE_MEASURE,
|
|
702
|
+
stylesheet: onePageStylesheet,
|
|
703
|
+
document: onePageDocument,
|
|
704
|
+
}),
|
|
705
|
+
/**
|
|
706
|
+
* `resume` joined the table in F2e, completing the v1 五版面. It is the first
|
|
707
|
+
* entry whose skin has to be right about **paper** rather than about a
|
|
708
|
+
* window: the `@page` box, the forced break between sheets and the print
|
|
709
|
+
* stand-down are not polish here, they are the difference between a PDF that
|
|
710
|
+
* is a résumé and a PDF that is a clipped ribbon of one.
|
|
711
|
+
*
|
|
712
|
+
* `root: NO_ROOT` restated for the same reason the other flowing-rooted
|
|
713
|
+
* entries restate it: the 文體 requires no root form, and a manifest that said
|
|
714
|
+
* nothing would be a disagreement rather than a silence
|
|
715
|
+
* (`KSB_LAYOUT_ROOT_CONFLICT`).
|
|
716
|
+
*/
|
|
717
|
+
resume: Object.freeze({
|
|
718
|
+
root: NO_ROOT,
|
|
719
|
+
blocks: ARTICLE_BLOCKS,
|
|
720
|
+
maxWidth: RESUME_MAX_WIDTH,
|
|
721
|
+
measure: RESUME_MEASURE,
|
|
722
|
+
stylesheet: resumeStylesheet,
|
|
723
|
+
document: resumeDocument,
|
|
724
|
+
}),
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
/** Verbatim — the 文體 `init -l` lets through, derived from the table above. */
|
|
728
|
+
export const SCAFFOLD_SUPPORTED_LAYOUTS = Object.freeze(Object.keys(SCAFFOLD_BLUEPRINTS))
|