{% extends "base.html" %}
{% from "_macros.html" import modal %}
{% block extra_head %}
<link rel="stylesheet" href="{{ static_url('/static/css/projects.css') }}">
<link rel="stylesheet" href="{{ static_url('/static/css/post.css') }}">
<link rel="stylesheet" href="{{ static_url('/static/css/feed.css') }}">
{% endblock %}
{% block content %}
{% set project_url = "/projects/" ~ (project['slug'] or project['uid']) %}
{% set image_attachments = attachments | selectattr('is_image') | list %}
{% set other_attachments = attachments | rejectattr('is_image') | list %}
{% set cover_src = ('/static/uploads/' ~ project['cover_image']) if project.get('cover_image') else (image_attachments[0]['url'] if image_attachments else none) %}
{% set logo_src = ('/static/uploads/' ~ project['logo_image']) if project.get('logo_image') else none %}
{% set tagline = (project.get('description', '') or '').split('\n')[0] %}
<div class="project-page">
<a href="/projects" class="back-link">&larr; Back to Projects</a>
<article class="project-hero">
<div class="project-cover{% if not cover_src %} project-cover-fallback{% endif %}">
{% if cover_src %}
<img class="project-cover-img" src="{{ cover_src }}" alt="{{ project['title'] }} cover image" loading="eager">
{% endif %}
<div class="project-cover-scrim" aria-hidden="true"></div>
<div class="project-hero-overlay">
{% if logo_src %}
<img class="project-logo" src="{{ logo_src }}" alt="{{ project['title'] }} logo" loading="eager">
{% endif %}
<div class="project-hero-headline">
<div class="project-detail-header">
<h1 class="project-detail-title">{{ render_title(project['title'], author_is_admin=is_admin(author)) }}</h1>
<div class="project-status {% if project.get('status') == 'Released' %}released{% else %}dev{% endif %}">
&#x25CF; {{ project.get('status', 'In Development') }}
</div>
</div>
{% if tagline %}
<p class="project-tagline">{{ render_title(tagline, author_is_admin=is_admin(author)) }}</p>
{% endif %}
<div class="project-detail-meta project-hero-chips">
<span class="badge badge-type">{{ project.get('project_type', 'software').replace('_', ' ') }}</span>
{% for plat in platforms %}
<span class="platform-tag">{{ plat.strip() }}</span>
{% endfor %}
{% if is_private %}<span class="badge badge-type">Private</span>{% endif %}
{% if read_only %}<span class="badge badge-type">Read-only</span>{% endif %}
</div>
<div class="project-detail-author">
{% set _size = 32 %}{% set _size_class = "sm" %}{% set _user = author %}{% include "_avatar_link.html" %}
<div>
{% set _user = author %}{% set _class = none %}{% include "_user_link.html" %}
<span class="meta-muted">&middot; Level {{ author.get('level', 1) if author else 1 }}</span>
</div>
{% if project.get('created_at') %}
<span class="meta-muted">&#x1F331; Started {{ dt_ago(project['created_at']) }}</span>
{% endif %}
</div>
</div>
{% if project.get('website_url') %}
<div class="project-hero-ctas">
<a href="{{ project['website_url'] }}" target="_blank" rel="noopener nofollow" class="btn btn-primary project-website-btn"><span class="icon">&#x1F310;</span> Visit Website</a>
</div>
{% endif %}
</div>
</div>
<div class="project-hero-body">
{% if project.get('release_date') or project.get('demo_date') or forked_from %}
<div class="project-detail-meta">
{% if project.get('release_date') %}
<span>&#x1F4C5; Released: {{ format_date(project['release_date']) }}</span>
{% endif %}
{% if project.get('demo_date') %}
<span>&#x1F3AD; Demo: {{ format_date(project['demo_date']) }}</span>
{% endif %}
{% if forked_from %}
<span>&#x2442; Forked from <a href="/projects/{{ forked_from['slug'] }}">{{ render_title(forked_from['title']) }}</a></span>
{% endif %}
</div>
{% endif %}
<div class="project-detail-actions">
<a href="{{ project_url }}/files" class="project-star-btn"><span class="icon">&#x1F4C1;</span><span class="label"> Files ({{ file_count }} files)</span></a>
{% if workspace_editor_url %}
<a href="{{ workspace_editor_url }}" target="_blank" rel="noopener" class="project-star-btn"><span class="icon">&#x1F4BB;</span><span class="label"> VS Code</span></a>
{% endif %}
<button type="button" class="project-star-btn" data-share="{{ project_url }}"><span class="icon">&#x1F517;</span><span class="label"> Share</span></button>
{% if user %}
{% set _type = "project" %}{% set _uid = project['uid'] %}{% set _my_vote = my_vote %}{% set _count = star_count %}{% set _btn_class = "project-star-btn" %}{% include "_star_vote.html" %}
{% endif %}
{% set _type = "project" %}{% set _uid = project['uid'] %}{% set _bookmarked = bookmarked %}{% include "_bookmark_button.html" %}
{% set _type = "project" %}{% set _uid = project['uid'] %}{% set _reactions = reactions %}{% include "_reaction_bar.html" %}
{% set _type = "project" %}{% set _uid = project['uid'] %}{% set _owner = project['user_uid'] %}{% set _owner_name = (author or {}).get('username', '') %}{% set _class = "project-star-btn" %}{% include "_report_button.html" %}
<button type="button" class="project-star-btn project-actions-more" aria-haspopup="menu" aria-expanded="false" aria-label="More actions"><span class="icon">&#x22EF;</span><span class="label"> More</span></button>
<div class="project-actions-overflow" hidden>
{% if viewer_can_workspace %}
<a href="{{ project_url }}/workspace" data-menu-action data-menu-icon="&#x1F4BB;" data-menu-label="Workspace">Workspace</a>
{% endif %}
{% if viewer_can_containers %}
<a href="{{ project_url }}/containers" data-menu-action data-menu-icon="&#x1F5A5;&#xFE0F;" data-menu-label="Containers">Containers</a>
{% endif %}
<button type="button" data-zip-download="{{ project_url }}/zip" data-menu-action data-menu-icon="&#x1F4E6;" data-menu-label="Download zip">Download zip</button>
{% if user %}
<button type="button" data-fork-project="{{ project_url }}/fork" data-fork-name="{{ project['title'] }}" data-menu-action data-menu-icon="&#x2442;" data-menu-label="Fork">Fork</button>
{% endif %}
{% if is_owner %}
<button type="button" data-modal="edit-project-modal" data-menu-action data-menu-icon="&#x270F;&#xFE0F;" data-menu-label="Edit">Edit</button>
<form method="POST" action="{{ project_url }}/private">
<input type="hidden" name="value" value="{{ 0 if is_private else 1 }}">
<button type="submit" data-confirm-danger data-confirm="{% if is_private %}Make this project public? Everyone will be able to see the project and all its files.{% else %}Make this project private? Only you and administrators will be able to see it.{% endif %}" data-menu-action data-menu-icon="{% if is_private %}&#x1F513;{% else %}&#x1F512;{% endif %}" data-menu-label="{% if is_private %}Make public{% else %}Make private{% endif %}">{% if is_private %}Make public{% else %}Make private{% endif %}</button>
</form>
<form method="POST" action="{{ project_url }}/readonly">
<input type="hidden" name="value" value="{{ 0 if read_only else 1 }}">
<button type="submit" data-confirm-danger data-confirm="{% if read_only %}Allow file changes again for this project?{% else %}Make this project read-only? Files become immutable until you turn this off.{% endif %}" data-menu-action data-menu-icon="{% if read_only %}&#x1F4DD;{% else %}&#x1F6AB;{% endif %}" data-menu-label="{% if read_only %}Allow edits{% else %}Make read-only{% endif %}">{% if read_only %}Allow edits{% else %}Make read-only{% endif %}</button>
</form>
{% endif %}
{% if is_owner or is_admin(user) %}
<form method="POST" action="/projects/delete/{{ project['slug'] or project['uid'] }}">
<button type="submit" data-confirm-danger data-confirm="Delete this project?" data-menu-action data-menu-icon="&#x1F5D1;&#xFE0F;" data-menu-label="Delete">Delete</button>
</form>
{% endif %}
</div>
</div>
</div>
</article>
<nav class="project-tabs" aria-label="Project sections">
<a href="#about" class="project-tab active">Overview</a>
<a href="#devlog" class="project-tab">Devlog <span class="project-tab-count">{{ devlog_count }}</span></a>
{% if image_attachments %}
<a href="#screenshots" class="project-tab">Screenshots <span class="project-tab-count">{{ image_attachments | length }}</span></a>
{% endif %}
<a href="#comments" class="project-tab">Comments <span class="project-tab-count">{{ comment_count }}</span></a>
<a href="{{ project_url }}/files" class="project-tab">Files <span class="project-tab-count">{{ file_count }}</span></a>
</nav>
<div class="project-columns">
<div class="project-main">
<section class="project-about" id="about">
<h2 class="project-section-label">About</h2>
{% if maturity_hidden(maturity, user) %}
{% set _level = maturity %}{% include "_maturity_gate.html" %}
{% else %}
<div class="project-detail-desc rendered-content">{{ render_content(project.get('description', ''), author_is_admin=is_admin(author)) }}</div>
{% endif %}
{% if other_attachments %}
{% set attachments = other_attachments %}
{% include "_attachment_display.html" %}
{% endif %}
</section>
<section class="project-devlog" id="devlog">
<div class="project-devlog-header">
<h2 class="project-section-label">Devlog</h2>
<span class="project-devlog-count">{{ devlog_count }} update{{ '' if devlog_count == 1 else 's' }}</span>
{% if is_owner %}
<button type="button" class="btn btn-primary btn-sm project-devlog-post-btn" data-modal="create-post-modal"><span class="icon">&#x270D;&#xFE0F;</span> Post update</button>
{% endif %}
</div>
{% if devlog_posts %}
{% for item in devlog_posts %}
{% set _author = item.author %}{% set _time = item.time_ago %}{% set _show_share = false %}{% set _show_comment_form = false %}{% include "_post_card.html" %}
{% endfor %}
{% set next_cursor = devlog_next_cursor %}{% include "_load_more.html" %}
{% else %}
<p class="empty-state">No devlog posts yet.{% if is_owner %} Share your first update to give this project a public build log.{% endif %}</p>
{% endif %}
</section>
{% if image_attachments %}
<section class="project-screenshots" id="screenshots">
<h2 class="project-section-label">Screenshots</h2>
<div class="project-screenshot-grid">
{% for shot in image_attachments %}
<img src="{{ shot['thumbnail_url'] or shot['url'] }}" data-lightbox data-full="{{ shot['url'] }}" alt="{{ project['title'] }} screenshot {{ loop.index }}" loading="lazy" class="project-screenshot">
{% endfor %}
</div>
</section>
{% endif %}
<section class="project-comments" id="comments">
{% with target_uid=project['uid'], target_type="project" %}
{% include "_comment_section.html" %}
{% endwith %}
</section>
</div>
<aside class="project-sidebar">
<div class="project-sidebar-card">
<h2 class="project-section-label">Links</h2>
<ul class="project-link-list">
{% if project.get('website_url') %}
<li><a href="{{ project['website_url'] }}" target="_blank" rel="noopener nofollow">&#x1F310; Website</a></li>
{% endif %}
{% if project.get('repo_url') %}
<li><a href="{{ project['repo_url'] }}" target="_blank" rel="noopener nofollow">&#x1F4BB; Repository</a></li>
{% endif %}
<li><a href="{{ project_url }}/files">&#x1F4C1; Browse files</a></li>
{% if forked_from %}
<li><a href="/projects/{{ forked_from['slug'] }}">&#x2442; Fork source: {{ render_title(forked_from['title']) }}</a></li>
{% endif %}
</ul>
</div>
<div class="project-sidebar-card">
<h2 class="project-section-label">Stats</h2>
<div class="project-stats">
<span class="project-stat"><span class="project-stat-value">{{ star_count }}</span> stars</span>
<span class="project-stat"><a href="#devlog"><span class="project-stat-value">{{ devlog_count }}</span> update{{ '' if devlog_count == 1 else 's' }}</a></span>
<span class="project-stat"><a href="#comments"><span class="project-stat-value">{{ comment_count }}</span> comment{{ '' if comment_count == 1 else 's' }}</a></span>
<span class="project-stat"><a href="{{ project_url }}/files"><span class="project-stat-value">{{ file_count }}</span> file{{ '' if file_count == 1 else 's' }}</a></span>
<span class="project-stat"><span class="project-stat-value">{{ fork_count }}</span> fork{{ '' if fork_count == 1 else 's' }}</span>
</div>
{% if devlog_posts %}
<div class="project-last-update">Last update {{ dt_ago(devlog_posts[0].post['created_at']) }}</div>
{% endif %}
</div>
<div class="project-sidebar-card">
<h2 class="project-section-label">Author</h2>
<div class="project-author-card">
{% set _size = 40 %}{% set _size_class = "md" %}{% set _user = author %}{% include "_avatar_link.html" %}
<div class="project-author-meta">
{% set _user = author %}{% set _class = none %}{% include "_user_link.html" %}
<span class="meta-muted">Level {{ author.get('level', 1) if author else 1 }} &middot; {{ author.get('stars', 0) if author else 0 }} stars</span>
</div>
</div>
</div>
</aside>
</div>
{% if is_owner %}
{% call modal('create-post-modal', 'Post an update') %}
{% set _composer_topic = 'devlog' %}{% set _composer_project = project['uid'] %}{% include "_post_composer_form.html" %}
{% endcall %}
{% call modal('edit-project-modal', 'Edit Project') %}
<form method="POST" action="/projects/edit/{{ project['slug'] or project['uid'] }}" enctype="multipart/form-data">
<div class="auth-field auth-field-gap">
<label for="edit-project-title">Title</label>
<input type="text" id="edit-project-title" name="title" required maxlength="200" value="{{ project['title'] }}">
</div>
<div class="auth-field auth-field-gap">
<label for="edit-project-description">Description</label>
<textarea id="edit-project-description" name="description" required maxlength="5000" class="min-h-100" data-mention>{{ project.get('description', '') }}</textarea>
</div>
<div class="grid-2col">
<div class="auth-field">
<label for="edit-project-release_date">Release Date</label>
<input type="text" id="edit-project-release_date" name="release_date" placeholder="DD/MM/YYYY" pattern="\d{2}/\d{2}/\d{4}" inputmode="numeric" autocomplete="off" value="{{ format_date(project.get('release_date', '')) }}">
</div>
<div class="auth-field">
<label for="edit-project-demo_date">Demo Date</label>
<input type="text" id="edit-project-demo_date" name="demo_date" placeholder="DD/MM/YYYY" pattern="\d{2}/\d{2}/\d{4}" inputmode="numeric" autocomplete="off" value="{{ format_date(project.get('demo_date', '')) }}">
</div>
</div>
<div class="grid-2col">
<div class="auth-field">
<label for="edit-project-website_url">Website (optional)</label>
<input type="url" id="edit-project-website_url" name="website_url" maxlength="500" placeholder="https://myproject.dev" value="{{ project.get('website_url', '') or '' }}">
</div>
<div class="auth-field">
<label for="edit-project-repo_url">Repository (optional)</label>
<input type="url" id="edit-project-repo_url" name="repo_url" maxlength="500" placeholder="https://github.com/me/project" value="{{ project.get('repo_url', '') or '' }}">
</div>
</div>
<div class="grid-2col">
<div class="auth-field">
<label for="edit-project-cover_image">Cover image (optional{% if project.get('cover_image') %}, replaces current{% endif %})</label>
<input type="file" id="edit-project-cover_image" name="cover_image" accept="image/*">
</div>
<div class="auth-field">
<label for="edit-project-logo_image">Project logo (optional{% if project.get('logo_image') %}, replaces current{% endif %})</label>
<input type="file" id="edit-project-logo_image" name="logo_image" accept="image/*">
</div>
</div>
<div class="auth-field auth-field-gap">
<label>Type</label>
<div class="flex-wrap-gap" role="group" aria-label="Type">
{% for t in [('game', 'Game'), ('game_asset', 'Game Asset'), ('software', 'Software'), ('mobile_app', 'Mobile App'), ('website', 'Website')] %}
<label class="topic-label">
<input type="radio" name="project_type" value="{{ t[0] }}" {% if t[0] == project.get('project_type', 'software') %}checked{% endif %} class="topic-radio">
{{ t[1] }}
</label>
{% endfor %}
</div>
</div>
<div class="auth-field auth-field-gap">
<label>Status</label>
<div class="flex-wrap-gap" role="group" aria-label="Status">
<label class="topic-label">
<input type="radio" name="status" value="In Development" {% if project.get('status', 'In Development') != 'Released' %}checked{% endif %} class="topic-radio"> In Development
</label>
<label class="topic-label">
<input type="radio" name="status" value="Released" {% if project.get('status') == 'Released' %}checked{% endif %} class="topic-radio"> Released
</label>
</div>
</div>
<div class="auth-field auth-field-gap-last">
<label>Platforms</label>
<div id="platforms-tags" class="flex-wrap-gap-bottom"></div>
<input type="text" id="platforms-input" class="input-sm" placeholder="Add platform (press Enter)" aria-label="Add platform">
<input type="hidden" id="platforms" name="platforms" value="{{ project.get('platforms', '') }}">
<div class="platform-presets-wrap">
{% for p in ['PC', 'Windows', 'Mac', 'Linux', 'iOS', 'Android', 'Web', 'Console'] %}
<button type="button" class="platform-preset" data-platform="{{ p }}">{{ p }}</button>
{% endfor %}
</div>
</div>
<div class="modal-footer">
<button type="button" class="modal-close btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-primary"><span class="icon">&#x1F4BE;</span>Save Changes</button>
</div>
</form>
{% endcall %}
{% endif %}
</div>
{% endblock %}
{% block extra_js %}
<script type="module">
import { ProjectActionsMenu } from "{{ static_url('/static/js/ProjectActionsMenu.js') }}";
const actions = document.querySelector(".project-detail-actions");
if (actions) {
new ProjectActionsMenu(actions);
}
</script>
{% endblock %}