@praeko/studio-kit 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/LICENSES.md +36 -0
- package/README.md +17 -2
- package/dist/color.d.ts +3 -0
- package/dist/declarative.d.ts +67 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +503 -0
- package/dist/long-cover.d.ts +31 -0
- package/dist/long-decl.d.ts +67 -0
- package/dist/long-parts.d.ts +57 -0
- package/dist/parts.d.ts +90 -0
- package/package.json +63 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
## [0.2.0] - unreleased
|
|
6
|
+
|
|
7
|
+
Breaking: the whole public API is English, and the declarative components read the English `episode.spec.json` only. There are no deprecated aliases; regenerate the spec with `praeko studio compile` (or run `praeko studio migrate`). Published types are generated by `tsc`, and their relative specifiers end in `.js`.
|
|
8
|
+
|
|
9
|
+
- Parts: `PainelD` -> `Panel` (`titulo` -> `title`), `Caixa` -> `Box` (`cor` -> `color`), `Linha` -> `Line`, `Big` (`cor` -> `color`), `Balao` -> `Bubble` (`autor` -> `author`), `Codigo` -> `Code` (`titulo` -> `title`, `linhas` -> `lines`, `cor` -> `color`, `marca` -> `mark`), `Janela` -> `Window` (`duracao` -> `clipDuration`, `de` -> `from`), `Celular` -> `Phone`, `Gato` -> `Mascot` (`nome` -> `name`, `tam` -> `size`; the image path comes from the theme `mascotPath`), `Moldura` -> `Frame` (`titulo` -> `title`, `cor` -> `color`, `tam` -> `size`).
|
|
10
|
+
- Declarative: `EpisodioDecl` -> `EpisodeDecl` (props `spec`, `words`, `theme`, `components`; were `spec`, `palavras`, `tema`, `pecas`), `EpSpec` -> `EpisodeSpec`, `CenaSpec` -> `SceneSpec`, `epDataDeSpec` -> `episodeDataFromSpec`, `LongoDecl` -> `LongDecl`, `LongoSpec` -> `LongSpec`, `CenaLSpec` -> `LongSceneSpec`, `longoDeSpec` -> `longDataFromSpec`, `CapaLongoSpec` -> `LongCoverSpec`, `CapasLongo` -> `LongCovers`, `CapaLongo16x9` -> `LongCover16x9`, `CapaLongoVertical` -> `LongCoverVertical`.
|
|
11
|
+
- Long parts: `CodigoL` -> `CodeL`, `CartaoL` -> `CardL`, `TxtL` -> `TextL`, `surgeL` -> `appearL`, `LinhaCodigo` -> `CodeLine`; props `titulo` -> `title`, `cor` -> `color`.
|
|
12
|
+
- Spec shape: see `docs/studio-ingles.md` (4.3, 4.4, 4.7) in the repository; new `resolveColor` helper.
|
|
13
|
+
- Defaults: the `Mascot` card no longer defaults to a game-specific path, and the `Bubble` default author is `— someone, in the chat`. Peer dependency: `@praeko/studio-core` ^0.2.0.
|
|
14
|
+
|
|
15
|
+
## [0.1.0] - 2026-11-07
|
|
16
|
+
|
|
17
|
+
First published version, extracted from the project where it was born. Publishes compiled `dist/` (ESM + `.d.ts`); `react` and `remotion` are peers.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marcone Santos
|
|
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/LICENSES.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Praeko Studio licenses
|
|
2
|
+
|
|
3
|
+
Verified on 2026-11-07. This file is not legal advice; each vendor's own text prevails.
|
|
4
|
+
|
|
5
|
+
## Praeko Studio's code
|
|
6
|
+
The packages `@praeko/studio`, `@praeko/studio-core` and `@praeko/studio-kit` are **MIT** (see `LICENSE`, Copyright (c) 2026 Marcone Santos), like `@praeko/sdk` and `@praeko/cli`. The sound effects (`pop`, `ding`, `thump`, `whoosh`) and the `avatar.png` of the project template were created for Studio (sounds synthesized by `scripts/generate-sfx.mjs`, with no third-party samples) and follow the same MIT license. The package ships no fonts, music, stock images or third-party art.
|
|
7
|
+
|
|
8
|
+
## Notice you need to see (Remotion is NOT MIT)
|
|
9
|
+
> **Remotion.** Remotion is *source-available*, not open source. It is **free** for individuals, non-profits, evaluation and companies with **up to 3 employees** (commercial use included). Companies with **4 or more people** need the **Company License** (remotion.pro/license). You install and run Remotion, so the license is **yours**, not Praeko's. Remotion also sells plans for those who **automate renders by program or agent** (Creators, Automators); their FAQ does not expressly say whether a local agent running `remotion render` counts: **ask Remotion** before using at scale. Praeko Studio does not copy or modify Remotion code: it uses it as a dependency (peer) that you install.
|
|
10
|
+
|
|
11
|
+
This notice appears in the `README`, at install time (`praeko studio init`), in `praeko studio doctor` and in `praeko studio licenses` (`--accept` records that you read it in `~/.config/praeko/studio/licenses.json`).
|
|
12
|
+
|
|
13
|
+
**Engines.** Remotion is the default engine for videos **and** covers. With `--engine hyperframes` (or `"engine": "hyperframes"` in `brand.json`) the video **and** the cover are rendered by HyperFrames and nothing from Remotion is used, so a project that only uses HyperFrames does not need a Remotion license. Scenes or covers written as TSX (`component:`) only exist in Remotion.
|
|
14
|
+
|
|
15
|
+
**We do not promise "free forever":** Remotion's conditions may change (their own page warns that the license changes in Remotion 5.0). Review the license when you upgrade.
|
|
16
|
+
|
|
17
|
+
## Third-party dependencies
|
|
18
|
+
| Component | Where | License | Source |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| `remotion`, `@remotion/cli` (4.0.539) | installed in the user's **project** (peer of core/kit) | Remotion License (see above), not MIT | remotion.pro/license; github.com/remotion-dev/remotion (LICENSE.md) |
|
|
21
|
+
| `react`, `react-dom` (19.2.3) | user's project (peer) | MIT | each package's package.json |
|
|
22
|
+
| `@praeko/studio-core`, `-kit` | user's project | MIT | LICENSE |
|
|
23
|
+
| `yaml` (2.9.1) | embedded in the `@praeko/studio` bundle | ISC | package.json |
|
|
24
|
+
| `@elevenlabs/elevenlabs-js` (2.x) | dependency of `@praeko/studio` | MIT | package.json |
|
|
25
|
+
| `hyperframes` (0.9.13, optional engine) | downloaded by `npx` the first time you use `--engine hyperframes` | Apache-2.0 | github.com/heygen-com/hyperframes |
|
|
26
|
+
| `typescript`, `esbuild`, `@types/*` | development/build only | Apache-2.0 / MIT | package.json |
|
|
27
|
+
| ffmpeg / ffprobe | you install | LGPL or GPL, depending on the build | ffmpeg.org/legal.html |
|
|
28
|
+
| Chrome Headless Shell / Chromium | Remotion downloads it on the first render | Chromium/Google licenses | remotion.dev/docs/miscellaneous/chrome-headless-shell |
|
|
29
|
+
|
|
30
|
+
## Services and models (you sign up)
|
|
31
|
+
- **ElevenLabs**: its terms and your plan (voice cloning only with rights over the voice). The key is yours; Studio never prints it.
|
|
32
|
+
- **Local VoiceStudio** (optional): the OmniVoice model is **CC-BY-NC (non-commercial use)**. It is not the default in any Studio configuration and **must not be used in monetized content**. Check the license of any local model you use.
|
|
33
|
+
- **YouTube, Instagram, TikTok**: each network's terms and synthetic-content rules (see `praeko studio docs publish`).
|
|
34
|
+
|
|
35
|
+
## Fonts and media
|
|
36
|
+
No font is distributed. The project template uses system fonts (`Arial Black`, `Menlo`) that `doctor` checks; swap them in `brand.json` for open fonts (OFL) if you want the video to look the same on every system. Music, images and clips you add are your responsibility (rights and licenses).
|
package/README.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @praeko/studio-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Praeko Studio scene parts (panel, box, line, bubble, code, window, phone, frame) and the declarative episode built from the compiled `episode.yaml`. A library for projects created with [`@praeko/studio`](https://www.npmjs.com/package/@praeko/studio) (`praeko studio init` installs and configures it).
|
|
4
|
+
|
|
5
|
+
> **Licenses.** This package is MIT and uses **Remotion** as a peer dependency that **you** install. Remotion is **not MIT**: free for individuals, non-profits and companies of up to 3 employees; companies of 4 or more people need the Company License ([remotion.pro/license](https://www.remotion.pro/license)). Details in [LICENSES.md](./LICENSES.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
```sh
|
|
9
|
+
# npm
|
|
10
|
+
npm install @praeko/studio-kit remotion react
|
|
11
|
+
# yarn
|
|
12
|
+
yarn add @praeko/studio-kit remotion react
|
|
13
|
+
# pnpm
|
|
14
|
+
pnpm add @praeko/studio-kit remotion react
|
|
15
|
+
# bun
|
|
16
|
+
bun add @praeko/studio-kit remotion react
|
|
17
|
+
```
|
|
18
|
+
Peers: `remotion` ^4.0.539 (all `@remotion/*` packages on the same version) and `react` ^19. Node 22 or newer. Usage: `praeko studio docs episode-yaml`.
|
package/dist/color.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { EpisodeData, Theme, Word } from "@praeko/studio-core";
|
|
2
|
+
type Color = string;
|
|
3
|
+
export type Item = {
|
|
4
|
+
type: string;
|
|
5
|
+
when?: string | [string, number];
|
|
6
|
+
at?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
pos?: {
|
|
9
|
+
left?: number;
|
|
10
|
+
right?: number;
|
|
11
|
+
top?: number;
|
|
12
|
+
bottom?: number;
|
|
13
|
+
w?: number;
|
|
14
|
+
h?: number;
|
|
15
|
+
};
|
|
16
|
+
[k: string]: any;
|
|
17
|
+
};
|
|
18
|
+
export type SceneSpec = {
|
|
19
|
+
start: number;
|
|
20
|
+
end?: number;
|
|
21
|
+
label?: string;
|
|
22
|
+
sounds?: {
|
|
23
|
+
at: number;
|
|
24
|
+
sound: "pop" | "ding" | "thump" | "whoosh";
|
|
25
|
+
v?: number;
|
|
26
|
+
}[];
|
|
27
|
+
layout?: "panel" | "free";
|
|
28
|
+
title?: string;
|
|
29
|
+
items?: Item[];
|
|
30
|
+
component?: string;
|
|
31
|
+
};
|
|
32
|
+
export type EpisodeSpec = {
|
|
33
|
+
id: string;
|
|
34
|
+
durS: number;
|
|
35
|
+
captionPx?: number;
|
|
36
|
+
voice: string;
|
|
37
|
+
backgroundLines?: string[];
|
|
38
|
+
engine?: string;
|
|
39
|
+
hits?: {
|
|
40
|
+
at: number;
|
|
41
|
+
type: "shake" | "zoom" | "flash" | "all";
|
|
42
|
+
}[];
|
|
43
|
+
outro: {
|
|
44
|
+
a: number;
|
|
45
|
+
cta: string;
|
|
46
|
+
};
|
|
47
|
+
scenes: SceneSpec[];
|
|
48
|
+
cover?: {
|
|
49
|
+
id: string;
|
|
50
|
+
component?: string;
|
|
51
|
+
title?: [string, string, string?];
|
|
52
|
+
color?: Color;
|
|
53
|
+
glow?: string;
|
|
54
|
+
size?: number;
|
|
55
|
+
items?: Item[];
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/** Builds the EpisodeData: each scene becomes a component (declarative or, with `component`, a TSX of the project). */
|
|
59
|
+
export declare function episodeDataFromSpec(spec: EpisodeSpec, words: Word[], components?: Record<string, React.FC<any>>): EpisodeData;
|
|
60
|
+
/** `<EpisodeDecl spec words theme components />` registers the episode Composition and the cover Still. */
|
|
61
|
+
export declare const EpisodeDecl: React.FC<{
|
|
62
|
+
spec: EpisodeSpec;
|
|
63
|
+
words: Word[];
|
|
64
|
+
theme: Theme;
|
|
65
|
+
components?: Record<string, React.FC<any>>;
|
|
66
|
+
}>;
|
|
67
|
+
export {};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
// src/parts.tsx
|
|
2
|
+
import { Loop, OffthreadVideo, interpolate, spring, staticFile, useCurrentFrame, useVideoConfig, Img, AbsoluteFill } from "remotion";
|
|
3
|
+
import { FPS, appear, pop, ramp, rgba, useTheme } from "@praeko/studio-core";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var Panel = ({ title, dur, children }) => {
|
|
6
|
+
const { accent: LAMP, text: INK, font: FONT } = useTheme();
|
|
7
|
+
const frame = useCurrentFrame();
|
|
8
|
+
const { fps } = useVideoConfig();
|
|
9
|
+
const pop3 = spring({ frame, fps, config: { damping: 13, stiffness: 190 } });
|
|
10
|
+
const out = interpolate(frame, [dur * FPS - 6, dur * FPS], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
|
|
11
|
+
return /* @__PURE__ */ jsxs(
|
|
12
|
+
"div",
|
|
13
|
+
{
|
|
14
|
+
style: {
|
|
15
|
+
position: "absolute",
|
|
16
|
+
left: 70,
|
|
17
|
+
right: 70,
|
|
18
|
+
top: 330,
|
|
19
|
+
height: 1e3,
|
|
20
|
+
display: "flex",
|
|
21
|
+
flexDirection: "column",
|
|
22
|
+
background: "rgba(20,17,15,.94)",
|
|
23
|
+
border: `4px solid ${LAMP}`,
|
|
24
|
+
borderRadius: 36,
|
|
25
|
+
boxShadow: "0 24px 60px rgba(0,0,0,.6)",
|
|
26
|
+
padding: "40px 48px",
|
|
27
|
+
fontFamily: FONT,
|
|
28
|
+
color: INK,
|
|
29
|
+
transform: `scale(${0.82 + 0.18 * pop3}) translateY(${(1 - pop3) * 60}px)`,
|
|
30
|
+
opacity: Math.min(1, pop3 * 1.6) * out,
|
|
31
|
+
overflow: "hidden",
|
|
32
|
+
boxSizing: "border-box"
|
|
33
|
+
},
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 38, letterSpacing: 5, color: LAMP, whiteSpace: "nowrap" }, children: title }),
|
|
36
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", flexDirection: "column", justifyContent: "center", gap: 40, minHeight: 0 }, children })
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
var Box = ({ t, q, color: color2, children, mono, size = 48, dim, dash }) => {
|
|
42
|
+
const { box: BOX, text: INK, font: FONT, mono: MONO } = useTheme();
|
|
43
|
+
return /* @__PURE__ */ jsx("div", { style: { background: BOX, border: `4px ${dash ? "dashed" : "solid"} ${color2}`, borderRadius: 22, padding: "18px 28px", fontFamily: mono ? MONO : FONT, fontSize: size, textAlign: "center", whiteSpace: "nowrap", color: INK, opacity: appear(t, q) * (dim ? 0.55 : 1), translate: `0px ${(1 - appear(t, q)) * 30}px` }, children });
|
|
44
|
+
};
|
|
45
|
+
var Line = ({ t, q, color: color2, size = 46, children }) => {
|
|
46
|
+
const { box: BOX, text: INK, font: FONT, accent: LAMP } = useTheme();
|
|
47
|
+
return /* @__PURE__ */ jsx("div", { style: { background: BOX, border: `4px solid ${color2 ?? LAMP}`, borderRadius: 24, padding: "22px 30px", fontSize: size, opacity: appear(t, q), translate: `${(1 - appear(t, q)) * 70}px 0px`, whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: 22, fontFamily: FONT, color: INK }, children });
|
|
48
|
+
};
|
|
49
|
+
var Big = ({ t, q, color: color2, size, mono, children }) => {
|
|
50
|
+
const { text: INK, font: FONT, mono: MONO } = useTheme();
|
|
51
|
+
return /* @__PURE__ */ jsx("div", { style: { fontFamily: mono ? MONO : FONT, fontSize: size, fontWeight: 900, color: color2 ?? INK, textAlign: "center", lineHeight: 1.05, opacity: appear(t, q, 0.25), scale: String(pop(t, q)), whiteSpace: "nowrap" }, children });
|
|
52
|
+
};
|
|
53
|
+
var Bubble = ({ t, q, color: color2, children, author = "\u2014 someone, in the chat" }) => {
|
|
54
|
+
const { box: BOX, text: INK, font: FONT, mono: MONO, muted: MUTED } = useTheme();
|
|
55
|
+
return /* @__PURE__ */ jsxs("div", { style: { position: "relative", background: BOX, border: `4px solid ${color2}`, borderRadius: 28, padding: "28px 34px", fontFamily: FONT, fontSize: 46, lineHeight: 1.25, color: INK, textAlign: "center", opacity: appear(t, q), translate: `0px ${(1 - appear(t, q)) * 30}px` }, children: [
|
|
56
|
+
/* @__PURE__ */ jsx("span", { style: { position: "absolute", left: 24, top: -34, fontSize: 90, color: color2, lineHeight: 1 }, children: "\u201C" }),
|
|
57
|
+
children,
|
|
58
|
+
/* @__PURE__ */ jsx("div", { style: { fontFamily: MONO, fontSize: 26, color: MUTED, marginTop: 16 }, children: author })
|
|
59
|
+
] });
|
|
60
|
+
};
|
|
61
|
+
var Code = ({ t, q, title, color: color2, lines }) => {
|
|
62
|
+
const { box: BOX, text: INK, mono: MONO } = useTheme();
|
|
63
|
+
return /* @__PURE__ */ jsxs("div", { style: { background: BOX, borderRadius: 24, overflow: "hidden", border: `4px solid ${color2}`, opacity: appear(t, q), translate: `0px ${(1 - appear(t, q)) * 30}px` }, children: [
|
|
64
|
+
/* @__PURE__ */ jsx("div", { style: { background: "#1c1830", padding: "12px 26px", fontFamily: MONO, fontSize: 26, color: "#8b86a8", whiteSpace: "nowrap" }, children: title }),
|
|
65
|
+
/* @__PURE__ */ jsx("div", { style: { fontFamily: MONO, fontSize: 30, lineHeight: 1.6, padding: "20px 0" }, children: lines.map((l, i) => /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre", padding: "0 26px", color: l.color || INK, background: l.mark ? `${l.color}26` : "transparent", borderLeft: `6px solid ${l.mark ? l.color : "transparent"}` }, children: l.s }, i)) })
|
|
66
|
+
] });
|
|
67
|
+
};
|
|
68
|
+
var Window = ({ t, q, src, clipDuration, from, left, top, w, reg = [0, 0, 1920, 1080], tag = "REAL FOOTAGE", dur }) => {
|
|
69
|
+
const { accent: LAMP, text: INK, error: BAD, mono: MONO } = useTheme();
|
|
70
|
+
const s = w / reg[2], h = w * reg[3] / reg[2], o = appear(t, q, 0.5);
|
|
71
|
+
const push = 1 + 0.07 * ramp(t, q, q + dur);
|
|
72
|
+
const total = clipDuration - from;
|
|
73
|
+
return /* @__PURE__ */ jsxs("div", { style: { position: "absolute", left, top, width: w, height: h, borderRadius: 26, overflow: "hidden", border: `4px solid ${LAMP}`, boxShadow: `0 0 70px ${rgba(LAMP, 0.25)}, 0 24px 60px rgba(0,0,0,.6)`, opacity: o, translate: `0px ${(1 - o) * 40}px`, background: "#0b0b10" }, children: [
|
|
74
|
+
/* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, scale: String(push), transformOrigin: "50% 60%" }, children: /* @__PURE__ */ jsx(Loop, { durationInFrames: Math.round(total * FPS), children: /* @__PURE__ */ jsx(OffthreadVideo, { src: staticFile(src), startFrom: Math.round(from * FPS), muted: true, style: { position: "absolute", left: -reg[0] * s, top: -reg[1] * s, width: 1920 * s, height: 1080 * s, imageRendering: "pixelated" } }) }) }),
|
|
75
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "absolute", left: 14, bottom: 12, background: "rgba(10,8,6,.78)", color: INK, fontFamily: MONO, fontSize: 22, padding: "4px 14px", borderRadius: 10, display: "flex", gap: 10, alignItems: "center" }, children: [
|
|
76
|
+
/* @__PURE__ */ jsx("span", { style: { width: 12, height: 12, borderRadius: 6, background: BAD, opacity: 0.5 + 0.5 * Math.abs(Math.sin(t * 4)) } }),
|
|
77
|
+
tag
|
|
78
|
+
] })
|
|
79
|
+
] });
|
|
80
|
+
};
|
|
81
|
+
var Phone = ({ t, q, src, clipDuration, from, left, top, w, dur }) => {
|
|
82
|
+
const { accent: LAMP } = useTheme();
|
|
83
|
+
const h = w * 1920 / 1080, o = appear(t, q, 0.5);
|
|
84
|
+
return /* @__PURE__ */ jsx("div", { style: { position: "absolute", left, top, width: w, height: h, borderRadius: 46, border: "10px solid #0b0b10", outline: `3px solid ${LAMP}`, overflow: "hidden", boxShadow: `0 0 70px ${rgba(LAMP, 0.22)}, 0 24px 60px rgba(0,0,0,.6)`, opacity: o, translate: `0px ${(1 - o) * 50}px`, background: "#0b0b10", scale: String(0.96 + 0.04 * ramp(t, q, q + dur)) }, children: /* @__PURE__ */ jsx(Loop, { durationInFrames: Math.round((clipDuration - from) * FPS), children: /* @__PURE__ */ jsx(OffthreadVideo, { src: staticFile(src), startFrom: Math.round(from * FPS), muted: true, style: { width: "100%", height: "100%", objectFit: "cover" } }) }) });
|
|
85
|
+
};
|
|
86
|
+
var Mascot = ({ name, size = 220, path }) => {
|
|
87
|
+
const { mascotPath } = useTheme();
|
|
88
|
+
return /* @__PURE__ */ jsx("div", { style: { background: "#1c1830", border: "6px solid #3a3358", borderRadius: 30, padding: 12 }, children: /* @__PURE__ */ jsx(Img, { src: staticFile(path ?? (mascotPath ?? "mascots/{name}.png").replace("{name}", name)), style: { width: size, height: size, imageRendering: "pixelated" } }) });
|
|
89
|
+
};
|
|
90
|
+
var Frame = ({ glow, title, color: color2, children, size = 130 }) => {
|
|
91
|
+
const { coverBackground: BG, text: INK, accent: LAMP, font: FONT, handle } = useTheme();
|
|
92
|
+
return /* @__PURE__ */ jsxs(AbsoluteFill, { style: { background: `radial-gradient(ellipse at 50% 44%, ${glow} 0%, rgba(0,0,0,0) 56%), ${BG}`, alignItems: "center", fontFamily: FONT, textAlign: "center" }, children: [
|
|
93
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: 350, fontSize: size, fontWeight: 900, color: INK, lineHeight: 1.02, textShadow: "0 8px 0 #000", whiteSpace: "nowrap" }, children: [
|
|
94
|
+
title[0],
|
|
95
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
96
|
+
/* @__PURE__ */ jsx("span", { style: { color: color2 }, children: title[1] }),
|
|
97
|
+
title[2] ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
98
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
99
|
+
title[2]
|
|
100
|
+
] }) : null
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 130, display: "flex", flexDirection: "column", alignItems: "center", width: 1e3, transform: "scale(1.16)", transformOrigin: "top center" }, children }),
|
|
103
|
+
/* @__PURE__ */ jsx("div", { style: { position: "absolute", bottom: 400, fontSize: 52, color: LAMP, letterSpacing: 2 }, children: handle })
|
|
104
|
+
] });
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// src/declarative.tsx
|
|
108
|
+
import { Composition, Still, useCurrentFrame as useCurrentFrame2 } from "remotion";
|
|
109
|
+
import { Episode, FPS as FPS2, ThemeProvider, makeAt, pop as pop2, ramp as ramp2, appear as appear2, useTheme as useTheme2 } from "@praeko/studio-core";
|
|
110
|
+
|
|
111
|
+
// src/color.ts
|
|
112
|
+
var resolveColor = (theme, name) => {
|
|
113
|
+
if (!name) return void 0;
|
|
114
|
+
if (name.startsWith("#")) return name;
|
|
115
|
+
const value = theme[name];
|
|
116
|
+
return value ?? name;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/declarative.tsx
|
|
120
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
121
|
+
var style = (it, base = {}) => {
|
|
122
|
+
const p = it.pos;
|
|
123
|
+
return p ? { position: "absolute", left: p.left, right: p.right, top: p.top, bottom: p.bottom, width: p.w, height: p.h, ...base } : base;
|
|
124
|
+
};
|
|
125
|
+
var alignMap = { center: "center", start: "flex-start", end: "flex-end", stretch: "stretch" };
|
|
126
|
+
var TextItem = ({ it, t }) => {
|
|
127
|
+
const T = useTheme2();
|
|
128
|
+
const q = it.at ?? 0.3, lines = String(it.text).split("\n");
|
|
129
|
+
const base = {
|
|
130
|
+
...it.font ? { fontFamily: it.font === "mono" ? T.mono : T.font } : {},
|
|
131
|
+
fontSize: it.size ?? 40,
|
|
132
|
+
color: resolveColor(T, it.color) ?? T.text,
|
|
133
|
+
...it.align === "left" ? { textAlign: "left" } : { textAlign: "center" },
|
|
134
|
+
...it.noFade ? {} : { opacity: appear2(t, q) },
|
|
135
|
+
...it.lineHeight ? { lineHeight: it.lineHeight } : {},
|
|
136
|
+
...it.weight ? { fontWeight: it.weight } : {},
|
|
137
|
+
...it.pop ? { scale: String(pop2(t, q)) } : {},
|
|
138
|
+
...it.noWrap ? { whiteSpace: "nowrap" } : {}
|
|
139
|
+
};
|
|
140
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, base), children: lines.map((l, i) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
141
|
+
l,
|
|
142
|
+
i < lines.length - 1 ? /* @__PURE__ */ jsx2("br", {}) : null
|
|
143
|
+
] }, i)) });
|
|
144
|
+
};
|
|
145
|
+
var BarItem = ({ it, t }) => {
|
|
146
|
+
const T = useTheme2();
|
|
147
|
+
const q = it.at ?? 0.3, pr = it.fill ?? {}, from = pr.from ?? 0, to = pr.to ?? 1;
|
|
148
|
+
const p = pr.duration ? from + (to - from) * ramp2(t, pr.start ?? q, (pr.start ?? q) + pr.duration) : to;
|
|
149
|
+
const g = (it.gradient ?? [T.error, T.error]).map((c) => {
|
|
150
|
+
const [n, ...r] = String(c).split(" ");
|
|
151
|
+
return [resolveColor(T, n), ...r].join(" ");
|
|
152
|
+
});
|
|
153
|
+
const end = it.glowAtEnd && p > 0.98;
|
|
154
|
+
const inner = { width: `${p * 100}%`, height: "100%", background: it.gradientAngle === false ? g[0] : `linear-gradient(90deg, ${g.join(", ")})` };
|
|
155
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, { height: it.height ?? 40, borderRadius: it.radius ?? 14, background: it.background ?? "#1d1812", ...it.border === false ? {} : { border: `${it.border ?? 3}px solid #3a2f22` }, overflow: "hidden", ...it.noFade ? {} : { opacity: appear2(t, q) }, ...end ? { boxShadow: `0 0 40px ${resolveColor(T, it.glowAtEnd)}` } : {} }), children: /* @__PURE__ */ jsx2("div", { style: inner }) });
|
|
156
|
+
};
|
|
157
|
+
var BarsItem = ({ it, t }) => {
|
|
158
|
+
const T = useTheme2();
|
|
159
|
+
const q0 = it.at ?? 0.3;
|
|
160
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, { display: "flex", flexDirection: "column", gap: it.gap ?? 16 }), children: it.lines.map((l, i) => {
|
|
161
|
+
const q = q0 + i * (it.step ?? 0.5);
|
|
162
|
+
return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 18, opacity: appear2(t, q) }, children: [
|
|
163
|
+
/* @__PURE__ */ jsx2("div", { style: { width: it.labelWidth ?? 190, fontFamily: T.mono, fontSize: 30, color: T.muted, whiteSpace: "nowrap" }, children: l.label }),
|
|
164
|
+
/* @__PURE__ */ jsx2("div", { style: { height: 34, width: (it.maxWidth ?? 640) * (l.value / it.max) * ramp2(t, q, q + 0.9), borderRadius: 10, background: `linear-gradient(90deg, #b9453a, ${resolveColor(T, it.color) ?? T.error})` } }),
|
|
165
|
+
/* @__PURE__ */ jsx2("div", { style: { fontFamily: T.mono, fontSize: 30, color: resolveColor(T, it.color) ?? T.error, fontWeight: 900 }, children: l.text })
|
|
166
|
+
] }, i);
|
|
167
|
+
}) });
|
|
168
|
+
};
|
|
169
|
+
var Render = ({ it, t, dur }) => {
|
|
170
|
+
const T = useTheme2();
|
|
171
|
+
const q = it.at ?? 0.3;
|
|
172
|
+
const children = it.items?.map((f, i) => /* @__PURE__ */ jsx2(Render, { it: f, t, dur }, i));
|
|
173
|
+
switch (it.type) {
|
|
174
|
+
case "text":
|
|
175
|
+
return /* @__PURE__ */ jsx2(TextItem, { it, t });
|
|
176
|
+
case "big":
|
|
177
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, it.pos ? { display: "flex", justifyContent: "center" } : {}), children: /* @__PURE__ */ jsx2(Big, { t, q, color: resolveColor(T, it.color), size: it.size, mono: it.mono, children: it.text }) });
|
|
178
|
+
case "box":
|
|
179
|
+
return /* @__PURE__ */ jsx2(Box, { t, q, color: resolveColor(T, it.color) ?? T.accent, size: it.size, mono: it.mono, dim: it.dim, dash: it.dashed, children: it.text });
|
|
180
|
+
case "line":
|
|
181
|
+
return /* @__PURE__ */ jsx2(Line, { t, q, color: resolveColor(T, it.color), size: it.size, children: it.parts.map((p, i) => /* @__PURE__ */ jsx2("span", { style: { ...p.mono ? { fontFamily: T.mono } : {}, ...p.color ? { color: resolveColor(T, p.color) } : {}, ...p.weight ? { fontWeight: p.weight } : {} }, children: p.text }, i)) });
|
|
182
|
+
case "bubble":
|
|
183
|
+
return /* @__PURE__ */ jsx2(Bubble, { t, q, color: resolveColor(T, it.color) ?? T.info, author: it.author, children: it.text });
|
|
184
|
+
case "code":
|
|
185
|
+
return /* @__PURE__ */ jsx2(Code, { t, q, title: it.title, color: resolveColor(T, it.color) ?? T.error, lines: it.lines.map((l) => ({ ...l, color: resolveColor(T, l.color) })) });
|
|
186
|
+
case "window":
|
|
187
|
+
return /* @__PURE__ */ jsx2(Window, { t, q, src: it.src, clipDuration: it.clipDuration, from: it.from ?? 0, left: it.pos.left, top: it.pos.top, w: it.pos.w, reg: it.reg, tag: it.tag, dur });
|
|
188
|
+
case "phone":
|
|
189
|
+
return /* @__PURE__ */ jsx2(Phone, { t, q, src: it.src, clipDuration: it.clipDuration, from: it.from ?? 0, left: it.pos.left, top: it.pos.top, w: it.pos.w, dur });
|
|
190
|
+
case "bar":
|
|
191
|
+
return /* @__PURE__ */ jsx2(BarItem, { it, t });
|
|
192
|
+
case "bars":
|
|
193
|
+
return /* @__PURE__ */ jsx2(BarsItem, { it, t });
|
|
194
|
+
case "stack":
|
|
195
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, { display: "flex", flexDirection: "column", gap: it.gap ?? 20, alignItems: alignMap[it.align ?? "stretch"] }), children });
|
|
196
|
+
case "row":
|
|
197
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, it.spread ? { display: "flex", justifyContent: "space-between" } : it.center === false ? { display: "flex", gap: it.gap ?? 20 } : { display: "flex", justifyContent: "center", alignItems: "center", gap: it.gap ?? 20, whiteSpace: "nowrap" }), children });
|
|
198
|
+
case "card": {
|
|
199
|
+
const o = appear2(t, q);
|
|
200
|
+
return /* @__PURE__ */ jsx2("div", { style: style(it, { background: T.box, border: `4px solid ${resolveColor(T, it.color) ?? T.accent}`, borderRadius: 24, padding: "20px 24px", fontFamily: T.mono, fontSize: it.size ?? 40, color: T.text, opacity: o, translate: `${(1 - o) * 60}px 0px` }), children: it.lines.map((l, i) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
201
|
+
l.map((p, k) => /* @__PURE__ */ jsx2("span", { style: { ...p.color ? { color: resolveColor(T, p.color) } : {}, ...p.weight ? { fontWeight: p.weight } : {} }, children: p.text }, k)),
|
|
202
|
+
i < it.lines.length - 1 ? /* @__PURE__ */ jsx2("br", {}) : null
|
|
203
|
+
] }, i)) });
|
|
204
|
+
}
|
|
205
|
+
default:
|
|
206
|
+
return /* @__PURE__ */ jsxs2("div", { style: { color: "#f00", fontSize: 30 }, children: [
|
|
207
|
+
"unknown type: ",
|
|
208
|
+
it.type
|
|
209
|
+
] });
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var SceneDecl = ({ spec, panelDur, dur }) => {
|
|
213
|
+
const t = useCurrentFrame2() / FPS2;
|
|
214
|
+
const items = (spec.items ?? []).map((it, i) => /* @__PURE__ */ jsx2(Render, { it, t, dur }, i));
|
|
215
|
+
if ((spec.layout ?? "panel") === "free") return /* @__PURE__ */ jsx2(Fragment2, { children: items });
|
|
216
|
+
return /* @__PURE__ */ jsx2(Panel, { title: spec.title ?? "", dur: panelDur, children: items });
|
|
217
|
+
};
|
|
218
|
+
var resolve = (it, at) => {
|
|
219
|
+
const o = { ...it };
|
|
220
|
+
if (it.when !== void 0) {
|
|
221
|
+
const [f, n] = Array.isArray(it.when) ? it.when : [it.when, 0];
|
|
222
|
+
o.at = at(f, n) + (it.offset ?? 0);
|
|
223
|
+
delete o.when;
|
|
224
|
+
}
|
|
225
|
+
if (it.items) o.items = it.items.map((f) => resolve(f, at));
|
|
226
|
+
return o;
|
|
227
|
+
};
|
|
228
|
+
function episodeDataFromSpec(spec, words, components = {}) {
|
|
229
|
+
const S = spec.scenes.map((c) => c.start);
|
|
230
|
+
const endOf = (i) => spec.scenes[i].end ?? (i + 1 < S.length ? S[i + 1] : spec.outro.a);
|
|
231
|
+
const A = makeAt(words, S);
|
|
232
|
+
return {
|
|
233
|
+
id: spec.id,
|
|
234
|
+
durS: spec.durS,
|
|
235
|
+
captionPx: spec.captionPx,
|
|
236
|
+
voice: spec.voice,
|
|
237
|
+
words,
|
|
238
|
+
backgroundLines: spec.backgroundLines,
|
|
239
|
+
hits: spec.hits,
|
|
240
|
+
outro: spec.outro,
|
|
241
|
+
scenes: spec.scenes.map((c, i) => {
|
|
242
|
+
const panelDur = endOf(i) - c.start;
|
|
243
|
+
const resolved = { ...c, items: c.items?.map((it) => resolve(it, (f, n) => A(i)(f, n))) };
|
|
244
|
+
return {
|
|
245
|
+
a: c.start,
|
|
246
|
+
b: i + 1 < S.length ? S[i + 1] - 0.1 : endOf(i) - 0.1,
|
|
247
|
+
label: c.label,
|
|
248
|
+
sounds: c.sounds,
|
|
249
|
+
C: c.component ? components[c.component] ?? (() => /* @__PURE__ */ jsxs2("div", { style: { color: "#f00", fontSize: 40 }, children: [
|
|
250
|
+
'component "',
|
|
251
|
+
c.component,
|
|
252
|
+
'" is not registered'
|
|
253
|
+
] })) : ({ dur }) => /* @__PURE__ */ jsx2(SceneDecl, { spec: resolved, panelDur, dur })
|
|
254
|
+
};
|
|
255
|
+
})
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
var CoverDecl = ({ spec }) => {
|
|
259
|
+
const T = useTheme2();
|
|
260
|
+
return /* @__PURE__ */ jsx2(Frame, { glow: spec.glow ?? "rgba(244,185,66,.16)", title: spec.title ?? ["", "", ""], color: resolveColor(T, spec.color) ?? T.accent, size: spec.size, children: (spec.items ?? []).map((it, i) => /* @__PURE__ */ jsx2(Render, { it, t: 99, dur: 99 }, i)) });
|
|
261
|
+
};
|
|
262
|
+
var EpisodeDecl = ({ spec, words, theme, components = {} }) => {
|
|
263
|
+
const d = episodeDataFromSpec(spec, words, components);
|
|
264
|
+
const CoverC = spec.cover?.component ? components[spec.cover.component] : void 0;
|
|
265
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
266
|
+
/* @__PURE__ */ jsx2(Composition, { id: spec.id, component: () => /* @__PURE__ */ jsx2(ThemeProvider, { theme, children: /* @__PURE__ */ jsx2(Episode, { d }) }), durationInFrames: Math.round(spec.durS * FPS2), fps: FPS2, width: 1080, height: 1920 }),
|
|
267
|
+
spec.cover ? /* @__PURE__ */ jsx2(Still, { id: spec.cover.id, component: () => /* @__PURE__ */ jsx2(ThemeProvider, { theme, children: CoverC ? /* @__PURE__ */ jsx2(CoverC, {}) : /* @__PURE__ */ jsx2(CoverDecl, { spec: spec.cover }) }), width: 1080, height: 1920 }) : null
|
|
268
|
+
] });
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/long-parts.tsx
|
|
272
|
+
import { useTheme as useTheme3 } from "@praeko/studio-core";
|
|
273
|
+
import { interpolate as interpolate2 } from "remotion";
|
|
274
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
275
|
+
var clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" };
|
|
276
|
+
var appearL = (t, q, d = 0.3) => interpolate2(t, [q, q + d], [0, 1], clamp);
|
|
277
|
+
var KW = /^(UPDATE|SET|WHERE|AND|INSERT|INTO|SELECT|FROM|CASE|WHEN|THEN|END|AS|VALUES|IS|NOT|NULL|const|await|if)$/;
|
|
278
|
+
var FN = /^(changes|abs|json_set|json_remove|json_extract)$/;
|
|
279
|
+
var colorize = (s, c) => s.split(/(--.*$|\/\/.*$|'[^']*'|\b\w+\b)/).map((p, i) => {
|
|
280
|
+
if (!p) return null;
|
|
281
|
+
let color2 = c.text;
|
|
282
|
+
if (/^(--|\/\/)/.test(p)) color2 = c.muted;
|
|
283
|
+
else if (/^'/.test(p)) color2 = c.ok;
|
|
284
|
+
else if (KW.test(p)) color2 = c.accent;
|
|
285
|
+
else if (/^\d+$/.test(p)) color2 = "#7fb4ff";
|
|
286
|
+
else if (FN.test(p)) color2 = c.info;
|
|
287
|
+
return /* @__PURE__ */ jsx3("span", { style: { color: color2 }, children: p }, i);
|
|
288
|
+
});
|
|
289
|
+
var CodeL = ({ title, lines, t, q, w, size = 32, left, top }) => {
|
|
290
|
+
const T = useTheme3();
|
|
291
|
+
const o = appearL(t, q);
|
|
292
|
+
return /* @__PURE__ */ jsxs3("div", { style: { position: "absolute", left, top, width: w, background: T.code, border: `3px solid #3a2f22`, borderRadius: 22, overflow: "hidden", opacity: o, transform: `translateY(${(1 - o) * 24}px)` }, children: [
|
|
293
|
+
/* @__PURE__ */ jsx3("div", { style: { background: "#262034", padding: "10px 26px", fontFamily: T.mono, fontSize: 26, color: "#9a95b8", whiteSpace: "nowrap" }, children: title }),
|
|
294
|
+
/* @__PURE__ */ jsx3("div", { style: { padding: "18px 0", fontFamily: T.mono, fontSize: size, lineHeight: 1.5 }, children: lines.map((l, i) => {
|
|
295
|
+
const on = l.hl ? t >= l.hl[0] && t < l.hl[1] : false;
|
|
296
|
+
return /* @__PURE__ */ jsx3("div", { style: { whiteSpace: "pre", padding: "0 26px", borderLeft: `6px solid ${on ? l.red ? T.error : T.accent : "transparent"}`, background: on ? l.red ? "rgba(255,107,94,.16)" : "rgba(244,185,66,.16)" : "transparent" }, children: colorize(l.s, T) || " " }, i);
|
|
297
|
+
}) })
|
|
298
|
+
] });
|
|
299
|
+
};
|
|
300
|
+
var CardL = ({ t, q, left, top, w, h, color: color2, children }) => {
|
|
301
|
+
const T = useTheme3();
|
|
302
|
+
const o = appearL(t, q);
|
|
303
|
+
return /* @__PURE__ */ jsx3("div", { style: { position: "absolute", left, top, width: w, height: h, boxSizing: "border-box", background: T.longBox ?? T.box, border: `4px solid ${color2 ?? T.accent}`, borderRadius: 24, padding: "20px 28px", fontFamily: T.font, color: T.text, opacity: o, transform: `translateY(${(1 - o) * 26}px)`, display: "flex", flexDirection: "column", justifyContent: "center", gap: 8 }, children });
|
|
304
|
+
};
|
|
305
|
+
var ChipL = ({ t, q, left, top, color: color2, size = 34, children }) => {
|
|
306
|
+
const T = useTheme3();
|
|
307
|
+
const o = appearL(t, q);
|
|
308
|
+
return /* @__PURE__ */ jsx3("div", { style: { position: "absolute", left, top, opacity: o, transform: `scale(${1.15 - 0.15 * o})`, transformOrigin: "left center", fontFamily: T.font, fontSize: size, color: T.background, background: color2 ?? T.accent, borderRadius: 16, padding: "10px 26px", whiteSpace: "nowrap" }, children });
|
|
309
|
+
};
|
|
310
|
+
var TextL = ({ t, q, left, top, w, size = 44, color: color2, align = "left", mono, children }) => {
|
|
311
|
+
const T = useTheme3();
|
|
312
|
+
const o = appearL(t, q);
|
|
313
|
+
return /* @__PURE__ */ jsx3("div", { style: { position: "absolute", left, top, width: w, opacity: o, transform: `translateY(${(1 - o) * 18}px)`, fontFamily: mono ? T.mono : T.font, fontSize: size, color: color2 ?? T.text, textAlign: align, lineHeight: 1.2 }, children });
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// src/long-decl.tsx
|
|
317
|
+
import { Composition as Composition2 } from "remotion";
|
|
318
|
+
import { FPS as FPS3, Long, ThemeProvider as ThemeProvider3, makeAtGlobal, useTheme as useTheme5 } from "@praeko/studio-core";
|
|
319
|
+
import { interpolate as interpolate3, Easing } from "remotion";
|
|
320
|
+
|
|
321
|
+
// src/long-cover.tsx
|
|
322
|
+
import { Still as Still2 } from "remotion";
|
|
323
|
+
import { ThemeProvider as ThemeProvider2, rgba as rgba2, useTheme as useTheme4 } from "@praeko/studio-core";
|
|
324
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
325
|
+
var color = resolveColor;
|
|
326
|
+
var CodeCard = ({ spec, w, scale = 1 }) => {
|
|
327
|
+
const T = useTheme4();
|
|
328
|
+
if (!spec.code) return null;
|
|
329
|
+
const px = (n) => n * scale;
|
|
330
|
+
return /* @__PURE__ */ jsxs4("div", { style: { width: w, background: T.code, border: `${px(5)}px solid ${T.accent}`, borderRadius: px(24), overflow: "hidden", textAlign: "left" }, children: [
|
|
331
|
+
/* @__PURE__ */ jsx4("div", { style: { background: "#262034", padding: `${px(8)}px ${px(22)}px`, fontFamily: T.mono, fontSize: px(24), color: "#9a95b8" }, children: spec.code.title }),
|
|
332
|
+
/* @__PURE__ */ jsx4("div", { style: { padding: `${px(18)}px ${px(24)}px`, fontFamily: T.mono, fontSize: px(30), lineHeight: 1.5, whiteSpace: "pre" }, children: spec.code.lines.map((l, i) => l.hl ? /* @__PURE__ */ jsx4("div", { style: { background: rgba2(T.accent, 0.2), borderLeft: `${px(6)}px solid ${T.accent}`, marginLeft: -px(24), paddingLeft: px(18) }, children: colorize(l.s, T) }, i) : /* @__PURE__ */ jsx4("div", { children: colorize(l.s, T) }, i)) })
|
|
333
|
+
] });
|
|
334
|
+
};
|
|
335
|
+
var Chips = ({ spec, scale = 1, column }) => {
|
|
336
|
+
const T = useTheme4();
|
|
337
|
+
return /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: column ? "column" : "row", alignItems: column ? "flex-start" : void 0, gap: 24 * scale }, children: (spec.chips ?? []).map((c, i) => /* @__PURE__ */ jsx4("div", { style: { background: color(T, c.color) ?? T.accent, color: T.background, fontSize: 48 * scale, padding: `${10 * scale}px ${34 * scale}px`, borderRadius: 18 * scale }, children: c.text }, i)) });
|
|
338
|
+
};
|
|
339
|
+
var LongCover16x9 = ({ spec }) => {
|
|
340
|
+
const T = useTheme4();
|
|
341
|
+
return /* @__PURE__ */ jsxs4("div", { style: { width: 1280, height: 720, background: `radial-gradient(900px 500px at 75% 30%, ${rgba2(T.accent, 0.16)}, ${T.background} 70%)`, position: "relative", fontFamily: T.font, color: T.text, overflow: "hidden" }, children: [
|
|
342
|
+
/* @__PURE__ */ jsxs4("div", { style: { position: "absolute", left: 70, top: 70, fontSize: 92, lineHeight: 1.05, color: T.text }, children: [
|
|
343
|
+
spec.title[0],
|
|
344
|
+
/* @__PURE__ */ jsx4("br", {}),
|
|
345
|
+
/* @__PURE__ */ jsx4("span", { style: { color: color(T, spec.color) ?? T.accent }, children: spec.title[1] })
|
|
346
|
+
] }),
|
|
347
|
+
/* @__PURE__ */ jsx4("div", { style: { position: "absolute", left: 70, top: 310 }, children: /* @__PURE__ */ jsx4(CodeCard, { spec, w: 700 }) }),
|
|
348
|
+
/* @__PURE__ */ jsx4("div", { style: { position: "absolute", left: 70, top: 560 }, children: /* @__PURE__ */ jsx4(Chips, { spec, scale: 0.9 }) }),
|
|
349
|
+
spec.mascot ? /* @__PURE__ */ jsx4("div", { style: { position: "absolute", right: 70, top: 150 }, children: /* @__PURE__ */ jsx4(Mascot, { name: spec.mascot, size: 380 }) }) : null,
|
|
350
|
+
/* @__PURE__ */ jsx4("div", { style: { position: "absolute", right: 70, bottom: 50, fontSize: 34, color: T.accent, letterSpacing: 2 }, children: T.handle })
|
|
351
|
+
] });
|
|
352
|
+
};
|
|
353
|
+
var LongCoverVertical = ({ spec }) => {
|
|
354
|
+
const T = useTheme4();
|
|
355
|
+
return /* @__PURE__ */ jsx4(Frame, { glow: spec.glow ?? rgba2(T.accent, 0.16), title: [spec.title[0], spec.title[1]], color: color(T, spec.color) ?? T.accent, size: spec.title.some((l) => l.length > 11) ? 104 : 130, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 34 }, children: [
|
|
356
|
+
/* @__PURE__ */ jsx4(CodeCard, { spec, w: 780, scale: 1 }),
|
|
357
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: 30 }, children: [
|
|
358
|
+
spec.mascot ? /* @__PURE__ */ jsx4(Mascot, { name: spec.mascot, size: 210 }) : null,
|
|
359
|
+
/* @__PURE__ */ jsx4(Chips, { spec, scale: 0.7, column: true })
|
|
360
|
+
] })
|
|
361
|
+
] }) });
|
|
362
|
+
};
|
|
363
|
+
var LongCovers = ({ spec, theme }) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
364
|
+
/* @__PURE__ */ jsx4(Still2, { id: spec.id, component: () => /* @__PURE__ */ jsx4(ThemeProvider2, { theme, children: /* @__PURE__ */ jsx4(LongCover16x9, { spec }) }), width: 1280, height: 720 }),
|
|
365
|
+
/* @__PURE__ */ jsx4(Still2, { id: `${spec.id}Vertical`, component: () => /* @__PURE__ */ jsx4(ThemeProvider2, { theme, children: /* @__PURE__ */ jsx4(LongCoverVertical, { spec }) }), width: 1080, height: 1920 })
|
|
366
|
+
] });
|
|
367
|
+
|
|
368
|
+
// src/long-decl.tsx
|
|
369
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
370
|
+
var TextParts = ({ it }) => {
|
|
371
|
+
const T = useTheme5();
|
|
372
|
+
if (!it.parts) return /* @__PURE__ */ jsx5(Fragment4, { children: it.text });
|
|
373
|
+
return /* @__PURE__ */ jsx5(Fragment4, { children: it.parts.map((p, i) => /* @__PURE__ */ jsx5("span", { style: { ...p.color ? { color: resolveColor(T, p.color) } : {}, ...p.mono ? { fontFamily: T.mono } : {} }, children: p.text }, i)) });
|
|
374
|
+
};
|
|
375
|
+
var clampL = { extrapolateLeft: "clamp", extrapolateRight: "clamp" };
|
|
376
|
+
var BarL = ({ it, t, q, T }) => {
|
|
377
|
+
const p = it.pos ?? {}, dur = it.duration ?? 1.4, v = it.value ?? 1, h = it.height ?? 46;
|
|
378
|
+
const o = appearL(t, q), f = interpolate3(t, [q, q + dur], [0, v], { ...clampL, easing: Easing.out(Easing.cubic) });
|
|
379
|
+
const c = resolveColor(T, it.color) ?? T.accent;
|
|
380
|
+
return /* @__PURE__ */ jsxs5("div", { style: { position: "absolute", left: p.left, top: p.top, width: p.w, opacity: o, fontFamily: T.font }, children: [
|
|
381
|
+
it.label ? /* @__PURE__ */ jsx5("div", { style: { fontSize: it.size ?? 30, color: T.muted, marginBottom: 8 }, children: it.label }) : null,
|
|
382
|
+
/* @__PURE__ */ jsxs5("div", { style: { position: "relative", height: h, borderRadius: h / 2, background: "rgba(255,255,255,.07)", border: `3px solid ${c}`, overflow: "hidden" }, children: [
|
|
383
|
+
/* @__PURE__ */ jsx5("div", { style: { position: "absolute", left: 0, top: 0, bottom: 0, width: `${f * 100}%`, background: `linear-gradient(90deg, ${c}88, ${c})` } }),
|
|
384
|
+
it.text ? /* @__PURE__ */ jsx5("div", { style: { position: "absolute", right: 22, top: 0, bottom: 0, display: "flex", alignItems: "center", fontSize: Math.round(h * 0.55), color: T.text, fontWeight: 900, textShadow: "0 2px 8px rgba(0,0,0,.8)" }, children: it.text }) : null
|
|
385
|
+
] })
|
|
386
|
+
] });
|
|
387
|
+
};
|
|
388
|
+
var CounterL = ({ it, t, q, T }) => {
|
|
389
|
+
const p = it.pos ?? {}, dur = it.duration ?? 1.6, a = it.from ?? 0, b = it.to ?? 0, decimals = it.decimals ?? 0;
|
|
390
|
+
const o = appearL(t, q), n = interpolate3(t, [q, q + dur], [a, b], { ...clampL, easing: Easing.out(Easing.cubic) });
|
|
391
|
+
const s = n.toLocaleString("pt-BR", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
|
|
392
|
+
return /* @__PURE__ */ jsxs5("div", { style: { position: "absolute", left: p.left, top: p.top, width: p.w, opacity: o, fontFamily: it.mono ? T.mono : T.font, fontSize: it.size ?? 96, fontWeight: 900, color: resolveColor(T, it.color) ?? T.accent, lineHeight: 1.1, textAlign: it.align === "center" ? "center" : "left" }, children: [
|
|
393
|
+
it.prefix ?? "",
|
|
394
|
+
s,
|
|
395
|
+
it.suffix ?? ""
|
|
396
|
+
] });
|
|
397
|
+
};
|
|
398
|
+
function longDataFromSpec(spec, words, T, components = {}) {
|
|
399
|
+
const at = makeAtGlobal(words);
|
|
400
|
+
const q = (x) => typeof x === "number" ? x : typeof x === "string" ? at(x) : at(x.when, x.nth ?? 0, x.after ?? 0) + (x.plus ?? 0);
|
|
401
|
+
const Item = ({ it, t }) => {
|
|
402
|
+
const p = it.pos ?? {};
|
|
403
|
+
switch (it.type) {
|
|
404
|
+
case "code":
|
|
405
|
+
return /* @__PURE__ */ jsx5(
|
|
406
|
+
CodeL,
|
|
407
|
+
{
|
|
408
|
+
title: it.title,
|
|
409
|
+
t,
|
|
410
|
+
q: q(it.when),
|
|
411
|
+
w: p.w,
|
|
412
|
+
left: p.left,
|
|
413
|
+
top: p.top,
|
|
414
|
+
size: it.size,
|
|
415
|
+
lines: it.lines.map((l) => ({ s: l.s, ...l.hl ? { hl: [q(l.hl[0]), q(l.hl[1])] } : {}, ...l.red ? { red: true } : {} }))
|
|
416
|
+
}
|
|
417
|
+
);
|
|
418
|
+
case "card":
|
|
419
|
+
return /* @__PURE__ */ jsx5(CardL, { t, q: q(it.when), left: p.left, top: p.top, w: p.w, h: p.h, color: resolveColor(T, it.color), children: it.items.map((f, i) => /* @__PURE__ */ jsx5(Item, { it: f, t }, i)) });
|
|
420
|
+
case "text":
|
|
421
|
+
return /* @__PURE__ */ jsx5("div", { style: { fontSize: it.size, color: resolveColor(T, it.color), ...it.spacing ? { letterSpacing: it.spacing } : {}, ...it.when !== void 0 ? { opacity: appearL(t, q(it.when)) } : {} }, children: /* @__PURE__ */ jsx5(TextParts, { it }) });
|
|
422
|
+
case "chip":
|
|
423
|
+
return /* @__PURE__ */ jsx5(ChipL, { t, q: q(it.when), left: p.left, top: p.top, color: resolveColor(T, it.color), size: it.size, children: it.text });
|
|
424
|
+
case "freetext":
|
|
425
|
+
return /* @__PURE__ */ jsx5(TextL, { t, q: q(it.when), left: p.left, top: p.top, w: p.w, size: it.size, color: resolveColor(T, it.color), align: it.align === "center" ? "center" : "left", mono: it.mono, children: /* @__PURE__ */ jsx5(TextParts, { it }) });
|
|
426
|
+
case "video":
|
|
427
|
+
return /* @__PURE__ */ jsx5(Window, { t, q: q(it.when), src: it.src, clipDuration: it.clipDuration, from: it.from ?? 0, left: p.left, top: p.top, w: p.w, reg: it.reg ?? [0, 0, 1920, 1080], tag: it.tag ?? "footage", dur: it.motionDuration ?? 20 });
|
|
428
|
+
case "phone":
|
|
429
|
+
return /* @__PURE__ */ jsx5(Phone, { t, q: q(it.when), src: it.src, clipDuration: it.clipDuration, from: it.from ?? 0, left: p.left, top: p.top, w: p.w, dur: it.motionDuration ?? 20 });
|
|
430
|
+
case "bar":
|
|
431
|
+
return /* @__PURE__ */ jsx5(BarL, { it, t, q: q(it.when), T });
|
|
432
|
+
case "counter":
|
|
433
|
+
return /* @__PURE__ */ jsx5(CounterL, { it, t, q: q(it.when), T });
|
|
434
|
+
case "flow": {
|
|
435
|
+
const nodes = it.nodes ?? [], size = it.size ?? 34, c0 = resolveColor(T, it.color) ?? T.accent;
|
|
436
|
+
return /* @__PURE__ */ jsx5("div", { style: { position: "absolute", left: p.left, top: p.top, width: p.w, display: "flex", alignItems: "center", justifyContent: "space-between", fontFamily: T.font }, children: nodes.map((n, i) => {
|
|
437
|
+
const o = appearL(t, q(n.when)), c = resolveColor(T, n.color) ?? c0;
|
|
438
|
+
return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", flex: i < nodes.length - 1 ? 1 : void 0 }, children: [
|
|
439
|
+
/* @__PURE__ */ jsx5("div", { style: { opacity: o, transform: `scale(${0.85 + 0.15 * o})`, background: T.longBox ?? T.box, border: `4px solid ${c}`, borderRadius: 22, padding: "16px 26px", fontSize: size, color: T.text, textAlign: "center", lineHeight: 1.2, whiteSpace: "pre-line" }, children: n.text }),
|
|
440
|
+
i < nodes.length - 1 ? /* @__PURE__ */ jsx5("div", { style: { flex: 1, textAlign: "center", fontSize: size * 1.2, color: T.muted, opacity: appearL(t, q(nodes[i + 1].when)) }, children: "\u2192" }) : null
|
|
441
|
+
] }, i);
|
|
442
|
+
}) });
|
|
443
|
+
}
|
|
444
|
+
default:
|
|
445
|
+
return /* @__PURE__ */ jsxs5("div", { style: { color: "#f00", fontSize: 30 }, children: [
|
|
446
|
+
"unknown type: ",
|
|
447
|
+
it.type
|
|
448
|
+
] });
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const S = spec.scenes.map((c) => c.start);
|
|
452
|
+
return {
|
|
453
|
+
id: spec.id,
|
|
454
|
+
durS: spec.durS,
|
|
455
|
+
voice: spec.voice,
|
|
456
|
+
words,
|
|
457
|
+
...spec.endcard ? { endcard: { a: spec.endcard.start, text: spec.endcard.text, link: spec.endcard.link, image: spec.endcard.image } } : {},
|
|
458
|
+
scenes: spec.scenes.map((c, i) => ({
|
|
459
|
+
n: c.n ?? String(i + 1),
|
|
460
|
+
title: c.title,
|
|
461
|
+
a: c.start,
|
|
462
|
+
b: i + 1 < S.length ? S[i + 1] : spec.durS,
|
|
463
|
+
C: c.component ? components[c.component] ?? (() => /* @__PURE__ */ jsxs5("div", { style: { color: "#f00", fontSize: 40 }, children: [
|
|
464
|
+
'component "',
|
|
465
|
+
c.component,
|
|
466
|
+
'" is not registered'
|
|
467
|
+
] })) : ({ t }) => /* @__PURE__ */ jsx5(Fragment4, { children: (c.items ?? []).map((it, k) => /* @__PURE__ */ jsx5(Item, { it, t }, k)) })
|
|
468
|
+
}))
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
var LongDecl = ({ spec, words, theme, components }) => {
|
|
472
|
+
const d = longDataFromSpec(spec, words, theme, components);
|
|
473
|
+
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
474
|
+
/* @__PURE__ */ jsx5(Composition2, { id: spec.id, component: () => /* @__PURE__ */ jsx5(ThemeProvider3, { theme, children: /* @__PURE__ */ jsx5(Long, { d }) }), durationInFrames: Math.round(spec.durS * FPS3), fps: FPS3, width: d.width ?? 1920, height: d.height ?? 1080 }),
|
|
475
|
+
spec.cover ? /* @__PURE__ */ jsx5(LongCovers, { spec: spec.cover, theme }) : null
|
|
476
|
+
] });
|
|
477
|
+
};
|
|
478
|
+
export {
|
|
479
|
+
Big,
|
|
480
|
+
Box,
|
|
481
|
+
Bubble,
|
|
482
|
+
CardL,
|
|
483
|
+
ChipL,
|
|
484
|
+
Code,
|
|
485
|
+
CodeL,
|
|
486
|
+
EpisodeDecl,
|
|
487
|
+
Frame,
|
|
488
|
+
Line,
|
|
489
|
+
LongCover16x9,
|
|
490
|
+
LongCoverVertical,
|
|
491
|
+
LongCovers,
|
|
492
|
+
LongDecl,
|
|
493
|
+
Mascot,
|
|
494
|
+
Panel,
|
|
495
|
+
Phone,
|
|
496
|
+
TextL,
|
|
497
|
+
Window,
|
|
498
|
+
appearL,
|
|
499
|
+
colorize,
|
|
500
|
+
episodeDataFromSpec,
|
|
501
|
+
longDataFromSpec,
|
|
502
|
+
resolveColor
|
|
503
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Theme } from "@praeko/studio-core";
|
|
2
|
+
export type LongCoverSpec = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: [string, string];
|
|
5
|
+
mascot?: string;
|
|
6
|
+
code?: {
|
|
7
|
+
title: string;
|
|
8
|
+
lines: {
|
|
9
|
+
s: string;
|
|
10
|
+
hl?: boolean;
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
chips?: {
|
|
14
|
+
text: string;
|
|
15
|
+
color?: string;
|
|
16
|
+
}[];
|
|
17
|
+
color?: string;
|
|
18
|
+
glow?: string;
|
|
19
|
+
};
|
|
20
|
+
export declare const LongCover16x9: React.FC<{
|
|
21
|
+
spec: LongCoverSpec;
|
|
22
|
+
}>;
|
|
23
|
+
/** Vertical (Instagram): two-line title, code card, chips and the mascot, inside the Shorts cover `Frame`. */
|
|
24
|
+
export declare const LongCoverVertical: React.FC<{
|
|
25
|
+
spec: LongCoverSpec;
|
|
26
|
+
}>;
|
|
27
|
+
/** The two Stills of a long-video cover: `<id>` (1280x720) and `<id>Vertical` (1080x1920). */
|
|
28
|
+
export declare const LongCovers: React.FC<{
|
|
29
|
+
spec: LongCoverSpec;
|
|
30
|
+
theme: Theme;
|
|
31
|
+
}>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { LongData, Theme, Word } from "@praeko/studio-core";
|
|
2
|
+
import type { LongCoverSpec } from "./long-cover.js";
|
|
3
|
+
export type Time = number | string | {
|
|
4
|
+
when: string;
|
|
5
|
+
nth?: number;
|
|
6
|
+
after?: number;
|
|
7
|
+
plus?: number;
|
|
8
|
+
};
|
|
9
|
+
type Pos = {
|
|
10
|
+
left?: number;
|
|
11
|
+
top?: number;
|
|
12
|
+
w?: number;
|
|
13
|
+
h?: number;
|
|
14
|
+
};
|
|
15
|
+
export type ItemL = {
|
|
16
|
+
type: string;
|
|
17
|
+
when?: Time;
|
|
18
|
+
pos?: Pos;
|
|
19
|
+
color?: string;
|
|
20
|
+
size?: number;
|
|
21
|
+
text?: string;
|
|
22
|
+
parts?: {
|
|
23
|
+
text: string;
|
|
24
|
+
color?: string;
|
|
25
|
+
mono?: boolean;
|
|
26
|
+
}[];
|
|
27
|
+
[k: string]: any;
|
|
28
|
+
};
|
|
29
|
+
export type LongSceneSpec = {
|
|
30
|
+
n?: string;
|
|
31
|
+
title: string;
|
|
32
|
+
start: number;
|
|
33
|
+
items?: ItemL[];
|
|
34
|
+
component?: string;
|
|
35
|
+
};
|
|
36
|
+
export type LongSpec = {
|
|
37
|
+
id: string;
|
|
38
|
+
durS: number;
|
|
39
|
+
voice: string;
|
|
40
|
+
engine?: string;
|
|
41
|
+
scenes: LongSceneSpec[];
|
|
42
|
+
cover?: LongCoverSpec;
|
|
43
|
+
endcard?: {
|
|
44
|
+
start: number;
|
|
45
|
+
text?: string;
|
|
46
|
+
link?: string;
|
|
47
|
+
image?: string;
|
|
48
|
+
};
|
|
49
|
+
mascot?: {
|
|
50
|
+
when: Time;
|
|
51
|
+
name: string;
|
|
52
|
+
plus?: number;
|
|
53
|
+
}[];
|
|
54
|
+
};
|
|
55
|
+
export declare function longDataFromSpec(spec: LongSpec, words: Word[], T: Theme, components?: Record<string, React.FC<{
|
|
56
|
+
t: number;
|
|
57
|
+
}>>): LongData;
|
|
58
|
+
/** Registers the long video Composition (1920x1080). */
|
|
59
|
+
export declare const LongDecl: React.FC<{
|
|
60
|
+
spec: LongSpec;
|
|
61
|
+
words: Word[];
|
|
62
|
+
theme: Theme;
|
|
63
|
+
components?: Record<string, React.FC<{
|
|
64
|
+
t: number;
|
|
65
|
+
}>>;
|
|
66
|
+
}>;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** 0 -> 1 from `q` (linear, d = 0.3 s), as in long videos. */
|
|
2
|
+
export declare const appearL: (t: number, q: number, d?: number) => number;
|
|
3
|
+
/** Colors a line of code/SQL (keywords, functions, numbers, strings, comments). */
|
|
4
|
+
export declare const colorize: (s: string, c: {
|
|
5
|
+
text: string;
|
|
6
|
+
muted: string;
|
|
7
|
+
ok: string;
|
|
8
|
+
accent: string;
|
|
9
|
+
info: string;
|
|
10
|
+
}) => (import("react/jsx-runtime").JSX.Element | null)[];
|
|
11
|
+
export type CodeLine = {
|
|
12
|
+
s: string;
|
|
13
|
+
hl?: [number, number];
|
|
14
|
+
red?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** Code block whose lines light up between `hl[0]` and `hl[1]` (global s). */
|
|
17
|
+
export declare const CodeL: React.FC<{
|
|
18
|
+
title: string;
|
|
19
|
+
lines: CodeLine[];
|
|
20
|
+
t: number;
|
|
21
|
+
q: number;
|
|
22
|
+
w: number;
|
|
23
|
+
size?: number;
|
|
24
|
+
left: number;
|
|
25
|
+
top: number;
|
|
26
|
+
}>;
|
|
27
|
+
export declare const CardL: React.FC<{
|
|
28
|
+
t: number;
|
|
29
|
+
q: number;
|
|
30
|
+
left: number;
|
|
31
|
+
top: number;
|
|
32
|
+
w: number;
|
|
33
|
+
h?: number;
|
|
34
|
+
color?: string;
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
}>;
|
|
37
|
+
export declare const ChipL: React.FC<{
|
|
38
|
+
t: number;
|
|
39
|
+
q: number;
|
|
40
|
+
left: number;
|
|
41
|
+
top: number;
|
|
42
|
+
color?: string;
|
|
43
|
+
size?: number;
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
}>;
|
|
46
|
+
export declare const TextL: React.FC<{
|
|
47
|
+
t: number;
|
|
48
|
+
q: number;
|
|
49
|
+
left: number;
|
|
50
|
+
top: number;
|
|
51
|
+
w?: number;
|
|
52
|
+
size?: number;
|
|
53
|
+
color?: string;
|
|
54
|
+
align?: "left" | "center";
|
|
55
|
+
mono?: boolean;
|
|
56
|
+
children: React.ReactNode;
|
|
57
|
+
}>;
|
package/dist/parts.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** Central panel of a Short: always the same size and place (70 px margin, clear of the label and the captions). */
|
|
2
|
+
export declare const Panel: React.FC<{
|
|
3
|
+
title: string;
|
|
4
|
+
dur: number;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}>;
|
|
7
|
+
export declare const Box: React.FC<{
|
|
8
|
+
t: number;
|
|
9
|
+
q: number;
|
|
10
|
+
color: string;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
mono?: boolean;
|
|
13
|
+
size?: number;
|
|
14
|
+
dim?: boolean;
|
|
15
|
+
dash?: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const Line: React.FC<{
|
|
18
|
+
t: number;
|
|
19
|
+
q: number;
|
|
20
|
+
color?: string;
|
|
21
|
+
size?: number;
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
}>;
|
|
24
|
+
export declare const Big: React.FC<{
|
|
25
|
+
t: number;
|
|
26
|
+
q: number;
|
|
27
|
+
color?: string;
|
|
28
|
+
size: number;
|
|
29
|
+
mono?: boolean;
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const Bubble: React.FC<{
|
|
33
|
+
t: number;
|
|
34
|
+
q: number;
|
|
35
|
+
color: string;
|
|
36
|
+
children: React.ReactNode;
|
|
37
|
+
author?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Code block with marked lines (the marked line color comes in `color`). */
|
|
40
|
+
export declare const Code: React.FC<{
|
|
41
|
+
t: number;
|
|
42
|
+
q: number;
|
|
43
|
+
title: string;
|
|
44
|
+
color: string;
|
|
45
|
+
lines: {
|
|
46
|
+
s: string;
|
|
47
|
+
color?: string;
|
|
48
|
+
mark?: boolean;
|
|
49
|
+
}[];
|
|
50
|
+
}>;
|
|
51
|
+
/** Real footage (of the game, or any recorded app) in a framed window, with optional crop and a slow camera push. */
|
|
52
|
+
export declare const Window: React.FC<{
|
|
53
|
+
t: number;
|
|
54
|
+
q: number;
|
|
55
|
+
src: string;
|
|
56
|
+
clipDuration: number;
|
|
57
|
+
from: number;
|
|
58
|
+
left: number;
|
|
59
|
+
top: number;
|
|
60
|
+
w: number;
|
|
61
|
+
reg?: [number, number, number, number];
|
|
62
|
+
tag?: string;
|
|
63
|
+
dur: number;
|
|
64
|
+
}>;
|
|
65
|
+
/** Vertical recording (phone) in a device frame. */
|
|
66
|
+
export declare const Phone: React.FC<{
|
|
67
|
+
t: number;
|
|
68
|
+
q: number;
|
|
69
|
+
src: string;
|
|
70
|
+
clipDuration: number;
|
|
71
|
+
from: number;
|
|
72
|
+
left: number;
|
|
73
|
+
top: number;
|
|
74
|
+
w: number;
|
|
75
|
+
dur: number;
|
|
76
|
+
}>;
|
|
77
|
+
/** Pixel-art mascot on a card. The image path comes from the theme (`mascotPath`, default `mascots/{name}.png`); `path` overrides it. */
|
|
78
|
+
export declare const Mascot: React.FC<{
|
|
79
|
+
name: string;
|
|
80
|
+
size?: number;
|
|
81
|
+
path?: string;
|
|
82
|
+
}>;
|
|
83
|
+
/** Frame of the vertical covers (content between y=260 and y=1660, the 3:4 crop of the Instagram grid). */
|
|
84
|
+
export declare const Frame: React.FC<{
|
|
85
|
+
glow: string;
|
|
86
|
+
title: [string, string, string?];
|
|
87
|
+
color: string;
|
|
88
|
+
children: React.ReactNode;
|
|
89
|
+
size?: number;
|
|
90
|
+
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praeko/studio-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Praeko Studio scene parts (panel, box, line, bubble, code, window, phone, frame) and the declarative episode that reads the compiled episode.yaml. A library for projects created with `praeko studio init`.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"praeko",
|
|
9
|
+
"video",
|
|
10
|
+
"shorts",
|
|
11
|
+
"remotion",
|
|
12
|
+
"captions",
|
|
13
|
+
"kit"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://praeko.app/docs",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://praeko.app/docs"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Praeko/praeko-js.git",
|
|
22
|
+
"directory": "studio/packages/kit"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"CHANGELOG.md",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"LICENSES.md"
|
|
39
|
+
],
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "node ../../../scripts/build-studio.mjs kit",
|
|
46
|
+
"prepack": "npm run build",
|
|
47
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
48
|
+
"test": "node --test test/*.test.ts"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@praeko/studio-core": "^0.2.0",
|
|
52
|
+
"react": "^19.0.0",
|
|
53
|
+
"remotion": "^4.0.539"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@praeko/studio-core": "*",
|
|
57
|
+
"@types/react": "19.2.7",
|
|
58
|
+
"react": "19.2.3",
|
|
59
|
+
"react-dom": "19.2.3",
|
|
60
|
+
"remotion": "4.0.539"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22"
|
|
64
|
+
}
|
|
65
|
+
}
|