Skip to content

Commit 3a8c53c

Browse files
angusbezzinaclaude
andcommitted
Overlay pill: only the pencil when idle; tools appear in annotate mode (cli-jk0hs.4)
Gate copy/export/clear on session.mode == .annotating instead of rendering them always (dimmed when empty). Idle now shows JUST the annotate (pencil) toggle; the tools (and their dim/disable-when-empty state) appear only while annotating. The count badge still overlays whenever notes exist; the cluster animates on the annotate toggle. Build clean (Swift 6); 44 tests; AnnotKitProbe + AnnotKitOverlayProbe pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5be4ac6 commit 3a8c53c

1 file changed

Lines changed: 36 additions & 33 deletions

File tree

Sources/AnnotKit/Overlay/OverlayView.swift

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -380,21 +380,20 @@ private struct AnnotationCard<Footer: View>: View {
380380
/// capability — no dead buttons (CLAUDE.md) — so Agentation's `settings`/`eye`
381381
/// (no settings model, no preview capability here) are deliberately omitted.
382382
///
383-
/// Left to right: annotate toggle (always), then two DISTINCT persist actions
384-
/// (Copy to the clipboard as markdown, and Export to `AGENTATION_NOTES.md`), and
385-
/// a destructive clear. Copy/Export/Clear are ALWAYS present and are disabled and
386-
/// dimmed while `pending` is empty (acting on zero notes is a no-op); a count
387-
/// badge overlays the pill only while notes exist. The pencil toggle exits
388-
/// annotate mode, so there is no separate close/exit control. Copy and Export
389-
/// never clear the retained set (only Clear does), so the same notes can be both
390-
/// copied and exported. Copy/Export flow through host callbacks (they need a
391-
/// sink); toggle needs the controller (activation); clear reads/writes the
392-
/// session directly.
383+
/// Left to right: annotate toggle (always), then — ONLY while annotating — two
384+
/// DISTINCT persist actions (Copy to the clipboard as markdown, and Export to
385+
/// `AGENTATION_NOTES.md`) and a destructive clear. Idle shows JUST the pencil; the
386+
/// tools appear when annotate mode is on, and are disabled/dimmed while `pending`
387+
/// is empty (acting on zero notes is a no-op). A count badge overlays the pill
388+
/// whenever notes exist. The pencil toggle exits annotate mode, so there is no
389+
/// separate close/exit control. Copy and Export never clear the retained set (only
390+
/// Clear does), so the same notes can be both copied and exported. Copy/Export
391+
/// flow through host callbacks (they need a sink); toggle needs the controller
392+
/// (activation); clear reads/writes the session directly.
393393
///
394394
/// The pill itself is rendered unconditionally and is NEVER gated on an entrance
395-
/// flag, so it stays visible across idle<->annotate and before/during/after
396-
/// capturing a note; the note-action cluster stays put and only its enabled/dim
397-
/// state animates as `pending` changes.
395+
/// flag, so it stays visible across idle<->annotate; the note-action cluster
396+
/// animates in and out as annotate mode toggles.
398397
private struct ToolbarView: View {
399398
@ObservedObject var session: AnnotationSession
400399
let onToggle: () -> Void
@@ -416,23 +415,26 @@ private struct ToolbarView: View {
416415
action: onToggle
417416
)
418417

419-
// Copy/Export/Clear are always present; they dim and disable while
420-
// there are no notes (acting on zero notes is a no-op).
421-
PillButton(
422-
icon: justCopied ? .check : .copy,
423-
isDisabled: !hasNotes,
424-
glyphTint: justCopied ? PillStyle.success : nil,
425-
tooltip: justCopied ? "Copied" : "Copy notes (Markdown)",
426-
action: { onCopy(); flashCopied() }
427-
)
428-
PillButton(
429-
icon: .download,
430-
isDisabled: !hasNotes,
431-
tooltip: "Export to AGENTATION_NOTES.md",
432-
action: onExport
433-
)
434-
PillButton(icon: .trash, isDestructive: true, isDisabled: !hasNotes, tooltip: "Clear notes") {
435-
session.clear()
418+
// Copy/Export/Clear appear ONLY in annotate mode; idle shows just the
419+
// pencil. While annotating they dim/disable when there are no notes
420+
// (acting on zero notes is a no-op).
421+
if annotating {
422+
PillButton(
423+
icon: justCopied ? .check : .copy,
424+
isDisabled: !hasNotes,
425+
glyphTint: justCopied ? PillStyle.success : nil,
426+
tooltip: justCopied ? "Copied" : "Copy notes (Markdown)",
427+
action: { onCopy(); flashCopied() }
428+
)
429+
PillButton(
430+
icon: .download,
431+
isDisabled: !hasNotes,
432+
tooltip: "Export to AGENTATION_NOTES.md",
433+
action: onExport
434+
)
435+
PillButton(icon: .trash, isDestructive: true, isDisabled: !hasNotes, tooltip: "Clear notes") {
436+
session.clear()
437+
}
436438
}
437439
}
438440
.padding(.horizontal, 6)
@@ -452,9 +454,10 @@ private struct ToolbarView: View {
452454
}
453455
}
454456
.shadow(color: .black.opacity(0.4), radius: 12, y: 8)
455-
// Reveal/hide the note-action cluster smoothly as pending changes. The
456-
// pill has no entrance opacity/scale gate: it must ALWAYS be visible.
457-
.animation(reduceMotion ? nil : .easeOut(duration: 0.15), value: hasNotes)
457+
// Animate the note-action cluster in/out as annotate mode toggles. The
458+
// pill itself has no entrance gate: it is always visible (just the pencil
459+
// when idle).
460+
.animation(reduceMotion ? nil : .easeOut(duration: 0.15), value: annotating)
458461
}
459462

460463
/// Show a green check on the Copy button for a beat as success feedback, then

0 commit comments

Comments
 (0)