@getdom/studio 0.1.0
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/LICENSE.md +109 -0
- package/README.md +44 -0
- package/SKILL.md +83 -0
- package/package.json +73 -0
- package/public/llms.txt +57 -0
- package/public/skills.sh +11 -0
- package/src/pages/_layout/componentCatalog.generated.js +4514 -0
- package/src/pages/_layout/componentInspector.js +60 -0
- package/src/pages/_layout/componentManager.js +192 -0
- package/src/pages/_layout/componentRuntimeRecords.js +49 -0
- package/src/pages/_layout/inspector/componentRegistry.js +639 -0
- package/src/pages/_layout/inspector/serialize.js +134 -0
- package/src/pages/_layout/inspector/useInspector.js +293 -0
- package/src/pages/components/accordion/DomAccordion.vue +39 -0
- package/src/pages/components/breadcrumbs/DomBreadcrumbs.vue +347 -0
- package/src/pages/components/button/DomButton.vue +132 -0
- package/src/pages/components/command-palette/DomCommandPalette.vue +482 -0
- package/src/pages/components/context-menu/DomContextMenu.vue +178 -0
- package/src/pages/components/dialog/DomDialog.vue +271 -0
- package/src/pages/components/dialog/DomDialogSchemaForm.vue +72 -0
- package/src/pages/components/dialog/DomDialogStack.vue +289 -0
- package/src/pages/components/dialog/useDialogs.js +253 -0
- package/src/pages/components/drawer/DomDrawer.vue +159 -0
- package/src/pages/components/dropdown/DomDropdown.vue +261 -0
- package/src/pages/components/icon-button/DomIconButton.vue +155 -0
- package/src/pages/components/lightbox/DomLightbox.vue +1561 -0
- package/src/pages/components/menu/DomMenu.vue +359 -0
- package/src/pages/components/menu/DomMenuItem.vue +233 -0
- package/src/pages/components/menu/DomMenuLabel.vue +19 -0
- package/src/pages/components/menu/DomMenuSeparator.vue +14 -0
- package/src/pages/components/menu/menuContext.js +11 -0
- package/src/pages/components/menu/menuPlacement.js +26 -0
- package/src/pages/components/menu/submenu.js +58 -0
- package/src/pages/components/pagination/DomPagination.vue +265 -0
- package/src/pages/components/playground/DomRenderer.vue +48 -0
- package/src/pages/components/popover/DomPopover.vue +244 -0
- package/src/pages/components/profile-menu/DomProfileMenu.vue +408 -0
- package/src/pages/components/profile-menu/DomProfileMenuItemContent.vue +45 -0
- package/src/pages/components/scroll-menu/DomScrollMenu.vue +430 -0
- package/src/pages/components/scroll-menu/DomScrollMenuItem.vue +212 -0
- package/src/pages/components/scroll-menu/scrollMenuContext.js +22 -0
- package/src/pages/components/splitter-panel/DomSplitterPanel.vue +283 -0
- package/src/pages/components/stepper/DomStepper.vue +386 -0
- package/src/pages/components/tabs/DomTabs.vue +149 -0
- package/src/pages/components/theme-switcher/DomThemeSwitcher.vue +226 -0
- package/src/pages/components/toast/DomToastItem.vue +52 -0
- package/src/pages/components/toast/DomToastStack.vue +768 -0
- package/src/pages/components/toast/useToasts.js +237 -0
- package/src/pages/components/toolbar/DomToolbar.vue +158 -0
- package/src/pages/components/tooltip/DomTooltip.vue +69 -0
- package/src/pages/components/tooltip/vDomTooltip.js +106 -0
- package/src/pages/components/tree-view/DomTreeView.vue +961 -0
- package/src/pages/components/tree-view/treeSchemas.js +129 -0
- package/src/pages/components/tree-view/treeUtils.js +228 -0
- package/src/pages/data/_shared/calculateVirtualWindow.js +101 -0
- package/src/pages/data/_shared/resourceRecord.js +29 -0
- package/src/pages/data/activity-timeline/DomActivityTimeline.vue +417 -0
- package/src/pages/data/agent-chat/DomAgentChatShell.vue +565 -0
- package/src/pages/data/agent-chat/DomAgentConversation.vue +737 -0
- package/src/pages/data/agent-chat/DomAgentConversationList.vue +319 -0
- package/src/pages/data/agent-chat/examples/agentChatMockApi.js +620 -0
- package/src/pages/data/agent-chat/examples/useAgentChatDemoStore.js +730 -0
- package/src/pages/data/data-grid/DataGridVirtualRows.vue +220 -0
- package/src/pages/data/data-grid/DomDataGrid.vue +1643 -0
- package/src/pages/data/document-editor/DomDocumentEditor.vue +3030 -0
- package/src/pages/data/document-editor/documentEditorCodeBlock.js +282 -0
- package/src/pages/data/document-editor/examples/documentEditorMockApi.js +272 -0
- package/src/pages/data/document-editor/examples/useDocumentEditorDemoStore.js +284 -0
- package/src/pages/data/filter-bar/DomFilterBar.vue +533 -0
- package/src/pages/data/kanban/DomKanban.vue +1703 -0
- package/src/pages/data/kanban/kanbanApi.js +80 -0
- package/src/pages/data/media-browser/DomMediaBrowser.vue +2344 -0
- package/src/pages/data/media-browser/examples/mediaBrowserMockApi.js +653 -0
- package/src/pages/data/media-browser/examples/useMediaBrowserDemoStore.js +1157 -0
- package/src/pages/data/month-calendar/DomMonthCalendar.vue +1718 -0
- package/src/pages/data/week-planner/DomWeekPlanner.vue +2308 -0
- package/src/pages/dev/code-block/DomCodeBlock.vue +263 -0
- package/src/pages/dev/json-viewer/DomJsonViewer.vue +962 -0
- package/src/pages/forms/_shared/DomFieldLoadingSpinner.vue +12 -0
- package/src/pages/forms/_shared/options.js +61 -0
- package/src/pages/forms/_shared/useOptionMenu.js +59 -0
- package/src/pages/forms/autocomplete/DomAutocomplete.vue +180 -0
- package/src/pages/forms/calendar/DomCalendar.vue +442 -0
- package/src/pages/forms/checkbox/DomCheckbox.vue +69 -0
- package/src/pages/forms/class-toggle-input/DomClassToggleInput.vue +316 -0
- package/src/pages/forms/code-input/DomCodeInput.vue +202 -0
- package/src/pages/forms/color-input/DomColorInput.vue +118 -0
- package/src/pages/forms/combobox/DomCombobox.vue +237 -0
- package/src/pages/forms/confirmation-code-input/DomConfirmationCodeInput.vue +345 -0
- package/src/pages/forms/date-picker/DomDatePicker.vue +299 -0
- package/src/pages/forms/date-time-picker/DomDateTimePicker.vue +630 -0
- package/src/pages/forms/email-input/DomEmailInput.vue +55 -0
- package/src/pages/forms/field/DomField.vue +267 -0
- package/src/pages/forms/field/fieldLayout.js +1 -0
- package/src/pages/forms/field/fieldProps.js +114 -0
- package/src/pages/forms/field/layouts/DomGridFieldLayout.vue +38 -0
- package/src/pages/forms/field/useField.js +343 -0
- package/src/pages/forms/field/validators.js +364 -0
- package/src/pages/forms/file-upload/DomFileUpload.vue +361 -0
- package/src/pages/forms/form/DomForm.vue +985 -0
- package/src/pages/forms/form/adapters/fieldBuilder.js +45 -0
- package/src/pages/forms/form/adapters/jsonSchema.js +264 -0
- package/src/pages/forms/form/adapters/shared.js +53 -0
- package/src/pages/forms/form/adapters/zod.js +235 -0
- package/src/pages/forms/form/formApi.js +38 -0
- package/src/pages/forms/form/formDefinition.js +392 -0
- package/src/pages/forms/form/schemaAdapter.js +54 -0
- package/src/pages/forms/icon-selector/DomIconSelector.vue +460 -0
- package/src/pages/forms/icon-selector/iconSelectorItems.js +490 -0
- package/src/pages/forms/input-group/DomInputGroup.vue +232 -0
- package/src/pages/forms/json-input/DomJsonInput.vue +227 -0
- package/src/pages/forms/json-list-input/DomJsonListInput.vue +206 -0
- package/src/pages/forms/listbox/DomListbox.vue +105 -0
- package/src/pages/forms/money-input/DomMoneyInput.vue +147 -0
- package/src/pages/forms/native-select/DomNativeSelect.vue +67 -0
- package/src/pages/forms/number-input/DomNumberInput.vue +47 -0
- package/src/pages/forms/password-input/DomPasswordInput.vue +218 -0
- package/src/pages/forms/password-input/pwnedPasswords.js +47 -0
- package/src/pages/forms/password-strength/DomPasswordStrength.vue +128 -0
- package/src/pages/forms/password-strength/passwordStrength.js +92 -0
- package/src/pages/forms/position-input/DomPositionInput.vue +83 -0
- package/src/pages/forms/radio-group/DomRadioGroup.vue +102 -0
- package/src/pages/forms/range-input/DomRangeInput.vue +296 -0
- package/src/pages/forms/select/DomSelect.vue +479 -0
- package/src/pages/forms/select-input/DomSelectInput.vue +155 -0
- package/src/pages/forms/tag-combobox/DomTagCombobox.vue +358 -0
- package/src/pages/forms/text-input/DomTextInput.vue +48 -0
- package/src/pages/forms/textarea-composer/DomTextareaComposer.vue +234 -0
- package/src/pages/forms/textarea-input/DomTextareaInput.vue +86 -0
- package/src/pages/forms/textarea-input/useTextareaAutosize.js +147 -0
- package/src/pages/forms/time-selector/DomTimeSelector.vue +389 -0
- package/src/pages/forms/time-selector/timeUtils.js +194 -0
- package/src/pages/forms/toggle/DomToggle.vue +158 -0
- package/src/pages/forms/toggle-button/DomToggleButton.vue +88 -0
- package/src/pages/forms/toggle-button-group/DomToggleButtonGroup.vue +340 -0
- package/src/pages/forms/url-input/DomUrlInput.vue +55 -0
- package/src/pages/forms/wysiwyg/DomWysiwyg.vue +204 -0
- package/src/pages/lib/css/color.js +245 -0
- package/src/pages/lib/headless/accordion.d.ts +7 -0
- package/src/pages/lib/headless/accordion.js +246 -0
- package/src/pages/lib/headless/autocomplete.d.ts +4 -0
- package/src/pages/lib/headless/autocomplete.js +33 -0
- package/src/pages/lib/headless/base.d.ts +25 -0
- package/src/pages/lib/headless/base.js +127 -0
- package/src/pages/lib/headless/checkbox.d.ts +19 -0
- package/src/pages/lib/headless/checkbox.js +99 -0
- package/src/pages/lib/headless/combobox.d.ts +7 -0
- package/src/pages/lib/headless/combobox.js +488 -0
- package/src/pages/lib/headless/dialog.d.ts +23 -0
- package/src/pages/lib/headless/dialog.js +384 -0
- package/src/pages/lib/headless/drawer.d.ts +21 -0
- package/src/pages/lib/headless/drawer.js +682 -0
- package/src/pages/lib/headless/dropdown.d.ts +19 -0
- package/src/pages/lib/headless/dropdown.js +412 -0
- package/src/pages/lib/headless/floating-effect.js +118 -0
- package/src/pages/lib/headless/floating.d.ts +34 -0
- package/src/pages/lib/headless/floating.js +382 -0
- package/src/pages/lib/headless/index.d.ts +19 -0
- package/src/pages/lib/headless/index.js +19 -0
- package/src/pages/lib/headless/listbox.d.ts +19 -0
- package/src/pages/lib/headless/listbox.js +126 -0
- package/src/pages/lib/headless/menu.d.ts +18 -0
- package/src/pages/lib/headless/menu.js +117 -0
- package/src/pages/lib/headless/popover-panel.d.ts +18 -0
- package/src/pages/lib/headless/popover-panel.js +309 -0
- package/src/pages/lib/headless/popover.d.ts +19 -0
- package/src/pages/lib/headless/popover.js +391 -0
- package/src/pages/lib/headless/radio-group.d.ts +19 -0
- package/src/pages/lib/headless/radio-group.js +100 -0
- package/src/pages/lib/headless/tabs.d.ts +18 -0
- package/src/pages/lib/headless/tabs.js +119 -0
- package/src/pages/lib/headless/toast.d.ts +13 -0
- package/src/pages/lib/headless/toast.js +65 -0
- package/src/pages/lib/headless/toggle.d.ts +19 -0
- package/src/pages/lib/headless/toggle.js +162 -0
- package/src/pages/lib/headless/tooltip-controller.d.ts +22 -0
- package/src/pages/lib/headless/tooltip-controller.js +549 -0
- package/src/pages/lib/headless/tooltip.d.ts +19 -0
- package/src/pages/lib/headless/tooltip.js +153 -0
- package/src/pages/lib/headless/transitions.d.ts +5 -0
- package/src/pages/lib/headless/transitions.js +89 -0
- package/src/pages/lib/vue/_internal/DomLiquidGlassSurface.vue +310 -0
- package/src/pages/lib/vue/_internal/liquidGlass.js +112 -0
- package/src/pages/lib/vue/_internal/liquidGlassConfig.js +10 -0
- package/src/pages/lib/vue/codeHighlighter.js +132 -0
- package/src/pages/lib/vue/floatingTeleportTarget.js +23 -0
- package/src/pages/lib/vue/index.d.ts +6806 -0
- package/src/pages/lib/vue/index.js +218 -0
- package/src/pages/lib/vue/useTheme.js +116 -0
- package/src/pages/mobile/action-sheet/DomActionSheet.vue +186 -0
- package/src/pages/mobile/bottom-nav/DomAppBottomNav.vue +94 -0
- package/src/pages/mobile/list-item/DomAppListItem.vue +90 -0
- package/src/pages/mobile/safe-area/DomSafeArea.vue +39 -0
- package/src/pages/mobile/shell/DomAppShell.vue +51 -0
- package/src/pages/mobile/stack/AppStackScreen.vue +63 -0
- package/src/pages/mobile/stack/DomAppStack.vue +711 -0
- package/src/pages/mobile/top-bar/DomAppTopBar.vue +67 -0
- package/src/pages/visual/alert/DomAlert.vue +363 -0
- package/src/pages/visual/avatar/DomAvatar.vue +110 -0
- package/src/pages/visual/avatar-stack/DomAvatarStack.vue +89 -0
- package/src/pages/visual/badge/DomBadge.vue +105 -0
- package/src/pages/visual/card/DomCard.vue +65 -0
- package/src/pages/visual/card-rail/DomCardRail.vue +99 -0
- package/src/pages/visual/empty-state/DomEmptyState.vue +88 -0
- package/src/pages/visual/progress/DomProgress.vue +212 -0
- package/src/pages/visual/progress/DomRadialProgress.vue +186 -0
- package/src/pages/visual/progress/DomSegmentedProgress.vue +350 -0
- package/src/pages/visual/skeleton/DomSkeleton.vue +174 -0
- package/src/pages/visual/spinner/DomSpinner.vue +122 -0
- package/src/pages/visual/status-pill/DomStatusPill.vue +75 -0
- package/src/pages/visual/text-diff/DomTextDiff.vue +577 -0
- package/src/pages/visual/text-diff/textDiff.js +300 -0
- package/src/style.css +446 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const agentChatShellProps = {
|
|
3
|
+
conversations: {
|
|
4
|
+
type: Array,
|
|
5
|
+
default: () => [],
|
|
6
|
+
_edit: { description: 'Conversation summaries for the sidebar.' },
|
|
7
|
+
},
|
|
8
|
+
conversation: {
|
|
9
|
+
type: Object,
|
|
10
|
+
default: null,
|
|
11
|
+
_edit: { description: 'Active conversation record.' },
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
type: Array,
|
|
15
|
+
default: () => [],
|
|
16
|
+
_edit: { description: 'Messages for the active conversation.' },
|
|
17
|
+
},
|
|
18
|
+
agents: {
|
|
19
|
+
type: Array,
|
|
20
|
+
default: () => [],
|
|
21
|
+
_edit: { description: 'Agents available to the chat shell.' },
|
|
22
|
+
},
|
|
23
|
+
activeConversationId: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: '',
|
|
26
|
+
_edit: { description: 'Selected conversation id.' },
|
|
27
|
+
},
|
|
28
|
+
modelValue: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: '',
|
|
31
|
+
_edit: { description: 'Controlled composer text.' },
|
|
32
|
+
},
|
|
33
|
+
mode: {
|
|
34
|
+
type: String,
|
|
35
|
+
default: 'text',
|
|
36
|
+
validator: (value) => ['text', 'audio'].includes(value),
|
|
37
|
+
_edit: { options: ['text', 'audio'], description: 'Active composer mode.' },
|
|
38
|
+
},
|
|
39
|
+
search: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: '',
|
|
42
|
+
_edit: { description: 'Controlled sidebar search value.' },
|
|
43
|
+
},
|
|
44
|
+
streaming: {
|
|
45
|
+
type: Boolean,
|
|
46
|
+
default: false,
|
|
47
|
+
_edit: { description: 'Whether the active assistant response is streaming.' },
|
|
48
|
+
},
|
|
49
|
+
loading: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: false,
|
|
52
|
+
_edit: { description: 'Whether active conversation messages are loading.' },
|
|
53
|
+
},
|
|
54
|
+
sidebarOpen: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: true,
|
|
57
|
+
_edit: { description: 'Controls the conversation sidebar visibility.' },
|
|
58
|
+
},
|
|
59
|
+
asideOpen: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
default: true,
|
|
62
|
+
_edit: { description: 'Controls the contextual aside visibility.' },
|
|
63
|
+
},
|
|
64
|
+
sidebarSize: {
|
|
65
|
+
type: Number,
|
|
66
|
+
default: 300,
|
|
67
|
+
_edit: { description: 'Resizable sidebar width in pixels.' },
|
|
68
|
+
},
|
|
69
|
+
asideSize: {
|
|
70
|
+
type: Number,
|
|
71
|
+
default: 340,
|
|
72
|
+
_edit: { description: 'Resizable aside width in pixels.' },
|
|
73
|
+
},
|
|
74
|
+
minSidebar: {
|
|
75
|
+
type: Number,
|
|
76
|
+
default: 240,
|
|
77
|
+
_edit: { description: 'Minimum sidebar width while open.' },
|
|
78
|
+
},
|
|
79
|
+
minConversation: {
|
|
80
|
+
type: Number,
|
|
81
|
+
default: 420,
|
|
82
|
+
_edit: { description: 'Minimum center conversation width.' },
|
|
83
|
+
},
|
|
84
|
+
minAside: {
|
|
85
|
+
type: Number,
|
|
86
|
+
default: 280,
|
|
87
|
+
_edit: { description: 'Minimum aside width while open.' },
|
|
88
|
+
},
|
|
89
|
+
toolComponents: {
|
|
90
|
+
type: Object,
|
|
91
|
+
default: () => ({}),
|
|
92
|
+
_edit: { description: 'Tool renderer component map passed through to DomAgentConversation.' },
|
|
93
|
+
},
|
|
94
|
+
title: {
|
|
95
|
+
type: String,
|
|
96
|
+
default: 'Agent workspace',
|
|
97
|
+
_edit: { description: 'Accessible label for the chat application shell.' },
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<script setup>
|
|
103
|
+
import { computed, useId } from 'vue';
|
|
104
|
+
import DomIconButton from '../../components/icon-button/DomIconButton.vue';
|
|
105
|
+
import DomSplitterPanel from '../../components/splitter-panel/DomSplitterPanel.vue';
|
|
106
|
+
import DomStatusPill from '../../visual/status-pill/DomStatusPill.vue';
|
|
107
|
+
import DomAgentConversation from './DomAgentConversation.vue';
|
|
108
|
+
import DomAgentConversationList from './DomAgentConversationList.vue';
|
|
109
|
+
|
|
110
|
+
defineOptions({
|
|
111
|
+
__doc: {
|
|
112
|
+
name: 'Agent chat shell',
|
|
113
|
+
tag: '<DomAgentChatShell>',
|
|
114
|
+
description: 'A three-pane AI chat application shell with a resizable conversation sidebar, scroll-contained conversation window, closable context aside, streaming tool calls, and audio/text handoff controls.',
|
|
115
|
+
icon: 'M4 5h5v14H4V5Zm7 0h9v14h-9V5Zm2 3h5M13 12h5M13 16h3',
|
|
116
|
+
events: [
|
|
117
|
+
{ name: 'update:modelValue', payload: 'string', description: 'Passes composer updates from the conversation component.' },
|
|
118
|
+
{ name: 'update:mode', payload: '\'text\' | \'audio\'', description: 'Passes audio/text mode changes from the conversation component.' },
|
|
119
|
+
{ name: 'update:search', payload: 'string', description: 'Passes sidebar search updates.' },
|
|
120
|
+
{ name: 'update:sidebarOpen', payload: 'boolean', description: 'Fired when the sidebar is opened or closed.' },
|
|
121
|
+
{ name: 'update:asideOpen', payload: 'boolean', description: 'Fired when the aside is opened or closed.' },
|
|
122
|
+
{ name: 'update:sidebarSize', payload: 'number', description: 'Fired when the sidebar splitter changes width.' },
|
|
123
|
+
{ name: 'update:asideSize', payload: 'number', description: 'Fired when the aside splitter changes width.' },
|
|
124
|
+
{ name: 'select-conversation', payload: 'conversation', description: 'Fired when a sidebar row is selected.' },
|
|
125
|
+
{ name: 'new-conversation', payload: 'undefined', description: 'Fired when the sidebar new button is activated.' },
|
|
126
|
+
{ name: 'submit', payload: '({ content, mode, conversation })', description: 'Fired when the composer submits.' },
|
|
127
|
+
{ name: 'stop', payload: '({ conversation })', description: 'Fired when streaming is stopped.' },
|
|
128
|
+
{ name: 'tool-action', payload: '({ action, tool, message })', description: 'Fired by generic or custom tool renderers.' },
|
|
129
|
+
],
|
|
130
|
+
slots: [
|
|
131
|
+
{ name: 'sidebar', payload: '{ conversations, activeConversationId }', description: 'Replace the conversation sidebar.' },
|
|
132
|
+
{ name: 'conversation', payload: '{ conversation, messages, activeAgent }', description: 'Replace the main conversation component.' },
|
|
133
|
+
{ name: 'aside', payload: '{ conversation, activeAgent, toolParts }', description: 'Replace the contextual aside.' },
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const props = defineProps(agentChatShellProps);
|
|
139
|
+
const emit = defineEmits([
|
|
140
|
+
'update:modelValue',
|
|
141
|
+
'update:mode',
|
|
142
|
+
'update:search',
|
|
143
|
+
'update:sidebarOpen',
|
|
144
|
+
'update:asideOpen',
|
|
145
|
+
'update:sidebarSize',
|
|
146
|
+
'update:asideSize',
|
|
147
|
+
'select-conversation',
|
|
148
|
+
'new-conversation',
|
|
149
|
+
'submit',
|
|
150
|
+
'stop',
|
|
151
|
+
'tool-action',
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
const shellId = useId();
|
|
155
|
+
|
|
156
|
+
const activeAgent = computed(() => findAgentById(props.agents, props.conversation?.agentId));
|
|
157
|
+
const toolParts = computed(() => collectToolParts(props.messages));
|
|
158
|
+
const sidebarSizeValue = computed(() => props.sidebarOpen ? props.sidebarSize : 0);
|
|
159
|
+
const sidebarMinimum = computed(() => props.sidebarOpen ? props.minSidebar : 0);
|
|
160
|
+
const asideMinimum = computed(() => props.asideOpen ? props.minAside : 0);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Emits a sidebar visibility update.
|
|
164
|
+
*
|
|
165
|
+
* @param {boolean} value Visibility state.
|
|
166
|
+
* @returns {void}
|
|
167
|
+
*/
|
|
168
|
+
function setSidebarOpen(value) {
|
|
169
|
+
emit('update:sidebarOpen', Boolean(value));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Emits an aside visibility update.
|
|
174
|
+
*
|
|
175
|
+
* @param {boolean} value Visibility state.
|
|
176
|
+
* @returns {void}
|
|
177
|
+
*/
|
|
178
|
+
function setAsideOpen(value) {
|
|
179
|
+
emit('update:asideOpen', Boolean(value));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Toggles the conversation sidebar.
|
|
184
|
+
*
|
|
185
|
+
* @returns {void}
|
|
186
|
+
*/
|
|
187
|
+
function toggleSidebar() {
|
|
188
|
+
setSidebarOpen(!props.sidebarOpen);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Toggles the contextual aside.
|
|
193
|
+
*
|
|
194
|
+
* @returns {void}
|
|
195
|
+
*/
|
|
196
|
+
function toggleAside() {
|
|
197
|
+
setAsideOpen(!props.asideOpen);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Emits a sidebar size update from the splitter.
|
|
202
|
+
*
|
|
203
|
+
* @param {number} value Sidebar width in pixels.
|
|
204
|
+
* @returns {void}
|
|
205
|
+
*/
|
|
206
|
+
function updateSidebarSize(value) {
|
|
207
|
+
if (!props.sidebarOpen) return;
|
|
208
|
+
emit('update:sidebarSize', value);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Emits an aside size update from the splitter.
|
|
213
|
+
*
|
|
214
|
+
* @param {number} value Aside width in pixels.
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
217
|
+
function updateAsideSize(value) {
|
|
218
|
+
if (!props.asideOpen) return;
|
|
219
|
+
emit('update:asideSize', value);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Passes a selected conversation up to the shell owner.
|
|
224
|
+
*
|
|
225
|
+
* @param {Record<string, unknown>} conversation Conversation record.
|
|
226
|
+
* @returns {void}
|
|
227
|
+
*/
|
|
228
|
+
function selectConversation(conversation) {
|
|
229
|
+
emit('select-conversation', conversation);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Passes a new conversation request to the shell owner.
|
|
234
|
+
*
|
|
235
|
+
* @returns {void}
|
|
236
|
+
*/
|
|
237
|
+
function createConversation() {
|
|
238
|
+
emit('new-conversation');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Emits a composer value update.
|
|
243
|
+
*
|
|
244
|
+
* @param {string} value Composer value.
|
|
245
|
+
* @returns {void}
|
|
246
|
+
*/
|
|
247
|
+
function updateComposer(value) {
|
|
248
|
+
emit('update:modelValue', value);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Emits a composer mode update.
|
|
253
|
+
*
|
|
254
|
+
* @param {'text'|'audio'} value Composer mode.
|
|
255
|
+
* @returns {void}
|
|
256
|
+
*/
|
|
257
|
+
function updateMode(value) {
|
|
258
|
+
emit('update:mode', value);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Emits a sidebar search update.
|
|
263
|
+
*
|
|
264
|
+
* @param {string} value Search value.
|
|
265
|
+
* @returns {void}
|
|
266
|
+
*/
|
|
267
|
+
function updateSearch(value) {
|
|
268
|
+
emit('update:search', value);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Passes a composer submit event to the shell owner.
|
|
273
|
+
*
|
|
274
|
+
* @param {Record<string, unknown>} payload Submit payload.
|
|
275
|
+
* @returns {void}
|
|
276
|
+
*/
|
|
277
|
+
function submitMessage(payload) {
|
|
278
|
+
emit('submit', payload);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Passes a streaming stop event to the shell owner.
|
|
283
|
+
*
|
|
284
|
+
* @param {Record<string, unknown>} payload Stop payload.
|
|
285
|
+
* @returns {void}
|
|
286
|
+
*/
|
|
287
|
+
function stopStreaming(payload) {
|
|
288
|
+
emit('stop', payload);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Passes a tool action event to the shell owner.
|
|
293
|
+
*
|
|
294
|
+
* @param {Record<string, unknown>} payload Tool action payload.
|
|
295
|
+
* @returns {void}
|
|
296
|
+
*/
|
|
297
|
+
function emitToolAction(payload) {
|
|
298
|
+
emit('tool-action', payload);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Finds an agent by id.
|
|
303
|
+
*
|
|
304
|
+
* @param {Array<Record<string, unknown>>} agents Agent collection.
|
|
305
|
+
* @param {string} id Agent id.
|
|
306
|
+
* @returns {Record<string, unknown>|null} Matching agent.
|
|
307
|
+
*/
|
|
308
|
+
function findAgentById(agents, id) {
|
|
309
|
+
return agents.find((agent) => agent.id === id) || null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Collects tool-call and tool-result parts from message data.
|
|
314
|
+
*
|
|
315
|
+
* @param {Array<Record<string, unknown>>} messages Message collection.
|
|
316
|
+
* @returns {Array<Record<string, unknown>>} Tool parts.
|
|
317
|
+
*/
|
|
318
|
+
function collectToolParts(messages) {
|
|
319
|
+
const tools = [];
|
|
320
|
+
for (const message of messages) {
|
|
321
|
+
for (const part of Array.isArray(message.parts) ? message.parts : []) {
|
|
322
|
+
if (part.type === 'tool-call' || part.type === 'tool-result') {
|
|
323
|
+
tools.push({ ...part, messageId: message.id });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return tools;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Resolves the label shown for an active agent.
|
|
332
|
+
*
|
|
333
|
+
* @returns {string} Agent label.
|
|
334
|
+
*/
|
|
335
|
+
function activeAgentLabel() {
|
|
336
|
+
return activeAgent.value?.name || 'No agent selected';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Resolves the model label shown in the aside.
|
|
341
|
+
*
|
|
342
|
+
* @returns {string} Model label.
|
|
343
|
+
*/
|
|
344
|
+
function activeModelLabel() {
|
|
345
|
+
return activeAgent.value?.model || 'Runtime not set';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Resolves a status tone for the active conversation.
|
|
350
|
+
*
|
|
351
|
+
* @returns {'neutral'|'info'|'success'|'warning'|'danger'} Status tone.
|
|
352
|
+
*/
|
|
353
|
+
function conversationStatusTone() {
|
|
354
|
+
return {
|
|
355
|
+
streaming: 'info',
|
|
356
|
+
waiting: 'warning',
|
|
357
|
+
error: 'danger',
|
|
358
|
+
complete: 'success',
|
|
359
|
+
}[props.streaming ? 'streaming' : props.conversation?.status] || 'neutral';
|
|
360
|
+
}
|
|
361
|
+
</script>
|
|
362
|
+
|
|
363
|
+
<template>
|
|
364
|
+
<section
|
|
365
|
+
:id="shellId"
|
|
366
|
+
class="h-full min-h-0 w-full min-w-0 overflow-hidden rounded-lg border border-border bg-canvas text-canvas-fg shadow-sm"
|
|
367
|
+
:aria-label="title"
|
|
368
|
+
>
|
|
369
|
+
<div class="hidden h-full min-h-0 md:block">
|
|
370
|
+
<DomSplitterPanel
|
|
371
|
+
class="h-full min-h-0"
|
|
372
|
+
:start-size="sidebarSizeValue"
|
|
373
|
+
:end-size="asideSize"
|
|
374
|
+
:min-start="sidebarMinimum"
|
|
375
|
+
:min-main="minConversation"
|
|
376
|
+
:min-end="asideMinimum"
|
|
377
|
+
:handle-size="8"
|
|
378
|
+
handle-class="transition hover:bg-border/70 data-[dragging=true]:bg-ring/30"
|
|
379
|
+
@update:start-size="updateSidebarSize"
|
|
380
|
+
@update:end-size="updateAsideSize"
|
|
381
|
+
>
|
|
382
|
+
<template #start>
|
|
383
|
+
<slot name="sidebar" :conversations="conversations" :active-conversation-id="activeConversationId">
|
|
384
|
+
<DomAgentConversationList
|
|
385
|
+
v-if="sidebarOpen"
|
|
386
|
+
class="h-full"
|
|
387
|
+
:conversations="conversations"
|
|
388
|
+
:agents="agents"
|
|
389
|
+
:active-conversation-id="activeConversationId"
|
|
390
|
+
:search="search"
|
|
391
|
+
@update:search="updateSearch"
|
|
392
|
+
@select="selectConversation"
|
|
393
|
+
@new="createConversation"
|
|
394
|
+
/>
|
|
395
|
+
</slot>
|
|
396
|
+
</template>
|
|
397
|
+
|
|
398
|
+
<slot name="conversation" :conversation="conversation" :messages="messages" :active-agent="activeAgent">
|
|
399
|
+
<DomAgentConversation
|
|
400
|
+
class="h-full rounded-none border-0 shadow-none"
|
|
401
|
+
:conversation="conversation"
|
|
402
|
+
:messages="messages"
|
|
403
|
+
:agents="agents"
|
|
404
|
+
:model-value="modelValue"
|
|
405
|
+
:mode="mode"
|
|
406
|
+
:streaming="streaming"
|
|
407
|
+
:loading="loading"
|
|
408
|
+
:tool-components="toolComponents"
|
|
409
|
+
@update:model-value="updateComposer"
|
|
410
|
+
@update:mode="updateMode"
|
|
411
|
+
@submit="submitMessage"
|
|
412
|
+
@stop="stopStreaming"
|
|
413
|
+
@tool-action="emitToolAction"
|
|
414
|
+
>
|
|
415
|
+
<template #header-leading="{ conversation: headerConversation, activeAgent: headerAgent }">
|
|
416
|
+
<DomIconButton
|
|
417
|
+
:label="sidebarOpen ? 'Close conversations' : 'Open conversations'"
|
|
418
|
+
size="sm"
|
|
419
|
+
variant="ghost"
|
|
420
|
+
:active="sidebarOpen"
|
|
421
|
+
icon="M4 5h6v14H4V5Zm10 2h6M14 12h6M14 17h4"
|
|
422
|
+
@click="toggleSidebar"
|
|
423
|
+
/>
|
|
424
|
+
<span class="sr-only">{{ headerConversation?.title || headerAgent?.name }}</span>
|
|
425
|
+
</template>
|
|
426
|
+
<template #header-actions>
|
|
427
|
+
<DomIconButton
|
|
428
|
+
:label="asideOpen ? 'Close context panel' : 'Open context panel'"
|
|
429
|
+
size="sm"
|
|
430
|
+
variant="ghost"
|
|
431
|
+
:active="asideOpen"
|
|
432
|
+
icon="M5 6h14M5 12h14M5 18h14"
|
|
433
|
+
@click="toggleAside"
|
|
434
|
+
/>
|
|
435
|
+
</template>
|
|
436
|
+
</DomAgentConversation>
|
|
437
|
+
</slot>
|
|
438
|
+
|
|
439
|
+
<template v-if="asideOpen" #end>
|
|
440
|
+
<slot name="aside" :conversation="conversation" :active-agent="activeAgent" :tool-parts="toolParts">
|
|
441
|
+
<aside class="flex h-full min-h-0 flex-col bg-canvas">
|
|
442
|
+
<header class="shrink-0 border-b border-border bg-secondary/25 p-4">
|
|
443
|
+
<div class="flex items-start justify-between gap-3">
|
|
444
|
+
<div class="min-w-0">
|
|
445
|
+
<p class="text-xs font-semibold uppercase text-muted-fg">Agent context</p>
|
|
446
|
+
<h2 class="mt-1 truncate text-sm font-semibold text-canvas-fg">{{ activeAgentLabel() }}</h2>
|
|
447
|
+
</div>
|
|
448
|
+
<DomIconButton label="Close context panel" size="sm" variant="ghost" icon="M6 6l12 12M18 6 6 18" @click="setAsideOpen(false)" />
|
|
449
|
+
</div>
|
|
450
|
+
</header>
|
|
451
|
+
<div class="min-h-0 flex-1 overflow-y-auto p-4">
|
|
452
|
+
<div class="rounded-lg border border-border bg-secondary/25 p-4">
|
|
453
|
+
<p class="text-xs font-semibold text-muted-fg">Runtime</p>
|
|
454
|
+
<p class="mt-1 text-sm font-semibold text-canvas-fg">{{ activeModelLabel() }}</p>
|
|
455
|
+
<p class="mt-2 text-sm leading-6 text-muted-fg">{{ activeAgent?.role || 'Connect an agent to show instructions, tools, and memory context here.' }}</p>
|
|
456
|
+
</div>
|
|
457
|
+
|
|
458
|
+
<div class="mt-4 rounded-lg border border-border p-4">
|
|
459
|
+
<div class="flex items-center justify-between gap-3">
|
|
460
|
+
<p class="text-xs font-semibold uppercase text-muted-fg">Conversation</p>
|
|
461
|
+
<DomStatusPill size="sm" :tone="conversationStatusTone()">{{ streaming ? 'streaming' : (conversation?.status || 'idle') }}</DomStatusPill>
|
|
462
|
+
</div>
|
|
463
|
+
<dl class="mt-3 grid grid-cols-2 gap-3 text-sm">
|
|
464
|
+
<div>
|
|
465
|
+
<dt class="text-xs text-muted-fg">Messages</dt>
|
|
466
|
+
<dd class="font-semibold text-canvas-fg">{{ messages.length }}</dd>
|
|
467
|
+
</div>
|
|
468
|
+
<div>
|
|
469
|
+
<dt class="text-xs text-muted-fg">Tools</dt>
|
|
470
|
+
<dd class="font-semibold text-canvas-fg">{{ toolParts.length }}</dd>
|
|
471
|
+
</div>
|
|
472
|
+
</dl>
|
|
473
|
+
</div>
|
|
474
|
+
|
|
475
|
+
<div class="mt-4 rounded-lg border border-border p-4">
|
|
476
|
+
<p class="text-xs font-semibold uppercase text-muted-fg">Tool calls</p>
|
|
477
|
+
<div v-if="toolParts.length" class="mt-3 space-y-2">
|
|
478
|
+
<div
|
|
479
|
+
v-for="tool in toolParts"
|
|
480
|
+
:key="`${tool.messageId}-${tool.id || tool.name}`"
|
|
481
|
+
class="rounded-md border border-border bg-secondary/25 p-3"
|
|
482
|
+
>
|
|
483
|
+
<div class="flex items-start justify-between gap-2">
|
|
484
|
+
<p class="min-w-0 truncate text-sm font-semibold text-canvas-fg">{{ tool.label || tool.name }}</p>
|
|
485
|
+
<span class="shrink-0 text-xs text-muted-fg">{{ tool.status || 'ready' }}</span>
|
|
486
|
+
</div>
|
|
487
|
+
<p class="mt-1 truncate text-xs text-muted-fg">{{ tool.name }}</p>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
<p v-else class="mt-3 text-sm leading-6 text-muted-fg">Tool activity appears here as the agent works.</p>
|
|
491
|
+
</div>
|
|
492
|
+
|
|
493
|
+
<div class="mt-4 rounded-lg border border-border p-4">
|
|
494
|
+
<p class="text-xs font-semibold uppercase text-muted-fg">Available tools</p>
|
|
495
|
+
<div v-if="activeAgent?.tools?.length" class="mt-3 flex flex-wrap gap-2">
|
|
496
|
+
<span
|
|
497
|
+
v-for="tool in activeAgent.tools"
|
|
498
|
+
:key="tool"
|
|
499
|
+
class="rounded-full border border-border bg-secondary px-2.5 py-1 text-xs font-medium text-muted-fg"
|
|
500
|
+
>
|
|
501
|
+
{{ tool }}
|
|
502
|
+
</span>
|
|
503
|
+
</div>
|
|
504
|
+
<p v-else class="mt-3 text-sm leading-6 text-muted-fg">No tool manifest supplied.</p>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</aside>
|
|
508
|
+
</slot>
|
|
509
|
+
</template>
|
|
510
|
+
</DomSplitterPanel>
|
|
511
|
+
</div>
|
|
512
|
+
|
|
513
|
+
<div class="flex h-full min-h-0 flex-col md:hidden">
|
|
514
|
+
<div v-if="sidebarOpen" class="max-h-64 shrink-0 overflow-hidden border-b border-border">
|
|
515
|
+
<DomAgentConversationList
|
|
516
|
+
class="h-64"
|
|
517
|
+
:conversations="conversations"
|
|
518
|
+
:agents="agents"
|
|
519
|
+
:active-conversation-id="activeConversationId"
|
|
520
|
+
:search="search"
|
|
521
|
+
@update:search="updateSearch"
|
|
522
|
+
@select="selectConversation"
|
|
523
|
+
@new="createConversation"
|
|
524
|
+
/>
|
|
525
|
+
</div>
|
|
526
|
+
<DomAgentConversation
|
|
527
|
+
class="min-h-0 flex-1 rounded-none border-0 shadow-none"
|
|
528
|
+
:conversation="conversation"
|
|
529
|
+
:messages="messages"
|
|
530
|
+
:agents="agents"
|
|
531
|
+
:model-value="modelValue"
|
|
532
|
+
:mode="mode"
|
|
533
|
+
:streaming="streaming"
|
|
534
|
+
:loading="loading"
|
|
535
|
+
:tool-components="toolComponents"
|
|
536
|
+
@update:model-value="updateComposer"
|
|
537
|
+
@update:mode="updateMode"
|
|
538
|
+
@submit="submitMessage"
|
|
539
|
+
@stop="stopStreaming"
|
|
540
|
+
@tool-action="emitToolAction"
|
|
541
|
+
>
|
|
542
|
+
<template #header-leading>
|
|
543
|
+
<DomIconButton
|
|
544
|
+
:label="sidebarOpen ? 'Close conversations' : 'Open conversations'"
|
|
545
|
+
size="sm"
|
|
546
|
+
variant="ghost"
|
|
547
|
+
:active="sidebarOpen"
|
|
548
|
+
icon="M4 5h6v14H4V5Zm10 2h6M14 12h6M14 17h4"
|
|
549
|
+
@click="toggleSidebar"
|
|
550
|
+
/>
|
|
551
|
+
</template>
|
|
552
|
+
<template #header-actions>
|
|
553
|
+
<DomIconButton
|
|
554
|
+
:label="asideOpen ? 'Close context panel' : 'Open context panel'"
|
|
555
|
+
size="sm"
|
|
556
|
+
variant="ghost"
|
|
557
|
+
:active="asideOpen"
|
|
558
|
+
icon="M5 6h14M5 12h14M5 18h14"
|
|
559
|
+
@click="toggleAside"
|
|
560
|
+
/>
|
|
561
|
+
</template>
|
|
562
|
+
</DomAgentConversation>
|
|
563
|
+
</div>
|
|
564
|
+
</section>
|
|
565
|
+
</template>
|