Skip to content

Releases: ddtcorex/dsh-maestro-mobile

v1.8.1

Choose a tag to compare

@github-actions github-actions released this 28 Oct 14:15
41fd7e7

Fixed

  • Session-header drawer toggle: 0.2.2's masked titleCluster made the
    absolutely positioned control untappable (present, sized, never hit). The
    phone branch now turns that mask off and the toggle keeps its composer-gutter
    place at x=16.
  • Session-delete route: it registered an exact /api path on webServer, and
    the launch-token fence is a /api prefix route an exact route shadows, so the
    handler ran unauthenticated. It now calls connection.requestRejection() and
    fails closed with 503 when the host publishes no such service.
  • Settings sheet: one content column instead of five left edges; the tab strip
    fades only a cut edge; the panel is a column instead of the host's grid (the
    drag handle had been auto-placed into the 36px header cell); a row's switcher
    no longer overflows the column; the panel honours its own height cap.
  • Response compression now installs only when the host does not compress: 0.2.2
    ships compression@1.8.2 and the deployed profile enables it, so the
    process-wide patch was dead weight.
  • The contract scan hit-tests interactive controls and drives a real session
    first (hit=15 skip=33 -> hit=26 skip=25 on the same page).

Changed

  • Peer ranges accept the running DSH prerelease (>=0.2.2-0 <0.3.0-0,
    cordis ~4.0.6-alpha.1); devDependencies pin the same release so pnpm verify
    type-checks against the host the plugin runs on.
  • Removed the inert dsh.client.inject entry (dsh-client-ui-primitives is a
    platform seed, not a boot row).

What's Changed

  • fix: make the plugin work on DSH 0.2.2-alpha.2 (toggle, delete-route auth, settings sheet) by @ddtcorex in #89

Full Changelog: v1.8.0...v1.8.1

v1.8.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 07:00
92e3d5e

Added

  • Composer clearance for the landscape keyboard band — while the frame
    carries data-mobile-nav-composer-tight, the sticky seat outranks the
    opaque session header (z-index: 31) and the stats row yields its height
    to the editing row, so rotating to landscape with the keyboard up no
    longer hides the input row. Pure geometry plus hysteresis, fail-open,
    complements the iOS-only keyboard lift. Ported from dsh-web-mobile
    v3.2.2 (#246).
  • Manual ask-card fullscreen with answer scroll-back — an expand button
    in the card header grows the card to the measured header-to-composer
    band (only when that band beats the host min(60vh, 520px) cap), and the
    focused answer field is scrolled back into the body intersect visual
    viewport, so typing stays visible with the keyboard up. Manual toggle
    only, fail-open to the host default. Ported from dsh-web-mobile
    v3.2.2 (#246).

Fixed

  • Drawer menu taps stay inside the drawer — a panelRow button that
    opens a menu (aria-haspopup) no longer counts as navigation, so the
    More menu no longer closes the drawer and floats over the session.
    Ported from dsh-web-mobile v3.2.2 (#246).
  • Ask-card overflow convergence and 44px touch targets — the card body
    pins overflow-x: hidden, the question title caps at
    min(23vh, 155px) with internal scroll, and the header actions widen to
    a 20px gap with 44x44 hit boxes. Ported from dsh-web-mobile
    v3.2.2 (#246).

What's Changed

Full Changelog: v1.7.4...v1.8.0

v1.7.4

Choose a tag to compare

@github-actions github-actions released this 21 Oct 03:21
75a2af8

Fixed

  • Large JSON responses no longer block the host event loop — response
    compression runs off the event loop (brotliCompress / gzip via the
    libuv thread pool); end() returns before headers are written and the
    reply completes from the zlib callback. Callers that flush headers during
    the window win (body replays verbatim), compression errors degrade to
    identity, and the callback never throws (process-wide patch). Fixes the
    first-run notice stuck at saving behind a multi-megabyte response.
    Ported from dsh-web-mobile v3.2.1 (#233).
  • The image lightbox keeps a full-viewport hit area on tablet — the
    tablet dialog cap (min(100vw - 32px, 720px) centered) matched the host
    ImageLightbox through its :not(...) branch (stage last child is the
    close button > svg), leaving a dead strip on the right side. A positive
    div[role="dialog"][aria-modal="true"]:has(> :first-child > img) override
    restores full-bleed after the cap. Ported from dsh-web-mobile v3.2.1
    (#234).

What's Changed

  • fix(mobile): compress large JSON off the event loop by @ddtcorex in #84
  • fix(mobile): keep the image lightbox full-viewport on tablet by @ddtcorex in #85
  • chore(release): 1.7.4 by @ddtcorex in #86

Full Changelog: v1.7.3...v1.7.4

v1.7.3

Choose a tag to compare

@github-actions github-actions released this 18 Oct 16:10
30f7f10

Fixed

  • Chrome for Android no longer pans the whole page when the keyboard opens
    — the viewport meta gains interactive-widget=resizes-content, opting
    into the WebView keyboard model (layout viewport shrinks instead of the
    browser panning the document and scrolling the top bar off-screen).
    Engines that do not recognise the field (iOS Safari, Firefox Android)
    ignore it. Consequence folded in: a shrinking layout viewport collapses
    the classic innerHeight - visualViewport.height keyboard inset to ~0,
    so both keyboard readers now compare against the shared resting baseline
    (restingViewportHeight, one home in core/composer-dom.ts) — otherwise
    the tap-time blur fires while the keyboard is up and the composer jumps.
    Ported from dsh-web-mobile v3.2.0 (#226).
  • The stats strip is marked again on hosts that split StatsPills — the
    slow path hunts in two tiers (_root first, then [data-slot] inside the
    composer stack) sharing one isStatsStripCandidate predicate, so a future
    host reshape fixes one place. Tier 2 is forward-looking for the 0.2.1
    split shape (unverifiable live: the installed host is 0.2.0-rc.2, where
    tier 1 short-circuits) — confirm on a real 0.2.1 host before relying on
    it. Ported from dsh-web-mobile v3.2.0 (#216).
  • The drawer keeps an opaque fill under wallpaper plugins — gated on
    body[data-dsh-bg], light #ffffff (this file's own bg-base fallback)
    and dark #151517 (upstream's bg-base fallback for the same token); the
    no-wallpaper path keeps resolving the theme token. Appearance under a real
    wallpaper install still needs on-device re-check. Ported from
    dsh-web-mobile v3.2.0 (#229).
  • Edge-zone touches no longer lose their native scroll on iOS — the
    swipe touchmove prevent is gated on the horizontal axis lock
    (shouldPreventTouchMove); pre-lock touches scroll natively
    (touch-action: pan-y already covers the horizontal race). Ported from
    dsh-web-mobile v3.2.0 (#223).

Audited, no change

  • Header chips need no 0.2.1-split fix: the lineage trigger is already a
    shape anchor (_trigger[aria-haspopup][aria-expanded], not _root) and
    the hover scope already a marker + role (data-lineage-root,
    role="tree"); the jobs chip is already role/slot-identified. The
    _titleRow 36px reserve sits on a non-scroll flex row for an out-of-flow
    toggle, so padding-right is correct there (the #217 margin lesson applies
    to scroll containers only).

What's Changed

Full Changelog: v1.7.2...v1.7.3

v1.7.2

Choose a tag to compare

@github-actions github-actions released this 17 Oct 10:21
619c750

Fixed

  • Settings sheet probe follows the Account menu — since DSH 0.2.1
    (account login) Settings no longer lives in the drawer: the drawer holds
    Plugins / Automation tasks / the Account menu, and Settings is a menuitem
    inside it. The probe clicked a drawer Settings entry that no longer exists
    and aborted at sheet.open; it now drives Account menu → Settings, with
    all geometry assertions unchanged. Shipped code needed no change (sheet
    bottom-anchoring, horizontal nav pills, close hit-test, drawer footer slot
    and toolbar reparent all verified live on 0.2.1-rc.2).

Fixed

  • In-place TPS text rewrites wake the stats task again — the shared
    full-tree observer never opened characterData, so frames where React
    rewrites the TPS text node without rebuilding it woke no task and the
    readout sat stale. The observer now delivers those records through an
    explicit characterData dirty key (mutationDirtyKey in the DOM-free
    core, fail-open to the tree sentinel), and the stats task declares that
    key next to '*'. Ported from dsh-web-mobile v3.1.0 (C08).
  • A throwing host selectPanel no longer wedges the panel exit — the
    call runs after the exit state flips but before the fallback timer arms,
    so a host error left the exit stuck with no safety net. The fallback now
    installs in a finally while the error still propagates. Ported from
    dsh-web-mobile v3.1.0.

Changed

  • DSH peer floor raised to 0.2.0-rc.2 (>=0.2.0-rc.2 <0.3.0-0 on every
    @deepseek-ai/dsh-* peer, owner decision 2026-10-07). Hosts older than
    0.2.0-rc.2 are no longer supported. Measured: all 13 peers publish
    0.2.0-rc.2, which pnpm resolves under default prerelease semantics and the
    DSH gate accepts with includePrerelease.

Removed

  • Pre-0.2.0-rc.2 host compat in the session-delete row resolution: the
    displayTitle legacy label match and the tolerance for a missing
    session.untitled dictionary key. untitledLabel is now a required input.

  • Pre-0.2.0-rc.2 host compat in session deletion: the refusal of a live
    session on a host without an agent disposal face (every supported host's
    Agent has cancel and whenIdle) and the flat persistence.list() entry
    shape (supported hosts return snapshots carrying .header). A live session
    that does not converge to idle is still refused with 409 session-busy.

  • Pre-0.2.0-rc.2 host compat in the client: the rc-era and Regular host
    icon candidates (the host exports the Medium weight), the dropped
    SessionListState.current and sessions.clear members, and the manager
    snapshot items read in session row resolution.

  • Legacy ?mobile-nav-debug=1 diagnostics alias, only
    ?dsh-maestro-mobile-debug=1 shows the floating debug bar now.

What's Changed

  • chore(mobile): raise the DSH peer floor and remove old host shims by @ddtcorex in #76
  • fix(mobile): prefix locale namespace and pin streaming fast path by @ddtcorex in #77
  • fix(mobile): wake stats-line on in-place TPS text rewrites by @ddtcorex in #78
  • fix(mobile): arm panel-exit fallback even when selectPanel throws by @ddtcorex in #79
  • fix(mobile): open Settings via the Account menu in the sheet probe by @ddtcorex in #80
  • chore(release): 1.7.2 by @ddtcorex in #81

Full Changelog: v1.7.1...v1.7.2

v1.7.1

Choose a tag to compare

@github-actions github-actions released this 07 Oct 08:15
b81cd46

Fixed

  • Deleting a session no longer reports failure when it succeeded — the
    host moves the session to trash then aborts the reply, so the browser
    fetch rejects with Failed to fetch for a delete that landed. The
    session list is now the judge: on a rejected request the list is
    re-read bounded (4 attempts x 350ms) and only an id that survives every
    attempt shows the error row.
  • Opening the model / reasoning-level menu no longer raises the soft
    keyboard
    — the host drills into the model pane and focuses its search
    field from a passive effect. The field's focus method is shadowed, armed
    on capture-phase pointerdown strictly before the effect can run; a real
    tap on the field still focuses natively. No hashed-class fallback, per
    the hash-prefix ban.
  • The iOS composer stays above the form-assistant bar while typing —
    the host selection-scroll helper mixes layout coordinates with the visual
    caret rect once the iOS keyboard forces a nonzero window scroll, dropping
    the seat behind the assistant bar on every keystroke. The seat is lifted
    from screen-space quantities only (seat bottom vs visualViewport
    height), fail-open on pinch or scroll-channel drift. The active path
    needs a real iPhone; headless cannot verify it.
  • Dragging a selection handle across the header no longer jumps the
    viewport to the top of the session
    — while a conversation selection
    exists the header seat exits hit-testing (pointer-events only, no
    layout change), so the drag stays in message text. Complements the
    data-mobile-nav-dragging drawer-yield mark on a different axis.

What's Changed

  • docs(mobile): correct row id, slots, paths and test commands by @ddtcorex in #70
  • ci: call the reusable workflows at their new repository name by @ddtcorex in #71
  • fix(mobile): prose floor at the host secondary tier, and opt code out of iOS text autosizing by @ddtcorex in #72
  • fix(mobile): port upstream v3.0.5 phone fixes by @ddtcorex in #74
  • chore: release 1.7.1 by @ddtcorex in #75

Full Changelog: v1.7.0...v1.7.1

v1.7.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 07:06
200fd60

Added

  • Opening or switching to a session no longer raises the soft keyboard:
    the host's InputBar focuses the Lexical editor from a React effect keyed on
    sessionId (InputBar.tsx:178-182), so every switch handed the WebView a DOM
    focus and both iOS and Android put the IME over half the screen. A new
    session-focus-guard effect arms an 800 ms window on a real current-session
    change and suppresses that focus in two layers, because one is not enough: an
    own-property shadow over the editor's focus(), plus a synchronous
    focusin-capture blur. The blur must be synchronous because a remount's host
    focus runs in the commit's synchronous layout-effect phase, before any
    MutationObserver microtask, so the freshly mounted editor is focused while
    it still carries no shadow; a deferred blur does not help either, since the
    IME has already begun by the next macrotask. The window arms only on a real
    switch (retainedBy.mainView > 0, the same rule session-menu.ts:73 uses,
    because 0.1.6-alpha.2 dropped SessionListState.current), so list churn
    cannot flash the guard, and a pointerdown on the editor closes the window
    early so a user's own tap always opens the keyboard. The guard owns its own
    data-mobile-nav-session-guard marker and deliberately does not touch
    data-mobile-nav-focus-shadow, which composer-plus-toggle.ts owns on
    documentElement and composer-focus-release.ts:269 reads. Added
    pnpm probe:session-focus, an INFO probe that reports the DOM half of the
    guard against a live host and exits 0 unconditionally. Gated on a device: the
    soft-keyboard behaviour cannot be settled headless.

Fixed

  • createFocusShadow is now reference counted per element. The composer's
    + menu shadow and the new session guard can hold the same editor at once, and
    they were independent instances: whichever restored first ran
    delete element.focus and unblocked native focus() for the other, so the
    surviving guard still reported armed while the keyboard rose inside the
    window it believed it owned. Holds now live in a WeakMap<FocusableLike, number>
    and only the last holder deletes the override, which protects every existing
    caller; a repeated restore() stays a no-op, so a stranded hold can never
    block focus forever.

What's Changed

  • fix(mobile): keep the phone font floor on prose only by @ddtcorex in #64
  • fix(mobile): drive the panel probe with a real touch on a clear screen by @ddtcorex in #65
  • fix(mobile): re-anchor dsh peer ceiling to 0.3.0-0 by @ddtcorex in #66
  • fix(mobile): resolve a title-less session row after the 0.2.0-rc.2 label move by @ddtcorex in #67
  • feat(mobile): stop a session switch from raising the phone keyboard by @ddtcorex in #68
  • chore: release v1.7.0 by @ddtcorex in #69

Full Changelog: v1.6.2...v1.7.0

v1.6.2

Choose a tag to compare

@github-actions github-actions released this 25 Sep 04:04
ba0df01

Fixed

  • The session-row Delete item is back on 0.1.7 hosts — the host composes
    the row menu from a slot list and 0.1.7 prepended "Pin session", so the menu
    holds four items and the old labels.length === 3 signature stopped matching:
    Delete disappeared with no error anywhere. The gate is now an inclusive label
    signature (rename + fork + archive present, the unarchive twin rejected, extra
    items allowed), read from the live host locale so wording changes stay
    harmless. Verified live on 0.1.7-rc.2: five items with Delete injected;
    pnpm probe:session-delete 7/1/0 (the skip is the destructive step, which
    needs an explicit session id).
  • The Settings sheet is a bottom sheet again — the overlay guard was written
    as :has() nested inside :has(), which is an invalid selector: Chrome
    throws and drops the whole rule from the CSSOM with no error, so the four
    overlay rules never applied a single declaration and the sheet rendered as a
    floating centred card (equal 56.5px gaps on an 844px viewport) since
    2026-08-30, while every source-text assertion kept passing. Flattened to
    overlay:has(navList); measured after: 4 overlay rules in the CSSOM,
    align-items: flex-end landed, panel bottom flush with the viewport. Guards:
    tests/css-selector-validity.test.ts fails the build on the nested shape and
    pnpm probe:settings-sheet asserts the CSSOM rule count plus the
    bottom-anchored geometry (8/8) — a text regex cannot see a rule the browser
    rejected.
  • An oversized delete body is answered, not cut off — the route destroyed
    the socket at the overflow point, racing the error response so the client saw
    an empty reply instead of the reason. The reader drains to the natural end
    and the route answers 413 payload-too-large; the cap stays at 64 KiB.
    Verified live after the host restart: a 70 KB body returns the 413 JSON.
  • end(callback) no longer writes the callback's source into the response
    — the deferred-response replay forwarded the end() tail blindly: a function
    argument was buffered as body text (and the callback never fired), an
    encoding token was written as a second body chunk, and a buffered write()'s
    callback was dropped. Arguments are now classified (function = callback,
    encoding = consumed by the buffering, buffered write callbacks replay once,
    in order, right after the real end()).
    tests/compress-response.test.ts drives a real HTTP server through the patch
    and fails all three behaviours against the pre-fix code.

Added

  • Deleted session logs are recoverable for 24 hours — the directory moves
    into <root>/.sessions-trash/ with every canonical payload renamed to
    <name>.trash plus a best-effort manifest.json, instead of rm -rf. The
    rename precedes the move because the host scans every directory under the
    storage root as a project directory, trash included, and a canonical payload
    name there breaks the whole session list; entries older than 24h are purged
    inline on a later deletion, never on a timer. A stash failure after a live
    teardown answers cleanup-failed with deletedLiveSession: true and still
    settles the workspace accounting, so the caller is never told a session is
    intact when it is already gone — and the client refreshes the list on that
    code for the same reason.
  • A reparent-leak probe — pnpm probe:reparent-leak drives a Settings
    open/close cycle and a session switch and asserts no moved host node is
    orphaned and no page exception or console.error is raised (5/5 on rc.2).
    That measurement is why the three reparent sites stay: same hazard class as
    upstream's #104/#105, no observable fault here.

What's Changed

  • fix(mobile): rc.2 session-menu compat, sheet anchoring, and delete-route hardening by @ddtcorex in #62
  • chore: release v1.6.2 by @ddtcorex in #63

Full Changelog: v1.6.1...v1.6.2

v1.6.1

Choose a tag to compare

@github-actions github-actions released this 23 Sep 23:47
4fcc094

Removed

  • Dead modules, and the composer marker got one home — a reachability +
    reference scan over src/ (every module reachable from the two entries, every
    export referenced somewhere, tsc --noUnusedLocals --noUnusedParameters clean
    on both faces) left four things to delete:
    components/BottomSheet.tsx and its styles/sheet.css.ts section — the
    component had no importer and its [data-mobile-sheet] rules therefore styled
    nothing; the walker that builds the client bundle never inlined it, so only the
    stylesheet section was being shipped. effects/overlay-backdrop-fab.ts — the
    legacy manual frame.appendChild backdrop/FAB task: nothing installed it, and
    because its fadeHook was the only thing that ever set it, the swipe-close
    call fadeOverlayOut() had been a silent no-op. isGestureConsumed — a
    gesture-guard.ts export referenced nowhere, not even by its own tests. The
    [data-composer-input] literal now lives once, in core/composer-dom.ts,
    together with the editorElement() copy both composer effects carried and the
    visual-viewport reader; five modules used to spell the marker out.
    probe:composer-plus 17/17, probe:pointer-gating 6/6 and
    probe:multi-width 33/33 after the deletions, pnpm test 223/223.

Fixed

  • The drawer's dimming layer fades with the slide-out again — the swipe
    layer's close commit called a fade that had been a silent no-op since the
    shell.overlay slot took over the backdrop (its hook's only setter lived in
    the legacy overlay task nothing installed), so the dimming snapped away ~280ms
    after the drawer had already left: the host flips data-sidebar-collapsed when
    the animation lands, and React unmounts the layer at that moment. The new
    effects/backdrop-fade.ts resolves the layer itself and drives the same two
    inline properties the retired task did — opacity and pointer-events, both
    !important so React's style prop and the layer's entry animation cannot
    outrank them — with the rect flushed between the transition and the target so
    the fade provably starts at opacity 1. Resolving the element instead of taking
    a registered hook is deliberate: that seam is what went stale, and a function
    that finds its own element cannot. No restore is needed, because the layer is
    conditionally rendered per open. Gate: pnpm probe:backdrop-fade (4 rows: the
    layer opens opaque, goes mid-fade while the drawer slides — 12 partial frames
    and a final opacity of 0 — lands gone with the drawer closed, and a
    non-animated backdrop tap still closes cleanly). A/B: with the fade call
    disabled the mid-fade row FAILs with the layer at opacity 1 for the whole
    755ms window, which is the reported symptom.
  • The composer "+" stops raising the keyboard on the host's second focus —
    reported after the release above landed: tapping + with the keyboard dismissed
    still brought it up. The focus shadow was lifting too early — the host focuses
    the editor again from the effect that runs when its menu opens, which is after
    this plugin's bubble handler has read the menu as "not open" (mounted, not yet
    laid out). FOCUS_SHADOW_MIN_MS (700ms, still bounded by the 1.5s cap) makes
    the window a floor rather than a click-scoped one, and a focusin capture-phase
    blur takes back any focus that reaches the editor during the window, because a
    blur in a macrotask is too late (the IME has started). Both come from the
    community plugin mexiaosqwq/dsh-web-mobile's composer-keyboard-guard.ts,
    which measured the timing on a real iPhone; their v3.0.2 does not touch this
    path. probe:composer-plus gates the window (shadow-window-outlives-the-click,
    shadow-lifts-on-its-own), and the take-back rule is unit-tested
    (shouldTakeBackArmedFocus) because this host leaves the editor unfocusable
    while its menu is open, so a focus-driving probe row would pass either way.
    Refs: mexiaosqwq/dsh-web-mobile v3.0.0/v3.0.1.
  • The composer no longer drops the keyboard while it is being typed in —
    reported from the phone as "the keyboard hides by itself" after the fix above
    landed. The release trusts the keyboard reading, and that reading was the
    classic innerHeight - visualViewport.height inset: on a page that cannot
    scroll (this shell is a full-height flex layout), iOS shrinks the LAYOUT
    viewport with the keyboard too, so both heights move together and the inset
    stays near zero while the keyboard is up. The reading now also compares against
    the tallest visualViewport.height seen recently (resetting on rotation) and
    treats either signal saying "up" as up, and a keystroke in the editor
    (beforeinput / input / keydown / compositionupdate) keeps the release
    away for 1.5s whatever the viewport says. probe:composer-plus gained
    focus-release-holds-when-both-heights-shrink (emulating the iOS case: both
    heights shrink by the same amount), A/B-validated against the inset-only
    reading, where it FAILs with the editor losing focus and the release count
    rising while the emulated keyboard is up.
  • The iOS keyboard stops coming back on the first tap of the composer "+" —
    reported from the phone after the focus shadow landed. The shadow neutralises a
    focus() the host calls, and the tap that raises the keyboard focuses nothing
    at all: iOS keeps the composer's contenteditable as the active element once
    the keyboard goes away, and WebKit shows the keyboard for that retained
    editable on the next tap. The plugin now releases the focus while the keyboard
    is hidden — at install, on a 500ms heartbeat, on an editor focusin, on a tap,
    on visualViewport resize/scroll, and as soon as the "+" shadow lifts — and
    never during the tap (the uncompensated blur that bounced the composer row),
    never while the keyboard is up, never inside the 700ms grace after a finger
    lands on the editor, and never while the visual viewport is unreadable (a
    pinch-zoomed viewport reads as "no keyboard"). The blur's own viewport nudge is
    undone by restoring the scroll on the next frame when it moved by
    NUDGE_MAX_PX or less. The invariant is a state, not an event: measured, a
    focus can land on the editor with no focusin dispatched at all, which is what
    the heartbeat is for. probe:composer-plus runs the battery under an iPhone
    user agent (the release arms on detectIosWebKit) and gained three rows for
    it — precondition, release armed, and the release resuming after an editor tap
    — A/B-validated against the release inverted to never install for iOS, where
    they report the retained focus, releases=null, and the retained focus
    surviving a "+" tap.
  • The composer probe no longer inherits the typing scene's fake viewport —
    the scene replaces window.visualViewport to emulate a keyboard and only put
    back an own descriptor, which Chrome does not have (the property lives on the
    prototype): the stub outlived the scene, every later row read "keyboard up",
    and the focus rows were silently disarmed. It now deletes the stub when there
    is nothing to restore, and a composer.focus-release-precondition row reports
    the keyboard inset it is asserting against. A probe throw also lands as a
    probe.crashed FAIL row with a SUMMARY instead of a stack, and the
    touch taps use the camelCase touchEnd CDP event type (touchend is rejected
    with -32602, which killed the run mid-battery).

What's Changed

  • fix(mobile): use an installer-resolvable dsh peer floor by @ddtcorex in #56
  • fix(mobile): stop the jobs chip from taking the lineage trigger's place by @ddtcorex in #57
  • fix(mobile): scope the lineage chip's hover swallow to the chip, not to a stale hash by @ddtcorex in #58
  • fix(mobile): hold the composer keyboard on iOS and drop dead modules by @ddtcorex in #59
  • fix(mobile): fade the drawer backdrop with the slide-out again by @ddtcorex in #60
  • chore: release v1.6.1 by @ddtcorex in #61

Full Changelog: v1.6.0...v1.6.1

v1.6.0

Choose a tag to compare

@github-actions github-actions released this 23 Sep 04:04
43207db

Added

  • Sidebar panels have a way back to the conversation — a global panel
    (Plugins, Skills, …) replaces the main column and the host ships no exit:
    PanelRow's onClick is a bare selectPanel(id), so re-tapping the selected
    row re-selects it, and a panel page renders no session header — which is
    where the drawer toggle lives. One shared exit() now serves three routes:
    the system back key (one history entry armed while a panel owns the main
    column, given back when the panel leaves by another route so it cannot
    swallow the user's next back press), a re-tap of the already-selected panel
    row ([class*="panelRow"][aria-current="page"]), and the shell FAB, which
    reads as "back to conversation" there and moves to the top-left corner
    (data-mobile-nav-fab-mode="exit-panel") instead of the hero seat that used
    to float over the panel's own head. The host's panel-selection face is
    capability-probed, so a host generation without selectPanel leaves every
    route inert instead of throwing. Refs: mexiaosqwq/dsh-web-mobile v3.0.0/v3.0.1.
  • pnpm probe:composer-plus, pnpm probe:panel-exit and
    pnpm probe:multi-width
    — three live gates for the phone interactions
    above: the composer menu across four taps (open/close/open/close), the panel
    FAB back face plus back-key and re-tap exits and their history bookkeeping,
    and the layout tiers (phone 320/360/390/430, tablet 768/1023, desktop 1280)
    including the header icon actually painting an svg path. All three were
    A/B-validated against the pre-fix behaviour so they fail on a build that
    lacks the fix. pnpm test gains tests/phone-tier-gating.test.ts, which
    locks the tier structure (a min-width: 768px block must also cap 1023 and
    require a coarse pointer; the tablet overrides must come after the mobile
    gate).

Fixed

  • A second tap on the composer "+" closes the command menu — the button's
    onClick focuses the editor before toggling the launcher, and that focus
    re-enters controller.track(), whose first act is clearLauncher(); the
    host's "already open ⇒ dismiss" branch then compares against a null launcher
    and is unreachable, so every tap re-opened the menu (the host's own
    aria-expanded stays false while it is visibly open). The plugin now
    remembers the menu's state in the click capture phase and, in the bubble
    phase after React's handler, closes it through the host's own Escape path
    when it is still open. The opening tap is never touched.
  • Tapping "+" releases the editor focus while the menu is open — the
    command menu needs no soft keyboard, but that same focus call re-raises the
    IME on Android and slides the composer row up under the user's second tap
    (measured upstream: visual viewport 754 → 471 about 170ms after the tap, with
    no DOM event reaching the page). A tap on "+" now drops the focus before the
    click fires and repeats the release at 120/320/640ms while the menu is on
    screen, cancelled the moment the user touches the editor.
  • The two probe reds are gone — probe:panel-font now opens a session
    through the drawer (the injected dsh.sessions.current is only a hint on this
    host) and waits for the conversation history to render before measuring the
    content font axis: it used to sample while the flow still showed
    Loading history..., so the axis row was red against a placeholder. The axis is
    live on 0.1.7-alpha.2 (15px -> 22px, floor held). smoke:cdp no longer seeds
    localStorage['dsh.sessions.current'] with the requested session: the restored
    selection and the probe's own navigation opened the same session twice, the app
    released the first reference while the right sidebar awaited it, and the host
    logged Sidebar Session opening failed: ... is released — a page error from the
    probe's boot, not the plugin (with an unrestorable id the same drive logs zero).
    The scenario opens a session through the drawer instead, which also exercises
    the real header toggle. Result: probe:panel-font 12/12 and smoke:cdp
    17 pass / 1 state-skip / 0 fail with page.errors count=0.
  • Tapping "+" no longer raises the keyboard on iOS — the Android half of
    this (blur the editor after the tap) is not enough there: iOS follows DOM
    focus, so once the host's focusDraftEditor inside the button's onClick has
    run the keyboard is up and a later blur() does not take it back (reported on
    iOS after the first fix landed). The editor's programmatic focus is now
    neutralised for the length of the interaction (armed on pointerdown and again
    in the click capture phase, which is what covers a click with no pointerdown
    at all), and it is always given back — on the next tap anywhere, as soon as the
    menu leaves the DOM, on a hard 1.5s cap, and on dispose — because an override
    that outlives the interaction is worse than the bug it prevents: the editor
    could never be focused again (the exact bug the community plugin fixed in
    v3.0.1). The blur the Android fix introduced is now conditional — it runs only
    while the keyboard is already hidden (shouldDropEditorFocus, read from the
    visual viewport). Blurring an editor whose keyboard is UP starts the hide
    animation, and the keyboard is the composer's floor, so the row slid down under
    the finger on the FIRST tap only (reported from the iPhone after the first iOS
    fix); blurring one whose keyboard is already down is the state the IME re-rises
    from, and there the blur is what keeps the row still. probe:composer-plus now
    gates all of it, including a typing-state scene (emulated keyboard) where the
    focus must survive the tap. A second phone report pinned the blur itself as the
    remaining movement: the composer bounced up and back within 10-20ms, which no
    keyboard can do (iOS takes ~250ms to show or hide one) but a programmatic blur
    on iOS can, because it nudges the visual viewport. iOS therefore never blurs —
    the focus shadow is the whole defence there — while Android keeps the blur,
    which is what stops the IME re-rising into a hidden-keyboard editor. The shadow
    is also armed on touchstart now (idempotent), because iOS can fire the touch
    before the pointer event.
  • Host icons are resolved by name at runtime — the host's icon exports are
    generation-specific (IconXxxOutline16 on the 0.1.0-rc line versus
    IconXxxOutlineRegular / …Medium on 0.1.7) and the two generations share no
    names, so a static import resolves to undefined on the other one and React
    reports "Element type is invalid" for the whole plugin tree. The first
    candidate name the installed host actually exports now wins, and a name
    nothing matches renders nothing rather than crashing.
  • The plugin stylesheet replaces its previous copy — a plugin re-applied in
    the same JS environment (client hot reload, or a second apply whose dispose
    never ran) stacked a second <style> tag with the same rules, and the older
    tag could win on source order inside the cascade: the symptom was "I changed
    the CSS and nothing moved" while the served bundle was correct. The mount now
    removes any tag carrying the plugin's data-plugin marker first, keeps the
    fresh tag last in <head> for its !important overrides, and never
    resurrects a disposed tag from the deferred re-append.

What's Changed

  • chore: target DSH 0.1.7-alpha.2 by @ddtcorex in #51
  • feat(mobile): panel exit, composer "+" fixes and icon/stylesheet hardening by @ddtcorex in #52
  • fix(mobile): keep the iOS keyboard down when the composer "+" is tapped by @ddtcorex in #53
  • fix(mobile): clear the two probe reds at their source by @ddtcorex in #54
  • chore: release v1.6.0 by @ddtcorex in #55

Full Changelog: v1.5.0...v1.6.0