diff --git a/bookmarks.backup.json b/bookmarks.backup.json index 15807e2..321638d 100644 --- a/bookmarks.backup.json +++ b/bookmarks.backup.json @@ -1,8 +1,8 @@ [ { - "title": "Apple", - "url": "apple.com", - "icon": "data:image/svg+xml;utf8,%3Csvg%20role%3D%22img%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20style%3D%22color%3A%20white%3B%22%3E%3Ctitle%3EApple%3C%2Ftitle%3E%3Cpath%20d%3D%22M12.152%206.896c-.948%200-2.415-1.078-3.96-1.04-2.04.027-3.91%201.183-4.961%203.014-2.117%203.675-.546%209.103%201.519%2012.09%201.013%201.454%202.208%203.09%203.792%203.039%201.52-.065%202.09-.987%203.935-.987%201.831%200%202.35.987%203.96.948%201.637-.026%202.676-1.48%203.676-2.948%201.156-1.688%201.636-3.325%201.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04%202.48-4.494%202.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675%201.09-4.61%201.09zM15.53%203.83c.843-1.012%201.4-2.427%201.245-3.83-1.207.052-2.662.805-3.532%201.818-.78.896-1.454%202.338-1.273%203.714%201.338.104%202.715-.688%203.559-1.701%22%2F%3E%3C%2Fsvg%3E", + "title": "Discord", + "url": "discord.gg", + "icon": "data:image/svg+xml;utf8,%3Csvg%20role%3D%22img%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ctitle%3EDiscord%3C%2Ftitle%3E%3Cpath%20d%3D%22M20.317%204.3698a19.7913%2019.7913%200%2000-4.8851-1.5152.0741.0741%200%2000-.0785.0371c-.211.3753-.4447.8648-.6083%201.2495-1.8447-.2762-3.68-.2762-5.4868%200-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077%200%2000-.0785-.037%2019.7363%2019.7363%200%2000-4.8852%201.515.0699.0699%200%2000-.0321.0277C.5334%209.0458-.319%2013.5799.0992%2018.0578a.0824.0824%200%2000.0312.0561c2.0528%201.5076%204.0413%202.4228%205.9929%203.0294a.0777.0777%200%2000.0842-.0276c.4616-.6304.8731-1.2952%201.226-1.9942a.076.076%200%2000-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077%200%2001-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743%200%2001.0776-.0105c3.9278%201.7933%208.18%201.7933%2012.0614%200a.0739.0739%200%2001.0785.0095c.1202.099.246.1981.3728.2924a.077.077%200%2001-.0066.1276%2012.2986%2012.2986%200%2001-1.873.8914.0766.0766%200%2000-.0407.1067c.3604.698.7719%201.3628%201.225%201.9932a.076.076%200%2000.0842.0286c1.961-.6067%203.9495-1.5219%206.0023-3.0294a.077.077%200%2000.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061%200%2000-.0312-.0286zM8.02%2015.3312c-1.1825%200-2.1569-1.0857-2.1569-2.419%200-1.3332.9555-2.4189%202.157-2.4189%201.2108%200%202.1757%201.0952%202.1568%202.419%200%201.3332-.9555%202.4189-2.1569%202.4189zm7.9748%200c-1.1825%200-2.1569-1.0857-2.1569-2.419%200-1.3332.9554-2.4189%202.1569-2.4189%201.2108%200%202.1757%201.0952%202.1568%202.419%200%201.3332-.946%202.4189-2.1568%202.4189Z%22%2F%3E%3C%2Fsvg%3E", "iconSet": "simple" } ] \ No newline at end of file diff --git a/main.js b/main.js index dc8132f..8d02349 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,5 @@ -const { app, BrowserWindow, ipcMain, session, screen, shell } = require('electron'); +const { app, BrowserWindow, ipcMain, session, screen, shell, dialog } = require('electron'); +const { pathToFileURL } = require('url'); const fs = require('fs'); const path = require('path'); const os = require('os'); @@ -548,3 +549,29 @@ ipcMain.handle('open-devtools', (event) => { } return contents.isDevToolsOpened(); }); + +// Open local file dialog -> returns file:// URL (or null if cancelled) +ipcMain.handle('show-open-file-dialog', async () => { + try { + const result = await dialog.showOpenDialog({ + properties: ['openFile'], + filters: [ + { name: 'HTML Files', extensions: ['html', 'htm', 'xhtml'] }, + { name: 'All Files', extensions: ['*'] } + ] + }); + if (result.canceled || !result.filePaths || !result.filePaths.length) return null; + const filePath = result.filePaths[0]; + try { + return pathToFileURL(filePath).href; + } catch { + // Fallback manual conversion + let p = filePath.replace(/\\/g, '/'); + if (!p.startsWith('/')) p = '/' + p; // ensure leading slash for drive letters + return 'file://' + (p.startsWith('/') ? '/' : '') + p; // double slash safety + } + } catch (err) { + console.error('open-file dialog failed:', err); + return null; + } +}); diff --git a/preload.js b/preload.js index 70125c1..bc93181 100644 --- a/preload.js +++ b/preload.js @@ -55,6 +55,14 @@ const electronAPI = { console.error('IPC open-devtools error:', err); return Promise.reject(err); } + }, + openLocalFile: async () => { + try { + return await ipcRenderer.invoke('show-open-file-dialog'); + } catch (err) { + console.error('IPC openLocalFile error:', err); + return null; + } } }; diff --git a/renderer/script.js b/renderer/script.js index 3698f3d..7301a93 100644 --- a/renderer/script.js +++ b/renderer/script.js @@ -295,16 +295,43 @@ function updateTabMetadata(id, key, value) { } function navigate() { - const input = urlBox.value.trim(); + const rawInput = urlBox.value.trim(); + // Strip surrounding single or double quotes (common when copying paths) + let input = rawInput; + if ((input.startsWith('"') && input.endsWith('"')) || (input.startsWith("'") && input.endsWith("'"))) { + input = input.slice(1, -1); + } + // If we modified input (removed quotes), reflect it back in the UI for clarity + if (input !== rawInput) { + urlBox.value = input; + } const tab = tabs.find(t => t.id === activeTabId); if (!tab) return; // decide if this is a search query or a URL/internal page const hasProtocol = /^https?:\/\//i.test(input); + const isFileProtocol = /^file:\/\//i.test(input); + const looksLikeLocalPath = /^(?:[A-Za-z]:\\|\\\\|\/?)[^?]*\.(?:x?html?)$/i.test(input); const isInternal = input.startsWith('browser://'); const isLikelyUrl = hasProtocol || input.includes('.'); let resolved; - if (!isInternal && !isLikelyUrl) { + if (isFileProtocol) { + resolved = input; // Electron will load file:// directly in + } else if (looksLikeLocalPath) { + // Convert Windows or *nix style path to file:// URL + let p = input; + // Expand backslashes + p = p.replace(/\\/g, '/'); + // If it starts with a drive letter like C:/ ensure single leading slash + if (/^[A-Za-z]:\//.test(p)) { + resolved = 'file:///' + encodeURI(p); + } else if (p.startsWith('/')) { + resolved = 'file://' + encodeURI(p); // already absolute + } else { + // relative path relative to app root (renderer directory) + resolved = 'file://' + encodeURI(p); // fallback; treat as relative from working dir + } + } else if (!isInternal && !isLikelyUrl) { resolved = `https://www.google.com/search?q=${encodeURIComponent(input)}`; } else { resolved = resolveInternalUrl(input); @@ -334,6 +361,21 @@ function navigate() { scheduleUpdateNavButtons(); } +// Keyboard shortcut: Ctrl+O (Cmd+O on mac) to open a local file +document.addEventListener('keydown', async (e) => { + const isAccel = (navigator.platform.includes('Mac') ? e.metaKey : e.ctrlKey); + if (isAccel && e.key.toLowerCase() === 'o') { + e.preventDefault(); + if (window.electronAPI && window.electronAPI.openLocalFile) { + const fileUrl = await window.electronAPI.openLocalFile(); + if (fileUrl) { + urlBox.value = fileUrl; + navigate(); + } + } + } +}); + function convertHomeTabToWebview(tabId, inputUrl, resolvedUrl) { const tab = tabs.find(t => t.id === tabId); if (!tab) return; diff --git a/site-history.json b/site-history.json index e458261..0637a08 100644 --- a/site-history.json +++ b/site-history.json @@ -1,4 +1 @@ -[ - "https://discord.com/", - "https://nebula.zambazosmedia.group/" -] \ No newline at end of file +[] \ No newline at end of file