mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-24 11:15:48 +08:00
Fix desktop settings modal behavior
This commit is contained in:
@@ -69,7 +69,7 @@ jobs:
|
|||||||
cat release/SHASUMS256.txt
|
cat release/SHASUMS256.txt
|
||||||
|
|
||||||
- name: Upload branch build artifacts
|
- name: Upload branch build artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact.outputs.name }}
|
name: ${{ steps.artifact.outputs.name }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
cat release/SHASUMS256.txt
|
cat release/SHASUMS256.txt
|
||||||
|
|
||||||
- name: Upload branch build artifacts
|
- name: Upload branch build artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact.outputs.name }}
|
name: ${{ steps.artifact.outputs.name }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export class DesktopWindowManager {
|
|||||||
this.tabs = tabs;
|
this.tabs = tabs;
|
||||||
|
|
||||||
this.mainWindow = null;
|
this.mainWindow = null;
|
||||||
|
this.settingsWindow = null;
|
||||||
this.tray = null;
|
this.tray = null;
|
||||||
this.launcherLoaded = false;
|
this.launcherLoaded = false;
|
||||||
this.activeContentView = null;
|
this.activeContentView = null;
|
||||||
@@ -205,8 +206,13 @@ export class DesktopWindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emitDesktopState() {
|
emitDesktopState() {
|
||||||
if (!this.mainWindow || this.mainWindow.webContents.isDestroyed()) return;
|
const state = this.getDesktopState();
|
||||||
this.mainWindow.webContents.send('cloudcli-desktop:state-updated', this.getDesktopState());
|
if (this.mainWindow && !this.mainWindow.webContents.isDestroyed()) {
|
||||||
|
this.mainWindow.webContents.send('cloudcli-desktop:state-updated', state);
|
||||||
|
}
|
||||||
|
if (this.settingsWindow && !this.settingsWindow.webContents.isDestroyed()) {
|
||||||
|
this.settingsWindow.webContents.send('cloudcli-desktop:state-updated', state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitLauncherCommand(command) {
|
emitLauncherCommand(command) {
|
||||||
@@ -214,6 +220,64 @@ export class DesktopWindowManager {
|
|||||||
this.mainWindow.webContents.send('cloudcli-desktop:launcher-command', command);
|
this.mainWindow.webContents.send('cloudcli-desktop:launcher-command', command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitSettingsCommand(command) {
|
||||||
|
if (!this.settingsWindow || this.settingsWindow.webContents.isDestroyed()) return;
|
||||||
|
this.settingsWindow.webContents.send('cloudcli-desktop:launcher-command', command);
|
||||||
|
}
|
||||||
|
|
||||||
|
syncSettingsWindowBounds() {
|
||||||
|
if (!this.mainWindow || !this.settingsWindow || this.settingsWindow.isDestroyed()) return;
|
||||||
|
this.settingsWindow.setBounds(this.mainWindow.getBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
async ensureSettingsWindow(sheet = 'desktop-settings') {
|
||||||
|
if (!this.mainWindow) return null;
|
||||||
|
|
||||||
|
if (this.settingsWindow && !this.settingsWindow.isDestroyed()) {
|
||||||
|
this.syncSettingsWindowBounds();
|
||||||
|
this.emitSettingsCommand({ type: 'open-sheet', sheet });
|
||||||
|
this.settingsWindow.focus();
|
||||||
|
return this.settingsWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settingsWindow = new BrowserWindow({
|
||||||
|
parent: this.mainWindow,
|
||||||
|
modal: true,
|
||||||
|
show: false,
|
||||||
|
frame: false,
|
||||||
|
transparent: true,
|
||||||
|
hasShadow: false,
|
||||||
|
resizable: false,
|
||||||
|
minimizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
fullscreenable: false,
|
||||||
|
movable: false,
|
||||||
|
skipTaskbar: true,
|
||||||
|
backgroundColor: '#00000000',
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: true,
|
||||||
|
preload: this.getPreloadPath(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.syncSettingsWindowBounds();
|
||||||
|
this.configureChildWebContents(this.settingsWindow.webContents);
|
||||||
|
this.settingsWindow.once('ready-to-show', () => this.settingsWindow?.show());
|
||||||
|
this.settingsWindow.on('closed', () => {
|
||||||
|
this.settingsWindow = null;
|
||||||
|
});
|
||||||
|
await this.settingsWindow.loadFile(this.getLauncherPath(), {
|
||||||
|
query: { modal: '1', sheet },
|
||||||
|
});
|
||||||
|
return this.settingsWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSettingsWindow() {
|
||||||
|
if (!this.settingsWindow || this.settingsWindow.isDestroyed()) return;
|
||||||
|
this.settingsWindow.close();
|
||||||
|
}
|
||||||
|
|
||||||
async showTarget(target, { trackTab = true } = {}) {
|
async showTarget(target, { trackTab = true } = {}) {
|
||||||
if (!this.mainWindow) return;
|
if (!this.mainWindow) return;
|
||||||
if (trackTab) {
|
if (trackTab) {
|
||||||
@@ -372,8 +436,8 @@ export class DesktopWindowManager {
|
|||||||
label: 'Services',
|
label: 'Services',
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Computer Access',
|
label: 'Computer Use',
|
||||||
click: () => void this.actions.showComputerAccess(),
|
click: () => void this.showDesktopSettings(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -572,8 +636,13 @@ export class DesktopWindowManager {
|
|||||||
|
|
||||||
async showDesktopSettings() {
|
async showDesktopSettings() {
|
||||||
if (!this.mainWindow) return this.getDesktopState();
|
if (!this.mainWindow) return this.getDesktopState();
|
||||||
await this.showLauncher();
|
await this.ensureSettingsWindow('desktop-settings');
|
||||||
this.emitLauncherCommand({ type: 'open-sheet', sheet: 'app-settings' });
|
return this.getDesktopState();
|
||||||
|
}
|
||||||
|
|
||||||
|
async showLocalSettings() {
|
||||||
|
if (!this.mainWindow) return this.getDesktopState();
|
||||||
|
await this.ensureSettingsWindow('local-settings');
|
||||||
return this.getDesktopState();
|
return this.getDesktopState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,11 +735,17 @@ export class DesktopWindowManager {
|
|||||||
if (this.activeContentView) {
|
if (this.activeContentView) {
|
||||||
this.activeContentView.setBounds(this.getContentViewBounds());
|
this.activeContentView.setBounds(this.getContentViewBounds());
|
||||||
}
|
}
|
||||||
|
this.syncSettingsWindowBounds();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mainWindow.on('move', () => {
|
||||||
|
this.syncSettingsWindowBounds();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mainWindow.on('closed', () => {
|
this.mainWindow.on('closed', () => {
|
||||||
this.tabViews.clear();
|
this.tabViews.clear();
|
||||||
this.activeContentView = null;
|
this.activeContentView = null;
|
||||||
|
this.settingsWindow = null;
|
||||||
this.mainWindow = null;
|
this.mainWindow = null;
|
||||||
this.launcherLoaded = false;
|
this.launcherLoaded = false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html.cc-modal-window,
|
||||||
|
body.cc-modal-window {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--bg: #111315;
|
--bg: #111315;
|
||||||
--s1: #171a1d;
|
--s1: #171a1d;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ window.__MOCK_STATE__ = {
|
|||||||
var MOCK = window.__MOCK_STATE__ || {};
|
var MOCK = window.__MOCK_STATE__ || {};
|
||||||
var VERSION = window.__APP_VERSION__ || '';
|
var VERSION = window.__APP_VERSION__ || '';
|
||||||
var LOGO_URL = new URL('../../public/logo-32.png', window.location.href).toString();
|
var LOGO_URL = new URL('../../public/logo-32.png', window.location.href).toString();
|
||||||
|
var SEARCH = new URLSearchParams(window.location.search || '');
|
||||||
|
|
||||||
function clone(value) {
|
function clone(value) {
|
||||||
return JSON.parse(JSON.stringify(value));
|
return JSON.parse(JSON.stringify(value));
|
||||||
@@ -48,7 +49,9 @@ window.__MOCK_STATE__ = {
|
|||||||
showComputerAccess: function () { return Promise.resolve(clone(mockState)); },
|
showComputerAccess: function () { return Promise.resolve(clone(mockState)); },
|
||||||
showEnvironmentPicker: function () { return Promise.resolve(clone(mockState)); },
|
showEnvironmentPicker: function () { return Promise.resolve(clone(mockState)); },
|
||||||
showLauncher: function () { return Promise.resolve(clone(mockState)); },
|
showLauncher: function () { return Promise.resolve(clone(mockState)); },
|
||||||
|
showLocalSettings: function () { return Promise.resolve(clone(mockState)); },
|
||||||
showDesktopSettings: function () { return Promise.resolve(clone(mockState)); },
|
showDesktopSettings: function () { return Promise.resolve(clone(mockState)); },
|
||||||
|
closeSettingsWindow: function () { return Promise.resolve(clone(mockState)); },
|
||||||
showActiveEnvironmentActionsMenu: function () { return Promise.resolve(clone(mockState)); },
|
showActiveEnvironmentActionsMenu: function () { return Promise.resolve(clone(mockState)); },
|
||||||
openCloudDashboard: function () { return Promise.resolve(clone(mockState)); },
|
openCloudDashboard: function () { return Promise.resolve(clone(mockState)); },
|
||||||
runActiveEnvironmentAction: function () { return Promise.resolve(clone(mockState)); },
|
runActiveEnvironmentAction: function () { return Promise.resolve(clone(mockState)); },
|
||||||
@@ -70,9 +73,6 @@ window.__MOCK_STATE__ = {
|
|||||||
mockState.computerUse.running = mockState.computerUse.enabled;
|
mockState.computerUse.running = mockState.computerUse.enabled;
|
||||||
return Promise.resolve(clone(mockState));
|
return Promise.resolve(clone(mockState));
|
||||||
},
|
},
|
||||||
showComputerAccessPermissions: function () {
|
|
||||||
return Promise.resolve(clone(mockState));
|
|
||||||
},
|
|
||||||
openEnvironment: function (id) {
|
openEnvironment: function (id) {
|
||||||
var env = (mockState.environments || []).filter(function (item) { return item.id === id; })[0];
|
var env = (mockState.environments || []).filter(function (item) { return item.id === id; })[0];
|
||||||
if (env) {
|
if (env) {
|
||||||
@@ -171,12 +171,12 @@ window.__MOCK_STATE__ = {
|
|||||||
function computerUseStatus(state) {
|
function computerUseStatus(state) {
|
||||||
var computerUse = state && state.computerUse ? state.computerUse : {};
|
var computerUse = state && state.computerUse ? state.computerUse : {};
|
||||||
if (!computerUse.enabled) {
|
if (!computerUse.enabled) {
|
||||||
return { label: 'Off', tone: 'idle', detail: 'CloudCLI cannot use this computer.' };
|
return { label: 'Disabled', tone: 'idle', detail: 'CloudCLI cannot use this computer.' };
|
||||||
}
|
}
|
||||||
if (computerUse.consentMode === 'auto') {
|
if (computerUse.consentMode === 'auto') {
|
||||||
return { label: 'Ready · Unattended', tone: 'warn', detail: 'Trusted agents can use this computer without a local approval prompt.' };
|
return { label: 'Unattended access', tone: 'warn', detail: 'Trusted agents can use this computer without a local approval prompt.' };
|
||||||
}
|
}
|
||||||
return { label: 'Ready · Ask first', tone: 'ok', detail: 'CloudCLI can use this computer. Agents need approval before control starts.' };
|
return { label: 'Ask before each session', tone: 'ok', detail: 'Agents need approval before control starts.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
var CC = {
|
var CC = {
|
||||||
@@ -198,6 +198,7 @@ window.__MOCK_STATE__ = {
|
|||||||
_reg: {},
|
_reg: {},
|
||||||
_wired: false,
|
_wired: false,
|
||||||
_poll: null,
|
_poll: null,
|
||||||
|
modalMode: SEARCH.get('modal') === '1',
|
||||||
};
|
};
|
||||||
|
|
||||||
window.CC = CC;
|
window.CC = CC;
|
||||||
@@ -206,11 +207,14 @@ window.__MOCK_STATE__ = {
|
|||||||
var overlay;
|
var overlay;
|
||||||
|
|
||||||
CC.setState = function (state) {
|
CC.setState = function (state) {
|
||||||
|
var currentSheet = CC.ui.openSheet || (CC.modalMode ? (CC.ui.initialSheet || 'desktop-settings') : null);
|
||||||
|
var sheetBody = overlay ? overlay.querySelector('.cc-sheet-body') : null;
|
||||||
|
var scrollTop = sheetBody ? sheetBody.scrollTop : 0;
|
||||||
if (state && typeof state === 'object') CC.state = state;
|
if (state && typeof state === 'object') CC.state = state;
|
||||||
CC.applyTheme(CC.state);
|
CC.applyTheme(CC.state);
|
||||||
CC.render(CC.state);
|
CC.render(CC.state);
|
||||||
if (CC.ui.openSheet) {
|
if (currentSheet) {
|
||||||
CC.openSheet(CC.ui.openSheet);
|
CC.openSheet(currentSheet, { scrollTop: scrollTop });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -334,11 +338,16 @@ window.__MOCK_STATE__ = {
|
|||||||
consentMode: current.consentMode === 'auto' ? 'auto' : 'ask',
|
consentMode: current.consentMode === 'auto' ? 'auto' : 'ask',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
case 'computer-permissions':
|
|
||||||
return CC.run('Opening system permissions...', function () { return bridge.showComputerAccessPermissions(); });
|
|
||||||
case 'settings-toggle':
|
case 'settings-toggle':
|
||||||
CC.openSheet('app-settings');
|
return CC.run('Opening desktop settings...', function () { return bridge.showDesktopSettings(); });
|
||||||
return;
|
case 'desktop-settings-toggle':
|
||||||
|
return CC.run('Opening desktop settings...', function () { return bridge.showDesktopSettings(); });
|
||||||
|
case 'local-settings-toggle':
|
||||||
|
return CC.run('Opening local settings...', function () { return bridge.showLocalSettings(); });
|
||||||
|
case 'computer-settings-toggle':
|
||||||
|
return CC.run('Opening desktop settings...', function () { return bridge.showDesktopSettings(); });
|
||||||
|
case 'settings-close':
|
||||||
|
return CC.closeSheet();
|
||||||
case 'dashboard':
|
case 'dashboard':
|
||||||
return CC.run('Opening CloudCLI dashboard...', function () { return bridge.openCloudDashboard(); });
|
return CC.run('Opening CloudCLI dashboard...', function () { return bridge.openCloudDashboard(); });
|
||||||
case 'env-action':
|
case 'env-action':
|
||||||
@@ -347,15 +356,6 @@ window.__MOCK_STATE__ = {
|
|||||||
return CC.run('Opening environment actions...', function () { return bridge.showActiveEnvironmentActionsMenu(); });
|
return CC.run('Opening environment actions...', function () { return bridge.showActiveEnvironmentActionsMenu(); });
|
||||||
case 'env-row-menu':
|
case 'env-row-menu':
|
||||||
return CC.run('Opening environment actions...', function () { return bridge.showEnvironmentActionsMenu(node.getAttribute('data-cc-environment-id')); });
|
return CC.run('Opening environment actions...', function () { return bridge.showEnvironmentActionsMenu(node.getAttribute('data-cc-environment-id')); });
|
||||||
case 'local-settings-toggle':
|
|
||||||
CC.openSheet('local-settings');
|
|
||||||
return;
|
|
||||||
case 'computer-settings-toggle':
|
|
||||||
CC.openSheet('computer-access');
|
|
||||||
return;
|
|
||||||
case 'settings-close':
|
|
||||||
CC.closeSheet();
|
|
||||||
return;
|
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -430,19 +430,26 @@ window.__MOCK_STATE__ = {
|
|||||||
'</label>';
|
'</label>';
|
||||||
};
|
};
|
||||||
|
|
||||||
CC.openSheet = function (sheet) {
|
CC.openSheet = function (sheet, options) {
|
||||||
if (sheet === 'app-settings') {
|
options = options || {};
|
||||||
CC.renderAppSettings();
|
if (sheet === 'desktop-settings') {
|
||||||
} else if (sheet === 'computer-access') {
|
CC.renderDesktopSettings();
|
||||||
CC.renderComputerAccess();
|
|
||||||
} else {
|
} else {
|
||||||
CC.renderLocalSettings();
|
CC.renderLocalSettings();
|
||||||
}
|
}
|
||||||
CC.ui.openSheet = sheet;
|
CC.ui.openSheet = sheet;
|
||||||
overlay.classList.add('open');
|
overlay.classList.add('open');
|
||||||
|
if (typeof options.scrollTop === 'number') {
|
||||||
|
var body = overlay.querySelector('.cc-sheet-body');
|
||||||
|
if (body) body.scrollTop = options.scrollTop;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CC.closeSheet = function () {
|
CC.closeSheet = function () {
|
||||||
|
if (CC.modalMode && bridge.closeSettingsWindow) {
|
||||||
|
CC.ui.openSheet = null;
|
||||||
|
return bridge.closeSettingsWindow();
|
||||||
|
}
|
||||||
CC.ui.openSheet = null;
|
CC.ui.openSheet = null;
|
||||||
overlay.classList.remove('open');
|
overlay.classList.remove('open');
|
||||||
};
|
};
|
||||||
@@ -478,46 +485,19 @@ window.__MOCK_STATE__ = {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CC.buildComputerAccessSections = function (state, options) {
|
CC.buildComputerUseSection = function (state) {
|
||||||
options = options || {};
|
|
||||||
var computerUse = state.computerUse || {};
|
var computerUse = state.computerUse || {};
|
||||||
var status = computerUseStatus(state);
|
var body =
|
||||||
var sections = [];
|
|
||||||
|
|
||||||
if (options.includeStatus !== false) {
|
|
||||||
sections.push(CC.renderSection(options.statusEyebrow || 'STATUS', status.label, '' +
|
|
||||||
'<div class="cc-surface">' +
|
|
||||||
'<div class="cc-status-badge ' + esc(status.tone) + '">' + esc(status.label) + '</div>' +
|
|
||||||
'<div class="cc-meta">' + esc(status.detail) + '</div>' +
|
|
||||||
'</div>'
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
sections.push(CC.renderSection(options.accessEyebrow || 'ACCESS', 'Allow desktop access', '' +
|
|
||||||
'<div class="cc-surface">' +
|
'<div class="cc-surface">' +
|
||||||
'<label class="cc-toggle"><input type="checkbox" data-cc-computer-enabled="true"' + (computerUse.enabled ? ' checked' : '') + '><span><b>Allow desktop access</b><br>Let CloudCLI use the computer. Agents cannot act until you approve a session.</span></label>' +
|
'<label class="cc-toggle"><input type="checkbox" data-cc-computer-enabled="true"' + (computerUse.enabled ? ' checked' : '') + '><span><b>Enable Computer Use</b><br>Let CloudCLI use the computer. Agents cannot act until you approve a session.</span></label>';
|
||||||
'</div>'
|
if (computerUse.enabled) {
|
||||||
));
|
body += '<div class="cc-choice-group">' +
|
||||||
|
CC.renderRadioOption('computer-access-mode', 'ask', computerUse.consentMode !== 'auto', 'Ask before each session', 'Agents can request control, but you approve every session.') +
|
||||||
sections.push(CC.renderSection(options.modeEyebrow || 'ACCESS MODE', 'Choose how agent approval works', '' +
|
CC.renderRadioOption('computer-access-mode', 'auto', computerUse.consentMode === 'auto', 'Unattended access', 'Trusted agents can use this computer without a local approval prompt.') +
|
||||||
'<div class="cc-surface cc-choice-group">' +
|
'</div>';
|
||||||
CC.renderRadioOption('computer-access-mode', 'ask', computerUse.consentMode !== 'auto', 'Ask before each session', 'Agents can request control, but you approve every session.') +
|
|
||||||
CC.renderRadioOption('computer-access-mode', 'auto', computerUse.consentMode === 'auto', 'Unattended access', 'Trusted agents can use this computer without a local approval prompt.') +
|
|
||||||
'</div>'
|
|
||||||
));
|
|
||||||
|
|
||||||
var setupBody = '<div class="cc-surface">' +
|
|
||||||
'<div class="cc-kv"><span>Linked environments</span><span>' + esc(String(computerUse.connectedCount || 0)) + '</span></div>' +
|
|
||||||
'<div class="cc-kv"><span>Target environments</span><span>' + esc(String(computerUse.targetCount || 0)) + '</span></div>';
|
|
||||||
if (options.includeTheme) {
|
|
||||||
setupBody += '<div class="cc-kv"><span>Theme</span><span>' + esc(themeLabel((state.desktopSettings && state.desktopSettings.themeMode) || 'system')) + '</span></div>';
|
|
||||||
}
|
}
|
||||||
if (CC.platform === 'mac') {
|
body += '</div>';
|
||||||
setupBody += '<div class="cc-actions-inline"><button class="btn sm" data-cc-action="computer-permissions">Open macOS permissions</button></div>';
|
return CC.renderSection('COMPUTER USE', 'Control how agents can use this computer', body);
|
||||||
}
|
|
||||||
setupBody += '</div>';
|
|
||||||
sections.push(CC.renderSection(options.setupEyebrow || 'SETUP', 'System permissions and environment links', setupBody));
|
|
||||||
return sections;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CC.renderLocalSettings = function () {
|
CC.renderLocalSettings = function () {
|
||||||
@@ -530,33 +510,17 @@ window.__MOCK_STATE__ = {
|
|||||||
'<label class="cc-toggle"><input type="checkbox" data-cc-setting="exposeLocalServerOnNetwork"' + ((state.desktopSettings || {}).exposeLocalServerOnNetwork ? ' checked' : '') + '><span><b>Allow LAN access</b><br>Use the copied URL from another device on this network.</span></label>' +
|
'<label class="cc-toggle"><input type="checkbox" data-cc-setting="exposeLocalServerOnNetwork"' + ((state.desktopSettings || {}).exposeLocalServerOnNetwork ? ' checked' : '') + '><span><b>Allow LAN access</b><br>Use the copied URL from another device on this network.</span></label>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
),
|
),
|
||||||
CC.buildThemeSection(state),
|
|
||||||
];
|
];
|
||||||
CC.renderSheet('Local Settings', 'Manage how Local CloudCLI runs and appears on this computer.', sections);
|
CC.renderSheet('Local Settings', 'Manage how Local CloudCLI runs on this computer.', sections);
|
||||||
};
|
};
|
||||||
|
|
||||||
CC.renderAppSettings = function () {
|
CC.renderDesktopSettings = function () {
|
||||||
var state = CC.state || {};
|
var state = CC.state || {};
|
||||||
var sections = [
|
var sections = [
|
||||||
CC.buildLocalServerSection(state, {
|
|
||||||
eyebrow: 'GENERAL',
|
|
||||||
title: 'Local CloudCLI',
|
|
||||||
includePreferences: true,
|
|
||||||
}),
|
|
||||||
CC.buildThemeSection(state),
|
CC.buildThemeSection(state),
|
||||||
|
CC.buildComputerUseSection(state),
|
||||||
];
|
];
|
||||||
sections.push.apply(sections, CC.buildComputerAccessSections(state, {
|
CC.renderSheet('Desktop Settings', 'Manage the desktop app appearance and Computer Use behavior.', sections);
|
||||||
statusEyebrow: 'COMPUTER ACCESS',
|
|
||||||
modeEyebrow: 'APPROVAL MODE',
|
|
||||||
includeTheme: false,
|
|
||||||
}));
|
|
||||||
CC.renderSheet('Settings', 'Manage local behavior, appearance, and desktop access for this computer.', sections);
|
|
||||||
};
|
|
||||||
|
|
||||||
CC.renderComputerAccess = function () {
|
|
||||||
var state = CC.state || {};
|
|
||||||
var sections = CC.buildComputerAccessSections(state, { includeTheme: true });
|
|
||||||
CC.renderSheet('Computer Access', 'Let cloud agents use this computer with explicit approval or unattended access.', sections);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CC.render = function (state) {
|
CC.render = function (state) {
|
||||||
@@ -564,7 +528,11 @@ window.__MOCK_STATE__ = {
|
|||||||
var titlebar = (CC._reg.titlebar || CC.titlebar)(state);
|
var titlebar = (CC._reg.titlebar || CC.titlebar)(state);
|
||||||
var statusbar = (CC._reg.statusbar || CC.statusbar)(state);
|
var statusbar = (CC._reg.statusbar || CC.statusbar)(state);
|
||||||
var body = CC._reg.renderBody ? CC._reg.renderBody(state) : '';
|
var body = CC._reg.renderBody ? CC._reg.renderBody(state) : '';
|
||||||
app.innerHTML = titlebar + '<div class="cc-body ' + (CC._reg.bodyClass || '') + '">' + body + '</div>' + statusbar;
|
if (CC.modalMode) {
|
||||||
|
app.innerHTML = '';
|
||||||
|
} else {
|
||||||
|
app.innerHTML = titlebar + '<div class="cc-body ' + (CC._reg.bodyClass || '') + '">' + body + '</div>' + statusbar;
|
||||||
|
}
|
||||||
if (CC._reg.afterRender) CC._reg.afterRender(state);
|
if (CC._reg.afterRender) CC._reg.afterRender(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -651,6 +619,11 @@ window.__MOCK_STATE__ = {
|
|||||||
var isWin = /Win/i.test(navigator.platform);
|
var isWin = /Win/i.test(navigator.platform);
|
||||||
CC.platform = isMac ? 'mac' : (isWin ? 'win' : 'linux');
|
CC.platform = isMac ? 'mac' : (isWin ? 'win' : 'linux');
|
||||||
document.body.classList.add(CC.platform);
|
document.body.classList.add(CC.platform);
|
||||||
|
CC.ui.initialSheet = SEARCH.get('sheet') || 'desktop-settings';
|
||||||
|
if (CC.modalMode) {
|
||||||
|
document.documentElement.classList.add('cc-modal-window');
|
||||||
|
document.body.classList.add('cc-modal-window');
|
||||||
|
}
|
||||||
|
|
||||||
wireEvents();
|
wireEvents();
|
||||||
if (window.matchMedia) {
|
if (window.matchMedia) {
|
||||||
@@ -664,6 +637,7 @@ window.__MOCK_STATE__ = {
|
|||||||
if (bridge.onLauncherCommand) {
|
if (bridge.onLauncherCommand) {
|
||||||
bridge.onLauncherCommand(function (command) {
|
bridge.onLauncherCommand(function (command) {
|
||||||
if (command && command.type === 'open-sheet') {
|
if (command && command.type === 'open-sheet') {
|
||||||
|
CC.ui.initialSheet = command.sheet || CC.ui.initialSheet || 'desktop-settings';
|
||||||
CC.openSheet(command.sheet);
|
CC.openSheet(command.sheet);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -699,8 +673,8 @@ window.__MOCK_STATE__ = {
|
|||||||
return '<div class="pane-h"><div><h2 class="pane-title">Local CloudCLI</h2><p class="pane-sub">Run the open-source app on this machine. No account required.</p></div></div>' +
|
return '<div class="pane-h"><div><h2 class="pane-title">Local CloudCLI</h2><p class="pane-sub">Run the open-source app on this machine. No account required.</p></div></div>' +
|
||||||
'<div class="card"><div class="card-head"><div><div class="card-t">Local server</div><div class="card-sub mono">' + CC.esc(CC.localUrl(state) || 'Starts on demand') + '</div></div><div class="card-tools"><span class="dot" style="background:' + (state.localServerRunning ? 'var(--ok)' : 'var(--tx3)') + '"></span><button class="icon-btn" data-cc-action="local-settings-toggle" title="Local settings">' + CC.icon('gear', 16) + '</button></div></div>' +
|
'<div class="card"><div class="card-head"><div><div class="card-t">Local server</div><div class="card-sub mono">' + CC.esc(CC.localUrl(state) || 'Starts on demand') + '</div></div><div class="card-tools"><span class="dot" style="background:' + (state.localServerRunning ? 'var(--ok)' : 'var(--tx3)') + '"></span><button class="icon-btn" data-cc-action="local-settings-toggle" title="Local settings">' + CC.icon('gear', 16) + '</button></div></div>' +
|
||||||
'<div class="card-actions"><button class="btn pri" data-cc-action="local">' + CC.icon('play', 15) + 'Open Local CloudCLI</button><button class="btn" data-cc-action="open-web">' + CC.icon('arrow', 14) + 'Open in browser</button><button class="btn" data-cc-action="copy-web">' + CC.icon('copy', 14) + 'Copy URL</button></div></div>' +
|
'<div class="card-actions"><button class="btn pri" data-cc-action="local">' + CC.icon('play', 15) + 'Open Local CloudCLI</button><button class="btn" data-cc-action="open-web">' + CC.icon('arrow', 14) + 'Open in browser</button><button class="btn" data-cc-action="copy-web">' + CC.icon('copy', 14) + 'Copy URL</button></div></div>' +
|
||||||
'<div class="card"><div class="card-head"><div><div class="card-t">Computer Access</div><div class="card-sub">' + CC.esc(computerUseStatus(state).detail) + '</div></div><div class="card-tools"><span class="badge ' + CC.esc(computerUseStatus(state).tone) + '">' + CC.esc(computerUseStatus(state).label) + '</span><button class="icon-btn" data-cc-action="computer-settings-toggle" title="Computer access">' + CC.icon('monitor', 16) + '</button></div></div>' +
|
'<div class="card"><div class="card-head"><div><div class="card-t">Computer Use</div><div class="card-sub">' + CC.esc(computerUseStatus(state).detail) + '</div></div><div class="card-tools"><span class="badge ' + CC.esc(computerUseStatus(state).tone) + '">' + CC.esc(computerUseStatus(state).label) + '</span><button class="icon-btn" data-cc-action="computer-settings-toggle" title="Computer Use settings">' + CC.icon('monitor', 16) + '</button></div></div>' +
|
||||||
'<div class="card-actions"><button class="btn" data-cc-action="computer-settings-toggle">' + CC.icon('settings', 14) + 'Manage access</button></div></div>';
|
'<div class="card-actions"><button class="btn" data-cc-action="computer-settings-toggle">' + CC.icon('settings', 14) + 'Open settings</button></div></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function envRow(environment) {
|
function envRow(environment) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { app, BrowserWindow, clipboard, dialog, ipcMain, shell, systemPreferences } from 'electron';
|
import { app, BrowserWindow, clipboard, dialog, ipcMain, shell } from 'electron';
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
@@ -71,7 +71,7 @@ async function promptComputerUseConsent(sessionId) {
|
|||||||
buttons: ['Allow this session', 'Deny'],
|
buttons: ['Allow this session', 'Deny'],
|
||||||
defaultId: 0,
|
defaultId: 0,
|
||||||
cancelId: 1,
|
cancelId: 1,
|
||||||
title: 'Computer Access request',
|
title: 'Computer Use request',
|
||||||
message: 'An agent wants to control this computer',
|
message: 'An agent wants to control this computer',
|
||||||
detail: [
|
detail: [
|
||||||
'A cloud agent is requesting control of your mouse, keyboard, and screen for this session.',
|
'A cloud agent is requesting control of your mouse, keyboard, and screen for this session.',
|
||||||
@@ -248,38 +248,8 @@ async function copyDiagnostics() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showMacComputerAccessPermissions() {
|
|
||||||
if (process.platform !== 'darwin') return;
|
|
||||||
const screenStatus = systemPreferences.getMediaAccessStatus('screen');
|
|
||||||
const accessibilityTrusted = systemPreferences.isTrustedAccessibilityClient(false);
|
|
||||||
const detail = [
|
|
||||||
`Screen Recording: ${screenStatus === 'granted' ? 'granted' : 'not granted'}`,
|
|
||||||
`Accessibility: ${accessibilityTrusted ? 'granted' : 'not granted'}`,
|
|
||||||
'',
|
|
||||||
'Computer Access needs both permissions to capture the screen and control the mouse and keyboard.',
|
|
||||||
'After granting a permission, fully quit and reopen CloudCLI so the change takes effect.',
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
const { response } = await dialog.showMessageBox(desktopWindow?.getMainWindow() || undefined, {
|
|
||||||
type: 'info',
|
|
||||||
buttons: ['Open Screen Recording', 'Open Accessibility', 'Close'],
|
|
||||||
defaultId: 0,
|
|
||||||
cancelId: 2,
|
|
||||||
title: 'Computer Access Permissions',
|
|
||||||
message: 'Grant macOS permissions for Computer Access',
|
|
||||||
detail,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response === 0) {
|
|
||||||
await shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture');
|
|
||||||
} else if (response === 1) {
|
|
||||||
await shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showComputerAccess() {
|
async function showComputerAccess() {
|
||||||
await desktopWindow?.showLauncher();
|
await desktopWindow?.showDesktopSettings();
|
||||||
desktopWindow?.emitLauncherCommand({ type: 'open-sheet', sheet: 'computer-access' });
|
|
||||||
return getDesktopState();
|
return getDesktopState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,11 +663,12 @@ function registerIpcHandlers() {
|
|||||||
return getDesktopState();
|
return getDesktopState();
|
||||||
});
|
});
|
||||||
ipcMain.handle('cloudcli-desktop:update-computer-use', async (_event, settings) => updateComputerUse(settings));
|
ipcMain.handle('cloudcli-desktop:update-computer-use', async (_event, settings) => updateComputerUse(settings));
|
||||||
ipcMain.handle('cloudcli-desktop:show-computer-use-permissions', async () => {
|
ipcMain.handle('cloudcli-desktop:show-desktop-settings', async () => desktopWindow.showDesktopSettings());
|
||||||
await showMacComputerAccessPermissions();
|
ipcMain.handle('cloudcli-desktop:show-local-settings', async () => desktopWindow.showLocalSettings());
|
||||||
|
ipcMain.handle('cloudcli-desktop:close-settings-window', async () => {
|
||||||
|
desktopWindow.closeSettingsWindow();
|
||||||
return getDesktopState();
|
return getDesktopState();
|
||||||
});
|
});
|
||||||
ipcMain.handle('cloudcli-desktop:show-desktop-settings', async () => desktopWindow.showDesktopSettings());
|
|
||||||
ipcMain.handle('cloudcli-desktop:show-active-environment-actions-menu', async () => desktopWindow.showActiveEnvironmentActionsMenu());
|
ipcMain.handle('cloudcli-desktop:show-active-environment-actions-menu', async () => desktopWindow.showActiveEnvironmentActionsMenu());
|
||||||
ipcMain.handle('cloudcli-desktop:show-environment-actions-menu', async (_event, environmentId) => desktopWindow.showEnvironmentActionsMenu(environmentId));
|
ipcMain.handle('cloudcli-desktop:show-environment-actions-menu', async (_event, environmentId) => desktopWindow.showEnvironmentActionsMenu(environmentId));
|
||||||
ipcMain.handle('cloudcli-desktop:switch-tab', async (_event, tabId) => desktopWindow.switchDesktopTab(tabId));
|
ipcMain.handle('cloudcli-desktop:switch-tab', async (_event, tabId) => desktopWindow.switchDesktopTab(tabId));
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ if (window.location.protocol === 'file:') {
|
|||||||
showEnvironmentPicker: () => ipcRenderer.invoke('cloudcli-desktop:show-environment-picker'),
|
showEnvironmentPicker: () => ipcRenderer.invoke('cloudcli-desktop:show-environment-picker'),
|
||||||
showLauncher: () => ipcRenderer.invoke('cloudcli-desktop:show-launcher'),
|
showLauncher: () => ipcRenderer.invoke('cloudcli-desktop:show-launcher'),
|
||||||
showComputerAccess: () => ipcRenderer.invoke('cloudcli-desktop:show-computer-access'),
|
showComputerAccess: () => ipcRenderer.invoke('cloudcli-desktop:show-computer-access'),
|
||||||
|
showLocalSettings: () => ipcRenderer.invoke('cloudcli-desktop:show-local-settings'),
|
||||||
updateComputerUse: (settings) => ipcRenderer.invoke('cloudcli-desktop:update-computer-use', settings),
|
updateComputerUse: (settings) => ipcRenderer.invoke('cloudcli-desktop:update-computer-use', settings),
|
||||||
showComputerAccessPermissions: () => ipcRenderer.invoke('cloudcli-desktop:show-computer-access-permissions'),
|
|
||||||
showDesktopSettings: () => ipcRenderer.invoke('cloudcli-desktop:show-desktop-settings'),
|
showDesktopSettings: () => ipcRenderer.invoke('cloudcli-desktop:show-desktop-settings'),
|
||||||
|
closeSettingsWindow: () => ipcRenderer.invoke('cloudcli-desktop:close-settings-window'),
|
||||||
showActiveEnvironmentActionsMenu: () => ipcRenderer.invoke('cloudcli-desktop:show-active-environment-actions-menu'),
|
showActiveEnvironmentActionsMenu: () => ipcRenderer.invoke('cloudcli-desktop:show-active-environment-actions-menu'),
|
||||||
showEnvironmentActionsMenu: (environmentId) => ipcRenderer.invoke('cloudcli-desktop:show-environment-actions-menu', environmentId),
|
showEnvironmentActionsMenu: (environmentId) => ipcRenderer.invoke('cloudcli-desktop:show-environment-actions-menu', environmentId),
|
||||||
switchTab: (tabId) => ipcRenderer.invoke('cloudcli-desktop:switch-tab', tabId),
|
switchTab: (tabId) => ipcRenderer.invoke('cloudcli-desktop:switch-tab', tabId),
|
||||||
|
|||||||
Reference in New Issue
Block a user