@aipanel/ui 1.2.0-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.
Files changed (153) hide show
  1. package/es/AI-panel-widget/composables/use-inspector.d.ts +52 -0
  2. package/es/AI-panel-widget/composables/use-inspector.mjs +437 -0
  3. package/es/AI-panel-widget/composables/use-persist-state.d.ts +32 -0
  4. package/es/AI-panel-widget/composables/use-persist-state.mjs +83 -0
  5. package/es/AI-panel-widget/composables/use-selection.d.ts +19 -0
  6. package/es/AI-panel-widget/composables/use-selection.mjs +112 -0
  7. package/es/AI-panel-widget/composables/use-session.d.ts +16 -0
  8. package/es/AI-panel-widget/composables/use-session.mjs +47 -0
  9. package/es/AI-panel-widget/composables/use-split.d.ts +30 -0
  10. package/es/AI-panel-widget/composables/use-split.mjs +133 -0
  11. package/es/AI-panel-widget/composables/use-widget.d.ts +27 -0
  12. package/es/AI-panel-widget/composables/use-widget.mjs +78 -0
  13. package/es/AI-panel-widget/index.d.ts +5 -0
  14. package/es/AI-panel-widget/index.mjs +9 -0
  15. package/es/AI-panel-widget/src/assets/logo.svg +13 -0
  16. package/es/AI-panel-widget/src/components/AIPanelLogo-sfc.css +1 -0
  17. package/es/AI-panel-widget/src/components/AIPanelLogo.vue.d.ts +8 -0
  18. package/es/AI-panel-widget/src/components/AIPanelLogo.vue.mjs +40 -0
  19. package/es/AI-panel-widget/src/components/ChatPanel-sfc.css +1 -0
  20. package/es/AI-panel-widget/src/components/ChatPanel.vue.d.ts +179 -0
  21. package/es/AI-panel-widget/src/components/ChatPanel.vue.mjs +308 -0
  22. package/es/AI-panel-widget/src/components/FloatingBubble/FloatingBubble-sfc.css +1 -0
  23. package/es/AI-panel-widget/src/components/FloatingBubble/FloatingBubble.vue.d.ts +44 -0
  24. package/es/AI-panel-widget/src/components/FloatingBubble/FloatingBubble.vue.mjs +311 -0
  25. package/es/AI-panel-widget/src/components/FloatingBubble/index.d.ts +3 -0
  26. package/es/AI-panel-widget/src/components/FloatingBubble/index.mjs +5 -0
  27. package/es/AI-panel-widget/src/components/FloatingBubble/types.d.ts +24 -0
  28. package/es/AI-panel-widget/src/components/FloatingBubble/types.mjs +0 -0
  29. package/es/AI-panel-widget/src/components/Frame-sfc.css +1 -0
  30. package/es/AI-panel-widget/src/components/Frame.vue.d.ts +22 -0
  31. package/es/AI-panel-widget/src/components/Frame.vue.mjs +143 -0
  32. package/es/AI-panel-widget/src/components/Header-sfc.css +1 -0
  33. package/es/AI-panel-widget/src/components/Header.vue.d.ts +27 -0
  34. package/es/AI-panel-widget/src/components/Header.vue.mjs +683 -0
  35. package/es/AI-panel-widget/src/components/ResizeHandle-sfc.css +1 -0
  36. package/es/AI-panel-widget/src/components/ResizeHandle.vue.d.ts +22 -0
  37. package/es/AI-panel-widget/src/components/ResizeHandle.vue.mjs +79 -0
  38. package/es/AI-panel-widget/src/components/SelectHint-sfc.css +1 -0
  39. package/es/AI-panel-widget/src/components/SelectHint.vue.d.ts +3 -0
  40. package/es/AI-panel-widget/src/components/SelectHint.vue.mjs +40 -0
  41. package/es/AI-panel-widget/src/components/SelectedBubbles-sfc.css +1 -0
  42. package/es/AI-panel-widget/src/components/SelectedBubbles.vue.d.ts +3 -0
  43. package/es/AI-panel-widget/src/components/SelectedBubbles.vue.mjs +84 -0
  44. package/es/AI-panel-widget/src/components/SelectedNodes-sfc.css +1 -0
  45. package/es/AI-panel-widget/src/components/SelectedNodes.vue.d.ts +3 -0
  46. package/es/AI-panel-widget/src/components/SelectedNodes.vue.mjs +103 -0
  47. package/es/AI-panel-widget/src/components/SessionList-sfc.css +1 -0
  48. package/es/AI-panel-widget/src/components/SessionList.vue.d.ts +13 -0
  49. package/es/AI-panel-widget/src/components/SessionList.vue.mjs +222 -0
  50. package/es/AI-panel-widget/src/components/SplitTrigger-sfc.css +1 -0
  51. package/es/AI-panel-widget/src/components/SplitTrigger.vue.d.ts +18 -0
  52. package/es/AI-panel-widget/src/components/SplitTrigger.vue.mjs +112 -0
  53. package/es/AI-panel-widget/src/components/Trigger-sfc.css +1 -0
  54. package/es/AI-panel-widget/src/components/Trigger.vue.d.ts +28 -0
  55. package/es/AI-panel-widget/src/components/Trigger.vue.mjs +74 -0
  56. package/es/AI-panel-widget/src/context.d.ts +67 -0
  57. package/es/AI-panel-widget/src/context.mjs +16 -0
  58. package/es/AI-panel-widget/src/index-sfc.css +1 -0
  59. package/es/AI-panel-widget/src/index.vue.d.ts +69 -0
  60. package/es/AI-panel-widget/src/index.vue.mjs +782 -0
  61. package/es/AI-panel-widget/src/types.d.ts +2 -0
  62. package/es/AI-panel-widget/src/types.mjs +0 -0
  63. package/es/AI-panel-widget/style/index-pure.d.ts +0 -0
  64. package/es/AI-panel-widget/style/index-pure.mjs +0 -0
  65. package/es/AI-panel-widget/style/index.d.ts +0 -0
  66. package/es/AI-panel-widget/style/index.mjs +0 -0
  67. package/es/AI-panel-widget/style/less-pure.d.ts +0 -0
  68. package/es/AI-panel-widget/style/less-pure.mjs +0 -0
  69. package/es/AI-panel-widget/style/less.d.ts +0 -0
  70. package/es/AI-panel-widget/style/less.mjs +0 -0
  71. package/es/env.d.ts +15 -0
  72. package/es/index.d.ts +7 -0
  73. package/es/index.mjs +22 -0
  74. package/es/setup.d.ts +1 -0
  75. package/es/setup.mjs +4 -0
  76. package/lib/AI-panel-widget/composables/use-inspector.cjs +466 -0
  77. package/lib/AI-panel-widget/composables/use-inspector.d.ts +52 -0
  78. package/lib/AI-panel-widget/composables/use-persist-state.cjs +102 -0
  79. package/lib/AI-panel-widget/composables/use-persist-state.d.ts +32 -0
  80. package/lib/AI-panel-widget/composables/use-selection.cjs +131 -0
  81. package/lib/AI-panel-widget/composables/use-selection.d.ts +19 -0
  82. package/lib/AI-panel-widget/composables/use-session.cjs +66 -0
  83. package/lib/AI-panel-widget/composables/use-session.d.ts +16 -0
  84. package/lib/AI-panel-widget/composables/use-split.cjs +152 -0
  85. package/lib/AI-panel-widget/composables/use-split.d.ts +30 -0
  86. package/lib/AI-panel-widget/composables/use-widget.cjs +97 -0
  87. package/lib/AI-panel-widget/composables/use-widget.d.ts +27 -0
  88. package/lib/AI-panel-widget/index.cjs +39 -0
  89. package/lib/AI-panel-widget/index.d.ts +5 -0
  90. package/lib/AI-panel-widget/src/assets/logo.svg +13 -0
  91. package/lib/AI-panel-widget/src/components/AIPanelLogo-sfc.css +1 -0
  92. package/lib/AI-panel-widget/src/components/AIPanelLogo.vue.cjs +69 -0
  93. package/lib/AI-panel-widget/src/components/AIPanelLogo.vue.d.ts +8 -0
  94. package/lib/AI-panel-widget/src/components/ChatPanel-sfc.css +1 -0
  95. package/lib/AI-panel-widget/src/components/ChatPanel.vue.cjs +337 -0
  96. package/lib/AI-panel-widget/src/components/ChatPanel.vue.d.ts +179 -0
  97. package/lib/AI-panel-widget/src/components/FloatingBubble/FloatingBubble-sfc.css +1 -0
  98. package/lib/AI-panel-widget/src/components/FloatingBubble/FloatingBubble.vue.cjs +330 -0
  99. package/lib/AI-panel-widget/src/components/FloatingBubble/FloatingBubble.vue.d.ts +44 -0
  100. package/lib/AI-panel-widget/src/components/FloatingBubble/index.cjs +35 -0
  101. package/lib/AI-panel-widget/src/components/FloatingBubble/index.d.ts +3 -0
  102. package/lib/AI-panel-widget/src/components/FloatingBubble/types.cjs +15 -0
  103. package/lib/AI-panel-widget/src/components/FloatingBubble/types.d.ts +24 -0
  104. package/lib/AI-panel-widget/src/components/Frame-sfc.css +1 -0
  105. package/lib/AI-panel-widget/src/components/Frame.vue.cjs +162 -0
  106. package/lib/AI-panel-widget/src/components/Frame.vue.d.ts +22 -0
  107. package/lib/AI-panel-widget/src/components/Header-sfc.css +1 -0
  108. package/lib/AI-panel-widget/src/components/Header.vue.cjs +702 -0
  109. package/lib/AI-panel-widget/src/components/Header.vue.d.ts +27 -0
  110. package/lib/AI-panel-widget/src/components/ResizeHandle-sfc.css +1 -0
  111. package/lib/AI-panel-widget/src/components/ResizeHandle.vue.cjs +98 -0
  112. package/lib/AI-panel-widget/src/components/ResizeHandle.vue.d.ts +22 -0
  113. package/lib/AI-panel-widget/src/components/SelectHint-sfc.css +1 -0
  114. package/lib/AI-panel-widget/src/components/SelectHint.vue.cjs +59 -0
  115. package/lib/AI-panel-widget/src/components/SelectHint.vue.d.ts +3 -0
  116. package/lib/AI-panel-widget/src/components/SelectedBubbles-sfc.css +1 -0
  117. package/lib/AI-panel-widget/src/components/SelectedBubbles.vue.cjs +103 -0
  118. package/lib/AI-panel-widget/src/components/SelectedBubbles.vue.d.ts +3 -0
  119. package/lib/AI-panel-widget/src/components/SelectedNodes-sfc.css +1 -0
  120. package/lib/AI-panel-widget/src/components/SelectedNodes.vue.cjs +122 -0
  121. package/lib/AI-panel-widget/src/components/SelectedNodes.vue.d.ts +3 -0
  122. package/lib/AI-panel-widget/src/components/SessionList-sfc.css +1 -0
  123. package/lib/AI-panel-widget/src/components/SessionList.vue.cjs +241 -0
  124. package/lib/AI-panel-widget/src/components/SessionList.vue.d.ts +13 -0
  125. package/lib/AI-panel-widget/src/components/SplitTrigger-sfc.css +1 -0
  126. package/lib/AI-panel-widget/src/components/SplitTrigger.vue.cjs +131 -0
  127. package/lib/AI-panel-widget/src/components/SplitTrigger.vue.d.ts +18 -0
  128. package/lib/AI-panel-widget/src/components/Trigger-sfc.css +1 -0
  129. package/lib/AI-panel-widget/src/components/Trigger.vue.cjs +103 -0
  130. package/lib/AI-panel-widget/src/components/Trigger.vue.d.ts +28 -0
  131. package/lib/AI-panel-widget/src/context.cjs +35 -0
  132. package/lib/AI-panel-widget/src/context.d.ts +67 -0
  133. package/lib/AI-panel-widget/src/index-sfc.css +1 -0
  134. package/lib/AI-panel-widget/src/index.vue.cjs +811 -0
  135. package/lib/AI-panel-widget/src/index.vue.d.ts +69 -0
  136. package/lib/AI-panel-widget/src/types.cjs +15 -0
  137. package/lib/AI-panel-widget/src/types.d.ts +2 -0
  138. package/lib/AI-panel-widget/style/index-pure.cjs +0 -0
  139. package/lib/AI-panel-widget/style/index-pure.d.ts +0 -0
  140. package/lib/AI-panel-widget/style/index.cjs +0 -0
  141. package/lib/AI-panel-widget/style/index.d.ts +0 -0
  142. package/lib/AI-panel-widget/style/less-pure.cjs +0 -0
  143. package/lib/AI-panel-widget/style/less-pure.d.ts +0 -0
  144. package/lib/AI-panel-widget/style/less.cjs +0 -0
  145. package/lib/AI-panel-widget/style/less.d.ts +0 -0
  146. package/lib/env.d.ts +15 -0
  147. package/lib/index.cjs +42 -0
  148. package/lib/index.css +0 -0
  149. package/lib/index.d.ts +7 -0
  150. package/lib/index.less +0 -0
  151. package/lib/setup.cjs +23 -0
  152. package/lib/setup.d.ts +1 -0
  153. package/package.json +42 -0
@@ -0,0 +1,112 @@
1
+ import { computed } from "vue";
2
+ import { truncate } from "@aipanel/core";
3
+ function getElementKey(element, index) {
4
+ if (element.filePath && element.line) {
5
+ return `${element.filePath}:${element.line}:${element.column ?? 0}`;
6
+ }
7
+ return `${element.description}-${index}`;
8
+ }
9
+ function getBubbleFileText(element) {
10
+ const fileName = element.filePath?.split("/").pop() || "";
11
+ const lineInfo = element.line ? `:${element.line}${element.column ? `:${element.column}` : ""}` : "";
12
+ return `${fileName}${lineInfo}`;
13
+ }
14
+ function getPanelFileText(element) {
15
+ const fileName = element.filePath?.split("/").pop() || "\u672A\u77E5\u6587\u4EF6";
16
+ const lineInfo = element.line ? `:${element.line}${element.column ? `:${element.column}` : ""}` : "";
17
+ const textPreview = element.innerText?.trim() ? `${truncate(element.innerText.trim(), 30)} \xB7 ` : "";
18
+ return `${textPreview}${fileName}${lineInfo}`;
19
+ }
20
+ function useSelection(options) {
21
+ const bubbleVisible = computed(() => options.selectMode.value);
22
+ const selectedElementItems = computed(
23
+ () => (options.selectedElements.value || []).map(
24
+ (element, index) => ({
25
+ key: getElementKey(element, index),
26
+ description: element.description || "\u672A\u77E5\u5143\u7D20",
27
+ bubbleFileText: getBubbleFileText(element),
28
+ panelFileText: getPanelFileText(element),
29
+ element
30
+ })
31
+ )
32
+ );
33
+ const hasSelectedElements = computed(() => selectedElementItems.value.length > 0);
34
+ function handleToggleSelectMode() {
35
+ options.onToggleSelectMode(!options.selectMode.value);
36
+ }
37
+ function handleClickSelectedNode(item) {
38
+ const description = item.element.description;
39
+ if (!description) return;
40
+ let targetElement = null;
41
+ if (description.includes("#")) {
42
+ const idMatch = description.match(/#([^.[\s]+)/);
43
+ if (idMatch) {
44
+ targetElement = document.getElementById(idMatch[1]);
45
+ }
46
+ }
47
+ if (!targetElement && description.includes(".")) {
48
+ const classMatch = description.match(/^([a-z]+)\.([^[\s]+)/i);
49
+ if (classMatch) {
50
+ const tagName = classMatch[1];
51
+ const classes = classMatch[2].split(".").filter(Boolean);
52
+ const selector = `${tagName}.${classes.join(".")}`;
53
+ targetElement = document.querySelector(selector);
54
+ }
55
+ }
56
+ if (!targetElement) {
57
+ const tagMatch = description.match(/^([a-z]+)/i);
58
+ if (tagMatch) {
59
+ const simpleSelector = description.split(/[.[\s]/)[0];
60
+ targetElement = document.querySelector(simpleSelector);
61
+ }
62
+ }
63
+ if (targetElement) {
64
+ targetElement.scrollIntoView({ behavior: "smooth", block: "center" });
65
+ const highlightOverlay = document.createElement("div");
66
+ highlightOverlay.className = "aipanel-element-highlight-temp";
67
+ const widget = document.querySelector(".aipanel-widget");
68
+ let primary = "#3b82f6";
69
+ let primaryBg = "rgba(59, 130, 246, 0.1)";
70
+ if (widget) {
71
+ const style = getComputedStyle(widget);
72
+ primary = style.getPropertyValue("--ap-primary").trim() || primary;
73
+ primaryBg = style.getPropertyValue("--ap-primary-bg").trim() || primaryBg;
74
+ }
75
+ highlightOverlay.style.border = `2px solid ${primary}`;
76
+ highlightOverlay.style.background = primaryBg;
77
+ const rect = targetElement.getBoundingClientRect();
78
+ highlightOverlay.style.top = `${rect.top + window.scrollY}px`;
79
+ highlightOverlay.style.left = `${rect.left + window.scrollX}px`;
80
+ highlightOverlay.style.width = `${rect.width}px`;
81
+ highlightOverlay.style.height = `${rect.height}px`;
82
+ document.body.appendChild(highlightOverlay);
83
+ setTimeout(() => {
84
+ highlightOverlay.remove();
85
+ }, 2e3);
86
+ }
87
+ }
88
+ function handleRemoveSelectedNode(item, index, source) {
89
+ options.onRemoveSelectedNode({ element: item.element, index, source });
90
+ }
91
+ async function handleClearSelectedNodes() {
92
+ if (!options.selectedElements.value || options.selectedElements.value.length === 0) return;
93
+ const confirmed = await options.showConfirmDialog(
94
+ `\u786E\u5B9A\u8981\u6E05\u7A7A\u6240\u6709 ${options.selectedElements.value.length} \u4E2A\u5DF2\u9009\u8282\u70B9\u5417\uFF1F`
95
+ );
96
+ if (confirmed) {
97
+ options.onClearSelectedNodes();
98
+ }
99
+ }
100
+ return {
101
+ bubbleVisible,
102
+ hasSelectedElements,
103
+ selectedElementItems,
104
+ handleClearSelectedNodes,
105
+ handleClickSelectedNode,
106
+ handleRemoveSelectedNode,
107
+ handleToggleSelectMode
108
+ };
109
+ }
110
+ export {
111
+ useSelection
112
+ };
@@ -0,0 +1,16 @@
1
+ import { type Ref } from "vue";
2
+ import type { AIPanelWidgetSession, AIPanelWidgetSessionItem } from "../src/types";
3
+ export interface UseSessionOptions {
4
+ sessions: Ref<AIPanelWidgetSession[]>;
5
+ currentSessionId: Ref<string | number | null>;
6
+ onCreateSession: () => void;
7
+ onSelectSession: (session: AIPanelWidgetSession) => void;
8
+ onDeleteSession: (session: AIPanelWidgetSession) => void;
9
+ showConfirmDialog: (message: string) => Promise<boolean>;
10
+ }
11
+ export declare function useSession(options: UseSessionOptions): {
12
+ sessionItems: import("vue").ComputedRef<AIPanelWidgetSessionItem[]>;
13
+ handleCreateSession: () => void;
14
+ handleDeleteSession: (item: AIPanelWidgetSessionItem) => Promise<void>;
15
+ handleSelectSession: (item: AIPanelWidgetSessionItem) => void;
16
+ };
@@ -0,0 +1,47 @@
1
+ import { computed } from "vue";
2
+ function formatSessionMeta(session) {
3
+ if (session.meta) {
4
+ return session.meta;
5
+ }
6
+ if (!session.updatedAt) {
7
+ return "";
8
+ }
9
+ const date = new Date(session.updatedAt);
10
+ if (Number.isNaN(date.getTime())) {
11
+ return "";
12
+ }
13
+ return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
14
+ }
15
+ function useSession(options) {
16
+ const sessionItems = computed(
17
+ () => (options.sessions.value || []).map((session) => ({
18
+ key: session.id,
19
+ id: session.id,
20
+ title: session.title || "\u65B0\u4F1A\u8BDD",
21
+ meta: formatSessionMeta(session),
22
+ active: session.id === options.currentSessionId.value,
23
+ session
24
+ }))
25
+ );
26
+ function handleCreateSession() {
27
+ options.onCreateSession();
28
+ }
29
+ function handleSelectSession(item) {
30
+ options.onSelectSession(item.session);
31
+ }
32
+ async function handleDeleteSession(item) {
33
+ const confirmed = await options.showConfirmDialog(`\u786E\u5B9A\u8981\u5220\u9664\u4F1A\u8BDD "${item.title}" \u5417\uFF1F`);
34
+ if (confirmed) {
35
+ options.onDeleteSession(item.session);
36
+ }
37
+ }
38
+ return {
39
+ sessionItems,
40
+ handleCreateSession,
41
+ handleDeleteSession,
42
+ handleSelectSession
43
+ };
44
+ }
45
+ export {
46
+ useSession
47
+ };
@@ -0,0 +1,30 @@
1
+ import { type Ref } from "vue";
2
+ import type { DisplayMode, SplitModeOptions } from "@aipanel/core";
3
+ export interface UseSplitModeOptions {
4
+ displayMode: Ref<DisplayMode>;
5
+ splitMode: Ref<SplitModeOptions | undefined>;
6
+ open: Ref<boolean>;
7
+ splitPosition?: Ref<"left" | "right">;
8
+ onOpenChange?: (open: boolean) => void;
9
+ onWidthChange?: (width: number) => void;
10
+ onPositionChange?: (position: "left" | "right") => void;
11
+ }
12
+ export declare function useSplitMode(options: UseSplitModeOptions): {
13
+ effectiveMode: import("vue").ComputedRef<"bubble" | "split">;
14
+ isSplitMode: import("vue").ComputedRef<boolean>;
15
+ isExtensionMode: import("vue").ComputedRef<boolean>;
16
+ panelWidth: Ref<number, number>;
17
+ splitConfig: import("vue").ComputedRef<{
18
+ width: number;
19
+ minWidth: number;
20
+ maxWidth: number;
21
+ resizable: boolean;
22
+ shrinkPage: boolean;
23
+ defaultOpen: boolean;
24
+ position: "left" | "right";
25
+ }>;
26
+ splitPosition: import("vue").ComputedRef<"left" | "right">;
27
+ handleResize: (width: number) => void;
28
+ handleToggle: () => void;
29
+ handleTogglePosition: () => void;
30
+ };
@@ -0,0 +1,133 @@
1
+ import { ref, computed, watch, onMounted, onUnmounted } from "vue";
2
+ const AUTO_MODE_THRESHOLD = 1440;
3
+ function useSplitMode(options) {
4
+ const windowWidth = ref(typeof window !== "undefined" ? window.innerWidth : 0);
5
+ const localSplitPosition = ref(options.splitPosition?.value ?? "right");
6
+ const isExtensionMode = computed(() => options.displayMode.value === "extension");
7
+ const shouldSkipBodyModification = computed(
8
+ () => isExtensionMode.value || options.displayMode.value === "extension-selector"
9
+ );
10
+ const splitConfig = computed(() => {
11
+ const config = options.splitMode.value || {};
12
+ const isExt = isExtensionMode.value;
13
+ return {
14
+ width: config.width ?? 500,
15
+ minWidth: config.minWidth ?? 400,
16
+ maxWidth: config.maxWidth ?? 800,
17
+ resizable: isExt ? false : config.resizable ?? true,
18
+ shrinkPage: isExt ? false : config.shrinkPage ?? true,
19
+ defaultOpen: isExt ? true : config.defaultOpen ?? true,
20
+ position: config.position ?? localSplitPosition.value
21
+ };
22
+ });
23
+ const panelWidth = ref(splitConfig.value.width);
24
+ const effectiveMode = computed(() => {
25
+ if (isExtensionMode.value) return "split";
26
+ if (options.displayMode.value === "bubble") {
27
+ return "bubble";
28
+ }
29
+ if (options.displayMode.value === "split") {
30
+ return "split";
31
+ }
32
+ return windowWidth.value >= AUTO_MODE_THRESHOLD ? "split" : "bubble";
33
+ });
34
+ const isSplitMode = computed(() => effectiveMode.value === "split");
35
+ const splitPosition = computed(() => splitConfig.value.position);
36
+ const handleResize = (width) => {
37
+ panelWidth.value = width;
38
+ options.onWidthChange?.(width);
39
+ };
40
+ const handleToggle = () => {
41
+ const nextOpen = !options.open.value;
42
+ options.onOpenChange?.(nextOpen);
43
+ };
44
+ const handleTogglePosition = () => {
45
+ const nextPosition = localSplitPosition.value === "right" ? "left" : "right";
46
+ localSplitPosition.value = nextPosition;
47
+ options.onPositionChange?.(nextPosition);
48
+ };
49
+ const handleWindowResize = () => {
50
+ if (typeof window !== "undefined") {
51
+ windowWidth.value = window.innerWidth;
52
+ }
53
+ };
54
+ const updateBodyClass = () => {
55
+ if (typeof document === "undefined") return;
56
+ if (shouldSkipBodyModification.value) {
57
+ document.body.classList.remove("has-aipanel-split");
58
+ document.body.classList.remove("has-aipanel-split-left");
59
+ document.body.classList.remove("has-aipanel-split-right");
60
+ document.body.style.removeProperty("--aipanel-split-width");
61
+ return;
62
+ }
63
+ const shouldShrink = isSplitMode.value && options.open.value && splitConfig.value.shrinkPage;
64
+ if (shouldShrink) {
65
+ document.body.classList.add("has-aipanel-split");
66
+ document.body.style.setProperty("--aipanel-split-width", `${panelWidth.value}px`);
67
+ if (splitPosition.value === "left") {
68
+ document.body.classList.add("has-aipanel-split-left");
69
+ document.body.classList.remove("has-aipanel-split-right");
70
+ } else {
71
+ document.body.classList.add("has-aipanel-split-right");
72
+ document.body.classList.remove("has-aipanel-split-left");
73
+ }
74
+ } else {
75
+ document.body.classList.remove("has-aipanel-split");
76
+ document.body.classList.remove("has-aipanel-split-left");
77
+ document.body.classList.remove("has-aipanel-split-right");
78
+ document.body.style.removeProperty("--aipanel-split-width");
79
+ }
80
+ };
81
+ watch([isSplitMode, options.open, panelWidth, splitPosition], updateBodyClass, {
82
+ immediate: true
83
+ });
84
+ watch(splitConfig, (config) => {
85
+ if (panelWidth.value < config.minWidth) {
86
+ panelWidth.value = config.minWidth;
87
+ }
88
+ if (panelWidth.value > config.maxWidth) {
89
+ panelWidth.value = config.maxWidth;
90
+ }
91
+ });
92
+ watch(
93
+ () => options.splitPosition?.value,
94
+ (val) => {
95
+ if (val && val !== localSplitPosition.value) {
96
+ localSplitPosition.value = val;
97
+ }
98
+ }
99
+ );
100
+ onMounted(() => {
101
+ if (typeof window !== "undefined") {
102
+ window.addEventListener("resize", handleWindowResize);
103
+ if (!isExtensionMode.value && isSplitMode.value && splitConfig.value.defaultOpen && !options.open.value) {
104
+ options.onOpenChange?.(true);
105
+ }
106
+ }
107
+ });
108
+ onUnmounted(() => {
109
+ if (typeof window !== "undefined") {
110
+ window.removeEventListener("resize", handleWindowResize);
111
+ if (!isExtensionMode.value) {
112
+ document.body.classList.remove("has-aipanel-split");
113
+ document.body.classList.remove("has-aipanel-split-left");
114
+ document.body.classList.remove("has-aipanel-split-right");
115
+ document.body.style.removeProperty("--aipanel-split-width");
116
+ }
117
+ }
118
+ });
119
+ return {
120
+ effectiveMode,
121
+ isSplitMode,
122
+ isExtensionMode,
123
+ panelWidth,
124
+ splitConfig,
125
+ splitPosition,
126
+ handleResize,
127
+ handleToggle,
128
+ handleTogglePosition
129
+ };
130
+ }
131
+ export {
132
+ useSplitMode
133
+ };
@@ -0,0 +1,27 @@
1
+ import { type Ref } from "vue";
2
+ import type { AIPanelWidgetTheme } from "../src/types";
3
+ export interface UseWidgetOptions {
4
+ theme: Ref<string>;
5
+ open: Ref<boolean>;
6
+ selectMode: Ref<boolean>;
7
+ iframeSrc: Ref<string>;
8
+ sessionListCollapsed: Ref<boolean>;
9
+ onToggle: (nextOpen: boolean) => void;
10
+ onToggleSelectMode?: (mode: boolean) => void;
11
+ onClose: () => void;
12
+ onToggleSessionList: (collapsed: boolean) => void;
13
+ onEmptyAction: () => void;
14
+ onToggleTheme?: (theme: AIPanelWidgetTheme) => void;
15
+ }
16
+ export declare function useWidget(options: UseWidgetOptions): {
17
+ buttonActive: import("vue").ComputedRef<boolean>;
18
+ containerClasses: import("vue").ComputedRef<string[]>;
19
+ iframeSource: import("vue").ComputedRef<string>;
20
+ sessionListTitle: import("vue").ComputedRef<"展开会话列表" | "折叠会话列表">;
21
+ resolvedTheme: import("vue").ComputedRef<"light" | "dark">;
22
+ handleClose: () => void;
23
+ handleEmptyAction: () => void;
24
+ handleToggle: () => void;
25
+ handleToggleSessionList: () => void;
26
+ handleToggleTheme: () => void;
27
+ };
@@ -0,0 +1,78 @@
1
+ import { computed, ref, onMounted, onUnmounted } from "vue";
2
+ const THEME_CYCLE = ["auto", "light", "dark"];
3
+ function useWidget(options) {
4
+ const systemTheme = ref("light");
5
+ function getSystemTheme() {
6
+ if (typeof window === "undefined") return "light";
7
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
8
+ }
9
+ let mediaQuery = null;
10
+ let handleChange = null;
11
+ onMounted(() => {
12
+ if (typeof window === "undefined") return;
13
+ systemTheme.value = getSystemTheme();
14
+ mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
15
+ handleChange = (e) => {
16
+ systemTheme.value = e.matches ? "dark" : "light";
17
+ };
18
+ mediaQuery.addEventListener("change", handleChange);
19
+ });
20
+ onUnmounted(() => {
21
+ if (mediaQuery && handleChange) {
22
+ mediaQuery.removeEventListener("change", handleChange);
23
+ }
24
+ });
25
+ const resolvedTheme = computed(() => {
26
+ if (options.theme.value === "auto") {
27
+ return systemTheme.value;
28
+ }
29
+ return options.theme.value;
30
+ });
31
+ const containerClasses = computed(() => [
32
+ "aipanel-widget",
33
+ `aipanel-theme-${resolvedTheme.value}`
34
+ ]);
35
+ const buttonActive = computed(() => !!(options.open.value || options.selectMode.value));
36
+ const iframeSource = computed(() => options.iframeSrc.value || "about:blank");
37
+ const sessionListTitle = computed(
38
+ () => options.sessionListCollapsed.value ? "\u5C55\u5F00\u4F1A\u8BDD\u5217\u8868" : "\u6298\u53E0\u4F1A\u8BDD\u5217\u8868"
39
+ );
40
+ function handleToggle() {
41
+ if (options.selectMode.value) {
42
+ options.onToggleSelectMode?.(false);
43
+ return;
44
+ }
45
+ const nextOpen = !options.open.value;
46
+ options.onToggle(nextOpen);
47
+ }
48
+ function handleClose() {
49
+ options.onClose();
50
+ }
51
+ function handleToggleSessionList() {
52
+ options.onToggleSessionList(!options.sessionListCollapsed.value);
53
+ }
54
+ function handleEmptyAction() {
55
+ options.onEmptyAction();
56
+ }
57
+ function handleToggleTheme() {
58
+ const currentIndex = THEME_CYCLE.indexOf(options.theme.value);
59
+ const nextIndex = (currentIndex + 1) % THEME_CYCLE.length;
60
+ const nextTheme = THEME_CYCLE[nextIndex];
61
+ options.onToggleTheme?.(nextTheme);
62
+ }
63
+ return {
64
+ buttonActive,
65
+ containerClasses,
66
+ iframeSource,
67
+ sessionListTitle,
68
+ resolvedTheme,
69
+ handleClose,
70
+ handleEmptyAction,
71
+ handleToggle,
72
+ handleToggleSessionList,
73
+ handleToggleTheme
74
+ };
75
+ }
76
+ export {
77
+ useWidget
78
+ };
@@ -0,0 +1,5 @@
1
+ import AIPanelWidget from "./src/index.vue";
2
+ import AIPanelLogo from "./src/components/AIPanelLogo.vue";
3
+ export { AIPanelWidget, AIPanelLogo };
4
+ export default AIPanelWidget;
5
+ export * from "./src/types";
@@ -0,0 +1,9 @@
1
+ import AIPanelWidget from "./src/index.vue.mjs";
2
+ import AIPanelLogo from "./src/components/AIPanelLogo.vue.mjs";
3
+ var AI_panel_widget_default = AIPanelWidget;
4
+ export * from "./src/types.mjs";
5
+ export {
6
+ AIPanelLogo,
7
+ AIPanelWidget,
8
+ AI_panel_widget_default as default
9
+ };
@@ -0,0 +1,13 @@
1
+ <svg t="1775402599580" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5390" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
2
+ <defs>
3
+ <linearGradient id="aipanel-logo-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#667eea"/>
5
+ <stop offset="100%" style="stop-color:#764ba2"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <g transform="rotate(180 512 512)">
9
+ <path d="M512 981.33H85.34c-15.85 0-30.38-8.77-37.77-22.81a42.624 42.624 0 0 1 2.6-44.02L135 791.08C75.25 710.5 42.67 612.6 42.67 512 42.67 253.21 253.21 42.67 512 42.67S981.34 253.21 981.34 512 770.8 981.33 512 981.33zM166.44 896H512c211.73 0 384-172.27 384-384S723.73 128 512 128 128 300.27 128 512c0 91.29 32.83 179.9 92.46 249.46 12.58 14.69 13.73 36 2.77 51.94L166.44 896z" fill="url(#aipanel-logo-gradient)" p-id="5391"/>
10
+ <path d="M384 448m-64 0a64 64 0 1 0 128 0 64 64 0 1 0 -128 0Z" fill="url(#aipanel-logo-gradient)" p-id="5392"/>
11
+ <path d="M640 448m-64 0a64 64 0 1 0 128 0a64 64 0 1 0 -128 0Z" fill="url(#aipanel-logo-gradient)" p-id="5393"/>
12
+ </g>
13
+ </svg>
@@ -0,0 +1 @@
1
+ .aipanel-logo svg{display:block;width:100%;height:100%}
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ size?: string | number;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
5
+ size: string | number;
6
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,40 @@
1
+ import "./AIPanelLogo-sfc.css";
2
+ import { defineComponent as _defineComponent } from "vue";
3
+ import { computed, useId } from "vue";
4
+ import logoSvg from "../assets/logo.svg?raw";
5
+ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
6
+ __name: "AIPanelLogo",
7
+ props: {
8
+ size: { type: [String, Number], required: false, default: "100%" }
9
+ },
10
+ setup(__props, { expose: __expose }) {
11
+ __expose();
12
+ const props = __props;
13
+ const baseId = useId();
14
+ const gradientId = `aipanel-logo-gradient-${baseId}`;
15
+ const rendered = computed(
16
+ () => logoSvg.replace('id="aipanel-logo-gradient"', `id="${gradientId}"`).replace(/url\(#aipanel-logo-gradient\)/g, `url(#${gradientId})`)
17
+ );
18
+ const sizeStyle = computed(() => {
19
+ const value = typeof props.size === "number" ? `${props.size}px` : props.size;
20
+ return { width: value, height: value };
21
+ });
22
+ const __returned__ = { props, baseId, gradientId, rendered, sizeStyle };
23
+ Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
24
+ return __returned__;
25
+ }
26
+ });
27
+ import { normalizeStyle as _normalizeStyle, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue";
28
+ const _hoisted_1 = ["innerHTML"];
29
+ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
30
+ return _openBlock(), _createElementBlock("span", {
31
+ class: "aipanel-logo",
32
+ style: _normalizeStyle([{ display: "inline-block", lineHeight: 0 }, $setup.sizeStyle]),
33
+ innerHTML: $setup.rendered
34
+ }, null, 12, _hoisted_1);
35
+ }
36
+ __vue_sfc__.render = __vue_render__;
37
+ var AIPanelLogo_vue_default = __vue_sfc__;
38
+ export {
39
+ AIPanelLogo_vue_default as default
40
+ };
@@ -0,0 +1 @@
1
+ .aipanel-chat{position:fixed;bottom:20px;width:700px;height:86vh;max-height:calc(100vh - 40px);background:var(--ap-bg-main);border-radius:16px;box-shadow:var(--ap-shadow-lg);overflow:hidden;display:flex;flex-direction:column;z-index:99999}.aipanel-chat:not(.split-mode){opacity:0;visibility:hidden;transition:all .3s ease;transform:translate3d(var(---animationOrigin\.x),var(---animationOrigin\.y),0) scale(.95)}.aipanel-chat.split-mode{position:fixed;top:0;bottom:0;height:100vh;max-height:100vh;border-radius:0;box-shadow:var(--ap-shadow-lg);overflow:visible;opacity:1;visibility:visible;transition:transform .3s ease}.aipanel-chat.split-mode.split-right{right:0;border-left:1px solid var(--ap-border-primary);transform:translate(100%)}.aipanel-chat.split-mode.split-left{left:0;border-right:1px solid var(--ap-border-primary);transform:translate(-100%)}.aipanel-chat.split-mode .aipanel-chat-content{overflow:hidden;flex:1}.aipanel-chat.split-mode.open{transform:translate(0)}.aipanel-chat.split-mode.dragging{transition:none}.aipanel-chat.dragging .aipanel-iframe{pointer-events:none}.aipanel-chat.extension-mode{position:static;width:100%;height:100%;bottom:auto;left:auto;right:auto;border-radius:0;border-left:none;border-right:none;max-height:none;box-shadow:none;transform:none;opacity:1;visibility:visible}.aipanel-chat:not(.split-mode){opacity:0;visibility:hidden;transition:all .3s ease}.aipanel-chat:not(.split-mode).open{opacity:1;visibility:visible;transform:translateZ(0) scale(1)}.aipanel-chat.no-transition,.aipanel-chat.no-transition.open{transition:none!important}.aipanel-chat.minimized:not(.split-mode){width:320px;height:320px}.aipanel-chat.minimized:not(.split-mode) .aipanel-iframe-container{margin-top:-146px}.aipanel-chat-content{display:flex;flex:1;overflow:hidden}.aipanel-session-empty{padding:24px;text-align:center;color:var(--ap-text-placeholder);font-size:14px}.aipanel-split-toggle-btn{position:absolute;left:-21px;top:50%;transform:translateY(-50%);width:20px;height:48px;background:#fff;border:none;border-radius:8px 0 0 8px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#667eea;box-shadow:0 4px 12px #667eea66;transition:all .3s ease;z-index:5;transform-origin:right center}.aipanel-split-toggle-btn.split-left{left:auto;right:-21px;border-radius:0 8px 8px 0;transform-origin:left center}.aipanel-split-toggle-btn:hover{transform:translateY(-50%) scale(1.1);box-shadow:0 6px 16px #667eea80}.aipanel-split-toggle-btn.split-left:hover{transform:translateY(-50%) scale(1.1)}.aipanel-split-toggle-btn.aipanel-theme-dark{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;box-shadow:0 4px 12px #667eea4d}.aipanel-split-toggle-btn.aipanel-theme-dark:before{content:"";position:absolute;inset:-2px 0 -2px -2px;border-radius:8px 0 0 8px;background:linear-gradient(135deg,#8b9cf5,#9d6bc7);z-index:-1}.aipanel-split-toggle-btn.aipanel-theme-dark.split-left:before{left:0;right:-2px;border-radius:0 8px 8px 0}.aipanel-split-toggle-btn.aipanel-theme-dark:hover{box-shadow:0 6px 16px #667eea66}.aipanel-split-toggle-btn.thinking{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;animation:split-thinking-glow 2s ease-in-out infinite,split-thinking-pulse 2s ease-in-out infinite;box-shadow:0 0 20px #667eea99,0 0 40px #764ba266,0 0 60px #667eea33}.aipanel-split-toggle-btn.thinking:before{content:"";position:absolute;inset:-2px 0 -2px -2px;border-radius:8px 0 0 8px;background:linear-gradient(135deg,#8b9cf5,#9d6bc7);z-index:-1}.aipanel-split-toggle-btn.thinking.split-left:before{left:0;right:-2px;border-radius:0 8px 8px 0}.aipanel-split-toggle-btn.thinking:after{content:"";position:absolute;inset:-3px -1px -3px -3px;border-radius:8px 0 0 8px;background:conic-gradient(from 180deg,transparent,rgba(102,126,234,.3),transparent,rgba(118,75,162,.3),transparent);z-index:-2;animation:split-thinking-rotate 2s linear infinite reverse;filter:blur(8px)}.aipanel-split-toggle-btn.thinking.split-left:after{left:-1px;right:-3px;border-radius:0 8px 8px 0}@keyframes split-thinking-glow{0%,to{box-shadow:0 0 20px #667eea99,0 0 40px #764ba266,0 0 60px #667eea33}50%{box-shadow:0 0 30px #667eeacc,0 0 60px #764ba299,0 0 90px #667eea4d}}@keyframes split-thinking-rotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes split-thinking-pulse{0%,to{transform:translateY(-50%) scale(1)}50%{transform:translateY(-50%) scale(.92)}}.aipanel-split-toggle-icon{display:flex;align-items:center;justify-content:center;transition:transform .25s ease}.aipanel-split-toggle-btn:hover .aipanel-split-toggle-icon{transform:scale(1.1)}