feat: Theme loading and metadata (#13)
commitc92f17c167Author: Minecon724 <minecon724@noreply.git.m724.eu> Date: Sat Sep 13 10:46:57 2025 +0000 Display everything in the UI commitfa533b8ba5Author: Minecon724 <minecon724@noreply.git.m724.eu> Date: Sat Sep 13 10:46:40 2025 +0000 Add theme loader commitadbd5af8d1Author: Minecon724 <minecon724@noreply.git.m724.eu> Date: Sat Sep 13 10:17:00 2025 +0000 Fix webpack commit933ad0b23aAuthor: Minecon724 <minecon724@noreply.git.m724.eu> Date: Sat Sep 13 10:16:51 2025 +0000 Add metadata to builtin themes commit1b8aa2fc35Author: Minecon724 <minecon724@noreply.git.m724.eu> Date: Sat Sep 13 10:16:38 2025 +0000 Remove accessibility themes
This commit is contained in:
parent
4d10f6261d
commit
93446a85af
21 changed files with 373 additions and 82 deletions
|
|
@ -16,6 +16,7 @@ import (
|
||||||
|
|
||||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||||
|
|
||||||
|
"forgejo.org/models/theme"
|
||||||
"forgejo.org/modules/container"
|
"forgejo.org/modules/container"
|
||||||
"forgejo.org/modules/graceful"
|
"forgejo.org/modules/graceful"
|
||||||
"forgejo.org/modules/log"
|
"forgejo.org/modules/log"
|
||||||
|
|
@ -217,6 +218,13 @@ func serveInstalled(_ context.Context, ctx *cli.Command) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load themes
|
||||||
|
themes, tlerr := theme.GetThemeIds()
|
||||||
|
if (tlerr != nil) {
|
||||||
|
log.Error("Failed to load themes: ", tlerr)
|
||||||
|
}
|
||||||
|
setting.UI.Themes = themes
|
||||||
|
|
||||||
// Set up Chi routes
|
// Set up Chi routes
|
||||||
webRoutes := routers.NormalRoutes()
|
webRoutes := routers.NormalRoutes()
|
||||||
err := listen(webRoutes, true)
|
err := listen(webRoutes, true)
|
||||||
|
|
|
||||||
107
models/theme/theme_loader.go
Normal file
107
models/theme/theme_loader.go
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
package theme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"forgejo.org/modules/assetfs"
|
||||||
|
"forgejo.org/modules/log"
|
||||||
|
"forgejo.org/modules/public"
|
||||||
|
|
||||||
|
"golang.org/x/sync/singleflight"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
themeFilenamePrefix = "theme-"
|
||||||
|
themeFilenameSuffix = ".css"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
group singleflight.Group
|
||||||
|
assetFs *assetfs.LayeredFS
|
||||||
|
loaded bool
|
||||||
|
loadedThemes map[string]Theme // this static variable feels suspicious
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetThemeIds() ([]string, error) {
|
||||||
|
themes, err := GetThemes()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices.Collect(maps.Keys(themes)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetThemes gets the installed Themes.
|
||||||
|
//
|
||||||
|
// It doesn't install them to settings.UI.Themes.
|
||||||
|
func GetThemes() (map[string]Theme, error) {
|
||||||
|
if loaded {
|
||||||
|
return loadedThemes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if assetFs == nil {
|
||||||
|
assetFs = public.AssetFS()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err, _ := group.Do("load-themes", func() (any, error) {
|
||||||
|
_themes, err := loadThemes(assetFs)
|
||||||
|
|
||||||
|
log.Info("Loaded %d themes", len(_themes))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Retry on error TODO might want to improve that
|
||||||
|
group.Forget("load-themes")
|
||||||
|
} else {
|
||||||
|
loadedThemes = _themes
|
||||||
|
loaded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
})
|
||||||
|
|
||||||
|
return loadedThemes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadThemes scans for and loads themes from an asset filesystem.
|
||||||
|
//
|
||||||
|
// Returns: a map of theme ID: theme metadata.
|
||||||
|
func loadThemes(assetFs *assetfs.LayeredFS) (map[string]Theme, error) {
|
||||||
|
entries, err := assetFs.ListFiles("assets/css")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
themes := make(map[string]Theme)
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !(strings.HasPrefix(entry, themeFilenamePrefix) && strings.HasSuffix(entry, themeFilenameSuffix)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id := entry[len(themeFilenamePrefix) : len(entry)-len(themeFilenameSuffix)]
|
||||||
|
|
||||||
|
theme := Theme{
|
||||||
|
ID: id,
|
||||||
|
Name: getFriendlyThemeName(id),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := loadThemeMeta(assetFs, entry, &theme); err != nil {
|
||||||
|
log.Warn("Failed to load meta of theme %s: %s", entry, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
themes[id] = theme
|
||||||
|
|
||||||
|
log.Debug("Found theme: %s (%s)", theme.Name, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(themes) == 0 {
|
||||||
|
return nil, fmt.Errorf("no themes found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return themes, nil
|
||||||
|
}
|
||||||
108
models/theme/theme_meta.go
Normal file
108
models/theme/theme_meta.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package theme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"forgejo.org/modules/assetfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The Theme struct represents the metadata of a theme. Most fields are allowed to be null / blank.
|
||||||
|
type Theme struct {
|
||||||
|
// ID is the theme id, e.g., forgejo-dark
|
||||||
|
ID string
|
||||||
|
// Name is the theme's readable name, e.g., Forgejo. Note that the scheme is excluded in favor of the Scheme property.
|
||||||
|
Name string
|
||||||
|
// Family is the theme's family, e.g. Catppuccin.
|
||||||
|
Family string
|
||||||
|
// Author is the theme's author. It may be an invididual, a group, or an entity.
|
||||||
|
Author string
|
||||||
|
// Url is the URL to the theme's website, e.g. https://forgejo.org.
|
||||||
|
Url string
|
||||||
|
// Description is the theme's short description.
|
||||||
|
Description string
|
||||||
|
// Scheme is the theme's color scheme. It must be either: dark, light, or auto.
|
||||||
|
Scheme string
|
||||||
|
// Recommended means the theme is guaranteed to be bug-free, usually a first-party theme.
|
||||||
|
Recommended bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadThemeMeta loads theme metadata from a file in a layered FS.
|
||||||
|
//
|
||||||
|
// Theme ID is expected to be prefilled. The metadata will be assigned to the passed `theme` object.
|
||||||
|
func loadThemeMeta(assetFs *assetfs.LayeredFS, filename string, theme *Theme) error {
|
||||||
|
file, err := assetFs.Open("assets/css/" + filename)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
kv, err := scanThemeForKeyValueMetaMap(file, theme.ID) // ID is expected to be prefilled
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error scanning theme for meta: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
theme.Name = kv["name"]
|
||||||
|
theme.Family = kv["family"]
|
||||||
|
theme.Author = kv["author"]
|
||||||
|
theme.Url = kv["url"]
|
||||||
|
theme.Description = kv["description"]
|
||||||
|
theme.Scheme = strings.ToLower(kv["scheme"])
|
||||||
|
theme.Recommended = kv["recommended"] == "yes"
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanThemeForKeyValueMetaMap finds raw key-value metadata in an open file.
|
||||||
|
func scanThemeForKeyValueMetaMap(file io.Reader, id string) (map[string]string, error) {
|
||||||
|
var kv = make(map[string]string)
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
// webpack adds headers
|
||||||
|
for scanner.Scan() {
|
||||||
|
if strings.TrimSpace(scanner.Text()) == "/* theme "+id {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("theme has no meta")
|
||||||
|
}
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
|
||||||
|
if len(parts) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.ToLower(parts[0])
|
||||||
|
|
||||||
|
if key == "*/" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) == 1 {
|
||||||
|
kv[key] = "yes"
|
||||||
|
} else {
|
||||||
|
kv[key] = strings.Join(parts[1:], " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("scanner error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv, nil
|
||||||
|
}
|
||||||
14
models/theme/theme_name.go
Normal file
14
models/theme/theme_name.go
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package theme
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// getFriendlyThemeName converts an raw theme name to a friendly, readable name.
|
||||||
|
// Example: forgejo-dark -> Forgejo Dark, catppuccin-maroon-auto -> Catppuccin Maroon Auto
|
||||||
|
func getFriendlyThemeName(themeName string) string {
|
||||||
|
themeName = strings.ReplaceAll(themeName, "-", " ")
|
||||||
|
|
||||||
|
themeName = strings.ToLower(themeName)
|
||||||
|
themeName = strings.Title(themeName)
|
||||||
|
|
||||||
|
return themeName
|
||||||
|
}
|
||||||
|
|
@ -812,6 +812,10 @@ manage_themes = Default theme
|
||||||
manage_openid = OpenID addresses
|
manage_openid = OpenID addresses
|
||||||
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
|
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
|
||||||
theme_desc = This theme will be used for the web interface when you are logged in.
|
theme_desc = This theme will be used for the web interface when you are logged in.
|
||||||
|
theme_recommended = Recommended
|
||||||
|
theme_light = Light
|
||||||
|
theme_dark = Dark
|
||||||
|
theme_auto = Auto (per system)
|
||||||
primary = Primary
|
primary = Primary
|
||||||
activated = Activated
|
activated = Activated
|
||||||
requires_activation = Requires activation
|
requires_activation = Requires activation
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"forgejo.org/models/db"
|
"forgejo.org/models/db"
|
||||||
"forgejo.org/models/organization"
|
"forgejo.org/models/organization"
|
||||||
repo_model "forgejo.org/models/repo"
|
repo_model "forgejo.org/models/repo"
|
||||||
|
"forgejo.org/models/theme"
|
||||||
user_model "forgejo.org/models/user"
|
user_model "forgejo.org/models/user"
|
||||||
"forgejo.org/modules/base"
|
"forgejo.org/modules/base"
|
||||||
"forgejo.org/modules/log"
|
"forgejo.org/modules/log"
|
||||||
|
|
@ -335,15 +336,15 @@ func Repos(ctx *context.Context) {
|
||||||
func Appearance(ctx *context.Context) {
|
func Appearance(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings.appearance")
|
ctx.Data["Title"] = ctx.Tr("settings.appearance")
|
||||||
ctx.Data["PageIsSettingsAppearance"] = true
|
ctx.Data["PageIsSettingsAppearance"] = true
|
||||||
ctx.Data["AllThemes"] = setting.UI.Themes
|
|
||||||
ctx.Data["ThemeName"] = func(themeName string) string {
|
themes, err := theme.GetThemes()
|
||||||
fullThemeName := "themes.names." + themeName
|
if err != nil {
|
||||||
if ctx.Locale.HasKey(fullThemeName) {
|
ctx.ServerError("Failed to load themes", err)
|
||||||
return ctx.Locale.TrString(fullThemeName)
|
return
|
||||||
}
|
|
||||||
return themeName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.Data["AllThemes"] = themes
|
||||||
|
|
||||||
var hiddenCommentTypes *big.Int
|
var hiddenCommentTypes *big.Int
|
||||||
val, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyHiddenCommentTypes)
|
val, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyHiddenCommentTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -7,37 +7,64 @@
|
||||||
</h4>
|
</h4>
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
<div class="ui email list">
|
<div class="ui email list">
|
||||||
<div class="item">
|
{{$theme := index .AllThemes $.SignedUser.Theme}}
|
||||||
{{ctx.Locale.Tr "settings.theme_desc"}}
|
{{if $theme.Author}}
|
||||||
</div>
|
<div class="item">
|
||||||
|
<p>
|
||||||
<form class="ui form" action="{{.Link}}/theme" method="post">
|
<strong>{{$theme.Name}}</strong>
|
||||||
{{.CsrfTokenHtml}}
|
by <em><a href="{{$theme.Url}}">{{$theme.Author}}</a></em> {{if $theme.Recommended}}<span data-tooltip-content="{{ctx.Locale.Tr "settings.theme_recommended"}}">{{svg "octicon-verified" 16 "dropdown icon"}}</span>{{end}}
|
||||||
<div class="field">
|
</p>
|
||||||
<label for="ui">{{ctx.Locale.Tr "settings.ui"}}</label>
|
{{if $theme.Description}}
|
||||||
<div class="ui selection dropdown" id="ui">
|
<p><em>"{{$theme.Description}}"</em></p>
|
||||||
<input name="theme" type="hidden" value="{{.SignedUser.Theme}}">
|
{{end}}
|
||||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
|
||||||
<div class="text">
|
|
||||||
{{- range $i,$a := .AllThemes -}}
|
|
||||||
{{if eq $.SignedUser.Theme $a}}{{call $.ThemeName $a}}{{end}}
|
|
||||||
{{- end -}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="menu">
|
|
||||||
{{range $i,$a := .AllThemes}}
|
|
||||||
<div class="item{{if eq $.SignedUser.Theme $a}} active selected{{end}}" data-value="{{$a}}">
|
|
||||||
{{call $.ThemeName $a}}
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="item tw-mb-auto">
|
||||||
<button class="ui primary button">{{ctx.Locale.Tr "settings.update_theme"}}</button>
|
{{ctx.Locale.Tr "settings.theme_desc"}}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
{{else}}
|
||||||
|
<div class="item tw-mb-4">
|
||||||
|
{{ctx.Locale.Tr "settings.theme_desc"}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<form class="ui form" action="{{.Link}}/theme" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<div class="field">
|
||||||
|
<label for="ui">{{ctx.Locale.Tr "settings.ui"}}</label>
|
||||||
|
<div class="ui selection dropdown" id="ui">
|
||||||
|
<input name="theme" type="hidden" value="{{.SignedUser.Theme}}">
|
||||||
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
|
<div class="text">
|
||||||
|
{{- range $i,$a := .AllThemes -}}
|
||||||
|
{{if eq $.SignedUser.Theme $i}}{{$a.Name}}{{end}}
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="menu">
|
||||||
|
{{range $i,$a := .AllThemes}}
|
||||||
|
<div class="item{{if eq $.SignedUser.Theme $i}} active selected{{end}} tw-flex" data-value="{{$i}}">
|
||||||
|
{{$a.Name}}
|
||||||
|
{{if $a.Recommended}}
|
||||||
|
<span data-tooltip-content="{{ctx.Locale.Tr "settings.theme_recommended"}}">{{svg "octicon-verified" 16 "dropdown icon"}}</span>
|
||||||
|
{{end}}
|
||||||
|
{{if eq $a.Scheme "auto"}}
|
||||||
|
<span data-tooltip-content="{{ctx.Locale.Tr "settings.theme_auto"}}">{{svg "octicon-sync" 16 "dropdown icon"}}</span>
|
||||||
|
{{else if eq $a.Scheme "dark"}}
|
||||||
|
<span data-tooltip-content="{{ctx.Locale.Tr "settings.theme_dark"}}">{{svg "octicon-moon" 16 "dropdown icon"}}</span>
|
||||||
|
{{else if eq $a.Scheme "light"}}
|
||||||
|
<span data-tooltip-content="{{ctx.Locale.Tr "settings.theme_light"}}">{{svg "octicon-sun" 16 "dropdown icon"}}</span>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<button class="ui primary button">{{ctx.Locale.Tr "settings.update_theme"}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
make --no-print-directory watch-frontend &
|
make --no-print-directory watch-frontend | tee >(
|
||||||
make --no-print-directory watch-backend &
|
awk -v pattern="webpack" '
|
||||||
|
$0 ~ pattern {
|
||||||
|
system("make --no-print-directory watch-backend &")
|
||||||
|
}
|
||||||
|
'
|
||||||
|
) &
|
||||||
|
|
||||||
trap 'kill $(jobs -p)' EXIT
|
trap 'kill $(jobs -p)' EXIT
|
||||||
wait
|
wait
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
@import "theme-forgejo-light-deuteranopia-protanopia.css";
|
|
||||||
@import "theme-forgejo-dark-deuteranopia-protanopia.css" (prefers-color-scheme: dark);
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
@import "theme-forgejo-light-tritanopia.css";
|
|
||||||
@import "theme-forgejo-dark-tritanopia.css" (prefers-color-scheme: dark);
|
|
||||||
|
|
@ -1,2 +1,12 @@
|
||||||
|
/* theme forgejo-auto
|
||||||
|
Name Forgejo
|
||||||
|
Family Forgejo
|
||||||
|
Author Forgejo authors
|
||||||
|
Url https://forgejo.org/
|
||||||
|
Description The fresh look of freedom.
|
||||||
|
Scheme Auto
|
||||||
|
Recommended
|
||||||
|
*/
|
||||||
|
|
||||||
@import "theme-forgejo-light.css";
|
@import "theme-forgejo-light.css";
|
||||||
@import "theme-forgejo-dark.css" (prefers-color-scheme: dark);
|
@import "theme-forgejo-dark.css" (prefers-color-scheme: dark);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
@import "./theme-forgejo-dark.css";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
/* removed rows/words: use red colors from vanilla forgejo-dark */
|
|
||||||
--color-diff-added-word-bg: #214d88;
|
|
||||||
--color-diff-added-row-border: #214d88;
|
|
||||||
--color-diff-added-row-bg: #18184f;
|
|
||||||
--color-code-bg: #0d1117;
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
@import "./theme-forgejo-dark.css";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
/* removed rows/words: use red colors from vanilla forgejo-dark */
|
|
||||||
--color-diff-added-word-bg: #214d88;
|
|
||||||
--color-diff-added-row-border: #214d88;
|
|
||||||
--color-diff-added-row-bg: #152846;
|
|
||||||
--color-code-bg: #0d1117;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,13 @@
|
||||||
|
/* theme forgejo-dark
|
||||||
|
Name Forgejo
|
||||||
|
Family Forgejo
|
||||||
|
Author Forgejo authors
|
||||||
|
Url https://forgejo.org/
|
||||||
|
Description The fresh look of freedom.
|
||||||
|
Scheme Dark
|
||||||
|
Recommended
|
||||||
|
*/
|
||||||
|
|
||||||
@import "../chroma/dark.css";
|
@import "../chroma/dark.css";
|
||||||
@import "../codemirror/dark.css";
|
@import "../codemirror/dark.css";
|
||||||
@import "../markup/dark.css";
|
@import "../markup/dark.css";
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
@import "./theme-forgejo-light.css";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--color-diff-removed-word-bg: #c8c850;
|
|
||||||
--color-diff-removed-row-border: #c8c850;
|
|
||||||
--color-diff-removed-row-bg: #ffecc4;
|
|
||||||
--color-diff-added-word-bg: #b8c0ff;
|
|
||||||
--color-diff-added-row-border: #b8c0ff;
|
|
||||||
--color-diff-added-row-bg: #e0e0ff;
|
|
||||||
--color-code-bg: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
@import "./theme-forgejo-light.css";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--color-diff-removed-word-bg: #ffb8c0;
|
|
||||||
--color-diff-removed-row-border: #ffb8c0;
|
|
||||||
--color-diff-removed-row-bg: #ffd8d8;
|
|
||||||
--color-diff-added-word-bg: #b8c0ff;
|
|
||||||
--color-diff-added-row-border: #b8c0ff;
|
|
||||||
--color-diff-added-row-bg: #c0e8ff;
|
|
||||||
--color-code-bg: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,13 @@
|
||||||
|
/* theme forgejo-light
|
||||||
|
Name Forgejo
|
||||||
|
Family Forgejo
|
||||||
|
Author Forgejo authors
|
||||||
|
Url https://forgejo.org/
|
||||||
|
Description The fresh look of freedom.
|
||||||
|
Scheme Light
|
||||||
|
Recommended
|
||||||
|
*/
|
||||||
|
|
||||||
@import "../chroma/light.css";
|
@import "../chroma/light.css";
|
||||||
@import "../codemirror/light.css";
|
@import "../codemirror/light.css";
|
||||||
@import "../markup/light.css";
|
@import "../markup/light.css";
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,12 @@
|
||||||
|
/* theme gitea-auto
|
||||||
|
Name Gitea
|
||||||
|
Family Gitea
|
||||||
|
Author The Gitea Authors
|
||||||
|
Url https://github.com/go-gitea/gitea
|
||||||
|
Description The OG style.
|
||||||
|
Scheme Auto
|
||||||
|
Recommended
|
||||||
|
*/
|
||||||
|
|
||||||
@import "./theme-gitea-light.css" (prefers-color-scheme: light);
|
@import "./theme-gitea-light.css" (prefers-color-scheme: light);
|
||||||
@import "./theme-gitea-dark.css" (prefers-color-scheme: dark);
|
@import "./theme-gitea-dark.css" (prefers-color-scheme: dark);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,13 @@
|
||||||
|
/* theme gitea-dark
|
||||||
|
Name Gitea
|
||||||
|
Family Gitea
|
||||||
|
Author The Gitea Authors
|
||||||
|
Url https://github.com/go-gitea/gitea
|
||||||
|
Description The OG style.
|
||||||
|
Scheme Dark
|
||||||
|
Recommended
|
||||||
|
*/
|
||||||
|
|
||||||
@import "../chroma/dark.css";
|
@import "../chroma/dark.css";
|
||||||
@import "../codemirror/dark.css";
|
@import "../codemirror/dark.css";
|
||||||
@import "../markup/dark.css";
|
@import "../markup/dark.css";
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,13 @@
|
||||||
|
/* theme gitea-light
|
||||||
|
Name Gitea
|
||||||
|
Family Gitea
|
||||||
|
Author The Gitea Authors
|
||||||
|
Url https://github.com/go-gitea/gitea
|
||||||
|
Description The OG style.
|
||||||
|
Scheme Light
|
||||||
|
Recommended
|
||||||
|
*/
|
||||||
|
|
||||||
@import "../chroma/light.css";
|
@import "../chroma/light.css";
|
||||||
@import "../codemirror/light.css";
|
@import "../codemirror/light.css";
|
||||||
@import "../markup/light.css";
|
@import "../markup/light.css";
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ export default {
|
||||||
minify: true,
|
minify: true,
|
||||||
css: true,
|
css: true,
|
||||||
legalComments: 'none',
|
legalComments: 'none',
|
||||||
|
exclude: [new RegExp(`css\\/(${Object.keys(themes).join('|')})\\.css$`)],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue