datepick 1.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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +182 -0
  3. data/bin/datepick +10 -0
  4. data/lib/datepick.rb +501 -0
  5. metadata +64 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 42e1650fbe65ad40a8bab0a5629d85aa2b43a6b5be8087058058d1f826a05a03
4
+ data.tar.gz: eb804269ae096c685567eb409c5c40f158bf0ca4b71ee0c1567f02a1f8caf8af
5
+ SHA512:
6
+ metadata.gz: 924025949e01cb91dd1542542334007883646c5bbb51c6a373b28ff9f58df24eb49b19c5b5534d52fdf39145d64d73937fec5e2bf1b5843c101636bbf088baba
7
+ data.tar.gz: 1470fe30803a6860d468c86f1afbbbe1557ebb3a674f7b9ba7c08f964df7320c0e1cc67af57e2f16f271f9e3c741a81bb495970180870c25a8467a8bf5566f1b
data/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # Datepick - Interactive Terminal Date Picker
2
+
3
+ A powerful, interactive terminal date picker built with Ruby and rcurses. Features vim-style navigation, configurable date formats, multiple month views, and extensive keyboard shortcuts. Perfect for shell scripts and command-line workflows that need date selection.
4
+
5
+ ## Features
6
+
7
+ - **Interactive calendar display** with multiple months
8
+ - **Vim-style navigation** with hjkl keys and numeric prefixes
9
+ - **Configurable date output formats** with quick presets
10
+ - **Customizable multi-month view** (months before/after current)
11
+ - **Week start preference** (Monday or Sunday)
12
+ - **Visual highlights** for today, selected date, and weekends
13
+ - **Flicker-free rendering** using rcurses
14
+ - **Persistent configuration** saved to `~/.datepick`
15
+
16
+ ## Installation
17
+
18
+ Install from RubyGems:
19
+
20
+ ```bash
21
+ gem install datepick
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Simply run `datepick` in your terminal:
27
+
28
+ ```bash
29
+ datepick
30
+ ```
31
+
32
+ The selected date will be printed to stdout when you press Enter, making it perfect for shell scripts:
33
+
34
+ ```bash
35
+ # Capture selected date in a variable
36
+ selected_date=$(datepick)
37
+ echo "You selected: $selected_date"
38
+
39
+ # Use in file operations
40
+ cp important_file.txt "backup_$(datepick).txt"
41
+ ```
42
+
43
+ ## Keyboard Navigation
44
+
45
+ ### Basic Movement
46
+ - **Arrow keys** or **hjkl** - Navigate between dates
47
+ - **Enter** - Select current date and exit
48
+ - **q** - Quit without selecting
49
+
50
+ ### Advanced Navigation
51
+ - **n/p** - Next/Previous month
52
+ - **N/P** - Next/Previous year
53
+ - **t** - Jump to today
54
+ - **H/^** - Go to start of week
55
+ - **L/$** - Go to end of week
56
+ - **Home** - Go to start of month
57
+ - **End** - Go to end of month
58
+
59
+ ### Vim-style Jumps
60
+ - **[number]g** - Jump forward by number of days
61
+ - Example: `7g` jumps 7 days ahead
62
+ - Example: `30g` jumps 30 days ahead
63
+
64
+ ### Configuration
65
+ - **c** - Enter configuration mode
66
+ - **r** - Force refresh display
67
+
68
+ ## Date Format Options
69
+
70
+ When configuring date format, you can either:
71
+
72
+ 1. **Use quick presets** by entering a number (1-8):
73
+ - `1`: 2025-07-01 (ISO format)
74
+ - `2`: 01/07/2025 (European)
75
+ - `3`: 07/01/2025 (US format)
76
+ - `4`: July 01, 2025 (Long format)
77
+ - `5`: Jul 01, 2025 (Abbreviated)
78
+ - `6`: 20250701 (Compact)
79
+ - `7`: 01-Jul-2025 (DD-Mon-YYYY)
80
+ - `8`: Tuesday, July 01, 2025 (Full with weekday)
81
+
82
+ 2. **Enter custom format** using Ruby's strftime syntax:
83
+ - `%Y-%m-%d` - ISO format (default)
84
+ - `%d/%m/%Y` - European format
85
+ - `%B %d, %Y` - Long format
86
+ - See [Ruby strftime documentation](https://ruby-doc.org/core/Time.html#method-i-strftime) for all options
87
+
88
+ ## Configuration Options
89
+
90
+ Access configuration by pressing `c`:
91
+
92
+ - **Date format**: Output format for selected date
93
+ - **Months before**: Number of months to show before current month
94
+ - **Months after**: Number of months to show after current month
95
+ - **Week starts Monday**: Toggle between Monday/Sunday week start
96
+
97
+ Configuration is automatically saved to `~/.datepick` and persists between sessions.
98
+
99
+ ## Visual Features
100
+
101
+ - **Current month**: Bold and underlined month header
102
+ - **Today's date**: Highlighted in magenta and bold
103
+ - **Selected date**: Yellow background with bold text
104
+ - **Weekends**: Red text for Saturday/Sunday
105
+ - **Day headers**: Bold with darker colors for better visibility
106
+
107
+ ## Shell Integration Examples
108
+
109
+ ### Bash Scripts
110
+ ```bash
111
+ #!/bin/bash
112
+ echo "Select a date for the backup:"
113
+ backup_date=$(datepick)
114
+ tar -czf "backup_${backup_date}.tar.gz" /important/files/
115
+ ```
116
+
117
+ ### ZSH Function
118
+ ```zsh
119
+ # Add to your .zshrc
120
+ function schedule() {
121
+ local date=$(datepick)
122
+ echo "Scheduled for: $date"
123
+ # Add your scheduling logic here
124
+ }
125
+ ```
126
+
127
+ ### Git Commits with Specific Dates
128
+ ```bash
129
+ # Select a date and create a commit with that date
130
+ commit_date=$(datepick)
131
+ git commit --date="$commit_date" -m "Your commit message"
132
+ ```
133
+
134
+ ## Requirements
135
+
136
+ - Ruby 2.7 or higher
137
+ - rcurses gem (automatically installed)
138
+ - Terminal with color support
139
+
140
+ ## Development
141
+
142
+ Clone the repository:
143
+ ```bash
144
+ git clone https://github.com/isene/datepick.git
145
+ cd datepick
146
+ ```
147
+
148
+ Install dependencies:
149
+ ```bash
150
+ bundle install
151
+ ```
152
+
153
+ Run locally:
154
+ ```bash
155
+ ruby -Ilib bin/datepick
156
+ ```
157
+
158
+ ## Contributing
159
+
160
+ 1. Fork the repository
161
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
162
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
163
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
164
+ 5. Open a Pull Request
165
+
166
+ ## Inspiration
167
+
168
+ Inspired by [pickdate](https://github.com/maraloon/pickdate) and designed to integrate seamlessly with terminal workflows.
169
+
170
+ ## License
171
+
172
+ This project is released under the Unlicense - see the project repository for details.
173
+
174
+ ## Author
175
+
176
+ Created by [Geir Isene](https://isene.com/)
177
+
178
+ ## Links
179
+
180
+ - **Homepage**: https://isene.com/
181
+ - **Source Code**: https://github.com/isene/datepick
182
+ - **RubyGems**: https://rubygems.org/gems/datepick
data/bin/datepick ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Add lib directory to load path
4
+ lib_dir = File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
6
+
7
+ require 'datepick'
8
+
9
+ # Run the date picker
10
+ DatePicker.new.run
data/lib/datepick.rb ADDED
@@ -0,0 +1,501 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'rcurses'
4
+
5
+ class DatePicker
6
+ include Rcurses::Input
7
+
8
+ CONFIG_FILE = File.expand_path('~/.datepick')
9
+
10
+ DEFAULT_CONFIG = {
11
+ 'date_format' => '%Y-%m-%d',
12
+ 'months_before' => 1,
13
+ 'months_after' => 1,
14
+ 'week_starts_monday' => true,
15
+ 'highlight_weekends' => true,
16
+ 'colors' => {
17
+ 'year' => 14, # cyan
18
+ 'month' => 10, # green
19
+ 'day' => 15, # white
20
+ 'selected' => 11, # yellow
21
+ 'today' => 13, # magenta
22
+ 'weekend' => 9 # red
23
+ }
24
+ }.freeze
25
+
26
+ # Common date formats for quick selection
27
+ DATE_FORMATS = {
28
+ '1' => '%Y-%m-%d', # ISO format
29
+ '2' => '%d/%m/%Y', # European
30
+ '3' => '%m/%d/%Y', # US format
31
+ '4' => '%B %d, %Y', # Long format
32
+ '5' => '%b %d, %Y', # Abbreviated
33
+ '6' => '%Y%m%d', # Compact
34
+ '7' => '%d-%b-%Y', # DD-Mon-YYYY
35
+ '8' => '%A, %B %d, %Y' # Full with weekday
36
+ }
37
+
38
+ def initialize
39
+ @config = load_config
40
+ @selected_date = Date.today
41
+ @current_month = Date.today
42
+ @config_mode = false
43
+ @config_selected = 0
44
+ @screen_w = `tput cols`.to_i
45
+ @screen_h = `tput lines`.to_i
46
+
47
+ # Initialize panes
48
+ @main_pane = Rcurses::Pane.new(1, 1, @screen_w, @screen_h - 4, nil, nil)
49
+ @main_pane.border = false
50
+
51
+ @help_pane = Rcurses::Pane.new(1, @screen_h - 3, @screen_w, 1, nil, nil)
52
+ @help_pane.border = false
53
+
54
+ @status_pane = Rcurses::Pane.new(1, @screen_h - 1, @screen_w, 1, nil, nil)
55
+ @status_pane.border = false
56
+
57
+ @prev_content = ""
58
+ end
59
+
60
+ def run
61
+ Rcurses.init!
62
+ Rcurses::Cursor.hide
63
+
64
+ begin
65
+ # Initial display
66
+ render
67
+
68
+ loop do
69
+ input = handle_input
70
+ break if input == :exit
71
+ render
72
+ end
73
+ rescue Interrupt
74
+ # Exit cleanly on Ctrl-C
75
+ ensure
76
+ Rcurses.cleanup!
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def load_config
83
+ if File.exist?(CONFIG_FILE)
84
+ JSON.parse(File.read(CONFIG_FILE))
85
+ else
86
+ DEFAULT_CONFIG.dup
87
+ end
88
+ rescue JSON::ParserError
89
+ DEFAULT_CONFIG.dup
90
+ end
91
+
92
+ def save_config
93
+ File.write(CONFIG_FILE, JSON.pretty_generate(@config))
94
+ end
95
+
96
+ def render
97
+ if @config_mode
98
+ render_config
99
+ else
100
+ render_calendar
101
+ end
102
+
103
+ # Update help text
104
+ help_text = if @config_mode
105
+ "Navigate: ↑↓ | Edit: Enter | Cancel: Esc".fg(245)
106
+ else
107
+ help_parts = []
108
+ help_parts << "←↓↑→/hjkl" if @numeric_prefix.nil? || @numeric_prefix.empty?
109
+ help_parts << "#{@numeric_prefix}g:jump #{@numeric_prefix} days" if @numeric_prefix && !@numeric_prefix.empty?
110
+ help_parts << "n/p:month | N/P:year | t:today | H/L:week | Home/End:month | Enter:select | c:config | q:quit"
111
+ help_parts.join(" | ").fg(245)
112
+ end
113
+
114
+ @help_pane.text = help_text
115
+ @help_pane.refresh
116
+
117
+ # Update status
118
+ status_text = "Selected: #{@selected_date.strftime(@config['date_format'])}".fg(@config['colors']['selected'])
119
+ @status_pane.text = status_text
120
+ @status_pane.refresh
121
+ end
122
+
123
+ def render_calendar
124
+ content = generate_calendar_content
125
+
126
+ # Only update if content changed
127
+ if content != @prev_content
128
+ @main_pane.text = content
129
+ @main_pane.refresh
130
+ @prev_content = content
131
+ end
132
+ end
133
+
134
+ def generate_calendar_content
135
+ lines = []
136
+ months_to_display = []
137
+
138
+ # Calculate range of months to display
139
+ start_month = @current_month.prev_month(@config['months_before'])
140
+ end_month = @current_month.next_month(@config['months_after'])
141
+
142
+ current = start_month
143
+ while current <= end_month
144
+ months_to_display << current
145
+ current = current.next_month
146
+ end
147
+
148
+ # Generate months horizontally
149
+ months_per_row = [(@screen_w - 4) / 22, 1].max
150
+
151
+ months_to_display.each_slice(months_per_row) do |month_group|
152
+ # Generate this row of months
153
+ month_lines = generate_month_row(month_group)
154
+ lines.concat(month_lines)
155
+ lines << "" # Add spacing between month rows
156
+ end
157
+
158
+ lines.join("\n")
159
+ end
160
+
161
+ def generate_month_row(months)
162
+ result_lines = []
163
+ max_weeks = 6
164
+
165
+ # Header line with month names
166
+ header_line = ""
167
+ months.each_with_index do |month_date, idx|
168
+ month_str = month_date.strftime("%B %Y")
169
+ # Highlight current month with bold and underline
170
+ if month_date.year == Date.today.year && month_date.month == Date.today.month
171
+ month_str = month_str.fg(@config['colors']['month']).b.u
172
+ else
173
+ month_str = month_str.fg(@config['colors']['month'])
174
+ end
175
+ header_line += month_str.ljust(22 + month_str.length - month_date.strftime("%B %Y").length)
176
+ end
177
+ result_lines << header_line
178
+
179
+ # Day headers
180
+ day_header_line = ""
181
+ months.each do |month_date|
182
+ days = @config['week_starts_monday'] ? %w[Mo Tu We Th Fr Sa Su] : %w[Su Mo Tu We Th Fr Sa]
183
+ days.each_with_index do |day, idx|
184
+ # Use darker colors for day headers and make them bold
185
+ color = ((@config['week_starts_monday'] && idx >= 5) || (!@config['week_starts_monday'] && (idx == 0 || idx == 6))) ?
186
+ 88 : 244 # Dark red for weekends, dark gray for weekdays
187
+ day_header_line += day.fg(color).b + " "
188
+ end
189
+ day_header_line += " " # Extra space between months
190
+ end
191
+ result_lines << day_header_line
192
+
193
+ # Generate week lines
194
+ week_data = months.map { |m| generate_month_weeks(m) }
195
+
196
+ (0...max_weeks).each do |week_idx|
197
+ week_line = ""
198
+ months.each_with_index do |month_date, month_idx|
199
+ week = week_data[month_idx][week_idx] || []
200
+
201
+ 7.times do |day_idx|
202
+ if week[day_idx]
203
+ date = week[day_idx]
204
+ day_str = date.day.to_s.rjust(2)
205
+
206
+ # Apply styling
207
+ if date == @selected_date
208
+ day_str = day_str.fb(@config['colors']['selected'], 236).b
209
+ elsif date == Date.today
210
+ day_str = day_str.fg(@config['colors']['today']).b
211
+ elsif date.saturday? || date.sunday?
212
+ day_str = day_str.fg(@config['colors']['weekend'])
213
+ else
214
+ day_str = day_str.fg(@config['colors']['day'])
215
+ end
216
+
217
+ week_line += day_str + " "
218
+ else
219
+ week_line += " "
220
+ end
221
+ end
222
+ week_line += " " # Extra space between months
223
+ end
224
+ result_lines << week_line unless week_line.strip.empty?
225
+ end
226
+
227
+ result_lines
228
+ end
229
+
230
+ def generate_month_weeks(month_date)
231
+ weeks = []
232
+ current_week = []
233
+
234
+ first_day = Date.new(month_date.year, month_date.month, 1)
235
+ last_day = Date.new(month_date.year, month_date.month, -1)
236
+
237
+ # Calculate starting position
238
+ wday = first_day.wday
239
+ if @config['week_starts_monday']
240
+ wday = (wday - 1) % 7
241
+ end
242
+
243
+ # Add empty days at the beginning
244
+ wday.times { current_week << nil }
245
+
246
+ # Add all days of the month
247
+ (first_day..last_day).each do |date|
248
+ current_week << date
249
+
250
+ # Check if week is complete
251
+ if @config['week_starts_monday']
252
+ if date.wday == 0 # Sunday
253
+ weeks << current_week
254
+ current_week = []
255
+ end
256
+ else
257
+ if date.wday == 6 # Saturday
258
+ weeks << current_week
259
+ current_week = []
260
+ end
261
+ end
262
+ end
263
+
264
+ # Add the last week if it has any days
265
+ weeks << current_week unless current_week.empty?
266
+
267
+ weeks
268
+ end
269
+
270
+ def render_config
271
+ config_items = [
272
+ ["Date format", @config['date_format']],
273
+ ["Months before", @config['months_before'].to_s],
274
+ ["Months after", @config['months_after'].to_s],
275
+ ["Week starts Monday", @config['week_starts_monday'] ? "Yes" : "No"],
276
+ ["Save and exit config", "Press Enter"]
277
+ ]
278
+
279
+ lines = []
280
+ lines << ""
281
+ lines << "Configuration".fg(@config['colors']['year']).b
282
+ lines << ""
283
+
284
+ config_items.each_with_index do |(label, value), idx|
285
+ line = " #{label}: #{value}"
286
+ if idx == @config_selected
287
+ line = line.fb(0, 15)
288
+ end
289
+ lines << line
290
+ lines << "" # Add spacing
291
+ end
292
+
293
+ content = lines.join("\n")
294
+
295
+ # Always refresh config screen to show updated values
296
+ @main_pane.text = content
297
+ @main_pane.refresh
298
+ @prev_content = content
299
+ end
300
+
301
+ def handle_input
302
+ ch = getchr
303
+
304
+ if @config_mode
305
+ handle_config_input(ch)
306
+ else
307
+ handle_calendar_input(ch)
308
+ end
309
+ end
310
+
311
+ def handle_calendar_input(ch)
312
+ # Reset numeric prefix for non-numeric keys (except 'g')
313
+ if ch !~ /[0-9g]/ && @numeric_prefix
314
+ @numeric_prefix = ""
315
+ end
316
+
317
+ case ch
318
+ when 'q', 'Q'
319
+ return :exit
320
+ when 'c', 'C'
321
+ @config_mode = true
322
+ @config_selected = 0
323
+ @prev_content = "" # Force refresh
324
+ when 'ENTER'
325
+ Rcurses.cleanup!
326
+ puts @selected_date.strftime(@config['date_format'])
327
+ exit
328
+ when 'LEFT', 'h', 'H'
329
+ @selected_date = @selected_date.prev_day
330
+ update_current_month
331
+ when 'RIGHT', 'l', 'L'
332
+ @selected_date = @selected_date.next_day
333
+ update_current_month
334
+ when 'UP', 'k', 'K'
335
+ @selected_date = @selected_date.prev_day(7)
336
+ update_current_month
337
+ when 'DOWN', 'j', 'J'
338
+ @selected_date = @selected_date.next_day(7)
339
+ update_current_month
340
+ when 'w', 'W'
341
+ @selected_date = @selected_date.next_day(7)
342
+ update_current_month
343
+ when 'b', 'B'
344
+ @selected_date = @selected_date.prev_day(7)
345
+ update_current_month
346
+ when 'n'
347
+ @selected_date = @selected_date.next_month
348
+ update_current_month
349
+ when 'p'
350
+ @selected_date = @selected_date.prev_month
351
+ update_current_month
352
+ when 'N'
353
+ @selected_date = @selected_date.next_year
354
+ update_current_month
355
+ when 'P'
356
+ @selected_date = @selected_date.prev_year
357
+ update_current_month
358
+ when 'H', '^'
359
+ # Go to start of week
360
+ days_back = @config['week_starts_monday'] ?
361
+ (@selected_date.wday == 0 ? 6 : @selected_date.wday - 1) :
362
+ @selected_date.wday
363
+ @selected_date = @selected_date.prev_day(days_back)
364
+ update_current_month
365
+ when 'L', '$'
366
+ # Go to end of week
367
+ days_forward = @config['week_starts_monday'] ?
368
+ (7 - (@selected_date.wday == 0 ? 7 : @selected_date.wday)) :
369
+ (6 - @selected_date.wday)
370
+ @selected_date = @selected_date.next_day(days_forward)
371
+ update_current_month
372
+ when 'HOME'
373
+ # Go to start of month
374
+ @selected_date = Date.new(@selected_date.year, @selected_date.month, 1)
375
+ update_current_month
376
+ when 'END'
377
+ # Go to end of month
378
+ @selected_date = Date.new(@selected_date.year, @selected_date.month, -1)
379
+ update_current_month
380
+ when 't', 'T'
381
+ # Go to today
382
+ @selected_date = Date.today
383
+ @current_month = Date.today
384
+ when '0'..'9'
385
+ # Numeric prefix for jumps (vim-style)
386
+ @numeric_prefix ||= ""
387
+ @numeric_prefix += ch
388
+ when 'g'
389
+ # Execute numeric jump
390
+ if @numeric_prefix && !@numeric_prefix.empty?
391
+ days = @numeric_prefix.to_i
392
+ @selected_date = @selected_date.next_day(days)
393
+ update_current_month
394
+ @numeric_prefix = ""
395
+ end
396
+ when 'r', 'R'
397
+ # Force full refresh
398
+ Rcurses.clear_screen
399
+ @prev_content = ""
400
+ @main_pane.full_refresh
401
+ @help_pane.full_refresh
402
+ @status_pane.full_refresh
403
+ end
404
+ end
405
+
406
+ def handle_config_input(ch)
407
+ case ch
408
+ when 'ESC', 'q', 'Q'
409
+ @config_mode = false
410
+ @prev_content = "" # Force refresh
411
+ when 'UP', 'k', 'K'
412
+ @config_selected = (@config_selected - 1) % 5
413
+ when 'DOWN', 'j', 'J'
414
+ @config_selected = (@config_selected + 1) % 5
415
+ when 'ENTER'
416
+ handle_config_edit
417
+ render_config # Immediately re-render config after edit
418
+ end
419
+ end
420
+
421
+ def handle_config_edit
422
+ case @config_selected
423
+ when 0 # Date format
424
+ format_help = DATE_FORMATS.map { |k, v| "#{k}: #{Date.today.strftime(v)}" }.join(" | ")
425
+ new_format = get_input_with_help("Date format", @config['date_format'], format_help)
426
+ # Check if user entered a number for quick format selection
427
+ if new_format && DATE_FORMATS[new_format]
428
+ @config['date_format'] = DATE_FORMATS[new_format]
429
+ elsif new_format && !new_format.empty?
430
+ @config['date_format'] = new_format
431
+ end
432
+ @prev_content = "" # Force refresh to show new value
433
+ when 1 # Months before
434
+ old_value = @config['months_before']
435
+ new_value = get_input("Months before", old_value.to_s)
436
+ if new_value && new_value != old_value.to_s
437
+ @config['months_before'] = new_value.to_i
438
+ end
439
+ @prev_content = "" # Force refresh to show new value
440
+ when 2 # Months after
441
+ new_value = get_input("Months after", @config['months_after'].to_s)
442
+ if new_value && new_value.to_i > 0
443
+ @config['months_after'] = new_value.to_i
444
+ end
445
+ @prev_content = "" # Force refresh to show new value
446
+ when 3 # Week starts Monday
447
+ @config['week_starts_monday'] = !@config['week_starts_monday']
448
+ @prev_content = "" # Force refresh
449
+ when 4 # Save and exit
450
+ save_config
451
+ @config_mode = false
452
+ @prev_content = "" # Force refresh
453
+ end
454
+ end
455
+
456
+ def get_input(prompt, default)
457
+ # Create input pane
458
+ pane = Rcurses::Pane.new(2, @screen_h - 4, @screen_w - 4, 3, 15, 0)
459
+ pane.border = true
460
+
461
+ # Use ask method which properly handles input
462
+ result = pane.ask("#{prompt}: ", default)
463
+
464
+ # Force a full screen refresh after input dialog
465
+ Rcurses.clear_screen
466
+ @main_pane.full_refresh
467
+ @help_pane.full_refresh
468
+ @status_pane.full_refresh
469
+ @prev_content = "" # Force refresh on next render
470
+
471
+ # Return the result (ask already returns the text)
472
+ result.strip
473
+ end
474
+
475
+ def get_input_with_help(prompt, default, help_text)
476
+ # Create input pane with extra height for help
477
+ pane = Rcurses::Pane.new(2, @screen_h - 6, @screen_w - 4, 5, 15, 0)
478
+ pane.border = true
479
+
480
+ # Show help text above input
481
+ Rcurses::Cursor.set(@screen_h - 6, 4)
482
+ print help_text.fg(245)
483
+
484
+ # Use ask method which properly handles input
485
+ result = pane.ask("#{prompt}: ", default)
486
+
487
+ # Force a full screen refresh after input dialog
488
+ Rcurses.clear_screen
489
+ @main_pane.full_refresh
490
+ @help_pane.full_refresh
491
+ @status_pane.full_refresh
492
+ @prev_content = "" # Force refresh on next render
493
+
494
+ # Return the result (ask already returns the text)
495
+ result.strip
496
+ end
497
+
498
+ def update_current_month
499
+ @current_month = Date.new(@selected_date.year, @selected_date.month, 1)
500
+ end
501
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datepick
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Geir Isene
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rcurses
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.8'
27
+ description: A powerful interactive terminal date picker built with rcurses. Features
28
+ vim-style navigation, configurable date formats, multiple month views, and extensive
29
+ keyboard shortcuts. Perfect for shell scripts and command-line workflows that need
30
+ date selection.
31
+ email: g@isene.com
32
+ executables:
33
+ - datepick
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - README.md
38
+ - bin/datepick
39
+ - lib/datepick.rb
40
+ homepage: https://isene.com/
41
+ licenses:
42
+ - Unlicense
43
+ metadata:
44
+ source_code_uri: https://github.com/isene/datepick
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubygems_version: 3.4.20
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Datepick - Interactive Terminal Date Picker
64
+ test_files: []