878 lines
56 KiB
HTML
878 lines
56 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ idea.id }}: {{ idea.title }} - ThinkStorm{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Idea Header Banner -->
|
|
{% if idea.lifecycle_state == 'TRASHED' %}
|
|
<div class="glass-card" style="padding:1.25rem 1.5rem; margin-bottom:1.5rem; border-color:rgba(239,68,68,0.4); background:rgba(239,68,68,0.08); display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:1rem;">
|
|
<div style="display:flex; align-items:center; gap:0.75rem;">
|
|
<span style="font-size:1.5rem;">🗑️</span>
|
|
<div>
|
|
<strong style="color:#f87171; font-size:1.05rem;">This idea is in the Trash Bin</strong>
|
|
<p style="color:var(--text-secondary); font-size:0.85rem; margin:0;">It is withheld from incubation catalogs until restored or permanently emptied.</p>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex; gap:0.5rem;">
|
|
<button onclick="restoreIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="color:#34d399;">
|
|
♻️ Restore Idea
|
|
</button>
|
|
<button onclick="deleteIdeaPermanently('{{ idea.id }}')" class="btn btn-danger btn-sm">
|
|
❌ Delete Forever
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="glass-card" style="padding: 2rem; margin-bottom: 2rem; position:relative; overflow:hidden;">
|
|
<div style="display:flex; justify-content:space-between; align-items:flex-start; flex-wrap:wrap; gap:1rem; margin-bottom:1rem;">
|
|
<div>
|
|
<div style="display:flex; align-items:center; gap:0.75rem; margin-bottom:0.5rem;">
|
|
<span style="font-family:var(--font-mono); font-size:1.1rem; font-weight:700; color:var(--accent-primary);">{{ idea.id }}</span>
|
|
<span class="badge badge-{{ idea.lifecycle_state|lower }}">{{ idea.lifecycle_state }}</span>
|
|
<span class="badge" style="background:rgba(255,255,255,0.06); color:var(--text-secondary);">Proc: {{ idea.processing_state }}</span>
|
|
{% if idea.lifecycle_state != 'TRASHED' %}
|
|
<div class="maturity-meter" title="Enrichment Maturity Level {{ idea.enrichment_level }}/5">
|
|
<span>Level {{ idea.enrichment_level }}</span>
|
|
<div class="dots">
|
|
{% for i in range(1, 6) %}
|
|
<div class="dot {% if idea.enrichment_level >= i %}active{% endif %}"></div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<h1 style="font-size: 2.2rem; line-height: 1.2; margin-bottom: 0.75rem;">{{ idea.title or "Processing Submission..." }}</h1>
|
|
<p style="color: var(--text-secondary); font-size: 1.05rem; max-width: 850px;">{{ idea.summary }}</p>
|
|
</div>
|
|
|
|
<!-- Action Center -->
|
|
<div style="display:flex; flex-direction:column; gap:0.5rem; min-width:200px;">
|
|
{% if idea.lifecycle_state == 'TRASHED' %}
|
|
<button onclick="restoreIdea('{{ idea.id }}')" class="btn btn-primary" style="width:100%;">
|
|
♻️ Restore Idea
|
|
</button>
|
|
<button onclick="deleteIdeaPermanently('{{ idea.id }}')" class="btn btn-danger btn-sm" style="width:100%; margin-top:0.3rem;">
|
|
❌ Delete Forever
|
|
</button>
|
|
{% elif idea.lifecycle_state == 'AVAILABLE' %}
|
|
{% if user and user.role.value != 'ANONYMOUS' %}
|
|
<button onclick="claimIdea('{{ idea.id }}')" class="btn btn-primary" style="width:100%;">
|
|
⚡ Claim Idea
|
|
</button>
|
|
{% else %}
|
|
<a href="/login" class="btn btn-primary" style="width:100%;">
|
|
Sign in to Claim
|
|
</a>
|
|
{% endif %}
|
|
{% if idea.enrichment_level < 4 %}
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-warning btn-sm" style="margin-top:0.3rem;" title="Generate missing research documents">
|
|
⚡ Generate Research
|
|
</button>
|
|
{% else %}
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem;" title="Re-run AI research and critique">
|
|
🔄 Re-run Research
|
|
</button>
|
|
{% endif %}
|
|
<button onclick="trashIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.2rem; color:#f87171;" title="Move idea to trash">
|
|
🗑️ Move to Trash
|
|
</button>
|
|
{% elif idea.lifecycle_state == 'CLAIMED' %}
|
|
<div style="background:rgba(99,102,241,0.12); padding:0.75rem; border-radius:var(--radius-md); border:1px solid rgba(99,102,241,0.3); margin-bottom:0.5rem;">
|
|
<div style="font-size:0.8rem; color:var(--text-muted);">Claimed by</div>
|
|
<div style="font-weight:700; color:#a5b4fc;">{{ idea.claimed_by }}</div>
|
|
</div>
|
|
{% if user and (user.username == idea.claimed_by or user.role.value == 'ADMIN') %}
|
|
<button onclick="activateIdea('{{ idea.id }}')" class="btn btn-success btn-sm">
|
|
▶ Begin Work (Activate)
|
|
</button>
|
|
<button onclick="releaseIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm">
|
|
Release Claim
|
|
</button>
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem;" title="Re-run research pipeline">
|
|
🔄 Re-run Research
|
|
</button>
|
|
<button onclick="trashIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem; color:#f87171;" title="Move idea to trash">
|
|
🗑️ Move to Trash
|
|
</button>
|
|
{% endif %}
|
|
{% elif idea.lifecycle_state == 'ACTIVE' %}
|
|
<div style="background:rgba(245,158,11,0.12); padding:0.75rem; border-radius:var(--radius-md); border:1px solid rgba(245,158,11,0.3);">
|
|
<div style="font-size:0.8rem; color:var(--text-muted);">Active Work by</div>
|
|
<div style="font-weight:700; color:#fbbf24;">{{ idea.claimed_by }}</div>
|
|
</div>
|
|
{% if user and (user.username == idea.claimed_by or user.role.value == 'ADMIN') %}
|
|
<button onclick="releaseIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.4rem;">
|
|
Release Claim
|
|
</button>
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem;" title="Re-run research pipeline">
|
|
🔄 Re-run Research
|
|
</button>
|
|
<button onclick="trashIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem; color:#f87171;" title="Move idea to trash">
|
|
🗑️ Move to Trash
|
|
</button>
|
|
{% endif %}
|
|
{% elif idea.lifecycle_state == 'DUPLICATE' %}
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-warning btn-sm" style="width:100%; font-weight:700;">
|
|
⚡ Force Run Research
|
|
</button>
|
|
<button onclick="trashIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem; color:#f87171;" title="Move idea to trash">
|
|
🗑️ Move to Trash
|
|
</button>
|
|
{% else %}
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-primary btn-sm" style="width:100%; font-weight:700;">
|
|
⚡ Process Idea Pipeline
|
|
</button>
|
|
<button onclick="trashIdea('{{ idea.id }}')" class="btn btn-secondary btn-sm" style="margin-top:0.3rem; color:#f87171;" title="Move idea to trash">
|
|
🗑️ Move to Trash
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Categories & Tags -->
|
|
<div style="display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap; border-top:1px solid var(--border-glass); padding-top:1rem; margin-top:1rem;">
|
|
{% if idea.categories and idea.categories|length > 0 %}
|
|
<div style="display:flex; align-items:center; gap:0.4rem;">
|
|
<span style="font-size:0.8rem; color:var(--text-muted);">Categories:</span>
|
|
{% for c in idea.categories %}
|
|
<span class="badge" style="background:rgba(255,255,255,0.06); color:var(--text-primary);">{{ c }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if idea.tags and idea.tags|length > 0 %}
|
|
<div style="display:flex; align-items:center; gap:0.4rem;">
|
|
<span style="font-size:0.8rem; color:var(--text-muted);">Tags:</span>
|
|
{% for t in idea.tags %}
|
|
<span class="badge" style="background:rgba(99,102,241,0.12); color:#a5b4fc;">#{{ t }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div style="margin-left:auto; font-size:0.8rem; color:var(--text-muted); font-family:var(--font-mono);">
|
|
Submitted: {{ idea.submitted_at[:19] }}Z
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if idea.lifecycle_state == 'DUPLICATE' %}
|
|
<div class="glass-card" style="margin-bottom:1.5rem; border-color:rgba(245, 158, 11, 0.4); background:rgba(245, 158, 11, 0.08); padding:1.25rem;">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:1rem;">
|
|
<div>
|
|
<div style="font-weight:700; color:#fbbf24; display:flex; align-items:center; gap:0.5rem; font-size:1.05rem;">
|
|
<span>⚠️ Potential Duplicate Submission Detected</span>
|
|
</div>
|
|
<p style="margin:0.4rem 0 0 0; font-size:0.9rem; color:var(--text-secondary); max-width:800px;">
|
|
Automatic pipeline flagged this idea as closely matching existing catalog submissions and paused downstream deep research generation to conserve compute. You can override this and force full research generation at any time.
|
|
</p>
|
|
</div>
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-primary btn-sm" style="background:linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color:#000; font-weight:700;">
|
|
⚡ Force Run Research Pipeline
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Original Submission & URL Safety Box -->
|
|
<div style="display:grid; grid-template-columns: 2fr 1fr; gap:1.5rem; margin-bottom:2rem;">
|
|
<div class="glass-card" style="padding:1.5rem;">
|
|
<div class="markdown-view-container" data-markdown-view>
|
|
<div class="markdown-view-header">
|
|
<h3 style="font-size:1.1rem; margin:0; color:var(--text-secondary); display:flex; align-items:center; gap:0.5rem;">
|
|
<span>📝 Immutable Original Submission</span>
|
|
</h3>
|
|
<div class="markdown-view-actions">
|
|
<div class="markdown-toggle-pill">
|
|
<button type="button" class="btn-toggle-view active" data-view="formatted" title="Formatted View">
|
|
<span>👁️ Formatted</span>
|
|
</button>
|
|
<button type="button" class="btn-toggle-view" data-view="code" title="Code / Raw View">
|
|
<span>💻 Code</span>
|
|
</button>
|
|
</div>
|
|
<button type="button" class="btn-copy-markdown" onclick="copyMarkdownFromContainer(this)" title="Copy Text">
|
|
<span>📋 Copy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="markdown-view-body" style="background:rgba(0,0,0,0.3); border:1px solid var(--border-glass); border-radius:var(--radius-md); padding:1rem;">
|
|
<div class="markdown-formatted markdown-body"></div>
|
|
<div class="markdown-code" style="display:none; background:transparent; border:none; padding:0; margin:0;">
|
|
<pre style="margin:0; background:transparent; border:none; padding:0; line-height:1.6;"><code>{{ idea.original_text }}</code></pre>
|
|
</div>
|
|
<textarea class="raw-markdown-source" style="display:none;">{{ idea.original_text }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="glass-card" style="padding:1.5rem;">
|
|
<h3 style="font-size:1.1rem; margin-bottom:0.75rem; color:var(--text-secondary); display:flex; align-items:center; gap:0.5rem;">
|
|
<span>🛡️ URL Safety & VirusTotal</span>
|
|
</h3>
|
|
{% if idea.urls and idea.urls|length > 0 %}
|
|
<div style="display:flex; flex-direction:column; gap:0.6rem;">
|
|
{% for u in idea.urls %}
|
|
<div style="background:rgba(0,0,0,0.25); border:1px solid var(--border-glass); border-radius:var(--radius-sm); padding:0.6rem 0.8rem;">
|
|
<div style="word-break:break-all; font-family:var(--font-mono); font-size:0.8rem; margin-bottom:0.4rem;">
|
|
{{ u.url }}
|
|
</div>
|
|
<div style="display:flex; justify-content:space-between; align-items:center;">
|
|
<span class="badge badge-{{ u.safety_state|lower }}">{{ u.safety_state }}</span>
|
|
<span style="font-size:0.75rem; font-family:var(--font-mono); color:var(--text-muted);">Policy: {{ u.automation_policy }}</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p style="color:var(--text-muted); font-size:0.88rem;">No external URLs extracted in submission.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reference Image Card (if attached) -->
|
|
{% if idea.submission_image and idea.submission_image.present %}
|
|
<div class="glass-card" style="padding:1.5rem; margin-bottom:2rem;">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem; flex-wrap:wrap; gap:0.5rem;">
|
|
<h3 style="font-size:1.1rem; margin:0; color:var(--text-secondary); display:flex; align-items:center; gap:0.5rem;">
|
|
<span>🖼️ Attached Reference Image</span>
|
|
<span style="font-family:var(--font-mono); font-size:0.85rem; color:var(--accent-primary);">{{ idea.submission_image.artifact_id }}</span>
|
|
</h3>
|
|
<div style="display:flex; gap:0.5rem; flex-wrap:wrap; font-size:0.78rem;">
|
|
<span class="badge" style="background:rgba(255,255,255,0.06);">{{ idea.submission_image.mime_type }}</span>
|
|
<span class="badge" style="background:rgba(255,255,255,0.06);">{{ idea.submission_image.width }}x{{ idea.submission_image.height }}</span>
|
|
<span class="badge" style="background:rgba(255,255,255,0.06);">{{ (idea.submission_image.size_bytes / 1024)|round(1) }} KB</span>
|
|
<span class="badge" style="background:rgba(99,102,241,0.12); color:#a5b4fc;">Source: {{ idea.submission_image.source|capitalize }}</span>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex; flex-direction:column; align-items:center; background:rgba(0,0,0,0.3); border:1px solid var(--border-glass); border-radius:var(--radius-md); padding:1rem;">
|
|
<a href="/api/ideas/{{ idea.id }}/image" target="_blank" title="Click to open full-resolution image in new tab">
|
|
<img
|
|
src="/api/ideas/{{ idea.id }}/image"
|
|
alt="Reference visual artifact for {{ idea.id }}"
|
|
style="max-width:100%; max-height:480px; width:auto; height:auto; object-fit:contain; border-radius:var(--radius-sm); display:block; border:1px solid var(--border-glass);"
|
|
/>
|
|
</a>
|
|
<div style="margin-top:0.75rem; font-family:var(--font-mono); font-size:0.75rem; color:var(--text-muted); width:100%; display:flex; justify-content:space-between; flex-wrap:wrap; gap:0.5rem;">
|
|
<span>File: {{ idea.submission_image.original_filename }}</span>
|
|
<span>SHA-256: <code>{{ idea.submission_image.sha256 }}</code></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Tabs Section -->
|
|
<div class="tab-container glass-card" style="padding:1.75rem;">
|
|
<div class="tab-nav">
|
|
{% if idea.submission_image and idea.submission_image.present %}
|
|
<button class="tab-btn" data-tab="tab-image-context">🖼️ Visual & Image Context</button>
|
|
{% endif %}
|
|
<button class="tab-btn active" data-tab="tab-prior-art">🔍 Prior Art & Alternatives</button>
|
|
<button class="tab-btn" data-tab="tab-research">📚 Deep Research Synthesis</button>
|
|
<button class="tab-btn" data-tab="tab-feasibility">⚙️ Feasibility & Critique</button>
|
|
<button class="tab-btn" data-tab="tab-worktracks">🚀 Work Tracks ({{ idea.work_tracks|length }})</button>
|
|
<button class="tab-btn" data-tab="tab-provenance">⏱️ Provenance & Tokens ({{ idea.provenance|length }})</button>
|
|
<button class="tab-btn" data-tab="tab-artifacts">📦 Dossier & Gitea Project</button>
|
|
</div>
|
|
|
|
<!-- Tab 0: Image Context (if present) -->
|
|
{% if idea.submission_image and idea.submission_image.present %}
|
|
<div id="tab-image-context" class="tab-panel">
|
|
{% set stage_data = idea.research_stages.IMAGE_CONTEXT if idea.research_stages else None %}
|
|
{% set failed_image_run = idea.provenance | selectattr("stage", "equalto", "IMAGE_CONTEXT") | selectattr("status", "equalto", "FAILED") | list %}
|
|
{% if stage_data and stage_data.total_versions > 0 %}
|
|
<div class="markdown-view-container" data-markdown-view>
|
|
<div class="markdown-view-header" style="flex-wrap:wrap; gap:0.75rem;">
|
|
<div class="markdown-view-meta" style="display:flex; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
|
<span class="badge" style="background:rgba(99,102,241,0.15); color:#a5b4fc;">Stage: IMAGE_CONTEXT</span>
|
|
<span class="badge" style="background:rgba(34,197,94,0.15); color:#4ade80;">AI Visual Interpretation</span>
|
|
|
|
{% if stage_data.total_versions > 1 %}
|
|
<div class="version-pulldown-wrapper" style="display:inline-flex; align-items:center; gap:0.35rem;">
|
|
<label for="select-version-IMAGE_CONTEXT" style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Version:</label>
|
|
<select id="select-version-IMAGE_CONTEXT" class="form-select form-select-sm research-version-select" data-stage="IMAGE_CONTEXT" onchange="switchResearchVersion(this, 'IMAGE_CONTEXT')" style="font-size:0.8rem; padding:0.25rem 0.6rem; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); border-radius:var(--radius-sm); color:var(--text-primary); cursor:pointer;">
|
|
{% for v in stage_data.versions | reverse %}
|
|
<option value="{{ v.version }}" {% if v.is_latest %}selected{% endif %}>
|
|
v{{ v.version }}{% if v.is_latest %} (Latest){% endif %} — {{ v.started_at[:19].replace('T', ' ') }} UTC [{{ v.model }}]
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% else %}
|
|
<span class="badge" style="background:rgba(16,185,129,0.15); color:#34d399; border:1px solid rgba(16,185,129,0.3); font-size:0.75rem;">v1 (Latest)</span>
|
|
{% endif %}
|
|
|
|
<span class="badge research-meta-model" style="background:rgba(255,255,255,0.06); font-size:0.75rem;">🤖 {{ stage_data.latest.model }}</span>
|
|
{% if stage_data.latest.tokens.total %}
|
|
<span class="badge research-meta-tokens" style="background:rgba(255,255,255,0.05); font-size:0.75rem; color:var(--text-muted);">⚡ {{ stage_data.latest.tokens.total }} tokens</span>
|
|
{% endif %}
|
|
<span class="research-meta-time" style="font-size:0.75rem; color:var(--text-muted); font-family:var(--font-mono);">🕒 {{ stage_data.latest.started_at[:19].replace('T', ' ') }} UTC</span>
|
|
</div>
|
|
|
|
<div class="markdown-view-actions">
|
|
<div class="markdown-toggle-pill">
|
|
<button type="button" class="btn-toggle-view active" data-view="formatted" title="Formatted View">
|
|
<span>👁️ Formatted</span>
|
|
</button>
|
|
<button type="button" class="btn-toggle-view" data-view="code" title="Code / Raw View">
|
|
<span>💻 Code</span>
|
|
</button>
|
|
</div>
|
|
<button type="button" class="btn-copy-markdown" onclick="copyMarkdownFromContainer(this)" title="Copy Markdown">
|
|
<span>📋 Copy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="markdown-view-body">
|
|
{% for v in stage_data.versions %}
|
|
<div class="research-version-pane" data-stage="IMAGE_CONTEXT" data-version="{{ v.version }}" data-model="{{ v.model }}" data-tokens="{{ v.tokens.total }}" data-time="{{ v.started_at[:19].replace('T', ' ') }} UTC" {% if not v.is_latest %}style="display:none;"{% endif %}>
|
|
<div class="markdown-formatted markdown-body"></div>
|
|
<div class="markdown-code" style="display:none;">
|
|
<pre><code>{{ v.content }}</code></pre>
|
|
</div>
|
|
<textarea class="raw-markdown-source" style="display:none;">{{ v.content }}</textarea>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% elif failed_image_run and failed_image_run|length > 0 %}
|
|
<div class="glass-card" style="padding:2.5rem 2rem; text-align:center; background:rgba(239,68,68,0.04); border:1px dashed rgba(239,68,68,0.3); border-radius:var(--radius-md);">
|
|
<div style="font-size:2.2rem; margin-bottom:0.75rem;">⚠️</div>
|
|
<h4 style="color:#f87171; margin-bottom:0.5rem;">Vision Processing Notice</h4>
|
|
<p style="color:var(--text-secondary); max-width:550px; margin:0 auto; font-size:0.9rem;">
|
|
The reference image was safely preserved, but automated vision analysis was unavailable or encountered an error:
|
|
<code>{{ failed_image_run[0].error_message or "Model refused or failed image processing." }}</code>.
|
|
The idea continues with text-only research synthesis.
|
|
</p>
|
|
</div>
|
|
{% else %}
|
|
<div class="glass-card" style="padding:2.5rem 2rem; text-align:center; background:rgba(255,255,255,0.02); border:1px dashed var(--border-glass); border-radius:var(--radius-md);">
|
|
<div style="font-size:2.2rem; margin-bottom:0.75rem;">🖼️</div>
|
|
<h4 style="color:var(--text-secondary); margin-bottom:0.5rem;">Visual Context Queued</h4>
|
|
<p style="color:var(--text-muted); max-width:450px; margin:0 auto; font-size:0.9rem;">
|
|
Image interpretation is queued or processing. Visual context will appear here once synthesized.
|
|
</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Tab 1: Prior Art -->
|
|
<div id="tab-prior-art" class="tab-panel active">
|
|
{% set stage_data = idea.research_stages.PRIOR_ART if idea.research_stages else None %}
|
|
{% if stage_data and stage_data.total_versions > 0 %}
|
|
<div class="markdown-view-container" data-markdown-view>
|
|
<div class="markdown-view-header" style="flex-wrap:wrap; gap:0.75rem;">
|
|
<div class="markdown-view-meta" style="display:flex; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
|
<span class="badge" style="background:rgba(99,102,241,0.15); color:#a5b4fc;">Stage: PRIOR_ART</span>
|
|
|
|
{% if stage_data.total_versions > 1 %}
|
|
<div class="version-pulldown-wrapper" style="display:inline-flex; align-items:center; gap:0.35rem;">
|
|
<label for="select-version-PRIOR_ART" style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Version:</label>
|
|
<select id="select-version-PRIOR_ART" class="form-select form-select-sm research-version-select" data-stage="PRIOR_ART" onchange="switchResearchVersion(this, 'PRIOR_ART')" style="font-size:0.8rem; padding:0.25rem 0.6rem; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); border-radius:var(--radius-sm); color:var(--text-primary); cursor:pointer;">
|
|
{% for v in stage_data.versions | reverse %}
|
|
<option value="{{ v.version }}" {% if v.is_latest %}selected{% endif %}>
|
|
v{{ v.version }}{% if v.is_latest %} (Latest){% endif %} — {{ v.started_at[:19].replace('T', ' ') }} UTC [{{ v.model }}]
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% else %}
|
|
<span class="badge" style="background:rgba(16,185,129,0.15); color:#34d399; border:1px solid rgba(16,185,129,0.3); font-size:0.75rem;">v1 (Latest)</span>
|
|
{% endif %}
|
|
|
|
<span class="badge research-meta-model" style="background:rgba(255,255,255,0.06); font-size:0.75rem;">🤖 {{ stage_data.latest.model }}</span>
|
|
{% if stage_data.latest.tokens.total %}
|
|
<span class="badge research-meta-tokens" style="background:rgba(255,255,255,0.05); font-size:0.75rem; color:var(--text-muted);">⚡ {{ stage_data.latest.tokens.total }} tokens</span>
|
|
{% endif %}
|
|
<span class="research-meta-time" style="font-size:0.75rem; color:var(--text-muted); font-family:var(--font-mono);">🕒 {{ stage_data.latest.started_at[:19].replace('T', ' ') }} UTC</span>
|
|
</div>
|
|
|
|
<div class="markdown-view-actions">
|
|
<div class="markdown-toggle-pill">
|
|
<button type="button" class="btn-toggle-view active" data-view="formatted" title="Formatted View">
|
|
<span>👁️ Formatted</span>
|
|
</button>
|
|
<button type="button" class="btn-toggle-view" data-view="code" title="Code / Raw View">
|
|
<span>💻 Code</span>
|
|
</button>
|
|
</div>
|
|
<button type="button" class="btn-copy-markdown" onclick="copyMarkdownFromContainer(this)" title="Copy Markdown">
|
|
<span>📋 Copy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="markdown-view-body">
|
|
{% for v in stage_data.versions %}
|
|
<div class="research-version-pane" data-stage="PRIOR_ART" data-version="{{ v.version }}" data-model="{{ v.model }}" data-tokens="{{ v.tokens.total }}" data-time="{{ v.started_at[:19].replace('T', ' ') }} UTC" {% if not v.is_latest %}style="display:none;"{% endif %}>
|
|
<div class="markdown-formatted markdown-body"></div>
|
|
<div class="markdown-code" style="display:none;">
|
|
<pre><code>{{ v.content }}</code></pre>
|
|
</div>
|
|
<textarea class="raw-markdown-source" style="display:none;">{{ v.content }}</textarea>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="glass-card" style="padding:2.5rem 2rem; text-align:center; background:rgba(255,255,255,0.02); border:1px dashed var(--border-glass); border-radius:var(--radius-md);">
|
|
<div style="font-size:2.2rem; margin-bottom:0.75rem;">🔍</div>
|
|
<h4 style="font-size:1.15rem; margin-bottom:0.4rem; color:var(--text-primary);">Prior Art & Competitive Discovery Not Generated</h4>
|
|
<p style="color:var(--text-secondary); max-width:560px; margin:0 auto 1.25rem auto; font-size:0.9rem;">
|
|
Prior art synthesis was not performed yet or was paused during intake. You can trigger the automated research pipeline to perform market and repository discovery now.
|
|
</p>
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-primary" style="display:inline-flex; align-items:center; gap:0.5rem; font-weight:600; padding:0.6rem 1.25rem;">
|
|
<span>⚡ Generate Prior Art & Research Documents</span>
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Tab 2: Research Synthesis -->
|
|
<div id="tab-research" class="tab-panel">
|
|
{% set stage_data = idea.research_stages.RESEARCH if idea.research_stages else None %}
|
|
{% if stage_data and stage_data.total_versions > 0 %}
|
|
<div class="markdown-view-container" data-markdown-view>
|
|
<div class="markdown-view-header" style="flex-wrap:wrap; gap:0.75rem;">
|
|
<div class="markdown-view-meta" style="display:flex; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
|
<span class="badge" style="background:rgba(99,102,241,0.15); color:#a5b4fc;">Stage: RESEARCH</span>
|
|
|
|
{% if stage_data.total_versions > 1 %}
|
|
<div class="version-pulldown-wrapper" style="display:inline-flex; align-items:center; gap:0.35rem;">
|
|
<label for="select-version-RESEARCH" style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Version:</label>
|
|
<select id="select-version-RESEARCH" class="form-select form-select-sm research-version-select" data-stage="RESEARCH" onchange="switchResearchVersion(this, 'RESEARCH')" style="font-size:0.8rem; padding:0.25rem 0.6rem; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); border-radius:var(--radius-sm); color:var(--text-primary); cursor:pointer;">
|
|
{% for v in stage_data.versions | reverse %}
|
|
<option value="{{ v.version }}" {% if v.is_latest %}selected{% endif %}>
|
|
v{{ v.version }}{% if v.is_latest %} (Latest){% endif %} — {{ v.started_at[:19].replace('T', ' ') }} UTC [{{ v.model }}]
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% else %}
|
|
<span class="badge" style="background:rgba(16,185,129,0.15); color:#34d399; border:1px solid rgba(16,185,129,0.3); font-size:0.75rem;">v1 (Latest)</span>
|
|
{% endif %}
|
|
|
|
<span class="badge research-meta-model" style="background:rgba(255,255,255,0.06); font-size:0.75rem;">🤖 {{ stage_data.latest.model }}</span>
|
|
{% if stage_data.latest.tokens.total %}
|
|
<span class="badge research-meta-tokens" style="background:rgba(255,255,255,0.05); font-size:0.75rem; color:var(--text-muted);">⚡ {{ stage_data.latest.tokens.total }} tokens</span>
|
|
{% endif %}
|
|
<span class="research-meta-time" style="font-size:0.75rem; color:var(--text-muted); font-family:var(--font-mono);">🕒 {{ stage_data.latest.started_at[:19].replace('T', ' ') }} UTC</span>
|
|
</div>
|
|
|
|
<div class="markdown-view-actions">
|
|
<div class="markdown-toggle-pill">
|
|
<button type="button" class="btn-toggle-view active" data-view="formatted" title="Formatted View">
|
|
<span>👁️ Formatted</span>
|
|
</button>
|
|
<button type="button" class="btn-toggle-view" data-view="code" title="Code / Raw View">
|
|
<span>💻 Code</span>
|
|
</button>
|
|
</div>
|
|
<button type="button" class="btn-copy-markdown" onclick="copyMarkdownFromContainer(this)" title="Copy Markdown">
|
|
<span>📋 Copy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="markdown-view-body">
|
|
{% for v in stage_data.versions %}
|
|
<div class="research-version-pane" data-stage="RESEARCH" data-version="{{ v.version }}" data-model="{{ v.model }}" data-tokens="{{ v.tokens.total }}" data-time="{{ v.started_at[:19].replace('T', ' ') }} UTC" {% if not v.is_latest %}style="display:none;"{% endif %}>
|
|
<div class="markdown-formatted markdown-body"></div>
|
|
<div class="markdown-code" style="display:none;">
|
|
<pre><code>{{ v.content }}</code></pre>
|
|
</div>
|
|
<textarea class="raw-markdown-source" style="display:none;">{{ v.content }}</textarea>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="glass-card" style="padding:2.5rem 2rem; text-align:center; background:rgba(255,255,255,0.02); border:1px dashed var(--border-glass); border-radius:var(--radius-md);">
|
|
<div style="font-size:2.2rem; margin-bottom:0.75rem;">🔬</div>
|
|
<h4 style="font-size:1.15rem; margin-bottom:0.4rem; color:var(--text-primary);">Deep Research Synthesis Not Generated</h4>
|
|
<p style="color:var(--text-secondary); max-width:560px; margin:0 auto 1.25rem auto; font-size:0.9rem;">
|
|
Technical analysis and architectural synthesis were not generated yet. You can trigger the automated research engine to analyze this submission now.
|
|
</p>
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-primary" style="display:inline-flex; align-items:center; gap:0.5rem; font-weight:600; padding:0.6rem 1.25rem;">
|
|
<span>⚡ Generate Deep Research Synthesis</span>
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Tab 3: Feasibility & Critique -->
|
|
<div id="tab-feasibility" class="tab-panel">
|
|
{% set stage_data = idea.research_stages.FEASIBILITY if idea.research_stages else None %}
|
|
{% if stage_data and stage_data.total_versions > 0 %}
|
|
<div class="markdown-view-container" data-markdown-view>
|
|
<div class="markdown-view-header" style="flex-wrap:wrap; gap:0.75rem;">
|
|
<div class="markdown-view-meta" style="display:flex; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
|
<span class="badge" style="background:rgba(99,102,241,0.15); color:#a5b4fc;">Stage: FEASIBILITY</span>
|
|
|
|
{% if stage_data.total_versions > 1 %}
|
|
<div class="version-pulldown-wrapper" style="display:inline-flex; align-items:center; gap:0.35rem;">
|
|
<label for="select-version-FEASIBILITY" style="font-size:0.75rem; color:var(--text-muted); font-weight:600;">Version:</label>
|
|
<select id="select-version-FEASIBILITY" class="form-select form-select-sm research-version-select" data-stage="FEASIBILITY" onchange="switchResearchVersion(this, 'FEASIBILITY')" style="font-size:0.8rem; padding:0.25rem 0.6rem; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); border-radius:var(--radius-sm); color:var(--text-primary); cursor:pointer;">
|
|
{% for v in stage_data.versions | reverse %}
|
|
<option value="{{ v.version }}" {% if v.is_latest %}selected{% endif %}>
|
|
v{{ v.version }}{% if v.is_latest %} (Latest){% endif %} — {{ v.started_at[:19].replace('T', ' ') }} UTC [{{ v.model }}]
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% else %}
|
|
<span class="badge" style="background:rgba(16,185,129,0.15); color:#34d399; border:1px solid rgba(16,185,129,0.3); font-size:0.75rem;">v1 (Latest)</span>
|
|
{% endif %}
|
|
|
|
<span class="badge research-meta-model" style="background:rgba(255,255,255,0.06); font-size:0.75rem;">🤖 {{ stage_data.latest.model }}</span>
|
|
{% if stage_data.latest.tokens.total %}
|
|
<span class="badge research-meta-tokens" style="background:rgba(255,255,255,0.05); font-size:0.75rem; color:var(--text-muted);">⚡ {{ stage_data.latest.tokens.total }} tokens</span>
|
|
{% endif %}
|
|
<span class="research-meta-time" style="font-size:0.75rem; color:var(--text-muted); font-family:var(--font-mono);">🕒 {{ stage_data.latest.started_at[:19].replace('T', ' ') }} UTC</span>
|
|
</div>
|
|
|
|
<div class="markdown-view-actions">
|
|
<div class="markdown-toggle-pill">
|
|
<button type="button" class="btn-toggle-view active" data-view="formatted" title="Formatted View">
|
|
<span>👁️ Formatted</span>
|
|
</button>
|
|
<button type="button" class="btn-toggle-view" data-view="code" title="Code / Raw View">
|
|
<span>💻 Code</span>
|
|
</button>
|
|
</div>
|
|
<button type="button" class="btn-copy-markdown" onclick="copyMarkdownFromContainer(this)" title="Copy Markdown">
|
|
<span>📋 Copy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="markdown-view-body">
|
|
{% for v in stage_data.versions %}
|
|
<div class="research-version-pane" data-stage="FEASIBILITY" data-version="{{ v.version }}" data-model="{{ v.model }}" data-tokens="{{ v.tokens.total }}" data-time="{{ v.started_at[:19].replace('T', ' ') }} UTC" {% if not v.is_latest %}style="display:none;"{% endif %}>
|
|
<div class="markdown-formatted markdown-body"></div>
|
|
<div class="markdown-code" style="display:none;">
|
|
<pre><code>{{ v.content }}</code></pre>
|
|
</div>
|
|
<textarea class="raw-markdown-source" style="display:none;">{{ v.content }}</textarea>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="glass-card" style="padding:2.5rem 2rem; text-align:center; background:rgba(255,255,255,0.02); border:1px dashed var(--border-glass); border-radius:var(--radius-md);">
|
|
<div style="font-size:2.2rem; margin-bottom:0.75rem;">⚖️</div>
|
|
<h4 style="font-size:1.15rem; margin-bottom:0.4rem; color:var(--text-primary);">Feasibility & Risk Critique Not Generated</h4>
|
|
<p style="color:var(--text-secondary); max-width:560px; margin:0 auto 1.25rem auto; font-size:0.9rem;">
|
|
Architectural risk assessment and feasibility scoring were not generated yet. You can trigger the critique engine now.
|
|
</p>
|
|
<button onclick="reprocessIdea('{{ idea.id }}')" class="btn btn-primary" style="display:inline-flex; align-items:center; gap:0.5rem; font-weight:600; padding:0.6rem 1.25rem;">
|
|
<span>⚡ Generate Feasibility & Risk Critique</span>
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Tab 4: Work Tracks -->
|
|
<div id="tab-worktracks" class="tab-panel">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1.5rem; flex-wrap:wrap; gap:1rem;">
|
|
<div>
|
|
<h3 style="font-size:1.2rem;">Independent Work Tracks</h3>
|
|
<p style="color:var(--text-secondary); font-size:0.9rem;">Assign multiple work types (Article, Blog Entry, Coding Project, YouTube Video) to produce distinct outputs.</p>
|
|
</div>
|
|
|
|
{% if user and (user.username == idea.claimed_by or user.role.value == 'ADMIN') %}
|
|
<div style="display:flex; gap:0.5rem; align-items:center; flex-wrap:wrap;">
|
|
<select id="work-type-select" class="form-select" style="width:auto; padding:0.4rem 0.8rem; font-size:0.85rem;">
|
|
<option value="ARTICLE">Article / Essay</option>
|
|
<option value="BLOG_ENTRY">Blog Entry</option>
|
|
<option value="CODING_PROJECT">Coding Project</option>
|
|
<option value="YOUTUBE_VIDEO">YouTube Video</option>
|
|
</select>
|
|
<input type="text" id="work-track-name" class="form-input" placeholder="Track Name (e.g. Technical Blueprint)" style="width:200px; padding:0.4rem 0.8rem; font-size:0.85rem;">
|
|
<select id="work-model-select" class="form-select" style="width:auto; padding:0.4rem 0.8rem; font-size:0.85rem;" title="Select AI Model">
|
|
<option value="">🤖 Default Policy</option>
|
|
<option value="auto/best-reasoning">🧠 Best Reasoning</option>
|
|
<option value="auto/best-coding">💻 Best Coding</option>
|
|
<option value="auto/best-fast">⚡ Best Fast</option>
|
|
<option value="auto/best-free">🆓 Best Free</option>
|
|
<option value="auto/pro-coding">🎯 Pro Coding</option>
|
|
<option value="auto/pro-reasoning">🏆 Pro Reasoning</option>
|
|
</select>
|
|
<button onclick="createWorkTrack('{{ idea.id }}')" class="btn btn-primary btn-sm">+ Add Track</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if idea.work_tracks and idea.work_tracks|length > 0 %}
|
|
<div style="display:flex; flex-direction:column; gap:1.25rem;">
|
|
{% for tr in idea.work_tracks %}
|
|
<div style="background:rgba(0,0,0,0.3); border:1px solid var(--border-glass); border-radius:var(--radius-md); padding:1.25rem;">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:0.75rem; flex-wrap:wrap; gap:0.5rem;">
|
|
<div style="display:flex; align-items:center; flex-wrap:wrap; gap:0.4rem;">
|
|
<span style="font-family:var(--font-mono); font-weight:700; color:var(--accent-primary);">{{ tr.id }}</span>
|
|
<span style="font-weight:700; font-size:1.1rem;">{{ tr.name }}</span>
|
|
<span class="badge" style="background:rgba(255,255,255,0.08);">{{ tr.work_type_id }}</span>
|
|
<span class="badge badge-{{ tr.state|lower }}">{{ tr.state }}</span>
|
|
{% if tr.model_override %}
|
|
<span class="badge" style="background:rgba(99,102,241,0.18); color:#a5b4fc; border:1px solid rgba(99,102,241,0.3);" title="Assigned AI Model">🤖 {{ tr.model_override }}</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div style="display:flex; gap:0.5rem; align-items:center;">
|
|
{% if user and (user.username == idea.claimed_by or user.role.value == 'ADMIN') %}
|
|
<select id="model-select-{{ tr.id }}" class="form-select form-select-sm" style="font-size:0.78rem; padding:0.25rem 0.5rem; background:rgba(0,0,0,0.4); border-color:var(--border-glass);" title="Select AI Model to run">
|
|
<option value="" {% if not tr.model_override %}selected{% endif %}>🤖 Default Policy</option>
|
|
<option value="auto/best-reasoning" {% if tr.model_override == 'auto/best-reasoning' %}selected{% endif %}>🧠 Best Reasoning</option>
|
|
<option value="auto/best-coding" {% if tr.model_override == 'auto/best-coding' %}selected{% endif %}>💻 Best Coding</option>
|
|
<option value="auto/best-fast" {% if tr.model_override == 'auto/best-fast' %}selected{% endif %}>⚡ Best Fast</option>
|
|
<option value="auto/best-free" {% if tr.model_override == 'auto/best-free' %}selected{% endif %}>🆓 Best Free</option>
|
|
<option value="auto/pro-coding" {% if tr.model_override == 'auto/pro-coding' %}selected{% endif %}>🎯 Pro Coding</option>
|
|
<option value="auto/pro-reasoning" {% if tr.model_override == 'auto/pro-reasoning' %}selected{% endif %}>🏆 Pro Reasoning</option>
|
|
</select>
|
|
<button onclick="activateWorkTrack('{{ tr.id }}')" class="btn btn-primary btn-sm">
|
|
{% if tr.outputs and tr.outputs|length > 0 %}🔄 Re-run Workflow{% else %}⚡ Run Workflow{% endif %}
|
|
</button>
|
|
{% if tr.work_type_id == 'CODING_PROJECT' and tr.state == 'COMPLETED' and idea.gitea_publish_allowed %}
|
|
<button onclick="graduateToGitea('{{ tr.id }}')" class="btn btn-success btn-sm">📦 Graduate to Gitea</button>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- External resources (Gitea repo) -->
|
|
{% if tr.external_resources and tr.external_resources|length > 0 %}
|
|
<div style="margin-bottom:0.75rem; background:rgba(16,185,129,0.08); border:1px solid rgba(16,185,129,0.25); border-radius:var(--radius-sm); padding:0.6rem 0.8rem; font-size:0.85rem;">
|
|
{% for ext in tr.external_resources %}
|
|
<span>🎉 Graduated Project Repository: <a href="{{ ext.url }}" target="_blank" style="color:var(--color-success); font-weight:700;">{{ ext.url }} ↗</a></span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Outputs -->
|
|
{% if tr.outputs and tr.outputs|length > 0 %}
|
|
<div style="margin-top:0.75rem;">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:0.5rem;">
|
|
<h4 style="font-size:0.9rem; color:var(--text-secondary); margin:0;">Generated Artifacts & Versions ({{ tr.outputs|length }}):</h4>
|
|
</div>
|
|
<div style="display:flex; flex-direction:column; gap:0.75rem;">
|
|
{% for out in tr.outputs %}
|
|
<details {% if out.is_current %}open{% endif %} style="background:rgba(255,255,255,0.03); border:1px solid var(--border-glass); border-radius:var(--radius-sm); padding:0.75rem 1rem; min-width:0; max-width:100%; overflow:hidden;">
|
|
<summary style="cursor:pointer; font-weight:600; font-family:var(--font-mono); font-size:0.9rem; color:var(--accent-primary); display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.5rem; min-width:0;">
|
|
<div style="display:flex; align-items:center; flex-wrap:wrap; gap:0.4rem;">
|
|
<span>📄 {{ out.name }}</span>
|
|
{% if out.is_current %}
|
|
<span class="badge" style="background:rgba(16,185,129,0.2); color:#34d399; border:1px solid rgba(16,185,129,0.4); font-size:0.75rem;">v{{ out.version }} (Latest)</span>
|
|
{% else %}
|
|
<span class="badge" style="background:rgba(255,255,255,0.08); color:var(--text-muted); font-size:0.75rem;">v{{ out.version }}</span>
|
|
{% endif %}
|
|
{% if out.model_used %}
|
|
<span class="badge" style="background:rgba(99,102,241,0.15); color:#a5b4fc; font-size:0.72rem;">🤖 {{ out.model_used }}</span>
|
|
{% endif %}
|
|
<span style="font-size:0.72rem; color:var(--text-muted); font-family:var(--font-sans); font-weight:normal;">🕒 {{ out.created_at[:19].replace('T', ' ') }} UTC</span>
|
|
</div>
|
|
|
|
<div>
|
|
{% if user and (user.username == idea.claimed_by or user.role.value == 'ADMIN') %}
|
|
<button data-output-id="{{ out.id }}" data-doc-name="{{ out.name }}" data-version="{{ out.version }}" onclick="event.stopPropagation(); deleteWorkTrackOutput(this.dataset.outputId, this.dataset.docName, this.dataset.version)" class="btn btn-sm" style="padding:0.15rem 0.5rem; font-size:0.75rem; background:rgba(239,68,68,0.18); border:1px solid rgba(239,68,68,0.35); color:#fca5a5;" title="Delete this artifact version">
|
|
🗑️ Delete
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</summary>
|
|
<div style="margin-top:0.75rem; border-top:1px solid var(--border-glass); padding-top:0.75rem; min-width:0; max-width:100%; overflow-x:auto;">
|
|
<div class="markdown-view-container" data-markdown-view style="width:100%; min-width:0; max-width:100%;">
|
|
<div class="markdown-view-header" style="margin-bottom:0.75rem;">
|
|
<div class="markdown-view-meta">
|
|
<span style="font-size:0.8rem; color:var(--text-muted); font-family:var(--font-mono);">Path: {{ out.artifact_path }}</span>
|
|
</div>
|
|
<div class="markdown-view-actions">
|
|
<div class="markdown-toggle-pill">
|
|
<button type="button" class="btn-toggle-view active" data-view="formatted" title="Formatted View">
|
|
<span>👁️ Formatted</span>
|
|
</button>
|
|
<button type="button" class="btn-toggle-view" data-view="code" title="Code / Raw View">
|
|
<span>💻 Code</span>
|
|
</button>
|
|
</div>
|
|
<button type="button" class="btn-copy-markdown" onclick="copyMarkdownFromContainer(this)" title="Copy Markdown">
|
|
<span>📋 Copy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="markdown-view-body">
|
|
<div class="markdown-formatted markdown-body"></div>
|
|
<div class="markdown-code" style="display:none;">
|
|
<pre><code>{{ out.content }}</code></pre>
|
|
</div>
|
|
<textarea class="raw-markdown-source" style="display:none;">{{ out.content }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div style="margin-top:0.75rem; padding:0.75rem; background:rgba(255,255,255,0.02); border-radius:var(--radius-sm); border:1px dashed var(--border-glass); display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:0.5rem;">
|
|
<span style="color:var(--text-muted); font-size:0.85rem;">No artifacts generated yet for this track.</span>
|
|
{% if user and (user.username == idea.claimed_by or user.role.value == 'ADMIN') %}
|
|
<button onclick="activateWorkTrack('{{ tr.id }}')" class="btn btn-primary btn-sm" style="padding:0.25rem 0.6rem; font-size:0.8rem;">⚡ Run Workflow</button>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p style="color:var(--text-muted);">No work tracks created yet. Claim this idea to create independent development tracks.</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Tab 5: Provenance & Tokens -->
|
|
<div id="tab-provenance" class="tab-panel">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;">
|
|
<h3 style="font-size:1.1rem;">Execution Provenance & Token Accounting</h3>
|
|
<div style="font-family:var(--font-mono); font-size:0.85rem; color:var(--text-secondary);">
|
|
Total Tokens Consumed: <strong style="color:var(--accent-primary);">{{ idea.usage_summary.total_tokens }}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
{% if idea.provenance and idea.provenance|length > 0 %}
|
|
<div style="overflow-x:auto;">
|
|
<table style="width:100%; border-collapse:collapse; font-size:0.85rem; text-align:left;">
|
|
<thead>
|
|
<tr style="border-bottom:1px solid var(--border-glass); color:var(--text-muted);">
|
|
<th style="padding:0.6rem;">Run ID</th>
|
|
<th style="padding:0.6rem;">Processor / Stage</th>
|
|
<th style="padding:0.6rem;">Prompt</th>
|
|
<th style="padding:0.6rem;">Model</th>
|
|
<th style="padding:0.6rem;">Tokens (In/Out/Total)</th>
|
|
<th style="padding:0.6rem;">Duration</th>
|
|
<th style="padding:0.6rem;">Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in idea.provenance %}
|
|
<tr style="border-bottom:1px solid rgba(255,255,255,0.04);">
|
|
<td style="padding:0.6rem; font-family:var(--font-mono); color:var(--accent-primary);">{{ r.id }}</td>
|
|
<td style="padding:0.6rem;"><strong>{{ r.processor_name }}</strong><br><span style="font-size:0.75rem; color:var(--text-muted);">{{ r.stage }}</span></td>
|
|
<td style="padding:0.6rem; font-family:var(--font-mono); font-size:0.78rem;">{{ r.prompt_id }} <span class="badge" style="font-size:0.65rem;">v{{ r.prompt_version }}</span></td>
|
|
<td style="padding:0.6rem; font-family:var(--font-mono); font-size:0.78rem;">{{ r.resolved_model }}</td>
|
|
<td style="padding:0.6rem; font-family:var(--font-mono);">{{ r.input_tokens }} / {{ r.output_tokens }} / <strong>{{ r.total_tokens }}</strong></td>
|
|
<td style="padding:0.6rem; font-family:var(--font-mono);">{{ r.duration_ms }}ms</td>
|
|
<td style="padding:0.6rem;"><span class="badge badge-success">{{ r.status }}</span></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p style="color:var(--text-muted);">No processor execution runs recorded yet.</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Tab 6: Dossier & Gitea Project -->
|
|
<div id="tab-artifacts" class="tab-panel">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem; flex-wrap:wrap; gap:0.5rem;">
|
|
<div>
|
|
<h3 style="font-size:1.1rem; margin-bottom:0.25rem;">Canonical Dossier & Gitea Project</h3>
|
|
<p style="color:var(--text-secondary); font-size:0.88rem; margin:0;">Ideas remain local until a Gitea-linked claimant deliberately publishes them under the <strong>thinkstorm</strong> organization.</p>
|
|
</div>
|
|
<div>
|
|
{% if idea.gitea_repo_url %}
|
|
<a href="{{ idea.gitea_repo_url }}" target="_blank" class="btn btn-primary btn-sm">
|
|
🔗 Open Project on Gitea ↗
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Gitea Project Card -->
|
|
<div style="background:rgba(0,0,0,0.3); border:1px solid var(--border-glass); border-radius:var(--radius-md); padding:1.25rem; margin-bottom:1.25rem;">
|
|
<div style="display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.75rem;">
|
|
<div>
|
|
<div style="font-weight:700; font-size:1.05rem; margin-bottom:0.25rem; display:flex; align-items:center; gap:0.5rem;">
|
|
<span>🐙 Gitea Project Repository</span>
|
|
<span class="badge" style="background:rgba(99,102,241,0.2); color:#a5b4fc; font-size:0.75rem;">org: thinkstorm</span>
|
|
</div>
|
|
<div style="font-family:var(--font-mono); font-size:0.85rem; color:var(--text-muted);">
|
|
Repo:
|
|
{% if idea.gitea_repo_url %}
|
|
<span style="color:var(--accent-primary);">{{ idea.gitea_repo_name }}</span>
|
|
{% else %}
|
|
<span style="color:var(--text-secondary);">Not published</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if idea.gitea_repo_url %}
|
|
<div style="font-size:0.8rem; color:var(--text-secondary); margin-top:0.25rem; font-family:var(--font-mono);">
|
|
Clone: <code style="color:#a5b4fc; background:rgba(0,0,0,0.4); padding:0.2rem 0.4rem; border-radius:4px;">git clone https://git.labyricorn.com/{{ idea.gitea_repo_name }}.git</code>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div style="display:flex; gap:0.5rem; flex-wrap:wrap; align-items:center;">
|
|
{% if idea.gitea_publish_allowed %}
|
|
<button id="sync-gitea-btn" onclick="syncIdeaToGitea('{{ idea.id }}')" class="btn btn-primary btn-sm">
|
|
{% if idea.gitea_repo_url %}🔄 Re-sync to Gitea{% else %}📤 Publish to Gitea{% endif %}
|
|
</button>
|
|
{% elif idea.gitea_publish_reason %}
|
|
<span style="color:var(--text-muted); font-size:0.82rem; max-width:330px;">{{ idea.gitea_publish_reason }}</span>
|
|
{% endif %}
|
|
{% if idea.gitea_repo_url %}
|
|
<a href="{{ idea.gitea_repo_url }}" target="_blank" class="btn btn-secondary btn-sm">
|
|
Open in Gitea ↗
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dossier File Tree -->
|
|
<div style="background:rgba(0,0,0,0.25); border:1px solid var(--border-glass); border-radius:var(--radius-md); padding:1.25rem;">
|
|
<h4 style="font-size:0.95rem; margin-bottom:0.75rem; color:var(--text-secondary);">Canonical Dossier Files in Store:</h4>
|
|
<div style="display:flex; flex-direction:column; gap:0.5rem; font-family:var(--font-mono); font-size:0.85rem;">
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(255,255,255,0.03); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>📄 idea.md</span>
|
|
<span style="color:var(--text-muted);">Summary & Original Text</span>
|
|
</div>
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(255,255,255,0.03); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>📊 metadata.json</span>
|
|
<span style="color:var(--text-muted);">Structured Metadata & State</span>
|
|
</div>
|
|
{% if idea.submission_image and idea.submission_image.present %}
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(99,102,241,0.08); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>🖼️ {{ idea.submission_image.artifact_id }} ({{ idea.submission_image.mime_type }})</span>
|
|
<span style="color:#a5b4fc;">Reference Image Artifact</span>
|
|
</div>
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(255,255,255,0.03); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>📄 research/image-context.md</span>
|
|
<span style="color:var(--text-muted);">Visual Interpretation Context</span>
|
|
</div>
|
|
{% endif %}
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(255,255,255,0.03); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>🔍 research/prior-art.md</span>
|
|
<span style="color:var(--text-muted);">Competitor Analysis</span>
|
|
</div>
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(255,255,255,0.03); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>📚 research/analysis.md</span>
|
|
<span style="color:var(--text-muted);">Deep Research Synthesis</span>
|
|
</div>
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(255,255,255,0.03); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>⚙️ research/feasibility.md</span>
|
|
<span style="color:var(--text-muted);">Risk & Feasibility Critique</span>
|
|
</div>
|
|
{% for tr in idea.work_tracks %}
|
|
{% for out in tr.outputs %}
|
|
<div style="padding:0.4rem 0.6rem; background:rgba(99,102,241,0.08); border:1px solid rgba(99,102,241,0.2); border-radius:var(--radius-sm); display:flex; justify-content:space-between;">
|
|
<span>🚀 {{ out.artifact_path }} (v{{ out.version }})</span>
|
|
<span style="color:#a5b4fc;">{{ tr.name }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|