experiments in a post-browser web
10
fork

Configure Feed

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

fix(datastore): show addresses/visits/tags stats with safe error handling

resolve: regenerate schema

+31 -13
+2 -2
app/datastore/viewer.js
··· 116 116 statsArea.innerHTML = ''; 117 117 118 118 const statItems = [ 119 - { label: 'Items', value: stats.totalItems || stats.totalAddresses || 0 }, 120 - { label: 'Tags', value: stats.totalTags || 0 }, 119 + { label: 'Addresses', value: stats.totalAddresses || 0 }, 121 120 { label: 'Visits', value: stats.totalVisits || 0 }, 121 + { label: 'Tags', value: stats.totalTags || 0 }, 122 122 ]; 123 123 124 124 statItems.forEach(({ label, value }) => {
+25 -7
backend/electron/datastore.ts
··· 1895 1895 1896 1896 export function getStats(): DatastoreStats { 1897 1897 const d = getDb(); 1898 + 1899 + // Helper to safely count rows (returns 0 if table doesn't exist or query fails) 1900 + const safeCount = (sql: string): number => { 1901 + try { 1902 + return (d.prepare(sql).get() as { count: number }).count; 1903 + } catch { 1904 + return 0; 1905 + } 1906 + }; 1907 + 1908 + const safeAvg = (sql: string): number => { 1909 + try { 1910 + return (d.prepare(sql).get() as { avg: number | null }).avg || 0; 1911 + } catch { 1912 + return 0; 1913 + } 1914 + }; 1915 + 1898 1916 return { 1899 - totalAddresses: (d.prepare('SELECT COUNT(*) as count FROM addresses').get() as { count: number }).count, 1900 - totalVisits: (d.prepare('SELECT COUNT(*) as count FROM visits').get() as { count: number }).count, 1901 - avgVisitDuration: (d.prepare('SELECT AVG(duration) as avg FROM visits').get() as { avg: number | null }).avg || 0, 1902 - totalContent: (d.prepare('SELECT COUNT(*) as count FROM content').get() as { count: number }).count, 1903 - syncedContent: (d.prepare('SELECT COUNT(*) as count FROM content WHERE synced = 1').get() as { count: number }).count, 1904 - totalItems: (d.prepare('SELECT COUNT(*) as count FROM items').get() as { count: number }).count, 1905 - totalTags: (d.prepare('SELECT COUNT(*) as count FROM tags').get() as { count: number }).count, 1917 + totalAddresses: safeCount('SELECT COUNT(*) as count FROM addresses'), 1918 + totalVisits: safeCount('SELECT COUNT(*) as count FROM visits'), 1919 + avgVisitDuration: safeAvg('SELECT AVG(duration) as avg FROM visits'), 1920 + totalContent: safeCount('SELECT COUNT(*) as count FROM content'), 1921 + syncedContent: safeCount('SELECT COUNT(*) as count FROM content WHERE synced = 1'), 1922 + totalItems: safeCount('SELECT COUNT(*) as count FROM items'), 1923 + totalTags: safeCount('SELECT COUNT(*) as count FROM tags'), 1906 1924 }; 1907 1925 } 1908 1926
+1 -1
schema/generated/sqlite-full.sql
··· 1 1 -- Generated by schema/codegen.js 2 2 -- Schema version: 1 3 - -- Generated: 2026-02-02T08:01:19.478Z 3 + -- Generated: 2026-02-03T16:59:11.716Z 4 4 -- DO NOT EDIT - regenerate with: yarn schema:codegen 5 5 6 6 -- Unified content storage - URLs, text notes, tagsets, and images
+1 -1
schema/generated/sqlite-sync.sql
··· 1 1 -- Generated by schema/codegen.js 2 2 -- Schema version: 1 3 - -- Generated: 2026-02-02T08:01:19.479Z 3 + -- Generated: 2026-02-03T16:59:11.716Z 4 4 -- DO NOT EDIT - regenerate with: yarn schema:codegen 5 5 6 6 -- Unified content storage - URLs, text notes, tagsets, and images
+1 -1
schema/generated/types.rs
··· 1 1 // Generated by schema/codegen.js 2 2 // Schema version: 1 3 - // Generated: 2026-02-02T08:01:19.479Z 3 + // Generated: 2026-02-03T16:59:11.717Z 4 4 // DO NOT EDIT - regenerate with: yarn schema:codegen 5 5 6 6 use serde::{Deserialize, Serialize};
+1 -1
schema/generated/types.ts
··· 1 1 /** 2 2 * Generated by schema/codegen.js 3 3 * Schema version: 1 4 - * Generated: 2026-02-02T08:01:19.479Z 4 + * Generated: 2026-02-03T16:59:11.716Z 5 5 * DO NOT EDIT - regenerate with: yarn schema:codegen 6 6 */ 7 7