reelkit-cli 0.1.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/LICENSE +21 -0
- package/README.md +91 -0
- package/bin/reelkit.mjs +5 -0
- package/package.json +89 -0
- package/skill/SKILL.md +101 -0
- package/skill/THIRD_PARTY.md +20 -0
- package/skill/command.md +5 -0
- package/skill/reference/asset-reuse.md +31 -0
- package/skill/reference/captions.md +57 -0
- package/skill/reference/component-authoring.md +26 -0
- package/skill/reference/kit.md +135 -0
- package/skill/reference/motion-design.md +62 -0
- package/skill/reference/remotion-composition.md +58 -0
- package/skill/reference/scene-treatments.md +35 -0
- package/skill/reference/scriptwriting.md +44 -0
- package/skill/reference/sound-design.md +43 -0
- package/src/agents.ts +103 -0
- package/src/api/client.ts +58 -0
- package/src/cli.ts +91 -0
- package/src/commands/assets.ts +196 -0
- package/src/commands/auth.ts +118 -0
- package/src/commands/build.ts +109 -0
- package/src/commands/init.ts +32 -0
- package/src/commands/install.ts +24 -0
- package/src/commands/plan.ts +45 -0
- package/src/context.ts +21 -0
- package/src/contract/index.ts +157 -0
- package/src/credentials.ts +55 -0
- package/src/open.ts +48 -0
- package/src/pipeline/review.ts +30 -0
- package/src/pipeline/schema.ts +110 -0
- package/src/pipeline/timing.ts +30 -0
- package/src/project/manifest.ts +71 -0
- package/src/project/probe.ts +54 -0
- package/src/project/project.ts +58 -0
- package/src/project/serve.ts +81 -0
- package/src/remotion/Root.tsx +29 -0
- package/src/remotion/kit/Captions.tsx +77 -0
- package/src/remotion/kit/Counter.tsx +18 -0
- package/src/remotion/kit/Entrance.tsx +53 -0
- package/src/remotion/kit/FootageLayer.tsx +10 -0
- package/src/remotion/kit/Icon.tsx +35 -0
- package/src/remotion/kit/KenBurnsImage.tsx +18 -0
- package/src/remotion/kit/Layers.tsx +46 -0
- package/src/remotion/kit/LowerThird.tsx +17 -0
- package/src/remotion/kit/SceneFrame.tsx +19 -0
- package/src/remotion/kit/ScreenOverlay.tsx +15 -0
- package/src/remotion/kit/Sfx.tsx +11 -0
- package/src/remotion/kit/TitleCard.tsx +23 -0
- package/src/remotion/kit/Voiceover.tsx +5 -0
- package/src/remotion/kit/brand-icons.ts +37 -0
- package/src/remotion/kit/docs.ts +98 -0
- package/src/remotion/kit/index.ts +20 -0
- package/src/remotion/kit/media.ts +10 -0
- package/src/remotion/kit/theme.ts +131 -0
- package/src/remotion/types.ts +2 -0
- package/src/render/component-preview.ts +65 -0
- package/src/render/deps.ts +8 -0
- package/src/render/render.ts +67 -0
- package/src/render/validate.ts +191 -0
- package/src/testing/conformance.ts +396 -0
- package/src/testing/fake-api.ts +228 -0
- package/src/testing/fixtures.ts +44 -0
- package/src/ui/CommandView.tsx +18 -0
- package/src/ui/run.tsx +20 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createReadStream, existsSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import type { AddressInfo } from "node:net";
|
|
5
|
+
import { extname, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import type { Project } from "./project";
|
|
7
|
+
|
|
8
|
+
const TYPES: Record<string, string> = {
|
|
9
|
+
".mp4": "video/mp4", ".mov": "video/quicktime", ".webm": "video/webm", ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4",
|
|
10
|
+
".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp", ".svg": "image/svg+xml", ".gif": "image/gif",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type MediaServer = { url(rel: string): string; close(): Promise<void> };
|
|
14
|
+
|
|
15
|
+
// Remotion loads media over HTTP, so the project's media is served on a local port while a build runs.
|
|
16
|
+
// Only files under <project>/assets/ are served, never .json, and only under a random per-run path prefix: access-control-allow-origin
|
|
17
|
+
// has to be * for Remotion's renderer, so the unguessable prefix is what keeps other web pages from reading the files.
|
|
18
|
+
export async function serveDir(root: string): Promise<MediaServer> {
|
|
19
|
+
const assets = join(resolve(root), "assets");
|
|
20
|
+
const prefix = randomBytes(16).toString("hex");
|
|
21
|
+
const server = createServer((req, res) => {
|
|
22
|
+
let path: string;
|
|
23
|
+
try {
|
|
24
|
+
path = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
25
|
+
} catch {
|
|
26
|
+
res.writeHead(400).end();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const lead = `/${prefix}/assets/`;
|
|
30
|
+
const file = path.startsWith(lead) ? resolve(assets, path.slice(lead.length)) : undefined;
|
|
31
|
+
let size = 0;
|
|
32
|
+
try {
|
|
33
|
+
if (!file || !file.startsWith(assets + sep) || extname(file).toLowerCase() === ".json" || !statSync(file).isFile()) { res.writeHead(404).end(); return; }
|
|
34
|
+
size = statSync(file).size;
|
|
35
|
+
} catch {
|
|
36
|
+
res.writeHead(404).end();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const headers: Record<string, string | number> = {
|
|
40
|
+
"content-type": TYPES[extname(file).toLowerCase()] ?? "application/octet-stream", "accept-ranges": "bytes", "access-control-allow-origin": "*",
|
|
41
|
+
};
|
|
42
|
+
let range: { start: number; end: number } | undefined;
|
|
43
|
+
const m = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range ?? "");
|
|
44
|
+
if (m && (m[1] || m[2])) {
|
|
45
|
+
// bytes=-N is the last N bytes; bytes=S- and bytes=S-E count from the start.
|
|
46
|
+
const start = m[1] ? Number(m[1]) : Math.max(0, size - Number(m[2]));
|
|
47
|
+
const end = m[1] ? Math.min(m[2] ? Number(m[2]) : size - 1, size - 1) : size - 1;
|
|
48
|
+
if (size === 0 || start >= size || start > end || (!m[1] && Number(m[2]) === 0)) {
|
|
49
|
+
res.writeHead(416, { ...headers, "content-range": `bytes */${size}` }).end();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
range = { start, end };
|
|
53
|
+
}
|
|
54
|
+
if (range) res.writeHead(206, { ...headers, "content-range": `bytes ${range.start}-${range.end}/${size}`, "content-length": range.end - range.start + 1 });
|
|
55
|
+
else res.writeHead(200, { ...headers, "content-length": size });
|
|
56
|
+
const stream = createReadStream(file, range);
|
|
57
|
+
// A file that cannot be read ends the response instead of crashing the build.
|
|
58
|
+
stream.on("error", () => res.destroy());
|
|
59
|
+
stream.pipe(res);
|
|
60
|
+
});
|
|
61
|
+
await new Promise<void>((r) => server.listen(0, "127.0.0.1", r));
|
|
62
|
+
const port = (server.address() as AddressInfo).port;
|
|
63
|
+
return {
|
|
64
|
+
url: (rel) => `http://127.0.0.1:${port}/${prefix}/${rel.split("/").map(encodeURIComponent).join("/")}`,
|
|
65
|
+
close: () => new Promise<void>((r) => { server.closeAllConnections(); server.close(() => r()); }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Every media file in the project, keyed by its project-relative path. This is the `urls` prop of the composition.
|
|
70
|
+
export function mediaUrls(project: Project, server: MediaServer): Record<string, string> {
|
|
71
|
+
const root = project.path("assets");
|
|
72
|
+
if (!existsSync(root)) return {};
|
|
73
|
+
const out: Record<string, string> = {};
|
|
74
|
+
for (const entry of readdirSync(root, { recursive: true }) as string[]) {
|
|
75
|
+
const abs = join(root, entry);
|
|
76
|
+
if (!statSync(abs).isFile() || entry.endsWith(".json")) continue;
|
|
77
|
+
const rel = relative(project.dir, abs).split(sep).join("/");
|
|
78
|
+
out[rel] = server.url(rel);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Composition } from "remotion";
|
|
3
|
+
import { UrlsContext } from "./kit/media";
|
|
4
|
+
import type { VideoProps } from "./types";
|
|
5
|
+
|
|
6
|
+
const EMPTY: VideoProps = { manifest: { fps: 30, width: 1080, height: 1920, totalFrames: 1, scenes: [] }, urls: {} };
|
|
7
|
+
|
|
8
|
+
export const makeRoot = (Video: React.FC<VideoProps>): React.FC => {
|
|
9
|
+
const WithMedia: React.FC<VideoProps> = (props) => (
|
|
10
|
+
<UrlsContext.Provider value={props.urls}>
|
|
11
|
+
<Video {...props} />
|
|
12
|
+
</UrlsContext.Provider>
|
|
13
|
+
);
|
|
14
|
+
return () => (
|
|
15
|
+
<Composition
|
|
16
|
+
id="Video"
|
|
17
|
+
component={WithMedia as unknown as React.FC<Record<string, unknown>>}
|
|
18
|
+
durationInFrames={1}
|
|
19
|
+
fps={30}
|
|
20
|
+
width={1080}
|
|
21
|
+
height={1920}
|
|
22
|
+
defaultProps={EMPTY as unknown as Record<string, unknown>}
|
|
23
|
+
calculateMetadata={({ props }) => {
|
|
24
|
+
const m = (props as unknown as VideoProps).manifest;
|
|
25
|
+
return { durationInFrames: m.totalFrames, fps: m.fps, width: m.width, height: m.height };
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
|
3
|
+
import type { WordTiming } from "../../pipeline/schema";
|
|
4
|
+
import { fonts, springs } from "./theme";
|
|
5
|
+
|
|
6
|
+
export type CaptionMode = "highlight" | "pop" | "karaoke";
|
|
7
|
+
|
|
8
|
+
// Word-timed captions drawn in the video itself.
|
|
9
|
+
// highlight: a line of words, the spoken one in the highlight colour (calm, educational)
|
|
10
|
+
// pop: one to three words at a time, each popping in as it is spoken (high energy)
|
|
11
|
+
// karaoke: a line that fills with the highlight colour as it is spoken (voiceover, music)
|
|
12
|
+
export const Captions: React.FC<{
|
|
13
|
+
words: WordTiming[];
|
|
14
|
+
mode?: CaptionMode;
|
|
15
|
+
highlight?: string;
|
|
16
|
+
color?: string;
|
|
17
|
+
perLine?: number;
|
|
18
|
+
uppercase?: boolean;
|
|
19
|
+
// Distance of the caption baseline from the bottom, as a fraction of the height. Default keeps it clear of platform UI.
|
|
20
|
+
bottom?: number;
|
|
21
|
+
// Font family. Set to font("heebo") or font("rubik") for Hebrew; rtl also reverses the word order on the line.
|
|
22
|
+
face?: string;
|
|
23
|
+
rtl?: boolean;
|
|
24
|
+
}> = ({ words, mode = "highlight", highlight = "#ffe14d", color = "#ffffff", perLine, uppercase = false, bottom = 0.16, face, rtl = false }) => {
|
|
25
|
+
const frame = useCurrentFrame();
|
|
26
|
+
const { fps, width, height } = useVideoConfig();
|
|
27
|
+
const t = frame / fps;
|
|
28
|
+
if (words.length === 0) return null;
|
|
29
|
+
|
|
30
|
+
const size = perLine ?? (mode === "pop" ? 2 : 4);
|
|
31
|
+
let active = words.findIndex((w) => t < w.endSec);
|
|
32
|
+
if (active === -1) active = words.length - 1;
|
|
33
|
+
const lineStart = Math.floor(active / size) * size;
|
|
34
|
+
const line = words.slice(lineStart, lineStart + size);
|
|
35
|
+
const show = (w: WordTiming) => (uppercase ? w.word.toUpperCase() : w.word);
|
|
36
|
+
|
|
37
|
+
const base: React.CSSProperties = {
|
|
38
|
+
fontFamily: face ?? fonts.body, fontWeight: 800, textAlign: "center", direction: rtl ? "rtl" : "ltr", padding: `0 ${width * 0.06}px`,
|
|
39
|
+
fontSize: width * (mode === "pop" ? 0.075 : 0.055), lineHeight: 1.15, color,
|
|
40
|
+
// A dark outline plus shadow keeps contrast above 4.5:1 on any background.
|
|
41
|
+
WebkitTextStroke: `${width * 0.006}px rgba(0,0,0,0.85)`, paintOrder: "stroke fill",
|
|
42
|
+
textShadow: "0 4px 18px rgba(0,0,0,0.7)",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<AbsoluteFill style={{ justifyContent: "flex-end", alignItems: "center", paddingBottom: height * bottom }}>
|
|
47
|
+
<div style={base}>
|
|
48
|
+
{line.map((w, i) => {
|
|
49
|
+
const index = lineStart + i;
|
|
50
|
+
const started = t >= w.startSec;
|
|
51
|
+
if (mode === "pop") {
|
|
52
|
+
// A word appears on the frame it starts, never before, with a small overshoot.
|
|
53
|
+
const p = spring({ frame: frame - Math.round(w.startSec * fps), fps, config: springs.snappy });
|
|
54
|
+
return (
|
|
55
|
+
<span key={index} style={{ display: "inline-block", margin: `0 ${width * 0.012}px`, opacity: started ? 1 : 0, transform: `scale(${interpolate(p, [0, 1], [0.6, 1])})`, color: index === active ? highlight : color }}>
|
|
56
|
+
{show(w)}
|
|
57
|
+
</span>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (mode === "karaoke") {
|
|
61
|
+
const fill = interpolate(t, [w.startSec, Math.max(w.endSec, w.startSec + 0.01)], [0, 100], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
|
|
62
|
+
return (
|
|
63
|
+
<span key={index} style={{ display: "inline-block", marginRight: "0.28em", backgroundImage: `linear-gradient(90deg, ${highlight} ${fill}%, ${color} ${fill}%)`, WebkitBackgroundClip: "text", backgroundClip: "text", color: "transparent", WebkitTextStroke: "0px transparent", textShadow: "none", filter: "drop-shadow(0 3px 6px rgba(0,0,0,0.75))" }}>
|
|
64
|
+
{show(w)}
|
|
65
|
+
</span>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return (
|
|
69
|
+
<span key={index} style={{ color: index === active && started ? highlight : color }}>
|
|
70
|
+
{show(w)}{" "}
|
|
71
|
+
</span>
|
|
72
|
+
);
|
|
73
|
+
})}
|
|
74
|
+
</div>
|
|
75
|
+
</AbsoluteFill>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fonts } from "./theme";
|
|
3
|
+
import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
|
|
4
|
+
|
|
5
|
+
export const Counter: React.FC<{ from?: number; to: number; durationFrames?: number; prefix?: string; suffix?: string; color?: string }> = ({
|
|
6
|
+
from = 0, to, durationFrames = 30, prefix = "", suffix = "", color = "#ffffff",
|
|
7
|
+
}) => {
|
|
8
|
+
const frame = useCurrentFrame();
|
|
9
|
+
const { width } = useVideoConfig();
|
|
10
|
+
const value = interpolate(frame, [0, durationFrames], [from, to], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
|
|
11
|
+
return (
|
|
12
|
+
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
|
|
13
|
+
<div style={{ color, fontFamily: fonts.display, fontWeight: 800, fontSize: width * 0.16, fontVariantNumeric: "tabular-nums" }}>
|
|
14
|
+
{prefix}{Math.round(value).toLocaleString("en-US")}{suffix}
|
|
15
|
+
</div>
|
|
16
|
+
</AbsoluteFill>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
|
3
|
+
import { ease, springs } from "./theme";
|
|
4
|
+
|
|
5
|
+
// The workhorse entrance: fade + rise + scale together, with an optional faster exit and a slow idle "breathe".
|
|
6
|
+
// delay and exitAt are frames, counted from the start of the enclosing scene.
|
|
7
|
+
export const Entrance: React.FC<{
|
|
8
|
+
delay?: number;
|
|
9
|
+
exitAt?: number;
|
|
10
|
+
rise?: number;
|
|
11
|
+
breathe?: boolean;
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}> = ({ delay = 0, exitAt, rise, breathe = true, style, children }) => {
|
|
15
|
+
const frame = useCurrentFrame();
|
|
16
|
+
const { fps, width } = useVideoConfig();
|
|
17
|
+
const distance = rise ?? width * 0.037;
|
|
18
|
+
const p = spring({ frame: frame - delay, fps, config: springs.smooth });
|
|
19
|
+
const out = exitAt === undefined ? 0 : interpolate(frame, [exitAt, exitAt + 10], [0, 1], { easing: ease.in, extrapolateLeft: "clamp", extrapolateRight: "clamp" });
|
|
20
|
+
const idle = breathe ? 1 + Math.sin((frame - delay) / 22) * 0.012 * p : 1;
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
style={{
|
|
24
|
+
opacity: p * (1 - out),
|
|
25
|
+
transform: `translateY(${interpolate(p, [0, 1], [distance, 0]) - out * distance}px) scale(${interpolate(p, [0, 1], [0.94, 1]) * idle})`,
|
|
26
|
+
...style,
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{children}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Text that rises in word by word from behind a mask. per is the stagger in frames.
|
|
35
|
+
export const WordReveal: React.FC<{ text: string; delay?: number; per?: number; highlight?: string; highlightColor?: string; style?: React.CSSProperties }> = ({
|
|
36
|
+
text, delay = 0, per = 3, highlight, highlightColor, style,
|
|
37
|
+
}) => {
|
|
38
|
+
const frame = useCurrentFrame();
|
|
39
|
+
const { fps } = useVideoConfig();
|
|
40
|
+
return (
|
|
41
|
+
<div style={{ display: "flex", flexWrap: "wrap", justifyContent: "center", columnGap: "0.26em", ...style }}>
|
|
42
|
+
{text.split(" ").map((word, i) => {
|
|
43
|
+
const p = spring({ frame: frame - delay - i * per, fps, config: springs.snappy });
|
|
44
|
+
const lit = highlight !== undefined && word.replace(/[^\p{L}\p{N}]/gu, "").toLowerCase() === highlight.toLowerCase();
|
|
45
|
+
return (
|
|
46
|
+
<span key={i} style={{ display: "inline-block", overflow: "hidden", paddingBottom: "0.12em" }}>
|
|
47
|
+
<span style={{ display: "inline-block", transform: `translateY(${interpolate(p, [0, 1], [105, 0])}%)`, color: lit ? highlightColor : undefined }}>{word}</span>
|
|
48
|
+
</span>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useMedia } from "./media";
|
|
3
|
+
import { AbsoluteFill, OffthreadVideo } from "remotion";
|
|
4
|
+
|
|
5
|
+
export const FootageLayer: React.FC<{ src: string; muted?: boolean; dim?: number }> = ({ src, muted = true, dim = 0 }) => (
|
|
6
|
+
<AbsoluteFill>
|
|
7
|
+
<OffthreadVideo src={useMedia(src)} muted={muted} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
|
|
8
|
+
{dim > 0 ? <AbsoluteFill style={{ backgroundColor: `rgba(0,0,0,${dim})` }} /> : null}
|
|
9
|
+
</AbsoluteFill>
|
|
10
|
+
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BRAND_ICONS, type BrandName } from "./brand-icons";
|
|
3
|
+
|
|
4
|
+
// Simple interface glyphs drawn on a 24 by 24 grid, for things that are not a brand.
|
|
5
|
+
const GLYPHS = {
|
|
6
|
+
message: "M4 4h16a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-7l-5 4v-4H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z",
|
|
7
|
+
mail: "M3 5h18a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm1.5 2.2V17h15V7.2L12 12.6 4.5 7.2z",
|
|
8
|
+
bell: "M12 2a6 6 0 0 0-6 6v4.2L4.2 15.6A1 1 0 0 0 5 17h14a1 1 0 0 0 .8-1.4L18 12.2V8a6 6 0 0 0-6-6zm-2.5 17a2.5 2.5 0 0 0 5 0h-5z",
|
|
9
|
+
phone: "M6.6 2h3.2l1.6 4.6-2.2 1.6a12.5 12.5 0 0 0 6.6 6.6l1.6-2.2L22 14.2v3.2A2.6 2.6 0 0 1 19.4 20 17.4 17.4 0 0 1 4 4.6 2.6 2.6 0 0 1 6.6 2z",
|
|
10
|
+
calendar: "M7 2v2H4a1 1 0 0 0-1 1v15a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-3V2h-2v2H9V2H7zm-2 8h14v9H5v-9z",
|
|
11
|
+
cart: "M3 3h2.6l.7 3H21l-2 8.5a1 1 0 0 1-1 .8H8.2a1 1 0 0 1-1-.8L4.9 5H3V3zm6 15.5a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2zm8 0a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z",
|
|
12
|
+
heart: "M12 21s-8.5-5.3-8.5-11.4A4.9 4.9 0 0 1 12 6.4a4.9 4.9 0 0 1 8.5 3.2C20.5 15.7 12 21 12 21z",
|
|
13
|
+
star: "M12 2.5l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.4l-5.9 3.1 1.2-6.5L2.5 9.4l6.6-.9L12 2.5z",
|
|
14
|
+
user: "M12 12a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9zm0 2c-4.4 0-8 2.4-8 5.4V21h16v-1.6c0-3-3.6-5.4-8-5.4z",
|
|
15
|
+
play: "M7 4.5v15a1 1 0 0 0 1.5.9l12-7.5a1 1 0 0 0 0-1.8l-12-7.5A1 1 0 0 0 7 4.5z",
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export type GlyphName = keyof typeof GLYPHS;
|
|
19
|
+
export type IconName = BrandName | GlyphName;
|
|
20
|
+
|
|
21
|
+
const isBrand = (name: IconName): name is BrandName => name in BRAND_ICONS;
|
|
22
|
+
|
|
23
|
+
// A platform's real logo mark (instagram, tiktok, youtube, whatsapp ...) or a plain interface glyph (message, bell, mail ...).
|
|
24
|
+
// color "brand" uses the platform's own colour; any other value is used as given. Glyphs treat "brand" as white.
|
|
25
|
+
export const Icon: React.FC<{ name: IconName; size: number; color?: string; style?: React.CSSProperties }> = ({ name, size, color = "#ffffff", style }) => {
|
|
26
|
+
const brand = isBrand(name) ? BRAND_ICONS[name] : undefined;
|
|
27
|
+
const fill = color === "brand" ? (brand?.hex ?? "#ffffff") : color;
|
|
28
|
+
return (
|
|
29
|
+
<svg viewBox="0 0 24 24" width={size} height={size} fill={fill} style={{ display: "block", flexShrink: 0, ...style }}>
|
|
30
|
+
<path d={brand ? brand.path : GLYPHS[name as GlyphName]} />
|
|
31
|
+
</svg>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const brandColor = (name: BrandName): string => BRAND_ICONS[name].hex;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useMedia } from "./media";
|
|
3
|
+
import { ease } from "./theme";
|
|
4
|
+
import { AbsoluteFill, Img, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
|
|
5
|
+
|
|
6
|
+
// direction "out" starts zoomed and pulls back; alternate between consecutive image scenes.
|
|
7
|
+
export const KenBurnsImage: React.FC<{ src: string; zoom?: number; direction?: "in" | "out" }> = ({ src, zoom = 1.1, direction = "in" }) => {
|
|
8
|
+
const frame = useCurrentFrame();
|
|
9
|
+
const { durationInFrames } = useVideoConfig();
|
|
10
|
+
const range = direction === "in" ? [1, zoom] : [zoom, 1];
|
|
11
|
+
const scale = interpolate(frame, [0, durationInFrames], range, { easing: ease.inOut, extrapolateRight: "clamp" });
|
|
12
|
+
const pan = interpolate(frame, [0, durationInFrames], [0, -1.2], { extrapolateRight: "clamp" });
|
|
13
|
+
return (
|
|
14
|
+
<AbsoluteFill style={{ overflow: "hidden" }}>
|
|
15
|
+
<Img src={useMedia(src)} style={{ width: "100%", height: "100%", objectFit: "cover", transform: `scale(${scale}) translateX(${pan}%)` }} />
|
|
16
|
+
</AbsoluteFill>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
|
|
3
|
+
|
|
4
|
+
// The bottom layer of every scene: the base colour with two slow-drifting soft colour fields. Never a flat fill.
|
|
5
|
+
export const BgMesh: React.FC<{ bg: string; hero: string; accent?: string }> = ({ bg, hero, accent = hero }) => {
|
|
6
|
+
const frame = useCurrentFrame();
|
|
7
|
+
const { width, height } = useVideoConfig();
|
|
8
|
+
const d1 = Math.sin(frame / 55) * width * 0.045;
|
|
9
|
+
const d2 = Math.cos(frame / 70) * width * 0.035;
|
|
10
|
+
const big = Math.max(width, height);
|
|
11
|
+
return (
|
|
12
|
+
<AbsoluteFill style={{ background: bg, overflow: "hidden" }}>
|
|
13
|
+
<div style={{ position: "absolute", width: big * 1.1, height: big * 1.1, borderRadius: "50%", top: -big * 0.42, left: -big * 0.28 + d1, filter: `blur(${big * 0.045}px)`, background: `radial-gradient(circle, ${hero}33, transparent 62%)` }} />
|
|
14
|
+
<div style={{ position: "absolute", width: big * 0.85, height: big * 0.85, borderRadius: "50%", bottom: -big * 0.38, right: -big * 0.24 - d2, filter: `blur(${big * 0.065}px)`, background: `radial-gradient(circle, ${accent}22, transparent 65%)` }} />
|
|
15
|
+
</AbsoluteFill>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Sits above all content. Tints everything slightly toward the hero colour so mixed images and graphics read as one look.
|
|
20
|
+
export const Grade: React.FC<{ color: string; strength?: number }> = ({ color, strength = 0.16 }) => (
|
|
21
|
+
<AbsoluteFill style={{ pointerEvents: "none" }}>
|
|
22
|
+
<AbsoluteFill style={{ backgroundColor: color, mixBlendMode: "soft-light", opacity: strength }} />
|
|
23
|
+
<AbsoluteFill style={{ background: "linear-gradient(180deg, rgba(0,0,0,0.10), transparent 28%, transparent 72%, rgba(0,0,0,0.2))" }} />
|
|
24
|
+
</AbsoluteFill>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const NOISE = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E")`;
|
|
28
|
+
|
|
29
|
+
// Film grain with no asset file. Use blend "multiply" on light themes and "overlay" on dark ones.
|
|
30
|
+
export const Grain: React.FC<{ opacity?: number; blend?: "multiply" | "overlay" }> = ({ opacity = 0.05, blend = "overlay" }) => {
|
|
31
|
+
const frame = useCurrentFrame();
|
|
32
|
+
return (
|
|
33
|
+
<AbsoluteFill
|
|
34
|
+
style={{
|
|
35
|
+
pointerEvents: "none", backgroundImage: NOISE, backgroundSize: "220px",
|
|
36
|
+
backgroundPosition: `${(frame * 7) % 220}px ${(frame * 13) % 220}px`,
|
|
37
|
+
opacity, mixBlendMode: blend,
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// The topmost layer.
|
|
44
|
+
export const Vignette: React.FC<{ strength?: number }> = ({ strength = 0.22 }) => (
|
|
45
|
+
<AbsoluteFill style={{ pointerEvents: "none", background: `radial-gradient(ellipse at center, transparent 56%, rgba(0,0,0,${strength}) 100%)` }} />
|
|
46
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fonts } from "./theme";
|
|
3
|
+
import { AbsoluteFill, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
|
4
|
+
|
|
5
|
+
export const LowerThird: React.FC<{ title: string; subtitle?: string; accent?: string }> = ({ title, subtitle, accent = "#5b8cff" }) => {
|
|
6
|
+
const frame = useCurrentFrame();
|
|
7
|
+
const { fps, width, height } = useVideoConfig();
|
|
8
|
+
const slide = spring({ frame, fps, config: { damping: 16 } });
|
|
9
|
+
return (
|
|
10
|
+
<AbsoluteFill style={{ justifyContent: "flex-end", padding: width * 0.06, paddingBottom: height * 0.22 }}>
|
|
11
|
+
<div style={{ transform: `translateX(${(1 - slide) * -width * 0.5}px)`, opacity: slide, borderLeft: `${width * 0.012}px solid ${accent}`, paddingLeft: width * 0.03, fontFamily: fonts.display, color: "#fff" }}>
|
|
12
|
+
<div style={{ fontSize: width * 0.05, fontWeight: 700 }}>{title}</div>
|
|
13
|
+
{subtitle ? <div style={{ fontSize: width * 0.032, opacity: 0.8 }}>{subtitle}</div> : null}
|
|
14
|
+
</div>
|
|
15
|
+
</AbsoluteFill>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AbsoluteFill, interpolate, Sequence, useCurrentFrame } from "remotion";
|
|
3
|
+
|
|
4
|
+
const Fade: React.FC<{ durationInFrames: number; children: React.ReactNode }> = ({ durationInFrames, children }) => {
|
|
5
|
+
const frame = useCurrentFrame();
|
|
6
|
+
const edge = Math.min(8, Math.floor(durationInFrames / 2));
|
|
7
|
+
const opacity = interpolate(frame, [0, edge, durationInFrames - edge, durationInFrames], [0, 1, 1, 0], {
|
|
8
|
+
extrapolateLeft: "clamp", extrapolateRight: "clamp",
|
|
9
|
+
});
|
|
10
|
+
return <AbsoluteFill style={{ opacity }}>{children}</AbsoluteFill>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const SceneFrame: React.FC<{ from: number; durationInFrames: number; children: React.ReactNode }> = ({
|
|
14
|
+
from, durationInFrames, children,
|
|
15
|
+
}) => (
|
|
16
|
+
<Sequence from={from} durationInFrames={durationInFrames}>
|
|
17
|
+
<Fade durationInFrames={durationInFrames}>{children}</Fade>
|
|
18
|
+
</Sequence>
|
|
19
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useMedia } from "./media";
|
|
3
|
+
import { AbsoluteFill, Loop, OffthreadVideo, useVideoConfig } from "remotion";
|
|
4
|
+
|
|
5
|
+
// Lays a white-on-black overlay clip (film marks, HUD frames) over the video. The "screen" blend drops the black.
|
|
6
|
+
// Pass durationSec from the overlay's record to loop it for the whole video; without it the clip plays once.
|
|
7
|
+
export const ScreenOverlay: React.FC<{ src: string; durationSec?: number; opacity?: number }> = ({ src, durationSec, opacity = 0.55 }) => {
|
|
8
|
+
const { fps } = useVideoConfig();
|
|
9
|
+
const clip = <OffthreadVideo src={useMedia(src)} muted style={{ width: "100%", height: "100%", objectFit: "cover" }} />;
|
|
10
|
+
return (
|
|
11
|
+
<AbsoluteFill style={{ mixBlendMode: "screen", opacity, pointerEvents: "none" }}>
|
|
12
|
+
{durationSec ? <Loop durationInFrames={Math.max(1, Math.floor(durationSec * fps))}>{clip}</Loop> : clip}
|
|
13
|
+
</AbsoluteFill>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useMedia } from "./media";
|
|
3
|
+
import { Audio, Sequence } from "remotion";
|
|
4
|
+
|
|
5
|
+
// Plays one sound effect starting at a frame. at is counted from the start of the enclosing scene, or of the video
|
|
6
|
+
// when placed outside the scenes. Keep volume well under the voiceover.
|
|
7
|
+
export const Sfx: React.FC<{ src: string; at?: number; volume?: number }> = ({ src, at = 0, volume = 0.35 }) => (
|
|
8
|
+
<Sequence from={Math.max(0, Math.round(at))} layout="none">
|
|
9
|
+
<Audio src={useMedia(src)} volume={volume} />
|
|
10
|
+
</Sequence>
|
|
11
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fonts } from "./theme";
|
|
3
|
+
import { AbsoluteFill, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
|
4
|
+
|
|
5
|
+
export const TitleCard: React.FC<{ text: string; subtitle?: string; color?: string; background?: string }> = ({
|
|
6
|
+
text, subtitle, color = "#ffffff", background = "transparent",
|
|
7
|
+
}) => {
|
|
8
|
+
const frame = useCurrentFrame();
|
|
9
|
+
const { fps, width } = useVideoConfig();
|
|
10
|
+
const pop = spring({ frame, fps, config: { damping: 14 } });
|
|
11
|
+
return (
|
|
12
|
+
<AbsoluteFill style={{ background, alignItems: "center", justifyContent: "center", padding: width * 0.08 }}>
|
|
13
|
+
<div style={{ color, fontFamily: fonts.display, fontWeight: 800, fontSize: width * 0.09, textAlign: "center", lineHeight: 1.1, transform: `scale(${0.8 + 0.2 * pop})`, opacity: pop }}>
|
|
14
|
+
{text}
|
|
15
|
+
</div>
|
|
16
|
+
{subtitle ? (
|
|
17
|
+
<div style={{ color, opacity: 0.75 * pop, fontFamily: fonts.display, fontSize: width * 0.04, marginTop: width * 0.03, textAlign: "center" }}>
|
|
18
|
+
{subtitle}
|
|
19
|
+
</div>
|
|
20
|
+
) : null}
|
|
21
|
+
</AbsoluteFill>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by scripts/extract-brand-icons.mjs from simple-icons (icon data CC0-1.0). Do not edit by hand.
|
|
2
|
+
// The marks are trademarks of their owners: use them only to refer to that service.
|
|
3
|
+
export const BRAND_ICONS = {
|
|
4
|
+
"instagram": { title: "Instagram", hex: "#FF0069", path: "M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077" },
|
|
5
|
+
"tiktok": { title: "TikTok", hex: "#000000", path: "M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z" },
|
|
6
|
+
"youtube": { title: "YouTube", hex: "#FF0000", path: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" },
|
|
7
|
+
"x": { title: "X", hex: "#000000", path: "M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z" },
|
|
8
|
+
"facebook": { title: "Facebook", hex: "#0866FF", path: "M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z" },
|
|
9
|
+
"whatsapp": { title: "WhatsApp", hex: "#25D366", path: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z" },
|
|
10
|
+
"telegram": { title: "Telegram", hex: "#26A5E4", path: "M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" },
|
|
11
|
+
"twitch": { title: "Twitch", hex: "#9146FF", path: "M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714Z" },
|
|
12
|
+
"discord": { title: "Discord", hex: "#5865F2", path: "M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" },
|
|
13
|
+
"spotify": { title: "Spotify", hex: "#1ED760", path: "M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z" },
|
|
14
|
+
"snapchat": { title: "Snapchat", hex: "#FFFC00", path: "M12.206.793c.99 0 4.347.276 5.93 3.821.529 1.193.403 3.219.299 4.847l-.003.06c-.012.18-.022.345-.03.51.075.045.203.09.401.09.3-.016.659-.12 1.033-.301.165-.088.344-.104.464-.104.182 0 .359.029.509.09.45.149.734.479.734.838.015.449-.39.839-1.213 1.168-.089.029-.209.075-.344.119-.45.135-1.139.36-1.333.81-.09.224-.061.524.12.868l.015.015c.06.136 1.526 3.475 4.791 4.014.255.044.435.27.42.509 0 .075-.015.149-.045.225-.24.569-1.273.988-3.146 1.271-.059.091-.12.375-.164.57-.029.179-.074.36-.134.553-.076.271-.27.405-.555.405h-.03c-.135 0-.313-.031-.538-.074-.36-.075-.765-.135-1.273-.135-.3 0-.599.015-.913.074-.6.104-1.123.464-1.723.884-.853.599-1.826 1.288-3.294 1.288-.06 0-.119-.015-.18-.015h-.149c-1.468 0-2.427-.675-3.279-1.288-.599-.42-1.107-.779-1.707-.884-.314-.045-.629-.074-.928-.074-.54 0-.958.089-1.272.149-.211.043-.391.074-.54.074-.374 0-.523-.224-.583-.42-.061-.192-.09-.389-.135-.567-.046-.181-.105-.494-.166-.57-1.918-.222-2.95-.642-3.189-1.226-.031-.063-.052-.15-.055-.225-.015-.243.165-.465.42-.509 3.264-.54 4.73-3.879 4.791-4.02l.016-.029c.18-.345.224-.645.119-.869-.195-.434-.884-.658-1.332-.809-.121-.029-.24-.074-.346-.119-1.107-.435-1.257-.93-1.197-1.273.09-.479.674-.793 1.168-.793.146 0 .27.029.383.074.42.194.789.3 1.104.3.234 0 .384-.06.465-.105l-.046-.569c-.098-1.626-.225-3.651.307-4.837C7.392 1.077 10.739.807 11.727.807l.419-.015h.06z" },
|
|
15
|
+
"pinterest": { title: "Pinterest", hex: "#BD081C", path: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026L12.017 0z" },
|
|
16
|
+
"messenger": { title: "Messenger", hex: "#0866FF", path: "M12 0C5.24 0 0 4.952 0 11.64c0 3.499 1.434 6.521 3.769 8.61a.96.96 0 0 1 .323.683l.065 2.135a.96.96 0 0 0 1.347.85l2.381-1.053a.96.96 0 0 1 .641-.046A13 13 0 0 0 12 23.28c6.76 0 12-4.952 12-11.64S18.76 0 12 0m6.806 7.44c.522-.03.971.567.63 1.094l-4.178 6.457a.707.707 0 0 1-.977.208l-3.87-2.504a.44.44 0 0 0-.49.007l-4.363 3.01c-.637.438-1.415-.317-.995-.966l4.179-6.457a.706.706 0 0 1 .977-.21l3.87 2.505c.15.097.344.094.491-.007l4.362-3.008a.7.7 0 0 1 .364-.13" },
|
|
17
|
+
"threads": { title: "Threads", hex: "#000000", path: "M18.263 11.097c-.03-3.486-1.92-5.586-5.111-5.586-2.13 0-3.922.963-4.863 2.499l2.062 1.438c.535-.843 1.272-1.543 2.628-1.543 1.528 0 2.318.85 2.544 2.431a15 15 0 0 0-2.236-.173c-4.125 0-6.068 1.867-6.068 4.336s1.943 3.99 4.804 3.99c3.139 0 5.013-2.115 5.781-4.735.798.361 1.348 1.204 1.348 2.47 0 3.387-3.907 5.232-7.22 5.232-4.885 0-8.077-3.207-8.077-8.424 0-6.392 4.223-10.487 9.9-10.487 3.808 0 5.69 1.671 6.97 3.914l2.108-1.475C21.44 2.078 18.331 0 13.663 0 6.227 0 1.168 5.277 1.168 12.934c0 7 4.953 11.066 10.856 11.066 4.878 0 9.809-2.846 9.809-7.716 0-2.545-1.46-4.231-3.569-5.187m-6.33 4.855c-1.077 0-2.026-.512-2.026-1.453 0-1.483 1.822-1.934 3.606-1.934.678 0 1.34.045 1.927.173-.422 1.927-1.671 3.215-3.508 3.214Z" },
|
|
18
|
+
"reddit": { title: "Reddit", hex: "#FF4500", path: "M12 0C5.373 0 0 5.373 0 12c0 3.314 1.343 6.314 3.515 8.485l-2.286 2.286C.775 23.225 1.097 24 1.738 24H12c6.627 0 12-5.373 12-12S18.627 0 12 0Zm4.388 3.199c1.104 0 1.999.895 1.999 1.999 0 1.105-.895 2-1.999 2-.946 0-1.739-.657-1.947-1.539v.002c-1.147.162-2.032 1.15-2.032 2.341v.007c1.776.067 3.4.567 4.686 1.363.473-.363 1.064-.58 1.707-.58 1.547 0 2.802 1.254 2.802 2.802 0 1.117-.655 2.081-1.601 2.531-.088 3.256-3.637 5.876-7.997 5.876-4.361 0-7.905-2.617-7.998-5.87-.954-.447-1.614-1.415-1.614-2.538 0-1.548 1.255-2.802 2.803-2.802.645 0 1.239.218 1.712.585 1.275-.79 2.881-1.291 4.64-1.365v-.01c0-1.663 1.263-3.034 2.88-3.207.188-.911.993-1.595 1.959-1.595Zm-8.085 8.376c-.784 0-1.459.78-1.506 1.797-.047 1.016.64 1.429 1.426 1.429.786 0 1.371-.369 1.418-1.385.047-1.017-.553-1.841-1.338-1.841Zm7.406 0c-.786 0-1.385.824-1.338 1.841.047 1.017.634 1.385 1.418 1.385.785 0 1.473-.413 1.426-1.429-.046-1.017-.721-1.797-1.506-1.797Zm-3.703 4.013c-.974 0-1.907.048-2.77.135-.147.015-.241.168-.183.305.483 1.154 1.622 1.964 2.953 1.964 1.33 0 2.47-.81 2.953-1.964.057-.137-.037-.29-.184-.305-.863-.087-1.795-.135-2.769-.135Z" },
|
|
19
|
+
"github": { title: "GitHub", hex: "#181717", path: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" },
|
|
20
|
+
"gmail": { title: "Gmail", hex: "#EA4335", path: "M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z" },
|
|
21
|
+
"soundcloud": { title: "SoundCloud", hex: "#FF5500", path: "M23.999 14.165c-.052 1.796-1.612 3.169-3.4 3.169h-8.18a.68.68 0 0 1-.675-.683V7.862a.747.747 0 0 1 .452-.724s.75-.513 2.333-.513a5.364 5.364 0 0 1 2.763.755 5.433 5.433 0 0 1 2.57 3.54c.282-.08.574-.121.868-.12.884 0 1.73.358 2.347.992s.948 1.49.922 2.373ZM10.721 8.421c.247 2.98.427 5.697 0 8.672a.264.264 0 0 1-.53 0c-.395-2.946-.22-5.718 0-8.672a.264.264 0 0 1 .53 0ZM9.072 9.448c.285 2.659.37 4.986-.006 7.655a.277.277 0 0 1-.55 0c-.331-2.63-.256-5.02 0-7.655a.277.277 0 0 1 .556 0Zm-1.663-.257c.27 2.726.39 5.171 0 7.904a.266.266 0 0 1-.532 0c-.38-2.69-.257-5.21 0-7.904a.266.266 0 0 1 .532 0Zm-1.647.77a26.108 26.108 0 0 1-.008 7.147.272.272 0 0 1-.542 0 27.955 27.955 0 0 1 0-7.147.275.275 0 0 1 .55 0Zm-1.67 1.769c.421 1.865.228 3.5-.029 5.388a.257.257 0 0 1-.514 0c-.21-1.858-.398-3.549 0-5.389a.272.272 0 0 1 .543 0Zm-1.655-.273c.388 1.897.26 3.508-.01 5.412-.026.28-.514.283-.54 0-.244-1.878-.347-3.54-.01-5.412a.283.283 0 0 1 .56 0Zm-1.668.911c.4 1.268.257 2.292-.026 3.572a.257.257 0 0 1-.514 0c-.241-1.262-.354-2.312-.023-3.572a.283.283 0 0 1 .563 0Z" },
|
|
22
|
+
"behance": { title: "Behance", hex: "#1769FF", path: "M16.969 16.927a2.561 2.561 0 0 0 1.901.677 2.501 2.501 0 0 0 1.531-.475c.362-.235.636-.584.779-.99h2.585a5.091 5.091 0 0 1-1.9 2.896 5.292 5.292 0 0 1-3.091.88 5.839 5.839 0 0 1-2.284-.433 4.871 4.871 0 0 1-1.723-1.211 5.657 5.657 0 0 1-1.08-1.874 7.057 7.057 0 0 1-.383-2.393c-.005-.8.129-1.595.396-2.349a5.313 5.313 0 0 1 5.088-3.604 4.87 4.87 0 0 1 2.376.563c.661.362 1.231.87 1.668 1.485a6.2 6.2 0 0 1 .943 2.133c.194.821.263 1.666.205 2.508h-7.699c-.063.79.184 1.574.688 2.187ZM6.947 4.084a8.065 8.065 0 0 1 1.928.198 4.29 4.29 0 0 1 1.49.638c.418.303.748.711.958 1.182.241.579.357 1.203.341 1.83a3.506 3.506 0 0 1-.506 1.961 3.726 3.726 0 0 1-1.503 1.287 3.588 3.588 0 0 1 2.027 1.437c.464.747.697 1.615.67 2.494a4.593 4.593 0 0 1-.423 2.032 3.945 3.945 0 0 1-1.163 1.413 5.114 5.114 0 0 1-1.683.807 7.135 7.135 0 0 1-1.928.259H0V4.084h6.947Zm-.235 12.9c.308.004.616-.029.916-.099a2.18 2.18 0 0 0 .766-.332c.228-.158.411-.371.534-.619.142-.317.208-.663.191-1.009a2.08 2.08 0 0 0-.642-1.715 2.618 2.618 0 0 0-1.696-.505h-3.54v4.279h3.471Zm13.635-5.967a2.13 2.13 0 0 0-1.654-.619 2.336 2.336 0 0 0-1.163.259 2.474 2.474 0 0 0-.738.62 2.359 2.359 0 0 0-.396.792c-.074.239-.12.485-.137.734h4.769a3.239 3.239 0 0 0-.679-1.785l-.002-.001Zm-13.813-.648a2.254 2.254 0 0 0 1.423-.433c.399-.355.607-.88.56-1.413a1.916 1.916 0 0 0-.178-.891 1.298 1.298 0 0 0-.495-.533 1.851 1.851 0 0 0-.711-.274 3.966 3.966 0 0 0-.835-.073H3.241v3.631h3.293v-.014ZM21.62 5.122h-5.976v1.527h5.976V5.122Z" },
|
|
23
|
+
"vimeo": { title: "Vimeo", hex: "#1AB7EA", path: "M23.9765 6.4168c-.105 2.338-1.739 5.5429-4.894 9.6088-3.2679 4.247-6.0258 6.3699-8.2898 6.3699-1.409 0-2.578-1.294-3.553-3.881l-1.9179-7.1138c-.719-2.584-1.488-3.878-2.312-3.878-.179 0-.806.378-1.8809 1.132l-1.129-1.457a315.06 315.06 0 003.501-3.1279c1.579-1.368 2.765-2.085 3.5539-2.159 1.867-.18 3.016 1.1 3.447 3.838.465 2.953.789 4.789.971 5.5069.5389 2.45 1.1309 3.674 1.7759 3.674.502 0 1.256-.796 2.265-2.385 1.004-1.589 1.54-2.797 1.612-3.628.144-1.371-.395-2.061-1.614-2.061-.574 0-1.167.121-1.777.391 1.186-3.8679 3.434-5.7568 6.7619-5.6368 2.4729.06 3.6279 1.664 3.4929 4.7969z" },
|
|
24
|
+
"dribbble": { title: "Dribbble", hex: "#EA4C89", path: "M12 24C5.385 24 0 18.615 0 12S5.385 0 12 0s12 5.385 12 12-5.385 12-12 12zm10.12-10.358c-.35-.11-3.17-.953-6.384-.438 1.34 3.684 1.887 6.684 1.992 7.308 2.3-1.555 3.936-4.02 4.395-6.87zm-6.115 7.808c-.153-.9-.75-4.032-2.19-7.77l-.066.02c-5.79 2.015-7.86 6.025-8.04 6.4 1.73 1.358 3.92 2.166 6.29 2.166 1.42 0 2.77-.29 4-.814zm-11.62-2.58c.232-.4 3.045-5.055 8.332-6.765.135-.045.27-.084.405-.12-.26-.585-.54-1.167-.832-1.74C7.17 11.775 2.206 11.71 1.756 11.7l-.004.312c0 2.633.998 5.037 2.634 6.855zm-2.42-8.955c.46.008 4.683.026 9.477-1.248-1.698-3.018-3.53-5.558-3.8-5.928-2.868 1.35-5.01 3.99-5.676 7.17zM9.6 2.052c.282.38 2.145 2.914 3.822 6 3.645-1.365 5.19-3.44 5.373-3.702-1.81-1.61-4.19-2.586-6.795-2.586-.825 0-1.63.1-2.4.285zm10.335 3.483c-.218.29-1.935 2.493-5.724 4.04.24.49.47.985.68 1.486.08.18.15.36.22.53 3.41-.43 6.8.26 7.14.33-.02-2.42-.88-4.64-2.31-6.38z" },
|
|
25
|
+
"imessage": { title: "iMessage", hex: "#34DA50", path: "M5.285 0A5.273 5.273 0 0 0 0 5.285v13.43A5.273 5.273 0 0 0 5.285 24h13.43A5.273 5.273 0 0 0 24 18.715V5.285A5.273 5.273 0 0 0 18.715 0ZM12 4.154a8.809 7.337 0 0 1 8.809 7.338A8.809 7.337 0 0 1 12 18.828a8.809 7.337 0 0 1-2.492-.303A8.656 7.337 0 0 1 5.93 19.93a9.929 7.337 0 0 0 1.54-2.155 8.809 7.337 0 0 1-4.279-6.283A8.809 7.337 0 0 1 12 4.154" },
|
|
26
|
+
"apple": { title: "Apple", hex: "#000000", path: "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701" },
|
|
27
|
+
"googlemessages": { title: "Google Messages", hex: "#1A73E8", path: "M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zM4.911 7.089h11.456a2.197 2.197 0 0 1 2.165 2.19v5.863a2.213 2.213 0 0 1-2.177 2.178H8.04c-1.174 0-2.04-.99-2.04-2.178v-4.639L4.503 7.905c-.31-.42-.05-.816.408-.816zm3.415 2.19c-.347 0-.68.21-.68.544 0 .334.333.544.68.544h7.905c.346 0 .68-.21.68-.544 0-.334-.334-.545-.68-.545zm0 2.177c-.347 0-.68.21-.68.544 0 .334.333.544.68.544h7.905c.346 0 .68-.21.68-.544 0-.334-.334-.544-.68-.544zm-.013 2.19c-.346 0-.68.21-.68.544 0 .334.334.544.68.544h5.728c.347 0 .68-.21.68-.544 0-.334-.333-.545-.68-.545z" },
|
|
28
|
+
"signal": { title: "Signal", hex: "#3B45FD", path: "M12 0q-.934 0-1.83.139l.17 1.111a11 11 0 0 1 3.32 0l.172-1.111A12 12 0 0 0 12 0M9.152.34A12 12 0 0 0 5.77 1.742l.584.961a10.8 10.8 0 0 1 3.066-1.27zm5.696 0-.268 1.094a10.8 10.8 0 0 1 3.066 1.27l.584-.962A12 12 0 0 0 14.848.34M12 2.25a9.75 9.75 0 0 0-8.539 14.459c.074.134.1.292.064.441l-1.013 4.338 4.338-1.013a.62.62 0 0 1 .441.064A9.7 9.7 0 0 0 12 21.75c5.385 0 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25m-7.092.068a12 12 0 0 0-2.59 2.59l.909.664a11 11 0 0 1 2.345-2.345zm14.184 0-.664.909a11 11 0 0 1 2.345 2.345l.909-.664a12 12 0 0 0-2.59-2.59M1.742 5.77A12 12 0 0 0 .34 9.152l1.094.268a10.8 10.8 0 0 1 1.269-3.066zm20.516 0-.961.584a10.8 10.8 0 0 1 1.27 3.066l1.093-.268a12 12 0 0 0-1.402-3.383M.138 10.168A12 12 0 0 0 0 12q0 .934.139 1.83l1.111-.17A11 11 0 0 1 1.125 12q0-.848.125-1.66zm23.723.002-1.111.17q.125.812.125 1.66c0 .848-.042 1.12-.125 1.66l1.111.172a12.1 12.1 0 0 0 0-3.662M1.434 14.58l-1.094.268a12 12 0 0 0 .96 2.591l-.265 1.14 1.096.255.36-1.539-.188-.365a10.8 10.8 0 0 1-.87-2.35m21.133 0a10.8 10.8 0 0 1-1.27 3.067l.962.584a12 12 0 0 0 1.402-3.383zm-1.793 3.848a11 11 0 0 1-2.345 2.345l.664.909a12 12 0 0 0 2.59-2.59zm-19.959 1.1L.357 21.48a1.8 1.8 0 0 0 2.162 2.161l1.954-.455-.256-1.095-1.953.455a.675.675 0 0 1-.81-.81l.454-1.954zm16.832 1.769a10.8 10.8 0 0 1-3.066 1.27l.268 1.093a12 12 0 0 0 3.382-1.402zm-10.94.213-1.54.36.256 1.095 1.139-.266c.814.415 1.683.74 2.591.961l.268-1.094a10.8 10.8 0 0 1-2.35-.869zm3.634 1.24-.172 1.111a12.1 12.1 0 0 0 3.662 0l-.17-1.111q-.812.125-1.66.125a11 11 0 0 1-1.66-.125" },
|
|
29
|
+
"googlecalendar": { title: "Google Calendar", hex: "#4285F4", path: "M18.316 5.684H24v12.632h-5.684V5.684zM5.684 24h12.632v-5.684H5.684V24zM18.316 5.684V0H1.895A1.894 1.894 0 0 0 0 1.895v16.421h5.684V5.684h12.632zm-7.207 6.25v-.065c.272-.144.5-.349.687-.617s.279-.595.279-.982c0-.379-.099-.72-.3-1.025a2.05 2.05 0 0 0-.832-.714 2.703 2.703 0 0 0-1.197-.257c-.6 0-1.094.156-1.481.467-.386.311-.65.671-.793 1.078l1.085.452c.086-.249.224-.461.413-.633.189-.172.445-.257.767-.257.33 0 .602.088.816.264a.86.86 0 0 1 .322.703c0 .33-.12.589-.36.778-.24.19-.535.284-.886.284h-.567v1.085h.633c.407 0 .748.109 1.02.327.272.218.407.499.407.843 0 .336-.129.614-.387.832s-.565.327-.924.327c-.351 0-.651-.103-.897-.311-.248-.208-.422-.502-.521-.881l-1.096.452c.178.616.505 1.082.977 1.401.472.319.984.478 1.538.477a2.84 2.84 0 0 0 1.293-.291c.382-.193.684-.458.902-.794.218-.336.327-.72.327-1.149 0-.429-.115-.797-.344-1.105a2.067 2.067 0 0 0-.881-.689zm2.093-1.931l.602.913L15 10.045v5.744h1.187V8.446h-.827l-2.158 1.557zM22.105 0h-3.289v5.184H24V1.895A1.894 1.894 0 0 0 22.105 0zm-3.289 23.5l4.684-4.684h-4.684V23.5zM0 22.105C0 23.152.848 24 1.895 24h3.289v-5.184H0v3.289z" },
|
|
30
|
+
"stripe": { title: "Stripe", hex: "#635BFF", path: "M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z" },
|
|
31
|
+
"paypal": { title: "PayPal", hex: "#002991", path: "M15.607 4.653H8.941L6.645 19.251H1.82L4.862 0h7.995c3.754 0 6.375 2.294 6.473 5.513-.648-.478-2.105-.86-3.722-.86m6.57 5.546c0 3.41-3.01 6.853-6.958 6.853h-2.493L11.595 24H6.74l1.845-11.538h3.592c4.208 0 7.346-3.634 7.153-6.949a5.24 5.24 0 0 1 2.848 4.686M9.653 5.546h6.408c.907 0 1.942.222 2.363.541-.195 2.741-2.655 5.483-6.441 5.483H8.714Z" },
|
|
32
|
+
"applepay": { title: "Apple Pay", hex: "#000000", path: "M2.15 4.318a42.16 42.16 0 0 0-.454.003c-.15.005-.303.013-.452.04a1.44 1.44 0 0 0-1.06.772c-.07.138-.114.278-.14.43-.028.148-.037.3-.04.45A10.2 10.2 0 0 0 0 6.222v11.557c0 .07.002.138.003.207.004.15.013.303.04.452.027.15.072.291.142.429a1.436 1.436 0 0 0 .63.63c.138.07.278.115.43.142.148.027.3.036.45.04l.208.003h20.194l.207-.003c.15-.004.303-.013.452-.04.15-.027.291-.071.428-.141a1.432 1.432 0 0 0 .631-.631c.07-.138.115-.278.141-.43.027-.148.036-.3.04-.45.002-.07.003-.138.003-.208l.001-.246V6.221c0-.07-.002-.138-.004-.207a2.995 2.995 0 0 0-.04-.452 1.446 1.446 0 0 0-1.2-1.201 3.022 3.022 0 0 0-.452-.04 10.448 10.448 0 0 0-.453-.003zm0 .512h19.942c.066 0 .131.002.197.003.115.004.25.01.375.032.109.02.2.05.287.094a.927.927 0 0 1 .407.407.997.997 0 0 1 .094.288c.022.123.028.258.031.374.002.065.003.13.003.197v11.552c0 .065 0 .13-.003.196-.003.115-.009.25-.032.375a.927.927 0 0 1-.5.693 1.002 1.002 0 0 1-.286.094 2.598 2.598 0 0 1-.373.032l-.2.003H1.906c-.066 0-.133-.002-.196-.003a2.61 2.61 0 0 1-.375-.032c-.109-.02-.2-.05-.288-.094a.918.918 0 0 1-.406-.407 1.006 1.006 0 0 1-.094-.288 2.531 2.531 0 0 1-.032-.373 9.588 9.588 0 0 1-.002-.197V6.224c0-.065 0-.131.002-.197.004-.114.01-.248.032-.375.02-.108.05-.199.094-.287a.925.925 0 0 1 .407-.406 1.03 1.03 0 0 1 .287-.094c.125-.022.26-.029.375-.032.065-.002.131-.002.196-.003zm4.71 3.7c-.3.016-.668.199-.88.456-.191.22-.36.58-.316.918.338.03.675-.169.888-.418.205-.258.345-.603.308-.955zm2.207.42v5.493h.852v-1.877h1.18c1.078 0 1.835-.739 1.835-1.812 0-1.07-.742-1.805-1.808-1.805zm.852.719h.982c.739 0 1.161.396 1.161 1.089 0 .692-.422 1.092-1.164 1.092h-.979zm-3.154.3c-.45.01-.83.28-1.05.28-.235 0-.593-.264-.981-.257a1.446 1.446 0 0 0-1.23.747c-.527.908-.139 2.255.374 2.995.249.366.549.769.944.754.373-.014.52-.242.973-.242.454 0 .586.242.98.235.41-.007.667-.366.915-.733.286-.417.403-.82.41-.841-.007-.008-.79-.308-.797-1.209-.008-.754.615-1.113.644-1.135-.352-.52-.9-.578-1.09-.593a1.123 1.123 0 0 0-.092-.002zm8.204.397c-.99 0-1.606.533-1.652 1.256h.777c.072-.358.369-.586.845-.586.502 0 .803.266.803.711v.309l-1.097.064c-.951.054-1.488.484-1.488 1.184 0 .72.548 1.207 1.332 1.207.526 0 1.032-.281 1.264-.727h.019v.659h.788v-2.76c0-.803-.62-1.317-1.591-1.317zm1.94.072l1.446 4.009c0 .003-.073.24-.073.247-.125.41-.33.571-.711.571-.069 0-.206 0-.267-.015v.666c.06.011.267.019.335.019.83 0 1.226-.312 1.568-1.283l1.5-4.214h-.868l-1.012 3.259h-.015l-1.013-3.26zm-1.167 2.189v.316c0 .521-.45.917-1.024.917-.442 0-.731-.228-.731-.579 0-.342.278-.56.769-.593z" },
|
|
33
|
+
"shopify": { title: "Shopify", hex: "#7AB55C", path: "M15.337 23.979l7.216-1.561s-2.604-17.613-2.625-17.73c-.018-.116-.114-.192-.211-.192s-1.929-.136-1.929-.136-1.275-1.274-1.439-1.411c-.045-.037-.075-.057-.121-.074l-.914 21.104h.023zM11.71 11.305s-.81-.424-1.774-.424c-1.447 0-1.504.906-1.504 1.141 0 1.232 3.24 1.715 3.24 4.629 0 2.295-1.44 3.76-3.406 3.76-2.354 0-3.54-1.465-3.54-1.465l.646-2.086s1.245 1.066 2.28 1.066c.675 0 .975-.545.975-.932 0-1.619-2.654-1.694-2.654-4.359-.034-2.237 1.571-4.416 4.827-4.416 1.257 0 1.875.361 1.875.361l-.945 2.715-.02.01zM11.17.83c.136 0 .271.038.405.135-.984.465-2.064 1.639-2.508 3.992-.656.213-1.293.405-1.889.578C7.697 3.75 8.951.84 11.17.84V.83zm1.235 2.949v.135c-.754.232-1.583.484-2.394.736.466-1.777 1.333-2.645 2.085-2.971.193.501.309 1.176.309 2.1zm.539-2.234c.694.074 1.141.867 1.429 1.755-.349.114-.735.231-1.158.366v-.252c0-.752-.096-1.371-.271-1.871v.002zm2.992 1.289c-.02 0-.06.021-.078.021s-.289.075-.714.21c-.423-1.233-1.176-2.37-2.508-2.37h-.115C12.135.209 11.669 0 11.265 0 8.159 0 6.675 3.877 6.21 5.846c-1.194.365-2.063.636-2.16.674-.675.213-.694.232-.772.87-.075.462-1.83 14.063-1.83 14.063L15.009 24l.927-21.166z" },
|
|
34
|
+
"notion": { title: "Notion", hex: "#000000", path: "M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233 4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632z" },
|
|
35
|
+
"zoom": { title: "Zoom", hex: "#0B5CFF", path: "M5.033 14.649H.743a.74.74 0 0 1-.686-.458.74.74 0 0 1 .16-.808L3.19 10.41H1.06A1.06 1.06 0 0 1 0 9.35h3.957c.301 0 .57.18.686.458a.74.74 0 0 1-.161.808L1.51 13.59h2.464c.585 0 1.06.475 1.06 1.06zM24 11.338c0-1.14-.927-2.066-2.066-2.066-.61 0-1.158.265-1.537.686a2.061 2.061 0 0 0-1.536-.686c-1.14 0-2.066.926-2.066 2.066v3.311a1.06 1.06 0 0 0 1.06-1.06v-2.251a1.004 1.004 0 0 1 2.013 0v2.251c0 .586.474 1.06 1.06 1.06v-3.311a1.004 1.004 0 0 1 2.012 0v2.251c0 .586.475 1.06 1.06 1.06zM16.265 12a2.728 2.728 0 1 1-5.457 0 2.728 2.728 0 0 1 5.457 0zm-1.06 0a1.669 1.669 0 1 0-3.338 0 1.669 1.669 0 0 0 3.338 0zm-4.82 0a2.728 2.728 0 1 1-5.458 0 2.728 2.728 0 0 1 5.457 0zm-1.06 0a1.669 1.669 0 1 0-3.338 0 1.669 1.669 0 0 0 3.338 0z" },
|
|
36
|
+
} as const;
|
|
37
|
+
export type BrandName = keyof typeof BRAND_ICONS;
|