demo: Restructure storytelling-chatbot directory, update README steps, link to vercel demo

This commit is contained in:
Mark Backman
2025-04-18 08:47:32 -04:00
parent e0cf5ec016
commit 814e7509e1
53 changed files with 69 additions and 41 deletions

View File

@@ -0,0 +1,65 @@
.panel{
width: 100%;
pointer-events: none;
color: #ffffff;
text-align: center;
position: relative;
@apply pb-8;
}
.panel::after{
content: "";
position: absolute;
inset: 0px;
z-index: 10;
opacity: 0;
transition: all 0.3s ease;
@apply bg-gradient-to-t from-gray-950 to-transparent;
}
.active::after{ opacity: 1;}
.micIcon{
position: relative;
width: 120px;
height: 120px;
border-radius: 120px;
border: 6px solid;
outline: 6px solid;
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
z-index: 20;
transition: all 0.5s ease;
@apply bg-gray-700/60 border-gray-600 outline-gray-900/20;
}
.micIcon svg{
position: relative;
z-index: 20;
opacity: 0.25;
transition: opacity 0.5s ease;
}
.micIconActive{
@apply bg-teal-950 border-teal-500 outline-teal-500/20;
}
.micIconActive svg{
opacity: 1;
}
.transcript{
flex: 0;
align-self: center;
opacity: 0.25;
transition: opacity 1s ease;
transition-delay: 2.5s;
@apply bg-gray-900/90 font-medium py-1 px-2 rounded-sm mt-4;
}
.active .transcript{
opacity: 1;
}

View File

@@ -0,0 +1,67 @@
import React, { useState, useEffect, useRef } from "react";
import { useAppMessage } from "@daily-co/daily-react";
import { DailyEventObjectAppMessage } from "@daily-co/daily-js";
import styles from "./UserInputIndicator.module.css";
import { IconMicrophone } from "@tabler/icons-react";
import { TypewriterEffect } from "../ui/typewriter";
import AudioIndicator from "../AudioIndicator";
interface Props {
active: boolean;
}
export default function UserInputIndicator({ active }: Props) {
const [transcription, setTranscription] = useState<string[]>([]);
const timeoutRef = useRef<NodeJS.Timeout>();
const resetTimeout = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setTranscription([]);
}, 5000);
};
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
useAppMessage({
onAppMessage: (e: DailyEventObjectAppMessage<any>) => {
if (e.fromId && e.fromId === "transcription") {
if (e.data.user_id === "" && e.data.is_final) {
setTranscription((t) => [...t, ...e.data.text.split(" ")]);
resetTimeout();
}
}
},
});
useEffect(() => {
if (active) return;
const t = setTimeout(() => setTranscription([]), 4000);
return () => clearTimeout(t);
}, [active]);
return (
<div className={`${styles.panel} ${active ? styles.active : ""}`}>
<div className="relative z-20 flex flex-col">
<div
className={`${styles.micIcon} ${active ? styles.micIconActive : ""}`}
>
<IconMicrophone size={42} />
{active && <AudioIndicator />}
</div>
<footer className={styles.transcript}>
<TypewriterEffect words={transcription} />
</footer>
</div>
</div>
);
}