diff --git a/submissions/Calculator/Readme.md b/submissions/Calculator/Readme.md new file mode 100644 index 00000000..5eacaaf2 --- /dev/null +++ b/submissions/Calculator/Readme.md @@ -0,0 +1,3 @@ +# TAB ORG +## Yes a browser tab organiser and more than that +### a tab organiser which also track how much time you spent on the each group along with custom groups and personalization \ No newline at end of file diff --git a/submissions/Calculator/background.js b/submissions/Calculator/background.js new file mode 100644 index 00000000..32b68696 --- /dev/null +++ b/submissions/Calculator/background.js @@ -0,0 +1,501 @@ +class BackgroundTabManager { + constructor() { + this.tabGroups = {}; + this.timeTracking = {}; + this.currentMode = 'work'; + this.activeTabStartTime = {}; + this.timeTrackingInterval = null; + this.init(); + } + + async init() { + try { + await this.loadData(); + this.setupMessageListener(); + this.setupTabListeners(); + this.startTimeTracking(); + } catch (error) { + console.error('Failed to initialize BackgroundTabManager:', error); + } + } + + async loadData() { + try { + const result = await chrome.storage.sync.get(['tabGroups', 'currentMode', 'timeTracking']); + this.tabGroups = result.tabGroups || {}; + this.currentMode = result.currentMode || 'work'; + this.timeTracking = result.timeTracking || {}; + } catch (error) { + console.error('Failed to load data:', error); + this.tabGroups = {}; + this.currentMode = 'work'; + this.timeTracking = {}; + } + } + + async saveData() { + try { + await chrome.storage.sync.set({ + tabGroups: this.tabGroups, + currentMode: this.currentMode, + timeTracking: this.timeTracking + }); + } catch (error) { + console.error('Failed to save data:', error); + } + } + + setupMessageListener() { + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + this.handleMessage(message, sender, sendResponse); + return true; + }); + } + + setupTabListeners() { + chrome.tabs.onActivated.addListener((activeInfo) => { + this.handleTabActivated(activeInfo).catch(error => { + console.error('Error handling tab activation:', error); + }); + }); + + + chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + if (changeInfo.status === 'complete') { + this.updateTabInGroups(tab).catch(error => { + console.error('Error updating tab in groups:', error); + }); + } + }); + + chrome.tabs.onRemoved.addListener((tabId) => { + this.removeTabFromGroups(tabId).catch(error => { + console.error('Error removing tab from groups:', error); + }); + }); + + chrome.windows.onFocusChanged.addListener((windowId) => { + if (windowId !== chrome.windows.WINDOW_ID_NONE) { + this.handleWindowFocusChanged(windowId).catch(error => { + console.error('Error handling window focus change:', error); + }); + } + }); + } + + async handleMessage(message, sender, sendResponse) { + try { + switch (message.action) { + case 'switchMode': + await this.switchMode(message.mode); + sendResponse({ success: true }); + break; + + case 'addTabToGroup': + await this.addTabToGroup(message.tabId, message.groupKey, message.groupName, message.groupColor); + sendResponse({ success: true }); + break; + + case 'openGroupTabs': + await this.openGroupTabs(message.tabs); + sendResponse({ success: true }); + break; + + case 'closeGroupTabs': + await this.closeGroupTabs(message.groupKey); + sendResponse({ success: true }); + break; + + case 'getTimeTracking': + sendResponse({ timeTracking: this.timeTracking }); + break; + + case 'trackPageTime': + await this.trackPageTime(message.url, message.title, message.timeSpent); + sendResponse({ success: true }); + break; + + case 'getTabGroups': + sendResponse({ tabGroups: this.tabGroups, currentMode: this.currentMode }); + break; + + case 'deleteTabGroup': + await this.deleteTabGroup(message.groupKey); + sendResponse({ success: true }); + break; + + default: + sendResponse({ success: false, error: 'Unknown action' }); + } + } catch (error) { + console.error('Error handling message:', error); + sendResponse({ success: false, error: error.message }); + } + } + + async deleteTabGroup(groupKey) { + try { + if (!this.tabGroups[groupKey]) { + console.log(`Group ${groupKey} not found`); + return; + } + + + const tabs = await chrome.tabs.query({ currentWindow: true }); + + + const groups = await chrome.tabGroups.query({}); + const targetGroup = groups.find(group => group.title === this.tabGroups[groupKey].name); + + if (targetGroup) { + + const groupTabs = tabs.filter(tab => tab.groupId === targetGroup.id); + + if (groupTabs.length > 0) { + + const tabIds = groupTabs.map(tab => tab.id); + await chrome.tabs.ungroup(tabIds); + } + } + delete this.tabGroups[groupKey]; + await this.saveData(); + + console.log(`Successfully deleted group: ${groupKey}`); + } catch (error) { + console.error('Error deleting tab group:', error); + throw error; + } + } + + async switchMode(mode) { + try { + this.currentMode = mode; + await this.saveData(); + await this.organizeTabsByMode(mode); + } catch (error) { + console.error('Error switching mode:', error); + throw error; + } + } + + async organizeTabsByMode(mode) { + try { + const tabs = await chrome.tabs.query({ currentWindow: true }); + const modeKeywords = { + work: ['linkedin', 'email', 'slack', 'teams', 'zoom', 'calendar', 'docs', 'sheets', 'office', 'notion'], + code: ['github', 'stackoverflow', 'codepen', 'repl', 'dev.to', 'coding', 'programming', 'vscode', 'ide'], + creativity: ['dribbble', 'behance', 'figma', 'canva', 'adobe', 'design', 'art', 'creative'], + fun: ['youtube', 'netflix', 'gaming', 'reddit', 'social', 'entertainment', 'memes', 'twitter', 'instagram'] + }; + + const keywords = modeKeywords[mode] || []; + + for (const tab of tabs) { + const tabUrl = (tab.url || '').toLowerCase(); + const tabTitle = (tab.title || '').toLowerCase(); + + const isRelevant = keywords.some(keyword => + tabUrl.includes(keyword) || tabTitle.includes(keyword) + ); + + if (isRelevant) { + const groupColors = { + work: '#3b82f6', + code: '#10b981', + creativity: '#8b5cf6', + fun: '#f59e0b' + }; + + await this.addTabToGroup( + tab.id, + mode, + mode.charAt(0).toUpperCase() + mode.slice(1), + groupColors[mode] || '#3b82f6' + ); + } + } + } catch (error) { + console.error('Error organizing tabs by mode:', error); + } + } + + async addTabToGroup(tabId, groupKey, groupName, groupColor) { + try { + const tab = await chrome.tabs.get(tabId); + let groupId = await this.getOrCreateTabGroup(groupName, groupColor); + await chrome.tabs.group({ tabIds: [tabId], groupId: groupId }); + if (!this.tabGroups[groupKey]) { + this.tabGroups[groupKey] = { + tabs: [], + color: groupColor, + name: groupName, + created: Date.now() + }; + } + + const existingIndex = this.tabGroups[groupKey].tabs.findIndex(t => t.id === tabId); + + if (existingIndex === -1) { + this.tabGroups[groupKey].tabs.push({ + id: tabId, + url: tab.url, + title: tab.title, + favIconUrl: tab.favIconUrl, + added: Date.now() + }); + } else { + this.tabGroups[groupKey].tabs[existingIndex] = { + ...this.tabGroups[groupKey].tabs[existingIndex], + url: tab.url, + title: tab.title, + favIconUrl: tab.favIconUrl + }; + } + + await this.saveData(); + } catch (error) { + console.error('Error adding tab to group:', error); + throw error; + } + } + + async getOrCreateTabGroup(groupName, groupColor) { + try { + const groups = await chrome.tabGroups.query({}); + let existingGroup = groups.find(group => group.title === groupName); + + if (existingGroup) { + return existingGroup.id; + } + const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (!activeTab) { + throw new Error('No active tab to create group with'); + } + + const groupId = await chrome.tabs.group({ tabIds: [activeTab.id] }); + + await chrome.tabGroups.update(groupId, { + title: groupName, + color: this.getGroupColor(groupColor) + }); + + return groupId; + } catch (error) { + console.error('Error creating/getting tab group:', error); + throw error; + } + } + + getGroupColor(hexColor) { + const colorMap = { + '#3b82f6': 'blue', + '#10b981': 'green', + '#f59e0b': 'yellow', + '#ef4444': 'red', + '#8b5cf6': 'purple', + '#06b6d4': 'cyan', + '#ec4899': 'pink', + '#64748b': 'grey' + }; + + return colorMap[hexColor] || 'blue'; + } + + async openGroupTabs(tabs) { + try { + for (const tabData of tabs) { + await chrome.tabs.create({ + url: tabData.url, + active: false + }); + } + } catch (error) { + console.error('Error opening group tabs:', error); + throw error; + } + } + + async closeGroupTabs(groupKey) { + try { + if (!this.tabGroups[groupKey]) return; + + const tabIds = this.tabGroups[groupKey].tabs.map(tab => tab.id); + + for (const tabId of tabIds) { + try { + await chrome.tabs.remove(tabId); + } catch (error) { + console.log('Tab already closed:', tabId); + } + } + + this.tabGroups[groupKey].tabs = []; + await this.saveData(); + } catch (error) { + console.error('Error closing group tabs:', error); + throw error; + } + } + + async handleTabActivated(activeInfo) { + try { + const tab = await chrome.tabs.get(activeInfo.tabId); + this.activeTabStartTime[activeInfo.tabId] = Date.now(); + const groupKey = this.findTabGroup(activeInfo.tabId); + if (groupKey) { + this.currentMode = groupKey; + await this.saveData(); + } + } catch (error) { + console.error('Error handling tab activation:', error); + } + } + + findTabGroup(tabId) { + for (const [groupKey, group] of Object.entries(this.tabGroups)) { + if (group.tabs && group.tabs.some(tab => tab.id === tabId)) { + return groupKey; + } + } + return null; + } + + async updateTabInGroups(tab) { + try { + let updated = false; + + for (const [groupKey, group] of Object.entries(this.tabGroups)) { + if (!group.tabs) continue; + + const tabIndex = group.tabs.findIndex(t => t.id === tab.id); + if (tabIndex !== -1) { + this.tabGroups[groupKey].tabs[tabIndex] = { + ...this.tabGroups[groupKey].tabs[tabIndex], + url: tab.url, + title: tab.title, + favIconUrl: tab.favIconUrl, + updated: Date.now() + }; + updated = true; + } + } + + if (updated) { + await this.saveData(); + } + } catch (error) { + console.error('Error updating tab in groups:', error); + } + } + + async removeTabFromGroups(tabId) { + try { + let removed = false; + + for (const [groupKey, group] of Object.entries(this.tabGroups)) { + if (!group.tabs) continue; + + const originalLength = group.tabs.length; + this.tabGroups[groupKey].tabs = group.tabs.filter(tab => tab.id !== tabId); + + if (this.tabGroups[groupKey].tabs.length !== originalLength) { + removed = true; + } + } + delete this.activeTabStartTime[tabId]; + + if (removed) { + await this.saveData(); + } + } catch (error) { + console.error('Error removing tab from groups:', error); + } + } + + async trackPageTime(url, title, timeSpent) { + try { + const domain = new URL(url).hostname; + + if (!this.timeTracking.pages) { + this.timeTracking.pages = {}; + } + + if (!this.timeTracking.pages[domain]) { + this.timeTracking.pages[domain] = { + totalTime: 0, + visits: 0, + title: title + }; + } + + this.timeTracking.pages[domain].totalTime += timeSpent; + this.timeTracking.pages[domain].visits += 1; + this.timeTracking.pages[domain].lastVisit = Date.now(); + + await this.saveData(); + } catch (error) { + console.error('Error tracking page time:', error); + } + } + + startTimeTracking() { + if (this.timeTrackingInterval) { + clearInterval(this.timeTrackingInterval); + } + + + this.timeTrackingInterval = setInterval(async () => { + try { + const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); + + if (activeTab) { + const groupKey = this.findTabGroup(activeTab.id) || this.currentMode; + if (groupKey) { + if (!this.timeTracking.groups) { + this.timeTracking.groups = {}; + } + + this.timeTracking.groups[groupKey] = (this.timeTracking.groups[groupKey] || 0) + 1; + await this.saveData(); + } + } + } catch (error) { + console.error('Error in time tracking interval:', error); + } + }, 60000); + } + + async handleWindowFocusChanged(windowId) { + try { + if (windowId !== chrome.windows.WINDOW_ID_NONE) { + const [activeTab] = await chrome.tabs.query({ active: true, windowId: windowId }); + if (activeTab) { + this.activeTabStartTime[activeTab.id] = Date.now(); + } + } + } catch (error) { + console.error('Error handling window focus change:', error); + } + } + + cleanup() { + if (this.timeTrackingInterval) { + clearInterval(this.timeTrackingInterval); + this.timeTrackingInterval = null; + } + } +} + +let backgroundManager; + +try { + backgroundManager = new BackgroundTabManager(); +} catch (error) { + console.error('Failed to initialize BackgroundTabManager:', error); +} + +chrome.runtime.onSuspend.addListener(() => { + if (backgroundManager) { + backgroundManager.cleanup(); + } +}); \ No newline at end of file diff --git a/submissions/Calculator/content.css b/submissions/Calculator/content.css new file mode 100644 index 00000000..bef33418 --- /dev/null +++ b/submissions/Calculator/content.css @@ -0,0 +1,314 @@ +/* Tab Persona Content Styles */ + +/* Global mood-based animations */ +:root { + --animation-speed: 1; + --animation-style: ease; + --tab-persona-primary: #667eea; +} + +/* Smooth transitions for all mood changes */ +* { + transition-duration: calc(0.3s * var(--animation-speed)) !important; + transition-timing-function: var(--animation-style) !important; +} + +/* Motivational overlay base styles */ +#tab-persona-motivational { + font-feature-settings: "liga" 1, "kern" 1; + letter-spacing: 0.3px; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +/* Focus timer styles */ +#focus-timer { + cursor: pointer; + user-select: none; +} + +#focus-timer:hover { + transform: scale(1.05); + background: rgba(255, 107, 107, 1) !important; +} + +/* Chill mode styles */ +.tab-persona-chill * { + transition-duration: 0.4s !important; +} + +.tab-persona-chill a:hover { + color: var(--tab-persona-primary) !important; + text-shadow: 0 0 8px rgba(102, 126, 234, 0.3); +} + +.tab-persona-chill button:hover { + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2) !important; +} + +/* Hyperfocus mode styles */ +.tab-persona-hyperfocus { + position: relative; +} + +.tab-persona-hyperfocus::before { + content: ''; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: radial-gradient( + circle at center, + transparent 40%, + rgba(0, 0, 0, 0.05) 70%, + rgba(0, 0, 0, 0.1) 100% + ); + pointer-events: none; + z-index: 1; +} + +.tab-persona-hyperfocus * { + transition-duration: 0.1s !important; +} + +.tab-persona-hyperfocus .advertisement, +.tab-persona-hyperfocus .ads, +.tab-persona-hyperfocus [class*="ad-"], +.tab-persona-hyperfocus [id*="ad-"] { + filter: blur(2px) !important; + opacity: 0.1 !important; +} + +/* Playful mode styles */ +.tab-persona-playful button { + overflow: hidden; + position: relative; +} + +.tab-persona-playful button::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%); + transition: width 0.6s, height 0.6s; + transform: translate(-50%, -50%); + border-radius: 50%; +} + +.tab-persona-playful button:hover::before { + width: 300px; + height: 300px; +} + +.tab-persona-playful h1, +.tab-persona-playful h2, +.tab-persona-playful h3 { + position: relative; + overflow: hidden; +} + +/* Zen mode styles */ +.tab-persona-zen { + font-smoothing: antialiased; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.tab-persona-zen * { + transition-duration: 0.5s !important; + transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94) !important; +} + +.tab-persona-zen p, +.tab-persona-zen div, +.tab-persona-zen span { + line-height: 1.7 !important; +} + +.tab-persona-zen h1, +.tab-persona-zen h2, +.tab-persona-zen h3 { + font-weight: 300 !important; + letter-spacing: 1px !important; +} + +/* Breathing reminder animation */ +@keyframes breathe { + 0%, 100% { + transform: translate(-50%, -50%) scale(1); + opacity: 0.9; + } + 50% { + transform: translate(-50%, -50%) scale(1.1); + opacity: 1; + } +} + +/* Tab persona indicator pulse */ +@keyframes indicatorPulse { + 0%, 100% { + opacity: 0.1; + transform: scale(1); + } + 50% { + opacity: 0.3; + transform: scale(1.1); + } +} + +/* Scroll-triggered focus reminder */ +.tab-persona-scroll-reminder { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(255, 107, 107, 0.95); + color: white; + padding: 15px 20px; + border-radius: 25px; + font-size: 14px; + z-index: 10002; + animation: fadeInScale 0.3s ease-out; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +@keyframes fadeInScale { + from { + opacity: 0; + transform: translate(-50%, -50%) scale(0.8); + } + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +/* Custom scrollbar for different moods */ +.tab-persona-chill ::-webkit-scrollbar { + width: 8px; +} + +.tab-persona-chill ::-webkit-scrollbar-track { + background: linear-gradient(180deg, #667eea, #764ba2); + border-radius: 4px; +} + +.tab-persona-chill ::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3); + border-radius: 4px; +} + +.tab-persona-hyperfocus ::-webkit-scrollbar { + width: 4px; +} + +.tab-persona-hyperfocus ::-webkit-scrollbar-track { + background: #ff6b6b; +} + +.tab-persona-hyperfocus ::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.5); +} + +.tab-persona-playful ::-webkit-scrollbar { + width: 12px; +} + +.tab-persona-playful ::-webkit-scrollbar-track { + background: linear-gradient(45deg, #feca57, #ff9ff3); +} + +.tab-persona-playful ::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.4); + border-radius: 6px; +} + +.tab-persona-zen ::-webkit-scrollbar { + width: 6px; +} + +.tab-persona-zen ::-webkit-scrollbar-track { + background: #74b9ff; +} + +.tab-persona-zen ::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.2); + border-radius: 3px; +} + +/* Selection colors based on mood */ +.tab-persona-chill ::selection { + background: rgba(102, 126, 234, 0.3); +} + +.tab-persona-hyperfocus ::selection { + background: rgba(255, 107, 107, 0.3); +} + +.tab-persona-playful ::selection { + background: rgba(254, 202, 87, 0.3); +} + +.tab-persona-zen ::selection { + background: rgba(116, 185, 255, 0.3); +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + #tab-persona-motivational { + right: 10px !important; + top: 10px !important; + max-width: 200px !important; + font-size: 11px !important; + } + + #focus-timer { + right: 10px !important; + top: 40px !important; + font-size: 10px !important; + } +} + +/* Accessibility improvements */ +@media (prefers-reduced-motion: reduce) { + .tab-persona-playful button::before, + .tab-persona-playful h1, + .tab-persona-playful h2, + .tab-persona-playful h3 { + animation: none !important; + } + + * { + transition-duration: 0.1s !important; + } +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + #tab-persona-motivational { + background: rgba(0, 0, 0, 0.95) !important; + border: 2px solid white !important; + } + + #focus-timer { + background: rgba(255, 0, 0, 0.95) !important; + border: 1px solid white !important; + } +} + +/* Dark mode adjustments */ +@media (prefers-color-scheme: dark) { + .tab-persona-zen p, + .tab-persona-zen div, + .tab-persona-zen span { + color: rgba(255, 255, 255, 0.8) !important; + } + + .tab-persona-zen a { + border-bottom-color: rgba(116, 185, 255, 0.5) !important; + } +} \ No newline at end of file diff --git a/submissions/Calculator/content.js b/submissions/Calculator/content.js new file mode 100644 index 00000000..b66b5659 --- /dev/null +++ b/submissions/Calculator/content.js @@ -0,0 +1,104 @@ +class TabDragDropManager { + constructor() { + this.init(); + } + + init() { + this.trackPageTime(); + } + + showNotification(message) { + const notification = document.createElement('div'); + notification.style.cssText = ` + position: fixed !important; + top: 20px !important; + right: 20px !important; + background: #000000 !important; + color: #ffffff !important; + padding: 20px 25px !important; + border: 4px solid #ffffff !important; + font-family: 'Courier New', monospace !important; + font-size: 16px !important; + font-weight: 900 !important; + text-transform: uppercase !important; + letter-spacing: 2px !important; + box-shadow: 8px 8px 0px #ffffff !important; + z-index: 999997 !important; + transform: translateX(500px) !important; + transition: all 0.4s ease !important; + white-space: nowrap !important; + `; + + notification.textContent = `◼ ${message.toUpperCase()}`; + + document.body.appendChild(notification); + + setTimeout(() => { + notification.style.transform = 'translateX(0)'; + }, 100); + + setTimeout(() => { + notification.style.transform = 'translateX(500px)'; + setTimeout(() => { + if (document.body.contains(notification)) { + document.body.removeChild(notification); + } + }, 400); + }, 3000); + } + + trackPageTime() { + let startTime = Date.now(); + let isVisible = true; + + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'hidden') { + if (isVisible) { + const timeSpent = Date.now() - startTime; + try { + if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.sendMessage) { + chrome.runtime.sendMessage({ + action: 'trackPageTime', + url: window.location.href, + title: document.title, + timeSpent: timeSpent + }); + } + } catch (error) { + console.log(`Page time: ${timeSpent}ms on ${document.title}`); + } + isVisible = false; + } + } else { + startTime = Date.now(); + isVisible = true; + } + }); + + window.addEventListener('beforeunload', () => { + if (isVisible) { + const timeSpent = Date.now() - startTime; + try { + if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.sendMessage) { + chrome.runtime.sendMessage({ + action: 'trackPageTime', + url: window.location.href, + title: document.title, + timeSpent: timeSpent + }); + } + } catch (error) { + console.log(`Page time on unload: ${timeSpent}ms on ${document.title}`); + } + } + }); + } +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + new TabDragDropManager(); + }); +} else { + new TabDragDropManager(); +} \ No newline at end of file diff --git a/submissions/Calculator/icons/icon128.png b/submissions/Calculator/icons/icon128.png new file mode 100644 index 00000000..0b6badfa Binary files /dev/null and b/submissions/Calculator/icons/icon128.png differ diff --git a/submissions/Calculator/icons/icon16.png b/submissions/Calculator/icons/icon16.png new file mode 100644 index 00000000..0b6badfa Binary files /dev/null and b/submissions/Calculator/icons/icon16.png differ diff --git a/submissions/Calculator/icons/icon48.png b/submissions/Calculator/icons/icon48.png new file mode 100644 index 00000000..0b6badfa Binary files /dev/null and b/submissions/Calculator/icons/icon48.png differ diff --git a/submissions/Calculator/manifest.json b/submissions/Calculator/manifest.json new file mode 100644 index 00000000..4ddcc70f --- /dev/null +++ b/submissions/Calculator/manifest.json @@ -0,0 +1,43 @@ +{ + "manifest_version": 3, + "name": "Smart Tab Organizer", + "version": "1.0", + "description": "Organize tabs into groups with time tracking and productivity modes", + + "permissions": [ + "tabs", + "tabGroups", + "storage", + "activeTab" + ], + + "background": { + "service_worker": "background.js" + }, + + "action": { + "default_popup": "popup.html", + "default_title": "Smart Tab Organizer", + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon128.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } + }, + + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"], + "run_at": "document_start" + } + ], + + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon128.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } +} \ No newline at end of file diff --git a/submissions/Calculator/popup.html b/submissions/Calculator/popup.html new file mode 100644 index 00000000..7188521b --- /dev/null +++ b/submissions/Calculator/popup.html @@ -0,0 +1,341 @@ + + + + + + Smart Tab Organizer + + + +
+

TAB ORGANIZER

+

ORGANIZE • TRACK • FOCUS

+
+ +
+
MODES
+
+ + + + +
+
+ +
+
GROUPS
+
+ +
+
+ +
+ + +
+ +
+
TODAY: 0M • TABS: 0
+
+ + + + \ No newline at end of file diff --git a/submissions/Calculator/popup.js b/submissions/Calculator/popup.js new file mode 100644 index 00000000..5fd46347 --- /dev/null +++ b/submissions/Calculator/popup.js @@ -0,0 +1,590 @@ +class TabOrganizer { + constructor() { + this.groups = {}; + this.currentMode = 'work'; + this.timeTracking = {}; + this.init(); + } + + async init() { + try { + await this.loadData(); + this.setupEventListeners(); + this.renderGroups(); + this.updateStats(); + this.startTimeTracking(); + } catch (error) { + console.error('Initialization error:', error); + this.showNotification('INITIALIZATION ERROR'); + } + } + + showNotification(message) { + const existingNotifications = document.querySelectorAll('.tab-organizer-notification'); + existingNotifications.forEach(notification => notification.remove()); + + const notification = document.createElement('div'); + notification.className = 'tab-organizer-notification'; + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + background: #000000; + color: #ffffff; + border: 3px solid #ffffff; + padding: 12px 16px; + font-family: 'Courier New', monospace; + font-weight: 900; + font-size: 14px; + text-transform: uppercase; + z-index: 10000; + transform: translateX(400px); + transition: all 0.3s ease; + border-radius: 4px; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + `; + notification.textContent = message; + + document.body.appendChild(notification); + + setTimeout(() => { + notification.style.transform = 'translateX(0)'; + }, 100); + + + setTimeout(() => { + if (notification.parentNode) { + notification.style.transform = 'translateX(400px)'; + setTimeout(() => { + if (notification.parentNode) { + notification.remove(); + } + }, 100); + } + }, 1000); + } + + async loadData() { + try { + + if (!chrome || !chrome.storage || !chrome.storage.sync) { + throw new Error('Chrome storage API not available'); + } + + const result = await chrome.storage.sync.get(['tabGroups', 'currentMode', 'timeTracking']); + + + this.groups = result.tabGroups || {}; + + + const defaultGroups = { + WORK: { name: 'WORK', tabs: [], color: '#ffffff', created: Date.now() }, + CODE: { name: 'CODE', tabs: [], color: '#ffffff', created: Date.now() }, + CREATIVITY: { name: 'CREATIVE', tabs: [], color: '#ffffff', created: Date.now() }, + FUN: { name: 'FUN', tabs: [], color: '#ffffff', created: Date.now() } + }; + + for (const [key, defaultGroup] of Object.entries(defaultGroups)) { + if (!this.groups[key]) { + this.groups[key] = { ...defaultGroup }; + } + } + + this.currentMode = result.currentMode || 'work'; + this.timeTracking = result.timeTracking || {}; + + + await this.saveData(); + } catch (error) { + console.error('Error loading data:', error); + this.initializeDefaults(); + } + } + + initializeDefaults() { + this.groups = { + WORK: { name: 'WORK', tabs: [], color: '#ffffff', created: Date.now() }, + CODE: { name: 'CODE', tabs: [], color: '#ffffff', created: Date.now() }, + CREATIVITY: { name: 'CREATIVE', tabs: [], color: '#ffffff', created: Date.now() }, + FUN: { name: 'FUN', tabs: [], color: '#ffffff', created: Date.now() } + }; + this.currentMode = 'work'; + this.timeTracking = {}; + } + + async saveData() { + try { + if (chrome && chrome.storage && chrome.storage.sync) { + await chrome.storage.sync.set({ + tabGroups: this.groups, + currentMode: this.currentMode, + timeTracking: this.timeTracking + }); + } + } catch (error) { + console.error('Error saving data:', error); + } + } + + setupEventListeners() { + + const modeButtons = document.querySelectorAll('.mode-btn'); + modeButtons.forEach(btn => { + btn.addEventListener('click', (e) => { + const mode = e.target.dataset.mode; + if (mode) { + this.switchMode(mode); + } + }); + }); + + + const addGroupBtn = document.getElementById('add-group-btn'); + if (addGroupBtn) { + addGroupBtn.addEventListener('click', () => { + this.createNewGroup(); + }); + } + + const newGroupInput = document.getElementById('new-group-name'); + if (newGroupInput) { + newGroupInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + this.createNewGroup(); + } + }); + } + + this.updateModeDisplay(); + } + + async switchMode(mode) { + if (!mode || !this.groups[mode]) { + console.error('Invalid mode:', mode); + return; + } + + this.currentMode = mode; + await this.saveData(); + this.updateModeDisplay(); + + try { + if (chrome && chrome.runtime) { + chrome.runtime.sendMessage({ + action: 'switchMode', + mode: mode + }); + } + } catch (error) { + console.error('Error sending message to background script:', error); + } + + this.showNotification(`SWITCHED TO ${mode.toUpperCase()} MODE`); + } + + updateModeDisplay() { + const modeButtons = document.querySelectorAll('.mode-btn'); + modeButtons.forEach(btn => { + btn.classList.remove('active'); + if (btn.dataset.mode === this.currentMode) { + btn.classList.add('active'); + } + }); + } + + async createNewGroup() { + const input = document.getElementById('new-group-name'); + if (!input) { + console.error('New group input not found'); + return; + } + + const groupName = input.value.trim().toUpperCase(); + + if (!groupName) { + this.showNotification('PLEASE ENTER A GROUP NAME'); + return; + } + + if (groupName.length > 20) { + this.showNotification('GROUP NAME TOO LONG'); + return; + } + + const groupKey = groupName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''); + + if (this.groups[groupKey]) { + this.showNotification('GROUP ALREADY EXISTS!'); + return; + } + + this.groups[groupKey] = { + name: groupName, + tabs: [], + color: '#ffffff', + created: Date.now() + }; + + await this.saveData(); + this.renderGroups(); + input.value = ''; + + + this.showNotification(`GROUP "${groupName}" CREATED`); + } + + async renderGroups() { + const container = document.getElementById('groups-container'); + if (!container) { + console.error('Groups container not found'); + return; + } + + container.innerHTML = ''; + + for (const [key, group] of Object.entries(this.groups)) { + try { + const groupElement = this.createGroupElement(key, group); + container.appendChild(groupElement); + } catch (error) { + console.error('Error creating group element:', error); + } + } + } + + createGroupElement(key, group) { + const div = document.createElement('div'); + div.className = 'group-item'; + + const groupName = group.name || key.toUpperCase(); + const tabCount = Array.isArray(group.tabs) ? group.tabs.length : 0; + const timeSpent = this.formatTime(this.timeTracking[key] || 0); + + div.innerHTML = ` +
+
${this.escapeHtml(groupName)}
+
${tabCount}
+
+
TIME: ${timeSpent}
+
+ DROP TABS HERE +
+
+ + + +
+ `; + + this.setupGroupActions(div, key); + return div; + } + + escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + setupGroupActions(element, groupKey) { + + const actionButtons = element.querySelectorAll('.action-btn'); + actionButtons.forEach(btn => { + btn.addEventListener('click', (e) => { + e.preventDefault(); + const action = e.target.dataset.action; + if (action) { + this.handleGroupAction(action, groupKey); + } + }); + }); + + + const dropZone = element.querySelector('.drop-zone'); + if (!dropZone) return; + + dropZone.addEventListener('click', () => { + this.addCurrentTabToGroup(groupKey); + }); + + dropZone.addEventListener('dragover', (e) => { + e.preventDefault(); + dropZone.classList.add('drag-over'); + }); + + dropZone.addEventListener('dragleave', () => { + dropZone.classList.remove('drag-over'); + }); + + dropZone.addEventListener('drop', (e) => { + e.preventDefault(); + dropZone.classList.remove('drag-over'); + this.handleTabDrop(e, groupKey); + }); + } + + async addCurrentTabToGroup(groupKey) { + try { + if (!chrome || !chrome.tabs) { + throw new Error('Chrome tabs API not available'); + } + + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + + if (!tab) { + this.showNotification('NO ACTIVE TAB FOUND'); + return; + } + + if (!this.groups[groupKey]) { + console.error('Group not found:', groupKey); + return; + } + + + if (!Array.isArray(this.groups[groupKey].tabs)) { + this.groups[groupKey].tabs = []; + } + + + const existingTab = this.groups[groupKey].tabs.find(t => t.id === tab.id || t.url === tab.url); + if (existingTab) { + this.showNotification('TAB ALREADY IN GROUP'); + return; + } + + this.groups[groupKey].tabs.push({ + id: tab.id, + url: tab.url, + title: tab.title || 'Untitled', + favIconUrl: tab.favIconUrl || '', + added: Date.now() + }); + + await this.saveData(); + + + try { + if (chrome && chrome.runtime) { + chrome.runtime.sendMessage({ + action: 'addTabToGroup', + tabId: tab.id, + groupKey: groupKey, + groupName: this.groups[groupKey].name || groupKey, + groupColor: this.groups[groupKey].color + }); + } + } catch (error) { + console.error('Error sending message to background:', error); + } + + this.renderGroups(); + this.showNotification(`TAB ADDED TO ${this.groups[groupKey].name || groupKey.toUpperCase()}`); + } catch (error) { + console.error('Error adding tab to group:', error); + this.showNotification('ERROR ADDING TAB'); + } + } + + async handleGroupAction(action, groupKey) { + if (!this.groups[groupKey]) { + console.error('Group not found:', groupKey); + return; + } + + const isDefaultGroup = ['work', 'code', 'creativity', 'fun'].includes(groupKey); + + try { + switch (action) { + case 'open': + if (this.groups[groupKey] && Array.isArray(this.groups[groupKey].tabs) && this.groups[groupKey].tabs.length > 0) { + if (chrome && chrome.runtime) { + chrome.runtime.sendMessage({ + action: 'openGroupTabs', + groupKey: groupKey, + tabs: this.groups[groupKey].tabs + }); + } + this.showNotification(`OPENED ${this.groups[groupKey].name || groupKey.toUpperCase()} TABS`); + } else { + this.showNotification('NO TABS IN GROUP'); + } + break; + + case 'close': + if (chrome && chrome.runtime) { + chrome.runtime.sendMessage({ + action: 'closeGroupTabs', + groupKey: groupKey + }); + } + this.groups[groupKey].tabs = []; + await this.saveData(); + this.renderGroups(); + this.showNotification(`CLOSED ${this.groups[groupKey].name || groupKey.toUpperCase()} TABS`); + break; + + case 'delete': + if (isDefaultGroup) { + this.showNotification('CANNOT DELETE DEFAULT GROUPS'); + return; + } + + if (confirm(`DELETE GROUP "${this.groups[groupKey].name || groupKey.toUpperCase()}"?\n\nNote: Tabs will remain open, only the group will be removed.`)) { + + if (chrome && chrome.runtime) { + chrome.runtime.sendMessage({ + action: 'deleteTabGroup', + groupKey: groupKey, + groupName: this.groups[groupKey].name || groupKey, + tabIds: this.groups[groupKey].tabs.map(tab => tab.id) + }); + } + + + delete this.groups[groupKey]; + delete this.timeTracking[groupKey]; + + + if (this.currentMode === groupKey) { + this.currentMode = 'work'; + } + + await this.saveData(); + this.renderGroups(); + this.updateModeDisplay(); + this.showNotification('GROUP PERMANENTLY DELETED'); + } + break; + + default: + console.error('Unknown action:', action); + } + } catch (error) { + console.error('Error handling group action:', error); + this.showNotification('ACTION FAILED'); + } + } + + async handleTabDrop(event, groupKey) { + try { + const tabData = event.dataTransfer.getData('text/plain'); + if (tabData) { + const tab = JSON.parse(tabData); + await this.addTabToGroup(tab, groupKey); + } + } catch (error) { + console.error('Error parsing dropped tab data:', error); + this.showNotification('INVALID TAB DATA'); + } + } + + async addTabToGroup(tab, groupKey) { + if (!this.groups[groupKey] || !tab) { + return; + } + + // Ensure tabs array exists + if (!Array.isArray(this.groups[groupKey].tabs)) { + this.groups[groupKey].tabs = []; + } + + // Check if tab already exists + const existingTab = this.groups[groupKey].tabs.find(t => t.id === tab.id || t.url === tab.url); + if (!existingTab) { + this.groups[groupKey].tabs.push({ + id: tab.id, + url: tab.url, + title: tab.title || 'Untitled', + favIconUrl: tab.favIconUrl || '', + added: Date.now() + }); + + await this.saveData(); + this.renderGroups(); + this.showNotification(`TAB ADDED TO ${this.groups[groupKey].name || groupKey.toUpperCase()}`); + } + } + + startTimeTracking() { + if (this.timeTrackingInterval) { + clearInterval(this.timeTrackingInterval); + } + + this.timeTrackingInterval = setInterval(async () => { + if (this.currentMode && this.groups[this.currentMode]) { + this.timeTracking[this.currentMode] = (this.timeTracking[this.currentMode] || 0) + 1; + await this.saveData(); + this.updateTimeDisplay(); + } + }, 60000); + } + + updateTimeDisplay() { + + Object.keys(this.groups).forEach(mode => { + const timeEl = document.getElementById(`${mode}-time`); + if (timeEl) { + timeEl.textContent = this.formatTime(this.timeTracking[mode] || 0); + } + }); + } + + async updateStats() { + try { + let totalTabs = 0; + if (chrome && chrome.tabs) { + const tabs = await chrome.tabs.query({}); + totalTabs = tabs.length; + } + + const totalTime = Object.values(this.timeTracking).reduce((sum, time) => sum + (time || 0), 0); + + const totalTimeEl = document.getElementById('total-time'); + const activeTabsEl = document.getElementById('active-tabs'); + + if (totalTimeEl) { + totalTimeEl.textContent = this.formatTime(totalTime); + } + if (activeTabsEl) { + activeTabsEl.textContent = totalTabs; + } + } catch (error) { + console.error('Error updating stats:', error); + } + } + + formatTime(minutes) { + const mins = parseInt(minutes) || 0; + if (mins < 60) { + return `${mins}m`; + } else { + const hours = Math.floor(mins / 60); + const remainingMins = mins % 60; + return `${hours}h ${remainingMins}m`; + } + } + + + destroy() { + if (this.timeTrackingInterval) { + clearInterval(this.timeTrackingInterval); + } + } +} + + +document.addEventListener('DOMContentLoaded', () => { + try { + window.tabOrganizer = new TabOrganizer(); + } catch (error) { + console.error('Failed to initialize TabOrganizer:', error); + } +}); + + +window.addEventListener('beforeunload', () => { + if (window.tabOrganizer && window.tabOrganizer.destroy) { + window.tabOrganizer.destroy(); + } +}); \ No newline at end of file diff --git a/submissions/Movio/icon.png b/submissions/Movio/icon.png new file mode 100644 index 00000000..fbefe2c2 Binary files /dev/null and b/submissions/Movio/icon.png differ diff --git a/submissions/Movio/manifest.json b/submissions/Movio/manifest.json new file mode 100644 index 00000000..6eebd5a3 --- /dev/null +++ b/submissions/Movio/manifest.json @@ -0,0 +1,14 @@ +{ + "manifest_version": 3, + "name": "Movio", + "version": "1.9", + "description": "Search movies using the OMDb API", + "action": { + "default_popup": "popup.html", + "default_title": "Movie Search" + }, + "permissions": [], + "icons": { + "128": "icon.png" + } +} diff --git a/submissions/Movio/ng.js b/submissions/Movio/ng.js new file mode 100644 index 00000000..868e35e1 --- /dev/null +++ b/submissions/Movio/ng.js @@ -0,0 +1,46 @@ + const apiKey = "1fe8889c"; + + document.getElementById("searchBtn").addEventListener("click", searchMovie); + document.getElementById("movieInput").addEventListener("keypress", (e) => { + if (e.key === "Enter") { + searchMovie(); + } + }); + + function searchMovie() { + const movieTitle = document.getElementById("movieInput").value.trim(); + const resultDiv = document.getElementById("result"); + + if (!movieTitle) { + resultDiv.innerHTML = '
⚠️ Please enter a movie name!
'; + return; + } + + // Show loading state + resultDiv.innerHTML = '
Searching...
'; + + fetch(`https://www.omdbapi.com/?apikey=${apiKey}&t=${encodeURIComponent(movieTitle)}`) + .then(response => response.json()) + .then(data => { + if (data.Response === "True") { + resultDiv.innerHTML = ` +

${data.Title} (${data.Year})

+
+ ${data.Poster !== "N/A" ? `Movie Poster` : ''} +

Genre: ${data.Genre || 'N/A'}

+

Director: ${data.Director || 'N/A'}

+

Actors: ${data.Actors || 'N/A'}

+

Plot: ${data.Plot || 'N/A'}

+

IMDb: ${data.imdbRating || 'N/A'}/10

+

Runtime: ${data.Runtime || 'N/A'}

+
+ `; + } else { + resultDiv.innerHTML = '
❌ Movie not found! Try another title.
'; + } + }) + .catch(err => { + resultDiv.innerHTML = '
🔥 Error fetching data. Check your connection!
'; + console.error(err); + }); + } \ No newline at end of file diff --git a/submissions/Movio/popup.css b/submissions/Movio/popup.css new file mode 100644 index 00000000..2dd2d9d6 --- /dev/null +++ b/submissions/Movio/popup.css @@ -0,0 +1,261 @@ + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + font-family: 'Courier New', monospace; + width: 380px; + background: linear-gradient(135deg, #4ecdc4 0%, #ff6b6b 100%); + color: #000; + overflow-x: hidden; + } + + .container { + padding: 20px; + position: relative; + } + + .container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 8px; + background: repeating-linear-gradient( + 90deg, + #000 0px, + #000 20px, + #fff 20px, + #fff 40px + ); + } + + h2 { + font-size: 28px; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 2px; + margin-bottom: 25px; + text-align: center; + background: #000; + color: #fff; + padding: 15px; + border: 4px solid #fff; + box-shadow: 8px 8px 0px #333; + transform: rotate(-1deg); + position: relative; + z-index: 2; + } + + h2::after { + content: '⚡'; + position: absolute; + top: -10px; + right: -10px; + background: #ff6b6b; + color: #fff; + width: 30px; + height: 30px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + border: 3px solid #000; + } + + .input-container { + position: relative; + margin-bottom: 20px; + } + + input { + width: 100%; + padding: 15px 20px; + font-size: 16px; + font-family: 'Courier New', monospace; + font-weight: bold; + border: 4px solid #000; + background: #fff; + color: #000; + text-transform: uppercase; + letter-spacing: 1px; + box-shadow: 6px 6px 0px #000; + transition: all 0.2s ease; + } + + input:focus { + outline: none; + transform: translate(-2px, -2px); + box-shadow: 8px 8px 0px #000; + background: #ffff00; + } + + input::placeholder { + color: #666; + text-transform: uppercase; + letter-spacing: 1px; + } + + button { + width: 100%; + padding: 18px; + font-size: 18px; + font-family: 'Courier New', monospace; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 2px; + background: #000; + color: #fff; + border: 4px solid #fff; + cursor: pointer; + box-shadow: 6px 6px 0px #333; + transition: all 0.2s ease; + position: relative; + overflow: hidden; + margin-bottom: 25px; + } + + button:hover { + background: #ff6b6b; + transform: translate(-2px, -2px); + box-shadow: 8px 8px 0px #333; + } + + button:active { + transform: translate(2px, 2px); + box-shadow: 2px 2px 0px #333; + } + + button::before { + content: '🎬'; + position: absolute; + left: -30px; + top: 50%; + transform: translateY(-50%); + font-size: 20px; + transition: all 0.3s ease; + } + + button:hover::before { + left: 10px; + } + + #result { + background: #fff; + border: 4px solid #000; + min-height: 100px; + padding: 20px; + box-shadow: 6px 6px 0px #000; + position: relative; + margin-top: 10px; + } + + #result:empty { + display: none; + } + + #result h3 { + font-size: 20px; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 15px; + color: #000; + background: #ffff00; + padding: 10px; + border: 3px solid #000; + display: inline-block; + transform: rotate(1deg); + } + + #result img { + width: 120px; + height: 180px; + object-fit: cover; + border: 4px solid #000; + box-shadow: 4px 4px 0px #333; + float: left; + margin: 0 20px 20px 0; + transition: transform 0.3s ease; + } + + #result img:hover { + transform: scale(1.05) rotate(-2deg); + } + + #result p { + font-size: 14px; + font-weight: bold; + line-height: 1.6; + margin-bottom: 12px; + color: #000; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + #result strong { + background: #000; + color: #fff; + padding: 2px 6px; + margin-right: 8px; + font-size: 12px; + letter-spacing: 1px; + } + + .error { + color: #ff0000; + font-weight: 900; + text-align: center; + font-size: 16px; + text-transform: uppercase; + letter-spacing: 1px; + background: #fff; + padding: 15px; + border: 4px solid #ff0000; + box-shadow: 4px 4px 0px #333; + } + + .loading { + display: flex; + justify-content: center; + align-items: center; + padding: 40px; + font-size: 18px; + font-weight: bold; + text-transform: uppercase; + letter-spacing: 2px; + } + + .loading::after { + content: ''; + width: 20px; + height: 20px; + border: 3px solid #000; + border-top: 3px solid #ff6b6b; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-left: 10px; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + + .container::after { + content: ''; + position: absolute; + bottom: 10px; + right: 10px; + width: 0; + height: 0; + border-left: 20px solid transparent; + border-right: 20px solid transparent; + border-bottom: 20px solid #000; + transform: rotate(45deg); + } diff --git a/submissions/Movio/popup.html b/submissions/Movio/popup.html new file mode 100644 index 00000000..7792263a --- /dev/null +++ b/submissions/Movio/popup.html @@ -0,0 +1,24 @@ + + + + + + Movio + + + +
+

🎬 Movie Search

+
+ +
+ + + +
+
+ + + + + \ No newline at end of file diff --git a/submissions/Movio/popup.js b/submissions/Movio/popup.js new file mode 100644 index 00000000..a6187930 --- /dev/null +++ b/submissions/Movio/popup.js @@ -0,0 +1,43 @@ +const apiKey = "1fe8889c"; + +document.getElementById("searchBtn").addEventListener("click", searchMovie); +document.getElementById("movieInput").addEventListener("keypress", (e) => { + if (e.key === "Enter") { + searchMovie(); + } +}); + +function searchMovie() { + const movieTitle = document.getElementById("movieInput").value.trim(); + const resultDiv = document.getElementById("result"); + if (!movieTitle) { + resultDiv.innerHTML = '
⚠️ Please enter a movie name!
'; + return; + } + resultDiv.innerHTML = '
Getting You Movie ......
'; + + fetch(`https://www.omdbapi.com/?apikey=${apiKey}&t=${encodeURIComponent(movieTitle)}`) + .then(response => response.json()) + .then(data => { + if (data.Response === "True") { + resultDiv.innerHTML = ` +

${data.Title} (${data.Year})

+
+ ${data.Poster !== "N/A" ? `Movie Poster` : ''} +

Genre: ${data.Genre || 'N/A'}

+

Director: ${data.Director || 'N/A'}

+

Actors: ${data.Actors || 'N/A'}

+

Plot: ${data.Plot || 'N/A'}

+

IMDb: ${data.imdbRating || 'N/A'}/10

+

Runtime: ${data.Runtime || 'N/A'}

+
+ `; + } else { + resultDiv.innerHTML = '
Movie not found
'; + } + }) + .catch(err => { + resultDiv.innerHTML = '
🔥 Error fetching data. Check your connection!
'; + console.error(err); + }); +} \ No newline at end of file