diff --git a/README.md b/README.md index 884e80a02b..72144cdeff 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ The AI-native Git platform. Self-hosted, fast, and designed for the age of AI-as - [AI Features Configuration](#ai-features-configuration) - [Authentication Sources](#authentication-sources) - [Email/SMTP Setup](#emailsmtp-setup) + - [Unsplash Integration](#unsplash-integration) - [Usage](#usage) - [Repository Operations](#repository-operations) - [Project Management](#project-management) @@ -549,6 +550,25 @@ USER = your-email@gmail.com PASSWD = your-app-password ``` +### Unsplash Integration + +Enable Unsplash image search for repository social card backgrounds (Media Kit). This allows repository admins to search and select high-quality background images directly from Unsplash. + +```ini +[unsplash] +; Enable Unsplash integration for Media Kit background images +ENABLED = true +; Unsplash API access key (get one at https://unsplash.com/developers) +ACCESS_KEY = your_unsplash_access_key +``` + +To obtain an access key: +1. Create an account at [unsplash.com/developers](https://unsplash.com/developers) +2. Create a new application +3. Copy the **Access Key** (the Secret Key is not needed) + +When enabled, repository admins can search Unsplash from **Settings > Media Kit** when using the "Background Image" social card style. Attribution is handled automatically per Unsplash API guidelines. + ## Usage ### Repository Operations diff --git a/modules/socialcard/socialcard.go b/modules/socialcard/socialcard.go index e7b0e3ae98..db981a8c34 100644 --- a/modules/socialcard/socialcard.go +++ b/modules/socialcard/socialcard.go @@ -378,9 +378,9 @@ func (r *Renderer) renderImageCard(data CardData) ([]byte, error) { fillRect(img, img.Bounds(), ThemeDark.Background) } - // Strong gradient scrim over bottom 45% - scrimStartY := h * 55 / 100 - drawGradientScrim(img, scrimStartY, h, 220) + // Strong gradient scrim over bottom 50% + scrimStartY := h * 50 / 100 + drawGradientScrim(img, scrimStartY, h, 240) // White text on scrim titleColor := color.RGBA{R: 255, G: 255, B: 255, A: 255} diff --git a/templates/base/head_opengraph.tmpl b/templates/base/head_opengraph.tmpl index 89f446424b..08f177e99a 100644 --- a/templates/base/head_opengraph.tmpl +++ b/templates/base/head_opengraph.tmpl @@ -33,8 +33,13 @@ {{end}} + {{if or (eq .Repository.SocialCardTheme "solid") (eq .Repository.SocialCardTheme "image")}} + + + {{else}} + {{end}} diff --git a/templates/repo/settings/media_kit.tmpl b/templates/repo/settings/media_kit.tmpl index a6838ff263..48b1d7667d 100644 --- a/templates/repo/settings/media_kit.tmpl +++ b/templates/repo/settings/media_kit.tmpl @@ -200,6 +200,11 @@ el.style.borderColor = 'var(--color-primary)'; el.style.opacity = '0.7'; el.style.pointerEvents = 'none'; + // Show a status message + const statusEl = document.createElement('div'); + statusEl.textContent = 'Downloading image…'; + statusEl.style.cssText = 'padding:8px;color:var(--color-text);font-size:13px;'; + resultsDiv.parentNode.insertBefore(statusEl, resultsDiv.nextSibling); try { const csrf = document.querySelector('input[name="_csrf"]').value; const fd = new FormData(); @@ -209,16 +214,15 @@ fd.append('url', photo.regular); const resp = await fetch('{{.Link}}/unsplash/select', {method: 'POST', body: fd}); if (resp.ok) { - // Switch radio to "image" and update UI - const imageRadio = document.querySelector('.style-radio[value="image"]'); - if (imageRadio) { - imageRadio.checked = true; - updateButtonStyles(); - solidOpts.style.display = 'none'; - imageOpts.style.display = ''; - } - updatePreview(); + statusEl.textContent = 'Image saved! Reloading…'; + window.location.reload(); + } else { + statusEl.textContent = 'Failed to save image. Please try again.'; + statusEl.style.color = 'var(--color-error)'; } + } catch (e) { + statusEl.textContent = 'Network error. Please try again.'; + statusEl.style.color = 'var(--color-error)'; } finally { el.style.opacity = '1'; el.style.pointerEvents = '';