From 8f198b393b7e88738ea762b23b0440783777f77b Mon Sep 17 00:00:00 2001 From: sirpiglr <49359077-sirpiglr@users.noreply.replit.com> Date: Sat, 13 Dec 2025 09:46:50 +0000 Subject: [PATCH] Add mobile responsiveness and AI chat functionality Introduces a mobile-friendly navigation system for the dashboard with a slide-out sidebar and overlay, alongside a new AI chat command utilizing the OpenAI API for image generation and conversational interactions. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: a4c8a9aa-aa7e-4928-95e7-0c95279d5dc6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/3tJ1Z1J Replit-Helium-Checkpoint-Created: true --- .replit | 1 + aethex-bot/commands/ai.js | 165 +++++++++++++++++++++++++++++++ aethex-bot/public/dashboard.html | 103 ++++++++++++++++++- package-lock.json | 22 +++++ package.json | 1 + 5 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 aethex-bot/commands/ai.js diff --git a/.replit b/.replit index 9585c0f..5fefc2d 100644 --- a/.replit +++ b/.replit @@ -12,6 +12,7 @@ deploymentTarget = "gce" [agent] expertMode = true +integrations = ["javascript_openai_ai_integrations:1.0.0"] [[ports]] localPort = 5000 diff --git a/aethex-bot/commands/ai.js b/aethex-bot/commands/ai.js new file mode 100644 index 0000000..d9955d0 --- /dev/null +++ b/aethex-bot/commands/ai.js @@ -0,0 +1,165 @@ +const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); +const OpenAI = require('openai'); + +const openai = new OpenAI({ + apiKey: process.env.AI_INTEGRATIONS_OPENAI_API_KEY, + baseURL: process.env.AI_INTEGRATIONS_OPENAI_BASE_URL +}); + +const conversationHistory = new Map(); +const MAX_HISTORY = 10; + +module.exports = { + data: new SlashCommandBuilder() + .setName('ai') + .setDescription('Chat with AeThex AI assistant') + .addSubcommand(sub => + sub.setName('chat') + .setDescription('Send a message to the AI') + .addStringOption(opt => + opt.setName('message') + .setDescription('Your message to the AI') + .setRequired(true))) + .addSubcommand(sub => + sub.setName('clear') + .setDescription('Clear your conversation history')) + .addSubcommand(sub => + sub.setName('image') + .setDescription('Generate an image with AI') + .addStringOption(opt => + opt.setName('prompt') + .setDescription('Describe the image you want to generate') + .setRequired(true))), + + async execute(interaction, supabase, client) { + const subcommand = interaction.options.getSubcommand(); + + if (subcommand === 'clear') { + conversationHistory.delete(interaction.user.id); + return interaction.reply({ + embeds: [new EmbedBuilder() + .setColor(0x5865f2) + .setDescription('Your conversation history has been cleared.')], + ephemeral: true + }); + } + + if (subcommand === 'image') { + await interaction.deferReply(); + const prompt = interaction.options.getString('prompt'); + + try { + const response = await openai.images.generate({ + model: 'gpt-image-1', + prompt: prompt, + n: 1, + size: '1024x1024' + }); + + const imageUrl = response.data[0]?.url || response.data[0]?.b64_json; + + if (!imageUrl) { + return interaction.editReply({ + embeds: [new EmbedBuilder() + .setColor(0xff0000) + .setDescription('Failed to generate image. Please try again.')] + }); + } + + const embed = new EmbedBuilder() + .setColor(0x5865f2) + .setTitle('AI Generated Image') + .setDescription(`**Prompt:** ${prompt}`) + .setImage(imageUrl) + .setFooter({ text: `Requested by ${interaction.user.username}`, iconURL: interaction.user.displayAvatarURL() }) + .setTimestamp(); + + return interaction.editReply({ embeds: [embed] }); + } catch (error) { + console.error('AI image error:', error); + return interaction.editReply({ + embeds: [new EmbedBuilder() + .setColor(0xff0000) + .setDescription('Failed to generate image. Please try again later.')] + }); + } + } + + if (subcommand === 'chat') { + await interaction.deferReply(); + const userMessage = interaction.options.getString('message'); + + try { + let history = conversationHistory.get(interaction.user.id) || []; + + history.push({ + role: 'user', + content: userMessage + }); + + const systemPrompt = `You are AeThex AI, a helpful and friendly assistant for the AeThex Discord community. You help users with questions about Discord, gaming, technology, and general topics. Keep responses concise but helpful. Be friendly and engaging. If asked about AeThex, explain it's a creative technology community focused on gaming, development, and digital arts.`; + + const messages = [ + { role: 'system', content: systemPrompt }, + ...history + ]; + + const completion = await openai.chat.completions.create({ + model: 'gpt-4o-mini', + messages: messages, + max_tokens: 1000, + temperature: 0.7 + }); + + const aiResponse = completion.choices[0]?.message?.content || 'I could not generate a response.'; + + history.push({ + role: 'assistant', + content: aiResponse + }); + + if (history.length > MAX_HISTORY * 2) { + history = history.slice(-MAX_HISTORY * 2); + } + + conversationHistory.set(interaction.user.id, history); + + let displayResponse = aiResponse; + if (displayResponse.length > 4000) { + displayResponse = displayResponse.substring(0, 4000) + '...'; + } + + const embed = new EmbedBuilder() + .setColor(0x5865f2) + .setAuthor({ + name: 'AeThex AI', + iconURL: client.user?.displayAvatarURL() || interaction.guild?.iconURL() + }) + .setDescription(displayResponse) + .setFooter({ + text: `Requested by ${interaction.user.username}`, + iconURL: interaction.user.displayAvatarURL() + }) + .setTimestamp(); + + return interaction.editReply({ embeds: [embed] }); + + } catch (error) { + console.error('AI chat error:', error); + + let errorMessage = 'Something went wrong while processing your request.'; + if (error.message?.includes('rate limit')) { + errorMessage = 'Too many requests. Please wait a moment and try again.'; + } else if (error.message?.includes('quota')) { + errorMessage = 'AI service quota exceeded. Please try again later.'; + } + + return interaction.editReply({ + embeds: [new EmbedBuilder() + .setColor(0xff0000) + .setDescription(errorMessage)] + }); + } + } + } +}; diff --git a/aethex-bot/public/dashboard.html b/aethex-bot/public/dashboard.html index 9415138..ff1a649 100644 --- a/aethex-bot/public/dashboard.html +++ b/aethex-bot/public/dashboard.html @@ -1088,16 +1088,79 @@ gap: 0.75rem; } + .mobile-menu-btn { + display: none; + position: fixed; + top: 1rem; + left: 1rem; + z-index: 200; + width: 44px; + height: 44px; + background: var(--card); + border: 1px solid var(--card-border); + border-radius: 10px; + cursor: pointer; + align-items: center; + justify-content: center; + backdrop-filter: blur(10px); + transition: all 0.2s; + } + + .mobile-menu-btn:hover { + border-color: var(--primary); + background: rgba(99, 102, 241, 0.1); + } + + .mobile-menu-btn svg { + color: var(--foreground); + } + + .sidebar-overlay { + display: none; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + z-index: 90; + backdrop-filter: blur(4px); + } + + .sidebar-overlay.open { + display: block; + } + @media (max-width: 768px) { + .mobile-menu-btn { + display: flex; + } .sidebar { transform: translateX(-100%); z-index: 100; - transition: transform 0.3s; + transition: transform 0.3s ease; + } + .sidebar.open { + transform: translateX(0); + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.5); + } + .main { + margin-left: 0; + max-width: 100%; + padding: 1rem; + padding-top: 4.5rem; } - .sidebar.open { transform: translateX(0); } - .main { margin-left: 0; max-width: 100%; padding: 1.5rem; } .stats-grid { grid-template-columns: repeat(2, 1fr); } .page-title { font-size: 1.5rem; } + .page-header { margin-bottom: 1.5rem; } + .form-grid { grid-template-columns: 1fr; } + .achievement-grid { grid-template-columns: 1fr; } + .shop-grid { grid-template-columns: repeat(2, 1fr); } + .tabs { flex-wrap: wrap; } + .modal-content { margin: 1rem; max-height: calc(100vh - 2rem); } + } + + @media (max-width: 480px) { + .stats-grid { grid-template-columns: 1fr; } + .shop-grid { grid-template-columns: 1fr; } + .main { padding: 0.75rem; padding-top: 4.5rem; } } @@ -1115,7 +1178,15 @@