dsh-pocket 2.1.3 → 2.2.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/README.en.md +1 -1
- package/README.md +1 -1
- package/client/client.js +70 -9
- package/client/mobile/layout-mode.mjs +46 -0
- package/client/mobile/mobile-apply.tsx +17 -6
- package/client/mobile/mobile.css.ts +34 -3
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -191,7 +191,7 @@ Such tools take over all traffic and often cut cloudflared's tunnel-edge connect
|
|
|
191
191
|
```sh
|
|
192
192
|
npm install
|
|
193
193
|
node client/build.mjs # rebuild after editing client/
|
|
194
|
-
npm test # proxy / auth / compression / tunnel / service / RPC / settings (
|
|
194
|
+
npm test # proxy / auth / compression / tunnel / service / RPC / settings (93 tests)
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
**Want to try your changes locally without publishing?** Point the installed plugin at your local checkout with a symlink and restart dsh web. Full steps (including switching back to the npm release) are in [LOCAL-DEV.md](./LOCAL-DEV.md).
|
package/README.md
CHANGED
|
@@ -192,7 +192,7 @@ npx @deepseek-ai/dsh web
|
|
|
192
192
|
```sh
|
|
193
193
|
npm install
|
|
194
194
|
node client/build.mjs # 改 client/ 后重新打包
|
|
195
|
-
npm test # 代理 / 认证 / 压缩 / 隧道 / 服务 / RPC / 设置(
|
|
195
|
+
npm test # 代理 / 认证 / 压缩 / 隧道 / 服务 / RPC / 设置(93 测试)
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
**改完想在本机先试?** 不用发版:把插件换成指向本地仓库的软链,重启 dsh web 就是本地代码。完整步骤(含怎么换回 npm 官方版本)见 [LOCAL-DEV.md](./LOCAL-DEV.md)。
|
package/client/client.js
CHANGED
|
@@ -535,14 +535,19 @@ var MOBILE_CSS = `
|
|
|
535
535
|
it to 500), and when the layer outranks the drawer, the backdrop paints
|
|
536
536
|
ABOVE the drawer and swallows every tap \u2014 the drawer opens but no row
|
|
537
537
|
can be pressed (every tap just closes it). The drawer must therefore
|
|
538
|
-
outrank any such raise
|
|
539
|
-
|
|
538
|
+
outrank any such raise.
|
|
539
|
+
1200 (was 600) clears the mobile layers shipped by
|
|
540
|
+
@linxin666/dsh-web-ui-all \u2014 its sidebar pane is z-index 1100, its
|
|
541
|
+
details pane 1000 and its full-screen frame ::after mask 1050 (issue
|
|
542
|
+
#67: that mask sat on top of the 600 drawer and ate every tap). Still
|
|
543
|
+
far under the fixed-position banners/toasts (z 9999) that float at the
|
|
544
|
+
viewport level. */
|
|
540
545
|
[data-mobile-nav="frame"] > :first-child {
|
|
541
546
|
position: absolute !important;
|
|
542
547
|
inset: 0 auto 0 0 !important;
|
|
543
548
|
width: max-content !important;
|
|
544
549
|
max-width: 92vw !important;
|
|
545
|
-
z-index:
|
|
550
|
+
z-index: 1200 !important;
|
|
546
551
|
transform: translateX(-110%);
|
|
547
552
|
transition: transform .28s var(--ds-ease-in-out, ease-in-out);
|
|
548
553
|
background: var(--dsw-alias-bg-base, #ffffff);
|
|
@@ -571,6 +576,32 @@ var MOBILE_CSS = `
|
|
|
571
576
|
transform: none !important;
|
|
572
577
|
}
|
|
573
578
|
|
|
579
|
+
/* Kill a competing full-screen mask (issue #67).
|
|
580
|
+
@linxin666/dsh-web-ui-all ships its own mobile drawer, and part of it is
|
|
581
|
+
|
|
582
|
+
[data-dsh-frame]:not([data-sidebar-collapsed])::after {
|
|
583
|
+
content: ""; position: fixed; inset: 0; z-index: 1050;
|
|
584
|
+
background: rgb(0 0 0 / 24%);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
The pseudo-element belongs to the frame we already mark, and the frame
|
|
588
|
+
carries only "position: relative" with z-index auto \u2014 no stacking context
|
|
589
|
+
\u2014 so this mask competes with the drawer in the parent stacking context
|
|
590
|
+
and, at 1050, paints over it. It covers the whole viewport, so every tap
|
|
591
|
+
on a session row lands on the mask instead: the drawer opens but nothing
|
|
592
|
+
inside it can be pressed, and the page behind cannot be scrolled.
|
|
593
|
+
Removing it is safe: the mobile stylesheet already renders its own
|
|
594
|
+
backdrop, and tapping outside the drawer is handled in JS.
|
|
595
|
+
|
|
596
|
+
The attribute selector is repeated on purpose. Their rule has the same
|
|
597
|
+
specificity (0,2,1) once ours is written the obvious way, and plugin
|
|
598
|
+
stylesheets are injected in load order, so a tie would be decided by
|
|
599
|
+
whichever plugin happened to load last. Doubling the attribute makes it
|
|
600
|
+
(0,3,1) and deterministic. */
|
|
601
|
+
[data-mobile-nav="frame"][data-mobile-nav="frame"]:not([data-sidebar-collapsed])::after {
|
|
602
|
+
content: none !important;
|
|
603
|
+
}
|
|
604
|
+
|
|
574
605
|
/* Drag handles are useless on touch and would float over the drawer. */
|
|
575
606
|
[data-side="sidebar"],
|
|
576
607
|
[data-side="details"] {
|
|
@@ -1200,8 +1231,44 @@ var en = {
|
|
|
1200
1231
|
"files": "Files"
|
|
1201
1232
|
};
|
|
1202
1233
|
|
|
1234
|
+
// client/mobile/layout-mode.mjs
|
|
1235
|
+
function resolveLayout({ urlValue, stored, narrowMatch }) {
|
|
1236
|
+
const url = String(urlValue ?? "").trim();
|
|
1237
|
+
if (url === "desktop") return "desktop";
|
|
1238
|
+
if (url === "mobile") return "mobile";
|
|
1239
|
+
if (stored === "desktop" || stored === "mobile") return stored;
|
|
1240
|
+
return narrowMatch ? "mobile" : "desktop";
|
|
1241
|
+
}
|
|
1242
|
+
function persistLayoutFromUrl(urlValue) {
|
|
1243
|
+
if (typeof localStorage === "undefined") return "";
|
|
1244
|
+
const v = String(urlValue ?? "").trim();
|
|
1245
|
+
try {
|
|
1246
|
+
if (v === "desktop" || v === "mobile") localStorage.setItem("dsh-pocket.layout", v);
|
|
1247
|
+
else if (v === "auto" || v === "") localStorage.removeItem("dsh-pocket.layout");
|
|
1248
|
+
} catch {
|
|
1249
|
+
}
|
|
1250
|
+
try {
|
|
1251
|
+
const s = localStorage.getItem("dsh-pocket.layout");
|
|
1252
|
+
return s === "desktop" || s === "mobile" ? s : "";
|
|
1253
|
+
} catch {
|
|
1254
|
+
return "";
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1203
1258
|
// client/mobile/mobile-apply.tsx
|
|
1204
1259
|
function mobileApply(ctx) {
|
|
1260
|
+
const urlValue = new URL(window.location.href).searchParams.get("dsh-layout") ?? "";
|
|
1261
|
+
const narrowMQ = window.matchMedia("(max-width: 1023px)");
|
|
1262
|
+
const stored = persistLayoutFromUrl(urlValue);
|
|
1263
|
+
const layout = resolveLayout({ urlValue, stored, narrowMatch: narrowMQ.matches });
|
|
1264
|
+
document.body?.setAttribute("data-dsh-pocket-layout", layout);
|
|
1265
|
+
if (layout === "desktop") return;
|
|
1266
|
+
let narrow = narrowMQ;
|
|
1267
|
+
if (layout === "mobile") {
|
|
1268
|
+
narrow = { matches: true, addEventListener: () => {
|
|
1269
|
+
}, removeEventListener: () => {
|
|
1270
|
+
} };
|
|
1271
|
+
}
|
|
1205
1272
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-mobile-nav: dictionaries");
|
|
1206
1273
|
ctx.effect(() => {
|
|
1207
1274
|
const tag = document.createElement("style");
|
|
@@ -1214,7 +1281,6 @@ function mobileApply(ctx) {
|
|
|
1214
1281
|
};
|
|
1215
1282
|
}, "dsh-mobile-nav: styles");
|
|
1216
1283
|
ctx.effect(() => {
|
|
1217
|
-
const narrow = window.matchMedia("(max-width: 1023px)");
|
|
1218
1284
|
const viewport = document.querySelector('meta[name="viewport"]');
|
|
1219
1285
|
const originalViewport = viewport?.content ?? "";
|
|
1220
1286
|
const themeMeta = document.createElement("meta");
|
|
@@ -1246,7 +1312,6 @@ function mobileApply(ctx) {
|
|
|
1246
1312
|
};
|
|
1247
1313
|
}, "dsh-mobile-nav: status bar theme + viewport + zoom guard");
|
|
1248
1314
|
ctx.effect(() => {
|
|
1249
|
-
const narrow = window.matchMedia("(max-width: 1023px)");
|
|
1250
1315
|
if (!narrow.matches) return () => {
|
|
1251
1316
|
};
|
|
1252
1317
|
const onChevronClick = (event) => {
|
|
@@ -1258,7 +1323,6 @@ function mobileApply(ctx) {
|
|
|
1258
1323
|
return () => document.removeEventListener("click", onChevronClick, true);
|
|
1259
1324
|
}, "dsh-mobile-nav: aionui explorer close marker");
|
|
1260
1325
|
ctx.effect(() => {
|
|
1261
|
-
const narrow = window.matchMedia("(max-width: 1023px)");
|
|
1262
1326
|
if (!narrow.matches) return () => {
|
|
1263
1327
|
};
|
|
1264
1328
|
const frame = () => document.querySelector('[data-mobile-nav="frame"]');
|
|
@@ -1276,7 +1340,6 @@ function mobileApply(ctx) {
|
|
|
1276
1340
|
};
|
|
1277
1341
|
}, "dsh-mobile-nav: explorer availability (issue #48)");
|
|
1278
1342
|
ctx.effect(() => {
|
|
1279
|
-
const narrow = window.matchMedia("(max-width: 1023px)");
|
|
1280
1343
|
if (!narrow.matches) return () => {
|
|
1281
1344
|
};
|
|
1282
1345
|
const frame = () => document.querySelector('[data-mobile-nav="frame"]');
|
|
@@ -1301,7 +1364,6 @@ function mobileApply(ctx) {
|
|
|
1301
1364
|
};
|
|
1302
1365
|
}, "dsh-mobile-nav: preview sheet open marker");
|
|
1303
1366
|
ctx.effect(() => {
|
|
1304
|
-
const narrow = window.matchMedia("(max-width: 1023px)");
|
|
1305
1367
|
if (!narrow.matches) return () => {
|
|
1306
1368
|
};
|
|
1307
1369
|
const moveTps = (stats) => {
|
|
@@ -1335,7 +1397,6 @@ function mobileApply(ctx) {
|
|
|
1335
1397
|
};
|
|
1336
1398
|
}, "dsh-mobile-nav: stats line marker");
|
|
1337
1399
|
ctx.effect(() => {
|
|
1338
|
-
const narrow = window.matchMedia("(max-width: 1023px)");
|
|
1339
1400
|
if (!narrow.matches) return () => {
|
|
1340
1401
|
};
|
|
1341
1402
|
const cols = ["[data-aionui-explorer-col]", "[data-aionui-preview-col]"];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// 布局模式判定(issue #74:宽屏手机/Pad 可选电脑布局)
|
|
2
|
+
//
|
|
3
|
+
// 优先级:URL 参数 (?dsh-layout=desktop|mobile|auto) > localStorage > 默认 auto。
|
|
4
|
+
// - 'auto' 不写 localStorage,仅按 matchMedia 走;
|
|
5
|
+
// - 显式 'desktop' / 'mobile' 同步到 localStorage,下次无 URL 参数也走同样布局。
|
|
6
|
+
//
|
|
7
|
+
// 纯函数,便于单测;mobile-apply.tsx 读 DOM/localStorage 喂进来。
|
|
8
|
+
|
|
9
|
+
/** @typedef {'mobile' | 'desktop'} ForcedLayout */
|
|
10
|
+
|
|
11
|
+
/** 解析输入。 */
|
|
12
|
+
export function resolveLayout({ urlValue, stored, narrowMatch }) {
|
|
13
|
+
const url = String(urlValue ?? '').trim();
|
|
14
|
+
if (url === 'desktop') return 'desktop';
|
|
15
|
+
if (url === 'mobile') return 'mobile';
|
|
16
|
+
// 'auto' / 空 / 非法值 → 用 localStorage / matchMedia
|
|
17
|
+
if (stored === 'desktop' || stored === 'mobile') return stored;
|
|
18
|
+
return narrowMatch ? 'mobile' : 'desktop';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 同步 localStorage 的副作用(在 mobileApply 入口跑一次)。返回最终存储值。 */
|
|
22
|
+
export function persistLayoutFromUrl(urlValue) {
|
|
23
|
+
if (typeof localStorage === 'undefined') return '';
|
|
24
|
+
const v = String(urlValue ?? '').trim();
|
|
25
|
+
try {
|
|
26
|
+
if (v === 'desktop' || v === 'mobile') localStorage.setItem('dsh-pocket.layout', v);
|
|
27
|
+
else if (v === 'auto' || v === '') localStorage.removeItem('dsh-pocket.layout');
|
|
28
|
+
} catch { /* 隐私模式/无 storage → 静默 */ }
|
|
29
|
+
try {
|
|
30
|
+
const s = localStorage.getItem('dsh-pocket.layout');
|
|
31
|
+
return s === 'desktop' || s === 'mobile' ? s : '';
|
|
32
|
+
} catch {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** 读 localStorage 当前的 layout 值('desktop' | 'mobile' | '')。 */
|
|
38
|
+
export function readStoredLayout() {
|
|
39
|
+
if (typeof localStorage === 'undefined') return '';
|
|
40
|
+
try {
|
|
41
|
+
const v = localStorage.getItem('dsh-pocket.layout');
|
|
42
|
+
return v === 'desktop' || v === 'mobile' ? v : '';
|
|
43
|
+
} catch {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -6,6 +6,7 @@ import { MobileDrawerFooter } from './MobileDrawerFooter.tsx'
|
|
|
6
6
|
import { MOBILE_CSS } from './mobile.css.ts'
|
|
7
7
|
import { NS, en, zh } from './locales.ts'
|
|
8
8
|
import type { MobileNavKey } from './locales.ts'
|
|
9
|
+
import { resolveLayout, persistLayoutFromUrl } from './layout-mode.mjs'
|
|
9
10
|
|
|
10
11
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
11
12
|
interface LocaleNamespaceMap {
|
|
@@ -23,6 +24,22 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
23
24
|
* @param ctx - client root context.
|
|
24
25
|
*/
|
|
25
26
|
export function mobileApply(ctx): void {
|
|
27
|
+
// 布局模式(issue #74):URL 参数 > localStorage > auto(=matchMedia)。
|
|
28
|
+
// desktop 模式(宽屏手机/平板强制电脑布局)下整段 mobile 效果都不挂——
|
|
29
|
+
// 不加 styles、不挂 slots、不跑 effects,直接走 DSH 原生桌面 UI。
|
|
30
|
+
const urlValue = new URL(window.location.href).searchParams.get('dsh-layout') ?? '';
|
|
31
|
+
const narrowMQ = window.matchMedia('(max-width: 1023px)');
|
|
32
|
+
const stored = persistLayoutFromUrl(urlValue);
|
|
33
|
+
const layout = resolveLayout({ urlValue, stored, narrowMatch: narrowMQ.matches });
|
|
34
|
+
document.body?.setAttribute('data-dsh-pocket-layout', layout);
|
|
35
|
+
if (layout === 'desktop') return;
|
|
36
|
+
// 强制 mobile:narrow 永远 true;宽度变化不再切换(用户已显式选 mobile)
|
|
37
|
+
// auto 模式:narrow 是真实的 matchMedia,宽度变化会触发 effect 挂载/卸载
|
|
38
|
+
let narrow: MediaQueryList = narrowMQ;
|
|
39
|
+
if (layout === 'mobile') {
|
|
40
|
+
narrow = { matches: true, addEventListener: () => {}, removeEventListener: () => {} } as MediaQueryList;
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-mobile-nav: dictionaries')
|
|
27
44
|
|
|
28
45
|
ctx.effect(() => {
|
|
@@ -52,7 +69,6 @@ export function mobileApply(ctx): void {
|
|
|
52
69
|
// zoom; modern browsers are covered by the stylesheet's
|
|
53
70
|
// touch-action: manipulation (which keeps pan and pinch zoom).
|
|
54
71
|
ctx.effect(() => {
|
|
55
|
-
const narrow = window.matchMedia('(max-width: 1023px)')
|
|
56
72
|
const viewport = document.querySelector<HTMLMetaElement>('meta[name="viewport"]')
|
|
57
73
|
const originalViewport = viewport?.content ?? ''
|
|
58
74
|
const themeMeta = document.createElement('meta')
|
|
@@ -96,7 +112,6 @@ export function mobileApply(ctx): void {
|
|
|
96
112
|
// sheet's own collapse chevron is tapped, so closing is symmetric with
|
|
97
113
|
// opening.
|
|
98
114
|
ctx.effect(() => {
|
|
99
|
-
const narrow = window.matchMedia('(max-width: 1023px)')
|
|
100
115
|
if (!narrow.matches) return () => {}
|
|
101
116
|
const onChevronClick = (event: MouseEvent) => {
|
|
102
117
|
const target = event.target as HTMLElement | null
|
|
@@ -115,7 +130,6 @@ export function mobileApply(ctx): void {
|
|
|
115
130
|
// `data-mobile-nav-explorer="1|0"` so the stylesheet can hide the entries
|
|
116
131
|
// on hosts without it (dsh-web-ui installs keep the feature).
|
|
117
132
|
ctx.effect(() => {
|
|
118
|
-
const narrow = window.matchMedia('(max-width: 1023px)')
|
|
119
133
|
if (!narrow.matches) return () => {}
|
|
120
134
|
const frame = (): HTMLElement | null => document.querySelector('[data-mobile-nav="frame"]')
|
|
121
135
|
const check = () => {
|
|
@@ -141,7 +155,6 @@ export function mobileApply(ctx): void {
|
|
|
141
155
|
// whenever the suite hides the column again (collapse chevron / tab
|
|
142
156
|
// close), so a restored-but-unwanted sheet never appears.
|
|
143
157
|
ctx.effect(() => {
|
|
144
|
-
const narrow = window.matchMedia('(max-width: 1023px)')
|
|
145
158
|
if (!narrow.matches) return () => {}
|
|
146
159
|
const frame = (): HTMLElement | null => document.querySelector('[data-mobile-nav="frame"]')
|
|
147
160
|
const onTap = (event: MouseEvent) => {
|
|
@@ -173,7 +186,6 @@ export function mobileApply(ctx): void {
|
|
|
173
186
|
// marked row out as ONE horizontally scrolling line with every metric
|
|
174
187
|
// reachable.
|
|
175
188
|
ctx.effect(() => {
|
|
176
|
-
const narrow = window.matchMedia('(max-width: 1023px)')
|
|
177
189
|
if (!narrow.matches) return () => {}
|
|
178
190
|
// The composer root renders the TPS readout ("TPS 89.4 tok/s") as its
|
|
179
191
|
// own row BELOW the status strip; fold it into the strip so every
|
|
@@ -218,7 +230,6 @@ export function mobileApply(ctx): void {
|
|
|
218
230
|
// with the Web Animations API each time a column turns visible, then
|
|
219
231
|
// leave the resting state to the stylesheet.
|
|
220
232
|
ctx.effect(() => {
|
|
221
|
-
const narrow = window.matchMedia('(max-width: 1023px)')
|
|
222
233
|
if (!narrow.matches) return () => {}
|
|
223
234
|
const cols = ['[data-aionui-explorer-col]', '[data-aionui-preview-col]']
|
|
224
235
|
const seen = new Map<string, boolean>()
|
|
@@ -210,14 +210,19 @@ export const MOBILE_CSS = `
|
|
|
210
210
|
it to 500), and when the layer outranks the drawer, the backdrop paints
|
|
211
211
|
ABOVE the drawer and swallows every tap — the drawer opens but no row
|
|
212
212
|
can be pressed (every tap just closes it). The drawer must therefore
|
|
213
|
-
outrank any such raise
|
|
214
|
-
|
|
213
|
+
outrank any such raise.
|
|
214
|
+
1200 (was 600) clears the mobile layers shipped by
|
|
215
|
+
@linxin666/dsh-web-ui-all — its sidebar pane is z-index 1100, its
|
|
216
|
+
details pane 1000 and its full-screen frame ::after mask 1050 (issue
|
|
217
|
+
#67: that mask sat on top of the 600 drawer and ate every tap). Still
|
|
218
|
+
far under the fixed-position banners/toasts (z 9999) that float at the
|
|
219
|
+
viewport level. */
|
|
215
220
|
[data-mobile-nav="frame"] > :first-child {
|
|
216
221
|
position: absolute !important;
|
|
217
222
|
inset: 0 auto 0 0 !important;
|
|
218
223
|
width: max-content !important;
|
|
219
224
|
max-width: 92vw !important;
|
|
220
|
-
z-index:
|
|
225
|
+
z-index: 1200 !important;
|
|
221
226
|
transform: translateX(-110%);
|
|
222
227
|
transition: transform .28s var(--ds-ease-in-out, ease-in-out);
|
|
223
228
|
background: var(--dsw-alias-bg-base, #ffffff);
|
|
@@ -246,6 +251,32 @@ export const MOBILE_CSS = `
|
|
|
246
251
|
transform: none !important;
|
|
247
252
|
}
|
|
248
253
|
|
|
254
|
+
/* Kill a competing full-screen mask (issue #67).
|
|
255
|
+
@linxin666/dsh-web-ui-all ships its own mobile drawer, and part of it is
|
|
256
|
+
|
|
257
|
+
[data-dsh-frame]:not([data-sidebar-collapsed])::after {
|
|
258
|
+
content: ""; position: fixed; inset: 0; z-index: 1050;
|
|
259
|
+
background: rgb(0 0 0 / 24%);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
The pseudo-element belongs to the frame we already mark, and the frame
|
|
263
|
+
carries only "position: relative" with z-index auto — no stacking context
|
|
264
|
+
— so this mask competes with the drawer in the parent stacking context
|
|
265
|
+
and, at 1050, paints over it. It covers the whole viewport, so every tap
|
|
266
|
+
on a session row lands on the mask instead: the drawer opens but nothing
|
|
267
|
+
inside it can be pressed, and the page behind cannot be scrolled.
|
|
268
|
+
Removing it is safe: the mobile stylesheet already renders its own
|
|
269
|
+
backdrop, and tapping outside the drawer is handled in JS.
|
|
270
|
+
|
|
271
|
+
The attribute selector is repeated on purpose. Their rule has the same
|
|
272
|
+
specificity (0,2,1) once ours is written the obvious way, and plugin
|
|
273
|
+
stylesheets are injected in load order, so a tie would be decided by
|
|
274
|
+
whichever plugin happened to load last. Doubling the attribute makes it
|
|
275
|
+
(0,3,1) and deterministic. */
|
|
276
|
+
[data-mobile-nav="frame"][data-mobile-nav="frame"]:not([data-sidebar-collapsed])::after {
|
|
277
|
+
content: none !important;
|
|
278
|
+
}
|
|
279
|
+
|
|
249
280
|
/* Drag handles are useless on touch and would float over the drawer. */
|
|
250
281
|
[data-side="sidebar"],
|
|
251
282
|
[data-side="details"] {
|
package/package.json
CHANGED