localvault 1.5.1 → 1.5.2

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: 62e1f820da0ed430364d0acaf708a3818ccffdaff8c19ec15cc6de6893abc6d9
4
- data.tar.gz: 12d59fefb6f347d2992557e411120813ec00496afb9a05e0b3336e840420562b
3
+ metadata.gz: 6281a806d5f5838ea6802eaae4cb16a4276590636476151b45ed0d7ae639f1a9
4
+ data.tar.gz: b1aad2994aff9c31e45759672a6f2fb35f77f5f5a3fabc24b897a112997abd80
5
5
  SHA512:
6
- metadata.gz: 9c9c1bcee26c3d3b86b57a9a3607837ca11642412d7e10422ce301aedc639e73ac4273ff79490d7110da4f7b9b7daf1f9901bbce2fcf6228fb8a6a278a1e289d
7
- data.tar.gz: cbe652d3dbdf1e69e986a7214ae35d2848c14a0d35f55c17bb6586d322f4fea5998096a80f4e0d4bfd90845ceb47166571ba48476aa32f2dc05e0f3603a19cd6
6
+ metadata.gz: b4b948bc2126f8ac488190d3f4d07f1ebe995d32baaf7fb458b95256507f62438d09e472cbcc059cf23d2bb91a63f05ffcd11d5585d6c0a4fec827fdc667d4cd
7
+ data.tar.gz: a32de3dd3fde6cba7da103d7c6d76924b236604e1cd80c28873a47dba01f52e0f99f2db7f44121a5343845e1a41a5644ff42548d96528b8c1551d0c2f47c3a10
@@ -1073,49 +1073,53 @@ module LocalVault
1073
1073
  }
1074
1074
  end
1075
1075
 
1076
- # ── OWNED BY YOU ──
1076
+ # ── Render everything in tables ──
1077
1077
  $stdout.puts
1078
- $stdout.puts VAULT_STYLE.render("OWNED BY YOU") + " " + COUNT_STYLE.render("(#{owned.size} vault#{owned.size == 1 ? "" : "s"})")
1079
- $stdout.puts
1080
- if owned.empty?
1081
- $stdout.puts " " + COUNT_STYLE.render("No vaults owned yet. Create one with `localvault init NAME`.")
1082
- else
1083
- owned.sort_by { |r| r[:name] }.each { |row| render_dashboard_vault(row, my_handle: my_handle) }
1078
+
1079
+ # OWNED BY YOU — one table with all vaults and their members
1080
+ unless owned.empty?
1081
+ $stdout.puts VAULT_STYLE.render("OWNED BY YOU")
1082
+ $stdout.puts render_dashboard_table(owned.sort_by { |r| r[:name] }, my_handle: my_handle)
1083
+ $stdout.puts
1084
1084
  end
1085
1085
 
1086
- # ── SHARED WITH YOU ──
1087
- $stdout.puts
1088
- $stdout.puts VAULT_STYLE.render("SHARED WITH YOU") + " " + COUNT_STYLE.render("(#{shared.size} vault#{shared.size == 1 ? "" : "s"})")
1089
- $stdout.puts
1090
- if shared.empty?
1091
- $stdout.puts " " + COUNT_STYLE.render("No vaults shared with you.")
1092
- else
1093
- shared.sort_by { |r| r[:name] }.each { |row| render_dashboard_vault(row, my_handle: my_handle) }
1086
+ # SHARED WITH YOU
1087
+ unless shared.empty?
1088
+ $stdout.puts VAULT_STYLE.render("SHARED WITH YOU")
1089
+ $stdout.puts render_dashboard_table(shared.sort_by { |r| r[:name] }, my_handle: my_handle)
1090
+ $stdout.puts
1091
+ end
1092
+
1093
+ if owned.empty? && shared.empty?
1094
+ $stdout.puts COUNT_STYLE.render("No vaults found. Create one with `localvault init NAME`.")
1095
+ $stdout.puts
1094
1096
  end
1095
1097
 
1096
- # ── LEGACY DIRECT SHARES ──
1098
+ # LEGACY DIRECT SHARES — only show if there are any
1097
1099
  sent = safe_fetch_shares { client.sent_shares }
1098
1100
  pending = safe_fetch_shares { client.pending_shares }
1099
1101
  outgoing_count = (sent["shares"] || []).reject { |s| s["status"] == "revoked" }.size
1100
1102
  pending_count = (pending["shares"] || []).size
1101
1103
 
1102
- $stdout.puts
1103
- $stdout.puts VAULT_STYLE.render("LEGACY DIRECT SHARES") + " " + COUNT_STYLE.render("(pre-v1.2 fallback)")
1104
- $stdout.puts " outgoing: #{outgoing_count} pending: #{pending_count}"
1105
1104
  if outgoing_count + pending_count > 0
1106
- $stdout.puts " " + COUNT_STYLE.render("Manage with `localvault receive`, `localvault revoke SHARE_ID`.")
1105
+ $stdout.puts VAULT_STYLE.render("LEGACY DIRECT SHARES")
1106
+ legacy_rows = []
1107
+ (sent["shares"] || []).reject { |s| s["status"] == "revoked" }.each do |s|
1108
+ legacy_rows << [s["vault_name"] || "—", "@#{s["recipient_handle"]}", s["status"], "outgoing"]
1109
+ end
1110
+ (pending["shares"] || []).each do |s|
1111
+ legacy_rows << [s["vault_name"] || "—", "@#{s["sender_handle"]}", "pending", "incoming"]
1112
+ end
1113
+ $stdout.puts render_legacy_shares_table(legacy_rows)
1114
+ $stdout.puts
1107
1115
  end
1108
1116
 
1109
- # ── Skipped ──
1117
+ # Skipped
1110
1118
  unless skipped.empty?
1119
+ $stderr.puts COUNT_STYLE.render("#{skipped.size} vault(s) could not be loaded:")
1120
+ skipped.each { |name, reason| $stderr.puts " #{name}: #{reason}" }
1111
1121
  $stdout.puts
1112
- $stderr.puts "Note: #{skipped.size} vault(s) could not be loaded:"
1113
- skipped.each do |name, reason|
1114
- $stderr.puts " #{name}: #{reason}"
1115
- end
1116
1122
  end
1117
-
1118
- $stdout.puts
1119
1123
  end
1120
1124
 
1121
1125
  desc "import FILE", "Bulk-import secrets from a .env, .json, or .yml file"
@@ -1408,53 +1412,70 @@ module LocalVault
1408
1412
  end
1409
1413
  end
1410
1414
 
1411
- # Render one vault row for `localvault dashboard`, as a Lipgloss table
1412
- # of its members (handle, access, scopes). Style mirrors `show`'s
1413
- # render_table rounded border, purple header, alternating rows.
1414
- def render_dashboard_vault(row, my_handle:)
1415
- slots = row[:key_slots]
1416
- valid = slots.select { |_, v| v.is_a?(Hash) && v["pub"].is_a?(String) }
1417
-
1418
- count_label = "#{valid.size} member#{valid.size == 1 ? "" : "s"}"
1419
- owner_label = row[:owner] ? "owner @#{row[:owner]}" : "personal"
1420
-
1421
- # Sync status badge
1422
- sync_badge = case row[:sync_status]
1423
- when "synced" then "synced #{row[:synced_at]}"
1424
- when "remote only" then "remote only"
1425
- when "local only" then "local only — not synced"
1426
- else row[:sync_status] || "unknown"
1427
- end
1428
- sync_badge += " · #{row[:size_label]}" if row[:size_label]
1429
-
1430
- $stdout.puts " " + GROUP_STYLE.render(row[:name]) + " " +
1431
- COUNT_STYLE.render("#{count_label} · #{owner_label} · #{sync_badge}")
1432
-
1433
- if !row[:is_team]
1434
- $stdout.puts " " + COUNT_STYLE.render("No team members. Convert with `localvault team init #{row[:name]}` to share.")
1435
- $stdout.puts
1436
- return
1437
- end
1415
+ # Render a single Lipgloss table for all vaults in a section (owned / shared).
1416
+ # Each vault contributes rows: the vault itself as a summary row, then one
1417
+ # row per team member. Personal vaults get a single summary row.
1418
+ #
1419
+ # Columns: Vault | Member | Access | Scopes | Sync | Size
1420
+ def render_dashboard_table(vault_rows, my_handle:)
1421
+ require "lipgloss"
1438
1422
 
1439
- if valid.empty?
1440
- $stdout.puts " " + COUNT_STYLE.render("No members yet. Add someone with `localvault add @HANDLE -v #{row[:name]}`.")
1441
- $stdout.puts
1442
- return
1423
+ rows = []
1424
+ vault_rows.each_with_index do |vr, idx|
1425
+ slots = vr[:key_slots]
1426
+ valid = slots.select { |_, v| v.is_a?(Hash) && v["pub"].is_a?(String) }
1427
+
1428
+ sync_label = case vr[:sync_status]
1429
+ when "synced" then "synced #{vr[:synced_at]}"
1430
+ when "remote only" then "remote only"
1431
+ when "local only" then "local only"
1432
+ else vr[:sync_status] || "—"
1433
+ end
1434
+ size_col = vr[:size_label] || "—"
1435
+ owner_col = vr[:owner] ? "@#{vr[:owner]}" : "personal"
1436
+
1437
+ if valid.empty?
1438
+ # Single summary row for personal / empty team vaults
1439
+ rows << [vr[:name], owner_col, "—", "—", sync_label, size_col]
1440
+ else
1441
+ # First member row carries the vault name; subsequent rows leave it blank
1442
+ valid.sort.each_with_index do |(handle, slot), mi|
1443
+ vault_col = mi == 0 ? vr[:name] : ""
1444
+ owner_show = mi == 0 ? owner_col : ""
1445
+ sync_show = mi == 0 ? sync_label : ""
1446
+ size_show = mi == 0 ? size_col : ""
1447
+
1448
+ marker = handle == my_handle ? " (you)" : ""
1449
+ access = slot["scopes"].is_a?(Array) ? "scoped" : "full"
1450
+ scopes = slot["scopes"].is_a?(Array) ? slot["scopes"].join(", ") : "—"
1451
+
1452
+ rows << [vault_col, "@#{handle}#{marker}", access, scopes, sync_show, size_show]
1453
+ end
1454
+ end
1443
1455
  end
1444
1456
 
1445
- rows = valid.sort.map do |handle, slot|
1446
- marker = handle == my_handle ? " (you)" : ""
1447
- access = slot["scopes"].is_a?(Array) ? "scoped" : "full"
1448
- scopes = slot["scopes"].is_a?(Array) ? slot["scopes"].join(", ") : "—"
1449
- ["@#{handle}#{marker}", access, scopes]
1450
- end
1457
+ Lipgloss::Table.new
1458
+ .headers(["Vault", "Member", "Access", "Scopes", "Sync", "Size"])
1459
+ .rows(rows)
1460
+ .border(:rounded)
1461
+ .style_func(rows: rows.size, columns: 6) do |row_idx, _col|
1462
+ if row_idx == Lipgloss::Table::HEADER_ROW
1463
+ HEADER_STYLE
1464
+ else
1465
+ row_idx.odd? ? ODD_STYLE : EVEN_STYLE
1466
+ end
1467
+ end
1468
+ .render
1469
+ end
1451
1470
 
1471
+ # Render legacy direct shares as a single table.
1472
+ def render_legacy_shares_table(rows)
1452
1473
  require "lipgloss"
1453
- table = Lipgloss::Table.new
1454
- .headers(["Member", "Access", "Scopes"])
1474
+ Lipgloss::Table.new
1475
+ .headers(["Vault", "User", "Status", "Direction"])
1455
1476
  .rows(rows)
1456
1477
  .border(:rounded)
1457
- .style_func(rows: rows.size, columns: 3) do |row_idx, _col|
1478
+ .style_func(rows: rows.size, columns: 4) do |row_idx, _col|
1458
1479
  if row_idx == Lipgloss::Table::HEADER_ROW
1459
1480
  HEADER_STYLE
1460
1481
  else
@@ -1462,9 +1483,6 @@ module LocalVault
1462
1483
  end
1463
1484
  end
1464
1485
  .render
1465
-
1466
- $stdout.puts table
1467
- $stdout.puts
1468
1486
  end
1469
1487
 
1470
1488
  # Fetch shares, swallowing API errors so the dashboard never bails
@@ -1,3 +1,3 @@
1
1
  module LocalVault
2
- VERSION = "1.5.1"
2
+ VERSION = "1.5.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: localvault
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nauman Tariq