tui-td 0.2.10 → 0.2.11
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/tui_td/ansi_parser.rb +139 -131
- data/lib/tui_td/ansi_utils.rb +22 -20
- data/lib/tui_td/cairo_renderer.rb +5 -2
- data/lib/tui_td/cli.rb +12 -10
- data/lib/tui_td/driver.rb +43 -12
- data/lib/tui_td/html_renderer.rb +19 -17
- data/lib/tui_td/matchers.rb +21 -12
- data/lib/tui_td/mcp/server.rb +87 -84
- data/lib/tui_td/screenshot.rb +70 -52
- data/lib/tui_td/state.rb +11 -4
- data/lib/tui_td/test_runner.rb +25 -25
- data/lib/tui_td/unifont_glyphs.rb +2142 -2141
- data/lib/tui_td/version.rb +1 -1
- data/lib/tui_td.rb +7 -3
- metadata +40 -11
data/lib/tui_td/mcp/server.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# rubocop:disable Metrics/ClassLength, Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Layout/LineLength
|
|
4
|
+
|
|
3
5
|
require "json"
|
|
4
6
|
|
|
5
7
|
module TUITD
|
|
@@ -35,7 +37,7 @@ module TUITD
|
|
|
35
37
|
$stderr.sync = true
|
|
36
38
|
|
|
37
39
|
# Signal readiness
|
|
38
|
-
|
|
40
|
+
warn "[tui-td MCP] Server started, awaiting JSON-RPC on stdin..."
|
|
39
41
|
|
|
40
42
|
while @running && (line = $stdin.gets)
|
|
41
43
|
line = line.strip
|
|
@@ -48,10 +50,10 @@ module TUITD
|
|
|
48
50
|
puts JSON.generate(response) if response
|
|
49
51
|
$stdout.flush
|
|
50
52
|
rescue JSON::ParserError => e
|
|
51
|
-
error_response(nil, -
|
|
53
|
+
error_response(nil, -32_700, "Parse error: #{e.message}")
|
|
52
54
|
rescue StandardError => e
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
warn "[tui-td MCP] Error: #{e.class}: #{e.message}"
|
|
56
|
+
warn e.backtrace.first(5).join("\n ") if $DEBUG
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
@@ -69,35 +71,35 @@ module TUITD
|
|
|
69
71
|
when "initialize"
|
|
70
72
|
handle_initialize(params, id)
|
|
71
73
|
when "notifications/initialized"
|
|
72
|
-
nil
|
|
74
|
+
nil # No response needed
|
|
73
75
|
when "tools/list"
|
|
74
76
|
handle_tools_list(id)
|
|
75
77
|
when "tools/call"
|
|
76
78
|
handle_tools_call(params, id)
|
|
77
79
|
else
|
|
78
80
|
if method&.start_with?("notifications/")
|
|
79
|
-
nil
|
|
81
|
+
nil # Ignore unknown notifications
|
|
80
82
|
else
|
|
81
|
-
error_response(id, -
|
|
83
|
+
error_response(id, -32_601, "Method not found: #{method}")
|
|
82
84
|
end
|
|
83
85
|
end
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
# Initialize handshake
|
|
87
|
-
def handle_initialize(
|
|
89
|
+
def handle_initialize(_params, id)
|
|
88
90
|
{
|
|
89
91
|
jsonrpc: "2.0",
|
|
90
92
|
id: id,
|
|
91
93
|
result: {
|
|
92
94
|
protocolVersion: PROTOCOL_VERSION,
|
|
93
95
|
capabilities: {
|
|
94
|
-
tools: {}
|
|
96
|
+
tools: {},
|
|
95
97
|
},
|
|
96
98
|
serverInfo: {
|
|
97
99
|
name: SERVER_NAME,
|
|
98
|
-
version: SERVER_VERSION
|
|
99
|
-
}
|
|
100
|
-
}
|
|
100
|
+
version: SERVER_VERSION,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
101
103
|
}
|
|
102
104
|
end
|
|
103
105
|
|
|
@@ -116,26 +118,26 @@ module TUITD
|
|
|
116
118
|
properties: {
|
|
117
119
|
command: {
|
|
118
120
|
type: "string",
|
|
119
|
-
description: "The command to run (e.g., 'htop', 'vim file.txt')"
|
|
121
|
+
description: "The command to run (e.g., 'htop', 'vim file.txt')",
|
|
120
122
|
},
|
|
121
123
|
rows: {
|
|
122
124
|
type: "integer",
|
|
123
125
|
description: "Terminal height in rows (default: 40)",
|
|
124
|
-
default: 40
|
|
126
|
+
default: 40,
|
|
125
127
|
},
|
|
126
128
|
cols: {
|
|
127
129
|
type: "integer",
|
|
128
130
|
description: "Terminal width in columns (default: 120)",
|
|
129
|
-
default: 120
|
|
131
|
+
default: 120,
|
|
130
132
|
},
|
|
131
133
|
timeout: {
|
|
132
134
|
type: "integer",
|
|
133
135
|
description: "Timeout in seconds for waits (default: 30)",
|
|
134
|
-
default: 30
|
|
135
|
-
}
|
|
136
|
+
default: 30,
|
|
137
|
+
},
|
|
136
138
|
},
|
|
137
|
-
required: ["command"]
|
|
138
|
-
}
|
|
139
|
+
required: ["command"],
|
|
140
|
+
},
|
|
139
141
|
},
|
|
140
142
|
{
|
|
141
143
|
name: "tui_send",
|
|
@@ -145,11 +147,11 @@ module TUITD
|
|
|
145
147
|
properties: {
|
|
146
148
|
text: {
|
|
147
149
|
type: "string",
|
|
148
|
-
description: "Text to send to the TUI. Use \\n for enter/newline."
|
|
149
|
-
}
|
|
150
|
+
description: "Text to send to the TUI. Use \\n for enter/newline.",
|
|
151
|
+
},
|
|
150
152
|
},
|
|
151
|
-
required: ["text"]
|
|
152
|
-
}
|
|
153
|
+
required: ["text"],
|
|
154
|
+
},
|
|
153
155
|
},
|
|
154
156
|
{
|
|
155
157
|
name: "tui_send_key",
|
|
@@ -159,14 +161,14 @@ module TUITD
|
|
|
159
161
|
properties: {
|
|
160
162
|
key: {
|
|
161
163
|
type: "string",
|
|
162
|
-
enum: [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
description: "Key to press"
|
|
166
|
-
}
|
|
164
|
+
enum: %w[enter tab escape up down left right
|
|
165
|
+
backspace ctrl_c ctrl_d ctrl_z page_up page_down
|
|
166
|
+
home end delete],
|
|
167
|
+
description: "Key to press",
|
|
168
|
+
},
|
|
167
169
|
},
|
|
168
|
-
required: ["key"]
|
|
169
|
-
}
|
|
170
|
+
required: ["key"],
|
|
171
|
+
},
|
|
170
172
|
},
|
|
171
173
|
{
|
|
172
174
|
name: "tui_wait_for_text",
|
|
@@ -176,16 +178,16 @@ module TUITD
|
|
|
176
178
|
properties: {
|
|
177
179
|
text: {
|
|
178
180
|
type: "string",
|
|
179
|
-
description: "Text to wait for in the terminal output"
|
|
181
|
+
description: "Text to wait for in the terminal output",
|
|
180
182
|
},
|
|
181
183
|
timeout: {
|
|
182
184
|
type: "integer",
|
|
183
185
|
description: "Custom timeout in seconds (overrides default)",
|
|
184
|
-
default: 30
|
|
185
|
-
}
|
|
186
|
+
default: 30,
|
|
187
|
+
},
|
|
186
188
|
},
|
|
187
|
-
required: ["text"]
|
|
188
|
-
}
|
|
189
|
+
required: ["text"],
|
|
190
|
+
},
|
|
189
191
|
},
|
|
190
192
|
{
|
|
191
193
|
name: "tui_wait_for_stable",
|
|
@@ -196,10 +198,10 @@ module TUITD
|
|
|
196
198
|
timeout: {
|
|
197
199
|
type: "integer",
|
|
198
200
|
description: "Custom timeout in seconds",
|
|
199
|
-
default: 30
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
201
|
+
default: 30,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
203
205
|
},
|
|
204
206
|
{
|
|
205
207
|
name: "tui_state",
|
|
@@ -209,20 +211,20 @@ module TUITD
|
|
|
209
211
|
properties: {
|
|
210
212
|
format: {
|
|
211
213
|
type: "string",
|
|
212
|
-
enum: [
|
|
214
|
+
enum: %w[ai full text],
|
|
213
215
|
description: "Output format: 'ai' (compact, text+highlights, default), 'full' (complete cell grid with ANSI colors), 'text' (plain text only)",
|
|
214
|
-
default: "ai"
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
216
|
+
default: "ai",
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
218
220
|
},
|
|
219
221
|
{
|
|
220
222
|
name: "tui_plain_text",
|
|
221
223
|
description: "Get the current terminal content as plain text (all ANSI stripped).",
|
|
222
224
|
inputSchema: {
|
|
223
225
|
type: "object",
|
|
224
|
-
properties: {}
|
|
225
|
-
}
|
|
226
|
+
properties: {},
|
|
227
|
+
},
|
|
226
228
|
},
|
|
227
229
|
{
|
|
228
230
|
name: "tui_screenshot",
|
|
@@ -232,10 +234,10 @@ module TUITD
|
|
|
232
234
|
properties: {
|
|
233
235
|
path: {
|
|
234
236
|
type: "string",
|
|
235
|
-
description: "Output file path (optional, auto-generated if omitted)"
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
237
|
+
description: "Output file path (optional, auto-generated if omitted)",
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
239
241
|
},
|
|
240
242
|
{
|
|
241
243
|
name: "tui_html_render",
|
|
@@ -245,26 +247,26 @@ module TUITD
|
|
|
245
247
|
properties: {
|
|
246
248
|
path: {
|
|
247
249
|
type: "string",
|
|
248
|
-
description: "Optional file path to save the HTML. If omitted, the HTML content is returned inline so you can view it directly."
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
250
|
+
description: "Optional file path to save the HTML. If omitted, the HTML content is returned inline so you can view it directly.",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
252
254
|
},
|
|
253
255
|
{
|
|
254
256
|
name: "tui_wait_for_exit",
|
|
255
257
|
description: "Wait until the TUI process exits. Returns the exit status code (0 = success, non-zero = error).",
|
|
256
258
|
inputSchema: {
|
|
257
259
|
type: "object",
|
|
258
|
-
properties: {}
|
|
259
|
-
}
|
|
260
|
+
properties: {},
|
|
261
|
+
},
|
|
260
262
|
},
|
|
261
263
|
{
|
|
262
264
|
name: "tui_exit_status",
|
|
263
265
|
description: "Get the exit status of the TUI process. Returns nil if still running, otherwise the exit code.",
|
|
264
266
|
inputSchema: {
|
|
265
267
|
type: "object",
|
|
266
|
-
properties: {}
|
|
267
|
-
}
|
|
268
|
+
properties: {},
|
|
269
|
+
},
|
|
268
270
|
},
|
|
269
271
|
{
|
|
270
272
|
name: "tui_find_text",
|
|
@@ -274,22 +276,22 @@ module TUITD
|
|
|
274
276
|
properties: {
|
|
275
277
|
pattern: {
|
|
276
278
|
type: "string",
|
|
277
|
-
description: "Text or regex pattern to search for (e.g., 'error', 'ERROR|FAIL')"
|
|
278
|
-
}
|
|
279
|
+
description: "Text or regex pattern to search for (e.g., 'error', 'ERROR|FAIL')",
|
|
280
|
+
},
|
|
279
281
|
},
|
|
280
|
-
required: ["pattern"]
|
|
281
|
-
}
|
|
282
|
+
required: ["pattern"],
|
|
283
|
+
},
|
|
282
284
|
},
|
|
283
285
|
{
|
|
284
286
|
name: "tui_close",
|
|
285
287
|
description: "Close the TUI application and clean up the PTY session. Call this when finished.",
|
|
286
288
|
inputSchema: {
|
|
287
289
|
type: "object",
|
|
288
|
-
properties: {}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
]
|
|
292
|
-
}
|
|
290
|
+
properties: {},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
},
|
|
293
295
|
}
|
|
294
296
|
end
|
|
295
297
|
|
|
@@ -304,7 +306,7 @@ module TUITD
|
|
|
304
306
|
when "tui_send_key" then call_tui_send_key(args)
|
|
305
307
|
when "tui_wait_for_text" then call_tui_wait_for_text(args)
|
|
306
308
|
when "tui_wait_for_stable" then call_tui_wait_for_stable(args)
|
|
307
|
-
when "tui_state"
|
|
309
|
+
when "tui_state" then call_tui_state(args)
|
|
308
310
|
when "tui_plain_text" then call_tui_plain_text
|
|
309
311
|
when "tui_screenshot" then call_tui_screenshot(args)
|
|
310
312
|
when "tui_html_render" then call_tui_html_render(args)
|
|
@@ -313,7 +315,7 @@ module TUITD
|
|
|
313
315
|
when "tui_find_text" then call_tui_find_text(args)
|
|
314
316
|
when "tui_close" then call_tui_close
|
|
315
317
|
else
|
|
316
|
-
return error_response(id, -
|
|
318
|
+
return error_response(id, -32_602, "Unknown tool: #{tool_name}")
|
|
317
319
|
end
|
|
318
320
|
|
|
319
321
|
{
|
|
@@ -323,10 +325,10 @@ module TUITD
|
|
|
323
325
|
content: [
|
|
324
326
|
{
|
|
325
327
|
type: "text",
|
|
326
|
-
text: result
|
|
327
|
-
}
|
|
328
|
-
]
|
|
329
|
-
}
|
|
328
|
+
text: result,
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
},
|
|
330
332
|
}
|
|
331
333
|
rescue TUITD::TimeoutError => e
|
|
332
334
|
{
|
|
@@ -336,11 +338,11 @@ module TUITD
|
|
|
336
338
|
content: [
|
|
337
339
|
{
|
|
338
340
|
type: "text",
|
|
339
|
-
text: "TIMEOUT: #{e.message}"
|
|
340
|
-
}
|
|
341
|
+
text: "TIMEOUT: #{e.message}",
|
|
342
|
+
},
|
|
341
343
|
],
|
|
342
|
-
isError: false
|
|
343
|
-
}
|
|
344
|
+
isError: false,
|
|
345
|
+
},
|
|
344
346
|
}
|
|
345
347
|
rescue StandardError => e
|
|
346
348
|
{
|
|
@@ -350,11 +352,11 @@ module TUITD
|
|
|
350
352
|
content: [
|
|
351
353
|
{
|
|
352
354
|
type: "text",
|
|
353
|
-
text: "ERROR: #{e.class}: #{e.message}"
|
|
354
|
-
}
|
|
355
|
+
text: "ERROR: #{e.class}: #{e.message}",
|
|
356
|
+
},
|
|
355
357
|
],
|
|
356
|
-
isError: true
|
|
357
|
-
}
|
|
358
|
+
isError: true,
|
|
359
|
+
},
|
|
358
360
|
}
|
|
359
361
|
end
|
|
360
362
|
|
|
@@ -400,7 +402,7 @@ module TUITD
|
|
|
400
402
|
state_to_ai_text(state_data)
|
|
401
403
|
end
|
|
402
404
|
|
|
403
|
-
def call_tui_wait_for_stable(
|
|
405
|
+
def call_tui_wait_for_stable(_args)
|
|
404
406
|
ensure_driver!
|
|
405
407
|
@driver.wait_for_stable
|
|
406
408
|
|
|
@@ -521,10 +523,11 @@ module TUITD
|
|
|
521
523
|
id: id,
|
|
522
524
|
error: {
|
|
523
525
|
code: code,
|
|
524
|
-
message: message
|
|
525
|
-
}
|
|
526
|
+
message: message,
|
|
527
|
+
},
|
|
526
528
|
}
|
|
527
529
|
end
|
|
528
530
|
end
|
|
529
531
|
end
|
|
530
532
|
end
|
|
533
|
+
# rubocop:enable Metrics/ClassLength, Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Layout/LineLength
|
data/lib/tui_td/screenshot.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockNesting, Metrics/ParameterLists, Metrics/ClassLength, Metrics/CollectionLiteralLength
|
|
4
|
+
|
|
3
5
|
require "chunky_png"
|
|
4
6
|
require_relative "ansi_utils"
|
|
5
7
|
require_relative "cairo_renderer"
|
|
@@ -200,7 +202,7 @@ module TUITD
|
|
|
200
202
|
"╧" => [true, false, true, true, :double],
|
|
201
203
|
"╨" => [true, false, true, true, :double],
|
|
202
204
|
"╪" => [true, true, true, true, :double],
|
|
203
|
-
"╫" => [true, true, true, true, :double]
|
|
205
|
+
"╫" => [true, true, true, true, :double],
|
|
204
206
|
}.freeze
|
|
205
207
|
|
|
206
208
|
private_constant :FONT
|
|
@@ -219,8 +221,10 @@ module TUITD
|
|
|
219
221
|
|
|
220
222
|
@grid.each_with_index do |row, ri|
|
|
221
223
|
next unless row
|
|
224
|
+
|
|
222
225
|
row.each_with_index do |cell, ci|
|
|
223
226
|
next unless cell
|
|
227
|
+
|
|
224
228
|
render_cell(image, ri, ci, cell)
|
|
225
229
|
end
|
|
226
230
|
end
|
|
@@ -256,15 +260,15 @@ module TUITD
|
|
|
256
260
|
end
|
|
257
261
|
|
|
258
262
|
char_ord = char.ord
|
|
259
|
-
if char_ord ==
|
|
263
|
+
if char_ord == 10_095 # '❯'
|
|
260
264
|
draw_chevron(image, px, py, fg_rgb)
|
|
261
265
|
draw_underline(image, px, py, CELL_W, fg_rgb) if underline
|
|
262
266
|
return
|
|
263
|
-
elsif
|
|
267
|
+
elsif [9210, 9679].include?(char_ord) # '⏺' or '●'
|
|
264
268
|
draw_circle(image, px, py, fg_rgb)
|
|
265
269
|
draw_underline(image, px, py, CELL_W, fg_rgb) if underline
|
|
266
270
|
return
|
|
267
|
-
elsif char_ord
|
|
271
|
+
elsif char_ord.between?(0x2800, 0x28ff) # Braille spinner
|
|
268
272
|
draw_braille(image, px, py, char, fg_rgb)
|
|
269
273
|
draw_underline(image, px, py, CELL_W, fg_rgb) if underline
|
|
270
274
|
return
|
|
@@ -399,7 +403,7 @@ module TUITD
|
|
|
399
403
|
|
|
400
404
|
def glyph_rows(char)
|
|
401
405
|
idx = (char.ord - 32) * 16
|
|
402
|
-
return nil if idx
|
|
406
|
+
return nil if idx.negative? || idx + 15 >= FONT.length
|
|
403
407
|
|
|
404
408
|
FONT[idx, 16]
|
|
405
409
|
end
|
|
@@ -408,7 +412,7 @@ module TUITD
|
|
|
408
412
|
color = ChunkyPNG::Color.rgb(*fg_rgb)
|
|
409
413
|
|
|
410
414
|
rows.each_with_index do |byte, dy|
|
|
411
|
-
next if byte
|
|
415
|
+
next if byte.zero?
|
|
412
416
|
|
|
413
417
|
slant = italic ? dy / 8 : 0
|
|
414
418
|
|
|
@@ -429,7 +433,7 @@ module TUITD
|
|
|
429
433
|
|
|
430
434
|
def box_drawing?(char)
|
|
431
435
|
char_ord = char.ord
|
|
432
|
-
char_ord
|
|
436
|
+
char_ord.between?(0x2500, 0x257F)
|
|
433
437
|
end
|
|
434
438
|
|
|
435
439
|
def draw_box_character(image, px, py, char, fg_rgb)
|
|
@@ -438,10 +442,18 @@ module TUITD
|
|
|
438
442
|
unless config
|
|
439
443
|
char_ord = char.ord
|
|
440
444
|
if [0x2500, 0x2501, 0x2504, 0x2505, 0x2508, 0x2509, 0x254c, 0x254d, 0x2550].include?(char_ord)
|
|
441
|
-
style = [0x2501, 0x2505, 0x2509, 0x254d].include?(char_ord)
|
|
445
|
+
style = if [0x2501, 0x2505, 0x2509, 0x254d].include?(char_ord)
|
|
446
|
+
:heavy
|
|
447
|
+
else
|
|
448
|
+
(char_ord == 0x2550 ? :double : :light)
|
|
449
|
+
end
|
|
442
450
|
config = [false, false, true, true, style]
|
|
443
451
|
elsif [0x2502, 0x2503, 0x2506, 0x2507, 0x250a, 0x250b, 0x254e, 0x254f, 0x2551].include?(char_ord)
|
|
444
|
-
style = [0x2503, 0x2507, 0x250b, 0x254f].include?(char_ord)
|
|
452
|
+
style = if [0x2503, 0x2507, 0x250b, 0x254f].include?(char_ord)
|
|
453
|
+
:heavy
|
|
454
|
+
else
|
|
455
|
+
(char_ord == 0x2551 ? :double : :light)
|
|
456
|
+
end
|
|
445
457
|
config = [true, true, false, false, style]
|
|
446
458
|
else
|
|
447
459
|
config = [true, true, true, true, :light]
|
|
@@ -454,24 +466,31 @@ module TUITD
|
|
|
454
466
|
|
|
455
467
|
color = ChunkyPNG::Color.rgb(*fg_rgb)
|
|
456
468
|
|
|
457
|
-
|
|
469
|
+
case style
|
|
470
|
+
when :double
|
|
458
471
|
if left
|
|
459
|
-
(px..(cx + 2)).each
|
|
460
|
-
|
|
472
|
+
(px..(cx + 2)).each do |x|
|
|
473
|
+
image[x, py + 6] = color
|
|
474
|
+
image[x, py + 10] = color
|
|
475
|
+
end
|
|
461
476
|
end
|
|
462
477
|
if right
|
|
463
478
|
((cx - 2)..(px + 7)).each { |x| image[x, py + 6] = color }
|
|
464
479
|
((cx - 2)..(px + 10)).each { |x| image[x, py + 10] = color }
|
|
465
480
|
end
|
|
466
481
|
if up
|
|
467
|
-
(py..(cy + 2)).each
|
|
468
|
-
|
|
482
|
+
(py..(cy + 2)).each do |y|
|
|
483
|
+
image[px + 2, y] = color
|
|
484
|
+
image[px + 6, y] = color
|
|
485
|
+
end
|
|
469
486
|
end
|
|
470
487
|
if down
|
|
471
|
-
((cy - 2)..(py + 15)).each
|
|
472
|
-
|
|
488
|
+
((cy - 2)..(py + 15)).each do |y|
|
|
489
|
+
image[px + 2, y] = color
|
|
490
|
+
image[px + 6, y] = color
|
|
491
|
+
end
|
|
473
492
|
end
|
|
474
|
-
|
|
493
|
+
when :heavy
|
|
475
494
|
if left
|
|
476
495
|
(px..cx).each do |x|
|
|
477
496
|
image[x, cy - 1] = color
|
|
@@ -500,42 +519,34 @@ module TUITD
|
|
|
500
519
|
image[cx + 1, y] = color
|
|
501
520
|
end
|
|
502
521
|
end
|
|
503
|
-
|
|
522
|
+
when :light_rounded
|
|
504
523
|
case char
|
|
505
524
|
when "╭"
|
|
506
|
-
(px + 5..px + 7).each { |x| image[x, py + 8] = color }
|
|
507
|
-
(py + 10..py + 15).each { |y| image[px + 4, y] = color }
|
|
525
|
+
((px + 5)..(px + 7)).each { |x| image[x, py + 8] = color }
|
|
526
|
+
((py + 10)..(py + 15)).each { |y| image[px + 4, y] = color }
|
|
508
527
|
image[px + 4, py + 9] = color
|
|
509
528
|
image[px + 5, py + 9] = color
|
|
510
529
|
when "╮"
|
|
511
|
-
(px..px + 3).each { |x| image[x, py + 8] = color }
|
|
512
|
-
(py + 10..py + 15).each { |y| image[px + 4, y] = color }
|
|
530
|
+
(px..(px + 3)).each { |x| image[x, py + 8] = color }
|
|
531
|
+
((py + 10)..(py + 15)).each { |y| image[px + 4, y] = color }
|
|
513
532
|
image[px + 4, py + 9] = color
|
|
514
533
|
image[px + 3, py + 9] = color
|
|
515
534
|
when "╯"
|
|
516
|
-
(px..px + 3).each { |x| image[x, py + 8] = color }
|
|
517
|
-
(py..py + 6).each { |y| image[px + 4, y] = color }
|
|
535
|
+
(px..(px + 3)).each { |x| image[x, py + 8] = color }
|
|
536
|
+
(py..(py + 6)).each { |y| image[px + 4, y] = color }
|
|
518
537
|
image[px + 4, py + 7] = color
|
|
519
538
|
image[px + 3, py + 7] = color
|
|
520
539
|
when "╰"
|
|
521
|
-
(px + 5..px + 7).each { |x| image[x, py + 8] = color }
|
|
522
|
-
(py..py + 6).each { |y| image[px + 4, y] = color }
|
|
540
|
+
((px + 5)..(px + 7)).each { |x| image[x, py + 8] = color }
|
|
541
|
+
(py..(py + 6)).each { |y| image[px + 4, y] = color }
|
|
523
542
|
image[px + 4, py + 7] = color
|
|
524
543
|
image[px + 5, py + 7] = color
|
|
525
544
|
end
|
|
526
545
|
else # :light
|
|
527
|
-
if left
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if
|
|
531
|
-
(cx..(px + 7)).each { |x| image[x, cy] = color }
|
|
532
|
-
end
|
|
533
|
-
if up
|
|
534
|
-
(py..cy).each { |y| image[cx, y] = color }
|
|
535
|
-
end
|
|
536
|
-
if down
|
|
537
|
-
(cy..(py + 15)).each { |y| image[cx, y] = color }
|
|
538
|
-
end
|
|
546
|
+
(px..cx).each { |x| image[x, cy] = color } if left
|
|
547
|
+
(cx..(px + 7)).each { |x| image[x, cy] = color } if right
|
|
548
|
+
(py..cy).each { |y| image[cx, y] = color } if up
|
|
549
|
+
(cy..(py + 15)).each { |y| image[cx, y] = color } if down
|
|
539
550
|
end
|
|
540
551
|
end
|
|
541
552
|
|
|
@@ -551,7 +562,7 @@ module TUITD
|
|
|
551
562
|
ri = cursor_info[:row] || cursor_info["row"] || 0
|
|
552
563
|
ci = cursor_info[:col] || cursor_info["col"] || 0
|
|
553
564
|
|
|
554
|
-
return if ri
|
|
565
|
+
return if ri.negative? || ri >= @rows || ci.negative? || ci >= @cols
|
|
555
566
|
|
|
556
567
|
style_val = @state[:cursor_style] || cursor_info[:style] || cursor_info["style"] || 1
|
|
557
568
|
|
|
@@ -568,6 +579,7 @@ module TUITD
|
|
|
568
579
|
x = px + dx
|
|
569
580
|
y = py + dy
|
|
570
581
|
next if x >= image.width || y >= image.height
|
|
582
|
+
|
|
571
583
|
original_color = image[x, y]
|
|
572
584
|
r = 255 - ChunkyPNG::Color.r(original_color)
|
|
573
585
|
g = 255 - ChunkyPNG::Color.g(original_color)
|
|
@@ -579,9 +591,11 @@ module TUITD
|
|
|
579
591
|
2.times do |h_offset|
|
|
580
592
|
y = py + CELL_H - 1 - h_offset
|
|
581
593
|
next if y >= image.height
|
|
594
|
+
|
|
582
595
|
CELL_W.times do |dx|
|
|
583
596
|
x = px + dx
|
|
584
597
|
next if x >= image.width
|
|
598
|
+
|
|
585
599
|
image[x, y] = color
|
|
586
600
|
end
|
|
587
601
|
end
|
|
@@ -589,9 +603,11 @@ module TUITD
|
|
|
589
603
|
2.times do |w_offset|
|
|
590
604
|
x = px + w_offset
|
|
591
605
|
next if x >= image.width
|
|
606
|
+
|
|
592
607
|
CELL_H.times do |dy|
|
|
593
608
|
y = py + dy
|
|
594
609
|
next if y >= image.height
|
|
610
|
+
|
|
595
611
|
image[x, y] = color
|
|
596
612
|
end
|
|
597
613
|
end
|
|
@@ -603,7 +619,7 @@ module TUITD
|
|
|
603
619
|
(0..3).each do |i|
|
|
604
620
|
image[px + 2 + i, py + 4 + i] = color
|
|
605
621
|
image[px + 3 + i, py + 4 + i] = color # bold/thick chevron
|
|
606
|
-
|
|
622
|
+
|
|
607
623
|
image[px + 5 - i, py + 8 + i] = color
|
|
608
624
|
image[px + 6 - i, py + 8 + i] = color # bold/thick chevron
|
|
609
625
|
end
|
|
@@ -623,6 +639,7 @@ module TUITD
|
|
|
623
639
|
x = cx + dx
|
|
624
640
|
y = cy + dy
|
|
625
641
|
next if x < px || x >= px + CELL_W || y < py || y >= py + CELL_H
|
|
642
|
+
|
|
626
643
|
image[x, y] = color
|
|
627
644
|
end
|
|
628
645
|
end
|
|
@@ -639,17 +656,18 @@ module TUITD
|
|
|
639
656
|
[5, 6], # Dot 5
|
|
640
657
|
[5, 9], # Dot 6
|
|
641
658
|
[2, 12], # Dot 7
|
|
642
|
-
[5, 12]
|
|
659
|
+
[5, 12], # Dot 8
|
|
643
660
|
]
|
|
644
661
|
dot_coords.each_with_index do |(dx, dy), idx|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
662
|
+
next unless mask.anybits?(1 << idx)
|
|
663
|
+
|
|
664
|
+
2.times do |ddy|
|
|
665
|
+
2.times do |ddx|
|
|
666
|
+
x = px + dx + ddx
|
|
667
|
+
y = py + dy + ddy
|
|
668
|
+
next if x >= image.width || y >= image.height
|
|
669
|
+
|
|
670
|
+
image[x, y] = color
|
|
653
671
|
end
|
|
654
672
|
end
|
|
655
673
|
end
|
|
@@ -659,7 +677,7 @@ module TUITD
|
|
|
659
677
|
color = ChunkyPNG::Color.rgb(*fg_rgb)
|
|
660
678
|
(5..8).each do |dy|
|
|
661
679
|
width = dy - 5
|
|
662
|
-
(4 - width..4 + width).each do |dx|
|
|
680
|
+
((4 - width)..(4 + width)).each do |dx|
|
|
663
681
|
image[px + dx, py + dy] = color
|
|
664
682
|
end
|
|
665
683
|
end
|
|
@@ -669,7 +687,7 @@ module TUITD
|
|
|
669
687
|
color = ChunkyPNG::Color.rgb(*fg_rgb)
|
|
670
688
|
(5..8).each do |dy|
|
|
671
689
|
width = 8 - dy
|
|
672
|
-
(4 - width..4 + width).each do |dx|
|
|
690
|
+
((4 - width)..(4 + width)).each do |dx|
|
|
673
691
|
image[px + dx, py + dy] = color
|
|
674
692
|
end
|
|
675
693
|
end
|
|
@@ -874,6 +892,6 @@ module TUITD
|
|
|
874
892
|
image[px + 3, py + 8] = color
|
|
875
893
|
image[px + 4, py + 8] = color
|
|
876
894
|
end
|
|
877
|
-
|
|
878
895
|
end
|
|
879
896
|
end
|
|
897
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockNesting, Metrics/ParameterLists, Metrics/ClassLength, Metrics/CollectionLiteralLength
|