Skip to content

Epic 5 UI - #170

Open
jgindin wants to merge 15 commits into
mainfrom
epic-5-ui
Open

Epic 5 UI#170
jgindin wants to merge 15 commits into
mainfrom
epic-5-ui

Conversation

@jgindin

@jgindin jgindin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

Fifth and final in a series of refactorings to clean up the A2UI Composer code.

UI Shell Encapsulation, CDK Share Service & Telemetry Directives

  • Introduces the declarative TrackEventDirective ([a2uiTrackEvent]) using modern Angular signal-based input() bindings.
  • Extracts ShareService backed by @angular/cdk/clipboard for robust clipboard operations.
  • Extracts FileIngestionService to decouple browser FileReader operations from UI components (ChatPanel).
  • Strips monolithic clipboard logic and normalizes component inputs and template bindings.

Pre-launch Checklist

  • I signed the [CLA].
  • I read the [Contributors Guide].
  • I read the [Style Guide].
  • I have added updates to the [CHANGELOG].
  • I updated/added relevant documentation.
  • My code changes (if any) have tests.
  • If my branch is on fork, I have verified that scripts/e2e_test.sh passes.

jgindin added 15 commits August 18, 2026 12:17
Create SafeUrlValidatorService for http/https validation.
Create DomainOriginVerificationService for strict iframe networking validation.
Wire bypassSecurityTrustResourceUrl inside rendered-frame.ts through the rigorous URL validator.
Swap the check in preview-bridge.ts for the strict origin verification.
Fix LLM concurrency in ChatCoordinator submitPrompt.
Fix Monaco model.dispose() in a2ui-composer-monaco-editor.
Inject takeUntilDestroyed in api-key-selector.ts and renderer-selector.ts.
Delete debug/mock-rules/ entirely.
Rip out console.error patch hacks from ChatCoordinator.
Delete obsolete storage key ACTIVE_DRAFT.
…ks into decoupled targets accurately abandoning monolithic arrays globally
…mentations and un-deferred webrtc layout code gracefully
…ngestion

- Implement TrackEventDirective applying declarative tracking boundaries natively.
- Construct FileIngestionService strictly leveraging @angular/cdk/clipboard utilities.
- Construct ShareService cleanly encapsulating clipboard interactions.
- Sweep all active standalone UI components throughout the app, converting default ChangeDetection behavior to ChangeDetectionStrategy.OnPush.
- Strip FileReader native accesses from ChatPanel into the FileIngestionService.
- Swap template tracking to the directive.
- Rip out the legacy shareDesign() bloated component implementation comprehensively.
- Delete deprecated inner test asserts and old inline code.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the A2UI Composer shell by modularizing several core features, including extracting payload parsing and syntax healing into a2ui-payload-parser, error formatting into ChatErrorFormatterService, file ingestion into FileIngestionService, and sharing capabilities into ShareService. It also introduces a base AbstractSelector for settings selectors, delegates startup state to StartupConfigStateService, and adds services for safe URL validation and screenshot capture. Feedback on these changes highlights several critical regressions and improvements: same-window messages should be allowed in preview-bridge.ts to avoid runtime regressions; the single JSON object fallback in a2ui-payload-parser.ts must be removed to comply with repository rules, while a nullish input guard should be added and the missing rules/mock* property filtering restored; the earlyMessageBuffer cleanup in host-communication.ts should be preserved to prevent memory leaks; and the gallery component should use @angular/cdk/clipboard for robust clipboard operations.

Comment on lines +350 to +353
if (
!DomainOriginVerificationService.verifyStrictOrigin(event.origin, event.source, window.parent)
)
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The new verifyStrictOrigin check only validates messages originating from window.parent. However, the original implementation explicitly allowed self-messages where event.source === window. Blocking these self-messages can cause runtime regressions if the preview bridge or its tests rely on same-window postMessage communication. Please preserve the original behavior by allowing same-window messages directly.

Suggested change
if (
!DomainOriginVerificationService.verifyStrictOrigin(event.origin, event.source, window.parent)
)
return;
if (
event.source !== window &&
!DomainOriginVerificationService.verifyStrictOrigin(event.origin, event.source, window.parent)
)
return;

Comment thread shell/src/app/chat/a2ui-payload-parser/a2ui-payload-parser.ts
Comment thread shell/src/app/chat/a2ui-payload-parser/a2ui-payload-parser.ts
Comment on lines +31 to +32
export function parseAndHealJsonLines(content: string): ParseResult {
let wasHealed = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To comply with the repository's General Rule on hardening string processing utility methods, please add a guard clause to safely handle nullish or undefined inputs by returning a safe default.

Suggested change
export function parseAndHealJsonLines(content: string): ParseResult {
let wasHealed = false;
export function parseAndHealJsonLines(content: string | null | undefined): ParseResult {
if (!content) {
return {blocks: [], wasHealed: false};
}
let wasHealed = false;
References
  1. Harden string processing utility methods with guard clauses to safely handle nullish or undefined inputs by returning safe defaults, and ensure corresponding unit tests are added to verify resilience against nullish values.

Comment on lines 208 to 212
if (!target) {
this.iframeElement = null;
this.iframeWindow = null;
this.earlyMessageBuffer.length = 0;
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

When unregistering the iframe (i.e., when target is nullish), the original implementation cleared the earlyMessageBuffer by setting its length to 0. Removing this cleanup can lead to memory leaks or stale messages being replayed when a new iframe is registered later. Please restore this cleanup step.

Suggested change
if (!target) {
this.iframeElement = null;
this.iframeWindow = null;
this.earlyMessageBuffer.length = 0;
return;
}
if (!target) {
this.iframeElement = null;
this.iframeWindow = null;
this.earlyMessageBuffer.length = 0;
return;
}

Comment on lines +274 to +276
navigator.clipboard.writeText(payload).catch(err => {
console.error('Failed to copy A2UI component usage to clipboard: ', err);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To ensure robust clipboard operations across all environments (including non-secure contexts or older browsers where navigator.clipboard is unavailable), please use @angular/cdk/clipboard's Clipboard service instead of navigator.clipboard.writeText directly. This aligns with the ShareService implementation introduced in this PR.

Note: You will need to import Clipboard from @angular/cdk/clipboard and inject it at the class level:

private readonly clipboard = inject(Clipboard);

      this.clipboard.copy(payload);

@github-actions

Copy link
Copy Markdown
Contributor

⚡ A2UI Composer PR Preview

Your automated preview is successfully live (commit f89784f):
👉 Launch PR Preview

Note: This environment will be wiped automatically when the PR is merged or closed.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant