vanilla-agent 1.24.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -858,143 +858,123 @@ function generateReactAdvancedCode(config: any): string {
858
858
  return lines.join("\n");
859
859
  }
860
860
 
861
- function generateScriptInstallerCode(config: any): string {
861
+ // Helper to build a serializable config object for JSON export
862
+ function buildSerializableConfig(config: any): Record<string, any> {
862
863
  const parserType = getParserTypeFromConfig(config as AgentWidgetConfig);
863
864
  const shouldEmitParserType = parserType !== "plain";
864
-
865
- const lines: string[] = [
866
- "<script>",
867
- " window.siteAgentConfig = {",
868
- " target: 'body',",
869
- " config: {"
870
- ];
871
-
872
- if (config.apiUrl) lines.push(` apiUrl: "${config.apiUrl}",`);
873
- if (config.flowId) lines.push(` flowId: "${config.flowId}",`);
874
- if (shouldEmitParserType) lines.push(` parserType: "${parserType}",`);
875
-
876
- if (config.theme) {
877
- lines.push(" theme: {");
878
- Object.entries(config.theme).forEach(([key, value]) => {
879
- lines.push(` ${key}: "${value}",`);
880
- });
881
- lines.push(" },");
882
- }
883
-
884
- if (config.launcher) {
885
- lines.push(" launcher: {");
886
- Object.entries(config.launcher).forEach(([key, value]) => {
887
- if (typeof value === "string") {
888
- lines.push(` ${key}: "${value}",`);
889
- } else if (typeof value === "boolean") {
890
- lines.push(` ${key}: ${value},`);
891
- }
892
- });
893
- lines.push(" },");
894
- }
895
-
896
- if (config.copy) {
897
- lines.push(" copy: {");
898
- Object.entries(config.copy).forEach(([key, value]) => {
899
- lines.push(` ${key}: "${value}",`);
865
+
866
+ const serializableConfig: Record<string, any> = {};
867
+
868
+ if (config.apiUrl) serializableConfig.apiUrl = config.apiUrl;
869
+ if (config.flowId) serializableConfig.flowId = config.flowId;
870
+ if (shouldEmitParserType) serializableConfig.parserType = parserType;
871
+ if (config.theme) serializableConfig.theme = config.theme;
872
+ if (config.launcher) serializableConfig.launcher = config.launcher;
873
+ if (config.copy) serializableConfig.copy = config.copy;
874
+ if (config.sendButton) serializableConfig.sendButton = config.sendButton;
875
+ if (config.voiceRecognition) serializableConfig.voiceRecognition = config.voiceRecognition;
876
+ if (config.statusIndicator) serializableConfig.statusIndicator = config.statusIndicator;
877
+ if (config.features) serializableConfig.features = config.features;
878
+ if (config.suggestionChips?.length > 0) serializableConfig.suggestionChips = config.suggestionChips;
879
+ if (config.suggestionChipsConfig) serializableConfig.suggestionChipsConfig = config.suggestionChipsConfig;
880
+ if (config.debug) serializableConfig.debug = config.debug;
881
+
882
+ // Add toolCall config (only serializable parts)
883
+ if (config.toolCall) {
884
+ const toolCallConfig: Record<string, any> = {};
885
+ Object.entries(config.toolCall).forEach(([key, value]) => {
886
+ if (typeof value === "string") toolCallConfig[key] = value;
900
887
  });
901
- lines.push(" },");
888
+ if (Object.keys(toolCallConfig).length > 0) {
889
+ serializableConfig.toolCall = toolCallConfig;
890
+ }
902
891
  }
903
-
904
- if (config.sendButton) {
905
- lines.push(" sendButton: {");
906
- Object.entries(config.sendButton).forEach(([key, value]) => {
907
- if (typeof value === "string") {
908
- lines.push(` ${key}: "${value}",`);
909
- } else if (typeof value === "boolean") {
910
- lines.push(` ${key}: ${value},`);
892
+
893
+ // Add messageActions config (excluding callbacks)
894
+ if (config.messageActions) {
895
+ const messageActionsConfig: Record<string, any> = {};
896
+ Object.entries(config.messageActions).forEach(([key, value]) => {
897
+ if (key !== "onFeedback" && key !== "onCopy" && value !== undefined) {
898
+ if (typeof value === "string" || typeof value === "boolean") {
899
+ messageActionsConfig[key] = value;
900
+ }
911
901
  }
912
902
  });
913
- lines.push(" },");
903
+ if (Object.keys(messageActionsConfig).length > 0) {
904
+ serializableConfig.messageActions = messageActionsConfig;
905
+ }
914
906
  }
915
-
916
- if (config.voiceRecognition) {
917
- lines.push(" voiceRecognition: {");
918
- Object.entries(config.voiceRecognition).forEach(([key, value]) => {
919
- if (typeof value === "string") {
920
- lines.push(` ${key}: "${value}",`);
921
- } else if (typeof value === "boolean") {
922
- lines.push(` ${key}: ${value},`);
923
- } else if (typeof value === "number") {
924
- lines.push(` ${key}: ${value},`);
925
- }
926
- });
927
- lines.push(" },");
907
+
908
+ // Add markdown config (excluding renderer functions)
909
+ if (config.markdown) {
910
+ const markdownConfig: Record<string, any> = {};
911
+ if (config.markdown.options) markdownConfig.options = config.markdown.options;
912
+ if (config.markdown.disableDefaultStyles !== undefined) {
913
+ markdownConfig.disableDefaultStyles = config.markdown.disableDefaultStyles;
914
+ }
915
+ if (Object.keys(markdownConfig).length > 0) {
916
+ serializableConfig.markdown = markdownConfig;
917
+ }
928
918
  }
929
-
930
- if (config.statusIndicator) {
931
- lines.push(" statusIndicator: {");
932
- Object.entries(config.statusIndicator).forEach(([key, value]) => {
933
- if (typeof value === "string") {
934
- lines.push(` ${key}: "${value}",`);
935
- } else if (typeof value === "boolean") {
936
- lines.push(` ${key}: ${value},`);
919
+
920
+ // Add layout config (excluding render functions)
921
+ if (config.layout) {
922
+ const layoutConfig: Record<string, any> = {};
923
+
924
+ if (config.layout.header) {
925
+ const headerConfig: Record<string, any> = {};
926
+ Object.entries(config.layout.header).forEach(([key, value]) => {
927
+ if (key !== "render" && (typeof value === "string" || typeof value === "boolean")) {
928
+ headerConfig[key] = value;
929
+ }
930
+ });
931
+ if (Object.keys(headerConfig).length > 0) {
932
+ layoutConfig.header = headerConfig;
937
933
  }
938
- });
939
- lines.push(" },");
940
- }
941
-
942
- if (config.features) {
943
- lines.push(" features: {");
944
- Object.entries(config.features).forEach(([key, value]) => {
945
- lines.push(` ${key}: ${value},`);
946
- });
947
- lines.push(" },");
948
- }
949
-
950
- if (config.suggestionChips && config.suggestionChips.length > 0) {
951
- lines.push(" suggestionChips: [");
952
- config.suggestionChips.forEach((chip: string) => {
953
- lines.push(` "${chip}",`);
954
- });
955
- lines.push(" ],");
956
- }
957
-
958
- if (config.suggestionChipsConfig) {
959
- lines.push(" suggestionChipsConfig: {");
960
- if (config.suggestionChipsConfig.fontFamily) {
961
- lines.push(` fontFamily: "${config.suggestionChipsConfig.fontFamily}",`);
962
934
  }
963
- if (config.suggestionChipsConfig.fontWeight) {
964
- lines.push(` fontWeight: "${config.suggestionChipsConfig.fontWeight}",`);
965
- }
966
- if (config.suggestionChipsConfig.paddingX) {
967
- lines.push(` paddingX: "${config.suggestionChipsConfig.paddingX}",`);
935
+
936
+ if (config.layout.messages) {
937
+ const messagesConfig: Record<string, any> = {};
938
+ Object.entries(config.layout.messages).forEach(([key, value]) => {
939
+ if (key !== "renderUserMessage" && key !== "renderAssistantMessage") {
940
+ if (key === "avatar" && typeof value === "object" && value !== null) {
941
+ messagesConfig.avatar = value;
942
+ } else if (key === "timestamp" && typeof value === "object" && value !== null) {
943
+ // Exclude format function
944
+ const tsConfig: Record<string, any> = {};
945
+ Object.entries(value as Record<string, unknown>).forEach(([tsKey, tsValue]) => {
946
+ if (tsKey !== "format" && (typeof tsValue === "string" || typeof tsValue === "boolean")) {
947
+ tsConfig[tsKey] = tsValue;
948
+ }
949
+ });
950
+ if (Object.keys(tsConfig).length > 0) {
951
+ messagesConfig.timestamp = tsConfig;
952
+ }
953
+ } else if (typeof value === "string" || typeof value === "boolean") {
954
+ messagesConfig[key] = value;
955
+ }
956
+ }
957
+ });
958
+ if (Object.keys(messagesConfig).length > 0) {
959
+ layoutConfig.messages = messagesConfig;
960
+ }
968
961
  }
969
- if (config.suggestionChipsConfig.paddingY) {
970
- lines.push(` paddingY: "${config.suggestionChipsConfig.paddingY}",`);
962
+
963
+ if (Object.keys(layoutConfig).length > 0) {
964
+ serializableConfig.layout = layoutConfig;
971
965
  }
972
- lines.push(" },");
973
- }
974
-
975
- // Add toolCall config
976
- lines.push(...generateToolCallConfig(config, " "));
977
-
978
- // Add messageActions config
979
- lines.push(...generateMessageActionsConfig(config, " "));
980
-
981
- // Add markdown config
982
- lines.push(...generateMarkdownConfig(config, " "));
983
-
984
- // Add layout config
985
- lines.push(...generateLayoutConfig(config, " "));
986
-
987
- if (config.debug) {
988
- lines.push(` debug: ${config.debug},`);
989
966
  }
967
+
968
+ return serializableConfig;
969
+ }
990
970
 
991
- lines.push(" postprocessMessage: ({ text }) => window.AgentWidget.markdownPostprocessor(text)");
992
- lines.push(" }");
993
- lines.push(" };");
994
- lines.push("</script>");
995
- lines.push("<script src=\"https://cdn.jsdelivr.net/npm/vanilla-agent@latest/dist/install.global.js\"></script>");
996
-
997
- return lines.join("\n");
971
+ function generateScriptInstallerCode(config: any): string {
972
+ const serializableConfig = buildSerializableConfig(config);
973
+
974
+ // Escape single quotes in JSON for HTML attribute
975
+ const configJson = JSON.stringify(serializableConfig, null, 0).replace(/'/g, "&#39;");
976
+
977
+ return `<script src="https://cdn.jsdelivr.net/npm/vanilla-agent@latest/dist/install.global.js" data-config='${configJson}'></script>`;
998
978
  }
999
979
 
1000
980
  function generateScriptManualCode(config: any): string {
@@ -1143,341 +1123,224 @@ function generateScriptManualCode(config: any): string {
1143
1123
  }
1144
1124
 
1145
1125
  function generateScriptAdvancedCode(config: any): string {
1126
+ const serializableConfig = buildSerializableConfig(config);
1127
+ const configJson = JSON.stringify(serializableConfig, null, 2);
1128
+
1146
1129
  const lines: string[] = [
1147
- "<!-- Load CSS -->",
1148
- "<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/vanilla-agent@latest/dist/widget.css\" />",
1149
- "",
1150
- "<!-- Chat Widget Configuration -->",
1151
1130
  "<script>",
1152
- " window.ChatWidgetConfig = {"
1131
+ "(function() {",
1132
+ " 'use strict';",
1133
+ "",
1134
+ " // Configuration",
1135
+ ` var CONFIG = ${configJson.split('\n').map((line, i) => i === 0 ? line : ' ' + line).join('\n')};`,
1136
+ "",
1137
+ " // Constants",
1138
+ " var CDN_BASE = 'https://cdn.jsdelivr.net/npm/vanilla-agent@latest/dist';",
1139
+ " var STORAGE_KEY = 'chat-widget-state';",
1140
+ " var PROCESSED_ACTIONS_KEY = 'chat-widget-processed-actions';",
1141
+ "",
1142
+ " // DOM context provider - extracts page elements for AI context",
1143
+ " var domContextProvider = function() {",
1144
+ " var selectors = {",
1145
+ " products: '[data-product-id], .product-card, .product-item, [role=\"article\"]',",
1146
+ " buttons: 'button, [role=\"button\"], .btn',",
1147
+ " links: 'a[href]',",
1148
+ " inputs: 'input, textarea, select'",
1149
+ " };",
1150
+ "",
1151
+ " var elements = [];",
1152
+ " Object.entries(selectors).forEach(function(entry) {",
1153
+ " var type = entry[0], selector = entry[1];",
1154
+ " document.querySelectorAll(selector).forEach(function(element) {",
1155
+ " if (!(element instanceof HTMLElement)) return;",
1156
+ " var widgetHost = element.closest('.vanilla-agent-host');",
1157
+ " if (widgetHost) return;",
1158
+ " var text = element.innerText ? element.innerText.trim() : '';",
1159
+ " if (!text) return;",
1160
+ "",
1161
+ " var selectorString = element.id ? '#' + element.id :",
1162
+ " element.getAttribute('data-testid') ? '[data-testid=\"' + element.getAttribute('data-testid') + '\"]' :",
1163
+ " element.getAttribute('data-product-id') ? '[data-product-id=\"' + element.getAttribute('data-product-id') + '\"]' :",
1164
+ " element.tagName.toLowerCase();",
1165
+ "",
1166
+ " var elementData = {",
1167
+ " type: type,",
1168
+ " tagName: element.tagName.toLowerCase(),",
1169
+ " selector: selectorString,",
1170
+ " innerText: text.substring(0, 200)",
1171
+ " };",
1172
+ "",
1173
+ " if (type === 'links' && element instanceof HTMLAnchorElement && element.href) {",
1174
+ " elementData.href = element.href;",
1175
+ " }",
1176
+ " elements.push(elementData);",
1177
+ " });",
1178
+ " });",
1179
+ "",
1180
+ " var counts = elements.reduce(function(acc, el) {",
1181
+ " acc[el.type] = (acc[el.type] || 0) + 1;",
1182
+ " return acc;",
1183
+ " }, {});",
1184
+ "",
1185
+ " return {",
1186
+ " page_elements: elements.slice(0, 50),",
1187
+ " page_element_count: elements.length,",
1188
+ " element_types: counts,",
1189
+ " page_url: window.location.href,",
1190
+ " page_title: document.title,",
1191
+ " timestamp: new Date().toISOString()",
1192
+ " };",
1193
+ " };",
1194
+ "",
1195
+ " // Load CSS dynamically",
1196
+ " var loadCSS = function() {",
1197
+ " if (document.querySelector('link[data-vanilla-agent]')) return;",
1198
+ " var link = document.createElement('link');",
1199
+ " link.rel = 'stylesheet';",
1200
+ " link.href = CDN_BASE + '/widget.css';",
1201
+ " link.setAttribute('data-vanilla-agent', 'true');",
1202
+ " document.head.appendChild(link);",
1203
+ " };",
1204
+ "",
1205
+ " // Load JS dynamically",
1206
+ " var loadJS = function(callback) {",
1207
+ " if (window.AgentWidget) { callback(); return; }",
1208
+ " var script = document.createElement('script');",
1209
+ " script.src = CDN_BASE + '/index.global.js';",
1210
+ " script.onload = callback;",
1211
+ " script.onerror = function() { console.error('Failed to load AgentWidget'); };",
1212
+ " document.head.appendChild(script);",
1213
+ " };",
1214
+ "",
1215
+ " // Create widget config with advanced features",
1216
+ " var createWidgetConfig = function(agentWidget) {",
1217
+ " var widgetConfig = Object.assign({}, CONFIG);",
1218
+ "",
1219
+ " // Flexible JSON stream parser for handling structured actions",
1220
+ " widgetConfig.streamParser = function() {",
1221
+ " return agentWidget.createFlexibleJsonStreamParser(function(parsed) {",
1222
+ " if (!parsed || typeof parsed !== 'object') return null;",
1223
+ " if (parsed.action === 'nav_then_click') return 'Navigating...';",
1224
+ " if (parsed.action === 'message') return parsed.text || '';",
1225
+ " if (parsed.action === 'message_and_click') return parsed.text || 'Processing...';",
1226
+ " return parsed.text || null;",
1227
+ " });",
1228
+ " };",
1229
+ "",
1230
+ " // Action parsers to detect JSON actions in responses",
1231
+ " widgetConfig.actionParsers = [",
1232
+ " agentWidget.defaultJsonActionParser,",
1233
+ " function(ctx) {",
1234
+ " var jsonSource = ctx.message.rawContent || ctx.text || ctx.message.content;",
1235
+ " if (!jsonSource || typeof jsonSource !== 'string') return null;",
1236
+ " var cleanJson = jsonSource",
1237
+ " .replace(/^```(?:json)?\\s*\\n?/, '')",
1238
+ " .replace(/\\n?```\\s*$/, '')",
1239
+ " .trim();",
1240
+ " if (!cleanJson.startsWith('{') || !cleanJson.endsWith('}')) return null;",
1241
+ " try {",
1242
+ " var parsed = JSON.parse(cleanJson);",
1243
+ " if (parsed.action) return { type: parsed.action, payload: parsed };",
1244
+ " } catch (e) { return null; }",
1245
+ " return null;",
1246
+ " }",
1247
+ " ];",
1248
+ "",
1249
+ " // Action handlers for navigation and other actions",
1250
+ " widgetConfig.actionHandlers = [",
1251
+ " agentWidget.defaultActionHandlers.message,",
1252
+ " agentWidget.defaultActionHandlers.messageAndClick,",
1253
+ " function(action, context) {",
1254
+ " if (action.type !== 'nav_then_click') return;",
1255
+ " var payload = action.payload || action.raw || {};",
1256
+ " var url = payload.page;",
1257
+ " var text = payload.on_load_text || 'Navigating...';",
1258
+ " if (!url) return { handled: true, displayText: text };",
1259
+ " var messageId = context.message ? context.message.id : null;",
1260
+ " var processedActions = JSON.parse(localStorage.getItem(PROCESSED_ACTIONS_KEY) || '[]');",
1261
+ " var actionKey = 'nav_' + messageId + '_' + url;",
1262
+ " if (processedActions.includes(actionKey)) {",
1263
+ " return { handled: true, displayText: text };",
1264
+ " }",
1265
+ " processedActions.push(actionKey);",
1266
+ " localStorage.setItem(PROCESSED_ACTIONS_KEY, JSON.stringify(processedActions));",
1267
+ " var targetUrl = url.startsWith('http') ? url : new URL(url, window.location.origin).toString();",
1268
+ " window.location.href = targetUrl;",
1269
+ " return { handled: true, displayText: text };",
1270
+ " }",
1271
+ " ];",
1272
+ "",
1273
+ " // Send DOM context with each request",
1274
+ " widgetConfig.requestMiddleware = function(ctx) {",
1275
+ " return Object.assign({}, ctx.payload, { metadata: domContextProvider() });",
1276
+ " };",
1277
+ "",
1278
+ " // Markdown postprocessor",
1279
+ " widgetConfig.postprocessMessage = function(ctx) {",
1280
+ " return agentWidget.markdownPostprocessor(ctx.text);",
1281
+ " };",
1282
+ "",
1283
+ " return widgetConfig;",
1284
+ " };",
1285
+ "",
1286
+ " // Initialize widget",
1287
+ " var init = function() {",
1288
+ " var agentWidget = window.AgentWidget;",
1289
+ " if (!agentWidget) {",
1290
+ " console.error('AgentWidget not loaded');",
1291
+ " return;",
1292
+ " }",
1293
+ "",
1294
+ " var widgetConfig = createWidgetConfig(agentWidget);",
1295
+ "",
1296
+ " // Load saved state",
1297
+ " var savedState = localStorage.getItem(STORAGE_KEY);",
1298
+ " if (savedState) {",
1299
+ " try {",
1300
+ " var parsed = JSON.parse(savedState);",
1301
+ " widgetConfig.initialMessages = parsed.messages || [];",
1302
+ " } catch (e) {",
1303
+ " console.error('Failed to load saved state:', e);",
1304
+ " }",
1305
+ " }",
1306
+ "",
1307
+ " // Initialize widget",
1308
+ " var handle = agentWidget.initAgentWidget({",
1309
+ " target: 'body',",
1310
+ " useShadowDom: false,",
1311
+ " config: widgetConfig",
1312
+ " });",
1313
+ "",
1314
+ " // Save state on message events",
1315
+ " window.addEventListener('vanilla-agent:message', function() {",
1316
+ " var session = handle.getSession ? handle.getSession() : null;",
1317
+ " if (session) {",
1318
+ " localStorage.setItem(STORAGE_KEY, JSON.stringify({",
1319
+ " messages: session.messages,",
1320
+ " timestamp: new Date().toISOString()",
1321
+ " }));",
1322
+ " }",
1323
+ " });",
1324
+ "",
1325
+ " // Clear state on clear chat",
1326
+ " window.addEventListener('vanilla-agent:clear-chat', function() {",
1327
+ " localStorage.removeItem(STORAGE_KEY);",
1328
+ " localStorage.removeItem(PROCESSED_ACTIONS_KEY);",
1329
+ " });",
1330
+ " };",
1331
+ "",
1332
+ " // Boot sequence: load CSS, then JS, then initialize",
1333
+ " loadCSS();",
1334
+ " loadJS(function() {",
1335
+ " if (document.readyState === 'loading') {",
1336
+ " document.addEventListener('DOMContentLoaded', init);",
1337
+ " } else {",
1338
+ " init();",
1339
+ " }",
1340
+ " });",
1341
+ "})();",
1342
+ "</script>"
1153
1343
  ];
1154
1344
 
1155
- if (config.apiUrl) lines.push(` apiUrl: "${config.apiUrl}",`);
1156
- if (config.flowId) lines.push(` flowId: "${config.flowId}",`);
1157
-
1158
- if (config.theme) {
1159
- lines.push(" theme: {");
1160
- Object.entries(config.theme).forEach(([key, value]) => {
1161
- lines.push(` ${key}: "${value}",`);
1162
- });
1163
- lines.push(" },");
1164
- }
1165
-
1166
- if (config.launcher) {
1167
- lines.push(" launcher: {");
1168
- Object.entries(config.launcher).forEach(([key, value]) => {
1169
- if (typeof value === "string") {
1170
- lines.push(` ${key}: "${value}",`);
1171
- } else if (typeof value === "boolean") {
1172
- lines.push(` ${key}: ${value},`);
1173
- }
1174
- });
1175
- lines.push(" },");
1176
- }
1177
-
1178
- if (config.copy) {
1179
- lines.push(" copy: {");
1180
- Object.entries(config.copy).forEach(([key, value]) => {
1181
- lines.push(` ${key}: "${value}",`);
1182
- });
1183
- lines.push(" },");
1184
- }
1185
-
1186
- if (config.sendButton) {
1187
- lines.push(" sendButton: {");
1188
- Object.entries(config.sendButton).forEach(([key, value]) => {
1189
- if (typeof value === "string") {
1190
- lines.push(` ${key}: "${value}",`);
1191
- } else if (typeof value === "boolean") {
1192
- lines.push(` ${key}: ${value},`);
1193
- }
1194
- });
1195
- lines.push(" },");
1196
- }
1197
-
1198
- if (config.voiceRecognition) {
1199
- lines.push(" voiceRecognition: {");
1200
- Object.entries(config.voiceRecognition).forEach(([key, value]) => {
1201
- if (typeof value === "string") {
1202
- lines.push(` ${key}: "${value}",`);
1203
- } else if (typeof value === "boolean") {
1204
- lines.push(` ${key}: ${value},`);
1205
- } else if (typeof value === "number") {
1206
- lines.push(` ${key}: ${value},`);
1207
- }
1208
- });
1209
- lines.push(" },");
1210
- }
1211
-
1212
- if (config.statusIndicator) {
1213
- lines.push(" statusIndicator: {");
1214
- Object.entries(config.statusIndicator).forEach(([key, value]) => {
1215
- if (typeof value === "string") {
1216
- lines.push(` ${key}: "${value}",`);
1217
- } else if (typeof value === "boolean") {
1218
- lines.push(` ${key}: ${value},`);
1219
- }
1220
- });
1221
- lines.push(" },");
1222
- }
1223
-
1224
- if (config.features) {
1225
- lines.push(" features: {");
1226
- Object.entries(config.features).forEach(([key, value]) => {
1227
- lines.push(` ${key}: ${value},`);
1228
- });
1229
- lines.push(" },");
1230
- }
1231
-
1232
- if (config.suggestionChips && config.suggestionChips.length > 0) {
1233
- lines.push(" suggestionChips: [");
1234
- config.suggestionChips.forEach((chip: string) => {
1235
- lines.push(` "${chip}",`);
1236
- });
1237
- lines.push(" ],");
1238
- }
1239
-
1240
- if (config.suggestionChipsConfig) {
1241
- lines.push(" suggestionChipsConfig: {");
1242
- if (config.suggestionChipsConfig.fontFamily) {
1243
- lines.push(` fontFamily: "${config.suggestionChipsConfig.fontFamily}",`);
1244
- }
1245
- if (config.suggestionChipsConfig.fontWeight) {
1246
- lines.push(` fontWeight: "${config.suggestionChipsConfig.fontWeight}",`);
1247
- }
1248
- if (config.suggestionChipsConfig.paddingX) {
1249
- lines.push(` paddingX: "${config.suggestionChipsConfig.paddingX}",`);
1250
- }
1251
- if (config.suggestionChipsConfig.paddingY) {
1252
- lines.push(` paddingY: "${config.suggestionChipsConfig.paddingY}",`);
1253
- }
1254
- lines.push(" },");
1255
- }
1256
-
1257
- // Add toolCall config
1258
- lines.push(...generateToolCallConfig(config, " "));
1259
-
1260
- // Add messageActions config
1261
- lines.push(...generateMessageActionsConfig(config, " "));
1262
-
1263
- // Add markdown config
1264
- lines.push(...generateMarkdownConfig(config, " "));
1265
-
1266
- // Add layout config
1267
- lines.push(...generateLayoutConfig(config, " "));
1268
-
1269
- lines.push(" };");
1270
- lines.push("</script>");
1271
- lines.push("");
1272
- lines.push("<!-- Load the widget library -->");
1273
- lines.push("<script src=\"https://cdn.jsdelivr.net/npm/vanilla-agent@latest/dist/index.global.js\"></script>");
1274
- lines.push("");
1275
- lines.push("<!-- Chat Widget Script with DOM Helper -->");
1276
- lines.push("<script>");
1277
- lines.push(" (function () {");
1278
- lines.push(" 'use strict';");
1279
- lines.push(" ");
1280
- lines.push(" const STORAGE_KEY = 'chat-widget-state';");
1281
- lines.push(" const PROCESSED_ACTIONS_KEY = 'chat-widget-processed-actions';");
1282
- lines.push("");
1283
- lines.push(" // DOM context provider - extracts page elements for AI context");
1284
- lines.push(" const domContextProvider = () => {");
1285
- lines.push(" const selectors = {");
1286
- lines.push(" products: '[data-product-id], .product-card, .product-item, [role=\"article\"]',");
1287
- lines.push(" buttons: 'button, [role=\"button\"], .btn',");
1288
- lines.push(" links: 'a[href]',");
1289
- lines.push(" inputs: 'input, textarea, select'");
1290
- lines.push(" };");
1291
- lines.push("");
1292
- lines.push(" const elements = [];");
1293
- lines.push(" Object.entries(selectors).forEach(([type, selector]) => {");
1294
- lines.push(" document.querySelectorAll(selector).forEach((element) => {");
1295
- lines.push(" if (!(element instanceof HTMLElement)) return;");
1296
- lines.push(" ");
1297
- lines.push(" // Exclude elements within the widget");
1298
- lines.push(" const widgetHost = element.closest('.vanilla-agent-host');");
1299
- lines.push(" if (widgetHost) return;");
1300
- lines.push(" ");
1301
- lines.push(" const text = element.innerText?.trim();");
1302
- lines.push(" if (!text) return;");
1303
- lines.push("");
1304
- lines.push(" const selectorString =");
1305
- lines.push(" element.id ? `#${element.id}` :");
1306
- lines.push(" element.getAttribute('data-testid') ? `[data-testid=\"${element.getAttribute('data-testid')}\"]` :");
1307
- lines.push(" element.getAttribute('data-product-id') ? `[data-product-id=\"${element.getAttribute('data-product-id')}\"]` :");
1308
- lines.push(" element.tagName.toLowerCase();");
1309
- lines.push("");
1310
- lines.push(" const elementData = {");
1311
- lines.push(" type,");
1312
- lines.push(" tagName: element.tagName.toLowerCase(),");
1313
- lines.push(" selector: selectorString,");
1314
- lines.push(" innerText: text.substring(0, 200)");
1315
- lines.push(" };");
1316
- lines.push("");
1317
- lines.push(" if (type === 'links' && element instanceof HTMLAnchorElement && element.href) {");
1318
- lines.push(" elementData.href = element.href;");
1319
- lines.push(" }");
1320
- lines.push("");
1321
- lines.push(" elements.push(elementData);");
1322
- lines.push(" });");
1323
- lines.push(" });");
1324
- lines.push("");
1325
- lines.push(" const counts = elements.reduce((acc, el) => {");
1326
- lines.push(" acc[el.type] = (acc[el.type] || 0) + 1;");
1327
- lines.push(" return acc;");
1328
- lines.push(" }, {});");
1329
- lines.push("");
1330
- lines.push(" return {");
1331
- lines.push(" page_elements: elements.slice(0, 50),");
1332
- lines.push(" page_element_count: elements.length,");
1333
- lines.push(" element_types: counts,");
1334
- lines.push(" page_url: window.location.href,");
1335
- lines.push(" page_title: document.title,");
1336
- lines.push(" timestamp: new Date().toISOString()");
1337
- lines.push(" };");
1338
- lines.push(" };");
1339
- lines.push("");
1340
- lines.push(" const createWidgetConfig = (agentWidget) => ({");
1341
- lines.push(" ...window.ChatWidgetConfig,");
1342
- lines.push(" // Flexible JSON stream parser for handling structured actions");
1343
- lines.push(" streamParser: () => agentWidget.createFlexibleJsonStreamParser((parsed) => {");
1344
- lines.push(" if (!parsed || typeof parsed !== 'object') return null;");
1345
- lines.push(" ");
1346
- lines.push(" // Extract display text based on action type");
1347
- lines.push(" if (parsed.action === 'nav_then_click') {");
1348
- lines.push(" return 'Navigating...';");
1349
- lines.push(" } else if (parsed.action === 'message') {");
1350
- lines.push(" return parsed.text || '';");
1351
- lines.push(" } else if (parsed.action === 'message_and_click') {");
1352
- lines.push(" return parsed.text || 'Processing...';");
1353
- lines.push(" }");
1354
- lines.push(" ");
1355
- lines.push(" return parsed.text || null;");
1356
- lines.push(" }),");
1357
- lines.push(" // Action parsers to detect JSON actions in responses");
1358
- lines.push(" actionParsers: [");
1359
- lines.push(" agentWidget.defaultJsonActionParser,");
1360
- lines.push(" // Custom parser for markdown-wrapped JSON");
1361
- lines.push(" ({ text, message }) => {");
1362
- lines.push(" const jsonSource = message.rawContent || text || message.content;");
1363
- lines.push(" if (!jsonSource || typeof jsonSource !== 'string') return null;");
1364
- lines.push(" ");
1365
- lines.push(" // Strip markdown code fences");
1366
- lines.push(" let cleanJson = jsonSource");
1367
- lines.push(" .replace(/^```(?:json)?\\s*\\n?/, '')");
1368
- lines.push(" .replace(/\\n?```\\s*$/, '')");
1369
- lines.push(" .trim();");
1370
- lines.push(" ");
1371
- lines.push(" if (!cleanJson.startsWith('{') || !cleanJson.endsWith('}')) return null;");
1372
- lines.push(" ");
1373
- lines.push(" try {");
1374
- lines.push(" const parsed = JSON.parse(cleanJson);");
1375
- lines.push(" if (parsed.action) {");
1376
- lines.push(" return { type: parsed.action, payload: parsed };");
1377
- lines.push(" }");
1378
- lines.push(" } catch (e) {");
1379
- lines.push(" return null;");
1380
- lines.push(" }");
1381
- lines.push(" return null;");
1382
- lines.push(" }");
1383
- lines.push(" ],");
1384
- lines.push(" // Action handlers for navigation and other actions");
1385
- lines.push(" actionHandlers: [");
1386
- lines.push(" agentWidget.defaultActionHandlers.message,");
1387
- lines.push(" agentWidget.defaultActionHandlers.messageAndClick,");
1388
- lines.push(" // Handler for nav_then_click action");
1389
- lines.push(" (action, context) => {");
1390
- lines.push(" if (action.type !== 'nav_then_click') return;");
1391
- lines.push(" ");
1392
- lines.push(" const payload = action.payload || action.raw || {};");
1393
- lines.push(" const url = payload?.page;");
1394
- lines.push(" const text = payload?.on_load_text || 'Navigating...';");
1395
- lines.push(" ");
1396
- lines.push(" if (!url) return { handled: true, displayText: text };");
1397
- lines.push(" ");
1398
- lines.push(" // Check if already processed");
1399
- lines.push(" const messageId = context.message?.id;");
1400
- lines.push(" const processedActions = JSON.parse(localStorage.getItem(PROCESSED_ACTIONS_KEY) || '[]');");
1401
- lines.push(" const actionKey = `nav_${messageId}_${url}`;");
1402
- lines.push(" ");
1403
- lines.push(" if (processedActions.includes(actionKey)) {");
1404
- lines.push(" return { handled: true, displayText: text };");
1405
- lines.push(" }");
1406
- lines.push(" ");
1407
- lines.push(" processedActions.push(actionKey);");
1408
- lines.push(" localStorage.setItem(PROCESSED_ACTIONS_KEY, JSON.stringify(processedActions));");
1409
- lines.push(" ");
1410
- lines.push(" const targetUrl = url.startsWith('http')");
1411
- lines.push(" ? url");
1412
- lines.push(" : new URL(url, window.location.origin).toString();");
1413
- lines.push(" ");
1414
- lines.push(" window.location.href = targetUrl;");
1415
- lines.push(" ");
1416
- lines.push(" return { handled: true, displayText: text };");
1417
- lines.push(" }");
1418
- lines.push(" ],");
1419
- lines.push(" // Send DOM context with each request");
1420
- lines.push(" requestMiddleware: ({ payload }) => ({");
1421
- lines.push(" ...payload,");
1422
- lines.push(" metadata: domContextProvider()");
1423
- lines.push(" }),");
1424
- lines.push(" postprocessMessage: ({ text }) => agentWidget.markdownPostprocessor(text)");
1425
- lines.push(" });");
1426
- lines.push("");
1427
- lines.push(" // Initialize widget when DOM is loaded");
1428
- lines.push(" function init() {");
1429
- lines.push(" const agentWidget = window.AgentWidget;");
1430
- lines.push(" if (!agentWidget) {");
1431
- lines.push(" console.error('AgentWidget not loaded');");
1432
- lines.push(" return;");
1433
- lines.push(" }");
1434
- lines.push("");
1435
- lines.push(" const widgetConfig = createWidgetConfig(agentWidget);");
1436
- lines.push("");
1437
- lines.push(" // Load saved state");
1438
- lines.push(" const savedState = localStorage.getItem(STORAGE_KEY);");
1439
- lines.push(" if (savedState) {");
1440
- lines.push(" try {");
1441
- lines.push(" const { messages } = JSON.parse(savedState);");
1442
- lines.push(" widgetConfig.initialMessages = messages || [];");
1443
- lines.push(" } catch (e) {");
1444
- lines.push(" console.error('Failed to load saved state:', e);");
1445
- lines.push(" }");
1446
- lines.push(" }");
1447
- lines.push("");
1448
- lines.push(" // Initialize widget with DOM context");
1449
- lines.push(" const handle = agentWidget.initAgentWidget({");
1450
- lines.push(" target: 'body',");
1451
- lines.push(" useShadowDom: false,");
1452
- lines.push(" config: widgetConfig");
1453
- lines.push(" });");
1454
- lines.push("");
1455
- lines.push(" // Save state on message events");
1456
- lines.push(" window.addEventListener('vanilla-agent:message', (event) => {");
1457
- lines.push(" const session = handle.getSession?.();");
1458
- lines.push(" if (session) {");
1459
- lines.push(" localStorage.setItem(STORAGE_KEY, JSON.stringify({");
1460
- lines.push(" messages: session.messages,");
1461
- lines.push(" timestamp: new Date().toISOString()");
1462
- lines.push(" }));");
1463
- lines.push(" }");
1464
- lines.push(" });");
1465
- lines.push("");
1466
- lines.push(" // Clear state on clear chat");
1467
- lines.push(" window.addEventListener('vanilla-agent:clear-chat', () => {");
1468
- lines.push(" localStorage.removeItem(STORAGE_KEY);");
1469
- lines.push(" localStorage.removeItem(PROCESSED_ACTIONS_KEY);");
1470
- lines.push(" });");
1471
- lines.push(" }");
1472
- lines.push("");
1473
- lines.push(" // Initialize when DOM is ready");
1474
- lines.push(" if (document.readyState === 'loading') {");
1475
- lines.push(" document.addEventListener('DOMContentLoaded', init);");
1476
- lines.push(" } else {");
1477
- lines.push(" init();");
1478
- lines.push(" }");
1479
- lines.push(" })();");
1480
- lines.push("</script>");
1481
-
1482
1345
  return lines.join("\n");
1483
1346
  }