#Single cell transformation in table
155 messages · Page 1 of 1 (latest)
?r theme=light
#show table.cell: it => {
if it.x == 1 or it.y == 3 {
show regex("^(7(\.0|(\.[0-9]))?|8(\.[0-8]|\.9)?\sHIGH)$"): it => {
highlight(fill: red)[#it]
}
it
} else {
it
}
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], [7.8 HIGH],
)
thx in advance 
unfortunately you might be falling victim to this bug: https://github.com/typst/typst/issues/86
when you have special characters, including spaces, your text is split into multiple elements (text or not) by Typst, while show rules on text (and regex) can only match on a single text element
so im not sure if you will be able to do that
if you have the original text as a string, i.e. "7.8 HIGH" , that's potentially doable, however
so depends on your context, on where you're getting this table data from
i do
it's hand-made
then, assuming you have your data in a dictionary or array or .json file or smth, you can go through it and manually convert the strings to content , applying that rule as applicable
oh the data is directly on the table itself
but i could create a dict or json if needed
well, if you want to batch-apply it, you will have to use strings stored somewhere else
due to that limitation
otherwise you'd just manually highlight on the table itself
so the idea is to store it in a dict or json and while we unpack it / process it, if it matches 7.8 HIGH" convert it into #highlight(fill: red)[7.8 HIGH], generate a new dict that's the result of this process and unpack it into the table?
yep
if it can be inside a larger string, you can use regex to split the string into matching parts
and then you convert matching individual parts into highlight(stuff), the rest is just directly translated to content
though
it appears theres a workaround you can try
?r
#show "a. * b": "c"
#"a. * b"
simply writing strings instead of content seems to be enough to make the show rules work
so you can try that instead
im new to that how would it apply to the above?
well
im presenting a separate idea
unrelated to the dict or json idea
this separate idea would work with your original solution
?r theme=light
#show table.cell: it => {
show "7.8 HIGH": it => {
highlight(fill: red)[#it]
}
it
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], "7.8 HIGH",
)
i think your regex is wrong as it wasnt matching
either way i added the literal string as a PoC
hmm
actually
it works either way lol, kinda funny
?r theme=light
#show table.cell: it => {
show regex("7\\.8 HIGH"): it => {
highlight(fill: red)[#it]
}
it
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], [7.8 HIGH],
)
though the problem does manifest itself for certain characters:
?r theme=light
#show table.cell: it => {
show regex("7\\.8\\* HIGH"): it => {
highlight(fill: red)[#it]
}
it
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], [7.8\* HIGH],
)
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], "7.8* HIGH",
)
so it's best to use strings where possible if you're gonna be using show rules on text
you're right it's a regex issue not a typst issue
damn i skill issued myself again
im trying to match a range of nums maybe typst regex doesn't support extended
it does
i suggest debugging here: https://regex101.com/
in particular it seems you match either 7.(0|1|...|9) (theres a redundant \.0 there btw), or 8.(0|1|...|9) (note that the [0-8] | 9 there could be merged into just [0-9]) - or just 8 - followed by HIGH
also note that [0-9] can be written as \d (Actually \\d since you're writing a string literal where backslashes are syntactically relevant)
in particular 7.8 HIGH doesnt match , but 8.8 HIGH does
i'd thus probably write it as regex("[78](?:\\.\\d)?\\s+HIGH")
without ^ $ as well to avoid potential problems
thx kindly! i got it to where i wanted and turned it into a more sophisticated highlighter! 
?r theme=light
#show table.cell: it => {
let none_ = regex("^0\.0 NONE$") // 0.0
let low = regex("^(0\.[1-9]|[1-2]\.[0-9]|3\.[0-9]) LOW$") // 0.1-3.9
let medium = regex("^(4\.[0-9]|5\.[0-9]|6\.[0-9]) MEDIUM$") // 4.0-6.9
let high = regex("^(7\.[0-9]|8\.[0-9]) HIGH$") // 7.0-8.9
let critical = regex("^(9\.[0-9]|10\.0) CRITICAL$") // 9.0-10.0
if it.x == 1 {
show none_ : it => {}
show low : it => highlight(fill: yellow)[#it]
show medium : it => highlight(fill: orange)[#it]
show high : it => highlight(fill: red)[#it]
show critical : it => highlight(fill: black)[#text(fill: white)[#it]]
it
} else {
it
}
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], "7.8 HIGH",
[CVSS], "6.2 MEDIUM",
[CVSS], "3.0 LOW",
[CVSS], "9.5 CRITICAL"
)
typst is so mighty and formidable 
crazy idea why not just make a CVSS function instead of regex?
?r theme=light
#show table.cell: it => {
let none_ = regex("^0\.0 NONE$") // 0.0
let low = regex("^(0\.[1-9]|[1-2]\.[0-9]|3\.[0-9]) LOW$") // 0.1-3.9
let medium = regex("^(4\.[0-9]|5\.[0-9]|6\.[0-9]) MEDIUM$") // 4.0-6.9
let high = regex("^(7\.[0-9]|8\.[0-9]) HIGH$") // 7.0-8.9
let critical = regex("^(9\.[0-9]|10\.0) CRITICAL$") // 9.0-10.0
if it.x == 1 {
show none_ : none
show low : highlight.with(fill: yellow)
show medium : highlight.with(fill: orange)
show high : highlight.with(fill: red)
show critical : highlight.with(fill: black)
show critical : set text(white)
it
} else {
it
}
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], "7.8 HIGH",
[CVSS], "6.2 MEDIUM",
[CVSS], "3.0 LOW",
[CVSS], "9.5 CRITICAL"
)
maybe a lil' fancier
but yea I agree
writing CVSS(7.8), CVSS(6.2) etc. would likely be more appropriate
it'd then generate the label and the highlighting for you automatically
could also map high medium etc
yea thats what i mean by label
like this?
no
first, set is used to set properties on existing elements
so you'd write set highlight(fill: black) (no with)
but still, theres no highlight there for that to apply to
that'd work with #show something: highlight.with(...) to wrap something in a new highlight element
however those set rules are not applying to anything, since they apply only to things in their scope, and the scope there is
{ set ... }
so they dont have any effect in practice
you'd have to return num on each branch, in theory
but actually dont return bcuz otherwise you're making the show rules useless as well
to elaborate on that: note that
{ let x = 5 (x + 2,) (2,) (3,) }
is the same as (let x = 5) + (5 + 2,) + (2,) + (3,) (and assignments always evaluate to none), so you get (7, 2, 3)
but if you write
{ (2,) (3,) return 10 }
you get just 10
and the way show rules, counter().update(...), state().update(...), and other stuff works is that what you return is not just the value you actually return, but the value joined with all those other things
so basically dont return if you want to apply "side effects " to things
in fact you will almost never want to return so when in doubt just dont
now, with that out of the way
the solution is
// note that Typst doesn't have type annotations,
// you were setting the default value for a named arg
#let CVSS(num) = {
let (highlight-fill, text-fill) = if num >= 0.1 and num <= 3.9 {
(rgb("E5C639"), auto)
} else if num >= 4.0 and num <= 6.9 {
(rgb("E59C39", auto)
} ... { // omitted for brevity
} else if num >= 9.0 and num <= 10.0 {
(black, white)
} else {
(none, auto)
}
let highlighted = if highlight-fill != none { highlight(fill: highlighted-fill)[#num] } else [#num]
let with-text-fill = if text-fill != auto { text(text-fill, highlighted) } else { highlighted }
with-text-fill
}
?r theme=light
#let CVSS(num, str) = {
if num >= 0.1 and num <= 3.9 {
highlight(fill: rgb("E5C639"))[#num #str]
} else if num >= 4.0 and num <= 6.9 {
highlight(fill: rgb("E59C39"))[#num #str]
} else if num >= 7.0 and num <= 8.9 {
highlight(fill: rgb("E5394B"))[#num #str]
} else if num >= 9.0 and num <= 10.0 {
highlight(fill: black)[#text(fill: white)[#num #str]]
} else {
num
}
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], [#CVSS(7.8, "HIGH")],
[CVSS], [#CVSS(6.2, "MEDIUM")],
[CVSS], [#CVSS(3.0, "LOW")],
[CVSS], [#CVSS(9.5, "CRITICAL")]
)
i was doing this ❤️
yea you're right thx for all the above i'll try to be mindful on the effects that and idiomatic typst is what im struggling w/ the most but little by little 
yea dont worry
just wanted to give you all the tips upfront so you'll slowly get used to how it works
also if there's anything more interesting to answer or support pls go for it haha i've asked a lot already
typst's code mode indeed has a few differences from other programming languages
dw
i'll try on my own to change the highlight with round corners with a box function have a great one!
should be as easy as defining something like
#let linebox(color, body) = box(
fill: color,
width: 5em,
outset: 1pt,
inset: 1pt,
radius: 2pt,
body
)
looking good
you can remove the [ ] and #, looks cleaner
btw maybe ur interested in this: https://github.com/miliog/typst-penreport
but I don't work on it anymore
I wrote this shortly after getting to know about Typst
thx u rock!
that's so cool actually!
im going for something kinda fancier but i'd love to share the template when i have it polished ❤️
im not very smart w/ code or with security kinda starting but i love typography books 💜
The Elements of Typographic Style by Bringhurst, Tufte's work and Butterick's Practical Typography are excellent resources that got me started into this world
i could give a lot of recommendations if needed
I already have something much fancier but can't share it anymore^^ but maybe you could even extract some of the stuff into its own library
it was more like a proof of concept that it's better than latex :D some of the things I've done there I wouldn't do today, like how I used query
it's inspiring still
i've done a lot of latex and latex3 specifically ❤️
feel free to PM or add to friends list btw, any of u
and thx again for all the help 
writing a complex template in latex is hard xD and at some point it's not fun anymore and takes a long time to compile if u add some logic to it
https://github.com/miliog/typst-penreport/blob/master/typst-penreport/content/summary_of_findings.typ
e.g. for this you would have to write to an aux file and it takes several compilation rounds
maybe with latex3 it's easier dunno, but still slow and unreadable to me, so I never bothered :D
yes typst is so cozy!
i got it to adapt a bit to the width of its content with
#let linebox(color, body) = context {
box(
fill : color,
width : measure(body).width,
outset: 1pt,
inset : 1pt,
radius: 2pt,
body
)
}
but it's too chomky
maybe manually fine-tuning the relative width with measure(body).width / 0.95?
if there's a better way lmk ❤️
You can just omit the width
aaaa thx, overcomplicating things again 😹
I—not entirely rationally—still miss parenthesis/brace-less syntax à la \seq_item:Nn\l__var_seq1.
i feel the same sometimes!
I would feel partly indemnified if if … {} <else {}> would have a syntax with a braceless variëty, since there's also no Ternary Conditional Operator.
?r theme=light
#let linebox(color, body) = box(
fill : color,
outset: 1pt,
inset : 1pt,
radius: 2pt,
body
)
#let CVSS(num, str) = {
(
(num >= 0.1 and num <= 3.9,
linebox(rgb("E5C639"))[#num #str]),
(num >= 4.0 and num <= 6.9,
linebox(rgb("E59C39"))[#num #str]),
(num >= 7.0 and num <= 8.9,
linebox(rgb("E5394B"))[#num #str]),
(num >= 9.0 and num <= 10.0,
linebox(rgb("000000"))[#text(fill: rgb("FFFFFF"))[#num #str]]),
(true, num)
).find(t => t.at(0)).at(1)
}
#table(
columns: 2,
[Name], [Name Text],
[Id.], [ID],
[CVSS], [#CVSS(7.8, "HIGH")],
[CVSS], [#CVSS(6.2, "MEDIUM")],
[CVSS], [#CVSS(3.0, "LOW")],
[CVSS], [#CVSS(9.5, "CRITICAL")]
)
for lack of a match/case 😅
thx a lot @gray mirage, @scenic cove and @fading dew u guys are helping me write cleaner code ✨
Once wrote an abstraction for that kind of switch imitation.
it's in typst#1316
Description I would like to request a match/switch statement feature for Typst. This would allow me to write conditional expressions that match a value against a set of patterns and execute differe...
?r
[0m[30m/// Return a value corresp. to the first case with a head (i.e. `haulm`) equal to `needle`[0m
[0m[30m/// - cb-args (none, arguments, array):[0m
[0m[30m/// / … (none):[0m
[0m[30m/// So that the value \@ `cb` is directly taken and not its return value[0m
[0m[30m/// / … (…):[0m
[0m[30m/// To get passed to the `cb` corresponding to the match[0m
[0m[30m/// - needle (any, -pos):[0m
[0m[30m/// To be tested for equality with `haulm`s[0m
[0m[30m/// - ..cases (array, -vic):[0m
[0m[30m/// _Elements:_[0m
[0m[30m/// / haulm (any):[0m
[0m[30m/// The basis on which the corresponding value gets selected[0m
[0m[30m/// / cb (function, type, any, -opt):[0m
[0m[30m/// When not given, the the current `case` gets or-combinated with the following,[0m
[0m[30m/// so that the `cb` of the last of them gets used,[0m
[0m[30m/// if itself or one of the cases without before matches.[0m
[0m[30m///[0m
[0m[30m/// -> any[0m
[0m[30m/// === →[0m
[0m[30m/// The corresp. `cb` respectively its result[0m
[0m[31m#[0m[35mlet[0m switch(cb-args: (), needle, ..cases) = {
[0m[35mlet[0m mat = cases.pos().position(
case=> needle == case.first(),
)
[0m[35mlet[0m cb = [0m[35mif[0m [0m[35mnone[0m != mat {
cases.pos().slice(mat).find(
((_, ..tail))=> () != tail,
).last()
}
[0m[35mif[0m [0m[35mnone[0m != cb-args [0m[35mand[0m (function, type).contains(type(cb)) {
cb(..cb-args)
} [0m[35melse[0m {cb}
}
[0m[31m#[0m[3m[34mswitch[0m(
cb-args: ([0m[33m2[0m,),
[0m[32m"hey"[0m,
([0m[32m"bye"[0m, calc.odd),
([0m[32m"hi"[0m),
([0m[32m"hey"[0m, calc.even),
) [0m[36m=[0m [0m[35mtrue[0m
Why doesn't (After reinstalling the same revision, everything's as expected again. 🤔)typst-ansi-hl work any longer?
Basically, but with some bells & whistles.
Have been showcasing it's features, but I feel like it verily needs a default: pendant.
?r
[0m[31m#[0m[35mimport[0m calc: *
[0m[31m#[0m[35mlet[0m [0m[3m[34mswitch[0m(cb-args: (), default: {}, needle, [0m[36m..[0margs) [0m[36m=[0m {
[0m[35mlet[0m call-types [0m[36m=[0m (function, type)
[0m[35mlet[0m cases [0m[36m=[0m args.[0m[3m[34mpos[0m()
[0m[35mlet[0m mat [0m[36m=[0m cases.[0m[3m[34mposition[0m(
case[0m[36m=>[0m needle [0m[36m==[0m case.[0m[3m[34mfirst[0m(),
)
[0m[35mlet[0m cb [0m[36m=[0m [0m[35mif[0m {} [0m[36m==[0m mat {default} [0m[35melse[0m {
cases.[0m[3m[34mslice[0m(mat).[0m[3m[34mfind[0m(
((_, [0m[36m..[0mtail))[0m[36m=>[0m () [0m[36m!=[0m tail,
).[0m[3m[34mlast[0m()
}
[0m[30m// `none` → opt-out[0m
[0m[35mif[0m {} [0m[36m!=[0m cb-args [0m[35mand[0m call-types.[0m[3m[34mcontains[0m([0m[3m[34mtype[0m(cb)) {
[0m[3m[34mcb[0m([0m[36m..[0mcb-args)
} [0m[35melse[0m {cb}
}
[0m[31m#[0m[3m[34mswitch[0m(
cb-args: ([0m[33m2[0m,),
default: [0m[35mfalse[0m,
[0m[32m"swag"[0m,
([0m[32m"bye"[0m, odd),
([0m[32m"hi"[0m),
([0m[32m"hey"[0m, even),
)
/// Return a value corresp. to the first case with a head (i.e. `haulm`) equal to `needle`
/// - cb-args (none, arguments, array):
/// / … (none):
/// So that the value \@ `cb` is directly taken and not its return value
/// / … (…):
/// To get passed to the `cb` corresponding to the match
/// - default (any):
/// To become the `cb` if no `haulm` matches
/// - needle (any, -pos):
/// To be tested for equality with `haulm`s
/// - ..cases (array, -vic):
/// _Elements:_
/// / haulm (any):
/// The basis on which the corresponding value gets selected
/// / cb (function, type, any, -opt):
/// When not given, the the current `case` gets or-combinated with the following,
/// so that the `cb` of the last of them gets used,
/// if itself or one of the cases without before matches.
///
/// -> any
/// === →
/// The corresp. `cb` respectively its result
ooooo fancy, tight syntax
hey low how do you make it so discord syntax highlights typst's code blocks?
#off-topic message
aaa so interesting
im using .at() to extract the 3.0 info from str, open to suggestions if there's a cleaner way:
?r theme=light
#let str = "CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H/E:P/RL:W/RC:C"
#let url = "https://www.first.org/cvss/calculator/"
#let sub = {str.at(5); str.at(6); str.at(7); "#"}
#let all = {url; sub; str}
#set text(6pt)
#link(all)
You can use regex in Typst, too. A more reliable approach would be using the pattern definitions in the JSON schemas found on https://www.first.org/cvss/data-representations to determine what version something belongs to
?r
#let cvss40 = regex("^CVSS:4[.]0\/AV:[NALP]\/AC:[LH]\/AT:[NP]\/PR:[NLH]\/UI:[NPA]\/VC:[HLN]\/VI:[HLN]\/VA:[HLN]\/SC:[HLN]\/SI:[HLN]\/SA:[HLN](\/E:[XAPU])?(\/CR:[XHML])?(\/IR:[XHML])?(\/AR:[XHML])?(\/MAV:[XNALP])?(\/MAC:[XLH])?(\/MAT:[XNP])?(\/MPR:[XNLH])?(\/MUI:[XNPA])?(\/MVC:[XNLH])?(\/MVI:[XNLH])?(\/MVA:[XNLH])?(\/MSC:[XNLH])?(\/MSI:[XNLHS])?(\/MSA:[XNLHS])?(\/S:[XNP])?(\/AU:[XNY])?(\/R:[XAUI])?(\/V:[XDC])?(\/RE:[XLMH])?(\/U:(X|Clear|Green|Amber|Red))?$")
#let cvss31 = regex("^CVSS:3[.]1/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$")
#let cvss30 = regex("^CVSS:3[.]0/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$")
#let cvss20 = regex("^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$")
#let vs = "CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H/E:P/RL:W/RC:C"
#vs.matches(cvss20).len()
#vs.matches(cvss30).len()
#vs.matches(cvss31).len()
#vs.matches(cvss40).len()