feat(saas): full SquareMCP SaaS platform v1
- JWT auth with bcrypt password hashing, cookie sessions, forgot/reset password - Per-user encrypted credential storage (Redis + AES-256-GCM) for all 9 platforms - Usage tracking with monthly limits per plan (free/starter/growth/enterprise) - Invoice generation and retrieval (admin + user views) - Admin panel with customer listing (role-based access) - Web app UI at app.squaremcp.com — login, dashboard, connections, usage, invoices - Unified auth middleware: API key, OAuth Bearer, and JWT cookie support - Facebook Graph API fixes: published_posts endpoint, photo/video post support - TikTok sandbox compliance: SELF_ONLY privacy for unaudited apps - URL verification files for TikTok app review
This commit is contained in:
@@ -2,6 +2,14 @@ import "./index.css";
|
||||
import { Composition } from "remotion";
|
||||
import { SquareMCPLinkedIn } from "./SquareMCPLinkedIn";
|
||||
import { SquareMCPHeroLoop } from "./SquareMCPHeroLoop";
|
||||
import {
|
||||
SquareMCPTikTokCTA,
|
||||
SquareMCPTikTokDemo,
|
||||
SquareMCPTikTokFull,
|
||||
SquareMCPTikTokHook,
|
||||
SquareMCPTikTokProblem,
|
||||
SquareMCPTikTokProof,
|
||||
} from "./SquareMCPTikTok";
|
||||
|
||||
export const RemotionRoot = () => {
|
||||
return (
|
||||
@@ -22,6 +30,54 @@ export const RemotionRoot = () => {
|
||||
width={1920}
|
||||
height={1080}
|
||||
/>
|
||||
<Composition
|
||||
id="SquareMCPTikTokFull"
|
||||
component={SquareMCPTikTokFull}
|
||||
durationInFrames={30 * 30}
|
||||
fps={30}
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
<Composition
|
||||
id="SquareMCPTikTokHook"
|
||||
component={SquareMCPTikTokHook}
|
||||
durationInFrames={3 * 30}
|
||||
fps={30}
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
<Composition
|
||||
id="SquareMCPTikTokProblem"
|
||||
component={SquareMCPTikTokProblem}
|
||||
durationInFrames={5 * 30}
|
||||
fps={30}
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
<Composition
|
||||
id="SquareMCPTikTokDemo"
|
||||
component={SquareMCPTikTokDemo}
|
||||
durationInFrames={12 * 30}
|
||||
fps={30}
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
<Composition
|
||||
id="SquareMCPTikTokProof"
|
||||
component={SquareMCPTikTokProof}
|
||||
durationInFrames={5 * 30}
|
||||
fps={30}
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
<Composition
|
||||
id="SquareMCPTikTokCTA"
|
||||
component={SquareMCPTikTokCTA}
|
||||
durationInFrames={5 * 30}
|
||||
fps={30}
|
||||
width={1080}
|
||||
height={1920}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
81
videos/remotion-demo/src/SquareMCPTikTok.tsx
Normal file
81
videos/remotion-demo/src/SquareMCPTikTok.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { AbsoluteFill, Sequence, useVideoConfig } from "remotion";
|
||||
import { TikTokBackground } from "./scenes/tiktok/TikTokBackground";
|
||||
import { TikTokCTA } from "./scenes/tiktok/TikTokCTA";
|
||||
import { TikTokDemo } from "./scenes/tiktok/TikTokDemo";
|
||||
import { TikTokHook } from "./scenes/tiktok/TikTokHook";
|
||||
import { TikTokProblem } from "./scenes/tiktok/TikTokProblem";
|
||||
import { TikTokProof } from "./scenes/tiktok/TikTokProof";
|
||||
|
||||
const TikTokShell = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
<TikTokBackground />
|
||||
{children}
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
|
||||
export const SquareMCPTikTokFull = () => {
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
return (
|
||||
<TikTokShell>
|
||||
<Sequence durationInFrames={3 * fps}>
|
||||
<TikTokHook />
|
||||
</Sequence>
|
||||
<Sequence from={3 * fps} durationInFrames={5 * fps}>
|
||||
<TikTokProblem />
|
||||
</Sequence>
|
||||
<Sequence from={8 * fps} durationInFrames={12 * fps}>
|
||||
<TikTokDemo />
|
||||
</Sequence>
|
||||
<Sequence from={20 * fps} durationInFrames={5 * fps}>
|
||||
<TikTokProof />
|
||||
</Sequence>
|
||||
<Sequence from={25 * fps} durationInFrames={5 * fps}>
|
||||
<TikTokCTA />
|
||||
</Sequence>
|
||||
</TikTokShell>
|
||||
);
|
||||
};
|
||||
|
||||
export const SquareMCPTikTokHook = () => {
|
||||
return (
|
||||
<TikTokShell>
|
||||
<TikTokHook />
|
||||
</TikTokShell>
|
||||
);
|
||||
};
|
||||
|
||||
export const SquareMCPTikTokProblem = () => {
|
||||
return (
|
||||
<TikTokShell>
|
||||
<TikTokProblem />
|
||||
</TikTokShell>
|
||||
);
|
||||
};
|
||||
|
||||
export const SquareMCPTikTokDemo = () => {
|
||||
return (
|
||||
<TikTokShell>
|
||||
<TikTokDemo />
|
||||
</TikTokShell>
|
||||
);
|
||||
};
|
||||
|
||||
export const SquareMCPTikTokProof = () => {
|
||||
return (
|
||||
<TikTokShell>
|
||||
<TikTokProof />
|
||||
</TikTokShell>
|
||||
);
|
||||
};
|
||||
|
||||
export const SquareMCPTikTokCTA = () => {
|
||||
return (
|
||||
<TikTokShell>
|
||||
<TikTokCTA />
|
||||
</TikTokShell>
|
||||
);
|
||||
};
|
||||
26
videos/remotion-demo/src/scenes/tiktok/TikTokBackground.tsx
Normal file
26
videos/remotion-demo/src/scenes/tiktok/TikTokBackground.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion";
|
||||
import { COLORS } from "../../styles";
|
||||
|
||||
export const TikTokBackground = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const orbit = interpolate(frame % 180, [0, 90, 180], [0, 1, 0]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill
|
||||
style={{
|
||||
background: `radial-gradient(circle at ${20 + orbit * 45}% ${18 + orbit * 30}%, rgba(125, 182, 255, 0.18), transparent 28%), radial-gradient(circle at 80% 78%, rgba(14, 99, 246, 0.22), transparent 30%), linear-gradient(180deg, #070812 0%, ${COLORS.bg} 100%)`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
backgroundImage:
|
||||
"linear-gradient(rgba(255,255,255,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.04) 1px, transparent 1px)",
|
||||
backgroundSize: "60px 60px",
|
||||
maskImage: "linear-gradient(180deg, rgba(0,0,0,0.65), rgba(0,0,0,0.95))",
|
||||
}}
|
||||
/>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
69
videos/remotion-demo/src/scenes/tiktok/TikTokCTA.tsx
Normal file
69
videos/remotion-demo/src/scenes/tiktok/TikTokCTA.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { AbsoluteFill, Img, interpolate, spring, staticFile, useCurrentFrame, useVideoConfig } from "remotion";
|
||||
import { COLORS, FONT, SPRING_CFG } from "../../styles";
|
||||
|
||||
export const TikTokCTA = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
const logoIn = spring({ fps, frame, config: SPRING_CFG });
|
||||
const glow = interpolate(frame % 60, [0, 30, 60], [0.32, 0.7, 0.32]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: 520,
|
||||
height: 520,
|
||||
borderRadius: 999,
|
||||
background: `radial-gradient(circle, rgba(14,99,246,${glow}) 0%, rgba(14,99,246,0) 68%)`,
|
||||
filter: "blur(12px)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
opacity: logoIn,
|
||||
transform: `scale(${0.9 + logoIn * 0.1}) translateY(${(1 - logoIn) * 50}px)`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Img src={staticFile("squaremcp-logo.svg")} style={{ width: 240, height: 240 }} />
|
||||
<div
|
||||
style={{
|
||||
marginTop: 26,
|
||||
fontFamily: FONT,
|
||||
fontSize: 82,
|
||||
fontWeight: 800,
|
||||
color: COLORS.text,
|
||||
letterSpacing: -2.2,
|
||||
}}
|
||||
>
|
||||
SquareMCP
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 12,
|
||||
fontFamily: FONT,
|
||||
fontSize: 34,
|
||||
fontWeight: 600,
|
||||
color: COLORS.accentLight,
|
||||
letterSpacing: -0.6,
|
||||
}}
|
||||
>
|
||||
squaremcp.com
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 22,
|
||||
fontFamily: FONT,
|
||||
fontSize: 24,
|
||||
color: COLORS.textSecondary,
|
||||
}}
|
||||
>
|
||||
Early access now open
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
124
videos/remotion-demo/src/scenes/tiktok/TikTokDemo.tsx
Normal file
124
videos/remotion-demo/src/scenes/tiktok/TikTokDemo.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
||||
import { COLORS, FONT, SPRING_CFG } from "../../styles";
|
||||
|
||||
export const TikTokDemo = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
const chatIn = spring({ fps, frame, config: SPRING_CFG });
|
||||
const jsonIn = spring({ fps, frame: Math.max(0, frame - 30), config: SPRING_CFG });
|
||||
const chipsIn = spring({ fps, frame: Math.max(0, frame - 70), config: SPRING_CFG });
|
||||
const pulse = interpolate(frame % 45, [0, 22, 45], [0.75, 1, 0.75]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ padding: 58 }}>
|
||||
<div style={{ fontFamily: FONT, color: COLORS.text, fontSize: 68, fontWeight: 800, lineHeight: 1, marginTop: 48 }}>
|
||||
One prompt.
|
||||
<br />
|
||||
Real execution.
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: 42,
|
||||
borderRadius: 36,
|
||||
border: `1px solid ${COLORS.borderBlue}`,
|
||||
background: "rgba(9, 12, 22, 0.94)",
|
||||
padding: 30,
|
||||
opacity: chatIn,
|
||||
transform: `translateY(${(1 - chatIn) * 60}px)`,
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: FONT, color: COLORS.textSecondary, fontSize: 22, marginBottom: 18 }}>Chat</div>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
maxWidth: 760,
|
||||
borderRadius: 28,
|
||||
background: "linear-gradient(135deg, rgba(14,99,246,0.95), rgba(125,182,255,0.9))",
|
||||
padding: "24px 26px",
|
||||
color: "#08111f",
|
||||
fontFamily: FONT,
|
||||
fontSize: 34,
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.25,
|
||||
}}
|
||||
>
|
||||
Post my launch video to LinkedIn, X, and Instagram.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: 28,
|
||||
borderRadius: 36,
|
||||
border: "1px solid rgba(34, 197, 94, 0.24)",
|
||||
background: "rgba(5, 14, 10, 0.9)",
|
||||
padding: 30,
|
||||
opacity: jsonIn,
|
||||
transform: `translateY(${(1 - jsonIn) * 60}px)`,
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "#86efac", fontSize: 22, marginBottom: 18 }}>
|
||||
response.json
|
||||
</div>
|
||||
{[
|
||||
'{',
|
||||
' "status": "success",',
|
||||
' "published": ["linkedin", "x", "instagram"],',
|
||||
' "duration_seconds": 12,',
|
||||
' "urls": 3',
|
||||
'}',
|
||||
].map((line) => (
|
||||
<div
|
||||
key={line}
|
||||
style={{
|
||||
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
||||
color: line.includes("success") ? "#86efac" : "#dbeafe",
|
||||
fontSize: 24,
|
||||
lineHeight: 1.55,
|
||||
}}
|
||||
>
|
||||
{line}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: 28,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr",
|
||||
gap: 16,
|
||||
opacity: chipsIn,
|
||||
transform: `scale(${0.96 + chipsIn * 0.04})`,
|
||||
}}
|
||||
>
|
||||
{["linkedin.com/post/7459...", "x.com/squaremcp/status/...", "instagram.com/reel/C..."].map((url) => (
|
||||
<div
|
||||
key={url}
|
||||
style={{
|
||||
borderRadius: 22,
|
||||
padding: "18px 22px",
|
||||
background: "rgba(255,255,255,0.06)",
|
||||
border: `1px solid ${COLORS.borderBlue}`,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 16,
|
||||
boxShadow: `0 0 24px rgba(34, 197, 94, ${0.12 * pulse})`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 18,
|
||||
height: 18,
|
||||
borderRadius: 999,
|
||||
background: "#22c55e",
|
||||
}}
|
||||
/>
|
||||
<div style={{ fontFamily: FONT, color: COLORS.text, fontSize: 24, fontWeight: 600 }}>{url}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
113
videos/remotion-demo/src/scenes/tiktok/TikTokHook.tsx
Normal file
113
videos/remotion-demo/src/scenes/tiktok/TikTokHook.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
||||
import { COLORS, FONT, SPRING_CFG } from "../../styles";
|
||||
|
||||
const TOOL_LABELS = [
|
||||
"Email",
|
||||
"Notes",
|
||||
"CRM",
|
||||
"LinkedIn",
|
||||
"X",
|
||||
"WhatsApp",
|
||||
"Docs",
|
||||
"Sheets",
|
||||
"Tickets",
|
||||
"Calendar",
|
||||
"DB",
|
||||
"API",
|
||||
];
|
||||
|
||||
export const TikTokHook = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
const terminalIn = spring({ fps, frame, config: SPRING_CFG });
|
||||
const gridIn = spring({ fps, frame: Math.max(0, frame - 18), config: SPRING_CFG });
|
||||
const captionIn = interpolate(frame, [20, 34], [0, 1], { extrapolateRight: "clamp" });
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ padding: 72 }}>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 120,
|
||||
borderRadius: 32,
|
||||
border: `1px solid ${COLORS.borderBlue}`,
|
||||
background: "rgba(7, 8, 18, 0.92)",
|
||||
boxShadow: `0 20px 60px ${COLORS.accentGlow}`,
|
||||
padding: "28px 32px",
|
||||
opacity: terminalIn,
|
||||
transform: `translateY(${(1 - terminalIn) * 80}px)`,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", gap: 12, marginBottom: 26 }}>
|
||||
{["#ff5f57", "#febc2e", "#28c840"].map((dot) => (
|
||||
<div key={dot} style={{ width: 14, height: 14, borderRadius: 999, background: dot }} />
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
||||
fontWeight: 700,
|
||||
fontSize: 44,
|
||||
color: COLORS.accentLight,
|
||||
letterSpacing: -1.2,
|
||||
}}
|
||||
>
|
||||
{"> connect squaremcp"}
|
||||
<span style={{ color: COLORS.text, opacity: (frame % 20) < 10 ? 1 : 0 }}>|</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: 48,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
gap: 18,
|
||||
opacity: gridIn,
|
||||
transform: `scale(${0.9 + gridIn * 0.1})`,
|
||||
}}
|
||||
>
|
||||
{TOOL_LABELS.map((label, index) => (
|
||||
<div
|
||||
key={label}
|
||||
style={{
|
||||
borderRadius: 24,
|
||||
border: `1px solid ${COLORS.borderBlue}`,
|
||||
background: index % 2 === 0 ? "rgba(18, 18, 31, 0.95)" : "rgba(10, 17, 38, 0.92)",
|
||||
padding: "26px 16px",
|
||||
minHeight: 112,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: COLORS.text,
|
||||
fontFamily: FONT,
|
||||
fontWeight: 700,
|
||||
fontSize: 28,
|
||||
boxShadow: "inset 0 0 0 1px rgba(255,255,255,0.03)",
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "auto",
|
||||
marginBottom: 68,
|
||||
fontFamily: FONT,
|
||||
fontSize: 54,
|
||||
fontWeight: 800,
|
||||
color: COLORS.text,
|
||||
lineHeight: 1.05,
|
||||
letterSpacing: -1.8,
|
||||
opacity: captionIn,
|
||||
transform: `translateY(${(1 - captionIn) * 40}px)`,
|
||||
}}
|
||||
>
|
||||
One API key.
|
||||
<br />
|
||||
One workflow layer.
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
131
videos/remotion-demo/src/scenes/tiktok/TikTokProblem.tsx
Normal file
131
videos/remotion-demo/src/scenes/tiktok/TikTokProblem.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
||||
import { COLORS, FONT, SPRING_CFG } from "../../styles";
|
||||
|
||||
const LEFT_APPS = ["Email", "LinkedIn", "Slack", "Notes", "Sheets", "Docs"];
|
||||
|
||||
export const TikTokProblem = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
const leftIn = spring({ fps, frame, config: SPRING_CFG });
|
||||
const rightIn = spring({ fps, frame: Math.max(0, frame - 12), config: SPRING_CFG });
|
||||
const divider = interpolate(frame, [4, 24], [0.25, 1], { extrapolateRight: "clamp" });
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ padding: 60, flexDirection: "column" }}>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: FONT,
|
||||
fontSize: 70,
|
||||
fontWeight: 800,
|
||||
color: COLORS.text,
|
||||
letterSpacing: -2,
|
||||
lineHeight: 1,
|
||||
marginTop: 44,
|
||||
marginBottom: 42,
|
||||
}}
|
||||
>
|
||||
8 tabs vs
|
||||
<br />
|
||||
1 gateway
|
||||
</div>
|
||||
<div style={{ display: "flex", flex: 1, gap: 28 }}>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
opacity: leftIn,
|
||||
transform: `translateX(${(1 - leftIn) * -80}px) rotate(${-4 + (1 - leftIn) * -8}deg)`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 32,
|
||||
border: `1px solid rgba(239, 68, 68, 0.4)`,
|
||||
background: "rgba(35, 10, 16, 0.88)",
|
||||
padding: 28,
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: FONT, color: "#fca5a5", fontSize: 30, fontWeight: 700, marginBottom: 20 }}>
|
||||
Before
|
||||
</div>
|
||||
<div style={{ display: "grid", gap: 16 }}>
|
||||
{LEFT_APPS.map((app, index) => (
|
||||
<div
|
||||
key={app}
|
||||
style={{
|
||||
borderRadius: 20,
|
||||
padding: "18px 20px",
|
||||
background: `rgba(255,255,255,${0.06 + index * 0.01})`,
|
||||
border: "1px solid rgba(255,255,255,0.06)",
|
||||
fontFamily: FONT,
|
||||
color: COLORS.text,
|
||||
fontSize: 28,
|
||||
fontWeight: 600,
|
||||
transform: `translateX(${index % 2 === 0 ? -12 : 18}px)`,
|
||||
}}
|
||||
>
|
||||
{app}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: 6,
|
||||
borderRadius: 999,
|
||||
background: `linear-gradient(180deg, rgba(255,255,255,0.05), rgba(14, 99, 246, ${divider}), rgba(255,255,255,0.05))`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
opacity: rightIn,
|
||||
transform: `translateX(${(1 - rightIn) * 80}px)`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 32,
|
||||
border: `1px solid ${COLORS.borderBlue}`,
|
||||
background: "rgba(9, 16, 36, 0.92)",
|
||||
padding: 28,
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
gap: 20,
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: FONT, color: COLORS.accentLight, fontSize: 30, fontWeight: 700 }}>
|
||||
After
|
||||
</div>
|
||||
{["AI agent", "SquareMCP", "Connected tools"].map((item, index) => (
|
||||
<div
|
||||
key={item}
|
||||
style={{
|
||||
borderRadius: 22,
|
||||
padding: "24px 22px",
|
||||
background: index === 1 ? "rgba(14, 99, 246, 0.22)" : "rgba(255,255,255,0.05)",
|
||||
border: `1px solid ${index === 1 ? COLORS.borderBlue : "rgba(255,255,255,0.08)"}`,
|
||||
fontFamily: FONT,
|
||||
color: COLORS.text,
|
||||
fontSize: 30,
|
||||
fontWeight: index === 1 ? 800 : 600,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
<div style={{ fontFamily: FONT, color: COLORS.textSecondary, fontSize: 24, lineHeight: 1.35 }}>
|
||||
One entry point for auth, permissions, posting, notes, and internal systems.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
58
videos/remotion-demo/src/scenes/tiktok/TikTokProof.tsx
Normal file
58
videos/remotion-demo/src/scenes/tiktok/TikTokProof.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { AbsoluteFill, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
||||
import { COLORS, FONT, SPRING_CFG } from "../../styles";
|
||||
|
||||
const CHANNELS = [
|
||||
{ name: "LinkedIn", time: "12:04:03", tint: "rgba(10, 102, 194, 0.28)" },
|
||||
{ name: "X", time: "12:04:05", tint: "rgba(255, 255, 255, 0.08)" },
|
||||
{ name: "Instagram", time: "12:04:07", tint: "rgba(225, 48, 108, 0.24)" },
|
||||
{ name: "TikTok", time: "12:04:09", tint: "rgba(37, 244, 238, 0.18)" },
|
||||
];
|
||||
|
||||
export const TikTokProof = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
const inView = spring({ fps, frame, config: SPRING_CFG });
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ padding: 60 }}>
|
||||
<div style={{ fontFamily: FONT, fontSize: 62, fontWeight: 800, color: COLORS.text, letterSpacing: -1.8, marginTop: 50 }}>
|
||||
Publish once.
|
||||
<br />
|
||||
Watch it land.
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 52,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
gap: 22,
|
||||
opacity: inView,
|
||||
transform: `scale(${0.92 + inView * 0.08})`,
|
||||
}}
|
||||
>
|
||||
{CHANNELS.map((channel, index) => (
|
||||
<div
|
||||
key={channel.name}
|
||||
style={{
|
||||
borderRadius: 30,
|
||||
border: `1px solid ${COLORS.borderBlue}`,
|
||||
background: channel.tint,
|
||||
minHeight: 260,
|
||||
padding: 24,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
transform: `translateY(${index % 2 === 0 ? -8 : 12}px)`,
|
||||
}}
|
||||
>
|
||||
<div style={{ fontFamily: FONT, color: COLORS.text, fontSize: 30, fontWeight: 700 }}>{channel.name}</div>
|
||||
<div style={{ fontFamily: FONT, color: "#86efac", fontSize: 26, fontWeight: 800 }}>LIVE</div>
|
||||
<div style={{ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: COLORS.textSecondary, fontSize: 22 }}>
|
||||
{channel.time}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user