chore: migrate mail themes to shared framework and locked upstream inputs
No files matched your search
@@ -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
|
||||
@@ -0,0 +1,130 @@
|
||||
"""Package verified generated mail overrides; never copy stale build directories."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
import tarfile
|
||||
import zipfile
|
||||
|
||||
|
||||
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 collect(root, version):
|
||||
root = Path(root)
|
||||
if not re.fullmatch(r"v[0-9]+\.[0-9]+\.[0-9]+", version):
|
||||
raise ValueError("Expected a stable vX.Y.Z package version")
|
||||
lock = json.loads(read_file(root / "gitea.lock.json"))
|
||||
if version != lock["tag"]:
|
||||
raise ValueError("Package version must match the locked Gitea tag")
|
||||
files = {}
|
||||
names = sorted(p.name for p in (root / "themes").iterdir() if p.is_dir() and not p.is_symlink())
|
||||
if not names:
|
||||
raise ValueError("No source theme manifests")
|
||||
for name in names:
|
||||
if not re.fullmatch(r"[a-z][a-z0-9-]*", name):
|
||||
raise ValueError(f"Invalid theme name: {name}")
|
||||
source = root / "themes" / name
|
||||
theme = json.loads(read_file(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
|
||||
meta = json.loads(read_file(built / "build.json"))
|
||||
if (meta["theme"], meta["mode"], meta["gitea_tag"], meta["gitea_commit"]) != (name, theme["mode"], version, lock["commit"]):
|
||||
raise ValueError(f"Stale build identity: {name}")
|
||||
if not {"theme.json", "theme.css"}.issubset(meta.get("sources") or {}) or not meta.get("files"):
|
||||
raise ValueError(f"Missing source/file provenance: {name}")
|
||||
expected_files = {"mail/base/head.tmpl", "mail/base/footer.tmpl"}
|
||||
if theme["mode"] == "framed":
|
||||
expected_files.update(path.removeprefix("templates/") for path in lock["files"] if path.startswith("templates/mail/") and path.endswith(".tmpl"))
|
||||
expected_files.update(path.relative_to(root / "framework").as_posix() for path in (root / "framework" / "mail").rglob("*.tmpl"))
|
||||
if set(meta["files"]) != expected_files:
|
||||
raise ValueError(f"Incomplete generated install package: {name}")
|
||||
for path, expected in meta["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}")
|
||||
for path, expected in meta["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")
|
||||
manifest_data = read_file(root / "preview" / "rendered.js")
|
||||
prefix = b"window.__MAIL_PREVIEW__ = "
|
||||
manifest = json.loads(manifest_data.split(prefix, 1)[1].strip().removesuffix(b";"))
|
||||
languages = sorted(path.removeprefix("options/locale/locale_").removesuffix(".json") for path in lock["files"] if path.startswith("options/locale/locale_") and path.endswith(".json"))
|
||||
if manifest["upstream"] != version or manifest.get("upstream_commit") != lock["commit"] or sorted(manifest["themes"]) != names or sorted(manifest["languages"]) != languages:
|
||||
raise ValueError("Preview does not match lock/all source themes and languages; run preview all")
|
||||
files["preview/rendered.js"] = manifest_data
|
||||
for lang in languages:
|
||||
data = read_file(root / "preview" / "rendered" / (lang + ".js"))
|
||||
marker = ('window.__MAIL_PREVIEW_DATA__[' + json.dumps(lang) + '] = ').encode()
|
||||
payload = json.loads(data.split(marker, 1)[1].strip().removesuffix(b";"))
|
||||
if sorted(payload) != names or any(set(payload[name]) != set(manifest["registry"]) for name in names):
|
||||
raise ValueError(f"Incomplete preview language bundle: {lang}")
|
||||
files[f"preview/rendered/{lang}.js"] = data
|
||||
for name in ["LICENSE", "README.md", "CONTRIBUTING.md", "COMPATIBILITY.md", "THIRD_PARTY_NOTICES.md", "docs/README.zh-CN.md", "docs/CONTRIBUTING.zh-CN.md", "preview/index.html"]:
|
||||
files[name] = read_file(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)
|
||||
license_data = read_file(root / "build" / "upstream" / "LICENSE")
|
||||
if digest(license_data) != lock["files"]["LICENSE"]:
|
||||
raise ValueError("Upstream license checksum mismatch")
|
||||
files["GITEA-LICENSE"] = license_data
|
||||
files["upstream-lock.json"] = read_file(root / "gitea.lock.json")
|
||||
return files
|
||||
|
||||
|
||||
def package(root, version, output):
|
||||
files = collect(root, version)
|
||||
archive = f"gitea-mail-templates-{version}"
|
||||
output = Path(output)
|
||||
targets = [output / (archive + ext) for ext in (".zip", ".tar.gz")]
|
||||
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, info.mode = len(data), 0o644
|
||||
tar.addfile(info, io.BytesIO(data))
|
||||
print(f"[PASS] Packaged {len(files)} verified files: {targets[0]}, {targets[1]}")
|
||||
|
||||
|
||||
if __name__ == "__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")
|
||||
@@ -0,0 +1,77 @@
|
||||
"""Offline artifact/provenance tests independent of generated repository output."""
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
SPEC = importlib.util.spec_from_file_location("package_release", Path(__file__).with_name("package_release.py"))
|
||||
PACKER = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(PACKER)
|
||||
|
||||
|
||||
class PackagingTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temp.cleanup)
|
||||
self.root = Path(self.temp.name)
|
||||
self.tag = "v28.0.0"
|
||||
self.commit = "a" * 40
|
||||
theme = json.dumps({"name": "demo", "mode": "shared"}).encode()
|
||||
css = b"a { color:blue; }"
|
||||
self.write("themes/demo/theme.json", theme)
|
||||
self.write("themes/demo/theme.css", css)
|
||||
files = {"mail/base/head.tmpl": b"<title>test</title>", "mail/base/footer.tmpl": b"<p>footer</p>"}
|
||||
meta = {"theme": "demo", "mode": "shared", "gitea_tag": self.tag, "gitea_commit": self.commit, "sources": {"theme.json": PACKER.digest(theme), "theme.css": PACKER.digest(css)}, "files": {p: PACKER.digest(data) for p, data in files.items()}}
|
||||
for path, data in files.items():
|
||||
self.write("build/themes/demo/" + path, data)
|
||||
self.write_json("build/themes/demo/build.json", meta)
|
||||
self.write_json("gitea.lock.json", {"tag": self.tag, "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")
|
||||
manifest = {"upstream": self.tag, "upstream_commit": self.commit, "themes": ["demo"], "languages": ["en-US"], "registry": {"activate": {}}}
|
||||
self.write("preview/rendered.js", ("window.__MAIL_PREVIEW__ = " + json.dumps(manifest) + ";").encode())
|
||||
self.write("preview/rendered/en-US.js", ('window.__MAIL_PREVIEW_DATA__["en-US"] = ' + json.dumps({"demo": {"activate": "<html></html>"}}) + ";").encode())
|
||||
for name in ["LICENSE", "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"]:
|
||||
self.write(name, b"test")
|
||||
|
||||
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())
|
||||
|
||||
def test_packages_declared_outputs_only_and_never_overwrites(self):
|
||||
self.write("build/themes/stale/mail/stale.tmpl", b"stale")
|
||||
out = self.root / "artifacts"
|
||||
PACKER.package(self.root, self.tag, out)
|
||||
with zipfile.ZipFile(next(out.glob("*.zip"))) as archive:
|
||||
names = archive.namelist()
|
||||
self.assertTrue(any(name.endswith("themes/demo/mail/base/head.tmpl") for name in names))
|
||||
self.assertFalse(any("stale" in name or "/build/" in name for name in names))
|
||||
with self.assertRaises(ValueError):
|
||||
PACKER.package(self.root, self.tag, out)
|
||||
|
||||
def test_refuses_stale_source_tampered_output_and_wrong_version(self):
|
||||
with self.assertRaises(ValueError):
|
||||
PACKER.collect(self.root, "v28.1.0")
|
||||
self.write("themes/demo/theme.css", b"changed")
|
||||
with self.assertRaisesRegex(ValueError, "Source changed"):
|
||||
PACKER.collect(self.root, self.tag)
|
||||
|
||||
def test_refuses_missing_language_bundle(self):
|
||||
(self.root / "preview/rendered/en-US.js").unlink()
|
||||
with self.assertRaises(ValueError):
|
||||
PACKER.collect(self.root, self.tag)
|
||||
|
||||
def test_refuses_modified_generated_files(self):
|
||||
self.write("build/themes/demo/mail/base/head.tmpl", b"modified")
|
||||
with self.assertRaisesRegex(ValueError, "checksum mismatch"):
|
||||
PACKER.collect(self.root, self.tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.25.x'
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
@@ -23,11 +23,17 @@ jobs:
|
||||
- name: Test documentation tracker
|
||||
run: python -B -m unittest discover -s .github/scripts -p 'test_*.py'
|
||||
|
||||
- name: Test — mail template data contexts
|
||||
- name: Verify pinned official snapshot
|
||||
working-directory: tools
|
||||
run: |
|
||||
go run . upstream prepare
|
||||
go run . upstream verify
|
||||
|
||||
- name: Test — framework alignment and multilingual notification parity
|
||||
working-directory: tools
|
||||
run: go test ./...
|
||||
|
||||
- name: Lint — render all templates
|
||||
- name: Build and render all themes and official languages
|
||||
working-directory: tools
|
||||
run: go run . preview all
|
||||
|
||||
@@ -45,12 +51,16 @@ jobs:
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.25.x'
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Verify release tag matches the official snapshot
|
||||
run: |
|
||||
python -c 'import json, os; lock=json.load(open("gitea.lock.json")); assert lock["tag"] == os.environ["GITHUB_REF_NAME"], "Release tag must match the pinned Gitea tag"'
|
||||
|
||||
- name: Verify release notes
|
||||
run: test -s ".github/release-notes/${GITHUB_REF_NAME}.md"
|
||||
|
||||
@@ -63,18 +73,10 @@ jobs:
|
||||
working-directory: tools
|
||||
run: go run . preview all
|
||||
|
||||
- name: Package themes/
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
ARCHIVE="gitea-mail-templates-${VERSION}"
|
||||
mkdir -p "dist/${ARCHIVE}/docs" "dist/${ARCHIVE}/preview"
|
||||
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/"
|
||||
cp -r docs/images "dist/${ARCHIVE}/docs/"
|
||||
cp preview/index.html preview/rendered.js "dist/${ARCHIVE}/preview/"
|
||||
cd dist
|
||||
zip -r "${ARCHIVE}.zip" "${ARCHIVE}"
|
||||
tar -czf "${ARCHIVE}.tar.gz" "${ARCHIVE}"
|
||||
- name: Package generated installable themes and multilingual preview
|
||||
run: python -B .github/scripts/package_release.py --version "$TEMPLATE_RELEASE"
|
||||
env:
|
||||
TEMPLATE_RELEASE: ${{ github.ref_name }}
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
|
||||
@@ -23,3 +23,8 @@ Desktop.ini
|
||||
|
||||
# Preview generated files
|
||||
preview/rendered.js
|
||||
preview/rendered/
|
||||
build/
|
||||
/upstream/
|
||||
dist/
|
||||
node_modules/
|
||||
@@ -3,102 +3,70 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
A growing collection of email template themes for self-hosted Gitea instances. Each theme contains 11 Go `html/template` files covering all supported Gitea notification email types.
|
||||
Theme presentation for self-hosted Gitea, driven by locked official inputs and a reusable control framework. New source architecture supports Gitea 28+; historical release tags retain their original layouts and compatibility.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```
|
||||
themes/ # One directory per theme, with 11 .tmpl files each
|
||||
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
|
||||
.../ # Additional themes can be added
|
||||
tools/ # Go CLI tooling (modular; uses urfave/cli/v2)
|
||||
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 (CLI dependency declared here)
|
||||
preview/ # Browser-based live preview
|
||||
index.html # SPA with style/template/client/viewport switching
|
||||
rendered.js # Pre-rendered HTML (generated, ignored in source clones)
|
||||
docs/ # Bilingual documentation (English + Simplified Chinese)
|
||||
gitea.lock.json # Generated official version, commit and file SHA-256; no vendored upstream source
|
||||
framework/ # Shared header/action/fallback/footer controls and structural layout presets
|
||||
themes/<name>/ # theme.json and theme.css only; no mail data/logic/translation definitions
|
||||
tools/ # Go 1.24+ CLI; urfave/cli and x/net HTML validation
|
||||
upstream/ # Explicit sync, checksum/adapter verification and key discovery
|
||||
builder/ # One official-context alignment layer and theme package generation
|
||||
config/, data/ # Names, descriptions and mock contexts; not a template inventory
|
||||
preview/ # Official-compatible rendering, locale adapter and SSE dev server
|
||||
cli/ # list, create, delete, build, preview, dev, upstream
|
||||
build/themes/ # Generated installable overrides; ignored
|
||||
build/upstream/ # Downloaded immutable official mail/locales/assets/license; ignored
|
||||
preview/ # Vanilla JS UI, generated manifest and per-language scripts
|
||||
docs/ # English/Simplified Chinese documentation and gallery images
|
||||
```
|
||||
|
||||
## Working With Templates
|
||||
## Source and Theme Rules
|
||||
|
||||
### Template Files
|
||||
- Never commit or hand-maintain official input files. `upstream prepare` downloads missing inputs to the ignored cache. `upstream sync --tag vX.Y.Z` explicitly regenerates the lock after reviewing a new version.
|
||||
- Build/preview bootstrap a missing cache, then verify it offline against the lock's commit and per-file SHA-256. Corruption is an error, never silently repaired.
|
||||
- Root `gitea.lock.json` is mandatory for prepare/verify/build/preview/dev; a cached lock is not a substitute. Restore the tracked lock for normal clones; intentional initialization uses explicit `upstream sync --tag v28.0.0`. There is no automatic latest-version selection.
|
||||
- Run upstream commands from `tools/`, or provide the subcommand's `--root` (default `..`). `verify` is read-only; `prepare` never rewrites the root lock; `sync` requires network and stable 28+ tags, replaces cache/lock but does not change docs or publish. See CONTRIBUTING for recovery; preserve invalid caches before rebuilding and avoid concurrent cache writers.
|
||||
- Changed reviewed translation/mail-renderer references require an adapter review before updating reference hashes.
|
||||
- Official templates own notification data, conditions, subject sections and functional URLs. The shared framework may reorganize presentation and add controls using those values and official translations; themes may not add business logic.
|
||||
- `theme.json` has `name`, `description`, `mode` (`shared` or `framed`) and optional `layout`. New themes default to `framed` with the `standard` framework preset. `shared` remains a minimal CSS-only mode without framework controls.
|
||||
- All framed themes use the same header/action/fallback/footer partials and single adapter. Structural presets live in `framework/layouts/`, not in theme directories; color/font/spacing rules live in theme CSS.
|
||||
- Layout fragments form balanced presentation markup. `__HEADER__` inserts the shared brand control; `__MAIL_TYPE__` expands to the discovered mail ID. Deployment logos use `{{AppUrl}}assets/img/favicon.png`; the static preview uses the downloaded icon in its generated data only.
|
||||
- CSS must not add text, external resources or hide official content. Use literal email-compatible CSS; browser preview is not a mail-client emulator.
|
||||
- All referenced translation keys must exist in the official English catalog, including keys outside `mail.*`. Other languages use official English fallback.
|
||||
- The reviewed Polish v28.0.0 invitation placeholder defect is reported as `[UPSTREAM-WARN]`; its exact source text is preserved.
|
||||
- Mail entrypoints come from the snapshot; new types need fixtures. Keep JSON fixture integers as integers for Go formatting.
|
||||
|
||||
- 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
|
||||
## Development
|
||||
|
||||
### Adding a New Theme
|
||||
1. `cd tools && go run . create <name>`.
|
||||
2. Edit only theme metadata/CSS. Shared controls or layout presets are framework changes.
|
||||
3. Run `go run . upstream prepare`, `go test ./...` and `go run . preview all`.
|
||||
4. Check desktop/mobile layouts and languages; update the gallery if appropriate.
|
||||
|
||||
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
|
||||
`build all` generates `build/themes/<name>/mail/`. `preview all` builds and renders every official language, writing `preview/rendered.js` and `preview/rendered/<locale>.js`. Static preview must work on `file://` via script loading. Keep generated output ignored.
|
||||
|
||||
### Preview System
|
||||
`dev` uses Go HTTP/SSE on loopback and watches theme resources, framework, lock/cache and fixture data. The preview remains vanilla JS with theme/template/language/view switching, desktop/mobile viewports, panel controls and keyboard navigation.
|
||||
|
||||
- `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 works via `file://` after running `go run . preview all` in a source clone; the updated packaging workflow includes generated preview data and screenshots for future builds (v28.0.0 and older archives include neither)
|
||||
- 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
|
||||
Tests compare subjects, notification text and functional links across every language/theme and relevant branch, accounting explicitly for framework-added controls/branding. Verify button labels and identical button/fallback URLs separately. Before release, capture real mail using an isolated Gitea matching the locked version.
|
||||
|
||||
## Versioning
|
||||
|
||||
- Release tags identify actual downloadable packages; the compatibility matrix distinguishes released tags from source-only fixes
|
||||
<!-- RELEASE:CURRENT -->
|
||||
- Current template release: **v28.0.0**.
|
||||
<!-- /RELEASE:CURRENT -->
|
||||
- v28.0.0 was verified against Gitea 28.0.0. Gitea 28 replaces the mail-template `FileSize` function with `FormatByteSize`; earlier template releases can fail on Gitea 28, and v28.0.0 is not a drop-in replacement for earlier mail contexts. Choose an archive from the per-version matrix in `COMPATIBILITY.md`; v1.27.2 is a known partial fix for push notifications
|
||||
- The snapshot-driven source refactor is unreleased. Its baseline is Gitea v28.0.0; do not replace the existing v28.0.0 assets.
|
||||
- Historical compatibility is documented in `COMPATIBILITY.md`, including the incomplete v1.27.2 push fix. New source architecture does not extend support to earlier Gitea versions.
|
||||
<!-- TRACKER:UPSTREAM -->
|
||||
- Latest upstream Gitea release: 28.1.0 [PENDING].
|
||||
<!-- /TRACKER:UPSTREAM -->
|
||||
- When a new Gitea version appears, the tracker adds pending compatibility and history rows and updates marked version lines and the pending README badge. After verification, update that version's matrix row and README tested text/badge; keep unreleased fixes distinct from the published release
|
||||
- Tag a new release (`vX.Y.Z`) only when the template content itself changes. On the new Gitea host, do not assume tag pushes automatically build or upload archives; verify the Gitea workflow before relying on it
|
||||
- Before tagging, add `.github/release-notes/vX.Y.Z.md`, run `go test ./...` and `go run . preview all` from `tools/`. The release workflow packages the tag, publishes the reviewed notes, and then updates `RELEASE` blocks on `main` when the host supports those Actions and write permissions; otherwise build/upload and update the labels manually
|
||||
- Participating Markdown documents declare their blocks in a `DOC-TAGS` JSON comment. Each block uses a paired opening `<!-- SUBJECT:CONTENT -->` and closing `<!-- /SUBJECT:CONTENT -->` comment; its body is the managed text. `TRACKER` handles upstream-pending content, `RELEASE` handles published-template labels, and `TRACKER:LATEST-TESTED` / `TRACKER:LATEST-VERIFIED` are manual-only. Within a document, the first block for a subject/content pair wins
|
||||
- Upstream discovery only adds pending documentation; it must not implicitly update snapshots or mark versions verified.
|
||||
- Release tags match the locked Gitea version. Review notes in `.github/release-notes/vX.Y.Z.md`, test, build and complete the matching-instance smoke test before publication.
|
||||
- The workflow packages generated overrides, preview language bundles, docs and upstream license/provenance. Verify Actions/write permissions on the configured Gitea host before relying on automatic upload.
|
||||
- Markdown managed blocks declare pairs in `DOC-TAGS`. Opening `<!-- SUBJECT:CONTENT -->` and closing `<!-- /SUBJECT:CONTENT -->` delimit managed text; the first repeated pair wins. TRACKER manages pending upstream labels; RELEASE manages published labels. LATEST-TESTED and LATEST-VERIFIED remain manual-only.
|
||||
|
||||
## 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
|
||||
- The CLI uses `github.com/urfave/cli/v2`; the preview server itself uses Go's standard HTTP library
|
||||
- Templates must remain compatible with Gitea's `html/template` execution environment
|
||||
- Preview works with `file://` protocol (no server needed)
|
||||
Use `style(name):`, `preview:`, `tools:`, `docs:`, `fix:`, `project:`, `refactor:` or `chore:` as appropriate. Preserve unrelated user changes and keep generated files out of commits.
|
||||
@@ -31,6 +31,20 @@ v1.0.1 retains the pre-1.27 push-commit fields used by the listed Gitea 1.25/1.2
|
||||
> **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. For other Gitea versions, follow the per-version matrix above.
|
||||
<!-- /TRACKER:LATEST-VERIFIED -->
|
||||
|
||||
## Snapshot-Driven Source Status
|
||||
|
||||
The refactor on `main` is **unreleased**. It locks Gitea **v28.0.0**, commit `15b8a5805adf57c5189602008d38cccfd3c795e0`, with 13 mail files (11 entrypoints and 2 shared partials) and 28 official locale files. New source architecture supports Gitea 28+; it does not replace historical release assets or change recommendations in the published matrix.
|
||||
|
||||
Theme sources contain CSS and metadata only. A shared framework organizes official mail values/translations into reusable branding, action/fallback and footer controls. Its single alignment layer preserves notification branches, subjects and functional link targets while allowing presentation changes. Only generated `gitea.lock.json` is committed; immutable official inputs are downloaded to ignored `build/upstream/`. Builds fail on checksum, English-key, adapter-reference or unreviewed action-anchor changes. New mail types require framework alignment and fixtures.
|
||||
|
||||
Missing translations in other languages follow the official English fallback and are reported as `[FALLBACK]`. Gitea v28.0.0's Polish `mail.team_invite.text_1` starts with malformed `%[1]z…` instead of `%[1]s`; preview preserves this official defect and reports `[UPSTREAM-WARN]`. The exception matches the exact source text; other formatting defects fail rendering.
|
||||
|
||||
Gitea 28.1.0 remains [PENDING]. Updating pending documentation does not synchronize the snapshot or verify a new version.
|
||||
|
||||
`upstream prepare` requires the committed root lock and creates only an absent cache; `upstream verify` requires both lock and cache and performs no download. Missing locks are not inferred from cache or latest releases. Explicit `upstream sync --tag vX.Y.Z` can initialize or replace the lock, but does not update this matrix or certify compatibility. See [command usage and cache recovery](CONTRIBUTING.md#official-snapshot-updates).
|
||||
|
||||
Current-source validation includes all-theme/all-language rendering and notification parity, browser checks for static/HTTP language switching and mobile layouts, and an isolated Gitea 28.0.0 template-loading/password-reset mail smoke test. This does not certify rendering in Gmail, Outlook or Apple Mail; those clients still require deployment-specific testing. The optional real-Gitea and browser suites are not automatically run by the current release workflow.
|
||||
|
||||
## Versioning and Known Exceptions
|
||||
|
||||
Release tags name actual downloadable packages. A matching version is useful, but it is not a compatibility guarantee: v1.27.2 has a known defect, and there is no v1.27.1 template tag. Use the recommended package in the matrix; do not infer support from a tag number or from the current `main` branch.
|
||||
@@ -72,7 +86,7 @@ This table describes changes in **Gitea**, not fixes in this template repository
|
||||
|
||||
## 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`).
|
||||
The downloaded official inputs define mail variables and calls; themes do not add business variables. The table below summarizes preview contexts. The locked upstream commit, shared alignment layer and strict fixture rendering are authoritative.
|
||||
|
||||
### Relevant Template Functions
|
||||
|
||||
@@ -106,24 +120,25 @@ All 11 template types use only Gitea built-in variables and functions. Verified
|
||||
| `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/transfer` | `Subject`, `Repo`, `Link` |
|
||||
| `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/actions/workflow_run` | `Subject`, `Run` (with `WorkflowID`, `HTMLURL`), `Jobs` (with status class, icon CID/alt, attempt, URL and duration) |
|
||||
| `repo/issue/assigned` | `Subject`, `Doer`, `Issue`, `Link`, `IsPull`, `CanReply` |
|
||||
| `repo/issue/default` | `Doer`, `Issue`, `Link`, `Body`, `ActionName`, `Comment`, `IsPull`, `IsMention`, `ReviewComments`, `CanReply` |
|
||||
|
||||
> [WARN] **`.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.
|
||||
Official templates reference `mail.*` keys and `actions.runs.attempt`. AST-based checks inspect `.locale.Tr`, `$.locale.Tr` and both plural keys in `TrN`, including nested pipelines. Every referenced key must exist in the locked English catalog. Preview loads full official locale JSON files rather than a copied Go dictionary; formatting, escaping and plural selection follow the reviewed Gitea adapter.
|
||||
|
||||
## How Compatibility Is Verified
|
||||
|
||||
1. **Local validation** — run `go test ./...` and `go run . preview all` from `tools/`; 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 every discovered theme
|
||||
4. **Release checklist** — Each release records its verified Gitea version in the matrix above
|
||||
1. **Locked input verification** — run `go run . upstream prepare` then `go run . upstream verify` from `tools/`; first preparation downloads missing cache files, verification checks hashes, adapter references and English keys offline.
|
||||
2. **Framework alignment** — `go test ./...` checks deterministic shared adaptation/generation and fails on changed primary-action anchors; theme sources cannot own business templates.
|
||||
3. **Notification semantics and controls** — tests compare subjects, notification text and functional links for every theme/language and critical branches, accounting explicitly for added controls/branding. Browser QA checks translated button labels, identical fallback targets, logos and mobile layout. `go run . preview all` builds all language bundles and reports fallback/upstream defects.
|
||||
4. **Matching-instance smoke test** — before publication, load generated overrides in an isolated Gitea matching the snapshot and capture a real notification or password-reset mail. The admin test email does not exercise custom templates.
|
||||
5. **Release identity** — the release tag must match the snapshot tag. Only mark the new release verified after its tests and smoke check; published tags/assets remain unchanged.
|
||||
|
||||
## Version Tracking
|
||||
|
||||
|
||||
@@ -1,100 +1,139 @@
|
||||
# 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.
|
||||
## Adding a Theme
|
||||
|
||||
---
|
||||
1. Run `cd tools && go run . create <name>` to create a framework-backed theme.
|
||||
2. Edit `themes/<name>/theme.json` and `theme.css`.
|
||||
3. Run `go run . upstream prepare`, `go test ./...` and `go run . preview all` from `tools/`.
|
||||
4. Check desktop/mobile previews in English, Simplified Chinese and another official language; submit screenshots with the PR.
|
||||
|
||||
## Ways to Contribute
|
||||
Theme names use lowercase letters, digits and hyphens, starting with a letter. Themes contain only `theme.json` and `theme.css`. New themes default to `framed` with the `standard` layout; `layout` selects a structural preset from the shared framework. The optional `shared` mode remains CSS-only and does not add framework controls.
|
||||
|
||||
### Adding a New Style
|
||||
Shared header, action button, fallback URL, sidebar and footer controls live in `framework/mail/base/`. Structural presets live in `framework/layouts/`; they are framework code, not theme-owned mail templates. `tools/builder/framework.go` is the single alignment layer for official primary-action values and translations. Notification branches, subjects, attachments and contextual links come from downloaded official templates. Do not copy business logic into a theme.
|
||||
|
||||
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)
|
||||
## Design Guidelines
|
||||
|
||||
### Style Guidelines
|
||||
- Theme sources define presentation only; the shared framework organizes official mail values and translations into reusable controls.
|
||||
- Use email-compatible CSS in theme sources and presentation markup in the framework. Theme CSS cannot load external resources, generate text or hide official content; the framework's instance-hosted logo is an intentional exception to external-image avoidance.
|
||||
- The fragment validator permits tables, table rows/cells, divs and spans with presentation attributes. Review desktop and 390px mobile layouts.
|
||||
- Test Gmail, Outlook and Apple Mail where available. Browser preview does not simulate every mail client's CSS support.
|
||||
- Preserve the original theme's colors, fonts, borders, header treatment, button/fallback controls and layout. Do not replace established designs with generic cards or invented decorations.
|
||||
- Gallery screenshots must be PNG, at most 50 KiB each; 10–20 KiB is preferred. Capture the current source build with the floating inspector closed.
|
||||
|
||||
- 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
|
||||
## Official Snapshot Updates
|
||||
|
||||
### Bug Reports
|
||||
Only the tool-generated `gitea.lock.json` is committed: stable Gitea 28+ tag, immutable commit and file checksums. Official templates, locale JSON files, favicon, license and adapter reference sources are downloaded to ignored `build/upstream/`. Do not maintain copies in the source repository.
|
||||
|
||||
If a template doesn't render correctly:
|
||||
### Command Reference
|
||||
|
||||
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
|
||||
Run the following commands from `tools/`. All three subcommands accept `--root <repository-root>` (default: `..`, relative to the working directory); place this flag after the subcommand. Paths containing spaces must be quoted.
|
||||
|
||||
### 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; Go modules download `github.com/urfave/cli/v2` and its dependencies
|
||||
|
||||
### 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)
|
||||
|
||||
```bash
|
||||
cd tools && go run . dev
|
||||
# → http://localhost:3456
|
||||
```powershell
|
||||
# From tools/, with an explicit repository root:
|
||||
go run . upstream verify --root "D:\Work\Development\WebSites\GiteaMailTemplates"
|
||||
```
|
||||
|
||||
- Watches `themes/**/*.tmpl` — auto-rebuilds on save
|
||||
- HTTP server and SSE live reload use Go's standard library; the CLI has a Go module dependency
|
||||
- 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`
|
||||
| Command | Purpose | Network and writes |
|
||||
|---|---|---|
|
||||
| `go run . upstream prepare` | Read the repository lock and prepare its exact inputs | If no cache exists, download files by locked commit, validate SHA-256 and create `build/upstream/`; otherwise verify the existing cache offline. Never changes the root lock. |
|
||||
| `go run . upstream verify` | Audit the existing cache against the root lock | Offline, read-only; does not download files. Reports `[PASS]` and non-English missing-key `[FALLBACK]` lists. |
|
||||
| `go run . upstream sync --tag vX.Y.Z` | Explicitly select an upstream version | Resolve the tag through the GitHub API, download by resolved commit and validate the snapshot; replace the cache and generate `gitea.lock.json`. Requires a stable `vX.Y.Z` tag with major version 28 or later. |
|
||||
|
||||
### Integration Testing
|
||||
There is no implicit `latest`, local Gitea checkout input, token flag or authentication environment-variable support in these commands. Downloads use `api.github.com` (`sync`) and `raw.githubusercontent.com` (`sync`/first `prepare`); network errors and API rate limits are failures, not permission to change versions. Go may separately need network access for its toolchain/modules, even when snapshot verification itself is offline.
|
||||
|
||||
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.
|
||||
### Existing Clone and Offline Use
|
||||
|
||||
The most reliable method is to trigger a real notification. For example, the password
|
||||
reset flow:
|
||||
```bash
|
||||
cd tools
|
||||
go mod download
|
||||
go run . upstream prepare
|
||||
go run . upstream verify
|
||||
go test ./...
|
||||
go run . preview all
|
||||
```
|
||||
|
||||
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
|
||||
`build all`, `preview all` and `dev` invoke preparation automatically. They still require the root lock. A complete verified cache and already installed Go dependencies/toolchain allow offline builds. `verify` checks official inputs, adapter reference hashes and official English keys; framework-added keys/action anchors are checked by build and rendering, so `verify` alone is not a compatibility certification.
|
||||
|
||||
---
|
||||
### Missing Lock and Explicit Version Updates
|
||||
|
||||
If `gitea.lock.json` is missing, `prepare`, `verify`, build and preview fail when opening it—even if `build/upstream/lock.json` remains. The cache lock is not a substitute. Restore the tracked root lock for a normal clone. For intentional initialization, `sync` does not require a prior root lock:
|
||||
|
||||
```bash
|
||||
cd tools
|
||||
# Current source baseline; explicit initialization, not a release operation:
|
||||
go run . upstream sync --tag v28.0.0
|
||||
go run . upstream verify
|
||||
go test ./...
|
||||
go run . preview all
|
||||
```
|
||||
|
||||
To review another version, replace the tag explicitly (for example, `v28.1.0`, still [PENDING] here). Review the generated lock diff, align the shared framework and fixtures, run all checks and the matching-instance smoke test, then update compatibility documentation. `sync` does not build themes, update Markdown, create Git tags, commit, push or publish a release. It must not be run merely to fix a missing cache.
|
||||
|
||||
Changed translation or mail-renderer reference hashes block synchronization until the Go adapter is reviewed; downloading a newer version is not enough to approve it. Official English must contain all official template keys; builds additionally validate framework keys. Other languages fall back to English. Validation/network failures before cache replacement leave existing inputs unchanged. Cache replacement and writing the root lock are separate operations: if a filesystem failure leaves them inconsistent, subsequent verification fails; inspect both before retrying.
|
||||
|
||||
### Cache Recovery
|
||||
|
||||
`prepare` refuses corrupt, incomplete or lock-mismatched caches; it does not merge or silently repair them. `sync` also refuses to replace a non-empty invalid snapshot. After inspecting the error, preserve the exact generated cache directory by moving it aside, then run `prepare` using the reviewed root lock. For example, in PowerShell **from the repository root**, choose 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
|
||||
```
|
||||
|
||||
Do not remove the whole `build/` directory or edit cached official files. To restore a previous version after a deliberate sync, restore its reviewed root lock and rebuild the cache in the same way. Avoid simultaneous `sync`/`prepare`/build processes against one cache; stop `dev` while replacing inputs.
|
||||
|
||||
Mail types are discovered from the snapshot. `tools/data/templates_config.json` supplies names, descriptions and mock contexts only. A new official mail type requires a fixture before preview can pass.
|
||||
|
||||
Gitea v28.0.0 has a reviewed Polish `mail.team_invite.text_1` placeholder defect. Preview preserves official behavior and reports `[UPSTREAM-WARN]`; the exception matches the exact official text. Other formatting errors fail rendering. Do not edit locale snapshots to conceal upstream defects.
|
||||
|
||||
## Local Development
|
||||
|
||||
Use **Go 1.24+**. The CLI uses urfave/cli; HTML validation uses Go's x/net HTML parser. Run `go mod download` and `upstream prepare` once; subsequent verified builds work offline.
|
||||
|
||||
```bash
|
||||
cd tools
|
||||
go run . list
|
||||
go run . build all
|
||||
go run . preview all
|
||||
go run . dev
|
||||
# http://127.0.0.1:3456
|
||||
```
|
||||
|
||||
`build` writes installable files to `build/themes/<name>/mail/`. `preview` also builds them, then writes a small manifest and one JS bundle per official language. Open `preview/index.html` directly for static preview; its language loader supports `file://`.
|
||||
|
||||
The loopback development server watches theme CSS/metadata, shared framework, lock/cache and fixtures. It rebuilds in-process and sends SSE reloads. Installable logos reference `{{AppUrl}}assets/img/favicon.png`; only the generated static preview embeds the downloaded official icon for offline display.
|
||||
|
||||
## Verification and Release
|
||||
|
||||
Tests check deterministic framework adaptation and generation, fail-closed action anchors, official keys and preserved notification semantics. Rendering tests cover all themes/languages and push, review, reply, workflow and attachment branches. Shared controls/branding are explicit presentation additions; button labels, fallback targets and logo references are validated separately.
|
||||
|
||||
Before a release, use an isolated Gitea instance matching the locked tag and trigger a real password-reset or notification email. The administration test-email button bypasses custom templates. Confirm template loading and capture the rendered mail without sending to real users.
|
||||
|
||||
The optional `tools/integration` test automates this with disposable SQLite, users, Git/SSH paths and loopback SMTP. Set `GITEA_SMOKE_BINARY` to a checksum-verified official binary of the locked version, then run `go test ./integration -v -count=1` from `tools/`. Optional browser QA lives in `tools/qa`: install its dependencies, then run `npm test` after generating preview data; `PREVIEW_DEV_URL` includes HTTP preview checks and `--update-gallery` refreshes screenshots.
|
||||
|
||||
Release tags must match the snapshot's Gitea version. Add reviewed notes at `.github/release-notes/vX.Y.Z.md`. The workflow verifies the lock and tests, packages generated themes, multilingual preview, documentation and upstream license/provenance, then updates marked release labels. Keep source-only work separate from the published compatibility matrix. Existing historical tags and assets are unchanged.
|
||||
|
||||
From the repository root, run `python .github/scripts/package_release.py --version vX.Y.Z --output <new-output-directory>` after `preview all` for manual packaging. The same script is used by the release workflow. It verifies source/generated hashes and all language bundles, excludes undeclared stale build directories, and refuses to overwrite existing archives.
|
||||
|
||||
## Reporting Problems
|
||||
|
||||
Include the snapshot tag/commit, theme, email type, preview language and error. Run `go run . upstream verify` and `go run . preview all` first; attach screenshots or the relevant render diagnostic.
|
||||
|
||||
## Commit Conventions
|
||||
|
||||
Any readable commit message in semantic format is welcome. Such as:
|
||||
- `style(<name>):` — theme presentation
|
||||
- `preview:` — browser preview
|
||||
- `tools:` — CLI/build/snapshot tooling
|
||||
- `docs:` — documentation and translations
|
||||
- `fix:` — bug fixes
|
||||
- `refactor:` — restructuring
|
||||
- `chore:` — maintenance
|
||||
|
||||
- `style(<name>):` — template changes for a specific theme
|
||||
- `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
|
||||
- English (this document)
|
||||
- [简体中文](docs/CONTRIBUTING.zh-CN.md)
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under the MIT License.
|
||||
Contributions are licensed under MIT. Retain Gitea copyright and the upstream license when distributing derived templates.
|
||||
@@ -17,7 +17,7 @@ Polished, drop-in email template themes for self-hosted [Gitea](https://about.gi
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
The source on `main` uses locked official Gitea inputs and a reusable control framework. Gitea supplies mail values, notification logic and translations; shared controls organize headers, buttons, fallback URLs and footers, while themes define styling. Official files are downloaded during development/CI, not maintained in this repository. This refactor is unreleased and uses v28.0.0 as its baseline. Source clones must build installable templates first; release archives contain ready-to-copy files. Check the [compatibility matrix](COMPATIBILITY.md) before choosing a published archive.
|
||||
|
||||
---
|
||||
|
||||
@@ -28,17 +28,17 @@ The templates on `main` can replace Gitea's built-in mail templates without patc
|
||||
|  | **Horizon** | Enterprise / Corporate | Blue accent, slate typography, centered cards |
|
||||
|  | **Terminal** | Developers / Tech | Dark mode, monospace, green CLI accents |
|
||||
|  | **Ember** | Community / Open Source | Warm amber, rounded, humanist, inclusive |
|
||||
|  | **Bloom** | Creative / Startup | Frosted glass, soft blue light, iridescent accents |
|
||||
|  | **Heritage** | Education / Research | Navy and gold, serif, classic, authoritative |
|
||||
|  | **Bloom** | Creative / Startup | Blue glass cards, soft gradients, rounded buttons |
|
||||
|  | **Heritage** | Education / Research | Paper texture palette, navy & gold, double borders, serif typography |
|
||||
|  | **Neon** | Gaming / Web3 / Creative Tech | Cyberpunk neon glow, hot pink & cyan, synthwave energy |
|
||||
|  | **Mono** | Design Studios / Editorial | Swiss brutalist, black & white, red accent, zero radius |
|
||||
|  | **Terra** | Sustainability / Wellness | Warm earth tones, organic textures, humanist serif |
|
||||
|  | **Ink** | Publishing / News / Literature | Editorial print, navy & gold, newspaper layout, drop caps |
|
||||
|  | **Terra** | Sustainability / Wellness | Earth tones, terracotta buttons, organic accents, soft cards |
|
||||
|  | **Ink** | Publishing / News / Literature | Newspaper columns, navy & gold rules, editorial serif and drop caps |
|
||||
|  | **Aurora** | Premium SaaS / Mindfulness | Ethereal light gradients, deep purple & teal, atmospheric glow |
|
||||
|
||||
The gallery shows the themes currently included in this repository; new themes can be added as separate directories under `themes/`.
|
||||
|
||||
> Images are screenshots from the [local preview](preview/index.html). See [docs/images/README.md](docs/images/README.md) for capture instructions.
|
||||
> Gallery images show the current shared-framework source build, not historical release archives. Original theme palettes, typography, header treatment and button/fallback controls are preserved. See the [local preview](preview/index.html) and [capture instructions](docs/images/README.md).
|
||||
|
||||
[**Local preview gallery**](preview/index.html) — generate the preview data as described below, then open it in a browser for an interactive style switcher with desktop/mobile viewports and view mode (Modern, Source).
|
||||
|
||||
@@ -48,14 +48,23 @@ The gallery shows the themes currently included in this repository; new themes c
|
||||
|
||||
### Quick Start
|
||||
|
||||
Choose a style, then copy the `mail/` directory into your Gitea custom templates path:
|
||||
For a source clone, build from the committed `gitea.lock.json` first (Go 1.24+; first build downloads locked inputs if the cache is absent). A missing lock is an error; the tool never selects the latest Gitea automatically:
|
||||
|
||||
```bash
|
||||
cd tools
|
||||
go run . build all
|
||||
cd ..
|
||||
```
|
||||
|
||||
Choose a style, then copy its generated `mail/` directory into your Gitea custom templates path. A release archive uses `themes/<name>/mail/` instead of `build/themes/<name>/mail/`:
|
||||
|
||||
```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/
|
||||
mkdir -p /var/lib/gitea/custom/templates/mail
|
||||
cp -r build/themes/horizon/mail/. /var/lib/gitea/custom/templates/mail/
|
||||
|
||||
# Restart Gitea
|
||||
systemctl restart gitea
|
||||
@@ -63,7 +72,9 @@ systemctl restart gitea
|
||||
|
||||
### Custom Directory Location
|
||||
|
||||
| Platform | Default Path |
|
||||
The paths below are common deployment examples, not universal defaults. Confirm your instance's configured custom path before copying files.
|
||||
|
||||
| Platform | Example Custom Path |
|
||||
|---|---|
|
||||
| Linux (binary) | `/var/lib/gitea/custom` |
|
||||
| Linux (Docker) | `/data/gitea` |
|
||||
@@ -71,10 +82,10 @@ systemctl restart gitea
|
||||
|
||||
### Switching Styles
|
||||
|
||||
Overwrite the files with a different style. All templates share the exact same variable structure — no configuration changes needed.
|
||||
Back up your current mail overrides before switching styles. Remove files installed by the previous theme (listed in its `build.json`), then install the new theme's complete output. This matters when switching from `framed` to `shared`: stale per-email overrides would continue using the previous layout. Preserve unrelated custom templates.
|
||||
|
||||
```bash
|
||||
cp -r themes/terminal/mail/* /var/lib/gitea/custom/templates/mail/
|
||||
cp -r build/themes/terminal/mail/. /var/lib/gitea/custom/templates/mail/
|
||||
systemctl restart gitea
|
||||
```
|
||||
|
||||
@@ -89,7 +100,13 @@ reset email — it will render with your custom styles.
|
||||
|
||||
## Preview
|
||||
|
||||
Two modes are available — a static preview that needs no server after generation, and a live-reload dev server for design work. A source clone must generate preview data first. Existing v28.0.0 and earlier archives do not contain the preview or gallery screenshots; the updated packaging workflow includes both for future builds.
|
||||
Static preview works without a server after generation; the development server supports live reload. A source clone must generate the manifest and per-language bundles first. The refactor packages all official languages in future archives. Existing v28.0.0 and earlier archives retain their original contents.
|
||||
|
||||
### Official Inputs (`upstream`)
|
||||
|
||||
Run from `tools/`: `go run . upstream prepare` downloads an absent cache using the existing lock; `go run . upstream verify` checks an existing cache offline; `go run . upstream sync --tag vX.Y.Z` explicitly replaces the version and generates the lock. All support `--root <repository-root>` after the subcommand (default `..`).
|
||||
|
||||
Without the root lock, preparation, verification, build and preview fail. Restore the tracked lock for a normal clone; intentional initialization can use `go run . upstream sync --tag v28.0.0`. Sync requires network access, accepts stable Gitea 28+ tags, and does not update documentation or publish releases. Damaged/mismatched caches require inspection and recovery, not an automatic repair. See the [full command and recovery guide](CONTRIBUTING.md#official-snapshot-updates).
|
||||
|
||||
### Static Preview
|
||||
|
||||
@@ -104,28 +121,29 @@ cd ..
|
||||
|
||||
### Dev Server (Live Reload)
|
||||
|
||||
Start a pure Go development server that watches for `.tmpl` changes, auto-rebuilds, and pushes live updates to the browser via SSE:
|
||||
Start a pure Go development server that watches theme resources, shared framework, lock/cache and fixtures, auto-rebuilds, and pushes live updates via SSE:
|
||||
|
||||
```bash
|
||||
cd tools
|
||||
go run . dev
|
||||
# Open http://localhost:3456 in a browser.
|
||||
# Open http://127.0.0.1:3456 in a browser.
|
||||
```
|
||||
|
||||
| Capability | Static | Dev |
|
||||
|-----------|--------|-----|
|
||||
| Go template rendering | [YES] | [YES] |
|
||||
| Theme/template switching | [YES] | [YES] |
|
||||
| Theme/template/language switching | [YES] | [YES] |
|
||||
| Live reload on save | [NO] | [YES] |
|
||||
|
||||
### Features
|
||||
|
||||
- Theme switcher — browse all available visual styles
|
||||
- Template switcher — all 11 email types
|
||||
- Template switcher — mail types discovered from the official snapshot (currently 11)
|
||||
- Language switcher — all official locale files (28 in the v28.0.0 snapshot), loaded on demand
|
||||
- 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
|
||||
- Keyboard shortcuts — `←→` tab between Theme/Template/Language/View, `↑↓` select within, `d`/`m` viewport
|
||||
|
||||
---
|
||||
|
||||
@@ -133,17 +151,24 @@ go run . dev
|
||||
|
||||
```
|
||||
gitea-mail-templates/
|
||||
├── themes/ # One directory per visual style, 11 .tmpl files each
|
||||
│ ├── ... # Custom styles are added here as separate directories
|
||||
├── gitea.lock.json # Generated version/commit/checksums, no vendored upstream source
|
||||
├── framework/ # Reusable mail controls and structural layout presets
|
||||
├── themes/ # theme.json and theme.css per style; no mail logic
|
||||
├── build/upstream/ # Downloaded official inputs and license; ignored
|
||||
├── build/themes/ # Generated installable overrides; ignored
|
||||
│ └── <name>/mail/ # Generated files for each source theme
|
||||
├── preview/ # Live preview SPA
|
||||
│ ├── index.html # Style/template/client/viewport switcher
|
||||
│ └── rendered.js # Generated by tools/; ignored in source clones
|
||||
│ ├── index.html # Theme/template/language/view/viewport switcher
|
||||
│ ├── rendered.js # Generated manifest; ignored
|
||||
│ └── rendered/ # Generated per-language JS bundles; ignored
|
||||
├── tools/ # Modular CLI tooling
|
||||
│ ├── tools.go # Main entry point
|
||||
│ ├── cli/ # CLI subcommands (list, create, delete, preview)
|
||||
│ ├── cli/ # CLI commands (upstream, build, preview, dev, list, create, delete)
|
||||
│ ├── config/ # Config types and templates_config.json loading
|
||||
│ ├── data/ # templates_config.json — single source of truth
|
||||
│ ├── preview/ # Template rendering engine
|
||||
│ ├── data/ # Preview metadata and mock contexts
|
||||
│ ├── upstream/ # Snapshot sync and offline verification
|
||||
│ ├── builder/ # Official-context alignment and framework/theme generation
|
||||
│ ├── preview/ # Rendering, locale adapter and dev server
|
||||
│ └── go.mod
|
||||
├── docs/ # Bilingual documentation (English + Simplified Chinese)
|
||||
├── AGENTS.md # AI agent guidance
|
||||
@@ -155,6 +180,8 @@ gitea-mail-templates/
|
||||
|
||||
### Template Types
|
||||
|
||||
These are the official v28.0.0 entrypoints, not individually maintained theme sources. Framework-backed `framed` themes generate them using one alignment layer and shared controls. Optional `shared` mode overrides only the two base partials without adding controls.
|
||||
|
||||
| File | Email Trigger |
|
||||
|---|---|
|
||||
| `mail/user/auth/activate.tmpl` | Account activation |
|
||||
@@ -183,7 +210,7 @@ gitea-mail-templates/
|
||||
<!-- TRACKER:UPSTREAM -->
|
||||
- **Upstream Gitea 28.1.0:** [PENDING]
|
||||
<!-- /TRACKER:UPSTREAM -->
|
||||
- The current source uses Gitea's official template data paths — see [COMPATIBILITY.md](COMPATIBILITY.md) for release-specific limitations
|
||||
- New source architecture targets Gitea 28+ and verifies the pinned snapshot offline; see [COMPATIBILITY.md](COMPATIBILITY.md) for source status and release-specific limitations
|
||||
- Uses only built-in Gitea template functions and official translation keys
|
||||
- No custom template functions or locale patches required
|
||||
|
||||
@@ -191,11 +218,13 @@ gitea-mail-templates/
|
||||
|
||||
## 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. **Locale-aware** — Notification text uses Gitea's `{{.locale.Tr}}` system; some decorative labels remain theme-specific English text
|
||||
1. **Official mail values** — Preserve notification data, conditions, subjects and functional link targets.
|
||||
2. **Shared controls, separate themes** — One framework owns content alignment and reusable controls; themes own CSS and retain original designs.
|
||||
3. **Responsive** — Current framed themes use 600px cards with 390px mobile previews; test target email clients before deployment.
|
||||
4. **Locale-aware** — Official catalogs supply all text; missing keys follow Gitea's English fallback.
|
||||
5. **Reproducible** — A lightweight lock pins immutable downloads; verified cached builds work offline and install files remain generated.
|
||||
|
||||
Gitea v28.0.0 contains a known Polish invitation placeholder defect. Preview reports `[UPSTREAM-WARN]` and preserves official behavior. Details and validation commands are in [CONTRIBUTING.md](CONTRIBUTING.md#official-snapshot-updates).
|
||||
|
||||
---
|
||||
|
||||
@@ -212,7 +241,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. A Simplified Chinese tran
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE). Free to use, modify, and distribute in any Gitea deployment.
|
||||
MIT — see [LICENSE](LICENSE) and [third-party notices](THIRD_PARTY_NOTICES.md). Generated archives retain the official Gitea license and snapshot provenance.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Third-Party Notices
|
||||
|
||||
Official mail templates, locale JSON catalogs, the preview favicon and reviewed implementation references originate from [Gitea](https://github.com/go-gitea/gitea). The tool downloads them to ignored `build/upstream/`; only the generated tag, commit and file SHA-256 lock (`gitea.lock.json`) is kept in source control.
|
||||
|
||||
Gitea is licensed under MIT. Its unmodified license is downloaded as `build/upstream/LICENSE`. Release archives retain it as `GITEA-LICENSE` and include `upstream-lock.json` as provenance for generated templates and translated previews. Deployment mail logos reference the instance asset URL; only generated preview data embeds the official icon for offline display.
|
||||
|
||||
Theme presentation and tooling are covered by this repository's [MIT license](LICENSE). Go module dependencies and their versions are recorded in `tools/go.mod` and `tools/go.sum`.
|
||||
@@ -1,83 +1,139 @@
|
||||
# 贡献指南 — Gitea 邮件模板
|
||||
|
||||
## 参与方式
|
||||
## 添加主题
|
||||
|
||||
### 添加新风格
|
||||
1. 执行 `cd tools && go run . create <名称>`,创建共享框架主题。
|
||||
2. 编辑 `themes/<名称>/theme.json` 和 `theme.css`。
|
||||
3. 在 `tools/` 中运行 `go run . upstream prepare`、`go test ./...` 和 `go run . preview all`。
|
||||
4. 检查英文、简体中文及另一种官方语言的桌面/移动端预览,随 PR 提交截图。
|
||||
|
||||
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)
|
||||
主题名称以小写字母开头,可包含小写字母、数字和连字符。主题只包含 `theme.json` 与 `theme.css`。新主题默认使用 `framed` 模式及 `standard` 框架布局;`layout` 选择共享框架中的结构预设。可选的 `shared` 模式仅注入 CSS,不添加框架控件。
|
||||
|
||||
### 风格指南
|
||||
页头、操作按钮、失效提示/备用链接、侧栏和页脚统一由 `framework/mail/base/` 提供;结构预设位于 `framework/layouts/`,均属于框架,而非主题维护的邮件模板。`tools/builder/framework.go` 是唯一的官方操作值/翻译对齐层。通知分支、主题行、附件及上下文链接来自下载的官方模板,不得复制到各主题中。
|
||||
|
||||
- 每个风格必须包含全部 **11 种模板类型**
|
||||
- 只使用 Gitea 内置模板函数
|
||||
- 翻译键必须来自 Gitea 官方语言文件(`mail.*` 命名空间)
|
||||
- **不要在以下模板中使用 `.DisplayName`**:collaborator、transfer、release、workflow_run、assigned、default
|
||||
- 面向 600px 宽度的邮件客户端设计
|
||||
- 尽可能在 Gmail、Outlook、Apple Mail 中测试
|
||||
## 设计规范
|
||||
|
||||
### 报告 Bug
|
||||
- 主题仅定义展示样式,共享框架使用官方邮件值与翻译组织可复用控件。
|
||||
- 主题使用兼容邮件客户端的 CSS,框架负责展示结构;主题 CSS 不加载外部资源、不生成文字、不隐藏官方内容。框架引用实例托管的 Logo 是有意保留的图片引用。
|
||||
- 片段校验允许展示性的 table、tr、td、div、span 等节点及展示属性,检查桌面及 390px 移动端布局。
|
||||
- 尽可能测试 Gmail、Outlook、Apple Mail;浏览器预览不能模拟所有客户端的 CSS 支持。
|
||||
- 忠于原主题的配色、字体、边框、页头、按钮/备用链接及布局,不使用通用卡片或新增装饰替代既有设计。
|
||||
- 画廊截图使用 PNG,单张最多 50 KiB,建议 10–20 KiB。截图须来自当前源码构建,并关闭浮动信息面板。
|
||||
|
||||
1. 检查引用的 Go 模板变量是否存在
|
||||
2. 验证翻译键是否与 Gitea 语言文件匹配
|
||||
3. 确认 `.DisplayName` 未在不支持的模板中使用
|
||||
4. 重新生成预览:`cd tools && go run . preview all`
|
||||
5. 提交 issue,注明风格名称、邮件类型及错误描述
|
||||
## 更新官方快照
|
||||
|
||||
### 改进文档
|
||||
源码仅保留工具生成的 `gitea.lock.json`,记录稳定 Gitea 28+ 标签、不可变提交及文件校验值。官方模板、语言 JSON、图标、许可证和适配参考实现均下载到被忽略的 `build/upstream/`,不在仓库中维护副本。
|
||||
|
||||
文档改进、预览截图、安装指南和翻译始终欢迎。
|
||||
### 命令参考
|
||||
|
||||
---
|
||||
以下命令均在 `tools/` 执行。三个子命令都支持 `--root <仓库根目录>`,默认值为相对当前工作目录的 `..`;参数放在子命令之后,含空格的路径须加引号。
|
||||
|
||||
## 开发环境
|
||||
|
||||
- **Go 1.21+** 用于模板渲染和 CLI 工具;Go 模块需下载 `github.com/urfave/cli/v2` 及其依赖
|
||||
|
||||
### 本地预览(静态)
|
||||
|
||||
1. 先生成预览数据:`cd tools && go run . preview all`
|
||||
2. 在浏览器中打开 `preview/index.html` — 无需服务器
|
||||
|
||||
### 开发服务器(实时重载)
|
||||
|
||||
```bash
|
||||
cd tools && go run . dev
|
||||
# → http://localhost:3456
|
||||
```powershell
|
||||
# 在 tools/ 中显式指定仓库根目录:
|
||||
go run . upstream verify --root "D:\Work\Development\WebSites\GiteaMailTemplates"
|
||||
```
|
||||
|
||||
修改 `.tmpl` 文件后自动重建并推送至浏览器。HTTP 服务与 SSE 实时重载使用 Go 标准库实现,CLI 本身依赖 Go 模块。
|
||||
| 命令 | 用途 | 联网与写入行为 |
|
||||
|---|---|---|
|
||||
| `go run . upstream prepare` | 根据仓库锁文件准备精确匹配的输入 | 缓存不存在时按锁定提交下载,验证 SHA-256 后创建 `build/upstream/`;缓存存在时离线校验。不修改根锁文件。 |
|
||||
| `go run . upstream verify` | 对照根锁文件检查已有缓存 | 离线、只读,不下载;输出 `[PASS]` 及非英文缺键的 `[FALLBACK]` 列表。 |
|
||||
| `go run . upstream sync --tag vX.Y.Z` | 显式选择上游版本 | 通过 GitHub API 解析标签,按提交下载并校验快照,替换缓存并生成 `gitea.lock.json`。仅接受主版本不小于 28 的稳定 `vX.Y.Z` 标签。 |
|
||||
|
||||
### 集成测试
|
||||
这些命令不支持隐式 `latest`、读取本地 Gitea 克隆、令牌参数或认证环境变量。`sync` 使用 `api.github.com`,首次 `prepare` 与 `sync` 下载使用 `raw.githubusercontent.com`;网络错误与 API 限流会报错,不会自动换版本。即使快照检查离线,Go 工具链与模块仍可能需要单独联网下载。
|
||||
|
||||
将模板部署到 Gitea 实例后,请通过真实的邮件通知进行验证。管理后台的测试邮件
|
||||
(**Site Administration > Configuration > Mailer > Send Test Email**)不会使用
|
||||
自定义邮件模板——它走的是内置代码路径。
|
||||
### 已有克隆与离线使用
|
||||
|
||||
最可靠的验证方式是触发一次真实的邮件通知,推荐使用密码重置流程:
|
||||
```bash
|
||||
cd tools
|
||||
go mod download
|
||||
go run . upstream prepare
|
||||
go run . upstream verify
|
||||
go test ./...
|
||||
go run . preview all
|
||||
```
|
||||
|
||||
1. 退出登录,点击登录页的**"忘记密码"**
|
||||
2. 输入账户邮箱并提交
|
||||
3. 查看密码重置邮件——它将使用你的自定义邮件模板渲染
|
||||
`build all`、`preview all` 和 `dev` 会自动准备输入,但仍要求根锁文件存在。缓存完整且 Go 工具链/依赖已安装后可离线构建。`verify` 检查官方输入、适配参考实现哈希和官方英文键;框架新增键与操作锚点由构建和渲染检查,因此单独通过 `verify` 不代表兼容性验证完成。
|
||||
|
||||
---
|
||||
### 锁文件缺失与显式更新版本
|
||||
|
||||
缺少 `gitea.lock.json` 时,`prepare`、`verify`、构建与预览均在读取锁文件时失败,即使 `build/upstream/lock.json` 仍在也不会代用。普通源码克隆应恢复受版本控制的根锁文件;有意首次初始化时,`sync` 不要求已有根锁文件:
|
||||
|
||||
```bash
|
||||
cd tools
|
||||
# 当前源码基线;显式初始化,不是发布操作:
|
||||
go run . upstream sync --tag v28.0.0
|
||||
go run . upstream verify
|
||||
go test ./...
|
||||
go run . preview all
|
||||
```
|
||||
|
||||
评审其它版本时显式替换标签,例如此处仍为 [PENDING] 的 `v28.1.0`。检查生成的锁文件差异,对齐共享框架与测试数据,执行全部检查及同版本实例冒烟测试后,再更新兼容性文档。`sync` 不构建主题、不更新 Markdown、不创建 Git 标签、不提交、不推送,也不发布。仅缓存缺失时不应使用它。
|
||||
|
||||
翻译或邮件渲染参考实现哈希变化会阻止同步,须先复核 Go 适配逻辑;下载新版本并不等于确认兼容。官方英文须包含官方模板引用的键,构建另行检查框架键;其它语言缺键回退英文。缓存替换前的网络/校验失败不会修改原输入。缓存替换和根锁文件写入是两个操作:文件系统错误可能留下不一致状态,后续校验会失败,重试前先检查二者。
|
||||
|
||||
### 缓存恢复
|
||||
|
||||
`prepare` 拒绝损坏、不完整或锁文件不匹配的缓存,不合并、不静默修复;`sync` 也拒绝替换非空的无效快照。检查错误后,将精确的生成缓存目录移到备份位置,再按已复核的根锁文件执行 `prepare`。例如在 **仓库根目录的 PowerShell** 中,选择尚不存在的备份名称:
|
||||
|
||||
```powershell
|
||||
Move-Item -LiteralPath .\build\upstream -Destination .\build\upstream.backup
|
||||
Set-Location tools
|
||||
go run . upstream prepare
|
||||
go run . upstream verify
|
||||
```
|
||||
|
||||
不要删除整个 `build/` 或修改缓存中的官方文件。显式同步后如需回退,恢复之前已复核的根锁文件,再按同样方式重建缓存。避免多个 `sync`/`prepare`/构建进程同时操作同一缓存;替换输入前先停止 `dev`。
|
||||
|
||||
邮件类型由快照发现,`tools/data/templates_config.json` 仅提供名称、描述和模拟上下文。新增官方邮件类型后,必须补充测试数据才能通过预览校验。
|
||||
|
||||
Gitea v28.0.0 的波兰语 `mail.team_invite.text_1` 存在已确认的占位符缺陷。预览保留官方行为并报告 `[UPSTREAM-WARN]`,例外严格匹配官方原文。其他格式错误会使渲染失败。不要直接修改语言快照来隐藏官方问题。
|
||||
|
||||
## 本地开发
|
||||
|
||||
使用 **Go 1.24+**。CLI 依赖 urfave/cli,HTML 校验使用 Go 的 x/net 解析器。先执行 `go mod download` 和 `upstream prepare` 下载依赖及锁定输入,之后可离线构建。
|
||||
|
||||
```bash
|
||||
cd tools
|
||||
go run . list
|
||||
go run . build all
|
||||
go run . preview all
|
||||
go run . dev
|
||||
# http://127.0.0.1:3456
|
||||
```
|
||||
|
||||
`build` 将安装文件写入 `build/themes/<名称>/mail/`。`preview` 同时完成构建,生成小型清单和每种官方语言各自的 JS 数据包。直接打开 `preview/index.html` 即可静态预览,语言加载支持 `file://`。
|
||||
|
||||
回环开发服务器监听主题 CSS/元数据、共享框架、锁文件/缓存和测试数据,通过 SSE 刷新。安装模板的 Logo 引用 `{{AppUrl}}assets/img/favicon.png`;仅静态预览的生成数据嵌入下载的官方图标,以支持离线显示。
|
||||
|
||||
## 验证与发布
|
||||
|
||||
测试检查框架适配与生成的确定性、操作锚点变化时失败、官方翻译键及通知语义保留。渲染覆盖全部主题/语言及推送、评审、回复、工作流和附件分支。控件及品牌区域是明确的展示增补;按钮文字、备用链接目标和 Logo 引用单独校验。
|
||||
|
||||
发布前,在与快照标签一致的隔离 Gitea 实例中触发真实的密码重置或通知邮件。管理后台的测试邮件按钮不会使用自定义模板。确认模板加载并捕获邮件,避免向真实用户发送测试内容。
|
||||
|
||||
可选的 `tools/integration` 测试使用临时 SQLite、用户、Git/SSH 路径及回环 SMTP 自动完成此流程。将 `GITEA_SMOKE_BINARY` 设为已校验官方 SHA-256、版本与快照一致的 Gitea 可执行文件,在 `tools/` 执行 `go test ./integration -v -count=1`。浏览器检查位于 `tools/qa`:生成预览后安装其可选依赖并运行 `npm test`;设置 `PREVIEW_DEV_URL` 可同时检查 HTTP 预览,`--update-gallery` 可更新截图。
|
||||
|
||||
发行标签须与快照中的 Gitea 版本一致,在 `.github/release-notes/vX.Y.Z.md` 添加已复核的说明。工作流校验快照和测试,打包生成主题、多语言预览、文档及官方许可证/来源信息,再更新发布标签区块。源码修改与已发布兼容矩阵保持区分;历史标签和附件不变。
|
||||
|
||||
手动打包时,先执行 `preview all`,再从仓库根目录运行 `python .github/scripts/package_release.py --version vX.Y.Z --output <新输出目录>`。发布工作流使用同一脚本;它校验源码/产物哈希和全部语言包,排除未声明的旧构建目录,并拒绝覆盖已有压缩包。
|
||||
|
||||
## 报告问题
|
||||
|
||||
注明快照标签/提交、主题、邮件类型、预览语言和错误。先运行 `go run . upstream verify` 与 `go run . preview all`,附上截图或渲染诊断。
|
||||
|
||||
## 提交规范
|
||||
|
||||
- `style(<名称>):` — 特定风格的模板变更
|
||||
- `preview:` — 预览工具变更
|
||||
- `tools:` — Go 构建脚本变更
|
||||
- `style(<名称>):` — 主题展示样式
|
||||
- `preview:` — 浏览器预览
|
||||
- `tools:` — CLI、构建及快照工具
|
||||
- `docs:` — 文档和翻译
|
||||
- `fix:` — Bug 修复
|
||||
- `project:` — README、LICENSE、元文件
|
||||
- `fix:` — 修复
|
||||
- `refactor:` — 重构
|
||||
- `chore:` — 维护
|
||||
|
||||
## 翻译
|
||||
## 翻译与许可证
|
||||
|
||||
- [English CONTRIBUTING](../CONTRIBUTING.md)
|
||||
- 简体中文(本文)
|
||||
|
||||
## 许可协议
|
||||
|
||||
参与贡献即表示您同意将您的贡献以 MIT 许可证授权。
|
||||
贡献以 MIT 许可证授权,分发派生模板时须保留 Gitea 版权及官方许可证。
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
大多数自托管 Gitea 实例使用默认的纯文本邮件模板。本项目提供了**开箱即用、视觉精美的替代方案**——每种方案都针对特定社区或受众设计,您可以选择最适合您用户的风格。
|
||||
|
||||
`main` 分支中的模板可直接替换 Gitea 内置邮件模板,**无需补丁、插件或 fork**。使用已发布的压缩包前,请先查看[兼容性说明](../COMPATIBILITY.md),了解旧版本的限制。
|
||||
`main` 分支使用锁定的官方输入及共享控件框架:Gitea 提供邮件值、通知逻辑与翻译,框架负责页头、按钮、备用链接和页脚,主题仅定义样式。官方文件在开发/CI 时下载,不在仓库中维护副本。该重构尚未发布,以 v28.0.0 为基线;源码克隆需先构建安装文件,发行包提供可直接复制的产物。使用已发布版本前,请查看[兼容性说明](../COMPATIBILITY.md)。
|
||||
|
||||
---
|
||||
|
||||
@@ -26,15 +26,15 @@
|
||||
|  | **Horizon** | 企业/公司 | 蓝色强调色、石板灰排版、居中卡片 |
|
||||
|  | **Terminal** | 开发者/技术 | 暗色模式、等宽字体、绿色命令行风格 |
|
||||
|  | **Ember** | 社区/开源 | 暖琥珀色、圆角、人文主义、包容 |
|
||||
|  | **Bloom** | 创意/初创 | 磨砂玻璃、柔和蓝色光线、虹彩点缀 |
|
||||
|  | **Heritage** | 教育/研究 | 海军蓝与金色、衬线字体、经典、权威 |
|
||||
|  | **Bloom** | 创意/初创 | 蓝色玻璃卡片、柔和渐变、圆角按钮 |
|
||||
|  | **Heritage** | 教育/研究 | 纸质色调、海军蓝与金色、双线边框、衬线字体 |
|
||||
|  | **Neon** | 游戏/Web3/创意科技 | 赛博朋克霓虹、粉红与青色、合成波能量 |
|
||||
|  | **Mono** | 设计工作室/编辑 | 瑞士粗野主义、黑白红强调、零圆角 |
|
||||
|  | **Terra** | 可持续/健康 | 温暖大地色调、有机质感、人文衬线 |
|
||||
|  | **Ink** | 出版/新闻/文学 | 编辑印刷、深蓝与金色、报纸排版 |
|
||||
|  | **Terra** | 可持续/健康 | 大地色、陶土色按钮、自然风格细节、柔和卡片 |
|
||||
|  | **Ink** | 出版/新闻/文学 | 报刊分栏、海军蓝与金色分隔线、衬线字体及首字下沉 |
|
||||
|  | **Aurora** | 高端SaaS/正念 | 空灵光效渐变、深紫与青绿、大气光晕 |
|
||||
|
||||
> 图片为 600px 宽截图,来自[本地预览](../preview/index.html)。截图方法参见 [images/README.md](images/README.md)。
|
||||
> 画廊展示共享框架的当前源码构建,并非历史发行包。保留原主题的配色、字体、页头及按钮/备用链接控件。查看[本地预览](../preview/index.html)及[截图说明](images/README.md)。
|
||||
|
||||
[**本地预览画廊**](../preview/index.html) — 先按下文生成预览数据,再在浏览器中打开。
|
||||
|
||||
@@ -42,16 +42,25 @@
|
||||
|
||||
## 安装
|
||||
|
||||
选择一种风格,将 `mail/` 目录复制到 Gitea 自定义模板路径:
|
||||
源码克隆需按仓库提交的 `gitea.lock.json` 先构建(Go 1.24+,缓存不存在时首次构建下载锁定输入)。锁文件缺失会报错,不会自动选择最新版 Gitea:
|
||||
|
||||
```bash
|
||||
cp -r themes/horizon/mail/* /var/lib/gitea/custom/templates/mail/
|
||||
cd tools
|
||||
go run . build all
|
||||
cd ..
|
||||
```
|
||||
|
||||
选择一种风格,将生成的 `mail/` 目录复制到 Gitea 自定义模板路径。发行压缩包中的路径为 `themes/<名称>/mail/`,源码构建后的路径为 `build/themes/<名称>/mail/`:
|
||||
|
||||
```bash
|
||||
mkdir -p /var/lib/gitea/custom/templates/mail
|
||||
cp -r build/themes/horizon/mail/. /var/lib/gitea/custom/templates/mail/
|
||||
systemctl restart gitea
|
||||
```
|
||||
|
||||
切换风格只需覆盖文件,无需更改配置。
|
||||
切换前备份当前邮件覆盖文件,根据上一主题的 `build.json` 移除其安装文件,再复制新主题完整输出;保留其他自定义模板。从 `framed` 切换到 `shared` 时,遗留的正文覆盖文件仍会使用旧布局,因此不能仅覆盖两个共享片段。
|
||||
|
||||
`GITEA_CUSTOM` 决定自定义目录;常见路径为 Linux 二进制部署的 `/var/lib/gitea/custom`、Docker 的 `/data/gitea`,以及 Windows 的 `C:\gitea\custom`。
|
||||
请确认实例实际配置的自定义目录再安装;常见部署示例为 Linux 二进制部署的 `/var/lib/gitea/custom`、Docker 的 `/data/gitea`,以及 Windows 的 `C:\gitea\custom`,这些并非所有实例的统一默认路径。
|
||||
|
||||
### 确认生效
|
||||
|
||||
@@ -63,7 +72,13 @@ systemctl restart gitea
|
||||
|
||||
## 预览
|
||||
|
||||
从源码克隆时,需先生成被忽略的 `preview/rendered.js`。现有 v28.0.0 及更早的压缩包未包含预览文件和画廊截图;更新后的打包流程会在后续构建中加入两者。
|
||||
源码克隆需先生成被忽略的 `preview/rendered.js` 清单及逐语言数据包。后续发行包会包含全部官方语言的预览,现有 v28.0.0 及更早压缩包保持原内容。
|
||||
|
||||
### 官方输入命令(`upstream`)
|
||||
|
||||
在 `tools/` 执行:`go run . upstream prepare` 根据已有锁文件下载尚不存在的缓存;`go run . upstream verify` 离线校验已有缓存;`go run . upstream sync --tag vX.Y.Z` 显式替换版本并生成锁文件。均支持子命令后的 `--root <仓库根目录>`,默认 `..`。
|
||||
|
||||
缺少根锁文件时,准备、校验、构建与预览都会失败。普通克隆应恢复受版本控制的锁文件;有意初始化可运行 `go run . upstream sync --tag v28.0.0`。同步须联网,仅接受稳定 Gitea 28+ 标签,不更新文档或发布版本。损坏/版本不匹配的缓存需要检查后恢复,而非自动修复,详见[完整命令与恢复指南](CONTRIBUTING.zh-CN.md#更新官方快照)。
|
||||
|
||||
**静态模式:**
|
||||
|
||||
@@ -80,16 +95,16 @@ cd ..
|
||||
```bash
|
||||
cd tools
|
||||
go run . dev
|
||||
# 在浏览器中打开 http://localhost:3456
|
||||
# 在浏览器中打开 http://127.0.0.1:3456
|
||||
```
|
||||
|
||||
| 功能 | 静态 | Dev |
|
||||
|-----------|--------|-----|
|
||||
| Go 模板渲染 | [YES] | [YES] |
|
||||
| 主题/模板切换 | [YES] | [YES] |
|
||||
| 主题/模板/语言切换 | [YES] | [YES] |
|
||||
| 实时重载 | [NO] | [YES] |
|
||||
|
||||
预览支持主题与模板切换、Modern/Source 视图、桌面/移动端尺寸以及模板参数面板。`←→` 可切换选择框,`↑↓` 可切换选项,`d`/`m` 可切换视口。
|
||||
预览支持主题、邮件类型及全部官方语言切换(v28.0.0 快照含 28 种语言),以及 Modern/Source 视图、桌面/移动端尺寸和参数面板。语言数据按需加载,支持直接打开本地文件。`←→` 可切换选择框,`↑↓` 可切换选项,`d`/`m` 可切换视口。
|
||||
|
||||
---
|
||||
|
||||
@@ -106,15 +121,15 @@ go run . dev
|
||||
<!-- TRACKER:UPSTREAM -->
|
||||
- **上游 Gitea 28.1.0:** [PENDING]
|
||||
<!-- /TRACKER:UPSTREAM -->
|
||||
- 当前源码使用 Gitea 官方模板的数据路径;各发布版的限制详见 [兼容性说明](../COMPATIBILITY.md)
|
||||
- 新源码架构支持 Gitea 28+,离线校验官方快照;源码状态及历史发行版限制详见[兼容性说明](../COMPATIBILITY.md)。
|
||||
|
||||
## 模板类型
|
||||
|
||||
每种风格都提供 11 种邮件类型:账户激活、邮箱验证、注册通知、密码重置、团队邀请、仓库协作者、仓库转移、新版发布、Actions 工作流、议题/合并请求指派及议题/合并请求更新。具体文件路径参见[英文 README](../README.md#template-types)。
|
||||
邮件类型由官方输入发现,当前为 11 种:账户激活、邮箱验证、注册通知、密码重置、团队邀请、仓库协作者、仓库转移、新版发布、Actions 工作流、议题/合并请求指派及议题/合并请求更新。具体路径参见[英文 README](../README.md#template-types)。`framed` 主题使用同一对齐层与共享控件生成安装文件;可选的 `shared` 模式仅覆盖两个基础片段,不添加框架控件。
|
||||
|
||||
## 设计与翻译
|
||||
|
||||
模板采用适合邮件客户端的响应式布局,并提供按钮失效时可用的备用链接。通知正文使用 Gitea 的翻译系统;部分主题装饰性标签仍为英文,并非所有可见文字都已本地化。
|
||||
主题仅维护 CSS 与元数据;共享框架使用官方邮件值及翻译组织可复用控件,保留通知条件、主题行和功能链接目标。官方模板及语言文件下载到忽略的 `build/upstream/`,仅提交工具生成的 `gitea.lock.json`。语言缺键按 Gitea 规则回退英文。v28.0.0 波兰语邀请文案存在官方占位符缺陷,预览报告 `[UPSTREAM-WARN]` 并保留官方行为,详见[贡献指南](CONTRIBUTING.zh-CN.md#更新官方快照)。
|
||||
|
||||
## 文档与贡献
|
||||
|
||||
@@ -125,4 +140,4 @@ go run . dev
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT — 详见 [LICENSE](../LICENSE)。
|
||||
MIT — 详见 [LICENSE](../LICENSE) 和[第三方声明](../THIRD_PARTY_NOTICES.md)。发行包保留官方 Gitea 许可证和快照来源信息。
|
||||
@@ -1,5 +1,7 @@
|
||||
# Style Preview Images
|
||||
|
||||
> Images show the current snapshot-driven source build. Historical release archives retain their original templates and are not replaced by these screenshots.
|
||||
|
||||
Place theme style screenshots of **Desktop** in PNG format here, captured from the [local preview](../../preview/index.html).
|
||||
|
||||
## Naming Convention
|
||||
@@ -19,9 +21,11 @@ To ensure screenshots can be displayed in the README, please follow these size r
|
||||
|
||||
## How to Capture
|
||||
|
||||
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"**
|
||||
1. Start the dev server: `cd tools && go run . dev` and open http://127.0.0.1:3456 in your browser
|
||||
2. For each style, select the **"Register Notify"** template and **"Modern"** view, **"en-US"** language and **"Desktop"** viewport; close the floating inspector
|
||||
3. Take a screenshot of the rendered email (600px width recommended)
|
||||
4. Save as `<style-name>.png` in this directory
|
||||
|
||||
> For a source clone, run `cd tools && go run . preview all`, return to the repository root, then open `preview/index.html` directly.
|
||||
|
||||
For automated capture, install the optional dependencies in `tools/qa` and run `node preview.cjs --update-gallery` there. Set `BROWSER_EXECUTABLE_PATH` when using an installed Chrome/Edge instead of Playwright's bundled Chromium.
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 17 KiB |
@@ -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__">
|
||||
@@ -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 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__">
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
</td></tr>
|
||||
</table>
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
</td><td width="36%" class="email-sidebar resp-hide">__SIDEBAR__</td></tr>
|
||||
<tr><td colspan="2" 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 colspan="2" class="email-header">__HEADER__</td></tr>
|
||||
<tr><td width="64%" 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__">
|
||||
@@ -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__">
|
||||
@@ -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__">
|
||||
@@ -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__">
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
<p class="email-footer-text"><span class="framework-extra">© </span><a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a></p>
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="framework-extra">
|
||||
{{if eq .Variant "terminal"}}<span class="email-brand">> {{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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -239,6 +239,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)">
|
||||
@@ -323,10 +328,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 +347,31 @@ function transform(html, mode) {
|
||||
const e = html.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
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.setAttribute('sandbox', '');
|
||||
|
||||
frame.style.display = 'none';
|
||||
loading.style.display = 'flex';
|
||||
@@ -363,14 +383,15 @@ function render() {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -391,10 +412,11 @@ function toggleSection(id) {
|
||||
// Panel content updates
|
||||
//
|
||||
function updatePanel() {
|
||||
const escape = v => String(v).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
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 +429,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,7 +444,7 @@ 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) {
|
||||
state.viewport=v;
|
||||
@@ -443,7 +465,7 @@ document.addEventListener('keydown',e=>{
|
||||
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 +482,85 @@ 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);
|
||||
}
|
||||
state.template = Object.keys(REGISTRY)[0] || '';
|
||||
} else {
|
||||
document.getElementById('loading').style.display='none';
|
||||
showMessage('rendered.js not found — run <code>cd tools && go run . preview all</code> to generate', 'error');
|
||||
// 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);
|
||||
}
|
||||
select.value=selected;
|
||||
}
|
||||
|
||||
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);
|
||||
let names; try { names=new Intl.DisplayNames([navigator.language],{type:'language'}); } catch (_) {}
|
||||
fillSelect('sel-locale',data.languages.map(l=>[l,names ? l+' — '+names.of(l) : l]),state.locale);
|
||||
}
|
||||
|
||||
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 && go run . preview all</code> to generate','error');
|
||||
}
|
||||
render();
|
||||
})();
|
||||
|
||||
// Message helpers
|
||||
@@ -512,7 +581,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 +589,7 @@ function setDevMode(on) {
|
||||
|
||||
// Dev-server live-reload (SSE)
|
||||
(function(){
|
||||
if (location.protocol === 'file:') return;
|
||||
let es;
|
||||
try {
|
||||
es = new EventSource('/events');
|
||||
@@ -533,14 +603,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(_) {}
|
||||
};
|
||||
|
||||
@@ -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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<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>
|
||||
{{.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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}}
|
||||
{{.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
/* 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,.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 { 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%; } }
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "aurora",
|
||||
"description": "Ethereal / Dreamlike",
|
||||
"mode": "framed",
|
||||
"layout": "aurora"
|
||||
}
|
||||
@@ -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">
|
||||
©
|
||||
<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}} — {{.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}} — {{$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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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}} {{.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> — {{.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
/* 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,.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%; } }
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "bloom",
|
||||
"description": "Creative / Startup",
|
||||
"mode": "framed",
|
||||
"layout": "bloom"
|
||||
}
|
||||
@@ -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;">👋</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">
|
||||
©
|
||||
<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;">👋</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}} — {{.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}} — {{$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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;">👋</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">
|
||||
©
|
||||
<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;">👋</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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;">👋</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> — {{.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
{{if .CanReply}} {{.locale.Tr "mail.reply"}}{{end}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;">👋</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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>
|
||||
{{.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;">👋</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;">
|
||||
{{$url := HTMLFormat "<a href='%[1]s' style='color:#ea580c;text-decoration:none;font-weight:600;'>%[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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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.activate_account.title" (.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;">👋</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.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,98 +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:#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 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;">👋</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" (.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">
|
||||
©
|
||||
<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>
|
||||
{{.locale.Tr "mail.register_notify.title" (.DisplayName|DotEscape) AppName}}
|
||||
</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;">👋</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" (.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:15px;line-height:1.6;color:#92400e;">
|
||||
{{.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,98 +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:#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 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;">👋</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" (.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">
|
||||
©
|
||||
<a href="{{AppUrl}}" class="email-footer-link">{{AppName}}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
/* 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,.card-body .email-btn,.card-body .email-btn:link,.card-body .email-btn:visited { display:inline-block;padding:14px 40px;font-size:16px;font-weight:600;color:#ffffff!important;text-decoration:none!important;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 }
|
||||
|
||||
/* Official-snapshot presentation adapter: no content or translation overrides. */
|
||||
body { margin:0; padding:24px 16px; background-color:#fff7ed; }
|
||||
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 #ea580c; }
|
||||
|
||||
|
||||
.mail-footer { margin:0; }
|
||||
.mail-footer td { border-top-color:transparent!important; color:#78350f!important; }
|
||||
.mail-footer a { color:#ea580c!important; text-decoration:none; }
|
||||
.email-body { padding:32px 32px 24px; color:#78350f; }
|
||||
.ember-sun { float:right; width:30px; height:30px; border:1px solid #fed7aa; border-radius:50%; background:#ffedd5; }
|
||||
.ember-sun span { display:block; width:14px; height:14px; margin:7px; background:#fb923c; border-radius:50%; }
|
||||
@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%; } }
|
||||