{% extends "scitex_ui/standalone_shell.html" %} {% load static %} {% comment %} Extends scitex-ui's shared workspace shell, so scholar renders inside the same frame as every other leaf (scitex-storage, scitex-writer, figrecipe) instead of shipping its own document. Block names -- extra_css / app_content / extra_js -- were verified against the INSTALLED scitex_ui in site-packages, not read off a source tree and not guessed. NO VERSION NUMBER HERE ON PURPOSE. This comment used to say "verified against the INSTALLED scitex_ui 0.17.0". The installed version moved to 0.18.0 and the sentence did not, so it became a confident false claim that nothing could fail on -- and it did mislead: on 2026-08-23 I quoted that 0.17.0 to another package as a MEASUREMENT of what was installed here, when the real answer was 0.18.0 and I had read a comment instead of running `md.version("scitex-ui")`. A pinned version in prose is a fact with no owner and no test. The floor lives in pyproject.toml, where a resolver reads it; ask the interpreter for what is installed. DROPPED deliberately, because the shell already provides them: , , , charset, viewport, , and scitex-ui's own theme.css. Any of those repeated here would either duplicate or fight the shell. KEPT deliberately: the stx-mount marker below. The shell does NOT emit it (checked), so the mount contract remains scholar's to declare. {% endcomment %} {% block extra_css %} {% comment %} scitex-app's `stx-mount` contract: the server declares where this app is mounted so client code never has to guess. Always ends in "/", so JS joins endpoint names WITHOUT a leading slash and never normalises. Emitted here rather than by the SDK because `scitex_editor_page` -- which injects it automatically -- serves built SPA shells, and this is a Django-rendered template. {% endcomment %} {% comment %} NO |default FILTER. The root prefix is "" under scitex-app >= 0.8.0, and Django's `default` fires on FALSY -- so `|default:'/'` silently rewrote the correct "" back into the old "/", making every root request build "//api/..." : protocol-relative, off-origin. That is the exact failure the convention reversal exists to prevent, and it passed a test asserting the OLD value. This block used to be `{# ... #}`, which DJANGO ONLY STRIPS ON A SINGLE LINE -- a multi-line `{# #}` is emitted verbatim into the page. It was rendering as visible text at the top of the UI; the operator saw it in the browser before any test did. {% endcomment %} <meta name="stx-mount" content="{{ stx_mount }}"> <link rel="stylesheet" href="{% static 'scholar/css/scholar.css' %}"> {% endblock %} {% block app_content %} <!-- Header --> <header class="app-header"> <h1>SciTeX Scholar</h1> <span class="subtitle">Scientific Literature Management</span> </header> <div class="app-container"> <!-- Sidebar --> <aside class="app-sidebar"> <!-- Service Status --> <div class="sidebar-section"> <div class="sidebar-section__header"> <span class="sidebar-section__title">Service Status</span> </div> <div class="sidebar-section__content"> <div id="serviceStatus"> <span class="status-indicator status-checking">● Checking...</span> </div> </div> </div> <!-- Controls --> <div class="sidebar-section"> <div class="sidebar-section__header"> <span class="sidebar-section__title">Controls</span> </div> <div class="sidebar-section__content" style="font-size: 11px; color: var(--text-muted)"> <div style="margin-bottom: 6px;"> <kbd style="background: var(--bg-tertiary); border: 1px solid var(--border-default); padding: 1px 4px; font-size: 10px">Scroll</kbd> Zoom </div> <div style="margin-bottom: 6px;"> <kbd style="background: var(--bg-tertiary); border: 1px solid var(--border-default); padding: 1px 4px; font-size: 10px">Drag</kbd> Pan </div> <div> <kbd style="background: var(--bg-tertiary); border: 1px solid var(--border-default); padding: 1px 4px; font-size: 10px">Click</kbd> Select node </div> </div> </div> <!-- DB Info --> <div class="sidebar-section"> <div class="sidebar-section__header"> <span class="sidebar-section__title">CrossRef API</span> </div> <div class="sidebar-section__content" style="font-size: 11px; color: var(--text-muted); word-break: break-all"> {% if api_available %} <span style="color: var(--accent);">●</span> Configured <br> <small>{{ api_url }}</small> {% else %} <span style="color: var(--status-error);">●</span> Not configured <br> <small>No crossref-local endpoint detected</small> {% endif %} </div> </div> </aside> <!-- Main Content --> <main class="app-main"> <!-- Tab Navigation --> <nav class="tab-nav"> <button class="tab-btn active" data-tab="graph">Citation Graph</button> <button class="tab-btn" data-tab="library">Library</button> <button class="tab-btn" data-tab="search">Search</button> <button class="tab-btn" data-tab="enrichment">Enrichment</button> </nav> <!-- Citation Graph Tab --> <div id="tab-graph" class="tab-panel active"> <div class="citation-graph-container"> <!-- Input Form --> <div class="graph-card"> <div class="graph-card__header"> <span class="graph-card__title">Build Citation Network</span> </div> <div class="graph-card__body"> <p class="graph-description"> Enter a DOI to build an interactive citation network. The graph shows related papers based on bibliographic coupling, co-citation, and direct citations. </p> <form id="graphForm" class="graph-form"> <div class="form-row"> <div class="form-group--doi"> <label class="form-label" for="doiInput">DOI</label> <div class="input-wrapper"> <input type="text" id="doiInput" class="form-input" placeholder="e.g. 10.1038/s41586-020-2008-3" value="10.1038/s41586-020-2008-3"> <button type="submit" class="btn-build">Build Graph</button> </div> </div> <div class="form-group--options"> <label class="form-label" for="topN">Papers</label> <select id="topN" class="form-select"> <option value="10">10</option> <option value="20" selected>20</option> <option value="30">30</option> <option value="50">50</option> </select> </div> </div> </form> </div> </div> <!-- Loading --> <div id="graphLoading" class="graph-loading hidden"> <div class="loading-spinner"> <div class="spinner-ring"></div> <div class="spinner-ring"></div> <div class="spinner-ring"></div> </div> <div class="loading-text">Building citation network... This may take a moment.</div> </div> <!-- Error --> <div id="graphError" class="graph-error hidden"> <p id="graphErrorMessage">An error occurred</p> <button class="btn-build" onclick="document.getElementById('graphError').classList.add('hidden')"> Dismiss </button> </div> <!-- Visualization --> <div id="graphVisualization" class="hidden"> <div class="graph-card graph-card--full"> <div class="graph-card__header"> <span class="graph-card__title"> <span id="graphTitle">Citation Network</span> <small id="graphStats" style="font-weight: 400; color: var(--text-muted); margin-left: 12px"></small> </span> <div class="graph-controls"> <button id="fitViewBtn" class="btn-control" title="Fit to view">⇱</button> <button id="resetZoomBtn" class="btn-control" title="Reset zoom">↺</button> <button id="downloadSvgBtn" class="btn-control" title="Download SVG">⇩</button> </div> </div> <div class="graph-card__body" style="padding: 0; position: relative;"> <div id="graphCanvas" class="graph-canvas-wrapper"></div> <div id="nodeDetailsPanel" class="node-details-panel hidden"></div> </div> </div> </div> <!-- Related Papers --> <div id="relatedPapersList" class="hidden related-papers-section"> <div class="graph-card"> <div class="graph-card__header"> <span class="graph-card__title">Related Papers</span> </div> <div class="graph-card__body" id="relatedPapersContent"></div> </div> </div> </div> </div> <!-- Library Tab (placeholder) --> <div id="tab-library" class="tab-panel"> <div class="tab-placeholder"> <div> <h3>Library</h3> <p>Zotero-style paper management. Coming in Phase 2.</p> </div> </div> </div> <!-- Search Tab --> <div id="tab-search" class="tab-panel"> <div class="citation-graph-container"> <!-- Input Form --> <div class="graph-card"> <div class="graph-card__header"> <span class="graph-card__title">Search Papers</span> </div> <div class="graph-card__body"> <p class="graph-description"> Search the configured academic databases. Supports advanced syntax: <code>-word</code> excludes a term, <code>year:2020-2024</code> bounds the publication year, <code>if:>5</code> filters by impact factor. </p> <form id="searchForm" class="graph-form"> <div class="form-row"> <div class="form-group--doi"> <label class="form-label" for="searchInput">Query</label> <div class="input-wrapper"> <input type="text" id="searchInput" class="form-input" placeholder="e.g. hippocampus sharp wave -seizure year:2020-2024"> <button type="submit" class="btn-build">Search</button> </div> </div> <div class="form-group--options"> <label class="form-label" for="searchMaxResults">Results</label> <select id="searchMaxResults" class="form-select"> <option value="10">10</option> <option value="20" selected>20</option> <option value="50">50</option> <option value="100">100</option> </select> </div> </div> </form> </div> </div> <!-- Loading --> <div id="searchLoading" class="graph-loading hidden"> <div class="loading-spinner"> <div class="spinner-ring"></div> <div class="spinner-ring"></div> <div class="spinner-ring"></div> </div> <div class="loading-text">Searching databases... This may take a moment.</div> </div> <!-- Error --> <div id="searchError" class="graph-error hidden"> <p id="searchErrorMessage">An error occurred</p> <button class="btn-build" onclick="document.getElementById('searchError').classList.add('hidden')"> Dismiss </button> </div> <!-- Results --> <div id="searchResults" class="hidden related-papers-section"> <div class="graph-card"> <div class="graph-card__header"> <span class="graph-card__title"> Results <small id="searchStats" class="card-subtitle"></small> </span> </div> <div class="graph-card__body" id="searchResultsContent"></div> </div> </div> </div> </div> <!-- Enrichment Tab (placeholder) --> <div id="tab-enrichment" class="tab-panel"> <div class="tab-placeholder"> <div> <h3>Metadata Enrichment</h3> <p>Enrich BibTeX with abstracts, citations, and impact factors. Coming in Phase 4.</p> </div> </div> </div> </main> </div> <!-- Scripts (order matters: ForceSimulation < GraphRenderer < citation-graph) --> {% endblock %} {% block extra_js %} <!-- Must load FIRST: declares STX_MOUNT, which every other script uses. --> <script src="{% static 'scholar/js/stx-mount.js' %}"></script> <script src="{% static 'scholar/js/app.js' %}"></script> <script src="{% static 'scholar/js/search.js' %}"></script> <script src="{% static 'scholar/js/graph/ForceSimulation.js' %}"></script> <script src="{% static 'scholar/js/graph/GraphRenderer.js' %}"></script> <script src="{% static 'scholar/js/graph/citation-graph.js' %}"></script> {% endblock %}