@informedai/react 0.4.22 → 0.4.24

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,833 @@ 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 MatchCard({ match, theme }) {
2439
+ const name = match.name || "Match";
2440
+ const score = match.score;
2441
+ const reasons = match.matchReasons || [];
2442
+ return /* @__PURE__ */ jsx5(
2443
+ "div",
2444
+ {
2445
+ style: {
2446
+ border: "1px solid #e5e7eb",
2447
+ borderRadius: "8px",
2448
+ overflow: "hidden",
2449
+ backgroundColor: "#fff"
2450
+ },
2451
+ children: /* @__PURE__ */ jsxs4("div", { style: { padding: "12px" }, children: [
2452
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "4px" }, children: [
2453
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "15px", fontWeight: 600, color: "#1f2937", lineHeight: "1.3" }, children: name }),
2454
+ score != null && /* @__PURE__ */ jsxs4(
2455
+ "div",
2456
+ {
2457
+ style: {
2458
+ fontSize: "11px",
2459
+ fontWeight: 600,
2460
+ color: theme.primaryColor,
2461
+ backgroundColor: `${theme.primaryColor}15`,
2462
+ padding: "2px 8px",
2463
+ borderRadius: "10px"
2464
+ },
2465
+ children: [
2466
+ score,
2467
+ "%"
2468
+ ]
2469
+ }
2470
+ )
2471
+ ] }),
2472
+ reasons.length > 0 && /* @__PURE__ */ jsx5("div", { style: { fontSize: "13px", color: "#6b7280", lineHeight: "1.4" }, children: reasons.join(" \u2022 ") })
2473
+ ] })
2474
+ }
2475
+ );
2476
+ }
2477
+ function SmartQuestionnaire({ className, ...config }) {
2478
+ const theme = { ...defaultTheme4, ...config.theme };
2479
+ const apiUrl = config.apiUrl || "https://api.informedai.app/api/v1";
2480
+ const jsonHeaders = { "Content-Type": "application/json" };
2481
+ const [phase, setPhase] = useState5("loading");
2482
+ const [steps, setSteps] = useState5([]);
2483
+ const [currentStepIndex, setCurrentStepIndex] = useState5(0);
2484
+ const [answers, setAnswers] = useState5({});
2485
+ const [messages, setMessages] = useState5([]);
2486
+ const [inputValue, setInputValue] = useState5("");
2487
+ const [isStreaming, setIsStreaming] = useState5(false);
2488
+ const [streamingContent, setStreamingContent] = useState5("");
2489
+ const [error, setError] = useState5(null);
2490
+ const [isCollapsed, setIsCollapsed] = useState5(config.defaultCollapsed ?? false);
2491
+ const [multiSelectChoices, setMultiSelectChoices] = useState5(/* @__PURE__ */ new Set());
2492
+ const [matches, setMatches] = useState5(null);
2493
+ const [resultsHistory, setResultsHistory] = useState5([]);
2494
+ const [answeredStepIndices, setAnsweredStepIndices] = useState5(/* @__PURE__ */ new Set());
2495
+ const [awaitingFreeTextResponse, setAwaitingFreeTextResponse] = useState5(false);
2496
+ const messagesEndRef = useRef5(null);
2497
+ useEffect5(() => {
2498
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
2499
+ }, [messages, streamingContent]);
2500
+ useEffect5(() => {
2501
+ fetchSteps();
2502
+ }, []);
2503
+ const fetchSteps = async () => {
2504
+ try {
2505
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/steps`, {
2506
+ headers: jsonHeaders
2507
+ });
2508
+ if (!res.ok) {
2509
+ const err = await res.json().catch(() => ({ error: "Failed to load" }));
2510
+ throw new Error(err.error || `HTTP ${res.status}`);
2511
+ }
2512
+ const data = await res.json();
2513
+ if (data.length === 0) {
2514
+ setError("This questionnaire has no steps configured.");
2515
+ setPhase("steps");
2516
+ return;
2517
+ }
2518
+ setSteps(data);
2519
+ setMessages([{ role: "assistant", content: data[0].question }]);
2520
+ setPhase("steps");
2521
+ } catch (err) {
2522
+ setError(err instanceof Error ? err.message : "Failed to load questionnaire");
2523
+ setPhase("steps");
2524
+ }
2525
+ };
2526
+ const advanceStep = (newAnswers, newMessages, newAnsweredIndices, fromIndex) => {
2527
+ const nextIndex = fromIndex + 1;
2528
+ if (nextIndex >= steps.length) {
2529
+ setMessages(newMessages);
2530
+ setAnswers(newAnswers);
2531
+ setAnsweredStepIndices(newAnsweredIndices);
2532
+ setCurrentStepIndex(nextIndex);
2533
+ startMatching(newAnswers, newMessages);
2534
+ } else {
2535
+ const nextStep = steps[nextIndex];
2536
+ const updated = [...newMessages, { role: "assistant", content: nextStep.question }];
2537
+ setMessages(updated);
2538
+ setAnswers(newAnswers);
2539
+ setAnsweredStepIndices(newAnsweredIndices);
2540
+ setCurrentStepIndex(nextIndex);
2541
+ }
2542
+ };
2543
+ const handleOptionClick = (option) => {
2544
+ const step = steps[currentStepIndex];
2545
+ const newAnswers = {
2546
+ ...answers,
2547
+ [step.answerKey]: { value: option, question: step.question, inputType: "selected" }
2548
+ };
2549
+ const newMessages = [...messages, { role: "user", content: option }];
2550
+ const newAnswered = new Set(answeredStepIndices);
2551
+ newAnswered.add(currentStepIndex);
2552
+ advanceStep(newAnswers, newMessages, newAnswered, currentStepIndex);
2553
+ };
2554
+ const handleMultiSelectConfirm = () => {
2555
+ if (multiSelectChoices.size === 0) return;
2556
+ const step = steps[currentStepIndex];
2557
+ const value = Array.from(multiSelectChoices).join(", ");
2558
+ const newAnswers = {
2559
+ ...answers,
2560
+ [step.answerKey]: { value, question: step.question, inputType: "selected" }
2561
+ };
2562
+ const newMessages = [...messages, { role: "user", content: value }];
2563
+ const newAnswered = new Set(answeredStepIndices);
2564
+ newAnswered.add(currentStepIndex);
2565
+ setMultiSelectChoices(/* @__PURE__ */ new Set());
2566
+ advanceStep(newAnswers, newMessages, newAnswered, currentStepIndex);
2567
+ };
2568
+ const handleFreeTextSubmit = async (text) => {
2569
+ if (!text.trim() || isStreaming) return;
2570
+ const step = steps[currentStepIndex];
2571
+ const userMsg = { role: "user", content: text };
2572
+ const newMessages = [...messages, userMsg];
2573
+ setMessages(newMessages);
2574
+ setInputValue("");
2575
+ setIsStreaming(true);
2576
+ setAwaitingFreeTextResponse(true);
2577
+ setError(null);
2578
+ try {
2579
+ const res = await fetch(
2580
+ `${apiUrl}/smart-questionnaire/${config.questionnaireId}/step/${step.name}`,
2581
+ {
2582
+ method: "POST",
2583
+ headers: jsonHeaders,
2584
+ body: JSON.stringify({ message: text, history: [] })
2585
+ }
2586
+ );
2587
+ if (!res.ok) {
2588
+ const err = await res.json().catch(() => ({ error: "Request failed" }));
2589
+ throw new Error(err.error || `HTTP ${res.status}`);
2590
+ }
2591
+ const data = await res.json();
2592
+ if (data.answer) {
2593
+ const newAnswers = {
2594
+ ...answers,
2595
+ [step.answerKey]: { value: data.answer, question: step.question, inputType: "typed" }
2596
+ };
2597
+ const newAnswered = new Set(answeredStepIndices);
2598
+ newAnswered.add(currentStepIndex);
2599
+ let msgs = newMessages;
2600
+ if (data.message) {
2601
+ msgs = [...msgs, { role: "assistant", content: data.message }];
2602
+ }
2603
+ advanceStep(newAnswers, msgs, newAnswered, currentStepIndex);
2604
+ } else {
2605
+ if (data.message) {
2606
+ setMessages([...newMessages, { role: "assistant", content: data.message }]);
2607
+ }
2608
+ }
2609
+ } catch (err) {
2610
+ setError(err instanceof Error ? err.message : "Failed to process answer");
2611
+ } finally {
2612
+ setIsStreaming(false);
2613
+ setAwaitingFreeTextResponse(false);
2614
+ }
2615
+ };
2616
+ const startMatching = async (finalAnswers, finalMessages) => {
2617
+ setPhase("matching");
2618
+ setError(null);
2619
+ try {
2620
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/match`, {
2621
+ method: "POST",
2622
+ headers: jsonHeaders,
2623
+ body: JSON.stringify({ answers: finalAnswers })
2624
+ });
2625
+ if (!res.ok) {
2626
+ const err = await res.json().catch(() => ({ error: "Matching failed" }));
2627
+ throw new Error(err.error || `HTTP ${res.status}`);
2628
+ }
2629
+ const data = await res.json();
2630
+ const matchList = data.matches || [];
2631
+ setMatches(matchList);
2632
+ startResults(finalAnswers, finalMessages, matchList);
2633
+ } catch (err) {
2634
+ setError(err instanceof Error ? err.message : "Failed to find matches");
2635
+ setPhase("results");
2636
+ }
2637
+ };
2638
+ const startResults = async (finalAnswers, finalMessages, matchList) => {
2639
+ setPhase("results");
2640
+ setError(null);
2641
+ try {
2642
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/results`, {
2643
+ method: "POST",
2644
+ headers: jsonHeaders,
2645
+ body: JSON.stringify({ matches: matchList })
2646
+ });
2647
+ if (!res.ok) {
2648
+ const err = await res.json().catch(() => ({ error: "Results failed" }));
2649
+ throw new Error(err.error || `HTTP ${res.status}`);
2650
+ }
2651
+ const data = await res.json();
2652
+ const introMsg = { role: "assistant", content: data.message || "Here is what I found:" };
2653
+ setMessages([...finalMessages, introMsg]);
2654
+ setResultsHistory([introMsg]);
2655
+ setMatches(data.matches || matchList);
2656
+ } catch (err) {
2657
+ setError(err instanceof Error ? err.message : "Failed to load results");
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
+ phase === "results" && matches && matches.length > 0 && /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexDirection: "column", gap: "8px", maxWidth: "90%" }, children: matches.map((match, i) => /* @__PURE__ */ jsx5(MatchCard, { match, theme }, i)) }),
2990
+ showOptions && currentStep.options.length > 0 && /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexDirection: "column", gap: "6px", maxWidth: "85%" }, children: currentStep.multiSelect ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
2991
+ currentStep.options.map((opt) => {
2992
+ const isSelected = multiSelectChoices.has(opt);
2993
+ return /* @__PURE__ */ jsxs4(
2994
+ "button",
2995
+ {
2996
+ onClick: () => {
2997
+ const next = new Set(multiSelectChoices);
2998
+ if (isSelected) next.delete(opt);
2999
+ else next.add(opt);
3000
+ setMultiSelectChoices(next);
3001
+ },
3002
+ style: {
3003
+ padding: "8px 14px",
3004
+ borderRadius: "8px",
3005
+ border: `1.5px solid ${isSelected ? theme.primaryColor : "#d1d5db"}`,
3006
+ backgroundColor: isSelected ? `${theme.primaryColor}10` : "white",
3007
+ color: isSelected ? theme.primaryColor : "#374151",
3008
+ fontSize: "13px",
3009
+ cursor: "pointer",
3010
+ textAlign: "left",
3011
+ fontFamily: "var(--sq-font)",
3012
+ transition: "all 0.15s ease"
3013
+ },
3014
+ children: [
3015
+ isSelected ? "\u2611 " : "\u2610 ",
3016
+ opt
3017
+ ]
3018
+ },
3019
+ opt
3020
+ );
3021
+ }),
3022
+ multiSelectChoices.size > 0 && /* @__PURE__ */ jsxs4(
3023
+ "button",
3024
+ {
3025
+ onClick: handleMultiSelectConfirm,
3026
+ style: {
3027
+ padding: "8px 14px",
3028
+ borderRadius: "8px",
3029
+ border: "none",
3030
+ backgroundColor: "var(--sq-primary)",
3031
+ color: "white",
3032
+ fontSize: "13px",
3033
+ fontWeight: 600,
3034
+ cursor: "pointer",
3035
+ fontFamily: "var(--sq-font)",
3036
+ marginTop: "4px"
3037
+ },
3038
+ children: [
3039
+ "Confirm (",
3040
+ multiSelectChoices.size,
3041
+ " selected)"
3042
+ ]
3043
+ }
3044
+ )
3045
+ ] }) : currentStep.options.map((opt) => /* @__PURE__ */ jsx5(
3046
+ "button",
3047
+ {
3048
+ onClick: () => handleOptionClick(opt),
3049
+ style: {
3050
+ padding: "8px 14px",
3051
+ borderRadius: "8px",
3052
+ border: "1.5px solid #d1d5db",
3053
+ backgroundColor: "white",
3054
+ color: "#374151",
3055
+ fontSize: "13px",
3056
+ cursor: "pointer",
3057
+ textAlign: "left",
3058
+ fontFamily: "var(--sq-font)",
3059
+ transition: "all 0.15s ease"
3060
+ },
3061
+ onMouseEnter: (e) => {
3062
+ e.currentTarget.style.borderColor = theme.primaryColor || "#8b5cf6";
3063
+ e.currentTarget.style.backgroundColor = `${theme.primaryColor}08`;
3064
+ },
3065
+ onMouseLeave: (e) => {
3066
+ e.currentTarget.style.borderColor = "#d1d5db";
3067
+ e.currentTarget.style.backgroundColor = "white";
3068
+ },
3069
+ children: opt
3070
+ },
3071
+ opt
3072
+ )) }),
3073
+ error && /* @__PURE__ */ jsx5(
3074
+ "div",
3075
+ {
3076
+ style: {
3077
+ padding: "10px 14px",
3078
+ borderRadius: "8px",
3079
+ backgroundColor: "#fef2f2",
3080
+ color: "#dc2626",
3081
+ fontSize: "13px"
3082
+ },
3083
+ children: error
3084
+ }
3085
+ ),
3086
+ /* @__PURE__ */ jsx5("div", { ref: messagesEndRef })
3087
+ ]
3088
+ }
3089
+ ),
3090
+ (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: [
3091
+ /* @__PURE__ */ jsx5(
3092
+ "input",
3093
+ {
3094
+ type: "text",
3095
+ value: inputValue,
3096
+ onChange: (e) => setInputValue(e.target.value),
3097
+ placeholder: phase === "results" ? config.placeholder || "Ask a follow-up question..." : config.placeholder || "Type your answer...",
3098
+ disabled: isStreaming,
3099
+ style: {
3100
+ flex: 1,
3101
+ padding: "10px 14px",
3102
+ borderRadius: "8px",
3103
+ border: "1px solid #d1d5db",
3104
+ fontSize: "14px",
3105
+ fontFamily: "var(--sq-font)",
3106
+ outline: "none",
3107
+ color: "#1f2937",
3108
+ backgroundColor: "#ffffff"
3109
+ }
3110
+ }
3111
+ ),
3112
+ /* @__PURE__ */ jsx5(
3113
+ "button",
3114
+ {
3115
+ type: "submit",
3116
+ disabled: isStreaming || !inputValue.trim(),
3117
+ style: {
3118
+ padding: "10px 16px",
3119
+ borderRadius: "8px",
3120
+ backgroundColor: isStreaming || !inputValue.trim() ? "#d1d5db" : "var(--sq-primary)",
3121
+ color: "white",
3122
+ border: "none",
3123
+ cursor: isStreaming || !inputValue.trim() ? "not-allowed" : "pointer",
3124
+ fontWeight: 500,
3125
+ fontSize: "14px"
3126
+ },
3127
+ children: isStreaming ? "..." : "Send"
3128
+ }
3129
+ )
3130
+ ] }) }),
3131
+ /* @__PURE__ */ jsx5("style", { children: `
3132
+ @keyframes sq-blink {
3133
+ 0%, 50% { opacity: 1; }
3134
+ 51%, 100% { opacity: 0; }
3135
+ }
3136
+ @keyframes sq-spin {
3137
+ from { transform: rotate(0deg); }
3138
+ to { transform: rotate(360deg); }
3139
+ }
3140
+ ` })
3141
+ ]
3142
+ }
3143
+ );
3144
+ }
3145
+
2332
3146
  // src/hooks/useSession.ts
2333
- import { useState as useState5, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef5 } from "react";
3147
+ import { useState as useState6, useEffect as useEffect6, useCallback as useCallback2, useRef as useRef6 } from "react";
2334
3148
  function useSession(options) {
2335
3149
  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(() => {
3150
+ const [session, setSession] = useState6(null);
3151
+ const [isLoading, setIsLoading] = useState6(true);
3152
+ const [error, setError] = useState6(null);
3153
+ const clientRef = useRef6(null);
3154
+ const isStreamingRef = useRef6(false);
3155
+ useEffect6(() => {
2342
3156
  clientRef.current = new InformedAIClient(apiUrl);
2343
3157
  }, [apiUrl]);
2344
- useEffect5(() => {
3158
+ useEffect6(() => {
2345
3159
  async function initialize() {
2346
3160
  if (!clientRef.current) return;
2347
3161
  try {
@@ -2455,6 +3269,7 @@ export {
2455
3269
  InformedAIClient,
2456
3270
  InformedAIProvider,
2457
3271
  InformedAssistant,
3272
+ SmartQuestionnaire,
2458
3273
  WebsiteChatbot,
2459
3274
  useInformedAI,
2460
3275
  useSession