Skip to content

Commit cc96c99

Browse files
peoraycwandev
authored andcommitted
feat: add confirmation component
1 parent 7a6c7bf commit cc96c99

File tree

17 files changed

+887
-0
lines changed

17 files changed

+887
-0
lines changed

apps/www/content/3.components/1.chatbot/confirmation.md

Lines changed: 490 additions & 0 deletions
Large diffs are not rendered by default.

apps/www/plugins/ai-elements.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import {
77
Checkpoint,
88
CodeBlock,
99
CodeBlockDark,
10+
Confirmation,
11+
ConfirmationAccepted,
12+
ConfirmationRejected,
13+
ConfirmationRequest,
1014
Conversation,
1115
Image,
1216
InlineCitation,
@@ -74,4 +78,8 @@ export default defineNuxtPlugin((nuxtApp) => {
7478
vueApp.component('CodeBlockDark', CodeBlockDark)
7579
vueApp.component('Checkpoint', Checkpoint)
7680
vueApp.component('Workflow', Workflow)
81+
vueApp.component('Confirmation', Confirmation)
82+
vueApp.component('ConfirmationAccepted', ConfirmationAccepted)
83+
vueApp.component('ConfirmationRejected', ConfirmationRejected)
84+
vueApp.component('ConfirmationRequest', ConfirmationRequest)
7785
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script setup lang="ts">
2+
import type { ToolUIPart } from 'ai'
3+
import type { HTMLAttributes } from 'vue'
4+
import type { ToolUIPartApproval } from './context'
5+
import { Alert } from '@repo/shadcn-vue/components/ui/alert'
6+
import { cn } from '@repo/shadcn-vue/lib/utils'
7+
import { provide, toRef } from 'vue'
8+
import { ConfirmationKey } from './context'
9+
10+
const props = defineProps<{
11+
approval?: ToolUIPartApproval
12+
state: ToolUIPart['state']
13+
class?: HTMLAttributes['class']
14+
}>()
15+
16+
provide(ConfirmationKey, {
17+
approval: toRef(props, 'approval'),
18+
state: toRef(props, 'state'),
19+
})
20+
</script>
21+
22+
<template>
23+
<Alert
24+
v-if="approval && state !== 'input-streaming' && state !== 'input-available'"
25+
:class="cn('flex flex-col gap-2', props.class)"
26+
v-bind="$attrs"
27+
>
28+
<slot />
29+
</Alert>
30+
</template>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script setup lang="ts">
2+
import { useConfirmationContext } from './context'
3+
4+
const { approval, state } = useConfirmationContext()
5+
</script>
6+
7+
<template>
8+
<template
9+
v-if="
10+
approval?.approved
11+
&& (state === 'approval-responded'
12+
|| state === 'output-denied'
13+
|| state === 'output-available')
14+
"
15+
>
16+
<slot />
17+
</template>
18+
</template>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script setup lang="ts">
2+
import { Button } from '@repo/shadcn-vue/components/ui/button'
3+
</script>
4+
5+
<template>
6+
<Button class="h-8 px-3 text-sm" type="button" v-bind="$attrs">
7+
<slot />
8+
</Button>
9+
</template>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { cn } from '@repo/shadcn-vue/lib/utils'
4+
import { useConfirmationContext } from './context'
5+
6+
const props = defineProps<{
7+
class?: HTMLAttributes['class']
8+
}>()
9+
10+
const { state } = useConfirmationContext()
11+
</script>
12+
13+
<template>
14+
<div
15+
v-if="state === 'approval-requested'"
16+
:class="
17+
cn('flex items-center justify-end gap-2 self-end', props.class)
18+
"
19+
v-bind="$attrs"
20+
>
21+
<slot />
22+
</div>
23+
</template>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script setup lang="ts">
2+
import { useConfirmationContext } from './context'
3+
4+
const { approval, state } = useConfirmationContext()
5+
</script>
6+
7+
<template>
8+
<template
9+
v-if="
10+
approval?.approved === false
11+
&& (state === 'approval-responded'
12+
|| state === 'output-denied'
13+
|| state === 'output-available')
14+
"
15+
>
16+
<slot />
17+
</template>
18+
</template>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script setup lang="ts">
2+
import { useConfirmationContext } from './context'
3+
4+
const { state } = useConfirmationContext()
5+
</script>
6+
7+
<template>
8+
<template v-if="state === 'approval-requested'">
9+
<slot />
10+
</template>
11+
</template>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue'
3+
import { AlertDescription } from '@repo/shadcn-vue/components/ui/alert'
4+
import { cn } from '@repo/shadcn-vue/lib/utils'
5+
6+
const props = defineProps<{
7+
class?: HTMLAttributes['class']
8+
}>()
9+
</script>
10+
11+
<template>
12+
<AlertDescription
13+
:class="cn('inline', props.class)"
14+
v-bind="$attrs"
15+
>
16+
<slot />
17+
</AlertDescription>
18+
</template>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { ToolUIPart } from 'ai'
2+
import type { InjectionKey, Ref } from 'vue'
3+
import { inject } from 'vue'
4+
5+
export type ToolUIPartApproval
6+
= | {
7+
id: string
8+
approved?: never
9+
reason?: never
10+
}
11+
| {
12+
id: string
13+
approved: boolean
14+
reason?: string
15+
}
16+
| {
17+
id: string
18+
approved: true
19+
reason?: string
20+
}
21+
| {
22+
id: string
23+
approved: true
24+
reason?: string
25+
}
26+
| {
27+
id: string
28+
approved: false
29+
reason?: string
30+
}
31+
| undefined
32+
33+
export interface ConfirmationContextValue {
34+
approval: Ref<ToolUIPartApproval>
35+
state: Ref<ToolUIPart['state']>
36+
}
37+
38+
export const ConfirmationKey: InjectionKey<ConfirmationContextValue>
39+
= Symbol('ConfirmationContext')
40+
41+
export function useConfirmationContext() {
42+
const context = inject<ConfirmationContextValue | null>(ConfirmationKey, null)
43+
if (!context)
44+
throw new Error('Confirmation components must be used within <Confirmation>')
45+
return context
46+
}

0 commit comments

Comments
 (0)