Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Packages/macOS/CmuxNext/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,11 @@ let package = Package(
dependencies: ["CmuxNextImportSuggestions", "CmuxNextBrowserImport"],
swiftSettings: daemonSwiftSettings
),
// The cards' center and surfaces: the scan schedule, the job runner, the `import.*`
// actions, the agent pages' push and the Chief front page's row. The App supplies `ImportCardsHost`.
// The cards' center: the scan schedule, the job runner and the `import.*` actions, for
// Settings › Import (no Home or New Tab suggestion). The App supplies `ImportCardsHost`.
.target(
name: "CmuxNextImportCards",
dependencies: ["CmuxNextActions", "CmuxNextAgentPane", "CmuxNextBrowserImport", "CmuxNextCompat", "CmuxNextDesign",
dependencies: ["CmuxNextActions", "CmuxNextBrowserImport", "CmuxNextCompat", "CmuxNextDesign",
"CmuxNextImportSuggestions", "CmuxNextOnboarding", "CmuxNextSettings"],
swiftSettings: uiSwiftSettings
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Import suggestion cards (CmuxNextImportCards): what this Mac has that cmux can bring over,
// on Home, New Tab and Settings › Import. Titles live in ImportActions.xcstrings. The CLI and
// listed in Settings › Import (never on Home or New Tab). Titles live in ImportActions.xcstrings. The CLI and
// agents list the cards, press a card's button, and raise a card for a site another browser is
// signed into (`cmux import suggest --browser chrome --site github.com --reason …`): a raised card
// reads nothing until the person clicks Import on it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public nonisolated struct AgentPageOps {
var methods = Dictionary(uniqueKeysWithValues: [
"pane.checkpointAvailability", "pane.framePacing", "pane.painted", "pane.renderRate", "pane.saveLog", "pane.saveFile", "pane.showContextUsage", "pane.showDictation", "pane.setDisplay",
"pane.edit", "pane.setQuickActions", "tab.open", "run.attach", "tab.typeAhead", "tab.jump",
"newTab.remember", "newTab.setTemplate", "newTab.editWidgets", "nowPlaying.command", "importCards.act", "newTab.inputReady", "newTab.touched", "newTab.context", "newTab.warm", "shortcut.edit", "action.run", "file.open", "browser.open",
"newTab.remember", "newTab.setTemplate", "newTab.editWidgets", "nowPlaying.command", "newTab.inputReady", "newTab.touched", "newTab.context", "newTab.warm", "shortcut.edit", "action.run", "file.open", "browser.open",
"project.list", "project.icon", "project.browse", "workspace.chooseFolder", "workspace.useFolder", "chat.folder.choose", "chat.attach.choose", "chat.attach.dropped", "app.action", "chats.open", "chats.openInTerminal", "chats.watch", "chats.page",
"quick.dismiss", "quick.openInWindow", "quick.startInBackground", "quick.setOnTop", "quick.setCollapsed", "quick.floatingState", "quick.context", "pane.action", "pane.tabState", "pane.tabRowTools", "chat.toolCard.respond", "chat.upload", "chat.sideChat", "chat.subagentPane", "chat.processTerminal", "chat.openTab", "chat.offer.open", "chat.asks.open", "chat.newHere", "chat.unsentDraft", "chat.scrollAnchor", "chat.scrollAnchor.get", "chat.tail", "chat.tail.get", "chat.draftAttachments", "chat.draftAttachments.get", "chat.asks.pending",
"shell.run", "shell.read", "shell.stop", "harness.update", "harness.restart",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ final class AgentPaneBridge: NSObject, WKScriptMessageHandlerWithReply {
AgentPaneDeviceChat.push(view.deviceChats, to: view)
AgentPaneClosedItem.push(view.recentlyClosed, to: view)
AgentPaneNowPlaying.shared.push(to: view)
AgentPaneImportCards.shared.push(to: view)
}
return view.model
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ public final class AgentPaneModel {
case .rememberNewTab(let pick): guard let onRememberNewTab else { return Self.unsupported("newTab.remember") }; onRememberNewTab(pick); return AgentPaneReply.success()
case .editNewTab(let edit): guard let onEditNewTab else { return Self.unsupported(edit.method) }; onEditNewTab(edit); return AgentPaneReply.success()
case .nowPlaying(let command): return AgentPaneNowPlaying.shared.perform(command) ? AgentPaneReply.success() : Self.unsupported("nowPlaying.command")
case .importCard(let act): return AgentPaneImportCards.shared.perform(act) ? AgentPaneReply.success() : Self.unsupported("importCards.act")
case .runAction(let id): return runAction(id)
case .jump(let target, let id): guard newTab != nil, let onJump else { return Self.unsupported("tab.jump") }; onJump(target, id); return AgentPaneReply.success()
case .chooseFolder where remoteChat: return Self.unsupported("workspace.chooseFolder")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ enum AgentPanePageHost {
events.append(.deviceChats(AgentPaneDeviceChat.shown(view.deviceChats, in: view)))
events.append(view.recentlyClosed.event)
events.append(AgentPaneNowPlaying.shared.event(for: view))
events.append(AgentPaneImportCards.shared.event(for: view))
events.append(.editedFiles(view.editedFiles))
events.append(.composer(view.model.composer))
events.append(.sidebarPreviewVariant(view.model.sidebarPreviewVariant))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ extension AgentPaneRequest {
self = AgentPaneNewTabEdit(widgetParams: params).map(AgentPaneRequest.editNewTab) ?? .unsupported(method)
case "nowPlaying.command":
if let command = AgentPaneNowPlaying.Command(params) { self = .nowPlaying(command) } else { self = .unsupported(method) }
case "importCards.act":
if let act = AgentPaneImportCards.Act(params) { self = .importCard(act) } else { self = .unsupported(method) }
case "newTab.remember":
// One input (R86): the agent pick, and the model picked on it in `folder`.
if let agent = params?["agent"] as? String, !agent.isEmpty, agent.count <= 128 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ extension AgentPaneRequest: CustomStringConvertible, CustomDebugStringConvertibl
case .listProjects: "listProjects"
case .projectIcon: "projectIcon"
case .nowPlaying: "nowPlaying"
case .importCard: "importCard"
case .openChat: "openChat"
case .openChatInTerminal: "openChatInTerminal"
case .watchChat: "watchChat"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ public nonisolated enum AgentPaneRequest: Equatable, Sendable {
case editNewTab(AgentPaneNewTabEdit)
/// The New Tab page player's control (`nowPlaying.command {command, seconds}`).
case nowPlaying(AgentPaneNowPlaying.Command)
/// An import card's button on the New Tab page (`importCards.act {id, action}`).
case importCard(AgentPaneImportCards.Act)
/// The new tab page got its first user input (`newTab.touched`); a
/// touched page is never recycled into the prewarm pool.
case touched
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ enum OnboardingHandlers {
track: { registry.track($0) },
destination: ChromiumCookieDestination { writes, profile in try await cef.importCookies(writes, into: profile) }))
registry.bind("browser.importCookies", run: { invocation in try cookieImports.run(invocation) })
// Import suggestion cards (Home, New Tab, Settings › Import): the same import run as the window's.
// Import suggestion cards (Settings › Import only): the same import run as the window's.
ImportSuggestionsCenter.shared.bind(into: registry, host: importCardsHost(services))
// Every entrypoint previews the exact snapshot and waits for the
// person's answer before any browser, SSH host or agent is restored.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import CmuxNextDesign
import CmuxNextHome
import CmuxNextHomeEngine
import CmuxNextHostedChief
import CmuxNextImportCards
import CmuxNextPages
import CmuxNextWakeups

Expand Down Expand Up @@ -230,12 +229,6 @@ final class HomeHostView: NSView {
}
// The first-run panel's agent rows open the agent's tab like a jump (Search Tabs).
transcript.onFirstRunAgent = { [weak services] tab in _ = services?.revealTab(tab) }
// What this Mac can bring over (Claude Code's sessions, a browser's history) is one row on
// the Chief's front page, under its action rows: never a strip over the conversation
// (Leo 2026-10-21, nightly 331e2e9). Not Now dismisses it; Show All opens Settings › Import.
let suggestion = ImportSuggestionRowView()
suggestion.onChange = { [weak transcript] in transcript?.firstRunAccessoryChanged() }
transcript.setFirstRunAccessory(suggestion)
// task-owner: lives as long as this view; event-driven (Observation):
// the user's agents for the first-run glance, only while it can show
// (the Chief with no messages yet): otherwise no tab is read, so tab
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ public enum HomeFirstRunAction: Sendable, Equatable {
/// The empty Chief conversation's first-run panel, the Chief's front page: the Chief's face
/// and what it does (`HomeFirstRunHero`), then things to do now, each a row with its shortcut
/// (start an agent, open a terminal) and the Chief pet's Show / Hide, then the user's agents
/// and the host's row under them (Home's import suggestion), then the user's agents at a
/// glance, most urgent first, each opening its tab, then the "Switching" card with the
/// at a glance, most urgent first, each opening its tab, then the "Switching" card with the
/// core switching keys (`HomeFirstRunSwitchCard`, hidden for good by its Hide button). Hidden
/// once the conversation has a message (`HomeNativeTranscriptView.updateFirstRun`). A short
/// pane drops agent rows first, then the hero.
Expand Down Expand Up @@ -44,26 +43,6 @@ final class HomeFirstRunView: NSView {
private var scale: CGFloat = 1
private var colors: Colors?
private lazy var headerWidth = agentsHeader.widthAnchor.constraint(equalToConstant: Self.maxWidth)
/// The host's row under the action rows (Home's import suggestion, `ImportSuggestionRowView`),
/// at the column's width; out of the layout while hidden. It sizes its own height and tells
/// the host when it shows or hides (`HomeNativeTranscriptView.firstRunAccessoryChanged`).
var accessory: NSView? {
didSet {
guard accessory !== oldValue else { return }
accessoryWidth?.isActive = false
accessoryWidth = nil
oldValue?.removeFromSuperview()
if let accessory {
accessory.translatesAutoresizingMaskIntoConstraints = false
let width = accessory.widthAnchor.constraint(equalToConstant: Self.maxWidth)
width.isActive = true
accessoryWidth = width
}
arrange()
needsLayout = true
}
}
private var accessoryWidth: NSLayoutConstraint?
/// Called when the user picks open a terminal or start an agent.
var onAction: (HomeFirstRunAction) -> Void = { _ in }
/// Called with an agent's tab id when the user picks its row.
Expand Down Expand Up @@ -142,8 +121,7 @@ final class HomeFirstRunView: NSView {
private func arrange() {
for view in stack.arrangedSubviews { stack.removeArrangedSubview(view); view.removeFromSuperview() }
let listed: [NSView] = agentRows.isEmpty ? [] : [agentsHeader] + agentRows
let hosted: [NSView] = accessory.map { [$0] } ?? []
for view in [hero] + actionRows + hosted + listed + [switchCard] as [NSView] { stack.addArrangedSubview(view) }
for view in [hero] + actionRows + listed + [switchCard] as [NSView] { stack.addArrangedSubview(view) }
applySpacing()
}

Expand All @@ -156,9 +134,7 @@ final class HomeFirstRunView: NSView {
/// stack's layout).
private func fitAgentRows() {
let row = HomeFirstRunRow.height * scale, gap = 6 * scale, group = 14 * scale
// Home's import suggestion sits under the action rows and goes with them.
let actions = CGFloat(actionRows.count) * row + CGFloat(actionRows.count - 1) * gap
+ (accessory.map { $0.isHidden ? 0 : group + $0.fittingSize.height } ?? 0)
let actionsFit = actions <= bounds.height
if stack.isHidden != !actionsFit { stack.isHidden = !actionsFit }
// The card's keys are set (Hide clears them); it shows when it fits under the rows.
Expand Down Expand Up @@ -189,7 +165,6 @@ final class HomeFirstRunView: NSView {
private func applySpacing() {
stack.spacing = 6 * scale
stack.setCustomSpacing(14 * scale, after: pet)
if let accessory { stack.setCustomSpacing(14 * scale, after: accessory) }
stack.setCustomSpacing(Self.heroGap * scale, after: hero)
}

Expand Down Expand Up @@ -262,7 +237,6 @@ final class HomeFirstRunView: NSView {
let width = max(0, min(Self.maxWidth * scale, bounds.width - 48 * scale))
for row in rows { row.width = width }
if headerWidth.constant != width { headerWidth.constant = width }
if let accessoryWidth, accessoryWidth.constant != width { accessoryWidth.constant = width }
if cardWidth.constant != width { cardWidth.constant = width }
if heroWidth.constant != width { heroWidth.constant = width }
let heroTall = hero.height(for: width)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,6 @@ extension HomeNativeTranscriptView {
}
}

/// A row the host puts on the Chief's front page under its action rows (Home's import
/// suggestion, `ImportSuggestionRowView`), in the panel's own layout so it never covers the
/// conversation; nil removes it.
public func setFirstRunAccessory(_ view: NSView?) {
firstRun.accessory = view
}

/// The front page's host row showed, hid or changed size: the panel fits its rows again.
public func firstRunAccessoryChanged() {
firstRun.needsLayout = true
}

/// The user's agents for the first-run panel's glance (the host maps
/// them from the tab status the strip and the sidebar draw); empty lists
/// none.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import AppKit
import CmuxNextImportSuggestions
import CmuxNextSettings

/// A card list as the web surfaces read it (New Tab's `importCards` push, Settings' host lists
/// `import_suggestions`, `import.list`): the Codable cards, each with its source app's icon as
/// a small `data:image/png` URL (a cmux page's CSP allows `img-src data:` only).
/// A card list as the web surfaces read it (Settings' host lists `import_suggestions`,
/// `import.list`): the Codable cards, each with its source app's icon as a small
/// `data:image/png` URL (a cmux page's CSP allows `img-src data:` only).
@MainActor
struct ImportCardJSON {
/// Null for an empty list: the surface draws nothing and keeps no room.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extension ImportSuggestionsCenter {
// Every card Settings › Import lists, as JSON text (`cmux import list`).
registry.bind("import.list", unavailable: refusedWhileOff, invoke: { [weak self, weak registry] _ in
guard let self, let registry else { return }
let text = ImportCardJSON.text(list(for: .settings))
let text = ImportCardJSON.text(list())
registry.track(result: Task { .success(["cards": .string(text)]) })
})
registry.bind("import.card", unavailable: refusedWhileOff, invoke: { [weak self, weak registry] invocation in
Expand Down
Loading
Loading