@shuffleio/shuffle-mcps 0.0.2 → 0.0.3

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
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- <img src="https://unpkg.com/@shuffleio/shuffle-mcps/docs/agent-icon.png" alt="Shuffle MCPs" width="96" />
3
+ <img src="https://security.shuffler.io/mcp/agent-icon.png" alt="Shuffle MCPs" width="96" />
4
4
 
5
5
  # Shuffle MCPs
6
6
 
@@ -17,11 +17,11 @@ Turn 3,000+ SaaS tools into MCP servers your agents can use. One search, one cli
17
17
  <table align="center">
18
18
  <tr>
19
19
  <td align="center" width="50%">
20
- <img src="https://unpkg.com/@shuffleio/shuffle-mcps/docs/search-preview.png" alt="Search 3,000+ integrations" width="380" style="border-radius: 12px;" />
20
+ <img src="https://security.shuffler.io/mcp/search-preview.png" alt="Search 3,000+ integrations" width="380" style="border-radius: 12px;" />
21
21
  <br/><sub><b>1. Search any of 3,000+ tools</b></sub>
22
22
  </td>
23
23
  <td align="center" width="50%">
24
- <img src="https://unpkg.com/@shuffleio/shuffle-mcps/docs/app-detail-preview.png" alt="Authenticate and use as MCP" width="380" style="border-radius: 12px;" />
24
+ <img src="https://security.shuffler.io/mcp/app-detail-preview.png" alt="Authenticate and use as MCP" width="380" style="border-radius: 12px;" />
25
25
  <br/><sub><b>2. Authenticate — your agent uses it instantly</b></sub>
26
26
  </td>
27
27
  </tr>
@@ -38,10 +38,18 @@ Turn 3,000+ SaaS tools into MCP servers your agents can use. One search, one cli
38
38
  Authenticate any tool at [shuffler.io](https://shuffler.io), grab your API key, and call it:
39
39
 
40
40
  ```bash
41
- curl https://shuffler.io/api/v1/apps/YOUR_APP/run \
41
+ curl https://shuffler.io/api/v1/apps/YOUR_APP/mcp \
42
42
  -H "Authorization: Bearer YOUR_API_KEY" \
43
43
  -H "Content-Type: application/json" \
44
- -d '{"action": "send_message", "fields": {"channel": "general", "text": "hi"}}'
44
+ -d '{
45
+ "jsonrpc": "2.0",
46
+ "id": "1",
47
+ "method": "tools/call",
48
+ "params": {
49
+ "tool_name": "YOUR_APP",
50
+ "input": { "text": "send a hi message to #general on slack" }
51
+ }
52
+ }'
45
53
  ```
46
54
 
47
55
  That is the MCP. Any agent, any language, no install. Full API reference: [shuffler.io/docs/API](https://shuffler.io/docs/API).
@@ -61,11 +69,11 @@ Peer deps: `react >= 18`, `react-dom >= 18`, `algoliasearch >= 5`.
61
69
  ## Quick start
62
70
 
63
71
  ```tsx
64
- import { SingulJS } from 'shuffle-mcps';
72
+ import { ShuffleMCP } from 'shuffle-mcps';
65
73
 
66
74
  export default function App() {
67
75
  return (
68
- <SingulJS
76
+ <ShuffleMCP
69
77
  authToken="any-token"
70
78
  inline
71
79
  layout="grid"
@@ -83,7 +91,7 @@ export default function App() {
83
91
  The most common pattern. Set `preventDefault` and handle `onAppSelected` to chain into your own auth or detail UI:
84
92
 
85
93
  ```tsx
86
- <SingulJS
94
+ <ShuffleMCP
87
95
  authToken={token}
88
96
  inline
89
97
  preventDefault
@@ -96,7 +104,7 @@ Full two-drawer reference implementation: [`src/components/shared/AppSearchDrawe
96
104
  ### Predefined search
97
105
 
98
106
  ```tsx
99
- <SingulJS
107
+ <ShuffleMCP
100
108
  authToken="..."
101
109
  inline
102
110
  initialFilterQuery="siem"
@@ -109,7 +117,7 @@ Full two-drawer reference implementation: [`src/components/shared/AppSearchDrawe
109
117
  ```tsx
110
118
  const [picked, setPicked] = useState<AlgoliaSearchApp[]>([]);
111
119
 
112
- <SingulJS
120
+ <ShuffleMCP
113
121
  authToken="..."
114
122
  inline
115
123
  multiSelect
@@ -122,7 +130,7 @@ const [picked, setPicked] = useState<AlgoliaSearchApp[]>([]);
122
130
  ### Your own backend
123
131
 
124
132
  ```tsx
125
- <SingulJS
133
+ <ShuffleMCP
126
134
  authToken={user.token}
127
135
  apiKey={user.apiKey}
128
136
  apiBaseUrl="https://your-backend.example.com"
@@ -153,8 +161,8 @@ Full prop reference, framework setup (Next.js, Vue), styling slots, custom rende
153
161
  ## Imperative handle
154
162
 
155
163
  ```tsx
156
- const ref = useRef<SingulJSHandle>(null);
157
- <SingulJS ref={ref} authToken="..." inline />
164
+ const ref = useRef<ShuffleMCPHandle>(null);
165
+ <ShuffleMCP ref={ref} authToken="..." inline />
158
166
 
159
167
  ref.current?.search('slack');
160
168
  ref.current?.clear();
package/dist/index.cjs CHANGED
@@ -29,12 +29,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // index.ts
30
30
  var index_exports = {};
31
31
  __export(index_exports, {
32
- SingulJS: () => SingulJS,
33
- default: () => SingulJS_default
32
+ ShuffleMCP: () => ShuffleMCP,
33
+ default: () => ShuffleMCP_default
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
- // SingulJS.tsx
37
+ // ShuffleMCP.tsx
38
38
  var import_react = __toESM(require("react"), 1);
39
39
  var import_algoliasearch = require("algoliasearch");
40
40
  var import_singul = require("./singul-GZKBHJNI.css");
@@ -42,7 +42,7 @@ 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
- var SingulJS = import_react.default.forwardRef(({
45
+ var ShuffleMCP = import_react.default.forwardRef(({
46
46
  authToken,
47
47
  placeholder = "Search apps...",
48
48
  layout = "list",
@@ -510,10 +510,10 @@ var SingulJS = import_react.default.forwardRef(({
510
510
  }
511
511
  );
512
512
  });
513
- SingulJS.displayName = "SingulJS";
514
- var SingulJS_default = SingulJS;
513
+ ShuffleMCP.displayName = "ShuffleMCP";
514
+ var ShuffleMCP_default = ShuffleMCP;
515
515
  // Annotate the CommonJS export names for ESM import in node:
516
516
  0 && (module.exports = {
517
- SingulJS
517
+ ShuffleMCP
518
518
  });
519
519
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../SingulJS.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 { SingulJS, default } from './SingulJS';\nexport type { SingulJSHandle } from './SingulJS';\nexport type {\n AlgoliaSearchApp,\n AppSelectedEvent,\n AppAuthentication,\n CustomStyles,\n SingulJSProps,\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, SingulJSProps, 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 SingulJSHandle {\n search: (query: string) => void;\n clear: () => void;\n}\n\nexport const SingulJS = React.forwardRef<SingulJSHandle, SingulJSProps>(({\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\nSingulJS.displayName = 'SingulJS';\n\nexport default SingulJS;\n"],"mappings":"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","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 './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":"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","names":["React"]}
package/dist/index.d.cts CHANGED
@@ -82,7 +82,7 @@ interface AppAuthentication {
82
82
  error?: string;
83
83
  };
84
84
  }
85
- interface SingulJSProps {
85
+ interface ShuffleMCPProps {
86
86
  authToken: string;
87
87
  placeholder?: string;
88
88
  layout?: 'list' | 'grid';
@@ -154,10 +154,10 @@ interface SingulJSProps {
154
154
  * https://github.com/Shuffle/singul.js
155
155
  */
156
156
 
157
- interface SingulJSHandle {
157
+ interface ShuffleMCPHandle {
158
158
  search: (query: string) => void;
159
159
  clear: () => void;
160
160
  }
161
- declare const SingulJS: React$1.ForwardRefExoticComponent<SingulJSProps & React$1.RefAttributes<SingulJSHandle>>;
161
+ declare const ShuffleMCP: React$1.ForwardRefExoticComponent<ShuffleMCPProps & React$1.RefAttributes<ShuffleMCPHandle>>;
162
162
 
163
- export { type AlgoliaSearchApp, type AppAuthentication, type AppSelectedEvent, type CustomStyles, SingulJS, type SingulJSHandle, type SingulJSProps, SingulJS as default };
163
+ export { type AlgoliaSearchApp, type AppAuthentication, type AppSelectedEvent, type CustomStyles, ShuffleMCP, type ShuffleMCPHandle, type ShuffleMCPProps, ShuffleMCP as default };
package/dist/index.d.ts CHANGED
@@ -82,7 +82,7 @@ interface AppAuthentication {
82
82
  error?: string;
83
83
  };
84
84
  }
85
- interface SingulJSProps {
85
+ interface ShuffleMCPProps {
86
86
  authToken: string;
87
87
  placeholder?: string;
88
88
  layout?: 'list' | 'grid';
@@ -154,10 +154,10 @@ interface SingulJSProps {
154
154
  * https://github.com/Shuffle/singul.js
155
155
  */
156
156
 
157
- interface SingulJSHandle {
157
+ interface ShuffleMCPHandle {
158
158
  search: (query: string) => void;
159
159
  clear: () => void;
160
160
  }
161
- declare const SingulJS: React$1.ForwardRefExoticComponent<SingulJSProps & React$1.RefAttributes<SingulJSHandle>>;
161
+ declare const ShuffleMCP: React$1.ForwardRefExoticComponent<ShuffleMCPProps & React$1.RefAttributes<ShuffleMCPHandle>>;
162
162
 
163
- export { type AlgoliaSearchApp, type AppAuthentication, type AppSelectedEvent, type CustomStyles, SingulJS, type SingulJSHandle, type SingulJSProps, SingulJS as default };
163
+ export { type AlgoliaSearchApp, type AppAuthentication, type AppSelectedEvent, type CustomStyles, ShuffleMCP, type ShuffleMCPHandle, type ShuffleMCPProps, ShuffleMCP as default };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // SingulJS.tsx
1
+ // ShuffleMCP.tsx
2
2
  import React, { useState, useEffect, useRef, useCallback, useMemo, useImperativeHandle } from "react";
3
3
  import { algoliasearch } from "algoliasearch";
4
4
  import "./singul-GZKBHJNI.css";
@@ -6,7 +6,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  var DEFAULT_ALGOLIA_APP_ID = "JNSS5CFDZZ";
7
7
  var DEFAULT_ALGOLIA_API_KEY = "33e4e3564f4f060e96e0531957bed552";
8
8
  var DEFAULT_ALGOLIA_INDEX = "appsearch";
9
- var SingulJS = React.forwardRef(({
9
+ var ShuffleMCP = React.forwardRef(({
10
10
  authToken,
11
11
  placeholder = "Search apps...",
12
12
  layout = "list",
@@ -474,10 +474,10 @@ var SingulJS = React.forwardRef(({
474
474
  }
475
475
  );
476
476
  });
477
- SingulJS.displayName = "SingulJS";
478
- var SingulJS_default = SingulJS;
477
+ ShuffleMCP.displayName = "ShuffleMCP";
478
+ var ShuffleMCP_default = ShuffleMCP;
479
479
  export {
480
- SingulJS,
481
- SingulJS_default as default
480
+ ShuffleMCP,
481
+ ShuffleMCP_default as default
482
482
  };
483
483
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../SingulJS.tsx"],"sourcesContent":["/**\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, SingulJSProps, 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 SingulJSHandle {\n search: (query: string) => void;\n clear: () => void;\n}\n\nexport const SingulJS = React.forwardRef<SingulJSHandle, SingulJSProps>(({\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\nSingulJS.displayName = 'SingulJS';\n\nexport default SingulJS;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../ShuffleMCP.tsx"],"sourcesContent":["/**\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":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shuffleio/shuffle-mcps",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Turn 3,000+ SaaS tools into MCP servers your agents can use. Headless React component for search, selection, and OAuth handoff.",
5
5
  "license": "MIT",
6
6
  "repository": {