TerminalUserInterfaces.jl question

I try to use TerminalUserInterfaces.jl and the list example: TerminalUserInterfaces.jl/examples/list.jl at main · kdheepak/TerminalUserInterfaces.jl · GitHub to create a list of options.
This part is fine and works.

Now, how can I expand this example to be able to multi select list entries?
I want to select more than 1 entries from the list and visible highlight the selected entries.

Another question is: when I am in the menu and switch to another window, the menu errors out with

ERROR: ArgumentError: invalid JSON at byte position 1 while parsing type JSON3.Object: ExpectedOpeningObjectChar
"FocusLost"

which is quite annoying.
How can this solved?

Is there also a stack trace? That probably contains the critical information about where the error is arising.

Also, you may have better luck with that part of your question if you work out a minimal reproduction and file an issue for the package.

The MWE is just the linked example above, again: TerminalUserInterfaces.jl/examples/list.jl at main · kdheepak/TerminalUserInterfaces.jl · GitHub

using TerminalUserInterfaces
const TUI = TerminalUserInterfaces
using Random
using Logging

@kwdef mutable struct Model <: TUI.Model
  words = [
    "Option A"
    "Option B"
    "Option C"
    "Option D"
    "Option E"
    "Option F"
    "Option G"
    "Option H"
    "Option I"
  ]
  selection = 1
  scroll = 1
  quit = false
end

function TUI.update!(m::Model, evt::TUI.KeyEvent)
  !isnothing(evt) && @info "Received event" evt
  if TUI.keypress(evt) == "j"
    m.selection += 1
  elseif TUI.keypress(evt) == "k"
    m.selection -= 1
  elseif TUI.keypress(evt) == "q"
    m.quit = true
  elseif TUI.keypress(evt) == "Enter"
    m.quit = true
  end
  if m.selection < 1
    m.selection = 1
  end
  if m.selection > length(m.words)
    m.selection = length(m.words)
  end
end

function TUI.view(m::Model)
  words = [TUI.Word(word, TUI.Crayon()) for word in m.words]
  block = TUI.Block(; title = "Option Picker")
  TUI.SelectableList(block, words, m.scroll, m.selection)
end

function main()
  m = Model()
  TUI.app(m)
  println(m.words[m.selection])
end

main()

How can I enhance this code for multiple selections?

This is the stacktrace which can also easily reproduced by using above example code and switching to another window (Windows11 here):

julia> main()
ERROR: ArgumentError: invalid JSON at byte position 1 while parsing type JSON3.Object: ExpectedOpeningObjectChar
"FocusLost"

Stacktrace:
  [1] invalid(error::JSON3.Error, buf::Base.CodeUnits{UInt8, String}, pos::Int64, T::Type)
    @ JSON3 C:\Users\Oli\.julia\packages\JSON3\jSAdy\src\JSON3.jl:30
  [2] #read#43
    @ C:\Users\Oli\.julia\packages\JSON3\jSAdy\src\structs.jl:429 [inlined]
  [3] read
    @ C:\Users\Oli\.julia\packages\JSON3\jSAdy\src\structs.jl:353 [inlined]
  [4] read
    @ C:\Users\Oli\.julia\packages\JSON3\jSAdy\src\structs.jl:350 [inlined]
  [5] read(str::String, ::Type{Dict}; jsonlines::Bool, kw::@Kwargs{})
    @ JSON3 C:\Users\Oli\.julia\packages\JSON3\jSAdy\src\structs.jl:41
  [6] read
    @ C:\Users\Oli\.julia\packages\JSON3\jSAdy\src\structs.jl:33 [inlined]
  [7] read()
    @ Crossterm C:\Users\Oli\.julia\packages\Crossterm\IcBlh\src\Crossterm.jl:384
  [8] #try_get_event#19
    @ C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\terminal.jl:48 [inlined]
  [9] try_get_event
    @ C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\terminal.jl:46 [inlined]
 [10] (::TerminalUserInterfaces.var"#54#55"{Float64, Model})()
    @ TerminalUserInterfaces C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\app.jl:37
 [11] tui(f::TerminalUserInterfaces.var"#54#55"{Float64, Model}; flags::@Kwargs{})
    @ TerminalUserInterfaces C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\terminal.jl:262
 [12] tui
    @ C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\terminal.jl:258 [inlined]
 [13] #app#53
    @ C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\app.jl:31 [inlined]
 [14] app
    @ C:\Users\Oli\.julia\packages\TerminalUserInterfaces\eHCFe\src\app.jl:30 [inlined]
 [15] main()
    @ Main .\REPL[8]:3
 [16] top-level scope
    @ REPL[9]:1

Alternative question:

Is there another TextUI usable in the REPL?

@kdheepak ?

Thanks for pinging! I don’t check discourse actively.

If you can open an issue with a Minimal Working Example reproducing the error, I can take a look at what is going on. My guess is that Crossterm is sending a FocusLost and that isn’t being parsed correctly somewhere along the way. I tested the FocusLost locally on iTerm2 and wasn’t able to reproduce it.

Are you interested in building a TUI? If you just want to enable multiple selections for yourself or your users, you probably want to check out QuickMenus.jl:

There’s also built-in functionality in the Julia REPL in TerminalMenus that can get you a long ways:

https://docs.julialang.org/en/v1/stdlib/REPL/#MultiSelectMenu

1 Like

Thanks for the reply, the FocusLost issue is created.
I will check the alternatives you mentioned, thanks again.