From 05559b8b1d0153422222448fc15767189890bbeb Mon Sep 17 00:00:00 2001 From: Aaron Elijah Mars Date: Tue, 15 Jul 2025 20:15:02 +0200 Subject: [PATCH] styling features --- README.md | 25 ++- opendia-extension/background.js | 68 ++++++ opendia-extension/content.js | 371 ++++++++++++++++++++++++++++++++ opendia-mcp/server.js | 118 ++++++++++ 4 files changed, 581 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a26525..6f17d24 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,13 @@ Perfect for **Cursor users** who want to automate their local testing and develo - **"Monitor this webpage and notify me when the content changes"** - **"Automatically bookmark interesting articles I'm reading"** +### šŸŽØ Visual Customization & Fun +- **"Apply a cyberpunk theme to this documentation site to make it more engaging"** +- **"Make this page dark mode with green text for late-night reading"** +- **"Add rainbow party effects to celebrate finishing this project"** +- **"Transform this boring form with a retro 80s theme while I fill it out"** +- **"Use high contrast styling so I can read this better"** + ## ⚔ Quick Start ### 1. Install the Browser Extension @@ -152,7 +159,7 @@ ngrok config add-authtoken YOUR_TOKEN_HERE ## šŸ› ļø Capabilities -OpenDia gives AI models **17 powerful browser tools**: +OpenDia gives AI models **18 powerful browser tools**: ### šŸŽÆ Smart Page Understanding - **Analyze any webpage** - AI automatically finds buttons, forms, and interactive elements @@ -180,6 +187,13 @@ OpenDia gives AI models **17 powerful browser tools**: - **Natural interactions** - Mimics human behavior to avoid triggering security measures - **Reliable automation** - Works consistently even on sites that block typical automation tools +### šŸŽØ Page Styling & Customization +- **Transform any website** - Apply fun themes, custom colors, and visual effects +- **Preset themes** - Dark hacker, retro 80s, rainbow party, minimalist zen, and more +- **AI mood styling** - Describe a mood and get matching visual design +- **Interactive effects** - Matrix rain, floating particles, neon glow, and cursor trails +- **Accessibility themes** - High contrast and readable designs for better visibility + ## šŸ’¬ Example Prompts to Try Once everything is set up, try asking your AI: @@ -202,6 +216,15 @@ Once everything is set up, try asking your AI: **Personal Assistant:** > *"Find that GitHub repo I was looking at yesterday about React components and bookmark it for later"* +**Page Styling & Fun:** +> *"Apply a dark hacker theme to this page to make it look more interesting"* + +> *"Make this boring documentation page feel like a cozy coffee shop"* + +> *"Add some matrix rain effects to this page for 30 seconds for a cool screenshot"* + +> *"Transform this page with a high contrast theme for better readability"* + ## šŸ—ļø How It Works ```mermaid diff --git a/opendia-extension/background.js b/opendia-extension/background.js index 807933d..eac2541 100644 --- a/opendia-extension/background.js +++ b/opendia-extension/background.js @@ -766,6 +766,71 @@ function getAvailableTools() { } } }, + { + name: "page_style", + description: "šŸŽØ Transform page appearance with themes, colors, fonts, and fun effects! Apply preset themes like 'dark_hacker', 'retro_80s', or create custom styles. Perfect for making boring pages fun or improving readability.", + inputSchema: { + type: "object", + examples: [ + { mode: "preset", theme: "dark_hacker" }, + { mode: "custom", background: "#000", text_color: "#00ff00", font: "monospace" }, + { mode: "ai_mood", mood: "cozy coffee shop vibes", intensity: "strong" }, + { mode: "effect", effect: "matrix_rain", duration: 30 } + ], + properties: { + mode: { + type: "string", + enum: ["preset", "custom", "ai_mood", "effect", "reset"], + description: "Styling mode to use" + }, + theme: { + type: "string", + enum: ["dark_hacker", "retro_80s", "rainbow_party", "minimalist_zen", "high_contrast", "cyberpunk", "pastel_dream", "newspaper"], + description: "Preset theme name (when mode=preset)" + }, + background: { + type: "string", + description: "Background color/gradient" + }, + text_color: { + type: "string", + description: "Text color" + }, + font: { + type: "string", + description: "Font family" + }, + font_size: { + type: "string", + description: "Font size (e.g., '1.2em', '16px')" + }, + mood: { + type: "string", + description: "Describe desired mood/feeling (when mode=ai_mood)" + }, + intensity: { + type: "string", + enum: ["subtle", "medium", "strong"], + default: "medium" + }, + effect: { + type: "string", + enum: ["matrix_rain", "floating_particles", "cursor_trail", "neon_glow", "typing_effect"] + }, + duration: { + type: "number", + description: "Effect duration in seconds", + default: 10 + }, + remember: { + type: "boolean", + description: "Remember this style for this website", + default: false + } + }, + required: ["mode"] + } + }, ]; } @@ -840,6 +905,9 @@ async function handleMCPRequest(message) { case "get_page_links": result = await sendToContentScript('get_page_links', params, params.tab_id); break; + case "page_style": + result = await sendToContentScript('page_style', params, params.tab_id); + break; default: throw new Error(`Unknown method: ${method}`); } diff --git a/opendia-extension/content.js b/opendia-extension/content.js index 6496d5e..9a98cd4 100644 --- a/opendia-extension/content.js +++ b/opendia-extension/content.js @@ -283,6 +283,9 @@ class BrowserAutomation { case "page_scroll": result = await this.scrollPage(data); break; + case "page_style": + result = await this.handlePageStyle(data); + break; case "ping": // Health check for background tab content script readiness result = { status: "ready", timestamp: Date.now(), url: window.location.href }; @@ -2507,7 +2510,375 @@ class BrowserAutomation { }; } } + + // šŸŽØ Page Styling System + async handlePageStyle(data) { + const { mode, theme, background, text_color, font, font_size, mood, intensity, effect, duration, remember } = data; + + // Remove existing custom styles + const existingStyle = document.getElementById('opendia-custom-style'); + if (existingStyle) existingStyle.remove(); + + let css = ''; + let description = ''; + + try { + switch (mode) { + case 'preset': + const themeData = THEME_PRESETS[theme]; + if (!themeData) throw new Error(`Unknown theme: ${theme}`); + css = themeData.css; + description = `Applied ${themeData.name} theme`; + break; + + case 'custom': + css = this.buildCustomCSS({ background, text_color, font, font_size }); + description = 'Applied custom styling'; + break; + + case 'ai_mood': + css = this.generateMoodCSS(mood, intensity); + description = `Applied AI-generated style for mood: "${mood}"`; + break; + + case 'effect': + css = this.applyEffect(effect, duration); + description = `Applied ${effect} effect for ${duration}s`; + break; + + case 'reset': + // CSS already removed above + description = 'Reset page to original styling'; + break; + + default: + throw new Error(`Unknown styling mode: ${mode}`); + } + + if (css) { + const styleElement = document.createElement('style'); + styleElement.id = 'opendia-custom-style'; + styleElement.textContent = css; + document.head.appendChild(styleElement); + } + + // Remember preference if requested + if (remember && mode !== 'reset') { + const domain = window.location.hostname; + chrome.storage.local.set({ [`style_${domain}`]: { mode, theme, css } }); + } + + return { + success: true, + description, + applied_css: css.length, + mode, + theme: theme || 'custom', + remember_enabled: remember, + effect_duration: duration, + mood, + intensity + }; + + } catch (error) { + return { + success: false, + error: error.message, + mode, + theme, + applied_css: 0 + }; + } + } + + buildCustomCSS({ background, text_color, font, font_size }) { + let css = ''; + + if (background || text_color || font || font_size) { + css += '* { '; + if (background) css += `background: ${background} !important; `; + if (text_color) css += `color: ${text_color} !important; `; + if (font) css += `font-family: ${font} !important; `; + if (font_size) css += `font-size: ${font_size} !important; `; + css += '}'; + } + + return css; + } + + generateMoodCSS(mood, intensity) { + const moodMap = { + 'cozy coffee shop': { + background: '#2c1810', + text: '#f4e4bc', + accent: '#d4af37', + font: 'Georgia, serif' + }, + 'energetic': { + background: 'linear-gradient(45deg, #ff6b35, #f7931e)', + text: '#ffffff', + effects: 'animation: energyPulse 1s infinite;' + }, + 'calm ocean': { + background: 'linear-gradient(to bottom, #87ceeb, #4682b4)', + text: '#ffffff', + effects: 'animation: gentleWave 4s ease-in-out infinite;' + }, + 'dark professional': { + background: '#1a1a1a', + text: '#e0e0e0', + accent: '#0066cc' + }, + 'warm sunset': { + background: 'linear-gradient(to bottom, #ff7e5f, #feb47b)', + text: '#ffffff' + } + }; + + const style = moodMap[mood.toLowerCase()] || moodMap['cozy coffee shop']; + return this.buildMoodCSS(style, intensity); + } + + buildMoodCSS(style, intensity) { + const opacity = intensity === 'subtle' ? '0.3' : intensity === 'medium' ? '0.6' : '0.9'; + + let css = ` + body { + background: ${style.background} !important; + color: ${style.text} !important; + ${style.font ? `font-family: ${style.font} !important;` : ''} + } + + * { + color: ${style.text} !important; + } + + a { + color: ${style.accent || style.text} !important; + } + `; + + if (style.effects) { + css += style.effects; + } + + // Add animation keyframes if needed + if (style.effects && style.effects.includes('energyPulse')) { + css += ` + @keyframes energyPulse { + 0%, 100% { filter: hue-rotate(0deg); } + 50% { filter: hue-rotate(180deg); } + } + `; + } + + if (style.effects && style.effects.includes('gentleWave')) { + css += ` + @keyframes gentleWave { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + } + `; + } + + return css; + } + + applyEffect(effect, duration) { + const effects = { + matrix_rain: ` + body::after { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: url('data:image/svg+xml;utf8,0101'); + animation: matrixFall 2s linear infinite; + pointer-events: none; + z-index: 9999; + opacity: 0.7; + } + @keyframes matrixFall { + from { transform: translateY(-100px); } + to { transform: translateY(100vh); } + } + `, + floating_particles: ` + body::before { + content: '✨ 🌟 ⭐ šŸ’«'; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + animation: floatParticles 6s ease-in-out infinite; + pointer-events: none; + z-index: 9999; + font-size: 20px; + } + @keyframes floatParticles { + 0%, 100% { transform: translateY(100vh) rotate(0deg); } + 50% { transform: translateY(-100px) rotate(180deg); } + } + `, + cursor_trail: ` + body { + cursor: url('data:image/svg+xml;utf8,'), auto !important; + } + `, + neon_glow: ` + * { + text-shadow: 0 0 10px #00ffff, 0 0 20px #00ffff, 0 0 30px #00ffff !important; + } + + a, button { + box-shadow: 0 0 15px #ff00ff !important; + } + `, + typing_effect: ` + * { + animation: typewriter 2s steps(40, end) infinite !important; + } + @keyframes typewriter { + from { width: 0; } + to { width: 100%; } + } + ` + }; + + const css = effects[effect] || ''; + + // Auto-remove effect after duration + if (duration && duration > 0) { + setTimeout(() => { + const effectStyle = document.getElementById('opendia-custom-style'); + if (effectStyle) effectStyle.remove(); + }, duration * 1000); + } + + return css; + } } +// Theme Presets Database +const THEME_PRESETS = { + "dark_hacker": { + name: "šŸ–¤ Dark Hacker", + css: ` + * { + background: #0a0a0a !important; + color: #00ff00 !important; + font-family: 'Courier New', monospace !important; + } + a { color: #00ffff !important; } + body::before { + content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%; + background: url('data:image/svg+xml;utf8,01010101'); + opacity: 0.1; pointer-events: none; z-index: -1; + } + ` + }, + "retro_80s": { + name: "šŸ“¼ Retro 80s", + css: ` + * { + background: linear-gradient(45deg, #ff0080, #8000ff) !important; + color: #ffffff !important; + font-family: 'Arial Black', sans-serif !important; + text-shadow: 2px 2px 4px #000000 !important; + } + body { animation: retroPulse 2s infinite; } + @keyframes retroPulse { 0%, 100% { filter: hue-rotate(0deg); } 50% { filter: hue-rotate(180deg); } } + ` + }, + "rainbow_party": { + name: "🌈 Rainbow Party", + css: ` + body { + background: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) !important; + background-size: 400% 400% !important; + animation: rainbowShift 3s ease infinite !important; + } + @keyframes rainbowShift { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + } + * { color: white !important; text-shadow: 1px 1px 2px black !important; } + ` + }, + "minimalist_zen": { + name: "🧘 Minimalist Zen", + css: ` + * { + background: #f8f8f8 !important; + color: #333333 !important; + font-family: 'Georgia', serif !important; + line-height: 1.6 !important; + } + body { max-width: 800px; margin: 0 auto; padding: 20px; } + ` + }, + "high_contrast": { + name: "šŸ” High Contrast", + css: ` + * { + background: #000000 !important; + color: #ffffff !important; + font-family: Arial, sans-serif !important; + font-weight: bold !important; + } + a { color: #ffff00 !important; } + ` + }, + "cyberpunk": { + name: "šŸ¤– Cyberpunk", + css: ` + * { + background: #0d1117 !important; + color: #ff006e !important; + font-family: 'Courier New', monospace !important; + } + a { color: #00ffff !important; } + body { + background-image: + linear-gradient(90deg, transparent 79px, #abced4 79px, #abced4 81px, transparent 81px), + linear-gradient(#eee .1em, transparent .1em); + background-size: 81px 1.2em; + } + ` + }, + "pastel_dream": { + name: "🌸 Pastel Dream", + css: ` + * { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; + color: #2c3e50 !important; + font-family: 'Comic Sans MS', cursive !important; + } + body { filter: sepia(20%) saturate(80%); } + ` + }, + "newspaper": { + name: "šŸ“° Newspaper", + css: ` + * { + background: #ffffff !important; + color: #000000 !important; + font-family: 'Times New Roman', serif !important; + line-height: 1.4 !important; + } + body { + column-count: 2; + column-gap: 2em; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + } + ` + } +}; + // Initialize the automation system const browserAutomation = new BrowserAutomation(); diff --git a/opendia-mcp/server.js b/opendia-mcp/server.js index 2c89cd6..f4edbab 100755 --- a/opendia-mcp/server.js +++ b/opendia-mcp/server.js @@ -495,6 +495,9 @@ function formatToolResult(toolName, result) { case "element_get_state": return formatElementStateResult(result, metadata); + case "page_style": + return formatPageStyleResult(result, metadata); + default: // Legacy tools or unknown tools return JSON.stringify(result, null, 2); @@ -949,6 +952,56 @@ function formatElementStateResult(result, metadata) { ${JSON.stringify(metadata, null, 2)}`; } +function formatPageStyleResult(result, metadata) { + const successIcon = result.success ? 'āœ…' : 'āŒ'; + const statusText = result.success ? 'successfully applied' : 'failed to apply'; + + let summary = `šŸŽØ Page styling ${statusText}\n\n`; + + summary += `šŸ“„ **Operation Details:**\n`; + summary += `• **Mode:** ${result.mode || 'Unknown'}\n`; + + if (result.theme) { + summary += `• **Theme:** ${result.theme}\n`; + } + + if (result.applied_css !== undefined) { + summary += `• **CSS Applied:** ${result.applied_css} characters\n`; + } + + if (result.description) { + summary += `• **Result:** ${result.description}\n`; + } + + if (result.remember_enabled) { + summary += `• **Saved:** Style preferences saved for this domain\n`; + } + + if (result.effect_duration) { + summary += `• **Effect Duration:** ${result.effect_duration} seconds\n`; + } + + if (result.mood) { + summary += `• **Mood Applied:** "${result.mood}"\n`; + } + + if (result.intensity) { + summary += `• **Intensity:** ${result.intensity}\n`; + } + + if (!result.success && result.error) { + summary += `\nāŒ **Error:** ${result.error}\n`; + } + + if (result.warning) { + summary += `\nāš ļø **Warning:** ${result.warning}\n`; + } + + summary += `\nšŸ’” **Tip:** Use mode="reset" to restore original page styling`; + + return `${summary}\n\n${JSON.stringify(metadata, null, 2)}`; +} + // Enhanced fallback tools when extension is not connected function getFallbackTools() { return [ @@ -1394,6 +1447,71 @@ function getFallbackTools() { } } }, + { + name: "page_style", + description: "šŸŽØ Transform page appearance with themes, colors, fonts, and fun effects! Apply preset themes like 'dark_hacker', 'retro_80s', or create custom styles. Perfect for making boring pages fun or improving readability.", + inputSchema: { + type: "object", + examples: [ + { mode: "preset", theme: "dark_hacker" }, + { mode: "custom", background: "#000", text_color: "#00ff00", font: "monospace" }, + { mode: "ai_mood", mood: "cozy coffee shop vibes", intensity: "strong" }, + { mode: "effect", effect: "matrix_rain", duration: 30 } + ], + properties: { + mode: { + type: "string", + enum: ["preset", "custom", "ai_mood", "effect", "reset"], + description: "Styling mode to use" + }, + theme: { + type: "string", + enum: ["dark_hacker", "retro_80s", "rainbow_party", "minimalist_zen", "high_contrast", "cyberpunk", "pastel_dream", "newspaper"], + description: "Preset theme name (when mode=preset)" + }, + background: { + type: "string", + description: "Background color/gradient" + }, + text_color: { + type: "string", + description: "Text color" + }, + font: { + type: "string", + description: "Font family" + }, + font_size: { + type: "string", + description: "Font size (e.g., '1.2em', '16px')" + }, + mood: { + type: "string", + description: "Describe desired mood/feeling (when mode=ai_mood)" + }, + intensity: { + type: "string", + enum: ["subtle", "medium", "strong"], + default: "medium" + }, + effect: { + type: "string", + enum: ["matrix_rain", "floating_particles", "cursor_trail", "neon_glow", "typing_effect"] + }, + duration: { + type: "number", + description: "Effect duration in seconds", + default: 10 + }, + remember: { + type: "boolean", + description: "Remember this style for this website", + default: false + } + }, + required: ["mode"] + } + }, ]; }