feat: display selected text from page in sidepanel (#496)

* feat: select text and pass to sidepanel

* fix: lint issues

* fix: persist selection across tabs

* fix: review comments

* fix: change when the selection is cleared

* feat: sanitize url
This commit is contained in:
Dani Akash
2026-03-19 20:21:31 +05:30
committed by GitHub
parent f4d4b73a24
commit 5bb6143373
9 changed files with 256 additions and 3 deletions

View File

@@ -38,10 +38,34 @@ export function formatBrowserContext(browserContext?: BrowserContext): string {
return `${lines.join('\n')}\n\n---\n\n`
}
/** Strip XML-like tags that match our prompt delimiters to prevent injection. */
function sanitizeForPrompt(s: string): string {
return s.replace(
/<\/?(?:selected_text|USER_QUERY|page_context|AGENT_PROMPT|soul|memory_and_identity|security|workspace)[^>]*>/gi,
'',
)
}
export function formatUserMessage(
message: string,
browserContext?: BrowserContext,
selectedText?: string,
selectedTextSource?: { url: string; title: string },
): string {
const contextPrefix = formatBrowserContext(browserContext)
return `${contextPrefix}<USER_QUERY>\n${message}\n</USER_QUERY>`
let selectedTextBlock = ''
if (selectedText) {
const sanitizedText = sanitizeForPrompt(selectedText)
const title = selectedTextSource?.title
? sanitizeForPrompt(selectedTextSource.title).replace(/"/g, "'")
: ''
const url = selectedTextSource?.url
? sanitizeForPrompt(selectedTextSource.url)
: ''
const source = title ? ` (from "${title}"${url ? `${url}` : ''})` : ''
selectedTextBlock = `<selected_text${source}>\n${sanitizedText}\n</selected_text>\n\n`
}
return `${contextPrefix}${selectedTextBlock}<USER_QUERY>\n${message}\n</USER_QUERY>`
}

View File

@@ -171,6 +171,8 @@ export class ChatService {
const userContent = formatUserMessage(
request.message,
resolvedMessageContext,
request.selectedText,
request.selectedTextSource,
)
session.agent.appendUserMessage(userContent)

View File

@@ -47,6 +47,13 @@ export const ChatRequestSchema = AgentLLMConfigSchema.extend({
supportsImages: z.boolean().optional().default(true),
mode: z.enum(['chat', 'agent']).optional().default('agent'),
declinedApps: z.array(z.string()).optional(),
selectedText: z.string().optional(),
selectedTextSource: z
.object({
url: z.string(),
title: z.string(),
})
.optional(),
previousConversation: z
.union([
z.array(