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