better-dsh 0.3.1-e → 0.3.1-f

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.
@@ -172,6 +172,20 @@ function FailoverRow({ catalog, loadConfig, save, t }) {
172
172
 
173
173
  //#endregion
174
174
  //#region src/mobile/gesture.ts
175
+ /**
176
+ * Whether the mobile adaptation mounts at all (2026-10-18 user ruling):
177
+ * touch-first means the device's PRIMARY input is a finger. A query
178
+ * answering FALSE — a hover-capable or fine primary pointer — decides
179
+ * desktop on its own (a touchscreen laptop keeps desktop behavior); the
180
+ * pair answering true is the standard idiom; and only when the queries
181
+ * are entirely unavailable does `maxTouchPoints` become the honest
182
+ * degrade (every such legacy device is a phone or tablet anyway).
183
+ */
184
+ function isTouchFirstDevice(input) {
185
+ if (input.hoverNone === false || input.pointerCoarse === false) return false;
186
+ if (input.hoverNone === true && input.pointerCoarse === true) return true;
187
+ return (input.maxTouchPoints ?? 0) > 0;
188
+ }
175
189
  /** Resolved defaults — the `1706b81` constants (120 / viewport÷4 / 40 / 1.3)
176
190
  * plus the one addition, the velocity gate. A slow text-selection drag runs
177
191
  * ~0.13 px/ms; 0.15 px/ms filters those while any comfortable sweep (0.4
@@ -260,6 +274,18 @@ function resolveMobileConfig(pageConfig) {
260
274
 
261
275
  //#endregion
262
276
  //#region src/mobile/client/index.ts
277
+ /**
278
+ * One media query, or `undefined` when the environment has none (old
279
+ * webviews throw or omit `matchMedia`) — the probe then rides the
280
+ * touch-points fallback in {@link isTouchFirstDevice}.
281
+ */
282
+ function safeMatchMedia(query) {
283
+ try {
284
+ return globalThis.matchMedia?.(query) ?? void 0;
285
+ } catch {
286
+ return;
287
+ }
288
+ }
263
289
  /** Left sidebar state, live off AppFrame's semantic attribute. */
264
290
  function readLeftCollapsed() {
265
291
  return document.querySelector("[data-sidebar-collapsed]") !== null;
@@ -340,17 +366,46 @@ function mobileCss() {
340
366
  }
341
367
  /**
342
368
  * Mount the mobile feature: style injection plus the swipe listeners.
343
- * Inert unless the host's boot script opted the page in.
369
+ * Inert unless the host's boot script opted the page in AND the device is
370
+ * touch-first (2026-10-18 user ruling). Capability flips (a tablet's
371
+ * primary pointer hardware changing) re-evaluate and mount/unmount the
372
+ * surface live; the media queries answer on every browser since ~2015 and
373
+ * a webview without `matchMedia` degrades to the touch-points probe.
344
374
  *
345
375
  * @param ctx - client root context.
346
- * @returns the effect disposer (style tag + listeners removed on unload).
347
376
  */
348
377
  function setupMobileLayout(ctx) {
349
378
  const pageConfig = globalThis.__DASHR_MOBILE__;
350
379
  const config = resolveMobileConfig(pageConfig);
351
380
  if (!config.enabled) return;
352
- const styleTagId = "better-dsh/mobile";
381
+ const coarseQuery = safeMatchMedia("(pointer: coarse)");
382
+ const hoverQuery = safeMatchMedia("(hover: none)");
353
383
  ctx.effect(() => {
384
+ let disposeSurface;
385
+ const apply$1 = () => {
386
+ const touchFirst = isTouchFirstDevice({
387
+ hoverNone: hoverQuery?.matches,
388
+ pointerCoarse: coarseQuery?.matches,
389
+ maxTouchPoints: navigator.maxTouchPoints
390
+ });
391
+ if (touchFirst && disposeSurface === void 0) disposeSurface = mountTouchSurface();
392
+ else if (!touchFirst && disposeSurface !== void 0) {
393
+ disposeSurface();
394
+ disposeSurface = void 0;
395
+ }
396
+ };
397
+ coarseQuery?.addEventListener("change", apply$1);
398
+ hoverQuery?.addEventListener("change", apply$1);
399
+ apply$1();
400
+ return () => {
401
+ coarseQuery?.removeEventListener("change", apply$1);
402
+ hoverQuery?.removeEventListener("change", apply$1);
403
+ disposeSurface?.();
404
+ };
405
+ }, "dashr: mobile layout");
406
+ /** The style tag + gesture listeners, mounted and disposed as one surface. */
407
+ function mountTouchSurface() {
408
+ const styleTagId = "better-dsh/mobile";
354
409
  const tag = document.createElement("style");
355
410
  tag.dataset.plugin = "better-dsh";
356
411
  tag.dataset.pluginCss = styleTagId;
@@ -363,9 +418,7 @@ function setupMobileLayout(ctx) {
363
418
  const onPointerDown = (event) => {
364
419
  start = void 0;
365
420
  const viewport = window.innerWidth;
366
- const coarsePointer = window.matchMedia?.("(pointer: coarse)").matches === true;
367
421
  const panels = readPanelState();
368
- if (!coarsePointer && !panels.leftCollapsed && !panels.rightOpen) return;
369
422
  if (!admitsSwipeStart(event.clientX, viewport, panels, config)) return;
370
423
  start = {
371
424
  x: event.clientX,
@@ -405,7 +458,7 @@ function setupMobileLayout(ctx) {
405
458
  tag.remove();
406
459
  Promise.resolve(offLayout.dispose());
407
460
  };
408
- }, "dashr: mobile layout");
461
+ }
409
462
  }
410
463
 
411
464
  //#endregion
package/lib/main.js CHANGED
@@ -8,8 +8,8 @@ import { URL as URL$1, urlToHttpOptions } from "node:url";
8
8
  import os from "node:os";
9
9
  import { debuglog, promisify } from "node:util";
10
10
  import { mkdir, readdir, symlink, unlink } from "node:fs/promises";
11
- import { Writable } from "node:stream";
12
11
  import { pipeline } from "node:stream/promises";
12
+ import { Writable } from "node:stream";
13
13
  import { EventEmitter } from "node:events";
14
14
  import * as https from "node:https";
15
15
 
@@ -8,8 +8,8 @@ import { fileURLToPath } from "node:url";
8
8
  import { homedir, tmpdir } from "node:os";
9
9
  import { defineTool } from "@deepseek-ai/dsh-tools";
10
10
  import { chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
11
- import { Readable } from "node:stream";
12
11
  import { pipeline } from "node:stream/promises";
12
+ import { Readable } from "node:stream";
13
13
  import { createConnection, createServer } from "node:net";
14
14
 
15
15
  //#region src/remote/archive.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-dsh",
3
- "version": "0.3.1-e",
3
+ "version": "0.3.1-f",
4
4
  "description": "Make your dsh ready for serious coding tasks. (Tools x Schemas)^REPL. `skill://`, `ctx://`, `agent://`, `dvc://`, `dsh://`, Python REPL, Context as Variables, cross compactions recallable, full context revive. `hash-edit`, `dvc://browser`, subagent as function, workflow as function.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",