typst

How to access the underlying value of state with correct type in Typst


I'm currently building a template with polylux where the user can define one of five color themes. As the chosen color theme is global state I used the state type. My colors are organized as a map and i want to use the chosen color scheme as a key for it, to get the correct colors. Howver I'm running into type issues as state stores everything as any.

A barebones example would be:

#let RedTheme = (light: rgb(255, 0, 0), dark: rgb(128, 0, 0))

#let GreenTheme = (light: rgb(0, 255, 0), dark: rgb(0, 128, 0))

#let AllThemes = (theme1: RedTheme, theme2: GreenTheme)

#let theme = state("theme", "theme1")

// sets up state
#let template(user-theme: "", body) = {
  theme.update(user-theme)

  body
}

#show: template.with(user-theme: "theme2")

// FIXME: this is broken
#let color = AllThemes.at(state("theme").display())

Whenever i try to do something like: AllColors.at(state("scheme").display()) i get an error essentielly telling me content cannot be used as string.

Am I missing some conversion function or is this even the right approach?


Solution

  • You must wrap the code into a locate so similar to extract the value of state.

    #let with-theme(body) = {
      locate(loc => {
        let theme-color = AllThemes.at(state("theme").at(loc))
        body(theme-color)
      })
    }
    
    #show: template.with(user-theme: "theme2")
    
    #with-theme(theme => {
      rect(width: 12pt, fill: theme.light)
    })
    

    But this inconvenience only happens when you want to use a state, of which main use case is counters. Otherwise just use a normal variable and you're just fine.

    // in template:
    #let get-theme(name) = AllThemes.at(name)
    
    // In main.typ:
    #let theme = get-theme("theme1")
    rect(width: 12pt, fill: theme.light)