WIP towards the tails GUI plugins
* Tup automated builds with hot-code reloading * Most the Plugin API fleshed out. The work-in-progress P2P traffic visualition plugin is not committed yet.
This commit is contained in:
parent
8b4e8e174b
commit
3b06528906
|
@ -1,5 +1,8 @@
|
|||
import
|
||||
karax/vdom
|
||||
jsffi, karax/[karax, vdom]
|
||||
|
||||
export
|
||||
karax, vdom
|
||||
|
||||
type
|
||||
SectionRenderer* = proc(): VNode
|
||||
|
@ -8,12 +11,68 @@ type
|
|||
title*: cstring
|
||||
content*: SectionRenderer
|
||||
|
||||
TailEvent* = object of js
|
||||
msg*: cstring
|
||||
level*: cstring
|
||||
ts*: cstring
|
||||
protocol*: cstring
|
||||
msgId*: int
|
||||
peer*: cstring
|
||||
port*: int
|
||||
data*: js
|
||||
topics*: cstring
|
||||
|
||||
TailEventFilter* = proc(e: TailEvent): bool
|
||||
|
||||
when defined(createChroniclesTail):
|
||||
proc getKarax*: KaraxInstance {.exportc.} = kxi
|
||||
|
||||
var
|
||||
sections* = newSeq[Section](0)
|
||||
filters* = newSeq[TailEventFilter]()
|
||||
|
||||
proc addSection*(title: cstring, content: SectionRenderer) {.exportc.} =
|
||||
sections.add Section(title: title, content: content)
|
||||
else:
|
||||
proc addSection*(title: cstring, content: SectionRenderer) {.importc.}
|
||||
redrawSync()
|
||||
|
||||
proc addEventFilter*(f: TailEventFilter) {.exportc.} =
|
||||
filters.add f
|
||||
|
||||
proc addEscaped*(result: var string, s: string) {.exportc.} =
|
||||
## same as ``result.add(escape(s))``, but more efficient.
|
||||
for c in items(s):
|
||||
case c
|
||||
of '<': result.add("<")
|
||||
of '>': result.add(">")
|
||||
of '&': result.add("&")
|
||||
of '"': result.add(""")
|
||||
of '\'': result.add("'")
|
||||
of '/': result.add("/")
|
||||
else: result.add(c)
|
||||
|
||||
proc addAsHtml*(result: var string, obj: js) {.exportc.} =
|
||||
if jsTypeOf(obj) == "object":
|
||||
result.add "<table>"
|
||||
for key, value in obj:
|
||||
result.add """<tr><td class = "key">"""
|
||||
result.addEscaped $key
|
||||
result.add """</td><td class = "value">"""
|
||||
result.addAsHtml value
|
||||
result.add "</td></tr>"
|
||||
result.add "</table>"
|
||||
else:
|
||||
result.add cast[cstring](obj.toString())
|
||||
|
||||
proc asHtml*(obj: js): string {.exportc.} =
|
||||
result = newStringOfCap(128)
|
||||
result.addAsHtml(obj)
|
||||
|
||||
else:
|
||||
proc addEscaped*(result: var string, s: string) {.importc.}
|
||||
proc addAsHtml*(result: var string, obj: js) {.importc.}
|
||||
proc asHtml*(obj: js): string {.importc.}
|
||||
|
||||
proc getKarax*(): KaraxInstance {.importc.}
|
||||
proc addSection*(title: cstring, content: SectionRenderer) {.importc.}
|
||||
proc addEventFilter*(f: TailEventFilter) {.importc.}
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
: karax_app.nim |> nim --hotCodeReloading:on -d:createChroniclesTail -o:%o js %f |> %B.js
|
||||
: eth_p2p_plugin.nim |> nim --hotCodeReloading:on -o:%o js %f |> %B.js
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="ROOT" />
|
||||
<div>
|
||||
<div id="ROOT"></div>
|
||||
</div>
|
||||
<script src="karax_app.js"></script>
|
||||
<script src="eth_p2p_plugin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,7 +3,7 @@ include
|
|||
|
||||
import
|
||||
jsconsole, jscore, jsffi, jswebsockets,
|
||||
tail_jsplugins
|
||||
chronicles_tail/jsplugins
|
||||
|
||||
type
|
||||
ChroniclesPrompt = object
|
||||
|
@ -11,13 +11,11 @@ type
|
|||
suggestions: seq[string]
|
||||
activeSuggestionIdx: int
|
||||
|
||||
LogEvent = js
|
||||
|
||||
var
|
||||
chroniclesCredentials* {.importc, nodecl.}: js
|
||||
chroniclesPrompt: ChroniclesPrompt
|
||||
activeSectionIdx = 0
|
||||
logEvents = newSeq[LogEvent]()
|
||||
logEvents = newSeq[TailEvent]()
|
||||
|
||||
proc activeOrInactive(idx, currentActiveIdx: int): cstring =
|
||||
if idx == currentActiveIdx: "active"
|
||||
|
@ -48,14 +46,12 @@ proc logSectionContent: VNode =
|
|||
|
||||
tbody:
|
||||
for event in logEvents:
|
||||
let level = cast[cstring](event.level)
|
||||
let level = event.level
|
||||
tr(class = level):
|
||||
td(class = "level"): text level
|
||||
td(class = "ts") : text cast[cstring](event.ts)
|
||||
td(class = "msg") : text cast[cstring](event.msg)
|
||||
td(class = "props"): text cast[cstring](event.x)
|
||||
|
||||
addSection("Log", logSectionContent)
|
||||
td(class = "ts") : text event.ts
|
||||
td(class = "msg") : text event.msg
|
||||
td(class = "props"): text event.data.asHtml
|
||||
|
||||
var chroniclesSocket = newWebSocket(cast[cstring](chroniclesCredentials.url))
|
||||
|
||||
|
@ -63,8 +59,13 @@ chroniclesSocket.onOpen = proc (e: jswebsockets.Event) =
|
|||
chroniclesSocket.send(cast[cstring](chroniclesCredentials.accessToken))
|
||||
|
||||
chroniclesSocket.onMessage = proc (e: MessageEvent) =
|
||||
var msg = JSON.parse(e.data).js
|
||||
if msg.level == jsundefined:
|
||||
var msg = cast[TailEvent](JSON.parse(e.data))
|
||||
|
||||
for f in filters:
|
||||
if f(msg):
|
||||
return
|
||||
|
||||
if msg.level.toJs == jsundefined:
|
||||
console.log msg
|
||||
return
|
||||
|
||||
|
@ -100,4 +101,5 @@ proc pageContent(): VNode =
|
|||
activeSection()
|
||||
|
||||
setRenderer pageContent
|
||||
addSection "Log", logSectionContent
|
||||
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
import
|
||||
macros, json, random, times, strutils,
|
||||
macros, json, random, times, strutils, os,
|
||||
asynchttpserver, asyncnet, asyncdispatch, websocket,
|
||||
chronicles_tail/configuration
|
||||
|
||||
const
|
||||
indexFile = staticRead "karax_app.html"
|
||||
faviconBytes = staticRead "favicon.png"
|
||||
styles = staticRead "styles.css"
|
||||
|
||||
template fileContents(file: string): string =
|
||||
when defined(debug):
|
||||
readFile("webui" / file)
|
||||
else:
|
||||
const contents = staticRead(file)
|
||||
contents
|
||||
|
||||
|
||||
type
|
||||
WebuiServer* = ref object
|
||||
|
@ -45,7 +52,7 @@ proc serve*(s: WebuiServer): Future[void] =
|
|||
|
||||
var nextLineToSend = 0
|
||||
while nextLineToSend < s.logLines.len:
|
||||
await ws.sendText(s.logLines[nextLineToSend])
|
||||
await ws.sendText(s.logLines[nextLineToSend], maskingKey = "")
|
||||
inc nextLineToSend
|
||||
|
||||
s.clients.add ws
|
||||
|
@ -54,9 +61,13 @@ proc serve*(s: WebuiServer): Future[void] =
|
|||
await req.respond(Http200, indexFile)
|
||||
|
||||
of "/styles.css":
|
||||
await req.respond(Http200, styles,
|
||||
await req.respond(Http200, fileContents("styles.css"),
|
||||
newHttpHeaders({"Content-Type": "text/css"}))
|
||||
|
||||
of "/eth_p2p_plugin.js":
|
||||
await req.respond(Http200, readFile("webui/eth_p2p_plugin.js"),
|
||||
newHttpHeaders({"Content-Type": "application/javascript"}))
|
||||
|
||||
of "/karax_app.js":
|
||||
var scripts = newStringOfCap(16000)
|
||||
|
||||
|
@ -69,8 +80,8 @@ proc serve*(s: WebuiServer): Future[void] =
|
|||
|
||||
scripts.add compileJs("karax_app.nim", "-d:createChroniclesTail")
|
||||
|
||||
for plugin in s.plugins:
|
||||
scripts.add plugin
|
||||
# for plugin in s.plugins:
|
||||
# scripts.add plugin
|
||||
|
||||
await req.respond(Http200, scripts,
|
||||
newHttpHeaders({"Content-Type": "application/javascript"}))
|
||||
|
|
|
@ -71,15 +71,16 @@ table {
|
|||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: white;
|
||||
flex: 0;
|
||||
flex: 0 auto;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
justify-self: flex-end;
|
||||
order: 3;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
flex: 1 auto;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#content, #header {
|
||||
|
@ -87,7 +88,7 @@ table {
|
|||
}
|
||||
|
||||
#content {
|
||||
flex: 1;
|
||||
flex: 1 auto;
|
||||
padding-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -136,7 +137,7 @@ table {
|
|||
#log_section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
flex: 1 auto;
|
||||
}
|
||||
|
||||
#log_section .events {
|
||||
|
@ -144,7 +145,7 @@ table {
|
|||
border: 2px solid #e8e8e8;
|
||||
padding-top: 1.875em;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
flex: 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue