feat(flexhub-main): ship flexhub-main theme + assets + custom chrome
flexhub-main is now DEFAULT_THEME. Magenta -> blue cyberpunk paint job on a #5b6cff periwinkle primary, JetBrains Mono site-wide, pixel-glitch "FLEXHUB" wordmark with 0.35s chromatic shake on hover, bracket-frame hover on nav items, panel-fill on the bordered Sign-in pill, gradient 1px under-line. Homepage now ships a 4-column agent grid with constant gradient stroke, outlined-letter markers, and -3px lift + dual outer glow on hover. What's in this commit - 3 themes (CSS): theme-flexhub-main, theme-flexhub-og, theme-monochrome - Homepage + navbar + footer templates (image-swap logo, stripped lang switch) - Full favicon set + apple-touch + .ico + PNG-wrapped SVG (works everywhere) - Pixel-glitch home wordmark + transparent-bg navbar wordmark - README rewrite covering current state English-only locked ([i18n] LANGS = en-US). All credit s8n. The flex is the work.
151
README.md
|
|
@ -2,91 +2,110 @@
|
|||
|
||||
> Where I flex my projects, code, and stuff. A place to flex.
|
||||
|
||||
FlexHub is the brand wrapped around the operator's self-hosted Forgejo
|
||||
instance. Same git host, same backing store — different paint job.
|
||||
FlexHub is a self-hosted git platform with a fully custom paint job —
|
||||
magenta → blue cyberpunk. Custom theme. Custom homepage. Custom
|
||||
favicon. Custom hover animations. Custom everything.
|
||||
|
||||
- **Canonical URL:** <https://git.s8n.ru/> (admin / push / clone)
|
||||
- **FlexHub-branded entry:** <https://flexhub.s8n.ru/> (public showcase)
|
||||
|
||||
Both hostnames hit the same Forgejo container on `nullstone`. Repos,
|
||||
PATs, SSH keys, issues — all identical. Login sessions are independent
|
||||
(separate cookies per hostname).
|
||||
Both hostnames hit the same instance on `nullstone`. Repos, PATs, SSH
|
||||
keys, issues — all identical. Login sessions are independent (separate
|
||||
cookies per hostname).
|
||||
|
||||
This repository holds the customisations that turn vanilla Forgejo into
|
||||
FlexHub: the homepage template, the theme CSS, the logo set, and the
|
||||
deploy notes that wire it all together.
|
||||
Built and operated by **[s8n](https://git.s8n.ru/s8n)**.
|
||||
|
||||
---
|
||||
|
||||
## The flex
|
||||
|
||||
FlexHub ships **three** themes:
|
||||
|
||||
| Theme | Status | Vibe |
|
||||
| --------------- | --------------------------------- | ------------------------------------------------------------------- |
|
||||
| `flexhub-main` | **default** — currently shipping | Magenta → blue gradient. Pixel-glitch wordmark. Bracket-frame nav. |
|
||||
| `flexhub-og` | available via dropdown | Original orange/black/white. The original "FlexHub" paint. |
|
||||
| `monochrome` | available via dropdown | Pure black + white. Security/terminal aesthetic. |
|
||||
|
||||
### What ships in `flexhub-main`
|
||||
|
||||
- **Wordmark** — pixel-glitch "FLEXHUB" PNG with magenta + cyan
|
||||
chromatic ghosts and a 0.35s shake on navbar hover.
|
||||
- **Favicon** — `FH` square favicon (PNG-wrapped SVG so it works
|
||||
everywhere), apple-touch-icon, .ico and 16/32px PNGs.
|
||||
- **Type** — JetBrains Mono everywhere, Major Mono Display for accent
|
||||
text and the navbar bracket-frame.
|
||||
- **Hover** — bracket-frame `[ label ]` on all navbar items in
|
||||
magenta/cyan, panel-fill on the bordered Sign-in pill.
|
||||
- **Homepage** — pixel-glitch wordmark, gradient subtitle, and a
|
||||
4-column "agent grid" of cards with constant gradient stroke,
|
||||
outlined-letter markers, and a -3px lift + dual outer glow on hover.
|
||||
- **Color** — primary `#5b6cff` periwinkle blue-violet (HSL 234°).
|
||||
Gradient stops `#d423ff → #6a3aff → #22b8ff`.
|
||||
|
||||
### Locale
|
||||
|
||||
English-only. Language dropdown removed from the footer.
|
||||
`[i18n] LANGS = en-US` locked, so `?lang=xx` query is a no-op.
|
||||
|
||||
---
|
||||
|
||||
## What's in this repo
|
||||
|
||||
| Path | Purpose |
|
||||
| ------------------------------------------------ | ------------------------------------------------------------------------ |
|
||||
| `templates/home.tmpl` | FlexHub homepage. Wordmark + tagline + 4-card "agent grid" feature list. |
|
||||
| `public/assets/css/theme-flexhub.css` *(planned)* | The orange/black/white theme. `DEFAULT_THEME = flexhub` in `app.ini`. |
|
||||
| `public/assets/img/` *(planned)* | Logo + wordmark + favicons (`logo.svg`, `flexhub-wordmark.png`, etc). |
|
||||
| `docs/DEPLOY.md` | How to roll a change out to the live `nullstone` Forgejo container. |
|
||||
| `LICENSE` | MIT. |
|
||||
```
|
||||
flexhub/
|
||||
├── README.md ← this file
|
||||
├── LICENSE ← MIT
|
||||
├── docs/
|
||||
│ └── DEPLOY.md ← how to roll changes to nullstone
|
||||
├── templates/
|
||||
│ ├── home.tmpl ← FlexHub homepage
|
||||
│ └── base/
|
||||
│ ├── head_navbar.tmpl ← navbar with image-swap logo
|
||||
│ └── footer_content.tmpl ← stripped footer (no lang switch)
|
||||
├── public/assets/
|
||||
│ ├── css/
|
||||
│ │ ├── theme-flexhub-main.css ← default, magenta→blue
|
||||
│ │ ├── theme-flexhub-og.css ← original orange paint
|
||||
│ │ └── theme-monochrome.css ← pure black + white
|
||||
│ └── img/
|
||||
│ ├── flexhub-main-favicon.{ico,svg,png} ← FH gradient favicons
|
||||
│ ├── flexhub-main-favicon-16.png
|
||||
│ ├── flexhub-main-favicon-32.png
|
||||
│ ├── flexhub-main-favicon-512.png
|
||||
│ ├── flexhub-main-apple-touch.png
|
||||
│ ├── flexhub-main-nav-wordmark.png ← transparent-bg navbar logo
|
||||
│ ├── flexhub-main-home-wordmark.png ← pixel-glitch homepage wordmark
|
||||
│ ├── favicon.{ico,svg,png} ← active globals (= main copies)
|
||||
│ ├── apple-touch-icon.png ← active global
|
||||
│ ├── favicon-{16,32}.png ← active globals
|
||||
│ ├── flexhub-wordmark.png ← og-theme wordmark
|
||||
│ └── logo.{png,svg} ← og-theme logo
|
||||
└── .forgejo/workflows/secret-scan.yml ← gitleaks scan
|
||||
```
|
||||
|
||||
The repo is currently template-only; assets get checked in as the
|
||||
operator pulls them out of the live Forgejo `custom/` tree. The
|
||||
authoritative copies live on `nullstone` until then.
|
||||
Anything in this repo maps 1:1 into the live `custom/` tree on
|
||||
`nullstone`. See `docs/DEPLOY.md` for the exact push flow.
|
||||
|
||||
---
|
||||
|
||||
## How it's wired up
|
||||
|
||||
Forgejo loads any file under `custom/` to override the equivalent
|
||||
built-in. On `nullstone` that directory is bind-mounted from the host:
|
||||
|
||||
```
|
||||
/home/docker/forgejo/data/gitea/ → /var/lib/gitea/ (in-container)
|
||||
└─ custom/
|
||||
├─ conf/app.ini
|
||||
├─ templates/home.tmpl ← from this repo
|
||||
├─ public/assets/css/... ← from this repo
|
||||
└─ public/assets/img/... ← from this repo
|
||||
```
|
||||
|
||||
The Forgejo container itself is described by the compose file at
|
||||
`/opt/docker/forgejo/docker-compose.yml`. Container data + customisations
|
||||
live under `/home/docker/forgejo/` because the root volume on nullstone
|
||||
is small.
|
||||
|
||||
Key settings in `app.ini` that drive the rebrand:
|
||||
## app.ini snippet
|
||||
|
||||
```ini
|
||||
APP_NAME = FlexHub
|
||||
DEFAULT_THEME = flexhub
|
||||
THEMES = forgejo-auto,forgejo-light,forgejo-dark,flexhub,veilor
|
||||
[ui]
|
||||
THEMES = monochrome,flexhub-og,flexhub-main
|
||||
DEFAULT_THEME = flexhub-main
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US
|
||||
NAMES = English
|
||||
```
|
||||
|
||||
The two hostnames are split at the Traefik layer, not Forgejo:
|
||||
|
||||
- `git.s8n.ru` — docker-provider router, gated by `no-guest@file` ACL.
|
||||
- `flexhub.s8n.ru` — file-provider router, **public**, no `no-guest`,
|
||||
rate-limited login. Service `flexhub-svc` points at `http://forgejo:3000`
|
||||
directly (avoids the file→docker provider race; see DEPLOY.md).
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
## License
|
||||
|
||||
Don't. This is the operator's personal git host — accounts are
|
||||
invite-only, registration is disabled. If you've found a real bug in
|
||||
Forgejo itself, file it upstream at
|
||||
<https://codeberg.org/forgejo/forgejo>. If you've found a typo on the
|
||||
homepage, open an issue here and the operator will get to it.
|
||||
MIT. See `LICENSE`.
|
||||
|
||||
Public read access is intentional: browse repos, clone what's MIT or
|
||||
AGPL, mirror what you like. That's the "flex" part.
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
- **Deploy + update workflow:** [`docs/DEPLOY.md`](docs/DEPLOY.md)
|
||||
- **Initial Forgejo bring-up runbook (operator-internal):**
|
||||
`_github/infra/forgejo/DEPLOY.md` in the `ai-lab` repo.
|
||||
- **ARRFLIX project** references `flexhub.s8n.ru` as a mirror — that's
|
||||
this instance.
|
||||
Built and maintained by **[s8n](https://git.s8n.ru/s8n)**. The flex is
|
||||
the work.
|
||||
|
|
|
|||
404
public/assets/css/theme-flexhub-main.css
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
@import url('/assets/css/theme-forgejo-dark.css');
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Major+Mono+Display&display=swap');
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url("/assets/font/inter-var.woff2") format("woff2-variations"),
|
||||
url("/assets/font/inter-var.woff2") format("woff2");
|
||||
}
|
||||
|
||||
/* Site-wide font override — JetBrains Mono everywhere */
|
||||
:root {
|
||||
--fonts-proportional: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
--fonts-monospace: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
html, body, .ui, .ui.menu, .ui.menu .item, .ui.button, .ui.input input,
|
||||
.ui.dropdown, .ui.dropdown .menu > .item, input, textarea, select, button {
|
||||
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* FlexHub Main — magenta -> blue gradient on black */
|
||||
|
||||
/* Primary = periwinkle blue-violet signal */
|
||||
--color-primary: #5b6cff;
|
||||
--color-primary-contrast: #ffffff;
|
||||
--color-primary-dark-1: #4f5ef3;
|
||||
--color-primary-dark-2: #3d4fe1;
|
||||
--color-primary-dark-3: #2b40c9;
|
||||
--color-primary-dark-4: #2031af;
|
||||
--color-primary-dark-5: #182694;
|
||||
--color-primary-dark-6: #111c78;
|
||||
--color-primary-dark-7: #0c145d;
|
||||
--color-primary-light-1: #7886ff;
|
||||
--color-primary-light-2: #99a3ff;
|
||||
--color-primary-light-3: #b8beff;
|
||||
--color-primary-light-4: #d2d6ff;
|
||||
--color-primary-light-5: #e0e3ff;
|
||||
--color-primary-light-6: #ebedff;
|
||||
--color-primary-light-7: #f5f6ff;
|
||||
--color-primary-alpha-10: #5b6cff1a;
|
||||
--color-primary-alpha-20: #5b6cff33;
|
||||
--color-primary-alpha-30: #5b6cff4d;
|
||||
--color-primary-alpha-40: #5b6cff66;
|
||||
--color-primary-alpha-50: #5b6cff80;
|
||||
--color-primary-alpha-60: #5b6cff99;
|
||||
--color-primary-alpha-70: #5b6cffb3;
|
||||
--color-primary-alpha-80: #5b6cffcc;
|
||||
--color-primary-alpha-90: #5b6cffe6;
|
||||
--color-primary-hover: var(--color-primary-light-1);
|
||||
--color-primary-active: var(--color-primary-dark-1);
|
||||
|
||||
/* Accent gradient stops */
|
||||
--fh-grad-from: #d423ff;
|
||||
--fh-grad-mid: #6a3aff;
|
||||
--fh-grad-to: #22b8ff;
|
||||
--fh-grad: linear-gradient(90deg, var(--fh-grad-from) 0%, var(--fh-grad-mid) 50%, var(--fh-grad-to) 100%);
|
||||
|
||||
/* Surfaces — pure black base */
|
||||
--color-body: #000000;
|
||||
--color-box-header: #0a0a0a;
|
||||
--color-box-body: #060606;
|
||||
--color-box-body-highlight: #101010;
|
||||
--color-card: #0a0a0a;
|
||||
--color-menu: #060606;
|
||||
--color-header-wrapper: #000000;
|
||||
--color-nav-bg: #000000;
|
||||
--color-secondary-nav-bg: var(--color-body);
|
||||
--color-footer: #000000;
|
||||
--color-button: #1a1a1a;
|
||||
--color-secondary-bg: #1a1a1a;
|
||||
--color-secondary: #1a1a1a;
|
||||
--color-secondary-dark-1: #222222;
|
||||
--color-secondary-dark-2: #2a2a2a;
|
||||
}
|
||||
|
||||
/* FlexHub homepage hero — L1 layout (4-in-row) */
|
||||
.flexhub-home {
|
||||
padding: 5rem 1rem 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
.flexhub-home .flexhub-text-wordmark {
|
||||
display: block;
|
||||
width: min(640px, 80vw);
|
||||
aspect-ratio: 1400 / 310;
|
||||
margin: 0 auto 1.5rem;
|
||||
background: url('/assets/img/flexhub-main-home-wordmark.png') center/contain no-repeat;
|
||||
/* hide any text children inherited from template */
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
color: transparent;
|
||||
}
|
||||
.flexhub-home .flexhub-text-wordmark .fh-flex,
|
||||
.flexhub-home .flexhub-text-wordmark::after {
|
||||
display: none !important;
|
||||
content: none !important;
|
||||
}
|
||||
.flexhub-home .tagline {
|
||||
font-size: clamp(1.1rem, 2vw, 1.4rem);
|
||||
color: var(--color-text-light);
|
||||
margin: 0 0 0.4rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
.flexhub-home .subtagline {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-light-2);
|
||||
margin: 0 0 3rem;
|
||||
font-style: italic;
|
||||
background: var(--fh-grad);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
display: inline-block;
|
||||
}
|
||||
.flexhub-home .agent-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 1.25rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem 4rem;
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.flexhub-home .agent-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
/* V2 — neon outline (constant gradient stroke + dual glow on hover) */
|
||||
.flexhub-home .agent-card {
|
||||
position: relative;
|
||||
padding: 18px 18px 20px;
|
||||
border-radius: 14px;
|
||||
background:
|
||||
linear-gradient(#0a0612, #0a0612) padding-box,
|
||||
var(--fh-grad) border-box;
|
||||
border: 1px solid transparent;
|
||||
text-align: left;
|
||||
transition: transform .25s ease, box-shadow .25s ease;
|
||||
}
|
||||
.flexhub-home .agent-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow:
|
||||
0 8px 28px -8px #d423ff66,
|
||||
0 8px 28px -8px #22b8ff55;
|
||||
}
|
||||
|
||||
/* Unified outlined-letter marker */
|
||||
.flexhub-home .agent-card .marker {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 9px;
|
||||
background:
|
||||
linear-gradient(#0a0612, #0a0612) padding-box,
|
||||
linear-gradient(180deg, var(--fh-grad-from), var(--fh-grad-mid), var(--fh-grad-to)) border-box;
|
||||
border: 1.5px solid transparent;
|
||||
font-family: "JetBrains Mono", ui-monospace, monospace;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
margin-bottom: 14px;
|
||||
transition: filter .25s ease;
|
||||
}
|
||||
.flexhub-home .agent-card .marker > span {
|
||||
background: linear-gradient(180deg, var(--fh-grad-from), var(--fh-grad-mid), var(--fh-grad-to));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
font-weight: 700;
|
||||
font-family: "JetBrains Mono", ui-monospace, monospace;
|
||||
}
|
||||
.flexhub-home .agent-card:hover .marker {
|
||||
filter: drop-shadow(0 0 6px #d423ff66) drop-shadow(0 0 10px #22b8ff44);
|
||||
}
|
||||
.flexhub-home .agent-card h3 {
|
||||
margin: 0 0 0.4rem;
|
||||
color: #ffffff;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.flexhub-home .agent-card p {
|
||||
margin: 0;
|
||||
color: var(--color-text-light);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
/* Kill default menu-item hover bg on the logo link */
|
||||
#navbar-logo,
|
||||
#navbar-logo:hover,
|
||||
#navbar-logo:focus,
|
||||
#navbar-logo.active,
|
||||
.navbar-left .item#navbar-logo:hover {
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Bracket-frame hover on navbar items except logo + sign-in */
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]) {
|
||||
position: relative;
|
||||
padding-left: 18px !important;
|
||||
padding-right: 18px !important;
|
||||
background: transparent !important;
|
||||
transition: color .2s ease, background .2s ease, border-color .2s ease;
|
||||
}
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"])::before,
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"])::after {
|
||||
font-family: "Major Mono Display", "JetBrains Mono", monospace !important;
|
||||
font-weight: 400 !important;
|
||||
display: inline-block !important;
|
||||
visibility: visible !important;
|
||||
position: absolute !important;
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%) translateX(0);
|
||||
opacity: 0;
|
||||
font-size: 16px !important;
|
||||
line-height: 1 !important;
|
||||
width: auto !important;
|
||||
min-width: 8px !important;
|
||||
height: auto !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
background: transparent !important;
|
||||
background-color: transparent !important;
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
overflow: visible !important;
|
||||
transition: opacity .2s ease, transform .25s cubic-bezier(.2,.7,.1,1.2);
|
||||
pointer-events: none;
|
||||
z-index: 2 !important;
|
||||
}
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"])::before {
|
||||
content: "[" !important;
|
||||
left: 4px !important;
|
||||
right: auto !important;
|
||||
color: var(--fh-grad-from) !important;
|
||||
}
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"])::after {
|
||||
content: "]" !important;
|
||||
right: 4px !important;
|
||||
left: auto !important;
|
||||
color: var(--fh-grad-to) !important;
|
||||
}
|
||||
/* Defeat Semantic UI's .ui.secondary.menu .item:before { display:none } and
|
||||
.ui.menu .item:before { width:1px; background:rgba(...) } divider rule. */
|
||||
.full.height #navbar .item:not(#navbar-logo):not([href*="/user/login"])::before,
|
||||
.ui.secondary.menu#navbar .item:not(#navbar-logo):not([href*="/user/login"])::before,
|
||||
.ui.menu#navbar .item:not(#navbar-logo):not([href*="/user/login"])::before {
|
||||
content: "[" !important;
|
||||
display: inline-block !important;
|
||||
visibility: visible !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
background: transparent !important;
|
||||
background-color: transparent !important;
|
||||
color: var(--fh-grad-from) !important;
|
||||
left: 4px !important;
|
||||
right: auto !important;
|
||||
top: 50% !important;
|
||||
font-size: 16px !important;
|
||||
font-family: "Major Mono Display","JetBrains Mono",monospace !important;
|
||||
}
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]):hover,
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]).active {
|
||||
color: #ffffff !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]):hover::before,
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]).active::before { opacity: 1; transform: translateY(-50%) translateX(-4px); }
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]):hover::after,
|
||||
#navbar .item:not(#navbar-logo):not([href*="/user/login"]).active::after { opacity: 1; transform: translateY(-50%) translateX( 4px); }
|
||||
|
||||
/* Sign-in — bordered pill + variant-D panel-fill hover + [← arrow text */
|
||||
#navbar .item[href*="/user/login"] {
|
||||
position: relative;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 6px !important;
|
||||
padding: 6px 12px !important;
|
||||
margin: 0 4px;
|
||||
border: 1px solid #ffffff22 !important;
|
||||
border-radius: 6px !important;
|
||||
color: #e9e6f7 !important;
|
||||
background-color: transparent !important;
|
||||
background-image: linear-gradient(90deg, #d423ff26, #22b8ff26) !important;
|
||||
background-size: 0% 100% !important;
|
||||
background-position: left center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
overflow: hidden;
|
||||
transition: background-size .35s cubic-bezier(.2,.7,.1,1), border-color .2s, color .2s;
|
||||
}
|
||||
#navbar .item[href*="/user/login"]:hover {
|
||||
background-size: 100% 100% !important;
|
||||
border-color: #ffffff14 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
/* hide built-in octicon, replace with [← in Major Mono */
|
||||
#navbar .item[href*="/user/login"] .svg.octicon-sign-in,
|
||||
#navbar .item[href*="/user/login"] svg.octicon-sign-in,
|
||||
#navbar .item[href*="/user/login"] > svg { display: none !important; }
|
||||
#navbar .item[href*="/user/login"]::before {
|
||||
content: "[←" !important;
|
||||
display: inline-block !important;
|
||||
visibility: visible !important;
|
||||
font-family: "Major Mono Display","JetBrains Mono",monospace !important;
|
||||
font-weight: 400 !important;
|
||||
color: #9d97b8 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 1 !important;
|
||||
position: static !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
background: transparent !important;
|
||||
background-color: transparent !important;
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
pointer-events: none;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
#navbar .item[href*="/user/login"]:hover::before {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
/* gradient 1px under-line on hover */
|
||||
#navbar .item[href*="/user/login"]::after {
|
||||
content: "" !important;
|
||||
position: absolute !important;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
height: 1px !important;
|
||||
width: auto !important;
|
||||
top: auto !important;
|
||||
background: var(--fh-grad) !important;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left center;
|
||||
transition: transform .35s cubic-bezier(.2,.7,.1,1);
|
||||
opacity: 1;
|
||||
font-size: 0 !important;
|
||||
border: 0 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
#navbar .item[href*="/user/login"]:hover::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
/* Navbar wordmark — image swap + chromatic-glitch hover */
|
||||
.flexhub-nav-wordmark {
|
||||
display: inline-block;
|
||||
width: 86px;
|
||||
height: 22px;
|
||||
background: url('/assets/img/flexhub-main-nav-wordmark.png') left center/contain no-repeat;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
color: transparent;
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
}
|
||||
.flexhub-nav-wordmark .fh-flex {
|
||||
display: none !important;
|
||||
}
|
||||
.flexhub-nav-wordmark::before,
|
||||
.flexhub-nav-wordmark::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: url('/assets/img/flexhub-main-nav-wordmark.png') left center/contain no-repeat;
|
||||
mix-blend-mode: screen;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity .12s linear, transform .12s linear;
|
||||
}
|
||||
.flexhub-nav-wordmark::before { filter: hue-rotate(-30deg) saturate(1.4); }
|
||||
.flexhub-nav-wordmark::after { filter: hue-rotate( 60deg) saturate(1.4); }
|
||||
.flexhub-nav-wordmark:hover { animation: fhGlitch .35s steps(6, end) 1; }
|
||||
.flexhub-nav-wordmark:hover::before { opacity: .9; transform: translate(-2px, 1px); }
|
||||
.flexhub-nav-wordmark:hover::after { opacity: .9; transform: translate( 2px, -1px); }
|
||||
@keyframes fhGlitch {
|
||||
0% { transform: translate(0, 0); }
|
||||
20% { transform: translate(-1px, 0); }
|
||||
40% { transform: translate( 1px, 0); }
|
||||
60% { transform: translate(-1px, 1px) skewX(-2deg); }
|
||||
80% { transform: translate( 1px,-1px) skewX( 2deg); }
|
||||
100% { transform: translate(0, 0); }
|
||||
}
|
||||
|
||||
/* Footer s8n.ru link in gradient */
|
||||
footer .footer-content a[href*="s8n.ru"],
|
||||
.page-footer a[href*="s8n.ru"] {
|
||||
background: var(--fh-grad);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
font-weight: 700;
|
||||
}
|
||||
320
public/assets/css/theme-flexhub-og.css
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
@import url('/assets/css/theme-forgejo-dark.css');
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url("/assets/font/inter-var.woff2") format("woff2-variations"),
|
||||
url("/assets/font/inter-var.woff2") format("woff2");
|
||||
}
|
||||
|
||||
:root {
|
||||
/* FlexHub — orange/black/white wordmark vibe */
|
||||
|
||||
/* Primary = signal orange */
|
||||
--color-primary: #ff9000;
|
||||
--color-primary-contrast: #000000;
|
||||
--color-primary-dark-1: #f08400;
|
||||
--color-primary-dark-2: #db7800;
|
||||
--color-primary-dark-3: #c66c00;
|
||||
--color-primary-dark-4: #b16100;
|
||||
--color-primary-dark-5: #9c5500;
|
||||
--color-primary-dark-6: #874900;
|
||||
--color-primary-dark-7: #723e00;
|
||||
--color-primary-light-1: #ffa12a;
|
||||
--color-primary-light-2: #ffb255;
|
||||
--color-primary-light-3: #ffc37f;
|
||||
--color-primary-light-4: #ffd4a9;
|
||||
--color-primary-light-5: #ffe5d4;
|
||||
--color-primary-light-6: #fff2e6;
|
||||
--color-primary-light-7: #fff8f0;
|
||||
--color-primary-alpha-10: #ff90001a;
|
||||
--color-primary-alpha-20: #ff900033;
|
||||
--color-primary-alpha-30: #ff90004d;
|
||||
--color-primary-alpha-40: #ff900066;
|
||||
--color-primary-alpha-50: #ff900080;
|
||||
--color-primary-alpha-60: #ff900099;
|
||||
--color-primary-alpha-70: #ff9000b3;
|
||||
--color-primary-alpha-80: #ff9000cc;
|
||||
--color-primary-alpha-90: #ff9000e6;
|
||||
--color-primary-hover: var(--color-primary-light-1);
|
||||
--color-primary-active: var(--color-primary-dark-1);
|
||||
|
||||
/* Surfaces — pure black base, slight lift on cards */
|
||||
--color-body: #000000;
|
||||
--color-box-header: #0a0a0a;
|
||||
--color-box-body: #060606;
|
||||
--color-box-body-highlight: #101010;
|
||||
--color-card: #0a0a0a;
|
||||
--color-menu: #060606;
|
||||
--color-header-wrapper: #000000;
|
||||
--color-nav-bg: #000000;
|
||||
--color-secondary-nav-bg: var(--color-body);
|
||||
--color-footer: #000000;
|
||||
--color-button: #1a1a1a;
|
||||
--color-secondary-bg: #1a1a1a;
|
||||
--color-secondary: #1a1a1a;
|
||||
--color-secondary-dark-1: #222222;
|
||||
--color-secondary-dark-2: #2a2a2a;
|
||||
|
||||
/* Borders — subtle orange tint on hover */
|
||||
--color-input-border: #2a2a2a;
|
||||
--color-input-border-hover: var(--color-primary);
|
||||
--color-light-border: #ffffff1a;
|
||||
|
||||
/* Inputs / search box — pure black, not blue-grey */
|
||||
--color-input-background: #000000;
|
||||
--color-input-toggle-background: #000000;
|
||||
--color-input-text: #f0f0f0;
|
||||
--color-form-background: #000000;
|
||||
--color-active-line: var(--color-primary);
|
||||
|
||||
/* Accent */
|
||||
--color-accent: var(--color-primary);
|
||||
--color-small-accent: var(--color-primary-light-2);
|
||||
--color-highlight-fg: var(--color-primary);
|
||||
--color-highlight-bg: var(--color-primary-alpha-10);
|
||||
|
||||
/* Text + focus */
|
||||
--color-text: #f0f0f0;
|
||||
--color-text-light: #c8c8c8;
|
||||
--color-text-light-1: #b0b0b0;
|
||||
--color-text-light-2: #909090;
|
||||
--color-text-light-3: #707070;
|
||||
--color-text-focus: var(--color-primary);
|
||||
|
||||
/* Reaction / overlays */
|
||||
--color-reaction-bg: var(--color-primary-alpha-10);
|
||||
--color-reaction-active-bg: var(--color-primary-alpha-30);
|
||||
--color-reaction-hover-bg: var(--color-primary-alpha-40);
|
||||
--color-overlay-backdrop: #000000d0;
|
||||
|
||||
/* Links */
|
||||
--color-link: var(--color-primary);
|
||||
--color-link-hover: var(--color-primary-light-1);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #ff9000 !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* FlexHub homepage hero */
|
||||
.flexhub-home {
|
||||
padding: 4rem 1rem 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
.flexhub-home .flexhub-wordmark {
|
||||
max-width: min(420px, 70vw);
|
||||
height: auto;
|
||||
margin: 0 auto 2rem;
|
||||
display: block;
|
||||
}
|
||||
.flexhub-home .flexhub-text-wordmark {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-family: "Inter", system-ui, Helvetica, Arial, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: clamp(3rem, 9vw, 6rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04em;
|
||||
margin: 0 auto 1.25rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.flexhub-home .flexhub-text-wordmark .fh-flex {
|
||||
color: #ffffff;
|
||||
padding: 0.1em 0.15em 0.1em 0;
|
||||
}
|
||||
.flexhub-home .flexhub-text-wordmark::after {
|
||||
content: "Hub";
|
||||
color: #000000;
|
||||
background: var(--color-primary);
|
||||
padding: 0.05em 0.2em;
|
||||
border-radius: 0.18em;
|
||||
}
|
||||
.flexhub-home h1 {
|
||||
font-size: clamp(2.2rem, 5vw, 3.4rem);
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.02em;
|
||||
margin: 0 0 0.6rem;
|
||||
color: #ffffff;
|
||||
}
|
||||
.flexhub-home h1 .flex {
|
||||
color: #ffffff;
|
||||
}
|
||||
.flexhub-home h1 .hub {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.flexhub-home .tagline {
|
||||
font-size: clamp(1.1rem, 2vw, 1.4rem);
|
||||
color: var(--color-text-light);
|
||||
margin: 0 0 0.4rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
.flexhub-home .subtagline {
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-light-2);
|
||||
margin: 0 0 3rem;
|
||||
font-style: italic;
|
||||
}
|
||||
.flexhub-home .agent-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 1.25rem;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.flexhub-home .agent-card {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-input-border);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem 1.25rem;
|
||||
text-align: left;
|
||||
transition: border-color .15s, transform .15s;
|
||||
}
|
||||
.flexhub-home .agent-card:hover {
|
||||
border-color: var(--color-primary);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.flexhub-home .agent-card .marker {
|
||||
display: inline-block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
background: var(--color-primary);
|
||||
color: #000;
|
||||
font-weight: 900;
|
||||
text-align: center;
|
||||
line-height: 28px;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.flexhub-home .agent-card h3 {
|
||||
margin: 0 0 0.4rem;
|
||||
color: #ffffff;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.flexhub-home .agent-card p {
|
||||
margin: 0;
|
||||
color: var(--color-text-light);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
/* Navbar wordmark — text-rendered via head_navbar.tmpl override */
|
||||
.flexhub-nav-wordmark {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-family: "Inter", system-ui, Helvetica, Arial, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.flexhub-nav-wordmark .fh-flex {
|
||||
color: #ffffff;
|
||||
padding: 4px 6px 4px 2px;
|
||||
}
|
||||
.flexhub-nav-wordmark::after {
|
||||
content: "Hub";
|
||||
color: #000000;
|
||||
background: var(--color-primary);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Footer: orange s8n.ru link + white placeholder text */
|
||||
.page-footer .footer-placeholder {
|
||||
color: #ffffff;
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
.page-footer .left-links a {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.page-footer .left-links a:hover {
|
||||
color: var(--color-primary-light-1);
|
||||
}
|
||||
|
||||
/* Top navbar (Explore etc): underlined hyperlink style, transparent bg, kill blue hover chip */
|
||||
#navbar a.item,
|
||||
#navbar .item {
|
||||
background: transparent !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
#navbar a.item:hover,
|
||||
#navbar .item:hover {
|
||||
background: transparent !important;
|
||||
color: var(--color-primary) !important;
|
||||
}
|
||||
#navbar a.item.active,
|
||||
#navbar .item.active {
|
||||
background: transparent !important;
|
||||
color: var(--color-primary) !important;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--color-primary);
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 6px;
|
||||
}
|
||||
#navbar a.item.active:hover {
|
||||
color: var(--color-primary-light-1) !important;
|
||||
}
|
||||
|
||||
/* Tab menus (Repositories / Users / Organizations): keep big border-bottom only,
|
||||
strip the close text-decoration line that doubles up under the label. */
|
||||
.ui.tabs.menu .item.active,
|
||||
.ui.tabs.menu .active.item,
|
||||
.ui.tabular.menu .item.active,
|
||||
.ui.tabular.menu .active.item,
|
||||
.ui.secondary.pointing.menu .item.active,
|
||||
.ui.secondary.pointing.menu .active.item {
|
||||
text-decoration: none !important;
|
||||
background: transparent !important;
|
||||
color: var(--color-primary) !important;
|
||||
border-color: var(--color-primary) !important;
|
||||
}
|
||||
|
||||
/* Search input + filter/sort dropdowns: pure black */
|
||||
.ui.input > input,
|
||||
.ui.action.input > input,
|
||||
.ui.form input[type="text"],
|
||||
.ui.form input[type="search"],
|
||||
.ui.search input,
|
||||
input[type="text"],
|
||||
input[type="search"] {
|
||||
background: #000000 !important;
|
||||
color: var(--color-input-text) !important;
|
||||
border-color: var(--color-input-border) !important;
|
||||
}
|
||||
.ui.input > input:focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="search"]:focus {
|
||||
background: #000000 !important;
|
||||
border-color: var(--color-primary) !important;
|
||||
}
|
||||
.ui.action.input > .button,
|
||||
.ui.action.input > .ui.button {
|
||||
background: #000000 !important;
|
||||
color: var(--color-text) !important;
|
||||
border: 1px solid var(--color-input-border) !important;
|
||||
}
|
||||
.ui.action.input > .button:hover {
|
||||
border-color: var(--color-primary) !important;
|
||||
color: var(--color-primary) !important;
|
||||
}
|
||||
|
||||
/* Primary buttons keep orange-on-black */
|
||||
.ui.primary.button,
|
||||
.ui.primary.buttons .button {
|
||||
background: var(--color-primary) !important;
|
||||
color: #000 !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
.ui.primary.button:hover,
|
||||
.ui.primary.buttons .button:hover {
|
||||
background: var(--color-primary-light-1) !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
122
public/assets/css/theme-monochrome.css
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
@import url('/assets/css/theme-forgejo-dark.css');
|
||||
|
||||
:root {
|
||||
/* Veilor monochrome — security/terminal aesthetic */
|
||||
|
||||
/* Steel scale: pure neutral grays (zero hue) */
|
||||
--steel-900: #0a0a0a;
|
||||
--steel-850: #0f0f0f;
|
||||
--steel-800: #131313;
|
||||
--steel-750: #181818;
|
||||
--steel-700: #1d1d1d;
|
||||
--steel-650: #232323;
|
||||
--steel-600: #2b2b2b;
|
||||
--steel-550: #353535;
|
||||
--steel-500: #424242;
|
||||
--steel-450: #525252;
|
||||
--steel-400: #636363;
|
||||
--steel-350: #767676;
|
||||
--steel-300: #8b8b8b;
|
||||
--steel-250: #a0a0a0;
|
||||
--steel-200: #b6b6b6;
|
||||
--steel-150: #cdcdcd;
|
||||
--steel-100: #e6e6e6;
|
||||
|
||||
/* Primary = stark white accent */
|
||||
--color-primary: #ffffff;
|
||||
--color-primary-contrast: #000000;
|
||||
--color-primary-dark-1: #f0f0f0;
|
||||
--color-primary-dark-2: #e0e0e0;
|
||||
--color-primary-dark-3: #d0d0d0;
|
||||
--color-primary-dark-4: #c0c0c0;
|
||||
--color-primary-dark-5: #b0b0b0;
|
||||
--color-primary-dark-6: #a0a0a0;
|
||||
--color-primary-dark-7: #909090;
|
||||
--color-primary-light-1: #e6e6e6;
|
||||
--color-primary-light-2: #cdcdcd;
|
||||
--color-primary-light-3: #b6b6b6;
|
||||
--color-primary-light-4: #a0a0a0;
|
||||
--color-primary-light-5: #8b8b8b;
|
||||
--color-primary-light-6: #767676;
|
||||
--color-primary-light-7: #636363;
|
||||
--color-primary-alpha-10: #ffffff19;
|
||||
--color-primary-alpha-20: #ffffff33;
|
||||
--color-primary-alpha-30: #ffffff4b;
|
||||
--color-primary-alpha-40: #ffffff66;
|
||||
--color-primary-alpha-50: #ffffff80;
|
||||
--color-primary-alpha-60: #ffffff99;
|
||||
--color-primary-alpha-70: #ffffffb3;
|
||||
--color-primary-alpha-80: #ffffffcc;
|
||||
--color-primary-alpha-90: #ffffffe1;
|
||||
--color-primary-hover: var(--color-primary-light-1);
|
||||
--color-primary-active: var(--color-primary-light-2);
|
||||
|
||||
/* Body / surfaces — pitch black base */
|
||||
--color-body: #08080a;
|
||||
--color-box-header: #121214;
|
||||
--color-box-body: #0e0e10;
|
||||
--color-box-body-highlight: #16161a;
|
||||
--color-card: #121214;
|
||||
--color-menu: #0e0e10;
|
||||
--color-header-wrapper: #050507;
|
||||
--color-nav-bg: #050507;
|
||||
--color-secondary-nav-bg: var(--color-body);
|
||||
--color-footer: #050507;
|
||||
--color-button: #1d1d1d;
|
||||
--color-secondary-bg: #1d1d1d;
|
||||
|
||||
/* Borders / lines */
|
||||
--color-input-border: #2a2a2a;
|
||||
--color-input-border-hover: #404040;
|
||||
--color-light-border: #ffffff1a;
|
||||
|
||||
/* Accent override (chains back to primary) */
|
||||
--color-accent: #ffffff;
|
||||
--color-small-accent: #b6b6b6;
|
||||
--color-highlight-fg: #e6e6e6;
|
||||
--color-highlight-bg: #ffffff14;
|
||||
|
||||
/* Reaction / focus */
|
||||
--color-text-focus: #ffffff;
|
||||
--color-reaction-bg: #ffffff10;
|
||||
--color-reaction-active-bg: #ffffff30;
|
||||
--color-reaction-hover-bg: #ffffff40;
|
||||
|
||||
/* Selection */
|
||||
--color-overlay-backdrop: #000000d0;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* Strip color from primary buttons / labels (override late-cascading rules) */
|
||||
.ui.primary.button,
|
||||
.ui.primary.buttons .button {
|
||||
background: var(--color-primary) !important;
|
||||
color: var(--color-primary-contrast) !important;
|
||||
}
|
||||
.ui.primary.button:hover,
|
||||
.ui.primary.buttons .button:hover {
|
||||
background: var(--color-primary-dark-2) !important;
|
||||
}
|
||||
.ui.primary.label,
|
||||
.ui.primary.labels .label,
|
||||
.ui.red.label.notification_count {
|
||||
background-color: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* Top nav: pure black bar, white text */
|
||||
.ui.secondary.menu .item,
|
||||
#navbar .item {
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
|
||||
/* Subtle white outline on cards */
|
||||
.repo-list .item,
|
||||
.ui.cards > .card,
|
||||
.ui.card {
|
||||
border: 1px solid #1f1f22 !important;
|
||||
}
|
||||
BIN
public/assets/img/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/assets/img/favicon-16.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/assets/img/favicon-32.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/assets/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/assets/img/favicon.png
Normal file
|
After Width: | Height: | Size: 255 KiB |
1
public/assets/img/favicon.svg
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
public/assets/img/flexhub-main-apple-touch.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/assets/img/flexhub-main-favicon-16.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/assets/img/flexhub-main-favicon-32.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/assets/img/flexhub-main-favicon-512.png
Normal file
|
After Width: | Height: | Size: 255 KiB |
BIN
public/assets/img/flexhub-main-favicon.ico
Normal file
|
After Width: | Height: | Size: 31 KiB |
1
public/assets/img/flexhub-main-favicon.svg
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
public/assets/img/flexhub-main-home-wordmark.png
Normal file
|
After Width: | Height: | Size: 335 KiB |
BIN
public/assets/img/flexhub-main-nav-wordmark.png
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
public/assets/img/flexhub-wordmark.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
7
public/assets/img/logo.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 407 135" width="407" height="135">
|
||||
<rect width="407" height="135" fill="#000000"/>
|
||||
<text x="15" y="105" font-family="Inter,system-ui,Helvetica,Arial,sans-serif" font-weight="900" font-size="105" fill="#FFFFFF" letter-spacing="-3">Flex</text>
|
||||
<rect x="205" y="10" width="190" height="115" rx="14" fill="#FF9000"/>
|
||||
<text x="222" y="105" font-family="Inter,system-ui,Helvetica,Arial,sans-serif" font-weight="900" font-size="105" fill="#000000" letter-spacing="-3">Hub</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 579 B |
6
templates/base/footer_content.tmpl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<footer class="page-footer" role="group" aria-label="{{ctx.Locale.Tr "aria.footer"}}">
|
||||
<div class="left-links" role="contentinfo" aria-label="{{ctx.Locale.Tr "aria.footer.software"}}">
|
||||
<span class="footer-placeholder">built by </span>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://s8n.ru">s8n.ru</a>
|
||||
</div>
|
||||
</footer>
|
||||
202
templates/base/head_navbar.tmpl
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
{{$notificationUnreadCount := 0}}
|
||||
{{if and .IsSigned .NotificationUnreadCount}}
|
||||
{{$notificationUnreadCount = call .NotificationUnreadCount}}
|
||||
{{end}}
|
||||
|
||||
<nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}">
|
||||
<div class="navbar-left ui secondary menu">
|
||||
<!-- the logo -->
|
||||
<a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}">
|
||||
<span class="flexhub-nav-wordmark" aria-hidden="true"><span class="fh-flex">Flex</span></span>
|
||||
</a>
|
||||
|
||||
<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
|
||||
<div class="ui secondary menu item navbar-mobile-right only-mobile">
|
||||
{{if .IsSigned}}
|
||||
<a id="mobile-notifications-icon" class="item tw-w-auto tw-p-2" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-bell"}}
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
<button class="item tw-w-auto ui icon mini button tw-p-2 tw-m-0" id="navbar-expand-toggle" aria-label="{{ctx.Locale.Tr "toggle_menu"}}">{{svg "octicon-three-bars"}}</button>
|
||||
</div>
|
||||
|
||||
<!-- navbar links non-mobile -->
|
||||
{{if and .IsSigned .MustChangePassword}}
|
||||
{{/* No links */}}
|
||||
{{else if .IsSigned}}
|
||||
{{if not .UnitIssuesGlobalDisabled}}
|
||||
<a class="item{{if .PageIsIssues}} active{{end}}" href="{{AppSubUrl}}/issues">{{ctx.Locale.Tr "issues"}}</a>
|
||||
{{end}}
|
||||
{{if not .UnitPullsGlobalDisabled}}
|
||||
<a class="item{{if .PageIsPulls}} active{{end}}" href="{{AppSubUrl}}/pulls">{{ctx.Locale.Tr "pull_requests"}}</a>
|
||||
{{end}}
|
||||
{{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}}
|
||||
{{if .ShowMilestonesDashboardPage}}
|
||||
<a class="item{{if .PageIsMilestonesDashboard}} active{{end}}" href="{{AppSubUrl}}/milestones">{{ctx.Locale.Tr "milestones"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/repos">{{ctx.Locale.Tr "explore"}}</a>
|
||||
{{else if .IsLandingPageOrganizations}}
|
||||
<a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{ctx.Locale.Tr "explore"}}</a>
|
||||
{{else}}
|
||||
<a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/repos">{{ctx.Locale.Tr "explore"}}</a>
|
||||
{{end}}
|
||||
|
||||
{{template "custom/extra_links" .}}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- the full dropdown menus -->
|
||||
<div class="navbar-right ui secondary menu">
|
||||
{{if and .IsSigned .MustChangePassword}}
|
||||
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span class="text tw-flex tw-items-center">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
|
||||
<span class="only-mobile tw-ml-2">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu">
|
||||
<div class="ui header">
|
||||
{{ctx.Locale.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{ctx.Locale.Tr "sign_out"}}
|
||||
</a>
|
||||
</div><!-- end content avatar menu -->
|
||||
</div><!-- end dropdown avatar menu -->
|
||||
{{else if .IsSigned}}
|
||||
{{if EnableTimetracking}}
|
||||
<a class="active-stopwatch-trigger item tw-mx-0{{if not .ActiveStopwatch}} tw-hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-stopwatch"}}
|
||||
<span class="header-stopwatch-dot"></span>
|
||||
</div>
|
||||
<span class="only-mobile tw-ml-2">{{ctx.Locale.Tr "active_stopwatch"}}</span>
|
||||
</a>
|
||||
<div class="active-stopwatch-popup item tippy-target tw-p-2">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<a class="stopwatch-link tw-flex tw-items-center" href="{{.ActiveStopwatch.IssueLink}}">
|
||||
{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
|
||||
<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
|
||||
<span class="ui primary label stopwatch-time tw-my-0 tw-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
|
||||
{{if .ActiveStopwatch}}{{Sec2Time .ActiveStopwatch.Seconds}}{{end}}
|
||||
</span>
|
||||
</a>
|
||||
<form class="stopwatch-commit" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/toggle">
|
||||
{{.CsrfTokenHtml}}
|
||||
<button
|
||||
type="submit"
|
||||
class="ui button mini compact basic icon"
|
||||
data-tooltip-content="{{ctx.Locale.Tr "repo.issues.stop_tracking"}}"
|
||||
>{{svg "octicon-square-fill"}}</button>
|
||||
</form>
|
||||
<form class="stopwatch-cancel" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/cancel">
|
||||
{{.CsrfTokenHtml}}
|
||||
<button
|
||||
type="submit"
|
||||
class="ui button mini compact basic icon"
|
||||
data-tooltip-content="{{ctx.Locale.Tr "repo.issues.cancel_tracking"}}"
|
||||
>{{svg "octicon-trash"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<a class="item not-mobile tw-mx-0" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-bell"}}
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ui dropdown jump item tw-mx-0 tw-pr-2" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
|
||||
<span class="text">
|
||||
{{svg "octicon-plus"}}
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
<span class="only-mobile">{{ctx.Locale.Tr "create_new"}}</span>
|
||||
</span>
|
||||
<div class="menu">
|
||||
<a class="item" href="{{AppSubUrl}}/repo/create">
|
||||
{{svg "octicon-plus"}} {{ctx.Locale.Tr "new_repo.link"}}
|
||||
</a>
|
||||
{{if not .DisableMigrations}}
|
||||
<a class="item" href="{{AppSubUrl}}/repo/migrate">
|
||||
{{svg "octicon-repo-push"}} {{ctx.Locale.Tr "new_migrate.link"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .SignedUser.CanCreateOrganization}}
|
||||
<a class="item" href="{{AppSubUrl}}/org/create">
|
||||
{{svg "octicon-organization"}} {{ctx.Locale.Tr "new_org.link"}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div><!-- end content create new menu -->
|
||||
</div><!-- end dropdown menu create new -->
|
||||
|
||||
<div class="ui dropdown jump item tw-mx-0 tw-pr-2" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span class="text tw-flex tw-items-center">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
|
||||
<span class="only-mobile tw-ml-2">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu">
|
||||
<div class="ui header">
|
||||
{{ctx.Locale.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item" href="{{.SignedUser.HomeLink}}">
|
||||
{{svg "octicon-person"}}
|
||||
{{ctx.Locale.Tr "your_profile"}}
|
||||
</a>
|
||||
{{if not .DisableStars}}
|
||||
<a class="item" href="{{.SignedUser.HomeLink}}?tab=stars">
|
||||
{{svg "octicon-star"}}
|
||||
{{ctx.Locale.Tr "your_starred"}}
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="item" href="{{AppSubUrl}}/notifications/subscriptions">
|
||||
{{svg "octicon-bell"}}
|
||||
{{ctx.Locale.Tr "notification.subscriptions"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsUserSettings}}active {{end}}item" href="{{AppSubUrl}}/user/settings">
|
||||
{{svg "octicon-tools"}}
|
||||
{{ctx.Locale.Tr "your_settings"}}
|
||||
</a>
|
||||
<a class="item" target="_blank" rel="noopener noreferrer" href="https://forgejo.org/docs/latest/">
|
||||
{{svg "octicon-question"}}
|
||||
{{ctx.Locale.Tr "help"}}
|
||||
</a>
|
||||
{{if .IsAdmin}}
|
||||
<div class="divider"></div>
|
||||
|
||||
<a class="{{if .PageIsAdmin}}active {{end}}item" href="{{AppSubUrl}}/admin">
|
||||
{{svg "octicon-server"}}
|
||||
{{ctx.Locale.Tr "admin_panel"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{ctx.Locale.Tr "sign_out"}}
|
||||
</a>
|
||||
</div><!-- end content avatar menu -->
|
||||
</div><!-- end dropdown avatar menu -->
|
||||
{{else}}
|
||||
{{if .ShowRegistrationButton}}
|
||||
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up">
|
||||
{{svg "octicon-person"}} {{ctx.Locale.Tr "register"}}
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login{{if not .PageIsSignIn}}?redirect_to={{.CurrentURL}}{{end}}">
|
||||
{{svg "octicon-sign-in"}} {{ctx.Locale.Tr "sign_in"}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div><!-- end full right menu -->
|
||||
</nav>
|
||||
|
|
@ -6,22 +6,22 @@
|
|||
|
||||
<div class="agent-grid">
|
||||
<div class="agent-card">
|
||||
<span class="marker">A</span>
|
||||
<span class="marker"><span>A</span></span>
|
||||
<h3>Hosted, hard, unfiltered</h3>
|
||||
<p>Code on my own metal. No upstream daddy, no rate limits, no policy drift.</p>
|
||||
</div>
|
||||
<div class="agent-card">
|
||||
<span class="marker">B</span>
|
||||
<span class="marker"><span>B</span></span>
|
||||
<h3>Off-platform</h3>
|
||||
<p>No GitHub, no GitLab, no shared landlord. Just metal in my house and a domain I own.</p>
|
||||
</div>
|
||||
<div class="agent-card">
|
||||
<span class="marker">C</span>
|
||||
<span class="marker"><span>C</span></span>
|
||||
<h3>Your repo, your rules</h3>
|
||||
<p>Push private. Push experimental. Push that thing you'd never put on a public profile.</p>
|
||||
</div>
|
||||
<div class="agent-card">
|
||||
<span class="marker">D</span>
|
||||
<span class="marker"><span>D</span></span>
|
||||
<h3>Drop the repo</h3>
|
||||
<p>If you built it, it lives here. No filter, no algorithm, no cold-storage policy.</p>
|
||||
</div>
|
||||
|
|
|
|||