@informedai/react 0.4.22 → 0.4.23

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/dist/index.mjs CHANGED
@@ -2329,19 +2329,832 @@ function WebsiteChatbot({ className, ...config }) {
2329
2329
  );
2330
2330
  }
2331
2331
 
2332
+ // src/components/SmartQuestionnaire.tsx
2333
+ import { useState as useState5, useRef as useRef5, useEffect as useEffect5 } from "react";
2334
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2335
+ var defaultTheme4 = {
2336
+ primaryColor: "#8b5cf6",
2337
+ backgroundColor: "#ffffff",
2338
+ textColor: "#1c1917",
2339
+ borderRadius: "12px",
2340
+ fontFamily: "system-ui, -apple-system, sans-serif"
2341
+ };
2342
+ function ChatCard2({ card, theme }) {
2343
+ const data = extractCardDisplayData(card);
2344
+ return /* @__PURE__ */ jsxs4(
2345
+ "div",
2346
+ {
2347
+ style: {
2348
+ border: "1px solid #e5e7eb",
2349
+ borderRadius: "8px",
2350
+ overflow: "hidden",
2351
+ backgroundColor: "#fff",
2352
+ marginTop: "8px",
2353
+ marginBottom: "8px"
2354
+ },
2355
+ children: [
2356
+ data.imageUrl && /* @__PURE__ */ jsx5("div", { style: { width: "100%", height: "140px", backgroundColor: "#f3f4f6", overflow: "hidden" }, children: /* @__PURE__ */ jsx5(
2357
+ "img",
2358
+ {
2359
+ src: data.imageUrl,
2360
+ alt: data.title,
2361
+ style: { width: "100%", height: "100%", objectFit: "cover" },
2362
+ onError: (e) => {
2363
+ e.target.style.display = "none";
2364
+ }
2365
+ }
2366
+ ) }),
2367
+ /* @__PURE__ */ jsxs4("div", { style: { padding: "12px" }, children: [
2368
+ /* @__PURE__ */ jsx5(
2369
+ "div",
2370
+ {
2371
+ style: {
2372
+ display: "inline-block",
2373
+ fontSize: "10px",
2374
+ fontWeight: 600,
2375
+ textTransform: "uppercase",
2376
+ letterSpacing: "0.5px",
2377
+ color: theme.primaryColor,
2378
+ backgroundColor: `${theme.primaryColor}15`,
2379
+ padding: "2px 6px",
2380
+ borderRadius: "4px",
2381
+ marginBottom: "6px"
2382
+ },
2383
+ children: card.type
2384
+ }
2385
+ ),
2386
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "15px", fontWeight: 600, color: "#1f2937", marginBottom: "4px", lineHeight: "1.3" }, children: data.title }),
2387
+ data.subtitle && /* @__PURE__ */ jsx5("div", { style: { fontSize: "13px", color: "#6b7280", marginBottom: "6px" }, children: data.subtitle }),
2388
+ data.description && /* @__PURE__ */ jsx5("div", { style: { fontSize: "13px", color: "#4b5563", lineHeight: "1.4", marginBottom: "8px" }, children: data.description }),
2389
+ data.extraValues.length > 0 && /* @__PURE__ */ jsx5("div", { style: { fontSize: "12px", color: "#6b7280", marginBottom: "8px" }, children: data.extraValues.join(" \u2022 ") }),
2390
+ data.actionUrl && /* @__PURE__ */ jsxs4(
2391
+ "a",
2392
+ {
2393
+ href: data.actionUrl,
2394
+ target: "_blank",
2395
+ rel: "noopener noreferrer",
2396
+ style: {
2397
+ display: "inline-flex",
2398
+ alignItems: "center",
2399
+ gap: "4px",
2400
+ fontSize: "13px",
2401
+ fontWeight: 500,
2402
+ color: theme.primaryColor,
2403
+ textDecoration: "none",
2404
+ padding: "6px 12px",
2405
+ borderRadius: "6px",
2406
+ backgroundColor: `${theme.primaryColor}10`
2407
+ },
2408
+ children: [
2409
+ data.actionLabel || "View",
2410
+ /* @__PURE__ */ jsxs4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2411
+ /* @__PURE__ */ jsx5("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
2412
+ /* @__PURE__ */ jsx5("polyline", { points: "15 3 21 3 21 9" }),
2413
+ /* @__PURE__ */ jsx5("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
2414
+ ] })
2415
+ ]
2416
+ }
2417
+ )
2418
+ ] })
2419
+ ]
2420
+ }
2421
+ );
2422
+ }
2423
+ function MessageContent2({ content, theme }) {
2424
+ const segments = parseMessageContent(content);
2425
+ if (segments.length === 1 && segments[0].type === "text") {
2426
+ return /* @__PURE__ */ jsx5(Fragment3, { children: segments[0].content });
2427
+ }
2428
+ return /* @__PURE__ */ jsx5(Fragment3, { children: segments.map((segment, index) => {
2429
+ if (segment.type === "text") {
2430
+ return /* @__PURE__ */ jsx5("span", { style: { whiteSpace: "pre-wrap" }, children: segment.content }, index);
2431
+ }
2432
+ if (segment.type === "card" && segment.card) {
2433
+ return /* @__PURE__ */ jsx5(ChatCard2, { card: segment.card, theme }, index);
2434
+ }
2435
+ return null;
2436
+ }) });
2437
+ }
2438
+ function SmartQuestionnaire({ className, ...config }) {
2439
+ const theme = { ...defaultTheme4, ...config.theme };
2440
+ const apiUrl = config.apiUrl || "https://api.informedai.app/api/v1";
2441
+ const jsonHeaders = { "Content-Type": "application/json" };
2442
+ const [phase, setPhase] = useState5("loading");
2443
+ const [steps, setSteps] = useState5([]);
2444
+ const [currentStepIndex, setCurrentStepIndex] = useState5(0);
2445
+ const [answers, setAnswers] = useState5({});
2446
+ const [messages, setMessages] = useState5([]);
2447
+ const [inputValue, setInputValue] = useState5("");
2448
+ const [isStreaming, setIsStreaming] = useState5(false);
2449
+ const [streamingContent, setStreamingContent] = useState5("");
2450
+ const [error, setError] = useState5(null);
2451
+ const [isCollapsed, setIsCollapsed] = useState5(config.defaultCollapsed ?? false);
2452
+ const [multiSelectChoices, setMultiSelectChoices] = useState5(/* @__PURE__ */ new Set());
2453
+ const [matches, setMatches] = useState5(null);
2454
+ const [resultsHistory, setResultsHistory] = useState5([]);
2455
+ const [answeredStepIndices, setAnsweredStepIndices] = useState5(/* @__PURE__ */ new Set());
2456
+ const [awaitingFreeTextResponse, setAwaitingFreeTextResponse] = useState5(false);
2457
+ const messagesEndRef = useRef5(null);
2458
+ useEffect5(() => {
2459
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
2460
+ }, [messages, streamingContent]);
2461
+ useEffect5(() => {
2462
+ fetchSteps();
2463
+ }, []);
2464
+ const fetchSteps = async () => {
2465
+ try {
2466
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/steps`, {
2467
+ headers: jsonHeaders
2468
+ });
2469
+ if (!res.ok) {
2470
+ const err = await res.json().catch(() => ({ error: "Failed to load" }));
2471
+ throw new Error(err.error || `HTTP ${res.status}`);
2472
+ }
2473
+ const data = await res.json();
2474
+ if (data.length === 0) {
2475
+ setError("This questionnaire has no steps configured.");
2476
+ setPhase("steps");
2477
+ return;
2478
+ }
2479
+ setSteps(data);
2480
+ setMessages([{ role: "assistant", content: data[0].question }]);
2481
+ setPhase("steps");
2482
+ } catch (err) {
2483
+ setError(err instanceof Error ? err.message : "Failed to load questionnaire");
2484
+ setPhase("steps");
2485
+ }
2486
+ };
2487
+ const advanceStep = (newAnswers, newMessages, newAnsweredIndices, fromIndex) => {
2488
+ const nextIndex = fromIndex + 1;
2489
+ if (nextIndex >= steps.length) {
2490
+ setMessages(newMessages);
2491
+ setAnswers(newAnswers);
2492
+ setAnsweredStepIndices(newAnsweredIndices);
2493
+ setCurrentStepIndex(nextIndex);
2494
+ startMatching(newAnswers, newMessages);
2495
+ } else {
2496
+ const nextStep = steps[nextIndex];
2497
+ const updated = [...newMessages, { role: "assistant", content: nextStep.question }];
2498
+ setMessages(updated);
2499
+ setAnswers(newAnswers);
2500
+ setAnsweredStepIndices(newAnsweredIndices);
2501
+ setCurrentStepIndex(nextIndex);
2502
+ }
2503
+ };
2504
+ const handleOptionClick = (option) => {
2505
+ const step = steps[currentStepIndex];
2506
+ const newAnswers = {
2507
+ ...answers,
2508
+ [step.answerKey]: { value: option, question: step.question, inputType: "selected" }
2509
+ };
2510
+ const newMessages = [...messages, { role: "user", content: option }];
2511
+ const newAnswered = new Set(answeredStepIndices);
2512
+ newAnswered.add(currentStepIndex);
2513
+ advanceStep(newAnswers, newMessages, newAnswered, currentStepIndex);
2514
+ };
2515
+ const handleMultiSelectConfirm = () => {
2516
+ if (multiSelectChoices.size === 0) return;
2517
+ const step = steps[currentStepIndex];
2518
+ const value = Array.from(multiSelectChoices).join(", ");
2519
+ const newAnswers = {
2520
+ ...answers,
2521
+ [step.answerKey]: { value, question: step.question, inputType: "selected" }
2522
+ };
2523
+ const newMessages = [...messages, { role: "user", content: value }];
2524
+ const newAnswered = new Set(answeredStepIndices);
2525
+ newAnswered.add(currentStepIndex);
2526
+ setMultiSelectChoices(/* @__PURE__ */ new Set());
2527
+ advanceStep(newAnswers, newMessages, newAnswered, currentStepIndex);
2528
+ };
2529
+ const handleFreeTextSubmit = async (text) => {
2530
+ if (!text.trim() || isStreaming) return;
2531
+ const step = steps[currentStepIndex];
2532
+ const userMsg = { role: "user", content: text };
2533
+ const newMessages = [...messages, userMsg];
2534
+ setMessages(newMessages);
2535
+ setInputValue("");
2536
+ setIsStreaming(true);
2537
+ setAwaitingFreeTextResponse(true);
2538
+ setError(null);
2539
+ try {
2540
+ const res = await fetch(
2541
+ `${apiUrl}/smart-questionnaire/${config.questionnaireId}/step/${step.name}`,
2542
+ {
2543
+ method: "POST",
2544
+ headers: jsonHeaders,
2545
+ body: JSON.stringify({ message: text, history: [] })
2546
+ }
2547
+ );
2548
+ if (!res.ok) {
2549
+ const err = await res.json().catch(() => ({ error: "Request failed" }));
2550
+ throw new Error(err.error || `HTTP ${res.status}`);
2551
+ }
2552
+ const data = await res.json();
2553
+ if (data.answer) {
2554
+ const newAnswers = {
2555
+ ...answers,
2556
+ [step.answerKey]: { value: data.answer, question: step.question, inputType: "typed" }
2557
+ };
2558
+ const newAnswered = new Set(answeredStepIndices);
2559
+ newAnswered.add(currentStepIndex);
2560
+ let msgs = newMessages;
2561
+ if (data.message) {
2562
+ msgs = [...msgs, { role: "assistant", content: data.message }];
2563
+ }
2564
+ advanceStep(newAnswers, msgs, newAnswered, currentStepIndex);
2565
+ } else {
2566
+ if (data.message) {
2567
+ setMessages([...newMessages, { role: "assistant", content: data.message }]);
2568
+ }
2569
+ }
2570
+ } catch (err) {
2571
+ setError(err instanceof Error ? err.message : "Failed to process answer");
2572
+ } finally {
2573
+ setIsStreaming(false);
2574
+ setAwaitingFreeTextResponse(false);
2575
+ }
2576
+ };
2577
+ const startMatching = async (finalAnswers, finalMessages) => {
2578
+ setPhase("matching");
2579
+ setError(null);
2580
+ try {
2581
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/match`, {
2582
+ method: "POST",
2583
+ headers: jsonHeaders,
2584
+ body: JSON.stringify({ answers: finalAnswers })
2585
+ });
2586
+ if (!res.ok) {
2587
+ const err = await res.json().catch(() => ({ error: "Matching failed" }));
2588
+ throw new Error(err.error || `HTTP ${res.status}`);
2589
+ }
2590
+ const data = await res.json();
2591
+ const matchList = data.matches || [];
2592
+ setMatches(matchList);
2593
+ startResults(finalAnswers, finalMessages, matchList);
2594
+ } catch (err) {
2595
+ setError(err instanceof Error ? err.message : "Failed to find matches");
2596
+ setPhase("results");
2597
+ }
2598
+ };
2599
+ const startResults = async (finalAnswers, finalMessages, matchList) => {
2600
+ setPhase("results");
2601
+ setIsStreaming(true);
2602
+ setStreamingContent("");
2603
+ try {
2604
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/results`, {
2605
+ method: "POST",
2606
+ headers: jsonHeaders,
2607
+ body: JSON.stringify({ matches: matchList, history: [] })
2608
+ });
2609
+ if (!res.ok) {
2610
+ const err = await res.json().catch(() => ({ error: "Results failed" }));
2611
+ throw new Error(err.error || `HTTP ${res.status}`);
2612
+ }
2613
+ const reader = res.body?.getReader();
2614
+ if (!reader) throw new Error("No response body");
2615
+ const decoder = new TextDecoder();
2616
+ let buffer = "";
2617
+ let fullContent = "";
2618
+ let action = null;
2619
+ while (true) {
2620
+ const { done, value } = await reader.read();
2621
+ if (done) break;
2622
+ buffer += decoder.decode(value, { stream: true });
2623
+ const lines = buffer.split("\n");
2624
+ buffer = lines.pop() || "";
2625
+ for (const line of lines) {
2626
+ if (line.startsWith("event:")) continue;
2627
+ if (line.startsWith("data:")) {
2628
+ const d = line.slice(5).trim();
2629
+ try {
2630
+ const parsed = JSON.parse(d);
2631
+ if (parsed.content) {
2632
+ fullContent += parsed.content;
2633
+ setStreamingContent(fullContent);
2634
+ }
2635
+ if (parsed.fullContent) {
2636
+ fullContent = parsed.fullContent;
2637
+ }
2638
+ if (parsed.action) {
2639
+ action = parsed.action;
2640
+ }
2641
+ } catch {
2642
+ }
2643
+ }
2644
+ }
2645
+ }
2646
+ const displayContent = fullContent.replace(/\[RESTART\]/g, "").trim();
2647
+ const resultMsg = { role: "assistant", content: displayContent };
2648
+ setMessages([...finalMessages, resultMsg]);
2649
+ setResultsHistory([resultMsg]);
2650
+ setStreamingContent("");
2651
+ if (action === "restart") {
2652
+ handleRestart();
2653
+ }
2654
+ } catch (err) {
2655
+ setError(err instanceof Error ? err.message : "Failed to stream results");
2656
+ } finally {
2657
+ setIsStreaming(false);
2658
+ }
2659
+ };
2660
+ const sendResultsFollowUp = async (message) => {
2661
+ if (!message.trim() || isStreaming) return;
2662
+ setIsStreaming(true);
2663
+ setStreamingContent("");
2664
+ setError(null);
2665
+ const userMsg = { role: "user", content: message };
2666
+ const newHistory = [...resultsHistory, userMsg];
2667
+ setMessages((prev) => [...prev, userMsg]);
2668
+ setInputValue("");
2669
+ try {
2670
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/results`, {
2671
+ method: "POST",
2672
+ headers: jsonHeaders,
2673
+ body: JSON.stringify({
2674
+ message,
2675
+ matches: matches || [],
2676
+ history: newHistory
2677
+ })
2678
+ });
2679
+ if (!res.ok) {
2680
+ const err = await res.json().catch(() => ({ error: "Request failed" }));
2681
+ throw new Error(err.error || `HTTP ${res.status}`);
2682
+ }
2683
+ const reader = res.body?.getReader();
2684
+ if (!reader) throw new Error("No response body");
2685
+ const decoder = new TextDecoder();
2686
+ let buffer = "";
2687
+ let fullContent = "";
2688
+ let action = null;
2689
+ while (true) {
2690
+ const { done, value } = await reader.read();
2691
+ if (done) break;
2692
+ buffer += decoder.decode(value, { stream: true });
2693
+ const lines = buffer.split("\n");
2694
+ buffer = lines.pop() || "";
2695
+ for (const line of lines) {
2696
+ if (line.startsWith("event:")) continue;
2697
+ if (line.startsWith("data:")) {
2698
+ const d = line.slice(5).trim();
2699
+ try {
2700
+ const parsed = JSON.parse(d);
2701
+ if (parsed.content) {
2702
+ fullContent += parsed.content;
2703
+ setStreamingContent(fullContent);
2704
+ }
2705
+ if (parsed.fullContent) {
2706
+ fullContent = parsed.fullContent;
2707
+ }
2708
+ if (parsed.action) {
2709
+ action = parsed.action;
2710
+ }
2711
+ } catch {
2712
+ }
2713
+ }
2714
+ }
2715
+ }
2716
+ const displayContent = fullContent.replace(/\[RESTART\]/g, "").trim();
2717
+ const assistantMsg = { role: "assistant", content: displayContent };
2718
+ const updatedHistory = [...newHistory, assistantMsg];
2719
+ setMessages((prev) => [...prev, assistantMsg]);
2720
+ setResultsHistory(updatedHistory);
2721
+ setStreamingContent("");
2722
+ if (action === "restart") {
2723
+ handleRestart();
2724
+ }
2725
+ } catch (err) {
2726
+ setError(err instanceof Error ? err.message : "Failed to send message");
2727
+ } finally {
2728
+ setIsStreaming(false);
2729
+ }
2730
+ };
2731
+ const handleRestart = () => {
2732
+ setPhase("steps");
2733
+ setCurrentStepIndex(0);
2734
+ setAnswers({});
2735
+ setMessages(steps.length > 0 ? [{ role: "assistant", content: steps[0].question }] : []);
2736
+ setResultsHistory([]);
2737
+ setMatches(null);
2738
+ setMultiSelectChoices(/* @__PURE__ */ new Set());
2739
+ setAnsweredStepIndices(/* @__PURE__ */ new Set());
2740
+ setStreamingContent("");
2741
+ setError(null);
2742
+ };
2743
+ const handleSubmit = (e) => {
2744
+ e.preventDefault();
2745
+ if (!inputValue.trim() || isStreaming) return;
2746
+ const msg = inputValue.trim();
2747
+ if (phase === "results") {
2748
+ sendResultsFollowUp(msg);
2749
+ } else if (phase === "steps") {
2750
+ handleFreeTextSubmit(msg);
2751
+ }
2752
+ };
2753
+ const progress = steps.length > 0 ? Math.min(currentStepIndex / steps.length, 1) : 0;
2754
+ const currentStep = phase === "steps" && currentStepIndex < steps.length ? steps[currentStepIndex] : null;
2755
+ const showOptions = currentStep && !answeredStepIndices.has(currentStepIndex) && !awaitingFreeTextResponse;
2756
+ const cssVars = {
2757
+ "--sq-primary": theme.primaryColor,
2758
+ "--sq-bg": theme.backgroundColor,
2759
+ "--sq-text": theme.textColor,
2760
+ "--sq-radius": theme.borderRadius,
2761
+ "--sq-font": theme.fontFamily
2762
+ };
2763
+ const positionStyles = config.position === "inline" ? {} : {
2764
+ position: "fixed",
2765
+ [config.position === "bottom-left" ? "left" : "right"]: "20px",
2766
+ bottom: "20px",
2767
+ zIndex: 9999
2768
+ };
2769
+ if (isCollapsed && config.position !== "inline") {
2770
+ return /* @__PURE__ */ jsx5(
2771
+ "button",
2772
+ {
2773
+ onClick: () => setIsCollapsed(false),
2774
+ className,
2775
+ style: {
2776
+ ...cssVars,
2777
+ ...positionStyles,
2778
+ width: "56px",
2779
+ height: "56px",
2780
+ borderRadius: "50%",
2781
+ backgroundColor: "var(--sq-primary)",
2782
+ color: "white",
2783
+ border: "none",
2784
+ cursor: "pointer",
2785
+ display: "flex",
2786
+ alignItems: "center",
2787
+ justifyContent: "center",
2788
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
2789
+ fontFamily: "var(--sq-font)"
2790
+ },
2791
+ children: /* @__PURE__ */ jsxs4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2792
+ /* @__PURE__ */ jsx5("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }),
2793
+ /* @__PURE__ */ jsx5("rect", { x: "9", y: "3", width: "6", height: "4", rx: "2" }),
2794
+ /* @__PURE__ */ jsx5("path", { d: "M9 14l2 2 4-4" })
2795
+ ] })
2796
+ }
2797
+ );
2798
+ }
2799
+ return /* @__PURE__ */ jsxs4(
2800
+ "div",
2801
+ {
2802
+ className,
2803
+ style: {
2804
+ ...cssVars,
2805
+ ...positionStyles,
2806
+ width: config.position === "inline" ? "100%" : "380px",
2807
+ maxHeight: config.position === "inline" ? "600px" : "520px",
2808
+ backgroundColor: "var(--sq-bg)",
2809
+ borderRadius: "var(--sq-radius)",
2810
+ border: "1px solid #e5e5e5",
2811
+ fontFamily: "var(--sq-font)",
2812
+ display: "flex",
2813
+ flexDirection: "column",
2814
+ boxShadow: config.position === "inline" ? "none" : "0 4px 20px rgba(0,0,0,0.15)",
2815
+ overflow: "hidden"
2816
+ },
2817
+ children: [
2818
+ /* @__PURE__ */ jsxs4(
2819
+ "div",
2820
+ {
2821
+ style: {
2822
+ padding: "14px 16px",
2823
+ borderBottom: "1px solid #e5e5e5",
2824
+ display: "flex",
2825
+ alignItems: "center",
2826
+ justifyContent: "space-between",
2827
+ backgroundColor: "var(--sq-primary)",
2828
+ color: "white"
2829
+ },
2830
+ children: [
2831
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
2832
+ /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2833
+ /* @__PURE__ */ jsx5("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }),
2834
+ /* @__PURE__ */ jsx5("rect", { x: "9", y: "3", width: "6", height: "4", rx: "2" }),
2835
+ /* @__PURE__ */ jsx5("path", { d: "M9 14l2 2 4-4" })
2836
+ ] }),
2837
+ /* @__PURE__ */ jsx5("span", { style: { fontWeight: 600 }, children: config.title || "Smart Questionnaire" })
2838
+ ] }),
2839
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "8px" }, children: [
2840
+ phase === "results" && /* @__PURE__ */ jsx5(
2841
+ "button",
2842
+ {
2843
+ onClick: handleRestart,
2844
+ style: {
2845
+ background: "rgba(255,255,255,0.2)",
2846
+ border: "none",
2847
+ borderRadius: "4px",
2848
+ padding: "4px 8px",
2849
+ color: "white",
2850
+ cursor: "pointer",
2851
+ fontSize: "12px"
2852
+ },
2853
+ children: "Restart"
2854
+ }
2855
+ ),
2856
+ config.position !== "inline" && /* @__PURE__ */ jsx5(
2857
+ "button",
2858
+ {
2859
+ onClick: () => setIsCollapsed(true),
2860
+ style: {
2861
+ background: "none",
2862
+ border: "none",
2863
+ color: "white",
2864
+ cursor: "pointer",
2865
+ padding: "4px"
2866
+ },
2867
+ children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
2868
+ /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2869
+ /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2870
+ ] })
2871
+ }
2872
+ )
2873
+ ] })
2874
+ ]
2875
+ }
2876
+ ),
2877
+ phase === "steps" && steps.length > 0 && /* @__PURE__ */ jsx5("div", { style: { height: "3px", backgroundColor: "#e5e7eb" }, children: /* @__PURE__ */ jsx5(
2878
+ "div",
2879
+ {
2880
+ style: {
2881
+ height: "100%",
2882
+ width: `${progress * 100}%`,
2883
+ backgroundColor: "var(--sq-primary)",
2884
+ transition: "width 0.3s ease"
2885
+ }
2886
+ }
2887
+ ) }),
2888
+ /* @__PURE__ */ jsxs4(
2889
+ "div",
2890
+ {
2891
+ style: {
2892
+ flex: 1,
2893
+ overflowY: "auto",
2894
+ padding: "16px",
2895
+ display: "flex",
2896
+ flexDirection: "column",
2897
+ gap: "12px",
2898
+ minHeight: "200px"
2899
+ },
2900
+ children: [
2901
+ phase === "loading" && /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", color: "#9ca3af", padding: "40px 20px" }, children: [
2902
+ /* @__PURE__ */ jsx5("div", { style: { marginBottom: "12px" }, children: /* @__PURE__ */ jsx5(
2903
+ "svg",
2904
+ {
2905
+ width: "24",
2906
+ height: "24",
2907
+ viewBox: "0 0 24 24",
2908
+ fill: "none",
2909
+ stroke: "currentColor",
2910
+ strokeWidth: "2",
2911
+ style: { margin: "0 auto", animation: "sq-spin 1s linear infinite" },
2912
+ children: /* @__PURE__ */ jsx5("path", { d: "M21 12a9 9 0 11-6.219-8.56" })
2913
+ }
2914
+ ) }),
2915
+ /* @__PURE__ */ jsx5("p", { style: { margin: 0, fontSize: "14px" }, children: "Loading questionnaire..." })
2916
+ ] }),
2917
+ messages.map((msg, i) => /* @__PURE__ */ jsx5(
2918
+ "div",
2919
+ {
2920
+ style: {
2921
+ alignSelf: msg.role === "user" ? "flex-end" : "flex-start",
2922
+ maxWidth: "85%"
2923
+ },
2924
+ children: /* @__PURE__ */ jsx5(
2925
+ "div",
2926
+ {
2927
+ style: {
2928
+ padding: "10px 14px",
2929
+ borderRadius: "12px",
2930
+ backgroundColor: msg.role === "user" ? "var(--sq-primary)" : "#f3f4f6",
2931
+ color: msg.role === "user" ? "white" : "var(--sq-text)",
2932
+ fontSize: "14px",
2933
+ lineHeight: "1.5"
2934
+ },
2935
+ children: msg.role === "user" ? msg.content : /* @__PURE__ */ jsx5(MessageContent2, { content: msg.content, theme })
2936
+ }
2937
+ )
2938
+ },
2939
+ i
2940
+ )),
2941
+ streamingContent && /* @__PURE__ */ jsx5("div", { style: { alignSelf: "flex-start", maxWidth: "85%" }, children: /* @__PURE__ */ jsxs4(
2942
+ "div",
2943
+ {
2944
+ style: {
2945
+ padding: "10px 14px",
2946
+ borderRadius: "12px",
2947
+ backgroundColor: "#f3f4f6",
2948
+ color: "var(--sq-text)",
2949
+ fontSize: "14px",
2950
+ lineHeight: "1.5"
2951
+ },
2952
+ children: [
2953
+ /* @__PURE__ */ jsx5(MessageContent2, { content: streamingContent, theme }),
2954
+ /* @__PURE__ */ jsx5("span", { style: { opacity: 0.5, animation: "sq-blink 1s infinite" }, children: "|" })
2955
+ ]
2956
+ }
2957
+ ) }),
2958
+ phase === "matching" && /* @__PURE__ */ jsx5("div", { style: { alignSelf: "flex-start", maxWidth: "85%" }, children: /* @__PURE__ */ jsxs4(
2959
+ "div",
2960
+ {
2961
+ style: {
2962
+ padding: "10px 14px",
2963
+ borderRadius: "12px",
2964
+ backgroundColor: "#f3f4f6",
2965
+ color: "#6b7280",
2966
+ fontSize: "14px",
2967
+ display: "flex",
2968
+ alignItems: "center",
2969
+ gap: "8px"
2970
+ },
2971
+ children: [
2972
+ /* @__PURE__ */ jsx5(
2973
+ "svg",
2974
+ {
2975
+ width: "16",
2976
+ height: "16",
2977
+ viewBox: "0 0 24 24",
2978
+ fill: "none",
2979
+ stroke: "currentColor",
2980
+ strokeWidth: "2",
2981
+ style: { animation: "sq-spin 1s linear infinite" },
2982
+ children: /* @__PURE__ */ jsx5("path", { d: "M21 12a9 9 0 11-6.219-8.56" })
2983
+ }
2984
+ ),
2985
+ "Finding matches..."
2986
+ ]
2987
+ }
2988
+ ) }),
2989
+ showOptions && currentStep.options.length > 0 && /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexDirection: "column", gap: "6px", maxWidth: "85%" }, children: currentStep.multiSelect ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
2990
+ currentStep.options.map((opt) => {
2991
+ const isSelected = multiSelectChoices.has(opt);
2992
+ return /* @__PURE__ */ jsxs4(
2993
+ "button",
2994
+ {
2995
+ onClick: () => {
2996
+ const next = new Set(multiSelectChoices);
2997
+ if (isSelected) next.delete(opt);
2998
+ else next.add(opt);
2999
+ setMultiSelectChoices(next);
3000
+ },
3001
+ style: {
3002
+ padding: "8px 14px",
3003
+ borderRadius: "8px",
3004
+ border: `1.5px solid ${isSelected ? theme.primaryColor : "#d1d5db"}`,
3005
+ backgroundColor: isSelected ? `${theme.primaryColor}10` : "white",
3006
+ color: isSelected ? theme.primaryColor : "#374151",
3007
+ fontSize: "13px",
3008
+ cursor: "pointer",
3009
+ textAlign: "left",
3010
+ fontFamily: "var(--sq-font)",
3011
+ transition: "all 0.15s ease"
3012
+ },
3013
+ children: [
3014
+ isSelected ? "\u2611 " : "\u2610 ",
3015
+ opt
3016
+ ]
3017
+ },
3018
+ opt
3019
+ );
3020
+ }),
3021
+ multiSelectChoices.size > 0 && /* @__PURE__ */ jsxs4(
3022
+ "button",
3023
+ {
3024
+ onClick: handleMultiSelectConfirm,
3025
+ style: {
3026
+ padding: "8px 14px",
3027
+ borderRadius: "8px",
3028
+ border: "none",
3029
+ backgroundColor: "var(--sq-primary)",
3030
+ color: "white",
3031
+ fontSize: "13px",
3032
+ fontWeight: 600,
3033
+ cursor: "pointer",
3034
+ fontFamily: "var(--sq-font)",
3035
+ marginTop: "4px"
3036
+ },
3037
+ children: [
3038
+ "Confirm (",
3039
+ multiSelectChoices.size,
3040
+ " selected)"
3041
+ ]
3042
+ }
3043
+ )
3044
+ ] }) : currentStep.options.map((opt) => /* @__PURE__ */ jsx5(
3045
+ "button",
3046
+ {
3047
+ onClick: () => handleOptionClick(opt),
3048
+ style: {
3049
+ padding: "8px 14px",
3050
+ borderRadius: "8px",
3051
+ border: "1.5px solid #d1d5db",
3052
+ backgroundColor: "white",
3053
+ color: "#374151",
3054
+ fontSize: "13px",
3055
+ cursor: "pointer",
3056
+ textAlign: "left",
3057
+ fontFamily: "var(--sq-font)",
3058
+ transition: "all 0.15s ease"
3059
+ },
3060
+ onMouseEnter: (e) => {
3061
+ e.currentTarget.style.borderColor = theme.primaryColor || "#8b5cf6";
3062
+ e.currentTarget.style.backgroundColor = `${theme.primaryColor}08`;
3063
+ },
3064
+ onMouseLeave: (e) => {
3065
+ e.currentTarget.style.borderColor = "#d1d5db";
3066
+ e.currentTarget.style.backgroundColor = "white";
3067
+ },
3068
+ children: opt
3069
+ },
3070
+ opt
3071
+ )) }),
3072
+ error && /* @__PURE__ */ jsx5(
3073
+ "div",
3074
+ {
3075
+ style: {
3076
+ padding: "10px 14px",
3077
+ borderRadius: "8px",
3078
+ backgroundColor: "#fef2f2",
3079
+ color: "#dc2626",
3080
+ fontSize: "13px"
3081
+ },
3082
+ children: error
3083
+ }
3084
+ ),
3085
+ /* @__PURE__ */ jsx5("div", { ref: messagesEndRef })
3086
+ ]
3087
+ }
3088
+ ),
3089
+ (phase === "steps" || phase === "results") && /* @__PURE__ */ jsx5("form", { onSubmit: handleSubmit, style: { padding: "12px", borderTop: "1px solid #e5e5e5" }, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "8px" }, children: [
3090
+ /* @__PURE__ */ jsx5(
3091
+ "input",
3092
+ {
3093
+ type: "text",
3094
+ value: inputValue,
3095
+ onChange: (e) => setInputValue(e.target.value),
3096
+ placeholder: phase === "results" ? config.placeholder || "Ask a follow-up question..." : config.placeholder || "Type your answer...",
3097
+ disabled: isStreaming,
3098
+ style: {
3099
+ flex: 1,
3100
+ padding: "10px 14px",
3101
+ borderRadius: "8px",
3102
+ border: "1px solid #d1d5db",
3103
+ fontSize: "14px",
3104
+ fontFamily: "var(--sq-font)",
3105
+ outline: "none",
3106
+ color: "#1f2937",
3107
+ backgroundColor: "#ffffff"
3108
+ }
3109
+ }
3110
+ ),
3111
+ /* @__PURE__ */ jsx5(
3112
+ "button",
3113
+ {
3114
+ type: "submit",
3115
+ disabled: isStreaming || !inputValue.trim(),
3116
+ style: {
3117
+ padding: "10px 16px",
3118
+ borderRadius: "8px",
3119
+ backgroundColor: isStreaming || !inputValue.trim() ? "#d1d5db" : "var(--sq-primary)",
3120
+ color: "white",
3121
+ border: "none",
3122
+ cursor: isStreaming || !inputValue.trim() ? "not-allowed" : "pointer",
3123
+ fontWeight: 500,
3124
+ fontSize: "14px"
3125
+ },
3126
+ children: isStreaming ? "..." : "Send"
3127
+ }
3128
+ )
3129
+ ] }) }),
3130
+ /* @__PURE__ */ jsx5("style", { children: `
3131
+ @keyframes sq-blink {
3132
+ 0%, 50% { opacity: 1; }
3133
+ 51%, 100% { opacity: 0; }
3134
+ }
3135
+ @keyframes sq-spin {
3136
+ from { transform: rotate(0deg); }
3137
+ to { transform: rotate(360deg); }
3138
+ }
3139
+ ` })
3140
+ ]
3141
+ }
3142
+ );
3143
+ }
3144
+
2332
3145
  // src/hooks/useSession.ts
2333
- import { useState as useState5, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef5 } from "react";
3146
+ import { useState as useState6, useEffect as useEffect6, useCallback as useCallback2, useRef as useRef6 } from "react";
2334
3147
  function useSession(options) {
2335
3148
  const { apiUrl, documentTypeId, sessionId, onSessionChange, onError } = options;
2336
- const [session, setSession] = useState5(null);
2337
- const [isLoading, setIsLoading] = useState5(true);
2338
- const [error, setError] = useState5(null);
2339
- const clientRef = useRef5(null);
2340
- const isStreamingRef = useRef5(false);
2341
- useEffect5(() => {
3149
+ const [session, setSession] = useState6(null);
3150
+ const [isLoading, setIsLoading] = useState6(true);
3151
+ const [error, setError] = useState6(null);
3152
+ const clientRef = useRef6(null);
3153
+ const isStreamingRef = useRef6(false);
3154
+ useEffect6(() => {
2342
3155
  clientRef.current = new InformedAIClient(apiUrl);
2343
3156
  }, [apiUrl]);
2344
- useEffect5(() => {
3157
+ useEffect6(() => {
2345
3158
  async function initialize() {
2346
3159
  if (!clientRef.current) return;
2347
3160
  try {
@@ -2455,6 +3268,7 @@ export {
2455
3268
  InformedAIClient,
2456
3269
  InformedAIProvider,
2457
3270
  InformedAssistant,
3271
+ SmartQuestionnaire,
2458
3272
  WebsiteChatbot,
2459
3273
  useInformedAI,
2460
3274
  useSession