dsh-zen-remote 1.0.0
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/AGENTS.md +51 -0
- package/LICENSE +38 -0
- package/README.md +237 -0
- package/assets/home.png +0 -0
- package/assets/info.png +0 -0
- package/assets/pairing.png +0 -0
- package/assets/session.png +0 -0
- package/assets/sheet.png +0 -0
- package/cordis.patch.yml +17 -0
- package/cordis.patch.yml.example +16 -0
- package/docs/interface.md +200 -0
- package/docs/remote-access.en.md +335 -0
- package/docs/remote-access.md +422 -0
- package/dsh-push.mjs +213 -0
- package/lan-gate.mjs +78 -0
- package/lib/client.js +6615 -0
- package/lib/index.js +310 -0
- package/lib/index.js.map +1 -0
- package/lib/lan-gate-server.cjs +626 -0
- package/lib/types/client/MobileAttachButton.d.ts +25 -0
- package/lib/types/client/MobileAttachButton.d.ts.map +1 -0
- package/lib/types/client/MobileAttachChips.d.ts +32 -0
- package/lib/types/client/MobileAttachChips.d.ts.map +1 -0
- package/lib/types/client/MobileDrawerFooter.d.ts +21 -0
- package/lib/types/client/MobileDrawerFooter.d.ts.map +1 -0
- package/lib/types/client/MobileHome.d.ts +24 -0
- package/lib/types/client/MobileHome.d.ts.map +1 -0
- package/lib/types/client/MobileHomeChips.d.ts +76 -0
- package/lib/types/client/MobileHomeChips.d.ts.map +1 -0
- package/lib/types/client/MobileNavOverlay.d.ts +15 -0
- package/lib/types/client/MobileNavOverlay.d.ts.map +1 -0
- package/lib/types/client/MobileNavToggle.d.ts +18 -0
- package/lib/types/client/MobileNavToggle.d.ts.map +1 -0
- package/lib/types/client/MobileSessionHeader.d.ts +58 -0
- package/lib/types/client/MobileSessionHeader.d.ts.map +1 -0
- package/lib/types/client/MobileSessionInfo.d.ts +41 -0
- package/lib/types/client/MobileSessionInfo.d.ts.map +1 -0
- package/lib/types/client/attach-upload.d.ts +78 -0
- package/lib/types/client/attach-upload.d.ts.map +1 -0
- package/lib/types/client/chips-store.d.ts +13 -0
- package/lib/types/client/chips-store.d.ts.map +1 -0
- package/lib/types/client/debug.d.ts +9 -0
- package/lib/types/client/debug.d.ts.map +1 -0
- package/lib/types/client/effects/aionui-compat.d.ts +4 -0
- package/lib/types/client/effects/aionui-compat.d.ts.map +1 -0
- package/lib/types/client/effects/gestures.d.ts +7 -0
- package/lib/types/client/effects/gestures.d.ts.map +1 -0
- package/lib/types/client/effects/header-status.d.ts +15 -0
- package/lib/types/client/effects/header-status.d.ts.map +1 -0
- package/lib/types/client/effects/phone-chrome.d.ts +106 -0
- package/lib/types/client/effects/phone-chrome.d.ts.map +1 -0
- package/lib/types/client/effects/turn-fold.d.ts +25 -0
- package/lib/types/client/effects/turn-fold.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +18 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/locales.d.ts +58 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/nav-store.d.ts +73 -0
- package/lib/types/client/nav-store.d.ts.map +1 -0
- package/lib/types/client/session-dot.d.ts +12 -0
- package/lib/types/client/session-dot.d.ts.map +1 -0
- package/lib/types/client/styles/base.css.d.ts +2 -0
- package/lib/types/client/styles/base.css.d.ts.map +1 -0
- package/lib/types/client/styles/chips.css.d.ts +2 -0
- package/lib/types/client/styles/chips.css.d.ts.map +1 -0
- package/lib/types/client/styles/compat.css.d.ts +2 -0
- package/lib/types/client/styles/compat.css.d.ts.map +1 -0
- package/lib/types/client/styles/composer.css.d.ts +2 -0
- package/lib/types/client/styles/composer.css.d.ts.map +1 -0
- package/lib/types/client/styles/header.css.d.ts +2 -0
- package/lib/types/client/styles/header.css.d.ts.map +1 -0
- package/lib/types/client/styles/home.css.d.ts +2 -0
- package/lib/types/client/styles/home.css.d.ts.map +1 -0
- package/lib/types/client/styles/index.d.ts +15 -0
- package/lib/types/client/styles/index.d.ts.map +1 -0
- package/lib/types/client/styles/info.css.d.ts +2 -0
- package/lib/types/client/styles/info.css.d.ts.map +1 -0
- package/lib/types/client/styles/layout.css.d.ts +2 -0
- package/lib/types/client/styles/layout.css.d.ts.map +1 -0
- package/lib/types/client/styles/misc.css.d.ts +2 -0
- package/lib/types/client/styles/misc.css.d.ts.map +1 -0
- package/lib/types/client/styles/turn-fold.css.d.ts +2 -0
- package/lib/types/client/styles/turn-fold.css.d.ts.map +1 -0
- package/lib/types/index.d.ts +83 -0
- package/lib/types/index.d.ts.map +1 -0
- package/llms.txt +20 -0
- package/package.json +128 -0
- package/pwa/app.css +95 -0
- package/pwa/icons/icon-192.png +0 -0
- package/pwa/icons/icon-512.png +0 -0
- package/pwa/icons/icon-maskable-512.png +0 -0
- package/pwa/icons/icon-maskable.svg +1 -0
- package/pwa/icons/icon.svg +1 -0
- package/pwa/inject.js +190 -0
- package/pwa/manifest.json +29 -0
- package/pwa/offline.html +38 -0
- package/pwa/sw.js +176 -0
- package/pwa/touch-gestures.js +94 -0
- package/scripts/build-client.mjs +101 -0
- package/scripts/cdp-probe.mjs +64 -0
- package/scripts/check-attach-upload.mjs +99 -0
- package/scripts/check-sunk-viewport.mjs +33 -0
- package/scripts/check-upload-endpoint.mjs +168 -0
- package/src/client/MobileAttachButton.tsx +125 -0
- package/src/client/MobileAttachChips.tsx +85 -0
- package/src/client/MobileDrawerFooter.tsx +56 -0
- package/src/client/MobileHome.tsx +348 -0
- package/src/client/MobileHomeChips.tsx +426 -0
- package/src/client/MobileNavOverlay.tsx +279 -0
- package/src/client/MobileNavToggle.tsx +52 -0
- package/src/client/MobileSessionHeader.tsx +242 -0
- package/src/client/MobileSessionInfo.tsx +303 -0
- package/src/client/attach-upload.ts +140 -0
- package/src/client/chips-store.ts +41 -0
- package/src/client/debug.ts +253 -0
- package/src/client/effects/aionui-compat.ts +183 -0
- package/src/client/effects/gestures.ts +265 -0
- package/src/client/effects/header-status.ts +30 -0
- package/src/client/effects/phone-chrome.ts +265 -0
- package/src/client/effects/turn-fold.ts +258 -0
- package/src/client/index.tsx +227 -0
- package/src/client/locales.ts +109 -0
- package/src/client/nav-store.ts +78 -0
- package/src/client/session-dot.ts +17 -0
- package/src/client/styles/base.css.ts +167 -0
- package/src/client/styles/chips.css.ts +224 -0
- package/src/client/styles/compat.css.ts +681 -0
- package/src/client/styles/composer.css.ts +504 -0
- package/src/client/styles/header.css.ts +368 -0
- package/src/client/styles/home.css.ts +477 -0
- package/src/client/styles/index.ts +25 -0
- package/src/client/styles/info.css.ts +238 -0
- package/src/client/styles/layout.css.ts +501 -0
- package/src/client/styles/misc.css.ts +128 -0
- package/src/client/styles/turn-fold.css.ts +106 -0
- package/src/index.ts +343 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
// compat — split from src/client/mobile.css.ts (2026-08-16), order preserved.
|
|
2
|
+
// Do not reorder: styles/index.ts concatenates in this exact order.
|
|
3
|
+
|
|
4
|
+
export const COMPAT_CSS = ` /* ---------- dsh-web-ui family compatibility ----------
|
|
5
|
+
The linxin666 plugin suite extends the shell frame directly:
|
|
6
|
+
- aionui-panel appends two trailing grid columns (explorer / preview)
|
|
7
|
+
plus absolute drag handles to [data-dsh-frame]; its 5-track inline
|
|
8
|
+
grid is already overridden above, but the handles and columns would
|
|
9
|
+
still float over the main UI. On mobile the columns leave the grid
|
|
10
|
+
as floating bottom sheets and keep their own visibility state —
|
|
11
|
+
the suite's collapse chevron / preview tabs still work, so no
|
|
12
|
+
feature is lost. The task-board / ssh plugins inject sidebar
|
|
13
|
+
entries and center-column takeover panels; the entries need
|
|
14
|
+
spacing and the kanban needs scrollable columns. */
|
|
15
|
+
|
|
16
|
+
/* Touch devices: the drag handles are useless — the floating expand
|
|
17
|
+
button is the opener. */
|
|
18
|
+
.aionui-explorer-handle,
|
|
19
|
+
.aionui-preview-handle {
|
|
20
|
+
display: none !important;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Shared base: both columns leave the grid as floating panels. The
|
|
24
|
+
explorer is gated shut by default (its own persisted expanded state
|
|
25
|
+
must never cover the mobile UI on load); the header Files action opens
|
|
26
|
+
it via the frame marker below, and the sheet's own collapse chevron
|
|
27
|
+
clears it. Preview stays owned by the suite (hidden while no tab is
|
|
28
|
+
open). The per-column rules below override the geometry. */
|
|
29
|
+
[data-aionui-explorer-col],
|
|
30
|
+
[data-aionui-preview-col] {
|
|
31
|
+
position: fixed !important;
|
|
32
|
+
z-index: 55 !important;
|
|
33
|
+
background: var(--aion-bg-base, #ffffff) !important;
|
|
34
|
+
border-left: none !important;
|
|
35
|
+
}
|
|
36
|
+
/* Explorer (file tree) bottom sheet: bottom edge aligned exactly with
|
|
37
|
+
the composer card's bottom line — the card sits 36px above the
|
|
38
|
+
viewport bottom (8px composer padding + the 28px stats strip below
|
|
39
|
+
the card), so the sheet uses the same 36px bottom offset. */
|
|
40
|
+
[data-aionui-explorer-col] {
|
|
41
|
+
visibility: hidden !important;
|
|
42
|
+
left: 8px !important;
|
|
43
|
+
right: 8px !important;
|
|
44
|
+
top: auto !important;
|
|
45
|
+
bottom: 36px !important;
|
|
46
|
+
width: auto !important;
|
|
47
|
+
height: min(55dvh, 460px) !important;
|
|
48
|
+
max-height: calc(100dvh - 44px) !important;
|
|
49
|
+
border-radius: 14px !important;
|
|
50
|
+
overflow: hidden !important;
|
|
51
|
+
box-shadow: 0 -4px 28px rgba(0, 0, 0, .18) !important;
|
|
52
|
+
animation: dsh-mobile-nav-sheet-up .24s var(--ds-ease-out, ease-in-out) !important;
|
|
53
|
+
}
|
|
54
|
+
/* Preview (file content) bottom sheet. Gated shut by default: the suite
|
|
55
|
+
persists open preview tabs in localStorage and restores them on load,
|
|
56
|
+
which would pop the sheet over the fresh UI. The client only sets the
|
|
57
|
+
frame marker after the user taps a file row in the explorer; the
|
|
58
|
+
suite's own collapse chevron clears it via the visibility watcher. */
|
|
59
|
+
[data-aionui-preview-col] {
|
|
60
|
+
visibility: hidden !important;
|
|
61
|
+
position: fixed !important;
|
|
62
|
+
left: 8px !important;
|
|
63
|
+
right: 8px !important;
|
|
64
|
+
top: auto !important;
|
|
65
|
+
bottom: 40px !important;
|
|
66
|
+
width: auto !important;
|
|
67
|
+
height: min(50dvh, 420px) !important;
|
|
68
|
+
max-height: calc(100dvh - 48px) !important;
|
|
69
|
+
border-radius: 14px !important;
|
|
70
|
+
overflow: hidden !important;
|
|
71
|
+
box-shadow: 0 -4px 28px rgba(0, 0, 0, .18) !important;
|
|
72
|
+
z-index: 56 !important;
|
|
73
|
+
animation: dsh-mobile-nav-sheet-up .24s var(--ds-ease-out, ease-in-out) !important;
|
|
74
|
+
/* Fullscreen toggle (issue #8): animate the geometry change instead of
|
|
75
|
+
snapping. visibility is deliberately not listed, so opening/closing
|
|
76
|
+
the sheet stays instant; the open/close keyframes own transform. */
|
|
77
|
+
transition:
|
|
78
|
+
left .24s var(--ds-ease-out, ease-in-out),
|
|
79
|
+
right .24s var(--ds-ease-out, ease-in-out),
|
|
80
|
+
top .24s var(--ds-ease-out, ease-in-out),
|
|
81
|
+
bottom .24s var(--ds-ease-out, ease-in-out),
|
|
82
|
+
width .24s var(--ds-ease-out, ease-in-out),
|
|
83
|
+
height .24s var(--ds-ease-out, ease-in-out),
|
|
84
|
+
border-radius .24s var(--ds-ease-out, ease-in-out),
|
|
85
|
+
box-shadow .24s var(--ds-ease-out, ease-in-out),
|
|
86
|
+
padding-top .24s var(--ds-ease-out, ease-in-out) !important;
|
|
87
|
+
}
|
|
88
|
+
/* User-opened preview sheet (frame marker, set on file-row tap). */
|
|
89
|
+
[data-mobile-nav="frame"][data-aionui-preview-open] [data-aionui-preview-col] {
|
|
90
|
+
visibility: visible !important;
|
|
91
|
+
}
|
|
92
|
+
/* The Files action opens the explorer sheet (frame marker). */
|
|
93
|
+
[data-mobile-nav="frame"][data-aionui-explorer-open] [data-aionui-explorer-col] {
|
|
94
|
+
visibility: visible !important;
|
|
95
|
+
}
|
|
96
|
+
/* While the preview sheet is up, the explorer sheet yields (two stacked
|
|
97
|
+
bottom sheets would read as one broken overlay). Closing the preview
|
|
98
|
+
via its collapse chevron / tab close clears the marker, and the
|
|
99
|
+
explorer sheet returns. Same specificity as the explorer-open rule, so
|
|
100
|
+
this must stay AFTER it. */
|
|
101
|
+
[data-mobile-nav="frame"][data-aionui-preview-open] [data-aionui-explorer-col] {
|
|
102
|
+
visibility: hidden !important;
|
|
103
|
+
}
|
|
104
|
+
/* The open drawer must never sit under a sheet: while the frame is in the
|
|
105
|
+
narrow-expanded state both sheets yield (later in the file than the
|
|
106
|
+
open marker rule, so it wins at equal specificity). The fullscreen
|
|
107
|
+
toggle has its own drawer-open rule at the end of its section. */
|
|
108
|
+
[data-mobile-nav="frame"]:not([data-sidebar-collapsed]) [data-aionui-explorer-col],
|
|
109
|
+
[data-mobile-nav="frame"]:not([data-sidebar-collapsed]) [data-aionui-preview-col] {
|
|
110
|
+
visibility: hidden !important;
|
|
111
|
+
display: none !important;
|
|
112
|
+
}
|
|
113
|
+
/* The suite's own expand button reads the store state we bypass on
|
|
114
|
+
mobile — hide it; the header Files action is the opener. */
|
|
115
|
+
.aionui-floating-expand {
|
|
116
|
+
display: none !important;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Preview sheet fullscreen toggle (issue #8): a fixed button parked in the
|
|
120
|
+
sheet's titlebar row, just left of the suite's collapse chevron (24px at
|
|
121
|
+
right:8px of the sheet, and the sheet spans 8px..(100vw-8px)). The top
|
|
122
|
+
calc mirrors the sheet geometry above (bottom 40px + min(50dvh, 420px));
|
|
123
|
+
when the frame carries "data-mobile-preview-full" the sheet goes
|
|
124
|
+
fullscreen and the button moves to the viewport corner. */
|
|
125
|
+
[data-mobile-nav="preview-full-toggle"] {
|
|
126
|
+
position: absolute !important;
|
|
127
|
+
right: 36px !important;
|
|
128
|
+
top: 8px !important;
|
|
129
|
+
z-index: 57 !important;
|
|
130
|
+
display: none !important;
|
|
131
|
+
align-items: center;
|
|
132
|
+
justify-content: center;
|
|
133
|
+
width: 20px;
|
|
134
|
+
height: 20px;
|
|
135
|
+
padding: 0;
|
|
136
|
+
border: none;
|
|
137
|
+
border-radius: 4px;
|
|
138
|
+
background: transparent;
|
|
139
|
+
color: var(--aion-text-secondary, var(--dsw-alias-label-secondary, inherit));
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
-webkit-tap-highlight-color: transparent;
|
|
142
|
+
/* Native look: same size/radius/hover language as the suite's tab-bar
|
|
143
|
+
icon buttons (the 20px panelCollapse next to it). The button lives
|
|
144
|
+
INSIDE the preview column, so it rides the sheet's own open
|
|
145
|
+
animation and geometry transition — no curve matching needed. */
|
|
146
|
+
transition: background-color .15s, top .24s var(--ds-ease-out, ease-in-out);
|
|
147
|
+
}
|
|
148
|
+
[data-mobile-nav="preview-full-toggle"]:hover {
|
|
149
|
+
background: var(--aion-bg-3, rgba(0, 0, 0, .22));
|
|
150
|
+
}
|
|
151
|
+
[data-mobile-nav="preview-full-toggle"]:active {
|
|
152
|
+
background: var(--aion-bg-active, rgba(0, 0, 0, .28));
|
|
153
|
+
}
|
|
154
|
+
[data-mobile-nav="preview-full-toggle"]:focus-visible {
|
|
155
|
+
outline: 2px solid var(--dsw-alias-state-business-primary, #4f6ef7);
|
|
156
|
+
outline-offset: 2px;
|
|
157
|
+
}
|
|
158
|
+
[data-mobile-nav="preview-full-toggle"] svg {
|
|
159
|
+
width: 14px;
|
|
160
|
+
height: 14px;
|
|
161
|
+
}
|
|
162
|
+
/* Keep the last tab (and the "+" URL-tab trigger) from sliding under the
|
|
163
|
+
fullscreen toggle: reserve the right end of the preview tab row. */
|
|
164
|
+
[data-aionui-preview-col] [class$="_tabScroll"] {
|
|
165
|
+
padding-right: 34px !important;
|
|
166
|
+
}
|
|
167
|
+
/* Visible only while the preview sheet is open. Visibility itself is
|
|
168
|
+
inherited from the column, so the sheet's own hide rules (collapse,
|
|
169
|
+
drawer open) cover the button too. */
|
|
170
|
+
[data-mobile-nav="frame"][data-aionui-preview-open] [data-aionui-preview-col] [data-mobile-nav="preview-full-toggle"] {
|
|
171
|
+
display: inline-flex !important;
|
|
172
|
+
}
|
|
173
|
+
/* Icon swap on the frame fullscreen marker. */
|
|
174
|
+
[data-mobile-nav="preview-full-toggle"] .dsh-mobile-nav-full-out {
|
|
175
|
+
display: none !important;
|
|
176
|
+
}
|
|
177
|
+
[data-mobile-nav="frame"][data-mobile-preview-full] [data-aionui-preview-col] [data-mobile-nav="preview-full-toggle"] .dsh-mobile-nav-full-in {
|
|
178
|
+
display: none !important;
|
|
179
|
+
}
|
|
180
|
+
[data-mobile-nav="frame"][data-mobile-preview-full] [data-aionui-preview-col] [data-mobile-nav="preview-full-toggle"] .dsh-mobile-nav-full-out {
|
|
181
|
+
display: inline !important;
|
|
182
|
+
}
|
|
183
|
+
/* Fullscreen preview: the sheet fills the whole viewport (notch included);
|
|
184
|
+
the safe-area padding drops the titlebar row below the status bar, and
|
|
185
|
+
the toggle follows the titlebar into the top corner. */
|
|
186
|
+
[data-mobile-nav="frame"][data-aionui-preview-open][data-mobile-preview-full] [data-aionui-preview-col] {
|
|
187
|
+
inset: 0 !important;
|
|
188
|
+
left: 0 !important;
|
|
189
|
+
right: 0 !important;
|
|
190
|
+
top: 0 !important;
|
|
191
|
+
bottom: 0 !important;
|
|
192
|
+
width: 100% !important;
|
|
193
|
+
height: 100dvh !important;
|
|
194
|
+
max-height: none !important;
|
|
195
|
+
box-sizing: border-box !important;
|
|
196
|
+
padding-top: var(--mnav-sat) !important;
|
|
197
|
+
border-radius: 0 !important;
|
|
198
|
+
box-shadow: none !important;
|
|
199
|
+
z-index: 57 !important;
|
|
200
|
+
animation: none !important;
|
|
201
|
+
}
|
|
202
|
+
/* Fullscreen: the column fills the viewport, so the button follows the
|
|
203
|
+
titlebar row down below the notch. */
|
|
204
|
+
[data-mobile-nav="frame"][data-mobile-preview-full] [data-aionui-preview-col] [data-mobile-nav="preview-full-toggle"] {
|
|
205
|
+
top: calc(var(--mnav-sat) + 8px) !important;
|
|
206
|
+
}
|
|
207
|
+
@media (prefers-reduced-motion: reduce) {
|
|
208
|
+
[data-aionui-preview-col],
|
|
209
|
+
[data-mobile-nav="preview-full-toggle"] {
|
|
210
|
+
transition: none !important;
|
|
211
|
+
animation: none !important;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* dsh-web-ui sidebar entries (task board / ssh) sit flush against each
|
|
216
|
+
other — give the injected rows breathing room. */
|
|
217
|
+
button[data-dsh-taskboard-entry],
|
|
218
|
+
button[data-dsh-ssh-entry] {
|
|
219
|
+
margin-bottom: 8px !important;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Task board: five kanban columns at minmax(0,1fr) crush into ~78px phone
|
|
223
|
+
strips. Give every column a usable minimum and let the row scroll. */
|
|
224
|
+
[data-dsh-taskboard-board] > [class$="_columns"] {
|
|
225
|
+
grid-template-columns: repeat(5, minmax(240px, 1fr)) !important;
|
|
226
|
+
overflow-x: auto !important;
|
|
227
|
+
}
|
|
228
|
+
/* The floating button must not float over a takeover panel (task board /
|
|
229
|
+
ssh own the center column while active). */
|
|
230
|
+
html[data-dsh-taskboard-active] [data-mobile-nav="fab"],
|
|
231
|
+
html[data-dsh-ssh-active] [data-mobile-nav="fab"],
|
|
232
|
+
html[data-dsh-taskboard-active] [data-mobile-nav="backdrop"],
|
|
233
|
+
html[data-dsh-ssh-active] [data-mobile-nav="backdrop"] {
|
|
234
|
+
display: none !important;
|
|
235
|
+
}
|
|
236
|
+
/* Board header: let the search field take the slack instead of squeezing
|
|
237
|
+
the action buttons. */
|
|
238
|
+
[data-dsh-taskboard-board] > [class$="_boardHeader"] [class$="_search"] {
|
|
239
|
+
flex: 1 1 auto !important;
|
|
240
|
+
min-width: 80px !important;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ---------- dsh-web-ui polish: plugin market search ----------
|
|
244
|
+
The market tab row (Discover / Themes / Installed + the plugin search
|
|
245
|
+
box) is a no-wrap flex: at 390px the tabs plus the ~218px search box
|
|
246
|
+
(~475px total) overflow the ~334px sheet and the search box runs off
|
|
247
|
+
the right edge of the screen (it also forces a horizontal scrollbar on
|
|
248
|
+
the sheet's options area). Let the row wrap: the tabs keep the first
|
|
249
|
+
line and the search box gets its own full-width second line. */
|
|
250
|
+
|
|
251
|
+
[aria-modal="true"] [class$="_tabs"] {
|
|
252
|
+
flex-wrap: wrap !important;
|
|
253
|
+
row-gap: 8px !important;
|
|
254
|
+
}
|
|
255
|
+
[aria-modal="true"] [class$="_searchInline"] {
|
|
256
|
+
flex: 1 1 100% !important;
|
|
257
|
+
width: 100% !important;
|
|
258
|
+
max-width: 100% !important;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* ---------- dsh-usage-stats polish: usage & balance panel ----------
|
|
262
|
+
The panel's stats row shows three token counters side by side
|
|
263
|
+
(today / month / total). The counters use tabular nowrap figures whose
|
|
264
|
+
min-content width overflows the ~336px panel body on a phone: figures
|
|
265
|
+
clip at the row's edges and the panel grows a horizontal scrollbar.
|
|
266
|
+
Stack the three counters vertically — full-width rows, so the figures
|
|
267
|
+
always fit. */
|
|
268
|
+
|
|
269
|
+
[class*="usg_"][class$="_statsRow"] {
|
|
270
|
+
flex-direction: column !important;
|
|
271
|
+
}
|
|
272
|
+
[class*="usg_"][class$="_stat"] {
|
|
273
|
+
flex: 0 0 auto !important;
|
|
274
|
+
width: 100% !important;
|
|
275
|
+
min-width: 0 !important;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* ---------- dsh-web-ui polish: settings sheet ----------
|
|
279
|
+
The official dialog is a desktop two-column form; on a phone the
|
|
280
|
+
label/control split leaves a huge dead gap and long descriptions wrap
|
|
281
|
+
into tall stacks. Stack each row (text above, control full-width) and
|
|
282
|
+
keep the nav tabs on ONE horizontally scrolling row. */
|
|
283
|
+
|
|
284
|
+
/* Nav tabs: single scrolling row instead of the 3-per-row grid — seven
|
|
285
|
+
categories wrap into three rows on a phone (~130px of sheet height);
|
|
286
|
+
one row with a thin scrollbar keeps every tab reachable and returns
|
|
287
|
+
that space to the options area (user feedback 2026-08-16). An earlier
|
|
288
|
+
one-row attempt had no scroll affordance and silently cut the last
|
|
289
|
+
tab off; the thin scrollbar IS the affordance. Scoped to the frame
|
|
290
|
+
marker: the desktop dialog keeps its official vertical nav column. */
|
|
291
|
+
[data-mobile-nav="frame"] [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])):not(:has([class*="ZuhsRW"])) > :first-child [class$="_navList"] {
|
|
292
|
+
display: flex !important;
|
|
293
|
+
flex-wrap: nowrap !important;
|
|
294
|
+
overflow-x: auto !important;
|
|
295
|
+
overflow-y: hidden !important;
|
|
296
|
+
gap: 6px !important;
|
|
297
|
+
width: 100% !important;
|
|
298
|
+
scrollbar-width: thin !important;
|
|
299
|
+
-webkit-overflow-scrolling: touch !important;
|
|
300
|
+
}
|
|
301
|
+
/* Hairline scrollbar for the tab row: the default WebKit scrollbar reads
|
|
302
|
+
fat on a phone; 2px keeps the scroll affordance without the bulk. */
|
|
303
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_navList"]::-webkit-scrollbar {
|
|
304
|
+
height: 2px !important;
|
|
305
|
+
}
|
|
306
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_navList"]::-webkit-scrollbar-thumb {
|
|
307
|
+
background: var(--dsw-alias-border-l2, rgba(0, 0, 0, .22)) !important;
|
|
308
|
+
border-radius: 1px !important;
|
|
309
|
+
}
|
|
310
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_navList"]::-webkit-scrollbar-track {
|
|
311
|
+
background: transparent !important;
|
|
312
|
+
}
|
|
313
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_navCell"] {
|
|
314
|
+
flex: 0 0 auto !important;
|
|
315
|
+
white-space: nowrap !important;
|
|
316
|
+
padding: 6px 8px !important;
|
|
317
|
+
gap: 6px !important;
|
|
318
|
+
font-size: 13px !important;
|
|
319
|
+
justify-content: flex-start !important;
|
|
320
|
+
}
|
|
321
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_navCell"] svg {
|
|
322
|
+
width: 14px !important;
|
|
323
|
+
height: 14px !important;
|
|
324
|
+
flex: none !important;
|
|
325
|
+
}
|
|
326
|
+
/* Content toolbar: the "Open configuration file" button is hidden on
|
|
327
|
+
mobile — it is rarely needed on a phone and steals ~180px from the
|
|
328
|
+
tab row's scroll area (user feedback 2026-08-16). Only the close ✕
|
|
329
|
+
stays, flush right in the nav row. Desktop untouched (frame scoped). */
|
|
330
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_header"] [class$="_actions"] {
|
|
331
|
+
display: none !important;
|
|
332
|
+
}
|
|
333
|
+
[data-mobile-nav="frame"] [aria-modal="true"] [class$="_header"] [class$="_actions"] [class$="_action"] {
|
|
334
|
+
font-size: 13px !important;
|
|
335
|
+
padding: 6px 12px !important;
|
|
336
|
+
min-height: 0 !important;
|
|
337
|
+
}
|
|
338
|
+
/* Setting rows: text on top, control below at full width. */
|
|
339
|
+
[aria-modal="true"] [class$="_section"] [class$="_row"] {
|
|
340
|
+
flex-direction: column !important;
|
|
341
|
+
align-items: stretch !important;
|
|
342
|
+
gap: 8px !important;
|
|
343
|
+
}
|
|
344
|
+
[aria-modal="true"] [class$="_section"] [class$="_row"] > :first-child {
|
|
345
|
+
width: 100% !important;
|
|
346
|
+
max-width: none !important;
|
|
347
|
+
}
|
|
348
|
+
[aria-modal="true"] [class$="_section"] [class$="_row"] > :last-child {
|
|
349
|
+
width: 100% !important;
|
|
350
|
+
max-width: none !important;
|
|
351
|
+
}
|
|
352
|
+
/* Appearance mode group: give the cube row a consistent bordered
|
|
353
|
+
segmented look (the official borders differ per state). */
|
|
354
|
+
[aria-modal="true"] [class$="_cubeRow"] > * {
|
|
355
|
+
border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, .12)) !important;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* ---------- dsh-web-ui polish: explorer sheet ----------
|
|
359
|
+
The aionui explorer was designed for a desktop side column: compact the
|
|
360
|
+
header, search box and tree rows so a phone shows more entries, and pad
|
|
361
|
+
the scroll bottom so the last row never sits flush on the edge. */
|
|
362
|
+
|
|
363
|
+
[data-aionui-explorer-col] [class$="_tabBar"] {
|
|
364
|
+
height: 36px !important;
|
|
365
|
+
}
|
|
366
|
+
[data-aionui-explorer-col] [class$="_tabBtn"],
|
|
367
|
+
[data-aionui-explorer-col] [class$="_tabBtnActive"] {
|
|
368
|
+
padding: 0 12px !important;
|
|
369
|
+
font-size: 13px !important;
|
|
370
|
+
}
|
|
371
|
+
[data-aionui-explorer-col] [class$="_searchBox"] {
|
|
372
|
+
height: 32px !important;
|
|
373
|
+
font-size: 13px !important;
|
|
374
|
+
}
|
|
375
|
+
[data-aionui-explorer-col] [class*="_treeRow"] {
|
|
376
|
+
height: 30px !important;
|
|
377
|
+
font-size: 13px !important;
|
|
378
|
+
}
|
|
379
|
+
[data-aionui-explorer-col] [class*="_treeRow"] svg {
|
|
380
|
+
width: 14px !important;
|
|
381
|
+
height: 14px !important;
|
|
382
|
+
}
|
|
383
|
+
[data-aionui-explorer-col] [class$="_scrollArea"] {
|
|
384
|
+
padding-bottom: 28px !important;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/* ---------- dsh-web-ui polish: drawer footer ----------
|
|
388
|
+
The injected footer actions (Files + Session log) become two equal pill
|
|
389
|
+
buttons instead of text-width capsules. */
|
|
390
|
+
|
|
391
|
+
/* The official footerActions row also hosts the remote-web-ui entry
|
|
392
|
+
row (two icon buttons); without wrapping the two groups squeeze each
|
|
393
|
+
other on one line. Wrap so each group gets its own full-width row. */
|
|
394
|
+
[data-mobile-nav="frame"] [class$="_footerActions"] {
|
|
395
|
+
flex-wrap: wrap !important;
|
|
396
|
+
gap: 6px !important;
|
|
397
|
+
}
|
|
398
|
+
[data-mobile-nav="drawer-actions"] {
|
|
399
|
+
width: 100% !important;
|
|
400
|
+
}
|
|
401
|
+
[data-mobile-nav="drawer-actions"] > button {
|
|
402
|
+
flex: 1 1 0 !important;
|
|
403
|
+
padding: 0 8px !important;
|
|
404
|
+
white-space: nowrap !important;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* ---------- dsh-web-ui polish: floating pet ----------
|
|
408
|
+
The whale-girl pet (dsh-pet) floats at the viewport corner with a
|
|
409
|
+
persisted, draggable position. On phones the pet is scaled down so
|
|
410
|
+
it does not dominate the screen; the plugin's own drag + persist
|
|
411
|
+
still work (the position itself is left alone — the mobile default
|
|
412
|
+
position is seeded via the pet API to just above the composer). */
|
|
413
|
+
|
|
414
|
+
body > [class$="_float"]:has([class$="_sprite"][role="button"]) {
|
|
415
|
+
transform: scale(.66);
|
|
416
|
+
transform-origin: bottom right;
|
|
417
|
+
}
|
|
418
|
+
/* While a modal dialog (settings sheet / export) owns the screen the pet
|
|
419
|
+
floats ABOVE it and covers the dialog content; modal semantics say the
|
|
420
|
+
background is inert, so hide the pet for the modal's lifetime. */
|
|
421
|
+
body:has([aria-modal="true"]) > [class$="_float"]:has([class$="_sprite"][role="button"]) {
|
|
422
|
+
display: none !important;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/* ---------- conversation stats line ----------
|
|
426
|
+
The official session-status row (turns / steps / LLM time / TTFT /
|
|
427
|
+
cache) is long. It is the single entry the official StatsLine puts in
|
|
428
|
+
conversation.composer.dock, so the structural anchor below reaches it
|
|
429
|
+
without any DOM marking (S3 deleted the text-matching effect that used
|
|
430
|
+
to set [data-slot="conversation.composer.dock"] > [class$="_root"]). Layout: ONE fixed-height (28px) flex
|
|
431
|
+
strip that scrolls horizontally — the full metrics stream stays
|
|
432
|
+
reachable by swiping, the row never grows vertically, no ellipsis or
|
|
433
|
+
fade, 12px gaps between metric groups, a 2px scrollbar as the swipe
|
|
434
|
+
affordance. The phone breakpoint hides the strip outright instead
|
|
435
|
+
(styles/composer.css.ts — its data moves into the session info card). */
|
|
436
|
+
|
|
437
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"] {
|
|
438
|
+
display: flex !important;
|
|
439
|
+
flex-flow: row nowrap !important;
|
|
440
|
+
align-items: center !important;
|
|
441
|
+
width: 100% !important;
|
|
442
|
+
max-width: 100% !important;
|
|
443
|
+
min-width: 0 !important;
|
|
444
|
+
height: 28px !important;
|
|
445
|
+
min-height: 28px !important;
|
|
446
|
+
max-height: 28px !important;
|
|
447
|
+
box-sizing: border-box !important;
|
|
448
|
+
white-space: nowrap !important;
|
|
449
|
+
overflow-x: auto !important;
|
|
450
|
+
overflow-y: hidden !important;
|
|
451
|
+
-webkit-overflow-scrolling: touch;
|
|
452
|
+
overscroll-behavior-x: contain;
|
|
453
|
+
scrollbar-width: thin !important;
|
|
454
|
+
scrollbar-color: var(--dsw-alias-border-l1, rgba(0, 0, 0, .28)) transparent !important;
|
|
455
|
+
padding: 0 0 4px !important;
|
|
456
|
+
line-height: 20px !important;
|
|
457
|
+
font-size: 12px !important;
|
|
458
|
+
}
|
|
459
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"]::-webkit-scrollbar {
|
|
460
|
+
height: 2px !important;
|
|
461
|
+
}
|
|
462
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"]::-webkit-scrollbar-thumb {
|
|
463
|
+
background: var(--dsw-alias-label-tertiary, rgba(0, 0, 0, .3)) !important;
|
|
464
|
+
border-radius: 2px !important;
|
|
465
|
+
}
|
|
466
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"]::-webkit-scrollbar-track {
|
|
467
|
+
background: transparent !important;
|
|
468
|
+
}
|
|
469
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"] > * {
|
|
470
|
+
display: flex !important;
|
|
471
|
+
flex: 0 0 auto !important;
|
|
472
|
+
flex-flow: row nowrap !important;
|
|
473
|
+
align-items: center !important;
|
|
474
|
+
width: max-content !important;
|
|
475
|
+
min-width: max-content !important;
|
|
476
|
+
max-width: none !important;
|
|
477
|
+
white-space: nowrap !important;
|
|
478
|
+
margin-right: 12px !important;
|
|
479
|
+
padding: 0 !important;
|
|
480
|
+
}
|
|
481
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"] > *:last-child {
|
|
482
|
+
margin-right: 0 !important;
|
|
483
|
+
}
|
|
484
|
+
[data-slot="conversation.composer.dock"] > [class$="_root"] * {
|
|
485
|
+
white-space: nowrap !important;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/* ---------- dsh-genui panel dock ----------
|
|
489
|
+
The genui panel docks above the composer (conversation.input.dock,
|
|
490
|
+
id genui-panel). On a phone its business-blue outline, generous chrome
|
|
491
|
+
and single-line ellipsis read as an unfinished artifact: long titles
|
|
492
|
+
truncate mid-word ("…default b···") with the chevron glued to the
|
|
493
|
+
ellipsis, and the pill crowds the composer. Mobile treatment: neutral
|
|
494
|
+
card border matching the composer, tighter chrome so the full title
|
|
495
|
+
fits, chevron with breathing room. Scoped to the mobile frame marker —
|
|
496
|
+
desktop keeps genui's own styling untouched. */
|
|
497
|
+
|
|
498
|
+
[data-mobile-nav="frame"] [data-genui-panel] {
|
|
499
|
+
margin: 6px 12px 4px !important;
|
|
500
|
+
border-color: var(--dsw-alias-border-l1, rgba(0, 0, 0, .12)) !important;
|
|
501
|
+
border-radius: 12px !important;
|
|
502
|
+
}
|
|
503
|
+
[data-mobile-nav="frame"] [data-genui-panel] [class*="_panelToggle"] {
|
|
504
|
+
padding: 7px 12px !important;
|
|
505
|
+
gap: 8px !important;
|
|
506
|
+
}
|
|
507
|
+
[data-mobile-nav="frame"] [data-genui-panel] [class*="_panelBadge"] {
|
|
508
|
+
padding: 0 7px !important;
|
|
509
|
+
border-radius: 5px !important;
|
|
510
|
+
font-size: 10.5px !important;
|
|
511
|
+
line-height: 1.7 !important;
|
|
512
|
+
}
|
|
513
|
+
[data-mobile-nav="frame"] [data-genui-panel] [class*="_panelTitle"] {
|
|
514
|
+
flex: 1 1 auto !important;
|
|
515
|
+
min-width: 0 !important;
|
|
516
|
+
font-size: 12.5px !important;
|
|
517
|
+
line-height: 1.45 !important;
|
|
518
|
+
}
|
|
519
|
+
[data-mobile-nav="frame"] [data-genui-panel] [class*="_panelChevron"] {
|
|
520
|
+
flex: none !important;
|
|
521
|
+
margin-left: 0 !important;
|
|
522
|
+
padding-left: 4px !important;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/* ---------- git-graph branch chip: inside the composer card ----------
|
|
526
|
+
The branch chip (conversation.input.dock) floats between the dock rows
|
|
527
|
+
and the input card; on a phone it reads as a stray capsule crowding the
|
|
528
|
+
composer. A client effect (MobileNavOverlay) reparents the chip INTO
|
|
529
|
+
the composer card; these rules pin it to the card's top-left and give
|
|
530
|
+
the card a dedicated chip row. The card is position: relative by the
|
|
531
|
+
official stylesheet, so the absolute anchor resolves against it. The
|
|
532
|
+
plugin's own sheet sets all four offsets on the anchor, so right/bottom
|
|
533
|
+
must be neutralized too. Scope is the frame marker + the anchor
|
|
534
|
+
attribute (NOT the dock slot — the reparenting moves the chip out of
|
|
535
|
+
the dock's subtree). Desktop untouched: the frame marker only exists
|
|
536
|
+
below 1024px, and the effect restores the chip to the dock when the
|
|
537
|
+
viewport widens. Chip row geometry (2026-08-16, user feedback): 48px
|
|
538
|
+
padding left a 16px dead gap between the chip and the input line and
|
|
539
|
+
made the composer read too tall; the row is now 40px = chip (24px) at
|
|
540
|
+
top 12px + ~4px to the textarea — the chip sits slightly lower and
|
|
541
|
+
the gap is compressed without touching the official height budget
|
|
542
|
+
further. */
|
|
543
|
+
|
|
544
|
+
[data-mobile-nav="frame"] [data-gitgraph-chip-anchor] {
|
|
545
|
+
position: absolute !important;
|
|
546
|
+
top: 12px !important;
|
|
547
|
+
left: 12px !important;
|
|
548
|
+
right: auto !important;
|
|
549
|
+
bottom: auto !important;
|
|
550
|
+
z-index: 1 !important;
|
|
551
|
+
}
|
|
552
|
+
[data-mobile-nav="frame"] [class$="_card"]:has([data-gitgraph-chip-anchor]) {
|
|
553
|
+
padding-top: 40px !important;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* ---------- dsh-better-sidebar: safe area (S2.1, 2026-08-17) ----------
|
|
557
|
+
THIRD-PARTY COMPAT RULE — dsh-better-sidebar (the workbench the session
|
|
558
|
+
header's panel button opens). Its shell is viewport-fixed and starts at
|
|
559
|
+
y=0: the panel at inset 0 (100vw drawer below 768px, a right column
|
|
560
|
+
above it) and the toggle cluster at top:3px. Neither knows about
|
|
561
|
+
env(safe-area-inset-*), so on a notched iPhone the whole tab strip —
|
|
562
|
+
including the one button that CLOSES the panel — sits behind the status
|
|
563
|
+
bar and cannot be tapped: the user opened the workbench and was stuck
|
|
564
|
+
there (real-device report, 2026-08-17).
|
|
565
|
+
Applied across the plugin's whole mobile band, not just <768px: the
|
|
566
|
+
panel is fixed at top:0 in the 768-1023px range too, so the same notch
|
|
567
|
+
covers the same tab strip. Zero effect wherever the inset is 0 (every
|
|
568
|
+
desktop browser, every non-notched device) — desktop is >=1024px and
|
|
569
|
+
out of this media block entirely.
|
|
570
|
+
Anchors: the plugin's own mount marker [data-dsh-better-sidebar]
|
|
571
|
+
(index.tsx) plus class-suffix selectors, per this repo's hashed-class
|
|
572
|
+
convention. --dsh-title-bar-strip is the plugin's own title-bar-compat
|
|
573
|
+
offset (set only while that mode is on, 0px fallback otherwise): adding
|
|
574
|
+
to it keeps both offsets rather than clobbering theirs.
|
|
575
|
+
!important because their :global(body[...]) rules outrank a plain
|
|
576
|
+
attribute selector.
|
|
577
|
+
No box-sizing here on purpose: the panel is position:fixed with BOTH
|
|
578
|
+
top and bottom set, so its used height already resolves to
|
|
579
|
+
"containing block - insets - padding - border" (CSS 2.1 10.6.4) and the
|
|
580
|
+
padding shrinks the content box without any help. Forcing border-box
|
|
581
|
+
also folds their 1px left border into the inline width — measured as a
|
|
582
|
+
1px panel-width change at 768px, i.e. a regression outside this
|
|
583
|
+
hotfix's remit. */
|
|
584
|
+
[data-dsh-better-sidebar] [class$="_panel"],
|
|
585
|
+
[data-dsh-better-sidebar] [class$="_panelHidden"] {
|
|
586
|
+
padding-top: calc(var(--mnav-sat) + var(--dsh-title-bar-strip, 0px)) !important;
|
|
587
|
+
}
|
|
588
|
+
/* Only the tablet/desktop range still shows the cluster (see the hide
|
|
589
|
+
rule below) — the notch offset is now dead weight below 768px, so it
|
|
590
|
+
is scoped out rather than left applying invisibly. min-width:768px
|
|
591
|
+
rather than the more common max-width pairing: this offset was
|
|
592
|
+
already a harmless no-op at >=1024px before S3.1 (--mnav-sat is 0 on
|
|
593
|
+
every desktop browser), so narrowing its floor to 768px changes
|
|
594
|
+
nothing there either — it only stops evaluating on phone. */
|
|
595
|
+
@media (min-width: 768px) {
|
|
596
|
+
[data-dsh-better-sidebar] [class$="_toggleCluster"] {
|
|
597
|
+
top: calc(var(--mnav-sat) + var(--dsh-title-bar-strip, 0px) + 3px) !important;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* ---------- dsh-better-sidebar: hide the phone toggle cluster (S3.1, 2026-08-17) ----------
|
|
602
|
+
Real-device round 2 feedback: this fixed top-right cluster duplicates
|
|
603
|
+
— and visually overlaps — the session header's own workbench button
|
|
604
|
+
(MobileSessionHeader.tsx), which already opens/closes the same panel
|
|
605
|
+
by clicking this cluster's toggle button through. Hidden below 768px
|
|
606
|
+
only; the 768-1023px tablet range has no workbench button (header.css.ts
|
|
607
|
+
scopes that entire reflow to <768px) and still depends on this cluster
|
|
608
|
+
as its only entry point, so it stays exactly as v1.0.0/S2.1 shipped it
|
|
609
|
+
there. The cluster also holds the panel's own close affordance — see
|
|
610
|
+
[data-mobile-nav="better-sidebar-close"] below for the phone
|
|
611
|
+
replacement, wired up in MobileSessionHeader.tsx. */
|
|
612
|
+
@media (max-width: 767px) {
|
|
613
|
+
[data-dsh-better-sidebar] [class$="_toggleCluster"] {
|
|
614
|
+
display: none !important;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/* ---------- dsh-better-sidebar: phone close button (S3.1 follow-up, 2026-08-17) ----------
|
|
619
|
+
Appended to document.body by MobileSessionHeader.tsx's
|
|
620
|
+
MobileHeaderUtilities effect — never inside the panel's own subtree
|
|
621
|
+
(the third party's React re-renders would wipe it) and never under any
|
|
622
|
+
transformed/backdrop-filter ancestor (the S4 info-card WebKit lesson in
|
|
623
|
+
AGENTS.md: position:fixed re-anchors to the nearest such ancestor
|
|
624
|
+
instead of the viewport). Default hidden — belt-and-braces, same
|
|
625
|
+
reasoning as header.css.ts's [data-mobile-nav="header-info"] etc list:
|
|
626
|
+
React does not know about media queries. Shown only below 768px AND
|
|
627
|
+
only while the panel is actually open: the panel's own class name ends
|
|
628
|
+
in "_panel" exclusively in the open state (the "_panelHidden" suffix is
|
|
629
|
+
appended once closed, so the string no longer ends in "_panel") — a
|
|
630
|
+
pure-CSS :has() open/closed read, no MutationObserver required.
|
|
631
|
+
|
|
632
|
+
Bottom-center pill, not a top-right circle (real-device follow-up,
|
|
633
|
+
2026-08-17): a top-right position collided with the panel's own
|
|
634
|
+
per-tab toolbar controls — measured live at 390px with the explorer
|
|
635
|
+
tab open, the panel's Refresh button sits at x:354-382 y:93-121, and a
|
|
636
|
+
44px circle at top:8px+safe-area/right:8px lands at x:338-382
|
|
637
|
+
y:(safe-area+8)-(safe-area+52), a direct overlap once the safe-area
|
|
638
|
+
offset is small (or zero on non-notched phones). Every per-tab toolbar
|
|
639
|
+
(explorer/git/tabBar) lives at the panel's TOP; nothing in the
|
|
640
|
+
default explorer or git tabs reaches the bottom 90px of the viewport
|
|
641
|
+
(checked live, both tabs, 2026-08-17), so bottom-center is clear
|
|
642
|
+
regardless of which tab is open — one fixed position that does not
|
|
643
|
+
need per-tab-type coordinates to dodge. */
|
|
644
|
+
/* Default-hidden for this pill lives in header.css.ts, NOT here: this
|
|
645
|
+
whole file sits inside the shared (max-width: 1023px) block, so a rule
|
|
646
|
+
here can never hide anything at desktop widths (learned the hard way,
|
|
647
|
+
2026-08-17 desktop leak). */
|
|
648
|
+
@media (max-width: 767px) {
|
|
649
|
+
body:has([data-dsh-better-sidebar] [class$="_panel"]) [data-mobile-nav="better-sidebar-close"] {
|
|
650
|
+
display: flex !important;
|
|
651
|
+
position: fixed;
|
|
652
|
+
left: 50%;
|
|
653
|
+
bottom: calc(var(--mnav-sab) + 12px);
|
|
654
|
+
transform: translateX(-50%);
|
|
655
|
+
z-index: 70;
|
|
656
|
+
align-items: center;
|
|
657
|
+
gap: 6px;
|
|
658
|
+
height: 44px;
|
|
659
|
+
padding: 0 18px;
|
|
660
|
+
border: none;
|
|
661
|
+
border-radius: 999px;
|
|
662
|
+
background: var(--dsw-alias-bg-base, #fff);
|
|
663
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, .2);
|
|
664
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
665
|
+
font-family: inherit;
|
|
666
|
+
font-size: 14px;
|
|
667
|
+
font-weight: 600;
|
|
668
|
+
cursor: pointer;
|
|
669
|
+
touch-action: manipulation;
|
|
670
|
+
-webkit-tap-highlight-color: transparent;
|
|
671
|
+
}
|
|
672
|
+
body:has([data-dsh-better-sidebar] [class$="_panel"]) [data-mobile-nav="better-sidebar-close"] svg {
|
|
673
|
+
width: 14px;
|
|
674
|
+
height: 14px;
|
|
675
|
+
flex: none;
|
|
676
|
+
}
|
|
677
|
+
body:has([data-dsh-better-sidebar] [class$="_panel"]) [data-mobile-nav="better-sidebar-close"]:active {
|
|
678
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, .06));
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
`
|