From 13832d4e5e3e45508e76d3d542f7473cd874e1eb Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:01:33 -0800 Subject: [PATCH] Remove Tui Examples - Replace with logos-core --- examples/tui.nim | 7 - examples/tui/layout.nim | 78 ----- examples/tui/persistence.nim | 135 -------- examples/tui/tui.nim | 584 ----------------------------------- examples/tui/utils.nim | 53 ---- 5 files changed, 857 deletions(-) delete mode 100644 examples/tui.nim delete mode 100644 examples/tui/layout.nim delete mode 100644 examples/tui/persistence.nim delete mode 100644 examples/tui/tui.nim delete mode 100644 examples/tui/utils.nim diff --git a/examples/tui.nim b/examples/tui.nim deleted file mode 100644 index 6120b20..0000000 --- a/examples/tui.nim +++ /dev/null @@ -1,7 +0,0 @@ -import chronos - -import tui/tui - - -when isMainModule: - waitFor main() diff --git a/examples/tui/layout.nim b/examples/tui/layout.nim deleted file mode 100644 index b72381a..0000000 --- a/examples/tui/layout.nim +++ /dev/null @@ -1,78 +0,0 @@ -import illwill - - -type - Pane* = object - xStart*: int - yStart*: int - width*: int - height*: int - -proc getPanes*(): seq[Pane] = - - let statusBarHeight = 6 - let convoWidth = 40 - let inboxHeight = 5 - let footerHeight = 14 - let modalXOffset = 10 - let modalHeight = 10 - - result = @[] - let statusBar = Pane( - xStart: 0, - yStart: 0, - width: terminalWidth(), - height: statusBarHeight - ) - result.add(statusBar) - - let convoPane = Pane( - xStart: 0, - yStart: statusBar.yStart + statusBar.height, - width: convoWidth, - height: terminalHeight() - statusBar.height - footerHeight - 1 - ) - result.add(convoPane) - - - let msgPane = Pane( - xStart: convoPane.width, - yStart: statusBar.yStart + statusBar.height, - width: terminalWidth() - convoPane.width, - height: convoPane.height - inboxHeight - ) - result.add(msgPane) - - let msgInputPane = Pane( - xStart: convoPane.width, - yStart: msgPane.yStart + msgPane.height, - width: msgPane.width, - height: inboxHeight - ) - result.add(msgInputPane) - - let footerPane = Pane( - xStart: 0, - yStart: convoPane.yStart + convoPane.height, - width: int(terminalWidth()), - height: footerHeight - ) - result.add(footerPane) - - - let modalPane = Pane( - xStart: modalXOffset, - yStart: int(terminalHeight()/2 - modalHeight/2), - width: int(terminalWidth() - 2*modalXOffset), - height: int(modalHeight) - ) - result.add(modalPane) - - -proc offsetPane(pane: Pane): Pane = - result = Pane( - xStart: pane.xStart , - yStart: pane.yStart + 1, - width: pane.width , - height: pane.height - 2 - ) diff --git a/examples/tui/persistence.nim b/examples/tui/persistence.nim deleted file mode 100644 index cfcfe46..0000000 --- a/examples/tui/persistence.nim +++ /dev/null @@ -1,135 +0,0 @@ -import chronicles -import chronos -import libp2p/crypto/crypto -import sequtils -import std/json -import std/jsonutils except distinctBase -import std/marshal -import std/options -import std/os -import std/streams -import strformat -import strutils -import tables - -import chat/crypto/ecdh -import chat/delivery/waku_client -import chat/identity - - -const REGISTRATION_DIR = ".registry" -const KEY_DIR = ".savedkeys" - - -type Config* = object - ident*: Identity - waku*: WakuConfig - -type SavedConfig* = object - name*: string - idkey*: seq[byte] - nodekey*: seq[byte] - port*: uint16 - clusterId*: uint16 - shardId*: seq[uint16] - pubsubTopic*: string - staticPeers*: seq[string] - - -proc toWakuConfig(s: SavedConfig): WakuConfig = - result = WakuConfig( - nodekey: crypto.PrivateKey.init(s.nodekey).get(), - port: s.port, - clusterId: s.clusterId, - shardId: s.shardId, - pubsubTopic: s.pubsubTopic, - staticPeers: s.staticPeers - ) - - -proc toIdent(s: SavedConfig): Identity = - result = Identity( - name: s.name, - privateKey: loadPrivateKeyFromBytes(s.idkey).get() - ) - - -proc register(name: string, multiAddr: string) {.async.} = - - notice "Registering Account", name=name, maddr=multiAddr - - if not dirExists(REGISTRATION_DIR): - createDir(REGISTRATION_DIR) - - try: - writeFile(joinPath(REGISTRATION_DIR, fmt"{name.toLower()}.maddr"), multiAddr) - except IOError as e: - echo "Failed to write registration file: ", e.msg - raise e - - -proc fetchRegistrations*(): Table[string, string] = - - let allFiles = toSeq(walkFiles(fmt"{REGISTRATION_DIR}/*")) - result = allFiles.mapIt((splitFile(it)[1], readFile(it).strip())).toTable() - - -proc loadCfg(file: string): Option[Config] = - let data = parseFile(file) - - let cfg = Config( - ident: toIdent(data.to(SavedConfig)), - waku: toWakuConfig(data.to(SavedConfig)) - ) - - result = some(cfg) - - -proc fetchCfg(name: string): Option[Config ] = - let allFiles = toSeq(walkFiles(fmt"{KEY_DIR}/*")) - - for file in allFiles: - if name == splitFile(file)[1]: - return loadCfg(file) - return none(Config) - - -proc saveCfg(name:string, cfg: Config) = - - let s = SavedConfig( - name: name, - idkey: cfg.ident.privatekey.bytes().toSeq(), - nodekey: cfg.waku.nodekey.getBytes().get(), - port: cfg.waku.port, - clusterId: cfg.waku.clusterId, - shardId: cfg.waku.shardId, - pubsubTopic: cfg.waku.pubsubTopic, - staticPeers: cfg.waku.staticPeers - ) - - let json = jsonutils.toJson(s) - - - if not dirExists(KEY_DIR): - createDir(KEY_DIR) - - try: - writeFile(joinPath(KEY_DIR, fmt"{name.toLower()}.cfg"), $json) - except IOError as e: - echo "Failed to write cfg file: ", e.msg - raise e - - -proc getCfg*(name: string): Future[Config] {.async.} = - let cfgOpt = fetchCfg(name) - if cfgOpt.isSome: - result = cfgOpt.get() - else: - let newCfg = Config( - ident: createIdentity(name), - waku: DefaultConfig() - ) - saveCfg(name, newCfg) - await register(name, newCfg.waku.getMultiAddr()) - - result = newCfg diff --git a/examples/tui/tui.nim b/examples/tui/tui.nim deleted file mode 100644 index 010f17d..0000000 --- a/examples/tui/tui.nim +++ /dev/null @@ -1,584 +0,0 @@ - -import algorithm -import chronicles -import chronos -import illwill -import libp2p/crypto/crypto -import times -import strformat -import strutils -import sugar -import tables - -import chat -import content_types/all - -import layout -import persistence -import utils - -const charVert = "│" -const charHoriz = "─" -const charTopLeft = "┌" -const charTopRight = "┐" -const charBottomLeft = "└" -const charBottomRight = "┘" - -type - - LogEntry = object - level: string - ts: DateTime - msg: string - - Message = object - sender: string - content: string - timestamp: DateTime - id: string - isAcknowledged: bool - - ConvoInfo = object - name: string - convo: Conversation - messages: seq[Message] - lastMsgTime*: DateTime - isTooLong*: bool - - ChatApp = ref object - client: Client - tb: TerminalBuffer - conversations: Table[string, ConvoInfo] - selectedConv: string - inputBuffer: string - inputInviteBuffer: string - scrollOffset: int - messageScrollOffset: int - inviteModal: bool - - currentInviteLink: string - isInviteReady: bool - peerCount: int - - logMsgs: seq[LogEntry] - -proc `==`(a,b: ConvoInfo):bool = - if a.name==b.name: - true - else: - false - - -################################################# -# Data Management -################################################# - -proc addMessage(conv: var ConvoInfo, messageId: MessageId, sender: string, content: string) = - - let now = now() - conv.messages.add(Message( - sender: sender, - id: messageId, - content: content, - timestamp: now, - isAcknowledged: false - )) - conv.lastMsgTime = now - - -proc mostRecentConvos(app: ChatApp): seq[ConvoInfo] = - var convos = collect(for v in app.conversations.values: v) - convos.sort(proc(a, b: ConvoInfo): int = -cmp(a.lastMsgTime, b.lastMsgTime)) - - return convos - -proc getSelectedConvo(app: ChatApp): ptr ConvoInfo = - if app.conversations.hasKey(app.selectedConv): - return addr app.conversations[app.selectedConv] - - return addr app.conversations[app.mostRecentConvos()[0].name] - - - -################################################# -# ChatSDK Setup -################################################# - -proc createChatClient(name: string): Future[Client] {.async.} = - var cfg = await getCfg(name) - result = newClient(cfg.waku, cfg.ident) - - -proc createInviteLink(app: var ChatApp): string = - app.client.createIntroBundle().toLink() - - -proc createConvo(app: ChatApp) {.async.} = - discard await app.client.newPrivateConversation(toBundle(app.inputInviteBuffer.strip()).get()) - -proc sendMessage(app: ChatApp, convoInfo: ptr ConvoInfo, msg: string) {.async.} = - - var msgId = "" - if convoInfo.convo != nil: - msgId = await convoInfo.convo.sendMessage(initTextFrame(msg).toContentFrame()) - - convoInfo[].addMessage(msgId, "You", app.inputBuffer) - - -proc setupChatSdk(app: ChatApp) = - - let client = app.client - - app.client.onNewMessage(proc(convo: Conversation, msg: ReceivedMessage) {.async.} = - info "New Message: ", convoId = convo.id(), msg= msg - app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: "NewMsg")) - - var contentStr = case msg.content.contentType - of text: - decode(msg.content.bytes, TextFrame).get().text - of unknown: - "" - - app.conversations[convo.id()].messages.add(Message(sender: msg.sender.toHex(), content: contentStr, timestamp: now())) - ) - - app.client.onNewConversation(proc(convo: Conversation) {.async.} = - app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt"Adding Convo: {convo.id()}")) - info "New Conversation: ", convoId = convo.id() - - app.conversations[convo.id()] = ConvoInfo(name: convo.id(), convo: convo, messages: @[], lastMsgTime: now(), isTooLong: false) - ) - - app.client.onDeliveryAck(proc(convo: Conversation, msgId: string) {.async.} = - info "DeliveryAck", msgId=msgId - app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt"Ack:{msgId}")) - - var s = "" - var msgs = addr app.conversations[convo.id()].messages - for i in countdown(msgs[].high, 0): - s = fmt"{s},{msgs[i].id}" - var m = addr msgs[i] - - if m.id == msgId: - m.isAcknowledged = true - break # Stop after - ) - - -################################################# -# Draw Funcs -################################################# - -proc resetTuiCursor(tb: var TerminalBuffer) = - tb.setForegroundColor(fgWhite) - tb.setBackgroundColor(bgBlack) - - -proc drawOutline(tb: var TerminalBuffer, layout: Pane, color: ForegroundColor, - bg: BackgroundColor = bgBlack) = - - for x in layout.xStart+1.. yEnd: - convo.isTooLong = true - app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt" TOO LONG: {convo.name}")) - - return - tb.write(x, y, fmt"[{timeStr}] {deliveryIcon} {m.sender}") - y = y + 1 - - while remainingText.len > 0: - if y > yEnd: - convo.isTooLong = true - app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt" TOO LON2: {convo.name}")) - - return - - let (line, remain) = remainingText.splitAt(maxContentWidth) - remainingText = remain - - tb.write(x+3, y, line) - y = y + 1 - y = y + 1 - else: - var y = yEnd - for i in countdown(convo.messages.len-1, 0): - - let m = convo.messages[i] - let timeStr = m.timestamp.format("HH:mm:ss") - var deliveryIcon = " " - var remainingText = m.content - - if m.sender == "You": - tb.setForegroundColor(fgYellow) - deliveryIcon = if m.isAcknowledged: "✔" else: "◯" - else: - tb.setForegroundColor(fgGreen) - - # Print lines in reverse order - while remainingText.len > 0: - if (y <= yStart + 1): - return - var lineLen = remainingText.len mod maxContentWidth - if lineLen == 0: - lineLen = maxContentWidth - - let line = remainingText[^lineLen..^1] - remainingText = remainingText[0..^lineLen+1] - - tb.write(x+3, y, line) - y = y - 1 - - tb.write(x, y, fmt"[{timeStr}] {deliveryIcon} {m.sender}") - y = y - 2 - -proc drawMsgInput( app: ChatApp, layout: Pane) = - var tb = app.tb - drawOutline(tb, layout, fgCyan) - - - let inputY = layout.yStart + 2 - let paneStart = layout.xStart - - # Draw input prompt - tb.write(paneStart + 1, inputY, " > " & app.inputBuffer, fgWhite) - - # Draw cursor - let cursorX = paneStart + 3 + app.inputBuffer.len + 1 - if cursorX < paneStart + layout.width - 1: - tb.write(cursorX, inputY, "_", fgYellow) - - discard - -proc drawFooter( app: ChatApp, layout: Pane) = - var tb = app.tb - drawOutline(tb, layout, fgBlue) - - let xStart = layout.xStart + 3 - let yStart = layout.yStart + 2 - - for i in countdown(app.logMsgs.len - 1, 0): - let o = app.logMsgs[i] - let timeStr = o.ts.format("HH:mm:ss") - let s = fmt"[{timeStr}] {o.level} - {o.msg}" - app.tb.write( xStart, yStart+i*2, s ) - discard - - -proc drawModal(app: ChatApp, layout: Pane, - color: ForegroundColor, bg: BackgroundColor = bgBlack) = - - var tb = app.tb - tb.setForegroundColor(color) - tb.setBackgroundColor(bg) - for x in layout.xStart.. " & app.inputInviteBuffer) - - # Draw cursor - let cursorX = layout.xStart+5+1 + app.inputInviteBuffer.len - if cursorX < terminalWidth() - 1: - tb.write(cursorX, layout.yStart+inputLine, "_", fgYellow) - - tb.setForegroundColor(fgBlack) - tb.setBackgroundColor(bgGreen) - tb.write(layout.xStart+5, layout.yStart+inputLine+3, "InviteLink: " & app.currentInviteLink) - - resetTuiCursor(tb) - -################################################# -# Input Handling -################################################# - -proc gePreviousConv(app: ChatApp): string = - let convos = app.mostRecentConvos() - let i = convos.find(app.getSelectedConvo()[]) - - return convos[max(i-1, 0)].name - - -proc getNextConv(app: ChatApp): string = - let convos = app.mostRecentConvos() - var s = "" - for c in convos: - s = s & c.name - - let i = convos.find(app.getSelectedConvo()[]) - app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt"i:{convos[min(i+1, convos.len-1)].isTooLong}")) - - return convos[min(i+1, convos.len-1)].name - - -proc handleInput(app: ChatApp, key: Key) {.async.} = - case key - of Key.Up: - app.selectedConv = app.gePreviousConv() - of Key.Down: - app.selectedConv = app.getNextConv() - - of Key.PageUp: - app.messageScrollOffset = min(app.messageScrollOffset + 5, 0) - of Key.PageDown: - app.messageScrollOffset = max(app.messageScrollOffset - 5, - -(max(0, app.getSelectedConvo().messages.len - 10))) - of Key.Enter: - - if app.inviteModal: - notice "Enter Invite", link= app.inputInviteBuffer - app.inviteModal = false - app.isInviteReady = true - else: - if app.inputBuffer.len > 0 and app.conversations.len > 0: - - let sc = app.getSelectedConvo() - await app.sendMessage(sc, app.inputBuffer) - - app.inputBuffer = "" - app.messageScrollOffset = 0 # Auto-scroll to bottom - of Key.Backspace: - if app.inputBuffer.len > 0: - app.inputBuffer.setLen(app.inputBuffer.len - 1) - of Key.Tab: - app.inviteModal = not app.inviteModal - - if app.inviteModal: - app.currentInviteLink = app.client.createIntroBundle().toLink() - of Key.Escape, Key.CtrlC: - quit(0) - else: - # Handle regular character input - let ch = char(key) - if ch.isAlphaNumeric() or ch in " !@#$%^&*()_+-=[]{}|;':\",./<>?": - if app.inviteModal: - app.inputInviteBuffer.add(ch) - else: - app.inputBuffer.add(ch) - - -################################################# -# Tasks -################################################# - -proc appLoop(app: ChatApp, panes: seq[Pane]) : Future[void] {.async.} = - illwillInit(fullscreen = false) - # Clear buffer - while true: - await sleepAsync(chronos.milliseconds(5)) - app.tb.clear() - - drawStatusBar(app, panes[0], fgBlack, getIdColor(app.client.getId())) - drawConversationPane(app, panes[1]) - drawMsgPane(app, panes[2]) - - if app.inviteModal: - drawModal(app, panes[5], fgYellow, bgGreen) - else: - drawMsgInput(app, panes[3]) - - drawFooter(app, panes[4]) - - # Draw help text - app.tb.write(1, terminalHeight()-1, "Tab: Invite Modal | ↑/↓: Select conversation | PgUp/PgDn: Scroll messages | Enter: Send | Esc: Quit", fgGreen) - - # Display buffer - app.tb.display() - - # Handle input - let key = getKey() - await handleInput(app, key) - - if app.isInviteReady: - - try: - let sanitized = app.inputInviteBuffer.replace(" ", "").replace("\r","") - discard await app.client.newPrivateConversation(toBundle(app.inputInviteBuffer.strip()).get()) - - except Exception as e: - info "bad invite", invite = app.inputInviteBuffer - app.inputInviteBuffer = "" - app.isInviteReady = false - -proc peerWatch(app: ChatApp): Future[void] {.async.} = - while true: - await sleepAsync(chronos.seconds(1)) - app.peerCount = app.client.ds.getConnectedPeerCount() - - -################################################# -# Main -################################################# - -proc initChatApp(client: Client): Future[ChatApp] {.async.} = - - var app = ChatApp( - client: client, - tb: newTerminalBuffer(terminalWidth(), terminalHeight()), - conversations: initTable[string, ConvoInfo](), - selectedConv: "Bob", - inputBuffer: "", - scrollOffset: 0, - messageScrollOffset: 0, - isInviteReady: false, - peerCount: -1, - logMsgs: @[] - ) - - app.setupChatSdk() - await app.client.start() - - - # Add some sample conversations with messages - var sender = "Nobody" - var conv1 = ConvoInfo(name: "ReadMe", messages: @[]) - conv1.addMessage("",sender, "First start multiple clients and ensure, that he PeerCount is correct (it's listed in the top left corner)") - conv1.addMessage("",sender, "Once connected, The sender needs to get the recipients introduction link. The links contains the key material and information required to initialize a conversation. Press `Tab` to generate a link") - conv1.addMessage("",sender, "Paste the link from one client into another. This will start the initialization protocol, which will send an invite to the recipient and negotiate a conversation") - conv1.addMessage("",sender, "Once established, Applications are notified by a callback that a new conversation has been established, and participants can send messages") - - - - app.conversations[conv1.name] = conv1 - - return app - - -proc main*() {.async.} = - - let args = getCmdArgs() - let client = await createChatClient(args.username) - var app = await initChatApp(client) - - let tasks: seq[Future[void]] = @[ - appLoop(app, getPanes()), - peerWatch(app) - ] - - discard await allFinished(tasks) - -when isMainModule: - waitFor main() - -# this is not nim code \ No newline at end of file diff --git a/examples/tui/utils.nim b/examples/tui/utils.nim deleted file mode 100644 index 8b56ca4..0000000 --- a/examples/tui/utils.nim +++ /dev/null @@ -1,53 +0,0 @@ -import std/parseopt -import illwill -import times - -################################################# -# Command Line Args -################################################# - -type CmdArgs* = object - username*: string - invite*: string - -proc getCmdArgs*(): CmdArgs = - var username = "" - var invite = "" - for kind, key, val in getopt(): - case kind - of cmdArgument: - discard - of cmdLongOption, cmdShortOption: - case key - of "name", "n": - username = val - of "invite", "i": - invite = val - of cmdEnd: - break - if username == "": - username = "" - - result = CmdArgs(username: username, invite:invite) - - -################################################# -# Utils -################################################# - -proc getIdColor*(id: string): BackgroundColor = - var i = ord(id[0]) - - - let colors = @[bgCyan, - bgGreen, - bgMagenta, - bgRed, - bgYellow, - bgBlue - ] - - return colors[i mod colors.len] - -proc toStr*(ts: DateTime): string = - ts.format("HH:mm:ss") \ No newline at end of file