feat: wikilink pills in message bubbles, noteList context injection
- Render [[wikilink]] reference pills inside sent message bubbles with type-colored badges; clicking a pill opens the note - Add noteList (filtered note list titles, max 100) and noteListFilter to the structured context snapshot sent to the AI - Thread noteList/noteListFilter from App → Editor → EditorRightPanel → AiPanel - Store references in AiAgentMessage for display in chat history - Add tests for reference pill rendering and noteList context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -294,6 +294,43 @@ describe('buildContextSnapshot', () => {
|
||||
expect(json.referencedNotes).toBeUndefined()
|
||||
})
|
||||
|
||||
it('includes noteList when provided', () => {
|
||||
const noteList = [
|
||||
{ path: '/vault/a.md', title: 'Alpha', type: 'Project' },
|
||||
{ path: '/vault/b.md', title: 'Beta', type: 'Person' },
|
||||
]
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, allContent, entries,
|
||||
noteList,
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteList).toHaveLength(2)
|
||||
expect(json.noteList[0].title).toBe('Alpha')
|
||||
expect(json.noteList[1].type).toBe('Person')
|
||||
})
|
||||
|
||||
it('truncates noteList at 100 items', () => {
|
||||
const noteList = Array.from({ length: 150 }, (_, i) => ({
|
||||
path: `/vault/note-${i}.md`, title: `Note ${i}`, type: 'Note',
|
||||
}))
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, allContent, entries,
|
||||
noteList,
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteList).toHaveLength(100)
|
||||
expect(json.noteListTruncated).toEqual({ shown: 100, total: 150 })
|
||||
})
|
||||
|
||||
it('omits noteList when empty', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, allContent, entries,
|
||||
noteList: [],
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteList).toBeUndefined()
|
||||
})
|
||||
|
||||
it('includes belongsTo and relatedTo in frontmatter', () => {
|
||||
const entryWithRels = makeEntry({
|
||||
path: '/vault/a.md', title: 'Alpha',
|
||||
|
||||
@@ -61,11 +61,19 @@ export interface NoteReference {
|
||||
type: string | null
|
||||
}
|
||||
|
||||
/** Lightweight note summary for the context snapshot. */
|
||||
export interface NoteListItem {
|
||||
path: string
|
||||
title: string
|
||||
type: string
|
||||
}
|
||||
|
||||
/** Parameters for building the structured context snapshot. */
|
||||
export interface ContextSnapshotParams {
|
||||
activeEntry: VaultEntry
|
||||
allContent: Record<string, string>
|
||||
openTabs?: VaultEntry[]
|
||||
noteList?: NoteListItem[]
|
||||
noteListFilter?: { type: string | null; query: string }
|
||||
entries: VaultEntry[]
|
||||
references?: NoteReference[]
|
||||
@@ -82,9 +90,11 @@ function entryFrontmatter(e: VaultEntry): Record<string, unknown> {
|
||||
return fm
|
||||
}
|
||||
|
||||
const MAX_NOTE_LIST_ITEMS = 100
|
||||
|
||||
/** Build a structured context snapshot as a system prompt for Claude. */
|
||||
export function buildContextSnapshot(params: ContextSnapshotParams): string {
|
||||
const { activeEntry, allContent, openTabs, noteListFilter, entries, references } = params
|
||||
const { activeEntry, allContent, openTabs, noteList, noteListFilter, entries, references } = params
|
||||
|
||||
const snapshot: Record<string, unknown> = {
|
||||
activeNote: {
|
||||
@@ -106,6 +116,14 @@ export function buildContextSnapshot(params: ContextSnapshotParams): string {
|
||||
}))
|
||||
}
|
||||
|
||||
if (noteList && noteList.length > 0) {
|
||||
const items = noteList.slice(0, MAX_NOTE_LIST_ITEMS)
|
||||
snapshot.noteList = items
|
||||
if (noteList.length > MAX_NOTE_LIST_ITEMS) {
|
||||
snapshot.noteListTruncated = { shown: MAX_NOTE_LIST_ITEMS, total: noteList.length }
|
||||
}
|
||||
}
|
||||
|
||||
if (noteListFilter && (noteListFilter.type || noteListFilter.query)) {
|
||||
snapshot.noteListFilter = noteListFilter
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user