Skip to content

[#515] Add top websites and window titles#522

Open
grigor-dochev wants to merge 4 commits into
devfrom
feature/515-top-window-title
Open

[#515] Add top websites and window titles#522
grigor-dochev wants to merge 4 commits into
devfrom
feature/515-top-window-title

Conversation

@grigor-dochev
Copy link
Copy Markdown
Collaborator

Add Top Websites and Window Titles to Retrospection (Closes #515)

Description

Adds two new Retrospection detail cards that summarize where participants spent the most time during the selected day: top websites and top window titles. The cards use compact horizontal bars, durations, and relative percentages to make the details easy to scan alongside the existing Retrospection timeline and insights.

Changes Made

  • New Top websites card showing the top 3 website domains for the selected day
  • New Top window titles card showing the top 3 non-browsing window titles for the selected day
  • Website entries are derived from WorkRelatedBrowsing activity and grouped by domain, without showing the browser app name
  • Window title entries exclude browsing categories to avoid duplicating website activity
  • Other, Unknown, and Idle entries are filtered out before selecting the top 3
  • Relative percentage shown next to each duration, calculated within each card
  • Card accent colors reuse the existing Retrospection activity color mapping used by the timeline legend
  • Added IPC handlers and typed renderer commands for loading top websites and top window titles
  • Updated Retrospection session aggregation to support derived grouping keys such as website domains and cleaned window titles
  • Removed noisy service warnings for intentionally skipped rows during derived aggregation

Implementation Details

  1. Retrospection session reconstruction now supports grouping by a derived key, not only direct fields such as processName or activity. This allows website domains to be extracted from URLs and window titles to be cleaned before aggregation.
  2. getTopWebsiteSessions only considers WorkRelatedBrowsing entries, extracts the hostname from each URL, groups active time by domain, filters irrelevant values, sorts by total duration, and returns the top 3.
  3. getTopWindowTitleSessions excludes browsing categories (WorkRelatedBrowsing, WorkUnrelatedBrowsing, and SocialMedia), strips matching app suffixes from window titles, groups active time by cleaned title, filters irrelevant values, sorts by total duration, and returns the top 3.
  4. Each returned top item carries its source activity so the frontend can use the same category color mapping as the timeline.
  5. Rows with no derived key are treated as expected separators during aggregation instead of creating log warnings.

Screenshots

Light mode Dark mode
Retrospection top websites and window titles in light mode Retrospection top websites and window titles in dark mode

Closes #515

Copy link
Copy Markdown
Member

@casaout casaout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great start, thank you!

I now mostly reviewed UI; and will review the code once we've settled on the design


<!-- Website and window title details -->
<div v-if="topItemsAvailable">
<div class="mb-2 mt-8">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we'll need another title

also in terms of what is displayed, it's very similar to "top apps" and "top activities pursued" anyways

this will also help to reduce the window size

v-if="topWebsites.length"
class="top-item-card rounded border border-gray-200 bg-gray-100 px-4 py-3 text-gray-800 dark:border-transparent dark:bg-neutral-800 dark:text-slate-200"
>
<h2 class="primary-blue font-bold leading-4">Top websites</h2>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that I don't see the "top website" box (tested on multiple days), even though "Microsoft Edge use" and "Work Related Browsing" was quite big overall

Image

(so i couldn't test that feature yet)

renderCompactTime(website.totalDurationMs)
}}</span>
<span class="top-item-percentage">{{
getTopItemPercentage(website, topWebsites)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should try to align the design slightly more to the existing design; few suggestions:

  1. the numbering can be simplified (no rounded border); or maybe even removed as it'll be intuitive which one is the highest one?
  2. the fonts should be the same as for the other insight boxes
  3. in the dark mode, i think the light background of the chart is a bit too strong
  4. i think we don't need to display the percentages (as the timeline chart will imply this anyways), and to have more space for the window title)
  5. it can be made a bit more compact, to reduce the overall height of the insights

Great that you're using the activity colors; that's awesome!!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2026-05-18 at 14 19 55

Copy link
Copy Markdown
Member

@casaout casaout May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It already looks better, thank you. However, to me still looks a bit like it's in light mode (with the bright colors)...

  • I wonder if it'd work better if the barchart was the actual category color
  • the font was white (in dark mode) and dark (as is in light mode)? (then it'd look much more similar to the activities timeline (which also helps to clarify the colors)
  • And the "white" part could be white in light mode; and a very dark shade (i.e. the background color); so the white font would easily be readable

Also, can we vertically make it slightly more compact, such as by removing some of the top/bottom padding between the text and the barchart?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It already looks better, thank you. However, to me still looks a bit like it's in light mode (with the bright colors)...

  • I wonder if it'd work better if the barchart was the actual category color
  • the font was white (in dark mode) and dark (as is in light mode)? (then it'd look much more similar to the activities timeline (which also helps to clarify the colors)
  • And the "white" part could be white in light mode; and a very dark shade (i.e. the background color); so the white font would easily be readable

Also, can we vertically make it slightly more compact, such as by removing some of the top/bottom padding between the text and the barchart?

Screenshot 2026-05-19 at 11 31 37

What do you think of this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's great, thanks! Do we need the broders at all? and how dos it look in light mode? thanks!

<span class="top-item-time">{{
renderCompactTime(website.totalDurationMs)
}}</span>
<span class="top-item-percentage">{{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we finalize on the design of these "charts", i think we can uniformly use the same design (and CSS) for all 4 boxes: top apps/activities & websites

return await getWindowActivitySessionsByKey((activity) => activity[prop], date);
}

function getDomainFromUrl(url: string | null): string | null {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guiding from your screenshot, i think just having the domain is not sufficient to know what a person was doing; e.g. on github it could be specification, code review, browsing, etc

is it possible to show slightly more (i.e. the core part of the displayed window title)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the updates, however,w ithout in-code documentation, it is very difficult to understand what exactly is done.

I suggest to make an example and describe the supsteps

}

if (processName) {
const suffixes = [` - ${processName}`, ` — ${processName}`, ` | ${processName}`];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think one more cleaning step should be in case a path is part of the window title; see example:

Image


if (processName) {
const suffixes = [` - ${processName}`, ` — ${processName}`, ` | ${processName}`];
const matchingSuffix = suffixes.find((suffix) => title.endsWith(suffix));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When matching an URL to an activity (not website); e.g. "Overleaf" to "read/write doucment", i think the window/browser cleaning doesn't fully work yet. it's great that it shows the website name first; but on hover one sees for example "4 or more pages"

Image

Copy link
Copy Markdown
Member

@casaout casaout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the updates! To understand the "algorithm" for loading the actual datasets, extracting window titles, I think it'd be beneficial to add more documentation and rationale. otherwise, it's difficult to review the code, and also later maintain it. Thanks!

</div>
</div>

<div v-if="topItemsAvailable" class="tile-grid mt-8">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing between the old rows and new rows is off (see red arrows):

Image

(please make it lower for the new row, same as for the previous ones)


type WindowActivitySessionKeySelector = (activity: WindowActivityEntity) => string | null;

const BROWSER_PROCESS_NAMES = [
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do we need this list for? (please add a quick comment to remember this later)

also, i wonder if we could reuse the list from https://github.com/HASEL-UZH/PA.WindowsActivityTracker/blob/main/typescript/src/mappings/browsers.ts and then compare the process name instead of app name? (and also extend it with the list from there)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracker doesn't export the list a sa runtime import and it's being used to recognise the browser process names for title cleanup. So, for now, I've mirred the tracker browser mapping instead.

map.set(key, entry);
}

function addSessionWithActiveMinuteSplits(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you briefly explain (and document) the goal and logic of this method?

async function getWindowActivitySessionsByType(prop: "processName" | "activity", date: Date): Promise<ActivitySessions[]> {
const windowActivityToday = await getWindowActivities(date)
const activeMinutesSet = await getActiveMinutesSet(date)
async function getWindowActivitySessionsByKey(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please explain this method a bit; and also what changed from the existing one?

what is a sessionkey?

return await getWindowActivitySessionsByKey((activity) => activity[prop], date);
}

function getDomainFromUrl(url: string | null): string | null {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the updates, however,w ithout in-code documentation, it is very difficult to understand what exactly is done.

I suggest to make an example and describe the supsteps

Add in-line comments and doc strings to RetrospectionService
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retrospection: Create Top Website/Window title

2 participants