chore: snapshot current state before cleanup
This commit is contained in:
parent
db4ce8b14f
commit
94e62c9456
14 changed files with 489 additions and 3929 deletions
|
|
@ -6,10 +6,11 @@ import { LogPanel } from "./components/LogPanel";
|
|||
import { useAudioMeter } from "./hooks/useAudioMeter";
|
||||
import { usePTT } from "./hooks/usePTT";
|
||||
import { useWebRTC } from "./hooks/useWebRTC";
|
||||
import type { CardItem, CardMessageMetadata, JsonValue } from "./types";
|
||||
import type { CardItem, CardMessageMetadata, CardSelectionRange, JsonValue } from "./types";
|
||||
|
||||
const SWIPE_THRESHOLD_PX = 64;
|
||||
const SWIPE_DIRECTION_RATIO = 1.15;
|
||||
const CARD_SELECTION_EVENT = "nanobot:card-selection-change";
|
||||
|
||||
interface AppRtcActions {
|
||||
connect(): Promise<void>;
|
||||
|
|
@ -25,6 +26,36 @@ interface AppRtcActions {
|
|||
connecting: boolean;
|
||||
}
|
||||
|
||||
function toNullableNumber(value: JsonValue | undefined): number | null {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
||||
}
|
||||
|
||||
function readCardSelection(cardId: string | null | undefined): CardSelectionRange | null {
|
||||
const raw = window.__nanobotGetCardSelection?.(cardId);
|
||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
||||
const record = raw as Record<string, JsonValue>;
|
||||
if (record.kind !== "git_diff_range") return null;
|
||||
if (typeof record.file_label !== "string" || typeof record.range_label !== "string") return null;
|
||||
|
||||
const filePath = typeof record.file_path === "string" ? record.file_path : record.file_label;
|
||||
const label =
|
||||
typeof record.label === "string"
|
||||
? record.label
|
||||
: `${record.file_label} · ${record.range_label}`;
|
||||
|
||||
return {
|
||||
kind: "git_diff_range",
|
||||
file_path: filePath,
|
||||
file_label: record.file_label,
|
||||
range_label: record.range_label,
|
||||
label,
|
||||
old_start: toNullableNumber(record.old_start),
|
||||
old_end: toNullableNumber(record.old_end),
|
||||
new_start: toNullableNumber(record.new_start),
|
||||
new_end: toNullableNumber(record.new_end),
|
||||
};
|
||||
}
|
||||
|
||||
function buildCardMetadata(card: CardItem): CardMessageMetadata {
|
||||
const metadata: CardMessageMetadata = {
|
||||
card_id: card.serverId,
|
||||
|
|
@ -35,6 +66,11 @@ function buildCardMetadata(card: CardItem): CardMessageMetadata {
|
|||
card_context_summary: card.contextSummary,
|
||||
card_response_value: card.responseValue,
|
||||
};
|
||||
const selection = card.serverId ? readCardSelection(card.serverId) : null;
|
||||
if (selection) {
|
||||
metadata.card_selection = selection as unknown as JsonValue;
|
||||
metadata.card_selection_label = selection.label;
|
||||
}
|
||||
const liveContent = card.serverId
|
||||
? window.__nanobotGetCardLiveContent?.(card.serverId)
|
||||
: undefined;
|
||||
|
|
@ -42,12 +78,27 @@ function buildCardMetadata(card: CardItem): CardMessageMetadata {
|
|||
return metadata;
|
||||
}
|
||||
|
||||
function AgentCardContext({ card, onClear }: { card: CardItem; onClear(): void }) {
|
||||
function AgentCardContext({
|
||||
card,
|
||||
selection,
|
||||
onClear,
|
||||
}: {
|
||||
card: CardItem;
|
||||
selection: CardSelectionRange | null;
|
||||
onClear(): void;
|
||||
}) {
|
||||
const label = selection ? "Using diff context" : "Using card";
|
||||
const title = selection?.file_label || card.title;
|
||||
const meta = selection?.range_label || "";
|
||||
|
||||
return (
|
||||
<div id="agent-card-context" data-no-swipe="1">
|
||||
<div class="agent-card-context-label">Using card</div>
|
||||
<div class="agent-card-context-label">{label}</div>
|
||||
<div class="agent-card-context-row">
|
||||
<div class="agent-card-context-title">{card.title}</div>
|
||||
<div class="agent-card-context-main">
|
||||
<div class="agent-card-context-title">{title}</div>
|
||||
{meta && <div class="agent-card-context-meta">{meta}</div>}
|
||||
</div>
|
||||
<button
|
||||
class="agent-card-context-clear"
|
||||
type="button"
|
||||
|
|
@ -146,6 +197,7 @@ export function App() {
|
|||
const [view, setView] = useState<"agent" | "feed">("agent");
|
||||
const [composing, setComposing] = useState(false);
|
||||
const [selectedCardId, setSelectedCardId] = useState<string | null>(null);
|
||||
const [selectionVersion, setSelectionVersion] = useState(0);
|
||||
const autoOpenedFeedRef = useRef(false);
|
||||
|
||||
const selectedCard = useMemo(
|
||||
|
|
@ -153,9 +205,13 @@ export function App() {
|
|||
selectedCardId ? (rtc.cards.find((card) => card.serverId === selectedCardId) ?? null) : null,
|
||||
[rtc.cards, selectedCardId],
|
||||
);
|
||||
const selectedCardSelection = useMemo(
|
||||
() => (selectedCardId ? readCardSelection(selectedCardId) : null),
|
||||
[selectedCardId, selectionVersion],
|
||||
);
|
||||
const selectedCardMetadata = useCallback(
|
||||
() => (selectedCard ? buildCardMetadata(selectedCard) : undefined),
|
||||
[selectedCard],
|
||||
[selectedCard, selectionVersion],
|
||||
);
|
||||
|
||||
const { agentStateOverride, handlePointerDown, handlePointerMove, handlePointerUp } = usePTT({
|
||||
|
|
@ -202,9 +258,29 @@ export function App() {
|
|||
setSelectedCardId(null);
|
||||
}, [rtc.cards, selectedCardId]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleSelectionChange = (event: Event) => {
|
||||
const detail = (event as CustomEvent<{ cardId?: string; selection?: JsonValue | null }>)
|
||||
.detail;
|
||||
setSelectionVersion((current) => current + 1);
|
||||
const cardId = typeof detail?.cardId === "string" ? detail.cardId : "";
|
||||
if (cardId && detail?.selection) setSelectedCardId(cardId);
|
||||
};
|
||||
|
||||
window.addEventListener(CARD_SELECTION_EVENT, handleSelectionChange as EventListener);
|
||||
return () => {
|
||||
window.removeEventListener(CARD_SELECTION_EVENT, handleSelectionChange as EventListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { handleToggleTextOnly } = useControlActions(rtc);
|
||||
const { handleAskCard } = useCardActions(setView, setSelectedCardId);
|
||||
|
||||
const clearSelectedCardContext = useCallback(() => {
|
||||
if (selectedCardId) window.__nanobotClearCardSelection?.(selectedCardId);
|
||||
setSelectedCardId(null);
|
||||
}, [selectedCardId]);
|
||||
|
||||
const handleCardChoice = useCallback(
|
||||
(cardId: string, value: string) => {
|
||||
rtc.sendJson({ type: "card-response", card_id: cardId, value });
|
||||
|
|
@ -220,10 +296,11 @@ export function App() {
|
|||
const handleResetWithSelection = useCallback(async () => {
|
||||
const confirmed = window.confirm("Clear the current conversation context and start fresh?");
|
||||
if (!confirmed) return;
|
||||
if (selectedCardId) window.__nanobotClearCardSelection?.(selectedCardId);
|
||||
setSelectedCardId(null);
|
||||
await rtc.connect();
|
||||
rtc.sendJson({ type: "command", command: "reset" });
|
||||
}, [rtc]);
|
||||
}, [rtc, selectedCardId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -238,7 +315,11 @@ export function App() {
|
|||
/>
|
||||
)}
|
||||
{view === "agent" && selectedCard && (
|
||||
<AgentCardContext card={selectedCard} onClear={() => setSelectedCardId(null)} />
|
||||
<AgentCardContext
|
||||
card={selectedCard}
|
||||
selection={selectedCardSelection}
|
||||
onClear={clearSelectedCardContext}
|
||||
/>
|
||||
)}
|
||||
{view === "agent" && (
|
||||
<LogPanel
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue