#Modular Project Structure in Typst with Context-Aware Functions

1 messages · Page 1 of 1 (latest)

mint hull
#

Hi!

It's really hard to summarize what I want in one sentence, so bear with me please ( :

I'd like to have the project structure like this:

main.typ
notes/
├── lec1.typ
└── lec2.typ

And imageine we have a magic funtion #f().
Here's semi-pseudo-code of the desired result I want:

// def. of f:
#let f() = {
    if /*inside main.typ*/ {
        // do it this way
        <root>
    } else if /*inside lecN.typ*/ {
        // display it differently
        <in_sub_file>
    }
}

// code of lec1.typ:
#f()

// render of lec1.typ:
<in_sub_file>

// code of main.typ
#include "notes/lec1.typ"

// render of main.typ
<root>

How do I implement that?
With .with?

I imagine we could (if #set was available for user-defined functions), do it like this:

// def of f somewhere
#let f(magic: none) = {...}

// main.typ
#set f(magic: "root")

#include /**/

// lec1.typ
#set f(magic: "sub_file")

Maybe realistically it might be possible with some fancy show rule inside main.typ .... idk
Any help is highly appreciated

mystic girder
#

options:

  1. .with at the top of each file
  2. use a global let st = state("current-file", "main.typ") and place a st.update("current file") at the top of each relevant file, though you'd need to reset the value of st afterwards (a custom set rule would automatically be reset so it would indeed be nice, sadly that doesnt exist yet)
mystic girder
#

.with just adds another argument to every call afterwards, so you can have a first argument which is "current file", and pre-apply it on every file

mint hull
#

lib.typ:

#let note(location: none) = {
  "I'm inside " + location
}

lec1.typ

#import "lib.typ": note
#let note = note.with(location: "subfile")

#note()

which renders to I'm inside subfile

main.typ

#import "lib.typ": note
#let note = note.with(location: "root")

#include "lec1.typ"

which also renders to I'm inside subfile

mystic girder
#

yeah

#

is that not what you're looking for?

mint hull
#

nah

#

the second time it should be «I'm inside root»

mystic girder
#

then you're not checking the file you're in, rather you're trying to check the file being compiled

mint hull
#

yep ig

mystic girder
#

in that case use a state

mint hull
#

lemme try
one moment

mystic girder
#

ok

#

but note that you'll have to do something like (on subfiles)

st.update(value => if value != none { value /* someone is including this file */ } else { "this file" })

#

writing that every time would be annoying so i suggest encapsulating that into a function like #set-file("this file.typ")

mint hull
# mystic girder but note that you'll have to do something like (on subfiles) `st.update(value =...

wait, what's that for
fairly new; don't get it

currently I wrote like this and it seems to work:

// lib.typ:
#let f() = {
  "I'm inside " + context state("current-file").get()
}

// code of main.typ
#import "lib.typ": f
#state("current-file").update("root")
#f()

// render of main.typ
"I'm inside root"

// code of lec1.typ
#import "lib.typ": f
#state("current-file").update("sub")
#f()

// render of lec1.typ
"I'm inside sub"
#

i.e. why do we need to check for none

mystic girder
#

include lec1.typ in the main file and it will become apparent

mint hull
#

damn xd
overlooked that

#

and so at the end of each file I just set it to none, right?

mystic girder
#

i mean

#

no

#

the idea of the none check is to preserve the value set by the file including it

#

so if it's none it means that we arent being included cuz nobody changed the value yet (it starts as none, or whatever you give as initial value, so you'd check for that instead if you change it)

#

so we change it to ours

#

otherwise keep it

mint hull
#

thank you so much!! 💚