@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.
- package/backend/engine/adapters/claude/stream.ts +107 -0
- package/backend/engine/adapters/opencode/stream.ts +81 -1
- package/backend/engine/types.ts +17 -0
- package/backend/git/git-service.ts +2 -1
- package/backend/ws/git/commit-message.ts +108 -0
- package/backend/ws/git/index.ts +3 -1
- package/backend/ws/system/index.ts +7 -1
- package/backend/ws/system/operations.ts +28 -2
- package/frontend/App.svelte +3 -0
- package/frontend/components/auth/SetupPage.svelte +2 -2
- package/frontend/components/chat/input/ChatInput.svelte +1 -1
- package/frontend/components/chat/message/ChatMessage.svelte +64 -16
- package/frontend/components/checkpoint/ConflictResolutionModal.svelte +189 -0
- package/frontend/components/checkpoint/TimelineModal.svelte +7 -162
- package/frontend/components/common/feedback/RestartRequiredModal.svelte +53 -0
- package/frontend/components/common/feedback/UpdateBanner.svelte +17 -6
- package/frontend/components/git/BranchManager.svelte +143 -155
- package/frontend/components/git/CommitForm.svelte +61 -11
- package/frontend/components/settings/SettingsModal.svelte +1 -1
- package/frontend/components/settings/SettingsView.svelte +1 -1
- package/frontend/components/settings/engines/AIEnginesSettings.svelte +2 -2
- package/frontend/components/settings/general/UpdateSettings.svelte +10 -3
- package/frontend/components/settings/git/GitSettings.svelte +392 -0
- package/frontend/components/settings/model/EngineModelPicker.svelte +275 -0
- package/frontend/components/settings/model/ModelSettings.svelte +172 -289
- package/frontend/components/workspace/PanelHeader.svelte +1 -3
- package/frontend/components/workspace/WorkspaceLayout.svelte +11 -1
- package/frontend/components/workspace/panels/GitPanel.svelte +12 -5
- package/frontend/main.ts +4 -0
- package/frontend/stores/features/settings.svelte.ts +13 -2
- package/frontend/stores/ui/settings-modal.svelte.ts +9 -9
- package/frontend/stores/ui/update.svelte.ts +45 -4
- package/package.json +1 -1
- package/shared/types/git.ts +15 -0
- 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
|
-
|
|
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
|
-
| '
|
|
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: '
|
|
35
|
-
label: '
|
|
36
|
-
icon: 'lucide:
|
|
37
|
-
description: '
|
|
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: '
|
|
60
|
-
icon: 'lucide:
|
|
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: '
|
|
90
|
+
activeSection: 'models'
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
// Helper functions
|
|
94
|
-
export function openSettingsModal(section: SettingsSection = '
|
|
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
|
-
//
|
|
53
|
-
if (result.
|
|
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
|
+
"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",
|
package/shared/types/git.ts
CHANGED
|
@@ -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 */
|