feat: Add components to show steps in the copilot thinking process (#11530)
This PR adds the components for new Copilot UI - Added a Header component - Added a thinking block. - Update the outline on copilot input --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup>
|
||||||
|
import CopilotHeader from './CopilotHeader.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Story
|
||||||
|
title="Captain/Copilot/CopilotHeader"
|
||||||
|
:layout="{ type: 'grid', width: '800px' }"
|
||||||
|
>
|
||||||
|
<!-- Default State -->
|
||||||
|
<Variant title="Default State">
|
||||||
|
<CopilotHeader />
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<!-- With New Conversation Button -->
|
||||||
|
<Variant title="With New Conversation Button">
|
||||||
|
<!-- eslint-disable-next-line vue/prefer-true-attribute-shorthand -->
|
||||||
|
<CopilotHeader :has-messages="true" />
|
||||||
|
</Variant>
|
||||||
|
</Story>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<script setup>
|
||||||
|
import Button from '../button/Button.vue';
|
||||||
|
defineProps({
|
||||||
|
hasMessages: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defineEmits(['reset', 'close']);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between px-5 py-2 border-b border-n-weak h-12"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between gap-2 flex-1">
|
||||||
|
<span class="font-medium text-sm text-n-slate-12">
|
||||||
|
{{ $t('CAPTAIN.COPILOT.TITLE') }}
|
||||||
|
</span>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Button
|
||||||
|
v-if="hasMessages"
|
||||||
|
icon="i-lucide-plus"
|
||||||
|
ghost
|
||||||
|
sm
|
||||||
|
@click="$emit('reset')"
|
||||||
|
/>
|
||||||
|
<Button icon="i-lucide-x" ghost sm @click="$emit('close')" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -13,19 +13,16 @@ const sendMessage = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form
|
<form class="relative" @submit.prevent="sendMessage">
|
||||||
class="border border-n-weak bg-n-alpha-3 rounded-lg h-12 flex"
|
|
||||||
@submit.prevent="sendMessage"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
v-model="message"
|
v-model="message"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('CAPTAIN.COPILOT.SEND_MESSAGE')"
|
:placeholder="$t('CAPTAIN.COPILOT.SEND_MESSAGE')"
|
||||||
class="w-full reset-base bg-transparent px-4 py-3 text-n-slate-11 text-sm"
|
class="w-full reset-base bg-n-alpha-3 ltr:pl-4 ltr:pr-12 rtl:pl-12 rtl:pr-4 py-3 text-n-slate-11 text-sm border border-n-weak rounded-lg focus:outline-none focus:ring-1 focus:ring-n-blue-11 focus:border-n-blue-11"
|
||||||
@keyup.enter="sendMessage"
|
@keyup.enter="sendMessage"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="h-auto w-12 flex items-center justify-center text-n-slate-11"
|
class="absolute ltr:right-1 rtl:left-1 top-1/2 -translate-y-1/2 h-9 w-10 flex items-center justify-center text-n-slate-11 hover:text-n-blue-11"
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
<i class="i-ph-arrow-up" />
|
<i class="i-ph-arrow-up" />
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import Icon from '../../components-next/icon/Icon.vue';
|
||||||
|
defineProps({
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="flex flex-col gap-2 p-3 rounded-lg bg-n-background/50 border border-n-weak hover:bg-n-background/80 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<div class="flex items-start gap-2">
|
||||||
|
<Icon
|
||||||
|
icon="i-lucide-sparkles"
|
||||||
|
class="w-4 h-4 mt-0.5 flex-shrink-0 text-n-slate-9"
|
||||||
|
/>
|
||||||
|
<div class="text-sm text-n-slate-11">
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<script setup>
|
||||||
|
import CopilotThinkingGroup from './CopilotThinkingGroup.vue';
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
content: 'Analyzing the user query',
|
||||||
|
reasoning: 'Breaking down the request into actionable steps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
content: 'Searching codebase',
|
||||||
|
reasoning: 'Looking for relevant files and functions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
content: 'Generating response',
|
||||||
|
reasoning: 'Composing a helpful and accurate answer',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Story title="Captain/Copilot/CopilotThinkingGroup" group="components">
|
||||||
|
<Variant title="Default">
|
||||||
|
<CopilotThinkingGroup :messages="messages" />
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<Variant title="With Default Collapsed">
|
||||||
|
<!-- eslint-disable-next-line -->
|
||||||
|
<CopilotThinkingGroup :messages="messages" :default-collapsed="true" />
|
||||||
|
</Variant>
|
||||||
|
</Story>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, watch, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import Icon from '../icon/Icon.vue';
|
||||||
|
import CopilotThinkingBlock from './CopilotThinkingBlock.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
messages: { type: Array, required: true },
|
||||||
|
defaultCollapsed: { type: Boolean, default: false },
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const isExpanded = ref(!props.defaultCollapsed);
|
||||||
|
|
||||||
|
const thinkingCount = computed(() => props.messages.length);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.defaultCollapsed,
|
||||||
|
newValue => {
|
||||||
|
if (newValue) {
|
||||||
|
isExpanded.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<button
|
||||||
|
class="group flex items-center gap-2 text-xs text-n-slate-10 hover:text-n-slate-11 transition-colors duration-200 -ml-3"
|
||||||
|
@click="isExpanded = !isExpanded"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:icon="isExpanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
||||||
|
class="w-4 h-4 transition-transform duration-200 group-hover:scale-110"
|
||||||
|
/>
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
{{ t('CAPTAIN.COPILOT.SHOW_STEPS') }}
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center justify-center h-4 min-w-4 px-1 text-xs font-medium rounded-full bg-n-solid-3 text-n-slate-11"
|
||||||
|
>
|
||||||
|
{{ thinkingCount }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-show="isExpanded"
|
||||||
|
class="space-y-3 transition-all duration-200"
|
||||||
|
:class="{
|
||||||
|
'opacity-100': isExpanded,
|
||||||
|
'opacity-0 max-h-0 overflow-hidden': !isExpanded,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<CopilotThinkingBlock
|
||||||
|
v-for="message in messages"
|
||||||
|
:key="message.id"
|
||||||
|
:content="message.content"
|
||||||
|
:reasoning="message.reasoning"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -327,12 +327,14 @@
|
|||||||
"NAME": "Captain",
|
"NAME": "Captain",
|
||||||
"HEADER_KNOW_MORE": "Know more",
|
"HEADER_KNOW_MORE": "Know more",
|
||||||
"COPILOT": {
|
"COPILOT": {
|
||||||
|
"TITLE": "Copilot",
|
||||||
"SEND_MESSAGE": "Send message...",
|
"SEND_MESSAGE": "Send message...",
|
||||||
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
|
"EMPTY_MESSAGE": "There was an error generating the response. Please try again.",
|
||||||
"LOADER": "Captain is thinking",
|
"LOADER": "Captain is thinking",
|
||||||
"YOU": "You",
|
"YOU": "You",
|
||||||
"USE": "Use this",
|
"USE": "Use this",
|
||||||
"RESET": "Reset",
|
"RESET": "Reset",
|
||||||
|
"SHOW_STEPS": "Show steps",
|
||||||
"SELECT_ASSISTANT": "Select Assistant",
|
"SELECT_ASSISTANT": "Select Assistant",
|
||||||
"PROMPTS": {
|
"PROMPTS": {
|
||||||
"SUMMARIZE": {
|
"SUMMARIZE": {
|
||||||
|
|||||||
Reference in New Issue
Block a user