rcurses 6.0.2 → 6.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2c123f55e8e4f06030596e1d16aca219f068152bf5d723db7a7aac5d98f1b6d
4
- data.tar.gz: 710f29dc036627434e650a7380c8ab8845c4fe075f185a70dfcc4274e84d3f36
3
+ metadata.gz: 5132248f03d0f62fed76d2f525b0a84bf512ba0454054623e4d8528b435d9f64
4
+ data.tar.gz: 11ab84d968557065955885eb27d188677889d78e5ee46acc1a62064bc9b27116
5
5
  SHA512:
6
- metadata.gz: 75f5d56983cab919f679e84123bbe3287a05356fd3c3cfc93def62f0273a386638cf8ca692ffc384e1db00377d6aa75f27bbad355547543649dd82284ef6a661
7
- data.tar.gz: e3bc08762e5fbed4f53149eab64b2543b0c6d145388b734037b65fa0eafc20887f4f71945725a5b1e5cf68b3a9b0cda3c5b6a4ea8215dbc853e5a10a5caca782
6
+ metadata.gz: 12c461a57d353bc5a7273de1a664676a95c2d9ef445096e08222b447b45a1b0b847e9cc1570e475046e24f49264cabb1ae9b9c1e2944e09639bd9b2c881e20b6
7
+ data.tar.gz: 8602247bbf7c6e6377349f68dc7c2f8ff422f28890886ad5772a5aa1f5b6aed7acd1dcee8d1a66afeb7cf563dc4da832dc3caf4225922e1ffa7d6591f9204d28
data/README.md CHANGED
@@ -94,7 +94,7 @@ Method | Description
94
94
  new/init | Initializes a Pane with optional arguments `x, y, w, h, fg and bg`
95
95
  move(x,y) | Move the pane by `x`and `y` (`mypane.move(-4,5)` will move the pane left four characters and five characters down)
96
96
  refresh | Refreshes/redraws the Pane with content
97
- border_refresh | Refresh the Pane border only
97
+ border_refresh | Refresh the Pane border only - **CRITICAL**: Use this after changing border property
98
98
  full_refresh | Refreshes/redraws the Pane with content completely (without diff rendering)
99
99
  edit | An editor for the Pane. When this is invoked, all existing font dressing is stripped and the user gets to edit the raw text. The user can add font effects similar to Markdown; Use an asterisk before and after text to be drawn in bold, text between forward-slashes become italic, and underline before and after text means the text will be underlined, a hash-sign before and after text makes the text reverse colored. You can also combine a whole set of dressings in this format: `<23,245,biurl\|Hello World!>` - this will make "Hello World!" print in the color 23 with the background color 245 (regardless of the Pane's fg/bg setting) in bold, italic, underlined, reversed colored and blinking. Hitting `ESC` while in edit mode will disregard the edits, while `Ctrl-S` will save the edits
100
100
  editline | Used for one-line Panes. It will print the content of the property `prompt` and then the property `text` that can then be edited by the user. Hitting `ESC` will disregard the edits, while `ENTER` will save the edited text
@@ -242,6 +242,119 @@ end
242
242
  You can also pass a timeout to `getchr` with `getchr(time)` to wait for `time` number of seconds and returning `nil` if the user does not press a key.
243
243
 
244
244
 
245
+ # Proper Initialization and Common Pitfalls
246
+
247
+ ## Critical: Proper Application Initialization
248
+
249
+ When building TUI applications with rcurses, follow this exact initialization pattern to avoid blank/frozen screens:
250
+
251
+ ```ruby
252
+ #!/usr/bin/env ruby
253
+ require 'rcurses'
254
+ require 'io/wait' # CRITICAL for stdin flush
255
+
256
+ class MyApp
257
+ include Rcurses
258
+ include Rcurses::Input
259
+
260
+ def run
261
+ # 1. Initialize rcurses FIRST
262
+ Rcurses.init!
263
+
264
+ # 2. Get terminal size
265
+ @h, @w = IO.console.winsize
266
+
267
+ # 3. Create your panes
268
+ @pane_top = Pane.new(1, 1, @w, 1, 255, 235)
269
+ @pane_left = Pane.new(1, 3, @w/3, @h-4, 15, 0)
270
+ @pane_right = Pane.new(@w/3+2, 3, @w-@w/3-2, @h-4, 255, 0)
271
+
272
+ # 4. Load initial data and render
273
+ load_data
274
+ render_all
275
+
276
+ # 5. CRITICAL: Flush stdin before main loop
277
+ # This prevents cursor position requests from blocking initial render
278
+ $stdin.getc while $stdin.wait_readable(0)
279
+
280
+ # 6. Main loop pattern
281
+ loop do
282
+ render_all # Render first
283
+ chr = getchr # Then get input
284
+ handle_input(chr) # Then handle it
285
+ break if chr == 'q'
286
+ end
287
+ ensure
288
+ # Clean up
289
+ Cursor.show
290
+ end
291
+
292
+ def render_all
293
+ # Use text= and refresh, NOT say for initial renders
294
+ @pane_top.text = "My Application"
295
+ @pane_top.refresh
296
+
297
+ @pane_left.text = @content
298
+ @pane_left.refresh
299
+ end
300
+ end
301
+ ```
302
+
303
+ ## Common Pitfalls and Solutions
304
+
305
+ ### 1. Blank Panes on Startup
306
+ **Problem:** All panes appear blank until user input.
307
+ **Cause:** Terminal sends cursor position request (`\e[6n`) that blocks rendering.
308
+ **Solution:** Always flush stdin before main loop: `$stdin.getc while $stdin.wait_readable(0)`
309
+
310
+ ### 2. Using `say` vs `text=` and `refresh`
311
+ **When to use `say`:**
312
+ - For status messages and one-time updates
313
+ - When you want to reset scroll position (ix = 0)
314
+
315
+ **When to use `text=` and `refresh`:**
316
+ - In render loops where you control scrolling
317
+ - When you need to preserve scroll position
318
+
319
+ ```ruby
320
+ # For render loops - preserves scroll
321
+ @pane.text = content
322
+ @pane.ix = saved_scroll_position # Maintain scroll
323
+ @pane.refresh
324
+
325
+ # For status messages - resets scroll
326
+ @pane.say("Processing...")
327
+ ```
328
+
329
+ ### 3. Double Rendering
330
+ **Problem:** Calling both `say` and `refresh` causes double rendering.
331
+ **Solution:** `say` already calls refresh internally - don't call refresh after say.
332
+
333
+ ### 4. Missing require 'io/wait'
334
+ **Problem:** `wait_readable` method not found.
335
+ **Solution:** Always include `require 'io/wait'` for stdin flush.
336
+
337
+ ### 5. Border Changes Not Visible
338
+ **Problem:** Changing pane border property doesn't show visual changes.
339
+ **Cause:** Border changes require explicit refresh to be displayed.
340
+ **Solution:** Use `border_refresh` method after changing border property.
341
+
342
+ ```ruby
343
+ # Wrong - border change not visible
344
+ @pane.border = true
345
+
346
+ # Correct - border change visible immediately
347
+ @pane.border = true
348
+ @pane.border_refresh
349
+ ```
350
+
351
+ ## Reference Applications
352
+
353
+ Study these applications for best practices:
354
+ - [RTFM](https://github.com/isene/RTFM) - File manager with complex pane management
355
+ - [GiTerm](https://github.com/isene/GiTerm) - Git interface with dynamic content
356
+ - [T-REX](https://github.com/isene/T-REX) - Calculator with simpler UI
357
+
245
358
  # Example
246
359
 
247
360
  Try this in `irb`:
data/lib/rcurses.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # Web_site: http://isene.com/
6
6
  # Github: https://github.com/isene/rcurses
7
7
  # License: Public domain
8
- # Version: 6.0.0: Breaking change - no auto-init, apps must call Rcurses.init!
8
+ # Version: 6.1.0: Added safe_gsub methods to prevent ANSI code corruption
9
9
 
10
10
  require 'io/console' # Basic gem for rcurses
11
11
  require 'io/wait' # stdin handling
@@ -156,5 +156,55 @@ class String
156
156
 
157
157
  out
158
158
  end
159
+
160
+ # Safely apply regex replacements without corrupting ANSI sequences
161
+ # This method temporarily removes ANSI codes, applies the regex, then restores them
162
+ def safe_gsub(pattern, replacement = nil, &block)
163
+ # Store all ANSI sequences and replace with placeholders
164
+ ansi_sequences = []
165
+ placeholder_text = self.gsub(/\e\[[0-9;]*m/) do |match|
166
+ ansi_sequences << match
167
+ "⟨ANSI#{ansi_sequences.length - 1}⟩"
168
+ end
169
+
170
+ # Apply the regex to the placeholder text
171
+ result = if block_given?
172
+ placeholder_text.gsub(pattern, &block)
173
+ else
174
+ placeholder_text.gsub(pattern, replacement)
175
+ end
176
+
177
+ # Restore ANSI sequences
178
+ ansi_sequences.each_with_index do |ansi, index|
179
+ result.gsub!("⟨ANSI#{index}⟩", ansi)
180
+ end
181
+
182
+ result
183
+ end
184
+
185
+ # Safe version of gsub! that modifies in place
186
+ def safe_gsub!(pattern, replacement = nil, &block)
187
+ result = safe_gsub(pattern, replacement, &block)
188
+ self.replace(result)
189
+ end
190
+
191
+ # Check if string contains ANSI codes
192
+ def has_ansi?
193
+ !!(self =~ /\e\[[0-9;]*m/)
194
+ end
195
+
196
+ # Get the visible length (without ANSI codes)
197
+ def visible_length
198
+ pure.length
199
+ end
200
+
201
+ # Apply color only if not already colored
202
+ def safe_fg(color)
203
+ has_ansi? ? self : fg(color)
204
+ end
205
+
206
+ def safe_bg(color)
207
+ has_ansi? ? self : bg(color)
208
+ end
159
209
  end
160
210
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcurses
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.2
4
+ version: 6.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-20 00:00:00.000000000 Z
11
+ date: 2025-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clipboard