Conversation
There was a problem hiding this comment.
Code Review
This is a substantial pull request that introduces the v0.9 architecture for the web renderers, successfully centralizing a significant amount of logic into the @a2ui/web_core package. This is a great architectural improvement that will enhance maintainability and consistency across different frameworks. The introduction of a demo application for the v0.9 Lit renderer is also a valuable addition.
My review focuses on some critical and high-severity issues in the new core implementation. These include a configuration change that reduces type safety across the project, a potential memory leak due to unmanaged subscriptions in the component context, and some issues related to accessibility and data model correctness. Addressing these will be crucial for the stability and robustness of the new v0.9 architecture.
|
|
||
| /* Linting */ | ||
| "strict": true, | ||
| "strict": false, |
There was a problem hiding this comment.
Disabling strict mode ("strict": false) is a significant regression in code quality and safety. It hides a wide range of potential bugs, null-pointer errors, and type mismatches that TypeScript is designed to catch. The project should aim to re-enable strict mode and fix the resulting type errors. This will improve long-term maintainability and reduce runtime errors. Many of the other issues found in this review (like the use of any or @ts-ignore) are symptoms of this underlying problem.
| // Subscribe to changes. When data changes, trigger a re-render. | ||
| this.dataContext.subscribe(value.path, () => this.updateCallback()); | ||
| return this.dataContext.getValue(value.path); |
There was a problem hiding this comment.
The resolve and resolveChildren methods subscribe to the dataContext to trigger updates, but there is no corresponding mechanism to unsubscribe. This will lead to memory leaks, as component contexts will never be garbage collected if they hold active subscriptions, and the updateCallback will be called unnecessarily for components that are no longer in the DOM.
A lifecycle method (e.g., dispose()) should be added to ComponentContext to clean up all subscriptions. This method would need to be called when a component is unmounted.
| for (const segment of segments) { | ||
| if (current[segment] === undefined || current[segment] === null) { | ||
| current[segment] = {}; | ||
| } |
There was a problem hiding this comment.
The set method's logic for creating intermediate paths always creates an object ({}). This doesn't correctly handle JSON Pointer paths that should create arrays. For example, a path like /items/0 on an empty model should ideally result in { "items": ["..."] }, but this implementation will create { "items": { "0": "..." } }. This can lead to incorrect data structures and runtime errors when code expects an array but gets an object.
The implementation should be updated to check if a path segment is a valid array index and create an array instead of an object when appropriate.
| render() { | ||
| return html` | ||
| <div @click="${() => this.isOpen = true}" style="display: inline-block;"> | ||
| ${this.trigger} | ||
| </div> | ||
| ${this.isOpen ? html` | ||
| <div class="modal-overlay" style="position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center;"> | ||
| <div class="modal-content" style="background: white; padding: 20px; border-radius: 8px; min-width: 300px;"> | ||
| ${this.content} | ||
| <div style="margin-top: 20px; text-align: right;"> | ||
| <button @click="${() => this.isOpen = false}">Close</button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ` : nothing} | ||
| `; | ||
| } |
There was a problem hiding this comment.
The A2UiModalWrapper component has several accessibility and maintainability issues:
- Accessibility: The modal is triggered by a click on a
<div>, which is not keyboard-accessible. This should be a<button>or haverole="button"andtabindex="0"to be focusable and activatable with Enter/Space. Additionally, focus is not trapped within the modal when it's open, and it cannot be closed with the Escape key. - Maintainability: All styles are applied inline. This makes them hard to override, theme, and maintain. They should be moved to a
static stylesblock within the component.
| @@ -0,0 +1,18 @@ | |||
| import { resolve } from 'path'; | |||
| // @ts-ignore - indexing might be flagged if variant is not strictly typed to keys | ||
| const variantClasses = theme[targetVariant] || {}; |
There was a problem hiding this comment.
The use of @ts-ignore here hides a potential typing issue. The variant property on TextRenderProps is a string, but it's being used to index theme.components.Text, which has a specific set of keys (h1, h2, body, etc.).
To fix this and remove the need for @ts-ignore, the variant type should be constrained to the actual keys of the theme's text component styles. This is a good example of an issue that would have been caught if strict mode were enabled.
| // @ts-ignore - indexing might be flagged if variant is not strictly typed to keys | |
| const variantClasses = theme[targetVariant] || {}; | |
| const variantClasses = theme[targetVariant as keyof typeof theme] || {}; |
No description provided.