Skip to content

refactor(frontend): convert all components to standalone#4873

Merged
Yicong-Huang merged 6 commits into
apache:mainfrom
Yicong-Huang:refactor/standalone-components
May 3, 2026
Merged

refactor(frontend): convert all components to standalone#4873
Yicong-Huang merged 6 commits into
apache:mainfrom
Yicong-Huang:refactor/standalone-components

Conversation

@Yicong-Huang
Copy link
Copy Markdown
Contributor

@Yicong-Huang Yicong-Huang commented May 3, 2026

What changes were proposed in this PR?

Run Angular's official ng generate @angular/core:standalone --mode=convert-to-standalone --defaults across the frontend. Flips every @Component({ standalone: false }) to standalone: true, auto-derives per-component imports: [...] from each template's directive usage, and updates AppModule.declarations accordingly.

Plus one manual fixup: 5 components (admin-user, filters, user-dataset, user-workflow, menu) reference <nz-button-group> / [nzBorderless] which ng-zorro-antd v21 has removed. The legacy builder didn't catch these latent dead-code refs; the new strict builder does. Add schemas: [CUSTOM_ELEMENTS_SCHEMA] to keep this PR focused — replacing with <nz-space-compact> belongs in its own follow-up.

Any related issues, documentation, discussions?

Closes #4864.

After this and #4862 land, the 43 component specs + 13 service specs currently excluded for the NgModule-scope template-check failure can come off the exclusion list in a follow-up.

How was this PR tested?

yarn run build exits 0 with a clean prod bundle. Given the lack of test suites in frontend, I manually tests this locally.
Tested login, create dataset, file upload, create workflow, run workflow, etc.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7 (1M context)

Run Angular's official `ng generate @angular/core:standalone
--mode=convert-to-standalone --defaults` across the frontend. This
flips every `@Component({ standalone: false })` to `standalone: true`,
auto-derives a per-component `imports: [...]` from each template's
directive usage, and updates `AppModule.declarations` to `imports`
where appropriate.

Why: under the new `@angular/build:unit-test` builder (Vitest path),
template directive resolution happens per-component instead of via
the declaring NgModule's imports. NgModule-declared components fail
NG8001/NG8002 in spec compilation because their templates reference
directives the component itself doesn't import. The migration PR
(apache#4862) excluded 43 component specs and 13 service specs reaching the
auth → modal chain for exactly this reason. With every component now
carrying its own imports list, those exclusions can come off in a
follow-up.

Manual fix-ups on top of the schematic:

- The schematic doesn't import directives that don't exist anymore.
  Five templates use `<nz-button-group>` and `[nzBorderless]`, which
  ng-zorro-antd v21 has removed. The legacy custom-webpack:browser
  builder doesn't enforce element/property checking the way the new
  builder does, so these were latent dead-code references already.
  Add `schemas: [CUSTOM_ELEMENTS_SCHEMA]` to the five affected
  components (admin-user, filters, user-dataset, user-workflow, menu)
  to keep the migration focused — replacing them with `<nz-space-compact>`
  is a real template fix that belongs in its own follow-up.

Result: 94 files changed, +2130 / −218. `yarn run build` exits 0;
the ng-zorro deprecation warnings on imports were pre-existing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added refactor Refactor the code frontend Changes related to the frontend GUI labels May 3, 2026
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 48.37%. Comparing base (9223ddf) to head (ad0bb69).

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #4873      +/-   ##
============================================
+ Coverage     47.66%   48.37%   +0.71%     
- Complexity     2151     2222      +71     
============================================
  Files           817      817              
  Lines         25975    25955      -20     
  Branches       2343     2343              
============================================
+ Hits          12381    12556     +175     
+ Misses        12845    12607     -238     
- Partials        749      792      +43     
Flag Coverage Δ
frontend 45.31% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aglinxinyuan
Copy link
Copy Markdown
Contributor

Did you try to run frontend locally? The homepage doesn't display when I try to convert to standalone last time.

@Yicong-Huang
Copy link
Copy Markdown
Contributor Author

yeah same issue, trying to fix it now

The standalone migration in the previous commit surfaced a real circular
import that NgModule's lazy declaration resolution had been hiding:

  dataset-detail.component.ts  →  imports FilesUploaderComponent (template uses it)
  files-uploader.component.ts  →  imports DatasetDetailComponent  (constructor injected
                                                                   it via @host() @optional()
                                                                   for ownerEmail / datasetName)

Under standalone, every component import becomes a hard ES-module edge.
The cycle then triggers a TDZ at runtime:
`ReferenceError: Cannot access 'DatasetDetailComponent' before initialization`,
and the whole app renders blank.

Fix: replace the parent-injection lookup with standard `@Input()` data
flow. `FilesUploaderComponent` no longer knows or cares about its host
component — it just exposes `ownerEmail` and `datasetName` as inputs.
`DatasetDetailComponent` passes its own values via template binding:

  <texera-user-files-uploader
    [ownerEmail]="ownerEmail"
    [datasetName]="datasetName"
    (uploadedFiles)="onNewUploadFilesChanged($event)">
  </texera-user-files-uploader>

The cycle disappears at the source: only one direction of import remains
(dataset-detail → files-uploader), which is the natural parent → child
relationship. No `forwardRef`, no DI token, no third file.

`FilesUploaderComponent` is only consumed by `DatasetDetailComponent`,
so the input default of empty string is sufficient — no other call site
loses behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Yicong-Huang
Copy link
Copy Markdown
Contributor Author

There was a cyclic dependency of DatasetDetailComponent between user-computing-unit.component.ts and files-uploader.component.ts. New commit fixes this by breaking the cycle.

@Yicong-Huang
Copy link
Copy Markdown
Contributor Author

@mengw15 can you help test the UI as well?

github-actions Bot and others added 3 commits May 3, 2026 14:56
ng-zorro-antd v21 removed `<nz-button-group>` (replaced by
`<nz-space-compact>`) and the `nzBorderless` input on `<nz-select>`
(replaced by `nzVariant="borderless"`). texera bumped to v21 but the
templates still reference the old names. The legacy
custom-webpack:browser builder silently ignored unknown elements /
inputs, so the breakage was visible only as a layout glitch — for
example, the admin user table's role pill rendered floating outside
its bordered `<nz-select>` cell because the borderless mode never
took effect.

The previous commit added `schemas: [CUSTOM_ELEMENTS_SCHEMA]` to the
five affected components to keep the standalone-migration commit
scoped tight; this commit replaces that workaround with the proper
v21 names:

- 5 `<nz-button-group>` → `<nz-space-compact>` (in: menu × 3,
  filters × 1, user-workflow × 1)
- 3 `nzBorderless` / `[nzBorderless]="true"` → `nzVariant="borderless"`
  (in: admin-user, user-dataset, user-workflow)

For each component whose template now uses `<nz-space-compact>`, add
`NzSpaceCompactComponent` from `ng-zorro-antd/space` to its `imports`
list. Drop `CUSTOM_ELEMENTS_SCHEMA` from all five components — the
templates are now valid v21 and don't need the schema escape hatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CUSTOM_ELEMENTS_SCHEMA-removal sed in the previous commit left
behind a bare \`,\` line in three components' imports arrays — between
the last legitimate entry and the appended \`NzSpaceCompactComponent\`.
TypeScript reads the empty slot as \`undefined\`, which doesn't satisfy
\`@Component\`'s \`imports?: (Type<any> | ReadonlyArray<any>)[]\` and
fails with TS2322.

Affected files: filters, user-workflow, menu component .ts. Strip the
empty-comma lines so the imports list goes straight from the last
real entry to NzSpaceCompactComponent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous sed replaced \`[nzBorderless]="true"\` with
\`[nzVariant="borderless"\` — the leading bracket from the property-binding
form survived because the regex matched only \`nzBorderless\`. The
result is an invalid HTML attribute name that surfaces as a runtime
\`InvalidCharacterError\` from \`setAttribute\` once the user-dataset /
user-workflow templates render.

Drop the leading \`[\` in both files. \`nzVariant\` is set to a static
string \`"borderless"\`, so the property-binding brackets aren't
needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@mengw15 mengw15 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Yicong-Huang Yicong-Huang enabled auto-merge (squash) May 3, 2026 22:28
@Yicong-Huang Yicong-Huang merged commit 3dd556a into apache:main May 3, 2026
15 checks passed
@Yicong-Huang Yicong-Huang deleted the refactor/standalone-components branch May 3, 2026 22:42
Yicong-Huang pushed a commit to Yicong-Huang/texera that referenced this pull request May 3, 2026
…standalone

After the standalone-component migration (PR apache#4873) and the Vitest
runner switch (PR apache#4862, now merged), many of the specs that were
excluded from the Vitest run in apache#4862 compile and pass cleanly.

Removed exclusions for the 24 specs that now pass:
  - All previously-excluded service specs that reach the auth/modal
    chain (workflow-websocket, execute-workflow, udf-debug,
    user-config, operator-menu, workflow-console,
    operator-reuse-cache-status, workflow-result-export,
    workflow-result, etc.)
  - The done-callback specs (workflow-result already rewritten;
    download / preset bodies stub `done`/`fail` until apache#4861 follow-ups)

The remaining 48 component / service specs are still excluded — they
have TestBed setups configured against the old NgModule scope. Each
needs its TestBed `imports`/`providers` adapted to the standalone
graph. Tracked as follow-ups under apache#4861.

Mechanical fixes applied along the way:
- 2 `xit(` → `it.skip(`
- 4 `.toHaveBeenCalledOnceWith(` → `.toHaveBeenCalledExactlyOnceWith(`
- 3 `.mockReturnValue()` (no arg) → `.mockReturnValue(undefined)`
  (Vitest requires the value, Jasmine's `.and.returnValue()` allowed empty)
- One partial-mock `as any` cast on a code-debugger.spec
- `done`/`fail` stubs in download / preset specs whose tests are
  `it.skip`'d pending async/await rewrites

Local result: 21 passed / 3 skipped (24 test files); 150 passed / 8
skipped / 2 todo (160 tests). Up from 14 / 109 on the migration
baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yicong-Huang added a commit to Yicong-Huang/texera that referenced this pull request May 4, 2026
… specs

Drop the two from the exclusion lists and migrate their TestBed setups
to the standalone-component shape: `declarations: [Comp]` →
`imports: [Comp]`. The components themselves are already standalone
post-apache#4873; the TestBed config just needed the same treatment.

Local result: 23 test files passed / 3 skipped (26); 152 tests passed.
Up from 21 / 150 on main.

Slice of apache#4880.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yicong-Huang added a commit that referenced this pull request May 4, 2026
…e migration (#4886)

### What changes were proposed in this PR?

Drop 13 spec files from the exclusion lists and migrate their TestBed
setups to the standalone-component shape — `declarations: [Comp]`
becomes `imports: [Comp]`. The components are already standalone
post-#4873; the spec configs just needed the same treatment.

Specs re-enabled:

- `hub/about`, `hub/browse-section`
- `dashboard/user/filters-instructions`, `user-avatar`, `user-icon`,
`user-quota`
- `dashboard/user/filters` (adds `provideNzI18n(en_US)` for the date
pickers' locale lookup)
- `dashboard/user/list-item` (adds `RouterTestingModule` +
`StubUserService` for `[routerLink]` and the `AuthService` chain)
- `dashboard/user/user-computing-unit` (registers `FileAddOutline` via
`NzIconModule.forChild` since jsdom can't fetch icons over HTTP)
- `dashboard/user/user-dataset-file-renderer`
- `workspace/menu/coeditor-user-icon` (also rewrites `waitForAsync`
`beforeEach` -> `async/await` so it runs without an outer ProxyZone)
- `workspace/service/workflow-graph/model/coeditor-presence`

### Any related issues, documentation, discussions?

A slice of #4880.

### How was this PR tested?

`yarn ng test --watch=false` locally:

```
Test Files  33 passed | 3 skipped (36)
     Tests  169 passed | 8 skipped | 2 todo (179)
```

Up from 21 / 150 on main.

### Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7 (1M context)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yicong-Huang added a commit that referenced this pull request May 4, 2026
…4889)

### What changes were proposed in this PR?

Drop 11 spec files from the exclusion lists and migrate their TestBed
setups to the standalone-component shape — `declarations: [Comp]`
becomes `imports: [Comp]`. The components are already standalone
post-#4873.

Specs re-enabled:

- `result-panel/console-frame`
- `result-panel/error-frame`
- `result-panel/result-panel`
- `result-panel/result-table-frame`
- `code-editor-dialog/breakpoint-condition-input`
- `power-button/computing-unit-selection`
- `property-editor/port-property-edit-frame`
- `property-editor/property-editor`
- `property-editor/typecasting-display`
- `workflow-editor/context-menu`
- `workflow-editor/mini-map`

The `waitForAsync` `beforeEach` wrappers are also rewritten to plain
`async/await` since `waitForAsync` needs an outer ProxyZone in
`beforeEach`, which the unit-test builder doesn't install.

### Specs that did NOT make it into this PR

Five workspace specs hit jsdom limitations beyond a TestBed migration
and remain in the exclude list with an inline comment explaining the
reason — to be picked up once Vitest browser mode (#4866) lands:

- `code-editor-dialog/code-debugger` & `code-editor` —
`document.queryCommandSupported` and monaco-editor CSS parsing aren't
supported by jsdom
- `codearea-custom-template` — same monaco-editor CSS parsing issue
- `property-editor/operator-property-edit-frame` — formly+nz registers
the `snippets-o` icon dynamically over HTTP, which jsdom rejects
- `workflow-editor/workflow-editor` — relies on real `getScreenCTM` for
jointjs paper math, which jsdom returns as null

### Any related issues, documentation, discussions?

A slice of #4880.

### How was this PR tested?

`yarn ng test --watch=false` locally:

```
Test Files  46 passed | 3 skipped (49)
     Tests  216 passed | 8 skipped | 2 todo (226)
```

Up from 35 / 182 on main.

### Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7 (1M context)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Yicong-Huang Yicong-Huang linked an issue May 4, 2026 that may be closed by this pull request
5 tasks
@Yicong-Huang Yicong-Huang mentioned this pull request May 4, 2026
5 tasks
Yicong-Huang added a commit that referenced this pull request May 4, 2026
### What changes were proposed in this PR?

Drop 6 spec files from the exclusion lists and migrate their TestBed
setups to the standalone-component shape — `declarations: [Comp]`
becomes `imports: [Comp]`. The components are already standalone
post-#4873.

Specs re-enabled:

- `dashboard/admin/execution`
- `dashboard/admin/settings`
- `dashboard/admin/user`
- `workspace/left-panel/left-panel` (also drops a `fakeAsync`
`beforeEach` wrapper that needed an outer ProxyZone; the body only used
`tick()` cosmetically)
- `workspace/left-panel/operator-menu/operator-label`
- `workspace/left-panel/operator-menu/operator-menu`

Also stubs `document.queryCommandSupported` in `jsdom-svg-polyfill.ts`
so specs that pull monaco in indirectly don't crash at module-load time.

### Specs that did NOT make it into this PR

The remaining excluded entries now fall into two clearly-labeled
buckets, captured inline in the exclude list:

**Placeholder specs (body entirely commented out — need real test cases
written):**
- `common/formly/preset-wrapper`
- `common/service/user/config/user-config`
- `workspace/left-panel/settings`
- `workspace/menu/menu`
- `workspace/workspace.component`
- `workspace/service/preset/preset.service`

**Blocked on jsdom limitations (belong under Vitest browser mode
#4866):**
- `code-editor-dialog/code-debugger`, `code-editor` — monaco CSS +
`queryCommandSupported`
- `codearea-custom-template` — monaco CSS
- `left-panel/time-travel`, `versions-list` — `monaco-breakpoints`
imports a `.css` file Vitest can't transform
- `property-editor/operator-property-edit-frame` — formly+nz fetches
`snippets-o` icon over HTTP
- `workflow-editor/workflow-editor` — jointjs paper math needs real
`getScreenCTM`
- `dashboard/dashboard.component` — `<asl-google-signin-button>`
dereferences fields a minimal `SocialAuthService` mock doesn't provide
- `user-project-list-item`, `user-workflow-list-item`,
`user-workflow.component` — templates rooted at `<nz-list-item>` need a
`<nz-list>` host wrapper

### Any related issues, documentation, discussions?

A slice of #4880 — closes the standalone-migration sweep portion of that
effort.

### How was this PR tested?

`yarn ng test --watch=false` locally:

```
Test Files  52 passed | 3 skipped (55)
     Tests  227 passed | 8 skipped | 2 todo (237)
```

Up from 46 / 216 on main.

### Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7 (1M context)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yicong-Huang added a commit that referenced this pull request May 6, 2026
…cs (#4944)

### What changes were proposed in this PR?

Drop 4 entries from the spec exclusion lists in `tsconfig.spec.json` and
`angular.json`'s test target, port their TestBed setups to the
standalone-component shape, and fix one malformed template attribute
that jsdom won't tolerate.

**Specs re-enabled:**

- `dashboard.component.spec.ts` — extends `socialAuthServiceMock` with
`initState` (the Google sign-in directive subscribes to it in its
constructor) and adds an `AdminSettingsService` stub; switches to
`imports: [DashboardComponent]` so its standalone graph carries
`GoogleSigninButtonModule` instead of relying on `NO_ERRORS_SCHEMA`.
- `user-project-list-item.component.spec.ts` — wraps the component in a
`TestHostComponent` template `<nz-list><texera-user-project-list-item
.../></nz-list>` so the `<nz-list-item>`-rooted component finds an
`NzListComponent` provider; adds `provideRouter([])` for the embedded
`[routerLink]`.
- `user-workflow-list-item.component.spec.ts` — same `<nz-list>` host
wrapper.
- `user-workflow.component.spec.ts` — switches `declarations: [...]` to
`imports: [...]` for the now-standalone children; converts
`beforeEach(waitForAsync(...))` to `beforeEach(async () => ...)` so the
setup runs outside the per-`it` ProxyZone wrapper.

**Drive-by template fix:**

`user-workflow.component.html` had a stray `]="true"` line — left over
from #4873's `[nzBorderless]="true"` → `nzVariant="borderless"` rewrite.
Browsers silently tolerate the malformed attribute, but jsdom throws
`InvalidCharacterError: "]" did not match the Name production` during
`setAttribute`, which is why the spec couldn't run under Vitest.

After this PR, the exclude list contains only specs gated on Vitest
browser mode (#4866) or specs whose body is still a placeholder.

### Any related issues, documentation, discussions?

Part of #4880.

### How was this PR tested?

`yarn install && yarn ng test --watch=false` exits 0 locally; `yarn
format:ci` is clean. CI exercises the same test command on the ubuntu /
windows / macos frontend matrices.

```
Test Files  56 passed | 3 skipped (59)
     Tests  248 passed | 8 skipped | 2 todo (258)
```

### Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7 (1M context)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yicong-Huang added a commit that referenced this pull request May 6, 2026
### What changes were proposed in this PR?

Most specs that #4866 listed as needing Vitest browser mode actually
weren't blocked by jsdom's missing layout — they were tripping on infra
and migration leftovers. Fix the root causes so they run under jsdom;
narrow #4866 to the real layout-dependent jointjs paper suite.

Three changes:

- `vitest.config.ts` inlines `monaco-breakpoints` so its `import
'./style.css'` goes through Vite instead of Node's ESM loader (which
crashes on `.css`).
- `jsdom-svg-polyfill.ts` stubs `requestIdleCallback` (Chrome-only).
- 6 specs get `declarations` → `imports` (post-#4873 standalone) and
`beforeEach(waitForAsync)` → `beforeEach(async)` (the ProxyZone wrapper
in `test-zone-setup.ts` only covers `it`/`test`).

Plus three small per-spec fixes: `vi.spyOn` → `.mockImplementation(() =>
{})` in `code-debugger` (vitest spies call through by default, unlike
Jasmine), `innerText` → `textContent` in `operator-property-edit-frame`
(jsdom's `innerText` is layout-dependent), and `it.skip` for the 2
drag-drop tests that genuinely need real geometry.

After this, the only spec excluded for jsdom-vs-real-browser reasons is
`workflow-editor.component.spec.ts`.

### Any related issues, documentation, discussions?

Part of #4861. Narrows #4866 to the jointjs paper suite + 2 drag-drop
geometry tests.

### How was this PR tested?

`yarn ng test --watch=false`: 251 pass, 11 skip, 2 todo. `yarn
format:ci` clean.

### Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7 (1M context)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aglinxinyuan pushed a commit that referenced this pull request May 15, 2026
…5072)

### What changes were proposed in this PR?

Delete a stray `]="true"` line in
`frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.html`.

**Root cause.** The standalone-conversion codemod from
[#4873](#4873) (commit
[`3dd556a`](3dd556a61c))
partially stripped an Angular property binding off the `<nz-select>` in
this template, leaving a bare `]="true"` with no matching `[<attribute>`
opening.

**Symptom.** During `UserDatasetComponent` initialization Angular calls
`element.setAttribute(']', 'true')`, which the browser rejects with:

```
InvalidCharacterError: Invalid qualified name: ']'
    at setAttribute (_dom_renderer-chunk.mjs:587)
    at UserDatasetComponent_Template — user-dataset.component.html:41
```

This blocks rendering of **both** `/dashboard/user/dataset` and
`/dashboard/user/workflow` (both routes load the same template chunk),
so users hit a blank page after login.

**Fix.** Delete the orphan line. Surrounding `[nzOpen]`, `[(ngModel)]`,
`[nzAllowClear]` bindings render fine on their own.

#### Before / after diff

```diff
       nzVariant="borderless"
-      ]="true"
       [nzOpen]="false"
       ngDefaultControl
       [(ngModel)]="filters.masterFilterList"
       [nzAllowClear]="true">
```

---

### Any related issues, documentation, discussions?

- Regression introduced by commit
[`3dd556a`](3dd556a61c) (PR
[#4873](#4873) —
"refactor(frontend): convert all components to standalone").
- No tracking issue was filed — the fix is one line and the root cause
is mechanical (codemod off-by-one).

---

### How was this PR tested?

1. **Formatting** (per `CONTRIBUTING.md` "yarn format:fix"). Ran the
project's prettier-eslint pipeline against the changed file:
   ```
   yarn prettier-eslint --list-different \

src/app/dashboard/component/user/user-dataset/user-dataset.component.html
   ```
   → exit 0, no diff. The file already conforms to the project format.

2. **Production build.** `yarn build` (≡ `ng build
--configuration=production`) → **exit 0**.

---

### Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.7

The bug was diagnosed with assistance from Claude Code (Anthropic Claude
Opus 4.7), which read the FileService server-side stack trace and Safari
console error, traced the regression to commit
[`3dd556a`](3dd556a61c) via `git
blame`, and authored this PR description. The one-line code change was
reviewed and verified manually.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend Changes related to the frontend GUI refactor Refactor the code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-enable component specs (NgModule → standalone or module-scope fix) Migrate Angular to standalone

4 participants