@copilotkit/vue 2.0.0-rc.1 → 2.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARITY.md +13 -12
- package/README.md +1 -2
- package/dist/components/copilot-provider/CopilotKit.vue.d.ts +3 -1
- package/dist/components/copilot-provider/CopilotKit.vue.d.ts.map +1 -1
- package/dist/index.cjs +43 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +10170 -7506
- package/dist/index.mjs.map +1 -1
- package/dist/v2/components/A2UIMessageRenderer.d.ts +7 -0
- package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -1
- package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +11 -2
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -1
- package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts +136 -0
- package/dist/v2/components/a2ui/A2UIRecoveryStates.d.ts.map +1 -0
- package/dist/v2/components/chat/BasicMarkdown.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChat.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +3 -1
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts +16 -0
- package/dist/v2/components/chat/CopilotChatAssistantToolbarButton.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts +22 -0
- package/dist/v2/components/chat/CopilotChatFeedbackCommentPopover.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +69 -16
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts +12 -0
- package/dist/v2/components/chat/CopilotChatInspectorButton.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts +22 -0
- package/dist/v2/components/chat/CopilotChatLightbox.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +20 -0
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatSuggestionPill.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatSuggestionView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToggleButton.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts +32 -0
- package/dist/v2/components/chat/CopilotChatToolCallGroup.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToolCallItem.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +11 -1
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts +5 -0
- package/dist/v2/components/chat/CopilotChatWorkSummary.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotModalHeaderCloseButton.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotModalHeaderTitle.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopup.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +1 -0
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotPopupWelcomeScreen.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +2 -0
- package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +2 -0
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +2 -0
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotSidebarWelcomeScreen.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/StreamingMarkdownDefault.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/index.d.ts +17 -0
- package/dist/v2/components/chat/index.d.ts.map +1 -1
- package/dist/v2/components/chat/inspector-shortcuts.d.ts +12 -0
- package/dist/v2/components/chat/inspector-shortcuts.d.ts.map +1 -0
- package/dist/v2/components/chat/message-feedback.d.ts +89 -0
- package/dist/v2/components/chat/message-feedback.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-capture.d.ts +39 -0
- package/dist/v2/components/chat/tool-call-capture.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-display-context.d.ts +39 -0
- package/dist/v2/components/chat/tool-call-display-context.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-group-parts.d.ts +144 -0
- package/dist/v2/components/chat/tool-call-group-parts.d.ts.map +1 -0
- package/dist/v2/components/chat/tool-call-layout.d.ts +115 -0
- package/dist/v2/components/chat/tool-call-layout.d.ts.map +1 -0
- package/dist/v2/components/chat/turn-work.d.ts +35 -0
- package/dist/v2/components/chat/turn-work.d.ts.map +1 -0
- package/dist/v2/components/chat/types.d.ts +104 -4
- package/dist/v2/components/chat/types.d.ts.map +1 -1
- package/dist/v2/components/chat/use-lightbox.d.ts +18 -0
- package/dist/v2/components/chat/use-lightbox.d.ts.map +1 -0
- package/dist/v2/components/icons/index.d.ts +1 -1
- package/dist/v2/components/icons/index.d.ts.map +1 -1
- package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts +55 -0
- package/dist/v2/components/ui/CopilotDropdownMenuContent.vue.d.ts.map +1 -0
- package/dist/v2/components/ui/CopilotPopover.vue.d.ts +40 -0
- package/dist/v2/components/ui/CopilotPopover.vue.d.ts.map +1 -0
- package/dist/v2/components/ui/CopilotTooltip.vue.d.ts +37 -0
- package/dist/v2/components/ui/CopilotTooltip.vue.d.ts.map +1 -0
- package/dist/v2/components/ui/dropdown-menu.d.ts +14 -0
- package/dist/v2/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/v2/components/ui/floating.d.ts +84 -0
- package/dist/v2/components/ui/floating.d.ts.map +1 -0
- package/dist/v2/components/ui/presence.d.ts +9 -0
- package/dist/v2/components/ui/presence.d.ts.map +1 -0
- package/dist/v2/components/ui/use-id.d.ts +9 -0
- package/dist/v2/components/ui/use-id.d.ts.map +1 -0
- package/dist/v2/hooks/use-default-render-tool.d.ts +6 -0
- package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -1
- package/dist/v2/hooks/use-render-tool.d.ts +4 -0
- package/dist/v2/hooks/use-render-tool.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.d.cts +3 -0
- package/dist/v2/index.d.mts +3 -0
- package/dist/v2/index.d.ts +3 -0
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/index.mjs +165 -162
- package/dist/v2/lib/message-feedback.d.ts +33 -0
- package/dist/v2/lib/message-feedback.d.ts.map +1 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +56 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts +5 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/index.d.ts +1 -1
- package/dist/v2/providers/index.d.ts.map +1 -1
- package/dist/v2/providers/keys.d.ts +8 -0
- package/dist/v2/providers/keys.d.ts.map +1 -1
- package/dist/v2/providers/types.d.ts +9 -0
- package/dist/v2/providers/types.d.ts.map +1 -1
- package/dist/v2/styles/CopilotKitStyles.d.ts +18 -0
- package/dist/v2/styles/CopilotKitStyles.d.ts.map +1 -0
- package/dist/v2/types/defineToolCallRenderer.d.ts +2 -0
- package/dist/v2/types/defineToolCallRenderer.d.ts.map +1 -1
- package/dist/v2/types/vue-tool-call-renderer.d.ts +7 -0
- package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -1
- package/package.json +11 -23
- package/src/components/copilot-provider/CopilotKit.vue +4 -1
- package/src/v2/__tests__/exports.test.ts +4 -2
- package/src/v2/components/A2UIMessageRenderer.ts +55 -67
- package/src/v2/components/A2UISurfaceActivityRenderer.vue +11 -23
- package/src/v2/components/__tests__/A2UIBasicCatalogColors.test.ts +8 -9
- package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +63 -36
- package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +14 -257
- package/src/v2/components/a2ui/A2UIRecoveryStates.ts +444 -0
- package/src/v2/components/a2ui/__tests__/A2UIRecoveryStates.test.ts +175 -0
- package/src/v2/components/chat/BasicMarkdown.vue +8 -6
- package/src/v2/components/chat/CopilotChat.vue +7 -0
- package/src/v2/components/chat/CopilotChatAssistantMessage.vue +267 -67
- package/src/v2/components/chat/CopilotChatAssistantToolbarButton.vue +46 -0
- package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +141 -168
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +67 -18
- package/src/v2/components/chat/CopilotChatAudioRecorder.vue +2 -2
- package/src/v2/components/chat/CopilotChatFeedbackCommentPopover.vue +240 -0
- package/src/v2/components/chat/CopilotChatInput.vue +210 -161
- package/src/v2/components/chat/CopilotChatInspectorButton.vue +331 -0
- package/src/v2/components/chat/CopilotChatLightbox.vue +105 -0
- package/src/v2/components/chat/CopilotChatMessageView.vue +261 -18
- package/src/v2/components/chat/CopilotChatReasoningMessage.vue +21 -22
- package/src/v2/components/chat/CopilotChatSuggestionPill.vue +5 -14
- package/src/v2/components/chat/CopilotChatSuggestionView.vue +1 -4
- package/src/v2/components/chat/CopilotChatToggleButton.vue +3 -9
- package/src/v2/components/chat/CopilotChatToggleButtonCloseIcon.ts +1 -1
- package/src/v2/components/chat/CopilotChatToggleButtonOpenIcon.ts +1 -1
- package/src/v2/components/chat/CopilotChatToolCallGroup.vue +330 -0
- package/src/v2/components/chat/CopilotChatToolCallItem.vue +21 -11
- package/src/v2/components/chat/CopilotChatToolCallsView.vue +192 -21
- package/src/v2/components/chat/CopilotChatUserMessage.vue +105 -42
- package/src/v2/components/chat/CopilotChatView.vue +33 -38
- package/src/v2/components/chat/CopilotChatWorkSummary.vue +85 -0
- package/src/v2/components/chat/CopilotModalHeader.vue +10 -8
- package/src/v2/components/chat/CopilotModalHeaderCloseButton.ts +7 -6
- package/src/v2/components/chat/CopilotModalHeaderTitle.ts +1 -4
- package/src/v2/components/chat/CopilotPopup.vue +3 -0
- package/src/v2/components/chat/CopilotPopupView.vue +1 -0
- package/src/v2/components/chat/CopilotPopupViewInternal.vue +9 -19
- package/src/v2/components/chat/CopilotPopupWelcomeScreen.vue +5 -8
- package/src/v2/components/chat/CopilotSidebar.vue +5 -0
- package/src/v2/components/chat/CopilotSidebarView.vue +2 -0
- package/src/v2/components/chat/CopilotSidebarViewInternal.vue +24 -17
- package/src/v2/components/chat/CopilotSidebarWelcomeScreen.vue +7 -10
- package/src/v2/components/chat/StreamingMarkdownDefault.vue +3 -5
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.inspector.test.ts +269 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.learningFeedback.test.ts +760 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.ts +3 -5
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.ts +2 -2
- package/src/v2/components/chat/__tests__/CopilotChatInput.addMenu.test.ts +328 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.ts +6 -3
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +22 -13
- package/src/v2/components/chat/__tests__/CopilotChatLightbox.test.ts +160 -0
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.toolCallDisplay.test.ts +735 -0
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionPill.test.ts +1 -1
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.ts +3 -1
- package/src/v2/components/chat/__tests__/CopilotChatToolCallsView.learning.test.ts +75 -0
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.attachments.test.ts +143 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.ts +5 -4
- package/src/v2/components/chat/__tests__/CopilotChatView.rootWidth.test.ts +7 -33
- package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.ts +1 -1
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +3 -3
- package/src/v2/components/chat/__tests__/CopilotSidebarView.position.test.ts +126 -0
- package/src/v2/components/chat/__tests__/messageFeedbackDefaults.test.ts +33 -0
- package/src/v2/components/chat/__tests__/modalChatDisplayProps.test.ts +44 -0
- package/src/v2/components/chat/index.ts +2 -0
- package/src/v2/components/chat/inspector-shortcuts.ts +34 -0
- package/src/v2/components/chat/message-feedback.ts +291 -0
- package/src/v2/components/chat/tool-call-capture.ts +55 -0
- package/src/v2/components/chat/tool-call-display-context.ts +49 -0
- package/src/v2/components/chat/tool-call-group-parts.ts +292 -0
- package/src/v2/components/chat/tool-call-layout.ts +304 -0
- package/src/v2/components/chat/turn-work.ts +61 -0
- package/src/v2/components/chat/types.ts +113 -3
- package/src/v2/components/chat/use-lightbox.ts +64 -0
- package/src/v2/components/icons/index.ts +4 -0
- package/src/v2/components/ui/CopilotDropdownMenuContent.vue +465 -0
- package/src/v2/components/ui/CopilotPopover.vue +138 -0
- package/src/v2/components/ui/CopilotTooltip.vue +237 -0
- package/src/v2/components/ui/__tests__/CopilotTooltip.test.ts +168 -0
- package/src/v2/components/ui/dropdown-menu.ts +15 -0
- package/src/v2/components/ui/floating.ts +321 -0
- package/src/v2/components/ui/presence.ts +54 -0
- package/src/v2/components/ui/use-id.ts +16 -0
- package/src/v2/hooks/use-default-render-tool.ts +123 -62
- package/src/v2/hooks/use-render-tool.ts +8 -0
- package/src/v2/index.ts +3 -0
- package/src/v2/lib/__tests__/message-feedback.test.ts +152 -0
- package/src/v2/lib/message-feedback.ts +120 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.vue +29 -2
- package/src/v2/providers/CopilotKitProvider.types.ts +57 -0
- package/src/v2/providers/CopilotKitProvider.vue +125 -3
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.openThread.test.ts +69 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.learning.test.ts +534 -0
- package/src/v2/providers/index.ts +4 -1
- package/src/v2/providers/keys.ts +12 -0
- package/src/v2/providers/types.ts +11 -0
- package/src/v2/styles/CopilotKitStyles.ts +95 -0
- package/src/v2/styles/__tests__/CopilotKitStyles.client.test.ts +149 -0
- package/src/v2/styles/__tests__/CopilotKitStyles.ssr.test.ts +71 -0
- package/src/v2/types/defineToolCallRenderer.ts +4 -0
- package/src/v2/types/vue-tool-call-renderer.ts +7 -0
- package/vite.config.ts +2 -1
- package/dist/styles.css +0 -2
- package/scripts/scope-preflight.mjs +0 -100
- package/src/styles/globals.css +0 -233
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { defineComponent, h, onBeforeUnmount, ref, watch } from "vue";
|
|
2
|
+
import type { PropType, VNodeChild } from "vue";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared pre-paint lifecycle states for the A2UI surface renderer, ported from
|
|
7
|
+
* React's `A2UIRecoveryStates`.
|
|
8
|
+
*
|
|
9
|
+
* The A2UI middleware drives the WHOLE generative-UI lifecycle on a single
|
|
10
|
+
* `a2ui-surface` activity (one stable messageId, `replace: true`), so the states
|
|
11
|
+
* below swap IN PLACE and the painted surface ultimately replaces them:
|
|
12
|
+
*
|
|
13
|
+
* building → animated skeleton + "Building interface" (+ optional ~N tokens)
|
|
14
|
+
* retrying → same skeleton; once perceptible, the sub-label becomes
|
|
15
|
+
* "Retrying generation… (N/M attempts)" (threshold-gated so a fast
|
|
16
|
+
* transient retry never flashes), with debug detail per debugExposure
|
|
17
|
+
* failed → a clean hard-failure card (replacing the skeleton), developer
|
|
18
|
+
* detail tucked into an expander gated by debugExposure
|
|
19
|
+
* painted → the surface renderer owns the UI (these states render nothing)
|
|
20
|
+
*
|
|
21
|
+
* Timing (`showAfterMs` / `showAfterAttempts`) and how much debug detail to
|
|
22
|
+
* surface (`debugExposure`) are client concerns; the server can override
|
|
23
|
+
* `debugExposure` by stamping it onto the activity content (it wins over the
|
|
24
|
+
* client option).
|
|
25
|
+
*/
|
|
26
|
+
export type A2UIRecoveryRendererOptions = {
|
|
27
|
+
/** Delay (ms) before the "Retrying…" sub-label becomes visible. Default 2000. */
|
|
28
|
+
showAfterMs?: number;
|
|
29
|
+
/** Show the "Retrying…" sub-label immediately once `attempt` reaches this. Default 2. */
|
|
30
|
+
showAfterAttempts?: number;
|
|
31
|
+
/** How much retry/debug detail to surface. Default "collapsed". */
|
|
32
|
+
debugExposure?: "hidden" | "collapsed" | "verbose";
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type DebugExposure = "hidden" | "collapsed" | "verbose";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The pre-paint lifecycle fields the middleware stamps onto the `a2ui-surface`
|
|
39
|
+
* activity content (alongside `a2ui_operations` on paint).
|
|
40
|
+
*/
|
|
41
|
+
export const A2UILifecycleFields = {
|
|
42
|
+
status: z.enum(["building", "retrying", "failed"]).optional(),
|
|
43
|
+
attempt: z.number().optional(),
|
|
44
|
+
maxAttempts: z.number().optional(),
|
|
45
|
+
progressTokens: z.number().optional(),
|
|
46
|
+
error: z.string().optional(),
|
|
47
|
+
errors: z.array(z.any()).optional(),
|
|
48
|
+
attempts: z.array(z.any()).optional(),
|
|
49
|
+
// Server-side knob (stamped by the A2UI middleware).
|
|
50
|
+
debugExposure: z.enum(["hidden", "collapsed", "verbose"]).optional(),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Server-stamped debugExposure wins; else the client option. */
|
|
54
|
+
export function resolveDebugExposure(
|
|
55
|
+
content: any,
|
|
56
|
+
optionDebugExposure: DebugExposure,
|
|
57
|
+
): DebugExposure {
|
|
58
|
+
return content?.debugExposure ?? optionDebugExposure;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const tokensOf = (content: any): number | undefined =>
|
|
62
|
+
typeof content?.progressTokens === "number"
|
|
63
|
+
? content.progressTokens
|
|
64
|
+
: undefined;
|
|
65
|
+
|
|
66
|
+
// --- Skeleton + primitives ---------------------------------------------------
|
|
67
|
+
|
|
68
|
+
const dot = () =>
|
|
69
|
+
h("div", {
|
|
70
|
+
style: {
|
|
71
|
+
width: "7px",
|
|
72
|
+
height: "7px",
|
|
73
|
+
borderRadius: "50%",
|
|
74
|
+
backgroundColor: "#d4d4d8",
|
|
75
|
+
flexShrink: 0,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const spacer = () => h("div", { style: { width: "12px" } });
|
|
80
|
+
|
|
81
|
+
const bar = (
|
|
82
|
+
w: number,
|
|
83
|
+
ht: number,
|
|
84
|
+
bg: string,
|
|
85
|
+
anim?: number,
|
|
86
|
+
opacity?: number,
|
|
87
|
+
transition?: string,
|
|
88
|
+
) =>
|
|
89
|
+
h("div", {
|
|
90
|
+
style: {
|
|
91
|
+
width: `${w}px`,
|
|
92
|
+
height: `${ht}px`,
|
|
93
|
+
borderRadius: "9999px",
|
|
94
|
+
backgroundColor: bg,
|
|
95
|
+
...(anim !== undefined
|
|
96
|
+
? {
|
|
97
|
+
animation: `cpk-a2ui-fade 2.4s ease-in-out ${anim}s infinite`,
|
|
98
|
+
}
|
|
99
|
+
: {}),
|
|
100
|
+
...(opacity !== undefined ? { opacity } : {}),
|
|
101
|
+
...(transition ? { transition } : {}),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const row = (show: boolean, delay: number, children: VNodeChild[]) =>
|
|
106
|
+
h(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
style: {
|
|
110
|
+
display: "flex",
|
|
111
|
+
alignItems: "center",
|
|
112
|
+
gap: "6px",
|
|
113
|
+
opacity: show ? 1 : 0,
|
|
114
|
+
transition: `opacity 0.4s ${delay}s`,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
children,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Animated wireframe skeleton with a label, an optional live token count, and
|
|
122
|
+
* optional debug detail below it. The `tokens` count drives a progressive
|
|
123
|
+
* reveal of skeleton rows.
|
|
124
|
+
*/
|
|
125
|
+
export function renderA2UIGeneratingSkeleton(
|
|
126
|
+
label: string,
|
|
127
|
+
tokens?: number,
|
|
128
|
+
children?: VNodeChild[],
|
|
129
|
+
): VNodeChild {
|
|
130
|
+
// No count yet → show the fuller skeleton rather than an empty box.
|
|
131
|
+
const phase =
|
|
132
|
+
tokens == null
|
|
133
|
+
? 3
|
|
134
|
+
: tokens < 50
|
|
135
|
+
? 0
|
|
136
|
+
: tokens < 200
|
|
137
|
+
? 1
|
|
138
|
+
: tokens < 400
|
|
139
|
+
? 2
|
|
140
|
+
: 3;
|
|
141
|
+
|
|
142
|
+
return h("div", { style: { margin: "12px 0", maxWidth: "320px" } }, [
|
|
143
|
+
h(
|
|
144
|
+
"div",
|
|
145
|
+
{
|
|
146
|
+
style: {
|
|
147
|
+
position: "relative",
|
|
148
|
+
overflow: "hidden",
|
|
149
|
+
borderRadius: "12px",
|
|
150
|
+
border: "1px solid rgba(228,228,231,0.8)",
|
|
151
|
+
backgroundColor: "#fff",
|
|
152
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
153
|
+
padding: "16px 18px 14px",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
[
|
|
157
|
+
// Top bar
|
|
158
|
+
h(
|
|
159
|
+
"div",
|
|
160
|
+
{
|
|
161
|
+
style: {
|
|
162
|
+
display: "flex",
|
|
163
|
+
alignItems: "center",
|
|
164
|
+
gap: "8px",
|
|
165
|
+
marginBottom: "12px",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
[
|
|
169
|
+
h("div", { style: { display: "flex", gap: "4px" } }, [
|
|
170
|
+
dot(),
|
|
171
|
+
dot(),
|
|
172
|
+
dot(),
|
|
173
|
+
]),
|
|
174
|
+
bar(
|
|
175
|
+
64,
|
|
176
|
+
6,
|
|
177
|
+
"#e4e4e7",
|
|
178
|
+
undefined,
|
|
179
|
+
phase >= 1 ? 1 : 0.4,
|
|
180
|
+
"opacity 0.5s",
|
|
181
|
+
),
|
|
182
|
+
],
|
|
183
|
+
),
|
|
184
|
+
// Skeleton lines
|
|
185
|
+
h("div", { style: { display: "grid", gap: "7px" } }, [
|
|
186
|
+
row(phase >= 0, 0, [
|
|
187
|
+
bar(36, 7, "rgba(147,197,253,0.7)", 0),
|
|
188
|
+
bar(80, 7, "rgba(219,234,254,0.8)", 0.2),
|
|
189
|
+
]),
|
|
190
|
+
row(phase >= 0, 0.1, [
|
|
191
|
+
spacer(),
|
|
192
|
+
dot(),
|
|
193
|
+
bar(100, 7, "rgba(24,24,27,0.2)", 0.3),
|
|
194
|
+
]),
|
|
195
|
+
row(phase >= 1, 0.15, [
|
|
196
|
+
spacer(),
|
|
197
|
+
bar(48, 7, "rgba(24,24,27,0.15)", 0.1),
|
|
198
|
+
bar(40, 7, "rgba(153,246,228,0.6)", 0.5),
|
|
199
|
+
bar(56, 7, "rgba(147,197,253,0.6)", 0.3),
|
|
200
|
+
]),
|
|
201
|
+
row(phase >= 1, 0.2, [
|
|
202
|
+
spacer(),
|
|
203
|
+
dot(),
|
|
204
|
+
bar(60, 7, "rgba(24,24,27,0.15)", 0.4),
|
|
205
|
+
]),
|
|
206
|
+
row(phase >= 2, 0.25, [
|
|
207
|
+
bar(40, 7, "rgba(153,246,228,0.5)", 0.2),
|
|
208
|
+
dot(),
|
|
209
|
+
bar(48, 7, "rgba(24,24,27,0.15)", 0.6),
|
|
210
|
+
bar(64, 7, "rgba(147,197,253,0.5)", 0.1),
|
|
211
|
+
]),
|
|
212
|
+
row(phase >= 2, 0.3, [
|
|
213
|
+
bar(36, 7, "rgba(147,197,253,0.6)", 0.5),
|
|
214
|
+
bar(36, 7, "rgba(24,24,27,0.12)", 0.7),
|
|
215
|
+
]),
|
|
216
|
+
row(phase >= 3, 0.35, [
|
|
217
|
+
dot(),
|
|
218
|
+
bar(44, 7, "rgba(24,24,27,0.18)", 0.3),
|
|
219
|
+
dot(),
|
|
220
|
+
bar(56, 7, "rgba(153,246,228,0.5)", 0.8),
|
|
221
|
+
bar(48, 7, "rgba(147,197,253,0.5)", 0.4),
|
|
222
|
+
]),
|
|
223
|
+
]),
|
|
224
|
+
// Shimmer
|
|
225
|
+
h("div", {
|
|
226
|
+
style: {
|
|
227
|
+
pointerEvents: "none",
|
|
228
|
+
position: "absolute",
|
|
229
|
+
inset: "0",
|
|
230
|
+
background:
|
|
231
|
+
"linear-gradient(105deg, transparent 0%, transparent 40%, rgba(255,255,255,0.6) 50%, transparent 60%, transparent 100%)",
|
|
232
|
+
backgroundSize: "250% 100%",
|
|
233
|
+
animation: "cpk-a2ui-sweep 3s ease-in-out infinite",
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
],
|
|
237
|
+
),
|
|
238
|
+
// Label
|
|
239
|
+
h(
|
|
240
|
+
"div",
|
|
241
|
+
{
|
|
242
|
+
style: {
|
|
243
|
+
display: "flex",
|
|
244
|
+
alignItems: "center",
|
|
245
|
+
justifyContent: "center",
|
|
246
|
+
gap: "8px",
|
|
247
|
+
marginTop: "8px",
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
[
|
|
251
|
+
h(
|
|
252
|
+
"span",
|
|
253
|
+
{
|
|
254
|
+
style: {
|
|
255
|
+
fontSize: "12px",
|
|
256
|
+
color: "#a1a1aa",
|
|
257
|
+
letterSpacing: "0.025em",
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
label,
|
|
261
|
+
),
|
|
262
|
+
...(typeof tokens === "number" && tokens > 0
|
|
263
|
+
? [
|
|
264
|
+
h(
|
|
265
|
+
"span",
|
|
266
|
+
{
|
|
267
|
+
style: {
|
|
268
|
+
fontSize: "11px",
|
|
269
|
+
color: "#d4d4d8",
|
|
270
|
+
fontVariantNumeric: "tabular-nums",
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
`~${tokens.toLocaleString()} tokens`,
|
|
274
|
+
),
|
|
275
|
+
]
|
|
276
|
+
: []),
|
|
277
|
+
],
|
|
278
|
+
),
|
|
279
|
+
...(children ?? []),
|
|
280
|
+
// Keyframe styles
|
|
281
|
+
h(
|
|
282
|
+
"style",
|
|
283
|
+
`
|
|
284
|
+
@keyframes cpk-a2ui-fade {
|
|
285
|
+
0%, 100% { opacity: 1; }
|
|
286
|
+
50% { opacity: 0.5; }
|
|
287
|
+
}
|
|
288
|
+
@keyframes cpk-a2ui-sweep {
|
|
289
|
+
0% { background-position: 250% 0; }
|
|
290
|
+
100% { background-position: -250% 0; }
|
|
291
|
+
}
|
|
292
|
+
`,
|
|
293
|
+
),
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function renderDebugDetails(
|
|
298
|
+
label: string,
|
|
299
|
+
open: boolean,
|
|
300
|
+
payload: unknown,
|
|
301
|
+
): VNodeChild {
|
|
302
|
+
return h("details", { open, class: "cpk-a2ui-debug-details" }, [
|
|
303
|
+
h("summary", { class: "cpk-a2ui-debug-details-summary" }, label),
|
|
304
|
+
h(
|
|
305
|
+
"pre",
|
|
306
|
+
{ class: "cpk-a2ui-debug-details-payload", style: { fontSize: "11px" } },
|
|
307
|
+
JSON.stringify(payload, null, 2),
|
|
308
|
+
),
|
|
309
|
+
]);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// --- Lifecycle states ---------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
/** building: the generic skeleton + optional live token count. */
|
|
315
|
+
export const A2UIBuildingState = defineComponent({
|
|
316
|
+
name: "A2UIBuildingState",
|
|
317
|
+
props: {
|
|
318
|
+
content: { type: null as unknown as PropType<any>, default: undefined },
|
|
319
|
+
},
|
|
320
|
+
setup(props) {
|
|
321
|
+
return () =>
|
|
322
|
+
renderA2UIGeneratingSkeleton(
|
|
323
|
+
"Building interface",
|
|
324
|
+
tokensOf(props.content),
|
|
325
|
+
);
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* retrying: stays the generic skeleton through fast/transient retries; only
|
|
331
|
+
* once the retry is perceptible (after `showAfterMs`, or once `attempt`
|
|
332
|
+
* crosses `showAfterAttempts`) does the sub-label reveal
|
|
333
|
+
* "Retrying generation… (N/M)".
|
|
334
|
+
*/
|
|
335
|
+
export const A2UIRetryingState = defineComponent({
|
|
336
|
+
name: "A2UIRetryingState",
|
|
337
|
+
props: {
|
|
338
|
+
content: { type: null as unknown as PropType<any>, default: undefined },
|
|
339
|
+
showAfterMs: { type: Number, required: true },
|
|
340
|
+
showAfterAttempts: { type: Number, required: true },
|
|
341
|
+
debugExposure: {
|
|
342
|
+
type: String as PropType<DebugExposure>,
|
|
343
|
+
required: true,
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
setup(props) {
|
|
347
|
+
const attempt = () =>
|
|
348
|
+
typeof props.content?.attempt === "number"
|
|
349
|
+
? (props.content.attempt as number)
|
|
350
|
+
: undefined;
|
|
351
|
+
const immediate = () => {
|
|
352
|
+
const current = attempt();
|
|
353
|
+
return current !== undefined && current >= props.showAfterAttempts;
|
|
354
|
+
};
|
|
355
|
+
const revealed = ref(immediate());
|
|
356
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
357
|
+
|
|
358
|
+
watch(
|
|
359
|
+
[immediate, () => props.showAfterMs],
|
|
360
|
+
([isImmediate, showAfterMs]) => {
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
if (isImmediate) {
|
|
363
|
+
revealed.value = true;
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
timer = setTimeout(() => {
|
|
367
|
+
revealed.value = true;
|
|
368
|
+
}, showAfterMs);
|
|
369
|
+
},
|
|
370
|
+
{ immediate: true },
|
|
371
|
+
);
|
|
372
|
+
onBeforeUnmount(() => clearTimeout(timer));
|
|
373
|
+
|
|
374
|
+
return () => {
|
|
375
|
+
const tokens = tokensOf(props.content);
|
|
376
|
+
// Not yet perceptible → indistinguishable from normal building.
|
|
377
|
+
if (!revealed.value) {
|
|
378
|
+
return renderA2UIGeneratingSkeleton("Building interface", tokens);
|
|
379
|
+
}
|
|
380
|
+
const current = attempt();
|
|
381
|
+
const maxAttempts =
|
|
382
|
+
typeof props.content?.maxAttempts === "number"
|
|
383
|
+
? props.content.maxAttempts
|
|
384
|
+
: undefined;
|
|
385
|
+
const label =
|
|
386
|
+
current !== undefined && maxAttempts !== undefined
|
|
387
|
+
? `Retrying generation… (${current}/${maxAttempts} attempts)`
|
|
388
|
+
: "Retrying generation…";
|
|
389
|
+
const errors = Array.isArray(props.content?.errors)
|
|
390
|
+
? props.content.errors
|
|
391
|
+
: [];
|
|
392
|
+
return renderA2UIGeneratingSkeleton(
|
|
393
|
+
label,
|
|
394
|
+
tokens,
|
|
395
|
+
props.debugExposure !== "hidden" && errors.length > 0
|
|
396
|
+
? [
|
|
397
|
+
renderDebugDetails(
|
|
398
|
+
"validation issues",
|
|
399
|
+
props.debugExposure === "verbose",
|
|
400
|
+
{ attempt: props.content?.attempt, errors },
|
|
401
|
+
),
|
|
402
|
+
]
|
|
403
|
+
: [],
|
|
404
|
+
);
|
|
405
|
+
};
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
/** failed: a clean hard-failure card that replaces the skeleton in place. */
|
|
410
|
+
export const A2UIRecoveryFailure = defineComponent({
|
|
411
|
+
name: "A2UIRecoveryFailure",
|
|
412
|
+
props: {
|
|
413
|
+
content: { type: null as unknown as PropType<any>, default: undefined },
|
|
414
|
+
debugExposure: {
|
|
415
|
+
type: String as PropType<DebugExposure>,
|
|
416
|
+
required: true,
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
setup(props) {
|
|
420
|
+
return () =>
|
|
421
|
+
h("div", { class: "cpk-a2ui-recovery-failure" }, [
|
|
422
|
+
h(
|
|
423
|
+
"div",
|
|
424
|
+
{ class: "cpk-a2ui-recovery-failure-title" },
|
|
425
|
+
"Couldn't generate the UI",
|
|
426
|
+
),
|
|
427
|
+
h(
|
|
428
|
+
"div",
|
|
429
|
+
{ class: "cpk-a2ui-recovery-failure-description" },
|
|
430
|
+
"Something went wrong rendering this. You can keep chatting and try again.",
|
|
431
|
+
),
|
|
432
|
+
props.debugExposure !== "hidden"
|
|
433
|
+
? renderDebugDetails(
|
|
434
|
+
"developer details",
|
|
435
|
+
props.debugExposure === "verbose",
|
|
436
|
+
{
|
|
437
|
+
error: props.content?.error,
|
|
438
|
+
attempts: props.content?.attempts,
|
|
439
|
+
},
|
|
440
|
+
)
|
|
441
|
+
: null,
|
|
442
|
+
]);
|
|
443
|
+
},
|
|
444
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { h, nextTick } from "vue";
|
|
3
|
+
import type { Component } from "vue";
|
|
4
|
+
import { render as renderVue } from "@testing-library/vue";
|
|
5
|
+
import {
|
|
6
|
+
A2UIBuildingState,
|
|
7
|
+
A2UIRetryingState,
|
|
8
|
+
A2UIRecoveryFailure,
|
|
9
|
+
resolveDebugExposure,
|
|
10
|
+
} from "../A2UIRecoveryStates";
|
|
11
|
+
|
|
12
|
+
// Ported from React's A2UIRecoveryStates tests; renders each Vue component
|
|
13
|
+
// with the same props the React tests pass.
|
|
14
|
+
const render = (component: Component, props: Record<string, unknown>) =>
|
|
15
|
+
renderVue({ render: () => h(component, props) });
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("A2UI lifecycle states (OSS-162)", () => {
|
|
22
|
+
describe("building", () => {
|
|
23
|
+
it("shows the 'Building interface' skeleton", () => {
|
|
24
|
+
const { container } = render(A2UIBuildingState, {
|
|
25
|
+
content: { status: "building" },
|
|
26
|
+
});
|
|
27
|
+
expect(container.textContent).toContain("Building interface");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("shows a live token count when progressTokens is present", () => {
|
|
31
|
+
const { container } = render(A2UIBuildingState, {
|
|
32
|
+
content: { status: "building", progressTokens: 97 },
|
|
33
|
+
});
|
|
34
|
+
expect(container.textContent).toContain("Building interface");
|
|
35
|
+
expect(container.textContent).toContain("97 tokens");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("omits the token count when progressTokens is absent or zero", () => {
|
|
39
|
+
const { container } = render(A2UIBuildingState, {
|
|
40
|
+
content: { status: "building", progressTokens: 0 },
|
|
41
|
+
});
|
|
42
|
+
expect(container.textContent).not.toContain("tokens");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("retrying (threshold-gated label)", () => {
|
|
47
|
+
it("stays the generic building skeleton before the delay on a fast first retry", () => {
|
|
48
|
+
vi.useFakeTimers();
|
|
49
|
+
const { container } = render(A2UIRetryingState, {
|
|
50
|
+
content: { status: "retrying", attempt: 1, maxAttempts: 3 },
|
|
51
|
+
showAfterMs: 2000,
|
|
52
|
+
showAfterAttempts: 2,
|
|
53
|
+
debugExposure: "collapsed",
|
|
54
|
+
});
|
|
55
|
+
expect(container.textContent).toContain("Building interface");
|
|
56
|
+
expect(container.textContent).not.toContain("Retrying");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("reveals 'Retrying generation… (N/M attempts)' once the delay elapses", async () => {
|
|
60
|
+
vi.useFakeTimers();
|
|
61
|
+
const { container } = render(A2UIRetryingState, {
|
|
62
|
+
content: { status: "retrying", attempt: 1, maxAttempts: 3 },
|
|
63
|
+
showAfterMs: 2000,
|
|
64
|
+
showAfterAttempts: 2,
|
|
65
|
+
debugExposure: "collapsed",
|
|
66
|
+
});
|
|
67
|
+
vi.advanceTimersByTime(2100);
|
|
68
|
+
await nextTick();
|
|
69
|
+
expect(container.textContent).toContain("Retrying generation");
|
|
70
|
+
expect(container.textContent).toContain("(1/3 attempts)");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("reveals the retry label immediately once attempts cross the threshold", () => {
|
|
74
|
+
vi.useFakeTimers();
|
|
75
|
+
const { container } = render(A2UIRetryingState, {
|
|
76
|
+
content: { status: "retrying", attempt: 2, maxAttempts: 3 },
|
|
77
|
+
showAfterMs: 999999,
|
|
78
|
+
showAfterAttempts: 2,
|
|
79
|
+
debugExposure: "collapsed",
|
|
80
|
+
});
|
|
81
|
+
expect(container.textContent).toContain("Retrying generation");
|
|
82
|
+
expect(container.textContent).toContain("(2/3 attempts)");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("shows validation-issue detail once revealed, unless debugExposure is 'hidden'", () => {
|
|
86
|
+
const shown = render(A2UIRetryingState, {
|
|
87
|
+
content: {
|
|
88
|
+
status: "retrying",
|
|
89
|
+
attempt: 2,
|
|
90
|
+
maxAttempts: 3,
|
|
91
|
+
errors: [{ code: "missing_required_prop" }],
|
|
92
|
+
},
|
|
93
|
+
showAfterMs: 0,
|
|
94
|
+
showAfterAttempts: 1,
|
|
95
|
+
debugExposure: "collapsed",
|
|
96
|
+
});
|
|
97
|
+
expect(shown.container.querySelector("details")).not.toBeNull();
|
|
98
|
+
expect(shown.container.textContent).toContain("missing_required_prop");
|
|
99
|
+
|
|
100
|
+
const hidden = render(A2UIRetryingState, {
|
|
101
|
+
content: {
|
|
102
|
+
status: "retrying",
|
|
103
|
+
attempt: 2,
|
|
104
|
+
maxAttempts: 3,
|
|
105
|
+
errors: [{ code: "missing_required_prop" }],
|
|
106
|
+
},
|
|
107
|
+
showAfterMs: 0,
|
|
108
|
+
showAfterAttempts: 1,
|
|
109
|
+
debugExposure: "hidden",
|
|
110
|
+
});
|
|
111
|
+
expect(hidden.container.querySelector("details")).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("failed (replaces the skeleton in place)", () => {
|
|
116
|
+
it("renders a clean message with expandable developer detail (collapsed default)", () => {
|
|
117
|
+
const { container } = render(A2UIRecoveryFailure, {
|
|
118
|
+
content: {
|
|
119
|
+
status: "failed",
|
|
120
|
+
error: "Failed to generate valid A2UI after 3 attempt(s)",
|
|
121
|
+
attempts: [
|
|
122
|
+
{
|
|
123
|
+
attempt: 1,
|
|
124
|
+
ok: false,
|
|
125
|
+
errors: [{ code: "missing_required_prop" }],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
debugExposure: "collapsed",
|
|
130
|
+
});
|
|
131
|
+
expect(container.textContent).toContain("Couldn't generate");
|
|
132
|
+
const details = container.querySelector("details");
|
|
133
|
+
expect(details).not.toBeNull();
|
|
134
|
+
expect(details!.hasAttribute("open")).toBe(false);
|
|
135
|
+
expect(container.textContent).toContain("missing_required_prop");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("hides developer detail entirely when debugExposure is 'hidden'", () => {
|
|
139
|
+
const { container } = render(A2UIRecoveryFailure, {
|
|
140
|
+
content: {
|
|
141
|
+
status: "failed",
|
|
142
|
+
error: "boom",
|
|
143
|
+
attempts: [{ attempt: 1, ok: false }],
|
|
144
|
+
},
|
|
145
|
+
debugExposure: "hidden",
|
|
146
|
+
});
|
|
147
|
+
expect(container.textContent).toContain("Couldn't generate");
|
|
148
|
+
expect(container.querySelector("details")).toBeNull();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("opens the developer detail when debugExposure is 'verbose'", () => {
|
|
152
|
+
const { container } = render(A2UIRecoveryFailure, {
|
|
153
|
+
content: { status: "failed", error: "boom", attempts: [] },
|
|
154
|
+
debugExposure: "verbose",
|
|
155
|
+
});
|
|
156
|
+
const details = container.querySelector("details");
|
|
157
|
+
expect(details).not.toBeNull();
|
|
158
|
+
expect(details!.hasAttribute("open")).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("resolveDebugExposure precedence", () => {
|
|
163
|
+
it("server-stamped content.debugExposure wins over the client option", () => {
|
|
164
|
+
expect(resolveDebugExposure({ debugExposure: "hidden" }, "verbose")).toBe(
|
|
165
|
+
"hidden",
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
it("falls back to the client option when content has none", () => {
|
|
169
|
+
expect(resolveDebugExposure({}, "verbose")).toBe("verbose");
|
|
170
|
+
});
|
|
171
|
+
it("falls back to the resolved default when neither is set", () => {
|
|
172
|
+
expect(resolveDebugExposure(undefined, "collapsed")).toBe("collapsed");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, h
|
|
3
|
-
import {
|
|
2
|
+
import { computed, h } from "vue";
|
|
3
|
+
import type { VNode } from "vue";
|
|
4
|
+
import { parseMarkdown } from "@copilotkit/core";
|
|
5
|
+
import type { MarkdownToken } from "@copilotkit/core";
|
|
4
6
|
|
|
5
7
|
const props = withDefaults(
|
|
6
8
|
defineProps<{ content: string; isStreaming?: boolean }>(),
|
|
@@ -39,7 +41,7 @@ function inline(toks: MarkdownToken[] | undefined): (VNode | string)[] {
|
|
|
39
41
|
case "codespan":
|
|
40
42
|
return h(
|
|
41
43
|
"code",
|
|
42
|
-
{ class: "cpk
|
|
44
|
+
{ class: "cpk-markdown-inline-code" },
|
|
43
45
|
(t as any).text,
|
|
44
46
|
);
|
|
45
47
|
case "br":
|
|
@@ -58,7 +60,7 @@ function inline(toks: MarkdownToken[] | undefined): (VNode | string)[] {
|
|
|
58
60
|
return h("img", {
|
|
59
61
|
src: sanitizeImgSrc((t as any).href),
|
|
60
62
|
alt: (t as any).text,
|
|
61
|
-
class: "cpk
|
|
63
|
+
class: "cpk-markdown-image",
|
|
62
64
|
});
|
|
63
65
|
case "escape":
|
|
64
66
|
return (t as any).text;
|
|
@@ -86,7 +88,7 @@ function block(t: MarkdownToken): VNode | null {
|
|
|
86
88
|
.filter(Boolean),
|
|
87
89
|
);
|
|
88
90
|
case "code":
|
|
89
|
-
return h("pre", { class: "cpk
|
|
91
|
+
return h("pre", { class: "cpk-markdown-code-block" }, [
|
|
90
92
|
h(
|
|
91
93
|
"code",
|
|
92
94
|
{ "data-language": (t as any).lang || undefined },
|
|
@@ -115,7 +117,7 @@ function block(t: MarkdownToken): VNode | null {
|
|
|
115
117
|
}
|
|
116
118
|
case "table": {
|
|
117
119
|
const tbl = t as any;
|
|
118
|
-
return h("div", { class: "cpk
|
|
120
|
+
return h("div", { class: "cpk-markdown-table-container" }, [
|
|
119
121
|
h("table", [
|
|
120
122
|
h("thead", [
|
|
121
123
|
h(
|
|
@@ -38,6 +38,7 @@ import type { LastUserMessageState } from "./last-user-message-context";
|
|
|
38
38
|
import type { Message } from "@ag-ui/core";
|
|
39
39
|
import type { InputContent } from "@copilotkit/shared";
|
|
40
40
|
import { useInspectorThreadOverride } from "../../providers/use-inspector-thread-override";
|
|
41
|
+
import { CopilotKitStyles } from "../../styles/CopilotKitStyles";
|
|
41
42
|
import type {
|
|
42
43
|
CopilotChatInputSlotProps,
|
|
43
44
|
CopilotChatProps,
|
|
@@ -51,6 +52,7 @@ defineOptions({ inheritAttrs: false });
|
|
|
51
52
|
|
|
52
53
|
const props = withDefaults(defineProps<CopilotChatProps>(), {
|
|
53
54
|
autoScroll: true,
|
|
55
|
+
messageFeedback: true,
|
|
54
56
|
welcomeScreen: true,
|
|
55
57
|
inputValue: undefined,
|
|
56
58
|
inputMode: "input",
|
|
@@ -831,6 +833,8 @@ const chatViewSlotProps = computed<CopilotChatViewOverrideSlotProps>(() => ({
|
|
|
831
833
|
inputToolsMenu: props.inputToolsMenu,
|
|
832
834
|
isConnecting: isConnecting.value,
|
|
833
835
|
hasExplicitThreadId: hasExplicitThreadId.value,
|
|
836
|
+
toolCallDisplay: props.toolCallDisplay,
|
|
837
|
+
messageFeedback: props.messageFeedback,
|
|
834
838
|
canStop: shouldAllowStop.value,
|
|
835
839
|
canAddFile: attachmentsEnabled.value,
|
|
836
840
|
canTranscribe: showTranscription.value,
|
|
@@ -887,6 +891,7 @@ const defaultChatViewBindings = computed(() => {
|
|
|
887
891
|
:labels="resolvedLabels"
|
|
888
892
|
>
|
|
889
893
|
<div ref="attachmentContainerRef" style="display: contents">
|
|
894
|
+
<CopilotKitStyles />
|
|
890
895
|
<input
|
|
891
896
|
v-if="attachmentsEnabled"
|
|
892
897
|
ref="fileInputRef"
|
|
@@ -931,6 +936,8 @@ const defaultChatViewBindings = computed(() => {
|
|
|
931
936
|
:input-tools-menu="chatViewSlotProps.inputToolsMenu"
|
|
932
937
|
:is-connecting="chatViewSlotProps.isConnecting"
|
|
933
938
|
:has-explicit-thread-id="chatViewSlotProps.hasExplicitThreadId"
|
|
939
|
+
:tool-call-display="chatViewSlotProps.toolCallDisplay"
|
|
940
|
+
:message-feedback="chatViewSlotProps.messageFeedback"
|
|
934
941
|
:on-finish-transcribe-with-audio="
|
|
935
942
|
chatViewSlotProps.onFinishTranscribeWithAudio
|
|
936
943
|
"
|