Your extension can capture pasting URLs from the clipboard to produce a custom block with embedded data. Your extension will register a domain pattern and a callback block that will be called automatically when the user pastes a matching URL onto the canvas.

From the URL you can return an UnfurlDetails object which will describe the content of the newly created block.
Lucid will take that information and populate a new block on the canvas. You can provide an optional async followup to perform additional computation and update the block as needed.

Created blocks can have one or multiple preview images that a user can browse through.

Example embed menu

You can also provide an iframe for a user to view expanded content.

Example Iframe

Listen for a URL by calling registerUnfurlHandler:

import EditorClient from 'lucid-extension-sdk';

const client = new EditorClient();

function performUnfurl(url: string): Promise<UnfurlDetails | undefined> {
    const idRegex = /^https:\/\/my\.url\.com\/\w+\/d\/([a-zA-Z0-9-_]+)/;
    const id = idRegex.exec(url)?.[1]
    if (id) {
        try {
            return {
                providerName: 'myUrlCompany',
                providerFaviconUrl: 'my.url.com/logo',
                unfurlTitle: 'Block title',
                previewImageUrl: 'my.url.com/preview?url=' + url,
            };
        } catch (error) {
            console.log(error);
        }
    }
    return undefined;
}

function performAfterUnfurlCallback(blockProxy: LinkUnfurlBlockProxy, url: string) {
    const [fileKey, frameId] = parseLink(url); // for example

    if (!fileKey || !frameId) {
        return undefined;
    }

    const nameOfFrame = await getNameOfFrame(fileKey, frameId); // for example
    if (nameOfFrame) {
        blockProxy.setTitle(nameOfFrame);
    }

    blockProxy.setIframe({
        iframeUrl: 'https://www.my.url.com/embed?embed_host=astra&url=' + url,

        aspectRatio: UnfurlIframeAspectRatio.Square,
    });
}

client.registerUnfurlHandler('my.url.com', {
    unfurlCallback: async (url: string) => performUnfurl(url),
    afterUnfurlCallback: async (blockProxy, url) => performAfterUnfurlCallback(blockProxy, url),
});

Domain URL

The domain is required and will be checked against any URLs pasted to the canvas. If the domain matches, your callback will be run. If it doesn't match, then other unfurl handlers will be called so multiple extensions can be running and watching for URLs at the same time. Domains of multiple extensions cannot overlap.

The domain supports subdomains and wildcards in the subdomains. For example:

domain.com
my.domain.com
*.domain.com

We do not support anything after the domain or wildcards in the domain itself. So we DO NOT support domain.com/something or d*main.com

Unfurl callback

unfurlCallback is a required parameter that will be called with the pasted url. Your block should return an UnfurlDetails object to produce a block or undefined to do nothing. This call must run quickly in case any long computations need to be run in the followup async call. The purpose is to get the minimal information to display the new shape.

After unfurl callback

If you have additional, longer work that you need to do for an unfurl you may provide an asynchronous callback named afterUnfurlCallback that will run after the block is generated on the canvas. You will get a LinkUnfurlBlockProxy which will let you update the title, thumbnail and preview URLs, and the details iframe.

Importing links

Links can be imported on to a page as link unfurl blocks.

This can be done by using the importLinks method of the PageProxy class:

const client = new EditorClient();
const linksToImport = ["https://some.link.com", "https://some.other.link.com"];
new Viewport(this.client).getCurrentPage()?.importLinks(linksToImport);

Links will be unfurled by Lucid based on extensions installed by the user.

Expand callback

If you want to customize the experience users get when they expand links they've added to the document, you may provide an asynchronous callback named expandCallback that runs when the expand button for the unfurled block is pressed. The callback lets you set/check/update the iframe before expanding.