diff --git a/build.rs b/build.rs
index 0035e54..47065f3 100644
--- a/build.rs
+++ b/build.rs
@@ -44,6 +44,14 @@ fn main() {
let gresource_xml = data_dir.join("resources.gresource.xml");
println!("cargo:rerun-if-changed={}", gresource_xml.display());
+ // Watch HTML/CSS so changes trigger a resource rebuild
+ let html_dir = manifest_dir.join("html");
+ if let Ok(entries) = std::fs::read_dir(&html_dir) {
+ for entry in entries.filter_map(|e| e.ok()) {
+ println!("cargo:rerun-if-changed={}", entry.path().display());
+ }
+ }
+
let gresource_out = out_dir.join("feedthemonkey.gresource");
let status = Command::new("glib-compile-resources")
.arg(format!("--sourcedir={}", data_dir.display()))
diff --git a/data/net.jeena.FeedTheMonkey.gschema.xml b/data/net.jeena.FeedTheMonkey.gschema.xml
index e0790c4..16ae75d 100644
--- a/data/net.jeena.FeedTheMonkey.gschema.xml
+++ b/data/net.jeena.FeedTheMonkey.gschema.xml
@@ -21,5 +21,9 @@
1.0
WebView zoom level
+
+ ''
+ Content rewrite rules, one per line: domain from to [from to …]
+
diff --git a/data/resources.gresource.xml b/data/resources.gresource.xml
index e5d363a..ef69e9b 100644
--- a/data/resources.gresource.xml
+++ b/data/resources.gresource.xml
@@ -5,6 +5,7 @@
ui/login_dialog.ui
ui/article_row.ui
ui/shortcuts.ui
+ ui/preferences_dialog.ui
html/content.html
html/content.css
diff --git a/data/ui/article_row.blp b/data/ui/article_row.blp
index 052b813..ea4c08d 100644
--- a/data/ui/article_row.blp
+++ b/data/ui/article_row.blp
@@ -27,6 +27,7 @@ template $ArticleRow : Gtk.Box {
}
Label title_label {
+ hexpand: true;
xalign: 0;
wrap: true;
lines: 2;
@@ -34,6 +35,7 @@ template $ArticleRow : Gtk.Box {
}
Label excerpt_label {
+ hexpand: true;
xalign: 0;
ellipsize: end;
lines: 1;
diff --git a/data/ui/article_row.ui b/data/ui/article_row.ui
index 1218020..e159ff1 100644
--- a/data/ui/article_row.ui
+++ b/data/ui/article_row.ui
@@ -41,6 +41,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
+
+
+ Toggle sidebar
+ F9
+
+
Toggle fullscreen
diff --git a/data/ui/window.blp b/data/ui/window.blp
index 9ded4f7..6c0192d 100644
--- a/data/ui/window.blp
+++ b/data/ui/window.blp
@@ -7,121 +7,140 @@ template $FeedTheMonkeyWindow : Adw.ApplicationWindow {
default-height: 600;
Adw.ToastOverlay toast_overlay {
- Adw.NavigationSplitView split_view {
- sidebar: Adw.NavigationPage {
- title: _("FeedTheMonkey");
+ Paned paned {
+ focusable: false;
+ shrink-start-child: false;
+ resize-start-child: false;
- Adw.ToolbarView {
- [top]
- Adw.HeaderBar {
- [start]
- Stack refresh_stack {
- StackPage {
- name: "button";
- child: Button refresh_button {
- icon-name: "view-refresh-symbolic";
- tooltip-text: _("Refresh");
- action-name: "win.reload";
- };
- }
+ start-child: Adw.ToolbarView sidebar_toolbar {
+ top-bar-style: raised;
- StackPage {
- name: "spinner";
- child: Adw.Spinner {};
- }
+ [top]
+ Adw.HeaderBar {
+ show-start-title-buttons: false;
+ show-end-title-buttons: false;
+
+ title-widget: Box {};
+
+ [start]
+ Stack refresh_stack {
+ StackPage {
+ name: "button";
+ child: Button refresh_button {
+ icon-name: "view-refresh-symbolic";
+ tooltip-text: _("Refresh");
+ action-name: "win.reload";
+ };
}
-
- [end]
- MenuButton menu_button {
- icon-name: "open-menu-symbolic";
- primary: true;
- menu-model: primary_menu;
+ StackPage {
+ name: "spinner";
+ child: Spinner {
+ spinning: true;
+ width-request: 16;
+ height-request: 16;
+ };
}
}
- Stack sidebar_content {
- StackPage {
- name: "placeholder";
- child: Adw.StatusPage {
- icon-name: "rss-symbolic";
- title: _("FeedTheMonkey");
- description: _("Log in to load your articles");
- };
- }
+ [end]
+ MenuButton menu_button {
+ icon-name: "open-menu-symbolic";
+ primary: true;
+ menu-model: primary_menu;
+ }
+ }
- StackPage {
- name: "loading";
- child: Adw.StatusPage {
- paintable: Adw.SpinnerPaintable {};
- title: _("Loading…");
- };
- }
+ Stack sidebar_content {
+ styles ["sidebar-content"]
- StackPage {
- name: "empty";
- child: Adw.StatusPage {
- icon-name: "rss-symbolic";
- title: _("No Unread Articles");
- };
- }
+ StackPage {
+ name: "placeholder";
+ child: Adw.StatusPage {
+ icon-name: "rss-symbolic";
+ title: _("FeedTheMonkey");
+ description: _("Log in to load your articles");
+ };
+ }
- StackPage {
- name: "error";
- child: Adw.StatusPage error_status {
- icon-name: "network-error-symbolic";
- title: _("Could Not Load Articles");
+ StackPage {
+ name: "loading";
+ child: Adw.StatusPage {
+ title: _("Loading…");
+ };
+ }
- Button {
- label: _("Try Again");
- halign: center;
- action-name: "win.reload";
- styles ["pill", "suggested-action"]
- }
- };
- }
+ StackPage {
+ name: "empty";
+ child: Adw.StatusPage {
+ icon-name: "rss-symbolic";
+ title: _("No Unread Articles");
+ };
+ }
- StackPage {
- name: "list";
- child: ScrolledWindow {
- hscrollbar-policy: never;
- ListView article_list_view {
- single-click-activate: true;
- }
- };
- }
+ StackPage {
+ name: "error";
+ child: Adw.StatusPage error_status {
+ icon-name: "network-error-symbolic";
+ title: _("Could Not Load Articles");
+
+ Button {
+ label: _("Try Again");
+ halign: center;
+ action-name: "win.reload";
+ styles ["pill", "suggested-action"]
+ }
+ };
+ }
+
+ StackPage {
+ name: "list";
+ child: ScrolledWindow {
+ hscrollbar-policy: never;
+ ListView article_list_view {
+ single-click-activate: false;
+ show-separators: true;
+ }
+ };
}
}
};
- content: Adw.NavigationPage content_page {
- title: _("FeedTheMonkey");
+ end-child: Adw.ToolbarView {
+ top-bar-style: raised;
- Adw.ToolbarView {
- top-bar-style: raised;
-
- [top]
- Adw.HeaderBar {
- [end]
- MenuButton article_menu_button {
- icon-name: "view-more-symbolic";
- menu-model: article_menu;
- visible: false;
- }
+ [top]
+ Adw.HeaderBar {
+ [start]
+ Button toggle_sidebar_button {
+ icon-name: "sidebar-show-symbolic";
+ tooltip-text: _("Toggle Sidebar");
+ action-name: "win.toggle-sidebar";
}
- Stack content_stack {
- StackPage {
- name: "empty";
- child: Adw.StatusPage {
- icon-name: "document-open-symbolic";
- title: _("No Article Selected");
- };
- }
+ title-widget: Adw.WindowTitle {
+ title: _("FeedTheMonkey");
+ };
- StackPage {
- name: "webview";
- child: WebKit.WebView web_view {};
- }
+ [end]
+ MenuButton article_menu_button {
+ icon-name: "view-more-symbolic";
+ menu-model: article_menu;
+ visible: false;
+ }
+ }
+
+ Stack content_stack {
+ StackPage {
+ name: "empty";
+ child: Adw.StatusPage {
+ icon-name: "document-open-symbolic";
+ title: _("No Article Selected");
+ };
+ }
+
+ StackPage {
+ name: "webview";
+ child: WebKit.WebView web_view {};
}
}
};
@@ -137,6 +156,13 @@ menu primary_menu {
}
}
+ section {
+ item {
+ label: _("Preferences");
+ action: "win.preferences";
+ }
+ }
+
section {
item {
label: _("Keyboard Shortcuts");
diff --git a/data/ui/window.ui b/data/ui/window.ui
index 5ee9da1..e9cd9cc 100644
--- a/data/ui/window.ui
+++ b/data/ui/window.ui
@@ -12,166 +12,182 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
-
-
-
- FeedTheMonkey
-
-
-
-
+
+
+
+
+ loading
+
+
+ Loading…
-
-
-
- empty
-
-
- rss-symbolic
- No Unread Articles
-
-
+
+
+
+
+
+ empty
+
+
+ rss-symbolic
+ No Unread Articles
-
-
-
- error
-
-
- network-error-symbolic
- Could Not Load Articles
-
-
- Try Again
- 3
- win.reload
-
-
-
+
+
+
+
+
+ error
+
+
+ network-error-symbolic
+ Could Not Load Articles
+
+
+ Try Again
+ 3
+ win.reload
+
-
+
-
-
-
- list
-
-
- 2
-
-
- true
-
-
+
+
+
+
+
+ list
+
+
+ 2
+
+
+ false
+ true
-
+
-
+
-
-
- FeedTheMonkey
+
+
+ 1
+
+
+
-
- 1
-
-
-
-
-
- empty
-
-
- document-open-symbolic
- No Article Selected
-
-
-
-
-
-
- webview
-
-
-
-
-
+
+ webview
+
+
+
@@ -190,6 +206,12 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
win.logout
+
+ -
+ Preferences
+ win.preferences
+
+
-
Keyboard Shortcuts
diff --git a/html/content.css b/html/content.css
index c3f979b..4fc396b 100644
--- a/html/content.css
+++ b/html/content.css
@@ -1,101 +1,131 @@
-* {
- box-sizing: border-box;
+/* CSS custom properties are set from Rust via AdwStyleManager.
+ The :root defaults below act as a light-mode fallback only. */
+
+:root {
+ --bg: #ffffff;
+ --fg: #1a1a1a;
+ --fg-dim: rgba(0,0,0,0.55);
+ --border: rgba(0,0,0,0.12);
+ --header-bg: #f6f5f4;
+ --link: #1c71d8;
+ --code-bg: rgba(0,0,0,0.06);
+ --blockquote-border: rgba(0,0,0,0.2);
+ --font: sans-serif;
+ --font-size: 15px;
}
-body {
- font-family: sans-serif;
+:root[data-dark="1"] {
+ --bg: #1e1e1e;
+ --fg: rgba(255,255,255,0.87);
+ --fg-dim: rgba(255,255,255,0.5);
+ --border: rgba(255,255,255,0.12);
+ --header-bg: #242424;
+ --link: #78aeed;
+ --code-bg: rgba(255,255,255,0.06);
+ --blockquote-border: rgba(255,255,255,0.2);
+}
+
+* { box-sizing: border-box; }
+
+html, body {
margin: 0;
padding: 0;
- line-height: 1.6;
- color: #222;
- background: #fff;
+ background: var(--bg);
+ color: var(--fg);
+ font-family: var(--font);
+ font-size: var(--font-size);
+ word-wrap: break-word;
}
-@media (prefers-color-scheme: dark) {
- body {
- color: #ddd;
- background: #1e1e1e;
- }
- a {
- color: #78aeed;
- }
- img {
- opacity: 0.85;
- }
+a {
+ color: var(--link);
+ text-decoration: none;
}
-#header {
+article a {
+ text-decoration: underline;
+}
+
+header {
padding: 1.5em 2em 1em;
- border-bottom: 1px solid rgba(0,0,0,0.1);
- margin-bottom: 1em;
+ background: var(--header-bg);
+ border-bottom: 1px solid var(--border);
}
-@media (prefers-color-scheme: dark) {
- #header {
- border-bottom-color: rgba(255,255,255,0.1);
- }
+header > .inner,
+article {
+ max-width: 720px;
+ margin-left: auto;
+ margin-right: auto;
}
-#feed-title {
- font-size: 0.8em;
- opacity: 0.6;
- margin-bottom: 0.25em;
- text-transform: uppercase;
- letter-spacing: 0.05em;
+header > .inner {
+ padding: 0 2em;
}
-#title {
- font-size: 1.5em;
- margin: 0 0 0.5em;
+header h1 {
+ font-size: 1.3em;
+ margin: 0.2em 0 0.4em;
+ padding: 0;
line-height: 1.3;
}
-#meta {
- font-size: 0.85em;
- opacity: 0.6;
- margin-bottom: 0.5em;
+header h1 a {
+ color: var(--fg);
}
-#meta span + span::before {
- content: ' · ';
-}
-
-#link {
+header p {
+ color: var(--fg-dim);
+ margin: 0;
+ padding: 0;
font-size: 0.85em;
}
-#content {
- padding: 0 2em 2em;
- max-width: 800px;
+article {
+ line-height: 1.6;
+ padding: 1.5em 2em 2em;
}
-#content img {
+img {
max-width: 100%;
height: auto;
}
-#content pre {
- overflow-x: auto;
- background: rgba(0,0,0,0.05);
+div > a:only-child img,
+figure > a:only-child img,
+p > a:only-child img,
+figure > img:only-child,
+div > img:only-child,
+p > img:only-child {
+ display: block;
+ margin: 1em auto;
+ float: none !important;
+}
+
+pre {
+ overflow: auto;
+ background: var(--code-bg);
padding: 1em;
- border-radius: 4px;
+ border-radius: 6px;
+ font-size: 0.9em;
}
-@media (prefers-color-scheme: dark) {
- #content pre {
- background: rgba(255,255,255,0.05);
- }
+code {
+ background: var(--code-bg);
+ padding: 0.15em 0.35em;
+ border-radius: 3px;
+ font-size: 0.9em;
}
-#content blockquote {
- border-left: 3px solid rgba(0,0,0,0.2);
+pre code {
+ background: none;
+ padding: 0;
+}
+
+blockquote {
+ border-left: 3px solid var(--blockquote-border);
margin-left: 0;
padding-left: 1em;
- opacity: 0.8;
-}
-
-@media (prefers-color-scheme: dark) {
- #content blockquote {
- border-left-color: rgba(255,255,255,0.2);
- }
+ color: var(--fg-dim);
+ font-style: italic;
}
diff --git a/html/content.html b/html/content.html
index f817038..82610c9 100644
--- a/html/content.html
+++ b/html/content.html
@@ -2,35 +2,47 @@
+
FeedTheMonkey
-
+
-