ibc-ai-web-sdk 2.0.3 → 2.0.5

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/dist/index.esm.js CHANGED
@@ -221,7 +221,8 @@ class AIChatClient {
221
221
  return token;
222
222
  }).catch(error => {
223
223
  if (silent) {
224
- console.warn('[AIChatClient] Silent token refresh failed (reason=' + reason + '):', error.message);
224
+ const errMsg = error && (error.message || String(error)) || 'unknown error';
225
+ console.warn('[AIChatClient] Silent token refresh failed (reason=' + reason + '):', errMsg);
225
226
  return this.config.token || '';
226
227
  }
227
228
  this.stopBackgroundRefresh();
@@ -379,8 +380,25 @@ class AIChatClient {
379
380
  if (conversationId) payload.conversationId = conversationId;
380
381
  const attachmentIds = options.attachmentIds || this.normalizeAttachmentIds(options.attachments);
381
382
  if (attachmentIds.length > 0) payload.attachmentIds = attachmentIds;
382
- const bizParams = options.bizParams || options.extendInfo || this.config.extendInfo;
383
- if (bizParams) payload.bizParams = typeof bizParams === 'string' ? bizParams : JSON.stringify(bizParams);
383
+ let bizParams = options.bizParams || options.extendInfo || this.config.extendInfo;
384
+ if (bizParams) {
385
+ if (typeof bizParams === 'string') {
386
+ try {
387
+ bizParams = JSON.parse(bizParams);
388
+ } catch (e) {
389
+ bizParams = {};
390
+ }
391
+ }
392
+ } else {
393
+ bizParams = {};
394
+ }
395
+ // P3: 将页面上下文合并到 bizParams(contextMode='bizParams' 时由 dialog 传入)
396
+ if (options.context) {
397
+ Object.assign(bizParams, options.context);
398
+ }
399
+ if (Object.keys(bizParams).length > 0) {
400
+ payload.bizParams = JSON.stringify(bizParams);
401
+ }
384
402
  return payload;
385
403
  }
386
404
  normalizeAttachmentIds(attachments) {
@@ -2715,12 +2733,855 @@ function createDOMPurify() {
2715
2733
  }
2716
2734
  var purify = createDOMPurify();
2717
2735
 
2736
+ /**
2737
+ * 预设主题配置
2738
+ * 提供多种常用主题,用户可以直接使用或自定义
2739
+ */
2740
+
2741
+ /**
2742
+ * 默认主题(蓝色渐变)
2743
+ */
2744
+ const DEFAULT_THEME = {
2745
+ name: 'default',
2746
+ label: '默认主题',
2747
+ colors: {
2748
+ primary: '#1890ff',
2749
+ primaryLight: '#40a9ff',
2750
+ primaryDark: '#096dd9',
2751
+ secondary: '#722ed1',
2752
+ background: '#ffffff',
2753
+ backgroundGradient: '#f8fafc',
2754
+ text: '#1e293b',
2755
+ textSecondary: '#64748b',
2756
+ userBubble: 'linear-gradient(135deg, #1890ff, #40a9ff)',
2757
+ aiBubble: '#ffffff',
2758
+ headerGradient: 'linear-gradient(90deg, rgba(24, 144, 255, 0.08), rgba(114, 46, 209, 0.08))',
2759
+ border: 'rgba(24, 144, 255, 0.15)',
2760
+ shadow: 'rgba(24, 144, 255, 0.12)',
2761
+ inputBackground: '#f8fafc',
2762
+ inputBorder: '#e2e8f0',
2763
+ loading: '#64748b',
2764
+ success: '#52c41a',
2765
+ error: '#ff4d4f',
2766
+ warning: '#faad14'
2767
+ },
2768
+ borderRadius: {
2769
+ dialog: '16px',
2770
+ bubble: '12px',
2771
+ button: '50%',
2772
+ input: '28px',
2773
+ small: '6px',
2774
+ medium: '12px'
2775
+ },
2776
+ fontSize: {
2777
+ title: '15px',
2778
+ welcomeText: '17px',
2779
+ welcomeDesc: '13px',
2780
+ message: '14px',
2781
+ time: '11px',
2782
+ small: '11px'
2783
+ },
2784
+ boxShadow: {
2785
+ dialog: '0 0 30px rgba(24, 144, 255, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
2786
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
2787
+ userBubble: '0 4px 12px rgba(24, 144, 255, 0.25)',
2788
+ button: '0 4px 12px rgba(24, 144, 255, 0.35)'
2789
+ }
2790
+ };
2791
+
2792
+ /**
2793
+ * 深色主题
2794
+ */
2795
+ const DARK_THEME = {
2796
+ name: 'dark',
2797
+ label: '深色主题',
2798
+ colors: {
2799
+ primary: '#40a9ff',
2800
+ primaryLight: '#69c0ff',
2801
+ primaryDark: '#1890ff',
2802
+ secondary: '#9254de',
2803
+ background: '#0f172a',
2804
+ backgroundGradient: '#1e293b',
2805
+ text: '#f1f5f9',
2806
+ textSecondary: '#94a3b8',
2807
+ userBubble: 'linear-gradient(135deg, #1890ff, #40a9ff)',
2808
+ aiBubble: '#1e293b',
2809
+ headerGradient: 'linear-gradient(90deg, rgba(64, 169, 255, 0.15), rgba(146, 84, 222, 0.15))',
2810
+ border: 'rgba(64, 169, 255, 0.3)',
2811
+ shadow: 'rgba(0, 0, 0, 0.3)',
2812
+ inputBackground: '#1e293b',
2813
+ inputBorder: '#334155',
2814
+ loading: '#94a3b8',
2815
+ success: '#73d13d',
2816
+ error: '#ff7875',
2817
+ warning: '#ffd666'
2818
+ },
2819
+ borderRadius: {
2820
+ dialog: '16px',
2821
+ bubble: '12px',
2822
+ button: '50%',
2823
+ input: '28px',
2824
+ small: '6px',
2825
+ medium: '12px'
2826
+ },
2827
+ fontSize: {
2828
+ title: '15px',
2829
+ welcomeText: '17px',
2830
+ welcomeDesc: '13px',
2831
+ message: '14px',
2832
+ time: '11px',
2833
+ small: '11px'
2834
+ },
2835
+ boxShadow: {
2836
+ dialog: '0 0 30px rgba(64, 169, 255, 0.15), 0 8px 32px rgba(0, 0, 0, 0.4)',
2837
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.2)',
2838
+ userBubble: '0 4px 12px rgba(24, 144, 255, 0.3)',
2839
+ button: '0 4px 12px rgba(24, 144, 255, 0.4)'
2840
+ }
2841
+ };
2842
+
2843
+ /**
2844
+ * 清新主题(绿色系)
2845
+ */
2846
+ const FRESH_THEME = {
2847
+ name: 'fresh',
2848
+ label: '清新主题',
2849
+ colors: {
2850
+ primary: '#52c41a',
2851
+ primaryLight: '#73d13d',
2852
+ primaryDark: '#389e0d',
2853
+ secondary: '#1890ff',
2854
+ background: '#ffffff',
2855
+ backgroundGradient: '#f6ffed',
2856
+ text: '#1e293b',
2857
+ textSecondary: '#64748b',
2858
+ userBubble: 'linear-gradient(135deg, #52c41a, #73d13d)',
2859
+ aiBubble: '#ffffff',
2860
+ headerGradient: 'linear-gradient(90deg, rgba(82, 196, 26, 0.08), rgba(24, 144, 255, 0.08))',
2861
+ border: 'rgba(82, 196, 26, 0.15)',
2862
+ shadow: 'rgba(82, 196, 26, 0.12)',
2863
+ inputBackground: '#f6ffed',
2864
+ inputBorder: '#d9f7be',
2865
+ loading: '#64748b',
2866
+ success: '#52c41a',
2867
+ error: '#ff4d4f',
2868
+ warning: '#faad14'
2869
+ },
2870
+ borderRadius: {
2871
+ dialog: '20px',
2872
+ bubble: '16px',
2873
+ button: '50%',
2874
+ input: '32px',
2875
+ small: '8px',
2876
+ medium: '16px'
2877
+ },
2878
+ fontSize: {
2879
+ title: '15px',
2880
+ welcomeText: '17px',
2881
+ welcomeDesc: '13px',
2882
+ message: '14px',
2883
+ time: '11px',
2884
+ small: '11px'
2885
+ },
2886
+ boxShadow: {
2887
+ dialog: '0 0 30px rgba(82, 196, 26, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
2888
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
2889
+ userBubble: '0 4px 12px rgba(82, 196, 26, 0.25)',
2890
+ button: '0 4px 12px rgba(82, 196, 26, 0.35)'
2891
+ }
2892
+ };
2893
+
2894
+ /**
2895
+ * 活力主题(橙色系)
2896
+ */
2897
+ const VIBRANT_THEME = {
2898
+ name: 'vibrant',
2899
+ label: '活力主题',
2900
+ colors: {
2901
+ primary: '#fa8c16',
2902
+ primaryLight: '#ffa940',
2903
+ primaryDark: '#d46b08',
2904
+ secondary: '#f5222d',
2905
+ background: '#ffffff',
2906
+ backgroundGradient: '#fff7e6',
2907
+ text: '#1e293b',
2908
+ textSecondary: '#64748b',
2909
+ userBubble: 'linear-gradient(135deg, #fa8c16, #ffa940)',
2910
+ aiBubble: '#ffffff',
2911
+ headerGradient: 'linear-gradient(90deg, rgba(250, 140, 22, 0.08), rgba(245, 34, 45, 0.08))',
2912
+ border: 'rgba(250, 140, 22, 0.15)',
2913
+ shadow: 'rgba(250, 140, 22, 0.12)',
2914
+ inputBackground: '#fff7e6',
2915
+ inputBorder: '#ffd591',
2916
+ loading: '#64748b',
2917
+ success: '#52c41a',
2918
+ error: '#ff4d4f',
2919
+ warning: '#faad14'
2920
+ },
2921
+ borderRadius: {
2922
+ dialog: '12px',
2923
+ bubble: '10px',
2924
+ button: '50%',
2925
+ input: '24px',
2926
+ small: '4px',
2927
+ medium: '10px'
2928
+ },
2929
+ fontSize: {
2930
+ title: '15px',
2931
+ welcomeText: '17px',
2932
+ welcomeDesc: '13px',
2933
+ message: '14px',
2934
+ time: '11px',
2935
+ small: '11px'
2936
+ },
2937
+ boxShadow: {
2938
+ dialog: '0 0 30px rgba(250, 140, 22, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
2939
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
2940
+ userBubble: '0 4px 12px rgba(250, 140, 22, 0.25)',
2941
+ button: '0 4px 12px rgba(250, 140, 22, 0.35)'
2942
+ }
2943
+ };
2944
+
2945
+ /**
2946
+ * 浪漫主题(粉色系)
2947
+ */
2948
+ const ROMANTIC_THEME = {
2949
+ name: 'romantic',
2950
+ label: '浪漫主题',
2951
+ colors: {
2952
+ primary: '#eb2f96',
2953
+ primaryLight: '#fb6f92',
2954
+ primaryDark: '#c41d7f',
2955
+ secondary: '#722ed1',
2956
+ background: '#ffffff',
2957
+ backgroundGradient: '#fff0f6',
2958
+ text: '#1e293b',
2959
+ textSecondary: '#64748b',
2960
+ userBubble: 'linear-gradient(135deg, #eb2f96, #fb6f92)',
2961
+ aiBubble: '#ffffff',
2962
+ headerGradient: 'linear-gradient(90deg, rgba(235, 47, 150, 0.08), rgba(114, 46, 209, 0.08))',
2963
+ border: 'rgba(235, 47, 150, 0.15)',
2964
+ shadow: 'rgba(235, 47, 150, 0.12)',
2965
+ inputBackground: '#fff0f6',
2966
+ inputBorder: '#ffadd2',
2967
+ loading: '#64748b',
2968
+ success: '#52c41a',
2969
+ error: '#ff4d4f',
2970
+ warning: '#faad14'
2971
+ },
2972
+ borderRadius: {
2973
+ dialog: '24px',
2974
+ bubble: '16px',
2975
+ button: '50%',
2976
+ input: '32px',
2977
+ small: '8px',
2978
+ medium: '16px'
2979
+ },
2980
+ fontSize: {
2981
+ title: '15px',
2982
+ welcomeText: '17px',
2983
+ welcomeDesc: '13px',
2984
+ message: '14px',
2985
+ time: '11px',
2986
+ small: '11px'
2987
+ },
2988
+ boxShadow: {
2989
+ dialog: '0 0 30px rgba(235, 47, 150, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
2990
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
2991
+ userBubble: '0 4px 12px rgba(235, 47, 150, 0.25)',
2992
+ button: '0 4px 12px rgba(235, 47, 150, 0.35)'
2993
+ }
2994
+ };
2995
+
2996
+ /**
2997
+ * 紫色主题(优雅神秘)
2998
+ */
2999
+ const PURPLE_THEME = {
3000
+ name: 'purple',
3001
+ label: '紫色主题',
3002
+ colors: {
3003
+ primary: '#722ed1',
3004
+ primaryLight: '#9254de',
3005
+ primaryDark: '#531dab',
3006
+ secondary: '#1890ff',
3007
+ background: '#ffffff',
3008
+ backgroundGradient: '#f9f0ff',
3009
+ text: '#1e293b',
3010
+ textSecondary: '#64748b',
3011
+ userBubble: 'linear-gradient(135deg, #722ed1, #9254de)',
3012
+ aiBubble: '#ffffff',
3013
+ headerGradient: 'linear-gradient(90deg, rgba(114, 46, 209, 0.08), rgba(24, 144, 255, 0.08))',
3014
+ border: 'rgba(114, 46, 209, 0.15)',
3015
+ shadow: 'rgba(114, 46, 209, 0.12)',
3016
+ inputBackground: '#f9f0ff',
3017
+ inputBorder: '#d3adf7',
3018
+ loading: '#64748b',
3019
+ success: '#52c41a',
3020
+ error: '#ff4d4f',
3021
+ warning: '#faad14'
3022
+ },
3023
+ borderRadius: {
3024
+ dialog: '16px',
3025
+ bubble: '12px',
3026
+ button: '50%',
3027
+ input: '28px',
3028
+ small: '6px',
3029
+ medium: '12px'
3030
+ },
3031
+ fontSize: {
3032
+ title: '15px',
3033
+ welcomeText: '17px',
3034
+ welcomeDesc: '13px',
3035
+ message: '14px',
3036
+ time: '11px',
3037
+ small: '11px'
3038
+ },
3039
+ boxShadow: {
3040
+ dialog: '0 0 30px rgba(114, 46, 209, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3041
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3042
+ userBubble: '0 4px 12px rgba(114, 46, 209, 0.25)',
3043
+ button: '0 4px 12px rgba(114, 46, 209, 0.35)'
3044
+ }
3045
+ };
3046
+
3047
+ /**
3048
+ * 海洋主题(深蓝深海)
3049
+ */
3050
+ const OCEAN_THEME = {
3051
+ name: 'ocean',
3052
+ label: '海洋主题',
3053
+ colors: {
3054
+ primary: '#0077b6',
3055
+ primaryLight: '#00b4d8',
3056
+ primaryDark: '#023e8a',
3057
+ secondary: '#00b4d8',
3058
+ background: '#ffffff',
3059
+ backgroundGradient: '#f0f9ff',
3060
+ text: '#1e293b',
3061
+ textSecondary: '#64748b',
3062
+ userBubble: 'linear-gradient(135deg, #0077b6, #00b4d8)',
3063
+ aiBubble: '#ffffff',
3064
+ headerGradient: 'linear-gradient(90deg, rgba(0, 119, 182, 0.08), rgba(0, 180, 216, 0.08))',
3065
+ border: 'rgba(0, 119, 182, 0.15)',
3066
+ shadow: 'rgba(0, 119, 182, 0.12)',
3067
+ inputBackground: '#f0f9ff',
3068
+ inputBorder: '#bae6fd',
3069
+ loading: '#64748b',
3070
+ success: '#52c41a',
3071
+ error: '#ff4d4f',
3072
+ warning: '#faad14'
3073
+ },
3074
+ borderRadius: {
3075
+ dialog: '14px',
3076
+ bubble: '10px',
3077
+ button: '50%',
3078
+ input: '28px',
3079
+ small: '6px',
3080
+ medium: '12px'
3081
+ },
3082
+ fontSize: {
3083
+ title: '15px',
3084
+ welcomeText: '17px',
3085
+ welcomeDesc: '13px',
3086
+ message: '14px',
3087
+ time: '11px',
3088
+ small: '11px'
3089
+ },
3090
+ boxShadow: {
3091
+ dialog: '0 0 30px rgba(0, 119, 182, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3092
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3093
+ userBubble: '0 4px 12px rgba(0, 119, 182, 0.25)',
3094
+ button: '0 4px 12px rgba(0, 119, 182, 0.35)'
3095
+ }
3096
+ };
3097
+
3098
+ /**
3099
+ * 暮光主题(紫罗兰渐变)
3100
+ */
3101
+ const TWILIGHT_THEME = {
3102
+ name: 'twilight',
3103
+ label: '暮光主题',
3104
+ colors: {
3105
+ primary: '#9333ea',
3106
+ primaryLight: '#a855f7',
3107
+ primaryDark: '#7e22ce',
3108
+ secondary: '#ec4899',
3109
+ background: '#ffffff',
3110
+ backgroundGradient: '#faf5ff',
3111
+ text: '#1e293b',
3112
+ textSecondary: '#64748b',
3113
+ userBubble: 'linear-gradient(135deg, #9333ea, #ec4899)',
3114
+ aiBubble: '#ffffff',
3115
+ headerGradient: 'linear-gradient(90deg, rgba(147, 51, 234, 0.08), rgba(236, 72, 153, 0.08))',
3116
+ border: 'rgba(147, 51, 234, 0.15)',
3117
+ shadow: 'rgba(147, 51, 234, 0.12)',
3118
+ inputBackground: '#faf5ff',
3119
+ inputBorder: '#e9d5ff',
3120
+ loading: '#64748b',
3121
+ success: '#52c41a',
3122
+ error: '#ff4d4f',
3123
+ warning: '#faad14'
3124
+ },
3125
+ borderRadius: {
3126
+ dialog: '18px',
3127
+ bubble: '14px',
3128
+ button: '50%',
3129
+ input: '30px',
3130
+ small: '8px',
3131
+ medium: '14px'
3132
+ },
3133
+ fontSize: {
3134
+ title: '15px',
3135
+ welcomeText: '17px',
3136
+ welcomeDesc: '13px',
3137
+ message: '14px',
3138
+ time: '11px',
3139
+ small: '11px'
3140
+ },
3141
+ boxShadow: {
3142
+ dialog: '0 0 30px rgba(147, 51, 234, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3143
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3144
+ userBubble: '0 4px 12px rgba(147, 51, 234, 0.25)',
3145
+ button: '0 4px 12px rgba(147, 51, 234, 0.35)'
3146
+ }
3147
+ };
3148
+
3149
+ /**
3150
+ * 薄荷主题(青绿色)
3151
+ */
3152
+ const MINT_THEME = {
3153
+ name: 'mint',
3154
+ label: '薄荷主题',
3155
+ colors: {
3156
+ primary: '#14b8a6',
3157
+ primaryLight: '#2dd4bf',
3158
+ primaryDark: '#0d9488',
3159
+ secondary: '#06b6d4',
3160
+ background: '#ffffff',
3161
+ backgroundGradient: '#f0fdfa',
3162
+ text: '#1e293b',
3163
+ textSecondary: '#64748b',
3164
+ userBubble: 'linear-gradient(135deg, #14b8a6, #2dd4bf)',
3165
+ aiBubble: '#ffffff',
3166
+ headerGradient: 'linear-gradient(90deg, rgba(20, 184, 166, 0.08), rgba(6, 182, 212, 0.08))',
3167
+ border: 'rgba(20, 184, 166, 0.15)',
3168
+ shadow: 'rgba(20, 184, 166, 0.12)',
3169
+ inputBackground: '#f0fdfa',
3170
+ inputBorder: '#99f6e4',
3171
+ loading: '#64748b',
3172
+ success: '#52c41a',
3173
+ error: '#ff4d4f',
3174
+ warning: '#faad14'
3175
+ },
3176
+ borderRadius: {
3177
+ dialog: '16px',
3178
+ bubble: '12px',
3179
+ button: '50%',
3180
+ input: '28px',
3181
+ small: '6px',
3182
+ medium: '12px'
3183
+ },
3184
+ fontSize: {
3185
+ title: '15px',
3186
+ welcomeText: '17px',
3187
+ welcomeDesc: '13px',
3188
+ message: '14px',
3189
+ time: '11px',
3190
+ small: '11px'
3191
+ },
3192
+ boxShadow: {
3193
+ dialog: '0 0 30px rgba(20, 184, 166, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3194
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3195
+ userBubble: '0 4px 12px rgba(20, 184, 166, 0.25)',
3196
+ button: '0 4px 12px rgba(20, 184, 166, 0.35)'
3197
+ }
3198
+ };
3199
+
3200
+ /**
3201
+ * 玫瑰主题(深红色)
3202
+ */
3203
+ const ROSE_THEME = {
3204
+ name: 'rose',
3205
+ label: '玫瑰主题',
3206
+ colors: {
3207
+ primary: '#e11d48',
3208
+ primaryLight: '#fb7185',
3209
+ primaryDark: '#be123c',
3210
+ secondary: '#f43f5e',
3211
+ background: '#ffffff',
3212
+ backgroundGradient: '#fff1f2',
3213
+ text: '#1e293b',
3214
+ textSecondary: '#64748b',
3215
+ userBubble: 'linear-gradient(135deg, #e11d48, #fb7185)',
3216
+ aiBubble: '#ffffff',
3217
+ headerGradient: 'linear-gradient(90deg, rgba(225, 29, 72, 0.08), rgba(244, 63, 94, 0.08))',
3218
+ border: 'rgba(225, 29, 72, 0.15)',
3219
+ shadow: 'rgba(225, 29, 72, 0.12)',
3220
+ inputBackground: '#fff1f2',
3221
+ inputBorder: '#fecdd3',
3222
+ loading: '#64748b',
3223
+ success: '#52c41a',
3224
+ error: '#ff4d4f',
3225
+ warning: '#faad14'
3226
+ },
3227
+ borderRadius: {
3228
+ dialog: '16px',
3229
+ bubble: '12px',
3230
+ button: '50%',
3231
+ input: '28px',
3232
+ small: '6px',
3233
+ medium: '12px'
3234
+ },
3235
+ fontSize: {
3236
+ title: '15px',
3237
+ welcomeText: '17px',
3238
+ welcomeDesc: '13px',
3239
+ message: '14px',
3240
+ time: '11px',
3241
+ small: '11px'
3242
+ },
3243
+ boxShadow: {
3244
+ dialog: '0 0 30px rgba(225, 29, 72, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3245
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3246
+ userBubble: '0 4px 12px rgba(225, 29, 72, 0.25)',
3247
+ button: '0 4px 12px rgba(225, 29, 72, 0.35)'
3248
+ }
3249
+ };
3250
+
3251
+ /**
3252
+ * 极光主题(蓝绿渐变)
3253
+ */
3254
+ const AURORA_THEME = {
3255
+ name: 'aurora',
3256
+ label: '极光主题',
3257
+ colors: {
3258
+ primary: '#0ea5e9',
3259
+ primaryLight: '#38bdf8',
3260
+ primaryDark: '#0284c7',
3261
+ secondary: '#10b981',
3262
+ background: '#ffffff',
3263
+ backgroundGradient: '#f0f9ff',
3264
+ text: '#1e293b',
3265
+ textSecondary: '#64748b',
3266
+ userBubble: 'linear-gradient(135deg, #0ea5e9, #10b981)',
3267
+ aiBubble: '#ffffff',
3268
+ headerGradient: 'linear-gradient(90deg, rgba(14, 165, 233, 0.08), rgba(16, 185, 129, 0.08))',
3269
+ border: 'rgba(14, 165, 233, 0.15)',
3270
+ shadow: 'rgba(14, 165, 233, 0.12)',
3271
+ inputBackground: '#f0f9ff',
3272
+ inputBorder: '#bae6fd',
3273
+ loading: '#64748b',
3274
+ success: '#52c41a',
3275
+ error: '#ff4d4f',
3276
+ warning: '#faad14'
3277
+ },
3278
+ borderRadius: {
3279
+ dialog: '20px',
3280
+ bubble: '14px',
3281
+ button: '50%',
3282
+ input: '30px',
3283
+ small: '8px',
3284
+ medium: '14px'
3285
+ },
3286
+ fontSize: {
3287
+ title: '15px',
3288
+ welcomeText: '17px',
3289
+ welcomeDesc: '13px',
3290
+ message: '14px',
3291
+ time: '11px',
3292
+ small: '11px'
3293
+ },
3294
+ boxShadow: {
3295
+ dialog: '0 0 30px rgba(14, 165, 233, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3296
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3297
+ userBubble: '0 4px 12px rgba(14, 165, 233, 0.25)',
3298
+ button: '0 4px 12px rgba(14, 165, 233, 0.35)'
3299
+ }
3300
+ };
3301
+
3302
+ /**
3303
+ * 薰衣草主题(淡紫色)
3304
+ */
3305
+ const LAVENDER_THEME = {
3306
+ name: 'lavender',
3307
+ label: '薰衣草主题',
3308
+ colors: {
3309
+ primary: '#8b5cf6',
3310
+ primaryLight: '#a78bfa',
3311
+ primaryDark: '#7c3aed',
3312
+ secondary: '#c084fc',
3313
+ background: '#ffffff',
3314
+ backgroundGradient: '#faf5ff',
3315
+ text: '#1e293b',
3316
+ textSecondary: '#64748b',
3317
+ userBubble: 'linear-gradient(135deg, #8b5cf6, #c084fc)',
3318
+ aiBubble: '#ffffff',
3319
+ headerGradient: 'linear-gradient(90deg, rgba(139, 92, 246, 0.08), rgba(192, 132, 252, 0.08))',
3320
+ border: 'rgba(139, 92, 246, 0.15)',
3321
+ shadow: 'rgba(139, 92, 246, 0.12)',
3322
+ inputBackground: '#faf5ff',
3323
+ inputBorder: '#ddd6fe',
3324
+ loading: '#64748b',
3325
+ success: '#52c41a',
3326
+ error: '#ff4d4f',
3327
+ warning: '#faad14'
3328
+ },
3329
+ borderRadius: {
3330
+ dialog: '22px',
3331
+ bubble: '16px',
3332
+ button: '50%',
3333
+ input: '32px',
3334
+ small: '8px',
3335
+ medium: '16px'
3336
+ },
3337
+ fontSize: {
3338
+ title: '15px',
3339
+ welcomeText: '17px',
3340
+ welcomeDesc: '13px',
3341
+ message: '14px',
3342
+ time: '11px',
3343
+ small: '11px'
3344
+ },
3345
+ boxShadow: {
3346
+ dialog: '0 0 30px rgba(139, 92, 246, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3347
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3348
+ userBubble: '0 4px 12px rgba(139, 92, 246, 0.25)',
3349
+ button: '0 4px 12px rgba(139, 92, 246, 0.35)'
3350
+ }
3351
+ };
3352
+
3353
+ /**
3354
+ * 珊瑚主题(粉橙色)
3355
+ */
3356
+ const CORAL_THEME = {
3357
+ name: 'coral',
3358
+ label: '珊瑚主题',
3359
+ colors: {
3360
+ primary: '#f97316',
3361
+ primaryLight: '#fb923c',
3362
+ primaryDark: '#ea580c',
3363
+ secondary: '#f43f5e',
3364
+ background: '#ffffff',
3365
+ backgroundGradient: '#fff7ed',
3366
+ text: '#1e293b',
3367
+ textSecondary: '#64748b',
3368
+ userBubble: 'linear-gradient(135deg, #f97316, #fb923c)',
3369
+ aiBubble: '#ffffff',
3370
+ headerGradient: 'linear-gradient(90deg, rgba(249, 115, 22, 0.08), rgba(244, 63, 94, 0.08))',
3371
+ border: 'rgba(249, 115, 22, 0.15)',
3372
+ shadow: 'rgba(249, 115, 22, 0.12)',
3373
+ inputBackground: '#fff7ed',
3374
+ inputBorder: '#fed7aa',
3375
+ loading: '#64748b',
3376
+ success: '#52c41a',
3377
+ error: '#ff4d4f',
3378
+ warning: '#faad14'
3379
+ },
3380
+ borderRadius: {
3381
+ dialog: '14px',
3382
+ bubble: '10px',
3383
+ button: '50%',
3384
+ input: '26px',
3385
+ small: '6px',
3386
+ medium: '10px'
3387
+ },
3388
+ fontSize: {
3389
+ title: '15px',
3390
+ welcomeText: '17px',
3391
+ welcomeDesc: '13px',
3392
+ message: '14px',
3393
+ time: '11px',
3394
+ small: '11px'
3395
+ },
3396
+ boxShadow: {
3397
+ dialog: '0 0 30px rgba(249, 115, 22, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3398
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3399
+ userBubble: '0 4px 12px rgba(249, 115, 22, 0.25)',
3400
+ button: '0 4px 12px rgba(249, 115, 22, 0.35)'
3401
+ }
3402
+ };
3403
+
3404
+ /**
3405
+ * 翡翠主题(深绿色)
3406
+ */
3407
+ const JADE_THEME = {
3408
+ name: 'jade',
3409
+ label: '翡翠主题',
3410
+ colors: {
3411
+ primary: '#059669',
3412
+ primaryLight: '#10b981',
3413
+ primaryDark: '#047857',
3414
+ secondary: '#34d399',
3415
+ background: '#ffffff',
3416
+ backgroundGradient: '#f0fdf4',
3417
+ text: '#1e293b',
3418
+ textSecondary: '#64748b',
3419
+ userBubble: 'linear-gradient(135deg, #059669, #10b981)',
3420
+ aiBubble: '#ffffff',
3421
+ headerGradient: 'linear-gradient(90deg, rgba(5, 150, 105, 0.08), rgba(52, 211, 153, 0.08))',
3422
+ border: 'rgba(5, 150, 105, 0.15)',
3423
+ shadow: 'rgba(5, 150, 105, 0.12)',
3424
+ inputBackground: '#f0fdf4',
3425
+ inputBorder: '#bbf7d0',
3426
+ loading: '#64748b',
3427
+ success: '#52c41a',
3428
+ error: '#ff4d4f',
3429
+ warning: '#faad14'
3430
+ },
3431
+ borderRadius: {
3432
+ dialog: '16px',
3433
+ bubble: '12px',
3434
+ button: '50%',
3435
+ input: '28px',
3436
+ small: '6px',
3437
+ medium: '12px'
3438
+ },
3439
+ fontSize: {
3440
+ title: '15px',
3441
+ welcomeText: '17px',
3442
+ welcomeDesc: '13px',
3443
+ message: '14px',
3444
+ time: '11px',
3445
+ small: '11px'
3446
+ },
3447
+ boxShadow: {
3448
+ dialog: '0 0 30px rgba(5, 150, 105, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3449
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3450
+ userBubble: '0 4px 12px rgba(5, 150, 105, 0.25)',
3451
+ button: '0 4px 12px rgba(5, 150, 105, 0.35)'
3452
+ }
3453
+ };
3454
+
3455
+ /**
3456
+ * 星空主题(深蓝紫色)
3457
+ */
3458
+ const STARSKY_THEME = {
3459
+ name: 'starsky',
3460
+ label: '星空主题',
3461
+ colors: {
3462
+ primary: '#6366f1',
3463
+ primaryLight: '#818cf8',
3464
+ primaryDark: '#4f46e5',
3465
+ secondary: '#a855f7',
3466
+ background: '#0f172a',
3467
+ backgroundGradient: '#1e1b4b',
3468
+ text: '#e2e8f0',
3469
+ textSecondary: '#94a3b8',
3470
+ userBubble: 'linear-gradient(135deg, #6366f1, #a855f7)',
3471
+ aiBubble: '#1e1b4b',
3472
+ headerGradient: 'linear-gradient(90deg, rgba(99, 102, 241, 0.15), rgba(168, 85, 247, 0.15))',
3473
+ border: 'rgba(99, 102, 241, 0.3)',
3474
+ shadow: 'rgba(0, 0, 0, 0.3)',
3475
+ inputBackground: '#1e1b4b',
3476
+ inputBorder: '#312e81',
3477
+ loading: '#94a3b8',
3478
+ success: '#34d399',
3479
+ error: '#f87171',
3480
+ warning: '#fbbf24'
3481
+ },
3482
+ borderRadius: {
3483
+ dialog: '18px',
3484
+ bubble: '14px',
3485
+ button: '50%',
3486
+ input: '30px',
3487
+ small: '8px',
3488
+ medium: '14px'
3489
+ },
3490
+ fontSize: {
3491
+ title: '15px',
3492
+ welcomeText: '17px',
3493
+ welcomeDesc: '13px',
3494
+ message: '14px',
3495
+ time: '11px',
3496
+ small: '11px'
3497
+ },
3498
+ boxShadow: {
3499
+ dialog: '0 0 30px rgba(99, 102, 241, 0.2), 0 8px 32px rgba(0, 0, 0, 0.4)',
3500
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.2)',
3501
+ userBubble: '0 4px 12px rgba(99, 102, 241, 0.3)',
3502
+ button: '0 4px 12px rgba(99, 102, 241, 0.4)'
3503
+ }
3504
+ };
3505
+
3506
+ /**
3507
+ * 日落主题(暖色调)
3508
+ */
3509
+ const SUNSET_THEME = {
3510
+ name: 'sunset',
3511
+ label: '日落主题',
3512
+ colors: {
3513
+ primary: '#ea580c',
3514
+ primaryLight: '#f97316',
3515
+ primaryDark: '#c2410c',
3516
+ secondary: '#dc2626',
3517
+ background: '#ffffff',
3518
+ backgroundGradient: '#fff8f0',
3519
+ text: '#1e293b',
3520
+ textSecondary: '#64748b',
3521
+ userBubble: 'linear-gradient(135deg, #ea580c, #dc2626)',
3522
+ aiBubble: '#ffffff',
3523
+ headerGradient: 'linear-gradient(90deg, rgba(234, 88, 12, 0.08), rgba(220, 38, 38, 0.08))',
3524
+ border: 'rgba(234, 88, 12, 0.15)',
3525
+ shadow: 'rgba(234, 88, 12, 0.12)',
3526
+ inputBackground: '#fff8f0',
3527
+ inputBorder: '#fed7aa',
3528
+ loading: '#64748b',
3529
+ success: '#52c41a',
3530
+ error: '#ff4d4f',
3531
+ warning: '#faad14'
3532
+ },
3533
+ borderRadius: {
3534
+ dialog: '16px',
3535
+ bubble: '12px',
3536
+ button: '50%',
3537
+ input: '28px',
3538
+ small: '6px',
3539
+ medium: '12px'
3540
+ },
3541
+ fontSize: {
3542
+ title: '15px',
3543
+ welcomeText: '17px',
3544
+ welcomeDesc: '13px',
3545
+ message: '14px',
3546
+ time: '11px',
3547
+ small: '11px'
3548
+ },
3549
+ boxShadow: {
3550
+ dialog: '0 0 30px rgba(234, 88, 12, 0.12), 0 8px 32px rgba(0, 0, 0, 0.1)',
3551
+ bubble: '0 2px 8px rgba(0, 0, 0, 0.06)',
3552
+ userBubble: '0 4px 12px rgba(234, 88, 12, 0.25)',
3553
+ button: '0 4px 12px rgba(234, 88, 12, 0.35)'
3554
+ }
3555
+ };
3556
+
3557
+ /**
3558
+ * 所有预设主题列表
3559
+ */
3560
+ const PRESET_THEMES = {
3561
+ DEFAULT: DEFAULT_THEME,
3562
+ DARK: DARK_THEME,
3563
+ FRESH: FRESH_THEME,
3564
+ VIBRANT: VIBRANT_THEME,
3565
+ ROMANTIC: ROMANTIC_THEME,
3566
+ PURPLE: PURPLE_THEME,
3567
+ OCEAN: OCEAN_THEME,
3568
+ TWILIGHT: TWILIGHT_THEME,
3569
+ MINT: MINT_THEME,
3570
+ ROSE: ROSE_THEME,
3571
+ AURORA: AURORA_THEME,
3572
+ LAVENDER: LAVENDER_THEME,
3573
+ CORAL: CORAL_THEME,
3574
+ JADE: JADE_THEME,
3575
+ STARSKY: STARSKY_THEME,
3576
+ SUNSET: SUNSET_THEME
3577
+ };
3578
+
2718
3579
  /**
2719
3580
  * AIChatDialog - 纯JavaScript实现的AI对话组件 (Web Components)
2720
3581
  * 样式与功能完全对齐 Vue 版本(1:1 复刻)
2721
3582
  *
2722
3583
  * @author IBC AI Team
2723
- * @version 2.1.0
3584
+ * @version 2.0.5
2724
3585
  */
2725
3586
 
2726
3587
 
@@ -2769,312 +3630,6 @@ const THEME_COLOR_KEY_MAP = {
2769
3630
  };
2770
3631
 
2771
3632
  // ========== 16种预设主题(与Vue版themes.js完全一致) ==========
2772
- const PRESET_THEMES = {
2773
- DEFAULT: {
2774
- colors: {
2775
- primary: '#1890ff',
2776
- primaryLight: '#40a9ff',
2777
- primaryDark: '#096dd9',
2778
- secondary: '#722ed1',
2779
- background: '#ffffff',
2780
- backgroundGradient: '#f8fafc',
2781
- text: '#1e293b',
2782
- textSecondary: '#64748b',
2783
- userBubble: 'linear-gradient(135deg, #1890ff, #40a9ff)',
2784
- aiBubble: '#ffffff',
2785
- headerGradient: 'linear-gradient(90deg, rgba(24,144,255,0.08), rgba(114,46,209,0.08))',
2786
- border: 'rgba(24,144,255,0.15)',
2787
- shadow: 'rgba(24,144,255,0.12)',
2788
- inputBackground: '#f8fafc',
2789
- inputBorder: '#e2e8f0'
2790
- }
2791
- },
2792
- DARK: {
2793
- colors: {
2794
- primary: '#40a9ff',
2795
- primaryLight: '#69c0ff',
2796
- primaryDark: '#1890ff',
2797
- secondary: '#9254de',
2798
- background: '#0f172a',
2799
- backgroundGradient: '#1e293b',
2800
- text: '#f1f5f9',
2801
- textSecondary: '#94a3b8',
2802
- userBubble: 'linear-gradient(135deg, #1890ff, #40a9ff)',
2803
- aiBubble: '#1e293b',
2804
- headerGradient: 'linear-gradient(90deg, rgba(64,169,255,0.15), rgba(146,84,222,0.15))',
2805
- border: 'rgba(64,169,255,0.3)',
2806
- shadow: 'rgba(0,0,0,0.3)',
2807
- inputBackground: '#1e293b',
2808
- inputBorder: '#334155'
2809
- }
2810
- },
2811
- FRESH: {
2812
- colors: {
2813
- primary: '#52c41a',
2814
- primaryLight: '#73d13d',
2815
- primaryDark: '#389e0d',
2816
- secondary: '#1890ff',
2817
- background: '#ffffff',
2818
- backgroundGradient: '#f6ffed',
2819
- text: '#1e293b',
2820
- textSecondary: '#64748b',
2821
- userBubble: 'linear-gradient(135deg, #52c41a, #73d13d)',
2822
- aiBubble: '#ffffff',
2823
- headerGradient: 'linear-gradient(90deg, rgba(82,196,26,0.08), rgba(24,144,255,0.08))',
2824
- border: 'rgba(82,196,26,0.15)',
2825
- shadow: 'rgba(82,196,26,0.12)',
2826
- inputBackground: '#f6ffed',
2827
- inputBorder: '#d9f7be'
2828
- }
2829
- },
2830
- VIBRANT: {
2831
- colors: {
2832
- primary: '#fa8c16',
2833
- primaryLight: '#ffa940',
2834
- primaryDark: '#d46b08',
2835
- secondary: '#f5222d',
2836
- background: '#ffffff',
2837
- backgroundGradient: '#fff7e6',
2838
- text: '#1e293b',
2839
- textSecondary: '#64748b',
2840
- userBubble: 'linear-gradient(135deg, #fa8c16, #ffa940)',
2841
- aiBubble: '#ffffff',
2842
- headerGradient: 'linear-gradient(90deg, rgba(250,140,22,0.08), rgba(245,34,45,0.08))',
2843
- border: 'rgba(250,140,22,0.15)',
2844
- shadow: 'rgba(250,140,22,0.12)',
2845
- inputBackground: '#fff7e6',
2846
- inputBorder: '#ffd591'
2847
- }
2848
- },
2849
- ROMANTIC: {
2850
- colors: {
2851
- primary: '#eb2f96',
2852
- primaryLight: '#fb6f92',
2853
- primaryDark: '#c41d7f',
2854
- secondary: '#722ed1',
2855
- background: '#ffffff',
2856
- backgroundGradient: '#fff0f6',
2857
- text: '#1e293b',
2858
- textSecondary: '#64748b',
2859
- userBubble: 'linear-gradient(135deg, #eb2f96, #fb6f92)',
2860
- aiBubble: '#ffffff',
2861
- headerGradient: 'linear-gradient(90deg, rgba(235,47,150,0.08), rgba(114,46,209,0.08))',
2862
- border: 'rgba(235,47,150,0.15)',
2863
- shadow: 'rgba(235,47,150,0.12)',
2864
- inputBackground: '#fff0f6',
2865
- inputBorder: '#ffadd2'
2866
- }
2867
- },
2868
- PURPLE: {
2869
- colors: {
2870
- primary: '#722ed1',
2871
- primaryLight: '#9254de',
2872
- primaryDark: '#531dab',
2873
- secondary: '#1890ff',
2874
- background: '#ffffff',
2875
- backgroundGradient: '#f9f0ff',
2876
- text: '#1e293b',
2877
- textSecondary: '#64748b',
2878
- userBubble: 'linear-gradient(135deg, #722ed1, #9254de)',
2879
- aiBubble: '#ffffff',
2880
- headerGradient: 'linear-gradient(90deg, rgba(114,46,209,0.08), rgba(24,144,255,0.08))',
2881
- border: 'rgba(114,46,209,0.15)',
2882
- shadow: 'rgba(114,46,209,0.12)',
2883
- inputBackground: '#f9f0ff',
2884
- inputBorder: '#d3adf7'
2885
- }
2886
- },
2887
- OCEAN: {
2888
- colors: {
2889
- primary: '#0077b6',
2890
- primaryLight: '#00b4d8',
2891
- primaryDark: '#023e8a',
2892
- secondary: '#00b4d8',
2893
- background: '#ffffff',
2894
- backgroundGradient: '#f0f9ff',
2895
- text: '#1e293b',
2896
- textSecondary: '#64748b',
2897
- userBubble: 'linear-gradient(135deg, #0077b6, #00b4d8)',
2898
- aiBubble: '#ffffff',
2899
- headerGradient: 'linear-gradient(90deg, rgba(0,119,182,0.08), rgba(0,180,216,0.08))',
2900
- border: 'rgba(0,119,182,0.15)',
2901
- shadow: 'rgba(0,119,182,0.12)',
2902
- inputBackground: '#f0f9ff',
2903
- inputBorder: '#bae6fd'
2904
- }
2905
- },
2906
- TWILIGHT: {
2907
- colors: {
2908
- primary: '#9333ea',
2909
- primaryLight: '#a855f7',
2910
- primaryDark: '#7e22ce',
2911
- secondary: '#ec4899',
2912
- background: '#ffffff',
2913
- backgroundGradient: '#faf5ff',
2914
- text: '#1e293b',
2915
- textSecondary: '#64748b',
2916
- userBubble: 'linear-gradient(135deg, #9333ea, #ec4899)',
2917
- aiBubble: '#ffffff',
2918
- headerGradient: 'linear-gradient(90deg, rgba(147,51,234,0.08), rgba(236,72,153,0.08))',
2919
- border: 'rgba(147,51,234,0.15)',
2920
- shadow: 'rgba(147,51,234,0.12)',
2921
- inputBackground: '#faf5ff',
2922
- inputBorder: '#e9d5ff'
2923
- }
2924
- },
2925
- MINT: {
2926
- colors: {
2927
- primary: '#14b8a6',
2928
- primaryLight: '#2dd4bf',
2929
- primaryDark: '#0d9488',
2930
- secondary: '#06b6d4',
2931
- background: '#ffffff',
2932
- backgroundGradient: '#f0fdfa',
2933
- text: '#1e293b',
2934
- textSecondary: '#64748b',
2935
- userBubble: 'linear-gradient(135deg, #14b8a6, #2dd4bf)',
2936
- aiBubble: '#ffffff',
2937
- headerGradient: 'linear-gradient(90deg, rgba(20,184,166,0.08), rgba(6,182,212,0.08))',
2938
- border: 'rgba(20,184,166,0.15)',
2939
- shadow: 'rgba(20,184,166,0.12)',
2940
- inputBackground: '#f0fdfa',
2941
- inputBorder: '#99f6e4'
2942
- }
2943
- },
2944
- ROSE: {
2945
- colors: {
2946
- primary: '#e11d48',
2947
- primaryLight: '#fb7185',
2948
- primaryDark: '#be123c',
2949
- secondary: '#f43f5e',
2950
- background: '#ffffff',
2951
- backgroundGradient: '#fff1f2',
2952
- text: '#1e293b',
2953
- textSecondary: '#64748b',
2954
- userBubble: 'linear-gradient(135deg, #e11d48, #fb7185)',
2955
- aiBubble: '#ffffff',
2956
- headerGradient: 'linear-gradient(90deg, rgba(225,29,72,0.08), rgba(244,63,94,0.08))',
2957
- border: 'rgba(225,29,72,0.15)',
2958
- shadow: 'rgba(225,29,72,0.12)',
2959
- inputBackground: '#fff1f2',
2960
- inputBorder: '#fecdd3'
2961
- }
2962
- },
2963
- AURORA: {
2964
- colors: {
2965
- primary: '#0ea5e9',
2966
- primaryLight: '#38bdf8',
2967
- primaryDark: '#0284c7',
2968
- secondary: '#10b981',
2969
- background: '#ffffff',
2970
- backgroundGradient: '#f0f9ff',
2971
- text: '#1e293b',
2972
- textSecondary: '#64748b',
2973
- userBubble: 'linear-gradient(135deg, #0ea5e9, #10b981)',
2974
- aiBubble: '#ffffff',
2975
- headerGradient: 'linear-gradient(90deg, rgba(14,165,233,0.08), rgba(16,185,129,0.08))',
2976
- border: 'rgba(14,165,233,0.15)',
2977
- shadow: 'rgba(14,165,233,0.12)',
2978
- inputBackground: '#f0f9ff',
2979
- inputBorder: '#bae6fd'
2980
- }
2981
- },
2982
- LAVENDER: {
2983
- colors: {
2984
- primary: '#8b5cf6',
2985
- primaryLight: '#a78bfa',
2986
- primaryDark: '#7c3aed',
2987
- secondary: '#c084fc',
2988
- background: '#ffffff',
2989
- backgroundGradient: '#faf5ff',
2990
- text: '#1e293b',
2991
- textSecondary: '#64748b',
2992
- userBubble: 'linear-gradient(135deg, #8b5cf6, #c084fc)',
2993
- aiBubble: '#ffffff',
2994
- headerGradient: 'linear-gradient(90deg, rgba(139,92,246,0.08), rgba(192,132,252,0.08))',
2995
- border: 'rgba(139,92,246,0.15)',
2996
- shadow: 'rgba(139,92,246,0.12)',
2997
- inputBackground: '#faf5ff',
2998
- inputBorder: '#ddd6fe'
2999
- }
3000
- },
3001
- CORAL: {
3002
- colors: {
3003
- primary: '#f97316',
3004
- primaryLight: '#fb923c',
3005
- primaryDark: '#ea580c',
3006
- secondary: '#f43f5e',
3007
- background: '#ffffff',
3008
- backgroundGradient: '#fff7ed',
3009
- text: '#1e293b',
3010
- textSecondary: '#64748b',
3011
- userBubble: 'linear-gradient(135deg, #f97316, #fb923c)',
3012
- aiBubble: '#ffffff',
3013
- headerGradient: 'linear-gradient(90deg, rgba(249,115,22,0.08), rgba(244,63,94,0.08))',
3014
- border: 'rgba(249,115,22,0.15)',
3015
- shadow: 'rgba(249,115,22,0.12)',
3016
- inputBackground: '#fff7ed',
3017
- inputBorder: '#fed7aa'
3018
- }
3019
- },
3020
- JADE: {
3021
- colors: {
3022
- primary: '#059669',
3023
- primaryLight: '#10b981',
3024
- primaryDark: '#047857',
3025
- secondary: '#34d399',
3026
- background: '#ffffff',
3027
- backgroundGradient: '#f0fdf4',
3028
- text: '#1e293b',
3029
- textSecondary: '#64748b',
3030
- userBubble: 'linear-gradient(135deg, #059669, #10b981)',
3031
- aiBubble: '#ffffff',
3032
- headerGradient: 'linear-gradient(90deg, rgba(5,150,105,0.08), rgba(52,211,153,0.08))',
3033
- border: 'rgba(5,150,105,0.15)',
3034
- shadow: 'rgba(5,150,105,0.12)',
3035
- inputBackground: '#f0fdf4',
3036
- inputBorder: '#bbf7d0'
3037
- }
3038
- },
3039
- STARSKY: {
3040
- colors: {
3041
- primary: '#6366f1',
3042
- primaryLight: '#818cf8',
3043
- primaryDark: '#4f46e5',
3044
- secondary: '#a855f7',
3045
- background: '#0f172a',
3046
- backgroundGradient: '#1e1b4b',
3047
- text: '#e2e8f0',
3048
- textSecondary: '#94a3b8',
3049
- userBubble: 'linear-gradient(135deg, #6366f1, #a855f7)',
3050
- aiBubble: '#1e1b4b',
3051
- headerGradient: 'linear-gradient(90deg, rgba(99,102,241,0.15), rgba(168,85,247,0.15))',
3052
- border: 'rgba(99,102,241,0.3)',
3053
- shadow: 'rgba(0,0,0,0.3)',
3054
- inputBackground: '#1e1b4b',
3055
- inputBorder: '#312e81'
3056
- }
3057
- },
3058
- SUNSET: {
3059
- colors: {
3060
- primary: '#ea580c',
3061
- primaryLight: '#f97316',
3062
- primaryDark: '#c2410c',
3063
- secondary: '#dc2626',
3064
- background: '#ffffff',
3065
- backgroundGradient: '#fff8f0',
3066
- text: '#1e293b',
3067
- textSecondary: '#64748b',
3068
- userBubble: 'linear-gradient(135deg, #ea580c, #dc2626)',
3069
- aiBubble: '#ffffff',
3070
- headerGradient: 'linear-gradient(90deg, rgba(234,88,12,0.08), rgba(220,38,38,0.08))',
3071
- border: 'rgba(234,88,12,0.15)',
3072
- shadow: 'rgba(234,88,12,0.12)',
3073
- inputBackground: '#fff8f0',
3074
- inputBorder: '#fed7aa'
3075
- }
3076
- }
3077
- };
3078
3633
  class AIChatDialog extends HTMLElement {
3079
3634
  static get observedAttributes() {
3080
3635
  return ['visible'];
@@ -3117,6 +3672,8 @@ class AIChatDialog extends HTMLElement {
3117
3672
  this._runtimeEventsContainerEl = null;
3118
3673
  this._runtimeStatusEl = null;
3119
3674
  this._runtimeCountEl = null;
3675
+ // P3: 页面上下文数据(宿主页面选中的数据)
3676
+ this._context = {};
3120
3677
 
3121
3678
  // ====== 默认主题配置(与 themes.js DEFAULT_THEME 完全一致) ======
3122
3679
  this._themeVars = {
@@ -3203,6 +3760,8 @@ class AIChatDialog extends HTMLElement {
3203
3760
  if (this._handleOffline) window.removeEventListener('offline', this._handleOffline);
3204
3761
  // P2-17: 清理resize监听
3205
3762
  if (this._handleResize) window.removeEventListener('resize', this._handleResize);
3763
+ // P0: 清理拖拽事件监听
3764
+ this._cleanupDragListeners();
3206
3765
  // 暂停后台Token刷新定时器(detach时停止,re-attach时由connectedCallback恢复)
3207
3766
  if (this._chatClient) {
3208
3767
  this._chatClient.stopBackgroundRefresh();
@@ -3245,6 +3804,8 @@ class AIChatDialog extends HTMLElement {
3245
3804
  // P2-17: 应用移动端/桌面端布局(自动全屏或恢复浮动气泡)
3246
3805
  _applyMobileLayout() {
3247
3806
  if (!this._dialog) return;
3807
+ // 行内嵌入模式跳过,保持 CSS 控制的布局
3808
+ if (this.getAttribute('mode') === 'inline') return;
3248
3809
  if (this._isMobile) {
3249
3810
  // 移动端:强制全屏(与Vue版 dialogStyle 一致)
3250
3811
  this._dialog.style.width = '100vw';
@@ -3352,6 +3913,14 @@ class AIChatDialog extends HTMLElement {
3352
3913
  authFn: null,
3353
3914
  onLoad: null,
3354
3915
  onError: null,
3916
+ // P3: 发送前回调 - 每发消息前调用,可返回最新页面上下文(与 setContext 合并,此回调结果优先)
3917
+ onBeforeSend: null,
3918
+ // P3: 上下文注入方式 — 'prompt'=拼到用户消息前面(默认), 'bizParams'=注入到 bizParams JSON 中
3919
+ contextMode: 'prompt',
3920
+ // P4: 调试模式 - 开启后 console 打印请求/响应/SSE/上下文合并全过程
3921
+ debug: false,
3922
+ // P4: 自定义消息操作按钮 — 返回数组 [{label, className?, onClick}] 在 AI 消息下方渲染
3923
+ onMessageActions: null,
3355
3924
  suggestions: [{
3356
3925
  label: '帮我购买一台轻量应用服务器用于部署应用',
3357
3926
  value: '帮我购买一台轻量应用服务器用于部署应用'
@@ -3365,6 +3934,11 @@ class AIChatDialog extends HTMLElement {
3365
3934
  this._chatClient = new AIChatClient(this._config);
3366
3935
  if (this._conversationId) this._chatClient.setConversationId(this._conversationId);
3367
3936
  this.applyConfig();
3937
+ // P1-2 fix: 在 _config 就绪后初始化拖拽,以正确读取 enableDrag
3938
+ if (this._config?.enableDrag !== false && this.getAttribute('mode') !== 'inline' && !this._dragInitialized) {
3939
+ this.initDraggable();
3940
+ }
3941
+ this._debugLog('初始化完成', 'appId=' + this._config.appId, 'userId=' + this._config.userId, 'baseUrl=' + this._config.apiBaseUrl, 'debug=' + !!this._config.debug, 'contextMode=' + (this._config.contextMode || 'prompt'));
3368
3942
  this.loadAppDetail();
3369
3943
  // 触发 onLoad 回调(与Vue版一致)
3370
3944
  if (this._config?.onLoad) this._config.onLoad(this);
@@ -3451,6 +4025,44 @@ class AIChatDialog extends HTMLElement {
3451
4025
  return this;
3452
4026
  }
3453
4027
 
4028
+ /**
4029
+ * 设置页面上下文数据(宿主页面选中的数据,随每次请求发送)
4030
+ * @param {Object} context - 上下文数据对象,传 null 清空
4031
+ * @returns {this}
4032
+ */
4033
+ setContext(context) {
4034
+ this._context = context && typeof context === 'object' ? {
4035
+ ...context
4036
+ } : {};
4037
+ return this;
4038
+ }
4039
+
4040
+ /**
4041
+ * 获取当前上下文数据
4042
+ * @returns {Object}
4043
+ */
4044
+ getContext() {
4045
+ return {
4046
+ ...this._context
4047
+ };
4048
+ }
4049
+
4050
+ /**
4051
+ * 外部触发表态发送(业务层主动调用,如点击外部按钮触发 AI 请求)
4052
+ * @param {string} text - 要发送的消息内容
4053
+ * @returns {this}
4054
+ */
4055
+ send(text) {
4056
+ const content = String(text || '').trim();
4057
+ if (!content) return this;
4058
+ if (this._input) this._input.value = content;
4059
+ this._autoResizeInput();
4060
+ this._updateSendButtonState();
4061
+ this._lastSendTime = 0; // 跳过防抖,外部触发视为独立操作
4062
+ this.handleSend();
4063
+ return this;
4064
+ }
4065
+
3454
4066
  // ==================== 显示/隐藏 ====================
3455
4067
 
3456
4068
  showDialog() {
@@ -3506,11 +4118,11 @@ class AIChatDialog extends HTMLElement {
3506
4118
  /* ====== 对话框主体(与Vue .ai-chat-dialog 完全一致) ====== */
3507
4119
  .ai-chat-dialog {
3508
4120
  position: fixed;
3509
- top: 96px;
3510
- right: 24px;
4121
+ top: var(--ai-dialog-top, 96px);
4122
+ right: var(--ai-dialog-right, 24px);
3511
4123
  bottom: auto;
3512
- width: min(420px, calc(100vw - 48px));
3513
- height: min(640px, 80vh);
4124
+ width: var(--ai-dialog-width, min(420px, calc(100vw - 48px)));
4125
+ height: var(--ai-dialog-height, min(640px, 80vh));
3514
4126
  background: var(--ai-bg);
3515
4127
  border-radius: 16px;
3516
4128
  border: 1px solid rgba(226, 232, 240, 0.78);
@@ -4086,6 +4698,19 @@ class AIChatDialog extends HTMLElement {
4086
4698
  color: var(--ai-success);
4087
4699
  background: rgba(16, 185, 129, 0.08);
4088
4700
  }
4701
+ /* P4: 自定义操作按钮(文本标签型,宽度自适应) */
4702
+ .action-icon.custom-action-btn {
4703
+ width: auto;
4704
+ padding: 4px 10px;
4705
+ font-size: 12px;
4706
+ font-weight: 500;
4707
+ white-space: nowrap;
4708
+ }
4709
+ .action-icon.custom-action-btn:hover {
4710
+ color: #fff;
4711
+ background: var(--ai-primary);
4712
+ transform: translateY(-1px);
4713
+ }
4089
4714
 
4090
4715
  /* ========== 底部输入框(与Vue .dialog-footer 一致) ========== */
4091
4716
  .dialog-footer {
@@ -4500,6 +5125,27 @@ class AIChatDialog extends HTMLElement {
4500
5125
  .action-icon { width: 30px; height: 30px; }
4501
5126
  }
4502
5127
 
5128
+ /* ========== 行内嵌入模式(mode="inline",挂载到用户指定容器) ========== */
5129
+ :host([mode="inline"]) { position: relative; top: auto; right: auto; bottom: auto; z-index: auto; width: 100%; height: 100%; }
5130
+ :host([mode="inline"]) .ai-float-container { height: 100%; }
5131
+ :host([mode="inline"]) .float-button { display: none; }
5132
+ :host([mode="inline"]) .ai-chat-dialog {
5133
+ position: relative;
5134
+ top: auto;
5135
+ right: auto;
5136
+ width: 100%;
5137
+ height: 100%;
5138
+ border-radius: 0;
5139
+ box-shadow: none;
5140
+ border: 1px solid var(--ai-border);
5141
+ opacity: 1;
5142
+ transform: none;
5143
+ pointer-events: auto;
5144
+ }
5145
+ :host([mode="inline"]) .dialog-header { cursor: default; }
5146
+ :host([mode="inline"]) .close-btn,
5147
+ :host([mode="inline"]) .expand-btn { display: none; }
5148
+
4503
5149
  /* ========== 暗色主题覆盖(theme: 'dark') ========== */
4504
5150
  :host(.dark-theme) .ai-chat-dialog {
4505
5151
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
@@ -4645,8 +5291,8 @@ class AIChatDialog extends HTMLElement {
4645
5291
  });
4646
5292
  }
4647
5293
 
4648
- // 拖拽
4649
- if (this._dialog && this._config?.enableDrag !== false) this.initDraggable();
5294
+ // 拖拽(内嵌模式禁用)— 由 init() 在 _config 就绪后触发
5295
+ // initDraggable 延迟到 init() 中执行以免 _config 为空
4650
5296
  });
4651
5297
  }
4652
5298
 
@@ -4699,59 +5345,97 @@ class AIChatDialog extends HTMLElement {
4699
5345
  initDraggable() {
4700
5346
  const header = this.shadowRoot?.querySelector('.dialog-header');
4701
5347
  if (!header) return;
5348
+
5349
+ // 清理上一次遗留的 document 级监听(SPA 路由切换场景)
5350
+ this._cleanupDragListeners();
5351
+ const self = this;
4702
5352
  const startDrag = (clientX, clientY) => {
4703
5353
  // P2-17: 移动端禁用拖拽(与Vue版 startTouchDrag: if(isExpanded || isMobile) return 一致)
4704
- if (this._isExpanded || this._isMobile) return;
4705
- this._isDragging = true;
4706
- this._dialog.classList.add('dragging');
4707
- const rect = this._dialog.getBoundingClientRect();
4708
- this._dragStartX = clientX - rect.left;
4709
- this._dragStartY = clientY - rect.top;
5354
+ if (self._isExpanded || self._isMobile) return;
5355
+ self._isDragging = true;
5356
+ self._dialog.classList.add('dragging');
5357
+ const rect = self._dialog.getBoundingClientRect();
5358
+ self._dragStartX = clientX - rect.left;
5359
+ self._dragStartY = clientY - rect.top;
4710
5360
  };
4711
5361
  const onDrag = (clientX, clientY) => {
4712
- if (!this._isDragging) return;
4713
- this._dialogX = clientX - this._dragStartX;
4714
- this._dialogY = clientY - this._dragStartY;
4715
- this._dialog.style.left = this._dialogX + 'px';
4716
- this._dialog.style.right = 'auto';
4717
- this._dialog.style.top = this._dialogY + 'px';
4718
- this._dialog.style.bottom = 'auto';
5362
+ if (!self._isDragging) return;
5363
+ self._dialogX = clientX - self._dragStartX;
5364
+ self._dialogY = clientY - self._dragStartY;
5365
+ self._dialog.style.left = self._dialogX + 'px';
5366
+ self._dialog.style.right = 'auto';
5367
+ self._dialog.style.top = self._dialogY + 'px';
5368
+ self._dialog.style.bottom = 'auto';
4719
5369
  };
4720
5370
  const stopDrag = () => {
4721
- if (this._isDragging) {
4722
- this._isDragging = false;
4723
- this._dialog.classList.remove('dragging');
5371
+ if (self._isDragging) {
5372
+ self._isDragging = false;
5373
+ self._dialog.classList.remove('dragging');
4724
5374
  }
4725
5375
  };
4726
5376
 
4727
- // 鼠标事件(桌面端)
4728
- header.addEventListener('mousedown', e => {
5377
+ // 鼠标事件(桌面端)— 保存引用以便清理
5378
+ this._dragMouseDownHandler = e => {
4729
5379
  if (e.target.closest('button, .header-icon')) return;
4730
5380
  startDrag(e.clientX, e.clientY);
4731
5381
  e.preventDefault();
4732
- });
4733
- document.addEventListener('mousemove', e => {
5382
+ };
5383
+ this._dragMouseMoveHandler = e => {
4734
5384
  onDrag(e.clientX, e.clientY);
4735
- });
4736
- document.addEventListener('mouseup', stopDrag);
5385
+ };
5386
+ this._dragMouseUpHandler = stopDrag;
5387
+ header.addEventListener('mousedown', this._dragMouseDownHandler);
5388
+ document.addEventListener('mousemove', this._dragMouseMoveHandler);
5389
+ document.addEventListener('mouseup', this._dragMouseUpHandler);
4737
5390
 
4738
5391
  // P1-7: 触摸事件(移动端,与Vue版一致)
4739
- header.addEventListener('touchstart', e => {
4740
- if (this._isExpanded) return;
5392
+ this._dragTouchStartHandler = e => {
5393
+ if (self._isExpanded) return;
4741
5394
  if (e.target.closest('button, .header-icon')) return;
4742
5395
  const touch = e.touches[0];
4743
5396
  startDrag(touch.clientX, touch.clientY);
4744
- }, {
4745
- passive: true
4746
- });
4747
- document.addEventListener('touchmove', e => {
4748
- if (!this._isDragging) return;
5397
+ };
5398
+ this._dragTouchMoveHandler = e => {
5399
+ if (!self._isDragging) return;
4749
5400
  const touch = e.touches[0];
4750
5401
  onDrag(touch.clientX, touch.clientY);
4751
- }, {
5402
+ };
5403
+ this._dragTouchEndHandler = stopDrag;
5404
+ header.addEventListener('touchstart', this._dragTouchStartHandler, {
5405
+ passive: true
5406
+ });
5407
+ document.addEventListener('touchmove', this._dragTouchMoveHandler, {
4752
5408
  passive: true
4753
5409
  });
4754
- document.addEventListener('touchend', stopDrag);
5410
+ document.addEventListener('touchend', this._dragTouchEndHandler);
5411
+ this._dragInitialized = true;
5412
+ }
5413
+
5414
+ // P0: 清理拖拽事件监听(SPA disconnectedCallback / re-init 调用)
5415
+ _cleanupDragListeners() {
5416
+ if (this._dragMouseDownHandler) {
5417
+ const header = this.shadowRoot?.querySelector('.dialog-header');
5418
+ if (header) {
5419
+ header.removeEventListener('mousedown', this._dragMouseDownHandler);
5420
+ header.removeEventListener('touchstart', this._dragTouchStartHandler, {
5421
+ passive: true
5422
+ });
5423
+ }
5424
+ this._dragMouseDownHandler = null;
5425
+ this._dragTouchStartHandler = null;
5426
+ }
5427
+ if (this._dragMouseMoveHandler) {
5428
+ document.removeEventListener('mousemove', this._dragMouseMoveHandler);
5429
+ document.removeEventListener('touchmove', this._dragTouchMoveHandler, {
5430
+ passive: true
5431
+ });
5432
+ document.removeEventListener('mouseup', this._dragMouseUpHandler);
5433
+ document.removeEventListener('touchend', this._dragTouchEndHandler);
5434
+ this._dragMouseMoveHandler = null;
5435
+ this._dragTouchMoveHandler = null;
5436
+ this._dragMouseUpHandler = null;
5437
+ this._dragTouchEndHandler = null;
5438
+ }
4755
5439
  }
4756
5440
  applyConfig() {
4757
5441
  if (!this._config) return;
@@ -4916,11 +5600,12 @@ class AIChatDialog extends HTMLElement {
4916
5600
  <div class="message-content">
4917
5601
  ${hasRuntimeEvents ? this._buildRuntimePanel(msg, idx) : ''}
4918
5602
  ${bubbleContent ? `<div class="${bubbleClass}">${bubbleContent}</div>` : ''}
4919
- ${!msg._isStreaming ? `<div class="message-actions-bar">
4920
- ${msg.type === 'ai' ? `<div class="action-icons">
4921
- <span class="action-icon copy-btn${msg.copied ? ' copied' : ''}" data-idx="${idx}" title="${msg.copied ? '已复制' : '复制'}">${msg.copied ? '<svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="m20 6-11 11-5-5"/></svg>' : '<svg viewBox="0 0 24 24" fill="none" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'}</span>
4922
- <span class="action-icon regenerate-btn" data-idx="${idx}" title="重新回答"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 3v6h-6"/></svg></span>
4923
- </div>` : ''}
5603
+ ${!msg._isStreaming ? `<div class="message-actions-bar">
5604
+ ${msg.type === 'ai' ? `<div class="action-icons">
5605
+ <span class="action-icon copy-btn${msg.copied ? ' copied' : ''}" data-idx="${idx}" title="${msg.copied ? '已复制' : '复制'}">${msg.copied ? '<svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="m20 6-11 11-5-5"/></svg>' : '<svg viewBox="0 0 24 24" fill="none" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'}</span>
5606
+ <span class="action-icon regenerate-btn" data-idx="${idx}" title="重新回答"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 3v6h-6"/></svg></span>
5607
+ ${this._buildCustomActions(msg, idx)}
5608
+ </div>` : ''}
4924
5609
  </div>` : ''}
4925
5610
  </div>
4926
5611
  </div>`;
@@ -4950,6 +5635,24 @@ class AIChatDialog extends HTMLElement {
4950
5635
  });
4951
5636
  });
4952
5637
 
5638
+ // P4: 绑定自定义操作按钮
5639
+ this._body.querySelectorAll('.custom-action-btn').forEach(btn => {
5640
+ btn.addEventListener('click', () => {
5641
+ const idx = parseInt(btn.dataset.idx);
5642
+ const actionIdx = parseInt(btn.dataset.action);
5643
+ const msg = this._messages[idx];
5644
+ if (!msg) return;
5645
+ try {
5646
+ const actions = this._getCustomActions(msg, idx);
5647
+ if (actions && actions[actionIdx]) {
5648
+ actions[actionIdx].onClick(msg.content);
5649
+ }
5650
+ } catch (e) {
5651
+ console.warn('[AIChatDialog] 自定义操作按钮执行失败:', e);
5652
+ }
5653
+ });
5654
+ });
5655
+
4953
5656
  // 绑定执行过程面板折叠/展开(Shadow DOM 下 inline onclick 不可靠)
4954
5657
  this._body.querySelectorAll('.runtime-header').forEach(header => {
4955
5658
  header.addEventListener('click', () => {
@@ -5202,7 +5905,7 @@ class AIChatDialog extends HTMLElement {
5202
5905
  this.showToast(this._config?.emptyMessageError || '请输入消息内容');
5203
5906
  return;
5204
5907
  }
5205
- if (this._isLoading) return;
5908
+ if (this._isLoading || this._streaming) return;
5206
5909
 
5207
5910
  // P1-11: 字数限制检查
5208
5911
  const maxLen = this._config?.maxLength ?? 500;
@@ -5247,10 +5950,59 @@ class AIChatDialog extends HTMLElement {
5247
5950
  }));
5248
5951
  this.setLoading(true);
5249
5952
  try {
5953
+ // 上传附件并收集 attachmentId
5954
+ const attachmentIds = [];
5250
5955
  for (const file of attachments) {
5251
- await this.uploadAttachment(file).catch(err => console.warn('[AIChatDialog] 附件上传失败:', err));
5956
+ const res = await this.uploadAttachment(file).catch(err => {
5957
+ console.warn('[AIChatDialog] 附件上传失败:', err);
5958
+ return null;
5959
+ });
5960
+ if (res?.success && res.data?.attachmentId) {
5961
+ attachmentIds.push(res.data.attachmentId);
5962
+ }
5963
+ }
5964
+
5965
+ // P3: 收集页面上下文(setContext + onBeforeSend)
5966
+ let context = {
5967
+ ...(this._context || {})
5968
+ };
5969
+ if (typeof this._config?.onBeforeSend === 'function') {
5970
+ try {
5971
+ const dynamicCtx = await this._config.onBeforeSend();
5972
+ if (dynamicCtx && typeof dynamicCtx === 'object') {
5973
+ context = {
5974
+ ...context,
5975
+ ...dynamicCtx
5976
+ };
5977
+ }
5978
+ } catch (e) {
5979
+ console.warn('[AIChatDialog] onBeforeSend 执行失败:', e);
5980
+ }
5981
+ }
5982
+ const hasContext = context && Object.keys(context).length > 0;
5983
+ const rawMode = this._config?.contextMode;
5984
+ const VALID_CONTEXT_MODES = ['prompt', 'bizParams'];
5985
+ const contextMode = VALID_CONTEXT_MODES.includes(rawMode) ? rawMode : rawMode ? (console.warn('[AIChatDialog] 无效的 contextMode: "' + rawMode + '",已回退为 "prompt"(有效值: ' + VALID_CONTEXT_MODES.join(', ') + ')'), 'prompt') : 'prompt';
5986
+ let finalPrompt = content || attachmentText;
5987
+ let contextForBizParams = null;
5988
+ if (hasContext) {
5989
+ this._debugLog('上下文合并', 'keys=', Object.keys(context), 'mode=', contextMode);
5990
+ if (contextMode === 'bizParams') {
5991
+ // 注入到 bizParams
5992
+ contextForBizParams = context;
5993
+ } else {
5994
+ // 默认:拼到 prompt 前面
5995
+ const ctxText = this._formatContextText(context);
5996
+ if (ctxText) {
5997
+ finalPrompt = ctxText + '\n---\n' + finalPrompt;
5998
+ }
5999
+ }
5252
6000
  }
5253
- await this.sendMessageToAI(content || attachmentText);
6001
+ this._debugLog('发送', 'prompt=' + (finalPrompt || '').substring(0, 150) + (finalPrompt && finalPrompt.length > 150 ? '...' : ''), 'ctxKeys=' + Object.keys(contextForBizParams || context || {}).join(','), 'mode=' + contextMode);
6002
+ await this.sendMessageToAI(finalPrompt, {
6003
+ attachmentIds,
6004
+ context: contextForBizParams
6005
+ });
5254
6006
  } catch (err) {
5255
6007
  console.error('[AIChatDialog] Error:', err);
5256
6008
  this.handleError(err);
@@ -5279,7 +6031,12 @@ class AIChatDialog extends HTMLElement {
5279
6031
  this._streaming = true;
5280
6032
  this.setLoading(false); // 关掉全局 loading,用消息级 loading 替代
5281
6033
  this.updateLoadingUI(); // re-lock input via _streaming flag
5282
- await this._directApiCallStream(msgId, content, options.streamOptions || {});
6034
+ const streamOpts = {
6035
+ ...(options.streamOptions || {})
6036
+ };
6037
+ if (options.attachmentIds?.length) streamOpts.attachmentIds = options.attachmentIds;
6038
+ if (options.context) streamOpts.context = options.context;
6039
+ await this._directApiCallStream(msgId, content, streamOpts);
5283
6040
  }
5284
6041
  }
5285
6042
  async mockResponse(userContent) {
@@ -5336,6 +6093,10 @@ class AIChatDialog extends HTMLElement {
5336
6093
  if (!this._chatClient) {
5337
6094
  this._chatClient = new AIChatClient(this._config || {});
5338
6095
  }
6096
+ if (this._config?.debug) {
6097
+ this._chatClient.config.debug = true;
6098
+ }
6099
+ this._debugLog('Stream开始', 'msgId=' + msgId, 'content=' + (content || '').substring(0, 80), 'opts=' + JSON.stringify(requestOptions));
5339
6100
  if (Object.prototype.hasOwnProperty.call(requestOptions, 'conversationId')) {
5340
6101
  this._chatClient.setConversationId(requestOptions.conversationId || null);
5341
6102
  } else if (this._conversationId) {
@@ -5454,6 +6215,7 @@ class AIChatDialog extends HTMLElement {
5454
6215
  }
5455
6216
  }, requestOptions);
5456
6217
  if (response?.success) {
6218
+ this._debugLog('Stream完成', 'ok, conversationId=' + (response.data?.conversationId || this._conversationId), 'textLen=' + streamText.length);
5457
6219
  if (response.data?.conversationId) {
5458
6220
  this._conversationId = response.data.conversationId;
5459
6221
  } else if (this._chatClient.conversationId) {
@@ -5466,6 +6228,7 @@ class AIChatDialog extends HTMLElement {
5466
6228
  streamText = result;
5467
6229
  }
5468
6230
  } else {
6231
+ this._finalizeMsg(msgId, streamText || '(无回复)');
5469
6232
  return;
5470
6233
  }
5471
6234
  this._finalizeMsg(msgId, streamText || '(无回复)');
@@ -5476,12 +6239,14 @@ class AIChatDialog extends HTMLElement {
5476
6239
  }));
5477
6240
  if (this._config?.onMessageReceived) this._config.onMessageReceived(streamText);
5478
6241
  } catch (e) {
6242
+ this._debugWarn('Stream异常', e.name + ': ' + (e.message || ''));
5479
6243
  if (e.name === 'AbortError') {
5480
6244
  this._updateMsg(msgId, '(请求超时或已取消)');
5481
6245
  } else {
5482
6246
  console.error('[AIChatDialog] 流式失败:', e);
5483
6247
  this._updateMsg(msgId, '网络错误: ' + (e.message || '请检查连接'));
5484
6248
  }
6249
+ this._finalizeMsg(msgId, this._messages.find(m => m.id === msgId)?.content || '(无回复)');
5485
6250
  }
5486
6251
  }
5487
6252
  _getStreamTextEl() {
@@ -5566,7 +6331,7 @@ class AIChatDialog extends HTMLElement {
5566
6331
  this._messages[idx].content = content;
5567
6332
  delete this._messages[idx]._isStreaming;
5568
6333
  this._messages[idx].time = this.formatTime();
5569
- this._messages[idx]._status = content && !content.startsWith('(') ? 'completed' : 'error';
6334
+ this._messages[idx]._status = this._isErrorContent(content) ? 'error' : 'completed';
5570
6335
  if (this._streamTypeRaf) {
5571
6336
  cancelAnimationFrame(this._streamTypeRaf);
5572
6337
  this._streamTypeRaf = null;
@@ -5598,6 +6363,34 @@ class AIChatDialog extends HTMLElement {
5598
6363
  return false;
5599
6364
  }
5600
6365
 
6366
+ // 判断错误标记内容(替换脆弱的前缀检查 startsWith('('))
6367
+ _ERROR_CONTENT_PATTERNS = ['(请求超时', '(等待AI处理', '(无回复)', '(已停止)', '(AI 处理超时', '错误:', '网络错误:', '抱歉,'];
6368
+ _isErrorContent(content) {
6369
+ if (!content) return true;
6370
+ return this._ERROR_CONTENT_PATTERNS.some(p => content.startsWith(p));
6371
+ }
6372
+
6373
+ // P3: 将上下文对象格式化为可读文本(拼入 prompt 前缀)
6374
+ _formatContextText(context) {
6375
+ if (!context || typeof context !== 'object') return '';
6376
+ const lines = ['[页面上下文]'];
6377
+ for (const key in context) {
6378
+ if (!Object.prototype.hasOwnProperty.call(context, key)) continue;
6379
+ const val = context[key];
6380
+ if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'object') {
6381
+ lines.push(key + ':');
6382
+ for (const item of val) {
6383
+ lines.push(' - ' + Object.values(item).join(' | '));
6384
+ }
6385
+ } else if (typeof val === 'object' && val !== null) {
6386
+ lines.push(key + ': ' + JSON.stringify(val));
6387
+ } else {
6388
+ lines.push(key + ': ' + val);
6389
+ }
6390
+ }
6391
+ return lines.join('\n');
6392
+ }
6393
+
5601
6394
  // 运行时事件管理 – incremental DOM, no renderMessages
5602
6395
  _addRuntimeEvent(msgId, event) {
5603
6396
  const idx = this._messages.findIndex(m => m.id === msgId);
@@ -5606,8 +6399,6 @@ class AIChatDialog extends HTMLElement {
5606
6399
  const last = this._messages[idx]._runtimeEvents[this._messages[idx]._runtimeEvents.length - 1];
5607
6400
  if (last && last._text === event._text) return;
5608
6401
  this._messages[idx]._runtimeEvents.push(event);
5609
- !['NODE_COMPLETE', 'TOOL_END', 'TOOL_RESULT'].includes(event._type);
5610
-
5611
6402
  // Path 1: panel DOM exists and is alive – incremental append
5612
6403
  if (this._runtimeEventsContainerEl && this._body.contains(this._runtimeEventsContainerEl)) {
5613
6404
  const row = document.createElement('div');
@@ -5694,6 +6485,30 @@ class AIChatDialog extends HTMLElement {
5694
6485
  panel.appendChild(eventsContainer);
5695
6486
  return panel;
5696
6487
  }
6488
+
6489
+ // P4: 获取自定义操作按钮列表(缓存结果避免重复调用 onMessageActions)
6490
+ _getCustomActions(msg, idx) {
6491
+ const key = '_customActions_' + idx;
6492
+ if (!msg[key] && typeof this._config?.onMessageActions === 'function') {
6493
+ try {
6494
+ msg[key] = this._config.onMessageActions(msg, idx) || [];
6495
+ } catch (e) {
6496
+ console.warn('[AIChatDialog] onMessageActions 执行失败:', e);
6497
+ msg[key] = [];
6498
+ }
6499
+ }
6500
+ return msg[key] || [];
6501
+ }
6502
+
6503
+ // P4: 生成自定义操作按钮 HTML
6504
+ _buildCustomActions(msg, idx) {
6505
+ const actions = this._getCustomActions(msg, idx);
6506
+ if (!actions.length) return '';
6507
+ return actions.map((a, i) => {
6508
+ const cls = a.className || '';
6509
+ return '<span class="action-icon custom-action-btn' + (cls ? ' ' + this.escapeAttr(cls) : '') + '" data-idx="' + idx + '" data-action="' + i + '" title="' + this.escapeAttr(a.label) + '">' + this.escapeHtml(a.label) + '</span>';
6510
+ }).join('');
6511
+ }
5697
6512
  _buildRuntimePanel(msg, msgIdx) {
5698
6513
  const events = msg._runtimeEvents || [];
5699
6514
  if (events.length === 0) return '';
@@ -5730,7 +6545,7 @@ class AIChatDialog extends HTMLElement {
5730
6545
  // ==================== 重新生成(与Vue regenerate 一致) ====================
5731
6546
 
5732
6547
  async regenerate(index) {
5733
- if (this._isLoading) return;
6548
+ if (this._isLoading || this._streaming) return;
5734
6549
  const aiMessage = this._messages[index];
5735
6550
  if (!aiMessage || aiMessage.type !== 'ai') return;
5736
6551
  let userQuestion = '';
@@ -5918,6 +6733,20 @@ class AIChatDialog extends HTMLElement {
5918
6733
  const n = new Date();
5919
6734
  return `${String(n.getHours()).padStart(2, '0')}:${String(n.getMinutes()).padStart(2, '0')}`;
5920
6735
  }
6736
+
6737
+ /**
6738
+ * 调试日志(仅在 config.debug === true 时输出)
6739
+ */
6740
+ _debugLog(tag, ...args) {
6741
+ if (this._config?.debug) {
6742
+ console.log('%c[AI-SDK]%c ' + tag, 'color:#2563eb;font-weight:600', 'color:inherit', ...args);
6743
+ }
6744
+ }
6745
+ _debugWarn(tag, ...args) {
6746
+ if (this._config?.debug) {
6747
+ console.warn('%c[AI-SDK]%c ' + tag, 'color:#f59e0b;font-weight:600', 'color:inherit', ...args);
6748
+ }
6749
+ }
5921
6750
  escapeHtml(s) {
5922
6751
  if (!s) return '';
5923
6752
  const d = document.createElement('div');
@@ -6008,7 +6837,7 @@ if (typeof window !== 'undefined') {
6008
6837
  * 支持任意前端框架:Vue, React, Angular, 原生HTML等
6009
6838
  *
6010
6839
  * @author IBC AI Team
6011
- * @version 2.0.0
6840
+ * @version 2.0.5
6012
6841
  */
6013
6842
 
6014
6843
 
@@ -6020,16 +6849,53 @@ function createAIChatDialog(options = {}) {
6020
6849
  // 设置属性
6021
6850
  if (options.title) dialog.setAttribute('title', options.title);
6022
6851
  if (options.placeholder) dialog.setAttribute('placeholder', options.placeholder);
6023
- if (options.width) dialog.style.setProperty('--dialog-width', `${options.width}px`);
6024
- if (options.height) dialog.style.setProperty('--dialog-height', `${options.height}px`);
6025
-
6026
- // 添加到DOM(隐藏状态)
6027
- document.body.appendChild(dialog);
6852
+ // 对话框尺寸和位置(浮窗模式可用)
6853
+ if (options.width) dialog.style.setProperty('--ai-dialog-width', typeof options.width === 'number' ? `${options.width}px` : options.width);
6854
+ if (options.height) dialog.style.setProperty('--ai-dialog-height', typeof options.height === 'number' ? `${options.height}px` : options.height);
6855
+ if (options.top != null) dialog.style.setProperty('--ai-dialog-top', typeof options.top === 'number' ? `${options.top}px` : options.top);
6856
+ if (options.right != null) dialog.style.setProperty('--ai-dialog-right', typeof options.right === 'number' ? `${options.right}px` : options.right);
6857
+
6858
+ // 挂载到指定容器或 body
6859
+ if (options.target) {
6860
+ const container = typeof options.target === 'string' ? document.querySelector(options.target) : options.target;
6861
+ if (container) {
6862
+ dialog.setAttribute('mode', 'inline');
6863
+ container.appendChild(dialog);
6864
+ } else {
6865
+ console.warn('[AI Web SDK] target 容器未找到,回退到 body');
6866
+ document.body.appendChild(dialog);
6867
+ }
6868
+ } else {
6869
+ document.body.appendChild(dialog);
6870
+ }
6028
6871
 
6029
6872
  // 初始化配置
6030
6873
  if (Object.keys(options).length > 0) {
6031
6874
  dialog.init(options);
6032
6875
  }
6876
+
6877
+ // 行内模式:挂载到 DOM 后直接展开
6878
+ if (options.target) {
6879
+ requestAnimationFrame(() => {
6880
+ dialog.style.position = 'relative';
6881
+ dialog.style.height = '100%';
6882
+ if (dialog.shadowRoot) {
6883
+ const container = dialog.shadowRoot.querySelector('.ai-float-container');
6884
+ const dialogEl = dialog.shadowRoot.querySelector('.ai-chat-dialog');
6885
+ const btn = dialog.shadowRoot.querySelector('.float-button');
6886
+ if (container) container.style.height = '100%';
6887
+ if (btn) btn.style.display = 'none';
6888
+ if (dialogEl) {
6889
+ dialogEl.style.position = 'relative';
6890
+ dialogEl.style.top = 'auto';
6891
+ dialogEl.style.right = 'auto';
6892
+ dialogEl.style.width = '100%';
6893
+ dialogEl.style.height = '100%';
6894
+ dialogEl.classList.add('dialog-visible');
6895
+ }
6896
+ }
6897
+ });
6898
+ }
6033
6899
  return dialog;
6034
6900
  }
6035
6901
 
@@ -6188,12 +7054,10 @@ function useAIChat(options = {}) {
6188
7054
  },
6189
7055
  sendMessage: content => {
6190
7056
  if (dialogRef.current) {
6191
- const input = dialogRef.current.shadowRoot?.querySelector('.ai-textarea');
7057
+ const input = dialogRef.current.shadowRoot?.querySelector('.message-input');
6192
7058
  if (input) {
6193
- input.value = content;
6194
- input.dispatchEvent(new Event('input'));
7059
+ dialogRef.current.send?.(content) || (input.value = content, input.dispatchEvent(new Event('input')), dialogRef.current.shadowRoot?.querySelector('.send-btn')?.click());
6195
7060
  }
6196
- dialogRef.current.handleSend?.() || dialogRef.current.shadowRoot?.querySelector('.ai-send-btn')?.click();
6197
7061
  }
6198
7062
  },
6199
7063
  clearMessages: () => {
@@ -6214,14 +7078,14 @@ var index = {
6214
7078
  createAIChatDialog,
6215
7079
  installVuePlugin,
6216
7080
  useAIChat,
6217
- version: '2.0.0'
7081
+ version: '2.0.5'
6218
7082
  };
6219
7083
 
6220
7084
  // 全局暴露(UMD/浏览器环境)
6221
7085
  if (typeof window !== 'undefined') {
6222
7086
  window.AICreateChatDialog = createAIChatDialog;
6223
7087
  window.AIWebSDK = {
6224
- version: '2.0.0',
7088
+ version: '2.0.5',
6225
7089
  create: createAIChatDialog,
6226
7090
  AIChatDialog,
6227
7091
  AIChatClient,