Nucleic: Build A Claude “ultracode”-like System
This commit is contained in:
@@ -8,6 +8,7 @@ struct StartChatComposer: View {
|
|||||||
@State private var projectID: ProjectID?
|
@State private var projectID: ProjectID?
|
||||||
@State private var draft = ""
|
@State private var draft = ""
|
||||||
@State private var auto = false
|
@State private var auto = false
|
||||||
|
@State private var effort = MobileEfforts.fallback
|
||||||
|
|
||||||
private var projects: [WireProject] { store.dashboard.projects }
|
private var projects: [WireProject] { store.dashboard.projects }
|
||||||
private var selected: WireProject? {
|
private var selected: WireProject? {
|
||||||
@@ -27,6 +28,7 @@ struct StartChatComposer: View {
|
|||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
|
EffortMenu(effort: $effort)
|
||||||
Toggle(isOn: $auto) {
|
Toggle(isOn: $auto) {
|
||||||
Label("Auto", systemImage: auto ? "bolt.fill" : "bolt.slash")
|
Label("Auto", systemImage: auto ? "bolt.fill" : "bolt.slash")
|
||||||
}
|
}
|
||||||
@@ -38,9 +40,10 @@ struct StartChatComposer: View {
|
|||||||
TextField("Describe a task…", text: $draft, axis: .vertical)
|
TextField("Describe a task…", text: $draft, axis: .vertical)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
.lineLimit(1...5)
|
.lineLimit(1...5)
|
||||||
|
.ultracodeGlow(active: MobileEfforts.isUltracode(effort))
|
||||||
Button {
|
Button {
|
||||||
if let project = selected {
|
if let project = selected {
|
||||||
store.startChat(in: project.id, message: draft, auto: auto)
|
store.startChat(in: project.id, message: draft, effort: effort, auto: auto)
|
||||||
draft = ""
|
draft = ""
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ struct ProjectDetailView: View {
|
|||||||
@EnvironmentObject var store: RemoteStore
|
@EnvironmentObject var store: RemoteStore
|
||||||
let project: WireProject
|
let project: WireProject
|
||||||
@State private var draft = ""
|
@State private var draft = ""
|
||||||
|
@State private var effort = MobileEfforts.fallback
|
||||||
|
|
||||||
private var sessions: [WireSessionSummary] {
|
private var sessions: [WireSessionSummary] {
|
||||||
store.liveSessions
|
store.liveSessions
|
||||||
@@ -57,16 +58,23 @@ struct ProjectDetailView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack {
|
||||||
|
EffortMenu(effort: $effort)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
HStack(alignment: .bottom, spacing: 8) {
|
HStack(alignment: .bottom, spacing: 8) {
|
||||||
TextField("Start a chat in \(project.name)…", text: $draft, axis: .vertical)
|
TextField("Start a chat in \(project.name)…", text: $draft, axis: .vertical)
|
||||||
.textFieldStyle(.roundedBorder).lineLimit(1...4)
|
.textFieldStyle(.roundedBorder).lineLimit(1...4)
|
||||||
|
.ultracodeGlow(active: MobileEfforts.isUltracode(effort))
|
||||||
Button {
|
Button {
|
||||||
store.startChat(in: project.id, message: draft)
|
store.startChat(in: project.id, message: draft, effort: effort)
|
||||||
draft = ""
|
draft = ""
|
||||||
} label: { Image(systemName: "arrow.up.circle.fill").font(.title2) }
|
} label: { Image(systemName: "arrow.up.circle.fill").font(.title2) }
|
||||||
.disabled(draft.trimmingCharacters(in: .whitespaces).isEmpty || !store.canControl)
|
.disabled(draft.trimmingCharacters(in: .whitespaces).isEmpty || !store.canControl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Section("Sessions") {
|
Section("Sessions") {
|
||||||
if sessions.isEmpty {
|
if sessions.isEmpty {
|
||||||
Text("No sessions yet.").foregroundStyle(.secondary)
|
Text("No sessions yet.").foregroundStyle(.secondary)
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ enum Palette {
|
|||||||
static let paused = Color(red: 0.62, green: 0.49, blue: 0.93) // purple
|
static let paused = Color(red: 0.62, green: 0.49, blue: 0.93) // purple
|
||||||
static let danger = Color(red: 0.92, green: 0.34, blue: 0.34) // red
|
static let danger = Color(red: 0.92, green: 0.34, blue: 0.34) // red
|
||||||
static let neutral = Color.secondary
|
static let neutral = Color.secondary
|
||||||
|
/// The ultracode (orchestration mode) signature purple — mirrors the Mac's
|
||||||
|
/// `AppTheme.ultracode`. Used for the effort picker's Ultracode option and the
|
||||||
|
/// composer's purple ring.
|
||||||
|
static let ultracode = Color(red: 0.55, green: 0.28, blue: 0.95)
|
||||||
|
|
||||||
/// Dot/accent color for a session's status, refined by the last turn's disposition — a
|
/// Dot/accent color for a session's status, refined by the last turn's disposition — a
|
||||||
/// finished-the-work turn reads as "done" (success) rather than the calm "ready" accent.
|
/// finished-the-work turn reads as "done" (success) rather than the calm "ready" accent.
|
||||||
@@ -113,4 +117,79 @@ extension View {
|
|||||||
func card(padding: CGFloat = 14) -> some View { modifier(CardBackground(padding: padding)) }
|
func card(padding: CGFloat = 14) -> some View { modifier(CardBackground(padding: padding)) }
|
||||||
/// Apply the teal accent app-wide.
|
/// Apply the teal accent app-wide.
|
||||||
func nucleicTint() -> some View { tint(Palette.accent) }
|
func nucleicTint() -> some View { tint(Palette.accent) }
|
||||||
|
/// Wrap a composer field in the ultracode purple ring while `active` (mirrors the Mac's
|
||||||
|
/// animated composer glow, lighter touch).
|
||||||
|
func ultracodeGlow(active: Bool, cornerRadius: CGFloat = 8) -> some View {
|
||||||
|
modifier(UltracodeGlowModifier(active: active, cornerRadius: cornerRadius))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The effort levels the remote offers, mirroring the Mac's `ModelCatalog.efforts` plus
|
||||||
|
/// ultracode. `ultracodeSentinel` MUST stay equal to the host's `OrchestrationMode.effortSentinel`
|
||||||
|
/// — it's sent verbatim over the sync wire and resolved on the host (to `xhigh` + standing
|
||||||
|
/// consent), so a mismatch would silently disable the mode.
|
||||||
|
enum MobileEfforts {
|
||||||
|
static let levels = ["low", "medium", "high", "xhigh", "max"]
|
||||||
|
static let ultracodeSentinel = "ultracode"
|
||||||
|
static let fallback = "high"
|
||||||
|
|
||||||
|
static func isUltracode(_ effort: String) -> Bool { effort == ultracodeSentinel }
|
||||||
|
static func displayName(_ effort: String) -> String { isUltracode(effort) ? "Ultracode" : effort }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A compact effort selector for the remote composers. Ultracode sits below the API levels,
|
||||||
|
/// set apart with a sparkles glyph and the purple accent.
|
||||||
|
struct EffortMenu: View {
|
||||||
|
@Binding var effort: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Menu {
|
||||||
|
ForEach(MobileEfforts.levels, id: \.self) { level in
|
||||||
|
Button { effort = level } label: {
|
||||||
|
if effort == level { Label(level, systemImage: "checkmark") } else { Text(level) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
Button { effort = MobileEfforts.ultracodeSentinel } label: {
|
||||||
|
Label("Ultracode", systemImage: MobileEfforts.isUltracode(effort) ? "checkmark" : "sparkles")
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: MobileEfforts.isUltracode(effort) ? "sparkles" : "slider.horizontal.3")
|
||||||
|
Text(MobileEfforts.displayName(effort))
|
||||||
|
}
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(MobileEfforts.isUltracode(effort) ? Palette.ultracode : Palette.accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A gentle purple ring that breathes around a composer while ultracode is selected — the
|
||||||
|
/// remote echo of the Mac's `UltracodeGlow`. Honors Reduce Motion (holds steady).
|
||||||
|
struct UltracodeGlowModifier: ViewModifier {
|
||||||
|
var active: Bool
|
||||||
|
var cornerRadius: CGFloat
|
||||||
|
@Environment(\.accessibilityReduceMotion) private var reduceMotion
|
||||||
|
@State private var pulsing = false
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
let animating = active && pulsing && !reduceMotion
|
||||||
|
return content
|
||||||
|
.overlay {
|
||||||
|
if active {
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||||
|
.strokeBorder(Palette.ultracode.opacity(animating ? 0.9 : 0.5),
|
||||||
|
lineWidth: animating ? 1.6 : 1.2)
|
||||||
|
.shadow(color: Palette.ultracode.opacity(animating ? 0.5 : 0.2),
|
||||||
|
radius: animating ? 8 : 3)
|
||||||
|
.animation(
|
||||||
|
reduceMotion ? .default
|
||||||
|
: .easeInOut(duration: 1.8).repeatForever(autoreverses: true),
|
||||||
|
value: pulsing)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear { pulsing = active }
|
||||||
|
.onChange(of: active) { _, now in pulsing = now }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user