Skip to content

feat(desktop): add Quick Entry overlay with global shortcut#2340

Draft
tink-bot wants to merge 14 commits intomainfrom
feat-quick-entry
Draft

feat(desktop): add Quick Entry overlay with global shortcut#2340
tink-bot wants to merge 14 commits intomainfrom
feat-quick-entry

Conversation

@tink-bot
Copy link
Collaborator

@tink-bot tink-bot commented Mar 3, 2026

Summary

  • Adds a global-hotkey-activated Quick Entry overlay window (CmdOrCtrl+Shift+A) to the Electron desktop app for fast task creation without switching to the main window
  • Reuses the existing QuickActions.vue component, auto-selecting "New Task" mode in the overlay
  • Adds system tray with Show/Quick Add/Quit menu and hide-to-tray on window close
  • Uses BroadcastChannel API to notify the main window to refresh after task creation from the overlay

New files

File Purpose
desktop/preload-quick-entry.js Context bridge exposing window.quickEntry.close() IPC
frontend/src/composables/useQuickAddMode.ts Detects ?mode=quick-add URL param
frontend/src/components/quick-actions/QuickAddOverlay.vue Wrapper component for the overlay window
frontend/src/types/quick-entry.d.ts TypeScript declaration for window.quickEntry

Modified files

File Changes
desktop/main.js Full rewrite: quick-entry BrowserWindow, globalShortcut, system tray, IPC, security fixes
desktop/package.json Added files array to electron-builder config
frontend/src/App.vue Routes ?mode=quick-add to QuickAddOverlay, transparent background
frontend/src/components/quick-actions/QuickActions.vue Auto-selects "New Task" in quick-add mode, BroadcastChannel notify, skip navigation
frontend/src/components/home/ContentAuth.vue BroadcastChannel listener to refresh on task creation
frontend/src/i18n/lang/en.json Added quickActions.notLoggedIn translation key

Test plan

  • App launches normally with main window and tray icon
  • CmdOrCtrl+Shift+A opens quick entry overlay centered in upper third
  • Overlay pre-selects "New Task" command
  • Typing a task title and pressing Enter creates the task and dismisses the overlay
  • Main window task list refreshes after quick-entry task creation
  • Clicking outside the overlay dismisses it (blur-to-hide)
  • Pressing Escape dismisses the overlay
  • Closing main window hides to tray (app keeps running)
  • Global shortcut works even with main window hidden
  • Tray menu: Show Vikunja, Quick Add Task, Quit all work
  • Not-logged-in state shows appropriate message in overlay

@kolaente
Copy link
Member

kolaente commented Mar 3, 2026

auggie review

@augmentcode
Copy link

augmentcode bot commented Mar 3, 2026

🤖 Augment PR Summary

Summary: Adds a global-hotkey “Quick Entry” overlay to the Electron desktop app for rapid task creation without switching to the main window.

Changes:

  • Rewrites desktop/main.js to start a local Express server, create both main + quick-entry windows, and add tray + hide-to-tray behavior
  • Adds a dedicated preload (desktop/preload-quick-entry.js) exposing close/resize IPC for the overlay
  • Introduces ?mode=quick-add detection and a QuickAddOverlay wrapper to render QuickActions in overlay mode
  • Updates QuickActions to auto-select “New Task”, avoid in-overlay navigation, and notify the main window after task creation via BroadcastChannel
  • Adds a main-window listener (ContentAuth) to refresh after quick-entry task creation, plus i18n + typing support

Technical Notes: Windows run with nodeIntegration: false + contextIsolation: true; external opens are routed through a protocol allowlist; the overlay is frameless/transparent/always-on-top and can be toggled via a global shortcut or tray menu.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 4 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

desktop/main.js Outdated
return { action: 'deny' };
});
mainWindow.webContents.setWindowOpenHandler(({url}) => {
shell.openExternal(url)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shell.openExternal(url) will open any protocol passed in (e.g. file: / javascript:) if a renderer can trigger window.open; consider validating/allowlisting safe protocols before delegating to the OS.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

},
})

quickEntryWindow.loadURL(`http://127.0.0.1:${serverPort}/?mode=quick-add`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The quick-entry window doesn’t set a webContents.setWindowOpenHandler, so window.open/target=_blank behavior may differ from the main window; consider applying the same “deny + safe external open” policy here as well.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

desktop/main.js Outdated
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
globalShortcut.register('CmdOrCtrl+Shift+A', toggleQuickEntry)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

globalShortcut.register(...) can fail (returns false) if the accelerator is already taken; consider handling/logging that outcome so the shortcut doesn’t fail silently.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

…indow open handler to quick-entry, log shortcut registration failure
@kolaente
Copy link
Member

kolaente commented Mar 3, 2026

Things to fix:

  • there is too much space below the input - the window should only expand when the help icon is clicked
  • the input should be focused when the quick add is triggered
  • the help icon is in a new line, it should not do that

kolaente added 5 commits March 3, 2026 23:38
Emit opened/closed events when the help modal is toggled, so parent
components can react (e.g. resize the quick entry window).
…lers

- Move QuickAddMagic inside .action-input so the help icon sits inline
  with the input instead of on a new line
- Focus the input via nextTick when quick-add mode activates
- Add onHelpOpened/onHelpClosed to resize the quick entry window when
  the help modal is toggled
…ocus

- Reduce initial window height from 500px to 120px so it only shows
  the input bar without excess space
- Add quick-entry:resize IPC channel so the frontend can expand the
  window when the help modal opens and collapse it when closed
- Wait for did-finish-load before showing/focusing the window so the
  input gets properly focused on each invocation
The v-focus directive fires when the input mounts, but in the Electron
quick entry window the page loads before the window is shown. This
means focus() is called on a hidden window and has no effect.

Now check document.hasFocus() and if the window isn't focused yet,
defer the focus call to when the window receives focus.
The previous approach using nextTick and window focus events didn't
work because the Modal transition (v-if + Transition appear) and the
Electron window visibility timing both prevent immediate focus. Use
requestAnimationFrame to retry until focus actually lands on the input.
@kolaente
Copy link
Member

kolaente commented Mar 3, 2026

auggie review

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Preview Deployment

Preview deployments for this PR are available at:

URL Tag Commit
https://pr-2340.preview.vikunja.dev ghcr.io/go-vikunja/vikunja:pr-2340 latest
https://sha-3c4804b6d45bff4d6a4987e23081dbdd82e119c4.preview.vikunja.dev ghcr.io/go-vikunja/vikunja:sha-3c4804b6d45bff4d6a4987e23081dbdd82e119c4 3c4804b
https://sha-d909b50989f7579aa23c44f53ed121c4c59ccdff.preview.vikunja.dev ghcr.io/go-vikunja/vikunja:sha-d909b50989f7579aa23c44f53ed121c4c59ccdff d909b50

The preview environment will start automatically on first visit. Subsequent pushes to this PR will update the pr-2340 image — the preview picks up the new version on restart. The per-commit URLs point to a specific version and will not change.

Run locally with Docker
docker pull ghcr.io/go-vikunja/vikunja:pr-2340
docker run -p 3456:3456 ghcr.io/go-vikunja/vikunja:pr-2340

Last updated for commit 3c4804b

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

searchInput.value.focus()
if (document.activeElement === searchInput.value) return
}
requestAnimationFrame(tryFocus)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requestAnimationFrame(tryFocus) loop has no cancellation/stop condition if active flips back to false or the component unmounts before focus lands; this can keep scheduling frames indefinitely. Consider adding a guard/cleanup so the focus retry loop terminates when the overlay closes.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


ipcMain.on('quick-entry:resize', (_event, width, height) => {
if (quickEntryWindow) {
quickEntryWindow.setSize(width, height)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick-entry:resize accepts width/height from the renderer and passes them straight to setSize; if these aren’t finite positive integers (or are extremely large), Electron may throw or behave poorly. Consider validating/clamping the values before applying them.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

@executed
Copy link

executed commented Mar 4, 2026

@kolaente Hey! Thank you for this draft.

  1. I saw following point in test plan:
    "Overlay pre-selects "New Task" command".
    Would be nice to double check if pressing "backspace" clears selection and allows to press arrow down to navigate to other options OR search OR run command.
  2. Closing the tray on Enter makes sense as you don't want to loose focus, but a lot of times you actually want to add description/other fields too.
    What if:
  • after Enter the new box pops-up right below the input field showing just created task title, same logic/CSS as tasks shown in search results. The box is in focus so subsequent Enter opens it fully in TaskDetail view with description field auto-focused for input. Otherwise if user don't want to continue filling task details - pressing Esc closes overlay.
  • next time app interface is opened it could reveal recently created task. This can help if user wanted quick task with title only, pressed Esc instead of Enter (closed overlay), but then decided to go back to add description for example.
  1. Would be also great to expose Electron program argument as alternative to keyboard shortcut. This would allow user to manually register a shortcut with custom name available in Spotlight/Start Menu (OS quick search). Or even better - let user configure the name of such shortcut in app's settings and register such shortcut automatically.
  2. CmdOrCtrl+Shift+A seems to me like a frequently used shortcut - even more so if app is focused - my favorite code editor already uses this combination - to avoid collisions it should be configurable in desktop app settings.

@kolaente
Copy link
Member

kolaente commented Mar 5, 2026

Hey @executed, these are great points.

Closing the tray on Enter makes sense as you don't want to loose focus, but a lot of times you actually want to add description/other fields too.

I think the best way to make that work would be to have it open the task detail view when creating the task by pressing Shift+Enter (instead of just Enter). I do like the search-results idea that you mentioned as well though.

+1 for making the shortcut configurable. It should be possible to do this from Vikunja's settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants