11 Commits
Author SHA1 Message Date
KenanZhu bcf1b87e82 preview: remove screenshot export and restore UI animations
Validate / Validate (push) Successful in 2m52s
2026-10-11 16:59:00 +08:00
KenanZhu 6733e81356 preview: improve sidebar layout and screenshot export
Validate / Validate (push) Successful in 3m29s
2026-10-11 11:05:48 +08:00
KenanZhu e51e2e9996 docs: align English compatibility guide with Chinese version 2026-10-11 11:04:41 +08:00
KenanZhu 5f5ad058a8 chore: split validation and release workflows and refactor packaging 2026-10-11 09:53:25 +08:00
KenanZhu 5b06a795e4 preview: hide email preview scrollbars
Release / Validate Templates (push) Successful in 2m53s
Release / Package & Release (push) Skipped
Release / Remind to Update Release Documentation (push) Skipped
2026-10-10 15:03:44 +08:00
KenanZhu c93f2bf8b2 chore: simplify workflows and unify tool logging and mail presentation
Release / Validate Templates (push) Successful in 2m55s
Release / Package & Release (push) Skipped
Release / Remind to Update Release Documentation (push) Skipped
2026-10-10 14:54:40 +08:00
KenanZhu f4d96de79e chore: refresh docs and adapt workflows for Gitea
Release / Validate Templates (push) Successful in 3m1s
Release / Package & Release (push) Skipped
Release / Update Latest Release Documentation (push) Skipped
2026-10-09 22:02:33 +08:00
KenanZhu fec3ace600 chore: migrate mail themes to shared framework and locked upstream inputs
Release / Validate Templates (push) Canceled after 0s
Release / Package & Release (push) Canceled after 0s
Release / Update Latest Release Documentation (push) Canceled after 0s
2026-10-09 18:34:36 +08:00
KenanZhu 5c0589f6f6 docs: clarify per-version Gitea compatibility
Release / Validate Templates (push) Canceled after 0s
Release / Package & Release (push) Canceled after 0s
Release / Update Latest Release Documentation (push) Canceled after 0s
2026-10-09 12:27:00 +08:00
KenanZhu 14dc37046d refactor: unify markdown release tracking blocks
Release / Validate Templates (push) Canceled after 0s
Release / Package & Release (push) Canceled after 0s
Release / Update Latest Release Documentation (push) Canceled after 0s
2026-10-09 12:10:12 +08:00
KenanZhu 23f0456622 chore: streamline compatibility tracking and documentation
Release / Validate Templates (push) Canceled after 0s
Release / Package & Release (push) Canceled after 0s
2026-10-09 11:43:02 +08:00
232 changed files with 5311 additions and 16495 deletions

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
* text=auto
# Generated lock and framework sources use stable line endings.
gitea.lock.json text eol=lf
framework/** text eol=lf
themes/** text eol=lf
tools/** text eol=lf
preview/** text eol=lf
.github/** text eol=lf
-22
View File
@@ -1,22 +0,0 @@
> **Correction (2026-09-23):** v1.27.2 did not fix every push notification template. Bloom, Ember, and Heritage still used the old commit ID path and can fail to render push notifications on Gitea 1.27.1+. Use v1.27.3 for the fix.
FEATURES
- THEMES (Aurora)
- Add `email-text` class to release template paragraphs for consistent typography with the rest of the theme
BUGFIXES
- THEMES
- Update push commit data paths in seven themes for Gitea 1.27.1 compatibility (`.ID` → `.UserCommit.GitCommit.ID`); see the correction above for the three remaining themes
- Replace `rgba()` colors with `#RRGGBBAA` hex notation across all 10 themes
DOCS
- Track Gitea 1.27.0, 1.27.1, and 1.27.2 compatibility
- Keep English and Simplified Chinese README and CONTRIBUTING files
---
**Full Changelog:** [v1.0.1...v1.27.2](https://github.com/KenanZhu/GiteaMailTemplates/compare/v1.0.1...v1.27.2)
-22
View File
@@ -1,22 +0,0 @@
FEATURES
- RELEASE
- Include English and Simplified Chinese README and CONTRIBUTING files in the release archives
- Use reviewed release notes in the publishing workflow
BUGFIXES
- THEMES (Bloom, Ember, Heritage)
- Fix pull request push notification commit links and abbreviated hashes for Gitea 1.27.1–1.27.3 by reading `.UserCommit.GitCommit.ID`
- CI
- Add a push notification regression test covering all 10 themes and run Go tests before packaging a release
DOCS
- Verify compatibility with Gitea 1.27.3; its mail templates, mailer, and locale files are unchanged from 1.27.2
- Correct the compatibility matrix to distinguish the affected v1.27.2 archive from this fixed release
---
**Full Changelog:** [v1.27.2...v1.27.3](https://github.com/KenanZhu/GiteaMailTemplates/compare/v1.27.2...v1.27.3)
-21
View File
@@ -1,21 +0,0 @@
FEATURES
- COMPATIBILITY
- Add a Gitea 28.0.0-aligned release of all 10 themes and 11 mail types per theme
BUGFIXES
- THEMES (All 10)
- Replace the removed `FileSize` function with `FormatByteSize` in release-attachment emails, preventing Gitea 28.0.0 template rendering errors
- PREVIEW AND TESTS
- Match the preview's file-size function to Gitea 28.0.0 and render release attachments in the example data
- Add an all-theme regression test for release attachments and the removed function
DOCS
- Update the English and Simplified Chinese READMEs and compatibility matrix for Gitea 28.0.0
- Clarify that v28.0.0 requires Gitea 28.0.0; use v1.27.3 for Gitea 1.25.0–1.27.3
---
**Full Changelog:** [v1.27.3...v28.0.0](https://github.com/KenanZhu/GiteaMailTemplates/compare/v1.27.3...v28.0.0)
+245
View File
@@ -0,0 +1,245 @@
"""Package verified generated mail overrides; never copy stale build directories."""
import argparse
import hashlib
import io
import json
import re
import tarfile
import zipfile
from pathlib import Path, PurePosixPath
VERSION_PATTERN = re.compile(r"v[0-9]+\.[0-9]+\.[0-9]+")
THEME_NAME_PATTERN = re.compile(r"[a-z][a-z0-9-]*")
BASE_TEMPLATES = {"mail/base/head.tmpl", "mail/base/footer.tmpl"}
REQUIRED_FILES = (
"LICENSE",
"README.md",
"CONTRIBUTING.md",
"COMPATIBILITY.md",
"docs/README.zh-CN.md",
"docs/CONTRIBUTING.zh-CN.md",
"preview/index.html",
)
OPTIONAL_ROOT_DOCUMENTS = ("AGENTS.md", "THIRD_PARTY_NOTICES.md")
DOCUMENTATION_DIRECTORIES = ("docs", ".github/release-notes")
PREVIEW_MANIFEST_PREFIX = b"window.__MAIL_PREVIEW__ = "
ARCHIVE_EXTENSIONS = (".zip", ".tar.gz")
def read_file(path):
path = Path(path)
if path.is_symlink() or not path.is_file():
raise ValueError(f"Expected regular file: {path}")
return path.read_bytes()
def checked_path(base, name):
parts = PurePosixPath(name)
if parts.is_absolute() or ".." in parts.parts or "\\" in name or ":" in name:
raise ValueError(f"Unsafe package path: {name}")
path = base.joinpath(*parts.parts)
if not path.resolve().is_relative_to(base.resolve()):
raise ValueError(f"Path escapes package root: {name}")
return path
def digest(data):
return hashlib.sha256(data).hexdigest()
def read_json(path):
return json.loads(read_file(path))
def load_lock(root, version):
"""Validate the requested release identity before collecting any output."""
if not VERSION_PATTERN.fullmatch(version):
raise ValueError("Expected a stable vX.Y.Z package version")
lock = read_json(root / "gitea.lock.json")
if version != lock["tag"]:
raise ValueError("Package version must match the locked Gitea tag")
return lock
def discover_theme_names(root):
names = sorted(
path.name
for path in (root / "themes").iterdir()
if path.is_dir() and not path.is_symlink()
)
if not names:
raise ValueError("No source theme manifests")
return names
def expected_template_paths(root, lock, mode):
"""Shared themes override base files; framed themes also supply all bodies."""
paths = set(BASE_TEMPLATES)
if mode == "framed":
paths.update(
path.removeprefix("templates/")
for path in lock["files"]
if path.startswith("templates/mail/") and path.endswith(".tmpl")
)
paths.update(
path.relative_to(root / "framework").as_posix()
for path in (root / "framework" / "mail").rglob("*.tmpl")
)
return paths
def collect_theme(root, lock, name):
"""Accept only a complete build whose identity and checksums still match."""
if not THEME_NAME_PATTERN.fullmatch(name):
raise ValueError(f"Invalid theme name: {name}")
source = root / "themes" / name
theme = read_json(source / "theme.json")
if theme["name"] != name or theme["mode"] not in ("shared", "framed"):
raise ValueError(f"Invalid theme metadata: {name}")
built = root / "build" / "themes" / name
metadata = read_json(built / "build.json")
actual_identity = (
metadata["theme"], metadata["mode"],
metadata["gitea_tag"], metadata["gitea_commit"],
)
expected_identity = (name, theme["mode"], lock["tag"], lock["commit"])
if actual_identity != expected_identity:
raise ValueError(f"Stale build identity: {name}")
has_sources = {"theme.json", "theme.css"}.issubset(metadata.get("sources") or {})
if not has_sources or not metadata.get("files"):
raise ValueError(f"Missing source/file provenance: {name}")
if set(metadata["files"]) != expected_template_paths(root, lock, theme["mode"]):
raise ValueError(f"Incomplete generated install package: {name}")
for path, expected in metadata["sources"].items():
base = root if path.startswith(("framework/", "tools/")) else source
if digest(read_file(checked_path(base, path))) != expected:
raise ValueError(f"Source changed since build: {name}/{path}")
files = {}
for path, expected in metadata["files"].items():
if not path.startswith("mail/") or not path.endswith(".tmpl"):
raise ValueError(f"Unexpected generated file: {path}")
data = read_file(checked_path(built, path))
if digest(data) != expected:
raise ValueError(f"Generated checksum mismatch: {name}/{path}")
files[f"themes/{name}/{path}"] = data
files[f"themes/{name}/build.json"] = read_file(built / "build.json")
return files
def parse_script_payload(data, prefix):
"""Read generated JSON without executing the surrounding JavaScript."""
return json.loads(data.split(prefix, 1)[1].strip().removesuffix(b";"))
def collect_preview(root, lock, theme_names):
manifest_data = read_file(root / "preview" / "rendered.js")
manifest = parse_script_payload(manifest_data, PREVIEW_MANIFEST_PREFIX)
languages = sorted(
path.removeprefix("options/locale/locale_").removesuffix(".json")
for path in lock["files"]
if path.startswith("options/locale/locale_") and path.endswith(".json")
)
matches_build = (
manifest["upstream"] == lock["tag"]
and manifest.get("upstream_commit") == lock["commit"]
and sorted(manifest["themes"]) == theme_names
and sorted(manifest["languages"]) == languages
)
if not matches_build:
raise ValueError(
"Preview does not match lock/all source themes and languages; run preview all"
)
files = {"preview/rendered.js": manifest_data}
expected_templates = set(manifest["registry"])
for language in languages:
data = read_file(root / "preview" / "rendered" / (language + ".js"))
prefix = ('window.__MAIL_PREVIEW_DATA__[' + json.dumps(language) + '] = ').encode()
payload = parse_script_payload(data, prefix)
if sorted(payload) != theme_names or any(
set(payload[name]) != expected_templates for name in theme_names
):
raise ValueError(f"Incomplete preview language bundle: {language}")
files[f"preview/rendered/{language}.js"] = data
return files
def collect_documentation(root):
files = {name: read_file(root / name) for name in REQUIRED_FILES}
for name in OPTIONAL_ROOT_DOCUMENTS:
if (root / name).exists():
files[name] = read_file(root / name)
for directory in DOCUMENTATION_DIRECTORIES:
for path in sorted((root / directory).rglob("*.md")):
name = path.relative_to(root).as_posix()
files[name] = read_file(checked_path(root, name))
for path in sorted((root / "docs" / "images").iterdir()):
if path.is_file() and (path.suffix == ".png" or path.name == "README.md"):
files["docs/images/" + path.name] = read_file(path)
return files
def collect_provenance(root, lock):
license_data = read_file(root / "build" / "upstream" / "LICENSE")
if digest(license_data) != lock["files"]["LICENSE"]:
raise ValueError("Upstream license checksum mismatch")
return {
"GITEA-LICENSE": license_data,
"upstream-lock.json": read_file(root / "gitea.lock.json"),
}
def collect(root, version):
"""Collect verified release content without writing any archives."""
root = Path(root)
lock = load_lock(root, version)
theme_names = discover_theme_names(root)
files = {}
for name in theme_names:
files.update(collect_theme(root, lock, name))
files.update(collect_preview(root, lock, theme_names))
files.update(collect_documentation(root))
files.update(collect_provenance(root, lock))
return files
def package(root, version, output):
"""Write both archive formats from the same verified file collection."""
files = collect(root, version)
archive = f"gitea-mail-templates-{version}"
output = Path(output)
targets = [output / (archive + ext) for ext in ARCHIVE_EXTENSIONS]
if any(path.exists() for path in targets):
raise ValueError(
"Refusing to overwrite existing release archives; use a new output directory"
)
output.mkdir(parents=True, exist_ok=True)
with (
zipfile.ZipFile(targets[0], "w", zipfile.ZIP_DEFLATED, compresslevel=9) as zipped,
tarfile.open(targets[1], "w:gz") as tar,
):
for name, data in sorted(files.items()):
path = archive + "/" + name
zipped.writestr(path, data)
info = tarfile.TarInfo(path)
info.size = len(data)
info.mode = 0o644
tar.addfile(info, io.BytesIO(data))
print(f"[PASS] Packaged {len(files)} verified files: {targets[0]}, {targets[1]}")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--version", required=True)
parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2])
parser.add_argument("--output", type=Path, default=Path("dist"))
args = parser.parse_args()
try:
package(args.root, args.version, args.output)
except (ValueError, KeyError, IndexError, OSError) as error:
parser.exit(1, f"[FAIL] {error}\n")
if __name__ == "__main__":
main()
+200
View File
@@ -0,0 +1,200 @@
"""Offline packaging tests using a minimal, independently generated repository."""
import importlib.util
import json
import tarfile
import tempfile
import unittest
import zipfile
from pathlib import Path
SCRIPT_PATH = Path(__file__).with_name("package_release.py")
SPEC = importlib.util.spec_from_file_location("package_release", SCRIPT_PATH)
PACKER = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(PACKER)
class PackagingTests(unittest.TestCase):
VERSION = "v28.0.0"
COMMIT = "a" * 40
THEME = "demo"
LANGUAGE = "en-US"
ROOT_DOCUMENTS = (
"LICENSE",
"AGENTS.md",
"README.md",
"CONTRIBUTING.md",
"COMPATIBILITY.md",
"THIRD_PARTY_NOTICES.md",
"docs/README.zh-CN.md",
"docs/CONTRIBUTING.zh-CN.md",
"preview/index.html",
"docs/images/README.md",
)
def setUp(self):
temporary_directory = tempfile.TemporaryDirectory()
self.addCleanup(temporary_directory.cleanup)
self.root = Path(temporary_directory.name)
self.output = self.root / "artifacts"
self.create_theme()
self.create_snapshot()
self.create_preview()
for name in self.ROOT_DOCUMENTS:
self.write(name, b"test")
# Fixture helpers keep test bodies focused on the behavior being checked.
def write(self, name, data):
path = self.root / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(data)
def write_json(self, name, data):
self.write(name, json.dumps(data).encode("utf-8"))
def create_theme(self):
theme_data = json.dumps({"name": self.THEME, "mode": "shared"}).encode("utf-8")
css_data = b"a { color:blue; }"
self.write("themes/demo/theme.json", theme_data)
self.write("themes/demo/theme.css", css_data)
generated_files = {
"mail/base/head.tmpl": b"<title>test</title>",
"mail/base/footer.tmpl": b"<p>footer</p>",
}
metadata = {
"theme": self.THEME,
"mode": "shared",
"gitea_tag": self.VERSION,
"gitea_commit": self.COMMIT,
"sources": {
"theme.json": PACKER.digest(theme_data),
"theme.css": PACKER.digest(css_data),
},
"files": {
path: PACKER.digest(data)
for path, data in generated_files.items()
},
}
for path, data in generated_files.items():
self.write("build/themes/demo/" + path, data)
self.write_json("build/themes/demo/build.json", metadata)
def create_snapshot(self):
self.write_json("gitea.lock.json", {
"tag": self.VERSION,
"commit": self.COMMIT,
"files": {
"LICENSE": PACKER.digest(b"MIT"),
"options/locale/locale_en-US.json": PACKER.digest(b"{}"),
},
})
self.write("build/upstream/LICENSE", b"MIT")
def create_preview(self):
manifest = {
"upstream": self.VERSION,
"upstream_commit": self.COMMIT,
"themes": [self.THEME],
"languages": [self.LANGUAGE],
"registry": {"activate": {}},
}
bundle = {self.THEME: {"activate": "<html></html>"}}
self.write("preview/rendered.js", (
"window.__MAIL_PREVIEW__ = " + json.dumps(manifest) + ";"
).encode("utf-8"))
self.write("preview/rendered/en-US.js", (
'window.__MAIL_PREVIEW_DATA__["en-US"] = ' + json.dumps(bundle) + ";"
).encode("utf-8"))
# Archives: both formats contain the verified bytes, never stale build files.
def test_archives_contain_only_verified_outputs(self):
self.write("build/themes/stale/mail/stale.tmpl", b"stale")
expected_files = PACKER.collect(self.root, self.VERSION)
prefix = f"gitea-mail-templates-{self.VERSION}/"
expected_members = {
prefix + name: data for name, data in expected_files.items()
}
PACKER.package(self.root, self.VERSION, self.output)
with zipfile.ZipFile(self.output / f"gitea-mail-templates-{self.VERSION}.zip") as archive:
zipped = {name: archive.read(name) for name in archive.namelist()}
with tarfile.open(self.output / f"gitea-mail-templates-{self.VERSION}.tar.gz") as archive:
tarred = {
member.name: archive.extractfile(member).read()
for member in archive.getmembers()
}
self.assertTrue(all(member.mode == 0o644 for member in archive.getmembers()))
self.assertEqual(expected_members, zipped)
self.assertEqual(expected_members, tarred)
self.assertIn(prefix + "themes/demo/mail/base/head.tmpl", zipped)
self.assertFalse(any("stale" in name or "/build/" in name for name in zipped))
def test_existing_archives_are_not_overwritten(self):
PACKER.package(self.root, self.VERSION, self.output)
original_archives = {
path.name: path.read_bytes() for path in self.output.iterdir()
}
with self.assertRaisesRegex(ValueError, "Refusing to overwrite"):
PACKER.package(self.root, self.VERSION, self.output)
self.assertEqual(original_archives, {
path.name: path.read_bytes() for path in self.output.iterdir()
})
# Validation: release identity, source checksums and generated checksums.
def test_wrong_version_is_rejected(self):
with self.assertRaisesRegex(ValueError, "must match the locked Gitea tag"):
PACKER.collect(self.root, "v28.1.0")
def test_changed_source_is_rejected(self):
self.write("themes/demo/theme.css", b"changed")
with self.assertRaisesRegex(ValueError, "Source changed"):
PACKER.collect(self.root, self.VERSION)
def test_missing_language_bundle_is_rejected(self):
(self.root / "preview/rendered/en-US.js").unlink()
with self.assertRaisesRegex(ValueError, "Expected regular file"):
PACKER.collect(self.root, self.VERSION)
def test_modified_generated_files_are_rejected(self):
self.write("build/themes/demo/mail/base/head.tmpl", b"modified")
with self.assertRaisesRegex(ValueError, "checksum mismatch"):
PACKER.collect(self.root, self.VERSION)
# Documentation: recursive inclusion and optional root files.
def test_nested_documentation_and_release_notes_are_included(self):
documents = {
"docs/INDEX.zh-CN.md": "中文索引".encode("utf-8"),
"docs/images/README.zh-CN.md": "截图指南".encode("utf-8"),
"docs/release-notes/v28.0.0.zh-CN.md": "发布说明".encode("utf-8"),
".github/release-notes/v28.0.0.md": b"English release notes",
}
for name, data in documents.items():
self.write(name, data)
self.write("docs/private.tmp", b"not documentation")
files = PACKER.collect(self.root, self.VERSION)
for name, data in documents.items():
with self.subTest(document=name):
self.assertEqual(data, files[name])
self.assertIn("AGENTS.md", files)
self.assertNotIn("docs/private.tmp", files)
def test_optional_root_documents_may_be_absent(self):
for name in ("AGENTS.md", "THIRD_PARTY_NOTICES.md"):
(self.root / name).unlink()
files = PACKER.collect(self.root, self.VERSION)
self.assertNotIn("AGENTS.md", files)
self.assertNotIn("THIRD_PARTY_NOTICES.md", files)
self.assertIn("GITEA-LICENSE", files)
if __name__ == "__main__":
unittest.main()
-122
View File
@@ -1,122 +0,0 @@
name: Gitea Version Tracker
on:
schedule:
# Daily at 08:00 UTC — check for new Gitea releases
- cron: '0 8 * * *'
workflow_dispatch:
inputs:
version:
description: 'Gitea version (e.g. 1.27.0) — leave empty to auto-detect'
required: false
jobs:
track:
name: Track Gitea Release
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Determine latest Gitea version
id: version
run: |
if [ -n "${{ github.event.inputs.version }}" ]; then
NEW_VERSION="${{ github.event.inputs.version }}"
else
# Fetch latest release from GitHub API (go-gitea/gitea mirror)
RELEASE_JSON=$(curl -sSf https://api.github.com/repos/go-gitea/gitea/releases/latest)
NEW_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name')
fi
# Strip 'v' prefix if present (API returns v1.27.0, we use 1.27.0)
NEW_VERSION="${NEW_VERSION#v}"
echo "New Gitea version: $NEW_VERSION"
# Parse marker line AND embedded OFFSET to locate the data row
MARKER_INFO=$(grep -n 'TRACKER:QUICK-REF-MAX' COMPATIBILITY.md)
MARKER_LINE=$(echo "$MARKER_INFO" | cut -d: -f1)
OFFSET=$(echo "$MARKER_INFO" | sed 's/.*OFFSET=\([0-9]*\).*/\1/')
CURRENT=$(sed -n "$((MARKER_LINE + OFFSET))p" COMPATIBILITY.md | awk -F'|' '{gsub(/\*| /, "", $4); print $4}')
echo "Current max tested: $CURRENT"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "current_version=$CURRENT" >> $GITHUB_OUTPUT
if [ "$NEW_VERSION" = "$CURRENT" ]; then
echo "No new Gitea release detected. Skipping."
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "New version detected: $CURRENT → $NEW_VERSION"
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: COMPATIBILITY.md — Quick Reference (max tested version)
if: steps.version.outputs.skip != 'true'
run: |
NEW="${{ steps.version.outputs.new_version }}"
# Parse marker line and embedded OFFSET to locate the data row
MARKER_INFO=$(grep -n 'TRACKER:QUICK-REF-MAX' COMPATIBILITY.md)
MARKER=$(echo "$MARKER_INFO" | cut -d: -f1)
OFFSET=$(echo "$MARKER_INFO" | sed 's/.*OFFSET=\([0-9]*\).*/\1/')
TARGET=$((MARKER + OFFSET))
# Update column 4 (Max Tested) and column 5 (Status) in the pipe-separated row
awk -i inplace -F'|' -v OFS='|' -v ver="${NEW}" '
NR == '$TARGET' { $4 = " **" ver "** "; $5 = " ⏳ Pending Verification " }
{ print }
' COMPATIBILITY.md
- name: COMPATIBILITY.md — Version History table
if: steps.version.outputs.skip != 'true'
run: |
NEW="${{ steps.version.outputs.new_version }}"
TODAY=$(date +%Y-%m-%d)
# Parse marker line and embedded OFFSET for the insertion point
MARKER_INFO=$(grep -n 'TRACKER:VERSION-INSERT' COMPATIBILITY.md)
MARKER=$(echo "$MARKER_INFO" | cut -d: -f1)
OFFSET=$(echo "$MARKER_INFO" | sed 's/.*OFFSET=\([0-9]*\).*/\1/')
INSERT=$((MARKER + OFFSET))
ROW="| **${NEW}** | ${TODAY} | ⏳ Pending Verification | TBD |"
sed -i "${INSERT}a ${ROW}" COMPATIBILITY.md
- name: README.md — pending badge
if: steps.version.outputs.skip != 'true'
run: |
NEW="${{ steps.version.outputs.new_version }}"
# Badge: update version, change label tested→pending, color blue→yellow
sed -i "/TRACKER:BADGE/s/%20[0-9a-z.-]*%20tested-blue/%20${NEW}%20pending-yellow/" README.md
- name: Create Pull Request
if: steps.version.outputs.skip != 'true'
uses: peter-evans/create-pull-request@v7
with:
branch: track/gitea-${{ steps.version.outputs.new_version }}
commit-message: "docs: track Gitea ${{ steps.version.outputs.new_version }} — pending verification"
title: "Track Gitea ${{ steps.version.outputs.new_version }} — ⏳ Pending Verification"
body: |
Gitea **${{ steps.version.outputs.new_version }}** has been released.
This PR:
- Updates the compatibility matrix in `COMPATIBILITY.md`
- Marks the new version as pending in the `README.md` badge
- Marks the new version as **⏳ Pending Verification**
## Verification Checklist
- [ ] Check [Gitea changelog](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) for mail template changes
- [ ] Review diff of official templates: `git diff v${{ steps.version.outputs.current_version }}..v${{ steps.version.outputs.new_version }} -- templates/mail/ services/mailer/`
- [ ] Run `cd tools && go run . preview all` to confirm templates still render
- [ ] Run `cd tools && go test ./...` to verify push-notification data paths
- [ ] Update status from ⏳ to ✅ (compatible) or ❌ (breaking) in COMPATIBILITY.md
- [ ] Update the `Mail Template Changes` and `Breaking?` columns with accurate notes
- [ ] After verification, update `TRACKER:LATEST-VERIFIED` and the English/Chinese `TRACKER:LATEST-TESTED` lines; switch the README badge from pending to tested
## Semantic tracking
Once verified, update the tracked Gitea version:
- [ ] If templates changed, mark the top **Template Release** row `Unreleased` until the fix is tagged; if no change is needed, retain the verified release tag
- [ ] Update the README release status and compatibility matrix to distinguish source fixes from published packages
- [ ] Write `.github/release-notes/vX.Y.Z.md` before tagging; the release workflow uses this file for the published notes
- [ ] Tag a new release when shipping template fixes — `git tag vX.Y.Z && git push origin vX.Y.Z` (the release workflow packages automatically)
labels: compatibility, automated
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+56 -41
View File
@@ -2,57 +2,72 @@ name: Release
on:
push:
branches: [main]
tags: ['v*']
jobs:
validate:
name: Validate Templates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Test — mail template data contexts
working-directory: tools
run: go test ./...
- name: Lint — render all templates
working-directory: tools
run: go run . preview all
name: Validate
uses: ./.github/workflows/validate.yml
permissions:
contents: read
package:
name: Package & Release
if: startsWith(github.ref, 'refs/tags/v')
name: Package
needs: validate
runs-on: ubuntu-latest
runs-on: linux-amd64-docker-small
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Verify release notes
run: test -s ".github/release-notes/${GITHUB_REF_NAME}.md"
- name: Package themes/
run: |
VERSION=${GITHUB_REF#refs/tags/}
ARCHIVE="gitea-mail-templates-${VERSION}"
mkdir -p "dist/${ARCHIVE}/docs"
cp -r themes LICENSE README.md CONTRIBUTING.md COMPATIBILITY.md "dist/${ARCHIVE}/"
cp docs/README.zh-CN.md docs/CONTRIBUTING.zh-CN.md "dist/${ARCHIVE}/docs/"
cd dist
zip -r "${ARCHIVE}.zip" "${ARCHIVE}"
tar -czf "${ARCHIVE}.tar.gz" "${ARCHIVE}"
- name: Create Release
uses: softprops/action-gh-release@v3
with:
files: dist/*.zip,dist/*.tar.gz
fail_on_unmatched_files: true
body_path: .github/release-notes/${{ github.ref_name }}.md
token: ${{ secrets.GITEA_TOKEN }}
- uses: actions/setup-go@v5
with:
go-version: '1.25.x'
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Verify tag matches the snapshot
run: |
python - <<'PY'
import json
import os
with open("gitea.lock.json", encoding="utf-8") as lock_file:
lock = json.load(lock_file)
assert lock["tag"] == os.environ["GITHUB_REF_NAME"], (
"Release tag must match the pinned Gitea tag"
)
PY
- name: Generate preview
working-directory: tools
run: go run . preview all
- name: Package themes and preview
run: python -B .github/scripts/package_release.py --version "$TEMPLATE_RELEASE"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TEMPLATE_RELEASE: ${{ github.ref_name }}
- name: Prepare release runtime
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Release
uses: actions/gitea-release-action@v1.3.7
with:
server_url: ${{ github.server_url }}
repository: ${{ github.repository }}
token: ${{ secrets.GITEA_TOKEN }}
name: ${{ github.ref_name }}
tag_name: ${{ github.ref_name }}
body: ''
draft: false
prerelease: false
files: |
dist/gitea-mail-templates-${{ github.ref_name }}.zip
dist/gitea-mail-templates-${{ github.ref_name }}.tar.gz
+42
View File
@@ -0,0 +1,42 @@
name: Validate
on:
push:
branches: ['**']
pull_request:
workflow_call:
permissions:
contents: read
jobs:
validate:
name: Validate
runs-on: linux-amd64-docker-small
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25.x'
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Test workflow scripts
run: python -B -m unittest discover -s .github/scripts -p 'test_*.py'
- name: Verify snapshot
working-directory: tools
run: |
go run . upstream prepare
go run . upstream verify
- name: Tools test
working-directory: tools
run: go test ./...
- name: Build and render all themes and official languages
working-directory: tools
run: go run . preview all
+10 -5
View File
@@ -1,7 +1,10 @@
# Dependencies
node_modules/
# Go dependencies
vendor/
# Python test cache
__pycache__/
*.pyc
# OS files
.DS_Store
Thumbs.db
@@ -18,8 +21,10 @@ Desktop.ini
*.zip
*.tar.gz
# Package lock file
package-lock.json
# Preview generated files
preview/rendered.js
preview/rendered/
build/
/upstream/
dist/
node_modules/
-90
View File
@@ -1,90 +0,0 @@
# AGENTS.md — Gitea Mail Templates
## Project Overview
A curated collection of email template themes (10 visual styles) for self-hosted Gitea instances. Each theme contains 11 Go `html/template` files covering all Gitea notification email types.
## Repository Layout
```
themes/ # Template themes (10 styles, 11 .tmpl each = 110 source files)
aurora/ # Ethereal / Dreamlike
bloom/ # Creative / Startup (glassmorphism)
ember/ # Community / Open Source
heritage/ # Education / Research
horizon/ # Enterprise / Corporate
ink/ # Editorial / Publishing
mono/ # Minimal / Swiss design
neon/ # Cyberpunk / Gaming
terminal/ # Developers / Tech
terra/ # Nature / Sustainability
tools/ # Go CLI tooling (modular, zero dependencies)
tools.go # Main entry point
cli/ # CLI subcommands: list, create, delete, preview
config/ # Config types and templates_config.json loading
data/ # templates_config.json — single source of truth for template metadata
preview/ # Template rendering engine (funcs, locale, engine)
go.mod # Go module (stdlib only, zero dependencies)
preview/ # Browser-based live preview
index.html # SPA with style/template/client/viewport switching
rendered.js # Pre-rendered HTML (generated, committed for clone-and-preview)
docs/ # Bilingual documentation (English + Simplified Chinese)
```
## Working With Templates
### Template Files
- All `.tmpl` files use Go `html/template` syntax
- Must use only Gitea 28's built-in template functions: `AppUrl`, `DotEscape`, `QueryEscape`, `ShortSha`, `HTMLFormat`, `PathEscapeSegments`, `FormatByteSize`
- Must use only official Gitea translation keys (`mail.*` namespace)
- Never reference `.DisplayName` in templates where the data context lacks it (collaborator, transfer, release, workflow_run, assigned, default)
- Each style must have all 11 template types
### Adding a New Theme
1. Scaffold the new theme: `cd tools && go run . create <name>` — creates the full directory structure with placeholder `.tmpl` files for all 11 email types
2. Write all 11 `.tmpl` files with unique visual design
3. Run `cd tools && go run . preview all` to regenerate preview data
4. Update README.md style gallery table
### Preview System
- `preview/index.html` loads `preview/rendered.js` (pre-rendered by Go) and displays in iframes
- Supports theme/template switching, view mode (Modern/Source), and viewport toggle (Desktop 1386x780 / Mobile 390x780)
- Keyboard navigation: `←→` cycles focus between Theme/Template/View selects, `↑↓` selects within the focused dropdown, `d`/`m` toggles viewport
- `REGISTRY` and `PARAMS` are auto-generated from `templates_config.json` — no manual syncing needed
- Static preview (open `index.html` directly) — works via `file://` protocol with Modern and Source views
- Dev server (`go run . dev`) — pure Go HTTP server with SSE live reload; watches `themes/` for `.tmpl` changes, re-renders in-process, and pushes reload events to the browser
### Build Tool
- `tools/tools.go` is the main entry point for the modular CLI
- Subcommands: `list`, `create`, `delete`, `preview`, `dev`
- Template metadata lives in `tools/data/templates_config.json` — the single source of truth
- `tools/config/` handles config loading and data flattening
- `tools/preview/` implements the rendering engine (template funcs, locale, engine, markSafeHTML)
- `tools/cli/` implements CLI subcommands using `github.com/urfave/cli/v2`
- `tools/preview/server.go` pure Go dev server with SSE live reload and in-process template re-rendering
- Uses Go's native `html/template` package for template rendering
## Versioning
- Release tags identify actual downloadable packages; the compatibility matrix distinguishes released tags from source-only fixes
- The current release is **v28.0.0**, verified against Gitea 28.0.0. Gitea 28 removes `FileSize` in favor of `FormatByteSize`, so v28.0.0 is not compatible with Gitea 1.25.0–1.27.3; use v1.27.3 for those versions. The quick-reference table in `COMPATIBILITY.md` lists the active release first; the tracker workflow updates that row by position
- When a new Gitea version is checked: update the top `COMPATIBILITY.md` row and README badge; keep unreleased fixes distinct from the published release, and update verified/tested wording only after verification
- Tag a new release (`vX.Y.Z`) only when the template content itself changes — the release workflow packages automatically on tag push
- Before tagging, add `.github/release-notes/vX.Y.Z.md`; the release workflow validates the file, runs `go test ./...`, and publishes those reviewed notes with the archives
- Keep the `TRACKER:` markers in `COMPATIBILITY.md` / `README.md` adjacent to their rows so the automated workflow keeps parsing them
## Commit Conventions
- `style(name):` — template changes for a specific theme
- `preview:` — preview tooling changes
- `tools:` — Go CLI/build tooling changes
- `docs:` — documentation and translations
- `fix:` — bug fixes
- `project:` — README, LICENSE, AGENTS.md, meta
- `refactor:` — code restructuring (e.g. modularization)
- `chore:` — maintenance (config updates, build scripts)
## Constraints
- No JavaScript framework dependencies — preview is vanilla JS
- No external Go dependencies — build uses stdlib only
- Templates must remain compatible with Gitea's `html/template` execution environment
- Preview works with `file://` protocol (no server needed)
+49 -107
View File
@@ -1,124 +1,66 @@
# Gitea Compatibility
---
[简体中文](docs/COMPATIBILITY.zh-CN.md)
This document tracks the compatibility between **Gitea Mail Templates** releases and **Gitea** versions.
This document describes the compatibility of Gitea Mail Templates with different Gitea versions.
## Quick Reference
## Compatibility Matrix
<!-- TRACKER:QUICK-REF-MAX OFFSET=3 -->
| Template Release | Min Gitea | Max Tested Gitea | Status |
|-----------------|-----------|-----------------|--------|
| **v28.0.0** | **28.0.0** | **28.0.0** | ✅ Active |
| **v1.27.3** | **1.25.0** | **1.27.3** | ✅ Superseded; release emails fail on Gitea 28.0.0 (`FileSize` removed) |
| **v1.27.2** | **1.25.0** | **1.27.3** | ⚠️ Push notices fail in Bloom, Ember, and Heritage on Gitea 1.27.1+ |
| **v1.0.1** | **1.25.0** | **1.27.0** | ✅ Superseded; push notices need newer release on 1.27.1+ |
| **v1.0.0** | **1.25.0** | **1.26.4** | ✅ Superseded |
Select the recommended template release for the Gitea version you run. `[PASS]` identifies a combination recorded as compatible; `[PENDING]` means verification is incomplete.
> **Latest verified:** Release v28.0.0 passes the Gitea 28.0.0 mail-template, mailer-context, function, and translation-key source audit plus all-theme rendering tests. This release requires Gitea 28.0.0; use v1.27.3 for Gitea 1.25.0–1.27.3. <!-- TRACKER:LATEST-VERIFIED -->
> [!NOTE]
> Historical entries reflect the project's existing compatibility assessments. They do not mean that every patch version is retested for each release.
## Versioning
| Gitea version | Recommended template release | Status | Notes |
|---|---|---|---|
| 28.1.0 | — | [PENDING] | Compatibility verification pending |
| 28.0.0 | **v28.0.0** | [PASS] | Gitea replaced the mail function `FileSize` with `FormatByteSize` |
| 1.27.3 | **v1.27.3** | [PASS] | Pull request push notification commit links fixed in every theme |
| 1.27.2 | **v1.27.3** | [PASS] | The matching v1.27.2 template release has push notification defects in Bloom, Ember and Heritage |
| 1.27.1 | **v1.27.3** | [PASS] | Older v1.0.x templates use obsolete push commit fields |
| 1.27.0 | — | [PENDING] | Gitea uses a new commit data structure; v1.27.3 may be compatible but has not been verified against this version |
| 1.26.4 | **v1.0.1** | [PASS] | — |
| 1.26.3 | **v1.0.1** | [PASS] | — |
| 1.26.2 | **v1.0.1** | [PASS] | — |
| 1.26.1 | **v1.0.1** | [PASS] | — |
| 1.26.0 | **v1.0.1** | [PASS] | — |
| 1.25.5 | **v1.0.1** | [PASS] | — |
| 1.25.0 | **v1.0.1** | [PASS] | — |
The release tag identifies the downloadable template package. The supported Gitea version may be appended in parentheses in this compatibility matrix; the parenthesized version is not a Git tag. An **unreleased** row describes fixes available in the repository but not yet in a downloadable release.
| Gitea version | Template release |
|---------------|------------------|
| 28.0.0 | **v28.0.0**; older releases use the removed `FileSize` function in release emails |
| 1.27.3 | **v1.27.3**; v1.27.2 has a push-notification issue in three themes |
| 1.27.2 | **v1.27.3**; v1.27.2 has the same issue |
| 1.27.1 | **v1.27.3**; v1.27.2 has the same issue |
- The [tracker workflow](.github/workflows/gitea-tracker.yml) opens a PR when a new Gitea release appears, marking it ⏳ Pending Verification.
- After verification, update the top **Template Release** row. Keep fixes on `main` marked **unreleased** until a new tag is published; the [release workflow](.github/workflows/release.yml) packages the archive on tag push.
- The older `v1.0.1` tag predates the Gitea 1.27.1 push notification data fix and should not be used for Gitea 1.27.1 or newer.
- Gitea 28.0.0 replaces the mail-template `FileSize` function with `FormatByteSize`. The two functions are not interchangeable across these Gitea versions; use the matching template release.
## Check Your Gitea Version
```bash
# On your Gitea server:
gitea --version
# Or check the web UI footer / Site Administration → Monitoring
```
> [!NOTE]
> Gitea 1.24.x and earlier use a different mail template layout and are `[UNSUPPORTED]`; Gitea Mail Templates no longer provides separate support for them. The v1.0.0 template tag has been superseded by v1.0.1 and is no longer recommended separately.
## Gitea Version History — Mail Template Impact
<!-- TRACKER:VERSION-INSERT OFFSET=2 -->
| Gitea | Release Date | Mail Template Changes | Breaking? |
|-------|-------------|----------------------|-----------|
| **28.0.0** | 2026-09-29 | `FileSize` removed and `FormatByteSize` added; mail templates now have `mail/`-prefixed internal names and shared head/footer partials; workflow emails gain status-icon fields. Our custom template paths and referenced data fields remain valid. | **Yes** (release attachment formatting) |
| **1.27.3** | 2026-08-29 | No upstream mail template, mailer, or locale changes; an existing push-notification defect in three themes is fixed in template release v1.27.3 | No upstream break |
| **1.27.2** | 2026-08-14 | None — security + bug fixes | No |
| **1.27.1** | 2026-07-27 | Push commit data paths changed: .ID → .UserCommit.GitCommit.ID (#38467); older custom templates can fail on push notifications | Yes (old .ID paths) |
| **1.27.0** | 2026-07-13 | None — no mail template changes | No |
| **1.26.4** | 2026-06-21 | None — hotfix release | No |
| **1.26.3** | 2026-06-20 | None — security release | No |
| **1.26.2** | 2026-05-20 | None — security + bug fixes | No |
| **1.26.1** | 2026-04-22 | None — bug fixes | No |
| **1.26.0** | 2026-04-19 | AppURL cleanup; SanitizeHTML deprecated → use HTMLFormat | No |
| **1.25.5** | 2026-03-10 | None — security + maintenance | No |
| **1.25.0** | 2025 | **Directory restructure** — templates moved to `mail/<category>/<type>.tmpl` (PR #35150); subject/body split with `---` separator; template preview support added | **Yes** (structural) |
| **≤ 1.24.x** | — | Flat directory structure under `custom/templates/mail/` | ❌ Unsupported |
This table describes changes in **Gitea**, not fixes in this template repository. “None” means that the Gitea version introduces no relevant mail template changes; recommended archives are listed in the compatibility matrix above.
| Gitea | Release date | Mail template changes | Impact |
|---|---|---|---|
| **28.1.0** | 2026-10-06 | [PENDING] | [PENDING] |
| **28.0.0** | 2026-09-29 | Mail function `FileSize` renamed to `FormatByteSize` ([PR](https://github.com/go-gitea/gitea/pull/39083)) | **Breaking change:** old release attachment templates fail |
| | | Introduced `templates/mail/base/` ([PR](https://github.com/go-gitea/gitea/pull/38396)) | **Breaking change:** older Gitea versions cannot directly use the new mail templates |
| **1.27.3** | 2026-08-29 | None | None |
| **1.27.2** | 2026-08-14 | None | None |
| **1.27.1** | 2026-07-27 | Bundled push template updated to use `.UserCommit.GitCommit` ([PR](https://github.com/go-gitea/gitea/pull/38467)) | **Breaking change: an existing defect remains unresolved.** Old custom pull request push templates can fail |
| **1.27.0** | 2026-07-13 | Push commit data moved under `.UserCommit.GitCommit` ([ISSUE](https://github.com/go-gitea/gitea/issues/38469)) | **Breaking change:** old pull request push templates fail |
| **1.26.4** | 2026-06-21 | None | None |
| **1.26.3** | 2026-06-20 | None | None |
| **1.26.2** | 2026-05-20 | None | None |
| **1.26.1** | 2026-04-22 | None | None |
| **1.26.0** | 2026-04-19 | None | None |
| **1.25.5** | 2026-03-10 | None | None |
| **1.25.0** | 2025 | Custom mail paths and subject/body format changed ([PR](https://github.com/go-gitea/gitea/pull/35150)) | **Breaking change:** older template layouts are unsupported |
| **≤ 1.24.x** | — | Legacy custom mail layout | [UNSUPPORTED] |
## Template Variable Reference
All 11 template types use only Gitea built-in variables and functions. Verified against Gitea source (`services/mailer/` + `modules/templates/mail.go`).
Official templates define the context for each mail type. For specific mail templates, functions and variables used during development, refer to the official mail template documentation [1](#notes).
### Template Functions (available in all templates)
## Translation Keys
| Function | Since Gitea | Notes |
|----------|------------|-------|
| `AppName` | ≤ 1.21 | Application name |
| `AppUrl` | ≤ 1.21 | Application base URL |
| `AppDomain` | ≤ 1.21 | Server domain |
| `DotEscape` | ≤ 1.21 | Prevents auto-linking of dotted text |
| `QueryEscape` | ≤ 1.21 | URL query encoding |
| `PathEscapeSegments` | ≤ 1.21 | Per-segment path encoding |
| `ShortSha` | ≤ 1.21 | Truncated commit hash |
| `FormatByteSize` | 28.0.0 | Human-readable IEC file size; replaces `FileSize` |
| `HTMLFormat` | ≤ 1.21 | Render string as safe HTML |
| `Iif` | ≤ 1.21 | Inline conditional |
| `dict` | ≤ 1.21 | Build maps from key-value pairs |
| `Eval` | ≤ 1.21 | Evaluate template tokens |
| `StringUtils` | ≤ 1.21 | String manipulation helpers |
| `SliceUtils` | ≤ 1.21 | Slice manipulation helpers |
| `JsonUtils` | ≤ 1.21 | JSON helpers |
| `DumpVar` | ≤ 1.21 | Debug variable dump |
| `SanitizeHTML` | ≤ 1.21 | **Deprecated in 1.26** — use `HTMLFormat` |
Official templates reference `mail.*` and `actions.runs.attempt`. AST-based checks cover `.locale.Tr`, `$.locale.Tr` and both plural keys in `TrN`, including nested pipelines. Every referenced key must exist in the locked English catalog.
### Data Contexts by Template
---
### Notes
| Template | Key Variables |
|----------|--------------|
| `user/auth/activate` | `DisplayName`, `Code`, `ActiveCodeLives` |
| `user/auth/activate_email` | `DisplayName`, `Code`, `Email`, `ActiveCodeLives` |
| `user/auth/register_notify` | `DisplayName`, `Username` |
| `user/auth/reset_passwd` | `DisplayName`, `Code`, `ResetPwdCodeLives` |
| `org/team_invite` | `Inviter`, `Team`, `Organization`, `InviteURL`, `Invite` |
| `repo/collaborator` | `Subject`, `RepoName`, `Link` |
| `repo/transfer` | `Doer`, `User`, `Repo`, `Link`, `Destination` |
| `repo/release` | `Release` (with `Publisher`, `TagName`, `Title`, `RenderedNote`, `Attachments`), `Link` |
| `repo/actions/workflow_run` | `Run` (with `WorkflowID`, `HTMLURL`), `Jobs`, `RunStatusText` |
| `repo/issue/assigned` | `Doer`, `Issue`, `Link`, `IsPull` |
| `repo/issue/default` | `Doer`, `Issue`, `Link`, `Body`, `ActionName`, `Comment`, `IsPull`, `IsMention`, `ReviewComments`, `CanReply` |
> ⚠️ **`.DisplayName`** is not available in collaborator, transfer, release, workflow_run, assigned, and default templates — do not reference it.
### Translation Keys
All templates use Gitea's official `mail.*` translation namespace. Every referenced key was checked against Gitea 28.0.0's English locale file.
## How Compatibility Is Verified
1. **Automated lint** — CI renders all templates via `go run . preview all` on every push; the preview function map mirrors Gitea 28.0.0
2. **Source audit** — Template data contexts are cross-referenced against Gitea's `services/mailer/` package
3. **Regression tests** — Go tests render push notifications and release attachments in all 10 themes
4. **Release checklist** — Each release confirms the max-tested Gitea version in this file
## Automated Tracking
A [workflow](.github/workflows/gitea-tracker.yml) runs daily (UTC 08:00) to detect new Gitea releases. When a new version is found, it automatically creates a PR updating the matrix and badges with a **⏳ Pending Verification** status. Manual trigger is also available via `workflow_dispatch`. Once verification passes, update the compatibility matrix; publish a new template tag when template changes require a release (see [Versioning](#versioning)).
## Reporting Issues
If you find a compatibility problem with a specific Gitea version:
1. Check the [Gitea changelog](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) for recent mail template changes
2. Open an issue with: your Gitea version, which template, and the error
[1](#notes): [Mail templates | Gitea Documentation](https://docs.gitea.com/administration/mail-templates/)
+183 -81
View File
@@ -1,100 +1,202 @@
# Contributing to Gitea Mail Templates
Thanks for your interest in contributing! This project aims to provide a diverse, well-maintained collection of email templates for the Gitea ecosystem.
[简体中文](docs/CONTRIBUTING.zh-CN.md)
---
Contributions to themes, tooling, tests, documentation and translations are welcome. This guide covers the current source architecture; installation and release selection are described in the [README](README.md#installation).
## Ways to Contribute
## Local Development
### Adding a New Style
1. Scaffold the new style: `cd tools && go run . create <your-style-name>` — this creates the directory structure with placeholder `.tmpl` files for all 11 email types
2. Edit each `.tmpl` file in `themes/<your-style-name>/` with your unique visual design
3. Regenerate the preview: `cd tools && go run . preview all` — the build script auto-discovers all theme directories under `themes/` and generates the theme selector dynamically
4. Submit a PR with screenshots of rendered emails (≤ 50 KiB each, 10–20 KiB recommended)
### Style Guidelines
- Each style must include **all 11 template types** listed in the README
- Use only Gitea's built-in template functions — check the [Gitea source](https://github.com/go-gitea/gitea) for reference
- Translation keys must come from Gitea's official locale files (`mail.*` namespace)
- **Never reference `.DisplayName`** in templates where the data context lacks it (collaborator, transfer, release, workflow_run, assigned, default)
- Design for 600px max-width email clients
- Test against major email clients (Apple Mail, Gmail, Outlook) when possible
### Bug Reports
If a template doesn't render correctly:
1. Check that all referenced Go template variables exist — compare against the Gitea source mail templates
2. Verify translation keys match Gitea's locale files
3. Confirm `.DisplayName` isn't used in templates that lack it
4. Regenerate the preview: `cd tools && go run . preview all`
5. Open an issue with: the style name, which email type, and the error or unexpected output
### Documentation Improvements
Documentation updates, preview screenshots, installation guides, and translations are always welcome.
---
## Development Setup
- **Go 1.21+** for template rendering and the CLI tool
### Previewing Locally (Static)
1. Run `cd tools && go run . preview all` to generate rendered data
2. Open `preview/index.html` directly in a browser — no server needed
3. Use the theme switcher, template selector, and view mode toggles
### Dev Server (Live Reload)
Use **Go 1.24 or later**. From the repository root, prepare the dependencies and locked official inputs, then run the checks and generate the preview:
```bash
cd tools && go run . dev
# → http://localhost:3456
cd tools
go mod download
go run . upstream prepare
go test ./...
go run . preview all
```
- Watches `themes/**/*.tmpl` — auto-rebuilds on save
- Pure Go HTTP server with SSE push — no external dependencies
- Re-renders templates in-process and pushes reload events to the browser
- Terminal output: `themes/aurora/mail/repo/release.tmpl changed` → `[Builder] Rebuild done in 45ms`
Open `preview/index.html` in a browser, or run `go run . dev` from `tools/` and visit [http://127.0.0.1:3456](http://127.0.0.1:3456) for live reload. The development server watches themes, the framework, the lock/cache and preview fixtures.
### Integration Testing
The CLI uses urfave/cli and the x/net HTML parser. Python 3.11 or later is used by the documentation and packaging checks.
Deploy the templates to a Gitea instance and verify with real transactional emails.
The admin test email (**Site Administration > Configuration > Mailer > Send Test Email**)
does not use custom mail templates — it follows a built-in code path.
### Common Commands
The most reliable method is to trigger a real notification. For example, the password
reset flow:
Run these commands from `tools/`. Place flags before positional arguments.
1. Log out and click **"Forgot password"** on the login page
2. Enter your account email and submit
3. Check the password reset email — it will render with your custom mail templates
| Command | Result |
|---|---|
| `go run . list` | List available themes |
| `go run . create my-theme` | Create theme metadata and CSS |
| `go run . build my-theme` | Generate one theme's installable templates |
| `go run . build all` | Generate every theme |
| `go run . preview all` | Build every theme and render all official languages |
| `go run . dev` | Start the development server on port 3456 |
| `go run . dev --port 3457` | Use a different local port |
| `go run . delete my-theme` | Remove the named theme's source directory |
---
`build` writes to `build/themes/<name>/mail/` and records file and source hashes in `build.json`. `preview` also builds the templates, then writes `preview/rendered.js` and `preview/rendered/<locale>.js`. These generated files and the downloaded inputs are ignored by Git. Keep them out of contributions.
## Adding a Theme
1. Run `go run . create my-theme` from `tools/`.
2. Edit `themes/my-theme/theme.json` and `theme.css`.
3. Run `go test ./...` and `go run . preview all` from `tools/`.
4. Check desktop and mobile layouts in English, Simplified Chinese and at least one other official language. Include screenshots with the pull request.
Theme names start with a lowercase letter and may contain lowercase letters, digits and hyphens. A theme directory contains only `theme.json` and `theme.css`.
```json
{
"name": "my-theme",
"description": "A short description of the theme",
"mode": "framed",
"layout": "standard"
}
```
| Mode | Generated presentation |
|---|---|
| `framed` | Shared controls and a framework layout, styled with the theme's CSS. New themes use this mode with the `standard` layout. |
| `shared` | CSS in the official head partial and the official footer partial; no additional framework controls or per-mail overrides. |
### Shared Framework
Headers, action buttons, fallback links, sidebars and footers live in `framework/mail/base/`. Layout presets live in `framework/layouts/`; the optional `layout` field selects a preset for framed themes. Colors, fonts and spacing belong in theme CSS.
`tools/builder/framework.go` adapts official action values and translation keys for the shared controls. Official templates retain responsibility for notification conditions, subjects, attachments and contextual links. Changes to controls or layout structure belong in the framework so all themes use the same adaptation logic.
Layout fragments must form balanced presentation markup. `__HEADER__` inserts the shared header, `__MAIL_TYPE__` expands to the discovered mail ID, and `__SIDEBAR__` inserts the shared sidebar in footer fragments. Deployment logos use `{{AppUrl}}assets/img/favicon.png`; generated preview data embeds the downloaded icon for offline display.
## Design Guidelines
- Use email-compatible CSS. Theme CSS must not load external resources, generate text or hide official content.
- Use presentation markup for layout fragments. The validator accepts `table`, `tbody`, `tr`, `td`, `div` and `span` with supported presentation attributes.
- When editing an existing theme, preserve its visual identity: palette, typography, borders, header, buttons and layout.
- Check desktop and 390px mobile previews, including long text and URLs. Test Gmail, Outlook and Apple Mail where available; the browser preview does not emulate mail clients.
- Keep gallery images in PNG format, at most 50 KiB each; 10–20 KiB is preferred. Follow the [capture guide](docs/images/README.md) for consistent images.
## Official Snapshot Updates
`gitea.lock.json` records a stable Gitea 28+ tag, its commit and per-file SHA-256 hashes. Official mail templates, locale catalogs, the favicon, license and reviewed adapter references are downloaded into `build/upstream/`. Only the generated root lock is committed; official files are not maintained in the source tree.
### Command Reference
Run from `tools/`. Each upstream subcommand accepts `--root <repository-root>`, defaulting to `..` relative to the working directory. Place the flag after the subcommand and quote paths containing spaces:
```powershell
go run . upstream verify --root "C:\Projects\GiteaMailTemplates"
```
| Command | Behavior |
|---|---|
| `go run . upstream prepare` | Read the root lock. Download and validate its exact inputs if the cache is absent; otherwise verify the existing cache offline. Leaves the root lock unchanged. |
| `go run . upstream verify` | Check the existing cache against the root lock, offline and read-only. Reports `[PASS]` and lists non-English missing keys as `[FALLBACK]`. |
| `go run . upstream sync --tag vX.Y.Z` | Resolve an explicit stable Gitea 28+ tag through the GitHub API, download and validate its files, then replace the cache and generate the root lock. |
`sync` uses `api.github.com`; file downloads use `raw.githubusercontent.com`. These commands do not accept a local Gitea checkout, authentication tokens or an implicit `latest` version. Network errors and API rate limits stop the operation. Go may separately need network access to download its toolchain or modules.
### Existing Clone and Offline Use
`build`, `preview` and `dev` prepare inputs automatically. They require the committed root lock and validate cached files before use. Once the cache, Go toolchain and dependencies are available, builds can run offline.
Use `upstream verify` to check the cache independently. It validates official file hashes, reviewed adapter references and official template keys in the English catalog. Build and rendering checks also cover framework translation keys and primary-action anchors, so cache verification is only one part of compatibility testing.
### Missing Lock and Explicit Version Updates
For an existing clone, restore the tracked `gitea.lock.json` if it is missing. The copy at `build/upstream/lock.json` cannot replace it. When intentionally initializing the lock, run:
```bash
cd tools
go run . upstream sync --tag v28.0.0
go run . upstream verify
go test ./...
go run . preview all
```
To review a different version, specify its tag explicitly. Review the lock diff, update the framework adapter and fixtures as needed, and complete the checks before updating compatibility records. `sync` changes the cache and lock only; documentation and release publication are separate steps. Use `prepare` for a missing cache.
Changes to the reviewed translation or mail-renderer sources require an adapter review before their reference hashes can be updated. Official English must contain all referenced keys; other languages use English fallback. New mail types require framework alignment and fixtures in `tools/data/templates_config.json`, which supplies preview metadata and example contexts. Keep integer values as integers in JSON fixtures for Go formatting.
Network or validation failures before cache replacement leave existing inputs unchanged. Cache replacement and root-lock writing are separate operations; if a filesystem error interrupts them, inspect both before retrying.
### Cache Recovery
`prepare` reports corrupt, incomplete or lock-mismatched caches without repairing them. `sync` also refuses to replace a non-empty invalid snapshot. Inspect the error and move the generated cache aside before preparing it again from the reviewed root lock.
For example, in PowerShell **from the repository root**, with an unused backup name:
```powershell
Move-Item -LiteralPath .\build\upstream -Destination .\build\upstream.backup
Set-Location tools
go run . upstream prepare
go run . upstream verify
```
Preserve the rest of `build/` and leave official cached files unedited. To return to a previous snapshot, restore its reviewed root lock and rebuild the cache in the same way. Stop `dev` before replacing inputs, and avoid concurrent processes writing to the same cache.
### Known Upstream Formatting Issue
Gitea v28.0.0 contains a Polish `mail.team_invite.text_1` placeholder defect. Preview preserves the official output and reports `[UPSTREAM-WARN]`. The exception matches the exact reviewed source text; other formatting errors fail rendering. Report upstream defects without modifying the cached locale files.
## Verification and Release
### Checks for a Pull Request
From `tools/`, run `go test ./...` and `go run . preview all`. Tests cover deterministic generation, changes to action anchors, translation keys, and notification subjects, text and links across all themes and languages. Fixtures include push, review, reply, workflow and attachment branches. Shared controls and branding are accounted for separately from official notification content.
For documentation or packaging changes, run from the repository root:
```bash
python -B -m unittest discover -s .github/scripts -p 'test_*.py'
```
Keep English and Simplified Chinese guides aligned when changing shared instructions. Include a description of the change, relevant checks and screenshots for visual changes in the pull request.
### Preparing a Release
Release tags must match the locked Gitea version. The current source refactor is unreleased; retain existing v28.0.0 and historical assets.
1. Complete the Go tests, documentation checks and preview generation.
2. Update documentation and compatibility records with the verification results. Release notes are optional and are not used as the Release body.
3. Generate all themes and language bundles with `go run . preview all` from `tools/`.
4. Package the release and verify its contents before publication.
For manual packaging, run from the repository root:
```bash
python .github/scripts/package_release.py --version vX.Y.Z --output <new-output-directory>
```
Replace the version and output placeholder with the reviewed tag and a new directory. The script checks source and generated hashes, includes all language bundles, excludes stale theme builds, and refuses to overwrite existing archives.
The release workflow uses the same script to package templates, previews, documentation, the upstream license and provenance. It publishes only the archives, with the tag as the Release title and an empty body. Version labels, badges and compatibility records are maintained manually; review archive documentation before tagging. Confirm Actions support and write permissions on the configured Gitea host before relying on automated publication. See [version maintenance](COMPATIBILITY.md#version-maintenance).
### Gitea Workflow Configuration
The release workflow uses the `linux-amd64-docker-small` runner label. The job image must support the Node.js actions used for checkout and toolchain setup, as well as Git and a POSIX shell. Go and Python are installed by the setup steps.
Publication uses [`gitea-release-action@v1.3.7`](https://gitea.com/actions/gitea-release-action/src/tag/v1.3.7/action.yml) directly, with the instance URL, repository, built-in `GITEA_TOKEN`, tag, title and the two archive paths. The Release body is empty when created; no notes file or Issue reminder is required. The repository must allow release writes. Action URLs explicitly use `https://gitea.com` rather than depending on `DEFAULT_ACTIONS_URL`.
Before publication, `actions/setup-node@v4` prepares Node.js 22. The current Gitea runner executes JavaScript actions with `node` from `PATH`; this provides the runtime interfaces used by the release action without `--experimental-fetch`. On repeated publication of an existing tag, the action preserves an existing body and replaces same-name attachments. It does not provide the previous script's draft-until-all-uploads-complete behavior. Review the release and attachments before retrying a failed publication.
## Reporting Problems
Include the Gitea version, template release or source commit, theme, mail type, language and steps to reproduce. For source-build problems, include the snapshot tag/commit and output from `go run . upstream verify` or `go run . preview all`. For display problems, include the mail client and a screenshot with personal data removed.
## Commit Conventions
Any readable commit message in semantic format is welcome. Such as:
| Prefix | Scope |
|---|---|
| `style(<name>):` | Theme presentation |
| `preview:` | Browser preview |
| `tools:` | CLI, build and snapshot tools |
| `docs:` | Documentation and translations |
| `fix:` | Bug fixes |
| `project:` | Repository configuration and project structure |
| `refactor:` | Code restructuring |
| `chore:` | Maintenance |
- `style(horizon|terminal|ember|bloom|heritage|neon|mono|terra|ink|aurora):` — template changes
- `preview(*):` — preview tooling changes
- `tools(*):` — Go build script changes
- `docs(*):` — documentation and translations
- `fix(*):` — bug fixes
- `project(*):` — README, LICENSE, AGENTS.md, meta
## Translations and License
---
## Translations
- English
- [简体中文](docs/CONTRIBUTING.zh-CN.md)
---
## License
By contributing, you agree that your contributions will be licensed under the MIT License.
The [Simplified Chinese guide](docs/CONTRIBUTING.zh-CN.md) covers the same workflow. Contributions are licensed under MIT. Retain Gitea copyright and the upstream license when distributing derived templates; see [third-party notices](THIRD_PARTY_NOTICES.md).
+138 -132
View File
@@ -1,204 +1,210 @@
# Gitea Mail Templates
A curated collection of professionally designed, audience-driven email templates for self-hosted [Gitea](https://about.gitea.com) instances.
[![Gitea](https://img.shields.io/badge/Gitea-28.0.0%20%7C%2028.0.0%20tested-blue)](COMPATIBILITY.md) <!-- TRACKER:BADGE -->
> **Release v28.0.0 — verified with Gitea 28.0.0 — 110 template files, 10 visual styles, 11 email types each.**
# Gitea Mail Template
---
## Philosophy
[简体中文](docs/README.zh-CN.md)
Most self-hosted Gitea instances use the default plain email templates. This project provides **ready-to-deploy, visually polished alternatives** — each designed for a specific community or audience, so you can pick the one that feels right for your users.
Gitea Mail Template offers a range of email template styles for self-hosted [Gitea](https://about.gitea.com) instances.
The templates on `main` can replace Gitea's built-in mail templates without patches, plugins, or forks. Check the [compatibility matrix](COMPATIBILITY.md) before using a published archive; older releases may have version-specific limitations.
> Latest release: [v28.0.0](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/releases/latest)
---
The repository includes several themes for Gitea emails, suitable for most deployment scenarios and use cases. Before installing, consult the [compatibility matrix](COMPATIBILITY.md#compatibility-matrix) for your Gitea version and choose the corresponding release archive.
## Style Gallery
| Preview | Style | Audience | Character |
|---|---|---|---|
| ![Horizon](docs/images/horizon.png) | **Horizon** | Enterprise / Corporate | Blue accent, slate typography, centered cards |
| ![Terminal](docs/images/terminal.png) | **Terminal** | Developers / Tech | Dark mode, monospace, green CLI accents |
| ![Ember](docs/images/ember.png) | **Ember** | Community / Open Source | Warm amber, rounded, humanist, inclusive |
| ![Bloom](docs/images/bloom.png) | **Bloom** | Creative / Startup | Frosted glass, soft blue light, iridescent accents |
| ![Heritage](docs/images/heritage.png) | **Heritage** | Education / Research | Navy and gold, serif, classic, authoritative |
| ![Neon](docs/images/neon.png) | **Neon** | Gaming / Web3 / Creative Tech | Cyberpunk neon glow, hot pink & cyan, synthwave energy |
| ![Mono](docs/images/mono.png) | **Mono** | Design Studios / Editorial | Swiss brutalist, black & white, red accent, zero radius |
| ![Terra](docs/images/terra.png) | **Terra** | Sustainability / Wellness | Warm earth tones, organic textures, humanist serif |
| ![Ink](docs/images/ink.png) | **Ink** | Publishing / News / Literature | Editorial print, navy & gold, newspaper layout, drop caps |
| ![Aurora](docs/images/aurora.png) | **Aurora** | Premium SaaS / Mindfulness | Ethereal light gradients, deep purple & teal, atmospheric glow |
The gallery shows how the email templates render in the preview tool.
> Images are screenshots from the [live preview](preview/index.html). See [docs/images/README.md](docs/images/README.md) for capture instructions.
| Preview | Theme | Style Features |
|---|---|---|
| ![Aurora](docs/images/aurora.png) | **Aurora** | Dark purple background, teal accents and a soft glow |
| ![Bloom](docs/images/bloom.png) | **Bloom** | Light blue gradients, rounded cards and buttons |
| ![Ember](docs/images/ember.png) | **Ember** | Warm orange tones, serif headings and rounded buttons |
| ![Heritage](docs/images/heritage.png) | **Heritage** | Navy and gold, double borders and serif fonts |
| ![Horizon](docs/images/horizon.png) | **Horizon** | Blue accents, gray text and a centered white card |
| ![Ink](docs/images/ink.png) | **Ink** | Newspaper layout, a sidebar, serif fonts and drop caps |
| ![Mono](docs/images/mono.png) | **Mono** | Black and white, red accents and square borders |
| ![Neon](docs/images/neon.png) | **Neon** | Dark background, pink and cyan accents and glow effects |
| ![Terminal](docs/images/terminal.png) | **Terminal** | Dark background, monospace fonts and green accents |
| ![Terra](docs/images/terra.png) | **Terra** | Earth tones, terracotta buttons and serif fonts |
[**Live preview gallery**](preview/index.html) — open in a browser for an interactive style switcher with desktop/mobile viewports and view mode (Modern, Source).
---
Screenshots show only the current default build. Use the [local preview](#preview) to view other email types and languages. See the [screenshot guide](docs/images/README.md) when updating screenshots.
## Installation
### Quick Start
### Choose an Installation Source
Choose a style, then copy the `mail/` directory into your Gitea custom templates path:
Run `gitea --version` to confirm the version of your deployed Gitea instance, then choose a template version from the [compatibility matrix](COMPATIBILITY.md#compatibility-matrix).
> [!WARNING]
> We do not recommend building directly from the source repository, as these versions have not yet been fully tested for release readiness. Outdated or missing template parameters that have not been identified may prevent your Gitea instance from starting correctly.
> [!TIP]
> If you still need to build the email templates from source, follow the steps below.
| Source | Email Template Directory | Preparation |
|---|---|---|
| Release archive | `themes/<name>/mail/` | Download and extract the recommended release archive |
| Source | `build/themes/<name>/mail/` | Build with Go 1.24 or later |
To build from source, run the following from the repository root:
```bash
# Locate your Gitea custom directory
# (set by GITEA_CUSTOM; defaults shown below)
# Copy templates (example: Horizon style)
cp -r themes/horizon/mail/* /var/lib/gitea/custom/templates/mail/
# Restart Gitea
systemctl restart gitea
cd tools
go run . build all
cd ..
```
### Custom Directory Location
Building usable templates requires resources from the official Gitea repository to ensure consistent source inputs. The command automatically downloads the official files pinned by `gitea.lock.json`. Subsequent builds use the cached resources without downloading them again. For missing files or a damaged cache, see [official snapshot updates](#official-snapshot-updates).
| Platform | Default Path |
### Install a Theme
Copy the contents of the selected theme's `mail/` directory into `<GITEA_CUSTOM>/templates/mail/`, then restart Gitea to apply the theme. Before copying, confirm the custom directory your instance actually uses. Common deployment paths include:
| Deployment | Example Custom Directory |
|---|---|
| Linux (binary) | `/var/lib/gitea/custom` |
| Linux (Docker) | `/data/gitea` |
| Linux binary | `/var/lib/gitea/custom` |
| Docker | `/data/gitea` |
| Windows | `C:\gitea\custom` |
### Switching Styles
Overwrite the files with a different style. All templates share the exact same variable structure — no configuration changes needed.
For example, on a Linux host where systemd manages Gitea, run the following from the extracted release archive directory:
```bash
cp -r themes/terminal/mail/* /var/lib/gitea/custom/templates/mail/
systemctl stop gitea
mkdir -p /var/lib/gitea/custom/templates/mail
cp -r themes/horizon/mail/. /var/lib/gitea/custom/templates/mail/
systemctl restart gitea
```
### Confirming It Works
> [!TIP]
> For templates built from source, change the copy source to `build/themes/horizon/mail/.`. For Docker and Windows deployments, use the appropriate container or service controls to stop and restart Gitea.
The admin test email does not use custom mail templates. To verify your templates
are active, trigger a real email notification. The quickest way is the password
reset flow: log out, click **"Forgot password"** on the login page, and check the
reset email — it will render with your custom styles.
### Switch Themes
---
Back up or remove the files installed by the previous theme, choose a new email theme, and repeat the installation steps above.
### Confirm the Theme Is Applied
Use a test account to trigger an email notification, such as a password reset, and check its appearance and links. The administration panel's test-email button does not use custom email templates, so it cannot confirm whether the theme has been applied.
## Preview
Two modes are available — a zero-dependency static preview for quick checks, and a live-reload dev server for design work.
The preview tool included in the repository lets you view how emails render across themes, template types and languages before deployment.
### Static Preview
Generate the preview data once (Go only), then open in a browser:
First, generate the preview data in the source repository:
```bash
cd tools && go run . preview all
open preview/index.html # no server needed
cd tools
go run . preview all
cd ..
```
### Dev Server (Live Reload)
Open [preview/index.html](preview/index.html) in a browser. No server is required.
Start a pure Go development server that watches for `.tmpl` changes, auto-rebuilds, and pushes live updates to the browser via SSE:
### Development with Live Reload
```bash
cd tools && go run . dev
open http://localhost:3456
cd tools
go run . dev
# Press Ctrl+C in the terminal to stop the server.
cd ..
```
| Capability | Static | Dev |
|-----------|--------|-----|
| Go template rendering | ✅ | ✅ |
| Theme/template switching | ✅ | ✅ |
| Live reload on save | — | ✅ |
Open [http://127.0.0.1:3456](http://127.0.0.1:3456). The Go server watches theme files, the shared framework, the lock file and cache, and preview test data. When a file changes, it rebuilds the email templates and refreshes the page through server-sent events (SSE) to provide live reload.
### Features
### Controls and Keyboard Shortcuts
- Theme switcher — browse all 10 visual styles
- Template switcher — all 11 email types
- View mode — Modern (rendered preview), Source (raw HTML)
- Viewport toggle — Desktop 1386×780 / Mobile 390×780
- Parameter panel — mock data per email type
- Keyboard shortcuts — `←→` tab between Theme/Template/View, `↑↓` select within, `d`/`m` viewport
| Control | Options or Shortcuts |
|---|---|
| Theme, template, language and view | `←` / `→` moves between selectors; `↑` / `↓` selects an option |
| View | **Modern** displays the rendered result; **Source** displays the generated HTML text |
| Viewport | **Desktop** (1386 × 780), **Mobile** (390 × 780); shortcuts `d` / `m` |
| Information panel | `p` expands or collapses the panel |
---
> [!WARNING]
> The preview renders templates using example data. Browsers and email clients differ in their CSS support, so check the appearance in your target email clients before deployment.
## Directory Structure
## Compatibility
- **Latest tested:** Gitea 28.0.0
- **Latest release:** [v28.0.0](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/releases/latest)
- **Upstream Gitea 28.1.0:** [PENDING]
## Project Directory Structure
The project is organized around a shared framework, theme styles and build tools. Source code and documentation are tracked in version control; downloaded official files, generated templates and preview data are not.
### Tracked in Version Control
```text
.github/ # Workflows and maintenance scripts
release-notes/ # Release notes for each version
scripts/ # Documentation updates, packaging and Gitea release scripts
workflows/ # Validation and publication
docs/ # Chinese documentation, documentation index and gallery images
framework/ # Shared email controls and layout presets
layouts/ # Layout presets shared across email types
mail/base/ # Header, action buttons, fallback links, sidebar and footer
preview/index.html # Preview interface
themes/<THEME_NAME>/ # Theme source files
tools/ # Go CLI, build tools and tests
builder/ # Official template adaptation and theme generation
cli/ # Command definitions and argument handling
config/ # Preview configuration loading and validation
data/ # Email type descriptions and preview example data
preview/ # Email rendering, locale adaptation and development server
upstream/ # Official snapshot downloads, verification and translation key discovery
gitea.lock.json # Official version tag, commit and file checksums
README.md
```
gitea-mail-templates/
├── themes/ # 10 visual styles, 11 .tmpl files each
│ ├── ... # Custom styles are added here as separate directories
├── preview/ # Live preview SPA
│ ├── index.html # Style/template/client/viewport switcher
│ └── rendered.js # Pre-rendered templates (generated by tools/)
├── tools/ # Modular CLI tooling
│ ├── tools.go # Main entry point
│ ├── cli/ # CLI subcommands (list, create, delete, preview)
│ ├── config/ # Config types and templates_config.json loading
│ ├── data/ # templates_config.json — single source of truth
│ ├── preview/ # Template rendering engine
│ └── go.mod
├── docs/ # Bilingual documentation (English + Simplified Chinese)
├── AGENTS.md # AI agent guidance
├── CONTRIBUTING.md
├── LICENSE
├── README.md
└── .gitignore
Official templates provide notification content, conditions, subject lines and links. The shared framework handles email presentation, while themes use metadata and CSS to select layouts and define styles. Theme directories do not contain official email source files or translations.
### Not Tracked in Version Control
The following files are generated by the build and preview commands or downloaded according to the lock file. They do not need to be maintained manually or committed:
```text
build/themes/<THEME_NAME>/ # Theme build output
mail/ # Email templates ready to install in Gitea
build.json # Build provenance and generated file checksums
build/upstream/ # Official templates, locales, assets and licenses pinned by the lock file
dist/ # Default output directory for release archives
preview/rendered.js # Preview manifest
preview/rendered/<LOCALE>.js # Preview data for each locale
```
`build all` generates installable templates for all themes. `preview all` generates both templates and preview data for each locale. On first use, the tools prepare any missing official file cache according to `gitea.lock.json`; subsequent uses verify the cache before proceeding.
### Template Types
| File | Email Trigger |
Gitea currently includes the following 11 email templates [1](#notes):
| File | Notification Type |
|---|---|
| `mail/user/auth/activate.tmpl` | Account activation |
| `mail/user/auth/activate_email.tmpl` | Email address verification |
| `mail/user/auth/register_notify.tmpl` | New registration notification |
| `mail/user/auth/register_notify.tmpl` | Registration notification |
| `mail/user/auth/reset_passwd.tmpl` | Password reset |
| `mail/org/team_invite.tmpl` | Team invitation |
| `mail/repo/collaborator.tmpl` | Repository collaborator added |
| `mail/repo/transfer.tmpl` | Repository ownership transfer |
| `mail/repo/release.tmpl` | New release published |
| `mail/repo/actions/workflow_run.tmpl` | Actions workflow run |
| `mail/repo/issue/assigned.tmpl` | Issue / Pull Request assigned |
| `mail/repo/issue/default.tmpl` | Issue / Pull Request updates |
---
## Compatibility
- **Gitea 28.0.0** — use template release v28.0.0; earlier Gitea versions require an older package
- **Latest tested:** Gitea 28.0.0 <!-- TRACKER:LATEST-TESTED -->
- **Latest release:** [v28.0.0](https://github.com/KenanZhu/GiteaMailTemplates/releases/tag/v28.0.0) updates all 10 release-notification templates for Gitea's `FormatByteSize` function. Use [v1.27.3](https://github.com/KenanZhu/GiteaMailTemplates/releases/tag/v1.27.3) for Gitea 1.25.0–1.27.3 (see [COMPATIBILITY.md](COMPATIBILITY.md))
- The current source uses Gitea's official template data paths — see [COMPATIBILITY.md](COMPATIBILITY.md) for release-specific limitations
- Uses only built-in Gitea template functions and official translation keys
- No custom template functions or locale patches required
---
## Design Principles
1. **Responsive** — Max-width 600px cards; works in all email clients
2. **Accessible** — 4.5:1 contrast ratios; semantic HTML
3. **Graceful degradation** — Fallback link visible when buttons fail to render
4. **Logo support** — References `{{AppUrl}}assets/img/favicon.png` by default
5. **i18n-ready** — All user-facing strings use Gitea's `{{.locale.Tr}}` system
---
## Documentation
- [English](README.md)
- [简体中文](docs/README.zh-CN.md)
---
| `mail/repo/issue/assigned.tmpl` | Issue or pull request assignment |
| `mail/repo/issue/default.tmpl` | Issue or pull request activity |
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. A Simplified Chinese translation is available in [docs/](docs/).
Contributions to themes, tools, documentation and translations are welcome. The [contributor guide](CONTRIBUTING.md) covers local environment setup, design guidelines, required checks and the release process.
## License
MIT — see [LICENSE](LICENSE). Free to use, modify, and distribute in any Gitea deployment.
This project is licensed under [MIT](LICENSE). Generated release archives retain Gitea's license and snapshot provenance.
---
### Notes
<p align="center">
<sub>Not affiliated with the Gitea project. Gitea is a community-managed lightweight code hosting solution written in Go.</sub>
</p>
[1](#notes): [Mail templates | Gitea Documentation](https://docs.gitea.com/administration/mail-templates/)
This project is not affiliated with Gitea.
+66
View File
@@ -0,0 +1,66 @@
# Gitea 兼容性
---
[English](../COMPATIBILITY.md)
此文件描述了 Gitea Mail Templates 与 Gitea 不同版本之间的兼容性。
## 兼容性表
根据所运行的 Gitea 版本选择对应推荐的模板版本。`[PASS]` 表示已记录为兼容的组合,`[PENDING]` 表示尚未完成验证。
> [!NOTE]
> 历史条目仅反映项目已有的兼容性评估,不代表每次发布都重新测试了每一个补丁版本。
| Gitea 版本 | 推荐模板版本 | 状态 | 说明 |
|---|---|---|---|
| 28.1.0 | — | [PENDING] | 等待兼容性验证 |
| 28.0.0 | **v28.0.0** | [PASS] | Gitea 将邮件函数 `FileSize` 替换为 `FormatByteSize` |
| 1.27.3 | **v1.27.3** | [PASS] | 全部主题已修复拉取请求推送通知的提交链接 |
| 1.27.2 | **v1.27.3** | [PASS] | 同号 v1.27.2 模板的 Bloom、Ember、Heritage 存在推送通知缺陷 |
| 1.27.1 | **v1.27.3** | [PASS] | 较早 v1.0.x 模板使用过时的推送提交字段 |
| 1.27.0 | — | [PENDING] | Gitea 使用了新的提交数据结构;v1.27.3 可能适用,但未在此版本上验证 |
| 1.26.4 | **v1.0.1** | [PASS] | — |
| 1.26.3 | **v1.0.1** | [PASS] | — |
| 1.26.2 | **v1.0.1** | [PASS] | — |
| 1.26.1 | **v1.0.1** | [PASS] | — |
| 1.26.0 | **v1.0.1** | [PASS] | — |
| 1.25.5 | **v1.0.1** | [PASS] | — |
| 1.25.0 | **v1.0.1** | [PASS] | — |
> [!NOTE]
> Gitea 1.24.x 及更早版本使用不同的邮件模板布局,属于 `[UNSUPPORTED]`,Gitea Mail Templates 不再另外支持。v1.0.0 模板标签已由 v1.0.1 替代,不再单独推荐。
## Gitea 历版本对邮件模板影响
此表描述 **Gitea** 的变化,不是本模板仓库的修复记录。“无”表示该 Gitea 版本没有新增相关邮件模板影响;推荐归档见上方兼容性表。
| Gitea | 发布日期 | 邮件模板变化 | 影响 |
|---|---|---|---|
| **28.1.0** | 2026-10-06 | [PENDING] | [PENDING] |
| **28.0.0** | 2026-09-29 | 邮件函数 `FileSize` 改为 `FormatByteSize` ([PR](https://github.com/go-gitea/gitea/pull/#39083)) | **不兼容变更:** 旧的发布附件模板失效 |
|||引入 templates/mail/base/ ([PR](https://github.com/go-gitea/gitea/pull/#38396))| **不兼容变更:**旧版本 Gitea 无法直接使用新版本邮件模板 |
| **1.27.3** | 2026-08-29 | 无 | 无 |
| **1.27.2** | 2026-08-14 | 无 | 无 |
| **1.27.1** | 2026-07-27 | 内置推送模板改用 `.UserCommit.GitCommit`([PR](https://github.com/go-gitea/gitea/pull/38467)) | **不兼容变更:存在既有未修复缺陷** 旧的自定义拉取请求推送模板可能失效 |
| **1.27.0** | 2026-07-13 | 推送提交数据移至 `.UserCommit.GitCommit`([ISSUE](https://github.com/go-gitea/gitea/issues/38469)) | **不兼容变更:** 旧的拉取请求推送模板失效 |
| **1.26.4** | 2026-06-21 | 无 | 无 |
| **1.26.3** | 2026-06-20 | 无 | 无 |
| **1.26.2** | 2026-05-20 | 无 | 无 |
| **1.26.1** | 2026-04-22 | 无 | 无 |
| **1.26.0** | 2026-04-19 | 无 | 无 |
| **1.25.5** | 2026-03-10 | 无 | 无 |
| **1.25.0** | 2025 | 自定义邮件路径及主题和正文格式变化([PR](https://github.com/go-gitea/gitea/pull/35150)) | **不兼容变更:** 不支持更早的模板布局 |
| **≤ 1.24.x** | — | 旧的自定义邮件布局 | [UNSUPPORTED] |
## 模板变量参考
各类邮件上下文由官方模板定义。开发时具体的邮件模板和函数和变量请参考官方邮件模板文档 [1](#注释)。
## 翻译键
官方模板引用 `mail.*` 和 `actions.runs.attempt`。基于 AST 的检查覆盖 `.locale.Tr`、`$.locale.Tr` 和 `TrN` 的两个复数键,包括嵌套管道。所有引用键必须存在于锁定的英文目录。
---
### 注释
[1](#注释): [Mail templates | Gitea Documentation](https://docs.gitea.com/administration/mail-templates/)
+182 -62
View File
@@ -1,80 +1,200 @@
# 贡献指南 — Gitea 邮件模板
# 贡献指南
## 参与方式
欢迎改进主题、工具、测试、文档和翻译。本指南介绍当前源码架构的开发流程;安装和版本选择请参阅 [README](README.zh-CN.md#安装)。
### 添加新风格
## 本地开发
1. 使用工具脚手架:`cd tools && go run . create <风格名称>` — 这会创建完整的目录结构和全部 11 种邮件类型的占位 `.tmpl` 文件
2. 在 `themes/<风格名称>/` 中编写每个 `.tmpl` 文件,应用独特的视觉设计
3. 重新生成预览:`cd tools && go run . preview all`(构建脚本会自动发现 `themes/` 下的所有主题目录并动态生成主题选择器)
4. 提交包含截图渲染效果的 PR(单张图片 ≤ 50 KiB,建议 10–20 KiB)
### 风格指南
- 每个风格必须包含全部 **11 种模板类型**
- 只使用 Gitea 内置模板函数
- 翻译键必须来自 Gitea 官方语言文件(`mail.*` 命名空间)
- **不要在以下模板中使用 `.DisplayName`**:collaborator、transfer、release、workflow_run、assigned、default
- 面向 600px 宽度的邮件客户端设计
- 尽可能在 Gmail、Outlook、Apple Mail 中测试
### 报告 Bug
1. 检查引用的 Go 模板变量是否存在
2. 验证翻译键是否与 Gitea 语言文件匹配
3. 确认 `.DisplayName` 未在不支持的模板中使用
4. 重新生成预览:`cd tools && go run . preview all`
5. 提交 issue,注明风格名称、邮件类型及错误描述
### 改进文档
文档改进、预览截图、安装指南和翻译始终欢迎。
---
## 开发环境
- **Go 1.21+** 用于模板渲染和 CLI 工具
### 本地预览(静态)
1. 先生成预览数据:`cd tools && go run . preview all`
2. 在浏览器中打开 `preview/index.html` — 无需服务器
### 开发服务器(实时重载)
使用 **Go 1.24 或更高版本**。从仓库根目录开始,准备依赖和锁定的官方文件,再运行检查并生成预览:
```bash
cd tools && go run . dev
# → http://localhost:3456
cd tools
go mod download
go run . upstream prepare
go test ./...
go run . preview all
```
修改 `.tmpl` 文件后自动重建并推送至浏览器。
在浏览器中打开 `preview/index.html`,或在 `tools/` 中运行 `go run . dev`,访问 [http://127.0.0.1:3456](http://127.0.0.1:3456) 使用实时预览。开发服务器监听主题、框架、锁文件与缓存、预览测试数据的变化。
CLI 使用 urfave/cli 和 x/net HTML 解析器。文档与打包检查使用 Python 3.11 或更高版本。
### 集成测试
### 常用命令
将模板部署到 Gitea 实例后,请通过真实的邮件通知进行验证。管理后台的测试邮件
(**Site Administration > Configuration > Mailer > Send Test Email**)不会使用
自定义邮件模板——它走的是内置代码路径。
以下命令均在 `tools/` 中执行。带有位置参数时,将选项放在位置参数之前。
最可靠的验证方式是触发一次真实的邮件通知,推荐使用密码重置流程:
| 命令 | 用途 |
|---|---|
| `go run . list` | 列出可用主题 |
| `go run . create my-theme` | 创建主题元数据和 CSS |
| `go run . build my-theme` | 生成指定主题的安装模板 |
| `go run . build all` | 生成全部主题 |
| `go run . preview all` | 构建全部主题并渲染所有官方语言 |
| `go run . dev` | 在端口 3456 启动开发服务器 |
| `go run . dev --port 3457` | 指定其他本地端口 |
| `go run . delete my-theme` | 删除指定主题的源码目录 |
1. 退出登录,点击登录页的**"忘记密码"**
2. 输入账户邮箱并提交
3. 查看密码重置邮件——它将使用你的自定义邮件模板渲染
`build` 将模板写入 `build/themes/<名称>/mail/`,并在 `build.json` 中记录文件和源码哈希。`preview` 同时完成构建,随后写入 `preview/rendered.js` 和 `preview/rendered/<语言>.js`。这些生成文件和下载的官方文件均被 Git 忽略,无需随贡献提交。
---
## 添加主题
1. 在 `tools/` 中运行 `go run . create my-theme`。
2. 编辑 `themes/my-theme/theme.json` 和 `theme.css`。
3. 在 `tools/` 中运行 `go test ./...` 和 `go run . preview all`。
4. 检查英文、简体中文及至少另一种官方语言的桌面与移动端布局,在拉取请求中附上截图。
主题名称以小写字母开头,可包含小写字母、数字和连字符。主题目录只包含 `theme.json` 和 `theme.css`。
```json
{
"name": "my-theme",
"description": "主题的简短说明",
"mode": "framed",
"layout": "standard"
}
```
| 模式 | 生成的展示结构 |
|---|---|
| `framed` | 使用共享控件和框架布局,由主题 CSS 定义样式。新主题默认使用此模式及 `standard` 布局。 |
| `shared` | 在官方页头片段中加入 CSS,并保留官方页脚片段;不增加框架控件或各类邮件的覆盖模板。 |
### 共享框架
页头、操作按钮、备用链接、侧栏和页脚位于 `framework/mail/base/`。布局预设位于 `framework/layouts/`,`framed` 主题可通过可选的 `layout` 字段选择预设。配色、字体和间距由主题 CSS 定义。
`tools/builder/framework.go` 将官方操作值和翻译键适配到共享控件。通知条件、主题行、附件和上下文链接由官方模板定义。控件或布局结构的修改应放在框架中,使各主题使用同一套适配逻辑。
布局片段组合后必须形成标签配对完整的展示结构。`__HEADER__` 插入共享页头,`__MAIL_TYPE__` 展开为发现的邮件 ID,页脚片段中的 `__SIDEBAR__` 插入共享侧栏。部署模板的 Logo 使用 `{{AppUrl}}assets/img/favicon.png`;生成的预览数据嵌入下载的图标,供离线显示。
## 设计规范
- 使用兼容邮件客户端的 CSS。主题 CSS 不得加载外部资源、生成文字或隐藏官方内容。
- 布局片段使用展示性标记。校验器接受 `table`、`tbody`、`tr`、`td`、`div`、`span` 及支持的展示属性。
- 修改已有主题时,保留其配色、字体、边框、页头、按钮和布局特征。
- 检查桌面和 390px 移动端预览,覆盖长文本和长 URL。条件允许时测试 Gmail、Outlook 和 Apple Mail;浏览器预览不能模拟邮件客户端。
- 画廊图片使用 PNG,单张不超过 50 KiB,建议为 10–20 KiB。按[截图指南](images/README.md)保持截图一致。
## 更新官方快照
`gitea.lock.json` 记录稳定的 Gitea 28+ 标签、对应提交和每个文件的 SHA-256。官方邮件模板、语言文件、图标、许可证和已评审的适配参考源码下载到 `build/upstream/`。仓库仅提交生成的根锁文件,不维护官方文件副本。
### 命令参考
在 `tools/` 中执行。每个上游子命令均支持 `--root <仓库根目录>`,默认值为相对当前工作目录的 `..`。参数放在子命令之后,包含空格的路径需要加引号:
```powershell
go run . upstream verify --root "C:\Projects\GiteaMailTemplates"
```
| 命令 | 行为 |
|---|---|
| `go run . upstream prepare` | 读取根锁文件。缓存不存在时下载并校验对应文件;已有缓存则离线校验。不修改根锁文件。 |
| `go run . upstream verify` | 对照根锁文件离线、只读检查已有缓存。输出 `[PASS]`,并以 `[FALLBACK]` 列出非英文语言缺少的键。 |
| `go run . upstream sync --tag vX.Y.Z` | 通过 GitHub API 解析明确指定的稳定 Gitea 28+ 标签,下载并校验文件,然后替换缓存并生成根锁文件。 |
`sync` 使用 `api.github.com`,文件下载使用 `raw.githubusercontent.com`。这些命令不接受本地 Gitea 克隆、认证令牌或隐式的 `latest` 版本。网络错误和 API 限流会中止操作;Go 工具链或模块可能仍需单独联网下载。
### 已有克隆与离线使用
`build`、`preview` 和 `dev` 会自动准备输入,需要已提交的根锁文件,并在使用缓存前完成校验。缓存、Go 工具链和依赖齐备后,可以离线构建。
需要单独检查缓存时,使用 `upstream verify`。它校验官方文件哈希、已评审的适配参考源码,以及官方模板引用的英文翻译键。构建和渲染还会检查框架翻译键及主要操作链接的锚点,因此缓存校验只是兼容性测试的一部分。
### 锁文件缺失与显式更新版本
已有克隆缺少 `gitea.lock.json` 时,应恢复受版本控制的文件。`build/upstream/lock.json` 中的副本不能替代根锁文件。需要有意初始化锁文件时,执行:
```bash
cd tools
go run . upstream sync --tag v28.0.0
go run . upstream verify
go test ./...
go run . preview all
```
评审其他版本时,明确指定对应标签。检查锁文件差异,按需更新框架适配逻辑和测试数据,完成检查后,再更新兼容性记录。`sync` 只修改缓存和锁文件,文档更新与版本发布需单独完成。仅缺少缓存时使用 `prepare`。
已评审的翻译或邮件渲染参考源码发生变化时,须先评审适配逻辑,再更新参考哈希。官方英文必须包含所有引用的翻译键;其他语言缺少的翻译回退为英文。新增邮件类型需要补充框架适配和 `tools/data/templates_config.json` 中的测试数据,该文件提供预览元数据和示例上下文。JSON 测试数据中的整数应保持整数形式,以便 Go 正确格式化。
缓存替换前发生网络或校验错误时,原有文件保持不变。缓存替换和根锁文件写入是两个操作;文件系统错误中断操作后,重试前应检查二者是否一致。
### 缓存恢复
`prepare` 会报告损坏、不完整或与锁文件不匹配的缓存,不会自动修复。`sync` 同样拒绝替换非空的无效快照。检查错误后,将生成的缓存移至备份位置,再按已评审的根锁文件重新准备。
例如,在**仓库根目录的 PowerShell** 中,使用尚不存在的备份名称:
```powershell
Move-Item -LiteralPath .\build\upstream -Destination .\build\upstream.backup
Set-Location tools
go run . upstream prepare
go run . upstream verify
```
保留 `build/` 中的其他内容,不直接编辑缓存中的官方文件。需要回退快照时,恢复此前已评审的根锁文件,再按同样方式重建缓存。替换输入前先停止 `dev`,避免多个进程同时写入同一缓存。
### 已知上游格式问题
Gitea v28.0.0 的波兰语 `mail.team_invite.text_1` 存在占位符缺陷。预览保留官方输出并报告 `[UPSTREAM-WARN]`。该例外仅匹配已评审的完整原文,其他格式错误会使渲染失败。发现上游问题时应报告问题,不修改缓存中的语言文件。
## 验证与发布
### 拉取请求检查
在 `tools/` 中运行 `go test ./...` 和 `go run . preview all`。测试覆盖生成结果的确定性、操作锚点变化、翻译键,以及全部主题和语言的通知主题行、正文与链接。测试数据包含推送、评审、回复、工作流和附件分支;共享控件与品牌展示作为附加内容单独处理。
修改文档或打包流程时,在仓库根目录执行:
```bash
python -B -m unittest discover -s .github/scripts -p 'test_*.py'
```
调整通用说明时,同步更新英文和简体中文指南。拉取请求应说明改动内容及相关检查结果,涉及视觉变化时附上截图。
### 准备发行版
发行标签必须与锁定的 Gitea 版本一致。当前源码重构尚未发布,已有 v28.0.0 和历史版本的附件应保留。
1. 完成 Go 测试、文档检查和预览生成。
2. 按验证结果更新文档和兼容性记录。发行说明为可选内容,不用于 Release 正文。
3. 在 `tools/` 中运行 `go run . preview all`,生成全部主题和语言数据。
4. 打包并检查发行包内容后再发布。
手动打包时,在仓库根目录执行:
```bash
python .github/scripts/package_release.py --version vX.Y.Z --output <new-output-directory>
```
将版本和输出目录占位符替换为已评审的标签及新的目录。脚本会检查源码与产物哈希,包含所有语言数据包,排除残留的旧主题构建,并拒绝覆盖已有压缩包。
发布工作流使用同一脚本打包模板、预览、文档、官方许可证和来源信息,仅上传发行归档,以标签作为 Release 标题,新建 Release 的正文为空。版本标记、徽章和兼容性记录均由维护者手动修改,归档文档应在打标签前检查。依赖自动发布前,请确认所用 Gitea 主机的 Actions 支持和写入权限。工作流行为见[版本维护](COMPATIBILITY.zh-CN.md#版本维护)。
### Gitea 工作流配置
发布工作流使用 `linux-amd64-docker-small` Runner 标签。任务镜像需要支持检出和工具链安装步骤使用的 Node.js Action,并提供 Git 和 POSIX Shell。Go 与 Python 由安装步骤准备。
发布直接使用 [`gitea-release-action@v1.3.7`](https://gitea.com/actions/gitea-release-action/src/tag/v1.3.7/action.yml),传入实例地址、仓库、内置 `GITEA_TOKEN`、标签、标题和两个归档路径。新建 Release 的正文为空,不要求发行说明文件,也不创建提醒 Issue。仓库需允许发行版写入。Action 地址显式使用 `https://gitea.com`,不依赖 `DEFAULT_ACTIONS_URL` 配置。
发布前通过 `actions/setup-node@v4` 准备 Node.js 22。当前 Gitea runner 使用 `PATH` 中的 `node` 执行 JavaScript Action,提供发布 Action 所需的运行接口,无需 `--experimental-fetch`。重复发布已有标签时,Action 保留已有正文并替换同名附件,不具备原脚本在全部附件上传完成前保持草稿的行为。发布失败后,重试前应检查发行版和附件。
## 报告问题
请提供 Gitea 版本、模板发行版或源码提交、主题、邮件类型、语言和复现步骤。源码构建问题还应包含快照标签与提交,以及 `go run . upstream verify` 或 `go run . preview all` 的输出。显示问题请注明邮件客户端,并附上移除个人信息后的截图。
## 提交规范
- `style(<名称>):` — 特定风格的模板变更
- `preview:` — 预览工具变更
- `tools:` — Go 构建脚本变更
- `docs:` — 文档和翻译
- `fix:` — Bug 修复
- `project:` — README、LICENSE、元文件
| 前缀 | 范围 |
|---|---|
| `style(<名称>):` | 主题展示样式 |
| `preview:` | 浏览器预览 |
| `tools:` | CLI、构建与快照工具 |
| `docs:` | 文档和翻译 |
| `fix:` | 问题修复 |
| `project:` | 仓库配置和项目结构 |
| `refactor:` | 代码重构 |
| `chore:` | 日常维护 |
## 许可协议
## 翻译与许可证
参与贡献即表示您同意将您的贡献以 MIT 许可证授权。
[英文指南](../CONTRIBUTING.md)介绍相同的开发流程。贡献采用 MIT 许可证;分发派生模板时,请保留 Gitea 版权和官方许可证,详见[第三方声明](../THIRD_PARTY_NOTICES.md)。
+174 -53
View File
@@ -1,88 +1,209 @@
# Gitea 邮件模板
为自托管 [Gitea](https://about.gitea.com) 实例精心设计、面向不同受众的邮件模板集合。
> **发布版 v28.0.0 — 已通过 Gitea 28.0.0 兼容性检查 — 110 个模板文件、10 种视觉风格、每种 11 种邮件类型**
# Gitea Mail Template
---
## 设计理念
[English](../README.md)
大多数自托管 Gitea 实例使用默认的纯文本邮件模板。本项目提供了**开箱即用、视觉精美的替代方案**——每种方案都针对特定社区或受众设计,您可以选择最适合您用户的风格。
Gitea Mail Template 为自托管的 [Gitea](https://about.gitea.com) 提供多种可选、适合的邮件模板风格。
`main` 分支中的模板可直接替换 Gitea 内置邮件模板,**无需补丁、插件或 fork**。使用已发布的压缩包前,请先查看[兼容性说明](../COMPATIBILITY.md),了解旧版本的限制。
> 最新发布版:[v28.0.0](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/releases/latest)
---
仓库包含数种主题的 Gitea 邮件模板,适合大多数的部署场景与应用范围。安装前,请根据 Gitea 版本查阅[兼容矩阵](../COMPATIBILITY.md#compatibility-matrix),选择对应的发行包。
## 风格画廊
| 预览 | 风格 | 受众 | 特点 |
|---|---|---|---|
| ![Horizon](images/horizon.png) | **Horizon** | 企业/公司 | 蓝色强调色、石板灰排版、居中卡片 |
| ![Terminal](images/terminal.png) | **Terminal** | 开发者/技术 | 暗色模式、等宽字体、绿色命令行风格 |
| ![Ember](images/ember.png) | **Ember** | 社区/开源 | 暖琥珀色、圆角、人文主义、包容 |
| ![Bloom](images/bloom.png) | **Bloom** | 创意/初创 | 磨砂玻璃、柔和蓝色光线、虹彩点缀 |
| ![Heritage](images/heritage.png) | **Heritage** | 教育/研究 | 海军蓝与金色、衬线字体、经典、权威 |
| ![Neon](images/neon.png) | **Neon** | 游戏/Web3/创意科技 | 赛博朋克霓虹、粉红与青色、合成波能量 |
| ![Mono](images/mono.png) | **Mono** | 设计工作室/编辑 | 瑞士粗野主义、黑白红强调、零圆角 |
| ![Terra](images/terra.png) | **Terra** | 可持续/健康 | 温暖大地色调、有机质感、人文衬线 |
| ![Ink](images/ink.png) | **Ink** | 出版/新闻/文学 | 编辑印刷、深蓝与金色、报纸排版 |
| ![Aurora](images/aurora.png) | **Aurora** | 高端SaaS/正念 | 空灵光效渐变、深紫与青绿、大气光晕 |
这里展示邮件模板通过预览工具呈现的实际渲染风格和效果。
> 图片为 600px 宽截图,来自[在线预览](../preview/index.html)。截图方法参见 [images/README.md](images/README.md)。
| 预览 | 主题 | 样式特点 |
|---|---|---|
| ![Aurora](images/aurora.png) | **Aurora** | 深紫色背景、青绿色强调色、柔和光晕 |
| ![Bloom](images/bloom.png) | **Bloom** | 浅蓝色渐变、圆角卡片与按钮 |
| ![Ember](images/ember.png) | **Ember** | 暖橙色调、衬线标题、圆角按钮 |
| ![Heritage](images/heritage.png) | **Heritage** | 藏蓝与金色、双线边框、衬线字体 |
| ![Horizon](images/horizon.png) | **Horizon** | 蓝色强调色、灰色文字、居中白色卡片 |
| ![Ink](images/ink.png) | **Ink** | 报刊式布局、侧栏、衬线字体与首字下沉 |
| ![Mono](images/mono.png) | **Mono** | 黑白配色、红色强调色、直角边框 |
| ![Neon](images/neon.png) | **Neon** | 深色背景、粉红与青色、发光效果 |
| ![Terminal](images/terminal.png) | **Terminal** | 深色背景、等宽字体、绿色强调色 |
| ![Terra](images/terra.png) | **Terra** | 大地色调、陶土色按钮、衬线字体 |
[**在线预览画廊**](../preview/index.html)
---
截图仅展示当前默认构建效果,其他邮件类型和语言可通过[本地预览](#预览)查看;更新截图请参阅[截图指南](images/README.md)。
## 安装
选择一种风格,将 `mail/` 目录复制到 Gitea 自定义模板路径:
### 选择安装来源
运行 `gitea --version` 确认部署的 Gitea 实例版本,再按照[兼容性表](COMPATIBILITY.zh-CN.md#兼容性表)选择模板版本。
> [!WARNING]
> 我们不建议直接从源码仓库拉取并构建,因为这些版本还未经过完全测试达到发行可用的水平。如果存在未知的模板参数过时或遗漏可能导致 Gitea 实例启动异常。
> [!TIP]
> 如果仍然需要从源码构建邮件模板,下方同样给出的相应步骤方法。
| 来源 | 邮件模板目录 | 准备步骤 |
|---|---|---|
| 发行版 | `themes/<名称>/mail/` | 下载并解压推荐版本的发行包 |
| 源码 | `build/themes/<名称>/mail/` | 使用 Go 1.24 或更高版本构建 |
从源码构建时,请在仓库根目录执行:
```bash
cp -r themes/horizon/mail/* /var/lib/gitea/custom/templates/mail/
cd tools
go run . build all
cd ..
```
构建可用模板时需要依赖于 Gitea 官方仓库的资源文件,以确保来源的一致性。因此该命令会自动下载由`gitea.lock.json` 文件锁定的官方文件,后续构建无需再次下载,使用缓存目录资源即可运行。文件缺失或缓存损坏的处理方式见[更新官方快照](#更新官方快照)。
### 安装主题
将所选主题 `mail/` 目录中的内容复制到 `<GITEA_CUSTOM>/templates/mail/`,然后重启 Gitea 即可生效。复制前请确认实例实际使用的自定义目录。以下是常见部署路径示例:
| 部署方式 | 自定义目录示例 |
|---|---|
| Linux Binary | `/var/lib/gitea/custom` |
| Docker | `/data/gitea` |
| Windows | `C:\gitea\custom` |
例如,在使用 systemd 管理 Gitea 的 Linux 主机上,从已解压的发行包目录执行:
```bash
systemctl stop gitea
mkdir -p /var/lib/gitea/custom/templates/mail
cp -r themes/horizon/mail/. /var/lib/gitea/custom/templates/mail/
systemctl restart gitea
```
切换风格只需覆盖文件,无需更改配置。
> [!TIP]
> 使用源码构建产物时,将复制来源改为 `build/themes/horizon/mail/.`。Docker 和 Windows 部署请使> 用相应的容器或服务管理方式暂停和重启。
### 切换主题
先备份或移除上一主题安装的文件,选择新的邮件主题依照上述安装步骤重新操作即可。
### 确认生效
管理后台的测试邮件不会使用自定义模板。要验证模板是否生效,请触发一次真实的
邮件通知。最快的方式是密码重置:退出登录,点击登录页的**"忘记密码"**,查看
重置邮件即可——它将使用你的自定义样式渲染。
---
使用测试账户触发密码重置等邮件通知,检查邮件样式和链接。管理后台的测试邮件按钮不使用自定义邮件模板,因此无法确认邮件模板是否生效。
## 预览
**静态模式:**
```bash
cd tools && go run . preview all
```
然后打开 `preview/index.html`。
通过仓库中提供的预览工具,可以在部署前预览邮件不同主题,不同模板类型和语言的渲染效果
### 静态预览
首先需要在源码仓库中生成预览数据:
**开发服务器(实时重载):**
```bash
cd tools && go run . dev
# → http://localhost:3456
cd tools
go run . preview all
cd ..
```
| 功能 | 静态 | Dev |
|-----------|--------|-----|
| Go 模板渲染 | ✅ | ✅ |
| 主题/模板切换 | ✅ | ✅ |
| 实时重载 | — | ✅ |
在浏览器中打开 [preview/index.html](../preview/index.html) 即可。无需启动服务器。
---
### 热重载开发
```bash
cd tools
go run . dev
## 在终端中输入 Ctrl + C 即可终止服务器运行
cd ..
```
打开 [http://127.0.0.1:3456](http://127.0.0.1:3456)。Go 服务器监听主题文件、共享框架、锁文件与缓存、预览测试数据的变化。一旦监听到文件更改,服务器会将邮件模板重新构建后通过发送事件(SSE)刷新页面,以实现实时热重载。
### 控制和快捷键
| 控件 | 选项或快捷键 |
|---|---|
| 主题、模板、语言和视图 | `←` / `→` 切换选择框,`↑` / `↓` 选择选项 |
| 视图 | **Modern** 显示渲染结果,**Source** 显示生成的 HTML 文本 |
| 视口 | **Desktop**(1386 × 780)、**Mobile**(390 × 780);快捷键 `d` / `m` |
| 信息面板 | `p` 展开或收起面板 |
> [!WARNING]
> 预览使用示例数据渲染模板。浏览器与邮件客户端对 CSS 的支持不同,部署前还需在目标邮件客户端中检查效果。
## 兼容性
- **Gitea 28.0.0** — 使用模板发布版 v28.0.0;旧版 Gitea 请选用对应的旧版模板
- **最新测试:** Gitea 28.0.0<!-- TRACKER:LATEST-TESTED -->
- **最新发布版:** [v28.0.0](https://github.com/KenanZhu/GiteaMailTemplates/releases/tag/v28.0.0) 已将全部 10 个主题的发布通知适配为 `FormatByteSize`;Gitea 1.25.0–1.27.3 请使用 [v1.27.3](https://github.com/KenanZhu/GiteaMailTemplates/releases/tag/v1.27.3)(详见[兼容性说明](../COMPATIBILITY.md))
- 当前源码使用 Gitea 官方模板的数据路径;各发布版的限制详见 [兼容性说明](../COMPATIBILITY.md)
- **最新测试:** Gitea 28.0.0
- **最新发布版:** [v28.0.0](https://gitea.kenanzhu.com/KenanZhu/GiteaMailTemplates/releases/latest)
- **上游 Gitea 28.1.0:** [PENDING]
## 项目目录结构
项目按共享框架、主题样式和构建工具组织。源码与文档纳入版本控制,下载的官方文件和生成的模板、预览数据不纳入版本控制。
### 版本控制中
```text
.github/ # 工作流及维护脚本
release-notes/ # 各版本的发行说明
scripts/ # 文档更新、打包和 Gitea 发布脚本
workflows/ # 验证和发布
docs/ # 中文文档、文档索引和画廊图片
framework/ # 共享邮件控件和布局预设
layouts/ # 各类邮件共用的布局预设
mail/base/ # 页头、操作按钮、备用链接、侧栏和页脚
preview/index.html # 预览界面
themes/<THEME_NAME>/ # 主题源码
tools/ # Go 命令行工具、构建工具和测试
builder/ # 官方模板适配和主题生成
cli/ # 命令定义和参数处理
config/ # 预览配置读取和校验
data/ # 邮件类型说明和预览示例数据
preview/ # 邮件渲染、语言适配和开发服务器
upstream/ # 官方快照下载、校验和翻译键发现
gitea.lock.json # 官方版本标签、提交和文件校验值
README.md
```
官方模板提供通知内容、条件、主题行和链接;共享框架负责邮件呈现,主题通过元数据和 CSS 选择布局、定义样式。主题目录中不保存官方邮件源码或翻译文件。
### 不在版本控制中
以下文件由构建和预览命令生成,或按锁文件下载,无需手动维护或提交:
```text
build/themes/<THEME_NAME>/ # 主题构建结果
mail/ # 可安装到 Gitea 的邮件模板
build.json # 构建来源和生成文件的校验记录
build/upstream/ # 锁文件指定的官方模板、语言、资源和许可证
dist/ # 默认输出的发行压缩包
preview/rendered.js # 预览清单
preview/rendered/<LOCALE>.js # 各语言的预览数据
```
`build all` 生成全部主题的安装模板,`preview all` 同时生成模板和各语言的预览数据。首次使用时,工具按 `gitea.lock.json` 准备缺失的官方文件缓存;后续使用前会校验缓存。
### 模板类型
当前 Gitea 包含以下 11 种邮件模板 [1](#注释):
| 文件 | 通知类型 |
|---|---|
| `mail/user/auth/activate.tmpl` | 账户激活 |
| `mail/user/auth/activate_email.tmpl` | 邮箱验证 |
| `mail/user/auth/register_notify.tmpl` | 注册通知 |
| `mail/user/auth/reset_passwd.tmpl` | 密码重置 |
| `mail/org/team_invite.tmpl` | 团队邀请 |
| `mail/repo/collaborator.tmpl` | 添加仓库协作者 |
| `mail/repo/transfer.tmpl` | 仓库所有权转移 |
| `mail/repo/release.tmpl` | 发布新版本 |
| `mail/repo/actions/workflow_run.tmpl` | Actions 工作流运行 |
| `mail/repo/issue/assigned.tmpl` | 议题或合并请求指派 |
| `mail/repo/issue/default.tmpl` | 议题或合并请求动态 |
## 参与贡献
欢迎改进主题、工具、文档和翻译。[贡献指南](CONTRIBUTING.zh-CN.md)介绍了本地环境准备、设计规范、检查要求和发布流程。
## 许可证
MIT — 详见 [LICENSE](../LICENSE)。
本项目采用 [MIT 许可证](../LICENSE)。生成的发行包保留 Gitea 许可证和快照来源信息。
---
### 注释
[1](#注释):[Mail templates | Gitea Documentation](https://docs.gitea.com/administration/mail-templates/)
本项目与 Gitea 官方无隶属关系。
+19 -19
View File
@@ -1,27 +1,27 @@
# Style Preview Images
# Gallery Images
Place theme style screenshots of **Desktop** in PNG format here, captured from the [live preview](../preview/index.html).
Gallery images show the current source build. Historical release archives retain their original templates.
## Naming Convention
## Capture Settings
```
horizon.png terminal.png ember.png bloom.png heritage.png
neon.png mono.png terra.png ink.png aurora.png
```
Use the same settings for every theme so images remain comparable:
## Image Size Requirements
| Setting | Value |
|---|---|
| Template | **Register Notify** |
| Language | **en-US** |
| View | **Modern** |
| Viewport | **Desktop** |
| Information panel | Collapsed |
| Output | PNG, 600px wide recommended |
To ensure screenshots can be displayed in the README, please follow these size requirements:
Save images as `<theme-name>.png` in this directory, matching the directory name under `themes/`. Each file must be at most **50 KiB**; **10–20 KiB** is preferred. Optimize larger PNGs with tools such as `pngquant` or `optipng`.
- **Maximum:** 50 KiB per image
- **Recommended:** 30–40 KiB
- **Format:** PNG, optimised — run through `pngquant` or `optipng` before committing
## Manual Capture
## How to Capture
1. Run `go run . dev` from `tools/` and open [http://127.0.0.1:3456](http://127.0.0.1:3456). Alternatively, run `go run . preview all` and open [preview/index.html](../../preview/index.html) directly.
2. Select a theme and apply the capture settings above.
3. Capture the rendered email, excluding the preview toolbar and information panel.
4. Save the PNG as `<theme-name>.png`, check its size and repeat for the remaining themes.
1. Start the dev server: `cd tools && go run . dev` (opens http://localhost:3456 in your browser)
2. For each style, select the **"Activate Account"** template and **"Modern"** client mode in **"Desktop"**
3. Take a screenshot of the rendered email (600px width recommended)
4. Save as `<style-name>.png` in this directory
> For static preview, run `cd tools && go run . preview all` then open `preview/index.html` directly.
When adding a theme, update its gallery entry in both the [English README](../../README.md#style-gallery) and [Simplified Chinese README](../README.zh-CN.md#风格画廊).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 17 KiB

@@ -1,92 +0,0 @@
# Spec: Remove Juice & Simplify Preview Module
**Date:** 2026-06-23
**Status:** approved
## Goal
Remove multi-email-client simulation (Gmail/Outlook) from the preview module, eliminate the `juice` CSS-inlining dependency, and rewrite the dev server in pure Go with SSE-based live reload.
## Motivation
- Multi-client CSS simulation has diminishing value — modern email clients render consistently
- `juice` + Node.js adds complexity (npm install, node_modules, separate runtime)
- The project already uses Go for all build tooling; a Node.js dev server is an outlier
- Simplifying to "Modern" and "Source" views covers the real use cases
## Changes
### 1. Delete `tools/server/` (entire directory)
Remove all Node.js artifacts:
- `inliner.mjs` — juice CSS inlining + Gmail/Outlook CSS stripping
- `server.mjs` — Express + WebSocket dev server
- `package.json` / `package-lock.json`
- `node_modules/` — all npm dependencies
### 2. Rewrite `tools/cli/dev.go`
- Remove Node.js dependency check
- Remove `exec.Command("node", "server.mjs")` subprocess
- Instead: create and start a pure Go HTTP server (calling into `tools/preview/server.go`)
- Keep the same CLI interface: `go run . dev [--port <port>]`
### 3. New file: `tools/preview/server.go`
Pure Go dev server with:
- **Static file serving** — serve `preview/` directory (index.html, rendered.js)
- **SSE endpoint** (`GET /events`) — Server-Sent Events for browser reload notifications
- **File watcher** — poll `themes/` every 500ms, compare mod times of `.tmpl` files
- **On change detected** — call `preview.RenderAll()` directly (in-process, no subprocess), write `rendered.js`, broadcast SSE `reload` event
- No external Go dependencies — uses `net/http` stdlib only
### 4. Modify `preview/index.html`
Frontend changes:
- **Client selector** — reduce from 4 options (Modern/Gmail/Outlook/Raw Source) to 2 (Modern/Source)
- **Remove** `renderedGmail` / `renderedOutlook` variables and all Gmail/Outlook JS logic
- **Simplify** `getRendered()` — always returns `rendered`
- **Simplify** `transform()` — only handles `source` mode (HTML escaping)
- **Remove** Client Simulation indicators panel (HTML section + JS in `updatePanel()`)
- **Remove** static-mode warning message ("Gmail / Outlook simulation is approximate...")
- **Replace** WebSocket with SSE (`new EventSource('/events')`)
- **SSE reload handler** — dynamically reload `rendered.js` on `reload` event, re-render iframe without losing current theme/template/viewport selection
- **Simplify** `setDevMode()` — remove multi-client disclaimer
### 5. Update `AGENTS.md`
- Remove references to Juice, Node.js, Gmail/Outlook variants
- Update dev server description to reflect pure Go implementation
## Non-Changes
- `tools/preview/engine.go` — template rendering engine unchanged
- `tools/preview/funcs.go` — template functions unchanged
- `tools/preview/locale.go` — locale data unchanged
- `tools/config/` — config loading unchanged
- `tools/data/templates_config.json` — unchanged
- `tools/cli/preview.go` — preview command unchanged
- `tools/cli/commands.go` — command registration unchanged
- All theme `.tmpl` files — unchanged
## Behavior
### Static preview (open `preview/index.html` directly)
- Works via `file://` protocol as before
- Two view modes: Modern (rendered HTML in iframe) and Source (escaped HTML source)
- No dev-mode warnings needed
### Dev mode (`go run . dev`)
- `go run . dev` starts the Go HTTP server on port 3456 (configurable)
- On startup: runs preview engine once, writes `rendered.js`
- Serves `preview/` as static files
- Watches `themes/` for `.tmpl` changes (500ms polling)
- On change: re-renders affected themes, updates `rendered.js`, pushes SSE event
- Browser auto-reloads preview content without page refresh (preserves UI state)
## Constraints
- No new Go dependencies — SSE and file polling use stdlib only
- No Node.js requirement
- `preview/rendered.js` format stays compatible: `window.__RENDERED__`, `window.__REGISTRY__`, `window.__PARAMS__`
- Preview still works with `file://` protocol (no server needed for basic use)
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap email-card">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-body card-body mail-type-__MAIL_TYPE__">
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap email-card">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-body card-body mail-type-__MAIL_TYPE__">
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap email-card">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-body card-body mail-type-__MAIL_TYPE__">
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
</table>
+3
View File
@@ -0,0 +1,3 @@
</td><td width="36%" class="email-sidebar resp-hide">__SIDEBAR__</td></tr>
<tr><td colspan="2" class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td colspan="2" class="email-header">__HEADER__</td></tr>
<tr><td width="64%" class="email-card card-body mail-type-__MAIL_TYPE__">
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
</table>
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
</table>
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
@@ -0,0 +1 @@
</td></tr></table>
@@ -0,0 +1 @@
</td></tr><tr><td class="email-footer">
@@ -0,0 +1 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap"><tr><td class="email-header">__HEADER__</td></tr><tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
+3
View File
@@ -0,0 +1,3 @@
</td></tr>
</table>
@@ -0,0 +1,3 @@
</td></tr>
<tr><td class="email-footer">
+4
View File
@@ -0,0 +1,4 @@
<table role="presentation" width="100%" align="center" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<tr><td class="email-header">__HEADER__</td></tr>
<tr><td class="email-card card-body mail-type-__MAIL_TYPE__">
+7
View File
@@ -0,0 +1,7 @@
<div class="email-action">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table framework-extra"><tr><td class="email-btn-row"><a href="{{.URL}}" class="email-btn">{{.Label}}</a></td></tr></table>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box"><tr><td class="email-link-cell">
<p class="email-link-label framework-extra">{{.Hint}}</p>
<p class="email-link-url"><a href="{{.URL}}">{{.URL}}</a></p>
</td></tr></table>
</div>
+1
View File
@@ -0,0 +1 @@
<p class="email-footer-text"><span class="framework-extra">&copy; </span><a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a></p>
+11
View File
@@ -0,0 +1,11 @@
<div class="framework-extra">
{{if eq .Variant "terminal"}}<span class="email-brand">&gt; {{AppName}}</span>
{{else if eq .Variant "mono"}}<span class="email-brand">{{AppName}}<span class="email-brand-star">*</span></span>
{{else if eq .Variant "neon"}}<span class="email-brand">{{AppName}}</span>
{{else if eq .Variant "heritage"}}<img src="{{AppUrl}}assets/img/favicon.png" alt="Gitea" width="40" height="40" class="email-logo" /><p class="email-brand">{{AppName}}</p>
{{else if eq .Variant "bloom"}}<img src="{{AppUrl}}assets/img/favicon.png" alt="Gitea" width="32" height="32" class="email-logo" /><p class="email-brand">{{AppName}}</p>
{{else if eq .Variant "ink"}}<img src="{{AppUrl}}assets/img/favicon.png" alt="Gitea" width="24" height="24" class="email-logo" /><span class="email-brand">{{AppName}}</span><br /><span class="email-tagline">Git with a cup of tea</span>
{{else if eq .Variant "terra"}}<img src="{{AppUrl}}assets/img/favicon.png" alt="Gitea" width="28" height="28" class="email-logo" /><span class="email-brand">{{AppName}}</span><div class="email-brand-rule"></div>
{{else if eq .Variant "aurora"}}<img src="{{AppUrl}}assets/img/favicon.png" alt="Gitea" width="24" height="24" class="email-logo" /><span class="email-brand">{{AppName}}</span>
{{else}}<img src="{{AppUrl}}assets/img/favicon.png" alt="Gitea" width="32" height="32" class="email-logo" /><span class="email-brand">{{AppName}}</span>{{end}}
</div>
+6
View File
@@ -0,0 +1,6 @@
<div class="framework-extra sidebar-info">
<p class="sidebar-label">{{AppName}}</p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" class="sidebar-info-table"><tr><td>
<strong>{{AppDomain}}</strong><br /><a href="{{AppUrl}}">{{AppUrl}}</a>
</td></tr></table>
</div>
+55
View File
@@ -0,0 +1,55 @@
{
"schema": 1,
"repository": "https://github.com/go-gitea/gitea",
"tag": "v28.0.0",
"commit": "15b8a5805adf57c5189602008d38cccfd3c795e0",
"files": {
"LICENSE": "ed2f10a9d78b8c6c9ef33f1420d0eb266981891caf2f15d630553f02dc60d3ae",
"modules/templates/mail.go": "fb8204b79f700f88c7c3f0da16e50c3d678a2216e62e98f712805ed1d42ab3a9",
"modules/translation/i18n/format.go": "f440236eaf1f73fe0a9aa36a2c036a4b34c6ab28c9e4cf971bef7fceee701cb4",
"modules/translation/i18n/localestore.go": "e55a7832d01753622a7d47e904de95e5aed7ad3535c59bf6ac3fec4346e40e9d",
"modules/translation/translation.go": "4217cbd9beb79c0e1be52dd9a7d7705b9ff4becc2d9515d49236db126da0f7ee",
"options/locale/locale_cs-CZ.json": "816c4a0cfc6112c8cc9a63e63b3de0bc623819a23d2f2bbb800b09c45d23da66",
"options/locale/locale_de-DE.json": "593ec4e3652c0faf9ecb5d5b27a649c1c0a933030ecdfbf4dc810ea4ad69bfb8",
"options/locale/locale_el-GR.json": "dd03e6e083d6286d8eb2045a37bcf5c578fc339ab87a72a7397e01733078d89e",
"options/locale/locale_en-US.json": "bb58779f10b2cd0ee94f71297b2231a621d4d30f2a56a2b223f3c586f635f592",
"options/locale/locale_es-ES.json": "cbb1cd7bc4709a189b8b189d26a83a06fed0c32e0b5ac5602370fb0f03a39d04",
"options/locale/locale_fa-IR.json": "ad85f5829335844883608ed5ed09dee5bc5f4800cda7ef9277200aa4a7f35f32",
"options/locale/locale_fi-FI.json": "3b811b0ee794548399bcb64a887711a26cc0c26e04dfd0305b0846742d065952",
"options/locale/locale_fr-FR.json": "a725246762b129e6323ff2e74baa75eb4c63e893ef0e3e226d3a020d60e1fd87",
"options/locale/locale_ga-IE.json": "670c916a233c83eea5efd7e73971763dd2cd5886a832064724fccfdef87d7bf9",
"options/locale/locale_hu-HU.json": "39fc4beb3a602bbb8d41d8ab6c2cbfa7220d0c52c7cf9d631e5d2304209618a5",
"options/locale/locale_id-ID.json": "1916c9d9f600ddbeba53d2566cb3687c28a57920b2e66a5ef67ea7989535932b",
"options/locale/locale_is-IS.json": "fc1a98822acc354d0915ff6e221722a4d6d26215d52a77d4b4b90259b22f7447",
"options/locale/locale_it-IT.json": "c67559a29021731cb7ffab0fe25a4300025c7e45d9d61488f1a1c99752360854",
"options/locale/locale_ja-JP.json": "e5e64b955a358630b43b6f4cb79b1790175f685a90d46f7071d93dafbdbce9cb",
"options/locale/locale_ko-KR.json": "5f4ed6436333a25c1c638c8e22c8bdf1cf71e96162c43e61a9fa86d0c3bb2bab",
"options/locale/locale_lv-LV.json": "eab8fa9711921b30166649050955c9ae661a0da15261c89be92687508469d863",
"options/locale/locale_nl-NL.json": "b8e58a9832d5da637656f53c6288e35860402e458b3ffd552c0d3c980310caef",
"options/locale/locale_pl-PL.json": "b8cec5b14ac491b16b0f11254df294ac6806be684d0d28e0758f42f48d6d993f",
"options/locale/locale_pt-BR.json": "c9e7d762eb9a00733fcdd935b565372ec28a2cdc11d6d95c9ea06a870b1951a4",
"options/locale/locale_pt-PT.json": "64ab9f016103379b9b5d18d71b03190bcb4fd38e9e4f1e1768a23b14edc21840",
"options/locale/locale_ru-RU.json": "d607a77a12abe0ccb87d9392a096369005b48ac74b2d4767a19455bd608f2c42",
"options/locale/locale_si-LK.json": "ed1f7fc6fd2153d541b124dc1051666fbc8750b9c1fd3f3befd5d976590ffc0a",
"options/locale/locale_sk-SK.json": "91adc0dfe01783eb9c6b7f4a7c9941a215079a610cd1ecbeac1fe574ea67c95d",
"options/locale/locale_sv-SE.json": "c4a6448c23dbed6e383d0b417a8a872abf187f76394780da6809182ba4343749",
"options/locale/locale_tr-TR.json": "6d5774ff6293e60bee0961c072e0ddea272656a12afe8d6eb94e330bcc8dcdae",
"options/locale/locale_uk-UA.json": "fabce931b5f7bc378494fe752f3a5148d6f37c6b9ae07674c09d3afa274378d5",
"options/locale/locale_zh-CN.json": "682925d41494eab564ff1a13269def6e396676d13cfcefabb22aeff75b808a4d",
"options/locale/locale_zh-TW.json": "9d242d16492e808889d7cdcc5db2ee5e53baabfc3d3dc13ea854c8852e963ec4",
"public/assets/img/favicon.png": "cbe472b55c0b2f2e57db58e991fc457e136fbaf246f54aaf7631ff089c76344f",
"templates/mail/base/footer.tmpl": "5accf2a39dffd952fd2d55a4fa867d33d91415792f11f90bc7e2fc8f8492117b",
"templates/mail/base/head.tmpl": "239a2f805949ca7ebd27fa11a34a20bbb7a5d435a95f3c25c55a4139126d7476",
"templates/mail/org/team_invite.tmpl": "646133e75c9efdce96eb7d58d8aea0111cae890c816a718ee8098346ab089a44",
"templates/mail/repo/actions/workflow_run.tmpl": "dbd34f3c7fb4f10a7497ca2872a648104c098f425866cae50f44b98033000b45",
"templates/mail/repo/collaborator.tmpl": "dd8a858a4fcdb37c71c88b9e51f7f5bbfa3c6afec572e2c9c7801fde4ececfb5",
"templates/mail/repo/issue/assigned.tmpl": "347cbe856d748a8c36fb8c9e5db64b831b2a8faf86a266a2e926e2eb1ab9a24b",
"templates/mail/repo/issue/default.tmpl": "98c36bb240f4946d9364fa4ebd386cff599f03bc2526c107881b8ce5a39a9ab8",
"templates/mail/repo/release.tmpl": "5b6c37286eedff5502efaeeaefac52190b835af0711055f387115bb4dd7ed4e5",
"templates/mail/repo/transfer.tmpl": "f394e8c0a75a16bbd19389b2bc286111326bcbcbb4e900e43b708ae74d0546a5",
"templates/mail/user/auth/activate.tmpl": "cd360177635d49663f25d700ac1d7f15f551f8de152053a39402267f896cfdee",
"templates/mail/user/auth/activate_email.tmpl": "3bfa50ce1801fc928d6359a8a52fd9578dac40f185aea4c1d792fc5ddd4bbf0c",
"templates/mail/user/auth/register_notify.tmpl": "ee11808ccc63241cedc29c2038a8ee29583817f51743bb56adc379944de5c8af",
"templates/mail/user/auth/reset_passwd.tmpl": "9efd3651c5e82a2d411c569f0736c6c55201a062d29747b985eb6af268f81b8e"
}
}
+209 -115
View File
@@ -29,6 +29,7 @@ body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:v
#topbar .btn-group button+button{border-left:none}
#topbar .btn-group button.on{background:var(--accent);color:#000;border-color:var(--accent);font-weight:600}
#topbar .hint{font-size:10px;color:var(--muted);opacity:.5;white-space:nowrap}
button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
/* Topbar responsive — progressively compact as width shrinks */
@media(max-width:900px){#topbar .hint{display:none}}
@@ -49,32 +50,29 @@ body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:v
/* Main layout */
#main{flex:1;display:flex;position:relative;min-height:0;overflow:hidden}
#preview-area{flex:1;display:flex;align-items:flex-start;justify-content:center;padding:24px;overflow:auto;background:linear-gradient(135deg,#0b1120,#131c30,#0d1525);transition:padding-right .3s cubic-bezier(.4,0,.2,1)}
#preview-area{flex:1;min-width:0;min-height:0;display:flex;align-items:flex-start;justify-content:center;padding:24px;overflow:auto;background:linear-gradient(135deg,#0b1120,#131c30,#0d1525)}
/* --- Floating panel --- */
/* Panel and preview share the available width; the handle occupies its own gutter. */
#panel{
position:absolute;right:12px;top:12px;bottom:12px;
width:var(--panel-w);
--panel-w:min(440px,40vw);
position:relative;flex:0 0 var(--panel-w);width:var(--panel-w);min-width:0;
margin:12px 12px 12px 40px;
background:var(--surface);
border:1px solid var(--border);
border-radius:10px;
box-shadow:-4px 4px 32px rgba(0,0,0,.5),0 0 0 1px rgba(255,255,255,.03) inset;
z-index:20;
display:flex;flex-direction:column;
overflow:hidden;
transition:transform .35s cubic-bezier(.4,0,.2,1),opacity .35s ease,box-shadow .35s ease;
transition:flex-basis .3s ease,width .3s ease;
}
#panel.collapsed{
transform:translateX(calc(var(--panel-w) - 36px));
box-shadow:-2px 2px 16px rgba(0,0,0,.35),0 0 0 1px rgba(255,255,255,.03) inset;
opacity:.92;
flex-basis:0;width:0;border-width:0;background:transparent;box-shadow:none;
}
#panel.collapsed:hover{opacity:1}
#panel.collapsed #panel-body{visibility:hidden}
/* Panel drag-handle / collapse tab */
#panel-handle{
position:absolute;left:0;top:50%;transform:translateY(-50%);
width:28px;height:64px;
position:absolute;left:-40px;top:50%;transform:translateY(-50%);
width:40px;height:80px;
background:var(--surface);
border:1px solid var(--border);
border-right:none;
@@ -82,17 +80,11 @@ body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:v
cursor:pointer;
display:flex;align-items:center;justify-content:center;
transition:background .15s;
z-index:21;
/* sit just outside the panel's left edge */
margin-left:-1px;
}
#panel.collapsed #panel-handle{
/* when collapsed, handle moves with panel, peeking from right edge */
left:1px;
color:var(--muted);
}
#panel-handle:hover{background:var(--bg)}
#panel-handle svg{
width:14px;height:14px;
width:20px;height:20px;
stroke:var(--muted);
fill:none;stroke-width:2;
transition:transform .35s cubic-bezier(.4,0,.2,1),stroke .15s;
@@ -101,22 +93,7 @@ body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:v
#panel.collapsed #panel-handle svg{transform:rotate(180deg)}
/* Panel inner scroll */
#panel-body{flex:1;overflow-y:auto;overflow-x:hidden}
/* Panel close button (top-right) */
#panel-close{
position:absolute;right:8px;top:8px;
width:24px;height:24px;
background:none;border:none;
cursor:pointer;
display:flex;align-items:center;justify-content:center;
border-radius:4px;
color:var(--muted);
font-size:16px;line-height:1;
transition:background .15s,color .15s;
z-index:2;
}
#panel-close:hover{background:rgba(255,255,255,.06);color:var(--text)}
#panel-body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;border-radius:10px}
/* Section */
#panel section{padding:12px 14px;border-bottom:1px solid var(--border)}
@@ -148,17 +125,15 @@ section.collapsed .section-chevron{transform:rotate(-90deg)}
/* Collapsible content wrapper */
.section-content{
overflow:hidden;
transition:max-height .3s ease,opacity .25s ease,margin-top .3s ease;
max-height:2000px;
opacity:1;
margin-top:10px;
display:grid;grid-template-rows:1fr;opacity:1;margin-top:10px;
transition:grid-template-rows .3s ease,opacity .25s ease,margin-top .3s ease;
}
.section-content-inner{min-height:0;overflow:hidden}
section.collapsed .section-content{
max-height:0;
opacity:0;
margin-top:0;
grid-template-rows:0fr;opacity:0;margin-top:0;
}
#sec-params .section-content-inner{max-height:50vh;overflow:auto;overscroll-behavior:contain}
section.collapsed .section-content-inner{overflow:hidden}
#panel .card{background:var(--bg);border-radius:var(--rad);padding:10px 12px;margin-bottom:8px}
#panel .card:last-child{margin-bottom:0}
@@ -166,19 +141,21 @@ section.collapsed .section-content{
#panel .card .value{font-size:12px;color:var(--text)}
#panel table{width:100%;border-collapse:collapse}
#panel table td{padding:4px 6px;font-size:11px;border-bottom:1px solid rgba(255,255,255,.03);vertical-align:top}
#panel table td:first-child{font-family:monospace;color:var(--accent);font-size:10px;width:0;white-space:nowrap}
#panel table td:last-child{color:var(--muted);text-align:right;font-size:10px;word-break:break-all}
#panel table td:first-child{font-family:monospace;color:var(--accent);font-size:10px;overflow-wrap:anywhere}
#panel table td:nth-child(2){color:var(--muted);font-size:10px;overflow-wrap:anywhere}
#panel table td:last-child{text-align:right;width:1%}
#panel .badge{display:inline-block;font-size:9px;padding:1px 6px;border-radius:3px;font-weight:600;letter-spacing:.2px;white-space:nowrap}
.badge-req{background:rgba(52,211,153,.12);color:var(--green)}
.badge-opt{background:rgba(107,125,153,.1);color:var(--muted)}
#panel .stat{font-size:10px;color:var(--muted);margin-top:6px}
/* Viewport */
#viewport{border-radius:var(--rad);box-shadow:0 8px 32px rgba(0,0,0,.45);transition:all .3s;flex-shrink:0;overflow:hidden;background:#fff}
#viewport.desktop{width:1386px;height:780px}
#viewport{border:0 solid #1e293b;border-radius:var(--rad);box-shadow:0 8px 32px rgba(0,0,0,.45);flex-shrink:0;max-width:100%;overflow:hidden;background:#fff;display:flex;flex-direction:column;transition:width .3s ease,border-radius .3s ease,border-width .3s ease,box-shadow .3s ease}
#viewport.desktop{width:100%;max-width:1386px;height:780px}
#viewport.mobile{width:390px;height:780px;border-radius:28px;border:3px solid #1e293b;box-shadow:0 8px 32px rgba(0,0,0,.5),inset 0 0 0 1px #0f172a;display:flex;flex-direction:column}
#viewport.mobile::before{content:'';display:block;height:24px;background:#1e293b;flex-shrink:0}
#preview-frame{width:100%;height:100%;border:none;display:block;background:#fff}
#viewport::before{content:'';display:block;height:0;background:#1e293b;flex-shrink:0;transition:height .3s ease}
#viewport.mobile::before{height:24px}
#preview-frame{width:100%;height:100%;flex:1;min-height:0;border:none;display:block;background:#fff}
#viewport.desktop #preview-frame{border-radius:var(--rad)}
/* Loading */
@@ -193,7 +170,7 @@ section.collapsed .section-content{
.message code{background:rgba(0,0,0,0.12);padding:1px 6px;border-radius:3px}
/* Scrollbar — neutral gray, consistent across all browsers */
html,body,#preview-area,#panel-body{
html,body,#preview-area,#panel-body,#sec-params .section-content-inner{
scrollbar-width:thin;
scrollbar-color:rgba(107,125,153,.25) transparent;
}
@@ -201,27 +178,30 @@ html,body,#preview-area,#panel-body{
-ms-overflow-style:-ms-autohiding-scrollbar;
}
#preview-area::-webkit-scrollbar,
#sec-params .section-content-inner::-webkit-scrollbar,
#panel-body::-webkit-scrollbar { width:6px; height:6px }
#preview-area::-webkit-scrollbar-track,
#sec-params .section-content-inner::-webkit-scrollbar-track,
#panel-body::-webkit-scrollbar-track { background:transparent }
#preview-area::-webkit-scrollbar-thumb,
#sec-params .section-content-inner::-webkit-scrollbar-thumb,
#panel-body::-webkit-scrollbar-thumb {
background-color:rgba(107,125,153,.25);
border-radius:3px;
border:1px solid rgba(255,255,255,.06);
}
#preview-area::-webkit-scrollbar-thumb:hover,
#sec-params .section-content-inner::-webkit-scrollbar-thumb:hover,
#panel-body::-webkit-scrollbar-thumb:hover { background-color:rgba(107,125,153,.45) }
#preview-area::-webkit-scrollbar-corner,
#sec-params .section-content-inner::-webkit-scrollbar-corner,
#panel-body::-webkit-scrollbar-corner { background:transparent }
@media(max-width:900px){
#panel{--panel-w:380px;right:6px;top:6px;bottom:6px}
#panel.collapsed{transform:translateX(calc(var(--panel-w) - 36px))}
}
@media(max-width:700px){
#panel{display:none}
#panel{--panel-w:50vw;margin-top:6px;margin-bottom:6px;margin-right:6px}
#preview-area{padding:12px}
}
@media(prefers-reduced-motion:reduce){#panel,#panel-handle svg,.section-content,.section-chevron,#viewport,#viewport::before{transition:none}}
</style>
</head>
<body>
@@ -239,6 +219,11 @@ html,body,#preview-area,#panel-body{
<select id="sel-template" onchange="switchTemplate(this.value)"></select>
</span>
<span class="sep"></span>
<span class="ctl-group">
<label>Language</label>
<select id="sel-locale" onchange="switchLocale(this.value)"></select>
</span>
<span class="sep"></span>
<span class="ctl-group">
<label>View</label>
<select id="sel-client" onchange="switchClient(this.value)">
@@ -269,12 +254,9 @@ html,body,#preview-area,#panel-body{
<aside id="panel">
<!-- Collapse handle tab on the left edge -->
<div id="panel-handle" title="Toggle panel" onclick="togglePanel()">
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
</div>
<!-- Close button (top-right) -->
<button id="panel-close" title="Close panel" onclick="togglePanel()">&times;</button>
<button id="panel-handle" type="button" title="Collapse panel" aria-label="Collapse panel" aria-expanded="true" aria-controls="panel-body" onclick="togglePanel()">
<svg viewBox="0 0 24 24" aria-hidden="true"><polyline points="9 18 15 12 9 6"/></svg>
</button>
<div id="panel-body">
<section id="sec-info">
@@ -283,6 +265,7 @@ html,body,#preview-area,#panel-body{
<svg class="section-chevron" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<div class="section-content">
<div class="section-content-inner">
<div class="card">
<div class="label">Type</div>
<div class="value" id="panel-name">-</div>
@@ -293,6 +276,7 @@ html,body,#preview-area,#panel-body{
</div>
<div class="stat" id="panel-stats"></div>
</div>
</div>
</section>
<section id="sec-params">
@@ -301,8 +285,10 @@ html,body,#preview-area,#panel-body{
<svg class="section-chevron" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<div class="section-content">
<div class="section-content-inner">
<table id="panel-params"><tr><td colspan="2" style="color:var(--muted)">-</td></tr></table>
</div>
</div>
</section>
<section id="sec-source">
@@ -311,8 +297,10 @@ html,body,#preview-area,#panel-body{
<svg class="section-chevron" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<div class="section-content">
<div class="section-content-inner">
<div class="card" style="font-family:monospace;font-size:10px;color:var(--muted);word-break:break-all" id="panel-path">-</div>
</div>
</div>
</section>
</div>
</aside>
@@ -323,10 +311,14 @@ html,body,#preview-area,#panel-body{
//
// State — REGISTRY and PARAMS are loaded from rendered.js (single source of truth)
//
const state = { theme:'', template:'', client:'modern', viewport:'desktop' };
const state = { theme:'', template:'', client:'modern', viewport:'desktop', locale:'en-US' };
let REGISTRY = {};
let PARAMS = {};
let rendered = null; // window.__RENDERED__
let rendered = null;
let manifest = null;
let localeRequest = 0;
let renderRequest = 0, renderTimer;
const localeLoads = new Map();
let devMode = false; // true once SSE connects
//
@@ -338,20 +330,31 @@ function transform(html, mode) {
const e = html.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
return '<!DOCTYPE html><html><head><style>body{background:#0d1117;color:#c9d1d9;font-family:Consolas,monospace;padding:16px;margin:0}pre{white-space:pre-wrap;word-break:break-all;font-size:11px;line-height:1.6;margin:0}</style></head><body><pre>'+e+'</pre></body></html>';
}
return html;
// Gitea sends status icons as CID attachments. Preview has no MIME message,
// so display equivalent local SVG icons without changing install templates.
const doc = new DOMParser().parseFromString(html, 'text/html');
// Static preview has no deployed instance; use the generated official asset.
// Installable mail templates keep the instance's {{AppUrl}} image URL.
doc.querySelectorAll('img.email-logo').forEach(img => { if(manifest?.logo) img.src=manifest.logo; });
doc.querySelectorAll('img[src^="cid:"]').forEach(img => {
const color = img.classList.contains('status-success') ? '#2da44e' : '#697077';
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><circle cx="9" cy="9" r="7" fill="'+color+'"/><path d="M5 9l3 3 5-6" fill="none" stroke="white" stroke-width="2"/></svg>';
img.src = 'data:image/svg+xml,' + encodeURIComponent(svg);
});
return '<!DOCTYPE html>' + doc.documentElement.outerHTML;
}
//
// Render preview
//
function render() {
const request=++renderRequest;
clearTimeout(renderTimer);
const frame = document.getElementById('preview-frame');
const loading = document.getElementById('loading');
if(!rendered) return;
if (location.protocol !== 'file:') {
frame.setAttribute('sandbox', '');
}
frame.style.display = 'none';
loading.style.display = 'flex';
@@ -359,18 +362,19 @@ function render() {
const src = rendered;
const raw = src[state.theme]?.[state.template];
let content = raw ? transform(raw, state.client) : '<p style=\"color:red;padding:20px\">Not found: '+state.theme+'/'+state.template+'</p>';
content = content.replace('</head>', '<style>html{scrollbar-color:rgba(107,125,153,.25) transparent;scrollbar-width:thin;-ms-overflow-style:-ms-autohiding-scrollbar}html::-webkit-scrollbar{width:6px;height:6px}html::-webkit-scrollbar-track{background:transparent}html::-webkit-scrollbar-thumb{background-color:rgba(107,125,153,.25);border-radius:3px;border:1px solid rgba(255,255,255,.06)}html::-webkit-scrollbar-thumb:hover{background-color:rgba(107,125,153,.45)}html::-webkit-scrollbar-corner{background:transparent}</style></head>');
content = content.replace('</head>', '<style>html,body{scrollbar-width:none;-ms-overflow-style:none}html::-webkit-scrollbar,body::-webkit-scrollbar{display:none;width:0;height:0}</style></head>');
let done = false;
function finish() {
if (done) return; done = true;
if (done || request!==renderRequest) return; done = true;
clearTimeout(renderTimer);
loading.style.display = 'none';
frame.style.display = 'block';
frame.onload = null;
}
frame.onload = finish;
frame.srcdoc = content;
setTimeout(finish, 5000);
renderTimer=setTimeout(finish, 5000);
updatePanel();
}
@@ -379,22 +383,29 @@ function render() {
//
function togglePanel() {
const panel = document.getElementById('panel');
panel.classList.toggle('collapsed');
const collapsed = panel.classList.toggle('collapsed');
const handle = document.getElementById('panel-handle');
handle.setAttribute('aria-expanded', String(!collapsed));
handle.setAttribute('aria-label', collapsed ? 'Expand panel' : 'Collapse panel');
handle.title = collapsed ? 'Expand panel' : 'Collapse panel';
document.getElementById('panel-body').inert = collapsed;
}
function toggleSection(id) {
const sec = document.getElementById(id);
sec.classList.toggle('collapsed');
const collapsed = sec.classList.toggle('collapsed');
sec.querySelector('.section-content').inert = collapsed;
}
//
// Panel content updates
//
function updatePanel() {
const escape = v => String(v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
const r = REGISTRY[state.template];
document.getElementById('panel-name').textContent = r ? r.name : state.template;
document.getElementById('panel-desc').textContent = r ? r.desc : '';
document.getElementById('panel-path').textContent = r ? 'themes/'+state.theme+'/'+r.path : '-';
document.getElementById('panel-path').textContent = r ? 'build/themes/'+state.theme+'/'+r.path+' (official snapshot + theme overrides)' : '-';
// Template stats
const themes = rendered ? Object.keys(rendered).length : 0;
@@ -407,13 +418,13 @@ function updatePanel() {
}
}
document.getElementById('panel-stats').textContent =
themes + ' themes loaded · ' + totalTemplates + ' templates (' + (totalTemplates - totalFailed) + ' ok, ' + totalFailed + ' failed)';
manifest.upstream + ' · ' + state.locale + ' · ' + themes + ' themes · ' + totalTemplates + ' templates (' + (totalTemplates - totalFailed) + ' ok, ' + totalFailed + ' failed)';
const p = PARAMS[state.template] || {};
let rows = '';
for(const [k,v] of Object.entries(p)){
const isReq = !k.includes('RenderedNote') && !k.includes('Attachments') && !k.includes('DisableDownload') && !k.includes('IsMention') && !k.includes('IsPull') && !k.includes('IsForcePush') && !k.includes('ReviewComments') && !k.includes('Commits') && !k.includes('BaseBranch');
rows += '<tr><td>.'+k+'</td><td>'+String(v)+'</td><td><span class="badge '+(isReq?'badge-req':'badge-opt')+'">'+(isReq?'required':'optional')+'</span></td></tr>';
rows += '<tr><td>.'+escape(k)+'</td><td>'+escape(v)+'</td><td><span class="badge '+(isReq?'badge-req':'badge-opt')+'">'+(isReq?'required':'optional')+'</span></td></tr>';
}
document.getElementById('panel-params').innerHTML = rows || '<tr><td colspan="3" style="color:var(--muted)">-</td></tr>';
}
@@ -422,14 +433,14 @@ function updatePanel() {
// Events
//
function switchTheme(v) { state.theme=v; render(); }
function switchTemplate(v) { state.template=v; render(); }
function switchTemplate(v) { state.template=v; switchLocale(state.locale); }
function switchClient(v) { state.client=v; render(); }
function setViewport(v) {
if (state.viewport === v) return;
state.viewport=v;
document.getElementById('btn-desk').classList.toggle('on',v==='desktop');
document.getElementById('btn-mob').classList.toggle('on',v==='mobile');
document.getElementById('viewport').className=v;
render();
}
//
@@ -439,11 +450,11 @@ document.addEventListener('keydown',e=>{
// Viewport shortcuts (no modifiers)
if(e.key==='d'&&!e.ctrlKey&&!e.metaKey&&!e.altKey){ setViewport('desktop'); return; }
if(e.key==='m'&&!e.ctrlKey&&!e.metaKey&&!e.altKey){ setViewport('mobile'); return; }
// 'p' to toggle floating panel
// 'p' to toggle the side panel
if(e.key==='p'&&!e.ctrlKey&&!e.metaKey&&!e.altKey){ togglePanel(); return; }
// ← → cycle focus between Theme / Template / Client selects
const selects=['sel-theme','sel-template','sel-client'];
const selects=['sel-theme','sel-template','sel-locale','sel-client'];
const activeId=document.activeElement?.id;
const cur=selects.indexOf(activeId);
@@ -460,38 +471,119 @@ document.addEventListener('keydown',e=>{
});
//
// Init — load everything from rendered.js (single source of truth)
//
(function(){
if(window.__REGISTRY__) REGISTRY = window.__REGISTRY__;
if(window.__PARAMS__) PARAMS = window.__PARAMS__;
if(window.__RENDERED__){
rendered = window.__RENDERED__;
const themeKeys = Object.keys(rendered).sort();
const selTheme = document.getElementById('sel-theme');
themeKeys.forEach(t => {
const o = document.createElement('option');
o.value = t;
o.textContent = t.charAt(0).toUpperCase() + t.slice(1);
selTheme.appendChild(o);
});
state.theme = themeKeys[0] || '';
const selTpl = document.getElementById('sel-template');
for(const [id, t] of Object.entries(REGISTRY)){
const o = document.createElement('option');
o.value = id;
o.textContent = t.name;
selTpl.appendChild(o);
// Manifest and language bundles
function fillSelect(id, entries, selected) {
const select = document.getElementById(id);
select.replaceChildren();
for (const [value,label] of entries) {
const option = document.createElement('option');
option.value=value; option.textContent=label; select.appendChild(option);
}
state.template = Object.keys(REGISTRY)[0] || '';
} else {
document.getElementById('loading').style.display='none';
showMessage('rendered.js not found — run <code>cd tools &amp;&amp; go run . preview all</code> to generate', 'error');
select.value=selected;
}
// Stable native names also cover browsers with incomplete ICU locale data.
const LOCALE_NAMES = {
'cs-CZ':'čeština (Česko)', 'de-DE':'Deutsch (Deutschland)',
'el-GR':'Ελληνικά (Ελλάδα)', 'en-US':'English (United States)',
'es-ES':'español (España)', 'fa-IR':'فارسی (ایران)',
'fi-FI':'suomi (Suomi)', 'fr-FR':'français (France)',
'ga-IE':'Gaeilge (Éire)', 'hu-HU':'magyar (Magyarország)',
'id-ID':'Indonesia (Indonesia)', 'is-IS':'íslenska (Ísland)',
'it-IT':'italiano (Italia)', 'ja-JP':'日本語 (日本)',
'ko-KR':'한국어 (대한민국)', 'lv-LV':'latviešu (Latvija)',
'nl-NL':'Nederlands (Nederland)', 'pl-PL':'polski (Polska)',
'pt-BR':'português (Brasil)', 'pt-PT':'português (Portugal)',
'ru-RU':'русский (Россия)', 'si-LK':'සිංහල (ශ්‍රී ලංකාව)',
'sk-SK':'slovenčina (Slovensko)',
'sv-SE':'svenska (Sverige)', 'tr-TR':'Türkçe (Türkiye)',
'uk-UA':'українська (Україна)', 'zh-CN':'中文 (简体,中国)',
'zh-TW':'中文 (繁體,台灣)'
};
function localeLabel(locale) {
let name = LOCALE_NAMES[locale];
if (!name) {
try {
if (Intl.DisplayNames.supportedLocalesOf([locale]).length) {
name = new Intl.DisplayNames([locale], {type:'language', fallback:'none'}).of(locale);
}
} catch (_) { /* Unknown languages remain selectable by their locale code. */ }
}
return name && name !== locale ? locale + ' — ' + name : locale;
}
function applyManifest(data) {
if (!data || data.schema !== 1 || !data.languages.length || !data.themes.length) {
throw new Error('Invalid preview manifest; regenerate with go run . preview all');
}
manifest=data; REGISTRY=data.registry; PARAMS=data.params;
if (!data.themes.includes(state.theme)) state.theme=data.themes[0];
if (!REGISTRY[state.template]) state.template=Object.keys(REGISTRY)[0];
if (!data.languages.includes(state.locale)) state.locale=data.languages.includes('en-US') ? 'en-US' : data.languages[0];
fillSelect('sel-theme',data.themes.map(t=>[t,t.charAt(0).toUpperCase()+t.slice(1)]),state.theme);
fillSelect('sel-template',Object.entries(REGISTRY).map(([id,t])=>[id,t.name]),state.template);
fillSelect('sel-locale',data.languages.map(locale=>[locale,localeLabel(locale)]),state.locale);
document.querySelectorAll('#sel-locale option').forEach(option=>{
option.lang=option.value;
option.dir='auto';
});
}
function loadLocale(lang) {
if (window.__MAIL_PREVIEW_DATA__?.[lang]) return Promise.resolve(window.__MAIL_PREVIEW_DATA__[lang]);
if (localeLoads.has(lang)) return localeLoads.get(lang);
const promise=new Promise((resolve,reject)=>{
const script=document.createElement('script');
script.src='rendered/'+encodeURIComponent(lang)+'.js'+(devMode ? '?v='+Date.now() : '');
script.onload=()=>{
script.remove();
const data=window.__MAIL_PREVIEW_DATA__?.[lang];
if (data) resolve(data); else reject(new Error('Invalid language bundle '+lang));
};
script.onerror=()=>{ script.remove(); reject(new Error('Language bundle missing: '+lang+'; regenerate preview data')); };
document.head.appendChild(script);
});
localeLoads.set(lang,promise);
promise.catch(()=>localeLoads.delete(lang));
return promise;
}
async function switchLocale(lang) {
if (!manifest?.languages.includes(lang)) return;
state.locale=lang; rendered=null;
const request=++localeRequest;
document.getElementById('preview-frame').style.display='none';
document.getElementById('loading').style.display='flex';
try {
const data=await loadLocale(lang);
if (request!==localeRequest) return;
rendered=data;
try { localStorage.setItem('mail-preview-locale',lang); } catch (_) {}
document.getElementById('locale-message')?.remove();
const warnings=manifest.warnings?.[lang]?.[state.template] || [];
const fallback=manifest.fallback?.[lang] || [];
if (fallback.length || warnings.length) {
const el=showMessage('',warnings.length ? 'warn' : 'info'); el.id='locale-message';
el.textContent=warnings.join(' ') || '[FALLBACK] '+lang+': '+fallback.length+' referenced translation keys use en-US';
}
render();
} catch (error) {
if (request!==localeRequest) return;
document.getElementById('loading').style.display='none';
showMessage('', 'error').textContent = String(error);
}
}
(function(){
try {
try { state.locale=localStorage.getItem('mail-preview-locale') || 'en-US'; } catch (_) {}
applyManifest(window.__MAIL_PREVIEW__);
switchLocale(state.locale);
} catch (error) {
document.getElementById('loading').style.display='none';
showMessage('Preview data unavailable — run <code>cd tools &amp;&amp; go run . preview all</code> to generate','error');
}
})();
// Message helpers
@@ -512,7 +604,7 @@ function setDevMode(on) {
if (on) {
clearMessages();
showMessage(
'Live reload active — changes to .tmpl files will auto-refresh the preview.',
'Live reload active — theme CSS, shared controls, lock/cache and fixtures auto-refresh the preview.',
'info'
).id = 'msg-dev-disclaimer';
}
@@ -520,6 +612,7 @@ function setDevMode(on) {
// Dev-server live-reload (SSE)
(function(){
if (location.protocol === 'file:') return;
let es;
try {
es = new EventSource('/events');
@@ -533,14 +626,15 @@ function setDevMode(on) {
const script = document.createElement('script');
script.src = 'rendered.js?v=' + Date.now();
script.onload = function() {
if (window.__RENDERED__) rendered = window.__RENDERED__;
if (window.__REGISTRY__) REGISTRY = window.__REGISTRY__;
if (window.__PARAMS__) PARAMS = window.__PARAMS__;
render();
script.remove();
window.__MAIL_PREVIEW_DATA__ = {};
localeLoads.clear();
applyManifest(window.__MAIL_PREVIEW__);
switchLocale(state.locale);
};
document.head.appendChild(script);
} else if (msg.type === 'error') {
showMessage(msg.message, 'error');
showMessage('', 'error').textContent = msg.message;
}
} catch(_) {}
};
-142
View File
@@ -1,142 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.team_invite.subject" (.Inviter.DisplayName|DotEscape) (.Organization.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" .Invite.Email}}
</p>
<p class="email-text">
{{.locale.Tr "mail.team_invite.text_1" (DotEscape .Inviter.DisplayName) (DotEscape .Team.Name) (DotEscape .Organization.DisplayName)}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.team_invite.text_2"}}
</p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.InviteURL}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.InviteURL}}</p>
</td>
</tr>
</table>
<p class="email-subtext" style="margin-top:24px;font-size:12px;color:#a0afc880;">
{{.locale.Tr "mail.team_invite.text_3" .Invite.Email}}
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
@@ -1,141 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading"></p>
<p class="email-text">
<h2>{{.Repo.FullName}} - {{.Run.WorkflowID}}: {{.RunStatusText}}</h2>
<ul>
{{range $job := .Jobs}}
<li>
<a href="{{$job.HTMLURL}}">{{$job.Status}}: {{$job.Name}}{{if gt $job.Attempt 1}} (retry #{{$job.Attempt}}){{end}} - {{$job.Duration}}</a>
</li>
{{end}}
</ul>
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.Run.HTMLURL}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Run.HTMLURL}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
-134
View File
@@ -1,134 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading"></p>
<p class="email-text">
{{.locale.Tr "mail.repo.collaborator.added.text"}} <code>{{.RepoName}}</code>.
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
-140
View File
@@ -1,140 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading"></p>
<p class="email-text">
{{$repo_url := HTMLFormat "<a href=\047%s\047>%s</a>" .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
{{$link := HTMLFormat "<a href=\047%s\047>#%d</a>" .Link .Issue.Index}}
{{if .IsPull}}
{{.locale.Tr "mail.issue_assigned.pull" .Doer.Name $link $repo_url}}
{{else}}
{{.locale.Tr "mail.issue_assigned.issue" .Doer.Name $link $repo_url}}
{{end}}
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
-209
View File
@@ -1,209 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Content */
.aurora-content,.aurora-content * { color:#d2fcf5e0!important }
.aurora-content a,.aurora-content a:link,.aurora-content a:visited { color:#5eead4!important;text-decoration:underline!important }
.aurora-content a:hover,.aurora-content a:active { color:#99f6e4!important }
.aurora-content code,.aurora-content pre { color:#99f6e4!important;background:#0000004d!important;border-color:#0d948833!important }
.aurora-content blockquote { color:#ffffff99!important }
.aurora-content h1,.aurora-content h2,.aurora-content h3,.aurora-content h4,.aurora-content h5,.aurora-content h6 { color:#ccfbf1!important }
.aurora-content hr { border-color:#0d94884d!important }
.aurora-content img { border:0;max-width:100%!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading"></p>
<p class="email-text">
{{if .IsMention}}
<p>{{.locale.Tr "mail.issue.x_mentioned_you" .Doer.Name}}</p>
{{end}}
{{if eq .ActionName "push"}}
<p>
{{if .Comment.IsForcePush}}
{{$oldCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.OldCommit}}
{{$oldShortSha := ShortSha .Comment.OldCommit}}
{{$oldCommitLink := HTMLFormat "<a href=\047%[1]s\047>%[2]s</a>" $oldCommitUrl $oldShortSha}}
{{$newCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.NewCommit}}
{{$newShortSha := ShortSha .Comment.NewCommit}}
{{$newCommitLink := HTMLFormat "<a href=\047%[1]s\047>%[2]s</a>" $newCommitUrl $newShortSha}}
{{.locale.Tr "mail.issue.action.force_push" .Doer.Name .Comment.Issue.PullRequest.HeadBranch $oldCommitLink $newCommitLink}}
{{else}}
{{.locale.TrN (len .Comment.Commits) "mail.issue.action.push_1" "mail.issue.action.push_n" .Doer.Name .Comment.Issue.PullRequest.HeadBranch (len .Comment.Commits)}}
{{end}}
</p>
{{end}}
<p>
{{if eq .ActionName "close"}}
{{.locale.Tr "mail.issue.action.close" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "reopen"}}
{{.locale.Tr "mail.issue.action.reopen" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "merge"}}
{{.locale.Tr "mail.issue.action.merge" .Doer.Name .Issue.Index .Issue.PullRequest.BaseBranch}}
{{else if eq .ActionName "approve"}}
{{.locale.Tr "mail.issue.action.approve" .Doer.Name}}
{{else if eq .ActionName "reject"}}
{{.locale.Tr "mail.issue.action.reject" .Doer.Name}}
{{else if eq .ActionName "review"}}
{{.locale.Tr "mail.issue.action.review" .Doer.Name}}
{{else if eq .ActionName "review_dismissed"}}
{{.locale.Tr "mail.issue.action.review_dismissed" .Doer.Name .Comment.Review.Reviewer.Name}}
{{else if eq .ActionName "ready_for_review"}}
{{.locale.Tr "mail.issue.action.ready_for_review" .Doer.Name}}
{{end}}
{{- if eq .Body ""}}
{{if eq .ActionName "new"}}
{{.locale.Tr "mail.issue.action.new" .Doer.Name .Issue.Index}}
{{end}}
{{else}}
<span class="aurora-content">{{.Body}}</span>
{{end -}}
{{if .CanReply}}
&nbsp;{{.locale.Tr "mail.reply"}}
{{end}}
</p>
{{- range .ReviewComments}}
<hr>
<p>{{$.locale.Tr "mail.issue.in_tree_path" .TreePath}}</p>
<pre>{{.Patch}}</pre>
<div class="aurora-content">{{.RenderedContent}}</div>
{{end -}}
{{if eq .ActionName "push"}}
<ul>
{{range .Comment.Commits}}
<li>
<a href="{{$.Comment.Issue.PullRequest.BaseRepo.HTMLURL}}/commit/{{.UserCommit.GitCommit.ID}}">{{ShortSha .UserCommit.GitCommit.ID.String}}</a> - {{.UserCommit.GitCommit.MessageTitle}}
</li>
{{end}}
</ul>
{{end}}
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
-166
View File
@@ -1,166 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Content */
.aurora-content,.aurora-content * { color:#d2fcf5e0!important }
.aurora-content a,.aurora-content a:link,.aurora-content a:visited { color:#5eead4!important;text-decoration:underline!important }
.aurora-content a:hover,.aurora-content a:active { color:#99f6e4!important }
.aurora-content code,.aurora-content pre { color:#99f6e4!important;background:#0000004d!important;border-color:#0d948833!important }
.aurora-content blockquote { color:#ffffff99!important }
.aurora-content h1,.aurora-content h2,.aurora-content h3,.aurora-content h4,.aurora-content h5,.aurora-content h6 { color:#ccfbf1!important }
.aurora-content hr { border-color:#0d94884d!important }
.aurora-content img { border:0;max-width:100%!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading"></p>
<p class="email-text">
{{$release_url := HTMLFormat "<a href=\047%s\047>%s</a>" .Release.HTMLURL .Release.TagName}}
{{$repo_url := HTMLFormat "<a href=\047%s\047>%s</a>" .Release.Repo.HTMLURL .Release.Repo.FullName}}
{{.locale.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url}}
<h4>{{.locale.Tr "mail.release.title" .Release.Title}}</h4>
{{if .Release.RenderedNote}}
<p class="email-text">{{.locale.Tr "mail.release.note"}}</p>
<div class="aurora-content" style="font-size:14px;line-height:2;">{{.Release.RenderedNote}}</div>
{{end}}
<p class="email-text">{{.locale.Tr "mail.release.downloads"}}</p>
<ul>
{{if not .DisableDownloadSourceArchives}}
<li><a href="{{.Release.Repo.HTMLURL}}/archive/{{.Release.TagName | PathEscapeSegments}}.zip">{{.locale.Tr "mail.release.download.zip"}}</a></li>
<li><a href="{{.Release.Repo.HTMLURL}}/archive/{{.Release.TagName | PathEscapeSegments}}.tar.gz">{{.locale.Tr "mail.release.download.targz"}}</a></li>
{{end}}
{{if .Release.Attachments}}
{{range .Release.Attachments}}
<li><a href="{{.DownloadURL}}">{{.Name}} ({{.Size | FormatByteSize}})</a></li>
{{end}}
{{end}}
</ul>
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
-135
View File
@@ -1,135 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading"></p>
<p class="email-text">
{{$url := HTMLFormat "<a href=\047%[1]s\047>%[2]s</a>" .Link .Repo}}
{{.Subject}}. {{.locale.Tr "mail.repo.transfer.body" $url}}
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
-136
View File
@@ -1,136 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.activate_account.title" (.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-text">
{{.locale.Tr "mail.activate_account.text_1" (.DisplayName|DotEscape) AppName}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.activate_account.text_2" .ActiveCodeLives}}
</p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
{{$activate_url := printf "%suser/activate?code=%s" AppUrl (QueryEscape .Code)}}
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{$activate_url}}" class="email-btn">{{.locale.Tr "mail.activate_account"}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{$activate_url}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
@@ -1,138 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.activate_email.title" (.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}
</p>
<p class="email-text">
{{.locale.Tr "mail.activate_email.text" .ActiveCodeLives}}
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
{{$activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl (QueryEscape .Code) (QueryEscape .Email)}}
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{$activate_url}}" class="email-btn">{{.locale.Tr "mail.activate_email"}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{$activate_url}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
@@ -1,140 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.register_notify.title" (.DisplayName|DotEscape) AppName}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}
</p>
<p class="email-text">
{{.locale.Tr "mail.register_notify.text_1" AppName}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.register_notify.text_2" .Username}}
{{$set_pwd_url := printf "%suser/forgot_password" AppUrl}}
{{.locale.Tr "mail.register_notify.text_3" $set_pwd_url}}
</p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{AppUrl}}user/login" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{AppUrl}}user/login</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
@@ -1,138 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.reset_password.title" (.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading { font-size:20px!important }
}
</style>
</head>
<body style="margin:0;padding:0;background:#0a0815;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrap">
<!-- Floating Title -->
<tr>
<td class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="24" height="24" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
<!-- Card -->
<tr>
<td class="email-card card-body">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}
</p>
<p class="email-text">
{{.locale.Tr "mail.reset_password.text" .ResetPwdCodeLives}}
</p>
<p class="email-subtext"></p>
</td>
</tr>
<!-- Divider -->
<tr>
<td class="email-divider"></td>
</tr>
<!-- Button & Link -->
<tr>
<td class="email-btn-row">
{{$recover_url := printf "%suser/recover_account?code=%s" AppUrl (QueryEscape .Code)}}
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-table">
<tr>
<td align="center">
<a href="{{$recover_url}}" class="email-btn">{{.locale.Tr "mail.reset_password"}}</a>
</td>
</tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="email-link-box">
<tr>
<td style="padding:16px 18px;">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{$recover_url}}</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</td>
</tr>
</table>
</center>
</body>
</html>
+86
View File
@@ -0,0 +1,86 @@
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#0a0815 }
.email-wrap { max-width:600px;margin:40px auto;font-family:system-ui,-apple-system,sans-serif }
/* Header */
.email-logo { display:block;margin:0 auto 14px;border:0;outline:none;opacity:0.5 }
.email-brand { font-size:16px;font-weight:300;color:#ffffff66;letter-spacing:4px;text-transform:uppercase }
.email-header { padding:0 0 32px;text-align:center }
/* Card */
.email-card { padding:48px 40px;background:#191432cc;border:1px solid #0d948833;box-shadow:0 0 60px #0d948814,0 0 120px #6366f10a }
.email-heading,
.mail-type-activate > p:first-child,
.mail-type-activate_email > p:first-child { margin:0 0 20px;font-size:24px;font-weight:300;color:#ccfbf1;line-height:1.3;letter-spacing:-.5px }
.email-text,
.mail-type-activate > p:nth-of-type(2) { margin:0 0 24px;font-size:14px;line-height:2;color:#d2fcf5e0;font-weight:300 }
.email-subtext { margin:0;font-size:13px;line-height:1.8;color:#b4c3d7f2;font-weight:300 }
/* Divider */
.email-divider { height:1px;background:linear-gradient(90deg,transparent,#0d94884d,transparent) }
/* Button */
.email-btn-row { padding:40px 40px;text-align:center }
.email-btn,.card-body .email-btn,.card-body .email-btn:link,.card-body .email-btn:visited { display:inline-block;padding:16px 48px;font-size:14px;font-weight:400;color:#e0f5f3!important;text-decoration:none!important;background:#0d94882e;border:1px solid #0d94884d;letter-spacing:1px }
.email-btn-table { margin:0 0 32px }
/* Fallback link */
.email-link-box { border:1px solid #0d94882e;background:#0d948808 }
.email-link-label { margin:0 0 8px;font-size:11px;color:#b4fceebf;font-weight:300 }
.email-link-url { margin:0;font-size:11px;font-family:monospace;color:#99f6e4;background:#00000033;padding:10px 12px;border:1px solid #0d948826;word-break:break-all }
/* Footer */
.email-footer { padding:32px 0 0;text-align:center }
.email-footer-text { margin:0;font-size:11px;color:#a0afc88c;font-weight:300 }
.email-footer-link { color:#5eead480;text-decoration:none;font-weight:400 }
/* Typography utilities */
.card-body h1,.card-body h2,.card-body h3,.card-body h4,.card-body h5,.card-body h6 { color:#ccfbf1!important }
.card-body a,.card-body a:link,.card-body a:visited { color:#5eead4!important;text-decoration:underline!important }
.card-body a:hover,.card-body a:active { color:#99f6e4!important }
.card-body li { color:#d2fcf5e0!important }
/* Responsive */
@media only screen and (max-width:480px) {
.email-card { padding:32px 20px!important }
.email-btn-row { padding:24px 20px!important }
.email-heading,
.mail-type-activate > p:first-child,
.mail-type-activate_email > p:first-child { font-size:20px!important }
}
/* Official-snapshot presentation adapter: no content or translation overrides. */
body { margin:0; padding:24px 16px; background-color:#0a0815; }
body, td { font-family:Arial,sans-serif; }
.email-wrap { width:100%; max-width:600px; border-collapse:separate; border-spacing:0; }
.card-body { font-size:14px; line-height:1.7; overflow-wrap:anywhere; }
.card-body > pre, .card-body code { font-family:Consolas,monospace; white-space:pre-wrap; overflow-wrap:anywhere; }
.card-body ul { padding-left:22px; }
.card-body blockquote { padding-left:16px; border-left:3px solid #0d9488; }
.mail-footer { margin:0; }
.mail-footer td { border-top-color:transparent!important; color:#d2fcf5!important; }
.mail-footer a { color:#0d9488!important; text-decoration:none; }
.email-body { padding:32px; }
body { background-image:radial-gradient(ellipse at center,#0d948814,transparent 65%); }
@media only screen and (max-width:480px) {
body { padding:12px 8px; }
.email-wrap { margin:0 auto; }
.email-body { padding:24px 18px!important; }
.card-body { width:auto!important; }
.resp-hide { display:none!important; }
}
/* Shared framework controls: keep URLs readable, never style them as CTAs. */
.email-link-cell { padding:16px 18px; }
.email-link-url a,.card-body .email-link-url a { color:inherit!important; text-decoration:none!important; font-size:inherit; }
.email-link-url { overflow-wrap:anywhere; word-break:break-all; }
.email-action { margin:20px 0 0; }
.email-btn-row { box-sizing:border-box; }
.card-body .email-btn { max-width:100%; box-sizing:border-box; white-space:normal; overflow-wrap:anywhere; }
@media only screen and (max-width:480px) { .card-body .email-btn { display:block; text-align:center; width:100%; } .email-btn-table { width:100%; } }
/* Account activation instructions use the normal body style. */
.mail-type-activate > p:nth-of-type(2) { font-style:normal; border-left:0; padding-left:0; }
+6
View File
@@ -0,0 +1,6 @@
{
"name": "aurora",
"description": "Ethereal / Dreamlike",
"mode": "framed",
"layout": "aurora"
}
-99
View File
@@ -1,99 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.team_invite.subject" (.Inviter.DisplayName|DotEscape) (.Organization.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { margin:0 0 12px;font-size:17px;line-height:1.5;color:#1e293b;font-weight:600 }
.email-text { margin:0 0 8px;font-size:15px;line-height:1.6;color:#475569 }
.email-subtext { margin:0 0 20px;font-size:14px;line-height:1.6;color:#64748b }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" .Invite.Email}}
</p>
<p class="email-text">
{{.locale.Tr "mail.team_invite.text_1" (DotEscape .Inviter.DisplayName) (DotEscape .Team.Name) (DotEscape .Organization.DisplayName)}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.team_invite.text_2"}}
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.InviteURL}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.InviteURL}}</p>
</div>
<p style="margin:16px 0 0;font-size:13px;color:#64748b;line-height:1.5;">
{{.locale.Tr "mail.team_invite.text_3" .Invite.Email}}
</p>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,100 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { font-size:17px;font-weight:600;color:#1e293b;margin:0 0 16px }
/* Content */
.job-list { list-style:none;padding:0;margin:0 0 24px 0 }
.job-item { background:linear-gradient(135deg,#f8faff,#f4f7fe);border:1px solid #0000000f;border-radius:10px;padding:12px 16px;margin-bottom:8px;box-shadow:inset 0 1px 0 #ffffffb3 }
.job-link { color:#3b82f6;text-decoration:none;font-weight:500;font-size:14px;display:block }
/* Button */
.email-btn-row { margin:16px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<h2 class="email-heading">
{{.Repo.FullName}} &mdash; {{.Run.WorkflowID}}: {{.RunStatusText}}
</h2>
<ul class="job-list">
{{range $job := .Jobs}}
<li class="job-item">
<a href="{{$job.HTMLURL}}" class="job-link">
{{$job.Status}}: {{$job.Name}}{{if gt $job.Attempt 1}} (retry #{{$job.Attempt}}){{end}} &mdash; {{$job.Duration}}
</a>
</li>
{{end}}
</ul>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Run.HTMLURL}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Run.HTMLURL}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-88
View File
@@ -1,88 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-text { margin:0 0 20px;font-size:15px;line-height:1.6;color:#475569 }
/* Typography utilities */
code { background:#eff4fd;padding:2px 8px;border-radius:5px;font-size:14px;color:#3b82f6;font-weight:500;border:1px solid #3b82f61f }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<p class="email-text">
{{.locale.Tr "mail.repo.collaborator.added.text"}} <code>{{.RepoName}}</code>.
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,90 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-text { margin:0 0 20px;font-size:15px;line-height:1.6;color:#475569 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
{{$repo_url := HTMLFormat "<a href='%s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>%s</a>" .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
{{$link := HTMLFormat "<a href='%s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>#%d</a>" .Link .Issue.Index}}
<p class="email-text">
{{if .IsPull}}
{{.locale.Tr "mail.issue_assigned.pull" .Doer.Name $link $repo_url}}
{{else}}
{{.locale.Tr "mail.issue_assigned.issue" .Doer.Name $link $repo_url}}
{{end}}
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-162
View File
@@ -1,162 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-text { margin:0 0 16px;font-size:15px;line-height:1.6;color:#475569 }
/* Content */
blockquote { padding-left:1em;margin:1em 0;border-left:2px solid #dbeafe;color:#64748b }
pre { background:#eff4fd;padding:12px;border-radius:8px;overflow-x:auto;font-size:13px;line-height:1.5;border:1px solid #0000000f }
hr { border:none;border-top:1px solid #0000000f;margin:20px 0 }
.review-path { margin:8px 0;font-size:13px;color:#64748b }
.review-content { padding-left:1em;margin:1em 0 }
.review-body { margin-top:8px;font-size:14px;line-height:1.6 }
.commit-list { margin:16px 0;padding-left:20px }
.commit-item { margin-bottom:6px;font-size:14px }
.commit-link { color:#3b82f6;text-decoration:none;font-weight:500 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
{{if .IsMention}}
<p class="email-text">{{.locale.Tr "mail.issue.x_mentioned_you" .Doer.Name}}</p>
{{end}}
{{if eq .ActionName "push"}}
<p class="email-text">
{{if .Comment.IsForcePush}}
{{$oldCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.OldCommit}}
{{$oldShortSha := ShortSha .Comment.OldCommit}}
{{$oldCommitLink := HTMLFormat "<a href='%[1]s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>%[2]s</a>" $oldCommitUrl $oldShortSha}}
{{$newCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.NewCommit}}
{{$newShortSha := ShortSha .Comment.NewCommit}}
{{$newCommitLink := HTMLFormat "<a href='%[1]s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>%[2]s</a>" $newCommitUrl $newShortSha}}
{{.locale.Tr "mail.issue.action.force_push" .Doer.Name .Comment.Issue.PullRequest.HeadBranch $oldCommitLink $newCommitLink}}
{{else}}
{{.locale.TrN (len .Comment.Commits) "mail.issue.action.push_1" "mail.issue.action.push_n" .Doer.Name .Comment.Issue.PullRequest.HeadBranch (len .Comment.Commits)}}
{{end}}
</p>
{{end}}
<p class="email-text">
{{if eq .ActionName "close"}}
{{.locale.Tr "mail.issue.action.close" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "reopen"}}
{{.locale.Tr "mail.issue.action.reopen" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "merge"}}
{{.locale.Tr "mail.issue.action.merge" .Doer.Name .Issue.Index .Issue.PullRequest.BaseBranch}}
{{else if eq .ActionName "approve"}}
{{.locale.Tr "mail.issue.action.approve" .Doer.Name}}
{{else if eq .ActionName "reject"}}
{{.locale.Tr "mail.issue.action.reject" .Doer.Name}}
{{else if eq .ActionName "review"}}
{{.locale.Tr "mail.issue.action.review" .Doer.Name}}
{{else if eq .ActionName "review_dismissed"}}
{{.locale.Tr "mail.issue.action.review_dismissed" .Doer.Name .Comment.Review.Reviewer.Name}}
{{else if eq .ActionName "ready_for_review"}}
{{.locale.Tr "mail.issue.action.ready_for_review" .Doer.Name}}
{{end}}
{{- if eq .Body ""}}
{{if eq .ActionName "new"}}
{{.locale.Tr "mail.issue.action.new" .Doer.Name .Issue.Index}}
{{end}}
{{else}}
{{.Body}}
{{end -}}{{if .CanReply}}&nbsp;{{.locale.Tr "mail.reply"}}{{end}}
</p>
{{- range .ReviewComments}}
<hr>
<p class="review-path">{{$.locale.Tr "mail.issue.in_tree_path" .TreePath}}</p>
<div class="review-content">
<pre>{{.Patch}}</pre>
<div class="review-body">{{.RenderedContent}}</div>
</div>
{{end -}}
{{if eq .ActionName "push"}}
<ul class="commit-list">
{{range .Comment.Commits}}
<li class="commit-item">
<a href="{{$.Comment.Issue.PullRequest.BaseRepo.HTMLURL}}/commit/{{.UserCommit.GitCommit.ID}}" class="commit-link">
{{ShortSha .UserCommit.GitCommit.ID.String}}
</a> &mdash; {{.UserCommit.GitCommit.MessageTitle}}
</li>
{{end}}
</ul>
{{end}}
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-122
View File
@@ -1,122 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { margin:20px 0 8px;font-size:17px;font-weight:600;color:#1e293b }
.email-subheading { margin:20px 0 8px;font-weight:600;color:#1e293b }
.email-text { margin:0 0 16px;font-size:15px;line-height:1.6;color:#475569 }
/* Content */
blockquote { padding-left:1em;margin:1em 0;border-left:2px solid #dbeafe;color:#64748b }
ul { margin:12px 0 16px;padding-left:20px }
li { margin-bottom:6px;font-size:14px;line-height:1.5;color:#475569 }
.email-content { margin:12px 0 16px;font-size:14px;line-height:1.6;color:#475569 }
/* Button */
.email-btn-row { margin:16px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
{{$release_url := HTMLFormat "<a href='%s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>%s</a>" .Release.HTMLURL .Release.TagName}}
{{$repo_url := HTMLFormat "<a href='%s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>%s</a>" .Release.Repo.HTMLURL .Release.Repo.FullName}}
<p class="email-text">
{{.locale.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url}}
</p>
<h4 class="email-heading">{{.locale.Tr "mail.release.title" .Release.Title}}</h4>
{{if .Release.RenderedNote}}
{{.locale.Tr "mail.release.note"}}<br>
<div class="email-content">
{{.Release.RenderedNote}}
</div>
{{end}}
<p class="email-subheading">{{.locale.Tr "mail.release.downloads"}}</p>
<ul>
{{if not .DisableDownloadSourceArchives}}
<li>
<a href="{{.Release.Repo.HTMLURL}}/archive/{{.Release.TagName | PathEscapeSegments}}.zip" rel="nofollow" style="color:#3b82f6;text-decoration:none;font-weight:500;">{{.locale.Tr "mail.release.download.zip"}}</a>
</li>
<li>
<a href="{{.Release.Repo.HTMLURL}}/archive/{{.Release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow" style="color:#3b82f6;text-decoration:none;font-weight:500;">{{.locale.Tr "mail.release.download.targz"}}</a>
</li>
{{end}}
{{if .Release.Attachments}}
{{range .Release.Attachments}}
<li>
<a href="{{.DownloadURL}}" target="_blank" style="color:#3b82f6;text-decoration:none;font-weight:500;">{{.Name}} ({{.Size | FormatByteSize}})</a>
</li>
{{end}}
{{end}}
</ul>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-85
View File
@@ -1,85 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-text { margin:0 0 20px;font-size:15px;line-height:1.6;color:#475569 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
{{$url := HTMLFormat "<a href='%[1]s' style='color:#3b82f6;text-decoration:none;font-weight:500;'>%[2]s</a>" .Link .Repo}}
<p class="email-text">
{{.Subject}}. {{.locale.Tr "mail.repo.transfer.body" $url}}
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-92
View File
@@ -1,92 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.activate_account.title" (.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { margin:0 0 12px;font-size:17px;line-height:1.5;color:#1e293b;font-weight:600 }
.email-text { margin:0 0 8px;font-size:15px;line-height:1.6;color:#475569 }
.email-subtext { margin:0 0 20px;font-size:14px;line-height:1.6;color:#64748b }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<p class="email-text">
{{.locale.Tr "mail.activate_account.text_1" (.DisplayName|DotEscape) AppName}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.activate_account.text_2" .ActiveCodeLives}}
</p>
<!-- Button -->
{{$activate_url := printf "%suser/activate?code=%s" AppUrl (QueryEscape .Code)}}
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{$activate_url}}" class="email-btn">{{.locale.Tr "mail.activate_account"}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{$activate_url}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,91 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.activate_email.title" (.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { margin:0 0 12px;font-size:17px;line-height:1.5;color:#1e293b;font-weight:600 }
.email-text { margin:0 0 20px;font-size:15px;line-height:1.6;color:#475569 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}
</p>
<p class="email-text">
{{.locale.Tr "mail.activate_email.text" .ActiveCodeLives}}
</p>
<!-- Button -->
{{$activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl (QueryEscape .Code) (QueryEscape .Email)}}
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{$activate_url}}" class="email-btn">{{.locale.Tr "mail.activate_email"}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{$activate_url}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,100 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.register_notify.title" (.DisplayName|DotEscape) AppName}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { margin:0 0 12px;font-size:17px;line-height:1.5;color:#1e293b;font-weight:600 }
.email-text { margin:0 0 8px;font-size:15px;line-height:1.6;color:#475569 }
.email-subtext { margin:0 0 20px;font-size:14px;line-height:1.6;color:#64748b }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}
</p>
<p class="email-text">
{{.locale.Tr "mail.register_notify.text_1" AppName}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.register_notify.text_2" .Username}}
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{AppUrl}}user/login" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
{{$set_pwd_url := printf "%suser/forgot_password" AppUrl}}
<p style="margin:0 0 16px;font-size:14px;line-height:1.6;color:#64748b;">
{{.locale.Tr "mail.register_notify.text_3" $set_pwd_url}}
</p>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{AppUrl}}user/login</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,91 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.reset_password.title" (.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading { margin:0 0 12px;font-size:17px;line-height:1.5;color:#1e293b;font-weight:600 }
.email-text { margin:0 0 20px;font-size:15px;line-height:1.6;color:#475569 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
</style>
</head>
<body style="margin:0;padding:0;background:#e2e8f2;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="32" height="32" class="email-logo" />
<p class="email-brand">{{AppName}}</p>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#334155;">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}
</p>
<p class="email-text">
{{.locale.Tr "mail.reset_password.text" .ResetPwdCodeLives}}
</p>
<!-- Button -->
{{$recover_url := printf "%suser/recover_account?code=%s" AppUrl (QueryEscape .Code)}}
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{$recover_url}}" class="email-btn">{{.locale.Tr "mail.reset_password"}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{$recover_url}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
+67
View File
@@ -0,0 +1,67 @@
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#e2e8f2 }
/* Header */
.email-logo { display:inline-block;margin:0 auto 8px;border:0;outline:none }
.email-brand { margin:0;font-size:18px;font-weight:700;color:#1e293b;letter-spacing:-0.3px }
.email-header { padding:28px 32px 18px;background:linear-gradient(135deg,#dbeafe,#e0e7ff,#e8f0fe);border-bottom:1px solid #3b82f61a;text-align:center }
/* Card */
.email-card { max-width:600px;margin:24px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 4px #0000000f,0 8px 24px #0000000f,0 0 0 1px #0000000a;overflow:hidden }
.email-heading,
.mail-type-activate > p:first-child,
.mail-type-activate_email > p:first-child { margin:0 0 12px;font-size:17px;line-height:1.5;color:#1e293b;font-weight:600 }
.email-text,
.mail-type-activate > p:nth-of-type(2) { margin:0 0 8px;font-size:15px;line-height:1.6;color:#475569 }
.email-subtext { margin:0 0 20px;font-size:14px;line-height:1.6;color:#64748b }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn,.card-body .email-btn,.card-body .email-btn:link,.card-body .email-btn:visited { display:inline-block;padding:14px 40px;font-size:15px;font-weight:600;color:#ffffff!important;text-decoration:none!important;background-color:#3b82f6;border-radius:40px;border:1px solid #3b82f6;letter-spacing:0.2px }
/* Fallback link */
.email-link-box { background:linear-gradient(135deg,#eff4fd,#f5f7fe);border:1px solid #3b82f61a;border-radius:12px;padding:16px 18px;margin:24px 0 16px;box-shadow:inset 0 1px 0 #ffffffe6 }
.email-link-label { margin:0 0 8px;font-size:13px;color:#64748b;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#3b82f6;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #0000000f;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:18px 32px 22px;background:linear-gradient(135deg,#f8f9fc,#f4f6fb);border-top:1px solid #0000000f;text-align:center }
.email-footer-text { margin:0;font-size:13px;color:#94a3b8 }
.email-footer-link { color:#3b82f6;text-decoration:none;font-weight:500 }
/* Official-snapshot presentation adapter: no content or translation overrides. */
body { margin:0; padding:24px 16px; background-color:#e2e8f2; }
body, td { font-family:Arial,sans-serif; }
.email-wrap { width:100%; max-width:600px; border-collapse:separate; border-spacing:0; }
.card-body { font-size:14px; line-height:1.7; overflow-wrap:anywhere; }
.card-body > pre, .card-body code { font-family:Consolas,monospace; white-space:pre-wrap; overflow-wrap:anywhere; }
.card-body ul { padding-left:22px; }
.card-body blockquote { padding-left:16px; border-left:3px solid #3b82f6; }
.mail-footer { margin:0; }
.mail-footer td { border-top-color:transparent!important; color:#334155!important; }
.mail-footer a { color:#3b82f6!important; text-decoration:none; }
.email-body { padding:32px 32px 24px; color:#334155; }
.glass-halo { display:inline-block; padding:15px 17px; border-radius:18px; background:#ffffffa8; border:1px solid #ffffff; box-shadow:0 4px 18px #3b82f617; }
.email-body { background:linear-gradient(180deg,#ffffff,#fefeff); }
@media only screen and (max-width:480px) {
body { padding:12px 8px; }
.email-wrap { margin:0 auto; }
.email-body { padding:24px 18px!important; }
.card-body { width:auto!important; }
.resp-hide { display:none!important; }
}
/* Shared framework controls: keep URLs readable, never style them as CTAs. */
.email-link-cell { padding:16px 18px; }
.email-link-url a,.card-body .email-link-url a { color:inherit!important; text-decoration:none!important; font-size:inherit; }
.email-link-url { overflow-wrap:anywhere; word-break:break-all; }
.email-action { margin:20px 0 0; }
.email-btn-row { box-sizing:border-box; }
.card-body .email-btn { max-width:100%; box-sizing:border-box; white-space:normal; overflow-wrap:anywhere; }
@media only screen and (max-width:480px) { .card-body .email-btn { display:block; text-align:center; width:100%; } .email-btn-table { width:100%; } }
/* Account activation instructions use the normal body style. */
.mail-type-activate > p:nth-of-type(2) { font-style:normal; border-left:0; padding-left:0; }
+6
View File
@@ -0,0 +1,6 @@
{
"name": "bloom",
"description": "Creative / Startup",
"mode": "framed",
"layout": "bloom"
}
-106
View File
@@ -1,106 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.locale.Tr "mail.team_invite.subject" (.Inviter.DisplayName|DotEscape) (.Organization.DisplayName|DotEscape)}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#fff7ed }
/* Header */
.email-logo { display:inline-block;vertical-align:middle;margin:0 10px;border:0;outline:none }
.email-brand { font-size:20px;font-weight:700;color:#9a3412;letter-spacing:-0.3px;vertical-align:middle;font-family:Georgia,'Times New Roman',serif }
.email-header { padding:28px 32px 16px;border-bottom:1px solid #fed7aa;background:linear-gradient(135deg,#fff7ed,#ffffff) }
/* Card */
.email-card { max-width:600px;margin:20px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 16px #ea580c14;overflow:hidden;border:1px solid #fed7aa }
.email-heading { margin:0 0 12px;font-size:17px;line-height:1.5;color:#9a3412;font-family:Georgia,'Times New Roman',serif;font-weight:600 }
.email-text { margin:0 0 8px;font-size:16px;line-height:1.6;color:#78350f }
.email-subtext { margin:0 0 20px;font-size:15px;line-height:1.6;color:#92400e }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none;background:linear-gradient(135deg,#ea580c,#c2410c);border-radius:50px;font-family:system-ui,sans-serif }
/* Fallback link */
.email-link-box { background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:16px 18px;margin:24px 0 16px }
.email-link-label { margin:0 0 8px;font-size:14px;color:#92400e;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#c2410c;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #fed7aa;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:20px 32px 24px;background:#fff7ed;border-top:1px solid #fed7aa }
.email-footer-text { margin:0;font-size:14px;color:#92400e }
.email-footer-link { color:#ea580c;text-decoration:none;font-weight:600 }
</style>
</head>
<body style="margin:0;padding:0;background-color:#fff7ed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;">
<tr>
<td style="vertical-align:middle;">
<span style="font-size:15px;">&#x1f44b;</span>
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="30" height="30" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
</table>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#78350f;">
<p class="email-heading">
{{.locale.Tr "mail.hi_user_x" .Invite.Email}}
</p>
<p class="email-text">
{{.locale.Tr "mail.team_invite.text_1" (DotEscape .Inviter.DisplayName) (DotEscape .Team.Name) (DotEscape .Organization.DisplayName)}}
</p>
<p class="email-subtext">
{{.locale.Tr "mail.team_invite.text_2"}}
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.InviteURL}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.InviteURL}}</p>
</div>
<p style="margin:16px 0 0;font-size:14px;color:#92400e;line-height:1.5;">
{{.locale.Tr "mail.team_invite.text_3" .Invite.Email}}
</p>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,107 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#fff7ed }
/* Header */
.email-logo { display:inline-block;vertical-align:middle;margin:0 10px;border:0;outline:none }
.email-brand { font-size:20px;font-weight:700;color:#9a3412;letter-spacing:-0.3px;vertical-align:middle;font-family:Georgia,'Times New Roman',serif }
.email-header { padding:28px 32px 16px;border-bottom:1px solid #fed7aa;background:linear-gradient(135deg,#fff7ed,#ffffff) }
/* Card */
.email-card { max-width:600px;margin:20px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 16px #ea580c14;overflow:hidden;border:1px solid #fed7aa }
.email-heading { font-size:19px;font-weight:700;color:#9a3412;margin:0 0 16px;font-family:Georgia,'Times New Roman',serif }
/* Content */
.job-list { list-style:none;padding:0;margin:0 0 24px 0 }
.job-item { background-color:#fff7ed;border:1px solid #fed7aa;border-radius:10px;padding:12px 16px;margin-bottom:8px }
.job-link { color:#c2410c;text-decoration:none;font-weight:600;font-size:15px;display:block }
/* Button */
.email-btn-row { margin:16px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none;background:linear-gradient(135deg,#ea580c,#c2410c);border-radius:50px;font-family:system-ui,sans-serif }
/* Fallback link */
.email-link-box { background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:16px 18px;margin:24px 0 16px }
.email-link-label { margin:0 0 8px;font-size:14px;color:#92400e;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#c2410c;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #fed7aa;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:20px 32px 24px;background:#fff7ed;border-top:1px solid #fed7aa }
.email-footer-text { margin:0;font-size:14px;color:#92400e }
.email-footer-link { color:#ea580c;text-decoration:none;font-weight:600 }
</style>
</head>
<body style="margin:0;padding:0;background-color:#fff7ed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;">
<tr>
<td style="vertical-align:middle;">
<span style="font-size:15px;">&#x1f44b;</span>
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="30" height="30" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
</table>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#78350f;">
<h2 class="email-heading">
{{.Repo.FullName}} &mdash; {{.Run.WorkflowID}}: {{.RunStatusText}}
</h2>
<ul class="job-list">
{{range $job := .Jobs}}
<li class="job-item">
<a href="{{$job.HTMLURL}}" class="job-link">
{{$job.Status}}: {{$job.Name}}{{if gt $job.Attempt 1}} (retry #{{$job.Attempt}}){{end}} &mdash; {{$job.Duration}}
</a>
</li>
{{end}}
</ul>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Run.HTMLURL}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Run.HTMLURL}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-95
View File
@@ -1,95 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#fff7ed }
/* Header */
.email-logo { display:inline-block;vertical-align:middle;margin:0 10px;border:0;outline:none }
.email-brand { font-size:20px;font-weight:700;color:#9a3412;letter-spacing:-0.3px;vertical-align:middle;font-family:Georgia,'Times New Roman',serif }
.email-header { padding:28px 32px 16px;border-bottom:1px solid #fed7aa;background:linear-gradient(135deg,#fff7ed,#ffffff) }
/* Card */
.email-card { max-width:600px;margin:20px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 16px #ea580c14;overflow:hidden;border:1px solid #fed7aa }
.email-text { margin:0 0 20px;font-size:16px;line-height:1.6;color:#78350f }
/* Typography utilities */
code { background:#fff7ed;padding:2px 8px;border-radius:6px;font-size:15px;color:#c2410c;font-weight:600 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none;background:linear-gradient(135deg,#ea580c,#c2410c);border-radius:50px;font-family:system-ui,sans-serif }
/* Fallback link */
.email-link-box { background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:16px 18px;margin:24px 0 16px }
.email-link-label { margin:0 0 8px;font-size:14px;color:#92400e;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#c2410c;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #fed7aa;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:20px 32px 24px;background:#fff7ed;border-top:1px solid #fed7aa }
.email-footer-text { margin:0;font-size:14px;color:#92400e }
.email-footer-link { color:#ea580c;text-decoration:none;font-weight:600 }
</style>
</head>
<body style="margin:0;padding:0;background-color:#fff7ed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;">
<tr>
<td style="vertical-align:middle;">
<span style="font-size:15px;">&#x1f44b;</span>
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="30" height="30" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
</table>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#78350f;">
<p class="email-text">
{{.locale.Tr "mail.repo.collaborator.added.text"}} <code>{{.RepoName}}</code>.
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
@@ -1,97 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#fff7ed }
/* Header */
.email-logo { display:inline-block;vertical-align:middle;margin:0 10px;border:0;outline:none }
.email-brand { font-size:20px;font-weight:700;color:#9a3412;letter-spacing:-0.3px;vertical-align:middle;font-family:Georgia,'Times New Roman',serif }
.email-header { padding:28px 32px 16px;border-bottom:1px solid #fed7aa;background:linear-gradient(135deg,#fff7ed,#ffffff) }
/* Card */
.email-card { max-width:600px;margin:20px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 16px #ea580c14;overflow:hidden;border:1px solid #fed7aa }
.email-text { margin:0 0 20px;font-size:16px;line-height:1.6;color:#78350f }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none;background:linear-gradient(135deg,#ea580c,#c2410c);border-radius:50px;font-family:system-ui,sans-serif }
/* Fallback link */
.email-link-box { background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:16px 18px;margin:24px 0 16px }
.email-link-label { margin:0 0 8px;font-size:14px;color:#92400e;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#c2410c;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #fed7aa;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:20px 32px 24px;background:#fff7ed;border-top:1px solid #fed7aa }
.email-footer-text { margin:0;font-size:14px;color:#92400e }
.email-footer-link { color:#ea580c;text-decoration:none;font-weight:600 }
</style>
</head>
<body style="margin:0;padding:0;background-color:#fff7ed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;">
<tr>
<td style="vertical-align:middle;">
<span style="font-size:15px;">&#x1f44b;</span>
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="30" height="30" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
</table>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#78350f;">
{{$repo_url := HTMLFormat "<a href='%s' style='color:#ea580c;text-decoration:none;font-weight:600;'>%s</a>" .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
{{$link := HTMLFormat "<a href='%s' style='color:#ea580c;text-decoration:none;font-weight:600;'>#%d</a>" .Link .Issue.Index}}
<p class="email-text">
{{if .IsPull}}
{{.locale.Tr "mail.issue_assigned.pull" .Doer.Name $link $repo_url}}
{{else}}
{{.locale.Tr "mail.issue_assigned.issue" .Doer.Name $link $repo_url}}
{{end}}
</p>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
-168
View File
@@ -1,168 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#fff7ed }
/* Header */
.email-logo { display:inline-block;vertical-align:middle;margin:0 10px;border:0;outline:none }
.email-brand { font-size:20px;font-weight:700;color:#9a3412;letter-spacing:-0.3px;vertical-align:middle;font-family:Georgia,'Times New Roman',serif }
.email-header { padding:28px 32px 16px;border-bottom:1px solid #fed7aa;background:linear-gradient(135deg,#fff7ed,#ffffff) }
/* Card */
.email-card { max-width:600px;margin:20px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 16px #ea580c14;overflow:hidden;border:1px solid #fed7aa }
.email-text { margin:0 0 16px;font-size:16px;line-height:1.6;color:#78350f }
/* Content */
blockquote { padding-left:1em;margin:1em 0;border-left:3px solid #fdba74;color:#92400e }
pre { background:#fff7ed;padding:12px;border-radius:8px;overflow-x:auto;font-size:14px;line-height:1.5;border:1px solid #fed7aa }
hr { border:none;border-top:1px solid #fed7aa;margin:20px 0 }
.review-path { margin:8px 0;font-size:14px;color:#92400e }
.review-content { padding-left:1em;margin:1em 0 }
.review-body { margin-top:8px;font-size:15px;line-height:1.6 }
.commit-list { margin:16px 0;padding-left:20px }
.commit-item { margin-bottom:6px;font-size:15px }
.commit-link { color:#ea580c;text-decoration:none;font-weight:600 }
/* Button */
.email-btn-row { margin:28px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none;background:linear-gradient(135deg,#ea580c,#c2410c);border-radius:50px;font-family:system-ui,sans-serif }
/* Fallback link */
.email-link-box { background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:16px 18px;margin:24px 0 16px }
.email-link-label { margin:0 0 8px;font-size:14px;color:#92400e;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#c2410c;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #fed7aa;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:20px 32px 24px;background:#fff7ed;border-top:1px solid #fed7aa }
.email-footer-text { margin:0;font-size:14px;color:#92400e }
.email-footer-link { color:#ea580c;text-decoration:none;font-weight:600 }
</style>
</head>
<body style="margin:0;padding:0;background-color:#fff7ed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;">
<tr>
<td style="vertical-align:middle;">
<span style="font-size:15px;">&#x1f44b;</span>
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="30" height="30" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
</table>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#78350f;">
{{if .IsMention}}
<p class="email-text">{{.locale.Tr "mail.issue.x_mentioned_you" .Doer.Name}}</p>
{{end}}
{{if eq .ActionName "push"}}
<p class="email-text">
{{if .Comment.IsForcePush}}
{{$oldCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.OldCommit}}
{{$oldShortSha := ShortSha .Comment.OldCommit}}
{{$oldCommitLink := HTMLFormat "<a href='%[1]s' style='color:#ea580c;text-decoration:none;font-weight:600;'>%[2]s</a>" $oldCommitUrl $oldShortSha}}
{{$newCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.NewCommit}}
{{$newShortSha := ShortSha .Comment.NewCommit}}
{{$newCommitLink := HTMLFormat "<a href='%[1]s' style='color:#ea580c;text-decoration:none;font-weight:600;'>%[2]s</a>" $newCommitUrl $newShortSha}}
{{.locale.Tr "mail.issue.action.force_push" .Doer.Name .Comment.Issue.PullRequest.HeadBranch $oldCommitLink $newCommitLink}}
{{else}}
{{.locale.TrN (len .Comment.Commits) "mail.issue.action.push_1" "mail.issue.action.push_n" .Doer.Name .Comment.Issue.PullRequest.HeadBranch (len .Comment.Commits)}}
{{end}}
</p>
{{end}}
<p class="email-text">
{{if eq .ActionName "close"}}
{{.locale.Tr "mail.issue.action.close" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "reopen"}}
{{.locale.Tr "mail.issue.action.reopen" .Doer.Name .Issue.Index}}
{{else if eq .ActionName "merge"}}
{{.locale.Tr "mail.issue.action.merge" .Doer.Name .Issue.Index .Issue.PullRequest.BaseBranch}}
{{else if eq .ActionName "approve"}}
{{.locale.Tr "mail.issue.action.approve" .Doer.Name}}
{{else if eq .ActionName "reject"}}
{{.locale.Tr "mail.issue.action.reject" .Doer.Name}}
{{else if eq .ActionName "review"}}
{{.locale.Tr "mail.issue.action.review" .Doer.Name}}
{{else if eq .ActionName "review_dismissed"}}
{{.locale.Tr "mail.issue.action.review_dismissed" .Doer.Name .Comment.Review.Reviewer.Name}}
{{else if eq .ActionName "ready_for_review"}}
{{.locale.Tr "mail.issue.action.ready_for_review" .Doer.Name}}
{{end}}
{{- if eq .Body ""}}
{{if eq .ActionName "new"}}
{{.locale.Tr "mail.issue.action.new" .Doer.Name .Issue.Index}}
{{end}}
{{else}}
{{.Body}}
{{end -}}
</p>
{{- range .ReviewComments}}
<hr>
<p class="review-path">{{$.locale.Tr "mail.issue.in_tree_path" .TreePath}}</p>
<div class="review-content">
<pre>{{.Patch}}</pre>
<div class="review-body">{{.RenderedContent}}</div>
</div>
{{end -}}
{{if eq .ActionName "push"}}
<ul class="commit-list">
{{range .Comment.Commits}}
<li class="commit-item">
<a href="{{$.Comment.Issue.PullRequest.BaseRepo.HTMLURL}}/commit/{{.UserCommit.GitCommit.ID}}" class="commit-link">
{{ShortSha .UserCommit.GitCommit.ID.String}}
</a> &mdash; {{.UserCommit.GitCommit.MessageTitle}}
</li>
{{end}}
</ul>
{{end}}
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
{{if .CanReply}}&nbsp;{{.locale.Tr "mail.reply"}}{{end}}
</p>
</div>
</div>
</center>
</body>
</html>
-129
View File
@@ -1,129 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" />
<title>
{{.Subject}}
</title>
<style type="text/css">
/* Base */
body,.ExternalClass,.ReadMsgBody { background-color:#fff7ed }
/* Header */
.email-logo { display:inline-block;vertical-align:middle;margin:0 10px;border:0;outline:none }
.email-brand { font-size:20px;font-weight:700;color:#9a3412;letter-spacing:-0.3px;vertical-align:middle;font-family:Georgia,'Times New Roman',serif }
.email-header { padding:28px 32px 16px;border-bottom:1px solid #fed7aa;background:linear-gradient(135deg,#fff7ed,#ffffff) }
/* Card */
.email-card { max-width:600px;margin:20px auto;background:#ffffff;border-radius:16px;box-shadow:0 2px 16px #ea580c14;overflow:hidden;border:1px solid #fed7aa }
.email-heading { margin:20px 0 8px;font-size:19px;font-weight:700;color:#9a3412;font-family:Georgia,'Times New Roman',serif }
.email-subheading { margin:20px 0 8px;font-weight:600;color:#9a3412 }
.email-text { margin:0 0 16px;font-size:16px;line-height:1.6;color:#78350f }
/* Content */
blockquote { padding-left:1em;margin:1em 0;border-left:3px solid #fdba74;color:#92400e }
ul { margin:12px 0 16px;padding-left:20px }
li { margin-bottom:8px;font-size:15px;line-height:1.5;color:#78350f }
.email-content { margin:12px 0 16px;font-size:15px;line-height:1.6;color:#78350f }
/* Button */
.email-btn-row { margin:16px 0 24px;border-collapse:separate }
.email-btn { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none;background:linear-gradient(135deg,#ea580c,#c2410c);border-radius:50px;font-family:system-ui,sans-serif }
/* Fallback link */
.email-link-box { background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:16px 18px;margin:24px 0 16px }
.email-link-label { margin:0 0 8px;font-size:14px;color:#92400e;line-height:1.5 }
.email-link-url { margin:0;font-size:13px;color:#c2410c;background:#ffffff;padding:10px 12px;border-radius:8px;border:1px solid #fed7aa;word-break:break-all;font-family:monospace }
/* Footer */
.email-footer { padding:20px 32px 24px;background:#fff7ed;border-top:1px solid #fed7aa }
.email-footer-text { margin:0;font-size:14px;color:#92400e }
.email-footer-link { color:#ea580c;text-decoration:none;font-weight:600 }
</style>
</head>
<body style="margin:0;padding:0;background-color:#fff7ed;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
<center style="width:100%;table-layout:fixed;">
<div class="email-card">
<!-- Floating Title -->
<div class="email-header">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse;">
<tr>
<td style="vertical-align:middle;">
<span style="font-size:15px;">&#x1f44b;</span>
<img src="{{AppUrl}}assets/img/favicon.png" alt="logo" width="30" height="30" class="email-logo" />
<span class="email-brand">{{AppName}}</span>
</td>
</tr>
</table>
</div>
<!-- Card -->
<div style="padding:32px 32px 24px;color:#78350f;">
{{$release_url := HTMLFormat "<a href='%s' style='color:#ea580c;text-decoration:none;font-weight:600;'>%s</a>" .Release.HTMLURL .Release.TagName}}
{{$repo_url := HTMLFormat "<a href='%s' style='color:#ea580c;text-decoration:none;font-weight:600;'>%s</a>" .Release.Repo.HTMLURL .Release.Repo.FullName}}
<p class="email-text">
{{.locale.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url}}
</p>
<h4 class="email-heading">{{.locale.Tr "mail.release.title" .Release.Title}}</h4>
<p style="margin:0 0 8px;font-weight:600;color:#9a3412;">{{.locale.Tr "mail.release.note"}}</p>
{{if .Release.RenderedNote}}
<div class="email-content">
{{.Release.RenderedNote}}
</div>
{{end}}
<p class="email-subheading">{{.locale.Tr "mail.release.downloads"}}</p>
<ul>
{{if not .DisableDownloadSourceArchives}}
<li>
<a href="{{.Release.Repo.HTMLURL}}/archive/{{.Release.TagName | PathEscapeSegments}}.zip" rel="nofollow" style="color:#ea580c;text-decoration:none;font-weight:600;">{{.locale.Tr "mail.release.download.zip"}}</a>
</li>
<li>
<a href="{{.Release.Repo.HTMLURL}}/archive/{{.Release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow" style="color:#ea580c;text-decoration:none;font-weight:600;">{{.locale.Tr "mail.release.download.targz"}}</a>
</li>
{{end}}
{{if .Release.Attachments}}
{{range .Release.Attachments}}
<li>
<a href="{{.DownloadURL}}" target="_blank" style="color:#ea580c;text-decoration:none;font-weight:600;">{{.Name}} ({{.Size | FormatByteSize}})</a>
</li>
{{end}}
{{end}}
</ul>
<!-- Button -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="email-btn-row">
<tr>
<td align="center">
<a href="{{.Link}}" class="email-btn">{{.locale.Tr "mail.view_it_on" AppName}}</a>
</td>
</tr>
</table>
<!-- Fallback link -->
<div class="email-link-box">
<p class="email-link-label">{{.locale.Tr "mail.link_not_working_do_paste"}}</p>
<p class="email-link-url">{{.Link}}</p>
</div>
</div>
<!-- Footer -->
<div class="email-footer">
<p class="email-footer-text">
&copy;
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
</p>
</div>
</div>
</center>
</body>
</html>
Loaded 100 of 232 files, more files were not shown because too many files have changed in this diff. Show more