From 53ddea638421601ec180b191ef5e5b460796955d Mon Sep 17 00:00:00 2001 From: Mauro Agent Date: Mon, 26 Oct 2026 09:42:07 +0100 Subject: [PATCH] feat(ui): serve an apple-touch-icon so iOS shows the logo on the home screen (#12917) iOS ignores the SVG favicon and draws the first letter of the page title when a page is saved to the home screen. Serve a 180x180 PNG made from logo.png at /apple-touch-icon.png, link it from the React UI head, and rewrite the link under a reverse-proxy prefix like the favicon link. The logo is flattened on white because iOS fills transparency with black. Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Mauro Morales Co-authored-by: Mauro Morales --- core/http/app.go | 11 +++++++++++ core/http/app_test.go | 1 + core/http/react-ui/index.html | 1 + core/http/static/apple-touch-icon.png | Bin 0 -> 26875 bytes 4 files changed, 13 insertions(+) create mode 100644 core/http/static/apple-touch-icon.png diff --git a/core/http/app.go b/core/http/app.go index 319a400ad..2dd9fe55e 100644 --- a/core/http/app.go +++ b/core/http/app.go @@ -389,6 +389,16 @@ func API(application *application.Application) (*echo.Echo, error) { return c.Blob(http.StatusOK, "image/svg+xml", data) }) + // iOS draws the home-screen icon from an apple-touch-icon PNG and ignores + // the SVG favicon, so it falls back to the first letter of the page title. + e.GET("/apple-touch-icon.png", func(c echo.Context) error { + data, err := embedDirStatic.ReadFile("static/apple-touch-icon.png") + if err != nil { + return c.NoContent(http.StatusNotFound) + } + return c.Blob(http.StatusOK, "image/png", data) + }) + // Static files - use fs.Sub to create a filesystem rooted at "static" staticFS, err := fs.Sub(embedDirStatic, "static") if err != nil { @@ -671,6 +681,7 @@ func API(application *application.Application) (*echo.Echo, error) { html := string(indexHTML) html = strings.ReplaceAll(html, `="/assets/`, `="`+safePrefix+`assets/`) html = strings.ReplaceAll(html, `="/favicon.svg"`, `="`+safePrefix+`favicon.svg"`) + html = strings.ReplaceAll(html, `="/apple-touch-icon.png"`, `="`+safePrefix+`apple-touch-icon.png"`) indexHTML = []byte(html) } return c.HTMLBlob(http.StatusOK, indexHTML) diff --git a/core/http/app_test.go b/core/http/app_test.go index 36072b96e..e3080161f 100644 --- a/core/http/app_test.go +++ b/core/http/app_test.go @@ -469,6 +469,7 @@ var _ = Describe("API test", func() { Expect(string(body)).To(ContainSubstring(``), "body") Expect(string(body)).ToNot(ContainSubstring(`="/assets/`), "asset URLs must include the prefix") Expect(string(body)).ToNot(ContainSubstring(`="/favicon.svg"`), "favicon URL must include the prefix") + Expect(string(body)).ToNot(ContainSubstring(`="/apple-touch-icon.png"`), "touch icon URL must include the prefix") }) // X-Forwarded-Prefix is attacker controllable on misconfigured diff --git a/core/http/react-ui/index.html b/core/http/react-ui/index.html index 080dd9124..c1ad282ba 100644 --- a/core/http/react-ui/index.html +++ b/core/http/react-ui/index.html @@ -5,6 +5,7 @@ LocalAI +