The card menu learns to tag — a labels submenu of twelve checkmarks, and Copy Link moves in beside its mirror
Right-clicking a card now offers Labels: up to twelve rows ranked by how many cards on the board carry each one, tie-broken by what this user reached for last, each a checkmark that says whether *this* card already has it. More… opens the whole inventory beside the card, in lookup order, with a field that mints a name the board has never used. The rows deliberately do not widen to the selection the way Copy, Cut and Send to Trash do. A checkmark is a claim about one card, and three cards where two carry `bug` have no honest checked state — the rows that widen are the ones that say what they will do rather than what is already true. It is also what keeps the menu cheap: a context menu's contents are rebuilt on every ordinary pass of every card face, so reading the selection there would resubscribe the whole board. The board-sized half of the ranking is cached on the store and gated on equality; what runs per face is bounded by how many distinct labels exist, not by how many cards do. Copy Link leaves the first group for a new Copy Special submenu, mirroring Paste Special — the placement the owner's latest layout asks for, and the one the row's own note has been waiting on since it shipped as a same-day deviation. It keeps its VoiceOver action even so: it is an action that changed doors, not a door. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -367,6 +367,17 @@ public final class TransientBoardState {
|
||||
/// session assignable from anywhere could be re-aimed behind the reload rule's back.
|
||||
public private(set) var styleEditor: StyleEditorSession?
|
||||
|
||||
/// The open **Labels ▸ More…** dialog's card, or `nil` when none is open (the card context menu's
|
||||
/// `labels` submenu; `FrontmatterKeys.labels`, activated 2026-08-09). `styleEditor`'s neighbour in
|
||||
/// every respect — same reason it lives here rather than in a view, same `private(set)`, same
|
||||
/// re-grounding below — narrowed to one card because every row in that dialog is a checkmark about
|
||||
/// one card (`LabelEditorSession`).
|
||||
///
|
||||
/// **Not an inline editor either**, `styleEditor`'s own note: the dialog's create field holds text,
|
||||
/// but `isEditingInline` is the answer to "may board commands run" and this surface is opened *by*
|
||||
/// one of them.
|
||||
public private(set) var labelEditor: LabelEditorSession?
|
||||
|
||||
/// Whether a title editor holds focus — 04-interactions.md's **focused-editor rule** as one
|
||||
/// boolean: "while an inline title editor — rename or the new-card placeholder — is focused,
|
||||
/// board-scoped menu commands (Delete, New Card, Paste, Move, Style, …) disable via menu
|
||||
@@ -618,6 +629,22 @@ public final class TransientBoardState {
|
||||
styleEditor = nil
|
||||
}
|
||||
|
||||
// MARK: - The labels dialog's lifecycle
|
||||
|
||||
/// Opens **Labels ▸ More…** on one card — the card context menu's row, and its only caller.
|
||||
///
|
||||
/// Replacing any session already open, `beginStyleEditor`'s rule and its reason: there is one
|
||||
/// dialog, so a second More… is the first one being re-aimed by a fresh gesture.
|
||||
public func beginLabelEditor(forCard cardID: ItemID) {
|
||||
labelEditor = LabelEditorSession(cardID: cardID)
|
||||
}
|
||||
|
||||
/// Closes it — the user dismissing it, and the anchor's response to a session the reload rule
|
||||
/// emptied.
|
||||
public func discardLabelEditor() {
|
||||
labelEditor = nil
|
||||
}
|
||||
|
||||
// MARK: - Reload
|
||||
|
||||
/// The one reload hook: re-grounds every piece of this container on a freshly applied snapshot.
|
||||
@@ -691,6 +718,7 @@ public final class TransientBoardState {
|
||||
pendingCut = pendingCut.resolved(against: snapshot)
|
||||
newCardPlaceholder = resolvedPlaceholder(against: snapshot)
|
||||
styleEditor = styleEditor?.resolved(against: snapshot)
|
||||
labelEditor = labelEditor?.resolved(against: snapshot)
|
||||
|
||||
// One universe computed once and asked three questions — the rename target's container, the
|
||||
// last-active lane's, and (via the placeholder above, which asks its own way) the anchor's.
|
||||
|
||||
Reference in New Issue
Block a user