@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.
Files changed (136) hide show
  1. package/README.md +192 -0
  2. package/package.json +54 -0
  3. package/src/blocks/board.js +210 -0
  4. package/src/blocks/callout.js +63 -0
  5. package/src/blocks/code.js +28 -0
  6. package/src/blocks/deck.js +76 -0
  7. package/src/blocks/diagram.js +265 -0
  8. package/src/blocks/element.js +51 -0
  9. package/src/blocks/graph.js +264 -0
  10. package/src/blocks/grid.js +156 -0
  11. package/src/blocks/index.js +106 -0
  12. package/src/blocks/list.js +50 -0
  13. package/src/blocks/placement.js +119 -0
  14. package/src/blocks/prose.js +28 -0
  15. package/src/blocks/quote.js +25 -0
  16. package/src/blocks/raw.js +47 -0
  17. package/src/blocks/registry.js +158 -0
  18. package/src/blocks/schema-parts.js +19 -0
  19. package/src/blocks/section.js +53 -0
  20. package/src/blocks/slide.js +104 -0
  21. package/src/blocks/stat.js +83 -0
  22. package/src/blocks/table.js +50 -0
  23. package/src/blocks/timeline.js +80 -0
  24. package/src/cli/commands/close.js +51 -0
  25. package/src/cli/commands/comments.js +73 -0
  26. package/src/cli/commands/debug.js +34 -0
  27. package/src/cli/commands/example.js +22 -0
  28. package/src/cli/commands/export.js +10 -0
  29. package/src/cli/commands/init.js +53 -0
  30. package/src/cli/commands/lint.js +93 -0
  31. package/src/cli/commands/list.js +54 -0
  32. package/src/cli/commands/open.js +31 -0
  33. package/src/cli/commands/promote.js +38 -0
  34. package/src/cli/commands/render.js +31 -0
  35. package/src/cli/commands/replay.js +49 -0
  36. package/src/cli/commands/schema.js +10 -0
  37. package/src/cli/commands/serve.js +178 -0
  38. package/src/cli/commands/setup.js +64 -0
  39. package/src/cli/commands/snapshot.js +29 -0
  40. package/src/cli/commands/templates.js +75 -0
  41. package/src/cli/deliver.js +54 -0
  42. package/src/cli/emit.js +29 -0
  43. package/src/cli/format.js +153 -0
  44. package/src/cli/index.js +365 -0
  45. package/src/cli/registry.js +18 -0
  46. package/src/core/blocks.js +147 -0
  47. package/src/core/diagram.js +282 -0
  48. package/src/core/errors.js +156 -0
  49. package/src/core/example.js +109 -0
  50. package/src/core/ir.js +62 -0
  51. package/src/core/lint-gates.js +427 -0
  52. package/src/core/lint.js +281 -0
  53. package/src/core/scan.js +84 -0
  54. package/src/core/schema.js +88 -0
  55. package/src/core/spec-check.js +33 -0
  56. package/src/core/validate.js +44 -0
  57. package/src/core/version.js +18 -0
  58. package/src/core/vocabulary.js +140 -0
  59. package/src/delivery/atomic.js +71 -0
  60. package/src/delivery/comments.js +180 -0
  61. package/src/delivery/home.js +70 -0
  62. package/src/delivery/open.js +31 -0
  63. package/src/delivery/project.js +141 -0
  64. package/src/delivery/read.js +109 -0
  65. package/src/delivery/run.js +95 -0
  66. package/src/delivery/scaffold-blueprints.js +728 -0
  67. package/src/delivery/store.js +219 -0
  68. package/src/delivery/template-extensions.js +183 -0
  69. package/src/delivery/template-format.js +112 -0
  70. package/src/delivery/template-package.js +376 -0
  71. package/src/delivery/template-promote.js +240 -0
  72. package/src/delivery/template-scaffold.js +181 -0
  73. package/src/delivery/templates.js +192 -0
  74. package/src/delivery/toml.js +195 -0
  75. package/src/delivery/write.js +35 -0
  76. package/src/export/browser.js +130 -0
  77. package/src/export/index.js +96 -0
  78. package/src/export/pdf.js +25 -0
  79. package/src/export/png.js +40 -0
  80. package/src/export/pptx.js +48 -0
  81. package/src/export/slides.js +33 -0
  82. package/src/export/snapshot.js +33 -0
  83. package/src/layouts/article.js +103 -0
  84. package/src/layouts/canvas.js +144 -0
  85. package/src/layouts/card.js +128 -0
  86. package/src/layouts/deck.js +88 -0
  87. package/src/layouts/index.js +90 -0
  88. package/src/layouts/one-page.js +161 -0
  89. package/src/layouts/registry.js +251 -0
  90. package/src/layouts/resume.js +172 -0
  91. package/src/layouts/template-index.js +78 -0
  92. package/src/parser/artifact.js +38 -0
  93. package/src/parser/container.js +103 -0
  94. package/src/parser/index.js +223 -0
  95. package/src/parser/tokens.js +265 -0
  96. package/src/render/board-filter.client.js +80 -0
  97. package/src/render/compile.js +29 -0
  98. package/src/render/context.js +98 -0
  99. package/src/render/element.js +32 -0
  100. package/src/render/fonts.js +129 -0
  101. package/src/render/graph-hover.client.js +148 -0
  102. package/src/render/html.js +52 -0
  103. package/src/render/index.js +241 -0
  104. package/src/render/measure.js +60 -0
  105. package/src/render/placement.js +136 -0
  106. package/src/render/playback.client.js +74 -0
  107. package/src/render/scale-to-fit.client.js +136 -0
  108. package/src/render/scale.js +41 -0
  109. package/src/render/skeleton.js +131 -0
  110. package/src/render/ssr.js +24 -0
  111. package/src/render/styles.js +56 -0
  112. package/src/render/templates.js +191 -0
  113. package/src/serve/daemon.js +117 -0
  114. package/src/serve/overlay.js +213 -0
  115. package/src/serve/protocol.js +36 -0
  116. package/src/serve/server.js +264 -0
  117. package/templates/kami/cards/components.js +40 -0
  118. package/templates/kami/cards/index.js +25 -0
  119. package/templates/kami/cards/manifest.js +67 -0
  120. package/templates/kami/cards/styles.css +389 -0
  121. package/templates/kami/long-form/components.js +102 -0
  122. package/templates/kami/long-form/index.js +25 -0
  123. package/templates/kami/long-form/manifest.js +87 -0
  124. package/templates/kami/long-form/styles.css +481 -0
  125. package/templates/kami/one-page/components.js +48 -0
  126. package/templates/kami/one-page/index.js +27 -0
  127. package/templates/kami/one-page/manifest.js +65 -0
  128. package/templates/kami/one-page/styles.css +375 -0
  129. package/templates/kami/resume/components.js +51 -0
  130. package/templates/kami/resume/index.js +27 -0
  131. package/templates/kami/resume/manifest.js +65 -0
  132. package/templates/kami/resume/styles.css +424 -0
  133. package/templates/kami/slides/components.js +41 -0
  134. package/templates/kami/slides/index.js +26 -0
  135. package/templates/kami/slides/manifest.js +64 -0
  136. package/templates/kami/slides/styles.css +406 -0
@@ -0,0 +1,481 @@
1
+ :root {
2
+ --paper: #f5f4ed;
3
+ --surface: #faf9f5;
4
+ --accent: #1B365D;
5
+ --accent-soft: #dbe2ec;
6
+ --rule: #ddd9cc;
7
+ --text-primary: #23201a;
8
+ --text-secondary: #5c574c;
9
+ --text-muted: #8a8371;
10
+ --code-bg: #efece1;
11
+ --warn: #8a5a12;
12
+ /* ksb:measure --measure */
13
+ /* ksb:maxWidth --max-width */
14
+ /* ksb:railWidth --rail-width */
15
+ /* Decoration, not geometry: the sum that makes --max-width work lives in
16
+ manifest.js (rail + gap + measure + the paper's own padding). */
17
+ --rail-gap: 44px;
18
+ /* 宣告即內嵌(CONTRACT A4):只列真的隨產物出貨的具名字族,
19
+ 其餘一律交給 generic 關鍵字,避免宣告一個產物裡不存在的字族。
20
+ 等寬字族的內嵌延至 S3,故 S1 等寬僅用 generic monospace。 */
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(--paper);
37
+ color: var(--text-primary);
38
+ font-family: var(--serif);
39
+ font-size: 17px;
40
+ line-height: 1.55;
41
+ -webkit-font-smoothing: antialiased;
42
+ }
43
+
44
+ .paper {
45
+ max-width: var(--measure);
46
+ margin: 0 auto;
47
+ padding: 72px 28px 96px;
48
+ background: var(--surface);
49
+ min-height: 100vh;
50
+ box-shadow: 0 0 0 1px var(--rule);
51
+ }
52
+
53
+ /* The rail: the `article` layout's side region, switched on by this template's
54
+ manifest. An ordinary two-column page -- masthead across the top, rail beside
55
+ the body, colophon across the bottom -- and the tracks are the applied state
56
+ of the layout's knobs, so nothing here is a number this file invented. */
57
+ .paper[data-rail] {
58
+ max-width: var(--max-width);
59
+ display: grid;
60
+ grid-template-columns: var(--rail-width) minmax(0, 1fr);
61
+ column-gap: var(--rail-gap);
62
+ align-items: start;
63
+ }
64
+
65
+ .paper[data-rail="end"] {
66
+ grid-template-columns: minmax(0, 1fr) var(--rail-width);
67
+ }
68
+
69
+ .paper[data-rail] > .masthead,
70
+ .paper[data-rail] > .colophon {
71
+ grid-column: 1 / -1;
72
+ }
73
+
74
+ .paper[data-rail="end"] > .doc-rail {
75
+ grid-column: 2;
76
+ }
77
+
78
+ .paper[data-rail="end"] > .doc-body {
79
+ grid-column: 1;
80
+ }
81
+
82
+ /* An empty rail is a document with no headings under a template whose applied
83
+ state is "rail on". The region stays in the markup (it is the template's
84
+ state, not the document's) and the page simply goes back to one column --
85
+ collapsing it here rather than in the markup is what keeps the two answers
86
+ from disagreeing. */
87
+ .doc-rail:empty {
88
+ display: none;
89
+ }
90
+
91
+ .paper[data-rail]:has(> .doc-rail:empty) {
92
+ display: block;
93
+ max-width: var(--measure);
94
+ }
95
+
96
+ /* Sticky, because the whole point of a rail on a long document is that it is
97
+ still there on page nine. It scrolls on its own once it outgrows the window. */
98
+ .doc-rail {
99
+ position: sticky;
100
+ top: 40px;
101
+ align-self: start;
102
+ max-height: calc(100vh - 80px);
103
+ overflow-y: auto;
104
+ font-size: 14px;
105
+ line-height: 1.5;
106
+ }
107
+
108
+ .doc-toc .toc-heading {
109
+ margin: 0 0 12px;
110
+ padding-bottom: 8px;
111
+ border-bottom: 1px solid var(--rule);
112
+ font-size: 11px;
113
+ letter-spacing: 0.22em;
114
+ text-transform: uppercase;
115
+ color: var(--accent);
116
+ }
117
+
118
+ .doc-toc .toc-list {
119
+ margin: 0;
120
+ padding: 0;
121
+ list-style: none;
122
+ }
123
+
124
+ .doc-toc .toc-list .toc-list {
125
+ margin: 6px 0 0;
126
+ padding-left: 12px;
127
+ border-left: 1px solid var(--rule);
128
+ }
129
+
130
+ .doc-toc .toc-item {
131
+ margin: 0 0 8px;
132
+ }
133
+
134
+ .doc-toc .toc-item:last-child {
135
+ margin-bottom: 0;
136
+ }
137
+
138
+ .doc-toc .toc-link {
139
+ color: var(--text-secondary);
140
+ text-decoration: none;
141
+ text-underline-offset: 3px;
142
+ }
143
+
144
+ .doc-toc .toc-link:hover {
145
+ color: var(--accent);
146
+ text-decoration: underline;
147
+ }
148
+
149
+ .masthead {
150
+ border-bottom: 2px solid var(--accent);
151
+ padding-bottom: 20px;
152
+ margin-bottom: 48px;
153
+ }
154
+
155
+ .masthead .kicker {
156
+ font-size: 12px;
157
+ letter-spacing: 0.22em;
158
+ text-transform: uppercase;
159
+ color: var(--accent);
160
+ margin: 0 0 10px;
161
+ }
162
+
163
+ .masthead .doc-title {
164
+ font-size: 34px;
165
+ line-height: 1.3;
166
+ font-weight: 700;
167
+ margin: 0;
168
+ color: var(--text-primary);
169
+ }
170
+
171
+ .masthead .byline {
172
+ margin: 12px 0 0;
173
+ font-size: 14px;
174
+ color: var(--text-secondary);
175
+ }
176
+
177
+ .section {
178
+ margin: 0 0 40px;
179
+ }
180
+
181
+ .section > .section-title {
182
+ font-weight: 700;
183
+ color: var(--accent);
184
+ line-height: 1.35;
185
+ }
186
+
187
+ .section-l1 > .section-title {
188
+ font-size: 26px;
189
+ margin: 56px 0 20px;
190
+ padding-bottom: 8px;
191
+ border-bottom: 1px solid var(--rule);
192
+ }
193
+
194
+ .section-l2 > .section-title {
195
+ font-size: 21px;
196
+ margin: 36px 0 14px;
197
+ }
198
+
199
+ .section-l3 > .section-title,
200
+ .section-l4 > .section-title,
201
+ .section-l5 > .section-title,
202
+ .section-l6 > .section-title {
203
+ font-size: 18px;
204
+ margin: 28px 0 12px;
205
+ color: var(--text-primary);
206
+ }
207
+
208
+ .prose {
209
+ margin: 0 0 18px;
210
+ line-height: 1.55;
211
+ }
212
+
213
+ .prose a {
214
+ color: var(--accent);
215
+ text-underline-offset: 3px;
216
+ }
217
+
218
+ /* 清單有兩種來路:list block 的 <ul|ol class="list">(S3a 起的正路),
219
+ 以及 raw 島嶼等塞進 prose 的清單 HTML(<p> 不能包 block-level,故承載於
220
+ <div class="prose">)。兩者共用同一組規則——行高與段距與 .prose 一致,
221
+ 縮排剛好讓標記懸在欄寬外緣;否則同一份語料會依來路長得不一樣。 */
222
+ .prose ul,
223
+ .prose ol,
224
+ .list {
225
+ margin: 0 0 18px;
226
+ padding-left: 1.4em;
227
+ line-height: 1.55;
228
+ }
229
+
230
+ .prose li,
231
+ .list li {
232
+ margin: 0 0 6px;
233
+ line-height: 1.55;
234
+ }
235
+
236
+ .prose li:last-child,
237
+ .list li:last-child {
238
+ margin-bottom: 0;
239
+ }
240
+
241
+ .prose li::marker,
242
+ .list li::marker {
243
+ color: var(--text-muted);
244
+ }
245
+
246
+ .prose ul ul,
247
+ .prose ul ol,
248
+ .prose ol ul,
249
+ .prose ol ol,
250
+ .list .list {
251
+ margin: 6px 0 0;
252
+ }
253
+
254
+ /* 項目內容是真的子 block(prose / callout / code …),各自帶著自己的段距。
255
+ 在 li 裡那些段距會把每一項撐成一大塊,故於此收斂。 */
256
+ .list li > .prose {
257
+ margin: 0;
258
+ }
259
+
260
+ .list li > .prose + * {
261
+ margin-top: 8px;
262
+ }
263
+
264
+ .list li > .callout,
265
+ .list li > .quote,
266
+ .list li > .code,
267
+ .list li > .table-wrap,
268
+ .list li > .island {
269
+ margin: 8px 0 0;
270
+ }
271
+
272
+ .prose code,
273
+ .callout code,
274
+ .quote code,
275
+ .table code {
276
+ font-family: var(--mono);
277
+ font-size: 0.9em;
278
+ background: var(--code-bg);
279
+ border-radius: 3px;
280
+ padding: 1px 5px;
281
+ }
282
+
283
+ .quote {
284
+ margin: 0 0 22px;
285
+ padding: 6px 0 6px 20px;
286
+ border-left: 3px solid var(--accent-soft);
287
+ color: var(--text-secondary);
288
+ }
289
+
290
+ .quote > .prose:last-child {
291
+ margin-bottom: 0;
292
+ }
293
+
294
+ .callout {
295
+ margin: 0 0 22px;
296
+ padding: 16px 20px;
297
+ background: var(--paper);
298
+ border-left: 4px solid var(--accent);
299
+ border-radius: 2px;
300
+ }
301
+
302
+ .callout-warn {
303
+ border-left-color: var(--warn);
304
+ }
305
+
306
+ .callout .callout-label {
307
+ display: block;
308
+ font-size: 11px;
309
+ letter-spacing: 0.18em;
310
+ text-transform: uppercase;
311
+ color: var(--accent);
312
+ margin-bottom: 6px;
313
+ }
314
+
315
+ .callout-warn .callout-label {
316
+ color: var(--warn);
317
+ }
318
+
319
+ .callout > .prose:last-child {
320
+ margin-bottom: 0;
321
+ }
322
+
323
+ .code {
324
+ margin: 0 0 22px;
325
+ padding: 16px 18px;
326
+ background: var(--code-bg);
327
+ border: 1px solid var(--rule);
328
+ border-radius: 4px;
329
+ overflow-x: auto;
330
+ font-family: var(--mono);
331
+ font-size: 14px;
332
+ line-height: 1.5;
333
+ }
334
+
335
+ .code code {
336
+ font-family: inherit;
337
+ }
338
+
339
+ .table-wrap {
340
+ margin: 0 0 22px;
341
+ overflow-x: auto;
342
+ }
343
+
344
+ .table {
345
+ width: 100%;
346
+ border-collapse: collapse;
347
+ font-size: 15px;
348
+ }
349
+
350
+ .table th,
351
+ .table td {
352
+ border-bottom: 1px solid var(--rule);
353
+ padding: 9px 12px;
354
+ text-align: left;
355
+ vertical-align: top;
356
+ }
357
+
358
+ .table th {
359
+ color: var(--accent);
360
+ font-weight: 700;
361
+ border-bottom: 2px solid var(--accent-soft);
362
+ }
363
+
364
+ /* 密集表(五欄以上,如錯字修改表的六欄 findings):欄寬一旦被 --measure
365
+ 均分就窄到中文一字一行。寧可撐出下界、讓 .table-wrap 橫向捲動,
366
+ 也不要把可讀性押在欄數上。以欄數選取而非新增 class——Table 元件
367
+ 只吐 .table-wrap / .table 兩個 hook,模板樣式不得要求 IR 多帶資訊。 */
368
+ .table:has(thead th:nth-child(5)) {
369
+ min-width: 660px;
370
+ font-size: 14px;
371
+ }
372
+
373
+ .table:has(thead th:nth-child(5)) th,
374
+ .table:has(thead th:nth-child(5)) td {
375
+ padding: 8px 10px;
376
+ }
377
+
378
+ /* 第一欄常是頁碼/編號一類的短鍵,讓它不要被長文欄拉開。 */
379
+ .table:has(thead th:nth-child(5)) th:first-child,
380
+ .table:has(thead th:nth-child(5)) td:first-child {
381
+ white-space: nowrap;
382
+ width: 1%;
383
+ color: var(--text-secondary);
384
+ }
385
+
386
+ .island {
387
+ margin: 0 0 22px;
388
+ }
389
+
390
+ /* diagram(CONTRACT D1)——幾何由 core/diagram.js 算出,樣式只上色。
391
+ 這段在兩套模板中逐字相同,且只用兩邊取值一致的變數(--accent /
392
+ --accent-soft / --text-primary / --text-secondary),所以同一份 spec
393
+ 在長文與簡報下畫出的是同一張圖,不是「看起來很像」的兩張。 */
394
+ .diagram {
395
+ margin: 0 0 22px;
396
+ overflow-x: auto;
397
+ }
398
+
399
+ .diagram-svg {
400
+ display: block;
401
+ max-width: 100%;
402
+ height: auto;
403
+ font-family: var(--serif);
404
+ }
405
+
406
+ .diagram-node-box {
407
+ fill: var(--accent-soft);
408
+ stroke: var(--accent);
409
+ stroke-width: 1.5;
410
+ }
411
+
412
+ .diagram-node-label {
413
+ fill: var(--text-primary);
414
+ font-size: 15px;
415
+ }
416
+
417
+ .diagram-edge-line {
418
+ fill: none;
419
+ stroke: var(--accent);
420
+ stroke-width: 1.5;
421
+ }
422
+
423
+ .diagram-arrow {
424
+ fill: var(--accent);
425
+ }
426
+
427
+ .diagram-edge-label {
428
+ fill: var(--text-secondary);
429
+ font-size: 12px;
430
+ }
431
+
432
+ .colophon {
433
+ margin-top: 64px;
434
+ padding-top: 16px;
435
+ border-top: 1px solid var(--rule);
436
+ font-size: 12px;
437
+ color: var(--text-muted);
438
+ letter-spacing: 0.06em;
439
+ }
440
+
441
+ /* Narrow window: the rail steps out of the way rather than squeezing the text.
442
+ It is first in the DOM, so going back to one column moves it above the body --
443
+ a table of contents at the top of the page, which is what a phone wants.
444
+ The flowing 文體 stays flowing: no fixed canvas, no scaling, measure intact. */
445
+ @media (max-width: 900px) {
446
+ .paper[data-rail] {
447
+ display: block;
448
+ max-width: var(--measure);
449
+ }
450
+
451
+ .doc-rail {
452
+ position: static;
453
+ max-height: none;
454
+ overflow-y: visible;
455
+ margin-bottom: 40px;
456
+ padding-bottom: 24px;
457
+ border-bottom: 1px solid var(--rule);
458
+ }
459
+ }
460
+
461
+ @media print {
462
+ body {
463
+ background: #fff;
464
+ }
465
+
466
+ .paper {
467
+ box-shadow: none;
468
+ }
469
+
470
+ /* On paper a table of contents is navigation for a device nobody is holding,
471
+ and its links cannot be followed. The column goes; the text keeps its
472
+ measure. */
473
+ .paper[data-rail] {
474
+ display: block;
475
+ max-width: var(--measure);
476
+ }
477
+
478
+ .doc-rail {
479
+ display: none;
480
+ }
481
+ }
@@ -0,0 +1,48 @@
1
+ import { el } from '../../../src/blocks/element.js'
2
+ import { manifest } from './manifest.js'
3
+
4
+ /**
5
+ * Chrome of the Kami one-page template.
6
+ *
7
+ * The shape of the page — the screens, their fixed logical canvas, which
8
+ * top-level blocks join the opening screen — is the `one-page` 文體's, and every
9
+ * template on that layout gets the same one (CONTRACT C1). What is left here is
10
+ * exactly this template's own business: the masthead, the colophon, and every
11
+ * word of them.
12
+ *
13
+ * Nothing here is a component: each slot returns the same neutral element tree a
14
+ * block module returns, so this file is data with a little arithmetic in it and
15
+ * imports no rendering library at all (CONTRACT A1).
16
+ *
17
+ * The masthead goes into `page-lead`, which the 文體 draws **inside the opening
18
+ * screen** rather than above the site — on a bounded 文體 anything outside a
19
+ * canvas is unscaled, so a masthead floating above the first screen would be the
20
+ * one element that ignores the window. There is deliberately no computed chrome
21
+ * (unlike `kami/long-form`'s table of contents): on a site you scroll, the
22
+ * screens are the navigation.
23
+ */
24
+
25
+ const TEMPLATE_LABEL = `${manifest.namespace}/${manifest.name}`
26
+
27
+ const masthead = (meta) =>
28
+ el('header', { class: 'masthead' }, [
29
+ meta.kicker ? el('p', { class: 'kicker' }, [meta.kicker]) : null,
30
+ el('h1', { class: 'doc-title' }, [meta.title]),
31
+ meta.author || meta.date
32
+ ? el('p', { class: 'byline' }, [[meta.author, meta.date].filter(Boolean).join(' · ')])
33
+ : null,
34
+ ])
35
+
36
+ export const chrome = Object.freeze({
37
+ /** The opening screen: who wrote this and what it is called. */
38
+ 'page-lead': (meta) => [masthead(meta)],
39
+
40
+ /**
41
+ * Below the last screen: the engine and skin that drew the page, in this
42
+ * template's words. Outside every canvas on purpose — a colophon is the one
43
+ * thing on a site that is allowed to be small.
44
+ */
45
+ 'page-foot': () => [
46
+ el('footer', { class: 'colophon' }, [`kamishibai · ${TEMPLATE_LABEL}@${manifest.version}`]),
47
+ ],
48
+ })
@@ -0,0 +1,27 @@
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 `root`: the shape of the page belongs to the `one-page` layout this
12
+ * manifest names, and this package supplies only skin (`styles`), config
13
+ * (`manifest`) and chrome (CONTRACT C1). The scale-to-fit behaviour is not here
14
+ * either — it is skeleton, keyed on the bounded canvas, so a third-party skin on
15
+ * this 文體 inherits it and cannot decline it (`src/render/skeleton.js`).
16
+ */
17
+ export default Object.freeze({
18
+ manifest,
19
+ key: templateKey,
20
+ chrome,
21
+ language: manifest.language,
22
+ /** Fonts subset into the artifact (SPEC §7.2 出廠字體堆疊). */
23
+ fonts: Object.freeze([
24
+ Object.freeze({ package: '@fontsource/noto-serif-tc', weights: Object.freeze(['400', '700']) }),
25
+ ]),
26
+ styles: () => readFileSync(STYLES_PATH, 'utf8'),
27
+ })
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Template package descriptor (SPEC §5.2 `[template]` section) — the built-in
3
+ * generic skin of the `one-page` 文體.
4
+ */
5
+ export const manifest = Object.freeze({
6
+ namespace: 'kami',
7
+ name: 'one-page',
8
+ version: '0.1.0',
9
+ description: 'Kami 一頁式模板 — 一屏一畫布的通用靜態站台版面',
10
+ language: 'zh-TW',
11
+ /**
12
+ * The 文體 this skin is a skin *of* (CONTRACT C1). The frame, the screens,
13
+ * the fixed logical canvas and the two chrome slots all come from there; this
14
+ * package only decides what they look like.
15
+ */
16
+ layout: 'one-page',
17
+ /** No required root form: this template renders whatever sits under `doc`. */
18
+ root: null,
19
+ blocks: Object.freeze([
20
+ 'doc',
21
+ 'section',
22
+ 'prose',
23
+ 'list',
24
+ 'quote',
25
+ 'callout',
26
+ 'code',
27
+ 'table',
28
+ 'raw',
29
+ 'diagram',
30
+ // F6a 四型資料 block(11 號票 P1「資料」)。五張 Kami 皮一起宣告:
31
+ // 詞彙表是准入規則,一張皮少宣告一種,同一份文件在它底下就會被
32
+ // KSB_TEMPLATE_BLOCK_UNSUPPORTED 擋掉——而那不是守門,是缺角。
33
+ 'stat',
34
+ 'timeline',
35
+ 'board',
36
+ 'graph',
37
+ ]),
38
+ /**
39
+ * 轉子/<型別>/config — the callout labels are *wording*, and wording is this
40
+ * template's answer rather than the SDK's (CONTRACT A7). Identical to the
41
+ * other Kami skins on purpose: they are one family, and a site that said
42
+ * 「注意」 while the long-form page said `NOTE` would read as two products.
43
+ */
44
+ blockConfig: Object.freeze({
45
+ callout: Object.freeze({ labels: Object.freeze({ note: 'NOTE', warn: 'WARNING' }) }),
46
+ }),
47
+ /**
48
+ * The applied state of the `one-page` layout's geometry, in CSS pixels.
49
+ *
50
+ * The logical canvas (1440×900) is **not** here: it belongs to the 文體 and
51
+ * arrives with the layout, exactly as the deck's 1280×720 does. What this
52
+ * template supplies is the pair every 文體 has — `maxWidth` 1200 is how wide
53
+ * the content region inside a screen may grow, and `measure` 740 is still the
54
+ * width of a line of text (CONTRACT C3). Collapsing them would either squeeze
55
+ * a full-bleed screen into a column of prose or stretch every line across the
56
+ * whole canvas.
57
+ *
58
+ * `styles.css` holds neither number; it declares where they go and the render
59
+ * layer writes them in (`src/render/styles.js`).
60
+ */
61
+ maxWidth: 1200,
62
+ measure: 740,
63
+ })
64
+
65
+ export const templateKey = `${manifest.namespace}/${manifest.name}`