Skip to content

EEx Engine and custom protocol#7

Draft
LostKobrakai wants to merge 6 commits intoHermanverschooten:mainfrom
LostKobrakai:bm/protocol
Draft

EEx Engine and custom protocol#7
LostKobrakai wants to merge 6 commits intoHermanverschooten:mainfrom
LostKobrakai:bm/protocol

Conversation

@LostKobrakai
Copy link

@LostKobrakai LostKobrakai commented Sep 15, 2025

Instead of using String.Chars – which is already implemented for many primitive datatypes, but in ways not specific to typst – use a custom protocol to transform to related datastructures in typst.

This currently goes to_string directly. Not sure if it would be useful to decouple eex and encoding like Phoenix.HTML.Safe does, where eex returns "safe iodata" and there's another step to turn that into a string. Probably good to skip for now.

What do you think?

@gworkman
Copy link
Contributor

I really like this - it would have saved me a lot of headache when designing the name badge screens 😅 Another thought that I had was to use a modified version of the sigil_H macro from Phoenix.Component, and create a sigil_TYPST:

  defmacro sigil_TYPST({:<<>>, meta, [expr]}, modifiers)
           when modifiers == [] or modifiers == ~c"noformat" do
           
    if not Macro.Env.has_var?(__CALLER__, {:assigns, nil}) do
      raise "~TYPST requires a variable named \"assigns\" to exist and be set to a map"
    end

    options = [
      engine: Typst.Engine,
      file: __CALLER__.file,
      line: __CALLER__.line + 1,
      caller: __CALLER__,
      indentation: meta[:indentation] || 0,
      source: expr,
    ]

    EEx.compile_string(expr, options)
  end

this enables defining functions very similar to LiveView:

defmodule SomeModule do
  import Typst, only: [sigil_TYPST: 2]

  def render_typst_markdown(assigns) do
    ~TYPST"""
    #text(<%= @text_props %>)[<%= @text_content %>]
    """
  end
end

SomeModule.render_typst_markdown(%{text_props: %{size: "16pt", font: "Helvetica"}, text_content: "Hello World"})

I was testing this with EEx.SmartEngine which has a couple of nice-to-haves, such as the @my_assign syntax for pulling values from the assigns map (as shown in the example above). I think the current implementation of Typst.Engine wouldn't allow for that - but if this is something we include in the library, maybe it's something we can also bring in support for?

@LostKobrakai
Copy link
Author

Ah, I missed handling the assigns (really the only additional thing SmartEngine does). That's something, which surely should be included, shouldn't be hard at all. A sigil would surely have been the next step..

@gworkman
Copy link
Contributor

gworkman commented Sep 18, 2025

@LostKobrakai yeah I think the assigns would be nice. Using the custom engine is also good, because eventually maybe we can get the engine to check if the syntax is valid, similar to HEEx. But that's probably a ways off 😄

@LostKobrakai
Copy link
Author

This needs some more work still. I noticed that there really need to be at least two protocols. One for the markup context and one for the code context (skipping math for now), given they both encode data differently. I might need something like {:safe, …} use by Phoenix.HTML.Engine / Phoenix.HTML.Safe, so both can make use of the results of each other. There's a whole lot more than previously as well though. Support for assign, multiple eex tags used for the different protocols. Just not done yet and still need to properly port the table struct over.

@kevinschweikert
Copy link
Contributor

@LostKobrakai let me know if you need help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants