Struggling with a transclusion from a state tiddler with a variable field name

Right now, your procedure applies selection to field <<advantage>> of <<currentTiddler>>.
State tiddler isn’t really useful in this case, and <$select> widget could work without any state tiddler like this:
<$select field=<<advantage>>>

Without state tiddler, no state tiddler conflict! :wink:

Fred

Mmmm :slight_smile: Thanks for the suggestion :slight_smile:

I have a problem though, I need that select to show the fields I indicated in the list filters, but add only the title field, not the modifier one, into the advantage field of the current Tiddler. So example in the case of

<$list filter='[tag[GURPS Modifier]search:to-advantages:<advantage>]'>
<option><$view field='title'/> (<$view field='modifier-value'/>%)</option></$list>

I need to list both the title and modifier-value in the dropdown of the select widget, but add only the title one to <<advantage>>; is there a way to do that?

Additionally, since an <<advantage>> can have multiple modifiers, I should actually list them (separated by a ", " because mod names can have spaces), and probably use action-listops instead of action-setfield. Finally, each modifier can have a level (zero by default, always positive), so to give out an example:

Let’s say I’m currently dealing with the <<advantage>> called Magery:

  • list the options as above, (in this example let’s say the options “Musical”, “One College Only” and “Solitary”), like this:

    • Musical (-50%)
    • One College Only (-40%)
    • Solitary (-40%)
  • create/modify the field called ‘Magery’ when an option is selected in the dropdown by the user, like this:

    • Magery: Musical 0, One College Only 0, Solitary 0

something like this? (doesn’t work, but that’s sort of what I’m at for now)

<$select field=<<advantage>> actions='<$action-listops $field=<<advantage>> $subfilter="+[toggle[]]"]/>'>

… bit out of my depth here :stuck_out_tongue_winking_eye:

For this you can use <option> html tag syntax:
<option value="Field value">Display label</option>

In your case, “Field value” is the result you want to store in <<advantage>> field, and “Display label” is the label displayed in the dropdown list. Both values should be computed with functions or filtered transclusions.

Fred

When user selects a value in the list, should the result be stored in <<advantage>> or be appended to current <<advantage>> value?

Fred

Appended, or removed if already there :slight_smile:

I tried to declare the value of the list items as a list variable named “modTitle” in my list widgets to be able to come up with the correct option value (and use a conditional to indicate whether it’s already in the field or not), like this:

\procedure select-mods(advantage, advbook)
<$select tiddler=<<advbook>> field=<<advantage>>><option disabled>Select modifier</option>
<optgroup label='Special'>
<$list filter='[tag[GURPS Modifier]search:to-advantages:<advantage>]' variable='modTitle'>
<option value=<<modTitle>>><<modTitle>> ({{{ [<modTitle>get[modifier-value]] }}}%)<% if [<advbook>search:<advantage>:<modTitle>] %>&nbsp;&#x2714;<% endif %></option></$list></optgroup>
<optgroup label='Global modifiers'>
<$list filter='[tag[GURPS Modifier]!has[to-advantages]] -[title[Modifier template]]' variable='modTitle'>
<option value=<<modTitle>>><<modTitle>> ({{{ [<modTitle>get[modifier-value]] }}}%)<% if [<advbook>search:<advantage>:<modTitle>] %>&nbsp;&#x2714;<% endif %></option></$list></optgroup></$select>
\end

Core functionality works now, but it’s still not an append/toggle, and I still need to get the conditionals working (I have no clue why they don’t ;P).

edit: tried that for the append/toggle, but it doesn’t work at all (field is emptied)

<option value={{{ [<advantage> +[toggle<modTitle>]] }}}><<modTitle>> ({{{ [<modTitle>get[modifier-value]] }}}%)<% if [<advbook>search:<advantage>:<modTitle>] %>&nbsp;&#x2714;<% endif %></option>

I suspect a problem in search operator syntax. An operator suffix can’t contain a variable part (here, <advantage>.
I failed to find an alternative filter for this conditional, hopefully someone with greater skills can help.
I got stuck because the compound data format you chose (storing multiple coma separated options along with their level in a single field) makes it difficult to deal with subvalues. At least too difficult for me :slightly_smiling_face:

Fred

A satisfying alternative to this whole UX could be:

  • dropdown <$selectwidget> listing the mods like before
  • add a button to add or remove what is selected, depending on whether or not it’s already in the field.

Would that help?

New failed attempt:

\procedure mod-select-actions()
<$action-listops $tiddler=<<book>> $field=<<advantage>> $subfilter="+[toggle[<modTitle>, ]]"/>
\end

\procedure select-modsB(advantage, book)
<$select tiddler=<<book>> field=<<advantage>>><option disabled>Select modifier</option>
<optgroup label='Special'>
<$list filter='[tag[GURPS Modifier]search:to-advantages:<advantage>]' variable='modTitle'>
<option value=<<mod-select-actions>>><<modTitle>> ({{{ [<modTitle>get[modifier-value]] }}}%)<% if [<advbook>search:<advantage>:<modTitle>] %>&nbsp;&#x2714;<% endif %></option></$list></optgroup>
<optgroup label='Global modifiers'>
<$list filter='[tag[GURPS Modifier]!has[to-advantages]] -[title[Modifier template]]' variable='modTitle'>
<option value=<<mod-select-actions>>><<modTitle>> ({{{ [<modTitle>get[modifier-value]] }}}%)<% if [<advbook>search:<advantage>:<modTitle>] %>&nbsp;&#x2714;<% endif %></option></$list></optgroup></$select>
\end

I haven’t read the thread exhaustively, but here are some suggestions based primarily on your most recent code.

\procedure mod-select-actions()
<$action-listops $tiddler=<<book>>
	$field=<<advantage>>
	$subfilter="+[toggle{$:/temp/generated-list-mod-state}]"/>
\end

\procedure mod-select-option()
<option value={{!!title}}>
{{!!title}} ({{{ [{!!title}get[modifier-value]] }}}%)
<$list filter=`[<advbook>search:$(advantage)${!!title}]`>&#x2714;</$list>
</option>
\end

\procedure select-modsB(advantage, book)
<$select tiddler="$:/temp/generated-list-mod-state"
	default=""
	actions=<<mod-select-actions>>
>
	<option value="" disabled>Select modifier</option>
	<optgroup label="Special">
		<$list filter="[tag[GURPS Modifier]search:to-advantages:<advantage>]">
			<<mod-select-option>>
		</$list>
	</optgroup>
	<optgroup label="Global modifiers">
		<$list filter="[tag[GURPS Modifier]!has[to-advantages]] -[[Modifier template]]">
			<<mod-select-option>>
		</$list>
	</optgroup>
</$select>
\end
  • There’s nothing wrong with specifying a modTitle variable if you find it easier to follow, but in this case, I don’t think you really need it: you can omit it and use the default <<currentTiddler>>/{{!!title}} instead.
  • You shouldn’t be using an action widget as the value of an option; instead, use the actions attribute of the select widget. <option> values are used to specify the string that is actually written to the specified tiddler/field (as contrasted with what that option displays. In your case, I believe you want that to be {{!!title}}, i.e. the current $list item (your original <<modTitle>>).
  • Since you’re using the same <option> format for both optgroup lists, I moved it to a procedure that can be invoked as a variable to make your code a little more concise.
  • As @tw-FRed pointed out, you can’t use a <variable> as an operator suffix (as you were trying to do with <advbook>search:<advantage>). However, if we change the <% if ... %> syntax to a $list, we can make use of substituted attribute values and write the variable as <advbook>search:$(advantage)$ instead. By surrounding the filter in backticks rather than quotes, we indicate that the $(variable)$ should be substituted before the filter is evaluated.
  • Finally, as Fred suggested, you’ll want to write the selected value to a secondary tiddler and then append the value of that tiddler to your <<advantage>> field. Otherwise, any existing contents of that field will be overwritten by your selection.
    • toggle is intended to work with a title list, not a list of comma-separated strings, so I’ve amended your $subfilter to reflect this. Generally speaking, I’d recommend using title lists in your fields as they’re more filter-friendly, and adding commas in as necessary in your display templates (e.g. {{{ [<currentTiddler>get<advantage>enlist-input[]join[, ]] }}}).

I hope this gets you moving in the right direction! Let me know if any of this needs further explanation.

2 Likes

Thanks again for this detailed explanation and pointing me to susbstitutions (didn’t know those at all).

However; the addition of the check marks :heavy_check_mark: after the options doesn’t seem to work.

Additionally, I do need the list to be formated with a comma and a space in between modifiers and their level, because as you’ve seen in the other thread you solved, that is the field I then manipulate to calculate the bonus/malus values of those modifiers depending on their level, and modifiers can have spaces in their names (which is handled by your current code but through adding double square brackets around those titles containing spaces). The default level when adding a new modifier would be 1. (My next problem to solve will be to be able to edit those levels by replacing them in the list with an <$edittext> widget, which I suspect will also be tricky for me but one step at a time)

In other words, the field should look like this:

<advantagename>: <modifier name 1> <level1>, <modifier name 2> <level2>, <modifier name n> <leveln>

This means that in the procedure mod-select-option() I need to change

<option value={{!!title}}>

to something like:

<option value=<modTitleAndLevel>>

where modTitleAndLevel could be defined elsewhere; and in mod-select-actions(), I need to account for the space character and the level in <modTitleAndLevel> to be removed as well when they’re in the field…

Sorry, an extra colon snuck into my code here:

<$list filter=`[<advbook>search:$(advantage)$:{!!title}]`>
                                             ^

Does it work if you take it out?

Hmm, okay, I’d try something like this:

\procedure mod-select-actions()
<$let
	currentTiddler=<<book>>
	current={{{ [<book>get<advantage>] }}}
	modTitleAndLevel=...
>
<!--  ^ However you want to derive this; you'll presumably need {$:/temp/generated-list-mod-state}, which stores the option selected by the $select widget -->
<% if [<current>search<modTitleAndLevel>] %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>search-replace<modTitleAndLevel>,[]search-replace:g[ ,],[]] }}} />
<% else %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>] [<modTitleAndLevel>] +[join[, ]] }}} />
<% endif %>
</$let>
\end

Since we’re not working with a title list, I’ve switched from $action-listops to $action-setfield, which requires us to retrieve the existing field value (stored here as <<current>>) and then modify that. toggle doesn’t work well in this scenario, since <<modTitleAndLevel>> might or might not need a comma after it; instead, we’ll need a separate action widget for each scenario.

  • If <<modTitleAndLevel>> is already present in the field…
    • replace <<modTitleAndLevel>> with nothing (i.e., delete it)
    • replace the comma left behind (now preceded by a space) with nothing, too.
  • Otherwise, append <<modTitleAndLevel>> to the existing field value, joining them with a comma + space.
  • You’ll see I also moved the tiddler=<<book>> attribute out of the $action-setfields and refined <<currentTiddler>> in the $let widget. Since the action widgets default to modifying <<currentTiddler>> if their tiddler attributes aren’t specified, this lets us remove a bit of redundancy.

No it doesn’t… I haven’t the faintest idea of why.

For the ModTitleAndLevel, I’m experiencing a bit of chicken and egg reasoning issue in my head, but, we basically need to look for the option selected (stored in $:/temp/generated-list-mod-state) followed by a space character and a number, in the current value of the field (so in your code, that would be <current>. I was tempted to do something along the lines of

<$list filter="[<current>split[, ]search::[[$:/temp/generated-list-mod-state]addsuffix[ #]]" variable=ModTitleAndLevel>

but as you can see in that line (which syntax I’m not even sure of), using search has a few issues:

  1. I don’t know how to add the “space and number” to each split result - but we would probably have to rely in part on regexp[\s\d+$] because we don’t know what the “#” number would be
  2. If the search fails to find what I’m looking for, which means it’s not in the , then I would want to add ModTitle 1

I’m a bit too tired to reason on this case I think. Maybe I’ll see things more clearly tomorrow…

I don’t have the broader context, but I do notice that you’ve got two similar variables here, <<book>> and <<advbook>>. Is it possible that <advbook> should be <book> instead?

If all newly added mods should start at 1, you could try something like this:

\procedure mod-select-actions()
<$let
	currentTiddler=<<book>>
	current={{{ [<book>get<advantage>] }}}
	selected={{$:/temp/generated-list-mod-state}}
	new=`$(selected)$ 1`
	existing=`$(selected)$ \d+(,\s?)?`
>
<!--  Use backtick substitution to define two permutations:
	<<new>>, for "ModTitle 1")
	<<existing>>, for ModTitle + any digit(s). This second variable defines a regexp string. -->
<% if [<current>search<selected>] %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>search-replace::regexp<existing>,[]] }}} />
<!--                                    ^ 2 colons! The first is for the flag-list, where we'd put g for global if we were using it; the second precedes regexp. -->
<% else %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>] [<new>] +[join[, ]] }}} />
<% endif %>
</$let>
\end

I’ve never actually tried to use backtick substitution to define a regex string this way, but conceptually I think it ought to work. I also moved the possible comma+space suffix into <<existing>>, since we’re using regex anyway; that lets us eliminate the second search-replace.

Get some sleep and let me know how it goes. :slight_smile:

Good catch, that was it :slight_smile:

That works!

Only two things to fix;

  1. test if the field is empty so we don’t start it with “, ” (that shouldn’t impede the system otherwise, but it tickles my OCD and would make the field a tiny bit less readable)
  2. reset the selected option to nothing once a change has happened (otherwise, you can’t remove what you just added or add what you just removed)

Which I did like so:

\procedure mod-select-actions()
<$let
	currentTiddler=<<book>>
	current={{{ [<book>get<advantage>] }}}
	selected={{$:/temp/generated-list-mod-state}}
	new=`$(selected)$ 1`
	existing=`$(selected)$ \d+(,\s?)?`
>
<!--  Use backtick substitution to define two permutations:
	<<new>>, for "ModTitle 1")
	<<existing>>, for ModTitle + any digit(s). This second variable defines a regexp string. -->
<% if [<book>!has<advantage>] %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<new>] }}} />
<$action-setfield
	$tiddler="$:/temp/generated-list-mod-state"
	$field=text
	$value="" />
</$let>
<% elseif [<current>search<selected>] %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>search-replace::regexp<existing>,[]] }}} />
<$action-setfield
	$tiddler="$:/temp/generated-list-mod-state"
	$field=text
	$value="" />
</$let>
<% else %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>] [<new>] +[join[, ]] }}} />
<$action-setfield
	$tiddler="$:/temp/generated-list-mod-state"
	$field=text
	$value="" />
</$let>
<% endif %>
\end

…I believe it all works correctly now :slight_smile: Thank you!

Quick question:
If I wanted to default the book parameter to the currentTiddler (i.e., here, the Tiddler where the procedure is called from) in the declaration of \procedure select-mods (advantage, book), how should I write it? I tried:

\procedure select-mods(advantage, book:<currentTiddler>)

and

\procedure select-mods(advantage, book:<<currentTiddler>>)

… seemingly to no avail?

The default value of a procedure parameter is expected to be a constant string (see Procedure Definition Syntax)

To assign a variable (i.e., <<currentTiddler>>) as a default value, you can use a $let widget, like this:

\procedure select-mods(advantage, book)
<$let book={{{ [<book>!match[]] ~[<currentTiddler>] }}}>

When a non-blank book value is passed into the procedure it is used as-is; however, if the passed in value is blank, then the <currentTiddler> value is assigned to book.

enjoy,
-e

1 Like

Good job!

A couple of suggestions to make your code more concise:

  1. Rather than adding a new <% if [<book>!has<advantage>] %> case to your conditional, replace $value={{{ [<current>] [<new>] +[join[, ]] }}} under the <% else %> case with $value={{{ [<current>!match[]] [<new>] +[join[, ]] }}}. join only applies when it receives at least 2 input values, so it won’t add the comma if [<new>] is the only input.

  2. Rather than repeating <$action-setfield $tiddler="$:/temp/generated-list-mod-state" $field=text $value="" /> for each case, move it outside the conditional. You want it to apply no matter what, after all!
    As a slightly shorter option, you could also use <$action-deletefield $tiddler="$:/temp/generated-list-mod-state" text /> or even <$action-deletetiddler $tiddler="$:/temp/generated-list-mod-state" />, since this is a temporary tiddler whose only function is to hold your selection.

1 Like

Thanks!

I’m moving on to editing the levels of modifiers… and it’s another tall order for me :slight_smile:

My plan:

  1. Retrieve the contents of the <<advantage>> field of the <<book>> tiddler as parsed by the list step we are currently using the procedure in and that works the same as the <list-modifiers> procedure we worked on earlier, and copy its contents in a temporary tiddler for edition, where every string of the form modifier level in between commas is copied to the temporary tiddler as fields named after modifier and containing the level.
  2. Use a combination of “edit” and “set” buttons to toggle the edition process (using <$reveal>) which will happen through the use of <$edittext> widgets editing the values of the relevant modifier field in the temp tiddler, then when the button is clicked, close the $reveal and replace the values in the <<advantage>> field with the values from the temp tiddler.

Does this make sense?

I’m currently writing this procedure as part of this plan (untested and unfinished), trying to replicate your approach with backtick substitutions:

\procedure edit-mod-level()
<$action-setfield $tiddler="$:/temp/generated-mod-level-values" $field=<<modname>> $value=<<modlevel>>/>
<$let current={{{ [<book>get<advantage>] }}} selected={{$:/temp/generated-mod-level-values!!<modname>}}>
<$reveal type="nomatch" state="$:/temp/modlevels" text="show" animate=yes><$button set="$:/temp/modlevels" setTo="show">Edit mod level</$button></$reveal><$reveal type="match" state="$:/temp/modlevels" text="show" animate=yes><$edittext tiddler="$:/temp/generated-mod-level-values" field=<<modname>> type="number"/><$let newvalue={{$:/temp/generated-mod-level-values!!<modname>}} new=`$(selected)$ <newvalue>` existing=`$(selected)$ \d+(,\s?)?`>
<$button set="$:/temp/modlevels" setTo="hide" actions="<action-setfield $tiddler=<book> $field=<<advantage>> $value={{{ [<current>] [<new>] +[join[, ]] }}}">set</$button></$let></$reveal>
</$let>
\end

In it, the thinking is:

  • The first <$action-setfield> creates the temporary tiddler, naming the field with the modifier being edited, and giving it the current value of its level,
  • Bunch of variables declarations with the first <$let>,
  • The first reveal draws the button to edit if we’re not already editing,
  • The second reveal is where the meat is: the edittext widget grabbing from the temporary tiddler, as well as the second <$let> to declare the rest of the needed variables including the backtick stuff, as well as
  • The second button to both close the editing process and save the new values

I’m afraid that I might be trying to do too much here, and quite possibly confused by the fact that <new> has to refer to the new level value instead of “1”.

Yes, I think your approach makes sense!

It’s often easier for me to write code than to think things through in the abstract, so here’s how I might do it (written without looking at your code):

Code under the fold for brevity; feel free to use or not use, as you like
\procedure update-mod-level()
\function updated() [<segment>split[ ]butlast[]] [<newLevel>] +[join[ ]]
<$action-setfield $tiddler=<<book>>
	$field=<<advantage>>
	$value={{{ [<current>search-replace<segment>,<updated>] }}} />
<$action-deletetiddler $tiddler=<<tempLevel>> />
\end

\procedure select-modsB(advantage, book)
<$let
	book={{{ [<book>!match[]] ~[<currentTiddler>] }}}
	current={{{ [<book>get<advantage>] }}}
>

Add or remove
<$select tiddler="$:/temp/generated-list-mod-state"
	default=""
	actions=<<mod-select-actions>>
>
	<option value="" disabled>Select modifier</option>
	<optgroup label="Special">
		<$list filter="[tag[GURPS Modifier]search:to-advantages:<advantage>]">
			<<mod-select-option>>
		</$list>
	</optgroup>
	<optgroup label="Global modifiers">
		<$list filter="[tag[GURPS Modifier]!has[to-advantages]] -[[Modifier template]]">
			<<mod-select-option>>
		</$list>
	</optgroup>
</$select>

<% if [<current>!match[]] %>
Modify levels:
<table>
<$list filter="[<current>split[, ]]" variable="segment">
	<tr>
	<$let
		mod={{{ [<segment>split[ ]butlast[]join[ ]] }}}
		currentLevel={{{ [<segment>split[ ]last[]else[1]] }}}
		tempLevel=`$:/temp/$(book)$/$(mod)$/level`
	>
		<td><<mod>></td>
		<td>
			<$select tiddler=<<tempLevel>> default=<<currentLevel>>>
				<$list filter="[range[20]]">
				<!--                   ^ use the maximum level possible -->
					<option>{{!!title}}</option>
				</$list>
			</$select>
		</td>
		<td>
			<$list filter="[<tempLevel>get[text]]" variable="newLevel">
				<$button actions=<<update-mod-level>>>Update</$button>
			</$list>
		</td>
	</$let>
	</tr>
</$list>
</table>
<% endif %>
</$let>
\end

Incidentally, I noticed a few stray </$let> tags in your mod-select-actions macro. These shouldn’t actually break anything (as they’re scoped within conditional cases) and won’t show up since they’re part of an action macro (and thus don’t get rendered as HTML). But they’re technically errors, and potentially confusing, so best to avoid.

Here’s a corrected version:

\procedure mod-select-actions()
<$let
	currentTiddler=<<book>>
	current={{{ [<book>get<advantage>] }}}
	selected={{$:/temp/generated-list-mod-state}}
	new=`$(selected)$ 1`
	existing=`$(selected)$ \d+(,\s?)?`
>
<% if [<current>search<selected>] %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>search-replace::regexp<existing>,[]] }}} />
<% else %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>!match[]] [<new>] +[join[, ]] }}} />
<% endif %>
<$action-deletetiddler $tiddler="$:/temp/generated-list-mod-state" />
\end

If you do end up using any of my code above, note that I included current={{{ [<book>get<advantage>] }}} in the first $let widget used in select-modsB, along with Eric’s <<book>> definition, as it’s convenient to have access to the <<current>> variable for the level-modifying code as well. This means you can remove that line from the mod-select-actions code above, as it’s already accounted for in the parent procedure.

1 Like

I ended up using most of your code in the procedure that I named edit-modifiers instead of select-modsB; just refactored it to be displayed on a line rather than vertically and a few minor tweaks. I also included a conditional to check if a modifier can be leveled or not (not all of them can be, and if they can’t be, they should remain at level 1 and not allow users to access their level), as well as a call to the lvl-mods-sum procedure for visibility, that calculates the total effect of the mods:

\procedure update-mod-level()
\function updated() [<segment>split[ ]butlast[]] [<newLevel>] +[join[ ]]
<$action-setfield $tiddler=<<book>>
	$field=<<advantage>>
	$value={{{ [<current>search-replace<segment>,<updated>] }}} />
<$action-deletetiddler $tiddler=<<tempLevel>> />
\end

\procedure edit-modifiers(advantage, book)
<$let
	book={{{ [<book>!match[]] ~[<currentTiddler>] }}}
	current={{{ [<book>get<advantage>] }}}
>
<% if [<current>!match[]] %>
<table>
	<tr><td><$macrocall $name="lvl-mods-sum" advbook=<<book>> adv=<<advantage>>/></td><$list filter="[<current>split[, ]!is[blank]]" variable="segment">
	<$let
		mod={{{ [<segment>split[ ]butlast[]join[ ]] }}}
		currentLevel={{{ [<segment>split[ ]last[]else[1]] }}}
		tempLevel=`$:/temp/$(book)$/$(mod)$/level`
	>
		<td><$link to=<<mod>>/></td>
		<td>
			<% if [<mod>get[levelable]match[yes]] %><$select tiddler=<<tempLevel>> default=<<currentLevel>>>
				<$list filter="[range[20]]">
				<!--                   ^ use the maximum level possible -->
					<option>{{!!title}}</option>
				</$list>
			</$select>
			<$list filter="[<tempLevel>get[text]]" variable="newLevel">
				<$button actions=<<update-mod-level>>>{{$:/core/images/save-button}}</$button>
			</$list><% else %>Not levelable<% endif %>
		</td>
	</$let>
</$list>
</tr></table>
<% endif %>
<$macrocall $name=select-mods advantage=<<advantage>> book=<<book>>/>
</$let>
\end

However, I have a bug to squash, as when one adds a modifier through the select, then removes it, the <advantage> field in the <book> is left with an extra empty mod that throws things off:

image

As you’ll see in the code above, I have a “cosmetic” workaround in place for now through the addition of !is[blank] after the split expression of the <$list> widget in the edit-modifiers procedure, but the root cause is most likely in the mod-select-actions procedure below (copied the whole thing as it stands currently for easier reference):

\procedure mod-select-actions()
<$let
	currentTiddler=<<book>>
	current={{{ [<book>get<advantage>] }}}
	selected={{$:/temp/generated-list-mod-state}}
	new=`$(selected)$ 1`
	existing=`$(selected)$ \d+(,\s?)?`
>
<% if [<current>search<selected>] %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>search-replace::regexp<existing>,[]] }}} />
<% else %>
<$action-setfield
	$field=<<advantage>>
	$value={{{ [<current>!match[]] [<new>] +[join[, ]] }}} />
<% endif %>
<$action-deletetiddler $tiddler="$:/temp/generated-list-mod-state" />
\end

\procedure mod-select-option()
<option value={{!!title}}>
{{!!title}} ({{{ [{!!title}get[modifier-value]] }}}%)
<$list filter=`[<book>search:$(advantage)${!!title}]`>&#x2714;</$list>
</option>
\end

\procedure select-mods(advantage, book)
<$let book={{{ [<book>!match[]] ~[<currentTiddler>] }}}>
<$select tiddler="$:/temp/generated-list-mod-state"
	default=""
	actions=<<mod-select-actions>>
>
	<option value="" disabled>Select modifier</option>
	<optgroup label="Special">
		<$list filter="[tag[GURPS Modifier]search:to-advantages:<advantage>]">
			<<mod-select-option>>
		</$list>
	</optgroup>
	<optgroup label="Global modifiers">
		<$list filter="[tag[GURPS Modifier]!has[to-advantages]] -[[Modifier template]]">
			<<mod-select-option>>
		</$list>
	</optgroup>
</$select></$let>
\end

My guess is there may be something off with the backtick substitution in $value={{{ [<current>search-replace::regexp<existing>,[]] }}} />, or the behaviour of the $value={{{ [<current>!match[]] [<new>] +[join[, ]] }}} /> line?

I’m not always able to reproduce it, unfortunately, that’s why I’m not entirely sure how/when it happens. It may be around acting on the first or last mod of the list. I’m trying to figure it out right now.

It occurs when you add a first mod, then a second, then remove the second and re-add it. image - in fact, when you add a first mod, then a second, then remove the second, the ", " remains after the first:

  1. Adding a first mod: image
  2. Adding a second mod: image
  3. remove the second mod: image ← here’s the culprit, I think
  4. Re-adding the second mod: image

It seems to be that adding then removing the last mod of the list, the list then ends with ", " rather than nothing, and ", " remains there afterwards because there’s no blank mod and one can’t access it through the Select.

Adding and removing the last mod of the list several times: image
:joy: