lisichatbot 2.0.9 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +213 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisichatbot",
3
- "version": "2.0.9",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -19,7 +19,11 @@ let chatState = {
19
19
  returnToStep: null,
20
20
  completed: false,
21
21
  editPath: [], // ✅ NEW: Array of step names/numbers to edit in sequence
22
- chatMode: 'create' // ✅ NEW: Track if in 'create' or 'edit' mode
22
+ chatMode: 'create', // ✅ NEW: Track if in 'create' or 'edit' mode
23
+ editSteps: null, // ✅ Array of step indices to walk through in edit-steps mode
24
+ editStepsIndex: 0, // ✅ Current position within editSteps array
25
+ onEditComplete: null, // ✅ Callback when editSteps flow finishes
26
+ editFinalButtonText: null // ✅ Button text for the last step in editSteps
23
27
  };
24
28
 
25
29
  let elements = {
@@ -197,45 +201,74 @@ function updateEditIcons() {
197
201
  const allBotMessages = elements.messages.querySelectorAll('[data-chat-element="bot-message-wrapper"]');
198
202
 
199
203
  console.log(`\n🔄 Updating edit icons - Current step: ${chatState.step}, Completed: ${chatState.completed}`);
200
-
204
+
201
205
  allBotMessages.forEach(wrapper => {
202
206
  const stepNumber = parseInt(wrapper.getAttribute('data-chat-step'));
203
207
  const editIcon = wrapper.querySelector('[data-chat-element="bot-edit-icon"]');
204
208
  const isLatest = wrapper.hasAttribute('data-chat-latest');
205
-
209
+
206
210
  if (editIcon && !isNaN(stepNumber)) {
207
211
  const stepData = flowData.flow[stepNumber];
208
212
  const hasInput = stepData && !!stepData.input;
209
-
213
+
210
214
  // ✅ NEW: Check if edit is disabled for this step
211
215
  if (stepData && stepData.disableEdit === true) {
212
216
  editIcon.style.setProperty('display', 'none', 'important');
213
217
  console.log(` 🚫 Step ${stepNumber}: Icon HIDDEN (edit disabled)`);
214
218
  return;
215
219
  }
216
-
220
+
217
221
  // ✅ FIX: In edit mode, keep edit icons visible even after completion
218
222
  if (chatState.completed && chatState.chatMode !== 'edit') {
219
223
  editIcon.style.setProperty('display', 'none', 'important');
220
224
  console.log(` 🏁 Step ${stepNumber}: Icon HIDDEN (flow completed in create mode)`);
221
225
  return;
222
226
  }
223
-
227
+
224
228
  // ✅ NEW: Check if edit icon should be forced to show (even without input)
225
229
  const forceShowEdit = stepData && stepData.showEditIcon === true;
226
-
230
+
227
231
  // ✅ FIX: In edit mode, show edit icons for all completed steps (not just previous)
228
232
  const shouldShowInEditMode = chatState.chatMode === 'edit' && chatState.completed && isLatest;
229
233
  const shouldShowNormally = stepNumber < chatState.step && isLatest;
230
-
231
- if ((hasInput || forceShowEdit) && (shouldShowInEditMode || shouldShowNormally)) {
234
+ // ✅ In editSteps mode, show edit icons for steps already visited in the editSteps list
235
+ const shouldShowInEditSteps = chatState.editSteps && chatState.editSteps.length > 0 &&
236
+ chatState.editSteps.includes(stepNumber) &&
237
+ chatState.editSteps.indexOf(stepNumber) < chatState.editStepsIndex && isLatest;
238
+
239
+ if ((hasInput || forceShowEdit) && (shouldShowInEditMode || shouldShowNormally || shouldShowInEditSteps)) {
232
240
  editIcon.onclick = (e) => {
233
241
  e.stopPropagation();
234
242
  e.preventDefault();
235
243
  console.log(`\n🖱️ EDIT ICON CLICKED (from updateEditIcons) - Step ${stepNumber}`);
236
244
  console.log(` Current step: ${chatState.step}`);
237
-
238
- // ✅ NEW: Check if this step has an editSteps path
245
+
246
+ // ✅ In editSteps mode, go back to that step within the editSteps flow
247
+ if (chatState.editSteps && chatState.editSteps.length > 0) {
248
+ const editStepIdx = chatState.editSteps.indexOf(stepNumber);
249
+ if (editStepIdx !== -1) {
250
+ console.log(` 🛤️ editSteps: jumping back to editSteps[${editStepIdx}] (step ${stepNumber})`);
251
+ chatState.editStepsIndex = editStepIdx;
252
+ chatState.step = stepNumber;
253
+ chatState.currentSelection = null;
254
+
255
+ // Remove history from this editStep onward
256
+ const stepsToRemove = chatState.editSteps.slice(editStepIdx);
257
+ chatState.history = chatState.history.filter(h => !stepsToRemove.includes(h.step));
258
+
259
+ // Set prefill override
260
+ const targetStepData = flowData.flow[stepNumber];
261
+ if (targetStepData?.input?.field && chatState.data[targetStepData.input.field] !== undefined) {
262
+ chatState.prefillOverrideFields = [targetStepData.input.field];
263
+ }
264
+
265
+ disableNextButton();
266
+ showNextStep();
267
+ return;
268
+ }
269
+ }
270
+
271
+ // ✅ Check if this step has an editSteps path (normal mode)
239
272
  if (stepData.editSteps && Array.isArray(stepData.editSteps) && stepData.editSteps.length > 0) {
240
273
  console.log(` 🛤️ Step has editSteps path:`, stepData.editSteps);
241
274
  startEditPath(stepData.editSteps);
@@ -320,7 +353,7 @@ function renderMultiSelectDropdown(options, field) {
320
353
  // ✅ NEW: Check if prefill is disabled for this step
321
354
  const currentStep = flowData.flow[chatState.step];
322
355
  const hasOverride = chatState.prefillOverrideFields && chatState.prefillOverrideFields.includes(field);
323
- const isEditing = chatState.returnToStep !== null && chatState.returnToStep !== undefined;
356
+ const isEditing = (chatState.returnToStep !== null && chatState.returnToStep !== undefined) || (chatState.editSteps && chatState.editSteps.length > 0);
324
357
  const disablePrefill = currentStep?.disableInputValuePrefill === true && !hasOverride && !isEditing;
325
358
 
326
359
  const existingData = disablePrefill ? [] : (chatState.data[field] || []);
@@ -622,7 +655,7 @@ function renderOptions(options, field, isSingleSelect = true) {
622
655
  // ✅ NEW: Check if prefill is disabled for this step
623
656
  const currentStep = flowData.flow[chatState.step];
624
657
  const hasOverride = chatState.prefillOverrideFields && chatState.prefillOverrideFields.includes(field);
625
- const isEditing = chatState.returnToStep !== null && chatState.returnToStep !== undefined;
658
+ const isEditing = (chatState.returnToStep !== null && chatState.returnToStep !== undefined) || (chatState.editSteps && chatState.editSteps.length > 0);
626
659
  const disablePrefill = currentStep?.disableInputValuePrefill === true && !hasOverride && !isEditing;
627
660
 
628
661
  const existingData = disablePrefill ? (isSingleSelect ? null : []) : chatState.data[field];
@@ -763,7 +796,7 @@ function renderColorOptions(options, field) {
763
796
  // ✅ NEW: Check if prefill is disabled for this step
764
797
  const currentStep = flowData.flow[chatState.step];
765
798
  const hasOverride = chatState.prefillOverrideFields && chatState.prefillOverrideFields.includes(field);
766
- const isEditing = chatState.returnToStep !== null && chatState.returnToStep !== undefined;
799
+ const isEditing = (chatState.returnToStep !== null && chatState.returnToStep !== undefined) || (chatState.editSteps && chatState.editSteps.length > 0);
767
800
  const disablePrefill = currentStep?.disableInputValuePrefill === true && !hasOverride && !isEditing;
768
801
 
769
802
  const existingData = disablePrefill ? [] : chatState.data[field];
@@ -901,7 +934,7 @@ function renderCustomSelectOptions(options, field, customConfig) {
901
934
  // ✅ NEW: Check if prefill is disabled for this step
902
935
  const currentStep = flowData.flow[chatState.step];
903
936
  const hasOverride = chatState.prefillOverrideFields && chatState.prefillOverrideFields.includes(field);
904
- const isEditing = chatState.returnToStep !== null && chatState.returnToStep !== undefined;
937
+ const isEditing = (chatState.returnToStep !== null && chatState.returnToStep !== undefined) || (chatState.editSteps && chatState.editSteps.length > 0);
905
938
  const disablePrefill = currentStep?.disableInputValuePrefill === true && !hasOverride && !isEditing;
906
939
 
907
940
  console.log(`\n🔍 === PREFILL DEBUG for field: ${field} ===`);
@@ -1692,7 +1725,7 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
1692
1725
  // ✅ NEW: Check if prefill is disabled for this step
1693
1726
  const currentStep = flowData.flow[chatState.step];
1694
1727
  const hasOverride = chatState.prefillOverrideFields && chatState.prefillOverrideFields.includes(field);
1695
- const isEditing = chatState.returnToStep !== null && chatState.returnToStep !== undefined;
1728
+ const isEditing = (chatState.returnToStep !== null && chatState.returnToStep !== undefined) || (chatState.editSteps && chatState.editSteps.length > 0);
1696
1729
  const disablePrefill = currentStep?.disableInputValuePrefill === true && !hasOverride && !isEditing;
1697
1730
 
1698
1731
  const existingValue = disablePrefill ? null : chatState.data[field];
@@ -2541,6 +2574,89 @@ async function handleNext() {
2541
2574
  chatState.currentSelection = null;
2542
2575
  disableNextButton();
2543
2576
 
2577
+ // ✅ Edit-steps mode: jump to next step in the list or finish
2578
+ if (chatState.editSteps && chatState.editSteps.length > 0) {
2579
+ const currentEditIndex = chatState.editStepsIndex;
2580
+ const nextEditIndex = currentEditIndex + 1;
2581
+
2582
+ if (nextEditIndex < chatState.editSteps.length) {
2583
+ // More steps to edit
2584
+ chatState.editStepsIndex = nextEditIndex;
2585
+ const nextStepIndex = chatState.editSteps[nextEditIndex];
2586
+ console.log(`\n🛤️ Edit-steps: moving to step ${nextEditIndex + 1}/${chatState.editSteps.length} (flow index ${nextStepIndex})`);
2587
+
2588
+ chatState.step = nextStepIndex;
2589
+
2590
+ // Set prefillOverrideFields for the next step
2591
+ const targetStep = flowData.flow[nextStepIndex];
2592
+ if (targetStep?.input?.field && chatState.data[targetStep.input.field] !== undefined) {
2593
+ chatState.prefillOverrideFields = [targetStep.input.field];
2594
+ }
2595
+
2596
+ const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
2597
+ allRangeWrappers.forEach(wrapper => {
2598
+ wrapper.style.display = 'none';
2599
+ });
2600
+
2601
+ updateEditIcons();
2602
+ await showNextStep();
2603
+ scrollToBottom();
2604
+ return;
2605
+ } else {
2606
+ // All edit steps done - call onEditComplete
2607
+ console.log(`\n✅ Edit-steps complete! All ${chatState.editSteps.length} steps finished.`);
2608
+
2609
+ const dataCopy = { ...chatState.data };
2610
+ const historyCopy = [...chatState.history];
2611
+ const callback = chatState.onEditComplete;
2612
+
2613
+ // Clean up edit-steps state
2614
+ chatState.editSteps = null;
2615
+ chatState.editStepsIndex = 0;
2616
+ chatState.onEditComplete = null;
2617
+ chatState.editFinalButtonText = null;
2618
+ chatState.completed = true;
2619
+
2620
+ // Hide inputs and next button
2621
+ const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
2622
+ allRangeWrappers.forEach(wrapper => { wrapper.style.display = 'none'; });
2623
+
2624
+ const previousInputs = elements.messages.querySelectorAll(
2625
+ '[data-chat-element="options-wrapper"], ' +
2626
+ '[data-chat-element="text-input"]:not([style*="display: none"]), ' +
2627
+ '[data-chat-element="number-input"]:not([style*="display: none"]), ' +
2628
+ '[data-chat-element="multi-select-dropdown"]:not([style*="display: none"])'
2629
+ );
2630
+ previousInputs.forEach(input => { input.style.display = 'none'; });
2631
+
2632
+ if (elements.nextBtn) {
2633
+ elements.nextBtn.style.display = 'none';
2634
+ }
2635
+
2636
+ updateEditIcons();
2637
+
2638
+ // Dispatch event
2639
+ if (typeof window !== 'undefined') {
2640
+ const event = new CustomEvent('conversationalFlowEditStepsComplete', {
2641
+ detail: { data: dataCopy, history: historyCopy }
2642
+ });
2643
+ window.dispatchEvent(event);
2644
+ }
2645
+
2646
+ // Call onEditComplete callback with updated data
2647
+ if (callback) {
2648
+ console.log('📞 Calling onEditComplete with data:', dataCopy);
2649
+ try {
2650
+ callback(dataCopy);
2651
+ } catch (error) {
2652
+ console.error('Error in onEditComplete callback:', error);
2653
+ }
2654
+ }
2655
+
2656
+ return;
2657
+ }
2658
+ }
2659
+
2544
2660
  if (chatState.returnToStep !== null) {
2545
2661
  const targetStep = chatState.returnToStep;
2546
2662
  console.log(`\n🔙 Returning to saved step ${targetStep} (edited step complete)`);
@@ -2740,7 +2856,12 @@ async function showNextStep() {
2740
2856
  }
2741
2857
 
2742
2858
  // ✅ NEW: Check if step should be displayed based on condition
2743
- if (nextStep.shouldDisplay) {
2859
+ // ✅ Skip shouldDisplay check entirely if this step is explicitly in editSteps
2860
+ const isInEditSteps = chatState.editSteps && chatState.editSteps.includes(chatState.step);
2861
+ if (isInEditSteps) {
2862
+ console.log(` 🔓 Step ${chatState.step} is in editSteps - overriding shouldDisplay, always showing`);
2863
+ }
2864
+ if (nextStep.shouldDisplay && !isInEditSteps) {
2744
2865
  let shouldShow = false;
2745
2866
 
2746
2867
  if (typeof nextStep.shouldDisplay === 'function') {
@@ -2762,13 +2883,31 @@ async function showNextStep() {
2762
2883
 
2763
2884
  if (!shouldShow) {
2764
2885
  console.log(` ⏭️ Skipping step ${chatState.step} (shouldDisplay returned false)`);
2886
+
2887
+ // ✅ In editSteps mode, skip to the next step in the editSteps list
2888
+ if (chatState.editSteps && chatState.editSteps.length > 0) {
2889
+ const nextEditIndex = chatState.editStepsIndex + 1;
2890
+ if (nextEditIndex < chatState.editSteps.length) {
2891
+ chatState.editStepsIndex = nextEditIndex;
2892
+ chatState.step = chatState.editSteps[nextEditIndex];
2893
+ console.log(` 🛤️ editSteps: skipping to next edit step ${chatState.step}`);
2894
+ await showNextStep();
2895
+ return;
2896
+ } else {
2897
+ // All remaining edit steps should be skipped - trigger edit completion
2898
+ console.log(` 🛤️ editSteps: no more steps - completing`);
2899
+ handleCompletion();
2900
+ return;
2901
+ }
2902
+ }
2903
+
2765
2904
  chatState.step++;
2766
-
2905
+
2767
2906
  if (chatState.step >= flowData.flow.length) {
2768
2907
  handleCompletion();
2769
2908
  return;
2770
2909
  }
2771
-
2910
+
2772
2911
  // Recursively check next step
2773
2912
  await showNextStep();
2774
2913
  return;
@@ -3348,12 +3487,20 @@ async function showNextStep() {
3348
3487
 
3349
3488
  if (elements.nextBtn) {
3350
3489
  const nextBtnTextElement = elements.nextBtn.querySelector('[data-chat-element="next-button-text"]');
3351
-
3490
+
3352
3491
  if (nextBtnTextElement) {
3353
- // ✅ Priority: step-level > global config > original
3492
+ // ✅ Priority: editFinalButtonText (last edit step) > step-level > global config > original
3354
3493
  let buttonText;
3355
-
3356
- if (nextStep.nextButtonText) {
3494
+
3495
+ // Check if this is the last step in editSteps mode
3496
+ const isLastEditStep = chatState.editSteps &&
3497
+ chatState.editStepsIndex === chatState.editSteps.length - 1 &&
3498
+ chatState.editFinalButtonText;
3499
+
3500
+ if (isLastEditStep) {
3501
+ buttonText = chatState.editFinalButtonText;
3502
+ console.log(` 📝 Next button text: "${buttonText}" (editFinalButtonText - last edit step)`);
3503
+ } else if (nextStep.nextButtonText) {
3357
3504
  // Step-level override takes highest priority
3358
3505
  buttonText = nextStep.nextButtonText;
3359
3506
  console.log(` 📝 Next button text: "${buttonText}" (step-level)`);
@@ -3366,7 +3513,7 @@ async function showNextStep() {
3366
3513
  buttonText = elements.originalNextBtnText;
3367
3514
  console.log(` 📝 Next button text: "${buttonText}" (original)`);
3368
3515
  }
3369
-
3516
+
3370
3517
  nextBtnTextElement.textContent = buttonText;
3371
3518
  } else if (nextStep.nextButtonText || config.nextButtonText) {
3372
3519
  console.warn(` ⚠️ nextButtonText specified but next-button-text element not found`);
@@ -3514,6 +3661,43 @@ function init(flowName, flowConfig, options = {}) {
3514
3661
  chatState.history = [];
3515
3662
  chatState.currentSelection = null;
3516
3663
 
3664
+ // ✅ NEW: Edit-steps mode - only walk through specific steps by name
3665
+ if (options.editSteps && Array.isArray(options.editSteps) && options.editSteps.length > 0) {
3666
+ // Resolve step names to indices
3667
+ const resolvedSteps = [];
3668
+ options.editSteps.forEach(stepName => {
3669
+ const index = flowConfig.flow.findIndex(s => s.name === stepName);
3670
+ if (index !== -1) {
3671
+ resolvedSteps.push(index);
3672
+ console.log(` 🔍 editSteps: "${stepName}" → step index ${index}`);
3673
+ } else {
3674
+ console.warn(` ⚠️ editSteps: step name "${stepName}" not found in flow`);
3675
+ }
3676
+ });
3677
+
3678
+ if (resolvedSteps.length > 0) {
3679
+ chatState.editSteps = resolvedSteps;
3680
+ chatState.editStepsIndex = 0;
3681
+ chatState.onEditComplete = typeof options.onEditComplete === 'function' ? options.onEditComplete : null;
3682
+ chatState.editFinalButtonText = options.editFinalButtonText || null;
3683
+ chatState.step = resolvedSteps[0]; // Start at the first edit step
3684
+ console.log(`🛤️ Edit-steps mode: ${resolvedSteps.length} steps to edit`, resolvedSteps);
3685
+ console.log(` onEditComplete: ${chatState.onEditComplete ? 'provided' : 'not provided'}`);
3686
+ console.log(` editFinalButtonText: ${chatState.editFinalButtonText || 'not set'}`);
3687
+ } else {
3688
+ console.warn('⚠️ editSteps provided but no valid step names found - using normal flow');
3689
+ chatState.editSteps = null;
3690
+ chatState.editStepsIndex = 0;
3691
+ chatState.onEditComplete = null;
3692
+ chatState.editFinalButtonText = null;
3693
+ }
3694
+ } else {
3695
+ chatState.editSteps = null;
3696
+ chatState.editStepsIndex = 0;
3697
+ chatState.onEditComplete = null;
3698
+ chatState.editFinalButtonText = null;
3699
+ }
3700
+
3517
3701
  elements.messages = elements.container.querySelector('[data-chat-element="messages-container"]');
3518
3702
  elements.nextBtn = elements.container.querySelector('[data-chat-element="next-button"]');
3519
3703
  elements.cancelBtn = elements.container.querySelector('[data-chat-element="cancel-button"]');
@@ -3617,7 +3801,11 @@ function reset() {
3617
3801
  chatState.history = [];
3618
3802
  chatState.currentSelection = null;
3619
3803
  chatState.chatMode = 'create'; // ✅ Reset to create mode
3620
-
3804
+ chatState.editSteps = null;
3805
+ chatState.editStepsIndex = 0;
3806
+ chatState.onEditComplete = null;
3807
+ chatState.editFinalButtonText = null;
3808
+
3621
3809
  // ✅ Move back any injected elements before clearing
3622
3810
  if (elements.messages) {
3623
3811
  const injectedElements = elements.messages.querySelectorAll('[data-chat-injected="true"]');