Compare commits
9 Commits
6668ba511c
...
c798b851dc
| Author | SHA1 | Date | |
|---|---|---|---|
| c798b851dc | |||
| c18d4eebc1 | |||
| 3f6e01ed65 | |||
| 19642175eb | |||
| 8ca378b25f | |||
| fad73a9fab | |||
| 627db45a56 | |||
| 976d872eed | |||
| 6c7ce24e1f |
@@ -230,6 +230,40 @@ impl<'a> ProfileTemplate<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "embed_profile.html")]
|
||||
pub struct EmbedProfileTemplate<'a> {
|
||||
pub profile_display_name: String,
|
||||
pub profile_user_id: uuid::Uuid,
|
||||
pub profile_url: String,
|
||||
pub stats: &'a UserStats,
|
||||
pub avg_rating_display: String,
|
||||
pub favorite_director_display: String,
|
||||
pub most_active_month_display: String,
|
||||
pub view: &'a str,
|
||||
pub entries: Option<&'a Paginated<DiaryEntry>>,
|
||||
pub current_offset: u32,
|
||||
pub has_more: bool,
|
||||
pub limit: u32,
|
||||
pub history: Option<&'a Vec<MonthActivity>>,
|
||||
pub trends: Option<&'a UserTrends>,
|
||||
pub monthly_rating_rows: Vec<MonthlyRatingRow<'a>>,
|
||||
pub heatmap: Vec<HeatmapCell>,
|
||||
pub page_items: Vec<PageItem>,
|
||||
pub sort_by: String,
|
||||
}
|
||||
|
||||
impl<'a> EmbedProfileTemplate<'a> {
|
||||
pub fn filter_qs(&self) -> String {
|
||||
let parts = [
|
||||
format!("view={}", self.view),
|
||||
format!("sort_by={}", self.sort_by),
|
||||
"embed=1".to_string(),
|
||||
];
|
||||
format!("&{}", parts.join("&"))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemoteActorData {
|
||||
pub handle: String,
|
||||
pub display_name: Option<String>,
|
||||
@@ -345,6 +379,7 @@ pub struct ProfileSettingsTemplate<'a> {
|
||||
pub also_known_as: Option<&'a str>,
|
||||
pub profile_fields: &'a [(String, String)],
|
||||
pub saved: bool,
|
||||
pub embed_url: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
|
||||
17
crates/adapters/template-askama/templates/embed_base.html
Normal file
17
crates/adapters/template-askama/templates/embed_base.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="/static/embed.css" />
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
189
crates/adapters/template-askama/templates/embed_profile.html
Normal file
189
crates/adapters/template-askama/templates/embed_profile.html
Normal file
@@ -0,0 +1,189 @@
|
||||
{% extends "embed_base.html" %}
|
||||
{% block content %}
|
||||
<div class="embed-profile">
|
||||
|
||||
<div class="embed-stats-header">
|
||||
<div class="embed-profile-name">{{ profile_display_name }}</div>
|
||||
<div class="embed-stats-grid">
|
||||
<div class="embed-stat-tile">
|
||||
<div class="embed-stat-value">{{ stats.total_movies }}</div>
|
||||
<div class="embed-stat-label">movies</div>
|
||||
</div>
|
||||
<div class="embed-stat-tile">
|
||||
<div class="embed-stat-value">{{ avg_rating_display }}★</div>
|
||||
<div class="embed-stat-label">avg rating</div>
|
||||
</div>
|
||||
<div class="embed-stat-tile">
|
||||
<div class="embed-stat-value">{{ favorite_director_display }}</div>
|
||||
<div class="embed-stat-label">fav director</div>
|
||||
</div>
|
||||
<div class="embed-stat-tile">
|
||||
<div class="embed-stat-value">{{ most_active_month_display }}</div>
|
||||
<div class="embed-stat-label">most active</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="embed-view-tabs">
|
||||
<a href="?view=recent&embed=1" class="embed-view-tab {% if view == "recent" %}active{% endif %}">Recent</a>
|
||||
<a href="?view=ratings&embed=1" class="embed-view-tab {% if view == "ratings" %}active{% endif %}">Top Rated</a>
|
||||
<a href="?view=history&embed=1" class="embed-view-tab {% if view == "history" %}active{% endif %}">History</a>
|
||||
<a href="?view=trends&embed=1" class="embed-view-tab {% if view == "trends" %}active{% endif %}">Trends</a>
|
||||
</div>
|
||||
|
||||
{% if view == "recent" || view == "ratings" %}
|
||||
<form method="get" class="embed-sort-form" action="/users/{{ profile_user_id }}">
|
||||
<input type="hidden" name="view" value="{{ view }}">
|
||||
<input type="hidden" name="limit" value="{{ limit }}">
|
||||
<input type="hidden" name="embed" value="1">
|
||||
<div class="embed-sort-controls">
|
||||
<select name="sort_by" onchange="this.form.submit()">
|
||||
<option value="date"{% if sort_by == "date" %} selected{% endif %}>Date: newest first</option>
|
||||
<option value="date_asc"{% if sort_by == "date_asc" %} selected{% endif %}>Date: oldest first</option>
|
||||
<option value="rating"{% if sort_by == "rating" %} selected{% endif %}>Rating: highest first</option>
|
||||
<option value="rating_asc"{% if sort_by == "rating_asc" %} selected{% endif %}>Rating: lowest first</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% if view == "history" %}
|
||||
{% if let Some(hist) = history %}
|
||||
<div class="embed-heatmap-section">
|
||||
<div class="embed-heatmap-label">Movies watched this year</div>
|
||||
<div class="embed-heatmap">
|
||||
{% for cell in heatmap %}
|
||||
<div class="embed-heatmap-cell" style="--alpha: {{ cell.alpha }}">
|
||||
<div class="embed-heatmap-count">{{ cell.count }}</div>
|
||||
<div class="embed-heatmap-month">{{ cell.month_label }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% for month in hist %}
|
||||
<div class="embed-history-month">
|
||||
<h3 class="embed-month-heading">{{ month.month_label }} <span class="embed-month-count">{{ month.count }}</span></h3>
|
||||
<div class="embed-diary">
|
||||
{% for entry in month.entries %}
|
||||
<article class="embed-entry">
|
||||
{% if let Some(poster) = entry.movie().poster_path() %}
|
||||
<div class="embed-poster"><img src="{{ poster.value()|poster_src }}" alt="" loading="lazy"></div>
|
||||
{% endif %}
|
||||
<div class="embed-entry-body">
|
||||
<div class="embed-entry-title"><a href="/movies/{{ entry.movie().id().value() }}" class="embed-movie-link" target="_blank" rel="noopener">{{ entry.movie().title().value() }}</a> <span class="embed-year">({{ entry.movie().release_year().value() }})</span></div>
|
||||
{% if let Some(dir) = entry.movie().director() %}<div class="embed-director">{{ dir }}</div>{% endif %}
|
||||
<div class="embed-rating">
|
||||
{% for filled in entry.review().stars() %}
|
||||
<span class="embed-star {% if filled %}filled{% else %}empty{% endif %}">★</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="embed-watched-at">{{ entry.review().watched_at().format("%b %-d") }}</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="embed-empty">No movies logged yet.</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% elif view == "trends" %}
|
||||
{% if let Some(t) = trends %}
|
||||
<div class="embed-trends-section">
|
||||
{% if !monthly_rating_rows.is_empty() %}
|
||||
<div class="embed-chart-block">
|
||||
<div class="embed-chart-label">Average rating per month</div>
|
||||
<div class="embed-bar-chart">
|
||||
{% for row in monthly_rating_rows %}
|
||||
<div class="embed-bar-col">
|
||||
<div class="embed-bar-value">{{ "{:.1}"|format(row.rating.avg_rating) }}</div>
|
||||
<div class="embed-bar-fill" style="height: {{ row.bar_height_px }}px"></div>
|
||||
<div class="embed-bar-month">{{ row.rating.month_label }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if !t.top_directors.is_empty() %}
|
||||
<div class="embed-chart-block">
|
||||
<div class="embed-chart-label">Most watched directors</div>
|
||||
<div class="embed-director-chart">
|
||||
{% for d in t.top_directors %}
|
||||
<div class="embed-director-row">
|
||||
<div class="embed-director-name">{{ d.director }}</div>
|
||||
<div class="embed-director-bar">
|
||||
{% if t.max_director_count > 0 %}
|
||||
<div class="embed-director-bar-fill" style="width: {{ d.count * 100 / t.max_director_count }}%"></div>
|
||||
{% else %}
|
||||
<div class="embed-director-bar-fill" style="width: 0%"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="embed-director-count">{{ d.count }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if let Some(paged) = entries %}
|
||||
<div class="embed-diary">
|
||||
{% for entry in paged.items %}
|
||||
<article class="embed-entry">
|
||||
{% if let Some(poster) = entry.movie().poster_path() %}
|
||||
<div class="embed-poster">
|
||||
<img src="{{ poster.value()|poster_src }}" alt="" loading="lazy">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="embed-entry-body">
|
||||
<div class="embed-entry-title">
|
||||
<a href="/movies/{{ entry.movie().id().value() }}" class="embed-movie-link" target="_blank" rel="noopener">{{ entry.movie().title().value() }}</a>
|
||||
<span class="embed-year">({{ entry.movie().release_year().value() }})</span>
|
||||
</div>
|
||||
{% if let Some(dir) = entry.movie().director() %}
|
||||
<div class="embed-director">{{ dir }}</div>
|
||||
{% endif %}
|
||||
<div class="embed-rating">
|
||||
{% for filled in entry.review().stars() %}
|
||||
<span class="embed-star {% if filled %}filled{% else %}empty{% endif %}">★</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if let Some(comment) = entry.review().comment() %}
|
||||
<div class="embed-comment">{{ comment.value() }}</div>
|
||||
{% endif %}
|
||||
<div class="embed-watched-at">{{ entry.review().watched_at().format("%Y-%m-%d") }}</div>
|
||||
</div>
|
||||
</article>
|
||||
{% else %}
|
||||
<p class="embed-empty">No reviews yet.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<nav class="embed-pagination">
|
||||
{% if current_offset >= limit %}
|
||||
<a href="/users/{{ profile_user_id }}?offset={{ current_offset - limit }}{{ self.filter_qs() }}" class="embed-page-nav">← Prev</a>
|
||||
{% endif %}
|
||||
{% for item in page_items %}
|
||||
{% if item.is_ellipsis %}
|
||||
<span class="embed-page-ellipsis">…</span>
|
||||
{% elif item.is_current %}
|
||||
<span class="embed-page-num current">{{ item.number + 1 }}</span>
|
||||
{% else %}
|
||||
<a href="/users/{{ profile_user_id }}?offset={{ item.number * limit }}{{ self.filter_qs() }}" class="embed-page-num">{{ item.number + 1 }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if has_more %}
|
||||
<a href="/users/{{ profile_user_id }}?offset={{ current_offset + limit }}{{ self.filter_qs() }}" class="embed-page-nav">Next →</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div class="embed-footer">
|
||||
<a href="{{ profile_url }}" target="_blank" rel="noopener">powered by Movies Diary</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -56,4 +56,10 @@
|
||||
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
|
||||
<section style="margin-top:2rem;padding-top:1rem;border-top:1px solid rgba(255,255,255,0.1)">
|
||||
<h3>Embed widget</h3>
|
||||
<p style="font-size:.85em;opacity:.7;margin-bottom:.5rem">Add your diary to your blog or website:</p>
|
||||
<pre style="background:rgba(255,255,255,0.06);padding:12px;border-radius:8px;font-size:.8em;overflow-x:auto;white-space:pre-wrap;word-break:break-all"><code><iframe src="{{ embed_url }}" width="100%" height="600" style="border:none;border-radius:8px" loading="lazy"></iframe></code></pre>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@@ -158,6 +158,8 @@ pub struct ProfileQueryParams {
|
||||
pub sort_by: String,
|
||||
#[serde(default)]
|
||||
pub search: String,
|
||||
#[serde(default)]
|
||||
pub embed: bool,
|
||||
}
|
||||
|
||||
pub struct LogReviewData {
|
||||
|
||||
@@ -42,10 +42,11 @@ use application::ports::HtmlPageContext;
|
||||
use domain::models::ExportFormat;
|
||||
use domain::{errors::DomainError, value_objects::UserId};
|
||||
use template_askama::{
|
||||
ActivityFeedTemplate, IntegrationsTemplate, LoginTemplate, MonthlyRatingRow,
|
||||
MovieDetailTemplate, NewReviewTemplate, ProfileSettingsTemplate, ProfileTemplate,
|
||||
RegisterTemplate, RemoteActorData, RemoteActorDisplay, UserSummaryView, UsersTemplate,
|
||||
WatchQueueTemplate, WatchlistTemplate, bar_height_px, build_heatmap, build_page_items,
|
||||
ActivityFeedTemplate, EmbedProfileTemplate, IntegrationsTemplate, LoginTemplate,
|
||||
MonthlyRatingRow, MovieDetailTemplate, NewReviewTemplate, ProfileSettingsTemplate,
|
||||
ProfileTemplate, RegisterTemplate, RemoteActorData, RemoteActorDisplay, UserSummaryView,
|
||||
UsersTemplate, WatchQueueTemplate, WatchlistTemplate, bar_height_px, build_heatmap,
|
||||
build_page_items,
|
||||
};
|
||||
#[cfg(feature = "federation")]
|
||||
use template_askama::{
|
||||
@@ -613,6 +614,35 @@ pub async fn get_user_profile(
|
||||
.iter()
|
||||
.map(crate::mappers::users::pending_follower_data)
|
||||
.collect();
|
||||
if params.embed {
|
||||
let profile_url = format!(
|
||||
"{}/users/{}",
|
||||
state.app_ctx.config.base_url, profile_user_uuid
|
||||
);
|
||||
let response = render_page(EmbedProfileTemplate {
|
||||
profile_display_name: display_name,
|
||||
profile_user_id: profile_user_uuid,
|
||||
profile_url,
|
||||
stats: &profile.stats,
|
||||
avg_rating_display,
|
||||
favorite_director_display,
|
||||
most_active_month_display,
|
||||
view: profile_view.as_str(),
|
||||
entries: profile.entries.as_ref(),
|
||||
current_offset: offset,
|
||||
has_more,
|
||||
limit,
|
||||
history: profile.history.as_ref(),
|
||||
trends: profile.trends.as_ref(),
|
||||
monthly_rating_rows,
|
||||
heatmap,
|
||||
page_items,
|
||||
sort_by: sort_by_str.to_string(),
|
||||
});
|
||||
let mut resp = response.into_response();
|
||||
resp.headers_mut().remove("x-frame-options");
|
||||
resp
|
||||
} else {
|
||||
render_page(ProfileTemplate {
|
||||
ctx: &ctx,
|
||||
profile_display_name: display_name,
|
||||
@@ -641,6 +671,7 @@ pub async fn get_user_profile(
|
||||
})
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
Err(e) => crate::errors::domain_error_response(e),
|
||||
}
|
||||
}
|
||||
@@ -1221,6 +1252,11 @@ pub async fn get_profile_settings(
|
||||
also_known_as: also_known_as.as_deref(),
|
||||
profile_fields: &profile_fields,
|
||||
saved,
|
||||
embed_url: format!(
|
||||
"{}/users/{}?embed=1",
|
||||
state.app_ctx.config.base_url,
|
||||
user_id.value()
|
||||
),
|
||||
})
|
||||
.into_response()
|
||||
}
|
||||
|
||||
460
static/embed.css
Normal file
460
static/embed.css
Normal file
@@ -0,0 +1,460 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--embed-bg: #1a1a2e;
|
||||
--embed-surface: rgba(255, 255, 255, 0.06);
|
||||
--embed-border: rgba(255, 255, 255, 0.1);
|
||||
--embed-primary: oklch(85.2% 0.199 91.936);
|
||||
--embed-primary-glow: oklch(85.2% 0.199 91.936 / 0.3);
|
||||
--embed-text: #fff;
|
||||
--embed-text-muted: rgba(255, 255, 255, 0.7);
|
||||
--embed-text-light: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Nunito", sans-serif;
|
||||
background: var(--embed-bg);
|
||||
color: var(--embed-text);
|
||||
line-height: 1.5;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.embed-profile {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.embed-stats-header {
|
||||
background: var(--embed-surface);
|
||||
border-radius: 12px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.embed-profile-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.embed-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embed-stat-tile {
|
||||
background: var(--embed-surface);
|
||||
border-radius: 8px;
|
||||
padding: 8px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.embed-stat-value {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
color: var(--embed-primary);
|
||||
}
|
||||
|
||||
.embed-stat-label {
|
||||
font-size: 0.7rem;
|
||||
color: var(--embed-text-light);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.embed-view-tabs {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.embed-view-tab {
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
text-decoration: none;
|
||||
color: var(--embed-text-muted);
|
||||
background: var(--embed-surface);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.embed-view-tab:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.embed-view-tab.active {
|
||||
background: var(--embed-primary);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.embed-sort-form {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.embed-sort-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embed-sort-controls select {
|
||||
background: var(--embed-surface);
|
||||
border: 1px solid var(--embed-border);
|
||||
border-radius: 6px;
|
||||
color: inherit;
|
||||
font-size: 0.85rem;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
.embed-sort-controls select option {
|
||||
background: var(--embed-bg);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.embed-diary {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.embed-entry {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background: var(--embed-surface);
|
||||
border: 1px solid var(--embed-border);
|
||||
border-radius: 10px;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.embed-entry:hover {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.embed-poster {
|
||||
width: 50px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.embed-poster img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.embed-entry-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.embed-entry-title {
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.embed-movie-link {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.embed-movie-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.embed-year {
|
||||
color: var(--embed-text-muted);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.embed-director {
|
||||
color: var(--embed-text-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.embed-rating {
|
||||
margin-bottom: 3px;
|
||||
font-size: 1.05rem;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.embed-star.filled {
|
||||
color: var(--embed-primary);
|
||||
}
|
||||
|
||||
.embed-star.empty {
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.embed-comment {
|
||||
font-style: italic;
|
||||
color: var(--embed-text-light);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.embed-watched-at {
|
||||
font-size: 0.78rem;
|
||||
color: var(--embed-text-muted);
|
||||
}
|
||||
|
||||
.embed-empty {
|
||||
color: var(--embed-text-muted);
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.embed-pagination {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.embed-pagination a {
|
||||
text-decoration: none;
|
||||
color: var(--embed-primary);
|
||||
font-weight: 600;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
background: var(--embed-surface);
|
||||
border: 1px solid var(--embed-border);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.embed-pagination a:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.embed-page-num.current {
|
||||
background: var(--embed-primary);
|
||||
color: #fff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.embed-page-ellipsis {
|
||||
padding: 4px 6px;
|
||||
color: var(--embed-text-light);
|
||||
}
|
||||
|
||||
.embed-heatmap-section {
|
||||
background: var(--embed-surface);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.embed-heatmap-label {
|
||||
font-size: 0.78rem;
|
||||
color: var(--embed-text-light);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.embed-heatmap {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.embed-heatmap-cell {
|
||||
border-radius: 4px;
|
||||
padding: 6px 2px;
|
||||
text-align: center;
|
||||
min-height: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: oklch(85.2% 0.199 91.936 / var(--alpha, 0.05));
|
||||
}
|
||||
|
||||
.embed-heatmap-count {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.embed-heatmap-month {
|
||||
font-size: 0.6rem;
|
||||
color: var(--embed-text-light);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.embed-history-month {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.embed-month-heading {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px;
|
||||
color: var(--embed-text-muted);
|
||||
}
|
||||
|
||||
.embed-month-count {
|
||||
font-size: 0.78rem;
|
||||
color: var(--embed-text-light);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.embed-trends-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.embed-chart-block {
|
||||
background: var(--embed-surface);
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.embed-chart-label {
|
||||
font-size: 0.78rem;
|
||||
color: var(--embed-text-light);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.embed-bar-chart {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.embed-bar-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.embed-bar-value {
|
||||
font-size: 0.58rem;
|
||||
color: var(--embed-primary);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.embed-bar-fill {
|
||||
width: 100%;
|
||||
background: var(--embed-primary);
|
||||
border-radius: 2px 2px 0 0;
|
||||
min-height: 2px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.embed-bar-month {
|
||||
font-size: 0.6rem;
|
||||
color: var(--embed-text-light);
|
||||
}
|
||||
|
||||
.embed-director-chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.embed-director-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embed-director-name {
|
||||
font-size: 0.82rem;
|
||||
width: 120px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.embed-director-bar {
|
||||
flex: 1;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border-radius: 3px;
|
||||
height: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.embed-director-bar-fill {
|
||||
height: 100%;
|
||||
background: var(--embed-primary);
|
||||
border-radius: 3px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.embed-director-count {
|
||||
font-size: 0.78rem;
|
||||
color: var(--embed-text-light);
|
||||
width: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.embed-footer {
|
||||
text-align: center;
|
||||
padding: 12px 0 4px;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.embed-footer a {
|
||||
color: var(--embed-text-light);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.embed-footer a:hover {
|
||||
color: var(--embed-text-muted);
|
||||
}
|
||||
|
||||
@media (max-width: 599px) {
|
||||
.embed-profile {
|
||||
padding: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embed-stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.embed-poster {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.embed-entry {
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.embed-comment {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.embed-heatmap {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
|
||||
.embed-director-name {
|
||||
width: 90px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user