diff --git a/src/components/QuickEntry.tsx b/src/components/QuickEntry.tsx index 78756d5..7d5892e 100644 --- a/src/components/QuickEntry.tsx +++ b/src/components/QuickEntry.tsx @@ -1,12 +1,15 @@ import { type Component, createSignal, createEffect, onCleanup, onMount, For } from "solid-js"; import { Search, Command, Clock, ArrowUpCircle, Plus } from "lucide-solid"; -import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, upsertTagDefinition } from "@/store"; +import { addTask, calculateDateFromUrgency, calculateUrgencyFromDate, store, upsertMultipleTagDefinitions } from "@/store"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { TextField, TextFieldInput } from "@/components/ui/textfield"; import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select"; import { TagPicker } from "@/components/TagPicker"; +import { lazy, Suspense } from "solid-js"; + +const TaskEditor = lazy(() => import("./TaskEditor").then(m => ({ default: m.TaskEditor }))); export const QuickEntry: Component = () => { // Diff tracking for reactive tags @@ -17,6 +20,8 @@ export const QuickEntry: Component = () => { const [priority, setPriority] = createSignal("5"); const [urgency, setUrgency] = createSignal("5"); const [tags, setTags] = createSignal([]); + const [description, setDescription] = createSignal(""); + const [editorInstance, setEditorInstance] = createSignal(null); // Reactive shorthand parsing createEffect(() => { @@ -49,10 +54,6 @@ export const QuickEntry: Component = () => { const tagName = match[1].trim(); if (tagName) { newParsedTags.push(tagName); - if (match[2]) { - const val = Math.min(10, Math.max(0, parseInt(match[2]))); - upsertTagDefinition(tagName, val); - } } } @@ -124,51 +125,40 @@ export const QuickEntry: Component = () => { }; const parseAndAdd = async () => { - const text = input(); + let text = input(); if (!text || !priority() || !urgency()) return; let finalPriority = parseInt(priority()); let finalUrgency = parseInt(urgency()); const finalTags = [...tags()]; - let title = text; - // 1. Handle Priority / Urgency (Simple regexes as they don't have spaces) - const pMatch = text.match(/\/p(\d+)/); + // 1. Handle Priority / Urgency and clean them from text + const pMatch = text.match(/\/p\s*(\d+)/); if (pMatch) { finalPriority = Math.min(10, Math.max(1, parseInt(pMatch[1]))); - title = title.replace(pMatch[0], ""); + text = text.replace(pMatch[0], ""); } - const uMatchNumeric = text.match(/\/u(\d+)/); - if (uMatchNumeric) { - finalUrgency = Math.min(10, Math.max(1, parseInt(uMatchNumeric[1]))); - title = title.replace(uMatchNumeric[0], ""); + const uMatch = text.match(/\/u\s*(\d+)/); + if (uMatch) { + finalUrgency = Math.min(10, Math.max(1, parseInt(uMatch[1]))); + text = text.replace(uMatch[0], ""); } // 2. Handle Tags: /t [name]:[value] - // We look for /t, then name, then optional :value. - // We split by /t to find all segments. const segments = text.split(/\/t\s*/); - // segments[0] is the part before first /t - title = segments[0]; + let title = segments[0]; + + const tagsToUpsert: Record = {}; for (let i = 1; i < segments.length; i++) { const seg = segments[i]; - // Name ends at first : or / (but / is handled by split) or end of string const firstColon = seg.indexOf(':'); let tagName = ""; let tagValue: number | undefined; let remainingText = ""; if (firstColon === -1) { - // No colon in this segment. Segment might be "Tag Name Title" or "Tag Name /t" - // But the user said / or : are endpoints. - // So if no colon, the WHOLE segment is the tag name? - // Wait, if I type "/t Tag Title", the user said / and : are endpoints. - // So if neither is there, the tag continues to end of string? - // Actually, let's treat the FIRST SPACE after some non-space text as a fallback? - // No, user specifically said: "It should allow spaces in this particular parse... use / or : as end points." - // So if I type "/t Tag Name", it IS the tag name. tagName = seg.trim(); } else { tagName = seg.slice(0, firstColon).trim(); @@ -179,7 +169,6 @@ export const QuickEntry: Component = () => { tagValue = Math.min(10, Math.max(0, parseInt(valueMatch[1]))); remainingText = afterColon.slice(valueMatch[0].length); } else { - // Colon was just a separator for title remainingText = afterColon; } } @@ -189,12 +178,18 @@ export const QuickEntry: Component = () => { finalTags.push(tagName); } if (tagValue !== undefined) { - upsertTagDefinition(tagName, tagValue); + tagsToUpsert[tagName] = tagValue; + } else if (store.tagDefinitions?.[tagName] === undefined) { + tagsToUpsert[tagName] = 5; } } title += " " + remainingText; } + if (Object.keys(tagsToUpsert).length > 0) { + upsertMultipleTagDefinitions(tagsToUpsert); + } + let finalDueDate: Date | null = null; if (dateString()) { finalDueDate = new Date(dateString()); @@ -207,16 +202,21 @@ export const QuickEntry: Component = () => { addTask(title.replace(/\s+/g, " ").trim(), { priority: finalPriority, dueDate: finalDueDate || undefined, - tags: finalTags + tags: finalTags, + content: description() }); setInput(""); setPriority("5"); setUrgency("5"); setTags([]); + setDescription(""); lastParsedTags = []; setDateString(""); setIsOpen(false); + // Reset editor content + const instance = editorInstance(); + if (instance) instance.commands.setContent(""); }; return ( @@ -335,6 +335,17 @@ export const QuickEntry: Component = () => { +
+ }> + + +
+
diff --git a/src/components/TagPicker.tsx b/src/components/TagPicker.tsx index bf5e1d6..36a0634 100644 --- a/src/components/TagPicker.tsx +++ b/src/components/TagPicker.tsx @@ -75,7 +75,7 @@ export const TagPicker: Component = (props) => {
upsertTagDefinition(tagName, parseInt(e.currentTarget.value))} class="flex h-8 w-16 items-center justify-between rounded-md border border-input bg-background px-2 py-1 text-xs shadow-sm ring-offset-background focus:outline-none focus:ring-1 focus:ring-ring appearance-none" >