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,346 @@
1
+ import type { ReactNode, CSSProperties } from "react";
2
+
3
+ /* ─── Headings ─────────────────────────────────────────────────── */
4
+
5
+ export function Heading({ children }: { children: ReactNode }) {
6
+ return (
7
+ <h2
8
+ style={{
9
+ fontSize: "var(--text-title2)",
10
+ fontWeight: "var(--weight-bold)",
11
+ color: "var(--text-primary)",
12
+ lineHeight: "var(--leading-snug)",
13
+ marginTop: "var(--space-10)",
14
+ marginBottom: "var(--space-4)",
15
+ }}
16
+ >
17
+ {children}
18
+ </h2>
19
+ );
20
+ }
21
+
22
+ export function SubHeading({ children }: { children: ReactNode }) {
23
+ return (
24
+ <h3
25
+ style={{
26
+ fontSize: "var(--text-body)",
27
+ fontWeight: "var(--weight-semibold)",
28
+ color: "var(--text-primary)",
29
+ lineHeight: "var(--leading-snug)",
30
+ marginTop: "var(--space-8)",
31
+ marginBottom: "var(--space-3)",
32
+ }}
33
+ >
34
+ {children}
35
+ </h3>
36
+ );
37
+ }
38
+
39
+ /* ─── Text ─────────────────────────────────────────────────────── */
40
+
41
+ export function Paragraph({ children }: { children: ReactNode }) {
42
+ return (
43
+ <p
44
+ style={{
45
+ fontSize: "var(--text-subheadline)",
46
+ lineHeight: "var(--leading-relaxed)",
47
+ color: "var(--text-secondary)",
48
+ marginBottom: "var(--space-4)",
49
+ }}
50
+ >
51
+ {children}
52
+ </p>
53
+ );
54
+ }
55
+
56
+ /* ─── Code ─────────────────────────────────────────────────────── */
57
+
58
+ export function CodeBlock({
59
+ children,
60
+ title,
61
+ }: {
62
+ children: string;
63
+ title?: string;
64
+ }) {
65
+ return (
66
+ <div
67
+ style={{
68
+ background: "var(--code-bg)",
69
+ border: "1px solid var(--code-border)",
70
+ borderRadius: "var(--radius-md)",
71
+ marginBottom: "var(--space-4)",
72
+ overflow: "hidden",
73
+ }}
74
+ >
75
+ {title && (
76
+ <div
77
+ style={{
78
+ padding: "var(--space-2) var(--space-4)",
79
+ borderBottom: "1px solid var(--code-border)",
80
+ fontSize: "var(--text-caption1)",
81
+ fontWeight: "var(--weight-semibold)",
82
+ color: "var(--text-tertiary)",
83
+ fontFamily: "var(--font-mono)",
84
+ }}
85
+ >
86
+ {title}
87
+ </div>
88
+ )}
89
+ <pre
90
+ style={{
91
+ padding: "var(--space-4)",
92
+ margin: 0,
93
+ fontSize: "var(--text-footnote)",
94
+ lineHeight: "var(--leading-relaxed)",
95
+ color: "var(--code-text)",
96
+ fontFamily: "var(--font-mono)",
97
+ whiteSpace: "pre-wrap",
98
+ overflowX: "auto",
99
+ }}
100
+ >
101
+ {children}
102
+ </pre>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ export function InlineCode({ children }: { children: ReactNode }) {
108
+ return (
109
+ <code
110
+ style={{
111
+ background: "var(--code-bg)",
112
+ border: "1px solid var(--code-border)",
113
+ borderRadius: "4px",
114
+ padding: "1px 6px",
115
+ fontSize: "0.9em",
116
+ fontFamily: "var(--font-mono)",
117
+ color: "var(--code-text)",
118
+ }}
119
+ >
120
+ {children}
121
+ </code>
122
+ );
123
+ }
124
+
125
+ /* ─── Cards & Panels ───────────────────────────────────────────── */
126
+
127
+ export function InfoCard({
128
+ title,
129
+ children,
130
+ style,
131
+ }: {
132
+ title?: string;
133
+ children: ReactNode;
134
+ style?: CSSProperties;
135
+ }) {
136
+ return (
137
+ <div
138
+ style={{
139
+ background: "var(--material-regular)",
140
+ border: "1px solid var(--separator)",
141
+ borderRadius: "var(--radius-md)",
142
+ padding: "var(--space-4) var(--space-5)",
143
+ marginBottom: "var(--space-4)",
144
+ ...style,
145
+ }}
146
+ >
147
+ {title && (
148
+ <div
149
+ style={{
150
+ fontSize: "var(--text-subheadline)",
151
+ fontWeight: "var(--weight-semibold)",
152
+ color: "var(--text-primary)",
153
+ marginBottom: "var(--space-3)",
154
+ }}
155
+ >
156
+ {title}
157
+ </div>
158
+ )}
159
+ {children}
160
+ </div>
161
+ );
162
+ }
163
+
164
+ /* ─── Tables ───────────────────────────────────────────────────── */
165
+
166
+ export function Table({
167
+ headers,
168
+ rows,
169
+ }: {
170
+ headers: string[];
171
+ rows: (string | ReactNode)[][];
172
+ }) {
173
+ return (
174
+ <div
175
+ style={{
176
+ overflowX: "auto",
177
+ marginBottom: "var(--space-4)",
178
+ border: "1px solid var(--separator)",
179
+ borderRadius: "var(--radius-md)",
180
+ }}
181
+ >
182
+ <table
183
+ style={{
184
+ width: "100%",
185
+ borderCollapse: "collapse",
186
+ fontSize: "var(--text-footnote)",
187
+ lineHeight: "var(--leading-normal)",
188
+ }}
189
+ >
190
+ <thead>
191
+ <tr>
192
+ {headers.map((h, i) => (
193
+ <th
194
+ key={i}
195
+ style={{
196
+ textAlign: "left",
197
+ padding: "var(--space-2) var(--space-3)",
198
+ fontWeight: "var(--weight-semibold)",
199
+ color: "var(--text-primary)",
200
+ background: "var(--material-thin)",
201
+ borderBottom: "1px solid var(--separator)",
202
+ whiteSpace: "nowrap",
203
+ }}
204
+ >
205
+ {h}
206
+ </th>
207
+ ))}
208
+ </tr>
209
+ </thead>
210
+ <tbody>
211
+ {rows.map((row, ri) => (
212
+ <tr key={ri}>
213
+ {row.map((cell, ci) => (
214
+ <td
215
+ key={ci}
216
+ style={{
217
+ padding: "var(--space-2) var(--space-3)",
218
+ color: "var(--text-secondary)",
219
+ borderBottom:
220
+ ri < rows.length - 1
221
+ ? "1px solid var(--separator)"
222
+ : undefined,
223
+ verticalAlign: "top",
224
+ }}
225
+ >
226
+ {cell}
227
+ </td>
228
+ ))}
229
+ </tr>
230
+ ))}
231
+ </tbody>
232
+ </table>
233
+ </div>
234
+ );
235
+ }
236
+
237
+ /* ─── Lists ────────────────────────────────────────────────────── */
238
+
239
+ export function BulletList({ items }: { items: ReactNode[] }) {
240
+ return (
241
+ <ul
242
+ style={{
243
+ marginBottom: "var(--space-4)",
244
+ paddingLeft: "var(--space-5)",
245
+ }}
246
+ >
247
+ {items.map((item, i) => (
248
+ <li
249
+ key={i}
250
+ style={{
251
+ fontSize: "var(--text-subheadline)",
252
+ lineHeight: "var(--leading-relaxed)",
253
+ color: "var(--text-secondary)",
254
+ marginBottom: "var(--space-1)",
255
+ }}
256
+ >
257
+ {item}
258
+ </li>
259
+ ))}
260
+ </ul>
261
+ );
262
+ }
263
+
264
+ export function NumberedList({ items }: { items: ReactNode[] }) {
265
+ return (
266
+ <ol
267
+ style={{
268
+ marginBottom: "var(--space-4)",
269
+ paddingLeft: "var(--space-5)",
270
+ }}
271
+ >
272
+ {items.map((item, i) => (
273
+ <li
274
+ key={i}
275
+ style={{
276
+ fontSize: "var(--text-subheadline)",
277
+ lineHeight: "var(--leading-relaxed)",
278
+ color: "var(--text-secondary)",
279
+ marginBottom: "var(--space-1)",
280
+ }}
281
+ >
282
+ {item}
283
+ </li>
284
+ ))}
285
+ </ol>
286
+ );
287
+ }
288
+
289
+ /* ─── Callout ──────────────────────────────────────────────────── */
290
+
291
+ const CALLOUT_COLORS: Record<string, string> = {
292
+ tip: "var(--system-green)",
293
+ warning: "var(--system-orange)",
294
+ note: "var(--system-blue)",
295
+ error: "var(--system-red)",
296
+ };
297
+
298
+ const CALLOUT_LABELS: Record<string, string> = {
299
+ tip: "Tip",
300
+ warning: "Warning",
301
+ note: "Note",
302
+ error: "Error",
303
+ };
304
+
305
+ export function Callout({
306
+ type = "note",
307
+ children,
308
+ }: {
309
+ type?: "tip" | "warning" | "note" | "error";
310
+ children: ReactNode;
311
+ }) {
312
+ const color = CALLOUT_COLORS[type] ?? "var(--system-blue)";
313
+ return (
314
+ <div
315
+ style={{
316
+ borderLeft: `3px solid ${color}`,
317
+ background: "var(--material-thin)",
318
+ borderRadius: "0 var(--radius-sm) var(--radius-sm) 0",
319
+ padding: "var(--space-3) var(--space-4)",
320
+ marginBottom: "var(--space-4)",
321
+ }}
322
+ >
323
+ <div
324
+ style={{
325
+ fontSize: "var(--text-caption1)",
326
+ fontWeight: "var(--weight-semibold)",
327
+ color,
328
+ textTransform: "uppercase",
329
+ letterSpacing: "0.04em",
330
+ marginBottom: "var(--space-1)",
331
+ }}
332
+ >
333
+ {CALLOUT_LABELS[type] ?? "Note"}
334
+ </div>
335
+ <div
336
+ style={{
337
+ fontSize: "var(--text-footnote)",
338
+ lineHeight: "var(--leading-relaxed)",
339
+ color: "var(--text-secondary)",
340
+ }}
341
+ >
342
+ {children}
343
+ </div>
344
+ </div>
345
+ );
346
+ }
@@ -0,0 +1,169 @@
1
+ import {
2
+ Heading,
3
+ SubHeading,
4
+ Paragraph,
5
+ CodeBlock,
6
+ InlineCode,
7
+ Table,
8
+ BulletList,
9
+ NumberedList,
10
+ Callout,
11
+ } from "./DocSection";
12
+
13
+ export function GettingStartedSection() {
14
+ return (
15
+ <>
16
+ <Heading>Getting Started</Heading>
17
+ <Paragraph>
18
+ This guide walks you through getting ClawPort running against your own
19
+ OpenClaw instance. ClawPort is a Next.js 16 dashboard for managing,
20
+ monitoring, and talking directly to your OpenClaw AI agents.
21
+ </Paragraph>
22
+
23
+ <SubHeading>Prerequisites</SubHeading>
24
+ <BulletList
25
+ items={[
26
+ <>
27
+ <strong style={{ color: "var(--text-primary)" }}>Node.js 22+</strong>{" "}
28
+ -- verify with <InlineCode>node -v</InlineCode>
29
+ </>,
30
+ <>
31
+ <strong style={{ color: "var(--text-primary)" }}>OpenClaw</strong>{" "}
32
+ -- installed and working: <InlineCode>openclaw --version</InlineCode>
33
+ </>,
34
+ <>
35
+ <strong style={{ color: "var(--text-primary)" }}>
36
+ OpenClaw gateway running
37
+ </strong>{" "}
38
+ -- ClawPort talks to the gateway at{" "}
39
+ <InlineCode>localhost:18789</InlineCode>
40
+ </>,
41
+ ]}
42
+ />
43
+
44
+ <SubHeading>Quick Start</SubHeading>
45
+ <CodeBlock title="terminal">
46
+ {`# Clone the repo
47
+ git clone https://github.com/openclaw/clawport.git
48
+ cd clawport
49
+
50
+ # Install dependencies
51
+ npm install
52
+
53
+ # Auto-detect your OpenClaw config and write .env.local
54
+ npm run setup
55
+
56
+ # Start the dev server
57
+ npm run dev`}
58
+ </CodeBlock>
59
+ <Paragraph>
60
+ Open <InlineCode>http://localhost:3000</InlineCode>. On first launch
61
+ you'll see the onboarding wizard which walks you through naming your
62
+ portal, choosing a theme, and personalizing agent avatars.
63
+ </Paragraph>
64
+
65
+ <SubHeading>Environment Variables</SubHeading>
66
+ <Paragraph>
67
+ The fastest way to configure is the auto-setup script:{" "}
68
+ <InlineCode>npm run setup</InlineCode>. It auto-detects your{" "}
69
+ <InlineCode>WORKSPACE_PATH</InlineCode>,{" "}
70
+ <InlineCode>OPENCLAW_BIN</InlineCode>, and gateway token from your local
71
+ OpenClaw installation.
72
+ </Paragraph>
73
+ <Paragraph>
74
+ To configure manually, copy the template and edit:
75
+ </Paragraph>
76
+ <CodeBlock>{`cp .env.example .env.local`}</CodeBlock>
77
+
78
+ <Table
79
+ headers={["Variable", "Required", "Description"]}
80
+ rows={[
81
+ [
82
+ <InlineCode key="ws">WORKSPACE_PATH</InlineCode>,
83
+ "Yes",
84
+ "Path to your OpenClaw workspace directory (default: ~/.openclaw/workspace)",
85
+ ],
86
+ [
87
+ <InlineCode key="bin">OPENCLAW_BIN</InlineCode>,
88
+ "Yes",
89
+ "Absolute path to the openclaw CLI binary",
90
+ ],
91
+ [
92
+ <InlineCode key="tok">OPENCLAW_GATEWAY_TOKEN</InlineCode>,
93
+ "Yes",
94
+ "Token that authenticates all API calls to the gateway",
95
+ ],
96
+ [
97
+ <InlineCode key="el">ELEVENLABS_API_KEY</InlineCode>,
98
+ "No",
99
+ "ElevenLabs API key for voice/TTS indicators on agent profiles",
100
+ ],
101
+ ]}
102
+ />
103
+
104
+ <Callout type="tip">
105
+ No separate AI API keys are needed. All AI calls (chat, vision, TTS,
106
+ transcription) route through the OpenClaw gateway. One subscription, one
107
+ token.
108
+ </Callout>
109
+
110
+ <SubHeading>Finding Your Values</SubHeading>
111
+ <NumberedList
112
+ items={[
113
+ <>
114
+ <strong style={{ color: "var(--text-primary)" }}>WORKSPACE_PATH</strong>:{" "}
115
+ Run <InlineCode>ls ~/.openclaw/workspace</InlineCode> to verify.
116
+ You should see files like <InlineCode>SOUL.md</InlineCode>, an{" "}
117
+ <InlineCode>agents/</InlineCode> directory, and a{" "}
118
+ <InlineCode>memory/</InlineCode> directory.
119
+ </>,
120
+ <>
121
+ <strong style={{ color: "var(--text-primary)" }}>OPENCLAW_BIN</strong>:{" "}
122
+ Run <InlineCode>which openclaw</InlineCode> and use the full path.
123
+ </>,
124
+ <>
125
+ <strong style={{ color: "var(--text-primary)" }}>
126
+ OPENCLAW_GATEWAY_TOKEN
127
+ </strong>
128
+ : Run <InlineCode>openclaw gateway status</InlineCode> to view your
129
+ gateway configuration including the token.
130
+ </>,
131
+ ]}
132
+ />
133
+
134
+ <SubHeading>Start the Gateway</SubHeading>
135
+ <Paragraph>
136
+ ClawPort expects the OpenClaw gateway running at{" "}
137
+ <InlineCode>localhost:18789</InlineCode>. Start it in a separate terminal:
138
+ </Paragraph>
139
+ <CodeBlock>{`openclaw gateway run`}</CodeBlock>
140
+
141
+ <SubHeading>First-Run Onboarding</SubHeading>
142
+ <Paragraph>
143
+ On your first visit, ClawPort launches the onboarding wizard (5 steps):
144
+ </Paragraph>
145
+ <NumberedList
146
+ items={[
147
+ "Naming your portal -- give your command centre a custom name and subtitle",
148
+ "Choosing a theme -- pick from Dark, Glass, Color, Light, or System",
149
+ "Setting an accent color -- personalize the UI highlight color",
150
+ "Voice chat -- optional microphone permission test",
151
+ "Overview -- feature summary of all pages",
152
+ ]}
153
+ />
154
+ <Paragraph>
155
+ All of these can be changed later in the Settings page.
156
+ </Paragraph>
157
+
158
+ <SubHeading>Production Build</SubHeading>
159
+ <CodeBlock title="terminal">
160
+ {`npx next build
161
+ npm start`}
162
+ </CodeBlock>
163
+ <Paragraph>
164
+ The production server runs on port 3000 by default. The gateway still
165
+ needs to be running.
166
+ </Paragraph>
167
+ </>
168
+ );
169
+ }