@tangle-network/sandbox-ui 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{WorkflowGraph-5QO6WCY2.js → WorkflowGraph-7YYGIBNV.js} +15 -1
- package/dist/assistant.d.ts +36 -2
- package/dist/assistant.js +188 -56
- package/dist/chunk-H7GMPMPB.js +191 -0
- package/dist/globals.css +6 -0
- package/dist/integrations.js +13 -131
- package/dist/styles.css +6 -0
- package/dist/workflows.js +51 -12
- package/package.json +1 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProviderIcon
|
|
3
|
+
} from "./chunk-H7GMPMPB.js";
|
|
1
4
|
import {
|
|
2
5
|
buildWorkflowGraph
|
|
3
6
|
} from "./chunk-SUC6ZJZW.js";
|
|
@@ -75,7 +78,18 @@ function WorkflowNode({ data }) {
|
|
|
75
78
|
children: d.subtitle
|
|
76
79
|
}
|
|
77
80
|
),
|
|
78
|
-
d.provider && /* @__PURE__ */
|
|
81
|
+
d.provider && /* @__PURE__ */ jsxs("span", { className: "mt-1.5 inline-flex items-center gap-1 rounded bg-background-tertiary px-1.5 py-0.5 text-[10px] text-text-muted", children: [
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
ProviderIcon,
|
|
84
|
+
{
|
|
85
|
+
id: d.provider,
|
|
86
|
+
displayName: providerLabel(d.provider),
|
|
87
|
+
size: 12,
|
|
88
|
+
className: "rounded-[2px]"
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
providerLabel(d.provider)
|
|
92
|
+
] }),
|
|
79
93
|
/* @__PURE__ */ jsx(
|
|
80
94
|
Handle,
|
|
81
95
|
{
|
package/dist/assistant.d.ts
CHANGED
|
@@ -38,6 +38,9 @@ interface ReasoningEventData {
|
|
|
38
38
|
interface ToolCallEventData {
|
|
39
39
|
callId: string;
|
|
40
40
|
name: string;
|
|
41
|
+
/** The parsed arguments the agent invoked the tool with. Lets a renderer show
|
|
42
|
+
* exactly what was called. Omitted by servers predating the field. */
|
|
43
|
+
args?: Record<string, unknown>;
|
|
41
44
|
}
|
|
42
45
|
interface ToolResultEventData {
|
|
43
46
|
callId: string;
|
|
@@ -84,6 +87,9 @@ interface UsageEventData {
|
|
|
84
87
|
completionTokens: number | null;
|
|
85
88
|
costUsd: number | null;
|
|
86
89
|
balanceUsd: number | null;
|
|
90
|
+
/** Wall-clock duration of the turn in milliseconds, when the server measures
|
|
91
|
+
* it. Drives the renderer's tokens/sec figure. Omitted by older servers. */
|
|
92
|
+
durationMs?: number | null;
|
|
87
93
|
/** True when a completed turn was replayed from storage (no charge). */
|
|
88
94
|
replayed?: boolean;
|
|
89
95
|
}
|
|
@@ -151,11 +157,12 @@ interface ChatMessage {
|
|
|
151
157
|
id: string;
|
|
152
158
|
role: ChatRole;
|
|
153
159
|
text: string;
|
|
154
|
-
/** Present only on `tool` messages — the activity's tool name,
|
|
155
|
-
* finished) its outcome. */
|
|
160
|
+
/** Present only on `tool` messages — the activity's tool name, the arguments it
|
|
161
|
+
* was called with, its state, and (once finished) its outcome. */
|
|
156
162
|
tool?: {
|
|
157
163
|
name: string;
|
|
158
164
|
status: ToolActivityStatus;
|
|
165
|
+
args?: Record<string, unknown>;
|
|
159
166
|
outcome?: ToolOutcome;
|
|
160
167
|
};
|
|
161
168
|
}
|
|
@@ -176,6 +183,13 @@ interface PendingProposal {
|
|
|
176
183
|
interface UsageInfo {
|
|
177
184
|
costUsd: number | null;
|
|
178
185
|
balanceUsd: number | null;
|
|
186
|
+
/** Token counts + wall-clock duration for the settled turn, when the server
|
|
187
|
+
* reports them — drive per-message tokens/sec + cost in a renderer. Optional
|
|
188
|
+
* so a consumer constructing this on the prior `{ costUsd, balanceUsd,
|
|
189
|
+
* replayed }` shape stays valid; a renderer treats a missing value as null. */
|
|
190
|
+
promptTokens?: number | null;
|
|
191
|
+
completionTokens?: number | null;
|
|
192
|
+
durationMs?: number | null;
|
|
179
193
|
replayed: boolean;
|
|
180
194
|
}
|
|
181
195
|
/**
|
|
@@ -202,6 +216,10 @@ interface AssistantTranscriptView {
|
|
|
202
216
|
* (drives a "thinking" affordance). */
|
|
203
217
|
isThinking: boolean;
|
|
204
218
|
pendingProposals: PendingProposal[];
|
|
219
|
+
/** Cost/tokens/duration for the most recently settled turn, or null before any
|
|
220
|
+
* turn settles. Optional so a host predating the field stays a valid consumer;
|
|
221
|
+
* a renderer treats a missing value the same as null. */
|
|
222
|
+
usage?: UsageInfo | null;
|
|
205
223
|
/** The panel's bound proposal card for a pending proposal — render it where the
|
|
206
224
|
* confirm/cancel UI should appear. */
|
|
207
225
|
renderProposal: (proposal: PendingProposal) => ReactNode;
|
|
@@ -303,6 +321,13 @@ interface AssistantClient {
|
|
|
303
321
|
fetchThreadHistory(threadId: string, signal?: AbortSignal): Promise<ThreadHistoryResult>;
|
|
304
322
|
streamChat(req: ChatRequest, onEvent: (event: AssistantStreamEvent) => void, signal: AbortSignal): Promise<void>;
|
|
305
323
|
confirmProposal(proposalId: string): Promise<ConfirmResult>;
|
|
324
|
+
/** Delete a thread and its server-side turns/proposals. Resolves `{ ok }`; a
|
|
325
|
+
* 404 (already gone) is treated as success so a double-delete is harmless.
|
|
326
|
+
* Optional so a host with no delete endpoint stays a valid client — the panel
|
|
327
|
+
* hides the delete affordance when it's absent (see `useAssistantThreads`). */
|
|
328
|
+
deleteThread?(threadId: string): Promise<{
|
|
329
|
+
ok: boolean;
|
|
330
|
+
}>;
|
|
306
331
|
}
|
|
307
332
|
/**
|
|
308
333
|
* Build an assistant client bound to one host's transport. The returned methods
|
|
@@ -450,6 +475,15 @@ interface AssistantThreads {
|
|
|
450
475
|
/** Load (or reload) the thread list. Must be called to populate `threads` —
|
|
451
476
|
* the hook never fetches on mount (the panel calls this when history opens). */
|
|
452
477
|
refresh: () => void;
|
|
478
|
+
/** Delete a thread. Optimistically drops it from the list (within the current
|
|
479
|
+
* owner scope); on failure the list is reloaded to restore the true state. A
|
|
480
|
+
* no-op resolving `{ ok: false }` when the client has no `deleteThread`. */
|
|
481
|
+
remove: (threadId: string) => Promise<{
|
|
482
|
+
ok: boolean;
|
|
483
|
+
}>;
|
|
484
|
+
/** Whether the configured client supports deletion — drives whether a host
|
|
485
|
+
* shows the delete affordance. */
|
|
486
|
+
canRemove: boolean;
|
|
453
487
|
}
|
|
454
488
|
declare function useAssistantThreads(userId: string | null): AssistantThreads;
|
|
455
489
|
|
package/dist/assistant.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProviderIcon
|
|
3
|
+
} from "./chunk-H7GMPMPB.js";
|
|
1
4
|
import {
|
|
2
5
|
providerLabel
|
|
3
6
|
} from "./chunk-7JLB7UZ3.js";
|
|
@@ -443,6 +446,18 @@ function createAssistantClient(config) {
|
|
|
443
446
|
ok: false,
|
|
444
447
|
error: body?.error?.message ?? "The action could not be completed"
|
|
445
448
|
};
|
|
449
|
+
},
|
|
450
|
+
async deleteThread(threadId) {
|
|
451
|
+
try {
|
|
452
|
+
const res = await fetch(url(`/threads/${encodeURIComponent(threadId)}`), {
|
|
453
|
+
method: "DELETE",
|
|
454
|
+
headers: authHeaders(),
|
|
455
|
+
credentials
|
|
456
|
+
});
|
|
457
|
+
return { ok: res.ok || res.status === 404 };
|
|
458
|
+
} catch {
|
|
459
|
+
return { ok: false };
|
|
460
|
+
}
|
|
446
461
|
}
|
|
447
462
|
};
|
|
448
463
|
}
|
|
@@ -845,7 +860,11 @@ function applyStreamEvent(state, event) {
|
|
|
845
860
|
id: chipId,
|
|
846
861
|
role: "tool",
|
|
847
862
|
text: "",
|
|
848
|
-
tool: {
|
|
863
|
+
tool: {
|
|
864
|
+
name: event.data.name,
|
|
865
|
+
status: "running",
|
|
866
|
+
args: event.data.args
|
|
867
|
+
}
|
|
849
868
|
};
|
|
850
869
|
return {
|
|
851
870
|
...state,
|
|
@@ -865,7 +884,14 @@ function applyStreamEvent(state, event) {
|
|
|
865
884
|
(m) => m.id === chipId ? {
|
|
866
885
|
...m,
|
|
867
886
|
text: errText,
|
|
868
|
-
|
|
887
|
+
// Preserve the args the matching tool_call recorded — the
|
|
888
|
+
// result event doesn't carry them.
|
|
889
|
+
tool: {
|
|
890
|
+
name: event.data.name,
|
|
891
|
+
status,
|
|
892
|
+
args: m.tool?.args,
|
|
893
|
+
outcome
|
|
894
|
+
}
|
|
869
895
|
} : m
|
|
870
896
|
)
|
|
871
897
|
};
|
|
@@ -893,6 +919,9 @@ function applyStreamEvent(state, event) {
|
|
|
893
919
|
usage: {
|
|
894
920
|
costUsd: event.data.costUsd,
|
|
895
921
|
balanceUsd: event.data.balanceUsd,
|
|
922
|
+
promptTokens: event.data.promptTokens,
|
|
923
|
+
completionTokens: event.data.completionTokens,
|
|
924
|
+
durationMs: event.data.durationMs ?? null,
|
|
896
925
|
replayed: event.data.replayed ?? false
|
|
897
926
|
}
|
|
898
927
|
};
|
|
@@ -1406,6 +1435,7 @@ function useAssistantThreads(userId) {
|
|
|
1406
1435
|
const clientRef = useRef2(client);
|
|
1407
1436
|
clientRef.current = client;
|
|
1408
1437
|
const abortRef = useRef2(null);
|
|
1438
|
+
const pendingDeletesRef = useRef2(/* @__PURE__ */ new Set());
|
|
1409
1439
|
const [state, setState] = useState2(() => ({
|
|
1410
1440
|
threads: [],
|
|
1411
1441
|
loading: false,
|
|
@@ -1440,7 +1470,11 @@ function useAssistantThreads(userId) {
|
|
|
1440
1470
|
if (!isCurrent()) return;
|
|
1441
1471
|
setState((s) => ({
|
|
1442
1472
|
// null = transient failure: keep the prior list, just drop the spinner.
|
|
1443
|
-
|
|
1473
|
+
// Drop any in-flight/finished deletions so a stale fetch can't resurrect
|
|
1474
|
+
// a row we already removed.
|
|
1475
|
+
threads: (result ?? s.threads).filter(
|
|
1476
|
+
(t) => !pendingDeletesRef.current.has(t.id)
|
|
1477
|
+
),
|
|
1444
1478
|
loading: false,
|
|
1445
1479
|
loaded: true,
|
|
1446
1480
|
ownerUserId: requestedUserId,
|
|
@@ -1452,13 +1486,45 @@ function useAssistantThreads(userId) {
|
|
|
1452
1486
|
}
|
|
1453
1487
|
});
|
|
1454
1488
|
}, []);
|
|
1455
|
-
|
|
1489
|
+
const remove = useCallback2(
|
|
1490
|
+
async (threadId) => {
|
|
1491
|
+
const requestedClient = clientRef.current;
|
|
1492
|
+
const requestedUserId = userRef.current;
|
|
1493
|
+
if (!requestedClient.deleteThread) return { ok: false };
|
|
1494
|
+
pendingDeletesRef.current.add(threadId);
|
|
1495
|
+
setState(
|
|
1496
|
+
(s) => s.ownerClient === requestedClient && s.ownerUserId === requestedUserId ? { ...s, threads: s.threads.filter((t) => t.id !== threadId) } : s
|
|
1497
|
+
);
|
|
1498
|
+
let res;
|
|
1499
|
+
try {
|
|
1500
|
+
res = await requestedClient.deleteThread(threadId);
|
|
1501
|
+
} catch {
|
|
1502
|
+
res = { ok: false };
|
|
1503
|
+
}
|
|
1504
|
+
if (!res.ok) {
|
|
1505
|
+
pendingDeletesRef.current.delete(threadId);
|
|
1506
|
+
if (userRef.current === requestedUserId && clientRef.current === requestedClient) {
|
|
1507
|
+
refresh();
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
return res;
|
|
1511
|
+
},
|
|
1512
|
+
[refresh]
|
|
1513
|
+
);
|
|
1514
|
+
useEffect3(() => {
|
|
1515
|
+
return () => abortRef.current?.abort();
|
|
1516
|
+
}, [userId, client]);
|
|
1517
|
+
useEffect3(() => {
|
|
1518
|
+
return () => pendingDeletesRef.current.clear();
|
|
1519
|
+
}, []);
|
|
1456
1520
|
const stale = state.ownerUserId !== userId || state.ownerClient !== client;
|
|
1457
1521
|
return {
|
|
1458
1522
|
threads: stale ? [] : state.threads,
|
|
1459
1523
|
loading: stale ? false : state.loading,
|
|
1460
1524
|
loaded: stale ? false : state.loaded,
|
|
1461
|
-
refresh
|
|
1525
|
+
refresh,
|
|
1526
|
+
remove,
|
|
1527
|
+
canRemove: typeof client.deleteThread === "function"
|
|
1462
1528
|
};
|
|
1463
1529
|
}
|
|
1464
1530
|
|
|
@@ -1466,13 +1532,13 @@ function useAssistantThreads(userId) {
|
|
|
1466
1532
|
import { MessageSquare } from "lucide-react";
|
|
1467
1533
|
import {
|
|
1468
1534
|
useEffect as useEffect5,
|
|
1469
|
-
useRef as
|
|
1535
|
+
useRef as useRef6
|
|
1470
1536
|
} from "react";
|
|
1471
1537
|
|
|
1472
1538
|
// src/assistant/AssistantPanel.tsx
|
|
1473
|
-
import { AgentTimeline, ChatInput } from "@tangle-network/ui/chat";
|
|
1474
|
-
import { History, Minus, Plus,
|
|
1475
|
-
import { useState as useState5 } from "react";
|
|
1539
|
+
import { AgentTimeline, ChatInput, ThinkingIndicator } from "@tangle-network/ui/chat";
|
|
1540
|
+
import { History, MessageSquarePlus, Minus, Plus, Trash2, X } from "lucide-react";
|
|
1541
|
+
import { useRef as useRef4, useState as useState5 } from "react";
|
|
1476
1542
|
|
|
1477
1543
|
// src/assistant/build-timeline.tsx
|
|
1478
1544
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
@@ -1607,7 +1673,7 @@ function ProposalCard({
|
|
|
1607
1673
|
)
|
|
1608
1674
|
] })
|
|
1609
1675
|
] }),
|
|
1610
|
-
showGraph && tab === "graph" ? /* @__PURE__ */ jsx3("div", { className: "mt-1 h-
|
|
1676
|
+
showGraph && tab === "graph" ? /* @__PURE__ */ jsx3("div", { className: "mt-1 h-64 overflow-hidden rounded border border-border", children: renderGraph?.(view.preview.content) }) : /* @__PURE__ */ jsx3("pre", { className: "mt-1 max-h-48 overflow-auto rounded border border-border bg-muted/50 p-2 text-xs", children: /* @__PURE__ */ jsx3("code", { children: view.preview.content }) })
|
|
1611
1677
|
] }),
|
|
1612
1678
|
proposal.requirements && proposal.requirements.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded border border-border p-2", children: [
|
|
1613
1679
|
/* @__PURE__ */ jsx3("p", { className: "text-muted-foreground text-xs", children: "Integrations" }),
|
|
@@ -1674,16 +1740,33 @@ function RequirementRow({
|
|
|
1674
1740
|
const canConnect = !req.connected && req.connectUrl !== null;
|
|
1675
1741
|
const target = req.connectUrl ?? "/app/integrations";
|
|
1676
1742
|
return /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-2 text-xs", children: [
|
|
1677
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
1743
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1678
1744
|
/* @__PURE__ */ jsx3(
|
|
1679
|
-
|
|
1745
|
+
ProviderIcon,
|
|
1680
1746
|
{
|
|
1681
|
-
|
|
1682
|
-
|
|
1747
|
+
id: req.provider,
|
|
1748
|
+
displayName: label,
|
|
1749
|
+
size: 16,
|
|
1750
|
+
className: "rounded"
|
|
1683
1751
|
}
|
|
1684
1752
|
),
|
|
1685
|
-
/* @__PURE__ */ jsx3("span", { className: "text-foreground", children: kindLabel }),
|
|
1686
|
-
/* @__PURE__ */
|
|
1753
|
+
/* @__PURE__ */ jsx3("span", { className: "truncate text-foreground", children: kindLabel }),
|
|
1754
|
+
/* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [
|
|
1755
|
+
/* @__PURE__ */ jsx3(
|
|
1756
|
+
"span",
|
|
1757
|
+
{
|
|
1758
|
+
"aria-hidden": "true",
|
|
1759
|
+
className: `h-1.5 w-1.5 rounded-full ${req.connected ? "bg-primary" : "border border-muted-foreground"}`
|
|
1760
|
+
}
|
|
1761
|
+
),
|
|
1762
|
+
/* @__PURE__ */ jsx3(
|
|
1763
|
+
"span",
|
|
1764
|
+
{
|
|
1765
|
+
className: req.connected ? "text-primary" : "text-muted-foreground",
|
|
1766
|
+
children: statusText
|
|
1767
|
+
}
|
|
1768
|
+
)
|
|
1769
|
+
] })
|
|
1687
1770
|
] }),
|
|
1688
1771
|
canConnect && /* @__PURE__ */ jsxs(
|
|
1689
1772
|
"button",
|
|
@@ -1841,10 +1924,15 @@ function AssistantPanel({
|
|
|
1841
1924
|
const font = useFontScale();
|
|
1842
1925
|
const [historyOpen, setHistoryOpen] = useState5(false);
|
|
1843
1926
|
const { state } = chat;
|
|
1927
|
+
const chatRef = useRef4(chat);
|
|
1928
|
+
chatRef.current = chat;
|
|
1844
1929
|
const effectiveBalance = state.usage?.balanceUsd ?? balanceUsd;
|
|
1845
1930
|
const errorView = state.error ? presentError(state.error.code, state.error.message) : null;
|
|
1846
1931
|
const low = isLowBalance(effectiveBalance) && !errorView;
|
|
1847
1932
|
const streaming = state.status === "streaming";
|
|
1933
|
+
const firstUserText = state.messages.find((m) => m.role === "user")?.text.trim();
|
|
1934
|
+
const titleChars = firstUserText ? Array.from(firstUserText) : [];
|
|
1935
|
+
const conversationTitle = firstUserText ? titleChars.length > 60 ? `${titleChars.slice(0, 60).join("")}\u2026` : firstUserText : null;
|
|
1848
1936
|
const renderProposal = (proposal) => /* @__PURE__ */ jsx4(
|
|
1849
1937
|
ProposalCard,
|
|
1850
1938
|
{
|
|
@@ -1864,20 +1952,42 @@ function AssistantPanel({
|
|
|
1864
1952
|
return next;
|
|
1865
1953
|
});
|
|
1866
1954
|
};
|
|
1955
|
+
const deleteThread = async (threadId) => {
|
|
1956
|
+
const pre = chatRef.current.state;
|
|
1957
|
+
if (pre.threadId === threadId && pre.status !== "idle") return;
|
|
1958
|
+
if (!window.confirm("Delete this conversation? This can't be undone.")) {
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
const res = await threads.remove(threadId);
|
|
1962
|
+
const live = chatRef.current.state;
|
|
1963
|
+
if (res.ok && live.threadId === threadId && live.status === "idle") {
|
|
1964
|
+
chatRef.current.reset();
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1867
1967
|
return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col bg-background", children: [
|
|
1868
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-border border-b px-4 py-3", children: [
|
|
1869
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex
|
|
1870
|
-
/* @__PURE__ */
|
|
1871
|
-
|
|
1968
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2 border-border border-b px-4 py-3", children: [
|
|
1969
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-col", children: [
|
|
1970
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-2", children: [
|
|
1971
|
+
/* @__PURE__ */ jsx4("span", { className: "font-medium text-foreground text-sm", children: "Assistant" }),
|
|
1972
|
+
/* @__PURE__ */ jsx4(
|
|
1973
|
+
"span",
|
|
1974
|
+
{
|
|
1975
|
+
"aria-label": "Your credit balance",
|
|
1976
|
+
className: "text-muted-foreground text-xs",
|
|
1977
|
+
children: formatMoney(effectiveBalance)
|
|
1978
|
+
}
|
|
1979
|
+
)
|
|
1980
|
+
] }),
|
|
1981
|
+
conversationTitle && /* @__PURE__ */ jsx4(
|
|
1872
1982
|
"span",
|
|
1873
1983
|
{
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
children:
|
|
1984
|
+
className: "truncate text-muted-foreground text-xs",
|
|
1985
|
+
title: conversationTitle,
|
|
1986
|
+
children: conversationTitle
|
|
1877
1987
|
}
|
|
1878
1988
|
)
|
|
1879
1989
|
] }),
|
|
1880
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1", children: [
|
|
1990
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
1881
1991
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center", role: "group", "aria-label": "Text size", children: [
|
|
1882
1992
|
/* @__PURE__ */ jsx4(
|
|
1883
1993
|
"button",
|
|
@@ -1919,8 +2029,9 @@ function AssistantPanel({
|
|
|
1919
2029
|
type: "button",
|
|
1920
2030
|
onClick: chat.reset,
|
|
1921
2031
|
"aria-label": "New chat",
|
|
2032
|
+
title: "New chat",
|
|
1922
2033
|
className: "rounded p-1 text-muted-foreground hover:text-foreground",
|
|
1923
|
-
children: /* @__PURE__ */ jsx4(
|
|
2034
|
+
children: /* @__PURE__ */ jsx4(MessageSquarePlus, { className: "h-4 w-4" })
|
|
1924
2035
|
}
|
|
1925
2036
|
),
|
|
1926
2037
|
/* @__PURE__ */ jsx4(
|
|
@@ -1958,18 +2069,35 @@ function AssistantPanel({
|
|
|
1958
2069
|
}
|
|
1959
2070
|
)
|
|
1960
2071
|
] }),
|
|
1961
|
-
historyOpen && /* @__PURE__ */ jsx4("div", { className: "max-h-48 overflow-y-auto border-border border-b", children: threads.threads.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-4 py-3 text-muted-foreground text-xs", children: threads.loaded ? "No past conversations." : "Loading\u2026" }) : /* @__PURE__ */ jsx4("ul", { children: threads.threads.map((t) =>
|
|
1962
|
-
"
|
|
1963
|
-
{
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2072
|
+
historyOpen && /* @__PURE__ */ jsx4("div", { className: "max-h-48 overflow-y-auto border-border border-b", children: threads.threads.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-4 py-3 text-muted-foreground text-xs", children: threads.loaded ? "No past conversations." : "Loading\u2026" }) : /* @__PURE__ */ jsx4("ul", { children: threads.threads.map((t) => {
|
|
2073
|
+
const busyActive = state.threadId === t.id && state.status !== "idle";
|
|
2074
|
+
return /* @__PURE__ */ jsxs2("li", { className: "group flex items-center hover:bg-muted/50", children: [
|
|
2075
|
+
/* @__PURE__ */ jsx4(
|
|
2076
|
+
"button",
|
|
2077
|
+
{
|
|
2078
|
+
type: "button",
|
|
2079
|
+
onClick: () => {
|
|
2080
|
+
chat.switchThread(t.id);
|
|
2081
|
+
setHistoryOpen(false);
|
|
2082
|
+
},
|
|
2083
|
+
className: "min-w-0 flex-1 truncate px-4 py-2 text-left text-foreground text-xs",
|
|
2084
|
+
children: t.title ?? "Untitled conversation"
|
|
2085
|
+
}
|
|
2086
|
+
),
|
|
2087
|
+
threads.canRemove && /* @__PURE__ */ jsx4(
|
|
2088
|
+
"button",
|
|
2089
|
+
{
|
|
2090
|
+
type: "button",
|
|
2091
|
+
onClick: () => void deleteThread(t.id),
|
|
2092
|
+
disabled: busyActive,
|
|
2093
|
+
"aria-label": "Delete conversation",
|
|
2094
|
+
title: busyActive ? "Can't delete while this conversation is active" : "Delete conversation",
|
|
2095
|
+
className: "shrink-0 p-2 text-muted-foreground opacity-0 transition hover:text-destructive focus-visible:opacity-100 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-30",
|
|
2096
|
+
children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3.5 w-3.5" })
|
|
2097
|
+
}
|
|
2098
|
+
)
|
|
2099
|
+
] }, t.id);
|
|
2100
|
+
}) }) }),
|
|
1973
2101
|
/* @__PURE__ */ jsx4(
|
|
1974
2102
|
"div",
|
|
1975
2103
|
{
|
|
@@ -1986,6 +2114,7 @@ function AssistantPanel({
|
|
|
1986
2114
|
isStreaming: streaming,
|
|
1987
2115
|
isThinking,
|
|
1988
2116
|
pendingProposals: state.pendingProposals,
|
|
2117
|
+
usage: state.usage,
|
|
1989
2118
|
renderProposal
|
|
1990
2119
|
}) : /* @__PURE__ */ jsx4(
|
|
1991
2120
|
AgentTimeline,
|
|
@@ -2038,19 +2167,22 @@ function AssistantPanel({
|
|
|
2038
2167
|
]
|
|
2039
2168
|
}
|
|
2040
2169
|
),
|
|
2041
|
-
/* @__PURE__ */
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2170
|
+
/* @__PURE__ */ jsxs2("div", { className: "border-border border-t p-2", children: [
|
|
2171
|
+
streaming && /* @__PURE__ */ jsx4("div", { className: "px-2 pb-1.5", "aria-label": "Assistant is working", children: /* @__PURE__ */ jsx4(ThinkingIndicator, {}) }),
|
|
2172
|
+
/* @__PURE__ */ jsx4(
|
|
2173
|
+
ChatInput,
|
|
2174
|
+
{
|
|
2175
|
+
onSend: (message) => chat.send(message),
|
|
2176
|
+
onCancel: chat.stop,
|
|
2177
|
+
isStreaming: streaming,
|
|
2178
|
+
disabled: chat.restoring || state.status === "awaiting_confirm",
|
|
2179
|
+
placeholder: "Message the assistant\u2026",
|
|
2180
|
+
inputLabel: null,
|
|
2181
|
+
idleStatus: null,
|
|
2182
|
+
streamingStatus: null
|
|
2183
|
+
}
|
|
2184
|
+
)
|
|
2185
|
+
] })
|
|
2054
2186
|
] });
|
|
2055
2187
|
}
|
|
2056
2188
|
|
|
@@ -2092,7 +2224,7 @@ function useAssistantLauncher() {
|
|
|
2092
2224
|
}
|
|
2093
2225
|
|
|
2094
2226
|
// src/assistant/ResizeHandle.tsx
|
|
2095
|
-
import { useRef as
|
|
2227
|
+
import { useRef as useRef5 } from "react";
|
|
2096
2228
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
2097
2229
|
function ResizeHandle({
|
|
2098
2230
|
width,
|
|
@@ -2101,7 +2233,7 @@ function ResizeHandle({
|
|
|
2101
2233
|
onCommit,
|
|
2102
2234
|
onNudge
|
|
2103
2235
|
}) {
|
|
2104
|
-
const dragRef =
|
|
2236
|
+
const dragRef = useRef5(null);
|
|
2105
2237
|
const onPointerDown = (e) => {
|
|
2106
2238
|
if (e.button !== 0) return;
|
|
2107
2239
|
e.preventDefault();
|
|
@@ -2190,10 +2322,10 @@ function AssistantDock({
|
|
|
2190
2322
|
const chat = useAssistantChat(userId, { onWorkflowMutation });
|
|
2191
2323
|
const isDesktop = useIsDesktop();
|
|
2192
2324
|
const { width, maxWidth, setWidth, previewWidth, nudgeWidth } = usePanelWidth();
|
|
2193
|
-
const launcherRef =
|
|
2194
|
-
const dialogRef =
|
|
2195
|
-
const returnFocusRef =
|
|
2196
|
-
const wasOpenRef =
|
|
2325
|
+
const launcherRef = useRef6(null);
|
|
2326
|
+
const dialogRef = useRef6(null);
|
|
2327
|
+
const returnFocusRef = useRef6(null);
|
|
2328
|
+
const wasOpenRef = useRef6(false);
|
|
2197
2329
|
useEffect5(() => {
|
|
2198
2330
|
if (!open) return;
|
|
2199
2331
|
const onKeyDown = (e) => {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// src/integrations/provider-logo.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
function normalizeProviderId(id) {
|
|
5
|
+
return id.toLowerCase().replace(/[_\s]+/g, "-").replace(/-(business|oauth|api|app|mail|sms|pack|connector|v\d+)$/g, "");
|
|
6
|
+
}
|
|
7
|
+
var PROVIDER_LOGO_SLUGS = {
|
|
8
|
+
gmail: "gmail",
|
|
9
|
+
googlemail: "gmail",
|
|
10
|
+
google: "google",
|
|
11
|
+
"google-drive": "googledrive",
|
|
12
|
+
"google-calendar": "googlecalendar",
|
|
13
|
+
"google-sheets": "googlesheets",
|
|
14
|
+
"google-docs": "googledocs",
|
|
15
|
+
"google-meet": "googlemeet",
|
|
16
|
+
"google-forms": "googleforms",
|
|
17
|
+
"google-ads": "googleads",
|
|
18
|
+
"google-analytics": "googleanalytics",
|
|
19
|
+
outlook: "microsoftoutlook",
|
|
20
|
+
"outlook-mail": "microsoftoutlook",
|
|
21
|
+
"microsoft-outlook": "microsoftoutlook",
|
|
22
|
+
"microsoft-calendar": "microsoftoutlook",
|
|
23
|
+
"microsoft-teams": "microsoftteams",
|
|
24
|
+
teams: "microsoftteams",
|
|
25
|
+
"microsoft-excel": "microsoftexcel",
|
|
26
|
+
excel: "microsoftexcel",
|
|
27
|
+
onedrive: "microsoftonedrive",
|
|
28
|
+
sharepoint: "microsoftsharepoint",
|
|
29
|
+
twitter: "x",
|
|
30
|
+
x: "x",
|
|
31
|
+
meta: "meta",
|
|
32
|
+
"stripe-pack": "stripe",
|
|
33
|
+
"twilio-sms": "twilio",
|
|
34
|
+
webhook: "webhooks",
|
|
35
|
+
webhooks: "webhooks",
|
|
36
|
+
hubspot: "hubspot",
|
|
37
|
+
salesforce: "salesforce",
|
|
38
|
+
pipedrive: "pipedrive",
|
|
39
|
+
zoho: "zoho",
|
|
40
|
+
quickbooks: "quickbooks",
|
|
41
|
+
intercom: "intercom",
|
|
42
|
+
zendesk: "zendesk",
|
|
43
|
+
freshdesk: "freshdesk",
|
|
44
|
+
monday: "mondaydotcom",
|
|
45
|
+
"monday-com": "mondaydotcom",
|
|
46
|
+
clickup: "clickup",
|
|
47
|
+
basecamp: "basecamp",
|
|
48
|
+
todoist: "todoist",
|
|
49
|
+
calendly: "calendly",
|
|
50
|
+
typeform: "typeform",
|
|
51
|
+
surveymonkey: "surveymonkey",
|
|
52
|
+
klaviyo: "klaviyo",
|
|
53
|
+
sendinblue: "brevo",
|
|
54
|
+
brevo: "brevo",
|
|
55
|
+
"constant-contact": "constantcontact",
|
|
56
|
+
"active-campaign": "activecampaign",
|
|
57
|
+
activecampaign: "activecampaign",
|
|
58
|
+
"google-chat": "googlechat",
|
|
59
|
+
whatsapp: "whatsapp",
|
|
60
|
+
telegram: "telegram",
|
|
61
|
+
bigquery: "googlebigquery",
|
|
62
|
+
snowflake: "snowflake",
|
|
63
|
+
postgres: "postgresql",
|
|
64
|
+
postgresql: "postgresql",
|
|
65
|
+
mysql: "mysql",
|
|
66
|
+
mongodb: "mongodb",
|
|
67
|
+
redis: "redis",
|
|
68
|
+
supabase: "supabase",
|
|
69
|
+
firebase: "firebase",
|
|
70
|
+
"aws-s3": "amazons3",
|
|
71
|
+
s3: "amazons3",
|
|
72
|
+
woocommerce: "woocommerce",
|
|
73
|
+
bigcommerce: "bigcommerce",
|
|
74
|
+
squarespace: "squarespace",
|
|
75
|
+
wix: "wix",
|
|
76
|
+
webflow: "webflow",
|
|
77
|
+
wordpress: "wordpress",
|
|
78
|
+
contentful: "contentful",
|
|
79
|
+
sanity: "sanity",
|
|
80
|
+
figma: "figma",
|
|
81
|
+
miro: "miro",
|
|
82
|
+
confluence: "confluence",
|
|
83
|
+
bitbucket: "bitbucket",
|
|
84
|
+
pagerduty: "pagerduty",
|
|
85
|
+
datadog: "datadog",
|
|
86
|
+
sentry: "sentry",
|
|
87
|
+
segment: "segment",
|
|
88
|
+
amplitude: "amplitude",
|
|
89
|
+
mixpanel: "mixpanel",
|
|
90
|
+
posthog: "posthog",
|
|
91
|
+
facebook: "facebook",
|
|
92
|
+
instagram: "instagram",
|
|
93
|
+
tiktok: "tiktok",
|
|
94
|
+
youtube: "youtube",
|
|
95
|
+
reddit: "reddit",
|
|
96
|
+
pinterest: "pinterest",
|
|
97
|
+
buffer: "buffer",
|
|
98
|
+
hootsuite: "hootsuite"
|
|
99
|
+
};
|
|
100
|
+
function providerLogoCandidates(opts) {
|
|
101
|
+
const out = [];
|
|
102
|
+
if (opts.iconUrl) out.push(opts.iconUrl);
|
|
103
|
+
const raw = opts.id.toLowerCase();
|
|
104
|
+
const norm = normalizeProviderId(raw);
|
|
105
|
+
const slugs = /* @__PURE__ */ new Set();
|
|
106
|
+
const curated = PROVIDER_LOGO_SLUGS[raw] ?? PROVIDER_LOGO_SLUGS[norm];
|
|
107
|
+
if (curated) slugs.add(curated);
|
|
108
|
+
slugs.add(norm.replace(/-/g, ""));
|
|
109
|
+
slugs.add(raw.replace(/[-_\s]/g, ""));
|
|
110
|
+
for (const slug of slugs) {
|
|
111
|
+
if (slug) out.push(`https://cdn.simpleicons.org/${slug}`);
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
function monogramColor(seed) {
|
|
116
|
+
const palette = [
|
|
117
|
+
"#6366f1",
|
|
118
|
+
"#8b5cf6",
|
|
119
|
+
"#ec4899",
|
|
120
|
+
"#f43f5e",
|
|
121
|
+
"#f97316",
|
|
122
|
+
"#eab308",
|
|
123
|
+
"#22c55e",
|
|
124
|
+
"#14b8a6",
|
|
125
|
+
"#0ea5e9",
|
|
126
|
+
"#3b82f6"
|
|
127
|
+
];
|
|
128
|
+
let hash = 0;
|
|
129
|
+
for (let i = 0; i < seed.length; i += 1) {
|
|
130
|
+
hash = hash * 31 + seed.charCodeAt(i) >>> 0;
|
|
131
|
+
}
|
|
132
|
+
return palette[hash % palette.length];
|
|
133
|
+
}
|
|
134
|
+
function ProviderIcon({
|
|
135
|
+
id,
|
|
136
|
+
iconUrl,
|
|
137
|
+
displayName,
|
|
138
|
+
size = 24,
|
|
139
|
+
className
|
|
140
|
+
}) {
|
|
141
|
+
const candidates = React.useMemo(
|
|
142
|
+
() => providerLogoCandidates({ id, iconUrl }),
|
|
143
|
+
[id, iconUrl]
|
|
144
|
+
);
|
|
145
|
+
const [index, setIndex] = React.useState(0);
|
|
146
|
+
const candidateKey = `${id} ${iconUrl ?? ""}`;
|
|
147
|
+
const [seenKey, setSeenKey] = React.useState(candidateKey);
|
|
148
|
+
if (candidateKey !== seenKey) {
|
|
149
|
+
setSeenKey(candidateKey);
|
|
150
|
+
setIndex(0);
|
|
151
|
+
}
|
|
152
|
+
const label = (displayName ?? id).trim();
|
|
153
|
+
const initial = label.charAt(0).toUpperCase() || "?";
|
|
154
|
+
const src = candidates[index];
|
|
155
|
+
if (!src) {
|
|
156
|
+
return /* @__PURE__ */ jsx(
|
|
157
|
+
"span",
|
|
158
|
+
{
|
|
159
|
+
className: `flex shrink-0 items-center justify-center font-semibold text-white ${className ?? ""}`,
|
|
160
|
+
style: {
|
|
161
|
+
width: size,
|
|
162
|
+
height: size,
|
|
163
|
+
backgroundColor: monogramColor(id),
|
|
164
|
+
fontSize: Math.round(size * 0.42)
|
|
165
|
+
},
|
|
166
|
+
"aria-hidden": true,
|
|
167
|
+
children: initial
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return /* @__PURE__ */ jsx(
|
|
172
|
+
"img",
|
|
173
|
+
{
|
|
174
|
+
src,
|
|
175
|
+
alt: "",
|
|
176
|
+
width: size,
|
|
177
|
+
height: size,
|
|
178
|
+
loading: "lazy",
|
|
179
|
+
className: `shrink-0 object-contain ${className ?? ""}`,
|
|
180
|
+
style: { width: size, height: size },
|
|
181
|
+
onError: () => setIndex((i) => i + 1)
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
normalizeProviderId,
|
|
188
|
+
providerLogoCandidates,
|
|
189
|
+
monogramColor,
|
|
190
|
+
ProviderIcon
|
|
191
|
+
};
|
package/dist/globals.css
CHANGED
|
@@ -692,6 +692,9 @@
|
|
|
692
692
|
.h-56 {
|
|
693
693
|
height: calc(var(--spacing) * 56);
|
|
694
694
|
}
|
|
695
|
+
.h-64 {
|
|
696
|
+
height: calc(var(--spacing) * 64);
|
|
697
|
+
}
|
|
695
698
|
.h-\[3px\] {
|
|
696
699
|
height: 3px;
|
|
697
700
|
}
|
|
@@ -2378,6 +2381,9 @@
|
|
|
2378
2381
|
.pb-1 {
|
|
2379
2382
|
padding-bottom: calc(var(--spacing) * 1);
|
|
2380
2383
|
}
|
|
2384
|
+
.pb-1\.5 {
|
|
2385
|
+
padding-bottom: calc(var(--spacing) * 1.5);
|
|
2386
|
+
}
|
|
2381
2387
|
.pb-2 {
|
|
2382
2388
|
padding-bottom: calc(var(--spacing) * 2);
|
|
2383
2389
|
}
|
package/dist/integrations.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
monogramColor,
|
|
3
|
+
normalizeProviderId,
|
|
4
|
+
providerLogoCandidates
|
|
5
|
+
} from "./chunk-H7GMPMPB.js";
|
|
6
|
+
|
|
1
7
|
// src/integrations/integrations-panel.tsx
|
|
2
8
|
import * as React from "react";
|
|
3
9
|
import { Card, CardContent, EmptyState } from "@tangle-network/ui/primitives";
|
|
@@ -42,141 +48,17 @@ var DEFAULT_FEATURED_IDS = [
|
|
|
42
48
|
function defaultConnectorOf(provider) {
|
|
43
49
|
return provider.connectors?.[0]?.connectorId ?? provider.providerId;
|
|
44
50
|
}
|
|
45
|
-
function normalizeProviderId(id) {
|
|
46
|
-
return id.toLowerCase().replace(/[_\s]+/g, "-").replace(/-(business|oauth|api|app|mail|sms|pack|connector|v\d+)$/g, "");
|
|
47
|
-
}
|
|
48
|
-
var PROVIDER_LOGO_SLUGS = {
|
|
49
|
-
gmail: "gmail",
|
|
50
|
-
googlemail: "gmail",
|
|
51
|
-
google: "google",
|
|
52
|
-
"google-drive": "googledrive",
|
|
53
|
-
"google-calendar": "googlecalendar",
|
|
54
|
-
"google-sheets": "googlesheets",
|
|
55
|
-
"google-docs": "googledocs",
|
|
56
|
-
"google-meet": "googlemeet",
|
|
57
|
-
"google-forms": "googleforms",
|
|
58
|
-
"google-ads": "googleads",
|
|
59
|
-
"google-analytics": "googleanalytics",
|
|
60
|
-
outlook: "microsoftoutlook",
|
|
61
|
-
"outlook-mail": "microsoftoutlook",
|
|
62
|
-
"microsoft-outlook": "microsoftoutlook",
|
|
63
|
-
"microsoft-calendar": "microsoftoutlook",
|
|
64
|
-
"microsoft-teams": "microsoftteams",
|
|
65
|
-
teams: "microsoftteams",
|
|
66
|
-
"microsoft-excel": "microsoftexcel",
|
|
67
|
-
excel: "microsoftexcel",
|
|
68
|
-
onedrive: "microsoftonedrive",
|
|
69
|
-
sharepoint: "microsoftsharepoint",
|
|
70
|
-
twitter: "x",
|
|
71
|
-
x: "x",
|
|
72
|
-
meta: "meta",
|
|
73
|
-
"stripe-pack": "stripe",
|
|
74
|
-
"twilio-sms": "twilio",
|
|
75
|
-
webhook: "webhooks",
|
|
76
|
-
webhooks: "webhooks",
|
|
77
|
-
hubspot: "hubspot",
|
|
78
|
-
salesforce: "salesforce",
|
|
79
|
-
pipedrive: "pipedrive",
|
|
80
|
-
zoho: "zoho",
|
|
81
|
-
quickbooks: "quickbooks",
|
|
82
|
-
intercom: "intercom",
|
|
83
|
-
zendesk: "zendesk",
|
|
84
|
-
freshdesk: "freshdesk",
|
|
85
|
-
monday: "mondaydotcom",
|
|
86
|
-
"monday-com": "mondaydotcom",
|
|
87
|
-
clickup: "clickup",
|
|
88
|
-
basecamp: "basecamp",
|
|
89
|
-
todoist: "todoist",
|
|
90
|
-
calendly: "calendly",
|
|
91
|
-
typeform: "typeform",
|
|
92
|
-
surveymonkey: "surveymonkey",
|
|
93
|
-
klaviyo: "klaviyo",
|
|
94
|
-
sendinblue: "brevo",
|
|
95
|
-
brevo: "brevo",
|
|
96
|
-
"constant-contact": "constantcontact",
|
|
97
|
-
"active-campaign": "activecampaign",
|
|
98
|
-
activecampaign: "activecampaign",
|
|
99
|
-
"google-chat": "googlechat",
|
|
100
|
-
whatsapp: "whatsapp",
|
|
101
|
-
telegram: "telegram",
|
|
102
|
-
bigquery: "googlebigquery",
|
|
103
|
-
snowflake: "snowflake",
|
|
104
|
-
postgres: "postgresql",
|
|
105
|
-
postgresql: "postgresql",
|
|
106
|
-
mysql: "mysql",
|
|
107
|
-
mongodb: "mongodb",
|
|
108
|
-
redis: "redis",
|
|
109
|
-
supabase: "supabase",
|
|
110
|
-
firebase: "firebase",
|
|
111
|
-
"aws-s3": "amazons3",
|
|
112
|
-
s3: "amazons3",
|
|
113
|
-
woocommerce: "woocommerce",
|
|
114
|
-
bigcommerce: "bigcommerce",
|
|
115
|
-
squarespace: "squarespace",
|
|
116
|
-
wix: "wix",
|
|
117
|
-
webflow: "webflow",
|
|
118
|
-
wordpress: "wordpress",
|
|
119
|
-
contentful: "contentful",
|
|
120
|
-
sanity: "sanity",
|
|
121
|
-
figma: "figma",
|
|
122
|
-
miro: "miro",
|
|
123
|
-
confluence: "confluence",
|
|
124
|
-
bitbucket: "bitbucket",
|
|
125
|
-
pagerduty: "pagerduty",
|
|
126
|
-
datadog: "datadog",
|
|
127
|
-
sentry: "sentry",
|
|
128
|
-
segment: "segment",
|
|
129
|
-
amplitude: "amplitude",
|
|
130
|
-
mixpanel: "mixpanel",
|
|
131
|
-
posthog: "posthog",
|
|
132
|
-
facebook: "facebook",
|
|
133
|
-
instagram: "instagram",
|
|
134
|
-
tiktok: "tiktok",
|
|
135
|
-
youtube: "youtube",
|
|
136
|
-
reddit: "reddit",
|
|
137
|
-
pinterest: "pinterest",
|
|
138
|
-
buffer: "buffer",
|
|
139
|
-
hootsuite: "hootsuite"
|
|
140
|
-
};
|
|
141
|
-
function logoCandidates(provider) {
|
|
142
|
-
const out = [];
|
|
143
|
-
if (provider.iconUrl) out.push(provider.iconUrl);
|
|
144
|
-
const raw = provider.providerId.toLowerCase();
|
|
145
|
-
const norm = normalizeProviderId(raw);
|
|
146
|
-
const slugs = /* @__PURE__ */ new Set();
|
|
147
|
-
const curated = PROVIDER_LOGO_SLUGS[raw] ?? PROVIDER_LOGO_SLUGS[norm];
|
|
148
|
-
if (curated) slugs.add(curated);
|
|
149
|
-
slugs.add(norm.replace(/-/g, ""));
|
|
150
|
-
slugs.add(raw.replace(/[-_\s]/g, ""));
|
|
151
|
-
for (const slug of slugs) {
|
|
152
|
-
if (slug) out.push(`https://cdn.simpleicons.org/${slug}`);
|
|
153
|
-
}
|
|
154
|
-
return out;
|
|
155
|
-
}
|
|
156
|
-
function monogramColor(seed) {
|
|
157
|
-
const palette = [
|
|
158
|
-
"#6366f1",
|
|
159
|
-
"#8b5cf6",
|
|
160
|
-
"#ec4899",
|
|
161
|
-
"#f43f5e",
|
|
162
|
-
"#f97316",
|
|
163
|
-
"#eab308",
|
|
164
|
-
"#22c55e",
|
|
165
|
-
"#14b8a6",
|
|
166
|
-
"#0ea5e9",
|
|
167
|
-
"#3b82f6"
|
|
168
|
-
];
|
|
169
|
-
let hash = 0;
|
|
170
|
-
for (let i = 0; i < seed.length; i += 1) {
|
|
171
|
-
hash = hash * 31 + seed.charCodeAt(i) >>> 0;
|
|
172
|
-
}
|
|
173
|
-
return palette[hash % palette.length];
|
|
174
|
-
}
|
|
175
51
|
function ProviderLogo({
|
|
176
52
|
provider,
|
|
177
53
|
size = 56
|
|
178
54
|
}) {
|
|
179
|
-
const candidates = React.useMemo(
|
|
55
|
+
const candidates = React.useMemo(
|
|
56
|
+
() => providerLogoCandidates({
|
|
57
|
+
id: provider.providerId,
|
|
58
|
+
iconUrl: provider.iconUrl
|
|
59
|
+
}),
|
|
60
|
+
[provider]
|
|
61
|
+
);
|
|
180
62
|
const [index, setIndex] = React.useState(0);
|
|
181
63
|
const label = (provider.displayName ?? provider.providerId).trim();
|
|
182
64
|
const initial = label.charAt(0).toUpperCase() || "?";
|
package/dist/styles.css
CHANGED
|
@@ -692,6 +692,9 @@
|
|
|
692
692
|
.h-56 {
|
|
693
693
|
height: calc(var(--spacing) * 56);
|
|
694
694
|
}
|
|
695
|
+
.h-64 {
|
|
696
|
+
height: calc(var(--spacing) * 64);
|
|
697
|
+
}
|
|
695
698
|
.h-\[3px\] {
|
|
696
699
|
height: 3px;
|
|
697
700
|
}
|
|
@@ -2378,6 +2381,9 @@
|
|
|
2378
2381
|
.pb-1 {
|
|
2379
2382
|
padding-bottom: calc(var(--spacing) * 1);
|
|
2380
2383
|
}
|
|
2384
|
+
.pb-1\.5 {
|
|
2385
|
+
padding-bottom: calc(var(--spacing) * 1.5);
|
|
2386
|
+
}
|
|
2381
2387
|
.pb-2 {
|
|
2382
2388
|
padding-bottom: calc(var(--spacing) * 2);
|
|
2383
2389
|
}
|
package/dist/workflows.js
CHANGED
|
@@ -4,11 +4,32 @@ import {
|
|
|
4
4
|
import "./chunk-7JLB7UZ3.js";
|
|
5
5
|
|
|
6
6
|
// src/workflows/WorkflowGraphLazy.tsx
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Component,
|
|
9
|
+
lazy,
|
|
10
|
+
Suspense,
|
|
11
|
+
useMemo,
|
|
12
|
+
useState
|
|
13
|
+
} from "react";
|
|
8
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
);
|
|
15
|
+
function retryImport(factory, retries = 2, delayMs = 350) {
|
|
16
|
+
return factory().catch((err) => {
|
|
17
|
+
if (retries <= 0) throw err;
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
setTimeout(
|
|
20
|
+
() => retryImport(factory, retries - 1, delayMs).then(resolve, reject),
|
|
21
|
+
delayMs
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function lazyWorkflowGraph() {
|
|
27
|
+
return lazy(
|
|
28
|
+
() => retryImport(
|
|
29
|
+
() => import("./WorkflowGraph-7YYGIBNV.js").then((m) => ({ default: m.WorkflowGraph }))
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
12
33
|
var GraphErrorBoundary = class extends Component {
|
|
13
34
|
state = { hasError: false };
|
|
14
35
|
static getDerivedStateFromError() {
|
|
@@ -18,27 +39,45 @@ var GraphErrorBoundary = class extends Component {
|
|
|
18
39
|
return this.state.hasError ? this.props.fallback : this.props.children;
|
|
19
40
|
}
|
|
20
41
|
};
|
|
21
|
-
function GraphFallback({
|
|
42
|
+
function GraphFallback({
|
|
43
|
+
yaml,
|
|
44
|
+
className,
|
|
45
|
+
onRetry
|
|
46
|
+
}) {
|
|
22
47
|
return /* @__PURE__ */ jsxs(
|
|
23
48
|
"div",
|
|
24
49
|
{
|
|
25
50
|
className: `overflow-auto rounded-lg border border-border bg-background p-2 ${className ?? ""}`,
|
|
26
51
|
children: [
|
|
27
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
|
|
53
|
+
/* @__PURE__ */ jsx("p", { className: "text-text-muted text-xs", children: "Couldn't render the graph \u2014 showing the definition." }),
|
|
54
|
+
/* @__PURE__ */ jsx(
|
|
55
|
+
"button",
|
|
56
|
+
{
|
|
57
|
+
type: "button",
|
|
58
|
+
onClick: onRetry,
|
|
59
|
+
className: "shrink-0 rounded border border-border px-1.5 py-0.5 text-text-muted text-xs transition hover:text-text",
|
|
60
|
+
children: "Retry"
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
] }),
|
|
28
64
|
/* @__PURE__ */ jsx("pre", { className: "text-text text-xs", children: /* @__PURE__ */ jsx("code", { children: yaml }) })
|
|
29
65
|
]
|
|
30
66
|
}
|
|
31
67
|
);
|
|
32
68
|
}
|
|
33
69
|
function WorkflowGraph(props) {
|
|
70
|
+
const [attempt, setAttempt] = useState(0);
|
|
71
|
+
const Impl = useMemo(() => lazyWorkflowGraph(), [attempt]);
|
|
34
72
|
return (
|
|
35
|
-
// Key the boundary by the workflow so a different/edited
|
|
36
|
-
// it and re-attempts the graph — a
|
|
37
|
-
// workflow doesn't permanently pin the
|
|
73
|
+
// Key the boundary by the workflow AND the attempt so a different/edited
|
|
74
|
+
// definition or a manual retry remounts it and re-attempts the graph — a
|
|
75
|
+
// transient chunk/render failure on one workflow doesn't permanently pin the
|
|
76
|
+
// YAML fallback.
|
|
38
77
|
/* @__PURE__ */ jsx(
|
|
39
78
|
GraphErrorBoundary,
|
|
40
79
|
{
|
|
41
|
-
fallback: /* @__PURE__ */ jsx(GraphFallback, { ...props }),
|
|
80
|
+
fallback: /* @__PURE__ */ jsx(GraphFallback, { ...props, onRetry: () => setAttempt((a) => a + 1) }),
|
|
42
81
|
children: /* @__PURE__ */ jsx(
|
|
43
82
|
Suspense,
|
|
44
83
|
{
|
|
@@ -49,11 +88,11 @@ function WorkflowGraph(props) {
|
|
|
49
88
|
children: "Loading graph\u2026"
|
|
50
89
|
}
|
|
51
90
|
),
|
|
52
|
-
children: /* @__PURE__ */ jsx(
|
|
91
|
+
children: /* @__PURE__ */ jsx(Impl, { ...props })
|
|
53
92
|
}
|
|
54
93
|
)
|
|
55
94
|
},
|
|
56
|
-
props.yaml
|
|
95
|
+
`${props.yaml}::${attempt}`
|
|
57
96
|
)
|
|
58
97
|
);
|
|
59
98
|
}
|
package/package.json
CHANGED