@vc-shell/framework 1.2.2 → 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,598 @@
|
|
|
1
|
+
import { computed, ref, shallowRef, watch, type ShallowRef } from "vue";
|
|
2
|
+
import { cloneDeep } from "lodash-es";
|
|
3
|
+
import { createLogger } from "../../../utilities";
|
|
4
|
+
import {
|
|
5
|
+
IAiAgentService,
|
|
6
|
+
IAiAgentConfig,
|
|
7
|
+
IAiAgentContext,
|
|
8
|
+
IAiAgentMessage,
|
|
9
|
+
AiAgentPanelState,
|
|
10
|
+
AiAgentMessageType,
|
|
11
|
+
AiAgentContextType,
|
|
12
|
+
IAiAgentBladeContext,
|
|
13
|
+
IAiAgentUserContext,
|
|
14
|
+
IInitContextPayload,
|
|
15
|
+
IUpdateContextPayload,
|
|
16
|
+
IChatBladeContext,
|
|
17
|
+
INavigateToAppPayload,
|
|
18
|
+
IApplyChangesPayload,
|
|
19
|
+
IChatErrorPayload,
|
|
20
|
+
IPreviewChangesPayload,
|
|
21
|
+
IDownloadFilePayload,
|
|
22
|
+
ISuggestion,
|
|
23
|
+
} from "../types";
|
|
24
|
+
import { DEFAULT_AI_AGENT_CONFIG } from "../constants";
|
|
25
|
+
|
|
26
|
+
const logger = createLogger("ai-agent-service");
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Options for creating the AI agent service
|
|
30
|
+
*/
|
|
31
|
+
export interface CreateAiAgentServiceOptions {
|
|
32
|
+
/** Function to get current user information */
|
|
33
|
+
userGetter: () => IAiAgentUserContext | undefined;
|
|
34
|
+
/** Function to get current blade context */
|
|
35
|
+
bladeGetter: () => IAiAgentBladeContext | null;
|
|
36
|
+
/** Function to get user locale */
|
|
37
|
+
localeGetter: () => string;
|
|
38
|
+
/** Function to get access token (handles automatic refresh) */
|
|
39
|
+
tokenGetter?: () => Promise<string | null>;
|
|
40
|
+
/** Function to navigate to blade */
|
|
41
|
+
navigateToBlade?: (bladeName: string, param?: string, options?: Record<string, unknown>) => void;
|
|
42
|
+
/** Function to reload current blade */
|
|
43
|
+
reloadBlade?: () => void;
|
|
44
|
+
/** Initial configuration */
|
|
45
|
+
initialConfig?: Partial<IAiAgentConfig>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Extended AI Agent Service with internal methods for component use
|
|
50
|
+
*/
|
|
51
|
+
export interface IAiAgentServiceInternal extends IAiAgentService {
|
|
52
|
+
/** Reference to the iframe element (set by component) */
|
|
53
|
+
iframeRef: ShallowRef<HTMLIFrameElement | null>;
|
|
54
|
+
/** Set the iframe reference (called by component) */
|
|
55
|
+
_setIframeRef: (iframe: HTMLIFrameElement | null) => void;
|
|
56
|
+
/** Handle incoming postMessage events (called by component) */
|
|
57
|
+
_handleIncomingMessage: (event: MessageEvent) => void;
|
|
58
|
+
/** Start listening for postMessage events */
|
|
59
|
+
_startListening: () => void;
|
|
60
|
+
/** Stop listening for postMessage events */
|
|
61
|
+
_stopListening: () => void;
|
|
62
|
+
/** Update context data for a blade (called by useAiAgentContext) */
|
|
63
|
+
_setContextData: (
|
|
64
|
+
items: Record<string, unknown>[],
|
|
65
|
+
type: AiAgentContextType,
|
|
66
|
+
suggestions?: ISuggestion[],
|
|
67
|
+
bladeId?: string,
|
|
68
|
+
) => void;
|
|
69
|
+
/** Register preview changes handler */
|
|
70
|
+
_onPreviewChanges: (handler: (payload: IPreviewChangesPayload) => void) => () => void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Convert internal blade context to chatbot format
|
|
75
|
+
*/
|
|
76
|
+
function toBladeContext(blade: IAiAgentBladeContext | null): IChatBladeContext {
|
|
77
|
+
if (!blade) {
|
|
78
|
+
return { id: "unknown", name: "unknown", title: "Unknown" };
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
id: blade.id,
|
|
82
|
+
name: blade.name,
|
|
83
|
+
title: blade.title || blade.name,
|
|
84
|
+
param: blade.param,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Decode organization_id from JWT if present.
|
|
90
|
+
* Used as fallback when tenantId is not configured in app options.
|
|
91
|
+
*
|
|
92
|
+
* @deprecated Prefer configuring tenantId via VirtoShellFramework aiAgent options.
|
|
93
|
+
* organization_id from JWT identifies a specific organization within the platform,
|
|
94
|
+
* while tenantId should identify the platform installation itself.
|
|
95
|
+
*/
|
|
96
|
+
function decodeOrganizationIdFromJwt(token?: string | null): string | undefined {
|
|
97
|
+
if (!token) return undefined;
|
|
98
|
+
try {
|
|
99
|
+
const parts = token.split(".");
|
|
100
|
+
if (parts.length < 2) return undefined;
|
|
101
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
102
|
+
const json = atob(base64);
|
|
103
|
+
const payload = JSON.parse(json);
|
|
104
|
+
return payload.organization_id;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logger.debug("decodeOrganizationIdFromJwt_failed", error);
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Download a file from base64 content
|
|
113
|
+
*/
|
|
114
|
+
function downloadFile(payload: IDownloadFilePayload): void {
|
|
115
|
+
const { filename, contentType, content } = payload;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const byteCharacters = atob(content);
|
|
119
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
120
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
121
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
122
|
+
}
|
|
123
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
124
|
+
const blob = new Blob([byteArray], { type: contentType });
|
|
125
|
+
|
|
126
|
+
const url = URL.createObjectURL(blob);
|
|
127
|
+
const link = document.createElement("a");
|
|
128
|
+
link.href = url;
|
|
129
|
+
link.download = filename;
|
|
130
|
+
document.body.appendChild(link);
|
|
131
|
+
link.click();
|
|
132
|
+
document.body.removeChild(link);
|
|
133
|
+
URL.revokeObjectURL(url);
|
|
134
|
+
|
|
135
|
+
logger.debug(`File downloaded: ${filename}`);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
logger.error("Failed to download file:", error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates an AI agent service for managing the AI panel state and communication.
|
|
143
|
+
*/
|
|
144
|
+
export function createAiAgentService(options: CreateAiAgentServiceOptions): IAiAgentServiceInternal {
|
|
145
|
+
const { userGetter, bladeGetter, localeGetter, tokenGetter, navigateToBlade, reloadBlade, initialConfig } = options;
|
|
146
|
+
|
|
147
|
+
// State
|
|
148
|
+
const panelState = ref<AiAgentPanelState>("closed");
|
|
149
|
+
const config = ref<IAiAgentConfig>({ ...DEFAULT_AI_AGENT_CONFIG, ...initialConfig });
|
|
150
|
+
const iframeRef: ShallowRef<HTMLIFrameElement | null> = shallowRef(null);
|
|
151
|
+
const isInitialized = ref(false);
|
|
152
|
+
const isListenerRegistered = ref(false);
|
|
153
|
+
const pendingInitContext = ref(false);
|
|
154
|
+
|
|
155
|
+
// Context data from useAiAgentContext - stored per blade
|
|
156
|
+
// Key is blade id, value is context for that blade
|
|
157
|
+
const bladeContexts = ref<
|
|
158
|
+
Map<
|
|
159
|
+
string,
|
|
160
|
+
{
|
|
161
|
+
items: Record<string, unknown>[];
|
|
162
|
+
type: AiAgentContextType;
|
|
163
|
+
suggestions?: ISuggestion[];
|
|
164
|
+
}
|
|
165
|
+
>
|
|
166
|
+
>(new Map());
|
|
167
|
+
|
|
168
|
+
// Get context for the active blade (last in the navigation stack)
|
|
169
|
+
const getActiveBladeContext = () => {
|
|
170
|
+
const activeBlade = bladeGetter();
|
|
171
|
+
if (!activeBlade) return { items: [], type: "list" as AiAgentContextType, suggestions: undefined };
|
|
172
|
+
|
|
173
|
+
const bladeContext = bladeContexts.value.get(activeBlade.id);
|
|
174
|
+
return bladeContext || { items: [], type: "list" as AiAgentContextType, suggestions: undefined };
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// Computed accessors for backward compatibility
|
|
178
|
+
const contextItems = computed(() => getActiveBladeContext().items);
|
|
179
|
+
const contextType = computed(() => getActiveBladeContext().type);
|
|
180
|
+
const contextSuggestions = computed(() => getActiveBladeContext().suggestions);
|
|
181
|
+
|
|
182
|
+
// Message handlers registry
|
|
183
|
+
const messageHandlers = new Set<(event: IAiAgentMessage) => void>();
|
|
184
|
+
const previewChangesHandlers = new Set<(payload: IPreviewChangesPayload) => void>();
|
|
185
|
+
|
|
186
|
+
// Computed: is panel open
|
|
187
|
+
const isOpen = computed(() => panelState.value !== "closed");
|
|
188
|
+
|
|
189
|
+
// Computed: is panel expanded
|
|
190
|
+
const isExpanded = computed(() => panelState.value === "expanded");
|
|
191
|
+
|
|
192
|
+
// Computed: total items count
|
|
193
|
+
const totalItemsCount = computed(() => contextItems.value.length);
|
|
194
|
+
|
|
195
|
+
// Computed: current context (reactive)
|
|
196
|
+
const context = computed<IAiAgentContext>(() => ({
|
|
197
|
+
user: userGetter(),
|
|
198
|
+
currentBlade: bladeGetter(),
|
|
199
|
+
items: contextItems.value,
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
}));
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Build INIT_CONTEXT payload for chatbot
|
|
205
|
+
* Uses cloneDeep to ensure data is fully serializable (postMessage cannot clone Vue proxies)
|
|
206
|
+
* Fetches fresh access token (with automatic refresh if expired)
|
|
207
|
+
*
|
|
208
|
+
* tenantId priority:
|
|
209
|
+
* 1. config.tenantId (configured by app via VirtoShellFramework options)
|
|
210
|
+
* 2. organization_id from JWT (deprecated fallback)
|
|
211
|
+
*/
|
|
212
|
+
const buildInitContextPayload = async (): Promise<IInitContextPayload> => {
|
|
213
|
+
const accessToken = tokenGetter ? ((await tokenGetter()) ?? undefined) : undefined;
|
|
214
|
+
// Use configured tenantId, fallback to JWT organization_id for backward compatibility
|
|
215
|
+
const tenantId = config.value.tenantId || decodeOrganizationIdFromJwt(accessToken);
|
|
216
|
+
return {
|
|
217
|
+
userId: userGetter()?.id || "",
|
|
218
|
+
locale: localeGetter(),
|
|
219
|
+
tenantId,
|
|
220
|
+
blade: toBladeContext(bladeGetter()),
|
|
221
|
+
contextType: contextType.value,
|
|
222
|
+
items: cloneDeep(contextItems.value),
|
|
223
|
+
suggestions: contextSuggestions.value ? cloneDeep(contextSuggestions.value) : undefined,
|
|
224
|
+
accessToken,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Build UPDATE_CONTEXT payload for chatbot
|
|
230
|
+
* Uses cloneDeep to ensure data is fully serializable (postMessage cannot clone Vue proxies)
|
|
231
|
+
* Fetches fresh access token (with automatic refresh if expired)
|
|
232
|
+
*
|
|
233
|
+
* tenantId priority:
|
|
234
|
+
* 1. config.tenantId (configured by app via VirtoShellFramework options)
|
|
235
|
+
* 2. organization_id from JWT (deprecated fallback)
|
|
236
|
+
*/
|
|
237
|
+
const buildUpdateContextPayload = async (): Promise<IUpdateContextPayload> => {
|
|
238
|
+
const accessToken = tokenGetter ? ((await tokenGetter()) ?? undefined) : undefined;
|
|
239
|
+
// Use configured tenantId, fallback to JWT organization_id for backward compatibility
|
|
240
|
+
const tenantId = config.value.tenantId || decodeOrganizationIdFromJwt(accessToken);
|
|
241
|
+
return {
|
|
242
|
+
tenantId,
|
|
243
|
+
blade: toBladeContext(bladeGetter()),
|
|
244
|
+
contextType: contextType.value,
|
|
245
|
+
items: cloneDeep(contextItems.value),
|
|
246
|
+
suggestions: contextSuggestions.value ? cloneDeep(contextSuggestions.value) : undefined,
|
|
247
|
+
locale: localeGetter(),
|
|
248
|
+
accessToken,
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Internal functions defined below, forward-declared here for watchers
|
|
253
|
+
let sendRawMessage: (message: { type: string; payload?: unknown }) => void;
|
|
254
|
+
let _handleIncomingMessage: (event: MessageEvent) => void;
|
|
255
|
+
|
|
256
|
+
// Watch for context changes and send UPDATE_CONTEXT to chatbot
|
|
257
|
+
watch(
|
|
258
|
+
() => ({
|
|
259
|
+
currentBlade: context.value.currentBlade,
|
|
260
|
+
items: context.value.items,
|
|
261
|
+
}),
|
|
262
|
+
async () => {
|
|
263
|
+
if (isOpen.value && isInitialized.value && iframeRef.value?.contentWindow) {
|
|
264
|
+
const payload = await buildUpdateContextPayload();
|
|
265
|
+
sendRawMessage({ type: "UPDATE_CONTEXT", payload });
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
{ deep: true },
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Watch for iframe ref changes - send pending INIT_CONTEXT when iframe becomes available
|
|
272
|
+
watch(iframeRef, async (iframe) => {
|
|
273
|
+
if (iframe?.contentWindow && pendingInitContext.value) {
|
|
274
|
+
logger.debug("Iframe became available, sending pending INIT_CONTEXT");
|
|
275
|
+
pendingInitContext.value = false;
|
|
276
|
+
isInitialized.value = true;
|
|
277
|
+
const payload = await buildInitContextPayload();
|
|
278
|
+
sendRawMessage({ type: "INIT_CONTEXT", payload });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Start listening for messages (called by component on mount)
|
|
284
|
+
*/
|
|
285
|
+
const _startListening = (): void => {
|
|
286
|
+
if (!isListenerRegistered.value) {
|
|
287
|
+
window.addEventListener("message", _handleIncomingMessage);
|
|
288
|
+
isListenerRegistered.value = true;
|
|
289
|
+
logger.debug("Message listener registered");
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Stop listening for messages (called by component on unmount)
|
|
295
|
+
*/
|
|
296
|
+
const _stopListening = (): void => {
|
|
297
|
+
if (isListenerRegistered.value) {
|
|
298
|
+
window.removeEventListener("message", _handleIncomingMessage);
|
|
299
|
+
isListenerRegistered.value = false;
|
|
300
|
+
logger.debug("Message listener unregistered");
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Open the AI panel
|
|
306
|
+
*/
|
|
307
|
+
const openPanel = (): void => {
|
|
308
|
+
if (panelState.value === "closed") {
|
|
309
|
+
panelState.value = "open";
|
|
310
|
+
logger.debug("Panel opened");
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Close the AI panel
|
|
316
|
+
*/
|
|
317
|
+
const closePanel = (): void => {
|
|
318
|
+
panelState.value = "closed";
|
|
319
|
+
// Reset initialized state since iframe will be destroyed (v-if in panel component)
|
|
320
|
+
// Next time panel opens, iframe will send CHAT_READY and we'll send INIT_CONTEXT again
|
|
321
|
+
isInitialized.value = false;
|
|
322
|
+
logger.debug("Panel closed, reset initialized state");
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Toggle panel open/close
|
|
327
|
+
*/
|
|
328
|
+
const togglePanel = (): void => {
|
|
329
|
+
if (panelState.value === "closed") {
|
|
330
|
+
openPanel();
|
|
331
|
+
} else {
|
|
332
|
+
closePanel();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Expand the panel to larger width
|
|
338
|
+
*/
|
|
339
|
+
const expandPanel = (): void => {
|
|
340
|
+
if (panelState.value === "open") {
|
|
341
|
+
panelState.value = "expanded";
|
|
342
|
+
logger.debug("Panel expanded");
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Collapse panel to normal width
|
|
348
|
+
*/
|
|
349
|
+
const collapsePanel = (): void => {
|
|
350
|
+
if (panelState.value === "expanded") {
|
|
351
|
+
panelState.value = "open";
|
|
352
|
+
logger.debug("Panel collapsed");
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Update configuration
|
|
358
|
+
*/
|
|
359
|
+
const setConfig = (newConfig: Partial<IAiAgentConfig>): void => {
|
|
360
|
+
config.value = { ...config.value, ...newConfig };
|
|
361
|
+
logger.debug("Config updated", newConfig);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Send raw message to iframe (chatbot protocol format)
|
|
366
|
+
* Payload data should already be unwrapped from Vue proxies via toRaw() in build*Payload functions
|
|
367
|
+
*/
|
|
368
|
+
sendRawMessage = (message: { type: string; payload?: unknown }): void => {
|
|
369
|
+
if (!iframeRef.value?.contentWindow) {
|
|
370
|
+
logger.warn("Cannot send message: iframe not available");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const targetOrigin = config.value.allowedOrigins?.[0] || "*";
|
|
375
|
+
iframeRef.value.contentWindow.postMessage(message, targetOrigin);
|
|
376
|
+
logger.debug(`Message sent: ${message.type}`, message.payload);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Send message to AI agent iframe (public API)
|
|
381
|
+
*/
|
|
382
|
+
const sendMessage = (type: AiAgentMessageType, payload: unknown): void => {
|
|
383
|
+
sendRawMessage({ type, payload });
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Register message handler, returns unsubscribe function
|
|
388
|
+
*/
|
|
389
|
+
const onMessage = (handler: (event: IAiAgentMessage) => void): (() => void) => {
|
|
390
|
+
messageHandlers.add(handler);
|
|
391
|
+
return () => {
|
|
392
|
+
messageHandlers.delete(handler);
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Set the iframe reference (called by component)
|
|
398
|
+
*/
|
|
399
|
+
const _setIframeRef = (iframe: HTMLIFrameElement | null): void => {
|
|
400
|
+
iframeRef.value = iframe;
|
|
401
|
+
logger.debug("Iframe ref set:", iframe ? "available" : "null");
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Set context data for a specific blade (called by useAiAgentContext)
|
|
406
|
+
* If bladeId is not provided, uses the current active blade
|
|
407
|
+
*/
|
|
408
|
+
const _setContextData = (
|
|
409
|
+
items: Record<string, unknown>[],
|
|
410
|
+
type: AiAgentContextType,
|
|
411
|
+
suggestions?: ISuggestion[],
|
|
412
|
+
bladeId?: string,
|
|
413
|
+
): void => {
|
|
414
|
+
const targetBladeId = bladeId || bladeGetter()?.id;
|
|
415
|
+
if (!targetBladeId) {
|
|
416
|
+
logger.warn("Cannot set context data: no blade id available");
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (items.length === 0 && !suggestions) {
|
|
421
|
+
// Remove context for this blade when cleared
|
|
422
|
+
bladeContexts.value.delete(targetBladeId);
|
|
423
|
+
logger.debug(`Context cleared for blade: ${targetBladeId}`);
|
|
424
|
+
} else {
|
|
425
|
+
// Set context for this blade
|
|
426
|
+
bladeContexts.value.set(targetBladeId, { items, type, suggestions });
|
|
427
|
+
logger.debug(`Context set for blade: ${targetBladeId}, items: ${items.length}, type: ${type}`);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Register preview changes handler
|
|
433
|
+
*/
|
|
434
|
+
const _onPreviewChanges = (handler: (payload: IPreviewChangesPayload) => void): (() => void) => {
|
|
435
|
+
previewChangesHandlers.add(handler);
|
|
436
|
+
return () => {
|
|
437
|
+
previewChangesHandlers.delete(handler);
|
|
438
|
+
};
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Handle incoming postMessage events (called by component)
|
|
443
|
+
*/
|
|
444
|
+
_handleIncomingMessage = (event: MessageEvent): void => {
|
|
445
|
+
// Log all incoming messages for debugging
|
|
446
|
+
if (event.data?.type) {
|
|
447
|
+
logger.debug("Raw message event received:", {
|
|
448
|
+
origin: event.origin,
|
|
449
|
+
type: event.data.type,
|
|
450
|
+
hasIframe: !!iframeRef.value,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Validate origin if configured
|
|
455
|
+
const allowedOrigins = config.value.allowedOrigins || ["*"];
|
|
456
|
+
if (!allowedOrigins.includes("*") && !allowedOrigins.includes(event.origin)) {
|
|
457
|
+
logger.warn(`Message from unauthorized origin: ${event.origin}`);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Validate message structure
|
|
462
|
+
const message = event.data;
|
|
463
|
+
if (!message?.type || typeof message.type !== "string") {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
logger.debug(`Message received: ${message.type}`, message.payload);
|
|
468
|
+
|
|
469
|
+
// Handle chatbot protocol messages
|
|
470
|
+
switch (message.type) {
|
|
471
|
+
case "CHAT_READY":
|
|
472
|
+
if (iframeRef.value?.contentWindow) {
|
|
473
|
+
isInitialized.value = true;
|
|
474
|
+
buildInitContextPayload().then((payload) => {
|
|
475
|
+
sendRawMessage({ type: "INIT_CONTEXT", payload });
|
|
476
|
+
logger.info("Chatbot ready, sent INIT_CONTEXT");
|
|
477
|
+
});
|
|
478
|
+
} else {
|
|
479
|
+
pendingInitContext.value = true;
|
|
480
|
+
logger.info("Chatbot ready, but iframe ref not available yet - pending INIT_CONTEXT");
|
|
481
|
+
}
|
|
482
|
+
break;
|
|
483
|
+
|
|
484
|
+
case "NAVIGATE_TO_APP": {
|
|
485
|
+
const navPayload = message.payload as INavigateToAppPayload;
|
|
486
|
+
if (navigateToBlade && navPayload?.bladeName) {
|
|
487
|
+
navigateToBlade(navPayload.bladeName, navPayload.param, navPayload.options);
|
|
488
|
+
logger.debug(`Navigation requested to: ${navPayload.bladeName}`);
|
|
489
|
+
}
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
case "RELOAD_BLADE":
|
|
494
|
+
if (reloadBlade) {
|
|
495
|
+
reloadBlade();
|
|
496
|
+
logger.debug("Blade reload requested");
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
|
|
500
|
+
case "PREVIEW_CHANGES": {
|
|
501
|
+
const previewPayload = message.payload as IPreviewChangesPayload;
|
|
502
|
+
console.log("[AI-AGENT-SERVICE] PREVIEW_CHANGES received", {
|
|
503
|
+
handlersCount: previewChangesHandlers.size,
|
|
504
|
+
payloadDataKeys: previewPayload?.data ? Object.keys(previewPayload.data) : [],
|
|
505
|
+
changedFields: previewPayload?.changedFields,
|
|
506
|
+
payloadPreview: JSON.stringify(previewPayload).substring(0, 500),
|
|
507
|
+
});
|
|
508
|
+
if (previewChangesHandlers.size === 0) {
|
|
509
|
+
console.warn("[AI-AGENT-SERVICE] No preview changes handlers registered!");
|
|
510
|
+
}
|
|
511
|
+
previewChangesHandlers.forEach((handler) => {
|
|
512
|
+
try {
|
|
513
|
+
console.log("[AI-AGENT-SERVICE] Calling preview changes handler");
|
|
514
|
+
handler(previewPayload);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
console.error("[AI-AGENT-SERVICE] Error in preview changes handler:", error);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
case "DOWNLOAD_FILE": {
|
|
523
|
+
const downloadPayload = message.payload as IDownloadFilePayload;
|
|
524
|
+
if (downloadPayload) {
|
|
525
|
+
downloadFile(downloadPayload);
|
|
526
|
+
}
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
case "APPLY_CHANGES": {
|
|
531
|
+
const changesPayload = message.payload as IApplyChangesPayload;
|
|
532
|
+
logger.debug("Apply changes requested:", changesPayload?.changes);
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
case "CHAT_ERROR": {
|
|
537
|
+
const errorPayload = message.payload as IChatErrorPayload;
|
|
538
|
+
logger.error(`Chatbot error [${errorPayload?.code}]: ${errorPayload?.message}`);
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
default:
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Notify all registered handlers
|
|
547
|
+
const normalizedMessage: IAiAgentMessage = {
|
|
548
|
+
type: message.type,
|
|
549
|
+
payload: message.payload,
|
|
550
|
+
timestamp: Date.now(),
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
messageHandlers.forEach((handler) => {
|
|
554
|
+
try {
|
|
555
|
+
handler(normalizedMessage);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
logger.error("Error in message handler:", error);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// Auto-register listener when service is created
|
|
563
|
+
_startListening();
|
|
564
|
+
logger.debug("AI Agent Service initialized, listener auto-registered");
|
|
565
|
+
|
|
566
|
+
return {
|
|
567
|
+
// State
|
|
568
|
+
panelState,
|
|
569
|
+
config,
|
|
570
|
+
context,
|
|
571
|
+
isOpen,
|
|
572
|
+
isExpanded,
|
|
573
|
+
totalItemsCount,
|
|
574
|
+
|
|
575
|
+
// Panel control
|
|
576
|
+
openPanel,
|
|
577
|
+
closePanel,
|
|
578
|
+
togglePanel,
|
|
579
|
+
expandPanel,
|
|
580
|
+
collapsePanel,
|
|
581
|
+
|
|
582
|
+
// Configuration
|
|
583
|
+
setConfig,
|
|
584
|
+
|
|
585
|
+
// Communication
|
|
586
|
+
sendMessage,
|
|
587
|
+
onMessage,
|
|
588
|
+
|
|
589
|
+
// Internal (for component use)
|
|
590
|
+
iframeRef,
|
|
591
|
+
_setIframeRef,
|
|
592
|
+
_handleIncomingMessage,
|
|
593
|
+
_startListening,
|
|
594
|
+
_stopListening,
|
|
595
|
+
_setContextData,
|
|
596
|
+
_onPreviewChanges,
|
|
597
|
+
};
|
|
598
|
+
}
|