Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 49 additions & 17 deletions docs/widgets/(Widget)-Github.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
| `tooltip` | boolean | `true` | Whether to show the tooltip on hover. |
| `update_interval` | integer | `600` | The interval in seconds to update the notifications. Must be between 60 and 3600. |
| `token` | string | `""` | The GitHub personal access token. |
| `max_notification` | integer | `20` | The maximum number of notifications to display in the menu. |
| `max_notification` | integer | `30` | The maximum number of notifications to display in the menu. |
| `notification_dot` | dict | `{'enabled': true, 'corner': 'bottom_left', 'color': 'red', 'margin': [1, 1]}` | A dictionary specifying the notification dot settings for the widget. |
| `only_unread` | boolean | `false` | Whether to show only unread notifications. |
| `show_comment_count`| boolean | `false` | Whether to request and display aggregated comment counts for supported notifications. |
| `reason_filters` | list | `[]` | Optional list of notification reasons to include (e.g. `['mention', 'assign']`). Empty list returns all reasons. |
| `max_field_size` | integer | `100` | The maximum number of characters in the title before truncation. |
| `menu` | dict | `{'blur': true, 'round_corners': true, 'round_corners_type': 'normal', 'border_color': 'System', 'alignment': 'right', 'direction': 'down', 'offset_top': 6, 'offset_left': 0}` | Menu settings for the widget. |
| `icons` | dict | `{'issue': '\uf41b', 'pull_request': '\uea64', 'release': '\uea84', 'discussion': '\uf442', 'checksuite': '\uf418', 'default': '\uea84', 'github_logo': '\uea84'}` | Icons for different types of notifications in the menu. |
| `menu` | dict | `{'blur': true, 'round_corners': true, 'round_corners_type': 'normal', 'border_color': 'System', 'alignment': 'right', 'direction': 'down', 'offset_top': 6, 'offset_left': 0, 'show_categories': true, 'categories_order': []}` | Menu settings for the widget. |
| `icons` | dict | `{'issue': '\uf41b', 'issue_closed': '\uf41d', 'pull_request': '\uea64', 'pull_request_closed': '\uebda', 'pull_request_merged': '\uf17f', 'pull_request_draft': '\uebdb', 'release': '\uea84', 'discussion': '\uf442', 'discussion_answered': '\uf4c0', 'checksuite': '\uf418', 'default': '\uea84', 'github_logo': '\uea84', 'comment': '\uf41f'}` | Icons for different types of notifications in the menu. |
| `animation` | dict | `{'enabled': true, 'type': 'fadeInOut', 'duration': 200}` | Animation settings for the widget. |
| `container_shadow` | dict | `None` | Container shadow options. |
| `label_shadow` | dict | `None` | Label shadow options. |
Expand All @@ -24,13 +26,15 @@ github:
label: "<span>\ueba1</span>"
label_alt: "Notifications {data}" # {data} return number of unread notification
token: ghp_xxxxxxxxxxx # GitHub Personal access tokens (classic) https://github.com/settings/tokens
max_notification: 20 # Max number of notification displaying in menu max: 50
max_notification: 30 # Max number of notification displaying in menu max: 50
notification_dot:
enabled: True
corner: "bottom_left" # Can be "top_left", "top_right", "bottom_left", "bottom_right"
color: "red" # Can be hex color or string
margin: [ 1, 1 ] # x and y margin for the dot
only_unread: false # Show only unread or all notifications;
show_comment_count: false # Summarize comment totals for issues, PRs, and discussions
reason_filters: [] # e.g. ['mention', 'assign'] to limit notifications by reason
max_field_size: 54 # Max characters in title before truncation.
update_interval: 300 # Check for new notification in seconds
menu:
Expand All @@ -40,6 +44,8 @@ github:
border_color: "System" # Set the border color for the menu (this option is not supported on Windows 10)
alignment: "right"
direction: "down"
show_categories: false
categories_order: ["PullRequest", "Issue", "CheckSuite", "Release", "Discussion"]
label_shadow:
enabled: True
color: "black"
Expand All @@ -50,14 +56,20 @@ github:

- **label:** The format string for the label. You can use placeholders like `{icon}` to dynamically insert icon information.
- **label_alt:** The alternative format string for the label. Useful for displaying additional notification details.
- **icons:** A dictionary specifying the icons for different types of notifications in the menu. The available keys are:
- **issue:** The icon for issue notifications.
- **pull_request:** The icon for pull request notifications.
- **icons:** A dictionary specifying the icons for different types of notifications in the menu. All icons have defaults defined in validation. The available keys are:
- **issue:** The icon for open issue notifications.
- **issue_closed:** The icon for closed issues.
- **pull_request:** The icon for open pull request notifications.
- **pull_request_closed:** The icon for closed pull requests (not merged).
- **pull_request_merged:** The icon for merged pull requests.
- **pull_request_draft:** The icon for draft pull requests.
- **release:** The icon for release notifications.
- **discussion:** The icon for discussion notifications.
- **discussion:** The icon for open discussion notifications.
- **discussion_answered:** The icon for discussions with an accepted answer.
- **checksuite:** The icon for check suite notifications.
- **default:** The default icon for other types of notifications.
- **github_logo:** The icon for the GitHub logo.
- **default:** The default icon for notification types not explicitly handled.
- **github_logo:** The icon for the GitHub logo (used in empty state).
- **comment:** The icon that prefixes the comment count badge when `show_comment_count` is enabled.
- **tooltip:** Whether to show the tooltip on hover.
- **update_interval:** The interval in seconds to update the notifications. Must be between 60 and 3600.
- **token:** The GitHub personal access token. GitHub Personal access tokens (classic) https://github.com/settings/tokens you can set `token: env`, this means you have to set YASB_GITHUB_TOKEN in environment variable.
Expand All @@ -68,6 +80,8 @@ github:
- **color:** Set the color of the notification dot. Can be hex or string color.
- **margin:** Set the x, y margin for the notification dot.
- **only_unread:** Whether to show only unread notifications.
- **show_comment_count:** When enabled, the widget performs a single batched GraphQL request to fetch comment totals for issues, pull requests, and discussions. Pull request counts include review threads. The comment count is displayed alongside each notification item.
- **reason_filters:** Optional list of notification reasons to include. Leave empty to show all reasons. Supported values include `assign`, `author`, `comment`, `ci_activity`, `invitation`, `manual`, `mention`, `review_requested`, `security_alert`, `state_change`, `subscribed`, and `team_mention`.
- **max_field_size:** The maximum number of characters in the title before truncation.
- **menu:** A dictionary specifying the menu settings for the widget. It contains the following keys:
- **blur:** Enable blur effect for the menu.
Expand All @@ -78,6 +92,11 @@ github:
- **direction:** Set the direction of the menu (up, down).
- **offset_top:** Set the offset from the top of the screen.
- **offset_left:** Set the offset from the left of the screen.
- **show_categories:** Toggle grouping notifications by their GitHub type. When enabled, each group renders inside a `.section` container with a `.section-header` label.
- **categories_order:** Optional list that defines the preferred order of categories when `show_categories` is enabled. Values are case-insensitive and must match GitHub notification types (for example `PullRequest`, `Issue`). Any categories not listed appear after the configured ones. Available categories include `PullRequest`, `Issue`, `CheckSuite`, `Release`, and `Discussion`.

When `show_categories` is enabled, the first and last notification card within each section gains the `.first` and `.last` classes. If categories are hidden, those classes are applied to the first and last items in the flat list instead. Use them to fine-tune spacing or borders.

- **animation:** A dictionary specifying the animation settings for the widget. It contains three keys: `enabled`, `type`, and `duration`. The `type` can be `fadeInOut` and the `duration` is the animation duration in milliseconds.
- **container_shadow:** Container shadow options.
- **label_shadow:** Label shadow options.
Expand All @@ -92,17 +111,30 @@ github:
.github-menu .header {}
.github-menu .footer {}
.github-menu .footer .label {}
.github-menu .contents {}
.github-menu .contents .item {}
.github-menu .contents .item.new {} /* New notification */
.github-menu .contents .item .title {}
.github-menu .contents .item .description {}
.github-menu .contents .item .icon {} /* Default icon */
.github-menu .contents {} /* Scrollable area containing all notification sections */
.github-menu .contents .section {} /* Container for notification items, wraps all items when categories disabled, or each category group when enabled */
.github-menu .contents .section-header {} /* Category title (e.g., "Issues", "Pull Requests"). Only visible when show_categories is enabled */
.github-menu .contents .item {} /* Individual notification card */
.github-menu .contents .item.first {} /* First item in section or list */
.github-menu .contents .item.last {} /* Last item in section or list */
.github-menu .contents .item.new {} /* Style for unread notification card */
.github-menu .contents .item .title {} /* Title text of notification card */
.github-menu .contents .item .description {} /* Description text of notification card */
.github-menu .contents .item .icon {} /* Default style for icon */
.github-menu .contents .item .icon.issue {} /* Issue icon */
.github-menu .contents .item .icon.issue.closed {}
.github-menu .contents .item .icon.issue.open {}
.github-menu .contents .item .icon.pullrequest {} /* Pull request icon */
.github-menu .contents .item .icon.pullrequest.open {}
.github-menu .contents .item .icon.pullrequest.closed {}
.github-menu .contents .item .icon.pullrequest.merged {}
.github-menu .contents .item .icon.pullrequest.draft {}
.github-menu .contents .item .icon.release {} /* Release icon */
.github-menu .contents .item .icon.discussion {} /* Discussion icon */
.github-menu .contents .item .icon.discussion.answered {}
.github-menu .contents .item .icon.checksuite {} /* Check suite icon */
.github-menu .contents .item .comment-count {} /* Comment label text */
.github-menu .contents .item .comment-icon {} /* Comment icon */
```

## Example Style for the Widget and Menu
Expand All @@ -117,7 +149,7 @@ github:
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
font-size: 15px;
font-weight: 400;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-family: 'Segoe UI';
padding: 8px;
color: white;
background-color: rgba(17, 17, 27, 0.75);
Expand Down
59 changes: 59 additions & 0 deletions src/core/utils/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import platform
import re
from datetime import datetime, timezone
from enum import StrEnum
from functools import lru_cache
from pathlib import Path
Expand Down Expand Up @@ -49,6 +50,64 @@ def is_windows_10() -> bool:
return bool(re.match(r"^10\.0\.1\d{4}$", version))


def get_relative_time(iso_timestamp: str) -> str:
"""
Convert an ISO 8601 timestamp to a human-readable relative time string.

Args:
iso_timestamp: ISO 8601 formatted timestamp (e.g., "2024-11-01T12:00:00Z")

Returns:
A relative time string (e.g., "3 days ago", "2 weeks ago", "just now")
Returns empty string if timestamp is invalid or empty.

Examples:
>>> get_relative_time("2024-11-07T12:00:00Z")
"just now"
>>> get_relative_time("2024-11-04T12:00:00Z")
"3 days ago"
"""
if not iso_timestamp:
return ""

try:
# Parse ISO 8601 timestamp
updated = datetime.fromisoformat(iso_timestamp.replace("Z", "+00:00"))
now = datetime.now(timezone.utc)
diff = now - updated

seconds = diff.total_seconds()
minutes = seconds / 60
hours = minutes / 60
days = hours / 24
weeks = days / 7
months = days / 30
years = days / 365

if seconds < 60:
return "just now"
elif minutes < 60:
m = int(minutes)
return f"{m} minute{'s' if m != 1 else ''} ago"
elif hours < 24:
h = int(hours)
return f"{h} hour{'s' if h != 1 else ''} ago"
elif days < 7:
d = int(days)
return f"{d} day{'s' if d != 1 else ''} ago"
elif weeks < 4:
w = int(weeks)
return f"{w} week{'s' if w != 1 else ''} ago"
elif months < 12:
mo = int(months)
return f"{mo} month{'s' if mo != 1 else ''} ago"
else:
y = int(years)
return f"{y} year{'s' if y != 1 else ''} ago"
except Exception:
return ""


def is_process_running(process_name: str) -> bool:
for proc in psutil.process_iter(["name"]):
if proc.info["name"] == process_name:
Expand Down
Loading
Loading