Scoping by Kimi (k2.x) · 2026-06-20 · read-only audit of ui/ + Jinja templates. Companion to MODERNIZATION_PLAN.md Phase 5 (Bootstrap 4→5, jQuery→Svelte). Not started — reference for when Phase 5 begins.
The frontend is a Vite 5 + Svelte 4 build under ui/ that produces the /static/ assets consumed by the Flask backend. The Docker build (docker/webApp/Dockerfile) copies ui/dist/ to /iriswebapp/static/, so every JS/CSS file referenced by the Jinja2 templates must end up in ui/dist/ after npm run build.
Key files:
ui/package.json— current dependencies.ui/vite.config.js— Rollup multi-entry build (ui/src/pages/*.js), static copies fromnode_modulestopublic/assets/, base path/static/.ui/src/App.svelte— effectively empty; only importsapp.css.ui/src/css/app.css— Tailwind 3 directives already present.ui/tailwind.config.js— shadcn-style Tailwind config, dark mode viaclass.ui/components.json— shadcn-svelte scaffolding.
| Area | Location | Count / size |
|---|---|---|
| Base layouts | source/app/templates/layouts/*.html (default.html, default_ext.html, static-default.html, default_centered.html) |
4 files |
| Shared chrome | source/app/templates/includes/ (navigation.html, navigation_ext.html, sidenav.html, footer.html, footer_case.html, quick-sidebar.html, datastore-sidebar.html, scripts.html, header_case.html) |
9 files |
| Global modals | source/app/templates/modals/*.html |
3 files |
| Blueprint page templates | source/app/blueprints/pages/**/templates/*.html |
84 files |
| Total template lines | — | ≈ 11,216 lines |
Representative templates inspected:
source/app/templates/layouts/default.html— loads Bootstrap 4 CSS/JS, jQuery, jQuery UI, DataTables, bootstrap-notify, sweetalert, bootstrap-select, ACE, showdown, Atlantis theme.source/app/templates/includes/navigation.html/sidenav.html— Bootstrap 4 navbar, collapse, caret, sidebar scroll.source/app/blueprints/pages/case/templates/case.html— case summary shell, Milkdown split editor placeholders, report modals.source/app/blueprints/pages/case/templates/case_notes_v2.html— notes folder tree + Milkdown split editor.source/app/blueprints/pages/case/templates/case_timeline.html— timeline list + add-event modal + vis graph timeline link.source/app/blueprints/pages/dashboard/templates/index.html— Chart.js 2 line chart + 4 DataTables.source/app/blueprints/pages/case/templates/case_graph.html— vis-network graph container.source/app/blueprints/pages/login/templates/login.html— static page, already loadslogin.js(Svelte entry).
ui/src/pages/*.js are the Vite-built page bundles. There are 39 JS entry files and ≈ 18,058 lines of legacy UI code.
| File | Lines | Role |
|---|---|---|
ui/src/pages/common.js |
1,957 | Global helpers, ACE editor wrapper, showdown converter, API wrappers, popover/tooltip init, draggable modals |
ui/src/pages/alerts.js |
2,255 | Alerts list, vis network relationships, saved filters, custom conditions ACE editor |
ui/src/pages/case.notes.js |
1,471 | Notes folder tree, collab persist/snapshot |
ui/src/pages/case.timeline.js |
1,416 | Timeline CRUD, ACE filter editor, selectpicker parent events |
ui/src/pages/milkdown_editor.js |
953 | Modern island: Milkdown Crepe + CodeMirror 6 + Yjs collab |
ui/src/pages/milkdown_split.js |
881 | Modern island: split-pane source/WYSIWYG editor |
ui/src/pages/case.summary.js |
666 | Case summary collab + split editor integration |
ui/src/pages/dashboard.js |
557 | Dashboard Chart.js 2 chart + DataTables |
ui/src/pages/datastore.js |
685 | Datastore sidebar, ACE file-search editor |
ui/src/pages/manage.objects.js |
651 | Objects / IOC / asset / classification / evidence type tables |
ui/src/pages/case.asset.js |
558 | Case assets DataTable + select2 IOC links |
ui/src/pages/manage.modules.js |
380 | Modules table + ACE config editor |
ui/src/pages/case.rfiles.js |
492 | Report files DataTable |
ui/src/pages/case.ioc.js |
470 | IOCs DataTable |
ui/src/pages/case.tasks.js |
482 | Tasks DataTable |
ui/src/pages/manage.users.js |
429 | Users DataTable |
ui/src/pages/overview.js |
436 | Overview DataTable |
ui/src/pages/view.customers.js |
294 | Customer view DataTable |
ui/src/pages/manage.cases.common.js |
383 | Shared case-management table logic |
ui/src/pages/case.graph.js |
72 | vis-network case graph |
ui/src/pages/case.timeline.visu.js |
73 | vis-timeline visualization |
ui/src/pages/datatablesUtils.js |
65 | Column-filter helper for DataTables |
Only 7 .svelte files exist:
| File | Role | Status |
|---|---|---|
ui/src/App.svelte |
CSS import only | Inert |
ui/src/pages/login/login.svelte |
Password visibility toggle on the existing login form | Minimal DOM script, not idiomatic Svelte |
ui/src/pages/demo/demo.svelte |
Empty shell | Inert |
ui/src/lib/components/ui/button/button.svelte |
shadcn-style Button | Scaffolding; imports bits-ui which is not in package.json |
ui/src/lib/components/ui/input/input.svelte |
shadcn-style Input | Same scaffolding issue |
ui/src/lib/components/ui/label/label.svelte |
shadcn-style Label | Same scaffolding issue |
ui/src/lib/components/ui/pages/user-auth-form.svelte |
Sample auth form | Imports Button/Input/Label/Icons; not wired to any route |
ui/src/lib/components/ui/icons/index.js |
lucide-svelte icon re-exports | Imports lucide-svelte which is not in package.json |
Conclusion: the only production Svelte usage is the tiny login/login.svelte bundle mounted by ui/src/pages/login/index.js. The shadcn-style component library is non-buildable today because bits-ui, tailwind-variants, and lucide-svelte are missing from ui/package.json.
| Library | Version | Used by | Migration target |
|---|---|---|---|
bootstrap |
^4.0.0 |
All layouts, CSS + JS | Bootstrap 5 |
jquery |
^3.7.1 |
All page JS | Vanilla / Svelte |
jquery-ui |
^1.12.1 |
Draggable modals, sortable | Native / Svelte |
chart.js |
^2.7.2 |
dashboard.js index chart |
Chart.js 4 |
vis |
^4.21.0-EOL |
alerts.js, case.graph.js, case.timeline.visu.js |
vis-timeline + vis-network |
moment |
^2.22.2 |
case_graph_timeline.html, case_rfile.html (global moments.min.js) |
Day.js |
ace-builds |
^1.4.9 |
common.js, alerts.js, case.timeline.js, datastore.js, manage.*.js |
CodeMirror 6 (already used by Milkdown) |
showdown |
^1.9.1 |
common.js markdown preview |
Already replaced by Milkdown for notes/summary; remove remaining uses |
dropzone |
^5.7.0 |
File uploads | Native / Svelte upload |
bootstrap-slider |
^11.0.2 |
Graph filters | Native range input / Svelte |
sortablejs |
^1.7.0 |
Notes folder tree, asset ordering | Native / Svelte |
bootstrap-select / bootstrap-multiselect / select2 |
vendored in public/assets/ |
Many forms | Svelte select components |
suggestags |
vendored | Tag inputs | Svelte tag input |
bootstrap-datetimepicker |
vendored | Datetime fields | Native datetime-local / flatpickr |
milkdown/crepe, codemirror 6, yjs, y-websocket |
modern deps | Notes/summary editors | Keep |
A grep over source/app/templates and source/app/blueprints/**/templates shows ≈ 845 Bootstrap 4/jQuery plugin attribute usages (data-toggle, data-dismiss, data-target, modal, btn, etc.). The most frequent classes are btn (≈ 982), col (≈ 762), modal (≈ 670), row (≈ 462), alert (≈ 275), table (≈ 271), nav (≈ 236), card (≈ 235), form-control (≈ 200). In ui/src/pages/*.js the Bootstrap/jQuery plugin calls are also pervasive (popovers, tooltips, selectpickers, modals, collapse).
| Concern | Current | Target |
|---|---|---|
| CSS framework | Bootstrap 4 + Atlantis theme | Bootstrap 5 + Tailwind 3 for new islands |
| JS framework | jQuery 3 + jQuery UI + vanilla plugins | Svelte 4 (islands) + vanilla helpers |
| Build | Vite 5 multi-entry | Vite 5 + Svelte islands |
| Tables | DataTables 1.x + BS4 styling | Svelte table components (or TanStack Table) |
| Charts | Chart.js 2 | Chart.js 4 |
| Graph/timeline | vis 4 EOL monolith |
vis-network + vis-timeline |
| Dates | moment | Day.js |
| Markdown editor | ACE + showdown (legacy) | Milkdown Crepe + CodeMirror 6 (already done for notes) |
| Icons | Flaticon + Font Awesome 5 | Keep Font Awesome / migrate to lucide gradually |
| Notifications | bootstrap-notify | Svelte toasts |
| Selects/tags | bootstrap-select, select2, multiselect, suggestags | Svelte select / multiselect components |
Tailwind is already configured (ui/tailwind.config.js, ui/src/css/app.css) but currently only used by the broken shadcn scaffolding. The first prerequisite is to fix the dependency manifest and make the component library build.
| Surface | Files | Tech | Difficulty |
|---|---|---|---|
| Top navigation | source/app/templates/includes/navigation.html, navigation_ext.html |
Bootstrap 4 navbar, collapse search, dropdowns, modals | L |
| Sidebar | source/app/templates/includes/sidenav.html |
Bootstrap 4 collapse, jquery.scrollbar, caret icons, permission guards |
L |
| Footer/global modals | source/app/templates/includes/footer.html, footer_case.html |
Global context switcher, tasklog, datastore, comment modal | L |
| Quick sidebar / datastore sidebar | source/app/templates/includes/quick-sidebar.html, datastore-sidebar.html |
Off-canvas jQuery panels | M |
Gotcha: The sidebar relies on jquery.scrollbar and the Atlantis scrollbar-inner classes. Reproducing the exact scroll behavior, mini-icon hover states, and collapse animation is the biggest visual-fidelity risk.
There are 50+ modal templates and ≈ 670 modal class occurrences. Modals fall into two patterns:
- Inline modals defined directly in page templates (e.g.,
case.htmlreport/owner modals,case_timeline.htmlupload CSV modal). - AJAX-loaded modals where the page has an empty modal shell and the content is fetched from a
*/modalroute (e.g.,#modal_add_case_content,#modal_add_task_content,#modal_add_gtask_content).
common.js initializes all modal dialogs as draggable via jQuery UI:
$('.modal-dialog').draggable({ handle: ".modal-header" });| Surface | Files | Difficulty |
|---|---|---|
| Inline simple modals | many modal_*.html templates |
M |
| AJAX-loaded CRUD modals | common.js, dashboard.js, case.*.js, manage.*.js |
L |
| Stacked / draggable modals | common.js line 1900 |
M |
At least 25 DataTable instances across the app:
dashboard.js—ureview_table,ucases_table,utasks_table,gtasks_tableoverview.js—overview_tablecase.tasks.js—tasks_tablecase.ioc.js—ioc_tablecase.asset.js—assets_tablecase.rfiles.js—rfiles_tablemanage.cases.common.js/manage.cases.js—cases_tablemanage.users.js— users tablemanage.objects.js— assets, IOC, classification, state, evidence tablesmanage.groups.js,manage.customers.js,view.customers.js,search.js,dim_tasks.js,activities.js
Most tables use:
- AJAX-loaded data (
ajax.reload()) - Column filtering via
datatablesUtils.js - Bootstrap 4 styling (
table display table-striped table-hover) dataTables.buttons,dataTables.select,dataTables.contextualActions,dataTables.cellEdit- Popovers rendered in cells
Difficulty: L per table family, XL in aggregate. The custom MakeCellsEditable in-cell editing in dashboard.js and case.tasks.js is a specific gotcha.
Forms are server-rendered with Flask-WTF (source/app/forms.py). Templates use {{ form.hidden_tag() }} for CSRF and render fields with Bootstrap 4 classes. Many forms rely on jQuery plugins:
bootstrap-select/selectpicker()select2bootstrap-multiselectsuggestagstag inputsbootstrap-datetimepickerdropzone- jQuery validation
| Surface | Difficulty |
|---|---|
| Simple text/select forms | M |
| Multi-select / tag / datetime forms | L |
| File upload (dropzone) forms | M |
| Sub-view | Files | Key complexity |
|---|---|---|
| Case summary | case.html, case.summary.js |
Milkdown split editor already modern; collab socket; needs Bootstrap 5 shell only |
| Notes | case_notes_v2.html, case.notes.js, milkdown_editor.js, milkdown_split.js |
Already modernized — use as the reference island |
| Assets | case_assets.html, case.asset.js |
DataTable + select2 + Milkdown desc + sortable |
| IOCs | case_ioc.html, case.ioc.js |
DataTable + popover actions |
| Tasks | case_tasks.html, case.tasks.js |
DataTable + in-cell status editing |
| Timeline | case_timeline.html, case.timeline.js, case.timeline.visu.js |
Custom list renderer, ACE filter, vis-timeline visualize |
| Graph | case_graph.html, case.graph.js |
vis-network physics options |
| Report files | case_rfile.html, case.rfiles.js |
DataTable + datetime picker |
The notes and case-summary editors are the reference modern islands. They use:
@milkdown/crepeWYSIWYG@codemirror/lang-markdownfor source paneyjs+y-websocketreal-time collaboration- Custom IRIS markdown image-size adapter (
irisToMilkdown/milkdownToIris)
Files: ui/src/pages/milkdown_editor.js, milkdown_split.js, case.notes.js, case.summary.js, ui/src/lib/collab_editor_session.js.
Difficulty: S for CSS shell migration, because the editor itself is already target-stack.
| Surface | Files | Difficulty |
|---|---|---|
| Dashboard chart | dashboard/templates/index.html, dashboard.js |
Chart.js 2 → 4 breaking options API; M |
| Dashboard tables | dashboard.js |
4 DataTables + in-cell edit; L |
| Case graph | case_graph.html, case.graph.js |
vis-network API mostly unchanged; M |
| Timeline visualize | case_graph_timeline.html, case.timeline.visu.js |
vis-timeline API; replace moment with Day.js; M |
| Alert relationship graph | alerts.js |
vis-network + context menu + mutation observer; L |
ui/src/pages/alerts.js is the largest and most complex page script (2,255 lines). It contains:
- Saved filter system with
selectpicker - Alert status/severity/classification inline editing
vis.Networkrelationship graph with custom context menuace.edit('custom_conditions')for custom filter conditionsshowEnrichment()using ACE for JSON display- Many modals (
editAlertModal, merge/escalate/enrichment modals)
Difficulty: L — leave for late in the migration.
Management surfaces are largely CRUD tables + modal forms:
- Users, groups, customers, cases, case templates, report templates, modules, objects, attributes, server settings, access control, audit.
Because they share the DataTables + AJAX-modal pattern, they are good mid-migration candidates once a reusable Svelte table and form-modal island are built.
Difficulty: M–L per page, but large volume.
The current architecture is ideal for Svelte islands: each Jinja2 page can keep its server-rendered shell and mount a Svelte component into a named <div> via a dedicated Vite entry. This is exactly how login.js and the Milkdown editors already work:
<div id="my-island"></div>
<script type="module" src="/static/assets/js/iris/my-island.js"></script>Each island entry (ui/src/pages/my-island/index.js) imports a Svelte component and mounts it. The component can call the existing REST API through a thin wrapper (reuse get_request_api / post_request_api from common.js or replace with a typed fetch helper).
Bootstrap 4 and 5 cannot safely coexist on the same page because they share class names (btn, modal, row, col, etc.) and global JS plugin namespaces. Two realistic options:
- Big-bang theme migration — migrate the entire layout shell and all global CSS to Bootstrap 5 + a custom theme in one release, then convert islands page-by-page. Higher risk, cleaner long term.
- Scoped Bootstrap 5 for islands — load a namespaced Bootstrap 5 build only inside Svelte island shadow roots, while the legacy shell stays on Bootstrap 4. This adds build complexity but lets islands ship independently.
Recommendation: Option 1 for the base layout/theme early, then island-by-island page migration. The Atlantis theme is Bootstrap-4-specific and will need to be recreated or replaced with a Tailwind/Bootstrap 5 theme anyway.
| Phase | Surfaces | Rationale |
|---|---|---|
| 0. Foundation | Fix ui/package.json (add bits-ui, tailwind-variants, lucide-svelte, svelte-radix or equivalent), make shadcn components build; add a reusable API client and a Svelte modal/table base |
Unblock modern development |
| 1. Low-risk islands | login (already Svelte; expand), demo-landing, profile/settings, static error pages |
Learn the island pattern without business logic |
| 2. Dashboard | Dashboard chart (Chart.js 4) + 4 DataTables + global-task modal | Single page, high visibility, well-contained API surface |
| 3. Management tables | manage.cases, manage.users, manage.customers, manage.groups |
Reuse table/modal components built in phase 2 |
| 4. Case sub-views | Tasks → IOCs → Assets → RFiles → Timeline → Graph | Progressive complexity; case summary/notes already modern |
| 5. Alerts | Full alerts.js rewrite |
Most complex page; do last when reusable components are mature |
| 6. Layout shell | Navigation, sidebar, footer, quick-sidebar, datastore-sidebar | Final cut-over once enough pages are modern |
For each legacy page, the integration point is:
- Add a container in the Jinja2 template:
<div id="gtasks-island"></div> <script type="module" src="/static/assets/js/iris/gtasks.island.js"></script>
- Create
ui/src/pages/gtasks.island.js:import GtasksIsland from './gtasks.island.svelte'; new GtasksIsland({ target: document.getElementById('gtasks-island') });
- The Svelte component fetches
/global/tasks/list, renders the table with a Svelte table component, and calls the same POST endpoints the legacy form used. - Gradually remove the old
#gtasks_tablemarkup anddashboard.jscode once the island is verified.
| Area | Effort | Notes |
|---|---|---|
| Build toolchain / shadcn deps fix | S | 1–2 days |
| Login / static pages | S | Already Svelte; restyle only |
| Dashboard | M | Chart + 4 tables; reusable patterns |
| Management CRUD pages (each) | M | ~1–2 weeks each once components exist |
| Case tasks / IOCs / assets / rfiles | M–L | 2–3 weeks each |
| Case timeline | L | Custom list + vis-timeline + ACE filter |
| Case graph / alert graph | M | vis-network mostly API-compatible |
| Alerts page | L | 4–6 weeks |
| Layout shell (nav/sidebar/footer) | L | Scrollbar behavior, permission-aware menu, dark mode |
| Bootstrap 4 → 5 theme migration | L | Atlantis replacement is non-trivial |
| ACE → CodeMirror 6 migration | M | Common helper used in 9 call sites |
| Showdown removal | S–M | Replace remaining markdown preview with Milkdown / unified |
| moment → Day.js | S | Only two pages load moments.min.js |
| Risk | Impact | Mitigation |
|---|---|---|
bits-ui / tailwind-variants / lucide-svelte missing |
Modern component library cannot build today | Add dependencies immediately; pin versions compatible with Svelte 4 |
| Bootstrap 4/5 class collisions | Visual regressions across all pages | Theme cut-over in dedicated release; use visual regression tests |
| DataTables replacement scope | 25+ tables with custom filtering, buttons, selection, cell editing | Build one reusable Svelte table wrapper; migrate table-by-table |
| AJAX modal routes return HTML | Svelte islands need JSON APIs | Extend existing REST blueprints (source/app/blueprints/rest/) or create JSON modal endpoints |
| CSRF token handling | Legacy code reads $('#csrf_token').val() |
Provide CSRF token to Svelte islands via meta tag or data attribute |
| jQuery plugin lifecycle | Plugins leak if not destroyed on island unmount | Add explicit onDestroy cleanup in islands |
| Real-time collab for notes/summary | Already complex and working | Keep the Milkdown implementation; only migrate surrounding Bootstrap 4 shell |
| Gotcha | Where | Why it needs human QA |
|---|---|---|
| Atlantis theme gradients | bg-primary-gradient, bg-info-gradient, bg-warning-gradient, bg-close-gradient |
Tailwind/Bootstrap 5 default colors will not match the current brand gradients |
| Custom spacing utilities | mt--5, mt--3, mb--2, ml-12, mr-4 in templates and JS |
Many classes are custom or BS4-specific; direct replacement changes whitespace |
| Sidebar scroll behavior | .sidebar-wrapper.scrollbar.scrollbar-inner |
jquery.scrollbar provides custom track styling; native scroll looks different |
| Icon fonts | Flaticon + Font Awesome 5 Solid/Regular/Brands + simple-line-icons | Replacing with lucide changes iconography and alignment |
| Modal backdrops / z-index | data-backdrop="static", bg-shadow-gradient, modal-case-focus |
Bootstrap 5 backdrop markup and stacking differ |
| Draggable modals | common.js line 1900 |
A Svelte modal must explicitly re-implement drag handles |
| Form-control focus rings / sizes | Bootstrap 4 form-control vs Bootstrap 5 |
Input heights and focus shadows change visibly |
| DataTables BS4 styling | table-striped table-hover table-sm + dataTables_* wrappers |
Replacing with a Svelte table loses the current header/filter layout unless explicitly styled |
| Dark mode | dark-theme.css + current_user.in_dark_mode |
Tailwind dark class strategy must be wired to the same backend flag |
| Popover/tooltip content injected as HTML | $('[data-toggle="popover"]').popover() |
Svelte popover components must allow safe HTML rendering or redesign content |
close button markup |
Bootstrap 4 <span aria-hidden="true">×</span> |
Bootstrap 5 uses <span class="btn-close"></span>; legacy modals must be updated coherently |
- Milkdown notes/summary editors — already target-stack; only their Bootstrap 4 wrapper needs updating.
- REST API layer under
source/app/blueprints/rest/— it already provides the JSON surface new islands will consume. - Jinja2 permission guards (
user_has_perm,std_permissions) — any client-side rendering must re-verify authorization server-side; do not expose permission logic to the frontend. - CSRF and session bridge (
iris_session_bridge.js) — keep existing token plumbing.
- Add missing dependencies to
ui/package.jsonand confirmnpm run buildsucceeds with the existing shadcn components. - Build one end-to-end proof-of-concept island (e.g., the global tasks table on the dashboard) that fetches JSON, renders with a Svelte table, and posts updates.
- Create a Bootstrap 5/Tailwind theme prototype for the layout shell and compare side-by-side with the current Atlantis theme.
- Audit all
ace.editandshowdowncall sites and define the CodeMirror 6 / Milkdown replacement strategy. - Set up visual-regression tests for the dashboard and one case view before broad changes begin.
To resume this session: kimi -r session_775aac82-11d4-4aeb-a9b4-44abcdbaeb94