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