From everything-evenhub
Measures pixel-accurate single-line widths, multi-line wrapped layouts, and truncates text for Even Realities G2 glasses, matching LVGL rendering. Use for precise text container sizing.
npx claudepluginhub even-realities/everything-evenhub --plugin everything-evenhubThis skill is limited to using the following tools:
Pixel-accurate text measurement for Even Realities G2 smart glasses. Predicts exact layout dimensions matching the LVGL rendering engine in the glasses firmware.
Measures multiline text heights, line counts, and layouts without DOM reflows in JavaScript/TypeScript using canvas font metrics. Use for dynamic textareas, canvas rendering, and responsive UIs.
Builds UI for Even Hub G2 glasses displays using text containers, lists, images, page lifecycle, and layout patterns on 576x288 greyscale canvas. For creating or updating glasses app content.
Generates pixel-precise ASCII TUI Text components with TUI_RENDER, COMPONENT_SPEC, PENCIL_SPEC, and PENCIL_BATCH_DESIGN outputs for Pencil MCP drawing workflows. Use for terminal UI or text-based interface text creation.
Share bugs, ideas, or general feedback.
Pixel-accurate text measurement for Even Realities G2 smart glasses. Predicts exact layout dimensions matching the LVGL rendering engine in the glasses firmware.
Note: Results may be off for characters not present in the firmware fonts, since the firmware and this library may handle missing glyphs differently in some cases.
npm install @evenrealities/pretext
All functions are exported from @evenrealities/pretext.
getTextWidth(text: string): numberReturns single-line pixel width of a string (with kerning, no wrapping).
import { getTextWidth } from '@evenrealities/pretext';
const width = getTextWidth('Hello, world!'); // => 79
measureTextWrap(text: string, maxWidth: number): MeasureTextResultMeasures multi-line text layout with word wrapping. maxWidth should be the inner content width — subtract padding and border before calling. The returned height is pure text height.
import { measureTextWrap } from '@evenrealities/pretext';
const result = measureTextWrap('The quick brown fox jumps over the lazy dog', 200);
// => { lineCount: 2, height: 54, lineWidths: [192, 96] }
// With padding + border — caller handles the math:
const pad = 8, border = 2;
const m = measureTextWrap('Hello world', 300 - 2 * (pad + border));
const containerHeight = m.height + 2 * (pad + border);
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
text | string | — | The string to measure |
maxWidth | number | — | Available width for text in pixels (subtract padding/border) |
Returns:
| Field | Type | Description |
|---|---|---|
lineCount | number | Number of lines after wrapping |
height | number | Total text height (lineCount * 27) |
lineWidths | number[] | Pixel width of each wrapped line |
pxTruncate(text: string, maxPx: number): stringTruncates a string to fit within a pixel budget, appending '...' if needed. Returns the original string unchanged when it already fits. Uses binary search and handles emoji/surrogate pairs correctly.
import { pxTruncate } from '@evenrealities/pretext';
const label = pxTruncate('Hello, world!', 50); // => 'Hell...'
const fits = pxTruncate('Hi', 50); // => 'Hi'
Parameters:
| Parameter | Type | Description |
|---|---|---|
text | string | The string to truncate |
maxPx | number | Maximum width in pixels |
Returns: The original string if it fits, otherwise a truncated string ending with '...'.
Note: This function is single-line only. For multiline text, truncate each line individually.
getAdvW(cp: number): numberReturns raw advance width of a codepoint in 1/16px units (no kerning, no rounding). Useful for debugging or custom measurement logic.
When a TextContainerProperty has paddingLength or borderWidth, the SDK's LVGL renderer subtracts these from the available text area inside the container. If you measure text against the full container width/height, the content will overflow and a scrollbar appears.
Container (width × height)
┌─ border (borderWidth pixels) ─────────────────────┐
│ ┌─ padding (paddingLength pixels) ──────────────┐ │
│ │ │ │
│ │ Text renders here │ │
│ │ innerWidth = width - 2*padding - 2*border │ │
│ │ innerHeight = height - 2*padding - 2*border │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────┘
paddingLength: N reduces text area by N pixels on all four sidesborderWidth: N reduces text area by N pixel on all four sides (border is drawn inside the container)paddingLength + borderWidth per sideimport { measureTextWrap } from '@evenrealities/pretext';
const containerW = 560;
const containerH = 258;
const padding = 8; // paddingLength
const border = 1; // borderWidth
const inset = padding + border;
const innerW = containerW - 2 * inset;
const innerH = containerH - 2 * inset;
const maxLines = Math.floor(innerH / 27);
// Measure and truncate against the INNER dimensions
const m = measureTextWrap(text, innerW);
if (m.lineCount > maxLines) {
text = truncateToFitLines(text, innerW, maxLines);
}
// WRONG — measures against container width, ignores padding/border
const m = measureTextWrap(text, 560);
// RIGHT — subtracts padding and border from both sides
const m = measureTextWrap(text, 560 - 2 * (padding + border));
import { measureTextWrap } from '@evenrealities/pretext';
const containerWidth = 300;
const padding = 8;
const innerW = containerWidth - 2 * padding;
const result = measureTextWrap(myText, innerW);
const containerHeight = result.height + 2 * padding;
// Use Math.max(...result.lineWidths) + 2 * padding for tight container width
import { pxTruncate } from '@evenrealities/pretext';
// Single-line truncation
const label = pxTruncate(longText, containerWidth);
// Multiline: truncate each line individually
import { measureTextWrap } from '@evenrealities/pretext';
const { lineWidths } = measureTextWrap(longText, containerWidth);
// If the last line is too long after wrapping, truncate it
When using this library to size UI containers for Even Realities glasses:
2 * padding for list container height.measureTextWrap results to set container width and height properties precisely.paddingLength or borderWidth, subtract these from the container dimensions before measuring text. Failing to do so causes content overflow and a scrollbar. See "Accounting for Padding and Borders" above.$ARGUMENTS