update agents skill design

This commit is contained in:
2026-08-20 18:18:10 +05:30
parent 161b237695
commit b2e6868824
75 changed files with 14586 additions and 98 deletions

View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import { ThumbsDown, ThumbsUp } from 'lucide-react';
import { cn } from '@/lib/utils';
import { ResponseDocument } from './ResponseBlocks';
import OwliverAvatar from '@/components/krow/OwliverAvatar';
@@ -35,9 +36,58 @@ export function ThinkingIndicator() {
* with tables and KPI tiles, and a chat bubble would waste a third of a 380px
* column on padding around them.
*/
/**
* How this conversation was rated.
*
* Offered on the latest answer only, because a rating belongs to the
* conversation rather than to a paragraph of it — thumbs on every turn would
* ask a reader to score an answer out of the exchange that produced it, and
* each one would set the same value anyway.
*
* Hidden until the turn is hovered or focused, the way History's delete control
* already is: a thread of reports should not carry two permanent buttons under
* every answer.
*/
function FeedbackControls({ feedback, onFeedback }) {
const rate = (rating) => onFeedback(feedback?.rating === rating ? null : rating);
return (
<div
className={cn(
'mt-2 flex items-center gap-0.5 transition-opacity',
/* Stays visible once rated, so a reader can see and change their own
answer rather than having to hunt for it again. */
feedback ? 'opacity-100' : 'opacity-0 group-hover/turn:opacity-100 group-focus-within/turn:opacity-100'
)}
>
{[
{ rating: 'up', Icon: ThumbsUp, label: 'This answer was useful' },
{ rating: 'down', Icon: ThumbsDown, label: 'This answer was not useful' },
].map(({ rating, Icon, label }) => (
<button
key={rating}
type="button"
onClick={() => rate(rating)}
aria-label={label}
aria-pressed={feedback?.rating === rating}
className={cn(
`rounded p-1 transition-colors focus-visible:outline-none
focus-visible:ring-2 focus-visible:ring-krow-blue/50`,
feedback?.rating === rating
? 'text-krow-blue'
: 'text-ink-4 hover:text-ink-2'
)}
>
<Icon className="h-3.5 w-3.5" aria-hidden="true" />
</button>
))}
</div>
);
}
export const Message = React.memo(
/** @param {any} props */
({ role, text, blocks, streaming, stopped, onPrompt }) => {
({ role, text, blocks, streaming, stopped, onPrompt, feedback = null, onFeedback = null }) => {
if (role === 'user') {
return (
<div className="flex justify-end">
@@ -49,7 +99,7 @@ export const Message = React.memo(
}
return (
<div className="animate-slide-up">
<div className="group/turn animate-slide-up">
<div className="mb-2 flex items-center gap-1.5">
<OwliverAvatar className="h-5 w-5" rounded="rounded-full" />
<span className="text-[10px] font-semibold uppercase tracking-wide text-ink-4">Owliver</span>
@@ -58,6 +108,12 @@ export const Message = React.memo(
<ResponseDocument blocks={blocks} streaming={streaming} onPrompt={onPrompt} />
{stopped && <p className="mt-2 text-caption italic text-ink-4">Stopped early.</p>}
{/* Only where a handler was passed — a turn nobody is collecting feedback
for renders exactly as it did before this existed. */}
{onFeedback && !streaming && (
<FeedbackControls feedback={feedback} onFeedback={onFeedback} />
)}
</div>
);
});