experiments in a post-browser web
10
fork

Configure Feed

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

fix migration, add diagnostics page

+196 -4
+188
app/diagnostic.html
··· 24 24 <button onclick="copyResults()">Copy Results</button> 25 25 </div> 26 26 27 + <div class="section" style="margin-top: 20px; padding: 15px; background: #2d2d2d; border-radius: 5px;"> 28 + <h3 style="margin-top: 0; color: #ff9800;">Migration Tools</h3> 29 + <p style="color: #aaa; font-size: 12px;">Use these to fix migration issues:</p> 30 + <button onclick="forceMigrateAll()" style="background: #ff9800; font-weight: bold;">Migrate ALL to Datastore (Recommended)</button> 31 + <button onclick="forceMigratePeeks()">Migrate Peeks</button> 32 + <button onclick="forceMigrateSlides()">Migrate Slides</button> 33 + <button onclick="clearExtensionSettings()" style="background: #f44336;">Clear extension_settings</button> 34 + </div> 35 + 27 36 <div id="output"></div> 28 37 29 38 <script type="module"> ··· 124 133 navigator.clipboard.writeText(text).then(() => { 125 134 alert('Copied to clipboard!'); 126 135 }); 136 + }; 137 + 138 + // Extension UUID to shortname mapping (from app/config.js) 139 + const extensionMap = { 140 + 'ef3bd271-d408-421f-9338-47b615571e43': 'peeks', 141 + '434108f3-18a6-437a-b507-2f998f693bb2': 'slides', 142 + '82de735f-a4b7-4fe6-a458-ec29939ae00d': 'groups', 143 + 'cee1225d-40ac-41e5-a34c-e2edba69d599': 'cmd', 144 + '30c25027-d367-4595-b37f-9db3de853c37': 'scripts' 145 + }; 146 + 147 + // Fix function: Copy from UUID keys to shortname keys in localStorage 148 + window.fixPeeksAndSlides = function() { 149 + clearOutput(); 150 + let results = []; 151 + 152 + // Peeks: copy from UUID to shortname 153 + const peeksUuidItems = localStorage.getItem('ef3bd271-d408-421f-9338-47b615571e43+items'); 154 + const peeksUuidPrefs = localStorage.getItem('ef3bd271-d408-421f-9338-47b615571e43+prefs'); 155 + 156 + if (peeksUuidItems) { 157 + localStorage.setItem('peeks+items', peeksUuidItems); 158 + const items = JSON.parse(peeksUuidItems); 159 + results.push(`Peeks items: Copied ${items.length} items from UUID key to shortname key`); 160 + } else { 161 + results.push('Peeks items: No UUID data found'); 162 + } 163 + 164 + if (peeksUuidPrefs) { 165 + localStorage.setItem('peeks+prefs', peeksUuidPrefs); 166 + results.push(`Peeks prefs: Copied from UUID key to shortname key`); 167 + } 168 + 169 + // Slides: copy from UUID to shortname 170 + const slidesUuidItems = localStorage.getItem('434108f3-18a6-437a-b507-2f998f693bb2+items'); 171 + const slidesUuidPrefs = localStorage.getItem('434108f3-18a6-437a-b507-2f998f693bb2+prefs'); 172 + 173 + if (slidesUuidItems) { 174 + localStorage.setItem('slides+items', slidesUuidItems); 175 + const items = JSON.parse(slidesUuidItems); 176 + results.push(`Slides items: Copied ${items.length} items from UUID key to shortname key`); 177 + } else { 178 + results.push('Slides items: No UUID data found'); 179 + } 180 + 181 + if (slidesUuidPrefs) { 182 + localStorage.setItem('slides+prefs', slidesUuidPrefs); 183 + results.push(`Slides prefs: Copied from UUID key to shortname key`); 184 + } 185 + 186 + // Groups: copy from UUID to shortname 187 + const groupsUuidPrefs = localStorage.getItem('82de735f-a4b7-4fe6-a458-ec29939ae00d+prefs'); 188 + if (groupsUuidPrefs) { 189 + localStorage.setItem('groups+prefs', groupsUuidPrefs); 190 + results.push(`Groups prefs: Copied from UUID key to shortname key`); 191 + } 192 + 193 + log('Fix Results', results.join('\n')); 194 + log('Next Steps', 'Close Settings and reopen it. Your peeks/slides should now show the correct data.\n\nIf using the datastore, also click "Force Migrate" buttons.'); 195 + 196 + // Refresh display 197 + window.dumpLocalStorage(); 198 + }; 199 + 200 + window.clearExtensionSettings = async function() { 201 + if (!confirm('This will clear all extension settings from the datastore. Continue?')) return; 202 + 203 + try { 204 + // Get all rows and delete them one by one 205 + const result = await api.datastore.getTable('extension_settings'); 206 + if (result.success) { 207 + const ids = Object.keys(result.data); 208 + for (const id of ids) { 209 + // Use raw SQL via evaluate if available, or just log 210 + console.log('Would delete:', id); 211 + } 212 + log('Clear Result', `Found ${ids.length} rows. Note: Direct deletion not implemented yet.\nPlease restart app after clearing to re-run migration.`); 213 + } 214 + } catch (err) { 215 + log('Clear Error', err.message); 216 + } 217 + 218 + // For now, show what would be cleared 219 + await window.dumpDatastore(); 220 + }; 221 + 222 + async function migrateExtension(uuid, shortname) { 223 + // Try both UUID keys and shortname keys (prefer UUID as it's the original) 224 + const uuidPrefsKey = `${uuid}+prefs`; 225 + const uuidItemsKey = `${uuid}+items`; 226 + const shortPrefsKey = `${shortname}+prefs`; 227 + const shortItemsKey = `${shortname}+items`; 228 + 229 + // Prefer UUID keys, fallback to shortname keys 230 + const prefsStr = localStorage.getItem(uuidPrefsKey) || localStorage.getItem(shortPrefsKey); 231 + const itemsStr = localStorage.getItem(uuidItemsKey) || localStorage.getItem(shortItemsKey); 232 + 233 + let migrated = []; 234 + const now = Date.now(); 235 + 236 + const prefsSource = localStorage.getItem(uuidPrefsKey) ? uuidPrefsKey : 237 + localStorage.getItem(shortPrefsKey) ? shortPrefsKey : null; 238 + const itemsSource = localStorage.getItem(uuidItemsKey) ? uuidItemsKey : 239 + localStorage.getItem(shortItemsKey) ? shortItemsKey : null; 240 + 241 + if (prefsStr) { 242 + try { 243 + const prefs = JSON.parse(prefsStr); 244 + await api.datastore.setRow('extension_settings', `${shortname}:prefs`, { 245 + extensionId: shortname, 246 + key: 'prefs', 247 + value: JSON.stringify(prefs), 248 + updatedAt: now 249 + }); 250 + migrated.push(`prefs: migrated from ${prefsSource}`); 251 + } catch (e) { 252 + migrated.push(`prefs error: ${e.message}`); 253 + } 254 + } else { 255 + migrated.push(`prefs: not found in localStorage`); 256 + } 257 + 258 + if (itemsStr) { 259 + try { 260 + const items = JSON.parse(itemsStr); 261 + await api.datastore.setRow('extension_settings', `${shortname}:items`, { 262 + extensionId: shortname, 263 + key: 'items', 264 + value: JSON.stringify(items), 265 + updatedAt: now 266 + }); 267 + migrated.push(`items: Array[${items.length}] migrated from ${itemsSource}`); 268 + } catch (e) { 269 + migrated.push(`items error: ${e.message}`); 270 + } 271 + } else { 272 + migrated.push(`items: not found in localStorage`); 273 + } 274 + 275 + return migrated; 276 + } 277 + 278 + window.forceMigratePeeks = async function() { 279 + clearOutput(); 280 + const result = await migrateExtension('ef3bd271-d408-421f-9338-47b615571e43', 'peeks'); 281 + log('Peeks Migration', result.join('\n')); 282 + 283 + // Notify extension to reload 284 + api.publish('peeks:settings-changed', {}, api.scopes.GLOBAL); 285 + log('Notification', 'Sent peeks:settings-changed to reload extension'); 286 + 287 + await window.dumpDatastore(); 288 + }; 289 + 290 + window.forceMigrateSlides = async function() { 291 + clearOutput(); 292 + const result = await migrateExtension('434108f3-18a6-437a-b507-2f998f693bb2', 'slides'); 293 + log('Slides Migration', result.join('\n')); 294 + 295 + // Notify extension to reload 296 + api.publish('slides:settings-changed', {}, api.scopes.GLOBAL); 297 + log('Notification', 'Sent slides:settings-changed to reload extension'); 298 + 299 + await window.dumpDatastore(); 300 + }; 301 + 302 + window.forceMigrateAll = async function() { 303 + clearOutput(); 304 + 305 + for (const [uuid, shortname] of Object.entries(extensionMap)) { 306 + const result = await migrateExtension(uuid, shortname); 307 + log(`${shortname} Migration`, result.join('\n')); 308 + 309 + // Notify extension 310 + api.publish(`${shortname}:settings-changed`, {}, api.scopes.GLOBAL); 311 + } 312 + 313 + log('Notification', 'Sent settings-changed for all extensions'); 314 + await window.dumpDatastore(); 127 315 }; 128 316 129 317 // Auto-dump on load
+8 -4
backend/electron/ipc.ts
··· 540 540 }); 541 541 542 542 // Extension settings handlers 543 + // Note: preload sends { extId } but we accept both extId and id for compatibility 543 544 ipcMain.handle('extension-settings-get', async (ev, data) => { 544 545 try { 545 546 const db = getDb(); 547 + const extId = data.extId || data.id; 546 548 const settings = db.prepare( 547 549 'SELECT * FROM extension_settings WHERE extensionId = ?' 548 - ).all(data.id) as Array<{ key: string; value: string }>; 550 + ).all(extId) as Array<{ key: string; value: string }>; 549 551 550 552 const result: Record<string, unknown> = {}; 551 553 for (const s of settings) { ··· 565 567 ipcMain.handle('extension-settings-set', async (ev, data) => { 566 568 try { 567 569 const db = getDb(); 568 - const extId = data.id; 570 + const extId = data.extId || data.id; 569 571 const settings = data.settings || {}; 570 572 571 573 for (const [key, value] of Object.entries(settings)) { ··· 586 588 ipcMain.handle('extension-settings-get-key', async (ev, data) => { 587 589 try { 588 590 const db = getDb(); 591 + const extId = data.extId || data.id; 589 592 const setting = db.prepare( 590 593 'SELECT * FROM extension_settings WHERE extensionId = ? AND key = ?' 591 - ).get(data.id, data.key) as { value: string } | undefined; 594 + ).get(extId, data.key) as { value: string } | undefined; 592 595 593 596 if (!setting) { 594 597 return { success: true, data: null }; ··· 608 611 ipcMain.handle('extension-settings-set-key', async (ev, data) => { 609 612 try { 610 613 const db = getDb(); 614 + const extId = data.extId || data.id; 611 615 const jsonValue = JSON.stringify(data.value); 612 616 db.prepare(` 613 617 INSERT OR REPLACE INTO extension_settings (id, extensionId, key, value, updatedAt) 614 618 VALUES (?, ?, ?, ?, ?) 615 - `).run(`${data.id}_${data.key}`, data.id, data.key, jsonValue, Date.now()); 619 + `).run(`${extId}_${data.key}`, extId, data.key, jsonValue, Date.now()); 616 620 617 621 return { success: true, data: { key: data.key, value: data.value } }; 618 622 } catch (error) {