@myrialabs/clopen 0.1.1 → 0.1.3

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 (51) hide show
  1. package/backend/index.ts +4 -1
  2. package/backend/lib/engine/adapters/opencode/message-converter.ts +53 -2
  3. package/backend/lib/engine/adapters/opencode/stream.ts +89 -5
  4. package/backend/lib/project/status-manager.ts +221 -181
  5. package/frontend/lib/components/chat/message/ChatMessages.svelte +16 -4
  6. package/frontend/lib/components/chat/tools/AgentTool.svelte +12 -11
  7. package/frontend/lib/components/chat/tools/BashOutputTool.svelte +3 -3
  8. package/frontend/lib/components/chat/tools/BashTool.svelte +4 -4
  9. package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +3 -1
  10. package/frontend/lib/components/chat/tools/EditTool.svelte +6 -6
  11. package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +1 -1
  12. package/frontend/lib/components/chat/tools/GlobTool.svelte +12 -12
  13. package/frontend/lib/components/chat/tools/GrepTool.svelte +5 -5
  14. package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +1 -1
  15. package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +6 -6
  16. package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +2 -2
  17. package/frontend/lib/components/chat/tools/ReadTool.svelte +4 -4
  18. package/frontend/lib/components/chat/tools/TaskStopTool.svelte +1 -1
  19. package/frontend/lib/components/chat/tools/TaskTool.svelte +1 -1
  20. package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +4 -4
  21. package/frontend/lib/components/chat/tools/WebSearchTool.svelte +1 -1
  22. package/frontend/lib/components/chat/tools/WriteTool.svelte +3 -3
  23. package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +3 -3
  24. package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +2 -2
  25. package/frontend/lib/components/chat/tools/components/FileHeader.svelte +1 -1
  26. package/frontend/lib/components/chat/tools/components/InfoLine.svelte +2 -2
  27. package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +1 -1
  28. package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +5 -5
  29. package/frontend/lib/components/common/Button.svelte +1 -1
  30. package/frontend/lib/components/common/Card.svelte +3 -3
  31. package/frontend/lib/components/common/Input.svelte +3 -3
  32. package/frontend/lib/components/common/LoadingSpinner.svelte +1 -1
  33. package/frontend/lib/components/common/Select.svelte +6 -6
  34. package/frontend/lib/components/common/Textarea.svelte +3 -3
  35. package/frontend/lib/components/files/FileViewer.svelte +1 -1
  36. package/frontend/lib/components/git/ChangesSection.svelte +2 -4
  37. package/frontend/lib/components/preview/browser/BrowserPreview.svelte +9 -29
  38. package/frontend/lib/components/preview/browser/components/Container.svelte +17 -0
  39. package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +2 -2
  40. package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +0 -6
  41. package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +15 -15
  42. package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +2 -2
  43. package/frontend/lib/components/workspace/DesktopNavigator.svelte +380 -383
  44. package/frontend/lib/components/workspace/MobileNavigator.svelte +391 -395
  45. package/frontend/lib/components/workspace/PanelHeader.svelte +115 -4
  46. package/frontend/lib/components/workspace/ViewMenu.svelte +9 -25
  47. package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +29 -4
  48. package/frontend/lib/services/notification/global-stream-monitor.ts +77 -86
  49. package/frontend/lib/services/project/status.service.ts +160 -159
  50. package/frontend/lib/stores/ui/workspace.svelte.ts +326 -283
  51. package/package.json +1 -1
@@ -1,159 +1,160 @@
1
- /**
2
- * Project Status Service
3
- * Tracks active streams and user presence for projects via WebSocket
4
- * Fully realtime - no cache, no polling
5
- *
6
- * Single event: `projects:presence-updated` contains full state
7
- */
8
-
9
- import { getOrCreateAnonymousUser, type AnonymousUser } from '$shared/utils/anonymous-user';
10
- import ws from '$frontend/lib/utils/ws';
11
- import { debug } from '$shared/utils/logger';
12
-
13
- export interface ProjectStatus {
14
- projectId: string;
15
- hasActiveStreams: boolean;
16
- activeStreamCount: number;
17
- activeUsers: { userId: string; userName: string }[];
18
- streams: {
19
- streamId: string;
20
- chatSessionId: string;
21
- status: string;
22
- startedAt: string;
23
- messagesCount: number;
24
- }[];
25
- /** Per-chat-session user presence: { chatSessionId → users[] } */
26
- chatSessionUsers?: Record<string, { userId: string; userName: string }[]>;
27
- }
28
-
29
- class ProjectStatusService {
30
- private currentUser: AnonymousUser | null = null;
31
- private currentProjectId: string | null = null;
32
- private initialized = false;
33
- private statusUpdateCallbacks: Set<(statuses: ProjectStatus[]) => void> = new Set();
34
- private unsubscribe: (() => void) | null = null;
35
-
36
- /**
37
- * Initialize the service - sets up WS listener
38
- * Called automatically on first use
39
- */
40
- async initialize(): Promise<void> {
41
- if (typeof window === 'undefined' || this.initialized) return;
42
-
43
- this.currentUser = await getOrCreateAnonymousUser();
44
- debug.log('project', 'Initialized with user:', this.currentUser?.name);
45
-
46
- this.unsubscribe = ws.on('projects:presence-updated', (data) => {
47
- try {
48
- if (data.type === 'presence-updated' && data.data) {
49
- const statuses = Array.isArray(data.data) ? data.data : [data.data];
50
- this.statusUpdateCallbacks.forEach(callback => {
51
- try {
52
- callback(statuses);
53
- } catch (error) {
54
- debug.error('project', 'Error in status callback:', error);
55
- }
56
- });
57
- }
58
- } catch (error) {
59
- debug.error('project', 'Error handling presence update:', error);
60
- }
61
- });
62
-
63
- this.initialized = true;
64
- }
65
-
66
- private async ensureInitialized(): Promise<void> {
67
- if (!this.initialized) {
68
- await this.initialize();
69
- }
70
- }
71
-
72
- /**
73
- * Subscribe to real-time presence updates
74
- * Auto-initializes WS listener if not done yet
75
- */
76
- onStatusUpdate(callback: (statuses: ProjectStatus[]) => void): () => void {
77
- this.statusUpdateCallbacks.add(callback);
78
- this.ensureInitialized();
79
-
80
- return () => {
81
- this.statusUpdateCallbacks.delete(callback);
82
- };
83
- }
84
-
85
- /**
86
- * Join a project - start presence tracking
87
- */
88
- async startTracking(projectId: string): Promise<void> {
89
- if (typeof window === 'undefined') return;
90
-
91
- await this.ensureInitialized();
92
-
93
- if (this.currentProjectId && this.currentProjectId !== projectId) {
94
- await this.stopTracking();
95
- }
96
-
97
- this.currentProjectId = projectId;
98
-
99
- if (this.currentUser) {
100
- ws.emit('projects:join', {
101
- userName: this.currentUser.name
102
- });
103
- }
104
-
105
- document.addEventListener('visibilitychange', this.handleVisibilityChange);
106
- window.addEventListener('beforeunload', this.handleBeforeUnload);
107
- }
108
-
109
- /**
110
- * Leave a project - stop presence tracking
111
- */
112
- async stopTracking(): Promise<void> {
113
- if (this.currentProjectId && this.currentUser) {
114
- // Send projectId explicitly because ws.setProject() may have already changed context
115
- ws.emit('projects:leave', {
116
- projectId: this.currentProjectId
117
- });
118
- }
119
-
120
- if (typeof window !== 'undefined') {
121
- document.removeEventListener('visibilitychange', this.handleVisibilityChange);
122
- window.removeEventListener('beforeunload', this.handleBeforeUnload);
123
- }
124
-
125
- this.currentProjectId = null;
126
- }
127
-
128
- cleanup(): void {
129
- if (this.unsubscribe) {
130
- this.unsubscribe();
131
- this.unsubscribe = null;
132
- }
133
- this.statusUpdateCallbacks.clear();
134
- this.initialized = false;
135
- }
136
-
137
- private handleVisibilityChange = () => {
138
- if (!document.hidden && this.currentProjectId && this.currentUser) {
139
- ws.http('projects:update-presence', {
140
- userName: this.currentUser.name,
141
- action: 'update'
142
- }).catch(() => {});
143
- }
144
- };
145
-
146
- private handleBeforeUnload = () => {
147
- if (this.currentProjectId && this.currentUser) {
148
- ws.emit('projects:leave', {
149
- projectId: this.currentProjectId
150
- });
151
- }
152
- };
153
-
154
- getCurrentUser(): AnonymousUser | null {
155
- return this.currentUser;
156
- }
157
- }
158
-
159
- export const projectStatusService = new ProjectStatusService();
1
+ /**
2
+ * Project Status Service
3
+ * Tracks active streams and user presence for projects via WebSocket
4
+ * Fully realtime - no cache, no polling
5
+ *
6
+ * Single event: `projects:presence-updated` contains full state
7
+ */
8
+
9
+ import { getOrCreateAnonymousUser, type AnonymousUser } from '$shared/utils/anonymous-user';
10
+ import ws from '$frontend/lib/utils/ws';
11
+ import { debug } from '$shared/utils/logger';
12
+
13
+ export interface ProjectStatus {
14
+ projectId: string;
15
+ hasActiveStreams: boolean;
16
+ activeStreamCount: number;
17
+ activeUsers: { userId: string; userName: string }[];
18
+ streams: {
19
+ streamId: string;
20
+ chatSessionId: string;
21
+ status: string;
22
+ startedAt: string;
23
+ messagesCount: number;
24
+ isWaitingInput?: boolean;
25
+ }[];
26
+ /** Per-chat-session user presence: { chatSessionId users[] } */
27
+ chatSessionUsers?: Record<string, { userId: string; userName: string }[]>;
28
+ }
29
+
30
+ class ProjectStatusService {
31
+ private currentUser: AnonymousUser | null = null;
32
+ private currentProjectId: string | null = null;
33
+ private initialized = false;
34
+ private statusUpdateCallbacks: Set<(statuses: ProjectStatus[]) => void> = new Set();
35
+ private unsubscribe: (() => void) | null = null;
36
+
37
+ /**
38
+ * Initialize the service - sets up WS listener
39
+ * Called automatically on first use
40
+ */
41
+ async initialize(): Promise<void> {
42
+ if (typeof window === 'undefined' || this.initialized) return;
43
+
44
+ this.currentUser = await getOrCreateAnonymousUser();
45
+ debug.log('project', 'Initialized with user:', this.currentUser?.name);
46
+
47
+ this.unsubscribe = ws.on('projects:presence-updated', (data) => {
48
+ try {
49
+ if (data.type === 'presence-updated' && data.data) {
50
+ const statuses = Array.isArray(data.data) ? data.data : [data.data];
51
+ this.statusUpdateCallbacks.forEach(callback => {
52
+ try {
53
+ callback(statuses);
54
+ } catch (error) {
55
+ debug.error('project', 'Error in status callback:', error);
56
+ }
57
+ });
58
+ }
59
+ } catch (error) {
60
+ debug.error('project', 'Error handling presence update:', error);
61
+ }
62
+ });
63
+
64
+ this.initialized = true;
65
+ }
66
+
67
+ private async ensureInitialized(): Promise<void> {
68
+ if (!this.initialized) {
69
+ await this.initialize();
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Subscribe to real-time presence updates
75
+ * Auto-initializes WS listener if not done yet
76
+ */
77
+ onStatusUpdate(callback: (statuses: ProjectStatus[]) => void): () => void {
78
+ this.statusUpdateCallbacks.add(callback);
79
+ this.ensureInitialized();
80
+
81
+ return () => {
82
+ this.statusUpdateCallbacks.delete(callback);
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Join a project - start presence tracking
88
+ */
89
+ async startTracking(projectId: string): Promise<void> {
90
+ if (typeof window === 'undefined') return;
91
+
92
+ await this.ensureInitialized();
93
+
94
+ if (this.currentProjectId && this.currentProjectId !== projectId) {
95
+ await this.stopTracking();
96
+ }
97
+
98
+ this.currentProjectId = projectId;
99
+
100
+ if (this.currentUser) {
101
+ ws.emit('projects:join', {
102
+ userName: this.currentUser.name
103
+ });
104
+ }
105
+
106
+ document.addEventListener('visibilitychange', this.handleVisibilityChange);
107
+ window.addEventListener('beforeunload', this.handleBeforeUnload);
108
+ }
109
+
110
+ /**
111
+ * Leave a project - stop presence tracking
112
+ */
113
+ async stopTracking(): Promise<void> {
114
+ if (this.currentProjectId && this.currentUser) {
115
+ // Send projectId explicitly because ws.setProject() may have already changed context
116
+ ws.emit('projects:leave', {
117
+ projectId: this.currentProjectId
118
+ });
119
+ }
120
+
121
+ if (typeof window !== 'undefined') {
122
+ document.removeEventListener('visibilitychange', this.handleVisibilityChange);
123
+ window.removeEventListener('beforeunload', this.handleBeforeUnload);
124
+ }
125
+
126
+ this.currentProjectId = null;
127
+ }
128
+
129
+ cleanup(): void {
130
+ if (this.unsubscribe) {
131
+ this.unsubscribe();
132
+ this.unsubscribe = null;
133
+ }
134
+ this.statusUpdateCallbacks.clear();
135
+ this.initialized = false;
136
+ }
137
+
138
+ private handleVisibilityChange = () => {
139
+ if (!document.hidden && this.currentProjectId && this.currentUser) {
140
+ ws.http('projects:update-presence', {
141
+ userName: this.currentUser.name,
142
+ action: 'update'
143
+ }).catch(() => {});
144
+ }
145
+ };
146
+
147
+ private handleBeforeUnload = () => {
148
+ if (this.currentProjectId && this.currentUser) {
149
+ ws.emit('projects:leave', {
150
+ projectId: this.currentProjectId
151
+ });
152
+ }
153
+ };
154
+
155
+ getCurrentUser(): AnonymousUser | null {
156
+ return this.currentUser;
157
+ }
158
+ }
159
+
160
+ export const projectStatusService = new ProjectStatusService();