From 4b4cdd8cd880bb8f1cd38da3214ef04497a7cb53 Mon Sep 17 00:00:00 2001 From: Till JS Date: Tue, 24 Mar 2026 19:49:39 +0100 Subject: [PATCH] feat(todo): inline title editing on click Click on task title to edit it directly inline instead of opening the expanded form. Enter to save, Escape to cancel, blur to save. Title shows subtle hover background to indicate editability. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/components/TaskItem.svelte | 90 ++++++++++++++++--- 1 file changed, 78 insertions(+), 12 deletions(-) diff --git a/apps/todo/apps/web/src/lib/components/TaskItem.svelte b/apps/todo/apps/web/src/lib/components/TaskItem.svelte index e59353f6d..83966bc35 100644 --- a/apps/todo/apps/web/src/lib/components/TaskItem.svelte +++ b/apps/todo/apps/web/src/lib/components/TaskItem.svelte @@ -172,6 +172,34 @@ } } + // Inline title editing + let isEditingTitle = $state(false); + let editingTitle = $state(''); + let titleEditRef = $state(null); + + function handleTitleClick(e: MouseEvent) { + e.stopPropagation(); + isEditingTitle = true; + editingTitle = task.title; + setTimeout(() => titleEditRef?.focus(), 0); + } + + function handleTitleKeydown(e: KeyboardEvent) { + if (e.key === 'Enter') { + commitTitleEdit(); + } else if (e.key === 'Escape') { + isEditingTitle = false; + } + } + + function commitTitleEdit() { + isEditingTitle = false; + const trimmed = editingTitle.trim(); + if (trimmed && trimmed !== task.title) { + onSave?.({ title: trimmed }); + } + } + function handleContentClick() { if (onExpand) { onExpand(); @@ -342,11 +370,24 @@ style="background-color: {priorityColors[task.priority] || priorityColors.medium}" > - - + {#if task.metadata?.assignee || (task.metadata?.involvedContacts && task.metadata.involvedContacts.length > 0)} @@ -820,12 +861,6 @@ flex-direction: column; justify-content: center; gap: 0.25rem; - background: none; - border: none; - padding: 0; - text-align: left; - cursor: pointer; - align-self: stretch; } .task-title { @@ -835,6 +870,37 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + cursor: text; + border-radius: 0.25rem; + padding: 0.125rem 0.25rem; + margin: -0.125rem -0.25rem; + } + + .task-title:hover { + background: rgba(0, 0, 0, 0.04); + } + + :global(.dark) .task-title:hover { + background: rgba(255, 255, 255, 0.06); + } + + .task-title-input { + font-size: 0.875rem; + font-weight: 500; + color: #374151; + background: rgba(0, 0, 0, 0.04); + border: 1px solid #8b5cf6; + border-radius: 0.25rem; + padding: 0.125rem 0.25rem; + margin: -0.125rem -0.25rem; + outline: none; + width: 100%; + } + + :global(.dark) .task-title-input { + color: #f3f4f6; + background: rgba(255, 255, 255, 0.08); + border-color: #8b5cf6; } :global(.dark) .task-title {