dp-widgets-framework 1.7.0 → 1.7.1

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.esm.js CHANGED
@@ -7,7 +7,7 @@ import * as LabelPrimitive from '@radix-ui/react-label';
7
7
  import { cva } from 'class-variance-authority';
8
8
  import * as SwitchPrimitives from '@radix-ui/react-switch';
9
9
  import * as SelectPrimitive from '@radix-ui/react-select';
10
- import { ChevronDown, ChevronUp, Check, AlertCircle, MoveUp, MoveDown, Trash2, Plus, Bot, Type, Layout, LayoutGrid, BarChart as BarChart$1, Filter, Search, ArrowUp, ArrowDown, ChevronRight, RefreshCw, Send, Loader2, X, AlignVerticalSpaceAround, LineChart as LineChart$1, PieChart as PieChart$1, Table, FileText, SlidersHorizontal, GripHorizontal, Edit, MessageCircleX, ChevronLeft, Maximize2, Grid3X3, Edit2 } from 'lucide-react';
10
+ import { ChevronDown, ChevronUp, Check, AlertCircle, MoveUp, MoveDown, Trash2, Plus, Bot, Type, Layout, LayoutGrid, BarChart as BarChart$1, Filter, Search, ArrowUp, ArrowDown, ChevronRight, RefreshCw, Loader2, Download, Send, X, AlignVerticalSpaceAround, LineChart as LineChart$1, PieChart as PieChart$1, Table, FileText, SlidersHorizontal, GripHorizontal, Edit, MessageCircleX, ChevronLeft, Maximize2, Grid3X3, Edit2 } from 'lucide-react';
11
11
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
12
12
  import { Slot, createSlot, createSlottable } from '@radix-ui/react-slot';
13
13
  import { debounce as debounce$1 } from 'lodash';
@@ -17,6 +17,7 @@ import { v4 } from 'uuid';
17
17
  import * as ReactDOM from 'react-dom';
18
18
  import { TextMessage, Role } from '@copilotkit/runtime-client-gql';
19
19
  import { CopilotKit, useCopilotContext, useCoAgent, useCopilotChat } from '@copilotkit/react-core';
20
+ import jsPDF from 'jspdf';
20
21
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
21
22
  import * as DialogPrimitive from '@radix-ui/react-dialog';
22
23
  import { format } from 'date-fns';
@@ -44409,6 +44410,175 @@ function SpacerWidget({ widget }) {
44409
44410
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full", style: styles });
44410
44411
  }
44411
44412
 
44413
+ const WISENBAKER_PRIMARY = "#8f002d";
44414
+ const WISENBAKER_GRAY = "#475569";
44415
+ function parseMarkdown(text) {
44416
+ const segments = [];
44417
+ const regex = /\*\*(.+?)\*\*/g;
44418
+ let lastIndex = 0;
44419
+ let match;
44420
+ while ((match = regex.exec(text)) !== null) {
44421
+ if (match.index > lastIndex) {
44422
+ segments.push({ text: text.slice(lastIndex, match.index), bold: false });
44423
+ }
44424
+ segments.push({ text: match[1], bold: true });
44425
+ lastIndex = regex.lastIndex;
44426
+ }
44427
+ if (lastIndex < text.length) {
44428
+ segments.push({ text: text.slice(lastIndex), bold: false });
44429
+ }
44430
+ return segments.length > 0 ? segments : [{ text, bold: false }];
44431
+ }
44432
+ function formatLine(line) {
44433
+ const bulletMatch = line.match(/^(\s*)[\*\-•]\s+(.*)$/);
44434
+ if (bulletMatch) {
44435
+ const indent = bulletMatch[1].length;
44436
+ return { text: bulletMatch[2], isBullet: true, indent };
44437
+ }
44438
+ const numberedMatch = line.match(/^(\s*)\d+\.\s+(.*)$/);
44439
+ if (numberedMatch) {
44440
+ const indent = numberedMatch[1].length;
44441
+ return { text: numberedMatch[2], isBullet: true, indent };
44442
+ }
44443
+ return { text: line, isBullet: false, indent: 0 };
44444
+ }
44445
+ function generateExportFilename(widgetId) {
44446
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
44447
+ return `NSight_Chat_Export_${widgetId}_${date}.pdf`;
44448
+ }
44449
+ function exportChatToPDF(options) {
44450
+ const { widgetId, messages, dashboardName } = options;
44451
+ const doc = new jsPDF();
44452
+ let yPosition = 25;
44453
+ const pageHeight = doc.internal.pageSize.height;
44454
+ const pageWidth = doc.internal.pageSize.width;
44455
+ const margin = 20;
44456
+ const contentWidth = pageWidth - margin * 2;
44457
+ const maxWidth = contentWidth - 10;
44458
+ const checkPageBreak = (requiredSpace = 20) => {
44459
+ if (yPosition > pageHeight - requiredSpace) {
44460
+ doc.addPage();
44461
+ yPosition = 25;
44462
+ }
44463
+ };
44464
+ const renderFormattedText = (text, x, maxW, baseIndent = 0) => {
44465
+ const lines = text.split("\n");
44466
+ lines.forEach((line) => {
44467
+ const { text: lineText, isBullet, indent } = formatLine(line);
44468
+ const lineIndent = x + baseIndent + indent * 2;
44469
+ if (isBullet) {
44470
+ checkPageBreak(10);
44471
+ doc.setFont("helvetica", "normal");
44472
+ doc.text("\u2022", lineIndent, yPosition);
44473
+ }
44474
+ const textX = isBullet ? lineIndent + 5 : lineIndent;
44475
+ const textMaxWidth = maxW - (textX - x);
44476
+ const segments = parseMarkdown(lineText);
44477
+ let currentLine = "";
44478
+ let currentX = textX;
44479
+ segments.forEach((segment) => {
44480
+ doc.setFont("helvetica", segment.bold ? "bold" : "normal");
44481
+ const words = segment.text.split(" ");
44482
+ words.forEach((word, wordIndex) => {
44483
+ const testLine = currentLine + (currentLine ? " " : "") + word;
44484
+ const testWidth = doc.getTextWidth(testLine);
44485
+ if (testWidth > textMaxWidth && currentLine) {
44486
+ checkPageBreak(10);
44487
+ doc.text(currentLine, currentX, yPosition);
44488
+ yPosition += 5;
44489
+ currentLine = word;
44490
+ currentX = isBullet ? lineIndent + 5 : x + baseIndent;
44491
+ } else {
44492
+ currentLine = testLine;
44493
+ }
44494
+ });
44495
+ });
44496
+ if (currentLine) {
44497
+ checkPageBreak(10);
44498
+ const lastSegment = segments[segments.length - 1];
44499
+ doc.setFont("helvetica", (lastSegment == null ? void 0 : lastSegment.bold) ? "bold" : "normal");
44500
+ doc.text(currentLine, currentX, yPosition);
44501
+ yPosition += 5;
44502
+ }
44503
+ });
44504
+ };
44505
+ doc.setFontSize(18);
44506
+ doc.setFont("helvetica", "bold");
44507
+ doc.setTextColor(WISENBAKER_PRIMARY);
44508
+ doc.text("Wisenbaker AI Analytics", margin, yPosition);
44509
+ yPosition += 8;
44510
+ doc.setFontSize(11);
44511
+ doc.setFont("helvetica", "normal");
44512
+ doc.setTextColor(WISENBAKER_GRAY);
44513
+ doc.text("Chat Export", margin, yPosition);
44514
+ yPosition += 12;
44515
+ doc.setFontSize(9);
44516
+ doc.setTextColor("#666666");
44517
+ if (dashboardName) {
44518
+ doc.setFont("helvetica", "bold");
44519
+ doc.text("Dashboard:", margin, yPosition);
44520
+ doc.setFont("helvetica", "normal");
44521
+ doc.text(dashboardName, margin + 38, yPosition);
44522
+ yPosition += 5;
44523
+ }
44524
+ doc.setFont("helvetica", "bold");
44525
+ doc.text("Exported:", margin, yPosition);
44526
+ doc.setFont("helvetica", "normal");
44527
+ doc.text((/* @__PURE__ */ new Date()).toLocaleString(), margin + 38, yPosition);
44528
+ doc.setFont("helvetica", "bold");
44529
+ doc.text("Messages:", margin + 100, yPosition);
44530
+ doc.setFont("helvetica", "normal");
44531
+ doc.text(String(messages.length), margin + 130, yPosition);
44532
+ yPosition += 12;
44533
+ doc.setDrawColor(200, 200, 200);
44534
+ doc.setLineWidth(0.5);
44535
+ doc.line(margin, yPosition, pageWidth - margin, yPosition);
44536
+ yPosition += 8;
44537
+ messages.forEach((message, index) => {
44538
+ checkPageBreak(30);
44539
+ const isUser = message.role === "user";
44540
+ const roleLabel = isUser ? "You" : "AI Assistant";
44541
+ const timestamp = message.createdAt ? new Date(message.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
44542
+ const bgColor = isUser ? [143, 0, 45] : [241, 243, 245];
44543
+ doc.setFillColor(bgColor[0], bgColor[1], bgColor[2]);
44544
+ doc.setFontSize(9);
44545
+ doc.setFont("helvetica", "bold");
44546
+ doc.setTextColor(isUser ? WISENBAKER_PRIMARY : WISENBAKER_GRAY);
44547
+ doc.text(roleLabel, margin, yPosition);
44548
+ if (timestamp) {
44549
+ doc.setFont("helvetica", "normal");
44550
+ doc.setTextColor("#888888");
44551
+ doc.text(timestamp, margin + doc.getTextWidth(roleLabel) + 5, yPosition);
44552
+ }
44553
+ yPosition += 6;
44554
+ doc.setFontSize(10);
44555
+ doc.setTextColor("#333333");
44556
+ doc.setFont("helvetica", "normal");
44557
+ renderFormattedText(message.content, margin + 5, maxWidth - 5, 0);
44558
+ yPosition += 8;
44559
+ if (index < messages.length - 1) {
44560
+ doc.setDrawColor(230, 230, 230);
44561
+ doc.setLineWidth(0.3);
44562
+ doc.line(margin + 10, yPosition - 4, pageWidth - margin - 10, yPosition - 4);
44563
+ }
44564
+ });
44565
+ const totalPages = doc.getNumberOfPages();
44566
+ for (let i = 1; i <= totalPages; i++) {
44567
+ doc.setPage(i);
44568
+ doc.setFontSize(8);
44569
+ doc.setTextColor("#999999");
44570
+ doc.setFont("helvetica", "normal");
44571
+ doc.text(
44572
+ `Wisenbaker AI Analytics \u2022 Page ${i} of ${totalPages}`,
44573
+ pageWidth / 2,
44574
+ pageHeight - 10,
44575
+ { align: "center" }
44576
+ );
44577
+ }
44578
+ const filename = generateExportFilename(widgetId);
44579
+ doc.save(filename);
44580
+ }
44581
+
44412
44582
  var __defProp$b = Object.defineProperty;
44413
44583
  var __defProps$9 = Object.defineProperties;
44414
44584
  var __getOwnPropDescs$9 = Object.getOwnPropertyDescriptors;
@@ -44457,7 +44627,8 @@ function CopilotKitChatbot({
44457
44627
  styles,
44458
44628
  onResetReady,
44459
44629
  widgetIds,
44460
- datasetId
44630
+ datasetId,
44631
+ dashboardName
44461
44632
  }) {
44462
44633
  var _a, _b, _c, _d;
44463
44634
  const { threadId, setThreadId } = useCopilotContext();
@@ -44474,7 +44645,24 @@ function CopilotKitChatbot({
44474
44645
  const { reset, visibleMessages, appendMessage, isLoading } = useCopilotChat();
44475
44646
  const [inputValue, setInputValue] = useState("");
44476
44647
  const [chatMessages, setChatMessages] = useState([]);
44648
+ const [isExporting, setIsExporting] = useState(false);
44477
44649
  const messagesEndRef = React__default.useRef(null);
44650
+ const handleExport = async () => {
44651
+ if (chatMessages.length === 0) return;
44652
+ setIsExporting(true);
44653
+ try {
44654
+ exportChatToPDF({
44655
+ widgetId: widget.id,
44656
+ widgetTitle: widget.title,
44657
+ messages: chatMessages,
44658
+ dashboardName
44659
+ });
44660
+ } catch (error) {
44661
+ console.error("Export failed:", error);
44662
+ } finally {
44663
+ setIsExporting(false);
44664
+ }
44665
+ };
44478
44666
  const scrollToBottom = () => {
44479
44667
  var _a2;
44480
44668
  const el = messagesEndRef.current;
@@ -44549,6 +44737,25 @@ function CopilotKitChatbot({
44549
44737
  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium", children: widget.title })
44550
44738
  ] }) }),
44551
44739
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 h-full flex flex-col", children: [
44740
+ chatMessages.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-start px-4 py-2 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
44741
+ "button",
44742
+ {
44743
+ onClick: handleExport,
44744
+ disabled: isExporting,
44745
+ className: cn(
44746
+ "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md",
44747
+ "bg-gray-100 border border-gray-200",
44748
+ "text-gray-700 hover:bg-gray-200 hover:border-gray-300",
44749
+ "disabled:opacity-50 disabled:cursor-not-allowed",
44750
+ "transition-colors duration-150"
44751
+ ),
44752
+ title: "Export conversation as PDF",
44753
+ children: [
44754
+ isExporting ? /* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "h-3.5 w-3.5" }),
44755
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export PDF" })
44756
+ ]
44757
+ }
44758
+ ) }),
44552
44759
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: chatMessages.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center text-gray-500 text-xs mt-8", children: ((_b = widget.config) == null ? void 0 : _b.copilotInitialMessage) || ((_c = widget.config) == null ? void 0 : _c.placeholder) || "How can I help you today?" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
44553
44760
  chatMessages.map((message) => /* @__PURE__ */ jsxRuntimeExports.jsx(
44554
44761
  "div",
@@ -44624,7 +44831,8 @@ function ChatbotWidget({
44624
44831
  widgetBackendUrl,
44625
44832
  onResetReady,
44626
44833
  widgetIds,
44627
- datasetId
44834
+ datasetId,
44835
+ dashboardName
44628
44836
  }) {
44629
44837
  var _a, _b;
44630
44838
  const styles = getStyleValues$4((_a = widget.config) == null ? void 0 : _a.styles);
@@ -44648,7 +44856,8 @@ function ChatbotWidget({
44648
44856
  styles,
44649
44857
  onResetReady,
44650
44858
  widgetIds,
44651
- datasetId
44859
+ datasetId,
44860
+ dashboardName
44652
44861
  }
44653
44862
  )
44654
44863
  }
@@ -47165,7 +47374,8 @@ function WidgetRenderer({
47165
47374
  datasetId,
47166
47375
  pageId,
47167
47376
  onApplyFilters,
47168
- isEditing = false
47377
+ isEditing = false,
47378
+ dashboardName
47169
47379
  }) {
47170
47380
  const handleConfigUpdate = (config) => {
47171
47381
  if (onConfigUpdate) {
@@ -47197,7 +47407,7 @@ function WidgetRenderer({
47197
47407
  }
47198
47408
  );
47199
47409
  case "chatbot":
47200
- return /* @__PURE__ */ jsxRuntimeExports.jsx(ChatbotWidget, { widget, showHeader: false, widgetBackendUrl, onResetReady, widgetIds, datasetId });
47410
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ChatbotWidget, { widget, showHeader: false, widgetBackendUrl, onResetReady, widgetIds, datasetId, dashboardName });
47201
47411
  case "filters":
47202
47412
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
47203
47413
  FiltersWidget,
@@ -48806,7 +49016,7 @@ function WidgetDashboard({
48806
49016
  containerPadding: [0, 0],
48807
49017
  margin: [16, 16],
48808
49018
  children: displayWidgets.map((w) => {
48809
- var _a, _b;
49019
+ var _a, _b, _c;
48810
49020
  const filterStatus = w.type === "agent" ? getWidgetFilterStatus(w.id) : null;
48811
49021
  const badgeInfo = filterStatus ? getFilterStatusBadge(filterStatus.status) : null;
48812
49022
  const isFocusMode = focusWidgetId && w.id === focusWidgetId;
@@ -48845,7 +49055,7 @@ function WidgetDashboard({
48845
49055
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: () => handleClearChat(w == null ? void 0 : w.id), onMouseOver: () => setVisibleClearButton(w == null ? void 0 : w.id), onMouseLeave: () => setVisibleClearButton(""), className: "absolute top-[12px] right-0 z-40 flex align-middle justify-center gap-2 text-sm px-4 py-2 border-primary-300 rounded-l-sm w-fit bg-primary-700 text-white cursor-pointer shadow-md transition-all", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircleX, { className: "w-5 h-5" }) }),
48846
49056
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `absolute top-[56px] right-[16px] z-50 w-max py-1 text-xs px-2 rounded-sm text-white bg-gray-950 ${visibleClearButton === (w == null ? void 0 : w.id) ? "block" : "hidden"}`, children: "Clear Chat" })
48847
49057
  ] }),
48848
- /* @__PURE__ */ jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter((widget) => widget.type !== "chatbot").map((widget) => widget.id), datasetId, pageId, onApplyFilters, isEditing })
49058
+ /* @__PURE__ */ jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter((widget) => widget.type !== "chatbot").map((widget) => widget.id), datasetId, pageId, onApplyFilters, isEditing, dashboardName: ((_c = pageData == null ? void 0 : pageData.basic) == null ? void 0 : _c.title) || (pageData == null ? void 0 : pageData.name) || (pageData == null ? void 0 : pageData.title) })
48849
49059
  ] })
48850
49060
  ] }, w.id);
48851
49061
  })
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ var uuid = require('uuid');
18
18
  var ReactDOM = require('react-dom');
19
19
  var runtimeClientGql = require('@copilotkit/runtime-client-gql');
20
20
  var reactCore = require('@copilotkit/react-core');
21
+ var jsPDF = require('jspdf');
21
22
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
22
23
  var DialogPrimitive = require('@radix-ui/react-dialog');
23
24
  var dateFns = require('date-fns');
@@ -44437,6 +44438,175 @@ function SpacerWidget({ widget }) {
44437
44438
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full", style: styles });
44438
44439
  }
44439
44440
 
44441
+ const WISENBAKER_PRIMARY = "#8f002d";
44442
+ const WISENBAKER_GRAY = "#475569";
44443
+ function parseMarkdown(text) {
44444
+ const segments = [];
44445
+ const regex = /\*\*(.+?)\*\*/g;
44446
+ let lastIndex = 0;
44447
+ let match;
44448
+ while ((match = regex.exec(text)) !== null) {
44449
+ if (match.index > lastIndex) {
44450
+ segments.push({ text: text.slice(lastIndex, match.index), bold: false });
44451
+ }
44452
+ segments.push({ text: match[1], bold: true });
44453
+ lastIndex = regex.lastIndex;
44454
+ }
44455
+ if (lastIndex < text.length) {
44456
+ segments.push({ text: text.slice(lastIndex), bold: false });
44457
+ }
44458
+ return segments.length > 0 ? segments : [{ text, bold: false }];
44459
+ }
44460
+ function formatLine(line) {
44461
+ const bulletMatch = line.match(/^(\s*)[\*\-•]\s+(.*)$/);
44462
+ if (bulletMatch) {
44463
+ const indent = bulletMatch[1].length;
44464
+ return { text: bulletMatch[2], isBullet: true, indent };
44465
+ }
44466
+ const numberedMatch = line.match(/^(\s*)\d+\.\s+(.*)$/);
44467
+ if (numberedMatch) {
44468
+ const indent = numberedMatch[1].length;
44469
+ return { text: numberedMatch[2], isBullet: true, indent };
44470
+ }
44471
+ return { text: line, isBullet: false, indent: 0 };
44472
+ }
44473
+ function generateExportFilename(widgetId) {
44474
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
44475
+ return `NSight_Chat_Export_${widgetId}_${date}.pdf`;
44476
+ }
44477
+ function exportChatToPDF(options) {
44478
+ const { widgetId, messages, dashboardName } = options;
44479
+ const doc = new jsPDF();
44480
+ let yPosition = 25;
44481
+ const pageHeight = doc.internal.pageSize.height;
44482
+ const pageWidth = doc.internal.pageSize.width;
44483
+ const margin = 20;
44484
+ const contentWidth = pageWidth - margin * 2;
44485
+ const maxWidth = contentWidth - 10;
44486
+ const checkPageBreak = (requiredSpace = 20) => {
44487
+ if (yPosition > pageHeight - requiredSpace) {
44488
+ doc.addPage();
44489
+ yPosition = 25;
44490
+ }
44491
+ };
44492
+ const renderFormattedText = (text, x, maxW, baseIndent = 0) => {
44493
+ const lines = text.split("\n");
44494
+ lines.forEach((line) => {
44495
+ const { text: lineText, isBullet, indent } = formatLine(line);
44496
+ const lineIndent = x + baseIndent + indent * 2;
44497
+ if (isBullet) {
44498
+ checkPageBreak(10);
44499
+ doc.setFont("helvetica", "normal");
44500
+ doc.text("\u2022", lineIndent, yPosition);
44501
+ }
44502
+ const textX = isBullet ? lineIndent + 5 : lineIndent;
44503
+ const textMaxWidth = maxW - (textX - x);
44504
+ const segments = parseMarkdown(lineText);
44505
+ let currentLine = "";
44506
+ let currentX = textX;
44507
+ segments.forEach((segment) => {
44508
+ doc.setFont("helvetica", segment.bold ? "bold" : "normal");
44509
+ const words = segment.text.split(" ");
44510
+ words.forEach((word, wordIndex) => {
44511
+ const testLine = currentLine + (currentLine ? " " : "") + word;
44512
+ const testWidth = doc.getTextWidth(testLine);
44513
+ if (testWidth > textMaxWidth && currentLine) {
44514
+ checkPageBreak(10);
44515
+ doc.text(currentLine, currentX, yPosition);
44516
+ yPosition += 5;
44517
+ currentLine = word;
44518
+ currentX = isBullet ? lineIndent + 5 : x + baseIndent;
44519
+ } else {
44520
+ currentLine = testLine;
44521
+ }
44522
+ });
44523
+ });
44524
+ if (currentLine) {
44525
+ checkPageBreak(10);
44526
+ const lastSegment = segments[segments.length - 1];
44527
+ doc.setFont("helvetica", (lastSegment == null ? void 0 : lastSegment.bold) ? "bold" : "normal");
44528
+ doc.text(currentLine, currentX, yPosition);
44529
+ yPosition += 5;
44530
+ }
44531
+ });
44532
+ };
44533
+ doc.setFontSize(18);
44534
+ doc.setFont("helvetica", "bold");
44535
+ doc.setTextColor(WISENBAKER_PRIMARY);
44536
+ doc.text("Wisenbaker AI Analytics", margin, yPosition);
44537
+ yPosition += 8;
44538
+ doc.setFontSize(11);
44539
+ doc.setFont("helvetica", "normal");
44540
+ doc.setTextColor(WISENBAKER_GRAY);
44541
+ doc.text("Chat Export", margin, yPosition);
44542
+ yPosition += 12;
44543
+ doc.setFontSize(9);
44544
+ doc.setTextColor("#666666");
44545
+ if (dashboardName) {
44546
+ doc.setFont("helvetica", "bold");
44547
+ doc.text("Dashboard:", margin, yPosition);
44548
+ doc.setFont("helvetica", "normal");
44549
+ doc.text(dashboardName, margin + 38, yPosition);
44550
+ yPosition += 5;
44551
+ }
44552
+ doc.setFont("helvetica", "bold");
44553
+ doc.text("Exported:", margin, yPosition);
44554
+ doc.setFont("helvetica", "normal");
44555
+ doc.text((/* @__PURE__ */ new Date()).toLocaleString(), margin + 38, yPosition);
44556
+ doc.setFont("helvetica", "bold");
44557
+ doc.text("Messages:", margin + 100, yPosition);
44558
+ doc.setFont("helvetica", "normal");
44559
+ doc.text(String(messages.length), margin + 130, yPosition);
44560
+ yPosition += 12;
44561
+ doc.setDrawColor(200, 200, 200);
44562
+ doc.setLineWidth(0.5);
44563
+ doc.line(margin, yPosition, pageWidth - margin, yPosition);
44564
+ yPosition += 8;
44565
+ messages.forEach((message, index) => {
44566
+ checkPageBreak(30);
44567
+ const isUser = message.role === "user";
44568
+ const roleLabel = isUser ? "You" : "AI Assistant";
44569
+ const timestamp = message.createdAt ? new Date(message.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
44570
+ const bgColor = isUser ? [143, 0, 45] : [241, 243, 245];
44571
+ doc.setFillColor(bgColor[0], bgColor[1], bgColor[2]);
44572
+ doc.setFontSize(9);
44573
+ doc.setFont("helvetica", "bold");
44574
+ doc.setTextColor(isUser ? WISENBAKER_PRIMARY : WISENBAKER_GRAY);
44575
+ doc.text(roleLabel, margin, yPosition);
44576
+ if (timestamp) {
44577
+ doc.setFont("helvetica", "normal");
44578
+ doc.setTextColor("#888888");
44579
+ doc.text(timestamp, margin + doc.getTextWidth(roleLabel) + 5, yPosition);
44580
+ }
44581
+ yPosition += 6;
44582
+ doc.setFontSize(10);
44583
+ doc.setTextColor("#333333");
44584
+ doc.setFont("helvetica", "normal");
44585
+ renderFormattedText(message.content, margin + 5, maxWidth - 5, 0);
44586
+ yPosition += 8;
44587
+ if (index < messages.length - 1) {
44588
+ doc.setDrawColor(230, 230, 230);
44589
+ doc.setLineWidth(0.3);
44590
+ doc.line(margin + 10, yPosition - 4, pageWidth - margin - 10, yPosition - 4);
44591
+ }
44592
+ });
44593
+ const totalPages = doc.getNumberOfPages();
44594
+ for (let i = 1; i <= totalPages; i++) {
44595
+ doc.setPage(i);
44596
+ doc.setFontSize(8);
44597
+ doc.setTextColor("#999999");
44598
+ doc.setFont("helvetica", "normal");
44599
+ doc.text(
44600
+ `Wisenbaker AI Analytics \u2022 Page ${i} of ${totalPages}`,
44601
+ pageWidth / 2,
44602
+ pageHeight - 10,
44603
+ { align: "center" }
44604
+ );
44605
+ }
44606
+ const filename = generateExportFilename(widgetId);
44607
+ doc.save(filename);
44608
+ }
44609
+
44440
44610
  var __defProp$b = Object.defineProperty;
44441
44611
  var __defProps$9 = Object.defineProperties;
44442
44612
  var __getOwnPropDescs$9 = Object.getOwnPropertyDescriptors;
@@ -44485,7 +44655,8 @@ function CopilotKitChatbot({
44485
44655
  styles,
44486
44656
  onResetReady,
44487
44657
  widgetIds,
44488
- datasetId
44658
+ datasetId,
44659
+ dashboardName
44489
44660
  }) {
44490
44661
  var _a, _b, _c, _d;
44491
44662
  const { threadId, setThreadId } = reactCore.useCopilotContext();
@@ -44502,7 +44673,24 @@ function CopilotKitChatbot({
44502
44673
  const { reset, visibleMessages, appendMessage, isLoading } = reactCore.useCopilotChat();
44503
44674
  const [inputValue, setInputValue] = React.useState("");
44504
44675
  const [chatMessages, setChatMessages] = React.useState([]);
44676
+ const [isExporting, setIsExporting] = React.useState(false);
44505
44677
  const messagesEndRef = React.useRef(null);
44678
+ const handleExport = async () => {
44679
+ if (chatMessages.length === 0) return;
44680
+ setIsExporting(true);
44681
+ try {
44682
+ exportChatToPDF({
44683
+ widgetId: widget.id,
44684
+ widgetTitle: widget.title,
44685
+ messages: chatMessages,
44686
+ dashboardName
44687
+ });
44688
+ } catch (error) {
44689
+ console.error("Export failed:", error);
44690
+ } finally {
44691
+ setIsExporting(false);
44692
+ }
44693
+ };
44506
44694
  const scrollToBottom = () => {
44507
44695
  var _a2;
44508
44696
  const el = messagesEndRef.current;
@@ -44577,6 +44765,25 @@ function CopilotKitChatbot({
44577
44765
  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium", children: widget.title })
44578
44766
  ] }) }),
44579
44767
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 h-full flex flex-col", children: [
44768
+ chatMessages.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-start px-4 py-2 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
44769
+ "button",
44770
+ {
44771
+ onClick: handleExport,
44772
+ disabled: isExporting,
44773
+ className: cn(
44774
+ "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md",
44775
+ "bg-gray-100 border border-gray-200",
44776
+ "text-gray-700 hover:bg-gray-200 hover:border-gray-300",
44777
+ "disabled:opacity-50 disabled:cursor-not-allowed",
44778
+ "transition-colors duration-150"
44779
+ ),
44780
+ title: "Export conversation as PDF",
44781
+ children: [
44782
+ isExporting ? /* @__PURE__ */ jsxRuntimeExports.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(lucideReact.Download, { className: "h-3.5 w-3.5" }),
44783
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export PDF" })
44784
+ ]
44785
+ }
44786
+ ) }),
44580
44787
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: chatMessages.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center text-gray-500 text-xs mt-8", children: ((_b = widget.config) == null ? void 0 : _b.copilotInitialMessage) || ((_c = widget.config) == null ? void 0 : _c.placeholder) || "How can I help you today?" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
44581
44788
  chatMessages.map((message) => /* @__PURE__ */ jsxRuntimeExports.jsx(
44582
44789
  "div",
@@ -44652,7 +44859,8 @@ function ChatbotWidget({
44652
44859
  widgetBackendUrl,
44653
44860
  onResetReady,
44654
44861
  widgetIds,
44655
- datasetId
44862
+ datasetId,
44863
+ dashboardName
44656
44864
  }) {
44657
44865
  var _a, _b;
44658
44866
  const styles = getStyleValues$4((_a = widget.config) == null ? void 0 : _a.styles);
@@ -44676,7 +44884,8 @@ function ChatbotWidget({
44676
44884
  styles,
44677
44885
  onResetReady,
44678
44886
  widgetIds,
44679
- datasetId
44887
+ datasetId,
44888
+ dashboardName
44680
44889
  }
44681
44890
  )
44682
44891
  }
@@ -47193,7 +47402,8 @@ function WidgetRenderer({
47193
47402
  datasetId,
47194
47403
  pageId,
47195
47404
  onApplyFilters,
47196
- isEditing = false
47405
+ isEditing = false,
47406
+ dashboardName
47197
47407
  }) {
47198
47408
  const handleConfigUpdate = (config) => {
47199
47409
  if (onConfigUpdate) {
@@ -47225,7 +47435,7 @@ function WidgetRenderer({
47225
47435
  }
47226
47436
  );
47227
47437
  case "chatbot":
47228
- return /* @__PURE__ */ jsxRuntimeExports.jsx(ChatbotWidget, { widget, showHeader: false, widgetBackendUrl, onResetReady, widgetIds, datasetId });
47438
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ChatbotWidget, { widget, showHeader: false, widgetBackendUrl, onResetReady, widgetIds, datasetId, dashboardName });
47229
47439
  case "filters":
47230
47440
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
47231
47441
  FiltersWidget,
@@ -48834,7 +49044,7 @@ function WidgetDashboard({
48834
49044
  containerPadding: [0, 0],
48835
49045
  margin: [16, 16],
48836
49046
  children: displayWidgets.map((w) => {
48837
- var _a, _b;
49047
+ var _a, _b, _c;
48838
49048
  const filterStatus = w.type === "agent" ? getWidgetFilterStatus(w.id) : null;
48839
49049
  const badgeInfo = filterStatus ? getFilterStatusBadge(filterStatus.status) : null;
48840
49050
  const isFocusMode = focusWidgetId && w.id === focusWidgetId;
@@ -48873,7 +49083,7 @@ function WidgetDashboard({
48873
49083
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: () => handleClearChat(w == null ? void 0 : w.id), onMouseOver: () => setVisibleClearButton(w == null ? void 0 : w.id), onMouseLeave: () => setVisibleClearButton(""), className: "absolute top-[12px] right-0 z-40 flex align-middle justify-center gap-2 text-sm px-4 py-2 border-primary-300 rounded-l-sm w-fit bg-primary-700 text-white cursor-pointer shadow-md transition-all", children: /* @__PURE__ */ jsxRuntimeExports.jsx(lucideReact.MessageCircleX, { className: "w-5 h-5" }) }),
48874
49084
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `absolute top-[56px] right-[16px] z-50 w-max py-1 text-xs px-2 rounded-sm text-white bg-gray-950 ${visibleClearButton === (w == null ? void 0 : w.id) ? "block" : "hidden"}`, children: "Clear Chat" })
48875
49085
  ] }),
48876
- /* @__PURE__ */ jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter((widget) => widget.type !== "chatbot").map((widget) => widget.id), datasetId, pageId, onApplyFilters, isEditing })
49086
+ /* @__PURE__ */ jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter((widget) => widget.type !== "chatbot").map((widget) => widget.id), datasetId, pageId, onApplyFilters, isEditing, dashboardName: ((_c = pageData == null ? void 0 : pageData.basic) == null ? void 0 : _c.title) || (pageData == null ? void 0 : pageData.name) || (pageData == null ? void 0 : pageData.title) })
48877
49087
  ] })
48878
49088
  ] }, w.id);
48879
49089
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dp-widgets-framework",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org"
@@ -90,6 +90,7 @@
90
90
  "express": "^4.21.2",
91
91
  "express-validator": "^7.2.1",
92
92
  "framer-motion": "^12.0.6",
93
+ "jspdf": "^2.5.2",
93
94
  "lodash": "^4.17.21",
94
95
  "lucide-react": "^0.474.0",
95
96
  "next": "14.0.4",