nvrsion: Add: Fix truncation of “Run now” button text in New idea composer, add model selector support for Claude Fable 5, refactor Project Workflow Enhancement, add Wait Button Logic, update iOS remote to match Chat Message Queue Display, block sandboxed/controlled agents from running git commands via mcp__nucleic__host_exec using container-level shims, add chat icon animation to bounce when chat is blocked for >60 seconds, add “Cmd+Shift+N” shortcut to open a floating chat composer over the window, remove “Run now” button from “New idea” composer, and shield Git command execution by scanning inside ‘-c’ strings.
Nucleic-Promote: 1 Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -170,17 +170,16 @@ struct SessionDetailView: View {
|
||||
let running = status == .running
|
||||
let canCompose = running || status == .awaitingInput || status.isTerminal
|
||||
VStack(spacing: 8) {
|
||||
// A follow-up submitted mid-turn, held by the host to send when the current
|
||||
// turn finishes.
|
||||
if let queued = summary?.queuedMessage, !queued.isEmpty {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "clock").foregroundStyle(.secondary)
|
||||
Text(queued).lineLimit(1).truncationMode(.tail)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer(minLength: 8)
|
||||
// Follow-ups submitted mid-turn, held by the host to send when the current turn
|
||||
// finishes. Each is shown as its own pill (with its attachments) and — at control
|
||||
// scope — can be cancelled individually, matching the Mac composer. (They still
|
||||
// send as one combined turn.)
|
||||
if let queued = summary?.queuedMessages, !queued.isEmpty {
|
||||
VStack(spacing: 6) {
|
||||
ForEach(queued) { message in
|
||||
queuedMessageRow(message)
|
||||
}
|
||||
}
|
||||
.font(.footnote)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
if running {
|
||||
HStack(spacing: 8) {
|
||||
@@ -211,6 +210,56 @@ struct SessionDetailView: View {
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
/// A queued follow-up shown above the composer: its text (or attachment names) with the
|
||||
/// attachments as chips, and — at control scope — a ✕ that cancels just this message.
|
||||
@ViewBuilder
|
||||
private func queuedMessageRow(_ message: QueuedMessage) -> some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "clock").foregroundStyle(.secondary)
|
||||
Text(queuedRowText(message)).lineLimit(1).truncationMode(.tail)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer(minLength: 8)
|
||||
if store.canControl {
|
||||
Button {
|
||||
// Refill the composer for editing (text only — the phone can't stage the
|
||||
// host's files) unless the user has already started typing; then drop it.
|
||||
if draft.trimmingCharacters(in: .whitespaces).isEmpty { draft = message.text }
|
||||
store.cancelQueuedMessage(sessionID, message.id)
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill").foregroundStyle(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(!store.connectivity.isLive)
|
||||
}
|
||||
}
|
||||
.font(.footnote)
|
||||
if !message.attachments.isEmpty {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 6) {
|
||||
ForEach(message.attachments) { attachment in
|
||||
Label(attachment.filename, systemImage: "doc")
|
||||
.font(.caption2)
|
||||
.lineLimit(1)
|
||||
.padding(.horizontal, 7).padding(.vertical, 3)
|
||||
.background(.quaternary, in: .capsule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 8).padding(.vertical, 6)
|
||||
.background(.quaternary.opacity(0.4), in: .rect(cornerRadius: 10))
|
||||
}
|
||||
|
||||
/// The one-line label for a queued pill: the message text, or — for an attachment-only
|
||||
/// message — the attachment names (the chips below repeat them), so it's never blank.
|
||||
private func queuedRowText(_ message: QueuedMessage) -> String {
|
||||
if !message.text.isEmpty { return message.text }
|
||||
return message.attachments.map(\.filename).joined(separator: ", ")
|
||||
}
|
||||
}
|
||||
|
||||
struct TranscriptList: View {
|
||||
|
||||
Reference in New Issue
Block a user