Commit
Β·
f80ee45
1
Parent(s):
5ac4ed0
fix(docs): update technical debt documentation and add root cause analysis for NiiVue loading issue
Browse files- Revised the TECHNICAL_DEBT.md to reflect the current status of the NiiVue/WebGL integration, marking it as a P0 BLOCKER due to issues on HuggingFace Spaces.
- Added detailed sections outlining the critical issue with NiiVue not loading, including root causes and proposed solutions.
- Introduced new specifications for the Gradio Custom Component approach to resolve the loading issue, emphasizing the need for a structured solution.
- Documented the findings from the recent audits and analyses, providing a clear path forward for developers addressing the integration challenges.
These updates aim to enhance clarity and provide actionable insights for future development efforts.
- docs/TECHNICAL_DEBT.md +40 -4
- docs/specs/{GRADIO_WEBGL_ANALYSIS.md β 24-bug-gradio-webgl-analysis.md} +18 -2
- docs/specs/28-gradio-custom-component-niivue.md +4 -4
- docs/specs/29-codebase-status-audit.md +272 -0
- docs/specs/{AUDIT_JS_LOADING_ISSUES.md β archive/AUDIT_JS_LOADING_ISSUES.md} +0 -0
- docs/specs/{DIAGNOSTIC_HF_LOADING.md β archive/DIAGNOSTIC_HF_LOADING.md} +0 -0
- docs/specs/{ROOT_CAUSE_ANALYSIS.md β archive/ROOT_CAUSE_ANALYSIS.md} +0 -0
docs/TECHNICAL_DEBT.md
CHANGED
|
@@ -1,21 +1,55 @@
|
|
| 1 |
# Technical Debt and Known Issues
|
| 2 |
|
| 3 |
-
> **Last Audit**: December 2025 (Revision
|
| 4 |
> **Auditor**: Claude Code + External Senior Review
|
| 5 |
-
> **Status**:
|
| 6 |
|
| 7 |
## Summary
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
| Severity | Count | Description | Status |
|
| 12 |
|----------|-------|-------------|--------|
|
|
|
|
| 13 |
| P2 (Medium) | 0 | Temp dir leak, silent empty dataset, brittle git dep | **All Fixed** |
|
| 14 |
| P3 (Low) | 0 | SSRF vector, float64 memory, base64 overhead | **All Fixed** |
|
| 15 |
| P3 (Low) | 1 | Type ignores | **Acceptable** |
|
| 16 |
|
| 17 |
---
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
## Resolved Issues (Fixed in `fix/technical-debt`)
|
| 20 |
|
| 21 |
### β
P2: Silent Empty Dataset on Missing Data Directory
|
|
@@ -58,4 +92,6 @@ See: `docs/specs/19-perf-base64-to-file-urls.md`
|
|
| 58 |
|
| 59 |
## Conclusion
|
| 60 |
|
| 61 |
-
The codebase
|
|
|
|
|
|
|
|
|
| 1 |
# Technical Debt and Known Issues
|
| 2 |
|
| 3 |
+
> **Last Audit**: December 2025 (Revision 6)
|
| 4 |
> **Auditor**: Claude Code + External Senior Review
|
| 5 |
+
> **Status**: P0 BLOCKER - NiiVue/WebGL integration broken on HF Spaces
|
| 6 |
|
| 7 |
## Summary
|
| 8 |
|
| 9 |
+
**CRITICAL ISSUE**: The NiiVue 3D viewer does not work on HuggingFace Spaces due to Gradio architecture limitations. See Issue #24.
|
| 10 |
|
| 11 |
| Severity | Count | Description | Status |
|
| 12 |
|----------|-------|-------------|--------|
|
| 13 |
+
| **P0 (Critical)** | 1 | NiiVue/WebGL on HF Spaces | **BLOCKED** |
|
| 14 |
| P2 (Medium) | 0 | Temp dir leak, silent empty dataset, brittle git dep | **All Fixed** |
|
| 15 |
| P3 (Low) | 0 | SSRF vector, float64 memory, base64 overhead | **All Fixed** |
|
| 16 |
| P3 (Low) | 1 | Type ignores | **Acceptable** |
|
| 17 |
|
| 18 |
---
|
| 19 |
|
| 20 |
+
## P0 BLOCKER: NiiVue/WebGL on HuggingFace Spaces (Issue #24)
|
| 21 |
+
|
| 22 |
+
### Problem
|
| 23 |
+
|
| 24 |
+
The Interactive 3D Viewer (NiiVue) causes the entire HF Spaces app to hang on "Loading..." forever.
|
| 25 |
+
|
| 26 |
+
### Root Cause
|
| 27 |
+
|
| 28 |
+
**Gradio does not natively support custom WebGL content.** All hack attempts have **FAILED** (confirmed Dec 10, 2025):
|
| 29 |
+
|
| 30 |
+
| Attempt | Date | Result |
|
| 31 |
+
|---------|------|--------|
|
| 32 |
+
| CDN import in js_on_load | Dec 9 | FAILED - CSP blocked |
|
| 33 |
+
| Vendored + import() in js_on_load | Dec 9 | FAILED - Blocks Svelte hydration |
|
| 34 |
+
| head_paths with loader HTML | Dec 9 | FAILED - Same issue |
|
| 35 |
+
| head= with inline import() | Dec 10 | **FAILED** - Confirmed DOA |
|
| 36 |
+
|
| 37 |
+
Gradio maintainers explicitly closed Issues #4511 (NIfTI support) and #7649 (WebGL canvas) as "not planned", recommending Custom Components instead.
|
| 38 |
+
|
| 39 |
+
**There is no gr.HTML hack that works. The only path forward is a Gradio Custom Component.**
|
| 40 |
+
|
| 41 |
+
### Solution
|
| 42 |
+
|
| 43 |
+
Build a **Gradio Custom Component** that properly wraps NiiVue using Svelte.
|
| 44 |
+
|
| 45 |
+
See: `docs/specs/28-gradio-custom-component-niivue.md`
|
| 46 |
+
|
| 47 |
+
### Workaround (Current)
|
| 48 |
+
|
| 49 |
+
The "Static Report" tab (Matplotlib 2D slices) works correctly. Only the "Interactive 3D" tab is broken.
|
| 50 |
+
|
| 51 |
+
---
|
| 52 |
+
|
| 53 |
## Resolved Issues (Fixed in `fix/technical-debt`)
|
| 54 |
|
| 55 |
### β
P2: Silent Empty Dataset on Missing Data Directory
|
|
|
|
| 92 |
|
| 93 |
## Conclusion
|
| 94 |
|
| 95 |
+
The codebase is **production-ready for all features EXCEPT the Interactive 3D Viewer (NiiVue)**. All other technical debt items are resolved.
|
| 96 |
+
|
| 97 |
+
**Next step:** Implement Gradio Custom Component per spec #28 to fix the P0 blocker.
|
docs/specs/{GRADIO_WEBGL_ANALYSIS.md β 24-bug-gradio-webgl-analysis.md}
RENAMED
|
@@ -1,7 +1,23 @@
|
|
| 1 |
-
# Gradio + WebGL/NiiVue Analysis
|
| 2 |
|
| 3 |
**Date:** 2025-12-10
|
| 4 |
-
**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
---
|
| 7 |
|
|
|
|
| 1 |
+
# Bug #24: Gradio + WebGL/NiiVue Root Cause Analysis
|
| 2 |
|
| 3 |
**Date:** 2025-12-10
|
| 4 |
+
**Status:** ALL HACKS FAILED - Custom Component Required
|
| 5 |
+
**Issue:** HF Spaces stuck on "Loading..." forever
|
| 6 |
+
**Root Cause:** Gradio does not natively support custom WebGL content
|
| 7 |
+
**Solution:** Build Gradio Custom Component (see spec #28)
|
| 8 |
+
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
## CONFIRMED: All gr.HTML Hacks Have Failed
|
| 12 |
+
|
| 13 |
+
| Attempt | Date | Result |
|
| 14 |
+
|---------|------|--------|
|
| 15 |
+
| CDN import in js_on_load | Dec 9 | FAILED - CSP blocks external imports |
|
| 16 |
+
| Vendored + dynamic import() in js_on_load | Dec 9 | FAILED - Blocks Svelte hydration |
|
| 17 |
+
| head_paths with loader HTML | Dec 9 | FAILED - Same hydration issue |
|
| 18 |
+
| head= with inline import() | Dec 10 | **FAILED** - Confirmed DOA |
|
| 19 |
+
|
| 20 |
+
**There is no hack that works.** The only path forward is spec #28 (Gradio Custom Component).
|
| 21 |
|
| 22 |
---
|
| 23 |
|
docs/specs/28-gradio-custom-component-niivue.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# Spec #28: Gradio Custom Component for NiiVue
|
| 2 |
|
| 3 |
**Date:** 2025-12-10
|
| 4 |
-
**Status:**
|
| 5 |
**Blocks:** Issue #24 (HF Spaces "Loading..." forever)
|
| 6 |
**Effort:** Medium (2-3 days)
|
| 7 |
**Success Probability:** 90%
|
|
@@ -10,7 +10,7 @@
|
|
| 10 |
|
| 11 |
## Executive Summary
|
| 12 |
|
| 13 |
-
**
|
| 14 |
|
| 15 |
Gradio maintainers have explicitly closed both:
|
| 16 |
- [Issue #4511](https://github.com/gradio-app/gradio/issues/4511) - NIfTI/medical imaging support β "Not planned"
|
|
@@ -301,8 +301,8 @@ viewer = NiiVueViewer(label="Interactive 3D Viewer")
|
|
| 301 |
|
| 302 |
### Alternative 1: Keep Hacking gr.HTML
|
| 303 |
- **Effort:** Low
|
| 304 |
-
- **Success probability:**
|
| 305 |
-
- **Why rejected:** We
|
| 306 |
|
| 307 |
### Alternative 2: Static HTML Space (No Gradio)
|
| 308 |
- **Effort:** High (rebuild entire UI)
|
|
|
|
| 1 |
# Spec #28: Gradio Custom Component for NiiVue
|
| 2 |
|
| 3 |
**Date:** 2025-12-10
|
| 4 |
+
**Status:** REQUIRED - All gr.HTML hacks have failed (confirmed Dec 10)
|
| 5 |
**Blocks:** Issue #24 (HF Spaces "Loading..." forever)
|
| 6 |
**Effort:** Medium (2-3 days)
|
| 7 |
**Success Probability:** 90%
|
|
|
|
| 10 |
|
| 11 |
## Executive Summary
|
| 12 |
|
| 13 |
+
**All `gr.HTML` + JavaScript approaches have FAILED. This is the only path forward.**
|
| 14 |
|
| 15 |
Gradio maintainers have explicitly closed both:
|
| 16 |
- [Issue #4511](https://github.com/gradio-app/gradio/issues/4511) - NIfTI/medical imaging support β "Not planned"
|
|
|
|
| 301 |
|
| 302 |
### Alternative 1: Keep Hacking gr.HTML
|
| 303 |
- **Effort:** Low
|
| 304 |
+
- **Success probability:** 0% (CONFIRMED FAILED)
|
| 305 |
+
- **Why rejected:** We tried 6 approaches over 2 days. ALL failed. This is not a viable path.
|
| 306 |
|
| 307 |
### Alternative 2: Static HTML Space (No Gradio)
|
| 308 |
- **Effort:** High (rebuild entire UI)
|
docs/specs/29-codebase-status-audit.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Spec #29: Codebase Status Audit (Issue #24 NiiVue/WebGL)
|
| 2 |
+
|
| 3 |
+
**Date:** 2025-12-10
|
| 4 |
+
**Status:** ALL HACKS CONFIRMED FAILED (Dec 10, 2025)
|
| 5 |
+
**Purpose:** Top-down analysis of current frontend/NiiVue implementation state after multiple hotfix attempts
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## Executive Summary: All gr.HTML Hacks Have Failed
|
| 10 |
+
|
| 11 |
+
After 6 iterations of attempted hotfixes for Issue #24 (HF Spaces "Loading..." forever), **every approach has failed**:
|
| 12 |
+
|
| 13 |
+
| Attempt | Result |
|
| 14 |
+
|---------|--------|
|
| 15 |
+
| CDN import | FAILED - CSP blocked |
|
| 16 |
+
| Vendored + js_on_load import() | FAILED - Blocks hydration |
|
| 17 |
+
| head_paths | FAILED - Same issue |
|
| 18 |
+
| head= with import() | **FAILED** - Confirmed Dec 10 |
|
| 19 |
+
|
| 20 |
+
**The codebase contains dead code from all these failed attempts.** The correct solution is Gradio Custom Component (spec #28).
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
## Current Frontend Architecture
|
| 25 |
+
|
| 26 |
+
### File Inventory
|
| 27 |
+
|
| 28 |
+
| File | Purpose | Lines | Status |
|
| 29 |
+
|------|---------|-------|--------|
|
| 30 |
+
| `ui/viewer.py` | NiiVue HTML/JS generation | 643 | **BLOATED** - contains 5 approaches |
|
| 31 |
+
| `ui/app.py` | Main Gradio app | 313 | Clean |
|
| 32 |
+
| `ui/components.py` | UI components | 94 | Clean |
|
| 33 |
+
| `app.py` (root) | Local dev entry | 61 | Clean |
|
| 34 |
+
| `ui/assets/niivue.js` | Vendored NiiVue v0.65.0 | 2.9MB | **NECESSARY** |
|
| 35 |
+
|
| 36 |
+
### What's in `viewer.py` Right Now
|
| 37 |
+
|
| 38 |
+
| Component | Lines | Status | Notes |
|
| 39 |
+
|-----------|-------|--------|-------|
|
| 40 |
+
| `NIIVUE_VERSION` | 30 | OK | Version tracking |
|
| 41 |
+
| `_ASSET_DIR`, `_NIIVUE_JS_PATH` | 31-32 | OK | Path constants |
|
| 42 |
+
| `NIIVUE_JS_URL` | 36 | **UNUSED** | Computed but not actually used |
|
| 43 |
+
| Module-level logging | 39-42 | **SLOP** | 4 log statements at import time |
|
| 44 |
+
| `get_niivue_head_html()` | 45-77 | **PROBLEMATIC** | Still uses `await import()` |
|
| 45 |
+
| `get_niivue_loader_path()` | 80-109 | **DEPRECATED** | Marked deprecated but still exists |
|
| 46 |
+
| `nifti_to_gradio_url()` | 112-142 | OK | Issue #19 fix, working |
|
| 47 |
+
| `get_slice_at_max_lesion()` | 145-187 | OK | Matplotlib helper |
|
| 48 |
+
| `render_3panel_view()` | 190-281 | OK | Matplotlib 3-panel |
|
| 49 |
+
| `render_slice_comparison()` | 284-380 | OK | Matplotlib comparison |
|
| 50 |
+
| `create_niivue_html()` | 383-434 | OK | HTML generation |
|
| 51 |
+
| `NIIVUE_ON_LOAD_JS` | 449-538 | **MOSTLY OK** | No import(), uses window.Niivue |
|
| 52 |
+
| `NIIVUE_UPDATE_JS` | 546-642 | **MOSTLY OK** | No import(), uses window.Niivue |
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## The Core Problem: `get_niivue_head_html()` Still Uses `import()`
|
| 57 |
+
|
| 58 |
+
The current "fix" in `get_niivue_head_html()` does this:
|
| 59 |
+
|
| 60 |
+
```javascript
|
| 61 |
+
// viewer.py:63-76
|
| 62 |
+
<script type="module">
|
| 63 |
+
try {
|
| 64 |
+
const niivueUrl = '{NIIVUE_JS_URL}';
|
| 65 |
+
console.log('[NiiVue Loader] Attempting to load from:', niivueUrl);
|
| 66 |
+
const { Niivue } = await import(niivueUrl); // <-- SAME BROKEN PATTERN!
|
| 67 |
+
window.Niivue = Niivue;
|
| 68 |
+
console.log('[NiiVue Loader] Successfully loaded');
|
| 69 |
+
} catch (error) {
|
| 70 |
+
console.error('[NiiVue Loader] FAILED to load:', error);
|
| 71 |
+
window.NIIVUE_LOAD_ERROR = error.message;
|
| 72 |
+
}
|
| 73 |
+
</script>
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
**This is the EXACT same `await import()` pattern that breaks on HF Spaces.**
|
| 77 |
+
|
| 78 |
+
The only difference from our previous attempts:
|
| 79 |
+
- Before: `await import()` in `js_on_load`
|
| 80 |
+
- Now: `await import()` in `head=` script
|
| 81 |
+
|
| 82 |
+
**Why this might not matter:** The A/B test proved that `js_on_load` with async code breaks Gradio. Moving the `import()` to `head=` might help, but it's still executing async code that could fail silently and leave `window.Niivue` undefined.
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
## What's Necessary vs What's Slop
|
| 87 |
+
|
| 88 |
+
### NECESSARY (Keep)
|
| 89 |
+
|
| 90 |
+
| Item | Why |
|
| 91 |
+
|------|-----|
|
| 92 |
+
| `ui/assets/niivue.js` | HF Spaces CSP blocks CDN imports |
|
| 93 |
+
| `gr.set_static_paths()` | Required for Gradio 6.x file serving |
|
| 94 |
+
| `nifti_to_gradio_url()` | Issue #19 fix, working |
|
| 95 |
+
| `create_niivue_html()` | Generates viewer HTML |
|
| 96 |
+
| `NIIVUE_ON_LOAD_JS` | Initializes viewer (doesn't import) |
|
| 97 |
+
| `NIIVUE_UPDATE_JS` | Re-initializes after updates |
|
| 98 |
+
| Matplotlib functions | Working 2D fallback |
|
| 99 |
+
| `allowed_paths` in launch() | Runtime file access |
|
| 100 |
+
|
| 101 |
+
### SLOP (Should Remove/Refactor)
|
| 102 |
+
|
| 103 |
+
| Item | Why It's Slop |
|
| 104 |
+
|------|---------------|
|
| 105 |
+
| `NIIVUE_JS_URL` module-level computation | Computed but unused in production |
|
| 106 |
+
| Module-level logging (lines 39-42) | Noisy startup logs, not useful |
|
| 107 |
+
| `get_niivue_loader_path()` | Deprecated, generates file we don't need |
|
| 108 |
+
| `get_niivue_head_html()` with import() | Still uses broken pattern |
|
| 109 |
+
| Multiple diagnostic docs | Overlapping, contradictory, stale |
|
| 110 |
+
|
| 111 |
+
### UNCERTAIN (Depends on head= fix working)
|
| 112 |
+
|
| 113 |
+
| Item | Status |
|
| 114 |
+
|------|--------|
|
| 115 |
+
| `head=get_niivue_head_html()` in launch() | **30% chance this works** |
|
| 116 |
+
|
| 117 |
+
---
|
| 118 |
+
|
| 119 |
+
## Documentation Status
|
| 120 |
+
|
| 121 |
+
### docs/specs/ Files
|
| 122 |
+
|
| 123 |
+
| File | Status | Issue |
|
| 124 |
+
|------|--------|-------|
|
| 125 |
+
| `00-context.md` | **ACCURATE** | None |
|
| 126 |
+
| `28-gradio-custom-component-niivue.md` | **ACCURATE** | Just written |
|
| 127 |
+
| `AUDIT_JS_LOADING_ISSUES.md` | **OUTDATED** | Says `set_static_paths` is blocker, but we've moved past that |
|
| 128 |
+
| `DIAGNOSTIC_HF_LOADING.md` | **OUTDATED** | Lists hypotheses we've since disproven |
|
| 129 |
+
| `ROOT_CAUSE_ANALYSIS.md` | **PARTIALLY OUTDATED** | Says "IN PROGRESS", discusses head= as solution |
|
| 130 |
+
| `GRADIO_WEBGL_ANALYSIS.md` | **ACCURATE** | Core analysis, identifies real problem |
|
| 131 |
+
|
| 132 |
+
### docs/TECHNICAL_DEBT.md
|
| 133 |
+
|
| 134 |
+
| Status | Issue |
|
| 135 |
+
|--------|-------|
|
| 136 |
+
| **OUTDATED** | Claims "Ironclad/Production-Ready" but doesn't mention P0 NiiVue/WebGL blocker |
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## Recommended Cleanup Actions
|
| 141 |
+
|
| 142 |
+
### Immediate (If head= fix fails)
|
| 143 |
+
|
| 144 |
+
1. **Delete deprecated code:**
|
| 145 |
+
- Remove `get_niivue_loader_path()`
|
| 146 |
+
- Remove module-level logging
|
| 147 |
+
- Clean up `NIIVUE_JS_URL` if unused
|
| 148 |
+
|
| 149 |
+
2. **Archive old diagnostic docs:**
|
| 150 |
+
- Move `AUDIT_JS_LOADING_ISSUES.md` to `archive/`
|
| 151 |
+
- Move `DIAGNOSTIC_HF_LOADING.md` to `archive/`
|
| 152 |
+
- Update `ROOT_CAUSE_ANALYSIS.md` status
|
| 153 |
+
|
| 154 |
+
3. **Update TECHNICAL_DEBT.md:**
|
| 155 |
+
- Add P0 section for NiiVue/WebGL blocker
|
| 156 |
+
- Link to spec #28 (Custom Component)
|
| 157 |
+
|
| 158 |
+
### Long-term (After decision on path forward)
|
| 159 |
+
|
| 160 |
+
1. **If Custom Component route:**
|
| 161 |
+
- Remove all `head=` NiiVue loading code
|
| 162 |
+
- Remove `get_niivue_head_html()`
|
| 163 |
+
- Simplify `viewer.py` to just Matplotlib functions
|
| 164 |
+
- NiiVue loading becomes the component's responsibility
|
| 165 |
+
|
| 166 |
+
2. **If 2D fallback route:**
|
| 167 |
+
- Remove entire NiiVue integration
|
| 168 |
+
- Remove `ui/assets/niivue.js` (2.9MB)
|
| 169 |
+
- Remove `NIIVUE_ON_LOAD_JS`, `NIIVUE_UPDATE_JS`
|
| 170 |
+
- Keep only Matplotlib rendering
|
| 171 |
+
|
| 172 |
+
---
|
| 173 |
+
|
| 174 |
+
## Honest Assessment
|
| 175 |
+
|
| 176 |
+
### What We've Tried (6+ iterations)
|
| 177 |
+
|
| 178 |
+
1. **CDN import** β Blocked by CSP
|
| 179 |
+
2. **Vendored + dynamic import in js_on_load** β Blocks Svelte hydration
|
| 180 |
+
3. **head_paths with loader HTML** β Complex, didn't work
|
| 181 |
+
4. **head= with inline import()** β Current state, **probably won't work**
|
| 182 |
+
5. **Various set_static_paths/allowed_paths combos** β File serving works, JS loading doesn't
|
| 183 |
+
|
| 184 |
+
### The Pattern
|
| 185 |
+
|
| 186 |
+
Every attempt has been a variation of:
|
| 187 |
+
> "Load NiiVue via some JavaScript mechanism within Gradio"
|
| 188 |
+
|
| 189 |
+
Every attempt has failed because:
|
| 190 |
+
> **Gradio was not designed for custom WebGL content**
|
| 191 |
+
|
| 192 |
+
### The Correct Solution
|
| 193 |
+
|
| 194 |
+
**Stop fighting Gradio's architecture. Use a Gradio Custom Component.**
|
| 195 |
+
|
| 196 |
+
This is:
|
| 197 |
+
- What Gradio maintainers recommend (Issues #4511, #7649)
|
| 198 |
+
- How existing WebGL components work (gradio-litmodel3d)
|
| 199 |
+
- 90% success probability vs 30% for more hacks
|
| 200 |
+
|
| 201 |
+
See spec #28 for implementation details.
|
| 202 |
+
|
| 203 |
+
---
|
| 204 |
+
|
| 205 |
+
## Current Entry Point Flow
|
| 206 |
+
|
| 207 |
+
```
|
| 208 |
+
HF Spaces Docker
|
| 209 |
+
β
|
| 210 |
+
CMD ["python", "-m", "stroke_deepisles_demo.ui.app"]
|
| 211 |
+
β
|
| 212 |
+
ui/app.py __main__ block
|
| 213 |
+
β
|
| 214 |
+
gr.set_static_paths([_ASSETS_DIR]) # Enable file serving
|
| 215 |
+
β
|
| 216 |
+
get_demo() # Creates Blocks with js_on_load components
|
| 217 |
+
β
|
| 218 |
+
demo.launch(
|
| 219 |
+
head=get_niivue_head_html(), # <-- Injects <script type="module"> with import()
|
| 220 |
+
allowed_paths=[_ASSETS_DIR],
|
| 221 |
+
)
|
| 222 |
+
β
|
| 223 |
+
Browser loads page
|
| 224 |
+
β
|
| 225 |
+
<head> script runs: await import('/gradio_api/file=.../niivue.js')
|
| 226 |
+
β
|
| 227 |
+
[UNCERTAIN] Does import() succeed? Does it block Svelte?
|
| 228 |
+
β
|
| 229 |
+
If yes: window.Niivue is set, js_on_load works
|
| 230 |
+
If no: window.Niivue undefined, viewer shows error
|
| 231 |
+
```
|
| 232 |
+
|
| 233 |
+
---
|
| 234 |
+
|
| 235 |
+
## Files Modified During Issue #24 Debug
|
| 236 |
+
|
| 237 |
+
| File | Changes | Commits |
|
| 238 |
+
|------|---------|---------|
|
| 239 |
+
| `viewer.py` | ~6 rewrites of JS loading approach | Multiple |
|
| 240 |
+
| `ui/app.py` | Added head=, set_static_paths | Multiple |
|
| 241 |
+
| `app.py` | Same as ui/app.py | Multiple |
|
| 242 |
+
| `ui/assets/niivue.js` | Added vendored library | 1 |
|
| 243 |
+
| `.gitignore` | Added niivue-loader.html | 1 |
|
| 244 |
+
| `.pre-commit-config.yaml` | Exclude assets/ from large file check | 1 |
|
| 245 |
+
|
| 246 |
+
---
|
| 247 |
+
|
| 248 |
+
## Conclusion
|
| 249 |
+
|
| 250 |
+
**The codebase is messy but not unfixable.** The mess comes from iterating through multiple failed approaches without cleaning up between attempts.
|
| 251 |
+
|
| 252 |
+
**The real issue is architectural:** Gradio + custom WebGL = unsupported pattern.
|
| 253 |
+
|
| 254 |
+
**Next steps:**
|
| 255 |
+
1. Test if current `head=` approach works on HF Spaces (low confidence)
|
| 256 |
+
2. If it fails, implement Gradio Custom Component (spec #28)
|
| 257 |
+
3. Clean up cruft regardless of which path we take
|
| 258 |
+
|
| 259 |
+
---
|
| 260 |
+
|
| 261 |
+
## Appendix: How to Verify Current State
|
| 262 |
+
|
| 263 |
+
```bash
|
| 264 |
+
# Check if NiiVue file serving works
|
| 265 |
+
curl -I "https://[space-url]/gradio_api/file=/home/user/demo/src/stroke_deepisles_demo/ui/assets/niivue.js"
|
| 266 |
+
# Should return 200 OK with application/javascript
|
| 267 |
+
|
| 268 |
+
# Check browser console for:
|
| 269 |
+
# - "[NiiVue Loader] Attempting to load from: ..."
|
| 270 |
+
# - "[NiiVue Loader] Successfully loaded" OR "[NiiVue Loader] FAILED"
|
| 271 |
+
# - Any errors during Gradio initialization
|
| 272 |
+
```
|
docs/specs/{AUDIT_JS_LOADING_ISSUES.md β archive/AUDIT_JS_LOADING_ISSUES.md}
RENAMED
|
File without changes
|
docs/specs/{DIAGNOSTIC_HF_LOADING.md β archive/DIAGNOSTIC_HF_LOADING.md}
RENAMED
|
File without changes
|
docs/specs/{ROOT_CAUSE_ANALYSIS.md β archive/ROOT_CAUSE_ANALYSIS.md}
RENAMED
|
File without changes
|