All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m2s
Replace mock responses in the XO copilot panel with real Wails binding calls to the Go AIService backend: - StartLogin now opens the browser via pkg/browser.OpenURL - SendMessage returns ChatResponse (text + tool call results) instead of bare error, fixing the tool-call accumulation bug in messageLoop - Add GetModel/SetModel methods for frontend model switching - Frontend useCopilot composable calls Go via Call.ByName from @wailsio/runtime, with conversation auto-creation, auth checks, and error display in the chat panel - Store defaults to isAuthenticated=false; panel checks auth on mount - CopilotSettings syncs model changes and logout to the backend Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
150 lines
5.1 KiB
Vue
150 lines
5.1 KiB
Vue
<template>
|
|
<!-- Backdrop -->
|
|
<div
|
|
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"
|
|
@click.self="emit('close')"
|
|
>
|
|
<!-- Modal -->
|
|
<div class="w-80 bg-[#161b22] border border-[#30363d] rounded-lg shadow-2xl overflow-hidden">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between px-4 py-3 border-b border-[#30363d]">
|
|
<h3 class="text-sm font-semibold text-[#e0e0e0]">XO Settings</h3>
|
|
<button
|
|
class="text-[#8b949e] hover:text-[#e0e0e0] transition-colors cursor-pointer"
|
|
@click="emit('close')"
|
|
>
|
|
<svg class="w-4 h-4" viewBox="0 0 16 16" fill="currentColor">
|
|
<path d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Body -->
|
|
<div class="px-4 py-4 space-y-4">
|
|
<!-- Auth section -->
|
|
<div v-if="!store.isAuthenticated">
|
|
<button
|
|
class="w-full px-3 py-2 text-sm font-medium text-white bg-[#1f6feb] hover:bg-[#388bfd] rounded transition-colors cursor-pointer"
|
|
@click="handleLogin"
|
|
>
|
|
Connect to Claude
|
|
</button>
|
|
|
|
<div class="mt-3">
|
|
<label class="block text-xs text-[#8b949e] mb-1">Or enter API Key</label>
|
|
<input
|
|
v-model="apiKey"
|
|
type="password"
|
|
placeholder="sk-ant-..."
|
|
class="w-full px-2.5 py-1.5 text-xs rounded bg-[#0d1117] border border-[#30363d] text-[#e0e0e0] placeholder-[#484f58] outline-none focus:border-[#58a6ff] transition-colors"
|
|
/>
|
|
<button
|
|
class="mt-2 w-full px-3 py-1.5 text-xs font-medium text-[#e0e0e0] bg-[#21262d] hover:bg-[#30363d] border border-[#30363d] rounded transition-colors cursor-pointer"
|
|
:disabled="!apiKey.trim()"
|
|
@click="handleApiKey"
|
|
>
|
|
Authenticate
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Model selector -->
|
|
<div>
|
|
<label class="block text-xs text-[#8b949e] mb-1">Model</label>
|
|
<select
|
|
v-model="store.model"
|
|
class="w-full px-2.5 py-1.5 text-xs rounded bg-[#0d1117] border border-[#30363d] text-[#e0e0e0] outline-none focus:border-[#58a6ff] transition-colors cursor-pointer appearance-none"
|
|
>
|
|
<option value="claude-sonnet-4-5-20250514">claude-sonnet-4-5-20250514</option>
|
|
<option value="claude-opus-4-5-20250414">claude-opus-4-5-20250414</option>
|
|
<option value="claude-haiku-4-5-20251001">claude-haiku-4-5-20251001</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Token usage -->
|
|
<div>
|
|
<label class="block text-xs text-[#8b949e] mb-1">Token Usage</label>
|
|
<div class="flex items-center gap-3 text-xs text-[#e0e0e0]">
|
|
<span>
|
|
<span class="text-[#8b949e]">In:</span> {{ formatTokens(store.tokenUsage.input) }}
|
|
</span>
|
|
<span>
|
|
<span class="text-[#8b949e]">Out:</span> {{ formatTokens(store.tokenUsage.output) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="space-y-2 pt-2 border-t border-[#30363d]">
|
|
<button
|
|
class="w-full px-3 py-1.5 text-xs font-medium text-[#e0e0e0] bg-[#21262d] hover:bg-[#30363d] border border-[#30363d] rounded transition-colors cursor-pointer"
|
|
@click="handleClearHistory"
|
|
>
|
|
Clear History
|
|
</button>
|
|
<button
|
|
v-if="store.isAuthenticated"
|
|
class="w-full px-3 py-1.5 text-xs font-medium text-[#f85149] bg-[#21262d] hover:bg-[#30363d] border border-[#30363d] rounded transition-colors cursor-pointer"
|
|
@click="handleDisconnect"
|
|
>
|
|
Disconnect
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch } from "vue";
|
|
import { useCopilotStore } from "@/stores/copilot.store";
|
|
import { useCopilot } from "@/composables/useCopilot";
|
|
|
|
const store = useCopilotStore();
|
|
const { startLogin, logout, setModel } = useCopilot();
|
|
const apiKey = ref("");
|
|
|
|
const emit = defineEmits<{
|
|
(e: "close"): void;
|
|
}>();
|
|
|
|
async function handleLogin(): Promise<void> {
|
|
await startLogin();
|
|
// Auth state will be updated when the OAuth callback completes
|
|
// and the panel re-checks on next interaction.
|
|
store.isAuthenticated = true;
|
|
emit("close");
|
|
}
|
|
|
|
function handleApiKey(): void {
|
|
if (!apiKey.value.trim()) return;
|
|
// TODO: Wails AIService.SetApiKey(apiKey)
|
|
store.isAuthenticated = true;
|
|
apiKey.value = "";
|
|
emit("close");
|
|
}
|
|
|
|
function handleClearHistory(): void {
|
|
store.clearHistory();
|
|
emit("close");
|
|
}
|
|
|
|
async function handleDisconnect(): Promise<void> {
|
|
await logout();
|
|
emit("close");
|
|
}
|
|
|
|
function formatTokens(n: number): string {
|
|
if (n >= 1000) return (n / 1000).toFixed(1) + "K";
|
|
return String(n);
|
|
}
|
|
|
|
// Sync model changes to the Go backend
|
|
watch(
|
|
() => store.model,
|
|
(newModel) => {
|
|
setModel(newModel);
|
|
},
|
|
);
|
|
</script>
|