cue-console 0.1.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.
@@ -0,0 +1,304 @@
1
+ @import "tailwindcss";
2
+ @import "highlight.js/styles/github.css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --color-background: var(--background);
8
+ --color-foreground: var(--foreground);
9
+ --font-sans: var(--font-geist-sans);
10
+ --font-mono: var(--font-geist-mono);
11
+ --color-sidebar-ring: var(--sidebar-ring);
12
+ --color-sidebar-border: var(--sidebar-border);
13
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14
+ --color-sidebar-accent: var(--sidebar-accent);
15
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16
+ --color-sidebar-primary: var(--sidebar-primary);
17
+ --color-sidebar-foreground: var(--sidebar-foreground);
18
+ --color-sidebar: var(--sidebar);
19
+ --color-chart-5: var(--chart-5);
20
+ --color-chart-4: var(--chart-4);
21
+ --color-chart-3: var(--chart-3);
22
+ --color-chart-2: var(--chart-2);
23
+ --color-chart-1: var(--chart-1);
24
+ --color-ring: var(--ring);
25
+ --color-input: var(--input);
26
+ --color-border: var(--border);
27
+ --color-destructive: var(--destructive);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-accent: var(--accent);
30
+ --color-muted-foreground: var(--muted-foreground);
31
+ --color-muted: var(--muted);
32
+ --color-secondary-foreground: var(--secondary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-primary-foreground: var(--primary-foreground);
35
+ --color-primary: var(--primary);
36
+ --color-popover-foreground: var(--popover-foreground);
37
+ --color-popover: var(--popover);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-card: var(--card);
40
+ --radius-sm: calc(var(--radius) - 4px);
41
+ --radius-md: calc(var(--radius) - 2px);
42
+ --radius-lg: var(--radius);
43
+ --radius-xl: calc(var(--radius) + 4px);
44
+ --radius-2xl: calc(var(--radius) + 8px);
45
+ --radius-3xl: calc(var(--radius) + 12px);
46
+ --radius-4xl: calc(var(--radius) + 16px);
47
+ }
48
+
49
+ :root {
50
+ --radius: 0.625rem;
51
+ --background: #F7F8FB;
52
+ --foreground: #0B1220;
53
+ --card: #FFFFFF;
54
+ --card-foreground: #0B1220;
55
+ --popover: #FFFFFF;
56
+ --popover-foreground: #0B1220;
57
+ --primary: #0B1220;
58
+ --primary-foreground: #FFFFFF;
59
+ --secondary: #FFFFFF;
60
+ --secondary-foreground: #0B1220;
61
+ --muted: rgb(15 23 42 / 0.04);
62
+ --muted-foreground: rgb(11 18 32 / 0.62);
63
+ --accent: rgb(11 18 32 / 0.06);
64
+ --accent-foreground: #0B1220;
65
+ --destructive: oklch(0.577 0.245 27.325);
66
+ --border: rgb(11 18 32 / 0.10);
67
+ --input: rgb(11 18 32 / 0.10);
68
+ --ring: rgb(37 99 235 / 0.28);
69
+ --chart-1: oklch(0.646 0.222 41.116);
70
+ --chart-2: oklch(0.6 0.118 184.704);
71
+ --chart-3: oklch(0.398 0.07 227.392);
72
+ --chart-4: oklch(0.828 0.189 84.429);
73
+ --chart-5: oklch(0.769 0.188 70.08);
74
+ --sidebar: #FFFFFF;
75
+ --sidebar-foreground: #0B1220;
76
+ --sidebar-primary: #0B1220;
77
+ --sidebar-primary-foreground: #FFFFFF;
78
+ --sidebar-accent: rgb(11 18 32 / 0.06);
79
+ --sidebar-accent-foreground: #0B1220;
80
+ --sidebar-border: rgb(11 18 32 / 0.10);
81
+ --sidebar-ring: rgb(37 99 235 / 0.24);
82
+
83
+ --glass-bg: rgb(255 255 255 / 0.72);
84
+ --glass-bg-soft: rgb(255 255 255 / 0.80);
85
+ --glass-bg-opaque: rgb(255 255 255 / 0.88);
86
+ --glass-border: rgb(255 255 255 / 0.55);
87
+ --glass-border-soft: rgb(255 255 255 / 0.45);
88
+ --glass-shadow: 0 18px 44px rgb(11 18 32 / 0.10);
89
+ --glass-shadow-soft: 0 14px 36px rgb(11 18 32 / 0.09);
90
+ --glass-inset: inset 0 1px 0 rgb(255 255 255 / 0.78), inset 0 0 0 1px rgb(255 255 255 / 0.22);
91
+ --glass-blur: 20px;
92
+
93
+ --backdrop-ink: rgb(11 18 32 / 0.05);
94
+ --backdrop-ice: rgb(56 189 248 / 0.16);
95
+ --backdrop-foam: rgb(99 102 241 / 0.10);
96
+ }
97
+
98
+ .dark {
99
+ --background: oklch(0.165 0.01 258);
100
+ --foreground: oklch(0.975 0.01 258);
101
+ --card: oklch(0.22 0.01 258);
102
+ --card-foreground: oklch(0.975 0.01 258);
103
+ --popover: oklch(0.22 0.01 258);
104
+ --popover-foreground: oklch(0.975 0.01 258);
105
+ --primary: oklch(0.93 0.01 258);
106
+ --primary-foreground: oklch(0.22 0.01 258);
107
+ --secondary: oklch(0.27 0.01 258);
108
+ --secondary-foreground: oklch(0.975 0.01 258);
109
+ --muted: oklch(0.27 0.01 258);
110
+ --muted-foreground: oklch(0.74 0.01 258);
111
+ --accent: oklch(0.27 0.01 258);
112
+ --accent-foreground: oklch(0.975 0.01 258);
113
+ --destructive: oklch(0.704 0.191 22.216);
114
+ --border: oklch(1 0 0 / 10%);
115
+ --input: oklch(1 0 0 / 15%);
116
+ --ring: oklch(0.72 0.1 240 / 0.55);
117
+ --chart-1: oklch(0.488 0.243 264.376);
118
+ --chart-2: oklch(0.696 0.17 162.48);
119
+ --chart-3: oklch(0.769 0.188 70.08);
120
+ --chart-4: oklch(0.627 0.265 303.9);
121
+ --chart-5: oklch(0.645 0.246 16.439);
122
+ --sidebar: oklch(0.22 0.01 258);
123
+ --sidebar-foreground: oklch(0.975 0.01 258);
124
+ --sidebar-primary: oklch(0.488 0.243 264.376);
125
+ --sidebar-primary-foreground: oklch(0.975 0.01 258);
126
+ --sidebar-accent: oklch(0.27 0.01 258);
127
+ --sidebar-accent-foreground: oklch(0.975 0.01 258);
128
+ --sidebar-border: oklch(1 0 0 / 10%);
129
+ --sidebar-ring: oklch(0.72 0.1 240 / 0.55);
130
+
131
+ --glass-bg: rgb(12 18 32 / 0.55);
132
+ --glass-bg-soft: rgb(12 18 32 / 0.62);
133
+ --glass-bg-opaque: rgb(12 18 32 / 0.72);
134
+ --glass-border: rgb(255 255 255 / 0.08);
135
+ --glass-border-soft: rgb(255 255 255 / 0.10);
136
+ --glass-shadow: 0 22px 60px rgb(0 0 0 / 0.42);
137
+ --glass-shadow-soft: 0 18px 48px rgb(0 0 0 / 0.34);
138
+ --glass-inset: inset 0 1px 0 rgb(255 255 255 / 0.06), inset 0 0 0 1px rgb(255 255 255 / 0.05);
139
+ --glass-blur: 22px;
140
+
141
+ --backdrop-ink: rgb(255 255 255 / 0.04);
142
+ --backdrop-ice: rgb(56 189 248 / 0.14);
143
+ --backdrop-foam: rgb(99 102 241 / 0.12);
144
+ }
145
+
146
+ @layer base {
147
+ * {
148
+ @apply border-border outline-ring/50;
149
+ }
150
+ body {
151
+ @apply bg-background text-foreground;
152
+ background-image:
153
+ radial-gradient(1000px 720px at 18% 10%, var(--backdrop-ice), transparent 62%),
154
+ radial-gradient(920px 640px at 92% 22%, var(--backdrop-foam), transparent 60%),
155
+ radial-gradient(1000px 860px at 54% 96%, var(--backdrop-ink), transparent 62%);
156
+ background-attachment: fixed;
157
+ background-repeat: no-repeat;
158
+ }
159
+
160
+ ::selection {
161
+ background: rgb(56 189 248 / 0.28);
162
+ color: var(--foreground);
163
+ }
164
+ }
165
+
166
+ @layer utilities {
167
+ .glass-surface {
168
+ background: var(--glass-bg);
169
+ border: 1px solid var(--glass-border);
170
+ box-shadow: var(--glass-shadow), var(--glass-inset);
171
+ backdrop-filter: blur(var(--glass-blur)) saturate(1.15);
172
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.15);
173
+ }
174
+
175
+ .glass-surface-soft {
176
+ background: var(--glass-bg-soft);
177
+ border: 1px solid var(--glass-border-soft);
178
+ box-shadow: var(--glass-shadow-soft), var(--glass-inset);
179
+ backdrop-filter: blur(calc(var(--glass-blur) - 4px)) saturate(1.12);
180
+ -webkit-backdrop-filter: blur(calc(var(--glass-blur) - 4px)) saturate(1.12);
181
+ }
182
+
183
+ .glass-surface-opaque {
184
+ background: var(--glass-bg-opaque);
185
+ border: 1px solid var(--glass-border);
186
+ box-shadow: var(--glass-shadow), var(--glass-inset);
187
+ backdrop-filter: blur(calc(var(--glass-blur) - 2px)) saturate(1.1);
188
+ -webkit-backdrop-filter: blur(calc(var(--glass-blur) - 2px)) saturate(1.1);
189
+ }
190
+
191
+ .glass-noise {
192
+ /* Don't override elements that already rely on their positioning (e.g. DialogContent uses fixed) */
193
+ }
194
+
195
+ .glass-noise:not(.fixed):not(.absolute):not(.relative):not(.sticky) {
196
+ position: relative;
197
+ overflow: hidden;
198
+ }
199
+
200
+ .glass-noise::after {
201
+ content: "";
202
+ position: absolute;
203
+ inset: 0;
204
+ pointer-events: none;
205
+ opacity: 0.03;
206
+ mix-blend-mode: multiply;
207
+ background-image:
208
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
209
+ background-size: 140px 140px;
210
+ }
211
+
212
+ /* Markdown document-flow: harden list/paragraph spacing to avoid regressions */
213
+ .md-flow {
214
+ max-width: 100%;
215
+ overflow-wrap: anywhere;
216
+ word-break: break-word;
217
+ }
218
+
219
+ .md-flow :where(p) {
220
+ margin: 0 !important;
221
+ line-height: 1.5 !important;
222
+ white-space: pre-wrap !important;
223
+ }
224
+
225
+ .md-flow :where(a) {
226
+ overflow-wrap: anywhere;
227
+ word-break: break-all;
228
+ }
229
+
230
+ .md-flow :where(pre, code) {
231
+ max-width: 100%;
232
+ }
233
+
234
+ .md-flow :where(table) {
235
+ max-width: 100%;
236
+ }
237
+
238
+ .md-flow :where(ul, ol) {
239
+ margin: 0 !important;
240
+ margin-block-start: 0 !important;
241
+ margin-block-end: 0 !important;
242
+ padding: 0 !important;
243
+ padding-left: 1.25rem !important;
244
+ }
245
+
246
+ .md-flow :where(ul) {
247
+ list-style: disc;
248
+ }
249
+
250
+ .md-flow :where(ol) {
251
+ list-style: decimal;
252
+ }
253
+
254
+ .md-flow :where(li) {
255
+ margin: 0 !important;
256
+ padding: 0 !important;
257
+ line-height: 1.45 !important;
258
+ white-space: normal !important;
259
+ }
260
+
261
+ .md-flow :where(li > p) {
262
+ margin: 0 !important;
263
+ }
264
+
265
+ .md-flow :where(li > :where(ul, ol)) {
266
+ margin: 0 !important;
267
+ margin-top: 0 !important;
268
+ margin-bottom: 0 !important;
269
+ }
270
+
271
+ .md-flow :where(ul, ol) :where(ul, ol) {
272
+ margin: 0 !important;
273
+ }
274
+
275
+ .md-flow :where(li) > :where(ul, ol) {
276
+ padding-top: 0 !important;
277
+ padding-bottom: 0 !important;
278
+ }
279
+
280
+ .md-flow :where(p + :where(ul, ol)) {
281
+ margin-top: 0 !important;
282
+ }
283
+
284
+ .md-flow :where(li > p + :where(ul, ol)) {
285
+ margin-top: 0 !important;
286
+ }
287
+
288
+ .md-flow :where(blockquote) {
289
+ margin: 0 !important;
290
+ }
291
+
292
+ [data-slot="scroll-area-scrollbar"] {
293
+ background: transparent;
294
+ }
295
+
296
+ [data-slot="scroll-area-thumb"] {
297
+ background: color-mix(in oklab, var(--foreground) 18%, transparent);
298
+ box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--foreground) 10%, transparent);
299
+ }
300
+
301
+ [data-slot="scroll-area-scrollbar"]:hover [data-slot="scroll-area-thumb"] {
302
+ background: color-mix(in oklab, var(--foreground) 26%, transparent);
303
+ }
304
+ }
@@ -0,0 +1,36 @@
1
+ import type { Metadata } from "next";
2
+ import { Recursive, Source_Sans_3 } from "next/font/google";
3
+ import "./globals.css";
4
+
5
+ const geistSans = Source_Sans_3({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ weight: ["400", "500", "600", "700"],
9
+ });
10
+
11
+ const geistMono = Recursive({
12
+ variable: "--font-geist-mono",
13
+ subsets: ["latin"],
14
+ });
15
+
16
+ export const metadata: Metadata = {
17
+ title: "Cue Hub",
18
+ description: "AI agent group chat console",
19
+ };
20
+
21
+ export default function RootLayout({
22
+ children,
23
+ }: Readonly<{
24
+ children: React.ReactNode;
25
+ }>) {
26
+ return (
27
+ <html lang="en" suppressHydrationWarning>
28
+ <body
29
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
30
+ suppressHydrationWarning
31
+ >
32
+ {children}
33
+ </body>
34
+ </html>
35
+ );
36
+ }
@@ -0,0 +1,109 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { ConversationList } from "@/components/conversation-list";
5
+ import { ChatView } from "@/components/chat-view";
6
+ import { CreateGroupDialog } from "@/components/create-group-dialog";
7
+ import { MessageCircle } from "lucide-react";
8
+
9
+ export default function Home() {
10
+ const [selectedId, setSelectedId] = useState<string | null>(null);
11
+ const [selectedType, setSelectedType] = useState<"agent" | "group" | null>(null);
12
+ const [selectedName, setSelectedName] = useState<string>("");
13
+ const [showCreateGroup, setShowCreateGroup] = useState(false);
14
+ const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
15
+ if (typeof window === "undefined") return false;
16
+ try {
17
+ const raw = window.localStorage.getItem("cuehub.sidebarCollapsed");
18
+ if (raw === "1") return true;
19
+ if (raw === "0") return false;
20
+ } catch {
21
+ // ignore
22
+ }
23
+ return false;
24
+ });
25
+
26
+ useEffect(() => {
27
+ try {
28
+ window.localStorage.setItem("cuehub.sidebarCollapsed", sidebarCollapsed ? "1" : "0");
29
+ } catch {
30
+ // ignore
31
+ }
32
+ }, [sidebarCollapsed]);
33
+
34
+ const handleSelect = (id: string, type: "agent" | "group", name: string) => {
35
+ setSelectedId(id);
36
+ setSelectedType(type);
37
+ setSelectedName(name);
38
+ };
39
+
40
+ const handleBack = () => {
41
+ setSelectedId(null);
42
+ setSelectedType(null);
43
+ };
44
+
45
+ const handleGroupCreated = (groupId: string, groupName: string) => {
46
+ setSelectedId(groupId);
47
+ setSelectedType("group");
48
+ setSelectedName(groupName);
49
+ };
50
+
51
+ return (
52
+ <div className="flex h-screen bg-transparent overflow-hidden">
53
+ {/* Desktop: Side by side */}
54
+ <div
55
+ className="hidden md:flex md:h-full md:w-full"
56
+ style={{
57
+ ["--cuehub-sidebar-w" as never]: sidebarCollapsed ? "4rem" : "18rem",
58
+ }}
59
+ >
60
+ <ConversationList
61
+ selectedId={selectedId}
62
+ selectedType={selectedType}
63
+ onSelect={handleSelect}
64
+ onCreateGroup={() => setShowCreateGroup(true)}
65
+ collapsed={sidebarCollapsed}
66
+ onToggleCollapsed={() => setSidebarCollapsed((v) => !v)}
67
+ />
68
+ {selectedId && selectedType ? (
69
+ <ChatView
70
+ type={selectedType}
71
+ id={selectedId}
72
+ name={selectedName}
73
+ />
74
+ ) : (
75
+ <div className="flex flex-1 flex-col items-center justify-center text-muted-foreground">
76
+ <MessageCircle className="mb-4 h-16 w-16 opacity-50" />
77
+ <p className="text-lg">Select a conversation to start chatting</p>
78
+ <p className="mt-2 text-sm">Or click + in the top-right to create a group</p>
79
+ </div>
80
+ )}
81
+ </div>
82
+
83
+ {/* Mobile: Stack view with smooth transition */}
84
+ <div className="flex h-full w-full flex-col md:hidden">
85
+ {selectedId && selectedType ? (
86
+ <ChatView
87
+ type={selectedType}
88
+ id={selectedId}
89
+ name={selectedName}
90
+ onBack={handleBack}
91
+ />
92
+ ) : (
93
+ <ConversationList
94
+ selectedId={selectedId}
95
+ selectedType={selectedType}
96
+ onSelect={handleSelect}
97
+ onCreateGroup={() => setShowCreateGroup(true)}
98
+ />
99
+ )}
100
+ </div>
101
+
102
+ <CreateGroupDialog
103
+ open={showCreateGroup}
104
+ onOpenChange={setShowCreateGroup}
105
+ onCreated={handleGroupCreated}
106
+ />
107
+ </div>
108
+ );
109
+ }