13 KiB
Frontend JS Dependency Audit
Version: v0.28.3 cs2 Date: 2026-03-13 Purpose: Map every global, cross-file dependency, and init sequence to prepare for module extraction.
Summary
| Metric | Value |
|---|---|
| JS files | 47 |
| Total lines | 25,056 |
| Total bytes | 1,103,572 (~1.1 MB) |
| Top-level globals (original) | 431 |
| IIFE-wrapped files | 47 (all) |
Explicit window.* exports |
~215 |
| Functions privatized | ~168 |
| Implicit globals remaining | 0 |
| Vendor libs | 3 (marked.min.js, purify.min.js, codemirror.bundle.js) |
Inline onclick handlers (templates) |
~50 unique functions |
Dynamic onclick in JS (innerHTML) |
143 total occurrences |
Architecture: IIFE Modules (Post-Decomposition)
No module system (no ES modules, no bundler, no AMD). Every file is a
<script> tag loaded in dependency order. All 47 files are now wrapped
in IIFEs with explicit window.* exports:
(function() {
'use strict';
// ... all code ...
window.MyGlobal = MyGlobal;
})();
Phase 1 complete. Zero implicit globals remain. ~168 functions privatized. The next phase is onclick → addEventListener migration (143 dynamic handlers in JS innerHTML + ~50 in Go templates), which is the prerequisite for ES module conversion.
IIFE Files
| File | Clean? | Leaks |
|---|---|---|
admin-handlers.js |
✅ | None (all via inline onclick from globals) |
admin-scaffold.js |
✅ | None |
editor-surface.js |
✅ | None |
knowledge-ui.js |
✅ | None |
pages-splash.js |
✅ | None |
task-admin.js |
❌ | window._loadAdminTasks |
task-settings.js |
❌ | window._createFromTemplate, window._loadSettingsTasks |
task-sidebar.js |
❌ | window.TaskSidebar |
tools-toggle.js |
✅ | None |
workflow-admin.js |
❌ | 5 functions (_loadAdminWorkflows, _wfCreate, _wfOpen, _wfEditStage, _wfDeleteStage) |
workflow-api.js |
✅ | None (registers on App) |
workflow-queue.js |
❌ | window.WorkflowQueue |
Script Load Order
base.html (shared — every surface)
1. app-state.js → const App = { ... }
2. api.js → const API = { ... }
3. (inline) → window.__BASE__, auth bootstrap
4. events.js → const Events = { ... }
5. ui-primitives.js → const UI = { ... }
6. vendor/marked.min.js
7. vendor/purify.min.js
8. ui-format.js → formatMessage(), toggleCodeCollapse(), etc.
9. ui-core.js → renderChatList(), renderMessages(), etc.
10. pages.js → const Pages = { ... }
11. user-menu.js → const UserMenu = { ... }
12. model-selector.js → const ModelSelector = { ... }
13. file-tree.js → const FileTree = { ... }
14. code-editor.js → const CodeEditor = { ... }
15. note-editor.js → const NoteEditor = { ... }
16. drag-resize.js → const DragResize = { ... }
17. pane-container.js → const PaneContainer = { ... }
18. (inline) → Theme.init()
19. surfaces/{surface}/js/main.js → surface CSS + scaffold
chat.html (25 additional scripts + 1 vendor)
vendor/codemirror/codemirror.bundle.js → window.CM (optional, onerror fallback)
20. extensions.js → const Extensions = { ... }
21. panels.js → const PanelRegistry = { ... }
22. ui-settings.js → settings rendering (bare globals)
23. ui-admin.js → admin rendering (bare globals)
24. tokens.js → token counting (bare globals)
25. notes.js → notes UI (bare globals)
26. note-graph.js → graph visualization (bare globals)
27. files.js → file upload/management (bare globals)
28. tools-toggle.js → tool enable/disable (IIFE)
29. knowledge-ui.js → KB picker (IIFE)
30. memory-ui.js → memory panel (bare globals)
31. persona-kb.js → persona KB bindings (bare globals)
32. projects-ui.js → project management (bare globals, 78 top-level decls)
33. channel-models.js → const ChannelModels = { ... }
34. notification-prefs.js → notification preferences (bare globals)
35. notifications.js → const Notifications = { ... }
36. chat.js → const ChatInput = { ... } + 29 top-level functions
37. settings-handlers.js → settings form handlers (bare globals, 24 decls)
38. admin-handlers.js → admin form handlers (IIFE, 40 internal functions)
39. workflow-api.js → workflow REST helpers (IIFE)
40. workflow-admin.js → workflow admin UI (IIFE, 5 window.* leaks)
41. workflow-queue.js → workflow queue sidebar (IIFE, window.WorkflowQueue)
42. task-sidebar.js → task sidebar (IIFE, window.TaskSidebar)
43. app.js → init(), startApp(), event listeners
44. debug.js → debug console (bare globals)
45. repl.js → REPL (bare globals)
admin.html (14 additional)
Loads: ui-settings, ui-admin, admin-handlers, settings-handlers, knowledge-ui, persona-kb, memory-ui, notifications, files, admin-scaffold, workflow-api, workflow-admin, task-admin, admin-surfaces.
notes.html (14 additional)
Loads: chat, files, channel-models, tokens, extensions, notes, note-graph, knowledge-ui, persona-kb, memory-ui, notifications, panels, projects-ui, app.
settings.html (9 additional)
Loads: ui-settings, settings-handlers, task-settings, knowledge-ui, persona-kb, memory-ui, notifications, notification-prefs, channel-models.
editor.html (4 additional + 1 vendor)
Loads: vendor/codemirror, chat-pane, editor-surface, app. Lightest surface — fewest dependencies.
Core Globals (Dependency Roots)
These are referenced by 10+ other files. They form the "root set" that must be extracted first.
| Global | Defined in | Referenced by | Role |
|---|---|---|---|
App |
app-state.js | 20 files | State container (chats, models, user, settings, presence) |
API |
api.js | 36 files | REST client (all HTTP calls) |
UI |
ui-core.js | 32 files | DOM helpers (toast, confirm, modal, sidebar, theme) |
Events |
events.js | 9 files | Pub/sub event bus (WebSocket bridge) |
Secondary Globals (3–9 references)
| Global | Defined in | Referenced by | Role |
|---|---|---|---|
Pages |
pages.js | Templates + 3 files | Page-level actions (login, admin forms) |
ChannelModels |
channel-models.js | 4 files | Model roster per channel |
ChatInput |
chat.js | 3 files | Message input state + send |
Notifications |
notifications.js | Templates + 2 files | Bell dropdown, badge count |
ChatPane |
chat-pane.js | 3 files | Embedded chat component (editor) |
CM |
vendor/codemirror.bundle.js | 5 files | CodeMirror 6 editor (optional, graceful fallback) |
PanelRegistry |
panels.js | Templates + 1 file | Side panel management |
Leaf Globals (1–2 references, mostly self-contained)
ModelSelector, FileTree, CodeEditor, NoteEditor, DragResize, PaneContainer, UserMenu, WorkflowQueue, TaskSidebar, Extensions.
Inline Handler Problem
143 onclick= patterns in JS files (innerHTML-generated HTML) plus
~50 in Go templates. These reference bare globals that must remain
on window or the handlers break. This is the primary blocker for
ES module migration.
Top offenders (dynamic onclick in JS):
| File | Count | Examples |
|---|---|---|
| ui-admin.js | 28 | Admin list row actions |
| projects-ui.js | 26 | Project CRUD, channel/KB/note associations |
| ui-core.js | 25 | Chat list, sidebar, context menus |
| ui-format.js | 10 | Code block copy/preview/download |
| ui-settings.js | 8 | Settings form toggles |
| notifications.js | 8 | Notification row actions |
Template onclick (must remain global):
UI.toggleSidebar, UI.toggleSidebarSection, UI.switchTeamTab, Notifications.toggleDropdown, PanelRegistry.toggle, PanelRegistry.closeAll, Pages.doLogin, Pages.saveSettings, Pages.saveProvider, Pages.saveTeam, handleLogin, handleRegister, switchAuthTab, newChat, newFolder, newChannelOrDM, sendMessage, createProject, closeLightbox, toggleSidePanelFullscreen, plus ~15 more Pages.* admin form handlers.
Cross-File Call Graph (Simplified)
app-state.js (App)
↑ read/write by 20 files
│
api.js (API)
↑ called by 36 files
│
events.js (Events)
↑ subscribed by 9 files
├── chat.js (typing, message.created, presence)
├── notifications.js (notification.new/read)
├── extensions.js (tool.call/result)
├── repl.js (debug events)
└── app.js (role.fallback banner)
│
ui-primitives.js (UI)
↑ called by 32 files
├── toast(), confirm(), modal()
├── toggleSidebar(), toggleSidebarSection()
└── theme, kbd shortcuts
│
ui-format.js (formatMessage, etc.)
↑ called by ui-core.js, chat.js, notes.js, projects-ui.js
├── depends on: marked.min.js, purify.min.js
│
ui-core.js (renderChatList, renderMessages, etc.)
↑ called by chat.js, app.js, projects-ui.js
├── depends on: App, API, UI, ui-format
│
chat.js (ChatInput, sendMessage, selectChat, loadChats)
↑ called by app.js, templates
├── depends on: App, API, Events, UI, ui-core, ui-format,
│ ChannelModels, tokens, files
│
app.js (init, startApp)
↑ entry point — called by inline <script> in base.html
├── depends on: everything above + surface-specific modules
Decomposition Strategy
Phase 1: Extract Core Four (no bundler required)
Move App, API, Events, UI to a pattern where they're defined
as proper singletons with explicit exports. No ES modules yet — just
clean up the global registration so each file has a single window.X
export with a documented interface.
Why no bundler yet: 143 dynamic onclick handlers + 50 template
onclick handlers require globals on window. A bundler would need
either (a) a global shim layer or (b) converting all handlers to
addEventListener. Option (b) is the right answer but it's ~200
call sites across 16 files + 10 templates — too much churn for one
changeset.
Phase 2: onclick → addEventListener migration
Convert dynamic HTML generation from:
`<button onclick="deleteItem('${id}')">Delete</button>`
to:
const btn = document.createElement('button');
btn.textContent = 'Delete';
btn.addEventListener('click', () => deleteItem(id));
This eliminates the global requirement. Target the top 6 files first (ui-admin, projects-ui, ui-core, ui-format, ui-settings, notifications) which account for 105 of 143 occurrences.
Template onclick handlers stay — Go templates can't do addEventListener.
These become the only remaining reason for window.* exports.
Phase 3: ES Module conversion
Once onclick handlers are gone from JS, files can become ES modules:
<script type="module" src="js/app.js"></script>
Import graph follows the dependency order already established by
<script> tag ordering. Vendor libs (marked, purify, codemirror) stay
as classic scripts since they self-register on window.
Phase 4: Template handler shim
Create a thin window.handlers = {} object that ES modules register
into. Template onclick handlers call handlers.doLogin() etc. This
is the final bridge — once templates move to client-side rendering
(extension surface SDK), this shim disappears.
Files by Decomposition Priority
Tier 1 — Core (extract first, most dependents)
| File | Lines | Globals | Dependents | Notes |
|---|---|---|---|---|
| app-state.js | 96 | 3 | 20 | Clean singleton, easy extract |
| api.js | 1,159 | 3 | 36 | Large but self-contained |
| events.js | 261 | 1 | 9 | Clean singleton |
| ui-primitives.js | 1,316 | 18 | 32 | Large, many utility functions |
Tier 2 — Rendering (depends on Tier 1)
| File | Lines | Globals | Dependents | Notes |
|---|---|---|---|---|
| ui-format.js | 573 | 20 | 4 | Markdown + code blocks |
| ui-core.js | 1,644 | 6 | 3 | Chat list, messages, sidebar |
| pages.js | 382 | 8 | Templates | Page-level orchestration |
Tier 3 — Feature modules (leaf nodes, mostly self-contained)
| File | Lines | Notes |
|---|---|---|
| channel-models.js | 425 | Clean object literal |
| chat-pane.js | 143 | Clean object literal |
| notifications.js | 390 | Clean singleton, IIFE candidate |
| model-selector.js | 191 | Clean object literal |
| file-tree.js | 295 | Clean object literal |
| tokens.js | 143 | 4 globals, minimal deps |
Tier 4 — Heavy pages (most onclick handlers, refactor last)
| File | Lines | onclick | Notes |
|---|---|---|---|
| ui-admin.js | 1,922 | 28 | Admin rendering, deeply coupled |
| projects-ui.js | 1,794 | 26 | 78 top-level globals (!), biggest mess |
| ui-core.js | 1,644 | 25 | Already in Tier 2, but onclick heavy |
| ui-settings.js | 1,011 | 8 | Settings rendering |
| settings-handlers.js | 1,064 | 0 | But 24 globals called by ui-settings |
| admin-handlers.js | 952 | 2 | IIFE but 40 internal functions |