Spaces:
Running
Running
Upload 2 files
Browse files- index.html +55 -41
- main.js +60 -22
index.html
CHANGED
@@ -3,27 +3,30 @@
|
|
3 |
<html lang="en">
|
4 |
<head>
|
5 |
<meta charset="UTF-8" />
|
6 |
-
<title>
|
7 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
8 |
<style>
|
9 |
body {
|
10 |
margin: 0;
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
display: flex;
|
15 |
flex-direction: column;
|
16 |
align-items: center;
|
17 |
}
|
18 |
-
|
|
|
|
|
|
|
19 |
width: 360px;
|
20 |
-
height:
|
21 |
background: #f0f2f5;
|
22 |
-
border-radius:
|
|
|
23 |
overflow-y: auto;
|
24 |
-
box-shadow: 0 0 12px rgba(0,0,0,0.5);
|
25 |
-
margin: 20px 0;
|
26 |
padding: 10px;
|
|
|
27 |
}
|
28 |
.message {
|
29 |
display: flex;
|
@@ -37,73 +40,84 @@
|
|
37 |
border-radius: 18px;
|
38 |
max-width: 70%;
|
39 |
font-size: 14px;
|
40 |
-
line-height: 1.4;
|
41 |
-
opacity: 1;
|
42 |
}
|
43 |
-
.left .bubble {
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
.avatar {
|
46 |
width: 28px;
|
47 |
height: 28px;
|
48 |
border-radius: 50%;
|
49 |
margin: 0 6px;
|
50 |
}
|
51 |
-
.
|
52 |
width: 100%;
|
53 |
max-width: 420px;
|
54 |
-
|
|
|
|
|
55 |
padding: 10px;
|
56 |
-
border-radius: 8px;
|
57 |
-
box-shadow: 0 0 8px rgba(255,255,255,0.1);
|
58 |
}
|
59 |
-
.msg-
|
60 |
-
background: #
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
display: flex;
|
65 |
flex-direction: column;
|
66 |
-
gap:
|
|
|
67 |
}
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
border-radius:
|
72 |
-
border:
|
73 |
-
background: #222;
|
74 |
-
color: white;
|
75 |
}
|
76 |
button {
|
77 |
-
margin-top: 10px;
|
78 |
-
padding: 8px 14px;
|
79 |
background: #007aff;
|
80 |
border: none;
|
81 |
border-radius: 6px;
|
82 |
color: white;
|
|
|
|
|
83 |
cursor: pointer;
|
84 |
}
|
85 |
-
.
|
86 |
display: flex;
|
87 |
-
|
|
|
88 |
width: 100%;
|
89 |
max-width: 420px;
|
|
|
|
|
|
|
|
|
|
|
90 |
}
|
91 |
</style>
|
92 |
</head>
|
93 |
<body>
|
94 |
-
<h2>📱 Fake Chat
|
95 |
-
<div class="
|
96 |
-
<select id="styleSelect" onchange="
|
97 |
<option value="imessage">iMessage</option>
|
98 |
<option value="whatsapp">WhatsApp</option>
|
99 |
<option value="instagram">Instagram</option>
|
100 |
<option value="reddit">Reddit</option>
|
101 |
</select>
|
102 |
-
<
|
|
|
|
|
|
|
103 |
</div>
|
104 |
-
<div class="
|
105 |
-
<div class="
|
106 |
-
<button onclick="exportJSON()">📤 Export JSON</button>
|
107 |
<script type="module" src="main.js"></script>
|
108 |
</body>
|
109 |
</html>
|
|
|
3 |
<html lang="en">
|
4 |
<head>
|
5 |
<meta charset="UTF-8" />
|
6 |
+
<title>Fake Chat Studio - AiCut Pro</title>
|
7 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
8 |
<style>
|
9 |
body {
|
10 |
margin: 0;
|
11 |
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
12 |
+
background: #f9f9f9;
|
13 |
+
color: #111;
|
14 |
display: flex;
|
15 |
flex-direction: column;
|
16 |
align-items: center;
|
17 |
}
|
18 |
+
h2 {
|
19 |
+
margin-top: 16px;
|
20 |
+
}
|
21 |
+
.chat-preview {
|
22 |
width: 360px;
|
23 |
+
height: 600px;
|
24 |
background: #f0f2f5;
|
25 |
+
border-radius: 24px;
|
26 |
+
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
27 |
overflow-y: auto;
|
|
|
|
|
28 |
padding: 10px;
|
29 |
+
margin: 12px 0;
|
30 |
}
|
31 |
.message {
|
32 |
display: flex;
|
|
|
40 |
border-radius: 18px;
|
41 |
max-width: 70%;
|
42 |
font-size: 14px;
|
|
|
|
|
43 |
}
|
44 |
+
.left .bubble {
|
45 |
+
background: #e5e5ea;
|
46 |
+
color: black;
|
47 |
+
}
|
48 |
+
.right .bubble {
|
49 |
+
background: #007aff;
|
50 |
+
color: white;
|
51 |
+
}
|
52 |
.avatar {
|
53 |
width: 28px;
|
54 |
height: 28px;
|
55 |
border-radius: 50%;
|
56 |
margin: 0 6px;
|
57 |
}
|
58 |
+
.editor {
|
59 |
width: 100%;
|
60 |
max-width: 420px;
|
61 |
+
display: flex;
|
62 |
+
flex-direction: column;
|
63 |
+
gap: 10px;
|
64 |
padding: 10px;
|
|
|
|
|
65 |
}
|
66 |
+
.msg-editor {
|
67 |
+
background: #fff;
|
68 |
+
border: 1px solid #ddd;
|
69 |
+
border-radius: 12px;
|
70 |
+
padding: 12px;
|
71 |
display: flex;
|
72 |
flex-direction: column;
|
73 |
+
gap: 6px;
|
74 |
+
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
|
75 |
}
|
76 |
+
.msg-editor input, .msg-editor select {
|
77 |
+
padding: 8px;
|
78 |
+
font-size: 14px;
|
79 |
+
border-radius: 6px;
|
80 |
+
border: 1px solid #ccc;
|
|
|
|
|
81 |
}
|
82 |
button {
|
|
|
|
|
83 |
background: #007aff;
|
84 |
border: none;
|
85 |
border-radius: 6px;
|
86 |
color: white;
|
87 |
+
padding: 10px;
|
88 |
+
font-size: 14px;
|
89 |
cursor: pointer;
|
90 |
}
|
91 |
+
.controls {
|
92 |
display: flex;
|
93 |
+
flex-wrap: wrap;
|
94 |
+
gap: 10px;
|
95 |
width: 100%;
|
96 |
max-width: 420px;
|
97 |
+
justify-content: space-between;
|
98 |
+
padding: 0 10px;
|
99 |
+
}
|
100 |
+
.controls input[type="text"] {
|
101 |
+
flex: 1;
|
102 |
}
|
103 |
</style>
|
104 |
</head>
|
105 |
<body>
|
106 |
+
<h2>📱 Fake Chat Studio</h2>
|
107 |
+
<div class="controls">
|
108 |
+
<select id="styleSelect" onchange="renderPreview()">
|
109 |
<option value="imessage">iMessage</option>
|
110 |
<option value="whatsapp">WhatsApp</option>
|
111 |
<option value="instagram">Instagram</option>
|
112 |
<option value="reddit">Reddit</option>
|
113 |
</select>
|
114 |
+
<input type="text" id="prompt" placeholder="Generate chat: funny convo about aliens">
|
115 |
+
<button onclick="generateAI()">🤖 Generate</button>
|
116 |
+
<button onclick="addMessage()">+ Add</button>
|
117 |
+
<button onclick="renderAndExport()">🎬 Export Video</button>
|
118 |
</div>
|
119 |
+
<div class="chat-preview" id="chatPreview"></div>
|
120 |
+
<div class="editor" id="editor"></div>
|
|
|
121 |
<script type="module" src="main.js"></script>
|
122 |
</body>
|
123 |
</html>
|
main.js
CHANGED
@@ -5,15 +5,17 @@ let messages = [
|
|
5 |
];
|
6 |
|
7 |
const preview = document.getElementById("chatPreview");
|
8 |
-
const
|
9 |
const styles = {
|
10 |
imessage: { left: "#e5e5ea", right: "#007aff" },
|
11 |
whatsapp: { left: "#dcf8c6", right: "#34b7f1" },
|
12 |
instagram: { left: "#fff0f5", right: "#ffb6c1" },
|
13 |
reddit: { left: "#f6f7f8", right: "#ccc" }
|
14 |
};
|
|
|
|
|
15 |
|
16 |
-
function
|
17 |
const style = document.getElementById("styleSelect").value;
|
18 |
preview.innerHTML = "";
|
19 |
messages.forEach(msg => {
|
@@ -32,43 +34,79 @@ function updatePreview() {
|
|
32 |
});
|
33 |
}
|
34 |
|
35 |
-
function
|
36 |
-
|
37 |
messages.forEach((msg, i) => {
|
38 |
const row = document.createElement("div");
|
39 |
-
row.className = "msg-
|
40 |
row.innerHTML = `
|
41 |
-
<input type="text" value="${msg.name}" placeholder="Name" onchange="messages[${i}].name
|
42 |
-
<input type="text" value="${msg.avatar}" placeholder="Avatar URL" onchange="messages[${i}].avatar
|
43 |
-
<select onchange="messages[${i}].voice
|
44 |
<option ${msg.voice==="Rachel"?"selected":""}>Rachel</option>
|
45 |
<option ${msg.voice==="Adam"?"selected":""}>Adam</option>
|
46 |
<option ${msg.voice==="Bella"?"selected":""}>Bella</option>
|
47 |
</select>
|
48 |
-
<select onchange="messages[${i}].side
|
49 |
<option value="left" ${msg.side==="left"?"selected":""}>Left</option>
|
50 |
<option value="right" ${msg.side==="right"?"selected":""}>Right</option>
|
51 |
</select>
|
52 |
-
<input type="text" value="${msg.text}" placeholder="Message" onchange="messages[${i}].text
|
53 |
`;
|
54 |
-
|
55 |
});
|
56 |
-
|
57 |
}
|
58 |
|
59 |
function addMessage() {
|
60 |
messages.push({ name: "New", side: "left", avatar: "https://i.pravatar.cc/30", voice: "Rachel", text: "..." });
|
61 |
-
|
62 |
}
|
63 |
|
64 |
-
function
|
65 |
-
const
|
66 |
-
const
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
}
|
73 |
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
];
|
6 |
|
7 |
const preview = document.getElementById("chatPreview");
|
8 |
+
const editor = document.getElementById("editor");
|
9 |
const styles = {
|
10 |
imessage: { left: "#e5e5ea", right: "#007aff" },
|
11 |
whatsapp: { left: "#dcf8c6", right: "#34b7f1" },
|
12 |
instagram: { left: "#fff0f5", right: "#ffb6c1" },
|
13 |
reddit: { left: "#f6f7f8", right: "#ccc" }
|
14 |
};
|
15 |
+
const apiKey = "sk_4e67c39c0e9cbc87462cd2643e1f4d1d9959d7d81203adc2";
|
16 |
+
const voiceIdMap = { Rachel: "21m00Tcm4TlvDq8ikWAM", Adam: "pNInz6obpgDQGcFmaJgB", Bella: "EXAVITQu4vr4xnSDxMaL" };
|
17 |
|
18 |
+
function renderPreview() {
|
19 |
const style = document.getElementById("styleSelect").value;
|
20 |
preview.innerHTML = "";
|
21 |
messages.forEach(msg => {
|
|
|
34 |
});
|
35 |
}
|
36 |
|
37 |
+
function renderEditor() {
|
38 |
+
editor.innerHTML = "";
|
39 |
messages.forEach((msg, i) => {
|
40 |
const row = document.createElement("div");
|
41 |
+
row.className = "msg-editor";
|
42 |
row.innerHTML = `
|
43 |
+
<input type="text" value="${msg.name}" placeholder="Name" onchange="messages[${i}].name=this.value">
|
44 |
+
<input type="text" value="${msg.avatar}" placeholder="Avatar URL" onchange="messages[${i}].avatar=this.value">
|
45 |
+
<select onchange="messages[${i}].voice=this.value">
|
46 |
<option ${msg.voice==="Rachel"?"selected":""}>Rachel</option>
|
47 |
<option ${msg.voice==="Adam"?"selected":""}>Adam</option>
|
48 |
<option ${msg.voice==="Bella"?"selected":""}>Bella</option>
|
49 |
</select>
|
50 |
+
<select onchange="messages[${i}].side=this.value">
|
51 |
<option value="left" ${msg.side==="left"?"selected":""}>Left</option>
|
52 |
<option value="right" ${msg.side==="right"?"selected":""}>Right</option>
|
53 |
</select>
|
54 |
+
<input type="text" value="${msg.text}" placeholder="Message" onchange="messages[${i}].text=this.value">
|
55 |
`;
|
56 |
+
editor.appendChild(row);
|
57 |
});
|
58 |
+
renderPreview();
|
59 |
}
|
60 |
|
61 |
function addMessage() {
|
62 |
messages.push({ name: "New", side: "left", avatar: "https://i.pravatar.cc/30", voice: "Rachel", text: "..." });
|
63 |
+
renderEditor();
|
64 |
}
|
65 |
|
66 |
+
async function getVoiceBlob(text, voice) {
|
67 |
+
const voiceId = voiceIdMap[voice] || voiceIdMap.Rachel;
|
68 |
+
const res = await fetch("https://api.elevenlabs.io/v1/text-to-speech/" + voiceId, {
|
69 |
+
method: "POST",
|
70 |
+
headers: {
|
71 |
+
"Content-Type": "application/json",
|
72 |
+
"xi-api-key": apiKey
|
73 |
+
},
|
74 |
+
body: JSON.stringify({ text, model_id: "eleven_monolingual_v1", voice_settings: { stability: 0.5, similarity_boost: 0.75 } })
|
75 |
+
});
|
76 |
+
return await res.blob();
|
77 |
+
}
|
78 |
+
|
79 |
+
async function renderAndExport() {
|
80 |
+
renderPreview();
|
81 |
+
const stream = preview.captureStream(30);
|
82 |
+
const recorder = new MediaRecorder(stream);
|
83 |
+
const chunks = [];
|
84 |
+
recorder.ondataavailable = e => chunks.push(e.data);
|
85 |
+
recorder.onstop = () => {
|
86 |
+
const blob = new Blob(chunks, { type: "video/webm" });
|
87 |
+
const a = document.createElement("a");
|
88 |
+
a.href = URL.createObjectURL(blob);
|
89 |
+
a.download = "chat_ai_video.mp4";
|
90 |
+
a.click();
|
91 |
+
};
|
92 |
+
recorder.start();
|
93 |
+
for (const msg of messages) {
|
94 |
+
const voice = await getVoiceBlob(msg.text, msg.voice);
|
95 |
+
const audio = new Audio(URL.createObjectURL(voice));
|
96 |
+
await new Promise(res => {
|
97 |
+
audio.onended = res;
|
98 |
+
audio.play();
|
99 |
+
});
|
100 |
+
}
|
101 |
+
recorder.stop();
|
102 |
}
|
103 |
|
104 |
+
async function generateAI() {
|
105 |
+
const prompt = document.getElementById("prompt").value || "funny conversation between Suren and Elon Musk";
|
106 |
+
const mock = [
|
107 |
+
{ name: "Suren", side: "left", avatar: "https://i.pravatar.cc/30?img=4", voice: "Rachel", text: "Did you launch that rocket again?" },
|
108 |
+
{ name: "Elon", side: "right", avatar: "https://i.pravatar.cc/30?img=8", voice: "Adam", text: "Yes... but it came back with fries." }
|
109 |
+
];
|
110 |
+
messages = mock;
|
111 |
+
renderEditor();
|
112 |
+
}
|