source_monitor 0.7.1 → 0.8.1

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +18 -6
  3. data/.claude/skills/sm-configure/SKILL.md +10 -1
  4. data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
  5. data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
  6. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
  7. data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
  8. data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
  9. data/.gitignore +10 -0
  10. data/AGENTS.md +1 -1
  11. data/CHANGELOG.md +45 -0
  12. data/CLAUDE.md +24 -3
  13. data/Gemfile.lock +1 -1
  14. data/README.md +6 -4
  15. data/Rakefile +0 -2
  16. data/VERSION +1 -1
  17. data/app/assets/builds/source_monitor/application.css +43 -0
  18. data/app/assets/builds/source_monitor/application.js +127 -0
  19. data/app/assets/builds/source_monitor/application.js.map +3 -3
  20. data/app/assets/javascripts/source_monitor/application.js +2 -0
  21. data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
  22. data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
  23. data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
  24. data/app/controllers/source_monitor/sources_controller.rb +11 -0
  25. data/app/helpers/source_monitor/application_helper.rb +51 -0
  26. data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
  27. data/app/jobs/source_monitor/import_opml_job.rb +9 -0
  28. data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
  29. data/app/models/source_monitor/source.rb +2 -0
  30. data/app/views/layouts/source_monitor/application.html.erb +23 -2
  31. data/app/views/source_monitor/import_sessions/steps/_preview.html.erb +7 -2
  32. data/app/views/source_monitor/shared/_toast.html.erb +1 -0
  33. data/app/views/source_monitor/sources/_details.html.erb +34 -5
  34. data/app/views/source_monitor/sources/_row.html.erb +11 -6
  35. data/config/routes.rb +1 -0
  36. data/docs/configuration.md +1 -1
  37. data/docs/upgrade.md +22 -0
  38. data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
  39. data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
  40. data/lib/source_monitor/configuration/http_settings.rb +1 -1
  41. data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
  42. data/lib/source_monitor/configuration.rb +3 -1
  43. data/lib/source_monitor/favicons/discoverer.rb +196 -0
  44. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
  45. data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
  46. data/lib/source_monitor/http.rb +5 -3
  47. data/lib/source_monitor/version.rb +1 -1
  48. data/lib/source_monitor.rb +4 -0
  49. data/source_monitor.gemspec +1 -1
  50. metadata +6 -106
  51. data/.vbw-planning/PROJECT.md +0 -51
  52. data/.vbw-planning/ROADMAP.md +0 -53
  53. data/.vbw-planning/SHIPPED.md +0 -63
  54. data/.vbw-planning/STATE.md +0 -27
  55. data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
  56. data/.vbw-planning/codebase/CONCERNS.md +0 -99
  57. data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
  58. data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
  59. data/.vbw-planning/codebase/INDEX.md +0 -86
  60. data/.vbw-planning/codebase/META.md +0 -42
  61. data/.vbw-planning/codebase/PATTERNS.md +0 -262
  62. data/.vbw-planning/codebase/STACK.md +0 -101
  63. data/.vbw-planning/codebase/STRUCTURE.md +0 -324
  64. data/.vbw-planning/codebase/TESTING.md +0 -154
  65. data/.vbw-planning/config.json +0 -53
  66. data/.vbw-planning/discovery.json +0 -26
  67. data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
  68. data/.vbw-planning/milestones/default/STATE.md +0 -82
  69. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
  70. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
  71. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
  72. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
  73. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
  74. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
  75. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
  76. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
  77. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
  78. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
  79. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
  80. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
  81. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
  82. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
  83. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
  84. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
  85. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
  86. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
  87. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
  88. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
  89. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
  90. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
  91. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
  92. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
  93. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
  94. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
  95. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
  96. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
  97. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
  98. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
  99. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
  100. data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
  101. data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
  102. data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
  103. data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
  104. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
  105. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
  106. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
  107. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
  108. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
  109. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
  110. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
  111. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
  112. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
  113. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
  114. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
  115. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
  116. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
  117. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
  118. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
  119. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
  120. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
  121. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
  122. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
  123. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
  124. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
  125. data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
  126. data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
  127. data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
  128. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
  129. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
  130. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
  131. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
  132. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
  133. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
  134. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
  135. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
  136. data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
  137. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
  138. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
  139. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
  140. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
  141. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
  142. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
  143. data/.vbw-planning/phases/02-test-performance/.context-dev.md +0 -75
  144. data/.vbw-planning/phases/02-test-performance/.context-lead.md +0 -89
  145. data/.vbw-planning/phases/02-test-performance/.context-qa.md +0 -23
  146. data/.vbw-planning/phases/02-test-performance/02-RESEARCH.md +0 -56
  147. data/.vbw-planning/phases/02-test-performance/02-VERIFICATION.md +0 -51
  148. data/.vbw-planning/phases/02-test-performance/PLAN-01-SUMMARY.md +0 -37
  149. data/.vbw-planning/phases/02-test-performance/PLAN-01.md +0 -156
  150. data/.vbw-planning/phases/02-test-performance/PLAN-02-SUMMARY.md +0 -33
  151. data/.vbw-planning/phases/02-test-performance/PLAN-02.md +0 -120
  152. data/.vbw-planning/phases/02-test-performance/PLAN-03-SUMMARY.md +0 -30
  153. data/.vbw-planning/phases/02-test-performance/PLAN-03.md +0 -154
  154. data/.vbw-planning/phases/02-test-performance/PLAN-04-SUMMARY.md +0 -28
  155. data/.vbw-planning/phases/02-test-performance/PLAN-04.md +0 -133
@@ -2488,6 +2488,7 @@ var notification_controller_default = class extends Controller {
2488
2488
  }
2489
2489
  this.clearTimeout();
2490
2490
  this.registerController();
2491
+ this.applyLevelDelay();
2491
2492
  this.startTimer();
2492
2493
  }
2493
2494
  disconnect() {
@@ -2505,8 +2506,17 @@ var notification_controller_default = class extends Controller {
2505
2506
  if (this.delayValue <= 0) return;
2506
2507
  this.timeoutId = window.setTimeout(() => this.dismiss(), this.delayValue);
2507
2508
  }
2509
+ applyLevelDelay() {
2510
+ const level = this.element.dataset.level;
2511
+ if (level === "error" && this.delayValue === 5e3) {
2512
+ this.delayValue = 1e4;
2513
+ }
2514
+ }
2508
2515
  dismiss() {
2509
2516
  if (!this.element) return;
2517
+ this.element.dispatchEvent(
2518
+ new CustomEvent("notification:dismissed", { bubbles: true })
2519
+ );
2510
2520
  this.element.classList.add("opacity-0", "translate-y-2");
2511
2521
  window.setTimeout(() => {
2512
2522
  if (this.element && this.element.remove) {
@@ -2521,6 +2531,122 @@ var notification_controller_default = class extends Controller {
2521
2531
  }
2522
2532
  };
2523
2533
 
2534
+ // app/assets/javascripts/source_monitor/controllers/notification_container_controller.js
2535
+ var notification_container_controller_default = class extends Controller {
2536
+ static values = {
2537
+ maxVisible: { default: 3, type: Number },
2538
+ expanded: { default: false, type: Boolean }
2539
+ };
2540
+ static targets = ["list", "badge", "badgeCount", "clearAll"];
2541
+ connect() {
2542
+ this.rafId = null;
2543
+ this.boundHandleClickOutside = this.handleClickOutside.bind(this);
2544
+ this.boundScheduleRecalculate = () => this.scheduleRecalculate();
2545
+ this.observer = new MutationObserver(this.boundScheduleRecalculate);
2546
+ this.observer.observe(this.listTarget, { childList: true });
2547
+ this.listTarget.addEventListener(
2548
+ "notification:dismissed",
2549
+ this.boundScheduleRecalculate
2550
+ );
2551
+ this.recalculateVisibility();
2552
+ }
2553
+ disconnect() {
2554
+ if (this.observer) {
2555
+ this.observer.disconnect();
2556
+ this.observer = null;
2557
+ }
2558
+ if (this.rafId) {
2559
+ cancelAnimationFrame(this.rafId);
2560
+ this.rafId = null;
2561
+ }
2562
+ this.listTarget.removeEventListener(
2563
+ "notification:dismissed",
2564
+ this.boundScheduleRecalculate
2565
+ );
2566
+ document.removeEventListener("click", this.boundHandleClickOutside);
2567
+ }
2568
+ scheduleRecalculate() {
2569
+ if (this.rafId) {
2570
+ cancelAnimationFrame(this.rafId);
2571
+ }
2572
+ this.rafId = requestAnimationFrame(() => {
2573
+ this.rafId = null;
2574
+ this.recalculateVisibility();
2575
+ });
2576
+ }
2577
+ recalculateVisibility() {
2578
+ const toasts = Array.from(this.listTarget.children);
2579
+ const total = toasts.length;
2580
+ if (this.expandedValue) {
2581
+ toasts.forEach((toast) => {
2582
+ toast.classList.remove("hidden");
2583
+ toast.removeAttribute("aria-hidden");
2584
+ toast.removeAttribute("inert");
2585
+ });
2586
+ } else {
2587
+ toasts.forEach((toast, index) => {
2588
+ if (index < this.maxVisibleValue) {
2589
+ toast.classList.remove("hidden");
2590
+ toast.removeAttribute("aria-hidden");
2591
+ toast.removeAttribute("inert");
2592
+ } else {
2593
+ toast.classList.add("hidden");
2594
+ toast.setAttribute("aria-hidden", "true");
2595
+ toast.setAttribute("inert", "");
2596
+ }
2597
+ });
2598
+ }
2599
+ const hiddenCount = this.expandedValue ? 0 : Math.max(0, total - this.maxVisibleValue);
2600
+ if (this.hasBadgeTarget) {
2601
+ if (this.hasBadgeCountTarget) {
2602
+ this.badgeCountTarget.textContent = `+${hiddenCount} more`;
2603
+ }
2604
+ if (hiddenCount > 0) {
2605
+ this.badgeTarget.classList.remove("hidden");
2606
+ } else {
2607
+ this.badgeTarget.classList.add("hidden");
2608
+ }
2609
+ }
2610
+ if (this.hasClearAllTarget) {
2611
+ const showClearAll = total > 0 && (hiddenCount > 0 || this.expandedValue);
2612
+ if (showClearAll) {
2613
+ this.clearAllTarget.classList.remove("hidden");
2614
+ } else {
2615
+ this.clearAllTarget.classList.add("hidden");
2616
+ }
2617
+ }
2618
+ }
2619
+ toggleExpand(event) {
2620
+ event.preventDefault();
2621
+ if (this.expandedValue) {
2622
+ this.collapseStack();
2623
+ } else {
2624
+ this.expandStack();
2625
+ }
2626
+ }
2627
+ expandStack() {
2628
+ this.expandedValue = true;
2629
+ this.recalculateVisibility();
2630
+ document.addEventListener("click", this.boundHandleClickOutside);
2631
+ }
2632
+ collapseStack() {
2633
+ this.expandedValue = false;
2634
+ document.removeEventListener("click", this.boundHandleClickOutside);
2635
+ this.recalculateVisibility();
2636
+ }
2637
+ handleClickOutside(event) {
2638
+ if (!this.element.contains(event.target)) {
2639
+ this.collapseStack();
2640
+ }
2641
+ }
2642
+ clearAll(event) {
2643
+ event.preventDefault();
2644
+ const toasts = Array.from(this.listTarget.children);
2645
+ toasts.forEach((toast) => toast.remove());
2646
+ this.collapseStack();
2647
+ }
2648
+ };
2649
+
2524
2650
  // app/assets/javascripts/source_monitor/controllers/dropdown_controller.js
2525
2651
  var dropdown_controller_default = class extends Controller {
2526
2652
  static targets = ["menu"];
@@ -2746,6 +2872,7 @@ if (!existingApplication) {
2746
2872
  window.SourceMonitorStimulus = application;
2747
2873
  }
2748
2874
  application.register("notification", notification_controller_default);
2875
+ application.register("notification-container", notification_container_controller_default);
2749
2876
  application.register("async-submit", async_submit_controller_default);
2750
2877
  application.register("dropdown", dropdown_controller_default);
2751
2878
  application.register("modal", modal_controller_default);