@deck-ui/connections 0.2.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/package.json +19 -0
- package/src/connection-row.tsx +47 -0
- package/src/connections-view.tsx +170 -0
- package/src/index.ts +6 -0
- package/src/styles.css +2 -0
- package/src/types.ts +14 -0
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deck-ui/connections",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"files": ["src"],
|
|
8
|
+
"peerDependencies": {
|
|
9
|
+
"react": "^19.0.0",
|
|
10
|
+
"react-dom": "^19.0.0",
|
|
11
|
+
"@deck-ui/core": "^0.1.0"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"lucide-react": "^0.577.0"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Check } from "lucide-react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import type { Connection } from "./types";
|
|
4
|
+
|
|
5
|
+
interface ConnectionRowProps {
|
|
6
|
+
connection: Connection;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function ConnectionRow({ connection }: ConnectionRowProps) {
|
|
10
|
+
const [imgError, setImgError] = useState(false);
|
|
11
|
+
const initial = connection.display_name.charAt(0).toUpperCase();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex items-center gap-3.5 px-1 py-3">
|
|
15
|
+
{/* App icon */}
|
|
16
|
+
{!imgError ? (
|
|
17
|
+
<img
|
|
18
|
+
src={connection.logo_url}
|
|
19
|
+
alt={connection.display_name}
|
|
20
|
+
className="size-10 rounded-[10px] object-contain shrink-0"
|
|
21
|
+
onError={() => setImgError(true)}
|
|
22
|
+
/>
|
|
23
|
+
) : (
|
|
24
|
+
<div className="size-10 rounded-[10px] bg-[#e8e8e8] flex items-center justify-center shrink-0">
|
|
25
|
+
<span className="text-sm font-semibold text-[#5d5d5d]">
|
|
26
|
+
{initial}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
|
|
31
|
+
{/* Text */}
|
|
32
|
+
<div className="flex-1 min-w-0">
|
|
33
|
+
<p className="text-[13px] font-medium text-[#0d0d0d] truncate">
|
|
34
|
+
{connection.display_name}
|
|
35
|
+
</p>
|
|
36
|
+
<p className="text-[12px] text-[#8e8e8e] truncate">
|
|
37
|
+
{connection.email ?? connection.description}
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{/* Connected indicator */}
|
|
42
|
+
<div className="size-5 rounded-full bg-emerald-50 flex items-center justify-center shrink-0">
|
|
43
|
+
<Check className="size-3 text-emerald-600" strokeWidth={2.5} />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { ExternalLink, Loader2, RefreshCw } from "lucide-react";
|
|
2
|
+
import { ConnectionRow } from "./connection-row";
|
|
3
|
+
import type { Connection, ConnectionsResult } from "./types";
|
|
4
|
+
|
|
5
|
+
export interface ConnectionsViewProps {
|
|
6
|
+
result: ConnectionsResult | null;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
onRetry: () => void;
|
|
9
|
+
onManage: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ConnectionsView({
|
|
13
|
+
result,
|
|
14
|
+
loading,
|
|
15
|
+
onRetry,
|
|
16
|
+
onManage,
|
|
17
|
+
}: ConnectionsViewProps) {
|
|
18
|
+
const items: Connection[] =
|
|
19
|
+
result?.status === "ok" ? result.connections : [];
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex-1 flex flex-col min-h-0 overflow-auto">
|
|
23
|
+
<div className="max-w-3xl mx-auto w-full px-6 py-8">
|
|
24
|
+
{/* Header -- only when there are connections */}
|
|
25
|
+
{!loading && items.length > 0 && (
|
|
26
|
+
<div className="flex items-start justify-between mb-6">
|
|
27
|
+
<div>
|
|
28
|
+
<h1 className="text-xl font-semibold text-[#0d0d0d] tracking-tight">
|
|
29
|
+
Connected apps
|
|
30
|
+
</h1>
|
|
31
|
+
<p className="text-[13px] text-[#8e8e8e] mt-1">
|
|
32
|
+
Services Houston can use across all your projects
|
|
33
|
+
</p>
|
|
34
|
+
</div>
|
|
35
|
+
<button
|
|
36
|
+
onClick={onManage}
|
|
37
|
+
className="inline-flex items-center gap-1.5 h-8 px-4 rounded-full bg-[#0d0d0d] text-white text-xs font-medium hover:bg-[#424242] transition-colors duration-200 shrink-0"
|
|
38
|
+
>
|
|
39
|
+
Manage connections
|
|
40
|
+
<ExternalLink className="size-3" />
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{/* Loading */}
|
|
46
|
+
{loading && (
|
|
47
|
+
<div className="flex flex-col items-center justify-center py-24 gap-3">
|
|
48
|
+
<Loader2 className="size-5 text-[#b4b4b4] animate-spin" />
|
|
49
|
+
<p className="text-[13px] text-[#8e8e8e]">
|
|
50
|
+
Checking your connections...
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
{/* Not configured */}
|
|
56
|
+
{!loading && result?.status === "not_configured" && (
|
|
57
|
+
<div className="flex flex-col items-center justify-center py-24 gap-4">
|
|
58
|
+
<div className="space-y-2 text-center max-w-md">
|
|
59
|
+
<h1 className="text-2xl font-semibold text-foreground tracking-tight">
|
|
60
|
+
Connect your apps
|
|
61
|
+
</h1>
|
|
62
|
+
<p className="text-sm text-[#5d5d5d]">
|
|
63
|
+
Set up Composio so Houston can use Gmail, Slack, Google Drive,
|
|
64
|
+
and 100+ other services on your behalf.
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
<button
|
|
68
|
+
onClick={onManage}
|
|
69
|
+
className="inline-flex items-center gap-1.5 h-9 px-4 rounded-full bg-[#0d0d0d] text-white text-sm font-medium hover:bg-[#424242] transition-colors duration-200"
|
|
70
|
+
>
|
|
71
|
+
Set up connections
|
|
72
|
+
<ExternalLink className="size-3.5" />
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
|
|
77
|
+
{/* Needs auth */}
|
|
78
|
+
{!loading && result?.status === "needs_auth" && (
|
|
79
|
+
<div className="flex flex-col items-center justify-center py-24 gap-4">
|
|
80
|
+
<div className="space-y-2 text-center max-w-md">
|
|
81
|
+
<h1 className="text-2xl font-semibold text-foreground tracking-tight">
|
|
82
|
+
Composio needs authentication
|
|
83
|
+
</h1>
|
|
84
|
+
<p className="text-sm text-[#5d5d5d]">
|
|
85
|
+
Open Claude Code in your terminal and type{" "}
|
|
86
|
+
<code className="px-1.5 py-0.5 bg-[#f4f4f4] rounded text-[13px]">
|
|
87
|
+
/mcp
|
|
88
|
+
</code>{" "}
|
|
89
|
+
to complete the OAuth setup for Composio.
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
<button
|
|
93
|
+
onClick={onRetry}
|
|
94
|
+
className="inline-flex items-center gap-1.5 h-9 px-4 rounded-full bg-[#0d0d0d] text-white text-sm font-medium hover:bg-[#424242] transition-colors duration-200"
|
|
95
|
+
>
|
|
96
|
+
<RefreshCw className="size-3.5" />
|
|
97
|
+
Retry
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{/* Error */}
|
|
103
|
+
{!loading && result?.status === "error" && (
|
|
104
|
+
<div className="flex flex-col items-center justify-center py-24 gap-4">
|
|
105
|
+
<div className="space-y-2 text-center max-w-md">
|
|
106
|
+
<h1 className="text-2xl font-semibold text-foreground tracking-tight">
|
|
107
|
+
Couldn't load connections
|
|
108
|
+
</h1>
|
|
109
|
+
<p className="text-sm text-[#5d5d5d]">
|
|
110
|
+
Composio is set up but we couldn't fetch your connections. This
|
|
111
|
+
can happen if your authentication expired or the service is
|
|
112
|
+
temporarily unavailable.
|
|
113
|
+
</p>
|
|
114
|
+
<p className="text-xs text-[#8e8e8e] font-mono mt-2">
|
|
115
|
+
{result.message}
|
|
116
|
+
</p>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="flex items-center gap-2">
|
|
119
|
+
<button
|
|
120
|
+
onClick={onRetry}
|
|
121
|
+
className="inline-flex items-center gap-1.5 h-9 px-4 rounded-full bg-[#0d0d0d] text-white text-sm font-medium hover:bg-[#424242] transition-colors duration-200"
|
|
122
|
+
>
|
|
123
|
+
<RefreshCw className="size-3.5" />
|
|
124
|
+
Retry
|
|
125
|
+
</button>
|
|
126
|
+
<button
|
|
127
|
+
onClick={onManage}
|
|
128
|
+
className="inline-flex items-center gap-1.5 h-9 px-4 rounded-full border border-black/15 bg-white text-[#0d0d0d] text-sm font-medium hover:bg-gray-50 transition-colors duration-200"
|
|
129
|
+
>
|
|
130
|
+
Reconnect
|
|
131
|
+
<ExternalLink className="size-3.5" />
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{/* Empty -- configured, no active connections */}
|
|
138
|
+
{!loading && result?.status === "ok" && items.length === 0 && (
|
|
139
|
+
<div className="flex flex-col items-center justify-center py-24 gap-4">
|
|
140
|
+
<div className="space-y-2 text-center max-w-md">
|
|
141
|
+
<h1 className="text-2xl font-semibold text-foreground tracking-tight">
|
|
142
|
+
No apps connected yet
|
|
143
|
+
</h1>
|
|
144
|
+
<p className="text-sm text-[#5d5d5d]">
|
|
145
|
+
Connect Gmail, Slack, Google Drive, and 100+ other services so
|
|
146
|
+
Houston can use them across all your projects.
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
<button
|
|
150
|
+
onClick={onManage}
|
|
151
|
+
className="inline-flex items-center gap-1.5 h-9 px-4 rounded-full bg-[#0d0d0d] text-white text-sm font-medium hover:bg-[#424242] transition-colors duration-200"
|
|
152
|
+
>
|
|
153
|
+
Add a connection
|
|
154
|
+
<ExternalLink className="size-3.5" />
|
|
155
|
+
</button>
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
|
|
159
|
+
{/* Connection grid */}
|
|
160
|
+
{!loading && items.length > 0 && (
|
|
161
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-6">
|
|
162
|
+
{items.map((conn) => (
|
|
163
|
+
<ConnectionRow key={conn.toolkit} connection={conn} />
|
|
164
|
+
))}
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
package/src/index.ts
ADDED
package/src/styles.css
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Connection {
|
|
2
|
+
toolkit: string;
|
|
3
|
+
display_name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
email: string | null;
|
|
6
|
+
logo_url: string;
|
|
7
|
+
connected_at: string | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ConnectionsResult =
|
|
11
|
+
| { status: "not_configured" }
|
|
12
|
+
| { status: "needs_auth" }
|
|
13
|
+
| { status: "error"; message: string }
|
|
14
|
+
| { status: "ok"; connections: Connection[] };
|