Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
dde9459
Fix TableBlockView: darker borders, grip dots outside, selection clea…
max4c Mar 31, 2026
39562c5
Fix Cmd+K content search: use pane system for navigation instead of l…
max4c Mar 31, 2026
cff9f99
Merge branch 'worktree-agent-ab4c81d4' into dev
max4c Mar 31, 2026
c58781e
Callout block: neutral default, icon/color picker, flexible styling
max4c Mar 31, 2026
017b80d
Fix table row grip dots: add explicit frame for 6-dot (2x3) layout
max4c Mar 31, 2026
cf66567
Wire TranscriptionService to MeetingBlockView for live recording and …
max4c Mar 31, 2026
7a004ec
Fix Cmd+K navigation: defer to DispatchQueue.main.async after palette…
max4c Mar 31, 2026
3a5692a
Merge branch 'worktree-agent-a52bd502' into dev
max4c Mar 31, 2026
13fcc31
Merge branch 'worktree-agent-a30d45d6' into dev
max4c Mar 31, 2026
9e2831e
Merge branch 'worktree-agent-ac72efbb' into dev
max4c Mar 31, 2026
4ac8c61
Merge Wave 2: callout, grip dots, transcription, Cmd+K fix (resolved …
max4c Mar 31, 2026
64f5c81
Outline/TOC block: clickable heading navigation
max4c Mar 31, 2026
68d8142
Meeting block: merge Summary/Notes toggle, neutral buttons
max4c Mar 31, 2026
a8ca32f
Ask AI: open full chat view instead of side panel
max4c Mar 31, 2026
278b3a3
Database row templates: create, select, edit per database
max4c Mar 31, 2026
57b1e34
Reduce vertical spacing between list items and heading-to-list gap
max4c Mar 31, 2026
3496168
Merge branch 'worktree-agent-aa3b575b' into dev
max4c Mar 31, 2026
2d04751
Merge branch 'worktree-agent-a229a1c0' into dev
max4c Mar 31, 2026
c9849bf
Merge meeting toggle (resolved WaveformView conflict: kept real audio…
max4c Mar 31, 2026
9da1448
Merge branch 'worktree-agent-a86e40e7' into dev
max4c Mar 31, 2026
3a2ad82
Merge branch 'worktree-agent-a988a881' into dev
max4c Mar 31, 2026
f591575
Wire heading toggles: BlockCellView routing and slash commands
max4c Mar 31, 2026
9cf304c
Floating recording indicator pill: NSPanel with audio bars
max4c Mar 31, 2026
6caefa9
Wire table block: verify editor routing and slash menu
max4c Mar 31, 2026
1325f02
Meeting block: reduce padding between title and notes area
max4c Mar 31, 2026
c062005
Rename Meetings to AI Meeting Notes with chat button
max4c Mar 31, 2026
7cb0fb7
Merge branch 'worktree-agent-a68251f4' into dev
max4c Mar 31, 2026
ee3f21a
Merge branch 'worktree-agent-ae89fbe6' into dev
max4c Mar 31, 2026
51da65a
Merge branch 'worktree-agent-a8df864f' into dev
max4c Mar 31, 2026
e99c1ca
Merge branch 'worktree-agent-a330f20b' into dev
max4c Mar 31, 2026
23530f9
Merge branch 'worktree-agent-a7579430' into dev
max4c Mar 31, 2026
e27e8ba
Fix calculations footer: transparent until hover, aligned to columns
max4c Mar 31, 2026
1580afc
Transcript modal: centered page-style instead of full-width sheet
max4c Mar 31, 2026
c145d2d
Table view grouping: remove dividers, zero-height collapsed groups
max4c Mar 31, 2026
faae1b4
Allow taking notes in finished meeting block
max4c Mar 31, 2026
2ca7d48
Style AI thread picker to match app popover design
max4c Mar 31, 2026
69df189
Merge branch 'worktree-agent-a068dd30' into dev
max4c Mar 31, 2026
74edc20
Merge branch 'worktree-agent-a0365283' into dev
max4c Mar 31, 2026
43deaef
Merge branch 'worktree-agent-af00cb9c' into dev
max4c Mar 31, 2026
2cf2143
Merge branch 'worktree-agent-a6ebef5d' into dev
max4c Mar 31, 2026
e04322a
Merge branch 'worktree-agent-a6d07969' into dev
max4c Mar 31, 2026
98668a0
Fix Cmd+K navigation: use @State + onChange instead of direct call fr…
max4c Mar 31, 2026
10c21c7
Fix Cmd+K navigation (attempt 3): state variable + onChange decouples…
max4c Mar 31, 2026
1ef5e0e
Ask anything AI bar for meeting transcript queries
max4c Mar 31, 2026
78f1f0f
Ask anything AI bar for meeting queries (resolved MeetingTab conflict)
max4c Mar 31, 2026
490c978
Post-meeting structured output: transcript cleaning, AI sections, cha…
max4c Mar 31, 2026
4e01770
Merge branch 'worktree-agent-aba56ceb' into dev
max4c Mar 31, 2026
788ff90
Select option color: kebab menu on option rows with inline edit + col…
max4c Mar 31, 2026
bb6c42f
Merge branch 'worktree-agent-ad95206f' into dev
max4c Mar 31, 2026
3a4d31e
Add page/database to sidebar via right-click context menu (replaces b…
max4c Mar 31, 2026
2cea396
Merge branch 'worktree-agent-a15f0245' into dev
max4c Mar 31, 2026
8370472
Skills viewer: scan ~/.claude/skills, render SKILL.md as read-only de…
max4c Mar 31, 2026
3527938
Merge branch 'worktree-agent-a89a23a4' into dev
max4c Mar 31, 2026
d0352b9
Fix Xcode build: link GhosttyKit xcframework + add missing SkillDetai…
max4c Mar 31, 2026
74182c2
Fix select option kebab: always visible (not hover-dependent), use on…
max4c Mar 31, 2026
cb39ae9
Select options: Notion-style colored pills, grip dots + kebab on hove…
max4c Mar 31, 2026
6784b60
Fix page icon not syncing to sidebar: update file tree via pane syste…
max4c Mar 31, 2026
454a1e8
Configurable AI model: add Opus option, thread model through meeting …
max4c Mar 31, 2026
c613800
Merge branch 'worktree-agent-a04a7b80' into dev
max4c Mar 31, 2026
6b919c5
Fix ambiguous summarizeTranscript call: add explicit String type anno…
max4c Mar 31, 2026
073c097
Add FilterGroup recursive data model: AND/OR filter groups with auto-…
max4c Mar 31, 2026
1832f42
Merge branch 'worktree-agent-a7a40185' into dev
max4c Mar 31, 2026
5b8f9ba
Mention picker: @ trigger in text blocks, filtered page list, inserts…
max4c Mar 31, 2026
c06887d
Merge branch 'worktree-agent-a2fd11ae' into dev
max4c Mar 31, 2026
c1ed44e
Native Gateway dashboard: live ticket counts, quick links, database g…
max4c Mar 31, 2026
8acec07
Merge Gateway dashboard (resolved FileEntry, OpenFile, ContentView co…
max4c Mar 31, 2026
61a4d45
FormulaEngine: recursive descent parser for database computed properties
max4c Mar 31, 2026
7b25886
Merge branch 'worktree-agent-a8067b84' into dev
max4c Mar 31, 2026
7650980
Import audio recordings: NSOpenPanel + existing transcription pipelin…
max4c Mar 31, 2026
497a529
Import audio recordings (resolved MeetingsView conflict: keep both im…
max4c Mar 31, 2026
be44594
Pre-/go: import removal, queue.json, stashed changes from session
max4c Apr 1, 2026
5a4a967
Merge origin/main: mail intelligence, Keychain secrets, OAuth hardening
max4c Apr 1, 2026
1adaa37
Ship meeting block: FluidAudio transcription, live transcript UI, AI …
max4c Apr 2, 2026
77f9c1f
Fix SwiftLint line-length violation in DatabaseRowViewModel
max4c Apr 2, 2026
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
82 changes: 5 additions & 77 deletions .go/progress.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,5 @@
# Long Run — 2026-03-29

Started: 9:45 PM
Finished: ~5:30 AM
Duration: ~8 hours (across 2 sessions due to interruptions)

## Summary
Completed: 7/10 executable Bugbook tickets
Skipped: 3 (extremely large features — each multi-day scope)
Blocked: 5 (external deps, different repo, too vague)

## Completed

- [x] Outline (TOC) block type [High] (row_u9mndd)
- [x] Database table view calculations footer [High] (row_kegyb1)
- [x] Add Agents sidebar section [Medium] (row_woy99m)
- [x] Add MCP Servers listing [Low] (row_qfrvll)
- [x] Callout block type [High] (row_fnmxx9)
- [x] Change select option color [Medium] (row_bu993v)
- [x] Meeting notes markdown shortcuts [High] (row_34on11)

## Discoveries

These findings should inform future work:
- AggregationEngine already existed in BugbookCore/Engine from prior session
- ~/.claude/skills/ contains both regular folders and symlinks to ~/.agents/skills/
- MCP servers are stored in ~/.claude.json (not ~/.claude/settings.json as ticket spec said)
- Select option color infrastructure existed but context menus were only on inline pills — added to dropdown popovers and edit dialog
- Dev branch has table block type (WIP) from prior commits

## Review Guide

All work is on `dev`. To review:

```bash
git checkout dev
open macos/Bugbook.xcodeproj # Cmd+R
```

### 1. Outline (TOC) block [Medium risk]
Type `/toc` or `/outline` → TOC block with indented heading list, clickable entries

### 2. Database calculations footer [Medium risk]
Open any database table → hover footer row → click "Calculate" → pick a function (Sum, Avg, etc.)

### 3. Agents sidebar section [Medium risk]
Look for "Agents" section between Favorites and Workspace → skills listed, click opens in editor with banner

### 4. MCP Servers listing [Low risk]
Under Agents section → MCP servers from ~/.claude.json shown with plug icon

### 5. Callout block [Medium risk]
Type `/callout` → info callout with blue border. Click icon to cycle variants (info/warning/success/error). Child blocks inside.

### 6. Select option color [Low risk]
Open a database → click a select cell → right-click an option → Color submenu, or Edit with color picker grid

### 7. Meeting notes markdown [High risk]
Create/open a meeting block → notes area supports bullets (- ), headings (# ), tasks ([] ), slash menu, Tab indent

## Skipped (too large for overnight)

- AND/OR filter groups (row_1i5rmc) — recursive filter model + nested UI, multi-day scope
- Inline mentions (row_xxvee0) — attributed text or parse-time mentions, picker UI, backlinks, multi-day scope
- Formula/rollup/lookup (row_cygwau) — expression parser, cross-DB resolution, multi-day scope

## Blocked

- Build native Gateway — too vague, needs spec
- Google OAuth — needs domain, Google Console (external)
- Restructure Gateway 8.0 — content/data migration
- Live knowledge retrieval — too vague
- 7 Canopy tickets — different repo (/Users/maxforsey/canopy-menu/)

## Build Status
Dev branch: PASSING (swift build clean)
All 7 features compile and build together.
# Go Run — 2026-03-31
Started: 05:18 PM
Time budget: 8h
Queue: 12 tickets from .go/queue.json
Worker mix: 9 Codex, 3 Claude agent
14 changes: 14 additions & 0 deletions .go/queue.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{"position": 1, "row_id": "row_wtl5f9", "name": "Ship Bugbook as standalone .app binary", "files": ["macos/Bugbook.xcodeproj/project.pbxproj", "macos/project.yml", "Package.swift", "Sources/Bugbook/App/BugbookApp.swift"], "worker": "claude", "note": "Complex build system work — needs xcode project investigation, GhosttyKit resolution, signing. Claude agent better suited."},
{"position": 2, "row_id": "row_xsiof2", "name": "Gateway redesign", "files": ["Sources/Bugbook/Views/Gateway/GatewayView.swift", "Sources/Bugbook/Views/ContentView.swift"], "worker": "codex", "note": "Has full design spec in Bugbook. Progressive disclosure, greeting+date, quick nav, todays focus, recent activity."},
{"position": 3, "row_id": "row_wcsgow", "name": "Rename Meetings → Meetings", "files": ["Sources/Bugbook/Views/Meetings/MeetingsView.swift", "Sources/Bugbook/Views/Sidebar/SidebarView.swift"], "worker": "codex", "note": "Simple rename. Sidebar + view title → Meetings. Keep chat button."},
{"position": 4, "row_id": "row_k1pfpn", "name": "Meeting padding fix", "files": ["Sources/Bugbook/Views/Editor/MeetingBlockView.swift"], "worker": "codex", "note": "Reduce title-to-notes gap to 4pt max."},
{"position": 5, "row_id": "row_fnmxx9", "name": "Callout block fix", "files": ["Sources/Bugbook/Views/Editor/CalloutBlockView.swift", "Sources/Bugbook/Views/Editor/BlockCellView.swift"], "worker": "codex", "note": "Remove left border. Just rounded gray bg + icon. Match TOC styling."},
{"position": 6, "row_id": "row_u9mndd", "name": "Outline/TOC fix", "files": ["Sources/Bugbook/Views/Editor/OutlineBlockView.swift", "Sources/Bugbook/Views/Editor/BlockCellView.swift"], "worker": "codex", "note": "Grey text links not bullets/stars. Match callout container. Click scrolls to heading."},
{"position": 7, "row_id": "row_b7h2vl", "name": "Heading toggles behavior", "files": ["Sources/Bugbook/Views/Editor/BlockCellView.swift", "Sources/Bugbook/Models/BlockDocument.swift", "Sources/BugbookCLI/PageBlockHelpers.swift"], "worker": "claude", "note": "Complex: Cmd+Shift+Enter toggle, Enter exits, auto-nest smaller headings. Needs multi-file reasoning."},
{"position": 8, "row_id": "row_srmgse", "name": "TableBlockView fix", "files": ["Sources/Bugbook/Views/Editor/TableBlockView.swift"], "worker": "codex", "note": "Attempt 3. Fix grip dots duplication, alignment, flush with content area."},
{"position": 9, "row_id": "row_0lsztg", "name": "Kebab menu fix", "files": ["Sources/Bugbook/Views/Database/SelectOptionViews.swift"], "worker": "codex", "note": "Larger hit area, popover positioning beside not on top."},
{"position": 10, "row_id": "row_qm7iyh", "name": "Chat redesign", "files": ["Sources/Bugbook/Views/AI/AiSidePanelView.swift", "Sources/Bugbook/Views/AI/NotesChatView.swift", "Sources/Bugbook/Views/ContentView.swift", "Sources/Bugbook/Views/Sidebar/SidebarView.swift"], "worker": "codex", "note": "Rename Ask AI → Chat, remove X, Cmd+I opens sidebar panel."},
{"position": 11, "row_id": "row_dimm5g", "name": "Mention picker link styling", "files": ["Sources/Bugbook/Views/Editor/BlockTextView.swift", "Sources/Bugbook/Views/Editor/WikiLinkView.swift"], "worker": "codex", "note": "Style @[[Page Name]] as visible link (pill or colored text), clickable."},
{"position": 12, "row_id": "row_uqw8vz", "name": "Cmd+K search navigation fix", "files": ["Sources/Bugbook/Views/ContentView.swift", "Sources/Bugbook/Views/Components/CommandPaletteView.swift"], "worker": "claude", "note": "Attempt 3. navigateToEntryInPane works from sidebar but not command palette. Deep nav system issue."}
]
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ let package = Package(
.linkedFramework("IOSurface"),
.linkedFramework("Metal"),
.linkedFramework("QuartzCore"),
.linkedFramework("WebKit"),
.linkedLibrary("c++"),
.linkedLibrary("z"),
]
Expand Down
40 changes: 33 additions & 7 deletions Sources/Bugbook/Lib/MarkdownBlockParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ enum MarkdownBlockParser {
}

// Callout block
if let calloutVariant = parseCalloutOpenComment(trimmed) {
if let calloutMeta = parseCalloutOpenComment(trimmed) {
i += 1
let title = i < lines.count ? String(lines[i]) : ""
i += 1
Expand All @@ -263,7 +263,8 @@ enum MarkdownBlockParser {
}
let children = childLines.isEmpty ? [] : parse(childLines.joined(separator: "\n"))
var block = makeBlock(type: .callout, text: title, children: children)
block.calloutType = calloutVariant
block.calloutIcon = calloutMeta.icon
block.calloutColor = calloutMeta.color
blocks.append(block)
continue
}
Expand Down Expand Up @@ -678,7 +679,7 @@ enum MarkdownBlockParser {
}

case .callout:
lines.append("<!-- callout \(block.calloutType) -->")
lines.append("<!-- callout icon:\(block.calloutIcon) color:\(block.calloutColor) -->")
lines.append(block.text)
if !block.children.isEmpty {
lines.append(serialize(block.children, includeBlockIDComments: includeBlockIDComments))
Expand Down Expand Up @@ -954,15 +955,40 @@ enum MarkdownBlockParser {
return level
}

private static func parseCalloutOpenComment(_ trimmed: String) -> String? {
private static func parseCalloutOpenComment(_ trimmed: String) -> (icon: String, color: String)? {
guard trimmed.hasPrefix("<!-- callout"), trimmed.hasSuffix("-->") else { return nil }
let inner = trimmed.dropFirst(4).dropLast(3).trimmingCharacters(in: .whitespaces)
// inner is like "callout info" or "callout warning"
// inner is like "callout icon:lightbulb color:default" or legacy "callout info"
guard inner.hasPrefix("callout") else { return nil }
let rest = inner.dropFirst("callout".count).trimmingCharacters(in: .whitespaces)

// New format: key:value pairs
if rest.contains("icon:") || rest.contains("color:") {
var icon = "lightbulb"
var color = "default"
let parts = rest.split(separator: " ")
for part in parts {
if part.hasPrefix("icon:") {
icon = String(part.dropFirst("icon:".count))
} else if part.hasPrefix("color:") {
color = String(part.dropFirst("color:".count))
}
}
return (icon: icon, color: color)
}

// Legacy format: "callout info", "callout warning", etc.
let legacyMap: [String: (String, String)] = [
"info": ("lightbulb", "default"),
"warning": ("exclamationmark.triangle", "orange"),
"success": ("checkmark.circle", "green"),
"error": ("xmark.circle", "red"),
]
let variant = rest.isEmpty ? "info" : String(rest)
let validVariants = ["info", "warning", "success", "error"]
return validVariants.contains(variant) ? variant : "info"
if let mapped = legacyMap[variant] {
return (icon: mapped.0, color: mapped.1)
}
return (icon: "lightbulb", color: "default")
}

private static func parsePageLinkComment(_ line: String) -> String? {
Expand Down
9 changes: 6 additions & 3 deletions Sources/Bugbook/Models/Block.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ struct Block: Identifiable, Equatable {
var hasHeaderRow: Bool = false

// Callout block properties
var calloutType: String = "info"
var calloutIcon: String = "lightbulb"
var calloutColor: String = "default"

init(
id: UUID = UUID(),
Expand Down Expand Up @@ -98,7 +99,8 @@ struct Block: Identifiable, Equatable {
meetingNotes: String = "",
tableData: [[String]] = [],
hasHeaderRow: Bool = false,
calloutType: String = "info"
calloutIcon: String = "lightbulb",
calloutColor: String = "default"
) {
self.id = id
self.type = type
Expand All @@ -125,6 +127,7 @@ struct Block: Identifiable, Equatable {
self.meetingNotes = meetingNotes
self.tableData = tableData
self.hasHeaderRow = hasHeaderRow
self.calloutType = calloutType
self.calloutIcon = calloutIcon
self.calloutColor = calloutColor
}
}
81 changes: 75 additions & 6 deletions Sources/Bugbook/Models/BlockDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class BlockDocument {
var pagePickerSearch: String = ""
var pagePickerSelectedIndex: Int = 0
var showTemplatePicker: Bool = false
var mentionPickerBlockId: UUID?
var mentionPickerFilter: String = ""
var mentionPickerSelectedIndex: Int = 0
/// Character index in the block text where '@' was typed.
var mentionPickerAnchorPos: Int = 0
var aiPromptBlockId: UUID?
var aiPromptText: String = ""
var isAiGenerating: Bool = false
Expand Down Expand Up @@ -161,6 +166,14 @@ class BlockDocument {
updateBlockProperty(id: blockId) { $0.meetingTitle = title }
}

func updateMeetingTranscript(blockId: UUID, transcript: String) {
updateBlockProperty(id: blockId) { $0.meetingTranscript = transcript }
}

func updateMeetingActionItems(blockId: UUID, actionItems: String) {
updateBlockProperty(id: blockId) { $0.meetingActionItems = actionItems }
}

func updateMeetingState(blockId: UUID, state: MeetingBlockState) {
updateBlockProperty(id: blockId) { $0.meetingState = state }
}
Expand Down Expand Up @@ -616,7 +629,7 @@ class BlockDocument {
}
updateBlockProperty(id: id) { block in
block.type = type
if type == .heading {
if type == .heading || type == .headingToggle {
block.headingLevel = 1
} else {
block.headingLevel = 0
Expand All @@ -626,7 +639,10 @@ class BlockDocument {

func setHeadingLevel(id: UUID, level: Int) {
updateBlockProperty(id: id) { block in
block.type = .heading
// Preserve the current type when it's already a heading variant
if block.type != .heading && block.type != .headingToggle {
block.type = .heading
}
block.headingLevel = level
}
}
Expand Down Expand Up @@ -1003,12 +1019,13 @@ class BlockDocument {
return

case let .blockType(type, headingLevel):
// Callout needs special handling — set calloutType and focus after
// Callout needs special handling — set icon/color defaults and focus after
if type == .callout {
saveUndo()
updateBlockProperty(id: blockId) { block in
block.type = .callout
block.calloutType = "info"
block.calloutIcon = "lightbulb"
block.calloutColor = "default"
block.text = ""
}
focusOrInsertParagraphAfter(blockId: blockId)
Expand All @@ -1030,12 +1047,12 @@ class BlockDocument {
return
}

// Table block — initialize with empty 3x2 grid
// Table block — initialize with empty 3x3 grid
if type == .table {
saveUndo()
updateBlockProperty(id: blockId) { block in
block.type = .table
block.tableData = Array(repeating: Array(repeating: "", count: 3), count: 2)
block.tableData = Array(repeating: Array(repeating: "", count: 3), count: 3)
block.hasHeaderRow = false
}
focusOrInsertParagraphAfter(blockId: blockId)
Expand Down Expand Up @@ -1147,6 +1164,58 @@ class BlockDocument {
slashMenuSelectedIndex = 0
}

// MARK: - Mention Picker (@-mention)

@ObservationIgnored private var _mentionPickerCache: (search: String, entries: [FileEntry])?

var filteredMentionEntries: [FileEntry] {
if let cache = _mentionPickerCache, cache.search == mentionPickerFilter {
return cache.entries
}
let flat = flattenEntries(availablePages)
.filter { !$0.isDirectory && ($0.name.hasSuffix(".md") || $0.isDatabase) }
let result = mentionPickerFilter.isEmpty
? flat
: flat.filter { $0.name.localizedCaseInsensitiveContains(mentionPickerFilter) }
_mentionPickerCache = (search: mentionPickerFilter, entries: result)
return result
}

func executeMentionPicker() {
let items = filteredMentionEntries
guard !items.isEmpty else { return }
let idx = min(mentionPickerSelectedIndex, items.count - 1)
guard idx >= 0 else { dismissMentionPicker(); return }
let name = items[idx].name.replacingOccurrences(of: ".md", with: "")
insertMention(name: name)
}

func insertMention(name: String) {
guard let blockId = mentionPickerBlockId,
blockLocation(for: blockId) != nil else {
dismissMentionPicker()
return
}
let searchToken = "@" + mentionPickerFilter
let mention = "@[[" + name + "]]"
saveUndo()
updateBlockProperty(id: blockId) { block in
// Find the @<filter> token nearest to the anchor and replace it.
if let range = block.text.range(of: searchToken) {
block.text.replaceSubrange(range, with: mention)
Comment on lines +1203 to +1205
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace mention at anchor instead of first token match

insertMention currently replaces block.text.range(of: searchToken), which always targets the first matching @... token in the block. If the same mention prefix appears earlier in the text, selecting a mention from the picker edits the wrong occurrence and leaves the one at the cursor unchanged. The picker already tracks mentionPickerAnchorPos, so this replacement should be anchored to that position.

Useful? React with 👍 / 👎.

}
}
dismissMentionPicker()
}

func dismissMentionPicker() {
mentionPickerBlockId = nil
mentionPickerFilter = ""
mentionPickerSelectedIndex = 0
mentionPickerAnchorPos = 0
_mentionPickerCache = nil
}

// MARK: - Inline AI Prompt

func showAiPrompt(blockId: UUID) {
Expand Down
Loading
Loading