feat: Add Focus Tools (ADHD-friendly productivity features)
continuous-integration/drone/push Build is passing

- Add Focus Tools dashboard with collapsible help section
- Implement Quick Wins page for tasks under 15 minutes
- Add Single Task Focus mode to reduce overwhelm
- Create Body Doubling page for virtual co-working
- Add visual timer, break reminders, and energy tracking
- Implement hyperfocus protection alerts
- Add ADHD settings panel with customizable options
- Include full English and German translations
- Fix larger touch targets CSS to not break button layouts
- Add Playwright tests for Focus Tools features
This commit is contained in:
Paul Nothaft
2026-01-15 21:20:04 +01:00
parent 74ffef48d3
commit 7ce4f7efdc
31 changed files with 5651 additions and 11 deletions
+82
View File
@@ -738,6 +738,88 @@ Format:
}
}
/**
* Break down a large task into smaller, manageable subtasks for ADHD users.
*/
async breakdownTask(task: { title: string; description?: string; estimatedDuration?: number }): Promise<{
subtasks: { title: string; estimatedMinutes: number; order: number }[];
totalEstimatedMinutes: number;
encouragement: string;
}> {
const provider = await this.storage.getSystemSettings("ai_provider") || "openai";
const apiKey = await this.storage.getSystemSettings("ai_api_key");
const model = await this.storage.getSystemSettings("ai_model") || "gpt-4o";
const baseUrl = await this.storage.getSystemSettings("ai_base_url");
// Default fallback if AI is not configured
if (!apiKey && provider !== "ollama") {
return {
subtasks: [
{ title: `Start: ${task.title}`, estimatedMinutes: 5, order: 1 },
{ title: `Continue: ${task.title}`, estimatedMinutes: 10, order: 2 },
{ title: `Finish: ${task.title}`, estimatedMinutes: 5, order: 3 },
],
totalEstimatedMinutes: 20,
encouragement: "Du schaffst das! Nimm dir einen Schritt nach dem anderen vor.",
};
}
const prompt = `Du bist ein ADHS-Coach. Der Nutzer hat eine überwältigende Aufgabe, die in kleinere Schritte zerlegt werden muss.
Aufgabe: "${task.title}"
${task.description ? `Beschreibung: "${task.description}"` : ''}
${task.estimatedDuration ? `Geschätzte Dauer: ${task.estimatedDuration} Minuten` : ''}
Zerlege diese Aufgabe in 3-7 kleine, konkrete Schritte.
Jeder Schritt sollte:
- In 5-15 Minuten erledigt werden können
- Eine klare, aktionsorientierte Beschreibung haben (auf Deutsch)
- Einen konkreten Endpunkt haben
Antworte NUR mit einem JSON-Objekt. Keine Markdown-Formatierung.
Format:
{
"subtasks": [
{ "title": "Schritt beschreibung", "estimatedMinutes": 5, "order": 1 },
...
],
"totalEstimatedMinutes": number,
"encouragement": "Eine ermutigende Nachricht auf Deutsch"
}`;
const messages: ChatMessage[] = [
{ role: "system", content: "Du bist ein hilfreicher ADHS-Coach. Antworte nur mit validem JSON." },
{ role: "user", content: prompt }
];
try {
let responseText = "";
if (provider === "openai" || provider === "ollama") {
responseText = await this.chatOpenAI(provider, apiKey || "", model, baseUrl, messages, undefined, []);
} else if (provider === "anthropic") {
responseText = await this.chatAnthropic(apiKey || "", model, messages);
} else if (provider === "google") {
responseText = await this.chatGemini(apiKey || "", model, messages);
}
// Clean response
const cleanJson = responseText.replace(/```json/g, '').replace(/```/g, '').trim();
return JSON.parse(cleanJson);
} catch (error) {
console.error("AI Breakdown Error:", error);
// Fallback
return {
subtasks: [
{ title: `Schritt 1: ${task.title} vorbereiten`, estimatedMinutes: 5, order: 1 },
{ title: `Schritt 2: ${task.title} durchführen`, estimatedMinutes: 10, order: 2 },
{ title: `Schritt 3: ${task.title} abschließen`, estimatedMinutes: 5, order: 3 },
],
totalEstimatedMinutes: 20,
encouragement: "Jeder kleine Schritt zählt! Du machst das großartig.",
};
}
}
private async chatAnthropic(apiKey: string, model: string, messages: any[]): Promise<string> {
const systemMessage = messages.find(m => m.role === "system")?.content || "";
const userAssistantMessages = messages.filter(m => m.role !== "system");