ui-eng-vision-local-lit-renderer
GitHub将遗留的命令式 DOM 构建迁移为局部的声明式 Lit-html 模板,保留原有类层级和辅助函数拓扑结构,优化渲染生命周期与导入规范。
Trigger Scenarios
Install
npx skills add ChromeDevTools/devtools-frontend --skill ui-eng-vision-local-lit-renderer -g -y
SKILL.md
Frontmatter
{
"name": "ui-eng-vision-local-lit-renderer",
"description": "Migrates legacy imperative DOM construction to local declarative Lit-html templates rendered inside existing containers.",
"allowed-tools": "code_search open_urls"
}
Subskill: Local Lit-HTML Renderer (Pass 2)
This subskill converts consolidated imperative DOM construction helper methods into reactive, declarative Lit-html templates, rendering them locally inside the existing element containers.
1. Declarative Technology Migration
-
Conserve Abstraction Topology:
- Maintain the existing class hierarchy, helper class boundaries, and logical groups.
- Preserve existing standalone/module-level DOM helper functions (e.g.,
linkifyIcon,maybeCreateLinkToElementsPanel) and class helper methods in their original locations, converting DOM-returning helpers in place to returnLitTemplate(orPromise<LitTemplate|null>) rather than deleting and inlining them intoperformUpdate()orDEFAULT_VIEW. - Do not aggressively "flatten" classes or collapse helper abstractions into the main view just to coerce a single master template. Pass 2 should only change the implementation details of rendering (imperative -> declarative) within existing compartments, not the public API or responsibilities of objects.
-
Determine Render Lifecycle per Layer:
- For UI.Widget classes: Define or update
performUpdate()to execute the main litrendercall, and usethis.requestUpdate()to queue updates. - For Delegate/Helper classes: Identify the custom update hook (e.g.,
update()orscheduleUpdate()) and invokerender()directly inside that method. Do not introduce Widget lifecycle methods (performUpdate) on classes that do not inherit them.
- For UI.Widget classes: Define or update
-
Import Modern Templating (Named Imports Only):
-
Always use named imports from
../../ui/lit/lit.js:import {html, nothing, render, Directives, type LitTemplate} from '../../ui/lit/lit.js'; -
NEVER use namespace imports like
import * as Litor referenceLit.nothing/Lit.render. Named imports enable better tree-shaking and maintain consistent codebase style.
-
-
Modern Component Mapping:
- MANDATORY: You MUST read and load ui_engineering.md first to understand specific component mappings (e.g., Toolbar vs
devtools-toolbar). Do not assume standard Lit defaults. - If unsure, also consult the automatic migration code
- Prefer Component Built-Ins Over Legacy Boilerplate: Before porting legacy event handlers, comparators, or state variables, check if the modern component handles them natively. Drop host-level workarounds in favor of declarative component attributes.
- MANDATORY: You MUST read and load ui_engineering.md first to understand specific component mappings (e.g., Toolbar vs
-
Local Modular Renders:
- If the class contains multiple separate legacy container fields (e.g.,
fields generated by
appendFieldorcreateChildon a parent layout), prefer local modular renders (rendering templates individually into each container) to preserve the legacy layout framework. Do not force a monolithic template if doing so violates the existing topology.
- If the class contains multiple separate legacy container fields (e.g.,
fields generated by
-
Resolve TypeScript Type Friction:
- Legacy DevTools elements may be typed as
Element. Lit'srender()function expectsHTMLElement | DocumentFragment. - When rendering into legacy containers, cast the container using
as HTMLElementor update its class property type definition fromElementtoHTMLElementto satisfy the TypeScript compiler. - Example:
render(this.template(), this.containerField as HTMLElement);
- Legacy DevTools elements may be typed as
-
Delegate to Unmigrated Rendering Engines:
- Do not attempt to rewrite complex historical subsystems (like Linkifiers or specialized UI utilities) to be "pure Lit" during this pass.
- Capitalize on Lit's ability to interpolate standard
HTMLElementorElementinstances directly. Treat unmigrated utilities as "black boxes" that generate DOM, and embed their output in standard template expressions:html${this.legacyElement}``. - CRITICAL: Before treating any component as "unmigrated," you MUST follow steps in Section 4. You are only allowed to use this escape hatch if no declarative migration instructions exist.
-
Handle Asynchronous DOM Updates:
- For asynchronous callback-driven updates (e.g., elements updated in a
.then()callback), render the parent container or a placeholder template first. Once the data is retrieved, execute a localrender()inside the callback targeting the specific sub-container.
- For asynchronous callback-driven updates (e.g., elements updated in a
-
Template Factorization Strategies:
- Prefer
nothing: Use Lit'snothingsentinel instead of empty strings''or empty templates for conditional rendering. - Parameterized Fragment Factories: For repeated UI patterns or
intermediate layout branches, extract them into parameterized helper
functions inside the class scope returning
LitTemplate. This avoids monoliths, preserves readability, and reuses template cache strategies.
- Prefer
-
Visual Parity, CSS Adaptation, and Accessibility:
- Zero-Tolerance Regression: Screenshot tests are the ground truth for this phase. Any visual diff (above 0%) in the generated screenshots is unacceptable and must be resolved before proceeding.
- CSS Selector Migration: When replacing legacy imperative components/widgets with modern web components (e.g., replacing legacy
DataGridwith<devtools-data-grid>), inspect and update the associated.cssfile. Replace legacy class selectors (.data-grid) with tag selectors (devtools-data-grid). - Strict Tag/Class Parity: Do not change tag types (e.g.,
spantodiv) or drop class names during template translation, as CSS may depend on them. - Explicit Component Variants: Modern Custom Element (e.g.
<devtools-button>) defaults may not match legacy appearance. Explicitly bind.data=${{variant: Buttons.Button.Variant.OUTLINED}}(or appropriate variant) rather than relying on defaults. - Accessibility Preservation: Convert custom accessibility
instrumentation (like
ARIAUtils.markAsAlert()) to native ARIA attributes in the template (e.g.,role="alert",aria-live="polite").
-
Render Templates Syntax & Code Movement:
- Surround the expressions containing lit template literals with
// clang-format offand// clang-format onto prevent clang-format from corrupting template indentation. - Minimize Code Movement: Avoid moving existing code to a different location in the file. Put the function signature around the existing code to minimize the diff.
- The Gerrit Code Review UI cannot identify moved blocks of unchanged code.
- If blocks of code need to be reordered, create a "prefactoring" change that first extracts the code that needs to be reordered into a named helper functions and pause the migration, waiting user confirmation.
- Surround the expressions containing lit template literals with
-
Embedding Pre-Existing
UI.WidgetInstances viaUI.Widget.WrapperWidget:- Use
${widget(UI.Widget.WrapperWidget, {widget: input.childWidget})}when embedding widgets owned byUI.ViewManager(TabbedViewLocation.tabbedPane(),StackLocation.widget()) or when conditionally moving a shared widget instance between containers ({widget: shouldShow ? this.#sharedWidget : null}). WrapperWidgetautomatically handles showing and detaching the inner widget and forwards calculateConstraints without requiring custom wrapper classes.
- Use
-
Declarative
<devtools-split-view>Guidelines:- CSS Sizing: Ensure
<devtools-split-view>hasflex: auto; height: 100%;in CSS so it fills available container space and doesn't collapse to 0x0. - Reuse Existing Settings for
showMode: When bindingshow=${...}declaratively on<devtools-split-view name="...">, reuse the existingSplitWidgetsetting ({vertical?: {size, showMode}, horizontal?: {size, showMode}}) forshowModerather than creating new boolean settings (which loses user preferences and requiresKnownContextValues.tsupdates). - Keep Environment Overrides in Templates: Keep environment overrides
(like
!input.isTraceApp) in theDEFAULT_VIEWtemplate (e.g.,show=${!input.isTraceApp && input.isSidebarOpen ? 'both' : 'main'}) rather than baking them into the presenter's setting state. - Clean Up Resizers: Do not leave unused
resizer-widgetclasses in templates;<devtools-split-view>manages its own built-in splitter unless custom elements are explicitly registered withinstallResizer.
- CSS Sizing: Ensure
-
Declarative Long-Press Action Buttons (
bindToAction):- Legacy
UI.Toolbar.Toolbar.createLongPressActionButton(action, [subButtons])maps declaratively to nesting option<devtools-button>elements directly inside<devtools-button ${bindToAction('action-id')}>...</devtools-button>.
- Legacy
-
Audit Removed
UIStrings:- If a
UIStringsentry becomes unused during template migration, verify that a conditional UI branch (such as a dynamically shown checkbox, button, or tooltip) wasn't accidentally dropped before deleting the string.
- If a
-
Micro-Commit Strategy:
- Do not attempt to convert every helper method and UI component in a single monolithic commit. Break the template extraction into small, self-contained micro-commits (e.g., migrating one helper method, toolbar button, or list item render at a time).
- Verify with build and run tests after each micro-commit to guarantee zero regressions and 100% visual parity before moving to the next compartment.
- Provide the exact list of files and commit message when presenting progress so the change history remains atomic and easy to review.
-
Wait for confirmation:
- Wait for an explicit confirmation from the user (or Parent Orchestrator Agent) before proceeding to the next step.
🔍 Mental Audit (Internal Self-Correction)
Before reporting back or committing, re-read the instructions and verify:
- ❓ Named Imports: Did I use named imports
{html, nothing, render}fromui/lit/lit.js(and noimport * as Lit)? - ❓ Tag Parity: Did I accidentally change any
spantodiv(or vice versa)? - ❓ Attribute Drop: Did I drop any
aria-*,role, or custom data attributes? - ❓ Clang-Off Boundary: Is the template wrapped in
// clang-format off? - ❓ Split Views: Does
<devtools-split-view>haveflex: auto; height: 100%;CSS and reuse existing split settings? - ❓ String Audit: Did I verify that any removed
UIStringsweren't from accidentally dropped UI branches? - ❓ Build: Does the code compile and do visual tests pass with 0% diff?
Version History
-
6527f72
Current 2026-10-20 02:26
新增保留辅助函数拓扑结构、命名导入规范及现代组件映射规则,明确禁止扁平化类抽象。
- 678d19c 2026-08-20 15:20


