fix: Mobile responsiveness for Focus Tools & AI chat improvements
continuous-integration/drone/push Build is passing

Mobile Responsiveness:
- Fix headers on all Focus Tools pages (responsive text sizes, proper spacing)
- Fix BodyDoublingLobby form grid (stacks on mobile)
- Add shrink-0 and min-w-0 to prevent flex overflow issues

AI Chat Improvements:
- Add labelName parameter to createTask tool for easier label assignment
- Auto-match or create labels when labelName is provided
- Improve system prompt to act immediately without confirmation
- Clearer instructions about using tools and handling labels
- Better support for Ollama models with function calling
This commit is contained in:
Paul Nothaft
2026-01-16 15:58:00 +01:00
parent 7ce4f7efdc
commit f165ae52e6
12 changed files with 3682 additions and 2219 deletions
+55 -14
View File
@@ -60,15 +60,26 @@ Answer the user's questions based on this context. Be concise, helpful, and frie
### 🛠️ AVAILABLE TOOLS
You can create, search, update, and delete tasks using the provided tools.
**CRITICAL: ALWAYS USE TOOLS IMMEDIATELY**
- When the user asks to create a task, IMMEDIATELY call createTask - DO NOT ask for confirmation.
- When the user provides multiple tasks, call createTask for EACH task in parallel.
- Never say "I will create..." or "Should I create..." - just DO IT.
**1. Task Management**
- **Create**: Use 'createTask'. Title is required.
- *Labels*: Use 'labelName' parameter with the label text (e.g., "Work", "Personal", "Shopping"). The system will automatically match or create the label.
- *Bulk Creation*: If the user provides a list of tasks, call 'createTask' multiple times in parallel.
- *Relative Dates*: Understand natural language! "Morgen" = Tomorrow, "Next Friday" = Date of next Friday. Always calculate the specific ISO string based on 'Current Date/Time'.
- *Priority*: Infer priority from context. Urgent/important = high, regular = medium, minor = low.
- *Returns*: The tool returns the created Task ID. Remember this ID for immediate edits.
- **Update/Delete**: First SEARCH for the task ID using 'searchTasks' (search by title), then use 'updateTask' or 'deleteTask'.
- *Editing Recently Created*: If the user says "Change that to...", refer to the ID of the task you just created.
- *Delete All*: Search all, then delete each.
- **Labels**: Use 'getLabels' tags.
### 🏷️ LABEL RULES
- When the user mentions a category like "Work task", "Personal errand", "Shopping item" - use the labelName parameter.
- Common labels: Work, Personal, Shopping, Health, Finance, Home, Family, Study
- The system automatically matches existing labels or creates new ones.
### 🛡️ SAFETY RULES
- **DELETION CONFIRMATION**: Before using 'deleteTask', you MUST:
@@ -89,10 +100,12 @@ You can create, search, update, and delete tasks using the provided tools.
### 📅 DATE & TIME RULES
- **"Today"**: Use the 'Current Date/Time' context.
- **"Morgen" / "Tomorrow"**: Add 1 day to Current Date.
- **"Next week"**: Add 7 days to Current Date.
- **Scheduling**: When using 'scheduleTask', inform the user specifically *when* you scheduled it.
IMPORTANT: Do NOT show Task IDs to the user. Reference tasks by Title.
CRITICAL: After executing tools, provide a concise summary of your actions.`;
CRITICAL: After executing tools, provide a concise summary of your actions.
CRITICAL: ACT IMMEDIATELY. Do not ask for confirmation before creating tasks.`;
}
// Variable replacement if using stored template
@@ -311,19 +324,20 @@ CRITICAL: After executing tools, provide a concise summary of your actions.`;
type: "function",
function: {
name: "createTask",
description: "Create a new task for the user.",
description: "Create a new task for the user. ALWAYS call this tool immediately when the user asks to create a task - do not ask for confirmation first.",
parameters: {
type: "object",
properties: {
title: { type: "string", description: "The title of the task (required)." },
description: { type: "string" },
priority: { type: "string", enum: ["low", "medium", "high"] },
status: { type: "string", enum: ["todo", "inProgress", "done"] },
dueDate: { type: "string", description: "ISO 8601 format (YYYY-MM-DD)." },
labelId: { type: "string" },
description: { type: "string", description: "Optional task description." },
priority: { type: "string", enum: ["low", "medium", "high"], description: "Task priority. Default: medium." },
status: { type: "string", enum: ["todo", "inProgress", "done"], description: "Task status. Default: todo." },
dueDate: { type: "string", description: "Due date in ISO 8601 format (YYYY-MM-DD). Calculate from relative dates like 'tomorrow', 'next week'." },
labelId: { type: "string", description: "The label ID (UUID). Use getLabels first to find the correct ID." },
labelName: { type: "string", description: "Alternative: Label name (e.g., 'Work', 'Personal'). Will be matched or created automatically." },
estimatedDuration: { type: "integer", description: "Estimated duration in minutes." },
parentTaskId: { type: "string" },
startDate: { type: "string" }
parentTaskId: { type: "string", description: "Parent task ID for subtasks." },
startDate: { type: "string", description: "Start date in ISO 8601 format." }
},
required: ["title"]
}
@@ -523,13 +537,40 @@ CRITICAL: After executing tools, provide a concise summary of your actions.`;
result = { success: true, labelId: newLabel.id, message: "Label created." };
}
} else if (fnName === "createTask") {
// Handle labelName -> labelId conversion
let resolvedLabelId = args.labelId || null;
let labelMessage = "";
if (!resolvedLabelId && args.labelName) {
const labels = await this.storage.getLabels(user.id);
const matchedLabel = labels.find(l =>
l.name.toLowerCase() === args.labelName.toLowerCase() ||
l.name.toLowerCase().includes(args.labelName.toLowerCase()) ||
args.labelName.toLowerCase().includes(l.name.toLowerCase())
);
if (matchedLabel) {
resolvedLabelId = matchedLabel.id;
labelMessage = ` with label "${matchedLabel.name}"`;
} else {
// Create new label if not found
const newLabel = await this.storage.createLabel({
name: args.labelName,
color: "#6366f1",
creatorId: user.id
});
resolvedLabelId = newLabel.id;
labelMessage = ` with new label "${args.labelName}"`;
}
}
const taskData = {
title: args.title,
description: args.description || null,
priority: args.priority || "medium",
status: args.status || "todo",
dueDate: args.dueDate ? new Date(args.dueDate) : null,
labelId: args.labelId || null,
labelId: resolvedLabelId,
estimatedDuration: args.estimatedDuration || null,
parentTaskId: args.parentTaskId || null,
startDate: args.startDate ? new Date(args.startDate) : null,
@@ -541,10 +582,10 @@ CRITICAL: After executing tools, provide a concise summary of your actions.`;
action: "CREATE",
entityType: "TASK",
entityId: createdTask.id,
details: { title: createdTask.title },
details: { title: createdTask.title, labelId: resolvedLabelId },
source: "AI"
});
result = { success: true, taskId: createdTask.id, message: "Task created." };
result = { success: true, taskId: createdTask.id, message: `Task created${labelMessage}.` };
} else if (fnName === "searchTasks") {
const tasks = await this.storage.searchTasks(args.query, user.id);
const labels = await this.storage.getLabels(user.id);