feat: snip a region into chat, and make links clickable (#14)
This commit was merged in pull request #14.
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { runAgent } from "../agent";
|
||||
import { toStoredAttachment } from "../attachments";
|
||||
import { ipPlace } from "../geo";
|
||||
import { buildMessages, composeSystemPrompt } from "../history";
|
||||
import { OllamaError } from "../ollama";
|
||||
import type { ResolvedPreferences } from "../preferences";
|
||||
import { createSession, createTurn, saveSession, withTitleFromFirstTurn } from "../sessions";
|
||||
import { toolsFor } from "../tools";
|
||||
import type { Session } from "../types";
|
||||
import type { PendingAttachment, Session } from "../types";
|
||||
import { errorMessage, isAbortError } from "../utils";
|
||||
|
||||
/**
|
||||
@@ -41,7 +43,7 @@ export interface UseChat {
|
||||
/** Short activity line such as "Searching the web", shown while a turn is in flight. */
|
||||
status?: string;
|
||||
error?: string;
|
||||
ask: (prompt: string) => void;
|
||||
ask: (prompt: string, attachment?: PendingAttachment) => void;
|
||||
cancel: () => void;
|
||||
replaceSession: (session: Session) => void;
|
||||
newSession: () => void;
|
||||
@@ -119,16 +121,22 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
}, []);
|
||||
|
||||
const ask = useCallback(
|
||||
(rawPrompt: string) => {
|
||||
(rawPrompt: string, attachment?: PendingAttachment) => {
|
||||
const prompt = rawPrompt.trim();
|
||||
if (!prompt || liveRef.current) return;
|
||||
|
||||
const preferences = preferencesRef.current;
|
||||
const base = sessionRef.current;
|
||||
|
||||
// Only the path, mime type and dimensions are kept on the turn. The base64
|
||||
// payload is used for this one request and then dropped, so it never
|
||||
// reaches storage. The dimensions have to survive because the transcript
|
||||
// lays the preview out long after the payload is gone.
|
||||
const attachments = attachment ? [toStoredAttachment(attachment)] : [];
|
||||
|
||||
// The turn is inserted immediately with an empty response so the UI shows
|
||||
// the question while the answer streams in beneath it.
|
||||
const turn = createTurn(prompt, preferences.model);
|
||||
const turn = createTurn(prompt, preferences.model, Date.now(), attachments);
|
||||
const started = withTitleFromFirstTurn({
|
||||
...base,
|
||||
model: preferences.model,
|
||||
@@ -152,6 +160,7 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
composeSystemPrompt(preferences.systemPrompt, preferences.webEnabled),
|
||||
base.turns,
|
||||
prompt,
|
||||
attachment ? [attachment.base64] : undefined,
|
||||
);
|
||||
|
||||
void (async () => {
|
||||
@@ -166,7 +175,14 @@ export function useChat(options: UseChatOptions): UseChat {
|
||||
think: preferences.think,
|
||||
messages,
|
||||
tools: toolsFor({ webEnabled: preferences.webEnabled }),
|
||||
toolContext: { host: preferences.host, apiKey: preferences.apiKey },
|
||||
toolContext: {
|
||||
host: preferences.host,
|
||||
apiKey: preferences.apiKey,
|
||||
place: preferences.place,
|
||||
// Only wired up when the user switched it on, so an off setting
|
||||
// cannot reach the network for a location at all.
|
||||
ipPlace: preferences.ipGeolocation ? ipPlace : undefined,
|
||||
},
|
||||
signal: controller.signal,
|
||||
events: {
|
||||
onContentDelta: (delta) => {
|
||||
|
||||
Reference in New Issue
Block a user