feat: snip a region into chat, and make links clickable (#14)

This commit was merged in pull request #14.
This commit is contained in:
2026-10-05 08:38:50 +00:00
parent f5d1ced4d6
commit 576c5946f7
44 changed files with 3890 additions and 120 deletions
+44
View File
@@ -2,6 +2,8 @@ import { Action, ActionPanel, Icon, List, openExtensionPreferences } from "@vici
import { useCallback, useEffect, useMemo, useState } from "react";
import { describeResumeTimeout, getLamPreferences, needsSetup } from "./lib/preferences";
import { listModels } from "./lib/ollama";
import { forgetGeoPlace } from "./lib/geo";
import { forgetVisionSupport, visionSupport, type VisionSupport } from "./lib/vision";
import type { ModelInfo } from "./lib/types";
import { errorMessage } from "./lib/utils";
@@ -36,6 +38,7 @@ export default function SetupCommand() {
const [error, setError] = useState<string | undefined>(undefined);
const [isLoading, setIsLoading] = useState(false);
const [hasTested, setHasTested] = useState(false);
const [vision, setVision] = useState<VisionSupport>("unknown");
const missingKey = needsSetup(preferences);
@@ -53,6 +56,23 @@ export default function SetupCommand() {
setHasTested(true);
setIsLoading(false);
}
// A model can gain or lose image support without its name changing (a
// re-pull, a retagged build), so re-testing drops the cached answer that the
// chat view gates the snip action on rather than leaving a stale "no".
await forgetVisionSupport();
// Same reasoning for the IP-derived place: a re-test is the moment to stop
// trusting an answer that was resolved hours ago, possibly in another city.
await forgetGeoPlace();
setVision(
await visionSupport({
host: preferences.host,
apiKey: preferences.apiKey,
model: preferences.model,
refresh: true,
}),
);
}, [preferences]);
// Verifying on open means the user sees something real immediately rather
@@ -149,6 +169,30 @@ export default function SetupCommand() {
subtitle={describeResumeTimeout(preferences.resumeTimeoutMs)}
actions={settingsActions}
/>
<List.Item
icon={vision === "yes" ? Icon.Image : Icon.QuestionMarkCircle}
title="Image Input"
subtitle={
vision === "yes"
? `Supported by ${preferences.model}. Snip commands are available.`
: vision === "no"
? `${preferences.model} cannot see images. Pick a vision model to snip.`
: "Not checked yet"
}
actions={settingsActions}
/>
<List.Item
icon={preferences.place || preferences.ipGeolocation ? Icon.Person : Icon.QuestionMarkCircle}
title="Location"
subtitle={
preferences.place
? `${preferences.place} (from preferences)`
: preferences.ipGeolocation
? "Rough area, resolved from the IP address via ipwho.is"
: "Region only, from the timezone and locale"
}
actions={settingsActions}
/>
</List.Section>
{error ? (