aigorithm's picture
Upload 2 files
3b2add9 verified
raw
history blame
4.25 kB
import { createElement as h, useState, useRef } from "https://esm.sh/react";
import { createRoot } from "https://esm.sh/react-dom/client";
import html2canvas from "https://esm.sh/html2canvas";
const App = () => {
const [characters, setCharacters] = useState([
{ id: 0, name: "User", avatar: "https://i.pravatar.cc/30?img=3" },
{ id: 1, name: "Bot", avatar: "https://i.pravatar.cc/30?img=7" }
]);
const [selectedChar, setSelectedChar] = useState(0);
const [messages, setMessages] = useState([]);
const [input, setInput] = useState("");
const [playing, setPlaying] = useState(false);
const chatRef = useRef(null);
const streamRef = useRef(null);
const addCharacter = () => {
const id = characters.length;
setCharacters([...characters, { id, name: "New", avatar: "" }]);
};
const handleAvatar = (id, file) => {
const url = URL.createObjectURL(file);
setCharacters(characters.map(c => c.id === id ? { ...c, avatar: url } : c));
};
const handleNameChange = (id, name) => {
setCharacters(characters.map(c => c.id === id ? { ...c, name } : c));
};
const addMessage = () => {
if (!input.trim()) return;
setMessages([...messages, { charId: selectedChar, text: input }]);
setInput("");
};
const playbackAndRecord = async () => {
setPlaying(true);
const container = chatRef.current;
const allMsgs = [...messages];
const tempContainer = container.cloneNode(false);
container.parentNode.appendChild(tempContainer);
const stream = tempContainer.captureStream(30);
streamRef.current = stream;
const recorder = new MediaRecorder(stream);
let chunks = [];
recorder.ondataavailable = (e) => chunks.push(e.data);
recorder.onstop = () => {
const blob = new Blob(chunks, { type: "video/webm" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "chat.mp4";
link.click();
tempContainer.remove();
setPlaying(false);
};
recorder.start();
for (let i = 0; i < allMsgs.length; i++) {
const msg = allMsgs[i];
const char = characters.find(c => c.id === msg.charId);
const msgDiv = document.createElement("div");
msgDiv.className = "message";
const avatar = document.createElement("img");
avatar.src = char.avatar;
const text = document.createElement("div");
text.className = "bubble";
text.textContent = msg.text;
msgDiv.appendChild(avatar);
msgDiv.appendChild(text);
tempContainer.appendChild(msgDiv);
await new Promise(r => setTimeout(r, 1500));
}
recorder.stop();
};
return h("div", { className: "app" }, [
h("h3", null, "Characters"),
...characters.map(char =>
h("div", { className: "character", key: char.id }, [
h("img", { src: char.avatar || "https://via.placeholder.com/30" }),
h("input", {
value: char.name,
onChange: (e) => handleNameChange(char.id, e.target.value)
}),
h("input", {
type: "file",
accept: "image/*",
onChange: (e) => handleAvatar(char.id, e.target.files[0])
})
])
),
h("button", { onClick: addCharacter }, "+ Add Character"),
h("div", { className: "controls" }, [
h("select", {
value: selectedChar,
onChange: (e) => setSelectedChar(parseInt(e.target.value))
}, characters.map(c => h("option", { value: c.id, key: c.id }, c.name))),
h("input", {
value: input,
onChange: (e) => setInput(e.target.value),
placeholder: "Type message..."
}),
h("button", { onClick: addMessage }, "Send Message")
]),
h("div", { className: "chat", ref: chatRef }, messages.map((msg, i) => {
const char = characters.find(c => c.id === msg.charId);
return h("div", { className: "message", key: i }, [
h("img", { src: char?.avatar }),
h("div", { className: "bubble" }, msg.text)
]);
})),
h("button", {
onClick: playbackAndRecord,
disabled: playing,
style: { marginTop: 20 }
}, playing ? "Recording..." : "🎬 Export Video")
]);
};
createRoot(document.getElementById("root")).render(h(App));