@vibelord/lanes 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/lanes/README.md +37 -7
- package/payload/lanes/VERSION +1 -1
- package/payload/lanes/bin/lanes +651 -50
- package/payload/lanes/client/ghostty/common.conf +3 -0
- package/payload/lanes/client/ghostty/lanes-palette-light.conf +25 -0
- package/payload/lanes/client/ghostty/lanes-palette.conf +25 -0
- package/payload/lanes/client/ghostty/omarchy.conf +3 -2
- package/payload/lanes/client/lanes-launch +18 -3
- package/payload/lanes/client/lanes-window +17 -1
- package/payload/lanes/container/env.sh +1 -1
- package/payload/lanes/container/hooks/claude-settings.json +10 -0
- package/payload/lanes/container/hooks/codex-hooks.json +22 -0
- package/payload/lanes/container/lane +310 -26
- package/payload/lanes/container/lane-signal +173 -0
- package/payload/lanes/docs/ARCHITECTURE.md +80 -13
- package/payload/lanes/docs/DESIGN.md +182 -4
- package/payload/lanes/docs/RUNBOOK.md +429 -39
- package/payload/lanes/docs/SHARING.md +1 -1
- package/payload/lanes/holders/exec.sh +2 -5
- package/payload/lanes/holders.lock +3 -0
- package/payload/lanes/install/host-install.sh +10 -1
- package/payload/lanes/install/services.py +30 -3
- package/payload/lanes/install.sh +108 -3
- package/payload/lanes/libexec/agent_state.py +32 -0
- package/payload/lanes/libexec/cursor_sync.py +10 -2
- package/payload/lanes/libexec/economy_poll.py +685 -0
- package/payload/lanes/libexec/guard.py +27 -1
- package/payload/lanes/libexec/lanes_lib.py +285 -38
- package/payload/lanes/libexec/notify.py +317 -0
- package/payload/lanes/libexec/registrations.py +1 -1
- package/payload/lanes/libexec/session_actions.py +7 -8
- package/payload/lanes/libexec/sidebar.py +911 -218
- package/payload/lanes/libexec/statusd.py +238 -69
- package/payload/lanes/libexec/tokens.py +38 -7
- package/payload/lanes/libexec/view.sh +13 -3
- package/payload/lanes/systemd/lanes-economy.service +20 -0
- package/payload/lanes/systemd/lanes-notify.service +19 -0
- package/payload/lanes/systemd/lanes-sync.service +3 -0
- package/payload/lanes/web/README.md +83 -4
- package/payload/lanes/web/index.html +2 -1
- package/payload/lanes/web/mobile.css +53 -198
- package/payload/lanes/web/mobile.js +87 -55
- package/payload/lanes/zellij/config.kdl +67 -16
- package/payload/manifest.json +4 -3
- package/payload/lanes/libexec/legacy_sessions.py +0 -130
|
@@ -4,6 +4,9 @@ Description=lanes registry sync (read-only: rebuild registry.json from the repos
|
|
|
4
4
|
[Service]
|
|
5
5
|
Type=oneshot
|
|
6
6
|
ExecStart="@REPO@/bin/lanes" sync
|
|
7
|
+
# Bounded: one `docker inspect` (20 s) plus the registry reads. A sync wedged on a hung docker or volume is
|
|
8
|
+
# stopped here instead of holding the oneshot (and every later timer tick) forever.
|
|
9
|
+
TimeoutStartSec=120
|
|
7
10
|
# A momentarily invalid registry keeps the previous lanes; a non-zero exit here is informational.
|
|
8
11
|
SuccessExitStatus=1
|
|
9
12
|
Nice=5
|
|
@@ -11,8 +11,9 @@ workspace sheet as a left panel, and set `zellij:mobile-ui` to `on` when it is u
|
|
|
11
11
|
user agent). Desktop browsers (fine pointer) get only a **Lanes** chip and an overlay bar with workspace and lane
|
|
12
12
|
pickers (`laneBarModel`, from the `MobileState` sent to every web client). The laptop/tablet code and styles live in
|
|
13
13
|
`browser.js`/`browser.css`, which `mobile.js` appends only on a tablet or desktop (a phone never downloads them), so
|
|
14
|
-
|
|
15
|
-
refocuses the terminal, so the keys stay the terminal's. On a tablet, `browser.js` also
|
|
14
|
+
a phone downloads only the two files that spec §8 budgets (measured figures under Bundle budget below). The chip
|
|
15
|
+
cancels `mousedown` and refocuses the terminal, so the keys stay the terminal's. On a tablet, `browser.js` also
|
|
16
|
+
undoes the client's per-load single-pane start once
|
|
16
17
|
(`laneFullRender`, the client's own `SetMobileRenderPreferences {single_pane: false, fit: true}`), so a desktop
|
|
17
18
|
view's sidebar shows beside its lanes. `tests/web/activation.test.mjs` (`node --test`) pins the activation matrix,
|
|
18
19
|
the picker model and that render preference; `tests/unit/test_web_view.py` pins the size budgets and runs that
|
|
@@ -25,6 +26,14 @@ Zellij's original capture input in its closed shadow root, preserving its keyboa
|
|
|
25
26
|
blocks programmatic focus on text editors and keeps `__zjSoftKbdEnabled` false, so upstream touch handlers,
|
|
26
27
|
two-finger gestures, server `SetSoftKeyboard` messages and modal autofocus cannot summon it. Search and
|
|
27
28
|
login inputs still focus normally when tapped. Keep this policy in place when upgrading the upstream client.
|
|
29
|
+
The guard is installed before Zellij's first terminal or login modal: otherwise Zellij refocuses a hidden input on
|
|
30
|
+
touchstart, touchend, click and server `SetSoftKeyboard` messages. The **Type here** field keeps the original input
|
|
31
|
+
and its event handlers inside the closed shadow root (including its protection from password-manager extensions) and
|
|
32
|
+
makes it a real, tappable field instead of a 1 px invisible focus target; keystrokes appear in the terminal and its
|
|
33
|
+
internal padding/history stays invisible. Upstream cancels `pointerdown` on the extra keys, which also suppresses
|
|
34
|
+
their touch click in Safari, so the shell cancels `mousedown` instead: typing focus is kept while the native click
|
|
35
|
+
still activates CTRL, the arrows and the other keys. The shell attaches only once server state has arrived — resizing
|
|
36
|
+
before the control socket opens makes 0.45.1 throw `InvalidStateError` and leaves the server with the old size.
|
|
28
37
|
|
|
29
38
|
`lanes-mobile.service` runs a separate, loopback-only Caddy on port 8083 with its admin API disabled.
|
|
30
39
|
It does not change the system Caddy. Tailscale Serve forwards private HTTPS on 8443 to this frontend;
|
|
@@ -41,8 +50,78 @@ clearing login cookies. There is no service worker caching terminal data or cred
|
|
|
41
50
|
Layout uses the visual viewport and safe-area insets, with independent handling for portrait, landscape,
|
|
42
51
|
keyboard-open and Home Screen views. The default terminal font is 16 px; the user's text-size choice
|
|
43
52
|
is stored locally. Minimum touch targets are 44 px (workspace cards are 74 px). The lane picker in
|
|
44
|
-
`libexec/sidebar.py` uses three terminal rows per lane on mobile
|
|
45
|
-
the touch-to-lane path with isolated holders; sidebar unit tests cover the
|
|
53
|
+
`libexec/sidebar.py` uses three terminal rows per lane on mobile: name, state and age, the agent's task.
|
|
54
|
+
`tests/mobile_view.py` verifies the touch-to-lane path with isolated holders; sidebar unit tests cover the
|
|
55
|
+
complete touch target.
|
|
56
|
+
|
|
57
|
+
Every byte the shell sends to a terminal goes through `__zjSendControl({type: 'Write', bytes})`
|
|
58
|
+
(`sendToTerminal`); there is no focus-and-type fallback, since the focus guard above would make it type nowhere.
|
|
59
|
+
The lane filter bar sends `Esc /` and the query as UTF-8 (`TextEncoder`), dropping control bytes and DEL.
|
|
60
|
+
Attention counts come from the picker sidebar's pane title (`WS · ◆1 ◇2 ▸6`, written by the sidebar as OSC 2):
|
|
61
|
+
`parseCounts` (also `window.__lanesParseCounts`) feeds the picker subtitle, `document.title`, the Lanes button's
|
|
62
|
+
`data-count` and the workspace cards, inside the existing `refresh()` / `renderCards()` — no fetch, file or timer.
|
|
63
|
+
Until plan Task 26 (2026-10-04) this reader was inert (DEF-1450): `bin/lanes` `mobile_workspace_tab` gave the
|
|
64
|
+
picker pane a fixed `name="Lanes"`, and Zellij reports a pane's layout name instead of its OSC 2 title, so
|
|
65
|
+
`pickerTitle()` read `Lanes`, `parseCounts` returned null and the shell showed no counts ("Choose a lane to continue").
|
|
66
|
+
Task 26 dropped that `name=` (pinned by `tests/unit/test_layout.py`) for sessions laid out since: an existing
|
|
67
|
+
phone device keeps the old picker pane until `lanes rebuild <device>` (`upgrade-session` never re-lays-out a tab;
|
|
68
|
+
`lanes doctor` names such a device). Whether the OSC 2 title now reaches
|
|
69
|
+
`panes[].title` is still unchecked in a browser (no WebKit pass has run; fallback: the counts stay in the
|
|
70
|
+
sidebar header). The quick-key row (`⏎ y n Esc ^C ↑ ↓`, `.lanes-quick`) shows on view
|
|
71
|
+
tabs only; `refresh()` sets `--lanes-quick` (48 px or 0), which the terminal height subtracts. `n` and `^C` send
|
|
72
|
+
only when `pointerup.timeStamp − pointerdown.timeStamp ≥ 400` (no `setTimeout`); the buttons cancel `mousedown`
|
|
73
|
+
so a tap never moves focus.
|
|
74
|
+
|
|
75
|
+
Colour (spec §6.3; the operator legend, token table and phone mockup are in [`docs/DESIGN.md`](../docs/DESIGN.md)):
|
|
76
|
+
`mobile.css` uses only the generated `--lanes-*` tokens (legacy names such as `--lanes-bg` stay
|
|
77
|
+
as aliases for one release); `test_web` rejects any colour literal outside the generated block. The terminal palette
|
|
78
|
+
is the phone's own, because Zellij's `web_client.theme` is one server-wide dark palette (DEF-1413): `lanesTheme()`
|
|
79
|
+
builds xterm.js's theme from the CSS tokens (`getComputedStyle(root)`) and the sixteen `LANES_ANSI` slots, which
|
|
80
|
+
`libexec/tokens.py --js` generates into `mobile.js` (never hand-edited). It is applied at three points, all event
|
|
81
|
+
driven: (1) on attach, after `build(); applyFont();`; (2) on every `data-zj-theme` change, through one
|
|
82
|
+
`MutationObserver` on `<html>` (upstream's theme toggle only flips that attribute); (3) after every `SetConfig`, by
|
|
83
|
+
chaining `window.__zjSyncInactiveCursorStyle`, which the pinned `app.js` calls in its `SetConfig` handler right after
|
|
84
|
+
assigning the server theme wholesale (`SetConfig` is sent on each config-file reload). The handler then runs
|
|
85
|
+
`applyTerminalBackground`, which writes the dark background inline on `<body>` and `#terminal`; the stylesheet's
|
|
86
|
+
`!important` `--lanes-surface` rule for those two elements outranks it in either variant. An SRI bump of `app.js`
|
|
87
|
+
re-opens this contract: re-read `startWsControl` and `installSoftKeyboardCapture` before accepting it.
|
|
88
|
+
`index.html` carries one `theme-color` per colour scheme. Bundle budget (spec §8, `test_web.test_bundle_growth`):
|
|
89
|
+
`mobile.css` ≤ 18 982 B and `mobile.js` ≤ 22 174 B; the caps are never raised — the rules sit one per line and the
|
|
90
|
+
explanations live here rather than in the shipped files. MEASURED 2026-10-04 with `wc -c` after LNS-31 (plan Task
|
|
91
|
+
31) met Tasks 13/28: `mobile.js` 21 964 B (23 268 B before its comments moved to Code notes below) and `mobile.css`
|
|
92
|
+
18 945 B. A laptop or tablet also fetches `browser.js` (3 641 B) and `browser.css` (1 947 B); `test_web_view` pins
|
|
93
|
+
that download at `LAPTOP_TABLET_JS_RATCHET` and `LAPTOP_TABLET_CSS_CAP` (DEF-1488).
|
|
94
|
+
|
|
95
|
+
## Code notes
|
|
96
|
+
|
|
97
|
+
`mobile.js` carries no explanations (the spec §8 budget above); each note is keyed to the code it explains, in file
|
|
98
|
+
order.
|
|
99
|
+
|
|
100
|
+
- The file: the Lanes browser shell (phone, tablet, desktop) over the pinned Zellij 0.45.1 client's window hooks.
|
|
101
|
+
- `root.dataset.lanesShell = mode`: read by `browser.js` for the desktop bar and the tablet's full rendering.
|
|
102
|
+
- `if (mode !== 'phone')`: appends the laptop/tablet extras (`browser.css`, `browser.js`); a phone never fetches them.
|
|
103
|
+
- `if (mode === 'tablet')`: iPad Safari's user agent is a desktop one, so the shell asks for the client's mobile UI
|
|
104
|
+
(`zellij:mobile-ui` set to `on`); a stored `off` stays.
|
|
105
|
+
- The `HTMLElement.prototype.focus` guard: only a native tap on an input may open the keyboard; it is installed before
|
|
106
|
+
upstream's first terminal or modal.
|
|
107
|
+
- `viewport()`: browser toolbars alone must not mean keyboard mode (`occluded` needs more than 120 px hidden). Zellij's
|
|
108
|
+
input is in a closed shadow root, so `document.activeElement` is its host; `__zjSoftKbdCapture.isFocused` is read
|
|
109
|
+
first.
|
|
110
|
+
- `buildTypingField()`: upstream's capture input stays in its closed shadow root and becomes the real, tappable field.
|
|
111
|
+
- `GLYPHS` / `parseCounts()`: counts from the picker sidebar's OSC 2 pane title (spec §6.4).
|
|
112
|
+
- `build()`, the `.zj-mobile-modbar` keys: cancel `mousedown`, not `pointerdown` (upstream's `pointerdown` cancel kills
|
|
113
|
+
Safari's touch click on the extra keys).
|
|
114
|
+
- `buildQuickRow()` (spec §6.5): `n` and `^C` send after a 400 ms hold, measured from event timestamps with no timer;
|
|
115
|
+
nothing takes focus.
|
|
116
|
+
- `clearLaneFilter()`: the lone `Esc` leaves the sidebar's filter mode.
|
|
117
|
+
- `lanesTheme()`: the shell's own terminal palette for both variants (spec §6.3, DEF-1413).
|
|
118
|
+
- `connect()`: waits for server state, because an earlier resize throws `InvalidStateError` in 0.45.1. `applyTheme` is
|
|
119
|
+
re-applied after every `SetConfig` (the client calls the chained `__zjSyncInactiveCursorStyle` hook) and on the theme
|
|
120
|
+
toggle.
|
|
121
|
+
- The `zellij:soft-keyboard-visibility` listener: rotation also shrinks the viewport, so the shell sends
|
|
122
|
+
`SoftKeyboardVisibilityChanged` itself to correct upstream's height-only keyboard heuristic.
|
|
123
|
+
- `awaitingClient` (the one `setInterval`, 200 ms, cleared once attached): upstream installs its window hooks just after
|
|
124
|
+
appending its mobile root.
|
|
46
125
|
|
|
47
126
|
Validate on Safari/WebKit at 440 × 763 (iPhone 17 Pro Max with browser chrome), 440 × 956 (Home Screen),
|
|
48
127
|
838 × 390 (landscape Safari), and smaller phone widths. Check login, workspace search and switching,
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
7
|
-
<meta name="theme-color" content="#101318" />
|
|
7
|
+
<meta name="theme-color" content="#101318" media="(prefers-color-scheme: dark)" />
|
|
8
|
+
<meta name="theme-color" content="#f3f5f2" media="(prefers-color-scheme: light)" />
|
|
8
9
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
9
10
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
10
11
|
<meta name="apple-mobile-web-app-title" content="Lanes" />
|
|
@@ -62,231 +62,100 @@ html.lanes-mobile:root {
|
|
|
62
62
|
--zj-yellow: var(--lanes-tone-attention);
|
|
63
63
|
}
|
|
64
64
|
/* END lanes tokens */
|
|
65
|
-
/*
|
|
66
|
-
|
|
67
|
-
html.lanes-
|
|
68
|
-
--lanes-safe-top: env(safe-area-inset-top, 0px);
|
|
69
|
-
--lanes-safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
70
|
-
--lanes-safe-left: env(safe-area-inset-left, 0px);
|
|
71
|
-
--lanes-safe-right: env(safe-area-inset-right, 0px);
|
|
72
|
-
--lanes-head: calc(58px + var(--lanes-safe-top));
|
|
73
|
-
--lanes-dock: calc(66px + var(--lanes-safe-bottom));
|
|
74
|
-
--lanes-keys: 0px;
|
|
75
|
-
--lanes-vh: 100dvh;
|
|
76
|
-
--lanes-vw: 100vw;
|
|
77
|
-
--lanes-offset-top: 0px;
|
|
78
|
-
--lanes-offset-left: 0px;
|
|
79
|
-
-webkit-text-size-adjust: 100%;
|
|
80
|
-
overscroll-behavior: none;
|
|
81
|
-
}
|
|
82
|
-
html.lanes-keyboard {
|
|
83
|
-
--lanes-dock: 48px;
|
|
84
|
-
--lanes-keys: 48px;
|
|
85
|
-
--lanes-safe-bottom: 0px;
|
|
86
|
-
}
|
|
65
|
+
/* Layout; colours only via the tokens above */
|
|
66
|
+
html.lanes-mobile { --lanes-safe-top: env(safe-area-inset-top, 0px); --lanes-safe-bottom: env(safe-area-inset-bottom, 0px); --lanes-safe-left: env(safe-area-inset-left, 0px); --lanes-safe-right: env(safe-area-inset-right, 0px); --lanes-head: calc(58px + var(--lanes-safe-top)); --lanes-dock: calc(66px + var(--lanes-safe-bottom)); --lanes-keys: 0px; --lanes-quick: 0px; --lanes-vh: 100dvh; --lanes-vw: 100vw; --lanes-offset-top: 0px; --lanes-offset-left: 0px; -webkit-text-size-adjust: 100%; overscroll-behavior: none; }
|
|
67
|
+
html.lanes-keyboard { --lanes-dock: 48px; --lanes-keys: 48px; --lanes-safe-bottom: 0px; }
|
|
87
68
|
.lanes-mobile body { overscroll-behavior: none; }
|
|
88
|
-
.lanes-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
.lanes-shell {
|
|
92
|
-
color: var(--lanes-text);
|
|
93
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
94
|
-
-webkit-tap-highlight-color: transparent;
|
|
95
|
-
}
|
|
69
|
+
.lanes-mobile body, .lanes-mobile #terminal { background: var(--lanes-surface) !important; }
|
|
70
|
+
.lanes-shell, .lanes-shell *, .lanes-mobile .security-modal *, .lanes-mobile #zj-mobile-root * { box-sizing: border-box; }
|
|
71
|
+
.lanes-shell { color: var(--lanes-text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; -webkit-tap-highlight-color: transparent; }
|
|
96
72
|
.lanes-shell [hidden], .lanes-shell[hidden] { display: none !important; }
|
|
97
|
-
.lanes-shell button, .lanes-mobile #zj-mobile-root button {
|
|
98
|
-
touch-action: manipulation;
|
|
99
|
-
-webkit-tap-highlight-color: transparent;
|
|
100
|
-
cursor: pointer;
|
|
101
|
-
}
|
|
73
|
+
.lanes-shell button, .lanes-mobile #zj-mobile-root button { touch-action: manipulation; -webkit-tap-highlight-color: transparent; cursor: pointer; }
|
|
102
74
|
.lanes-shell button { font: inherit; color: inherit; }
|
|
103
|
-
.lanes-shell button:focus-visible, .lanes-shell input:focus-visible {
|
|
104
|
-
outline: 2px solid var(--lanes-accent); outline-offset: -3px;
|
|
105
|
-
}
|
|
75
|
+
.lanes-shell button:focus-visible, .lanes-shell input:focus-visible { outline: 2px solid var(--lanes-accent); outline-offset: -3px; }
|
|
106
76
|
.lanes-ready .zj-mobile-topbar { display: none !important; }
|
|
107
|
-
.lanes-ready body.zj-mobile-active #terminal {
|
|
108
|
-
|
|
109
|
-
top: calc(var(--lanes-offset-top) + var(--lanes-head) + 8px);
|
|
110
|
-
left: calc(var(--lanes-offset-left) + var(--lanes-safe-left) + 8px);
|
|
111
|
-
width: calc(var(--lanes-vw) - var(--lanes-safe-left) - var(--lanes-safe-right) - 16px);
|
|
112
|
-
height: calc(var(--lanes-vh) - var(--lanes-head) - var(--lanes-dock) - var(--lanes-keys) - 16px);
|
|
113
|
-
margin: 0;
|
|
114
|
-
padding: 0;
|
|
115
|
-
}
|
|
116
|
-
.lanes-header {
|
|
117
|
-
position: fixed; z-index: 510;
|
|
118
|
-
top: var(--lanes-offset-top); left: var(--lanes-offset-left);
|
|
119
|
-
width: var(--lanes-vw); height: var(--lanes-head);
|
|
120
|
-
padding: var(--lanes-safe-top) calc(8px + var(--lanes-safe-right)) 0 calc(12px + var(--lanes-safe-left));
|
|
121
|
-
display: flex; align-items: center; gap: 10px;
|
|
122
|
-
background: var(--lanes-bg); border-bottom: 1px solid var(--lanes-border);
|
|
123
|
-
}
|
|
77
|
+
.lanes-ready body.zj-mobile-active #terminal { position: fixed; top: calc(var(--lanes-offset-top) + var(--lanes-head) + 8px); left: calc(var(--lanes-offset-left) + var(--lanes-safe-left) + 8px); width: calc(var(--lanes-vw) - var(--lanes-safe-left) - var(--lanes-safe-right) - 16px); height: calc(var(--lanes-vh) - var(--lanes-head) - var(--lanes-dock) - var(--lanes-keys) - var(--lanes-quick) - 16px); margin: 0; padding: 0; }
|
|
78
|
+
.lanes-header { position: fixed; z-index: 510; top: var(--lanes-offset-top); left: var(--lanes-offset-left); width: var(--lanes-vw); height: var(--lanes-head); padding: var(--lanes-safe-top) calc(8px + var(--lanes-safe-right)) 0 calc(16px + var(--lanes-safe-left)); display: flex; align-items: center; gap: 10px; background: var(--lanes-bg); border-bottom: 1px solid var(--lanes-border); }
|
|
124
79
|
.lanes-brand { height: 34px; width: 34px; flex: 0 0 34px; }
|
|
125
|
-
.lanes-context {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
border: 0; background: none; padding: 0; text-align: left;
|
|
129
|
-
}
|
|
130
|
-
.lanes-context strong { font-size: 16px; letter-spacing: -.25px; line-height: 22px; }
|
|
131
|
-
.lanes-context span { font-size: 12px; color: var(--lanes-muted); line-height: 18px; }
|
|
80
|
+
.lanes-context { display: flex; flex: 1; min-width: 0; height: 54px; flex-direction: column; justify-content: center; align-items: flex-start; border: 0; background: none; padding: 0; text-align: left; }
|
|
81
|
+
.lanes-context strong { font-size: 17px; letter-spacing: -.3px; line-height: 22px; }
|
|
82
|
+
.lanes-context span { font-size: 13px; color: var(--lanes-muted); line-height: 18px; }
|
|
132
83
|
.lanes-context strong, .lanes-context span { width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
133
|
-
.lanes-button {
|
|
134
|
-
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
135
|
-
gap: 5px; min-width: 48px; min-height: 48px; padding: 5px;
|
|
136
|
-
border: 0; background: transparent; border-radius: 12px;
|
|
137
|
-
}
|
|
84
|
+
.lanes-button { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; min-width: 48px; min-height: 48px; padding: 5px; border: 0; background: transparent; border-radius: 12px; }
|
|
138
85
|
.lanes-button svg { width: 23px; height: 23px; flex: 0 0 23px; }
|
|
139
|
-
.lanes-button span { font-size:
|
|
86
|
+
.lanes-button span { font-size: 12px; font-weight: 600; letter-spacing: .1px; }
|
|
140
87
|
.lanes-more, .lanes-close { flex: 0 0 48px; }
|
|
141
88
|
.lanes-more span, .lanes-close span { display: none; }
|
|
142
|
-
.lanes-button:active, .lanes-workspace:active { background: var(--lanes-press); }
|
|
143
|
-
.lanes-dock {
|
|
144
|
-
|
|
145
|
-
top: calc(var(--lanes-offset-top) + var(--lanes-vh) - var(--lanes-dock));
|
|
146
|
-
left: var(--lanes-offset-left); width: var(--lanes-vw); height: var(--lanes-dock);
|
|
147
|
-
display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
148
|
-
padding: 4px calc(8px + var(--lanes-safe-right)) var(--lanes-safe-bottom) calc(8px + var(--lanes-safe-left));
|
|
149
|
-
border-top: 1px solid var(--lanes-border); background: var(--lanes-bg);
|
|
150
|
-
}
|
|
151
|
-
.lanes-dock .lanes-button { color: var(--lanes-muted); }
|
|
89
|
+
.lanes-button:active, .lanes-workspace:active, .lanes-quick button:active { background: var(--lanes-press); }
|
|
90
|
+
.lanes-dock { position: fixed; z-index: 510; top: calc(var(--lanes-offset-top) + var(--lanes-vh) - var(--lanes-dock)); left: var(--lanes-offset-left); width: var(--lanes-vw); height: var(--lanes-dock); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); padding: 4px calc(8px + var(--lanes-safe-right)) var(--lanes-safe-bottom) calc(8px + var(--lanes-safe-left)); border-top: 1px solid var(--lanes-border); background: var(--lanes-bg); }
|
|
91
|
+
.lanes-dock .lanes-button { color: var(--lanes-muted); position: relative; }
|
|
152
92
|
.lanes-dock .lanes-button[aria-pressed="true"] { color: var(--lanes-accent); background: var(--lanes-current); }
|
|
153
|
-
.lanes-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
background: var(--lanes-panel); color: var(--lanes-muted);
|
|
157
|
-
}
|
|
158
|
-
.lanes-input-label {
|
|
159
|
-
position: absolute; inset: 0; pointer-events: none;
|
|
160
|
-
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
|
|
161
|
-
}
|
|
93
|
+
.lanes-button[data-count]::after { content: attr(data-count); position: absolute; top: 2px; right: 8px; display: grid; place-items: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--lanes-tone-attention); color: var(--lanes-on-accent); font-size: 12px; font-weight: 600; }
|
|
94
|
+
.lanes-terminal-input { position: relative; align-self: center; min-width: 0; height: 52px; margin: 0 2px; border: 1px solid var(--lanes-border); border-radius: 12px; background: var(--lanes-panel); color: var(--lanes-muted); }
|
|
95
|
+
.lanes-input-label { position: absolute; inset: 0; pointer-events: none; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; }
|
|
162
96
|
.lanes-input-label svg { width: 23px; height: 23px; }
|
|
163
|
-
.lanes-input-label span { font-size:
|
|
164
|
-
.lanes-terminal-input:focus-within, .lanes-terminal-input.is-typing {
|
|
165
|
-
border-color: var(--lanes-accent); color: var(--lanes-accent); background: var(--lanes-current);
|
|
166
|
-
}
|
|
97
|
+
.lanes-input-label span { font-size: 12px; font-weight: 600; letter-spacing: .1px; }
|
|
98
|
+
.lanes-terminal-input:focus-within, .lanes-terminal-input.is-typing { border-color: var(--lanes-accent); color: var(--lanes-accent); background: var(--lanes-current); }
|
|
167
99
|
.lanes-keyboard .lanes-dock { padding-top: 0; }
|
|
168
100
|
.lanes-keyboard .lanes-dock .lanes-button { min-height: 44px; padding: 0; }
|
|
169
101
|
.lanes-keyboard .lanes-dock .lanes-button span { display: none; }
|
|
170
102
|
.lanes-keyboard .lanes-terminal-input { height: 44px; }
|
|
171
103
|
.lanes-keyboard .lanes-input-label { flex-direction: row; gap: 5px; }
|
|
172
|
-
.lanes-ready .zj-mobile-modbar {
|
|
173
|
-
top: calc(var(--lanes-offset-top) + var(--lanes-vh) - var(--lanes-dock) - var(--lanes-keys));
|
|
174
|
-
bottom: auto; left: calc(var(--lanes-offset-left) + var(--lanes-safe-left));
|
|
175
|
-
right: auto; width: calc(var(--lanes-vw) - var(--lanes-safe-left) - var(--lanes-safe-right));
|
|
176
|
-
height: 48px; z-index: 510;
|
|
177
|
-
overflow-x: auto; overscroll-behavior: contain;
|
|
178
|
-
scrollbar-width: none; background: var(--lanes-panel); border-color: var(--lanes-border);
|
|
179
|
-
}
|
|
104
|
+
.lanes-ready .zj-mobile-modbar, .lanes-quick { top: calc(var(--lanes-offset-top) + var(--lanes-vh) - var(--lanes-dock) - var(--lanes-keys)); bottom: auto; left: calc(var(--lanes-offset-left) + var(--lanes-safe-left)); right: auto; width: calc(var(--lanes-vw) - var(--lanes-safe-left) - var(--lanes-safe-right)); height: 48px; z-index: 510; overflow-x: auto; overscroll-behavior: contain; scrollbar-width: none; background: var(--lanes-panel); border-color: var(--lanes-border); }
|
|
180
105
|
.lanes-ready:not(.lanes-keyboard) .zj-mobile-modbar { display: none !important; }
|
|
106
|
+
.lanes-quick { position: fixed; top: calc(var(--lanes-offset-top) + var(--lanes-vh) - var(--lanes-dock) - var(--lanes-keys) - var(--lanes-quick)); display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; padding: 2px 16px; border-top: 1px solid var(--lanes-border); }
|
|
107
|
+
.lanes-quick button { min-height: 44px; min-width: 44px; border: 1px solid var(--lanes-border); border-radius: 12px; background: var(--lanes-bg); font: 600 15px ui-monospace, Menlo, monospace; }
|
|
108
|
+
.lanes-quick .is-destructive { color: var(--lanes-tone-urgent); }
|
|
109
|
+
.lanes-quick .is-destructive:active { background: var(--lanes-tone-urgent); color: var(--lanes-on-accent); transition: background 0s 400ms, color 0s 400ms; }
|
|
181
110
|
.lanes-ready.lanes-keyboard .zj-mobile-modbar { display: flex !important; }
|
|
182
|
-
.lanes-ready .zj-mobile-modbar button {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
.lanes-ready .zj-mobile-modbar button.zj-armed { background: var(--lanes-accent); color: #101318; }
|
|
188
|
-
.lanes-sheet {
|
|
189
|
-
position: fixed; z-index: 800;
|
|
190
|
-
top: var(--lanes-offset-top); left: var(--lanes-offset-left);
|
|
191
|
-
height: var(--lanes-vh); width: var(--lanes-vw);
|
|
192
|
-
display: flex; align-items: flex-end; background: #0009;
|
|
193
|
-
padding: var(--lanes-safe-top) var(--lanes-safe-right) 0 var(--lanes-safe-left);
|
|
194
|
-
}
|
|
195
|
-
.lanes-sheet-panel {
|
|
196
|
-
display: flex; flex-direction: column; width: 100%; min-height: 0;
|
|
197
|
-
max-height: calc(var(--lanes-vh) - var(--lanes-safe-top) - 20px);
|
|
198
|
-
background: var(--lanes-bg); border: 1px solid var(--lanes-border);
|
|
199
|
-
border-bottom: 0; border-radius: 24px 24px 0 0;
|
|
200
|
-
padding: 20px 16px calc(8px + var(--lanes-safe-bottom));
|
|
201
|
-
box-shadow: 0 -16px 80px #0007;
|
|
202
|
-
}
|
|
111
|
+
.lanes-ready .zj-mobile-modbar button { flex: 1 0 44px; min-width: 44px; min-height: 48px; color: var(--lanes-text); font: 500 13px -apple-system, sans-serif; border-color: var(--lanes-border); }
|
|
112
|
+
.lanes-ready .zj-mobile-modbar button.zj-armed { background: var(--lanes-accent); color: var(--lanes-on-accent); }
|
|
113
|
+
.lanes-sheet { position: fixed; z-index: 800; top: var(--lanes-offset-top); left: var(--lanes-offset-left); height: var(--lanes-vh); width: var(--lanes-vw); display: flex; align-items: flex-end; background: var(--lanes-scrim); padding: var(--lanes-safe-top) var(--lanes-safe-right) 0 var(--lanes-safe-left); }
|
|
114
|
+
.lanes-sheet-panel { display: flex; flex-direction: column; width: 100%; min-height: 0; max-height: calc(var(--lanes-vh) - var(--lanes-safe-top) - 20px); background: var(--lanes-bg); border: 1px solid var(--lanes-border); border-bottom: 0; border-radius: 24px 24px 0 0; padding: 20px 16px calc(8px + var(--lanes-safe-bottom)); box-shadow: var(--lanes-shadow); }
|
|
203
115
|
.lanes-sheet-head { display: flex; justify-content: space-between; align-items: center; flex: 0 0 auto; margin-bottom: 18px; }
|
|
204
116
|
.lanes-sheet-head h2 { font-size: 25px; line-height: 30px; letter-spacing: -.8px; margin: 0 0 3px; }
|
|
205
117
|
.lanes-sheet-head p { font-size: 13px; color: var(--lanes-muted); margin: 0; }
|
|
206
118
|
.lanes-close { background: var(--lanes-panel); border-radius: 50%; }
|
|
207
|
-
.lanes-search {
|
|
208
|
-
display: flex; align-items: center; gap: 10px; flex: 0 0 48px;
|
|
209
|
-
height: 48px; background: var(--lanes-panel); border: 1px solid var(--lanes-border);
|
|
210
|
-
border-radius: 12px; padding: 0 14px; margin-bottom: 12px;
|
|
211
|
-
}
|
|
119
|
+
.lanes-search { display: flex; align-items: center; gap: 10px; flex: 0 0 48px; height: 48px; background: var(--lanes-panel); border: 1px solid var(--lanes-border); border-radius: 12px; padding: 0 14px; margin-bottom: 12px; }
|
|
212
120
|
.lanes-search svg { width: 20px; height: 20px; color: var(--lanes-muted); flex-shrink: 0; }
|
|
213
|
-
.lanes-search input {
|
|
214
|
-
border: 0; outline: none; background: transparent; color: var(--lanes-text);
|
|
215
|
-
font: 16px -apple-system, sans-serif; min-width: 0; width: 100%; height: 44px;
|
|
216
|
-
}
|
|
121
|
+
.lanes-search input { border: 0; outline: none; background: transparent; color: var(--lanes-text); font: 16px -apple-system, sans-serif; min-width: 0; width: 100%; height: 44px; }
|
|
217
122
|
.lanes-search input::placeholder { color: var(--lanes-muted); }
|
|
218
|
-
.lanes-workspaces {
|
|
219
|
-
|
|
220
|
-
display: flex; flex-direction: column; gap: 6px;
|
|
221
|
-
-webkit-overflow-scrolling: touch;
|
|
222
|
-
}
|
|
223
|
-
.lanes-workspace {
|
|
224
|
-
display: flex; align-items: center; gap: 12px;
|
|
225
|
-
flex: 0 0 auto; width: 100%; min-height: 74px; padding: 12px;
|
|
226
|
-
background: var(--lanes-panel); border: 1px solid transparent; border-radius: 14px;
|
|
227
|
-
text-align: left;
|
|
228
|
-
}
|
|
123
|
+
.lanes-workspaces { min-height: 0; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 8px; -webkit-overflow-scrolling: touch; }
|
|
124
|
+
.lanes-workspace { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; width: 100%; min-height: 74px; padding: 12px; background: var(--lanes-panel); border: 1px solid transparent; border-radius: 14px; text-align: left; }
|
|
229
125
|
.lanes-workspace.is-current { border-color: var(--lanes-current-border); background: var(--lanes-current); }
|
|
230
|
-
.lanes-monogram {
|
|
231
|
-
display: grid; place-items: center; width: 40px; height: 40px; flex: 0 0 40px;
|
|
232
|
-
font-size: 17px; font-weight: 650; color: var(--lanes-accent);
|
|
233
|
-
background: var(--lanes-chip); border: 1px solid var(--lanes-border); border-radius: 11px;
|
|
234
|
-
}
|
|
126
|
+
.lanes-monogram { display: grid; place-items: center; width: 40px; height: 40px; flex: 0 0 40px; font-size: 17px; font-weight: 650; color: var(--lanes-accent); background: var(--lanes-chip); border: 1px solid var(--lanes-border); border-radius: 11px; }
|
|
235
127
|
.lanes-workspace-copy { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
|
|
236
128
|
.lanes-workspace-copy strong { font-size: 16px; font-weight: 600; line-height: 21px; overflow-wrap: anywhere; }
|
|
237
|
-
.lanes-workspace-copy small { font-size:
|
|
129
|
+
.lanes-workspace-copy small { font-size: 13px; color: var(--lanes-muted); }
|
|
130
|
+
.lanes-tone-urgent { color: var(--lanes-tone-urgent); }
|
|
131
|
+
.lanes-tone-attention { color: var(--lanes-tone-attention); }
|
|
132
|
+
.lanes-tone-working { color: var(--lanes-tone-working); }
|
|
133
|
+
.lanes-tone-done { color: var(--lanes-tone-done); }
|
|
238
134
|
.lanes-workspace-arrow { width: 18px; flex: 0 0 18px; color: var(--lanes-muted); }
|
|
239
135
|
.is-current .lanes-workspace-arrow { color: var(--lanes-accent); }
|
|
240
136
|
.lanes-session-link { border: 0; background: transparent; min-height: 48px; color: var(--lanes-muted) !important; font-size: 13px !important; flex-shrink: 0; }
|
|
241
137
|
.lanes-empty { text-align: center; color: var(--lanes-muted); padding: 30px 12px; }
|
|
242
|
-
.lanes-mobile #zj-mobile-root { font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
|
243
|
-
.lanes-mobile .zj-mobile-menu {
|
|
244
|
-
top: calc(var(--lanes-offset-top) + var(--lanes-head) + 6px);
|
|
245
|
-
right: calc(var(--lanes-safe-right) + 8px); left: auto;
|
|
246
|
-
max-height: calc(var(--lanes-vh) - var(--lanes-head) - 16px);
|
|
247
|
-
max-width: calc(var(--lanes-vw) - var(--lanes-safe-left) - var(--lanes-safe-right) - 16px);
|
|
248
|
-
min-width: min(290px, 90vw); overflow-y: auto;
|
|
249
|
-
background: var(--lanes-panel); border: 1px solid var(--lanes-border);
|
|
250
|
-
border-radius: 16px; box-shadow: 0 12px 48px #0009;
|
|
251
|
-
}
|
|
138
|
+
.lanes-mobile #zj-mobile-root, .lanes-mobile .security-modal { font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
|
139
|
+
.lanes-mobile .zj-mobile-menu { top: calc(var(--lanes-offset-top) + var(--lanes-head) + 6px); right: calc(var(--lanes-safe-right) + 8px); left: auto; max-height: calc(var(--lanes-vh) - var(--lanes-head) - 16px); max-width: calc(var(--lanes-vw) - var(--lanes-safe-left) - var(--lanes-safe-right) - 16px); min-width: min(290px, 90vw); overflow-y: auto; background: var(--lanes-panel); border: 1px solid var(--lanes-border); border-radius: 16px; box-shadow: var(--lanes-shadow); }
|
|
252
140
|
.lanes-mobile .zj-mobile-menu button { min-height: 48px; color: var(--lanes-text); font-size: 15px; }
|
|
253
141
|
.lanes-font-controls { display: flex; align-items: center; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--lanes-border); }
|
|
254
142
|
.lanes-font-controls > span { margin-right: auto; font-size: 13px; color: var(--lanes-muted); }
|
|
255
143
|
.lanes-font-controls button { min-width: 44px; padding: 8px !important; text-align: center !important; }
|
|
256
144
|
.lanes-font-controls output { font: 12px -apple-system, sans-serif; width: 36px; text-align: center; color: var(--lanes-muted); }
|
|
257
|
-
.lanes-mobile .zj-mobile-overlay {
|
|
258
|
-
top: var(--lanes-offset-top); left: var(--lanes-offset-left); right: auto;
|
|
259
|
-
width: var(--lanes-vw); height: var(--lanes-vh); max-height: var(--lanes-vh);
|
|
260
|
-
padding: var(--lanes-safe-top) var(--lanes-safe-right) var(--lanes-safe-bottom) var(--lanes-safe-left);
|
|
261
|
-
background: var(--lanes-bg); color: var(--lanes-text);
|
|
262
|
-
}
|
|
145
|
+
.lanes-mobile .zj-mobile-overlay { top: var(--lanes-offset-top); left: var(--lanes-offset-left); right: auto; width: var(--lanes-vw); height: var(--lanes-vh); max-height: var(--lanes-vh); padding: var(--lanes-safe-top) var(--lanes-safe-right) var(--lanes-safe-bottom) var(--lanes-safe-left); background: var(--lanes-bg); color: var(--lanes-text); }
|
|
263
146
|
.lanes-mobile .zj-mobile-overlay-header { border-color: var(--lanes-border); padding: 8px 12px; min-height: 64px; }
|
|
264
147
|
.lanes-mobile .zj-mobile-overlay-header .zj-back { min-width: 64px; min-height: 48px; border-radius: 12px; border-color: var(--lanes-border); }
|
|
265
148
|
.lanes-mobile .zj-mobile-overlay-header .zj-title { font-size: 19px; font-weight: 600; }
|
|
266
149
|
.lanes-mobile .zj-mobile-search-row { padding: 12px; }
|
|
267
|
-
.lanes-mobile .zj-mobile-overlay input.zj-search, .lanes-mobile .zj-mobile-overlay input.zj-name {
|
|
268
|
-
min-width: 0; font-size: 16px; min-height: 48px; border-radius: 10px; padding: 10px;
|
|
269
|
-
color: var(--lanes-text); background: var(--lanes-panel); border-color: var(--lanes-border);
|
|
270
|
-
}
|
|
150
|
+
.lanes-mobile .zj-mobile-overlay input.zj-search, .lanes-mobile .zj-mobile-overlay input.zj-name { min-width: 0; font-size: 16px; min-height: 48px; border-radius: 10px; padding: 10px; color: var(--lanes-text); background: var(--lanes-panel); border-color: var(--lanes-border); }
|
|
271
151
|
.lanes-mobile .zj-mobile-cards { padding: 0 12px 12px; overscroll-behavior: contain; }
|
|
272
152
|
.lanes-mobile .zj-mobile-card { min-height: 72px; padding: 16px; border: 1px solid var(--lanes-border); border-radius: 14px; background: var(--lanes-panel); }
|
|
273
153
|
.lanes-mobile .zj-mobile-card .zj-card-title { color: var(--lanes-accent); font-size: 16px; overflow-wrap: anywhere; }
|
|
274
|
-
.lanes-mobile .zj-mobile-card .zj-card-meta { color: var(--lanes-muted); font-size:
|
|
154
|
+
.lanes-mobile .zj-mobile-card .zj-card-meta { color: var(--lanes-muted); font-size: 13px; overflow-wrap: anywhere; }
|
|
275
155
|
.lanes-mobile .zj-mobile-footer { padding-bottom: 8px; }
|
|
276
156
|
.lanes-mobile .zj-mobile-footer button { min-height: 48px; border-radius: 12px; color: var(--lanes-accent); border-color: var(--lanes-border); }
|
|
277
|
-
.lanes-mobile .security-modal {
|
|
278
|
-
|
|
279
|
-
display: flex; align-items: center; justify-content: center;
|
|
280
|
-
top: var(--lanes-offset-top); left: var(--lanes-offset-left);
|
|
281
|
-
width: var(--lanes-vw); height: var(--lanes-vh);
|
|
282
|
-
padding: calc(16px + var(--lanes-safe-top)) calc(20px + var(--lanes-safe-right)) calc(16px + var(--lanes-safe-bottom)) calc(20px + var(--lanes-safe-left));
|
|
283
|
-
overflow: auto; background: var(--lanes-bg); font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
284
|
-
}
|
|
285
|
-
.lanes-mobile .security-modal .security-modal-content {
|
|
286
|
-
width: 100%; max-width: 400px; max-height: 100%; overflow-y: auto;
|
|
287
|
-
border: 1px solid var(--lanes-border); border-radius: 22px;
|
|
288
|
-
padding: 24px; background: var(--lanes-panel); color: var(--lanes-text); box-shadow: none;
|
|
289
|
-
}
|
|
157
|
+
.lanes-mobile .security-modal { box-sizing: border-box; top: var(--lanes-offset-top); left: var(--lanes-offset-left); width: var(--lanes-vw); height: var(--lanes-vh); padding: calc(16px + var(--lanes-safe-top)) calc(20px + var(--lanes-safe-right)) calc(16px + var(--lanes-safe-bottom)) calc(20px + var(--lanes-safe-left)); overflow: auto; background: var(--lanes-bg); }
|
|
158
|
+
.lanes-mobile .security-modal .security-modal-content { width: 100%; max-width: 400px; max-height: 100%; overflow-y: auto; border: 1px solid var(--lanes-border); border-radius: 24px; padding: 24px; background: var(--lanes-panel); color: var(--lanes-text); box-shadow: none; }
|
|
290
159
|
.lanes-mobile .security-modal .security-modal-content::before { display: none; }
|
|
291
160
|
.lanes-mobile .security-modal h3 { font-size: 25px; font-weight: 650; text-transform: none; letter-spacing: -.7px; color: var(--lanes-text); border: 0; margin-bottom: 18px; }
|
|
292
161
|
.lanes-mobile .security-modal input[type="password"] { display: block; width: 100%; padding: 12px; margin-bottom: 12px; font-size: 16px; min-height: 52px; border-radius: 12px; background: var(--lanes-bg); color: var(--lanes-text); border: 1px solid var(--lanes-border); appearance: none; }
|
|
@@ -294,18 +163,15 @@ html.lanes-keyboard {
|
|
|
294
163
|
.lanes-mobile .security-modal input[type="checkbox"] { width: 22px; height: 22px; border-radius: 6px; flex: 0 0 22px; accent-color: var(--lanes-accent); }
|
|
295
164
|
.lanes-mobile .security-modal .button-row { display: flex; gap: 12px; margin-top: 20px; }
|
|
296
165
|
.lanes-mobile .security-modal button { min-height: 48px; border: 1px solid; border-radius: 12px; font-size: 15px; padding: 10px 20px; appearance: none; }
|
|
297
|
-
.lanes-mobile .security-modal .submit-btn { background: var(--lanes-accent); border-color: var(--lanes-accent); color:
|
|
166
|
+
.lanes-mobile .security-modal .submit-btn { background: var(--lanes-accent); border-color: var(--lanes-accent); color: var(--lanes-on-accent); font-weight: 650; flex: 1; }
|
|
298
167
|
.lanes-mobile .security-modal .cancel-btn { background: transparent; color: var(--lanes-muted); border-color: var(--lanes-border); }
|
|
299
168
|
@media (orientation: landscape) and (max-height: 500px) {
|
|
300
169
|
html.lanes-mobile { --lanes-head: calc(48px + var(--lanes-safe-top)); --lanes-dock: calc(48px + var(--lanes-safe-bottom)); }
|
|
301
|
-
html.lanes-keyboard { --lanes-dock: 48px; }
|
|
302
170
|
.lanes-context { height: 46px; }
|
|
303
171
|
.lanes-dock { padding-top: 0; }
|
|
304
172
|
.lanes-dock .lanes-button { flex-direction: row; gap: 8px; min-height: 44px; }
|
|
305
|
-
.lanes-dock .lanes-button span { font-size: 12px; }
|
|
306
173
|
.lanes-terminal-input { height: 44px; }
|
|
307
174
|
.lanes-input-label { flex-direction: row; gap: 8px; }
|
|
308
|
-
.lanes-input-label span { font-size: 12px; }
|
|
309
175
|
.lanes-sheet-panel { padding-top: 12px; }
|
|
310
176
|
.lanes-sheet-head { margin-bottom: 8px; }
|
|
311
177
|
.lanes-workspaces { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
@@ -314,20 +180,9 @@ html.lanes-keyboard {
|
|
|
314
180
|
.lanes-sheet-panel { animation: lanes-sheet-in 150ms ease-out; }
|
|
315
181
|
@keyframes lanes-sheet-in { from {transform: translateY(20px); opacity: .4;} to {transform: translateY(0); opacity: 1;} }
|
|
316
182
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
.lanes-filter-bar {
|
|
320
|
-
display: flex; align-items: center; gap: 8px;
|
|
321
|
-
flex: 1 1 auto; min-width: 0;
|
|
322
|
-
padding: 0 12px; height: 44px;
|
|
323
|
-
background: var(--lanes-chip); border: 1px solid var(--lanes-border); border-radius: 12px;
|
|
324
|
-
}
|
|
325
|
-
.lanes-filter-bar[hidden] { display: none; }
|
|
183
|
+
/* Lane filter bar (☰ picker tab) */
|
|
184
|
+
.lanes-filter-bar { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; padding: 0 12px; height: 44px; background: var(--lanes-chip); border: 1px solid var(--lanes-border); border-radius: 12px; }
|
|
326
185
|
.lanes-filter-bar > svg { width: 18px; height: 18px; flex: 0 0 18px; color: var(--lanes-muted); }
|
|
327
|
-
.lanes-filter-bar input {
|
|
328
|
-
flex: 1 1 auto; min-width: 0;
|
|
329
|
-
background: transparent; border: 0; outline: none;
|
|
330
|
-
color: var(--lanes-text); font-size: 16px; caret-color: var(--lanes-accent);
|
|
331
|
-
}
|
|
186
|
+
.lanes-filter-bar input { flex: 1 1 auto; min-width: 0; background: transparent; border: 0; outline: none; color: var(--lanes-text); font-size: 16px; caret-color: var(--lanes-accent); }
|
|
332
187
|
.lanes-filter-bar input::placeholder { color: var(--lanes-muted); }
|
|
333
188
|
.lanes-filter-bar input::-webkit-search-cancel-button { -webkit-appearance: none; }
|