@arbidocs/blocks 0.4.106 → 0.4.108

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 (67) hide show
  1. package/bin/arbidocs-render-take.mjs +27 -0
  2. package/dist/NativeDocxEditor-UAZUKZ77.js +10 -0
  3. package/dist/NativeDocxEditor-UAZUKZ77.js.map +1 -0
  4. package/dist/NativeDocxEditor-YX7B7OFH.cjs +31 -0
  5. package/dist/NativeDocxEditor-YX7B7OFH.cjs.map +1 -0
  6. package/dist/{chunk-W2RTZOK3.js → chunk-A2MN2ZLX.js} +1750 -1779
  7. package/dist/chunk-A2MN2ZLX.js.map +1 -0
  8. package/dist/{chunk-3R6XBYL7.cjs → chunk-DTBSGG36.cjs} +7 -2
  9. package/dist/chunk-DTBSGG36.cjs.map +1 -0
  10. package/dist/{chunk-4GFB5U3K.js → chunk-EGDWNEAG.js} +4 -3
  11. package/dist/chunk-EGDWNEAG.js.map +1 -0
  12. package/dist/chunk-GYAUJ4KT.js +71 -0
  13. package/dist/chunk-GYAUJ4KT.js.map +1 -0
  14. package/dist/{chunk-PIT2YZCK.js → chunk-JQVPGMLU.js} +3 -3
  15. package/dist/{chunk-PIT2YZCK.js.map → chunk-JQVPGMLU.js.map} +1 -1
  16. package/dist/{chunk-LGX6UFYG.js → chunk-P2KPDPFF.js} +8 -3
  17. package/dist/chunk-P2KPDPFF.js.map +1 -0
  18. package/dist/{chunk-ZWWN4CUL.cjs → chunk-QT6EGMAJ.cjs} +1748 -1778
  19. package/dist/chunk-QT6EGMAJ.cjs.map +1 -0
  20. package/dist/chunk-S24XEVPS.cjs +73 -0
  21. package/dist/chunk-S24XEVPS.cjs.map +1 -0
  22. package/dist/{chunk-TP3FAP5J.cjs → chunk-TTXK62YI.cjs} +5 -5
  23. package/dist/{chunk-TP3FAP5J.cjs.map → chunk-TTXK62YI.cjs.map} +1 -1
  24. package/dist/{chunk-7LLLQUM7.cjs → chunk-ZYSY4L5L.cjs} +6 -5
  25. package/dist/chunk-ZYSY4L5L.cjs.map +1 -0
  26. package/dist/collaboration/index.cjs +15 -15
  27. package/dist/collaboration/index.js +1 -1
  28. package/dist/collaboration/mail/MailMessageView.d.ts +3 -1
  29. package/dist/connect/docx.cjs +7 -6
  30. package/dist/connect/docx.cjs.map +1 -1
  31. package/dist/connect/docx.js +5 -4
  32. package/dist/connect/docx.js.map +1 -1
  33. package/dist/connect/index.cjs +368 -52
  34. package/dist/connect/index.cjs.map +1 -1
  35. package/dist/connect/index.js +356 -41
  36. package/dist/connect/index.js.map +1 -1
  37. package/dist/connection/mail/ConnectedMailComposer.d.ts +12 -2
  38. package/dist/connection/mail/ConnectedMailMessage.d.ts +19 -0
  39. package/dist/connection/mail/ConnectedMailbox.d.ts +7 -3
  40. package/dist/connection/mail/MailComposerForm.d.ts +12 -2
  41. package/dist/connection/mail/index.d.ts +2 -1
  42. package/dist/connection/mail/types.d.ts +15 -0
  43. package/dist/connection/mail/useWordBody.d.ts +48 -0
  44. package/dist/docx/NativeDocxEditor.d.ts +9 -1
  45. package/dist/docx/index.cjs +51 -50
  46. package/dist/docx/index.cjs.map +1 -1
  47. package/dist/docx/index.js +4 -3
  48. package/dist/docx/index.js.map +1 -1
  49. package/dist/docx/template/HouseTemplate.d.ts +5 -0
  50. package/dist/index.cjs +15 -15
  51. package/dist/index.js +1 -1
  52. package/dist/record/index.cjs +307 -66
  53. package/dist/record/index.cjs.map +1 -1
  54. package/dist/record/index.d.ts +145 -26
  55. package/dist/record/index.js +301 -67
  56. package/dist/record/index.js.map +1 -1
  57. package/node_modules/@arbidocs/coedit/package.json +1 -1
  58. package/package.json +8 -5
  59. package/record/render-overlays.mjs +152 -0
  60. package/record/render-take.py +985 -0
  61. package/src/docx/styles.css +22 -0
  62. package/dist/chunk-3R6XBYL7.cjs.map +0 -1
  63. package/dist/chunk-4GFB5U3K.js.map +0 -1
  64. package/dist/chunk-7LLLQUM7.cjs.map +0 -1
  65. package/dist/chunk-LGX6UFYG.js.map +0 -1
  66. package/dist/chunk-W2RTZOK3.js.map +0 -1
  67. package/dist/chunk-ZWWN4CUL.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ export { BlocksProvider, useBlocksEnvironment } from './chunk-76JDDMSP.js';
46
46
  export { daysUntil, displayName, fmtDate, fmtDateTime, formatCount, formatCurrency, formatPageRefs, fromNow, hrs, initials, pct, pluralize } from './chunk-OXF2IFXG.js';
47
47
  export { ActionMenu, AddContactsForm, AddContactsResults, ContactCard, DEFAULT_ADD_CONTACTS_LABELS, DEFAULT_CONTACT_CARD_LABELS, DEFAULT_CONTACT_OUTCOME_LABELS, DEFAULT_DM_ACCESS_REQUEST_BANNER_LABELS, DEFAULT_DM_GRANT_ACCESS_DIALOG_LABELS, DEFAULT_DM_INVITE_CHIP_LABELS, DEFAULT_DM_THREAD_FRAME_LABELS, DEFAULT_MAIL_FOLDER_TREE_LABELS, DEFAULT_RECIPIENT_PICKER_LABELS, DmAccessRequestBanner, DmGrantAccessDialogView, DmInviteAvatar, DmInviteChip, DmNewTabIcon, DmThreadFrame, DmThreadHeader, MAX_INVITE_EMAILS, MailFolderTree, MemberBulkActions, MemberListLoading, MemberMutedText, MemberNameCell, MemberRoleCell, RecipientPicker, RoleSelect, WorkspaceDeleteButton, buildMailFolderTree, contactToVCard, descendantFolderIds, downloadVCard, isInboxFolder, parseEmails, toAddContactsRow, toAddContactsRows, toggleMailFolder, vCardFilename } from './chunk-7C35WXYB.js';
48
48
  import './chunk-NBV56SGU.js';
49
- export { MailDeliveryBadge, MailDeliveryList, MailDocumentPicker, MailGrid, MailMessagePreview, MailMessageView, NEEDS_CONNECTION, PlainMailBodyEditor, draftMailCompose, emptyMailCompose, hasCalendarPart, mailAddressLabel, parseMailRecipients, replyMailCompose } from './chunk-LGX6UFYG.js';
49
+ export { MailDeliveryBadge, MailDeliveryList, MailDocumentPicker, MailGrid, MailMessagePreview, MailMessageView, NEEDS_CONNECTION, PlainMailBodyEditor, draftMailCompose, emptyMailCompose, hasCalendarPart, mailAddressLabel, parseMailRecipients, replyMailCompose } from './chunk-P2KPDPFF.js';
50
50
  export { DEFAULT_SHARE_CONTROL_LABELS, ShareControl } from './chunk-K4BGOPGG.js';
51
51
  import './chunk-TX2ZICS7.js';
52
52
  import './chunk-ZPLG7QYF.js';
@@ -1,6 +1,141 @@
1
1
  'use strict';
2
2
 
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var path__default = /*#__PURE__*/_interopDefault(path);
9
+
3
10
  // src/record/index.ts
11
+ var TIMECODE_CELL = 8;
12
+ var round = (n) => Math.round(n * 100) / 100;
13
+ var Take = class _Take {
14
+ constructor(page, options, brand) {
15
+ this.page = page;
16
+ this.options = options;
17
+ this.brand = brand ? cinematicSkin(brand) : {};
18
+ const dir = path__default.default.resolve(options.dir ?? "recordings");
19
+ this.file = path__default.default.join(dir, `${options.name}.waits.json`);
20
+ this.focusFile = path__default.default.join(dir, `${options.name}.focus.json`);
21
+ this.timeline.viewport = page.viewportSize();
22
+ fs.mkdirSync(dir, { recursive: true });
23
+ _Take.active = this;
24
+ this.save();
25
+ }
26
+ page;
27
+ options;
28
+ /** The take running now (one at a time: recordings run one after another). */
29
+ static active = null;
30
+ t0 = Date.now();
31
+ waits = [];
32
+ /** The brand the edit paints the cards, captions and callouts in. */
33
+ brand;
34
+ file;
35
+ focusFile;
36
+ timeline = {
37
+ sync: null,
38
+ viewport: null,
39
+ focus: [],
40
+ cards: [],
41
+ lowerThirds: [],
42
+ captions: []
43
+ };
44
+ /** The take running on `page`, if any. */
45
+ static on(page) {
46
+ return _Take.active?.page === page ? _Take.active : null;
47
+ }
48
+ /** Seconds since the take started. */
49
+ now() {
50
+ return (Date.now() - this.t0) / 1e3;
51
+ }
52
+ /** Log a wait (written at once, so a take that fails part-way still has its cut list). */
53
+ log(label, start, end = this.now()) {
54
+ if (end - start < 1) return;
55
+ this.waits.push({ label, start: round(start), end: round(end) });
56
+ this.save();
57
+ }
58
+ /** Wait for `until` (a UI state), logged as a wait the edit may cut. */
59
+ async wait(label, until) {
60
+ const start = this.now();
61
+ try {
62
+ return await until();
63
+ } finally {
64
+ this.log(label, start);
65
+ }
66
+ }
67
+ /**
68
+ * Paint the sync marker: a magenta frame for a moment, at a logged time. The edit finds it in the
69
+ * video and lines the take's clock up with the video's. Call it before the first navigation.
70
+ */
71
+ async markSync() {
72
+ await this.page.evaluate(
73
+ () => new Promise((done) => {
74
+ document.documentElement.style.background = "#ff00ff";
75
+ if (document.body) document.body.style.background = "#ff00ff";
76
+ requestAnimationFrame(() => requestAnimationFrame(() => done()));
77
+ })
78
+ );
79
+ this.timeline.sync = round(this.now());
80
+ await this.page.waitForTimeout(400);
81
+ this.save();
82
+ }
83
+ /** Log a focus event (a `point` with no end lasts until the next event). */
84
+ record(event) {
85
+ const { focus } = this.timeline;
86
+ const open = focus[focus.length - 1];
87
+ if (open && open.kind === "point" && open.end === void 0) open.end = round(event.start);
88
+ focus.push({
89
+ ...event,
90
+ start: round(event.start),
91
+ end: event.end === void 0 ? void 0 : round(event.end)
92
+ });
93
+ this.save();
94
+ }
95
+ /** @internal The stage's cards, lower-thirds and captions (drawn by the edit). */
96
+ cardStart(card) {
97
+ this.cardEnd();
98
+ this.captionEnd();
99
+ this.timeline.cards.push({ ...card, start: round(this.now()), end: null });
100
+ this.save();
101
+ }
102
+ cardEnd() {
103
+ const last = this.timeline.cards[this.timeline.cards.length - 1];
104
+ if (last && last.end === null) {
105
+ last.end = round(this.now());
106
+ this.save();
107
+ }
108
+ }
109
+ lowerThird(kicker, title, holdMs) {
110
+ const start = round(this.now());
111
+ this.timeline.lowerThirds.push({ kicker, title, start, end: round(start + holdMs / 1e3) });
112
+ this.save();
113
+ }
114
+ captionStart(text) {
115
+ this.captionEnd();
116
+ this.timeline.captions.push({ text, start: round(this.now()), end: null });
117
+ this.save();
118
+ }
119
+ captionEnd() {
120
+ const last = this.timeline.captions[this.timeline.captions.length - 1];
121
+ if (last && last.end === null) {
122
+ last.end = round(this.now());
123
+ this.save();
124
+ }
125
+ }
126
+ save() {
127
+ const { zoom, maxZoom, pip } = this.options;
128
+ fs.writeFileSync(this.file, JSON.stringify({ t0: this.t0, waits: this.waits }, null, 1));
129
+ fs.writeFileSync(
130
+ this.focusFile,
131
+ JSON.stringify(
132
+ { t0: this.t0, config: { zoom, maxZoom, pip }, brand: this.brand, ...this.timeline },
133
+ null,
134
+ 1
135
+ )
136
+ );
137
+ }
138
+ };
4
139
  var DEFAULTS = {
5
140
  displayFont: "ui-serif, Georgia, serif",
6
141
  sansFont: "Inter, ui-sans-serif, system-ui, sans-serif",
@@ -11,9 +146,8 @@ var DEFAULTS = {
11
146
  ink: "#0e1b2d",
12
147
  coverTitle: "#f4f6f8"
13
148
  };
14
- function createCinematic(config) {
15
- const b = config.brand;
16
- const skin = {
149
+ function cinematicSkin(b) {
150
+ return {
17
151
  firmLabel: b.firmLabel,
18
152
  displayFont: b.displayFont ?? DEFAULTS.displayFont,
19
153
  sansFont: b.sansFont ?? DEFAULTS.sansFont,
@@ -25,8 +159,13 @@ function createCinematic(config) {
25
159
  coverTo: b.cover?.to ?? DEFAULTS.cover.to,
26
160
  accent: b.accent ?? DEFAULTS.accent,
27
161
  ink: b.ink ?? DEFAULTS.ink,
28
- coverTitle: b.coverTitle ?? DEFAULTS.coverTitle
162
+ coverTitle: b.coverTitle ?? DEFAULTS.coverTitle,
163
+ fontFiles: b.fontFiles ?? []
29
164
  };
165
+ }
166
+ function createCinematic(config) {
167
+ const b = config.brand;
168
+ const skin = cinematicSkin(b);
30
169
  const loginPath = config.loginPath ?? "/app/login";
31
170
  const appUrlPattern = config.appUrlPattern ?? /\/app$/;
32
171
  const ids = {
@@ -35,113 +174,207 @@ function createCinematic(config) {
35
174
  submit: config.testIds?.submit ?? "auth-login-submit"
36
175
  };
37
176
  const resolve = (page, t) => typeof t === "string" ? page.getByTestId(t) : t;
177
+ async function boxOf(page, loc) {
178
+ const box = await loc.boundingBox().catch(() => null);
179
+ const vp = page.viewportSize();
180
+ if (!box || !vp) return box;
181
+ const x = Math.max(box.x, 0);
182
+ const y = Math.max(box.y, 0);
183
+ const width = Math.min(box.x + box.width, vp.width) - x;
184
+ const height = Math.min(box.y + box.height, vp.height) - y;
185
+ return width > 0 && height > 0 ? { x: round(x), y: round(y), width: round(width), height: round(height) } : null;
186
+ }
187
+ async function glide(page, target) {
188
+ const loc = resolve(page, target).first();
189
+ await loc.scrollIntoViewIfNeeded().catch(() => {
190
+ });
191
+ const box = await boxOf(page, loc);
192
+ if (box) {
193
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 26 });
194
+ await page.waitForTimeout(180);
195
+ }
196
+ return { loc, box };
197
+ }
198
+ async function take(page, options) {
199
+ return new Take(page, options, b);
200
+ }
38
201
  async function installStage(page) {
39
- await page.addInitScript((s) => {
40
- const boot = () => {
41
- if (document.getElementById("__cine_cursor")) return;
42
- const cursor = document.createElement("div");
43
- cursor.id = "__cine_cursor";
44
- cursor.style.cssText = `position:fixed;left:0;top:0;z-index:2147483647;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;pointer-events:none;transition:transform .09s ease-out;background:radial-gradient(circle at 35% 35%, ${s.cursorFrom}, ${s.cursorTo});box-shadow:0 0 0 3px rgba(255,255,255,.6),0 2px 10px rgba(0,0,0,.4);`;
45
- document.body.appendChild(cursor);
46
- window.addEventListener(
47
- "mousemove",
48
- (e) => {
49
- cursor.style.transform = `translate(${e.clientX}px,${e.clientY}px)`;
50
- },
51
- true
52
- );
53
- window.addEventListener(
54
- "mousedown",
55
- (e) => {
56
- const m = e;
57
- const r = document.createElement("div");
58
- r.style.cssText = `position:fixed;left:${m.clientX}px;top:${m.clientY}px;z-index:2147483646;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;pointer-events:none;border:2px solid ${s.ripple};animation:__cine_ripple .55s ease-out forwards;`;
59
- document.body.appendChild(r);
60
- setTimeout(() => r.remove(), 600);
61
- },
62
- true
63
- );
64
- const cap = document.createElement("div");
65
- cap.id = "__cine_cap";
66
- cap.style.cssText = `position:fixed;left:50%;bottom:34px;transform:translateX(-50%) translateY(8px);z-index:2147483646;max-width:72%;padding:13px 24px;border-radius:13px;font:500 17px/1.45 ${s.sansFont};color:#fff;letter-spacing:.01em;background:${s.ink}e0;backdrop-filter:blur(7px);box-shadow:0 10px 34px rgba(0,0,0,.4);text-align:center;opacity:0;transition:opacity .35s,transform .35s;`;
67
- document.body.appendChild(cap);
68
- const style = document.createElement("style");
69
- style.textContent = "@keyframes __cine_ripple{to{width:46px;height:46px;margin:-23px 0 0 -23px;opacity:0}}";
70
- document.head.appendChild(style);
71
- window.__cine_cap = (t) => {
72
- cap.textContent = t;
73
- cap.style.opacity = t ? "1" : "0";
74
- cap.style.transform = `translateX(-50%) translateY(${t ? 0 : 8}px)`;
202
+ await page.addInitScript(
203
+ ({ s, t0, TC }) => {
204
+ const boot = () => {
205
+ if (document.getElementById("__cine_cursor")) return;
206
+ const cursor = document.createElement("div");
207
+ cursor.id = "__cine_cursor";
208
+ cursor.style.cssText = `position:fixed;left:0;top:0;z-index:2147483647;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;pointer-events:none;transition:transform .09s ease-out;background:radial-gradient(circle at 35% 35%, ${s.cursorFrom}, ${s.cursorTo});box-shadow:0 0 0 3px rgba(255,255,255,.6),0 2px 10px rgba(0,0,0,.4);`;
209
+ document.body.appendChild(cursor);
210
+ window.addEventListener(
211
+ "mousemove",
212
+ (e) => {
213
+ cursor.style.transform = `translate(${e.clientX}px,${e.clientY}px)`;
214
+ },
215
+ true
216
+ );
217
+ window.addEventListener(
218
+ "mousedown",
219
+ (e) => {
220
+ const m = e;
221
+ const r = document.createElement("div");
222
+ r.style.cssText = `position:fixed;left:${m.clientX}px;top:${m.clientY}px;z-index:2147483646;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;pointer-events:none;border:2px solid ${s.ripple};animation:__cine_ripple .55s ease-out forwards;`;
223
+ document.body.appendChild(r);
224
+ setTimeout(() => r.remove(), 600);
225
+ },
226
+ true
227
+ );
228
+ const cap = document.createElement("div");
229
+ cap.id = "__cine_cap";
230
+ cap.style.cssText = `position:fixed;left:50%;bottom:34px;transform:translateX(-50%) translateY(8px);z-index:2147483646;max-width:72%;padding:13px 24px;border-radius:13px;font:500 17px/1.45 ${s.sansFont};color:#fff;letter-spacing:.01em;background:${s.ink}e0;backdrop-filter:blur(7px);box-shadow:0 10px 34px rgba(0,0,0,.4);text-align:center;opacity:0;transition:opacity .35s,transform .35s;`;
231
+ document.body.appendChild(cap);
232
+ const style = document.createElement("style");
233
+ style.textContent = "@keyframes __cine_ripple{to{width:46px;height:46px;margin:-23px 0 0 -23px;opacity:0}}";
234
+ document.head.appendChild(style);
235
+ window.__cine_cap = (t) => {
236
+ cap.textContent = t;
237
+ cap.style.opacity = t ? "1" : "0";
238
+ cap.style.transform = `translateX(-50%) translateY(${t ? 0 : 8}px)`;
239
+ };
240
+ if (t0 !== null) {
241
+ const strip = document.createElement("div");
242
+ strip.id = "__cine_timecode";
243
+ strip.style.cssText = `position:fixed;right:0;bottom:0;z-index:2147483647;display:flex;pointer-events:none;height:${TC}px`;
244
+ const cells = Array.from({ length: 18 }, (_, i) => {
245
+ const cell = document.createElement("div");
246
+ cell.style.cssText = `width:${TC}px;height:${TC}px;background:${i === 0 ? "#fff" : "#000"}`;
247
+ strip.appendChild(cell);
248
+ return cell;
249
+ });
250
+ document.body.appendChild(strip);
251
+ const tick = () => {
252
+ const ds = Math.max(0, Math.round((Date.now() - t0) / 100)) & 65535;
253
+ for (let bit = 0; bit < 16; bit += 1)
254
+ cells[bit + 2].style.background = ds >> 15 - bit & 1 ? "#fff" : "#000";
255
+ requestAnimationFrame(tick);
256
+ };
257
+ tick();
258
+ }
75
259
  };
76
- };
77
- if (document.body) boot();
78
- else document.addEventListener("DOMContentLoaded", boot);
79
- }, skin);
260
+ if (document.body) boot();
261
+ else document.addEventListener("DOMContentLoaded", boot);
262
+ },
263
+ { s: skin, t0: Take.on(page)?.t0 ?? null, TC: TIMECODE_CELL }
264
+ );
80
265
  }
81
266
  async function cover(page, { kicker, title, subtitle }, holdMs = 2600) {
267
+ const running = Take.on(page);
268
+ running?.cardStart({ kicker, title, subtitle });
82
269
  await page.evaluate(
83
- ({ kicker: kicker2, title: title2, subtitle: subtitle2, s }) => {
270
+ ({ kicker: kicker2, title: title2, subtitle: subtitle2, s, instant }) => {
84
271
  document.getElementById("__cine_cover")?.remove();
85
272
  const el = document.createElement("div");
86
273
  el.id = "__cine_cover";
87
- el.style.cssText = `position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:0 8%;background:linear-gradient(135deg,${s.coverFrom} 0%,${s.coverMid} 48%,${s.coverTo} 165%);opacity:0;transition:opacity .5s ease;font-family:${s.sansFont};`;
274
+ el.style.cssText = `position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:0 8%;background:linear-gradient(135deg,${s.coverFrom} 0%,${s.coverMid} 48%,${s.coverTo} 165%);opacity:${instant ? 1 : 0};transition:opacity ${instant ? 0 : 0.5}s ease;font-family:${s.sansFont};`;
88
275
  el.innerHTML = `<div style="text-transform:uppercase;letter-spacing:.26em;font-size:13px;font-weight:600;color:${s.accent};margin-bottom:22px">${kicker2}</div><div style="font-family:${s.displayFont};font-weight:600;font-size:52px;line-height:1.08;letter-spacing:-.02em;color:${s.coverTitle};max-width:16ch">${title2}</div>` + (subtitle2 ? `<div style="margin-top:22px;font-size:19px;line-height:1.5;color:${s.coverTitle}b8;max-width:46ch">${subtitle2}</div>` : "") + `<div style="margin-top:44px;display:flex;align-items:center;gap:10px;color:${s.coverTitle}80;font-size:13px;letter-spacing:.02em"><span style="width:26px;height:1px;background:${s.accent}"></span>${s.firmLabel}</div>`;
89
276
  document.body.appendChild(el);
90
277
  requestAnimationFrame(() => el.style.opacity = "1");
91
278
  },
92
- { kicker, title, subtitle: subtitle ?? "", s: skin }
279
+ { kicker, title, subtitle: subtitle ?? "", s: skin, instant: !!running }
93
280
  );
94
281
  await page.waitForTimeout(holdMs);
95
282
  }
96
- async function uncover(page) {
283
+ async function chapter(page, card, holdMs) {
284
+ await cover(page, card, holdMs);
97
285
  await page.evaluate(() => {
286
+ const el = document.getElementById("__cine_cover");
287
+ if (el) el.style.pointerEvents = "none";
288
+ });
289
+ }
290
+ async function uncover(page) {
291
+ const running = Take.on(page);
292
+ await page.evaluate((instant) => {
98
293
  const el = document.getElementById("__cine_cover");
99
294
  if (!el) return;
295
+ if (instant) return el.remove();
100
296
  el.style.opacity = "0";
101
297
  setTimeout(() => el.remove(), 520);
102
- });
298
+ }, !!running);
299
+ running?.cardEnd();
103
300
  await page.waitForTimeout(560);
104
301
  }
302
+ function lowerThird(page, kicker, title, holdMs = 3200) {
303
+ Take.on(page)?.lowerThird(kicker, title, holdMs);
304
+ }
105
305
  async function caption(page, text, holdMs) {
106
- await page.evaluate(
107
- (t) => window.__cine_cap?.(t),
108
- text
109
- );
306
+ const running = Take.on(page);
307
+ if (running) running.captionStart(text);
308
+ else
309
+ await page.evaluate(
310
+ (t) => window.__cine_cap?.(t),
311
+ text
312
+ );
110
313
  const hold = holdMs ?? Math.min(4200, 900 + text.length * 45);
111
314
  await page.waitForTimeout(hold);
112
315
  }
113
316
  async function clearCaption(page) {
114
- await page.evaluate(
115
- () => window.__cine_cap?.("")
116
- );
317
+ const running = Take.on(page);
318
+ if (running) running.captionEnd();
319
+ else
320
+ await page.evaluate(
321
+ () => window.__cine_cap?.("")
322
+ );
117
323
  }
118
324
  async function beat(page, ms = 650) {
119
325
  await page.waitForTimeout(ms);
120
326
  }
121
327
  async function glideTo(page, target) {
122
- const loc = resolve(page, target).first();
123
- await loc.scrollIntoViewIfNeeded().catch(() => {
124
- });
125
- const box = await loc.boundingBox();
126
- if (box) {
127
- await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 26 });
128
- await page.waitForTimeout(180);
129
- }
328
+ const running = Take.on(page);
329
+ const start = running?.now() ?? 0;
330
+ const { loc, box } = await glide(page, target);
331
+ if (running && box) running.record({ kind: "point", start, box });
130
332
  return loc;
131
333
  }
132
334
  async function click(page, target) {
133
- const loc = await glideTo(page, target);
335
+ const running = Take.on(page);
336
+ const start = running?.now() ?? 0;
337
+ const { loc, box } = await glide(page, target);
134
338
  await loc.click();
135
339
  await page.waitForTimeout(260);
340
+ if (running && box) running.record({ kind: "click", start, end: running.now(), box });
136
341
  }
137
342
  async function type(page, target, text) {
138
- const loc = await glideTo(page, target);
343
+ const running = Take.on(page);
344
+ const start = running?.now() ?? 0;
345
+ const { loc, box } = await glide(page, target);
139
346
  await loc.click();
140
347
  await loc.fill("");
141
348
  await loc.pressSequentially(text, { delay: 42 });
142
349
  await page.waitForTimeout(240);
350
+ if (running && box) running.record({ kind: "type", start, end: running.now(), box });
351
+ }
352
+ async function focusOn(page, target, label, {
353
+ holdMs = 2200,
354
+ callout = true,
355
+ mode
356
+ } = {}) {
357
+ const running = Take.on(page);
358
+ const start = running?.now() ?? 0;
359
+ const { box } = await glide(page, target);
360
+ await page.waitForTimeout(holdMs);
361
+ if (running && box)
362
+ running.record({ kind: "focus", label, start, end: running.now(), box, callout, mode });
363
+ }
364
+ async function readDown(page, target, scrolls = 3, pauseMs = 1600) {
365
+ const running = Take.on(page);
366
+ const start = running?.now() ?? 0;
367
+ const { box } = await glide(page, target);
368
+ for (let i = 0; i < scrolls; i += 1) {
369
+ await page.mouse.wheel(0, 320);
370
+ await beat(page, pauseMs);
371
+ }
372
+ await page.mouse.wheel(0, -320 * scrolls);
373
+ await beat(page, 500);
374
+ if (running && box) running.record({ kind: "read", start, end: running.now(), box });
143
375
  }
144
376
  async function login(page, opts = {}) {
377
+ if (!config.creds) throw new Error("createCinematic: pass creds to sign in");
145
378
  await page.goto(loginPath);
146
379
  if (opts.show === false) {
147
380
  await page.getByTestId(ids.email).fill(config.creds.email);
@@ -158,19 +391,27 @@ function createCinematic(config) {
158
391
  await beat(page, 800);
159
392
  }
160
393
  return {
394
+ take,
161
395
  installStage,
162
396
  cover,
397
+ chapter,
163
398
  uncover,
399
+ lowerThird,
164
400
  caption,
165
401
  clearCaption,
166
402
  beat,
167
403
  glideTo,
168
404
  click,
169
405
  type,
406
+ focusOn,
407
+ readDown,
170
408
  login
171
409
  };
172
410
  }
173
411
 
412
+ exports.TIMECODE_CELL = TIMECODE_CELL;
413
+ exports.Take = Take;
414
+ exports.cinematicSkin = cinematicSkin;
174
415
  exports.createCinematic = createCinematic;
175
416
  //# sourceMappingURL=index.cjs.map
176
417
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/record/index.ts"],"names":["kicker","title","subtitle"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Cinematic recording harness — host-agnostic.\n *\n * Turns a Playwright run into a narrated product demo: a visible cursor that\n * glides between targets, a click ripple, a caption bar that narrates each step,\n * full-screen chapter \"cover\" cards, and human-speed pacing. Every host app gets\n * the same polished demo capability from one factory — brand colours, fonts,\n * credentials and the login flow arrive as config, so nothing here names a host.\n *\n * Pair with a Playwright project that records video (see the host's\n * `playwright.record.config.ts`: video on, a large viewport, some slowMo).\n *\n * Usage in a `*.record.ts` spec:\n * const cine = createCinematic({ brand, creds })\n * await cine.installStage(page) // before the first navigation\n * await cine.cover(page, { kicker, title, subtitle })\n * await cine.login(page)\n * await cine.uncover(page)\n * await cine.caption(page, \"Opening the flagship matter…\")\n * await cine.click(page, \"matters-row-…\")\n *\n * `@playwright/test` is an optional peer used for TYPES only (no runtime import), so\n * importing this entry never loads Playwright.\n */\nimport type { Page, Locator } from '@playwright/test'\n\n/** Brand skin for the on-screen chrome. All optional — sensible neutral defaults. */\nexport interface CinematicBrand {\n /** Footer line on chapter cards, e.g. \"Meridian · AI-native counsel\". */\n firmLabel: string\n /** Serif display font for cover titles (CSS font-family value). */\n displayFont?: string\n /** Sans font for captions + cover body. */\n sansFont?: string\n /** Cursor dot radial-gradient stops. */\n cursor?: { from: string; to: string }\n /** Click-ripple ring colour. */\n ripple?: string\n /** Chapter-card background gradient stops. */\n cover?: { from: string; mid: string; to: string }\n /** Accent (kicker text + rule) on chapter cards. */\n accent?: string\n /** Caption bar background base colour. */\n ink?: string\n /** Cover title text colour. */\n coverTitle?: string\n}\n\nexport interface CinematicCreds {\n email: string\n password: string\n}\n\nexport interface CinematicConfig {\n brand: CinematicBrand\n creds: CinematicCreds\n /** Login route (default \"/app/login\"). */\n loginPath?: string\n /** URL the app lands on after auth (default /\\/app$/). */\n appUrlPattern?: RegExp\n /** Login form testids (defaults match the shared AppShell/login convention). */\n testIds?: { email?: string; password?: string; submit?: string }\n}\n\nexport interface Cinematic {\n installStage(page: Page): Promise<void>\n cover(\n page: Page,\n opts: { kicker: string; title: string; subtitle?: string },\n holdMs?: number\n ): Promise<void>\n uncover(page: Page): Promise<void>\n caption(page: Page, text: string, holdMs?: number): Promise<void>\n clearCaption(page: Page): Promise<void>\n beat(page: Page, ms?: number): Promise<void>\n glideTo(page: Page, target: string | Locator): Promise<Locator>\n click(page: Page, target: string | Locator): Promise<void>\n type(page: Page, target: string | Locator, text: string): Promise<void>\n /** Sign in. `{ show: false }` authenticates fast + silent (no animation/caption). */\n login(page: Page, opts?: { show?: boolean; caption?: string }): Promise<void>\n}\n\nconst DEFAULTS = {\n displayFont: 'ui-serif, Georgia, serif',\n sansFont: 'Inter, ui-sans-serif, system-ui, sans-serif',\n cursor: { from: 'rgba(31,111,235,.98)', to: 'rgba(11,42,74,.92)' },\n ripple: 'rgba(11,42,74,.85)',\n cover: { from: '#0b1622', mid: '#0e1b2c', to: '#1f6feb' },\n accent: '#c9a24b',\n ink: '#0e1b2d',\n coverTitle: '#f4f6f8',\n}\n\nexport function createCinematic(config: CinematicConfig): Cinematic {\n const b = config.brand\n const skin = {\n firmLabel: b.firmLabel,\n displayFont: b.displayFont ?? DEFAULTS.displayFont,\n sansFont: b.sansFont ?? DEFAULTS.sansFont,\n cursorFrom: b.cursor?.from ?? DEFAULTS.cursor.from,\n cursorTo: b.cursor?.to ?? DEFAULTS.cursor.to,\n ripple: b.ripple ?? DEFAULTS.ripple,\n coverFrom: b.cover?.from ?? DEFAULTS.cover.from,\n coverMid: b.cover?.mid ?? DEFAULTS.cover.mid,\n coverTo: b.cover?.to ?? DEFAULTS.cover.to,\n accent: b.accent ?? DEFAULTS.accent,\n ink: b.ink ?? DEFAULTS.ink,\n coverTitle: b.coverTitle ?? DEFAULTS.coverTitle,\n }\n const loginPath = config.loginPath ?? '/app/login'\n const appUrlPattern = config.appUrlPattern ?? /\\/app$/\n const ids = {\n email: config.testIds?.email ?? 'auth-email',\n password: config.testIds?.password ?? 'auth-password',\n submit: config.testIds?.submit ?? 'auth-login-submit',\n }\n\n const resolve = (page: Page, t: string | Locator): Locator =>\n typeof t === 'string' ? page.getByTestId(t) : t\n\n async function installStage(page: Page): Promise<void> {\n await page.addInitScript((s: typeof skin) => {\n const boot = () => {\n if (document.getElementById('__cine_cursor')) return\n\n const cursor = document.createElement('div')\n cursor.id = '__cine_cursor'\n cursor.style.cssText =\n 'position:fixed;left:0;top:0;z-index:2147483647;width:22px;height:22px;' +\n 'margin:-11px 0 0 -11px;border-radius:50%;pointer-events:none;' +\n 'transition:transform .09s ease-out;' +\n `background:radial-gradient(circle at 35% 35%, ${s.cursorFrom}, ${s.cursorTo});` +\n 'box-shadow:0 0 0 3px rgba(255,255,255,.6),0 2px 10px rgba(0,0,0,.4);'\n document.body.appendChild(cursor)\n\n window.addEventListener(\n 'mousemove',\n (e) => {\n cursor.style.transform = `translate(${(e as MouseEvent).clientX}px,${(e as MouseEvent).clientY}px)`\n },\n true\n )\n window.addEventListener(\n 'mousedown',\n (e) => {\n const m = e as MouseEvent\n const r = document.createElement('div')\n r.style.cssText =\n `position:fixed;left:${m.clientX}px;top:${m.clientY}px;z-index:2147483646;` +\n 'width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;pointer-events:none;' +\n `border:2px solid ${s.ripple};animation:__cine_ripple .55s ease-out forwards;`\n document.body.appendChild(r)\n setTimeout(() => r.remove(), 600)\n },\n true\n )\n\n const cap = document.createElement('div')\n cap.id = '__cine_cap'\n cap.style.cssText =\n 'position:fixed;left:50%;bottom:34px;transform:translateX(-50%) translateY(8px);' +\n 'z-index:2147483646;max-width:72%;padding:13px 24px;border-radius:13px;' +\n `font:500 17px/1.45 ${s.sansFont};color:#fff;letter-spacing:.01em;` +\n `background:${s.ink}e0;backdrop-filter:blur(7px);` +\n 'box-shadow:0 10px 34px rgba(0,0,0,.4);text-align:center;opacity:0;transition:opacity .35s,transform .35s;'\n document.body.appendChild(cap)\n\n const style = document.createElement('style')\n style.textContent =\n '@keyframes __cine_ripple{to{width:46px;height:46px;margin:-23px 0 0 -23px;opacity:0}}'\n document.head.appendChild(style)\n\n ;(window as unknown as { __cine_cap: (t: string) => void }).__cine_cap = (t: string) => {\n cap.textContent = t\n cap.style.opacity = t ? '1' : '0'\n cap.style.transform = `translateX(-50%) translateY(${t ? 0 : 8}px)`\n }\n }\n if (document.body) boot()\n else document.addEventListener('DOMContentLoaded', boot)\n }, skin)\n }\n\n async function cover(\n page: Page,\n { kicker, title, subtitle }: { kicker: string; title: string; subtitle?: string },\n holdMs = 2600\n ): Promise<void> {\n await page.evaluate(\n ({ kicker, title, subtitle, s }) => {\n document.getElementById('__cine_cover')?.remove()\n const el = document.createElement('div')\n el.id = '__cine_cover'\n el.style.cssText =\n 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;' +\n 'align-items:center;justify-content:center;text-align:center;padding:0 8%;' +\n `background:linear-gradient(135deg,${s.coverFrom} 0%,${s.coverMid} 48%,${s.coverTo} 165%);` +\n `opacity:0;transition:opacity .5s ease;font-family:${s.sansFont};`\n el.innerHTML =\n `<div style=\"text-transform:uppercase;letter-spacing:.26em;font-size:13px;font-weight:600;color:${s.accent};margin-bottom:22px\">${kicker}</div>` +\n `<div style=\"font-family:${s.displayFont};font-weight:600;font-size:52px;line-height:1.08;letter-spacing:-.02em;color:${s.coverTitle};max-width:16ch\">${title}</div>` +\n (subtitle\n ? `<div style=\"margin-top:22px;font-size:19px;line-height:1.5;color:${s.coverTitle}b8;max-width:46ch\">${subtitle}</div>`\n : '') +\n `<div style=\"margin-top:44px;display:flex;align-items:center;gap:10px;color:${s.coverTitle}80;font-size:13px;letter-spacing:.02em\">` +\n `<span style=\"width:26px;height:1px;background:${s.accent}\"></span>${s.firmLabel}</div>`\n document.body.appendChild(el)\n requestAnimationFrame(() => (el.style.opacity = '1'))\n },\n { kicker, title, subtitle: subtitle ?? '', s: skin }\n )\n await page.waitForTimeout(holdMs)\n }\n\n async function uncover(page: Page): Promise<void> {\n await page.evaluate(() => {\n const el = document.getElementById('__cine_cover')\n if (!el) return\n el.style.opacity = '0'\n setTimeout(() => el.remove(), 520)\n })\n await page.waitForTimeout(560)\n }\n\n async function caption(page: Page, text: string, holdMs?: number): Promise<void> {\n await page.evaluate(\n (t) => (window as unknown as { __cine_cap?: (s: string) => void }).__cine_cap?.(t),\n text\n )\n const hold = holdMs ?? Math.min(4200, 900 + text.length * 45)\n await page.waitForTimeout(hold)\n }\n\n async function clearCaption(page: Page): Promise<void> {\n await page.evaluate(() =>\n (window as unknown as { __cine_cap?: (s: string) => void }).__cine_cap?.('')\n )\n }\n\n async function beat(page: Page, ms = 650): Promise<void> {\n await page.waitForTimeout(ms)\n }\n\n async function glideTo(page: Page, target: string | Locator): Promise<Locator> {\n const loc = resolve(page, target).first()\n await loc.scrollIntoViewIfNeeded().catch(() => {})\n const box = await loc.boundingBox()\n if (box) {\n await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 26 })\n await page.waitForTimeout(180)\n }\n return loc\n }\n\n async function click(page: Page, target: string | Locator): Promise<void> {\n const loc = await glideTo(page, target)\n await loc.click()\n await page.waitForTimeout(260)\n }\n\n async function type(page: Page, target: string | Locator, text: string): Promise<void> {\n const loc = await glideTo(page, target)\n await loc.click()\n await loc.fill('')\n await loc.pressSequentially(text, { delay: 42 })\n await page.waitForTimeout(240)\n }\n\n async function login(page: Page, opts: { show?: boolean; caption?: string } = {}): Promise<void> {\n await page.goto(loginPath)\n if (opts.show === false) {\n await page.getByTestId(ids.email).fill(config.creds.email)\n await page.getByTestId(ids.password).fill(config.creds.password)\n await page.getByTestId(ids.submit).click()\n await page.waitForURL(appUrlPattern, { timeout: 45_000 })\n return\n }\n await caption(page, opts.caption ?? `${b.firmLabel} — signing in.`, 1600)\n await type(page, ids.email, config.creds.email)\n await type(page, ids.password, config.creds.password)\n await click(page, ids.submit)\n await page.waitForURL(appUrlPattern, { timeout: 45_000 })\n await beat(page, 800)\n }\n\n return {\n installStage,\n cover,\n uncover,\n caption,\n clearCaption,\n beat,\n glideTo,\n click,\n type,\n login,\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/record/index.ts"],"names":["path","mkdirSync","writeFileSync","kicker","title","subtitle"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Cinematic recording harness — host-agnostic.\n *\n * Turns a Playwright run into a narrated product demo: a visible cursor that glides between\n * targets, a click ripple, captions, full-screen chapter cards, lower-thirds and human-speed\n * pacing. Every host app gets the same demo capability from one factory: brand colours, fonts,\n * credentials and the login flow arrive as config, so nothing here names a host.\n *\n * With a take running (`cine.take`), the run is also logged for the edit, in seconds from the\n * start of the video:\n * - every wait (the AI working, a file uploading) to `<dir>/<name>.waits.json`, so the edit can\n * cut the middle of each long one;\n * - the focus timeline to `<dir>/<name>.focus.json`: each click, type and read with its target's\n * box on screen, the key moments `focusOn` marks, the cards, lower-thirds and captions, the\n * take's config (zoom, max zoom, picture-in-picture) and the brand.\n * Captions, cards and lower-thirds are then drawn by the edit (brand PNGs over the video, outside\n * any zoom), not in the page, and a timecode strip in the corner lines the two clocks up.\n * `arbidocs-render-take` (this package's bin) renders the cut, zoomed, titled video from them.\n *\n * Usage in a recording script:\n * const cine = createCinematic({ brand, creds })\n * const take = await cine.take(page, { name: 'reply-with-arbi', dir: 'recordings' })\n * await cine.installStage(page) // after the take, before the first navigation\n * await take.markSync()\n * await page.goto('/')\n * await cine.chapter(page, { kicker, title, subtitle })\n * await cine.login(page, { show: false })\n * await cine.uncover(page)\n * await cine.click(page, 'sidebar-mail-button')\n * await take.wait('ARBI drafting', () => expect(reply).toBeVisible())\n * await cine.focusOn(page, reply, 'ARBI’s reply, as tracked changes')\n *\n * `@playwright/test` is an optional peer used for TYPES only (no runtime import), so importing\n * this entry never loads Playwright.\n */\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport path from 'node:path'\nimport type { Page, Locator } from '@playwright/test'\n\n/** Brand skin for the on-screen chrome. All optional — sensible neutral defaults. */\nexport interface CinematicBrand {\n /** Footer line on chapter cards, e.g. \"Meridian · AI-native counsel\". */\n firmLabel: string\n /** Serif display font for cover titles (CSS font-family value). */\n displayFont?: string\n /** Sans font for captions + cover body. */\n sansFont?: string\n /** Cursor dot radial-gradient stops. */\n cursor?: { from: string; to: string }\n /** Click-ripple ring colour. */\n ripple?: string\n /** Chapter-card background gradient stops. */\n cover?: { from: string; mid: string; to: string }\n /** Accent (kicker text + rule) on chapter cards, callouts and lower-thirds. */\n accent?: string\n /** Caption bar background base colour. */\n ink?: string\n /** Cover title text colour. */\n coverTitle?: string\n /**\n * Font files for the edit's overlays (cards, captions), when the display or sans font is not\n * installed where the video is rendered: each a family name and a `file://` or `https://` URL.\n */\n fontFiles?: { family: string; url: string; weight?: string; unicodeRange?: string }[]\n}\n\nexport interface CinematicCreds {\n email: string\n password: string\n}\n\nexport interface CinematicConfig {\n brand: CinematicBrand\n /** For `login`. */\n creds?: CinematicCreds\n /** Login route (default \"/app/login\"). */\n loginPath?: string\n /** URL the app lands on after auth (default /\\/app$/). */\n appUrlPattern?: RegExp\n /** Login form testids (defaults match the shared AppShell/login convention). */\n testIds?: { email?: string; password?: string; submit?: string }\n}\n\n/** A box on screen, in page pixels. */\nexport interface Box {\n x: number\n y: number\n width: number\n height: number\n}\n\n/**\n * A moment the edit may frame: a click, typing, reading down a result, a glide-and-look (`point`,\n * which lasts until the next gesture), or a key moment marked with `focusOn`.\n */\nexport interface FocusEvent {\n kind: 'click' | 'type' | 'read' | 'point' | 'focus'\n label?: string\n start: number\n end?: number\n box: Box\n /** Draw a highlight (and the label as its tag) round the box. */\n callout?: boolean\n /** How to frame it: zoom in, a picture-in-picture inset, or not at all (default: the edit decides). */\n mode?: 'zoom' | 'pip' | 'none'\n}\n\n/** A take's say in its edit. */\nexport interface TakeOptions {\n /** The take's name: its files are `<dir>/<name>.waits.json` and `<dir>/<name>.focus.json`. */\n name: string\n /** Where the take's files go (default `recordings`). */\n dir?: string\n /** Zoom and pan onto the action (default true). */\n zoom?: boolean\n /** At most this much (default 1.8). */\n maxZoom?: number\n /** A picture-in-picture inset for long typing (default true). */\n pip?: boolean\n}\n\nexport interface Wait {\n label: string\n start: number\n end: number\n}\n\ninterface Card {\n kicker: string\n title: string\n subtitle?: string\n start: number\n end: number | null\n}\n\n/** The timecode's square, in page pixels (the renderer reads it at this size). */\nexport const TIMECODE_CELL = 8\n\nconst round = (n: number) => Math.round(n * 100) / 100\n\n/**\n * One recorded take: its clock, its waits and its focus timeline. Start it first thing (the video\n * starts with the page, a moment before; `markSync` lines the two clocks up exactly).\n */\nexport class Take {\n /** The take running now (one at a time: recordings run one after another). */\n private static active: Take | null = null\n\n readonly t0 = Date.now()\n readonly waits: Wait[] = []\n /** The brand the edit paints the cards, captions and callouts in. */\n private readonly brand: object\n readonly file: string\n readonly focusFile: string\n private readonly timeline = {\n sync: null as number | null,\n viewport: null as { width: number; height: number } | null,\n focus: [] as FocusEvent[],\n cards: [] as Card[],\n lowerThirds: [] as { kicker: string; title: string; start: number; end: number }[],\n captions: [] as { text: string; start: number; end: number | null }[],\n }\n\n constructor(\n readonly page: Page,\n private readonly options: TakeOptions,\n brand?: CinematicBrand\n ) {\n this.brand = brand ? cinematicSkin(brand) : {}\n const dir = path.resolve(options.dir ?? 'recordings')\n this.file = path.join(dir, `${options.name}.waits.json`)\n this.focusFile = path.join(dir, `${options.name}.focus.json`)\n this.timeline.viewport = page.viewportSize()\n mkdirSync(dir, { recursive: true })\n Take.active = this\n this.save()\n }\n\n /** The take running on `page`, if any. */\n static on(page: Page): Take | null {\n return Take.active?.page === page ? Take.active : null\n }\n\n /** Seconds since the take started. */\n now(): number {\n return (Date.now() - this.t0) / 1000\n }\n\n /** Log a wait (written at once, so a take that fails part-way still has its cut list). */\n log(label: string, start: number, end = this.now()): void {\n if (end - start < 1) return\n this.waits.push({ label, start: round(start), end: round(end) })\n this.save()\n }\n\n /** Wait for `until` (a UI state), logged as a wait the edit may cut. */\n async wait<T>(label: string, until: () => Promise<T>): Promise<T> {\n const start = this.now()\n try {\n return await until()\n } finally {\n this.log(label, start)\n }\n }\n\n /**\n * Paint the sync marker: a magenta frame for a moment, at a logged time. The edit finds it in the\n * video and lines the take's clock up with the video's. Call it before the first navigation.\n */\n async markSync(): Promise<void> {\n await this.page.evaluate(\n () =>\n new Promise<void>((done) => {\n document.documentElement.style.background = '#ff00ff'\n if (document.body) document.body.style.background = '#ff00ff'\n requestAnimationFrame(() => requestAnimationFrame(() => done()))\n })\n )\n this.timeline.sync = round(this.now())\n await this.page.waitForTimeout(400)\n this.save()\n }\n\n /** Log a focus event (a `point` with no end lasts until the next event). */\n record(event: Omit<FocusEvent, 'start' | 'end'> & { start: number; end?: number }): void {\n const { focus } = this.timeline\n const open = focus[focus.length - 1]\n if (open && open.kind === 'point' && open.end === undefined) open.end = round(event.start)\n focus.push({\n ...event,\n start: round(event.start),\n end: event.end === undefined ? undefined : round(event.end),\n })\n this.save()\n }\n\n /** @internal The stage's cards, lower-thirds and captions (drawn by the edit). */\n cardStart(card: Omit<Card, 'start' | 'end'>): void {\n this.cardEnd()\n this.captionEnd()\n this.timeline.cards.push({ ...card, start: round(this.now()), end: null })\n this.save()\n }\n\n cardEnd(): void {\n const last = this.timeline.cards[this.timeline.cards.length - 1]\n if (last && last.end === null) {\n last.end = round(this.now())\n this.save()\n }\n }\n\n lowerThird(kicker: string, title: string, holdMs: number): void {\n const start = round(this.now())\n this.timeline.lowerThirds.push({ kicker, title, start, end: round(start + holdMs / 1000) })\n this.save()\n }\n\n captionStart(text: string): void {\n this.captionEnd()\n this.timeline.captions.push({ text, start: round(this.now()), end: null })\n this.save()\n }\n\n captionEnd(): void {\n const last = this.timeline.captions[this.timeline.captions.length - 1]\n if (last && last.end === null) {\n last.end = round(this.now())\n this.save()\n }\n }\n\n private save(): void {\n const { zoom, maxZoom, pip } = this.options\n writeFileSync(this.file, JSON.stringify({ t0: this.t0, waits: this.waits }, null, 1))\n writeFileSync(\n this.focusFile,\n JSON.stringify(\n { t0: this.t0, config: { zoom, maxZoom, pip }, brand: this.brand, ...this.timeline },\n null,\n 1\n )\n )\n }\n}\n\nexport interface Cinematic {\n /** Start a take on `page` (before `installStage`, so the stage paints its timecode). */\n take(page: Page, options: TakeOptions): Promise<Take>\n installStage(page: Page): Promise<void>\n cover(\n page: Page,\n opts: { kicker: string; title: string; subtitle?: string },\n holdMs?: number\n ): Promise<void>\n /** A cover the work can go on behind (clicks pass through it) until `uncover`. */\n chapter(\n page: Page,\n opts: { kicker: string; title: string; subtitle?: string },\n holdMs?: number\n ): Promise<void>\n uncover(page: Page): Promise<void>\n /** A section's name over the bottom left while the work goes on (drawn by the edit). */\n lowerThird(page: Page, kicker: string, title: string, holdMs?: number): void\n caption(page: Page, text: string, holdMs?: number): Promise<void>\n clearCaption(page: Page): Promise<void>\n beat(page: Page, ms?: number): Promise<void>\n glideTo(page: Page, target: string | Locator): Promise<Locator>\n click(page: Page, target: string | Locator): Promise<void>\n type(page: Page, target: string | Locator, text: string): Promise<void>\n /** A key moment: glide to it and hold; the edit zooms onto it and draws a callout with `label`. */\n focusOn(\n page: Page,\n target: string | Locator,\n label?: string,\n opts?: { holdMs?: number; callout?: boolean; mode?: FocusEvent['mode'] }\n ): Promise<void>\n /** Read down a result at a reader's pace, then back to the top. */\n readDown(page: Page, target: string | Locator, scrolls?: number, pauseMs?: number): Promise<void>\n /** Sign in. `{ show: false }` authenticates fast + silent (no animation/caption). */\n login(page: Page, opts?: { show?: boolean; caption?: string }): Promise<void>\n}\n\nconst DEFAULTS = {\n displayFont: 'ui-serif, Georgia, serif',\n sansFont: 'Inter, ui-sans-serif, system-ui, sans-serif',\n cursor: { from: 'rgba(31,111,235,.98)', to: 'rgba(11,42,74,.92)' },\n ripple: 'rgba(11,42,74,.85)',\n cover: { from: '#0b1622', mid: '#0e1b2c', to: '#1f6feb' },\n accent: '#c9a24b',\n ink: '#0e1b2d',\n coverTitle: '#f4f6f8',\n}\n\n/** A brand with every default filled in: what the stage paints and the take hands the edit. */\nexport function cinematicSkin(b: CinematicBrand) {\n return {\n firmLabel: b.firmLabel,\n displayFont: b.displayFont ?? DEFAULTS.displayFont,\n sansFont: b.sansFont ?? DEFAULTS.sansFont,\n cursorFrom: b.cursor?.from ?? DEFAULTS.cursor.from,\n cursorTo: b.cursor?.to ?? DEFAULTS.cursor.to,\n ripple: b.ripple ?? DEFAULTS.ripple,\n coverFrom: b.cover?.from ?? DEFAULTS.cover.from,\n coverMid: b.cover?.mid ?? DEFAULTS.cover.mid,\n coverTo: b.cover?.to ?? DEFAULTS.cover.to,\n accent: b.accent ?? DEFAULTS.accent,\n ink: b.ink ?? DEFAULTS.ink,\n coverTitle: b.coverTitle ?? DEFAULTS.coverTitle,\n fontFiles: b.fontFiles ?? [],\n }\n}\n\nexport function createCinematic(config: CinematicConfig): Cinematic {\n const b = config.brand\n const skin = cinematicSkin(b)\n const loginPath = config.loginPath ?? '/app/login'\n const appUrlPattern = config.appUrlPattern ?? /\\/app$/\n const ids = {\n email: config.testIds?.email ?? 'auth-email',\n password: config.testIds?.password ?? 'auth-password',\n submit: config.testIds?.submit ?? 'auth-login-submit',\n }\n\n const resolve = (page: Page, t: string | Locator): Locator =>\n typeof t === 'string' ? page.getByTestId(t) : t\n\n /** The target's box on screen (clipped to the viewport), or null when it is not on screen. */\n async function boxOf(page: Page, loc: Locator): Promise<Box | null> {\n const box = await loc.boundingBox().catch(() => null)\n const vp = page.viewportSize()\n if (!box || !vp) return box\n const x = Math.max(box.x, 0)\n const y = Math.max(box.y, 0)\n const width = Math.min(box.x + box.width, vp.width) - x\n const height = Math.min(box.y + box.height, vp.height) - y\n return width > 0 && height > 0\n ? { x: round(x), y: round(y), width: round(width), height: round(height) }\n : null\n }\n\n /** Move the cursor to the centre of a target, smoothly; its box once there. */\n async function glide(\n page: Page,\n target: string | Locator\n ): Promise<{ loc: Locator; box: Box | null }> {\n const loc = resolve(page, target).first()\n await loc.scrollIntoViewIfNeeded().catch(() => {})\n const box = await boxOf(page, loc)\n if (box) {\n await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps: 26 })\n await page.waitForTimeout(180)\n }\n return { loc, box }\n }\n\n async function take(page: Page, options: TakeOptions): Promise<Take> {\n return new Take(page, options, b)\n }\n\n async function installStage(page: Page): Promise<void> {\n await page.addInitScript(\n ({ s, t0, TC }: { s: typeof skin; t0: number | null; TC: number }) => {\n const boot = () => {\n if (document.getElementById('__cine_cursor')) return\n\n const cursor = document.createElement('div')\n cursor.id = '__cine_cursor'\n cursor.style.cssText =\n 'position:fixed;left:0;top:0;z-index:2147483647;width:22px;height:22px;' +\n 'margin:-11px 0 0 -11px;border-radius:50%;pointer-events:none;' +\n 'transition:transform .09s ease-out;' +\n `background:radial-gradient(circle at 35% 35%, ${s.cursorFrom}, ${s.cursorTo});` +\n 'box-shadow:0 0 0 3px rgba(255,255,255,.6),0 2px 10px rgba(0,0,0,.4);'\n document.body.appendChild(cursor)\n\n window.addEventListener(\n 'mousemove',\n (e) => {\n cursor.style.transform = `translate(${(e as MouseEvent).clientX}px,${(e as MouseEvent).clientY}px)`\n },\n true\n )\n window.addEventListener(\n 'mousedown',\n (e) => {\n const m = e as MouseEvent\n const r = document.createElement('div')\n r.style.cssText =\n `position:fixed;left:${m.clientX}px;top:${m.clientY}px;z-index:2147483646;` +\n 'width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;pointer-events:none;' +\n `border:2px solid ${s.ripple};animation:__cine_ripple .55s ease-out forwards;`\n document.body.appendChild(r)\n setTimeout(() => r.remove(), 600)\n },\n true\n )\n\n const cap = document.createElement('div')\n cap.id = '__cine_cap'\n cap.style.cssText =\n 'position:fixed;left:50%;bottom:34px;transform:translateX(-50%) translateY(8px);' +\n 'z-index:2147483646;max-width:72%;padding:13px 24px;border-radius:13px;' +\n `font:500 17px/1.45 ${s.sansFont};color:#fff;letter-spacing:.01em;` +\n `background:${s.ink}e0;backdrop-filter:blur(7px);` +\n 'box-shadow:0 10px 34px rgba(0,0,0,.4);text-align:center;opacity:0;transition:opacity .35s,transform .35s;'\n document.body.appendChild(cap)\n\n const style = document.createElement('style')\n style.textContent =\n '@keyframes __cine_ripple{to{width:46px;height:46px;margin:-23px 0 0 -23px;opacity:0}}'\n document.head.appendChild(style)\n\n ;(window as unknown as { __cine_cap: (t: string) => void }).__cine_cap = (t: string) => {\n cap.textContent = t\n cap.style.opacity = t ? '1' : '0'\n cap.style.transform = `translateX(-50%) translateY(${t ? 0 : 8}px)`\n }\n\n // The timecode: with a take running, the take's clock in tenths of a second as 16 squares\n // (white = 1, most significant first) after a white and a black guard, in the bottom right\n // corner. The edit reads it from every frame to line the timeline up with the video (the\n // video's clock drifts from the wall clock over a long take), and paints it out.\n if (t0 !== null) {\n const strip = document.createElement('div')\n strip.id = '__cine_timecode'\n strip.style.cssText = `position:fixed;right:0;bottom:0;z-index:2147483647;display:flex;pointer-events:none;height:${TC}px`\n const cells = Array.from({ length: 18 }, (_, i) => {\n const cell = document.createElement('div')\n cell.style.cssText = `width:${TC}px;height:${TC}px;background:${i === 0 ? '#fff' : '#000'}`\n strip.appendChild(cell)\n return cell\n })\n document.body.appendChild(strip)\n const tick = () => {\n const ds = Math.max(0, Math.round((Date.now() - t0) / 100)) & 0xffff\n for (let bit = 0; bit < 16; bit += 1)\n cells[bit + 2].style.background = (ds >> (15 - bit)) & 1 ? '#fff' : '#000'\n requestAnimationFrame(tick)\n }\n tick()\n }\n }\n if (document.body) boot()\n else document.addEventListener('DOMContentLoaded', boot)\n },\n { s: skin, t0: Take.on(page)?.t0 ?? null, TC: TIMECODE_CELL }\n )\n }\n\n async function cover(\n page: Page,\n { kicker, title, subtitle }: { kicker: string; title: string; subtitle?: string },\n holdMs = 2600\n ): Promise<void> {\n // With a take running, the edit draws the card over this one from the brand PNG (crisp, cut to\n // length) and does the fades, so in the page it shows and goes at once.\n const running = Take.on(page)\n running?.cardStart({ kicker, title, subtitle })\n await page.evaluate(\n ({ kicker, title, subtitle, s, instant }) => {\n document.getElementById('__cine_cover')?.remove()\n const el = document.createElement('div')\n el.id = '__cine_cover'\n el.style.cssText =\n 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;' +\n 'align-items:center;justify-content:center;text-align:center;padding:0 8%;' +\n `background:linear-gradient(135deg,${s.coverFrom} 0%,${s.coverMid} 48%,${s.coverTo} 165%);` +\n `opacity:${instant ? 1 : 0};transition:opacity ${instant ? 0 : 0.5}s ease;font-family:${s.sansFont};`\n el.innerHTML =\n `<div style=\"text-transform:uppercase;letter-spacing:.26em;font-size:13px;font-weight:600;color:${s.accent};margin-bottom:22px\">${kicker}</div>` +\n `<div style=\"font-family:${s.displayFont};font-weight:600;font-size:52px;line-height:1.08;letter-spacing:-.02em;color:${s.coverTitle};max-width:16ch\">${title}</div>` +\n (subtitle\n ? `<div style=\"margin-top:22px;font-size:19px;line-height:1.5;color:${s.coverTitle}b8;max-width:46ch\">${subtitle}</div>`\n : '') +\n `<div style=\"margin-top:44px;display:flex;align-items:center;gap:10px;color:${s.coverTitle}80;font-size:13px;letter-spacing:.02em\">` +\n `<span style=\"width:26px;height:1px;background:${s.accent}\"></span>${s.firmLabel}</div>`\n document.body.appendChild(el)\n requestAnimationFrame(() => (el.style.opacity = '1'))\n },\n { kicker, title, subtitle: subtitle ?? '', s: skin, instant: !!running }\n )\n await page.waitForTimeout(holdMs)\n }\n\n async function chapter(\n page: Page,\n card: { kicker: string; title: string; subtitle?: string },\n holdMs?: number\n ): Promise<void> {\n await cover(page, card, holdMs)\n await page.evaluate(() => {\n const el = document.getElementById('__cine_cover')\n if (el) el.style.pointerEvents = 'none'\n })\n }\n\n async function uncover(page: Page): Promise<void> {\n const running = Take.on(page)\n await page.evaluate((instant) => {\n const el = document.getElementById('__cine_cover')\n if (!el) return\n if (instant) return el.remove()\n el.style.opacity = '0'\n setTimeout(() => el.remove(), 520)\n }, !!running)\n running?.cardEnd()\n await page.waitForTimeout(560)\n }\n\n function lowerThird(page: Page, kicker: string, title: string, holdMs = 3200): void {\n Take.on(page)?.lowerThird(kicker, title, holdMs)\n }\n\n async function caption(page: Page, text: string, holdMs?: number): Promise<void> {\n const running = Take.on(page)\n if (running) running.captionStart(text)\n else\n await page.evaluate(\n (t) => (window as unknown as { __cine_cap?: (s: string) => void }).__cine_cap?.(t),\n text\n )\n const hold = holdMs ?? Math.min(4200, 900 + text.length * 45)\n await page.waitForTimeout(hold)\n }\n\n async function clearCaption(page: Page): Promise<void> {\n const running = Take.on(page)\n if (running) running.captionEnd()\n else\n await page.evaluate(() =>\n (window as unknown as { __cine_cap?: (s: string) => void }).__cine_cap?.('')\n )\n }\n\n async function beat(page: Page, ms = 650): Promise<void> {\n await page.waitForTimeout(ms)\n }\n\n async function glideTo(page: Page, target: string | Locator): Promise<Locator> {\n const running = Take.on(page)\n const start = running?.now() ?? 0\n const { loc, box } = await glide(page, target)\n if (running && box) running.record({ kind: 'point', start, box })\n return loc\n }\n\n async function click(page: Page, target: string | Locator): Promise<void> {\n const running = Take.on(page)\n const start = running?.now() ?? 0\n const { loc, box } = await glide(page, target)\n await loc.click()\n await page.waitForTimeout(260)\n if (running && box) running.record({ kind: 'click', start, end: running.now(), box })\n }\n\n async function type(page: Page, target: string | Locator, text: string): Promise<void> {\n const running = Take.on(page)\n const start = running?.now() ?? 0\n const { loc, box } = await glide(page, target)\n await loc.click()\n await loc.fill('')\n await loc.pressSequentially(text, { delay: 42 })\n await page.waitForTimeout(240)\n if (running && box) running.record({ kind: 'type', start, end: running.now(), box })\n }\n\n async function focusOn(\n page: Page,\n target: string | Locator,\n label?: string,\n {\n holdMs = 2200,\n callout = true,\n mode,\n }: { holdMs?: number; callout?: boolean; mode?: FocusEvent['mode'] } = {}\n ): Promise<void> {\n const running = Take.on(page)\n const start = running?.now() ?? 0\n const { box } = await glide(page, target)\n await page.waitForTimeout(holdMs)\n if (running && box)\n running.record({ kind: 'focus', label, start, end: running.now(), box, callout, mode })\n }\n\n async function readDown(\n page: Page,\n target: string | Locator,\n scrolls = 3,\n pauseMs = 1600\n ): Promise<void> {\n const running = Take.on(page)\n const start = running?.now() ?? 0\n const { box } = await glide(page, target)\n for (let i = 0; i < scrolls; i += 1) {\n await page.mouse.wheel(0, 320)\n await beat(page, pauseMs)\n }\n await page.mouse.wheel(0, -320 * scrolls)\n await beat(page, 500)\n if (running && box) running.record({ kind: 'read', start, end: running.now(), box })\n }\n\n async function login(page: Page, opts: { show?: boolean; caption?: string } = {}): Promise<void> {\n if (!config.creds) throw new Error('createCinematic: pass creds to sign in')\n await page.goto(loginPath)\n if (opts.show === false) {\n await page.getByTestId(ids.email).fill(config.creds.email)\n await page.getByTestId(ids.password).fill(config.creds.password)\n await page.getByTestId(ids.submit).click()\n await page.waitForURL(appUrlPattern, { timeout: 45_000 })\n return\n }\n await caption(page, opts.caption ?? `${b.firmLabel} — signing in.`, 1600)\n await type(page, ids.email, config.creds.email)\n await type(page, ids.password, config.creds.password)\n await click(page, ids.submit)\n await page.waitForURL(appUrlPattern, { timeout: 45_000 })\n await beat(page, 800)\n }\n\n return {\n take,\n installStage,\n cover,\n chapter,\n uncover,\n lowerThird,\n caption,\n clearCaption,\n beat,\n glideTo,\n click,\n type,\n focusOn,\n readDown,\n login,\n }\n}\n"]}