heathrow 0.7.2 → 0.7.3

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: cbe8be982ab3300b591a24bdba7bb67c9feff58167a7785fb288a4b62d558f46
4
- data.tar.gz: 513f905dff1e8a9e74b87488a1fa5b007d3a76e26287d7643961ea2b1b0b2268
3
+ metadata.gz: 123f2c7c6134d4ea1add0517f649b94353918d1e76583a9f714a1e49d7879f96
4
+ data.tar.gz: 26e26497ec050fc246cb2ee1eff2389ceb3088dccb8c83d09257425dd152888a
5
5
  SHA512:
6
- metadata.gz: 29e42a3922e7dfb70188c3bc03345812ccb1a7a21c992384e1b50938a00418ecb6bdba009ba30d0020960b354f144b0ac1a9cc18910b2c2e775c0137a7f68c61
7
- data.tar.gz: 3a7a0e541c490c8d783f679d6be4bcab6dabe703041ea2620d95b7756ec74cc53d9bfee851e15f3863685168f8c33f1c1b227ecf5e1aea784681d4b12cfb6f16
6
+ metadata.gz: 2130ee038cbc2d9bc8ee4b7855809afaa1c171aba9b8b0967a85d71c9397b39b2ef86afcef58b3301d15556dfc3d07ab8d91d8d12ed77d4a90879904562829a5
7
+ data.tar.gz: 3557ffc9cf7af99441c6c4062e63d7bc9c18145b1f537d10607d420f059221f36c33d1f1a86cad76036a69d003eedb39f26c471013c2dba839187350a10c6aff
@@ -725,6 +725,8 @@ module Heathrow
725
725
  page_down
726
726
  when 'PgUP'
727
727
  page_up
728
+ when 'L'
729
+ load_more_messages
728
730
  when 'w'
729
731
  change_width
730
732
  when 'Y'
@@ -1498,28 +1500,32 @@ module Heathrow
1498
1500
  available_width -= 2 if @panes[:left].border # Extra space for border chars
1499
1501
  available_width -= 3 # Space for N flag + replied flag + indicator column (tag/star/attachment/D)
1500
1502
 
1501
- # Truncate sender to fit
1503
+ # Truncate sender to fit (use display_width for CJK characters)
1502
1504
  sender_max = 15
1503
- sender_display = sender.length > sender_max ? sender[0..sender_max-2] + '…' : sender.ljust(sender_max)
1504
-
1505
+ dw = Rcurses.display_width(sender)
1506
+ sender_display = if dw > sender_max
1507
+ truncate_to_width(sender, sender_max - 1) + '…'
1508
+ else
1509
+ sender + ' ' * [sender_max - dw, 0].max
1510
+ end
1511
+
1505
1512
  # Build the line with timestamp, icon and sender
1506
1513
  icon = respond_to?(:get_source_icon) ? get_source_icon(msg['source_type']) : '•'
1507
1514
  line_prefix = "#{timestamp} #{icon} #{sender_display} "
1508
-
1509
- # Calculate remaining space for subject
1510
- subject_width = available_width - line_prefix.length - 1 # -1 for safety
1511
- if subject_width > 0 && subject.length > subject_width
1512
- subject = subject[0..subject_width-2] + '…'
1515
+
1516
+ # Calculate remaining space for subject (use display_width for CJK)
1517
+ prefix_dw = Rcurses.display_width(line_prefix)
1518
+ subject_width = available_width - prefix_dw - 1 # -1 for safety
1519
+ subject_dw = Rcurses.display_width(subject)
1520
+ if subject_width > 0 && subject_dw > subject_width
1521
+ subject = truncate_to_width(subject, subject_width - 1) + '…'
1522
+ subject_dw = Rcurses.display_width(subject)
1513
1523
  end
1514
-
1515
- # Format the complete line and pad to full width
1516
- line = line_prefix + subject
1517
- # Pad line to full available width so background color spans entire width
1518
- line = line.ljust(available_width)
1519
-
1520
- prefix_part = "#{timestamp} #{icon} #{sender_display} "
1524
+
1525
+ prefix_part = line_prefix
1521
1526
  subject_part = subject.strip
1522
- padding = " " * [available_width - prefix_part.length - subject_part.length, 0].max
1527
+ total_dw = Rcurses.display_width(prefix_part) + Rcurses.display_width(subject_part)
1528
+ padding = " " * [available_width - total_dw, 0].max
1523
1529
  finalize_line(msg, selected, prefix_part, subject_part, source_color, padding)
1524
1530
  end
1525
1531
 
@@ -1885,7 +1891,7 @@ module Heathrow
1885
1891
  # Show view name instantly
1886
1892
  render_top_bar
1887
1893
 
1888
- @load_limit = 1000
1894
+ @load_limit = 200
1889
1895
  @filtered_messages = @db.get_messages({}, @load_limit, 0, light: true)
1890
1896
  sort_messages
1891
1897
  @index = 0
@@ -1911,7 +1917,11 @@ module Heathrow
1911
1917
  clear_inline_image
1912
1918
  end
1913
1919
 
1914
- return unless current_msg
1920
+ unless current_msg
1921
+ @panes[:right].text = ""
1922
+ @panes[:right].refresh
1923
+ return
1924
+ end
1915
1925
 
1916
1926
  msg = current_msg
1917
1927
 
@@ -2314,18 +2324,22 @@ module Heathrow
2314
2324
 
2315
2325
  def check_load_more
2316
2326
  return unless @load_limit && @filtered_messages
2317
- n = @filtered_messages.size
2318
- return if n < @load_limit # Haven't hit the limit yet
2319
- threshold = (@load_limit * 0.95).to_i
2320
- if @index >= threshold
2321
- load_more_messages
2322
- end
2327
+ return if @filtered_messages.size < @load_limit # Haven't hit the limit yet
2328
+ display_size = message_count
2329
+ return if display_size == 0
2330
+ return unless @index >= display_size - 10
2331
+ return if @last_autoload_index == @index
2332
+ @last_autoload_index = @index
2333
+ load_more_messages
2323
2334
  end
2324
2335
 
2325
2336
  def load_more_messages
2326
2337
  return unless @load_limit
2338
+ # Remember current message so we can restore position after reload
2339
+ cur = current_message
2340
+ cur_id = cur['id'] if cur
2327
2341
  old_count = @filtered_messages.size
2328
- @load_limit += 1000
2342
+ @load_limit += 200
2329
2343
 
2330
2344
  if @current_folder
2331
2345
  light_cols = "id, source_id, external_id, thread_id, parent_id, sender, sender_name, recipients, subject, substr(content, 1, 200) as content, timestamp, received_at, read AS is_read, starred AS is_starred, archived, labels, metadata, attachments, folder, replied"
@@ -2345,7 +2359,21 @@ module Heathrow
2345
2359
 
2346
2360
  sort_messages
2347
2361
  new_count = @filtered_messages.size
2348
- set_feedback("Loaded #{new_count} messages (+#{new_count - old_count})", 156, 2) if new_count > old_count
2362
+ return if new_count <= old_count
2363
+
2364
+ # In threaded mode, set pending restore so the threaded rebuild finds our position
2365
+ if @show_threaded && cur_id
2366
+ @pending_restore_id = cur_id
2367
+ end
2368
+
2369
+ # Force threaded view to rebuild organizer with new messages
2370
+ if @show_threaded && respond_to?(:organize_current_messages)
2371
+ organize_current_messages(true)
2372
+ end
2373
+
2374
+ set_feedback("Loaded #{new_count} messages (+#{new_count - old_count})", 156, 2)
2375
+ render_message_list
2376
+ render_top_bar
2349
2377
  end
2350
2378
 
2351
2379
  # Ensure all feeds/channels from a source have at least some messages loaded
@@ -2512,7 +2540,7 @@ module Heathrow
2512
2540
 
2513
2541
  render_top_bar
2514
2542
 
2515
- @load_limit = 1000
2543
+ @load_limit = 200
2516
2544
  @filtered_messages = @db.get_messages({is_read: false}, @load_limit, 0, light: true)
2517
2545
  sort_messages
2518
2546
  @index = 0
@@ -2559,7 +2587,7 @@ module Heathrow
2559
2587
  @section_order = view[:filters]['section_order'].dup
2560
2588
  end
2561
2589
 
2562
- @load_limit = 1000
2590
+ @load_limit = 200
2563
2591
  if view && view[:filters] && !view[:filters].empty?
2564
2592
  apply_view_filters(view)
2565
2593
  sort_messages
@@ -3701,6 +3729,7 @@ module Heathrow
3701
3729
 
3702
3730
  def show_folder_browser
3703
3731
  @in_folder_browser = true
3732
+ @in_favorites_browser = false
3704
3733
  @folder_browser_index = 0
3705
3734
  @panes[:top].bg = @topcolor
3706
3735
  @folder_collapsed ||= {}
@@ -3781,8 +3810,10 @@ module Heathrow
3781
3810
  @panes[:right].refresh
3782
3811
  end
3783
3812
 
3784
- # Update top bar
3785
- @panes[:top].text = " Heathrow - ".b.fg(255) + "Folder Browser".b.fg(201) + " [#{@folder_display.size} folders]".fg(246)
3813
+ # Update top bar (preserve Favorites title if in favorites mode)
3814
+ browser_title = @in_favorites_browser ? "Favorites" : "Folder Browser"
3815
+ browser_color = @in_favorites_browser ? 226 : 201
3816
+ @panes[:top].text = " Heathrow - ".b.fg(255) + browser_title.b.fg(browser_color) + " [#{@folder_display.size} folders]".fg(246)
3786
3817
  @panes[:top].refresh
3787
3818
 
3788
3819
  # Update bottom bar
@@ -3902,6 +3933,7 @@ module Heathrow
3902
3933
  render_folder_browser
3903
3934
  when 'q', 'ESC', "\e"
3904
3935
  @in_folder_browser = false
3936
+ @in_favorites_browser = false
3905
3937
  render_all
3906
3938
  break
3907
3939
  else
@@ -3918,6 +3950,7 @@ module Heathrow
3918
3950
  # Open a specific folder and show its messages
3919
3951
  def open_folder(folder_name)
3920
3952
  @in_folder_browser = false
3953
+ @in_favorites_browser = false
3921
3954
  @current_folder = folder_name
3922
3955
  @current_view = 'A'
3923
3956
  @in_source_view = false
@@ -3932,7 +3965,7 @@ module Heathrow
3932
3965
  @panes[:bottom].refresh
3933
3966
 
3934
3967
  # Light query with limit (full content loaded lazily when viewing)
3935
- @load_limit = 1000
3968
+ @load_limit = 200
3936
3969
  light_cols = "id, source_id, external_id, thread_id, parent_id, sender, sender_name, recipients, subject, substr(content, 1, 200) as content, timestamp, received_at, read AS is_read, starred AS is_starred, archived, labels, metadata, attachments, folder, replied"
3937
3970
  results = @db.execute(
3938
3971
  "SELECT #{light_cols} FROM messages WHERE folder >= ? AND folder < ? ORDER BY timestamp DESC LIMIT ?",
@@ -3969,6 +4002,7 @@ module Heathrow
3969
4002
  def show_favorites_browser
3970
4003
  favorites = get_favorite_folders
3971
4004
  @in_folder_browser = true
4005
+ @in_favorites_browser = true
3972
4006
  @folder_browser_index = 0
3973
4007
  @panes[:top].bg = @topcolor
3974
4008
  @folder_count_cache = {} # Fresh counts each time
@@ -3999,7 +4033,7 @@ module Heathrow
3999
4033
  when 'k', 'UP'
4000
4034
  @folder_browser_index = (@folder_browser_index - 1) % @folder_display.size if @folder_display.size > 0
4001
4035
  render_folder_browser
4002
- when 'ENTER'
4036
+ when 'l', 'RIGHT', 'ENTER'
4003
4037
  folder = @folder_display[@folder_browser_index]
4004
4038
  if folder
4005
4039
  open_folder(folder[:full_name])
@@ -4052,6 +4086,7 @@ module Heathrow
4052
4086
  end
4053
4087
  when 'q', 'ESC', "\e", 'h', 'LEFT'
4054
4088
  @in_folder_browser = false
4089
+ @in_favorites_browser = false
4055
4090
  render_all
4056
4091
  break
4057
4092
  end
@@ -4314,6 +4349,9 @@ module Heathrow
4314
4349
  @folder_collapsed.delete(folder[:full_name])
4315
4350
  @folder_display = flatten_folder_tree(@folder_tree, '', 0, @folder_collapsed)
4316
4351
  render_save_folder_picker(idx, title)
4352
+ elsif folder
4353
+ render_all
4354
+ return folder[:full_name]
4317
4355
  end
4318
4356
  when 'h', 'LEFT'
4319
4357
  folder = @folder_display[idx]
@@ -5679,9 +5717,16 @@ module Heathrow
5679
5717
 
5680
5718
  if result[:success]
5681
5719
  if orig_id
5720
+ # Re-read metadata from DB to get current maildir_file path
5721
+ # (poller may have renamed the file since we captured orig_msg)
5722
+ fresh = @db.get_message(orig_id)
5723
+ if fresh
5724
+ orig_msg = fresh
5725
+ end
5726
+ # Sync disk flag first, then DB, to avoid poller race condition
5727
+ sync_maildir_flag(orig_msg, 'R', true) if orig_msg
5682
5728
  @db.execute("UPDATE messages SET replied = 1 WHERE id = ?", [orig_id])
5683
5729
  orig_msg['replied'] = 1 if orig_msg
5684
- sync_maildir_flag(orig_msg, 'R', true) if orig_msg
5685
5730
  end
5686
5731
  msg = result[:message]
5687
5732
  if composed[:attachments] && !composed[:attachments].empty?
@@ -7670,7 +7715,9 @@ Required: URL, optional CSS selector
7670
7715
  Config.identity_for_folder(folder)
7671
7716
  end
7672
7717
 
7673
- # Colorize email content with mutt-style quote levels and signature dimming
7718
+ # Colorize email content with mutt-style quote levels and signature dimming.
7719
+ # Detects both ">" prefix quoting and indentation-based quoting (from HTML
7720
+ # emails rendered via w3m, where blockquotes become indented text).
7674
7721
  def colorize_email_content(content)
7675
7722
  quote_colors = [theme[:quote1] || 114, theme[:quote2] || 180,
7676
7723
  theme[:quote3] || 139, theme[:quote4] || 109]
@@ -7678,23 +7725,41 @@ Required: URL, optional CSS selector
7678
7725
  link_color = theme[:link] || 4 # blue (vim String)
7679
7726
  email_color = theme[:email] || 5 # magenta (vim Special)
7680
7727
  in_signature = false
7728
+ indent_quote_level = 0 # tracks nesting from "wrote:" attribution lines
7681
7729
 
7682
- content.lines.map do |line|
7730
+ lines = content.lines
7731
+ result = []
7732
+ lines.each_with_index do |line, i|
7683
7733
  stripped = line.rstrip
7684
7734
  # Detect signature delimiter (RFC 3676: "-- " on its own line)
7685
7735
  if stripped == '-- ' || stripped == '--'
7686
7736
  in_signature = true
7687
- colorize_links(stripped, sig_color, link_color)
7737
+ indent_quote_level = 0
7738
+ result << colorize_links(stripped, sig_color, link_color)
7688
7739
  elsif in_signature
7689
- colorize_links(stripped, sig_color, link_color)
7740
+ result << colorize_links(stripped, sig_color, link_color)
7690
7741
  elsif stripped =~ /^(>{1,})\s?/
7691
7742
  level = $1.length
7692
7743
  color = quote_colors[[level - 1, quote_colors.length - 1].min]
7693
- colorize_links(stripped, color, link_color)
7744
+ result << colorize_links(stripped, color, link_color)
7694
7745
  else
7695
- colorize_links(stripped, nil, link_color)
7746
+ # Detect "wrote:" attribution lines (start of indented quote block)
7747
+ if stripped =~ /\bwrote:\s*$/
7748
+ indent_quote_level += 1
7749
+ color = quote_colors[[indent_quote_level - 1, quote_colors.length - 1].min]
7750
+ result << colorize_links(stripped, color, link_color)
7751
+ elsif indent_quote_level > 0 && stripped =~ /^\s{2,}/
7752
+ # Indented line inside a quote block
7753
+ color = quote_colors[[indent_quote_level - 1, quote_colors.length - 1].min]
7754
+ result << colorize_links(stripped, color, link_color)
7755
+ else
7756
+ # Non-indented line resets indent quoting
7757
+ indent_quote_level = 0 if indent_quote_level > 0 && !stripped.empty?
7758
+ result << colorize_links(stripped, nil, link_color)
7759
+ end
7696
7760
  end
7697
- end.join("\n")
7761
+ end
7762
+ result.join("\n")
7698
7763
  end
7699
7764
 
7700
7765
  # Convert HTML to readable text via w3m
@@ -7739,12 +7804,13 @@ Required: URL, optional CSS selector
7739
7804
  tags.each do |tag|
7740
7805
  src = tag[/src=["']([^"']+)["']/i, 1]
7741
7806
  next unless src
7742
- # Skip by URL patterns
7743
- next if src =~ /track|pixel|spacer|beacon|\.gif$/i
7744
- # Skip by HTML dimensions (1x1 tracking pixels, tiny icons)
7807
+ # Skip by URL patterns (tracking, icons, social media badges)
7808
+ next if src =~ /track|pixel|spacer|beacon|\.gif$|icon|logo|badge|button|social|facebook|linkedin|twitter|instagram/i
7809
+ # Skip by HTML dimensions (tracking pixels and small icons)
7745
7810
  w = tag[/width=["']?(\d+)/i, 1]&.to_i
7746
7811
  h = tag[/height=["']?(\d+)/i, 1]&.to_i
7747
- next if w && h && (w <= 2 || h <= 2)
7812
+ next if w && w <= 40
7813
+ next if h && h <= 40
7748
7814
  urls << src
7749
7815
  end
7750
7816
  urls
@@ -7826,7 +7892,7 @@ Required: URL, optional CSS selector
7826
7892
  # Clear right pane and show images
7827
7893
  n = image_paths.size
7828
7894
  label = n == 1 ? "1 image" : "#{n} images"
7829
- @panes[:right].text = " [#{label}] Press I to return".fg(245)
7895
+ @panes[:right].text = " [#{label}] Press ESC to return".fg(245)
7830
7896
  @panes[:right].full_refresh # Full refresh to clear image area
7831
7897
 
7832
7898
  pane_w = @panes[:right].w - 2
@@ -7909,7 +7975,6 @@ Required: URL, optional CSS selector
7909
7975
  def format_attachments(attachments)
7910
7976
  return nil unless attachments.is_a?(Array) && !attachments.empty?
7911
7977
  lines = []
7912
- lines << ("─" * 60).fg(238)
7913
7978
  lines << "Attachments:".b.fg(208)
7914
7979
  attachments.each_with_index do |att, i|
7915
7980
  name = att['name'] || att['filename'] || 'unnamed'
@@ -7930,6 +7995,17 @@ Required: URL, optional CSS selector
7930
7995
  end
7931
7996
 
7932
7997
  # Highlight URLs in a line, applying base_color to non-URL text
7998
+ # Truncate a string to fit within a given display width (CJK-aware)
7999
+ def truncate_to_width(str, max_width)
8000
+ w = 0
8001
+ str.each_char.with_index do |c, i|
8002
+ cw = Rcurses.display_width(c)
8003
+ return str[0...i] if w + cw > max_width
8004
+ w += cw
8005
+ end
8006
+ str
8007
+ end
8008
+
7933
8009
  def colorize_links(line, base_color, link_color)
7934
8010
  url_re = %r{https?://[^\s<>\[\]()]+}
7935
8011
  parts = line.split(url_re, -1)
@@ -533,7 +533,7 @@ module Heathrow
533
533
  if (source_type || msg['source_type']) == 'rss'
534
534
  sender = '' # Don't show sender for RSS
535
535
  else
536
- sender = sender[0..14] if sender.length > 15
536
+ sender = truncate_to_width(sender, 14) + '…' if Rcurses.display_width(sender) > 15
537
537
  end
538
538
 
539
539
  # For Discord/Slack channels, show content not channel name
@@ -558,14 +558,14 @@ module Heathrow
558
558
  if sender.empty?
559
559
  prefix = "#{unread} "
560
560
  else
561
- prefix = "#{unread} #{sender.ljust(15)} "
561
+ prefix = "#{unread} #{sender + ' ' * [15 - Rcurses.display_width(sender), 0].max} "
562
562
  end
563
563
 
564
564
  # Truncate content to fit single line (prefix + 2 for nflag+ind from finalize_line)
565
565
  pane_width = @panes[:left].w - 5
566
- available = pane_width - prefix.length
567
- if display_content && display_content.length > available && available > 0
568
- display_content = display_content[0..(available-2)] + "…"
566
+ available = pane_width - Rcurses.display_width(prefix)
567
+ if display_content && Rcurses.display_width(display_content) > available && available > 0
568
+ display_content = truncate_to_width(display_content, available - 1) + "…"
569
569
  end
570
570
 
571
571
  finalize_line(msg, selected, prefix, display_content, color)
@@ -574,17 +574,17 @@ module Heathrow
574
574
  # Format a thread reply
575
575
  def format_thread_reply(msg, selected, indent)
576
576
  sender = display_sender(msg)
577
- sender = sender[0..12] if sender.length > 12
578
-
577
+ sender = truncate_to_width(sender, 12) if Rcurses.display_width(sender) > 12
578
+
579
579
  content = msg['content'] || ''
580
580
  content = content.gsub(/\n/, ' ')
581
-
581
+
582
582
  # Truncate based on pane width
583
583
  pane_width = @panes[:left].w - 5
584
- used_space = 2 + indent.length + 3 + sender.length + 2 # arrow + indent + └─ + sender + :
584
+ used_space = 2 + Rcurses.display_width(indent) + 3 + Rcurses.display_width(sender) + 2
585
585
  available = pane_width - used_space
586
- if content.length > available && available > 0
587
- content = content[0..(available-2)] + "…"
586
+ if Rcurses.display_width(content) > available && available > 0
587
+ content = truncate_to_width(content, available - 1) + "…"
588
588
  end
589
589
 
590
590
  prefix = "#{indent}└─ #{sender}: "
@@ -599,17 +599,20 @@ module Heathrow
599
599
 
600
600
  timestamp = (parse_timestamp(msg['timestamp']) || "").ljust(6)
601
601
  sender_max = 15
602
- sender = sender.length > sender_max ? sender[0..sender_max-2] + '…' : sender.ljust(sender_max)
602
+ sdw = Rcurses.display_width(sender)
603
+ sender = sdw > sender_max ? truncate_to_width(sender, sender_max - 1) + '…' : sender + ' ' * [sender_max - sdw, 0].max
603
604
  child_indent = indent.empty? ? "" : " "
604
605
  prefix = "#{child_indent}#{timestamp} #{icon} #{sender} "
605
606
 
606
607
  content = msg['subject'] || msg['content'] || ''
608
+ content = content.dup if content.frozen?
609
+ content = content.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?') rescue content.force_encoding('UTF-8').scrub('?')
607
610
  content = content.gsub(/\n/, ' ')
608
611
 
609
- used_space = prefix.length + 1
612
+ used_space = Rcurses.display_width(prefix) + 1
610
613
  available = pane_width - used_space
611
- if content.length > available && available > 0
612
- content = content[0..(available-2)] + "…"
614
+ if Rcurses.display_width(content) > available && available > 0
615
+ content = truncate_to_width(content, available - 1) + "…"
613
616
  end
614
617
 
615
618
  color = get_source_color(msg)
@@ -623,7 +626,8 @@ module Heathrow
623
626
 
624
627
  # Truncate sender to fixed width
625
628
  sender_max = 15
626
- sender = sender.length > sender_max ? sender[0..sender_max-2] + '…' : sender.ljust(sender_max)
629
+ sdw = Rcurses.display_width(sender)
630
+ sender = sdw > sender_max ? truncate_to_width(sender, sender_max - 1) + '…' : sender + ' ' * [sender_max - sdw, 0].max
627
631
 
628
632
  content = msg['content'] || ''
629
633
  content = content.gsub(/\n/, ' ')
@@ -633,9 +637,9 @@ module Heathrow
633
637
 
634
638
  # Truncate content to fit single line
635
639
  pane_width = @panes[:left].w - 5
636
- available = pane_width - prefix.length
637
- if content.length > available && available > 0
638
- content = content[0..(available-2)] + "…"
640
+ available = pane_width - Rcurses.display_width(prefix)
641
+ if Rcurses.display_width(content) > available && available > 0
642
+ content = truncate_to_width(content, available - 1) + "…"
639
643
  end
640
644
 
641
645
  color = get_source_color(msg)
@@ -1,3 +1,3 @@
1
1
  module Heathrow
2
- VERSION = '0.7.2'
2
+ VERSION = '0.7.3'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heathrow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene