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:
Zahary Karadjov 2018-11-27 13:50:56 +02:00
parent 8b4e8e174b
commit 3b06528906
6 changed files with 106 additions and 29 deletions

View File

@ -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("&lt;")
of '>': result.add("&gt;")
of '&': result.add("&amp;")
of '"': result.add("&quot;")
of '\'': result.add("&#x27;")
of '/': result.add("&#x2F;")
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.}

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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"}))

View File

@ -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,14 +88,14 @@ table {
}
#content {
flex: 1;
flex: 1 auto;
padding-top: 10px;
display: flex;
flex-direction: column;
}
#sections {
display: flex;
display: flex;
flex-direction: row;
align-items: center;
}
@ -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;
}