@@ -968,21 +968,32 @@ function compile_invoke(ctx::JSCompilationContext, expr::Expr)
968968 return " jl_print($(join (call_args, " , " )) )"
969969 end
970970
971- # JS escape hatch: js("raw code") → emits string content as raw JavaScript
971+ # JS escape hatch: js("raw code") or js("template with \$1", val) → raw JavaScript
972972 # Used by Therapy.jl to call browser APIs (document, localStorage, etc.)
973+ # Supports value passing: js("Plotly.react(el, \$1)", data()) substitutes $1 with compiled JS
973974 if func_name == " js"
975+ # Extract template string from IR (most reliable) or compiled args
976+ template_str = nothing
974977 if length (expr. args) >= 3 && expr. args[3 ] isa String
975- return expr. args[3 ]
976- end
977- # Fallback: compiled arg with quotes stripped
978- if length (call_args) >= 1
978+ template_str = expr. args[3 ]
979+ elseif length (call_args) >= 1
979980 s = call_args[1 ]
980981 if length (s) >= 2 && s[1 ] == ' "' && s[end ] == ' "'
981- return replace (replace (s[2 : end - 1 ], " \\\" " => " \" " ), " \\\\ " => " \\ " )
982+ template_str = replace (replace (s[2 : end - 1 ], " \\\" " => " \" " ), " \\\\ " => " \\ " )
982983 end
983- return s
984984 end
985- return " /* js() called with no arguments */"
985+
986+ if template_str === nothing
987+ return " /* js() called with no string template */"
988+ end
989+
990+ # Substitute $1, $2, etc. with compiled JS expressions for additional args
991+ result = template_str
992+ for i in 2 : length (call_args)
993+ result = replace (result, " \$ $(i- 1 ) " => call_args[i])
994+ end
995+
996+ return result
986997 end
987998
988999 # Math: div, fld, mod, cld, rem
0 commit comments