feat: attach desktop file paths as editable learner chips

This commit is contained in:
amosblomqvist
2026-10-28 18:54:57 +02:00
parent 84f93999c5
commit c4e934c9dc
30 changed files with 2331 additions and 59 deletions
+16 -16
View File
@@ -5,6 +5,7 @@ import { ArrowDownIcon, ChevronRightIcon, PlayIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { finishDictation, isDictationActive, joinDictation, useDictationTarget } from "@/components/dictation";
import { useEnterSubmits } from "@/components/enter-submits";
import { PathInput } from "@/components/path-input";
import { ToolContentResponse } from "@/components/ai-elements/message";
import { cn } from "@/lib/utils";
import {
@@ -439,8 +440,8 @@ function QuestionDockForm({
expanded — collapsed, the bar's native Enter expands it. */
const [kbdIdx, setKbdIdx] = useState<number | null>(null);
const rootRef = useRef<HTMLDivElement>(null);
const otherInputRef = useRef<HTMLInputElement>(null);
const thinkingRef = useRef<HTMLTextAreaElement>(null);
const otherInputRef = useRef<HTMLDivElement>(null);
const thinkingRef = useRef<HTMLDivElement>(null);
// One row past the options is the trailing row: quiz → don't-know,
// ask → Other (a question never has both). Text mode (ask/explain)
// has no rows — the composer IS the answer surface.
@@ -473,7 +474,7 @@ function QuestionDockForm({
const focusRow = (idx: number) => {
const row = rootRef.current?.querySelector(`[data-kbdrow="${idx}"]`) as HTMLElement | null;
if (!row) return;
const el = (row.querySelector("input") as HTMLElement | null) ?? row;
const el = (row.querySelector("input, [data-file-path-input]") as HTMLElement | null) ?? row;
el.focus();
};
@@ -696,11 +697,12 @@ function QuestionDockForm({
</span>
)}
</span>
<input
<PathInput
ref={otherInputRef}
singleLine
value={other}
onChange={(e) => {
setOther(e.target.value);
onValueChange={(value) => {
setOther(value);
// Typing selects the row — displacing in
// single-select, alongside the siblings in multi.
setSel((prev) => (prev.otherSelected ? prev : toggleOther(selectMode, prev)));
@@ -851,11 +853,11 @@ function QuestionDockForm({
{qd.mode === "text" ? "Your answer" : "YOUR THINKING"}
</div>
{qd.mode === "text" ? (
<textarea
<PathInput
value={text}
onChange={(e) => {
answerTextRef.current = e.target.value;
setText(e.target.value);
onValueChange={(value) => {
answerTextRef.current = value;
setText(value);
}}
onKeyDown={(e) => {
// Esc ends dictation (the dictate decisions: click-again
@@ -875,16 +877,15 @@ function QuestionDockForm({
}
}}
placeholder="Type your answer…"
rows={1}
className="field-sizing-content lh-input max-h-48 min-h-10 w-full resize-none bg-transparent px-2.5 py-2 outline-none placeholder:text-muted-foreground"
/>
) : (
<textarea
<PathInput
ref={thinkingRef}
value={thinking}
onChange={(e) => {
thinkingTextRef.current = e.target.value;
setThinking(e.target.value);
onValueChange={(value) => {
thinkingTextRef.current = value;
setThinking(value);
}}
onKeyDown={(e) => {
// Esc ends dictation (the dictate decisions: click-again
@@ -905,7 +906,6 @@ function QuestionDockForm({
}
}}
placeholder="Why this answer? Whatever comes to mind."
rows={1}
className={cn(
"field-sizing-content lh-input max-h-48 w-full resize-none bg-transparent px-2.5 py-2 outline-none placeholder:text-muted-foreground/70",
// Phase-scaled prominence: teaching invites depth,