From e476e81e6e7e320e641d5f454143be612feab8e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Feb 2026 11:31:39 +0000 Subject: [PATCH 1/3] Initial plan From 44338da608dd09f5a955783329d2efc3f2ca0f6f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Feb 2026 11:36:55 +0000 Subject: [PATCH 2/3] Add hint tooltip for qualifying markdown textareas Co-authored-by: da-ba <17434220+da-ba@users.noreply.github.com> --- e2e/extension.spec.ts | 62 ++++++++++++++++++++++++++++++ src/content/hint-tooltip.ts | 75 +++++++++++++++++++++++++++++++++++++ src/content/index.ts | 22 +++++++++-- 3 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 src/content/hint-tooltip.ts diff --git a/e2e/extension.spec.ts b/e2e/extension.spec.ts index 0888763..3ee058b 100644 --- a/e2e/extension.spec.ts +++ b/e2e/extension.spec.ts @@ -286,6 +286,68 @@ test.describe("Slash Commands", () => { await context.close(); }); + + test("hint tooltip appears on focus and hides on blur", async () => { + const context = await chromium.launch({ headless: false }); + const page = await context.newPage(); + + await page.goto(`http://localhost:${testServer.port}/`); + await page.waitForLoadState("domcontentloaded"); + await injectContentScript(page); + await page.waitForTimeout(500); + + const textarea = page.locator("#test-textarea"); + await expect(textarea).toBeVisible(); + + // Focus the textarea - tooltip should appear + await textarea.click(); + await page.waitForTimeout(200); + + const tooltip = page.locator("#slashPaletteHint"); + await expect(tooltip).toBeVisible({ timeout: 2000 }); + + // Tooltip should contain the "//" hint text + const tooltipText = await tooltip.textContent(); + expect(tooltipText).toContain("//"); + + // Blur the textarea - tooltip should hide + await page.keyboard.press("Tab"); + await page.waitForTimeout(200); + await expect(tooltip).not.toBeVisible(); + + await context.close(); + }); + + test("hint tooltip hides when picker opens", async () => { + const context = await chromium.launch({ headless: false }); + const page = await context.newPage(); + + await page.goto(`http://localhost:${testServer.port}/`); + await page.waitForLoadState("domcontentloaded"); + await injectContentScript(page); + await page.waitForTimeout(500); + + const textarea = page.locator("#test-textarea"); + await textarea.click(); + await page.waitForTimeout(200); + + // Tooltip should be visible after focus + const tooltip = page.locator("#slashPaletteHint"); + await expect(tooltip).toBeVisible({ timeout: 2000 }); + + // Type // to open picker + await textarea.fill("//"); + await page.waitForTimeout(500); + + // Picker should be visible + const picker = page.locator("#slashPalettePicker"); + await expect(picker).toBeVisible({ timeout: 3000 }); + + // Tooltip should be hidden + await expect(tooltip).not.toBeVisible(); + + await context.close(); + }); }); test.describe("Options Page - Giphy Image Settings", () => { diff --git a/src/content/hint-tooltip.ts b/src/content/hint-tooltip.ts new file mode 100644 index 0000000..06c8872 --- /dev/null +++ b/src/content/hint-tooltip.ts @@ -0,0 +1,75 @@ +/** + * Hint tooltip shown near qualifying markdown textareas. + * Displays a small indicator hinting to type "//" to open the command list. + */ + +import { isDarkMode, fontSystemUi, fontSansSerif } from "../utils/theme.ts" +import { COMMAND_PREFIX } from "../utils/command-prefix.ts" + +let tooltipEl: HTMLElement | null = null + +function applyTooltipStyles(el: HTMLElement): void { + const dark = isDarkMode() + el.style.position = "fixed" + el.style.zIndex = "999998" + el.style.fontSize = "12px" + el.style.padding = "3px 7px" + el.style.borderRadius = "4px" + el.style.pointerEvents = "none" + el.style.whiteSpace = "nowrap" + el.style.fontFamily = fontSystemUi() + ", " + fontSansSerif() + el.style.color = dark ? "#8d96a0" : "#656d76" + el.style.border = `1px solid ${dark ? "#3d444d" : "#d0d7de"}` + el.style.backgroundColor = dark ? "#161b22" : "#ffffff" + el.style.boxShadow = dark + ? "0 2px 8px rgba(1,4,9,0.4)" + : "0 2px 8px rgba(140,149,159,0.15)" +} + +function ensureTooltip(): HTMLElement { + if (!tooltipEl || !document.body.contains(tooltipEl)) { + const el = document.createElement("div") + el.id = "slashPaletteHint" + el.textContent = `Type ${COMMAND_PREFIX} for commands` + el.style.display = "none" + applyTooltipStyles(el) + document.body.appendChild(el) + tooltipEl = el + } + return tooltipEl +} + +function positionTooltip(field: HTMLTextAreaElement): void { + const el = tooltipEl + if (!el) return + const rect = field.getBoundingClientRect() + const elWidth = el.offsetWidth || 160 + const left = Math.max(0, rect.right - elWidth) + const top = rect.bottom + 4 + el.style.left = `${left}px` + el.style.top = `${top}px` +} + +export function showHintTooltip(field: HTMLTextAreaElement): void { + const el = ensureTooltip() + applyTooltipStyles(el) + el.style.display = "block" + positionTooltip(field) +} + +export function hideHintTooltip(): void { + if (tooltipEl) { + tooltipEl.style.display = "none" + } +} + +export function repositionHintTooltip(field: HTMLTextAreaElement): void { + if (!tooltipEl || tooltipEl.style.display === "none") return + positionTooltip(field) +} + +export function refreshHintTooltipTheme(): void { + if (tooltipEl && tooltipEl.style.display !== "none") { + applyTooltipStyles(tooltipEl) + } +} diff --git a/src/content/index.ts b/src/content/index.ts index 17b74db..06046aa 100644 --- a/src/content/index.ts +++ b/src/content/index.ts @@ -23,6 +23,12 @@ import { moveSelectionGrid, applyPickerStyles, } from "./picker/index.ts" +import { + showHintTooltip, + hideHintTooltip, + repositionHintTooltip, + refreshHintTooltipTheme, +} from "./hint-tooltip.ts" // Import commands to register them import "./commands/giphy/index.ts" @@ -119,6 +125,7 @@ async function handleCommandInput( state.activeCommand = cmdName setHeader("Slash Palette", COMMAND_PREFIX + cmdName + (query ? " " + query : "")) + hideHintTooltip() showPicker(field) positionPickerAtCaret(field) @@ -303,6 +310,9 @@ function attachToField(field: HTMLTextAreaElement): void { ;(field as unknown as { __slashPaletteBound: boolean }).__slashPaletteBound = true field.addEventListener("input", () => handleFieldInput(field)) + field.addEventListener("focus", () => { + if (!isPickerVisible()) showHintTooltip(field) + }) field.addEventListener("keyup", (ev) => { // Skip action keys that are handled elsewhere if (ev.key === "Escape") return @@ -336,10 +346,12 @@ function attachToField(field: HTMLTextAreaElement): void { }) field.addEventListener("scroll", () => { if (isPickerVisible()) positionPickerAtCaret(field) + repositionHintTooltip(field) }) field.addEventListener("keydown", (ev) => onFieldKeyDown(ev, field)) field.addEventListener("blur", () => { + hideHintTooltip() setTimeout(() => { // Never hide while settings panel is open - user is interacting with controls if (state.showingSettings) return @@ -420,6 +432,7 @@ function boot(): void { if (isPickerVisible() && state.activeField) { positionPickerAtCaret(state.activeField) } + if (state.activeField) repositionHintTooltip(state.activeField) }, { passive: true } ) @@ -428,13 +441,16 @@ function boot(): void { if (isPickerVisible() && state.activeField) { positionPickerAtCaret(state.activeField) } + if (state.activeField) repositionHintTooltip(state.activeField) }) // Handle theme changes onThemeChange(() => { - if (!isPickerVisible()) return - if (state.pickerEl) applyPickerStyles(state.pickerEl) - refreshSelectionStyles() + if (isPickerVisible()) { + if (state.pickerEl) applyPickerStyles(state.pickerEl) + refreshSelectionStyles() + } + refreshHintTooltipTheme() }) } From ec62800a3a8590fb76188c065b6b6577c4085744 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Feb 2026 18:23:25 +0000 Subject: [PATCH 3/3] Fix prettier formatting in hint-tooltip.ts Co-authored-by: da-ba <17434220+da-ba@users.noreply.github.com> --- src/content/hint-tooltip.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/content/hint-tooltip.ts b/src/content/hint-tooltip.ts index 06c8872..3ec2b42 100644 --- a/src/content/hint-tooltip.ts +++ b/src/content/hint-tooltip.ts @@ -21,9 +21,7 @@ function applyTooltipStyles(el: HTMLElement): void { el.style.color = dark ? "#8d96a0" : "#656d76" el.style.border = `1px solid ${dark ? "#3d444d" : "#d0d7de"}` el.style.backgroundColor = dark ? "#161b22" : "#ffffff" - el.style.boxShadow = dark - ? "0 2px 8px rgba(1,4,9,0.4)" - : "0 2px 8px rgba(140,149,159,0.15)" + el.style.boxShadow = dark ? "0 2px 8px rgba(1,4,9,0.4)" : "0 2px 8px rgba(140,149,159,0.15)" } function ensureTooltip(): HTMLElement {