@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.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,832 @@ 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 SmartQuestionnaire({ className, ...config }) {
2472
+ const theme = { ...defaultTheme4, ...config.theme };
2473
+ const apiUrl = config.apiUrl || "https://api.informedai.app/api/v1";
2474
+ const jsonHeaders = { "Content-Type": "application/json" };
2475
+ const [phase, setPhase] = (0, import_react5.useState)("loading");
2476
+ const [steps, setSteps] = (0, import_react5.useState)([]);
2477
+ const [currentStepIndex, setCurrentStepIndex] = (0, import_react5.useState)(0);
2478
+ const [answers, setAnswers] = (0, import_react5.useState)({});
2479
+ const [messages, setMessages] = (0, import_react5.useState)([]);
2480
+ const [inputValue, setInputValue] = (0, import_react5.useState)("");
2481
+ const [isStreaming, setIsStreaming] = (0, import_react5.useState)(false);
2482
+ const [streamingContent, setStreamingContent] = (0, import_react5.useState)("");
2370
2483
  const [error, setError] = (0, import_react5.useState)(null);
2371
- const clientRef = (0, import_react5.useRef)(null);
2372
- const isStreamingRef = (0, import_react5.useRef)(false);
2484
+ const [isCollapsed, setIsCollapsed] = (0, import_react5.useState)(config.defaultCollapsed ?? false);
2485
+ const [multiSelectChoices, setMultiSelectChoices] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
2486
+ const [matches, setMatches] = (0, import_react5.useState)(null);
2487
+ const [resultsHistory, setResultsHistory] = (0, import_react5.useState)([]);
2488
+ const [answeredStepIndices, setAnsweredStepIndices] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
2489
+ const [awaitingFreeTextResponse, setAwaitingFreeTextResponse] = (0, import_react5.useState)(false);
2490
+ const messagesEndRef = (0, import_react5.useRef)(null);
2491
+ (0, import_react5.useEffect)(() => {
2492
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
2493
+ }, [messages, streamingContent]);
2373
2494
  (0, import_react5.useEffect)(() => {
2495
+ fetchSteps();
2496
+ }, []);
2497
+ const fetchSteps = async () => {
2498
+ try {
2499
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/steps`, {
2500
+ headers: jsonHeaders
2501
+ });
2502
+ if (!res.ok) {
2503
+ const err = await res.json().catch(() => ({ error: "Failed to load" }));
2504
+ throw new Error(err.error || `HTTP ${res.status}`);
2505
+ }
2506
+ const data = await res.json();
2507
+ if (data.length === 0) {
2508
+ setError("This questionnaire has no steps configured.");
2509
+ setPhase("steps");
2510
+ return;
2511
+ }
2512
+ setSteps(data);
2513
+ setMessages([{ role: "assistant", content: data[0].question }]);
2514
+ setPhase("steps");
2515
+ } catch (err) {
2516
+ setError(err instanceof Error ? err.message : "Failed to load questionnaire");
2517
+ setPhase("steps");
2518
+ }
2519
+ };
2520
+ const advanceStep = (newAnswers, newMessages, newAnsweredIndices, fromIndex) => {
2521
+ const nextIndex = fromIndex + 1;
2522
+ if (nextIndex >= steps.length) {
2523
+ setMessages(newMessages);
2524
+ setAnswers(newAnswers);
2525
+ setAnsweredStepIndices(newAnsweredIndices);
2526
+ setCurrentStepIndex(nextIndex);
2527
+ startMatching(newAnswers, newMessages);
2528
+ } else {
2529
+ const nextStep = steps[nextIndex];
2530
+ const updated = [...newMessages, { role: "assistant", content: nextStep.question }];
2531
+ setMessages(updated);
2532
+ setAnswers(newAnswers);
2533
+ setAnsweredStepIndices(newAnsweredIndices);
2534
+ setCurrentStepIndex(nextIndex);
2535
+ }
2536
+ };
2537
+ const handleOptionClick = (option) => {
2538
+ const step = steps[currentStepIndex];
2539
+ const newAnswers = {
2540
+ ...answers,
2541
+ [step.answerKey]: { value: option, question: step.question, inputType: "selected" }
2542
+ };
2543
+ const newMessages = [...messages, { role: "user", content: option }];
2544
+ const newAnswered = new Set(answeredStepIndices);
2545
+ newAnswered.add(currentStepIndex);
2546
+ advanceStep(newAnswers, newMessages, newAnswered, currentStepIndex);
2547
+ };
2548
+ const handleMultiSelectConfirm = () => {
2549
+ if (multiSelectChoices.size === 0) return;
2550
+ const step = steps[currentStepIndex];
2551
+ const value = Array.from(multiSelectChoices).join(", ");
2552
+ const newAnswers = {
2553
+ ...answers,
2554
+ [step.answerKey]: { value, question: step.question, inputType: "selected" }
2555
+ };
2556
+ const newMessages = [...messages, { role: "user", content: value }];
2557
+ const newAnswered = new Set(answeredStepIndices);
2558
+ newAnswered.add(currentStepIndex);
2559
+ setMultiSelectChoices(/* @__PURE__ */ new Set());
2560
+ advanceStep(newAnswers, newMessages, newAnswered, currentStepIndex);
2561
+ };
2562
+ const handleFreeTextSubmit = async (text) => {
2563
+ if (!text.trim() || isStreaming) return;
2564
+ const step = steps[currentStepIndex];
2565
+ const userMsg = { role: "user", content: text };
2566
+ const newMessages = [...messages, userMsg];
2567
+ setMessages(newMessages);
2568
+ setInputValue("");
2569
+ setIsStreaming(true);
2570
+ setAwaitingFreeTextResponse(true);
2571
+ setError(null);
2572
+ try {
2573
+ const res = await fetch(
2574
+ `${apiUrl}/smart-questionnaire/${config.questionnaireId}/step/${step.name}`,
2575
+ {
2576
+ method: "POST",
2577
+ headers: jsonHeaders,
2578
+ body: JSON.stringify({ message: text, history: [] })
2579
+ }
2580
+ );
2581
+ if (!res.ok) {
2582
+ const err = await res.json().catch(() => ({ error: "Request failed" }));
2583
+ throw new Error(err.error || `HTTP ${res.status}`);
2584
+ }
2585
+ const data = await res.json();
2586
+ if (data.answer) {
2587
+ const newAnswers = {
2588
+ ...answers,
2589
+ [step.answerKey]: { value: data.answer, question: step.question, inputType: "typed" }
2590
+ };
2591
+ const newAnswered = new Set(answeredStepIndices);
2592
+ newAnswered.add(currentStepIndex);
2593
+ let msgs = newMessages;
2594
+ if (data.message) {
2595
+ msgs = [...msgs, { role: "assistant", content: data.message }];
2596
+ }
2597
+ advanceStep(newAnswers, msgs, newAnswered, currentStepIndex);
2598
+ } else {
2599
+ if (data.message) {
2600
+ setMessages([...newMessages, { role: "assistant", content: data.message }]);
2601
+ }
2602
+ }
2603
+ } catch (err) {
2604
+ setError(err instanceof Error ? err.message : "Failed to process answer");
2605
+ } finally {
2606
+ setIsStreaming(false);
2607
+ setAwaitingFreeTextResponse(false);
2608
+ }
2609
+ };
2610
+ const startMatching = async (finalAnswers, finalMessages) => {
2611
+ setPhase("matching");
2612
+ setError(null);
2613
+ try {
2614
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/match`, {
2615
+ method: "POST",
2616
+ headers: jsonHeaders,
2617
+ body: JSON.stringify({ answers: finalAnswers })
2618
+ });
2619
+ if (!res.ok) {
2620
+ const err = await res.json().catch(() => ({ error: "Matching failed" }));
2621
+ throw new Error(err.error || `HTTP ${res.status}`);
2622
+ }
2623
+ const data = await res.json();
2624
+ const matchList = data.matches || [];
2625
+ setMatches(matchList);
2626
+ startResults(finalAnswers, finalMessages, matchList);
2627
+ } catch (err) {
2628
+ setError(err instanceof Error ? err.message : "Failed to find matches");
2629
+ setPhase("results");
2630
+ }
2631
+ };
2632
+ const startResults = async (finalAnswers, finalMessages, matchList) => {
2633
+ setPhase("results");
2634
+ setIsStreaming(true);
2635
+ setStreamingContent("");
2636
+ try {
2637
+ const res = await fetch(`${apiUrl}/smart-questionnaire/${config.questionnaireId}/results`, {
2638
+ method: "POST",
2639
+ headers: jsonHeaders,
2640
+ body: JSON.stringify({ matches: matchList, history: [] })
2641
+ });
2642
+ if (!res.ok) {
2643
+ const err = await res.json().catch(() => ({ error: "Results failed" }));
2644
+ throw new Error(err.error || `HTTP ${res.status}`);
2645
+ }
2646
+ const reader = res.body?.getReader();
2647
+ if (!reader) throw new Error("No response body");
2648
+ const decoder = new TextDecoder();
2649
+ let buffer = "";
2650
+ let fullContent = "";
2651
+ let action = null;
2652
+ while (true) {
2653
+ const { done, value } = await reader.read();
2654
+ if (done) break;
2655
+ buffer += decoder.decode(value, { stream: true });
2656
+ const lines = buffer.split("\n");
2657
+ buffer = lines.pop() || "";
2658
+ for (const line of lines) {
2659
+ if (line.startsWith("event:")) continue;
2660
+ if (line.startsWith("data:")) {
2661
+ const d = line.slice(5).trim();
2662
+ try {
2663
+ const parsed = JSON.parse(d);
2664
+ if (parsed.content) {
2665
+ fullContent += parsed.content;
2666
+ setStreamingContent(fullContent);
2667
+ }
2668
+ if (parsed.fullContent) {
2669
+ fullContent = parsed.fullContent;
2670
+ }
2671
+ if (parsed.action) {
2672
+ action = parsed.action;
2673
+ }
2674
+ } catch {
2675
+ }
2676
+ }
2677
+ }
2678
+ }
2679
+ const displayContent = fullContent.replace(/\[RESTART\]/g, "").trim();
2680
+ const resultMsg = { role: "assistant", content: displayContent };
2681
+ setMessages([...finalMessages, resultMsg]);
2682
+ setResultsHistory([resultMsg]);
2683
+ setStreamingContent("");
2684
+ if (action === "restart") {
2685
+ handleRestart();
2686
+ }
2687
+ } catch (err) {
2688
+ setError(err instanceof Error ? err.message : "Failed to stream results");
2689
+ } finally {
2690
+ setIsStreaming(false);
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
+ 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: [
3023
+ currentStep.options.map((opt) => {
3024
+ const isSelected = multiSelectChoices.has(opt);
3025
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3026
+ "button",
3027
+ {
3028
+ onClick: () => {
3029
+ const next = new Set(multiSelectChoices);
3030
+ if (isSelected) next.delete(opt);
3031
+ else next.add(opt);
3032
+ setMultiSelectChoices(next);
3033
+ },
3034
+ style: {
3035
+ padding: "8px 14px",
3036
+ borderRadius: "8px",
3037
+ border: `1.5px solid ${isSelected ? theme.primaryColor : "#d1d5db"}`,
3038
+ backgroundColor: isSelected ? `${theme.primaryColor}10` : "white",
3039
+ color: isSelected ? theme.primaryColor : "#374151",
3040
+ fontSize: "13px",
3041
+ cursor: "pointer",
3042
+ textAlign: "left",
3043
+ fontFamily: "var(--sq-font)",
3044
+ transition: "all 0.15s ease"
3045
+ },
3046
+ children: [
3047
+ isSelected ? "\u2611 " : "\u2610 ",
3048
+ opt
3049
+ ]
3050
+ },
3051
+ opt
3052
+ );
3053
+ }),
3054
+ multiSelectChoices.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3055
+ "button",
3056
+ {
3057
+ onClick: handleMultiSelectConfirm,
3058
+ style: {
3059
+ padding: "8px 14px",
3060
+ borderRadius: "8px",
3061
+ border: "none",
3062
+ backgroundColor: "var(--sq-primary)",
3063
+ color: "white",
3064
+ fontSize: "13px",
3065
+ fontWeight: 600,
3066
+ cursor: "pointer",
3067
+ fontFamily: "var(--sq-font)",
3068
+ marginTop: "4px"
3069
+ },
3070
+ children: [
3071
+ "Confirm (",
3072
+ multiSelectChoices.size,
3073
+ " selected)"
3074
+ ]
3075
+ }
3076
+ )
3077
+ ] }) : currentStep.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3078
+ "button",
3079
+ {
3080
+ onClick: () => handleOptionClick(opt),
3081
+ style: {
3082
+ padding: "8px 14px",
3083
+ borderRadius: "8px",
3084
+ border: "1.5px solid #d1d5db",
3085
+ backgroundColor: "white",
3086
+ color: "#374151",
3087
+ fontSize: "13px",
3088
+ cursor: "pointer",
3089
+ textAlign: "left",
3090
+ fontFamily: "var(--sq-font)",
3091
+ transition: "all 0.15s ease"
3092
+ },
3093
+ onMouseEnter: (e) => {
3094
+ e.currentTarget.style.borderColor = theme.primaryColor || "#8b5cf6";
3095
+ e.currentTarget.style.backgroundColor = `${theme.primaryColor}08`;
3096
+ },
3097
+ onMouseLeave: (e) => {
3098
+ e.currentTarget.style.borderColor = "#d1d5db";
3099
+ e.currentTarget.style.backgroundColor = "white";
3100
+ },
3101
+ children: opt
3102
+ },
3103
+ opt
3104
+ )) }),
3105
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3106
+ "div",
3107
+ {
3108
+ style: {
3109
+ padding: "10px 14px",
3110
+ borderRadius: "8px",
3111
+ backgroundColor: "#fef2f2",
3112
+ color: "#dc2626",
3113
+ fontSize: "13px"
3114
+ },
3115
+ children: error
3116
+ }
3117
+ ),
3118
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: messagesEndRef })
3119
+ ]
3120
+ }
3121
+ ),
3122
+ (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: [
3123
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3124
+ "input",
3125
+ {
3126
+ type: "text",
3127
+ value: inputValue,
3128
+ onChange: (e) => setInputValue(e.target.value),
3129
+ placeholder: phase === "results" ? config.placeholder || "Ask a follow-up question..." : config.placeholder || "Type your answer...",
3130
+ disabled: isStreaming,
3131
+ style: {
3132
+ flex: 1,
3133
+ padding: "10px 14px",
3134
+ borderRadius: "8px",
3135
+ border: "1px solid #d1d5db",
3136
+ fontSize: "14px",
3137
+ fontFamily: "var(--sq-font)",
3138
+ outline: "none",
3139
+ color: "#1f2937",
3140
+ backgroundColor: "#ffffff"
3141
+ }
3142
+ }
3143
+ ),
3144
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3145
+ "button",
3146
+ {
3147
+ type: "submit",
3148
+ disabled: isStreaming || !inputValue.trim(),
3149
+ style: {
3150
+ padding: "10px 16px",
3151
+ borderRadius: "8px",
3152
+ backgroundColor: isStreaming || !inputValue.trim() ? "#d1d5db" : "var(--sq-primary)",
3153
+ color: "white",
3154
+ border: "none",
3155
+ cursor: isStreaming || !inputValue.trim() ? "not-allowed" : "pointer",
3156
+ fontWeight: 500,
3157
+ fontSize: "14px"
3158
+ },
3159
+ children: isStreaming ? "..." : "Send"
3160
+ }
3161
+ )
3162
+ ] }) }),
3163
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `
3164
+ @keyframes sq-blink {
3165
+ 0%, 50% { opacity: 1; }
3166
+ 51%, 100% { opacity: 0; }
3167
+ }
3168
+ @keyframes sq-spin {
3169
+ from { transform: rotate(0deg); }
3170
+ to { transform: rotate(360deg); }
3171
+ }
3172
+ ` })
3173
+ ]
3174
+ }
3175
+ );
3176
+ }
3177
+
3178
+ // src/hooks/useSession.ts
3179
+ var import_react6 = require("react");
3180
+ function useSession(options) {
3181
+ const { apiUrl, documentTypeId, sessionId, onSessionChange, onError } = options;
3182
+ const [session, setSession] = (0, import_react6.useState)(null);
3183
+ const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
3184
+ const [error, setError] = (0, import_react6.useState)(null);
3185
+ const clientRef = (0, import_react6.useRef)(null);
3186
+ const isStreamingRef = (0, import_react6.useRef)(false);
3187
+ (0, import_react6.useEffect)(() => {
2374
3188
  clientRef.current = new InformedAIClient(apiUrl);
2375
3189
  }, [apiUrl]);
2376
- (0, import_react5.useEffect)(() => {
3190
+ (0, import_react6.useEffect)(() => {
2377
3191
  async function initialize() {
2378
3192
  if (!clientRef.current) return;
2379
3193
  try {
@@ -2398,7 +3212,7 @@ function useSession(options) {
2398
3212
  }
2399
3213
  initialize();
2400
3214
  }, [documentTypeId, sessionId, onSessionChange, onError]);
2401
- const handleSSEEvent = (0, import_react5.useCallback)((event) => {
3215
+ const handleSSEEvent = (0, import_react6.useCallback)((event) => {
2402
3216
  if (event.type === "done" || event.type === "session_update") {
2403
3217
  if (event.session) {
2404
3218
  setSession(event.session);
@@ -2413,7 +3227,7 @@ function useSession(options) {
2413
3227
  isStreamingRef.current = false;
2414
3228
  }
2415
3229
  }, [onSessionChange, onError]);
2416
- const sendMessage = (0, import_react5.useCallback)(async (message) => {
3230
+ const sendMessage = (0, import_react6.useCallback)(async (message) => {
2417
3231
  if (!clientRef.current || !session || isStreamingRef.current) return;
2418
3232
  try {
2419
3233
  isStreamingRef.current = true;
@@ -2426,7 +3240,7 @@ function useSession(options) {
2426
3240
  isStreamingRef.current = false;
2427
3241
  }
2428
3242
  }, [session, handleSSEEvent, onError]);
2429
- const sendQuickAction = (0, import_react5.useCallback)(async (action) => {
3243
+ const sendQuickAction = (0, import_react6.useCallback)(async (action) => {
2430
3244
  if (!clientRef.current || !session || isStreamingRef.current) return;
2431
3245
  try {
2432
3246
  isStreamingRef.current = true;
@@ -2446,7 +3260,7 @@ function useSession(options) {
2446
3260
  isStreamingRef.current = false;
2447
3261
  }
2448
3262
  }, [session, handleSSEEvent, onSessionChange, onError]);
2449
- const applyPendingValue = (0, import_react5.useCallback)(async () => {
3263
+ const applyPendingValue = (0, import_react6.useCallback)(async () => {
2450
3264
  if (!clientRef.current || !session) return;
2451
3265
  try {
2452
3266
  setError(null);
@@ -2459,7 +3273,7 @@ function useSession(options) {
2459
3273
  onError?.(error2);
2460
3274
  }
2461
3275
  }, [session, onSessionChange, onError]);
2462
- const skipTask = (0, import_react5.useCallback)(async () => {
3276
+ const skipTask = (0, import_react6.useCallback)(async () => {
2463
3277
  if (!clientRef.current || !session) return;
2464
3278
  try {
2465
3279
  setError(null);
@@ -2488,6 +3302,7 @@ function useSession(options) {
2488
3302
  InformedAIClient,
2489
3303
  InformedAIProvider,
2490
3304
  InformedAssistant,
3305
+ SmartQuestionnaire,
2491
3306
  WebsiteChatbot,
2492
3307
  useInformedAI,
2493
3308
  useSession