Files
hapi/web/src/components
SSU-WEI HUANGandGitHub 79ffe9aa0e feat(web): composer model/effort value buttons and first-class permission (part of #1438) (#1475)
* feat(web): composer model/effort value buttons and settings order

Wide composers now show [model] and [effort] value buttons for non-Pi
flavors (labels from the current session values), opening the settings
sheet on click. Narrow viewports collapse to the settings button only
via a new useNarrowViewport hook. The settings sheet reorders to
Model -> Effort -> Permission -> other settings (Fast mode,
collaboration, Copilot agent mode) so permission is first-class.

Toolbar customization gains 'model'/'effort' items with settings labels.
Pi keeps its dedicated model/thinking panels unchanged (unified
descriptor-driven sheet is a follow-up).

* fix(web): satisfy strict types in composer value-button test harness

* fix(web): address review findings on composer model/effort value buttons

- Normalize null/'auto'/'default' model wire values onto the value:null
  option so default-model sessions keep a localized label button (Major)
- Exempt model/effort value buttons from the settings outside-click
  dismissal so a second click closes the sheet instead of reopening it
- Read matchMedia synchronously in useNarrowViewport so narrow first
  paints never flash the wide toolbar
- Add regression tests: model=null/'auto' labels, toggle-close behavior,
  and initial narrow-viewport render

* feat(web): fold Pi into the generic composer model/effort value buttons

Pi sessions previously exposed model/effort twice: dedicated 'Pi model' /
'Pi thinking level' toolbar buttons (PiModelPanel/PiThinkingLevelPanel)
AND the settings sheet's generic Model/Effort sections. Consolidate so Pi
looks exactly like every other flavor:

- Pi now uses the generic model/effort value buttons; labels resolve from
  the provider-qualified piModels catalog (name -> modelId -> session id).
- The settings sheet's Model section already renders provider-grouped Pi
  rows and Effort renders Pi thinking levels, so the dedicated panels and
  their toolbar slots are deleted.
- Keep Pi's mid-turn control affordance (#1442): Pi turns hold
  thread.isDisabled for minutes, so Pi model/effort controls stay enabled
  while a turn is running (configurationControlsDisabled instead of
  controlsDisabled), including the sheet rows.
- Drop 'piModel'/'piThinking' toolbar layout items; persisted layouts
  normalize them away automatically.

Tests: pi value-button label/sheet tests, mid-turn model selection via the
unified sheet, toolbar layout defaults.

* fix(web): keep a session-settings trigger on narrow viewports and mid-turn Pi

Address the HAPI review bot's two Minor findings on the Pi consolidation:

- Narrow viewports collapse the model/effort value buttons into the
  settings sheet, so a persisted toolbar layout hiding the gear left no
  session-settings trigger at all. ComposerButtons now forces the gear
  back into the rendered layout on narrow viewports (wide layouts keep
  honoring the user's hidden choice).
- The Pi mid-turn live-control rule only reached the value buttons and
  sheet rows; with those buttons gone on narrow, the gear was still
  disabled by controlsDisabled for the whole (minutes-long) Pi turn.
  HappyComposer now passes settingsDisabled={modelEffortControlsDisabled}
  so the gear stays clickable mid-turn for Pi exactly like the buttons.

Tests: narrow + hidden-gear layout keeps Settings; narrow Pi mid-turn
gear stays enabled and opens the provider-grouped sheet.

* fix(web): address HAPI Bot Pi settings-sheet findings

Three Minor findings from the review bot on the unified Pi sheet:

- Provider-qualified selection: rows compared only modelId, so duplicate
  model IDs across providers all looked selected. Compare against
  piSelectedModel's provider+modelId when available.
- Thinking-level reset: the removed Pi panel toggled the current level
  back to null; the unified effort rows only submitted concrete values.
  Re-clicking the selected effort row now clears it for Pi.
- Memo staleness: the settings-sheet memo did not depend on
  modelEffortControlsDisabled, so a Pi disabled-state transition while
  the sheet was open left rows enabled from the prior render.

Tests: colliding model IDs highlight only the matching provider row,
re-clicking the selected effort row sends null, and a rerender with
active=false disables the open sheet's rows.

* fix(web): include piSelectedModel in settings-sheet memo deps

The overlays memo reads piSelectedModel for provider-qualified row
highlighting but only declared the derived selectedPiModel. When
piSelectedModel hydrates from absent to a qualifier that resolves to
the same catalog object, the memo is reused and duplicate model IDs
stay highlighted across providers. Add the raw prop to the dep array.

* fix(web): gate Pi model rows on catalog; reset drill-down via value button

Address the HAPI Bot review on the unified composer settings sheet:

- Pi no longer falls back to the generic synthesized modelOptions rows
  when its provider catalog is empty/loading. Selecting one of those
  would post a bare model id that runPi cannot resolve to a provider
  (first cached match or 409). The Model section now only renders for
  Pi when piModelGroups exists, and renders grouped rows exclusively.
- Closing the sheet through the model/effort value button now goes
  through handleSettingsToggle, so a Cursor variant drill-down resets
  to the base model list on reopen (previously only the gear and
  outside-click paths cleared it).

Tests: empty Pi catalog hides Model section; value-button close resets
Cursor drill-down.

* fix(web): hide Pi effort controls until the selected model resolves

Address the HAPI Bot review: with the catalog still loading/failed there
is no selectedPiModel to derive a capability map from, but the unified
effort control stayed enabled (mid-turn Pi controls are intentionally
live). Selecting a level would send set_thinking_level for a model that
may not support reasoning, and the RPC can be rejected after the sheet
closed. The old dedicated panel guarded this state.

showEffortSettings now requires a resolved, reasoning-capable Pi model;
the effort value button hides the same way. With an empty catalog Pi
exposes no settings trigger at all, matching the old control states.

Tests: unresolved Pi catalog mid-turn exposes no effort action.

* fix(web): hide the Pi model trigger until the catalog resolves

Address the HAPI Bot Minor finding: with an empty/loading Pi catalog the
model value button fell back to the bare session model id and rendered
an enabled trigger that opens no Model section. Show the button only
once the provider-qualified catalog entry resolves.
2026-08-16 22:43:10 +08:00
..