@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,5 +1,28 @@
1
- import { _, g as getHeadOrThrow, A as AbortedError, N, a as getApplicationSubmissionsPayload, i as invariant, o as o$1, c as clsx, b as _$1, p, d as parse, e as picklist, m, f as AnimatePresence, h as application, j as h, k, l as isSubmissionOfType, C as Cn, n as parseAsync, V as ValiError, q as object, t as transform, r as maxLength, s as minLength, u as record, v as boolean, w as string, x as email, y as url, z as regex, B as y, T, D as inputHeight, F, E as viewState, G as ERROR_MESSAGES } from "./index-716f9049.js";
1
+ import { _, N, i as invariant, o as o$1, c as clsx, a as _$1, p, b as parse, d as picklist, y, e as application, h, k, C as Cn, f as parseAsync, V as ValiError, g as object, t as transform, m as maxLength, j as minLength, r as record, l as boolean, s as string, n as email, u as url, q as regex, T, v as inputHeight, w as m, A as AnimatePresence, F, x as viewState, E as ERROR_MESSAGES } from "./index-1808db67.js";
2
2
  import "@inploi/sdk";
3
+ const getHeadOrThrow = (nodes) => {
4
+ const head = nodes.find((n2) => n2.isHead);
5
+ if (!head)
6
+ throw new Error("No head node found");
7
+ return head;
8
+ };
9
+ const getApplicationSubmissionsPayload = (submissions) => {
10
+ const payload = Object.entries(submissions).reduce((acc, [key, submission]) => {
11
+ acc[key] = submission.value;
12
+ return acc;
13
+ }, {});
14
+ return payload;
15
+ };
16
+ const isSubmissionOfType = (type) => (submission) => {
17
+ if (!submission)
18
+ return false;
19
+ return submission.type === type;
20
+ };
21
+ class AbortedError extends Error {
22
+ constructor() {
23
+ super("Aborted");
24
+ }
25
+ }
3
26
  const followNodes = ({
4
27
  node,
5
28
  nodes,
@@ -238,6 +261,7 @@ async function interpretSubmitNode({
238
261
  href: href.toString(),
239
262
  text: "Finalise application on partner"
240
263
  });
264
+ next(node.nextId);
241
265
  }).with({
242
266
  success: true
243
267
  }, async () => {
@@ -318,15 +342,24 @@ async function interpretQuestionTextNode({
318
342
  key: node.data.key,
319
343
  type: "text",
320
344
  config: {
345
+ optional: node.data.optional,
321
346
  placeholder: node.data.placeholder,
322
347
  format: node.data.format
323
348
  }
324
349
  });
325
- await chat.sendMessage({
326
- author: "user",
327
- type: "text",
328
- text: reply.value
329
- });
350
+ if (reply.value === null) {
351
+ await chat.sendMessage({
352
+ type: "system",
353
+ variant: "info",
354
+ text: "Skipped"
355
+ });
356
+ } else {
357
+ await chat.sendMessage({
358
+ author: "user",
359
+ type: "text",
360
+ text: reply.value
361
+ });
362
+ }
330
363
  next(node.nextId);
331
364
  }
332
365
  async function interpretQuestionNumberNode({
@@ -343,15 +376,24 @@ async function interpretQuestionNumberNode({
343
376
  key: node.data.key,
344
377
  type: "text",
345
378
  config: {
379
+ optional: node.data.optional,
346
380
  placeholder: node.data.placeholder,
347
381
  format: "text"
348
382
  }
349
383
  });
350
- await chat.sendMessage({
351
- author: "user",
352
- type: "text",
353
- text: reply.value
354
- });
384
+ if (reply.value === null) {
385
+ await chat.sendMessage({
386
+ type: "system",
387
+ variant: "info",
388
+ text: "Skipped"
389
+ });
390
+ } else {
391
+ await chat.sendMessage({
392
+ author: "user",
393
+ type: "text",
394
+ text: reply.value
395
+ });
396
+ }
355
397
  next(node.nextId);
356
398
  }
357
399
  async function interpretQuestionEnumNode({
@@ -369,11 +411,19 @@ async function interpretQuestionEnumNode({
369
411
  type: "multiple-choice",
370
412
  config: node.data
371
413
  });
372
- await chat.sendMessage({
373
- author: "user",
374
- type: "text",
375
- text: node.data.options.filter((o2) => reply.value.includes(o2.value)).map((o2) => o2.label).join(", ")
376
- });
414
+ if (reply.value.length === 0) {
415
+ await chat.sendMessage({
416
+ type: "system",
417
+ variant: "info",
418
+ text: "Skipped"
419
+ });
420
+ } else {
421
+ await chat.sendMessage({
422
+ author: "user",
423
+ type: "text",
424
+ text: node.data.options.filter((o2) => reply.value.includes(o2.value)).map((o2) => o2.label).join(", ")
425
+ });
426
+ }
377
427
  next(node.nextId);
378
428
  }
379
429
  async function interpretQuestionBooleanNode({
@@ -390,6 +440,7 @@ async function interpretQuestionBooleanNode({
390
440
  key: node.data.key,
391
441
  type: "boolean",
392
442
  config: {
443
+ optional: node.data.optional,
393
444
  labels: {
394
445
  true: node.data.trueLabel,
395
446
  false: node.data.falseLabel
@@ -397,15 +448,22 @@ async function interpretQuestionBooleanNode({
397
448
  }
398
449
  });
399
450
  const reply = input.value;
400
- const labelMap = {
401
- true: node.data.trueLabel,
402
- false: node.data.falseLabel
403
- };
404
- await chat.sendMessage({
405
- author: "user",
406
- type: "text",
407
- text: labelMap[reply]
408
- });
451
+ if (reply === null) {
452
+ await chat.sendMessage({
453
+ type: "system",
454
+ variant: "info",
455
+ text: "Skipped"
456
+ });
457
+ } else {
458
+ await chat.sendMessage({
459
+ author: "user",
460
+ type: "text",
461
+ text: {
462
+ true: node.data.trueLabel,
463
+ false: node.data.falseLabel
464
+ }[reply]
465
+ });
466
+ }
409
467
  next(node.nextId);
410
468
  }
411
469
  async function interpretQuestionAddressNode({
@@ -434,19 +492,28 @@ async function interpretQuestionFileNode({
434
492
  key: node.data.key,
435
493
  type: "file",
436
494
  config: {
495
+ optional: node.data.optional,
437
496
  extensions: node.data.extensions,
438
497
  // default value for fileSizeLimitKib is 10MB
439
498
  fileSizeLimitKib: node.data.maxSizeKb ?? 10 * 1024,
440
499
  allowMultiple: node.data.multiple === true
441
500
  }
442
501
  });
443
- for await (const file of files.value) {
502
+ if (files.value === null) {
444
503
  await chat.sendMessage({
445
- author: "user",
446
- type: "file",
447
- fileName: file.name,
448
- fileSizeKb: file.sizeKb
504
+ type: "system",
505
+ variant: "info",
506
+ text: "Skipped"
449
507
  });
508
+ } else {
509
+ for (const file of files.value) {
510
+ await chat.sendMessage({
511
+ author: "user",
512
+ type: "file",
513
+ fileName: file.name,
514
+ fileSizeKb: file.sizeKb
515
+ });
516
+ }
450
517
  }
451
518
  next(node.nextId);
452
519
  }
@@ -455,15 +522,14 @@ async function interpretEndFlowNode({
455
522
  end,
456
523
  node
457
524
  }) {
458
- var _a;
459
525
  await chat.sendMessage({
460
526
  type: "system",
461
527
  variant: "info",
462
- text: ((_a = node.data) == null ? void 0 : _a.systemMessage) ?? "Conversation finished"
528
+ text: node.data.systemMessage
463
529
  });
464
530
  end();
465
531
  }
466
- const stringOrStringArray = _.union(_.string, _.array(_.string));
532
+ const possibleContainsValue = _.union(_.string, _.array(_.string), null);
467
533
  const isIfBlockConditionMet = (ifBlock, submissions) => {
468
534
  const answer = submissions == null ? void 0 : submissions[ifBlock.data.compareKey];
469
535
  if (!answer)
@@ -490,20 +556,20 @@ const isIfBlockConditionMet = (ifBlock, submissions) => {
490
556
  }).with({
491
557
  compare: "contains",
492
558
  answer: {
493
- value: stringOrStringArray
559
+ value: possibleContainsValue
494
560
  }
495
561
  }, ({
496
562
  compareValue,
497
563
  answer: answer2
498
- }) => answer2.value.includes(compareValue)).with({
564
+ }) => answer2.value !== null && answer2.value.includes(compareValue)).with({
499
565
  compare: "notContains",
500
566
  answer: {
501
- value: stringOrStringArray
567
+ value: possibleContainsValue
502
568
  }
503
569
  }, ({
504
570
  compareValue,
505
571
  answer: answer2
506
- }) => !answer2.value.includes(compareValue)).with({
572
+ }) => answer2.value === null || !answer2.value.includes(compareValue)).with({
507
573
  answer: {
508
574
  type: "file"
509
575
  }
@@ -533,6 +599,30 @@ const SendButton = ({
533
599
  })]
534
600
  })
535
601
  });
602
+ const SkipButton = ({
603
+ class: className,
604
+ ...props
605
+ }) => o$1("button", {
606
+ class: 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),
607
+ ...props,
608
+ children: [o$1("svg", {
609
+ class: "block",
610
+ width: "16",
611
+ height: "16",
612
+ viewBox: "0 0 16 16",
613
+ fill: "transparent",
614
+ stroke: "currentColor",
615
+ "stroke-linecap": "round",
616
+ "stroke-width": "1.5",
617
+ children: [o$1("title", {
618
+ children: "Skip"
619
+ }), o$1("path", {
620
+ d: "M3.25 11.25A5 5 0 0 1 12 7"
621
+ }), o$1("path", {
622
+ d: "M13.25 4.5V8.25H9.5"
623
+ })]
624
+ }), "Skip"]
625
+ });
536
626
  const useFocusOnMount = () => {
537
627
  const focusRef = _$1(null);
538
628
  p(() => {
@@ -553,7 +643,7 @@ const ChatInputBoolean = ({
553
643
  onHeightChange();
554
644
  return o$1("form", {
555
645
  noValidate: true,
556
- class: "flex items-center gap-2",
646
+ class: "",
557
647
  onSubmit: (e) => {
558
648
  e.preventDefault();
559
649
  const value = N(e).with({
@@ -568,73 +658,73 @@ const ChatInputBoolean = ({
568
658
  const answer = parse(AnswerSchema, value);
569
659
  onSubmitSuccess(answer);
570
660
  },
571
- children: options.map((value, i2) => {
572
- return o$1("button", {
573
- ref: i2 === 0 ? focusRef : null,
574
- type: "submit",
575
- name: FIELD_NAME,
576
- value,
577
- 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",
578
- children: o$1("p", {
579
- class: "truncate text-center text-base",
580
- children: input.config.labels[value]
581
- })
582
- });
583
- })
661
+ children: [o$1("div", {
662
+ class: "flex items-center gap-2 p-2.5",
663
+ children: options.map((value, i2) => {
664
+ return o$1("button", {
665
+ ref: i2 === 0 ? focusRef : null,
666
+ type: "submit",
667
+ name: FIELD_NAME,
668
+ value,
669
+ 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",
670
+ children: o$1("p", {
671
+ class: "truncate text-center text-base",
672
+ children: input.config.labels[value]
673
+ })
674
+ });
675
+ })
676
+ }), input.config.optional && o$1("div", {
677
+ class: "pb-2",
678
+ children: o$1(SkipButton, {
679
+ class: "w-full",
680
+ type: "button",
681
+ onClick: () => onSubmitSuccess(null)
682
+ })
683
+ })]
584
684
  });
585
685
  };
586
686
  const InputError = ({
587
687
  error,
588
688
  onAnimationComplete
589
689
  }) => {
590
- return o$1(AnimatePresence, {
591
- children: error && o$1(m.div, {
592
- initial: {
593
- height: 0,
594
- opacity: 0
595
- },
596
- animate: {
597
- height: "auto",
598
- opacity: 1
599
- },
600
- exit: {
601
- height: 0,
602
- opacity: 0
603
- },
604
- onAnimationComplete,
605
- role: "alert",
606
- class: "text-error-11 flex max-w-full items-center gap-1 overflow-hidden rounded-full p-0.5 px-1 opacity-0",
607
- children: [o$1("svg", {
608
- class: "text-error-10",
609
- width: "16",
610
- height: "16",
611
- viewBox: "0 0 16 16",
612
- fill: "none",
613
- xmlns: "http://www.w3.org/2000/svg",
614
- children: [o$1("circle", {
615
- cx: "8",
616
- cy: "8",
617
- r: "6.3",
618
- stroke: "currentColor",
619
- "stroke-width": "1.4"
620
- }), o$1("rect", {
621
- x: "7",
622
- y: "4",
623
- width: "2",
624
- height: "5",
625
- fill: "currentColor"
626
- }), o$1("rect", {
627
- x: "7",
628
- y: "10",
629
- width: "2",
630
- height: "2",
631
- fill: "currentColor"
632
- })]
633
- }), o$1("p", {
634
- class: "truncate pr-1 text-sm",
635
- children: error.message
690
+ y(() => {
691
+ if (error) {
692
+ onAnimationComplete();
693
+ }
694
+ }, [error, onAnimationComplete]);
695
+ if (!error)
696
+ return null;
697
+ return o$1("div", {
698
+ role: "alert",
699
+ class: "text-error-11 flex max-w-full items-center gap-1 overflow-hidden rounded-full px-2 py-2",
700
+ children: [o$1("svg", {
701
+ class: "text-error-10 h-4 w-4",
702
+ viewBox: "0 0 16 16",
703
+ fill: "none",
704
+ xmlns: "http://www.w3.org/2000/svg",
705
+ children: [o$1("circle", {
706
+ cx: "8",
707
+ cy: "8",
708
+ r: "6.3",
709
+ stroke: "currentColor",
710
+ "stroke-width": "1.4"
711
+ }), o$1("rect", {
712
+ x: "7",
713
+ y: "4",
714
+ width: "2",
715
+ height: "5",
716
+ fill: "currentColor"
717
+ }), o$1("rect", {
718
+ x: "7",
719
+ y: "10",
720
+ width: "2",
721
+ height: "2",
722
+ fill: "currentColor"
636
723
  })]
637
- })
724
+ }), o$1("p", {
725
+ class: "truncate pr-1 text-sm",
726
+ children: error.message
727
+ })]
638
728
  });
639
729
  };
640
730
  const toBase64 = (file) => new Promise((resolve, reject) => {
@@ -691,13 +781,13 @@ const ChatInputFile = ({
691
781
  }) => {
692
782
  var _a;
693
783
  const submission = (_a = application.current$.value.application) == null ? void 0 : _a.data.submissions[input.key];
694
- const [files, setFiles] = h(isFileSubmission(submission) ? submission.value : []);
784
+ const [files, setFiles] = h(isFileSubmission(submission) && submission.value !== null ? submission.value : []);
695
785
  const [error, setError] = h();
696
786
  const hiddenFileCount = files.length - FILENAMES_TO_SHOW_QTY;
697
787
  const totalSize = addFileSizesKb(files);
698
788
  const focusRef = useFocusOnMount();
699
789
  return o$1("form", {
700
- class: "flex flex-col gap-1",
790
+ class: "flex flex-col gap-1 p-2.5",
701
791
  onSubmit: (e) => {
702
792
  e.preventDefault();
703
793
  setError(void 0);
@@ -815,8 +905,13 @@ const ChatInputFile = ({
815
905
  type: "file",
816
906
  class: "sr-only"
817
907
  })]
818
- }), o$1(SendButton, {
819
- disabled: files.length === 0
908
+ }), o$1("div", {
909
+ class: "flex h-full flex-col items-center gap-2",
910
+ children: [o$1(SendButton, {
911
+ disabled: files.length === 0
912
+ }), input.config.optional && o$1(SkipButton, {
913
+ onClick: () => onSubmitSuccess(null)
914
+ })]
820
915
  })]
821
916
  }), error && o$1(InputError, {
822
917
  onAnimationComplete: onHeightChange,
@@ -2285,6 +2380,9 @@ var a = function(r, e) {
2285
2380
  }
2286
2381
  };
2287
2382
  };
2383
+ const LABEL_HEIGHT = 27;
2384
+ const GAP = 12;
2385
+ const PADDING = 10;
2288
2386
  const submitIfSingleChecked = (form) => {
2289
2387
  const formObj = Object.fromEntries(new FormData(form).entries());
2290
2388
  const isSingleChecked = Object.keys(formObj).length;
@@ -2327,7 +2425,7 @@ const ChatInputMultipleChoice = ({
2327
2425
  const isSingleChoice = input.config.minSelected === 1 && input.config.maxSelected === 1;
2328
2426
  return o$1("form", {
2329
2427
  noValidate: true,
2330
- class: "flex flex-col gap-1",
2428
+ class: "flex flex-col gap-1 pr-2.5",
2331
2429
  onChange: (e) => {
2332
2430
  if (isSingleChoice) {
2333
2431
  submitIfSingleChecked(e.currentTarget);
@@ -2338,9 +2436,12 @@ const ChatInputMultipleChoice = ({
2338
2436
  onSubmitSuccess(checked);
2339
2437
  }),
2340
2438
  children: [o$1("div", {
2341
- class: "flex items-center gap-2",
2342
- children: [o$1("div", {
2343
- class: clsx("flex w-full flex-1 flex-wrap gap-3 p-1", {
2439
+ class: "flex items-center gap-1",
2440
+ children: [o$1("ul", {
2441
+ style: {
2442
+ maxHeight: 6.5 * LABEL_HEIGHT + 5 * GAP + 2 * PADDING
2443
+ },
2444
+ class: clsx("gutter-stable flex w-full flex-1 flex-wrap gap-3 overflow-y-auto rounded-xl p-2.5 pr-4", {
2344
2445
  "justify-center": input.config.options.length === 1
2345
2446
  }),
2346
2447
  children: input.config.options.map((option, i2) => {
@@ -2349,7 +2450,8 @@ const ChatInputMultipleChoice = ({
2349
2450
  ref: setRef,
2350
2451
  ...props
2351
2452
  } = register(id);
2352
- return o$1("div", {
2453
+ return o$1("li", {
2454
+ class: "relative",
2353
2455
  children: [o$1("input", {
2354
2456
  autoFocus: i2 === 0,
2355
2457
  ref: (e) => {
@@ -2360,7 +2462,7 @@ const ChatInputMultipleChoice = ({
2360
2462
  },
2361
2463
  id,
2362
2464
  ...props,
2363
- class: "peer sr-only",
2465
+ class: "peer sr-only h-full",
2364
2466
  type: "checkbox"
2365
2467
  }), o$1("label", {
2366
2468
  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",
@@ -2369,24 +2471,29 @@ const ChatInputMultipleChoice = ({
2369
2471
  })]
2370
2472
  }, option.value);
2371
2473
  })
2372
- }), !isSingleChoice && o$1(SendButton, {})]
2373
- }), o$1(InputError, {
2374
- onAnimationComplete: onHeightChange,
2375
- error: (_b = errors2.checked) == null ? void 0 : _b.root
2474
+ }), o$1("div", {
2475
+ class: "flex flex-col items-center gap-2",
2476
+ children: [!isSingleChoice && o$1(SendButton, {}), input.config.minSelected === 0 && o$1(SkipButton, {
2477
+ type: "button",
2478
+ onClick: () => onSubmitSuccess([])
2479
+ })]
2480
+ })]
2481
+ }), o$1("div", {
2482
+ class: "px-1",
2483
+ children: o$1(InputError, {
2484
+ onAnimationComplete: onHeightChange,
2485
+ error: (_b = errors2.checked) == null ? void 0 : _b.root
2486
+ })
2376
2487
  })]
2377
2488
  });
2378
2489
  };
2379
2490
  const errors = {
2491
+ empty: "Please enter some text",
2380
2492
  email: "That doesn’t look like a valid email address",
2381
- phone: "That doesn’t look like a valid phone number"
2493
+ phone: "That doesn’t look like a valid phone number",
2494
+ url: "That doesn’t look like a valid URL"
2382
2495
  };
2383
2496
  const PhoneSchema = string(errors.phone, [regex(/^\+?[0-9 -]+$/, errors.phone)]);
2384
- const inputFormatToSchema = {
2385
- email: string(errors.email, [email(errors.email)]),
2386
- phone: transform(PhoneSchema, (value) => value.replace(/[^0-9]/g, "")),
2387
- text: string([minLength(1, "Please enter some text")]),
2388
- url: string([url("That doesn’t look like a valid URL")])
2389
- };
2390
2497
  const inputFormatToProps = {
2391
2498
  email: {
2392
2499
  type: "email",
@@ -2409,7 +2516,12 @@ const inputFormatToProps = {
2409
2516
  };
2410
2517
  const isTextSubmission = isSubmissionOfType("text");
2411
2518
  const getResolver = (config) => i(object({
2412
- text: inputFormatToSchema[config.format]
2519
+ text: {
2520
+ email: string(errors.email, [email(errors.email)]),
2521
+ phone: transform(PhoneSchema, (value) => value.replace(/[^0-9]/g, "")),
2522
+ text: string([minLength(1, errors.empty)]),
2523
+ url: string([url(errors.url)])
2524
+ }[config.format]
2413
2525
  }));
2414
2526
  const ChatInputText = ({
2415
2527
  input,
@@ -2434,7 +2546,7 @@ const ChatInputText = ({
2434
2546
  ref: setRef,
2435
2547
  ...props
2436
2548
  } = register("text", {
2437
- required: true
2549
+ required: !input.config.optional
2438
2550
  });
2439
2551
  const ref = _$1();
2440
2552
  y(() => {
@@ -2443,35 +2555,43 @@ const ChatInputText = ({
2443
2555
  ref.current.select();
2444
2556
  }
2445
2557
  }, []);
2446
- return o$1("form", {
2447
- noValidate: true,
2448
- class: "flex flex-col gap-1",
2449
- onSubmit: handleSubmit((submission2) => {
2450
- onSubmitSuccess(submission2.text);
2451
- }),
2452
- children: [o$1("div", {
2453
- class: "flex items-center gap-2",
2454
- children: [o$1("input", {
2455
- id: "chat-input",
2456
- ...props,
2457
- ...inputFormatToProps[input.config.format],
2458
- autocomplete: "off",
2459
- autoCapitalize: "off",
2460
- autoCorrect: "off",
2461
- autoFocus: true,
2462
- ref: (e) => {
2463
- if (e) {
2464
- ref.current = e;
2465
- }
2466
- setRef(e);
2467
- },
2468
- 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",
2469
- placeholder: input.config.placeholder
2470
- }), o$1(SendButton, {})]
2471
- }), o$1(InputError, {
2472
- onAnimationComplete: onHeightChange,
2473
- error: errors2.text
2474
- })]
2558
+ return o$1(k, {
2559
+ children: o$1("form", {
2560
+ noValidate: true,
2561
+ class: "flex flex-col gap-1 p-2.5",
2562
+ onSubmit: handleSubmit((submission2) => {
2563
+ onSubmitSuccess(submission2.text);
2564
+ }),
2565
+ children: [o$1("div", {
2566
+ class: "flex items-center gap-2",
2567
+ children: [o$1("div", {
2568
+ class: "relative min-w-0 flex-grow",
2569
+ children: [o$1("input", {
2570
+ id: "chat-input",
2571
+ ...props,
2572
+ ...inputFormatToProps[input.config.format],
2573
+ autocomplete: "off",
2574
+ autoCapitalize: "off",
2575
+ autoCorrect: "off",
2576
+ autoFocus: true,
2577
+ ref: (element) => {
2578
+ if (element) {
2579
+ ref.current = element;
2580
+ }
2581
+ setRef(element);
2582
+ },
2583
+ 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",
2584
+ placeholder: input.config.placeholder
2585
+ }), input.config.optional && o$1(SkipButton, {
2586
+ class: "absolute right-0 top-0",
2587
+ onClick: () => onSubmitSuccess(null)
2588
+ })]
2589
+ }), o$1(SendButton, {})]
2590
+ }), o$1(InputError, {
2591
+ onAnimationComplete: onHeightChange,
2592
+ error: errors2.text
2593
+ })]
2594
+ })
2475
2595
  });
2476
2596
  };
2477
2597
  const ChatInput = ({
@@ -2507,7 +2627,7 @@ const ChatInput = ({
2507
2627
  class: "bg-neutral-2/80 absolute bottom-0 w-full overflow-hidden rounded-b-3xl backdrop-blur-md backdrop-saturate-150",
2508
2628
  children: o$1("div", {
2509
2629
  ref: inputWrapperRef,
2510
- class: "border-neutral-12/5 border-t p-2.5",
2630
+ class: "border-neutral-12/5 border-t",
2511
2631
  children: N({
2512
2632
  application,
2513
2633
  input,
@@ -2515,7 +2635,7 @@ const ChatInput = ({
2515
2635
  }).with({
2516
2636
  input: _.nullish
2517
2637
  }, () => o$1("div", {
2518
- class: "flex items-center gap-2",
2638
+ class: "flex items-center gap-2 p-2.5",
2519
2639
  children: [o$1("input", {
2520
2640
  "aria-hidden": "true",
2521
2641
  id: "chat-input",
@@ -2817,6 +2937,7 @@ const useChatService = () => {
2817
2937
  throw new AbortedError();
2818
2938
  application.setInput(void 0);
2819
2939
  if (input.key) {
2940
+ application.setSubmission;
2820
2941
  application.setSubmission(input.key, submission);
2821
2942
  }
2822
2943
  resolve(submission);
@@ -2867,14 +2988,27 @@ const JobApplicationContent = ({
2867
2988
  } = application.current$.peek();
2868
2989
  if (state !== "loaded")
2869
2990
  throw new Error(ERROR_MESSAGES.invalid_state);
2870
- const fromNodeId = currentApplication2.data.currentNodeId;
2991
+ let fromNodeId = currentApplication2.data.currentNodeId;
2871
2992
  scrollToEnd({
2872
2993
  behavior: "instant"
2873
2994
  });
2874
2995
  application.setInput(void 0);
2875
2996
  if (currentApplication2.data.isFinished)
2876
2997
  return;
2877
- application.removeLastGroupMessagesById(fromNodeId);
2998
+ if (fromNodeId === null) {
2999
+ fromNodeId = getHeadOrThrow(flow.nodes).id;
3000
+ application.setCurrentNodeId(fromNodeId);
3001
+ analytics.log({
3002
+ event: "APPLY_START",
3003
+ attributionKey: `job_${job.id}`,
3004
+ properties: {
3005
+ job_id: job.id,
3006
+ flow_id: flow.id
3007
+ }
3008
+ });
3009
+ } else {
3010
+ application.removeLastGroupMessagesById(fromNodeId);
3011
+ }
2878
3012
  const {
2879
3013
  interpret: interpret2,
2880
3014
  abort
@@ -2896,20 +3030,22 @@ const JobApplicationContent = ({
2896
3030
  onInterpret: (node, prevNode) => {
2897
3031
  const currentState = application.current$.peek().application;
2898
3032
  invariant(currentState);
2899
- currentState.data.sequence = currentState.data.sequence + 1;
2900
- analytics.log({
2901
- event: "FLOW_NODE",
2902
- attributionKey: `job_${job.id}`,
2903
- properties: {
2904
- flow_id: flow.id,
2905
- flow_version: flow.version,
2906
- job_id: job.id,
2907
- from_node_id: prevNode ? prevNode.id : null,
2908
- to_node_id: node.id,
2909
- sequence: currentState.data.sequence,
2910
- flow_session_id: currentState.data.flowSessionId
2911
- }
2912
- });
3033
+ if (prevNode) {
3034
+ currentState.data.sequence = currentState.data.sequence + 1;
3035
+ analytics.log({
3036
+ event: "FLOW_NODE",
3037
+ attributionKey: `job_${job.id}`,
3038
+ properties: {
3039
+ flow_id: flow.id,
3040
+ flow_version: flow.version,
3041
+ job_id: job.id,
3042
+ from_node_id: prevNode.id,
3043
+ to_node_id: node.id,
3044
+ sequence: currentState.data.sequence,
3045
+ flow_session_id: currentState.data.flowSessionId
3046
+ }
3047
+ });
3048
+ }
2913
3049
  application.setCurrentNodeId(node.id);
2914
3050
  },
2915
3051
  onFlowEnd: async () => {