Skip to content

Commit fd51c5a

Browse files
committed
add info card for element
1 parent 864ea0b commit fd51c5a

File tree

6 files changed

+159
-27
lines changed

6 files changed

+159
-27
lines changed

NetworkDynamicsInspector/assets/app.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,22 @@ html, body {
8585
display: none; /* Initially hidden */
8686
transform: translate(12px, 12px); /* Offset from cursor */
8787
}
88+
89+
#element-info-box {
90+
font-family: monospace;
91+
overflow: scroll;
92+
border: 2px solid #dbdbdb;
93+
border-radius: 4px;
94+
background-color: #f5f5f5;
95+
color: #222;
96+
padding: 1px 10px;
97+
}
98+
.element-info-card {
99+
/* overflow:hidden; */
100+
/* resize:vertical; */
101+
}
102+
103+
.julia-prompt {
104+
color: #066f00;
105+
font-weight: bolder;
106+
}

NetworkDynamicsInspector/src/NetworkDynamicsInspector.jl

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ using GraphMakie.NetworkLayout
1515
using Bonito.Hyperscript
1616
using Bonito.Tables.OrderedCollections
1717

18+
# defined based on the julia version
19+
using NetworkDynamics: AnnotatedIOBuffer, AnnotatedString
20+
1821
const JQUERY = Asset("https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js")
1922
const SELECT2_CSS = Asset("https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css")
2023
const SELECT2_JS = Asset("https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js")
@@ -113,7 +116,7 @@ function graphplot_card(app, session)
113116
BIG = 50
114117
node_size = Observable(fill(SMALL, NV))
115118
THIN = 5
116-
THICK = 8
119+
THICK = 10
117120
edge_width = Observable(fill(THIN, NE))
118121

119122
onany(app.graphplot._selcomp; update=true) do selcomp
@@ -228,19 +231,24 @@ function graphplot_card(app, session)
228231
end
229232

230233
# interactions
231-
clickaction = (i, type) -> begin
234+
clickaction = (i, ax, type) -> begin
235+
shift_pressed = Makie.Keyboard.left_shift events(ax).keyboardstate ||
236+
Makie.Keyboard.right_shift events(ax).keyboardstate
237+
232238
idx = type == :vertex ? VIndex(i) : EIndex(i)
233-
selcomp = app.tsplots[][app.active_tsplot[]].selcomp
234-
if idx selcomp[]
235-
push!(selcomp[], idx)
236-
else
237-
filter!(x -> x != idx, selcomp[])
239+
if !shift_pressed # only update info window when click + shift
240+
selcomp = app.tsplots[][app.active_tsplot[]].selcomp
241+
if idx selcomp[]
242+
push!(selcomp[], idx)
243+
else
244+
filter!(x -> x != idx, selcomp[])
245+
end
246+
notify(selcomp)
238247
end
239248
app.graphplot._lastclickel[] = idx
240-
notify(selcomp)
241249
end
242-
nch = NodeClickHandler((i, _, _) -> clickaction(i, :vertex))
243-
ech = EdgeClickHandler((i, _, _) -> clickaction(i, :edge))
250+
nch = NodeClickHandler((i, _, ax) -> clickaction(i, ax, :vertex))
251+
ech = EdgeClickHandler((i, _, ax) -> clickaction(i, ax, :edge))
244252

245253
register_interaction!(ax, :nodeclick, nch)
246254
register_interaction!(ax, :nodehover, nhh)
@@ -810,4 +818,42 @@ function _smallest_free(d::Dict)
810818
return i
811819
end
812820

821+
function element_info_card(app, session)
822+
eltext = Observable{AnnotatedString}("")
823+
onany_throttled(app.graphplot._lastclickel, app.t; update=true, delay=0.1) do el, t
824+
if isnothing(el)
825+
eltext[] = ""
826+
else
827+
buf = AnnotatedIOBuffer()
828+
NetworkDynamics.dump_state(buf, app.sol[], t, el)
829+
s = read(seekstart(buf), AnnotatedString)
830+
htmlbuf = IOBuffer()
831+
show(htmlbuf, MIME"text/html"(), s)
832+
html = replace(String(take!(htmlbuf)), r"\n" => "<br>")
833+
834+
t = NetworkDynamics.str_significant(app.t[]; sigdigits=3)
835+
fake_prompt = "<span class=julia-prompt>julia&gt; </span><span class=julia-command>dump_state(sol, $t, $(repr(el)))</span>"
836+
eltext[] = "<pre>$fake_prompt<br><br>$html</pre>"
837+
end
838+
end
839+
840+
js = js"""
841+
const div = document.getElementById("element-info-box");
842+
843+
function replaceDivContentWithHTML(htmlString) {
844+
if (!div) return;
845+
div.innerHTML = htmlString;
846+
}
847+
848+
$(eltext).on(replaceDivContentWithHTML);
849+
replaceDivContentWithHTML($(eltext).value);
850+
"""
851+
evaljs(session, js)
852+
853+
Card(
854+
DOM.div(;id="element-info-box");
855+
class="element-info-card resize-with-gp"
856+
)
857+
end
858+
813859
end # module NetworkDynamicsInspector

NetworkDynamicsInspector/src/utils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ function on_throttled(f, obs; update=false, delay)
105105
end
106106
function onany_throttled(f, obs...; update=false, delay)
107107
tr = Throttle(f, delay)
108-
onany(obs; update) do args...
108+
onany(obs...; update) do args...
109109
tr(args...)
110110
end
111111
end

src/component_functions.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@ insym(c::EdgeModel)::@NamedTuple{src::Vector{Symbol},dst::Vector{Symbol}} = c.in
489489

490490
insym_all(c::VertexModel) = c.insym
491491
insym_all(c::EdgeModel) = Iterators.flatten(values(c.insym))
492+
insym_flat(c::VertexModel) = insym_all(c)
493+
insym_flat(c::EdgeModel) = collect(insym_all(c))
492494

493495
"""
494496
indim(c::VertexModel)::Int

src/metadata.jl

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ contains parameters and observed.
303303
304304
See also: [`get_initial_state`](@ref).
305305
"""
306-
function dump_initial_state(cf::ComponentModel; sigdigits=5, p=true, obs=true)
306+
dump_initial_state(cf::ComponentModel; kwargs...) = dump_initial_state(stdout, cf; kwargs...)
307+
function dump_initial_state(io, cf::ComponentModel; sigdigits=5, p=true, obs=true)
307308
lns = AnnotatedString[]
308309
symidx = _append_states!(lns, cf, sort(sym(cf)); sigdigits)
309310
psymidx = _append_states!(lns, cf, sort(psym(cf)); sigdigits)
@@ -313,26 +314,26 @@ function dump_initial_state(cf::ComponentModel; sigdigits=5, p=true, obs=true)
313314
obsidx = _append_observed!(lns, cf; sigdigits)
314315
aligned = align_strings(lns)
315316

316-
printstyled("Inputs:\n", bold=true)
317-
_printlines(aligned, insymidx)
318-
printstyled("States:\n", bold=true)
319-
_printlines(aligned, symidx)
320-
printstyled("Outputs:\n", bold=true)
321-
_printlines(aligned, outsymidx)
317+
printstyled(io, "Inputs:\n", bold=true)
318+
_printlines(io, aligned, insymidx)
319+
printstyled(io, "States:\n", bold=true)
320+
_printlines(io, aligned, symidx)
321+
printstyled(io, "Outputs:\n", bold=true)
322+
_printlines(io, aligned, outsymidx)
322323
if p
323-
printstyled("Parameters:\n", bold=true)
324-
_printlines(aligned, psymidx)
324+
printstyled(io, "Parameters:\n", bold=true)
325+
_printlines(io, aligned, psymidx)
325326
end
326327
if obs
327328
if length(obsidx) == 0
328-
printstyled("$(length(obssym(cf))) Observed symbols uninitialized.", bold=true)
329+
printstyled(io, "$(length(obssym(cf))) Observed symbols uninitialized.", bold=true)
329330
elseif length(obsidx) == length(obssym(cf))
330-
printstyled("Observed:\n", bold=true)
331+
printstyled(io, "Observed:\n", bold=true)
331332
else
332333
diff = length(obssym(cf)) - length(obsidx)
333-
printstyled("Observed ($diff additional uninitialized):\n", bold=true)
334+
printstyled(io, "Observed ($diff additional uninitialized):\n", bold=true)
334335
end
335-
_printlines(aligned, obsidx; newline=false)
336+
_printlines(io, aligned, obsidx; newline=false)
336337
end
337338
end
338339
function _append_states!(lns, cf, syms; sigdigits)
@@ -396,13 +397,76 @@ function _append_observed!(lns, cf; sigdigits)
396397
end
397398
fidx:length(lns)
398399
end
399-
function _printlines(aligned, range; newline=true)
400+
function _printlines(io, aligned, range; newline=true)
400401
lines = @views aligned[range]
401402
for i in eachindex(lines)
402403
if newline == false && i == lastindex(lines)
403-
print(lines[i])
404+
print(io, lines[i])
404405
else
405-
println(lines[i])
406+
println(io, lines[i])
407+
end
408+
end
409+
end
410+
411+
dump_state(sol, idx, t; kwargs...) = dump_state(stdout, sol, idx, t; kwargs...)
412+
function dump_state(io, sol, t, idx; sigdigits=3)
413+
cf = extract_nw(sol)[idx]
414+
_sym = sort(sym(cf))
415+
_outsym = sort(outsym_flat(cf))
416+
_insym = sort(insym_flat(cf))
417+
_psym = sort(psym(cf))
418+
_obssym = sort(obssym(cf))
419+
420+
groups = [("Outputs", _outsym), ("States", _sym), ("Inputs", _insym), ("Parameters", _psym), ("Observables", _obssym)]
421+
filter!(g -> !isempty(g[2]), groups)
422+
allsym = reduce(vcat, g[2] for g in groups)
423+
nwidxs = idxtype(idx).(idx.compidx, allsym)
424+
u0s, uts = sol([sol.t[begin], t]; idxs=nwidxs)
425+
guesses = [has_guess(cf, sym) ? get_guess(cf, sym) : nothing for sym in allsym]
426+
inits = [has_init(cf, sym) ? get_init(cf, sym) : nothing for sym in allsym]
427+
defaults = [has_default(cf, sym) ? get_default(cf, sym) : nothing for sym in allsym]
428+
429+
t_str = str_significant(t; sigdigits)
430+
t0_str = str_significant(sol.t[begin]; sigdigits)
431+
strings = AnnotatedString[styled"{bold:& && t=$t_str && t=$t0_str && Default/Init}"]
432+
for (sym, g, d, i, u0, ut) in zip(allsym, guesses, defaults, inits, u0s, uts)
433+
buf = AnnotatedIOBuffer()
434+
print(buf, " &"*string(sym)*" && ")
435+
436+
if !isnothing(ut)
437+
print(buf, str_significant(ut; sigdigits, phantom_minus=true))
438+
end
439+
print(buf, " && ")
440+
441+
if !isnothing(u0)
442+
print(buf, str_significant(u0; sigdigits, phantom_minus=true))
443+
end
444+
print(buf, " && ")
445+
446+
if !isnothing(d)
447+
print(buf, str_significant(d; sigdigits, phantom_minus=true))
448+
elseif !isnothing(i)
449+
ival = str_significant(i; sigdigits, phantom_minus=true)
450+
print(buf, styled"{blue: $ival}")
451+
if !isnothing(g)
452+
gval = str_significant(i; sigdigits, phantom_minus=true)
453+
print(buf, " (from $gval)")
454+
end
455+
end
456+
457+
s = read(seekstart(buf), AnnotatedString)
458+
push!(strings, s)
459+
end
460+
aligned = align_strings(strings)
461+
462+
let i = 2
463+
println(io, aligned[1])
464+
for (label, syms) in groups
465+
println(io, styled"{bold:$label}")
466+
for sym in syms
467+
println(io, aligned[i])
468+
i += 1
469+
end
406470
end
407471
end
408472
end

test/Inspector_test.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ let
142142
NetworkDynamicsInspector.graphplot_card(app, session),
143143
NetworkDynamicsInspector.gpstate_control_card(app, :vertex),
144144
NetworkDynamicsInspector.gpstate_control_card(app, :edge),
145+
NetworkDynamicsInspector.element_info_card(app, session),
145146
class="graphplot-col"
146147
),
147148
DOM.div(

0 commit comments

Comments
 (0)