Draw a tile on the editor's mockup the way the screen does: the CYD shows the whole tile again (0.2.76)
Both boards draw a normal tile with the icon on the left and the name and value beside it; the Vue editor stacked icon, name and value in one column. On a CYD mockup a tile is 47 px high and that stack needed 58 px (63 for a watch card), so the value line was cut in half and a big value hid the name. A single tile is now a row (.head: icon + .tx), a watch card puts the name row on top and the number under it, and the small slider runs underneath; on a CYD a tile with a slider uses the screen's 4 px padding and 8 px slider. The Guition mockup follows the screen the same way. App 0.2.76, firmware stays 0.2.63. Test results in docs/TEST_RESULTS_0276.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# Test results app 0.2.76 / firmware 0.2.63 (2026-09-18)
|
||||
|
||||
Max sent a screenshot of a CYD page in the editor ("het gaat over CYD in de EDITOR die niet 100% goed eruit
|
||||
ziet"): on every single tile the second line (*7,2 W*, *26,0 °C*, *Cleaning*, *Off*) was cut in half at the bottom
|
||||
edge. See CHANGELOG 0.2.76. Built on 0.2.75 (`261fa82`).
|
||||
|
||||
## What happened before
|
||||
|
||||
- Both boards draw a normal tile the same way (`runtime_tiles.h`, `TILE_TITLE_X`/`TILE_VALUE_X` in both profiles):
|
||||
the icon in its circle on the left, the name and the value beside it, vertically centred. A small slider runs
|
||||
under that row; a watch card has the icon and the name on top and the big value below.
|
||||
- The Vue editor (`TileCard.vue`, since 0.2.73) stacked the icon, the name and the value in one column. On a CYD
|
||||
mockup (292 × 219 px, the screen at 0.91) a tile is 47 px high; the stack needed 58 px, a watch card 63 px, and
|
||||
`.tile { overflow: hidden }` cut off the rest. The Guition's 73 px tiles had room, so it only showed on the CYD.
|
||||
- The wide tiles already had the screen's row layout and were fine.
|
||||
|
||||
## What changed
|
||||
|
||||
- `TileCard.vue`: a single tile draws `.head` (icon + `.tx` with name and value) as a row, like the screen. A watch
|
||||
card has `.head.top` with the big value under it; the small slider sits under the row.
|
||||
- `app.css`: `.tile .head` is the row, `.tile .tx` (was only for wide tiles) is the name/value column. On a CYD a
|
||||
tile with a small slider uses the screen's own numbers: 4 px padding above and below, an 8 px slider.
|
||||
- `web/tests/components.spec.ts`: a test for the three shapes (plain, watch, small slider).
|
||||
|
||||
## Checks
|
||||
|
||||
- Demo home (`.esphome/readme-render/demo_home.py`) with the editor on Vite, kitchen screen (CYD): every tile
|
||||
measured with `scrollHeight` against `clientHeight` in the browser.
|
||||
|
||||
| Tile | 0.2.75 needs / has | 0.2.76 needs / has |
|
||||
| --- | --- | --- |
|
||||
| Pasta · Active | 58 / 47 px | 47 / 47 px |
|
||||
| Coffee machine · On | 58 / 47 px | 47 / 47 px |
|
||||
| Kitchen lamp · On | 58 / 47 px | 47 / 47 px |
|
||||
| Power · 1249 W (watch) | 63 / 47 px | 47 / 47 px |
|
||||
| Sam · Home | 58 / 47 px | 47 / 47 px |
|
||||
| Kitchen lamp with a small slider (added in the DOM) | not measured | 47 / 47 px |
|
||||
|
||||
- Living room screen (Guition): all 8 tiles fit; the single tiles now show the icon left as on the screen's render
|
||||
(`docs/images/guition-home.png`).
|
||||
- `npm test` 47/47 (one new), `npm run check` clean, `npm run build`; Python `unittest discover -s tests` 409 OK.
|
||||
- No firmware change, so no ESPHome build and no C++ tests; `FIRMWARE_VERSION` stays 0.2.63.
|
||||
- Still for Max: open a CYD in the editor after updating the app and look at the tiles.
|
||||
@@ -1,3 +1,10 @@
|
||||
## 0.2.76 (firmware 0.2.63)
|
||||
|
||||
The CYD in the editor shows the whole tile again.
|
||||
|
||||
- **A tile on the mockup looks like the tile on the screen.** Both boards draw a normal tile with the icon on the left and the name and value beside it; the editor stacked the icon, the name and the value on top of each other. On a CYD's short tiles that did not fit, so the value under the name was cut in half (*26.0 °C*, *Cleaning*, *Off*) and a large value hid the name. Now the mockup uses the screen's layout on both boards: icon left, name and value next to it, the small slider underneath, and for a large value the name at the top with the number below it.
|
||||
- Only the editor changed. There is no new firmware: 0.2.63 stays current. Includes everything from 0.2.75.
|
||||
|
||||
## 0.2.75 (firmware 0.2.63)
|
||||
|
||||
Smooth icons on the CYD: the power ring is a ring again.
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7
-7
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<title>ESP Screens</title>
|
||||
<script type="module" crossorigin src="./assets/index-FSwnYVTm.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-C6IelTvW.css">
|
||||
<script type="module" crossorigin src="./assets/index-Cj3m_z3Z.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-BL5pkID8.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.2.75"
|
||||
version: "0.2.76"
|
||||
slug: esp_screen_manager
|
||||
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
@@ -154,14 +154,18 @@ async function onKey(e: KeyboardEvent) {
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="ic mdi" :class="{ lit: isOn }">{{ glyph(tileIconCp(tile)) }}</span>
|
||||
<span class="lead">
|
||||
<span v-if="display === 'watch'" class="big">{{ bigValue }}<small v-if="unit && !gone">{{ unit }}</small></span>
|
||||
<span class="nm">{{ name }}</span>
|
||||
<span v-if="goesTo" class="goto">Page {{ goesTo }} ›</span>
|
||||
<span v-else-if="display !== 'watch' && status" class="st" :class="{ off: gone }">{{ status }}</span>
|
||||
<span v-if="tile.options?.inline === 'slider'" class="mini-slider" :style="sliderStyle"></span>
|
||||
<!-- As the screen draws it: the icon on the left, the name and the value beside it. A watch
|
||||
card puts the name on top and the big value under it; the small slider runs underneath. -->
|
||||
<span class="head" :class="{ top: display === 'watch' }">
|
||||
<span class="ic mdi" :class="{ lit: isOn }">{{ glyph(tileIconCp(tile)) }}</span>
|
||||
<span class="tx">
|
||||
<span class="nm">{{ name }}</span>
|
||||
<span v-if="goesTo" class="goto">Page {{ goesTo }} ›</span>
|
||||
<span v-else-if="display !== 'watch' && status" class="st" :class="{ off: gone }">{{ status }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span v-if="display === 'watch'" class="big">{{ bigValue }}<small v-if="unit && !gone">{{ unit }}</small></span>
|
||||
<span v-if="tile.options?.inline === 'slider'" class="mini-slider" :style="sliderStyle"></span>
|
||||
</template>
|
||||
<button v-if="live" type="button" class="remove" title="Remove tile" :aria-label="`Remove ${name}`" @click.stop="removeTile(tile)">✕</button>
|
||||
</div>
|
||||
|
||||
@@ -149,12 +149,18 @@ a:hover { text-decoration: underline; }
|
||||
.tile .ic { font-size: 18px; line-height: 1; color: #1b1b1b; flex: none; }
|
||||
.tile .lead { display: grid; gap: 2px; min-width: 0; flex: 1; align-content: end; }
|
||||
.tile.wide .lead { display: flex; align-items: center; gap: 8px; align-content: initial; }
|
||||
.tile.wide .lead .tx { display: grid; min-width: 0; gap: 1px; }
|
||||
.tile .tx { display: grid; min-width: 0; gap: 1px; }
|
||||
.tile .head { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }
|
||||
.tile .head.top { flex: none; }
|
||||
.tile .nm { font-size: 11.5px; font-weight: 600; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.tile .st { font-size: 10.5px; color: #5a5f66; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.device.cyd .tile { padding: 6px 7px; }
|
||||
.device.cyd .tile .nm { font-size: 10.5px; }
|
||||
.device.cyd .tile .ic { font-size: 15px; }
|
||||
.device.cyd .tile .head { gap: 6px; }
|
||||
/* The screen's own numbers on its short tiles: 4 px above and below, an 8 px slider. */
|
||||
.device.cyd .tile:has(> .mini-slider) { padding-block: 4px; gap: 3px; }
|
||||
.device.cyd .tile .mini-slider { height: 8px; }
|
||||
.tile .mini-slider { height: 10px; border-radius: 5px; background: linear-gradient(to right, #ffbf38 60%, #fff1d3 60%); flex: none; width: 100%; }
|
||||
.tile.wide .mini-slider { width: 70px; }
|
||||
.tile .ctl { flex: none; display: flex; align-items: center; gap: 4px; color: #1b1b1b; }
|
||||
|
||||
@@ -65,6 +65,21 @@ describe("TileCard", () => {
|
||||
expect(gone.find(".st").text()).toBe("Unavailable");
|
||||
expect(gone.find(".st").classes()).toContain("off");
|
||||
});
|
||||
it("draws a single tile as the screen does: the icon left, the name and value beside it", () => {
|
||||
state.liveStates["sensor.t"] = { state: "1249", word: null, a: { unit_of_measurement: "W" } };
|
||||
const plain = placed({ entity: "sensor.t", name: "Power", slot: 0 });
|
||||
expect(plain.find(".head > .ic").exists()).toBe(true);
|
||||
expect(plain.find(".head > .tx > .nm").text()).toBe("Power");
|
||||
expect(plain.find(".head > .tx > .st").text()).toBe("1249 W");
|
||||
// A watch card keeps the name next to the icon and puts the big value underneath.
|
||||
const watch = placed({ entity: "sensor.t", name: "Power", slot: 1, options: { display: "watch" } });
|
||||
expect(watch.find(".head").classes()).toContain("top");
|
||||
expect(watch.find(".head .big").exists()).toBe(false);
|
||||
expect(watch.find(".head + .big").text()).toBe("1249W");
|
||||
state.liveStates["light.a"] = { state: "on", word: "On", a: { brightness: 255 } };
|
||||
const lamp = placed({ entity: "light.a", name: "", slot: 2, options: { inline: "slider" } });
|
||||
expect(lamp.find(".head + .mini-slider").exists()).toBe(true);
|
||||
});
|
||||
it("shows the display name when Home Assistant has no value, and nothing for a scene", () => {
|
||||
const graph = placed({ entity: "sensor.x", name: "Unknown sensor", slot: 0, options: { display: "graph" } });
|
||||
expect(graph.find(".st").text()).toBe("graph");
|
||||
|
||||
Reference in New Issue
Block a user