Finished with Phase 2.6
This commit is contained in:
@@ -65,7 +65,9 @@
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="versions" role="tab">Versions</button>
|
||||
{% if animation_manifest %}<button class="tab-btn" data-tab="animations" role="tab">Animations</button>{% endif %}
|
||||
{% if project.category == 'modpack' %}<button class="tab-btn" data-tab="mods" role="tab">Mods</button>{% endif %}
|
||||
{% if models_manifest %}<button class="tab-btn" data-tab="models" role="tab">Models/Textures</button>{% endif %}
|
||||
{% if logical_manifest %}<button class="tab-btn" data-tab="logical" role="tab">Logical</button>{% endif %}
|
||||
{% if mods_manifest %}<button class="tab-btn" data-tab="mods" role="tab">Mods</button>{% endif %}
|
||||
<button class="tab-btn" data-tab="gallery" role="tab">Gallery</button>
|
||||
</div>
|
||||
|
||||
@@ -229,24 +231,129 @@
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if project.category == 'modpack' %}
|
||||
{% if models_manifest %}
|
||||
<section class="tab-panel" id="tab-models">
|
||||
<div class="card">
|
||||
<h2><i class="fas fa-cube"></i> Models & Textures</h2>
|
||||
<div class="model-grid">
|
||||
{% for model in models_manifest.models %}
|
||||
<div class="model-card">
|
||||
<div class="model-card-head">
|
||||
<strong>{{ model.name }}</strong>
|
||||
<button type="button" class="btn btn-secondary btn-sm model-render-btn"
|
||||
data-member="{{ model.member }}" data-name="{{ model.name }}"
|
||||
data-base-url="{% url 'library:pack_asset' project.slug versions.0.pk 'ASSET' %}"><i class="fas fa-cube"></i> Render</button>
|
||||
</div>
|
||||
<p class="model-meta">{{ model.bones }} bones · {{ model.cubes }} cubes</p>
|
||||
<div class="model-textures">
|
||||
{% for member in model.textures %}
|
||||
<img src="{% url 'library:pack_asset' project.slug versions.0.pk member %}" alt="{{ member }}" loading="lazy" title="{{ member }}">
|
||||
{% empty %}
|
||||
<span class="empty-hint">No textures</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if models_manifest.textures %}
|
||||
<h3 class="models-textures-title">Pack textures</h3>
|
||||
<div class="model-textures strip">
|
||||
{% for texture in models_manifest.textures %}
|
||||
<img src="{% url 'library:pack_asset' project.slug versions.0.pk texture %}" alt="{{ texture }}" loading="lazy" title="{{ texture }}">
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest %}
|
||||
<section class="tab-panel" id="tab-logical">
|
||||
<div class="card">
|
||||
<h2><i class="fas fa-box"></i> What this pack adds</h2>
|
||||
|
||||
{% if logical_manifest.items %}
|
||||
<h3>Items ({{ logical_manifest.items|length }})</h3>
|
||||
<div class="logical-items">
|
||||
{% for item in logical_manifest.items %}
|
||||
<span class="logical-item">
|
||||
{% if item.texture %}
|
||||
<img src="{% url 'library:pack_asset' project.slug versions.0.pk item.texture %}" alt="" loading="lazy">
|
||||
{% endif %}
|
||||
{{ item.name }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.recipes %}
|
||||
<h3>Recipes ({{ logical_manifest.recipes|length }})</h3>
|
||||
<ul class="logical-list">
|
||||
{% for r in logical_manifest.recipes %}
|
||||
<li>
|
||||
{% if r.disabled %}<span class="badge-disabled">disabled</span>{% endif %}
|
||||
<code>{{ r.ingredients|join:' + ' }}</code> → <code>{{ r.result }}</code>
|
||||
{% if r.type %}<small>({{ r.type }})</small>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.custom_recipes %}
|
||||
<h3>Custom crafting</h3>
|
||||
<ul class="logical-list">
|
||||
{% for r in logical_manifest.custom_recipes %}
|
||||
<li>{{ r.name }} → <code>{{ r.reward }}</code></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.advancements %}
|
||||
<h3>Advancements</h3>
|
||||
<ul class="logical-list">
|
||||
{% for a in logical_manifest.advancements %}
|
||||
<li>{{ a.name }}{% if a.reward %} → <code>{{ a.reward }}</code>{% endif %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.functions %}
|
||||
<h3>Functions ({{ logical_manifest.functions|length }}{% if logical_manifest.custom_functions %} + {{ logical_manifest.custom_functions }} custom{% endif %})</h3>
|
||||
<div class="logical-tags">{% for f in logical_manifest.functions %}<code>{{ f }}</code>{% endfor %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.liquid_gains %}
|
||||
<h3>Liquids</h3>
|
||||
<div class="logical-tags">{% for l in logical_manifest.liquid_gains %}<code>{{ l }}</code>{% endfor %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.entity_profiles %}
|
||||
<h3>Entity profiles</h3>
|
||||
<div class="logical-tags">{% for e in logical_manifest.entity_profiles %}<code>{{ e }}</code>{% endfor %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if logical_manifest.trinkets %}
|
||||
<h3>Trinkets</h3>
|
||||
<div class="logical-tags">{% for t in logical_manifest.trinkets %}<code>{{ t }}</code>{% endfor %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if mods_manifest %}
|
||||
<section class="tab-panel" id="tab-mods">
|
||||
<div class="card">
|
||||
<h2><i class="fas fa-cubes"></i> Mods</h2>
|
||||
{% for version in versions %}
|
||||
{% if version.mods_manifest.files %}
|
||||
<div class="mods-version">
|
||||
<h3>{{ version.version_name }} <span class="mods-source-badge">{{ version.mods_manifest.source }}</span></h3>
|
||||
<ul class="mods-list">
|
||||
{% for mod in version.mods_manifest.files %}
|
||||
<li><i class="fas fa-cube"></i> {{ mod.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<p class="empty-hint">No mods detected in the uploaded packs.</p>
|
||||
{% endfor %}
|
||||
{% if mods_manifest.files %}
|
||||
<span class="mods-source-badge">{{ mods_manifest.source }}</span>
|
||||
<ul class="mods-list">
|
||||
{% for mod in mods_manifest.files %}
|
||||
<li><i class="fas fa-cube"></i> {{ mod.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="empty-hint">No mods detected in the uploaded pack.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
@@ -256,12 +363,13 @@
|
||||
<h2><i class="fas fa-images"></i> Gallery</h2>
|
||||
<div class="gallery-grid">
|
||||
{% for asset in assets %}
|
||||
<figure class="gallery-item">
|
||||
{% if asset.is_video %}
|
||||
<video controls preload="metadata" src="{{ asset.file_url }}"></video>
|
||||
<figure class="gallery-item" data-media-url="{{ asset.file_url }}" data-media-type="{% if asset.is_video %}video{% else %}image{% endif %}" data-caption="{{ asset.caption }}">
|
||||
{% if asset.is_video or asset.is_gif %}
|
||||
<img class="gallery-thumb" src="{% url 'library:asset_thumbnail' project.slug asset.pk %}" alt="{{ asset.caption }}" loading="lazy">
|
||||
{% else %}
|
||||
<a href="{{ asset.file_url }}" target="_blank"><img src="{{ asset.file_url }}" alt="{{ asset.caption }}"></a>
|
||||
<img src="{{ asset.file_url }}" alt="{{ asset.caption }}" loading="lazy">
|
||||
{% endif %}
|
||||
<span class="gallery-expand" title="Open fullscreen"><i class="fas fa-expand"></i></span>
|
||||
{% if asset.caption %}<figcaption>{{ asset.caption }}</figcaption>{% endif %}
|
||||
{% if can_edit %}
|
||||
<a href="{% url 'library:asset_delete' project.slug asset.pk %}" class="gallery-delete" title="Delete media"><i class="fas fa-trash"></i></a>
|
||||
@@ -273,9 +381,30 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="modal-overlay" id="gallery-modal" hidden>
|
||||
<div class="modal-content media-modal-content">
|
||||
<button type="button" class="modal-close" data-close-modal aria-label="Close"><i class="fas fa-times"></i></button>
|
||||
<div class="modal-media" id="gallery-modal-media"></div>
|
||||
<p class="modal-caption" id="gallery-modal-caption"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="model-modal" hidden>
|
||||
<div class="modal-content model-modal-content">
|
||||
<button type="button" class="modal-close" data-close-modal aria-label="Close"><i class="fas fa-times"></i></button>
|
||||
<h3 class="model-modal-title" id="model-modal-title"></h3>
|
||||
<div id="model-viewer-container"></div>
|
||||
<p class="bio-help"><i class="fas fa-info-circle"></i> Drag to rotate · scroll to zoom</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% if models_manifest %}
|
||||
<script src="{% static 'vendor/three.min.js' %}"></script>
|
||||
<script src="{% static 'js/model_viewer.js' %}"></script>
|
||||
{% endif %}
|
||||
<script>
|
||||
(function () {
|
||||
const buttons = document.querySelectorAll('.tab-btn');
|
||||
@@ -332,6 +461,67 @@
|
||||
const first = guideBtns[0];
|
||||
if (first) loadGuide(first.dataset.url);
|
||||
}
|
||||
|
||||
// Full-screen gallery modal (images/gifs/videos; videos muted).
|
||||
const galleryModal = document.getElementById('gallery-modal');
|
||||
if (galleryModal) {
|
||||
const mediaEl = document.getElementById('gallery-modal-media');
|
||||
const captionEl = document.getElementById('gallery-modal-caption');
|
||||
document.querySelectorAll('.gallery-item[data-media-url]').forEach(item => {
|
||||
item.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.gallery-delete')) return;
|
||||
const url = item.dataset.mediaUrl;
|
||||
const type = item.dataset.mediaType;
|
||||
mediaEl.innerHTML = type === 'video'
|
||||
? '<video controls muted playsinline autoplay src="' + url + '"></video>'
|
||||
: '<img src="' + url + '" alt="">';
|
||||
captionEl.textContent = item.dataset.caption || '';
|
||||
galleryModal.hidden = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Close buttons for modals.
|
||||
document.querySelectorAll('[data-close-modal]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const modal = btn.closest('.modal-overlay');
|
||||
if (modal) {
|
||||
modal.hidden = true;
|
||||
const media = modal.querySelector('.modal-media');
|
||||
if (media) media.innerHTML = '';
|
||||
if (modal.id === 'model-modal') {
|
||||
const viewer = document.getElementById('model-viewer-container');
|
||||
if (viewer) viewer.innerHTML = '';
|
||||
if (window.PacksModelViewer) window.PacksModelViewer.dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.modal-overlay').forEach(modal => {
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
modal.hidden = true;
|
||||
if (modal.id === 'model-modal' && window.PacksModelViewer) window.PacksModelViewer.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Model renderer (Three.js showcase).
|
||||
if (window.PacksModelViewer) {
|
||||
document.querySelectorAll('.model-render-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.getElementById('model-modal-title').textContent = btn.dataset.name || '';
|
||||
const container = document.getElementById('model-viewer-container');
|
||||
container.innerHTML = '';
|
||||
window.PacksModelViewer.render(
|
||||
btn.dataset.member,
|
||||
container,
|
||||
btn.dataset.baseUrl
|
||||
);
|
||||
document.getElementById('model-modal').hidden = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user