1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// scripts/seed.ts — ensure default sources + settings exist in the local db.
// Idempotent per-kind: inserts any kind not already present. Safe to re-run.
// Run: pnpm db:seed (uses relative imports so plain `tsx` resolves it)
import { randomUUID } from "node:crypto";
import { db } from "../db/index";
import { sources, settings } from "../db/schema";
import { SETTINGS_DEFAULTS } from "../lib/schemas/setting";
const vault = process.env.OBSIDIAN_VAULT ?? "/Users/kortum/Humdrum";
interface Seed {
kind: string;
label: string;
enabled: boolean;
refreshSeconds: number;
size: string;
config: Record<string, unknown>;
}
// Keyless / local sources start enabled; credentialed ones start disabled (enable
// them in Settings once their .env.local keys are filled).
const DEFAULTS: Seed[] = [
{ kind: "weather", label: "Weather", enabled: true, refreshSeconds: 1800, size: "wide", config: {} },
{ kind: "todos", label: "Today", enabled: true, refreshSeconds: 300, size: "tall", config: { list: "Today" } },
{ kind: "calendar", label: "Calendar", enabled: true, refreshSeconds: 1800, size: "md", config: { mode: "local", calendars: [], urls: [], daysAhead: 7 } },
{ kind: "onthisday", label: "On This Day", enabled: true, refreshSeconds: 86_400, size: "tall", config: {} },
{
kind: "news",
label: "News",
enabled: true,
refreshSeconds: 1800,
size: "tall",
config: {
feeds: [
"https://hnrss.org/frontpage",
"https://www.theverge.com/rss/index.xml",
"https://feeds.arstechnica.com/arstechnica/index",
"http://feeds.bbci.co.uk/news/world/rss.xml",
"https://www.theguardian.com/world/rss",
"https://feeds.npr.org/1001/rss.xml",
],
limit: 14,
},
},
// Screamer news briefs — replaces raw headlines in the paper. Keyless.
{
kind: "briefs",
label: "News Briefs",
enabled: true,
refreshSeconds: 3600,
size: "tall",
config: {
baseUrl: "https://screamer.humdrum.one",
categories: [
{ key: "politics", name: "Politics" },
{ key: "tech", name: "Technology" },
{ key: "sports", name: "Sports" },
{ key: "culture", name: "Culture" },
],
lookbackDays: 7,
},
},
// Personal RSS — starts empty; add your own feed URLs in Settings → My Feeds.
{ kind: "feeds", label: "My Feeds", enabled: true, refreshSeconds: 1800, size: "tall", config: { feeds: [], limit: 14 } },
{ kind: "sports", label: "Scores", enabled: false, refreshSeconds: 600, size: "md", config: { leagues: [], teams: [] } },
{ kind: "github", label: "GitHub", enabled: false, refreshSeconds: 900, size: "md", config: {} },
{ kind: "vercel", label: "Vercel", enabled: false, refreshSeconds: 600, size: "md", config: {} },
{ kind: "mastodon", label: "Mastodon", enabled: false, refreshSeconds: 600, size: "md", config: {} },
{ kind: "bluesky", label: "Bluesky", enabled: false, refreshSeconds: 600, size: "md", config: {} },
// Keyless live-stat sources. Air quality + Hacker News work out of the box;
// Markets seeds a starter watchlist; Uptime starts empty (add URLs in Settings).
{ kind: "aqi", label: "Air Quality", enabled: true, refreshSeconds: 3600, size: "md", config: {} },
{
kind: "markets",
label: "Markets",
enabled: true,
refreshSeconds: 900,
size: "md",
config: { symbols: ["AAPL", "NVDA", "BTC-USD", "ETH-USD"] },
},
{ kind: "hackernews", label: "Hacker News", enabled: true, refreshSeconds: 1800, size: "tall", config: { limit: 10, subreddits: [] } },
{ kind: "uptime", label: "Uptime", enabled: false, refreshSeconds: 600, size: "md", config: { urls: [] } },
{ kind: "obsidian", label: "Obsidian", enabled: true, refreshSeconds: 600, size: "md", config: { vault, limit: 8 } },
{ kind: "links", label: "Links", enabled: true, refreshSeconds: 86_400, size: "wide", config: { links: [] } },
// Now Playing panel was removed from the dashboard; keep the source seeded but
// disabled so it isn't polled for a card that no longer exists.
{ kind: "cider", label: "Now Playing", enabled: false, refreshSeconds: 30, size: "md", config: { host: "http://localhost:10767", appToken: "" } },
];
function main() {
const existing = db.select().from(sources).all();
const present = new Set(existing.map((s) => s.kind));
let pos = existing.length;
let added = 0;
for (const s of DEFAULTS) {
if (present.has(s.kind)) continue;
db.insert(sources)
.values({
id: randomUUID(),
kind: s.kind,
label: s.label,
enabled: s.enabled,
config: s.config,
refreshSeconds: s.refreshSeconds,
position: pos++,
size: s.size,
})
.run();
added++;
}
db.insert(settings)
.values({ key: "settings", value: SETTINGS_DEFAULTS })
.onConflictDoNothing()
.run();
console.log(added > 0 ? `Seeded ${added} new source(s).` : "All source kinds already present.");
}
main();
|