Add pomodoro, microbreaks, breathing guide, screen dimming, presentation mode, goals, multi-monitor, and activity manager

This commit is contained in:
2026-02-07 15:11:44 +02:00
parent 3ca3857141
commit be41f61740
28 changed files with 3792 additions and 448 deletions

View File

@@ -106,10 +106,55 @@ export function getCategoryLabel(cat: BreakActivity["category"]): string {
return categoryLabels[cat];
}
/** Pick a random activity, optionally excluding a previous one */
export function pickRandomActivity(exclude?: BreakActivity): BreakActivity {
const pool = exclude
? breakActivities.filter((a) => a.text !== exclude.text)
: breakActivities;
return pool[Math.floor(Math.random() * pool.length)];
/** Pick a random activity, optionally excluding a previous one.
* When config is provided, respects disabled/favorite/custom activity settings. */
export function pickRandomActivity(
exclude?: BreakActivity,
config?: {
disabled_builtin_activities?: string[];
favorite_builtin_activities?: string[];
custom_activities?: Array<{ category: string; text: string; is_favorite: boolean; enabled: boolean }>;
favorite_weight?: number;
},
): BreakActivity {
const disabled = new Set(config?.disabled_builtin_activities ?? []);
const favorites = new Set(config?.favorite_builtin_activities ?? []);
const weight = config?.favorite_weight ?? 3;
// Build pool: enabled builtins + enabled customs
let pool: BreakActivity[] = breakActivities.filter((a) => !disabled.has(a.text));
// Add enabled custom activities
if (config?.custom_activities) {
for (const ca of config.custom_activities) {
if (ca.enabled) {
const cat = (["eyes", "stretch", "breathing", "movement"].includes(ca.category)
? ca.category
: "movement") as BreakActivity["category"];
pool.push({ category: cat, text: ca.text });
}
}
}
// Exclude previous
if (exclude) {
pool = pool.filter((a) => a.text !== exclude.text);
}
if (pool.length === 0) {
return exclude ?? breakActivities[0];
}
// Build weighted pool: favorites appear `weight` times
const weighted: BreakActivity[] = [];
for (const a of pool) {
const isFav = favorites.has(a.text) ||
(config?.custom_activities?.some((c) => c.text === a.text && c.is_favorite) ?? false);
const count = isFav ? weight : 1;
for (let i = 0; i < count; i++) {
weighted.push(a);
}
}
return weighted[Math.floor(Math.random() * weighted.length)];
}