Changed session table from using a Table[string, UIAgent] to seq[Agent] as that works better with the multi select. Separate table for heartbeat modifications.
This commit is contained in:
@@ -1,18 +1,18 @@
|
|||||||
[Window][Sessions [Table View]]
|
[Window][Sessions [Table View]]
|
||||||
Pos=10,43
|
Pos=10,43
|
||||||
Size=1386,340
|
Size=1188,394
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,0
|
DockId=0x00000003,0
|
||||||
|
|
||||||
[Window][Listeners]
|
[Window][Listeners]
|
||||||
Pos=10,385
|
Pos=10,439
|
||||||
Size=1888,604
|
Size=1888,550
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Eventlog]
|
[Window][Eventlog]
|
||||||
Pos=1398,43
|
Pos=1200,43
|
||||||
Size=500,340
|
Size=698,394
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
@@ -26,70 +26,70 @@ Size=1908,999
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][[7E248CBA] jakob@AURA]
|
[Window][[7E248CBA] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][[7BE69219] jakob@AURA]
|
[Window][[7BE69219] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,2
|
DockId=0x00000002,2
|
||||||
|
|
||||||
[Window][[CD44669B] jakob@AURA]
|
[Window][[CD44669B] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
|
||||||
DockId=0x00000002
|
|
||||||
|
|
||||||
[Window][[F300DB27] jakob@AURA]
|
|
||||||
Pos=10,385
|
|
||||||
Size=1888,604
|
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
|
[Window][[F300DB27] jakob@AURA]
|
||||||
|
Pos=10,514
|
||||||
|
Size=1888,475
|
||||||
|
Collapsed=0
|
||||||
|
DockId=0x00000002,3
|
||||||
|
|
||||||
[Window][[AB3464CE] jakob@AURA]
|
[Window][[AB3464CE] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002
|
||||||
|
|
||||||
[Window][[3FC9903D] jakob@AURA]
|
[Window][[3FC9903D] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,3
|
||||||
|
|
||||||
[Window][[6CD04F2C] jakob@AURA]
|
[Window][[6CD04F2C] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][[4E93619C] jakob@AURA]
|
[Window][[4E93619C] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,8
|
||||||
|
|
||||||
[Window][[0C56BE7D] jakob@AURA]
|
[Window][[0C56BE7D] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,7
|
||||||
|
|
||||||
[Window][[37C08F6C] jakob@AURA]
|
[Window][[37C08F6C] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,4
|
||||||
|
|
||||||
[Window][[BCC8B616] jakob@AURA]
|
[Window][[BCC8B616] jakob@AURA]
|
||||||
Pos=10,186
|
Pos=10,315
|
||||||
Size=1004,604
|
Size=1004,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Debug##Default]
|
[Window][Debug##Default]
|
||||||
Pos=60,60
|
Pos=60,60
|
||||||
@@ -97,81 +97,81 @@ Size=400,400
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][[9592878D] jakob@AURA]
|
[Window][[9592878D] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][[8F8DC95F] jakob@AURA]
|
[Window][[8F8DC95F] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][[E05185F6] jakob@AURA]
|
[Window][[E05185F6] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,2
|
||||||
|
|
||||||
[Window][[022E62E0] jakob@AURA]
|
[Window][[022E62E0] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002
|
||||||
|
|
||||||
[Window][[F5CE46E3] jakob@AURA]
|
[Window][[F5CE46E3] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,6
|
||||||
|
|
||||||
[Window][[37DAA990] jakob@AURA]
|
[Window][[37DAA990] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,439
|
||||||
Size=1888,604
|
Size=1888,550
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][[824B6EC7] jakob@AURA]
|
[Window][[824B6EC7] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002
|
||||||
|
|
||||||
[Window][[386BFA92] jakob@AURA]
|
[Window][[386BFA92] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,5
|
||||||
|
|
||||||
[Window][[209155FC] jakob@AURA]
|
[Window][[209155FC] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][[2498CB04] jakob@AURA]
|
[Window][[2498CB04] jakob@AURA]
|
||||||
Pos=10,385
|
Pos=10,514
|
||||||
Size=1888,604
|
Size=1888,475
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][[0EBC9C82] jakob@AURA]
|
[Window][[0EBC9C82] jakob@AURA]
|
||||||
Pos=10,186
|
Pos=10,439
|
||||||
Size=1004,604
|
Size=1888,550
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,2
|
||||||
|
|
||||||
[Table][0x32886A44,9]
|
[Table][0x32886A44,9]
|
||||||
Column 0 Weight=0.9944 Visible=1
|
Column 0 Weight=0.9882 Visible=1
|
||||||
Column 1 Weight=1.2029 Visible=1
|
Column 1 Weight=1.1970 Visible=1
|
||||||
Column 2 Weight=0.7801 Visible=1
|
Column 2 Weight=0.7725 Visible=1
|
||||||
Column 3 Weight=0.6312 Visible=1
|
Column 3 Weight=0.6681 Visible=1
|
||||||
Column 4 Weight=1.3658 Visible=0
|
Column 4 Weight=1.3658 Visible=0
|
||||||
Column 5 Weight=1.0064 Visible=1
|
Column 5 Weight=0.9604 Visible=1
|
||||||
Column 6 Weight=1.1671 Visible=1
|
Column 6 Weight=1.2944 Visible=1
|
||||||
Column 7 Weight=0.8456 Visible=1
|
Column 7 Weight=0.4175 Visible=1
|
||||||
Column 8 Weight=1.0064 Visible=1
|
Column 8 Weight=1.3361 Visible=1
|
||||||
|
|
||||||
[Table][0x064A67CC,4]
|
[Table][0x064A67CC,4]
|
||||||
Column 0 Weight=1.0000
|
Column 0 Weight=1.0000
|
||||||
@@ -181,8 +181,8 @@ Column 3 Weight=1.0000
|
|||||||
|
|
||||||
[Docking][Data]
|
[Docking][Data]
|
||||||
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
|
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
|
||||||
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,340 Split=X
|
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,394 Split=X
|
||||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1386,159 CentralNode=1 Selected=0x61E02D75
|
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1188,159 CentralNode=1 Selected=0x61E02D75
|
||||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=500,159 Selected=0x0FA43D88
|
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=698,159 Selected=0x0FA43D88
|
||||||
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,604 Selected=0x6BE22050
|
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,550 Selected=0xE5DB7127
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,11 @@ proc main() =
|
|||||||
|
|
||||||
of CLIENT_AGENT_ADD:
|
of CLIENT_AGENT_ADD:
|
||||||
let agent = event.data.to(UIAgent)
|
let agent = event.data.to(UIAgent)
|
||||||
sessionsTable.agents[agent.agentId] = agent
|
|
||||||
|
# The ImGui Multi Select only works well with seq's, so we maintain a
|
||||||
|
# separate table of the latest agent heartbeats to have the benefit of quick and direct O(1) access
|
||||||
|
sessionsTable.agents.add(agent)
|
||||||
|
sessionsTable.agentActivity[agent.agentId] = agent.latestCheckin
|
||||||
|
|
||||||
# Initialize position of console windows to bottom by drawing them once when they are added
|
# Initialize position of console windows to bottom by drawing them once when they are added
|
||||||
# By default, the consoles are attached to the same DockNode as the Listeners table (Default: bottom),
|
# By default, the consoles are attached to the same DockNode as the Listeners table (Default: bottom),
|
||||||
@@ -90,7 +94,7 @@ proc main() =
|
|||||||
consoles[agent.agentId].showConsole = false
|
consoles[agent.agentId].showConsole = false
|
||||||
|
|
||||||
of CLIENT_AGENT_CHECKIN:
|
of CLIENT_AGENT_CHECKIN:
|
||||||
sessionsTable.agents[event.data["agentId"].getStr()].latestCheckin = event.timestamp
|
sessionsTable.agentActivity[event.data["agentId"].getStr()] = event.timestamp
|
||||||
|
|
||||||
of CLIENT_AGENT_PAYLOAD:
|
of CLIENT_AGENT_PAYLOAD:
|
||||||
discard
|
discard
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ import ../../common/[types, utils]
|
|||||||
type
|
type
|
||||||
SessionsTableComponent* = ref object of RootObj
|
SessionsTableComponent* = ref object of RootObj
|
||||||
title: string
|
title: string
|
||||||
agents*: Table[string, UIAgent]
|
agents*: seq[UIAgent]
|
||||||
|
agentActivity*: Table[string, int64] # Direct O(1) access to latest checkin
|
||||||
selection: ptr ImGuiSelectionBasicStorage
|
selection: ptr ImGuiSelectionBasicStorage
|
||||||
consoles: ptr Table[string, ConsoleComponent]
|
consoles: ptr Table[string, ConsoleComponent]
|
||||||
|
|
||||||
proc SessionsTable*(title: string, consoles: ptr Table[string, ConsoleComponent]): SessionsTableComponent =
|
proc SessionsTable*(title: string, consoles: ptr Table[string, ConsoleComponent]): SessionsTableComponent =
|
||||||
result = new SessionsTableComponent
|
result = new SessionsTableComponent
|
||||||
result.title = title
|
result.title = title
|
||||||
result.agents = initTable[string, UIAgent]()
|
result.agents = @[]
|
||||||
|
result.agentActivity = initTable[string, int64]()
|
||||||
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
|
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
|
||||||
result.consoles = consoles
|
result.consoles = consoles
|
||||||
|
|
||||||
@@ -24,20 +26,16 @@ proc interact(component: SessionsTableComponent) =
|
|||||||
var it: pointer = nil
|
var it: pointer = nil
|
||||||
var row: ImGuiID
|
var row: ImGuiID
|
||||||
while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row):
|
while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row):
|
||||||
var selectedAgent: UIAgent = nil
|
let agent = component.agents[cast[int](row)]
|
||||||
for agentId, agent in component.agents.mpairs():
|
|
||||||
if igGetID_Str(agentId) == row:
|
|
||||||
selectedAgent = agent
|
|
||||||
|
|
||||||
if selectedAgent != nil:
|
# Create a new console window
|
||||||
# Create a new console window
|
if not component.consoles[].hasKey(agent.agentId):
|
||||||
if not component.consoles[].hasKey(selectedAgent.agentId):
|
component.consoles[][agent.agentId] = Console(agent)
|
||||||
component.consoles[][selectedAgent.agentId] = Console(selectedAgent)
|
|
||||||
|
# Focus the existing console window
|
||||||
|
else:
|
||||||
|
igSetWindowFocus_Str(fmt"[{agent.agentId}] {agent.username}@{agent.hostname}")
|
||||||
|
|
||||||
# Focus the existing console window
|
|
||||||
else:
|
|
||||||
igSetWindowFocus_Str(fmt"[{selectedAgent.agentId}] {selectedAgent.username}@{selectedAgent.hostname}")
|
|
||||||
|
|
||||||
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
||||||
igBegin(component.title, showComponent, 0)
|
igBegin(component.title, showComponent, 0)
|
||||||
defer: igEnd()
|
defer: igEnd()
|
||||||
@@ -75,15 +73,13 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
|||||||
var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape.int32 or ImGuiMultiSelectFlags_BoxSelect1d.int32, component.selection[].Size, int32(component.agents.len()))
|
var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape.int32 or ImGuiMultiSelectFlags_BoxSelect1d.int32, component.selection[].Size, int32(component.agents.len()))
|
||||||
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
||||||
|
|
||||||
for agentId, agent in component.agents.mpairs():
|
for row, agent in component.agents:
|
||||||
|
|
||||||
igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)
|
igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)
|
||||||
|
|
||||||
let row = igGetID_Str(agentId)
|
|
||||||
|
|
||||||
if igTableSetColumnIndex(0):
|
if igTableSetColumnIndex(0):
|
||||||
# Enable multi-select functionality
|
# Enable multi-select functionality
|
||||||
igSetNextItemSelectionUserData(cast[ImGuiSelectionUserData](row))
|
igSetNextItemSelectionUserData(row)
|
||||||
var isSelected = ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](row))
|
var isSelected = ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](row))
|
||||||
discard igSelectable_Bool(agent.agentId, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32, vec2(0.0f, 0.0f))
|
discard igSelectable_Bool(agent.agentId, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32, vec2(0.0f, 0.0f))
|
||||||
|
|
||||||
@@ -106,16 +102,14 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
|||||||
if igTableSetColumnIndex(7):
|
if igTableSetColumnIndex(7):
|
||||||
igText($agent.pid)
|
igText($agent.pid)
|
||||||
if igTableSetColumnIndex(8):
|
if igTableSetColumnIndex(8):
|
||||||
let duration = now() - agent.latestCheckin.fromUnix().utc()
|
let duration = now() - component.agentActivity[agent.agentId].fromUnix().utc()
|
||||||
let totalSeconds = duration.inSeconds
|
let totalSeconds = duration.inSeconds
|
||||||
|
|
||||||
let hours = totalSeconds div 3600
|
let hours = totalSeconds div 3600
|
||||||
let minutes = (totalSeconds mod 3600) div 60
|
let minutes = (totalSeconds mod 3600) div 60
|
||||||
let seconds = totalSeconds mod 60
|
let seconds = totalSeconds mod 60
|
||||||
|
|
||||||
let dummyTime = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int)
|
let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss")
|
||||||
let timeText = dummyTime.format("HH:mm:ss")
|
|
||||||
|
|
||||||
igText(fmt"{timeText} ago")
|
igText(fmt"{timeText} ago")
|
||||||
|
|
||||||
# Handle right-click context menu
|
# Handle right-click context menu
|
||||||
@@ -128,15 +122,12 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
|||||||
|
|
||||||
if igMenuItem("Remove", nil, false, true):
|
if igMenuItem("Remove", nil, false, true):
|
||||||
# Update agents table with only non-selected ones
|
# Update agents table with only non-selected ones
|
||||||
var agentsToDelete: seq[string] = @[]
|
var newAgents: seq[UIAgent] = @[]
|
||||||
for agentId, agent in component.agents.mpairs():
|
for i, agent in component.agents:
|
||||||
let i = igGetID_Str(agentId)
|
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
|
||||||
if ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
|
newAgents.add(agent)
|
||||||
agentsToDelete.add(agentId)
|
|
||||||
|
|
||||||
for agentId in agentsToDelete:
|
|
||||||
component.agents.del(agentId)
|
|
||||||
|
|
||||||
|
component.agents = newAgents
|
||||||
ImGuiSelectionBasicStorage_Clear(component.selection)
|
ImGuiSelectionBasicStorage_Clear(component.selection)
|
||||||
igCloseCurrentPopup()
|
igCloseCurrentPopup()
|
||||||
|
|
||||||
@@ -146,6 +137,3 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
|||||||
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO)
|
||||||
|
|
||||||
igEndTable()
|
igEndTable()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user