Deepen theme semantic color coverage
This commit is contained in:
parent
60e3a9c0f8
commit
ce84789edb
3 changed files with 94 additions and 22 deletions
|
|
@ -13,14 +13,29 @@ type Palette struct {
|
||||||
Panel lipgloss.Color
|
Panel lipgloss.Color
|
||||||
PanelInactive lipgloss.Color
|
PanelInactive lipgloss.Color
|
||||||
PanelElevated lipgloss.Color
|
PanelElevated lipgloss.Color
|
||||||
|
StatusBar lipgloss.Color
|
||||||
|
Footer lipgloss.Color
|
||||||
Border lipgloss.Color
|
Border lipgloss.Color
|
||||||
BorderActive lipgloss.Color
|
BorderActive lipgloss.Color
|
||||||
Text lipgloss.Color
|
Text lipgloss.Color
|
||||||
Muted lipgloss.Color
|
Muted lipgloss.Color
|
||||||
Accent lipgloss.Color
|
Accent lipgloss.Color
|
||||||
|
Info lipgloss.Color
|
||||||
|
Success lipgloss.Color
|
||||||
Selection lipgloss.Color
|
Selection lipgloss.Color
|
||||||
|
Hover lipgloss.Color
|
||||||
|
Marked lipgloss.Color
|
||||||
Warning lipgloss.Color
|
Warning lipgloss.Color
|
||||||
Danger lipgloss.Color
|
Danger lipgloss.Color
|
||||||
|
ActivePath lipgloss.Color
|
||||||
|
ConfirmButton lipgloss.Color
|
||||||
|
CancelButton lipgloss.Color
|
||||||
|
ProgressFill lipgloss.Color
|
||||||
|
ProgressEmpty lipgloss.Color
|
||||||
|
HelpNav lipgloss.Color
|
||||||
|
HelpPanels lipgloss.Color
|
||||||
|
HelpDialogs lipgloss.Color
|
||||||
|
HelpMouse lipgloss.Color
|
||||||
Folder lipgloss.Color
|
Folder lipgloss.Color
|
||||||
TextFile lipgloss.Color
|
TextFile lipgloss.Color
|
||||||
ConfigFile lipgloss.Color
|
ConfigFile lipgloss.Color
|
||||||
|
|
@ -66,14 +81,29 @@ func Resolve(name string) (Palette, error) {
|
||||||
Panel: lipgloss.Color("#181825"),
|
Panel: lipgloss.Color("#181825"),
|
||||||
PanelInactive: lipgloss.Color("#1E1E2E"),
|
PanelInactive: lipgloss.Color("#1E1E2E"),
|
||||||
PanelElevated: lipgloss.Color("#24273A"),
|
PanelElevated: lipgloss.Color("#24273A"),
|
||||||
|
StatusBar: lipgloss.Color("#1E1E2E"),
|
||||||
|
Footer: lipgloss.Color("#11111B"),
|
||||||
Border: lipgloss.Color("#45475A"),
|
Border: lipgloss.Color("#45475A"),
|
||||||
BorderActive: lipgloss.Color("#89B4FA"),
|
BorderActive: lipgloss.Color("#89B4FA"),
|
||||||
Text: lipgloss.Color("#CDD6F4"),
|
Text: lipgloss.Color("#CDD6F4"),
|
||||||
Muted: lipgloss.Color("#A6ADC8"),
|
Muted: lipgloss.Color("#A6ADC8"),
|
||||||
Accent: lipgloss.Color("#F5C2E7"),
|
Accent: lipgloss.Color("#F5C2E7"),
|
||||||
|
Info: lipgloss.Color("#89DCEB"),
|
||||||
|
Success: lipgloss.Color("#A6E3A1"),
|
||||||
Selection: lipgloss.Color("#313244"),
|
Selection: lipgloss.Color("#313244"),
|
||||||
|
Hover: lipgloss.Color("#2A2B3C"),
|
||||||
|
Marked: lipgloss.Color("#F38BA8"),
|
||||||
Warning: lipgloss.Color("#F9E2AF"),
|
Warning: lipgloss.Color("#F9E2AF"),
|
||||||
Danger: lipgloss.Color("#F38BA8"),
|
Danger: lipgloss.Color("#F38BA8"),
|
||||||
|
ActivePath: lipgloss.Color("#89DCEB"),
|
||||||
|
ConfirmButton: lipgloss.Color("#A6E3A1"),
|
||||||
|
CancelButton: lipgloss.Color("#F38BA8"),
|
||||||
|
ProgressFill: lipgloss.Color("#89B4FA"),
|
||||||
|
ProgressEmpty: lipgloss.Color("#45475A"),
|
||||||
|
HelpNav: lipgloss.Color("#89B4FA"),
|
||||||
|
HelpPanels: lipgloss.Color("#F9E2AF"),
|
||||||
|
HelpDialogs: lipgloss.Color("#CBA6F7"),
|
||||||
|
HelpMouse: lipgloss.Color("#F38BA8"),
|
||||||
Folder: lipgloss.Color("#89B4FA"),
|
Folder: lipgloss.Color("#89B4FA"),
|
||||||
TextFile: lipgloss.Color("#A6E3A1"),
|
TextFile: lipgloss.Color("#A6E3A1"),
|
||||||
ConfigFile: lipgloss.Color("#F9E2AF"),
|
ConfigFile: lipgloss.Color("#F9E2AF"),
|
||||||
|
|
@ -89,14 +119,29 @@ func Resolve(name string) (Palette, error) {
|
||||||
Panel: lipgloss.Color("#1A1B26"),
|
Panel: lipgloss.Color("#1A1B26"),
|
||||||
PanelInactive: lipgloss.Color("#24283B"),
|
PanelInactive: lipgloss.Color("#24283B"),
|
||||||
PanelElevated: lipgloss.Color("#2A2F44"),
|
PanelElevated: lipgloss.Color("#2A2F44"),
|
||||||
|
StatusBar: lipgloss.Color("#24283B"),
|
||||||
|
Footer: lipgloss.Color("#16161E"),
|
||||||
Border: lipgloss.Color("#3B4261"),
|
Border: lipgloss.Color("#3B4261"),
|
||||||
BorderActive: lipgloss.Color("#7AA2F7"),
|
BorderActive: lipgloss.Color("#7AA2F7"),
|
||||||
Text: lipgloss.Color("#C0CAF5"),
|
Text: lipgloss.Color("#C0CAF5"),
|
||||||
Muted: lipgloss.Color("#9AA5CE"),
|
Muted: lipgloss.Color("#9AA5CE"),
|
||||||
Accent: lipgloss.Color("#BB9AF7"),
|
Accent: lipgloss.Color("#BB9AF7"),
|
||||||
|
Info: lipgloss.Color("#73DACA"),
|
||||||
|
Success: lipgloss.Color("#9ECE6A"),
|
||||||
Selection: lipgloss.Color("#292E42"),
|
Selection: lipgloss.Color("#292E42"),
|
||||||
|
Hover: lipgloss.Color("#252A3D"),
|
||||||
|
Marked: lipgloss.Color("#F7768E"),
|
||||||
Warning: lipgloss.Color("#E0AF68"),
|
Warning: lipgloss.Color("#E0AF68"),
|
||||||
Danger: lipgloss.Color("#F7768E"),
|
Danger: lipgloss.Color("#F7768E"),
|
||||||
|
ActivePath: lipgloss.Color("#73DACA"),
|
||||||
|
ConfirmButton: lipgloss.Color("#9ECE6A"),
|
||||||
|
CancelButton: lipgloss.Color("#F7768E"),
|
||||||
|
ProgressFill: lipgloss.Color("#7AA2F7"),
|
||||||
|
ProgressEmpty: lipgloss.Color("#3B4261"),
|
||||||
|
HelpNav: lipgloss.Color("#7AA2F7"),
|
||||||
|
HelpPanels: lipgloss.Color("#E0AF68"),
|
||||||
|
HelpDialogs: lipgloss.Color("#BB9AF7"),
|
||||||
|
HelpMouse: lipgloss.Color("#F7768E"),
|
||||||
Folder: lipgloss.Color("#7AA2F7"),
|
Folder: lipgloss.Color("#7AA2F7"),
|
||||||
TextFile: lipgloss.Color("#9ECE6A"),
|
TextFile: lipgloss.Color("#9ECE6A"),
|
||||||
ConfigFile: lipgloss.Color("#E0AF68"),
|
ConfigFile: lipgloss.Color("#E0AF68"),
|
||||||
|
|
@ -112,14 +157,29 @@ func Resolve(name string) (Palette, error) {
|
||||||
Panel: lipgloss.Color("#282828"),
|
Panel: lipgloss.Color("#282828"),
|
||||||
PanelInactive: lipgloss.Color("#32302F"),
|
PanelInactive: lipgloss.Color("#32302F"),
|
||||||
PanelElevated: lipgloss.Color("#3C3836"),
|
PanelElevated: lipgloss.Color("#3C3836"),
|
||||||
|
StatusBar: lipgloss.Color("#32302F"),
|
||||||
|
Footer: lipgloss.Color("#1D2021"),
|
||||||
Border: lipgloss.Color("#504945"),
|
Border: lipgloss.Color("#504945"),
|
||||||
BorderActive: lipgloss.Color("#FABD2F"),
|
BorderActive: lipgloss.Color("#FABD2F"),
|
||||||
Text: lipgloss.Color("#EBDBB2"),
|
Text: lipgloss.Color("#EBDBB2"),
|
||||||
Muted: lipgloss.Color("#BDAE93"),
|
Muted: lipgloss.Color("#BDAE93"),
|
||||||
Accent: lipgloss.Color("#83A598"),
|
Accent: lipgloss.Color("#83A598"),
|
||||||
|
Info: lipgloss.Color("#8EC07C"),
|
||||||
|
Success: lipgloss.Color("#B8BB26"),
|
||||||
Selection: lipgloss.Color("#3C3836"),
|
Selection: lipgloss.Color("#3C3836"),
|
||||||
|
Hover: lipgloss.Color("#45403D"),
|
||||||
|
Marked: lipgloss.Color("#FB4934"),
|
||||||
Warning: lipgloss.Color("#FE8019"),
|
Warning: lipgloss.Color("#FE8019"),
|
||||||
Danger: lipgloss.Color("#FB4934"),
|
Danger: lipgloss.Color("#FB4934"),
|
||||||
|
ActivePath: lipgloss.Color("#8EC07C"),
|
||||||
|
ConfirmButton: lipgloss.Color("#B8BB26"),
|
||||||
|
CancelButton: lipgloss.Color("#FB4934"),
|
||||||
|
ProgressFill: lipgloss.Color("#FABD2F"),
|
||||||
|
ProgressEmpty: lipgloss.Color("#504945"),
|
||||||
|
HelpNav: lipgloss.Color("#83A598"),
|
||||||
|
HelpPanels: lipgloss.Color("#FABD2F"),
|
||||||
|
HelpDialogs: lipgloss.Color("#D3869B"),
|
||||||
|
HelpMouse: lipgloss.Color("#FB4934"),
|
||||||
Folder: lipgloss.Color("#83A598"),
|
Folder: lipgloss.Color("#83A598"),
|
||||||
TextFile: lipgloss.Color("#B8BB26"),
|
TextFile: lipgloss.Color("#B8BB26"),
|
||||||
ConfigFile: lipgloss.Color("#FABD2F"),
|
ConfigFile: lipgloss.Color("#FABD2F"),
|
||||||
|
|
@ -135,14 +195,29 @@ func Resolve(name string) (Palette, error) {
|
||||||
Panel: lipgloss.Color("#3B4252"),
|
Panel: lipgloss.Color("#3B4252"),
|
||||||
PanelInactive: lipgloss.Color("#434C5E"),
|
PanelInactive: lipgloss.Color("#434C5E"),
|
||||||
PanelElevated: lipgloss.Color("#4C566A"),
|
PanelElevated: lipgloss.Color("#4C566A"),
|
||||||
|
StatusBar: lipgloss.Color("#434C5E"),
|
||||||
|
Footer: lipgloss.Color("#2E3440"),
|
||||||
Border: lipgloss.Color("#4C566A"),
|
Border: lipgloss.Color("#4C566A"),
|
||||||
BorderActive: lipgloss.Color("#88C0D0"),
|
BorderActive: lipgloss.Color("#88C0D0"),
|
||||||
Text: lipgloss.Color("#ECEFF4"),
|
Text: lipgloss.Color("#ECEFF4"),
|
||||||
Muted: lipgloss.Color("#D8DEE9"),
|
Muted: lipgloss.Color("#D8DEE9"),
|
||||||
Accent: lipgloss.Color("#81A1C1"),
|
Accent: lipgloss.Color("#81A1C1"),
|
||||||
|
Info: lipgloss.Color("#8FBCBB"),
|
||||||
|
Success: lipgloss.Color("#A3BE8C"),
|
||||||
Selection: lipgloss.Color("#434C5E"),
|
Selection: lipgloss.Color("#434C5E"),
|
||||||
|
Hover: lipgloss.Color("#505A70"),
|
||||||
|
Marked: lipgloss.Color("#BF616A"),
|
||||||
Warning: lipgloss.Color("#EBCB8B"),
|
Warning: lipgloss.Color("#EBCB8B"),
|
||||||
Danger: lipgloss.Color("#BF616A"),
|
Danger: lipgloss.Color("#BF616A"),
|
||||||
|
ActivePath: lipgloss.Color("#8FBCBB"),
|
||||||
|
ConfirmButton: lipgloss.Color("#A3BE8C"),
|
||||||
|
CancelButton: lipgloss.Color("#BF616A"),
|
||||||
|
ProgressFill: lipgloss.Color("#88C0D0"),
|
||||||
|
ProgressEmpty: lipgloss.Color("#4C566A"),
|
||||||
|
HelpNav: lipgloss.Color("#81A1C1"),
|
||||||
|
HelpPanels: lipgloss.Color("#EBCB8B"),
|
||||||
|
HelpDialogs: lipgloss.Color("#B48EAD"),
|
||||||
|
HelpMouse: lipgloss.Color("#BF616A"),
|
||||||
Folder: lipgloss.Color("#81A1C1"),
|
Folder: lipgloss.Color("#81A1C1"),
|
||||||
TextFile: lipgloss.Color("#A3BE8C"),
|
TextFile: lipgloss.Color("#A3BE8C"),
|
||||||
ConfigFile: lipgloss.Color("#EBCB8B"),
|
ConfigFile: lipgloss.Color("#EBCB8B"),
|
||||||
|
|
|
||||||
|
|
@ -1390,7 +1390,7 @@ func renderStatus(m Model) string {
|
||||||
return lipgloss.NewStyle().
|
return lipgloss.NewStyle().
|
||||||
Width(m.width).
|
Width(m.width).
|
||||||
Padding(0, 1).
|
Padding(0, 1).
|
||||||
Background(m.palette.PanelInactive).
|
Background(m.palette.StatusBar).
|
||||||
Foreground(m.palette.Text).
|
Foreground(m.palette.Text).
|
||||||
Render(summary + " :: " + m.status)
|
Render(summary + " :: " + m.status)
|
||||||
}
|
}
|
||||||
|
|
@ -1403,12 +1403,12 @@ func renderFooter(m Model) string {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
keyView := lipgloss.NewStyle().
|
keyView := lipgloss.NewStyle().
|
||||||
Background(m.palette.Background).
|
Background(m.palette.Footer).
|
||||||
Foreground(m.palette.FooterKey).
|
Foreground(m.palette.FooterKey).
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Render(help.Key)
|
Render(help.Key)
|
||||||
descView := lipgloss.NewStyle().
|
descView := lipgloss.NewStyle().
|
||||||
Background(m.palette.Background).
|
Background(m.palette.Footer).
|
||||||
Foreground(m.palette.Text).
|
Foreground(m.palette.Text).
|
||||||
Render(" " + help.Desc)
|
Render(" " + help.Desc)
|
||||||
parts = append(parts, keyView+descView)
|
parts = append(parts, keyView+descView)
|
||||||
|
|
@ -1416,7 +1416,7 @@ func renderFooter(m Model) string {
|
||||||
line := strings.Join(parts, " ")
|
line := strings.Join(parts, " ")
|
||||||
if m.selectMode {
|
if m.selectMode {
|
||||||
modeLabel := lipgloss.NewStyle().
|
modeLabel := lipgloss.NewStyle().
|
||||||
Foreground(m.palette.Accent).
|
Foreground(m.palette.Info).
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Render("SELECT TEXT MODE")
|
Render("SELECT TEXT MODE")
|
||||||
if line != "" {
|
if line != "" {
|
||||||
|
|
@ -1429,7 +1429,7 @@ func renderFooter(m Model) string {
|
||||||
m.width,
|
m.width,
|
||||||
lipgloss.Left,
|
lipgloss.Left,
|
||||||
line,
|
line,
|
||||||
lipgloss.WithWhitespaceBackground(m.palette.Background),
|
lipgloss.WithWhitespaceBackground(m.palette.Footer),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1557,7 +1557,7 @@ func renderConfirmActions(width int, palette theme.Palette) string {
|
||||||
confirm := lipgloss.NewStyle().
|
confirm := lipgloss.NewStyle().
|
||||||
Width(buttonWidth).
|
Width(buttonWidth).
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
Background(palette.TextFile).
|
Background(palette.ConfirmButton).
|
||||||
Foreground(palette.Background).
|
Foreground(palette.Background).
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Render("Enter / y")
|
Render("Enter / y")
|
||||||
|
|
@ -1565,7 +1565,7 @@ func renderConfirmActions(width int, palette theme.Palette) string {
|
||||||
cancel := lipgloss.NewStyle().
|
cancel := lipgloss.NewStyle().
|
||||||
Width(buttonWidth).
|
Width(buttonWidth).
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
Background(palette.Danger).
|
Background(palette.CancelButton).
|
||||||
Foreground(palette.Background).
|
Foreground(palette.Background).
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Render("Esc / n")
|
Render("Esc / n")
|
||||||
|
|
@ -1604,7 +1604,6 @@ func renderHelpModal(modal modalState, palette theme.Palette, width int) string
|
||||||
noteStyle := lipgloss.NewStyle().Width(contentWidth).Background(palette.Panel).Foreground(palette.FooterKey).Bold(true)
|
noteStyle := lipgloss.NewStyle().Width(contentWidth).Background(palette.Panel).Foreground(palette.FooterKey).Bold(true)
|
||||||
spacer := lipgloss.NewStyle().Width(contentWidth).Background(palette.Panel).Render(" ")
|
spacer := lipgloss.NewStyle().Width(contentWidth).Background(palette.Panel).Render(" ")
|
||||||
|
|
||||||
sectionColors := []lipgloss.Color{palette.Accent, palette.Warning, palette.FooterKey, palette.Danger}
|
|
||||||
keyColStyle := lipgloss.NewStyle().Width(24).Background(palette.Panel).Foreground(palette.FooterKey).Bold(true)
|
keyColStyle := lipgloss.NewStyle().Width(24).Background(palette.Panel).Foreground(palette.FooterKey).Bold(true)
|
||||||
descColStyle := lipgloss.NewStyle().Background(palette.Panel).Foreground(palette.Text)
|
descColStyle := lipgloss.NewStyle().Background(palette.Panel).Foreground(palette.Text)
|
||||||
|
|
||||||
|
|
@ -1618,7 +1617,6 @@ func renderHelpModal(modal modalState, palette theme.Palette, width int) string
|
||||||
BorderBackground(palette.Panel)
|
BorderBackground(palette.Panel)
|
||||||
|
|
||||||
lines := []string{titleStyle.Render(modal.title), spacer}
|
lines := []string{titleStyle.Render(modal.title), spacer}
|
||||||
sectionIdx := 0
|
|
||||||
for _, raw := range strings.Split(modal.body, "\n") {
|
for _, raw := range strings.Split(modal.body, "\n") {
|
||||||
trimmed := strings.TrimSpace(raw)
|
trimmed := strings.TrimSpace(raw)
|
||||||
if trimmed == "" {
|
if trimmed == "" {
|
||||||
|
|
@ -1642,8 +1640,7 @@ func renderHelpModal(modal modalState, palette theme.Palette, width int) string
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionColor := sectionColorForHeader(trimmed, sectionIdx, sectionColors, palette)
|
sectionColor := sectionColorForHeader(trimmed, palette)
|
||||||
sectionIdx++
|
|
||||||
header := lipgloss.NewStyle().
|
header := lipgloss.NewStyle().
|
||||||
Width(contentWidth).
|
Width(contentWidth).
|
||||||
Background(palette.Panel).
|
Background(palette.Panel).
|
||||||
|
|
@ -1674,18 +1671,18 @@ func splitHelpItem(raw string) (string, string) {
|
||||||
return value, ""
|
return value, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func sectionColorForHeader(header string, idx int, fallback []lipgloss.Color, palette theme.Palette) lipgloss.Color {
|
func sectionColorForHeader(header string, palette theme.Palette) lipgloss.Color {
|
||||||
switch header {
|
switch header {
|
||||||
case "Navigation":
|
case "Navigation":
|
||||||
return palette.Folder
|
return palette.HelpNav
|
||||||
case "View and Panels":
|
case "View and Panels":
|
||||||
return fallback[1]
|
return palette.HelpPanels
|
||||||
case "Dialogs and Transfers":
|
case "Dialogs and Transfers":
|
||||||
return palette.BinaryFile
|
return palette.HelpDialogs
|
||||||
case "Mouse":
|
case "Mouse":
|
||||||
return fallback[3]
|
return palette.HelpMouse
|
||||||
default:
|
default:
|
||||||
return fallback[idx%len(fallback)]
|
return palette.Accent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1749,8 +1746,8 @@ func renderProgressBar(ratio float64, width int, palette theme.Palette) string {
|
||||||
filled = 0
|
filled = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
bar := lipgloss.NewStyle().Foreground(palette.Accent).Render(strings.Repeat("█", filled))
|
bar := lipgloss.NewStyle().Foreground(palette.ProgressFill).Render(strings.Repeat("█", filled))
|
||||||
rest := lipgloss.NewStyle().Foreground(palette.Border).Render(strings.Repeat("░", width-filled))
|
rest := lipgloss.NewStyle().Foreground(palette.ProgressEmpty).Render(strings.Repeat("░", width-filled))
|
||||||
percent := fmt.Sprintf(" %3.0f%%", ratio*100)
|
percent := fmt.Sprintf(" %3.0f%%", ratio*100)
|
||||||
return bar + rest + percent
|
return bar + rest + percent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ func renderPaneHeader(pane BrowserPane, cfg config.Config, palette theme.Palette
|
||||||
Foreground(palette.Text).
|
Foreground(palette.Text).
|
||||||
Bold(active)
|
Bold(active)
|
||||||
if active {
|
if active {
|
||||||
pathStyle = pathStyle.Foreground(palette.TextFile)
|
pathStyle = pathStyle.Foreground(palette.ActivePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lipgloss.NewStyle().
|
return lipgloss.NewStyle().
|
||||||
|
|
@ -292,11 +292,11 @@ func renderEntryRow(entry vfs.Entry, cfg config.Config, width int, selected bool
|
||||||
rowBackground := baseBackground
|
rowBackground := baseBackground
|
||||||
switch {
|
switch {
|
||||||
case marked:
|
case marked:
|
||||||
rowBackground = palette.Danger
|
rowBackground = palette.Marked
|
||||||
case selected:
|
case selected:
|
||||||
rowBackground = palette.Selection
|
rowBackground = palette.Selection
|
||||||
case hovered:
|
case hovered:
|
||||||
rowBackground = palette.PanelElevated
|
rowBackground = palette.Hover
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := make([]string, 0, len(columns))
|
parts := make([]string, 0, len(columns))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue