CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/tamboui

Teaches coding agents how to build TUIs with TamboUI correctly: API-level selection, render-thread discipline, display-width safety, CSS-aware element authoring, and JFR conventions.

94

2.77x
Quality

93%

Does it follow best practices?

Impact

97%

2.77x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdskills/wrap-widget-as-element/

name:
wrap-widget-as-element
description:
Add a Toolkit `Element` wrapping an existing TamboUI widget so it gains CSS support, styled sub-components, focus integration, and a Toolkit factory method. Use when the user says "wrap a widget", "add an element for X widget", "make widget Y CSS-aware", "expose widget Z in the DSL", or asks to integrate a custom widget into the Toolkit.

Wrap a Widget as a Toolkit Element

Process steps in order, do not skip ahead. The end state is an Element class that goes through resolveEffectiveStyle for every sub-component, with a Toolkit factory method, JavaDoc-documented CSS selectors, and tests that assert against BufferAssertions.

Step 1 — Identify the Widget and Its Styled Sub-Components

  • Locate the widget under tamboui-widgets/src/main/java/dev/tamboui/widgets/{area}/ and read its render method to enumerate every visually distinct piece (e.g., for a Gauge: filled bar, label; for a List: items, scrollbar thumb/track)
  • Each sub-component becomes one CSS child selector; pick a short stable name (e.g., filled, unfilled, cursor, placeholder, item, header)
  • Run scripts/list-tamboui-modules.sh to confirm the toolkit module path, then list tamboui-toolkit/src/main/java/dev/tamboui/toolkit/element/ for any existing {Name}Element.java — if one exists, stop and tell the user; the right move is to extend the existing Element, not write a duplicate

Step 2 — Create the Element Class

  • Create tamboui-toolkit/src/main/java/dev/tamboui/toolkit/element/<Name>Element.java extending StyledElement (or another existing element base if the widget is stateful)
  • Constructor takes the widget's required inputs as parameters; optional configuration goes through fluent setters that return this
  • Field declarations come first, then constructor, then setters, then renderContent last — match the existing element files

Step 3 — Declare Nullable Style Fields and Defaults

  • For each sub-component identified in Step 1, add private Style <name>Style; (nullable, no initializer) and private static final Style DEFAULT_<NAME>_STYLE = ...;
  • Defaults should be minimal: Style.EMPTY plus a single modifier like .reversed() or .dim() — they compose with whatever the user adds
  • Do not initialize the field to Style.EMPTY — that breaks CSS resolution; see rules/css-element-style-resolution.md
  • Add a fluent setter for each: public <Name>Element <name>Style(Style s) { this.<name>Style = s; return this; }

Step 4 — Implement renderContent Using resolveEffectiveStyle

  • Inside renderContent(RenderContext context, Rect area, Buffer buffer), resolve every sub-component style via resolveEffectiveStyle(context, "<name>", <name>Style, DEFAULT_<NAME>_STYLE) (or the pseudo-class overload for selected/focused state)
  • Then either render directly to the buffer using the resolved styles, or instantiate the widget, configure it with the resolved styles, and call widget.render(area, buffer) — pick whichever is less duplication
  • Honor RenderThread.checkRenderThread() if the element holds mutable state — StyledElement already does this, but custom render helpers may not

Step 5 — Add the Toolkit Factory Method

  • Open dev/tamboui/toolkit/Toolkit.java and add a public static <Name>Element <name>(...) method that constructs and returns the element
  • The method name is the lower-camel-case widget name (e.g., gauge, barChart, lineGauge); match the table in the project README
  • Place the new method alphabetically among existing factories; do not reorder unrelated entries

Step 6 — Document the CSS Selectors

  • In the Element class JavaDoc, add a <h2>CSS Child Selectors</h2> block listing each <ElementName>-<name> selector and its default
  • Update the "Available CSS Child Selectors" table in AGENTS.md with one new row
  • Add a brief widget entry to docs/src/docs/asciidoc/widgets.adoc if the widget itself is new; if the widget already exists, mention only the new Element wrapper

Step 7 — Write Tests Using BufferAssertions

  • Create tamboui-toolkit/src/test/java/dev/tamboui/toolkit/element/<Name>ElementTest.java using JUnit 5
  • At minimum: render at a fixed Rect, assert the buffer contents with BufferAssertions, then assert that an explicit style overrides the default and (if you have a TCSS test fixture) that a CSS rule overrides the default but is overridden by an explicit setter
  • Test data is fixed — never randomize; see testing-standards.md from the user's coding-policy tile

Step 8 — Verify Locally

  • Run ./gradlew -q :tamboui-toolkit:test and then ./gradlew -q javadoc — both must pass
  • If the new element introduces a public type, also run ./gradlew -q assemble to confirm Java 8 source compatibility
  • Run scripts/check-display-width.sh (it emits JSON candidates) and triage any new entries against rules/char-width-for-display.md — false positives are expected, but a real violation in your render path means a glyph-width bug
  • If any check fails, stop and surface the failure — do not paper over it by widening exception handlers or reducing assertions
  • Finish here.

skills

wrap-widget-as-element

README.md

tile.json