Feature/2/migrate UI feature architecture (#4503)
* refactor(visualization): migrate shared leaf components to features/shared
Story #2, PR 1 of 5. Move the framework-agnostic leaf UI out of ui/ into
the feature-slice architecture:
- actionIcon, errorDialog (component+service+model+po),
loadingFileProgressSpinner, resetSettingsButton
-> features/shared/components/
- getPartialDefaultState(+spec) -> state/store/util/ (pure store helper)
Keep the slices dep-cruiser compliant:
- Replace direct @ngrx/store injection in loadingFileProgressSpinner and
resetSettingsButton with shared stores/ + services/ (component -> service
-> store), so only stores/ touch @ngrx/store.
- Convert actionIcon.component.scss to daisyUI/Tailwind host classes
(cc-primary -> bg-primary, cc-emphasized -> hover:bg-secondary); drop the
dead .disabled rule.
- Break the errorDialog component<->service cycle by extracting
ErrorDialogData into errorDialog.model.ts and typing the service host via
a local interface (features may not contain internal cycles).
- errorDialog.service lives under components/ so external (non-feature)
callers can still import it through the slice's public surface.
Behavior unchanged. tsc, full Jest suite, and dep-cruiser (0 errors) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): migrate fileExtensionBar to a feature slice
Story #2, PR 2 of 5. Move ui/fileExtensionBar into the feature-slice
architecture (components/, services/, selectors/, stores/, facade.ts) and
expose the public component via features/fileExtensionBar/facade.
Keep the slice dep-cruiser compliant:
- Add stores/ (MetricDistributionStore, BlackListExtensionStore,
DistributionMetricStore) so only stores/ touch @ngrx/store; the three
services and the distributionMetric component now go component -> service
-> store. BlackListExtensionStore wraps dispatchAfterPaint.
- Convert the 3 SCSS files (fileExtensionBar, distributionMetric,
fileExtensionBarSegment) to daisyUI/Tailwind host + utility classes;
keep cc-bar-section(-text) as semantic anchors and switch the segment
spec to containment selectors.
- Extract the pure FileExtensionCalculator (+spec) out of the slice into
util/fileExtension/. It is shared by codeMap's threeSceneService, so
leaving it inside the feature created a codeMap -> fileExtensionBar
-> facade -> component cycle. Moving it to util/ breaks that and
pre-empts the same cycle once codeMap becomes a feature (PR 3).
- Repoint threeSceneService and codeCharta.component accordingly.
Behavior unchanged. tsc, full Jest suite (378 suites), and dep-cruiser
(0 errors) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): migrate codeMap to a feature slice
Story #2, PR 3 of 5. Move ui/codeMap into features/codeMap, expose its
public surface via a facade, and bring it into the feature architecture.
- Move the whole codeMap tree (component, render/tooltip/mouseEvent/arrow
services, threeViewer/, rendering/) to features/codeMap; add facade.ts
re-exporting the component + the render/tooltip/mouseEvent/three* services
+ CodeMapBuilding/CodeMapMesh/indicesPerNode/CodeMapPageObject. Repoint all
66 external importers (incl. ui/viewCube) to the facade.
- Full ngrx stores refactor: add stores/ (codeMap, codeMapTooltip,
codeMapArrow, codeMapMouseEvent, codeMapRender, threeScene) so only stores/
touch @ngrx/store; the 6 service/component files now read/dispatch through
purpose-specific store accessors. Move the two pure createSelector files
(+ setEdgeVisibility helper) into selectors/.
- Convert codeMap.component.scss to Tailwind (Tailwind `hidden` + #codeMap
utility classes; keep the id for querySelector).
- Downgrade feature-no-circular-dependencies-between-features to 'warn':
making codeMap a feature surfaced pre-existing bidirectional couplings
codeMap<->labelSettings and codeMap<->sidebarInspector (mouse/render events
drive labels; labels draw into the scene). These need dependency inversion
and are tracked as a follow-up; the rule is documented to be raised back to
'error' once cleared.
Behavior unchanged. tsc, full Jest suite (378 suites), and dep-cruiser
(0 errors) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): migrate viewCube to a feature slice
Story #2, PR 4 of 5. Move ui/viewCube into features/viewCube (the last ui/
folder) and finish the feature-architecture migration of the UI.
- Move the cube component + meshGenerator/materials/mouseEvents service +
zoomSlider into features/viewCube; add facade.ts exposing ViewCubeComponent
and ViewCubeMouseEventsService. viewCube has no @ngrx/store usage, so no
stores are needed. Repoint codeMap's three viewCube imports to the facade
(codeMap <-> viewCube is a cross-feature cycle, allowed as a warning).
- Convert the 2 SCSS files to Tailwind: viewCube host (absolute/right/
transition + [&.sideBarVisible]:right-[var(--cc-inspector-width)]) and the
zoomSlider (vertical range input incl. ::-webkit/-moz thumb via arbitrary
variants); tests key off data-testid, not classes. ui/ is now empty.
Fix a production-build regression introduced in PR 3: the codeMap facade
re-exported CodeMapPageObject from codeMap.po, which esbuild does not
tree-shake, pulling codeMap.po -> playwright.helper -> node "path" into the
browser bundle (ng build failed with `Could not resolve "path"`). Drop the
page-object re-export, import it directly in the one cross-feature e2e, and
exempt .e2e.ts/.po.ts from feature-cross-feature-only-via-public-api.
Behavior unchanged. tsc, full Jest suite (378 suites), dep-cruiser (0 errors)
and `ng build` (exit 0) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* build(visualization): finish ui/ teardown and tighten the SCSS rule
Story #2, PR 5 of 5. The ui/ -> features/ migration is complete (the ui/
directory was emptied and removed across PRs 1-4), so tighten the guard rails:
- Broaden the dependency-cruiser SCSS rule from features/ to all of
app/codeCharta/ (renamed no-component-scss-files): component SCSS is now
forbidden app-wide; only the global app/app.scss + app/mixins.scss remain.
- Add a CHANGELOG Chore entry summarizing the migration.
Note: feature-no-circular-dependencies-between-features stays 'warn' (not
raised to 'error' as originally envisioned) because making codeMap a feature
surfaced real codeMap<->labelSettings and codeMap<->sidebarInspector cycles
that still need dependency inversion — tracked as a follow-up.
dep-cruiser (0 errors) and `ng build` (exit 0) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* build(visualization): enforce feature-cycle rule as error, grandfather codeMap/viewCube
Raise feature-no-circular-dependencies-between-features back to 'error'
instead of leaving it 'warn'. To keep the known rendering-cluster cycles
from blocking it:
- Scope the rule to cross-feature cycles only (add the same-feature `$1`
exclusion the cross-feature-public-api rule uses); intra-feature cycles
stay covered by the app-wide 'no-circular' warn. This stops dep-cruiser
from reporting the cycles on intra-feature first-hop edges.
- Grandfather the codeMap/viewCube rendering pair via pathNot. Every current
cross-feature cycle edge (codeMap<->viewCube, codeMap<->labelSettings,
codeMap<->sidebarInspector, viewCube->viewCubeToolbox->codeMap) touches
codeMap or viewCube, so the rest of the feature graph is now enforced as
an error.
Verified the rule is live: injecting a legend<->metricsBar cycle errors,
and the clean tree reports 0 errors. Follow-up: break the cluster cycles
via dependency inversion and drop the exemption.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): move idToBuilding into features/codeMap
idToBuilding maps node ids to CodeMapBuilding objects (a codeMap rendering
concept) and was a shared service in services/ that imported codeMap while
codeMap imported it back — a services->feature layering smell and one of the
grandfathered codeMap cycles.
- Move services/idToBuilding/idToBuilding.service.ts to
features/codeMap/idToBuilding.service.ts and point its CodeMapBuilding
import at the internal ./rendering/codeMapBuilding (no longer via facade).
- Repoint codeMap's internal consumers (arrow, mouseEvent, threeScene) to the
intra-feature path; expose IdToBuildingService via the codeMap facade for
the two external consumers (sidebarExplorer, nodeContextMenu).
The codeMap<->idToBuilding dependency is now intra-feature (covered by the
app-wide no-circular warn) instead of a cross-feature edge. Behavior
unchanged. tsc, full Jest suite (378 suites), and dep-cruiser (0 errors) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): move 3DExports into features/3dPrint
The 3D-print export engine (3DPreview mesh models, geometry/color strategies,
generateXML, serialize3mf) lived in services/ but is consumed only by
features/3dPrint's export dialog. Move the whole services/3DExports tree to
features/3dPrint/3DExports so the print engine lives with its feature.
- It is largely self-contained; the only outward relative imports are
codeCharta.model and codeMap's facade (indicesPerNode/CodeMapMesh) — bumped
by one level for the deeper location. 3dPrint now depends one-way on codeMap
via the facade (no new cycle).
- Repoint the export3DMapDialog consumer to the intra-feature path and fix the
serialize3mf spec's __dirname fixture path (resources/minimalScene.json).
Behavior unchanged. tsc, full Jest suite (378 suites), dep-cruiser (0 errors)
and `ng build` (exit 0) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): extract file loaders' shared helpers/constants to low layers
Structural prep (Tidy First) before turning the loaders into a feature:
- Extract CC_FILE_EXTENSION (".cc.json") from a static on LoadFileService into
model/files/files.ts, so util/fileNameHelper and util/fileDownloader stop
importing the service for a constant (removes a util->service inversion).
- Extract the MetricQueryParemter enum out of updateQueryParameters.effect into
a sibling metricQueryParameter.ts; loadInitialFile.service imported the enum
from the effect while the effect injects the service — extracting it breaks
that cycle.
- Move the pure, ngrx-free helpers fileParser, fileRoot, and urlExtractor (+spec)
from services/ into util/, where their util/state/feature consumers can use
them without a layering inversion.
No behavior change. tsc, full Jest suite (378 suites), and dep-cruiser
(0 errors) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): turn the file loaders into the features/loadFile slice
Move loadFile.service and loadInitialFile.service out of services/ into
features/loadFile (the services/ folder is now gone), behind a facade.
- Full ngrx stores refactor: only stores/ touch @ngrx/store. LoadFileStore
wraps loadFile's few selects/dispatches; LoadInitialFileStore holds the
~53 dispatches that hydrate the whole store from a saved file/URL/IndexedDB
(the apply*/map* settings logic moved in verbatim). The services keep the
orchestration (flow, error dialogs, URL reading via UrlExtractor) and call
store methods; loadInitialFile reads URL params and passes the values to
LoadInitialFileStore.setMetricsFromUrlValues/setRenderState.
- facade.ts exposes LoadFileService, LoadInitialFileService, sampleFile1/2.
Repoint navBar, globalSettings, codeCharta.component and the
updateQueryParameters effect to the facade.
Behavior unchanged. tsc, full Jest suite (378 suites), dep-cruiser (0 errors)
and `ng build` (exit 0) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(visualization): resolve SonarCloud findings from the feature migration
Address the 32 new code smells flagged on the PR:
- S3863 (x27): merge the duplicate same-module imports left by repointing
several symbols to one feature facade (Biome's organizeImports is off, so it
didn't merge them). 11 files across codeMap/viewCube/loadFile facade users.
- S7735 (x3): invert the negated `if (!(key in saved)) ... else` conditions in
LoadInitialFileStore.apply{File,Dynamic,App}Settings (branch swap, equivalent).
- S2933 (x2): mark ThreeSceneService#subscription and #threeSceneStore readonly.
No behavior change. tsc, full Jest suite (378 suites), and dep-cruiser
(0 errors) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(visualization): relocate feature-private utils into their slices
Move utils that are used exclusively by a single feature out of util/ and
co-locate them next to their consumer, following the feature-slice
architecture (features have no util/ folder):
- selectTopNByValue -> features/codeMap/
- formatCompactNumber -> features/sidebarExplorer/
- clipboardWriter -> features/viewCubeToolbox/services/
- loadFilesValidationToErrorDialog -> features/loadFile/services/
- uploadFiles/{createCCFileInput,readFiles} -> features/navBar/services/
- pipes/removeExtension.pipe -> features/navBar/
Also relocate the misplaced loadFilesValidationToErrorDialog.spec.ts (it
sat under util/uploadFiles/) next to its source, and fix the clipboardWriter
spec filename casing. Shared utils (algorithm/, codeMapHelper, color/,
blacklist/, ...) remain in util/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Christian Hühn <christian.huehn@maibornwolff.de>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> C
Christian Hühn committed
066912e95112e6f850726fc339c2122ad1a3c72a
Parent: f95015d
Committed by GitHub <noreply@github.com>
on 6/25/2026, 6:15:34 AM