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,115 @@
1
+ 'use client';
2
+
3
+ import { useCallback } from 'react';
4
+ import { NavLinks } from '@/components/NavLinks';
5
+ import { ThemeToggle } from '@/components/ThemeToggle';
6
+ import { MobileSidebar } from '@/components/MobileSidebar';
7
+ import { GlobalSearch, SearchTrigger } from '@/components/GlobalSearch';
8
+ import { useSettings } from '@/app/settings-provider';
9
+
10
+ /**
11
+ * Sidebar -- client wrapper that coordinates desktop sidebar, mobile sidebar,
12
+ * and the Cmd+K search palette. Rendered inside layout.tsx.
13
+ */
14
+ export function Sidebar() {
15
+ const { settings } = useSettings();
16
+ const openSearch = useCallback(() => {
17
+ // We trigger the search modal by simulating Cmd+K.
18
+ // Instead, we expose a controlled open state via a custom event.
19
+ // The GlobalSearch component listens for this.
20
+ window.dispatchEvent(new CustomEvent('clawport:open-search'));
21
+ }, []);
22
+
23
+ return (
24
+ <>
25
+ {/* Desktop sidebar — hidden on mobile */}
26
+ <aside
27
+ className="hidden md:flex md:flex-col"
28
+ style={{
29
+ width: '220px',
30
+ flexShrink: 0,
31
+ background: 'var(--sidebar-bg)',
32
+ backdropFilter: 'blur(40px) saturate(180%)',
33
+ WebkitBackdropFilter: 'blur(40px) saturate(180%)',
34
+ borderRight: '1px solid var(--separator)',
35
+ }}
36
+ >
37
+ {/* App icon + title */}
38
+ <div className="px-4 pt-5 pb-3">
39
+ <div className="flex items-center gap-3">
40
+ {settings.portalIcon ? (
41
+ <img
42
+ src={settings.portalIcon}
43
+ alt=""
44
+ style={{
45
+ width: '36px',
46
+ height: '36px',
47
+ borderRadius: '10px',
48
+ objectFit: 'cover',
49
+ boxShadow: 'var(--shadow-card)',
50
+ flexShrink: 0,
51
+ }}
52
+ />
53
+ ) : (
54
+ <div
55
+ style={{
56
+ width: '36px',
57
+ height: '36px',
58
+ borderRadius: '10px',
59
+ background: settings.iconBgHidden
60
+ ? 'transparent'
61
+ : settings.accentColor
62
+ ? `linear-gradient(135deg, ${settings.accentColor}, ${settings.accentColor}dd)`
63
+ : 'linear-gradient(135deg, #f5c518, #e8b800)',
64
+ boxShadow: settings.iconBgHidden ? 'none' : 'var(--shadow-card)',
65
+ display: 'flex',
66
+ alignItems: 'center',
67
+ justifyContent: 'center',
68
+ fontSize: settings.iconBgHidden ? '28px' : '18px',
69
+ flexShrink: 0,
70
+ }}
71
+ >
72
+ {settings.portalEmoji ?? '\ud83e\udd9e'}
73
+ </div>
74
+ )}
75
+ <div>
76
+ <div
77
+ style={{
78
+ fontSize: '17px',
79
+ fontWeight: 600,
80
+ letterSpacing: '-0.3px',
81
+ color: 'var(--text-primary)',
82
+ }}
83
+ >
84
+ {settings.portalName ?? 'ClawPort'}
85
+ </div>
86
+ <div
87
+ style={{
88
+ fontSize: '12px',
89
+ color: 'var(--text-secondary)',
90
+ letterSpacing: '0.01em',
91
+ }}
92
+ >
93
+ {settings.portalSubtitle ?? 'Command Centre'}
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ {/* Search trigger */}
100
+ <div className="px-3 pb-2">
101
+ <SearchTrigger onClick={openSearch} />
102
+ </div>
103
+
104
+ <NavLinks />
105
+ <ThemeToggle />
106
+ </aside>
107
+
108
+ {/* Mobile sidebar */}
109
+ <MobileSidebar onOpenSearch={openSearch} />
110
+
111
+ {/* Global search modal (Cmd+K) */}
112
+ <GlobalSearch />
113
+ </>
114
+ );
115
+ }
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+
3
+ import { useRef, useCallback } from 'react';
4
+ import { THEMES } from '@/lib/themes';
5
+ import { useTheme } from '@/app/providers';
6
+
7
+ export function ThemeToggle() {
8
+ const { theme, setTheme } = useTheme();
9
+ const containerRef = useRef<HTMLDivElement>(null);
10
+
11
+ const handleKeyDown = useCallback(
12
+ (e: React.KeyboardEvent) => {
13
+ const buttons =
14
+ containerRef.current?.querySelectorAll<HTMLButtonElement>('button');
15
+ if (!buttons || buttons.length === 0) return;
16
+
17
+ const currentIndex = THEMES.findIndex((t) => t.id === theme);
18
+
19
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
20
+ e.preventDefault();
21
+ const nextIndex = (currentIndex + 1) % THEMES.length;
22
+ setTheme(THEMES[nextIndex].id);
23
+ buttons[nextIndex].focus();
24
+ } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
25
+ e.preventDefault();
26
+ const prevIndex =
27
+ (currentIndex - 1 + THEMES.length) % THEMES.length;
28
+ setTheme(THEMES[prevIndex].id);
29
+ buttons[prevIndex].focus();
30
+ }
31
+ },
32
+ [theme, setTheme]
33
+ );
34
+
35
+ return (
36
+ <div style={{ padding: '8px 16px 12px' }}>
37
+ <div
38
+ style={{
39
+ fontSize: '11px',
40
+ fontWeight: 600,
41
+ letterSpacing: '0.06em',
42
+ color: 'var(--text-tertiary)',
43
+ textTransform: 'uppercase',
44
+ marginBottom: '6px',
45
+ paddingLeft: '4px',
46
+ }}
47
+ >
48
+ Theme
49
+ </div>
50
+ <div
51
+ ref={containerRef}
52
+ className="flex flex-wrap gap-1.5"
53
+ role="radiogroup"
54
+ aria-label="Theme selection"
55
+ onKeyDown={handleKeyDown}
56
+ >
57
+ {THEMES.map((t) => {
58
+ const isActive = theme === t.id;
59
+ return (
60
+ <button
61
+ key={t.id}
62
+ onClick={() => setTheme(t.id)}
63
+ title={t.label}
64
+ role="radio"
65
+ aria-checked={isActive}
66
+ aria-label={`${t.label} theme`}
67
+ tabIndex={isActive ? 0 : -1}
68
+ className="focus-ring"
69
+ style={{
70
+ display: 'flex',
71
+ alignItems: 'center',
72
+ gap: '4px',
73
+ height: '28px',
74
+ padding: isActive ? '0 10px' : '0 6px',
75
+ borderRadius: '14px',
76
+ fontSize: '12px',
77
+ fontWeight: isActive ? 600 : 500,
78
+ border: 'none',
79
+ cursor: 'pointer',
80
+ transition: 'all 150ms var(--ease-spring)',
81
+ background: isActive
82
+ ? 'var(--accent-fill)'
83
+ : 'var(--fill-quaternary)',
84
+ color: isActive ? 'var(--accent)' : 'var(--text-tertiary)',
85
+ outline: 'none',
86
+ }}
87
+ >
88
+ <span style={{ fontSize: '13px', lineHeight: 1 }}>
89
+ {t.emoji}
90
+ </span>
91
+ {isActive && (
92
+ <span
93
+ style={{
94
+ fontSize: '11px',
95
+ fontWeight: 600,
96
+ letterSpacing: '-0.01em',
97
+ }}
98
+ >
99
+ {t.label}
100
+ </span>
101
+ )}
102
+ </button>
103
+ );
104
+ })}
105
+ </div>
106
+ </div>
107
+ );
108
+ }