@autoaiai/aicut 0.0.0-stage → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/MANUAL.md +84 -0
  3. package/README.md +36 -2
  4. package/SPEC.md +221 -0
  5. package/THIRD_PARTY_NOTICES.md +10 -0
  6. package/TRADEMARK.md +25 -0
  7. package/bin/aicut.mjs +264 -0
  8. package/package.json +89 -6
  9. package/product.json +5 -0
  10. package/skills/aicut/LICENSE +21 -0
  11. package/skills/aicut/SKILL.md +66 -0
  12. package/skills/aicut/references/commands.md +72 -0
  13. package/skills/aicut/references/film-format.md +119 -0
  14. package/skills/aicut/references/recipes.md +184 -0
  15. package/skills/aicut/references/troubleshooting.md +57 -0
  16. package/skills/aicut-motion/LICENSE +21 -0
  17. package/skills/aicut-motion/SKILL.md +255 -0
  18. package/skills/aicut-motion/references/design-systems/README.md +25 -0
  19. package/skills/aicut-motion/references/design-systems/bold-poster.md +62 -0
  20. package/skills/aicut-motion/references/design-systems/data-instrument.md +71 -0
  21. package/skills/aicut-motion/references/design-systems/drafting-table.md +76 -0
  22. package/skills/aicut-motion/references/design-systems/editorial-ink.md +83 -0
  23. package/skills/aicut-motion/references/design-systems/riso-halftone.md +70 -0
  24. package/skills/aicut-motion/references/design-systems/storybook-scene.md +75 -0
  25. package/skills/aicut-motion/references/design-systems/swiss-kinetic.md +67 -0
  26. package/skills/aicut-motion/references/pages.md +258 -0
  27. package/src/film-doc.d.mts +204 -0
  28. package/src/film-doc.mjs +1225 -0
  29. package/src/host.d.mts +35 -0
  30. package/src/host.mjs +542 -0
  31. package/src/look.d.mts +2 -0
  32. package/src/look.mjs +358 -0
  33. package/src/media-links.mjs +68 -0
  34. package/src/of-filters.mjs +237 -0
  35. package/src/overrides.mjs +75 -0
  36. package/src/render.mjs +202 -0
  37. package/src/seekable.mjs +101 -0
  38. package/src/shared.d.mts +27 -0
  39. package/src/shared.mjs +391 -0
  40. package/src/timeline.mjs +1158 -0
  41. package/studio/client.mjs +311 -0
  42. package/studio/server/atomic.mjs +41 -0
  43. package/studio/server/bridge.js +89 -0
  44. package/studio/server/browse.mjs +104 -0
  45. package/studio/server/captions.mjs +502 -0
  46. package/studio/server/chat.mjs +49 -0
  47. package/studio/server/closure.mjs +407 -0
  48. package/studio/server/compound.mjs +133 -0
  49. package/studio/server/custom-services.mjs +299 -0
  50. package/studio/server/derived.mjs +225 -0
  51. package/studio/server/examples.mjs +89 -0
  52. package/studio/server/exports.mjs +1621 -0
  53. package/studio/server/fetch-error.mjs +14 -0
  54. package/studio/server/fetch-github.mjs +181 -0
  55. package/studio/server/fetch-repo.mjs +399 -0
  56. package/studio/server/files.mjs +628 -0
  57. package/studio/server/film-subtitle.mjs +869 -0
  58. package/studio/server/film.mjs +109 -0
  59. package/studio/server/folder-picker.mjs +199 -0
  60. package/studio/server/fonts.mjs +308 -0
  61. package/studio/server/get.mjs +700 -0
  62. package/studio/server/heard.mjs +108 -0
  63. package/studio/server/hidden.mjs +85 -0
  64. package/studio/server/history-migrate.mjs +239 -0
  65. package/studio/server/history-store.mjs +538 -0
  66. package/studio/server/history.mjs +682 -0
  67. package/studio/server/html-deck.mjs +168 -0
  68. package/studio/server/html-film.mjs +686 -0
  69. package/studio/server/html-pages.mjs +604 -0
  70. package/studio/server/http.mjs +40 -0
  71. package/studio/server/jianying.mjs +289 -0
  72. package/studio/server/keys.mjs +126 -0
  73. package/studio/server/main.mjs +120 -0
  74. package/studio/server/markers.mjs +80 -0
  75. package/studio/server/nle.mjs +537 -0
  76. package/studio/server/ops.mjs +506 -0
  77. package/studio/server/page-links.mjs +98 -0
  78. package/studio/server/pageframes.mjs +335 -0
  79. package/studio/server/pages.mjs +197 -0
  80. package/studio/server/prefs.mjs +56 -0
  81. package/studio/server/project-fonts.mjs +263 -0
  82. package/studio/server/project-settings.mjs +130 -0
  83. package/studio/server/projects.mjs +457 -0
  84. package/studio/server/providers/azure.mjs +100 -0
  85. package/studio/server/providers/brave.mjs +55 -0
  86. package/studio/server/providers/common.mjs +321 -0
  87. package/studio/server/providers/contract.mjs +98 -0
  88. package/studio/server/providers/doubao.mjs +65 -0
  89. package/studio/server/providers/edge-drm.mjs +263 -0
  90. package/studio/server/providers/edge-speech.mjs +292 -0
  91. package/studio/server/providers/edge-tts.mjs +112 -0
  92. package/studio/server/providers/elevenlabs.mjs +144 -0
  93. package/studio/server/providers/fal.mjs +212 -0
  94. package/studio/server/providers/google.mjs +201 -0
  95. package/studio/server/providers/groq.mjs +97 -0
  96. package/studio/server/providers/index.mjs +284 -0
  97. package/studio/server/providers/local-mdx.mjs +293 -0
  98. package/studio/server/providers/local-whisper.mjs +847 -0
  99. package/studio/server/providers/mdx-net.mjs +275 -0
  100. package/studio/server/providers/openai.mjs +131 -0
  101. package/studio/server/providers/pexels.mjs +40 -0
  102. package/studio/server/providers/pixabay.mjs +53 -0
  103. package/studio/server/providers/tar-bz2.mjs +356 -0
  104. package/studio/server/providers/tavily.mjs +68 -0
  105. package/studio/server/providers/web.mjs +231 -0
  106. package/studio/server/proxies.mjs +151 -0
  107. package/studio/server/recycle.mjs +222 -0
  108. package/studio/server/render-worker.mjs +29 -0
  109. package/studio/server/separate.mjs +44 -0
  110. package/studio/server/server.mjs +1021 -0
  111. package/studio/server/sfx.mjs +321 -0
  112. package/studio/server/skills.mjs +357 -0
  113. package/studio/server/slides.mjs +230 -0
  114. package/studio/server/stage.js +1553 -0
  115. package/studio/server/stock.mjs +122 -0
  116. package/studio/server/subtitle-edits.mjs +234 -0
  117. package/studio/server/subtitle-segments.mjs +602 -0
  118. package/studio/server/template-posters.mjs +68 -0
  119. package/studio/server/templates-page.mjs +93 -0
  120. package/studio/server/templates-styles.mjs +583 -0
  121. package/studio/server/templates.mjs +269 -0
  122. package/studio/server/transcripts.mjs +490 -0
  123. package/studio/server/transition-pages.mjs +43 -0
  124. package/studio/server/trash.mjs +143 -0
  125. package/studio/server/video-models.mjs +226 -0
  126. package/studio/server/watch.mjs +160 -0
  127. package/studio/server/web-agents.mjs +143 -0
  128. package/studio/server/websocket.mjs +82 -0
  129. package/studio/server/zip.mjs +159 -0
  130. package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +1 -0
  131. package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +2 -0
  132. package/studio/ui/dist/assets/app-icon-BpqHp2uj.png +0 -0
  133. package/studio/ui/dist/assets/es-BRUzvuoM.js +1 -0
  134. package/studio/ui/dist/assets/index-Bhg-9dA3.css +1 -0
  135. package/studio/ui/dist/assets/index-CWur1Iw9.js +1625 -0
  136. package/studio/ui/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  137. package/studio/ui/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  138. package/studio/ui/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  139. package/studio/ui/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  140. package/studio/ui/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  141. package/studio/ui/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  142. package/studio/ui/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  143. package/studio/ui/dist/assets/ja-Cth-uDcW.js +1 -0
  144. package/studio/ui/dist/assets/ko-upvw591D.js +1 -0
  145. package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +1 -0
  146. package/studio/ui/dist/index.html +23 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AiCut contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/MANUAL.md ADDED
@@ -0,0 +1,84 @@
1
+ # AICUT
2
+
3
+ You make films. A film is a web page, `film.html`, that cuts pages, footage, pictures and sound together. A page is a web page that sets `window.film` and draws any moment t it is asked for.
4
+
5
+ ## The project
6
+
7
+ A folder:
8
+ - `film.html`: the film. The person edits it in Studio at any moment: read it right before you change it, and change only what you mean to.
9
+ - Pages (`*.html`): pictures written as code, with the files they load by relative path.
10
+ - `assets/`: media files (video, pictures, sound). The person sees every one in Studio: keep only what the film can use, and delete the pieces you made them from.
11
+ - Files and folders starting with a dot (`.film`, `.git`) belong to the tools: leave them.
12
+
13
+ ## film.html
14
+
15
+ <!doctype html>
16
+ <html>
17
+ <head>
18
+ <meta charset="utf-8">
19
+ <meta name="viewport" content="width=1920, height=1080">
20
+ <style>
21
+ .card { object-fit: cover; border-radius: 24px; }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <section>
26
+ <iframe src="title.html"></iframe>
27
+ <img src="assets/logo.png#t=0,3" at="6" style="left: 1640px; top: 60px; width: 200px">
28
+ </section>
29
+ <section>
30
+ <video src="assets/a.mp4#t=2,8" at="2" class="card" style="left: 160px; top: 90px; width: 1600px; height: 700px"></video>
31
+ </section>
32
+ <section>
33
+ <audio src="assets/bed.mp3" volume="0.6"></audio>
34
+ </section>
35
+ </body>
36
+ </html>
37
+
38
+ - The viewport is the film's frame, in CSS pixels; without it, 1920×1080.
39
+ - Each `<section>` is a track of clips that do not overlap; any clip goes on any track. The sections are the tracks as Studio's timeline shows them, top first: the first one's pictures are on top, so titles go before the footage they cover, and the background last. What a page does not paint is transparent; a moment with no clip is black.
40
+ - A track the person `locked` is theirs: change nothing on it. A `hidden` track shows and plays nothing; a `muted` one plays no sound.
41
+ - A clip is an element: `<iframe>` a page, `<video>`, `<img>` a picture, `<audio>` a sound.
42
+ - `src`: its file, relative to film.html. `#t=in,out` says which seconds of it play, `#t=in` to its end; without it, all of them. A picture, or a page without a `duration`, needs `#t=0,length`: how long it stays.
43
+ - `at`: where it starts in the film, in seconds; without it, 0.
44
+ - `left`, `top`, `width`, `height` in its `style` (px) and `rotate` (degrees, clockwise): where it sits. Without them, a video or picture is the stage, and a page is its own size, centered. With only `width` or `height`, the other keeps its proportions.
45
+ - Its look is CSS, in `class` or `style`: a video or picture fits whole in its box unless you say otherwise (`object-fit: cover` crops it to the box). The film's CSS holds still; what moves is a page.
46
+ - `speed` (video and sound): 0.25 to 4. The clip lasts `(out − in) / speed` seconds. To make it faster or slower, change `speed` only: never make the file again at another speed, nor move its `.vtt`'s times. Its subtitles follow `speed` on their own.
47
+ - `volume` (video and sound): 1 is the file's own level; `muted` silences it.
48
+ - `id`: a name, unique in the film; without it, the file's name.
49
+ - `overrides`: the person's changes made in Studio. Keep them as they are.
50
+ - `size` (page): the px a page without its own `width` and `height` is laid out at; without it, the stage's. Studio sets it when the frame changes shape. Keep it as it is; remove it only when you lay the page out again for the new frame.
51
+ - Cut a long recording by trimming it: every clip of it is the same file with its own `#t=`. Never cut, crop or re-encode media into new files; CSS crops.
52
+
53
+ ## A page
54
+
55
+ <script type="module">
56
+ window.film = {
57
+ frame(t) { /* draw the whole picture at t seconds */ },
58
+ duration: 6, // optional: the page's own length, in seconds
59
+ ready: loadEverything(), // optional: a Promise; drawing starts once it settles
60
+ width: 480, height: 320, // optional: a page that is only a part of the picture
61
+ };
62
+ </script>
63
+
64
+ **The one rule: the picture depends only on t.** The same t always draws the same picture, whatever was drawn before and in whatever order.
65
+
66
+ - One shot per page: a film of five shots is five pages cut together in film.html, so the person can trim, reorder and replace each one. What the pages share (fonts, colors, a logo) goes in a file they load.
67
+ - Studio edits elements, not pixels: make what the person may change a DOM element.
68
+ - Without `width` and `height`, a page is the size of the stage: lay it out for it. With them (a chart in a corner), it is that size, and its clip's style puts it in place.
69
+ - A page's t is its own time, starting at 0. Where it sits in the film, and for how long, is film.html's.
70
+ - The host calls `frame(t)`; a page never animates by itself (no animation loop, no autoplay).
71
+ - The picture is captured once `frame` returns, or once the Promise it returns resolves. Create a WebGL context with `preserveDrawingBuffer: true`.
72
+ - A page has no sound: every sound is a clip in film.html. A shot's sound effect goes at the film second it plays: the page clip's `at`, plus the page's t, less the clip's in point (a hit at t = 2 in a clip at 5 goes at 7). When you move the page clip, move its sounds with it.
73
+
74
+ ## Tools
75
+
76
+ Run them in the film's folder. Each one's `--help` lists its options:
77
+ - `npx @autoaiai/aicut open <folder>`: first. A new film gets a folder of its own, named after it (`open launch-video`); an existing one opens, and a GitHub address of one (a repository, or `…/tree/main/<folder>`) is copied into the projects library and opened. It prints Studio's address: open it in your built-in browser before you write anything and keep it open, so the person watches every change land and edits too. Without a built-in browser, Studio opens in the person's default browser by itself. `open` alone shows the film you are in, else the last one: run it when the person only asks to open AICUT.
78
+ - `npx @autoaiai/aicut look`: a contact sheet of the whole film with its sound, and a check that the same t draws the same picture. `look 4.5` is one frame at full size, `look 3-6` a stretch, `look assets/a.mp4` a media file.
79
+ - `npx @autoaiai/aicut render`: the MP4, beside film.html (30 fps unless `--fps`). Only when the person asks for the file: you check with `look`, and the person exports from Studio.
80
+ - `npx @autoaiai/aicut get`: voice-over, music, sound effects, transcripts, images, image search, video and web search. Each kind works only once the person has connected a service that makes it, with their own key, in Studio (Settings → Providers); Studio's subtitle translation too. Run it alone to see what works now and which services make the rest. When a kind is not connected, tell the person which services make it and where they connect one. A video clip costs a lot: the person confirms it in Studio first.
81
+
82
+ The words of a sound or a video are a WebVTT beside it with the same name (`vo/1.wav` → `vo/1.vtt`): a cue per subtitle line, a timestamp tag at each word. Studio makes the film's subtitles from them; never draw subtitles in a page.
83
+
84
+ Media can come from `get`, any service the person uses (with its API docs and their key), or your own code. Save it under `assets/`.
package/README.md CHANGED
@@ -1,3 +1,37 @@
1
- # Temporary Holding Version
1
+ # aicut
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The open source AI video agent: films as web pages, the manual that teaches your coding agent to make them, the
4
+ `aicut` command, and Studio, the editor to change anything in them.
5
+
6
+ Ask Codex, Claude Code or any coding agent to make a film. It writes the film as web pages (`film.html` cuts them
7
+ together with footage, pictures and sound), and Studio, a local editor in your browser, shows it taking shape. Then
8
+ change anything you like, by hand or by asking.
9
+
10
+ ## Install
11
+
12
+ Requires Node 22.2+ and ffmpeg, on macOS, Windows or Linux; the first run downloads a headless Chromium.
13
+
14
+ ```sh
15
+ npx skills add autoaiai/aicut # choose your agents; -g for every project, --all for every agent
16
+ ```
17
+
18
+ Then ask your agent for a film. If your agent has no skills, tell it: "Run `npx -y @autoaiai/aicut@latest` and follow the
19
+ manual it prints."
20
+
21
+ ## Commands
22
+
23
+ ```sh
24
+ aicut # the manual: all an agent needs to know
25
+ aicut open [folder | url] # show a film in Studio (a GitHub address fetches it first)
26
+ aicut look [what] [t | a-b] # check the film, a page or a media file
27
+ aicut render [a-b] # the MP4; --4k, --blur, --alpha
28
+ aicut get [what] # voice-over, music, sound effects, transcripts, images, video, from your services
29
+ ```
30
+
31
+ `MANUAL.md` (in this package) is what `aicut` tells your agent, and `SPEC.md` is the film format, for anyone
32
+ building a player, renderer or editor. Settings, how it works and the source:
33
+ [github.com/autoaiai/aicut](https://github.com/autoaiai/aicut).
34
+
35
+ ## License
36
+
37
+ [MIT](LICENSE). The AICUT name and logo are trademarks: see [TRADEMARK.md](TRADEMARK.md).
package/SPEC.md ADDED
@@ -0,0 +1,221 @@
1
+ # AICUT Protocol 0.1 (draft)
2
+
3
+ A film is a web page, `film.html`, that cuts pages, footage, pictures and sound together. A page is a web page that
4
+ can draw any moment of itself.
5
+
6
+ That is the whole idea. A page exposes one object, `window.film`, so it can be previewed, scrubbed, checked,
7
+ rendered to video, cut into a timeline and edited by any host that speaks this protocol. It may be written with any
8
+ library or none: three.js, p5, Canvas 2D, WebGL, GSAP, SVG, plain DOM. The protocol says nothing about how a page
9
+ draws, only how a host asks it to.
10
+
11
+ The key words MUST, SHOULD and MAY are used as in RFC 2119.
12
+
13
+ ## 1. The film: film.html
14
+
15
+ The edit, the part a person adjusts by hand in an editor, is `film.html`: HTML that says which clip plays when and
16
+ where, and CSS that says how each looks. A page draws a shot.
17
+
18
+ ```html
19
+ <!doctype html>
20
+ <html>
21
+ <head>
22
+ <meta charset="utf-8">
23
+ <meta name="viewport" content="width=1080, height=1920">
24
+ <style>
25
+ .card { object-fit: cover; object-position: top; border-radius: 24px; }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <section>
30
+ <iframe id="title" src="scenes/title.html" at="1" style="left: 80px; top: 120px; width: 920px"
31
+ overrides='[{"at":"#title","text":"Aura Pods","style":{"color":"#7fd4ff"},"t":[0,-40]}]'></iframe>
32
+ </section>
33
+ <section>
34
+ <video id="open" src="assets/a.mp4#t=12.2,15" speed="0.5" volume="0.8" class="card"
35
+ style="left: 56px; top: 700px; width: 968px; height: 474px" overrides='[{"fade":[0.5,0]}]'></video>
36
+ </section>
37
+ <section>
38
+ <audio id="bed" src="assets/bed.mp3#t=0,10" volume="0.5"></audio>
39
+ </section>
40
+ </body>
41
+ </html>
42
+ ```
43
+
44
+ - The stage, the frame in CSS pixels, is the viewport: `<meta name="viewport" content="width=…, height=…">`;
45
+ without one, 1920 × 1080. The head is the film's own (styles, fonts), with no script: what moves is a page.
46
+ - The body is the tracks, each a `<section>`, in the order a timeline shows them: the first on top (a host stacks
47
+ them; a page's own paint order does not). A track has no kind: any clip goes on any track. A track's clips do not
48
+ overlap; overlapping pictures (a picture-in-picture, a title over footage) sit on different tracks. A track may be
49
+ `hidden` (no picture and no sound), `muted` (no sound) or `locked`: the person's, so an agent changes nothing on it
50
+ and an editor keeps it from being changed. Like any element of the film it may carry `id`, `class`, `title`,
51
+ `lang`, `dir`, `aria-*` and `data-*`, which say nothing to the film.
52
+ - A clip is an element: `<iframe>` a page, `<video>`, `<img>` a still picture, `<audio>` a sound, holding nothing.
53
+ - `src`: its file, relative to film.html. Which seconds of it play is a media fragment: `#t=in,out` in the file's
54
+ own seconds, `#t=in` to its end; without one, all of it. A still, or a page without a `duration`, has no end:
55
+ its `#t=0,length` is required, and is how long it stays.
56
+ - `id`: unique across the film; without it, the file's name.
57
+ - `at`: where it starts on the film, in seconds from 0 (default 0).
58
+ - `speed` on videos and sounds: the playback rate, 0.25 to 4, sound keeping its pitch. The clip lasts
59
+ `(out − in) / speed` on the film.
60
+ - `volume` on videos and sounds: linear gain, 1 the file's own level; `muted` is 0.
61
+ - `size` on pages: `size="1920x1080"`, the CSS pixels a page without its own `width` and `height` (§2) is laid
62
+ out at; without it, the stage's. An editor writes it when the stage changes shape, so such a page is scaled into
63
+ its box like a video rather than laid out again in the new shape.
64
+ - Where a picture sits is its own style's `left`, `top`, `width` and `height` in stage pixels, and `rotate` in
65
+ degrees, clockwise about its center. Nothing else places a clip: the host does, on the element, so no other CSS
66
+ moves it (`position`, `transform`, `margin` and the like in a clip's style are an error). `width` and `height`
67
+ default to the picture's own size (a page's `width × height` (§2), a video's or still's pixels); with one of them,
68
+ the other follows the picture's proportions. A video or still is its box, the picture in it as its CSS says
69
+ (`object-fit: contain` unless the film says otherwise: `cover` crops it to the box); without a box it is the
70
+ stage. A page is fitted whole inside its box, centered; without one it is its own size, centered, so a page
71
+ without a size covers the stage.
72
+ - How a picture looks is CSS: its `class` and the rest of its `style` (`border-radius`, `clip-path`, `mask`,
73
+ `filter`, `opacity`, `mix-blend-mode`…), and the film's `<style>`. The film's CSS has no time: what moves is a
74
+ page.
75
+ - `overrides` are the person's changes made in Studio, as JSON, a list of entries. An entry with `at` (a CSS
76
+ selector), on a page clip only, tweaks an element of the page, applied by the host after each frame: optional `n`
77
+ (which match, from 1), `t` (translate px), `s` (scale), `r` (rotate degrees) as the independent CSS transform
78
+ properties so they compose with the page's own animation, `style` (CSS properties, applied with priority), `text`
79
+ (replaces the element's own text: all of it when it has no child elements or only `<br>`s, a line break in `text`
80
+ being a `<br>`; with other inline children, its first run of text, the children kept), `lock` (`true`: an editor keeps the element from being selected or moved; it changes
81
+ nothing in the picture). An entry without `at`, at most one, on any clip, is the clip's own: `fade: [in, out]`,
82
+ seconds (0 or more, to the millisecond, together no longer than the clip): its picture's opacity and its sound's
83
+ gain ramp linearly from 0 to 1 over its first `in` seconds and from 1 to 0 over its last `out` seconds, times its
84
+ own opacity and volume.
85
+ - `alt`, `title`, `lang`, `dir`, `data-*` and `aria-*` are kept and say nothing to the film.
86
+ - Times (`at`, the fragment's) are seconds to the millisecond: a host rounds them to three decimals as it reads them,
87
+ and an editor writes no more (no frame shows a finer difference). Each is rounded alone, so clips on a track that
88
+ overlap by a millisecond or less touch.
89
+ - A page clip: the host calls the page's `frame` with the clip's local time `in + (t − at)`, holding the page's
90
+ last frame when the clip runs past the page's `duration`.
91
+ - Anything else is an error: a host reads film.html strictly, so a misspelled attribute or a stray element is
92
+ reported, at its line, not ignored. The reference reading is `src/film-doc.mjs`.
93
+ - The film's sound is its sound clips and the sound of its video clips, each unless its volume is 0 or its track is
94
+ muted or hidden.
95
+
96
+ A host plays film.html as a film: served, it is itself a protocol page (the reference host puts its timeline,
97
+ `src/timeline.mjs`, in it), so a film can be previewed, checked and rendered like any page. Editors change
98
+ film.html, writing over only what they changed; the pages stay as their authors wrote them.
99
+
100
+ What film.html says is kept to what an agent and a person must agree on, so it hardly ever changes. An attribute is
101
+ added only when it changes the film (or is an agreement between the person and the agent) and cannot be CSS, a page
102
+ or a file: a look is CSS, a title is a page, a transition is a page or clips that overlap and fade, a treated recording
103
+ is a file. An editor's own state (its history, caches, what it shows) lives in its own folder (`.film/`), not in
104
+ film.html.
105
+
106
+ A transition that is a page is cut in over the picture across the cut, on a track of its own: Studio's are
107
+ `transitions/fx-<kind>-<ms>.html`, the kind and the length in the name, so a clip says what it is without a query and
108
+ an agent reads it as any other page (studio/ui/src/lib/transition-fx.ts writes them). One is drawn from the time it is
109
+ given alone: an effect's peak is the middle of its span, which is the cut, and both its ends are clear, so nothing pops
110
+ when it starts or ends.
111
+
112
+ ### Sound
113
+
114
+ A page has no sound. Every sound in a film is a clip: an `<audio>`, or a `<video>`'s own sound, each one placed,
115
+ trimmed, muted and set in volume on the timeline like any clip. A sound that belongs to a shot is placed at the film
116
+ second the shot plays it; moving the page clip does not move it. The host mixes and encodes the sound; it never
117
+ records it from the page.
118
+
119
+ Timing that follows sound (beats, onsets, word timings) is data, not API: precompute it into files the page reads
120
+ during `ready`. Hosts MAY provide tools that write such files.
121
+
122
+ ### Words
123
+
124
+ What a sound or a video says is a WebVTT file beside it with the same name (`assets/vo/1.wav` → `assets/vo/1.vtt`),
125
+ in the file's own seconds: a cue is a line, timestamp tags inside it time its words, and a voice tag names who speaks.
126
+ Its translations are beside it as `1.<language>.vtt`, the same lines at the same times. A file that begins with
127
+ `NOTE Each cue is one subtitle, as cut by hand in AICUT Studio.` has its cues as the person cut them: each is shown
128
+ as one subtitle as it is, where an editor would otherwise cut long lines into subtitles. An editor's subtitles are the
129
+ lines of the clips that can be heard, placed by each clip's `at`, its `#t=` fragment and `speed`; a host draws none
130
+ of them into the picture.
131
+
132
+ ## 2. The page
133
+
134
+ ```js
135
+ window.film = {
136
+ frame(t) { /* the picture at t seconds */ }, // may return a Promise
137
+ duration: 12, // optional: its own length in seconds, finite, > 0
138
+ ready: loadEverything(), // optional: a Promise, or a function returning one
139
+ width: 480, height: 320, // optional: a page that is part of the picture; without them, the stage
140
+ };
141
+ ```
142
+
143
+ | Field | Type | Meaning |
144
+ | --- | --- | --- |
145
+ | `frame(t)` | function | Draw the complete picture for time `t ≥ 0` (below `duration` when there is one). If it returns a Promise, the picture is complete when it resolves. |
146
+ | `duration` | number | Optional. The page's own length in seconds. Without it a page has no end, like a still: its clip's media fragment (`title.html#t=0,4`) says how long it plays, and a range is given to check or render it alone. |
147
+ | `ready` | Promise \| () => Promise | Optional. Resolves when fonts, images, models and data are loaded. The host calls `frame` only after it settles. For web fonts, load each face used (`document.fonts.load('700 64px Fraunces')`), not only `document.fonts.ready`. |
148
+ | `width`, `height` | number | Optional. The page's size in CSS pixels, for a page that is part of the picture (a chart in a corner). Without them the page is its clip's `size` (§1), else the size of the stage, or 1920 × 1080 opened on its own. The host sizes the viewport to it; higher resolutions use device pixel ratio. |
149
+
150
+ The frame rate is not the page's: a film is timed in seconds, and the rate is chosen when it is rendered.
151
+
152
+ A page SHOULD set `window.film` before its `load` event. Hosts wait for it to appear (for at least 10 seconds
153
+ after `load`) before reporting the page as not a film.
154
+
155
+ ## 3. The one rule: a frame is a function of t
156
+
157
+ For the same `t`, `frame` MUST produce the same picture, whatever was drawn before it and in whatever order frames
158
+ are requested. Hosts rely on this to scrub, to render frames in parallel, to render fractions of a frame for motion
159
+ blur, and to re-render one shot without the rest.
160
+
161
+ In practice:
162
+
163
+ - Read time from `t`. A host holds the page's clocks at `t` while it draws (§4), so `performance.now()`,
164
+ `requestAnimationFrame` timestamps, `Date`, CSS animations and `Math.random` agree with it; timers and history
165
+ do not.
166
+ - Repaint everything each call. Do not accumulate (`x += v`) across calls; if something has history (a trail, a
167
+ physics run), compute it from `t`, or precompute it during `ready` and look it up.
168
+ - Tweens that record their start values the first time they run (GSAP `to`, `from`) remember whatever was on screen
169
+ then, which depends on the order frames were drawn. Give every tween explicit start and end values (`fromTo`).
170
+ - Any library that runs its own clock is driven by `t` instead: `timeline.seek(t)`, `animation.currentTime = t * 1000`,
171
+ `p5.redraw()`, `renderer.render(scene, camera)`, `video.currentTime = …` (awaiting `seeked`).
172
+
173
+ Notes for authors:
174
+
175
+ - `window.film` may be assigned from a module script (`<script type="module">`): modules run before `load`.
176
+ - A WebGL canvas is captured after `frame` returns. Draw inside `frame` and create the context with
177
+ `preserveDrawingBuffer: true`, or the host may capture a cleared buffer.
178
+ - Lay the page out for its viewport (`innerWidth` × `innerHeight`, in CSS pixels): the same page can then be cut
179
+ into films of other shapes. Hosts raise resolution with device pixel ratio, so a canvas should allocate
180
+ `innerWidth × devicePixelRatio` pixels.
181
+ - Repeating a frame is not always bit-identical in a browser (layers are re-rasterized as they change). Hosts compare
182
+ repeated frames with a tolerance, not byte for byte.
183
+
184
+ ## 4. Hosts
185
+
186
+ Before any page script runs, a host sets `window.filmHost = { version: '0.1', name }`. When it is present the page
187
+ MUST NOT advance on its own (no self-running loop, no autoplay of media or sound). When it is absent the page is
188
+ being opened as an ordinary web page and SHOULD play itself, so every film is also a page you can just open:
189
+
190
+ ```js
191
+ if (!window.filmHost) {
192
+ const t0 = performance.now();
193
+ const loop = () => { const t = (performance.now() - t0) / 1000; film.frame(film.duration ? t % film.duration : t); requestAnimationFrame(loop); };
194
+ Promise.resolve(typeof film.ready === 'function' ? film.ready() : film.ready).then(loop);
195
+ }
196
+ ```
197
+
198
+ A host:
199
+
200
+ - serves the page over HTTP (not `file://`), with its project (the folder of the film.html around it, else the
201
+ page's own folder) as the root, so a page can load the project's assets;
202
+ - sizes the viewport to the page's `width × height`, or the stage's (times a device pixel ratio for higher
203
+ resolutions);
204
+ - awaits `ready`, then calls `frame(t)` and awaits its result before capturing the viewport;
205
+ - MAY call `frame` concurrently in separate page instances, and at fractional frame times;
206
+ - reports uncaught errors and failed requests from the page.
207
+
208
+ From the first frame it draws, a host holds the page's clocks at the frame's `t`: `performance.now()` and
209
+ `requestAnimationFrame` timestamps read `t` in milliseconds, `Date` reads `t` after the epoch 2026-01-01T00:00:00Z,
210
+ CSS and Web Animations the page left running stand at `t` (a CSS transition, whose progress is history, is
211
+ finished), and `Math.random` restarts from a seed of `t` (while loading it runs from a fixed seed). Before the first
212
+ frame (`ready`, loading) the page runs on real time. The reference host's prelude is `FILM_CLOCK` in
213
+ `src/host.mjs`: `filmHost.time(t)` before `frame(t)`, `filmHost.settle(t)` after it.
214
+
215
+ A page may play footage with native `<video>` elements, silent, and set `currentTime` from `t`. The host makes
216
+ that exact: a media seek lands inside the frame it names, and the host waits until each video's new frame is on
217
+ screen before it captures.
218
+
219
+ ## 5. Versioning
220
+
221
+ This is version 0.1. Attributes may be added (§1 says when); their absence must keep meaning what it means today.
@@ -0,0 +1,10 @@
1
+ # Third-party notices
2
+
3
+ Studio's editor (`studio/ui/dist`) bundles:
4
+
5
+ - **Inter** by Rasmus Andersson, via `@fontsource-variable/inter`: SIL Open Font License 1.1
6
+ (https://openfontlicense.org).
7
+ - **React** and **React DOM**: MIT License, © Meta Platforms, Inc. and affiliates.
8
+ - **Lucide** icons (`lucide-react`): ISC License, © Lucide Contributors.
9
+
10
+ The CLI depends on **playwright-core** (Apache License 2.0), which downloads a headless Chromium on first use.
package/TRADEMARK.md ADDED
@@ -0,0 +1,25 @@
1
+ # AiCut trademarks
2
+
3
+ The code in this repository is licensed under [MIT](LICENSE): use it, change it, ship it. The license covers the code,
4
+ not the AiCut name and logo. This page says how you can use them, so that people always
5
+ know what comes from the AiCut project and what does not.
6
+
7
+ ## You can
8
+
9
+ - Say what your work does with AiCut: "made with AiCut", "an AiCut film", "works with AiCut", "for the
10
+ AiCut format".
11
+ - Refer to AiCut in articles, talks, tutorials, courses and videos.
12
+ - Fork this repository and keep the name while you work on changes you mean to send back.
13
+ - Redistribute unmodified releases under their own name.
14
+
15
+ ## You can't, without written permission
16
+
17
+ - Name a modified version, a product, a hosted service or a company "AiCut", or anything easily confused with it.
18
+ A fork you publish as your own needs its own name.
19
+ - Use the AiCut logo for your own product or service, or a logo that looks like it.
20
+ - Suggest that the AiCut project made, endorses or supports your work when it does not.
21
+ - Use "aicut" in a domain name, package name or account name in a way that looks official.
22
+
23
+ ## Questions
24
+
25
+ Write to support@aicut.dev. If you are not sure whether a use is fine, ask: most are.