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