fix(editor): keep file basename in @-reference links
The explorer @ button appended plain text (@folder/file.ts), which DSH's composer only decorates as a folder ref (@folder/) — the file name stayed outside the link. Insert FILE references through DSH's own structured insert-reference event instead, minting one chip for the whole reference (displayed as @basename, serialized as the full @path). Directories keep the plain @dir/ mention so folder completion still works.
This commit is contained in:
@@ -5,6 +5,14 @@
|
||||
* through `ctx.get` (the inject-free read the app's own plugins use); a
|
||||
* missing service or scope degrades to a logged no-op, never a crash.
|
||||
*
|
||||
* File references additionally use DSH's own structured insert event
|
||||
* (`slash/input-insert-reference`, see `insertFileReference`) instead of
|
||||
* plain draft text: the native `@file` picker emits this event, and the
|
||||
* conversation input machine mints one occurrence whose chip covers the
|
||||
* whole reference. Plain text `@folder/file.ts` only ever gets DSH's
|
||||
* folder-ref decoration (`@folder/`) — the file name stays undecorated — so
|
||||
* plain append is the fallback, not the primary path, for files.
|
||||
*
|
||||
* Insert position (fixes upstream issue #425): the draft store only exposes
|
||||
* the whole string (`getSnapshot().draft` + `setDraft(text)`) — there is no
|
||||
* caret API on the conversation service. The composer's `<textarea>` keeps
|
||||
@@ -184,4 +192,64 @@ export function appendToDraft(ctx: Context, sessionId: string, text: string): bo
|
||||
console.warn('[dsh-better-sidebar] draft insert failed:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The DSH `@file` spelling for one relative path, mirroring the host grammar
|
||||
* (`formatFileMention` in `@deepseek-ai/dsh-file-reference`): plain when
|
||||
* there is no whitespace, quoted when there is, and `undefined` when the
|
||||
* path contains a control character or an embedded quote the editor grammar
|
||||
* cannot represent.
|
||||
*/
|
||||
export function fileMention(relativePath: string): { mention: string; label: string } | undefined {
|
||||
const path = relativePath.replace(/[\\/]+$/, '')
|
||||
if (/[\u0000-\u001f\u007f-\u009f"]/u.test(path)) return undefined
|
||||
const mention = /\s/u.test(path) ? `@"${path}"` : `@${path}`
|
||||
const at = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))
|
||||
const label = at === -1 ? path : path.slice(at + 1)
|
||||
return { mention, label }
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert one FILE reference as a structured chip (like DSH's own `@` picker).
|
||||
* The chip displays `@<basename>` but serializes to `@<relative path>` on
|
||||
* send, so the reference stays a single link from trigger to basename.
|
||||
*
|
||||
* Directories are NOT handled here: DSH's folder grammar wants the trailing
|
||||
* slash as plain text (`@dir/`) so completion can descend, which
|
||||
* `appendToDraft` already covers.
|
||||
*/
|
||||
export function insertFileReference(ctx: Context, sessionId: string, relativePath: string): boolean {
|
||||
const reference = fileMention(relativePath)
|
||||
if (reference === undefined) return false
|
||||
try {
|
||||
const actx = ctx.sessions.scope(sessionId)
|
||||
if (actx === undefined) return false
|
||||
const conversation = ctx.get('conversation') as SidebarConversation | undefined
|
||||
if (conversation === undefined) return false
|
||||
const input = conversation.input.for(actx)
|
||||
const before = input.state.getSnapshot()
|
||||
if (before.draftRev === undefined) return false
|
||||
// The session-scope Context's typed `emit` is keyed to DSH's closed event
|
||||
// map; this internal composer event is deliberately string-loose at runtime.
|
||||
;(actx as unknown as { emit(name: string, payload: unknown): void }).emit('slash/input-insert-reference', {
|
||||
reference: {
|
||||
source: 'reference',
|
||||
ref: reference.mention,
|
||||
label: reference.label,
|
||||
appearance: 'file',
|
||||
clipboardText: reference.mention,
|
||||
},
|
||||
span: {
|
||||
draftRev: before.draftRev,
|
||||
start: before.draft.length,
|
||||
end: before.draft.length,
|
||||
},
|
||||
})
|
||||
const after = input.state.getSnapshot()
|
||||
return after.draftRev !== before.draftRev
|
||||
} catch (error) {
|
||||
console.warn('[dsh-better-sidebar] file-reference insert failed:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user