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
+7 -6
View File
@@ -14,6 +14,7 @@ import {
ConversationScrollButton,
} from "@/components/ai-elements/conversation";
import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";
import { PathText } from "@/components/file-path-chip";
import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";
import { Shimmer } from "@/components/ai-elements/shimmer";
import { BrailleSpinner } from "@/components/braille-spinner";
@@ -687,10 +688,10 @@ export function Chat({
const cycleChatFields = useCallback((): readonly HTMLElement[] => {
const pick = (sel: string) => Array.from(document.querySelectorAll<HTMLElement>(sel));
return [
...pick('[data-testid="reading-thinking-field"] textarea'),
...pick('[data-testid="question-dock"] textarea'),
...pick('[data-testid="practice-dock"] textarea'),
...pick('[data-testid="composer"] textarea'),
...pick('[data-testid="reading-thinking-field"] [data-file-path-input]'),
...pick('[data-testid="question-dock"] [data-file-path-input]'),
...pick('[data-testid="practice-dock"] [data-file-path-input]'),
...pick('[data-testid="composer"] [data-file-path-input]'),
];
}, []);
useFieldCycle(cycleChatFields);
@@ -705,7 +706,7 @@ export function Chat({
* composer: with a question pending the Composer is unmounted, the ref
* is null, and the thinking field is left alone by design. */
const dictationWasActiveRef = useRef(false);
const composerInputRef = useRef<HTMLTextAreaElement>(null);
const composerInputRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (dictation.active) {
dictationWasActiveRef.current = true;
@@ -1507,7 +1508,7 @@ export function Chat({
rail as the thinking rows — one quote geometry. */}
<div className="font-medium uppercase tracking-wide text-muted-foreground">You</div>
<div className="lh-prose whitespace-pre-wrap text-foreground">
{m.parts.filter((p) => p.type === "text").map((p) => p.text).join("\n")}
<PathText text={m.parts.filter((p) => p.type === "text").map((p) => p.text).join("\n")} />
</div>
</div>
) : (