Merge nucleic/bold-thistle-marten-19eu into dev
This commit is contained in:
@@ -9,6 +9,7 @@ struct SessionDetailView: View {
|
||||
@Environment(AppStore.self) private var store
|
||||
@Environment(\.appPalette) private var palette
|
||||
@Environment(\.accessibilityReduceMotion) private var reduceMotion
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
/// Panel layout for the session's side panels.
|
||||
@Environment(PanelLayoutStore.self) private var panels
|
||||
@State private var integrating = false
|
||||
@@ -1183,6 +1184,20 @@ struct SessionDetailView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gap between the Intelligence rail and the model·effort caption it resolves to. Negative on
|
||||
/// purpose: the rail is drawn centered in a 28pt frame sized for its 20pt nub, so it carries
|
||||
/// ~10pt of its own empty space below the track. Pulling the caption up through that dead
|
||||
/// space sets the two a few points apart optically, which is what the eye reads as spacing —
|
||||
/// a positive gap here left the pair looking detached. Shared with the reserved footprint
|
||||
/// below so the invariant height stays exact.
|
||||
private static let intelligenceCaptionSpacing: CGFloat = -5
|
||||
|
||||
/// Type size for the composer's flat mode controls (Auto, Ship, its branch menu). A step down
|
||||
/// from the row's `.callout`: with the borders and fills gone these read as quiet labels
|
||||
/// rather than buttons, and the smaller size is what lets the row lose height without the
|
||||
/// words crowding each other.
|
||||
private static let composerControlFont: Font = .subheadline
|
||||
|
||||
private var autoToggle: some View {
|
||||
// Nucleic Control chats always run autonomously, so Auto is forced on and the toggle
|
||||
// is locked there (nvrsion can't function without it). Off-control it's a normal toggle.
|
||||
@@ -1194,14 +1209,16 @@ struct SessionDetailView: View {
|
||||
} label: {
|
||||
Label("Auto", systemImage: on ? "bolt.fill" : "bolt.slash")
|
||||
.foregroundStyle(on ? composerAccent : Color.secondary)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
// Flat and tight (see `composerControlFont`): no fill, no border, and barely any
|
||||
// vertical padding — the glyph and the accent carry the state, and the hit area
|
||||
// still clears the pointer target with the horizontal inset alone.
|
||||
.font(Self.composerControlFont)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 1)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(locked)
|
||||
.background(AppTheme.composerFill, in: .rect(cornerRadius: 6))
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).strokeBorder(AppTheme.composerStroke, lineWidth: 1))
|
||||
.fixedSize()
|
||||
.help(locked
|
||||
? lockedHelp
|
||||
@@ -1241,8 +1258,9 @@ struct SessionDetailView: View {
|
||||
} label: {
|
||||
Label("Ship", systemImage: on ? "shippingbox.fill" : "shippingbox")
|
||||
.foregroundStyle(on ? composerAccent : Color.secondary)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.font(Self.composerControlFont)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 1)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
@@ -1266,13 +1284,13 @@ struct SessionDetailView: View {
|
||||
// nvrsion there is a single shared trunk promoted at the project level, so there's no
|
||||
// per-session destination to choose — show just the Merge toggle, no branch picker.
|
||||
if on && !nvrsionActive {
|
||||
Divider().frame(height: 16)
|
||||
// With the shared border gone, this hairline is the only thing binding Ship to
|
||||
// the branch it ships into — so it stays, trimmed to the shorter row.
|
||||
Divider().frame(height: 11)
|
||||
shipDestinationMenu(inherited: inherited, override: override, current: current)
|
||||
.transition(.move(edge: .leading).combined(with: .opacity))
|
||||
}
|
||||
}
|
||||
.background(AppTheme.composerFill, in: .rect(cornerRadius: 6))
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).strokeBorder(AppTheme.composerStroke, lineWidth: 1))
|
||||
.fixedSize()
|
||||
}
|
||||
|
||||
@@ -1306,8 +1324,9 @@ struct SessionDetailView: View {
|
||||
Text(current)
|
||||
Image(systemName: "chevron.down").font(.caption2)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.font(Self.composerControlFont)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 1)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.menuStyle(.button)
|
||||
@@ -2916,6 +2935,15 @@ struct SessionDetailView: View {
|
||||
/// Whether there is content and a usable routed destination.
|
||||
private var canSend: Bool { hasSendableContent && routedRouteAvailable }
|
||||
|
||||
/// A little ink in the composer's glass. The bare `.regular` material brightens whatever it
|
||||
/// samples, so over a dark transcript the card sat a shade lighter than the pane around it and
|
||||
/// read as a pale bar laid on the chat rather than a pane of it. A black tint settles it back
|
||||
/// down without touching the refraction. Light mode takes much less — the same amount there
|
||||
/// would be a gray smudge over an already-pale background.
|
||||
private var composerGlassTint: Color {
|
||||
.black.opacity(colorScheme == .dark ? 0.3 : 0.08)
|
||||
}
|
||||
|
||||
/// The pane's floating bottom furniture: a pending approval card (when there is one) above the
|
||||
/// composer, both hovering over the transcript on glass. Measured as one block — the height the
|
||||
/// transcript reserves beneath its last message and anchors its fade to — so the two stay in
|
||||
@@ -3096,7 +3124,7 @@ struct SessionDetailView: View {
|
||||
ZStack(alignment: .trailing) {
|
||||
composerIntelligenceControlsFootprint
|
||||
if intelligenceRoutingActive {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
VStack(alignment: .leading, spacing: Self.intelligenceCaptionSpacing) {
|
||||
IntelligenceSlider(
|
||||
level: sessionIntelligenceLevelBinding,
|
||||
orchestraTier: sessionOrchestraTierBinding,
|
||||
@@ -3158,7 +3186,8 @@ struct SessionDetailView: View {
|
||||
// Backed by the system Liquid Glass material, exactly like the floating new-chat
|
||||
// composer: the input area is a card hovering over the conversation, sampling and
|
||||
// refracting the transcript that scrolls behind it — no opaque backing, no divider.
|
||||
.glassEffect(.regular, in: .rect(cornerRadius: 16))
|
||||
// Tinted a little darker than the bare material (see `composerGlassTint`).
|
||||
.glassEffect(.regular.tint(composerGlassTint), in: .rect(cornerRadius: 16))
|
||||
// The glass renders but doesn't hit-test, so give the card a solid hit shape. It now
|
||||
// floats over live transcript, so this is load-bearing: without it a click in the gap
|
||||
// between two controls would land on the message underneath.
|
||||
@@ -3241,7 +3270,7 @@ struct SessionDetailView: View {
|
||||
/// Dynamic-Type-aware maximum footprint of the lower control row. Reusing the real route
|
||||
/// preview keeps this exactly in step with its scaled caption height without a magic constant.
|
||||
private var composerIntelligenceControlsFootprint: some View {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
VStack(alignment: .leading, spacing: Self.intelligenceCaptionSpacing) {
|
||||
Color.clear.frame(
|
||||
width: IntelligenceSlider.preferredWidth,
|
||||
height: IntelligenceSlider.preferredHeight)
|
||||
|
||||
Reference in New Issue
Block a user