-
Notifications
You must be signed in to change notification settings - Fork 0
Move settings to separate window and improve UI spacing #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1156c82
2c5ecac
2140102
b97b9ba
451663b
42e3ac0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # AGENTS.md | ||
|
|
||
| ## Cursor Cloud specific instructions | ||
|
|
||
| This is a **Tauri 2** desktop application (Rust backend + React 19/TypeScript frontend). A single command `pnpm tauri dev` starts both the Vite dev server (port 1420) and the native Tauri window. | ||
|
|
||
| ### Prerequisites (system-level, installed once in the VM snapshot) | ||
|
|
||
| - **Tauri Linux system libraries**: `libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf` | ||
| - **Rust stable** (1.94+): The default rustup toolchain must be `stable`, not the older system-pinned `1.83.0`. Run `rustup default stable` if `cargo --version` shows < 1.94. | ||
| - **Node.js** (v18+) and **pnpm** (v9+): Already available via nvm. | ||
|
|
||
| ### Key commands | ||
|
|
||
| | Task | Command | | ||
| |---|---| | ||
| | Install frontend deps | `pnpm install` | | ||
| | TypeScript lint/check | `pnpm exec tsc --noEmit` | | ||
| | Frontend build | `pnpm build` | | ||
| | Rust check | `cd src-tauri && cargo check` | | ||
| | Run dev (full app) | `pnpm tauri dev` | | ||
| | Production build | `pnpm tauri build` | | ||
|
|
||
| ### Gotchas | ||
|
|
||
| - The first `cargo check` / `pnpm tauri dev` after a clean clone takes ~60s to compile all Rust dependencies. Subsequent builds are incremental and much faster. | ||
| - In headless/VM environments, expect `libEGL warning: DRI3 error` messages — these are benign and the app still renders correctly via software rendering. | ||
| - `pnpm tauri dev` auto-runs `pnpm dev` (Vite) as a `beforeDevCommand`; you don't need to start Vite separately. | ||
| - There are no automated test suites in this repository. Validation is done via TypeScript type checking (`tsc --noEmit`) and building the app. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||
| import { getCurrentWindow } from "@tauri-apps/api/window"; | ||||||||||||||||||||||||
| import { X } from "lucide-react"; | ||||||||||||||||||||||||
| import { SettingsProvider } from "./contexts/SettingsContext"; | ||||||||||||||||||||||||
| import Settings from "./components/Settings"; | ||||||||||||||||||||||||
| import "./globals.css"; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const SettingsWindowContent: React.FC = () => { | ||||||||||||||||||||||||
| const handleClose = async () => { | ||||||||||||||||||||||||
| const win = getCurrentWindow(); | ||||||||||||||||||||||||
| await win.close(); | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
| <div className="bg-[#1a1a1a] min-h-screen overflow-y-auto"> | ||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||
| data-tauri-drag-region | ||||||||||||||||||||||||
| className="sticky top-0 z-10 flex items-center justify-between bg-[#1a1a1a] cursor-move" | ||||||||||||||||||||||||
| style={{ padding: '18px 28px', borderBottom: '2px solid #444' }} | ||||||||||||||||||||||||
| > | ||||||||||||||||||||||||
| <h2 className="text-lg font-semibold text-white pointer-events-none">Settings</h2> | ||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||
| onClick={handleClose} | ||||||||||||||||||||||||
| className="p-1.5 hover:bg-gray-700 rounded-md transition-colors cursor-pointer" | ||||||||||||||||||||||||
| title="Close settings" | ||||||||||||||||||||||||
| > | ||||||||||||||||||||||||
|
Comment on lines
+21
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add an accessible name to the icon-only close button.
Suggested fix <button
onClick={handleClose}
className="p-1.5 hover:bg-gray-700 rounded-md transition-colors cursor-pointer"
+ aria-label="Close settings"
title="Close settings"
>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| <X className="w-5 h-5 text-gray-400" /> | ||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||
| <Settings /> | ||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const SettingsWindow: React.FC = () => { | ||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
|
Comment on lines
+7
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== tsconfig files =="
fd -HI '^tsconfig.*\.json$' -x sh -c 'echo "--- $1 ---"; cat "$1"' sh {}
echo "== React.FC usages =="
rg -nP '\bReact\.FC\b' --type ts --type tsx
echo "== allowUmdGlobalAccess explicitly true =="
rg -nP '"allowUmdGlobalAccess"\s*:\s*true' -g 'tsconfig*.json' || trueRepository: emmsixx/always-on-clock Length of output: 1060 🏁 Script executed: # Find and read the SettingsWindow.tsx file
fd -HI 'SettingsWindow\.tsx$' -x cat -n {}
# Also check other .tsx files to see if React/FC is imported
echo "=== Checking other React.FC/FC imports in tsx files ==="
rg 'import.*(?:React|FC)' --type-list | head -1
fd -HI '\.tsx$' | head -10 | xargs -I {} sh -c 'echo "--- {} ---"; head -20 {}'Repository: emmsixx/always-on-clock Length of output: 7191 🏁 Script executed: # Search for .d.ts files that might declare React globally
fd -HI '\.d\.ts$' -x cat -n {}
# Search for any triple-slash directives or ambient declarations
rg -HI 'declare\s+namespace\s+React|/// <reference' --type ts --type tsx
# Check if tsc would actually report an error
# Try to find tsconfig references
find . -name "*.d.ts" -o -name "react.d.ts" 2>/dev/null | head -20Repository: emmsixx/always-on-clock Length of output: 177 Import React types explicitly instead of relying on Using Suggested fix+import type { FC } from "react";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { X } from "lucide-react";
import { SettingsProvider } from "./contexts/SettingsContext";
import Settings from "./components/Settings";
import "./globals.css";
-const SettingsWindowContent: React.FC = () => {
+const SettingsWindowContent: FC = () => {
const handleClose = async () => {
const win = getCurrentWindow();
await win.close();
};
@@
-const SettingsWindow: React.FC = () => {
+const SettingsWindow: FC = () => {🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| <SettingsProvider> | ||||||||||||||||||||||||
| <SettingsWindowContent /> | ||||||||||||||||||||||||
| </SettingsProvider> | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| export default SettingsWindow; | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,41 @@ | ||
| import { getCurrentWindow } from "@tauri-apps/api/window"; | ||
| import { WebviewWindow } from "@tauri-apps/api/webviewWindow"; | ||
| import React, { useState, useEffect, useRef } from "react"; | ||
| import { TrayIcon } from '@tauri-apps/api/tray'; | ||
| import { defaultWindowIcon } from "@tauri-apps/api/app"; | ||
| import { Menu } from '@tauri-apps/api/menu'; | ||
| import { Clock1, PinIcon, Settings as SettingsIcon } from "lucide-react"; | ||
| import Settings from "./components/Settings"; | ||
|
|
||
| interface TitleBarProps { | ||
| onSettingsOpen?: () => void; | ||
| async function openSettingsWindow() { | ||
| const existing = await WebviewWindow.getByLabel('settings'); | ||
| if (existing) { | ||
| await existing.setFocus(); | ||
| return; | ||
| } | ||
|
|
||
| const mainWin = getCurrentWindow(); | ||
| const pos = await mainWin.innerPosition(); | ||
| const size = await mainWin.innerSize(); | ||
|
|
||
| const webview = new WebviewWindow('settings', { | ||
| url: '/', | ||
| title: 'Settings', | ||
| width: 420, | ||
| height: 700, | ||
| x: pos.x + size.width + 16, | ||
| y: pos.y, | ||
| resizable: false, | ||
| decorations: false, | ||
| transparent: false, | ||
| }); | ||
|
Comment on lines
+16
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clamp the settings window into the visible work area.
🤖 Prompt for AI Agents |
||
|
|
||
| webview.once('tauri://error', (e) => { | ||
| console.error('Failed to create settings window:', e); | ||
| }); | ||
| } | ||
|
|
||
| const TitleBar: React.FC<TitleBarProps> = () => { | ||
| const TitleBar: React.FC = () => { | ||
| const [onTop, setOnTop] = useState<boolean | null>(null); | ||
| const [settingsOpen, setSettingsOpen] = useState(false); | ||
| const trayRef = useRef<TrayIcon | null>(null); | ||
|
|
||
| useEffect(() => { | ||
|
|
@@ -39,8 +62,9 @@ const TitleBar: React.FC<TitleBarProps> = () => { | |
| { | ||
| id: 'settings', | ||
| text: 'Settings', | ||
| action: () => { | ||
| setTop(false).then(() => setSettingsOpen(true)); | ||
| action: async () => { | ||
| await setTop(false); | ||
| await openSettingsWindow(); | ||
| }, | ||
| }, | ||
| { | ||
|
|
@@ -90,22 +114,23 @@ const TitleBar: React.FC<TitleBarProps> = () => { | |
| ) : onTop ? null : ( | ||
| <div | ||
| data-tauri-drag-region | ||
| className="w-full cursor-move flex flex-row justify-between items-center text-white px-3 py-2 text-lg bg-[#0c0d0f] transition-all" | ||
| className="w-full cursor-move flex flex-row justify-between items-center text-white text-lg bg-[#0c0d0f] transition-all" | ||
| style={{ padding: '14px 28px' }} | ||
| > | ||
| <div className="flex flex-row items-center space-x-2 pointer-events-none"> | ||
| <div className="flex flex-row items-center space-x-2.5 pointer-events-none"> | ||
| <Clock1 className="w-5 h-5" /> | ||
| <span className="text-sm font-medium">Clock</span> | ||
| </div> | ||
| <div className="flex flex-row items-center space-x-1"> | ||
| <div className="flex flex-row items-center space-x-1.5"> | ||
| <button | ||
| className="p-1.5 hover:bg-gray-700 rounded transition-colors cursor-pointer" | ||
| onClick={() => setSettingsOpen(true)} | ||
| className="p-2 hover:bg-gray-700 rounded-md transition-colors cursor-pointer" | ||
| onClick={() => openSettingsWindow()} | ||
| title="Settings" | ||
| > | ||
| <SettingsIcon className="w-4 h-4" /> | ||
| </button> | ||
| <button | ||
| className="p-1.5 hover:bg-gray-700 rounded transition-colors cursor-pointer" | ||
| className="p-2 hover:bg-gray-700 rounded-md transition-colors cursor-pointer" | ||
| onClick={() => setTop(true)} | ||
| title="Pin window (always on top)" | ||
| > | ||
|
|
@@ -114,7 +139,6 @@ const TitleBar: React.FC<TitleBarProps> = () => { | |
| </div> | ||
| </div> | ||
| )} | ||
| <Settings isOpen={settingsOpen} onClose={() => setSettingsOpen(false)} /> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the default.json file structure cat -n src-tauri/capabilities/default.jsonRepository: emmsixx/always-on-clock
Length of output: 1220
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 138
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 786
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 49
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 996
🏁 Script executed:
# Check the desktop.json file cat -n src-tauri/capabilities/desktop.jsonRepository: emmsixx/always-on-clock
Length of output: 347
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 1491
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 49
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 2157
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 49
🏁 Script executed:
# Check the full TitleBar.tsx to see window creation details cat -n src/TitleBar.tsxRepository: emmsixx/always-on-clock
Length of output: 5375
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 210
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 49
🏁 Script executed:
Repository: emmsixx/always-on-clock
Length of output: 275
Split
settingsinto its own minimal capability.The settings window receives permissions it does not use. Autostart and global-shortcut setup are guarded to main window only in
SettingsContext.tsx(lines 67, 83). Window control operations likesetAlwaysOnTop(),setIgnoreCursorEvents(), and start-dragging are used only on the main window inTitleBar.tsx. WebviewWindow creation also happens only from the main window context. Create a separate capability forsettingswith only:core:default,opener:default,store:default, and minimal window permissions (allow-close,allow-show,allow-hide,allow-set-focus).🤖 Prompt for AI Agents