Uh oh!
There was an error while loading. Please reload this page.
') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })();
There was an error while loading. Please reload this page.
type="number" widget can display one value and store another with nothing said — announce it, and at what scope? #6780
Filed by the
domain:uiexecution seat (PM sessionsession_01CRJge11jso9TpXRWFt1Z49) from the measurement in #6765 / PR #6777. ⛔ This card does not repeat that measurement — #6765 is the authoritative record.The measurement changed the question
#6765 predicted that browser sanitization would turn residue into an empty string. Measured in Chromium 141.0.7390.37 via Playwright, real widgets mounted from source, keyboard + real-clipboard paste +
insertText+ programmatic set:Sanitization holds — but by FILTERING, not by emptying.
12abc"""12"1.2.3"""1.23"0x10"""010"⇒ Residue never reaches these widgets, which makes #6715's anchored whole-string guard a provable no-op here — it would accept every string these boxes can produce and reject only strings the test environment fabricates. It was deliberately not copied.
The defect that WAS reproduced
Typing
1eleaves Chromium visibly displaying1ewhile.valuereads the empty string. SoonChangereceivesnull,aria-invalidstays"false", and nothing is said (screenshot taken). Pasting1einto an empty box fires no change event at all, so the model silently keeps its previous value.That is #6716's class — on a money field.
Two facts constrain any answer, and are why this was escalated rather than implemented:
NumberFieldandGeolocationFieldread the box the same way. This is one ruling about thetype="number"widget class.1.2.3into a currency field stores1.23;0x10stores10. The information is already gone beforehandleChangeruns — only abandoningtype="number"recovers it.Also now pinned: the oracle and the product disagree in 6 of 10 measured cases (e.g.
0x10emits0under happy-dom and10in Chromium), so every existing unit test for these widgets drives a string no browser can deliver.Options
e.target.validity.badInputacross the whole class (Currency, Percent, Number, Geolocation), reusing #6716's refusal shape. ⭐ Uses the platform's OWN predicate rather than a renderer-side dialect — andbadInputis the one signal happy-dom and Chromium were measured to AGREE on, so the oracle stops lying for exactly this guard. Needs anonBlurarm too (React fires nothing when the value stays empty);PercentFieldhas noonBlurtoday. Leaves the filtering truncation silent.type="number"behaviour and close. Zero risk, zero cost, honest given no user report exists. Leaves a money field that can display one thing and store another with nothing said.type="text"plus anchored parsing, exactlyLocationField's shape. The only option that refuses both the silent drop and the filtering truncation, because it takes the text before the browser rewrites it. Cost: reverses #2572's deliberate min/max/step affordances, and loses the spinner and the mobile numeric keyboard on every currency and percent field in the product.四维分析
① 实际业务需求。 静默丢弃是在钱字段上实测复现的,这是真的。⚠️ 但没有测到的是:有没有人真的往货币框里粘
1.2.3或0x10。所以 C 的高昂代价目前压在一条没有需求证据的路线上 —— 这与 #6715 的裁决拒绝做「度分秒/半球解析」是同一个理由。② 平台长远合理性。 ⭐ A 是问平台它自己读到了什么,而不是再发明一套渲染器侧的「什么算数字」——contract-first。C 在架构上是最健全的终态,但它反转的是一个刻意做过的决定(#2572 的 min/max/step 与移动端数字键盘),那需要维护者,不是 dev。
③ 防 AI 写错元数据 —— 也是 A 唯一真正的风险,必须明说而不是掩饰。 沉默正是错值藏身之处,所以"说"胜过"不说"。⛔ 但:一个对
1e报警、却对1.2.3静默存成 1.23 的控件,等于教会用户「没报警就说明值是对的」 —— 这正是 #6272 自己的原则反过来指向我们。所以 A 若实施,必须同时写明 filtering 截断仍然静默,否则它制造的信任比它消除的风险更贵。④ 创业阶段不扩散需求。 A 是一个包内四个控件的有界改动;C 是四个控件重写外加产品里每一个数值字段的可用性回退,应当等到有实测用户报告再谈。
本席意见与实施席一致:A,但范围按
type="number"这一类的四个控件立成一张卡,⛔ 不是给 #6765 点名的那两个打补丁;且与「截断仍静默」的书面说明同批发布。⛔ 但这改变已发布控件在什么条件下向用户发声,且要在
PercentField上新增onBlur面,属于公开行为契约变更,落在人工地板上 —— 本席记录,不代裁。Scope note
⭐
NumberField/GeolocationFieldwere deliberately not filed as a separate card by the implementer, on the grounds that doing so would split one ruling across two cards. This seat agrees; that is why this card is scoped to all four.Related
#6765 (the measurement) · PR #6777 (the comment-only records and the divergence pin) · #6716 (the silent-refusal class, ruled worth announcing for a coordinate) · #6715 / PR #6766 (the anchored guard, measurably a no-op here) · #6272 (the plausible-wrong-value precedent) · #2572 (the
type="number"affordances C would reverse)