From 8f31e53cfc1d87f5c32be4426272dbb8a818441b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 15 Oct 2026 09:24:06 -0400 Subject: [PATCH] 0.1.5 update video and Chinese changelog Real footage of the installed 0.1.5 apps, composited in Remotion with shape morphs between sections; synthesized score and UI sounds. Logs P3-17 (ElyTable keyboard zoom leaves the grid half-scrolled). Co-Authored-By: Claude Opus 5.5 (1M context) --- site/video/update-0.1.5/.gitignore | 8 + site/video/update-0.1.5/CHANGELOG.zh.md | 26 + site/video/update-0.1.5/SPEC.md | 50 + site/video/update-0.1.5/audio/README.md | 3 + site/video/update-0.1.5/audio/build.py | 19 + site/video/update-0.1.5/audio/compose.py | 496 ++ site/video/update-0.1.5/audio/sfx.py | 43 + site/video/update-0.1.5/package-lock.json | 4285 +++++++++++++++++ site/video/update-0.1.5/package.json | 26 + .../public/fonts/OFL-NotoSansSC.txt | 93 + .../public/icons/ely-doc-macos.svg | 1 + .../update-0.1.5/public/icons/ely-doc.svg | 1 + .../public/icons/ely-mark-macos.svg | 1 + .../update-0.1.5/public/icons/ely-mark.svg | 1 + .../public/icons/ely-pdf-macos.svg | 1 + .../update-0.1.5/public/icons/ely-pdf.svg | 1 + .../public/icons/ely-show-macos.svg | 1 + .../update-0.1.5/public/icons/ely-show.svg | 1 + .../public/icons/ely-table-macos.svg | 1 + .../update-0.1.5/public/icons/ely-table.svg | 1 + .../public/icons/ely-tree-macos.svg | 1 + .../update-0.1.5/public/icons/ely-tree.svg | 1 + site/video/update-0.1.5/record/drive.py | 306 ++ site/video/update-0.1.5/record/explore.py | 15 + site/video/update-0.1.5/record/prep.py | 32 + site/video/update-0.1.5/record/samples.py | 100 + site/video/update-0.1.5/record/sync.py | 47 + site/video/update-0.1.5/record/takes.py | 156 + site/video/update-0.1.5/src/Root.tsx | 7 + site/video/update-0.1.5/src/Video.tsx | 53 + .../update-0.1.5/src/design/Background.tsx | 24 + .../video/update-0.1.5/src/design/Caption.tsx | 50 + .../video/update-0.1.5/src/design/Heading.tsx | 203 + site/video/update-0.1.5/src/design/KeyCap.tsx | 108 + site/video/update-0.1.5/src/design/Shape.tsx | 185 + site/video/update-0.1.5/src/design/Stage.tsx | 75 + site/video/update-0.1.5/src/design/fonts.ts | 20 + .../video/update-0.1.5/src/design/geometry.ts | 186 + site/video/update-0.1.5/src/design/morph.ts | 259 + site/video/update-0.1.5/src/flubber.d.ts | 9 + site/video/update-0.1.5/src/footage/Clip.tsx | 100 + .../video/update-0.1.5/src/footage/Cursor.tsx | 70 + .../video/update-0.1.5/src/footage/Sounds.tsx | 42 + .../update-0.1.5/src/footage/Triangle.tsx | 84 + .../update-0.1.5/src/footage/preview.tsx | 31 + site/video/update-0.1.5/src/footage/take.ts | 62 + site/video/update-0.1.5/src/index.ts | 4 + site/video/update-0.1.5/src/scenes/Cards.tsx | 121 + site/video/update-0.1.5/src/scenes/Cells.tsx | 50 + site/video/update-0.1.5/src/scenes/Intro.tsx | 175 + site/video/update-0.1.5/src/scenes/Menus.tsx | 46 + site/video/update-0.1.5/src/scenes/More.tsx | 49 + site/video/update-0.1.5/src/scenes/Outro.tsx | 152 + .../update-0.1.5/src/scenes/RightClick.tsx | 65 + site/video/update-0.1.5/src/theme.ts | 84 + site/video/update-0.1.5/tsconfig.json | 19 + tasks/BUGS.md | 1 + 57 files changed, 8051 insertions(+) create mode 100644 site/video/update-0.1.5/.gitignore create mode 100644 site/video/update-0.1.5/CHANGELOG.zh.md create mode 100644 site/video/update-0.1.5/SPEC.md create mode 100644 site/video/update-0.1.5/audio/README.md create mode 100644 site/video/update-0.1.5/audio/build.py create mode 100644 site/video/update-0.1.5/audio/compose.py create mode 100644 site/video/update-0.1.5/audio/sfx.py create mode 100644 site/video/update-0.1.5/package-lock.json create mode 100644 site/video/update-0.1.5/package.json create mode 100644 site/video/update-0.1.5/public/fonts/OFL-NotoSansSC.txt create mode 100644 site/video/update-0.1.5/public/icons/ely-doc-macos.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-doc.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-mark-macos.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-mark.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-pdf-macos.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-pdf.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-show-macos.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-show.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-table-macos.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-table.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-tree-macos.svg create mode 100644 site/video/update-0.1.5/public/icons/ely-tree.svg create mode 100644 site/video/update-0.1.5/record/drive.py create mode 100644 site/video/update-0.1.5/record/explore.py create mode 100644 site/video/update-0.1.5/record/prep.py create mode 100644 site/video/update-0.1.5/record/samples.py create mode 100644 site/video/update-0.1.5/record/sync.py create mode 100644 site/video/update-0.1.5/record/takes.py create mode 100644 site/video/update-0.1.5/src/Root.tsx create mode 100644 site/video/update-0.1.5/src/Video.tsx create mode 100644 site/video/update-0.1.5/src/design/Background.tsx create mode 100644 site/video/update-0.1.5/src/design/Caption.tsx create mode 100644 site/video/update-0.1.5/src/design/Heading.tsx create mode 100644 site/video/update-0.1.5/src/design/KeyCap.tsx create mode 100644 site/video/update-0.1.5/src/design/Shape.tsx create mode 100644 site/video/update-0.1.5/src/design/Stage.tsx create mode 100644 site/video/update-0.1.5/src/design/fonts.ts create mode 100644 site/video/update-0.1.5/src/design/geometry.ts create mode 100644 site/video/update-0.1.5/src/design/morph.ts create mode 100644 site/video/update-0.1.5/src/flubber.d.ts create mode 100644 site/video/update-0.1.5/src/footage/Clip.tsx create mode 100644 site/video/update-0.1.5/src/footage/Cursor.tsx create mode 100644 site/video/update-0.1.5/src/footage/Sounds.tsx create mode 100644 site/video/update-0.1.5/src/footage/Triangle.tsx create mode 100644 site/video/update-0.1.5/src/footage/preview.tsx create mode 100644 site/video/update-0.1.5/src/footage/take.ts create mode 100644 site/video/update-0.1.5/src/index.ts create mode 100644 site/video/update-0.1.5/src/scenes/Cards.tsx create mode 100644 site/video/update-0.1.5/src/scenes/Cells.tsx create mode 100644 site/video/update-0.1.5/src/scenes/Intro.tsx create mode 100644 site/video/update-0.1.5/src/scenes/Menus.tsx create mode 100644 site/video/update-0.1.5/src/scenes/More.tsx create mode 100644 site/video/update-0.1.5/src/scenes/Outro.tsx create mode 100644 site/video/update-0.1.5/src/scenes/RightClick.tsx create mode 100644 site/video/update-0.1.5/src/theme.ts create mode 100644 site/video/update-0.1.5/tsconfig.json diff --git a/site/video/update-0.1.5/.gitignore b/site/video/update-0.1.5/.gitignore new file mode 100644 index 0000000..9113eb7 --- /dev/null +++ b/site/video/update-0.1.5/.gitignore @@ -0,0 +1,8 @@ +node_modules +out +public/footage +public/fonts/*.ttf +public/fonts/*.otf +node_modules +public/audio +__pycache__ diff --git a/site/video/update-0.1.5/CHANGELOG.zh.md b/site/video/update-0.1.5/CHANGELOG.zh.md new file mode 100644 index 0000000..83ac736 --- /dev/null +++ b/site/video/update-0.1.5/CHANGELOG.zh.md @@ -0,0 +1,26 @@ +# Ely Office 0.1.5 更新日志 + +## 全部应用 +- 二级菜单更好选:鼠标斜着移向子菜单时,路过的项不再抢先弹出;子菜单右侧放不下时改在左侧打开。 +- 应用已开着时再打开文件,直接在原窗口开成新标签,不再提示“已在运行”。 +- 中文显示统一:Windows 上同一个词不再一半粗一半细;界面里残留的英文改为中文。 +- macOS 安装包已签名并经 Apple 公证,打开时不再提示“无法验证开发者”。 + +## ElyMark +- 表格可以在格内直接编辑:点哪格开哪格,Tab 与方向键在格间移动,回车或 Esc 写回。 +- 右键菜单随所点的内容变化:剪贴板、复制为 Markdown / HTML / 纯文本、格式、段落、表格、图片。 +- “段落”“格式”菜单与快捷键和 Typora 一致(如 Ctrl+1–6 设标题)。 +- 支持 `:表情:`、GitHub 提示块、`[toc]` 目录、front matter 与常用行内 HTML。 +- 侧栏可清除最近文件,重启后仍保持清空。 + +## ElyDoc +- 右键菜单照 Word:正文、表格、图片各有对应的菜单。 +- 拖动表格边线即可调整列宽与行高。 +- 光标在表格里时,属性栏把表格设置排在最前。 + +## ElyTable +- 右键菜单照 Excel:单元格、行列表头、图形各有对应的菜单。 + +## ElyShow +- 右键菜单照 PowerPoint;表格边线可以拖动。 +- 左侧幻灯片缩略图条可以用滚轮滚动(修复 Linux Wayland 上的问题)。 diff --git a/site/video/update-0.1.5/SPEC.md b/site/video/update-0.1.5/SPEC.md new file mode 100644 index 0000000..576e36b --- /dev/null +++ b/site/video/update-0.1.5/SPEC.md @@ -0,0 +1,50 @@ +# Ely Office 0.1.5 更新视频:规格 + +60.000 秒,1920×1080,60 fps,3600 帧。96 BPM,4/4:一拍 0.625 秒(37.5 帧),一小节 2.5 秒(150 帧),共 24 小节。段落切换都落在小节强拍上。 + +画面以**真实操作录屏**为主(已安装的 0.1.5,Windows,深色主题,中文界面)。录屏放在居中的“舞台”里,镜头在舞台内跟随指针平滑推近(像 Screen Studio)。段落之间用形变(shape morph)过渡:舞台收拢、变形成下一个应用的图标轮廓,再展开成下一个舞台。节奏从容,不快闪:任何画面至少停留一拍,形变用 0.6–1 秒。 + +## 时间表 + +| 小节 | 帧 | 段落 | 内容 | +| --- | --- | --- | --- | +| 1–2 | 0–300 | 开场 | 一个点在拍上呼吸,形变成六个应用图标的轮廓依次(每个至少一拍),最后化为“Ely Office 0.1.5”与“更顺手的一次更新”;第 3 小节强拍展开成第一个舞台 | +| 3–6 | 300–900 | 菜单 | ElyMark 右键 → 表格 ▸,指针斜穿“插入”行进入子菜单,子菜单不被抢走;安全三角叠在录屏上 | +| 7–10 | 900–1500 | 表格格内编辑 | ElyMark 点格即在格内编辑原文,Tab 到下一格,回车写回 | +| 11–16 | 1500–2400 | 右键照 Word / Excel / PowerPoint | ElyDoc(表格右键、插入行、拖边线)、ElyTable(表头右键)、ElyShow(缩略图滚轮、表格右键) | +| 17–21 | 2400–3150 | 还有 | 再开文件进同一窗口、清除最近文件、中文字形统一、macOS 已公证 | +| 22–24 | 3150–3600 | 结尾 | 舞台收拢成点,再展开成六个图标与“Ely Office 0.1.5”、平台与下载地址;第 24 小节末结束 | + +音乐的能量:开场渐起(无鼓),第 3 小节进入律动,第 11 小节加厚,第 17 小节最高,第 22 小节强拍落定后渐弱到 60 秒收尾。每个段落切换前有半小节的上扬(riser),强拍上有柔和的冲击(impact)。 + +## 视觉 + +- 底色 `#1c1c1e` 到 `#252525` 的极轻径向渐变,细微颗粒;舞台外不放杂物。 +- 强调色 `#3D7FE5`(ElyDoc 蓝);各应用色:Doc `#3D7FE5`,Table `#2E9E5B`,Show `#F07B2A`,PDF `#E0344F`,Tree `#7E57C2`,Mark `#3E4A5C`(点缀 `#F2A900`)。 +- 字体:中文 Noto Sans SC(`public/fonts/NotoSansSC.ttf`,可变字重),西文 Inter,等宽 JetBrains Mono。 +- 舞台:`STAGE = { x: 120, y: 118, w: 1680, h: 945 }`,圆角 18,1px `rgba(255,255,255,0.08)` 描边,柔和的大投影。录屏 2048×1152,按舞台宽缩放(0.8203)。 +- 段落标题在舞台上方左侧(y 30–100):中文大字 + 一行小字说明。 +- 动效:缓动用 `Easing.bezier(0.22, 1, 0.36, 1)`(出)与 `(0.65, 0, 0.35, 1)`(进出);弹簧 `damping 200`;无闪烁,无高频抖动。 + +## 代码结构(Remotion) + +```text +src/index.ts registerRoot +src/Root.tsx +src/Video.tsx 各段 ,音乐与音效 +src/theme.ts 帧、节拍、颜色、STAGE、字体 +src/design/ 共用:Stage、Heading、Caption、KeyCap、Morph、背景、字体加载 +src/scenes/Intro.tsx 开场 +src/scenes/Outro.tsx 结尾 +src/scenes/Menus.tsx … 录屏段落(由主导者写) +src/footage/ 录屏的镜头跟随、事件读入 +public/footage/ 录屏与事件日志(不入库) +public/audio/ music.wav 与音效 +``` + +`Stage` 负责舞台的外框、裁切与进出形变:`{录屏}`;进场时从图标轮廓(flubber 插值)展开成圆角矩形,出场时收拢成下一个图标的轮廓。裁切用 CSS `clip-path: path(...)`,录屏画在其中。 + +## 音频 + +- `public/audio/music.wav`:48 kHz 立体声,恰 60.000 秒,响度约 −16 LUFS,峰值 ≤ −1 dBTP,原创合成,无版权素材。 +- `public/audio/sfx/`:`click.wav`(轻点击)、`menu.wav`(菜单弹出的轻“嗒”)、`key.wav`(键入)、`whoosh.wav`(形变)、`impact.wav`(强拍落定)、`tick.wav`(细小提示)。都短、干净、不刺耳。 diff --git a/site/video/update-0.1.5/audio/README.md b/site/video/update-0.1.5/audio/README.md new file mode 100644 index 0000000..152c805 --- /dev/null +++ b/site/video/update-0.1.5/audio/README.md @@ -0,0 +1,3 @@ +# Audio + +`build.py` (score in `compose.py`, UI sounds in `sfx.py`) writes `public/audio/music.wav` (48 kHz, 24-bit stereo, 2,880,000 samples, 96 BPM, 24 bars of 2.5 s in F major) and the six UI sounds in `public/audio/sfx/`; it is deterministic (fixed seeds) and needs numpy, scipy, pedalboard (GPL-3.0, used only as a tool, its output is unencumbered) and pyloudnorm (MIT). Structure: bars 1–2 a rising felt-piano line over a Bb–Csus pad swell, no drums; bar 3 the groove enters (half-time kick, brushed snare on 2 and 4, eighth hats, sub bass, eighth-note pluck arpeggio, piano motif over F–Dm–Bb–C); bar 7 a lift (shaker, busier bass, motif answered higher, FM bell counter-line); bar 11 thicker (sixteenth arpeggio, octave strings, ghost snares, motif B through Gm–Csus); bar 17 the peak (four-on-the-floor, open hats, melody in octaves over Bb–C–Am–Dm–Gm–C); bar 22 a resolving Fadd9 hit with the drums gone, then piano echoes decaying to silence by 59.4 s. A half-bar riser (swept noise plus a reversed swell of the next chord) precedes the downbeats of bars 3, 7, 11, 17 and 22, each carrying a soft sub impact; drums stop in the half-bar before each for space (snare rolls before 17 and 22). Every sound is synthesized here (additive felt piano, polyBLEP saw pads, additive plucks, FM bells, synthesized drums, convolution reverb from a generated impulse); no samples, soundfonts or existing melodies are used. Mix: kick-keyed ducking on pads, bass, arp and reverb, a 2.6 kHz dip so UI sounds sit on top, section gain rides, bus compression, soft saturation and a 4×-oversampled look-ahead limiter; the result is normalized to −16 LUFS. diff --git a/site/video/update-0.1.5/audio/build.py b/site/video/update-0.1.5/audio/build.py new file mode 100644 index 0000000..2124cb6 --- /dev/null +++ b/site/video/update-0.1.5/audio/build.py @@ -0,0 +1,19 @@ +import numpy as np + +from compose import OUT, SR, Mix, master, score, true_peak, write +from sfx import sfx + + +def main(): + mix = Mix() + kicks = score(mix) + x, lufs, stems = master(mix, kicks) + write(OUT / "music.wav", x) + print(f"music: {len(x) / SR:.3f} s, {lufs:.2f} LUFS, true peak {20 * np.log10(true_peak(x)):.2f} dBTP") + print("stems (dB RMS):", {k: round(v, 1) for k, v in stems.items()}) + # Shares compose's generator, so runs after the music. + sfx() + + +if __name__ == "__main__": + main() diff --git a/site/video/update-0.1.5/audio/compose.py b/site/video/update-0.1.5/audio/compose.py new file mode 100644 index 0000000..798128e --- /dev/null +++ b/site/video/update-0.1.5/audio/compose.py @@ -0,0 +1,496 @@ +# Usage: .work/video015/venv/Scripts/python.exe site/video/update-0.1.5/audio/compose.py +"""Music bed (96 BPM, 24 bars, 60 s) and UI sounds for the 0.1.5 update video.""" +import wave +from pathlib import Path + +import numpy as np +import pedalboard as pb +import pyloudnorm as pyln +from scipy import signal +from scipy.ndimage import minimum_filter1d, uniform_filter1d + +SR = 48000 +BEAT = 30000 +BAR = 4 * BEAT +N = 24 * BAR +TAIL = 8 * SR +OUT = Path(__file__).resolve().parent.parent / "public" / "audio" +TARGET_LUFS = -16.0 +CEILING_DB = -1.4 +LIFTS = (3, 7, 11, 17, 22) +BREAKS = (6, 10, 16, 21) +GAINS = {"piano": 0.86, "pad": 0.245, "strings": 0.13, "arp": 0.14, "bell": 0.12, + "bass": 0.16, "kick": 0.52, "snare": 0.64, "hats": 0.17, "fx": 0.37} +rng = np.random.default_rng(105) + +# chord: bass root, pad voicing, arp tones, piano left hand +CHORDS = { + "Bb": (34, [50, 57, 60, 62, 65], [62, 65, 69, 74], [46, 53, 62]), + "Csus": (36, [55, 60, 65, 67, 72], [65, 67, 72, 74], [48, 55, 65]), + "C": (36, [55, 60, 62, 64, 69], [64, 67, 72, 74], [48, 55, 64]), + "F": (41, [53, 60, 64, 67, 69], [65, 69, 72, 76], [41, 48, 57]), + "Dm": (38, [50, 57, 60, 65, 69], [62, 65, 69, 72], [50, 57, 65]), + "Gm": (31, [53, 57, 58, 62, 65], [62, 65, 70, 74], [43, 50, 58]), + "Am": (33, [52, 55, 60, 64, 67], [64, 67, 69, 72], [45, 52, 60]), + "Fend": (29, [53, 60, 65, 67, 69, 72], [], [41, 48, 57, 65]), +} +CHANGES = [(0, "Bb"), (4, "Csus"), (8, "F"), (12, "Dm"), (16, "Bb"), (20, "C"), + (24, "F"), (28, "Dm"), (32, "Bb"), (36, "C"), (40, "F"), (44, "Dm"), + (48, "Bb"), (52, "C"), (56, "Gm"), (60, "Csus"), (62, "C"), (64, "Bb"), + (68, "C"), (72, "Am"), (76, "Dm"), (80, "Gm"), (82, "Csus"), (84, "Fend")] + +MEL_A = [(0, 72, 1.5, .62), (1.5, 77, .5, .5), (2, 76, 1, .55), (3, 72, 1, .46), + (4, 74, 1.5, .6), (5.5, 69, .5, .46), (6, 72, 2, .55)] +MEL_A1 = MEL_A + [(8, 74, 1.5, .6), (9.5, 77, .5, .5), (10, 76, 1, .55), (11, 72, 1, .46), + (12, 67, 1.5, .55), (13.5, 72, .5, .46), (14, 74, 1, .5), (15, 76, 1, .48)] +MEL_A2 = MEL_A + [(8, 74, 1.5, .62), (9.5, 77, .5, .52), (10, 81, 1, .6), (11, 79, 1, .52), + (12, 76, 1.5, .58), (13.5, 74, .5, .48), (14, 72, 2, .55)] +MEL_B = [(0, 77, 1, .66), (1, 76, .5, .5), (1.5, 72, 1, .55), (2.5, 69, .5, .48), + (3, 72, 1, .52), (4, 74, 1, .62), (5, 72, .5, .5), (5.5, 69, 1, .52), + (6.5, 65, .5, .45), (7, 69, 1, .5), (8, 74, 1, .62), (9, 77, .5, .52), + (9.5, 81, 1.5, .62), (11, 79, 1, .52), (12, 76, 1.5, .6), (13.5, 74, .5, .5), + (14, 72, 1, .52), (15, 74, 1, .5), (16, 77, 1.5, .62), (17.5, 74, .5, .5), + (18, 70, 1, .52), (19, 72, 1, .5), (20, 74, 2, .58), (22, 76, 1, .6), (23, 79, 1, .66)] +MEL_D = [(0, 81, 1.5, .72), (1.5, 79, .5, .56), (2, 77, 1, .62), (3, 74, 1, .56), + (4, 76, 1.5, .68), (5.5, 72, .5, .52), (6, 79, 2, .64), (8, 76, 1.5, .68), + (9.5, 72, .5, .52), (10, 74, 1, .58), (11, 76, 1, .56), (12, 77, 1.5, .68), + (13.5, 76, .5, .54), (14, 74, 1, .58), (15, 72, 1, .54), (16, 74, 1.5, .64), + (17.5, 77, .5, .56), (18, 76, 2, .62)] +INTRO = [(0, 65, .3), (1, 69, .33), (2, 72, .36), (3, 74, .4), + (4, 67, .42), (5, 72, .46), (6, 74, .5), (7, 77, .54)] +BELLS = [(26.5, 81), (30.5, 79), (34.5, 81), (66.5, 84), (70.5, 84), (74.5, 81), (78.5, 81)] + + +def hz(m): + return 440.0 * 2 ** ((m - 69) / 12) + + +def sample(beat): + return int(round(beat * BEAT)) + + +def secs(n): + return np.arange(n) / SR + + +def filt(x, kind, f, order=2): + return signal.sosfilt(signal.butter(order, f, kind, fs=SR, output="sos"), x, axis=0) + + +def peaking(x, f0, gain_db, q): + a, w = 10 ** (gain_db / 40), 2 * np.pi * f0 / SR + alpha = np.sin(w) / (2 * q) + b = [1 + alpha * a, -2 * np.cos(w), 1 - alpha * a] + d = [1 + alpha / a, -2 * np.cos(w), 1 - alpha / a] + return signal.lfilter(np.array(b) / d[0], np.array(d) / d[0], x, axis=0) + + +def sweep(x, cutoff, kind, order=2, block=512): + """Filter whose corner follows cutoff(seconds); state carries across blocks.""" + y, zi = np.empty_like(x), None + for s in range(0, len(x), block): + e = min(s + block, len(x)) + sos = signal.butter(order, cutoff((s + e) / 2 / SR), kind, fs=SR, output="sos") + if zi is None: + zi = np.zeros((sos.shape[0], 2) + x.shape[1:]) + y[s:e], zi = signal.sosfilt(sos, x[s:e], axis=0, zi=zi) + return y + + +def curve(points, log=False): + ts, vs = np.array(points).T + vs = np.log(vs) if log else vs + return lambda t: np.exp(np.interp(t, ts, vs)) if log else np.interp(t, ts, vs) + + +def edges(x, a=0.003, r=0.02): + x = x.copy() + ai, ri = max(2, int(a * SR)), max(2, int(r * SR)) + up, down = np.linspace(0, 1, ai), np.linspace(1, 0, ri) + if x.ndim == 2: + up, down = up[:, None], down[:, None] + x[:ai] *= up + x[-ri:] *= down + return x + + +def place(x, pan): + th = (pan + 1) * np.pi / 4 + return np.stack([x * np.cos(th), x * np.sin(th)], 1) * np.sqrt(2) + + +def noise(n, ch=1): + return rng.standard_normal((n, ch) if ch > 1 else n) + + +class Mix: + def __init__(self): + self.stems, self.sends = {}, {k: self.blank() for k in ("hall", "room", "echo")} + + @staticmethod + def blank(): + return np.zeros((N + TAIL, 2)) + + def add(self, stem, sig, beat, gain=1.0, pan=0.0, **sends): + sig = place(sig, pan) if sig.ndim == 1 else sig + i = sample(beat) + bus = self.stems.setdefault(stem, self.blank()) + n = min(len(sig), len(bus) - i) + bus[i:i + n] += sig[:n] * gain + for name, amount in sends.items(): + self.sends[name][i:i + n] += sig[:n] * gain * amount + + +def piano(m, dur, vel): + f0 = hz(m) + n = int((dur + 0.5) * SR) + t = secs(n) + tau = np.clip(3.2 * (262 / f0) ** 0.7, 0.6, 7.0) + bright = 800 + 2400 * vel + y = np.zeros(n) + for k in range(1, 16): + fk = k * f0 * np.sqrt(1 + 0.0004 * k * k) + if fk > 7000: + break + amp = k ** -1.2 / (1 + (fk / bright) ** 3) + tk = tau / (1 + 0.45 * (k - 1)) + env = 0.6 * np.exp(-t / (0.3 * tk)) + 0.4 * np.exp(-t / tk) + for cents in (-0.9, 0.2, 1.1): + y += amp * env * np.sin(2 * np.pi * fk * 2 ** (cents / 1200) * t + rng.uniform(0, 6.28)) + off = int(dur * SR) + y[off:] *= np.exp(-secs(n - off) / 0.14) + k = int(0.012 * SR) + y[:k] += filt(noise(k), "low", 900) * np.exp(-secs(k) / 0.003) * 0.12 + return edges(y, 0.006, 0.02) * vel ** 1.6 / 3 + + +def saw(f, n, phase): + dt = f / SR + p = (phase + dt * np.arange(n)) % 1.0 + y = 2 * p - 1 + lo = p < dt + x = p[lo] / dt + y[lo] -= 2 * x - x * x - 1 + hi = p > 1 - dt + x = (p[hi] - 1) / dt + y[hi] -= x * x + 2 * x + 1 + return y + + +def pad(notes, dur, attack, release, octave=0, bright=1.0): + n = int((dur + release) * SR) + out = np.zeros((n, 2)) + for m in notes: + f = hz(m + octave) + for cents, side in ((-11, 0), (-4, 1), (4, 0), (11, 1)): + v = saw(f * 2 ** (cents / 1200), n, rng.uniform()) + out[:, side] += 0.75 * v * bright + out[:, 1 - side] += 0.25 * v * bright + out += np.sin(2 * np.pi * f * secs(n))[:, None] * 0.6 + t = secs(n) + env = np.minimum(1, t / attack) ** 2 + rel = np.clip((t - dur) / release, 0, 1) + env *= np.cos(rel * np.pi / 2) ** 2 + return out * env[:, None] / len(notes) + + +def pluck(m, vel): + f0, n = hz(m), int(0.9 * SR) + t = secs(n) + y = np.zeros(n) + for k in range(1, 25): + if k * f0 > 7500: + break + y += k ** -1.3 * np.exp(-t / (0.5 / (1 + 0.7 * (k - 1)))) * np.sin(2 * np.pi * k * f0 * t + rng.uniform(0, 6.28)) + return edges(y, 0.0015, 0.05) * vel + + +def bell(m, vel, dur=3.0): + f, n = hz(m), int(dur * SR) + t = secs(n) + index = 1.6 * np.exp(-t / 0.35) + y = np.sin(2 * np.pi * f * t + index * np.sin(2 * np.pi * 3.5 * f * t)) * np.exp(-t / 1.1) + y += 0.3 * np.sin(2 * np.pi * 2 * f * t) * np.exp(-t / 0.5) + return edges(y, 0.002, 0.1) * vel + + +def bass(m, dur, vel, decay=None): + f, n = hz(m), int((dur + 0.08) * SR) + t = secs(n) + y = np.sin(2 * np.pi * f * t) + 0.22 * np.sin(4 * np.pi * f * t) + 0.07 * np.sin(6 * np.pi * f * t) + y = np.tanh(1.4 * y) / np.tanh(1.4) + env = np.minimum(1, t / 0.008) * np.clip((dur + 0.06 - t) / 0.06, 0, 1) + if decay: + env *= np.exp(-t / decay) + return y * env * vel + + +def kick(vel): + n = int(0.5 * SR) + t = secs(n) + f = 47 + 75 * np.exp(-t / 0.035) + y = np.tanh(1.5 * np.sin(2 * np.pi * np.cumsum(f) / SR) * np.exp(-t / 0.26)) + k = int(0.004 * SR) + y[:k] += filt(noise(k), "low", 2200) * 0.2 + return edges(y, 0.001, 0.04) * vel + + +def snare(vel): + n = int(0.35 * SR) + t = secs(n) + env = 0.55 * np.exp(-t / 0.13) + for d in (0.0, 0.009, 0.019): + env += np.where(t >= d, np.exp(-(t - d) / 0.006), 0) * 0.6 + y = filt(noise(n), "band", [1200, 7500]) * env * np.minimum(1, t / 0.002) + y += np.sin(2 * np.pi * 185 * t) * np.exp(-t / 0.045) * 0.35 + return edges(peaking(y, 2600, -5, 1.0), 0.001, 0.03) * vel + + +def hat(vel, open_=False): + n = int((0.3 if open_ else 0.08) * SR) + t = secs(n) + y = filt(noise(n), "band", [7000, 12500], 3) * np.exp(-t / (0.2 if open_ else 0.03)) + return edges(y, 0.001, 0.02) * vel + + +def shaker(vel): + n = int(0.09 * SR) + t = secs(n) + y = filt(noise(n), "band", [4800, 10000]) * np.minimum(1, t / 0.007) * np.exp(-t / 0.035) + return edges(y, 0.001, 0.02) * vel + + +def riser(strength, notes): + n = int(1.25 * SR) + t = secs(n) + u = t / t[-1] + swish = sweep(noise(n, 2), lambda s: [r := 300 * (16 ** ((s / 1.25) ** 1.4)), min(r * 2.4, 9000)], "band", 1) + y = filt(swish, "low", 8000) * (u ** 2.4)[:, None] * 0.9 + swell = sum(np.sin(2 * np.pi * hz(m + 12) * t) for m in notes) / len(notes) + y += place(swell * np.exp(5 * (u - 1)) * u * 0.6, 0) + y[-int(0.02 * SR):] *= np.linspace(1, 0, int(0.02 * SR))[:, None] + return y * strength + + +def impact(strength, tail=2.4): + n = int(tail * SR) + t = secs(n) + f = 38 + 32 * np.exp(-t / 0.12) + boom = np.tanh(1.3 * np.sin(2 * np.pi * np.cumsum(f) / SR) * np.exp(-t / 0.65)) + thump = filt(noise(n), "low", 220) * np.exp(-t / 0.05) * 0.8 + air = filt(noise(n, 2), "band", [250, 2200]) * np.exp(-t / 0.45)[:, None] * 0.07 + return edges(place(boom + thump, 0) + air, 0.001, 0.2) * strength + + +def impulse(seconds, t60, seed): + """Stereo reverb impulse: decorrelated noise with per-band decay (low, mid, high).""" + g = np.random.default_rng(seed) + n = int(seconds * SR) + t = secs(n) + ir = np.zeros((n, 2)) + for ch in range(2): + w = g.standard_normal(n) + lo, hi = filt(w, "low", 500), filt(w, "high", 4000) + bands = (lo, w - lo - hi, hi) + ir[:, ch] = sum(b * 10 ** (-3 * t / T) for b, T in zip(bands, t60)) + ir = filt(ir, "low", 7000) * np.minimum(1, t / 0.02)[:, None] + ir = np.concatenate([np.zeros((int(0.022 * SR), 2)), edges(ir, 0.001, seconds * 0.15)]) + return ir / np.sqrt((ir ** 2).sum(0)) + + +def reverb(x, ir): + return np.stack([signal.fftconvolve(x[:, c], ir[:, c])[:len(x)] for c in range(2)], 1) + + +def pingpong(x, beats=0.75, fb=0.4, taps=7): + d, y, out = sample(beats), x.mean(1), np.zeros_like(x) + for k in range(1, taps + 1): + y = filt(filt(y, "low", 3200, 1), "high", 250, 1) + out[k * d:, k % 2] += y[:len(x) - k * d] * fb ** (k - 1) + return out + + +def chord_at(beat): + return CHORDS[[c for b, c in CHANGES if b <= beat][-1]] + + +def drops(bar, beat): + return bar in BREAKS and beat >= 2 + + +def score(mix): + for i, (b, name) in enumerate(CHANGES): + end = CHANGES[i + 1][0] if i + 1 < len(CHANGES) else 96 + notes = CHORDS[name][1] + first, final = b == 0, name == "Fend" + sig = pad(notes, (end - b) * 0.625, 2.6 if first else 0.25, 3.0 if final else 0.9) + mix.add("pad", sig, b, hall=0.5) + if 40 <= b < 84: + mix.add("strings", pad(notes[-3:], (end - b) * 0.625, 0.4, 0.9, 12, 0.5), b, hall=0.6) + + for x, m, v in INTRO: + mix.add("piano", piano(m, 2.4, v), x, pan=(m - 70) / 30, hall=0.35) + for x, name in ((0, "Bb"), (4, "Csus")): + for m in CHORDS[name][3]: + mix.add("piano", piano(m, 3.8, 0.3), x, pan=-0.2, hall=0.3) + + for start, line, low in ((8, MEL_A1, 0), (24, MEL_A2, 0), (40, MEL_B, 0), (64, MEL_D, 1)): + for x, m, d, v in line: + mix.add("piano", piano(m, d * 0.625 + 0.15, v), start + x, pan=0.08, hall=0.28) + if low: + mix.add("piano", piano(m - 12, d * 0.625 + 0.15, v * 0.55), start + x, pan=-0.1, hall=0.28) + for x, m, d, v in ((84, 77, 4.5, .66), (88, 84, 1.5, .32), (89.5, 81, .5, .27), (90, 77, 3.0, .26)): + mix.add("piano", piano(m, d * 0.625, v), x, pan=0.1, hall=0.45) + for m in CHORDS["Fend"][3]: + mix.add("piano", piano(m, 4.0, 0.38), 84, pan=-0.15, hall=0.45) + for x, m in BELLS + [(84, 89)]: + mix.add("bell", bell(m, 0.5 if x < 84 else 0.35), x, pan=0.35, hall=0.7, echo=0.35) + + kicks = [] + for bar in range(3, 22): + base = (bar - 1) * 4 + section = 0 if bar < 7 else 1 if bar < 11 else 2 if bar < 17 else 3 + lh_vel = (0.3, 0.33, 0.37, 0.4)[section] + for x in ((0,) if section < 2 else (0, 2)): + for m in chord_at(base + x)[3]: + mix.add("piano", piano(m, 1.8, lh_vel), base + x, pan=-0.2, hall=0.3) + + pattern = ([0, 2], [0, 2, 2.5], [0, 0.75, 2, 2.5], [0, 1, 2, 3])[section] + for x in pattern: + if not drops(bar, x): + kicks.append(base + x) + mix.add("kick", kick(0.9 if x in (0, 2) else 0.7), base + x, room=0.05) + for x in (1, 3): + if not drops(bar, x): + mix.add("snare", snare(0.55 + 0.1 * (section > 1)), base + x, pan=0.05, room=0.35, hall=0.12) + if section > 1: + for x in ((3.75,) if section == 2 else (1.75, 3.75)): + if not drops(bar, x): + mix.add("snare", snare(0.14), base + x, pan=0.1, room=0.3) + if bar in (16, 21): + for j in range(8): + mix.add("snare", snare(0.12 + 0.05 * j), base + 2 + j * 0.25, pan=0.1, room=0.3, hall=0.1) + for j in range(8): + x = j * 0.5 + if drops(bar, x) and bar in (6, 10): + continue + jitter = rng.normal(0, 0.004) + if section == 3 and j % 2: + mix.add("hats", hat(0.32, True), base + x + jitter, pan=0.3, room=0.15) + elif section == 2 and j == 7: + mix.add("hats", hat(0.3, True), base + x + jitter, pan=0.3, room=0.15) + else: + mix.add("hats", hat(0.5 if j % 2 else 0.28), base + x + jitter, pan=0.3, room=0.1) + if section > 0: + for j in range(16): + vel = (0.5, 0.22, 0.34, 0.22)[j % 4] * (1 + 0.3 * (section == 3)) + mix.add("hats", shaker(vel), base + j * 0.25 + rng.normal(0, 0.004), pan=-0.35, room=0.1) + + step = 0.5 if section < 2 else 0.25 + for j in range(int(4 / step)): + x = j * step + tones = chord_at(base + x)[2] + idx = ([0, 1, 2, 3, 2, 1, 2, 3] if section < 2 else [0, 1, 2, 3, 1, 2, 3, 2])[j % 8] + m = tones[idx] + (12 if section == 3 and j % 8 in (3, 6) else 0) + vel = (0.7 if j % 2 == 0 else 0.5) * (0.8 if step < 0.5 else 1.0) + mix.add("arp", pluck(m, vel), base + x, pan=0.4 * np.sin(j * 0.9), hall=0.2, echo=0.32) + + rhythm = ([(0, 1.8, 0), (2, 1.3, 0), (3.5, 0.4, 0)], + [(0, 1.3, 0), (1.5, 0.4, 0), (2, 1.3, 0), (3.5, 0.4, 12)], + [(0, .9, 0), (1, .4, 0), (1.5, .4, 12), (2, .9, 0), (3, .4, 0), (3.5, .4, 12)], + [(0, .9, 0), (1, .4, 0), (1.5, .4, 12), (2, .9, 0), (3, .4, 0), (3.5, .4, 12)])[section] + if bar in BREAKS: + rhythm = [r for r in rhythm if r[0] < 2] + [(2, 1.9, 0)] + for x, d, octave in rhythm: + mix.add("bass", bass(chord_at(base + x)[0] + octave, d * 0.625, 0.8 if octave else 1.0), base + x) + + kicks.append(84) + mix.add("kick", kick(1.0), 84, room=0.05) + mix.add("bass", bass(29, 5.0, 1.0, decay=1.8), 84) + strengths = {3: (0.6, 0.6), 7: (0.55, 0.45), 11: (0.75, 0.7), 17: (1.0, 1.0), 22: (0.9, 1.0)} + for bar in LIFTS: + beat = (bar - 1) * 4 + rise, hit = strengths[bar] + mix.add("fx", riser(rise, chord_at(beat)[1]), beat - 2, hall=0.3) + mix.add("fx", impact(hit, 4.0 if bar == 22 else 2.4), beat, hall=0.45) + return np.array(kicks) + + +def duck(kicks, depth): + g = np.ones(N + TAIL) + shape = np.minimum(1, secs(int(0.5 * SR)) / 0.004) * np.exp(-secs(int(0.5 * SR)) / 0.13) + for k in kicks: + i = sample(k) + n = min(len(shape), len(g) - i) + g[i:i + n] = np.minimum(g[i:i + n], 1 - depth * shape[:n]) + return g[:, None] + + +def run_board(board, x): + return board(x.T.astype(np.float32), SR).T.astype(np.float64) + + +def true_peak(x): + return np.abs(signal.resample_poly(x, 4, 1, axis=0)).max() + + +def limit(x, ceiling_db): + """Look-ahead limiter on 4x-oversampled peaks, release 120 ms.""" + c = 10 ** (ceiling_db / 20) + peak = np.abs(signal.resample_poly(x, 4, 1, axis=0)).max(1)[:len(x) * 4].reshape(-1, 4).max(1) + need = np.minimum(1, c / np.maximum(peak, 1e-9)) + look = 96 + red = 1 - minimum_filter1d(need, 2 * look + 1) + r, a = np.empty_like(red), np.exp(-1 / (0.12 * SR)) + acc = 0.0 + for i, v in enumerate(red): + acc = v if v > acc else acc * a + r[i] = acc + return x * uniform_filter1d(1 - r, look)[:, None] + + +def master(mix, kicks): + s = mix.stems + gains = GAINS + pad_lp = curve([(0, 220), (4.9, 1700), (5, 1500), (14.9, 1700), (15, 1900), (24.9, 2200), + (25, 2300), (39.9, 2700), (40, 3000), (52.4, 3100), (52.5, 2600), (59, 450), (70, 450)], True) + pad_bus = sweep(s["pad"] + s["strings"] * gains["strings"] / gains["pad"], pad_lp, "low", 4) + pad_bus = run_board(pb.Pedalboard([pb.Chorus(rate_hz=0.22, depth=0.3, centre_delay_ms=9, mix=0.45)]), pad_bus) + piano = peaking(filt(s["piano"], "high", 90), 2800, -2.5, 0.9) + dry = (piano * gains["piano"] + pad_bus * gains["pad"] * duck(kicks, 0.4) + + s["arp"] * gains["arp"] * duck(kicks, 0.3) + s["bell"] * gains["bell"] + + s["bass"] * gains["bass"] * duck(kicks, 0.55) + s["kick"] * gains["kick"] + + s["snare"] * gains["snare"] + s["hats"] * gains["hats"] + s["fx"] * gains["fx"]) + sends = mix.sends + echo = pingpong(sends["echo"] * 0.2) + hall = reverb((sends["hall"] + echo * 0.5) * 0.4, impulse(3.6, (3.4, 2.6, 1.3), 7)) + room = reverb(sends["room"] * 0.25, impulse(0.9, (0.6, 0.5, 0.3), 11)) + wet = filt(hall, "high", 160) * duck(kicks, 0.25) + filt(room, "high", 200) + echo * 0.6 + ride = curve([(0, -10), (4.97, -4.5), (5.0, -3.2), (14.97, -3.2), (15, -2.2), (24.97, -2.2), + (25, -1.1), (39.97, -1.1), (40, 0.8), (52.47, 0.8), (52.5, 0.4), (70, 0.4)]) + x = filt(dry + wet, "high", 28) * 10 ** (ride(secs(N + TAIL)) / 20)[:, None] + x = peaking(x, 2600, -2.0, 0.8) + x = x + 0.25 * filt(x, "high", 8000) + x = run_board(pb.Pedalboard([pb.Compressor(threshold_db=-20, ratio=1.8, attack_ms=25, release_ms=250)]), x) + x = x[:N] + t = secs(N) + x *= np.clip(np.cos(np.clip((t - 57.0) / 2.3, 0, 1) * np.pi / 2), 0, 1)[:, None] ** 2 + x = edges(x, 0.002, 0.01) + meter = pyln.Meter(SR) + for _ in range(3): + x *= 10 ** ((TARGET_LUFS - meter.integrated_loudness(x)) / 20) + x = np.tanh(x * 1.1) / 1.1 + x = limit(x, CEILING_DB) + stems = {k: 20 * np.log10(np.sqrt((v[:N] ** 2).mean()) * gains.get(k, 1) + 1e-12) for k, v in s.items()} + return x, meter.integrated_loudness(x), stems + + +def write(path, x): + x = np.atleast_2d(x.T).T + q = np.round(np.clip(x, -1, 1 - 2 ** -23) * 2 ** 23).astype("=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/generator/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mediabunny/aac-encoder": { + "version": "1.61.3", + "resolved": "https://registry.npmjs.org/@mediabunny/aac-encoder/-/aac-encoder-1.61.3.tgz", + "integrity": "sha512-SH4DdqqUJRqIErXbuxIze0ACbSyi3Rau90skDwp8YbwopFYm2dgPidLPbwqdmTd/sESHwq7Nf3nDbczAxtcVmA==", + "license": "MPL-2.0", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + }, + "peerDependencies": { + "mediabunny": "^1.0.0" + } + }, + "node_modules/@mediabunny/flac-encoder": { + "version": "1.61.3", + "resolved": "https://registry.npmjs.org/@mediabunny/flac-encoder/-/flac-encoder-1.61.3.tgz", + "integrity": "sha512-j/WaaxIKA1KtyplrRLkSEb68+TMkMfg854CPm/R5Luf0KEx6maZabuHtARmoZ25PsFoyTgutJXLiVupyF7V/jw==", + "license": "MPL-2.0", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + }, + "peerDependencies": { + "mediabunny": "^1.0.0" + } + }, + "node_modules/@mediabunny/mp3-encoder": { + "version": "1.61.3", + "resolved": "https://registry.npmjs.org/@mediabunny/mp3-encoder/-/mp3-encoder-1.61.3.tgz", + "integrity": "sha512-Qmegig3hXp8dhbJnze1fumsC7GXkMp6NH5mGgvl5o4zzp2cYgmjjbWps5mPyRq3VHcxLbDdiXDNVcl4Mv7c1tQ==", + "license": "MPL-2.0", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + }, + "peerDependencies": { + "mediabunny": "^1.0.0" + } + }, + "node_modules/@module-federation/error-codes": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.22.0.tgz", + "integrity": "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==", + "license": "MIT" + }, + "node_modules/@module-federation/runtime": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.22.0.tgz", + "integrity": "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.22.0", + "@module-federation/runtime-core": "0.22.0", + "@module-federation/sdk": "0.22.0" + } + }, + "node_modules/@module-federation/runtime-core": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.22.0.tgz", + "integrity": "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.22.0", + "@module-federation/sdk": "0.22.0" + } + }, + "node_modules/@module-federation/runtime-tools": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.22.0.tgz", + "integrity": "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.22.0", + "@module-federation/webpack-bundler-runtime": "0.22.0" + } + }, + "node_modules/@module-federation/sdk": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.22.0.tgz", + "integrity": "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==", + "license": "MIT" + }, + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.22.0.tgz", + "integrity": "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.22.0", + "@module-federation/sdk": "0.22.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", + "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@remotion/bundler": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/bundler/-/bundler-4.0.535.tgz", + "integrity": "sha512-K306vh+WMXbTIw7v+i6916p2D2Der2W0u40sDBqh6fkZ7sdqE27ePR2oXw0RhN2S0UwoknaBiiC9zqb20v9Tdg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/studio": "4.0.535", + "@remotion/studio-shared": "4.0.535", + "@remotion/timeline-utils": "4.0.535", + "@rspack/core": "1.7.11", + "@rspack/plugin-react-refresh": "1.6.1", + "css-loader": "7.1.4", + "esbuild": "0.28.1", + "react-refresh": "0.18.0", + "remotion": "4.0.535", + "style-loader": "4.0.0", + "webpack": "5.105.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/captions": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/captions/-/captions-4.0.535.tgz", + "integrity": "sha512-eluB1X7J0zYlzbgCbq3qIRJQuFEoVuyGV2Rs3bkYzPyVFAsjV/C8otk/ecaWbqyloWRhonUsLOQ9Yof6uP0rVA==", + "license": "MIT" + }, + "node_modules/@remotion/cli": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/cli/-/cli-4.0.535.tgz", + "integrity": "sha512-wWVLG9Kx4L+aEcMGPm0JnFHGRYEwvQrCFpmmzhaxFIBdbeBx8A7mng/GmVGB59DBZ0Em8T7FIeJrT9q0Toz5Wg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@babel/parser": "7.24.1", + "@remotion/bundler": "4.0.535", + "@remotion/codemods": "4.0.535", + "@remotion/media-utils": "4.0.535", + "@remotion/player": "4.0.535", + "@remotion/renderer": "4.0.535", + "@remotion/studio": "4.0.535", + "@remotion/studio-server": "4.0.535", + "@remotion/studio-shared": "4.0.535", + "dotenv": "17.3.1", + "mediabunny": "1.61.3", + "minimist": "1.2.6", + "prompts": "2.4.2", + "remotion": "4.0.535", + "semver": "7.5.3" + }, + "bin": { + "remotion": "remotion-cli.js", + "remotionb": "remotionb-cli.js", + "remotiond": "remotiond-cli.js" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/codemods": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/codemods/-/codemods-4.0.535.tgz", + "integrity": "sha512-VusZb7BZQyhZyDQAYckKhqKmeKiZhVLdE5ml1Wyi67QARaB3aCr6vff3P3h6Dpk9yq5QGMjvy2mNnz4JioaP6w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "7.24.1", + "@babel/types": "7.24.0", + "@remotion/studio-protocol": "4.0.535", + "@remotion/studio-shared": "4.0.535", + "ast-types": "0.16.1", + "recast": "0.23.21", + "remotion": "4.0.535" + } + }, + "node_modules/@remotion/compositor-darwin-arm64": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-arm64/-/compositor-darwin-arm64-4.0.535.tgz", + "integrity": "sha512-aJCO62F3A/PWk2zXPZonn+htwN2ou6kwDde9djyBrs2a69kp4mRr9BrqaYNina9S05EJCq0Fgh3NBiy+rthrRg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@remotion/compositor-darwin-x64": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-x64/-/compositor-darwin-x64-4.0.535.tgz", + "integrity": "sha512-8OPoXULXbLCv6X2b0VVCHL+5po3tz/vm8l26ZY8i3K5ejfv5C3a6xlSJKoI9qFDtoL7ZPIIidpner/+8XkZ03Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@remotion/compositor-linux-arm64-gnu": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-gnu/-/compositor-linux-arm64-gnu-4.0.535.tgz", + "integrity": "sha512-c4nj5zQh+PCG2Beo0upSMalLWF7zH7CSxRPySNaIRjZ1+kdVMLvswUA00EAbMpTYBt4cIMajNlg+Z1mAGkJMdA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-arm64-musl": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-musl/-/compositor-linux-arm64-musl-4.0.535.tgz", + "integrity": "sha512-XMCJZZEZeK7eoqPSTFJzV00LovVLG+zsC1LHIQB0rQ846aHSawPZ/LmCcCMUJYq/e1ZPXOLWamAofOEMt7klyw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-x64-gnu": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-gnu/-/compositor-linux-x64-gnu-4.0.535.tgz", + "integrity": "sha512-gzmNFl4lw99eZjooTw0z7YMcM+Wyvi5alBaD90qZxN8EgyZWBhW2Bv7zGVURmiBC4zfHLEBAAzo/zb9EcNytIg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-x64-musl": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-musl/-/compositor-linux-x64-musl-4.0.535.tgz", + "integrity": "sha512-KS+fDLeV8xG+AFgTvFUky5jGh52EGTRr8UNWqmjlg3/x8eqrIIT6EvPm8Li3hvpiUzkmkQE5fotCm8cUaYi6EA==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-win32-x64-msvc": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/compositor-win32-x64-msvc/-/compositor-win32-x64-msvc-4.0.535.tgz", + "integrity": "sha512-xqajZBFO8LRWQTK4QFZapebRRD7Rtqn7SPt5kyXJin9c1EaLzzxVyp5QU3zDdWGQBzKM9r0UEAP0huG9zwbAMA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@remotion/elevenlabs": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/elevenlabs/-/elevenlabs-4.0.535.tgz", + "integrity": "sha512-zlahnfRRtwUKFWzjC3pjgqIFawlYLnoRqJGK58MU/nldz15x3ByIykM9w4AcR4pL/4cBC1J7pnwUFOwPxhl1Jg==", + "license": "MIT", + "dependencies": { + "@remotion/captions": "4.0.535", + "zod": "4.5.4" + } + }, + "node_modules/@remotion/licensing": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/licensing/-/licensing-4.0.535.tgz", + "integrity": "sha512-rLh99jYpCHrJVk0LHA5fvHX0YBGI4TPQnwEVmHAQyxCOGG7jw0cbKC9nYRKyOAbObfHwzwl7Dk1Xg0k0t2yqig==", + "license": "MIT" + }, + "node_modules/@remotion/media": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/media/-/media-4.0.535.tgz", + "integrity": "sha512-v2A3K05IgDQVKB/icwAhriJZ6Kdv12l91q34KGwWS+x4x64fPtizVpPuE1gBaCTcE3ZRl51U2AMXQxz8JwdBBA==", + "dependencies": { + "mediabunny": "1.61.3", + "remotion": "4.0.535", + "zod": "4.5.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/media-utils": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/media-utils/-/media-utils-4.0.535.tgz", + "integrity": "sha512-c9RW9O4QMknsNOQvjetMMPUNWfDhEfmYvRl83DtaXl7QBaGMsZDN/Xo9whm6lUluD5HL7TjoQEEgc8/sgnAfTQ==", + "license": "MIT", + "dependencies": { + "mediabunny": "1.61.3", + "remotion": "4.0.535" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/openai-whisper": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/openai-whisper/-/openai-whisper-4.0.535.tgz", + "integrity": "sha512-PjofMagv+rvrURK+AUiRGa7GSN/ZXBuRwnPFipKI2Z2mZORhYcpm3iag1hkwp0XAJhgG6peUMQ5wjJ3r6O2mPg==", + "license": "MIT", + "dependencies": { + "@remotion/captions": "4.0.535", + "zod": "4.5.4" + } + }, + "node_modules/@remotion/paths": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/paths/-/paths-4.0.535.tgz", + "integrity": "sha512-y00F4FRRkrLPORV6yavhGcWZg5ONoHWvLLSp/Pf9B+Y6dFgNiGY7F2CBhNEQM+0blOlFunZYwNLWjq+1syLmWQ==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.535" + } + }, + "node_modules/@remotion/player": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/player/-/player-4.0.535.tgz", + "integrity": "sha512-FYbw2frv8M49LB9NrAmc6A9LCRqZhI6QxeJ9tJKGB9JZg+DLaz8x3zrhuqC3tHfkbsqZOauRZSK6FbT7f8uXgg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "remotion": "4.0.535" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/renderer": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/renderer/-/renderer-4.0.535.tgz", + "integrity": "sha512-Jk4gCfF705KcJPmIJ0A0SkQnbi3Syj1hf7igBxghGmpqgjosEs3oErdmeRknPoRu2Y7VMbJDfGSww2UOZvZmwA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/licensing": "4.0.535", + "@remotion/streaming": "4.0.535", + "execa": "5.1.1", + "remotion": "4.0.535", + "source-map": "0.8.0", + "ws": "8.21.0" + }, + "optionalDependencies": { + "@remotion/compositor-darwin-arm64": "4.0.535", + "@remotion/compositor-darwin-x64": "4.0.535", + "@remotion/compositor-linux-arm64-gnu": "4.0.535", + "@remotion/compositor-linux-arm64-musl": "4.0.535", + "@remotion/compositor-linux-x64-gnu": "4.0.535", + "@remotion/compositor-linux-x64-musl": "4.0.535", + "@remotion/compositor-win32-x64-msvc": "4.0.535" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/sdk": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/sdk/-/sdk-4.0.535.tgz", + "integrity": "sha512-FvkRBbVaNVL1eMojF+vlJSG3cR+ycpNOvNsAn+lwRjdl+C5K3PgO+HjWfRNSZPDzIDA+eIRyHdjiIgtIWYRwkQ==", + "license": "Remotion License", + "dependencies": { + "@remotion/player": "4.0.535", + "@remotion/studio-shared": "4.0.535", + "remotion": "4.0.535" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/streaming": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/streaming/-/streaming-4.0.535.tgz", + "integrity": "sha512-RBjhEOsG2QiTR1SE2FCGWrJdxyOflDawhPODYaNNmwzjneoyXvk4+PwswJs9Pfn4lRIieD7xo4wxxa8AT47Uaw==", + "license": "MIT" + }, + "node_modules/@remotion/studio": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/studio/-/studio-4.0.535.tgz", + "integrity": "sha512-ZANw6ikS3v6qU5ijLd2YNVJR5ckkdGamh24L5tX9wgwJEhO+PPe3qZHD8MYJWRlAvHWapZd3v/dCIEH3P9l6ag==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@remotion/captions": "4.0.535", + "@remotion/codemods": "4.0.535", + "@remotion/elevenlabs": "4.0.535", + "@remotion/media": "4.0.535", + "@remotion/media-utils": "4.0.535", + "@remotion/openai-whisper": "4.0.535", + "@remotion/paths": "4.0.535", + "@remotion/player": "4.0.535", + "@remotion/renderer": "4.0.535", + "@remotion/sdk": "4.0.535", + "@remotion/studio-protocol": "4.0.535", + "@remotion/studio-shared": "4.0.535", + "@remotion/timeline-utils": "4.0.535", + "@remotion/web-renderer": "4.0.535", + "@remotion/zod-types": "4.0.535", + "@tanstack/react-virtual": "3.14.9", + "mediabunny": "1.61.3", + "memfs": "3.4.3", + "open": "8.4.2", + "prismjs": "1.30.0", + "remotion": "4.0.535", + "semver": "7.5.3", + "zod": "4.5.4" + }, + "peerDependencies": { + "@remotion/video-matting": "4.0.535", + "@remotion/whisper-webgpu": "4.0.535", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@remotion/video-matting": { + "optional": true + }, + "@remotion/whisper-webgpu": { + "optional": true + } + } + }, + "node_modules/@remotion/studio-protocol": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/studio-protocol/-/studio-protocol-4.0.535.tgz", + "integrity": "sha512-yPMMlrAcz8K79HyILMGtIvH+6x00c8KuBHpLCD3BSCDEXNqp20IOgoBVI8AZZD82vO1f90Jv3ePenRI2kVhL5Q==", + "license": "Remotion License", + "dependencies": { + "zod": "4.5.4" + } + }, + "node_modules/@remotion/studio-server": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/studio-server/-/studio-server-4.0.535.tgz", + "integrity": "sha512-JTFuXiUqY7z8poReLeUPqk0e368OSKVUhOAFZ4fSDfu02c3/Wqj8s3xZjgSSRso0t6zIU7kKwELQOjkF8gVvXA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "7.24.1", + "@babel/types": "7.24.0", + "@remotion/bundler": "4.0.535", + "@remotion/codemods": "4.0.535", + "@remotion/renderer": "4.0.535", + "@remotion/studio-protocol": "4.0.535", + "@remotion/studio-shared": "4.0.535", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "kiwi-schema": "0.5.0", + "memfs": "3.4.3", + "open": "8.4.2", + "recast": "0.23.21", + "remotion": "4.0.535", + "semver": "7.5.3", + "zod": "4.5.4" + } + }, + "node_modules/@remotion/studio-shared": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/studio-shared/-/studio-shared-4.0.535.tgz", + "integrity": "sha512-+gJKMERFMuKQBg6QmsArj/i7K0dECCBenmnXKlbT8FiwCiDnZG/x0mswlFsSbhjWrCtv5+uJ+YO5exDCmSj4BA==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.535" + } + }, + "node_modules/@remotion/timeline-utils": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/timeline-utils/-/timeline-utils-4.0.535.tgz", + "integrity": "sha512-98m/UtDdJI2Zfp2X380H/3MRc1kB2LHUmx0kQb7AmL3+Uipv+YUt2MClV9L51zx0n0InX47PgV28IrbtfYI25Q==", + "license": "MIT", + "dependencies": { + "mediabunny": "1.61.3", + "remotion": "4.0.535" + } + }, + "node_modules/@remotion/web-renderer": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/web-renderer/-/web-renderer-4.0.535.tgz", + "integrity": "sha512-wDd0NufeX71xNjqqbZduVP6AFXQEPHnn5gu+eI4I2W3SL7a0F3tOYsRtlP7vbZXINy/X1F7AqrKBEpne4xa8pA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@mediabunny/aac-encoder": "1.61.3", + "@mediabunny/flac-encoder": "1.61.3", + "@mediabunny/mp3-encoder": "1.61.3", + "@remotion/licensing": "4.0.535", + "mediabunny": "1.61.3", + "remotion": "4.0.535" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@remotion/zod-types": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/@remotion/zod-types/-/zod-types-4.0.535.tgz", + "integrity": "sha512-qszmVpUw47OpW77cCnkO7hGXEsn70oLXMOZi5JRJgDK3IsRlL3ttMu0k0ntACJEx1Ja0uuEfYEJVlOGxZ1JRRA==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.535" + } + }, + "node_modules/@rspack/binding": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.7.11.tgz", + "integrity": "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q==", + "license": "MIT", + "optionalDependencies": { + "@rspack/binding-darwin-arm64": "1.7.11", + "@rspack/binding-darwin-x64": "1.7.11", + "@rspack/binding-linux-arm64-gnu": "1.7.11", + "@rspack/binding-linux-arm64-musl": "1.7.11", + "@rspack/binding-linux-x64-gnu": "1.7.11", + "@rspack/binding-linux-x64-musl": "1.7.11", + "@rspack/binding-wasm32-wasi": "1.7.11", + "@rspack/binding-win32-arm64-msvc": "1.7.11", + "@rspack/binding-win32-ia32-msvc": "1.7.11", + "@rspack/binding-win32-x64-msvc": "1.7.11" + } + }, + "node_modules/@rspack/binding-darwin-arm64": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.11.tgz", + "integrity": "sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-darwin-x64": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.11.tgz", + "integrity": "sha512-a1+TtTE9ap6RalgFi7FGIgkJP6O4Vy6ctv+9WGJy53E4kuqHR0RygzaiVxCI/GMc/vBT9vY23hyrpWb3d1vtXA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-linux-arm64-gnu": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.11.tgz", + "integrity": "sha512-P0QrGRPbTWu6RKWfN0bDtbnEps3rXH0MWIMreZABoUrVmNQKtXR6e73J3ub6a+di5s2+K0M2LJ9Bh2/H4UsDUA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-arm64-musl": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.11.tgz", + "integrity": "sha512-6ky7R43VMjWwmx3Yx7Jl7faLBBMAgMDt+/bN35RgwjiPgsIByz65EwytUVuW9rikB43BGHvA/eqlnjLrUzNBqw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-gnu": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.11.tgz", + "integrity": "sha512-cuOJMfCOvb2Wgsry5enXJ3iT1FGUjdPqtGUBVupQlEG4ntSYsQ2PtF4wIDVasR3wdxC5nQbipOrDiN/u6fYsdQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-musl": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.11.tgz", + "integrity": "sha512-CoK37hva4AmHGh3VCsQXmGr40L36m1/AdnN5LEjUX6kx5rEH7/1nEBN6Ii72pejqDVvk9anEROmPDiPw10tpFg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-wasm32-wasi": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.11.tgz", + "integrity": "sha512-OtrmnPUVJMxjNa3eDMfHyPdtlLRmmp/aIm0fQHlAOATbZvlGm12q7rhPW5BXTu1yh+1rQ1/uqvz+SzKEZXuJaQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "1.0.7" + } + }, + "node_modules/@rspack/binding-win32-arm64-msvc": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.7.11.tgz", + "integrity": "sha512-lObFW6e5lCWNgTBNwT//yiEDbsxm9QG4BYUojqeXxothuzJ/L6ibXz6+gLMvbOvLGV3nKgkXmx8GvT9WDKR0mA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/binding-win32-ia32-msvc": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.7.11.tgz", + "integrity": "sha512-0pYGnZd8PPqNR68zQ8skamqNAXEA1sUfXuAdYcknIIRq2wsbiwFzIc0Pov1cIfHYab37G7sSIPBiOUdOWF5Ivw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/binding-win32-x64-msvc": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.7.11.tgz", + "integrity": "sha512-EeQXayoQk/uBkI3pdoXfQBXNIUrADq56L3s/DFyM2pJeUDrWmhfIw2UFIGkYPTMSCo8F2JcdcGM32FGJrSnU0Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/core": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.7.11.tgz", + "integrity": "sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime-tools": "0.22.0", + "@rspack/binding": "1.7.11", + "@rspack/lite-tapable": "1.1.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.1" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@rspack/lite-tapable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz", + "integrity": "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==", + "license": "MIT" + }, + "node_modules/@rspack/plugin-react-refresh": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.6.1.tgz", + "integrity": "sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw==", + "license": "MIT", + "dependencies": { + "error-stack-parser": "^2.1.4", + "html-entities": "^2.6.0" + }, + "peerDependencies": { + "react-refresh": ">=0.10.0 <1.0.0", + "webpack-hot-middleware": "2.x" + }, + "peerDependenciesMeta": { + "webpack-hot-middleware": { + "optional": true + } + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.14.9", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.9.tgz", + "integrity": "sha512-qZyr0FZDP8rDC4WBhsryIZmAd9bveJvFGUJJtskWaew6/0dTRS6wZxnR6VQ5bY2KwL3LjerrHqQLk3a0GKcPXQ==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.17.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.17.7", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.7.tgz", + "integrity": "sha512-bp+v10y65sp2H7WpWfIMyxTNfl8ZVfxFTLRjPIFRryi6FV/J33z4IS53WO4pTk36KlvJ4iLiQz+oaydDC1xbcA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/dom-mediacapture-transform": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.12.tgz", + "integrity": "sha512-d7/QsLRwF864A5mgIM/YrfiglHoYn7zgCcAoJgW404r+2DwnNr7EBbLnCWpmOMgH8y0te73L1AV6H1bmauaWFw==", + "license": "MIT", + "dependencies": { + "@types/dom-webcodecs": "*" + } + }, + "node_modules/@types/dom-webcodecs": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz", + "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz", + "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.28", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.28.tgz", + "integrity": "sha512-br0LOmjJqr6Rt4AEkadbcNf67Zpo7EPwKsWbjnRrJIa1QYFHQBqM4C7F2WTbV8ahTEcg1Eo+Y0aWjjORInYFyA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001816", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001816.tgz", + "integrity": "sha512-+8Yx7BXCoqk6IF8yTicjfUR6D/ldJzdlsodlEOiOSPmhK844WIgk67/zIQaGAOxNkZyyvBlrRFnLvqKIoqbAFw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.40", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.6.3" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-polygon": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", + "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==", + "license": "BSD-3-Clause" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "license": "ISC" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.456", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.456.tgz", + "integrity": "sha512-8hboqI6xesozteFb8aa7OGknvxUtTeRJLz0FoePysUEh/oWbddykIGdgpEJRbPRg/nIMfBf2DFos+H2MtvJl2A==", + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/flubber": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/flubber/-/flubber-0.4.2.tgz", + "integrity": "sha512-79RkJe3rA4nvRCVc2uXjj7U/BAUq84TS3KHn6c0Hr9K64vhj83ZNLUziNx4pJoBumSPhOl5VjH+Z0uhi+eE8Uw==", + "license": "MIT", + "dependencies": { + "d3-array": "^1.2.0", + "d3-polygon": "^1.0.3", + "earcut": "^2.1.1", + "svg-path-properties": "^0.2.1", + "svgpath": "^2.2.1", + "topojson-client": "^3.0.0" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "license": "Unlicense" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kiwi-schema": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/kiwi-schema/-/kiwi-schema-0.5.0.tgz", + "integrity": "sha512-X+FpfU0yTEtc6aTHS7VwbOpvQwRt70+pXXWRI5fd6CvWhe7pSVC854TVo4Zo0x5/wwcWj+/9KUlXpdcP0dY9AA==", + "license": "MIT", + "bin": { + "kiwic": "cli.js" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/mediabunny": { + "version": "1.61.3", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.61.3.tgz", + "integrity": "sha512-kUqLKVXQVwHR62GkOJxYosAmXERJbqnXBgJDTtRlxHEYviVkMg/Evhl9XN6kedqS5LP1tV4kFKyzY/V4a4ofow==", + "license": "MPL-2.0", + "workspaces": [ + ".", + "packages/*" + ], + "dependencies": { + "@types/dom-mediacapture-transform": "^0.1.11", + "@types/dom-webcodecs": "0.1.13" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + } + }, + "node_modules/memfs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.59", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.59.tgz", + "integrity": "sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", + "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/recast": { + "version": "0.23.21", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz", + "integrity": "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remotion": { + "version": "4.0.535", + "resolved": "https://registry.npmjs.org/remotion/-/remotion-4.0.535.tgz", + "integrity": "sha512-BfrVKlgo2+g3VcjFoHMhkLrJgwyX+IgEbljwKiZ6M+UCJlvpHhXkfT968iYfr+1VDvH5JWQUIUmHQhoU9PLCEw==", + "license": "SEE LICENSE IN LICENSE.md", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.5.0.tgz", + "integrity": "sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz", + "integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/svg-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.1.0.tgz", + "integrity": "sha512-bwLf38YmY+TDYHJw1Ex0Co8c4yeXuJAo8YnXGZrscxrvYoVVIvLeniEkV1Ks/54VteMnL4FtyN1+P+TZJdUPmQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/svg-path-properties": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/svg-path-properties/-/svg-path-properties-0.2.2.tgz", + "integrity": "sha512-GmrB+b6woz6CCdQe6w1GHs/1lt25l7SR5hmhF8jRdarpv/OgjLyuQygLu1makJapixeb1aQhP/Oa1iKi93o/aQ==", + "license": "ISC" + }, + "node_modules/svgpath": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.6.0.tgz", + "integrity": "sha512-OIWR6bKzXvdXYyO4DK/UWa1VA1JeKq8E+0ug2DG98Y/vOmMpfZNj+TIG988HjfYSqtcy/hFOtZq/n/j5GSESNg==", + "license": "MIT", + "funding": { + "url": "https://github.com/fontello/svg2ttf?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.51.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.51.2.tgz", + "integrity": "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.6.1.tgz", + "integrity": "sha512-8mk/LnXpQAiM2xKZhTkxikwly5GhL2Z+DWiLTkMa/E1Nfv9zURhHgiFyWvM4BWqW+LjIgL2AIS7JntEUx6v3vQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.6.0.tgz", + "integrity": "sha512-EIMmPVvNI0CYXyRPp34F2Qk7W37/BZVZIofS6LAUnrNjCVahS+hO4mxwFJDjQjODTGZDtVzLYoN2+1dYkhk9qA==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/site/video/update-0.1.5/package.json b/site/video/update-0.1.5/package.json new file mode 100644 index 0000000..90c8ad4 --- /dev/null +++ b/site/video/update-0.1.5/package.json @@ -0,0 +1,26 @@ +{ + "name": "ely-update-0-1-5", + "private": true, + "type": "module", + "scripts": { + "studio": "remotion studio src/index.ts", + "still": "remotion still src/index.ts", + "render": "remotion render src/index.ts Update out/ely-office-0.1.5.mp4", + "check": "tsc --noEmit" + }, + "dependencies": { + "@remotion/cli": "^4.0.535", + "flubber": "^0.4.2", + "react": "^19.3.1", + "react-dom": "^19.3.1", + "remotion": "^4.0.535" + }, + "devDependencies": { + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "typescript": "^7.0.2" + }, + "allowScripts": { + "esbuild@0.28.1": true + } +} diff --git a/site/video/update-0.1.5/public/fonts/OFL-NotoSansSC.txt b/site/video/update-0.1.5/public/fonts/OFL-NotoSansSC.txt new file mode 100644 index 0000000..1c9f432 --- /dev/null +++ b/site/video/update-0.1.5/public/fonts/OFL-NotoSansSC.txt @@ -0,0 +1,93 @@ +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source' + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/site/video/update-0.1.5/public/icons/ely-doc-macos.svg b/site/video/update-0.1.5/public/icons/ely-doc-macos.svg new file mode 100644 index 0000000..0e61a80 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-doc-macos.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-doc.svg b/site/video/update-0.1.5/public/icons/ely-doc.svg new file mode 100644 index 0000000..c69ce42 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-doc.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-mark-macos.svg b/site/video/update-0.1.5/public/icons/ely-mark-macos.svg new file mode 100644 index 0000000..508152d --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-mark-macos.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-mark.svg b/site/video/update-0.1.5/public/icons/ely-mark.svg new file mode 100644 index 0000000..d89af5a --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-mark.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-pdf-macos.svg b/site/video/update-0.1.5/public/icons/ely-pdf-macos.svg new file mode 100644 index 0000000..0f122e2 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-pdf-macos.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-pdf.svg b/site/video/update-0.1.5/public/icons/ely-pdf.svg new file mode 100644 index 0000000..ea8b952 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-pdf.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-show-macos.svg b/site/video/update-0.1.5/public/icons/ely-show-macos.svg new file mode 100644 index 0000000..c785b0c --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-show-macos.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-show.svg b/site/video/update-0.1.5/public/icons/ely-show.svg new file mode 100644 index 0000000..14ecd35 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-show.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-table-macos.svg b/site/video/update-0.1.5/public/icons/ely-table-macos.svg new file mode 100644 index 0000000..e972873 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-table-macos.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-table.svg b/site/video/update-0.1.5/public/icons/ely-table.svg new file mode 100644 index 0000000..9766f08 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-table.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-tree-macos.svg b/site/video/update-0.1.5/public/icons/ely-tree-macos.svg new file mode 100644 index 0000000..9223edb --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-tree-macos.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/public/icons/ely-tree.svg b/site/video/update-0.1.5/public/icons/ely-tree.svg new file mode 100644 index 0000000..d3d0bb6 --- /dev/null +++ b/site/video/update-0.1.5/public/icons/ely-tree.svg @@ -0,0 +1 @@ + diff --git a/site/video/update-0.1.5/record/drive.py b/site/video/update-0.1.5/record/drive.py new file mode 100644 index 0000000..a123dee --- /dev/null +++ b/site/video/update-0.1.5/record/drive.py @@ -0,0 +1,306 @@ +"""Real input, window placement and screen capture for the update video.""" +import ctypes +import json +import math +import os +import shutil +import subprocess +import time +import tkinter +from contextlib import contextmanager +from ctypes import wintypes +from pathlib import Path + +from PIL import ImageGrab + +user32 = ctypes.windll.user32 +user32.SetProcessDpiAwarenessContext(ctypes.c_void_p(-4)) +user32.SetWindowPos.argtypes = [wintypes.HWND, wintypes.HWND, ctypes.c_int, ctypes.c_int, ctypes.c_int, + ctypes.c_int, wintypes.UINT] +ctypes.windll.winmm.timeBeginPeriod(1) + +# The captured region, in physical pixels. +X0, Y0, W, H = 1536, 504, 2048, 1152 +APPS = Path(os.environ["LOCALAPPDATA"]) / "Programs" / "Ely Office" +DATA = Path(os.environ["LOCALAPPDATA"]) / "ElyOffice" +KEEP = DATA.with_name("ElyOffice.video-keep") +FFMPEG = shutil.which("ffmpeg") + + +class MOUSEINPUT(ctypes.Structure): + _fields_ = [("dx", wintypes.LONG), ("dy", wintypes.LONG), ("mouseData", wintypes.DWORD), + ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", ctypes.c_size_t)] + + +class KEYBDINPUT(ctypes.Structure): + _fields_ = [("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), + ("time", wintypes.DWORD), ("dwExtraInfo", ctypes.c_size_t)] + + +class _U(ctypes.Union): + _fields_ = [("mi", MOUSEINPUT), ("ki", KEYBDINPUT), ("pad", ctypes.c_byte * 32)] + + +class INPUT(ctypes.Structure): + _fields_ = [("type", wintypes.DWORD), ("u", _U)] + + +def _send(*inputs): + arr = (INPUT * len(inputs))(*inputs) + if user32.SendInput(len(inputs), arr, ctypes.sizeof(INPUT)) != len(inputs): + raise OSError("SendInput was blocked") + + +def _mouse(flags, data=0): + i = INPUT(type=0) + i.u.mi = MOUSEINPUT(0, 0, data, flags, 0, 0) + return i + + +def _key(vk=0, scan=0, flags=0): + i = INPUT(type=1) + i.u.ki = KEYBDINPUT(vk, scan, flags, 0, 0) + return i + + +VK = {"ctrl": 0x11, "shift": 0x10, "alt": 0x12, "enter": 0x0D, "esc": 0x1B, "tab": 0x09, + "up": 0x26, "down": 0x28, "left": 0x25, "right": 0x27, "back": 0x08, "space": 0x20, + "home": 0x24, "end": 0x23, "delete": 0x2E, "=": 0xBB, "-": 0xBD} +EXTENDED = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x2D, 0x2E} + + +class Take: + """One recorded clip: the capture and its input log.""" + + def __init__(self, name, out: Path): + self.name, self.out = name, out + self.events = [] + self.zero = None + self.proc = None + + # Positions are region pixels; the log keeps them so. + def log(self, kind, **fields): + self.events.append({"t": round(time.perf_counter() - self.zero, 4), "kind": kind, **fields}) + + def start(self): + path = self.out / f"{self.name}.mp4" + grab = (f"ddagrab=output_idx=0:framerate=60:offset_x={X0}:offset_y={Y0}:" + f"video_size={W}x{H}:draw_mouse=0,hwdownload,format=bgra") + self.proc = subprocess.Popen( + [FFMPEG, "-hide_banner", "-loglevel", "error", "-y", "-f", "lavfi", "-i", grab, + "-c:v", "h264_nvenc", "-preset", "p7", "-tune", "hq", "-rc", "vbr", "-cq", "14", + "-pix_fmt", "yuv420p", str(path)], stdin=subprocess.PIPE) + self.zero = time.perf_counter() + time.sleep(1.5) + if self.proc.poll() is not None: + raise RuntimeError("the capture stopped at once") + # A square flashes in a corner; a frame diff finds it. + front = user32.GetForegroundWindow() + flash = tkinter.Tk() + flash.overrideredirect(True) + flash.attributes("-topmost", True) + flash.configure(bg="#ff00ff") + flash.geometry(f"60x60+{X0 + W - 90}+{Y0 + H - 90}") + flash.update() + self.log("sync", x=W - 60, y=H - 60) + time.sleep(0.3) + flash.destroy() + _send(_key(0x12), _key(0x12, 0, 2)) + user32.SetForegroundWindow(front) + time.sleep(0.5) + if user32.GetForegroundWindow() != front: + raise RuntimeError("the app lost the foreground to the sync flash") + + def stop(self): + time.sleep(0.8) + self.proc.communicate(b"q") + if self.proc.returncode != 0: + raise RuntimeError(f"the capture failed: {self.proc.returncode}") + (self.out / f"{self.name}.json").write_text(json.dumps(self.events, ensure_ascii=False, indent=1), + encoding="utf-8") + + +TAKE: Take | None = None + + +def _log(kind, **fields): + if TAKE is not None and TAKE.zero is not None: + TAKE.log(kind, **fields) + + +def where(): + p = wintypes.POINT() + user32.GetCursorPos(ctypes.byref(p)) + return p.x - X0, p.y - Y0 + + +def _ease(t): + return 0.5 - 0.5 * math.cos(math.pi * t) + + +def move_to(x, y, dur=0.6, path=None): + """Moves the pointer to region (x, y); `path` bends the line.""" + sx, sy = where() + steps = max(1, int(dur * 240)) + start = time.perf_counter() + for i in range(1, steps + 1): + f = _ease(i / steps) + px, py = (path(f) if path else (sx + (x - sx) * f, sy + (y - sy) * f)) + user32.SetCursorPos(int(round(X0 + px)), int(round(Y0 + py))) + _log("move", x=round(px, 1), y=round(py, 1)) + target = start + i * dur / steps + while time.perf_counter() < target: + time.sleep(0.0005) + user32.SetCursorPos(X0 + int(x), Y0 + int(y)) + + +def press(right=False): + x, y = where() + down, up = (0x8, 0x10) if right else (0x2, 0x4) + _send(_mouse(down)) + _log("down", x=x, y=y, right=right) + time.sleep(0.07) + _send(_mouse(up)) + _log("up", x=x, y=y, right=right) + + +def click(x, y, dur=0.6, right=False, settle=0.12): + move_to(x, y, dur) + time.sleep(settle) + press(right) + + +def wheel(notches, gap=0.09): + """Scrolls `notches` (negative is down), one notch at a time.""" + step = -120 if notches < 0 else 120 + for _ in range(abs(notches)): + _send(_mouse(0x0800, step & 0xFFFFFFFF)) + x, y = where() + _log("wheel", x=x, y=y, down=notches < 0) + time.sleep(gap) + + +def drag(x, y, dur=1.0): + sx, sy = where() + _send(_mouse(0x2)) + _log("down", x=sx, y=sy, right=False) + time.sleep(0.15) + move_to(x, y, dur) + time.sleep(0.15) + _send(_mouse(0x4)) + _log("up", x=x, y=y, right=False) + + +def key(chord, label=None): + codes = [VK[k] if k in VK else ord(k.upper()) for k in chord.split("+")] + downs = [_key(c, 0, 1 if c in EXTENDED else 0) for c in codes] + ups = [_key(c, 0, (1 if c in EXTENDED else 0) | 2) for c in reversed(codes)] + _send(*downs) + _log("key", key=label or chord) + time.sleep(0.05) + _send(*ups) + + +def type_text(text, cps=9.0): + """Types `text` as Unicode characters at a human pace.""" + for ch in text: + if ch == "\n": + key("enter") + else: + code = ord(ch) + _send(_key(0, code, 4), _key(0, code, 4 | 2)) + _log("char", ch=ch) + time.sleep(1 / cps) + + +def window_of(pid, timeout=60): + found = [] + + @ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM) + def each(h, _): + owner = wintypes.DWORD() + user32.GetWindowThreadProcessId(h, ctypes.byref(owner)) + if owner.value == pid and user32.IsWindowVisible(h) and not user32.GetWindow(h, 4): + found.append(h) + return True + + deadline = time.time() + timeout + while time.time() < deadline: + found.clear() + user32.EnumWindows(each, 0) + if found: + return found[0] + time.sleep(0.2) + raise RuntimeError(f"process {pid} showed no window") + + +def place(h): + user32.ShowWindow(h, 9) + if not user32.SetWindowPos(h, -1, X0, Y0, W, H, 0x40): + raise ctypes.WinError() + # The visible frame, without DWM's invisible border, fills the region. + seen, outer = wintypes.RECT(), wintypes.RECT() + ctypes.windll.dwmapi.DwmGetWindowAttribute(h, 9, ctypes.byref(seen), ctypes.sizeof(seen)) + user32.GetWindowRect(h, ctypes.byref(outer)) + left, top = seen.left - outer.left, seen.top - outer.top + right, bottom = outer.right - seen.right, outer.bottom - seen.bottom + user32.SetWindowPos(h, -1, X0 - left, Y0 - top, W + left + right, H + top + bottom, 0x40) + # Alt lets this process take the foreground. + _send(_key(0x12), _key(0x12, 0, 2)) + user32.SetForegroundWindow(h) + time.sleep(0.4) + if user32.GetForegroundWindow() != h: + raise RuntimeError("the app is not in front; no input sent") + + +def shot(path, scale=0.5): + img = ImageGrab.grab(bbox=(X0, Y0, X0 + W, Y0 + H), all_screens=True) + img.resize((int(W * scale), int(H * scale))).save(path) + + +def launch(app, exe, *files, wait=6.0): + env = dict(os.environ, ELY_LOCALE="zh-CN", ELY_APPEARANCE="dark", ELY_LOG="info", ELY_NO_DIALOG="1") + proc = subprocess.Popen([str(APPS / app / exe), *map(str, files)], env=env) + h = window_of(proc.pid) + place(h) + time.sleep(wait) + place(h) + return proc, h + + +def quit_all(): + for exe in ("ely-doc", "ely-table", "ely-show", "ely-mark", "ely-pdf", "ely-tree"): + subprocess.run(["taskkill", "/F", "/IM", f"{exe}.exe"], capture_output=True) + for exe in ("ely-lok-worker", "ely-pdf-worker"): + subprocess.run(["taskkill", "/F", "/IM", f"{exe}.exe"], capture_output=True) + time.sleep(1.5) + + +@contextmanager +def fresh_data(): + """The user's ElyOffice data waits aside; recordings start clean.""" + if KEEP.exists(): + raise RuntimeError(f"{KEEP} exists: restore it by hand first") + quit_all() + if DATA.exists(): + DATA.rename(KEEP) + try: + yield + finally: + quit_all() + if DATA.exists(): + shutil.rmtree(DATA) + if KEEP.exists(): + KEEP.rename(DATA) + + +@contextmanager +def take(name, out: Path): + global TAKE + TAKE = Take(name, out) + TAKE.start() + try: + yield TAKE + finally: + TAKE.stop() + TAKE = None diff --git a/site/video/update-0.1.5/record/explore.py b/site/video/update-0.1.5/record/explore.py new file mode 100644 index 0000000..0b47aae --- /dev/null +++ b/site/video/update-0.1.5/record/explore.py @@ -0,0 +1,15 @@ +# Usage: explore.py [python actions] +import sys +import time +from pathlib import Path + +from drive import * # noqa: F403 + +app, exe, file, out = sys.argv[1:5] +actions = sys.argv[5] if len(sys.argv) > 5 else "" +with fresh_data(): + files = [] if file == "-" else [Path(file).resolve()] + proc, h = launch(app, exe, *files) + exec(actions) + time.sleep(0.6) + shot(out) diff --git a/site/video/update-0.1.5/record/prep.py b/site/video/update-0.1.5/record/prep.py new file mode 100644 index 0000000..23d46cf --- /dev/null +++ b/site/video/update-0.1.5/record/prep.py @@ -0,0 +1,32 @@ +"""Copies synced takes into public/footage with events in footage time. + +Usage: prep.py +Writes public/footage/.mp4 and .json: {fps, size, events, menus}. +Event times are footage seconds; positions are footage pixels (2048×1152). +""" +import json +import shutil +import sys +from pathlib import Path + +TAKES = ["menus", "cells", "doc", "table", "show", "more"] +# Submenu panels as recorded (x0, y0, x1, y1), for the safe triangle. +SUBMENUS = { + "menus": {"parent": [1100, 610, 1396, 652], "panel": [1394, 576, 1784, 1144]}, + "doc": {"parent": [1072, 702, 1370, 742], "panel": [1364, 698, 1660, 874]}, + "show": {"parent": [1182, 598, 1478, 638], "panel": [1474, 594, 1770, 774]}, +} + +root = Path(sys.argv[1]) +out = Path(__file__).resolve().parent.parent / "public" / "footage" +out.mkdir(parents=True, exist_ok=True) +for name in TAKES: + offset = json.loads((root / f"{name}.sync.json").read_text())["offset"] + events = json.loads((root / f"{name}.json").read_text(encoding="utf-8")) + for event in events: + event["t"] = round(event["t"] + offset, 4) + shutil.copyfile(root / f"{name}.60.mp4", out / f"{name}.mp4") + data = {"fps": 60, "size": [2048, 1152], "events": events, "menus": SUBMENUS.get(name)} + (out / f"{name}.json").write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8") + actions = [e for e in events if e["kind"] not in ("move", "sync")] + print(name, " ".join(f"{e['kind']}@{e['t']:.2f}" for e in actions)) diff --git a/site/video/update-0.1.5/record/samples.py b/site/video/update-0.1.5/record/samples.py new file mode 100644 index 0000000..7e90f53 --- /dev/null +++ b/site/video/update-0.1.5/record/samples.py @@ -0,0 +1,100 @@ +# Writes the sample files the recordings open. Usage: samples.py +import sys +from pathlib import Path + +import docx +import openpyxl +from docx.oxml.ns import qn +from docx.shared import Pt +from openpyxl.styles import Font +from pptx import Presentation +from pptx.util import Inches, Pt as PPt + +out = Path(sys.argv[1]) +out.mkdir(parents=True, exist_ok=True) + +ROWS = [ + ("模块", "负责人", "状态", "进度"), + ("编辑器", "林夏", "已完成", "100%"), + ("表格", "周沐", "进行中", "72%"), + ("演示", "陈一", "进行中", "58%"), + ("发布", "许言", "未开始", "0%"), +] + +mark = """# 0.1.5 发布计划 + +这一版的重点是**更顺手**:菜单、表格与右键都照常用的软件来。 + +| 模块 | 负责人 | 状态 | 进度 | +| --- | --- | --- | --- | +| 编辑器 | 林夏 | **已完成** | 100% | +| 表格 | 周沐 | 进行中 | 72% | +| 演示 | 陈一 | 进行中 | 58% | +| 发布 | 许言 | 未开始 | 0% | + +> [!TIP] +> 点任意一格即可在格内编辑,Tab 到下一格。 + +## 下一步 + +- 收集反馈 :rocket: +- 整理文档 +""" +(out / "发布计划.md").write_text(mark, encoding="utf-8") +(out / "会议记录.md").write_text("# 会议记录\n\n- 确认发布时间\n", encoding="utf-8") +(out / "读书笔记.md").write_text("# 读书笔记\n\n摘录与想法。\n", encoding="utf-8") + +d = docx.Document() +d.styles["Normal"].font.size = Pt(12) +# One installed face for every script: no missing-font banner. +FACE = "微软雅黑" +defaults = d.styles.element.find(qn("w:docDefaults")) +for fonts in [*defaults.iter(qn("w:rFonts")), *(style.element.get_or_add_rPr().get_or_add_rFonts() + for style in d.styles if style.type == 1)]: + for name in list(fonts.attrib): + if name.endswith("Theme"): + del fonts.attrib[name] + for attr in ("w:ascii", "w:hAnsi", "w:eastAsia", "w:cs"): + fonts.set(qn(attr), FACE) +d.add_heading("第三季度工作汇报", 1) +d.add_paragraph("本季度完成了编辑器与表格两大模块,演示模块进入收尾阶段。下表列出各模块的负责人与进度。") +t = d.add_table(rows=len(ROWS), cols=4) +t.style = "Table Grid" +for r, row in enumerate(ROWS): + for c, value in enumerate(row): + t.cell(r, c).text = value +d.add_paragraph("下季度计划:完善导出与打印,补齐快捷键。") +d.save(out / "工作汇报.docx") + +wb = openpyxl.Workbook() +ws = wb.active +ws.title = "销售" +ws.append(["地区", "一月", "二月", "三月", "合计"]) +data = [("华东", 128, 142, 160), ("华南", 96, 104, 121), ("华北", 88, 97, 110), ("西南", 64, 71, 83)] +for i, (name, *months) in enumerate(data, start=2): + ws.append([name, *months, f"=SUM(B{i}:D{i})"]) +ws.append(["合计", "=SUM(B2:B5)", "=SUM(C2:C5)", "=SUM(D2:D5)", "=SUM(E2:E5)"]) +for cell in ws[1]: + cell.font = Font(bold=True) +for col in "ABCDE": + ws.column_dimensions[col].width = 12 +wb.save(out / "季度销售.xlsx") + +p = Presentation() +p.slide_width, p.slide_height = Inches(13.333), Inches(7.5) +s = p.slides.add_slide(p.slide_layouts[0]) +s.shapes.title.text = "Ely Office 0.1.5" +s.placeholders[1].text = "更顺手的一次更新" +s = p.slides.add_slide(p.slide_layouts[5]) +s.shapes.title.text = "模块进度" +table = s.shapes.add_table(len(ROWS), 4, Inches(1.2), Inches(2), Inches(10.9), Inches(3.6)).table +for r, row in enumerate(ROWS): + for c, value in enumerate(row): + table.cell(r, c).text = value + table.cell(r, c).text_frame.paragraphs[0].runs[0].font.size = PPt(20) +for title in ("用户反馈", "编辑器", "表格", "演示", "文档", "快捷键", "右键菜单", "跨平台", "发布节奏", "下一步"): + s = p.slides.add_slide(p.slide_layouts[1]) + s.shapes.title.text = title + s.placeholders[1].text = "要点一\n要点二" +p.save(out / "季度汇报.pptx") +print("samples in", out) diff --git a/site/video/update-0.1.5/record/sync.py b/site/video/update-0.1.5/record/sync.py new file mode 100644 index 0000000..19a8069 --- /dev/null +++ b/site/video/update-0.1.5/record/sync.py @@ -0,0 +1,47 @@ +"""Finds each take's sync jump; writes footage time = log time + offset. + +Usage: sync.py ... +Converts each take to constant 60 fps (`.60.mp4`) and writes +`.sync.json` with the offset and the source start time. +""" +import json +import subprocess +import sys +from pathlib import Path + +import av +import numpy as np + + +def first_change(video: Path, x: int, y: int, after: float, until: float): + """The first frame time when the patch at (x, y) changes.""" + with av.open(str(video)) as box: + stream = box.streams.video[0] + start = float(stream.start_time * stream.time_base) if stream.start_time else 0.0 + base = None + for frame in box.decode(stream): + t = float(frame.pts * stream.time_base) - start + if t < after: + continue + if t > until: + break + patch = frame.to_ndarray(format="gray")[y - 24:y + 24, x - 24:x + 24].astype(np.int16) + if base is None: + base = patch + continue + if np.abs(patch - base).mean() > 6: + return t, start + raise RuntimeError(f"{video.name}: no sync jump between {after} and {until} s") + + +root = Path(sys.argv[1]) +for name in sys.argv[2:]: + events = json.loads((root / f"{name}.json").read_text(encoding="utf-8")) + sync = next(e for e in events if e["kind"] == "sync") + seen, start = first_change(root / f"{name}.mp4", sync["x"], sync["y"], sync["t"] - 0.5, sync["t"] + 1.5) + offset = round(seen - sync["t"], 4) + (root / f"{name}.sync.json").write_text(json.dumps({"offset": offset, "start": start}), encoding="utf-8") + subprocess.run(["ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-i", str(root / f"{name}.mp4"), + "-vf", "fps=60", "-c:v", "h264_nvenc", "-preset", "p7", "-rc", "vbr", "-cq", "14", + "-g", "30", "-pix_fmt", "yuv420p", str(root / f"{name}.60.mp4")], check=True) + print(f"{name}: jump seen at {seen:.3f} s, logged at {sync['t']:.3f} s, offset {offset:+.3f} s") diff --git a/site/video/update-0.1.5/record/takes.py b/site/video/update-0.1.5/record/takes.py new file mode 100644 index 0000000..6a29b8c --- /dev/null +++ b/site/video/update-0.1.5/record/takes.py @@ -0,0 +1,156 @@ +"""The recorded takes. Usage: takes.py [take ...]""" +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path + +from drive import * # noqa: F403 +from drive import _log + + +def menus(files): + """ElyMark: right-click › 表格 ▸, a diagonal over 插入 into the submenu.""" + proc, h = launch("ElyMark", "ely-mark.exe", files / "发布计划.md") + with take("menus", OUT): + move_to(900, 520, 0.8) + time.sleep(0.4) + click(1100, 394, 0.8, right=True) + time.sleep(0.9) + move_to(1200, 632, 0.7) + time.sleep(1.1) + # Across 插入, which has its own submenu, to 下移该行. + move_to(1490, 824, 0.95) + time.sleep(0.8) + press() + time.sleep(1.6) + move_to(1000, 900, 0.9) + time.sleep(0.8) + + +def cells(files): + """ElyMark: a cell opens alone, shows its source, Tab and arrows move.""" + proc, h = launch("ElyMark", "ely-mark.exe", files / "发布计划.md") + with take("cells", OUT): + move_to(1000, 480, 0.8) + time.sleep(0.3) + click(1236, 344, 0.8) + time.sleep(1.4) + key("tab", "Tab") + time.sleep(1.1) + key("down", "↓") + time.sleep(1.0) + key("end", "End") + time.sleep(0.3) + for _ in range(3): + key("back", "⌫") + time.sleep(0.16) + time.sleep(0.3) + type_text("85%", cps=5) + time.sleep(0.8) + key("enter", "Enter") + time.sleep(1.4) + move_to(1100, 760, 0.8) + time.sleep(0.8) + + +def doc(files): + """ElyDoc: Word-style table menu, insert a row, drag a column border.""" + proc, h = launch("ElyDoc", "ely-doc.exe", files / "工作汇报.docx", wait=8) + with take("doc", OUT): + move_to(1000, 760, 0.8) + time.sleep(0.3) + click(1070, 634, 0.7) + time.sleep(0.4) + click(1070, 634, 0.2, right=True) + time.sleep(0.9) + move_to(1140, 722, 0.6) + time.sleep(0.9) + # Across 删除, which has its own submenu. + move_to(1468, 848, 0.9) + time.sleep(0.7) + press() + time.sleep(1.4) + # The border between 负责人 and 状态. + move_to(962, 560, 0.8) + time.sleep(0.7) + drag(1040, 560, 1.1) + time.sleep(1.4) + + +def table(files): + """ElyTable: Excel-style column header menu, insert a column.""" + proc, h = launch("ElyTable", "ely-table.exe", files / "季度销售.xlsx", wait=8) + with take("table", OUT): + move_to(600, 500, 0.8) + time.sleep(0.3) + click(406, 208, 0.8, right=True) + time.sleep(1.1) + move_to(510, 294, 0.6) + time.sleep(0.6) + press() + time.sleep(1.6) + move_to(700, 520, 0.8) + time.sleep(0.6) + + +def show(files): + """ElyShow: the slide strip scrolls; PowerPoint-style table menu.""" + proc, h = launch("ElyShow", "ely-show.exe", files / "季度汇报.pptx", wait=8) + with take("show", OUT): + move_to(180, 640, 0.9) + time.sleep(0.3) + wheel(-9, 0.11) + time.sleep(0.6) + wheel(9, 0.11) + time.sleep(0.5) + click(180, 380, 0.6) + time.sleep(1.4) + click(1180, 530, 0.8) + time.sleep(0.4) + click(1180, 530, 0.2, right=True) + time.sleep(0.9) + move_to(1250, 618, 0.6) + time.sleep(0.8) + move_to(1578, 662, 0.8) + time.sleep(0.6) + press() + time.sleep(1.6) + + +def more(files): + """ElyMark: a second launch hands its file over; the recent list clears.""" + proc, h = launch("ElyMark", "ely-mark.exe", files / "发布计划.md", files / "读书笔记.md") + with take("more", OUT): + move_to(900, 600, 0.8) + time.sleep(0.6) + env = dict(os.environ, ELY_LOCALE="zh-CN", ELY_APPEARANCE="dark") + subprocess.Popen([str(APPS / "ElyMark" / "ely-mark.exe"), str(files / "会议记录.md")], env=env) + _log("launch", file="会议记录.md") + time.sleep(2.6) + click(166, 1082, 1.0) + time.sleep(1.8) + move_to(700, 700, 0.8) + time.sleep(0.6) + + +OUT = Path(sys.argv[1]) +OUT.mkdir(parents=True, exist_ok=True) +SAMPLES = Path(sys.argv[2]) +ALL = {"menus": menus, "cells": cells, "doc": doc, "table": table, "show": show, "more": more} +names = sys.argv[3:] or list(ALL) +with fresh_data(): + for name in names: + # Each take opens untouched copies under the same names. + with tempfile.TemporaryDirectory(prefix="ely-video-") as tmp: + files = Path(tmp) / "样例" + shutil.copytree(SAMPLES, files) + try: + ALL[name](files) + finally: + quit_all() + # The next take starts without this one's session. + if DATA.exists(): + shutil.rmtree(DATA) + print("recorded", name, flush=True) diff --git a/site/video/update-0.1.5/src/Root.tsx b/site/video/update-0.1.5/src/Root.tsx new file mode 100644 index 0000000..648c9b5 --- /dev/null +++ b/site/video/update-0.1.5/src/Root.tsx @@ -0,0 +1,7 @@ +import { Composition } from 'remotion'; +import { FPS, HEIGHT, TOTAL, WIDTH } from './theme'; +import { Video } from './Video'; + +export const Root: React.FC = () => ( + +); diff --git a/site/video/update-0.1.5/src/Video.tsx b/site/video/update-0.1.5/src/Video.tsx new file mode 100644 index 0000000..dcd76ca --- /dev/null +++ b/site/video/update-0.1.5/src/Video.tsx @@ -0,0 +1,53 @@ +import { AbsoluteFill, Audio, Sequence, getStaticFiles, staticFile } from 'remotion'; +import './design/fonts'; +import { Background } from './design/Background'; +import { Intro } from './scenes/Intro'; +import { Outro } from './scenes/Outro'; +import { Menus } from './scenes/Menus'; +import { Cells } from './scenes/Cells'; +import { RightClick } from './scenes/RightClick'; +import { More } from './scenes/More'; +import { SECTION } from './theme'; + +// Set false to render without the score. +const MUSIC = true; +const MUSIC_FILE = 'audio/music.wav'; +const hasMusic = () => getStaticFiles().some((file) => file.name.split('\\').join('/') === MUSIC_FILE); + +// Whooshes at each morph, impact on the outro downbeat. +const MORPHS = [300, 840, 1440, 2340, 3090]; + +const at = (section: { from: number; duration: number }) => ({ from: section.from, durationInFrames: section.duration }); + +export const Video: React.FC = () => ( + + + + + + + + + + + + + + + + + + + + + {MORPHS.map((frame) => ( + + + ))} + + + {MUSIC && hasMusic() ? +); diff --git a/site/video/update-0.1.5/src/design/Background.tsx b/site/video/update-0.1.5/src/design/Background.tsx new file mode 100644 index 0000000..c29451e --- /dev/null +++ b/site/video/update-0.1.5/src/design/Background.tsx @@ -0,0 +1,24 @@ +import { AbsoluteFill, useCurrentFrame } from 'remotion'; +import { COLOR, HEIGHT, WIDTH } from '../theme'; + +// Near-black field with a faint lift at the centre and fine film grain. The grain +// changes every other frame (30 Hz) from a seed fixed by the frame number. +export const Background: React.FC = () => { + const frame = useCurrentFrame(); + const seed = Math.floor(frame / 2) % 997; + return ( + + + + + + + + + + + + ); +}; diff --git a/site/video/update-0.1.5/src/design/Caption.tsx b/site/video/update-0.1.5/src/design/Caption.tsx new file mode 100644 index 0000000..dc7ae4c --- /dev/null +++ b/site/video/update-0.1.5/src/design/Caption.tsx @@ -0,0 +1,50 @@ +import { interpolate, useCurrentFrame } from 'remotion'; +import { COLOR, EASE, FONT, STAGE } from '../theme'; + +export type CaptionProps = { + text: string; + // Section-local frames: starts entering at `from`, gone by `to`. + from: number; + to: number; + // Centre of the pill in composition pixels (default: bottom centre of the stage). + x?: number; + y?: number; +}; + +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; + +// A glass pill with one line of explanation, over the stage. +export const Caption: React.FC = ({ text, from, to, x = STAGE.x + STAGE.w / 2, y = STAGE.y + STAGE.h - 64 }) => { + const frame = useCurrentFrame(); + if (frame < from || frame >= to) return null; + const p = interpolate(frame, [from, from + 22], [0, 1], { ...clamp, easing: EASE.out }); + const q = interpolate(frame, [to - 16, to], [0, 1], { ...clamp, easing: EASE.in }); + const blur = (1 - p) * 6 + q * 5; + return ( +
0.05 ? `blur(${blur.toFixed(2)}px)` : undefined, + padding: '13px 26px 14px', + borderRadius: 16, + background: 'rgba(22,22,24,0.66)', + backdropFilter: 'blur(18px) saturate(1.2)', + border: '1px solid rgba(255,255,255,0.09)', + boxShadow: '0 12px 40px rgba(0,0,0,0.42)', + fontFamily: FONT.sans, + fontWeight: 500, + fontSize: 26, + lineHeight: '34px', + letterSpacing: '0.03em', + color: COLOR.ink, + whiteSpace: 'nowrap', + }} + > + {text} +
+ ); +}; diff --git a/site/video/update-0.1.5/src/design/Heading.tsx b/site/video/update-0.1.5/src/design/Heading.tsx new file mode 100644 index 0000000..cf979d6 --- /dev/null +++ b/site/video/update-0.1.5/src/design/Heading.tsx @@ -0,0 +1,203 @@ +import { Img, interpolate, staticFile, useCurrentFrame } from 'remotion'; +import { mix } from './Shape'; +import { type AppId, COLOR, EASE, FONT, HEADING, STAGE, appOf } from '../theme'; + +export type HeadingSpec = { + title: string; + sub?: string; + // App chip at the right edge, above the stage. + app?: AppId; + // Section-local frame the title starts to enter (default: beat 1). + at?: number; + // Section-local frame it starts to leave (default: the next heading's + // entrance, or HEADING.out frames before the section ends). + until?: number; +}; + +const ENTER = 28; +const LEAVE = 22; + +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; + +// Per-character rise, unblur and fade; leaving drifts on up and dissolves. +// The first `keepIn` characters are already on screen (shared with the previous +// heading); the first `keepOut` stay for the next one until `handoff`. +const Kinetic: React.FC<{ + text: string; + local: number; + leave: number; + stagger: number; + rise: number; + keepIn?: number; + keepOut?: number; + handoff?: number; +}> = ({ text, local, leave, stagger, rise, keepIn = 0, keepOut = 0, handoff = Infinity }) => { + const chars = Array.from(text); + return ( + <> + {chars.map((char, i) => { + const kept = i < keepOut; + if (kept && local >= handoff) return {char}; + const order = i - keepIn; + const p = i < keepIn ? 1 : interpolate(local - order * stagger, [0, ENTER], [0, 1], { ...clamp, easing: EASE.out }); + const q = kept ? 0 : interpolate(local - leave - (i - keepOut) * stagger * 0.55, [0, LEAVE], [0, 1], { ...clamp, easing: EASE.in }); + const y = (1 - p) * rise - q * rise * 0.6; + const blur = (1 - p) * 9 + q * 7; + return ( + 0.05 ? `blur(${blur.toFixed(2)}px)` : undefined, + }} + > + {char} + + ); + })} + + ); +}; + +// One section title above the stage's left edge, with an optional app chip. +export type HeadingTiming = { + spec: HeadingSpec; + enter: number; + leave: number; + // Characters shared with the previous / next heading of the section. + keepIn: number; + keepOut: number; + // Section-local frame the next heading takes over (null for the last). + handoff: number | null; + // Rule colour of the previous heading, which this one eases from. + fromColor: string | null; +}; + +export const Heading: React.FC<{ timing: HeadingTiming; color: string }> = ({ timing, color }) => { + const { spec, enter, leave, keepIn, keepOut, handoff, fromColor } = timing; + const frame = useCurrentFrame(); + const local = frame - enter; + const until = handoff ?? leave + LEAVE + 40; + if (local < 0 || frame >= until) return null; + const stay = leave - enter; + const subAt = HEADING.sub; + const rule = fromColor === null ? interpolate(local, [-6, 22], [0, 1], { ...clamp, easing: EASE.out }) : 1; + const ruleOut = handoff === null ? interpolate(local - stay - 8, [0, 18], [0, 1], { ...clamp, easing: EASE.inOut }) : 0; + const ruleColor = fromColor === null ? color : mix(fromColor, color, interpolate(local, [0, 24], [0, 1], { ...clamp, easing: EASE.inOut })); + const chip = interpolate(local - 10, [0, 30], [0, 1], { ...clamp, easing: EASE.out }); + const chipOut = interpolate(local - stay, [0, LEAVE], [0, 1], { ...clamp, easing: EASE.in }); + const app = spec.app ? appOf(spec.app) : null; + return ( +
+
+
+ +
+ {spec.sub ? ( +
+ +
+ ) : null} + {app ? ( +
0 ? `blur(${((1 - chip) * 6 + chipOut * 5).toFixed(2)}px)` : undefined, + }} + > + + + {app.name} + +
+ ) : null} +
+ ); +}; + +const shared = (a: string, b: string) => { + const x = Array.from(a); + const y = Array.from(b); + let n = 0; + while (n < x.length && n < y.length && x[n] === y[n]) n++; + // Keep whole words only: stop after the last space of the shared run. + const cut = x.slice(0, n).lastIndexOf(' '); + return n === x.length || n === y.length ? n : cut + 1; +}; + +// Resolve each heading's frames within a section; `accent` colours the rule. +export const headingTimes = (specs: HeadingSpec[], duration: number, accent: (spec: HeadingSpec) => string): HeadingTiming[] => + specs.map((spec, i) => { + const enter = spec.at ?? HEADING.in; + const prev = specs[i - 1]; + const next = specs[i + 1]; + const nextAt = next ? (next.at ?? HEADING.in) : null; + const leave = spec.until ?? (nextAt !== null ? nextAt - LEAVE - 6 : duration - HEADING.out); + return { + spec, + enter, + leave, + keepIn: prev ? shared(prev.title, spec.title) : 0, + keepOut: next ? shared(spec.title, next.title) : 0, + handoff: nextAt, + fromColor: prev ? accent(prev) : null, + }; + }); diff --git a/site/video/update-0.1.5/src/design/KeyCap.tsx b/site/video/update-0.1.5/src/design/KeyCap.tsx new file mode 100644 index 0000000..9a47c7e --- /dev/null +++ b/site/video/update-0.1.5/src/design/KeyCap.tsx @@ -0,0 +1,108 @@ +import { Fragment } from 'react'; +import { interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'; +import { COLOR, EASE, FONT, SPRING, STAGE } from '../theme'; + +export type KeyCapProps = { + // Keys of one chord, e.g. ['Ctrl', '1'], ['Tab'], ['右键']. + keys: string[]; + // Section-local frames: pops in at `from`, gone by `to`. + from: number; + to: number; + // Section-local frames at which the chord is pressed. + press?: number[]; + // Centre in composition pixels; default: bottom-right corner of the stage. + x?: number; + y?: number; +}; + +const MOUSE: Record = { 右键: 'right', 左键: 'left', 点击: 'left', 滚轮: 'wheel' }; + +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; + +const Mouse: React.FC<{ part: 'left' | 'right' | 'wheel'; lit: number }> = ({ part, lit }) => { + const on = `rgba(61,127,229,${(0.55 + 0.45 * lit).toFixed(3)})`; + return ( + + + {part === 'left' ? : null} + {part === 'right' ? : null} + + + + ); +}; + +// A keyboard chord or mouse button that pops in, presses on cue, and leaves. +export const KeyCap: React.FC = ({ keys, from, to, press = [], x, y }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + if (frame < from || frame >= to) return null; + const pop = spring({ frame: frame - from, fps, config: SPRING, durationInFrames: 24 }); + const out = interpolate(frame, [to - 14, to], [0, 1], { ...clamp, easing: EASE.in }); + // Down in 4 frames, back up over 12. + const down = press.reduce((acc, at) => { + const t = frame - at; + const v = t < 0 ? 0 : t < 4 ? EASE.out(t / 4) : 1 - EASE.inOut(Math.min(1, (t - 4) / 12)); + return Math.max(acc, v); + }, 0); + const anchored = x === undefined || y === undefined; + const blur = (1 - pop) * 5 + out * 4; + return ( +
0.05 ? `blur(${blur.toFixed(2)}px)` : undefined, + display: 'flex', + alignItems: 'center', + gap: 10, + }} + > + {keys.map((key, i) => { + const mouse = MOUSE[key]; + return ( + + {i > 0 ? ( + + + ) : null} +
+ {mouse ? : null} + {key} +
+
+ ); + })} +
+ ); +}; diff --git a/site/video/update-0.1.5/src/design/Shape.tsx b/site/video/update-0.1.5/src/design/Shape.tsx new file mode 100644 index 0000000..e3f8842 --- /dev/null +++ b/site/video/update-0.1.5/src/design/Shape.tsx @@ -0,0 +1,185 @@ +import type { ReactNode } from 'react'; +import { AbsoluteFill, Img, staticFile } from 'remotion'; +import { ICON, STAGE, STAGE_CENTER } from '../theme'; +import type { ShapeId } from './morph'; + +const lerp = (a: number, b: number, t: number) => a + (b - a) * t; + +export const rgba = (hex: string, alpha: number) => { + const n = parseInt(hex.slice(1), 16); + return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${alpha.toFixed(4)})`; +}; + +export const mix = (a: string, b: string, t: number) => { + const x = parseInt(a.slice(1), 16); + const y = parseInt(b.slice(1), 16); + const c = (shift: number) => Math.round(lerp((x >> shift) & 255, (y >> shift) & 255, t)); + return `#${((c(16) << 16) | (c(8) << 8) | c(0)).toString(16).padStart(6, '0')}`; +}; + +// The highlighted table cell that the cell-editing section grows out of. +const CellArt: React.FC = () => ( + + + + + + + + + + + + + + + + + + + + + + + +); + +export type Details = { shape: ShapeId; opacity: number }; + +const Artwork: React.FC<{ details: Details; size: number; cx: number; cy: number }> = ({ details, size, cx, cy }) => { + if (details.shape === 'dot' || details.opacity <= 0.001) return null; + const style: React.CSSProperties = { + position: 'absolute', + left: cx - size / 2, + top: cy - size / 2, + width: size, + height: size, + opacity: details.opacity, + }; + if (details.shape === 'cell') { + return ( +
+ +
+ ); + } + return ; +}; + +export type ShapeViewProps = { + d: string; + color: string; + // Ambient glow colour (default: `color`). + glowColor?: string; + cx: number; + cy: number; + k: number; + // App-colour wash over the content. + fill: number; + lift: number; + border?: number; + sweep?: number | null; + details?: Details[]; + // Multiplier on shadow and glow, for shapes stacked on one another. + presence?: number; + content?: { + node: ReactNode; + opacity: number; + scale: number; + blur: number; + background: string; + }; +}; + +// One morphing shape: glow, shadow, clipped content, colour wash, artwork, edge. +export const ShapeView: React.FC = ({ + d, + color, + glowColor = color, + cx, + cy, + k, + fill, + lift, + border = 0, + sweep = null, + details = [], + presence = 1, + content, +}) => { + const glowW = 400 * k; + const glowH = glowW * lerp(1, 0.62, lift); + const glowAlpha = lerp(0.3, 0.09, lift) * presence; + const shadow = `drop-shadow(0 ${lerp(6, 34, lift).toFixed(2)}px ${lerp(10, 90, lift).toFixed(2)}px rgba(0,0,0,${(lerp(0.42, 0.6, lift) * presence).toFixed(3)}))`; + const size = 192 * ICON.unit * k; + const sweepX = sweep === null ? 0 : STAGE.x - 500 + sweep * (STAGE.w + 1000); + return ( + +
+ + + {content && content.opacity > 0.001 ? ( + 0.05 ? `blur(${content.blur.toFixed(2)}px)` : undefined, + }} + > +
+ {content.node} +
+
+ ) : null} + {fill > 0.001 ? ( + + ) : null} + +
+ {details.map((detail, i) => ( + + ))} + {border > 0.001 || sweep !== null ? ( + + + + + + + + + + {sweep !== null ? ( + + ) : null} + + ) : null} +
+ ); +}; diff --git a/site/video/update-0.1.5/src/design/Stage.tsx b/site/video/update-0.1.5/src/design/Stage.tsx new file mode 100644 index 0000000..dd1aa46 --- /dev/null +++ b/site/video/update-0.1.5/src/design/Stage.tsx @@ -0,0 +1,75 @@ +import type { ReactNode } from 'react'; +import { AbsoluteFill, useCurrentFrame, useVideoConfig } from 'remotion'; +import { COLOR, ENTER_END, EXIT_LEN, STAGE, STAGE_CENTER } from '../theme'; +import { roundedRectPath } from './geometry'; +import { Heading, type HeadingSpec, headingTimes } from './Heading'; +import { type MorphState, SHAPES, STAGE_ZOOM, type ShapeId, enterState, exitState, shapePath } from './morph'; +import { ShapeView, mix } from './Shape'; + +export type StageProps = { + // Icon the stage blooms out of on the section's downbeat. + enter?: { from: ShapeId }; + // Icon the stage collapses into on the next section's downbeat. + exit?: { to: ShapeId }; + heading?: HeadingSpec | HeadingSpec[]; + // Panel colour behind the recording. + background?: string; + children?: ReactNode; +}; + +const OPEN: MorphState = { + s: 1, + k: STAGE_ZOOM, + cx: STAGE_CENTER.x, + cy: STAGE_CENTER.y, + details: 0, + fill: 0, + content: 1, + contentScale: 1, + blur: 0, + lift: 1, + border: 1, + sweep: null, +}; + +const OPEN_PATH = roundedRectPath(STAGE.x, STAGE.y, STAGE.w, STAGE.h, STAGE.r); + +// The framed screen of a footage section. Children are laid out in stage +// coordinates (0..STAGE.w, 0..STAGE.h) and clipped to the stage's shape. +export const Stage: React.FC = ({ enter, exit, heading, background = COLOR.panel, children }) => { + const frame = useCurrentFrame(); + const { durationInFrames } = useVideoConfig(); + const entering = enter && frame < ENTER_END; + const exiting = exit && frame >= durationInFrames - EXIT_LEN; + const icon: ShapeId | null = entering ? enter.from : exiting ? exit.to : null; + const state = entering ? enterState(frame) : exiting ? exitState(frame, durationInFrames) : OPEN; + const d = icon === null ? OPEN_PATH : shapePath(icon, 'stage', state.s, state.k, state.cx, state.cy); + const openColor = SHAPES[enter?.from ?? exit?.to ?? 'dot'].color; + const color = icon === null ? openColor : SHAPES[icon].color; + // The open stage glows in its own app's colour; closing hands over gradually. + const glowColor = exiting ? mix(openColor, color, 1 - state.lift) : color; + const sectionAccent = SHAPES[enter?.from ?? exit?.to ?? 'dot'].accent; + const accent = (spec: HeadingSpec) => (spec.app ? SHAPES[spec.app].accent : sectionAccent); + const headings = heading === undefined ? [] : headingTimes(Array.isArray(heading) ? heading : [heading], durationInFrames, accent); + return ( + + + {headings.map((timing) => ( + + ))} + + ); +}; diff --git a/site/video/update-0.1.5/src/design/fonts.ts b/site/video/update-0.1.5/src/design/fonts.ts new file mode 100644 index 0000000..4ad00dc --- /dev/null +++ b/site/video/update-0.1.5/src/design/fonts.ts @@ -0,0 +1,20 @@ +import { cancelRender, continueRender, delayRender, staticFile } from 'remotion'; + +const FACES = [ + { family: 'Noto Sans SC', file: 'fonts/NotoSansSC.ttf', weight: '100 900' }, + { family: 'Inter', file: 'fonts/Inter.ttf', weight: '100 900' }, + { family: 'JetBrains Mono', file: 'fonts/JetBrainsMono.ttf', weight: '100 800' }, +]; + +const handle = delayRender('Loading fonts', { timeoutInMilliseconds: 120000 }); + +Promise.all( + FACES.map(async ({ family, file, weight }) => { + const face = new FontFace(family, `url(${staticFile(file)}) format("truetype")`, { weight }); + await face.load(); + document.fonts.add(face); + }), +).then( + () => continueRender(handle), + (error: unknown) => cancelRender(error), +); diff --git a/site/video/update-0.1.5/src/design/geometry.ts b/site/video/update-0.1.5/src/design/geometry.ts new file mode 100644 index 0000000..279aa2e --- /dev/null +++ b/site/video/update-0.1.5/src/design/geometry.ts @@ -0,0 +1,186 @@ +export type Point = [number, number]; + +const TAU = Math.PI * 2; + +// Quarter (or any) arc sampled in y-down screen space; angles increase clockwise. +export const arc = (cx: number, cy: number, r: number, from: number, to: number, steps: number): Point[] => { + const out: Point[] = []; + for (let i = 0; i <= steps; i++) { + const a = from + ((to - from) * i) / steps; + out.push([cx + r * Math.cos(a), cy + r * Math.sin(a)]); + } + return out; +}; + +export const roundedRect = (x: number, y: number, w: number, h: number, r: number, steps = 10): Point[] => [ + ...arc(x + w - r, y + r, r, -TAU / 4, 0, steps), + ...arc(x + w - r, y + h - r, r, 0, TAU / 4, steps), + ...arc(x + r, y + h - r, r, TAU / 4, TAU / 2, steps), + ...arc(x + r, y + r, r, TAU / 2, (TAU * 3) / 4, steps), +]; + +export const circle = (r: number, steps = 96): Point[] => + Array.from({ length: steps }, (_, i): Point => [r * Math.cos((TAU * i) / steps - TAU / 4), r * Math.sin((TAU * i) / steps - TAU / 4)]); + +// Exact rounded rectangle as an SVG path (arcs), for the open stage. +export const roundedRectPath = (x: number, y: number, w: number, h: number, r: number) => + `M${x + r} ${y}H${x + w - r}A${r} ${r} 0 0 1 ${x + w} ${y + r}V${y + h - r}A${r} ${r} 0 0 1 ${x + w - r} ${y + h}` + + `H${x + r}A${r} ${r} 0 0 1 ${x} ${y + h - r}V${y + r}A${r} ${r} 0 0 1 ${x + r} ${y}Z`; + +export const toPath = (points: Point[], scale: number, cx: number, cy: number) => { + let d = ''; + for (let i = 0; i < points.length; i++) { + const [x, y] = points[i]; + d += `${i === 0 ? 'M' : 'L'}${(cx + x * scale).toFixed(2)} ${(cy + y * scale).toFixed(2)}`; + } + return `${d}Z`; +}; + +// ---- Signed-distance silhouettes ------------------------------------------ + +export type Sdf = (x: number, y: number) => number; + +export const sdfCircle = (cx: number, cy: number, r: number): Sdf => (x, y) => Math.hypot(x - cx, y - cy) - r; + +export const sdfCapsule = (ax: number, ay: number, bx: number, by: number, r: number): Sdf => { + const dx = bx - ax; + const dy = by - ay; + const len2 = dx * dx + dy * dy; + return (x, y) => { + const t = Math.max(0, Math.min(1, ((x - ax) * dx + (y - ay) * dy) / len2)); + return Math.hypot(x - ax - t * dx, y - ay - t * dy) - r; + }; +}; + +export const sdfRoundedBox = (x: number, y: number, w: number, h: number, r: number): Sdf => { + const cx = x + w / 2; + const cy = y + h / 2; + return (px, py) => { + const qx = Math.abs(px - cx) - w / 2 + r; + const qy = Math.abs(py - cy) - h / 2 + r; + return Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) + Math.min(Math.max(qx, qy), 0) - r; + }; +}; + +// A cubic Bézier stroke as a chain of capsules. +export const sdfCubicStroke = (p: [Point, Point, Point, Point], r: number, segments = 32): Sdf => { + const at = (t: number): Point => { + const u = 1 - t; + return [ + u * u * u * p[0][0] + 3 * u * u * t * p[1][0] + 3 * u * t * t * p[2][0] + t * t * t * p[3][0], + u * u * u * p[0][1] + 3 * u * u * t * p[1][1] + 3 * u * t * t * p[2][1] + t * t * t * p[3][1], + ]; + }; + const parts: Sdf[] = []; + for (let i = 0; i < segments; i++) { + const a = at(i / segments); + const b = at((i + 1) / segments); + parts.push(sdfCapsule(a[0], a[1], b[0], b[1], r)); + } + return union(parts); +}; + +export const union = (parts: Sdf[]): Sdf => (x, y) => { + let d = Infinity; + for (const part of parts) d = Math.min(d, part(x, y)); + return d; +}; + +// Marching squares on the zero level set; returns the longest closed loop. +export const contour = (sdf: Sdf, x0: number, y0: number, x1: number, y1: number, step: number): Point[] => { + const nx = Math.ceil((x1 - x0) / step) + 1; + const ny = Math.ceil((y1 - y0) / step) + 1; + const v = new Float64Array(nx * ny); + for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) v[j * nx + i] = sdf(x0 + i * step, y0 + j * step); + const at = (i: number, j: number) => v[j * nx + i]; + + const points = new Map(); + const links = new Map(); + const edgePoint = (key: string, ax: number, ay: number, bx: number, by: number, va: number, vb: number) => { + if (!points.has(key)) { + const t = va / (va - vb); + points.set(key, [x0 + (ax + (bx - ax) * t) * step, y0 + (ay + (by - ay) * t) * step]); + } + return key; + }; + const link = (a: string, b: string) => { + links.set(a, [...(links.get(a) ?? []), b]); + links.set(b, [...(links.get(b) ?? []), a]); + }; + + for (let j = 0; j < ny - 1; j++) { + for (let i = 0; i < nx - 1; i++) { + const a = at(i, j); + const b = at(i + 1, j); + const c = at(i + 1, j + 1); + const d = at(i, j + 1); + const top = a < 0 !== b < 0 ? edgePoint(`h${i},${j}`, i, j, i + 1, j, a, b) : null; + const right = b < 0 !== c < 0 ? edgePoint(`v${i + 1},${j}`, i + 1, j, i + 1, j + 1, b, c) : null; + const bottom = d < 0 !== c < 0 ? edgePoint(`h${i},${j + 1}`, i, j + 1, i + 1, j + 1, d, c) : null; + const left = a < 0 !== d < 0 ? edgePoint(`v${i},${j}`, i, j, i, j + 1, a, d) : null; + const crossed = [top, right, bottom, left].filter((e): e is string => e !== null); + if (crossed.length === 2) link(crossed[0], crossed[1]); + else if (crossed.length === 4) { + const centreInside = (a + b + c + d) / 4 < 0; + const aInside = a < 0; + if (aInside === centreInside) { + link(top!, right!); + link(bottom!, left!); + } else { + link(top!, left!); + link(right!, bottom!); + } + } + } + } + + const seen = new Set(); + let best: Point[] = []; + for (const start of links.keys()) { + if (seen.has(start)) continue; + const loop: Point[] = []; + let prev = ''; + let cur = start; + while (!seen.has(cur)) { + seen.add(cur); + loop.push(points.get(cur)!); + const next = links.get(cur)!.find((n) => n !== prev && !seen.has(n)); + if (!next) break; + prev = cur; + cur = next; + } + if (loop.length > best.length) best = loop; + } + return simplify(best, step * 0.12); +}; + +// Ramer–Douglas–Peucker on a closed ring. +const simplify = (ring: Point[], tolerance: number): Point[] => { + const keep = new Uint8Array(ring.length); + const walk = (a: number, b: number) => { + let worst = -1; + let dist = tolerance; + const [ax, ay] = ring[a]; + const [bx, by] = ring[b]; + const len = Math.hypot(bx - ax, by - ay) || 1; + for (let i = a + 1; i < b; i++) { + const d = Math.abs((bx - ax) * (ay - ring[i][1]) - (ax - ring[i][0]) * (by - ay)) / len; + if (d > dist) { + dist = d; + worst = i; + } + } + if (worst >= 0) { + keep[worst] = 1; + walk(a, worst); + walk(worst, b); + } + }; + const half = Math.floor(ring.length / 2); + keep[0] = 1; + keep[half] = 1; + walk(0, half); + walk(half, ring.length - 1); + keep[ring.length - 1] = 1; + return ring.filter((_, i) => keep[i] === 1); +}; diff --git a/site/video/update-0.1.5/src/design/morph.ts b/site/video/update-0.1.5/src/design/morph.ts new file mode 100644 index 0000000..5f43cb7 --- /dev/null +++ b/site/video/update-0.1.5/src/design/morph.ts @@ -0,0 +1,259 @@ +import { interpolate as flubber } from 'flubber'; +import { interpolate } from 'remotion'; +import { type AppId, EASE, ICON, MORPH, STAGE, STAGE_CENTER } from '../theme'; +import { + type Point, + circle, + contour, + roundedRect, + sdfCapsule, + sdfCircle, + sdfCubicStroke, + sdfRoundedBox, + toPath, + union, + arc, +} from './geometry'; + +export type ShapeId = AppId | 'dot' | 'cell'; + +// Silhouettes in Papirus grid units, centred on the grid centre (96, 96). +const centre = (points: Point[]): Point[] => points.map(([x, y]) => [x - 96, y - 96]); +const Q = Math.PI / 2; + +const page = centre([ + [40, 16], + [112, 16], + [160, 64], + ...arc(152, 168, 8, 0, Q, 8), + ...arc(40, 168, 8, Q, 2 * Q, 8), + ...arc(40, 24, 8, 2 * Q, 3 * Q, 8), +]); + +const board = centre( + contour( + union([sdfRoundedBox(16, 28, 160, 112, 10), sdfCapsule(96, 136, 64, 176, 6), sdfCapsule(96, 136, 128, 176, 6)]), + 0, + 0, + 192, + 192, + 0.5, + ), +); + +const branch = centre( + contour( + union([ + sdfCapsule(64, 44, 64, 148, 10), + sdfCubicStroke( + [ + [64, 128], + [64, 92], + [128, 104], + [128, 72], + ], + 10, + ), + sdfCircle(64, 40, 24), + sdfCircle(128, 64, 24), + sdfCircle(64, 152, 24), + ]), + 0, + 0, + 192, + 192, + 0.5, + ), +); + +// `color` fills the silhouette; `accent` marks the section heading. +export const SHAPES: Record = { + 'ely-doc': { points: page, color: '#3D7FE5', accent: '#3D7FE5' }, + 'ely-table': { points: page, color: '#2E9E5B', accent: '#2E9E5B' }, + 'ely-pdf': { points: page, color: '#E0344F', accent: '#E0344F' }, + 'ely-show': { points: board, color: '#F07B2A', accent: '#F07B2A' }, + 'ely-tree': { points: branch, color: '#7E57C2', accent: '#7E57C2' }, + 'ely-mark': { points: centre(roundedRect(28, 24, 136, 152, 10)), color: '#3E4A5C', accent: '#F2A900' }, + cell: { points: centre(roundedRect(16, 51, 160, 90, 8)), color: '#3E4A5C', accent: '#F2A900' }, + dot: { points: circle(15), color: '#3D7FE5', accent: '#3D7FE5' }, +}; + +// The open stage, shrunk to icon size so shape and size can morph separately. +const PROXY_W = 168; +const PROXY_H = (PROXY_W * STAGE.h) / STAGE.w; +const PROXY_R = (STAGE.r * PROXY_W) / STAGE.w; +const proxy = roundedRect(-PROXY_W / 2, -PROXY_H / 2, PROXY_W, PROXY_H, PROXY_R, 10); +// Zoom at which the proxy is exactly the stage. +export const STAGE_ZOOM = STAGE.w / (PROXY_W * ICON.unit); + +// Rings are resampled into four runs between corresponding anchors (the +// shape's outer corners), so corners travel to corners and nothing shears. +const RUN = 128; +const ANCHORS: Partial> = { + // The board's corners, not the stand's feet. + 'ely-show': centre([ + [16, 28], + [176, 28], + [176, 140], + [16, 140], + ]), +}; + +const resample = (line: Point[], count: number): Point[] => { + const lengths = [0]; + for (let i = 1; i < line.length; i++) lengths.push(lengths[i - 1] + Math.hypot(line[i][0] - line[i - 1][0], line[i][1] - line[i - 1][1])); + const total = lengths[lengths.length - 1]; + const out: Point[] = []; + let j = 1; + for (let i = 0; i < count; i++) { + const at = (total * i) / count; + while (j < line.length - 1 && lengths[j] < at) j++; + const span = lengths[j] - lengths[j - 1] || 1; + const u = (at - lengths[j - 1]) / span; + out.push([line[j - 1][0] + (line[j][0] - line[j - 1][0]) * u, line[j - 1][1] + (line[j][1] - line[j - 1][1]) * u]); + } + return out; +}; + +const prepared = new Map(); +const prepare = (id: ShapeId | 'stage'): Point[] => { + const found = prepared.get(id); + if (found) return found; + let ring = id === 'stage' ? proxy : SHAPES[id].points; + let area = 0; + for (let i = 0; i < ring.length; i++) { + const [ax, ay] = ring[i]; + const [bx, by] = ring[(i + 1) % ring.length]; + area += ax * by - bx * ay; + } + // Clockwise on screen. + if (area < 0) ring = [...ring].reverse(); + const xs = ring.map((p) => p[0]); + const ys = ring.map((p) => p[1]); + const [x0, x1, y0, y1] = [Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys)]; + const anchors = (id !== 'stage' && ANCHORS[id]) || [ + [x0, y0], + [x1, y0], + [x1, y1], + [x0, y1], + ]; + const nearest = ([ax, ay]: Point) => ring.reduce((best, p, i) => (Math.hypot(p[0] - ax, p[1] - ay) < Math.hypot(ring[best][0] - ax, ring[best][1] - ay) ? i : best), 0); + const first = nearest(anchors[0]); + ring = [...ring.slice(first), ...ring.slice(0, first)]; + const cuts = [0, ...anchors.slice(1).map(nearest), ring.length]; + if (!cuts.every((c, i) => i === 0 || c > cuts[i - 1])) throw new Error(`Anchors of ${id} are out of order`); + const out: Point[] = []; + for (let k = 0; k < 4; k++) out.push(...resample([...ring.slice(cuts[k], cuts[k + 1]), ring[cuts[k + 1] % ring.length]], RUN)); + prepared.set(id, out); + return out; +}; + +type Ring = (t: number) => Point[]; +const cache = new Map(); + +const ring = (from: ShapeId | 'stage', to: ShapeId | 'stage'): Ring => { + const key = `${from}>${to}`; + let found = cache.get(key); + if (!found) { + // Equal, aligned rings: flubber neither bisects nor needs to rotate them. + found = flubber(prepare(from), prepare(to), { string: false, maxSegmentLength: 0 }); + cache.set(key, found); + } + return found; +}; + +// Path of `from` turned `t` of the way into `to`, drawn at zoom `k` around (cx, cy). +export const shapePath = (from: ShapeId | 'stage', to: ShapeId | 'stage', t: number, k: number, cx: number, cy: number) => { + const points = from === to ? prepare(from) : ring(from, to)(t); + return toPath(points, ICON.unit * k, cx, cy); +}; + +// ---- Boundary choreography ------------------------------------------------ + +export type MorphState = { + // 0: icon silhouette, 1: stage rectangle. + s: number; + // Zoom of the shape (1 = icon size, STAGE_ZOOM = stage size). + k: number; + cx: number; + cy: number; + // Opacity of the full-colour icon artwork over the silhouette. + details: number; + // Opacity of the app-colour wash over the stage content. + fill: number; + content: number; + contentScale: number; + blur: number; + // 0: icon shadow, 1: stage shadow. + lift: number; + border: number; + // 0..1 travel of the light sweep along the stage edge; null when idle. + sweep: number | null; +}; + +const clamp = (v: number) => Math.min(1, Math.max(0, v)); +const smooth = (v: number, a: number, b: number) => { + const x = clamp((v - a) / (b - a)); + return x * x * (3 - 2 * x); +}; +const lerp = (a: number, b: number, t: number) => a + (b - a) * t; + +// The downbeat swell: the icon breathes in on the cut. +export const boundaryPulse = (d: number) => 0.04 * Math.exp(-((d / 6.5) ** 2)); + +// Icon resting on a cut, `d` frames from it (negative: before it). +export const restState = (d: number): MorphState => ({ + s: 0, + k: 1 + boundaryPulse(d), + cx: ICON.x, + cy: ICON.y, + details: 1 - smooth(Math.abs(d), 10, MORPH.hold), + fill: 1, + content: 0, + contentScale: 0.72, + blur: 14, + lift: 0, + border: 0, + sweep: null, +}); + +// `o` is how open the stage is (0 icon, 1 stage), in each direction's own time. +const openState = (shape: number, zoom: number, o: number, sweep: number | null): MorphState => ({ + s: shape, + k: Math.exp(Math.log(STAGE_ZOOM) * zoom), + cx: lerp(ICON.x, STAGE_CENTER.x, zoom), + cy: lerp(ICON.y, STAGE_CENTER.y, zoom), + details: 0, + fill: 1 - smooth(o, 0.32, 0.82), + content: smooth(o, 0.22, 0.7), + contentScale: lerp(0.72, 1, EASE.out(o)), + blur: 14 * (1 - o) ** 2, + lift: o, + border: smooth(o, 0.8, 1), + sweep, +}); + +// Section-local frame `f` of an entering stage. +export const enterState = (f: number): MorphState => { + if (f < MORPH.hold) return restState(f); + const o = clamp((f - MORPH.hold) / MORPH.morph); + const shape = EASE.inOut(clamp(o / 0.6)); + const zoom = EASE.bloom(o); + const sweep = o > 0.55 ? clamp((o - 0.55) / 0.45) : null; + return openState(shape, zoom, zoom, sweep); +}; + +// Section-local frame `f` of a stage in a section `duration` frames long. +export const exitState = (f: number, duration: number): MorphState => { + const d = f - duration; + if (d >= -MORPH.hold) return restState(d); + const q = clamp((d + MORPH.hold + MORPH.morph) / MORPH.morph); + const zoom = 1 - EASE.land(q); + const shape = 1 - EASE.inOut(clamp((q - 0.22) / 0.78)); + return openState(shape, zoom, zoom, null); +}; + +export const isOpen = (state: MorphState) => state.s >= 1 && state.k >= STAGE_ZOOM - 1e-6; + +export const fade = (f: number, from: number, to: number, ease = EASE.out) => + interpolate(f, [from, to], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: ease }); diff --git a/site/video/update-0.1.5/src/flubber.d.ts b/site/video/update-0.1.5/src/flubber.d.ts new file mode 100644 index 0000000..77ef026 --- /dev/null +++ b/site/video/update-0.1.5/src/flubber.d.ts @@ -0,0 +1,9 @@ +declare module 'flubber' { + type Point = [number, number]; + type Shape = string | Point[]; + interface Options { + maxSegmentLength?: number; + } + export function interpolate(from: Shape, to: Shape, options: Options & { string: false }): (t: number) => Point[]; + export function interpolate(from: Shape, to: Shape, options?: Options & { string?: true }): (t: number) => string; +} diff --git a/site/video/update-0.1.5/src/footage/Clip.tsx b/site/video/update-0.1.5/src/footage/Clip.tsx new file mode 100644 index 0000000..95abb9e --- /dev/null +++ b/site/video/update-0.1.5/src/footage/Clip.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { AbsoluteFill, interpolate, OffthreadVideo, Sequence, staticFile, useCurrentFrame } from 'remotion'; +import { EASE, FOOTAGE, FPS, STAGE } from '../theme'; +import { Cursor } from './Cursor'; +import type { TakeName } from './take'; + +/** A stretch of one take: footage seconds `from` to `to`. */ +export type Cut = { take: TakeName; from: number; to: number; rate?: number; fade?: number }; + +/** A camera key at section frame `f`: zoom and the footage point in the middle. */ +export type Shot = { f: number; z: number; x: number; y: number }; + +export type Placed = Cut & { start: number; length: number }; + +/** Each cut's section frames; a cut's fade overlaps the one before. */ +export const lay = (cuts: Cut[], first = 0): Placed[] => { + let at = first; + return cuts.map((cut, i) => { + const length = Math.round(((cut.to - cut.from) * FPS) / (cut.rate ?? 1)); + const start = i === 0 ? at : at - (cut.fade ?? 0); + at = start + length; + return { ...cut, start, length }; + }); +}; + +/** Footage time a placed cut shows at section frame `f`. */ +export const sourceAt = (cut: Placed, f: number) => cut.from + ((f - cut.start) * (cut.rate ?? 1)) / FPS; + +const camera = (shots: Shot[], f: number) => { + if (f <= shots[0].f) return shots[0]; + const last = shots[shots.length - 1]; + if (f >= last.f) return last; + const i = shots.findIndex((s, k) => k > 0 && s.f > f); + const a = shots[i - 1]; + const b = shots[i]; + const k = EASE.inOut((f - a.f) / (b.f - a.f)); + return { f, z: a.z + (b.z - a.z) * k, x: a.x + (b.x - a.x) * k, y: a.y + (b.y - a.y) * k }; +}; + +/** Footage pixels to stage pixels, under the camera at frame `f`. */ +export const viewOf = (shots: Shot[], f: number) => { + const c = camera(shots, f); + const k = FOOTAGE.scale * c.z; + const vw = FOOTAGE.w / c.z; + const vh = FOOTAGE.h / c.z; + const left = Math.min(Math.max(c.x - vw / 2, 0), FOOTAGE.w - vw); + const top = Math.min(Math.max(c.y - vh / 2, 0), FOOTAGE.h - vh); + return { k, left, top, z: c.z }; +}; + +type Props = { + cuts: Cut[]; + shots: Shot[]; + /** Footage-space overlays for a cut at footage time `t`. */ + overlay?: (cut: Placed, t: number, f: number) => React.ReactNode; +}; + +/** Recorded takes on the stage, with the drawn pointer and a directed camera. */ +export const Clip: React.FC = ({ cuts, shots, overlay }) => { + const f = useCurrentFrame(); + const view = viewOf(shots, f); + const placed = lay(cuts); + return ( + + {placed.map((cut, i) => { + const fade = i === 0 ? 0 : cut.fade ?? 0; + const opacity = fade ? interpolate(f, [cut.start, cut.start + fade], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: EASE.inOut }) : 1; + const next = placed[i + 1]; + const end = next ? next.start + (next.fade ?? 0) : cut.start + cut.length; + if (f < cut.start || f >= end) return null; + const t = sourceAt(cut, f); + return ( + +
+ + + + {overlay?.(cut, t, f)} + +
+
+ ); + })} +
+ ); +}; diff --git a/site/video/update-0.1.5/src/footage/Cursor.tsx b/site/video/update-0.1.5/src/footage/Cursor.tsx new file mode 100644 index 0000000..cc3132f --- /dev/null +++ b/site/video/update-0.1.5/src/footage/Cursor.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { interpolate } from 'remotion'; +import { COLOR, EASE } from '../theme'; +import { actions, pointerAt, type TakeName } from './take'; + +const RIPPLE = 0.55; +const RIGHT = '#F2A900'; + +/** The pointer, drawn from the input log; presses ripple. */ +export const Cursor: React.FC<{ take: TakeName; t: number; zoom: number }> = ({ take, t, zoom }) => { + const p = pointerAt(take, t); + const downs = actions(take, 'down'); + const ups = actions(take, 'up'); + const held = downs.some((d) => { + const up = ups.find((u) => u.t >= d.t); + return t >= d.t && t <= (up ? up.t : d.t) + 0.06; + }); + // Larger under zoom, but less than the footage grows. + const size = 44 / Math.sqrt(zoom); + const press = held ? 0.86 : 1; + return ( + <> + {downs + .filter((d) => t >= d.t && t < d.t + RIPPLE) + .map((d) => { + const k = (t - d.t) / RIPPLE; + const r = interpolate(k, [0, 1], [8, 46], { easing: EASE.out }) / Math.sqrt(zoom); + const tint = d.right ? RIGHT : COLOR.accent; + return ( +
+ ); + })} + + + + + ); +}; diff --git a/site/video/update-0.1.5/src/footage/Sounds.tsx b/site/video/update-0.1.5/src/footage/Sounds.tsx new file mode 100644 index 0000000..f27be59 --- /dev/null +++ b/site/video/update-0.1.5/src/footage/Sounds.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Audio, Sequence, staticFile } from 'remotion'; +import { FPS } from '../theme'; +import { lay, type Cut } from './Clip'; +import { TAKES } from './take'; + +const SOUND: Record = { + down: { file: 'click', volume: 0.55 }, + right: { file: 'menu', volume: 0.6 }, + key: { file: 'key', volume: 0.5 }, + char: { file: 'key', volume: 0.45 }, + wheel: { file: 'tick', volume: 0.18 }, +}; + +/** UI sounds on the logged presses, keys and scrolls of `cuts`. */ +export const Sounds: React.FC<{ cuts: Cut[] }> = ({ cuts }) => { + const hits: { frame: number; file: string; volume: number }[] = []; + for (const cut of lay(cuts)) { + for (const e of TAKES[cut.take].events) { + if (e.t < cut.from || e.t >= cut.to) continue; + const sound = SOUND[e.kind === 'down' && e.right ? 'right' : e.kind]; + if (!sound) continue; + hits.push({ frame: Math.round(cut.start + ((e.t - cut.from) * FPS) / (cut.rate ?? 1)), ...sound }); + } + } + return ( + <> + {hits.map((hit, i) => ( + + + ))} + + ); +}; + +/** Section frame of footage time `t` in `cuts`, for timing overlays. */ +export const frameOf = (cuts: Cut[], t: number) => { + const cut = lay(cuts).find((c) => t >= c.from && t < c.to); + if (!cut) throw new Error(`no cut shows footage time ${t}`); + return Math.round(cut.start + ((t - cut.from) * FPS) / (cut.rate ?? 1)); +}; diff --git a/site/video/update-0.1.5/src/footage/Triangle.tsx b/site/video/update-0.1.5/src/footage/Triangle.tsx new file mode 100644 index 0000000..54c023f --- /dev/null +++ b/site/video/update-0.1.5/src/footage/Triangle.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { interpolate } from 'remotion'; +import { COLOR, EASE, FONT } from '../theme'; +import { pointerAt, TAKES, type TakeName } from './take'; + +type Props = { + take: TakeName; + t: number; + /** Footage seconds the pointer crosses toward the submenu. */ + from: number; + to: number; + /** The row it crosses without opening, as y0..y1, and its note. */ + passed: [number, number]; + note: string; +}; + +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; + +/** The safe triangle: pointer to the submenu's near corners, live. */ +export const Triangle: React.FC = ({ take, t, from, to, passed, note }) => { + const menu = TAKES[take].menus; + if (!menu) throw new Error(`${take} has no submenu rectangle`); + const show = interpolate(t, [from - 0.35, from, to + 0.25, to + 0.7], [0, 1, 1, 0], { ...clamp, easing: EASE.inOut }); + if (show <= 0) return null; + const p = pointerAt(take, Math.min(t, to + 0.25)); + const [x0, y0, , y1] = menu.panel; + const [px0, , px1] = menu.parent; + const drawn = interpolate(t, [from - 0.35, from + 0.1], [0, 1], { ...clamp, easing: EASE.out }); + const near = { x: x0 + 2, top: y0 + 6, bottom: y0 + 6 + (y1 - y0 - 12) * drawn }; + const row = interpolate(t, [from + 0.15, from + 0.45], [0, 1], { ...clamp, easing: EASE.out }); + return ( +
+ + + + + + + + + + + +
+ {note} +
+
+ ); +}; diff --git a/site/video/update-0.1.5/src/footage/preview.tsx b/site/video/update-0.1.5/src/footage/preview.tsx new file mode 100644 index 0000000..4e26bd9 --- /dev/null +++ b/site/video/update-0.1.5/src/footage/preview.tsx @@ -0,0 +1,31 @@ +// Test entry for the footage sections: `remotion still src/footage/preview.tsx
…`. +import { AbsoluteFill, Composition, registerRoot } from 'remotion'; +import '../design/fonts'; +import { Background } from '../design/Background'; +import { Cells } from '../scenes/Cells'; +import { Menus } from '../scenes/Menus'; +import { More } from '../scenes/More'; +import { RightClick } from '../scenes/RightClick'; +import { SECTION } from '../theme'; + +const framed = (Section: React.FC) => () => ( + + +
+ +); + +const ALL = [ + ['Menus', Menus, SECTION.menus], + ['Cells', Cells, SECTION.cells], + ['RightClick', RightClick, SECTION.rightClick], + ['More', More, SECTION.more], +] as const; + +registerRoot(() => ( + <> + {ALL.map(([id, Section, at]) => ( + + ))} + +)); diff --git a/site/video/update-0.1.5/src/footage/take.ts b/site/video/update-0.1.5/src/footage/take.ts new file mode 100644 index 0000000..c76c816 --- /dev/null +++ b/site/video/update-0.1.5/src/footage/take.ts @@ -0,0 +1,62 @@ +import cells from '../../public/footage/cells.json'; +import doc from '../../public/footage/doc.json'; +import menus from '../../public/footage/menus.json'; +import more from '../../public/footage/more.json'; +import show from '../../public/footage/show.json'; +import table from '../../public/footage/table.json'; + +// Times are footage seconds; positions are footage pixels (2048×1152). +export type Event = { + t: number; + kind: string; + x?: number; + y?: number; + right?: boolean; + key?: string; + ch?: string; + down?: boolean; +}; + +export type Box = [number, number, number, number]; + +export type Take = { + fps: number; + size: number[]; + events: Event[]; + menus: { parent: number[]; panel: number[] } | null; +}; + +export const TAKES = { menus, cells, doc, table, show, more } as Record; +export type TakeName = 'menus' | 'cells' | 'doc' | 'table' | 'show' | 'more'; + +const placed = new Map(); +const pointed = (name: TakeName) => { + let list = placed.get(name); + if (!list) { + list = TAKES[name].events.filter((e) => e.x !== undefined && e.kind !== 'sync'); + placed.set(name, list); + } + return list; +}; + +/** Where the pointer was at footage time `t`. */ +export const pointerAt = (name: TakeName, t: number) => { + const list = pointed(name); + let lo = 0; + let hi = list.length - 1; + if (t <= list[0].t) return { x: list[0].x!, y: list[0].y! }; + if (t >= list[hi].t) return { x: list[hi].x!, y: list[hi].y! }; + while (hi - lo > 1) { + const mid = (lo + hi) >> 1; + if (list[mid].t <= t) lo = mid; + else hi = mid; + } + const a = list[lo]; + const b = list[hi]; + const f = b.t === a.t ? 1 : (t - a.t) / (b.t - a.t); + return { x: a.x! + (b.x! - a.x!) * f, y: a.y! + (b.y! - a.y!) * f }; +}; + +/** Events of `kinds` in the take, in order. */ +export const actions = (name: TakeName, ...kinds: string[]) => + TAKES[name].events.filter((e) => kinds.includes(e.kind)); diff --git a/site/video/update-0.1.5/src/index.ts b/site/video/update-0.1.5/src/index.ts new file mode 100644 index 0000000..aa152d6 --- /dev/null +++ b/site/video/update-0.1.5/src/index.ts @@ -0,0 +1,4 @@ +import { registerRoot } from 'remotion'; +import { Root } from './Root'; + +registerRoot(Root); diff --git a/site/video/update-0.1.5/src/scenes/Cards.tsx b/site/video/update-0.1.5/src/scenes/Cards.tsx new file mode 100644 index 0000000..1f357df --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/Cards.tsx @@ -0,0 +1,121 @@ +import { cancelRender, continueRender, delayRender, interpolate, staticFile, useCurrentFrame } from 'remotion'; +import { COLOR, EASE, FONT } from '../theme'; + +// The 'before' card mixes faces, as Windows did. +const serif = delayRender('Loading Noto Serif SC', { timeoutInMilliseconds: 120000 }); +new FontFace('Noto Serif SC', `url(${staticFile('fonts/NotoSerifSC.ttf')}) format("truetype")`, { weight: '200 900' }) + .load() + .then((face) => { + document.fonts.add(face); + continueRender(serif); + }, cancelRender); + +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; +const BEAT = 37.5; + +const Card: React.FC<{ at: number; x: number; title: string; sub: string; children: React.ReactNode }> = ({ at, x, title, sub, children }) => { + const f = useCurrentFrame(); + const p = interpolate(f, [at, at + 34], [0, 1], { ...clamp, easing: EASE.out }); + return ( +
+
{children}
+
+
{title}
+
{sub}
+
+
+ ); +}; + +/** One word in two faces, settling into one. */ +const Glyphs: React.FC<{ at: number }> = ({ at }) => { + const f = useCurrentFrame(); + const fix = interpolate(f, [at + 2 * BEAT, at + 3.4 * BEAT], [0, 1], { ...clamp, easing: EASE.inOut }); + const label = (text: string, on: number) => ( + {text} + ); + const word = (style: React.CSSProperties, text: string) => {text}; + return ( +
+
+
{label('之前', 1 - fix)}
+
{label('现在', fix)}
+
+
+ {word({ fontFamily: FONT.sans, fontVariationSettings: `'wght' ${800 - 300 * fix}`, fontWeight: 800 - 300 * fix }, '打开')} + + {word({ fontFamily: '"Noto Serif SC"', fontWeight: 300, opacity: 1 - fix, filter: `blur(${fix * 6}px)` }, '仓库')} + {word( + { position: 'absolute', left: 0, top: 0, fontFamily: FONT.sans, fontWeight: 500, opacity: fix, filter: `blur(${(1 - fix) * 6}px)` }, + '仓库', + )} + +
+
+
+ ); +}; + +/** A shield draws itself, then a check. */ +const Notarized: React.FC<{ at: number }> = ({ at }) => { + const f = useCurrentFrame(); + const shield = interpolate(f, [at + 10, at + 10 + 2 * BEAT], [0, 1], { ...clamp, easing: EASE.inOut }); + const check = interpolate(f, [at + 2.4 * BEAT, at + 3.4 * BEAT], [0, 1], { ...clamp, easing: EASE.out }); + const glow = interpolate(f, [at + 3 * BEAT, at + 4.2 * BEAT], [0, 1], { ...clamp, easing: EASE.out }); + const SHIELD = 'M120 20 L210 52 V128 C210 186 172 228 120 248 C68 228 30 186 30 128 V52 Z'; + return ( +
+ + + + + +
+ ); +}; + +/** The two cards after the footage, in stage coordinates. */ +export const Cards: React.FC<{ at: number }> = ({ at }) => ( + <> + + + + + + + +); diff --git a/site/video/update-0.1.5/src/scenes/Cells.tsx b/site/video/update-0.1.5/src/scenes/Cells.tsx new file mode 100644 index 0000000..5df4374 --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/Cells.tsx @@ -0,0 +1,50 @@ +import { AbsoluteFill } from 'remotion'; +import { Caption } from '../design/Caption'; +import { KeyCap } from '../design/KeyCap'; +import { Stage } from '../design/Stage'; +import { Clip, type Cut, type Shot } from '../footage/Clip'; +import { frameOf, Sounds } from '../footage/Sounds'; + +const CUTS: Cut[] = [{ take: 'cells', from: 3.1, to: 13.1 }]; +const SHOTS: Shot[] = [ + { f: 0, z: 1, x: 1024, y: 576 }, + { f: 60, z: 1.25, x: 1190, y: 440 }, + { f: 110, z: 1.75, x: 1190, y: 420 }, + { f: 470, z: 1.75, x: 1190, y: 420 }, + { f: 560, z: 1.2, x: 1200, y: 460 }, +]; + +// One badge per key, held until the next one. +const KEYS: { t: number; keys: string[] }[] = [ + { t: 4.26, keys: ['点击'] }, + { t: 5.73, keys: ['Tab'] }, + { t: 6.88, keys: ['↓'] }, + { t: 7.94, keys: ['End'] }, + { t: 8.29, keys: ['⌫'] }, + { t: 10.63, keys: ['Enter'] }, +]; + +export const Cells: React.FC = () => { + const at = (t: number) => frameOf(CUTS, t); + const backs = [8.29, 8.5, 8.71].map(at); + return ( + + + + + {KEYS.map((k, i) => { + const from = at(k.t) - 12; + const to = i + 1 < KEYS.length ? at(KEYS[i + 1].t) - 12 : at(k.t) + 70; + return ; + })} + + + + + + ); +}; diff --git a/site/video/update-0.1.5/src/scenes/Intro.tsx b/site/video/update-0.1.5/src/scenes/Intro.tsx new file mode 100644 index 0000000..5a84a59 --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/Intro.tsx @@ -0,0 +1,175 @@ +import { AbsoluteFill, interpolate, useCurrentFrame } from 'remotion'; +import { COLOR, EASE, FONT, ICON, SECTION } from '../theme'; +import { type ShapeId, SHAPES, boundaryPulse, shapePath } from '../design/morph'; +import { ShapeView, type Details, mix, rgba } from '../design/Shape'; + +// Each icon lands on a beat; the dot breathes for the first two beats. +const STEPS: { shape: ShapeId; land: number; dur: number }[] = [ + { shape: 'ely-doc', land: 75, dur: 30 }, + { shape: 'ely-show', land: 112.5, dur: 24 }, + { shape: 'ely-table', land: 150, dur: 24 }, + { shape: 'ely-tree', land: 187.5, dur: 24 }, + { shape: 'ely-pdf', land: 225, dur: 24 }, + { shape: 'ely-mark', land: 262.5, dur: 24 }, +]; + +const END = SECTION.intro.duration; +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; +const ramp = (f: number, a: number, b: number, ease = EASE.out) => interpolate(f, [a, b], [0, 1], { ...clamp, easing: ease }); + +const iconAt = (f: number) => { + // Before the wordmark the icon sits a little higher, at the frame's centre. + const cy = ICON.y - 40 * (1 - ramp(f, 150, 240, EASE.inOut)); + const pulse = boundaryPulse(f - END); + const index = STEPS.findLastIndex((s) => f >= s.land - s.dur); + if (index < 0) { + const appear = ramp(f, 4, 38); + const breath = 0.16 * Math.exp(-(((f - 37.5) / 8) ** 2)); + const k = Math.max(0.001, appear * (1 + breath)); + return { d: shapePath('dot', 'stage', 0, k, ICON.x, cy), color: SHAPES.dot.color, k, cy, details: [] as Details[] }; + } + const step = STEPS[index]; + const from: ShapeId = index === 0 ? 'dot' : STEPS[index - 1].shape; + const start = step.land - step.dur; + const t = EASE.inOut(Math.min(1, (f - start) / step.dur)); + const swell = STEPS.slice(0, -1).reduce((sum, s) => sum + 0.03 * Math.exp(-(((f - s.land) / 6) ** 2)), 0); + const k = (1 - 0.07 * Math.sin(Math.PI * t)) * (1 + swell + pulse); + const d = t >= 1 ? shapePath(step.shape, 'stage', 0, k, ICON.x, cy) : shapePath(from, step.shape, t, k, ICON.x, cy); + const details: Details[] = [ + { shape: from, opacity: 1 - ramp(f, start, start + 6, EASE.inOut) }, + { shape: step.shape, opacity: ramp(f, step.land - 3, step.land + 7, EASE.inOut) }, + ]; + return { d, color: mix(SHAPES[from].color, SHAPES[step.shape].color, t), k, cy, details }; +}; + +const Letters: React.FC<{ text: string; start: number; stagger: number; rise: number; blur: number }> = ({ text, start, stagger, rise, blur }) => { + const f = useCurrentFrame(); + return ( + <> + {Array.from(text).map((char, i) => { + const p = ramp(f, start + i * stagger, start + i * stagger + 32); + const b = (1 - p) * blur; + return ( + 0.05 ? `blur(${b.toFixed(2)}px)` : undefined, + }} + > + {char} + + ); + })} + + ); +}; + +const WORD: React.CSSProperties = { + fontFamily: FONT.latin, + fontSize: 92, + lineHeight: '100px', + whiteSpace: 'nowrap', + display: 'flex', + alignItems: 'baseline', + gap: 30, +}; + +const Wordmark: React.FC<{ reveal: boolean; color?: string }> = ({ reveal, color }) => { + const word = (text: string, start: number) => + reveal ? : text.split('').map((c, i) => {c}); + return ( +
+ {word('Ely Office', 150)} + {word('0.1.5', 172)} +
+ ); +}; + +export const Intro: React.FC = () => { + const f = useCurrentFrame(); + const icon = iconAt(f); + const dawn = 1 - ramp(f, 0, 50, EASE.inOut); + const ripple = ramp(f, 37.5, 37.5 + 50); + const leaveWord = ramp(f, 272, 294, EASE.in); + const leaveLine = ramp(f, 264, 286, EASE.in); + const rule = ramp(f, 182, 210) * (1 - ramp(f, 258, 278, EASE.inOut)); + const sweep = ramp(f, 216, 262, EASE.inOut); + return ( + + {f < 92 ? ( + + + + ) : null} + +
0.01 ? `blur(${(8 * leaveWord).toFixed(2)}px)` : undefined, + }} + > +
+ + {sweep > 0 && sweep < 1 ? ( +
+ +
+ ) : null} +
+
+
+
0.01 ? `blur(${(6 * leaveLine).toFixed(2)}px)` : undefined, + }} + > + +
+ {dawn > 0.001 ? : null} + + ); +}; diff --git a/site/video/update-0.1.5/src/scenes/Menus.tsx b/site/video/update-0.1.5/src/scenes/Menus.tsx new file mode 100644 index 0000000..1044e43 --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/Menus.tsx @@ -0,0 +1,46 @@ +import { AbsoluteFill } from 'remotion'; +import { Caption } from '../design/Caption'; +import { KeyCap } from '../design/KeyCap'; +import { Stage } from '../design/Stage'; +import { Clip, type Cut, type Shot } from '../footage/Clip'; +import { frameOf, Sounds } from '../footage/Sounds'; +import { Triangle } from '../footage/Triangle'; + +// The crossing over 插入 plays at half speed. +const CROSS = { from: 7.09, to: 8.04 }; +const CUTS: Cut[] = [ + { take: 'menus', from: 3.6, to: 7.0 }, + { take: 'menus', from: 7.0, to: 8.15, rate: 0.5 }, + { take: 'menus', from: 8.15, to: 12.45 }, +]; +const SHOTS: Shot[] = [ + { f: 0, z: 1, x: 1024, y: 576 }, + { f: 40, z: 1.15, x: 1120, y: 560 }, + { f: 115, z: 1.45, x: 1380, y: 720 }, + { f: 355, z: 1.45, x: 1380, y: 720 }, + { f: 420, z: 1.25, x: 1150, y: 600 }, + { f: 560, z: 1, x: 1024, y: 576 }, +]; + +export const Menus: React.FC = () => { + const right = frameOf(CUTS, 4.43); + const cross = frameOf(CUTS, CROSS.from); + return ( + + + } + /> + + + + + + ); +}; diff --git a/site/video/update-0.1.5/src/scenes/More.tsx b/site/video/update-0.1.5/src/scenes/More.tsx new file mode 100644 index 0000000..033bb1f --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/More.tsx @@ -0,0 +1,49 @@ +import { AbsoluteFill, interpolate, useCurrentFrame } from 'remotion'; +import { Caption } from '../design/Caption'; +import { KeyCap } from '../design/KeyCap'; +import { Stage } from '../design/Stage'; +import { Clip, type Cut, type Shot } from '../footage/Clip'; +import { frameOf, Sounds } from '../footage/Sounds'; +import { EASE } from '../theme'; +import { Cards } from './Cards'; + +const CUTS: Cut[] = [{ take: 'more', from: 2.6, to: 9.0 }]; +const SHOTS: Shot[] = [ + { f: 0, z: 1, x: 1024, y: 576 }, + { f: 40, z: 1.4, x: 731, y: 360 }, + { f: 190, z: 1.4, x: 731, y: 360 }, + { f: 240, z: 1.5, x: 683, y: 780 }, + { f: 296, z: 1.5, x: 683, y: 780 }, + { f: 336, z: 1.5, x: 683, y: 384 }, +]; +// The cards replace the footage from here. +const CARDS = 372; + +export const More: React.FC = () => { + const f = useCurrentFrame(); + const clear = frameOf(CUTS, 7.38); + const away = interpolate(f, [CARDS - 20, CARDS + 10], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: EASE.inOut }); + return ( + + + {f < CARDS + 10 ? ( + + + + ) : null} + {f >= CARDS - 20 ? : null} + + + + + + + ); +}; diff --git a/site/video/update-0.1.5/src/scenes/Outro.tsx b/site/video/update-0.1.5/src/scenes/Outro.tsx new file mode 100644 index 0000000..27326ce --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/Outro.tsx @@ -0,0 +1,152 @@ +import { AbsoluteFill, interpolate, useCurrentFrame } from 'remotion'; +import { APPS, BEAT, COLOR, EASE, FONT, ICON } from '../theme'; +import { SHAPES, boundaryPulse, shapePath } from '../design/morph'; +import { ShapeView, mix, rgba } from '../design/Shape'; + +const clamp = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const; +const ramp = (f: number, a: number, b: number, ease = EASE.out) => interpolate(f, [a, b], [0, 1], { ...clamp, easing: ease }); +const lerp = (a: number, b: number, t: number) => a + (b - a) * t; + +// Row after the bloom, and the tighter row it gathers into above the wordmark. +const ROW = { gap: 212, y: 470, k: 0.62 }; +const GATHER = { gap: 128, y: 384, k: 0.38, at: 5 * BEAT }; +const BLOOM = { at: BEAT, every: BEAT / 2, dur: 26 }; +const SPLIT = { at: 16, dur: 40 }; +const FADE = { from: 390, to: 446 }; + +const Ring: React.FC<{ start: number; reach: number; width: number }> = ({ start, reach, width }) => { + const f = useCurrentFrame(); + const p = ramp(f, start, start + 64); + if (p <= 0 || p >= 1) return null; + return ( + + ); +}; + +const AppIcon: React.FC<{ index: number }> = ({ index }) => { + const f = useCurrentFrame(); + const app = APPS[index]; + const offset = index - (APPS.length - 1) / 2; + const travel = Math.min(1, Math.max(0, (f - SPLIT.at) / SPLIT.dur)); + const bloomAt = BLOOM.at + index * BLOOM.every; + const t = EASE.inOut(Math.min(1, Math.max(0, (f - bloomAt) / BLOOM.dur))); + const land = bloomAt + BLOOM.dur; + const gather = ramp(f, GATHER.at + index * 2, GATHER.at + index * 2 + 44, EASE.inOut); + + // Split: the dots rise first, then fan out, like a fountain. + // Both start from rest; the rise leads the spread. + const splitX = lerp(ICON.x, ICON.x + offset * ROW.gap, EASE.inOut(Math.max(0, (travel - 0.15) / 0.85))); + const splitY = lerp(ICON.y, ROW.y, EASE.inOut(Math.min(1, travel / 0.7))); + const cx = lerp(splitX, ICON.x + offset * GATHER.gap, gather); + const cy = lerp(splitY, GATHER.y, gather); + + const swell = 0.035 * Math.exp(-(((f - land) / 6) ** 2)); + const base = lerp(lerp(1 + boundaryPulse(f), ROW.k, EASE.inOut(travel)), GATHER.k, gather); + const k = base * (1 - 0.08 * Math.sin(Math.PI * t)) * (1 + swell); + const d = t >= 1 ? shapePath(app.id, 'stage', 0, k, cx, cy) : shapePath('dot', app.id, t, k, cx, cy); + const color = mix(SHAPES.dot.color, app.color, Math.max(EASE.inOut(travel) * 0.6, t)); + return ( + + ); +}; + +const Line: React.FC<{ text: string; start: number; stagger: number; style: React.CSSProperties }> = ({ text, start, stagger, style }) => { + const f = useCurrentFrame(); + return ( +
+ {Array.from(text).map((char, i) => { + const p = ramp(f, start + i * stagger, start + i * stagger + 30); + const b = (1 - p) * 8; + return ( + 0.05 ? `blur(${b.toFixed(2)}px)` : undefined, + }} + > + {char} + + ); + })} +
+ ); +}; + +export const Outro: React.FC = () => { + const f = useCurrentFrame(); + const fade = 1 - ramp(f, FADE.from, FADE.to, EASE.inOut); + const push = 1 + 0.022 * ramp(f, GATHER.at, FADE.to, EASE.inOut); + const glow = ramp(f, 0, 18) * (1 - ramp(f, 18, 80, EASE.inOut)); + return ( + +
+ + + + + {f < SPLIT.at ? ( + + ) : ( + APPS.map((app, i) => ) + )} +
+ + + +
+ + ); +}; diff --git a/site/video/update-0.1.5/src/scenes/RightClick.tsx b/site/video/update-0.1.5/src/scenes/RightClick.tsx new file mode 100644 index 0000000..ce51f2c --- /dev/null +++ b/site/video/update-0.1.5/src/scenes/RightClick.tsx @@ -0,0 +1,65 @@ +import { AbsoluteFill } from 'remotion'; +import { Caption } from '../design/Caption'; +import { KeyCap } from '../design/KeyCap'; +import { Stage } from '../design/Stage'; +import { Clip, lay, type Cut, type Shot } from '../footage/Clip'; +import { frameOf, Sounds } from '../footage/Sounds'; +import { Triangle } from '../footage/Triangle'; + +const CUTS: Cut[] = [ + { take: 'doc', from: 3.9, to: 9.5 }, + { take: 'doc', from: 11.75, to: 13.85, fade: 12 }, + { take: 'table', from: 3.9, to: 7.4, fade: 14 }, + { take: 'show', from: 3.1, to: 7.57, fade: 14 }, +]; +const [, drag, table, show] = lay(CUTS); +const SHOTS: Shot[] = [ + { f: 0, z: 1, x: 1024, y: 576 }, + { f: 45, z: 1.2, x: 1100, y: 620 }, + { f: 120, z: 1.45, x: 1260, y: 800 }, + { f: 290, z: 1.45, x: 1260, y: 800 }, + { f: drag.start, z: 1.35, x: 920, y: 640 }, + { f: drag.start + 28, z: 1.65, x: 990, y: 620 }, + { f: table.start - 6, z: 1.65, x: 990, y: 620 }, + { f: table.start + 34, z: 1.45, x: 560, y: 380 }, + { f: show.start - 6, z: 1.45, x: 560, y: 380 }, + { f: show.start + 40, z: 1.5, x: 430, y: 620 }, + { f: show.start + 160, z: 1.5, x: 430, y: 620 }, + { f: show.start + 230, z: 1.15, x: 900, y: 560 }, +]; + +export const RightClick: React.FC = () => { + const docRight = frameOf(CUTS.slice(0, 1), 4.95); + const tableRight = table.start + Math.round((4.25 - table.from) * 60); + const wheel = show.start + Math.round((3.42 - show.from) * 60); + return ( + + + + cut.take === 'doc' ? ( + + ) : null + } + /> + + + + + + + + + + ); +}; diff --git a/site/video/update-0.1.5/src/theme.ts b/site/video/update-0.1.5/src/theme.ts new file mode 100644 index 0000000..233342a --- /dev/null +++ b/site/video/update-0.1.5/src/theme.ts @@ -0,0 +1,84 @@ +import { Easing } from 'remotion'; + +export const WIDTH = 1920; +export const HEIGHT = 1080; +export const FPS = 60; +export const TOTAL = 3600; + +// 96 BPM, 4/4. +export const BEAT = 37.5; +export const BAR = 150; +export const beat = (n: number) => Math.round(n * BEAT); +export const bar = (n: number) => n * BAR; + +// Section start frames (bars 1, 3, 7, 11, 17, 22) and lengths. +export const SECTION = { + intro: { from: 0, duration: 300 }, + menus: { from: 300, duration: 600 }, + cells: { from: 900, duration: 600 }, + rightClick: { from: 1500, duration: 900 }, + more: { from: 2400, duration: 750 }, + outro: { from: 3150, duration: 450 }, +} as const; + +export const COLOR = { + bgInner: '#252525', + bgOuter: '#1c1c1e', + ink: '#F4F4F2', + muted: '#A8A6A3', + faint: '#6E6C6A', + accent: '#3D7FE5', + panel: '#1f1f21', + hairline: 'rgba(255,255,255,0.08)', +} as const; + +export type AppId = 'ely-doc' | 'ely-table' | 'ely-show' | 'ely-pdf' | 'ely-tree' | 'ely-mark'; + +export const APPS: { id: AppId; name: string; color: string }[] = [ + { id: 'ely-doc', name: 'ElyDoc', color: '#3D7FE5' }, + { id: 'ely-table', name: 'ElyTable', color: '#2E9E5B' }, + { id: 'ely-show', name: 'ElyShow', color: '#F07B2A' }, + { id: 'ely-pdf', name: 'ElyPDF', color: '#E0344F' }, + { id: 'ely-tree', name: 'ElyTree', color: '#7E57C2' }, + { id: 'ely-mark', name: 'ElyMark', color: '#3E4A5C' }, +]; + +export const appOf = (id: AppId) => APPS.find((a) => a.id === id)!; + +export const STAGE = { x: 120, y: 118, w: 1680, h: 945, r: 18 } as const; +export const STAGE_CENTER = { x: STAGE.x + STAGE.w / 2, y: STAGE.y + STAGE.h / 2 }; +// Recordings are 2048×1152; this maps them onto the stage width. +export const FOOTAGE = { w: 2048, h: 1152, scale: STAGE.w / 2048 } as const; + +// Where boundary icons rest, and the size of one icon-grid unit (Papirus 192 grid). +export const ICON = { x: 960, y: 590, unit: 1.375 } as const; + +// Section-local morph frames. Boundary icons rest for `hold` frames on each side +// of the cut (one beat in total, centred on the downbeat); the shape morph takes +// `morph` frames. The stage is fully open from `ENTER_END`, and starts closing at +// `duration - EXIT_LEN`. +export const MORPH = { hold: 18, morph: 42 } as const; +export const ENTER_END = MORPH.hold + MORPH.morph; +export const EXIT_LEN = MORPH.hold + MORPH.morph; + +// Headings enter on beat 1 of a section, the sub line half a beat later, and +// start to leave `out` frames before the section ends (gone before it closes). +export const HEADING = { in: beat(1), sub: beat(0.5), out: 92 } as const; + +export const EASE = { + out: Easing.bezier(0.22, 1, 0.36, 1), + inOut: Easing.bezier(0.65, 0, 0.35, 1), + in: Easing.bezier(0.55, 0, 0.75, 0.06), + // Starts from rest, decelerates long: for growth that should feel heavy. + bloom: Easing.bezier(0.5, 0, 0.1, 1), + // Gentle start, soft landing: for collapses that must come to rest. + land: Easing.bezier(0.6, 0, 0.2, 1), +} as const; + +export const SPRING = { damping: 200 } as const; + +export const FONT = { + sans: '"Noto Sans SC", "Inter", sans-serif', + latin: '"Inter", "Noto Sans SC", sans-serif', + mono: '"JetBrains Mono", monospace', +} as const; diff --git a/site/video/update-0.1.5/tsconfig.json b/site/video/update-0.1.5/tsconfig.json new file mode 100644 index 0000000..8a34841 --- /dev/null +++ b/site/video/update-0.1.5/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "skipLibCheck": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "types": [], + "noEmit": true + }, + "include": ["src"] +} diff --git a/tasks/BUGS.md b/tasks/BUGS.md index cfd9426..146231d 100644 --- a/tasks/BUGS.md +++ b/tasks/BUGS.md @@ -157,4 +157,5 @@ P0 次序:W-01、K-01、L-01、O-01、W-02、L-02、W-03、L-03、W-04、L-04 - [ ] P3-14 Windows:ElyTree 九个单测恒失败:五个比对 Git 写出的文字,本机 Git 的 `core.autocrlf` 写成 CRLF(测试仓库未设 `core.autocrlf false`);其余写 Windows 不许的文件名(os error 123)。代码行为未见错,未修 - [ ] P3-15 Windows:全量并行单测中偶有一个 LOK worker 读到的 BuildId 为 `development`(`desktop/source/app/app.cxx` 读不到 bootstrap 的 buildid 时的默认值),握手照规则拒绝;同一时刻别的 worker 读到正确的 8221e31,内核与 worker 都未重建,单独重跑即过(2026-10-14 ElyDoc `review::tests::one_unreadable_time_spares_the_other_notes`,三次重跑皆过)。疑为并发时读 `version.ini` 偶发失败,原因未查 - [ ] P3-16 发布:`ci.sh` 在三端各自调用 `packaging/source.sh` 并上传同名的源码包与上游源码包,发布里同名文件出现三份(Gitea 允许重名);上游源码包打的是各机的 `engines/mupdf-src`,含构建留下的文件,三份大小不同(v0.1.5:Linux 108 MB,macOS 161 MB)。v0.1.5 手动删去后到的两份,留 Linux 的一份。应只出一份,且上游包只含源码 +- [ ] P3-17 ElyTable(Windows,0.1.5 安装版,2026-10-15 录视频时见):A1 选中后按两次 Ctrl+= 缩放到 150%,网格停在半滚动处:第 5 行被表头截去一半,第 1–4 行看不见,Ctrl+Home 也回不到 A1。应保持首行与首列在视图内(同 R2.7 的按键缩放)