diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 0247c54..0000000
--- a/android/app/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index 5fb7b02..0000000
--- a/android/app/src/main/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index bca081d..31092ba 100644
--- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,7 +1,9 @@
+ density-bucket PNG fallbacks are needed. The foreground is the full circular
+ app icon (ic_launcher_foreground.png); the launcher mask crops the disc to
+ its shape, and the solid gruvbox background fills the transparent corners. -->
-
-
+
+
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 6b78462..af8535f 100644
--- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..e9c95b1
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/desktop/CMakeLists.txt b/desktop/CMakeLists.txt
index cb3bf7a..c78249e 100644
--- a/desktop/CMakeLists.txt
+++ b/desktop/CMakeLists.txt
@@ -4,6 +4,7 @@ project(photogallery_uploader LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets Network Concurrent)
find_package(Qt6Keychain REQUIRED)
@@ -15,6 +16,7 @@ add_executable(uploader
settings.cpp
uploadclient.cpp
imageprep.cpp
+ app.qrc
)
target_link_libraries(uploader PRIVATE
@@ -23,3 +25,14 @@ target_link_libraries(uploader PRIVATE
Qt6::Concurrent
Qt6Keychain::Qt6Keychain
)
+
+# Installation — the binary, a desktop entry, and the app icon in the hicolor
+# theme, so `nix profile install ./desktop` puts "Photog" in the app menu.
+include(GNUInstallDirs)
+install(TARGETS uploader RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES photog.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
+foreach(sz 32 128 256 512)
+ install(FILES icons/app-${sz}.png
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${sz}x${sz}/apps
+ RENAME photog.png)
+endforeach()
diff --git a/desktop/app.qrc b/desktop/app.qrc
new file mode 100644
index 0000000..ea4e298
--- /dev/null
+++ b/desktop/app.qrc
@@ -0,0 +1,9 @@
+
+
+
+ icons/app-32.png
+ icons/app-128.png
+ icons/app-256.png
+ icons/app-512.png
+
+
diff --git a/desktop/flake.nix b/desktop/flake.nix
index 2a27e5f..7e9b1f3 100644
--- a/desktop/flake.nix
+++ b/desktop/flake.nix
@@ -17,6 +17,28 @@
pkgs.qt6.qtimageformats
];
in {
+ # Installable build: `nix profile install ./desktop` drops "Photog" into
+ # the application menu with its icon. wrapQtAppsHook wraps the binary so
+ # it finds the Qt platform (xcb + wayland) and image-format plugins on
+ # its own, without the dev shell.
+ packages.default = pkgs.stdenv.mkDerivation {
+ pname = "photog";
+ version = "0.1.0";
+ src = ./.;
+ nativeBuildInputs = with pkgs; [
+ cmake
+ ninja
+ pkg-config
+ qt6.wrapQtAppsHook
+ ];
+ buildInputs = with pkgs; [
+ qt6.qtbase
+ qt6.qtwayland
+ qt6.qtimageformats
+ qt6Packages.qtkeychain
+ ];
+ };
+
devShells.default = pkgs.mkShell {
packages = with pkgs; [
cmake
diff --git a/desktop/icons/app-128.png b/desktop/icons/app-128.png
new file mode 100644
index 0000000..35493a8
Binary files /dev/null and b/desktop/icons/app-128.png differ
diff --git a/desktop/icons/app-256.png b/desktop/icons/app-256.png
new file mode 100644
index 0000000..6d0c67c
Binary files /dev/null and b/desktop/icons/app-256.png differ
diff --git a/desktop/icons/app-32.png b/desktop/icons/app-32.png
new file mode 100644
index 0000000..1de4612
Binary files /dev/null and b/desktop/icons/app-32.png differ
diff --git a/desktop/icons/app-512.png b/desktop/icons/app-512.png
new file mode 100644
index 0000000..8fd0b36
Binary files /dev/null and b/desktop/icons/app-512.png differ
diff --git a/desktop/main.cpp b/desktop/main.cpp
index dd2280c..cdce43b 100644
--- a/desktop/main.cpp
+++ b/desktop/main.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include "mainwindow.h"
@@ -8,6 +9,15 @@ int main(int argc, char **argv)
app.setApplicationName(QStringLiteral("photogallery-uploader"));
app.setOrganizationName(QStringLiteral("inflo.ws"));
+ // Window / taskbar / alt-tab icon, embedded via app.qrc. Several sizes so
+ // the small titlebar rendering stays crisp rather than downscaling 512px.
+ QIcon icon;
+ icon.addFile(QStringLiteral(":/icons/app-32.png"));
+ icon.addFile(QStringLiteral(":/icons/app-128.png"));
+ icon.addFile(QStringLiteral(":/icons/app-256.png"));
+ icon.addFile(QStringLiteral(":/icons/app-512.png"));
+ app.setWindowIcon(icon);
+
MainWindow window;
window.resize(440, 680);
window.show();
diff --git a/desktop/photog.desktop b/desktop/photog.desktop
new file mode 100644
index 0000000..4c61052
--- /dev/null
+++ b/desktop/photog.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Type=Application
+Name=Photog
+GenericName=Photo Uploader
+Comment=Upload photos to your gallery
+Exec=uploader
+Icon=photog
+Terminal=false
+Categories=Graphics;Photography;Network;
diff --git a/generator.go b/generator.go
index ceb3c09..57db661 100644
--- a/generator.go
+++ b/generator.go
@@ -713,6 +713,14 @@ func (g *Generator) copyStatic() error {
if err := os.WriteFile(dst, data, 0644); err != nil {
return err
}
+
+ // Also serve the favicon from the site root, where browsers and
+ // crawlers request /favicon.ico by default without a tag.
+ if e.Name() == "favicon.ico" {
+ if err := os.WriteFile(filepath.Join(g.outputDir, e.Name()), data, 0644); err != nil {
+ return err
+ }
+ }
}
return nil
}
diff --git a/static/favicon.ico b/static/favicon.ico
new file mode 100644
index 0000000..1ae8fe5
Binary files /dev/null and b/static/favicon.ico differ
diff --git a/static/web-16.png b/static/web-16.png
new file mode 100644
index 0000000..62e4270
Binary files /dev/null and b/static/web-16.png differ
diff --git a/static/web-192.png b/static/web-192.png
new file mode 100644
index 0000000..d94b94e
Binary files /dev/null and b/static/web-192.png differ
diff --git a/static/web-32.png b/static/web-32.png
new file mode 100644
index 0000000..307e1ba
Binary files /dev/null and b/static/web-32.png differ
diff --git a/static/web-48.png b/static/web-48.png
new file mode 100644
index 0000000..589fe37
Binary files /dev/null and b/static/web-48.png differ
diff --git a/static/web-512.png b/static/web-512.png
new file mode 100644
index 0000000..bfb911b
Binary files /dev/null and b/static/web-512.png differ
diff --git a/templates/day.html b/templates/day.html
index fe9dfa5..1089d06 100644
--- a/templates/day.html
+++ b/templates/day.html
@@ -6,6 +6,9 @@
{{.Date}} · Photo Gallery
+
+
+
diff --git a/templates/grid.html b/templates/grid.html
index a081a40..5cba5e8 100644
--- a/templates/grid.html
+++ b/templates/grid.html
@@ -6,6 +6,9 @@
Photo Gallery
+
+
+
diff --git a/templates/photo.html b/templates/photo.html
index 55dde89..f6b9d87 100644
--- a/templates/photo.html
+++ b/templates/photo.html
@@ -25,6 +25,9 @@
+
+
+
{{if .Newer}}
diff --git a/templates/timeline.html b/templates/timeline.html
index 04559f4..89f9242 100644
--- a/templates/timeline.html
+++ b/templates/timeline.html
@@ -6,6 +6,9 @@
Photo Gallery
+
+
+
diff --git a/templates/upload.html b/templates/upload.html
index 54dc5ab..c1e1c05 100644
--- a/templates/upload.html
+++ b/templates/upload.html
@@ -6,6 +6,9 @@
Upload — Photo Gallery
+
+
+