@cardstack/choreo 0.0.0 → 0.1.0-unstable.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 (165) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +55 -6
  4. package/addon-main.cjs +4 -0
  5. package/declarations/anchors.d.ts +26 -0
  6. package/declarations/anchors.d.ts.map +1 -0
  7. package/declarations/arming.d.ts +33 -0
  8. package/declarations/arming.d.ts.map +1 -0
  9. package/declarations/beacon.d.ts +9 -0
  10. package/declarations/beacon.d.ts.map +1 -0
  11. package/declarations/beacons.d.ts +23 -0
  12. package/declarations/beacons.d.ts.map +1 -0
  13. package/declarations/changeset.d.ts +46 -0
  14. package/declarations/changeset.d.ts.map +1 -0
  15. package/declarations/choreo.d.ts +261 -0
  16. package/declarations/choreo.d.ts.map +1 -0
  17. package/declarations/compile.d.ts +45 -0
  18. package/declarations/compile.d.ts.map +1 -0
  19. package/declarations/deliver.d.ts +44 -0
  20. package/declarations/deliver.d.ts.map +1 -0
  21. package/declarations/easings.d.ts +20 -0
  22. package/declarations/easings.d.ts.map +1 -0
  23. package/declarations/far.d.ts +33 -0
  24. package/declarations/far.d.ts.map +1 -0
  25. package/declarations/film/clip.d.ts +51 -0
  26. package/declarations/film/clip.d.ts.map +1 -0
  27. package/declarations/film/clips.d.ts +155 -0
  28. package/declarations/film/clips.d.ts.map +1 -0
  29. package/declarations/film/film.d.ts +1208 -0
  30. package/declarations/film/film.d.ts.map +1 -0
  31. package/declarations/film/graph/adjust.d.ts +112 -0
  32. package/declarations/film/graph/adjust.d.ts.map +1 -0
  33. package/declarations/film/graph/compile.d.ts +140 -0
  34. package/declarations/film/graph/compile.d.ts.map +1 -0
  35. package/declarations/film/graph/host.d.ts +67 -0
  36. package/declarations/film/graph/host.d.ts.map +1 -0
  37. package/declarations/film/graph/nodes.d.ts +287 -0
  38. package/declarations/film/graph/nodes.d.ts.map +1 -0
  39. package/declarations/film/index.d.ts +24 -0
  40. package/declarations/film/index.d.ts.map +1 -0
  41. package/declarations/film/joins.d.ts +143 -0
  42. package/declarations/film/joins.d.ts.map +1 -0
  43. package/declarations/film/math.d.ts +15 -0
  44. package/declarations/film/math.d.ts.map +1 -0
  45. package/declarations/film/overlays.d.ts +54 -0
  46. package/declarations/film/overlays.d.ts.map +1 -0
  47. package/declarations/film/picture.d.ts +104 -0
  48. package/declarations/film/picture.d.ts.map +1 -0
  49. package/declarations/film/plate.d.ts +37 -0
  50. package/declarations/film/plate.d.ts.map +1 -0
  51. package/declarations/film/player.d.ts +120 -0
  52. package/declarations/film/player.d.ts.map +1 -0
  53. package/declarations/film/rail.d.ts +40 -0
  54. package/declarations/film/rail.d.ts.map +1 -0
  55. package/declarations/film/schedule.d.ts +93 -0
  56. package/declarations/film/schedule.d.ts.map +1 -0
  57. package/declarations/film/seam.d.ts +38 -0
  58. package/declarations/film/seam.d.ts.map +1 -0
  59. package/declarations/film/titles.d.ts +37 -0
  60. package/declarations/film/titles.d.ts.map +1 -0
  61. package/declarations/film/types.d.ts +544 -0
  62. package/declarations/film/types.d.ts.map +1 -0
  63. package/declarations/film.d.ts +3 -0
  64. package/declarations/film.d.ts.map +1 -0
  65. package/declarations/gesture.d.ts +33 -0
  66. package/declarations/gesture.d.ts.map +1 -0
  67. package/declarations/index.d.ts +26 -0
  68. package/declarations/index.d.ts.map +1 -0
  69. package/declarations/measure.d.ts +29 -0
  70. package/declarations/measure.d.ts.map +1 -0
  71. package/declarations/path.d.ts +73 -0
  72. package/declarations/path.d.ts.map +1 -0
  73. package/declarations/registry.d.ts +46 -0
  74. package/declarations/registry.d.ts.map +1 -0
  75. package/declarations/run.d.ts +406 -0
  76. package/declarations/run.d.ts.map +1 -0
  77. package/declarations/space.d.ts +31 -0
  78. package/declarations/space.d.ts.map +1 -0
  79. package/declarations/steps.d.ts +479 -0
  80. package/declarations/steps.d.ts.map +1 -0
  81. package/declarations/test-support/index.d.ts +30 -0
  82. package/declarations/test-support/index.d.ts.map +1 -0
  83. package/declarations/types.d.ts +761 -0
  84. package/declarations/types.d.ts.map +1 -0
  85. package/dist/anchors.js +34 -0
  86. package/dist/anchors.js.map +1 -0
  87. package/dist/arming.js +120 -0
  88. package/dist/arming.js.map +1 -0
  89. package/dist/beacon.js +25 -0
  90. package/dist/beacon.js.map +1 -0
  91. package/dist/beacons.js +77 -0
  92. package/dist/beacons.js.map +1 -0
  93. package/dist/changeset.js +129 -0
  94. package/dist/changeset.js.map +1 -0
  95. package/dist/choreo.js +1026 -0
  96. package/dist/choreo.js.map +1 -0
  97. package/dist/compile.js +1403 -0
  98. package/dist/compile.js.map +1 -0
  99. package/dist/deliver.js +326 -0
  100. package/dist/deliver.js.map +1 -0
  101. package/dist/easings.js +39 -0
  102. package/dist/easings.js.map +1 -0
  103. package/dist/far.js +147 -0
  104. package/dist/far.js.map +1 -0
  105. package/dist/film/clip.js +100 -0
  106. package/dist/film/clip.js.map +1 -0
  107. package/dist/film/clips.js +147 -0
  108. package/dist/film/clips.js.map +1 -0
  109. package/dist/film/film.js +4424 -0
  110. package/dist/film/film.js.map +1 -0
  111. package/dist/film/graph/adjust.js +160 -0
  112. package/dist/film/graph/adjust.js.map +1 -0
  113. package/dist/film/graph/compile.js +225 -0
  114. package/dist/film/graph/compile.js.map +1 -0
  115. package/dist/film/graph/host.js +77 -0
  116. package/dist/film/graph/host.js.map +1 -0
  117. package/dist/film/graph/nodes.js +500 -0
  118. package/dist/film/graph/nodes.js.map +1 -0
  119. package/dist/film/index.js +18 -0
  120. package/dist/film/index.js.map +1 -0
  121. package/dist/film/joins.js +260 -0
  122. package/dist/film/joins.js.map +1 -0
  123. package/dist/film/math.js +49 -0
  124. package/dist/film/math.js.map +1 -0
  125. package/dist/film/overlays.js +66 -0
  126. package/dist/film/overlays.js.map +1 -0
  127. package/dist/film/picture.js +58 -0
  128. package/dist/film/picture.js.map +1 -0
  129. package/dist/film/plate.js +36 -0
  130. package/dist/film/plate.js.map +1 -0
  131. package/dist/film/player.js +79 -0
  132. package/dist/film/player.js.map +1 -0
  133. package/dist/film/rail.js +38 -0
  134. package/dist/film/rail.js.map +1 -0
  135. package/dist/film/schedule.js +238 -0
  136. package/dist/film/schedule.js.map +1 -0
  137. package/dist/film/seam.js +64 -0
  138. package/dist/film/seam.js.map +1 -0
  139. package/dist/film/titles.js +45 -0
  140. package/dist/film/titles.js.map +1 -0
  141. package/dist/film/types.js +2 -0
  142. package/dist/film/types.js.map +1 -0
  143. package/dist/film.js +18 -0
  144. package/dist/film.js.map +1 -0
  145. package/dist/gesture.js +111 -0
  146. package/dist/gesture.js.map +1 -0
  147. package/dist/index.js +9 -0
  148. package/dist/index.js.map +1 -0
  149. package/dist/measure.js +99 -0
  150. package/dist/measure.js.map +1 -0
  151. package/dist/path.js +272 -0
  152. package/dist/path.js.map +1 -0
  153. package/dist/registry.js +55 -0
  154. package/dist/registry.js.map +1 -0
  155. package/dist/run.js +2423 -0
  156. package/dist/run.js.map +1 -0
  157. package/dist/space.js +57 -0
  158. package/dist/space.js.map +1 -0
  159. package/dist/steps.js +831 -0
  160. package/dist/steps.js.map +1 -0
  161. package/dist/test-support/index.js +150 -0
  162. package/dist/test-support/index.js.map +1 -0
  163. package/dist/types.js +2 -0
  164. package/dist/types.js.map +1 -0
  165. package/package.json +201 -6
@@ -0,0 +1,79 @@
1
+ import { fn } from '@ember/helper';
2
+ import { on } from '@ember/modifier';
3
+ import Component from '@glimmer/component';
4
+ import { motion } from 'glimmer-motion';
5
+ import { Choreo } from '../choreo.js';
6
+ import { precompileTemplate } from '@ember/template-compilation';
7
+ import { setComponentTemplate } from '@ember/component';
8
+
9
+ /** what the hand is pointing at on the bar: the chapter and the time */
10
+ /**
11
+ * THE PLAYER. The grammar every viewer knows from a decade of video on the
12
+ * web: one full-width bar with the chapters as segments, a knob that sits
13
+ * exactly where the fill ends, the transport on the left, the modes on
14
+ * the right, the time beside the chapter's name, and the whole apparatus
15
+ * fading off the picture when nobody is touching it. The bar listens: a
16
+ * resting pointer gets the chapter and the time in a bubble and a ghost
17
+ * fill to where it would cut, and every button names itself and its key.
18
+ * The playhead is honest: a film cut on a chased lens cannot seek, so the
19
+ * drag reads as time and the release RE-CUTS from the shot under the hand.
20
+ */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
21
+ class Player extends Component {
22
+ static {
23
+ setComponentTemplate(precompileTemplate("<div class=\"cf-player {{if @idle \"is-idle\"}} {{if @away \"is-away\"}}\">\n <div class=\"cf-scrub {{if @scrubAt \"is-live\"}}\" role=\"slider\" aria-label=\"playhead\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"0\" tabindex=\"0\" {{@slider}} {{on \"pointerdown\" @scrubDown}} {{on \"pointermove\" @scrubMove}} {{on \"pointerup\" @scrubUp}} {{on \"pointerleave\" @scrubLeave}}>\n <span class=\"cf-scrub-track\" {{@trackWrap}}>\n {{#each @playbar as |c|}}\n <span class=\"cf-scrub-ch\" style={{c.style}}>\n <span class=\"cf-scrub-ghost\"></span>\n <span class=\"cf-scrub-fill\"></span>\n </span>\n {{/each}}\n <span class=\"cf-scrub-head\"></span>\n {{#if @tip}}\n <span class=\"cf-scrub-tip\">\n <span class=\"cf-scrub-tip-ch\">{{@tip.ch}}</span>\n <span class=\"cf-scrub-tip-t\">{{@tip.t}}</span>\n </span>\n {{/if}}\n </span>\n </div>\n\n <div class=\"cf-controls\">\n <button type=\"button\" class=\"cf-ic\" aria-label={{if @live \"pause\" \"play\"}} data-tip={{if @live \"Pause (k)\" \"Play (k)\"}} {{on \"click\" @toggle}}>\n {{#if @live}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M7 5h4v14H7zM13 5h4v14h-4z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\" /></svg>\n {{/if}}\n </button>\n <button type=\"button\" class=\"cf-ic\" aria-label=\"previous chapter\" data-tip=\"Previous chapter (\u2190)\" {{on \"click\" @prev}}>\n <svg viewBox=\"0 0 24 24\"><path d=\"M6 6h2v12H6zM18 6l-9 6 9 6z\" /></svg>\n </button>\n <button type=\"button\" class=\"cf-ic\" aria-label=\"next chapter\" data-tip=\"Next chapter (\u2192)\" {{on \"click\" @next}}>\n <svg viewBox=\"0 0 24 24\"><path d=\"M16 6h2v12h-2zM6 6l9 6-9 6z\" /></svg>\n </button>\n {{!-- the speaker and, on a hover, the fader beside it --}}\n <span class=\"cf-vol\">\n <button type=\"button\" class=\"cf-ic\" aria-label={{if @sound \"mute\" \"sound\"}} data-tip={{if @sound \"Mute (m)\" \"Unmute (m)\"}} {{on \"click\" @hear}}>\n {{#if (eq @loud \"high\")}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2zm-2-7.6v2.1a5.6 5.6 0 0 1 0 11v2.1a7.7 7.7 0 0 0 0-15.2z\" /></svg>\n {{else if (eq @loud \"low\")}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.6 3 2.7-2.7-1.4-1.4-2.7 2.7-2.7-2.7-1.4 1.4 2.7 2.7-2.7 2.7 1.4 1.4 2.7-2.7 2.7 2.7 1.4-1.4z\" /></svg>\n {{/if}}\n </button>\n <input class=\"cf-vol-range\" type=\"range\" min=\"0\" max=\"1\" step=\"0.02\" value={{@vol}} aria-label=\"volume\" style={{@volStyle}} {{on \"input\" @fade}} />\n </span>\n <span class=\"cf-time\">\n <b>{{@clock}}</b>\n <i>/</i>\n {{@duration}}\n </span>\n {{!-- the chapter's name, beside the clock, is the way into the list --}}\n <button type=\"button\" class=\"cf-chap\" data-tip=\"Chapters (c)\" {{on \"click\" @toc}}>\n <span>\xB7</span>\n {{@chapter.n}}\n {{@chapter.title}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M8.6 6 7.2 7.4 11.8 12l-4.6 4.6L8.6 18l6-6z\" /></svg>\n </button>\n <span class=\"cf-spacer\"></span>\n {{#if @debug}}\n <span class=\"cf-build\">{{@build}}</span>\n {{/if}}\n <button type=\"button\" class=\"cf-ic cf-ic-cc {{if @subsOn \"is-on\"}}\" aria-label=\"captions\" data-tip=\"Captions (v)\" {{on \"click\" @cc}}>CC</button>\n {{!-- the way back to the door, with its name on it: a title screen\n is a place, not a loop --}}\n <button type=\"button\" class=\"cf-word\" data-tip=\"Back to the title screen (0)\" {{on \"click\" @restart}}>\n {{!-- a title card, not a loop: the glyph is the card itself --}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M3 5h18v14H3zm2 2v10h14V7zm3 3h8v1.6H8zm0 3.4h5V15H8z\" /></svg>\n Title screen\n </button>\n <button type=\"button\" class=\"cf-ic cf-ic-last\" aria-label={{if @full \"exit fullscreen\" \"fullscreen\"}} data-tip={{if @full \"Exit full screen (f)\" \"Full screen (f)\"}} {{on \"click\" @screen}}>\n {{#if @full}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M9 4H7v3H4v2h5zM4 15h3v3h2v-5H4zm11 3h2v-3h3v-2h-5zm0-14v5h5V7h-3V4z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M4 4h5v2H6v3H4zm11 0h5v5h-2V6h-3zM4 15h2v3h3v2H4zm14 0h2v5h-5v-2h3z\" /></svg>\n {{/if}}\n </button>\n </div>\n</div>", {
24
+ strictMode: true,
25
+ scope: () => ({
26
+ on,
27
+ eq
28
+ })
29
+ }), this);
30
+ }
31
+ }
32
+ /**
33
+ * THE CONFIRMATION GLYPH: a play or a pause, once, in the middle of the
34
+ * picture, gone in half a second — the way every player answers a tap
35
+ * on the glass.
36
+ */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
37
+ class Burst extends Component {
38
+ static {
39
+ setComponentTemplate(precompileTemplate("<div class=\"cf-burst\" {{on \"animationend\" @done}}>\n {{#if (eq @kind \"play\")}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\" /></svg>\n {{else}}\n <svg viewBox=\"0 0 24 24\"><path d=\"M7 5h4v14H7zM13 5h4v14h-4z\" /></svg>\n {{/if}}\n</div>", {
40
+ strictMode: true,
41
+ scope: () => ({
42
+ on,
43
+ eq
44
+ })
45
+ }), this);
46
+ }
47
+ }
48
+ /** a chapter, as the menu lists it */
49
+
50
+ /**
51
+ * THE DISC MENU. A film with chapters owes the viewer a way into them,
52
+ * and the arrow keys alone are a secret. Picking one re-cuts the score
53
+ * from that chapter's head — the same move the arrows make, because a
54
+ * skip here is an edit and never a seek. The film keeps running behind
55
+ * it, blurred: a menu that freezes the picture makes it feel like a file.
56
+ */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
57
+ class Menu extends Component {
58
+ static {
59
+ setComponentTemplate(precompileTemplate("<Choreo class=\"cf-menu\" as |m|>\n <div class=\"cf-menu-in\" {{motion id=\"menu\" role=\"sheet\"}}>\n <p class=\"cf-menu-head\">{{@title}}</p>\n <p class=\"cf-menu-sub\">{{@sub}}</p>\n <ol class=\"cf-menu-list\">\n {{#each @contents as |c|}}\n <li>\n <button type=\"button\" class=\"cf-menu-item {{if c.here \"is-here\"}}\" {{on \"click\" (fn @pick c.head)}}>\n <span class=\"cf-menu-n\">{{c.n}}</span>\n <span class=\"cf-menu-t\">{{c.title}}</span>\n <span class=\"cf-menu-d\">{{c.shots}} shots</span>\n </button>\n </li>\n {{/each}}\n </ol>\n <p class=\"cf-menu-keys\">{{if @keys @keys \"space / K play \xB7 \u2190 \u2192 chapter \xB7 1\u20139 chapter \xB7 M sound \xB7 F full screen \xB7 V captions \xB7 0 title screen \xB7 C close\"}}</p>\n </div>\n <m.Tween @of={{m.inserted \"sheet\"}} @y={{array 26 0}} @scale={{array 0.97 1}} @opacity={{array 0 1}} @duration={{0.42}} @ease={{array 0.22 1 0.36 1}} />\n <m.Tween @of={{m.removed \"sheet\"}} @y={{array 0 18}} @opacity={{array 1 0}} @duration={{0.2}} @ease=\"easeIn\" />\n</Choreo>", {
60
+ strictMode: true,
61
+ scope: () => ({
62
+ Choreo,
63
+ motion,
64
+ on,
65
+ fn,
66
+ array
67
+ })
68
+ }), this);
69
+ }
70
+ }
71
+ function array(...items) {
72
+ return items;
73
+ }
74
+ function eq(a, b) {
75
+ return a === b;
76
+ }
77
+
78
+ export { Burst, Menu, Player };
79
+ //# sourceMappingURL=player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player.js","sources":["../../src/film/player.gts"],"sourcesContent":["import { fn } from '@ember/helper';\nimport { on } from '@ember/modifier';\nimport Component from '@glimmer/component';\nimport { motion } from 'glimmer-motion';\n\nimport { Choreo } from '../choreo.gts';\nimport type { Chapter, ElementModifier } from './types.ts';\n\n/** one chapter as a segment of the scrub bar */\nexport interface PlaybarSegment {\n head: number;\n n: string;\n style: string;\n title: string;\n}\n\n/** what the hand is pointing at on the bar: the chapter and the time */\nexport interface ScrubTip {\n ch: string;\n t: string;\n}\n\nexport interface PlayerSignature {\n Args: {\n /** the front door is up: the transport waits outside */\n away: boolean;\n /** which cut of the film this is; shown only under debug */\n build?: string;\n /** toggle captions */\n cc: () => void;\n chapter: Chapter;\n clock: string;\n debug?: boolean;\n duration: string;\n /** the fader moved */\n fade: (e: Event) => void;\n full: boolean;\n hear: () => void;\n /** nobody has touched the frame for a while: the apparatus leaves */\n idle: boolean;\n /** rolling AND not held by the viewer: what the play button shows */\n live: boolean;\n /** sound on, and the fader up: the speaker glyph's three states */\n loud: 'high' | 'low' | 'off';\n menu: boolean;\n next: () => void;\n playbar: PlaybarSegment[];\n prev: () => void;\n restart: () => void;\n screen: () => void;\n /** the fraction the hand is holding while scrubbing, else null */\n scrubAt: null | number;\n scrubDown: (event: Event) => void;\n scrubLeave: () => void;\n scrubMove: (event: Event) => void;\n scrubUp: (event: Event) => void;\n /** keeps the bar's element, for the slider's live value */\n slider: ElementModifier;\n sound: boolean;\n subsOn: boolean;\n /** the bubble over the bar, while a pointer is on it */\n tip: null | ScrubTip;\n toc: () => void;\n toggle: () => void;\n /** measures the track, so the head can sit at the fill's end in pixels */\n trackWrap: ElementModifier;\n /** the master fader, 0..1 */\n vol: number;\n /** the fader's value as a custom property, for its own fill */\n volStyle: string;\n };\n}\n\n/**\n * THE PLAYER. The grammar every viewer knows from a decade of video on the\n * web: one full-width bar with the chapters as segments, a knob that sits\n * exactly where the fill ends, the transport on the left, the modes on\n * the right, the time beside the chapter's name, and the whole apparatus\n * fading off the picture when nobody is touching it. The bar listens: a\n * resting pointer gets the chapter and the time in a bubble and a ghost\n * fill to where it would cut, and every button names itself and its key.\n * The playhead is honest: a film cut on a chased lens cannot seek, so the\n * drag reads as time and the release RE-CUTS from the shot under the hand.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Player extends Component<PlayerSignature> {\n <template>\n <div class='cf-player {{if @idle \"is-idle\"}} {{if @away \"is-away\"}}'>\n <div\n class='cf-scrub {{if @scrubAt \"is-live\"}}'\n role='slider'\n aria-label='playhead'\n aria-valuemin='0'\n aria-valuemax='100'\n aria-valuenow='0'\n tabindex='0'\n {{@slider}}\n {{on 'pointerdown' @scrubDown}}\n {{on 'pointermove' @scrubMove}}\n {{on 'pointerup' @scrubUp}}\n {{on 'pointerleave' @scrubLeave}}\n >\n <span class='cf-scrub-track' {{@trackWrap}}>\n {{#each @playbar as |c|}}\n <span class='cf-scrub-ch' style={{c.style}}>\n <span class='cf-scrub-ghost'></span>\n <span class='cf-scrub-fill'></span>\n </span>\n {{/each}}\n <span class='cf-scrub-head'></span>\n {{#if @tip}}\n <span class='cf-scrub-tip'>\n <span class='cf-scrub-tip-ch'>{{@tip.ch}}</span>\n <span class='cf-scrub-tip-t'>{{@tip.t}}</span>\n </span>\n {{/if}}\n </span>\n </div>\n\n <div class='cf-controls'>\n <button\n type='button'\n class='cf-ic'\n aria-label={{if @live 'pause' 'play'}}\n data-tip={{if @live 'Pause (k)' 'Play (k)'}}\n {{on 'click' @toggle}}\n >\n {{#if @live}}\n <svg viewBox='0 0 24 24'><path\n d='M7 5h4v14H7zM13 5h4v14h-4z'\n /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path d='M8 5v14l11-7z' /></svg>\n {{/if}}\n </button>\n <button\n type='button'\n class='cf-ic'\n aria-label='previous chapter'\n data-tip='Previous chapter (←)'\n {{on 'click' @prev}}\n >\n <svg viewBox='0 0 24 24'><path\n d='M6 6h2v12H6zM18 6l-9 6 9 6z'\n /></svg>\n </button>\n <button\n type='button'\n class='cf-ic'\n aria-label='next chapter'\n data-tip='Next chapter (→)'\n {{on 'click' @next}}\n >\n <svg viewBox='0 0 24 24'><path\n d='M16 6h2v12h-2zM6 6l9 6-9 6z'\n /></svg>\n </button>\n {{! the speaker and, on a hover, the fader beside it }}\n <span class='cf-vol'>\n <button\n type='button'\n class='cf-ic'\n aria-label={{if @sound 'mute' 'sound'}}\n data-tip={{if @sound 'Mute (m)' 'Unmute (m)'}}\n {{on 'click' @hear}}\n >\n {{#if (eq @loud 'high')}}\n <svg viewBox='0 0 24 24'><path\n d='M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2zm-2-7.6v2.1a5.6 5.6 0 0 1 0 11v2.1a7.7 7.7 0 0 0 0-15.2z'\n /></svg>\n {{else if (eq @loud 'low')}}\n <svg viewBox='0 0 24 24'><path\n d='M4 9v6h4l5 4V5L8 9zm11.5 3a3.5 3.5 0 0 0-2-3.2v6.4a3.5 3.5 0 0 0 2-3.2z'\n /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path\n d='M4 9v6h4l5 4V5L8 9zm12.6 3 2.7-2.7-1.4-1.4-2.7 2.7-2.7-2.7-1.4 1.4 2.7 2.7-2.7 2.7 1.4 1.4 2.7-2.7 2.7 2.7 1.4-1.4z'\n /></svg>\n {{/if}}\n </button>\n <input\n class='cf-vol-range'\n type='range'\n min='0'\n max='1'\n step='0.02'\n value={{@vol}}\n aria-label='volume'\n style={{@volStyle}}\n {{on 'input' @fade}}\n />\n </span>\n <span class='cf-time'>\n <b>{{@clock}}</b>\n <i>/</i>\n {{@duration}}\n </span>\n {{! the chapter's name, beside the clock, is the way into the list }}\n <button\n type='button'\n class='cf-chap'\n data-tip='Chapters (c)'\n {{on 'click' @toc}}\n >\n <span>·</span>\n {{@chapter.n}}\n {{@chapter.title}}\n <svg viewBox='0 0 24 24'><path\n d='M8.6 6 7.2 7.4 11.8 12l-4.6 4.6L8.6 18l6-6z'\n /></svg>\n </button>\n <span class='cf-spacer'></span>\n {{#if @debug}}\n <span class='cf-build'>{{@build}}</span>\n {{/if}}\n <button\n type='button'\n class='cf-ic cf-ic-cc {{if @subsOn \"is-on\"}}'\n aria-label='captions'\n data-tip='Captions (v)'\n {{on 'click' @cc}}\n >CC</button>\n {{! the way back to the door, with its name on it: a title screen\n is a place, not a loop }}\n <button\n type='button'\n class='cf-word'\n data-tip='Back to the title screen (0)'\n {{on 'click' @restart}}\n >\n {{! a title card, not a loop: the glyph is the card itself }}\n <svg viewBox='0 0 24 24'><path\n d='M3 5h18v14H3zm2 2v10h14V7zm3 3h8v1.6H8zm0 3.4h5V15H8z'\n /></svg>\n Title screen\n </button>\n <button\n type='button'\n class='cf-ic cf-ic-last'\n aria-label={{if @full 'exit fullscreen' 'fullscreen'}}\n data-tip={{if @full 'Exit full screen (f)' 'Full screen (f)'}}\n {{on 'click' @screen}}\n >\n {{#if @full}}\n <svg viewBox='0 0 24 24'><path\n d='M9 4H7v3H4v2h5zM4 15h3v3h2v-5H4zm11 3h2v-3h3v-2h-5zm0-14v5h5V7h-3V4z'\n /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path\n d='M4 4h5v2H6v3H4zm11 0h5v5h-2V6h-3zM4 15h2v3h3v2H4zm14 0h2v5h-5v-2h3z'\n /></svg>\n {{/if}}\n </button>\n </div>\n </div>\n </template>\n}\n\nexport interface BurstSignature {\n Args: {\n /** the animation ran out: take the glyph down */\n done: () => void;\n kind: 'pause' | 'play';\n };\n}\n\n/**\n * THE CONFIRMATION GLYPH: a play or a pause, once, in the middle of the\n * picture, gone in half a second — the way every player answers a tap\n * on the glass.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Burst extends Component<BurstSignature> {\n <template>\n <div class='cf-burst' {{on 'animationend' @done}}>\n {{#if (eq @kind 'play')}}\n <svg viewBox='0 0 24 24'><path d='M8 5v14l11-7z' /></svg>\n {{else}}\n <svg viewBox='0 0 24 24'><path d='M7 5h4v14H7zM13 5h4v14h-4z' /></svg>\n {{/if}}\n </div>\n </template>\n}\n\n/** a chapter, as the menu lists it */\nexport interface MenuEntry {\n head: number;\n here: boolean;\n n: string;\n secs: number;\n shots: number;\n title: string;\n}\n\nexport interface MenuSignature {\n Args: {\n contents: MenuEntry[];\n /** the key legend under the list */\n keys?: string;\n pick: (head: number) => void;\n sub: string;\n title: string;\n };\n}\n\n/**\n * THE DISC MENU. A film with chapters owes the viewer a way into them,\n * and the arrow keys alone are a secret. Picking one re-cuts the score\n * from that chapter's head — the same move the arrows make, because a\n * skip here is an edit and never a seek. The film keeps running behind\n * it, blurred: a menu that freezes the picture makes it feel like a file.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Menu extends Component<MenuSignature> {\n <template>\n <Choreo class='cf-menu' as |m|>\n <div class='cf-menu-in' {{motion id='menu' role='sheet'}}>\n <p class='cf-menu-head'>{{@title}}</p>\n <p class='cf-menu-sub'>{{@sub}}</p>\n <ol class='cf-menu-list'>\n {{#each @contents as |c|}}\n <li>\n <button\n type='button'\n class='cf-menu-item {{if c.here \"is-here\"}}'\n {{on 'click' (fn @pick c.head)}}\n >\n <span class='cf-menu-n'>{{c.n}}</span>\n <span class='cf-menu-t'>{{c.title}}</span>\n <span class='cf-menu-d'>{{c.shots}} shots</span>\n </button>\n </li>\n {{/each}}\n </ol>\n <p class='cf-menu-keys'>{{if\n @keys\n @keys\n 'space / K play · ← → chapter · 1–9 chapter · M sound · F full screen · V captions · 0 title screen · C close'\n }}</p>\n </div>\n <m.Tween\n @of={{m.inserted 'sheet'}}\n @y={{array 26 0}}\n @scale={{array 0.97 1}}\n @opacity={{array 0 1}}\n @duration={{0.42}}\n @ease={{array 0.22 1 0.36 1}}\n />\n <m.Tween\n @of={{m.removed 'sheet'}}\n @y={{array 0 18}}\n @opacity={{array 1 0}}\n @duration={{0.2}}\n @ease='easeIn'\n />\n </Choreo>\n </template>\n}\n\nfunction array<T>(...items: T[]): T[] {\n return items;\n}\n\nfunction eq(a: unknown, b: unknown): boolean {\n return a === b;\n}\n"],"names":["Player","Component","setComponentTemplate","precompileTemplate","strictMode","scope","on","eq","Burst","Menu","Choreo","motion","fn","array","items","a","b"],"mappings":";;;;;;;;AAgBA;AAyDA;;;;;;;;;;AAUC;AAEM,MAAMA,eAAeC,SAAA,CAAU;AACpC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,2hJAAA,EAyKA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAUA;;;;AAIC;AAEM,MAAMC,cAAcP,SAAA,CAAU;AACnC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,yQAAA,EAQA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA;;AAqBA;;;;;;AAMC;AAEM,MAAME,aAAaR,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,8mCAAA,EA0CA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAK,MAAA;QAAAC,MAAA;QAAAL,EAAA;QAAAM,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASA,KAAAA,CAAS,GAAGC,KAAU,EAAG;AAChC,EAAA,OAAOA,KAAA;AACT;AAEA,SAASP,GAAGQ,CAAU,EAAEC,CAAU,EAAU;EAC1C,OAAOD,CAAA,KAAMC,CAAA;AACf;;;;"}
@@ -0,0 +1,38 @@
1
+ import { fn } from '@ember/helper';
2
+ import { on } from '@ember/modifier';
3
+ import Component from '@glimmer/component';
4
+ import { precompileTemplate } from '@ember/template-compilation';
5
+ import { setComponentTemplate } from '@ember/component';
6
+
7
+ /**
8
+ * THE FILM ON ONE LINE. A rule the width of the chapter strip, the run
9
+ * so far filled in the accent, a dot on the head and the readout riding
10
+ * it; the chapters are dots at the places they open, so the distance
11
+ * between them is a fact and not a layout. The dots are the doors into
12
+ * the chapters. The head's position is a custom property (`--cf-head`)
13
+ * written by the film every frame, so nothing here re-renders on the
14
+ * clock.
15
+ */
16
+ class Rail extends Component {
17
+ atStyle = m => `left:calc(5px + ${m.at.toFixed(4)} * (100% - 10px))`;
18
+ static {
19
+ setComponentTemplate(precompileTemplate("<div class=\"cf-rail\">\n <span class=\"cf-rail-n\">{{@chapter.n}}</span>\n <span class=\"cf-rail-t\">{{@chapter.title}}</span>\n <span class=\"cf-years\">\n <i class=\"cf-years-rule\" aria-hidden=\"true\"></i>\n <i class=\"cf-years-fill\" aria-hidden=\"true\"></i>\n {{#each @marks as |m|}}\n <button type=\"button\" class=\"cf-years-ch {{if m.here \"is-here\"}} {{if m.done \"is-done\"}}\" style={{this.atStyle m}} title=\"{{m.n}} {{m.title}}{{if m.label (concat \" \xB7 \" m.label)}}\" {{on \"click\" (fn @pick m.head)}}></button>\n {{/each}}\n {{#if @readout}}\n <b class=\"cf-years-now\" aria-hidden=\"true\">{{@readout}}</b>\n {{else}}\n <b class=\"cf-years-now is-mute\" aria-hidden=\"true\"></b>\n {{/if}}\n {{#if @labels}}\n <em class=\"cf-years-a\" aria-hidden=\"true\">{{get @labels 0}}</em>\n <em class=\"cf-years-b\" aria-hidden=\"true\">{{get @labels 1}}</em>\n {{/if}}\n </span>\n</div>", {
20
+ strictMode: true,
21
+ scope: () => ({
22
+ concat,
23
+ on,
24
+ fn,
25
+ get
26
+ })
27
+ }), this);
28
+ }
29
+ }
30
+ function concat(...parts) {
31
+ return parts.map(p => String(p ?? '')).join('');
32
+ }
33
+ function get(list, i) {
34
+ return list[i];
35
+ }
36
+
37
+ export { Rail };
38
+ //# sourceMappingURL=rail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rail.js","sources":["../../src/film/rail.gts"],"sourcesContent":["import { fn } from '@ember/helper';\nimport { on } from '@ember/modifier';\nimport Component from '@glimmer/component';\n\nimport type { Chapter } from './types.ts';\n\n/** a chapter as a door on the rule */\nexport interface RailMark {\n /** where on the rule, 0..1 */\n at: number;\n done: boolean;\n head: number;\n here: boolean;\n /** the label the door announces when the rule is a clock */\n label?: string;\n n: string;\n title: string;\n}\n\nexport interface RailSignature {\n Args: {\n /** the chapter on screen */\n chapter: Chapter;\n /** the ends of the rule, as the film writes them; empty when the rule is time */\n labels?: [string, string];\n marks: RailMark[];\n /** cut to a chapter's head */\n pick: (head: number) => void;\n /** the readout riding the head — the year, or nothing */\n readout?: string;\n };\n}\n\n/**\n * THE FILM ON ONE LINE. A rule the width of the chapter strip, the run\n * so far filled in the accent, a dot on the head and the readout riding\n * it; the chapters are dots at the places they open, so the distance\n * between them is a fact and not a layout. The dots are the doors into\n * the chapters. The head's position is a custom property (`--cf-head`)\n * written by the film every frame, so nothing here re-renders on the\n * clock.\n */\nexport class Rail extends Component<RailSignature> {\n atStyle = (m: RailMark): string =>\n `left:calc(5px + ${m.at.toFixed(4)} * (100% - 10px))`;\n\n <template>\n <div class='cf-rail'>\n <span class='cf-rail-n'>{{@chapter.n}}</span>\n <span class='cf-rail-t'>{{@chapter.title}}</span>\n <span class='cf-years'>\n <i class='cf-years-rule' aria-hidden='true'></i>\n <i class='cf-years-fill' aria-hidden='true'></i>\n {{#each @marks as |m|}}\n <button\n type='button'\n class='cf-years-ch {{if m.here \"is-here\"}} {{if m.done \"is-done\"}}'\n style={{this.atStyle m}}\n title='{{m.n}} {{m.title}}{{if m.label (concat \" · \" m.label)}}'\n {{on 'click' (fn @pick m.head)}}\n ></button>\n {{/each}}\n {{#if @readout}}\n <b class='cf-years-now' aria-hidden='true'>{{@readout}}</b>\n {{else}}\n <b class='cf-years-now is-mute' aria-hidden='true'></b>\n {{/if}}\n {{#if @labels}}\n <em class='cf-years-a' aria-hidden='true'>{{get @labels 0}}</em>\n <em class='cf-years-b' aria-hidden='true'>{{get @labels 1}}</em>\n {{/if}}\n </span>\n </div>\n </template>\n}\n\nfunction concat(...parts: unknown[]): string {\n return parts.map((p) => String(p ?? '')).join('');\n}\n\nfunction get<T>(list: readonly T[], i: number): T | undefined {\n return list[i];\n}\n"],"names":["Rail","Component","atStyle","m","at","toFixed","setComponentTemplate","precompileTemplate","strictMode","scope","concat","on","fn","get","parts","map","p","String","join","list","i"],"mappings":";;;;;;AAiCA;;;;;;;;;AASO,MAAMA,IAAA,SAAaC,SAAA,CAAU;AAClCC,EAAAA,OAAA,GAAWC,CAAG,IACZ,CAAA,gBAAA,EAAmBA,CAAA,CAAEC,EAAE,CAACC,OAAO,CAAC,CAAA,CAAA,CAAA,iBAAA,CAAqB;AAEvD,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,+7BAAA,EA2BA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,MAAA;QAAAC,EAAA;QAAAC,EAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASH,OAAO,GAAGI,KAAgB,EAAS;AAC1C,EAAA,OAAOA,KAAA,CAAMC,GAAG,CAAEC,KAAMC,MAAA,CAAOD,CAAA,IAAK,EAAA,CAAA,CAAA,CAAKE,IAAI,CAAC,EAAA,CAAA;AAChD;AAEA,SAASL,GAAAA,CAAOM,IAAkB,EAAEC,CAAS,EAAgB;EAC3D,OAAOD,IAAI,CAACC,CAAA,CAAE;AAChB;;;;"}
@@ -0,0 +1,238 @@
1
+ import { lerp } from './math.js';
2
+
3
+ /**
4
+ * THE SCHEDULE — everything a film derives from its shot list before a
5
+ * single frame is drawn, as pure functions of the table.
6
+ *
7
+ * A film is its cue table and its camera path: when a beat starts, what
8
+ * fires when, where the lens is at every waypoint, how the chapters are
9
+ * cut. These were getters on the engine; lifting them here changes no
10
+ * number — the engine calls them — and lets the same arithmetic be run
11
+ * headless, in Node, to write the golden fixtures the graph refactor is
12
+ * measured against (`scripts/film-fixtures.mjs`,
13
+ * `tests/unit/film-schedule-test.ts`).
14
+ *
15
+ * Nothing in here reads the clock, the page or the DOM. Everything is a
16
+ * function of `Beat[]` and a few numbers, which is the property the
17
+ * fixtures pin.
18
+ */
19
+ /** one tick of the shot list, in seconds: a beat's `ticks` × this is its length */
20
+ const TICK = 2;
21
+
22
+ /** the whole film, in seconds, leads included */
23
+ function totalSecs(beats) {
24
+ return beats.reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : b.lead ?? 0), 0) * TICK;
25
+ }
26
+
27
+ /** film seconds before a beat's nominal head, leads included */
28
+ function secsBefore(beats, index) {
29
+ return beats.slice(0, index).reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : b.lead ?? 0), 0) * TICK;
30
+ }
31
+
32
+ /**
33
+ * FILM SECONDS AT WHICH A BEAT'S OWN SHOT BEGINS. The cues fire one tick
34
+ * after the beat table says, because the pose-in-force seed occupies the
35
+ * spline's first slot — so the windows the exact clock derives carry the
36
+ * same offset, and the picture and the type agree with the cut film to
37
+ * the frame. This is the single most load-bearing constant in the film.
38
+ */
39
+ function beatStart(beats, i) {
40
+ if (i <= 0) {
41
+ return 0;
42
+ }
43
+ return secsBefore(beats, i) + (beats[i].lead ?? 0) * TICK + TICK;
44
+ }
45
+
46
+ /** one cue per beat (and an `air` cue ahead of a led beat), as delays into the score */
47
+
48
+ function cues(beats) {
49
+ let t = 0;
50
+ const out = [];
51
+ beats.forEach((b, i) => {
52
+ const lead = i === 0 ? 0 : b.lead ?? 0;
53
+ if (lead > 0) {
54
+ out.push({
55
+ action: 'air',
56
+ delay: t + TICK,
57
+ index: String(i)
58
+ });
59
+ }
60
+ const at = t + lead * TICK;
61
+ t = at + b.ticks * TICK;
62
+ /* cue 0 is exempt from the offset: the boot applies the head beat by
63
+ hand and a guard swallows its immediate re-fire */
64
+ out.push({
65
+ action: 'beat',
66
+ delay: at === 0 ? 0 : at + TICK,
67
+ index: String(i)
68
+ });
69
+ });
70
+ return out;
71
+ }
72
+
73
+ /** the first beat of each chapter, in whole-film indices */
74
+ function chapterHeads(beats) {
75
+ const heads = [];
76
+ beats.forEach((b, i) => {
77
+ if (heads.length === 0 || beats[heads[heads.length - 1]].ch !== b.ch) {
78
+ heads.push(i);
79
+ }
80
+ });
81
+ return heads;
82
+ }
83
+
84
+ /** the chapter menu's rows: head index, numeral, title, shot count, running time */
85
+
86
+ function contents(beats, chapters) {
87
+ const heads = chapterHeads(beats);
88
+ return heads.map((head, i) => {
89
+ const nextHead = heads[i + 1] ?? beats.length;
90
+ const ch = chapters[beats[head].ch] ?? chapters[0];
91
+ return {
92
+ head,
93
+ n: ch.n,
94
+ secs: beats.slice(head, nextHead).reduce((t, b) => t + b.ticks, 0) * TICK,
95
+ shots: nextHead - head,
96
+ title: ch.title
97
+ };
98
+ });
99
+ }
100
+
101
+ /** the seam a beat is entered through, or none when it neither cuts nor names one */
102
+ function joinInto(beats, i, defaultJoin) {
103
+ const beat = beats[i];
104
+ return beat.cut || beat.join ? beat.join ?? defaultJoin : null;
105
+ }
106
+
107
+ /**
108
+ * WHERE A SHOT ENDS — the beat's authored tail, floored so it always reads
109
+ * as a move, and clamped so it never travels past the pose the next shot
110
+ * begins on. Shared by the path (which lerps its waypoints across it) and
111
+ * by the launch: a cut hands the chaser this shot's own speed.
112
+ *
113
+ * A shot must move on screen, not on paper: frame-differencing a capture
114
+ * of the cut showed authored tails of a few degrees reading as stills, so
115
+ * a tail is a DIRECTION and a floor, not a distance — what the beat asks
116
+ * for is honoured, and anything slower than a real slow move is stretched
117
+ * up to one. The floors are per second. And it never travels past where
118
+ * the next shot begins: at a cut that jumped BACKWARDS into the successor.
119
+ */
120
+ function tailFor(beats, bi) {
121
+ const b = beats[bi];
122
+ const drift = b.toCam ?? {
123
+ ...b.cam,
124
+ dolly: b.cam.dolly * 1.05,
125
+ pitch: b.cam.pitch + 1.4,
126
+ yaw: b.cam.yaw + 6
127
+ };
128
+ const next = beats[bi + 1];
129
+ const secs = b.ticks * TICK;
130
+ const sgn = d => d < 0 ? -1 : 1;
131
+ const stretch = (key, floor) => {
132
+ const head = b.cam[key] ?? 0;
133
+ const d = (drift[key] ?? 0) - head;
134
+ const nose = next ? (next.cam[key] ?? 0) - head : 0;
135
+ /* the direction is the beat's own, or the next shot's if the beat
136
+ asked for nothing at all */
137
+ const way = d !== 0 ? sgn(d) : nose !== 0 ? sgn(nose) : 1;
138
+ const reach = !next ? Infinity : sgn(nose) === way && nose !== 0 ? Math.abs(nose) : 0;
139
+ const want = Math.min(Math.max(Math.abs(d), floor), Math.max(Math.abs(d), reach));
140
+ return head + way * want;
141
+ };
142
+ const to = {
143
+ ...drift,
144
+ dolly: stretch('dolly', b.cam.dolly * Math.min(0.22, 0.022 * secs)),
145
+ ox: stretch('ox', Math.min(0.07, 0.008 * secs)),
146
+ pitch: stretch('pitch', Math.min(6, 0.45 * secs)),
147
+ yaw: stretch('yaw', Math.min(22, 2 * secs))
148
+ };
149
+ return {
150
+ dolly: to.dolly,
151
+ fx: to.fx ?? b.cam.fx ?? 0,
152
+ fz: to.fz ?? b.cam.fz ?? 0,
153
+ lookY: to.lookY,
154
+ ox: to.ox,
155
+ pitch: to.pitch,
156
+ yaw: to.yaw
157
+ };
158
+ }
159
+
160
+ /** one waypoint of the camera spline, in the terms `c.Camera3D @through` takes */
161
+
162
+ /**
163
+ * THE PATH. Every beat contributes `ticks` waypoints, lerped from its
164
+ * head to its tail, so waypoint count is screen time; a led beat first
165
+ * contributes `lead` waypoints travelling in from the previous tail; a
166
+ * `cut` beat splices the spline. `recut` marks the first beat as a splice
167
+ * too — a cut film re-cut from a chapter's head has nothing behind it.
168
+ */
169
+ function waypoints(beats, recut = false) {
170
+ const pts = [];
171
+ for (const [bi, b] of beats.entries()) {
172
+ const splice = b.cut === true || bi === 0 && recut;
173
+ const to = tailFor(beats, bi);
174
+ const lead = bi === 0 ? 0 : b.lead ?? 0;
175
+ const prev = pts[pts.length - 1];
176
+ if (lead > 0 && prev) {
177
+ for (let k = 1; k <= lead; k++) {
178
+ const f = k / (lead + 1);
179
+ pts.push({
180
+ dolly: lerp(prev.dolly, b.cam.dolly, f),
181
+ look: {
182
+ x: lerp(prev.look.x, b.cam.fx ?? 0, f),
183
+ y: lerp(prev.look.y, b.cam.lookY, f),
184
+ z: lerp(prev.look.z, b.cam.fz ?? 0, f)
185
+ },
186
+ pitch: lerp(prev.pitch, b.cam.pitch, f),
187
+ x: lerp(prev.x, b.cam.ox ?? 0, f),
188
+ y: 0,
189
+ yaw: lerp(prev.yaw, b.cam.yaw, f)
190
+ });
191
+ }
192
+ }
193
+ for (let k = 0; k < b.ticks; k++) {
194
+ const f = b.ticks === 1 ? 0 : k / (b.ticks - 1);
195
+ pts.push({
196
+ cut: splice && k === 0 ? true : undefined,
197
+ dolly: lerp(b.cam.dolly, to.dolly, f),
198
+ look: {
199
+ x: lerp(b.cam.fx ?? 0, to.fx ?? 0, f),
200
+ y: lerp(b.cam.lookY, to.lookY, f),
201
+ z: lerp(b.cam.fz ?? 0, to.fz ?? 0, f)
202
+ },
203
+ pitch: lerp(b.cam.pitch, to.pitch, f),
204
+ x: lerp(b.cam.ox ?? 0, to.ox ?? 0, f),
205
+ y: 0,
206
+ yaw: lerp(b.cam.yaw, to.yaw, f)
207
+ });
208
+ }
209
+ }
210
+ return pts;
211
+ }
212
+
213
+ /**
214
+ * THE GOLDEN SCHEDULE: one object per film, everything above at once, in
215
+ * the shape the fixtures are written in. A refactor of the engine that
216
+ * changes any number in here has changed the film.
217
+ */
218
+
219
+ function schedule(beats, chapters, defaultJoin) {
220
+ return {
221
+ beats: beats.map((b, i) => ({
222
+ ch: b.ch,
223
+ id: b.id,
224
+ join: joinInto(beats, i, defaultJoin),
225
+ lead: i === 0 ? 0 : b.lead ?? 0,
226
+ secsBefore: secsBefore(beats, i),
227
+ start: beatStart(beats, i),
228
+ ticks: b.ticks
229
+ })),
230
+ contents: contents(beats, chapters),
231
+ cues: cues(beats),
232
+ total: totalSecs(beats),
233
+ waypoints: waypoints(beats)
234
+ };
235
+ }
236
+
237
+ export { TICK, beatStart, chapterHeads, contents, cues, joinInto, schedule, secsBefore, tailFor, totalSecs, waypoints };
238
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sources":["../../src/film/schedule.ts"],"sourcesContent":["/**\n * THE SCHEDULE — everything a film derives from its shot list before a\n * single frame is drawn, as pure functions of the table.\n *\n * A film is its cue table and its camera path: when a beat starts, what\n * fires when, where the lens is at every waypoint, how the chapters are\n * cut. These were getters on the engine; lifting them here changes no\n * number — the engine calls them — and lets the same arithmetic be run\n * headless, in Node, to write the golden fixtures the graph refactor is\n * measured against (`scripts/film-fixtures.mjs`,\n * `tests/unit/film-schedule-test.ts`).\n *\n * Nothing in here reads the clock, the page or the DOM. Everything is a\n * function of `Beat[]` and a few numbers, which is the property the\n * fixtures pin.\n */\nimport { lerp } from './math.ts';\nimport type { Beat, Cam, Chapter, Join, JoinName } from './types.ts';\n\n/** one tick of the shot list, in seconds: a beat's `ticks` × this is its length */\nexport const TICK = 2;\n\n/** the whole film, in seconds, leads included */\nexport function totalSecs(beats: Beat[]): number {\n return (\n beats.reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : (b.lead ?? 0)), 0) *\n TICK\n );\n}\n\n/** film seconds before a beat's nominal head, leads included */\nexport function secsBefore(beats: Beat[], index: number): number {\n return (\n beats\n .slice(0, index)\n .reduce((n, b, i) => n + b.ticks + (i === 0 ? 0 : (b.lead ?? 0)), 0) *\n TICK\n );\n}\n\n/**\n * FILM SECONDS AT WHICH A BEAT'S OWN SHOT BEGINS. The cues fire one tick\n * after the beat table says, because the pose-in-force seed occupies the\n * spline's first slot — so the windows the exact clock derives carry the\n * same offset, and the picture and the type agree with the cut film to\n * the frame. This is the single most load-bearing constant in the film.\n */\nexport function beatStart(beats: Beat[], i: number): number {\n if (i <= 0) {\n return 0;\n }\n return secsBefore(beats, i) + (beats[i]!.lead ?? 0) * TICK + TICK;\n}\n\n/** one cue per beat (and an `air` cue ahead of a led beat), as delays into the score */\nexport interface Cue {\n action: 'air' | 'beat';\n delay: number;\n index: string;\n}\n\nexport function cues(beats: Beat[]): Cue[] {\n let t = 0;\n const out: Cue[] = [];\n beats.forEach((b, i) => {\n const lead = i === 0 ? 0 : (b.lead ?? 0);\n if (lead > 0) {\n out.push({ action: 'air', delay: t + TICK, index: String(i) });\n }\n const at = t + lead * TICK;\n t = at + b.ticks * TICK;\n /* cue 0 is exempt from the offset: the boot applies the head beat by\n hand and a guard swallows its immediate re-fire */\n out.push({\n action: 'beat',\n delay: at === 0 ? 0 : at + TICK,\n index: String(i),\n });\n });\n return out;\n}\n\n/** the first beat of each chapter, in whole-film indices */\nexport function chapterHeads(beats: Beat[]): number[] {\n const heads: number[] = [];\n beats.forEach((b, i) => {\n if (heads.length === 0 || beats[heads[heads.length - 1]!]!.ch !== b.ch) {\n heads.push(i);\n }\n });\n return heads;\n}\n\n/** the chapter menu's rows: head index, numeral, title, shot count, running time */\nexport interface Content {\n head: number;\n n: string;\n secs: number;\n shots: number;\n title: string;\n}\n\nexport function contents(beats: Beat[], chapters: Chapter[]): Content[] {\n const heads = chapterHeads(beats);\n return heads.map((head, i) => {\n const nextHead = heads[i + 1] ?? beats.length;\n const ch = chapters[beats[head]!.ch] ?? chapters[0]!;\n return {\n head,\n n: ch.n,\n secs: beats.slice(head, nextHead).reduce((t, b) => t + b.ticks, 0) * TICK,\n shots: nextHead - head,\n title: ch.title,\n };\n });\n}\n\n/** the seam a beat is entered through, or none when it neither cuts nor names one */\nexport function joinInto(\n beats: Beat[],\n i: number,\n defaultJoin: Join,\n): JoinName | null {\n const beat = beats[i]!;\n return beat.cut || beat.join ? (beat.join ?? defaultJoin) : null;\n}\n\n/**\n * WHERE A SHOT ENDS — the beat's authored tail, floored so it always reads\n * as a move, and clamped so it never travels past the pose the next shot\n * begins on. Shared by the path (which lerps its waypoints across it) and\n * by the launch: a cut hands the chaser this shot's own speed.\n *\n * A shot must move on screen, not on paper: frame-differencing a capture\n * of the cut showed authored tails of a few degrees reading as stills, so\n * a tail is a DIRECTION and a floor, not a distance — what the beat asks\n * for is honoured, and anything slower than a real slow move is stretched\n * up to one. The floors are per second. And it never travels past where\n * the next shot begins: at a cut that jumped BACKWARDS into the successor.\n */\nexport function tailFor(beats: Beat[], bi: number): Cam {\n const b = beats[bi]!;\n const drift = b.toCam ?? {\n ...b.cam,\n dolly: b.cam.dolly * 1.05,\n pitch: b.cam.pitch + 1.4,\n yaw: b.cam.yaw + 6,\n };\n const next = beats[bi + 1];\n const secs = b.ticks * TICK;\n const sgn = (d: number) => (d < 0 ? -1 : 1);\n const stretch = (\n key: 'dolly' | 'ox' | 'pitch' | 'yaw',\n floor: number,\n ): number => {\n const head = b.cam[key] ?? 0;\n const d = (drift[key] ?? 0) - head;\n const nose = next ? (next.cam[key] ?? 0) - head : 0;\n /* the direction is the beat's own, or the next shot's if the beat\n asked for nothing at all */\n const way = d !== 0 ? sgn(d) : nose !== 0 ? sgn(nose) : 1;\n const reach = !next\n ? Infinity\n : sgn(nose) === way && nose !== 0\n ? Math.abs(nose)\n : 0;\n const want = Math.min(\n Math.max(Math.abs(d), floor),\n Math.max(Math.abs(d), reach),\n );\n return head + way * want;\n };\n const to = {\n ...drift,\n dolly: stretch('dolly', b.cam.dolly * Math.min(0.22, 0.022 * secs)),\n ox: stretch('ox', Math.min(0.07, 0.008 * secs)),\n pitch: stretch('pitch', Math.min(6, 0.45 * secs)),\n yaw: stretch('yaw', Math.min(22, 2 * secs)),\n };\n return {\n dolly: to.dolly,\n fx: to.fx ?? b.cam.fx ?? 0,\n fz: to.fz ?? b.cam.fz ?? 0,\n lookY: to.lookY,\n ox: to.ox,\n pitch: to.pitch,\n yaw: to.yaw,\n };\n}\n\n/** one waypoint of the camera spline, in the terms `c.Camera3D @through` takes */\nexport interface Waypoint {\n cut?: boolean;\n dolly: number;\n look: { x: number; y: number; z: number };\n pitch: number;\n x: number;\n y: number;\n yaw: number;\n}\n\n/**\n * THE PATH. Every beat contributes `ticks` waypoints, lerped from its\n * head to its tail, so waypoint count is screen time; a led beat first\n * contributes `lead` waypoints travelling in from the previous tail; a\n * `cut` beat splices the spline. `recut` marks the first beat as a splice\n * too — a cut film re-cut from a chapter's head has nothing behind it.\n */\nexport function waypoints(beats: Beat[], recut = false): Waypoint[] {\n const pts: Waypoint[] = [];\n for (const [bi, b] of beats.entries()) {\n const splice = b.cut === true || (bi === 0 && recut);\n const to = tailFor(beats, bi);\n const lead = bi === 0 ? 0 : (b.lead ?? 0);\n const prev = pts[pts.length - 1];\n if (lead > 0 && prev) {\n for (let k = 1; k <= lead; k++) {\n const f = k / (lead + 1);\n pts.push({\n dolly: lerp(prev.dolly, b.cam.dolly, f),\n look: {\n x: lerp(prev.look.x, b.cam.fx ?? 0, f),\n y: lerp(prev.look.y, b.cam.lookY, f),\n z: lerp(prev.look.z, b.cam.fz ?? 0, f),\n },\n pitch: lerp(prev.pitch, b.cam.pitch, f),\n x: lerp(prev.x, b.cam.ox ?? 0, f),\n y: 0,\n yaw: lerp(prev.yaw, b.cam.yaw, f),\n });\n }\n }\n for (let k = 0; k < b.ticks; k++) {\n const f = b.ticks === 1 ? 0 : k / (b.ticks - 1);\n pts.push({\n cut: splice && k === 0 ? true : undefined,\n dolly: lerp(b.cam.dolly, to.dolly, f),\n look: {\n x: lerp(b.cam.fx ?? 0, to.fx ?? 0, f),\n y: lerp(b.cam.lookY, to.lookY, f),\n z: lerp(b.cam.fz ?? 0, to.fz ?? 0, f),\n },\n pitch: lerp(b.cam.pitch, to.pitch, f),\n x: lerp(b.cam.ox ?? 0, to.ox ?? 0, f),\n y: 0,\n yaw: lerp(b.cam.yaw, to.yaw, f),\n });\n }\n }\n return pts;\n}\n\n/**\n * THE GOLDEN SCHEDULE: one object per film, everything above at once, in\n * the shape the fixtures are written in. A refactor of the engine that\n * changes any number in here has changed the film.\n */\nexport interface Schedule {\n beats: {\n ch: number;\n id: string;\n join: JoinName | null;\n lead: number;\n secsBefore: number;\n start: number;\n ticks: number;\n }[];\n contents: Content[];\n cues: Cue[];\n total: number;\n waypoints: Waypoint[];\n}\n\nexport function schedule(\n beats: Beat[],\n chapters: Chapter[],\n defaultJoin: Join,\n): Schedule {\n return {\n beats: beats.map((b, i) => ({\n ch: b.ch,\n id: b.id,\n join: joinInto(beats, i, defaultJoin),\n lead: i === 0 ? 0 : (b.lead ?? 0),\n secsBefore: secsBefore(beats, i),\n start: beatStart(beats, i),\n ticks: b.ticks,\n })),\n contents: contents(beats, chapters),\n cues: cues(beats),\n total: totalSecs(beats),\n waypoints: waypoints(beats),\n };\n}\n"],"names":["TICK","totalSecs","beats","reduce","n","b","i","ticks","lead","secsBefore","index","slice","beatStart","cues","t","out","forEach","push","action","delay","String","at","chapterHeads","heads","length","ch","contents","chapters","map","head","nextHead","secs","shots","title","joinInto","defaultJoin","beat","cut","join","tailFor","bi","drift","toCam","cam","dolly","pitch","yaw","next","sgn","d","stretch","key","floor","nose","way","reach","Infinity","Math","abs","want","min","max","to","ox","fx","fz","lookY","waypoints","recut","pts","entries","splice","prev","k","f","lerp","look","x","y","z","undefined","schedule","id","start","total"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * THE SEAM'S GATE: hold the cut until its still can be PAINTED.
3
+ *
4
+ * Every join that carries the outgoing frame carries it as a JPEG data
5
+ * URL in the DOM, and a full-resolution JPEG handed to the DOM is not
6
+ * paintable on the frame it is handed over — the browser decodes it off
7
+ * the main thread. The lens, meanwhile, moves on that frame. So the seam
8
+ * used to play backwards: three frames of the incoming shot at 60 Hz,
9
+ * then the still of the outgoing one, then the transition over it.
10
+ *
11
+ * The gate decodes the frame into the memory cache first and holds the
12
+ * whole cut — the lens snap, the overlay, an iris's projection — until
13
+ * it is ready; the DOM image then paints from that decode on the frame
14
+ * it is inserted. The outgoing shot stays live and moving for those few
15
+ * milliseconds, which nobody can see: a cut two frames late reads as a
16
+ * cut, a cut that shows the wrong picture does not.
17
+ *
18
+ * Three rules the film depends on:
19
+ *
20
+ * - **Nothing to decode, nothing to wait for.** A seam with no still (a
21
+ * cut, a flash, a dissolve the page held in its own glass) runs
22
+ * SYNCHRONOUSLY, on the caller's stack, exactly as it always did.
23
+ * - **One seam at a time.** A cut that arrives while an earlier one is
24
+ * still waiting retires it; the earlier one never runs.
25
+ * - **No seam waits for ever.** A decode that never comes back (a
26
+ * truncated capture, a browser without `HTMLImageElement.decode`) is
27
+ * given `cap` milliseconds and then the cut goes anyway.
28
+ */
29
+ class Seam {
30
+ /** which cut is in hand; a later one retires an earlier one */
31
+ turn = 0;
32
+
33
+ /**
34
+ * Hold `cut` until `still` can be painted, or run it now if there is
35
+ * no still. `cap` is the longest the film will wait on a decode.
36
+ */
37
+ hold(still, cut, cap = 120) {
38
+ if (!still) {
39
+ cut();
40
+ return;
41
+ }
42
+ const mine = this.turn += 1;
43
+ let ran = false;
44
+ const go = () => {
45
+ if (ran || mine !== this.turn) {
46
+ return;
47
+ }
48
+ ran = true;
49
+ cut();
50
+ };
51
+ const pre = new Image();
52
+ pre.src = still;
53
+ setTimeout(go, cap);
54
+ const decoding = pre.decode?.();
55
+ if (decoding) {
56
+ void decoding.then(go, go);
57
+ } else {
58
+ go();
59
+ }
60
+ }
61
+ }
62
+
63
+ export { Seam };
64
+ //# sourceMappingURL=seam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seam.js","sources":["../../src/film/seam.ts"],"sourcesContent":["/**\n * THE SEAM'S GATE: hold the cut until its still can be PAINTED.\n *\n * Every join that carries the outgoing frame carries it as a JPEG data\n * URL in the DOM, and a full-resolution JPEG handed to the DOM is not\n * paintable on the frame it is handed over — the browser decodes it off\n * the main thread. The lens, meanwhile, moves on that frame. So the seam\n * used to play backwards: three frames of the incoming shot at 60 Hz,\n * then the still of the outgoing one, then the transition over it.\n *\n * The gate decodes the frame into the memory cache first and holds the\n * whole cut — the lens snap, the overlay, an iris's projection — until\n * it is ready; the DOM image then paints from that decode on the frame\n * it is inserted. The outgoing shot stays live and moving for those few\n * milliseconds, which nobody can see: a cut two frames late reads as a\n * cut, a cut that shows the wrong picture does not.\n *\n * Three rules the film depends on:\n *\n * - **Nothing to decode, nothing to wait for.** A seam with no still (a\n * cut, a flash, a dissolve the page held in its own glass) runs\n * SYNCHRONOUSLY, on the caller's stack, exactly as it always did.\n * - **One seam at a time.** A cut that arrives while an earlier one is\n * still waiting retires it; the earlier one never runs.\n * - **No seam waits for ever.** A decode that never comes back (a\n * truncated capture, a browser without `HTMLImageElement.decode`) is\n * given `cap` milliseconds and then the cut goes anyway.\n */\nexport class Seam {\n /** which cut is in hand; a later one retires an earlier one */\n private turn = 0;\n\n /**\n * Hold `cut` until `still` can be painted, or run it now if there is\n * no still. `cap` is the longest the film will wait on a decode.\n */\n hold(still: string, cut: () => void, cap = 120): void {\n if (!still) {\n cut();\n return;\n }\n const mine = (this.turn += 1);\n let ran = false;\n const go = () => {\n if (ran || mine !== this.turn) {\n return;\n }\n ran = true;\n cut();\n };\n const pre = new Image();\n pre.src = still;\n setTimeout(go, cap);\n const decoding = pre.decode?.();\n if (decoding) {\n void decoding.then(go, go);\n } else {\n go();\n }\n }\n}\n"],"names":["Seam","turn","hold","still","cut","cap","mine","ran","go","pre","Image","src","setTimeout","decoding","decode","then"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,IAAI,CAAC;AAChB;AACQC,EAAAA,IAAI,GAAG,CAAC;;AAEhB;AACF;AACA;AACA;EACEC,IAAIA,CAACC,KAAa,EAAEC,GAAe,EAAEC,GAAG,GAAG,GAAG,EAAQ;IACpD,IAAI,CAACF,KAAK,EAAE;AACVC,MAAAA,GAAG,EAAE;AACL,MAAA;AACF,IAAA;AACA,IAAA,MAAME,IAAI,GAAI,IAAI,CAACL,IAAI,IAAI,CAAE;IAC7B,IAAIM,GAAG,GAAG,KAAK;IACf,MAAMC,EAAE,GAAGA,MAAM;AACf,MAAA,IAAID,GAAG,IAAID,IAAI,KAAK,IAAI,CAACL,IAAI,EAAE;AAC7B,QAAA;AACF,MAAA;AACAM,MAAAA,GAAG,GAAG,IAAI;AACVH,MAAAA,GAAG,EAAE;IACP,CAAC;AACD,IAAA,MAAMK,GAAG,GAAG,IAAIC,KAAK,EAAE;IACvBD,GAAG,CAACE,GAAG,GAAGR,KAAK;AACfS,IAAAA,UAAU,CAACJ,EAAE,EAAEH,GAAG,CAAC;AACnB,IAAA,MAAMQ,QAAQ,GAAGJ,GAAG,CAACK,MAAM,IAAI;AAC/B,IAAA,IAAID,QAAQ,EAAE;AACZ,MAAA,KAAKA,QAAQ,CAACE,IAAI,CAACP,EAAE,EAAEA,EAAE,CAAC;AAC5B,IAAA,CAAC,MAAM;AACLA,MAAAA,EAAE,EAAE;AACN,IAAA;AACF,EAAA;AACF;;;;"}
@@ -0,0 +1,45 @@
1
+ import Component from '@glimmer/component';
2
+ import { motion } from 'glimmer-motion';
3
+ import { Choreo } from '../choreo.js';
4
+ import { precompileTemplate } from '@ember/template-compilation';
5
+ import { setComponentTemplate } from '@ember/component';
6
+
7
+ /**
8
+ * THE GATE — the front door. The film is narrated, so the door asks; and
9
+ * the click that answers is the same gesture autoplay policy wants. The
10
+ * scene stands behind it, already seated on the opening frame and
11
+ * turning. What the door SAYS is the film's: the block is the front
12
+ * matter, set in the title package (`cf-matter`, `cf-mg-*`), and the
13
+ * shell here is the museum frame and the wash the type stands on.
14
+ */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
15
+ class Gate extends Component {
16
+ static {
17
+ setComponentTemplate(precompileTemplate("<div class=\"cf-gate\">\n {{!-- the museum frame: a hairline border drawn just inside the screen,\n the way a plate is matted --}}\n <i class=\"cf-gate-frame\" aria-hidden=\"true\"></i>\n {{yield @film}}\n</div>", {
18
+ strictMode: true
19
+ }), this);
20
+ }
21
+ }
22
+ /**
23
+ * THE END CARD. It arrives a moment after the last line has settled, over
24
+ * the held final frame — the film does not loop past its own ending;
25
+ * watching again is the viewer's choice. Back matter is the same package
26
+ * as the front, run in reverse order of importance.
27
+ */ // eslint-disable-next-line ember/no-empty-glimmer-component-classes
28
+ class EndCard extends Component {
29
+ static {
30
+ setComponentTemplate(precompileTemplate("<Choreo class=\"cf-end\" as |m|>\n <div class=\"cf-end-in cf-matter\" {{motion id=\"end\" role=\"card\"}}>\n {{yield @film}}\n </div>\n <m.Tween @of={{m.inserted \"card\"}} @delay={{0.7}} @opacity={{array 0 1}} @y={{array 18 0}} @duration={{0.9}} @ease={{array 0.22 1 0.36 1}} />\n <m.Tween @of={{m.removed \"card\"}} @opacity={{array 1 0}} @duration={{0.2}} @ease=\"easeIn\" />\n</Choreo>", {
31
+ strictMode: true,
32
+ scope: () => ({
33
+ Choreo,
34
+ motion,
35
+ array
36
+ })
37
+ }), this);
38
+ }
39
+ }
40
+ function array(...items) {
41
+ return items;
42
+ }
43
+
44
+ export { EndCard, Gate };
45
+ //# sourceMappingURL=titles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"titles.js","sources":["../../src/film/titles.gts"],"sourcesContent":["import Component from '@glimmer/component';\nimport { motion } from 'glimmer-motion';\n\nimport { Choreo } from '../choreo.gts';\nimport type { FilmHandle } from './types.ts';\n\nexport interface GateSignature {\n Args: {\n film: FilmHandle;\n };\n Blocks: {\n default: [FilmHandle];\n };\n}\n\n/**\n * THE GATE — the front door. The film is narrated, so the door asks; and\n * the click that answers is the same gesture autoplay policy wants. The\n * scene stands behind it, already seated on the opening frame and\n * turning. What the door SAYS is the film's: the block is the front\n * matter, set in the title package (`cf-matter`, `cf-mg-*`), and the\n * shell here is the museum frame and the wash the type stands on.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class Gate extends Component<GateSignature> {\n <template>\n <div class='cf-gate'>\n {{! the museum frame: a hairline border drawn just inside the screen,\n the way a plate is matted }}\n <i class='cf-gate-frame' aria-hidden='true'></i>\n {{yield @film}}\n </div>\n </template>\n}\n\nexport interface EndCardSignature {\n Args: {\n film: FilmHandle;\n };\n Blocks: {\n default: [FilmHandle];\n };\n}\n\n/**\n * THE END CARD. It arrives a moment after the last line has settled, over\n * the held final frame — the film does not loop past its own ending;\n * watching again is the viewer's choice. Back matter is the same package\n * as the front, run in reverse order of importance.\n */\n// eslint-disable-next-line ember/no-empty-glimmer-component-classes\nexport class EndCard extends Component<EndCardSignature> {\n <template>\n <Choreo class='cf-end' as |m|>\n <div class='cf-end-in cf-matter' {{motion id='end' role='card'}}>\n {{yield @film}}\n </div>\n <m.Tween\n @of={{m.inserted 'card'}}\n @delay={{0.7}}\n @opacity={{array 0 1}}\n @y={{array 18 0}}\n @duration={{0.9}}\n @ease={{array 0.22 1 0.36 1}}\n />\n <m.Tween\n @of={{m.removed 'card'}}\n @opacity={{array 1 0}}\n @duration={{0.2}}\n @ease='easeIn'\n />\n </Choreo>\n </template>\n}\n\nfunction array<T>(...items: T[]): T[] {\n return items;\n}\n"],"names":["Gate","Component","setComponentTemplate","precompileTemplate","strictMode","EndCard","scope","Choreo","motion","array","items"],"mappings":";;;;;;AAeA;;;;;;;AAOC;AAEM,MAAMA,aAAaC,SAAA,CAAU;AAClC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,yNAAA,EAOA;MAAAC,UAAA,EAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAWA;;;;;AAKC;AAEM,MAAMC,gBAAgBJ,SAAA,CAAU;AACrC,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,8YAAA,EAoBA;MAAAC,UAAA,EAAA,IAAA;AAAAE,MAAAA,KAAA,EAAAA,OAAA;QAAAC,MAAA;QAAAC,MAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;AAEA,SAASA,KAAAA,CAAS,GAAGC,KAAU,EAAG;AAChC,EAAA,OAAOA,KAAA;AACT;;;;"}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/film.js ADDED
@@ -0,0 +1,18 @@
1
+ export { Clip } from './film/clip.js';
2
+ export { clipLanes, clipWindow, resolveClip, resolveClips } from './film/clips.js';
3
+ export { Film } from './film/film.js';
4
+ export { Adjustment, Build, Filter, IFRAME_PICTURE, Light, Look, Mix, SOUND, Set, Sun, Weather, Winter } from './film/graph/adjust.js';
5
+ export { compileGraph } from './film/graph/compile.js';
6
+ export { FilmGraph, VOCABULARY } from './film/graph/host.js';
7
+ export { Eye, Freeze, Attach as GraphAttach, Chapter as GraphChapter, Insert as GraphInsert, GraphNodeComponent, Stamp as GraphStamp, JoinInto, Lineup, Mark, PatchComponent, Sequence, Shot, Sky, Spine, To, Trace, Type, Video, Voice, collectGraph } from './film/graph/nodes.js';
8
+ export { Blend, Blur, Dip, Flash, Iris, JOIN_SECS, Joins, Luma, Melt, PRESENTATIONS, STILL_JOINS, Wipe, inGlass, retire, seamShape } from './film/joins.js';
9
+ export { RAD, clamp01, hex, lerp, luminance, mmss, rgba, smooth } from './film/math.js';
10
+ export { Captions, Insert, Stamp, Track } from './film/overlays.js';
11
+ export { IframePicture } from './film/picture.js';
12
+ export { Plate } from './film/plate.js';
13
+ export { Burst, Menu, Player } from './film/player.js';
14
+ export { Rail } from './film/rail.js';
15
+ export { TICK, beatStart, chapterHeads, contents, cues, joinInto, schedule, secsBefore, tailFor, totalSecs, waypoints } from './film/schedule.js';
16
+ export { Seam } from './film/seam.js';
17
+ export { EndCard, Gate } from './film/titles.js';
18
+ //# sourceMappingURL=film.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"film.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}