@emblemvault/agentwallet 1.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/tui.js ADDED
@@ -0,0 +1,171 @@
1
+ /**
2
+ * TUI Core — Blessed-based terminal UI layout
3
+ * Main screen, panels, focus management, key bindings
4
+ */
5
+
6
+ import blessed from 'blessed';
7
+
8
+ /**
9
+ * Create the full TUI layout.
10
+ * @param {object} opts - Optional overrides
11
+ * @returns {{ screen, panels, destroy }}
12
+ */
13
+ export function createTUI(opts = {}) {
14
+ const screen = blessed.screen({
15
+ smartCSR: false,
16
+ fastCSR: false,
17
+ title: opts.title || 'EMBLEM AI - Agent Command & Control',
18
+ mouse: true,
19
+ fullUnicode: false,
20
+ autoPadding: false,
21
+ warnings: false,
22
+ });
23
+
24
+ // ── Banner — top row, full width, single line ────────────────────────
25
+ const bannerBox = blessed.box({
26
+ parent: screen,
27
+ top: 0,
28
+ left: 0,
29
+ width: '100%',
30
+ height: 1,
31
+ tags: true,
32
+ style: {
33
+ fg: 'cyan',
34
+ bg: 'black',
35
+ bold: true,
36
+ },
37
+ });
38
+
39
+ // ── Sidebar — left 25%, from row 1 to bottom-4 ──────────────────────
40
+ const sidebarBox = blessed.box({
41
+ parent: screen,
42
+ top: 1,
43
+ left: 0,
44
+ width: '25%',
45
+ height: '100%-5',
46
+ border: { type: 'line' },
47
+ label: ' Plugins ',
48
+ tags: true,
49
+ scrollable: true,
50
+ alwaysScroll: true,
51
+ mouse: true,
52
+ keys: true,
53
+ vi: true,
54
+ style: {
55
+ border: { fg: 'gray' },
56
+ label: { fg: 'cyan', bold: true },
57
+ scrollbar: { fg: 'cyan' },
58
+ },
59
+ scrollbar: {
60
+ ch: '|',
61
+ style: { fg: 'cyan' },
62
+ },
63
+ });
64
+
65
+ // ── Chat — right 75%, from row 1 to bottom-4 ────────────────────────
66
+ const chatBox = blessed.box({
67
+ parent: screen,
68
+ top: 1,
69
+ left: '25%',
70
+ width: '75%',
71
+ height: '100%-5',
72
+ border: { type: 'line' },
73
+ label: ' Chat ',
74
+ tags: true,
75
+ scrollable: true,
76
+ alwaysScroll: true,
77
+ mouse: true,
78
+ keys: true,
79
+ vi: true,
80
+ scrollbar: {
81
+ ch: '|',
82
+ style: { fg: 'cyan' },
83
+ },
84
+ style: {
85
+ border: { fg: 'gray' },
86
+ label: { fg: 'cyan', bold: true },
87
+ },
88
+ });
89
+
90
+ // ── Event Log — left 25%, bottom 3 rows ──────────────────────────────
91
+ const eventLogBox = blessed.box({
92
+ parent: screen,
93
+ bottom: 0,
94
+ left: 0,
95
+ width: '25%',
96
+ height: 3,
97
+ border: { type: 'line' },
98
+ label: ' Log ',
99
+ tags: true,
100
+ scrollable: true,
101
+ alwaysScroll: true,
102
+ mouse: true,
103
+ style: {
104
+ border: { fg: 'gray' },
105
+ label: { fg: 'yellow', bold: true },
106
+ },
107
+ });
108
+
109
+ // ── Input — right 75%, bottom 3 rows ─────────────────────────────────
110
+ const inputBox = blessed.textarea({
111
+ parent: screen,
112
+ bottom: 0,
113
+ left: '25%',
114
+ width: '75%',
115
+ height: 3,
116
+ border: { type: 'line' },
117
+ label: ' You ',
118
+ inputOnFocus: true,
119
+ mouse: true,
120
+ keys: true,
121
+ style: {
122
+ border: { fg: 'cyan' },
123
+ label: { fg: 'cyan', bold: true },
124
+ focus: {
125
+ border: { fg: 'white' },
126
+ },
127
+ },
128
+ });
129
+
130
+ // ── Focus cycling: Tab ────────────────────────────────────────────────
131
+ const focusable = [inputBox, chatBox, sidebarBox];
132
+ let focusIndex = 0;
133
+
134
+ screen.key(['tab'], () => {
135
+ focusIndex = (focusIndex + 1) % focusable.length;
136
+ focusable[focusIndex].focus();
137
+ screen.render();
138
+ });
139
+
140
+ screen.key(['S-tab'], () => {
141
+ focusIndex = (focusIndex - 1 + focusable.length) % focusable.length;
142
+ focusable[focusIndex].focus();
143
+ screen.render();
144
+ });
145
+
146
+ // ── Exit: Ctrl+C ─────────────────────────────────────────────────────
147
+ screen.key(['C-c'], () => {
148
+ screen.destroy();
149
+ process.exit(0);
150
+ });
151
+
152
+ // ── Start focused on input ────────────────────────────────────────────
153
+ inputBox.focus();
154
+ screen.render();
155
+
156
+ const panels = {
157
+ banner: bannerBox,
158
+ sidebar: sidebarBox,
159
+ chat: chatBox,
160
+ eventLog: eventLogBox,
161
+ input: inputBox,
162
+ };
163
+
164
+ return {
165
+ screen,
166
+ panels,
167
+ destroy() {
168
+ screen.destroy();
169
+ },
170
+ };
171
+ }