@shuffleio/shuffle-mcps 0.0.3 → 0.0.4

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/README.md CHANGED
@@ -146,16 +146,32 @@ When `apiKey` is set, the component also fetches the user's **private apps** fro
146
146
  | Prop | Type | Description |
147
147
  |---|---|---|
148
148
  | `authToken` | `string` | Required. Forwarded into the auth URL. |
149
+ | `orgId` | `string` | Optional Shuffle organization ID. When set, every API call (`/api/v1/apps/authentication`, `/api/v1/apps`) is sent with an `Org-Id: <orgId>` header, and the auth URL gets `&org_id=<orgId>` appended. Default: not sent. |
149
150
  | `inline` | `boolean` | Inline results vs floating dropdown. |
150
151
  | `layout` | `'list' \| 'grid'` | Result layout. Default `'list'`. |
151
152
  | `initialFilterQuery` | `string` | Pre-filter without filling the input. |
152
- | `preventDefault` | `boolean` | Skip default `window.open(authUrl)` so you can handle selection. |
153
- | `onAppSelected` | `(detail) => void` | Fires on single-select pick. |
153
+ | `preventDefault` | `boolean` | Skip the default click behavior (built-in drawer or `window.open(authUrl)`) so you can handle selection yourself. |
154
+ | `onAppSelected` | `(detail) => void` | Fires on single-select pick. If provided, the built-in drawer is **not** opened — you take over. |
154
155
  | `multiSelect` | `boolean` | Allow selecting multiple apps. |
155
156
  | `apiKey` | `string` | Bearer token. Enables status dots and merges your private apps from `/api/v1/apps`. |
156
157
  | `showSourceFilter` | `boolean` | Toggle the All / Public / Private filter. Default `true`. |
157
158
  | `customStyles` | `CustomStyles` | Per-slot style overrides. |
158
159
 
160
+ ### Default click behavior
161
+
162
+ When you do **not** pass `onAppSelected`, clicking an app opens a built-in right-side drawer that shows the app, its existing authentication entries (with Configured / Tested / Inactive chips), and a CTA that opens the Shuffle auth flow in a new tab. Pass `onAppSelected` to take over, or `preventDefault` to disable both.
163
+
164
+ ### Org scoping
165
+
166
+ ```tsx
167
+ <ShuffleMCP
168
+ authToken={token}
169
+ apiKey={user.apiKey}
170
+ orgId={currentOrg.id} // every request now scoped to this org
171
+ />
172
+ ```
173
+
174
+
159
175
  Full prop reference, framework setup (Next.js, Vue), styling slots, custom rendering, and publishing: [**LIBRARY.md**](./LIBRARY.md).
160
176
 
161
177
  ## Imperative handle
package/dist/index.cjs CHANGED
@@ -37,13 +37,14 @@ module.exports = __toCommonJS(index_exports);
37
37
  // ShuffleMCP.tsx
38
38
  var import_react = __toESM(require("react"), 1);
39
39
  var import_algoliasearch = require("algoliasearch");
40
- var import_singul = require("./singul-GZKBHJNI.css");
40
+ var import_shuffle_mcp = require("./shuffle-mcp-XZEVQXW2.css");
41
41
  var import_jsx_runtime = require("react/jsx-runtime");
42
42
  var DEFAULT_ALGOLIA_APP_ID = "JNSS5CFDZZ";
43
43
  var DEFAULT_ALGOLIA_API_KEY = "33e4e3564f4f060e96e0531957bed552";
44
44
  var DEFAULT_ALGOLIA_INDEX = "appsearch";
45
45
  var ShuffleMCP = import_react.default.forwardRef(({
46
46
  authToken,
47
+ orgId,
47
48
  placeholder = "Search apps...",
48
49
  layout = "list",
49
50
  gridColumns = 3,
@@ -89,6 +90,7 @@ var ShuffleMCP = import_react.default.forwardRef(({
89
90
  const [selectedIndex, setSelectedIndex] = (0, import_react.useState)(-1);
90
91
  const [internalSelectedApps, setInternalSelectedApps] = (0, import_react.useState)(selectedApps);
91
92
  const [authenticatedApps, setAuthenticatedApps] = (0, import_react.useState)(externalAuthenticatedApps || []);
93
+ const [drawerApp, setDrawerApp] = (0, import_react.useState)(null);
92
94
  const hasInitialized = (0, import_react.useRef)(false);
93
95
  const containerRef = (0, import_react.useRef)(null);
94
96
  const inputRef = (0, import_react.useRef)(null);
@@ -100,7 +102,8 @@ var ShuffleMCP = import_react.default.forwardRef(({
100
102
  try {
101
103
  const response = await fetch(`${apiBaseUrl}${authPath}`, {
102
104
  headers: {
103
- "Authorization": `Bearer ${apiKey}`
105
+ "Authorization": `Bearer ${apiKey}`,
106
+ ...orgId ? { "Org-Id": orgId } : {}
104
107
  }
105
108
  });
106
109
  if (response.ok) {
@@ -117,7 +120,7 @@ var ShuffleMCP = import_react.default.forwardRef(({
117
120
  };
118
121
  fetchAuthenticatedApps();
119
122
  }
120
- }, [apiKey, apiBaseUrl]);
123
+ }, [apiKey, apiBaseUrl, orgId]);
121
124
  (0, import_react.useEffect)(() => {
122
125
  if (!apiKey || disablePrivateApps) {
123
126
  setPrivateApps([]);
@@ -126,7 +129,10 @@ var ShuffleMCP = import_react.default.forwardRef(({
126
129
  const fetchPrivateApps = async () => {
127
130
  try {
128
131
  const response = await fetch(`${apiBaseUrl}${privateAppsPath}`, {
129
- headers: { "Authorization": `Bearer ${apiKey}` },
132
+ headers: {
133
+ "Authorization": `Bearer ${apiKey}`,
134
+ ...orgId ? { "Org-Id": orgId } : {}
135
+ },
130
136
  credentials: "include"
131
137
  });
132
138
  if (!response.ok) return;
@@ -158,7 +164,7 @@ var ShuffleMCP = import_react.default.forwardRef(({
158
164
  }
159
165
  };
160
166
  fetchPrivateApps();
161
- }, [apiKey, apiBaseUrl, privateAppsPath, disablePrivateApps]);
167
+ }, [apiKey, apiBaseUrl, privateAppsPath, disablePrivateApps, orgId]);
162
168
  (0, import_react.useEffect)(() => {
163
169
  if (authenticatedApps.length > 0 && results.length > 0) {
164
170
  const validatedApps = authenticatedApps.filter((auth) => auth.validation?.valid);
@@ -277,22 +283,24 @@ var ShuffleMCP = import_react.default.forwardRef(({
277
283
  }, 300);
278
284
  }, [performSearch, onSearchChange]);
279
285
  const selectApp = (0, import_react.useCallback)((app) => {
280
- const authUrl = `${apiBaseUrl}${appAuthPath}?app_id=${app.objectID}&auth=${authToken}&source=shuffle`;
286
+ const authUrl = `${apiBaseUrl}${appAuthPath}?app_id=${app.objectID}&auth=${authToken}&source=shuffle${orgId ? `&org_id=${encodeURIComponent(orgId)}` : ""}`;
281
287
  if (multiSelect) {
282
288
  const isAlreadySelected = internalSelectedApps.some((a) => a.objectID === app.objectID);
283
289
  const newSelection = isAlreadySelected ? internalSelectedApps.filter((a) => a.objectID !== app.objectID) : [...internalSelectedApps, app];
284
290
  setInternalSelectedApps(newSelection);
285
291
  onSelectionChange?.(newSelection);
286
292
  } else {
287
- onAppSelected?.({ app, authUrl });
288
- if (!preventDefault) {
289
- window.open(authUrl, "_blank");
293
+ if (onAppSelected) {
294
+ onAppSelected({ app, authUrl });
295
+ if (!preventDefault) {
296
+ window.open(authUrl, "_blank");
297
+ }
298
+ } else if (!preventDefault) {
299
+ setDrawerApp(app);
290
300
  }
291
301
  setIsOpen(false);
292
- setQuery("");
293
- setResults([]);
294
302
  }
295
- }, [authToken, apiBaseUrl, appAuthPath, multiSelect, internalSelectedApps, onAppSelected, onSelectionChange, preventDefault]);
303
+ }, [authToken, apiBaseUrl, appAuthPath, orgId, multiSelect, internalSelectedApps, onAppSelected, onSelectionChange, preventDefault]);
296
304
  const handleKeyDown = (0, import_react.useCallback)((event) => {
297
305
  if (!isOpen) return;
298
306
  switch (event.key) {
@@ -413,100 +421,163 @@ var ShuffleMCP = import_react.default.forwardRef(({
413
421
  app.objectID
414
422
  );
415
423
  };
416
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
424
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
417
425
  "div",
418
426
  {
419
427
  ref: containerRef,
420
428
  className: `singul-container ${inline ? "singul-inline" : ""} ${className}`,
421
429
  style: customStyles.container,
422
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-search-bar-container", children: [
423
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-search-input-wrapper", style: customStyles.inputWrapper, children: [
424
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
425
- "input",
430
+ children: [
431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-search-bar-container", children: [
432
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-search-input-wrapper", style: customStyles.inputWrapper, children: [
433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
434
+ "input",
435
+ {
436
+ ref: inputRef,
437
+ type: "text",
438
+ className: "singul-search-input",
439
+ style: customStyles.input,
440
+ placeholder,
441
+ value: query,
442
+ onChange: handleInputChange,
443
+ onFocus: () => {
444
+ if (!inline && query.trim() && results.length > 0) {
445
+ setIsOpen(true);
446
+ }
447
+ },
448
+ onKeyDown: handleKeyDown,
449
+ autoComplete: "off"
450
+ }
451
+ ),
452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-search-icon", style: customStyles.searchIcon, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
453
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m21 21-4.35-4.35" })
455
+ ] }) }),
456
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-loading-spinner", style: customStyles.loadingSpinner, children: renderLoadingState ? renderLoadingState() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-spinner", style: customStyles.spinner }) })
457
+ ] }),
458
+ showSourceFilter && privateApps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-source-filter", role: "tablist", "aria-label": "Filter by app source", children: [
459
+ { key: "all", label: "All", count: results.length + filteredPrivateApps.length },
460
+ { key: "public", label: "Public", count: results.length },
461
+ { key: "private", label: "Private", count: filteredPrivateApps.length }
462
+ ].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
463
+ "button",
464
+ {
465
+ type: "button",
466
+ role: "tab",
467
+ "aria-selected": sourceFilter === opt.key,
468
+ className: `singul-source-pill ${sourceFilter === opt.key ? "singul-source-pill-active" : ""}`,
469
+ onClick: () => setSourceFilter(opt.key),
470
+ children: [
471
+ opt.label,
472
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "singul-source-count", children: opt.count })
473
+ ]
474
+ },
475
+ opt.key
476
+ )) }),
477
+ inline && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
478
+ "div",
426
479
  {
427
- ref: inputRef,
428
- type: "text",
429
- className: "singul-search-input",
430
- style: customStyles.input,
431
- placeholder,
432
- value: query,
433
- onChange: handleInputChange,
434
- onFocus: () => {
435
- if (!inline && query.trim() && results.length > 0) {
436
- setIsOpen(true);
437
- }
480
+ className: `singul-results-container ${layout === "grid" ? "singul-results-grid" : ""}`,
481
+ style: {
482
+ ...customStyles.resultsContainer,
483
+ ...getGridColumnsStyle,
484
+ ...layout === "grid" ? { display: "grid" } : {}
438
485
  },
439
- onKeyDown: handleKeyDown,
440
- autoComplete: "off"
486
+ children: displayResults.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
487
+ displayResults.map((app, index) => renderAppItem(app, index)),
488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-end-of-results", style: {
489
+ padding: "10px 16px",
490
+ color: "rgba(255, 255, 255, 0.3)",
491
+ textAlign: "center",
492
+ fontSize: "11px",
493
+ gridColumn: "1 / -1"
494
+ }, children: "Can't find what you're looking for? Try a different search term." })
495
+ ] }) : query.trim() ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-empty-state", style: customStyles.emptyState, children: renderEmptyState ? renderEmptyState() : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
496
+ 'No integrations match "',
497
+ query,
498
+ '". Try searching for a different app or category.'
499
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-empty-state", style: customStyles.emptyState, children: "Start typing to search integrations..." })
441
500
  }
442
501
  ),
443
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-search-icon", style: customStyles.searchIcon, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
444
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
445
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m21 21-4.35-4.35" })
446
- ] }) }),
447
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-loading-spinner", style: customStyles.loadingSpinner, children: renderLoadingState ? renderLoadingState() : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-spinner", style: customStyles.spinner }) })
502
+ !inline && isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
503
+ "div",
504
+ {
505
+ className: `singul-dropdown ${layout === "grid" ? "singul-dropdown-grid" : ""}`,
506
+ style: {
507
+ ...customStyles.dropdown,
508
+ ...getGridColumnsStyle,
509
+ ...layout === "grid" ? { display: "grid" } : {}
510
+ },
511
+ children: displayResults.length > 0 ? displayResults.map((app, index) => renderAppItem(app, index)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-empty-state", style: customStyles.emptyState, children: renderEmptyState ? renderEmptyState() : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
512
+ 'No apps found for "',
513
+ query,
514
+ '"'
515
+ ] }) })
516
+ }
517
+ )
448
518
  ] }),
449
- showSourceFilter && privateApps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-source-filter", role: "tablist", "aria-label": "Filter by app source", children: [
450
- { key: "all", label: "All", count: results.length + filteredPrivateApps.length },
451
- { key: "public", label: "Public", count: results.length },
452
- { key: "private", label: "Private", count: filteredPrivateApps.length }
453
- ].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
454
- "button",
455
- {
456
- type: "button",
457
- role: "tab",
458
- "aria-selected": sourceFilter === opt.key,
459
- className: `singul-source-pill ${sourceFilter === opt.key ? "singul-source-pill-active" : ""}`,
460
- onClick: () => setSourceFilter(opt.key),
461
- children: [
462
- opt.label,
463
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "singul-source-count", children: opt.count })
464
- ]
465
- },
466
- opt.key
467
- )) }),
468
- inline && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
469
- "div",
470
- {
471
- className: `singul-results-container ${layout === "grid" ? "singul-results-grid" : ""}`,
472
- style: {
473
- ...customStyles.resultsContainer,
474
- ...getGridColumnsStyle,
475
- ...layout === "grid" ? { display: "grid" } : {}
476
- },
477
- children: displayResults.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
478
- displayResults.map((app, index) => renderAppItem(app, index)),
479
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-end-of-results", style: {
480
- padding: "10px 16px",
481
- color: "rgba(255, 255, 255, 0.3)",
482
- textAlign: "center",
483
- fontSize: "11px",
484
- gridColumn: "1 / -1"
485
- }, children: "Can't find what you're looking for? Try a different search term." })
486
- ] }) : query.trim() ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-empty-state", style: customStyles.emptyState, children: renderEmptyState ? renderEmptyState() : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
487
- 'No integrations match "',
488
- query,
489
- '". Try searching for a different app or category.'
490
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-empty-state", style: customStyles.emptyState, children: "Start typing to search integrations..." })
491
- }
492
- ),
493
- !inline && isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
494
- "div",
495
- {
496
- className: `singul-dropdown ${layout === "grid" ? "singul-dropdown-grid" : ""}`,
497
- style: {
498
- ...customStyles.dropdown,
499
- ...getGridColumnsStyle,
500
- ...layout === "grid" ? { display: "grid" } : {}
501
- },
502
- children: displayResults.length > 0 ? displayResults.map((app, index) => renderAppItem(app, index)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-empty-state", style: customStyles.emptyState, children: renderEmptyState ? renderEmptyState() : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
503
- 'No apps found for "',
504
- query,
505
- '"'
506
- ] }) })
507
- }
508
- )
509
- ] })
519
+ drawerApp && (() => {
520
+ const norm = (n) => (n || "").toLowerCase().replace(/[\s_\-]+/g, "_");
521
+ const matchingAuths = authenticatedApps.filter(
522
+ (a) => norm(a.app?.name || "") === norm(drawerApp.name)
523
+ );
524
+ const drawerAuthUrl = `${apiBaseUrl}${appAuthPath}?app_id=${drawerApp.objectID}&auth=${authToken}&source=shuffle${orgId ? `&org_id=${encodeURIComponent(orgId)}` : ""}`;
525
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-drawer-backdrop", onClick: () => setDrawerApp(null) }),
527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", { className: "singul-drawer", role: "dialog", "aria-label": `${drawerApp.name} configuration`, children: [
528
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "singul-drawer-header", children: [
529
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-drawer-title-row", children: [
530
+ drawerApp.image_url && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: drawerApp.image_url, alt: drawerApp.name, className: "singul-drawer-icon" }),
531
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-drawer-title", children: drawerApp.name.replace(/_/g, " ") }),
533
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-drawer-subtitle", children: "App configuration" })
534
+ ] })
535
+ ] }),
536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
537
+ "button",
538
+ {
539
+ type: "button",
540
+ className: "singul-drawer-close",
541
+ onClick: () => setDrawerApp(null),
542
+ "aria-label": "Close",
543
+ children: "\xD7"
544
+ }
545
+ )
546
+ ] }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-drawer-body", children: [
548
+ drawerApp.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "singul-drawer-desc", children: drawerApp.description }),
549
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-drawer-section-title", children: [
550
+ "Authentication",
551
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "singul-drawer-count", children: [
552
+ matchingAuths.length,
553
+ " configuration",
554
+ matchingAuths.length === 1 ? "" : "s",
555
+ " found"
556
+ ] })
557
+ ] }),
558
+ matchingAuths.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "singul-drawer-auth-list", children: matchingAuths.map((a) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "singul-drawer-auth-item", children: [
559
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-drawer-auth-label", children: a.label || "Untitled" }),
560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "singul-drawer-auth-chips", children: [
561
+ a.active && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "singul-chip singul-chip-configured", children: "Configured" }),
562
+ a.validation?.valid && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "singul-chip singul-chip-tested", children: "Tested" }),
563
+ !a.active && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "singul-chip singul-chip-inactive", children: "Inactive" })
564
+ ] })
565
+ ] }, a.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "singul-drawer-empty", children: "No authentications yet for this app." }),
566
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
567
+ "a",
568
+ {
569
+ href: drawerAuthUrl,
570
+ target: "_blank",
571
+ rel: "noopener noreferrer",
572
+ className: "singul-drawer-cta",
573
+ children: matchingAuths.length > 0 ? "Manage authentication" : "Add authentication"
574
+ }
575
+ )
576
+ ] })
577
+ ] })
578
+ ] });
579
+ })()
580
+ ]
510
581
  }
511
582
  );
512
583
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../ShuffleMCP.tsx"],"sourcesContent":["/**\n * Local Singul Library\n * This is a local copy for development. Changes here should be merged back to:\n * https://github.com/Shuffle/singul.js\n */\n\nexport { ShuffleMCP, default } from './ShuffleMCP';\nexport type { ShuffleMCPHandle } from './ShuffleMCP';\nexport type {\n AlgoliaSearchApp,\n AppSelectedEvent,\n AppAuthentication,\n CustomStyles,\n ShuffleMCPProps,\n} from './singul.helpers';\n","/**\n * Local Singul Library - React Component\n * This is a local copy for development. Changes here should be merged back to:\n * https://github.com/Shuffle/singul.js\n */\n\nimport React, { useState, useEffect, useRef, useCallback, useMemo, useImperativeHandle } from 'react';\nimport { algoliasearch, SearchClient } from 'algoliasearch';\nimport type { AlgoliaSearchApp, AppSelectedEvent, ShuffleMCPProps, AppAuthentication } from './singul.helpers';\nimport './singul.css';\n\nconst DEFAULT_ALGOLIA_APP_ID = 'JNSS5CFDZZ';\nconst DEFAULT_ALGOLIA_API_KEY = '33e4e3564f4f060e96e0531957bed552';\nconst DEFAULT_ALGOLIA_INDEX = 'appsearch';\n\nexport interface ShuffleMCPHandle {\n search: (query: string) => void;\n clear: () => void;\n}\n\nexport const ShuffleMCP = React.forwardRef<ShuffleMCPHandle, ShuffleMCPProps>(({\n authToken,\n placeholder = 'Search apps...',\n layout = 'list',\n gridColumns = 3,\n showDescription = false,\n showCategories = false,\n showCheckbox = false,\n multiSelect = false,\n selectedApps = [],\n preventDefault = false,\n inline = false,\n initialQuery = '',\n initialFilterQuery,\n hitsPerPage = 15,\n apiKey,\n apiBaseUrl = 'https://shuffler.io',\n authPath = '/api/v1/apps/authentication',\n appAuthPath = '/appauth',\n privateAppsPath = '/api/v1/apps',\n disablePrivateApps = false,\n showSourceFilter = true,\n singulBaseUrl = 'https://singul.io',\n hideAuthStatus = false,\n authenticatedApps: externalAuthenticatedApps,\n pinnedApps,\n algoliaAppId = DEFAULT_ALGOLIA_APP_ID,\n algoliaApiKey = DEFAULT_ALGOLIA_API_KEY,\n algoliaIndexName = DEFAULT_ALGOLIA_INDEX,\n customStyles = {},\n className = '',\n renderItem,\n renderEmptyState,\n renderLoadingState,\n onAppSelected,\n onSelectionChange,\n onSearchChange,\n}, ref) => {\n const [query, setQuery] = useState(initialQuery);\n const [results, setResults] = useState<AlgoliaSearchApp[]>([]);\n const [privateApps, setPrivateApps] = useState<AlgoliaSearchApp[]>([]);\n const [sourceFilter, setSourceFilter] = useState<'all' | 'public' | 'private'>('all');\n const [isLoading, setIsLoading] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const [internalSelectedApps, setInternalSelectedApps] = useState<AlgoliaSearchApp[]>(selectedApps);\n const [authenticatedApps, setAuthenticatedApps] = useState<AppAuthentication[]>(externalAuthenticatedApps || []);\n const hasInitialized = useRef(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const searchClient = useRef<SearchClient | null>(null);\n\n // Fetch authenticated apps when apiKey is provided\n useEffect(() => {\n if (apiKey) {\n const fetchAuthenticatedApps = async () => {\n try {\n const response = await fetch(`${apiBaseUrl}${authPath}`, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n },\n });\n if (response.ok) {\n const result = await response.json();\n // Handle both { success: true, data: [...] } and direct array response\n const authData = result.data || result;\n if (Array.isArray(authData)) {\n console.log('Loaded authenticated apps:', authData.length, authData.map(a => a.app?.name));\n setAuthenticatedApps(authData);\n }\n }\n } catch (error) {\n console.error('Failed to fetch authenticated apps:', error);\n }\n };\n fetchAuthenticatedApps();\n }\n }, [apiKey, apiBaseUrl]);\n\n // Fetch the user's private apps from /api/v1/apps when apiKey is provided.\n // These get merged into search results so users can find their own apps too.\n useEffect(() => {\n if (!apiKey || disablePrivateApps) {\n setPrivateApps([]);\n return;\n }\n const fetchPrivateApps = async () => {\n try {\n const response = await fetch(`${apiBaseUrl}${privateAppsPath}`, {\n headers: { 'Authorization': `Bearer ${apiKey}` },\n credentials: 'include',\n });\n if (!response.ok) return;\n const data = await response.json();\n const apps = Array.isArray(data) ? data : (data?.data || []);\n // Normalize to AlgoliaSearchApp shape, tagged as 'private' source.\n const normalized: AlgoliaSearchApp[] = apps.map((a: any) => ({\n name: a.name || '',\n description: a.description || '',\n objectID: a.id || a.objectID || `private-${a.name}`,\n creator: a.owner || a.creator || '',\n app_version: a.app_version || '1.0.0',\n image_url: a.large_image || a.image_url || '',\n time_edited: a.edited || 0,\n generated: !!a.generated,\n invalid: !!a.invalid,\n priority: a.priority || 0,\n actions: Array.isArray(a.actions) ? a.actions.length : (a.actions || 0),\n tags: a.tags || [],\n accessible_by: a.accessible_by || [],\n categories: a.categories || [],\n action_labels: a.action_labels || [],\n triggers: a.triggers || [],\n verified: !!a.verified,\n source: 'private',\n }));\n setPrivateApps(normalized);\n } catch (error) {\n console.error('Failed to fetch private apps:', error);\n }\n };\n fetchPrivateApps();\n }, [apiKey, apiBaseUrl, privateAppsPath, disablePrivateApps]);\n\n // Auto-select validated apps when they're loaded (validated = tests ran successfully)\n useEffect(() => {\n if (authenticatedApps.length > 0 && results.length > 0) {\n const validatedApps = authenticatedApps.filter(auth => auth.validation?.valid);\n const appsToAutoSelect = results.filter(app => \n validatedApps.some(auth => auth.app.name.toLowerCase() === app.name.toLowerCase())\n );\n \n if (appsToAutoSelect.length > 0) {\n const newSelection = [...internalSelectedApps];\n appsToAutoSelect.forEach(app => {\n if (!newSelection.some(a => a.objectID === app.objectID)) {\n newSelection.push(app);\n }\n });\n if (newSelection.length !== internalSelectedApps.length) {\n setInternalSelectedApps(newSelection);\n onSelectionChange?.(newSelection);\n }\n }\n }\n }, [authenticatedApps, results]);\n\n // Sync external authenticatedApps\n useEffect(() => {\n if (externalAuthenticatedApps) {\n setAuthenticatedApps(externalAuthenticatedApps);\n }\n }, [externalAuthenticatedApps]);\n\n // Initialize Algolia client and run initial search\n useEffect(() => {\n searchClient.current = algoliasearch(algoliaAppId, algoliaApiKey);\n \n // Run initial search after client is ready\n if (!hasInitialized.current) {\n hasInitialized.current = true;\n const searchTerm = initialQuery || initialFilterQuery || '';\n setTimeout(() => {\n if (searchClient.current) {\n performSearch(searchTerm);\n onSearchChange?.(initialQuery); // Only report the visible query\n }\n }, 100);\n }\n }, []);\n\n // Sync external selectedApps\n useEffect(() => {\n setInternalSelectedApps(selectedApps);\n }, [selectedApps]);\n\n // Handle click outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n setSelectedIndex(-1);\n }\n };\n\n document.addEventListener('click', handleClickOutside);\n return () => document.removeEventListener('click', handleClickOutside);\n }, []);\n\n // Check if an app is configured (found in authentication API with active: true)\n const isAppConfigured = useCallback((app: AlgoliaSearchApp) => {\n return authenticatedApps.some(\n auth => auth.app.name.toLowerCase() === app.name.toLowerCase() && auth.active === true\n );\n }, [authenticatedApps]);\n\n // Check if an app is validated/tested (validation.valid is true)\n const isAppValidated = useCallback((app: AlgoliaSearchApp) => {\n return authenticatedApps.some(\n auth => auth.app.name.toLowerCase() === app.name.toLowerCase() && auth.validation?.valid === true\n );\n }, [authenticatedApps]);\n\n // Get auth state for an app\n const getAppAuthState = useCallback((app: AlgoliaSearchApp) => {\n return {\n configured: isAppConfigured(app),\n validated: isAppValidated(app),\n };\n }, [isAppConfigured, isAppValidated]);\n\n // Perform search\n const performSearch = useCallback(async (searchQuery: string) => {\n if (!searchClient.current) {\n return;\n }\n\n setIsLoading(true);\n try {\n const searchResult = await searchClient.current.searchSingleIndex({\n indexName: algoliaIndexName,\n searchParams: {\n query: searchQuery || '', // Empty string gets top results\n hitsPerPage,\n },\n });\n\n const hits = (searchResult.hits as AlgoliaSearchApp[]).map(h => ({ ...h, source: 'public' as const }));\n setResults(hits);\n setIsOpen(hits.length > 0);\n setSelectedIndex(-1);\n } catch (error) {\n console.error('Search failed:', error);\n setResults([]);\n setIsOpen(false);\n } finally {\n setIsLoading(false);\n }\n }, [hitsPerPage]);\n\n // Expose imperative methods via ref\n useImperativeHandle(ref, () => ({\n search: (searchQuery: string) => {\n setQuery(searchQuery);\n onSearchChange?.(searchQuery);\n performSearch(searchQuery);\n },\n clear: () => {\n setQuery('');\n performSearch(''); // Show top results when cleared\n },\n getQuery: () => query,\n }), [performSearch, onSearchChange, query]);\n\n // Handle input change\n const handleInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.target.value;\n setQuery(value);\n setSelectedIndex(-1);\n onSearchChange?.(value);\n\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n debounceTimer.current = setTimeout(() => {\n performSearch(value);\n }, 300);\n }, [performSearch, onSearchChange]);\n\n // Handle app selection\n const selectApp = useCallback((app: AlgoliaSearchApp) => {\n const authUrl = `${apiBaseUrl}${appAuthPath}?app_id=${app.objectID}&auth=${authToken}&source=shuffle`;\n \n if (multiSelect) {\n const isAlreadySelected = internalSelectedApps.some((a) => a.objectID === app.objectID);\n const newSelection = isAlreadySelected\n ? internalSelectedApps.filter((a) => a.objectID !== app.objectID)\n : [...internalSelectedApps, app];\n \n setInternalSelectedApps(newSelection);\n onSelectionChange?.(newSelection);\n } else {\n onAppSelected?.({ app, authUrl });\n \n if (!preventDefault) {\n window.open(authUrl, '_blank');\n }\n \n setIsOpen(false);\n setQuery('');\n setResults([]);\n }\n }, [authToken, apiBaseUrl, appAuthPath, multiSelect, internalSelectedApps, onAppSelected, onSelectionChange, preventDefault]);\n\n // Handle keyboard navigation\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (!isOpen) return;\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n setSelectedIndex((prev) => Math.min(prev + 1, displayResults.length - 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n setSelectedIndex((prev) => Math.max(prev - 1, -1));\n break;\n case 'Enter':\n event.preventDefault();\n if (selectedIndex >= 0 && displayResults[selectedIndex]) {\n selectApp(displayResults[selectedIndex]);\n }\n break;\n case 'Escape':\n event.preventDefault();\n setIsOpen(false);\n setSelectedIndex(-1);\n inputRef.current?.blur();\n break;\n }\n }, [isOpen, results, selectedIndex, selectApp]);\n\n // Check if app is selected\n const isAppSelected = useCallback((app: AlgoliaSearchApp) => {\n return internalSelectedApps.some((a) => a.objectID === app.objectID);\n }, [internalSelectedApps]);\n\n // Filter the user's private apps client-side by the current query.\n const filteredPrivateApps = useMemo(() => {\n if (privateApps.length === 0) return [];\n const q = query.trim().toLowerCase();\n if (!q) return privateApps;\n return privateApps.filter(a =>\n a.name.toLowerCase().includes(q) ||\n (a.description || '').toLowerCase().includes(q) ||\n (a.categories || []).some(c => c.toLowerCase().includes(q))\n );\n }, [privateApps, query]);\n\n // Merge private + public apps, apply source filter, prepend pinned, dedupe by name.\n const displayResults = useMemo(() => {\n const norm = (n: string) => (n || '').toLowerCase().replace(/[\\s_\\-]+/g, '');\n\n let merged: AlgoliaSearchApp[];\n if (sourceFilter === 'public') {\n merged = results;\n } else if (sourceFilter === 'private') {\n merged = filteredPrivateApps;\n } else {\n // 'all' — private apps first (your own tools win), then public, deduped by name\n const privateNames = new Set(filteredPrivateApps.map(a => norm(a.name)));\n const publicOnly = results.filter(a => !privateNames.has(norm(a.name)));\n merged = [...filteredPrivateApps, ...publicOnly];\n }\n\n if (!pinnedApps || pinnedApps.length === 0) return merged;\n const pinnedNames = new Set(pinnedApps.map(a => norm(a.name)));\n return [...pinnedApps, ...merged.filter(a => !pinnedNames.has(norm(a.name)))];\n }, [pinnedApps, results, filteredPrivateApps, sourceFilter]);\n\n // Get grid columns style\n const getGridColumnsStyle = useMemo(() => {\n if (layout !== 'grid') return {};\n \n const cols = typeof gridColumns === 'number' ? gridColumns : gridColumns.md || 3;\n return { '--singul-grid-columns': cols } as React.CSSProperties;\n }, [layout, gridColumns]);\n\n // Render individual app item\n const renderAppItem = (app: AlgoliaSearchApp, index: number) => {\n const selected = isAppSelected(app);\n const isHighlighted = index === selectedIndex;\n const authState = getAppAuthState(app);\n\n // Use custom render if provided\n if (renderItem) {\n return (\n <div\n key={app.objectID}\n onClick={() => selectApp(app)}\n onMouseEnter={() => setSelectedIndex(index)}\n >\n {renderItem(app, selected, () => selectApp(app), authState)}\n </div>\n );\n }\n\n // Show both chips (configured/not configured AND tested/not tested)\n return (\n <div\n key={app.objectID}\n className={`singul-dropdown-item ${selected ? 'singul-selected' : ''} ${authState.validated ? 'singul-validated' : ''} ${authState.configured ? 'singul-configured' : ''}`}\n style={{\n ...customStyles.dropdownItem,\n ...(selected ? customStyles.selectedItem : {}),\n }}\n onClick={() => selectApp(app)}\n >\n <div className=\"singul-app-info\" style={customStyles.appInfo}>\n {app.image_url && (\n <div style={{ position: 'relative', flexShrink: 0 }}>\n <img\n src={app.image_url}\n alt={app.name}\n className=\"singul-app-icon\"\n style={customStyles.appIcon}\n />\n {/* Color-coded status dot: green=tested, yellow=configured, blue=activated/selected, gray=inactive */}\n {!hideAuthStatus && (\n <span\n className={`singul-status-dot ${\n authState.validated ? 'singul-dot-validated' :\n authState.configured ? 'singul-dot-configured' :\n selected ? 'singul-dot-activated' :\n 'singul-dot-inactive'\n }`}\n />\n )}\n </div>\n )}\n <div className=\"singul-app-details\" style={customStyles.appDetails}>\n <span className=\"singul-app-name\" style={customStyles.appName}>\n {app.name.replace(/_/g, ' ')}\n {app.source === 'private' && (\n <span className=\"singul-private-badge\" title=\"From your private apps\">Private</span>\n )}\n </span>\n {showDescription && app.description && (\n <span className=\"singul-app-description\" style={customStyles.appDescription}>\n {app.description}\n </span>\n )}\n {showCategories && app.categories?.[0] && (\n <span className=\"singul-app-category\" style={customStyles.appCategory}>\n {app.categories[0]}\n </span>\n )}\n </div>\n {showCheckbox && (\n <div\n className={`singul-checkbox ${selected ? 'singul-checked' : ''}`}\n style={selected ? customStyles.checkboxChecked : customStyles.checkbox}\n >\n {selected && (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n )}\n </div>\n )}\n </div>\n </div>\n );\n };\n\n return (\n <div\n ref={containerRef}\n className={`singul-container ${inline ? 'singul-inline' : ''} ${className}`}\n style={customStyles.container}\n >\n <div className=\"singul-search-bar-container\">\n <div className=\"singul-search-input-wrapper\" style={customStyles.inputWrapper}>\n <input\n ref={inputRef}\n type=\"text\"\n className=\"singul-search-input\"\n style={customStyles.input}\n placeholder={placeholder}\n value={query}\n onChange={handleInputChange}\n onFocus={() => {\n if (!inline && query.trim() && results.length > 0) {\n setIsOpen(true);\n }\n }}\n onKeyDown={handleKeyDown}\n autoComplete=\"off\"\n />\n <div className=\"singul-search-icon\" style={customStyles.searchIcon}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.35-4.35\" />\n </svg>\n </div>\n {isLoading && (\n <div className=\"singul-loading-spinner\" style={customStyles.loadingSpinner}>\n {renderLoadingState ? (\n renderLoadingState()\n ) : (\n <div className=\"singul-spinner\" style={customStyles.spinner} />\n )}\n </div>\n )}\n </div>\n\n {/* Source filter — only shown when private apps are loaded */}\n {showSourceFilter && privateApps.length > 0 && (\n <div className=\"singul-source-filter\" role=\"tablist\" aria-label=\"Filter by app source\">\n {([\n { key: 'all', label: 'All', count: results.length + filteredPrivateApps.length },\n { key: 'public', label: 'Public', count: results.length },\n { key: 'private', label: 'Private', count: filteredPrivateApps.length },\n ] as const).map(opt => (\n <button\n key={opt.key}\n type=\"button\"\n role=\"tab\"\n aria-selected={sourceFilter === opt.key}\n className={`singul-source-pill ${sourceFilter === opt.key ? 'singul-source-pill-active' : ''}`}\n onClick={() => setSourceFilter(opt.key)}\n >\n {opt.label}\n <span className=\"singul-source-count\">{opt.count}</span>\n </button>\n ))}\n </div>\n )}\n\n {/* Inline Results Container */}\n {inline && (\n <div\n className={`singul-results-container ${layout === 'grid' ? 'singul-results-grid' : ''}`}\n style={{\n ...customStyles.resultsContainer,\n ...getGridColumnsStyle,\n ...(layout === 'grid' ? { display: 'grid' } : {}),\n }}\n >\n {displayResults.length > 0 ? (\n <>\n {displayResults.map((app, index) => renderAppItem(app, index))}\n <div className=\"singul-end-of-results\" style={{\n padding: '10px 16px',\n color: 'rgba(255, 255, 255, 0.3)',\n textAlign: 'center' as const,\n fontSize: '11px',\n gridColumn: '1 / -1',\n }}>\n Can't find what you're looking for? Try a different search term.\n </div>\n </>\n ) : query.trim() ? (\n <div className=\"singul-empty-state\" style={customStyles.emptyState}>\n {renderEmptyState ? (\n renderEmptyState()\n ) : (\n <>No integrations match \"{query}\". Try searching for a different app or category.</>\n )}\n </div>\n ) : (\n <div className=\"singul-empty-state\" style={customStyles.emptyState}>\n Start typing to search integrations...\n </div>\n )}\n </div>\n )}\n\n {/* Dropdown Results (non-inline mode) */}\n {!inline && isOpen && (\n <div\n className={`singul-dropdown ${layout === 'grid' ? 'singul-dropdown-grid' : ''}`}\n style={{\n ...customStyles.dropdown,\n ...getGridColumnsStyle,\n ...(layout === 'grid' ? { display: 'grid' } : {}),\n }}\n >\n {displayResults.length > 0 ? (\n displayResults.map((app, index) => renderAppItem(app, index))\n ) : (\n <div className=\"singul-empty-state\" style={customStyles.emptyState}>\n {renderEmptyState ? (\n renderEmptyState()\n ) : (\n <>No apps found for \"{query}\"</>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n});\n\nShuffleMCP.displayName = 'ShuffleMCP';\n\nexport default ShuffleMCP;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMA,mBAA8F;AAC9F,2BAA4C;AAE5C,oBAAO;AAuYC;AArYR,IAAM,yBAAyB;AAC/B,IAAM,0BAA0B;AAChC,IAAM,wBAAwB;AAOvB,IAAM,aAAa,aAAAA,QAAM,WAA8C,CAAC;AAAA,EAC7E;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,EACT,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,eAAe,CAAC;AAAA,EAChB,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,eAAe;AAAA,EACf;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,aAAa;AAAA,EACb,WAAW;AAAA,EACX,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB;AAAA,EACA,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,eAAe,CAAC;AAAA,EAChB,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAG,QAAQ;AACT,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,YAAY;AAC/C,QAAM,CAAC,SAAS,UAAU,QAAI,uBAA6B,CAAC,CAAC;AAC7D,QAAM,CAAC,aAAa,cAAc,QAAI,uBAA6B,CAAC,CAAC;AACrE,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAuC,KAAK;AACpF,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAChD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAC1C,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,EAAE;AACrD,QAAM,CAAC,sBAAsB,uBAAuB,QAAI,uBAA6B,YAAY;AACjG,QAAM,CAAC,mBAAmB,oBAAoB,QAAI,uBAA8B,6BAA6B,CAAC,CAAC;AAC/G,QAAM,qBAAiB,qBAAO,KAAK;AAEnC,QAAM,mBAAe,qBAAuB,IAAI;AAChD,QAAM,eAAW,qBAAyB,IAAI;AAC9C,QAAM,oBAAgB,qBAA6C,IAAI;AACvE,QAAM,mBAAe,qBAA4B,IAAI;AAGrD,8BAAU,MAAM;AACd,QAAI,QAAQ;AACV,YAAM,yBAAyB,YAAY;AACzC,YAAI;AACF,gBAAM,WAAW,MAAM,MAAM,GAAG,UAAU,GAAG,QAAQ,IAAI;AAAA,YACvD,SAAS;AAAA,cACP,iBAAiB,UAAU,MAAM;AAAA,YACnC;AAAA,UACF,CAAC;AACD,cAAI,SAAS,IAAI;AACf,kBAAM,SAAS,MAAM,SAAS,KAAK;AAEnC,kBAAM,WAAW,OAAO,QAAQ;AAChC,gBAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,sBAAQ,IAAI,8BAA8B,SAAS,QAAQ,SAAS,IAAI,OAAK,EAAE,KAAK,IAAI,CAAC;AACzF,mCAAqB,QAAQ;AAAA,YAC/B;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ,MAAM,uCAAuC,KAAK;AAAA,QAC5D;AAAA,MACF;AACA,6BAAuB;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAIvB,8BAAU,MAAM;AACd,QAAI,CAAC,UAAU,oBAAoB;AACjC,qBAAe,CAAC,CAAC;AACjB;AAAA,IACF;AACA,UAAM,mBAAmB,YAAY;AACnC,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,GAAG,UAAU,GAAG,eAAe,IAAI;AAAA,UAC9D,SAAS,EAAE,iBAAiB,UAAU,MAAM,GAAG;AAAA,UAC/C,aAAa;AAAA,QACf,CAAC;AACD,YAAI,CAAC,SAAS,GAAI;AAClB,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAQ,MAAM,QAAQ,CAAC;AAE1D,cAAM,aAAiC,KAAK,IAAI,CAAC,OAAY;AAAA,UAC3D,MAAM,EAAE,QAAQ;AAAA,UAChB,aAAa,EAAE,eAAe;AAAA,UAC9B,UAAU,EAAE,MAAM,EAAE,YAAY,WAAW,EAAE,IAAI;AAAA,UACjD,SAAS,EAAE,SAAS,EAAE,WAAW;AAAA,UACjC,aAAa,EAAE,eAAe;AAAA,UAC9B,WAAW,EAAE,eAAe,EAAE,aAAa;AAAA,UAC3C,aAAa,EAAE,UAAU;AAAA,UACzB,WAAW,CAAC,CAAC,EAAE;AAAA,UACf,SAAS,CAAC,CAAC,EAAE;AAAA,UACb,UAAU,EAAE,YAAY;AAAA,UACxB,SAAS,MAAM,QAAQ,EAAE,OAAO,IAAI,EAAE,QAAQ,SAAU,EAAE,WAAW;AAAA,UACrE,MAAM,EAAE,QAAQ,CAAC;AAAA,UACjB,eAAe,EAAE,iBAAiB,CAAC;AAAA,UACnC,YAAY,EAAE,cAAc,CAAC;AAAA,UAC7B,eAAe,EAAE,iBAAiB,CAAC;AAAA,UACnC,UAAU,EAAE,YAAY,CAAC;AAAA,UACzB,UAAU,CAAC,CAAC,EAAE;AAAA,UACd,QAAQ;AAAA,QACV,EAAE;AACF,uBAAe,UAAU;AAAA,MAC3B,SAAS,OAAO;AACd,gBAAQ,MAAM,iCAAiC,KAAK;AAAA,MACtD;AAAA,IACF;AACA,qBAAiB;AAAA,EACnB,GAAG,CAAC,QAAQ,YAAY,iBAAiB,kBAAkB,CAAC;AAG5D,8BAAU,MAAM;AACd,QAAI,kBAAkB,SAAS,KAAK,QAAQ,SAAS,GAAG;AACtD,YAAM,gBAAgB,kBAAkB,OAAO,UAAQ,KAAK,YAAY,KAAK;AAC7E,YAAM,mBAAmB,QAAQ;AAAA,QAAO,SACtC,cAAc,KAAK,UAAQ,KAAK,IAAI,KAAK,YAAY,MAAM,IAAI,KAAK,YAAY,CAAC;AAAA,MACnF;AAEA,UAAI,iBAAiB,SAAS,GAAG;AAC/B,cAAM,eAAe,CAAC,GAAG,oBAAoB;AAC7C,yBAAiB,QAAQ,SAAO;AAC9B,cAAI,CAAC,aAAa,KAAK,OAAK,EAAE,aAAa,IAAI,QAAQ,GAAG;AACxD,yBAAa,KAAK,GAAG;AAAA,UACvB;AAAA,QACF,CAAC;AACD,YAAI,aAAa,WAAW,qBAAqB,QAAQ;AACvD,kCAAwB,YAAY;AACpC,8BAAoB,YAAY;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,mBAAmB,OAAO,CAAC;AAG/B,8BAAU,MAAM;AACd,QAAI,2BAA2B;AAC7B,2BAAqB,yBAAyB;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,yBAAyB,CAAC;AAG9B,8BAAU,MAAM;AACd,iBAAa,cAAU,oCAAc,cAAc,aAAa;AAGhE,QAAI,CAAC,eAAe,SAAS;AAC3B,qBAAe,UAAU;AACzB,YAAM,aAAa,gBAAgB,sBAAsB;AACzD,iBAAW,MAAM;AACf,YAAI,aAAa,SAAS;AACxB,wBAAc,UAAU;AACxB,2BAAiB,YAAY;AAAA,QAC/B;AAAA,MACF,GAAG,GAAG;AAAA,IACR;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,8BAAU,MAAM;AACd,4BAAwB,YAAY;AAAA,EACtC,GAAG,CAAC,YAAY,CAAC;AAGjB,8BAAU,MAAM;AACd,UAAM,qBAAqB,CAAC,UAAsB;AAChD,UAAI,aAAa,WAAW,CAAC,aAAa,QAAQ,SAAS,MAAM,MAAc,GAAG;AAChF,kBAAU,KAAK;AACf,yBAAiB,EAAE;AAAA,MACrB;AAAA,IACF;AAEA,aAAS,iBAAiB,SAAS,kBAAkB;AACrD,WAAO,MAAM,SAAS,oBAAoB,SAAS,kBAAkB;AAAA,EACvE,GAAG,CAAC,CAAC;AAGL,QAAM,sBAAkB,0BAAY,CAAC,QAA0B;AAC7D,WAAO,kBAAkB;AAAA,MACvB,UAAQ,KAAK,IAAI,KAAK,YAAY,MAAM,IAAI,KAAK,YAAY,KAAK,KAAK,WAAW;AAAA,IACpF;AAAA,EACF,GAAG,CAAC,iBAAiB,CAAC;AAGtB,QAAM,qBAAiB,0BAAY,CAAC,QAA0B;AAC5D,WAAO,kBAAkB;AAAA,MACvB,UAAQ,KAAK,IAAI,KAAK,YAAY,MAAM,IAAI,KAAK,YAAY,KAAK,KAAK,YAAY,UAAU;AAAA,IAC/F;AAAA,EACF,GAAG,CAAC,iBAAiB,CAAC;AAGtB,QAAM,sBAAkB,0BAAY,CAAC,QAA0B;AAC7D,WAAO;AAAA,MACL,YAAY,gBAAgB,GAAG;AAAA,MAC/B,WAAW,eAAe,GAAG;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,iBAAiB,cAAc,CAAC;AAGpC,QAAM,oBAAgB,0BAAY,OAAO,gBAAwB;AAC/D,QAAI,CAAC,aAAa,SAAS;AACzB;AAAA,IACF;AAEA,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,eAAe,MAAM,aAAa,QAAQ,kBAAkB;AAAA,QAChE,WAAW;AAAA,QACX,cAAc;AAAA,UACZ,OAAO,eAAe;AAAA;AAAA,UACtB;AAAA,QACF;AAAA,MACF,CAAC;AAED,YAAM,OAAQ,aAAa,KAA4B,IAAI,QAAM,EAAE,GAAG,GAAG,QAAQ,SAAkB,EAAE;AACrG,iBAAW,IAAI;AACf,gBAAU,KAAK,SAAS,CAAC;AACzB,uBAAiB,EAAE;AAAA,IACrB,SAAS,OAAO;AACd,cAAQ,MAAM,kBAAkB,KAAK;AACrC,iBAAW,CAAC,CAAC;AACb,gBAAU,KAAK;AAAA,IACjB,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAGhB,wCAAoB,KAAK,OAAO;AAAA,IAC9B,QAAQ,CAAC,gBAAwB;AAC/B,eAAS,WAAW;AACpB,uBAAiB,WAAW;AAC5B,oBAAc,WAAW;AAAA,IAC3B;AAAA,IACA,OAAO,MAAM;AACX,eAAS,EAAE;AACX,oBAAc,EAAE;AAAA,IAClB;AAAA,IACA,UAAU,MAAM;AAAA,EAClB,IAAI,CAAC,eAAe,gBAAgB,KAAK,CAAC;AAG1C,QAAM,wBAAoB,0BAAY,CAAC,UAA+C;AACpF,UAAM,QAAQ,MAAM,OAAO;AAC3B,aAAS,KAAK;AACd,qBAAiB,EAAE;AACnB,qBAAiB,KAAK;AAEtB,QAAI,cAAc,SAAS;AACzB,mBAAa,cAAc,OAAO;AAAA,IACpC;AAEA,kBAAc,UAAU,WAAW,MAAM;AACvC,oBAAc,KAAK;AAAA,IACrB,GAAG,GAAG;AAAA,EACR,GAAG,CAAC,eAAe,cAAc,CAAC;AAGlC,QAAM,gBAAY,0BAAY,CAAC,QAA0B;AACvD,UAAM,UAAU,GAAG,UAAU,GAAG,WAAW,WAAW,IAAI,QAAQ,SAAS,SAAS;AAEpF,QAAI,aAAa;AACf,YAAM,oBAAoB,qBAAqB,KAAK,CAAC,MAAM,EAAE,aAAa,IAAI,QAAQ;AACtF,YAAM,eAAe,oBACjB,qBAAqB,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,QAAQ,IAC9D,CAAC,GAAG,sBAAsB,GAAG;AAEjC,8BAAwB,YAAY;AACpC,0BAAoB,YAAY;AAAA,IAClC,OAAO;AACL,sBAAgB,EAAE,KAAK,QAAQ,CAAC;AAEhC,UAAI,CAAC,gBAAgB;AACnB,eAAO,KAAK,SAAS,QAAQ;AAAA,MAC/B;AAEA,gBAAU,KAAK;AACf,eAAS,EAAE;AACX,iBAAW,CAAC,CAAC;AAAA,IACf;AAAA,EACF,GAAG,CAAC,WAAW,YAAY,aAAa,aAAa,sBAAsB,eAAe,mBAAmB,cAAc,CAAC;AAG5H,QAAM,oBAAgB,0BAAY,CAAC,UAA+B;AAChE,QAAI,CAAC,OAAQ;AAEb,YAAQ,MAAM,KAAK;AAAA,MACjB,KAAK;AACH,cAAM,eAAe;AACrB,yBAAiB,CAAC,SAAS,KAAK,IAAI,OAAO,GAAG,eAAe,SAAS,CAAC,CAAC;AACxE;AAAA,MACF,KAAK;AACH,cAAM,eAAe;AACrB,yBAAiB,CAAC,SAAS,KAAK,IAAI,OAAO,GAAG,EAAE,CAAC;AACjD;AAAA,MACF,KAAK;AACH,cAAM,eAAe;AACrB,YAAI,iBAAiB,KAAK,eAAe,aAAa,GAAG;AACvD,oBAAU,eAAe,aAAa,CAAC;AAAA,QACzC;AACA;AAAA,MACF,KAAK;AACH,cAAM,eAAe;AACrB,kBAAU,KAAK;AACf,yBAAiB,EAAE;AACnB,iBAAS,SAAS,KAAK;AACvB;AAAA,IACJ;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,eAAe,SAAS,CAAC;AAG9C,QAAM,oBAAgB,0BAAY,CAAC,QAA0B;AAC3D,WAAO,qBAAqB,KAAK,CAAC,MAAM,EAAE,aAAa,IAAI,QAAQ;AAAA,EACrE,GAAG,CAAC,oBAAoB,CAAC;AAGzB,QAAM,0BAAsB,sBAAQ,MAAM;AACxC,QAAI,YAAY,WAAW,EAAG,QAAO,CAAC;AACtC,UAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAI,CAAC,EAAG,QAAO;AACf,WAAO,YAAY;AAAA,MAAO,OACxB,EAAE,KAAK,YAAY,EAAE,SAAS,CAAC,MAC9B,EAAE,eAAe,IAAI,YAAY,EAAE,SAAS,CAAC,MAC7C,EAAE,cAAc,CAAC,GAAG,KAAK,OAAK,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAAA,IAC5D;AAAA,EACF,GAAG,CAAC,aAAa,KAAK,CAAC;AAGvB,QAAM,qBAAiB,sBAAQ,MAAM;AACnC,UAAM,OAAO,CAAC,OAAe,KAAK,IAAI,YAAY,EAAE,QAAQ,aAAa,EAAE;AAE3E,QAAI;AACJ,QAAI,iBAAiB,UAAU;AAC7B,eAAS;AAAA,IACX,WAAW,iBAAiB,WAAW;AACrC,eAAS;AAAA,IACX,OAAO;AAEL,YAAM,eAAe,IAAI,IAAI,oBAAoB,IAAI,OAAK,KAAK,EAAE,IAAI,CAAC,CAAC;AACvE,YAAM,aAAa,QAAQ,OAAO,OAAK,CAAC,aAAa,IAAI,KAAK,EAAE,IAAI,CAAC,CAAC;AACtE,eAAS,CAAC,GAAG,qBAAqB,GAAG,UAAU;AAAA,IACjD;AAEA,QAAI,CAAC,cAAc,WAAW,WAAW,EAAG,QAAO;AACnD,UAAM,cAAc,IAAI,IAAI,WAAW,IAAI,OAAK,KAAK,EAAE,IAAI,CAAC,CAAC;AAC7D,WAAO,CAAC,GAAG,YAAY,GAAG,OAAO,OAAO,OAAK,CAAC,YAAY,IAAI,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AAAA,EAC9E,GAAG,CAAC,YAAY,SAAS,qBAAqB,YAAY,CAAC;AAG3D,QAAM,0BAAsB,sBAAQ,MAAM;AACxC,QAAI,WAAW,OAAQ,QAAO,CAAC;AAE/B,UAAM,OAAO,OAAO,gBAAgB,WAAW,cAAc,YAAY,MAAM;AAC/E,WAAO,EAAE,yBAAyB,KAAK;AAAA,EACzC,GAAG,CAAC,QAAQ,WAAW,CAAC;AAGxB,QAAM,gBAAgB,CAAC,KAAuB,UAAkB;AAC9D,UAAM,WAAW,cAAc,GAAG;AAClC,UAAM,gBAAgB,UAAU;AAChC,UAAM,YAAY,gBAAgB,GAAG;AAGrC,QAAI,YAAY;AACd,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,UAAU,GAAG;AAAA,UAC5B,cAAc,MAAM,iBAAiB,KAAK;AAAA,UAEzC,qBAAW,KAAK,UAAU,MAAM,UAAU,GAAG,GAAG,SAAS;AAAA;AAAA,QAJrD,IAAI;AAAA,MAKX;AAAA,IAEJ;AAGA,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,WAAW,wBAAwB,WAAW,oBAAoB,EAAE,IAAI,UAAU,YAAY,qBAAqB,EAAE,IAAI,UAAU,aAAa,sBAAsB,EAAE;AAAA,QACxK,OAAO;AAAA,UACL,GAAG,aAAa;AAAA,UAChB,GAAI,WAAW,aAAa,eAAe,CAAC;AAAA,QAC9C;AAAA,QACA,SAAS,MAAM,UAAU,GAAG;AAAA,QAE5B,uDAAC,SAAI,WAAU,mBAAkB,OAAO,aAAa,SAClD;AAAA,cAAI,aACH,6CAAC,SAAI,OAAO,EAAE,UAAU,YAAY,YAAY,EAAE,GAChD;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK,IAAI;AAAA,gBACT,KAAK,IAAI;AAAA,gBACT,WAAU;AAAA,gBACV,OAAO,aAAa;AAAA;AAAA,YACtB;AAAA,YAEC,CAAC,kBACA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,qBACT,UAAU,YAAY,yBACtB,UAAU,aAAa,0BACvB,WAAW,yBACX,qBACF;AAAA;AAAA,YACF;AAAA,aAEJ;AAAA,UAEF,6CAAC,SAAI,WAAU,sBAAqB,OAAO,aAAa,YACtD;AAAA,yDAAC,UAAK,WAAU,mBAAkB,OAAO,aAAa,SACnD;AAAA,kBAAI,KAAK,QAAQ,MAAM,GAAG;AAAA,cAC1B,IAAI,WAAW,aACd,4CAAC,UAAK,WAAU,wBAAuB,OAAM,0BAAyB,qBAAO;AAAA,eAEjF;AAAA,YACC,mBAAmB,IAAI,eACtB,4CAAC,UAAK,WAAU,0BAAyB,OAAO,aAAa,gBAC1D,cAAI,aACP;AAAA,YAED,kBAAkB,IAAI,aAAa,CAAC,KACnC,4CAAC,UAAK,WAAU,uBAAsB,OAAO,aAAa,aACvD,cAAI,WAAW,CAAC,GACnB;AAAA,aAEJ;AAAA,UACC,gBACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,mBAAmB,WAAW,mBAAmB,EAAE;AAAA,cAC9D,OAAO,WAAW,aAAa,kBAAkB,aAAa;AAAA,cAE7D,sBACC,4CAAC,SAAI,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KACrE,sDAAC,cAAS,QAAO,kBAAiB,GACpC;AAAA;AAAA,UAEJ;AAAA,WAEJ;AAAA;AAAA,MA5DK,IAAI;AAAA,IA6DX;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,oBAAoB,SAAS,kBAAkB,EAAE,IAAI,SAAS;AAAA,MACzE,OAAO,aAAa;AAAA,MAEpB,uDAAC,SAAI,WAAU,+BACb;AAAA,qDAAC,SAAI,WAAU,+BAA8B,OAAO,aAAa,cAC/D;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAO,aAAa;AAAA,cACpB;AAAA,cACA,OAAO;AAAA,cACP,UAAU;AAAA,cACV,SAAS,MAAM;AACb,oBAAI,CAAC,UAAU,MAAM,KAAK,KAAK,QAAQ,SAAS,GAAG;AACjD,4BAAU,IAAI;AAAA,gBAChB;AAAA,cACF;AAAA,cACA,WAAW;AAAA,cACX,cAAa;AAAA;AAAA,UACf;AAAA,UACA,4CAAC,SAAI,WAAU,sBAAqB,OAAO,aAAa,YACtD,uDAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAC5F;AAAA,wDAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,YAC9B,4CAAC,UAAK,GAAE,oBAAmB;AAAA,aAC7B,GACF;AAAA,UACC,aACC,4CAAC,SAAI,WAAU,0BAAyB,OAAO,aAAa,gBACzD,+BACC,mBAAmB,IAEnB,4CAAC,SAAI,WAAU,kBAAiB,OAAO,aAAa,SAAS,GAEjE;AAAA,WAEJ;AAAA,QAGC,oBAAoB,YAAY,SAAS,KACxC,4CAAC,SAAI,WAAU,wBAAuB,MAAK,WAAU,cAAW,wBAC5D;AAAA,UACA,EAAE,KAAK,OAAO,OAAO,OAAO,OAAO,QAAQ,SAAS,oBAAoB,OAAO;AAAA,UAC/E,EAAE,KAAK,UAAU,OAAO,UAAU,OAAO,QAAQ,OAAO;AAAA,UACxD,EAAE,KAAK,WAAW,OAAO,WAAW,OAAO,oBAAoB,OAAO;AAAA,QACxE,EAAY,IAAI,SACd;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,iBAAe,iBAAiB,IAAI;AAAA,YACpC,WAAW,sBAAsB,iBAAiB,IAAI,MAAM,8BAA8B,EAAE;AAAA,YAC5F,SAAS,MAAM,gBAAgB,IAAI,GAAG;AAAA,YAErC;AAAA,kBAAI;AAAA,cACL,4CAAC,UAAK,WAAU,uBAAuB,cAAI,OAAM;AAAA;AAAA;AAAA,UAR5C,IAAI;AAAA,QASX,CACD,GACH;AAAA,QAID,UACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,4BAA4B,WAAW,SAAS,wBAAwB,EAAE;AAAA,YACrF,OAAO;AAAA,cACL,GAAG,aAAa;AAAA,cAChB,GAAG;AAAA,cACH,GAAI,WAAW,SAAS,EAAE,SAAS,OAAO,IAAI,CAAC;AAAA,YACjD;AAAA,YAEC,yBAAe,SAAS,IACvB,4EACG;AAAA,6BAAe,IAAI,CAAC,KAAK,UAAU,cAAc,KAAK,KAAK,CAAC;AAAA,cAC7D,4CAAC,SAAI,WAAU,yBAAwB,OAAO;AAAA,gBAC5C,SAAS;AAAA,gBACT,OAAO;AAAA,gBACP,WAAW;AAAA,gBACX,UAAU;AAAA,gBACV,YAAY;AAAA,cACd,GAAG,8EAEH;AAAA,eACF,IACE,MAAM,KAAK,IACb,4CAAC,SAAI,WAAU,sBAAqB,OAAO,aAAa,YACrD,6BACC,iBAAiB,IAEjB,4EAAE;AAAA;AAAA,cAAwB;AAAA,cAAM;AAAA,eAAiD,GAErF,IAEA,4CAAC,SAAI,WAAU,sBAAqB,OAAO,aAAa,YAAY,oDAEpE;AAAA;AAAA,QAEJ;AAAA,QAID,CAAC,UAAU,UACV;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,mBAAmB,WAAW,SAAS,yBAAyB,EAAE;AAAA,YAC7E,OAAO;AAAA,cACL,GAAG,aAAa;AAAA,cAChB,GAAG;AAAA,cACH,GAAI,WAAW,SAAS,EAAE,SAAS,OAAO,IAAI,CAAC;AAAA,YACjD;AAAA,YAEC,yBAAe,SAAS,IACvB,eAAe,IAAI,CAAC,KAAK,UAAU,cAAc,KAAK,KAAK,CAAC,IAE5D,4CAAC,SAAI,WAAU,sBAAqB,OAAO,aAAa,YACrD,6BACC,iBAAiB,IAEjB,4EAAE;AAAA;AAAA,cAAoB;AAAA,cAAM;AAAA,eAAC,GAEjC;AAAA;AAAA,QAEJ;AAAA,SAEJ;AAAA;AAAA,EACF;AAEJ,CAAC;AAED,WAAW,cAAc;AAEzB,IAAO,qBAAQ;","names":["React"]}
1
+ {"version":3,"sources":["../index.ts","../ShuffleMCP.tsx"],"sourcesContent":["/**\n * Local Singul Library\n * This is a local copy for development. Changes here should be merged back to:\n * https://github.com/Shuffle/singul.js\n */\n\nexport { ShuffleMCP, default } from './ShuffleMCP';\nexport type { ShuffleMCPHandle } from './ShuffleMCP';\nexport type {\n AlgoliaSearchApp,\n AppSelectedEvent,\n AppAuthentication,\n CustomStyles,\n ShuffleMCPProps,\n} from './shuffle-mcp.helpers';\n","/**\n * Local Singul Library - React Component\n * This is a local copy for development. Changes here should be merged back to:\n * https://github.com/Shuffle/singul.js\n */\n\nimport React, { useState, useEffect, useRef, useCallback, useMemo, useImperativeHandle } from 'react';\nimport { algoliasearch, SearchClient } from 'algoliasearch';\nimport type { AlgoliaSearchApp, AppSelectedEvent, ShuffleMCPProps, AppAuthentication } from './shuffle-mcp.helpers';\nimport './shuffle-mcp.css';\n\nconst DEFAULT_ALGOLIA_APP_ID = 'JNSS5CFDZZ';\nconst DEFAULT_ALGOLIA_API_KEY = '33e4e3564f4f060e96e0531957bed552';\nconst DEFAULT_ALGOLIA_INDEX = 'appsearch';\n\nexport interface ShuffleMCPHandle {\n search: (query: string) => void;\n clear: () => void;\n}\n\nexport const ShuffleMCP = React.forwardRef<ShuffleMCPHandle, ShuffleMCPProps>(({\n authToken,\n orgId,\n placeholder = 'Search apps...',\n layout = 'list',\n gridColumns = 3,\n showDescription = false,\n showCategories = false,\n showCheckbox = false,\n multiSelect = false,\n selectedApps = [],\n preventDefault = false,\n inline = false,\n initialQuery = '',\n initialFilterQuery,\n hitsPerPage = 15,\n apiKey,\n apiBaseUrl = 'https://shuffler.io',\n authPath = '/api/v1/apps/authentication',\n appAuthPath = '/appauth',\n privateAppsPath = '/api/v1/apps',\n disablePrivateApps = false,\n showSourceFilter = true,\n singulBaseUrl = 'https://singul.io',\n hideAuthStatus = false,\n authenticatedApps: externalAuthenticatedApps,\n pinnedApps,\n algoliaAppId = DEFAULT_ALGOLIA_APP_ID,\n algoliaApiKey = DEFAULT_ALGOLIA_API_KEY,\n algoliaIndexName = DEFAULT_ALGOLIA_INDEX,\n customStyles = {},\n className = '',\n renderItem,\n renderEmptyState,\n renderLoadingState,\n onAppSelected,\n onSelectionChange,\n onSearchChange,\n}, ref) => {\n const [query, setQuery] = useState(initialQuery);\n const [results, setResults] = useState<AlgoliaSearchApp[]>([]);\n const [privateApps, setPrivateApps] = useState<AlgoliaSearchApp[]>([]);\n const [sourceFilter, setSourceFilter] = useState<'all' | 'public' | 'private'>('all');\n const [isLoading, setIsLoading] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const [internalSelectedApps, setInternalSelectedApps] = useState<AlgoliaSearchApp[]>(selectedApps);\n const [authenticatedApps, setAuthenticatedApps] = useState<AppAuthentication[]>(externalAuthenticatedApps || []);\n const [drawerApp, setDrawerApp] = useState<AlgoliaSearchApp | null>(null);\n const hasInitialized = useRef(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const searchClient = useRef<SearchClient | null>(null);\n\n // Fetch authenticated apps when apiKey is provided\n useEffect(() => {\n if (apiKey) {\n const fetchAuthenticatedApps = async () => {\n try {\n const response = await fetch(`${apiBaseUrl}${authPath}`, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n ...(orgId ? { 'Org-Id': orgId } : {}),\n },\n });\n if (response.ok) {\n const result = await response.json();\n // Handle both { success: true, data: [...] } and direct array response\n const authData = result.data || result;\n if (Array.isArray(authData)) {\n console.log('Loaded authenticated apps:', authData.length, authData.map(a => a.app?.name));\n setAuthenticatedApps(authData);\n }\n }\n } catch (error) {\n console.error('Failed to fetch authenticated apps:', error);\n }\n };\n fetchAuthenticatedApps();\n }\n }, [apiKey, apiBaseUrl, orgId]);\n\n // Fetch the user's private apps from /api/v1/apps when apiKey is provided.\n // These get merged into search results so users can find their own apps too.\n useEffect(() => {\n if (!apiKey || disablePrivateApps) {\n setPrivateApps([]);\n return;\n }\n const fetchPrivateApps = async () => {\n try {\n const response = await fetch(`${apiBaseUrl}${privateAppsPath}`, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n ...(orgId ? { 'Org-Id': orgId } : {}),\n },\n credentials: 'include',\n });\n if (!response.ok) return;\n const data = await response.json();\n const apps = Array.isArray(data) ? data : (data?.data || []);\n // Normalize to AlgoliaSearchApp shape, tagged as 'private' source.\n const normalized: AlgoliaSearchApp[] = apps.map((a: any) => ({\n name: a.name || '',\n description: a.description || '',\n objectID: a.id || a.objectID || `private-${a.name}`,\n creator: a.owner || a.creator || '',\n app_version: a.app_version || '1.0.0',\n image_url: a.large_image || a.image_url || '',\n time_edited: a.edited || 0,\n generated: !!a.generated,\n invalid: !!a.invalid,\n priority: a.priority || 0,\n actions: Array.isArray(a.actions) ? a.actions.length : (a.actions || 0),\n tags: a.tags || [],\n accessible_by: a.accessible_by || [],\n categories: a.categories || [],\n action_labels: a.action_labels || [],\n triggers: a.triggers || [],\n verified: !!a.verified,\n source: 'private',\n }));\n setPrivateApps(normalized);\n } catch (error) {\n console.error('Failed to fetch private apps:', error);\n }\n };\n fetchPrivateApps();\n }, [apiKey, apiBaseUrl, privateAppsPath, disablePrivateApps, orgId]);\n\n // Auto-select validated apps when they're loaded (validated = tests ran successfully)\n useEffect(() => {\n if (authenticatedApps.length > 0 && results.length > 0) {\n const validatedApps = authenticatedApps.filter(auth => auth.validation?.valid);\n const appsToAutoSelect = results.filter(app => \n validatedApps.some(auth => auth.app.name.toLowerCase() === app.name.toLowerCase())\n );\n \n if (appsToAutoSelect.length > 0) {\n const newSelection = [...internalSelectedApps];\n appsToAutoSelect.forEach(app => {\n if (!newSelection.some(a => a.objectID === app.objectID)) {\n newSelection.push(app);\n }\n });\n if (newSelection.length !== internalSelectedApps.length) {\n setInternalSelectedApps(newSelection);\n onSelectionChange?.(newSelection);\n }\n }\n }\n }, [authenticatedApps, results]);\n\n // Sync external authenticatedApps\n useEffect(() => {\n if (externalAuthenticatedApps) {\n setAuthenticatedApps(externalAuthenticatedApps);\n }\n }, [externalAuthenticatedApps]);\n\n // Initialize Algolia client and run initial search\n useEffect(() => {\n searchClient.current = algoliasearch(algoliaAppId, algoliaApiKey);\n \n // Run initial search after client is ready\n if (!hasInitialized.current) {\n hasInitialized.current = true;\n const searchTerm = initialQuery || initialFilterQuery || '';\n setTimeout(() => {\n if (searchClient.current) {\n performSearch(searchTerm);\n onSearchChange?.(initialQuery); // Only report the visible query\n }\n }, 100);\n }\n }, []);\n\n // Sync external selectedApps\n useEffect(() => {\n setInternalSelectedApps(selectedApps);\n }, [selectedApps]);\n\n // Handle click outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n setSelectedIndex(-1);\n }\n };\n\n document.addEventListener('click', handleClickOutside);\n return () => document.removeEventListener('click', handleClickOutside);\n }, []);\n\n // Check if an app is configured (found in authentication API with active: true)\n const isAppConfigured = useCallback((app: AlgoliaSearchApp) => {\n return authenticatedApps.some(\n auth => auth.app.name.toLowerCase() === app.name.toLowerCase() && auth.active === true\n );\n }, [authenticatedApps]);\n\n // Check if an app is validated/tested (validation.valid is true)\n const isAppValidated = useCallback((app: AlgoliaSearchApp) => {\n return authenticatedApps.some(\n auth => auth.app.name.toLowerCase() === app.name.toLowerCase() && auth.validation?.valid === true\n );\n }, [authenticatedApps]);\n\n // Get auth state for an app\n const getAppAuthState = useCallback((app: AlgoliaSearchApp) => {\n return {\n configured: isAppConfigured(app),\n validated: isAppValidated(app),\n };\n }, [isAppConfigured, isAppValidated]);\n\n // Perform search\n const performSearch = useCallback(async (searchQuery: string) => {\n if (!searchClient.current) {\n return;\n }\n\n setIsLoading(true);\n try {\n const searchResult = await searchClient.current.searchSingleIndex({\n indexName: algoliaIndexName,\n searchParams: {\n query: searchQuery || '', // Empty string gets top results\n hitsPerPage,\n },\n });\n\n const hits = (searchResult.hits as AlgoliaSearchApp[]).map(h => ({ ...h, source: 'public' as const }));\n setResults(hits);\n setIsOpen(hits.length > 0);\n setSelectedIndex(-1);\n } catch (error) {\n console.error('Search failed:', error);\n setResults([]);\n setIsOpen(false);\n } finally {\n setIsLoading(false);\n }\n }, [hitsPerPage]);\n\n // Expose imperative methods via ref\n useImperativeHandle(ref, () => ({\n search: (searchQuery: string) => {\n setQuery(searchQuery);\n onSearchChange?.(searchQuery);\n performSearch(searchQuery);\n },\n clear: () => {\n setQuery('');\n performSearch(''); // Show top results when cleared\n },\n getQuery: () => query,\n }), [performSearch, onSearchChange, query]);\n\n // Handle input change\n const handleInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.target.value;\n setQuery(value);\n setSelectedIndex(-1);\n onSearchChange?.(value);\n\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n debounceTimer.current = setTimeout(() => {\n performSearch(value);\n }, 300);\n }, [performSearch, onSearchChange]);\n\n // Handle app selection\n const selectApp = useCallback((app: AlgoliaSearchApp) => {\n const authUrl = `${apiBaseUrl}${appAuthPath}?app_id=${app.objectID}&auth=${authToken}&source=shuffle${orgId ? `&org_id=${encodeURIComponent(orgId)}` : ''}`;\n\n if (multiSelect) {\n const isAlreadySelected = internalSelectedApps.some((a) => a.objectID === app.objectID);\n const newSelection = isAlreadySelected\n ? internalSelectedApps.filter((a) => a.objectID !== app.objectID)\n : [...internalSelectedApps, app];\n\n setInternalSelectedApps(newSelection);\n onSelectionChange?.(newSelection);\n } else {\n // If consumer wired up onAppSelected, defer entirely to them.\n if (onAppSelected) {\n onAppSelected({ app, authUrl });\n if (!preventDefault) {\n window.open(authUrl, '_blank');\n }\n } else if (!preventDefault) {\n // New default: open built-in side drawer with the app + its existing\n // authentications, instead of popping a new tab to shuffler.io.\n setDrawerApp(app);\n }\n\n setIsOpen(false);\n }\n }, [authToken, apiBaseUrl, appAuthPath, orgId, multiSelect, internalSelectedApps, onAppSelected, onSelectionChange, preventDefault]);\n\n // Handle keyboard navigation\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (!isOpen) return;\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n setSelectedIndex((prev) => Math.min(prev + 1, displayResults.length - 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n setSelectedIndex((prev) => Math.max(prev - 1, -1));\n break;\n case 'Enter':\n event.preventDefault();\n if (selectedIndex >= 0 && displayResults[selectedIndex]) {\n selectApp(displayResults[selectedIndex]);\n }\n break;\n case 'Escape':\n event.preventDefault();\n setIsOpen(false);\n setSelectedIndex(-1);\n inputRef.current?.blur();\n break;\n }\n }, [isOpen, results, selectedIndex, selectApp]);\n\n // Check if app is selected\n const isAppSelected = useCallback((app: AlgoliaSearchApp) => {\n return internalSelectedApps.some((a) => a.objectID === app.objectID);\n }, [internalSelectedApps]);\n\n // Filter the user's private apps client-side by the current query.\n const filteredPrivateApps = useMemo(() => {\n if (privateApps.length === 0) return [];\n const q = query.trim().toLowerCase();\n if (!q) return privateApps;\n return privateApps.filter(a =>\n a.name.toLowerCase().includes(q) ||\n (a.description || '').toLowerCase().includes(q) ||\n (a.categories || []).some(c => c.toLowerCase().includes(q))\n );\n }, [privateApps, query]);\n\n // Merge private + public apps, apply source filter, prepend pinned, dedupe by name.\n const displayResults = useMemo(() => {\n const norm = (n: string) => (n || '').toLowerCase().replace(/[\\s_\\-]+/g, '');\n\n let merged: AlgoliaSearchApp[];\n if (sourceFilter === 'public') {\n merged = results;\n } else if (sourceFilter === 'private') {\n merged = filteredPrivateApps;\n } else {\n // 'all' — private apps first (your own tools win), then public, deduped by name\n const privateNames = new Set(filteredPrivateApps.map(a => norm(a.name)));\n const publicOnly = results.filter(a => !privateNames.has(norm(a.name)));\n merged = [...filteredPrivateApps, ...publicOnly];\n }\n\n if (!pinnedApps || pinnedApps.length === 0) return merged;\n const pinnedNames = new Set(pinnedApps.map(a => norm(a.name)));\n return [...pinnedApps, ...merged.filter(a => !pinnedNames.has(norm(a.name)))];\n }, [pinnedApps, results, filteredPrivateApps, sourceFilter]);\n\n // Get grid columns style\n const getGridColumnsStyle = useMemo(() => {\n if (layout !== 'grid') return {};\n \n const cols = typeof gridColumns === 'number' ? gridColumns : gridColumns.md || 3;\n return { '--singul-grid-columns': cols } as React.CSSProperties;\n }, [layout, gridColumns]);\n\n // Render individual app item\n const renderAppItem = (app: AlgoliaSearchApp, index: number) => {\n const selected = isAppSelected(app);\n const isHighlighted = index === selectedIndex;\n const authState = getAppAuthState(app);\n\n // Use custom render if provided\n if (renderItem) {\n return (\n <div\n key={app.objectID}\n onClick={() => selectApp(app)}\n onMouseEnter={() => setSelectedIndex(index)}\n >\n {renderItem(app, selected, () => selectApp(app), authState)}\n </div>\n );\n }\n\n // Show both chips (configured/not configured AND tested/not tested)\n return (\n <div\n key={app.objectID}\n className={`singul-dropdown-item ${selected ? 'singul-selected' : ''} ${authState.validated ? 'singul-validated' : ''} ${authState.configured ? 'singul-configured' : ''}`}\n style={{\n ...customStyles.dropdownItem,\n ...(selected ? customStyles.selectedItem : {}),\n }}\n onClick={() => selectApp(app)}\n >\n <div className=\"singul-app-info\" style={customStyles.appInfo}>\n {app.image_url && (\n <div style={{ position: 'relative', flexShrink: 0 }}>\n <img\n src={app.image_url}\n alt={app.name}\n className=\"singul-app-icon\"\n style={customStyles.appIcon}\n />\n {/* Color-coded status dot: green=tested, yellow=configured, blue=activated/selected, gray=inactive */}\n {!hideAuthStatus && (\n <span\n className={`singul-status-dot ${\n authState.validated ? 'singul-dot-validated' :\n authState.configured ? 'singul-dot-configured' :\n selected ? 'singul-dot-activated' :\n 'singul-dot-inactive'\n }`}\n />\n )}\n </div>\n )}\n <div className=\"singul-app-details\" style={customStyles.appDetails}>\n <span className=\"singul-app-name\" style={customStyles.appName}>\n {app.name.replace(/_/g, ' ')}\n {app.source === 'private' && (\n <span className=\"singul-private-badge\" title=\"From your private apps\">Private</span>\n )}\n </span>\n {showDescription && app.description && (\n <span className=\"singul-app-description\" style={customStyles.appDescription}>\n {app.description}\n </span>\n )}\n {showCategories && app.categories?.[0] && (\n <span className=\"singul-app-category\" style={customStyles.appCategory}>\n {app.categories[0]}\n </span>\n )}\n </div>\n {showCheckbox && (\n <div\n className={`singul-checkbox ${selected ? 'singul-checked' : ''}`}\n style={selected ? customStyles.checkboxChecked : customStyles.checkbox}\n >\n {selected && (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n )}\n </div>\n )}\n </div>\n </div>\n );\n };\n\n return (\n <div\n ref={containerRef}\n className={`singul-container ${inline ? 'singul-inline' : ''} ${className}`}\n style={customStyles.container}\n >\n <div className=\"singul-search-bar-container\">\n <div className=\"singul-search-input-wrapper\" style={customStyles.inputWrapper}>\n <input\n ref={inputRef}\n type=\"text\"\n className=\"singul-search-input\"\n style={customStyles.input}\n placeholder={placeholder}\n value={query}\n onChange={handleInputChange}\n onFocus={() => {\n if (!inline && query.trim() && results.length > 0) {\n setIsOpen(true);\n }\n }}\n onKeyDown={handleKeyDown}\n autoComplete=\"off\"\n />\n <div className=\"singul-search-icon\" style={customStyles.searchIcon}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.35-4.35\" />\n </svg>\n </div>\n {isLoading && (\n <div className=\"singul-loading-spinner\" style={customStyles.loadingSpinner}>\n {renderLoadingState ? (\n renderLoadingState()\n ) : (\n <div className=\"singul-spinner\" style={customStyles.spinner} />\n )}\n </div>\n )}\n </div>\n\n {/* Source filter — only shown when private apps are loaded */}\n {showSourceFilter && privateApps.length > 0 && (\n <div className=\"singul-source-filter\" role=\"tablist\" aria-label=\"Filter by app source\">\n {([\n { key: 'all', label: 'All', count: results.length + filteredPrivateApps.length },\n { key: 'public', label: 'Public', count: results.length },\n { key: 'private', label: 'Private', count: filteredPrivateApps.length },\n ] as const).map(opt => (\n <button\n key={opt.key}\n type=\"button\"\n role=\"tab\"\n aria-selected={sourceFilter === opt.key}\n className={`singul-source-pill ${sourceFilter === opt.key ? 'singul-source-pill-active' : ''}`}\n onClick={() => setSourceFilter(opt.key)}\n >\n {opt.label}\n <span className=\"singul-source-count\">{opt.count}</span>\n </button>\n ))}\n </div>\n )}\n\n {/* Inline Results Container */}\n {inline && (\n <div\n className={`singul-results-container ${layout === 'grid' ? 'singul-results-grid' : ''}`}\n style={{\n ...customStyles.resultsContainer,\n ...getGridColumnsStyle,\n ...(layout === 'grid' ? { display: 'grid' } : {}),\n }}\n >\n {displayResults.length > 0 ? (\n <>\n {displayResults.map((app, index) => renderAppItem(app, index))}\n <div className=\"singul-end-of-results\" style={{\n padding: '10px 16px',\n color: 'rgba(255, 255, 255, 0.3)',\n textAlign: 'center' as const,\n fontSize: '11px',\n gridColumn: '1 / -1',\n }}>\n Can't find what you're looking for? Try a different search term.\n </div>\n </>\n ) : query.trim() ? (\n <div className=\"singul-empty-state\" style={customStyles.emptyState}>\n {renderEmptyState ? (\n renderEmptyState()\n ) : (\n <>No integrations match \"{query}\". Try searching for a different app or category.</>\n )}\n </div>\n ) : (\n <div className=\"singul-empty-state\" style={customStyles.emptyState}>\n Start typing to search integrations...\n </div>\n )}\n </div>\n )}\n\n {/* Dropdown Results (non-inline mode) */}\n {!inline && isOpen && (\n <div\n className={`singul-dropdown ${layout === 'grid' ? 'singul-dropdown-grid' : ''}`}\n style={{\n ...customStyles.dropdown,\n ...getGridColumnsStyle,\n ...(layout === 'grid' ? { display: 'grid' } : {}),\n }}\n >\n {displayResults.length > 0 ? (\n displayResults.map((app, index) => renderAppItem(app, index))\n ) : (\n <div className=\"singul-empty-state\" style={customStyles.emptyState}>\n {renderEmptyState ? (\n renderEmptyState()\n ) : (\n <>No apps found for \"{query}\"</>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Built-in app auth drawer — shown when no custom onAppSelected handler is provided */}\n {drawerApp && (() => {\n const norm = (n: string) => (n || '').toLowerCase().replace(/[\\s_\\-]+/g, '_');\n const matchingAuths = authenticatedApps.filter(\n a => norm(a.app?.name || '') === norm(drawerApp.name)\n );\n const drawerAuthUrl = `${apiBaseUrl}${appAuthPath}?app_id=${drawerApp.objectID}&auth=${authToken}&source=shuffle${orgId ? `&org_id=${encodeURIComponent(orgId)}` : ''}`;\n return (\n <>\n <div className=\"singul-drawer-backdrop\" onClick={() => setDrawerApp(null)} />\n <aside className=\"singul-drawer\" role=\"dialog\" aria-label={`${drawerApp.name} configuration`}>\n <header className=\"singul-drawer-header\">\n <div className=\"singul-drawer-title-row\">\n {drawerApp.image_url && (\n <img src={drawerApp.image_url} alt={drawerApp.name} className=\"singul-drawer-icon\" />\n )}\n <div>\n <div className=\"singul-drawer-title\">{drawerApp.name.replace(/_/g, ' ')}</div>\n <div className=\"singul-drawer-subtitle\">App configuration</div>\n </div>\n </div>\n <button\n type=\"button\"\n className=\"singul-drawer-close\"\n onClick={() => setDrawerApp(null)}\n aria-label=\"Close\"\n >×</button>\n </header>\n\n <div className=\"singul-drawer-body\">\n {drawerApp.description && (\n <p className=\"singul-drawer-desc\">{drawerApp.description}</p>\n )}\n\n <div className=\"singul-drawer-section-title\">\n Authentication\n <span className=\"singul-drawer-count\">\n {matchingAuths.length} configuration{matchingAuths.length === 1 ? '' : 's'} found\n </span>\n </div>\n\n {matchingAuths.length > 0 ? (\n <ul className=\"singul-drawer-auth-list\">\n {matchingAuths.map(a => (\n <li key={a.id} className=\"singul-drawer-auth-item\">\n <div className=\"singul-drawer-auth-label\">{a.label || 'Untitled'}</div>\n <div className=\"singul-drawer-auth-chips\">\n {a.active && <span className=\"singul-chip singul-chip-configured\">Configured</span>}\n {a.validation?.valid && <span className=\"singul-chip singul-chip-tested\">Tested</span>}\n {!a.active && <span className=\"singul-chip singul-chip-inactive\">Inactive</span>}\n </div>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"singul-drawer-empty\">No authentications yet for this app.</div>\n )}\n\n <a\n href={drawerAuthUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"singul-drawer-cta\"\n >\n {matchingAuths.length > 0 ? 'Manage authentication' : 'Add authentication'}\n </a>\n </div>\n </aside>\n </>\n );\n })()}\n </div>\n );\n});\n\nShuffleMCP.displayName = 'ShuffleMCP';\n\nexport default ShuffleMCP;\n"],"mappings":"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","names":["React"]}
package/dist/index.d.cts CHANGED
@@ -84,6 +84,12 @@ interface AppAuthentication {
84
84
  }
85
85
  interface ShuffleMCPProps {
86
86
  authToken: string;
87
+ /**
88
+ * Optional Shuffle organization ID. When provided, every API call this
89
+ * component makes (Shuffle backend + Singul gateway) will include an
90
+ * `Org-Id: <orgId>` header so the request is scoped to that org.
91
+ */
92
+ orgId?: string;
87
93
  placeholder?: string;
88
94
  layout?: 'list' | 'grid';
89
95
  gridColumns?: number | {
package/dist/index.d.ts CHANGED
@@ -84,6 +84,12 @@ interface AppAuthentication {
84
84
  }
85
85
  interface ShuffleMCPProps {
86
86
  authToken: string;
87
+ /**
88
+ * Optional Shuffle organization ID. When provided, every API call this
89
+ * component makes (Shuffle backend + Singul gateway) will include an
90
+ * `Org-Id: <orgId>` header so the request is scoped to that org.
91
+ */
92
+ orgId?: string;
87
93
  placeholder?: string;
88
94
  layout?: 'list' | 'grid';
89
95
  gridColumns?: number | {