- name
- eui-neo-ui-replicator
- description
- Design or replicate high-quality desktop interfaces in the EUI-NEO C++ DSL. Use when Codex needs to recreate screenshots, mockups, web pages, product UI, or create an original competition-grade desktop UI/UX concept from user requirements and reference research, prioritizing Row/Column/Stack/Flow layout, built-in components from components/, theme tokens, short non-looping motion, Bing image placeholders when media is unavailable, and only falling back to Rect/Text/Image/SVG/Polygon primitives or new lightweight components when no built-in component can express the target.
# EUI-NEO UI Designer And Replicator
## Goal
Recreate a target interface or design an original desktop UI as idiomatic EUI-NEO code. Prefer stable layout and built-in components over pixel-by-pixel drawing. Use primitive DSL layers only for visual details, custom shapes, or new components that the framework does not already provide.
When the user provides only a product idea, vague requirements, or a desired style, act as a UI/UX designer first: extract requirements, gather or infer high-quality desktop UI references, analyze their reusable techniques, define a design direction, and then implement it in EUI-NEO.
## First Pass
Before writing code, inspect these local sources when available:
- `README.md` or `README.zh-CN.md` for app setup and public entry points.
- `site/llms.txt` for the current public API, removed input APIs, build options, and documentation index.
- `docs/DSL.md` for DSL element capabilities.
- `docs/*.md` with searches for `Row`, `Column`, `Stack`, `Flow`, `SizeValue`, `ignoreLayout`, and component names for layout and component behavior.
- `apps/gallery/app.cpp` and `apps/gallery/pages/*.h` only as references for idiomatic multi-page composition.
- `components/components.h` for the exported component list.
- `components/workshop/SKILL.md` when porting an effect-heavy or CSS-like custom component.
Create runnable user apps under `apps/`, not under `examples/`, unless the user explicitly asks to modify the built-in gallery or examples. Prefer a directory app such as `apps/my_app/app.cpp` when the UI may grow, has multiple pages, uses app-specific custom components, or needs assets. Put distinct pages in `apps/my_app/pages/`, app-specific custom or primitive-built components in `apps/my_app/components/`, and app-local media in `apps/my_app/assets/`. A single-file `apps/my_app.cpp` is fine only for a very small single-view experiment with no app-specific component modules.
Before creating code, choose and state the app target name. The target name comes from the flat file stem (`apps/my_app.cpp` -> `my_app`) or directory name (`apps/my_app/app.cpp` -> `my_app`). Build that exact target for verification.
Create a short implementation inventory:
```text
Mode: replicate | original design | hybrid
Design brief:
Reference set:
Reference techniques:
Target areas:
Layout containers:
Reusable built-in components:
Custom primitive pieces:
State and interactions:
Motion plan:
Assets or SVG needed:
Verification target:
```
## Requirement Discovery
If the target UI is not fully specified, collect enough context to design well:
- Product purpose and primary user.
- Core desktop workflows and the most frequent actions.
- Information hierarchy, data density, and navigation model.
- Required pages, panels, overlays, empty states, and error states.
- Brand mood, visual constraints, and accessibility needs.
- Media needs: photos, illustrations, logos, thumbnails, charts, maps, or avatars.
Ask concise follow-up questions only when missing information would materially change the interface. Otherwise make explicit assumptions and continue. For a competition-grade concept, favor a focused, memorable workflow over a generic dashboard with many unrelated cards.
## Reference Research
When designing an original UI or when the user asks for top-tier, modern, award-level, or current references, research current desktop UI/UX examples before designing if internet access is available. Collect a small reference set from credible product pages, design award galleries, platform design systems, or high-quality case studies. Cite sources in the work summary when external research is used.
Analyze references for transferable techniques rather than copying them:
- Layout rhythm: shell, sidebar, toolbar, canvas, inspector, split panes, density.
- Visual hierarchy: contrast, typography, accent use, spacing, grouping.
- Interaction model: command placement, progressive disclosure, shortcuts, drag/drop, hover affordances.
- Motion: page transitions, panel entrance, control feedback, selection changes.
- Data presentation: tables, charts, cards, timelines, previews, and comparisons.
- Desktop polish: resizable regions, persistent navigation, keyboard/focus states, status feedback.
Convert the analysis into a design direction:
```text
Design thesis:
Primary workflow:
Navigation model:
Surface system:
Component system:
Motion language:
Media strategy:
```
## Decision Order
Use this order for every target region:
1. Use layout containers for structure: `ui.row`, `ui.column`, `ui.stack`, `ui.flow`.
2. Use built-in components for familiar controls and data display.
3. Use themed wrappers for simple surfaces: `components::card`, `components::panel`, `components::text`, `components::image`.
4. Use primitives for missing visual details: `ui.rect`, `ui.text`, `ui.image`, `ui.svg`, `ui.polygon`.
5. Create a component only when the same custom piece repeats, has interaction, or would make the page hard to read.
6. Put generic reusable controls in `components/`; put creative, brand-like, or effect-heavy ports in `components/workshop/`; keep one-off page decorations as local helper functions.
Treat motion as part of the replica. Built-in components already include interaction motion, so keep and configure their `.transition(...)` rather than reimplementing it. Add short transitions to page switches and to custom components made from primitives. Do not use long-running, looping, pulsing, marquee, spinner-like, or decorative infinite animations.
Do not bypass the DSL runtime or hold backend primitives directly. Do not read GLFW/SDL state directly. Keep rendering, events, animation, hit testing, dirty rects, and state lifecycle inside the EUI runtime.
## Layout Rules
Use `Row` for horizontal groups, toolbars, two-column shells, button rows, and chart rows.
Use `Column` for forms, stacked sections, sidebars, property lists, and vertical content.
Use `Stack` for overlays, absolute local coordinates, backgrounds behind content, badges, decorative layers, and modal surfaces.
Use `Flow` for chips, tags, filter buttons, compact action groups, and any horizontal content that should wrap.
Use `components::scrollView` for scrollable measured content. Use `components::virtualList` for very large fixed-height lists and `components::virtualMasonry` for large variable-height grids where composing every item would be wasteful. Use low-level `components::scroll` only when manually binding a runtime scroll state is necessary.
`scrollView` creates and measures its own wrap-content root. Make every child report its real height, and do not add a viewport-height wrapper around variable content unless that child is intentionally fixed-height; otherwise the measured scroll range will be wrong.
## Performance Rules For Interactive Data UI
Classify a performance problem before changing code: distinguish page compose/layout cost, Runtime tree traversal cost, primitive submission cost, and backend/GPU cost. Use the Runtime render statistics (`Dirty`, `Draw`, and `Pass`) to identify whether the active cost is CPU traversal, excessive primitive count, or GPU repaint area. Do not assume low GPU utilization means a page is cheap: a large DSL tree can still consume CPU during input and state updates.
`clip()` only limits visible pixels. It does not avoid composing, laying out, updating, or submitting its offscreen children. For a long fixed-height list, timeline, log, signal browser, or waveform table, use `components::virtualList`; it composes only the viewport plus overscan slots. Use `scrollView` only when the whole measured content is small enough to compose cheaply.
The built-in `virtualList` should move its visible window through the Runtime scroll transform and request a compose only when the overscanned slot range changes. Recomputing every row position and rebuilding the complete page for every scroll pixel defeats virtualization.
Do not implement a manual virtual list by retaining all rows in `scrollView`, copying its offset into page state, and calling `requestUpdate()` from every scroll callback. That turns wheel and thumb motion into full-page compose work. `virtualList` owns the required Runtime-to-compose handoff and reuses visible slots. Keep row business state keyed by the real item index or data key, not by the reusable slot id.
For dense charts, traces, waveforms, and timelines, set a visual budget from the available pixel width. Downsample or aggregate changes that would become sub-pixel segments, and keep retained primitive count bounded per visible row. Do not build a complete waveform as one self-intersecting polygon: it is not a valid general line representation, and current OpenGL/Vulkan polygon paths cap an individual polygon at 128 edges. Draw independent convex segments or use the existing line-chart step-segment technique instead.
When building custom controls, make input math and render math use the same geometry. A slider whose knob renders over `width - knobSize` pixels must map pointer input over that same travel range, with the pointer aligned to the knob center. Do not draw a page-local track behind `components::slider` unless it intentionally replaces the component track; duplicate tracks with different vertical alignment look like hover jitter or a jumping control.
For high-frequency interactions, prefer Runtime bindings and state (`slider`, `scrollView`, `virtualList`, hover/pressed states, pointer transform bindings). Request a page compose only when business data or the visible declarative structure must change; do not request it merely to animate a control that Runtime already owns.
### Resource lifetime and memory release
Treat retained GPU layers and per-primitive caches as resources with an explicit frame lifetime:
- `Runtime::update()` may mark retained layers, paint bounds, and primitive instances as unseen, but must not destroy retained GPU layers while the current frame may still render them. Pruning and `destroyLayer(...)` belong after the render/cache-blit path has completed, including early-return render paths.
- A retained layer is owned by the active render backend. Release it only through the backend that created it, and clear its handle/valid flag immediately after destruction. Never release a layer merely because a declarative subtree was recomposed; release only entries that were confirmed unseen after the frame.
- Do not solve memory growth by disabling retained-layer caching or by clearing every cache on every scroll/input event. Preserve stable element ids and let the runtime reuse unchanged layers; release only stale resources after a completed frame.
- When a virtualized `TextPrimitive` receives new content, clear content-dependent glyph/line/vertex data before rebuilding it. Keep small reusable capacity, but shrink oversized capacity when it was created by an unusually long label or waveform value. Shared font atlas storage is independent and must not be recreated for each slot update.
- Bound retained primitive count and text size before composition. `clear()` alone does not necessarily return a container's capacity; use an explicit shrink/swap only when the retained capacity is materially larger than the next workload, otherwise repeated scrolling can keep peak memory permanently attached to reusable slots.
- Any resource-lifetime change must be checked on both normal and early-return render paths, plus shutdown/backend teardown. Verify that memory stabilizes after repeated virtual-list scroll/zoom cycles and that CPU/GPU work returns to idle when input stops.
Before declaring a performance fix complete, validate all of these:
- Static page: CPU and GPU return to idle after input and transitions stop.
- Scroll: visible slots remain bounded; CPU/GPU work does not scale with total item count.
- Drag or slider: pointer-to-visual mapping is stable at both endpoints and does not trigger full-page compose per move.
- Dense primitive view: changes remain readable at the current zoom while primitive count stays within the per-row budget.
Keep one logical wrapping grid in one `Flow`. Do not split one continuous card grid into multiple sibling flows, because each flow wraps independently and will restart from a new line.
Treat `Flow` as wrapping, not as a full responsive layout engine. Use explicit thresholds when structure changes: keep two-column `Row` layouts while both children fit, switch to single-column `Column` only when the width is truly too small.
Avoid putting content with variable visible height inside a fixed-height parent just to group it. If a table, picker, markdown block, wrapped flow, or stacked section can grow, let it participate directly in the parent column or reserve enough measured height; otherwise later content can visually overlap.
Prefer `.padding(...)` on containers over extra spacer containers. Prefer `.gap(...)` for repeated child spacing. Use `.fill()`, `SizeValue::wrapContent()`, `flexGrow`, `minWidth`, and `maxWidth` to handle responsive resizing instead of hard-coded screen branches everywhere.
Use `.ignoreLayout()` only for decorative or debug overlays that should not affect measurement. Use `.zIndex(...)` only for drawing and hit-test order; it does not remove an element from layout.
For responsive pages, branch at clear layout thresholds and keep both branches structurally simple:
```cpp
const bool twoColumns = width >= 820.0f;
if (twoColumns) {
ui.row("page.grid").size(width, height).gap(18.0f).content([&] {
ui.column("page.grid.left").size(leftWidth, leftHeight).gap(18.0f).content([&] {
// sections
}).build();
ui.column("page.grid.right").size(rightWidth, rightHeight).gap(18.0f).content([&] {
// sections
}).build();
}).build();
} else {
ui.column("page.grid.single").size(width, singleHeight).gap(18.0f).content([&] {
// same sections in single-column order
}).build();
}
```
## Component Selection
Use these built-ins first:
- Commands: `components::button`
- Boolean choices: `components::checkbox`, `components::toggleSwitch`
- Exclusive choices: `components::radio`, `components::segmented`, `components::tabs`
- Numeric input: `components::slider`, `components::stepper`, `components::progress`
- Text entry: `components::input`
- Menus and pickers: `components::dropdown`, `components::datePicker`, `components::timePicker`, `components::colorPicker`, `components::contextMenu`
- Navigation: `components::navbar`, `components::sidebar`
- Overlays and feedback: `components::dialog`, `components::toast`, `components::tooltip`
- Data and media: `components::dataTable`, `components::carousel`, `components::lineChart`, `components::barChart`, `components::pieChart`, `components::markdown`
- Scrolling and large collections: `components::scrollView`, `components::virtualList`, `components::virtualMasonry`
- Hit regions: `components::mouseArea`
- Surfaces and typography: `components::card`, `components::panel`, `components::text`, `components::image`
For controlled components, keep business state in the page or owning model. Pass current values into the component and update them through `onChange`, `onOpenChange`, or `onDismiss`. When a builder supports signals, call the builder method `.bind(signal)`, `.bindOpen(signal)`, or `.bindVisible(signal)`; `eui::Signal<T>` itself has no `bind` method.
Place global overlays near the end of root composition so they sit above normal content. Call `.screen(screen.width, screen.height)` on dialogs, date/time/color pickers, toasts, and context menus so they can constrain overlays to the window.
## Current Input Contract
Use the v0.5.9 event model:
- Key callbacks receive `eui::KeyEvent` with `key`, `action`, `modifiers`, and `scanCode`.
- Text and IME callbacks receive `eui::TextInputEvent`; never combine text input with key events.
- Element `.onKeyEvent(...)` callbacks return `bool` to report whether they handled the event.
- Pointer callbacks use `action`, `button`, `buttons`, and `modifiers`.
- Middle, right, X1, and X2 interactions must be enabled explicitly with `.acceptedButtons(...)`; left is the default.
Never generate removed `KeyboardEvent`, `PointerEvent::down`, `rightDown`, `pressedThisFrame`, `releasedThisFrame`, other `*ThisFrame` fields, compatibility aliases, native button polling, or dual input paths. Do not disable retained-layer caching to work around rendering or resize behavior.
## Primitive Translation
Translate visual details into DSL primitives:
- CSS block, panel, card, track, chip, or pill -> `ui.rect(...)`
- Text label, icon font glyph, heading, metric -> `ui.text(...)`
- Photos, PNG/JPG assets, SVG files -> `ui.image(...)`
- Exact inline SVG icon or logo -> `ui.svg(...).source(svg).tint(color).contain()`
- Triangles, pointers, sectors, custom polygonal marks -> `ui.polygon(...)`
- Pseudo-elements like `::before` and `::after` -> extra stable DSL layers
- Box shadow -> `.shadow(...)`
- Inset shadow -> `.insetShadow(...)`
- Multiple shadows -> multiple always-present rect layers with stable ids
- Border radius -> `.radius(...)`
- CSS transform -> `.translate(...)`, `.scale(...)`, `.rotate(...)`, `.rotateX(...)`, `.rotateY(...)`, `.perspective(...)`
If an exact SVG path matters, inline SVG through `ui.svg` instead of approximating with polygons. If the shape is simple and themable, prefer primitives so the result stays native and animatable.
Keep internal ids stable. Use caller ids for root components and `id + ".part"` for child layers. Avoid creating and removing layers for hover/pressed visual changes; keep layers present and animate color, opacity, or transform.
## Media And Placeholder Rules
Use real user-provided assets first. If the user references a product, person, venue, object, or brand and exact imagery is important, search for or request the correct asset instead of inventing it.
If a media slot needs an image and no asset or URL is available, use Bing daily image as the first placeholder:
```cpp
ui.image("hero.placeholder")
.size(width, height)
.bingDaily(0, "zh-CN")
.cover()
.radius(18.0f)
.transition(pageTransition())
.build();
```
Use `components::image(...)` when theme defaults are useful; use `ui.image(...)` when the placeholder needs exact primitive control. Do not leave major media frames blank unless the design intentionally uses an empty-state treatment. Keep placeholder ids clear, such as `.placeholder`, so they are easy to replace with final assets.
## Visual Fidelity
Extract the target interface into tokens before coding:
```text
Canvas/background:
Surfaces:
Primary/accent:
Text primary/muted:
Borders:
Shadow:
Radii:
Spacing scale:
Typography:
```
Prefer `components::theme::dark()`, `components::theme::light()`, `pageVisuals(tokens)`, `fieldVisuals(tokens)`, and helpers such as `withAlpha`, `withOpacity`, `buttonHover`, `buttonPressed`, `border`, and `shadow`. Override colors directly only when matching a specific source design.
Use Font Awesome icon codepoints through `.icon(...)` on `components::button` or `ui.text(...).icon(...)` when an icon glyph exists. Use inline SVG for exact brand or path icons.
Keep text boxes large enough for the intended strings. Set `fontSize`, `lineHeight`, `horizontalAlign`, and `verticalAlign` explicitly for polished compact controls.
## App File Placement
For user-facing generated apps, use a directory whenever the app has multiple pages, app-specific custom components, or assets:
```text
apps/
my_app/
app.cpp
pages/ # required when the app has distinct pages or routed views
components/ # required for app-specific custom or primitive-built components
assets/ # optional app-local assets
```
For a multi-page app, keep `app.cpp` limited to application configuration, the persistent shell, navigation, global overlays, and page dispatch. Put each distinct page in its own header under `pages/`; a small `pages/page_context.h` may own shared page state, models, theme tokens, and navigation actions, but must not become a container for rendered components.
عرض على GitHub