Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27920a7919 | ||
|
|
b012280373 | ||
|
|
c6fac8bf1c | ||
|
|
d1691f4bc2 | ||
|
|
206732e9f5 |
@@ -745,6 +745,42 @@ jobs:
|
||||
fi
|
||||
echo "документ опубликован в $target: $doc"
|
||||
|
||||
# Материал раунда обязан быть достижим с origin (#413).
|
||||
#
|
||||
# SPEC-REVIEW-403-r2 объявил материал на `HEAD = 83005c3c`, и тот же SHA
|
||||
# независимо назвал автор ТЗ в комментарии issue. Коммит существовал, но
|
||||
# к моменту публикации был осиротевшим: ветку перебазировали за 15 минут
|
||||
# ДО публикации документа, спец-коммит переехал в 94502d3d с тем же
|
||||
# сообщением и тем же содержимым. Через раунд команда `git diff
|
||||
# 83005c3c..HEAD` из §2.10 буквально не работала, и r3 восстанавливал
|
||||
# реальный коммит по содержимому диффа руками.
|
||||
#
|
||||
# Проверка стоит ПОСЛЕ публикации намеренно. Артефакт ревью терялся здесь
|
||||
# трижды (#171, #220), и «вердикт без документа» в этом репозитории
|
||||
# дороже мёртвой ссылки: документ сначала спасается, потом судится. Шаг
|
||||
# при этом идёт ДО «Переставить метку», поэтому инвариант «метка не
|
||||
# сменилась = прогон упал» сохраняется.
|
||||
#
|
||||
# Достижимость считается от `refs/remotes/origin/*`, а не от локальных
|
||||
# ссылок: осиротевший 83005c3c до сих пор лежит в клоне автора и
|
||||
# достижим там из необновлённой локальной ветки. Читателю отчёта от этого
|
||||
# пользы нет — он достанет только то, что есть на origin.
|
||||
- name: "Материал раунда воспроизводим (#413)"
|
||||
if: steps.rebase.outputs.conflict != 'true'
|
||||
env:
|
||||
NUM: ${{ github.event.issue.number }}
|
||||
STAGE: ${{ needs.guard.outputs.stage }}
|
||||
CYCLE: ${{ needs.guard.outputs.cycle }}
|
||||
BRANCH: ${{ steps.branch.outputs.name }}
|
||||
run: |
|
||||
marker=CODE-REVIEW
|
||||
if [ "$STAGE" = "spec" ]; then marker=SPEC-REVIEW; fi
|
||||
doc="docs/reviews/${marker}-${NUM}-r${CYCLE}.md"
|
||||
target="${BRANCH:-dev}"
|
||||
git fetch -q origin "$target"
|
||||
# Судится опубликованная версия, а не рабочая копия: именно её прочтёт
|
||||
# следующий раунд.
|
||||
git show "origin/$target:$doc" | node scripts/review-doc-guard.mjs --doc=-
|
||||
- name: Решение по вердикту
|
||||
id: decide
|
||||
if: steps.rebase.outputs.conflict != 'true'
|
||||
|
||||
@@ -1,125 +1,125 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "cfeec183260c075204dd0fdd0333fdf69f1bab8b9e9142483b49b645e0526710",
|
||||
"fingerprint": "baf59953cfd6eb3543f7da5af21787b20dc76f136b74782ae917f811d2fc1ab5",
|
||||
"entry": "houseplan-card.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js",
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 290375,
|
||||
"initialViewGzipBytes": 290391,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/de-h-9CiLUA.js",
|
||||
"houseplan-assets/editor-BcMfn7wA.js",
|
||||
"houseplan-assets/fr-JMJhplAF.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/de-CNmZS2Bl.js",
|
||||
"houseplan-assets/editor-CcKD8T48.js",
|
||||
"houseplan-assets/fr-ChOylnG-.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BKOSee7A.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js"
|
||||
],
|
||||
"lazyGzipBytes": 205825,
|
||||
"lazyGzipBytes": 205820,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/editor-BcMfn7wA.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/editor-CcKD8T48.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BKOSee7A.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 148717,
|
||||
"lazyEditorGzipBytes": 148720,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 14030,
|
||||
"lazyOnboardingGzipBytes": 14028,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-h-9CiLUA.js",
|
||||
"houseplan-assets/fr-JMJhplAF.js"
|
||||
"houseplan-assets/de-CNmZS2Bl.js",
|
||||
"houseplan-assets/fr-ChOylnG-.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 50148,
|
||||
"lazyLocaleGzipBytes": 50142,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"sha256": "056fb0975dfa0f753ff8a606d2276add24f02a92517d3536f02be93857970e83",
|
||||
"path": "houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"sha256": "26a9c96fdb60fb7fe12df6a09b2c27c26b2b90479cf4968bfc1b32a895c4e944",
|
||||
"rawBytes": 20636,
|
||||
"gzipBytes": 7070,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/de-h-9CiLUA.js",
|
||||
"sha256": "de934d5d3553dd90a853777514c7ec6e67b765adbd4b379bdd5e15b80448bc97",
|
||||
"path": "houseplan-assets/de-CNmZS2Bl.js",
|
||||
"sha256": "7850782105783e0dcdb910c7071a101e9a02b00c1c2015dbc05baeb3c47e15cc",
|
||||
"rawBytes": 83981,
|
||||
"gzipBytes": 25345,
|
||||
"gzipBytes": 25342,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-BcMfn7wA.js",
|
||||
"sha256": "367dd4bd9ef49463e12b3d9f405c5aa221d34fac6559f886a98c27946f4b341e",
|
||||
"path": "houseplan-assets/editor-CcKD8T48.js",
|
||||
"sha256": "61452fe308e80615a91df46691524b34a71022e6aa9168b3010acf8608899c45",
|
||||
"rawBytes": 3826,
|
||||
"gzipBytes": 1581,
|
||||
"gzipBytes": 1582,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/fr-JMJhplAF.js",
|
||||
"sha256": "d11639e11eac93a821abd81307203d8ca00ab14d3fade8e4049be725fee60df5",
|
||||
"path": "houseplan-assets/fr-ChOylnG-.js",
|
||||
"sha256": "11a94a647483fa90c9b79f9f45206ffc5f9d5008cb0c2227f53654399ad8e574",
|
||||
"rawBytes": 86212,
|
||||
"gzipBytes": 24803,
|
||||
"gzipBytes": 24800,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-DdbeLYki.js",
|
||||
"sha256": "85dbbe6644990d350c9658b44514ae5db444203ed537c2d01726f267d2766679",
|
||||
"rawBytes": 1032271,
|
||||
"gzipBytes": 289578,
|
||||
"path": "houseplan-assets/houseplan-card-BGFqEA3T.js",
|
||||
"sha256": "2f556d1f30b8368e1a866a171031f42968b0c6f00aa40d9f25d650b8bc469fa6",
|
||||
"rawBytes": 1032340,
|
||||
"gzipBytes": 289594,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-h-9CiLUA.js",
|
||||
"houseplan-assets/editor-BcMfn7wA.js",
|
||||
"houseplan-assets/fr-JMJhplAF.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js"
|
||||
"houseplan-assets/de-CNmZS2Bl.js",
|
||||
"houseplan-assets/editor-CcKD8T48.js",
|
||||
"houseplan-assets/fr-ChOylnG-.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BKOSee7A.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js",
|
||||
"sha256": "5889a92ae87a014f6e858b94da6f4683c10637384b44263156d23e2e52fea3e3",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-BKOSee7A.js",
|
||||
"sha256": "8361e79183dfc42c6aae966758c29018b4143d7cfae6a86d7bba886f68238dd1",
|
||||
"rawBytes": 543272,
|
||||
"gzipBytes": 140066,
|
||||
"gzipBytes": 140068,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js",
|
||||
"sha256": "68031aa68413c961ea1dd1c10fd0b3fe40775425dad227ea680ddc22c2e96f09",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js",
|
||||
"sha256": "c8eed0234c0928fcf49cca348ab38640f4662c85d2f3fc9b84a68f28e26ac214",
|
||||
"rawBytes": 28088,
|
||||
"gzipBytes": 6960,
|
||||
"gzipBytes": 6958,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "cacb921be9d92f674ba3fca7bd7bf07427d1a6e2e626b8a36d56aca54ae1d7e9",
|
||||
"sha256": "588c8aef7a904a7f4522ee1fa04949f3dc29dec8e9c3910234ff76d996a151cf",
|
||||
"rawBytes": 1183,
|
||||
"gzipBytes": 797,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="cfeec183260c075204dd0fdd0333fdf69f1bab8b9e9142483b49b645e0526710";import{b as e,l as o,t,d8 as s,A as a,d9 as i,da as l,db as n,E as r,c}from"./houseplan-card-DdbeLYki.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="baf59953cfd6eb3543f7da5af21787b20dc76f136b74782ae917f811d2fc1ab5";import{b as e,l as o,t,d8 as s,A as a,d9 as i,da as l,db as n,E as r,c}from"./houseplan-card-BGFqEA3T.js";class f extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._formData}
|
||||
.schema=${e}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
.computeLabel=${e=>f[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(f.slice(0,3))}
|
||||
${d(h.slice(0,3))}
|
||||
${_?c`<div class="default-floor-error" role="alert"
|
||||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||||
${t(s,"editor.default_floor_missing",{id:_})}
|
||||
</div>`:a}
|
||||
${d(f.slice(3))}
|
||||
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
|
||||
${d(h.slice(3))}
|
||||
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="cfeec183260c075204dd0fdd0333fdf69f1bab8b9e9142483b49b645e0526710";try{await import("./houseplan-assets/houseplan-card-DdbeLYki.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="baf59953cfd6eb3543f7da5af21787b20dc76f136b74782ae917f811d2fc1ab5";try{await import("./houseplan-assets/houseplan-card-BGFqEA3T.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
|
||||
|
||||
@@ -125,7 +125,12 @@ const result = await page.evaluate(async ({ version, previewText, previewSha })
|
||||
const supportCallsBeforeOpen = calls.length;
|
||||
await open();
|
||||
const dialog = root().querySelector('#support-dialog');
|
||||
const touchRect = root().querySelector('.support-button').getBoundingClientRect();
|
||||
const supportButton = root().querySelector('.support-button');
|
||||
const settingsButton = supportButton?.previousElementSibling;
|
||||
const touchRect = supportButton.getBoundingClientRect();
|
||||
const settingsRect = settingsButton?.getBoundingClientRect();
|
||||
const message = dialog?.querySelector('#support-message');
|
||||
const contact = dialog?.querySelector('#support-contact');
|
||||
const guide = dialog?.querySelector('#support-docs-heading + a');
|
||||
out.openIsLocal = calls.length === supportCallsBeforeOpen;
|
||||
out.aboutAndEnglishGuide = !!dialog?.querySelector('.aboutver')
|
||||
@@ -135,6 +140,11 @@ const result = await page.evaluate(async ({ version, previewText, previewSha })
|
||||
&& dialog?.querySelector('#support-message')?.value === ''
|
||||
&& dialog?.querySelector('.supportattach input')?.checked === false;
|
||||
out.touchTarget = touchRect.width >= 44 && touchRect.height >= 44;
|
||||
out.headerActionsMatch = !!settingsRect
|
||||
&& Math.abs(touchRect.width - settingsRect.width) < 0.01
|
||||
&& Math.abs(touchRect.height - settingsRect.height) < 0.01;
|
||||
out.messageSurfaceMatchesContact = !!message && !!contact
|
||||
&& getComputedStyle(message).backgroundColor === getComputedStyle(contact).backgroundColor;
|
||||
out.noHorizontalOverflow = dialog.scrollWidth <= dialog.clientWidth + 1
|
||||
&& dialog.querySelector('.supportbody').scrollWidth
|
||||
<= dialog.querySelector('.supportbody').clientWidth + 1;
|
||||
|
||||
@@ -1,125 +1,125 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "cfeec183260c075204dd0fdd0333fdf69f1bab8b9e9142483b49b645e0526710",
|
||||
"fingerprint": "baf59953cfd6eb3543f7da5af21787b20dc76f136b74782ae917f811d2fc1ab5",
|
||||
"entry": "houseplan-card.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js",
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 290375,
|
||||
"initialViewGzipBytes": 290391,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/de-h-9CiLUA.js",
|
||||
"houseplan-assets/editor-BcMfn7wA.js",
|
||||
"houseplan-assets/fr-JMJhplAF.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/de-CNmZS2Bl.js",
|
||||
"houseplan-assets/editor-CcKD8T48.js",
|
||||
"houseplan-assets/fr-ChOylnG-.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BKOSee7A.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js"
|
||||
],
|
||||
"lazyGzipBytes": 205825,
|
||||
"lazyGzipBytes": 205820,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/editor-BcMfn7wA.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/editor-CcKD8T48.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BKOSee7A.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 148717,
|
||||
"lazyEditorGzipBytes": 148720,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 14030,
|
||||
"lazyOnboardingGzipBytes": 14028,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-h-9CiLUA.js",
|
||||
"houseplan-assets/fr-JMJhplAF.js"
|
||||
"houseplan-assets/de-CNmZS2Bl.js",
|
||||
"houseplan-assets/fr-ChOylnG-.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 50148,
|
||||
"lazyLocaleGzipBytes": 50142,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"sha256": "056fb0975dfa0f753ff8a606d2276add24f02a92517d3536f02be93857970e83",
|
||||
"path": "houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"sha256": "26a9c96fdb60fb7fe12df6a09b2c27c26b2b90479cf4968bfc1b32a895c4e944",
|
||||
"rawBytes": 20636,
|
||||
"gzipBytes": 7070,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/de-h-9CiLUA.js",
|
||||
"sha256": "de934d5d3553dd90a853777514c7ec6e67b765adbd4b379bdd5e15b80448bc97",
|
||||
"path": "houseplan-assets/de-CNmZS2Bl.js",
|
||||
"sha256": "7850782105783e0dcdb910c7071a101e9a02b00c1c2015dbc05baeb3c47e15cc",
|
||||
"rawBytes": 83981,
|
||||
"gzipBytes": 25345,
|
||||
"gzipBytes": 25342,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-BcMfn7wA.js",
|
||||
"sha256": "367dd4bd9ef49463e12b3d9f405c5aa221d34fac6559f886a98c27946f4b341e",
|
||||
"path": "houseplan-assets/editor-CcKD8T48.js",
|
||||
"sha256": "61452fe308e80615a91df46691524b34a71022e6aa9168b3010acf8608899c45",
|
||||
"rawBytes": 3826,
|
||||
"gzipBytes": 1581,
|
||||
"gzipBytes": 1582,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/fr-JMJhplAF.js",
|
||||
"sha256": "d11639e11eac93a821abd81307203d8ca00ab14d3fade8e4049be725fee60df5",
|
||||
"path": "houseplan-assets/fr-ChOylnG-.js",
|
||||
"sha256": "11a94a647483fa90c9b79f9f45206ffc5f9d5008cb0c2227f53654399ad8e574",
|
||||
"rawBytes": 86212,
|
||||
"gzipBytes": 24803,
|
||||
"gzipBytes": 24800,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-DdbeLYki.js",
|
||||
"sha256": "85dbbe6644990d350c9658b44514ae5db444203ed537c2d01726f267d2766679",
|
||||
"rawBytes": 1032271,
|
||||
"gzipBytes": 289578,
|
||||
"path": "houseplan-assets/houseplan-card-BGFqEA3T.js",
|
||||
"sha256": "2f556d1f30b8368e1a866a171031f42968b0c6f00aa40d9f25d650b8bc469fa6",
|
||||
"rawBytes": 1032340,
|
||||
"gzipBytes": 289594,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-h-9CiLUA.js",
|
||||
"houseplan-assets/editor-BcMfn7wA.js",
|
||||
"houseplan-assets/fr-JMJhplAF.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js"
|
||||
"houseplan-assets/de-CNmZS2Bl.js",
|
||||
"houseplan-assets/editor-CcKD8T48.js",
|
||||
"houseplan-assets/fr-ChOylnG-.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BKOSee7A.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-DBsb8Jgp.js",
|
||||
"sha256": "5889a92ae87a014f6e858b94da6f4683c10637384b44263156d23e2e52fea3e3",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-BKOSee7A.js",
|
||||
"sha256": "8361e79183dfc42c6aae966758c29018b4143d7cfae6a86d7bba886f68238dd1",
|
||||
"rawBytes": 543272,
|
||||
"gzipBytes": 140066,
|
||||
"gzipBytes": 140068,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DuBh8LrV.js",
|
||||
"sha256": "68031aa68413c961ea1dd1c10fd0b3fe40775425dad227ea680ddc22c2e96f09",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-B8zj38Pl.js",
|
||||
"sha256": "c8eed0234c0928fcf49cca348ab38640f4662c85d2f3fc9b84a68f28e26ac214",
|
||||
"rawBytes": 28088,
|
||||
"gzipBytes": 6960,
|
||||
"gzipBytes": 6958,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-BDzJ-1w0.js",
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/backdrop-pick-78QbziGu.js",
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "cacb921be9d92f674ba3fca7bd7bf07427d1a6e2e626b8a36d56aca54ae1d7e9",
|
||||
"sha256": "588c8aef7a904a7f4522ee1fa04949f3dc29dec8e9c3910234ff76d996a151cf",
|
||||
"rawBytes": 1183,
|
||||
"gzipBytes": 797,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DdbeLYki.js"
|
||||
"houseplan-assets/houseplan-card-BGFqEA3T.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="cfeec183260c075204dd0fdd0333fdf69f1bab8b9e9142483b49b645e0526710";import{b as e,l as o,t,d8 as s,A as a,d9 as i,da as l,db as n,E as r,c}from"./houseplan-card-DdbeLYki.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="baf59953cfd6eb3543f7da5af21787b20dc76f136b74782ae917f811d2fc1ab5";import{b as e,l as o,t,d8 as s,A as a,d9 as i,da as l,db as n,E as r,c}from"./houseplan-card-BGFqEA3T.js";class f extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._formData}
|
||||
.schema=${e}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
.computeLabel=${e=>f[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(f.slice(0,3))}
|
||||
${d(h.slice(0,3))}
|
||||
${_?c`<div class="default-floor-error" role="alert"
|
||||
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
|
||||
${t(s,"editor.default_floor_missing",{id:_})}
|
||||
</div>`:a}
|
||||
${d(f.slice(3))}
|
||||
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
|
||||
${d(h.slice(3))}
|
||||
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="cfeec183260c075204dd0fdd0333fdf69f1bab8b9e9142483b49b645e0526710";try{await import("./houseplan-assets/houseplan-card-DdbeLYki.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="baf59953cfd6eb3543f7da5af21787b20dc76f136b74782ae917f811d2fc1ab5";try{await import("./houseplan-assets/houseplan-card-BGFqEA3T.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- **Help & feedback** now uses the same 44 px header button as General settings,
|
||||
and its message field uses the ordinary input surface instead of a grey tint
|
||||
([#415](https://github.com/Matysh/houseplan-card/issues/415)).
|
||||
|
||||
## v1.70.0-beta.3 — 2026-09-02
|
||||
|
||||
- The new **Help & feedback** dialog keeps version, documentation and project
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Кнопка **«Помощь и обратная связь»** теперь совпадает по размеру 44 px с
|
||||
кнопкой «Общие настройки», а поле сообщения использует обычный фон поля ввода
|
||||
вместо серой заливки
|
||||
([#415](https://github.com/Matysh/houseplan-card/issues/415)).
|
||||
|
||||
## v1.70.0-beta.3 — 2026-09-02
|
||||
|
||||
- Новый диалог **«Помощь и обратная связь»** объединяет версию, документацию и
|
||||
|
||||
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 288 KiB After Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 277 KiB After Width: | Height: | Size: 272 KiB |
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 116 KiB |
@@ -3,7 +3,7 @@
|
||||
"fixture": "synthetic-only",
|
||||
"chromium": "151.0.7922.34",
|
||||
"oxipng": "oxipng 10.2.0",
|
||||
"sourceFingerprint": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"sourceFingerprint": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"captureScriptSha256": "135dd9a566653b05cae3010d7861fca6453daef0ecdcdf1ba10059e65f0d4de8",
|
||||
"command": "npm run build && node demo/docs/capture.mjs",
|
||||
"scenarios": {
|
||||
@@ -15,8 +15,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "f4ecb7fd1f107ccda6c8a1e90be1bce0f2690f93e5e42776ca783297f4143135"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "e0d39cfae61d194e85147ca36499abc5a173efdf00195f2f00a7111866dd54bd"
|
||||
},
|
||||
"view-touch": {
|
||||
"file": "02-view-touch.png",
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "946801f6475a40c0e31a70e4b67eaa34c6f70df5329a63915f445e6e4811ca6a"
|
||||
},
|
||||
"space-create": {
|
||||
@@ -37,7 +37,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "617b51b3648498787b5039980c9f3eceb75ba56ed63a1a20e616bc05bc304362"
|
||||
},
|
||||
"room-contour-close": {
|
||||
@@ -48,8 +48,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "9a79b7e517741e74701d1172ec3c66a340aaa9049a7f7a7b2fe86f4c3a2eccf0"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "f81cfc6b9e4a923d36031c514f02927a33e8b8b2c75f007e8aed45dee16e491d"
|
||||
},
|
||||
"plan-context-tray": {
|
||||
"file": "05-plan-context-tray.png",
|
||||
@@ -59,8 +59,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "8fb569f326627700e08156c1ca35a4042bcb529a955663b54228d04357c881d0"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "6b8bfaada24ff1fadcda1b6946a5894d90d981e2c7f7096273e23bfa58ac3218"
|
||||
},
|
||||
"device-editor": {
|
||||
"file": "06-device-editor.png",
|
||||
@@ -70,8 +70,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "720fc3aa2070d1fcf6907091941751c623f30cc6a001ff95e22de9be998136df"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "a13536d93f5bf70207e7ece0d3f4617d4ba919cb556342de67eaa81961589fb2"
|
||||
},
|
||||
"device-display-preview": {
|
||||
"file": "06-device-display-preview.png",
|
||||
@@ -81,8 +81,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "b2294c6de0b18ef386b5a2d18422a866b84d095f133d31e56e307664e31e5bc0"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "d93be8761de4451fa792cc4415789b4624f38638eccc3623502ea61dcf3b3a13"
|
||||
},
|
||||
"background-editor": {
|
||||
"file": "07-background-editor.png",
|
||||
@@ -92,8 +92,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "8dbc5864124c4bddbe076722a1503e666c40f432cfe7fff86e6d196c428365f3"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "054170fd9ef45762b602b4d5c9c3b9ea9724858be61af137970c246f485c13bb"
|
||||
},
|
||||
"room-card": {
|
||||
"file": "08-room-card.png",
|
||||
@@ -103,8 +103,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "d09709ae1479cfaa1902068cb2a6015dfee3b58f1bbd349bafaa2c0592812463"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "2ae4a58853d98e10d12456b2078ec2f6a0b597722c8310d7b016abd2bc42561e"
|
||||
},
|
||||
"device-info": {
|
||||
"file": "09-device-info.png",
|
||||
@@ -114,15 +114,13 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "c3e3e7dba1910e09c702472e6ea09d7ef13b91e082c09d2f9474437c776fb26e",
|
||||
"imageSha256": "c0b20a31c2987c8737e7b746c7d358e6c97231ea7e06d73050a2d3aebb216558"
|
||||
"sourceSha256": "51672b157f9dfe4f36747b0c8963b079295b67ec3c93e33f3c18287aca59fa90",
|
||||
"imageSha256": "cb37f7eefd936f98ef44969b21dfe8e40ee27d1f558abe8d53c0223f6488b8a6"
|
||||
}
|
||||
},
|
||||
"acceptance": {
|
||||
"declared": [
|
||||
"view-desktop",
|
||||
"view-touch",
|
||||
"space-create",
|
||||
"room-contour-close",
|
||||
"plan-context-tray",
|
||||
"device-editor",
|
||||
@@ -131,8 +129,7 @@
|
||||
"room-card",
|
||||
"device-info"
|
||||
],
|
||||
"witnesses": 0,
|
||||
"floor": 1,
|
||||
"witnessesSkippedBecause": "сменились флаги растеризации и обрезка стала целой (#410)"
|
||||
"witnesses": 2,
|
||||
"floor": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
# CODE-REVIEW-415-r1
|
||||
|
||||
Issue: #415 — «Полиш диалога помощи: размер кнопки и белый фон сообщения»
|
||||
Ветка: `issue/415-support-visual-polish`
|
||||
SHA ревью: `2fc8c1203782026bb45647ad54ef11857f7eb2c3`
|
||||
Трек: `trivial` (AC в теле issue, без файла в `docs/specs/`) — этап ревью: **code**.
|
||||
Заход: r1 (первый цикл, предыдущих раундов и вердиктов нет — разбор полный).
|
||||
|
||||
## Скоуп
|
||||
|
||||
Issue фиксирует два визуальных расхождения после #43:
|
||||
|
||||
- **AC1 (smoke):** кнопки «Общие настройки» и «Помощь и обратная связь» в одной
|
||||
шапке должны иметь одинаковые фактические ширину и высоту; отдельный размер
|
||||
для кнопки помощи не задаётся.
|
||||
- **AC2 (smoke + golden перед бетой):** поле «Сообщение» должно использовать ту
|
||||
же поверхность ввода, что поле контакта (белую в светлой теме, штатную
|
||||
тёмную — в тёмной); read-only preview (`.supportraw`) не меняется.
|
||||
|
||||
Затронутые файлы по диффу: `src/houseplan-card.ts`, `src/styles/plan.styles.ts`,
|
||||
`src/styles/dialogs.styles.ts`, `demo/smoke_support_feedback.mjs`,
|
||||
`test/support-feedback.test.mjs`, оба changelog, сгенерированные бандлы (класс D).
|
||||
Без i18n, без миграции, без geometry — совпадает с заявленным в issue «Затрагиваемое».
|
||||
|
||||
## Как проверялось
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Типы | `npx tsc --noEmit` | зелёный, без вывода |
|
||||
| Юнит-тесты | `npm test` | 1724 passed / 0 failed / 1 skipped (совпадает с хендоффом автора) |
|
||||
| Сборка + сверка бандла | `npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` | идентичны |
|
||||
| Новый `any` | `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | 7 добавленных строк в 3 файлах, новых `any` нет |
|
||||
| Выбор смоков | `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | см. раздел ниже |
|
||||
| Целевой смок (AC1+AC2) | `node demo/smoke_support_feedback.mjs` | **OK**, включая новые поля `headerActionsMatch`, `messageSurfaceMatchesContact` |
|
||||
| Смок-спецпроверка на падение | тот же смок на `src/*` из родительского коммита `afe5e989` | `headerActionsMatch: false`, `messageSurfaceMatchesContact: false` — тест умеет падать |
|
||||
| Документационный гейт | `node scripts/check-docs.mjs` | **ERROR: screenshot source fingerprint is stale** |
|
||||
| То же на `origin/dev` (`afe5e989`) | `node scripts/check-docs.mjs` (с исходниками родителя) | зелёный — подтверждает, что именно этот диф делает отпечаток устаревшим |
|
||||
|
||||
Рабочее дерево после всех экспериментов с временным `git checkout` родительских
|
||||
версий файлов возвращено в исходное состояние; `git status --porcelain` пуст,
|
||||
`git rev-parse HEAD` = `2fc8c120...`, сгенерированные файлы побайтово совпадают
|
||||
с `HEAD` (`cmp` зелёный).
|
||||
|
||||
### Выбор браузерных смоков (`scripts/smoke-select.mjs`)
|
||||
|
||||
```
|
||||
Изменено файлов src/**: 3 · символов проекта на изменённых строках: 2
|
||||
Матрица: 213 смоков · порог «широкого» символа: больше 42 смоков
|
||||
|
||||
Прямое совпадение (9):
|
||||
demo/smoke_backup_transfer.mjs ← _openSettingsDialog
|
||||
demo/smoke_bg_color.mjs ← _openSettingsDialog
|
||||
demo/smoke_color_picker_consumers.mjs ← _openSettingsDialog
|
||||
demo/smoke_dialog_zombie.mjs ← _openSettingsDialog
|
||||
demo/smoke_esc_dialogs.mjs ← _openSettingsDialog
|
||||
demo/smoke_general_settings.mjs ← _openSettingsDialog
|
||||
demo/smoke_ha_controls.mjs ← _openSettingsDialog
|
||||
demo/smoke_help_affordance.mjs ← _openSettingsDialog
|
||||
demo/smoke_sun.mjs ← _openSettingsDialog
|
||||
```
|
||||
|
||||
Инструмент не назвал `demo/smoke_support_feedback.mjs` (совпадение идёт по
|
||||
имени JS-символа на изменённой строке, а не по предметной области; сам смок
|
||||
селектор `.support-button`, а не `_openSupportDialog`, не матчит построчно) —
|
||||
он взят отдельно, так как прямо назван в AC1/AC2.
|
||||
|
||||
Решение по 9 найденным смокам: причина совпадения — обе кнопки шапки лежат на
|
||||
строке, где стоит вызов `_openSettingsDialog`/`_openSupportDialog`; сама функция
|
||||
не менялась, менялся только список CSS-классов элемента. Прогнал два самых
|
||||
предметно близких — `smoke_general_settings.mjs` (диалог настроек целиком) и
|
||||
`smoke_help_affordance.mjs` (соседняя механика помощи/подсказок) — оба **OK**
|
||||
без регрессий в открытии диалогов и фокусе. Остальные 7 (`backup_transfer`,
|
||||
`bg_color`, `color_picker_consumers`, `dialog_zombie`, `esc_dialogs`,
|
||||
`ha_controls`, `sun`) не прогонял: они используют `_openSettingsDialog` только
|
||||
как способ добраться до необязанной с этим issue функциональности, а
|
||||
изменение — чисто визуальное (класс `header-action`, без изменения обработчика
|
||||
клика или разметки диалогов). Полный прогон всех 213 смоков — предрелизный
|
||||
гейт, не гейт ревью (§8), и здесь ему делать нечего: диф не «задевает всё».
|
||||
|
||||
### Что не проверял и почему
|
||||
|
||||
- **`npm run golden:verify`** — не прогонял. AC2 сам относит golden-проверку к
|
||||
«перед бетой», а не к код-ревью; в `demo/golden/` нет сценариев, ссылающихся
|
||||
на `.support-button`, `.header-action` или `.supportmessage` — совпадает с
|
||||
этим разделением.
|
||||
- **`node scripts/model-invariants.mjs`** — не прогонял. Диф не касается
|
||||
геометрии, рёбер, `layout`, `marker.space`, `open_spans`.
|
||||
- **`python -m pytest tests_backend`** — не прогонял. `custom_components/**/*.py`
|
||||
не тронут.
|
||||
- **Performance-профили** — не названы в AC, не прогонял.
|
||||
- Остальные 7 из 9 «прямое совпадение» смоков — см. решение выше.
|
||||
|
||||
## Находки
|
||||
|
||||
### High — check-docs: отпечаток скриншотов документации устарел
|
||||
|
||||
`node scripts/check-docs.mjs` падает на этом SHA:
|
||||
`ERROR screenshot source fingerprint is stale; run npm run build && node demo/docs/capture.mjs`.
|
||||
|
||||
Этот гейт — часть `validate.yml` (`node scripts/check-docs.mjs --external`),
|
||||
то есть часть CI Validate, а не только локальная рекомендация. Проверено, что
|
||||
регрессия внесена именно этим коммитом: на исходниках родителя
|
||||
(`afe5e989`, = `origin/dev`) тот же скрипт зелёный
|
||||
(«Documentation checks passed»); после возврата `src/houseplan-card.ts`,
|
||||
`src/styles/plan.styles.ts`, `src/styles/dialogs.styles.ts` к текущему SHA
|
||||
скрипт снова красный. Причина устройства гейта задокументирована в PROCESS.md
|
||||
§8: отпечаток считается по всему `src/**`, поэтому любая правка фронтенда
|
||||
делает его устаревшим безусловно — «выбирать тут нечего». Ровно этот пропуск
|
||||
уже дважды стоил продукту красного `docs`-джоба на `dev` (#230, #234, #237);
|
||||
хендофф автора («Передача на код-ревью») не упоминает пересъёмку скриншотов
|
||||
вообще.
|
||||
|
||||
**Воспроизведение:**
|
||||
```
|
||||
$ node scripts/check-docs.mjs
|
||||
ERROR screenshot source fingerprint is stale; run npm run build && node demo/docs/capture.mjs
|
||||
```
|
||||
|
||||
**Почему High:** гейт входит в обязательную часть CI (`validate.yml`) и
|
||||
детерминированно красный на этом SHA; слияние в `dev` в этом состоянии
|
||||
повторяет уже случавшийся инцидент (`dev` с красным `docs`-джобом до следующей
|
||||
задачи). Это не вопрос вкуса и не техническое несогласие — факт, проверенный
|
||||
запуском.
|
||||
|
||||
**Что нужно для закрытия:** прогнать джобу `Docs screenshots`
|
||||
(`workflow_dispatch`), принять артефакт локально
|
||||
`npm run docs:accept -- --reviewed --from=<распакованный артефакт>` и
|
||||
закоммитить эталоны вместе с задачей (класс D, трейлер `Baseline-Reviewed`).
|
||||
Это не в моих полномочиях как ревьюера (я не запускаю релизные workflow и не
|
||||
пишу в репозиторий) и не то, что можно решить локальной правкой кода — гейт
|
||||
обслуживается человеком/автором по регламенту §8.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **AC1 (кнопки одного размера).** `.support-button` больше не несёт стилей
|
||||
размера — правило переименовано в `.header-action`
|
||||
(`src/styles/plan.styles.ts:188`, было `.support-button {min-width:44px...}`)
|
||||
и навешено на **обе** кнопки шапки (`src/houseplan-card.ts:11496,11499`);
|
||||
класс `support-button` остался только как селектор-хук для JS/смоков, без
|
||||
собственных правил стиля (проверено чтением — единственное вхождение строки
|
||||
`support-button` в `src/**` осталось в шаблоне, ни одного в `*.styles.ts`).
|
||||
Доказано автотестом: `out.headerActionsMatch` в
|
||||
`demo/smoke_support_feedback.mjs` сравнивает `getBoundingClientRect()` обеих
|
||||
кнопок с допуском 0.01px и **умеет падать** — воспроизведено на исходниках
|
||||
родительского коммита (оба новых поля дают `false`).
|
||||
- **AC2 (единая поверхность поля).** `.supportmessage` получил явный
|
||||
`background: var(--hp-bg)` (`src/styles/dialogs.styles.ts:1189-1192`), что
|
||||
побайтово тот же токен, что использует поле контакта `.namein`
|
||||
(`src/styles/dialogs.styles.ts:353-355`, тоже `var(--hp-bg)`) — источник
|
||||
значения один (переменная темы), а не два литерала, вычисляющих одно и то же
|
||||
число/цвет. `.supportraw` (read-only preview) не тронут: делит с
|
||||
`.supportmessage` только общий базовый блок правил (border/padding/шрифт), а
|
||||
собственный фон (`color-mix(...)`) остаётся прежним — соответствует «preview
|
||||
не меняется». Доказано автотестом `out.messageSurfaceMatchesContact`
|
||||
(сравнение `getComputedStyle(...).backgroundColor`), тоже проверено на
|
||||
падение тем же способом.
|
||||
- **Трейлеры и changelog.** Коммит `2fc8c120` несёт `Issue: #415` и
|
||||
`User-Visible: yes`; оба changelog (`docs/CHANGELOG.md`,
|
||||
`docs/CHANGELOG.ru.md`) правлены в том же коммите, формулировки совпадают с
|
||||
терминологией `docs/USER-GUIDE.ru.md` («Общие настройки», «Помощь и
|
||||
обратная связь» — см. `docs/USER-GUIDE.ru.md:58,1920`).
|
||||
- **Тестовый контракт остался честным.** `test/support-feedback.test.mjs`
|
||||
проверяет ровно две вещи по коду (не только по смоку): что в шапке ровно два
|
||||
вхождения `header-action` и что `.header-action`/`.supportmessage` содержат
|
||||
нужные правила — статический дубль-контроль поверх браузерного смока.
|
||||
- **Никакого расширения скоупа.** Диф ограничен двумя классами CSS и одной
|
||||
строкой фона; ни новых сценариев, ни миграции, ни i18n, ни touch/perf.
|
||||
- **Одно число — один источник (44px, фон).** Оба случая сведены к общему
|
||||
правилу (`.header-action`, `var(--hp-bg)`), а не к двум местам, вычисляющим
|
||||
одно значение по отдельности — ровно то, о чём предупреждает §8.
|
||||
|
||||
## Вывод
|
||||
|
||||
Единственная находка — High, блокирующая: `check-docs` детерминированно красный
|
||||
на этом SHA и является частью CI Validate. AC1 и AC2 при этом выполнены и
|
||||
доказаны автотестом, который умеет падать. Возврат автору для прогона джобы
|
||||
скриншотов и коммита эталонов; повторный цикл — по дельте (§2.10), с разделами
|
||||
«Закрытие раунда r1» и «Унаследовано из r1».
|
||||
@@ -0,0 +1,194 @@
|
||||
# CODE-REVIEW-415-r2
|
||||
|
||||
Issue: #415 — «Полиш диалога помощи: размер кнопки и белый фон сообщения»
|
||||
Ветка: `issue/415-support-visual-polish`
|
||||
SHA ревью: `b0122803737d47950f8c2295b479afa53b8369e0`
|
||||
Трек: `trivial` (AC в теле issue, без файла в `docs/specs/`) — этап ревью: **code**.
|
||||
Заход: r2 · блокирующих циклов израсходовано 1 из 2 (зелёный вердикт этого
|
||||
захода цикл не образует, #227).
|
||||
|
||||
## Почему разбор снова полный, а не только по дельте
|
||||
|
||||
Между r1 и r2 ветка была **перебазирована на ушедший вперёд `dev`**: r1 стоял на
|
||||
`afe5e989` (тогдашний `origin/dev`), сейчас `origin/dev` = `206732e9` («ci:
|
||||
refuse a review round that cites an unreachable SHA», issue #413, чужая
|
||||
задача — попала в базу только ребейзом). Старый SHA r1
|
||||
(`2fc8c1203782026bb45647ad54ef11857f7eb2c3`) в дереве больше не существует
|
||||
(`git cat-file -t` — «could not get object info»): команда `git diff <тот
|
||||
SHA>..HEAD` из §2.10 не выполнима буквально. Это ровно случай §7.2 «ребейз на
|
||||
ушедший вперёд `dev` — после ребейза это другой код», поэтому материал этого
|
||||
раунда — `git diff origin/dev...HEAD` целиком, а не узкая дельта поверх
|
||||
осиротевшего SHA.
|
||||
|
||||
Продуктовый диф при этом **побайтово не менялся** — сверено построчно с телом
|
||||
CODE-REVIEW-415-r1 (см. ниже раздел «Закрытие раунда r1»): те же три строки в
|
||||
`src/houseplan-card.ts`, `src/styles/plan.styles.ts`,
|
||||
`src/styles/dialogs.styles.ts`. Новый код в диапазоне — только пересъёмка
|
||||
скриншотов и сам документ r1.
|
||||
|
||||
## Скоуп
|
||||
|
||||
Issue фиксирует два визуальных расхождения после #43:
|
||||
|
||||
- **AC1 (smoke):** кнопки «Общие настройки» и «Помощь и обратная связь» в одной
|
||||
шапке должны иметь одинаковые фактические ширину и высоту; отдельный размер
|
||||
для кнопки помощи не задаётся.
|
||||
- **AC2 (smoke + golden перед бетой):** поле «Сообщение» должно использовать ту
|
||||
же поверхность ввода, что поле контакта (белую в светлой теме, штатную
|
||||
тёмную — в тёмной); read-only preview (`.supportraw`) не меняется.
|
||||
|
||||
Диапазон `origin/dev...HEAD` (3 коммита):
|
||||
`d1691f4b` fix: align support controls (класс A+B+C, `Issue: #415`,
|
||||
`User-Visible: yes`) → `c6fac8bf` docs: review document for #415 (класс C,
|
||||
`User-Visible: no`) → `b0122803` docs: refresh reviewed support screenshots
|
||||
(класс C — `docs/images/**`, не класс D; `User-Visible: no`).
|
||||
|
||||
Затронуто по коду: `src/houseplan-card.ts`, `src/styles/plan.styles.ts`,
|
||||
`src/styles/dialogs.styles.ts`, `demo/smoke_support_feedback.mjs`,
|
||||
`test/support-feedback.test.mjs`, оба changelog, бандлы (класс D),
|
||||
`docs/images/*.png` + `docs/images/screenshots.json` (класс C, пересъёмка).
|
||||
Без i18n, без миграции, без geometry — совпадает с заявленным в issue
|
||||
«Затрагиваемое».
|
||||
|
||||
## Как проверялось
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Типы | `npx tsc --noEmit` | зелёный, без вывода |
|
||||
| Юнит-тесты | `npm test` | 1729 passed / 0 failed / 1 skipped (совпадает с хендоффом автора) |
|
||||
| Сборка | `npm run build` | зелёный |
|
||||
| Сверка бандла (3 копии) | `npm run bundle:sync` → `cmp dist/houseplan-card.js custom_components/.../houseplan-card.js`, `cmp dist/houseplan-assets.json custom_components/.../houseplan-assets.json`, `git status --porcelain` | все три идентичны, рабочее дерево чисто |
|
||||
| Документационный гейт | `node scripts/check-docs.mjs` | **зелёный** («Documentation checks passed (7 files, 10 external links)») — была High-находка r1, закрыта |
|
||||
| Новый `any` | `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | 7 добавленных строк в 3 файлах, новых `any` нет |
|
||||
| Оффлайн-гейт процесса | `node scripts/process-gate.mjs` | «гейт пройден, предупреждений 1» — WARN п.3 (нет `docs/specs/415-*.md`) ожидаемо для трека `trivial` |
|
||||
| Выбор смоков | `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | см. раздел ниже — идентичен выбору r1 (продуктовый диф не менялся) |
|
||||
| Целевой смок (AC1+AC2) | `node demo/smoke_support_feedback.mjs` | **OK**, `headerActionsMatch: true`, `messageSurfaceMatchesContact: true` |
|
||||
| Смок умеет падать | тот же смок на `src/houseplan-card.ts` + оба `*.styles.ts` из `afe5e989` (временный `git checkout`, независимо от r1) | `headerActionsMatch: false`, `messageSurfaceMatchesContact: false` — воспроизведено лично, дерево возвращено (`git status --porcelain` пуст, `cmp` бандла зелёный) |
|
||||
| Точечные смежные смоки | `node demo/smoke_general_settings.mjs`, `node demo/smoke_help_affordance.mjs` | оба **OK** |
|
||||
| CI-прогон пересъёмки | `gh run view 33592550716` | `conclusion: success`, workflow «Скриншоты документации», `headSha: c6fac8bf` — ссылка в коммите `b0122803` подтверждена, не выдумана |
|
||||
| Провенанс пересъёмки | `git diff origin/dev...HEAD -- docs/images/screenshots.json` | 10 сценариев, у всех 10 обновился `sourceSha256` (глобальный отпечаток), у 8 обновился `imageSha256` — ровно совпадает с текстом коммита «Eight changed frames were reviewed; two byte-identical frames remain as environment witnesses» |
|
||||
|
||||
### Выбор браузерных смоков (`scripts/smoke-select.mjs`)
|
||||
|
||||
Вывод идентичен r1 (продуктовый диф не менялся):
|
||||
|
||||
```
|
||||
Изменено файлов src/**: 3 · символов проекта на изменённых строках: 2
|
||||
Матрица: 213 смоков · порог «широкого» символа: больше 42 смоков
|
||||
|
||||
Прямое совпадение (9): smoke_backup_transfer, smoke_bg_color,
|
||||
smoke_color_picker_consumers, smoke_dialog_zombie, smoke_esc_dialogs,
|
||||
smoke_general_settings, smoke_ha_controls, smoke_help_affordance, smoke_sun
|
||||
← _openSettingsDialog (совпадение по имени JS-символа на изменённой строке,
|
||||
сама функция не менялась — менялся только список CSS-классов)
|
||||
```
|
||||
|
||||
Решение по 9 найденным — то же, что в r1, и оно не устарело: причина
|
||||
совпадения (общая строка вызова `_openSettingsDialog`/`_openSupportDialog`, не
|
||||
сама функция) не изменилась, диф не изменился. Прогнал повторно два самых
|
||||
предметно близких — `smoke_general_settings.mjs`, `smoke_help_affordance.mjs` —
|
||||
оба **OK**. Остальные 7 не прогонял по той же причине, что и в r1: чисто
|
||||
визуальная правка класса, не затрагивающая обработчик клика или разметку
|
||||
диалогов. Полный прогон всех 213 смоков — предрелизный гейт (§8), не гейт
|
||||
ревью.
|
||||
|
||||
### Что не проверял и почему
|
||||
|
||||
- **`npm run golden:verify`** — не прогонял. В `demo/golden/` нет сценариев,
|
||||
ссылающихся на `.support-button`, `.header-action` или `.supportmessage`
|
||||
(проверено `grep -rl` по `demo/golden/`) — совпадает с тем, что AC2 сам
|
||||
относит golden-проверку к «перед бетой», не к код-ревью.
|
||||
- **`node scripts/model-invariants.mjs`** — не прогонял. Диф не касается
|
||||
геометрии, рёбер, `layout`, `marker.space`, `open_spans`.
|
||||
- **`python -m pytest tests_backend`** — не прогонял. `custom_components/**/*.py`
|
||||
не тронут.
|
||||
- **Performance-профили** — не названы в AC, не прогонял.
|
||||
- Остальные 7 из 9 «прямое совпадение» смоков — решение см. выше, не изменилось
|
||||
с r1.
|
||||
- Полный прогон 213 смоков — предрелизный гейт, здесь не нужен: диф не «задевает
|
||||
всё».
|
||||
|
||||
## Закрытие раунда r1
|
||||
|
||||
| Находка r1 | Чем закрыта | Где это видно |
|
||||
|---|---|---|
|
||||
| **High** — `node scripts/check-docs.mjs` красный («screenshot source fingerprint is stale»): диф трогает `src/**`, отпечаток документации считается по всему `src/**` безусловно (§8) | Джоба `Docs screenshots` (`workflow_dispatch`) прогнана, артефакт принят `npm run docs:accept -- --reviewed --from=<артефакт>` с явным `--expect-change` на 8 сценариях, эталоны закоммичены в `b0122803` | `node scripts/check-docs.mjs` → «Documentation checks passed (7 files, 10 external links)» — перепроверено лично на SHA `b0122803`, не только со слов автора; CI-прогон `33592550716` подтверждён `gh run view` как реально существующий `success` |
|
||||
|
||||
Других находок в r1 не было — таблица из одной строки полна.
|
||||
|
||||
## Унаследовано из r1
|
||||
|
||||
Ниже — то, что подтверждено уже в r1 (`docs/reviews/CODE-REVIEW-415-r1.md`, SHA
|
||||
`2fc8c1203782026bb45647ad54ef11857f7eb2c3`) и **дополнительно перепроверено
|
||||
лично в этом раунде** тем же способом (документ ревью недостижим по SHA, но
|
||||
процитированные в нём строки кода сверены построчно с текущим диффом — они не
|
||||
изменились):
|
||||
|
||||
- **AC1.** `.support-button` не несёт стилей размера — правило переименовано в
|
||||
`.header-action` (`src/styles/plan.styles.ts:188`) и навешено на обе кнопки
|
||||
шапки (`src/houseplan-card.ts:11496,11499`); `support-button` остался только
|
||||
как селектор-хук, ноль правил стиля на нём (перепроверено `grep -rn
|
||||
"support-button" src/` — одно вхождение, в шаблоне). Автотест
|
||||
`headerActionsMatch` умеет падать — перепроверено лично на исходниках
|
||||
`afe5e989`, независимо от r1.
|
||||
- **AC2.** `.supportmessage` получил `background: var(--hp-bg)`
|
||||
(`src/styles/dialogs.styles.ts`), тот же токен, что `.namein`
|
||||
(`src/styles/dialogs.styles.ts:355`, тоже `var(--hp-bg)`) — перепроверено
|
||||
`grep -n ".namein" -A5`. `.supportraw` не тронут — не входит в диф. Автотест
|
||||
`messageSurfaceMatchesContact` умеет падать — перепроверено тем же способом.
|
||||
- **Трейлеры и changelog коммита `d1691f4b`** — `Issue: #415`,
|
||||
`User-Visible: yes`, оба changelog правлены в том же коммите, терминология
|
||||
совпадает с `docs/USER-GUIDE.ru.md:58,1920` («Общие настройки», «Помощь и
|
||||
обратная связь») — перепроверено повторным `grep` по гайду.
|
||||
- **Тестовый контракт `test/support-feedback.test.mjs`** — статический
|
||||
дубль-контроль поверх браузерного смока (ровно два вхождения
|
||||
`header-action` в шапке, правило `.header-action` содержит `min-width:44px`,
|
||||
`.supportmessage` содержит `background: var(--hp-bg)`) — диф файла
|
||||
перечитан целиком в этом раунде, не только унаследован.
|
||||
|
||||
Не перепроверялось заново, унаследовано без замечаний (диф этих утверждений не
|
||||
касается): продуктовое рассуждение r1 о том, что задача не расширяет скоуп
|
||||
(«ни новых сценариев, ни миграции, ни i18n, ни touch/perf») — дельта раунда
|
||||
(рёбейз + пересъёмка скриншотов + документ) этого вывода не задевает.
|
||||
|
||||
## Находки
|
||||
|
||||
Новых находок нет. Находка r1 (High, check-docs) закрыта и перепроверена лично
|
||||
(см. выше), новых High/Medium/Low в дельте этого раунда (рёбейз на `206732e9`,
|
||||
пересъёмка 8 кадров, документ ревью r1) не обнаружено — рёбейз принёс только
|
||||
чужой, не пересекающийся с этим диффом коммит #413 (CI-инструмент, не
|
||||
продуктовый код), конфликтов слияния нет (`git status --porcelain` пуст на
|
||||
каждом шаге проверки).
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **AC1 и AC2** — доказаны автотестом, который умеет падать; перепроверено
|
||||
лично в этом раунде независимо от r1 (см. «Унаследовано из r1»).
|
||||
- **check-docs** — зелёный на точном SHA `b0122803`, ссылка на реальный
|
||||
успешный CI-прогон пересъёмки подтверждена через `gh run view`, а не принята
|
||||
на слово.
|
||||
- **Провенанс пересъёмки** — 8 изменившихся кадров и 2 кадра-свидетеля в
|
||||
`screenshots.json` совпадают с формулировкой коммита `b0122803` число в
|
||||
число.
|
||||
- **Рёбейз не сломал ничего**: `npm test` (1729/1730), `tsc`, `build`+`cmp`
|
||||
трёх копий бандла, `process-gate.mjs`, целевой и точечные смоки — все
|
||||
зелёные на актуальном `origin/dev` (`206732e9`), а не на устаревшей базе.
|
||||
- **Трейлеры класса C.** `b0122803` трогает только `docs/images/**`
|
||||
(класс C по таблице §1, не класс D — `dist/**`,
|
||||
`custom_components/houseplan/frontend/**`, `demo/golden/baselines/**`), и
|
||||
`scripts/validate-commit-provenance.mjs` требует `Release`/`Baseline-Reviewed`
|
||||
только для `demo/golden/baselines/*.png|.json` — эта пара трейлеров сюда не
|
||||
относится; вместо неё коммит несёт содержательную строку «Canonical Linux
|
||||
capture: <ссылка>» в стиле вывода `scripts/docs-accept.mjs`. Формальных
|
||||
требований гейта это не нарушает.
|
||||
- **Никакого расширения скоупа.** Диф раунда — рёбейз, документ ревью,
|
||||
пересъёмка скриншотов; продуктовый код не менялся ни строкой.
|
||||
|
||||
## Вывод
|
||||
|
||||
Единственная находка r1 (High, `check-docs`) закрыта и лично перепроверена, а
|
||||
не принята со слов автора: гейт зелёный, CI-прогон пересъёмки существует и
|
||||
успешен, провенанс `screenshots.json` совпадает с текстом коммита. Рёбейз на
|
||||
ушедший вперёд `dev` потребовал полного разбора (§7.2), а не узкой дельты —
|
||||
проведён полностью: оба AC передоказаны автотестом, который умеет падать,
|
||||
независимо от r1. Новых находок нет. Вердикт — зелёный.
|
||||
@@ -23,6 +23,7 @@
|
||||
* который пуш добавит в целевую ветку. Пустой список — тоже отказ: публиковать
|
||||
* нечего, значит что-то пошло не так раньше.
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
export const REVIEW_DOC_ALLOWLIST = ['docs/reviews/'];
|
||||
@@ -59,10 +60,138 @@ export function reviewDocPushRefusal(paths, allowlist = REVIEW_DOC_ALLOWLIST) {
|
||||
+ ' превратилось в «затереть dev целиком» (#365).';
|
||||
}
|
||||
|
||||
/**
|
||||
* Сколько первых строк документа считаются шапкой. Материал раунда объявляется
|
||||
* там — измерено по корпусу: из 555 опубликованных ревью 409 называют SHA в
|
||||
* первых пятнадцати строках. Дальше начинается проза, и в ней SHA упоминаются
|
||||
* исторически («коммит bb2919f откатил dev»), проверять их нечего.
|
||||
*/
|
||||
export const REVIEW_HEADER_LINES = 20;
|
||||
|
||||
/** Строки шапки, объявляющие материал раунда. */
|
||||
const MATERIAL_MARKER = /(Материал|Коммит дельты|SHA|HEAD\s*=|коммит)/;
|
||||
|
||||
/**
|
||||
* Кандидаты в SHA. Границы подобраны по корпусу, а не по вкусу:
|
||||
*
|
||||
* - 7–40 знаков: короче не бывает сокращений git, длиннее не бывает sha1.
|
||||
* Отсекает заодно sha256 (64) — их в отчётах много, и они не коммиты;
|
||||
* - хотя бы одна буква a–f: иначе в кандидаты попадают номера прогонов и даты
|
||||
* вида `20260901`;
|
||||
* - не после `#`: цвет `#607d8bff` — восемь шестнадцатеричных знаков;
|
||||
* - не внутри более длинной шестнадцатеричной последовательности и не через
|
||||
* дефис: `sha256-…` и обрезанные хвосты хешей кандидатами не считаются.
|
||||
*/
|
||||
const SHA_CANDIDATE = /(?<![0-9a-f#-])[0-9a-f]{7,40}(?![0-9a-f-])/g;
|
||||
|
||||
/**
|
||||
* SHA, объявленные материалом раунда: `[{ line, sha }]`.
|
||||
*
|
||||
* Зачем отдельная функция и почему только шапка. PROCESS.md §2.10 требует
|
||||
* называть SHA предыдущего раунда затем, чтобы дельта следующего объявлялась
|
||||
* воспроизводимой командой `git diff <sha>..HEAD`. Проверять имеет смысл ровно
|
||||
* то, что этой командой пользуются: объявление материала. Исторические
|
||||
* упоминания в прозе — не обещание воспроизводимости.
|
||||
*/
|
||||
export function citedMaterialShas(text, headerLines = REVIEW_HEADER_LINES) {
|
||||
const found = [];
|
||||
String(text ?? '').split('\n').slice(0, headerLines).forEach((line, index) => {
|
||||
if (!MATERIAL_MARKER.test(line)) return;
|
||||
for (const sha of line.match(SHA_CANDIDATE) || []) {
|
||||
if (!/[a-f]/.test(sha)) continue;
|
||||
found.push({ line: index + 1, sha });
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вердикт: `null` — все объявленные SHA существуют коммитами.
|
||||
*
|
||||
* Зачем этот рубеж (#413). `SPEC-REVIEW-403-r2.md` объявил материал раунда на
|
||||
* `HEAD = 83005c3c`, и тот же SHA независимо назвал автор ТЗ в комментарии
|
||||
* issue. Коммита с таким именем в репозитории нет и не было: клон не мелкий,
|
||||
* `git rev-list --all` его не знает. Скорее всего значение снято до `amend`
|
||||
* или `rebase` при публикации — то есть проверка `git rev-parse HEAD` перед
|
||||
* выводом отчёта, которую требует §7.2, не выполнялась ни у автора, ни у
|
||||
* ревьюера.
|
||||
*
|
||||
* Цена уже заплачена на следующем раунде: пункт «найти SHA, на котором получен
|
||||
* предыдущий вердикт» выполнить буквально не удалось, реальный коммит
|
||||
* реконструировали по содержимому диффа.
|
||||
*
|
||||
* Чего этот рубеж НЕ умеет, и это важно знать. Он судит момент публикации.
|
||||
* Ветка задачи после ревью нередко перебазируется или сквошится, и SHA умирает
|
||||
* уже потом — по корпусу таких объявлений 98 из 804. Здесь ловится другой
|
||||
* класс: SHA, мёртвый уже в момент, когда его объявляют воспроизводимым.
|
||||
*
|
||||
* Достижимость проверяется от ссылок ПУБЛИКАЦИИ (`refs/remotes/origin/*` и
|
||||
* теги), а не от локальных. Разница не теоретическая: осиротевший `83005c3c`
|
||||
* до сих пор лежит объектом в клоне Codex и достижим там из локальной
|
||||
* `refs/heads/issue/403-area-relocation-safety`, не обновлённой после ребейза.
|
||||
* Читателю отчёта от этого нет никакой пользы — он может достать только то,
|
||||
* что есть на origin. Локальная проверка дала бы «всё в порядке» ровно на той
|
||||
* машине, где ошибку и совершили.
|
||||
*
|
||||
* @param resolveReachable функция `(shas) => Map<sha, ref|null>`
|
||||
*/
|
||||
export function danglingMaterialRefusal(text, resolveReachable, headerLines = REVIEW_HEADER_LINES) {
|
||||
const cited = citedMaterialShas(text, headerLines);
|
||||
if (!cited.length) return null;
|
||||
const refs = resolveReachable([...new Set(cited.map((item) => item.sha))]);
|
||||
const bad = cited.filter((item) => !refs.get(item.sha));
|
||||
if (!bad.length) return null;
|
||||
const lines = bad
|
||||
.map((item) => ` строка ${item.line}: ${item.sha} → не достижим ни из одной ссылки origin`)
|
||||
.join('\n');
|
||||
return 'ревью-документ объявляет материал раунда на SHA, которого нет на'
|
||||
+ ` origin:\n${lines}\n`
|
||||
+ 'Команда `git diff <sha>..HEAD` из PROCESS.md §2.10 на таком отчёте не'
|
||||
+ ' работает, а следующий раунд восстанавливает коммит по содержимому'
|
||||
+ ' диффа руками (#413). Сверьте SHA командой `git rev-parse HEAD`'
|
||||
+ ' непосредственно перед выводом отчёта — §7.2 требует именно этого,'
|
||||
+ ' а не значения, записанного до amend или rebase.';
|
||||
}
|
||||
|
||||
const invokedDirectly = process.argv[1]
|
||||
&& import.meta.url === new URL(`file://${process.argv[1]}`).href;
|
||||
if (invokedDirectly) {
|
||||
const argv = process.argv.slice(2);
|
||||
// Режим проверки объявленного материала (#413): на входе сам документ.
|
||||
const docArg = argv.find((item) => item.startsWith('--doc='));
|
||||
if (docArg) {
|
||||
const path = docArg.slice('--doc='.length);
|
||||
let text;
|
||||
try {
|
||||
text = readFileSync(path === '-' ? 0 : path, 'utf8');
|
||||
} catch (error) {
|
||||
console.error(`::error::ревью-документ не прочитан: ${path} (${error.code || error.message})`);
|
||||
process.exit(1);
|
||||
}
|
||||
const resolveReachable = (shas) => {
|
||||
const map = new Map(shas.map((sha) => [sha, null]));
|
||||
for (const sha of shas) {
|
||||
const probe = spawnSync('git', [
|
||||
'for-each-ref', '--contains', sha, '--count=1',
|
||||
'--format=%(refname)', 'refs/remotes/origin', 'refs/tags',
|
||||
], { encoding: 'utf8' });
|
||||
const ref = (probe.stdout || '').trim().split('\n')[0];
|
||||
if (probe.status === 0 && ref) map.set(sha, ref);
|
||||
}
|
||||
return map;
|
||||
};
|
||||
const refusal = danglingMaterialRefusal(text, resolveReachable);
|
||||
if (refusal) {
|
||||
console.error(`::error::${refusal.split('\n')[0]}`);
|
||||
console.error(refusal);
|
||||
process.exit(1);
|
||||
}
|
||||
const cited = citedMaterialShas(text);
|
||||
console.log(cited.length
|
||||
? `материал раунда объявлен и достижим с origin: ${cited.map((item) => item.sha).join(', ')}`
|
||||
: 'материал раунда в шапке не объявлен — проверять нечего');
|
||||
process.exit(0);
|
||||
}
|
||||
const allowArg = argv.find((item) => item.startsWith('--allow='));
|
||||
const allowlist = allowArg
|
||||
? allowArg.slice('--allow='.length).split(',').map((item) => item.trim()).filter(Boolean)
|
||||
|
||||
@@ -11493,10 +11493,10 @@ export class HouseplanCard extends LitElement {
|
||||
<button class="btn zb" @click=${() => this._stepZoom(1)} title=${this._t('title.zoom_in')}><ha-icon icon="mdi:plus"></ha-icon></button>
|
||||
</div>
|
||||
${this._norm && this._canEdit
|
||||
? html`<button class="btn" @click=${this._openSettingsDialog} title=${this._t('title.general_settings')}>
|
||||
? html`<button class="btn header-action" @click=${this._openSettingsDialog} title=${this._t('title.general_settings')}>
|
||||
<ha-icon icon="mdi:cog-outline"></ha-icon>
|
||||
</button>
|
||||
<button class="btn support-button" @click=${this._openSupportDialog}
|
||||
<button class="btn header-action support-button" @click=${this._openSupportDialog}
|
||||
title=${this._t('support.title')} aria-label=${this._t('support.title')}>
|
||||
<ha-icon icon="mdi:help-circle-outline"></ha-icon>
|
||||
</button>`
|
||||
|
||||
@@ -1186,7 +1186,10 @@ export const dialogsStyles = css`
|
||||
background: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 92%, var(--hp-txt));
|
||||
font: inherit;
|
||||
}
|
||||
.supportmessage { min-height: 120px; }
|
||||
.supportmessage {
|
||||
min-height: 120px;
|
||||
background: var(--hp-bg);
|
||||
}
|
||||
.supportraw {
|
||||
min-height: 220px;
|
||||
margin-top: var(--sp-2);
|
||||
|
||||
@@ -185,7 +185,7 @@ export const planStyles = css`
|
||||
justify-content: center;
|
||||
padding: var(--sp-3);
|
||||
}
|
||||
.support-button {
|
||||
.header-action {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
justify-content: center;
|
||||
|
||||
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
import {
|
||||
REVIEW_DOC_ALLOWLIST, pathsOutsideAllowlist, reviewDocPushRefusal,
|
||||
REVIEW_DOC_ALLOWLIST, citedMaterialShas, danglingMaterialRefusal, pathsOutsideAllowlist, reviewDocPushRefusal,
|
||||
} from '../scripts/review-doc-guard.mjs';
|
||||
|
||||
// #365. 28.08 шаг публикации ревью-дока запушил в dev коммит bb2919f с тридцатью
|
||||
@@ -87,3 +87,53 @@ test('шаг публикации в конвейере проверяет и и
|
||||
// Индексируется один путь, а не каталог.
|
||||
assert.match(step, /git add -- "\$doc"/);
|
||||
});
|
||||
|
||||
// --- материал раунда обязан быть достижим (#413) ----------------------------
|
||||
|
||||
test('SHA из шапки извлекаются, а из прозы — нет (#413)', () => {
|
||||
const doc = [
|
||||
'# SPEC-REVIEW-403-r2',
|
||||
'',
|
||||
'## Скоуп',
|
||||
'',
|
||||
'- Материал: спец-файл на `HEAD = 83005c3c` (ветка `issue/403-x`,',
|
||||
' коммит «docs: revise area relocation safety spec»)',
|
||||
'- Ревизия: 2',
|
||||
].join('\n') + '\n'.repeat(30) + 'Так коммит bb2919f7 откатил dev на три часа.\n';
|
||||
const cited = citedMaterialShas(doc);
|
||||
assert.deepEqual(cited.map((item) => item.sha), ['83005c3c']);
|
||||
assert.equal(cited[0].line, 5);
|
||||
});
|
||||
|
||||
test('не-SHA в шапку не попадают: цвета, sha256, номера (#413)', () => {
|
||||
const doc = [
|
||||
'- Материал: коммит `cbf5cc1b`, цвет #607d8bff, прогон 20260901,',
|
||||
' imageSha256 `9119ab87502038f787529f621c39e1e0d01f3bc3b0289051c3791a1886e97a6b`,',
|
||||
' ссылка sha256-abc1234def',
|
||||
].join('\n');
|
||||
assert.deepEqual(citedMaterialShas(doc).map((item) => item.sha), ['cbf5cc1b']);
|
||||
});
|
||||
|
||||
test('недостижимый SHA останавливает раунд и объясняет, почему (#413)', () => {
|
||||
const doc = '- Материал: спец-файл на `HEAD = 83005c3c`\n';
|
||||
const refusal = danglingMaterialRefusal(doc, () => new Map([['83005c3c', null]]));
|
||||
assert.match(refusal, /83005c3c/);
|
||||
assert.match(refusal, /не достижим ни из одной ссылки origin/);
|
||||
// Отказ обязан называть и команду из канона, и способ не повторить:
|
||||
// на #403 ревьюер снял HEAD до ребейза и не сверился перед выводом.
|
||||
assert.match(refusal, /git diff/);
|
||||
assert.match(refusal, /git rev-parse HEAD/);
|
||||
});
|
||||
|
||||
test('достижимый SHA раунд не задерживает (#413)', () => {
|
||||
const doc = '- Материал: коммит `cbf5cc1b`\n';
|
||||
const resolve = () => new Map([['cbf5cc1b', 'refs/remotes/origin/dev']]);
|
||||
assert.equal(danglingMaterialRefusal(doc, resolve), null);
|
||||
});
|
||||
|
||||
test('шапка без объявления материала не судится (#413)', () => {
|
||||
// Часть документов материал не объявляет вовсе — по корпусу таких 146 из 555.
|
||||
// Требовать объявление — отдельное решение о каноне, а не дело гейта.
|
||||
assert.equal(danglingMaterialRefusal('# CODE-REVIEW-1-r1\n\nтекст\n', () => new Map()), null);
|
||||
assert.deepEqual(citedMaterialShas('# CODE-REVIEW-1-r1\n\nтекст\n'), []);
|
||||
});
|
||||
|
||||
@@ -63,13 +63,16 @@ test('Help is lazy, ordered after settings, and owns the single About/Guide surf
|
||||
const card = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
|
||||
const runtime = readFileSync(new URL('../src/houseplan-editor-runtime.ts', import.meta.url), 'utf8');
|
||||
const styles = readFileSync(new URL('../src/styles/plan.styles.ts', import.meta.url), 'utf8');
|
||||
const dialogStyles = readFileSync(new URL('../src/styles/dialogs.styles.ts', import.meta.url), 'utf8');
|
||||
const header = card.slice(card.indexOf('<div class="zoomctl">'), card.indexOf('</div>\n ${this._canEdit'));
|
||||
assert.ok(header.indexOf('_openSettingsDialog') < header.indexOf('_openSupportDialog'));
|
||||
assert.match(card, /if \(!this\._editorRuntime\)[\s\S]*?_ensureEditorRuntime\(\)[\s\S]*?_openSupportDialog/);
|
||||
assert.equal((runtime.match(/_t\('gs\.about_version'/g) || []).length, 1);
|
||||
assert.match(runtime, /docs\/USER-GUIDE\.ru\.md/);
|
||||
assert.match(runtime, /docs\/USER-GUIDE\.md/);
|
||||
assert.match(styles, /\.support-button\s*\{[\s\S]*?min-width:\s*44px;[\s\S]*?min-height:\s*44px;/);
|
||||
assert.equal((header.match(/header-action/g) || []).length, 2);
|
||||
assert.match(styles, /\.header-action\s*\{[\s\S]*?min-width:\s*44px;[\s\S]*?min-height:\s*44px;/);
|
||||
assert.match(dialogStyles, /\.supportmessage\s*\{[\s\S]*?background:\s*var\(--hp-bg\);/);
|
||||
});
|
||||
|
||||
test('the consent copy names exact geometry, project relay, retention and network address', () => {
|
||||
|
||||