experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

url normalization, groups fixes

+41 -9
+22 -5
app/datastore/history.js
··· 1 1 // Navigation history tracking helper 2 2 import api from '../api.js'; 3 3 4 + // Normalize URL by ensuring root paths have trailing slash 5 + // Matches normalization in main process 6 + const normalizeUrl = (uri) => { 7 + try { 8 + const url = new URL(uri); 9 + if (!url.pathname || url.pathname === '') { 10 + url.pathname = '/'; 11 + } 12 + return url.toString(); 13 + } catch (error) { 14 + return uri; 15 + } 16 + }; 17 + 4 18 /** 5 19 * Track a navigation event 6 20 * @param {string} uri - The URL navigated to ··· 13 27 */ 14 28 export const trackNavigation = async (uri, options = {}) => { 15 29 try { 30 + // Normalize URI for consistent lookups 31 + const normalizedUri = normalizeUrl(uri); 32 + 16 33 // Get or create address 17 34 let addressId; 18 35 const addressesResult = await api.datastore.queryAddresses({}); ··· 22 39 } 23 40 24 41 const addresses = addressesResult.data; 25 - const existing = addresses.find(addr => addr.uri === uri); 42 + const existing = addresses.find(addr => addr.uri === normalizedUri); 26 43 27 44 if (existing) { 28 45 addressId = existing.id; 29 46 } else { 30 - // Create new address 31 - const addResult = await api.datastore.addAddress(uri, { 47 + // Create new address (using normalized URI) 48 + const addResult = await api.datastore.addAddress(normalizedUri, { 32 49 title: options.title || '', 33 50 mimeType: options.mimeType || 'text/html' 34 51 }); ··· 39 56 } 40 57 41 58 addressId = addResult.id; 42 - console.log('Created new address:', addressId, uri); 59 + console.log('Created new address:', addressId, normalizedUri); 43 60 } 44 61 45 62 // Add visit ··· 61 78 console.log('Tracked navigation:', { 62 79 visitId: visitResult.id, 63 80 addressId, 64 - uri, 81 + uri: normalizedUri, 65 82 source: options.source 66 83 }); 67 84
+1 -2
app/index.js
··· 3 3 import windowManager from "./windows.js"; 4 4 import api from './api.js'; 5 5 import fc from './features.js'; 6 - // Use absolute peek:// URL since relative paths stay within the app host 7 - import extensionLoader from 'peek://extensions/loader.js'; 6 + import extensionLoader from './extensions/loader.js'; 8 7 9 8 const { id, labels, schemas, storageKeys, defaults } = appConfig; 10 9
extensions/loader.js app/extensions/loader.js
+18 -2
index.js
··· 223 223 } 224 224 }; 225 225 226 + // Normalize URL by ensuring root paths have trailing slash 227 + // e.g., https://example.com -> https://example.com/ 228 + const normalizeUrl = (uri) => { 229 + try { 230 + const url = new URL(uri); 231 + // If pathname is empty, set it to / 232 + if (!url.pathname || url.pathname === '') { 233 + url.pathname = '/'; 234 + } 235 + return url.toString(); 236 + } catch (error) { 237 + return uri; 238 + } 239 + }; 240 + 226 241 // ***** Features / Strings ***** 227 242 228 243 const labels = { ··· 1264 1279 ipcMain.handle('datastore-add-address', async (ev, data) => { 1265 1280 try { 1266 1281 const { uri, options = {} } = data; 1267 - const parsed = parseUrl(uri); 1282 + const normalizedUri = normalizeUrl(uri); 1283 + const parsed = parseUrl(normalizedUri); 1268 1284 const addressId = generateId('addr'); 1269 1285 const timestamp = now(); 1270 1286 1271 1287 const row = { 1272 - uri, 1288 + uri: normalizedUri, 1273 1289 protocol: options.protocol || parsed.protocol, 1274 1290 domain: options.domain || parsed.domain, 1275 1291 path: options.path || parsed.path,