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 +4 -4
- data/README.md +114 -1
- data/lib/rcurses.rb +1 -1
- data/lib/string_extensions.rb +50 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5132248f03d0f62fed76d2f525b0a84bf512ba0454054623e4d8528b435d9f64
|
4
|
+
data.tar.gz: 11ab84d968557065955885eb27d188677889d78e5ee46acc1a62064bc9b27116
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
data/lib/string_extensions.rb
CHANGED
@@ -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
|
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-
|
11
|
+
date: 2025-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|