@myrialabs/clopen 0.2.3 → 0.2.4

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 (35) hide show
  1. package/backend/engine/adapters/claude/stream.ts +107 -0
  2. package/backend/engine/adapters/opencode/stream.ts +81 -1
  3. package/backend/engine/types.ts +17 -0
  4. package/backend/git/git-service.ts +2 -1
  5. package/backend/ws/git/commit-message.ts +108 -0
  6. package/backend/ws/git/index.ts +3 -1
  7. package/backend/ws/system/index.ts +7 -1
  8. package/backend/ws/system/operations.ts +28 -2
  9. package/frontend/App.svelte +3 -0
  10. package/frontend/components/auth/SetupPage.svelte +2 -2
  11. package/frontend/components/chat/input/ChatInput.svelte +1 -1
  12. package/frontend/components/chat/message/ChatMessage.svelte +64 -16
  13. package/frontend/components/checkpoint/ConflictResolutionModal.svelte +189 -0
  14. package/frontend/components/checkpoint/TimelineModal.svelte +7 -162
  15. package/frontend/components/common/feedback/RestartRequiredModal.svelte +53 -0
  16. package/frontend/components/common/feedback/UpdateBanner.svelte +17 -6
  17. package/frontend/components/git/BranchManager.svelte +143 -155
  18. package/frontend/components/git/CommitForm.svelte +61 -11
  19. package/frontend/components/settings/SettingsModal.svelte +1 -1
  20. package/frontend/components/settings/SettingsView.svelte +1 -1
  21. package/frontend/components/settings/engines/AIEnginesSettings.svelte +2 -2
  22. package/frontend/components/settings/general/UpdateSettings.svelte +10 -3
  23. package/frontend/components/settings/git/GitSettings.svelte +392 -0
  24. package/frontend/components/settings/model/EngineModelPicker.svelte +275 -0
  25. package/frontend/components/settings/model/ModelSettings.svelte +172 -289
  26. package/frontend/components/workspace/PanelHeader.svelte +1 -3
  27. package/frontend/components/workspace/WorkspaceLayout.svelte +11 -1
  28. package/frontend/components/workspace/panels/GitPanel.svelte +12 -5
  29. package/frontend/main.ts +4 -0
  30. package/frontend/stores/features/settings.svelte.ts +13 -2
  31. package/frontend/stores/ui/settings-modal.svelte.ts +9 -9
  32. package/frontend/stores/ui/update.svelte.ts +45 -4
  33. package/package.json +1 -1
  34. package/shared/types/git.ts +15 -0
  35. package/shared/types/stores/settings.ts +12 -0
@@ -32,7 +32,13 @@ const defaultSettings: AppSettings = {
32
32
  soundNotifications: true,
33
33
  pushNotifications: false,
34
34
  layoutPresetVisibility: createDefaultPresetVisibility(),
35
- fontSize: 13
35
+ fontSize: 13,
36
+ commitGenerator: {
37
+ useCustomModel: false,
38
+ engine: 'claude-code',
39
+ model: 'claude-code:haiku',
40
+ format: 'single-line'
41
+ }
36
42
  };
37
43
 
38
44
  // Default system settings
@@ -63,7 +69,12 @@ export function applyFontSize(size: number): void {
63
69
  export function applyServerSettings(serverSettings: Partial<AppSettings> | null): void {
64
70
  if (serverSettings && typeof serverSettings === 'object') {
65
71
  // Merge with defaults to ensure all properties exist
66
- Object.assign(settings, { ...defaultSettings, ...serverSettings });
72
+ const merged = { ...defaultSettings, ...serverSettings };
73
+ // Deep merge nested objects so new default fields are preserved
74
+ if (serverSettings.commitGenerator) {
75
+ merged.commitGenerator = { ...defaultSettings.commitGenerator, ...serverSettings.commitGenerator };
76
+ }
77
+ Object.assign(settings, merged);
67
78
  applyFontSize(settings.fontSize);
68
79
  debug.log('settings', 'Applied server settings');
69
80
  }
@@ -6,7 +6,7 @@
6
6
  import type { IconName } from '$shared/types/ui/icons';
7
7
 
8
8
  export type SettingsSection =
9
- | 'model'
9
+ | 'models'
10
10
  | 'engines'
11
11
  | 'appearance'
12
12
  | 'notifications'
@@ -31,10 +31,10 @@ export interface SettingsSectionMeta {
31
31
 
32
32
  export const settingsSections: SettingsSectionMeta[] = [
33
33
  {
34
- id: 'model',
35
- label: 'Model',
36
- icon: 'lucide:cpu',
37
- description: 'AI engine and model'
34
+ id: 'models',
35
+ label: 'Models',
36
+ icon: 'lucide:sparkles',
37
+ description: 'Chat and commit model'
38
38
  },
39
39
  {
40
40
  id: 'appearance',
@@ -56,8 +56,8 @@ export const settingsSections: SettingsSectionMeta[] = [
56
56
  },
57
57
  {
58
58
  id: 'engines',
59
- label: 'AI Engine',
60
- icon: 'lucide:bot',
59
+ label: 'Engines',
60
+ icon: 'lucide:plug',
61
61
  description: 'Installation and accounts',
62
62
  adminOnly: true
63
63
  },
@@ -87,11 +87,11 @@ export const settingsSections: SettingsSectionMeta[] = [
87
87
  // Create the state using Svelte 5 runes
88
88
  export const settingsModalState = $state<SettingsModalState>({
89
89
  isOpen: false,
90
- activeSection: 'model'
90
+ activeSection: 'models'
91
91
  });
92
92
 
93
93
  // Helper functions
94
- export function openSettingsModal(section: SettingsSection = 'model') {
94
+ export function openSettingsModal(section: SettingsSection = 'models') {
95
95
  settingsModalState.isOpen = true;
96
96
  settingsModalState.activeSection = section;
97
97
  }
@@ -18,6 +18,9 @@ interface UpdateState {
18
18
  errorType: 'check' | 'update' | null;
19
19
  updateOutput: string | null;
20
20
  updateSuccess: boolean;
21
+ pendingRestart: boolean;
22
+ pendingVersions: { from: string; to: string } | null;
23
+ showRestartModal: boolean;
21
24
  }
22
25
 
23
26
  export const updateState = $state<UpdateState>({
@@ -30,7 +33,10 @@ export const updateState = $state<UpdateState>({
30
33
  error: null,
31
34
  errorType: null,
32
35
  updateOutput: null,
33
- updateSuccess: false
36
+ updateSuccess: false,
37
+ pendingRestart: false,
38
+ pendingVersions: null,
39
+ showRestartModal: false
34
40
  });
35
41
 
36
42
  let checkInterval: ReturnType<typeof setInterval> | null = null;
@@ -49,8 +55,17 @@ export async function checkForUpdate(): Promise<void> {
49
55
  updateState.latestVersion = result.latestVersion;
50
56
  updateState.updateAvailable = result.updateAvailable;
51
57
 
52
- // Auto-update if enabled and update is available
53
- if (result.updateAvailable && systemSettings.autoUpdate) {
58
+ // Restore pending restart state from backend (survives page refresh)
59
+ if (result.pendingRestart && result.pendingUpdate) {
60
+ updateState.pendingRestart = true;
61
+ updateState.pendingVersions = { from: result.pendingUpdate.fromVersion, to: result.pendingUpdate.toVersion };
62
+ updateState.updateSuccess = true;
63
+ updateState.latestVersion = result.pendingUpdate.toVersion;
64
+ updateState.showRestartModal = true;
65
+ }
66
+
67
+ // Auto-update if enabled and update is available (skip if already pending restart)
68
+ if (result.updateAvailable && systemSettings.autoUpdate && !result.pendingRestart) {
54
69
  debug.log('server', 'Auto-update enabled, starting update...');
55
70
  await runUpdate();
56
71
  }
@@ -77,7 +92,10 @@ export async function runUpdate(): Promise<void> {
77
92
  updateState.updateOutput = result.output;
78
93
  updateState.updateSuccess = true;
79
94
  updateState.updateAvailable = false;
95
+ updateState.pendingRestart = true;
96
+ updateState.pendingVersions = { from: updateState.currentVersion, to: result.newVersion };
80
97
  updateState.latestVersion = result.newVersion;
98
+ updateState.showRestartModal = true;
81
99
 
82
100
  debug.log('server', 'Update completed successfully');
83
101
  } catch (err) {
@@ -89,11 +107,34 @@ export async function runUpdate(): Promise<void> {
89
107
  }
90
108
  }
91
109
 
92
- /** Dismiss the update banner */
110
+ /** Dismiss the update banner (not allowed when restart is pending) */
93
111
  export function dismissUpdate(): void {
112
+ if (updateState.pendingRestart) return;
94
113
  updateState.dismissed = true;
95
114
  }
96
115
 
116
+ /** Show the restart instructions modal */
117
+ export function showRestartModal(): void {
118
+ updateState.showRestartModal = true;
119
+ }
120
+
121
+ /** Hide the restart instructions modal */
122
+ export function hideRestartModal(): void {
123
+ updateState.showRestartModal = false;
124
+ }
125
+
126
+ // Listen for update-completed broadcast (notifies other tabs/clients)
127
+ ws.on('system:update-completed', (payload) => {
128
+ debug.log('server', 'Update completed broadcast received');
129
+ updateState.updateSuccess = true;
130
+ updateState.updateAvailable = false;
131
+ updateState.pendingRestart = true;
132
+ updateState.pendingVersions = { from: payload.fromVersion, to: payload.toVersion };
133
+ updateState.latestVersion = payload.toVersion;
134
+ updateState.showRestartModal = true;
135
+ updateState.dismissed = false;
136
+ });
137
+
97
138
  /** Start periodic update checks (every 30 minutes) */
98
139
  export function startUpdateChecker(): void {
99
140
  // Initial check after 5 seconds (let the app settle)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myrialabs/clopen",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "All-in-one web workspace for Claude Code & OpenCode — chat, terminal, git, browser preview, checkpoints, and real-time collaboration",
5
5
  "author": "Myria Labs",
6
6
  "license": "MIT",
@@ -169,3 +169,18 @@ export interface GitTag {
169
169
  date: string;
170
170
  isAnnotated: boolean;
171
171
  }
172
+
173
+ // ============================================
174
+ // Commit Message Generation
175
+ // ============================================
176
+
177
+ /** Format for AI-generated commit messages */
178
+ export type CommitMessageFormat = 'single-line' | 'multi-line';
179
+
180
+ /** Structured commit message output from AI */
181
+ export interface GeneratedCommitMessage {
182
+ type: string;
183
+ scope: string;
184
+ subject: string;
185
+ body: string;
186
+ }
@@ -1,4 +1,14 @@
1
1
  import type { EngineType } from '$shared/types/engine';
2
+ import type { CommitMessageFormat } from '$shared/types/git';
3
+
4
+ /** AI commit message generator settings */
5
+ export interface CommitGeneratorSettings {
6
+ /** When false, uses the chat model (selectedEngine/selectedModel). When true, uses custom engine/model below. */
7
+ useCustomModel: boolean;
8
+ engine: EngineType;
9
+ model: string;
10
+ format: CommitMessageFormat;
11
+ }
2
12
 
3
13
  /** Per-user settings (stored per user) */
4
14
  export interface AppSettings {
@@ -13,6 +23,8 @@ export interface AppSettings {
13
23
  layoutPresetVisibility: Record<string, boolean>;
14
24
  /** Base font size in pixels (10–20). Default: 13. */
15
25
  fontSize: number;
26
+ /** AI commit message generator configuration */
27
+ commitGenerator: CommitGeneratorSettings;
16
28
  }
17
29
 
18
30
  /** Authentication mode */