diff --git a/a.sh b/a.sh
new file mode 100755
index 0000000..3f3ab8a
--- /dev/null
+++ b/a.sh
@@ -0,0 +1,1131 @@
+#!/bin/bash
+
+# Create the full directory structure
+mkdir -p my-tauri-app/src/chat/{components,lib,providers,styles,types}
+mkdir -p my-tauri-app/src/chat/components/{chat,projector,selector,ui}
+mkdir -p my-tauri-app/src/styles
+
+# Create all files with React+HTML equivalents
+cat > my-tauri-app/src/chat/components/chat/chat-header.tsx << 'EOL'
+import React from 'react';
+import { useChat } from '../../providers/chat-provider';
+import '../../styles/chat.css';
+
+export function ChatHeader() {
+ const { instance } = useChat();
+
+ return (
+
+
+
+ {instance?.name || 'Chat'}
+
+ Online
+
+
+
+
+
+
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/chat/chat-input.tsx << 'EOL'
+import React from 'react';
+import { EmojiPicker } from '../ui/emoji-picker';
+import { useChat } from '../../providers/chat-provider';
+import { useSound } from '../../providers/sound-provider';
+import '../../styles/chat.css';
+
+export function ChatInput() {
+ const [message, setMessage] = React.useState('');
+ const [showEmoji, setShowEmoji] = React.useState(false);
+ const { sendActivity } = useChat();
+ const { playSound } = useSound();
+
+ const handleSend = () => {
+ if (!message.trim()) return;
+ playSound('send');
+ sendActivity({
+ type: 'message',
+ text: message.trim(),
+ });
+ setMessage('');
+ };
+
+ const handleEmojiSelect = (emoji: string) => {
+ playSound('click');
+ setMessage(prev => prev + emoji);
+ };
+
+ return (
+ <>
+
+
playSound('click')}>
+
+
+
+
+
+
setShowEmoji(true)}>
+
+
+
+
+
+
+
+ {
+ playSound('click');
+ setShowEmoji(false);
+ }}
+ onEmojiSelect={handleEmojiSelect}
+ />
+ >
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/chat/chat-window.tsx << 'EOL'
+import React from 'react';
+import { MessageList } from './message-list';
+import { ChatInput } from './chat-input';
+import { ChatHeader } from './chat-header';
+import { useChat } from '../../providers/chat-provider';
+import { Message } from '../../types';
+import '../../styles/chat.css';
+
+export function ChatWindow() {
+ const { line } = useChat();
+ const [messages, setMessages] = React.useState([]);
+
+ React.useEffect(() => {
+ if (!line) return;
+
+ const subscription = line.activity$.subscribe((activity: any) => {
+ if (activity.type === 'message') {
+ setMessages(prev => [...prev, activity as Message]);
+ }
+ });
+
+ return () => subscription.unsubscribe();
+ }, [line]);
+
+ return (
+
+
+
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/chat/message-list.tsx << 'EOL'
+import React from 'react';
+import { useChat } from '../../providers/chat-provider';
+import { useSound } from '../../providers/sound-provider';
+import { Message } from '../../types';
+import '../../styles/chat.css';
+
+interface MessageListProps {
+ messages: Message[];
+}
+
+export function MessageList({ messages }: MessageListProps) {
+ const scrollRef = React.useRef(null);
+ const { user } = useChat();
+ const { playSound } = useSound();
+ const prevMessagesLength = React.useRef(messages.length);
+
+ React.useEffect(() => {
+ if (messages.length > prevMessagesLength.current) {
+ const lastMessage = messages[messages.length - 1];
+ if (lastMessage.from.id !== user.id) {
+ playSound('receive');
+ }
+ scrollRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }
+ prevMessagesLength.current = messages.length;
+ }, [messages]);
+
+ return (
+
+ {messages.map((message, index) => (
+
+
{message.text}
+
+ {new Date(message.timestamp).toLocaleTimeString()}
+
+
+ ))}
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/chat-layout.tsx << 'EOL'
+import React from 'react';
+import { PersonSelector } from './selector/person-selector';
+import { ProjectorView } from './projector/projector-view';
+import { ChatWindow } from './chat/chat-window';
+import '../../styles/layout.css';
+
+export function ChatLayout() {
+ return (
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/projector/image-viewer.tsx << 'EOL'
+import React from 'react';
+import '../../styles/projector.css';
+
+interface ImageViewerProps {
+ url: string;
+}
+
+export function ImageViewer({ url }: ImageViewerProps) {
+ return (
+
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/projector/markdown-viewer.tsx << 'EOL'
+import React from 'react';
+import ReactMarkdown from 'react-markdown';
+import '../../styles/projector.css';
+
+interface MarkdownViewerProps {
+ content: string;
+}
+
+export function MarkdownViewer({ content }: MarkdownViewerProps) {
+ return (
+
+ {content}
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/projector/projector-view.tsx << 'EOL'
+import React from 'react';
+import { VideoPlayer } from './video-player';
+import { ImageViewer } from './image-viewer';
+import { MarkdownViewer } from './markdown-viewer';
+import { useChat } from '../../providers/chat-provider';
+import '../../styles/projector.css';
+
+export function ProjectorView() {
+ const { line } = useChat();
+ const [content, setContent] = React.useState(null);
+
+ React.useEffect(() => {
+ if (!line) return;
+
+ const subscription = line.activity$
+ .subscribe((activity: any) => {
+ if (activity.type === 'event' && activity.name === 'project') {
+ setContent(activity.value);
+ }
+ });
+
+ return () => subscription.unsubscribe();
+ }, [line]);
+
+ const renderContent = () => {
+ if (!content) return null;
+
+ switch (content.type) {
+ case 'video':
+ return ;
+ case 'image':
+ return ;
+ case 'markdown':
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+ {renderContent()}
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/projector/video-player.tsx << 'EOL'
+import React from 'react';
+import '../../styles/projector.css';
+
+interface VideoPlayerProps {
+ url: string;
+}
+
+export function VideoPlayer({ url }: VideoPlayerProps) {
+ return (
+
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/selector/person-selector.tsx << 'EOL'
+import React from 'react';
+import { useChat } from '../../providers/chat-provider';
+import '../../styles/selector.css';
+
+export function PersonSelector() {
+ const [search, setSearch] = React.useState('');
+ const { instance } = useChat();
+
+ return (
+
+
+ {instance?.logo && (
+
+ )}
+
+
+
+
+
+
+
setSearch(e.target.value)}
+ className="search-input"
+ placeholder="Search conversations..."
+ />
+
+
+
+ {['FAQ', 'Support', 'Sales'].map((item) => (
+
+
+ {item[0]}
+
+
+
{item}
+
Start a conversation
+
+
+ ))}
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/sound-initializer.tsx << 'EOL'
+import React, { useEffect, useState } from 'react';
+import { soundAssets } from '../../../public/sounds/manifest';
+import { cacheAssets } from '../lib/asset-loader';
+
+export function SoundInitializer({ children }: { children: React.ReactNode }) {
+ const [isReady, setIsReady] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const initializeSounds = async () => {
+ try {
+ await cacheAssets(Object.values(soundAssets));
+ setIsReady(true);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to initialize sounds');
+ }
+ };
+
+ initializeSounds();
+ }, []);
+
+ if (error) {
+ return (
+
+ );
+ }
+
+ if (!isReady) {
+ return (
+
+ );
+ }
+
+ return <>{children}>;
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/ui/emoji-picker.tsx << 'EOL'
+import React from 'react';
+import '../../styles/ui.css';
+
+const EMOJI_CATEGORIES = {
+ "đ đŽ": ["đ", "đ", "đ¤", "đž", "đŽ", "â¨", "đ", "đĢ"],
+ "đ đĢ": ["â", "đ", "đĢ", "â¨", "âĄ", "đĨ", "đĨ", "đ"],
+ "đ¤ đ¯": ["đ¤", "đ¯", "đ˛", "đŽ", "đšī¸", "đž", "đģ", "â¨ī¸"]
+};
+
+export function EmojiPicker({ visible, onClose, onEmojiSelect }: {
+ visible: boolean;
+ onClose: () => void;
+ onEmojiSelect: (emoji: string) => void;
+}) {
+ if (!visible) return null;
+
+ return (
+
+
+
Select Emoji
+
+
+
+
+
+
+
+
+ {Object.entries(EMOJI_CATEGORIES).map(([category, emojis]) => (
+
+
{category}
+
+ {emojis.map(emoji => (
+ {
+ onEmojiSelect(emoji);
+ onClose();
+ }}
+ >
+ {emoji}
+
+ ))}
+
+
+ ))}
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/components/ui/settings-panel.tsx << 'EOL'
+import React from 'react';
+import { useSound } from '../../providers/sound-provider';
+import '../../styles/ui.css';
+
+export function SettingsPanel() {
+ const [theme, setTheme] = React.useState('dark');
+ const [sound, setSound] = React.useState(true);
+ const [powerMode, setPowerMode] = React.useState(false);
+ const { setEnabled, playSound } = useSound();
+
+ const handleSoundToggle = (value: boolean) => {
+ setSound(value);
+ setEnabled(value);
+ if (value) {
+ playSound('success');
+ }
+ };
+
+ const handleThemeChange = () => {
+ playSound('click');
+ setTheme(theme === 'dark' ? 'light' : 'dark');
+ };
+
+ const handlePowerMode = (value: boolean) => {
+ playSound(value ? 'success' : 'click');
+ setPowerMode(value);
+ };
+
+ return (
+
+
+ {sound ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
Sound Effects
+
+ handleSoundToggle(e.target.checked)}
+ />
+
+
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/index.tsx << 'EOL'
+import React from 'react';
+import { ChatProvider } from './providers/chat-provider';
+import { ChatLayout } from './components/chat-layout';
+import { SoundInitializer } from './components/sound-initializer';
+import { SoundProvider } from './providers/sound-provider';
+
+export function Chat() {
+ return (
+
+
+
+
+
+
+
+ );
+}
+EOL
+
+cat > my-tauri-app/src/chat/lib/utils.ts << 'EOL'
+export function formatTimestamp(date: Date): string {
+ return new Intl.DateTimeFormat('en-US', {
+ hour: '2-digit',
+ minute: '2-digit',
+ }).format(date);
+}
+
+export function cn(...classes: string[]): string {
+ return classes.filter(Boolean).join(' ');
+}
+
+export function generateId(): string {
+ return Math.random().toString(36).slice(2);
+}
+EOL
+
+cat > my-tauri-app/src/chat/providers/chat-provider.tsx << 'EOL'
+import React, { createContext, useContext, useState, useEffect } from 'react';
+import { invoke } from '@tauri-apps/api/tauri';
+import { User, ChatInstance } from '../types';
+
+interface ChatContextType {
+ line: any;
+ user: User;
+ instance: ChatInstance | null;
+ sendActivity: (activity: any) => void;
+ selectedVoice: any;
+ setVoice: (voice: any) => void;
+}
+
+const ChatContext = createContext(undefined);
+
+export function ChatProvider({ children }: { children: React.ReactNode }) {
+ const [line, setLine] = useState(null);
+ const [instance, setInstance] = useState(null);
+ const [selectedVoice, setSelectedVoice] = useState(null);
+ const [user] = useState({
+ id: `user_${Math.random().toString(36).slice(2)}`,
+ name: 'You'
+ });
+
+ useEffect(() => {
+ const initializeChat = async () => {
+ try {
+ const botId = window.location.pathname.split('/')[1] || 'default';
+ const instanceData = await invoke('get_chat_instance', { botId });
+ setInstance(instanceData as ChatInstance);
+
+ // Initialize DirectLine or other chat service
+ const directLine = {
+ activity$: { subscribe: () => {} },
+ postActivity: () => ({ subscribe: () => {} })
+ };
+ setLine(directLine);
+ } catch (error) {
+ console.error('Failed to initialize chat:', error);
+ }
+ };
+
+ initializeChat();
+ }, []);
+
+ const sendActivity = async (activity: any) => {
+ try {
+ await invoke('send_chat_activity', {
+ activity: {
+ ...activity,
+ from: user,
+ timestamp: new Date().toISOString()
+ }
+ });
+ line?.postActivity(activity).subscribe();
+ } catch (error) {
+ console.error('Failed to send activity:', error);
+ }
+ };
+
+ const setVoice = (voice: any) => {
+ setSelectedVoice(voice);
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useChat() {
+ const context = useContext(ChatContext);
+ if (!context) {
+ throw new Error('useChat must be used within ChatProvider');
+ }
+ return context;
+}
+EOL
+
+cat > my-tauri-app/src/chat/providers/sound-provider.tsx << 'EOL'
+import React, { createContext, useContext, useCallback } from 'react';
+import { invoke } from '@tauri-apps/api/tauri';
+
+interface SoundContextType {
+ playSound: (sound: string) => void;
+ setEnabled: (enabled: boolean) => void;
+}
+
+const SoundContext = createContext(undefined);
+
+export function SoundProvider({ children }: { children: React.ReactNode }) {
+ const [enabled, setEnabled] = React.useState(true);
+
+ const playSound = useCallback(async (sound: string) => {
+ if (!enabled) return;
+ try {
+ await invoke('play_sound', { sound });
+ } catch (error) {
+ console.error('Failed to play sound:', error);
+ }
+ }, [enabled]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useSound() {
+ const context = React.useContext(SoundContext);
+ if (!context) {
+ throw new Error('useSound must be used within SoundProvider');
+ }
+ return context;
+}
+EOL
+
+cat > my-tauri-app/src/chat/types/index.ts << 'EOL'
+export interface Message {
+ id: string;
+ type: 'message' | 'event';
+ text?: string;
+ timestamp: string;
+ from: User;
+ attachments?: Attachment[];
+}
+
+export interface User {
+ id: string;
+ name: string;
+ avatar?: string;
+}
+
+export interface Attachment {
+ type: 'image' | 'video' | 'document';
+ url: string;
+ thumbnailUrl?: string;
+}
+
+export interface ChatInstance {
+ id: string;
+ name: string;
+ logo?: string;
+ botId: string;
+ webchatToken: string;
+}
+EOL
+
+# Create CSS files
+cat > my-tauri-app/src/styles/chat.css << 'EOL'
+.chat-window {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background-color: #111;
+}
+
+.chat-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ background-color: #111;
+ border-bottom: 1px solid #333;
+}
+
+.header-content {
+ display: flex;
+ flex-direction: column;
+}
+
+.header-title {
+ color: white;
+ font-size: 1.25rem;
+ font-weight: bold;
+ margin: 0;
+}
+
+.header-subtitle {
+ color: #00f3ff;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
+
+.message-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 1rem;
+}
+
+.message-container {
+ max-width: 70%;
+ margin-bottom: 0.5rem;
+ padding: 0.75rem;
+ border-radius: 0.75rem;
+}
+
+.user-message {
+ align-self: flex-end;
+ background-color: rgba(0, 243, 255, 0.1);
+ border: 1px solid #00f3ff;
+}
+
+.bot-message {
+ align-self: flex-start;
+ background-color: rgba(191, 0, 255, 0.1);
+ border: 1px solid #bf00ff;
+}
+
+.message-text {
+ color: white;
+ margin: 0;
+}
+
+.message-time {
+ color: #666;
+ font-size: 0.75rem;
+ margin-top: 0.25rem;
+}
+
+.input-container {
+ display: flex;
+ align-items: center;
+ padding: 1rem;
+ border-top: 1px solid #333;
+ background-color: #111;
+}
+
+.chat-input {
+ flex: 1;
+ min-height: 2.5rem;
+ max-height: 6rem;
+ padding: 0.5rem 1rem;
+ margin: 0 0.5rem;
+ background-color: #1a1a1a;
+ color: white;
+ border: 1px solid #333;
+ border-radius: 1.25rem;
+ resize: none;
+}
+
+.icon-button {
+ background: none;
+ border: none;
+ color: #00f3ff;
+ cursor: pointer;
+ padding: 0.5rem;
+}
+
+.send-button {
+ background-color: rgba(0, 243, 255, 0.1);
+ border: 1px solid #00f3ff;
+ border-radius: 50%;
+ padding: 0.5rem;
+ cursor: pointer;
+}
+
+.icon {
+ width: 1.5rem;
+ height: 1.5rem;
+ fill: currentColor;
+}
+EOL
+
+cat > my-tauri-app/src/styles/layout.css << 'EOL'
+.chat-layout {
+ display: flex;
+ height: 100vh;
+ background-color: #111;
+}
+
+.sidebar {
+ width: 18rem;
+ border-right: 1px solid #333;
+}
+
+.main-content {
+ flex: 1;
+ display: flex;
+}
+
+.projector {
+ width: 40%;
+ border-right: 1px solid #333;
+}
+
+.chat-area {
+ flex: 1;
+}
+EOL
+
+cat > my-tauri-app/src/styles/projector.css << 'EOL'
+.projector-container {
+ height: 100%;
+ padding: 1rem;
+ background-color: #111;
+}
+
+.image-container, .video-container {
+ width: 100%;
+ height: 100%;
+ background-color: #111;
+ border-radius: 0.5rem;
+ overflow: hidden;
+}
+
+.projector-image, .projector-video {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.markdown-container {
+ height: 100%;
+ padding: 1rem;
+ color: white;
+ background-color: #111;
+ border-radius: 0.5rem;
+ overflow-y: auto;
+}
+
+.markdown-container h1,
+.markdown-container h2,
+.markdown-container h3 {
+ color: #00f3ff;
+}
+
+.markdown-container a {
+ color: #00f3ff;
+ text-decoration: none;
+}
+
+.markdown-container pre {
+ background-color: #1a1a1a;
+ padding: 1rem;
+ border-radius: 0.25rem;
+}
+EOL
+
+cat > my-tauri-app/src/styles/selector.css << 'EOL'
+.selector-container {
+ height: 100%;
+ background-color: #111;
+}
+
+.selector-header {
+ padding: 1rem;
+ border-bottom: 1px solid #333;
+}
+
+.selector-logo {
+ height: 2.5rem;
+}
+
+.search-container {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem;
+ border-bottom: 1px solid #333;
+}
+
+.search-icon {
+ width: 1.25rem;
+ height: 1.25rem;
+ margin-right: 0.5rem;
+ color: #00f3ff;
+}
+
+.search-input {
+ flex: 1;
+ background: none;
+ border: none;
+ color: white;
+ font-size: 1rem;
+}
+
+.search-input::placeholder {
+ color: #666;
+}
+
+.selector-list {
+ height: calc(100% - 7rem);
+ overflow-y: auto;
+}
+
+.selector-item {
+ display: flex;
+ padding: 1rem;
+ border-bottom: 1px solid #333;
+ cursor: pointer;
+}
+
+.selector-item:hover {
+ background-color: #1a1a1a;
+}
+
+.selector-avatar {
+ width: 3rem;
+ height: 3rem;
+ border-radius: 50%;
+ background-color: #1a1a1a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid #00f3ff;
+ color: #00f3ff;
+ font-size: 1.25rem;
+ font-weight: bold;
+}
+
+.item-content {
+ margin-left: 1rem;
+ flex: 1;
+}
+
+.item-title {
+ color: white;
+ font-size: 1rem;
+ font-weight: bold;
+ margin: 0;
+}
+
+.item-subtitle {
+ color: #666;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
+EOL
+
+cat > my-tauri-app/src/styles/ui.css << 'EOL'
+.emoji-picker-modal {
+ position: fixed;
+ bottom: 5rem;
+ right: 2rem;
+ width: 20rem;
+ max-height: 25rem;
+ background-color: rgba(0, 0, 0, 0.95);
+ border: 1px solid #00f3ff;
+ border-radius: 0.75rem;
+ box-shadow: 0 0 1rem rgba(0, 243, 255, 0.5);
+ z-index: 1000;
+ overflow: hidden;
+}
+
+.emoji-picker-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ border-bottom: 1px solid rgba(0, 243, 255, 0.2);
+}
+
+.emoji-picker-header h3 {
+ color: #00f3ff;
+ margin: 0;
+}
+
+.close-button {
+ background: none;
+ border: none;
+ color: #00f3ff;
+ cursor: pointer;
+}
+
+.emoji-picker-content {
+ padding: 1rem;
+ overflow-y: auto;
+ max-height: calc(25rem - 3.5rem);
+}
+
+.emoji-category {
+ margin-bottom: 1.5rem;
+}
+
+.category-title {
+ color: #bf00ff;
+ font-size: 0.875rem;
+ margin-bottom: 0.75rem;
+}
+
+.emoji-grid {
+ display: grid;
+ grid-template-columns: repeat(8, 1fr);
+ gap: 0.5rem;
+}
+
+.emoji-button {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0.25rem;
+ border-radius: 0.25rem;
+}
+
+.emoji-button:hover {
+ background-color: rgba(0, 243, 255, 0.1);
+}
+
+.settings-panel {
+ background-color: #111;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ border: 1px solid #333;
+}
+
+.settings-option {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem;
+ margin-bottom: 0.5rem;
+ background-color: #1a1a1a;
+ border-radius: 0.5rem;
+ border: 1px solid #333;
+}
+
+.option-text {
+ flex: 1;
+ color: white;
+ margin-left: 0.75rem;
+}
+
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 3rem;
+ height: 1.5rem;
+}
+
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #333;
+ transition: .4s;
+ border-radius: 1.5rem;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 1.1rem;
+ width: 1.1rem;
+ left: 0.2rem;
+ bottom: 0.2rem;
+ background-color: #666;
+ transition: .4s;
+ border-radius: 50%;
+}
+
+input:checked + .slider {
+ background-color: rgba(0, 243, 255, 0.2);
+}
+
+input:checked + .slider:before {
+ transform: translateX(1.5rem);
+ background-color: #00f3ff;
+}
+EOL
+
+echo "All files have been created with React + Tauri compatible versions!"
\ No newline at end of file
diff --git a/package.json b/package.json
index 4e95b0c..a33852b 100644
--- a/package.json
+++ b/package.json
@@ -19,13 +19,13 @@
"botframework-directlinejs": "0.15.1",
"botframework-webchat": "4.15.7",
"date-fns": "2.30.0",
-
"lucide-react": "0.454.0",
"nativewind": "2.0.10",
"postcss": "8.4.35",
+ "react": "18.3.1",
"react-dom": "18.3.1",
"react-hook-form": "7.53.2",
- "react": "18.3.1",
+ "react-router-dom": "^7.4.1",
"tailwindcss": "3.4.1",
"uuid": "11.0.3",
"zod": "3.21.4"
@@ -34,9 +34,9 @@
"@babel/core": "7.18.6",
"@tauri-apps/cli": "2",
"@types/jest": "29.5.12",
+ "@types/react": "18.3.1",
"@types/react-dom": "18.3.1",
"@types/react-test-renderer": "18.0.7",
- "@types/react": "18.3.1",
"@vitejs/plugin-react": "4.3.4",
"copy-webpack-plugin": "12.0.2",
"jest": "29.2.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 59973c3..c9b6dc3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -53,6 +53,9 @@ importers:
react-hook-form:
specifier: 7.53.2
version: 7.53.2(react@18.3.1)
+ react-router-dom:
+ specifier: ^7.4.1
+ version: 7.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tailwindcss:
specifier: 3.4.1
version: 3.4.1
@@ -1292,6 +1295,9 @@ packages:
'@types/babel__traverse@7.20.7':
resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==}
+ '@types/cookie@0.6.0':
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
'@types/eslint-scope@3.7.7':
resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
@@ -1741,6 +1747,10 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie@1.0.2:
+ resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
+ engines: {node: '>=18'}
+
copy-webpack-plugin@12.0.2:
resolution: {integrity: sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==}
engines: {node: '>= 18.12.0'}
@@ -2976,6 +2986,23 @@ packages:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
+ react-router-dom@7.4.1:
+ resolution: {integrity: sha512-L3/4tig0Lvs6m6THK0HRV4eHUdpx0dlJasgCxXKnavwhh4tKYgpuZk75HRYNoRKDyDWi9QgzGXsQ1oQSBlWpAA==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+
+ react-router@7.4.1:
+ resolution: {integrity: sha512-Vmizn9ZNzxfh3cumddqv3kLOKvc7AskUT0dC1prTabhiEi0U4A33LmkDOJ79tXaeSqCqMBXBU/ySX88W85+EUg==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
react-say@2.1.0:
resolution: {integrity: sha512-TSGEA1GQuxa3nc9PEO5fvS3XjM1GGXPUTmcAXV2zlxA1w/vLE+gy0eGJPDYg1ovWmkbe+JZamr7BncwqkicKYg==}
peerDependencies:
@@ -3146,6 +3173,9 @@ packages:
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
+ set-cookie-parser@2.7.1:
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -3343,6 +3373,9 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+ turbo-stream@2.4.0:
+ resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==}
+
type-detect@4.0.8:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
@@ -4973,6 +5006,8 @@ snapshots:
dependencies:
'@babel/types': 7.27.0
+ '@types/cookie@0.6.0': {}
+
'@types/eslint-scope@3.7.7':
dependencies:
'@types/eslint': 9.6.1
@@ -5608,6 +5643,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie@1.0.2: {}
+
copy-webpack-plugin@12.0.2(webpack@5.98.0):
dependencies:
fast-glob: 3.3.3
@@ -6970,6 +7007,22 @@ snapshots:
react-refresh@0.14.2: {}
+ react-router-dom@7.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-router: 7.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+
+ react-router@7.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@types/cookie': 0.6.0
+ cookie: 1.0.2
+ react: 18.3.1
+ set-cookie-parser: 2.7.1
+ turbo-stream: 2.4.0
+ optionalDependencies:
+ react-dom: 18.3.1(react@18.3.1)
+
react-say@2.1.0(react@18.3.1):
dependencies:
'@babel/runtime': 7.15.4
@@ -7171,6 +7224,8 @@ snapshots:
dependencies:
randombytes: 2.1.0
+ set-cookie-parser@2.7.1: {}
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -7361,6 +7416,8 @@ snapshots:
tslib@2.8.1: {}
+ turbo-stream@2.4.0: {}
+
type-detect@4.0.8: {}
type-fest@0.21.3: {}
diff --git a/src/authentication/components/user-auth-form.tsx b/src/authentication/components/user-auth-form.tsx
index a5522ae..e96e695 100644
--- a/src/authentication/components/user-auth-form.tsx
+++ b/src/authentication/components/user-auth-form.tsx
@@ -1,5 +1,4 @@
import React, { useState } from 'react';
-import { View, Text, TextInput, TouchableOpacity, ActivityIndicator, StyleSheet } from 'react-native';
interface UserAuthFormProps {
// Add any props you need
@@ -17,90 +16,50 @@ export function UserAuthForm({ }: UserAuthFormProps) {
}
return (
-
-
-
-
+
+
+
-
- setEmail(e.target.value)}
+ />
+
+
- {isLoading && }
- Sign In with Email
-
-
-
-
- Or continue with
-
-
- {/* Add GitHub sign in logic */}}
+ {isLoading ? (
+
+ ) : (
+ 'Sign In with Email'
+ )}
+
+
+
+ {}}
disabled={isLoading}
>
{isLoading ? (
-
+
) : (
- GitHub Icon // Replace with actual GitHub icon
+ GitHub Icon
)}
- GitHub
-
-
+ GitHub
+
+
);
}
-
-const styles = StyleSheet.create({
- container: {
- gap: 20,
- },
- form: {
- gap: 10,
- },
- inputContainer: {
- // Add styles for input container
- },
- input: {
- // Add styles for input
- },
- button: {
- // Add styles for button
- },
- buttonText: {
- // Add styles for button text
- },
- spinner: {
- marginRight: 8,
- },
- divider: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- dividerLine: {
- flex: 1,
- height: 1,
- backgroundColor: '#e0e0e0',
- },
- dividerText: {
- paddingHorizontal: 10,
- // Add styles for divider text
- },
- githubButton: {
- // Add styles for GitHub button
- },
- githubButtonText: {
- // Add styles for GitHub button text
- },
-});
diff --git a/src/authentication/index.tsx b/src/authentication/index.tsx
index 95046bb..2578154 100644
--- a/src/authentication/index.tsx
+++ b/src/authentication/index.tsx
@@ -1,157 +1,66 @@
import React, { useState } from 'react';
-import { View, Text, Image, StyleSheet, TouchableOpacity, ScrollView, Alert } from 'react-native';
-import { Auth as ZitadelAuth } from '@zitadel/react';
+import { UserAuthForm } from './components/user-auth-form';
const AuthenticationScreen = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const handleLogin = async () => {
try {
- const auth = new ZitadelAuth({
- clientId: 'YOUR_CLIENT_ID',
- issuer: 'YOUR_ZITADEL_ISSUER_URL',
- redirectUri: 'YOUR_REDIRECT_URI',
- scopes: ['openid', 'profile', 'email'],
- });
-
- const result = await auth.authorize();
- if (result?.accessToken) {
- await AsyncStorage.setItem('authToken', result.accessToken);
- setIsAuthenticated(true);
- Alert.alert('Login Successful', 'You are now authenticated.');
- } else {
- Alert.alert('Login Failed', 'Unable to retrieve access token.');
- }
+ localStorage.setItem('authToken', 'dummy-token');
+ setIsAuthenticated(true);
+ alert('Login Successful');
} catch (error) {
console.error('Login error:', error);
- Alert.alert('Login Error', 'An error occurred during login.');
+ alert('Login Error');
}
};
const handleLogout = async () => {
try {
- await AsyncStorage.removeItem('authToken');
+ localStorage.removeItem('authToken');
setIsAuthenticated(false);
- Alert.alert('Logout Successful', 'You are now logged out.');
+ alert('Logout Successful');
} catch (error) {
console.error('Logout error:', error);
- Alert.alert('Logout Error', 'An error occurred during logout.');
+ alert('Logout Error');
}
};
return (
-
-
-
-
+
+
+
+ {isAuthenticated ? 'Logout' : 'Login'}
+
-
-
- {isAuthenticated ? 'Logout' : 'Login'}
-
+
+
+
Welcome to General Bots Online
+
+
+
"Errar Ê Humano."
+
General Bots
+
+
-
-
- {/* Replace with your logo component */}
- Welcome to General Bots Online
-
-
-
- "Errar Ê Humano."
-
- General Bots
-
-
-
-
-
- Create an account
-
- Enter your email below to create your account
-
-
+
+
+
Create an account
+
Enter your email below to create your account
+
-
- By clicking continue, you agree to our Terms of Service and Privacy Policy.
-
-
-
-
-
+
+
+
+ By clicking continue, you agree to our Terms of Service and Privacy Policy.
+
+
+
+
);
};
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#fff',
- },
- scrollContent: {
- flexGrow: 1,
- },
- imageContainer: {
- // ... styles for image container
- },
- image: {
- width: '100%',
- height: 300,
- resizeMode: 'cover',
- },
- contentContainer: {
- flex: 1,
- padding: 16,
- },
- loginButton: {
- position: 'absolute',
- top: 16,
- right: 16,
- padding: 8,
- },
- loginButtonText: {
- color: '#000',
- },
- leftPanel: {
- // ... styles for left panel
- },
- logoContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- logoText: {
- fontSize: 18,
- fontWeight: 'bold',
- },
- quoteContainer: {
- marginTop: 'auto',
- },
- quoteText: {
- fontSize: 16,
- marginBottom: 8,
- },
- quoteAuthor: {
- fontSize: 14,
- },
- formContainer: {
- // ... styles for form container
- },
- formHeader: {
- alignItems: 'center',
- marginBottom: 16,
- },
- formTitle: {
- fontSize: 24,
- fontWeight: 'bold',
- },
- formSubtitle: {
- fontSize: 14,
- color: '#666',
- },
- termsText: {
- textAlign: 'center',
- fontSize: 12,
- color: '#666',
- marginTop: 16,
- },
-});
-
-export default AuthenticationScreen;
\ No newline at end of file
+export default AuthenticationScreen;
diff --git a/src/chat/components/chat-layout.tsx b/src/chat/components/chat-layout.tsx
index ecbead6..c7fb143 100644
--- a/src/chat/components/chat-layout.tsx
+++ b/src/chat/components/chat-layout.tsx
@@ -1,27 +1,23 @@
import React from 'react';
-import { View, Platform } from 'react-native';
import { PersonSelector } from './selector/person-selector';
import { ProjectorView } from './projector/projector-view';
import { ChatWindow } from './chat/chat-window';
-import { layoutStyles } from '../styles/layout.styles';
+import '../../styles/layout.css';
export function ChatLayout() {
return (
-
-
+
);
}
diff --git a/src/chat/components/chat/chat-header.tsx b/src/chat/components/chat/chat-header.tsx
index be2854d..cfaf4d0 100644
--- a/src/chat/components/chat/chat-header.tsx
+++ b/src/chat/components/chat/chat-header.tsx
@@ -1,26 +1,24 @@
import React from 'react';
-import { View, Text, TouchableOpacity } from 'react-native';
-import { MoreVertical } from 'lucide-react-native';
import { useChat } from '../../providers/chat-provider';
-import { chatStyles } from '../../styles/chat.styles';
+import '../../styles/chat.css';
export function ChatHeader() {
const { instance } = useChat();
return (
-
-
-
+
+
+
{instance?.name || 'Chat'}
-
-
- Online
-
-
+
+ Online
+
-
-
-
-
+
+
+
+
+
+
);
}
diff --git a/src/chat/components/chat/chat-input.tsx b/src/chat/components/chat/chat-input.tsx
index 6d3c53c..5fc7ed2 100644
--- a/src/chat/components/chat/chat-input.tsx
+++ b/src/chat/components/chat/chat-input.tsx
@@ -1,27 +1,14 @@
import React from 'react';
-import { View, TextInput, TouchableOpacity, Animated } from 'react-native';
-import { Send, Paperclip, Mic, Smile } from 'lucide-react-native';
import { EmojiPicker } from '../ui/emoji-picker';
import { useChat } from '../../providers/chat-provider';
import { useSound } from '../../providers/sound-provider';
-import { chatStyles } from '../../styles/chat.styles';
+import '../../styles/chat.css';
export function ChatInput() {
const [message, setMessage] = React.useState('');
const [showEmoji, setShowEmoji] = React.useState(false);
- const pulseAnim = React.useRef(new Animated.Value(1)).current;
const { sendActivity } = useChat();
const { playSound } = useSound();
- const typingTimeout = React.useRef();
-
- const handleKeyPress = () => {
- if (typingTimeout.current) {
- clearTimeout(typingTimeout.current);
- }
- typingTimeout.current = setTimeout(() => {
- playSound('typing');
- }, 100);
- };
const handleSend = () => {
if (!message.trim()) return;
@@ -40,55 +27,40 @@ export function ChatInput() {
return (
<>
-
- playSound('click')}
- >
-
-
+
+
playSound('click')}>
+
+
+
+
-
{
- playSound('click');
- setShowEmoji(true);
- }}
- >
-
-
+
setShowEmoji(true)}>
+
+
+
+
-
setMessage(e.target.value)}
+ className="chat-input"
placeholder="Type a message..."
- placeholderTextColor="#666"
- multiline
/>
{message.trim().length > 0 ? (
-
-
-
-
-
+
+
+
+
+
) : (
- playSound('click')}
- >
-
-
+ playSound('click')}>
+
+
+
+
)}
-
+
{
if (!line) return;
- const subscription = line.activity$.subscribe(activity => {
+ const subscription = line.activity$.subscribe((activity: any) => {
if (activity.type === 'message') {
setMessages(prev => [...prev, activity as Message]);
}
@@ -24,10 +23,10 @@ export function ChatWindow() {
}, [line]);
return (
-
+
-
+
);
}
diff --git a/src/chat/components/chat/message-list.tsx b/src/chat/components/chat/message-list.tsx
index 9447faf..e406944 100644
--- a/src/chat/components/chat/message-list.tsx
+++ b/src/chat/components/chat/message-list.tsx
@@ -1,16 +1,15 @@
import React from 'react';
-import { ScrollView, View, Text } from 'react-native';
import { useChat } from '../../providers/chat-provider';
import { useSound } from '../../providers/sound-provider';
import { Message } from '../../types';
-import { chatStyles } from '../../styles/chat.styles';
+import '../../styles/chat.css';
interface MessageListProps {
messages: Message[];
}
export function MessageList({ messages }: MessageListProps) {
- const scrollViewRef = React.useRef(null);
+ const scrollRef = React.useRef(null);
const { user } = useChat();
const { playSound } = useSound();
const prevMessagesLength = React.useRef(messages.length);
@@ -21,33 +20,26 @@ export function MessageList({ messages }: MessageListProps) {
if (lastMessage.from.id !== user.id) {
playSound('receive');
}
- scrollViewRef.current?.scrollToEnd({ animated: true });
+ scrollRef.current?.scrollIntoView({ behavior: 'smooth' });
}
prevMessagesLength.current = messages.length;
}, [messages]);
return (
-
+
{messages.map((message, index) => (
-
- {message.text}
-
+ {message.text}
+
{new Date(message.timestamp).toLocaleTimeString()}
-
-
+
+
))}
-
+
);
}
diff --git a/src/chat/components/projector/image-viewer.tsx b/src/chat/components/projector/image-viewer.tsx
index 1854f9b..2acbd40 100644
--- a/src/chat/components/projector/image-viewer.tsx
+++ b/src/chat/components/projector/image-viewer.tsx
@@ -1,6 +1,5 @@
import React from 'react';
-import { View, Image } from 'react-native';
-import { projectorStyles } from '../../styles/projector.styles';
+import '../../styles/projector.css';
interface ImageViewerProps {
url: string;
@@ -8,12 +7,8 @@ interface ImageViewerProps {
export function ImageViewer({ url }: ImageViewerProps) {
return (
-
-
-
+
+
+
);
}
diff --git a/src/chat/components/projector/markdown-viewer.tsx b/src/chat/components/projector/markdown-viewer.tsx
index ec21c3c..83f11fe 100644
--- a/src/chat/components/projector/markdown-viewer.tsx
+++ b/src/chat/components/projector/markdown-viewer.tsx
@@ -1,7 +1,6 @@
import React from 'react';
-import { ScrollView } from 'react-native';
-import Markdown from 'react-native-markdown-display';
-import { projectorStyles } from '../../styles/projector.styles';
+import ReactMarkdown from 'react-markdown';
+import '../../styles/projector.css';
interface MarkdownViewerProps {
content: string;
@@ -9,10 +8,8 @@ interface MarkdownViewerProps {
export function MarkdownViewer({ content }: MarkdownViewerProps) {
return (
-
-
- {content}
-
-
+
+ {content}
+
);
}
diff --git a/src/chat/components/projector/projector-view.tsx b/src/chat/components/projector/projector-view.tsx
index d776e62..4f0039c 100644
--- a/src/chat/components/projector/projector-view.tsx
+++ b/src/chat/components/projector/projector-view.tsx
@@ -1,10 +1,9 @@
import React from 'react';
-import { View } from 'react-native';
import { VideoPlayer } from './video-player';
import { ImageViewer } from './image-viewer';
import { MarkdownViewer } from './markdown-viewer';
import { useChat } from '../../providers/chat-provider';
-import { projectorStyles } from '../../styles/projector.styles';
+import '../../styles/projector.css';
export function ProjectorView() {
const { line } = useChat();
@@ -14,9 +13,10 @@ export function ProjectorView() {
if (!line) return;
const subscription = line.activity$
- .filter(activity => activity.type === 'event' && activity.name === 'project')
- .subscribe(activity => {
- setContent(activity.value);
+ .subscribe((activity: any) => {
+ if (activity.type === 'event' && activity.name === 'project') {
+ setContent(activity.value);
+ }
});
return () => subscription.unsubscribe();
@@ -38,8 +38,8 @@ export function ProjectorView() {
};
return (
-
+
{renderContent()}
-
+
);
}
diff --git a/src/chat/components/projector/video-player.tsx b/src/chat/components/projector/video-player.tsx
index 54c3847..b8d1eee 100644
--- a/src/chat/components/projector/video-player.tsx
+++ b/src/chat/components/projector/video-player.tsx
@@ -1,19 +1,14 @@
import React from 'react';
-import { View, Image } from 'react-native';
-import { projectorStyles } from '../../styles/projector.styles';
+import '../../styles/projector.css';
-interface ImageViewerProps {
+interface VideoPlayerProps {
url: string;
}
-export function VideoViewer({ url }: ImageViewerProps) {
+export function VideoPlayer({ url }: VideoPlayerProps) {
return (
-
-
-
+
+
+
);
}
diff --git a/src/chat/components/selector/person-selector.tsx b/src/chat/components/selector/person-selector.tsx
index 61e2a9b..1523a6f 100644
--- a/src/chat/components/selector/person-selector.tsx
+++ b/src/chat/components/selector/person-selector.tsx
@@ -1,50 +1,44 @@
import React from 'react';
-import { View, Text, Image, TextInput, ScrollView, TouchableOpacity } from 'react-native';
-import { Search } from 'lucide-react-native';
import { useChat } from '../../providers/chat-provider';
-import { selectorStyles } from '../../styles/selector.styles';
+import '../../styles/selector.css';
export function PersonSelector() {
const [search, setSearch] = React.useState('');
const { instance } = useChat();
return (
-
-
-
-
+
+
+ {instance?.logo && (
+
+ )}
+
-
-
-
+
+
+
+ setSearch(e.target.value)}
+ className="search-input"
placeholder="Search conversations..."
- placeholderTextColor="#666"
- style={selectorStyles.searchInput}
/>
-
+
-
+
{['FAQ', 'Support', 'Sales'].map((item) => (
-
-
- {item[0]}
-
-
- {item}
- Start a conversation
-
-
+
+
+ {item[0]}
+
+
+
{item}
+
Start a conversation
+
+
))}
-
-
+
+
);
}
diff --git a/src/chat/components/sound-initializer.tsx b/src/chat/components/sound-initializer.tsx
index 046d9b4..dcbd738 100644
--- a/src/chat/components/sound-initializer.tsx
+++ b/src/chat/components/sound-initializer.tsx
@@ -1,9 +1,7 @@
import React, { useEffect, useState } from 'react';
-import { View, Text } from 'react-native';
import { soundAssets } from '../../../public/sounds/manifest';
import { cacheAssets } from '../lib/asset-loader';
-
export function SoundInitializer({ children }: { children: React.ReactNode }) {
const [isReady, setIsReady] = useState(false);
const [error, setError] = useState(null);
@@ -11,31 +9,29 @@ export function SoundInitializer({ children }: { children: React.ReactNode }) {
useEffect(() => {
const initializeSounds = async () => {
try {
- await cacheAssets(Object.values(soundAssets));
+ await cacheAssets(Object.values(soundAssets));
setIsReady(true);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to initialize sounds');
}
};
- //initializeSounds();
- setIsReady(true);
+ initializeSounds();
}, []);
- if (error) {
-
+ if (error) {
return (
-
- Error: {error}
-
+
);
}
if (!isReady) {
return (
-
- Loading sounds...
-
+
);
}
diff --git a/src/chat/components/ui/emoji-picker.tsx b/src/chat/components/ui/emoji-picker.tsx
index 9070acd..d2ed8ee 100644
--- a/src/chat/components/ui/emoji-picker.tsx
+++ b/src/chat/components/ui/emoji-picker.tsx
@@ -1,7 +1,5 @@
import React from 'react';
-import { View, Text, ScrollView, TouchableOpacity, Modal } from 'react-native';
-import { X } from 'lucide-react-native';
-import { emojiStyles } from '../../styles/ui.styles';
+import '../../styles/ui.css';
const EMOJI_CATEGORIES = {
"đ đŽ": ["đ", "đ", "đ¤", "đž", "đŽ", "â¨", "đ", "đĢ"],
@@ -9,43 +7,45 @@ const EMOJI_CATEGORIES = {
"đ¤ đ¯": ["đ¤", "đ¯", "đ˛", "đŽ", "đšī¸", "đž", "đģ", "â¨ī¸"]
};
-export function EmojiPicker({ visible, onClose, onEmojiSelect }) {
+export function EmojiPicker({ visible, onClose, onEmojiSelect }: {
+ visible: boolean;
+ onClose: () => void;
+ onEmojiSelect: (emoji: string) => void;
+}) {
+ if (!visible) return null;
+
return (
-
-
-
- Select Emoji
-
-
-
-
-
-
- {Object.entries(EMOJI_CATEGORIES).map(([category, emojis]) => (
-
- {category}
-
- {emojis.map(emoji => (
- {
- onEmojiSelect(emoji);
- onClose();
- }}
- >
- {emoji}
-
- ))}
-
-
- ))}
-
-
-
+
+
+
Select Emoji
+
+
+
+
+
+
+
+
+ {Object.entries(EMOJI_CATEGORIES).map(([category, emojis]) => (
+
+
{category}
+
+ {emojis.map(emoji => (
+ {
+ onEmojiSelect(emoji);
+ onClose();
+ }}
+ >
+ {emoji}
+
+ ))}
+
+
+ ))}
+
+
);
}
diff --git a/src/chat/components/ui/settings-panel.tsx b/src/chat/components/ui/settings-panel.tsx
index d56ff89..4c48ca7 100644
--- a/src/chat/components/ui/settings-panel.tsx
+++ b/src/chat/components/ui/settings-panel.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-import { View, Text, Switch, TouchableOpacity, ScrollView } from 'react-native';
-import { Moon, Sun, Volume2, VolumeX, Zap, Settings } from 'lucide-react-native';
import { useSound } from '../../providers/sound-provider';
-import { settingsStyles } from '../../styles/ui.styles';
+import '../../styles/ui.css';
export function SettingsPanel() {
const [theme, setTheme] = React.useState('dark');
@@ -29,21 +27,27 @@ export function SettingsPanel() {
};
return (
- // ... rest of the settings panel code ...
-
- {sound ? (
-
- ) : (
-
- )}
- Sound Effects
-
-
- // ... rest of the settings panel code ...
+
+
+ {sound ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
Sound Effects
+
+ handleSoundToggle(e.target.checked)}
+ />
+
+
+
+
);
}
diff --git a/src/chat/index.tsx b/src/chat/index.tsx
index 74b55cb..bd7a6af 100644
--- a/src/chat/index.tsx
+++ b/src/chat/index.tsx
@@ -8,10 +8,10 @@ export function Chat() {
return (
-
-
-
-
+
+
+
+
);
}
diff --git a/src/chat/providers/chat-provider.tsx b/src/chat/providers/chat-provider.tsx
index 7c1a4a3..5afffb6 100644
--- a/src/chat/providers/chat-provider.tsx
+++ b/src/chat/providers/chat-provider.tsx
@@ -1,10 +1,9 @@
-import React, { useState } from 'react';
-import { DirectLine } from 'botframework-directlinejs';
-import { ChatInstance, User } from '../types';
-import { v4 as uuidv4 } from 'uuid';
+import React, { createContext, useContext, useState, useEffect } from 'react';
+import { invoke } from '@tauri-apps/api/tauri';
+import { User, ChatInstance } from '../types';
interface ChatContextType {
- line: DirectLine | null;
+ line: any;
user: User;
instance: ChatInstance | null;
sendActivity: (activity: any) => void;
@@ -12,92 +11,66 @@ interface ChatContextType {
setVoice: (voice: any) => void;
}
-const generateUserId = () => {
- return 'usergb@gb';
-};
-
-export const ChatContext = React.createContext(undefined);
+const ChatContext = createContext(undefined);
export function ChatProvider({ children }: { children: React.ReactNode }) {
- const [line, setLine] = React.useState(null);
- const [instance, setInstance] = React.useState(null);
+ const [line, setLine] = useState(null);
+ const [instance, setInstance] = useState(null);
const [selectedVoice, setSelectedVoice] = useState(null);
- const [user] = React.useState(() => ({
+ const [user] = useState({
id: `user_${Math.random().toString(36).slice(2)}`,
name: 'You'
- }));
+ });
- React.useEffect(() => {
+ useEffect(() => {
+ const initializeChat = async () => {
+ try {
+ const botId = window.location.pathname.split('/')[1] || 'default';
+ const instanceData = await invoke('get_chat_instance', { botId });
+ setInstance(instanceData as ChatInstance);
+
+ // Initialize DirectLine or other chat service
+ const directLine = {
+ activity$: { subscribe: () => {} },
+ postActivity: () => ({ subscribe: () => {} })
+ };
+ setLine(directLine);
+ } catch (error) {
+ console.error('Failed to initialize chat:', error);
+ }
+ };
+
initializeChat();
}, []);
- const initializeChat = async () => {
+ const sendActivity = async (activity: any) => {
try {
- var botId = window.location.href.split('/')[3];
- if (botId.indexOf('#') !== -1) {
- botId = botId.split('#')[0];
- }
-
- if (!botId || botId === '') {
- botId = '[default]';
- }
-
- const response = await fetch(
- 'http://localhost:4242/instances/' + botId,
- )
- const data = await response.json();
- const userId = generateUserId();
-
- const directLine = data.webchatToken
- ? new DirectLine({
- token: data.token,
- webSocket: true
- })
- : new DirectLine({
- domain: data.domain,
- secret: null,
- token: null,
- webSocket: false
- });
- directLine.setUserId(userId);
- setLine(directLine);
- setInstance(data.instance);
- console.info (`DirectLine for user:` + userId);
+ await invoke('send_chat_activity', {
+ activity: {
+ ...activity,
+ from: user,
+ timestamp: new Date().toISOString()
+ }
+ });
+ line?.postActivity(activity).subscribe();
} catch (error) {
- console.error('Failed to initialize chat:', error);
+ console.error('Failed to send activity:', error);
}
};
- const sendActivity = (activity: any) => {
- line?.postActivity({
- ...activity,
- from: user,
- timestamp: new Date().toISOString()
- }).subscribe();
- };
-
const setVoice = (voice: any) => {
setSelectedVoice(voice);
};
- const contextValue: ChatContextType = {
- line,
- user,
- instance,
- sendActivity,
- selectedVoice,
- setVoice
- };
-
return (
-
+
{children}
);
}
export function useChat() {
- const context = React.useContext(ChatContext);
+ const context = useContext(ChatContext);
if (!context) {
throw new Error('useChat must be used within ChatProvider');
}
diff --git a/src/chat/providers/sound-provider.tsx b/src/chat/providers/sound-provider.tsx
index 1ef4860..cd42d0c 100644
--- a/src/chat/providers/sound-provider.tsx
+++ b/src/chat/providers/sound-provider.tsx
@@ -1,20 +1,24 @@
-import React from 'react';
+import React, { createContext, useContext, useCallback } from 'react';
+import { invoke } from '@tauri-apps/api/tauri';
interface SoundContextType {
playSound: (sound: string) => void;
setEnabled: (enabled: boolean) => void;
}
-const SoundContext = React.createContext(undefined);
+const SoundContext = createContext(undefined);
export function SoundProvider({ children }: { children: React.ReactNode }) {
- const playSound = React.useCallback((sound: string) => {
- // soundManager.play(sound as any);
- }, []);
+ const [enabled, setEnabled] = React.useState(true);
- const setEnabled = React.useCallback((enabled: boolean) => {
- // soundManager.setEnabled(enabled);
- }, []);
+ const playSound = useCallback(async (sound: string) => {
+ if (!enabled) return;
+ try {
+ await invoke('play_sound', { sound });
+ } catch (error) {
+ console.error('Failed to play sound:', error);
+ }
+ }, [enabled]);
return (
diff --git a/src/chat/styles/audio.styles.ts b/src/chat/styles/audio.styles.ts
deleted file mode 100644
index 0e7eca7..0000000
--- a/src/chat/styles/audio.styles.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-export const audioStyles = StyleSheet.create({
- volumeContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 16,
- backgroundColor: '#1a1a1a',
- borderRadius: 12,
- borderWidth: 1,
- borderColor: '#333',
- },
- volumeButton: {
- padding: 8,
- },
- sliderContainer: {
- flex: 1,
- marginLeft: 16,
- },
- slider: {
- flex: 1,
- height: 40,
- },
- visualizerContainer: {
- flexDirection: 'row',
- height: 40,
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingHorizontal: 8,
- },
- visualizerBar: {
- width: 3,
- height: 20,
- backgroundColor: '#00f3ff',
- borderRadius: 2,
- marginHorizontal: 1,
- },
- modal: {
- flex: 1,
- backgroundColor: 'rgba(0,0,0,0.95)',
- margin: 20,
- marginTop: 100,
- borderRadius: 20,
- borderWidth: 1,
- borderColor: '#00f3ff',
- shadowColor: '#00f3ff',
- shadowOffset: { width: 0, height: 0 },
- shadowOpacity: 0.5,
- shadowRadius: 10,
- },
- voiceList: {
- flex: 1,
- padding: 16,
- },
- voiceOption: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- padding: 16,
- backgroundColor: '#1a1a1a',
- borderRadius: 12,
- marginBottom: 8,
- borderWidth: 1,
- borderColor: '#333',
- },
- selectedVoice: {
- borderColor: '#00f3ff',
- backgroundColor: '#00f3ff11',
- },
- voiceInfo: {
- flex: 1,
- },
- voiceName: {
- color: '#ffffff',
- fontSize: 16,
- fontWeight: 'bold',
- },
- voiceAccent: {
- color: '#666',
- fontSize: 14,
- marginTop: 4,
- },
- header: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- padding: 16,
- borderBottomWidth: 1,
- borderBottomColor: '#333',
- },
- title: {
- color: '#00f3ff',
- fontSize: 18,
- fontWeight: 'bold',
- },
- closeButton: {
- color: '#00f3ff',
- fontSize: 24,
- fontWeight: 'bold',
- },
- trigger: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 8,
- backgroundColor: '#1a1a1a',
- borderRadius: 8,
- borderWidth: 1,
- borderColor: '#333',
- },
- triggerText: {
- color: '#ffffff',
- marginLeft: 8,
- fontSize: 14,
- },
-});
-
-export const voiceStyles = StyleSheet.create({
- // ... copy from audioStyles the modal-related styles ...
- // Add voice-specific styles here
-});
diff --git a/src/chat/styles/chat.styles.ts b/src/chat/styles/chat.styles.ts
deleted file mode 100644
index c65ccd5..0000000
--- a/src/chat/styles/chat.styles.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-export const chatStyles = StyleSheet.create({
- window: {
- flex: 1,
- backgroundColor: '#111111',
- },
- messageList: {
- flex: 1,
- padding: 16,
- },
- messageListContent: {
- paddingBottom: 16,
- },
- messageContainer: {
- maxWidth: '70%',
- marginVertical: 4,
- padding: 12,
- borderRadius: 12,
- },
- userMessage: {
- alignSelf: 'flex-end',
- backgroundColor: '#00f3ff22',
- borderColor: '#00f3ff',
- borderWidth: 1,
- },
- botMessage: {
- alignSelf: 'flex-start',
- backgroundColor: '#bf00ff22',
- borderColor: '#bf00ff',
- borderWidth: 1,
- },
- messageText: {
- color: '#ffffff',
- fontSize: 16,
- },
- messageTime: {
- color: '#666666',
- fontSize: 12,
- marginTop: 4,
- },
- inputContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 16,
- borderTopWidth: 1,
- borderTopColor: '#1a1a1a',
- },
- input: {
- flex: 1,
- marginHorizontal: 12,
- padding: 12,
- backgroundColor: '#1a1a1a',
- borderRadius: 24,
- color: '#ffffff',
- maxHeight: 100,
- },
- iconButton: {
- padding: 8,
- },
- header: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 16,
- borderBottomWidth: 1,
- borderBottomColor: '#1a1a1a',
- },
- headerContent: {
- flex: 1,
- },
- headerTitle: {
- color: '#ffffff',
- fontSize: 18,
- fontWeight: 'bold',
- },
- headerSubtitle: {
- color: '#00f3ff',
- fontSize: 14,
- marginTop: 2,
- },
- headerButton: {
- padding: 8,
- },
- sendButton: {
- backgroundColor: '#00f3ff22',
- borderRadius: 20,
- padding: 10,
- borderWidth: 1,
- borderColor: '#00f3ff',
- }
-});
\ No newline at end of file
diff --git a/src/chat/styles/layout.styles.ts b/src/chat/styles/layout.styles.ts
deleted file mode 100644
index 9afde5f..0000000
--- a/src/chat/styles/layout.styles.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// layout.styles.ts
-import { Colors } from '../../../constants/Colors';
-import { StyleSheet } from 'react-native';
-export const layoutStyles = StyleSheet.create({
- container: {
- flex: 1,
- flexDirection: 'row',
- backgroundColor: Colors.dark.background,
- },
- sidebar: {
- width: 300,
- borderRightWidth: 1,
- borderRightColor: Colors.dark.icon,
- },
- mainContent: {
- flex: 1,
- flexDirection: 'row',
- },
- projector: {
- width: '40%',
- borderRightWidth: 1,
- borderRightColor: Colors.dark.icon,
- },
- chatArea: {
- flex: 1,
- },
-});
diff --git a/src/chat/styles/projector.styles.ts b/src/chat/styles/projector.styles.ts
deleted file mode 100644
index 7cb9de8..0000000
--- a/src/chat/styles/projector.styles.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-// projector.styles.ts
-import { Colors } from '../../../constants/Colors';
-import { StyleSheet } from 'react-native';
-export const projectorStyles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: Colors.dark.background,
- padding: 16,
- },
- videoContainer: {
- aspectRatio: 16/9,
- backgroundColor: Colors.dark.background,
- borderRadius: 8,
- overflow: 'hidden',
- },
- imageContainer: {
- flex: 1,
- backgroundColor: Colors.dark.background,
- borderRadius: 8,
- overflow: 'hidden',
- },
- markdownContainer: {
- flex: 1,
- padding: 16,
- backgroundColor: Colors.dark.background,
- borderRadius: 8,
- },
- body: {
- color: Colors.dark.text,
- fontSize: 16,
- },
- heading1: {
- color: Colors.dark.tint,
- fontSize: 24,
- marginBottom: 16,
- },
- heading2: {
- color: Colors.dark.tint,
- fontSize: 20,
- marginBottom: 12,
- },
- link: {
- color: Colors.dark.tint,
- },
- code_block: {
- backgroundColor: Colors.dark.background,
- padding: 12,
- borderRadius: 4,
- }
-});
\ No newline at end of file
diff --git a/src/chat/styles/selector.styles.ts b/src/chat/styles/selector.styles.ts
deleted file mode 100644
index 7ebd0bf..0000000
--- a/src/chat/styles/selector.styles.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-// selector.styles.ts
-import { StyleSheet } from 'react-native';
-import { Colors } from '../../../constants/Colors';
-
-export const selectorStyles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: Colors.dark.background,
- },
- header: {
- padding: 16,
- borderBottomWidth: 1,
- borderBottomColor: Colors.dark.icon,
- },
- logo: {
- width: 150,
- height: 50,
- },
- searchContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 12,
- borderBottomWidth: 1,
- borderBottomColor: Colors.dark.icon,
- },
- searchInput: {
- flex: 1,
- marginLeft: 8,
- color: Colors.dark.text,
- fontSize: 16,
- },
- list: {
- flex: 1,
- },
- item: {
- flexDirection: 'row',
- padding: 16,
- borderBottomWidth: 1,
- borderBottomColor: Colors.dark.icon,
- },
- avatar: {
- width: 48,
- height: 48,
- borderRadius: 24,
- backgroundColor: Colors.dark.background,
- alignItems: 'center',
- justifyContent: 'center',
- borderWidth: 1,
- borderColor: Colors.dark.tint,
- },
- avatarText: {
- color: Colors.dark.tint,
- fontSize: 20,
- },
- itemContent: {
- marginLeft: 12,
- flex: 1,
- },
- itemTitle: {
- color: Colors.dark.text,
- fontSize: 16,
- fontWeight: 'bold',
- },
- itemSubtitle: {
- color: Colors.dark.icon,
- fontSize: 14,
- marginTop: 4,
- },
-});
diff --git a/src/chat/styles/ui.styles.ts b/src/chat/styles/ui.styles.ts
deleted file mode 100644
index 5826f2b..0000000
--- a/src/chat/styles/ui.styles.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-export const emojiStyles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: 'rgba(0,0,0,0.95)',
- margin: 20,
- marginTop: 100,
- borderRadius: 20,
- borderWidth: 1,
- borderColor: '#00f3ff',
- shadowColor: '#00f3ff',
- shadowOffset: { width: 0, height: 0 },
- shadowOpacity: 0.5,
- shadowRadius: 10,
- },
- header: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- padding: 16,
- borderBottomWidth: 1,
- borderBottomColor: '#00f3ff33',
- },
- title: {
- color: '#00f3ff',
- fontSize: 18,
- fontWeight: 'bold',
- },
- content: {
- flex: 1,
- padding: 16,
- },
- category: {
- marginBottom: 24,
- },
- categoryTitle: {
- color: '#bf00ff',
- fontSize: 16,
- marginBottom: 12,
- },
- emojiGrid: {
- flexDirection: 'row',
- flexWrap: 'wrap',
- },
- emojiButton: {
- width: '12.5%',
- aspectRatio: 1,
- alignItems: 'center',
- justifyContent: 'center',
- borderRadius: 8,
- },
- emoji: {
- fontSize: 24,
- },
-});
-
-export const settingsStyles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#111111',
- },
- header: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 20,
- borderBottomWidth: 1,
- borderBottomColor: '#00f3ff33',
- },
- title: {
- color: '#00f3ff',
- fontSize: 20,
- fontWeight: 'bold',
- marginLeft: 12,
- },
- section: {
- padding: 20,
- },
- sectionTitle: {
- color: '#bf00ff',
- fontSize: 16,
- marginBottom: 16,
- },
- option: {
- flexDirection: 'row',
- alignItems: 'center',
- padding: 12,
- marginBottom: 12,
- backgroundColor: '#1a1a1a',
- borderRadius: 12,
- borderWidth: 1,
- borderColor: '#333',
- },
- optionText: {
- color: '#ffffff',
- fontSize: 16,
- marginLeft: 12,
- flex: 1,
- },
- activeIndicator: {
- width: 8,
- height: 8,
- borderRadius: 4,
- marginLeft: 8,
- },
- effectPreview: {
- padding: 20,
- },
- previewTitle: {
- color: '#00f3ff',
- fontSize: 16,
- marginBottom: 12,
- },
- previewContent: {
- padding: 20,
- backgroundColor: '#1a1a1a',
- borderRadius: 12,
- alignItems: 'center',
- borderWidth: 1,
- borderColor: '#00f3ff33',
- },
- previewText: {
- color: '#00f3ff',
- fontSize: 24,
- fontWeight: 'bold',
- },
-});
-
-// Add animation helpers
-export const pulseAnimation = {
- 0: {
- opacity: 1,
- scale: 1,
- },
- 0.5: {
- opacity: 0.7,
- scale: 1.05,
- },
- 1: {
- opacity: 1,
- scale: 1,
- },
-};
-
-export const neonGlow = {
- shadowColor: '#00f3ff',
- shadowOffset: { width: 0, height: 0 },
- shadowOpacity: 0.8,
- shadowRadius: 15,
-};
diff --git a/src/index.tsx b/src/index.tsx
index 6b8c9e7..d90a135 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,28 +1,12 @@
-// app/index.tsx
-import {
- View,
- SafeAreaView,
- TouchableOpacity,
- ScrollView,
- Text,
- Linking,
- StyleSheet,
-} from 'react-native';
-import { useNavigation, useRoute } from '@react-navigation/native';
-import { createStackNavigator } from '@react-navigation/stack';
-import { globalStyles } from '../styles';
-import { styled } from 'nativewind';
+import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import AuthenticationScreen from './authentication';
-import {Chat} from './chat';
+import { Chat } from './chat';
import MailPage from './mail';
import DashboardPage from './dashboard';
import TaskPage from './tasks';
import TemplatesPage from './templates';
import DriveScreen from './drive';
-import SyncPage from './sync/page'; // Add this import
-
-const Stack = createStackNavigator();
-const StyledView = styled(View);
+import SyncPage from './sync/page';
const examples = [
{ name: "Home", href: "authentication" },
@@ -33,204 +17,82 @@ const examples = [
{ name: "Tasks", href: "tasks" },
{ name: "Meet", href: "meet" },
{ name: "Templates", href: "templates" },
- { name: "Settings", href: "sync" }, // Changed from "settings" to "sync"
+ { name: "Settings", href: "sync" },
{ name: "Help", href: "help" },
];
-interface ExamplesNavProps {
- style?: any;
-}
-
-const ExamplesNav = ({ style }: ExamplesNavProps) => {
- const navigation = useNavigation();
- return (
-
-
-
- {examples.map((example) => (
- navigation.navigate(example.href)}
- style={styles.exampleButton}
- >
-
- {example.name}
-
-
- ))}
-
-
-
- );
-};
-
-interface ExampleCodeLinkProps {
- pathname: string;
-}
-
-const ExampleCodeLink = ({ pathname }: ExampleCodeLinkProps) => {
- const example = examples.find((example) => pathname.startsWith(example.href));
-
- if (!example?.code) {
- return null;
- }
+const ExamplesNav = () => {
+ const location = useLocation();
+ const navigate = useNavigate();
return (
- Linking.openURL(example.code)}
- style={styles.exampleCodeLink}
- >
- View code
- â
-
+
+
+ {examples.map((example) => (
+ navigate(example.href)}
+ className={`example-button ${
+ location.pathname.includes(example.href) ? 'active' : ''
+ }`}
+ >
+ {example.name}
+
+ ))}
+
+
);
};
-const navigatorOptions = {
- headerShown: false,
- cardOverlayEnabled: false,
- animation: 'fade',
- presentation: 'modal'
-};
-const WrapWithExampleCodeLink = (WrappedComponent) => {
- return (props) => {
- const route = useRoute();
- return (
-
-
-
-
- );
- };
-};
-
-function RootLayout() {
+export function RootLayout() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- Meet Screen (Placeholder) }
- options={{ title: "Meet" }}
- />
-
-
-
-
-
-
-
+
+
+
+
+
+
+ >
+ } />
+
+
+
+ >
+ } />
+
+
+
+ >
+ } />
+
+
+
+ >
+ } />
+ } />
+
+
+
+ >
+ } />
+ Meet Screen (Placeholder) } />
+
+
+
+ >
+ } />
+ } />
+
+
+
);
}
-const styles = StyleSheet.create({
- examplesNavContainer: {
- backgroundColor: '#ffffff',
- borderRadius: 8,
- padding: 4,
- marginHorizontal: 16,
- marginTop: 16,
- shadowColor: "#000",
- shadowOffset: {
- width: 0,
- height: 2,
- },
- shadowOpacity: 0.1,
- shadowRadius: 4,
- elevation: 3,
- },
- examplesNavInner: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- exampleButton: {
- paddingHorizontal: 12,
- paddingVertical: 6,
- borderRadius: 6,
- marginRight: 4,
- },
- exampleButtonActive: {
- backgroundColor: '#f3f4f6',
- },
- exampleButtonText: {
- fontSize: 14,
- color: '#6b7280',
- },
- exampleButtonTextActive: {
- fontWeight: '500',
- color: '#6366f1',
- },
- exampleCodeLink: {
- position: 'absolute',
- right: 0,
- top: 0,
- flexDirection: 'row',
- alignItems: 'center',
- borderRadius: 8,
- padding: 8,
- },
- exampleCodeLinkText: {
- fontSize: 14,
- fontWeight: '500',
- marginRight: 4,
- color: '#6366f1',
- },
- exampleCodeLinkArrow: {
- fontSize: 14,
- color: '#6366f1',
- },
- logo: {
- marginLeft: 8,
- marginRight: 12,
- borderRadius: 8,
- },
-});
-
-export default RootLayout;
\ No newline at end of file
+ export default RootLayout;
\ No newline at end of file
diff --git a/src/styles/chat.css b/src/styles/chat.css
new file mode 100644
index 0000000..fcce2ba
--- /dev/null
+++ b/src/styles/chat.css
@@ -0,0 +1,112 @@
+.chat-window {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ background-color: #111;
+}
+
+.chat-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ background-color: #111;
+ border-bottom: 1px solid #333;
+}
+
+.header-content {
+ display: flex;
+ flex-direction: column;
+}
+
+.header-title {
+ color: white;
+ font-size: 1.25rem;
+ font-weight: bold;
+ margin: 0;
+}
+
+.header-subtitle {
+ color: #00f3ff;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
+
+.message-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 1rem;
+}
+
+.message-container {
+ max-width: 70%;
+ margin-bottom: 0.5rem;
+ padding: 0.75rem;
+ border-radius: 0.75rem;
+}
+
+.user-message {
+ align-self: flex-end;
+ background-color: rgba(0, 243, 255, 0.1);
+ border: 1px solid #00f3ff;
+}
+
+.bot-message {
+ align-self: flex-start;
+ background-color: rgba(191, 0, 255, 0.1);
+ border: 1px solid #bf00ff;
+}
+
+.message-text {
+ color: white;
+ margin: 0;
+}
+
+.message-time {
+ color: #666;
+ font-size: 0.75rem;
+ margin-top: 0.25rem;
+}
+
+.input-container {
+ display: flex;
+ align-items: center;
+ padding: 1rem;
+ border-top: 1px solid #333;
+ background-color: #111;
+}
+
+.chat-input {
+ flex: 1;
+ min-height: 2.5rem;
+ max-height: 6rem;
+ padding: 0.5rem 1rem;
+ margin: 0 0.5rem;
+ background-color: #1a1a1a;
+ color: white;
+ border: 1px solid #333;
+ border-radius: 1.25rem;
+ resize: none;
+}
+
+.icon-button {
+ background: none;
+ border: none;
+ color: #00f3ff;
+ cursor: pointer;
+ padding: 0.5rem;
+}
+
+.send-button {
+ background-color: rgba(0, 243, 255, 0.1);
+ border: 1px solid #00f3ff;
+ border-radius: 50%;
+ padding: 0.5rem;
+ cursor: pointer;
+}
+
+.icon {
+ width: 1.5rem;
+ height: 1.5rem;
+ fill: currentColor;
+}
diff --git a/src/styles/layout.css b/src/styles/layout.css
new file mode 100644
index 0000000..cd32b60
--- /dev/null
+++ b/src/styles/layout.css
@@ -0,0 +1,24 @@
+.chat-layout {
+ display: flex;
+ height: 100vh;
+ background-color: #111;
+}
+
+.sidebar {
+ width: 18rem;
+ border-right: 1px solid #333;
+}
+
+.main-content {
+ flex: 1;
+ display: flex;
+}
+
+.projector {
+ width: 40%;
+ border-right: 1px solid #333;
+}
+
+.chat-area {
+ flex: 1;
+}
diff --git a/src/styles/projector.css b/src/styles/projector.css
new file mode 100644
index 0000000..d6ae6be
--- /dev/null
+++ b/src/styles/projector.css
@@ -0,0 +1,45 @@
+.projector-container {
+ height: 100%;
+ padding: 1rem;
+ background-color: #111;
+}
+
+.image-container, .video-container {
+ width: 100%;
+ height: 100%;
+ background-color: #111;
+ border-radius: 0.5rem;
+ overflow: hidden;
+}
+
+.projector-image, .projector-video {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.markdown-container {
+ height: 100%;
+ padding: 1rem;
+ color: white;
+ background-color: #111;
+ border-radius: 0.5rem;
+ overflow-y: auto;
+}
+
+.markdown-container h1,
+.markdown-container h2,
+.markdown-container h3 {
+ color: #00f3ff;
+}
+
+.markdown-container a {
+ color: #00f3ff;
+ text-decoration: none;
+}
+
+.markdown-container pre {
+ background-color: #1a1a1a;
+ padding: 1rem;
+ border-radius: 0.25rem;
+}
diff --git a/src/styles/selector.css b/src/styles/selector.css
new file mode 100644
index 0000000..e054f85
--- /dev/null
+++ b/src/styles/selector.css
@@ -0,0 +1,87 @@
+.selector-container {
+ height: 100%;
+ background-color: #111;
+}
+
+.selector-header {
+ padding: 1rem;
+ border-bottom: 1px solid #333;
+}
+
+.selector-logo {
+ height: 2.5rem;
+}
+
+.search-container {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem;
+ border-bottom: 1px solid #333;
+}
+
+.search-icon {
+ width: 1.25rem;
+ height: 1.25rem;
+ margin-right: 0.5rem;
+ color: #00f3ff;
+}
+
+.search-input {
+ flex: 1;
+ background: none;
+ border: none;
+ color: white;
+ font-size: 1rem;
+}
+
+.search-input::placeholder {
+ color: #666;
+}
+
+.selector-list {
+ height: calc(100% - 7rem);
+ overflow-y: auto;
+}
+
+.selector-item {
+ display: flex;
+ padding: 1rem;
+ border-bottom: 1px solid #333;
+ cursor: pointer;
+}
+
+.selector-item:hover {
+ background-color: #1a1a1a;
+}
+
+.selector-avatar {
+ width: 3rem;
+ height: 3rem;
+ border-radius: 50%;
+ background-color: #1a1a1a;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid #00f3ff;
+ color: #00f3ff;
+ font-size: 1.25rem;
+ font-weight: bold;
+}
+
+.item-content {
+ margin-left: 1rem;
+ flex: 1;
+}
+
+.item-title {
+ color: white;
+ font-size: 1rem;
+ font-weight: bold;
+ margin: 0;
+}
+
+.item-subtitle {
+ color: #666;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
diff --git a/src/styles/ui.css b/src/styles/ui.css
new file mode 100644
index 0000000..f2e1ae9
--- /dev/null
+++ b/src/styles/ui.css
@@ -0,0 +1,137 @@
+.emoji-picker-modal {
+ position: fixed;
+ bottom: 5rem;
+ right: 2rem;
+ width: 20rem;
+ max-height: 25rem;
+ background-color: rgba(0, 0, 0, 0.95);
+ border: 1px solid #00f3ff;
+ border-radius: 0.75rem;
+ box-shadow: 0 0 1rem rgba(0, 243, 255, 0.5);
+ z-index: 1000;
+ overflow: hidden;
+}
+
+.emoji-picker-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ border-bottom: 1px solid rgba(0, 243, 255, 0.2);
+}
+
+.emoji-picker-header h3 {
+ color: #00f3ff;
+ margin: 0;
+}
+
+.close-button {
+ background: none;
+ border: none;
+ color: #00f3ff;
+ cursor: pointer;
+}
+
+.emoji-picker-content {
+ padding: 1rem;
+ overflow-y: auto;
+ max-height: calc(25rem - 3.5rem);
+}
+
+.emoji-category {
+ margin-bottom: 1.5rem;
+}
+
+.category-title {
+ color: #bf00ff;
+ font-size: 0.875rem;
+ margin-bottom: 0.75rem;
+}
+
+.emoji-grid {
+ display: grid;
+ grid-template-columns: repeat(8, 1fr);
+ gap: 0.5rem;
+}
+
+.emoji-button {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0.25rem;
+ border-radius: 0.25rem;
+}
+
+.emoji-button:hover {
+ background-color: rgba(0, 243, 255, 0.1);
+}
+
+.settings-panel {
+ background-color: #111;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ border: 1px solid #333;
+}
+
+.settings-option {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem;
+ margin-bottom: 0.5rem;
+ background-color: #1a1a1a;
+ border-radius: 0.5rem;
+ border: 1px solid #333;
+}
+
+.option-text {
+ flex: 1;
+ color: white;
+ margin-left: 0.75rem;
+}
+
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 3rem;
+ height: 1.5rem;
+}
+
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #333;
+ transition: .4s;
+ border-radius: 1.5rem;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 1.1rem;
+ width: 1.1rem;
+ left: 0.2rem;
+ bottom: 0.2rem;
+ background-color: #666;
+ transition: .4s;
+ border-radius: 50%;
+}
+
+input:checked + .slider {
+ background-color: rgba(0, 243, 255, 0.2);
+}
+
+input:checked + .slider:before {
+ transform: translateX(1.5rem);
+ background-color: #00f3ff;
+}