发布

  • fix(x): one hover companion — delete the summoned ask bar, no flash, no dead summons (#878)

    frostbyte_neo 发布于 2026-08-20 12:19:23 +00:00

    • fix(x): hover companion — no flash of the old bar, no dead summons, one flow for every entry point

    The ⌥⇧Space hover mode (quick access) had three real bugs:

    1. The first summon created the companion window and show()ed it while
      the page was still loading: index.html painted a theme slab, then the
      renderer's default summoned layout (the old text bar), then the
      Skipper. Re-summons flashed the summoned layout for a frame too (the
      hidden window keeps it, and show() fired before the re-render).
    2. startHoverCall held its in-flight guard across the sticky screen
      share; getDisplayMedia hangs forever when Screen Recording is denied,
      so after the first summon every later chord was a silent no-op.
    3. QuickAskBar called useMemo after conditional early returns — a pill ⇄
      card switch (camera toggle) would throw React's hook-count error and
      blank the window.

    Fixes:

    • Reveal protocol: quick-ask:mode carries a seq, the renderer acks
      quickAsk:modeApplied two frames after committing that role, and main
      orders the window in at opacity 0 and only makes it opaque / focuses on
      the ack. Fold pushes first and shrinks on the ack. The renderer paints
      nothing until its role is known; index.html gives #quick-ask and
      #screen-pointer a transparent background from the very first paint.
    • startScreenShare is time-boxed (10s) and fails cleanly; the hover guard
      is released right after startCall and the share is fire-and-forget; a
      session that fails to start falls back to the text card.
    • ONE hover flow: chord, tray, tuck handle, toast "Try it", composer call
      button and the "Share screen" preset all end in startHoverCall(). A
      summon with no (or a still-loading) app window recreates it hidden and
      re-relays on a quickAsk:appReady handshake; closing the app window
      mid-call unpins the companion. Call-state cache survives fullscreen ⇄
      popout (camera calls return as the pill) and is cleared by an explicit
      idle push at call end. Hooks hoisted above the early returns.
    • [companion] mode-transition breadcrumbs in main; VIDEO_MODE.md updated.

    Co-Authored-By: Claude Fable 5 noreply@anthropic.com

    • fix(x): a summon right after app start waits for the voice probe instead of answering with the text card

    The remaining "old quick access comes up instead of hover mode" case:
    voiceAvailable/ttsAvailable start false and are filled by an async probe
    (voice:getConfig + oauth:getState) at mount. A chord in the first seconds
    after reopening the app read that not-yet-resolved false as "no voice
    configured" and answered with quickAsk:show — the old text card, with no
    Skipper ever arriving.

    Reproduced deterministically by delaying the probe: main logged
    hidden → summoned (text card (fresh window)). With this change the same
    run logs hidden → pinned (call surface (summoned)).

    • App: mirror the probe result into refs (written inside the probe, so a
      waiter doesn't depend on a re-render), keep the in-flight promise, and
      have startHoverCall await it (capped at 4s, starting one if nothing has
      probed yet) before deciding. The in-flight guard now covers that wait,
      and a call started meanwhile just re-asserts its floating surface. A
      genuinely unconfigured voice still falls back to the text card.
    • Bar: the tuck handle is optimistic until its own probe answers — the app
      window owns the authoritative check and its own fallback, so an early
      click relays instead of dying as a dimmed no-op.

    Co-Authored-By: Claude Fable 5 noreply@anthropic.com

    • fix(x): delete the summoned ask bar — the hover companion is the only surface

    The old Spotlight-style quick-ask bar is gone, not just avoided. It was
    only ever a fallback surface, and every "hover mode is glitchy" report was
    that bar appearing where the Skipper belonged. With one role left there is
    no second layout to flash, race, or get stuck in.

    Removed:

    • main: the summoned mode, its geometry (positionSummoned, FRAME_*,
      BOTTOM_MARGIN), showSummonedCard/showQuickAsk/hideQuickAsk, the
      blur-to-dismiss handler, and the trigger enum. toggleQuickAsk() now has
      exactly one outcome: fold/unfold the Skipper's text when it's up, else
      relay a summon. An unanswered relay is re-sent once by a watchdog and
      then logged — never answered with a different surface.
    • IPC: quickAsk:show / quickAsk:hide / quickAsk:stop + quick-ask:stop /
      quickAsk:state + quick-ask:state / quickAsk:setOptions +
      quick-ask:set-options / quickAsk:optionsState + quick-ask:options-state /
      quick-ask:summoned. CompanionMode is now 'hidden' | 'pinned'.
    • renderer: the bar layout, its answer panel, local dictation and the
      ⌥⇧Space hold-to-talk chord, the voice-out and share-without-a-call
      toggles, dismiss/stage-click-to-dismiss, and the tuck handle's
      start-a-call role. The window paints NOTHING unless it is pinned.
    • App: quickAskActiveRef / quickAskOptionsRef / quickAskStartedAtRef /
      speakTurnRef and the answer-mirror effect. Speech now follows the
      question's modality alone.

    A summon that can't become a session (voice unconfigured, or the engine
    failed to start) is explained in the APP window — brought to the front
    with a toast that opens Settings — instead of silently becoming a
    different floating thing.

    Verified on the dev build: earliest-possible summon after an app start
    lands as the Skipper (no other layout at any frame), fold/unfold/end/
    re-summon cycle clean, hidden window renders 0 bytes, and the no-voice
    path shows the toast with the companion staying hidden.

    Co-Authored-By: Claude Fable 5 noreply@anthropic.com

    • docs(x): retire the last references to the deleted ask bar

    Comment- and copy-only follow-up to 5eeb56c9: several comments still
    described the summoned bar's status line, voice toggle, destination chip
    and geometry, and two user-facing strings still called the feature "Quick
    Ask". Nothing behavioural.

    Co-Authored-By: Claude Fable 5 noreply@anthropic.com


    Co-authored-by: Claude Fable 5 noreply@anthropic.com

    下载附件