83 lines
4.5 KiB
Vue
83 lines
4.5 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref } from "vue";
|
|
import { useWorkbenchStore } from "@/stores/workbench";
|
|
|
|
const workbench = useWorkbenchStore();
|
|
const draft = ref("");
|
|
const draftsOpen = ref(false);
|
|
const draftText = computed(() => draft.value.trim());
|
|
const primaryAction = computed<"turn" | "steer" | "cancel">(() => {
|
|
if (workbench.composer.submitMode === "steer") return draftText.value ? "steer" : "cancel";
|
|
return "turn";
|
|
});
|
|
const primaryLabel = computed(() => primaryAction.value === "cancel" ? "取消" : primaryAction.value === "steer" ? "Steer" : "发送");
|
|
const primaryModeLabel = computed(() => {
|
|
if (workbench.composer.disabledReason) return workbench.composer.disabledReason;
|
|
if (primaryAction.value === "cancel") return `运行中 ${workbench.composer.targetTraceId ?? "trace"}`;
|
|
if (primaryAction.value === "steer") return `steer ${workbench.composer.targetTraceId}`;
|
|
return "turn / 新请求";
|
|
});
|
|
const primaryDisabled = computed(() => {
|
|
if (primaryAction.value === "cancel") return workbench.composer.disabled || !workbench.composer.targetTraceId;
|
|
return workbench.composer.disabled || !draftText.value;
|
|
});
|
|
|
|
async function submit(): Promise<void> {
|
|
const value = draft.value;
|
|
if (primaryAction.value === "cancel") {
|
|
if (primaryDisabled.value) return;
|
|
await workbench.cancelRunningTrace();
|
|
return;
|
|
}
|
|
if (!value.trim() || workbench.composer.disabled) return;
|
|
draft.value = "";
|
|
const accepted = await workbench.submitMessage(value);
|
|
if (!accepted && !draft.value.trim()) draft.value = value;
|
|
}
|
|
|
|
async function submitFromInput(): Promise<void> {
|
|
if (!draftText.value) return;
|
|
await submit();
|
|
}
|
|
|
|
function pickDraft(text: string): void {
|
|
draft.value = text;
|
|
draftsOpen.value = false;
|
|
workbench.recordActivity("draft-pick");
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<form id="command-form" class="command-composer" :title="workbench.composer.disabledReason || (primaryAction === 'cancel' ? `取消运行中的 turn: ${workbench.composer.targetTraceId}` : primaryAction === 'steer' ? `Steer 运行中的 turn: ${workbench.composer.targetTraceId}` : '发送新 turn')" @submit.prevent="submit">
|
|
<div class="composer-toolbar">
|
|
<label class="composer-field" for="code-agent-provider-profile">
|
|
<span>模型通道</span>
|
|
<select id="code-agent-provider-profile" v-model="workbench.providerProfile" @change="workbench.setProviderProfile(($event.target as HTMLSelectElement).value)">
|
|
<option v-for="option in workbench.providerOptions" :key="option.value" :value="option.value">
|
|
{{ option.label }}{{ option.configured === false ? ' · 未配置' : '' }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<button id="command-drafts-toggle" class="btn btn-secondary btn-sm" type="button" :aria-expanded="draftsOpen" aria-controls="command-drafts-panel" @click="draftsOpen = !draftsOpen">最近输入</button>
|
|
<button id="command-clear" class="btn btn-secondary btn-sm" type="button" @click="draft = ''; workbench.clearSessionMessages()">清空</button>
|
|
</div>
|
|
<section v-if="draftsOpen && workbench.recentDrafts.length > 0" id="command-drafts-panel" class="drafts-list" data-drafts-list aria-label="最近发送过的草稿">
|
|
<header class="drafts-list-head">
|
|
<p class="page-eyebrow">最近输入</p>
|
|
<button class="btn btn-secondary btn-sm" type="button" @click="workbench.clearRecentDrafts">清空记录</button>
|
|
</header>
|
|
<div class="drafts-list-items" role="list">
|
|
<button v-for="item in workbench.recentDrafts" :key="`${item.ts}-${item.text}`" class="draft-chip" type="button" role="listitem" :title="`${item.ts.slice(0, 19)} · ${item.text}`" @click="pickDraft(workbench.pickDraft(item))">
|
|
{{ item.text.length > 40 ? `${item.text.slice(0, 40)}...` : item.text }}
|
|
</button>
|
|
</div>
|
|
</section>
|
|
<div class="composer-actions">
|
|
<span v-if="workbench.composer.disabledReason" class="composer-warning">{{ workbench.composer.disabledReason }}</span>
|
|
<span v-else class="composer-mode" :data-mode="primaryAction">{{ primaryModeLabel }}</span>
|
|
<button id="command-send" class="btn btn-primary" type="submit" :data-action="primaryAction" :disabled="primaryDisabled">{{ primaryLabel }}</button>
|
|
</div>
|
|
<textarea id="command-input" v-model="draft" rows="3" placeholder="输入任务,Enter 发送,Shift+Enter 换行" :disabled="false" @input="workbench.recordActivity('typing')" @keydown.enter.exact.prevent="submitFromInput" />
|
|
</form>
|
|
</template>
|