diff --git a/mods/mods_available/web-shortcuts/index.js b/mods/mods_available/web-shortcuts/index.js deleted file mode 100644 index 630149dd..00000000 --- a/mods/mods_available/web-shortcuts/index.js +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Web Shortcuts Mod for Puter - * - * This mod adds a "Create Web Shortcut" option to the context menu - * that allows users to create .weblink files that open websites. - */ - -const BaseService = require("../../../src/backend/src/services/BaseService"); - -class WebShortcutsService extends BaseService { - async _init() { - const svc_puterHomepage = this.services.get('puter-homepage'); - svc_puterHomepage.register_script('/web-shortcuts/main.js'); - } -} - -// Function to extract URL from text (handles pasted URLs) -function extractURL(text) { - // Remove any warning messages that might be in the pasted content - const cleanText = text.replace(/⚠️Warning⚠️.*?(?=http)/s, '').trim(); - - // Try to find a URL in the text - const urlRegex = /(https?:\/\/[^\s]+)/g; - const matches = cleanText.match(urlRegex); - - if (matches && matches.length > 0) { - return matches[0]; - } - - return text; -} - -// Function to validate URL -function isValidURL(url) { - try { - new URL(url); - return true; - } catch (error) { - return false; - } -} - -// Function to create a web shortcut -async function createWebShortcut(targetPath, url = null) { - try { - // If no URL provided, prompt the user - if (!url) { - let userInput = prompt('Enter or paste the URL for the web shortcut:', 'https://example.com'); - if (!userInput) { - console.log('User cancelled URL input'); - return; - } - url = extractURL(userInput); - } - - // Ensure URL has protocol - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = 'https://' + url; - } - - // Validate URL - if (!isValidURL(url)) { - console.error('Invalid URL:', url); - alert('Invalid URL. Please enter a valid URL.'); - return; - } - - // Get the website title (for the shortcut name) - const validUrl = new URL(url); - const siteName = validUrl.hostname; - - // Get the favicon - const faviconUrl = `https://www.google.com/s2/favicons?domain=${validUrl.origin}&sz=64`; - - // Create a JSON file that will store the shortcut data - const shortcutData = { - url: validUrl.href, - icon: faviconUrl, - type: 'web_shortcut' - }; - - // Create the shortcut file - const shortcutFileName = `${siteName}.weblink`; - - // Get the target path (default to desktop if not provided) - const desktopPath = window.desktop_path || '/Desktop'; - const targetDirectory = targetPath || desktopPath; - - // Write the file - const result = await window.puter.fs.write( - targetDirectory + '/' + shortcutFileName, - JSON.stringify(shortcutData), - { dedupeName: true } - ); - - console.log('Web shortcut created:', result); - } catch (error) { - console.error('Error creating web shortcut:', error); - alert('Error creating web shortcut: ' + error.message); - } -} - -// Handle URL drops on desktop -window.addEventListener('dragover', function(e) { - // Check if we're dragging over the desktop - if (!$(e.target).closest('.desktop').length) return; - - // Check if we have text/uri-list or text/plain data - if (e.dataTransfer.types.includes('text/uri-list') || - e.dataTransfer.types.includes('text/plain')) { - e.preventDefault(); - e.stopPropagation(); - } -}); - -window.addEventListener('drop', async function(e) { - // Check if we're dropping on the desktop - if (!$(e.target).closest('.desktop').length) return; - - // Check if we have text/uri-list or text/plain data - if (e.dataTransfer.types.includes('text/uri-list')) { - e.preventDefault(); - e.stopPropagation(); - - const url = e.dataTransfer.getData('text/uri-list'); - if (isValidURL(url)) { - await createWebShortcut(window.desktop_path, url); - } - } else if (e.dataTransfer.types.includes('text/plain')) { - e.preventDefault(); - e.stopPropagation(); - - const text = e.dataTransfer.getData('text/plain'); - const url = extractURL(text); - if (isValidURL(url)) { - await createWebShortcut(window.desktop_path, url); - } - } -}); - -// Handle URL pastes on desktop -window.addEventListener('paste', async function(e) { - // Check if we're pasting on the desktop - if (!$(e.target).closest('.desktop').length) return; - - const text = e.clipboardData.getData('text/plain'); - const url = extractURL(text); - - if (isValidURL(url)) { - e.preventDefault(); - e.stopPropagation(); - await createWebShortcut(window.desktop_path, url); - } -}); - -// Add "Create Web Shortcut" to the desktop context menu -window.addEventListener('ctxmenu-will-open', function(e) { - const options = e.detail.options; - - // Only add to desktop context menu or directory context menus - if (!options || !options.items) return; - - // Check if this is a desktop or directory context menu - const isDesktopOrDirMenu = options.items.some(item => - (item.html === 'New Folder' || item.html === i18n('new_folder')) || - (item.html === 'Paste' || item.html === i18n('paste')) - ); - - if (isDesktopOrDirMenu) { - // Find the position to insert our menu item (after "New Folder") - let insertIndex = options.items.findIndex(item => - item.html === 'New Folder' || item.html === i18n('new_folder') - ); - - if (insertIndex === -1) { - // If "New Folder" not found, insert at the beginning - insertIndex = 0; - } else { - // Insert after "New Folder" - insertIndex += 1; - } - - // Get the target path - let targetPath; - if (options.parent_element) { - const $parentElement = $(options.parent_element); - if ($parentElement.hasClass('item-container')) { - targetPath = $parentElement.attr('data-path'); - } else if ($parentElement.hasClass('item') && $parentElement.attr('data-is_dir') === '1') { - targetPath = $parentElement.attr('data-path'); - } - } - - // Insert our menu item - options.items.splice(insertIndex, 0, { - html: 'Create Web Shortcut', - icon: '', - onClick: function() { - createWebShortcut(targetPath); - } - }); - } -}); - -// Add "Create Web Shortcut" to the "New" submenu in the desktop context menu -const originalUIContextMenu = window.UIContextMenu; -window.UIContextMenu = function(options) { - if (options && options.items) { - // Find the "New" submenu - const newItemIndex = options.items.findIndex(item => - (item.html === 'New' || item.html === i18n('new')) && - Array.isArray(item.items) - ); - - if (newItemIndex !== -1 && options.items[newItemIndex].items) { - // Add our item to the "New" submenu - options.items[newItemIndex].items.push({ - html: 'Web Shortcut', - icon: '', - onClick: function() { - // Get the target path - let targetPath; - if (options.parent_element) { - const $parentElement = $(options.parent_element); - if ($parentElement.hasClass('item-container')) { - targetPath = $parentElement.attr('data-path'); - } else if ($parentElement.hasClass('item') && $parentElement.attr('data-is_dir') === '1') { - targetPath = $parentElement.attr('data-path'); - } - } - createWebShortcut(targetPath); - } - }); - } - } - - return originalUIContextMenu(options); -}; - -module.exports = WebShortcutsService; \ No newline at end of file diff --git a/mods/mods_available/web-shortcuts/manifest.json b/mods/mods_available/web-shortcuts/manifest.json deleted file mode 100644 index b6ff9617..00000000 --- a/mods/mods_available/web-shortcuts/manifest.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "web-shortcuts", - "version": "1.0.0", - "description": "Create web shortcuts on the desktop", - "author": "Puter", - "license": "MIT", - "dependencies": ["fs", "path"], - "client": { - "scripts": ["/mods/web-shortcuts"] - } -} \ No newline at end of file diff --git a/mods/mods_available/web-shortcuts/module.js b/mods/mods_available/web-shortcuts/module.js deleted file mode 100644 index bf8adfca..00000000 --- a/mods/mods_available/web-shortcuts/module.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024-present Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -const WebShortcutsService = require('./index'); - -module.exports = { - name: 'web-shortcuts', - version: '1.0.0', - description: 'Create web shortcuts on the desktop', - author: 'Puter', - license: 'MIT', - dependencies: ['fs', 'path'], - init: async (puter) => { - const service = new WebShortcutsService(puter); - await service.init(); - return service; - }, - routes: { - '/mods/web-shortcuts': { - GET: (req, res) => { - res.sendFile(__dirname + '/public/main.js'); - } - } - } -}; \ No newline at end of file diff --git a/mods/mods_available/web-shortcuts/package.json b/mods/mods_available/web-shortcuts/package.json deleted file mode 100644 index 488d32a1..00000000 --- a/mods/mods_available/web-shortcuts/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "web-shortcuts", - "version": "1.0.0", - "description": "Create web shortcuts on the desktop", - "author": "Puter", - "license": "MIT", - "dependencies": { - "fs": "*", - "path": "*" - } -} \ No newline at end of file diff --git a/mods/mods_available/web-shortcuts/public/main.js b/mods/mods_available/web-shortcuts/public/main.js deleted file mode 100644 index b98e86cd..00000000 --- a/mods/mods_available/web-shortcuts/public/main.js +++ /dev/null @@ -1,197 +0,0 @@ -// Web Shortcuts Mod -(function() { - console.log('[Web Shortcuts] Mod initializing...'); - - // URL validation helper - function isValidURL(str) { - const pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string - '(\\#[-a-z\\d_]*)?$','i'); // fragment - return !!pattern.test(str); - } - - // Extract URL from text - function extractURL(text) { - // Clean the text - text = text.trim(); - - // If it's already a valid URL, return it - if (isValidURL(text)) { - return text; - } - - // Try to extract a URL - const urlRegex = /(https?:\/\/[^\s]+)/g; - const matches = text.match(urlRegex); - return matches ? matches[0] : null; - } - - // Create web shortcut - async function createWebShortcut(url, name = null) { - console.log('[Web Shortcuts] Creating shortcut with URL:', url); - try { - if (!url) { - url = await window.puter.prompt('Enter URL:'); - if (!url) return; - } - - // Add https:// if no protocol specified - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = 'https://' + url; - } - - if (!isValidURL(url)) { - console.log('[Web Shortcuts] Invalid URL:', url); - window.puter.alert('Invalid URL'); - return; - } - - // Get domain/hostname and build favicon link - const { hostname } = new URL(url); - const favicon = `https://www.google.com/s2/favicons?domain=${hostname}&sz=64`; - - // Use hostname as default name if none provided - if (!name) { - name = await window.puter.prompt('Enter shortcut name:', hostname); - if (!name) return; - } - - console.log('[Web Shortcuts] Creating shortcut:', { url, name, favicon }); - - const shortcutData = { - url: url, - favicon: favicon, - created: new Date().toISOString(), - type: 'link' - }; - - // Build the path for storing the shortcut - const filePath = `${window.desktop_path}/${name}.weblink`; - - // Write the file - const file = await window.puter.fs.write( - filePath, - JSON.stringify(shortcutData), - { - type: 'link', - icon: favicon - } - ); - - console.log('[Web Shortcuts] File created:', file); - - // Create the UI icon on the desktop - window.UIItem({ - appendTo: $('.desktop.item-container'), - 'data-type': 'link', - uid: file.uid, - path: filePath, - icon: favicon, - name: name, - is_dir: false, - metadata: JSON.stringify(shortcutData) - }); - - window.puter.notify('Web shortcut created successfully'); - } catch (error) { - console.error('[Web Shortcuts] Error creating shortcut:', error); - window.puter.alert('Error creating web shortcut: ' + (error.message || 'Please check the URL and try again')); - } - } - - // Add context menu items - window.addEventListener('DOMContentLoaded', () => { - console.log('[Web Shortcuts] DOM Content Loaded'); - - // Get desktop element - const el_desktop = document.querySelector('.desktop'); - console.log('[Web Shortcuts] Desktop element found:', !!el_desktop); - - if (!el_desktop) return; - - // Handle paste events - el_desktop.addEventListener('paste', (e) => { - console.log('[Web Shortcuts] Paste event on desktop:', e.target === el_desktop); - if (e.target !== el_desktop) return; - const text = e.clipboardData.getData('text'); - if (isValidURL(text)) { - e.preventDefault(); - createWebShortcut(text); - } - }); - - // Handle drop events - el_desktop.addEventListener('drop', (e) => { - console.log('[Web Shortcuts] Drop event on desktop:', e.target === el_desktop); - if (e.target !== el_desktop) return; - e.preventDefault(); - const url = e.dataTransfer.getData('text/uri-list') || e.dataTransfer.getData('text/plain'); - if (url && isValidURL(url)) { - createWebShortcut(url); - } - }); - - // Listen for context menu opening - window.addEventListener('ctxmenu-will-open', (e) => { - console.log('[Web Shortcuts] Context menu will open:', e.detail); - const options = e.detail.options; - - // Only modify desktop context menu - if (!options || !options.items) { - console.log('[Web Shortcuts] No menu options found'); - return; - } - - // Check if this is a desktop context menu - const isDesktopMenu = options.items.some(item => - (item.html === 'New Folder' || item.html === window.i18n('new_folder')) || - (item.html === 'Paste' || item.html === window.i18n('paste')) - ); - console.log('[Web Shortcuts] Is desktop menu:', isDesktopMenu); - - if (isDesktopMenu) { - // Find the position to insert our menu item (after "New") - const newIndex = options.items.findIndex(item => - item.html === 'New' || item.html === window.i18n('new') - ); - console.log('[Web Shortcuts] New menu index:', newIndex); - - // Insert our menu item after "New" and before the next divider - if (newIndex !== -1) { - let insertIndex = newIndex + 1; - // Find the next divider - while (insertIndex < options.items.length && options.items[insertIndex] !== '-') { - insertIndex++; - } - - console.log('[Web Shortcuts] Inserting at index:', insertIndex); - - // Insert our item before the divider - options.items.splice(insertIndex, 0, { - html: 'Create Web Shortcut', - icon: '', - onClick: () => createWebShortcut() - }); - - console.log('[Web Shortcuts] Menu items after insertion:', options.items); - } - } - }); - - // Add right-click handler to desktop - el_desktop.addEventListener('contextmenu', (e) => { - console.log('[Web Shortcuts] Context menu event on desktop:', e.target === el_desktop); - // Only handle right-clicks directly on the desktop, not on items - if (e.target !== el_desktop) return; - - // The rest of the context menu handling will be done by the ctxmenu-will-open event - }); - - console.log('[Web Shortcuts] All event listeners attached'); - }); - - console.log('[Web Shortcuts] Mod initialization complete'); -})(); \ No newline at end of file diff --git a/src/gui/src/helpers/item_icon.js b/src/gui/src/helpers/item_icon.js index d078c5cb..ec239021 100644 --- a/src/gui/src/helpers/item_icon.js +++ b/src/gui/src/helpers/item_icon.js @@ -196,58 +196,6 @@ const item_icon = async (fsentry)=>{ } // *.weblink else if(fsentry.name.toLowerCase().endsWith('.weblink')){ - let faviconUrl = null; - - // First try to get icon from data attribute - if (fsentry.icon) { - faviconUrl = fsentry.icon; - } - // Then try metadata - else if (fsentry.metadata) { - try { - const metadata = JSON.parse(fsentry.metadata); - if (metadata && metadata.faviconUrl) { - faviconUrl = metadata.faviconUrl; - } else if (metadata && metadata.url) { - // If we have the URL but no favicon, generate the Google favicon URL - const urlObj = new URL(metadata.url); - faviconUrl = `https://www.google.com/s2/favicons?domain=${urlObj.hostname}&sz=64`; - } - } catch (e) { - console.error("Error parsing weblink metadata:", e); - } - } - // Finally try content - else if (fsentry.content) { - try { - const content = JSON.parse(fsentry.content); - if (content && content.faviconUrl) { - faviconUrl = content.faviconUrl; - } else if (content && content.url) { - // If we have the URL but no favicon, generate the Google favicon URL - const urlObj = new URL(content.url); - faviconUrl = `https://www.google.com/s2/favicons?domain=${urlObj.hostname}&sz=64`; - } - } catch (e) { - console.error("Error parsing weblink content:", e); - } - } - - // If we found a favicon URL, use it - if (faviconUrl) { - return { - image: faviconUrl, - type: 'icon', - onerror: function() { - // If favicon fails to load, switch to default icon - const $icons = $(`img[data-icon="${faviconUrl}"]`); - $icons.attr('src', window.icons['link.svg']); - return window.icons['link.svg']; - } - }; - } - - // Fallback to default link icon return {image: window.icons['link.svg'], type: 'icon'}; } // -------------------------------------------------- diff --git a/src/gui/src/helpers/new_context_menu_item.js b/src/gui/src/helpers/new_context_menu_item.js index 8dbc2d0f..ae778606 100644 --- a/src/gui/src/helpers/new_context_menu_item.js +++ b/src/gui/src/helpers/new_context_menu_item.js @@ -307,10 +307,10 @@ const new_context_menu_item = function(dirname, append_to_element){ }); if (url) { - // Extract domain for naming and favicon - try { - const urlObj = new URL(url); - const domain = urlObj.hostname; + // Extract domain for naming + try { + const urlObj = new URL(url); + const domain = urlObj.hostname; // Extract a simple name from the domain (e.g., "google" from "google.com") let siteName = domain.replace(/^www\./, ''); @@ -325,21 +325,9 @@ const new_context_menu_item = function(dirname, append_to_element){ let linkName = siteName; let fileName = linkName + '.weblink'; - // Get favicon URL from Google favicon service - let faviconUrl = `https://www.google.com/s2/favicons?domain=${domain}&sz=64`; - - // Check if we have a cached favicon - if (window.favicon_cache[domain]) { - faviconUrl = window.favicon_cache[domain]; - } - - // Store in favicon cache - window.favicon_cache[domain] = faviconUrl; - - // Store the URL and favicon in a comprehensive JSON object + // Store the URL in a simple JSON object const weblink_content = JSON.stringify({ url: url, - faviconUrl: faviconUrl, type: 'weblink', domain: domain, created: Date.now(), @@ -347,22 +335,20 @@ const new_context_menu_item = function(dirname, append_to_element){ version: '2.0', metadata: { originalUrl: url, - originalFaviconUrl: faviconUrl, linkName: linkName, simpleName: siteName } }); - // Create the file with favicon + // Create the file with standard link icon const item = await window.create_file({ dirname: dirname, append_to_element: append_to_element, name: fileName, content: weblink_content, - icon: faviconUrl, + icon: window.icons['link.svg'], type: 'weblink', metadata: JSON.stringify({ - faviconUrl: faviconUrl, url: url, domain: domain, timestamp: Date.now(), @@ -370,7 +356,7 @@ const new_context_menu_item = function(dirname, append_to_element){ }), html_attributes: { 'data-weblink': 'true', - 'data-icon': faviconUrl, + 'data-icon': window.icons['link.svg'], 'data-url': url, 'data-domain': domain, 'data-display-name': linkName, @@ -379,49 +365,6 @@ const new_context_menu_item = function(dirname, append_to_element){ force_refresh: true, class: 'weblink-item' }); - - // Apply icon using our consolidated function - if (item) { - applyWeblinkIcon(item, faviconUrl, url); - - // Ensure the item is visible in the container - const container = append_to_element || document.querySelector('.desktop, .explorer-container.active, .files-container.active'); - if (container) { - // Force a refresh of the container - await refresh_item_container(dirname); - - // Hide the extension in the displayed name - const $item = $(item); - const $nameElement = $item.find('.item-name'); - if ($nameElement.length > 0) { - // Store the original name for reference - $nameElement.attr('data-full-name', fileName); - $nameElement.attr('data-display-name', linkName); - - // Set the text directly (no extension) - $nameElement.text(linkName); - } - - // If this is the desktop, trigger a desktop refresh - if (container.classList.contains('desktop')) { - if (typeof window.refresh_desktop === 'function') { - window.refresh_desktop(); - } else if (typeof window.refresh_desktop_items === 'function') { - window.refresh_desktop_items(); - } - } - - // Trigger a custom event to ensure icon is properly applied - const event = new CustomEvent('weblink-created', { - detail: { - item: item, - url: url, - faviconUrl: faviconUrl - } - }); - document.dispatchEvent(event); - } - } } catch (error) { console.error("Error creating web link:", error); UIAlert("Error creating web link: " + error.message);