Security
--------
ComfyUI's PromptServer.add_routes() mirrors every registered route under a
second "/api" prefix, so /mjr/am/<mutation> is also served at
/api/mjr/am/<mutation>. The auth middleware matched only the canonical
prefix, so the mirrored path reached the same handler with the
authentication check skipped entirely, and without the hardening headers:
POST /mjr/am/assets/delete -> requires_auth = True
POST /api/mjr/am/assets/delete -> requires_auth = False
Replaced the prefix test with a shared _is_majoor_path() boundary covering
the canonical prefixes, ComfyUI's /api mirror, and the /api/v2/assets
compat surface. Verified it does not capture ComfyUI's own routes. CSRF,
rate limiting and safe-mode gates are per-handler and were unaffected.
Licensing
---------
PrimeVue 5 is no longer MIT: PrimeTek moved it to the commercial "PrimeUI"
license, which requires a key, shows a license notice without one, and
forbids redistributing it "as a component library or development tool"
without a separate OEM License. Dependabot had auto-merged 4.5.5 -> 5.0.1.
This project is GPL-3.0 and commits dist/ to the repo, so shipping it would
nag every user and conflict with the GPL's redistribution requirement.
Pinned back to 4.5.5 (last MIT release). The 5.x bump had touched only
package.json/package-lock.json, so no source depended on v5 APIs. The
revert drops @primeui/license-manager and its @noble/ed25519 key-signature
stack; the primevue chunk shrinks 555.70 -> 520.61 kB. Dependabot is now
configured to ignore primevue major bumps.
Correctness
-----------
- api_v2_assets: stop returning the DB's absolute filepath on this
network-reachable surface; subfolder + root_id already convey location
without disclosing local disk layout.
- watcher: enforce _RECENT_GENERATED_HARD_CAP after inserting a batch;
pruning only beforehand let a large batch overshoot the cap.
- routes/registry: track registered route groups individually so a group
failing part-way through does not re-register earlier groups on retry.
- fs_walker/scanner: apply the viewer's root check to symlinked files, so
the scanner stops indexing symlinks whose target escapes every allowed
root ("ghost assets" the viewer then refuses to open). Consulted only for
actual symlinks, so ordinary scans pay no extra I/O.
- routes/registry_app: skip the redundant app.add_routes() on the live
PromptServer app; ComfyUI adds the shared table itself after node
loading, so every core and Majoor route was registered twice at startup.
- Result: unwrap()/unwrap_or()/map() gated on `data is not None`, treating
a legitimate Ok(None) as failure; gate on `ok` instead.
- custom_roots: replace a leftover debug print() that wrote user file paths
to stderr on every move request with logger.debug.
- schema: drop the redundant UNIQUE(filepath, source, root_id) index
(migration m022). assets.filepath is already UNIQUE, so it could never
reject a row the column constraint does not, nor narrow a lookup - it
only cost a second B-tree write per asset on the scan hot path.
Frontend
--------
- entry.ts: app.registerExtension() throws on a duplicate name and ComfyUI
exposes no unregisterExtension, so a live re-import threw before setup()
ran and the reload-safe teardown never executed. Catch it and re-run
setup() directly.
- Rebuilt dist/ so both the reload fix and the PrimeVue revert ship.
CI / tooling
------------
- frontend-compat-nightly: the types step died with a bare "cp: cannot
stat" (upstream's build has not emitted dist/index.d.ts since Aug 26).
Check both candidate paths and dump dist/ with an actionable message.
- package.json: lenient checks that swallowed their exit code are now
suffixed :advisory, the plain names are strict, and quality:strict runs
the full set.
Verified: 1841 Python tests pass (5 skipped, symlink privileges), ruff
clean; tsc --noEmit clean, 831 JS tests pass, eslint clean, build succeeds.
- Default 'Show folders in Input / Output panels' (browser.showFolders) to
disabled: fresh installs keep the classic flat grid until the user opts in
(backend settings service, frontend defaults, settings UI toggle).
- Gate the root-level 'Create folder here' context menu on the setting.
- Kick off a throttled incremental background scan when browse mode serves
the first page, so unindexed files get indexed/enriched and asset status
dots resolve instead of staying pending (blue).
- Document the feature and setting in README, CHANGELOG, and
docs/SETTINGS_CONFIGURATION.md.
- Fix return type mismatch in filesystem.py:1501 by converting error
Result to the expected generic type before returning.
- Update drag_out.vitest.ts to expect 'copyMove' instead of 'copy'
to match the DragOut.ts change for folder drag support.
Root causes found by deep CSS + Vue audit:
1. Missing .mjr-show-details CSS rule (PRIMARY cause of reload impression)
- .mjr-card-info had no hide rule in the main grid
- applyGridSettingsClasses() toggles .mjr-show-details on the container
- estimateRowHeight() assumed metaHeight=0 when showDetails=false
- But .mjr-card-info was always rendered (~44px) -> virtualizer under-
estimated every row -> repositioned all rows on first measure -> visual
jump that looked like a full grid reload on each scroll
Fix: add .mjr-grid .mjr-card-info { display:none } default with
.mjr-grid.mjr-show-details .mjr-card-info { display:block } toggle
(same pattern already used by the bottom feed at line 9119)
2. estimateRowHeight metaHeight constant was 82px instead of ~52px
- Actual card-info height: padding 12 + filename 15 + margin 4 + row 13 = 44px
- 82px caused over-allocation when showDetails=true -> gap between rows
that the virtualizer would correct on measure -> same jump artifact
Fix: reduce to 52px (44px actual + 8px buffer for font variance)
3. scale(0.92) on .is-selected caused a visible blink on selection change
- The 8% shrink animated over 120ms transition; rapid selection change
(e.g. keyboard nav) caused all de-selected cards to snap back -> blink
Fix: reduce to scale(0.97) - still gives inset feel without the flash
4. Skeleton card min-height:146px conflicted with computed skeletonCardStyle
height -> skeleton cards taller than real cards -> layout jump on first
asset load
Fix: remove min-height from .mjr-grid-skeleton-card
Nodes like iToolsPromptRecord store large texts in widgets_values.
The 8 KB per-value cap caused isValidWorkflowShape() to return false,
making canvas drops create loader nodes instead of loading the workflow.
The total workflow size (MAX_WORKFLOW_BYTES = 5 MB) is the right security
boundary; the per-value cap is raised to 500 KB (safety ceiling only).
Fixes#128