From e1e37865317c010b8609b8aba871e29f271cb02d Mon Sep 17 00:00:00 2001 From: murashit Date: Fri, 10 Jul 2026 20:35:50 +0900 Subject: [PATCH] Clarify composer context reference suggestions --- .../composer/daily-note-references.ts | 1 + .../chat/application/composer/suggestions.ts | 14 +++++--- .../vault-daily-note-references.obsidian.ts | 7 +++- .../application/composer/suggestions.test.ts | 36 ++++++++++++++----- .../vault-note-candidate-provider.test.ts | 3 ++ .../chat/panel/composer-controller.test.ts | 1 + 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/features/chat/application/composer/daily-note-references.ts b/src/features/chat/application/composer/daily-note-references.ts index 03508539..7a133a48 100644 --- a/src/features/chat/application/composer/daily-note-references.ts +++ b/src/features/chat/application/composer/daily-note-references.ts @@ -1,6 +1,7 @@ export interface DailyNoteReferenceCandidate { keyword: "today" | "tomorrow" | "yesterday"; display: string; + name: string; path: string; linktext: string; } diff --git a/src/features/chat/application/composer/suggestions.ts b/src/features/chat/application/composer/suggestions.ts index 269aacfa..a8bcb0cd 100644 --- a/src/features/chat/application/composer/suggestions.ts +++ b/src/features/chat/application/composer/suggestions.ts @@ -94,6 +94,7 @@ const THREAD_COMMAND_SUGGESTION_POLICIES: Record candidate.keyword.startsWith(query)); suggestions.push( ...matchingDailyNotes.map((candidate) => ({ - display: candidate.display, + display: `${candidate.display} · ${candidate.name}`, detail: candidate.path, replacement: `[[${candidate.linktext}]]`, start, @@ -170,6 +171,11 @@ function activeContextReferenceSuggestions( return suggestions.slice(0, 8); } +function selectionSuggestionPreview(selection: SelectionContextReference): string { + const preview = selection.text.replace(/\s+/g, " ").trim(); + return preview.length > SELECTION_SUGGESTION_PREVIEW_LIMIT ? `${preview.slice(0, SELECTION_SUGGESTION_PREVIEW_LIMIT - 1)}…` : preview; +} + function dailyNoteReferenceList( references: readonly DailyNoteReferenceCandidate[] | (() => readonly DailyNoteReferenceCandidate[]) | undefined, ): readonly DailyNoteReferenceCandidate[] { diff --git a/src/features/chat/host/obsidian/vault-daily-note-references.obsidian.ts b/src/features/chat/host/obsidian/vault-daily-note-references.obsidian.ts index 796db12d..09f1d32e 100644 --- a/src/features/chat/host/obsidian/vault-daily-note-references.obsidian.ts +++ b/src/features/chat/host/obsidian/vault-daily-note-references.obsidian.ts @@ -3,7 +3,7 @@ import { type App, moment, normalizePath, TFile } from "obsidian"; import { appHasDailyNotesPluginLoaded, getDailyNoteSettings, type IPeriodicNoteSettings } from "obsidian-daily-notes-interface"; import type { DailyNoteReferenceCandidate } from "../../application/composer/daily-note-references"; -import { linktextForFile } from "./vault-note-links.obsidian"; +import { displayNameForFile, linktextForFile } from "./vault-note-links.obsidian"; const RELATIVE_DAILY_NOTES = [ { keyword: "today", display: "Today", dayOffset: 0 }, @@ -38,12 +38,17 @@ export function dailyNoteReferencesFromSettings( return { keyword, display, + name: existingFile instanceof TFile ? displayNameForFile(existingFile) : dailyNoteName(filename), path, linktext: existingFile instanceof TFile ? linktextForFile(app, existingFile, sourcePath) : path.replace(/\.md$/i, ""), }; }); } +function dailyNoteName(filename: string): string { + return (filename.split("/").at(-1) ?? filename).replace(/\.md$/i, ""); +} + function dailyNotePath(folder: string, filename: string): string { const markdownFilename = filename.toLowerCase().endsWith(".md") ? filename : `${filename}.md`; return normalizePath(folder ? `${folder}/${markdownFilename}` : markdownFilename); diff --git a/tests/features/chat/application/composer/suggestions.test.ts b/tests/features/chat/application/composer/suggestions.test.ts index 0d6284c6..a074eb46 100644 --- a/tests/features/chat/application/composer/suggestions.test.ts +++ b/tests/features/chat/application/composer/suggestions.test.ts @@ -236,7 +236,7 @@ describe("composer suggestions", () => { }, })[0], ).toMatchObject({ - display: "Active file", + display: "Active · Beta Note", detail: "topics/Beta Note.md", replacement: "[[Beta Note]]", }); @@ -257,20 +257,20 @@ describe("composer suggestions", () => { path: "topics/Beta Note.md", linktext: "Beta Note", range: { from: { line: 41, ch: 4 }, to: { line: 46, ch: 0 } }, - text: "selected", + text: " selected\n\ttext ", }, }, })[0], ).toMatchObject({ - display: "Selection", - detail: "topics/Beta Note.md L42:C5-L47:C1", + display: "Selection · Beta Note · L42:C5-L47:C1", + detail: "selected text", replacement: "[[Beta Note]] (L42:C5-L47:C1)", selectionContext: { name: "Beta Note", path: "topics/Beta Note.md", linktext: "Beta Note", range: { from: { line: 41, ch: 4 }, to: { line: 46, ch: 0 } }, - text: "selected", + text: " selected\n\ttext ", }, }); expect(activeComposerSuggestions("/pla", notes, [])[0]?.replacement).toBe("/plan"); @@ -281,23 +281,43 @@ describe("composer suggestions", () => { expect(activeComposerSuggestions("/help", notes, [])).toEqual([]); }); + it("bounds selection preview text before rendering it in the suggestion list", () => { + const suggestion = activeComposerSuggestions("@sel", notes, [], [], [], null, { + contextReferences: { + activeNote: null, + selection: { + name: "Beta Note", + path: "topics/Beta Note.md", + linktext: "Beta Note", + range: { from: { line: 0, ch: 0 }, to: { line: 0, ch: 600 } }, + text: "x".repeat(600), + }, + }, + })[0]; + + expect(suggestion?.detail).toBe(`${"x".repeat(499)}…`); + }); + it("resolves relative daily-note references to configured wikilinks", () => { const dailyNoteReferences = [ { keyword: "today", display: "Today", + name: "2026-07-10", path: "Journal/2026/07/2026-07-10.md", linktext: "Journal/2026/07/2026-07-10", }, { keyword: "tomorrow", display: "Tomorrow", + name: "2026-07-11", path: "Journal/2026/07/2026-07-11.md", linktext: "Journal/2026/07/2026-07-11", }, { keyword: "yesterday", display: "Yesterday", + name: "2026-07-09", path: "Journal/2026/07/2026-07-09.md", linktext: "Journal/2026/07/2026-07-09", }, @@ -309,12 +329,12 @@ describe("composer suggestions", () => { }), ).toMatchObject([ { - display: "Today", + display: "Today · 2026-07-10", detail: "Journal/2026/07/2026-07-10.md", replacement: "[[Journal/2026/07/2026-07-10]]", }, { - display: "Tomorrow", + display: "Tomorrow · 2026-07-11", detail: "Journal/2026/07/2026-07-11.md", replacement: "[[Journal/2026/07/2026-07-11]]", }, @@ -324,7 +344,7 @@ describe("composer suggestions", () => { dailyNoteReferences, })[0], ).toMatchObject({ - display: "Yesterday", + display: "Yesterday · 2026-07-09", detail: "Journal/2026/07/2026-07-09.md", replacement: "[[Journal/2026/07/2026-07-09]]", }); diff --git a/tests/features/chat/host/vault-note-candidate-provider.test.ts b/tests/features/chat/host/vault-note-candidate-provider.test.ts index f0095cec..c829ab03 100644 --- a/tests/features/chat/host/vault-note-candidate-provider.test.ts +++ b/tests/features/chat/host/vault-note-candidate-provider.test.ts @@ -48,18 +48,21 @@ describe("VaultNoteCandidateProvider", () => { { keyword: "today", display: "Today", + name: "2026-07-10", path: "Journal/2026/07/2026-07-10.md", linktext: "2026-07-10", }, { keyword: "tomorrow", display: "Tomorrow", + name: "2026-07-11", path: "Journal/2026/07/2026-07-11.md", linktext: "Journal/2026/07/2026-07-11", }, { keyword: "yesterday", display: "Yesterday", + name: "2026-07-09", path: "Journal/2026/07/2026-07-09.md", linktext: "Journal/2026/07/2026-07-09", }, diff --git a/tests/features/chat/panel/composer-controller.test.ts b/tests/features/chat/panel/composer-controller.test.ts index 3be0d871..dd99d94a 100644 --- a/tests/features/chat/panel/composer-controller.test.ts +++ b/tests/features/chat/panel/composer-controller.test.ts @@ -178,6 +178,7 @@ describe("ChatComposerController", () => { { keyword: "today" as const, display: "Today", + name: "2026-07-10", path: "Journal/2026-07-10.md", linktext: "Journal/2026-07-10", },