mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
feat(workspace): route platform Back gestures to the header Back (#1943)
* feat(workspace): route platform Back gestures to the header Back The mouse Back button, Alt+Left (Cmd+[ on macOS) and, in Electron, the window's app-command browser-backward (Windows/Linux) and trackpad swipe (macOS) now run the workspace header's Back, one step each. - WorkspaceBackGesturesService (shell-provided) dispatches every gesture through WorkspaceBackNavigationService.goBack(). A gesture is swallowed while a dialog, menu, the phone drawer or a fullscreen player is up, and left to the browser only where the header has no Back. - The chord is a navigate-back entry in the shortcuts dialog, handled in a capture-phase listener of WorkspaceKeyboardShortcutsService; ignored while typing, repeats swallowed. - Electron forwards app-command and swipe over the push-only channel WINDOW:BACK_REQUESTED (preload onWindowBackRequested), gated in the renderer on RuntimeCapabilitiesService.supportsWindowBackGestures. - A mouse press that also arrives as app-command goes back once, however long the button is held; Electron's own Chromium history step on the mouse button is always prevented. - No Forward binding. Docs: workspace-shell (Platform gestures), portal-detail-navigation, electron-security, UI guidelines, README shortcuts table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(workspace): drop NgZone from the Back gestures service Master runs zoneless change detection and no longer uses NgZone; the bridge callback calls goBack() directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(workspace): keep Back gestures where the Fullscreen API is missing iPhone Safari has no unprefixed document.fullscreenElement; the undefined value read as "a player is fullscreen" and blocked every gesture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(workspace): keep a held Back chord's repeats swallowed after Back Repeats asked for the current result, so once the first press reached a page with no Back left they were released to the browser, which could take another history step. Repeats now follow the chord's first press until a key is released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(electron): one Back per press of the Linux Browser Back key On Linux Electron raises app-command browser-backward for every unhandled event of the Browser Back key (NativeWindowViews:: HandleKeyboardEvent), keydown and keyup alike, so one press went back twice. The main process now consumes that key in before-input-event and requests Back once per press; mouse Back buttons still arrive as app-command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
44 files changed
+1758
-8
No files matched your search
@@ -97,8 +97,9 @@ The shell is intentionally split into four persistent regions:
|
||||
or Sources when the dashboard is off).
|
||||
2. Top header:
|
||||
1. Leading Back slot: the current page's registered Back, else browser
|
||||
history while an in-app previous page exists, else nothing. See
|
||||
[Header Back](#header-back).
|
||||
history while an in-app previous page exists, else nothing. The mouse
|
||||
Back button, Alt+← / Cmd+[ and the Electron window gestures run it.
|
||||
See [Header Back](#header-back).
|
||||
2. Playlist switcher.
|
||||
3. Route-aware search input and command palette trigger.
|
||||
4. Add source action.
|
||||
@@ -319,13 +320,80 @@ of jumping (see [Layout slides](#layout-slides)).
|
||||
There is no Forward button: Stalker inline details are store state, not
|
||||
history entries, so Forward would skip them.
|
||||
|
||||
**Platform gestures.** The mouse Back button, Alt+← (Cmd+[ on macOS) and, in
|
||||
Electron, the window's `app-command` `browser-backward` (Windows and Linux:
|
||||
mouse Back buttons and the Browser Back key) and trackpad `swipe` left (macOS)
|
||||
run the same action as the header button, through
|
||||
`WorkspaceBackGesturesService` (shell-provided). Each gesture has one of
|
||||
three results (`WorkspaceBackGestureResult`):
|
||||
|
||||
- `back`: the header has a target and nothing sits above the page, so
|
||||
`WorkspaceBackNavigationService.goBack()` runs. "Above the page" is document
|
||||
fullscreen (a fullscreen player), an open phone context drawer, a showing
|
||||
CDK backdrop, or a visible `[role="dialog"]`, `[role="alertdialog"]` or
|
||||
`[role="menu"]` outside hidden, `inert` or `aria-hidden` subtrees.
|
||||
- `blocked`: something sits above the page. Nothing navigates, and the
|
||||
gesture is still swallowed, because browser Back would navigate under the
|
||||
dialog (which then closes) or out of the fullscreen player.
|
||||
- `none`: the header has no Back. The gesture stays the browser's: in the PWA
|
||||
it may leave the app, as browser Back does.
|
||||
|
||||
Unless the result is `none`, the browser's default is prevented (the mouse
|
||||
press on `mouseup` and `auxclick`, listened to in the capture phase), so the
|
||||
browser cannot take a second history step. In Electron the mouse Back button
|
||||
is prevented even then: Electron's Chromium steps through history on an
|
||||
unprevented press itself, also to entries from before a reload, which reloads
|
||||
the app.
|
||||
|
||||
- **Chord.** `navigate-back` in `keyboard-shortcut-definitions.ts` lists it in
|
||||
the shortcuts dialog; `isWorkspaceBackShortcut()` matches it, on macOS also
|
||||
by the `BracketLeft` key position for layouts that type `[` with Option.
|
||||
`WorkspaceKeyboardShortcutsService` handles it in a capture-phase document
|
||||
listener and stops a chord that runs Back there, so a focused carousel, tab
|
||||
list, guide or player does not also read it as a plain arrow; a blocked
|
||||
chord is prevented but still reaches the dialog or menu. It is ignored
|
||||
while typing (Alt and Option arrows edit text). A held chord runs Back
|
||||
once: its repeats follow its first press until a key is released, so after
|
||||
a press that ran Back they are swallowed even when the page it reached has
|
||||
no Back left, and the browser cannot step on them.
|
||||
- **Electron.** `attachWindowBackGestures()`
|
||||
(`apps/electron-backend/src/app/services/window-back-gestures.ts`) forwards
|
||||
the two window events over the main → renderer push channel
|
||||
`WINDOW:BACK_REQUESTED` with the source (`app-command` or `swipe`); the
|
||||
preload's `onWindowBackRequested` passes only those two values on. The
|
||||
renderer subscribes only when
|
||||
`RuntimeCapabilitiesService.supportsWindowBackGestures` reports the
|
||||
subscription, so an older preload forwards nothing. `browser-forward` and
|
||||
`swipe` right are not forwarded. `swipe` needs macOS's "Swipe between
|
||||
pages" set to two or three fingers. On Linux Electron raises `app-command`
|
||||
for the keydown and the keyup of the Browser Back key alike, so the main
|
||||
process takes that key in `before-input-event` instead (Back once per
|
||||
press, the key never reaches the page) and leaves `app-command` to mouse
|
||||
Back buttons.
|
||||
- **One press, two events.** In Electron on Windows and Linux a mouse Back
|
||||
press also reaches the window as `app-command`: with the release on
|
||||
Windows, on press on Linux, and before or after the renderer sees the
|
||||
button. Whichever half comes first runs Back and the other is consumed. A
|
||||
command that arrives while the button is down belongs to that press however
|
||||
long it is held; otherwise a command and a press (or a press and a command)
|
||||
within `BACK_PRESS_TWIN_MS` (500 ms) of each other pair. Each press pairs
|
||||
once, so two quick presses still go back twice, and the chord and the swipe
|
||||
never pair.
|
||||
|
||||
Electron E2E `back-gestures.e2e.ts` drives each gesture (the mouse through
|
||||
CDP's `back` button, the window events emitted on the `BrowserWindow`) and
|
||||
checks that each goes back one history entry, a paired press goes back once
|
||||
(also when held), Forward does nothing, and the gestures wait while typing
|
||||
(the chord) or with a dialog open. Web E2E `workspace-header-back.e2e.ts`
|
||||
checks that the mouse Back button and the chord step once in the browser.
|
||||
|
||||
**Phone width.** `phoneDrawerToggle` sets how Back shares the leading slot with
|
||||
the context drawer toggle. `replace` (the default) takes the toggle's slot: a
|
||||
detail page's drawer belongs to the list that Back returns to. `beside` keeps
|
||||
both: the settings drawer holds the page's own sections. `yield` hides Back
|
||||
while the toggle shows: the history fallback must not cost a category list its
|
||||
only way into the drawer, and two navigation icons do not fit beside the
|
||||
switcher. System and browser Back still work there.
|
||||
switcher. The platform gestures above and browser Back still work there.
|
||||
|
||||
**Left in place.** These controls stay inside their surface on purpose:
|
||||
|
||||
@@ -517,7 +585,8 @@ Keyboard shortcut help is shell-owned:
|
||||
|
||||
1. `WorkspaceKeyboardShortcutsService` is provided by `WorkspaceShellComponent`.
|
||||
It owns the workspace-scoped `document:keydown` listener for `?` /
|
||||
`Shift+/`.
|
||||
`Shift+/`, and a capture-phase one for the Back chord (see
|
||||
[Header Back](#header-back)).
|
||||
2. The listener ignores events from inputs, textareas, selects, and
|
||||
content-editable elements via `isTypingInInput(...)`.
|
||||
3. `libs/portal/shared/util/src/lib/keyboard-shortcut-definitions.ts` is the
|
||||
|
||||
Reference in new issue
Block a user