diff --git a/packages/studio/src/components/editor/InlineTextToolbar.test.tsx b/packages/studio/src/components/editor/InlineTextToolbar.test.tsx index 148d786f41..c7a70106e6 100644 --- a/packages/studio/src/components/editor/InlineTextToolbar.test.tsx +++ b/packages/studio/src/components/editor/InlineTextToolbar.test.tsx @@ -3,7 +3,7 @@ import React, { act } from "react"; import { createRoot } from "react-dom/client"; import { afterEach, describe, expect, it } from "vitest"; -import { InlineTextToolbar } from "./InlineTextToolbar"; +import { InlineTextToolbar, swatchBackground } from "./InlineTextToolbar"; import type { InlineTextEditSession } from "../../hooks/useInlineTextEdit"; (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; @@ -191,4 +191,51 @@ describe("InlineTextToolbar", () => { expect(toolbar.style.left).toBe(`${100 + (20 + 50) * scale}px`); expect(toolbar.style.top).toBe(`${50 + 40 * scale - 10}px`); }); + it("shows the selection's colours in the swatch when they differ", () => { + const { element, session, iframe } = scene( + 'Helloworld', + ); + const { host } = render(session, iframe); + + selectAll(element); + + const swatch = toolbarIn(host)!.querySelector("span[aria-hidden]")!; + expect(swatch.style.backgroundImage).toBe("linear-gradient(90deg, red 25.00%, lime 75.00%)"); + // Without this the gradient repeats under the border, painting the end + // colour along the leading edge and the start colour along the trailing one. + expect(swatch.style.backgroundOrigin).toBe("border-box"); + }); + + it("shows a plain swatch when the whole selection is one colour", () => { + const { element, session, iframe } = scene('Hello world'); + const { host } = render(session, iframe); + + selectAll(element); + + const swatch = toolbarIn(host)!.querySelector("span[aria-hidden]")!; + expect(swatch.style.backgroundColor).toBe("red"); + }); +}); + +describe("swatchBackground", () => { + it("blends every colour in the selection, weighted by how much text carries it", () => { + expect( + swatchBackground( + [ + { value: "red", chars: 5 }, + { value: "lime", chars: 15 }, + ], + undefined, + ), + ).toBe("linear-gradient(90deg, red 12.50%, lime 62.50%)"); + }); + + it("stays a plain swatch when the selection is one colour", () => { + expect(swatchBackground([{ value: "red", chars: 5 }], "red")).toBe("red"); + }); + + it("falls back to the agreed colour when the characters carry none", () => { + expect(swatchBackground([], "rgb(1, 2, 3)")).toBe("rgb(1, 2, 3)"); + expect(swatchBackground([], undefined)).toBe("#ffffff"); + }); }); diff --git a/packages/studio/src/components/editor/InlineTextToolbar.tsx b/packages/studio/src/components/editor/InlineTextToolbar.tsx index 8ecc2b43f7..04f5e9283e 100644 --- a/packages/studio/src/components/editor/InlineTextToolbar.tsx +++ b/packages/studio/src/components/editor/InlineTextToolbar.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { applyInlineStyle, readInlineStyle } from "./inlineTextStyleRange"; +import { applyInlineStyle, readInlineStyle, readInlineStyleSpread } from "./inlineTextStyleRange"; import type { InlineTextEditSession } from "../../hooks/useInlineTextEdit"; /** @@ -24,6 +24,7 @@ interface ToolbarPlacement { left: number; top: number; styles: Record; + colours: Array<{ value: string; chars: number }>; } export function InlineTextToolbar({ @@ -90,7 +91,15 @@ export function InlineTextToolbar({