Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ private let authLog = Logger(subsystem: "ai.manaflow.cmux", category: "auth")
/// Owns the observable session state (``isAuthenticated`` / ``currentUser`` /
/// ``isLoading`` / ``isRestoringSession``) and sequences every sign-in flow plus
/// session restore/validation. Replaces the iOS `AuthManager.shared` singleton
/// (and is intended to replace the macOS `ObservableObject` AuthManager too).
/// and the former macOS `AuthManager` state surface.
///
/// Construct it once at the app composition root with an injected
/// ``AuthClient``, persistence stores, presentation anchor, config, and launch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Observation
/// `isLoading`/`estimatedProgress`, and a pending ``loadRequest`` tells the
/// representable what URL to load next.
///
/// It is `@MainActor @Observable` (not `ObservableObject`/`@Published`), so
/// It is `@MainActor @Observable` (not `ObservableObject`), so
/// SwiftUI tracks individual property reads and the `WKWebView` coordinator can
/// mutate it directly on the main actor.
@MainActor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
public import AppKit
public import Combine
public import Observation

/// Drives sidebar auto-scrolling while a drag hovers near the scroll view's top
/// or bottom edge. Prefers AppKit's native `NSClipView.autoscroll(with:)` when a
/// drag event is available and falls back to a manual per-tick scroll computed
/// from `SidebarDragAutoScrollPlanner`.
@MainActor
public final class SidebarDragAutoScrollController: ObservableObject {
private weak var scrollView: NSScrollView?
private var timer: Timer?
private var activePlan: SidebarAutoScrollPlan?
@Observable
public final class SidebarDragAutoScrollController {
@ObservationIgnored private weak var scrollView: NSScrollView?
@ObservationIgnored private var timer: Timer?
@ObservationIgnored private var activePlan: SidebarAutoScrollPlan?

public init() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Observation
/// `UserDefaults` and the injected ``BrowserProfileHistoryProviding``,
/// ``BrowserProfileWebsiteDataStoreProviding``, and ``BrowserProfileFileRemoving``
/// seams so the WebKit, filesystem, and bundle dependencies stay in the app
/// target. The app's `BrowserProfileStore` is a thin `ObservableObject` facade
/// target. The app's `BrowserProfileStore` is a thin `@Observable` facade
/// over this repository.
///
/// `@MainActor` because it seeds synchronously in `init` and is consumed by the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,24 @@ public import Foundation
/// state changes. The owning `Workspace` is the single implementer.
///
/// **Why synchronous hooks and not an AsyncStream.** These hooks replace the
/// legacy `@Published` property observers one-for-one: they fire while the
/// property still holds its old value (the host re-emits `objectWillChange`
/// and the legacy Combine bridge publishers there, at the exact timing
/// `@Published` used). A stream would open a suspension window between the
/// mutation and its observers.
/// legacy published property observers one-for-one: they fire while the
/// property still holds its old value, and the host re-emits the legacy
/// Combine bridge publishers there at the original timing. A stream would
/// open a suspension window between the mutation and its observers.
///
/// Parity contract: hooks fire on **every** assignment, including
/// assignments of an equal value — `@Published` never compared.
/// assignments of an equal value — the legacy publisher never compared.
@MainActor
public protocol PaneTreeHosting<PanelValue>: AnyObject {
/// The window's panel type; the app target's `any Panel` existential.
/// Named `PanelValue` so the binding does not shadow the app's `Panel`
/// protocol inside the conforming type.
associatedtype PanelValue

/// The `panels` map is about to change (legacy `@Published panels`
/// The `panels` map is about to change (legacy `panels`
/// willSet).
func panelsWillChange(to newValue: [UUID: PanelValue])
/// The pane-layout version is about to change (legacy `@Published
/// The pane-layout version is about to change (legacy
/// paneLayoutVersion` willSet).
func paneLayoutVersionWillChange(to newValue: Int)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public import Bonsplit
///
/// The owning `Workspace` composition root holds one instance, forwards its
/// legacy accessors here, and implements `PaneTreeHosting` to receive the
/// property-observer hooks the legacy `@Published` observers provided
/// (objectWillChange/bridge re-emission).
/// property-observer hooks the legacy published observers provided for bridge
/// re-emission. Its stored properties are the tracked Observation surface.
@MainActor
@Observable
public final class PaneTreeModel<Panel> {
Expand Down Expand Up @@ -58,7 +58,7 @@ public final class PaneTreeModel<Panel> {
public init() {}

/// Attaches the workspace-side host. Must be called before the first
/// mutation so the property-observer hooks match the legacy `@Published`
/// mutation so the property-observer hooks match the legacy publisher
/// timing from the very first panel insertion.
public func attach(host: any PaneTreeHosting<Panel>) {
self.host = host
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public import Bonsplit
/// `Transfer` is the window's detached-surface transfer payload type (the
/// app target's `Workspace.DetachedSurfaceTransfer`, which carries panel
/// references and app-domain snapshots, so it stays app-side). None of the
/// stored properties were `@Published` on the legacy god object, so this
/// stored properties were published on the legacy god object, so this
/// storage move carries no observer-parity hooks.
@MainActor
@Observable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private final class RecordingPaneTreeHost: PaneTreeHosting {
@MainActor
@Suite("PaneTreeModel")
struct PaneTreeModelTests {
/// The hooks replace `@Published` willSet observers: they must see the
/// The hooks replace legacy willSet observers: they must see the
/// new value as an argument while the property still reads the OLD value.
@Test func hooksFireAtWillSetTimeWithNewValue() {
let host = RecordingPaneTreeHost()
Expand All @@ -50,7 +50,7 @@ struct PaneTreeModelTests {
#expect(host.versionValueAtHookTime == 0)
}

/// `@Published` fired on every assignment, never comparing; the hooks
/// The legacy publisher fired on every assignment, never comparing; the hooks
/// must keep equal-assignment emissions.
@Test func hooksFireOnEqualAssignment() {
let host = RecordingPaneTreeHost()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ public import Observation
/// The per-workspace sidebar-metadata sub-model: owns the sidebar status
/// entries, metadata blocks, log entries, progress, and git-branch /
/// pull-request presentation state the legacy `Workspace` god object kept as
/// loose `@Published` stored properties (`statusEntries`, `metadataBlocks`,
/// loose published stored properties (`statusEntries`, `metadataBlocks`,
/// `logEntries`, `progress`, `gitBranch`, `panelGitBranches`, `pullRequest`,
/// `panelPullRequests`).
///
/// `Workspace` owns one instance and forwards each former stored property
/// through a computed `get`/`set` pair, so every call site (`statusEntries[key]
/// = …`, `logEntries.append(…)`, `workspace.progress`) stays byte-identical.
///
/// Byte-identical observer parity: the legacy properties were `@Published`, and
/// Byte-identical observer parity: the legacy properties were published, and
/// the sidebar observation publishers (`Workspace.sidebarObservationPublisher`)
/// fused their `$projection`s through `CombineLatest` + `removeDuplicates()`.
/// To preserve that exactly, each property here mirrors its value into a
/// `CurrentValueSubject` in `didSet`; the matching `…Publisher` accessor
/// replaces the former `$property`. `CombineLatest` over current-value subjects
/// seeded with the initial values, then deduplicated, produces the identical
/// sequence of distinct fused states the `@Published` projections did, so the
/// sequence of distinct fused states the legacy projections did, so the
/// debounced sidebar refresh fires at the same moments.
@MainActor
@Observable
Expand Down
Loading