-
Yes — a favicon can show a badge, but only on a tab that is already open.
Favicon vs extension icon
A browser-extension toolbar icon can be badged even when no OneDev tab exists. A favicon belongs to a loaded page, so if the issue or pull request is not open in any tab, there is nothing to paint. Closing the tab also drops the badge.
What is possible
For a background tab that already has the issue or pull request loaded, page JavaScript can:
- Draw the current site icon onto a canvas
- Paint a small circle in the bottom-right corner
- Replace
<link rel="icon">with the canvas image - Restore the original icon when the tab becomes visible (
document.hidden/visibilitychange)
Chrome and Firefox generally update the tab icon this way. Safari (especially on iOS) has historically been unreliable with dynamically replaced favicons. A more portable extra cue is a title prefix such as a bullet, which several web apps use alongside or instead of a favicon badge.
OneDev already serves the icon from the same origin (
/~img/logo.png, or/logo.pngwhen a custom branding logo is set), so canvas drawing is not blocked by CORS. Issue and pull request pages also already receive live WebSocket updates; new unread activity is marked with classnew(the list column is.new-indicator). That is enough signal to badge a background tab.This is not implemented today. It would be a UI enhancement, not current behavior.
-
I am in favor of this approach.
- Draw the current site icon onto a canvas
- Paint a small circle in the bottom-right corner
- Replace
<link rel="icon">with the canvas image - Restore the original icon when the tab becomes visible (
document.hidden/visibilitychange)
If we implement this, would it result in a conflict on Safari?
For browsers that are not compatible with this feature, could we use a title prefix, such as a bullet? But not both, only one or the other.
Don't assume. Ask high-level clarifying questions if needed.
- Numbered questions (1, 2, 3, etc)
- Lettered answer options (A, B, C, etc)
- Include a markdown table that contains the following for each question:
- Pros and Cons?
- Your recommendation (including justification)?
Once any questions have been answered, please create a new issue.
-
Safari does not conflict with the canvas badge in the sense of showing a broken or competing icon. Apple’s policy is that favicons should not be scripted, so Safari (including iOS / WebKit) typically keeps the original OneDev logo and ignores the
<link rel="icon">swap. Chrome and Firefox tabs are unaffected.The only practical risk is if we still rewrite the icon URL on Safari anyway. Safari caches favicons aggressively, so a data-URL or a failed canvas export can leave a blank or sticky icon. The safe rule is: do not touch
<link rel="icon">on Safari / WebKit; use a title prefix there instead.Yes — for browsers that cannot show the badge, a title prefix such as a bullet is the usual fallback. Using only one cue per browser is the right call: favicon badge where it works, title prefix everywhere else, never both on the same tab.
OneDev already writes
document.titleduring in-page history navigation (onedev.server.history.pushState/replaceStateinbase.js). A prefix would need to go through that path so AJAX navigation does not drop or double it.This remains an enhancement, not current behavior. After the questions below are answered, I will open a separate implementation issue.
- How should we decide which cue a browser gets?
- A. Engine rule: Chromium and Firefox get the favicon badge; Safari / WebKit get a title prefix
- B. Try the favicon badge first; fall back to a title prefix if we cannot confirm it applied
- C. Title prefix in every browser (no favicon change)
Option Pros Cons Recommendation A Predictable; never mutates the icon on Safari / iOS WebKit Relies on engine detection; a future Safari change would need a follow-up Recommended. Safari’s restriction is intentional and stable. An engine rule avoids blank/sticky icons without a fragile runtime check. B Would pick up a Safari that later allows scripted icons Whether a favicon swap “took” is hard to detect; a failed swap can still leave a bad icon Not recommended. C Simplest; identical everywhere Drops the favicon badge on Chrome/Firefox Only if we want zero favicon risk. - Which title prefix should unsupported browsers use?
- A. A bullet:
• Issue title - B. A filled circle:
● Issue title - C. A short word:
(new) Issue title
Option Pros Cons Recommendation A Common in web apps; compact; easy to strip Easy to miss next to a long title Recommended. Matches the “small indicator” idea without clutter. B Slightly more visible Heavier; can look like a status glyph Fine if we want more contrast. C Unambiguous Longer; noisier in the tab strip Use only if a glyph is too subtle. - Where should the indicator appear?
- A. Issue and pull request detail pages only (the tab for that issue or PR)
- B. Detail pages plus list pages when a row has
.new-indicator - C. Any page that already shows unread activity with class
new
Option Pros Cons Recommendation A Matches the original request; one tab maps to one issue/PR Lists stay unchanged Recommended. Background-tab badges make the most sense on the entity you already opened. B Lists also call you back A list tab would badge for any new row, which is a different product meaning Defer unless we want list-wide “something is new”. C Broadest coverage Easy to over-notify (dashboards, comment lists) Too wide for a first cut. -
- a
- b
- b. But only when there has been an updated new indicator. For example, within issues and pull requests, refreshing the page would remove the new indicator. In this scenario, we would always want the indicator to appear on the wave icon. On pages where there are lists, such as the issues page or the pull request page, refreshing the page shows a new indicator, which means something different. It means unread, and refreshing the page does not change that unread status. Therefore, we should only have a new indicator on the fav icon if there is a fresh new indicator in addition to what was already there whilst the page was already loaded. I hope that makes sense.
Don't assume. Ask high-level clarifying questions if needed.
- Numbered questions (1, 2, 3, etc)
- Lettered answer options (A, B, C, etc)
- Include a markdown table that contains the following for each question:
- Pros and Cons?
- Your recommendation (including justification)?
-
Thanks — that distinction is clear.
Recorded answers
- Chromium and Firefox get the favicon badge; Safari / WebKit get a title prefix
- Title prefix is a filled circle:
● … - Detail pages and list pages, with different triggers:
- Issue/PR detail: any in-page
.newactivity can drive the cue (refresh clears those markers) - Issue/PR lists: ignore unread rows that were already there at load; only a fresh unread marker that appears while the page is already loaded should drive the cue
- Issue/PR detail: any in-page
A few decisions are still open before I file the implementation issue.
- After you look at a tab and the cue is cleared, when may it appear again?
- A. Only if another fresh update arrives after you last focused the tab (re-baseline on focus)
- B. As soon as you switch away again, if any
.newmarkers are still on the page - C. Never again until the page is reloaded
Option Pros Cons Recommendation A Matches “remove when the tab is opened”; list pages will not keep nagging about unread you already glanced at A detail-page .newstripe stays in the DOM until refresh, so leaving the tab again will not re-badge unless a newer activity arrivesRecommended. Same rule for detail and lists: the cue means “something happened since you last looked at this tab.” B Detail pages would keep reminding you until refresh List pages would re-badge for unread that was already visible Conflicts with the list-page rule you described C Simplest A second comment while you are still on the same loaded page would be silent after the first look Too weak for a background tab you keep open - On list pages, what counts as a “fresh” unread marker?
- A. A row that was not unread at last baseline becomes unread
- B. A, plus further activity on a row that was already unread
- C. Only a newly created issue or pull request showing up in the list (not updates to existing rows)
Option Pros Cons Recommendation A Matches “in addition to what was already there”; existing unread rows do not retrigger You will not be called back for another comment on an issue that was already unread in that list Recommended. The extra marker is a new unread row, not more activity on an already-unread row. B More complete for “something changed in this list” Noisy; the row was already flagged unread Easy to over-notify C Narrow Misses the common live case: an existing listed item gets activity while the tab is in the background Too narrow - Which list pages should use that list rule?
- A. All issue lists and all pull request lists (project and global / “my” lists)
- B. A, plus code comment lists (they use the same unread column)
- C. Only the project issues page and the project pull requests page
Option Pros Cons Recommendation A Same unread column and the same “refresh still shows unread” meaning everywhere those lists appear Slightly more surface than the two pages you named Recommended. The meaning of the marker is the same on every issue/PR list. B Consistent for every .new-indicatortableCode comments were not part of the original request Defer C Smallest scope Global / “my” lists would behave differently from project lists Inconsistent After these are answered I will open a separate implementation issue.
-
- a
- a. Option A already makes option B redundant. If that is not the case, then please elaborate as to why you are suggesting option B as a feasible option
- b
-
@zaakiy Option A does not make option B redundant. They watch different signals.
On list pages the unread stripe is a boolean class (
tr.issue.new,tr.request.new,tr.comment.new). It is on when you have not visited sincelastActivity, and it stays on until you open that item. Another comment on an issue that was already unread does not add a second stripe; the row was alreadynew.Concrete case: the issues list is open in a background tab, issue 10 is already unread at load (or at last focus), and someone comments on issue 10 while that list is still loaded.
Option What happens A No favicon/title cue. Issue 10 was already unread, so this is not a new unread row. B Cue appears, because issue 10’s last activity changed even though it was already unread. A is “a row became unread that was not unread at baseline.” B is A plus “an already-unread row got further activity.” B is feasible, but it needs a second signal (last-activity time, or the row being live-updated), not the CSS class alone. That is why it was listed separately.
Recorded so far
-
Re-baseline on focus: the cue appears only if another fresh update arrives after you last focused the tab
-
Pending confirmation below
-
Issue lists, pull request lists, and code comment lists (same “fresh unread row” rule). Code comment detail pages stay out of scope unless you say otherwise.
-
Given that distinction, which list-page rule do you want?
- A. Only a row that was not unread at last baseline becoming unread (no cue for more activity on an already-unread row)
- B. A, plus further activity on a row that was already unread
Option Pros Cons Recommendation A Matches “in addition to what was already there”; existing unread rows do not retrigger You will not be called back for another comment on an item that was already unread in that list Recommended. Same meaning you described for refresh vs live: the extra marker is a new unread row. B Calls you back when an already-unread item gets more activity Noisier; that row was already flagged Use only if a list tab should mean “anything in this list moved,” not “a new unread appeared.” After this is confirmed I will open a separate implementation issue.
-
-
- A. Only a row that was not unread at last baseline becoming unread (no cue for more activity on an already-unread row)
All decisions locked in. Please create a new issue. Please ensure that the new issue has all required context and motivation so that the AI agent or user does not need to look at this issue
-
Closing as answered; implementation tracked in issue #3009.
-
@zaakiy Created implementation issue #3009 with the agreed design:
- Chromium and Firefox get the favicon badge; Safari / WebKit get a
●title prefix (never both) - Re-baseline on tab focus: the cue appears only if another fresh update arrives after you last looked at the tab
- Issue and pull request detail pages: in-page new activity
- Issue, pull request, and code comment lists: only a row that was not unread at last baseline becoming unread
Closing this Question as answered.
- Chromium and Firefox get the favicon badge; Safari / WebKit get a
-
Previous Value Current Value Open
Closed
| Type |
Question
|
| Priority |
Normal
|
| Assignee | |
| Labels |
No labels
|
When an issue or pull request has a new update (css
.new-indicator), And the user currently does not have the tab open, consider painting a small circlular indicator on the bottom right of the favicon.Then remove the indicator when the user opens the tab.
I know this can be done with extension icons, not sure about favicons.