Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 59 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Glimpse supports several window style flags that can be combined freely:
| `floating` | Always on top of other windows |
| `transparent` | Clear window background — HTML body needs `background: transparent` |
| `clickThrough` | Window ignores all mouse events |
| `kiosk` | Open borderless on the active display and request shortcut inhibition while the window is open |
| `noDock` | No dock icon (macOS) — window works normally but the app doesn't appear in the dock or app switcher |

Common combinations:
Expand All @@ -77,6 +78,25 @@ Common combinations:
- **Custom dialog**: `frameless: true` — clean UI with no system chrome
- **Overlay**: `frameless + transparent` — shaped widgets that float over content
- **Companion widget**: `frameless + transparent + floating + clickThrough` — visual-only overlays that don't interfere with the desktop
- **Kiosk prompt**: `kiosk: true` — fill the active display, keep focus, and ask the OS to route common system shortcuts to the window

## Kiosk Mode

`kiosk: true` opens a borderless window on the active display, sizes it to that display, focuses the WebView for normal typing, and asks the operating system or compositor to inhibit common window-switching shortcuts while the window is open.

```js
const win = open('<input autofocus placeholder="Stay here" />', {
kiosk: true
});

win.on('kiosk', ({ active, reason }) => {
console.log(active, reason);
});
```

Kiosk mode is explicit and best-effort. `active: true` means the backend installed its shortcut-control mechanism; `active: false` means kiosk was disabled, deferred, or unavailable in that backend. macOS uses the screen containing the pointer plus AppKit presentation options. Linux native uses the pointer's monitor, an active keyboard grab on X11, and layer-shell keyboard exclusivity plus GDK shortcut inhibition on Wayland where the compositor supports it. Windows uses the screen containing the pointer plus a low-level keyboard hook for common switch-away shortcuts. The Linux Chromium fallback passes Chromium's `--kiosk` flag and may open fullscreen, but cannot reliably inhibit OS-level shortcuts and reports `active: false`.

When `kiosk` is set, Glimpse treats it as a screen-owning mode. The Node wrapper ignores `width`, `height`, `x`, `y`, `followCursor`, `followMode`, `cursorAnchor`, and `cursorOffset`, and emits a `GLIMPSE_KIOSK_IGNORED_OPTIONS` warning if any are provided.

## Follow Cursor

Expand Down Expand Up @@ -106,7 +126,7 @@ const win = open(`

The window tracks the cursor in real-time across all screens. `followCursor` implies `floating` — the window stays on top automatically.

**Platform support:** Follow cursor works on macOS and Windows. On Linux with the native backend, it requires Hyprland (via IPC socket). The Chromium CDP backend also supports X11 (via `xdotool`). Other Wayland compositors without the Chromium backend will emit a warning and silently ignore `followCursor`.
**Platform support:** Follow cursor works on macOS and Windows. On Linux with the native backend, it requires Hyprland (via IPC socket). Runtime `win.followCursor(true)` on Linux native also requires the window to have been launched with a layer-shell mode such as `followCursor`, `floating`, `clickThrough`, `kiosk`, or an explicit position. The Chromium CDP backend also supports X11 (via `xdotool`). Other Wayland compositors without the Chromium backend will emit a warning and silently ignore `followCursor`.

You can also toggle tracking dynamically after the window is open:

Expand Down Expand Up @@ -186,19 +206,20 @@ const win = open('<html>...</html>', {

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `width` | number | `800` | Window width in pixels |
| `height` | number | `600` | Window height in pixels |
| `width` | number | `800` | Window width in pixels (ignored with `kiosk`) |
| `height` | number | `600` | Window height in pixels (ignored with `kiosk`) |
| `title` | string | `"Glimpse"` | Title bar text (ignored when frameless) |
| `x` | number | — | Horizontal screen position (omit to center) |
| `y` | number | — | Vertical screen position (omit to center) |
| `x` | number | — | Horizontal screen position (omit to center; ignored with `kiosk`) |
| `y` | number | — | Vertical screen position (omit to center; ignored with `kiosk`) |
| `frameless` | boolean | `false` | Remove the title bar |
| `floating` | boolean | `false` | Always on top of other windows |
| `transparent` | boolean | `false` | Transparent window background |
| `clickThrough` | boolean | `false` | Window ignores all mouse events |
| `followCursor` | boolean | `false` | Track cursor position in real-time |
| `followMode` | string | `"snap"` | Follow animation: `snap` (instant) or `spring` (elastic with overshoot) |
| `cursorAnchor` | string | — | Snap point around cursor: `top-left`, `top-right`, `right`, `bottom-right`, `bottom-left`, `left` |
| `cursorOffset` | `{ x?, y? }` | `{ x: 20, y: -20 }` | Pixel offset from cursor (or fine-tuning on top of `cursorAnchor`) |
| `kiosk` | boolean | `false` | Fill the active display and request system shortcut inhibition |
| `followCursor` | boolean | `false` | Track cursor position in real-time (ignored with `kiosk`) |
| `followMode` | string | `"snap"` | Follow animation: `snap` (instant) or `spring` (elastic with overshoot; ignored with `kiosk`) |
| `cursorAnchor` | string | — | Snap point around cursor: `top-left`, `top-right`, `right`, `bottom-right`, `bottom-left`, `left` (ignored with `kiosk`) |
| `cursorOffset` | `{ x?, y? }` | `{ x: 20, y: -20 }` | Pixel offset from cursor (or fine-tuning on top of `cursorAnchor`; ignored with `kiosk`) |
| `openLinks` | boolean | `false` | Open clicked `http`/`https` links in the system browser (macOS only) |
| `openLinksApp` | string | — | App bundle path for opening links, e.g. `"/Applications/Firefox.app"` (macOS only) |
| `hidden` | boolean | `false` | Start hidden (prewarm mode) — load HTML in the background, reveal with `win.show()` |
Expand Down Expand Up @@ -285,6 +306,7 @@ if (supportsFollowCursor()) {
| `ready` | `info: object` | WebView loaded — includes screen, appearance, and cursor info |
| `message` | `data: object` | Message sent from the page via `window.glimpse.send(data)` |
| `info` | `info: object` | Fresh system info (response to `.getInfo()`) |
| `kiosk` | `{ active, reason }` | Kiosk shortcut-control state changed or was reported |
| `click` | — | Menu bar icon clicked (status item mode only) |
| `closed` | — | Window was closed (by user or via `.close()`) |
| `error` | `Error` | Process error or malformed protocol line |
Expand Down Expand Up @@ -321,6 +343,12 @@ win.followCursor(true, 'top-right', 'spring'); // spring physics
win.followCursor(false); // detach
```

**`win.kiosk(enabled?)`** — Enable or disable kiosk shortcut control at runtime. Enabling also applies kiosk geometry where supported; disabling releases shortcut control, but backends may leave the window's current size/style in place.
```js
win.kiosk(true);
win.kiosk(false);
```

**`win.info`** — Getter for the last-known system info. Available after `ready`.
```js
const { width, height } = win.info.screen;
Expand Down Expand Up @@ -378,6 +406,12 @@ Glimpse uses a newline-delimited JSON (JSON Lines) protocol over stdin/stdout. E
{"type":"follow-cursor","enabled":false}
```

**Kiosk** — Toggle kiosk shortcut control.
```json
{"type":"kiosk","enabled":true}
{"type":"kiosk","enabled":false}
```

**Load File** — Load a local HTML file by absolute path.
```json
{"type":"file","path":"/path/to/page.html"}
Expand Down Expand Up @@ -431,6 +465,11 @@ Glimpse uses a newline-delimited JSON (JSON Lines) protocol over stdin/stdout. E
{"type":"click"}
```

**Kiosk** — Kiosk shortcut-control state or backend support changed.
```json
{"type":"kiosk","active":true,"reason":"system shortcut inhibition granted"}
```

**Closed** — Window closed.
```json
{"type":"closed"}
Expand Down Expand Up @@ -459,20 +498,21 @@ npx glimpseui page.html --frameless --transparent

| Flag | Default | Description |
|------|---------|-------------|
| `--width N` | `800` | Window width in pixels |
| `--height N` | `600` | Window height in pixels |
| `--width N` | `800` | Window width in pixels (ignored with `--kiosk`) |
| `--height N` | `600` | Window height in pixels (ignored with `--kiosk`) |
| `--title STR` | `"Glimpse"` | Window title bar text |
| `--x N` | — | Horizontal screen position |
| `--y N` | — | Vertical screen position |
| `--x N` | — | Horizontal screen position (ignored with `--kiosk`) |
| `--y N` | — | Vertical screen position (ignored with `--kiosk`) |
| `--frameless` | off | Remove the title bar |
| `--floating` | off | Always on top |
| `--transparent` | off | Transparent background |
| `--click-through` | off | Mouse passes through |
| `--follow-cursor` | off | Track cursor position |
| `--follow-mode MODE` | `snap` | `snap` (instant) or `spring` (elastic) |
| `--cursor-anchor POS` | — | Snap point: `top-left`, `top-right`, `right`, `bottom-right`, `bottom-left`, `left` |
| `--cursor-offset-x N` | `20` | Horizontal cursor offset |
| `--cursor-offset-y N` | `-20` | Vertical cursor offset |
| `--kiosk` | off | Fill the active display and request shortcut inhibition |
| `--follow-cursor` | off | Track cursor position (ignored with `--kiosk`) |
| `--follow-mode MODE` | `snap` | `snap` (instant) or `spring` (elastic; ignored with `--kiosk`) |
| `--cursor-anchor POS` | — | Snap point: `top-left`, `top-right`, `right`, `bottom-right`, `bottom-left`, `left` (ignored with `--kiosk`) |
| `--cursor-offset-x N` | `20` | Horizontal cursor offset (ignored with `--kiosk`) |
| `--cursor-offset-y N` | `-20` | Vertical cursor offset (ignored with `--kiosk`) |
| `--open-links` | off | Open `http`/`https` links in system browser (macOS) |
| `--open-links-app PATH` | — | Open links in a specific app (macOS) |
| `--status-item` | off | Menu bar mode instead of window (macOS) |
Expand Down Expand Up @@ -572,6 +612,7 @@ The core protocol and Node.js API are identical across platforms. Some features
| Window modes (frameless, floating, transparent, click-through) | ✅ | ✅ | ✅ | ✅ |
| Follow cursor | ✅ | Hyprland only | Hyprland + X11 | ✅ |
| Spring physics (follow mode) | ✅ | ✅ (Hyprland) | ✅ | ✅ |
| Kiosk shortcut inhibition | ✅ | X11 grab; Wayland compositor-dependent | — | Best effort |
| Status item (menu bar / tray) | ✅ | — | ✅ | — |
| Open links externally | ✅ | — | ✅ | — |
| Hidden / prewarm | ✅ | ✅ | ✅ | ✅ |
Expand Down
26 changes: 15 additions & 11 deletions bin/glimpse.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ for (let i = 0; i < args.length; i++) {
else if (arg === '--floating') { flags.floating = true; }
else if (arg === '--transparent') { flags.transparent = true; }
else if (arg === '--click-through') { flags.clickThrough = true; }
else if (arg === '--kiosk') { flags.kiosk = true; }
else if (arg === '--follow-cursor') { flags.followCursor = true; }
else if (arg === '--auto-close') { flags.autoClose = true; }
else if (arg === '--width' && args[i + 1]) { flags.width = parseInt(args[++i]); }
Expand Down Expand Up @@ -44,23 +45,24 @@ Usage:
glimpseui --demo Show a demo window

Options:
--width <n> Window width (default: 800)
--height <n> Window height (default: 600)
--width <n> Window width (ignored with --kiosk)
--height <n> Window height (ignored with --kiosk)
--title <text> Window title (default: "Glimpse")
--frameless No title bar
--floating Always on top
--transparent Transparent background
--click-through Mouse passes through
--follow-cursor Window follows cursor
--follow-mode <mode> Follow mode: snap (default) or spring
--cursor-anchor <pos> Snap point: top-left, top-right, right, bottom-right, bottom-left, left
--cursor-offset-x <n> Cursor X offset (default: 20)
--cursor-offset-y <n> Cursor Y offset (default: -20)
--kiosk Request kiosk mode and inhibit system shortcuts
--follow-cursor Window follows cursor (ignored with --kiosk)
--follow-mode <mode> Follow mode: snap (default) or spring (ignored with --kiosk)
--cursor-anchor <pos> Snap point (ignored with --kiosk)
--cursor-offset-x <n> Cursor X offset (ignored with --kiosk)
--cursor-offset-y <n> Cursor Y offset (ignored with --kiosk)
--open-links Open http/https links in default browser
--open-links-app <app> Open http/https links in a specific browser app (full path)
--auto-close Close after first window.glimpse.send()
--x <n> Window X position
--y <n> Window Y position
--x <n> Window X position (ignored with --kiosk)
--y <n> Window Y position (ignored with --kiosk)
--demo Show a demo window
--help, -h Show this help
`);
Expand Down Expand Up @@ -125,8 +127,10 @@ async function main() {
html = DEMO_HTML;
flags.frameless = flags.frameless ?? true;
flags.transparent = flags.transparent ?? true;
flags.width = flags.width ?? 380;
flags.height = flags.height ?? 320;
if (!flags.kiosk) {
flags.width = flags.width ?? 380;
flags.height = flags.height ?? 320;
}
} else if (positional.length > 0) {
// Load from file
const file = resolve(positional[0]);
Expand Down
1 change: 1 addition & 0 deletions native/windows/Glimpse.Windows.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<RollForward>LatestMajor</RollForward>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
Loading