retrieve mouth dragging on button of main header

This commit is contained in:
sanio 2025-07-11 17:58:31 +09:00
parent 50ffaa0894
commit 2ce82a7edc
4 changed files with 122 additions and 4 deletions

View File

@ -78,6 +78,7 @@ export class ApiKeyHeader extends LitElement {
} }
.close-button { .close-button {
-webkit-app-region: no-drag;
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 10px; right: 10px;
@ -157,6 +158,7 @@ export class ApiKeyHeader extends LitElement {
} }
.api-input { .api-input {
-webkit-app-region: no-drag;
width: 100%; width: 100%;
height: 34px; height: 34px;
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
@ -184,6 +186,7 @@ export class ApiKeyHeader extends LitElement {
.provider-column { flex: 1; display: flex; flex-direction: column; align-items: center; } .provider-column { flex: 1; display: flex; flex-direction: column; align-items: center; }
.provider-label { color: rgba(255, 255, 255, 0.7); font-size: 11px; font-weight: 500; margin-bottom: 6px; } .provider-label { color: rgba(255, 255, 255, 0.7); font-size: 11px; font-weight: 500; margin-bottom: 6px; }
.api-input, .provider-select { .api-input, .provider-select {
-webkit-app-region: no-drag;
width: 100%; width: 100%;
height: 34px; height: 34px;
text-align: center; text-align: center;
@ -210,6 +213,7 @@ export class ApiKeyHeader extends LitElement {
.action-button { .action-button {
-webkit-app-region: no-drag;
width: 100%; width: 100%;
height: 34px; height: 34px;
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);

View File

@ -82,6 +82,7 @@ export class MainHeader extends LitElement {
} }
.listen-button { .listen-button {
-webkit-app-region: no-drag;
height: 26px; height: 26px;
padding: 0 13px; padding: 0 13px;
background: transparent; background: transparent;
@ -189,6 +190,7 @@ export class MainHeader extends LitElement {
} }
.header-actions { .header-actions {
-webkit-app-region: no-drag;
height: 26px; height: 26px;
box-sizing: border-box; box-sizing: border-box;
justify-content: flex-start; justify-content: flex-start;
@ -260,6 +262,7 @@ export class MainHeader extends LitElement {
} }
.settings-button { .settings-button {
-webkit-app-region: no-drag;
padding: 5px; padding: 5px;
border-radius: 50%; border-radius: 50%;
background: transparent; background: transparent;
@ -350,6 +353,61 @@ export class MainHeader extends LitElement {
this.actionText = 'Listen'; this.actionText = 'Listen';
this.animationEndTimer = null; this.animationEndTimer = null;
this.handleAnimationEnd = this.handleAnimationEnd.bind(this); this.handleAnimationEnd = this.handleAnimationEnd.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.dragState = null;
this.wasJustDragged = false;
}
async handleMouseDown(e) {
e.preventDefault();
const { ipcRenderer } = window.require('electron');
const initialPosition = await ipcRenderer.invoke('get-header-position');
this.dragState = {
initialMouseX: e.screenX,
initialMouseY: e.screenY,
initialWindowX: initialPosition.x,
initialWindowY: initialPosition.y,
moved: false,
};
window.addEventListener('mousemove', this.handleMouseMove, { capture: true });
window.addEventListener('mouseup', this.handleMouseUp, { once: true, capture: true });
}
handleMouseMove(e) {
if (!this.dragState) return;
const deltaX = Math.abs(e.screenX - this.dragState.initialMouseX);
const deltaY = Math.abs(e.screenY - this.dragState.initialMouseY);
if (deltaX > 3 || deltaY > 3) {
this.dragState.moved = true;
}
const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX);
const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY);
const { ipcRenderer } = window.require('electron');
ipcRenderer.invoke('move-header-to', newWindowX, newWindowY);
}
handleMouseUp(e) {
if (!this.dragState) return;
const wasDragged = this.dragState.moved;
window.removeEventListener('mousemove', this.handleMouseMove, { capture: true });
this.dragState = null;
if (wasDragged) {
this.wasJustDragged = true;
setTimeout(() => {
this.wasJustDragged = false;
}, 0);
}
} }
toggleVisibility() { toggleVisibility() {
@ -455,6 +513,7 @@ export class MainHeader extends LitElement {
} }
invoke(channel, ...args) { invoke(channel, ...args) {
if (this.wasJustDragged) return;
if (window.require) { if (window.require) {
window.require('electron').ipcRenderer.invoke(channel, ...args); window.require('electron').ipcRenderer.invoke(channel, ...args);
} }
@ -462,6 +521,7 @@ export class MainHeader extends LitElement {
} }
showSettingsWindow(element) { showSettingsWindow(element) {
if (this.wasJustDragged) return;
if (window.require) { if (window.require) {
const { ipcRenderer } = window.require('electron'); const { ipcRenderer } = window.require('electron');
console.log(`[MainHeader] showSettingsWindow called at ${Date.now()}`); console.log(`[MainHeader] showSettingsWindow called at ${Date.now()}`);
@ -481,6 +541,7 @@ export class MainHeader extends LitElement {
} }
hideSettingsWindow() { hideSettingsWindow() {
if (this.wasJustDragged) return;
if (window.require) { if (window.require) {
console.log(`[MainHeader] hideSettingsWindow called at ${Date.now()}`); console.log(`[MainHeader] hideSettingsWindow called at ${Date.now()}`);
window.require('electron').ipcRenderer.send('hide-settings-window'); window.require('electron').ipcRenderer.send('hide-settings-window');
@ -488,6 +549,7 @@ export class MainHeader extends LitElement {
} }
async _handleListenClick() { async _handleListenClick() {
if (this.wasJustDragged) return;
if (this.isTogglingSession) { if (this.isTogglingSession) {
return; return;
} }
@ -536,7 +598,7 @@ export class MainHeader extends LitElement {
const showStopIcon = this.actionText === 'Stop' || this.actionText === 'Done'; const showStopIcon = this.actionText === 'Stop' || this.actionText === 'Done';
return html` return html`
<div class="header"> <div class="header" @mousedown=${this.handleMouseDown}>
<button <button
class="listen-button ${Object.keys(buttonClasses).filter(k => buttonClasses[k]).join(' ')}" class="listen-button ${Object.keys(buttonClasses).filter(k => buttonClasses[k]).join(' ')}"
@click=${this._handleListenClick} @click=${this._handleListenClick}

View File

@ -56,6 +56,7 @@ export class PermissionHeader extends LitElement {
} }
.close-button { .close-button {
-webkit-app-region: no-drag;
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 10px; right: 10px;
@ -146,6 +147,7 @@ export class PermissionHeader extends LitElement {
} }
.action-button { .action-button {
-webkit-app-region: no-drag;
width: 100%; width: 100%;
height: 34px; height: 34px;
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
@ -187,6 +189,7 @@ export class PermissionHeader extends LitElement {
} }
.continue-button { .continue-button {
-webkit-app-region: no-drag;
width: 100%; width: 100%;
height: 34px; height: 34px;
background: rgba(34, 197, 94, 0.8); background: rgba(34, 197, 94, 0.8);

View File

@ -37,8 +37,7 @@ const isLiquidGlassSupported = () => {
} }
const majorVersion = parseInt(os.release().split('.')[0], 10); const majorVersion = parseInt(os.release().split('.')[0], 10);
// return majorVersion >= 25; // macOS 26+ (Darwin 25+) // return majorVersion >= 25; // macOS 26+ (Darwin 25+)
return majorVersion >= 25; // See you soon! return majorVersion >= 26; // See you soon!
return majorVersion >= 25; // See you soon!
}; };
let shouldUseLiquidGlass = isLiquidGlassSupported(); let shouldUseLiquidGlass = isLiquidGlassSupported();
if (shouldUseLiquidGlass) { if (shouldUseLiquidGlass) {
@ -395,7 +394,6 @@ function createWindows() {
headerLoadOptions.query = { glass: 'true' }; headerLoadOptions.query = { glass: 'true' };
header.loadFile(path.join(__dirname, '../app/header.html'), headerLoadOptions); header.loadFile(path.join(__dirname, '../app/header.html'), headerLoadOptions);
header.webContents.once('did-finish-load', () => { header.webContents.once('did-finish-load', () => {
const viewId = liquidGlass.addView(header.getNativeWindowHandle());
const viewId = liquidGlass.addView(header.getNativeWindowHandle()); const viewId = liquidGlass.addView(header.getNativeWindowHandle());
if (viewId !== -1) { if (viewId !== -1) {
liquidGlass.unstable_setVariant(viewId, liquidGlass.GlassMaterialVariant.bubbles); liquidGlass.unstable_setVariant(viewId, liquidGlass.GlassMaterialVariant.bubbles);
@ -948,6 +946,57 @@ function setupIpcHandlers(movementManager) {
} }
}); });
// ipcMain.handle('get-header-position', () => {
// const header = windowPool.get('header');
// if (header) {
// const [x, y] = header.getPosition();
// return { x, y };
// }
// return { x: 0, y: 0 };
// });
// ipcMain.handle('move-header', (event, newX, newY) => {
// const header = windowPool.get('header');
// if (header) {
// const currentY = newY !== undefined ? newY : header.getBounds().y;
// header.setPosition(newX, currentY, false);
// updateLayout();
// }
// });
// ipcMain.handle('move-header-to', (event, newX, newY) => {
// const header = windowPool.get('header');
// if (header) {
// const targetDisplay = screen.getDisplayNearestPoint({ x: newX, y: newY });
// const { x: workAreaX, y: workAreaY, width, height } = targetDisplay.workArea;
// const headerBounds = header.getBounds();
// // Only clamp if the new position would actually go out of bounds
// // This prevents progressive restriction of movement
// let clampedX = newX;
// let clampedY = newY;
// // Check if we need to clamp X position
// if (newX < workAreaX) {
// clampedX = workAreaX;
// } else if (newX + headerBounds.width > workAreaX + width) {
// clampedX = workAreaX + width - headerBounds.width;
// }
// // Check if we need to clamp Y position
// if (newY < workAreaY) {
// clampedY = workAreaY;
// } else if (newY + headerBounds.height > workAreaY + height) {
// clampedY = workAreaY + height - headerBounds.height;
// }
// header.setPosition(clampedX, clampedY, false);
// updateLayout();
// }
// });
ipcMain.handle('move-window-step', (event, direction) => { ipcMain.handle('move-window-step', (event, direction) => {
if (movementManager) { if (movementManager) {