@myrialabs/clopen 0.2.6 → 0.2.8

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 (39) hide show
  1. package/backend/chat/stream-manager.ts +24 -13
  2. package/backend/engine/adapters/claude/stream.ts +10 -19
  3. package/backend/mcp/project-context.ts +20 -0
  4. package/backend/mcp/servers/browser-automation/actions.ts +0 -2
  5. package/backend/mcp/servers/browser-automation/browser.ts +86 -132
  6. package/backend/mcp/servers/browser-automation/inspection.ts +5 -11
  7. package/backend/preview/browser/browser-mcp-control.ts +175 -180
  8. package/backend/preview/browser/browser-pool.ts +3 -1
  9. package/backend/preview/browser/browser-preview-service.ts +3 -3
  10. package/backend/preview/browser/browser-tab-manager.ts +1 -1
  11. package/backend/preview/browser/browser-video-capture.ts +12 -14
  12. package/backend/preview/browser/scripts/audio-stream.ts +11 -0
  13. package/backend/preview/browser/scripts/video-stream.ts +14 -14
  14. package/backend/preview/browser/types.ts +7 -7
  15. package/backend/preview/index.ts +1 -1
  16. package/backend/ws/chat/stream.ts +1 -1
  17. package/backend/ws/preview/browser/tab-info.ts +5 -2
  18. package/backend/ws/preview/index.ts +3 -3
  19. package/frontend/components/chat/input/ChatInput.svelte +0 -3
  20. package/frontend/components/chat/input/composables/use-chat-actions.svelte.ts +6 -2
  21. package/frontend/components/chat/message/MessageBubble.svelte +2 -2
  22. package/frontend/components/history/HistoryModal.svelte +1 -1
  23. package/frontend/components/preview/browser/BrowserPreview.svelte +15 -1
  24. package/frontend/components/preview/browser/components/Canvas.svelte +323 -49
  25. package/frontend/components/preview/browser/components/Container.svelte +21 -0
  26. package/frontend/components/preview/browser/components/Toolbar.svelte +3 -3
  27. package/frontend/components/preview/browser/core/coordinator.svelte.ts +15 -0
  28. package/frontend/components/preview/browser/core/mcp-handlers.svelte.ts +78 -51
  29. package/frontend/components/preview/browser/core/tab-operations.svelte.ts +1 -0
  30. package/frontend/components/workspace/PanelHeader.svelte +15 -0
  31. package/frontend/components/workspace/panels/GitPanel.svelte +22 -13
  32. package/frontend/components/workspace/panels/PreviewPanel.svelte +2 -0
  33. package/frontend/services/chat/chat.service.ts +3 -7
  34. package/frontend/services/preview/browser/browser-webcodecs.service.ts +32 -135
  35. package/frontend/stores/core/app.svelte.ts +4 -3
  36. package/frontend/stores/core/presence.svelte.ts +3 -2
  37. package/frontend/stores/core/sessions.svelte.ts +2 -0
  38. package/frontend/stores/ui/notification.svelte.ts +4 -1
  39. package/package.json +1 -1
@@ -470,7 +470,7 @@ export const streamHandler = createRouter()
470
470
  return;
471
471
  }
472
472
 
473
- const cancelled = await streamManager.cancelStream(streamState.streamId);
473
+ await streamManager.cancelStream(streamState.streamId);
474
474
  // Always send cancelled to chat session room to clear UI
475
475
  ws.emit.chatSession(chatSessionId, 'chat:cancelled', {
476
476
  status: 'cancelled',
@@ -7,6 +7,7 @@
7
7
  import { t } from 'elysia';
8
8
  import { createRouter } from '$shared/utils/ws-server';
9
9
  import { browserPreviewServiceManager } from '../../../preview/index';
10
+ import { browserMcpControl } from '../../../preview/browser/browser-mcp-control';
10
11
  import { ws } from '$backend/utils/ws';
11
12
  import { debug } from '$shared/utils/logger';
12
13
 
@@ -62,7 +63,8 @@ export const tabInfoPreviewHandler = createRouter()
62
63
  isStreaming: t.Boolean(),
63
64
  deviceSize: t.String(),
64
65
  rotation: t.String(),
65
- isActive: t.Boolean()
66
+ isActive: t.Boolean(),
67
+ isMcpControlled: t.Boolean()
66
68
  })),
67
69
  activeTabId: t.Union([t.String(), t.Null()]),
68
70
  count: t.Number()
@@ -87,7 +89,8 @@ export const tabInfoPreviewHandler = createRouter()
87
89
  isStreaming: tab.isStreaming,
88
90
  deviceSize: tab.deviceSize,
89
91
  rotation: tab.rotation,
90
- isActive: tab.isActive
92
+ isActive: tab.isActive,
93
+ isMcpControlled: browserMcpControl.isTabControlled(tab.id, projectId)
91
94
  })),
92
95
  activeTabId: activeTab?.id || null,
93
96
  count: allTabsInfo.length
@@ -117,12 +117,12 @@ export const previewRouter = createRouter()
117
117
  }))
118
118
  // MCP control events
119
119
  .emit('preview:browser-mcp-control-start', t.Object({
120
- browserSessionId: t.String(),
121
- mcpSessionId: t.Optional(t.String()),
120
+ browserTabId: t.String(),
121
+ chatSessionId: t.Optional(t.String()),
122
122
  timestamp: t.Number()
123
123
  }))
124
124
  .emit('preview:browser-mcp-control-end', t.Object({
125
- browserSessionId: t.String(),
125
+ browserTabId: t.String(),
126
126
  timestamp: t.Number()
127
127
  }))
128
128
  .emit('preview:browser-mcp-cursor-position', t.Object({
@@ -66,9 +66,6 @@
66
66
 
67
67
  // Chat actions params
68
68
  const chatActionsParams = {
69
- get messageText() {
70
- return messageText;
71
- },
72
69
  get attachedFiles() {
73
70
  return fileHandling.attachedFiles;
74
71
  },
@@ -10,7 +10,6 @@ import { debug } from '$shared/utils/logger';
10
10
  import type { FileAttachment } from './use-file-handling.svelte';
11
11
 
12
12
  interface ChatActionsParams {
13
- messageText: string;
14
13
  attachedFiles: FileAttachment[];
15
14
  clearAllAttachments: () => void;
16
15
  adjustTextareaHeight: () => void;
@@ -27,7 +26,6 @@ export function useChatActions(params: ChatActionsParams) {
27
26
  function handleCancelEdit() {
28
27
  cancelEdit();
29
28
  clearInput();
30
- params.messageText = ''; // This won't work directly, need to pass setter
31
29
  params.clearAllAttachments();
32
30
  params.adjustTextareaHeight();
33
31
  }
@@ -52,6 +50,12 @@ export function useChatActions(params: ChatActionsParams) {
52
50
  await snapshotService.restore(restoreTargetId, sessionState.currentSession.id);
53
51
  }
54
52
 
53
+ // Set skip and clear draft BEFORE reloading messages — editing the first message
54
+ // causes messages to become empty (welcome state), which remounts ChatInput.
55
+ // Without this, the new instance restores stale server state into the input.
56
+ setSkipNextRestore(true);
57
+ params.clearDraft();
58
+
55
59
  // Reload messages from database to update UI
56
60
  if (sessionState.currentSession?.id) {
57
61
  await loadMessagesForSession(sessionState.currentSession.id);
@@ -52,7 +52,7 @@
52
52
 
53
53
  // Auto-scroll reasoning/system content to bottom while receiving partial text
54
54
  $effect(() => {
55
- if (roleCategory !== 'reasoning' && roleCategory !== 'system') return;
55
+ if (roleCategory !== 'reasoning' && roleCategory !== 'system' && roleCategory !== 'compact') return;
56
56
  if (!scrollContainer) return;
57
57
  // Track message content changes (partialText for streaming, message for final)
58
58
  const _track = message.type === 'stream_event' && 'partialText' in message
@@ -93,7 +93,7 @@
93
93
  <!-- Message Content -->
94
94
  <div
95
95
  bind:this={scrollContainer}
96
- class="p-3 md:p-4 {roleCategory === 'reasoning' || roleCategory === 'system' ? 'max-h-80 overflow-y-auto' : ''}"
96
+ class="p-3 md:p-4 {roleCategory === 'reasoning' || roleCategory === 'system' || roleCategory === 'compact' ? 'max-h-80 overflow-y-auto' : ''}"
97
97
  >
98
98
  <div class="max-w-none space-y-4">
99
99
  <!-- Content rendering using MessageFormatter component -->
@@ -428,7 +428,7 @@
428
428
  <span
429
429
  class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-white dark:border-slate-900 {isSessionWaitingInput(session.id, projectState.currentProject?.id) ? 'bg-amber-500' : 'bg-emerald-500'}"
430
430
  ></span>
431
- {:else if isSessionUnread(session.id)}
431
+ {:else if isSessionUnread(session.id) && session.id !== sessionState.currentSession?.id}
432
432
  <span
433
433
  class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-white dark:border-slate-900 bg-blue-500"
434
434
  ></span>
@@ -61,6 +61,9 @@
61
61
  // Flag to prevent URL watcher from double-launching during MCP session creation
62
62
  const mcpLaunchInProgress = $state(false);
63
63
 
64
+ // Touch interaction mode for canvas
65
+ let touchMode = $state<'scroll' | 'cursor'>('scroll');
66
+
64
67
  // Flag to track if sessions were recovered (prevents creating empty tab on mount)
65
68
  let sessionsRecovered = $state(false);
66
69
 
@@ -417,6 +420,14 @@
417
420
  return mcpHandler.isCurrentTabMcpControlled();
418
421
  }
419
422
 
423
+ // Hide MCP virtual cursor when switching to a non-MCP-controlled tab
424
+ $effect(() => {
425
+ void activeTabId; // track activeTabId changes
426
+ if (!isCurrentTabMcpControlled()) {
427
+ mcpVirtualCursor = { x: 0, y: 0, visible: false, clicking: false };
428
+ }
429
+ });
430
+
420
431
  // Stream message handling
421
432
  $effect(() => {
422
433
  if (activeTabId && sessionId) {
@@ -426,6 +437,8 @@
426
437
 
427
438
  // Expose methods for parent (PreviewPanel)
428
439
  export const browserActions = {
440
+ getTouchMode: () => touchMode,
441
+ setTouchMode: (mode: 'scroll' | 'cursor') => { touchMode = mode; },
429
442
  changeDeviceSize: (size: DeviceSize) => {
430
443
  coordinator.changeDeviceSize(size, previewDimensions?.scale);
431
444
  },
@@ -460,7 +473,7 @@
460
473
  bind:isConsoleOpen
461
474
  {tabs}
462
475
  {activeTabId}
463
- mcpControlledTabId={mcpHandler.mcpControlState.controlledTabId}
476
+ mcpControlledTabIds={mcpHandler.getControlledTabIds()}
464
477
  onGoClick={handleGoClick}
465
478
  onRefresh={refreshPreview}
466
479
  onOpenInExternalBrowser={() => {}}
@@ -494,6 +507,7 @@
494
507
  bind:canvasAPI
495
508
  bind:previewDimensions
496
509
  bind:lastFrameData={currentTabLastFrameData}
510
+ bind:touchMode
497
511
  isMcpControlled={isCurrentTabMcpControlled()}
498
512
  onInteraction={handleCanvasInteraction}
499
513
  onRetry={handleGoClick}