@polderlabs/bizar 2.4.0 → 2.6.1

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 (43) hide show
  1. package/cli/bin.mjs +73 -0
  2. package/cli/copy.mjs +39 -34
  3. package/cli/dashboard/api.mjs +473 -0
  4. package/cli/dashboard/browser.mjs +40 -0
  5. package/cli/dashboard/server.mjs +366 -0
  6. package/cli/dashboard/state.mjs +438 -0
  7. package/cli/dashboard/tasks-store.mjs +203 -0
  8. package/cli/dashboard/watcher.mjs +81 -0
  9. package/cli/dashboard.mjs +97 -0
  10. package/config/commands/bizar.md +13 -39
  11. package/dist/assets/index-BVvY22Gt.css +1 -0
  12. package/dist/assets/index-CO3c8O32.js +285 -0
  13. package/dist/assets/index-CO3c8O32.js.map +1 -0
  14. package/dist/index.html +18 -0
  15. package/package.json +26 -2
  16. package/src/App.tsx +233 -0
  17. package/src/components/Button.tsx +55 -0
  18. package/src/components/Card.tsx +40 -0
  19. package/src/components/EmptyState.tsx +30 -0
  20. package/src/components/Modal.tsx +137 -0
  21. package/src/components/Spinner.tsx +19 -0
  22. package/src/components/StatusBadge.tsx +25 -0
  23. package/src/components/Tag.tsx +28 -0
  24. package/src/components/Toast.tsx +142 -0
  25. package/src/components/Topbar.tsx +88 -0
  26. package/src/index.html +17 -0
  27. package/src/lib/api.ts +71 -0
  28. package/src/lib/markdown.tsx +59 -0
  29. package/src/lib/types.ts +200 -0
  30. package/src/lib/utils.ts +79 -0
  31. package/src/lib/ws.ts +132 -0
  32. package/src/main.tsx +12 -0
  33. package/src/styles/main.css +2324 -0
  34. package/src/views/Agents.tsx +199 -0
  35. package/src/views/Chat.tsx +255 -0
  36. package/src/views/Config.tsx +250 -0
  37. package/src/views/Overview.tsx +267 -0
  38. package/src/views/Plans.tsx +667 -0
  39. package/src/views/Projects.tsx +155 -0
  40. package/src/views/Settings.tsx +253 -0
  41. package/src/views/Tasks.tsx +567 -0
  42. package/tsconfig.json +23 -0
  43. package/vite.config.ts +24 -0
@@ -0,0 +1,267 @@
1
+ // src/views/Overview.tsx — system overview: counts, activity, quick actions.
2
+ import { useEffect, useState } from 'react';
3
+ import {
4
+ Bot,
5
+ CheckSquare,
6
+ Folder,
7
+ LayoutDashboard,
8
+ Map,
9
+ MessageSquare,
10
+ RefreshCw,
11
+ PlayCircle,
12
+ ShieldCheck,
13
+ FileText,
14
+ Zap,
15
+ } from 'lucide-react';
16
+ import { Card, CardTitle, CardMeta } from '../components/Card';
17
+ import { Button } from '../components/Button';
18
+ import { EmptyState } from '../components/EmptyState';
19
+ import { Spinner } from '../components/Spinner';
20
+ import { useToast } from '../components/Toast';
21
+ import { useModal } from '../components/Modal';
22
+ import { api } from '../lib/api';
23
+ import { formatRelative, formatTime } from '../lib/utils';
24
+ import type { Overview, Settings, Snapshot, ActivityItem } from '../lib/types';
25
+
26
+ type Props = {
27
+ snapshot: Snapshot;
28
+ settings: Settings;
29
+ activeTab: string;
30
+ setActiveTab: (id: string) => void;
31
+ refreshSnapshot: () => Promise<void>;
32
+ };
33
+
34
+ type StatCard = {
35
+ key: keyof Overview['counts'];
36
+ label: string;
37
+ Icon: typeof Bot;
38
+ tab: string;
39
+ };
40
+
41
+ const STATS: StatCard[] = [
42
+ { key: 'agents', label: 'Agents', Icon: Bot, tab: 'agents' },
43
+ { key: 'plans', label: 'Plans', Icon: Map, tab: 'plans' },
44
+ { key: 'projects', label: 'Projects', Icon: Folder, tab: 'projects' },
45
+ { key: 'sessions', label: 'Sessions', Icon: MessageSquare, tab: 'chat' },
46
+ ];
47
+
48
+ export function Overview({
49
+ snapshot,
50
+ setActiveTab,
51
+ refreshSnapshot,
52
+ }: Props) {
53
+ const toast = useToast();
54
+ const modal = useModal();
55
+ const [overview, setOverview] = useState<Overview | null>(
56
+ snapshot.overview ?? null,
57
+ );
58
+ const [loading, setLoading] = useState(!snapshot.overview);
59
+
60
+ useEffect(() => {
61
+ if (snapshot.overview) {
62
+ setOverview(snapshot.overview);
63
+ setLoading(false);
64
+ return;
65
+ }
66
+ let cancelled = false;
67
+ api
68
+ .get<Overview>('/overview')
69
+ .then((o) => {
70
+ if (!cancelled) {
71
+ setOverview(o);
72
+ setLoading(false);
73
+ }
74
+ })
75
+ .catch((err) => {
76
+ if (!cancelled) {
77
+ setLoading(false);
78
+ toast.error(`Could not load overview: ${err.message}`);
79
+ }
80
+ });
81
+ return () => {
82
+ cancelled = true;
83
+ };
84
+ }, [snapshot.overview, toast]);
85
+
86
+ const onRefresh = async () => {
87
+ toast.info('Refreshing…', 1500);
88
+ await refreshSnapshot();
89
+ };
90
+
91
+ const onAudit = () => {
92
+ modal.open({
93
+ title: 'Run audit',
94
+ children: (
95
+ <div>
96
+ <p>
97
+ The full security audit runs in the opencode TUI for proper
98
+ reporting and exit codes.
99
+ </p>
100
+ <p>Run this in your terminal:</p>
101
+ <pre className="code-block">
102
+ <code>bizar audit</code>
103
+ </pre>
104
+ </div>
105
+ ),
106
+ footer: (
107
+ <Button variant="primary" onClick={() => modal.close()}>
108
+ Got it
109
+ </Button>
110
+ ),
111
+ });
112
+ };
113
+
114
+ if (loading || !overview) {
115
+ return (
116
+ <div className="view-loading">
117
+ <Spinner size="lg" />
118
+ <p>Loading overview…</p>
119
+ </div>
120
+ );
121
+ }
122
+
123
+ return (
124
+ <div className="view view-overview">
125
+ <header className="view-header">
126
+ <div className="view-header-text">
127
+ <h2 className="view-title">
128
+ <LayoutDashboard size={18} />
129
+ System Overview
130
+ </h2>
131
+ <p className="view-subtitle">
132
+ Live snapshot of agents, plans, projects, and recent activity.
133
+ </p>
134
+ </div>
135
+ </header>
136
+
137
+ <section className="stat-grid">
138
+ {STATS.map((s) => {
139
+ const Icon = s.Icon;
140
+ return (
141
+ <Card
142
+ key={s.key}
143
+ variant="elevated"
144
+ interactive
145
+ className="stat-card"
146
+ onClick={() => setActiveTab(s.tab)}
147
+ role="button"
148
+ tabIndex={0}
149
+ onKeyDown={(e) => {
150
+ if (e.key === 'Enter' || e.key === ' ') {
151
+ e.preventDefault();
152
+ setActiveTab(s.tab);
153
+ }
154
+ }}
155
+ >
156
+ <div className="stat-card-icon">
157
+ <Icon size={20} />
158
+ </div>
159
+ <div className="stat-card-body">
160
+ <div className="stat-card-value tabular-nums">
161
+ {overview.counts[s.key]}
162
+ </div>
163
+ <div className="stat-card-label">{s.label}</div>
164
+ </div>
165
+ <div className="stat-card-arrow">→</div>
166
+ </Card>
167
+ );
168
+ })}
169
+ </section>
170
+
171
+ <Card className="quick-actions">
172
+ <CardTitle>Quick actions</CardTitle>
173
+ <CardMeta>Jump straight to common workflows.</CardMeta>
174
+ <div className="quick-actions-row">
175
+ <Button
176
+ variant="secondary"
177
+ onClick={() => setActiveTab('chat')}
178
+ iconOnly={false}
179
+ >
180
+ <MessageSquare size={14} /> New chat
181
+ </Button>
182
+ <Button variant="secondary" onClick={() => setActiveTab('plans')}>
183
+ <Map size={14} /> New plan
184
+ </Button>
185
+ <Button variant="secondary" onClick={() => setActiveTab('tasks')}>
186
+ <CheckSquare size={14} /> Add task
187
+ </Button>
188
+ <Button variant="secondary" onClick={() => setActiveTab('projects')}>
189
+ <Folder size={14} /> Switch project
190
+ </Button>
191
+ <Button variant="secondary" onClick={onAudit}>
192
+ <ShieldCheck size={14} /> Run audit
193
+ </Button>
194
+ <Button variant="primary" onClick={onRefresh}>
195
+ <RefreshCw size={14} /> Refresh
196
+ </Button>
197
+ </div>
198
+ </Card>
199
+
200
+ <div className="overview-cols">
201
+ <Card>
202
+ <CardTitle>
203
+ <Zap size={14} /> Recent activity
204
+ </CardTitle>
205
+ <CardMeta>Last 30 events from .bizar/activity.log</CardMeta>
206
+ {overview.recentActivity.length === 0 ? (
207
+ <EmptyState
208
+ icon={<FileText size={28} />}
209
+ title="No activity yet"
210
+ message="Use the chat or invoke a Bizar command to start a feed."
211
+ />
212
+ ) : (
213
+ <ul className="activity-list">
214
+ {overview.recentActivity.slice(0, 30).map((it, idx) => (
215
+ <li key={idx} className="activity-item">
216
+ <span className="activity-ts tabular-nums">
217
+ {formatRelative(it.ts)}
218
+ </span>
219
+ <span className="activity-kind">{it.kind}</span>
220
+ <span className="activity-msg">{formatActivity(it)}</span>
221
+ </li>
222
+ ))}
223
+ </ul>
224
+ )}
225
+ </Card>
226
+
227
+ <Card>
228
+ <CardTitle>Environment</CardTitle>
229
+ <CardMeta>Runtime + paths</CardMeta>
230
+ <dl className="env-table">
231
+ <dt>Node</dt>
232
+ <dd className="mono">{overview.versions.node}</dd>
233
+ <dt>Platform</dt>
234
+ <dd className="mono">{overview.versions.platform}</dd>
235
+ <dt>Project root</dt>
236
+ <dd className="mono ellipsis" title={overview.versions.projectRoot}>
237
+ {overview.versions.projectRoot}
238
+ </dd>
239
+ <dt>Bizar root</dt>
240
+ <dd className="mono ellipsis" title={overview.versions.bizarRoot}>
241
+ {overview.versions.bizarRoot}
242
+ </dd>
243
+ <dt>Generated</dt>
244
+ <dd className="mono tabular-nums">
245
+ {formatTime(overview.generatedAt)}
246
+ </dd>
247
+ </dl>
248
+ </Card>
249
+ </div>
250
+
251
+ <div className="overview-footer">
252
+ <PlayCircle size={14} /> Dashboard built {formatTime(overview.generatedAt)}
253
+ </div>
254
+ </div>
255
+ );
256
+ }
257
+
258
+ function formatActivity(it: ActivityItem): string {
259
+ if (typeof it.message === 'string') return it.message;
260
+ if (typeof it.prompt === 'string') return it.prompt;
261
+ if (typeof it.slug === 'string') {
262
+ const title = typeof it.title === 'string' ? ` title=${it.title}` : '';
263
+ return `slug=${it.slug}${title}`;
264
+ }
265
+ if (typeof it.name === 'string') return `name=${it.name}`;
266
+ return JSON.stringify(it);
267
+ }