clawport-ui 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.
Files changed (132) hide show
  1. package/.env.example +35 -0
  2. package/BRANDING.md +131 -0
  3. package/CLAUDE.md +252 -0
  4. package/README.md +262 -0
  5. package/SETUP.md +337 -0
  6. package/app/agents/[id]/page.tsx +727 -0
  7. package/app/api/agents/route.ts +12 -0
  8. package/app/api/chat/[id]/route.ts +139 -0
  9. package/app/api/cron-runs/route.ts +13 -0
  10. package/app/api/crons/route.ts +12 -0
  11. package/app/api/kanban/chat/[id]/route.ts +119 -0
  12. package/app/api/kanban/chat-history/[ticketId]/route.ts +36 -0
  13. package/app/api/memory/route.ts +12 -0
  14. package/app/api/transcribe/route.ts +37 -0
  15. package/app/api/tts/route.ts +42 -0
  16. package/app/chat/[id]/page.tsx +10 -0
  17. package/app/chat/page.tsx +200 -0
  18. package/app/crons/page.tsx +870 -0
  19. package/app/docs/page.tsx +399 -0
  20. package/app/favicon.ico +0 -0
  21. package/app/globals.css +692 -0
  22. package/app/kanban/page.tsx +327 -0
  23. package/app/layout.tsx +45 -0
  24. package/app/memory/page.tsx +685 -0
  25. package/app/page.tsx +817 -0
  26. package/app/providers.tsx +37 -0
  27. package/app/settings/page.tsx +901 -0
  28. package/app/settings-provider.tsx +209 -0
  29. package/components/AgentAvatar.tsx +54 -0
  30. package/components/AgentNode.tsx +122 -0
  31. package/components/Breadcrumbs.tsx +126 -0
  32. package/components/DynamicFavicon.tsx +62 -0
  33. package/components/ErrorState.tsx +97 -0
  34. package/components/FeedView.tsx +494 -0
  35. package/components/GlobalSearch.tsx +571 -0
  36. package/components/GridView.tsx +532 -0
  37. package/components/ManorMap.tsx +157 -0
  38. package/components/MobileSidebar.tsx +251 -0
  39. package/components/NavLinks.tsx +271 -0
  40. package/components/OnboardingWizard.tsx +1067 -0
  41. package/components/Sidebar.tsx +115 -0
  42. package/components/ThemeToggle.tsx +108 -0
  43. package/components/chat/AgentList.tsx +537 -0
  44. package/components/chat/ConversationView.tsx +1047 -0
  45. package/components/chat/FileAttachment.tsx +140 -0
  46. package/components/chat/MediaPreview.tsx +111 -0
  47. package/components/chat/VoiceMessage.tsx +139 -0
  48. package/components/crons/PipelineGraph.tsx +327 -0
  49. package/components/crons/WeeklySchedule.tsx +630 -0
  50. package/components/docs/AgentsSection.tsx +209 -0
  51. package/components/docs/ApiReferenceSection.tsx +256 -0
  52. package/components/docs/ArchitectureSection.tsx +221 -0
  53. package/components/docs/ComponentsSection.tsx +253 -0
  54. package/components/docs/CronSystemSection.tsx +235 -0
  55. package/components/docs/DocSection.tsx +346 -0
  56. package/components/docs/GettingStartedSection.tsx +169 -0
  57. package/components/docs/ThemingSection.tsx +257 -0
  58. package/components/docs/TroubleshootingSection.tsx +200 -0
  59. package/components/kanban/AgentPicker.tsx +321 -0
  60. package/components/kanban/CreateTicketModal.tsx +333 -0
  61. package/components/kanban/KanbanBoard.tsx +70 -0
  62. package/components/kanban/KanbanColumn.tsx +166 -0
  63. package/components/kanban/TicketCard.tsx +245 -0
  64. package/components/kanban/TicketDetailPanel.tsx +850 -0
  65. package/components/ui/badge.tsx +48 -0
  66. package/components/ui/button.tsx +64 -0
  67. package/components/ui/card.tsx +92 -0
  68. package/components/ui/dialog.tsx +158 -0
  69. package/components/ui/scroll-area.tsx +58 -0
  70. package/components/ui/separator.tsx +28 -0
  71. package/components/ui/skeleton.tsx +27 -0
  72. package/components/ui/tabs.tsx +91 -0
  73. package/components/ui/tooltip.tsx +57 -0
  74. package/components.json +23 -0
  75. package/docs/API.md +648 -0
  76. package/docs/COMPONENTS.md +1059 -0
  77. package/docs/THEMING.md +795 -0
  78. package/lib/agents-registry.ts +35 -0
  79. package/lib/agents.json +282 -0
  80. package/lib/agents.test.ts +367 -0
  81. package/lib/agents.ts +32 -0
  82. package/lib/anthropic.test.ts +422 -0
  83. package/lib/anthropic.ts +220 -0
  84. package/lib/api-error.ts +16 -0
  85. package/lib/audio-recorder.test.ts +72 -0
  86. package/lib/audio-recorder.ts +169 -0
  87. package/lib/conversations.test.ts +331 -0
  88. package/lib/conversations.ts +117 -0
  89. package/lib/cron-pipelines.test.ts +69 -0
  90. package/lib/cron-pipelines.ts +58 -0
  91. package/lib/cron-runs.test.ts +118 -0
  92. package/lib/cron-runs.ts +67 -0
  93. package/lib/cron-utils.test.ts +222 -0
  94. package/lib/cron-utils.ts +160 -0
  95. package/lib/crons.test.ts +502 -0
  96. package/lib/crons.ts +114 -0
  97. package/lib/env.test.ts +44 -0
  98. package/lib/env.ts +14 -0
  99. package/lib/kanban/automation.test.ts +245 -0
  100. package/lib/kanban/automation.ts +143 -0
  101. package/lib/kanban/chat-store.test.ts +149 -0
  102. package/lib/kanban/chat-store.ts +81 -0
  103. package/lib/kanban/store.test.ts +238 -0
  104. package/lib/kanban/store.ts +98 -0
  105. package/lib/kanban/types.ts +50 -0
  106. package/lib/kanban/useAgentWork.ts +78 -0
  107. package/lib/memory.ts +45 -0
  108. package/lib/multimodal.test.ts +219 -0
  109. package/lib/multimodal.ts +68 -0
  110. package/lib/pipeline.integration.test.ts +343 -0
  111. package/lib/sanitize.ts +194 -0
  112. package/lib/settings.test.ts +137 -0
  113. package/lib/settings.ts +94 -0
  114. package/lib/styles.ts +24 -0
  115. package/lib/themes.ts +9 -0
  116. package/lib/transcribe.test.ts +141 -0
  117. package/lib/transcribe.ts +111 -0
  118. package/lib/types.ts +66 -0
  119. package/lib/utils.ts +6 -0
  120. package/lib/validation.test.ts +132 -0
  121. package/lib/validation.ts +80 -0
  122. package/next.config.ts +7 -0
  123. package/package.json +56 -0
  124. package/postcss.config.mjs +7 -0
  125. package/public/file.svg +1 -0
  126. package/public/globe.svg +1 -0
  127. package/public/next.svg +1 -0
  128. package/public/vercel.svg +1 -0
  129. package/public/window.svg +1 -0
  130. package/scripts/setup.mjs +215 -0
  131. package/tsconfig.json +34 -0
  132. package/vitest.config.ts +17 -0
@@ -0,0 +1,399 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { GettingStartedSection } from "@/components/docs/GettingStartedSection";
5
+ import { ArchitectureSection } from "@/components/docs/ArchitectureSection";
6
+ import { AgentsSection } from "@/components/docs/AgentsSection";
7
+ import { ApiReferenceSection } from "@/components/docs/ApiReferenceSection";
8
+ import { CronSystemSection } from "@/components/docs/CronSystemSection";
9
+ import { ThemingSection } from "@/components/docs/ThemingSection";
10
+ import { ComponentsSection } from "@/components/docs/ComponentsSection";
11
+ import { TroubleshootingSection } from "@/components/docs/TroubleshootingSection";
12
+
13
+ /* ─── Section Definitions ──────────────────────────────────────── */
14
+
15
+ interface DocSectionDef {
16
+ id: string;
17
+ label: string;
18
+ emoji: string;
19
+ description: string;
20
+ component: React.ComponentType;
21
+ }
22
+
23
+ const SECTIONS: DocSectionDef[] = [
24
+ {
25
+ id: "getting-started",
26
+ label: "Getting Started",
27
+ emoji: "\u{1F680}",
28
+ description: "Setup, prerequisites, env vars",
29
+ component: GettingStartedSection,
30
+ },
31
+ {
32
+ id: "architecture",
33
+ label: "Architecture",
34
+ emoji: "\u{1F3D7}\u{FE0F}",
35
+ description: "System overview, pipelines, data flows",
36
+ component: ArchitectureSection,
37
+ },
38
+ {
39
+ id: "agents",
40
+ label: "Agents",
41
+ emoji: "\u{1F916}",
42
+ description: "Registry, hierarchy, customization",
43
+ component: AgentsSection,
44
+ },
45
+ {
46
+ id: "api-reference",
47
+ label: "API Reference",
48
+ emoji: "\u{1F50C}",
49
+ description: "All endpoints, request/response",
50
+ component: ApiReferenceSection,
51
+ },
52
+ {
53
+ id: "cron-system",
54
+ label: "Cron System",
55
+ emoji: "\u{23F0}",
56
+ description: "Schedules, monitoring, delivery",
57
+ component: CronSystemSection,
58
+ },
59
+ {
60
+ id: "theming",
61
+ label: "Theming",
62
+ emoji: "\u{1F3A8}",
63
+ description: "Themes, CSS properties, customization",
64
+ component: ThemingSection,
65
+ },
66
+ {
67
+ id: "components",
68
+ label: "Components",
69
+ emoji: "\u{1F9E9}",
70
+ description: "Component tree, props, patterns",
71
+ component: ComponentsSection,
72
+ },
73
+ {
74
+ id: "troubleshooting",
75
+ label: "Troubleshooting",
76
+ emoji: "\u{1F527}",
77
+ description: "Common issues and solutions",
78
+ component: TroubleshootingSection,
79
+ },
80
+ ];
81
+
82
+ /* ─── Back Arrow ───────────────────────────────────────────────── */
83
+
84
+ function BackArrow() {
85
+ return (
86
+ <svg
87
+ width="16"
88
+ height="16"
89
+ viewBox="0 0 16 16"
90
+ fill="none"
91
+ stroke="currentColor"
92
+ strokeWidth="1.5"
93
+ strokeLinecap="round"
94
+ strokeLinejoin="round"
95
+ >
96
+ <polyline points="10 3 5 8 10 13" />
97
+ </svg>
98
+ );
99
+ }
100
+
101
+ /* ─── Component ────────────────────────────────────────────────── */
102
+
103
+ export default function DocsPage() {
104
+ const [activeSection, setActiveSection] = useState(SECTIONS[0].id);
105
+ const [search, setSearch] = useState("");
106
+ const [mobileShowContent, setMobileShowContent] = useState(false);
107
+
108
+ const listRef = useRef<HTMLDivElement>(null);
109
+ const searchRef = useRef<HTMLInputElement>(null);
110
+
111
+ /* Hash routing: read on mount */
112
+ useEffect(() => {
113
+ const hash = window.location.hash.replace("#", "");
114
+ if (hash && SECTIONS.some((s) => s.id === hash)) {
115
+ setActiveSection(hash);
116
+ setMobileShowContent(true);
117
+ }
118
+ }, []);
119
+
120
+ /* Hash routing: update on section change */
121
+ const selectSection = useCallback((id: string) => {
122
+ setActiveSection(id);
123
+ setMobileShowContent(true);
124
+ window.history.replaceState(null, "", `#${id}`);
125
+ }, []);
126
+
127
+ /* Filtered sections by search */
128
+ const filteredSections = useMemo(
129
+ () =>
130
+ SECTIONS.filter(
131
+ (s) =>
132
+ s.label.toLowerCase().includes(search.toLowerCase()) ||
133
+ s.description.toLowerCase().includes(search.toLowerCase())
134
+ ),
135
+ [search]
136
+ );
137
+
138
+ /* Keyboard navigation in section list */
139
+ function handleListKeyDown(e: React.KeyboardEvent) {
140
+ const items =
141
+ listRef.current?.querySelectorAll<HTMLButtonElement>('[role="option"]');
142
+ if (!items || items.length === 0) return;
143
+
144
+ const currentIdx = Array.from(items).findIndex(
145
+ (el) => el.getAttribute("aria-selected") === "true"
146
+ );
147
+
148
+ let nextIdx = currentIdx;
149
+
150
+ if (e.key === "ArrowDown") {
151
+ e.preventDefault();
152
+ nextIdx = Math.min(currentIdx + 1, items.length - 1);
153
+ } else if (e.key === "ArrowUp") {
154
+ e.preventDefault();
155
+ nextIdx = Math.max(currentIdx - 1, 0);
156
+ } else if (e.key === "Enter") {
157
+ e.preventDefault();
158
+ if (currentIdx >= 0) {
159
+ items[currentIdx].click();
160
+ }
161
+ return;
162
+ } else if (e.key === "Escape") {
163
+ e.preventDefault();
164
+ searchRef.current?.focus();
165
+ return;
166
+ }
167
+
168
+ if (nextIdx !== currentIdx && nextIdx >= 0) {
169
+ items[nextIdx].click();
170
+ items[nextIdx].focus();
171
+ }
172
+ }
173
+
174
+ /* Active section definition */
175
+ const active = SECTIONS.find((s) => s.id === activeSection) ?? SECTIONS[0];
176
+ const ActiveComponent = active.component;
177
+
178
+ return (
179
+ <div
180
+ className="flex h-full animate-fade-in"
181
+ style={{ background: "var(--bg)" }}
182
+ >
183
+ {/* ── Section list sidebar ─────────────────────────────── */}
184
+ <aside
185
+ className={`flex-shrink-0 flex flex-col ${
186
+ mobileShowContent ? "hidden md:flex" : "flex"
187
+ }`}
188
+ style={{
189
+ width: "100%",
190
+ maxWidth: "100%",
191
+ background: "var(--material-regular)",
192
+ backdropFilter: "var(--sidebar-backdrop)",
193
+ WebkitBackdropFilter: "var(--sidebar-backdrop)",
194
+ borderRight: "1px solid var(--separator)",
195
+ }}
196
+ >
197
+ <style>{`@media (min-width: 768px) { aside { width: 280px !important; min-width: 280px !important; } }`}</style>
198
+
199
+ {/* Sidebar header */}
200
+ <div
201
+ className="flex items-center justify-between flex-shrink-0"
202
+ style={{
203
+ padding: "var(--space-3) var(--space-4)",
204
+ borderBottom: "1px solid var(--separator)",
205
+ }}
206
+ >
207
+ <span
208
+ style={{
209
+ fontSize: "var(--text-body)",
210
+ fontWeight: "var(--weight-semibold)",
211
+ color: "var(--text-primary)",
212
+ }}
213
+ >
214
+ Docs
215
+ </span>
216
+ </div>
217
+
218
+ {/* Search */}
219
+ <div style={{ padding: "var(--space-2) var(--space-3)" }}>
220
+ <input
221
+ ref={searchRef}
222
+ type="search"
223
+ placeholder="Search sections..."
224
+ value={search}
225
+ onChange={(e) => setSearch(e.target.value)}
226
+ className="apple-input focus-ring"
227
+ aria-label="Search documentation sections"
228
+ style={{
229
+ width: "100%",
230
+ height: 32,
231
+ fontSize: "var(--text-footnote)",
232
+ padding: "0 var(--space-3)",
233
+ borderRadius: "var(--radius-sm)",
234
+ }}
235
+ />
236
+ </div>
237
+
238
+ {/* Section list */}
239
+ <div
240
+ ref={listRef}
241
+ role="listbox"
242
+ aria-label="Documentation sections"
243
+ onKeyDown={handleListKeyDown}
244
+ className="flex-1 overflow-y-auto"
245
+ >
246
+ {filteredSections.length === 0 ? (
247
+ <div
248
+ className="flex items-center justify-center"
249
+ style={{
250
+ height: 120,
251
+ fontSize: "var(--text-footnote)",
252
+ color: "var(--text-tertiary)",
253
+ }}
254
+ >
255
+ No sections match
256
+ </div>
257
+ ) : (
258
+ filteredSections.map((section) => {
259
+ const isActive = activeSection === section.id;
260
+ return (
261
+ <button
262
+ key={section.id}
263
+ role="option"
264
+ aria-selected={isActive}
265
+ onClick={() => selectSection(section.id)}
266
+ className="w-full text-left hover-bg focus-ring"
267
+ style={{
268
+ display: "flex",
269
+ alignItems: "flex-start",
270
+ gap: "var(--space-3)",
271
+ padding: "var(--space-3) var(--space-4)",
272
+ border: "none",
273
+ cursor: "pointer",
274
+ background: isActive
275
+ ? "var(--fill-secondary)"
276
+ : "transparent",
277
+ borderLeft: isActive
278
+ ? "3px solid var(--accent)"
279
+ : "3px solid transparent",
280
+ }}
281
+ >
282
+ <span
283
+ style={{
284
+ fontSize: "var(--text-body)",
285
+ lineHeight: "1",
286
+ flexShrink: 0,
287
+ marginTop: 1,
288
+ }}
289
+ >
290
+ {section.emoji}
291
+ </span>
292
+ <div className="min-w-0 flex-1">
293
+ <div
294
+ style={{
295
+ fontSize: "var(--text-footnote)",
296
+ fontWeight: "var(--weight-semibold)",
297
+ color: "var(--text-primary)",
298
+ lineHeight: "var(--leading-snug)",
299
+ }}
300
+ >
301
+ {section.label}
302
+ </div>
303
+ <div
304
+ style={{
305
+ fontSize: "var(--text-caption2)",
306
+ color: "var(--text-tertiary)",
307
+ marginTop: 2,
308
+ }}
309
+ >
310
+ {section.description}
311
+ </div>
312
+ </div>
313
+ </button>
314
+ );
315
+ })
316
+ )}
317
+ </div>
318
+ </aside>
319
+
320
+ {/* ── Content view ─────────────────────────────────────── */}
321
+ <main
322
+ className={`flex-1 flex flex-col overflow-hidden ${
323
+ !mobileShowContent ? "hidden md:flex" : "flex"
324
+ }`}
325
+ style={{ background: "var(--bg)" }}
326
+ >
327
+ {/* Content header (sticky) */}
328
+ <div
329
+ className="flex-shrink-0"
330
+ style={{
331
+ padding: "var(--space-3) var(--space-6)",
332
+ borderBottom: "1px solid var(--separator)",
333
+ background: "var(--material-regular)",
334
+ backdropFilter: "blur(40px) saturate(180%)",
335
+ WebkitBackdropFilter: "blur(40px) saturate(180%)",
336
+ }}
337
+ >
338
+ {/* Mobile back button */}
339
+ <button
340
+ onClick={() => setMobileShowContent(false)}
341
+ className="md:hidden btn-ghost focus-ring"
342
+ aria-label="Back to section list"
343
+ style={{
344
+ display: "inline-flex",
345
+ alignItems: "center",
346
+ gap: "var(--space-1)",
347
+ padding: "4px 8px",
348
+ borderRadius: "var(--radius-sm)",
349
+ fontSize: "var(--text-footnote)",
350
+ color: "var(--system-blue)",
351
+ marginBottom: "var(--space-2)",
352
+ marginLeft: "-8px",
353
+ }}
354
+ >
355
+ <BackArrow />
356
+ Sections
357
+ </button>
358
+
359
+ <div className="flex items-center gap-3">
360
+ <span style={{ fontSize: "var(--text-title3)" }}>
361
+ {active.emoji}
362
+ </span>
363
+ <div>
364
+ <div
365
+ style={{
366
+ fontSize: "var(--text-body)",
367
+ fontWeight: "var(--weight-semibold)",
368
+ color: "var(--text-primary)",
369
+ }}
370
+ >
371
+ {active.label}
372
+ </div>
373
+ <div
374
+ style={{
375
+ fontSize: "var(--text-caption1)",
376
+ color: "var(--text-tertiary)",
377
+ }}
378
+ >
379
+ {active.description}
380
+ </div>
381
+ </div>
382
+ </div>
383
+ </div>
384
+
385
+ {/* Scrollable content area */}
386
+ <div
387
+ className="flex-1 overflow-y-auto"
388
+ style={{
389
+ padding: "var(--space-6) var(--space-10)",
390
+ }}
391
+ >
392
+ <div style={{ maxWidth: 760, margin: "0 auto" }}>
393
+ <ActiveComponent />
394
+ </div>
395
+ </div>
396
+ </main>
397
+ </div>
398
+ );
399
+ }
Binary file