@inploi/plugin-chatbot 3.6.3 → 3.7.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.
@@ -1,7 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("./index-4fa91c08.cjs");
3
+ const index = require("./index-09e54786.cjs");
4
4
  require("@inploi/sdk");
5
+ const getHeadOrThrow = (nodes) => {
6
+ const head = nodes.find((n2) => n2.isHead);
7
+ if (!head)
8
+ throw new Error("No head node found");
9
+ return head;
10
+ };
11
+ const getApplicationSubmissionsPayload = (submissions) => {
12
+ const payload = Object.entries(submissions).reduce((acc, [key, submission]) => {
13
+ acc[key] = submission.value;
14
+ return acc;
15
+ }, {});
16
+ return payload;
17
+ };
18
+ const isSubmissionOfType = (type) => (submission) => {
19
+ if (!submission)
20
+ return false;
21
+ return submission.type === type;
22
+ };
23
+ class AbortedError extends Error {
24
+ constructor() {
25
+ super("Aborted");
26
+ }
27
+ }
5
28
  const followNodes = ({
6
29
  node,
7
30
  nodes,
@@ -88,14 +111,14 @@ const createFlowInterpreter = ({
88
111
  end: () => onFlowEnd == null ? void 0 : onFlowEnd(node)
89
112
  });
90
113
  } catch (e) {
91
- if (e instanceof index.AbortedError)
114
+ if (e instanceof AbortedError)
92
115
  return;
93
116
  throw e;
94
117
  }
95
118
  };
96
119
  return {
97
120
  interpret: async (startFromNodeId) => {
98
- const startNode = flow.find((node) => node.id === startFromNodeId) ?? index.getHeadOrThrow(flow);
121
+ const startNode = flow.find((node) => node.id === startFromNodeId) ?? getHeadOrThrow(flow);
99
122
  return interpretNode(startNode);
100
123
  },
101
124
  abort: () => {
@@ -216,7 +239,7 @@ async function interpretSubmitNode({
216
239
  ats: node.data.ats,
217
240
  integration_id: node.data.integrationId,
218
241
  anonymous_id: analytics.getAnonymousId(),
219
- submissions: index.getApplicationSubmissionsPayload(submissions || {})
242
+ submissions: getApplicationSubmissionsPayload(submissions || {})
220
243
  })
221
244
  }).catch((e) => e);
222
245
  await index.N(response).with({
@@ -240,6 +263,7 @@ async function interpretSubmitNode({
240
263
  href: href.toString(),
241
264
  text: "Finalise application on partner"
242
265
  });
266
+ next(node.nextId);
243
267
  }).with({
244
268
  success: true
245
269
  }, async () => {
@@ -320,15 +344,24 @@ async function interpretQuestionTextNode({
320
344
  key: node.data.key,
321
345
  type: "text",
322
346
  config: {
347
+ optional: node.data.optional,
323
348
  placeholder: node.data.placeholder,
324
349
  format: node.data.format
325
350
  }
326
351
  });
327
- await chat.sendMessage({
328
- author: "user",
329
- type: "text",
330
- text: reply.value
331
- });
352
+ if (reply.value === null) {
353
+ await chat.sendMessage({
354
+ type: "system",
355
+ variant: "info",
356
+ text: "Skipped"
357
+ });
358
+ } else {
359
+ await chat.sendMessage({
360
+ author: "user",
361
+ type: "text",
362
+ text: reply.value
363
+ });
364
+ }
332
365
  next(node.nextId);
333
366
  }
334
367
  async function interpretQuestionNumberNode({
@@ -345,15 +378,24 @@ async function interpretQuestionNumberNode({
345
378
  key: node.data.key,
346
379
  type: "text",
347
380
  config: {
381
+ optional: node.data.optional,
348
382
  placeholder: node.data.placeholder,
349
383
  format: "text"
350
384
  }
351
385
  });
352
- await chat.sendMessage({
353
- author: "user",
354
- type: "text",
355
- text: reply.value
356
- });
386
+ if (reply.value === null) {
387
+ await chat.sendMessage({
388
+ type: "system",
389
+ variant: "info",
390
+ text: "Skipped"
391
+ });
392
+ } else {
393
+ await chat.sendMessage({
394
+ author: "user",
395
+ type: "text",
396
+ text: reply.value
397
+ });
398
+ }
357
399
  next(node.nextId);
358
400
  }
359
401
  async function interpretQuestionEnumNode({
@@ -371,11 +413,19 @@ async function interpretQuestionEnumNode({
371
413
  type: "multiple-choice",
372
414
  config: node.data
373
415
  });
374
- await chat.sendMessage({
375
- author: "user",
376
- type: "text",
377
- text: node.data.options.filter((o2) => reply.value.includes(o2.value)).map((o2) => o2.label).join(", ")
378
- });
416
+ if (reply.value.length === 0) {
417
+ await chat.sendMessage({
418
+ type: "system",
419
+ variant: "info",
420
+ text: "Skipped"
421
+ });
422
+ } else {
423
+ await chat.sendMessage({
424
+ author: "user",
425
+ type: "text",
426
+ text: node.data.options.filter((o2) => reply.value.includes(o2.value)).map((o2) => o2.label).join(", ")
427
+ });
428
+ }
379
429
  next(node.nextId);
380
430
  }
381
431
  async function interpretQuestionBooleanNode({
@@ -392,6 +442,7 @@ async function interpretQuestionBooleanNode({
392
442
  key: node.data.key,
393
443
  type: "boolean",
394
444
  config: {
445
+ optional: node.data.optional,
395
446
  labels: {
396
447
  true: node.data.trueLabel,
397
448
  false: node.data.falseLabel
@@ -399,15 +450,22 @@ async function interpretQuestionBooleanNode({
399
450
  }
400
451
  });
401
452
  const reply = input.value;
402
- const labelMap = {
403
- true: node.data.trueLabel,
404
- false: node.data.falseLabel
405
- };
406
- await chat.sendMessage({
407
- author: "user",
408
- type: "text",
409
- text: labelMap[reply]
410
- });
453
+ if (reply === null) {
454
+ await chat.sendMessage({
455
+ type: "system",
456
+ variant: "info",
457
+ text: "Skipped"
458
+ });
459
+ } else {
460
+ await chat.sendMessage({
461
+ author: "user",
462
+ type: "text",
463
+ text: {
464
+ true: node.data.trueLabel,
465
+ false: node.data.falseLabel
466
+ }[reply]
467
+ });
468
+ }
411
469
  next(node.nextId);
412
470
  }
413
471
  async function interpretQuestionAddressNode({
@@ -436,19 +494,28 @@ async function interpretQuestionFileNode({
436
494
  key: node.data.key,
437
495
  type: "file",
438
496
  config: {
497
+ optional: node.data.optional,
439
498
  extensions: node.data.extensions,
440
499
  // default value for fileSizeLimitKib is 10MB
441
500
  fileSizeLimitKib: node.data.maxSizeKb ?? 10 * 1024,
442
501
  allowMultiple: node.data.multiple === true
443
502
  }
444
503
  });
445
- for await (const file of files.value) {
504
+ if (files.value === null) {
446
505
  await chat.sendMessage({
447
- author: "user",
448
- type: "file",
449
- fileName: file.name,
450
- fileSizeKb: file.sizeKb
506
+ type: "system",
507
+ variant: "info",
508
+ text: "Skipped"
451
509
  });
510
+ } else {
511
+ for (const file of files.value) {
512
+ await chat.sendMessage({
513
+ author: "user",
514
+ type: "file",
515
+ fileName: file.name,
516
+ fileSizeKb: file.sizeKb
517
+ });
518
+ }
452
519
  }
453
520
  next(node.nextId);
454
521
  }
@@ -457,15 +524,14 @@ async function interpretEndFlowNode({
457
524
  end,
458
525
  node
459
526
  }) {
460
- var _a;
461
527
  await chat.sendMessage({
462
528
  type: "system",
463
529
  variant: "info",
464
- text: ((_a = node.data) == null ? void 0 : _a.systemMessage) ?? "Conversation finished"
530
+ text: node.data.systemMessage
465
531
  });
466
532
  end();
467
533
  }
468
- const stringOrStringArray = index._.union(index._.string, index._.array(index._.string));
534
+ const possibleContainsValue = index._.union(index._.string, index._.array(index._.string), null);
469
535
  const isIfBlockConditionMet = (ifBlock, submissions) => {
470
536
  const answer = submissions == null ? void 0 : submissions[ifBlock.data.compareKey];
471
537
  if (!answer)
@@ -492,20 +558,20 @@ const isIfBlockConditionMet = (ifBlock, submissions) => {
492
558
  }).with({
493
559
  compare: "contains",
494
560
  answer: {
495
- value: stringOrStringArray
561
+ value: possibleContainsValue
496
562
  }
497
563
  }, ({
498
564
  compareValue,
499
565
  answer: answer2
500
- }) => answer2.value.includes(compareValue)).with({
566
+ }) => answer2.value !== null && answer2.value.includes(compareValue)).with({
501
567
  compare: "notContains",
502
568
  answer: {
503
- value: stringOrStringArray
569
+ value: possibleContainsValue
504
570
  }
505
571
  }, ({
506
572
  compareValue,
507
573
  answer: answer2
508
- }) => !answer2.value.includes(compareValue)).with({
574
+ }) => answer2.value === null || !answer2.value.includes(compareValue)).with({
509
575
  answer: {
510
576
  type: "file"
511
577
  }
@@ -535,6 +601,30 @@ const SendButton = ({
535
601
  })]
536
602
  })
537
603
  });
604
+ const SkipButton = ({
605
+ class: className,
606
+ ...props
607
+ }) => index.o("button", {
608
+ class: index.clsx("focus-visible:ring-accent-7/50 text-neutral-9 flex flex-shrink-0 items-center justify-center gap-1 rounded-full py-[6px] pl-2 pr-3 text-sm underline-offset-2 transition-all focus:outline-none focus-visible:ring-4 focus-visible:ring-offset-2", className),
609
+ ...props,
610
+ children: [index.o("svg", {
611
+ class: "block",
612
+ width: "16",
613
+ height: "16",
614
+ viewBox: "0 0 16 16",
615
+ fill: "transparent",
616
+ stroke: "currentColor",
617
+ "stroke-linecap": "round",
618
+ "stroke-width": "1.5",
619
+ children: [index.o("title", {
620
+ children: "Skip"
621
+ }), index.o("path", {
622
+ d: "M3.25 11.25A5 5 0 0 1 12 7"
623
+ }), index.o("path", {
624
+ d: "M13.25 4.5V8.25H9.5"
625
+ })]
626
+ }), "Skip"]
627
+ });
538
628
  const useFocusOnMount = () => {
539
629
  const focusRef = index._$1(null);
540
630
  index.p(() => {
@@ -555,7 +645,7 @@ const ChatInputBoolean = ({
555
645
  onHeightChange();
556
646
  return index.o("form", {
557
647
  noValidate: true,
558
- class: "flex items-center gap-2",
648
+ class: "",
559
649
  onSubmit: (e) => {
560
650
  e.preventDefault();
561
651
  const value = index.N(e).with({
@@ -570,73 +660,73 @@ const ChatInputBoolean = ({
570
660
  const answer = index.parse(AnswerSchema, value);
571
661
  onSubmitSuccess(answer);
572
662
  },
573
- children: options.map((value, i2) => {
574
- return index.o("button", {
575
- ref: i2 === 0 ? focusRef : null,
576
- type: "submit",
577
- name: FIELD_NAME,
578
- value,
579
- class: "bg-lowest ease-expo-out ring-neutral-12/5 text-neutral-12 active:ring-accent-7 active:bg-accent-2 active:text-accent-11 fr duration-snappy block flex-1 overflow-hidden rounded-2xl px-2.5 py-2.5 ring-2 transition-all selection:bg-transparent",
580
- children: index.o("p", {
581
- class: "truncate text-center text-base",
582
- children: input.config.labels[value]
583
- })
584
- });
585
- })
663
+ children: [index.o("div", {
664
+ class: "flex items-center gap-2 p-2.5",
665
+ children: options.map((value, i2) => {
666
+ return index.o("button", {
667
+ ref: i2 === 0 ? focusRef : null,
668
+ type: "submit",
669
+ name: FIELD_NAME,
670
+ value,
671
+ class: "bg-lowest ease-expo-out ring-neutral-12/5 text-neutral-12 active:ring-accent-7 active:bg-accent-2 active:text-accent-11 fr duration-snappy block flex-1 overflow-hidden rounded-2xl px-2.5 py-2.5 ring-2 transition-all selection:bg-transparent",
672
+ children: index.o("p", {
673
+ class: "truncate text-center text-base",
674
+ children: input.config.labels[value]
675
+ })
676
+ });
677
+ })
678
+ }), input.config.optional && index.o("div", {
679
+ class: "pb-2",
680
+ children: index.o(SkipButton, {
681
+ class: "w-full",
682
+ type: "button",
683
+ onClick: () => onSubmitSuccess(null)
684
+ })
685
+ })]
586
686
  });
587
687
  };
588
688
  const InputError = ({
589
689
  error,
590
690
  onAnimationComplete
591
691
  }) => {
592
- return index.o(index.AnimatePresence, {
593
- children: error && index.o(index.m.div, {
594
- initial: {
595
- height: 0,
596
- opacity: 0
597
- },
598
- animate: {
599
- height: "auto",
600
- opacity: 1
601
- },
602
- exit: {
603
- height: 0,
604
- opacity: 0
605
- },
606
- onAnimationComplete,
607
- role: "alert",
608
- class: "text-error-11 flex max-w-full items-center gap-1 overflow-hidden rounded-full p-0.5 px-1 opacity-0",
609
- children: [index.o("svg", {
610
- class: "text-error-10",
611
- width: "16",
612
- height: "16",
613
- viewBox: "0 0 16 16",
614
- fill: "none",
615
- xmlns: "http://www.w3.org/2000/svg",
616
- children: [index.o("circle", {
617
- cx: "8",
618
- cy: "8",
619
- r: "6.3",
620
- stroke: "currentColor",
621
- "stroke-width": "1.4"
622
- }), index.o("rect", {
623
- x: "7",
624
- y: "4",
625
- width: "2",
626
- height: "5",
627
- fill: "currentColor"
628
- }), index.o("rect", {
629
- x: "7",
630
- y: "10",
631
- width: "2",
632
- height: "2",
633
- fill: "currentColor"
634
- })]
635
- }), index.o("p", {
636
- class: "truncate pr-1 text-sm",
637
- children: error.message
692
+ index.y(() => {
693
+ if (error) {
694
+ onAnimationComplete();
695
+ }
696
+ }, [error, onAnimationComplete]);
697
+ if (!error)
698
+ return null;
699
+ return index.o("div", {
700
+ role: "alert",
701
+ class: "text-error-11 flex max-w-full items-center gap-1 overflow-hidden rounded-full px-2 py-2",
702
+ children: [index.o("svg", {
703
+ class: "text-error-10 h-4 w-4",
704
+ viewBox: "0 0 16 16",
705
+ fill: "none",
706
+ xmlns: "http://www.w3.org/2000/svg",
707
+ children: [index.o("circle", {
708
+ cx: "8",
709
+ cy: "8",
710
+ r: "6.3",
711
+ stroke: "currentColor",
712
+ "stroke-width": "1.4"
713
+ }), index.o("rect", {
714
+ x: "7",
715
+ y: "4",
716
+ width: "2",
717
+ height: "5",
718
+ fill: "currentColor"
719
+ }), index.o("rect", {
720
+ x: "7",
721
+ y: "10",
722
+ width: "2",
723
+ height: "2",
724
+ fill: "currentColor"
638
725
  })]
639
- })
726
+ }), index.o("p", {
727
+ class: "truncate pr-1 text-sm",
728
+ children: error.message
729
+ })]
640
730
  });
641
731
  };
642
732
  const toBase64 = (file) => new Promise((resolve, reject) => {
@@ -651,7 +741,7 @@ const toBase64 = (file) => new Promise((resolve, reject) => {
651
741
  });
652
742
  const kbToReadableSize = (kb) => index.N(kb).with(index._.number.lte(1e3), () => `${Math.round(kb)}KB`).with(index._.number.lt(1e3 * 10), () => `${(kb / 1e3).toFixed(1)}MB`).otherwise(() => `${Math.round(kb / 1e3)}MB`);
653
743
  const addFileSizesKb = (files) => files.reduce((acc, cur) => acc + cur.sizeKb, 0);
654
- const isFileSubmission = index.isSubmissionOfType("file");
744
+ const isFileSubmission = isSubmissionOfType("file");
655
745
  const FILENAMES_TO_SHOW_QTY = 3;
656
746
  const FileThumbnail = ({
657
747
  file,
@@ -693,13 +783,13 @@ const ChatInputFile = ({
693
783
  }) => {
694
784
  var _a;
695
785
  const submission = (_a = index.application.current$.value.application) == null ? void 0 : _a.data.submissions[input.key];
696
- const [files, setFiles] = index.h(isFileSubmission(submission) ? submission.value : []);
786
+ const [files, setFiles] = index.h(isFileSubmission(submission) && submission.value !== null ? submission.value : []);
697
787
  const [error, setError] = index.h();
698
788
  const hiddenFileCount = files.length - FILENAMES_TO_SHOW_QTY;
699
789
  const totalSize = addFileSizesKb(files);
700
790
  const focusRef = useFocusOnMount();
701
791
  return index.o("form", {
702
- class: "flex flex-col gap-1",
792
+ class: "flex flex-col gap-1 p-2.5",
703
793
  onSubmit: (e) => {
704
794
  e.preventDefault();
705
795
  setError(void 0);
@@ -817,8 +907,13 @@ const ChatInputFile = ({
817
907
  type: "file",
818
908
  class: "sr-only"
819
909
  })]
820
- }), index.o(SendButton, {
821
- disabled: files.length === 0
910
+ }), index.o("div", {
911
+ class: "flex h-full flex-col items-center gap-2",
912
+ children: [index.o(SendButton, {
913
+ disabled: files.length === 0
914
+ }), input.config.optional && index.o(SkipButton, {
915
+ onClick: () => onSubmitSuccess(null)
916
+ })]
822
917
  })]
823
918
  }), error && index.o(InputError, {
824
919
  onAnimationComplete: onHeightChange,
@@ -2287,6 +2382,9 @@ var a = function(r, e) {
2287
2382
  }
2288
2383
  };
2289
2384
  };
2385
+ const LABEL_HEIGHT = 27;
2386
+ const GAP = 12;
2387
+ const PADDING = 10;
2290
2388
  const submitIfSingleChecked = (form) => {
2291
2389
  const formObj = Object.fromEntries(new FormData(form).entries());
2292
2390
  const isSingleChecked = Object.keys(formObj).length;
@@ -2296,7 +2394,7 @@ const submitIfSingleChecked = (form) => {
2296
2394
  bubbles: true
2297
2395
  }));
2298
2396
  };
2299
- const isMultipleChoiceSubmission = index.isSubmissionOfType("multiple-choice");
2397
+ const isMultipleChoiceSubmission = isSubmissionOfType("multiple-choice");
2300
2398
  const getResolver$1 = (config) => {
2301
2399
  const length = {
2302
2400
  min: config.minSelected ?? 0,
@@ -2329,7 +2427,7 @@ const ChatInputMultipleChoice = ({
2329
2427
  const isSingleChoice = input.config.minSelected === 1 && input.config.maxSelected === 1;
2330
2428
  return index.o("form", {
2331
2429
  noValidate: true,
2332
- class: "flex flex-col gap-1",
2430
+ class: "flex flex-col gap-1 pr-2.5",
2333
2431
  onChange: (e) => {
2334
2432
  if (isSingleChoice) {
2335
2433
  submitIfSingleChecked(e.currentTarget);
@@ -2340,9 +2438,12 @@ const ChatInputMultipleChoice = ({
2340
2438
  onSubmitSuccess(checked);
2341
2439
  }),
2342
2440
  children: [index.o("div", {
2343
- class: "flex items-center gap-2",
2344
- children: [index.o("div", {
2345
- class: index.clsx("flex w-full flex-1 flex-wrap gap-3 p-1", {
2441
+ class: "flex items-center gap-1",
2442
+ children: [index.o("ul", {
2443
+ style: {
2444
+ maxHeight: 6.5 * LABEL_HEIGHT + 5 * GAP + 2 * PADDING
2445
+ },
2446
+ class: index.clsx("gutter-stable flex w-full flex-1 flex-wrap gap-3 overflow-y-auto rounded-xl p-2.5 pr-4", {
2346
2447
  "justify-center": input.config.options.length === 1
2347
2448
  }),
2348
2449
  children: input.config.options.map((option, i2) => {
@@ -2351,7 +2452,8 @@ const ChatInputMultipleChoice = ({
2351
2452
  ref: setRef,
2352
2453
  ...props
2353
2454
  } = register(id);
2354
- return index.o("div", {
2455
+ return index.o("li", {
2456
+ class: "relative",
2355
2457
  children: [index.o("input", {
2356
2458
  autoFocus: i2 === 0,
2357
2459
  ref: (e) => {
@@ -2362,7 +2464,7 @@ const ChatInputMultipleChoice = ({
2362
2464
  },
2363
2465
  id,
2364
2466
  ...props,
2365
- class: "peer sr-only",
2467
+ class: "peer sr-only h-full",
2366
2468
  type: "checkbox"
2367
2469
  }), index.o("label", {
2368
2470
  class: "bg-lowest peer-focus-visible:ring-accent-7 active:outline-neutral-10 ease-expo-out outline-neutral-12/5 text-neutral-11 peer-checked:outline-accent-7 peer-checked:bg-accent-2 peer-checked:text-accent-9 duration-snappy block rounded-2xl px-2.5 py-1 outline outline-2 ring-0 ring-transparent transition-all selection:bg-transparent peer-focus-visible:ring-4 peer-focus-visible:ring-offset-2",
@@ -2371,24 +2473,29 @@ const ChatInputMultipleChoice = ({
2371
2473
  })]
2372
2474
  }, option.value);
2373
2475
  })
2374
- }), !isSingleChoice && index.o(SendButton, {})]
2375
- }), index.o(InputError, {
2376
- onAnimationComplete: onHeightChange,
2377
- error: (_b = errors2.checked) == null ? void 0 : _b.root
2476
+ }), index.o("div", {
2477
+ class: "flex flex-col items-center gap-2",
2478
+ children: [!isSingleChoice && index.o(SendButton, {}), input.config.minSelected === 0 && index.o(SkipButton, {
2479
+ type: "button",
2480
+ onClick: () => onSubmitSuccess([])
2481
+ })]
2482
+ })]
2483
+ }), index.o("div", {
2484
+ class: "px-1",
2485
+ children: index.o(InputError, {
2486
+ onAnimationComplete: onHeightChange,
2487
+ error: (_b = errors2.checked) == null ? void 0 : _b.root
2488
+ })
2378
2489
  })]
2379
2490
  });
2380
2491
  };
2381
2492
  const errors = {
2493
+ empty: "Please enter some text",
2382
2494
  email: "That doesn’t look like a valid email address",
2383
- phone: "That doesn’t look like a valid phone number"
2495
+ phone: "That doesn’t look like a valid phone number",
2496
+ url: "That doesn’t look like a valid URL"
2384
2497
  };
2385
2498
  const PhoneSchema = index.string(errors.phone, [index.regex(/^\+?[0-9 -]+$/, errors.phone)]);
2386
- const inputFormatToSchema = {
2387
- email: index.string(errors.email, [index.email(errors.email)]),
2388
- phone: index.transform(PhoneSchema, (value) => value.replace(/[^0-9]/g, "")),
2389
- text: index.string([index.minLength(1, "Please enter some text")]),
2390
- url: index.string([index.url("That doesn’t look like a valid URL")])
2391
- };
2392
2499
  const inputFormatToProps = {
2393
2500
  email: {
2394
2501
  type: "email",
@@ -2409,9 +2516,14 @@ const inputFormatToProps = {
2409
2516
  formNoValidate: true
2410
2517
  }
2411
2518
  };
2412
- const isTextSubmission = index.isSubmissionOfType("text");
2519
+ const isTextSubmission = isSubmissionOfType("text");
2413
2520
  const getResolver = (config) => i(index.object({
2414
- text: inputFormatToSchema[config.format]
2521
+ text: {
2522
+ email: index.string(errors.email, [index.email(errors.email)]),
2523
+ phone: index.transform(PhoneSchema, (value) => value.replace(/[^0-9]/g, "")),
2524
+ text: index.string([index.minLength(1, errors.empty)]),
2525
+ url: index.string([index.url(errors.url)])
2526
+ }[config.format]
2415
2527
  }));
2416
2528
  const ChatInputText = ({
2417
2529
  input,
@@ -2436,7 +2548,7 @@ const ChatInputText = ({
2436
2548
  ref: setRef,
2437
2549
  ...props
2438
2550
  } = register("text", {
2439
- required: true
2551
+ required: !input.config.optional
2440
2552
  });
2441
2553
  const ref = index._$1();
2442
2554
  index.y(() => {
@@ -2445,35 +2557,43 @@ const ChatInputText = ({
2445
2557
  ref.current.select();
2446
2558
  }
2447
2559
  }, []);
2448
- return index.o("form", {
2449
- noValidate: true,
2450
- class: "flex flex-col gap-1",
2451
- onSubmit: handleSubmit((submission2) => {
2452
- onSubmitSuccess(submission2.text);
2453
- }),
2454
- children: [index.o("div", {
2455
- class: "flex items-center gap-2",
2456
- children: [index.o("input", {
2457
- id: "chat-input",
2458
- ...props,
2459
- ...inputFormatToProps[input.config.format],
2460
- autocomplete: "off",
2461
- autoCapitalize: "off",
2462
- autoCorrect: "off",
2463
- autoFocus: true,
2464
- ref: (e) => {
2465
- if (e) {
2466
- ref.current = e;
2467
- }
2468
- setRef(e);
2469
- },
2470
- class: "outline-neutral-12/5 ease-expo-out placeholder:text-neutral-5 focus-visible:outline-accent-7 caret-accent-9 duration-snappy flex-grow rounded-full px-3 py-1 text-base outline outline-2 transition-all",
2471
- placeholder: input.config.placeholder
2472
- }), index.o(SendButton, {})]
2473
- }), index.o(InputError, {
2474
- onAnimationComplete: onHeightChange,
2475
- error: errors2.text
2476
- })]
2560
+ return index.o(index.k, {
2561
+ children: index.o("form", {
2562
+ noValidate: true,
2563
+ class: "flex flex-col gap-1 p-2.5",
2564
+ onSubmit: handleSubmit((submission2) => {
2565
+ onSubmitSuccess(submission2.text);
2566
+ }),
2567
+ children: [index.o("div", {
2568
+ class: "flex items-center gap-2",
2569
+ children: [index.o("div", {
2570
+ class: "relative min-w-0 flex-grow",
2571
+ children: [index.o("input", {
2572
+ id: "chat-input",
2573
+ ...props,
2574
+ ...inputFormatToProps[input.config.format],
2575
+ autocomplete: "off",
2576
+ autoCapitalize: "off",
2577
+ autoCorrect: "off",
2578
+ autoFocus: true,
2579
+ ref: (element) => {
2580
+ if (element) {
2581
+ ref.current = element;
2582
+ }
2583
+ setRef(element);
2584
+ },
2585
+ class: "outline-neutral-12/5 ease-expo-out placeholder:text-neutral-5 focus-visible:outline-accent-7 caret-accent-9 duration-snappy w-full rounded-full px-3 py-1 text-base outline outline-2 transition-all",
2586
+ placeholder: input.config.placeholder
2587
+ }), input.config.optional && index.o(SkipButton, {
2588
+ class: "absolute right-0 top-0",
2589
+ onClick: () => onSubmitSuccess(null)
2590
+ })]
2591
+ }), index.o(SendButton, {})]
2592
+ }), index.o(InputError, {
2593
+ onAnimationComplete: onHeightChange,
2594
+ error: errors2.text
2595
+ })]
2596
+ })
2477
2597
  });
2478
2598
  };
2479
2599
  const ChatInput = ({
@@ -2509,7 +2629,7 @@ const ChatInput = ({
2509
2629
  class: "bg-neutral-2/80 absolute bottom-0 w-full overflow-hidden rounded-b-3xl backdrop-blur-md backdrop-saturate-150",
2510
2630
  children: index.o("div", {
2511
2631
  ref: inputWrapperRef,
2512
- class: "border-neutral-12/5 border-t p-2.5",
2632
+ class: "border-neutral-12/5 border-t",
2513
2633
  children: index.N({
2514
2634
  application: index.application,
2515
2635
  input,
@@ -2517,7 +2637,7 @@ const ChatInput = ({
2517
2637
  }).with({
2518
2638
  input: index._.nullish
2519
2639
  }, () => index.o("div", {
2520
- class: "flex items-center gap-2",
2640
+ class: "flex items-center gap-2 p-2.5",
2521
2641
  children: [index.o("input", {
2522
2642
  "aria-hidden": "true",
2523
2643
  id: "chat-input",
@@ -2792,7 +2912,7 @@ const useChatService = () => {
2792
2912
  type: "text"
2793
2913
  }, async (message2) => {
2794
2914
  if (signal == null ? void 0 : signal.aborted)
2795
- throw new index.AbortedError();
2915
+ throw new AbortedError();
2796
2916
  setIsBotTyping(true);
2797
2917
  const typingTime = Math.min(Math.max(20, message2.text.length), 100) * TYPING_SPEED_MS_PER_CHARACTER;
2798
2918
  await new Promise((resolve) => {
@@ -2803,7 +2923,7 @@ const useChatService = () => {
2803
2923
  setIsBotTyping(false);
2804
2924
  }).otherwise(async () => void 0);
2805
2925
  if (signal == null ? void 0 : signal.aborted)
2806
- throw new index.AbortedError();
2926
+ throw new AbortedError();
2807
2927
  index.application.addMessage(message, groupId);
2808
2928
  },
2809
2929
  input: async ({
@@ -2811,14 +2931,15 @@ const useChatService = () => {
2811
2931
  signal
2812
2932
  }) => {
2813
2933
  if (signal == null ? void 0 : signal.aborted)
2814
- throw new index.AbortedError();
2934
+ throw new AbortedError();
2815
2935
  index.application.setInput(input);
2816
2936
  return await new Promise((resolve) => {
2817
2937
  const submitFunction = (submission) => {
2818
2938
  if (signal == null ? void 0 : signal.aborted)
2819
- throw new index.AbortedError();
2939
+ throw new AbortedError();
2820
2940
  index.application.setInput(void 0);
2821
2941
  if (input.key) {
2942
+ index.application.setSubmission;
2822
2943
  index.application.setSubmission(input.key, submission);
2823
2944
  }
2824
2945
  resolve(submission);
@@ -2869,14 +2990,27 @@ const JobApplicationContent = ({
2869
2990
  } = index.application.current$.peek();
2870
2991
  if (state !== "loaded")
2871
2992
  throw new Error(index.ERROR_MESSAGES.invalid_state);
2872
- const fromNodeId = currentApplication2.data.currentNodeId;
2993
+ let fromNodeId = currentApplication2.data.currentNodeId;
2873
2994
  scrollToEnd({
2874
2995
  behavior: "instant"
2875
2996
  });
2876
2997
  index.application.setInput(void 0);
2877
2998
  if (currentApplication2.data.isFinished)
2878
2999
  return;
2879
- index.application.removeLastGroupMessagesById(fromNodeId);
3000
+ if (fromNodeId === null) {
3001
+ fromNodeId = getHeadOrThrow(flow.nodes).id;
3002
+ index.application.setCurrentNodeId(fromNodeId);
3003
+ analytics.log({
3004
+ event: "APPLY_START",
3005
+ attributionKey: `job_${job.id}`,
3006
+ properties: {
3007
+ job_id: job.id,
3008
+ flow_id: flow.id
3009
+ }
3010
+ });
3011
+ } else {
3012
+ index.application.removeLastGroupMessagesById(fromNodeId);
3013
+ }
2880
3014
  const {
2881
3015
  interpret: interpret2,
2882
3016
  abort
@@ -2898,20 +3032,22 @@ const JobApplicationContent = ({
2898
3032
  onInterpret: (node, prevNode) => {
2899
3033
  const currentState = index.application.current$.peek().application;
2900
3034
  index.invariant(currentState);
2901
- currentState.data.sequence = currentState.data.sequence + 1;
2902
- analytics.log({
2903
- event: "FLOW_NODE",
2904
- attributionKey: `job_${job.id}`,
2905
- properties: {
2906
- flow_id: flow.id,
2907
- flow_version: flow.version,
2908
- job_id: job.id,
2909
- from_node_id: prevNode ? prevNode.id : null,
2910
- to_node_id: node.id,
2911
- sequence: currentState.data.sequence,
2912
- flow_session_id: currentState.data.flowSessionId
2913
- }
2914
- });
3035
+ if (prevNode) {
3036
+ currentState.data.sequence = currentState.data.sequence + 1;
3037
+ analytics.log({
3038
+ event: "FLOW_NODE",
3039
+ attributionKey: `job_${job.id}`,
3040
+ properties: {
3041
+ flow_id: flow.id,
3042
+ flow_version: flow.version,
3043
+ job_id: job.id,
3044
+ from_node_id: prevNode.id,
3045
+ to_node_id: node.id,
3046
+ sequence: currentState.data.sequence,
3047
+ flow_session_id: currentState.data.flowSessionId
3048
+ }
3049
+ });
3050
+ }
2915
3051
  index.application.setCurrentNodeId(node.id);
2916
3052
  },
2917
3053
  onFlowEnd: async () => {