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.
- checksums.yaml +4 -4
- data/.claude/commands/release.md +18 -6
- data/.claude/skills/sm-configure/SKILL.md +10 -1
- data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
- data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
- data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
- data/.gitignore +10 -0
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +45 -0
- data/CLAUDE.md +24 -3
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/Rakefile +0 -2
- data/VERSION +1 -1
- data/app/assets/builds/source_monitor/application.css +43 -0
- data/app/assets/builds/source_monitor/application.js +127 -0
- data/app/assets/builds/source_monitor/application.js.map +3 -3
- data/app/assets/javascripts/source_monitor/application.js +2 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
- data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
- data/app/controllers/source_monitor/sources_controller.rb +11 -0
- data/app/helpers/source_monitor/application_helper.rb +51 -0
- data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
- data/app/jobs/source_monitor/import_opml_job.rb +9 -0
- data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
- data/app/models/source_monitor/source.rb +2 -0
- data/app/views/layouts/source_monitor/application.html.erb +23 -2
- data/app/views/source_monitor/import_sessions/steps/_preview.html.erb +7 -2
- data/app/views/source_monitor/shared/_toast.html.erb +1 -0
- data/app/views/source_monitor/sources/_details.html.erb +34 -5
- data/app/views/source_monitor/sources/_row.html.erb +11 -6
- data/config/routes.rb +1 -0
- data/docs/configuration.md +1 -1
- data/docs/upgrade.md +22 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
- data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
- data/lib/source_monitor/configuration/http_settings.rb +1 -1
- data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
- data/lib/source_monitor/configuration.rb +3 -1
- data/lib/source_monitor/favicons/discoverer.rb +196 -0
- data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
- data/lib/source_monitor/http.rb +5 -3
- data/lib/source_monitor/version.rb +1 -1
- data/lib/source_monitor.rb +4 -0
- data/source_monitor.gemspec +1 -1
- metadata +6 -106
- data/.vbw-planning/PROJECT.md +0 -51
- data/.vbw-planning/ROADMAP.md +0 -53
- data/.vbw-planning/SHIPPED.md +0 -63
- data/.vbw-planning/STATE.md +0 -27
- data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
- data/.vbw-planning/codebase/CONCERNS.md +0 -99
- data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
- data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
- data/.vbw-planning/codebase/INDEX.md +0 -86
- data/.vbw-planning/codebase/META.md +0 -42
- data/.vbw-planning/codebase/PATTERNS.md +0 -262
- data/.vbw-planning/codebase/STACK.md +0 -101
- data/.vbw-planning/codebase/STRUCTURE.md +0 -324
- data/.vbw-planning/codebase/TESTING.md +0 -154
- data/.vbw-planning/config.json +0 -53
- data/.vbw-planning/discovery.json +0 -26
- data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
- data/.vbw-planning/milestones/default/STATE.md +0 -82
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
- data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
- data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
- data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
- data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
- data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
- data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
- data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
- data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
- data/.vbw-planning/phases/02-test-performance/.context-dev.md +0 -75
- data/.vbw-planning/phases/02-test-performance/.context-lead.md +0 -89
- data/.vbw-planning/phases/02-test-performance/.context-qa.md +0 -23
- data/.vbw-planning/phases/02-test-performance/02-RESEARCH.md +0 -56
- data/.vbw-planning/phases/02-test-performance/02-VERIFICATION.md +0 -51
- data/.vbw-planning/phases/02-test-performance/PLAN-01-SUMMARY.md +0 -37
- data/.vbw-planning/phases/02-test-performance/PLAN-01.md +0 -156
- data/.vbw-planning/phases/02-test-performance/PLAN-02-SUMMARY.md +0 -33
- data/.vbw-planning/phases/02-test-performance/PLAN-02.md +0 -120
- data/.vbw-planning/phases/02-test-performance/PLAN-03-SUMMARY.md +0 -30
- data/.vbw-planning/phases/02-test-performance/PLAN-03.md +0 -154
- data/.vbw-planning/phases/02-test-performance/PLAN-04-SUMMARY.md +0 -28
- data/.vbw-planning/phases/02-test-performance/PLAN-04.md +0 -133
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16d5152ca0edc61e1d7f67878516e93769dd43c34daa757c554d36355b998fec
|
|
4
|
+
data.tar.gz: f93013eedf10c9af8b758b302993f701531b51170a399ea1e09c0f53efb4498d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d06eedf5dd22a3cf5c96a252883870b7f416c53f41da5121100b3a66564e7c3021a963512c6355f3aa52be6e30aba23d74b135e47bc1e6fbc17401fc929d5865
|
|
7
|
+
data.tar.gz: 70655afb16134fecaa25f6547ed8ef05e2fcb7c61fbf3d2b46c7c9b1199eb8f14503db99eb8e165b71927bc9ba4301c3e035e6973eb3fbec85fc3f5be17b1eeb
|
data/.claude/commands/release.md
CHANGED
|
@@ -17,8 +17,15 @@ These are real issues encountered in previous releases. Each step below accounts
|
|
|
17
17
|
5. **Single squashed commit**: Always create ONE commit on the release branch with ALL changes (version bump, changelog, Gemfile.lock, any fixes). Multiple commits cause pre-push hook issues.
|
|
18
18
|
6. **Diff coverage CI gate**: The `test` CI job enforces diff coverage. Any changed lines in source code (not just test files) must have test coverage. **This applies to ALL changes in the PR diff vs main, including unpushed commits made before the release started.** If the release includes source code changes (bug fixes, features), every changed source line must be covered.
|
|
19
19
|
7. **Local main divergence after merge**: After the PR merges, `gh pr merge --merge --delete-branch` will attempt to fast-forward local main. This usually succeeds automatically. If it doesn't (divergent history), you must `git reset --hard origin/main` to sync -- this requires user approval since the sandbox blocks it.
|
|
20
|
-
8. **Run local
|
|
21
|
-
|
|
20
|
+
8. **Run FULL local CI suite BEFORE pushing**: Always run ALL of these locally before the first push to the release branch:
|
|
21
|
+
- `bin/rubocop` -- Ruby lint
|
|
22
|
+
- `PARALLEL_WORKERS=1 bin/rails test` -- tests + diff coverage readiness
|
|
23
|
+
- `bin/brakeman --no-pager` -- security scan
|
|
24
|
+
- `yarn build` -- rebuild JS assets (catches ESLint issues; CI runs ESLint separately on JS files)
|
|
25
|
+
Each CI roundtrip (fail → fix → amend → force-push → re-run) costs ~5 minutes. In v0.7.0, skipping local checks caused two wasted CI cycles. In v0.8.0, skipping ESLint (`yarn build`) and diff coverage pre-checks caused another two wasted cycles: first for ESLint `no-undef` on browser globals (MutationObserver, requestAnimationFrame), then for 13 uncovered rescue/error path lines across 3 files.
|
|
26
|
+
9. **ESLint browser globals**: Any JS file using browser APIs (MutationObserver, requestAnimationFrame, cancelAnimationFrame, IntersectionObserver, etc.) MUST declare them with a `/* global ... */` comment at the top. ESLint's `no-undef` rule in CI will reject them otherwise.
|
|
27
|
+
10. **Diff coverage rescue paths**: Every `rescue`/fallback/error handling branch in changed source code needs test coverage. Common blind spots: `rescue StandardError => e` logging, `rescue URI::InvalidURIError` returning nil, fallback `false` returns. Write targeted tests for these BEFORE creating the release commit.
|
|
28
|
+
11. **Zsh glob nomatch**: Commands like `rm -f *.gem` fail in zsh when no files match. Always use `rm -f *.gem 2>/dev/null || true` or check existence first with `ls`.
|
|
22
29
|
|
|
23
30
|
## Step 1: Git Hygiene
|
|
24
31
|
|
|
@@ -116,21 +123,26 @@ The changelog follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) f
|
|
|
116
123
|
|
|
117
124
|
## Step 5: Local Pre-flight Checks
|
|
118
125
|
|
|
119
|
-
**CRITICAL**: Run
|
|
126
|
+
**CRITICAL**: Run the FULL local CI equivalent BEFORE creating the release branch and pushing. Each CI failure → fix → amend → force-push cycle wastes ~5 minutes. In v0.7.0, skipping this step caused two wasted CI roundtrips. In v0.8.0, skipping ESLint and diff coverage pre-checks caused another two wasted cycles.
|
|
120
127
|
|
|
121
128
|
1. **RuboCop**: Run `bin/rubocop` and fix any violations. Auto-fix with `bin/rubocop -a` if needed. This catches lint issues (like `SpaceInsideArrayLiteralBrackets`) that would fail the CI lint job.
|
|
122
129
|
|
|
123
130
|
2. **Tests**: Run `PARALLEL_WORKERS=1 bin/rails test` and ensure all tests pass.
|
|
124
131
|
|
|
125
132
|
3. **Diff coverage pre-check**: If the release includes source code changes beyond version/changelog/lockfile (check with `git diff --name-only origin/main`), review those files for uncovered branches. The CI diff coverage gate will reject any changed source lines without test coverage. Common blind spots:
|
|
133
|
+
- `rescue StandardError` logging/fallback paths
|
|
134
|
+
- `rescue URI::InvalidURIError` nil returns
|
|
135
|
+
- Guard clauses returning early (e.g., `return if blank?`)
|
|
126
136
|
- Fallback/else branches in new methods
|
|
127
|
-
- Error handling paths
|
|
128
|
-
- Guard clauses
|
|
129
137
|
If you find uncovered source lines, write tests for them NOW before creating the release commit — it's far cheaper than a CI roundtrip.
|
|
130
138
|
|
|
131
139
|
4. **Brakeman**: Run `bin/brakeman --no-pager` and ensure zero warnings.
|
|
132
140
|
|
|
133
|
-
|
|
141
|
+
5. **ESLint / JS build**: If any `.js` files were changed, run `yarn build` to rebuild assets. CI runs ESLint separately on JS files and will catch issues RuboCop doesn't:
|
|
142
|
+
- Browser globals (MutationObserver, requestAnimationFrame, cancelAnimationFrame, IntersectionObserver, etc.) must be declared with `/* global ... */` comments at the top of the file.
|
|
143
|
+
- Missing `/* global */` declarations cause ESLint `no-undef` failures.
|
|
144
|
+
|
|
145
|
+
Only proceed to Step 6 when ALL five checks pass.
|
|
134
146
|
|
|
135
147
|
## Step 6: Create Release Branch with Single Squashed Commit
|
|
136
148
|
|
|
@@ -29,7 +29,7 @@ After the block executes, `ModelExtensions.reload!` runs automatically to apply
|
|
|
29
29
|
|
|
30
30
|
## Configuration Sections
|
|
31
31
|
|
|
32
|
-
The `config` object (`SourceMonitor::Configuration`) has
|
|
32
|
+
The `config` object (`SourceMonitor::Configuration`) has 13 sub-sections plus top-level queue/job settings:
|
|
33
33
|
|
|
34
34
|
| Section | Accessor | Class |
|
|
35
35
|
|---|---|---|
|
|
@@ -45,6 +45,7 @@ The `config` object (`SourceMonitor::Configuration`) has 11 sub-sections plus to
|
|
|
45
45
|
| Realtime | `config.realtime` | `RealtimeSettings` |
|
|
46
46
|
| Authentication | `config.authentication` | `AuthenticationSettings` |
|
|
47
47
|
| Images | `config.images` | `ImagesSettings` |
|
|
48
|
+
| Favicons | `config.favicons` | `FaviconsSettings` |
|
|
48
49
|
|
|
49
50
|
See `reference/configuration-reference.md` for every setting with types, defaults, and examples.
|
|
50
51
|
|
|
@@ -91,6 +92,14 @@ config.models.source.include_concern "MyApp::SourceExtension"
|
|
|
91
92
|
config.models.item.validate :custom_check
|
|
92
93
|
```
|
|
93
94
|
|
|
95
|
+
### Favicons (Active Storage)
|
|
96
|
+
```ruby
|
|
97
|
+
config.favicons.enabled = true
|
|
98
|
+
config.favicons.fetch_timeout = 10
|
|
99
|
+
config.favicons.max_download_size = 512 * 1024 # 512 KB
|
|
100
|
+
config.favicons.retry_cooldown_days = 14
|
|
101
|
+
```
|
|
102
|
+
|
|
94
103
|
### Realtime
|
|
95
104
|
```ruby
|
|
96
105
|
config.realtime.adapter = :redis
|
|
@@ -342,6 +342,50 @@ When enabled, `DownloadContentImagesJob` is automatically enqueued after new ite
|
|
|
342
342
|
|
|
343
343
|
---
|
|
344
344
|
|
|
345
|
+
## Favicons Settings (`config.favicons`)
|
|
346
|
+
|
|
347
|
+
Class: `SourceMonitor::Configuration::FaviconsSettings`
|
|
348
|
+
|
|
349
|
+
Controls automatic favicon fetching and storage for sources via Active Storage.
|
|
350
|
+
|
|
351
|
+
**Prerequisite:** The host app must have Active Storage installed (`rails active_storage:install` + migrations). Without Active Storage, favicons are silently disabled and colored initials placeholders are shown instead.
|
|
352
|
+
|
|
353
|
+
| Setting | Type | Default | Description |
|
|
354
|
+
|---|---|---|---|
|
|
355
|
+
| `enabled` | Boolean | `true` | Enable automatic favicon fetching |
|
|
356
|
+
| `fetch_timeout` | Integer | `5` | HTTP timeout for favicon requests (seconds) |
|
|
357
|
+
| `max_download_size` | Integer | `1048576` (1 MB) | Maximum favicon file size in bytes; larger files are skipped |
|
|
358
|
+
| `retry_cooldown_days` | Integer | `7` | Days to wait before retrying a failed favicon fetch |
|
|
359
|
+
| `allowed_content_types` | Array | `["image/x-icon", "image/vnd.microsoft.icon", "image/png", "image/jpeg", "image/gif", "image/svg+xml", "image/webp"]` | Permitted MIME types for downloaded favicons |
|
|
360
|
+
|
|
361
|
+
### Helper Method
|
|
362
|
+
|
|
363
|
+
| Method | Returns | Description |
|
|
364
|
+
|---|---|---|
|
|
365
|
+
| `enabled?` | Boolean | Returns `true` when `enabled` is truthy AND `ActiveStorage` is defined |
|
|
366
|
+
|
|
367
|
+
```ruby
|
|
368
|
+
# Customize favicon settings
|
|
369
|
+
config.favicons.enabled = true
|
|
370
|
+
config.favicons.fetch_timeout = 10
|
|
371
|
+
config.favicons.max_download_size = 512 * 1024 # 512 KB
|
|
372
|
+
config.favicons.retry_cooldown_days = 14
|
|
373
|
+
config.favicons.allowed_content_types = %w[image/png image/x-icon image/svg+xml]
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
When enabled, `FaviconFetchJob` is automatically enqueued:
|
|
377
|
+
1. After a new source is created (via UI or OPML import) with a `website_url`
|
|
378
|
+
2. After a successful feed fetch when the source has no favicon attached and is outside the retry cooldown
|
|
379
|
+
|
|
380
|
+
The job uses `Favicons::Discoverer` which tries three strategies in order:
|
|
381
|
+
1. Direct `/favicon.ico` fetch from the source's domain
|
|
382
|
+
2. HTML page parsing for `<link rel="icon">`, `<link rel="apple-touch-icon">`, and similar tags (prefers largest by `sizes` attribute)
|
|
383
|
+
3. Google Favicon API as a last resort
|
|
384
|
+
|
|
385
|
+
Failed attempts are tracked in the source's `metadata` JSONB column (`favicon_last_attempted_at`) to respect the cooldown period.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
345
389
|
## Environment Variables
|
|
346
390
|
|
|
347
391
|
| Variable | Purpose |
|
|
@@ -176,6 +176,23 @@ SourceMonitor.configure do |config|
|
|
|
176
176
|
# record.errors.add(:base, "custom error") unless record.valid_for_my_app?
|
|
177
177
|
# }
|
|
178
178
|
|
|
179
|
+
# ===========================================================================
|
|
180
|
+
# Favicons (Active Storage)
|
|
181
|
+
# ===========================================================================
|
|
182
|
+
# Automatically fetch and store source favicons via Active Storage.
|
|
183
|
+
# Requires Active Storage in the host app (rails active_storage:install).
|
|
184
|
+
# Without Active Storage, favicons are silently disabled -- colored
|
|
185
|
+
# initials placeholders are shown instead.
|
|
186
|
+
|
|
187
|
+
# config.favicons.enabled = true # default: true
|
|
188
|
+
# config.favicons.fetch_timeout = 5 # seconds
|
|
189
|
+
# config.favicons.max_download_size = 1_048_576 # 1 MB
|
|
190
|
+
# config.favicons.retry_cooldown_days = 7
|
|
191
|
+
# config.favicons.allowed_content_types = %w[
|
|
192
|
+
# image/x-icon image/vnd.microsoft.icon image/png
|
|
193
|
+
# image/jpeg image/gif image/svg+xml image/webp
|
|
194
|
+
# ]
|
|
195
|
+
|
|
179
196
|
# ===========================================================================
|
|
180
197
|
# Realtime (Action Cable) Adapter
|
|
181
198
|
# ===========================================================================
|
|
@@ -155,6 +155,8 @@ bin/source_monitor verify
|
|
|
155
155
|
- [ ] Event callbacks wired for host integration
|
|
156
156
|
- [ ] Realtime adapter confirmed (Solid Cable or Redis)
|
|
157
157
|
- [ ] Mission Control integration enabled (if desired)
|
|
158
|
+
- [ ] Active Storage installed (required for favicons and image downloads)
|
|
159
|
+
- [ ] Favicon settings configured (`config.favicons.*`) if customization needed
|
|
158
160
|
|
|
159
161
|
## Troubleshooting
|
|
160
162
|
|
|
@@ -164,6 +164,32 @@ class ScheduleFetchesJob < ApplicationJob
|
|
|
164
164
|
end
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
+
### Lightweight Fetch Job (FaviconFetchJob)
|
|
168
|
+
|
|
169
|
+
Demonstrates multi-strategy cascade with guard clauses:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
class FaviconFetchJob < ApplicationJob
|
|
173
|
+
source_monitor_queue :fetch
|
|
174
|
+
discard_on ActiveJob::DeserializationError
|
|
175
|
+
|
|
176
|
+
def perform(source_id)
|
|
177
|
+
source = Source.find_by(id: source_id)
|
|
178
|
+
return unless source
|
|
179
|
+
return unless should_fetch?(source)
|
|
180
|
+
|
|
181
|
+
result = Favicons::Discoverer.new(source: source).call
|
|
182
|
+
attach_favicon(source, result) if result.success?
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Notable patterns:
|
|
188
|
+
- Multiple guard clauses: source exists, Active Storage defined, no existing favicon, outside cooldown period
|
|
189
|
+
- Uses `Favicons::Discoverer` service with 3-strategy cascade (direct `/favicon.ico`, HTML parsing, Google API)
|
|
190
|
+
- Failed attempts tracked in source `metadata` JSONB (`favicon_last_attempted_at`) for retry cooldown
|
|
191
|
+
- Graceful degradation: host apps without Active Storage never enqueue this job
|
|
192
|
+
|
|
167
193
|
### Broadcast Job (SourceHealthCheckJob)
|
|
168
194
|
|
|
169
195
|
Demonstrates result broadcasting:
|
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Version-specific migration notes for each major/minor version transition. Agents should reference this file when guiding users through multi-version upgrades.
|
|
4
4
|
|
|
5
|
+
## 0.7.x to 0.8.0
|
|
6
|
+
|
|
7
|
+
**Key changes:**
|
|
8
|
+
- Default HTTP User-Agent changed from `SourceMonitor/<version>` to `Mozilla/5.0 (compatible; SourceMonitor/<version>)` with browser-like headers (Accept-Language, DNT, Referer). Prevents bot-blocking by feed servers.
|
|
9
|
+
- Default `max_in_flight_per_source` changed from `25` to `nil` (unlimited). If you relied on the previous default for per-source rate limiting, set it explicitly.
|
|
10
|
+
- Successful manual health checks on degraded sources now trigger a feed fetch for faster recovery.
|
|
11
|
+
- Automatic source favicons via Active Storage with multi-strategy discovery (direct `/favicon.ico`, HTML `<link>` parsing, Google Favicon API fallback)
|
|
12
|
+
- New configuration section: `config.favicons` with `enabled`, `fetch_timeout`, `max_download_size`, `retry_cooldown_days`, and `allowed_content_types` settings
|
|
13
|
+
- Colored initials placeholder shown when no favicon is available or Active Storage is not installed
|
|
14
|
+
- OPML imports trigger favicon fetches for each imported source with a `website_url`
|
|
15
|
+
- Toast notifications capped at 3 visible with "+N more" badge, click-to-expand, and "Clear all" button
|
|
16
|
+
- Error-level toasts auto-dismiss after 10 seconds (vs 5 seconds for info/success)
|
|
17
|
+
|
|
18
|
+
**Action items:**
|
|
19
|
+
1. Re-run `bin/rails source_monitor:upgrade` to get updated initializer template
|
|
20
|
+
2. If you explicitly set `config.http.user_agent`, your value is preserved. Otherwise the new browser-like default applies automatically.
|
|
21
|
+
3. If you need per-source scrape rate limiting, add `config.scraping.max_in_flight_per_source = 25` (or your preferred value) to your initializer
|
|
22
|
+
4. If using Active Storage, favicons are enabled by default -- no action needed
|
|
23
|
+
5. If NOT using Active Storage, favicons are silently disabled -- no action needed
|
|
24
|
+
6. Toast stacking is automatic -- no configuration needed
|
|
25
|
+
7. No breaking changes -- all existing configuration remains valid
|
|
26
|
+
|
|
5
27
|
## 0.3.x to 0.4.0
|
|
6
28
|
|
|
7
29
|
**Released:** 2026-02-12
|
data/.gitignore
CHANGED
|
@@ -27,5 +27,15 @@
|
|
|
27
27
|
.vbw-planning/.active-agent
|
|
28
28
|
.vbw-planning/.active-agent-count
|
|
29
29
|
.vbw-planning/.todo-flat-migrated
|
|
30
|
+
.vbw-planning/.agent-worktrees/
|
|
31
|
+
.vbw-planning/.cache/
|
|
32
|
+
.vbw-planning/.context-usage
|
|
33
|
+
.vbw-planning/.contracts/
|
|
34
|
+
.vbw-planning/.events/
|
|
35
|
+
.vbw-planning/.execution-state.json
|
|
30
36
|
/codebase_analysis.md
|
|
37
|
+
/VERIFICATION.md
|
|
38
|
+
/test/dummy/public/assets/
|
|
39
|
+
/test/lib/tmp/
|
|
31
40
|
*.gem
|
|
41
|
+
.vbw-worktrees/
|
data/AGENTS.md
CHANGED
|
@@ -83,7 +83,7 @@ Store secrets (API keys, webhook tokens) in `config/credentials/` and never comm
|
|
|
83
83
|
|
|
84
84
|
## Claude Code Skills
|
|
85
85
|
|
|
86
|
-
SourceMonitor ships
|
|
86
|
+
SourceMonitor ships 15 engine-specific Claude Code skills (`sm-*` prefix) covering the domain model, configuration DSL, pipeline stages, testing conventions, and more. Skills are distributed with the gem and installed into `.claude/skills/` via rake tasks:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
bin/rails source_monitor:skills:install # Consumer skills (host app integration)
|
data/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,51 @@ All notable changes to this project are documented below. The format follows [Ke
|
|
|
15
15
|
|
|
16
16
|
- No unreleased changes yet.
|
|
17
17
|
|
|
18
|
+
## [0.8.1] - 2026-02-21
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **OPML import now imports all selected feeds across pages.** Previously, only the 25 feeds visible on the current preview page were imported. Pagination links inside the preview form triggered full-page navigation (bypassing Turbo Frames), which caused a "leave site?" warning and lost selections from other pages. Hidden fields now preserve selections across pages, and pagination uses Turbo Frame navigation.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Removed deprecated `rails/tasks/statistics.rake` from Rakefile (Rails 8.2 compatibility).
|
|
27
|
+
|
|
28
|
+
## [0.8.0] - 2026-02-21
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **Automatic source favicons.** Sources now display favicons next to their names in list and detail views. Favicons are fetched automatically via background job on source creation and successful feed fetches using a multi-strategy cascade: `/favicon.ico` direct fetch, HTML `<link>` tag parsing (preferring largest available), and Google Favicon API fallback. Requires Active Storage in the host app.
|
|
33
|
+
- New configuration section: `config.favicons` with `enabled` (default: `true`), `fetch_timeout` (5s), `max_download_size` (1MB), `retry_cooldown_days` (7), and `allowed_content_types` settings.
|
|
34
|
+
- Colored initials placeholder shown when no favicon is available (consistent HSL color derived from source name).
|
|
35
|
+
- Graceful degradation: host apps without Active Storage see placeholders only, no errors.
|
|
36
|
+
- OPML imports also trigger favicon fetches for each imported source with a `website_url`.
|
|
37
|
+
- Manual "Fetch Favicon" button on source detail pages; favicon fetch also triggered on 304 Not Modified responses when missing.
|
|
38
|
+
- Redirect-following in favicon discoverer for domains that redirect (e.g., `reddit.com` -> `www.reddit.com`).
|
|
39
|
+
- **Toast notification stacking.** Bulk operations no longer flood the screen with overlapping toasts. At most 3 toasts are visible at a time; overflow is shown as a "+N more" badge that expands the full stack on click. "Clear all" button dismisses every toast at once.
|
|
40
|
+
- Error-level toasts persist for 10 seconds (vs 5 seconds for info/success).
|
|
41
|
+
- Hidden toasts promote into visible slots as earlier toasts auto-dismiss.
|
|
42
|
+
- Container controller tracks DOM changes via MutationObserver and properly cleans up event listeners on disconnect.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **Browser-like default User-Agent.** Default HTTP User-Agent changed from `SourceMonitor/<version>` to `Mozilla/5.0 (compatible; SourceMonitor/<version>)` with full browser-like headers (Accept, Accept-Language, DNT, Referer from source `website_url`). This prevents bot-blocking by feed servers.
|
|
47
|
+
- **Smarter scrape rate limiting.** Default `max_in_flight_per_source` changed from `25` to `nil` (unlimited). The previous default unnecessarily throttled scraping for sources with many items. Set an explicit value in your initializer if you need per-source caps.
|
|
48
|
+
- **Health check triggers status re-evaluation.** A successful manual health check on a degraded (declining/critical/warning) source now triggers a feed fetch, allowing the health monitor to transition the source back to "improving" status instead of requiring the source to recover on its own schedule.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Favicon discoverer properly follows HTTP redirects (e.g., `reddit.com` -> `www.reddit.com`).
|
|
53
|
+
- Favicon fetch uses `rails_blob_path` for correct routing within the engine context.
|
|
54
|
+
- Favicon display prefers PNG format (via Google Favicon API) over raw ICO for better browser compatibility.
|
|
55
|
+
- Gemspec excludes `.vbw-planning/` from gem package to reduce gem size.
|
|
56
|
+
|
|
57
|
+
### Testing
|
|
58
|
+
|
|
59
|
+
- 1,125 tests, 0 failures.
|
|
60
|
+
- RuboCop: 0 offenses.
|
|
61
|
+
- Brakeman: 0 warnings.
|
|
62
|
+
|
|
18
63
|
## [0.7.1] - 2026-02-18
|
|
19
64
|
|
|
20
65
|
### Changed
|
data/CLAUDE.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
## Active Context
|
|
6
6
|
|
|
7
7
|
**Milestone:** (none active)
|
|
8
|
-
**Last shipped:**
|
|
9
|
-
**Previous:**
|
|
8
|
+
**Last shipped:** polish-and-reliability (2026-02-21) -- v0.8.0, 3 phases, 1134 tests
|
|
9
|
+
**Previous:** aia-ssl-fix (2026-02-20), upgrade-assurance (2026-02-13)
|
|
10
10
|
**Next action:** /vbw:vibe to start a new milestone
|
|
11
11
|
|
|
12
12
|
## Key Decisions
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- Keep PostgreSQL-only for now
|
|
15
15
|
- Keep host-app auth model
|
|
16
16
|
- Ruby autoload for lib/ modules (not Zeitwerk)
|
|
17
|
-
- PG parallel fork segfault
|
|
17
|
+
- PG parallel fork segfault resolved: switched to thread-based parallelism in aia-ssl-fix milestone
|
|
18
18
|
|
|
19
19
|
## Installed Skills
|
|
20
20
|
|
|
@@ -97,9 +97,30 @@ Run /vbw:help for all commands.
|
|
|
97
97
|
- `bin/rubocop` -- zero offenses before commit.
|
|
98
98
|
- `bin/brakeman --no-pager` -- zero warnings before merge.
|
|
99
99
|
- `bin/rails test` -- all tests pass.
|
|
100
|
+
- `yarn build` -- rebuild JS assets if any `.js` files changed (ESLint runs in CI).
|
|
100
101
|
- No N+1 queries (use `includes`/`preload`).
|
|
101
102
|
- No hardcoded credentials (use Rails credentials or ENV).
|
|
102
103
|
|
|
104
|
+
### Pre-Push CI Checklist (run ALL before pushing to GitHub)
|
|
105
|
+
|
|
106
|
+
Before pushing any branch (especially release branches), run the full CI equivalent locally:
|
|
107
|
+
|
|
108
|
+
1. `bin/rubocop` -- catches Ruby lint issues
|
|
109
|
+
2. `PARALLEL_WORKERS=1 bin/rails test` -- catches test failures AND diff coverage gaps
|
|
110
|
+
3. `bin/brakeman --no-pager` -- catches security issues
|
|
111
|
+
4. `yarn build` -- rebuilds JS and catches ESLint issues (CI runs ESLint separately)
|
|
112
|
+
|
|
113
|
+
**Why:** CI failures cost ~5 min per round-trip. In v0.8.0, skipping ESLint and diff coverage checks locally caused 2 wasted CI cycles. Common blind spots:
|
|
114
|
+
- JS files need `/* global */` declarations for browser APIs (MutationObserver, requestAnimationFrame, etc.)
|
|
115
|
+
- Every `rescue` / fallback / error path in new source code needs test coverage (CI diff coverage gate rejects uncovered lines)
|
|
116
|
+
- `yarn build` must run after JS changes to sync sourcemaps
|
|
117
|
+
|
|
118
|
+
## QA and UAT Rules
|
|
119
|
+
|
|
120
|
+
- **Browser-first verification:** During VBW QA (`/vbw:qa`) and UAT (`/vbw:verify`), ALWAYS start by using `agent-browser` to test UI scenarios yourself before presenting checkpoints to the user. Navigate to the dummy app (port 3002), take snapshots/screenshots, and verify visual and functional behavior with agents first.
|
|
121
|
+
- **Automate what you can:** Any test that can be verified programmatically (config defaults, job enqueue behavior, controller responses) should be automated -- only present truly visual/interactive tests to the user.
|
|
122
|
+
- **Dummy app port:** The SourceMonitor dummy app runs on port 3002 (`cd test/dummy && bin/rails server -p 3002`).
|
|
123
|
+
|
|
103
124
|
## Security Rules
|
|
104
125
|
|
|
105
126
|
### Protected Files (NEVER read or output)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -9,8 +9,8 @@ SourceMonitor is a production-ready Rails 8 mountable engine for ingesting, norm
|
|
|
9
9
|
In your host Rails app:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
bundle add source_monitor --version "~> 0.
|
|
13
|
-
# or add `gem "source_monitor", "~> 0.
|
|
12
|
+
bundle add source_monitor --version "~> 0.7.1"
|
|
13
|
+
# or add `gem "source_monitor", "~> 0.7.1"` manually, then run:
|
|
14
14
|
bundle install
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -19,7 +19,9 @@ This exposes `bin/source_monitor` (via Bundler binstubs) so you can run the guid
|
|
|
19
19
|
## Highlights
|
|
20
20
|
- Full-featured source and item administration backed by Turbo Streams and Tailwind UI components
|
|
21
21
|
- Adaptive fetch pipeline (Feedjira + Faraday) with conditional GETs, retention pruning, and scrape orchestration
|
|
22
|
+
- Automatic source favicons via Active Storage with multi-strategy discovery and graceful fallback
|
|
22
23
|
- Realtime dashboard metrics, batching/caching query layer, and Mission Control integration hooks
|
|
24
|
+
- Smart toast notification stacking (max 3 visible, "+N more" overflow badge, click-to-expand)
|
|
23
25
|
- Extensible scraper adapters (Readability included) with per-source settings and structured result metadata
|
|
24
26
|
- Declarative configuration DSL covering queues, HTTP, retention, events, model extensions, authentication, and realtime transports
|
|
25
27
|
- First-class observability through ActiveSupport notifications and `SourceMonitor::Metrics` counters/gauges
|
|
@@ -41,7 +43,7 @@ This exposes `bin/source_monitor` (via Bundler binstubs) so you can run the guid
|
|
|
41
43
|
Before running any SourceMonitor commands inside your host app, add the gem and install dependencies:
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
bundle add source_monitor --version "~> 0.
|
|
46
|
+
bundle add source_monitor --version "~> 0.7.1"
|
|
45
47
|
# or edit your Gemfile, then run
|
|
46
48
|
bundle install
|
|
47
49
|
```
|
|
@@ -113,7 +115,7 @@ See [docs/configuration.md](docs/configuration.md) for exhaustive coverage and e
|
|
|
113
115
|
|
|
114
116
|
## Claude Code Skills
|
|
115
117
|
|
|
116
|
-
SourceMonitor ships
|
|
118
|
+
SourceMonitor ships 15 engine-specific Claude Code skills (`sm-*` prefix) that give AI agents deep context about the engine's domain model, configuration DSL, pipeline stages, and testing conventions. Skills are bundled with the gem and installed into your host app's `.claude/skills/` directory.
|
|
117
119
|
|
|
118
120
|
```bash
|
|
119
121
|
bin/rails source_monitor:skills:install # Consumer skills (host app integration)
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.8.1
|
|
@@ -651,6 +651,14 @@ video {
|
|
|
651
651
|
right: 0px;
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
+
.fm-admin .-bottom-1 {
|
|
655
|
+
bottom: -0.25rem;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.fm-admin .-right-1 {
|
|
659
|
+
right: -0.25rem;
|
|
660
|
+
}
|
|
661
|
+
|
|
654
662
|
.fm-admin .left-4 {
|
|
655
663
|
left: 1rem;
|
|
656
664
|
}
|
|
@@ -957,6 +965,10 @@ video {
|
|
|
957
965
|
align-items: flex-start;
|
|
958
966
|
}
|
|
959
967
|
|
|
968
|
+
.fm-admin .items-end {
|
|
969
|
+
align-items: flex-end;
|
|
970
|
+
}
|
|
971
|
+
|
|
960
972
|
.fm-admin .items-center {
|
|
961
973
|
align-items: center;
|
|
962
974
|
}
|
|
@@ -977,6 +989,10 @@ video {
|
|
|
977
989
|
gap: 0.25rem;
|
|
978
990
|
}
|
|
979
991
|
|
|
992
|
+
.fm-admin .gap-1\.5 {
|
|
993
|
+
gap: 0.375rem;
|
|
994
|
+
}
|
|
995
|
+
|
|
980
996
|
.fm-admin .gap-2 {
|
|
981
997
|
gap: 0.5rem;
|
|
982
998
|
}
|
|
@@ -1348,6 +1364,11 @@ video {
|
|
|
1348
1364
|
background-color: rgb(248 250 252 / var(--tw-bg-opacity, 1));
|
|
1349
1365
|
}
|
|
1350
1366
|
|
|
1367
|
+
.fm-admin .bg-slate-700 {
|
|
1368
|
+
--tw-bg-opacity: 1;
|
|
1369
|
+
background-color: rgb(51 65 85 / var(--tw-bg-opacity, 1));
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1351
1372
|
.fm-admin .bg-slate-800 {
|
|
1352
1373
|
--tw-bg-opacity: 1;
|
|
1353
1374
|
background-color: rgb(30 41 59 / var(--tw-bg-opacity, 1));
|
|
@@ -1367,6 +1388,15 @@ video {
|
|
|
1367
1388
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
|
1368
1389
|
}
|
|
1369
1390
|
|
|
1391
|
+
.fm-admin .object-contain {
|
|
1392
|
+
-o-object-fit: contain;
|
|
1393
|
+
object-fit: contain;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
.fm-admin .p-0\.5 {
|
|
1397
|
+
padding: 0.125rem;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1370
1400
|
.fm-admin .p-3 {
|
|
1371
1401
|
padding: 0.75rem;
|
|
1372
1402
|
}
|
|
@@ -1726,6 +1756,10 @@ video {
|
|
|
1726
1756
|
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
|
1727
1757
|
}
|
|
1728
1758
|
|
|
1759
|
+
.fm-admin .underline {
|
|
1760
|
+
text-decoration-line: underline;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1729
1763
|
.fm-admin .opacity-0 {
|
|
1730
1764
|
opacity: 0;
|
|
1731
1765
|
}
|
|
@@ -1848,6 +1882,11 @@ video {
|
|
|
1848
1882
|
background-color: rgb(248 250 252 / var(--tw-bg-opacity, 1));
|
|
1849
1883
|
}
|
|
1850
1884
|
|
|
1885
|
+
.fm-admin .hover\:bg-slate-600:hover {
|
|
1886
|
+
--tw-bg-opacity: 1;
|
|
1887
|
+
background-color: rgb(71 85 105 / var(--tw-bg-opacity, 1));
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1851
1890
|
.fm-admin .hover\:bg-slate-700:hover {
|
|
1852
1891
|
--tw-bg-opacity: 1;
|
|
1853
1892
|
background-color: rgb(51 65 85 / var(--tw-bg-opacity, 1));
|
|
@@ -1962,6 +2001,10 @@ video {
|
|
|
1962
2001
|
background-color: rgb(241 245 249 / var(--tw-bg-opacity, 1));
|
|
1963
2002
|
}
|
|
1964
2003
|
|
|
2004
|
+
.fm-admin :is(.group:hover .group-hover\:inline-flex) {
|
|
2005
|
+
display: inline-flex;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
1965
2008
|
.fm-admin :is(.peer:checked ~ .peer-checked\:border-blue-500) {
|
|
1966
2009
|
--tw-border-opacity: 1;
|
|
1967
2010
|
border-color: rgb(59 130 246 / var(--tw-border-opacity, 1));
|