Commit graph

20 commits

Author SHA1 Message Date
Johannes Millan
95d3b212bc
fix(electron): gate Jira IPC behind a one-shot capability (#9008)
* docs(plugins): add microsoft 365 calendar provider plan

* fix(jira): gate Electron requests behind one-shot capability

Claim privileged Jira IPC before plugin startup and return responses through invoke instead of a broadcast event. Keep arbitrary HTTP(S) Jira hosts supported while rejecting redirects and bounding request resources.

* fix(jira): enforce Electron request capability

Bind privileged Jira IPC to a main-issued renderer-document token and strip raw Electron events from renderer callbacks. Scope image authentication by origin, base path, and resource type while preserving safe redirects and legacy configurations.

* fix(electron): handle payload-only IPC lifecycle

Clear Jira image authentication before replacement and when a new renderer document claims the capability. Parse before-close IDs from payload-only events so pending sync and finish-day hooks can complete.

* fix(electron): address Jira IPC capability review findings

- electron.effects: read ANY_FILE_DOWNLOADED payload at [0] after the
  payload-only IPC refactor (was [1], now undefined -> TypeError on every
  download); guard against a malformed payload
- jira-capability: rotate the token on re-register so a renderer reload
  that reuses the WebFrameMain object is not permanently locked out of
  Jira; invalidates any stale token
- document that the one-shot consumption order, not the bypassable
  main-frame check, is the real capability boundary
- jira-electron-bridge: skip the no-op clearImgHeaders IPC round-trip
  when image auth was never set up (non-Jira detail-panel open/close)
- jira-api: route a synchronous _toElectronRequestInit throw through
  _handleResponse instead of leaking a dangling request-log entry

* test(electron): cover ANY_FILE_DOWNLOADED payload parsing

Extract parseDownloadedFilePayload from ElectronEffects and add a
regression spec pinning the payload-only shape ([file], not [event,
file]) that caused a TypeError on every download. Hardened against
non-array input surfaced by the new test.

* fix(electron): restore Node ambient globals for frontend build
2026-07-14 21:02:27 +02:00
Johannes Millan
4e0e2d9ffd
fix(electron): open file:// URLs with special characters on Windows (#8695) (#8698)
* fix(electron): open file:// URLs with special characters (#8695)

Local file:// URLs with non-ASCII names or spaces (e.g. Grüne, "Another
One") failed to open on Windows: both open sinks handed the URL to
shell.openExternal, which percent-encodes the path (ü → %C3%BC, space →
%20) before ShellExecute, so the OS then searched for a literally-named
folder and failed. Pure-ASCII paths worked because there was nothing to
encode.

Route local file: URLs through shell.openPath with a decoded filesystem
path (fileURLToPath) instead. openPath takes a raw path, so Unicode names
and spaces open correctly. This also applies the executable-extension
guard to file: URLs, which previously only ran on the OPEN_PATH sink
(GHSA-hr87-735w-hfq3).

Shared openLocalPath()/isLocalFileUrl() cover both the OPEN_EXTERNAL IPC
handler and the navigation-interception path in main-window.

* test(electron): cover NTLM/exec vectors at the un-pre-gated openPath sink

Follow-up to the #8695 review. Add regression tests locking in that
openLocalPath alone (OPEN_PATH has no scheme pre-gate) blocks the
path-based UNC file: URL (file:////host/share, GHSA-hr87-735w-hfq3) and
an executable hidden behind a percent-encoded dot (evil%2Ebat). Document
that the decode tests run on Linux and therefore don't exercise the
Windows drive-letter/backslash conversion, and clarify why isLocalFileUrl
is intentionally broader than the renderer/scheme-allowlist checks.
2026-07-02 11:47:51 +02:00
Johannes Millan
ad7564bad2
fix(electron): block executable launch via shell.openPath (#8668)
* fix(electron): block executable launch via shell.openPath

window.ea.openPath is reachable by any plugin, same-origin iframe plugin, or
renderer XSS, and its guard only blocked UNC/remote paths (NTLM leak,
GHSA-hr87). shell.openPath hands a local file to the OS handler, and on
Windows ShellExecute runs .bat/.cmd/.vbs/.hta/... from a plain file with no
exec bit. Chained with a writable-dir drop (e.g. FILE_SYNC_SAVE into the
local-file sync folder) or a malicious synced FILE attachment clicked by the
user, that is renderer -> native code execution bypassing the nodeExecution
consent gate.

Add hasExecutableFileExtension() (normalizes Windows trailing dots/spaces and
NTFS alternate data streams) and refuse those paths at the openPath sink.
Applied at the sink, not in the shared isPathSafeToOpen, so link/image
rendering (where a remote https://.../x.exe is legitimate) is unaffected.

* fix(electron): close # filename bypass in openPath executable gate

The executable-extension check split every path on `?`/`#` to drop a URL
query/fragment, but `#` is a legal Windows/NTFS filename char (and both `#`
and `?` are legal on POSIX). For a bare filesystem path that split truncated
the real filename: `C:\sync\evil.txt#.bat` was read as `.txt` and allowed,
yet ShellExecute runs it as `.bat` — defeating the gate via the same
drop-a-file-then-openPath vector the fix targets. Gate the query/fragment
split behind an actual `file:` scheme so real filenames keep their extension.

Also add well-known ShellExecute/launcher vectors the curated denylist
missed: settingcontent-ms, appref-ms, library-ms, wsc, chm, hlp, diagcab,
msix/appx family (Windows) and pkg, terminal, fileloc, inetloc (macOS).

Adds regression tests for both.

* fix(electron): require file:// (not bare file:) before query/fragment split

The executable-extension gate stripped a URL query/fragment for any string
starting with `file:`. On POSIX `#`, `?` and `:` are all legal filename
chars, so a file whose name merely starts with the literal `file:` (e.g.
`file:notes.txt#.sh`) was mis-parsed as a URL and its real `.sh`/`.desktop`
extension hidden. Require the `//` of an actual `file://` URL so only genuine
URLs get the query/fragment split; bare paths keep their real extension.
2026-07-01 17:56:24 +02:00
Johannes Millan
97e97042cd
fix(electron): remove exec IPC to close GHSA-256q (#8669)
* fix(electron): fail-safe exec confirmation dialog (GHSA-256q)

The EXEC confirmation was the only gate before an arbitrary shell command
runs with the user's privileges, but it did not fail safe:

- defaultId: 2 was out of range for a two-button dialog, leaving the
  focused default per-platform-undefined, so an accidental Enter could
  execute. Cancel is now defaultId + cancelId (Enter/Escape never runs).
- 'Remember my answer' defaulted to checked, so one careless click could
  whitelist a command to the silent allow-list forever. It is now opt-in.

Add electron/ipc-handlers/exec.test.cjs as a regression guard.

* docs(plugins): correct misleading plugin sandboxing claims

Docs claimed JS plugins run in 'isolated VM contexts' and iframes run
'without allow-same-origin' — both are false. JS plugins run in the host
renderer via new Function, and iframes use allow-same-origin (required for
#8467), so both can reach the privileged window.ea bridge. Align the docs
with the code (plugin-iframe.util.ts) and stress the trust model.

* fix(electron): fail closed on corrupt exec allow-list, cover error paths

The EXEC handler is wired to ipcMain.on (fire-and-forget), so a throw on a
corrupt allow-list surfaced as an unhandled promise rejection with no user
feedback. Wrap the handler body in try/catch and route failures through
errorHandlerWithFrontendInform (the same channel exec errors already use),
failing closed so a corrupt store never falls through to executing.

Expand the regression tests: assert the corrupt-config path informs the error
and runs nothing, cover exec-error routing, and guard allow-list append (an
overwrite regression that wipes remembered commands previously passed green).

* docs(plugins): document window.ea.exec shell path in trust model

The trust-model section implied executeNodeScript() was the only process
path; on desktop window.ea.exec also runs arbitrary shell commands via
child_process.exec behind a separate, weaker gate (confirmation dialog +
persistent allow-list, not the nodeExecution consent).

* test(electron): run exec security regression tests in CI

The test:electron runner globs electron/*.test.cjs (non-recursive), so the
guard at electron/ipc-handlers/exec.test.cjs was never executed by CI —
verified: the suite went 160 -> 168 tests once discovered. Move it to
electron/exec.test.cjs (matching every sibling electron test) and point
execModulePath at ipc-handlers/exec.ts.

* refactor(electron): narrow exec allow-list without an unsafe cast

Drop the `as string[]` cast that asserted away the very corruption the
Array.isArray guard is meant to catch; narrow the unknown value honestly so
the guard is a real type-check. Behavior is unchanged (falsy -> empty list,
truthy non-array -> fail closed).

* fix(electron): stop logging exec command content

Per CLAUDE.md rule 9 (log history is exportable, never log user content), a
command can carry a secret in its arguments; log a content-free line instead.
Also fold the duplicated exec-spawn into a single runCommand() helper so the
allow-listed and just-confirmed paths share one audited call site.

* fix(electron): remove exec IPC to close GHSA-256q at the root

window.ea.exec exposed arbitrary shell (child_process.exec) to the whole
renderer: JS plugins (new Function in the host realm), same-origin iframe
plugins (window.parent.ea), and any renderer XSS — bypassing the per-plugin
nodeExecution consent gate entirely. Its only consumer, COMMAND task
attachments, is dormant: no UI creates them (the edit dialog offers only
LINK/IMG/FILE).

Rather than guard a dormant RCE primitive, remove it: delete the EXEC IPC
handler + preload.exec + the ElectronAPI method + the directive's COMMAND
branch. This closes the vector for plugins, iframes, AND host-realm XSS at
once (a bootstrap handoff would only hide it from plugins), and supersedes
the earlier dialog hardening (that primitive no longer exists).

Keep the 'COMMAND' literal in the synced TaskAttachment type (removing a
synced union member breaks typia validation on peers/legacy data); a click
on a legacy COMMAND attachment now shows an informational snack instead of
executing.

* docs(plugins): reflect exec IPC removal in the trust model

window.ea.exec no longer exists (removed to close GHSA-256q); the docs now
state executeNodeScript is the only sanctioned native-code path.

* test(electron): guard exec IPC stays removed (GHSA-256q)

The interim exec security tests were deleted together with the executor, so the
shipped fix had no regression coverage. Add electron/exec.test.cjs (picked up by
the electron/*.test.cjs CI glob) asserting the bare exec primitive stays gone:
no IPC.EXEC event, no exec.ts handler, no preload exec bridge, no ElectronAPI.exec
method, no initExecIpc wiring. Targets only the removed surface, not the
sanctioned PLUGIN_EXEC_NODE_SCRIPT/executeScript nodeExecution path.

* chore(electron): mark ALLOWED_COMMANDS store key as legacy

Its only reader/writer was the deleted exec handler (GHSA-256q). Document that it
is retained purely so older persisted stores keep loading.
2026-07-01 17:55:35 +02:00
Johannes Millan
14a56f861e
feat(electron): trigger global shortcuts via superproductivity:// URLs (#8645)
* feat(electron): trigger global shortcuts via superproductivity:// URLs

On Wayland the compositor owns global hotkeys, so Electron's globalShortcut
often does not register. Add three actions — toggle-visibility, new-note and
new-task — to the existing protocol handler so a compositor keybind can call
`xdg-open superproductivity://<action>`. No extra CLI tool or runtime is
needed: xdg-open (Linux), open (macOS) and start (Windows) already ship with
the OS, and the running instance receives the URL via the existing
single-instance / second-instance path.

Extract the show/hide logic into a shared toggleWindowVisibility() used by both
the globalShowHide shortcut and the new protocol action, and add a key-repeat
debounce so one held key press no longer hides then immediately re-shows the
window.

Docs: add a Wayland keybind recipe (Niri/sway/Hyprland) to the keyboard
shortcuts wiki page.

Refs #7114

* fix(electron): correct toggle-visibility focus race and debounce

On Linux/Windows the second-instance handler pre-focused the window before processProtocolUrl ran, so toggle-visibility always read 'visible' and hid the window the user asked to show. Skip that pre-focus for toggle-visibility only (new getProtocolAction helper); every other action keeps the bring-to-front behavior.

Make the key-repeat debounce direction-agnostic with a sliding quiet-gap (1000->750ms): it now guards both show and hide, settles a held key on a single toggle, and fires on the xdg-open path where the old isHidden-only guard was always false.

Stop logging the create-task title and URL path to the exportable log (CLAUDE.md rule 9 / privacy).

Add coverage for the real second-instance path, held-key-from-hidden, gap expiry, the #7282 minimize fallback, the macOS hide path, unfocused-show, and the log redaction; document AppImage/Flatpak/Snap scheme registration. Refs #7114.

* fix(electron): show window on cold-start toggle-visibility launch

Cold start: when superproductivity://toggle-visibility launches the app (it wasn't running), the freshly-shown window was immediately hidden again because the toggle saw it focused. Flag the cold-start URL during the argv scan and SHOW — never toggle — the window once it's ready (via processPendingProtocolUrls), respecting start-minimized-to-tray. The already-running second-instance path keeps real toggle behavior.

Rename the two interactive protocol actions new-task/new-note to add-task/add-note: aligns with the app's Add-Task vocabulary and the globalAddTask/globalAddNote keys, and avoids colliding with the programmatic create-task/<title>. The action names are a frozen public contract once users bind them in compositor configs, so this is the pre-merge moment to settle the naming. Refs #7114.
2026-06-30 17:22:30 +02:00
Maikel Hajiabadi
ec16757c82
fix(keyboard): resolve macOS global shortcut layout mismatch (#8378) (#8381)
* fix(keyboard): resolve macOS global shortcut layout mismatch (#8378)

* fix(keyboard-layout): log layout-detection failure and resolve layoutReady with map copy

* fix(keyboard-shortcut): remove debug console logs and add macOS scope comment

* fix(keyboard-shortcut): preserve modifier separator when mapping plus key shortcut

* refactor(keyboard-shortcut): export mapping helpers and avoid as any cast in configuration mapping

* test(keyboard-shortcut): add unit tests for layout shortcut translation logic

* test(keyboard-shortcut): use correct KeyboardConfig type instead of as any in test fixture

* docs(keyboard-shortcut): hoist macOS physical shortcut layout comments to helper JSDoc

* refactor(config): extract global shortcut keys and mapping helpers

* test: add test helper capability for IS_ELECTRON and IS_MAC

* test: add comprehensive integration unit tests for global shortcut effects

* feat(electron): eagerly trigger layout detection on Electron startup for macOS timing fix

* refactor(config): InjectionToken migration, layout detection hardening, and startup optimization

* refactor: address non-blocking suggestions for layout detection and DI tokens

* build(electron): move keyboard-config.model to shared-with-frontend to fix ASAR require

* fix(client-id): increase entropy to 6 chars and fix related test regressions
2026-06-17 12:57:47 +02:00
Johannes Millan
cd86ba2c0d
fix(electron): validate URL scheme before shell.openExternal/openPath (GHSA-hr87-735w-hfq3) (#8210)
* fix(electron): validate URL scheme before shell.openExternal (GHSA-hr87-735w-hfq3)

Task-note Markdown links had their href passed verbatim to shell.openExternal
on click, letting note content (sync, imported backups, issue-provider data)
silently invoke any OS-registered protocol handler — ms-msdt: (Follina),
search-ms:, \\host\share (NTLM hash capture), etc. — with one click and no
prompt. Angular's sanitizer does not help: its SAFE_URL_PATTERN blocks only
javascript:, so these schemes pass.

Add a shared scheme allowlist (http/https/mailto/file) enforced at both layers:

- marked renderer (marked-options-factory): blocked-scheme links render as
  inert <span> text instead of an anchor (covers all note render paths + web).
- Electron main process, both shell.openExternal sinks: the OPEN_EXTERNAL IPC
  handler (system.ts) and openUrlInBrowser (main-window.ts, the
  will-navigate/window-open path used by the fullscreen/archived dialogs).

The allowlist lives in electron/shared-with-frontend so renderer and main agree
on one policy. Blocked links are styled muted/non-interactive with an
explanatory tooltip.

* fix(electron): harden openExternal/openPath against SMB & attribute-injection (GHSA-hr87-735w-hfq3)

Follow-up hardening from multi-agent review of the initial fix:

- file:// SMB bypass: `file:` was allow-listed unconditionally, so
  `file://host/share` / `file:////host` reached shell.openExternal — the same
  NTLM-leak vector the raw `\\host\share` check blocked. Restrict `file:` to the
  canonical local form `file:///<path>`. Gated on the raw string because
  Chromium (renderer) and Node (main) parse file: hosts/paths differently.
- IPC.OPEN_PATH was unguarded: a synced FILE-attachment path of `\\host\share`
  leaked NTLM via shell.openPath. Reject UNC paths (new isUncPath helper).
- Attribute-injection XSS: the marked link renderer interpolated href/title
  raw; marked's angle-bracket link form could inject an event handler that runs
  under disableSanitizer. HTML-escape href/title.
- Remove the now-dead/misleading explicit backslash check and redundant
  toLowerCase (new URL already throws on raw UNC; protocol is spec-lowercase).

Adds cross-engine-verified tests for file:// SMB forms, isUncPath, and the
href quote-injection case.

* fix(electron): block remote file:// in OPEN_PATH too (GHSA-hr87-735w-hfq3)

Re-review found the OPEN_PATH guard was incomplete: isUncPath only rejects raw
slash-prefixed paths, but shell.openPath('file://host/share') resolves to
\\host\share on Windows — the same NTLM leak. A synced FILE-attachment path is
attacker-controllable.

Centralize the path policy in isPathSafeToOpen() (rejects UNC paths and remote
file:// URLs, allows local paths and file:///) so the two sinks can't drift.

* fix(electron): gate image src against remote file:// / UNC (GHSA-hr87-735w-hfq3)

Markdown image src auto-loads on render (no click), so a remote
file://host/share or UNC src silently triggers an SMB connection and
leaks the user's NTLM hash just by viewing a synced/imported note — the
same vector the link/openExternal gate already blocks, but with no user
interaction. CSP (img-src ... file:) does not block it.

Gate image href through isPathSafeToOpen: remote file:// and UNC srcs
render as inert (HTML-escaped) alt text instead of an <img>; local
file://, http(s), data: and blob: images are unaffected.

Also drop a dead eslint-disable directive on ALLOWED_EXTERNAL_URL_SCHEMES
(the name does not trip naming-convention).

* fix(tasks): gate IMG attachment src against remote file:// / UNC (GHSA-hr87-735w-hfq3)

An IMG-type task attachment renders <img [src]="resolvedOriginalPath">,
which auto-loads on render with no click. A synced/imported remote
file://host or UNC path there silently triggers an SMB fetch and leaks
the user's NTLM hash just by opening the task — the same no-click vector
the markdown-image gate already blocks, but via Angular template binding
(Angular's URL sanitizer does not help; SAFE_URL_PATTERN allows file:).

Drop unsafe srcs via isPathSafeToOpen so they never reach the binding;
local file://, http(s), data: and blob: images are unaffected.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-09 17:29:18 +02:00
Symon Baikov
e0be3a1a16
Feat/task widget global shortcut (#7099)
* feat(electron): add global shortcut for task widget toggle

Adds a configurable global shortcut (`globalToggleTaskWidget`) that
shows/hides the task widget without changing the persisted
enabled/disabled preference. The shortcut is a no-op while the task
widget feature is disabled.

When the user reveals the widget via the shortcut while the main window
is visible, a sticky "user-forced visible" flag keeps it up (like
always-show) instead of letting the next focus/show event immediately
hide it. The flag clears when the user toggles the widget off, opens the
app from the widget, or the feature is disabled.

Rebased onto master to drop the now-superseded tray-indicator refactor;
the only main-window.ts change is the additive user-forced-visible gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(electron): handle task widget shortcut races

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 16:11:16 +02:00
felix bear
376e068412
fix(electron): respect tray title settings #7823 (#8097)
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
2026-06-08 12:07:09 +02:00
Johannes Millan
e7454ea6b9 fix(window): preserve maximized state for shortcut hide 2026-05-11 14:31:19 +02:00
Johannes Millan
297aeb30ab fix(electron): harden Snap+Wayland argv wrapper after multi-review
Address findings from code-reviewer + debugger-assistant +
refactor-specialist multi-review on 8e1c47ebc2:

- afterPack: read wrapper source before rename; on writeFile failure,
  roll the rename back so a broken intermediate state can't ship a
  package with no launcher. Strengthen idempotency check to require a
  shebang at binPath (both files alone no longer counts as "installed").
- wrapper: gate the X11 injection on \$SNAP_NAME = "superproductivity",
  not just \$SNAP set. An xdg-open from a sibling snap (e.g. Firefox)
  leaks \$SNAP into the child env, which previously would have silently
  forced XWayland on .deb/.rpm users. Derive BIN_DIR from \$SNAP
  directly when we are our snap, avoiding fragile \$0 resolution
  through snapd's wrapper chain. Stop argv scanning at -- so positional
  args that resemble --ozone-platform aren't misread as a user override.
- app-control: point app.relaunch() execPath at the sibling shell
  wrapper instead of the default process.execPath (which is the renamed
  ELF and would bypass the flag injection on a relaunched Snap+Wayland
  instance).
- Move build/snap-wrapper.sh -> build/linux/snap-wrapper.sh to match
  the existing build/linux/ layout convention.
- Update research doc §18 to reflect the tightened predicate and the
  relaunch fix.
2026-04-21 15:03:42 +02:00
Johannes Millan
2cefde61bf fix(electron): keep window reachable when show/hide shortcut has no tray
The globalShowHide shortcut called mainWin.hide() unconditionally. On
Windows/Linux hide() removes the taskbar entry, so with no tray icon
the window became unreachable.

- macOS: use hide() (dock icon always remains).
- Windows/Linux: hide to tray only when minimize-to-tray is on AND the
  tray was created; otherwise minimize() to keep a taskbar handle.
- Log whether Windows tray creation used the GUID path, to help diagnose
  cases where the icon lands in the hidden overflow area.

Refs #7282
2026-04-20 15:48:40 +02:00
Onur Neşvat
959004ee18
feat(electron): add Local REST API for desktop automation (#6981)
* feat(electron): add Local REST API for desktop automation

Add a local HTTP server (port 3876) that allows external scripts and
tools to interact with a running Super Productivity desktop app.

Features:
- Task CRUD operations (create, read, update, delete)
- Task control (start, stop, set current)
- Task archive/restore operations
- Query filters for tasks (by title, project, tag, done status)
- Read-only project and tag listing

The API is disabled by default and can be enabled in Settings > Misc.
Only accepts connections from localhost (127.0.0.1) for security.

* fix: resolve lint errors for local REST API

* fix: use lazy injection for LocalRestApiHandlerService

Fixes StartupService tests by using Injector.get() instead of direct
injection, preventing LocalRestApiHandlerService from being instantiated
when StartupService tests don't have NgRx providers.

* fix: resolve markdown lint errors in API docs

* fix: add security warning to Local REST API hint
2026-03-28 13:35:04 +01:00
Johannes Millan
6ca7223dc6 refactor: add markdown renderer tests and remove dead IPC code
- Add 18 unit tests for marked-options-factory covering:
  - Image renderer (sizing syntax, lazy loading, dimensions)
  - Paragraph renderer (headings h1./h2. syntax)
  - parseImageDimensionsFromTitle helper
  - preprocessMarkdown hook
- Export parseImageDimensionsFromTitle and preprocessMarkdown for testability
- Remove unused IPC.TOGGLE_FULLSCREEN event and handler
  (F11 fullscreen is handled directly in main-window.ts)
2026-01-26 20:55:29 +01:00
Benjamin Hooper
5a2b675383
feat(ui): Desktop Full Screen Mode (F11) #5851 (#6183) 2026-01-26 13:22:32 +01:00
Johannes Millan
e19d3fb593 fix(electron): minimize to tray not working immediately after enabling
Closes #5622

The 'Minimize to tray' setting was not updating the main process shared state when changed in the settings, requiring an app restart to take effect. This was because the 'UPDATE_SETTINGS' IPC event (sent on change) was not updating the 'isMinimizeToTray' flag. This commit ensures that 'UPDATE_SETTINGS' updates the relevant shared state.
2025-12-04 11:50:27 +01:00
Johannes Millan
c5625de6f1 feat(customWindowTitleBar): add sexy custom window title bar <3 2025-12-02 13:30:36 +01:00
Johannes Millan
5bd59e2760 Merge remote-tracking branch 'origin/master'
* origin/master:
  feat(i18n): add export task list string to localization files
  feat(ipc): improve error handling and streamline IPC initialization
  feat(startup): refactor startup logic and move initialization to StartupService
  feat(ipc): implement IPC handlers for app control, data, exec, global shortcuts, system, and Jira
  feat: add placeholder text support to daily summary and inline markdown components
  feat: don't install api test plugin
  chore(deps): bump the npm_and_yarn group across 4 directories with 1 update

# Conflicts:
#	electron/ipc-handler.ts
#	src/app/app.component.ts
2025-11-28 20:28:46 +01:00
Johannes Millan
f704eebc7e feat(ipc): improve error handling and streamline IPC initialization 2025-11-27 15:06:26 +01:00
Johannes Millan
653ae62dbf feat(ipc): implement IPC handlers for app control, data, exec, global shortcuts, system, and Jira 2025-11-27 13:44:18 +01:00