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);