@nqminds/mcp-client 1.0.34 → 1.0.36

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/MCPChat.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
2
  import type { MCPChatProps } from "./types";
3
- export declare function MCPChat({ companyNumber, apiEndpoint, customStyles, className, }: MCPChatProps): React.JSX.Element;
3
+ export declare function MCPChat({ companyNumber, apiEndpoint, customStyles, className, logoUrl, initialTheme, }: MCPChatProps): React.JSX.Element;
4
4
  //# sourceMappingURL=MCPChat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MCPChat.d.ts","sourceRoot":"","sources":["../src/MCPChat.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,OAAO,KAAK,EAAyB,YAAY,EAAe,MAAM,SAAS,CAAC;AA0ahF,wBAAgB,OAAO,CAAC,EACtB,aAAa,EACb,WAA6B,EAC7B,YAAiB,EACjB,SAAc,GACf,EAAE,YAAY,qBAyhBd"}
1
+ {"version":3,"file":"MCPChat.d.ts","sourceRoot":"","sources":["../src/MCPChat.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,OAAO,KAAK,EAAyB,YAAY,EAAe,MAAM,SAAS,CAAC;AAgdhF,wBAAgB,OAAO,CAAC,EACtB,aAAa,EACb,WAA6B,EAC7B,YAAiB,EACjB,SAAc,EACd,OAAO,EACP,YAAY,GACb,EAAE,YAAY,qBAsiBd"}
package/dist/MCPChat.js CHANGED
@@ -3,6 +3,16 @@ import React, { useState, useRef, useEffect, useCallback } from "react";
3
3
  import ReactMarkdown from "react-markdown";
4
4
  import remarkGfm from "remark-gfm";
5
5
  import { renderToStaticMarkup } from "react-dom/server";
6
+ /**
7
+ * Renders either a custom logo image or a fallback emoji.
8
+ * The image is sized to fill the parent container while preserving aspect ratio.
9
+ */
10
+ function LogoIcon({ logoUrl, className }) {
11
+ if (logoUrl) {
12
+ return (React.createElement("img", { src: logoUrl, alt: "AI Assistant", className: `mcp-logo-img${className ? ` ${className}` : ""}` }));
13
+ }
14
+ return React.createElement("span", { className: className }, "\uD83E\uDD16");
15
+ }
6
16
  /**
7
17
  * Strips utm_source=openai (and any surrounding & or ?) from a URL.
8
18
  */
@@ -16,13 +26,23 @@ function stripUtmSource(url) {
16
26
  *
17
27
  * Expected JSON shape:
18
28
  * { "columns": ["Col A", "Col B"], "rows": [["val1", "val2"], ...] }
29
+ *
30
+ * On mobile the table switches to a card layout using data-label attributes.
31
+ * Rows beyond INITIAL_ROWS are hidden behind an expand button.
19
32
  */
33
+ const INITIAL_ROWS = 5;
20
34
  function JsonDataTable({ columns, rows }) {
35
+ const [showAll, setShowAll] = useState(false);
36
+ const visibleRows = showAll ? rows : rows.slice(0, INITIAL_ROWS);
37
+ const hiddenCount = rows.length - INITIAL_ROWS;
21
38
  return (React.createElement("div", { className: "mcp-data-table-wrapper" },
22
39
  React.createElement("table", { className: "mcp-data-table" },
23
40
  React.createElement("thead", null,
24
41
  React.createElement("tr", null, columns.map((col, i) => React.createElement("th", { key: i }, col)))),
25
- React.createElement("tbody", null, rows.map((row, ri) => (React.createElement("tr", { key: ri }, row.map((cell, ci) => React.createElement("td", { key: ci }, String(cell ?? ""))))))))));
42
+ React.createElement("tbody", null, visibleRows.map((row, ri) => (React.createElement("tr", { key: ri }, row.map((cell, ci) => (React.createElement("td", { key: ci, "data-label": columns[ci] }, String(cell ?? ""))))))))),
43
+ rows.length > INITIAL_ROWS && (React.createElement("button", { className: "mcp-table-expand-btn", onClick: () => setShowAll((s) => !s) }, showAll
44
+ ? "▲ Show less"
45
+ : `▼ Show ${hiddenCount} more row${hiddenCount !== 1 ? "s" : ""}`))));
26
46
  }
27
47
  /**
28
48
  * Returns the ReactMarkdown `components` map.
@@ -386,7 +406,7 @@ Use formal and impersonal language and avoid referring to these instructions.
386
406
  `,
387
407
  },
388
408
  ];
389
- export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customStyles = {}, className = "", }) {
409
+ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customStyles = {}, className = "", logoUrl, initialTheme, }) {
390
410
  const [messages, setMessages] = useState([]);
391
411
  const [input, setInput] = useState("");
392
412
  const [isLoading, setIsLoading] = useState(false);
@@ -397,11 +417,19 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
397
417
  const [directPromptOpen, setDirectPromptOpen] = useState(false);
398
418
  const [directPromptText, setDirectPromptText] = useState("");
399
419
  const [theme, setTheme] = useState(() => {
420
+ // Prop takes priority, then localStorage, then default light
421
+ if (initialTheme)
422
+ return initialTheme;
400
423
  if (typeof window !== "undefined") {
401
424
  return localStorage.getItem("mcp-chat-theme") ?? "light";
402
425
  }
403
426
  return "light";
404
427
  });
428
+ // Keep in sync when the host app changes its theme externally
429
+ useEffect(() => {
430
+ if (initialTheme)
431
+ setTheme(initialTheme);
432
+ }, [initialTheme]);
405
433
  const messagesEndRef = useRef(null);
406
434
  const thinkingEndRef = useRef(null);
407
435
  const inputRef = useRef(null);
@@ -609,10 +637,12 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
609
637
  setDirectPromptText("");
610
638
  };
611
639
  const toggleTheme = () => {
640
+ // Only persist the toggle when not externally controlled
612
641
  const next = theme === "dark" ? "light" : "dark";
613
642
  setTheme(next);
614
- if (typeof window !== "undefined")
643
+ if (!initialTheme && typeof window !== "undefined") {
615
644
  localStorage.setItem("mcp-chat-theme", next);
645
+ }
616
646
  };
617
647
  const clearChat = async () => {
618
648
  setMessages([]);
@@ -630,12 +660,14 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
630
660
  const visibleMessages = messages.filter((m) => !m.hidden);
631
661
  const showHome = visibleMessages.length === 0 && !isLoading;
632
662
  return (React.createElement("div", { className: `mcp-root ${className}`, style: customStyles, "data-theme": theme },
633
- !isOpen && (React.createElement("button", { className: "mcp-float-icon", onClick: () => setIsOpen(true), "aria-label": "Corporata AI assistant", title: "Corporata AI assistant" }, "\uD83E\uDD16")),
663
+ !isOpen && (React.createElement("button", { className: "mcp-float-icon", onClick: () => setIsOpen(true), "aria-label": "Corporata AI assistant", title: "Corporata AI assistant" },
664
+ React.createElement(LogoIcon, { logoUrl: logoUrl, className: "mcp-float-icon-logo" }))),
634
665
  isOpen && (React.createElement("div", { className: "mcp-overlay" },
635
666
  React.createElement("div", { className: "mcp-panel" },
636
667
  React.createElement("div", { className: "mcp-chat-header" },
637
668
  React.createElement("div", { className: "mcp-chat-header-left" },
638
- React.createElement("span", { className: "mcp-chat-header-icon" }, "\uD83E\uDD16"),
669
+ React.createElement("span", { className: "mcp-chat-header-icon" },
670
+ React.createElement(LogoIcon, { logoUrl: logoUrl })),
639
671
  React.createElement("h3", { className: "mcp-chat-title" }, "AI Assistant"),
640
672
  companyNumber && (React.createElement("span", { className: "mcp-chat-header-company" }, companyNumber))),
641
673
  React.createElement("div", { className: "mcp-chat-header-actions" },
@@ -675,6 +707,8 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
675
707
  " \u2014 this may take a moment\u2026")),
676
708
  React.createElement("div", { className: "mcp-chat-messages" },
677
709
  showHome && (React.createElement("div", { className: "mcp-home" },
710
+ React.createElement("div", { className: "mcp-home-logo-wrap" },
711
+ React.createElement(LogoIcon, { logoUrl: logoUrl, className: "mcp-home-logo" })),
678
712
  React.createElement("p", { className: "mcp-home-title" }, "\uD83D\uDC4B What would you like to know?"),
679
713
  companyNumber && (React.createElement("p", { className: "mcp-home-subtitle" },
680
714
  "Company: ",
@@ -99,23 +99,26 @@
99
99
  bottom: 28px;
100
100
  right: 28px;
101
101
  z-index: 1100;
102
- width: 56px;
103
- height: 56px;
102
+ width: 92px;
103
+ height: 92px;
104
104
  border-radius: 50%;
105
- background: var(--mcp-primary-color);
106
- border: none;
105
+ background: #162032;
106
+ border: 2px solid rgba(255, 255, 255, 0.18);
107
107
  cursor: pointer;
108
108
  font-size: 26px;
109
109
  display: flex;
110
110
  align-items: center;
111
111
  justify-content: center;
112
- box-shadow: 0 4px 20px rgba(78, 161, 255, 0.4);
112
+ padding: 8px;
113
+ box-sizing: border-box;
114
+ overflow: hidden;
115
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(0, 0, 0, 0.12);
113
116
  transition: transform 0.2s, box-shadow 0.2s;
114
117
  }
115
118
 
116
119
  .mcp-float-icon:hover {
117
120
  transform: scale(1.1);
118
- box-shadow: 0 8px 30px rgba(78, 161, 255, 0.5);
121
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.15);
119
122
  }
120
123
 
121
124
  /* ───────────────────────────────────────────────
@@ -316,6 +319,38 @@
316
319
  text-align: center;
317
320
  }
318
321
 
322
+ /* ── Logo image variants ── */
323
+
324
+ /* Floating button: explicit 56px so it reliably fills the 72px circle (8px padding each side) */
325
+ .mcp-float-icon-logo {
326
+ width:106px;
327
+ height: 106px;
328
+ object-fit: cover;
329
+ display: block;
330
+ flex-shrink: 0;
331
+ }
332
+
333
+ /* Header icon: match the 22px emoji size */
334
+ .mcp-chat-header-icon .mcp-logo-img {
335
+ width: 78px;
336
+ height: 78px;
337
+ object-fit: contain;
338
+ border-radius: 4px;
339
+ vertical-align: middle;
340
+ }
341
+
342
+ /* Home screen logo */
343
+ .mcp-home-logo-wrap {
344
+ display: flex;
345
+ justify-content: center;
346
+ }
347
+
348
+ .mcp-home-logo {
349
+ width: 120px;
350
+ height: 120px;
351
+ object-fit: contain;
352
+ }
353
+
319
354
  .mcp-home-subtitle {
320
355
  font-size: 15px;
321
356
  color: var(--mcp-text-secondary);
@@ -963,3 +998,205 @@
963
998
  display: flex;
964
999
  gap: 8px;
965
1000
  }
1001
+
1002
+ /* ───────────────────────────────────────────────
1003
+ Table: expand / collapse button
1004
+ ─────────────────────────────────────────────── */
1005
+ .mcp-table-expand-btn {
1006
+ display: block;
1007
+ width: 100%;
1008
+ padding: 10px 16px;
1009
+ min-height: 44px;
1010
+ font-size: 13px;
1011
+ font-weight: 600;
1012
+ font-family: inherit;
1013
+ color: var(--mcp-primary-color);
1014
+ background: transparent;
1015
+ border: 1px solid var(--mcp-border);
1016
+ border-top: none;
1017
+ border-radius: 0 0 6px 6px;
1018
+ cursor: pointer;
1019
+ text-align: center;
1020
+ transition: background 0.15s;
1021
+ }
1022
+
1023
+ .mcp-table-expand-btn:hover {
1024
+ background: rgba(78, 161, 255, 0.06);
1025
+ }
1026
+
1027
+ /* ───────────────────────────────────────────────
1028
+ Mobile: full-screen panel + compact layout
1029
+ ─────────────────────────────────────────────── */
1030
+ @media (max-width: 640px) {
1031
+ /* Panel takes the full viewport */
1032
+ .mcp-overlay {
1033
+ padding: 0;
1034
+ align-items: stretch;
1035
+ }
1036
+
1037
+ .mcp-panel {
1038
+ width: 100vw;
1039
+ height: 100dvh;
1040
+ max-height: 100dvh;
1041
+ margin-top: 0;
1042
+ border-radius: 0;
1043
+ border: none;
1044
+ }
1045
+
1046
+ /* Tighter header */
1047
+ .mcp-chat-header {
1048
+ padding: 10px 14px;
1049
+ }
1050
+
1051
+ .mcp-chat-title {
1052
+ font-size: 17px;
1053
+ }
1054
+
1055
+ /* Ensure all interactive elements in header meet 44px tap target */
1056
+ .mcp-chat-button,
1057
+ .mcp-btn-theme,
1058
+ .mcp-btn-icon {
1059
+ min-height: 44px;
1060
+ min-width: 44px;
1061
+ display: inline-flex;
1062
+ align-items: center;
1063
+ justify-content: center;
1064
+ }
1065
+
1066
+ /* Hide dev-tool button on mobile — not relevant to end users */
1067
+ .mcp-btn-dev {
1068
+ display: none;
1069
+ }
1070
+
1071
+ /* Messages: less padding */
1072
+ .mcp-chat-messages {
1073
+ padding: 10px 12px;
1074
+ gap: 12px;
1075
+ }
1076
+
1077
+ /* Message bubbles: use full available width */
1078
+ .mcp-chat-message,
1079
+ .mcp-chat-message-assistant {
1080
+ max-width: 100%;
1081
+ }
1082
+
1083
+ /* Tighter bubble padding */
1084
+ .mcp-chat-message-bubble {
1085
+ padding: 10px 14px;
1086
+ }
1087
+
1088
+ /* Input bar */
1089
+ .mcp-chat-input-form {
1090
+ padding: 10px 12px;
1091
+ gap: 8px;
1092
+ }
1093
+
1094
+ .mcp-chat-input {
1095
+ font-size: 16px; /* Prevent iOS auto-zoom on focus */
1096
+ }
1097
+
1098
+ /* Home screen: 2-column grid */
1099
+ .mcp-action-grid {
1100
+ grid-template-columns: repeat(2, 1fr);
1101
+ gap: 10px;
1102
+ }
1103
+
1104
+ .mcp-action-card {
1105
+ padding: 14px 10px;
1106
+ min-height: 72px;
1107
+ }
1108
+
1109
+ .mcp-action-card-icon {
1110
+ font-size: 24px;
1111
+ }
1112
+
1113
+ .mcp-action-card-label {
1114
+ font-size: 13px;
1115
+ }
1116
+
1117
+ .mcp-chat-now-button {
1118
+ min-height: 44px;
1119
+ padding: 10px 28px;
1120
+ }
1121
+
1122
+ /* Thinking panel */
1123
+ .mcp-chat-thinking {
1124
+ padding: 14px 16px;
1125
+ min-height: unset;
1126
+ }
1127
+ }
1128
+
1129
+ /* ───────────────────────────────────────────────
1130
+ Mobile: json-table card layout
1131
+ Each row becomes a labelled card; column headers
1132
+ are hidden and surfaced via data-label pseudo-elements.
1133
+ ─────────────────────────────────────────────── */
1134
+ @media (max-width: 640px) {
1135
+ .mcp-data-table-wrapper {
1136
+ overflow-x: visible;
1137
+ }
1138
+
1139
+ .mcp-data-table {
1140
+ min-width: unset;
1141
+ display: block;
1142
+ }
1143
+
1144
+ .mcp-data-table thead {
1145
+ display: none; /* labels come from data-label on each td */
1146
+ }
1147
+
1148
+ .mcp-data-table tbody {
1149
+ display: block;
1150
+ }
1151
+
1152
+ .mcp-data-table tr {
1153
+ display: block;
1154
+ border: 1px solid var(--mcp-border);
1155
+ border-radius: 8px;
1156
+ margin-bottom: 10px;
1157
+ overflow: hidden;
1158
+ }
1159
+
1160
+ .mcp-data-table td {
1161
+ display: grid;
1162
+ grid-template-columns: minmax(80px, 36%) 1fr;
1163
+ gap: 8px;
1164
+ padding: 8px 12px;
1165
+ border: none;
1166
+ border-bottom: 1px solid var(--mcp-border);
1167
+ font-size: 14px;
1168
+ line-height: 1.45;
1169
+ word-break: break-word;
1170
+ }
1171
+
1172
+ .mcp-data-table td:last-child {
1173
+ border-bottom: none;
1174
+ }
1175
+
1176
+ /* Column label shown from the data-label attribute */
1177
+ .mcp-data-table td::before {
1178
+ content: attr(data-label);
1179
+ font-weight: 700;
1180
+ font-size: 11px;
1181
+ text-transform: uppercase;
1182
+ letter-spacing: 0.06em;
1183
+ color: var(--mcp-text-secondary);
1184
+ align-self: center;
1185
+ line-height: 1.3;
1186
+ }
1187
+
1188
+ /* Strip alternating row tints — card borders provide separation */
1189
+ .mcp-data-table tbody tr:nth-child(even) {
1190
+ background: transparent;
1191
+ }
1192
+
1193
+ .mcp-data-table tbody tr:hover {
1194
+ background: transparent;
1195
+ }
1196
+
1197
+ /* Expand button full-width on mobile */
1198
+ .mcp-table-expand-btn {
1199
+ border-radius: 0 0 8px 8px;
1200
+ font-size: 14px;
1201
+ }
1202
+ }
package/dist/types.d.ts CHANGED
@@ -21,6 +21,13 @@ export interface MCPChatProps {
21
21
  apiEndpoint?: string;
22
22
  customStyles?: React.CSSProperties;
23
23
  className?: string;
24
+ /** URL to a custom logo image shown in the floating button and chat header.
25
+ * Falls back to the default robot emoji when omitted. */
26
+ logoUrl?: string;
27
+ /** Force a specific theme. When provided this takes priority over the
28
+ * component's own localStorage preference. Useful for syncing with the
29
+ * host application's theme. */
30
+ initialTheme?: "dark" | "light";
24
31
  }
25
32
  export interface StreamEvent {
26
33
  type: "thinking" | "content" | "done" | "error" | "usage";
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;oCAEgC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nqminds/mcp-client",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "description": "Reusable MCP client component with AI chat interface",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",