contour-deck 0.0.0-stage → 0.2.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.
Files changed (94) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +261 -2
  3. package/dist/astro/Deck.astro +68 -0
  4. package/dist/astro/host.d.ts +20 -0
  5. package/dist/astro/host.js +170 -0
  6. package/dist/astro/legacy.d.ts +22 -0
  7. package/dist/astro/legacy.js +42 -0
  8. package/dist/core/deck.d.ts +2 -0
  9. package/dist/core/deck.js +598 -0
  10. package/dist/core/dom.d.ts +8 -0
  11. package/dist/core/dom.js +22 -0
  12. package/dist/core/gestures.d.ts +6 -0
  13. package/dist/core/gestures.js +24 -0
  14. package/dist/core/index.d.ts +3 -0
  15. package/dist/core/index.js +2 -0
  16. package/dist/core/labels.d.ts +2 -0
  17. package/dist/core/labels.js +20 -0
  18. package/dist/core/model.d.ts +37 -0
  19. package/dist/core/model.js +51 -0
  20. package/dist/core/navigator.d.ts +6 -0
  21. package/dist/core/navigator.js +12 -0
  22. package/dist/core/notes.d.ts +11 -0
  23. package/dist/core/notes.js +34 -0
  24. package/dist/core/overview.d.ts +11 -0
  25. package/dist/core/overview.js +104 -0
  26. package/dist/core/rail.d.ts +8 -0
  27. package/dist/core/rail.js +26 -0
  28. package/dist/core/stops.d.ts +17 -0
  29. package/dist/core/stops.js +40 -0
  30. package/dist/core/transitions.d.ts +9 -0
  31. package/dist/core/transitions.js +37 -0
  32. package/dist/core/types.d.ts +82 -0
  33. package/dist/core/types.js +1 -0
  34. package/dist/rehype/hast.d.ts +26 -0
  35. package/dist/rehype/hast.js +48 -0
  36. package/dist/rehype/index.d.ts +12 -0
  37. package/dist/rehype/index.js +221 -0
  38. package/dist/rehype/page.d.ts +2 -0
  39. package/dist/rehype/page.js +84 -0
  40. package/dist/rehype/reading.d.ts +11 -0
  41. package/dist/rehype/reading.js +9 -0
  42. package/dist/rehype/titles.d.ts +19 -0
  43. package/dist/rehype/titles.js +63 -0
  44. package/dist/remark/index.d.ts +20 -0
  45. package/dist/remark/index.js +117 -0
  46. package/dist/remote/clock.d.ts +35 -0
  47. package/dist/remote/clock.js +48 -0
  48. package/dist/remote/guest.d.ts +41 -0
  49. package/dist/remote/guest.js +223 -0
  50. package/dist/remote/index.d.ts +18 -0
  51. package/dist/remote/index.js +11 -0
  52. package/dist/remote/ink.d.ts +11 -0
  53. package/dist/remote/ink.js +74 -0
  54. package/dist/remote/labels.d.ts +72 -0
  55. package/dist/remote/labels.js +68 -0
  56. package/dist/remote/links.d.ts +2 -0
  57. package/dist/remote/links.js +12 -0
  58. package/dist/remote/overlay.d.ts +11 -0
  59. package/dist/remote/overlay.js +63 -0
  60. package/dist/remote/pairing.d.ts +40 -0
  61. package/dist/remote/pairing.js +97 -0
  62. package/dist/remote/panel.d.ts +38 -0
  63. package/dist/remote/panel.js +131 -0
  64. package/dist/remote/phone.d.ts +10 -0
  65. package/dist/remote/phone.js +407 -0
  66. package/dist/remote/presenter.d.ts +2 -0
  67. package/dist/remote/presenter.js +126 -0
  68. package/dist/remote/protocol.d.ts +106 -0
  69. package/dist/remote/protocol.js +123 -0
  70. package/dist/remote/qr.d.ts +8 -0
  71. package/dist/remote/qr.js +266 -0
  72. package/dist/remote/sensor.d.ts +9 -0
  73. package/dist/remote/sensor.js +50 -0
  74. package/dist/remote/session.d.ts +39 -0
  75. package/dist/remote/session.js +287 -0
  76. package/dist/remote/store.d.ts +9 -0
  77. package/dist/remote/store.js +27 -0
  78. package/dist/remote/style.d.ts +2 -0
  79. package/dist/remote/style.js +131 -0
  80. package/dist/remote/tilt.d.ts +78 -0
  81. package/dist/remote/tilt.js +148 -0
  82. package/dist/remote/transport.d.ts +35 -0
  83. package/dist/remote/transport.js +162 -0
  84. package/dist/remote/view.d.ts +48 -0
  85. package/dist/remote/view.js +119 -0
  86. package/dist/shared/markers.d.ts +15 -0
  87. package/dist/shared/markers.js +15 -0
  88. package/dist/style.css +629 -0
  89. package/docs/CONTRACT.md +178 -0
  90. package/docs/PROTOCOL.md +99 -0
  91. package/package.json +89 -4
  92. package/styles/base.css +3 -0
  93. package/styles/deck.css +151 -0
  94. package/styles/index.css +3 -0
package/LICENSE ADDED
@@ -0,0 +1,373 @@
1
+ Mozilla Public License Version 2.0
2
+ ==================================
3
+
4
+ 1. Definitions
5
+ --------------
6
+
7
+ 1.1. "Contributor"
8
+ means each individual or legal entity that creates, contributes to
9
+ the creation of, or owns Covered Software.
10
+
11
+ 1.2. "Contributor Version"
12
+ means the combination of the Contributions of others (if any) used
13
+ by a Contributor and that particular Contributor's Contribution.
14
+
15
+ 1.3. "Contribution"
16
+ means Covered Software of a particular Contributor.
17
+
18
+ 1.4. "Covered Software"
19
+ means Source Code Form to which the initial Contributor has attached
20
+ the notice in Exhibit A, the Executable Form of such Source Code
21
+ Form, and Modifications of such Source Code Form, in each case
22
+ including portions thereof.
23
+
24
+ 1.5. "Incompatible With Secondary Licenses"
25
+ means
26
+
27
+ (a) that the initial Contributor has attached the notice described
28
+ in Exhibit B to the Covered Software; or
29
+
30
+ (b) that the Covered Software was made available under the terms of
31
+ version 1.1 or earlier of the License, but not also under the
32
+ terms of a Secondary License.
33
+
34
+ 1.6. "Executable Form"
35
+ means any form of the work other than Source Code Form.
36
+
37
+ 1.7. "Larger Work"
38
+ means a work that combines Covered Software with other material, in
39
+ a separate file or files, that is not Covered Software.
40
+
41
+ 1.8. "License"
42
+ means this document.
43
+
44
+ 1.9. "Licensable"
45
+ means having the right to grant, to the maximum extent possible,
46
+ whether at the time of the initial grant or subsequently, any and
47
+ all of the rights conveyed by this License.
48
+
49
+ 1.10. "Modifications"
50
+ means any of the following:
51
+
52
+ (a) any file in Source Code Form that results from an addition to,
53
+ deletion from, or modification of the contents of Covered
54
+ Software; or
55
+
56
+ (b) any new file in Source Code Form that contains any Covered
57
+ Software.
58
+
59
+ 1.11. "Patent Claims" of a Contributor
60
+ means any patent claim(s), including without limitation, method,
61
+ process, and apparatus claims, in any patent Licensable by such
62
+ Contributor that would be infringed, but for the grant of the
63
+ License, by the making, using, selling, offering for sale, having
64
+ made, import, or transfer of either its Contributions or its
65
+ Contributor Version.
66
+
67
+ 1.12. "Secondary License"
68
+ means either the GNU General Public License, Version 2.0, the GNU
69
+ Lesser General Public License, Version 2.1, the GNU Affero General
70
+ Public License, Version 3.0, or any later versions of those
71
+ licenses.
72
+
73
+ 1.13. "Source Code Form"
74
+ means the form of the work preferred for making modifications.
75
+
76
+ 1.14. "You" (or "Your")
77
+ means an individual or a legal entity exercising rights under this
78
+ License. For legal entities, "You" includes any entity that
79
+ controls, is controlled by, or is under common control with You. For
80
+ purposes of this definition, "control" means (a) the power, direct
81
+ or indirect, to cause the direction or management of such entity,
82
+ whether by contract or otherwise, or (b) ownership of more than
83
+ fifty percent (50%) of the outstanding shares or beneficial
84
+ ownership of such entity.
85
+
86
+ 2. License Grants and Conditions
87
+ --------------------------------
88
+
89
+ 2.1. Grants
90
+
91
+ Each Contributor hereby grants You a world-wide, royalty-free,
92
+ non-exclusive license:
93
+
94
+ (a) under intellectual property rights (other than patent or trademark)
95
+ Licensable by such Contributor to use, reproduce, make available,
96
+ modify, display, perform, distribute, and otherwise exploit its
97
+ Contributions, either on an unmodified basis, with Modifications, or
98
+ as part of a Larger Work; and
99
+
100
+ (b) under Patent Claims of such Contributor to make, use, sell, offer
101
+ for sale, have made, import, and otherwise transfer either its
102
+ Contributions or its Contributor Version.
103
+
104
+ 2.2. Effective Date
105
+
106
+ The licenses granted in Section 2.1 with respect to any Contribution
107
+ become effective for each Contribution on the date the Contributor first
108
+ distributes such Contribution.
109
+
110
+ 2.3. Limitations on Grant Scope
111
+
112
+ The licenses granted in this Section 2 are the only rights granted under
113
+ this License. No additional rights or licenses will be implied from the
114
+ distribution or licensing of Covered Software under this License.
115
+ Notwithstanding Section 2.1(b) above, no patent license is granted by a
116
+ Contributor:
117
+
118
+ (a) for any code that a Contributor has removed from Covered Software;
119
+ or
120
+
121
+ (b) for infringements caused by: (i) Your and any other third party's
122
+ modifications of Covered Software, or (ii) the combination of its
123
+ Contributions with other software (except as part of its Contributor
124
+ Version); or
125
+
126
+ (c) under Patent Claims infringed by Covered Software in the absence of
127
+ its Contributions.
128
+
129
+ This License does not grant any rights in the trademarks, service marks,
130
+ or logos of any Contributor (except as may be necessary to comply with
131
+ the notice requirements in Section 3.4).
132
+
133
+ 2.4. Subsequent Licenses
134
+
135
+ No Contributor makes additional grants as a result of Your choice to
136
+ distribute the Covered Software under a subsequent version of this
137
+ License (see Section 10.2) or under the terms of a Secondary License (if
138
+ permitted under the terms of Section 3.3).
139
+
140
+ 2.5. Representation
141
+
142
+ Each Contributor represents that the Contributor believes its
143
+ Contributions are its original creation(s) or it has sufficient rights
144
+ to grant the rights to its Contributions conveyed by this License.
145
+
146
+ 2.6. Fair Use
147
+
148
+ This License is not intended to limit any rights You have under
149
+ applicable copyright doctrines of fair use, fair dealing, or other
150
+ equivalents.
151
+
152
+ 2.7. Conditions
153
+
154
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155
+ in Section 2.1.
156
+
157
+ 3. Responsibilities
158
+ -------------------
159
+
160
+ 3.1. Distribution of Source Form
161
+
162
+ All distribution of Covered Software in Source Code Form, including any
163
+ Modifications that You create or to which You contribute, must be under
164
+ the terms of this License. You must inform recipients that the Source
165
+ Code Form of the Covered Software is governed by the terms of this
166
+ License, and how they can obtain a copy of this License. You may not
167
+ attempt to alter or restrict the recipients' rights in the Source Code
168
+ Form.
169
+
170
+ 3.2. Distribution of Executable Form
171
+
172
+ If You distribute Covered Software in Executable Form then:
173
+
174
+ (a) such Covered Software must also be made available in Source Code
175
+ Form, as described in Section 3.1, and You must inform recipients of
176
+ the Executable Form how they can obtain a copy of such Source Code
177
+ Form by reasonable means in a timely manner, at a charge no more
178
+ than the cost of distribution to the recipient; and
179
+
180
+ (b) You may distribute such Executable Form under the terms of this
181
+ License, or sublicense it under different terms, provided that the
182
+ license for the Executable Form does not attempt to limit or alter
183
+ the recipients' rights in the Source Code Form under this License.
184
+
185
+ 3.3. Distribution of a Larger Work
186
+
187
+ You may create and distribute a Larger Work under terms of Your choice,
188
+ provided that You also comply with the requirements of this License for
189
+ the Covered Software. If the Larger Work is a combination of Covered
190
+ Software with a work governed by one or more Secondary Licenses, and the
191
+ Covered Software is not Incompatible With Secondary Licenses, this
192
+ License permits You to additionally distribute such Covered Software
193
+ under the terms of such Secondary License(s), so that the recipient of
194
+ the Larger Work may, at their option, further distribute the Covered
195
+ Software under the terms of either this License or such Secondary
196
+ License(s).
197
+
198
+ 3.4. Notices
199
+
200
+ You may not remove or alter the substance of any license notices
201
+ (including copyright notices, patent notices, disclaimers of warranty,
202
+ or limitations of liability) contained within the Source Code Form of
203
+ the Covered Software, except that You may alter any license notices to
204
+ the extent required to remedy known factual inaccuracies.
205
+
206
+ 3.5. Application of Additional Terms
207
+
208
+ You may choose to offer, and to charge a fee for, warranty, support,
209
+ indemnity or liability obligations to one or more recipients of Covered
210
+ Software. However, You may do so only on Your own behalf, and not on
211
+ behalf of any Contributor. You must make it absolutely clear that any
212
+ such warranty, support, indemnity, or liability obligation is offered by
213
+ You alone, and You hereby agree to indemnify every Contributor for any
214
+ liability incurred by such Contributor as a result of warranty, support,
215
+ indemnity or liability terms You offer. You may include additional
216
+ disclaimers of warranty and limitations of liability specific to any
217
+ jurisdiction.
218
+
219
+ 4. Inability to Comply Due to Statute or Regulation
220
+ ---------------------------------------------------
221
+
222
+ If it is impossible for You to comply with any of the terms of this
223
+ License with respect to some or all of the Covered Software due to
224
+ statute, judicial order, or regulation then You must: (a) comply with
225
+ the terms of this License to the maximum extent possible; and (b)
226
+ describe the limitations and the code they affect. Such description must
227
+ be placed in a text file included with all distributions of the Covered
228
+ Software under this License. Except to the extent prohibited by statute
229
+ or regulation, such description must be sufficiently detailed for a
230
+ recipient of ordinary skill to be able to understand it.
231
+
232
+ 5. Termination
233
+ --------------
234
+
235
+ 5.1. The rights granted under this License will terminate automatically
236
+ if You fail to comply with any of its terms. However, if You become
237
+ compliant, then the rights granted under this License from a particular
238
+ Contributor are reinstated (a) provisionally, unless and until such
239
+ Contributor explicitly and finally terminates Your grants, and (b) on an
240
+ ongoing basis, if such Contributor fails to notify You of the
241
+ non-compliance by some reasonable means prior to 60 days after You have
242
+ come back into compliance. Moreover, Your grants from a particular
243
+ Contributor are reinstated on an ongoing basis if such Contributor
244
+ notifies You of the non-compliance by some reasonable means, this is the
245
+ first time You have received notice of non-compliance with this License
246
+ from such Contributor, and You become compliant prior to 30 days after
247
+ Your receipt of the notice.
248
+
249
+ 5.2. If You initiate litigation against any entity by asserting a patent
250
+ infringement claim (excluding declaratory judgment actions,
251
+ counter-claims, and cross-claims) alleging that a Contributor Version
252
+ directly or indirectly infringes any patent, then the rights granted to
253
+ You by any and all Contributors for the Covered Software under Section
254
+ 2.1 of this License shall terminate.
255
+
256
+ 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257
+ end user license agreements (excluding distributors and resellers) which
258
+ have been validly granted by You or Your distributors under this License
259
+ prior to termination shall survive termination.
260
+
261
+ ************************************************************************
262
+ * *
263
+ * 6. Disclaimer of Warranty *
264
+ * ------------------------- *
265
+ * *
266
+ * Covered Software is provided under this License on an "as is" *
267
+ * basis, without warranty of any kind, either expressed, implied, or *
268
+ * statutory, including, without limitation, warranties that the *
269
+ * Covered Software is free of defects, merchantable, fit for a *
270
+ * particular purpose or non-infringing. The entire risk as to the *
271
+ * quality and performance of the Covered Software is with You. *
272
+ * Should any Covered Software prove defective in any respect, You *
273
+ * (not any Contributor) assume the cost of any necessary servicing, *
274
+ * repair, or correction. This disclaimer of warranty constitutes an *
275
+ * essential part of this License. No use of any Covered Software is *
276
+ * authorized under this License except under this disclaimer. *
277
+ * *
278
+ ************************************************************************
279
+
280
+ ************************************************************************
281
+ * *
282
+ * 7. Limitation of Liability *
283
+ * -------------------------- *
284
+ * *
285
+ * Under no circumstances and under no legal theory, whether tort *
286
+ * (including negligence), contract, or otherwise, shall any *
287
+ * Contributor, or anyone who distributes Covered Software as *
288
+ * permitted above, be liable to You for any direct, indirect, *
289
+ * special, incidental, or consequential damages of any character *
290
+ * including, without limitation, damages for lost profits, loss of *
291
+ * goodwill, work stoppage, computer failure or malfunction, or any *
292
+ * and all other commercial damages or losses, even if such party *
293
+ * shall have been informed of the possibility of such damages. This *
294
+ * limitation of liability shall not apply to liability for death or *
295
+ * personal injury resulting from such party's negligence to the *
296
+ * extent applicable law prohibits such limitation. Some *
297
+ * jurisdictions do not allow the exclusion or limitation of *
298
+ * incidental or consequential damages, so this exclusion and *
299
+ * limitation may not apply to You. *
300
+ * *
301
+ ************************************************************************
302
+
303
+ 8. Litigation
304
+ -------------
305
+
306
+ Any litigation relating to this License may be brought only in the
307
+ courts of a jurisdiction where the defendant maintains its principal
308
+ place of business and such litigation shall be governed by laws of that
309
+ jurisdiction, without reference to its conflict-of-law provisions.
310
+ Nothing in this Section shall prevent a party's ability to bring
311
+ cross-claims or counter-claims.
312
+
313
+ 9. Miscellaneous
314
+ ----------------
315
+
316
+ This License represents the complete agreement concerning the subject
317
+ matter hereof. If any provision of this License is held to be
318
+ unenforceable, such provision shall be reformed only to the extent
319
+ necessary to make it enforceable. Any law or regulation which provides
320
+ that the language of a contract shall be construed against the drafter
321
+ shall not be used to construe this License against a Contributor.
322
+
323
+ 10. Versions of the License
324
+ ---------------------------
325
+
326
+ 10.1. New Versions
327
+
328
+ Mozilla Foundation is the license steward. Except as provided in Section
329
+ 10.3, no one other than the license steward has the right to modify or
330
+ publish new versions of this License. Each version will be given a
331
+ distinguishing version number.
332
+
333
+ 10.2. Effect of New Versions
334
+
335
+ You may distribute the Covered Software under the terms of the version
336
+ of the License under which You originally received the Covered Software,
337
+ or under the terms of any subsequent version published by the license
338
+ steward.
339
+
340
+ 10.3. Modified Versions
341
+
342
+ If you create software not governed by this License, and you want to
343
+ create a new license for such software, you may create and use a
344
+ modified version of this License if you rename the license and remove
345
+ any references to the name of the license steward (except to note that
346
+ such modified license differs from this License).
347
+
348
+ 10.4. Distributing Source Code Form that is Incompatible With Secondary
349
+ Licenses
350
+
351
+ If You choose to distribute Source Code Form that is Incompatible With
352
+ Secondary Licenses under the terms of this version of the License, the
353
+ notice described in Exhibit B of this License must be attached.
354
+
355
+ Exhibit A - Source Code Form License Notice
356
+ -------------------------------------------
357
+
358
+ This Source Code Form is subject to the terms of the Mozilla Public
359
+ License, v. 2.0. If a copy of the MPL was not distributed with this
360
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
361
+
362
+ If it is not possible or desirable to put the notice in a particular
363
+ file, then You may include the notice in a location (such as a LICENSE
364
+ file in a relevant directory) where a recipient would be likely to look
365
+ for such a notice.
366
+
367
+ You may add additional accurate notices of copyright ownership.
368
+
369
+ Exhibit B - "Incompatible With Secondary Licenses" Notice
370
+ ---------------------------------------------------------
371
+
372
+ This Source Code Form is "Incompatible With Secondary Licenses", as
373
+ defined by the Mozilla Public License, v. 2.0.
package/README.md CHANGED
@@ -1,3 +1,262 @@
1
- # Temporary Holding Version
1
+ # contour-deck
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <p align="center">
4
+ <a href="https://wayne930242.github.io/contour-deck/">
5
+ <picture>
6
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/tour-light.gif">
7
+ <img src="https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/tour.gif" alt="Open a slide from the outline, move left and right, scroll down its pages, and return to the outline" width="800">
8
+ </picture>
9
+ </a>
10
+ </p>
11
+
12
+ Outline-first slide decks on real DOM.
13
+ A deck is blocks, slides, and pages: it opens on an outline of every block, a block opens into a left-right track of slides, and each slide scrolls down one page at a time.
14
+ The core is plain TypeScript with no runtime dependency (7 KB of JavaScript and 3 KB of CSS, gzipped); the phone remote is a separate module that loads on first use.
15
+
16
+ Status: 0.1.0 on npm (M1). The M2 client is on `main`, not yet released: phone remote, laser pointer, drawing, tilt pointing, and a presenter window ([Remote](#remote)); the relay it talks to is a separate small service ([docs/PROTOCOL.md](docs/PROTOCOL.md)). Live demo: https://wayne930242.github.io/contour-deck/
17
+ The design is in [docs/DESIGN.md](docs/DESIGN.md) (Traditional Chinese); the HTML the core reads is in [docs/CONTRACT.md](docs/CONTRACT.md).
18
+
19
+ ## 看看它怎麼動
20
+
21
+ [線上示範](https://wayne930242.github.io/contour-deck/)可以直接操作;[影片與截圖頁](https://wayne930242.github.io/contour-deck/media.html)有每一段的 mp4 與原尺寸截圖。
22
+ 下面的動畫是真實的鍵盤與滑鼠操作(圓圈是滑鼠,黑底小標是按下的鍵),用 `pnpm media` 重錄。
23
+
24
+ | | |
25
+ | --- | --- |
26
+ | ![跨區塊移動:翻到區塊盡頭時拉遠再推進,位置列的圓點直接飛到另一個區塊](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/move.gif) | ![3D 結構:輪廓傾斜、頁疊成層,仍可點進投影片](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/structure.gif) |
27
+ | 跨區塊移動:拉遠再推進,位置列的圓點直接飛到別的區塊 | 3D 結構:按 3,頁疊成層 |
28
+ | ![步驟:表格一列一列出現,步驟用完才換頁](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/build.gif) | ![手機:講稿檢視、輪廓、舞台與往上滑的換頁](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/phone.gif) |
29
+ | 步驟:空白鍵讓表格一列一列出現 | 手機:講稿檢視與往上滑換頁 |
30
+
31
+ | | |
32
+ | --- | --- |
33
+ | ![輪廓:七個區塊的縮圖牆](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/overview.png) | ![3D 結構](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/overview-3d.png) |
34
+ | 輪廓 | 3D 結構 |
35
+ | ![舞台旁的講稿面板](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/notes.png) | ![淺色主題的舞台](https://raw.githubusercontent.com/wayne930242/contour-deck/main/docs/media/light.png) |
36
+ | 講稿面板(按 N) | 淺色主題 |
37
+
38
+ ## Use
39
+
40
+ ```ts
41
+ import { createDeck } from "contour-deck";
42
+ import "contour-deck/style.css";
43
+
44
+ const root = document.querySelector<HTMLElement>("[data-ct-deck]")!;
45
+ if (new URLSearchParams(location.search).has("view")) {
46
+ const deck = createDeck(root);
47
+ deck.on("exit", () => deck.destroy());
48
+ }
49
+ ```
50
+
51
+ Without the script the deck element is a plain document.
52
+ `createDeck` moves that same element onto a stage inside a full-window shell (`.ct-app`), and `destroy()` puts it back.
53
+ [demo/index.html](demo/index.html) is a complete host page.
54
+
55
+ ### Astro
56
+
57
+ ```ts
58
+ // astro.config.ts: the Markdown side, see docs/PLUGINS.md
59
+ import { remarkDeck } from "contour-deck/remark";
60
+ import { rehypeDeck } from "contour-deck/rehype";
61
+
62
+ const isDeck = file => file.data.astro?.frontmatter?.type === "slide";
63
+ // remarkPlugins: [remarkDirective, [remarkDeck, { match: isDeck }], …]
64
+ // rehypePlugins: […, [rehypeDeck, { match: isDeck }]]
65
+ ```
66
+
67
+ ```astro
68
+ ---
69
+ import Deck from "contour-deck/astro";
70
+ const { Content } = await render(post);
71
+ ---
72
+
73
+ <button data-ct-start>START</button>
74
+ <a href="?view=notes" data-ct-start="notes">Notes</a>
75
+
76
+ <Deck docClass="prose" legacy>
77
+ <Content />
78
+ </Deck>
79
+ ```
80
+
81
+ `Deck` renders a wrapper around the slot, loads the stylesheet, and starts the core: on `?view=deck` or `?view=notes`, and on a click on any `[data-ct-start]` control (`data-ct-start="notes"` opens the notes view).
82
+ It closes the deck on `exit`, gives the focus back to the control that opened it, and works under Astro's client router.
83
+
84
+ | Prop | Meaning |
85
+ | --- | --- |
86
+ | `class` | Classes of the wrapper in both views. |
87
+ | `docClass` | Classes the wrapper carries only while the deck is closed. Use it for a prose class: the open deck stays inside the wrapper, and such a class would restyle the stage and the shell. |
88
+ | `paging`, `overview` | The options of the same name below. |
89
+ | `legacy` | Accept old reveal.js links: `?view=slides&slide=h.v` opens the slide that carries that `data-legacy` index. |
90
+
91
+ A page that is not Astro gets the same behaviour from `mountAll()` in `contour-deck/host`, with the wrapper written by hand (`<div data-ct-host data-ct-doc-class="prose" data-ct-legacy>`).
92
+
93
+ ### Options
94
+
95
+ | Option | Default | Meaning |
96
+ | --- | --- | --- |
97
+ | `paging` | `"snap"` | `"snap"`: one wheel or trackpad gesture moves one stop. `"free"`: native scrolling with proximity snap. |
98
+ | `overview` | `"flat"` | `"3d"` opens the outline as the tilted 3D structure. |
99
+ | `linear.skip` | `["appendix"]` | Block kinds the linear advance and the arrow keys never enter. |
100
+ | `view` | from the URL | `"deck"` or `"notes"` (the speaker notes as a scrolling script, for a phone). |
101
+ | `at` | from the URL | Entry position, such as `"4.4.2"` or a slide id. |
102
+ | `url` | `true` | Read and write `?view=` and `?at=`. |
103
+ | `labels` | Traditional Chinese | Interface text; pass the entries to replace. |
104
+ | `remote` | none | A function the remote button and the `R` key call with the deck; it usually loads `contour-deck/remote`. Without it the deck has neither. See [Remote](#remote). |
105
+
106
+ ### API
107
+
108
+ ```ts
109
+ deck.next(); // next step, down within the page, next page, next slide, next block
110
+ deck.prev();
111
+ deck.open("4.2"); // put a block on the stage
112
+ deck.go("4.4.2"); // or deck.go("slide-id")
113
+ deck.overview(); // back to the outline
114
+ deck.slideBy(1); // the next slide, as the right arrow key; -1 for the one before
115
+ deck.pageBy(1); // one stop down inside the slide, as the down arrow key
116
+ deck.state; // { mode, block, slide, page, step }; ordinals are 1-based
117
+ deck.on("change", ({ mode, at, step }) => {});
118
+ deck.on("exit", () => deck.destroy()); // Esc on the outline, or the ✕ button of the bar
119
+ deck.on("destroy", () => {}); // once, at the start of destroy()
120
+ deck.destroy();
121
+ ```
122
+
123
+ On opening, the focus moves into the shell: to the stage, to the current thumbnail of the outline, or to the notes script.
124
+
125
+ Each page receives `ct:enter` when it becomes the current page and `ct:leave` when it stops; both bubble.
126
+
127
+ ### Remote
128
+
129
+ A phone controls the deck, points at it, and draws on it; a second window on the same computer shows the presenter's view.
130
+ The code is `contour-deck/remote`, loaded on first use, so a reader who never presses `R` downloads none of it.
131
+
132
+ ```ts
133
+ import { createDeck } from "contour-deck";
134
+
135
+ const options = { relay: "wss://relay.example" }; // see "The relay" below
136
+ const load = () => import("contour-deck/remote");
137
+ const view = new URLSearchParams(location.search).get("view");
138
+
139
+ if (view === "remote") load().then(remote => remote.remoteView(root, options)); // the phone
140
+ else if (view === "presenter") load().then(remote => remote.presenterView(root, options)); // the presenter window
141
+ else if (view) {
142
+ const deck = createDeck(root, { remote: deck => load().then(remote => remote.remoteHost(deck, root, options)).then(host => host.toggle()) });
143
+ deck.on("exit", () => deck.destroy());
144
+ }
145
+ ```
146
+
147
+ With Astro, `<Deck relay="wss://relay.example">` does all of this (`<Deck remote>` without a relay); so does `data-ct-relay` or `data-ct-remote` on a `[data-ct-host]` wrapper.
148
+
149
+ How to connect:
150
+
151
+ 1. On the deck, press `R` (or the 遙控 button). The panel shows two QR codes.
152
+ 2. Scan 手機遙控 with the phone. It opens the same page as `?view=remote&room=…` and shows a 4-digit code.
153
+ 3. The deck shows the same code under 有手機要求控制. Compare the two screens and press 允許.
154
+ 4. The phone now has: position and clocks (whole talk and current block, from `data-minutes`), the notes, what the next step shows, 下一步 / 上一步 / 上一張 / 下一張 / 輪廓, and three pointing tools: 紅點 (drag on a small copy of the page), 畫筆 (draw on it; a page change clears the ink), and 傾斜 (hold a button and aim the phone like a laser pointer).
155
+ 5. 講稿, the other QR, is `?view=notes`: the script on the phone, turned by hand, with no connection at all.
156
+
157
+ 開啟講者視窗 in the panel opens `?view=presenter` in a second window of the same browser: the current page, the next step, the notes, and the clocks. It follows over `BroadcastChannel`, needs no relay, and takes the deck's keys; the mouse over its current page is the laser pointer.
158
+
159
+ Tilt pointing reads the phone's orientation sensor (iOS asks for permission on the tap that enables it).
160
+ 校正 guides through five aims: the screen's centre, then its four corners, each marked by the dot on the deck. From these the phone knows where the screen is from where you stand, and keeps it for the session.
161
+ Without calibration, the direction of the first press is the centre of a screen ±20° wide and ±12° high.
162
+ When the pointer drifts, aim at the centre and press 重新置中; aiming past an edge also drags the calibration along, as a mouse at the edge of a screen.
163
+
164
+ Security, in three lines: the relay only forwards small messages (position, commands, pointer coordinates) between the deck and at most four phones; notes, block titles, and slide content never pass through it, because the phone loads the page itself.
165
+ The room in the QR lets a phone ask; only the deck's tab holds the key the room is hashed from, so nobody else can take the deck's seat at the relay and hear what the phones send.
166
+ Control belongs to the one phone the presenter allowed on the deck after comparing the code; everything any other guest sends is ignored, and the relay removes a denied guest, so a QR photographed from the projector yields only a request to deny.
167
+
168
+ | Option of `remoteHost`, `remoteView`, `presenterView` | Default | Meaning |
169
+ | --- | --- | --- |
170
+ | `relay` | none | Base URL of the relay, `wss://…` or `https://…`. Without it the remote stays on this computer: presenter window and notes link only. |
171
+ | `qr` | built-in | `(text) => svgString`, for a site that already ships a QR library: `text => QRCode.toString(text, { type: "svg" })`. The built-in encoder takes links up to 287 bytes; a longer link shows as text. |
172
+ | `skip` | `["appendix"]` | Block kinds whose minutes are not planned time; keep it equal to `linear.skip`. |
173
+ | `labels` | Traditional Chinese | Interface text of the panel, the phone, and the presenter window. |
174
+ | `nonce` | none | Nonce for the `<style>` the module injects, for a page with a `style-src` policy. |
175
+ | `room` (views only) | `?room=` | The room to join. |
176
+
177
+ `remoteHost(deck, root, options)` resolves to the session of that deck, creating it once: `toggle()` shows or hides the panel, `end()` takes control back and closes the room, `room` is its name.
178
+ It is a promise because the room is the SHA-256 of the session's key (`crypto.subtle`, so with a relay the page needs https or localhost).
179
+ The key, the paired phone's token, and the clock live in `sessionStorage`, so a reload of the deck or of the phone continues the same session; the Astro host and the demo restart it by themselves after a reload.
180
+ With the Astro component, pass a QR encoder or labels through `configureRemote({ qr })` from `contour-deck/host`.
181
+ The remote follows the first deck of a page.
182
+
183
+ The clock starts with the first move on the stage (not when a slide is opened from the outline) and is reset from the panel.
184
+ The laser dot eases towards its target and fades when released; with `prefers-reduced-motion` it jumps and does not fade.
185
+
186
+ #### The relay
187
+
188
+ The relay is a WebSocket forwarder, one room per talk, specified in [docs/PROTOCOL.md](docs/PROTOCOL.md): rooms, the pairing messages, close codes, and limits (2 KB frames, 60 messages a second, 4 guests).
189
+ `relay(url, { room, role, key })` and `broadcast(room)` are the two `Transport`s (`send`, `subscribe`, `watch`, `reconnect`, `close`, `status`); the relay one reconnects with backoff, except after `4003` (removed by the host) and `4009` (another tab of the session took the host seat).
190
+ The demo takes a relay from the query string, the deployed one or one on this machine: `https://wayne930242.github.io/contour-deck/?relay=wss://contour-relay.wayne930242.workers.dev&view=deck`, or `?relay=ws://localhost:8787`; it ignores any other.
191
+ The phone must be able to open the page's URL, so a deck on `localhost` needs a tunnel or the LAN address for the phone part.
192
+
193
+ ### Keys
194
+
195
+ | Key | On the stage | On the outline |
196
+ | --- | --- | --- |
197
+ | `Space`, `Enter`, `PageDown` | advance one step | open the current slide |
198
+ | `Shift+Space`, `PageUp`, `Backspace` | back one step | |
199
+ | `←` `→` | previous, next slide | move between thumbnails |
200
+ | `↑` `↓` | previous, next stop | move between thumbnails |
201
+ | `Home`, `End` | first, last slide of the block | |
202
+ | `N` | notes panel | |
203
+ | `R` | remote panel (with the `remote` option) | remote panel |
204
+ | `Esc`, `O` | outline | `Esc` leaves the deck (`exit`) |
205
+ | `3` | | 3D structure |
206
+
207
+ ### URL
208
+
209
+ `?view=deck&at=4.4.2` is the second page of the fourth slide of the fourth block, counted from 1.
210
+ `at` also takes the id of a slide, a page, or an element inside one.
211
+ `?view=notes` opens the notes view.
212
+ The core removes both parameters in `destroy()` and leaves the others alone.
213
+
214
+ ### Theme and host styles
215
+
216
+ - Set `--ct-bg`, `--ct-fg`, `--ct-accent`, `--ct-accent-fg`, `--ct-border`, `--ct-muted`, `--ct-mono`, and `--ct-measure` on `.ct-app`.
217
+ By default the colours follow the site's `--background`, `--foreground`, `--accent`, `--accent-foreground`, `--border`, and `--muted-foreground`.
218
+ - A page keeps its content in a centred column of `--ct-measure` through its own side padding, so margins and widths the host sets on the content stay as written.
219
+ The type of a page follows the size of the stage and never goes below `1rem`; sizes the host writes in `em` follow it, sizes in `rem` do not.
220
+ - The open deck keeps its ancestors, so content styles that depend on them still apply.
221
+ Document-view styles for the deck's own elements (slide cards, block headings) must stop applying when it opens: scope them to the deck's original parent with a child combinator, as `demo/demo.css` does with `.doc > [data-ct-deck] …`.
222
+ A class that styles every descendant (a prose class) cannot be scoped that way: take it off the ancestor while the deck is open, as `Deck`'s `docClass` does.
223
+ - The shell is `position: fixed`, so no ancestor of the deck element may have a `transform`, `filter`, or `contain` that captures fixed descendants.
224
+
225
+ ## Layout
226
+
227
+ ```text
228
+ src/core/ the core: model, stops, gestures, navigator, outline, rail, notes, transitions, deck
229
+ src/remote/ contour-deck/remote: protocol, transports, pairing, clock, tilt math, QR, and the three views
230
+ src/remark/ Markdown directives (block, vertical, summary, build)
231
+ src/rehype/ builds the deck > block > slide > page HTML
232
+ src/astro/ Deck.astro and the host script it runs (also contour-deck/host)
233
+ styles/ index.css is the entry of contour-deck/style.css
234
+ demo/ a plain-HTML deck that follows the contract, and media.html (clips and stills)
235
+ docs/media/ GIF, mp4, and PNG made by scripts/media.mjs (not in the npm package)
236
+ tests/unit/ Vitest, tests/unit/**/*.test.ts
237
+ tests/browser/ Playwright, tests/browser/**/*.spec.ts, run against demo/; relay-stub.ts is the relay of docs/PROTOCOL.md in the test process
238
+ ```
239
+
240
+ Every module under `src/` is compiled to the same path under `dist/`, so a new subpath export only needs an entry in `package.json`.
241
+ `.astro` files are copied there as they are.
242
+
243
+ ## Commands
244
+
245
+ | Command | What it does |
246
+ | --- | --- |
247
+ | `pnpm install` | Install dependencies. |
248
+ | `pnpm exec playwright install chromium` | Once, for the browser tests. |
249
+ | `pnpm typecheck` | Type-check `src/`, `tests/`, and `scripts/`. |
250
+ | `pnpm build` | Compile to `dist/` and build the demo assets in `demo/assets/`. |
251
+ | `pnpm test` | Unit tests. |
252
+ | `pnpm test:browser` | Build, then run the browser tests against the demo in Chromium. |
253
+ | `CT_BROWSERS=all pnpm test:browser` | The same in Chromium, Firefox, and WebKit (`pnpm exec playwright install firefox webkit` once). |
254
+ | `pnpm size` | Minified gzip size of the core and the CSS against the budgets (12 KB, 6 KB), and of the parts of the remote. |
255
+ | `pnpm demo` | Build and serve the demo on `http://127.0.0.1:41731/demo/`. |
256
+ | `pnpm media` | Record the clips and stills in `docs/media/` (needs `ffmpeg` and Chromium); the Pages workflow publishes the mp4 and png files next to the demo. |
257
+
258
+ The demo also opens straight from the file system: `demo/index.html` after `pnpm build`.
259
+
260
+ ## License
261
+
262
+ [MPL-2.0](LICENSE)