HEX
Server: nginx/1.18.0
System: Linux vcwordpress 5.15.0-174-generic #184-Ubuntu SMP Fri Mar 13 18:41:50 UTC 2026 x86_64
User: root (0)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/bharti-foundation.stgviitor.com/wp-content/themes/lifeline/assets/js/assistant-float.js
/*
 * Lifeline Theme – Floating AI Assistant
 * --------------------------------------
 * Entire file with formatting upgrades:
 * • Bot answers now support **bold**, line breaks, and bullets.
 * • User messages remain fully escaped.
 * • History logic unchanged.
 */

jQuery(function ($) {
  // Only run on admin pages
  if (!window.pagenow || !$("body").hasClass("wp-admin")) {
    return;
  }

  const STORAGE_KEY = "lifeline_ai_history_v2";
  let isOpen = false;
  let isMinimized = false;

  /* ---------- Create floating widget HTML ---------- */
  const createWidget = () => {
    const widgetHTML = `
      <div id="lifeline-float-widget" class="lifeline-float-widget">
        <!-- Floating Button -->
        <div id="lifeline-float-btn" class="lifeline-float-btn">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
          </svg>
          <span class="lifeline-float-badge" id="lifeline-float-badge">?</span>
        </div>

        <!-- Chat Window -->
        <div id="lifeline-float-window" class="lifeline-float-window">
          <div class="lifeline-float-header">
            <div class="lifeline-float-title">
              <strong>Lifeline Assistant</strong>
              <span class="lifeline-float-subtitle">Ask about theme features</span>
            </div>
            <div class="lifeline-float-actions">
              <button id="lifeline-float-minimize" class="lifeline-float-action-btn" title="Minimize">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <line x1="5" y1="12" x2="19" y2="12"></line>
                </svg>
              </button>
              <button id="lifeline-float-close" class="lifeline-float-action-btn" title="Close">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <line x1="18" y1="6" x2="6" y2="18"></line>
                  <line x1="6" y1="6" x2="18" y2="18"></line>
                </svg>
              </button>
            </div>
          </div>

          <div class="lifeline-float-body">
            <div id="lifeline-float-chat-box" class="lifeline-float-chat-box">
              <div class="lifeline-welcome-msg">
                <strong>Bot:</strong> Hi! I'm here to help you with the Lifeline theme. Ask me anything about features, customization, or settings.
              </div>
            </div>

            <div class="lifeline-float-input-area">
              <textarea 
                id="lifeline-float-input" 
                rows="2" 
                placeholder="Type your question about the theme..."
                maxlength="1000"
              ></textarea>
              <div class="lifeline-float-input-actions">
                <button id="lifeline-float-clear" class="lifeline-float-clear-btn" title="Clear chat">
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <polyline points="3,6 5,6 21,6"></polyline>
                    <path d="M19,6v14a2,2 0,0 1,-2,2H7a2,2 0,0 1,-2,-2V6m3,0V4a2,2 0,0 1,2,-2h4a2,2 0,0 1,2,2v2"></path>
                  </svg>
                </button>
                <button id="lifeline-float-send" class="lifeline-float-send-btn">
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <line x1="22" y1="2" x2="11" y2="13"></line>
                    <polygon points="22,2 15,22 11,13 2,9 22,2"></polygon>
                  </svg>
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
    $("body").append(widgetHTML);
  };

  /* ---------- Helper functions ---------- */
  const escHTML = (str) =>
    str.replace(
      /[&<>'"]/g,
      (c) =>
        ({
          "&": "&amp;",
          "<": "&lt;",
          ">": "&gt;",
          '"': "&quot;",
          "'": "&#039;",
        }[c])
    );

  // ⭐ UPDATED: Markdown‑to‑HTML formatter for the bot’s replies
  const formatAIText = (raw) =>
    raw
      .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>") 
      .replace(/(^|\n)(\d+)\.\s+/g, "$1$2.&nbsp;") 
      .replace(/\n/g, "<br>"); 

  const scrollDown = () => {
    const $chatBox = $("#lifeline-float-chat-box");
    $chatBox.scrollTop($chatBox[0].scrollHeight);
  };

  // ⭐ UPDATED: allow HTML in Bot messages, keep user messages escaped
  const append = (who, text) => {
    const $chatBox = $("#lifeline-float-chat-box");
    const cls = who === "You" ? "lifeline-msg-user" : "lifeline-msg-bot";
    const raw = text ? String(text) : "No response received";

    const content =
      who === "Bot"
        ? formatAIText(raw) // Bot: markdown ➜ minimal HTML
        : escHTML(raw); // User: fully escaped

    $chatBox.append(
      `<div class="lifeline-msg ${cls}"><strong>${who}:</strong> <span>${content}</span></div>`
    );
    scrollDown();
    saveHistory();
  };

  /* ---------- History management ---------- */
  const loadHistory = () => {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return;

    let arr;
    try {
      arr = JSON.parse(raw);
    } catch {
      return;
    }

    // Clear welcome message if there's history
    if (arr.length > 0) {
      $("#lifeline-float-chat-box").find(".lifeline-welcome-msg").remove();
    }

    arr.forEach(({ who, text }) => append(who, text));
  };

  const saveHistory = () => {
    const messages = [];
    $("#lifeline-float-chat-box")
      .children(".lifeline-msg")
      .each(function () {
        const who = $(this).hasClass("lifeline-msg-user") ? "You" : "Bot";
        const fullText = $(this).text() || "";
        const text = fullText.replace(/^You:\s|^Bot:\s/, "") || "";
        if (text.trim()) {
          messages.push({ who, text });
        }
      });
    localStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
  };

  const clearHistory = () => {
    localStorage.removeItem(STORAGE_KEY);
    $("#lifeline-float-chat-box")
      .empty()
      .append(
        '<div class="lifeline-welcome-msg"><strong>Bot:</strong> Hi! I\'m here to help you with the Lifeline theme. Ask me anything about features, customization, or settings.</div>'
      );
  };

  /* ---------- Typing indicator ---------- */
  const setTyping = (on) => {
    const $chatBox = $("#lifeline-float-chat-box");
    if (on) {
      $chatBox.append(
        `<div class="lifeline-msg lifeline-msg-bot typing-holder">
           <strong>Shad:</strong>
           <span class="lifeline-typing-indicator">
             <span></span><span></span><span></span>
           </span>
         </div>`
      );
      scrollDown();
    } else {
      $chatBox.find(".typing-holder").remove();
    }
  };

  /* ---------- Send message ---------- */
  const sendMessage = () => {
    const $input = $("#lifeline-float-input");
    const raw = $input.val().trim();
    if (!raw) return;

    // Remove welcome message on first user message
    $("#lifeline-float-chat-box").find(".lifeline-welcome-msg").remove();

    append("You", raw);
    $input.val("").focus();
    setTyping(true);

    // Debug: Check if lifelineAI is available
    if (typeof lifelineAI === "undefined") {
      setTyping(false);
      append(
        "Bot",
        "Configuration error: lifelineAI not found. Please check your setup."
      );
      return;
    }

    $.post(
      lifelineAI.ajax_url,
      {
        action: "lifeline_send_to_ai",
        nonce: lifelineAI.nonce,
        message: raw,
      },
      (res) => {
        setTyping(false);
        // console.log("Response received:", res); // Debug log

        if (res && res.success) {
          const botResponse = res.data || "No response data received";
          append("Bot", botResponse);
        } else {
          const errorMsg =
            res && res.data ? res.data : "Unknown error occurred";
          append("Bot", "Error: " + errorMsg);
        }
      }
    ).fail((xhr, status, error) => {
      setTyping(false);
      console.error("AJAX request failed:", xhr, status, error); // Debug log
      append("Bot", "Network error: " + (error || "Request failed"));
    });
  };

  /* ---------- Widget controls ---------- */
  const openWidget = () => {
    isOpen = true;
    isMinimized = false;
    $("#lifeline-float-window").addClass("open");
    $("#lifeline-float-btn").addClass("open");
    $("#lifeline-float-badge").hide();
    $("#lifeline-float-input").focus();
  };

  const closeWidget = () => {
    isOpen = false;
    isMinimized = false;
    $("#lifeline-float-window").removeClass("open minimized");
    $("#lifeline-float-btn").removeClass("open");
    $("#lifeline-float-badge").show();
  };

  const minimizeWidget = () => {
    isMinimized = true;
    $("#lifeline-float-window").addClass("minimized");
  };

  const restoreWidget = () => {
    isMinimized = false;
    $("#lifeline-float-window").removeClass("minimized");
    $("#lifeline-float-input").focus();
  };

  /* ---------- Initialize ---------- */
  const init = () => {
    createWidget();
    loadHistory();

    // Event listeners
    $("#lifeline-float-btn").on("click", () => {
      if (isOpen) {
        if (isMinimized) {
          restoreWidget();
        } else {
          closeWidget();
        }
      } else {
        openWidget();
      }
    });

    $("#lifeline-float-close").on("click", closeWidget);
    $("#lifeline-float-minimize").on("click", minimizeWidget);
    $("#lifeline-float-clear").on("click", clearHistory);
    $("#lifeline-float-send").on("click", sendMessage);

    $("#lifeline-float-input").on("keydown", (e) => {
      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();
        sendMessage();
      }
    });

    // Auto-resize textarea
    $("#lifeline-float-input").on("input", function () {
      this.style.height = "auto";
      this.style.height = Math.min(this.scrollHeight, 100) + "px";
    });

    // Close on escape
    $(document).on("keydown", (e) => {
      if (e.key === "Escape" && isOpen) {
        closeWidget();
      }
    });
  };

  // Initialize when DOM is ready
  init();
});