Project Structure
A summary of how HTML templates, CSS, and JavaScript interact in this project.
Overview
This is a Flask application with a single-page frontend. The server renders one HTML page via Jinja2 templates, loads all CSS globally, and boots a single ES module entry point. All subsequent UI interaction is handled in JavaScript without page reloads. The application also serves several standalone pages (landing, account, presentation, etc.) with their own templates and stylesheets.
File Map
templates/ Jinja2 HTML templates
├── index.html Root app page — loads CSS, loads main.js
├── sidebar.html Sidebar partial
├── workspace.html Workspace partial
├── start_page.html Empty-state page partial
├── landing.html Public landing/marketing page
├── account.html User account management page
├── admin.html Admin panel page
├── donate.html Donation page
├── downloads.html Downloads page
├── faq.html FAQ page
├── license.html License page
├── privacy.html Privacy policy page
├── terms.html Terms of service page
├── changelog.html Changelog detail page
├── changelog_index.html Changelog index page
├── presentation.html Public presentation view
├── presentation_folder.html Public presentation folder view
├── components/
│ └── shortcut_legend.html Keyboard shortcut reference
├── workspace_panels/
│ ├── waveform_panel.html Audio player and waveform
│ ├── speakers_panel.html Speaker list and management
│ └── transcript_panel.html Transcript editor
├── partials/
│ ├── _app_icon_svg.html App icon SVG include
│ ├── _title_svg.html Title SVG include
│ ├── _landing_header.html Landing page header
│ ├── _landing_footer.html Landing page footer
│ ├── _landing_modals.html Landing page modals
│ ├── _report_modal.html Report/abuse modal
│ └── _signup_modal.html Signup/registration modal
└── documentation/
├── index.html Documentation index
└── guide/ User guide pages (standalone)
static/css/
├── variables.css Global CSS custom properties (colors, dark + light theme)
├── main.css Base/global styles
├── sidebar.css Sidebar layout (includes folder breadcrumb styles)
├── start_page.css Empty-state page styles
├── icons.css Icon styles
├── landing.css Landing page styles
├── account.css Account page styles
├── admin.css Admin page styles
├── downloads.css Downloads page styles
├── faq.css FAQ page styles
├── changelog.css Changelog page styles
├── presentation.css Presentation view styles
├── components/
│ ├── context_menu.css Base context menu styles
│ ├── confirm_dialog.css
│ ├── donate_dialog.css
│ ├── download_modal.css
│ ├── embed_dialog.css
│ ├── export_panel.css
│ ├── hue_picker.css
│ ├── info_widget.css
│ ├── live_quotes_dialog.css
│ ├── login_dialog.css
│ ├── report_modal.css
│ ├── segment_context_menu.css
│ ├── share_dialog.css
│ ├── shortcut_legend.css
│ ├── signup_modal.css
│ ├── split_popup.css
│ ├── transcribe_dialog.css
│ └── tutorial_dialog.css
├── workspace/
│ ├── workspace.css
│ ├── waveform_panel.css
│ ├── speakers_panel.css
│ └── transcript_panel.css
└── documentation/
└── guide.css
static/js/
├── main.js Entry point — App class, sidebar, project list, folder navigation
├── project.js Project, ProjectData, Transcript, Speaker classes
├── workspace.js Workspace — coordinates the three panels
├── server.js Server class — connection state, auth, and all server operations
├── start_page.js StartPage class
├── firebase.js Firebase auth integration (cloud mode)
├── account.js Account page logic
├── admin.js Admin page logic
├── donate.js Donation page logic
├── presentation.js Presentation view logic
├── presentation_folder.js Presentation folder view logic
├── transcription_manager.js Transcription state and progress management
├── workspace_panels/
│ ├── waveform_panel.js WaveformPanel — audio playback, waveform, regions
│ ├── speakers_panel.js SpeakersPanel — speaker list and editing
│ └── transcript_panel.js TranscriptPanel — transcript display and editing
├── components/
│ ├── context_menu.js Base ContextMenu class
│ ├── apply_effect_dialog.js ApplyEffectDialog — audio effect application
│ ├── confirm_dialog.js ConfirmDialog — modal confirmation
│ ├── effect_context_menu.js EffectContextMenu — right-click menu on effects
│ ├── embed_dialog.js EmbedDialog — quote embed creation
│ ├── export_panel.js ExportPanel — export format selection and download
│ ├── hue_picker.js HuePicker — speaker color selection
│ ├── hyperlink_dialog.js HyperlinkDialog — hyperlink editing in transcript
│ ├── info_widget.js InfoWidget — toast/notification display
│ ├── link_context_menu.js LinkContextMenu — right-click menu on links
│ ├── live_quotes_dialog.js LiveQuotesDialog — live quote embedding feature
│ ├── login_dialog.js LoginDialog — Firebase login UI
│ ├── project_context_menu.js ProjectContextMenu — right-click menu on projects
│ ├── section_context_menu.js SectionContextMenu — right-click menu on sections
│ ├── segment_context_menu.js SegmentContextMenu — right-click menu on segments
│ ├── selection_context_menu.js SelectionContextMenu — right-click menu on text selections
│ ├── share_dialog.js ShareDialog — project sharing
│ ├── speaker_context_menu.js SpeakerContextMenu — right-click menu on speakers
│ ├── split_popup.js SplitPopup — word-level segment splitting
│ ├── stale_context_menu.js StaleContextMenu — stale content resolution
│ ├── tooltip.js Tooltip — hover tooltips
│ ├── transcribe_dialog.js TranscribeDialog — transcription options and progress
│ ├── tutorial_dialog.js TutorialDialog — onboarding tutorial
│ └── version_history_dialog.js VersionHistoryDialog — project version history browser
├── utilities/
│ ├── tools.js General utility functions
│ ├── audio.js Audio loading helpers
│ ├── colors.js Color manipulation utilities
│ ├── constants.js Shared constants (GAP_THRESHOLD, SPEAKER_COLORS, etc.)
│ ├── export.js Export formatting helpers (PDF, DOCX, TXT, CSV, MD)
│ ├── history_manager.js Undo/redo history management
│ ├── theme.js Theme switching (dark/light/auto) helpers
│ ├── transcription_pricing.js Cloud transcription cost estimation
│ ├── version_manager.js Client-side version management helpers
│ └── server_access.js Low-level fetch functions (raw API calls, no state)
└── utils/
└── avatars.js User avatar generation
application/ Flask server
├── __init__.py App factory
├── analytics.py Usage analytics tracking
├── auth.py Authentication helpers
├── config.py Server configuration
├── effect_chains.py Audio effect chain logic
├── embed_generator.py Quote embed HTML generation
├── embeds.py Embed CRUD and management
├── files.py Filesystem helpers
├── folders.py Folder hierarchy management
├── notify.py Notification delivery
├── permissions.py Permission checks (per-resource access control)
├── project_versions.py Project version history management
├── projects.py Project CRUD logic
├── stats.py Usage statistics tracking
├── stripe_integration.py Stripe payment integration helpers
├── subscriptions.py Subscription plan logic
├── tools.py Server-side utility functions
├── usage_rollover.py Scheduled usage counter rollover (cron job)
├── user_subscriptions.py Per-user subscription state management
├── users.py User account management
├── db_access/
│ ├── db.py DB abstraction layer (interface)
│ ├── db_postgres.py PostgreSQL implementation
│ └── db_sqlite.py SQLite implementation
├── routing/
│ ├── admin.py Admin panel routes
│ ├── commands.py Flask CLI commands
│ ├── pages.py Page routes (landing, account, docs, etc.)
│ └── api/
│ ├── auth.py Auth API routes (/auth/*)
│ ├── dialog.py Dialog/widget API routes
│ ├── embeds.py Embeds API routes (/api/embeds/*)
│ ├── files.py File management API routes (/api/projects/<id>/*)
│ ├── folder.py Folder API routes (/api/folders/*)
│ ├── project.py Project CRUD API routes (/api/projects/*)
│ ├── reporting.py Reporting/abuse API routes
│ ├── signup.py Signup/registration API routes
│ ├── stats.py Stats API routes (/api/stats)
│ ├── stripe_routes.py Stripe webhook and billing routes
│ ├── user.py User account API routes (/api/user/*)
│ └── versions.py Version history API routes (/api/projects/<id>/versions/*)
├── storage/
│ ├── gcs.py Google Cloud Storage backend
│ └── local.py Local filesystem storage backend
└── transcription/
├── cuda_check.py CUDA/GPU availability detection
├── local_transcribe.py Local transcription (CPU/GPU, faster-whisper + pyannote)
└── modal_transcribe.py Cloud transcription (Modal AI, serverless GPU)
app.py Flask application entry point and blueprint registration
serve.py Production WSGI launcher (Gunicorn/Waitress)
local_launcher.py Desktop app launcher (local/offline mode)
runtime_hook_gpu.py PyInstaller runtime hook for GPU detection
run.sh / run.bat Convenience startup scripts
Caddyfile Caddy reverse-proxy configuration
How Templates, CSS, and JS Interact
1. Template Composition
index.html is the single root template rendered by Flask for the main app. It composes the page using Jinja2 {% include %} tags:
index.html
├── {% include "sidebar.html" %}
│ └── {% include "components/shortcut_legend.html" %}
└── {% include "workspace.html" %}
├── {% include "start_page.html" %}
├── {% include "workspace_panels/waveform_panel.html" %}
├── {% include "workspace_panels/speakers_panel.html" %}
└── {% include "workspace_panels/transcript_panel.html" %}
Standalone pages (landing, account, presentation, etc.) each have their own top-level template and are served by routes in application/routing/pages.py.
Templates define static HTML structure and element IDs only. No data is injected into templates at render time (aside from static asset URLs via url_for). All dynamic content is written by JavaScript after the page loads.
2. CSS Loading
All stylesheets are declared as <link> tags in index.html, referenced via Flask's url_for('static', filename=...). There is no CSS bundler or preprocessor.
Load order matters:
variables.css— defines CSS custom properties consumed by everything elsemain.css— base/reset styles- Layout stylesheets (
sidebar.css,start_page.css) - Component stylesheets (
components/*.css) - Workspace panel stylesheets (
workspace/*.css)
CSS files are scoped by directory: global styles live at the top level, component styles under components/, panel styles under workspace/. Standalone pages load only the stylesheets they need.
3. JavaScript as ES Modules
index.html loads a single script:
<script src="{{ url_for('static', filename='js/main.js') }}" type="module"></script>
main.js imports all other JS files using native ES module import syntax. There is no bundler (Webpack, Vite, etc.) — the browser resolves modules directly from the filesystem via Flask's static file serving.
4. JS ↔ Template Binding
JavaScript classes bind to the pre-rendered DOM by querying element IDs defined in the templates:
// Example from main.js / Workspace
this.sidebar = document.querySelector("#sidebar");
this.sidebarList = document.querySelector("#sidebarList");
The templates establish the DOM contract (IDs, element types). The JS classes assume those IDs exist and will fail silently or throw if a template changes an ID without updating the corresponding JS.
5. Dynamic HTML Generation
Not all HTML comes from templates. Some elements are created entirely in JavaScript:
- Sidebar project items — built by
App.#buildProjectItem()inmain.js - Context menus —
ProjectContextMenu,SegmentContextMenuand others extendContextMenuand create their own DOM trees - Confirm dialogs —
ConfirmDialogappends a modal todocument.body - Status badges —
Workspace.updateProjectServerStatus()writes badge elements into#projectServerStatus
These dynamic elements inherit styles from the globally-loaded CSS via shared class names (e.g., .ctx-item, .sidebar-item, .project-cloud-badge).
6. Cross-Panel Communication
The three workspace panels (WaveformPanel, TranscriptPanel, SpeakersPanel) do not reference each other directly. Workspace owns all three and wires them together at construction time via callbacks:
TranscriptPanel --onSegmentHover--> Workspace --calls--> WaveformPanel.setHoveredRegion()
WaveformPanel --onRegionSelect--> Workspace --calls--> TranscriptPanel.setSelectedSegment()
SpeakersPanel --onSpeakerHover--> Workspace --calls--> WaveformPanel.drawRegions()
This keeps panels decoupled: each panel exposes a callback API; Workspace provides the implementations.
7. State Management
State is split across two layers:
| Layer | Location | Contents |
|---|---|---|
| Server/connection | Server (server.js) | Server connection, auth token, connection status |
| Per-project | Project instances | Transcript, speakers, waveform data, dirty flags |
Project tracks dirty state per data category (transcriptDirty, speakersDirty, waveformDirty). When a category is marked dirty, the project fires registered callbacks that trigger UI re-renders in Workspace. This is the primary mechanism for keeping the three panels in sync after an edit.
8. Server Communication
Server communication is handled by two layers:
Serverclass (server.js) — stateful class owned byApp. Manages connection URL, auth token, and connection lifecycle. Exposes all server operations as methods. Fires callbacks (onStatusChanged,onConnect,onDisconnect) when connection state changes.server_access.js(utilities) — stateless module of rawfetch()wrappers. Called byServerwith the current base URL and token. All functions areasyncand throw on non-2xx responses.
9. Backend Routing
Routes are split into blueprints registered in app.py:
routing/pages.py— page routes (/,/account,/landing,/docs,/presentation/<id>, etc.)routing/admin.py— admin panel routesrouting/api/project.py—/api/projects— list, create, get, update, delete, duplicaterouting/api/files.py—/api/projects/<id>/audio,/transcript,/speakers,/waveform,/transcribe,/samplesrouting/api/folder.py—/api/folders— folder CRUD and project-folder assignmentrouting/api/auth.py—/auth/login,/auth/logoutrouting/api/user.py—/api/user— account managementrouting/api/signup.py—/api/signup— registrationrouting/api/stats.py—/api/stats— usage statisticsrouting/api/embeds.py—/api/embeds— quote embed managementrouting/api/versions.py—/api/projects/<id>/versions— version historyrouting/api/stripe_routes.py—/api/billing, Stripe webhooksrouting/api/reporting.py— abuse/content reportingrouting/api/dialog.py— dialog state endpoints
10. Storage and Database Backends
Storage and database are abstracted behind interfaces to support both local and cloud deployments:
storage/local.py— stores files on the local filesystemstorage/gcs.py— stores files in Google Cloud Storage (production)db_access/db.py— DB interface; selects backend based on configdb_access/db_sqlite.py— SQLite backend (local/desktop)db_access/db_postgres.py— PostgreSQL backend (cloud/production)