Skip to content

Commit 181b42b

Browse files
committed
feat(demo): a settings-screen demo app worth recording, booted via task
AnnotKitDemo grows from a gray control stack into a small Acme Settings screen: a profile card (click targets), three stat cards (drag-a-frame targets that show the largest-surrounded-element rule), and an actions row, all with dot-notation accessibilityIdentifiers so emitted notes show off selector anchoring. Semantic colors keep it dark-mode friendly. Add Taskfile.yml (task demo / build / test / probe / mcp / clean) so a practice or recording run is one command, and gitignore the AGENTATION_NOTES artifacts the demo writes into the working directory.
1 parent becb59c commit 181b42b

5 files changed

Lines changed: 156 additions & 37 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
DerivedData/
77
.DS_Store
88
Package.resolved
9+
10+
# Demo/practice artifacts: AnnotKitDemo writes notes into the working directory
11+
AGENTATION_NOTES.md
12+
AGENTATION_NOTES.json

Package.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ let package = Package(
6262
.swiftLanguageMode(.v6)
6363
]
6464
),
65-
// Interactive macOS demo: a window of identified controls with the
66-
// annotation overlay mounted. `swift run AnnotKitDemo`, click Annotate,
67-
// click a control, type a note, Save. Writes AGENTATION_NOTES.md in the
68-
// working directory.
65+
// Interactive macOS demo: a small settings screen of identified
66+
// controls with the annotation overlay mounted. `swift run
67+
// AnnotKitDemo`, click Annotate, click a control (or drag a frame
68+
// around a stat card), type a note, Save. Writes AGENTATION_NOTES.md
69+
// in the working directory. Also serves as the on-camera demo app.
6970
.executableTarget(
7071
name: "AnnotKitDemo",
7172
dependencies: ["AnnotKit"],

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Tools: `annotation_get_pending`, `annotation_resolve`.
7474
swift build
7575
swift test
7676
swift run AnnotKitProbe # live macOS smoke test
77+
swift run AnnotKitDemo # interactive demo app (overlay mounted; good for recordings)
7778
```
7879

7980
Swift 6 (strict concurrency), macOS 15+, iOS 17+.

Sources/AnnotKitDemo/main.swift

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,124 @@ import AnnotKit
33
import AppKit
44
import SwiftUI
55

6-
// A tiny host app for trying AnnotKit by hand. Run it, use the "Annotate"
7-
// toolbar (bottom-right), click any control below, type a note, and Save. Notes
8-
// are written to AGENTATION_NOTES.md in the current working directory.
6+
// A tiny host app for trying AnnotKit by hand — and for recording demos. Run
7+
// it (`swift run AnnotKitDemo`), use the "Annotate" toolbar (bottom-right),
8+
// click any control below, type a note, and Save. Notes are written to
9+
// AGENTATION_NOTES.md in the current working directory.
10+
//
11+
// The screen is a small, realistic settings page so both gestures demo well:
12+
// click a control (deepest actionable wins) or drag a frame around one of the
13+
// stat cards (largest surrounded element wins). Everything meaningful carries
14+
// a dot-notation accessibilityIdentifier so generated selectors show the
15+
// `#Settings.Profile >> @NameField`-style anchoring in the emitted notes.
16+
// Semantic colors only, so the window is dark-mode friendly on camera.
17+
18+
private struct StatCard: View {
19+
let identifier: String
20+
let value: String
21+
let label: String
22+
23+
var body: some View {
24+
VStack(spacing: 4) {
25+
Text(value)
26+
.font(.title).bold()
27+
.accessibilityIdentifier("\(identifier).Value")
28+
Text(label)
29+
.font(.caption)
30+
.foregroundStyle(.secondary)
31+
.accessibilityIdentifier("\(identifier).Label")
32+
}
33+
.frame(maxWidth: .infinity)
34+
.padding(.vertical, 14)
35+
.background(.quaternary, in: RoundedRectangle(cornerRadius: 10))
36+
// Expose the card itself as an AX group so a drawn frame binds to the
37+
// whole card rather than the value/label leaves inside it.
38+
.accessibilityElement(children: .contain)
39+
.accessibilityIdentifier(identifier)
40+
}
41+
}
942

1043
struct DemoView: View {
11-
@State private var text = ""
12-
@State private var toggle = false
44+
@State private var name = "Ada Lovelace"
45+
@State private var email = "ada@example.com"
46+
@State private var notifications = true
47+
@State private var plan = "Pro"
1348

1449
var body: some View {
15-
VStack(alignment: .leading, spacing: 18) {
16-
Text("AnnotKit Demo")
17-
.font(.largeTitle).bold()
18-
.accessibilityIdentifier("Demo.Title")
50+
VStack(alignment: .leading, spacing: 20) {
51+
VStack(alignment: .leading, spacing: 6) {
52+
Text("Acme Settings")
53+
.font(.largeTitle).bold()
54+
.accessibilityIdentifier("Settings.Title")
55+
Text("Click 'Annotate' (bottom-right), then click a control — or drag a frame around a card — type a note, and Save. Notes land in AGENTATION_NOTES.md in your working directory.")
56+
.font(.callout)
57+
.foregroundStyle(.secondary)
58+
.fixedSize(horizontal: false, vertical: true)
59+
.accessibilityIdentifier("Settings.Instructions")
60+
}
1961

20-
Text("Click 'Annotate' (bottom-right), then click any control below, type a note, and Save. Notes land in AGENTATION_NOTES.md in your working directory.")
21-
.foregroundStyle(.secondary)
22-
.fixedSize(horizontal: false, vertical: true)
23-
.accessibilityIdentifier("Demo.Instructions")
62+
GroupBox {
63+
HStack(alignment: .center, spacing: 16) {
64+
ZStack {
65+
Circle()
66+
.fill(.tint.opacity(0.15))
67+
Text("AL")
68+
.font(.headline)
69+
.foregroundStyle(Color.accentColor)
70+
}
71+
.frame(width: 48, height: 48)
72+
.accessibilityIdentifier("Settings.Profile.Avatar")
2473

25-
HStack(spacing: 12) {
26-
Button("Primary action") {}
27-
.buttonStyle(.borderedProminent)
28-
.accessibilityIdentifier("Demo.PrimaryButton")
29-
Button("Secondary") {}
30-
.accessibilityIdentifier("Demo.SecondaryButton")
31-
Toggle("A toggle", isOn: $toggle)
32-
.accessibilityIdentifier("Demo.Toggle")
74+
VStack(alignment: .leading, spacing: 8) {
75+
TextField("Name", text: $name)
76+
.textFieldStyle(.roundedBorder)
77+
.accessibilityIdentifier("Settings.Profile.NameField")
78+
TextField("Email", text: $email)
79+
.textFieldStyle(.roundedBorder)
80+
.accessibilityIdentifier("Settings.Profile.EmailField")
81+
}
82+
83+
Spacer()
84+
85+
Toggle("Notifications", isOn: $notifications)
86+
.accessibilityIdentifier("Settings.Profile.NotificationsToggle")
87+
}
88+
.padding(8)
89+
} label: {
90+
Text("Profile")
91+
.accessibilityIdentifier("Settings.Profile.Header")
3392
}
93+
.accessibilityIdentifier("Settings.Profile")
3494

35-
TextField("A text field to annotate", text: $text)
36-
.textFieldStyle(.roundedBorder)
37-
.frame(width: 280)
38-
.accessibilityIdentifier("Demo.TextField")
95+
HStack(spacing: 12) {
96+
StatCard(identifier: "Settings.Stats.Projects", value: "12", label: "Projects")
97+
StatCard(identifier: "Settings.Stats.Streak", value: "8 days", label: "Streak")
98+
StatCard(identifier: "Settings.Stats.Storage", value: "64%", label: "Storage")
99+
}
39100

40-
GroupBox("A list") {
41-
ForEach(0 ..< 4) { index in
42-
Text("Item \(index)")
43-
.frame(maxWidth: .infinity, alignment: .leading)
44-
.accessibilityIdentifier("Demo.Item[\(index)]")
101+
HStack(spacing: 12) {
102+
Button("Save changes") {}
103+
.buttonStyle(.borderedProminent)
104+
.accessibilityIdentifier("Settings.Actions.Save")
105+
Button("Reset") {}
106+
.accessibilityIdentifier("Settings.Actions.Reset")
107+
Spacer()
108+
Picker("Plan", selection: $plan) {
109+
Text("Free").tag("Free")
110+
Text("Pro").tag("Pro")
111+
Text("Team").tag("Team")
45112
}
113+
.frame(width: 160)
114+
.accessibilityIdentifier("Settings.Actions.PlanPicker")
46115
}
116+
117+
Text("AnnotKit Demo · v0.1 · dev build")
118+
.font(.caption)
119+
.foregroundStyle(.tertiary)
120+
.accessibilityIdentifier("Settings.Footer.Version")
47121
}
48-
.padding(32)
49-
.frame(width: 480, height: 460)
122+
.padding(28)
123+
.frame(width: 620, height: 560)
50124
}
51125
}
52126

@@ -56,7 +130,7 @@ final class DemoAppDelegate: NSObject, NSApplicationDelegate {
56130

57131
func applicationDidFinishLaunching(_ notification: Notification) {
58132
let window = NSWindow(
59-
contentRect: NSRect(x: 0, y: 0, width: 480, height: 460),
133+
contentRect: NSRect(x: 0, y: 0, width: 620, height: 560),
60134
styleMask: [.titled, .closable, .miniaturizable],
61135
backing: .buffered,
62136
defer: false

Taskfile.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Taskfile for AnnotKit. Requires go-task (brew install go-task).
2+
# `task` or `task --list` shows all commands.
3+
version: '3'
4+
5+
tasks:
6+
default:
7+
cmds:
8+
- task --list
9+
silent: true
10+
11+
build:
12+
desc: Build all package targets (debug)
13+
cmds:
14+
- swift build
15+
16+
test:
17+
desc: Run the full test suite
18+
cmds:
19+
- swift test
20+
21+
demo:
22+
desc: Boot the interactive demo app (overlay mounted; for practice and recordings)
23+
cmds:
24+
- swift run AnnotKitDemo
25+
26+
probe:
27+
desc: Run the live macOS AX smoke probe (off-screen window)
28+
cmds:
29+
- swift run AnnotKitProbe
30+
31+
mcp:
32+
desc: Run the MCP server over a notes file — `task mcp -- path/to/AGENTATION_NOTES.json`
33+
cmds:
34+
- swift run annotkit-mcp {{.CLI_ARGS | default "./AGENTATION_NOTES.json"}}
35+
36+
clean:
37+
desc: Remove Swift build artifacts (.build)
38+
cmds:
39+
- swift package clean

0 commit comments

Comments
 (0)