@vc-shell/framework 1.2.1 → 1.2.3-beta.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/core/composables/index.ts +2 -0
- package/core/composables/useAssets/index.ts +72 -28
- package/core/composables/useAsync/index.ts +4 -1
- package/core/composables/useBladeRegistry/index.ts +6 -5
- package/core/composables/useBreadcrumbs/index.ts +4 -1
- package/core/composables/useErrorHandler/index.ts +4 -1
- package/core/composables/useFunctions/debounce.ts +0 -1
- package/core/composables/useFunctions/delay.ts +0 -1
- package/core/composables/useFunctions/index.ts +0 -1
- package/core/composables/useFunctions/once.ts +0 -1
- package/core/composables/useFunctions/sleep.ts +0 -1
- package/core/composables/useFunctions/throttle.ts +0 -1
- package/core/composables/useGlobalSearch/index.ts +3 -3
- package/core/composables/useMenuService/index.ts +5 -2
- package/core/composables/useNotifications/index.ts +5 -2
- package/core/composables/useTheme/index.ts +4 -1
- package/core/composables/useUser/index.ts +189 -20
- package/core/composables/useWidgets/index.ts +5 -2
- package/core/constants/defaults.ts +76 -0
- package/core/constants/index.ts +2 -0
- package/core/constants/ui.ts +68 -0
- package/core/interceptors/index.ts +5 -2
- package/core/plugins/ai-agent/README.md +336 -0
- package/core/plugins/ai-agent/components/VcAiAgentPanel.vue +125 -0
- package/core/plugins/ai-agent/components/_internal/VcAiAgentHeader.vue +182 -0
- package/core/plugins/ai-agent/components/_internal/VcAiAgentIframe.vue +77 -0
- package/core/plugins/ai-agent/components/index.ts +1 -0
- package/core/plugins/ai-agent/composables/index.ts +4 -0
- package/core/plugins/ai-agent/composables/useAiAgent.ts +231 -0
- package/core/plugins/ai-agent/composables/useAiAgentContext.ts +280 -0
- package/core/plugins/ai-agent/constants.ts +89 -0
- package/core/plugins/ai-agent/index.ts +91 -0
- package/core/plugins/ai-agent/services/ai-agent-service.ts +598 -0
- package/core/plugins/ai-agent/types.ts +310 -0
- package/core/plugins/modularity/index.ts +8 -6
- package/core/plugins/modularity/loader.ts +36 -33
- package/core/plugins/signalR/index.ts +6 -3
- package/core/services/app-bar-menu-service.ts +4 -1
- package/core/services/dashboard-service.ts +4 -1
- package/core/services/index.ts +2 -0
- package/core/services/menu-service.ts +4 -1
- package/core/services/settings-menu-service.ts +4 -1
- package/core/services/toolbar-service.ts +18 -3
- package/core/services/widget-service.ts +7 -4
- package/core/types/index.ts +3 -0
- package/core/types/services.ts +194 -0
- package/core/utilities/errorTypes.ts +126 -0
- package/core/utilities/index.ts +2 -0
- package/core/utilities/logger.ts +120 -0
- package/dist/core/composables/useAssets/index.d.ts.map +1 -1
- package/dist/core/composables/useAsync/index.d.ts.map +1 -1
- package/dist/core/composables/useBladeRegistry/index.d.ts.map +1 -1
- package/dist/core/composables/useBreadcrumbs/index.d.ts.map +1 -1
- package/dist/core/composables/useErrorHandler/index.d.ts.map +1 -1
- package/dist/core/composables/useFunctions/debounce.d.ts.map +1 -1
- package/dist/core/composables/useFunctions/delay.d.ts.map +1 -1
- package/dist/core/composables/useFunctions/index.d.ts.map +1 -1
- package/dist/core/composables/useFunctions/once.d.ts.map +1 -1
- package/dist/core/composables/useFunctions/sleep.d.ts.map +1 -1
- package/dist/core/composables/useFunctions/throttle.d.ts.map +1 -1
- package/dist/core/composables/useGlobalSearch/index.d.ts.map +1 -1
- package/dist/core/composables/useMenuService/index.d.ts.map +1 -1
- package/dist/core/composables/useNotifications/index.d.ts.map +1 -1
- package/dist/core/composables/useTheme/index.d.ts.map +1 -1
- package/dist/core/composables/useUser/index.d.ts +8 -0
- package/dist/core/composables/useUser/index.d.ts.map +1 -1
- package/dist/core/composables/useWidgets/index.d.ts.map +1 -1
- package/dist/core/constants/defaults.d.ts +63 -0
- package/dist/core/constants/defaults.d.ts.map +1 -0
- package/dist/core/constants/index.d.ts +2 -0
- package/dist/core/constants/index.d.ts.map +1 -1
- package/dist/core/constants/ui.d.ts +50 -0
- package/dist/core/constants/ui.d.ts.map +1 -0
- package/dist/core/interceptors/index.d.ts.map +1 -1
- package/dist/core/plugins/ai-agent/components/VcAiAgentPanel.vue.d.ts +3 -0
- package/dist/core/plugins/ai-agent/components/VcAiAgentPanel.vue.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentHeader.vue.d.ts +15 -0
- package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentHeader.vue.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentIframe.vue.d.ts +10 -0
- package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentIframe.vue.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/components/index.d.ts +2 -0
- package/dist/core/plugins/ai-agent/components/index.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/composables/index.d.ts +4 -0
- package/dist/core/plugins/ai-agent/composables/index.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/composables/useAiAgent.d.ts +95 -0
- package/dist/core/plugins/ai-agent/composables/useAiAgent.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/composables/useAiAgentContext.d.ts +55 -0
- package/dist/core/plugins/ai-agent/composables/useAiAgentContext.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/constants.d.ts +47 -0
- package/dist/core/plugins/ai-agent/constants.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/index.d.ts +48 -0
- package/dist/core/plugins/ai-agent/index.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/services/ai-agent-service.d.ts +45 -0
- package/dist/core/plugins/ai-agent/services/ai-agent-service.d.ts.map +1 -0
- package/dist/core/plugins/ai-agent/types.d.ts +258 -0
- package/dist/core/plugins/ai-agent/types.d.ts.map +1 -0
- package/dist/core/plugins/modularity/index.d.ts.map +1 -1
- package/dist/core/plugins/modularity/loader.d.ts.map +1 -1
- package/dist/core/plugins/signalR/index.d.ts.map +1 -1
- package/dist/core/services/app-bar-menu-service.d.ts.map +1 -1
- package/dist/core/services/dashboard-service.d.ts.map +1 -1
- package/dist/core/services/menu-service.d.ts.map +1 -1
- package/dist/core/services/settings-menu-service.d.ts.map +1 -1
- package/dist/core/services/toolbar-service.d.ts.map +1 -1
- package/dist/core/services/widget-service.d.ts.map +1 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/services.d.ts +169 -0
- package/dist/core/types/services.d.ts.map +1 -0
- package/dist/core/utilities/errorTypes.d.ts +61 -0
- package/dist/core/utilities/errorTypes.d.ts.map +1 -0
- package/dist/core/utilities/index.d.ts +2 -0
- package/dist/core/utilities/index.d.ts.map +1 -1
- package/dist/core/utilities/logger.d.ts +259 -0
- package/dist/core/utilities/logger.d.ts.map +1 -0
- package/dist/framework.js +9623 -8417
- package/dist/index.css +1 -1
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/injection-keys.d.ts +21 -6
- package/dist/injection-keys.d.ts.map +1 -1
- package/dist/shared/components/app-switcher/composables/useAppSwitcher/index.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts.map +1 -1
- package/dist/shared/components/notifications/composables/useContainer/index.d.ts.map +1 -1
- package/dist/shared/components/notifications/composables/useInstance/index.d.ts.map +1 -1
- package/dist/shared/components/notifications/core/notification.d.ts.map +1 -1
- package/dist/shared/components/popup-handler/components/vc-popup-container/vc-popup-container.vue.d.ts.map +1 -1
- package/dist/shared/components/sign-in/useExternalProvider.d.ts.map +1 -1
- package/dist/shared/composables/index.d.ts +1 -0
- package/dist/shared/composables/index.d.ts.map +1 -1
- package/dist/shared/composables/useExternalWidgets.d.ts.map +1 -1
- package/dist/shared/composables/useMenuExpanded.d.ts.map +1 -1
- package/dist/shared/composables/useTableSelection.d.ts +57 -0
- package/dist/shared/composables/useTableSelection.d.ts.map +1 -0
- package/dist/shared/composables/useTableSort.d.ts.map +1 -1
- package/dist/shared/modules/assets-manager/components/assets-manager/assets-manager.vue.d.ts.map +1 -1
- package/dist/shared/pages/LoginPage/components/login/Login.vue.d.ts.map +1 -1
- package/dist/shared/utilities/colorUtils.d.ts +0 -6
- package/dist/shared/utilities/colorUtils.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/components/atoms/vc-banner/vc-banner.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts +0 -15
- package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-container/vc-container.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-icon/vc-lucide-icon.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-link/vc-link.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-loading/vc-loading.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-status/vc-status.vue.d.ts +0 -5
- package/dist/ui/components/atoms/vc-status/vc-status.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-tooltip/vc-tooltip.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-video/vc-video.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-widget/vc-widget.vue.d.ts.map +1 -1
- package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue.d.ts.map +1 -1
- package/dist/ui/components/molecules/vc-input/vc-input.vue.d.ts.map +1 -1
- package/dist/ui/components/molecules/vc-pagination/vc-pagination.vue.d.ts.map +1 -1
- package/dist/ui/components/molecules/vc-toast/vc-toast.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-login-form/vc-login-form.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/composables/useTableActions.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/composables/useTableColumnResize.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/composables/useTableRowReorder.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/composables/useTableSelection.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/composables/useTableState.d.ts.map +1 -1
- package/dist/{vendor-lodash-es-BqkGj3Jl.js → vendor-lodash-es-SgOIjJF8.js} +2 -0
- package/package.json +5 -5
- package/shared/components/app-switcher/composables/useAppSwitcher/index.ts +4 -1
- package/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +67 -4
- package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +13 -10
- package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.ts +7 -4
- package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.ts +4 -1
- package/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.ts +4 -1
- package/shared/components/change-password/change-password.vue +1 -1
- package/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.ts +14 -5
- package/shared/components/draggable-dashboard/composables/useLayoutPersistence.ts +5 -2
- package/shared/components/index.ts +2 -0
- package/shared/components/notifications/composables/useContainer/index.ts +8 -6
- package/shared/components/notifications/composables/useInstance/index.ts +4 -1
- package/shared/components/notifications/core/notification.ts +10 -7
- package/shared/components/popup-handler/components/vc-popup-container/vc-popup-container.vue +20 -1
- package/shared/components/sign-in/useExternalProvider.ts +6 -4
- package/shared/composables/index.ts +1 -0
- package/shared/composables/useExternalWidgets.ts +7 -4
- package/shared/composables/useMenuExpanded.ts +15 -1
- package/shared/composables/useTableSelection.ts +151 -0
- package/shared/composables/useTableSort.ts +4 -4
- package/shared/modules/assets-manager/components/assets-manager/assets-manager.vue +6 -3
- package/shared/pages/LoginPage/components/login/Login.vue +4 -1
- package/shared/utilities/colorUtils.ts +5 -12
- package/ui/components/atoms/vc-banner/vc-banner.vue +4 -1
- package/ui/components/atoms/vc-button/vc-button.vue +2 -25
- package/ui/components/atoms/vc-container/vc-container.vue +12 -3
- package/ui/components/atoms/vc-icon/vc-icon.vue +0 -10
- package/ui/components/atoms/vc-icon/vc-lucide-icon.vue +5 -2
- package/ui/components/atoms/vc-image/vc-image.vue +4 -1
- package/ui/components/atoms/vc-link/vc-link.vue +59 -54
- package/ui/components/atoms/vc-loading/vc-loading.vue +4 -0
- package/ui/components/atoms/vc-status/vc-status.vue +0 -5
- package/ui/components/atoms/vc-status-icon/vc-status-icon.vue +4 -4
- package/ui/components/atoms/vc-tooltip/vc-tooltip.vue +8 -1
- package/ui/components/atoms/vc-video/vc-video.vue +4 -2
- package/ui/components/atoms/vc-widget/vc-widget.vue +4 -1
- package/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue +7 -2
- package/ui/components/molecules/vc-input/vc-input.vue +0 -1
- package/ui/components/molecules/vc-pagination/vc-pagination.vue +6 -1
- package/ui/components/molecules/vc-rating/vc-rating.vue +1 -1
- package/ui/components/molecules/vc-textarea/vc-textarea.vue +1 -1
- package/ui/components/molecules/vc-toast/vc-toast.vue +11 -1
- package/ui/components/organisms/vc-app/vc-app.vue +22 -3
- package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue +4 -1
- package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue +14 -14
- package/ui/components/organisms/vc-blade/vc-blade.vue +3 -1
- package/ui/components/organisms/vc-login-form/vc-login-form.vue +3 -1
- package/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +34 -2
- package/ui/components/organisms/vc-table/composables/useTableActions.ts +7 -10
- package/ui/components/organisms/vc-table/composables/useTableColumnResize.ts +4 -1
- package/ui/components/organisms/vc-table/composables/useTableRowReorder.ts +5 -2
- package/ui/components/organisms/vc-table/composables/useTableSelection.ts +26 -18
- package/ui/components/organisms/vc-table/composables/useTableState.ts +4 -1
- package/core/services/global-search-service.ts +0 -36
- package/dist/core/services/global-search-service.d.ts +0 -10
- package/dist/core/services/global-search-service.d.ts.map +0 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { inject, ref, computed, watch, onUnmounted, type Ref, type ComputedRef } from "vue";
|
|
2
|
+
import { mergeWith, isPlainObject } from "lodash-es";
|
|
3
|
+
import { AiAgentServiceKey, BladeInstanceKey } from "../../../../injection-keys";
|
|
4
|
+
import type { IAiAgentServiceInternal } from "../services/ai-agent-service";
|
|
5
|
+
import type {
|
|
6
|
+
ISuggestion,
|
|
7
|
+
UseAiAgentContextOptions,
|
|
8
|
+
UseAiAgentContextReturn,
|
|
9
|
+
IPreviewChangesPayload,
|
|
10
|
+
AiAgentContextType,
|
|
11
|
+
} from "../types";
|
|
12
|
+
import { createLogger } from "../../../utilities";
|
|
13
|
+
|
|
14
|
+
const logger = createLogger("use-ai-agent-context");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom merge function for AI agent preview changes.
|
|
18
|
+
* Handles sparse arrays where null means "skip this index".
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Nested objects - deep merge
|
|
22
|
+
* deepMergeChanges({seo: {title: "Old", desc: "Desc"}}, {seo: {title: "New"}})
|
|
23
|
+
* // → {seo: {title: "New", desc: "Desc"}}
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Sparse arrays - null skips index
|
|
27
|
+
* deepMergeChanges({items: [{a:1}, {b:2}]}, {items: [null, {c:3}]})
|
|
28
|
+
* // → {items: [{a:1}, {b:2, c:3}]}
|
|
29
|
+
*/
|
|
30
|
+
function deepMergeChanges<T extends object>(target: T, source: object): T {
|
|
31
|
+
const customizer = (targetVal: unknown, sourceVal: unknown): unknown => {
|
|
32
|
+
// If source value is null/undefined, keep target value (skip this index/field)
|
|
33
|
+
if (sourceVal === null || sourceVal === undefined) {
|
|
34
|
+
return targetVal;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// If both are arrays, merge by index with sparse array support
|
|
38
|
+
if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {
|
|
39
|
+
const result = [...targetVal];
|
|
40
|
+
sourceVal.forEach((item, index) => {
|
|
41
|
+
if (item !== null && item !== undefined) {
|
|
42
|
+
if (isPlainObject(item) && isPlainObject(result[index])) {
|
|
43
|
+
// Recursively merge objects in array
|
|
44
|
+
result[index] = deepMergeChanges(result[index] as object, item);
|
|
45
|
+
} else {
|
|
46
|
+
// Replace primitive or non-object values
|
|
47
|
+
result[index] = item;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// null/undefined in source array = skip this index
|
|
51
|
+
});
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// For objects, let mergeWith handle recursively with this customizer
|
|
56
|
+
return undefined;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return mergeWith({}, target, source, customizer);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks if the ref value is an array
|
|
64
|
+
*/
|
|
65
|
+
function isArrayRef<T>(dataRef: Ref<T> | Ref<T[]>): dataRef is Ref<T[]> {
|
|
66
|
+
return Array.isArray(dataRef.value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Normalizes any ref value to an array for sending to the AI agent
|
|
71
|
+
*/
|
|
72
|
+
function normalizeToArray<T>(value: T | T[]): T[] {
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
return value != null ? [value] : [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Gets the target object for applying changes based on ref type
|
|
81
|
+
*/
|
|
82
|
+
function getTargetForChanges<T>(dataRef: Ref<T> | Ref<T[]>): T | undefined {
|
|
83
|
+
if (isArrayRef(dataRef)) {
|
|
84
|
+
// Array ref - return first item
|
|
85
|
+
return dataRef.value[0];
|
|
86
|
+
}
|
|
87
|
+
// Single object ref - return the object directly
|
|
88
|
+
return dataRef.value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Composable for binding blade data to AI agent context.
|
|
93
|
+
*
|
|
94
|
+
* This composable automatically:
|
|
95
|
+
* - Sends data updates to the AI agent when dataRef changes
|
|
96
|
+
* - Normalizes single objects to arrays for the AI agent
|
|
97
|
+
* - Handles PREVIEW_CHANGES messages to update form data
|
|
98
|
+
* - Provides preview state for visual feedback
|
|
99
|
+
*
|
|
100
|
+
* @example List blade (array of selected items)
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const { selectedItems } = useTableSelection<Offer>();
|
|
103
|
+
*
|
|
104
|
+
* useAiAgentContext({ dataRef: selectedItems });
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example Details blade (single object - automatically wrapped in array)
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const offer = ref<Offer>({});
|
|
110
|
+
*
|
|
111
|
+
* // Just pass the ref directly - no need to wrap in array!
|
|
112
|
+
* const { previewState } = useAiAgentContext({ dataRef: offer });
|
|
113
|
+
*
|
|
114
|
+
* // Use previewState.isActive to show preview indicator
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @example With custom suggestions
|
|
118
|
+
* ```typescript
|
|
119
|
+
* useAiAgentContext({
|
|
120
|
+
* dataRef: offer,
|
|
121
|
+
* suggestions: [
|
|
122
|
+
* {
|
|
123
|
+
* id: "translate",
|
|
124
|
+
* title: "Translate description",
|
|
125
|
+
* icon: "translation",
|
|
126
|
+
* iconColor: "#FF4A4A",
|
|
127
|
+
* prompt: "Translate the offer description to English"
|
|
128
|
+
* },
|
|
129
|
+
* ],
|
|
130
|
+
* });
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export function useAiAgentContext<T extends { id: string; objectType: string; name: string; [key: string]: unknown }>(
|
|
134
|
+
options: UseAiAgentContextOptions<T>,
|
|
135
|
+
): UseAiAgentContextReturn {
|
|
136
|
+
const { dataRef, suggestions } = options;
|
|
137
|
+
|
|
138
|
+
// Try to get the service (may not be available if plugin not installed)
|
|
139
|
+
const service = inject(AiAgentServiceKey) as IAiAgentServiceInternal | undefined;
|
|
140
|
+
|
|
141
|
+
// Get current blade instance to identify which blade this context belongs to
|
|
142
|
+
const bladeInstance = inject(BladeInstanceKey, null);
|
|
143
|
+
const bladeId = computed(() => bladeInstance?.value?.id);
|
|
144
|
+
|
|
145
|
+
// Preview state
|
|
146
|
+
const isPreviewActive = ref(false);
|
|
147
|
+
const changedFieldsList = ref<string[]>([]);
|
|
148
|
+
|
|
149
|
+
// If service is not available, return dummy preview state
|
|
150
|
+
if (!service) {
|
|
151
|
+
logger.debug("AiAgentService not available, context binding disabled");
|
|
152
|
+
return {
|
|
153
|
+
previewState: {
|
|
154
|
+
isActive: computed(() => false),
|
|
155
|
+
changedFields: computed(() => []),
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Determine context type based on dataRef type
|
|
161
|
+
// Array ref = list blade (multiple selected items)
|
|
162
|
+
// Single object ref = details blade (single item being edited)
|
|
163
|
+
const detectedContextType: AiAgentContextType = isArrayRef(dataRef) ? "list" : "details";
|
|
164
|
+
|
|
165
|
+
// Set context data in service (items, contextType, and suggestions)
|
|
166
|
+
// Always sends an array to the service, normalizing single objects
|
|
167
|
+
// Context is bound to specific blade ID
|
|
168
|
+
const updateContextData = () => {
|
|
169
|
+
const items = normalizeToArray(dataRef.value).map((item) => ({
|
|
170
|
+
id: item.id,
|
|
171
|
+
objectType: item.objectType,
|
|
172
|
+
name: item.name,
|
|
173
|
+
}));
|
|
174
|
+
service._setContextData(items, detectedContextType, suggestions, bladeId.value);
|
|
175
|
+
console.log(
|
|
176
|
+
`[USE-AI-AGENT-CONTEXT] Context updated: ${items.length} items, type: ${detectedContextType}, blade: ${bladeId.value}`,
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Log that the handler is being registered
|
|
181
|
+
console.log("[USE-AI-AGENT-CONTEXT] Registering PREVIEW_CHANGES handler for blade:", bladeId.value);
|
|
182
|
+
|
|
183
|
+
// Watch dataRef for changes and update context
|
|
184
|
+
const stopWatch = watch(
|
|
185
|
+
dataRef,
|
|
186
|
+
() => {
|
|
187
|
+
updateContextData();
|
|
188
|
+
// Clear preview state when data changes from outside
|
|
189
|
+
if (isPreviewActive.value) {
|
|
190
|
+
isPreviewActive.value = false;
|
|
191
|
+
changedFieldsList.value = [];
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{ deep: true, immediate: true },
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Handle PREVIEW_CHANGES messages
|
|
198
|
+
// Applies changes to the target object (single ref or first item in array)
|
|
199
|
+
const unsubscribe = service._onPreviewChanges((payload: IPreviewChangesPayload) => {
|
|
200
|
+
console.log("[USE-AI-AGENT-CONTEXT] PREVIEW_CHANGES handler called", {
|
|
201
|
+
bladeId: bladeId.value,
|
|
202
|
+
payloadKeys: Object.keys(payload.data || {}),
|
|
203
|
+
changedFields: payload.changedFields,
|
|
204
|
+
hasDataRef: !!dataRef,
|
|
205
|
+
dataRefValue: dataRef?.value ? "present" : "empty",
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const target = getTargetForChanges(dataRef);
|
|
209
|
+
if (!target) {
|
|
210
|
+
console.warn("[USE-AI-AGENT-CONTEXT] Cannot apply preview changes: no data in dataRef", {
|
|
211
|
+
bladeId: bladeId.value,
|
|
212
|
+
dataRefValue: dataRef?.value,
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const targetObj = target as Record<string, unknown>;
|
|
218
|
+
const updatedData = payload.data;
|
|
219
|
+
|
|
220
|
+
console.log("[USE-AI-AGENT-CONTEXT] Applying preview changes", {
|
|
221
|
+
bladeId: bladeId.value,
|
|
222
|
+
targetObjKeys: Object.keys(targetObj),
|
|
223
|
+
updatedDataKeys: Object.keys(updatedData),
|
|
224
|
+
updatedDataPreview: JSON.stringify(updatedData).substring(0, 500),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Deep merge changes into target object
|
|
228
|
+
// Supports:
|
|
229
|
+
// - Nested objects: {"seo": {"metaTitle": "New"}} merges into existing seo
|
|
230
|
+
// - Sparse arrays: [null, {"price": 50}] updates index 1 only (null = skip)
|
|
231
|
+
const mergedResult = deepMergeChanges(targetObj, updatedData);
|
|
232
|
+
|
|
233
|
+
// Copy merged result back to target object (to trigger Vue reactivity)
|
|
234
|
+
Object.keys(mergedResult).forEach((key) => {
|
|
235
|
+
targetObj[key] = mergedResult[key as keyof typeof mergedResult];
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
console.log("[USE-AI-AGENT-CONTEXT] Deep merge completed", {
|
|
239
|
+
bladeId: bladeId.value,
|
|
240
|
+
resultPreview: JSON.stringify(targetObj).substring(0, 500),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Update preview state
|
|
244
|
+
isPreviewActive.value = true;
|
|
245
|
+
changedFieldsList.value = payload.changedFields || Object.keys(updatedData);
|
|
246
|
+
|
|
247
|
+
console.log("[USE-AI-AGENT-CONTEXT] Preview changes applied successfully", {
|
|
248
|
+
bladeId: bladeId.value,
|
|
249
|
+
changedFields: changedFieldsList.value,
|
|
250
|
+
isPreviewActive: isPreviewActive.value,
|
|
251
|
+
targetObjAfter: JSON.stringify(targetObj).substring(0, 500),
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Cleanup on unmount
|
|
256
|
+
onUnmounted(() => {
|
|
257
|
+
stopWatch();
|
|
258
|
+
unsubscribe();
|
|
259
|
+
// Clear context for this specific blade when component unmounts
|
|
260
|
+
service._setContextData([], "list", undefined, bladeId.value);
|
|
261
|
+
logger.debug(`Context cleared on unmount for blade: ${bladeId.value}`);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
previewState: {
|
|
266
|
+
isActive: computed(() => isPreviewActive.value),
|
|
267
|
+
changedFields: computed(() => changedFieldsList.value),
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Clears the preview state manually.
|
|
274
|
+
* Useful when user saves or discards changes.
|
|
275
|
+
*/
|
|
276
|
+
export function clearPreviewState(previewState: UseAiAgentContextReturn["previewState"]): void {
|
|
277
|
+
// This is a helper for external clearing if needed
|
|
278
|
+
// The actual state is cleared when dataRef changes
|
|
279
|
+
logger.debug("Preview state cleared manually");
|
|
280
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { IAiAgentConfig, ISuggestion } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default configuration for AI agent panel
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_AI_AGENT_CONFIG: IAiAgentConfig = {
|
|
7
|
+
url: "",
|
|
8
|
+
title: "Virto OZ",
|
|
9
|
+
width: 350,
|
|
10
|
+
expandedWidth: 500,
|
|
11
|
+
allowedOrigins: ["*"],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Environment variable name for AI agent URL
|
|
16
|
+
*/
|
|
17
|
+
export const AI_AGENT_URL_ENV_KEY = "APP_AI_AGENT_URL";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Message types for Shell -> Chat communication
|
|
21
|
+
*/
|
|
22
|
+
export const SHELL_TO_CHAT_MESSAGE_TYPES = {
|
|
23
|
+
INIT_CONTEXT: "INIT_CONTEXT",
|
|
24
|
+
UPDATE_CONTEXT: "UPDATE_CONTEXT",
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Message types for Chat -> Shell communication
|
|
29
|
+
*/
|
|
30
|
+
export const CHAT_TO_SHELL_MESSAGE_TYPES = {
|
|
31
|
+
CHAT_READY: "CHAT_READY",
|
|
32
|
+
NAVIGATE_TO_APP: "NAVIGATE_TO_APP",
|
|
33
|
+
EXPAND_IN_CHAT: "EXPAND_IN_CHAT",
|
|
34
|
+
RELOAD_BLADE: "RELOAD_BLADE",
|
|
35
|
+
PREVIEW_CHANGES: "PREVIEW_CHANGES",
|
|
36
|
+
APPLY_CHANGES: "APPLY_CHANGES",
|
|
37
|
+
DOWNLOAD_FILE: "DOWNLOAD_FILE",
|
|
38
|
+
SHOW_MORE: "SHOW_MORE",
|
|
39
|
+
CHAT_ERROR: "CHAT_ERROR",
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default suggestions when no custom suggestions provided
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_SUGGESTIONS: ISuggestion[] = [
|
|
46
|
+
{
|
|
47
|
+
id: "generate-report",
|
|
48
|
+
title: "Generate a report",
|
|
49
|
+
icon: "analytics-01",
|
|
50
|
+
iconColor: "#57AB79",
|
|
51
|
+
prompt: "Generate a report",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "find-sales",
|
|
55
|
+
title: "Find sales statistics",
|
|
56
|
+
icon: "progress-02",
|
|
57
|
+
iconColor: "#FFBA35",
|
|
58
|
+
prompt: "Find sales statistics",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "translate",
|
|
62
|
+
title: "Translate description",
|
|
63
|
+
icon: "translation",
|
|
64
|
+
iconColor: "#FF4A4A",
|
|
65
|
+
prompt: "Translate description",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "top-sales",
|
|
69
|
+
title: "Find top sales report",
|
|
70
|
+
icon: "analytics-up",
|
|
71
|
+
iconColor: "#319ED4",
|
|
72
|
+
prompt: "Find top sales report",
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Default toolbar button ID for AI agent
|
|
78
|
+
*/
|
|
79
|
+
export const AI_AGENT_TOOLBAR_BUTTON_ID = "ai-agent-toggle";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Default toolbar button icon
|
|
83
|
+
*/
|
|
84
|
+
export const AI_AGENT_TOOLBAR_BUTTON_ICON = "lucide-sparkles";
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Default toolbar button title
|
|
88
|
+
*/
|
|
89
|
+
export const AI_AGENT_TOOLBAR_BUTTON_TITLE = "AI Assistant";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { App } from "vue";
|
|
2
|
+
import type { IAiAgentConfig } from "./types";
|
|
3
|
+
import { DEFAULT_AI_AGENT_CONFIG, AI_AGENT_URL_ENV_KEY } from "./constants";
|
|
4
|
+
import { createLogger } from "../../utilities";
|
|
5
|
+
|
|
6
|
+
const logger = createLogger("ai-agent-plugin");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Options for the AI Agent Plugin
|
|
10
|
+
*/
|
|
11
|
+
export interface AiAgentPluginOptions {
|
|
12
|
+
/**
|
|
13
|
+
* AI Agent configuration.
|
|
14
|
+
* URL can also be set via APP_AI_AGENT_URL environment variable.
|
|
15
|
+
*/
|
|
16
|
+
config?: Partial<IAiAgentConfig>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether to add the AI button to all blade toolbars automatically.
|
|
20
|
+
* Default: true
|
|
21
|
+
*/
|
|
22
|
+
addGlobalToolbarButton?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Vue plugin for AI Agent integration.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* import { createApp } from "vue";
|
|
31
|
+
* import { aiAgentPlugin } from "@vc-shell/framework";
|
|
32
|
+
*
|
|
33
|
+
* const app = createApp(App);
|
|
34
|
+
*
|
|
35
|
+
* // Install with options
|
|
36
|
+
* app.use(aiAgentPlugin, {
|
|
37
|
+
* config: {
|
|
38
|
+
* title: "AI Assistant",
|
|
39
|
+
* width: 400,
|
|
40
|
+
* },
|
|
41
|
+
* addGlobalToolbarButton: true,
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const aiAgentPlugin = {
|
|
46
|
+
install(app: App, options: AiAgentPluginOptions = {}) {
|
|
47
|
+
const { config = {}, addGlobalToolbarButton = true } = options;
|
|
48
|
+
|
|
49
|
+
// Get URL from environment variable if not provided
|
|
50
|
+
let url = config.url || "";
|
|
51
|
+
if (!url && typeof import.meta !== "undefined" && import.meta.env) {
|
|
52
|
+
url = import.meta.env[AI_AGENT_URL_ENV_KEY] || "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Skip installation if no URL configured
|
|
56
|
+
if (!url) {
|
|
57
|
+
logger.info("AI Agent plugin skipped: no URL configured. Set APP_AI_AGENT_URL env variable or pass config.url option.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Merge config with defaults
|
|
62
|
+
const finalConfig: IAiAgentConfig = {
|
|
63
|
+
...DEFAULT_AI_AGENT_CONFIG,
|
|
64
|
+
...config,
|
|
65
|
+
url,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Store config in app global properties for access during service creation
|
|
69
|
+
app.config.globalProperties.$aiAgentConfig = finalConfig;
|
|
70
|
+
app.provide("aiAgentConfig", finalConfig);
|
|
71
|
+
app.provide("aiAgentAddGlobalToolbarButton", addGlobalToolbarButton);
|
|
72
|
+
|
|
73
|
+
logger.info(`AI Agent plugin installed. URL: ${url}, addGlobalToolbarButton: ${addGlobalToolbarButton}`);
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Re-export all types
|
|
78
|
+
export * from "./types";
|
|
79
|
+
export * from "./constants";
|
|
80
|
+
|
|
81
|
+
// Re-export composables
|
|
82
|
+
export { useAiAgent, provideAiAgentService, createAiAgentToolbarButton } from "./composables/useAiAgent";
|
|
83
|
+
export type { UseAiAgentReturn, ProvideAiAgentServiceOptions } from "./composables/useAiAgent";
|
|
84
|
+
|
|
85
|
+
export { useAiAgentContext, clearPreviewState } from "./composables/useAiAgentContext";
|
|
86
|
+
|
|
87
|
+
// Re-export components
|
|
88
|
+
export { VcAiAgentPanel } from "./components";
|
|
89
|
+
|
|
90
|
+
// Re-export service types
|
|
91
|
+
export type { IAiAgentServiceInternal, CreateAiAgentServiceOptions } from "./services/ai-agent-service";
|