+
untitled.md
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 8c2d1c9..d74a012 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -31,4 +31,3 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-deep-link = "2.4.6"
url = "2.5.8"
-
diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index d44f1ad..9d2896c 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -9,6 +9,7 @@
"core:window:allow-destroy",
"core:window:allow-set-title",
"core:window:allow-set-focus",
+ "core:window:allow-set-position",
"opener:default",
"dialog:default",
"fs:default",
diff --git a/src/main.ts b/src/main.ts
index 438abf3..f06ff9c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,7 @@
import type { Editor } from '@tiptap/core'
import { TextSelection } from '@tiptap/pm/state'
+import { LogicalPosition } from '@tauri-apps/api/dpi'
+import { getCurrentWindow } from '@tauri-apps/api/window'
import { createQuillEditor, ensureGrammarsForDoc, getMarkdown, setMarkdown } from './editor-setup'
import { FileOps } from './file-ops'
import { selectStartupFiles } from './startup-files'
@@ -86,6 +88,37 @@ document.addEventListener('DOMContentLoaded', async () => {
const modifiedIndicator = document.getElementById('modified-indicator')!
const wordCountEl = document.getElementById('word-count')!
const editorEl = document.getElementById('editor')!
+ const titlebarEl = document.getElementById('titlebar')!
+
+ // macOS 27 can reject AppKit's performWindowDragWithEvent for an overlay
+ // titlebar. Move only this header by cursor delta instead; the editor never
+ // receives this listener, so text selection and its own drag/drop are intact.
+ titlebarEl.addEventListener('mousedown', async event => {
+ if (event.button !== 0) return
+ event.preventDefault()
+
+ try {
+ const win = getCurrentWindow()
+ const [position, scale] = await Promise.all([win.outerPosition(), win.scaleFactor()])
+ const startX = event.screenX
+ const startY = event.screenY
+
+ const move = (moveEvent: MouseEvent) => {
+ const x = position.x / scale + moveEvent.screenX - startX
+ const y = position.y / scale + moveEvent.screenY - startY
+ void win.setPosition(new LogicalPosition(x, y))
+ }
+ const stop = () => {
+ window.removeEventListener('mousemove', move)
+ window.removeEventListener('mouseup', stop)
+ }
+
+ window.addEventListener('mousemove', move)
+ window.addEventListener('mouseup', stop, { once: true })
+ } catch {
+ // Browser-based harness: no native window.
+ }
+ })
// --- editor ---
let fileOps: FileOps
diff --git a/src/styles.css b/src/styles.css
index dd8fa6b..2291ea5 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -84,7 +84,6 @@ button {
background: var(--bg-primary);
user-select: none;
-webkit-user-select: none;
- -webkit-app-region: drag;
flex-shrink: 0;
}