From ade62ceff707b0c9a6de529d87d78f27791a0db4 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Tue, 7 Jul 2026 05:17:27 -0700 Subject: [PATCH] nvrsion: Add: Provisioned VM Image Preparation to run on macvm-spike; Fix: Move Model Selector Position. Nucleic-Promote: 1 Co-authored-by: Nucleic --- .../Views/SessionDetailView.swift | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift b/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift index 73323df..ea3f2e1 100644 --- a/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift +++ b/NucleicRemote/NucleicRemote/Views/SessionDetailView.swift @@ -159,23 +159,13 @@ struct SessionDetailView: View { return min(100, Int((Double(used) / Double(window)) * 100)) } - /// The model / effort / auto controls for the open session — the mobile echo of the Mac's - /// session header, folded into the glass chat bar so they don't cost a strip of transcript - /// height. Reads current state from the summary; each change is a control intent. + /// The context-occupancy readout and the Auto toggle for the open session, folded into the + /// glass chat bar so they don't cost a strip of transcript height. The model / effort + /// selectors used to sit here too, but they moved into the ⋯ menu (`modelEffortMenu`) to keep + /// the bar compact. Reads current state from the summary; each change is a control intent. @ViewBuilder private func controlRow(_ summary: WireSessionSummary) -> some View { HStack(spacing: 10) { - // A session's backend is fixed at creation, so only same-backend models are offered. - ModelMenu(model: modelBinding, catalog: store.modelCatalog, backend: summary.backend) - EffortMenu(effort: effortBinding, catalog: store.modelCatalog, - // No explicit model yet → use the session backend's default, so the menu - // shows that backend's effort range rather than collapsing to one level. - modelSKU: summary.model ?? store.modelCatalog.models(for: summary.backend).first?.sku, - controlled: controlled, - // Orchestra is a one-way latch for an existing chat — the host won't turn it - // off, so disable the other levels here to match. - lockWhenOrchestra: true) - Spacer(minLength: 4) if let percent = contextPercent { // Weight and primary ink (not `.secondary`) so the metric stays legible over // whatever the Liquid Glass bar is blurring through. @@ -183,6 +173,7 @@ struct SessionDetailView: View { .font(.caption2.weight(.semibold)).foregroundStyle(.primary) .help("Context window used") } + Spacer(minLength: 4) // Locked on for a Nucleic Control project — those chats always run autonomously, and // the host keeps Auto on regardless of what the phone sends, so show it locked on to // match rather than offering a toggle that wouldn't take. @@ -198,7 +189,8 @@ struct SessionDetailView: View { } } - /// The ⋯ menu: the diff viewer for everyone, then the control-scope session actions. + /// The ⋯ menu: the diff viewer for everyone, then — at control scope — the next-turn model / + /// effort selectors (moved off the composer to keep it compact) and the session actions. @ViewBuilder private var sessionMenu: some View { Menu { @@ -206,6 +198,8 @@ struct SessionDetailView: View { Label(diffLabel, systemImage: "plus.forwardslash.minus") } if let summary, store.canControl { + Divider() + modelEffortMenu(summary) Divider() sessionActions(summary) } @@ -214,6 +208,23 @@ struct SessionDetailView: View { } } + /// The model and effort selectors for the next turn, relocated from the composer's control row + /// into the ⋯ menu so the glass chat bar stays compact — each opens as a submenu of levels. + /// `ModelMenu` / `EffortMenu` are themselves menus, so they nest cleanly here. + @ViewBuilder + private func modelEffortMenu(_ summary: WireSessionSummary) -> some View { + // A session's backend is fixed at creation, so only same-backend models are offered. + ModelMenu(model: modelBinding, catalog: store.modelCatalog, backend: summary.backend) + EffortMenu(effort: effortBinding, catalog: store.modelCatalog, + // No explicit model yet → use the session backend's default, so the menu + // shows that backend's effort range rather than collapsing to one level. + modelSKU: summary.model ?? store.modelCatalog.models(for: summary.backend).first?.sku, + controlled: controlled, + // Orchestra is a one-way latch for an existing chat — the host won't turn it + // off, so disable the other levels here to match. + lockWhenOrchestra: true) + } + /// "View changes" with the live +/− counts when the host projects them, so the menu hints /// whether there's anything to look at before opening the sheet. private var diffLabel: String {