@inploi/plugin-chatbot 3.11.0 → 3.12.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.
- package/cdn/index.js +12 -12
- package/dist/{job-application-content-3cc77e28.js → chatbot-body-04c70e40.js} +341 -265
- package/dist/{job-application-content-69860abf.cjs → chatbot-body-a6d50df4.cjs} +352 -276
- package/dist/chatbot.api.d.ts +2441 -13
- package/dist/chatbot.d.ts +25 -5
- package/dist/chatbot.idb.d.ts +15 -7
- package/dist/chatbot.utils.d.ts +9 -6
- package/dist/{index-356e7fb6.cjs → index-413f4efe.cjs} +2175 -1999
- package/dist/{index-5333c591.js → index-ec980c39.js} +2194 -2018
- package/dist/index.dev.d.ts +1 -0
- package/dist/interpreter.d.ts +5 -2
- package/dist/plugin-chatbot.cjs +1 -1
- package/dist/plugin-chatbot.js +2 -2
- package/package.json +8 -6
- package/dist/chatbot.state.d.ts +0 -94
|
@@ -1,29 +1,5 @@
|
|
|
1
|
-
import { N,
|
|
1
|
+
import { _, g as getHeadOrThrow, i as invariant, A as AbortedError, N, a as getFlowSubmissionsPayload, h as hasProp, k as kbToReadableSize, o as o$1, c as clsx, b as a$2, d as _$1, y, F, s as store, p, e as parse, f as picklist, j as h, l as k, m as isSubmissionOfType, C as Cn, n as parseAsync, V as ValiError, q as object, t as transform, r as maxLength, u as minLength, v as record, w as boolean, x as string, z as email, B as url, D as regex, T, E as m, G as AnimatePresence, H as ERROR_MESSAGES } from "./index-ec980c39.js";
|
|
2
2
|
import "@inploi/sdk";
|
|
3
|
-
const kbToReadableSize = (kb) => N(kb).with(_.number.lte(1e3), () => `${Math.round(kb)}KB`).with(_.number.lt(1e3 * 10), () => `${(kb / 1e3).toFixed(1)}MB`).otherwise(() => `${Math.round(kb / 1e3)}MB`);
|
|
4
|
-
const getHeadOrThrow = (nodes) => {
|
|
5
|
-
const head = nodes.find((n2) => n2.isHead);
|
|
6
|
-
if (!head)
|
|
7
|
-
throw new Error("No head node found");
|
|
8
|
-
return head;
|
|
9
|
-
};
|
|
10
|
-
const getApplicationSubmissionsPayload = (submissions) => {
|
|
11
|
-
const payload = Object.entries(submissions).reduce((acc, [key, submission]) => {
|
|
12
|
-
acc[key] = submission.value;
|
|
13
|
-
return acc;
|
|
14
|
-
}, {});
|
|
15
|
-
return payload;
|
|
16
|
-
};
|
|
17
|
-
const isSubmissionOfType = (type) => (submission) => {
|
|
18
|
-
if (!submission)
|
|
19
|
-
return false;
|
|
20
|
-
return submission.type === type;
|
|
21
|
-
};
|
|
22
|
-
class AbortedError extends Error {
|
|
23
|
-
constructor() {
|
|
24
|
-
super("Aborted");
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
3
|
const followNodes = ({
|
|
28
4
|
node,
|
|
29
5
|
nodes,
|
|
@@ -75,7 +51,7 @@ const createFlowInterpreter = ({
|
|
|
75
51
|
onFlowEnd,
|
|
76
52
|
onInterpret
|
|
77
53
|
}) => {
|
|
78
|
-
|
|
54
|
+
let controller = new AbortController();
|
|
79
55
|
const interpretNode = async (node, prevNode) => {
|
|
80
56
|
const submissions = getSubmissions();
|
|
81
57
|
onInterpret == null ? void 0 : onInterpret(node, prevNode);
|
|
@@ -120,6 +96,27 @@ const createFlowInterpreter = ({
|
|
|
120
96
|
const startNode = flow.find((node) => node.id === startFromNodeId) ?? getHeadOrThrow(flow);
|
|
121
97
|
return interpretNode(startNode);
|
|
122
98
|
},
|
|
99
|
+
undo: (nodeHistory) => {
|
|
100
|
+
let removed = 1;
|
|
101
|
+
const formerLastNode = flow.find((n2) => n2.id === nodeHistory[nodeHistory.length - 1]);
|
|
102
|
+
for (let i2 = nodeHistory.length - 2; i2 > 0; i2--) {
|
|
103
|
+
const nodeId = nodeHistory[i2];
|
|
104
|
+
const node = flow.find((n2) => n2.id === nodeId);
|
|
105
|
+
if (!node)
|
|
106
|
+
break;
|
|
107
|
+
removed++;
|
|
108
|
+
if (node.type.startsWith("question-"))
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
controller.abort();
|
|
112
|
+
controller = new AbortController();
|
|
113
|
+
const newStartNode = flow.find((node) => node.id === nodeHistory[nodeHistory.length - removed]);
|
|
114
|
+
invariant(newStartNode, "Undo failed: new start node not found");
|
|
115
|
+
interpretNode(newStartNode, formerLastNode);
|
|
116
|
+
return {
|
|
117
|
+
removed
|
|
118
|
+
};
|
|
119
|
+
},
|
|
123
120
|
abort: () => {
|
|
124
121
|
controller.abort();
|
|
125
122
|
}
|
|
@@ -198,22 +195,17 @@ async function interpretSubmitNode({
|
|
|
198
195
|
analytics
|
|
199
196
|
}) {
|
|
200
197
|
await chat.userInput({
|
|
201
|
-
type: "
|
|
198
|
+
type: "submit",
|
|
202
199
|
key: void 0,
|
|
203
200
|
config: {
|
|
204
|
-
|
|
205
|
-
label: "Submit my application",
|
|
206
|
-
value: "submit"
|
|
207
|
-
}],
|
|
208
|
-
maxSelected: 1,
|
|
209
|
-
minSelected: 1
|
|
201
|
+
label: "Submit my application"
|
|
210
202
|
}
|
|
211
203
|
});
|
|
212
204
|
const logApplyComplete = () => {
|
|
213
|
-
const contextJobId = typeof context.
|
|
214
|
-
const contextFlowId = typeof context.
|
|
215
|
-
|
|
216
|
-
|
|
205
|
+
const contextJobId = typeof context.job_id === "string" ? context.job_id : void 0;
|
|
206
|
+
const contextFlowId = typeof context.flow_id === "string" ? context.flow_id : void 0;
|
|
207
|
+
if (!contextJobId || !contextFlowId)
|
|
208
|
+
return;
|
|
217
209
|
if (contextJobId) {
|
|
218
210
|
analytics.log({
|
|
219
211
|
event: "APPLY_COMPLETE",
|
|
@@ -241,7 +233,7 @@ async function interpretSubmitNode({
|
|
|
241
233
|
integration_id: node.data.integrationId,
|
|
242
234
|
anonymous_id,
|
|
243
235
|
session_id,
|
|
244
|
-
submissions:
|
|
236
|
+
submissions: getFlowSubmissionsPayload(submissions || {})
|
|
245
237
|
})
|
|
246
238
|
}).catch((e) => e);
|
|
247
239
|
await N(response).with({
|
|
@@ -812,6 +804,96 @@ const SendButton = ({
|
|
|
812
804
|
})]
|
|
813
805
|
})
|
|
814
806
|
});
|
|
807
|
+
const TYPING_SPEED_MS_PER_CHARACTER = 25;
|
|
808
|
+
const scrollToEndFn$ = a$2({
|
|
809
|
+
instant: () => {
|
|
810
|
+
},
|
|
811
|
+
smooth: () => {
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
const chatStore = {
|
|
815
|
+
onSubmitSuccessFn$: a$2(() => {
|
|
816
|
+
}),
|
|
817
|
+
isBotTyping$: a$2(false),
|
|
818
|
+
scrollToEnd: {
|
|
819
|
+
instant: () => scrollToEndFn$.value.instant(),
|
|
820
|
+
smooth: () => scrollToEndFn$.value.smooth()
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
const useChatService = () => {
|
|
824
|
+
const chatRef = _$1(null);
|
|
825
|
+
y(() => {
|
|
826
|
+
scrollToEndFn$.value = {
|
|
827
|
+
instant: () => {
|
|
828
|
+
var _a;
|
|
829
|
+
return (_a = chatRef.current) == null ? void 0 : _a.scrollTo({
|
|
830
|
+
top: chatRef.current.scrollHeight,
|
|
831
|
+
behavior: "instant"
|
|
832
|
+
});
|
|
833
|
+
},
|
|
834
|
+
smooth: () => {
|
|
835
|
+
if (!chatRef.current)
|
|
836
|
+
return;
|
|
837
|
+
if (chatRef.current.scrollHeight - chatRef.current.scrollTop <= chatRef.current.clientHeight * 1.5) {
|
|
838
|
+
chatRef.current.scrollTo({
|
|
839
|
+
top: chatRef.current.scrollHeight,
|
|
840
|
+
behavior: "smooth"
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
}, [chatRef]);
|
|
846
|
+
const chatService = F(() => ({
|
|
847
|
+
send: async ({
|
|
848
|
+
message,
|
|
849
|
+
signal: signal2,
|
|
850
|
+
groupId
|
|
851
|
+
}) => {
|
|
852
|
+
await N(message).with({
|
|
853
|
+
author: "bot",
|
|
854
|
+
type: "text"
|
|
855
|
+
}, async (message2) => {
|
|
856
|
+
if (signal2 == null ? void 0 : signal2.aborted)
|
|
857
|
+
throw new AbortedError();
|
|
858
|
+
chatStore.isBotTyping$.value = true;
|
|
859
|
+
const typingTime = Math.min(Math.max(20, message2.text.length), 100) * TYPING_SPEED_MS_PER_CHARACTER;
|
|
860
|
+
await new Promise((resolve) => {
|
|
861
|
+
return setTimeout(resolve, typingTime, {
|
|
862
|
+
signal: signal2
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
chatStore.isBotTyping$.value = false;
|
|
866
|
+
}).otherwise(async () => void 0);
|
|
867
|
+
if (signal2 == null ? void 0 : signal2.aborted)
|
|
868
|
+
throw new AbortedError();
|
|
869
|
+
store.addMessage(message, groupId);
|
|
870
|
+
},
|
|
871
|
+
input: async ({
|
|
872
|
+
input,
|
|
873
|
+
signal: signal2
|
|
874
|
+
}) => {
|
|
875
|
+
if (signal2 == null ? void 0 : signal2.aborted)
|
|
876
|
+
throw new AbortedError();
|
|
877
|
+
store.setInput(input);
|
|
878
|
+
return await new Promise((resolve) => {
|
|
879
|
+
const submitFunction = (submission) => {
|
|
880
|
+
if (signal2 == null ? void 0 : signal2.aborted)
|
|
881
|
+
throw new AbortedError();
|
|
882
|
+
store.setInput(void 0);
|
|
883
|
+
if (input.key) {
|
|
884
|
+
store.setSubmission(input.key, submission);
|
|
885
|
+
}
|
|
886
|
+
resolve(submission);
|
|
887
|
+
};
|
|
888
|
+
chatStore.onSubmitSuccessFn$.value = submitFunction;
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
}), []);
|
|
892
|
+
return {
|
|
893
|
+
chatRef,
|
|
894
|
+
chatService
|
|
895
|
+
};
|
|
896
|
+
};
|
|
815
897
|
const SkipButton = ({
|
|
816
898
|
class: className,
|
|
817
899
|
...props
|
|
@@ -1006,7 +1088,7 @@ const ChatInputFile = ({
|
|
|
1006
1088
|
onHeightChange
|
|
1007
1089
|
}) => {
|
|
1008
1090
|
var _a;
|
|
1009
|
-
const submission = (_a =
|
|
1091
|
+
const submission = (_a = store.current$.value.flow) == null ? void 0 : _a.data.submissions[input.key];
|
|
1010
1092
|
const [files, setFiles] = h(isFileSubmission(submission) && submission.value !== null ? submission.value : []);
|
|
1011
1093
|
const [error, setError] = h();
|
|
1012
1094
|
const hiddenFileCount = files.length - FILENAMES_TO_SHOW_QTY;
|
|
@@ -2643,7 +2725,8 @@ const ChatInputMultipleChoice = ({
|
|
|
2643
2725
|
onHeightChange
|
|
2644
2726
|
}) => {
|
|
2645
2727
|
var _a, _b;
|
|
2646
|
-
const submission = input.key ? (_a =
|
|
2728
|
+
const submission = input.key ? (_a = store.current$.value.flow) == null ? void 0 : _a.data.submissions[input.key] : void 0;
|
|
2729
|
+
const isSingleChoice = (input.config.minSelected === 1 || input.config.minSelected === void 0) && input.config.maxSelected === 1;
|
|
2647
2730
|
const {
|
|
2648
2731
|
register,
|
|
2649
2732
|
handleSubmit,
|
|
@@ -2652,12 +2735,11 @@ const ChatInputMultipleChoice = ({
|
|
|
2652
2735
|
}
|
|
2653
2736
|
} = useForm({
|
|
2654
2737
|
defaultValues: {
|
|
2655
|
-
checked: isMultipleChoiceSubmission(submission) ? Object.fromEntries(submission.value.map((key) => [key, true])) : {}
|
|
2738
|
+
checked: isSingleChoice ? {} : isMultipleChoiceSubmission(submission) ? Object.fromEntries(submission.value.map((key) => [key, true])) : {}
|
|
2656
2739
|
},
|
|
2657
2740
|
resolver: getResolver$1(input.config)
|
|
2658
2741
|
});
|
|
2659
2742
|
const focusRef = useFocusOnMount();
|
|
2660
|
-
const isSingleChoice = (input.config.minSelected === 1 || input.config.minSelected === void 0) && input.config.maxSelected === 1;
|
|
2661
2743
|
return o$1("form", {
|
|
2662
2744
|
noValidate: true,
|
|
2663
2745
|
class: "flex flex-col gap-1 pr-2.5",
|
|
@@ -2722,6 +2804,41 @@ const ChatInputMultipleChoice = ({
|
|
|
2722
2804
|
})]
|
|
2723
2805
|
});
|
|
2724
2806
|
};
|
|
2807
|
+
const ChatInputSubmit = ({
|
|
2808
|
+
input,
|
|
2809
|
+
onSubmitSuccess
|
|
2810
|
+
}) => {
|
|
2811
|
+
return o$1("div", {
|
|
2812
|
+
class: "flex flex-col items-center py-3",
|
|
2813
|
+
children: o$1("button", {
|
|
2814
|
+
class: "bg-accent-9 hover:bg-accent-10 active:bg-submit-bg-active hover:border-accent-10 active:border-submit-bg-active border-accent-9 ring-accent-6 focus-visible:outline-accent-8 ring-offset-neutral-1 group flex cursor-pointer rounded-full border border-solid px-5 py-3 pr-4 text-white shadow-[inset_0px_-6px_2px_-1px_oklch(100_0_0/.45),inset_0px_1px_1px_oklch(100_0_0/.3)] outline-none ring-1 ring-offset-[1.5px] transition-all duration-300 focus-visible:outline-2 active:shadow-[inset_0px_0px_2px_-1px_oklch(100_0_0/.45),inset_0px_3px_1px_.5px_oklch(0_0_0/.08)] active:ring-2 active:ring-offset-2",
|
|
2815
|
+
name: input.key,
|
|
2816
|
+
onClick: () => {
|
|
2817
|
+
onSubmitSuccess(null);
|
|
2818
|
+
},
|
|
2819
|
+
children: o$1("span", {
|
|
2820
|
+
class: "relative bottom-[2px] top-[-2px] flex items-center gap-1.5 transition-all duration-300 group-active:bottom-0 group-active:top-0",
|
|
2821
|
+
children: [o$1("span", {
|
|
2822
|
+
class: "inline-flex items-center text-sm font-medium",
|
|
2823
|
+
children: input.config.label
|
|
2824
|
+
}), o$1("svg", {
|
|
2825
|
+
stroke: "currentColor",
|
|
2826
|
+
"stroke-width": "1.5",
|
|
2827
|
+
width: "16",
|
|
2828
|
+
height: "16",
|
|
2829
|
+
viewBox: "0 0 16 16",
|
|
2830
|
+
fill: "none",
|
|
2831
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2832
|
+
children: [o$1("path", {
|
|
2833
|
+
d: "M4 8L8 4L12 8"
|
|
2834
|
+
}), o$1("path", {
|
|
2835
|
+
d: "M8 4V13"
|
|
2836
|
+
})]
|
|
2837
|
+
})]
|
|
2838
|
+
})
|
|
2839
|
+
})
|
|
2840
|
+
});
|
|
2841
|
+
};
|
|
2725
2842
|
const errors = {
|
|
2726
2843
|
empty: "Please enter some text",
|
|
2727
2844
|
email: "That doesn’t look like a valid email address",
|
|
@@ -2764,7 +2881,7 @@ const ChatInputText = ({
|
|
|
2764
2881
|
onHeightChange
|
|
2765
2882
|
}) => {
|
|
2766
2883
|
var _a;
|
|
2767
|
-
const submission = input.key ? (_a =
|
|
2884
|
+
const submission = input.key ? (_a = store.current$.value.flow) == null ? void 0 : _a.data.submissions[input.key] : void 0;
|
|
2768
2885
|
const defaultValue = input.config.defaultValue;
|
|
2769
2886
|
const {
|
|
2770
2887
|
register,
|
|
@@ -2830,22 +2947,19 @@ const ChatInputText = ({
|
|
|
2830
2947
|
})
|
|
2831
2948
|
});
|
|
2832
2949
|
};
|
|
2833
|
-
const ChatInput = ({
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
input
|
|
2837
|
-
}) => {
|
|
2950
|
+
const ChatInput = () => {
|
|
2951
|
+
var _a;
|
|
2952
|
+
const input = (_a = store.current$.value.flow) == null ? void 0 : _a.data.currentInput;
|
|
2838
2953
|
const inputWrapperRef = _$1(null);
|
|
2839
2954
|
const updateHeight = T(() => {
|
|
2840
2955
|
if (inputWrapperRef.current) {
|
|
2841
|
-
inputHeight
|
|
2956
|
+
store.inputHeight$.value = inputWrapperRef.current.getBoundingClientRect().height;
|
|
2842
2957
|
}
|
|
2843
2958
|
}, []);
|
|
2844
2959
|
p(() => {
|
|
2845
2960
|
updateHeight();
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
const handleSubmitSuccess = (type) => (value) => onSubmit({
|
|
2961
|
+
}, [input == null ? void 0 : input.type, updateHeight]);
|
|
2962
|
+
const handleSubmitSuccess = (type) => (value) => chatStore.onSubmitSuccessFn$.value({
|
|
2849
2963
|
type,
|
|
2850
2964
|
value
|
|
2851
2965
|
});
|
|
@@ -2854,18 +2968,19 @@ const ChatInput = ({
|
|
|
2854
2968
|
height: 0
|
|
2855
2969
|
},
|
|
2856
2970
|
animate: {
|
|
2857
|
-
height: inputHeight
|
|
2971
|
+
height: store.inputHeight$.value
|
|
2858
2972
|
},
|
|
2859
2973
|
exit: {
|
|
2860
2974
|
height: 0,
|
|
2861
2975
|
opacity: 0
|
|
2862
2976
|
},
|
|
2977
|
+
onAnimationStart: chatStore.scrollToEnd.smooth,
|
|
2978
|
+
onAnimationComplete: chatStore.scrollToEnd.smooth,
|
|
2863
2979
|
class: "bg-statusbar absolute bottom-0 w-full overflow-hidden rounded-b-3xl backdrop-blur-md backdrop-saturate-150",
|
|
2864
2980
|
children: o$1("div", {
|
|
2865
2981
|
ref: inputWrapperRef,
|
|
2866
2982
|
class: "border-divider border-t",
|
|
2867
2983
|
children: N({
|
|
2868
|
-
application,
|
|
2869
2984
|
input,
|
|
2870
2985
|
onHeightChange: updateHeight
|
|
2871
2986
|
}).with({
|
|
@@ -2910,6 +3025,13 @@ const ChatInput = ({
|
|
|
2910
3025
|
}, (props) => o$1(ChatInputFile, {
|
|
2911
3026
|
onSubmitSuccess: handleSubmitSuccess(props.input.type),
|
|
2912
3027
|
...props
|
|
3028
|
+
})).with({
|
|
3029
|
+
input: {
|
|
3030
|
+
type: "submit"
|
|
3031
|
+
}
|
|
3032
|
+
}, (props) => o$1(ChatInputSubmit, {
|
|
3033
|
+
onSubmitSuccess: handleSubmitSuccess(props.input.type),
|
|
3034
|
+
...props
|
|
2913
3035
|
})).exhaustive()
|
|
2914
3036
|
})
|
|
2915
3037
|
});
|
|
@@ -2958,7 +3080,7 @@ const cva = (base, config) => {
|
|
|
2958
3080
|
return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
|
|
2959
3081
|
};
|
|
2960
3082
|
};
|
|
2961
|
-
const chatBubbleVariants = cva("max-w-[min(100%,24rem)] [text-wrap:pretty] leading-snug flex-shrink min-w-[2rem] py-2 px-3 rounded-[18px] min-h-[36px] break-words", {
|
|
3083
|
+
const chatBubbleVariants = cva("max-w-[min(100%,24rem)] [text-wrap:pretty] leading-snug flex-shrink min-w-[2rem] py-2 px-3 rounded-[18px] min-h-[36px] break-words relative", {
|
|
2962
3084
|
variants: {
|
|
2963
3085
|
side: {
|
|
2964
3086
|
left: "bg-bubble-weak-bg text-neutral-12 shadow-surface-sm outline outline-1 outline-bubble-weak rounded-bl-md",
|
|
@@ -3018,18 +3140,24 @@ const TypingIndicator = ({
|
|
|
3018
3140
|
className,
|
|
3019
3141
|
...props
|
|
3020
3142
|
}) => {
|
|
3143
|
+
p(() => {
|
|
3144
|
+
chatStore.scrollToEnd.smooth();
|
|
3145
|
+
});
|
|
3021
3146
|
return o$1("div", {
|
|
3022
|
-
"aria-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3147
|
+
"aria-hidden": true,
|
|
3148
|
+
children: chatStore.isBotTyping$.value === true ? o$1("div", {
|
|
3149
|
+
"aria-label": "Typing…",
|
|
3150
|
+
class: clsx("flex gap-1 p-4", className),
|
|
3151
|
+
...props,
|
|
3152
|
+
children: Array.from({
|
|
3153
|
+
length: 3
|
|
3154
|
+
}, (_2, i2) => o$1("div", {
|
|
3155
|
+
class: "bg-accent-9 h-1.5 w-1.5 animate-bounce rounded-full",
|
|
3156
|
+
style: {
|
|
3157
|
+
animationDelay: `${-i2 * 200}ms`
|
|
3158
|
+
}
|
|
3159
|
+
}))
|
|
3160
|
+
}) : void 0
|
|
3033
3161
|
});
|
|
3034
3162
|
};
|
|
3035
3163
|
const authorToSide = {
|
|
@@ -3047,213 +3175,131 @@ const systemMessageStyle = cva("w-full select-none py-2 text-wrap-balance text-c
|
|
|
3047
3175
|
}
|
|
3048
3176
|
});
|
|
3049
3177
|
const Conversation = ({
|
|
3050
|
-
|
|
3051
|
-
isBotTyping
|
|
3178
|
+
lastSentMessageFooter
|
|
3052
3179
|
}) => {
|
|
3180
|
+
var _a;
|
|
3181
|
+
const messages = ((_a = store.current$.value.flow) == null ? void 0 : _a.data.messages) ?? [];
|
|
3182
|
+
p(() => {
|
|
3183
|
+
chatStore.scrollToEnd.smooth();
|
|
3184
|
+
}, [messages.length]);
|
|
3053
3185
|
return o$1("ol", {
|
|
3054
3186
|
"aria-label": "Chat messages",
|
|
3055
3187
|
class: "flex flex-col justify-end gap-2 p-2 pt-[calc(var(--header-height)+1rem)]",
|
|
3056
3188
|
children: [o$1(AnimatePresence, {
|
|
3057
3189
|
initial: false,
|
|
3058
|
-
children: messages.map((message, i2) =>
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3190
|
+
children: messages.map((message, i2) => {
|
|
3191
|
+
return o$1(k, {
|
|
3192
|
+
children: o$1("li", {
|
|
3193
|
+
class: "flex",
|
|
3194
|
+
children: N(message).with({
|
|
3195
|
+
type: "system"
|
|
3196
|
+
}, (message2) => o$1("p", {
|
|
3197
|
+
class: systemMessageStyle({
|
|
3198
|
+
variant: message2.variant
|
|
3199
|
+
}),
|
|
3200
|
+
children: message2.text
|
|
3201
|
+
})).with({
|
|
3202
|
+
type: "text",
|
|
3203
|
+
author: _.union("bot", "user")
|
|
3204
|
+
}, (message2) => {
|
|
3205
|
+
const isLastSentMessage = message2.author === "user" && !messages.slice(i2 + 1).some((m2) => m2.type === "text" && m2.author === "user");
|
|
3206
|
+
return o$1(ChatBubble, {
|
|
3207
|
+
side: authorToSide[message2.author],
|
|
3208
|
+
children: [message2.text, isLastSentMessage ? lastSentMessageFooter : null]
|
|
3209
|
+
}, i2);
|
|
3210
|
+
}).with({
|
|
3211
|
+
type: "link"
|
|
3212
|
+
}, (message2) => {
|
|
3213
|
+
return o$1("div", {
|
|
3214
|
+
class: "bg-accent-3 flex w-full items-center justify-center overflow-hidden rounded-xl px-2 py-2",
|
|
3215
|
+
children: o$1("a", {
|
|
3216
|
+
class: "bg-lowest shadow-surface-sm ring-accent-6 hover:ring-accent-8 active:bg-accent-2 active:text-accent-10 text-accent-9 focus-visible:ring-accent-7/50 text-wrap-balance flex items-center gap-1.5 rounded-full py-2 pl-4 pr-2.5 text-center no-underline ring-1 transition-all focus:outline-none focus-visible:ring-4 focus-visible:ring-offset-2",
|
|
3217
|
+
target: "_blank",
|
|
3218
|
+
href: message2.href,
|
|
3219
|
+
children: [message2.text, o$1("svg", {
|
|
3220
|
+
class: "flex-none",
|
|
3221
|
+
width: "15",
|
|
3222
|
+
height: "15",
|
|
3223
|
+
viewBox: "0 0 15 15",
|
|
3224
|
+
fill: "none",
|
|
3225
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3226
|
+
children: o$1("path", {
|
|
3227
|
+
d: "M3.64645 11.3536C3.45118 11.1583 3.45118 10.8417 3.64645 10.6465L10.2929 4L6 4C5.72386 4 5.5 3.77614 5.5 3.5C5.5 3.22386 5.72386 3 6 3L11.5 3C11.6326 3 11.7598 3.05268 11.8536 3.14645C11.9473 3.24022 12 3.36739 12 3.5L12 9.00001C12 9.27615 11.7761 9.50001 11.5 9.50001C11.2239 9.50001 11 9.27615 11 9.00001V4.70711L4.35355 11.3536C4.15829 11.5488 3.84171 11.5488 3.64645 11.3536Z",
|
|
3228
|
+
fill: "currentColor",
|
|
3229
|
+
"fill-rule": "evenodd",
|
|
3230
|
+
"clip-rule": "evenodd"
|
|
3231
|
+
})
|
|
3232
|
+
})]
|
|
3096
3233
|
})
|
|
3097
|
-
})
|
|
3098
|
-
})
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
}
|
|
3117
|
-
})
|
|
3118
|
-
})
|
|
3119
|
-
}
|
|
3120
|
-
}), o$1(
|
|
3121
|
-
"aria-hidden": true,
|
|
3122
|
-
children: isBotTyping && o$1(TypingIndicator, {})
|
|
3123
|
-
})]
|
|
3234
|
+
});
|
|
3235
|
+
}).with({
|
|
3236
|
+
type: "image"
|
|
3237
|
+
}, (image) => o$1("img", {
|
|
3238
|
+
class: "shadow-surface-md w-full max-w-[min(100%,24rem)] rounded-2xl",
|
|
3239
|
+
src: image.url,
|
|
3240
|
+
style: {
|
|
3241
|
+
aspectRatio: image.width / image.height
|
|
3242
|
+
}
|
|
3243
|
+
})).with({
|
|
3244
|
+
type: "file"
|
|
3245
|
+
}, (file) => {
|
|
3246
|
+
return o$1(FileThumbnail, {
|
|
3247
|
+
class: file.author === "bot" ? "" : "ml-auto",
|
|
3248
|
+
file: {
|
|
3249
|
+
name: file.fileName,
|
|
3250
|
+
sizeKb: file.fileSizeKb
|
|
3251
|
+
}
|
|
3252
|
+
});
|
|
3253
|
+
}).exhaustive()
|
|
3254
|
+
})
|
|
3255
|
+
}, i2);
|
|
3256
|
+
})
|
|
3257
|
+
}), o$1(TypingIndicator, {}, "typing")]
|
|
3124
3258
|
});
|
|
3125
3259
|
};
|
|
3126
|
-
const
|
|
3127
|
-
const useChatService = () => {
|
|
3128
|
-
const chatRef = _$1(null);
|
|
3129
|
-
const [isBotTyping, setIsBotTyping] = h(false);
|
|
3130
|
-
const [onSubmitSuccessFn, setOnSubmitSuccessFn] = h(() => () => {
|
|
3131
|
-
});
|
|
3132
|
-
const scrollToEnd = F(() => (options2) => {
|
|
3133
|
-
var _a;
|
|
3134
|
-
return (_a = chatRef.current) == null ? void 0 : _a.scrollTo({
|
|
3135
|
-
top: chatRef.current.scrollHeight,
|
|
3136
|
-
...options2
|
|
3137
|
-
});
|
|
3138
|
-
}, [chatRef]);
|
|
3139
|
-
const chatService = F(() => ({
|
|
3140
|
-
send: async ({
|
|
3141
|
-
message,
|
|
3142
|
-
signal,
|
|
3143
|
-
groupId
|
|
3144
|
-
}) => {
|
|
3145
|
-
await N(message).with({
|
|
3146
|
-
author: "bot",
|
|
3147
|
-
type: "text"
|
|
3148
|
-
}, async (message2) => {
|
|
3149
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
3150
|
-
throw new AbortedError();
|
|
3151
|
-
setIsBotTyping(true);
|
|
3152
|
-
const typingTime = Math.min(Math.max(20, message2.text.length), 100) * TYPING_SPEED_MS_PER_CHARACTER;
|
|
3153
|
-
await new Promise((resolve) => {
|
|
3154
|
-
return setTimeout(resolve, typingTime, {
|
|
3155
|
-
signal
|
|
3156
|
-
});
|
|
3157
|
-
});
|
|
3158
|
-
setIsBotTyping(false);
|
|
3159
|
-
}).otherwise(async () => void 0);
|
|
3160
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
3161
|
-
throw new AbortedError();
|
|
3162
|
-
application.addMessage(message, groupId);
|
|
3163
|
-
},
|
|
3164
|
-
input: async ({
|
|
3165
|
-
input,
|
|
3166
|
-
signal
|
|
3167
|
-
}) => {
|
|
3168
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
3169
|
-
throw new AbortedError();
|
|
3170
|
-
application.setInput(input);
|
|
3171
|
-
return await new Promise((resolve) => {
|
|
3172
|
-
const submitFunction = (submission) => {
|
|
3173
|
-
if (signal == null ? void 0 : signal.aborted)
|
|
3174
|
-
throw new AbortedError();
|
|
3175
|
-
application.setInput(void 0);
|
|
3176
|
-
if (input.key) {
|
|
3177
|
-
application.setSubmission;
|
|
3178
|
-
application.setSubmission(input.key, submission);
|
|
3179
|
-
}
|
|
3180
|
-
resolve(submission);
|
|
3181
|
-
};
|
|
3182
|
-
setOnSubmitSuccessFn(() => submitFunction);
|
|
3183
|
-
});
|
|
3184
|
-
}
|
|
3185
|
-
}), []);
|
|
3186
|
-
return {
|
|
3187
|
-
chatRef,
|
|
3188
|
-
chatService,
|
|
3189
|
-
isBotTyping,
|
|
3190
|
-
onSubmitSuccessFn,
|
|
3191
|
-
scrollToEnd
|
|
3192
|
-
};
|
|
3193
|
-
};
|
|
3194
|
-
const JobApplicationContent = ({
|
|
3195
|
-
currentApplication,
|
|
3260
|
+
const ChatbotBody = ({
|
|
3196
3261
|
logger,
|
|
3197
3262
|
apiClient,
|
|
3198
3263
|
analytics
|
|
3199
3264
|
}) => {
|
|
3265
|
+
const {
|
|
3266
|
+
flow
|
|
3267
|
+
} = store.current$.value;
|
|
3268
|
+
invariant(flow, "Flow is required to exist to show chatbot body");
|
|
3269
|
+
const view = store.viewState$.value;
|
|
3200
3270
|
const {
|
|
3201
3271
|
chatRef,
|
|
3202
|
-
chatService
|
|
3203
|
-
isBotTyping,
|
|
3204
|
-
onSubmitSuccessFn,
|
|
3205
|
-
scrollToEnd
|
|
3272
|
+
chatService
|
|
3206
3273
|
} = useChatService();
|
|
3207
|
-
const
|
|
3208
|
-
const flow = currentApplication.flow;
|
|
3209
|
-
const job = currentApplication.job;
|
|
3274
|
+
const [undoFn, setUndoFn] = h();
|
|
3210
3275
|
y(() => {
|
|
3211
3276
|
if (view === "maximised")
|
|
3212
|
-
scrollToEnd(
|
|
3213
|
-
|
|
3214
|
-
});
|
|
3215
|
-
}, [scrollToEnd, view]);
|
|
3216
|
-
p(() => {
|
|
3217
|
-
scrollToEnd({
|
|
3218
|
-
behavior: "smooth"
|
|
3219
|
-
});
|
|
3220
|
-
}, [currentApplication.data.messages, scrollToEnd]);
|
|
3277
|
+
chatStore.scrollToEnd.instant();
|
|
3278
|
+
}, [view]);
|
|
3221
3279
|
y(() => {
|
|
3222
3280
|
const {
|
|
3223
3281
|
state,
|
|
3224
|
-
|
|
3225
|
-
} =
|
|
3282
|
+
flow: currentApplication
|
|
3283
|
+
} = store.current$.peek();
|
|
3226
3284
|
if (state !== "loaded")
|
|
3227
3285
|
throw new Error(ERROR_MESSAGES.invalid_state);
|
|
3228
|
-
let fromNodeId =
|
|
3229
|
-
scrollToEnd(
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
application.setInput(void 0);
|
|
3233
|
-
if (currentApplication2.data.isFinished)
|
|
3286
|
+
let fromNodeId = currentApplication.data.nodeHistory.at(-1);
|
|
3287
|
+
chatStore.scrollToEnd.instant();
|
|
3288
|
+
store.setInput(void 0);
|
|
3289
|
+
if (currentApplication.data.isFinished)
|
|
3234
3290
|
return;
|
|
3235
|
-
if (fromNodeId ===
|
|
3291
|
+
if (fromNodeId === void 0) {
|
|
3236
3292
|
fromNodeId = getHeadOrThrow(flow.nodes).id;
|
|
3237
|
-
|
|
3238
|
-
analytics.log({
|
|
3239
|
-
event: "APPLY_START",
|
|
3240
|
-
attributionKey: `job_${job.id}`,
|
|
3241
|
-
properties: {
|
|
3242
|
-
job_id: job.id,
|
|
3243
|
-
flow_id: flow.id
|
|
3244
|
-
}
|
|
3245
|
-
});
|
|
3293
|
+
store.setCurrentNodeId(fromNodeId);
|
|
3246
3294
|
} else {
|
|
3247
|
-
|
|
3295
|
+
store.removeMessagesSentByNodeIds([fromNodeId]);
|
|
3248
3296
|
}
|
|
3249
3297
|
const {
|
|
3250
3298
|
interpret: interpret2,
|
|
3251
|
-
abort
|
|
3299
|
+
abort,
|
|
3300
|
+
undo
|
|
3252
3301
|
} = createFlowInterpreter({
|
|
3253
|
-
context:
|
|
3254
|
-
jobId: job.id,
|
|
3255
|
-
flowId: flow.id
|
|
3256
|
-
},
|
|
3302
|
+
context: flow.context,
|
|
3257
3303
|
analytics,
|
|
3258
3304
|
apiClient,
|
|
3259
3305
|
logger,
|
|
@@ -3262,59 +3308,89 @@ const JobApplicationContent = ({
|
|
|
3262
3308
|
// We need to get fresh submissions, that’s why we call `peek` here.
|
|
3263
3309
|
getSubmissions: () => {
|
|
3264
3310
|
var _a;
|
|
3265
|
-
return (_a =
|
|
3311
|
+
return (_a = store.current$.peek().flow) == null ? void 0 : _a.data.submissions;
|
|
3266
3312
|
},
|
|
3267
3313
|
onInterpret: (node, prevNode) => {
|
|
3268
|
-
const currentState =
|
|
3314
|
+
const currentState = store.current$.peek().flow;
|
|
3269
3315
|
invariant(currentState);
|
|
3270
3316
|
if (prevNode) {
|
|
3271
3317
|
currentState.data.sequence = currentState.data.sequence + 1;
|
|
3272
3318
|
analytics.log({
|
|
3273
3319
|
event: "FLOW_NODE",
|
|
3274
|
-
attributionKey: `job_${job.id}`,
|
|
3275
3320
|
properties: {
|
|
3276
3321
|
flow_id: flow.id,
|
|
3277
3322
|
flow_version: flow.version,
|
|
3278
|
-
job_id: job.id,
|
|
3279
3323
|
from_node_id: prevNode.id,
|
|
3280
3324
|
to_node_id: node.id,
|
|
3281
3325
|
sequence: currentState.data.sequence,
|
|
3282
3326
|
flow_session_id: currentState.data.flowSessionId
|
|
3283
|
-
}
|
|
3327
|
+
},
|
|
3328
|
+
customProperties: flow.context
|
|
3284
3329
|
});
|
|
3285
3330
|
}
|
|
3286
|
-
|
|
3331
|
+
store.setCurrentNodeId(node.id);
|
|
3287
3332
|
},
|
|
3288
3333
|
onFlowEnd: async () => {
|
|
3289
|
-
|
|
3334
|
+
store.markAsFinished();
|
|
3290
3335
|
}
|
|
3291
3336
|
});
|
|
3337
|
+
setUndoFn(() => undo);
|
|
3292
3338
|
interpret2(fromNodeId);
|
|
3293
3339
|
return abort;
|
|
3294
|
-
}, [analytics, apiClient, chatService, logger,
|
|
3340
|
+
}, [analytics, apiClient, chatService, logger, flow]);
|
|
3295
3341
|
return o$1(k, {
|
|
3296
3342
|
children: [o$1("div", {
|
|
3297
3343
|
ref: chatRef,
|
|
3298
|
-
className: "hide-scrollbars relative flex max-w-full flex-grow flex-col overflow-y-scroll",
|
|
3344
|
+
className: "hide-scrollbars relative flex w-full max-w-full flex-grow flex-col overflow-y-scroll",
|
|
3299
3345
|
style: {
|
|
3300
3346
|
WebkitOverflowScrolling: "touch",
|
|
3301
|
-
paddingBottom: inputHeight
|
|
3347
|
+
paddingBottom: store.inputHeight$.value
|
|
3302
3348
|
},
|
|
3303
|
-
children: o$1(
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
messages: currentApplication.data.messages
|
|
3349
|
+
children: o$1(Conversation, {
|
|
3350
|
+
lastSentMessageFooter: flow.data.isFinished || !undoFn ? null : o$1(UndoButton, {
|
|
3351
|
+
undoFn
|
|
3307
3352
|
})
|
|
3308
3353
|
})
|
|
3309
|
-
}), o$1(ChatInput, {
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3354
|
+
}), o$1(ChatInput, {})]
|
|
3355
|
+
});
|
|
3356
|
+
};
|
|
3357
|
+
const getNodeKeys = (node) => {
|
|
3358
|
+
if ("key" in node.data && node.data.key)
|
|
3359
|
+
return [node.data.key];
|
|
3360
|
+
if ("keys" in node.data)
|
|
3361
|
+
return Object.values(node.data.keys).filter((key) => typeof key === "string");
|
|
3362
|
+
return [];
|
|
3363
|
+
};
|
|
3364
|
+
const UndoButton = ({
|
|
3365
|
+
undoFn
|
|
3366
|
+
}) => {
|
|
3367
|
+
return o$1("div", {
|
|
3368
|
+
class: "absolute bottom-0 right-0 flex w-full translate-y-full justify-end",
|
|
3369
|
+
children: o$1("button", {
|
|
3370
|
+
class: "fr touch-hitbox text-neutral-9 hover:text-neutral-12 rounded-full p-1 text-right text-xs transition-all",
|
|
3371
|
+
onClick: async () => {
|
|
3372
|
+
const {
|
|
3373
|
+
flow
|
|
3374
|
+
} = store.current$.peek();
|
|
3375
|
+
invariant(flow);
|
|
3376
|
+
const {
|
|
3377
|
+
removed
|
|
3378
|
+
} = undoFn(flow.data.nodeHistory);
|
|
3379
|
+
const removedNodeIds = flow.data.nodeHistory.splice(-removed);
|
|
3380
|
+
if (removedNodeIds.length === 0)
|
|
3381
|
+
return;
|
|
3382
|
+
store.removeMessagesSentByNodeIds(removedNodeIds);
|
|
3383
|
+
store.setInput(void 0);
|
|
3384
|
+
removedNodeIds.pop();
|
|
3385
|
+
removedNodeIds.map((nodeId) => flow.nodes.find((node) => node.id === nodeId)).filter(Boolean).flatMap(getNodeKeys).forEach((key) => delete flow.data.submissions[key]);
|
|
3386
|
+
store.current$.value = {
|
|
3387
|
+
...store.current$.value
|
|
3388
|
+
};
|
|
3389
|
+
},
|
|
3390
|
+
children: "Undo"
|
|
3391
|
+
})
|
|
3316
3392
|
});
|
|
3317
3393
|
};
|
|
3318
3394
|
export {
|
|
3319
|
-
|
|
3395
|
+
ChatbotBody
|
|
3320
3396
|
};
|