@polderlabs/bizar 2.3.0 → 2.6.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 (48) hide show
  1. package/cli/bin.mjs +73 -0
  2. package/cli/copy.mjs +42 -2
  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/cli/install.mjs +17 -4
  11. package/config/commands/bizar.md +18 -0
  12. package/config/commands/plan.md +26 -0
  13. package/config/commands/visual-plan.md +15 -0
  14. package/config/opencode.json +259 -1
  15. package/dist/assets/index-BVvY22Gt.css +1 -0
  16. package/dist/assets/index-CO3c8O32.js +285 -0
  17. package/dist/assets/index-CO3c8O32.js.map +1 -0
  18. package/dist/index.html +18 -0
  19. package/package.json +26 -2
  20. package/src/App.tsx +233 -0
  21. package/src/components/Button.tsx +55 -0
  22. package/src/components/Card.tsx +40 -0
  23. package/src/components/EmptyState.tsx +30 -0
  24. package/src/components/Modal.tsx +137 -0
  25. package/src/components/Spinner.tsx +19 -0
  26. package/src/components/StatusBadge.tsx +25 -0
  27. package/src/components/Tag.tsx +28 -0
  28. package/src/components/Toast.tsx +142 -0
  29. package/src/components/Topbar.tsx +88 -0
  30. package/src/index.html +17 -0
  31. package/src/lib/api.ts +71 -0
  32. package/src/lib/markdown.tsx +59 -0
  33. package/src/lib/types.ts +200 -0
  34. package/src/lib/utils.ts +79 -0
  35. package/src/lib/ws.ts +132 -0
  36. package/src/main.tsx +12 -0
  37. package/src/styles/main.css +2324 -0
  38. package/src/views/Agents.tsx +199 -0
  39. package/src/views/Chat.tsx +255 -0
  40. package/src/views/Config.tsx +250 -0
  41. package/src/views/Overview.tsx +267 -0
  42. package/src/views/Plans.tsx +667 -0
  43. package/src/views/Projects.tsx +155 -0
  44. package/src/views/Settings.tsx +253 -0
  45. package/src/views/Tasks.tsx +567 -0
  46. package/tsconfig.json +23 -0
  47. package/vite.config.ts +24 -0
  48. package/config/opencode.json.template +0 -52
package/src/lib/ws.ts ADDED
@@ -0,0 +1,132 @@
1
+ // src/lib/ws.ts — WebSocket with auto-reconnect + status + handlers.
2
+ import type { WsMessage, WsStatus } from './types';
3
+
4
+ type Handler = (msg: WsMessage) => void;
5
+ type StatusHandler = (status: WsStatus) => void;
6
+
7
+ export class Ws {
8
+ private url: string;
9
+ private handlers = new Set<Handler>();
10
+ private statusHandlers = new Set<StatusHandler>();
11
+ private ws: WebSocket | null = null;
12
+ private reconnectDelay = 1000;
13
+ private readonly maxReconnectDelay = 15000;
14
+ private _status: WsStatus = 'connecting';
15
+ private pingTimer: ReturnType<typeof setInterval> | null = null;
16
+ private closed = false;
17
+
18
+ constructor(url?: string) {
19
+ this.url =
20
+ url ||
21
+ (typeof location !== 'undefined'
22
+ ? `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`
23
+ : 'ws://localhost/ws');
24
+ this.connect();
25
+ }
26
+
27
+ on(handler: Handler): () => void {
28
+ this.handlers.add(handler);
29
+ return () => {
30
+ this.handlers.delete(handler);
31
+ };
32
+ }
33
+
34
+ onStatus(handler: StatusHandler): () => void {
35
+ this.statusHandlers.add(handler);
36
+ // Fire current status immediately
37
+ handler(this._status);
38
+ return () => {
39
+ this.statusHandlers.delete(handler);
40
+ };
41
+ }
42
+
43
+ get status(): WsStatus {
44
+ return this._status;
45
+ }
46
+
47
+ send(msg: unknown): boolean {
48
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
49
+ this.ws.send(JSON.stringify(msg));
50
+ return true;
51
+ }
52
+ return false;
53
+ }
54
+
55
+ close(): void {
56
+ this.closed = true;
57
+ if (this.pingTimer) clearInterval(this.pingTimer);
58
+ this.pingTimer = null;
59
+ try {
60
+ this.ws?.close();
61
+ } catch {
62
+ /* ignore */
63
+ }
64
+ }
65
+
66
+ private connect(): void {
67
+ this.setStatus('connecting');
68
+ try {
69
+ this.ws = new WebSocket(this.url);
70
+ } catch (err) {
71
+ console.warn('[ws] construct failed:', err);
72
+ this.scheduleReconnect();
73
+ return;
74
+ }
75
+ this.ws.addEventListener('open', () => {
76
+ this.reconnectDelay = 1000;
77
+ this.setStatus('connected');
78
+ // Keep-alive ping every 30s
79
+ if (this.pingTimer) clearInterval(this.pingTimer);
80
+ this.pingTimer = setInterval(() => {
81
+ if (this._status === 'connected') this.send({ type: 'ping' });
82
+ }, 30_000);
83
+ });
84
+ this.ws.addEventListener('close', () => {
85
+ this.setStatus('disconnected');
86
+ if (this.pingTimer) {
87
+ clearInterval(this.pingTimer);
88
+ this.pingTimer = null;
89
+ }
90
+ if (!this.closed) this.scheduleReconnect();
91
+ });
92
+ this.ws.addEventListener('error', () => {
93
+ // 'close' will follow — keep this for logs
94
+ console.warn('[ws] error');
95
+ });
96
+ this.ws.addEventListener('message', (e) => {
97
+ let msg: WsMessage;
98
+ try {
99
+ msg = JSON.parse(e.data);
100
+ } catch {
101
+ console.warn('[ws] bad message');
102
+ return;
103
+ }
104
+ for (const h of this.handlers) {
105
+ try {
106
+ h(msg);
107
+ } catch (err) {
108
+ console.error('[ws] handler error:', err);
109
+ }
110
+ }
111
+ });
112
+ }
113
+
114
+ private scheduleReconnect(): void {
115
+ setTimeout(() => this.connect(), this.reconnectDelay);
116
+ this.reconnectDelay = Math.min(
117
+ this.reconnectDelay * 1.6,
118
+ this.maxReconnectDelay,
119
+ );
120
+ }
121
+
122
+ private setStatus(s: WsStatus): void {
123
+ this._status = s;
124
+ for (const h of this.statusHandlers) {
125
+ try {
126
+ h(s);
127
+ } catch (err) {
128
+ console.error('[ws] status handler error:', err);
129
+ }
130
+ }
131
+ }
132
+ }
package/src/main.tsx ADDED
@@ -0,0 +1,12 @@
1
+ // src/main.tsx — entry point.
2
+ import { StrictMode } from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
+ import { App } from './App';
5
+
6
+ const root = document.getElementById('root');
7
+ if (!root) throw new Error('Root element #root not found');
8
+ createRoot(root).render(
9
+ <StrictMode>
10
+ <App />
11
+ </StrictMode>,
12
+ );