Add a module API for overriding the composer preview. (#33978)

* Modify LinkPreview to export shared atomics

* Implement MessageComposerUrlPreviewView

* Create UrlPreviewFetcher utility function

* Modify view models

* Implement in composer

* Support running tests in dom-less vitest environment

* Add a playwright test

* hide another one

* fmt

* cleanup

* test rte too

* fixup

* Add back docstring

* cleanup

* off by one

* remove description check

* Cleanup hacks

* Remove another hack

* cleanup

* one more

* fixup window here too

* whoops type

* Rename to be cleaeer

* Trim URLs first

* fix bug

* Add a module API for overriding the composer preview.

* tests + cleanup

* fixup comment

* revert accidental change

* fixup

* happy tests

* cleanup
This commit is contained in:
Will Hunt
2026-07-06 12:32:07 +00:00
committed by GitHub
parent 6027ec5142
commit b6cbc3a9d8
5 changed files with 246 additions and 16 deletions
@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import type { JSX, ReactNode } from "react";
import type { MatrixEvent } from "../models/event";
import type { AccountAuthInfo } from "./auth.ts";
import { ComposerApiTarget } from "./composer.ts";
/**
* Properties for all message components.
@@ -157,6 +158,38 @@ export type ExtendablePropsRenderFunction<BaseProps> = <P extends BaseProps>(
*/
export type CustomLoginRenderFunction = ExtendablePropsRenderFunction<CustomLoginComponentProps>;
/**
* Properties for composer preview.
* @alpha Subject to change.
*/
export type CustomComposerPreviewComponentProps = {
/**
* The plain text currently inside the composer.
*/
text: string;
/**
* The ID of the room currently in focus.
*/
roomId: string;
/**
* Details about the target of the composer.
*/
target?: ComposerApiTarget;
/**
* Details about what relation the user may be responding to.
*/
relation?: {
inReplyToEventId?: string;
relType?: string;
};
};
/**
* Function used to render a composer preview.
* @alpha Unlikely to change
*/
export type CustomComposerPreviewRenderFunction = ExtendablePropsRenderFunction<CustomComposerPreviewComponentProps>;
/**
* API for inserting custom components into Element.
* @alpha Subject to change.
@@ -224,4 +257,29 @@ export interface CustomComponentsApi {
* ```
*/
registerLoginComponent(renderer: CustomLoginRenderFunction): void;
/**
* Register a renderer for replacing the preview above the message composer.
*
* The render function should return a rendered component.
*
* Multiple render function may be registered, however the first matching result will be used.
* If no events match or are registered then the originalComponent is rendered.
*
* @param filterFn - A filter function to determine if this renderer should be used.
* @param renderer - The render function.
* @example
* ```
* customComponents.registerComposerPreview(
* (composerText) => composerText === "hello!",
* (props, originalComponent) => {
* return <b>You've said hello!</b>;
* }
* );
* ```
*/
registerComposerPreview(
filterFn: (composerText: string, roomId: string) => boolean,
renderer: CustomComposerPreviewRenderFunction,
): void;
}