diff --git a/config/parser.go b/config/parser.go index 6ec15db8..29d8347a 100644 --- a/config/parser.go +++ b/config/parser.go @@ -71,6 +71,7 @@ type PrsLayoutConfig struct { CreatedAt ColumnConfig `yaml:"createdAt,omitempty"` Repo ColumnConfig `yaml:"repo,omitempty"` Author ColumnConfig `yaml:"author,omitempty"` + AuthorIcon ColumnConfig `yaml:"authorIcon,omitempty"` Assignees ColumnConfig `yaml:"assignees,omitempty"` Title ColumnConfig `yaml:"title,omitempty"` Base ColumnConfig `yaml:"base,omitempty"` @@ -81,15 +82,16 @@ type PrsLayoutConfig struct { } type IssuesLayoutConfig struct { - UpdatedAt ColumnConfig `yaml:"updatedAt,omitempty"` - CreatedAt ColumnConfig `yaml:"createdAt,omitempty"` - State ColumnConfig `yaml:"state,omitempty"` - Repo ColumnConfig `yaml:"repo,omitempty"` - Title ColumnConfig `yaml:"title,omitempty"` - Creator ColumnConfig `yaml:"creator,omitempty"` - Assignees ColumnConfig `yaml:"assignees,omitempty"` - Comments ColumnConfig `yaml:"comments,omitempty"` - Reactions ColumnConfig `yaml:"reactions,omitempty"` + UpdatedAt ColumnConfig `yaml:"updatedAt,omitempty"` + CreatedAt ColumnConfig `yaml:"createdAt,omitempty"` + State ColumnConfig `yaml:"state,omitempty"` + Repo ColumnConfig `yaml:"repo,omitempty"` + Title ColumnConfig `yaml:"title,omitempty"` + Creator ColumnConfig `yaml:"creator,omitempty"` + CreatorIcon ColumnConfig `yaml:"creatorIcon,omitempty"` + Assignees ColumnConfig `yaml:"assignees,omitempty"` + Comments ColumnConfig `yaml:"comments,omitempty"` + Reactions ColumnConfig `yaml:"reactions,omitempty"` } type LayoutConfig struct { @@ -143,6 +145,15 @@ type Pager struct { type HexColor string +type ColorThemeIcon struct { + NewContributor HexColor `yaml:"newcontributor" validate:"omitempty,hexcolor"` + Contributor HexColor `yaml:"contributor" validate:"omitempty,hexcolor"` + Collaborator HexColor `yaml:"collaborator" validate:"omitempty,hexcolor"` + Member HexColor `yaml:"member" validate:"omitempty,hexcolor"` + Owner HexColor `yaml:"owner" validate:"omitempty,hexcolor"` + UnknownRole HexColor `yaml:"unknownrole" validate:"omitempty,hexcolor"` +} + type ColorThemeText struct { Primary HexColor `yaml:"primary" validate:"omitempty,hexcolor"` Secondary HexColor `yaml:"secondary" validate:"omitempty,hexcolor"` @@ -164,6 +175,7 @@ type ColorThemeBackground struct { } type ColorTheme struct { + Icon ColorThemeIcon `yaml:"icon" validate:"required"` Text ColorThemeText `yaml:"text" validate:"required"` Background ColorThemeBackground `yaml:"background" validate:"required"` Border ColorThemeBorder `yaml:"border" validate:"required"` @@ -173,6 +185,19 @@ type ColorThemeConfig struct { Inline ColorTheme `yaml:",inline"` } +type IconTheme struct { + NewContributor string `yaml:"newcontributor,omitempty"` + Contributor string `yaml:"contributor,omitempty"` + Collaborator string `yaml:"collaborator,omitempty"` + Member string `yaml:"member,omitempty"` + Owner string `yaml:"owner,omitempty"` + UnknownRole string `yaml:"unknownrole,omitempty"` +} + +type IconThemeConfig struct { + Inline IconTheme `yaml:",inline"` +} + type TableUIThemeConfig struct { ShowSeparator bool `yaml:"showSeparator" default:"true"` Compact bool `yaml:"compact" default:"false"` @@ -186,18 +211,20 @@ type UIThemeConfig struct { type ThemeConfig struct { Ui UIThemeConfig `yaml:"ui,omitempty" validate:"omitempty"` Colors *ColorThemeConfig `yaml:"colors,omitempty" validate:"omitempty"` + Icons *IconThemeConfig `yaml:"icons,omitempty" validate:"omitempty"` } type Config struct { - PRSections []PrsSectionConfig `yaml:"prSections"` - IssuesSections []IssuesSectionConfig `yaml:"issuesSections"` - Repo RepoConfig `yaml:"repo"` - Defaults Defaults `yaml:"defaults"` - Keybindings Keybindings `yaml:"keybindings"` - RepoPaths map[string]string `yaml:"repoPaths"` - Theme *ThemeConfig `yaml:"theme,omitempty" validate:"omitempty"` - Pager Pager `yaml:"pager"` - ConfirmQuit bool `yaml:"confirmQuit"` + PRSections []PrsSectionConfig `yaml:"prSections"` + IssuesSections []IssuesSectionConfig `yaml:"issuesSections"` + Repo RepoConfig `yaml:"repo"` + Defaults Defaults `yaml:"defaults"` + Keybindings Keybindings `yaml:"keybindings"` + RepoPaths map[string]string `yaml:"repoPaths"` + Theme *ThemeConfig `yaml:"theme,omitempty" validate:"omitempty"` + Pager Pager `yaml:"pager"` + ConfirmQuit bool `yaml:"confirmQuit"` + ShowAuthorIcons bool `yaml:"showAuthorIcons"` } type configError struct { @@ -233,6 +260,9 @@ func (parser ConfigParser) getDefaultConfig() Config { Author: ColumnConfig{ Width: utils.IntPtr(15), }, + AuthorIcon: ColumnConfig{ + Hidden: utils.BoolPtr(false), + }, Assignees: ColumnConfig{ Width: utils.IntPtr(20), Hidden: utils.BoolPtr(true), @@ -258,6 +288,9 @@ func (parser ConfigParser) getDefaultConfig() Config { Creator: ColumnConfig{ Width: utils.IntPtr(10), }, + CreatorIcon: ColumnConfig{ + Hidden: utils.BoolPtr(false), + }, Assignees: ColumnConfig{ Width: utils.IntPtr(20), Hidden: utils.BoolPtr(true), @@ -313,6 +346,7 @@ func (parser ConfigParser) getDefaultConfig() Config { }, }, ConfirmQuit: false, + ShowAuthorIcons: true, } } diff --git a/data/issueapi.go b/data/issueapi.go index 86a7455e..76ace009 100644 --- a/data/issueapi.go +++ b/data/issueapi.go @@ -7,6 +7,8 @@ import ( "github.com/charmbracelet/log" gh "github.com/cli/go-gh/v2/pkg/api" graphql "github.com/cli/shurcooL-graphql" + + "github.com/dlvhdr/gh-dash/v4/ui/theme" ) type IssueData struct { @@ -17,14 +19,15 @@ type IssueData struct { Author struct { Login string } - UpdatedAt time.Time - CreatedAt time.Time - Url string - Repository Repository - Assignees Assignees `graphql:"assignees(first: 3)"` - Comments IssueComments `graphql:"comments(first: 15)"` - Reactions IssueReactions `graphql:"reactions(first: 1)"` - Labels IssueLabels `graphql:"labels(first: 3)"` + AuthorAssociation string + UpdatedAt time.Time + CreatedAt time.Time + Url string + Repository Repository + Assignees Assignees `graphql:"assignees(first: 3)"` + Comments IssueComments `graphql:"comments(first: 15)"` + Reactions IssueReactions `graphql:"reactions(first: 1)"` + Labels IssueLabels `graphql:"labels(first: 3)"` } type IssueComments struct { @@ -53,6 +56,14 @@ type IssueLabels struct { Nodes []Label } +func (data IssueData) GetAuthor(theme theme.Theme, showAuthorIcons bool) string { + author := data.Author.Login + if showAuthorIcons { + author += fmt.Sprintf(" %s", GetAuthorRoleIcon(data.AuthorAssociation, theme)) + } + return author +} + func (data IssueData) GetTitle() string { return data.Title } diff --git a/data/prapi.go b/data/prapi.go index 11271ff1..56ac1adb 100644 --- a/data/prapi.go +++ b/data/prapi.go @@ -13,6 +13,7 @@ import ( "github.com/shurcooL/githubv4" "github.com/dlvhdr/gh-dash/v4/config" + "github.com/dlvhdr/gh-dash/v4/ui/theme" ) type PullRequestData struct { @@ -22,16 +23,17 @@ type PullRequestData struct { Author struct { Login string } - UpdatedAt time.Time - CreatedAt time.Time - Url string - State string - Mergeable string - ReviewDecision string - Additions int - Deletions int - HeadRefName string - BaseRefName string + AuthorAssociation string + UpdatedAt time.Time + CreatedAt time.Time + Url string + State string + Mergeable string + ReviewDecision string + Additions int + Deletions int + HeadRefName string + BaseRefName string HeadRepository struct { Name string } @@ -167,6 +169,14 @@ type PageInfo struct { EndCursor string } +func (data PullRequestData) GetAuthor(theme theme.Theme, showAuthorIcon bool) string { + author := data.Author.Login + if showAuthorIcon { + author += fmt.Sprintf(" %s", GetAuthorRoleIcon(data.AuthorAssociation, theme)) + } + return author +} + func (data PullRequestData) GetTitle() string { return data.Title } diff --git a/data/utils.go b/data/utils.go index bad7ff58..21d4961b 100644 --- a/data/utils.go +++ b/data/utils.go @@ -2,6 +2,9 @@ package data import ( "time" + + "github.com/charmbracelet/lipgloss" + "github.com/dlvhdr/gh-dash/v4/ui/theme" ) type RowData interface { @@ -22,3 +25,21 @@ func IsStatusWaiting(status string) bool { func IsConclusionAFailure(conclusion string) bool { return conclusion == "FAILURE" || conclusion == "TIMED_OUT" || conclusion == "STARTUP_FAILURE" } + +func GetAuthorRoleIcon(role string, theme theme.Theme) string { + // https://docs.github.com/en/graphql/reference/enums#commentauthorassociation + switch role { + case "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "NONE": + return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor(theme.NewContributorIconColor)).Render(theme.NewContributorIcon) + case "COLLABORATOR": + return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor(theme.CollaboratorIconColor)).Render(theme.CollaboratorIcon) + case "CONTRIBUTOR": + return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor(theme.ContributorIconColor)).Render(theme.ContributorIcon) + case "MEMBER": + return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor(theme.MemberIconColor)).Render(theme.MemberIcon) + case "OWNER": + return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor(theme.OwnerIconColor)).Render(theme.OwnerIcon) + default: + return lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor(theme.UnknownRoleIconColor)).Render(theme.UnknownRoleIcon) + } +} diff --git a/docs/data/schemas/gh-dash.yaml b/docs/data/schemas/gh-dash.yaml index a41a8c52..ef7b3713 100644 --- a/docs/data/schemas/gh-dash.yaml +++ b/docs/data/schemas/gh-dash.yaml @@ -239,3 +239,14 @@ properties: - less - delta default: less + showAuthorIcons: + title: Show Author Role Icons + description: | + Specifies whether to show author-role icons in the dashboard. + Set this value to `false` to hide the author-role icons. + See the [Theme Icons](theme#icons) section and + [Icon Colors](theme#colors.icons) section for + configuration options you can set to change the author-role icons and author-role icon colors. + type: boolean + schematize: + weight: 8 diff --git a/docs/data/schemas/layout/issue.yaml b/docs/data/schemas/layout/issue.yaml index 81ff87ff..fccd03d1 100644 --- a/docs/data/schemas/layout/issue.yaml +++ b/docs/data/schemas/layout/issue.yaml @@ -152,6 +152,24 @@ properties: The heading for this column is ![styled:`Creator`](). default: width: 10 + creatorIcon: + title: Issue Creator Role Icon + description: Defines options for the role icon for each issue in an issue section. + type: object + properties: + hidden: + title: Hide Creator Icon + description: Specify whether the role icon for issue creators should be hidden from view. + schematize: + details: | + Set this value to `true` to hide the role icon for issue creators. + type: boolean + schematize: + weight: 6 + skip_schema_render: true + format: yaml + details: | + This setting affects display of the role icon for the person who created the issue. assignees: title: Issue Assignees Column description: Defines options for the assignees column in an issue section. @@ -159,7 +177,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 6 + weight: 7 skip_schema_render: true format: yaml details: | @@ -176,7 +194,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 7 + weight: 8 skip_schema_render: true format: yaml details: | @@ -190,7 +208,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 8 + weight: 9 skip_schema_render: true format: yaml details: | diff --git a/docs/data/schemas/layout/pr.yaml b/docs/data/schemas/layout/pr.yaml index 345b0200..32bfbd9f 100644 --- a/docs/data/schemas/layout/pr.yaml +++ b/docs/data/schemas/layout/pr.yaml @@ -162,8 +162,27 @@ properties: This column displays the username for the person who created the PR. The heading for this column is ![styled:`Author`](). + properties: default: width: 15 + authorIcon: + title: PR Author Role Icon + description: Defines options for the role icon for each PR in a PR section. + type: object + properties: + hidden: + title: Hide Author Role Icon + description: Specify whether the role icon for PR authors should be hidden from view. + schematize: + details: | + Set this value to `true` to hide the role icon for PR authors. + type: boolean + schematize: + weight: 6 + skip_schema_render: true + format: yaml + details: | + This setting affects display of the role icon for the person who created the PR. assignees: title: PR Assignees Column description: Defines options for the assignees column in a PR section. @@ -171,7 +190,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 6 + weight: 7 skip_schema_render: true format: yaml details: | @@ -188,7 +207,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 7 + weight: 8 skip_schema_render: true format: yaml details: | @@ -205,7 +224,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 8 + weight: 9 skip_schema_render: true format: yaml details: | @@ -230,7 +249,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 9 + weight: 10 skip_schema_render: true format: yaml details: | @@ -255,7 +274,7 @@ properties: oneOf: - $ref: ./options.yaml schematize: - weight: 10 + weight: 11 skip_schema_render: true format: yaml details: | diff --git a/docs/data/schemas/theme.yaml b/docs/data/schemas/theme.yaml index 379e1a45..61b6a177 100644 --- a/docs/data/schemas/theme.yaml +++ b/docs/data/schemas/theme.yaml @@ -8,8 +8,8 @@ schematize: format: yaml details: | This setting defines the dashboard's theme. It only effects the presentation of the dashboard, - not the data. Currently, the theme only defines colors. To control how table columns and - preview pane display for the views, use the [sref:`defaults`], [sref:`prSections`], and + not the data. Currently, the theme only defines colors and icons. To control how table columns + and preview pane display for the views, use the [sref:`defaults`], [sref:`prSections`], and [sref:`issueSections`] settings. ```alert @@ -75,13 +75,65 @@ properties: schematize: skip_schema_render: true format: yaml + icons: + title: Theme Icons + description: Defines the author-role icons for the dashboard. + type: object + schematize: + skip_schema_render: true + weight: 2 + details: | + This setting defines a map of author-role icons for the dashboard. + properties: + newcontributor: + title: New Contributor Role Icon + description: >- + Specifies the character to use as the new-contributor-role icon. + schematize: + weight: 1 + type: string + contributor: + title: Contributor Role Icon Color + description: >- + Specifies the character to use as the contributor-role icon. + schematize: + weight: 2 + type: string + collaborator: + title: Collaborator Role Icon Color + description: >- + Specifies the character to use as the collaborator-role icon. + schematize: + weight: 3 + type: string + member: + title: Member Role Icon Color + description: >- + Specifies the character to use as the member-role icon. + schematize: + weight: 4 + type: string + owner: + title: Owner Role Icon Color + description: >- + Specifies the character to use as the owner-role icon. + schematize: + weight: 5 + type: string + unknownrole: + title: Unknown Role Icon Color + description: >- + Specifies the character to use as the unknown-role icon. + schematize: + weight: 6 + type: string colors: title: Theme Colors description: Defines text, background, and border colors for the dashboard. type: object schematize: skip_schema_render: true - weight: 1 + weight: 3 details: | This setting defines a map of colors for the dashboard's text, background, and border colors. @@ -110,6 +162,7 @@ properties: Defines the foreground (text) colors for the dashboard. type: object schematize: + weight: 1 skip_schema_render: true format: yaml required: @@ -276,6 +329,7 @@ properties: description: Defines the background colors for the dashboard. type: object schematize: + weight: 2 skip_schema_render: true details: | Defines the background colors for the dashboard. By default, the background color for @@ -312,6 +366,7 @@ properties: description: Defines the border colors for the dashboard. type: object schematize: + weight: 3 skip_schema_render: true format: yaml required: @@ -382,6 +437,64 @@ properties: type: string default: "#000000" pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ + icon: + title: Icon Colors + description: >- + Defines author-role icon colors for the dashboard. + type: object + schematize: + weight: 4 + skip_schema_render: true + format: yaml + properties: + newcontributor: + title: New Contributor Role Icon Color + description: >- + Specifies the icon color for the new-contributor-role icon. + schematize: + weight: 1 + type: string + pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ + contributor: + title: Contributor Role Icon Color + description: >- + Specifies the icon color for the contributor-role icon. + schematize: + weight: 2 + type: string + pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ + collaborator: + title: Collaborator Role Icon Color + description: >- + Specifies the icon color for the collaborator-role icon. + schematize: + weight: 3 + type: string + pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ + member: + title: Member Role Icon Color + description: >- + Specifies the icon color for the member-role icon. + schematize: + weight: 4 + type: string + pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ + owner: + title: Owner Role Icon Color + description: >- + Specifies the icon color for the owner-role icon. + schematize: + weight: 5 + type: string + pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ + unknownrole: + title: Unknown Role Icon Color + description: >- + Specifies the icon color for the unknown-role icon. + schematize: + weight: 6 + type: string + pattern: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ default: ui: sectionsShowCount: true diff --git a/ui/components/issue/issue.go b/ui/components/issue/issue.go index 1a696b92..410c69ba 100644 --- a/ui/components/issue/issue.go +++ b/ui/components/issue/issue.go @@ -16,6 +16,7 @@ import ( type Issue struct { Ctx *context.ProgramContext Data data.IssueData + ShowAuthorIcon bool } func (issue *Issue) ToTableRow() table.Row { @@ -72,7 +73,7 @@ func (issue *Issue) renderTitle() string { } func (issue *Issue) renderOpenedBy() string { - return issue.getTextStyle().Render(issue.Data.Author.Login) + return issue.getTextStyle().Render(issue.Data.GetAuthor(issue.Ctx.Theme, issue.ShowAuthorIcon)) } func (issue *Issue) renderAssignees() string { diff --git a/ui/components/issuessection/issuessection.go b/ui/components/issuessection/issuessection.go index e016e691..2ebbbab3 100644 --- a/ui/components/issuessection/issuessection.go +++ b/ui/components/issuessection/issuessection.go @@ -241,7 +241,7 @@ func GetSectionColumns( func (m Model) BuildRows() []table.Row { var rows []table.Row for _, currIssue := range m.Issues { - issueModel := issue.Issue{Ctx: m.Ctx, Data: currIssue} + issueModel := issue.Issue{Ctx: m.Ctx, Data: currIssue, ShowAuthorIcon: m.ShowAuthorIcon} rows = append(rows, issueModel.ToTableRow()) } @@ -349,6 +349,9 @@ func FetchAllSections( time.Now(), time.Now(), ) // 0 is the search section + if sectionConfig.Layout.CreatorIcon.Hidden != nil { + sectionModel.ShowAuthorIcon = !*sectionConfig.Layout.CreatorIcon.Hidden + } sections = append(sections, §ionModel) fetchIssuesCmds = append( fetchIssuesCmds, diff --git a/ui/components/pr/pr.go b/ui/components/pr/pr.go index 0bc293ad..f615d8ca 100644 --- a/ui/components/pr/pr.go +++ b/ui/components/pr/pr.go @@ -20,6 +20,7 @@ type PullRequest struct { Data *data.PullRequestData Branch git.Branch Columns []table.Column + ShowAuthorIcon bool } func (pr *PullRequest) getTextStyle() lipgloss.Style { @@ -195,7 +196,7 @@ func (pr *PullRequest) renderExtendedTitle(isSelected bool) string { baseStyle = baseStyle.Foreground(pr.Ctx.Theme.SecondaryText).Background(pr.Ctx.Theme.SelectedBackground) } - author := baseStyle.Render(fmt.Sprintf("@%s", pr.Data.Author.Login)) + author := baseStyle.Render(fmt.Sprintf("@%s", pr.Data.GetAuthor(pr.Ctx.Theme, pr.ShowAuthorIcon))) top := lipgloss.JoinHorizontal(lipgloss.Top, pr.Data.Repository.NameWithOwner, fmt.Sprintf(" #%d by %s", pr.Data.Number, author)) branchHidden := pr.Ctx.Config.Defaults.Layout.Prs.Base.Hidden if branchHidden == nil || !*branchHidden { @@ -217,7 +218,7 @@ func (pr *PullRequest) renderExtendedTitle(isSelected bool) string { } func (pr *PullRequest) renderAuthor() string { - return pr.getTextStyle().Render(pr.Data.Author.Login) + return pr.getTextStyle().Render(pr.Data.GetAuthor(pr.Ctx.Theme, pr.ShowAuthorIcon)) } func (pr *PullRequest) renderAssignees() string { diff --git a/ui/components/prssection/prssection.go b/ui/components/prssection/prssection.go index be32d326..6381f6d2 100644 --- a/ui/components/prssection/prssection.go +++ b/ui/components/prssection/prssection.go @@ -341,7 +341,7 @@ func (m Model) BuildRows() []table.Row { currItem := m.Table.GetCurrItem() for i, currPr := range m.Prs { i := i - prModel := pr.PullRequest{Ctx: m.Ctx, Data: &currPr, Columns: m.Table.Columns} + prModel := pr.PullRequest{Ctx: m.Ctx, Data: &currPr, Columns: m.Table.Columns, ShowAuthorIcon: m.ShowAuthorIcon} rows = append( rows, prModel.ToTableRow(currItem == i), @@ -467,6 +467,9 @@ func FetchAllSections( sectionModel.Prs = oldSection.Prs sectionModel.LastFetchTaskId = oldSection.LastFetchTaskId } + if sectionConfig.Layout.AuthorIcon.Hidden != nil { + sectionModel.ShowAuthorIcon = !*sectionConfig.Layout.AuthorIcon.Hidden + } sections = append(sections, §ionModel) fetchPRsCmds = append( fetchPRsCmds, diff --git a/ui/components/section/section.go b/ui/components/section/section.go index 011eecd0..fc6f5ead 100644 --- a/ui/components/section/section.go +++ b/ui/components/section/section.go @@ -39,6 +39,7 @@ type BaseModel struct { PromptConfirmationAction string LastFetchTaskId string IsSearchSupported bool + ShowAuthorIcon bool } type NewSectionOptions struct { @@ -74,6 +75,7 @@ func NewModel( TotalCount: 0, PageInfo: nil, PromptConfirmationBox: prompt.NewModel(ctx), + ShowAuthorIcon: ctx.Config.ShowAuthorIcons, } m.Table = table.NewModel( *ctx, diff --git a/ui/constants/constants.go b/ui/constants/constants.go index a488af48..0f1bb4a2 100644 --- a/ui/constants/constants.go +++ b/ui/constants/constants.go @@ -37,4 +37,11 @@ const ( MergedIcon = "" OpenIcon = "" ClosedIcon = "" + + NewContributorIcon = "" // \uebe9 nf-cod-verified_filled + ContributorIcon = "" // \uedc6 nf-fa-user_check + CollaboratorIcon = "" // \uedcf nf-fa-user_shield + MemberIcon = "󰢏" // \udb82\udc8f nf-md-shield_account + OwnerIcon = "󱇐" // \udb84\uddd0 nf-md-crown_outline + UnknownRoleIcon = "󱐡" // \udb85\udc21 nf-md-incognito_circle ) diff --git a/ui/theme/theme.go b/ui/theme/theme.go index 0af8c237..33dee3d6 100644 --- a/ui/theme/theme.go +++ b/ui/theme/theme.go @@ -5,34 +5,59 @@ import ( "github.com/charmbracelet/log" "github.com/dlvhdr/gh-dash/v4/config" + "github.com/dlvhdr/gh-dash/v4/ui/constants" ) type Theme struct { - SelectedBackground lipgloss.AdaptiveColor // config.Theme.Colors.Background.Selected - PrimaryBorder lipgloss.AdaptiveColor // config.Theme.Colors.Border.Primary - FaintBorder lipgloss.AdaptiveColor // config.Theme.Colors.Border.Faint - SecondaryBorder lipgloss.AdaptiveColor // config.Theme.Colors.Border.Secondary - FaintText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Faint - PrimaryText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Primary - SecondaryText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Secondary - InvertedText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Inverted - SuccessText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Success - WarningText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Warning - ErrorText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Error + SelectedBackground lipgloss.AdaptiveColor // config.Theme.Colors.Background.Selected + PrimaryBorder lipgloss.AdaptiveColor // config.Theme.Colors.Border.Primary + FaintBorder lipgloss.AdaptiveColor // config.Theme.Colors.Border.Faint + SecondaryBorder lipgloss.AdaptiveColor // config.Theme.Colors.Border.Secondary + FaintText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Faint + PrimaryText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Primary + SecondaryText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Secondary + InvertedText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Inverted + SuccessText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Success + WarningText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Warning + ErrorText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Error + NewContributorIconColor lipgloss.AdaptiveColor // config.Theme.Colors.Icon.NewContributor + ContributorIconColor lipgloss.AdaptiveColor // config.Theme.Colors.Icon.Contributor + CollaboratorIconColor lipgloss.AdaptiveColor // config.Theme.Colors.Icon.Collaborator + MemberIconColor lipgloss.AdaptiveColor // config.Theme.Colors.Icon.Member + OwnerIconColor lipgloss.AdaptiveColor // config.Theme.Colors.Icon.Owner + UnknownRoleIconColor lipgloss.AdaptiveColor // config.Theme.Colors.Icon.UnknownRole + NewContributorIcon string // config.Theme.Icons.NewContributor + ContributorIcon string // config.Theme.Icons.Contributor + CollaboratorIcon string // config.Theme.Icons.Collaborator + MemberIcon string // config.Theme.Icons.Member + OwnerIcon string // config.Theme.Icons.Owner + UnknownRoleIcon string // config.Theme.Icons.UnknownRole } var DefaultTheme = &Theme{ - PrimaryBorder: lipgloss.AdaptiveColor{Light: "013", Dark: "008"}, - SecondaryBorder: lipgloss.AdaptiveColor{Light: "008", Dark: "007"}, - SelectedBackground: lipgloss.AdaptiveColor{Light: "006", Dark: "008"}, - FaintBorder: lipgloss.AdaptiveColor{Light: "254", Dark: "000"}, - PrimaryText: lipgloss.AdaptiveColor{Light: "000", Dark: "015"}, - SecondaryText: lipgloss.AdaptiveColor{Light: "244", Dark: "251"}, - FaintText: lipgloss.AdaptiveColor{Light: "007", Dark: "245"}, - InvertedText: lipgloss.AdaptiveColor{Light: "015", Dark: "236"}, - SuccessText: lipgloss.AdaptiveColor{Light: "002", Dark: "002"}, - WarningText: lipgloss.AdaptiveColor{Light: "003", Dark: "003"}, - ErrorText: lipgloss.AdaptiveColor{Light: "001", Dark: "001"}, + PrimaryBorder: lipgloss.AdaptiveColor{Light: "013", Dark: "008"}, + SecondaryBorder: lipgloss.AdaptiveColor{Light: "008", Dark: "007"}, + SelectedBackground: lipgloss.AdaptiveColor{Light: "006", Dark: "008"}, + FaintBorder: lipgloss.AdaptiveColor{Light: "254", Dark: "000"}, + PrimaryText: lipgloss.AdaptiveColor{Light: "000", Dark: "015"}, + SecondaryText: lipgloss.AdaptiveColor{Light: "244", Dark: "251"}, + FaintText: lipgloss.AdaptiveColor{Light: "007", Dark: "245"}, + InvertedText: lipgloss.AdaptiveColor{Light: "015", Dark: "236"}, + SuccessText: lipgloss.AdaptiveColor{Light: "002", Dark: "002"}, + WarningText: lipgloss.AdaptiveColor{Light: "003", Dark: "003"}, + ErrorText: lipgloss.AdaptiveColor{Light: "001", Dark: "001"}, + NewContributorIconColor: lipgloss.AdaptiveColor{Light: "077", Dark: "077"}, + ContributorIconColor: lipgloss.AdaptiveColor{Light: "075", Dark: "075"}, + CollaboratorIconColor: lipgloss.AdaptiveColor{Light: "178", Dark: "178"}, + MemberIconColor: lipgloss.AdaptiveColor{Light: "178", Dark: "178"}, + OwnerIconColor: lipgloss.AdaptiveColor{Light: "178", Dark: "178"}, + UnknownRoleIconColor: lipgloss.AdaptiveColor{Light: "178", Dark: "178"}, + NewContributorIcon: constants.NewContributorIcon, + ContributorIcon: constants.ContributorIcon, + CollaboratorIcon: constants.CollaboratorIcon, + MemberIcon: constants.MemberIcon, + OwnerIcon: constants.OwnerIcon, + UnknownRoleIcon: constants.UnknownRoleIcon, } func ParseTheme(cfg *config.Config) Theme { @@ -42,57 +67,108 @@ func ParseTheme(cfg *config.Config) Theme { } return lipgloss.AdaptiveColor{Light: string(hex), Dark: string(hex)} } + _shimIcon := func(icon string, fallback string) string { + if icon != "" { + return icon + } + return fallback + } if cfg.Theme.Colors != nil { - DefaultTheme = &Theme{ - SelectedBackground: _shimHex( - cfg.Theme.Colors.Inline.Background.Selected, - DefaultTheme.SelectedBackground, - ), - PrimaryBorder: _shimHex( - cfg.Theme.Colors.Inline.Border.Primary, - DefaultTheme.PrimaryBorder, - ), - FaintBorder: _shimHex( - cfg.Theme.Colors.Inline.Border.Faint, - DefaultTheme.FaintBorder, - ), - SecondaryBorder: _shimHex( - cfg.Theme.Colors.Inline.Border.Secondary, - DefaultTheme.SecondaryBorder, - ), - FaintText: _shimHex( - cfg.Theme.Colors.Inline.Text.Faint, - DefaultTheme.FaintText, - ), - PrimaryText: _shimHex( - cfg.Theme.Colors.Inline.Text.Primary, - DefaultTheme.PrimaryText, - ), - SecondaryText: _shimHex( - cfg.Theme.Colors.Inline.Text.Secondary, - DefaultTheme.SecondaryText, - ), - InvertedText: _shimHex( - cfg.Theme.Colors.Inline.Text.Inverted, - DefaultTheme.InvertedText, - ), - SuccessText: _shimHex( - cfg.Theme.Colors.Inline.Text.Success, - DefaultTheme.SuccessText, - ), - WarningText: _shimHex( - cfg.Theme.Colors.Inline.Text.Warning, - DefaultTheme.WarningText, - ), - ErrorText: _shimHex( - cfg.Theme.Colors.Inline.Text.Error, - DefaultTheme.ErrorText, - ), - } + DefaultTheme.SelectedBackground = _shimHex( + cfg.Theme.Colors.Inline.Background.Selected, + DefaultTheme.SelectedBackground, + ) + DefaultTheme.PrimaryBorder = _shimHex( + cfg.Theme.Colors.Inline.Border.Primary, + DefaultTheme.PrimaryBorder, + ) + DefaultTheme.FaintBorder = _shimHex( + cfg.Theme.Colors.Inline.Border.Faint, + DefaultTheme.FaintBorder, + ) + DefaultTheme.SecondaryBorder = _shimHex( + cfg.Theme.Colors.Inline.Border.Secondary, + DefaultTheme.SecondaryBorder, + ) + DefaultTheme.FaintText = _shimHex( + cfg.Theme.Colors.Inline.Text.Faint, + DefaultTheme.FaintText, + ) + DefaultTheme.PrimaryText = _shimHex( + cfg.Theme.Colors.Inline.Text.Primary, + DefaultTheme.PrimaryText, + ) + DefaultTheme.SecondaryText = _shimHex( + cfg.Theme.Colors.Inline.Text.Secondary, + DefaultTheme.SecondaryText, + ) + DefaultTheme.InvertedText= _shimHex( + cfg.Theme.Colors.Inline.Text.Inverted, + DefaultTheme.InvertedText, + ) + DefaultTheme.SuccessText = _shimHex( + cfg.Theme.Colors.Inline.Text.Success, + DefaultTheme.SuccessText, + ) + DefaultTheme.WarningText = _shimHex( + cfg.Theme.Colors.Inline.Text.Warning, + DefaultTheme.WarningText, + ) + DefaultTheme.ErrorText = _shimHex( + cfg.Theme.Colors.Inline.Text.Error, + DefaultTheme.ErrorText, + ) + DefaultTheme.NewContributorIconColor = _shimHex( + cfg.Theme.Colors.Inline.Icon.NewContributor, + DefaultTheme.NewContributorIconColor, + ) + DefaultTheme.ContributorIconColor = _shimHex( + cfg.Theme.Colors.Inline.Icon.Contributor, + DefaultTheme.ContributorIconColor, + ) + DefaultTheme.CollaboratorIconColor = _shimHex( + cfg.Theme.Colors.Inline.Icon.Collaborator, + DefaultTheme.CollaboratorIconColor, + ) + DefaultTheme.MemberIconColor = _shimHex( + cfg.Theme.Colors.Inline.Icon.Member, + DefaultTheme.MemberIconColor, + ) + DefaultTheme.OwnerIconColor = _shimHex( + cfg.Theme.Colors.Inline.Icon.Owner, + DefaultTheme.OwnerIconColor, + ) + } + + if cfg.ShowAuthorIcons && cfg.Theme.Icons != nil { + DefaultTheme.NewContributorIcon = _shimIcon( + cfg.Theme.Icons.Inline.NewContributor, + DefaultTheme.NewContributorIcon, + ) + DefaultTheme.ContributorIcon = _shimIcon( + cfg.Theme.Icons.Inline.Contributor, + DefaultTheme.ContributorIcon, + ) + DefaultTheme.CollaboratorIcon = _shimIcon( + cfg.Theme.Icons.Inline.Collaborator, + DefaultTheme.CollaboratorIcon, + ) + DefaultTheme.MemberIcon = _shimIcon( + cfg.Theme.Icons.Inline.Member, + DefaultTheme.MemberIcon, + ) + DefaultTheme.OwnerIcon = _shimIcon( + cfg.Theme.Icons.Inline.Owner, + DefaultTheme.OwnerIcon, + ) + DefaultTheme.UnknownRoleIcon = _shimIcon( + cfg.Theme.Icons.Inline.UnknownRole, + DefaultTheme.UnknownRoleIcon, + ) } - log.Debug("Parsing theme", "config", cfg.Theme.Colors, "theme", DefaultTheme) + log.Debug("Parsing theme", "config", cfg.Theme, "theme", DefaultTheme) return *DefaultTheme }