Skip to content

feat(image-editor): add Angular image editor#36236

Merged
oidacra merged 39 commits into
mainfrom
issue-36063-image-editor-build-dotimageeditorcomponent-modal-s
Jun 26, 2026
Merged

feat(image-editor): add Angular image editor#36236
oidacra merged 39 commits into
mainfrom
issue-36063-image-editor-build-dotimageeditorcomponent-modal-s

Conversation

@oidacra

@oidacra oidacra commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

CleanShot.2026-06-24.at.16.03.56.mp4

Replaces the legacy Dojo ImageEditor.js with a new standalone Angular library
@dotcms/image-editor, wired into Edit Content's binary field through an
IMAGE_EDITOR_LAUNCHER seam (Angular-only path).

The editor is a viewer of a dotCMS endpoint: the <img> src is a computed
/contentAsset/image/{id}/{field}/filter/... URL, so every control change rebuilds
the URL and the server renders the adjusted image — no client-side pixel work.

State is an events-based NgRx Signal Store (@ngrx/signals/events), composed of
one vertical signalStoreFeature per area of functionality (adjust, transform,
crop, focal point, file info, tools, history, asset, preview, download) — each
bundling its own reducers, selectors and effects.

Scope note

This PR delivers the editor shell, the server-side preview pipeline, the panels and
the crop/focal tools, plus Download. Saving the edited image back to the field
is intentionally deferred to #36067
— the editor currently previews and downloads.

What's included

  • Library @dotcms/image-editor: root dialog, header, canvas (crossfade, zoom +
    pan, crop/focal overlays), side panels (Adjust / Transform / File info / History),
    footer (Cancel + Download).
  • Server-side preview via the filter-URL builder (single source of truth) and Download.
  • Command history: removable applied-edits list + undo/redo, keyboard shortcuts
    (Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z, Ctrl+Y), ignored while a text field is focused.
  • Launcher seam in libs/edit-content (token + Angular launcher) and binary-field wiring.
  • Canvas interactions: zoom with drag-to-pan, fit-to-screen, crop-to-current-view,
    focal-centered aspect crop.

Preview robustness

  • Preview fetched as a complete, verified blob (local object URL), so a
    partially-generated server response can't paint a truncated frame; incomplete
    responses are detected and retried silently.
  • decode() + natural-dimension gate before promoting a frame.

Code organization

  • Constants in one image-editor.constants.ts; types/models in models/image-editor.models.ts.
  • Store split into store/features/with-*.feature.ts + store-utils.ts; store.ts is a thin composition.

Testing

  • nx lint image-editor / nx lint edit-content — clean
  • nx test image-editor258 passing (per-feature unit specs + store-utils + dimensions.util + integration; store-feature branch coverage ~100%)
  • nx test edit-content1961 passing (28 skipped)
  • Verified live (local dotCMS, demo content): preview, crop, focal, zoom/pan, fit, download, undo/redo (incl. Cmd+Z), panel persistence.

Issues

This PR fixes: #36063

@claude

claude Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @oidacra's task in 2m 18s —— View job


Rollback Safety Analysis

  • Read rollback-unsafe categories reference
  • Get PR diff between commits
  • Analyze diff against all categories
  • Post findings and add label

Result: ✅ Safe to Roll Back

Added label: AI: Safe To Rollback

Scope of changes analyzed:

The entire diff between e8dae4ed...7f5067ae touches only these categories:

File(s) Category check Verdict
core-web/libs/image-editor/** (new Angular library) No DB, ES, API, or OSGi changes ✅ Safe
dotCMS/src/main/java/com/dotcms/featureflag/FeatureFlagName.java Two new interface constants added (FEATURE_FLAG_NEW_IMAGE_EDITOR, IMAGE_API_USE_LIBVIPS) ✅ Additive — N-1 ignores unknown constants
dotCMS/src/main/java/com/dotcms/rest/api/v1/system/ConfigurationResource.java Two new keys added to BOOLEAN_FEATURE_FLAGS and WHITE_LIST ✅ Additive — N-1 simply won't emit the new keys; frontend falls back gracefully (feature flags default to false)
dotCMS/src/main/resources/dotmarketing-config.properties New FEATURE_FLAG_NEW_IMAGE_EDITOR=false and IMAGE_API_USE_LIBVIPS=false defaults added ✅ Additive — undeclared keys in N-1 already resolve to empty/default
dotCMS/src/main/webapp/WEB-INF/messages/Language.properties ~85 new i18n keys added ✅ Additive — N-1 ignores unknown keys
core-web/libs/edit-content/** Angular-only DI seam wired to feature flag; legacy Dojo editor is the fallback path when flag is off ✅ N-1 backend has no awareness of the Angular editor

No matches found for: C-1 (structural DB change), C-2 (ES mapping), C-3 (JSON model version), C-4 (DROP TABLE/COLUMN), H-1 (destructive backfill), H-2 (RENAME), H-3 (PK restructuring), H-4 (new field type), H-5 (storage provider), H-6 (DROP PROCEDURE), H-7 (NOT NULL without default), H-8 (VTL viewtool contract), M-1 (column type change), M-2 (push bundle format), M-3 (REST contract break), M-4 (OSGi interface).

The new image editor is entirely behind the FEATURE_FLAG_NEW_IMAGE_EDITOR=false flag. Rolling back to N-1 means the flag is not served at all, the Angular editor is not loaded, and the legacy Dojo editor path remains the default — no user-visible regression.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — deepseek.v3.2

[🟡 Medium] core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:125#imageEditorLauncher is injected with { optional: true } but onEditImage() doesn't handle the case where it's null (when isAvailable() returns false). The method will call launcher.open() on null, causing a runtime error. This matters because the feature flag could be off, or the launcher might not be provided in non-Angular contexts.

[🟡 Medium] core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:395 — The onEditImage method calls launcher.open() without checking if launcher is defined after the !launcher?.isAvailable() guard. If launcher is null but isAvailable() would be false, the guard passes (because !null?.isAvailable() is true), then the method proceeds to the legacy path. This is correct, but the comment on line 125 incorrectly states it "safely no-ops". It should clarify that the fallback works.

[🟡 Medium] core-web/libs/edit-content/src/lib/fields/shared/image-editor-launcher/angular-image-editor.launcher.ts:34#enabled signal uses initialValue: false. This means isAvailable() returns false until the server responds, causing a brief flash of the legacy editor even if the feature flag is enabled. This could cause a race condition where the user clicks "edit" before the flag loads, triggering the legacy editor incorrectly. Consider if this is acceptable or if the UI should be disabled until the flag is known.

[🟡 Medium] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:452 — The #cropOverlay view child is typed as DotImageEditorCropOverlayComponent but the template uses dot-image-editor-crop-overlay without a template reference variable (#). The view child will not find the component instance, causing applyCrop() and cancelCrop() calls in the footer to fail. This matters because the crop overlay's apply/cancel actions will be broken.

[🟡 Medium] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:454 — The #focalOverlay view child has the same issue: no template reference variable, so it will be undefined. The focal overlay interactions may fail.

[🟡 Medium] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:456 — The #displayImg view child is used to observe the image's rendered rect via ResizeObserver. However, the displayImg element is referenced in the template only by #displayImg on the <img> tag, which is correct. Ensure the ResizeObserver is properly cleaned up in DestroyRef to prevent memory leaks.

[🟠 High] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.spec.ts:40 — The test suite globally mocks URL.createObjectURL and URL.revokeObjectURL. This is a shared resource; other tests in the same test run may rely on the real implementation. This could cause flaky tests or false positives. It's safer to mock per-test or restore after each test.

[🟡 Medium] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.spec.ts:45 — The test stubs HTMLImageElement.prototype.decode globally. This affects all tests running in the same context, potentially breaking other specs. Use jest.spyOn(instance, 'decode') instead.

[🟡 Medium] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.spec.ts:48naturalWidth and naturalHeight are defined as getters on the prototype, affecting all image elements. This could cause unrealistic test behavior elsewhere. Prefer mocking per instance.

[🟡 Medium] core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.spec.ts:344 — The dispatchedEvent helper function searches for events by type suffix, which is fragile if event types change. It's used across multiple tests; a change in event naming could break all tests. Consider a more robust method.

[🟡 Medium] core-web/libs/edit-content/src/lib/edit-content.shell.component.ts:25DialogService is provided at the component level. This is correct for scoping, but ensure that any parent injector doesn't already provide DialogService (which could cause conflicts). The comment explains the intent, which is good.

[🟡 Medium] core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts:359 — The test 'should fall back to the legacy Dojo editor when the new editor is disabled' mocks DotBinaryFieldEditImageService.prototype.openImageEditor. This modifies the prototype, which could affect other tests running in the same suite. Use jest.spyOn(instance, 'openImageEditor') instead.

[🟡 Medium] core-web/libs/edit-content/src/lib/fields/shared/image-editor-launcher/angular-image-editor.launcher.spec.ts:15featureFlag$ is a BehaviorSubject but the test uses featureFlag$.next(false) to simulate the flag being off. However, the service's #enabled signal is derived from getFeatureFlagWithDefault, which returns the subject. Changing the subject after service creation tests the reactive behavior, which is correct. Ensure the service is recreated for each test to avoid state leakage.

The PR introduces a new image editor with a feature flag and fallback to legacy. The changes are mostly clean, but there are a few issues with optional injection handling, view child references, and test pollution that need attention.


Run: #28042028043 · tokens: in: 21199 · out: 1407 · total: 22606

@oidacra oidacra force-pushed the issue-36063-image-editor-build-dotimageeditorcomponent-modal-s branch from 03448ba to c2f4635 Compare June 18, 2026 21:27
@oidacra oidacra force-pushed the issue-36063-image-editor-build-dotimageeditorcomponent-modal-s branch from c2f4635 to a1d5b80 Compare June 19, 2026 14:46
@oidacra oidacra force-pushed the issue-36063-image-editor-build-dotimageeditorcomponent-modal-s branch from a1d5b80 to 5f38ef3 Compare June 19, 2026 14:51
@oidacra oidacra changed the title feat(image-editor): add Angular image editor with events-based signalStore feat(image-editor): add Angular image editor Jun 19, 2026
@oidacra oidacra marked this pull request as ready for review June 22, 2026 17:14
@oidacra oidacra requested review from hmoreras and nicobytes June 22, 2026 17:15
Comment thread core-web/libs/image-editor/src/lib/utils/panel-state.storage.ts Outdated
Comment thread core-web/libs/image-editor/src/lib/image-editor.constants.ts
Comment thread core-web/libs/ui/src/lib/theme/theme.config.ts
- Give scale/resize their own 'resize' history category (no longer coalesce with
  brightness/hue/saturation under 'adjust').
- Undo/redo guard also treats role=combobox (PrimeNG select/dropdown/autocomplete)
  as an editable target so shortcuts don't hijack keys inside open controls.
- Binary field launcher subscribe gets an error callback (no silent failure).
- Transform panel dispatches the displayed (computed outputDimensions) value for the
  unchanged axis, so persisted output dims match what the field shows.
- History entry ids use a monotonic counter instead of Date.now() (collision-free,
  fake-timer stable).
- with-preview resolveSize$ gets catchError so a dispatch throw can't freeze the size
  readout; remove console.warn from panel-state storage; fix canvas/launcher-token
  comments. Test additions: quality lower-bound clamp, download append/remove asserts.
oidacra added 5 commits June 26, 2026 11:34
…ory step

Edit history coalesced on the broad FilterCategory, so changing brightness
then hue (both 'adjust') overwrote the brightness step instead of recording a
new one — leaving only the last edit per category. The same affected flipH/flipV,
scale/output-dims and compression/quality.

Coalescing now keys on a per-control 'coalesceKey' on the history entry:
repeated edits to one control still merge into a single step, but switching to a
different control in the same category starts a new step.
…rmed

A crop drawn before rotating was emitted after the Rotate filter and applied
with original-image coordinates, so the server cropped the already-rotated
(width/height-swapped) image and cut the wrong region.

The filter chain now orders Crop relative to rotate/flip by the order the user
performed them (read from history): crop-then-rotate builds Crop,Rotate (crop the
original, then rotate the result); rotate-then-crop keeps Rotate,Crop. The crop
overlay now converts the box against the displayed (post-transform) image's
intrinsic size instead of the original asset, so a crop drawn on a rotated
preview scales correctly as well.
Esc closed the dialog directly through PrimeNG's closeOnEscape, skipping the
unsaved-changes guard. Esc now routes through requestClose() (closeOnEscape is
disabled on the dialog): it prompts to discard when there are edits and closes
immediately when clean. A re-entry guard stops a repeated Esc from re-opening
the prompt.
…F on libvips

File info now shows the effective output Format (the chosen compression format,
or the source format derived from the asset MIME type when not converting) and
the Focal point (the store value, defaulting to the centre 0.50, 0.50 since it
is not read back from the asset yet).

AVIF is a libvips-only output format, so the AVIF compression option is offered
only when IMAGE_API_USE_LIBVIPS is enabled. The UI reads it through
DotPropertiesService.getKey; the key is whitelisted in ConfigurationResource so
the configuration endpoint exposes it to the client.
…tor-build-dotimageeditorcomponent-modal-s

# Conflicts:
#	core-web/tsconfig.base.json
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:400getFileMetadata(this.contentlet) returns Partial<DotFileMetadata>; if metadata.name is undefined, fileName becomes undefined and is passed to ImageEditorOpenParams which expects a non-optional string — causes runtime crash
  • 🟠 High: core-web/libs/image-editor/src/lib/components/dot-image-editor-address-bar/dot-image-editor-address-bar.component.ts:117navigator.clipboard.writeText() called without user gesture validation — fails silently in Safari and strict privacy browsers
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:147stageTransform() uses zoomLevel() and panOffset() without bounds checking — may cause CSS transform overflow or layout thrashing

Existing

  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:404 — fileName may be undefined when metadata.name is undefined but ImageEditorOpenParams.fileName is non-optional — causes runtime crash
  • 🟠 High: core-web/libs/image-editor/src/lib/components/dot-image-editor-address-bar/dot-image-editor-address-bar.component.ts:117 — navigator.clipboard.writeText() called without user gesture validation — fails silently in Safari and strict privacy browsers
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:147 — stageTransform() uses zoomLevel() and panOffset() without bounds checking — may cause CSS transform overflow or layout thrashing

Resolved

  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:400 — now uses this.contentlet?.fileName ?? metadata?.name to avoid undefined fileName
  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:404 — now uses this.contentlet?.fileName ?? metadata?.name to avoid undefined fileName

Run: #28253375326 · tokens: in: 21774 · out: 896 · total: 22670

oidacra added 2 commits June 26, 2026 13:44
…order, config, docs)

- SCSS: use the PrimeNG 21 --p-* theme tokens (canvas/crop/focal overlays) instead
  of the bare --surface-*/--primary-* names, which are undefined under the styled
  theme and silently fell back to hardcoded hex (active controls ignored the theme).
- service: getFileSize returns null (not 0) for a missing/unreadable Content-Length
  so an unknown size renders as the em dash, not a misleading 0.0 KB; inject DOCUMENT
  instead of the global document/new Image(); log asset-metadata load failures.
- with-preview: order Crop relative to rotate/flip by the LAST crop entry so a
  crop -> rotate -> crop sequence orders against the current crop, not the stale first.
- tsconfig: drop the stray @dotcms/agentic-tools path mapping (no such file, unused).
- config: declare IMAGE_API_USE_LIBVIPS=false so the endpoint returns a real boolean.
- docs: correct the focal-point (editor-state-only), save-flow, address-bar tool list,
  and coalesceHistory JSDoc placement.
…y failure

- Bail out of onEditImage() when the field name/variable haven't resolved instead of
  leaking undefined into the launcher params (the image-editor lib treats them as
  required strings; edit-content's strict:false hid the mismatch).
- Surface a failure of the editor stream to the user with an error toast (optional
  MessageService, best-effort) in addition to the console log, so an edit that fails
  to apply is no longer silently dropped.
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:404fileName: this.contentlet?.fileName ?? metadata?.name may pass undefined to ImageEditorOpenParams.fileName which is non-optional — causes runtime crash if contentlet.fileName is null and metadata.name is undefined
  • 🟠 High: core-web/libs/image-editor/src/lib/components/dot-image-editor-address-bar/dot-image-editor-address-bar.component.ts:117navigator.clipboard.writeText() called without user gesture validation — fails silently in Safari and strict privacy browsers
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:147stageTransform() uses zoomLevel() and panOffset() without bounds checking — may cause CSS transform overflow or layout thrashing

Existing

  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:400 — getFileMetadata(this.contentlet) returns Partial; if metadata.name is undefined, fileName becomes undefined and is passed to ImageEditorOpenParams which expects a non-optional string — causes runtime crash
  • 🟠 High: core-web/libs/image-editor/src/lib/components/dot-image-editor-address-bar/dot-image-editor-address-bar.component.ts:117 — navigator.clipboard.writeText() called without user gesture validation — fails silently in Safari and strict privacy browsers
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:147 — stageTransform() uses zoomLevel() and panOffset() without bounds checking — may cause CSS transform overflow or layout thrashing

Resolved

  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:395 — Legacy dotBinaryFieldEditImageService.openImageEditor() call now guarded by isAvailable() check — no longer unconditionally invoked
  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.spec.ts:301 — Test now verifies launcher path instead of legacy service — no longer mocks deprecated behavior

Run: #28255273355 · tokens: in: 21790 · out: 924 · total: 22714

…agName constant

Add IMAGE_API_USE_LIBVIPS to FeatureFlagName and reference it in ConfigurationResource's
whitelist/boolean-flag sets instead of a bare string literal, matching the pattern of the
other entries.
oidacra added 2 commits June 26, 2026 14:01
- Add redo-disabled, async clipboard-copy success, and clipboard-error tests to the
  address bar; assert the copy toast after the promise settles (whenStable).
- Add a quality-slider dispatch test to the file-info panel.
- Restore the Dispatcher.prototype/confirm spies after each test in the shell spec
  (clearAllMocks does not restore spied methods).
- Scope the with-crop TestBed configuration into per-suite beforeEach blocks.
- Add data-testid to the crop-mask panels and query them by test id.
- Drive the crop preset/orientation through the dropdown and buttons instead of poking
  the component's internal signals.
…omponents

Per ANGULAR_STANDARDS, prefix every component signal member (input/output/computed/
signal/viewChild) with $. Inputs and outputs carry an explicit alias matching the
original name so public binding names — and therefore parent templates — are unchanged.
Updated all internal references in each component's .ts, template, and spec, plus the
cross-component reads (canvas -> crop overlay $naturalCropSize, shell spec $close/$cancel,
address-bar spec output names).
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — qwen.qwen3-next-80b-a3b

New Issues

  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:414console.error used instead of Logger for error logging; violates dotCMS convention
  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:427console.error used instead of Logger for error logging; violates dotCMS convention
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-address-bar/dot-image-editor-address-bar.component.ts:110navigator.clipboard.writeText() called without user gesture validation — fails silently in Safari and strict privacy browsers (repeats prior finding)
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:147stageTransform() uses zoomLevel() and panOffset() without bounds checking — may cause CSS transform overflow or layout thrashing (repeats prior finding)

Existing

  • 🟡 Medium: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:404 — fileName may be undefined when both contentlet.fileName and metadata.name are null/undefined, but ImageEditorOpenParams.fileName is non-optional (still present: fileName: this.contentlet?.fileName ?? metadata?.name — no null/undefined guard)
  • 🟠 High: core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:400 — getFileMetadata(this.contentlet) returns Partial; if metadata.name is undefined, fileName becomes undefined and is passed to ImageEditorOpenParams which expects a non-optional string — causes runtime crash (still present: fileName: this.contentlet?.fileName ?? metadata?.name — no fallback to empty string)
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-address-bar/dot-image-editor-address-bar.component.ts:117 — navigator.clipboard.writeText() called without user gesture validation — fails silently in Safari and strict privacy browsers (repeats prior finding)
  • 🟡 Medium: core-web/libs/image-editor/src/lib/components/dot-image-editor-canvas/dot-image-editor-canvas.component.ts:147 — stageTransform() uses zoomLevel() and panOffset() without bounds checking — may cause CSS transform overflow or layout thrashing (repeats prior finding)

Resolved

  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:400 — Prior finding about undefined fileName now has fallback logic, but still lacks null/undefined guard — not fully resolved
  • core-web/libs/edit-content/src/lib/fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component.ts:404 — Prior finding about undefined fileName now has fallback logic, but still lacks null/undefined guard — not fully resolved

Run: #28257230742 · tokens: in: 21773 · out: 1179 · total: 22952

@mergify

mergify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code Area : Frontend PR changes Angular/TypeScript frontend code

Projects

Status: No status

3 participants