satori icon indicating copy to clipboard operation
satori copied to clipboard

Find a way to inline options into the markup

Open tolmasky opened this issue 2 years ago • 3 comments

It would be nice to be able to (optionally) place as much of the options object into the actual markup, that way functions could just return markup instead of markup/options tuples.

For example, we could piggyback off of the meta viewport tag to inline the width and height of the resulting image:

<div>
    <meta name = "viewport" content = "width=800, height=400" />
</div>

This allows is the equivalent of passing in { width:800, height:400 }.

Fonts would be harder, but arguably even nicer to support from a usability perspective, so that we can remove the file reading boilerplate:

<div>
<style>
@font-face {
  font-family: "my-font";
  src: url("../fonts/font.ttf")
}
</style>

Perhaps as an easier precursor step, just looking for a fontFace(s) entry in the root element's style object.

I've got a version of the width/height thign already working as a wrapper in our stuff, and am considering doing the fontFace thing as well. If these are things that you are open to taking into the main library I could PR them.

tolmasky avatar Feb 15 '23 13:02 tolmasky

Something else I've done for the width/height is to just key off of the root elements width and height. This can be done either before or after "collapsing" into the final components. That is to say, the simple way would be to just do:

const { width, height } = root.props.style;

The more advanced way is to do:

const { width, height } = collapse(root).props.style;

Such that if you pass in like <Whatever/> which eventually returns <div style = { { width: 100, height: 100 } } >, it will still work.

tolmasky avatar Feb 17 '23 14:02 tolmasky

We are already using the resolved container's size as the default image size, so that part works :)

As for fonts and potentially other things, may I ask that what's the intention of embedding all the configurations into the markup?

shuding avatar Apr 12 '23 00:04 shuding

The most practical application is what I mentioned above, that it would be nice to just be able to refer to fonts instead of having to manually put in the font-reading boilerplate at the call-site.

Generally speaking though, it also just makes it difficult to organize code well, for the same reason you wouldn't want to arbitrarily give internal responsibilities to the caller. The caller gets to treat every other aspect of the image a black box, but for some reason needs to have "special knowledge" of internally used fonts. this means if the fonts change, you may need to go edit them somewhere else:

function 
return await satori(<GenericSocialImage title = "blah" subtitle = "something-else" />,
{
     fonts: [
     {
        // I have to know that GenericSocialImage
        name: 'Roboto',
        data: robotoArrayBuffer,
        weight: 400,
        style: 'normal',
      },
    ],
});

This gets even more cumbersome with what we do where we can have routes just return JSX elements and have the surrounding framework handle the rendering/etc:

app.get("/social-image?", ({ params }) => <GenericSocialImage title = { params.title } />);

The router then special cases svg elements:

if (React. isValidElement(response) && collapsed(response).tag === "svg")
{
    response.send(await satori(response, /* oops, what goes here? */));
}

tolmasky avatar Apr 12 '23 00:04 tolmasky