intent/templates/comments/_thread.html
Alec Perkins f6b7374be7 Add feature files, image pins and implementations (Phase 4)
Files: prototype and documentation files on features, with versions.
Adding, replacing or removing a file is a feature revision with a
change note. Types come from the extension; uploads are capped at
25 MB and stored under opaque paths. Every file URL checks project
access, then redirects to a short-lived signed URL on the bucket, or
streams with nosniff and a CSP sandbox when no bucket is configured.
HTML prototypes run in a sandboxed iframe without same-origin access.

Threads: whole-file threads and pins at a point on an image, drawn as
an SVG overlay in percentages. Pins are placed with number fields, or
by clicking the image with JS.

Implementations: name, link and status, plus a per-feature status grid
stamped with the feature revision, marked behind when the feature
changes, and confirmable without changing the status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:15:30 -04:00

68 lines
3.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% comment %}
A feature or line thread. Needs `thread` (prepared), `project`, `access`, `focus`, `error`.
Resolved threads start collapsed. This whole element is what HTMX swaps.
{% endcomment %}
<div id="{{ thread.dom_id }}" class="thread" tabindex="-1"{% if focus == thread.dom_id %} data-autofocus{% endif %}>
{% if error %}<p role="alert"><strong>Error:</strong> {{ error }}</p>{% endif %}
<details{% if not thread.is_resolved or error or focus %} open{% endif %}>
<summary>
{% if thread.pin_number %}Pin {{ thread.pin_number }}: {% endif %}Thread by {{ thread.author.username|default:"deleted user" }},
{{ thread.live_count }} comment{{ thread.live_count|pluralize }}{% if thread.promoted_to %}, became issue {{ thread.promoted_to.key }}{% elif thread.is_resolved %}, resolved{% endif %}
</summary>
{% if thread.kind == "line" and thread.outdated %}
<p class="muted">
On lines {{ thread.line_start }}–{{ thread.line_end }} of
<a href="{% url 'features:revision' org_slug=project.org.slug project_slug=project.slug feature_key=thread.feature.key revision=thread.revision %}#L{{ thread.line_start }}-{{ thread.line_end }}">revision {{ thread.revision }}</a>,
which have changed since:
</p>
<pre class="quote">{{ thread.quoted }}</pre>
{% endif %}
{% if thread.kind == "image" or thread.kind == "file" %}
<p class="muted">
{% if thread.kind == "image" %}Pinned at {{ thread.x_pct }}% across, {{ thread.y_pct }}% down{% else %}About the whole file{% endif %}{% if thread.earlier %},
on <a href="{% url 'files:detail' org_slug=project.org.slug project_slug=project.slug feature_key=thread.feature.key file_id=thread.file_version.file_id %}?version={{ thread.file_version.version }}">version {{ thread.file_version.version }}</a>{% endif %}.
</p>
{% endif %}
{% if thread.promoted_to %}
<p>Continued in issue <a href="{% url 'issues:detail' org_slug=project.org.slug project_slug=project.slug number=thread.promoted_to.number %}">{{ thread.promoted_to.key }} {{ thread.promoted_to.title }}</a>.</p>
{% endif %}
{% for c in thread.comment_list %}
{% include "comments/_comment.html" %}
{% endfor %}
{% if access.can_contribute %}
{% url 'comments:reply' org_slug=project.org.slug project_slug=project.slug thread_id=thread.pk as reply_url %}
<form method="post" action="{{ reply_url }}" hx-post="{{ reply_url }}" hx-target="#{{ thread.dom_id }}" hx-swap="outerHTML">
{% csrf_token %}
<label for="reply-{{ thread.pk }}">Reply</label>
<textarea id="reply-{{ thread.pk }}" name="body" rows="3" required>{{ thread.draft }}</textarea>
<button type="submit">Reply</button>
</form>
{% endif %}
{% if thread.can_resolve or thread.can_promote %}
<div class="actions">
{% if thread.can_resolve %}
{% url 'comments:resolve' org_slug=project.org.slug project_slug=project.slug thread_id=thread.pk as resolve_url %}
<form method="post" action="{{ resolve_url }}" hx-post="{{ resolve_url }}" hx-target="#{{ thread.dom_id }}" hx-swap="outerHTML">
{% csrf_token %}
{% if thread.is_resolved %}
<input type="hidden" name="resolved" value="0">
<button type="submit">Reopen thread</button>
{% else %}
<input type="hidden" name="resolved" value="1">
<button type="submit">Resolve thread</button>
{% endif %}
</form>
{% endif %}
{% if thread.can_promote %}
<a href="{% url 'comments:promote' org_slug=project.org.slug project_slug=project.slug thread_id=thread.pk %}">Turn into an issue</a>
{% endif %}
</div>
{% endif %}
</details>
</div>