349 lines
13 KiB
Markdown
349 lines
13 KiB
Markdown
# 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:
|
||
|
||
```js
|
||
(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:
|
||
```js
|
||
`<button onclick="deleteItem('${id}')">Delete</button>`
|
||
```
|
||
to:
|
||
```js
|
||
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:
|
||
```html
|
||
<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 |
|