{% extends "base.html" %} {% block title %}Workspace{% endblock %} {% block breadcrumb %}Workspace{% endblock %} {% block content %} {# ── Stats Row ── #} {% if workspace.stats %}
{{ workspace.stats.total_modules }}
Modules
{{ workspace.stats.total_models }}
Models
{{ workspace.stats.total_controllers }}
Controllers
{{ workspace.stats.total_services }}
Services
{{ workspace.stats.total_integrations }}
Integrations
{% if workspace.stats.total_files %}
{{ workspace.stats.total_files }}
Files
{% endif %}
{% endif %} {# ── Tabs ── #}
{# ═══════════════════════════════════════════════════════════════════ #} {# ── TAB 1: Overview ── #} {# ═══════════════════════════════════════════════════════════════════ #}
{# ── Workspace Identity Card ── #}

{{ workspace.name or 'Workspace' }}

{% if workspace.version %}v{{ workspace.version }}{% endif %} {% if workspace.python_version %}Python {{ workspace.python_version }}{% endif %} {% if workspace.platform %}{{ workspace.platform }}{% endif %}
{% if workspace.description %}

{{ workspace.description }}

{% endif %} {# ── Project metadata ── #} {% if workspace.project_meta %}
{% for key, value in workspace.project_meta.items() %} {% if value %}
{{ key|replace('_', ' ')|title }}
{% if value is iterable and value is not string %}{{ value|join(', ') }}{% else %}{{ value }}{% endif %}
{% endif %} {% endfor %}
{% endif %}
{# ── Architecture Overview ── #}
Architecture Overview
{% for mod in workspace.modules %}
{{ mod.name }}
{% if mod.manifest and mod.manifest.version %}v{{ mod.manifest.version }}{% endif %}
{% if mod.manifest %}
{% if mod.manifest.controllers %}{{ mod.manifest.controllers|length }} ctrl{% endif %} {% if mod.manifest.services %}{{ mod.manifest.services|length }} svc{% endif %} {% if mod.manifest.models %}{{ mod.manifest.models|length }} model{% endif %} {% if mod.manifest.guards %}{{ mod.manifest.guards|length }} guard{% endif %}
{% endif %} {% if mod.manifest and mod.manifest.route_prefix %}
{{ mod.manifest.route_prefix }}
{% endif %}
{% endfor %} {% if not workspace.modules %}
No modules discovered. Create a modules/ directory.
{% endif %}
{# ── License Section (hidden by default) ── #} {% if workspace.license_text %}
License
{% if workspace.project_meta and workspace.project_meta.license %} {{ workspace.project_meta.license }} License {% else %} License {% endif %}
Click to view
{{ workspace.license_text }}
{% endif %}
{# ═══════════════════════════════════════════════════════════════════ #} {# ── TAB 2: Modules (expandable) ── #} {# ═══════════════════════════════════════════════════════════════════ #}
Modules ({{ workspace.modules|length }})
{% if workspace.modules and workspace.modules|length > 0 %} {% for mod in workspace.modules %}
{# Module Header #}
{{ mod.name }}
{% if mod.manifest %} {% set parts = [] %} {% if mod.manifest.controllers %}{% set _ = parts.append(mod.manifest.controllers|length ~ ' controller' ~ ('s' if mod.manifest.controllers|length != 1 else '')) %}{% endif %} {% if mod.manifest.services %}{% set _ = parts.append(mod.manifest.services|length ~ ' service' ~ ('s' if mod.manifest.services|length != 1 else '')) %}{% endif %} {% if mod.manifest.models %}{% set _ = parts.append(mod.manifest.models|length ~ ' model' ~ ('s' if mod.manifest.models|length != 1 else '')) %}{% endif %} {% if mod.manifest.guards %}{% set _ = parts.append(mod.manifest.guards|length ~ ' guard' ~ ('s' if mod.manifest.guards|length != 1 else '')) %}{% endif %} {{ parts|join(' · ') or 'Empty manifest' }} {% else %} No manifest found {% endif %}
{% if mod.manifest and mod.manifest.tags %} {% for tag in mod.manifest.tags[:3] %} {{ tag }} {% endfor %} {% endif %} {% if mod.manifest and mod.manifest.version %} v{{ mod.manifest.version }} {% endif %}
{# Module Body #}
{% if mod.manifest %}
{# ── Manifest Metadata Grid ── #}
{% if mod.manifest.route_prefix %}
Route Prefix
{{ mod.manifest.route_prefix }}
{% endif %} {% if mod.manifest.base_path %}
Base Path
{{ mod.manifest.base_path }}
{% endif %} {% if mod.manifest.fault_domain %}
Fault Domain
{{ mod.manifest.fault_domain }}
{% endif %} {% if mod.manifest.fault_strategy %}
Fault Strategy
{{ mod.manifest.fault_strategy }}
{% endif %} {% if mod.manifest.auto_discover is not none %}
Auto Discover
{{ 'Enabled' if mod.manifest.auto_discover else 'Disabled' }}
{% endif %} {% if mod.manifest.description %}
Description
{{ mod.manifest.description }}
{% endif %}
{# ── Component Lists ── #} {% set sections = [ ('Controllers', mod.manifest.controllers, 'icon-terminal', 'var(--info)'), ('Services', mod.manifest.services, 'icon-cpu', 'var(--purple)'), ('Models', mod.manifest.models, 'icon-database', 'var(--accent)'), ('Guards', mod.manifest.guards, 'icon-shield', 'var(--warning)'), ('Pipes', mod.manifest.pipes, 'icon-filter', '#ec4899'), ('Interceptors', mod.manifest.interceptors, 'icon-layers', '#06b6d4'), ('Imports', mod.manifest.imports, 'icon-download', '#6366f1'), ('Exports', mod.manifest.exports, 'icon-upload', '#14b8a6'), ] %} {% for section_name, items, icon, color in sections %} {% if items and items|length > 0 %}
{{ section_name }} ({{ items|length }})
{% for item in items %} {{ item }} {% endfor %}
{% endif %} {% endfor %}
{# ── Module Files ── #} {% if mod.files and mod.files|length > 0 %}
Files ({{ mod.files|length }})
{% for f in mod.files %}
{{ f.name }}
{{ f.kind }}
{% endfor %}
{% endif %} {# ── Manifest Source Code (collapsible) ── #} {% if mod.manifest and mod.manifest.source %}
Manifest Source
manifest.py
{{ mod.manifest.source_highlighted|default(mod.manifest.source)|safe }}
{% endif %} {% else %}
No manifest.py found in this module.
{% endif %}
{% endfor %} {% else %}
No modules discovered. Create a modules/ directory to get started.
{% endif %}
{# ═══════════════════════════════════════════════════════════════════ #} {# ── TAB 3: Integrations ── #} {# ═══════════════════════════════════════════════════════════════════ #}
Active Integrations ({{ workspace.integrations|length if workspace.integrations else 0 }})
{% if workspace.integrations and workspace.integrations|length > 0 %}
{% for intg in workspace.integrations %}
{{ intg.display_name|default(intg.name) }}
Integration.{{ intg.name }}()
{% if intg.params and intg.params|length > 0 %}
Parameters
{% for pk, pv in intg.params.items() %}
{{ pk }} {{ pv }}
{% endfor %}
{% endif %}
{% endfor %}
{% else %}
No integrations configured in workspace.py
{% endif %}
{# ═══════════════════════════════════════════════════════════════════ #} {# ── TAB 4: Registered ORM Models ── #} {# ═══════════════════════════════════════════════════════════════════ #}
Registered ORM Models ({{ workspace.registered_models|length if workspace.registered_models else 0 }})
{% if workspace.registered_models and workspace.registered_models|length > 0 %}
{% for m in workspace.registered_models %} {% endfor %}
Model Table Fields App
{{ m.name }} {{ m.table }} {{ m.field_count }} {{ m.app_label }}
{% else %}
No ORM models registered
{% endif %}
{# ═══════════════════════════════════════════════════════════════════ #} {# ── TAB 5: Source (workspace.py) ── #} {# ═══════════════════════════════════════════════════════════════════ #}
Workspace Configuration Source
{% if workspace.workspace_source %}
workspace.py
{{ workspace.workspace_source_highlighted|default(workspace.workspace_source)|safe }}
{% else %}
No workspace.py file found
{% endif %}
{% endblock %} {% block extra_js %} // ── Module expand / collapse ── function toggleModule(headerEl) { var card = headerEl.closest('.module-card'); if (!card) return; var body = card.querySelector('.module-body'); var chevron = card.querySelector('.module-chevron'); if (!body) return; var isCollapsed = body.getAttribute('data-collapsed') === 'true'; if (isCollapsed) { body.style.maxHeight = 'none'; body.style.overflow = 'visible'; body.setAttribute('data-collapsed', 'false'); if (chevron) chevron.style.transform = 'rotate(180deg)'; } else { body.style.maxHeight = '0'; body.style.overflow = 'hidden'; body.setAttribute('data-collapsed', 'true'); if (chevron) chevron.style.transform = 'rotate(0deg)'; // Also collapse any open manifest sources inside body.querySelectorAll('.manifest-source-body').forEach(function(src) { src.style.maxHeight = '0'; src.style.overflow = 'hidden'; var ch = src.previousElementSibling ? src.previousElementSibling.querySelector('.manifest-chevron') : null; if (ch) ch.style.transform = 'rotate(0deg)'; }); } } function toggleAllModules() { var bodies = document.querySelectorAll('.module-body'); var allOpen = Array.from(bodies).every(function(b) { return b.getAttribute('data-collapsed') === 'false'; }); bodies.forEach(function(body) { var card = body.closest('.module-card'); var chevron = card ? card.querySelector('.module-chevron') : null; if (allOpen) { body.style.maxHeight = '0'; body.style.overflow = 'hidden'; body.setAttribute('data-collapsed', 'true'); if (chevron) chevron.style.transform = 'rotate(0deg)'; } else { body.style.maxHeight = 'none'; body.style.overflow = 'visible'; body.setAttribute('data-collapsed', 'false'); if (chevron) chevron.style.transform = 'rotate(180deg)'; } }); } // ── License toggle ── function toggleLicense() { var body = document.getElementById('licenseBody'); var chevron = document.getElementById('licenseChevron'); var hint = document.getElementById('licenseToggleHint'); if (!body) return; if (body.style.maxHeight && body.style.maxHeight !== '0px') { body.style.maxHeight = '0'; if (chevron) chevron.style.transform = 'rotate(0deg)'; if (hint) hint.textContent = 'Click to view'; } else { body.style.maxHeight = body.scrollHeight + 'px'; if (chevron) chevron.style.transform = 'rotate(180deg)'; if (hint) hint.textContent = 'Click to hide'; } } // ── Manifest source toggle ── function toggleManifestSource(labelEl) { var body = labelEl.nextElementSibling; var chevron = labelEl.querySelector('.manifest-chevron'); if (!body) return; if (body.style.maxHeight && body.style.maxHeight !== '0px' && body.style.maxHeight !== '0') { body.style.maxHeight = '0'; body.style.overflow = 'hidden'; if (chevron) chevron.style.transform = 'rotate(0deg)'; } else { body.style.maxHeight = 'none'; body.style.overflow = 'visible'; if (chevron) chevron.style.transform = 'rotate(180deg)'; } } // ── Auto-expand first module on load ── document.addEventListener('DOMContentLoaded', function() { var firstCard = document.querySelector('.module-card'); if (firstCard) { var header = firstCard.querySelector('.module-header'); if (header) toggleModule(header); } }); // ── Animated stat counters ── document.querySelectorAll('.stat-number[data-count]').forEach(function(el) { var target = parseInt(el.getAttribute('data-count'), 10); if (isNaN(target) || target <= 0) return; var duration = 600; var start = performance.now(); el.textContent = '0'; function tick(now) { var t = Math.min((now - start) / duration, 1); var ease = 1 - Math.pow(1 - t, 3); el.textContent = Math.round(ease * target); if (t < 1) requestAnimationFrame(tick); } requestAnimationFrame(tick); }); // ── Keyboard: 'e' to expand/collapse all modules ── document.addEventListener('keydown', function(e) { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; if (e.key === 'e' && !e.ctrlKey && !e.metaKey) { toggleAllModules(); } }); {% endblock %}