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.
@@ -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
- $stderr.puts "[tui-td MCP] Server started, awaiting JSON-RPC on stdin..."
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, -32700, "Parse error: #{e.message}")
53
+ error_response(nil, -32_700, "Parse error: #{e.message}")
52
54
  rescue StandardError => e
53
- $stderr.puts "[tui-td MCP] Error: #{e.class}: #{e.message}"
54
- $stderr.puts e.backtrace.first(5).join("\n ") if $DEBUG
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 # No response needed
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 # Ignore unknown notifications
81
+ nil # Ignore unknown notifications
80
82
  else
81
- error_response(id, -32601, "Method not found: #{method}")
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(params, id)
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: ["enter", "tab", "escape", "up", "down", "left", "right",
163
- "backspace", "ctrl_c", "ctrl_d", "ctrl_z", "page_up", "page_down",
164
- "home", "end", "delete"],
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: ["ai", "full", "text"],
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" then call_tui_state(args)
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, -32602, "Unknown tool: #{tool_name}")
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(args)
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
@@ -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 == 10095 # '❯'
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 char_ord == 9210 || char_ord == 9679 # '⏺' or '●'
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 >= 0x2800 && char_ord <= 0x28ff # Braille spinner
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 < 0 || idx + 15 >= FONT.length
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 == 0
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 >= 0x2500 && char_ord <= 0x257F
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) ? :heavy : (char_ord == 0x2550 ? :double : :light)
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) ? :heavy : (char_ord == 0x2551 ? :double : :light)
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
- if style == :double
469
+ case style
470
+ when :double
458
471
  if left
459
- (px..(cx + 2)).each { |x| image[x, py + 6] = color }
460
- (px..(cx + 2)).each { |x| image[x, py + 10] = color }
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 { |y| image[px + 2, y] = color }
468
- (py..(cy + 2)).each { |y| image[px + 6, y] = color }
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 { |y| image[px + 2, y] = color }
472
- ((cy - 2)..(py + 15)).each { |y| image[px + 6, y] = color }
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
- elsif style == :heavy
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
- elsif style == :light_rounded
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
- (px..cx).each { |x| image[x, cy] = color }
529
- end
530
- if right
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 < 0 || ri >= @rows || ci < 0 || ci >= @cols
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] # Dot 8
659
+ [5, 12], # Dot 8
643
660
  ]
644
661
  dot_coords.each_with_index do |(dx, dy), idx|
645
- if (mask & (1 << idx)) != 0
646
- 2.times do |ddy|
647
- 2.times do |ddx|
648
- x = px + dx + ddx
649
- y = py + dy + ddy
650
- next if x >= image.width || y >= image.height
651
- image[x, y] = color
652
- end
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